Differences between revisions 6 and 7
Revision 6 as of 2006-12-18 19:03:17
Size: 2596
Editor: BrendanCully
Comment: brain-dump of transplant merge idea
Revision 7 as of 2006-12-19 20:00:17
Size: 2796
Editor: BrendanCully
Comment: Flesh out transplant merge
Deletions are marked like this. Additions are marked like this.
Line 59: Line 59:
      a -> b -> c1 -> d -> e -> f
      a -> B -> C -> D -> E -> f
      node [shape=box]
a -> b -> c1 -> d -> e
      a -> B -> C -> D -> E -> F
Line 62: Line 63:
      c1 [shape="doublecircle"]       e -> F [constraint=false]
c1 [shape="circle"]
Line 66: Line 68:
To produce the merge node f, we'd start with a merge base of a, and merge local node C with remote node b. When the merge was complete, we'd use that as a merge base for the remaining nodes. To produce the merge node F, we'd start with a merge base of a, and merge local node C with remote node b to produce CM. We'd then use CM as a merge base for e and E to produce F:


{{{#!dot
   digraph {
     rankdir=LR
     node [shape=box]
     a -> b -> CM -> e -> F
     a -> C -> CM -> E -> F
     CM [shape="circle"]
   }
}}}

This extension allows you to transplant patches from another branch. It records the original changeset ID in the transplanted changeset, and avoids transplanting previously-transplanted patches. It can also be used to rebase a branch against upstream changes (including dropping changesets that have been adopted upstream).

Using transplant to rewrite changesets

The transplant extension accepts a --filter option, which lets you edit the changelog message and patch before applying it to its destination (they are supplied to the script as $1 and $2, respectively). For example, you could add a "Signed-off-by: " header to each changeset with a script like this:

cat <<EOF >> "$1"

Signed-off-by: Me
EOF

As a slightly more complicated example, I used the following script to import my transplant extension from a standalone repository into mercurial's hgext folder (transplant uses git-style patches, so a little extra work was required):

import.sh:

MESSAGE="$1"
PATCH="$2"

sed -e's,^\(--- a/\)\|\(+++ b/\),&hgext/,' \
    -e'/^diff --git/s,a/\(.*\) b/\(.*\),a/hgext/\1 b/hgext/\2,' \
    -e's,^\(rename\|copy\) \(from\|to\) ,&hgext/', \
    -i "$PATCH"

I then did the import like so:

hg transplant -s ../transplant --filter import.sh 0:tip

Merging with transplants

Three-way merge doesn't cope particularly well with transplanted patches - it will tend to generate false conflicts. Here's a possible strategy for handling transplant merges that I mean to integrate into the transplant extension soon:

  1. Walk forward from the merge base to the first node that is transplanted from one tree to the other.
  2. Find the source node on the other tree.
  3. Perform a merge where the local node is the node found above (whichever is on the local branch) and the remote node is the "parent" of the corresponding node.
  4. Use the result as the merge base, skipping over the remote node found above, and repeat until the target heads are merged.

In the following example, c1 is transplanted from C.

To produce the merge node F, we'd start with a merge base of a, and merge local node C with remote node b to produce CM. We'd then use CM as a merge base for e and E to produce F:


CategoryExtension

TransplantExtension (last edited 2015-05-29 19:43:43 by mpm)