One of Git’s defining characteristics is its extreme (some say “ridiculous”) flexibility. Even with all the fancy porcelain on top, what you’re get when you use Git is basically a general DAG builder for patches, and the ability to apply labels to points within.
It’s interesting to see how this flexibility is put to use in practice. In my many years (ok, months) of Git usage, across a variety of projects, I’ve noticed several distinct styles of Git usage.
The most salient differences between styles are:
- How much they care about keeping the development history “beautiful”, i.e.
free of unnecessary merges. Git gives you two tools for adding your commit to a
branch: merge and rebase. A rebase will always preserves linearity, a merge has
the potential for introducing non-linearity. Some projects are fanatic about
this. Linus has been known to reject code because there were too many “test
merges” (see the
git-rerereman page). Other projects don’t care at all. - How much they make use of topic branches. Some projects do the majority of development through them. Some do all development directly onto master, branching only for long-term divergent development.
- How new commits come into the system: patches to mailing lists, merges from remote branches performed by the maintainer, or commits directly into the central repo.
Each of these decisions results in a different style of development. The styles I’ve encountered in the wild are:
- The just-like-SVN approach. Example project: Rubinius. Individual contributers have a commit bit, or they don’t. Everyone works from local clones. If you have a commit bit, you push directly to origin/master. Non-committers can post patches to a mailinglist or to IRC. There are some published branches, but they’re for long-running lines of development that are eventually merged in and discarded. There’s no real pickiness about merges in development history; rebasing is encouraged but not required.
- The Gitorious / Github approach. Example project: everything on those systems. Only the maintainer can commit to the central repository. Anyone can create a remote clone, push commits, and send a formal merge request through the system to the maintainer. All code addition (except for the maintainer’s additions) are done through merges.
- The topic-based approach. Example projects: Git itself, the Linux kernel, Sup. Patches are submitted to the mailing list. The maintainer builds topic branches for each feature/bugfix in development and merges these into different “version branches”, which correspond to different versions of the project such as stable/experimental/released version distinctions. Sub-maintainers are used when the project gets large, and their repositories are merged by the maintainer upon request.
- The remote topic branch approach. This was an experiment I tried with Ditz, and is roughly my attempt to do topic-based Git with Gitorious. In this approach, contributors, instead of submitting patches to a mailing list, maintain feature branches themselves. When a branch is updated, a merge request is sent to the maintainer, who merges the remote branch into a version branch.
I’ve listed the styles in order from least to most overhead. The just-like-SVN
style requires very little knowledge of Git; at the other end of the spectrum,
the topic-based approaches require a fair amount of branch managment. For
example, care has to be taken that merging a topic branch into a version branch
doesn’t accidentally merge another version branch in as well. (This type of
complexity spurred me to write tools like git
show-merges and the soon-to-be-released
git wtf.)
The advantage of the topic-based approaches, of course, is that it’s possible to maintain concurrent versions of the same program at different levels of stability, and to pick and choose which features go where.
Which style is best for you depends on what you’re trying to accomplish. Like all good tools, what you get out of Git depends on what you’re willing to put into it, and that’s a decision you’ll have to make.