digital-domain.net

Contents

Git - Fast Version Control System / A stupid content tracker

git-scm.com.png Homepage

git-tutorial.png Introduction to Git

git-community-book.png Git community book

progit.png Pro Git professional version control

User manual http://www.kernel.org/pub/software/scm/git/docs/user-manual.html

tour_of_git.png A tour of git: the basics

Why you should choose git as your DVCS: http://0pointer.de/blog/projects/on-version-control-systems.html

Git magic: http://www-cs-students.stanford.edu/~blynn/gitmagic/

gitcasts.png Screencasts of how to do stuff in git

Git for Subversion users: http://git.or.cz/course/svn.html

Git for computer scientists: http://eagain.net/articles/git-for-computer-scientists/

Branching and merging with git: http://lwn.net/Articles/210045/

repository_formats_matter.png Keith Packard's - Repository formats matter

Merge-level conflict resolution: Linus style http://www.ussg.iu.edu/hypermail/linux/kernel/0802.1/2406.html

Sharing work

Mostly taken from the git user manual. /usr/share/doc/git-core-1.5.2.2/user-manual.txt

One way to submit changes to a project is to tell the maintainer of that project to pull the changes from your repository using git-pull.

A common way to do this is to maintain a separate public repository (usually on a different host) for others to pull changes from. This is usually more convenient, and allows you to cleanly separate private work in progress from publicly visible work.

You will continue to do your day-to-day work in your personal repository, but periodically "push" changes from your personal repository into your public repository, allowing other developers to pull from that repository. So the flow of changes, in a situation where there is one other developer with a public repository, looks like this:

                        you push
  your personal repo ------------------> your public repo
        ^                                     |
        |                                     |
        | you pull                            | they pull
        |                                     |
        |                                     |
        |               they push             V
  their public repo <------------------- their repo

HOWTOs

Setting up a central Git repository

To setup a new repository, do the following.

   $ cd /srv/git
   $ mkdir <new-repository>.git
   $ cd <new-repository>.git
   $ git init --bare
   $ vi description
   $ vi config

add the following stanza to deny non fast-forward pushes

[receive]
        denyNonFastForwards = true

add the following stanza

[gitweb]
        owner = Your Name

If you want this repository to be served via git-daemon:

   $ touch git-daemon-export-ok

Back on your workstation. If you don't have a repository, create one.

   $ cd project
   $ git init
   $ git add .
   $ git commit -m "Initial Commit"

Now's the time to make sure your details are correct.

You can set your name and email address globally or on a per repository basis.

   $ git config --global user.name "Your Name"
   $ git config --global user.email you@example.com

Will set them globally. To set them just for the current repository, strip out the --global

With an existing repository, make sure your on the 'master' branch.

   $ git checkout master

Create a remote link with the repository you created above.

   $ git remote add origin ssh://git.example.com/srv/git/new-repository.git

Push your repository up

   $ git push -u origin master

If you have other branches you wish to push

   $ git push origin <branch>

If you have tags you wish to push, you can push them all in one go

   $ git push origin --tags

Individual tags can be pushed as

   $ git push origin <tag>

Migrating a SVN repository to Git

Largely taken from http://progit.org/book/ch8-2.html with some modifications.

Clone the SVN repository.

   $ git svn clone https://svn.example.com/repos/repository \
   --authors-file=users.txt --no-metadata -s my_project

or

   $ git svn clone https://svn.example.com/repos/repository/branches/somebranch \
   --authors-file=users.txt --no-metadata my_project

Do some cleanups

   $ cp -rf .git/refs/remotes/tags/* .git/refs/tags/
   $ rm -rf .git/refs/remotes/tags
   $ cp -rf .git/refs/remotes/* .git/refs/heads/
   $ rm -rf .git/refs/remotes
   $ rm -rf .git/svn

Delete any branches it left around from the migration. e.g trunk, git-svn

   $ git branch -d trunk

Edit .git/config and remove the svn stanzas.

Set your details if needed.

   $ git config user.name "Your Name"
   $ git config user.email you@example.com

Garbage collect the repository

   $ git gc

Connect to the remote repository

   $ git remote add origin ssh://git.example.com/srv/git/repository.git

Push all branches

   $ git push origin --all

Push all tags

   $ git push origin --tags

Migrating a Mercurial repository to Git

Following the steps from here.

We need to get the hg-fast-exporter.

   $ git clone git://repo.or.cz/fast-export.git

Create the new git repository

   $ mkdir repository_git

Clone the Mercurial repository

   $ hg clone ssh://hg.example.com//srv/hg/repository

Change into the new git repository and initialise it

   $ cd repository_git
   $ git init

Now to run the export script

   $ ../fast-export/hg-fast-export.sh -r ../<hg repository> -A ../git-author-mapping.txt

Checkout the trunk or HEAD branch

   $ git checkout trunk or HEAD

Use git filter-branch to rewrite authorships if necessary.

Make a master branch

   $ git checkout -b master

Delete the other branch

   $ git branch -d trunk or HEAD

Set your Name and email address if required.

   $ git config user.name "Your Name"
   $ git config user.email you@example.com

Garbage collect the repository

   $ git gc

Connect to the remote repository

   $ git remote add origin ssh://git.example.com/srv/git/repository.git

Push all branches

   $ git push origin --all

Push all tags

   $ git push origin --tags

Detach a subdirectory into a separate Git repository

As gleamed from

Problem:

You have a repository that has a foo subdirectory and you want to make that its own repository with its history intact.

repository/
    bar/
    baz/
    foo/

You want foo/ to become a repository.

There are two ways of doing this. With Git versions >= 1.7.11 you can do this
    $ mkdir new_repository
    $ cd new_repository
    $ git init --bare

    $ cd ../current_repository
    $ git subtree split --prefix=foo -b foo
    $ git push ../new_repository foo:master
    $ git branch -D foo

    $ cd ../new_repository
    $ mkdir .git
    $ mv * .git/
    $ vim .git/config		# set bare = false
    $ git reset --hard

For older versions of Git, you should do this

Firstly make a clone of the repository.

   $ git clone --no-hardlinks repository new-repository

Then make foo/ the new HEAD

   $ cd new-repository
   $ git filter-branch --subdirectory-filter foo HEAD -- --all
   $ git reset --hard
   $ git gc --aggressive
   $ git prune

You should then remove the old origin

   $ git remote rm origin

You can then set this up as a new central repository as shown above.

Making separate git repositories branches of a single one

Particularly useful when migrating mercurial repositories to git, where what should be a branch is in a separate repository.

This is actually pretty simple. Once you have your git repositories, it boils down to:-

   $ mkdir combined-repo
   $ cd combined-repo
   $ git init

For each repository you want to make a branch

   $ git remote add <repo> </path/to/repo>
   $ git fetch <repo>
   $ git checkout -b <repo> <repo/branch>

Split a branch out into its own repository

Kind of the reverse of the above.

   $ mkdir split-repo
   $ cd split-repo
   $ git init
   $ git remote add <repo> </path/to/repo>
   $ git fetch <repo>
   $ git checkout -b master <repo/branch>

Remote branches

If you have checked out a git repository with more branches than just master. Then these additional branches won't show up by default.

List remote branches.

   $ git branch -r

Switch to a remote branch keeping track of changes. This means running git-pull will automatically merge in changes in the upstream <remote branch> into your local branch just like origin/master --> master.

   $ git checkout --track origin/<remote name>

or to give the branch a different name

   $ git checkout --track -b <local name> origin/<remote name>

Misc.

   $ git config --global user.name "Your Name"
   $ git config --global user.email email@address

That sets those globally, you can set it per repository by dropping the --global

Have the current git branch displayed in your shell prompt when your inside a git repository.

export PS1="[\u@\h \W\$(git branch 2> /dev/null | grep -e '\* ' | sed 's/^..\(.*\)/{\1}/')]\$ "

If you have committed local changes, then git-pull will create a spurious "Merge" commit, which will pollute the change list when you later push upstream. To avoid this, do these two commands:

   $ git fetch
   $ git rebase origin

Instead of merging, this attempts to insert the changes you pull from the origin repository before your local changes, avoiding the merge message.

Tips

Push only some commits to your upstream repo

git status says:

   $ git status
   # On branch master
   # Your branch is ahead of 'origin/master' by 2 commits.

You want to git push only one of those commits to the public repo. Here’s how:

First use git log to get the commit hash of the commit you want to push. Then:

   $ git push origin <thelonghash>:master

Fetch, Inspect, Merge

When pulling changes from another repository you can simply do something like
    $ git pull https://github.com/jasonblewis/xlander master
This would merge the changes from the specified repository/branch into your current branch.

However it may often be useful to inspect the changes locally before actually applying them. To do this you can

    $ git fetch https://github.com/jasonblewis/xlander master
Inspect the changes like
    $ git diff ..FETCH_HEAD
then merge the changes with
    $ git merge FETCH_HEAD

Retrieving commits to a detached HEAD

http://gitolite.com/detached-head.html

Delete a remote branch

   $ git push origin :branchname

Where branchname is the branch you want to delete. This basically says push up an empty/non-existent branch to branchname

Create an empty branch

http://book.git-scm.com/5_creating_new_empty_branches.html

   $ git symbolic-ref HEAD refs/heads/NEWBRANCH 
   $ rm .git/index 
   $ git clean -fdx 
   
   <do work> 
   
   $ git add your files 
   $ git commit -m 'Initial commit'

Colour highlighting in VIM when committing

Set one of these environment variables to vim

   GIT_EDITOR
   VISUAL
   EDITOR

in your ~/.bashrc or equivalent.

Or set the core.editor git config option to vim, i.e

   $ git config --global core.editor vim

Prevent vim from remembering where it last was in the commit message

When using vim as your commit message editor, it will remember its last position in the commit message file, .git/COMMIT_EDITMSG. Put the following in your ~/.vimrc to make vim always start at the origin for commit messages.

    autocmd FileType gitcommit call setpos('.', [0, 1, 1, 0])

Showing fuller pathnames in git diff/show etc

Use the width arguments(s) to --stat.

   --stat=<width>[,<name-width>]

The first width is the terminal width, the second is the width allowed for the filename

e.g

   $ git diff --stat=160,140

Show function names instead of labels in git diff output

With C code, sometimes git diff output will show a label name instead of the function name for a hunk, e.g

@@ -2749,6 +2726,8 @@ out:
 
 out2:
        free_vars(qvars);
+       free_avars();
+       free_u_files();
        gettimeofday(&etv, NULL);
        d_fprintf(access_log, "Got request from %s for %s (%s), %f secs\n",
                                env_vars.http_x_forwarded_for,

By adding the following (from Peter Zijlstra) to your ~/.gitconfig or <project>/.git/config

[diff "default"]
        xfuncname = "^[[:alpha:]$_].*[^:]$"

You will now get:

@@ -2749,6 +2726,8 @@ void handle_request(void)
 
 out2:
        free_vars(qvars);
+       free_avars();
+       free_u_files();
        gettimeofday(&etv, NULL);
        d_fprintf(access_log, "Got request from %s for %s (%s), %f secs\n",
                                env_vars.http_x_forwarded_for,
This can also be achieved using gitattributes by adding
*.c     diff=cpp
*.h     diff=cpp
to a .gitattributes file

Edit the first commit

If you've made two or more commits and you need to edit the first one, you can't do that with git rebase -i

Instead, it's a little more involved (from stackoverflow)

Note: Of course, you should only do this on a branch that has never been pulled by anyone... I.e a project that you haven't pushed anywhere and you want to clean up before you do.

# tag the old root, "git rev-list ..." will return the hash of first commit
git tag root `git rev-list HEAD | tail -1`

# switch to a new branch pointing at the first commit
git checkout -b new-root root

# make any edits and then commit them with:
git commit --amend

# check out the previous branch (i.e. master)
git checkout @{-1}

# replace old root with amended version
git rebase --onto new-root root

# you might encounter merge conflicts, fix any conflicts and continue with:
# git rebase --continue

# delete the branch "new-root"
git branch -d new-root

# delete the tag "root"
git tag -d root

Uncommit the last commit

   $ git reset HEAD^

Show a file at a particular revision

   $ git show <treeish>:<file>

Show branch(es) a commit is on

   $ git branch --contains <commit>

GUD (Git Urban Dictionary)

Source: (http://lwn.net/Articles/290428/)

http://lwn.net/Articles/291302/
http://lwn.net/Articles/291303/
http://lwn.net/Articles/291304/