Move branch start in Mercurial - mercurial

My problem is similar to Mercurial move changes to a new branch, but is not exactly the same.
A colleague of mine started working on a new feature and issued a few local commits. Then he noticed "Oh well, that should go into a named branch" and thus created one. The problem is, the commits before should have been in that branch already. So the simplified history now looks a little like this:
O 7: default
|
O 6: default
|
| O 5: newbranch
| |
| O 4: default
| |
| O 3: default
| /
O 2: default
|
O 1: default
So default now has two open heads: 7 and 4. I'd like to get rid of the open head 4 but of course still have 3 and 4 merged back into the "main" default later on when we merge newbranch back into main (merging them now is undesired as it would bring changes into default that we don't want there yet).
The question is: what's the best way to handle this ?
Do a --close-branch on 5, create a new branch off 2 and re-apply revisions 3-5 ?
As far as I've understood there's no good way to simply say "newbranch started at 3 instead of 5".
Update: Since this is has already been pulled by others I don't want to rebase the revisions in question. Forcing the colleague to recreate/switch branches would be OK since that would affect only one person instead of 5.

To get rid of the extra default head, you could:
hg update -r 4
hg commit --close-branch -m "Closed extra default head. The changes from this branch will be merged along with 'newbranch', once it is complete."
When you eventually merge newbranch, it will include revisions 3-5 plus any others that you commit on top of 5.
The only potential problem with this is that revisions 3-4 will not be labeled as part of the named branch. If this is really important for you, then you should close both the extra default head and the newbranch head and start over with a new branch rooted at revision 2.

Related

Can I retrospectively move Mercurial revisions to a branch?

I have a project with a natural long-term branch cum fork; I won't want to re-merge this with the default branch. I initially managed this fork with a bookmark, but on reflection it seemed to make more sense to have this as a named branch, so (without thinking about it too much) I created a branch at the bookmarked head (4).
o 7:default
|
| o 6:fork
| |
| o 5:fork
| |
| o 4:default
| |
o | 3:default
| |
| o 2:default
|/
o 1:default
|
o 0:default
But this means that I now have a branch plus a lonely head (4) on the default branch which isn't going to go anywhere (because it's really part of the branch/fork, and I don't plan to commit to (4) on the default branch). But this means that the repository is now somewhat untidy: I have a fork which exists partly on the default branch and partly, later, on the named branch and which, because it's on default looks as if it should be merged later. And oooh, that untidiness is a terrible thing (perhaps I should get out more).
What I'd like is either (a) to magically put selected revisions (ie, the ones leading to the now lonely head) onto the named branch, or (b) go back to plan A (bookmarks) and retrospectively put revisions on the named branch (5 & 6) back on the default branch and add a bookmark.
Possibilities:
There is a question Move branch start in Mercurial which overlaps with this, but is framed in terms of an expectation that the branch will be re-merged in future. Also, I'd quite like to retain the intermediate (post-fork pre-branch) revisions as being on the branch.
Use hg convert to create a copy of the repository minus the fork, create the branch, and start hand-applying changesets. Messy and rather error-prone, but doable (there aren't too many revisions in the branch).
Acknowledge that life is messy and full of disappointments, that history is history, and that I should stop worrying about inessentials.
or...
I'm holding out for magic.
Ghost merge. Merge revision 4 into revision 7. Discard all
changes of rev. 4 when doing the merge.
Close the head. Update to rev. 4, close the branch there.
Rebase. If revisions 2, 4, 5, 6 are in the draft phase. You can rebase starting from rev. 2, moving all descendants to another branch.
Options 1 and 2 will only hide the unwanted head (history unchanged). To rewrite history, go with option 3.
Rebase to default. You can also rebase from revision 5, moving that branch back to default. Using bookmarks again, as the branch never existed.

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.

How does the 3 way merge in Mercurial/Meld work?

I'm working on a project where I have a commit that introduced a feature with major problems that weren't discovered immediately. Now I want to completely remove that revision while keeping the work following it but i'm having a hard time wrapping my head around this 3 way merge. Here is a simplified graph of my project.
o changeset: 134:7f81764aa03a
| tag: tip
| parent: 128:451d8a19edea
| summary: Backed out changeset 451d8a19edea
|
| # changeset: 133:5eefa40e2a29
| | summary: (Change I need to keep keep)
| |
*snip 3 commits*
| o changeset: 129:5f6182a97d40
|/ summary: (Change I need to keep keep)
|
o changeset: 128:451d8a19edea
| summary: (Change that introduced a major problem)
|
o changeset: 127:4f26dc55455d
| summary: (summary doesn't matter for this question)
If I understand this correctly, r127 and r134 are exactly the same. When I hg up -C -r 133 and then run hg merge, Meld pops up with three forms of one of my files: local, base, and other. local seems to be r133 but i'm having a hard time wrapping my head around what "base" and "other" mean.
Local is r133
Other is r134
Base is r128 (the common ancestor to both r133 and r 134)
When you perform a 3 way merge it compares all three of those together to help you decide what to take and from where. By seeing what change is in the other revision and what the common ancestor looked like you are able to make a much more informed decision about what to keep and what to change.
Your question is really confusing, but here are some information that may help you.
What is base?
Base is the unmodified version of the revision that you have currently checked out and worked on. where possibly other changes have forked off (you can have revisions in between your current local and base!). its just where the nearest revision where no other fork has diverted from afterwards (same parent) (in your case r128)
What is head?
Head is the latest revision in version control. if you work alone on only one copy it will probably be base. but a co worker might have modified the same file and checked it into version control, then head is later than your base.
What is local?
Local is your modified version (in your case r133)
What is other?
Other is the some fork/branch that has also your Base as parent (in your case r134)
How does 3-way merge work?
3 way merge works (at least in meld) hierarchically. usually from left to right like this:
local > base > other / head
local/base is mostly trivial because its just what you modified
then you can merge your changes into the head revision or the one of your co worker or whatever.
There can be multiple other/head revisions, but then it's not your job to merge in and therefore more than 3 way compare doesn't make sense.

Mercurial: how can I see only the changes introduced by a merge?

I'm trying to get in the habit of doing code reviews, but merges have been making the process difficult because I don't know how to ask Mercurial to "show only changes introduced by the merge which were not present in either of its parents."
Or, slightly more formally (thanks to Steve Losh):
Show me every hunk in the merge that wasn't present in either of its parents, and show me every hunk present in either of its parents that isn't also present in 3.
For example, assume I have a repository with two files, a and b. If "a" is changed in revision 1, "b" is changed in revision 2 (which is on a separate branch) and these two changes are merged in revision 3, I'll get a history which looks like this:
# changeset: 3
|\ summary: Merged.
| |
| o changeset: 2
| | summary: Changing b
| |
o | changeset: 1
|/ summary: Changing a
|
o changeset: 0
summary: Adding a and b
But if I ask to see the changes introduced by revision 3, hg di -c 3, Mercurial will show me the same thing as if I asked to see the changes introduced in revision 1, hg di -c 1:
$ hg di -c 3
--- a/a
+++ b/a
## -1,1 +1,1 ##
-a
+Change to a
$ hg di -c 1
--- a/a
+++ b/a
## -1,1 +1,1 ##
-a
+Change to a
But, obviously, this isn't very helpful - instead, I would like to be told that no new changes were introduced by revision 3 (or, if there was a conflict during the merge, I would like to see only the resolution to that conflict). Something like:
$ hg di -c 3
$
So, how can I do this?
ps: I know that I can reduce the number of merges in my repository using rebaseā€¦ But that's not my problem - my problem is figuring out what was changed with a merge.
The short answer: you can't do this with any stock Mercurial command.
Running hg diff -c 3 will show you the changes between 3 and its first parent -- i.e. the changeset you were at when you ran hg merge.
This makes sense when you think of branches as more than just simple changesets. When you run hg up 1 && hg merge 2 you're telling Mercurial: "Merge changeset 2 into changeset 1".
It's more obvious if you're using named branches. Say changeset 2 in your example was on a named branch called rewrite-ui. When you run hg update 1 && hg merge rewrite-ui you're effectively saying: "Merge all the changes in the rewrite-ui branch into the current branch." When you later run hg diff -c on this changeset it's showing you everything that was introduced to the default branch (or whatever branch 1 happens to be on) by the merge, which makes sense.
From your question, though, it looks like you're looking for a way to say:
Show me every hunk in this changeset that wasn't present in either of its parents, and show me every hunk present in either of its parents that isn't also present in 3.
This isn't a simple thing to calculate (I'm not even sure I got the description right just now). I can definitely see how it would be useful, though, so if you can define it unambiguously you might convince one of us Mercurial contributors that read SO to implement it.
In order to do code reviews you really want to see just the changes in the project that you are reviewing. To that end we use a new branch for each story and use pull requests to spotlight the changes, having merged all changes into the story branch before creating the pull request. We host our code on bitbucket and the review / pull request tools are really very good, offering a side by side diff.
Pull requests with side-by-side diffs