This is not a “let’s use git” post. This is a “here’s how we’d use git, if we did” post. With all the recent git talk, I decided I’d try learning it with one of my on-again-off-again projects, Pulse. Caveat: I am not an SCM geek. I don’t enjoy learning SCM systems for the fun of it. I just want to be sure that whatever we use fits our needs. (Further disclaimer: I actually like CVS more than I like SVN. Infer what you will from that.)
If you go looking for documentation on git, you’ll find information on how to set up your own repository, how to clone other people’s repositories, how to make your own little personal branches (and why you should), and how to push/pull/merge. What they generally don’t discuss is how to work with a central repository.
So here’s my day-to-day Gnome SCM activities, translated into git. These instructions assume that Yelp is stored in the mythical git repository ssh://git.gnome.org/yelp.git. If any git people spot something braindead, point it out.
Checking Out
git clone ssh://git.gnome.org/yelp.git
This will clone the entire Yelp repository into a new directory called yelp
. Initially, that directory will be working on a local branch called master
, which is branched from the master
branch on the server. The alias origin
is created to refer to the repository on the server.
To get updates from the remote repository (the analog to cvs|svn update
), run
git pull origin
If you’re using lots of remote branches, you may need to run somethinng like
git pull origin master
Committing
So you’ve made some changes to some files. To see what’s been changed, run
git diff
Without any options, this shows the differences between the data on your file system and the data in the index. The index is basically all those local changes that you’ve told git you want to commit. Let’s say you’ve modified yelp-document.[ch]
. To add your changes to the index, run
git add src/yelp-document.c src/yelp-document.h
Now git diff
shows no changes, because all the changes are in the index. But they haven’t actually been committed to the repository yet. To see the differences between the index and the repository, run
git diff --cached
To commit these changes, run
git commit
If you’re thinking there must be a way to skip the git add
step, you’re right.
git commit -a
This will find all the differences between your local files and the repository, add them to the index, and commit them. Of course, you still need to use git add
to add new files.
Committing Remotely
All you’ve done in the steps above is commit to your local repository. Git people will tell you you should do this early and often, and then push your local changes when a suitable chunk of work has been done. So to commit your work to the central repository, there’s one more step to take.
git push origin
Remember that git clone
set up origin
as an alias for the remote repository you cloned. Without any extra options, this will merge the changes on your local branch (master
) to the branch of the same name on the remote repository.
Branching
Git people will tell you to create a new local branch for any set of changes you’re working on. If you’re working on fixing some bug, make a branch for that bug fix. If you’re implementing some whizbang feature, make another branch for that. You can easily switch between these branches. To create a new branch, run
git branch whizbang-feature
This creates a local branch, branched from whatever you currently have checked out. If you currently have the stupid-bug
branch checked out, and you want whizbang-feature
to branch from master
instead, run
git branch whizbang-feature master
This just creates the branch. Your local copy is still a checkout of whatever it was before you called git branch
. To start working on the new branch, you’ll want to check it out from your local repository:
git checkout whizbang-feature
You can always see your local branches by running
git branch
Remote Branches
If you check out Yelp and run git branch
, you’ll only see your own local branch, master
. But Yelp has lots of branches in the central repository. We make them for every stable release series. So where are they?
git branch -r
The -r
argument causes git to show the branches on the remote repository you cloned from. You refer to the remote branches by prefixing them with origin/
, which you’ll recall is the alias for the remote repository. If you want to look at the stable gnome-2-18
branch, you could run
git checkout origin/gnome-2-20
But if you want to make changes, you should really create your own local branch instead.
git branch gnome-2-20 origin/gnome-2-20
Now you can commit your changes to your local branch, then git push
them to the remote branch.
Creating Remote Branches
In Gnome, we create branches for each stable release series. As a maintainer, you need to create branches in the central repository. So you can create a branch like this
git branch gnome-2-20
But that branch is just in your local repository. To push the branch to the central repository, run
git push origin gnome-2-20
Now other users will see this branch when they run git branch -r
.
Tagging
In Gnome, we tag our central repository for releases. So if you release Yelp 2.20.0, you need to create the tag YELP_2_20_0. Creating a tag in your local repository is simple:
git tag YELP_2_20_0
But we need this tag to be in the central repository. To do this, we git push
the tag up:
git push origin tag YELP_2_20_0
When you clone a repository, you get all its tags. To see all the tags in the repository, run
git tag -l
And So On
Obviously, there’s a lot more to learn to make really effective use of git. But most of that information is readily available elsewhere. I mosty wanted to address how to work with (and maintain) a central repository, because most git documentation doesn’t. Comments welcome.
Shaun, thanks for posting this; it helps to clarify how a normal GNOME project would use git. I’m glad I managed to read past the heretic statement of “I actually like CVS more than I like SVN.” 🙂
It would be cool to see this on l.g.o, similar to http://live.gnome.org/GitForGnomeDevelopers (which focuses on using git-svn on existing GNOME infrastructure).
Hi,
Excellent writeup from our Editor-in-Chief, thanks!
Couple comments:
“git checkout -b new-branch” comes handy. That creates the branch and switches to it.
Instead of git-pull, many of us use git-fetch followed by “git-rebase origin/master”. That ports your changes to the latest upstream development instead of leaving you with one of those useless “merge” commits.
I agree, I haven’t seen much written on using git with central repositories. Excellent intro, this one goes into my collection of git-references!
To me it is not clear in your post whether it is necessary to do ‘git add’ before committing – it certainly isn’t and… why would you want to do it?
‘git status’ is also useful of course.
It is necessary to do ‘git gc’ occasionally to “reduce disk space and increase performance” – eg running ‘git log’ and ‘gitk’ will always be fast.
You only need to push the tag if you’ve tagged a commit that has already been pushed in the repository, otherwise it goes along with the commits with ‘git push’.
‘git rebase’ is important because it makes your local commits in sync with the updated upstream head, ie the local commits are moved forward to start from the new head.
-git pull
+git fetch && git rebase origin
so you don’t end up with 10000000 merge changesets
The index is now called the staging area. It stages the pending commit. This is what it should have been called all along!
@Marko: Why ‘git add’ when you’ve already added a file? Because, in git’s opinion, svn’s policy of automatically committing every change it can find is reckless. It’s easy to explicitly add files to the commit each time, and it cuts down on mistakes (like checking important passwords into the repository, oops). Personally, I much prefer this approach.
But, as Shaun says, if you don’t like creating each changeset by hand, you can just use ‘git commit -a’ to just use the shotgun approach.
Thank you for sharing!