My (current) view of using branches in VCSes

March 25, 2013

In a comment on this entry, Aristotle Pagaltzis asked:

(Though I admit I wonder why you do have fred-1 and fred-2 [source directories] rather than branches in your VCS.)

The simple answer is that my favorite way of changing branches is with cd. This is especially the case if I'm developing things in parallel and may well wind up throwing one of them away; for good reasons VCSes make totally deleting a branch much harder than a plain 'rm -rf'. I'll admit that part of my preference is because I haven't yet gotten around to master branching in either git or Mercurial (partly because I frankly don't entirely like it or need it yet).

(Some people will say that you should keep even experimental stuff that didn't work out in your VCS in case you ever want to go back to it later. This may work for them but it doesn't work for me; I want my VCS to be neater than that.)

But even without that I think cd is much easier than going back and forth between branches in the same directory hierarchy, especially if you're developing on both branches. With today's VCSes, flipping back and forth between branches generally wants you to have actually committed your work to both of them and this itself leads to messy history (or to a lot of redoing commits or the equivalent, as you commit only so that you can flip branches, then annul and overwrite the commit the next time). I also personally think that it is a cleaner and more natural model for (multi-)branch development, with the only downside being more disk space being used.

(And disk space is generally cheap unless you're dealing with huge repos. The two largest repos I have handy are Mozilla and the Linux kernel; Mozilla is 2 GB and Linux is 1.2 GB. That's not going to break the bank on modern machines.)

I understand why VCSes have branch-switching commands (they can't not have them, to put it one way) and the benefits of having multiple branches in the same repo (including things like being able to do easy diffs between branches). But it just doesn't fit into the way that I prefer to interact with VCSes and I like to keep my life simple.


Comments on this page:

From 84.112.126.145 at 2013-03-25 05:07:29:

That's how bzr works, a different directory for each branch. Unfortunately, it doesn't work too well with Go. Go won, for me.

From 87.79.78.105 at 2013-03-25 21:03:39:

VCSes make totally deleting a branch much harder than a plain rm -rf

git branch -D

Slightly longer and has a shifted character. That doesn’t meet my bar for “much”, maybe “slightly”.

With today's VCSes, flipping back and forth between branches generally wants you to have actually committed your work to both of them and this itself leads to messy history (or to a lot of redoing commits or the equivalent, as you commit only so that you can flip branches, then annul and overwrite the commit the next time).

First: you don’t have to have your work committed in Git, unless a file you have modified in your work tree also has differences between the two branches, i.e. Git would have to overwrite your changes in order to check it out. This means that a lot of the time, you can switch branches without checking anything in; although it does also mean that modifications to your work tree will persist across branch switches.

This, OTOH, makes it very easy to migrate some or all of the modifications you started making on one branch to another (possibly entirely new) branch, if after the fact you realise they should go elsewhere. If you’re using multiple directories, achieving that is of a type of “much harder” that meets my bar for “much”.

If you do want to stow away the changes, Git offers the stash to do that with no further ado. It’s creating commit objects under the hood of course, but I don’t see why that is inherently an issue.

Admittedly, when I am going to be leaving the changes aside for a long time, then I will in fact generally git commit -mwip -a. Then upon return to it I’ll keep doing git commit --amend -a. There will then be a git reset HEAD^ at some point in my future. So I do in fact do what you said, which is create and annul temporary commits. But I don’t see why I should avoid that; esp Git predicates itself on commits (and branches) being throw-away cheap, in order to make it easy for published history to be made pristine. As for in-/convenience, well, judge for yourself how hard those commands are to juggle.

The major downside to using multiple directories is that you have multiple clones, which means you must manually push and pull commits across in order to exchange committed work between them. That in turn also means that you are prematurely forcing yourself to follow the rules of published history, unless you comfortable wielding the arcana of manipulating commit trees constantly. (Or, you must use multiple work trees that share the same repository, in which case you must be very careful to coordinate changes made to a branch that is checked out in some other work tree.)

To me that all is a far bigger hassle than contending with WIP commits.

Aristotle Pagaltzis

From 91.57.167.51 at 2013-03-26 06:33:48:

For git you might try the script git-new-workdir (included in the git repository in contrib/workdir). Works like a charm for working on several branches of the same repository at the same time. Just take care you never check out and work on the same branch in several workdirs at the same time.

By cks at 2013-04-01 15:35:32:

(Belated reply time)

I admit that part of my uncertainty with things like git branch -D is that I'm never entirely sure what simply deletes HEAD markers and so on and what actually removes the commits and related objects. But this is really my issue, not git's as such; if I knew what it did, it would probably be as simple as rm -rf is now.

This means that a lot of the time, you can switch branches without checking anything in; although it does also mean that modifications to your work tree will persist across branch switches.

In my work style that's not what I want. If I'm working on two alternatives that I'm comparing, the natural state is that both alternatives clash (often they modify the same files) so when I switch between branches (with cd) I definitely want my modifications to change. I can't see a natural way to do this short of WIP commits (stashes would have me making and remaking stashes and trying to keep track of which stash went with which branch).

The major downside to using multiple directories is that you have multiple clones, which means you must manually push and pull commits across in order to exchange committed work between them.

In my current work style this hasn't been much of an issue. Because the directories are experiments with alternatives (or experiments with things that I'm not sure I'm going to keep) I don't exchange changes back and forth between them; at most I pull things in from a common upstream source. If I declare something good it propagates back into the upstream (or becomes the new master version if I throw away the other repo(s)). If a particular change in a branch directory was the kind of thing I was sure I wanted to keep but I wasn't done with the branch, I'd push it upstream and then pull it into the other repo from there.

Written on 25 March 2013.
« Looking at how many external recipients inbound email goes to
Rethinking avoiding Apache »

Page tools: View Source, View Normal, Add Comment.
Search:
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Mon Mar 25 01:20:58 2013
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.