Finding Uncommon Changesets
posted by Steve Losh on December 9, 2009
This post was inspired by an email on the mercurial-devel mailing list.
The question in a nutshell is: how can you determine which changesets are
included in revision X but not included in revision Y? This could be
useful if you’re updating a piece of software you use and want to know
everything that changed between the old revision and the new one.
It sounds like a simple task, but there are a few tricky things that you need to watch out for.
Note: In this tip I’ll be using the shortlog alias instead of the
normal hg log command to make the output more readable. Everything will work
exactly the same with hg log (because hg slog is just an alias to hg log
with an extra template), so feel free to use that instead if you prefer more detail.
The Simple (and Incorrect) Way
Let’s start with the simplest way to list revisions: the --rev option. Say
we have a simple repository whose graph looks like this:
$ hg glog
o 4 Fix another bug. (6 seconds ago by Steve Losh) tip
|
o 3 Fix a bug. (16 seconds ago by Steve Losh)
|
@ 2 Add another simple feature. (34 seconds ago by Steve Losh)
|
o 1 Add a simple feature. (40 seconds ago by Steve Losh)
|
o 0 Initial revision. (7 minutes ago by Steve Losh)
If you’re currently at revision 2 and want to know what changes would be
included if you updated to tip, you might use something like this:
$ hg slog --rev 2:tip
2 Add another simple feature. (5 minutes ago by Steve Losh)
3 Fix a bug. (5 minutes ago by Steve Losh)
4 Fix another bug. (5 minutes ago by Steve Losh) tip
You can see that the output includes revision 2, which really shouldn’t be
listed because it’s the one you’re already at. This probably isn’t a big deal
because you can just ignore it.
However, this approach doesn’t work if you have multiple branches in the repository that are being worked on simultaneously. For example:
$ hg glog
@ 6 Fix a critical bug. (1 second ago by Steve Losh) tip
|
| o 5 Start the rewrite of the UI. (24 seconds ago by Steve Losh) ui-rewrite
| |
o | 4 Fix another bug. (8 minutes ago by Steve Losh)
| |
o | 3 Fix a bug. (8 minutes ago by Steve Losh)
| |
o | 2 Add another simple feature. (9 minutes ago by Steve Losh)
|/
o 1 Add a simple feature. (9 minutes ago by Steve Losh)
|
o 0 Initial revision. (16 minutes ago by Steve Losh)
$ hg slog --rev 2:tip
2 Add another simple feature. (9 minutes ago by Steve Losh)
3 Fix a bug. (9 minutes ago by Steve Losh)
4 Fix another bug. (9 minutes ago by Steve Losh)
5 Start the rewrite of the UI. (54 seconds ago by Steve Losh)
6 Fix a critical bug. (31 seconds ago by Steve Losh) tip
Notice that the output of the hg slog command included changeset 5. It’s
on a separate branch, so updating from 2 to tip (6) won’t actually
include changes from 5.
This happens because when you specify a range of revisions Mercurial will step
through them in the revision-number order. That means saying 1:4 really
means “1, 2, 3, and 4 no matter which branches they happen to be on.”
We can do better than this by using some extra hg log options.
The Correct Way
If we step back and think about the problem we’re trying to solve, we can reduce it to a simple definition. We want to see all changesets that:
- Are included by (i.e. ancestors of) the destination revision.
- Are not included by the source revision.
To solve the first problem, we can use a combination of two options, --rev
and --follow, like so:
$ hg slog --rev tip:0 --follow
6 Fix a critical bug. (11 minutes ago by Steve Losh) tip
4 Fix another bug. (20 minutes ago by Steve Losh)
3 Fix a bug. (20 minutes ago by Steve Losh)
2 Add another simple feature. (20 minutes ago by Steve Losh)
1 Add a simple feature. (21 minutes ago by Steve Losh)
0 Initial revision. (28 minutes ago by Steve Losh)
Notice how the extra changeset on the other branch isn’t shown.
Using --rev DESTINATION:0 with --follow means “show me every changeset
that is an ancestor of DESTINATION“. Check out hg help log for the details
of what each option does.
We’ve still got one more thing to do — we want to remove changesets that are already included in our current revision from the list:
$ hg slog --rev tip:0 --follow --prune 2
6 Fix a critical bug. (16 minutes ago by Steve Losh) tip
4 Fix another bug. (25 minutes ago by Steve Losh)
3 Fix a bug. (25 minutes ago by Steve Losh)
That’s it! Changesets 6, 4 and 3 are the changesets that will take
effect if we update from 2 to 6 in our sample repository.
The general form for this command looks like this:
hg slog --rev DESTINATION:0 --follow --prune SOURCE
Of course you can use all the normal shortcuts for specifying revisions. If
you want to see would be included in an update to tip from the current
revision you’re working on:
hg slog -r tip:0 -fP .
Note: this command won’t work if you’re updating “backwards”. It can only
show you additional changesets that would be included — it will not mention
changesets that are included in SOURCE but not in DESTINATION.