Development:Using Git for Camino development

From Camino Wiki
Revision as of 17:04, 26 May 2010 by Sardisson (talk | contribs) (→‎Prerequisites: comma)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

How to use Git locally to supercharge your Camino development workflow.
by Sean Murphy
on 2009-03-23

Even though the Camino source is managed in CVS and Hg, you can use Git locally to track the official repository. Doing so offers many advantages to the development process. You can easily work on multiple bugs at once, you can stash your code or make a new branch to perform a review on the side, and you can easily keep your work synchronized on multiple computers.

Warning: these instructions were written without the need to commit to the original source repository (CVS or Hg); if you are committing to Camino from the git-managed tree, you have to be very, very careful.

Prerequisites

This article was originally written when Camino development happened solely in CVS. Currently, Camino development takes place in a Mercurial repository, while some older branches are still in CVS. Generally speaking, these instructions work equally well for interacting with Hg and CVS repositories; simply substitute the equivalent Hg command when a CVS command is provided.

This article assumes you have already checked out a fresh copy of the Camino source. Follow our build instructions to do this. Note: you don't need to build yet, so after setting things up you can issue the following command to checkout the source only:

$ make -f client.mk checkout MOZ_CO_PROJECT=macbrowser

Note: Throughout the article /PathToCaminoSource/ is the directory that contains a mozilla/ subfolder with the source tree, and a CaminoObj/ folder for all the build objects.

Installing Git on Mac OS X

The short and sweet steps to install Git on the Mac:

Note: Replace the "1.6.2.1" Git version below with the most current release. Check http://git-scm.com/ to find out what the current revision is.

$ curl http://kernel.org/pub/software/scm/git/git-1.6.2.1.tar.bz2 | tar xj
$ cd git-1.6.2.1
$ ./configure
$ make prefix=/usr/local
$ sudo make prefix=/usr/local install
$ curl http://www.kernel.org/pub/software/scm/git/git-manpages-1.6.2.1.tar.bz2 | sudo tar xj -C /usr/local/share/man

(The last line installs the documentation. Building it directly requires a mess of dependencies ;)).

Binary Installer

Git can also be installed with a binary installer hosted at Google Code

Initial Git configuration

At minimum, you'll need to tell Git about yourself:

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

Additionally, some other settings can make interacting with Git more pleasant.

Display Git output with color:

$ git config --global color.ui auto
$ git config --global color.status auto
$ git config --global color.diff auto
$ git config --global color.branch auto
$ git config --global color.interactive auto

Make some shortcuts for common commands, and support your svn habits:

$ git config --global alias.st status
$ git config --global alias.ci commit
$ git config --global alias.co checkout

e.g. git checkout can be shortened to git co

Excluding certain files from being tracked by Git

Just like .cvsignore and Mozilla's .hgignore, we'll create an ignore file for Git to exclude certain files we do not want it to manage.

Exclude files for all repositories

You can create a global ignore file to always exclude files from any Git repository on your machine. In here we'll list certain Mac development related files we'll never want Git to see and ask us if we want to track it.

Save this file as ~/.gitignore

	.DS_Store
	*.orig
	*.rej
	*~
	
	# The following ignores all files in the .xcodeproj bundle,
	# And then adds back in only the project (skipping the user files)
	*.xcodeproj/
	!*.xcodeproj/project.pbxproj
	
	*~.nib
	*.swp
	
	*.o
	.#*
	a.out

Then, we have to configure Git to use our global excludes file:

$ git config --global core.excludesfile ~/.gitignore

Exclude Mozilla specific files

Git will also look for a .gitignore file in the root directory of each repository. In here we can exclude additional files that are specific to Camino development, ones that we would not want to ignore across the entire system.

Save this file as /PathToCaminoSource/.gitignore

	# Exclude certain Mozilla files (user specific / auto generated)
	.mozconfig.mk
	.mozconfig.out
	configure$
	config.cache
	config.log
	ID
	Makefile
	config.status
	.client-defs.mk
	config-defs.h
	unallmakefiles
	nss
	mozilla-config.h
	mozconfig
	.mozconfig

	# Empty marker file that's generated when we check out NSS
	security/manager/.nss.checkout

	# We ignored Xcode's build/ directory globally in Git
	# But the mozilla source has */build/ directories we'll need.
	# Cancel the ignore, then exclude only the camino Xcode build directory.
	!build
	*camino/build

	# Ignore CVS metadata
	CVS
	.cvsignore
	cvsco.log

	# Ignore Hg metadata
	.hg
	.hgignore

	# Build objects
	dist
	*.pyc
	CaminoObj

Initializing your Git repository

We're now ready to create a new Git repository. I use the mozilla/ directory inside of my Camino source tree as the root of my repository

$ cd /PathToCaminoSource/

$ git init

This creates an empty Git repository. At this point, issuing a status command shows which files Git sees but is not yet tracking:

$ git status

	# On branch master
	#
	# Initial commit
	#
	# Untracked files:
	#   (use "git add <file>..." to include in what will be committed)
	#
	#	.gitignore
	#	mozilla/

Note: We do want to manage the .gitignore file, as otherwise we'd need to hand create it and keep it in sync on each clone of the repository.

Now we're ready to add all of the files into the Git repository.

$ git add .

Now, showing status lists all of the files that Git has staged to commit:

$ git status

	# On branch master
	#
	# Initial commit
	#
	# Changes to be committed:
	#   (use "git rm --cached <file>..." to unstage)
	#
	#	new file:   mozilla/browser/config/version.txt
	#	new file:   mozilla/docshell/Makefile.in
	#	new file:   mozilla/docshell/base/Makefile.in
	#	new file:   mozilla/docshell/base/crashtests/369126-1.html
	#	new file:   mozilla/docshell/base/crashtests/403574-1.xhtml
	#	new file:   mozilla/docshell/base/crashtests/crashtests.list
	#	new file:   mozilla/docshell/base/nsAboutRedirector.cpp
	#	new file:   mozilla/docshell/base/nsAboutRedirector.h
	#	new file:   mozilla/docshell/base/nsCDefaultURIFixup.idl
	#	new file:   mozilla/docshell/base/nsCDocShell.idl
	#	new file:   mozilla/docshell/base/nsDSURIContentListener.cpp
	#	new file:   mozilla/docshell/base/nsDSURIContentListener.h
	#	new file:   mozilla/docshell/base/nsDefaultURIFixup.cpp
	...

Finally, create a new, initial commit

$ git commit -m "Initial import"

Day to day Camino development with Git

Now, this is where the real fun begins, and when you notice that all the above setup was extremely worth the effort!

Creating a branch to work on a bug

Git, by default, will create a master branch. This is where I keep the official, untouched source from Camino's CVS repository.

When I want to begin work on a certain bug, I will create a new branch. The following command is a shortcut to create a new branch and also check it out:

$ git checkout -b NameOfCurrentBug

Now, you can edit away. Anytime you need to switch contexts, maybe to work on a different bug that suddenly came up or to step aside and review a patch, you can just keep creating branches to do this:

$ git checkout -b NameOfCurrentBugToReview master

You can list the branch you want to stem from as the last argument. In this case, we weren't on the master branch, so we explicitly told Git to branch from there rather than defaulting to the current one.

Staying in sync with the Camino CVS repository

Your Git repo's master branch is our mirror to the pristine CVS source. Whenever you want to sync with the official Camino repository, just checkout your master branch, perform an update through CVS, and commit the changes:

$ git checkout master
$ cd /PathToCaminoRepository/mozilla
$ cvs update -dP client.mk
$ make -f client.mk checkout

Many times though, you only need/want to update the mozilla/camino/ tree:
$ cd /PathToCaminoRepository/mozilla/camino
$ cvs update -dP

Optionally, you can easily see what was changed upstream:
$ git status

Finally, add/commit the updates into Git:
$ git add .
$ git commit -m "Sync with CVS"

Incorporating the latest CVS changes into your working branches

We now have pulled the latest CVS updates into Git, but the branches that we're working on need to be merged with the latest updates as well, which are kept on the master branch. For this, we can use the git rebase command.

$ git checkout NameOfBugBranch
$ git rebase master

You can read more about git rebase, and how it differs from git merge.

Working on multiple Macs

One of the other huge advantages of using Git locally is that it easily makes it possible to develop the same feature across multiple machines, keeping all of your work in sync.

On your other Mac, we'll clone the original repository:

(SecondMac)$ git clone ssh://FirstMac/~/PathToCaminoRepository PathToLocalCaminoRepository

When you clone, Git sets up a remote corresponding to the original machine, called origin. You can use this remote identifier to push and pull updates to and from your other Mac.

To check out branches that you had on the other machine:

(SecondMac)$ git checkout -b BranchName origin/BranchName

	Branch BranchName set up to track remote branch refs/remotes/origin/BranchName.

This output means that Git automatically configured this new branch as a "tracking" branch, and any remote operations will know to work against the branch on the original machine. Learn more about what that means here.

Edit files, then commit the changes locally:

$ git commit -am "Changed a few files"

Push my updates back onto the other machine where we cloned from:

$ git push

Or, on the other end, use git pull and git fetch on the original machine. See the community Git book for more information about the distributed workflow in Git.

The Gotcha: Communicating with CVS on your other Mac

Our Git repository does not track or keep a copy of the CVS metadata. This means that when we clone the repository onto another machine, we are unable to actually sync or otherwise communicate with Camino's CVS repo on any secondary machine. Tracking the CVS/ information creates too much noise in the Git repository and I haven't found any sane way to keep this data in Git.

Therefore, anytime you need to interact with the official CVS repository, it has to be done on the original machine. To sync with upstream changes on the Camino trunk, you first need to cvs update on the original Mac commit the changes into the master branch like always (this can be done over SSH since you're remote). Then, you can then git fetch/pull the updates through origin/master branch on your remote repository.

Performing cvs diff and creating patches must also be done on the original machine, unfortunately. To do this, just push or pull your changes onto the machine with the original repository (and CVS information), and then perform a cvs diff from the Git branch your changes are on. (It is possible to perform a diff with Git, from your working branch to a freshly synced master, but the patch will appear like it's from Git and I'm not sure if this is acceptable or not, so I usually just do the regular CVS routine).

Automatically updating Camino.xcodeproj when switching branches

When switching back and forth between branches, the working copy of the Camino Xcode project will become out of sync. This is because it's kept in $OBJDIR/camino and Git is managing and modifying files in the source directory ($topsrcdir/camino).

You can use a Git hook to automatically update the Xcode project in the objdir when a checkout command is performed. This is for most times when a complete rebuild (using make in $OBJDIR/camino) is not necessary, but rather just a sync of Camino.xcodeproj.

The steps to create a hook are simple:

In your Camino git repository, go inside the .git folder. Then, you'll see a hooks subfolder inside of there. Create a new file titled "post-checkout" with the following contents:

#!/bin/bash
cp -v Camino.xcodeproj/project.pbxproj ../../CaminoObj/camino/Camino.xcodeproj/

Finally, make the file executable. (Note I use the "-v" argument to let cp give me some feedback that the copy is taking place. This explicitness means I won't forget the hook is in place and wonder a month later what's going on!)

Now, whenever you switch branches your working copy of the Xcode project will stay in sync with any changes in the given branch.

Example output:

Murph@~/Software/Camino/mozilla/camino (reporting)$ git checkout master
Switched to branch "master"
Camino.xcodeproj/project.pbxproj -> ../../CaminoObj/camino/Camino.xcodeproj/project.pbxproj

Committing to the Camino repository

Warning: these instructions were written without the need to commit to the original source repository (CVS or Hg); if you are committing to Camino from the git-managed tree, you have to be very, very careful.

Appendix: Misc Git tips

Bash enhancements for use with Git

Tab completion

Git includes a bash script that offers tab-competion of commands, branch names, etc. It's easy to install and a joy to have.

  1. Look for the "git-completion.sh" file. It is usually found in git-x.x.x.x/contrib/completion/, but I think recently a Git patch has it moving to /usr/local/share/git-completion/ during installation.
  2. Copy this file to somewhere (e.g. ~/.git-completion.sh).
  3. Added the following line to your .bashrc:

source ~/.git-completion.sh

Branch name in prompt

Additionally, it is extremely helpful to also have your bash prompt list the current branch you are on. Enable this by doing the following:

  1. In your bash profile, add the following to your PS1:

$(__git_ps1 " (%s)")
(If you aren't in a Git repo, it won't display anything).