1. Steve Losh
  2. hgtip-content


hgtip-content / advanced / 2010-04-23-debug-command-tricks.html

{% extends "_tip.html" %}
    title: Debug Command Tricks
    author_name: Friedrich Kastner-Masilko
    author_link: http://bitbucket.org/face
    created: 2010-04-23

{% block excerpt %}
Mercurial has some "hidden" debug commands that can make your day in special
{% endblock %}

{% block tip %}

If you tried `hg help --debug`, you may be familiar with the commands prefixed
with 'debug'. Most of them are used to inspect Mercurial's internal storage,
but some can come handy in certain situations. Two of these commands are `hg
debugsetparents` and `hg debugrebuildstate`.

The first command forces Mercurial to forget the "real" parent revisions of a
working-copy in favor of the specified revisions in 'debugsetparents'. The
second command is forcing Mercurial to rebuild its internal state.

So how can this be useful in normal conditions at all? Here are 3 scenarios
where it comes handy to work with them:

### 1. Late-binding working-copy with repository.

Imagine an interesting project hosted with Mercurial. At first you only
download a snapshot of the project to inspect the code and try something out.
You didn't clone it fully, because you wanted to avoid the overhead to just
check the source.

Later in the process of playing with the code, you realize that this is going
to be a longer-lasting relationship, so you decide to clone the repo and
commit your changes. Since it is quite a big working-copy and you
renamed/deleted/added some files, you don't want to copy the whole thing over
some checkout in order to not confuse the working-copy state. So how to commit
this state at the proper location?

The solution is to copy the .hg folder into the snapshot's root and issue:

    $ hg debugsetparent tip
    $ hg debugrebuildstate
    $ hg commit -Am "my snapshot"

### 2. Creating alternate check-ins.

If you are anything like me, you don't trust yourself. Therefore, you'd want
to add alternative work BEFORE you delete one of the alternatives. So the
usual workflow of using rollback to take back an ill-composed commit is not
your thing.

Instead, it is possible to simply:

    $ hg debugsetparent tip^^
    $ hg debugrebuildstate

And you are back at the state you where before you commited, just with the
"wrong" commit still there.

### 3. Simulate Git's --no-ff merges with anonymous heads.

Most of the workflows for Git are easily reproducible with Mercurial, but some
of them ([gitflow][]) use so-called "non-fast-forward" merges. These merges
can be done in Mercurial, too, but only with named-branches:

    $ hg sl
    $ echo x > x
    $ hg ci -Am "base"
    adding x
    $ hg branch feature
    marked working directory as branch feature
    $ echo y > x
    $ hg ci -m "feature"
    $ hg up default
    1 files updated, 0 files merged, 0 files removed, 0 files unresolved
    $ hg merge feature
    1 files updated, 0 files merged, 0 files removed, 0 files unresolved
    (branch merge, don't forget to commit)
    $ hg ci -m "merged"
    $ hg glog
    @    changeset:   2:e0502e1d12c6
    |\   tag:         tip
    | |  parent:      0:387ca709aeaf
    | |  parent:      1:205ec0dce78f
    | |  summary:     merged
    | |
    | o  changeset:   1:205ec0dce78f
    |/   branch:      feature
    |    summary:     feature
    o  changeset:   0:387ca709aeaf
       summary:     base

As you can see in the above graphlog output, the merge contains no actual
information besides indicating the merging of two named-branches ('default'
and 'feature'). As with Mercurial 1.5.1, you can't do that with anonymous

    $ echo x > x
    $ hg ci -Am "base"
    adding x
    $ echo y > x
    $ hg ci -m "feature"
    $ hg up 0
    1 files updated, 0 files merged, 0 files removed, 0 files unresolved
    $ hg merge 1
    abort: nothing to merge (use 'hg update' or check 'hg heads')

Why would you want to do that with anonymous heads, anyway? Well, one way of
simulating Git branches is the bookmark extension. Unfortunately, using
bookmarks (to name anonymous heads) is still using anonymous heads, so the
above problem gets in your way:

    $ hg bookmark master
    $ hg bookmark feature -r 1
    $ hg bookmark
     * master                    0:6965ee5bb884
       feature                   1:2061809a8e23
    $ hg merge feature
    abort: nothing to merge (use 'hg update' or check 'hg heads')

The solution:

    $ hg debugsetparents master feature
    $ hg revert -a -r feature
    reverting x
    $ hg ci -m "merged"
    $ hg glog
    @    changeset:   2:0dfae55684f6
    |\   tag:         master
    | |  tag:         tip
    | |  parent:      0:6965ee5bb884
    | |  parent:      1:2061809a8e23
    | |  summary:     merged
    | |
    | o  changeset:   1:2061809a8e23
    |/   tag:         feature
    |    summary:     feature
    o  changeset:   0:6965ee5bb884
       summary:     base

[gitflow]: http://nvie.com/git-model

{% endblock %}