Overview
Git is a distributed vesioning control system.
References
Overviews
DONE Git vs. Mercurial blog entry: MacGyver vs. Bond
- State "DONE"
- State "TODO"
A nice, touchy-feely intro to the difference between Git and Mercurial. Despite the one-stop-shopping-appeal of Mercurial, I will go with my command-line-linux-philosophy-loving, blogs-with-ikiwiki heart and head into git-land.
Tutorials
TODO Rubinius git page Get going fast
- State "TODO"
TODO gittutorial The official intro ;), looks fairly readable…
- State "TODO"
TODO Everyday GIT With 20 Commands Or So Nicely broken down by goal
- State "TODO"
TODO Git Community Book Open source wiki/howto website
- State "TODO"
Interface with SVN
TODO Diving into git blog entry Importing the Subversion history
- State "TODO"
TODO Git - SVN Crash Course ?
- State "TODO"
TODO An introduction to git-svn for Subversion/SVK users and deserters
- State "TODO"
Lots of political digression, but also lots of useful info. Your call.
Enforcing development policies
TODO Push hooks
- State "TODO"
Push hooks
DONE Pre commit hooks
- State "DONE"
- State "TODO"
Basically, put whatever you want in .git/hooks/pre-commit
.
For example, I added:
./.git/hooks/pre-commit-make-check || exit 1
right after the initial comments with
$ cat .git/hooks/pre-commit-make-check make check
Don't forget to chmod 744
both scripts to make them executible.
Also note that these scripts are run from the base repo directory,
which is why I had to include the relative path to
pre-commit-make-check
, and didn't need cd ../../
in the check
script.
DONE Rebase Considered Harmful
- State "DONE"
- State "TODO"
A “keep the warts” vs. “stay true to history” monologue. Advocates against rebasing public repos because (quotes from the git-rebase manpage):
"When you rebase a branch, you are changing its history in a way that will cause problems for anyone who already has a copy of the branch in their repository and tries to pull updates from you."
Personally, I feel like a middle ground, where private mini-branches get a little constructive history tweaking is a good thing. Noone cares about typos in comments, and it adds noise to the development signal, so fix those commits in your private branch. Once you go public though, don't mess with the history, since it would be even more confusing to have conflicting histories in seperate public repositories. Anything serious enough to require a altering the author field should probably not be changed.
My thoughts are shared by others, for example commenter #7 Jing Xue. However, commenter #8 links to Synchronizing development: rebase vs. pull+merge, GIT vs. Mercurial, where oddbod points out that the real problem when “mid-level”, public repos rebase to avoid sending known-bugged patches upstream. He says they avoid the obviously better solution of sending up the bad patch with a patch-patch hard on its heels, which would avoid rebasing a public repo, and still preserve the spirit and authorship of the changes.
- Aside: rebase etymology
I just realized that “rebasing” is attaching your branch to a different base point on the tree. Afterward, it looks like you made all of your adjustments right now, not in parallel with a bunch of other fixes on the main branch.
Setting a description for your repo
Edit .git/description
.
Peripherals
GitTorrent
People are indeed working on the obvious protocol.
The git-* to git * transition
The transition occured between git 1.5.4 and 1.6.0. See the 1.6.0 release notes for details.
Case studies
Gitting chem_web
, my first git project
Repository creation and setup
From the Git Community Book.
Install
$ apt-get install git-core gitk
Setup
From the Git Community Book.
By default that file is ~/.gitconfig
and the contents will then look like this:
[user] name = W. Trevor King email = wking at drexel dot edu
Initialize repository
From the Git Community Book.
$ cd ~/rsrch/chem_web $ git init
Add some files to the repository
From the Git Community Book.
$ git add README *.py docs templates $ git status
Oops, git add
adds all of a directory's contents too. Remove some
of the automatically generated files. git rm
removes a file from
both the working directory and the index. I wish I could just remove
from the index, but I'm not sure how yet. Ah well, the reason I want
to remove them is that they are automatically generated ;).
$ git rm -f docs/*.pdf ...
a bunch of other work while I clean up the mess I've made. Keep going
until git status
looks right.
Commit the code to the master branch
From the Git Community Book.
$ git commit
Or you can automatically add any changed tracked-files with
$ git commit -a
Creating the development branch
From the Git Community Book.
My workflow will be in two branches:
-
master
, the current stable/working release -
dev
, the feature development branch
I will develop new code in dev
until I am happy with its
performance, and then merge back into the master branch. Old bugs
will be fixed in the master branch, and then cherry-picked into the
dev branch. That's the current plan anyway ;). Create the dev
branch with
$ git branch dev
Changing branches and coding
From the Git Community Book.
List branches with
$ git branch
Change to dev with
$ git checkout dev
Now add that cool new feature, NFPA diamonds for the cabinets :p. When you feel it is appropriate, commit your changes in the local branch with
$ git commit -a
Correcting private commit mistakes
From the Git Community Book.
Oops, I forgot X or made a typo in my commit message. My dev
branch
is not public, so go ahead and fix the mistake. Then run
$ git commit --amend
This may leave some danglers
$ git fsck dangling blob 03cabac5fa426ca8df4dff1fdb2596b68d2f4c5a dangling blob 87488a0b4ea976127d6b9171ef6f10941a1dd74e ...
which you can clean up with
$ git prune
Bring the master branch up to speed
From the Git Community Book.
$ git checkout master $ git merge dev
You may have to deal with a conflicting merge.
Delete a branch
From the Git Community Book.
$ git branch -d dev
The -d
ensures the changes have already been merged back into the
current branch. If you want to kill the brach without merging use
$ git branch -D dev
Repository maitenance
From the Git Community Book.
Recompress (to keep the compression most effective)
$ git gc
Check repository consitency
$ git fsck
Both should be run manually from time to time.
Working over a network
From the Git Community Book.
- Grab a remote repository
A remote friend wants to work on your code, or more likely, you're at home and you want to work on your code at work. Grab the repo using ssh with
$ git clone ssh://wking@loki/~/rsrch/chem_inventory ci
using the
ssh://[user@]host.xz/~/path/to/repo.git
git URL notation listed in
man git-clone
.
- Possibly remove the reference to the remote repository
If you're decomissioning the remote repository, you can remove it from the current one with
$ git remote rm <name>
- Work on the repository
Nothing changes here.
- Get your remote partner to pull your changes back upstream
$ git remote add bob /home/bob/repo $ git fetch remote $ git log -p dev remotes/bob/master $ git merge remotes/bob/master
and possibly
$ git remote rm bob
Gitting sawsim, migration from svn
Install git
$ apt-get install git-core git-svn gitk
Repository migration from svn
Following Jon Maddox at the Simplistic Complexity blog.
- Create the decoy directory
git svn
keeps some info about the svn repository, to make it easier to keep the two in sync. However we are not interested in staying in sync, we just want to move to Git. Create a decoy Git version of the repo with$ mkdir sawsim.svn $ cd sawsim.svn $ git svn init svn://abax.physics.drexel.edu/sawsim/trunk/ $ cat >> .git/config <<EOF [svn] authorsfile = users.txt EOF $ cat > users.txt <<EOF wking = W. Trevor King <wking at drexel dot edu> EOF $ git svn fetch
Jon Maddox suggests using
$ git config svn.authorsfile ~/Desktop/users.txt
instead of my manual
.git/config
editing, but my stock Debian git (1.4.4.4) doesn't seem to havegit config
. No big deal.Check that this worked and translated your users correctly with
$ git log
- Warning: here documents
Some shells might not like my here document Bash syntax. In that case, write the files another way.
- Warning: here documents
- Create the clean directory
When we clone the decoy directory, all the svn junk gets left behind.
First, leave the sawsim.svn directory
$ cd ..
and clone like you normally would
$ git clone sawsim.svn sawsim
You don't need the
[svn]
stuff in.git/config
anymore either. I don't remember if they came over on their own or not…I removed the origin information with
$ rm .git/remotes/origin
Although a better approach would probably be
$ git remote rm origin
Setup
- gitignore
Fromman gitignore
The sawsim code is mostly in a single noweb file. Extracting all the source from the file creates lot of indirectly versioned clutter. To avoid having all your
git status
calls swamped with untracked files, Just add the filenames (globbing allowed) to.gitignore
.
- Push hooks
TODO
Making your repository public/Publishing your repository
From the Git Community Book
To distribute your repo via HTTP (easier on someone else's server).
user@devel$ ssh server server$ cd ~/public_html server$ git clone --bare ssh://user@devel/~/path/to/repo.git repo.git server$ cd repo.git server$ touch git-daemon-export-ok server$ git --bare update-server-info server$ chmod a+x hooks/post-update
Others can clone or pull from your URL with
anon$ git clone http://server/~you/repo.git
From the Git Community Book
Once you've set up your repo, you can push changes to it with ssh
devel$ git push ssh://user@server/~/public_html/repo.git master
To save typing, add
[remote "public"] url = ssh://user@server/~/public_html/repo.git
to .git/config
, after which you can push with
devel$ git push public master
Note that you may need to copy .git/description
over by hand. I
wrote up a git-publish script to automate this.
Gitting comedi, using Git as a frontend for CVS
From Takis blog
Repository creation and setup
This is quite similar to svn migration.
- Create the decoy directory
$ mkdir comedi.cvs $ cd comedi.cvs $ #cvs -d :pserver:anonymous@cvs.comedi.org:/cvs/comedi login $ git cvsimport -p x -v -d :pserver:anonymous@cvs.comedi.org:/cvs/comedi comedi
The login line may not be necessary for other CVS projects. Arguments to
git cvsimport
:Option Meaning -p x
Pass -x
tocvsps
, which ignores any~/.cvsps/cvsps.cache
file-v
Verbose -d ...
From http://www.comedi.org/download.html
- Create the clean directory
$ cd .. $ git clone comedi.cvs comedi
Do your work like normal in comedi
Update with the latest cvs
$ cd comedi.cvs $ git cvsimport -p x -v -d :pserver:anonymous@cvs.comedi.org:/cvs/comedi comedi $ cd ../comedi $ git pull
You may have to deal with a conflicting merge.
Create a patch against the cvs source
$ git format-patch origin
Other useful examples
Purge all knowledge of a given file or subdir from history
For example, in case you accidentally started versioning /etc/shadow
,
or some other document containing sensative information.
$ git filter-branch --index-filter 'git rm -rf --cached --ignore-unmatch /etc/shadow' $ git reflog expire --expire=0 --all $ git prune $ git repack -adf
Rewrite the repository to look as if foodir/ had been its project root
Discarding all other history
$ git filter-branch --subdirectory-filter foodir -- --all
Cherry pick a commit from another repository
First, give the remote repository a nickname (optional)
$ git remote add bob /home/bob/myrepo
Then fetch the remote repo
$ git fetch bob
You can merge all the changes Bob made to his master branch with
$ git pull . remotes/bob/master
Or cherry-pick a particular one, e.g. commit 1d8fb1fe41dfc1b1eb38c7b5d574577c4b341c58
$ git cherry-pick 1d8fb1fe41dfc1b1eb38c7b5d574577c4b341c58
When a particular remote repo no longer contains interesting material, you can purge the fetched tags and objects with
$ git remote rm bob $ git remote prune bob
Restore a deleted file to match an earlier version
Source: stackoverflow.
Find the commit that deleted the file in question
$ git rev-list -n 1 HEAD -- <file_path>
or
$ git log --diff-filter=D --summary
Then checkout the file
$ git checkout <deleting_commit>^ -- <file_path>
Move master.HEAD to a different location
Sometimes you screw up and want to drop the thread you've been working
on. Place the HEAD
of the master branch on commit XYZ
with
$ git checkout XYZ $ git branch -D master $ git branch master XYZ $ git checkout master
Note that this may remove some information from your .git/config
's
[branch "master"]
entry. You should save your earlier .git/config
before doing it, and make any appropriate corrections afterwards.
If you're just trying to roll back a branch a few commits, skip the above and try
$ git reset --hard HEAD^
or add however many ^
you need to get back to the last good commit.
Git submodules, nesting/tracking sub-repositories.
This is a nice way of grouping associated projects. The submodules are included as stand-alone repositories, and the super-project has pointers picking out a particular revision of each submoduel. See the related Git subtrees for an alternate approach.
Setup a super-module
From the Git Book
$ git submodule add ~/path/to/submoduleX $submoduleX
Warning: Do not use local URLs here if you plan to publish your
supermodule. git submodule add
- clones the submodule under the current directory and by default checks out the master branch.
- adds the submodule's clone path to the gitmodules file and adds this file to the index, ready to be committed.
-
adds the submodule's current commit ID to the index, ready to be
committed.
$ git submodule init
Cloning a super-module
From the Git Wiki
Grab the content living in the super module itself
$ git clone ~/subtut/public/super
See how the submodules look
$ git submodule status -d266b9873ad50488163457f025db7cdd9683d88b a ...
Add submodule repository URLs to .git/config
$ git submodule init
Check that they're there if you like
$ git config -l ... submodule.a.url=/home/moses/subtut/public/a/.git
Now check out the referenced submodules
$ git submodule update
Changing submodules from supermodules
From the Git Wiki
To update a submodule, remember that it's just a checked-out branch in your supermodule. Create and publish the change using the usual method:
$ cd a a$ git branch * (no branch) master a$ git checkout master a$ echo "adding a line again" >> a.txt a$ git commit -a -m "Updated the submodule from within the superproject." a$ git push $ cd ..
Now point the supermodule at the new commit. Warning: don't use
git add a/
or git will think you mean to add the contents of the
directory. For submodule updates, you must leave off the trailing
slash.
$ git add a $ git commit -m "Updated submodule a." $ git show ... diff --git a/a b/a index d266b98..261dfac 160000 --- a/a +++ b/a @@ -1 +1 @@ -Subproject commit d266b9873ad50488163457f025db7cdd9683d88b +Subproject commit 261dfac35cb99d380eb966e102c1197139f7fa24
Take a look at the submodule changes from the supermodule.
$ git submodule summary HEAD^ * a d266b98...261dfac (1): > Updated the submodule from within the superproject.
Publish your updated supermodule.
$ git push
Removing submodules
From the Git Wiki
-
Delete the relevant line from the
.gitmodules
file. -
Delete the relevant section from
.git/config
. -
Run
git rm --cached path_to_submodule
(no trailing slash). - Commit and delete the now untracked submodule files.
Warnings
From the Git Wiki
It's not safe to run git submodule update
if you've made changes
within a submodule. They will be silently overwritten:
- Example
From the Git Wikia$ cat a.txt module a a$ echo line added from private2 >> a.txt a$ git commit -a -m "line added inside private2" $ cd .. $ git submodule update Submodule path 'a': checked out 'd266b9873ad50488163457f025db7cdd9683d88b' $ cat a/a.txt module a
The changes are still visible in the submodule's reflog:
$ git log -g --pretty=oneline d266b9873ad50488163457f025db7cdd9683d88b HEAD@{0}: checkout: moving to d266b9873ad50488163457f025db7 4389b0d8e22e616c88a99ebd072cfebba40797ef HEAD@{1}: commit: line added inside private2 d266b9873ad50488163457f025db7cdd9683d88b HEAD@{2}: checkout: moving to d266b9873ad50488163457f025db7
Git subtrees, merging an entire repository into a subdirectory of your repository
Setup
From kernel.org: merge-subtree
Name the other project Bproject
, and fetch.
$ git remote add -f Bproject /path/to/B
Prepare for the later step to record the result as a merge.
(-s ours
selects the merge strategy as "keep our version")
$ git merge -s ours --no-commit Bproject/master
Read master
branch of Bproject
to the subdirectory dir-B
.
$ git read-tree --prefix=dir-B/ -u Bproject/master
Record the merge result.
$ git commit -m "Merge B project as our subdirectory"
Keeping up to date with the sub-repository source
Maintain the result with subsequent pulls/merges using "subtree"
$ git pull -s subtree Bproject master
Pulling changes back into the sub-repository source
From git-subtree
The super-project makes some local alterations. Pull/merge them
with git subtree
.
Backdating Git tags
I rarely bother tagging my projects in the early stages. This is probably a mistake ;), but Git makes it easy to add backdated tags later on:
$ GIT_COMMITTER_DATE="2006-10-02 10:31" git tag -s v1.0.1
(from git tag --help
). Note that you will probably be tagging a
previous commit
$ GIT_COMMITTER_DATE="2006-10-02 10:31" git tag -s v1.0.1 <commit>
Inline word diff
$ git diff --word-diff
Find out if a change is part of a release
$ git name-rev --tags 33db5f4d9027a10e477ccf054b2c1ab94f74c85a 33db5f4d9027a10e477ccf054b2c1ab94f74c85a tags/v0.99~940
Now you are wiser, because you know that it happened 940 revisions before v0.99.
List unmerged commits
To see what's outstanding in a given branch
$ git cherry [-v] [<upstream> [<head> [<limit>]]]
The -v
option prints summaries as well as the usual commit hashes.
Remove a remote branch
If you want to delete a branch from a repository to which you only have push access, use:
git push repository :branch
after deleting the branch in your local repository.
Troubleshooting
Git commit hangs with no output
You probably corrupted something in .git
.
$ git gc $ git fsck
fixed the problem for me.
Conflicting merge
git pull
or git merge
aborts with merge-failed
or some such.
This leaves your checked out branch in a state where
$ git diff
shows the merge difficulties. You can either abort the merge, or merge by hand.