Setting up git and git repositories
Set up some local variables:
$ git config --global user.name 'User Name on [machine]' $ git config --global user.email 'user-email@example.com' $ git config --global color.ui true
To make a git repo of the current directory:
$ git init
To add the entire current directory to git source control:
$ git add .
To add a specific file in the current directory to git source control:
$ git add some.file
Committing changes to git
Once files are being tracked by git, you need to commit any changes you've made into the git repo. This can be done in two ways. The first is to write a one-line commit message indicating what changed:
$ git commit -am 'initial commit message'
The second way involves launching an editor to explain your changes in detail (using as many lines as you want):
$ git commit -a
If you want to revert changes to a file that you've saved, but not yet commited to git, you can use:
$ git checkout -- [file-to-revert]
This effectively returns the file to the last committed version and discards any changes made since then.
If you want to reset the current state of the repository to an earlier state (this might happen when a change you made results in a catastrophe), use the following:
$ git reset [commit name] --hard
This requires that the current state of the repository have no
uncommitted changes. This also completely wipes out any history of the
commits between the current state and the state you reset to
([commit name]
). The commit id [commit name]
is
usually the short id shown by git log --oneline
.
Git branches and merging
Create a git branch to work on some feature in isolation from the master branch:
$ git branch [new-branch-name]
To switch to a branch, use git checkout:
$ git checkout [branch]
To look at the changelog:
$ git log
To merge changes from another branch to the current branch:
$ git merge [other-branch-name]
If an automatic merge fails, you will have to resolve the changes manually. One way to do this is to launch a diff viewer such as vimdiff, or meld:
$ git pull staging master remote: Counting objects: 5, done. remote: Compressing objects: 100% (3/3), done. remote: Total 3 (delta 2), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. From remote-repo * branch master -> FETCH_HEAD Auto-merging problematic-file.txt CONFLICT (content): Merge conflict in problematic-file.txt Automatic merge failed; fix conflicts and then commit the result. $ git mergetool merge tool candidates: meld [...blah, blah, tool names...] vimdiff Merging: problematic-file.txt Normal merge conflict for 'problematic-file.txt': {local}: modified file {remote}: modified file Hit return to start merge resolution tool (vimdiff): [fix merge conflicts using the merge resolution tool]
If you'd rather replace your local copy with the remote copy of the problematic file:
$ git checkout --theirs problematic-file.txt
Once you've resolved the merge conflicts, add the changed file back to the git tracking repository:
$ git add problematic-file.txt $ git commit -am 'fixed merge conflict for problematic-file.txt'
See this Stack Overflow question for more hints on how to resolve git merge conflicts manually.
Storing changes to the current branch temporarily
You can temporarily store your uncommitted changes to the current
branch using git stash
. An example is shown below:
$ git status # On branch master # Your branch is ahead of 'origin/master' by 17 commits. # # Changes not staged for commit: # (use "git add..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # # modified: lc_actions.py # modified: search_utils.py # modified: var_search.py # no changes added to commit (use "git add" and/or "git commit -a") $ git stash Saved working directory and index state WIP on master: 550a557 more work to get UID direct info working HEAD is now at 550a557 more work to get UID direct info working $ git status # On branch master # Your branch is ahead of 'origin/master' by 17 commits. # nothing to commit (working directory clean)
You can then recall these changes later and apply them to the current
branch using git stash apply
, like in the following
example:
$ git checkout cat-search-newdb Switched to branch 'cat-search-newdb' $ git stash list stash@{0}: WIP on master: 550a557 more work to get UID direct info working $ git stash apply # On branch cat-search-newdb # Changes not staged for commit: # (use "git add..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # # modified: lc_actions.py # modified: search_utils.py # modified: var_search.py # no changes added to commit (use "git add" and/or "git commit -a")
Dealing with other git repositories
To clone a git repo in full:
$ git clone user@remote.example.com:/path/to/repo -- remote repo over ssh $ git clone path/to/repo -- local repo
A bare git repository can be useful in some situations. This will act as a central or staging code repository with code being checked in and out. Make an empty directory to serve as this staging repo, and then inside this directory, do:
$ git init --bare
To pull from a remote repo:
$ git pull ssh://user@remote.example.com:/path/to/repo master
This pulls the master
branch from the remote repo.
To pull from a local repo:
$ git pull /path/to/repo master
This pulls the master
branch from the other repo.
To set up repo aliases:
$ git remote add repo-alias ssh://user@remote.example.com:/path/to/remote/repo
or
$ git remote add repo-alias /path/to/local/repo
To deal with nonstandard SSH ports for remote repos, use the following:
$ git remote add repo-alias ssh://user@remote.example.com:port/path/to/remote/repo
To push local changes in the master branch to a remote BARE repo (set up with git init --bare):
$ git push remote-alias master
To list remote aliases:
$ git remote -v
To remove remote aliases:
$ git remote rm remote-alias
Deploying websites/webapps with git
- set up a bare git repo to serve as the staging repo
- git init in the local project directory
- git commit the local project directory
- git push the local repo to the remote staging repo
- git pull from the remote staging repo to the deployment directory
Now all changes made in the local repo can be pushed to staging, and then pulled from there to the deployment repo. I use this method to deploy this website.
Automating parts of the git workflow
Git comes with launchable shell scripts that fire before/after
certain events in the commit process take place. Git places examples of
these scripts in the local directory in the .git/hooks
subdirectory. You can use these to automate any part of the workflow you
wish.
One particular case I've found useful is to automatically backup
local git repositories to a couple of remote repositories outside of
normal work hours when code is committed. This ensures that at the end
of the day, your code is safe in multiple locations. For this, I use
the post-commit
git hook. An example listing is below:
#!/bin/sh # # An example hook script that is called after a successful # commit is made. # # To enable this hook, rename this file to 'post-commit'. CURR_TIME=`date +%H%M` if [ $CURR_TIME -gt 1700 ]; then echo 'OUTSIDE WORKING HOURS: auto-pushing master branch to backup repos' git push backup-repo-one master git push backup-repo-two master fi if [ $CURR_TIME -lt 0800 ]; then echo 'OUTSIDE WORKING HOURS: auto-pushing master branch to backup repos' git push backup-repo-one master git push backup-repo-two master fi
In the case outlined above, backup-repo-one
and backup-repo-two
are bare git repositories.
Emacs and git
Emacs is git aware and will allow commits with C-x v v. This pulls up a buffer asking for the commit message. C-c C-c will close the buffer and commit the changes to the local git repo.