What is the rebase command used in hg pull --rebase - mercurial

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

Related

How to bring latest changes in a mercurial feature branch (workflow)?

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.

TortoiseHg files shown in merge

I am writing code to do analysis of our commits to Hg, and am comparing my results to TortoiseHg. I am having trouble understanding the behavior of TortoiseHg in the case of a merge.
When I select a merge changeset in TortoiseHg, the list of affected files only shows those files that had conflicts, unless I press the "Show All" button. At least that appears to be the intent, based on what I can glean from the web, and from observation that the files shown in the list have a double headed arrow if I press the Show All button.
I am attempting to emulate that by diffing each file in the changeset against both parents, and only including the file in my analysis if it differs from both parents. However, I am encountering files that TortoiseHg shows in the description of a merge, but that only differ from one parent. I see that in TortoiseHg as well - diffing against parent 1 or 2 shows a change, but the other parent doesn't.
I have also tried diffing with the --git option, to make sure it is not a metadata change I am missing, but that doesn't change the results at all.
To get the information about a changeset I am using:
hg log -v -r <rev> --removed --style xml
I pick up the parents of the merge changeset, and for each file in the merge, do
hg diff -r <parent1> -r <rev> filename
hg diff -r <parent2> -r <rev< filename
And I find that files TortoiseHg shows in its summary of the merge I report as having merged with no conflicts.
Can anyone shed light on the discrepancy?
Update:
I was able to reproduce this with the source code for TortoiseHg itself.
Clone from https://hg01.codeplex.com/tortoisehg
Open the repo in tortoiseHg and select rev 12602 (58eb7c70). This is a merge with parents of 12599 (6c716caa) and 12601 (39c95a81).
TortoiseHg shows the file tortoisehg/hgqt/repowidget.py as the only conflicted file in the merge, yet
hg diff -r 12599 -r 12602 tortoisehg/hgqt/repowidget.py
returns nothing, while
hg diff -r 12601 -r 12602 tortoisehg/hgqt/repowidget.py
shows two lines changing.
I think I've figured out what tortoisehg's logic is here (though I haven't checked the source to be sure).
As you've guessed, tortoise shows files changed on both sides of a merge with a double arrow. However, it does not look simply at the diff of the merge to each of its parents (e.g. p1(58eb7c70)::58eb7c70 and p2(58eb7c70)::58eb7c70). Instead, tortoise finds all changes introduced in the merge, compared the last common ancestor of the two parents.
Let's take the tortoise repo as an example. The graph view of the ancestry of 58eb7c70 is:
Jonathan:tortoisehg $ hg log --graph -r ::58eb7c70 -l 5 --template "{node|short}\n{desc|firstline}\n\n"
o 58eb7c70d501
|\ Merge with stable (noop)
| |
| o 39c95a813105
| | repowidget: show all errors on infobar
| |
| o da7ff15b4b96
| | repowidget: limit infobar error messages to 2 lines of up to 140 chars by default
| |
o | 6c716caa11fd
|\| Merge with stable
| |
| o 48c055ad634f
| | sync: show non-ascii command-line arguments correctly
| |
As you can see, merge 58eb7c70d501 merged two branches of development, with one changeset (p1, 6c716caa11fd) on one side, but two on the other (p2, 39c95a813105, and its parent, da7ff15b4b96). The point where these branches diverged is the last common ancestor of p1 and p2 -- 48c055ad634f.
(The last common ancestor can be found directly with hg log -r "last(ancestor(p1(58eb7c70), p2(58eb7c70)))")
Let's look at the changes that were made on those two branches. We'll compare each parent of the merge with the common ancestor:
Jonathan:tortoisehg $ hg status --rev "48c055ad634f::6c716caa11fd"
M .hgtags
M tortoisehg/hgqt/commit.py
M tortoisehg/hgqt/compress.py
M tortoisehg/hgqt/hgemail.py
M tortoisehg/hgqt/postreview.py
M tortoisehg/hgqt/purge.py
M tortoisehg/hgqt/rename.py
M tortoisehg/hgqt/repowidget.py
M tortoisehg/hgqt/revset.py
M tortoisehg/hgqt/run.py
M tortoisehg/hgqt/settings.py
M tortoisehg/hgqt/status.py
M tortoisehg/hgqt/sync.py
M tortoisehg/hgqt/visdiff.py
M tortoisehg/util/cachethg.py
M tortoisehg/util/hglib.py
Jonathan:tortoisehg $ hg status --rev "48c055ad634f::39c95a813105"
M tortoisehg/hgqt/repowidget.py
These are the changes that were actually merged by 58eb7c70d501 -- everything changed on the two branches since they diverged. As you can see, the only file in common between the lists -- the only file that was changed on both branches -- is tortoisehg/hgqt/repowidget.py, just as you expected. You'll see that this file was changed in da7ff15b4b96, the one changeset that's not a parent of the merge but is still included in the changes merged from the two branches.
tortoisehg/hgqt/repowidget.py was modified in 6c716caa11fd, which would explain why your second call to hg diff gives you results; the first call compares two revisions across which no changes were registered to tortoisehg/hgqt/repowidget.py; this seems sound to me, unless I am missing something else about how hg diff behaves.

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.

What's the best way to back out multiple changesets in mercurial?

Is the most reliable method to go one-by-one, using the backout command for each of many changesets, or is there a way to create one big reversal changeset to cover a whole bunch of [edit: non-contiguous] changesets.
If one-by-one, does order matter? (Should one go last-to-first?)
Does the best method differ if there are merges among different sub-projects along the way?
Does this tend to go smoothly in your experience? :-)
If you have no merges along the way, you can either back out every individual change (in reverse order), or, if there are many of them, do it with one big inverse patch.
If you have good changesets atop the ones you need to back out, better commit the inverse patch on top of the most recent bad changeset, then rebasing them onto the tip of the branch.
1 -- 2 -- A -- B -- C -- 3 -- 4
\
C'B'A'
$ hg up C
$ hg diff -r C:2 > backout.diff
$ hg import --no-commit backout.diff
$ hg ci -m "Backout A, B, C"
$ hg up 4
$ hg rebase -s C'B'A -d .
There will be problems if you want to back out merge changesets, see this wiki page for more information.
In such a case, if possible, consider re-doing the branch and stripping the old lineage. Otherwise, you might have to abandon the branch altogether, salvaging the good changesets via graft or transplant.
There is --collapse option for rebase.
Helgi's answer can be upgraded into:
1 -- A -- 2 -- B -- 3 -- C -- 4 -- 5
\
C' -- B' -- A'
$ hg update --clean C
$ hg backout --rev C --message "Backed out changeset: C"
$ hg backout --rev B
$ hg commit --message "Backed out changeset: B"
$ hg backout --rev A
$ hg commit --message "Backed out changeset: A"
$ hg rebase --collapse --source C' --dest 5
$ hg commit --message "Backed out C, B, A"
which will result in the following
1 -- A -- 2 -- B -- 3 -- C -- 4 -- 5 -- C'B'A'
However, backing out in separate branch may result in [logical] conflict in the subsequent merge.
1 -- A -- 2 -- B -- 3 -- X -- 4
\ \
B' -- A' -- M
if X depends on A or B, then M will have conflict (at least logical conflict).
What I came up with is inelegant, but got the job done, despite that the changes I needed to back out were interspersed with other work and had some internal branching. Here's what I did. (Comments and improvements are welcome.)
Got a list of all of the changesets (which I then used to generate the commands below):
hg log -r 'keyword(xyz)' --template '{rev}\n'
Generated a patch for each changeset:
hg diff -p -U 8 --reverse -c 15094 > 15094.rev.patch
hg diff -p -U 8 --reverse -c 15095 > 15095.rev.patch
...
Then, applied each reverse patch. Here the order matters, last-to-first:
hg import -m "reversing changeset 15302" 15302.rev.patch
hg import -m "reversing changeset 15292" 15292.rev.patch
...
This process was interrupted several times for merges that didn't go through automatically, where I had to manually apply changes to a file from its .rej file and then manually commit, before picking up the imports where it had left off.
Finally (in another clone... did I mention I did this all in a clone?) I compressed the whole set of reverse changesets into one changeset using hg histedit -o and its fold command.
Now I've got a single changeset that I should be able to reverse and apply if I decide to put the work back in at a later date (Although if I cross that bridge, I might apply the "forward" patches piecemeal again in order to get better blame/annotate information)
This is how you can do it with TortoiseHg.
Of course you can do the same with the command line.
Given this history, where you wan't to get rid of changeset A, B and C:
1 -- 2 -- A -- B -- C -- 3 -- 4
First update to revision 2.
Then rebase the first of any later revisions you wan't to keep - in this case revision 3.
Your history now looks like this:
1 -- 2 -- A -- B -- C
\
3 -- 4
Now update to revison 4.
And finally use "Merge with local" to merge revision C onto revision 4.
At this point it is crucial that you select the option "Discard all changes from merge target (other) revision".
The description may not be the most logical, but it means that you merge the old tip C back to the default branch - but without the changesets A, B and C.
The result is:
1 -- 2 -- A -- B -- C --
\ /
3 -- 4
Commit and you're done.
If you don't want the "backout" changesets in your history, you could also do something else:
Make a clone of your repository, but only up to the last changeset that you don't want to get rid of.
See Mercurial: Fix a borked history for an example how to do this.
If your repository was a local one, that's all you have to do.
But if the bad changesets were already pushed to a central repository, you'd need server access to delete the repository there and replace it by your clone.
Plus, if someone else already pulled from the repo with the bad changesets, they need to delete and re-clone (otherwise the bad changesets are in the central repo again as soon as one of the other people pushes again).
So it depends on the circumstances whether this solution is a good one for you...