All celebrity gossip, all the time

Using Git locally for a Subversion-based project (like BuddyPress)

In the past, I’ve written extensively about using Git with WordPress projects. I’ve focused primarily on Git as the primary development channel, with SVN (in this case, plugins.svn.wordpress.org) used for distribution only.

In contrast, I use Git for all my local development on the BuddyPress project. In this case, BP’s “official” history is in its SVN repo. My local Git repo is just a mirror. This setup means that you need a different kind of workflow, one that gives precedence to the Subversion repository. I’ll be using BuddyPress as an example below, but a similar workflow will work for any Subversion-based project where you want to do local development in Git.

(Side note: Mark Jaquith published a similar guide on how he uses Git to do WordPress core development. His process and mine are independently derived, but they are, of necessity, conceptually similar.)

Git will crawl through the entire revision history of the BuddyPress project, which will take a while.

I generally have two active, ongoing branches in my local BP-Git repo, one corresponding to trunk and one corresponding to the current bugfix branch. I use master (the default Git branch) for trunk, since that’ll be the default setup after the clone. You’ll need to create the bugfix branch manually. I use the ‘1.6.x’ naming convention for the 1.6 SVN branch, etc.

If you’ll need to do development using BP’s bbPress 1.x implementation (“Group Forums”), you’ll need to manually download bbPress 1.1 into buddypress/bp-forums/bbpress/

Development

Day-to-day development goes something like this:

Make sure you’re on the right branch. Most day-to-day dev happens on trunk/master, but in some cases it’s necessary to work on the current bugfix branch. Once you’re on the right branch, make sure that you have no unstaged changes, and use git-svn-rebase to get the most recent changes from upstream:

$ git checkout 1.6.x
$ git svn rebase

Create a new topic branch for this bugfixing session. I generally name it after the ticket number.

When you’re done with your development, you’ll be in one of the following situations.

You’ve fixed the issue, and want to merge your commit history directly back into the public branch. This generally means that you fixed everything with a single changeset, or perhaps a small number of changesets that have good commit messages, etc. In that case, you can do a straight merge back to the public branch. Switch back, git-svn-rebase to make sure none of your collaborators have updated the SVN branch since you started working, and then merge.

$ git checkout 1.6.x
$ git svn rebase
$ git merge bp4453

You’ve fixed the issue, but you want to reduce a large number of changesets to a single commit. You have a few options here. You can use git rebase -i like Mark suggests. I generally do not do this, because rebasing on publicly shared SVN branches makes me nervous. Instead, in these cases I’ll use a squashed merge, which lays all of your changes on top of the destination branch, and leaves them uncommitted.

You want to share your changes with others, in the form of a patch, before committing to SVN. You’ll need to use the git diff utility, while doing a formatting trick to make sure that it’s compatible with the standard UNIX patch utility used in the SVN world.

$ git diff --no-prefix 1.6.x...HEAD > ~/path/to/patches/4453.01.patch

Assuming you are ready to send some commits up to SVN:

$ git svn dcommit

In some cases, you may have sent a commit to the bugfix branch that needs to be applied separately to the main dev branch (trunk). There’s a couple different ways you might handle this. I usually use git-cherry-pick:

The main differences between git merge --no-ff and git merge --squash, as I understand them, are this. git merge --squash will not actually do any commits. It generates a diff between the source branch and target branch, lays it on top of the HEAD of the target branch, and stages the changes. In contrast, git merge --no-ff will not only give you a merge commit (that’s the --no-ff bit), but it will also lay every changeset since the fork on top of the target branch. This can be fine if you are happy with your local commit history. But if you’re like me, then your topic branches are full of junk commits which serve about the same function as saving your game of Wolfenstein just before you go into fight a boss because you know you’re going to die like 20 times.

The git commit --amend trick just allows you to reset the most recent commit, restage it, and then recommit it with a new commit message. So when coupled with git merge --no-ff, it just allows you to change the commit message “Merging branch foo into bar” to something more meaningful.

Anyway, in a broader sense, I don’t think anyone looking at the history of the shared repo will particularly care whether you used a local branch to do development on a specific feature or bugfix, especially if you were the only one doing that development (you never pushed the topic branch up). In other words, allowing the merge to fast-forward seems like the desired behavior in this case – the extra merge commit doesn’t provide any real context that’s useful to future devs. In contrast, --no-ff *is* useful when a major shared topic branch is merged into another branch. For example, on the CUNY Academic Commons, when merging changes from a bugfix branch into the bleeding-edge master branch at release time (which is how I do it on a Git-only project, instead of cherry-picking), I generally use --no-ff so that you could look through the log of the master branch and tell which commits came from maintenance releases.

All this said, I don’t think --no-ff would cause any problems, but it will create extra commits in the SVN repo that don’t have much context (the merge commits). So I would avoid --no-ff on a project like BP.