One of Arch's strengths is its merge commands. But there are a bunch of them, and it can be hard to know which to use in which situation.
One strategy, many commands
replay, update and star-merge can all use an apply-delta strategy to perform merges. star-merge has an additional strategy available.
replay REVISION is a special case of apply-delta-- it applies the delta between a revision and its ancestor to the current tree. replay VERSION just repeats the process for every revision in the version that doesn't have a patchlog in the tree.
update has two strategies, but both are based on apply-delta. update's default strategy determines the last revision with a patchlog in the tree, and the last revision in the archive, calculates their delta, and applies it. In some cases, it will use replay VERSION instead, but only as an optimization, when the result would be the same. It does undo before applying the changes, and redo afterward.
star-merge is the oddball here, because it has three-way merging as well as apply-delta. But almost everything that applies to the apply-delta strategy also applies to the three-way strategy.
Since all of these commands are based on apply-delta, the difference lies in which deltas they apply.
Commands in detail
replay REVISION
replay REVISION is useful when you don't want to merge. For example, when you only want to cherry-pick some of the changes from another tree. As noted above, it applies the delta of a given revision to its direct ancestor.
replay VERSION
replay VERSION will apply all revisions in VERSION that do not have a log in the current tree. This will include revisions that you've reversed out of the tree, and versions that were merges of revisions that are in the tree.
A better tool for merging is replay --skip-present VERSION. This will avoid replaying revisions that were merges of the current version, by looking at which revisions each patchlog adds. Since a merge also adds the patchlog of the original revision, and since you should still have that patchlog in your tree, it can say: does this changeset add any patchlogs which I already have? If so, it skips it.
replay --skip-present VERSION has a few downsides:
- After two people have merged each others' changes, their trees will look different, at least when it comes to patchlogs.
- If A merges B, but makes changes at the same time, B won't get them, because B will use his own version, not A's modified version.
- Since it applies multiple deltas, there's more opportunity to produce a conflict.
- It doesn't detect when someone has tagged after committing.
update VERSION
update can be useful for pull merging-- when A merges B, but B never merges A. It determines the delta by determining the latest revision with a patchlog in the tree, and the latest revision in the archive. This works fine until B merges A. Then, the next time A merges B, they'll be re-applying their own changes, which won't be productive.
star-merge
star-merge attempts to solve this by determining who merged from who last. It does this by comparing the patchlogs of the current tree with the patchlogs of the specified revision. (If you specify a version, it uses the latest revision of that version in the archive.)
When it's done, we've got three trees: the working tree, which we'll call MINE, the tree of the specified revision, which we'll call OTHER, and the tree of the revision we found by examining patchlogs, which we'll call ANCESTOR. At this point, it can do apply-delta ANCESTOR OTHER to MINE.
Or, if --three-way is specified, it will use diff3 to apply changes to MINE.
Things get a big hairier when you start looking at the details.
The first thing to know is that there's a third version, called REFERENCE. By default, this is MINE's version, but you can set it with a commandline switch. When star-merge is picking a revision, it will always pick one in REFERENCE. This revision is the last revision that merged OTHER, or was merged in OTHER.
You should also know that star-merge doesn't follow ancestry. Tags confuse it, and it reports that versions are unrelated. This can sometimes be fixed by overriding REFERENCE.
star-merge behaves badly when OTHER has cherry-picked changes from REFERENCE. The problem is that star-merge assumes the presence of a patchlog means that OTHER has merged everything up to that patchlog. It interprets the absence of the earlier changes as though the changes were deliberately removed from OTHER. So performing a star-merge in this situation will undo a bunch of changes from MINE.
Suggestions
Use star-merge by default. If it removes desirable changes, you can restore them with replay TREE-VERSION. If that doesn't work, try using replay --skip-present VERSION instead of star-merge.
If star-merge complains about versions being unrelated, try giving it a --reference argument. If that doesn't work, try update or use apply-delta manually.
Is star-merge the best we can do?
No, better things have envisioned. Unfortunately, they require the ability to add and subtract changesets, which is theoretically possible, but not implemented.
