obliterate functionality?

Martin Geisler mg at daimi.au.dk
Wed Mar 19 04:54:00 CDT 2008

"Paul Moore" <p.f.moore at gmail.com> writes:

> Thanks. I apologise if I sounded grumpy, it was getting late last
> night and I was struggling to see the technical content through the
> philosophy. My problem, not anyone else's.

No problem!

> Could I ask, at a technical level, if I have a file XXX in my
> repository, maybe added a few changes ago (possibly lots, on the
> case of the FreeBSD guys, but they can probably speak for
> themselves), how would I remove that file from my repository?
> 1. What commands would I use? hg strip was mentioned, but that looks
> like it would also remove any other unrelated changes made after the
> addition, so presumably I'd need to do some cleanup as well - how
> would I do that? Another option mentioned was hg convert, presumably
> I'd use a filemap to exclude the offending file, is there anything
> else I'd need to do (for example, if my repository was a private
> copy of a public project and I wanted to continue pulling after
> fixing my mistake)?

If the mistake is only in your private copy, then I would use the MQ
extension to "dig out" the bad changeset. Let's pretend you comitted
the file in revision BAD. Then doing

  hg qimport -r BAD:tip

will import the changesets into MQ. If you want to undo the entire
changeset BAD (obliterate it!), then do this:

  hg qpop -a
  hg qdelete BAD.diff

If you only want to edit the changeset, then do this:

  hg qpop BAD.diff
  # edit files, remove passwords, etc.
  hg qrefresh

To go back to standard Mercurial changesets you do

  hg qpush -a
  hg qdelete -r qbase:qtip

> 2. What implications might there be? For example, I mention above
> the case of a private copy of a public repository. Would I break the
> relationship between the two?

No, nothing bad will happen. Two repositories are related if they
share a common ancestor. The first commit made to a repository creates
the first hash value, and this is all that matters.

> Specifically, can it be made to *only* change the hashes of
> changesets from the introduction of file XXX onwards?

That is exactly what will happen -- in the example above, the hashes
are changed of the changeset BAD and the following changesets. As long
as nobody else has seen them this is okay.

If people have already pulled from your repository, then it becomes
more complex. Let us pretend your repository starts out looking like

  R1 --> R2 --> R3 --> BAD --> R4 --> R5

You edit BAD and end up with a changed repository:

  R1 --> R2 --> R3 --> BAD' --> R4' --> R5'

where BAD has turned into BAD', and so on. If someone pulls from your
changed repository into one that looks like your starting point, then
the result is simply:

                     /- BAD --> R4 --> R5
  R1 --> R2 --> R3 -<
                     \- BAD' --> R4' --> R5'

This is exactly what one would expect, Mercurial always works this way
when you pull in changes from others: It takes two directed acyclic
graphs (one representing your repository, the other representing the
repository you pull from) and merges common nodes in the graphs to
produce a new acyclic graph.

Notice that the changesets R4 and R4' and R5 and R5' are identical
changes (neither of you edited them), but they have different hash
values since they have different histories (because of BAD').

So you can simply ask your friend to strip BAD, which will strip R4
and R5 as well -- their changes are preserved in R4' and R5'. This
makes your friends repository identical to your:

  R1 --> R2 --> R3 --> BAD' --> R4' --> R5'

Furthermore, if your friend had already committed new work on top of
R5, then his repository looks like this before the pull:

  R1 --> R2 --> R3 --> BAD --> R4 --> R5 --> R6 --> R7

After the pull he get

                     /- BAD --> R4 --> R5 --> R6 --> R7
  R1 --> R2 --> R3 -<
                     \- BAD' --> R4' --> R5'

Now it is not simply enough to strip BAD since that destroys the work
in R6 and R7. But you can ask him to import R6 and R7 into MQ, pop the
queue, strip BAD and apply the queue again (this time to R5'):

  hg qimport -r R6:R7
  hg qpop -a
  hg strip BAD
  hg update -C R5' # might not be necessary
  hg qpush -a
  hg qdelete -r qbase:qtip

I have not tested this last recipie, but it (or something close to it)
should work :-)

With all this I just want to stress that Mercurial is very flexible,
and that it follows some simple rules.

Martin Geisler

VIFF (Virtual Ideal Functionality Framework) brings easy and efficient
SMPC (Secure Multi-Party Computation) to Python. See: http://viff.dk/.

More information about the Mercurial mailing list