This post is from the CollabNet VersionOne blog and has not been updated since the original publish date.
Deconstructing a Branch — Rolling Up Our Sleeves, Battling the Beast (Part 5 of 5)
This is the final installment in the continuing saga of cleaning up the feature branch I put through the Osterizer. In previous episodes, we’ve decided a GUI tool would be handy, surveyed some available GUI tools, and explored the power and quirks of our likely choice. It’s now time to roll up our sleeves and get some work done!
As I’ve mentioned before, this will be a detailed, step-by-step walk through of some actual (artificial) data. You really might want to download the repo I’lll be using, so you can play along. Here’s the repo, and here’s its GPG signature.
Within this repository, you’ll find four branches:
- master, used as the actual release for the product
- br1, a major feature branch begun some time ago
- br2, a minor feature branch, related to the br1 work
- br2.1, a small branch for some side work related to br2
At the time br2 was created, it was based on existing work in br1 and was planned for release along with br1. But then, Reality happened: br1 was delayed for a massive growth in scope, becoming far more wonderful, but also far later. Meanwhile, customer demand for the features of br2 was becoming strong. The decision was made to reorder the releases, shipping br2 first, without the br1 stuff. By this time, however, br2 contained substantial amounts of br1 work, which ought not to be shipped And thereby hangs the tail.
We created a new branch, containing all the br2 and br2.1 work, but excluding (as much as possible) the br1 work. We did this primarily through git’s cherry-pick feature, applied and managed through the git GUI tool. Along the way, we ran into several problems that forced us to work around, or even restart the process again. My principle goal in this blog is to point out these pit-falls, so you don’t run into them yourself.
At this point, you should unpack the sample repository. If you’re on Unix, Linux, or OS X, you should be able simply to double-click therepo.tgz, to produce a directory tree named therepo. I suggest you open two shell windows (xterm, Terminal, or whatever you like).
In the first shell window, run git branch. You’ll see the four branches I’ve mentioned, and the branch br2 will have a “bullet”, indicating that it’s your current branch. This is the branch with all the stuff we want, and some stuff we don’t.
In this same window, run gitk. A new window will open up. (You may get some inscrutable messages in this shell window, from time to time; you can ignore them so long as the gitk window behaves properly.)
What you see
Look first at the panel in the upper left. This is the primary control point. There’s a graphical representation of the history of this repository, with each change represented by a dot. There’s also a list of the comments associated with each change.
Caution: you’ll notice that every comment is annotated to show that the change was made “On branch br2” or whatever. I did this to make this blog easier. If you’re ever doing this for real, you won’t have this!
Find the starting point
We want the latest (nearest the top) version of branch br2 that is not polluted by undesirable br1 work. Scroll around a bit, and you’ll find that the very earliest reference to br2 is on row 50 (notice the “Row 50 / 71” indicator right below the column of author information).
Surprise #1: Notice that the changes on br1 and the changes on br2 appear to be in the same line, even though they’re different branches. In general, gitk doesn’t keep a given branch on the same line. Not only do branches wander among lines, as we see here, but there can be stretches when no line on screen represents a particular branch, even though the branch existed at that time, and has changes both earlier and later than that. So be very careful in determining which branch a change applies to!
OK, so, we want “br2 without any br1 stuff,” yet even the very first work on br2 was based on, and contains, br1 stuff. In our case, we were able to convince ourselves that the early br1 stuff was OK, wouldn’t actually appear on screen, so we ignored it. So we will take this line here, the first one that was actually made on br2, as the base of our new branch.
Click on that line, line 50 (if you haven’t already), so it’s grayed as I show here.
Right-click (or control-click, or whatever your favorite window system does to mean “open the contextual menu”). You should get this:
Pick “Create new branch,” and name it new-br2.
A new notation appears on the screen, showing the location of your new-br2:
Point your cursor at the “new-br2” marker (not the “make some changes” text), and right-click yourself a contextual menu again. It will be different from before:
Pick “Check out this branch.” This will switch your repository to looking at new-br2.
Surprise #2: At this point, your repository is looking at new-br2, but your gitk window is more or less still looking at (old) br2. This is a somewhat delicate situation. Don’t refresh the gitk window, or quit-and-restart, or you’ll lose this “straddling the branches” view, and it may be hard to get back.
What comes next?
What we’re going to do now is work our way upwards through the display, looking at successively more recent changes, and deciding, one by one, whether we want them in new-br2. For this blog, the answer’s easy — anything with the comment “On branch br2” or “On branch br2.1” is good; anything that says “On branch br1” or “master,” we skip. Basically. But don’t tune out just yet.
Use the keyboard up arrow, or click with the mouse, to select line #49, another “On branch br2.” Do we want this? Yes, we do: it’s br2. So context-menu on that line and cherry#####
Surprise #3: Before you cherry-pick, you should mark the current line:
Why? Well, here, let me show you: context menu again, and now do pick “Cherry-pick”:
Whoa! What happened? The graph scrolled up to the top, and started new-br2 up there.
Meanwhile, the gray marker showing which commit we considered last is gone. That would make it much harder to figure out which commit to do next!
Cherry-picking means plucking one commit from one branch, and attaching it to another. I guess it should be called “cherry-transplanting.” But, it’s not.
Good thing we marked it! So now, either use the context menu “Return to mark,” or just scroll around until you spot the commit with a box around it:
So, remember: mark first, then cherry-pick!
Up arrow or click to the next one. Do we want it? Sure do: it’s br2. Mark it. Cherry-pick it. Back to the mark. Up-arrow through a few br1 changes–we don’t want those!
This brings us to line 44:
Now this one says “br2,” but do we want it? Actually not. Notice the graph: this change represents merging some stuff from br1 to br2. We just carefully skipped over those br1 changes, it would be a shame to drag them in via this merge! So, don’t.
Or, actually, just for drill, do cherry-pick this merge. What you’ll see is this:
OK, that’s a relief: it refuses to do the merges. So if you ever git caught up in the rhythms of the process and do try to cherry-pick a merge, you’re safe. (You really are “safe.” Yes, I know the alert a chock-full of words like “fatal” and “abnormal,” but in this case that’s exactly what we wanted.) OK that, and on we go.
Next (line #43) is some work done “on branch master.” Skip those, too. Carry on skipping br1, and mark-and-picking br2 a bit, until you get to line #39, and this:
What this means is that some changes in this commit attempt to change the same lines as some other changes. In context, what it really means is that, originally, these changes built on top of some br1 stuff we’ve been skipping. Well, now the hens come home to roost: we have to straighten this out.
I recommend you not use “git citool” here (just cancel it), but instead go to your second shell window. You remember that second shell window I had you o pen? Now you know why.
In the second shell window (if you haven’t already), cd into the repository. Just for confirmation, try a git branch here, and see the new “new-br2” branch, and the bullet indicating it’s the current one.
Now, use git status to see what the problem is:
OK, so notice that “both” branches modified the file A/B/E/alpha. Pull that file into your favorite text editor, and you’ll see a region like this:
The part between “<<<<<< HEAD” and “======” (i.e., the nothing) indicates that the new-br2 branch we’re building up doesn’t have anything at that point. Maybe something was deleted. Correspondingly, the part between “======” and “>>>>>>>>c0e0dfa” is the stuff we’re bringing in from br2 into new-br2.
This is not the place to fully explore git merge conflicts. For purposes of this blog, let’s just say that we want the line that begins “apply, the main one”. Remove the marker lines and the nothing between “<<<<” and “=====”, save that out, git add A/B/E/alpha, and git commit.
The trick that isn’t
If you proceed in this way, you’ll eventually come to some changes that were made “On branch br2.1.” I said, above, that we do want these changes, so we must “mark and cherry-pick” them somehow or other, right? Right. But they’re on Some Other Branch, that must introduce some new complications, right?
Nope. Just “mark-and-pick” like always. Cherry-picking can take individual changes from nearly anywhere, in just the same way.
Repeat until done
That’s about it. Keep advancing from one commit to the next, deciding whether you want it, marking, cherry-picking, and occasionally resolving merge conflicts.
Remember: If you can keep your head about you when all around are losing theirs, … Yours is the Earth and everything that’s in it!
Thanks for keeping up to speed in my 5 part blog series on this subject matter. In case you missed any of the installments, click below to catch up!
Did you know we offer free Git hosting? Try a free trial of Git hosting on CloudForge here.