GIT

Links

Basic usage

  • this site is heavily inspired by a tutorial introduction to git
  • git-gui and gitk --all are very helpful to understand what is happening
  • See the paragraph SPECIFYING REVISIONS in git-rev-parse(1) for possibilities to address a revision in git

Start a new repository

Introduce yourself to git

git config --global user.name "Mr Foo"
git config --global user.email "foo@example.com"

Either create a new, empty folder ...

mkdir project
cd project
git init # git init-db

... or use an existing one

tar xzf project.tar.gz
cd project
git init # git init-db

Note: gitk won't work in new git repositories before your first commit. Until then the following error will occur:

Error in startup script: child process exited abnormally
    while executing
"close $refd"
    (procedure "readrefs" line 47)
    invoked from within
"readrefs"
    (file "/usr/bin/gitk" line 6370)

Add all files to the repository and commit.

git add .
git add--interactive
git commit

Adopt changes to local repository

What has changed?

git diff
git diff path/to/file

In contrast to Subversion you have to tell git which before each commit which of the tracked files should be considered. This is also achieved via git-add

git add path/to/file

You can also add them via git-gui in a more convenient way.

After you added those files with git-add they are no longer shown by git-diff. But you can still see what changes will be committed

git diff --cached
git diff --cached path/to/file

Now really add changes to your local repository

git commit path/to/new/file
git commit -m "* Bla bla" path/to/new/file

Another way is to tell git to commit all tracked and changed files without calling git-add on each file

git commit -a

If you made a mistake in the last commit you can still change it

git commit --amend

Another way

git reset --soft HEAD^
...
git commit -c ORIG_HEAD

Show modifications

git diff

git diff --color --color-words
git diff --ignore-space-at-eol --ignore-space-change
git diff                      -- path/to/file

git diff HEAD                 -- path/to/file
git diff HEAD^                -- path/to/file
git diff HEAD^^               -- path/to/file
git diff HEAD^^ HEAD^         -- path/to/file

git diff 68fcc0 ef6274        -- path/to/file
git diff HEAD   ef6274        -- path/to/file
git diff ef6274 ef6274^       -- path/to/file
git diff master experimental  -- path/to/file
git diff master origin/master -- path/to/file

git whatchanged 68fcc0 ef6274 -- path/to/file

git diff --name-only 68fcc0 ef6274

git status
git status -s

git log

You can also filter the output of diff, e.g. to show only deleted files, only modified files, ...

git diff --diff-filter=D --name-only
git diff --diff-filter=M --name-only

Remove files from the work area and the repository

rm path/to/file # git-rm path/to/file

Remove files only from the repository

git rm --cached foo

Show who changed what in a file

git blame foo/bar/file.cpp

Exclude files from the repository

There are two possibilities to exclude files from the repository

  • You can exclude them via .git/info/exclude. However this will only affect your local repository.
# (1)
foo.txt
# (2)
*.html
# (3)
!foo.html
# (4)
*.[oa]
  • You can create files named .gitignore in any of your folders to exclude files. This will affect files this folder and all of its subfolders. This has the advantage that the .gitignores can be added to the repository and shared among different repositories. If you like to exclude all files of a folder with this method don't forget to exclude the .gitignore from the exclusion.
*
!.gitignore

Additional information can be found in gitignore(5).

Patches

Save changes of repository in files in the given folder. These patches can be easily send via mail to propagate changes

git format-patch --numbered --thread -o /tmp/mypatches/ HEAD^^ HEAD
git format-patch --numbered --thread -o /tmp/mypatches/ -2

Save to diff between current branch and the branch origin into a patch

git format-patch                     -o /tmp/mypatches/ origin

Get all patches which are in the given mbox and apply them to the current branch

git am mymbox
git format-patch -k --stdout HEAD^^ HEAD | git am --3way --keep

Send patches directly via mail (if you skip recipient or sender it will be extracted from the repository automatically)

git send-email --from foo@example.com --envelope-sender foo@example.com --to bar@example.com  /tmp/mypatches/

Apply a patch

git apply --check   foo.patch
git apply           foo.patch
git apply --reject  foo.patch
git apply --reverse foo.patch

Apply a given commit to the current branch

git cherry-pick f6a2c

Maintenance

Speed up repository with compression (do this from time to time)

git gc

Check repository for bugs

git fsck

If this prints lots of rows starting with dangling ... do this

git gc --prune=now

Get other versions of a file in the repository

View other versions of a given file

git show abcde:path/to/file
git show HEAD:path/to/file
git show HEAD^:path/to/file
git show HEAD^^:path/to/file
git show HEAD~3:path/to/file
 

Restore older versions of a given file

git checkout HEAD^ path/to/file
it checkout filename

Tags

Create a Tag

git tag v1.0

Branches

Create branch

git branch experimental; git checkout experimental
git checkout -b experimental

Show all branches

git branch
git branch -r
git branch -a

Which branch is current

ls -l .git/HEAD

Switch branch

git checkout experimental

Switch back

git checkout master

Undo all local modifications

git checkout -f

Revert local changes

git checkout FILE

Merge all changes from branch experimental into the current branch

git pull . experimental

Delete branch

git branch -D experimental

Merge two branch back together The current branch will get the changes of the branch experimental so both will be equal

git merge experimental

Merge a conflict during merging Either edit the files manually with an editor, or chose like this if you want to keep your version and overwrite what the others did or keep what the others did and throw away your changes.

git checkout --ours the/file/with/problems.java
git checkout --theirs the/file/with/problems.java

Whatever you did at the end you need

git add the/file/with/problems.java
git commit

Continue merge after conflict

git rebase --continue

rebase

Assume you have the following branches

     -->A-->B-->C foo
    /
D-->E-->F-->G master

and you want that the foo branch

A-->B-->C

starts at the head of the master branch

             -->-->-->C foo
            /
D-->E-->F-->G master

If you are already in the foo branch it's enough to issue

git rebase master

to move the current branch on the head of the master branch.

Otherwise the following command

git rebase master foo

moves regardless of the current branch the foo branch on top of the head of the master branch.

Finally you can even move a given sub branch onto the head of a another branch. Assume you have

              F-->G-->H bar
             /
     C-->D-->E  foo
    /
A-->B  master
 

the the following command

git rebase --onto master foo bar

will achieve this result

     C-->D-->E  foo
    /
A-->B  master

     F-->G-->H  bar
 

Avoid git merge commits with rebasing

git config --global pull.rebase true

Branch strategies

Teamwork

Suppose Alice has a git repository in /home/alice/project, now Bob wants to help Alice and also work on the project. With the following command he'll gets a clone of the repository in the folder myrepo.

git clone /home/alice/project myrepo

After you cloned the remote repository you'll find one branch, master

git branch
* master

However, the remote repository might have more than one branch. Luckily, they are already here for you

git branch -r
git branch -a

If you want to have a first look into one of those branches, you can switch to them right away

git checkout origin/SuperBranchNr1

If you like to use this remote branch also in your repository, create a local branch for it and mark it to track changes

git branch --track SuperBranchNr1 origin/SuperBranchNr1

If Alice commits new changes into her repository Bod can pull those changes into his repository

git pull

If Bob has local, not yet commited changes he can park those changes for a moment, pull, and then try to apply the parked changes again

git stash
git pull
git stash pop

If it Bob who did commits into his repository, he can either push those changes into Alice's repository, or she can pull them by herself.

git pull /home/bob/myrepo
 Updating 123456..abcdefgh

To see the changes you just got (replace with the ids you saw in the pull)

git log 123456..abcdefgh
git diff 123456..abcdefgh

If Bob created new branches he can push the like this into Alice's repository

git push --all origin

He can also push the single new branch MyNewBranch to the remote repository origin

git push origin MyNewBranch

You can also fetch the changes first without an immediate merge

git fetch /home/bob/myrepo

How to review those changes

gitk --all
git diff master...origin/master
git diff master origin/master
git log origin/master ^master

And then merge the fetched changes (as git pull would have done

git merge origin/master

It is even possible to add references to more than one repository. Add a new one like this

git remote add origin git@foo/bar
git remote add origin2 git://foo/bar
git remote add origin3 ssh://user@foo.bar/~/foobar.git

Show remote repositories, get URL of repository you cloned from / push to

git remote show
git remote show origin

Remove remote repository

git remote rm origin2

Your own git server

There are several possible protocols available

  • git-deamon

Fast, but read only

  • http

Slow, but no problems with firewalls / proxies. Normally also only read only.

  • ssh

Read and write access

  • Local filesystem

Fast, read and write access

See also HOWTO setup a git repository.

Example for git deamon server

Server, with a given folder where the repository should lie, e.g. /var/cache/git

git daemon --verbose --base-path=/var/cache/git

Client, we already have a repository (e.g. /home/foo/bar) and like to export it to the server

git clone --bare /home/foo/bar MyFirstRepository.git

Enable export on repository

touch MyFirstRepository.git/git-daemon-export-ok

Copy folder to server

scp -r MyFirstRepository.git myserver.example.com:/var/cache/git/

Now you should be able to access the repository via git

git clone git://myserver.example.com/MyFirstRepository.git

Example for git ssh server

We also clone a repository (or use an existing one)

git clone --bare /home/foo/bar MyFirstRepository.git

We create a new user, e.g. git which will be used to log in via ssh (or use existing users). If you use an extra user, you might consider to restrict his shell to /usr/bin/git-shell (/etc/passwd) You might use a different port for the ssh deamon for the git usage

git clone ssh://git@foo.bar:9418/~/foo.git

Subversion and git

One time import from a subversion repository into a git repository

git svnimport http://example.com/mysvnrepository

This will expect that you followed the subversion recommendation to have the following repository structure

trunk/
branches/foo
branches/bar
branches/...

but with appropriate command line parameters you can import other structures as well

In one of my projects there was no trunk folder at all. As of now (August 2007) git-svnimport can not import such repositories. However there is a small patch for git-svnimport and empty trunk folder. In my project with commit 325 a trunk folder was created and everything was moved into it. With a git-importsvn version which can handle empty trunk folders the import worked in two steps

git svnimport         -s   1 -T ''      -l 324 http://example.com/myproject
git svnimport -o tmp1 -s 325 -T 'trunk'        http://example.com/myproject
git checkout tmp1
git rebase master

However there was manual intervention required to merge both parts.

If you like to use git and subversion in parallel you might be interested in git-svn

git svn init

git undo

If something went wrong with your git repository you can try to undo the last git actions

# git reflog
24f34f2... HEAD@{0}: pull : Fast forward
68fh47g... HEAD@{1}: checkout: moving to master
90cdrt3... HEAD@{2}: pull : Fast forward
62dgs35... HEAD@{3}: rebase: ...

# git reset --hard HEAD@{1}

Find erros with bisec

If the current project has a bug delcare it as bad and explain which is the last version which worked

git bisect start
git bisect bad
git bisect good v2.6.13-rc2

This will give you a version between the broken and the working version. Try it and declare it as bad or good.

git bisect good

until you find the modification which causes it. To quit bisec and get back the latest version

git bisect reset

Kernel

Get a copy of the (complete!) kernel repository from Linus

git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git linux-2.6

Note that there will be no tags for the stable kernel in it. So you may decide to use the stable Kernel git instead

git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-2.6.16.y.git

which has tags for the stable kernel.

List all available tags

ls .git/refs/tags/

If you already have downloaded the Linus git you can use it as a source for the stable one (untested, needs git 1.3)

git clone --reference oldfolder git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-2.6.16.y.git newfolder

Show difference between two versions for the file MAKEFILE

git diff v2.6.17-rc4..HEAD Makefile

Create a new branch, and get version with a specific tag into it (does not require network as you have complete repository). With this command you can get a specific kernel out of the git

git checkout -f master
git pull
git checkout -f -b my-new-branch tags/v2.6.16.19