Mercurial: applying changes one by one to resolve merging issues - mercurial

I recently tried to merge a series of changeset and encountered a huge number of merging issues. Hence I'd like to to try to apply each changeset, in order, one by one, in order to make the merging issues easier to manage.
I'll give an example with 4 problematic changesets (514,515,516 and 517) [in my real case, I've got a bit more than that]
o changeset: 517
|
o changeset: 516
|
o changeset: 515
|
o changeset: 514
|
|
| # changeset: 513
| |
| o changeset: 512
| |
| o
| |
| o
| |
| o
|/
|
|
o changeset 508
Note that I've got clones of my repos before pulling the problematic changesets.
When I pull the 4 changesets and try a merge, things are too complicated to resolve.
So I wanted to pull only changeset 514, then merge. Then once I solve the merging issue, pull only changeset 515 and apply it, etc. (I know the numbering shall change, this is not my problem here).
How am I supposed to do that, preferably without using any extension? (because I'd like to understand Mercurial and what I'm doing better).
Is the way to go generate a patch between 508 and 514 and apply that patch? (if so, how would I generate that patch)
Answers including concrete command-line example(s) most welcome :)

I haven't tested this, but merging individual changesets should be easy enough:
$ hg update -r 513
$ hg merge -r 514
... # do your conflict resolution and commit
$ hg merge -r 515
... # repeat

I haven't tested either, but it should work to just hg up to each of the foreign changesets one after the other. I don't think you have to commit between the updates.
As a bonus, the command line example you wished :-)
hg up 513
hg up 514
hg up 515
hg up 516
hg up 517

Related

Hg backout, then take the changes and store on a branch

Here is what I am trying to do. I have a commit to the default branch that I need off default. We build for production from default and this commit is not ready. So I have used hg backout to undo the changes from the default branch. Now I have created a new branch to store the changes. But when I attempt to transplant or graft the changes onto the branch I get told that I cannot graft in changes from an ancestor. This makes sense, but I need to pull changes from an ancestor, how do I do this.
I have tried
hg diff -c 8c13fc133926 > new_branch.diff
hg import new_branch.diff
but this Fails with no explanation. Any pointers?
EDIT
It seems I'm a little hard to follow, so I will try and clarify.
o Default with only ready changes
|
| o Not ready changes (new commit on a branch)
| |
|/
o Hg backout commit Undoing not ready changes from default
|
o Lots of other commits and merges to default
|
o ...
|
o Not ready changes (original commit)
The graph above shows where I need to get to.
I have done this by using hg diff -c 6877 | patch -p1 where 6877 is the revision of the commit that needs to be moved off of default.
But I think I am doing this wrong.
Make your new branch off of the revision BEFORE you made the "not ready changes" commit. I will call that revision 123:
hg up 123
hg branch NewBranch
hg ci -m "branching for NewBranch"
Now you can do your GRAFT, with no concerns about ancestors.
Tying up loose ends:
It seems that you'll already have another head on NewBranch, the one you said you created. You can merge with that or just close it. You can see the heads with:
hg heads NewBranch
I will call that unwanted head revision 456. And I would close it like this:
hg up 456
hg ci --close-branch -m "It was all just a dream... a terrible, terrible dream."
Which reminds me: you could have used the same technique (closing an unwanted branch head) instead of hg backout! Then you could have done the steps above ... except you'd be able to do a neat-and-tidy REBASE ("leaving no witnesses", like they say in the movies) instead of cherry-picking with GRAFT, and leaving behind the wreckage on the default branch!
I personally avoid grafting as much as possible, and your commit is already in the history. You seem to be using the correct commands, but it is hard to understand your issue, more information is needed.
How about creating your new branch from the commit that you wanted to backout?
o NewBranch: head, creation of the branch
|
|
o | default: head, Backed-out changeset
| |
|/
o default: commit (not ready)
|
|
o default: initial state
|
Another idea, if you don't care about grafting, simply start your new branch from the initial state, and transplant the commit; it won't be considered an ancestor anymore.
o NewBranch: head, grafted commit
|
/|
| o NewBranch: creation
| |
| |
o | | default: Backed-out changeset
| | |
|/ |
o / default: commit (not ready)
| /
|/
o default: initial state
|

Mercurial, pulling a single branch

I have two Mercurial repos C1 and C2 which both derived from the same parent P some time ago but have since had separate lines of development. In addition, in C2, there is a named branch B2 which happened since the diversion. I want to pull only branch B2 into C1, which I can easily do with hg pull C2 --branch B2.
Now B2 branches off of some points in the default branch of the C2 repo. So those default changesets from C2 get pulled over into C1 even though I am only trying to pull branch B2. (I can understand that since they are ancestors of the B2 changesets).
After the above pull, I will have two heads on the default branch of C1, the original head and the head composed of those default changesets that got pulled over as a result of pulling B2 over. I want to leave the default branch of C1 unchanged, otherwise I have two heads, keep getting told that updates "cross branches" and telling me I have to merge. (I will be pulling new default branch things into C1 from other external repos going forward).
How can I do the above so that I do not have two heads on default?
Thinking about the problem, am I right in thinking you have something like this?
# 4[tip]:1 439255c536ee 2013-01-25 10:42 +0000 rob
| More changes on original default branch
|
| o 3 379f384c1d73 2013-01-25 10:41 +0000 rob
| | Changes on named branch B2
| /
| o 2:0 d225da266931 2013-01-25 10:40 +0000 rob
| | Changes on cloned default
| |
o | 1 7088660d3ba6 2013-01-25 10:41 +0000 rob
|/ Changes on original default branch
|
o 0 a02a921256b3 2013-01-25 10:39 +0000 rob
Project Start
The problem you're describing is that while you want to keep the B2 branch, you don't want the two heads on default:
$ hg heads default --style=compact
4[tip]:1 439255c536ee 2013-01-25 10:42 +0000 rob
More changes on original default branch
2:0 d225da266931 2013-01-25 10:40 +0000 rob
Changes on cloned default
I can see two possibilities to "fix" this. One is simply to close the "cloned" default branch (at least in your repo):
$ hg update 2
$ hg commit -m "Closing cloned default" --close-branch
$ hg heads default --style=compact
4[tip]:1 439255c536ee 2013-01-25 10:42 +0000 rob
More changes on original default branch
This would mean that if you issue an hg update default, it shouldn't be ambiguous, which I guess is part of the problem.
An alternative would be to do a "null merge" from the other default branch:
$ hg update
$ hg -y merge --tool=internal:fail 2
$ hg revert --all --rev .
$ hg resolve -a -m
$ hg commit -m "Merged cloned default"
The main difference in these approaches is that using the --close-branch option, you can still see the extra head when you use hg heads -c... it is still a head, but it simply has a flag set in the metadata to say it's closed. You can still update to the closed branch, and even commit changes to it. Doing a merge you won't see the head at all, as it will not longer be a head.
Both of these methods mean that you can still pull changes to the B2 branch - however, if in the repo you're pulling from they make changes on default and merge those into B2 (often used when bugfixing, etc), you will hit the same problem later on, and have to repeat the above.
I hope that makes sense. Of course, if you later push to another repo, you will push the closed/merged changesets too. It would be wise to clone your repo and try these approaches locally to check if that's what you actually want.

mercurial - see changes on the branch ignoring all the merge commits

I have a branch that was developed for a long period of time. During the development default branch was merged into that branch several times. I would like now to review all the changes done on that branch ignoring merges, to decide whether it is safe to merge it to default.
I tried
hg diff -r "branch('myBranch') - merge()"
but it still shows changes introduced by merges. Also tried following this How to show the diff specific to a named branch in mercurial but
hg diff -r "branch('myBranch') - branch('default')"
still bring changes introduced by merges.
You have to read about revsets syntax
Your case
hg log -r "branch('myBranch') and ! merge()"
The problem with your commands is that when you perform a hg diff and pass it several changesets, you actually perform a diff between those changesets, hence you will see the merge result.
If you want to see just the changes made by the changesets then you could use export:
$ hg export -r "branch('mybranch') and not merge()"
// lists the changes made by each changeset
For easier reviewing, you can output these to files with names based on the revision/changeset id:
$ hg export -r "branch('mybranch') and not merge()" -o "%H.patch"
... creates a file for each non-merge changeset in mybranch, and outputs it to a file with the name "40-digit changeset id.patch". If you'd prefer the revision number (only useful for your local repository as revision id's are local), use "%R.patch".
The following uses the log command but with the --patch parameter it can show the modified lines as well:
hg log --branch my-branch --no-merges --patch
Short form:
hg log -Mpb my-branch
That is a very good question, which I am trying to find good answer for a long time, and yet not found a good one. OK, one thing which 100% works is this:
hg status # make sure that you don't have local changes
hg up <target_branch>
hg merge <your branch>
hg diff > merge.diff
hg up -C # this one cleans the merge
I use this workflow all the time, but it does not satisfy me fully because it requires to switch branches (when I actually might not want to do actual merge right at this exact moment, I am just checking whats there)
I've been searching for ages for a good solution here, but so far there are none found. Tried those:
hg diff -r "branch('.') and ! merge()" # this page
hg diff -r "default:branch('.') and not merge()"
hg diff -r "parents(branch(.)):branch('.') and not merge()"
This problem also discussed in:
Mercurial: how can I see only the changes introduced by a merge?
which has good answer as: "so if you can define it unambiguously you might convince one of us Mercurial contributors that read SO to implement it."
Try:
hg diff -r"ancestor(default,my_branch)" -rmy_branch
If you haven't done any merges to the branch, then "ancestor" will pick up the original branch point. If you've done merges, then "ancestor" will pick up the latest merge point.
For example, on the graph below you'll get the diff between 520 and 519:
# 521 (default)
|
| o 520 (my_branch)
|/|
o | 519
| |
o | 518
| |
o | 517
| |
| o 516
| |
| o 515
| |
| o 514
|/
o 513
On the simpler graph below you'll get a diff between 516 and 513:
# 517 (default)
|
| o 516 (my_branch)
| |
| o 515
| |
| o 514
|/
o 513
Per Matt Mackall what you probably want is:
hg diff -r mainbranchrev -r mywork
He writes:
You may be under the impression that "a changeset is a delta" or
similar, and that this means that Mercurial can magically chain together
a bunch of non-contiguous deltas to construct a bigger delta that's just
your changes.
In fact, Mercurial is much simpler than that. A changeset is actually a
snapshot: a complete set of complete files representing the state of the
project at the time of commit. And a diff is simply the difference
between these two snapshots. So when you do:
hg stat --rev '3408::3575 and user(mdiamond) and not merge()'
..status simply uses the first and last changesets of the resulting set
to do its calculation.
When you do incremental merges on your development branch, you're
inextricably tangling your work with mainline development, and Mercurial
is not nearly smart enough to untangle it in the way you're asking. Your
best bet is to compare the head of your work with the head of the main
branch:
hg diff -r mainbranchrev -r mywork
..which will show all your work plus whatever merge fixups you had to
do.

Mercurial: how to make Repository1 track Repository0 but pushing selective only certain tagged revisions/changesets

Using Mercurial. Working in private clones, making changes, pushing to master. Etc. That's fine.
(By the way, let me state that I am familiar with many version control systems ranging from the classics SCCS, RCS, CVS, SVN to DVCSs like BitKeeper, Git, Mercurial, passingly familiar with Monotone, Darcs, Bzr. Not so familiar with Perforce, ... I just mention this for people who may be able to explain equivalents and similarities between systems.)
Unfortunately, other members of the project think that I check in too often. I use the "check in early and often" approach, sometimes checking in more often than once every half hour. They don't want to see so many checkin messages in the log.
NOT QUESTION: Now, I have taken to the habit of making most of my changes on branches (although hg lacks retroactive branching), summarizing all of the changes when I merge my task branches back to the trunk. hg log -b default allows them to filter out my task branches, and works okay when the merge messages are meaningful. This is not my question.
NOT QUESTION: similarly, I know how to use history editing, etc., to remove my fine granularity changesets, so that the only changesets that get pushed are coarse grain. (Although, by the way, I often find the simplest thing to do is move .hg directories around.) History editing ias annoying, but I can do it.
MY QUESTION: my question is: I would like to keep track of my fine granularity changesets and log messages, even though I don't push them to the project master repository.
Q: how do I do this?
I.e. how do I keep two repositories tracking each other, but NOT push all of my changes to the master? Preferably tag changesrts not to be pushed. (By the way, I prefer to say "tag a revision set". Changeset sounds too much like patch.)
The way I do it now is to have
a) the project master repo
b) my personal master repo, with all of the fine grain changes
c) one or more workspaces
Where
a) clone the project master repo to get the workspace (using hg clone, yyeah, I know)
b) work in the workspace clone with fine grain checkins (I know this)
c) push from the workspace clone to my personal master
d) and, when it comes time to push to the master, edit the history, and push that.
Two troubles:
1) conflicts merging or pushing from the workspace clone to my personal master
- more precisely, conflicts when merging from the master repo with history edited to remove the fine grain checkins, to my personal master with the fine grain checkins preserved.
2) I would like to record what has been pushed to the project master in my personal master.
--
A very simple example.
A task branch, with fine grain checkins.
o changeset: 1022:
| branch: default
| parent: 1017
| parent: 1021
| summary: Merged task branch onto main, default, branch, with changes to FOO and BAR
|
o changeset: 1021
|\ branch: task-branch
| | parent: 1020
| | summary: Merged default branch to task-branch, with changes to FOO and BAR
| |
| o changeset: 1020
| | branch: task-branch
| | parent: 1018:
| | summary: yet another fine grain checkin on a branch changing BAR for a second time
| |
o | changeset: 1019
| | branch: task-branch
| | parent: 1018:
| | summary: yet another fine grain checkin on a branch changing FOO
| |
o | changeset: 1018
| | branch: task-branch
| | parent: 1017
| | summary: yet another fine grain checkin on a branch changing BAR a first time
| |
|/
o changeset: 1017
| branch: default
| summary: some other changeset on the default branch, i.e. trunk
|
The edited history, that gets pushed to the project master
o changeset: 1018: ...changed checksumm because checksum seems to include history
| branch: default
| parent: 1017
| summary: Merged task-branch onto main, default, branch, with changes to FOO and BAR.
| Removed task-branch-history from what got pushed to project repo,
| although may still be in personal repo
|
o changeset: 1017
| branch: default
| summary: some other changeset on the default branch, i.e. trunk
|
The problem is, when I try to merge an updated projecvt master, that may look like
o changeset: 1019
| branch: default
| parent: 1018
| summary: again, some other changeset on the default branch, i.e. trunk
|
o changeset: 1018: ...changed checksumm because checksum seems to include history
| branch: default
| parent: 1017
| summary: Merged task-branch onto main, default, branch, with changes to FOO and BAR.
| Removed task-branch-history from what got pushed to project repo, ]
| although may still be in personal repo
|
o changeset: 1017
| branch: default
| summary: some other changeset on the default branch, i.e. trunk
|
I get conflicts, even though changeset 1018 of the project master and 1022 in the personal master are exactly the same, in terms of file contents.
I've tried not changing the comments in the merge changesrt that gets pushed. Doesn't help, at least not reliably.
I wonder if the full history is included in the changesets.
I also wonder if there is some merge tool that can recognize same files, even though different history and log messages.
In general any workflow that involves editing history is not going to work well. It's just not the intended work mode, and no effort goes into making it work well.
Better options for you might be:
Versioned Mercurial Queues (hg qinit --create-repo) which lets you make multiple commits iterating on a single patch. You get the full history in your private patch repo and they only get the qfinished whole.
Rather than merge back your task branch and edit history, just re-apply the change as a single commit and push that. In your example it would be hg diff -r 1017 -r 1021 | hg import, which creates a new commit (1022) that is the sum of all the changes 1018::1021, inclusive. that should merge cleanly and be pushable without ever having to push any changesets on your task-branch out to them. You can even hg commit --close-branch on the task branch
Tell your coworkers to STFU. ;) High changeset granularity is good practice, and they should adapt.
Also, consider using bookmarks instead of named branches for your task-branches. Similarly the new phases feature makes it possible to mark changesets in your task branches (be they bookmark-branches or old-style named-branches) as secret so they can't be accidentally pushed.

Did the behavior of `hg backout` change since the hg book was written?

I created a new repository, test-backout, and added a new file in it, file. I then made 4 commits, each time, appending the number of the commit to file using
echo [manually entered number] >> file
hg commit -m '[manually entered number]'
In effect, file had:
init
1
2
3
According to the hg book, if I run hg backout --merge 2, I should have:
init
1
3
but instead, it fails to merge and opens up my difftool (vimdiff), and I get 3 options:
init | init | init
1 | 1 |
2 | |
3 | |
I initially tried it with the --merge option, then again without it. My question now is, is there still a way for me to get:
init
1
3
did I just make a mistake or miss something, or am I stuck with those options?
A big factor in why you got the 3-way merge is that your context is too artificial, and I will get to that.
If I take a 50-line text file and change a different part and commit each change, I won't have to resolve conflicts. And what I mean is I have 4 changesets: rev 0 adds the file, revs 1, 2, and 3 each change one area of the file: the beginning, middle, or end.
In this situation, when I do hg backout 2, it makes a reverse of rev 2 and merges those changes to my working directory, and when I commit, the graph is linear:
# backout 2
|
o 3
|
o 2
|
o 1
|
o initial
If I instead do hg backout 2 --merge, it automatically commits the backout as a child of the revision it is backing out, and then merges that with the tip, producing a branched graph after I commit the merge:
# merge
|\
| o backout 2
| |
o | 3
|/
o 2
|
o 1
|
o initial
In both situations, I didn't have to do any 3-way merging. The reason you don't automatically get
init
1
3
and instead have to do a 3-way merge is that the changes are too close together. The context and changes in each changeset are completely overlapped (default number of lines of context for a diff chunk is 3 lines, which encompasses the entire file still in your 4th changeset).
A similar example is if you had 3 changesets that each modified the same line. If you backed out the middle change like you're doing here, you would still be presented with a 3-way merge that you'll likely have to manually edit to get correct.
By the way, behavior did change in 1.7, as attested by hg help backout:
Before version 1.7, the behavior without --merge was equivalent to specifying --merge followed by "hg update --clean ." to cancel the merge and leave the child of REV as a head to be merged separately.
However, I don't think that's quite what you suspected.