tags:

views:

119

answers:

2

In the good old days of Subversion, I would sometimes derive a new file from an existing one using svn copy. Then if something changed in sections they had in common, I could still use svn merge to update the derived version.

To use the example from hginit.com, say the "guac" recipe already exists, and I want to create a "superguac" that includes instructions on how to serve guacamole to 1000 raving soccer fans. Using the process I just described, I could:

svn cp guac superguac
svn ci -m "Created superguac by copying guac"
(edit superguac)
svn ci -m "Added instructions for serving 1000 raving soccer fans to superguac"
(edit guac)
svn ci -m "Fixed a typo in guac"
svn merge -r3:4 guac superguac

and thus the typo fix would be applied to superguac.

Mercurial provides an hg copy command that marks a file as a copy of the original, but I'm not sure the repository structure supports a similar workflow. Here's the same example, and I carefully only edit a single file in the commit I want to use in the merge:

hg cp guac superguac
hg ci -m "Created superguac by copying guac"
(edit superguac)
hg ci -m "Added instructions for serving 1000 raving soccer fans to superguac"
(edit guac)
hg ci -m "Fixed a typo in guac"

I now want to apply the change in guac to superguac. Is that possible? If so, what's the right command? Is there a different workflow in Mercurial that achieves the same results (limited to a single branch)?

+5  A: 

There's no pure-mercurial way to go cross-file with your patches, but if patch is installed on your system you could achieve essentially the same thing by following up your series of mercurial commands with:

hg log -p -r tip -I quac | patch superquac

That's essentially saying: "take the diff (-p) that was applied to file quac (-I quac) in the most recent changeset (-r tip) send it to standard output (hg log), and use that as the input to the patch (| patch) command acting on file superquac (superquac).

Ry4an
Hm... For a non-hg solution, this is nice and concise. Also makes me wonder if an hg extension might be in order. Thanks!
Stephen
Mayhaps. I skipped svn, going from CVS straight to mercurial, so I never knew svn had that functionality. Honestly it sounds like something that would discourage good refactoring of the sort which obviates making the same patch in multiple files.
Ry4an
+4  A: 

You can do this by

hg cp guac superguac
hg ci -m "Created superguac by copying guac" # CS1
(edit superguac)
hg ci -m "Added instructions for serving 1000 raving soccer fans to superguac" # CS2
hg up -r revision-before-copy
(edit guac)
hg ci -m "Fixed a typo in guac" #CS3
hg merge # this will transfer the typo-fix both to guac and superguac
hg ci -m "merged typo-fix from guac" # CS4

After this the repository looks like this

CS1 <--- CS2 <--------- CS4
  \                     /
   \--<-------- CS3 -<-/
Rudi
Oh, that's sneaky... (though I guess from the correct point of view it's not) So your solution creates multiple heads and then merges them. But at a deeper level, this method "inserts" a change in superguac's history out of chronological order. That's really cool, and I think my mental model of DVCS just improved! Thanks!
Stephen
One of the clarifying points that DVCS has over CVCS is that the latter forces you to look at the commit history more linearly, and in term of "revisions". The former has a DAG at the heart of its history model, which is way more flexible, and you should think of commits as sets of "changes".
Santa
@Santa I'd read about the DAG, but I guess I hadn't really internalized it yet. This is a really good example to help me anchor what the DAG really means in DVCS (and I'm also sharing it around the office to help others grasp the concept).
Stephen