How to adjust history after merge of two unrelated

2019-04-08 16:06发布

I have just merged two repositories following advice given here. Since both repositories are completely unrelated, i used

hg convert --filemap fm rep1a rep1b
hg convert --filemap fm rep2a rep2b

to move files in repository 1 to a subdirectory a, and files from repository 2 to a subdirectory b. There are no conflicting files in both repositories now, so I combined them both.

The result is a repository that has a merge revision with two parents, one containing the history of repository 1, the other of repository 2. Nice!

mb--ma--2f--2e--2d--2c--2b--2a
      \
       -------------------------1e--1d--1c--1b--1a     

Now what I really want is a history where patches are sorted by time. That is no problem, I used hg convert --datesort src dst and everything looks ok:

mb--ma--2f------2e--2d------2c--2b--2a
      \
       -----1e----------1d--------------1c--1b--1a     

The final thing I want now is that all patches are merged so that they have a linear dependency graph, effectively eliminating the merge revision `ma'. I think the solution is to rebase some revisions, but I lack the understanding which revision needs to be rebased on which. I experimented some, but nothing seemed to work as expected. One try yielded strange questions like:

use (c)hanged version or (d)elete?

How can I do the last step?

EDIT: I partly solved the problem. Since rebase seemed to mess things up (or I didn't fully understand what was the problem), a different approch seems to work much better (as suggested by Lasse): rewriting history.

What I did was split the repository into the original two parts again, using hg convert with filemap, and up until the merge revision. Then I used hg transplant to transplant one repository into the other. Voila, linear history!

2f--2e--2d--2c--2b--2a--1e--1d--1c--1b--1a

After this, I transplanted the changesets I did after the merge on top of this. Result: No differences in the actual working copy to the backup with the two history timelines. So far, so good!

mb--2f--2e--2d--2c--2b--2a--1e--1d--1c--1b--1a

Now the revisions are not sorted by time, which would be ideal, so I tried a hg convert --datesort repository.src repository.sorted, but the result still has the changesets in the same order like shown above. I could sort them by hand, if anyone has an idea how this could be done.

EDIT 2: I finally solved the issue, I created a new repository

mkdir repository_c
cd repository_c
hg init .

the I pulled in all patches in the correct order, using hg transplant:

hg transplant --source ../src 0:53
hg transplant --source ../src 70
hg transplant --source ../src 54:55
hg transplant --source ../src 71:79
hg transplant --source ../src 55:60
...

This did the job, the history is linear, and patches are sorted in the correct order. Since there were no conflicting patches, and the head revision is identical to the original repository with two timelines, I'm happy. I did use the MQ extension to create a list of patchfiles for all revisions and controlled them by hand, but from what I can tell the history is perfect.

1条回答
Bombasti
2楼-- · 2019-04-08 16:45

Merging unrelated histories doesn't work well because each changeset is a snapshot of the project at some state. If you simply pull -f or graft with convert, you'll get a repository with clearly unrelated history grafted together. You won't get a nice tidy history that shows the grafted-on history being added to the main project a commit at a time like you'd want. Instead you'll get a bunch of the commits with files only from project A, and a bunch with only files from project B. In short, this is because "things didn't actually happen that way". Stitching together history graphs won't fix up the contents of those changesets.

So the only available answer is to actually replay the history of project B inside of project A a commit at a time, and in the right part of A's directory namespace. This can either be done "by hand" with hg export + import or with some combination of hg convert --filemap and transplant or rebase.

I've adjusted the wiki page you mentioned to deprecate that 'advice'. Generally speaking, I don't recommend trying to do this sort of thing at all, again because "it didn't actually happen that way". It's better to just pull in the whole history of project B in one commit and have a pointer back to its true history in your commit message.

查看更多
登录 后发表回答