Given a branch called 'main'. As a developer wanting to work on a new feature, I create 'f1' and do several commits, pushing regularly to our central repo. While working on the feature, I need to get the changes form 'main' in my branch.
I know I can hg merge main to get the changes in 'f1'. But when I later integrate in 'main' then the history will be full of references to that temporary branch. Are there ways to make my branch work less visible after the fact?
To make branch less visible in history you should use bookmarks. Let's see both cases, with a named branch or with a bookmark.
With new branch named 'f1' from 'main':
$ hg update main
$ hg branch f1
...
$ hg glog --template "{rev} {branch} {bookmarks} {desc}\n"
# 5 main Hacking main.
|
| o 4 f1 Hacking f1.
| |
| o 3 f1 Hacking f1.
|/
o 2 main Hacking main.
|
o 1 main Hacking main.
|
o 0 default
With new bookmark named 'f1' from 'main':
$ hg update main
$ hg bookmark f1
...
$ hg glog --template "{rev} {branch} {bookmarks} {desc}\n"
# 5 main Hacking main.
|
| o 4 main f1 Hacking f1.
| |
| o 3 main Hacking f1.
|/
o 2 main Hacking main.
|
o 1 main Hacking main.
|
o 0 default
In the bookmark case the 'f1' feature can be removed from history with hg bookmarks --delete f1 (from Mercurial 2.3 new bookmarks are pulled by default WhatsNew) and there are two paths in the 'main' branch.
First of all, why do you worry about that the main branch would contain the history of the f1 branch after merging the latter to the former, if the f1 branch is already in the central repo??? The f1 branch is not temporary/disposable/invisible any more once it's pushed into the central repo, it's very visible to everyone who can access the central repo.
The answer to your question is: No way unless f1 is a by-cloning branch. All suggestions of using bookmark and/or anonymous branch are false.
Related
Typically, in HG my workflow is to exclusively use:
hg pull --rebase
If I wanted to run this in two commands, how would I do it?
hg pull
hg rebase <probably with some options?>
What hg pull --rebase does is to indeed first do a hg pull and then hg rebase with default arguments on top of that (you can look at the code in rebase.py in the Mercurial distribution in function pullrebase()), but only if any new revisions were pulled in. If no rebasing is necessary, hg pull --rebase will update to the new branch tip instead. So, hg pull && hg rebase is approximately correct, but doesn't quite capture some corner cases (no new revisions, no rebase necessary).
By default, hg rebase will use the parent of the working directory as the base revision of the rebase and the most recent revision of the current branch (i.e. usually what you just pulled in) as the destination. In short, it's equivalent to hg rebase -b . -d 'last(branch(.))'.
What does "base revision" mean in this context? It means that Mercurial will go and look for the least common ancestor of the base revision and the destination. Then it will rebase everything up to, but not including that least common ancestor on top of the destination. I.e., specifying the base revision allows you to pick pretty much any revision on the branch [1] that you want to rebase and let Mercurial figure out which revisions belong to that branch.
Note that because the rebase is based on the parent of the current working directory, this means that if your current checkout is not what you have been working on, then hg pull --rebase may surprise you by actually trying to rebase a different branch (it will usually fail, because those revisions are generally part of the public phase, but it's something you need to be aware of if you're working with so-called non-publishing repositories and don't use named branches).
[1] Branch in this context refers to an anonymous or topological branch, not a named branch. See hg help glossary for further details.
If you want to rebase by hand (bad idea in common), you have to
Read hg help rebase before
Understand usable for you options of rebase (at least -s and -d)
Use these options
Let's see at toy-repos:
Repo A
A>hg log -T "{rev} {desc}\n"
1 A2
0 A1
with 2 changesets A1 and A2 was cloned to repos B and C (B for pull --rebase A, C for clean pull A)
and two additional changesets was added to A and B+C after clone in order to test your use-case (diverged history)
A>hg log -T "{rev} {desc}\n"
3 A2++
2 A2+
1 A2
0 A1
B>hg log -T "{rev} {desc}\n"
3 B2
2 B1
1 A2
0 A1
C state is identical to B
B>hg pull --rebase
...
B>hg log -T "{rev} {desc}\n" -G
# 5 B2
|
o 4 B1
|
o 3 A2++
|
o 2 A2+
|
o 1 A2
|
o 0 A1
I.e. result of rebased pull is "linear history with local changes on top of remote changes", compared to just pull from C
C>hg log -T "{rev} {desc}\n" -G
# 5 A2++
|
o 4 A2+
|
| o 3 B2
| |
| o 2 B1
|/
o 1 A2
|
o 0 A1
or, in GUI
and in order to get B from C, you have to rebase 2 (-s 2) to new parent 5 (-d 5), but short hg rebase -b 2 will work also and will have the same effect
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
|
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.
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.
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.