[ImageJ-devel] Helper script for fixup commits

Johannes Schindelin schindelin at wisc.edu
Mon Feb 4 17:55:29 CST 2013


Hi Curtis,

as promised, a little Perl script to help with fixup commits.

For everybody who wonders what I am talking about: my preferred workflow
involves topic branches for projects involving more than one commit
(single commits are not worth the hassle). For this, I start with

	git checkout -b <name>

(Actually, very often I do have changes already, but that's okay, I can
still make a new branch from the current state.)

Then I make changes and commit and rewrite the topic branch frequently.

For example, when I find that I made a mistake in the first commit, but I
am already on the fourth commit in the topic branch, I commit a "fixup":

	git commit --fixup HEAD^^^

This tells Git to look at the third-last commit, take the first line,
prepend it with "fixup! " and commit with that message. Such a
specially-crafted commit message is interpreted by "git rebase -i
--autosquash" to mean that it should reorder the commits so that the fixup
commit can amend the original one.

After a couple of commits, my topic branch would look a bit like this:

	<hash>   <first-line>
	01234567 Fix typos in the README
	cafebabe Format LICENSE
	fedcba98 fixup! Fix typos in the README
	223387ab Add another author

When calling "git rebase -i --autosquash origin/master" -- assuming that I
started my topic branch directly on origin/master -- Git would present me
with an "edit script" like this:

	pick 01234567 Fix typos in the README
	fixup fedcba98 fixup! Fix typos in the README
	pick cafebabe Format LICENSE
	pick 223387ab Add another author

The "fixup" means that the second commit will be applied, but these
changes will be merged into the first commit.

I could ask rebase for further changes, such as "reword"ing the rather
unhelpful commit message of the now-last commit, but usually I keep things
as they are with the autosquash'ed reordering.

If you grow tired -- as did I -- of having to type "--autosquash" all the
time: do what I did:

	git config --global rebase.autosquash true

That will make that rather helpful behavior the default.

I try to document such things on the Fiji Wiki, of course:
http://fiji.sc/Topic_branches

Back to you Curtis: I did write that Perl script which finds out what
commit you should rebase onto, but the truth is that

	git rebase -i --autosquash $(git merge-base HEAD origin/master)

would work just as well: interactive rebase is clever enough not to
rewrite the first commits when there is no need to. In other words, it
does not re-commit any commit when it would have the same parent.

Ciao,
Dscho
-------------- next part --------------
#!/usr/bin/perl

my $countdown = 100;
my %fixups = ();
my $commit = '';

open(my $in, '-|', 'git', 'log', '--format=%h:%s', 'origin/master..');
while (<$in>) {
	s/\r*\n*$//;
	if (/^([0-9a-f]*):(.*)$/) {
		my $hash = $1;
		my $oneline = $2;

		if ($oneline =~ /^fixup! (.*)$/) {
			$fixups{$1} = 1;
		} elsif ($fixups{$oneline} ne undef) {
			$commit = $hash;
			delete $fixups{$oneline};
		}
	}
	if (0 < keys(%fixups)) {
		$countdown = 100;
	} else {
		$countdown--;
		if ($countdown <= 0) {
			last;
		}
	}
}
close($in);

if ($commit ne '') {
	system('git', 'rebase', '-i', $commit . '^');
}


More information about the ImageJ-devel mailing list