Can I retrospectively move Mercurial revisions to a branch? - mercurial

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.

Related

Condensing a mercurial repository - recommanded way?

Let's say I have a repository 'Main', and Max and co work on a clone each. Max has some local commits ('f'&'g') that are not yet pushed to 'Main'. This is how it looks now (pipes being pushs/pulls):
A--B1--B2--C--D1--D2--D3--E (Main)
| | | |
A--B1--B2--C--D1--D2--D3--E--f--g (Max)
'B1' and 'B2' as well as 'D1', 'D2' and 'D3' are changes that only make sense together. We would like to combine 'B1' and 'B2' to a single changeset 'B' and combine 'D1', 'D2' and 'D3' to a single changeset 'D'. The new structure should look like this:
A--B--C--D--E (Main)
| | |
A--B--C--D--E--f--g (Max)
My (main) question is: What is the reccommended way of doing this?
Now let's make things worse:
We have a branch that was merged within the change-sets that we want to collapse. It would look like this:
A--B1--B2--C--D1--D2------D4--E (Main)
| | \-------D3-/ |
| | |
A--B1--B2--C--D1--D2------D4--E--f--g (Max)
\-------D3-/
The new history should look like this:
A--B--C--D--E (Main)
| | |
A--B--C--D--E--f--g (Max)
How would you do that?
Thanks in advance.
It depends on how much effort you want to put into this. While I don't know a solution within Mercurial itself (I only know history editing functions which can't cope with merges), Git does have the functionality you need:
If I would really have to do such an operation, I would
Try to convince the management that this is not worth it
Try harder to convince the management that this is not worth it
Make a backup! The following steps involve destructive operations, so consider this as not optional. You have been warned.
exort the repo with hg-git into a git repository
export the complete (git) history into a fast-import-stream with git fastexport --no-data --all > history.fi
Create a Pseudohistory by editing history.fi, dropping your unwanted revisions
import the adjusted history into the git repo with ``git fast-import -f < history.fi`
check extensively if the newly created history is in fact the way you want it to have
clone Max into a local work repository
Remove successors of commmit A in the local work repository
pull your updated history back from git (again with hg-git) into the local work repository
check, if the Mercurial history matches your expectation (diffs of commits between the new and old repos, metadata (time stamps, committer names, ...)
Remove successors of commmit A in every repo (Main, Max and every developer clone)
hg push -r E Main the partial history back to Main out of the work repository
hg push -r g Max the complete history back to Max out of the work repository

'Forgetting' a dead-end branch

I've got a mercurial repository. It was on rev A. I made some changes, committed (to rev B), and pushed. However, later, I realised I didn't want to make those changes. I updated back to rev A, and made some alternative changes, to rev C.
C
| -
| B
|/
A
However, now I can't push rev C, because it complains that it would create a new remote head (which it would). How do I make the remote mercurial simply forget about rev B and all the changes therein, so I can push rev C and carry on from there?
Editing History is hard. Once you push a changeset to a public repository, it can no longer be easily pruned from the history.
The most direct solution to your problem is:
hg update <tip of branch you want to forget>
hg commit --close-branch -m "close unwanted branch"
hg update <tip of branch you want to keep>
Push all your changes. As you noted, you will need to use --force since multiple heads on the branch now exist.
If you really need to prune the branch, then read EditingHistory again. If it still seems feasible, you can use one of the methods described in PruningDeadBranches.
Personally I'd close the branch and force the push (as Tim Henigan describes), as it leaves the DAG in a state which is truthful. There is another option though. Doing a dummy merge. This is a merge, but one where you ignore the incoming changes.
hg update C
hg -y merge --tool=internal:fail B
hg revert --all --rev .
hg resolve -a -m
hg ci
The end result is
M
|\
C |
| |
| B
|/
A
... but M doesn't contain any of B's changes.
Use the backout command to reverse the B change.
hg up B
hg backout B
Now, you're working directory is in the same state it was before B, which I'll call A'.
After you backout, make your real change and commit. This is what your history will look like:
C
|
A'
|
B
|
A
Mercurial branches are permanent and registered in commit objects themselves. There are some (not too easy) methods for closing and/or removing branches, mainly listed here. I have even already used some of them before. Note that those are somewhat desperate solutions for people who did use branches thinking they can be temporary.
However, if you really want temporary branches, use bookmarks.
Other people have some good answers for getting rid of 'B', but just to put it out there you can always do:
hg push --rev C
which will push C but not B and not complain about new heads.

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.

Move branch start in 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.

Mercurial branching a branch doesn't display right in hg serve or hg view

I've been doing some development on a branch and realized that before it could be complete something else need to be done first. I decided that I would branch my current branch and do the requiste changes in that branch then merge them back together and then merge my working branch into default. Basically I expected this:
| | + requiste work branch commit.
| |/
| + working branch commit
|/
+Default branch commit
and in the end what I expect to do is this:
+ Merge into defualt
|\
| + Merge requisite work into working branch
| | \
| | + requiste work branch commit.
| |/
| + working branch commit
|/
+Default branch commit
What I'm getting in both hg view and hg serve is this:
| + requiste work branch commit.
| |
| + working branch commit
|/
+Default branch commit
However, when I look at the commit log "requiste work branch commit" is marked as a part of a different branch.
Am I doing something wrong? Is this a bug in hg view and hg serve? Anyone experienced this before?
If there are no further commits on the first branch after "working branch commit" (except on the second branch), then the view may appear as a straight line (which is what you are seeing). I suspect that the reason for this is merely an optimization in the display code. As soon as you make another commit to the first branch, it should display the way that you are expecting.
The missing link here is that there is no commit on which is a child of "working branch commit" and not on the same branch as "requisite work branch commit". Thus, as one is a child of the other and there's nothing to show in a third column, you only see two columns. For the same reason, merging "req..." with "working..." is currently meaningless.