Version Control with Git


Setting up a Git project

If you want to work with an existing project, clone it:

   $ git clone <url>

If you do not have an existing git project, create one:

   $ cd project/
   $ git init          # initializes the repository
   $ git add .         # add those 'unknown' files
   $ git commit        # commit all changes, edit changelog entry
   $ git rm --cached <file>... # ridiculously complicated command to undo, in case you forgot .gitignore

Git will look for a file named **.gitignore** in the root of your repository which contains a set of shell patterns to ignore in file paths.

Branching and merging

   $ git checkout -b linux-work        # create a new branch named "linux-work"
   $ <make changes>
   $ git commit -a
   $ git checkout master               # go back to master branch
   $ git merge linux-work              # merge changesets from linux-work (Git >= 1.5)
   $ git pull . linux-work             # merge changesets from linux-work (all Git versions)

Importing patches

   $ git apply < ../p/foo.patch
   $ git commit -a

Exporting a patch

   $ <make changes>
   $ git commit -a -m "commit message"
   $ git format-patch HEAD^  # creates 0001-commit-message.txt
                             # (HEAD^ means every patch since one revision before the
                             # tip of the branch, also known as HEAD)

Network support

   # clone from the primary Git repo
   $ git clone git://
   $ cd git
   # pushing changes to a remote repo with SSH
   $ git push
   # fetch changes to a remote branch into a local branch
   $ git fetch remote-branch:local-branch
   # merge changes from a remote machine
   bar$ git pull git://foo/repo.git/ branch
   # Serve repository via git protocol
   $ cd /my/repository/
   $ touch .git/git-daemon-export-ok
   $ git daemon  # now others can fetch from git://your.machine/my/repository/.git/
   # Set up a bare (= without working directory) repository (e.g. on a webserver)
   $ mkdir my-repo.git
   $ cd my-repo.git
   $ git --bare init
   $ chmod a+x hooks/post-update # this is needed for HTTP transport
                                         # you need to populate this repository via push

see Remote (bare) repository for more

Inspecting revisions

   # inspect history visually
   $ gitk       # this opens a Tk window, and shows you how the revisions are connected
   # inspect history
   $ git log    # this pipes a log of the current branch into your PAGER
   $ git log -p # ditto, but append a patch after each commit message
   $ git log -S'string' --oneline # search for string in all patches
   $ git log -G'regex' --oneline # search for regex in all patches
   # inspect a specific commit
   $ git show HEAD    # show commit info, diffstat and patch
                         # of the tip of the current branch

Referring to revisions

   # by name
   $ git log v1.0.0   # show history leading up to tag "v1.0.0"
   $ git log master   # show history of branch "master"
   # relative to a name
   $ git show master^   # show parent to last revision of master
   $ git show master~2  # show grand parent to tip of master
   $ git show master~3  # show great grand parent to tip of master (you get the idea)
   # by output of "git describe"
   $ git show v1.4.4-g730996f  # you get this string by calling "git describe"
   # by hash (internally, all objects are identified by a hash)
   $ git show f665776185ad074b236c00751d666da7d1977dbe
   $ git show f665776   # a unique prefix is sufficient
   # tag a revision
   $ git tag v1.0.0                      # make current HEAD known as "v1.0.0"
   $ git tag interesting v1.4.4-g730996f # tag a specific revision (not HEAD)

Comparing revisions

   # diff between two branches
   $ git diff origin..master            # pipes a diff into PAGER
   $ git diff origin..master > my.patch # pipes a diff into my.patch
   # get diffstat of uncommitted work
   $ git diff --stat HEAD

Cherry picking patches

   $ git cherry-pick other-branch~3     # apply 4th last patch of other-branch to current branch

Remote (bare) repositories

create a bare Git repository to store changes

   $ ssh remotehost
   $ mkdir -p scm/myapp.git
   $ cd scm/myapp.git
   $ git --bare init
   $ exit

then go to your project and push your changes to the remote repository

   $ cd ~/Sites/myapp
   $ git remote add origin ssh://remotehost/scm/myapp.git
   $ git push origin master

now if you want to share this repository with others all they need to do is

   $ cd ~/Sites
   $ git clone ssh://remotehost/scm/myapp.git

Note that you can also push a new branch to a remote repository by simply doing:

   $ git checkout -b newbranch
   # make changes and commit them
   $ git checkout master
   $ git push ssh://remotehost/scm/myapp.git newbranch

That creates a new remotes/origin/newbranch branch on the remote origin (shared repository). Users doing //git pull// will get this branch

You can then make git pull --rebase [origin]; git push [origin] work by just doing

   git config branch.master.merge refs/heads/master
   git config branch.master.remote  origin

Removing Files from history

Say you have a large file that is making your repository too big as git maintains multiple copies of its history (say a binary file). If you decided that it was not worthy keeping this file in your repository, you can do the following to re-write history and remove the file. Be sure to archive a copy of your repository before typing these commands:

   git filter-branch --tree-filter 'rm -f filename' HEAD
   git prune --expire=now
   git reflog expire --expire-unreachable=now --rewrite --all
   git repack -a -d
   git prune-packed


Say you do:

git branch

and it shows

 * ()
 * master

And all your changes were committed to "* ()" by mistake and you switch back to master and now your changes are lost! Don't despair!

In git nothing is lost, so you can simply use the reflogs to recover your changes:

 git log --walk-reflogs HEAD
 git log --walk-reflogs master

Then once you have the id that you need, you can proceed with:

 git show 375703efa1c465671723aac9ad43aa25cd5494f7 > /tmp/patch.diff

And apply your patch as you normally would.

Nice Logging

 git config --global alias.logline "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit" 

Your logs will now have color and easier to read:

 538c0f9 - (HEAD, origin/master, origin/HEAD, master) return same output as rpm -q (48 minutes ago) <My Name>

Nice Searching

 git config --global alias.logr "log --oneline -G"


 git logr 'rm.*reboot'
 e9ade4e adds support for new kernel
 cd0de3b adds new kernel
 49da89c fixed ips of servers
 84da5dc change to version to foo 3.10

Alias branches

 git symbolic-ref refs/heads/trunk refs/heads/master
 git branch
    trunk -> master

And now you can use this name "trunk" where you'd normally use "master"

 git checkout trunk

git bundle

Use this whenever you need to transfer a branch or series of patches from one machine to another that are not directly connected via SSH or any other protocol.

   git bundle create /tmp/foo-repo-from-origin-to-master origin/master..master