Mercurial: Pushing a changeset without its ancestor - mercurial

I have a "Default" branch that comes from another repository.
I have my work in the branch "Mytask".
When I was finished with my work, I pull from the remote repository the revision 7. I merged from my local changesets 6 with 7 and committed to 8.
Rev Branch
* 8 Default
|\
* \ 7 Default
| |
| * 6 Mytask
| |
| * 5 Mytask
| |
* | 4 Default
| |
* / 3 Default
|/
* 2 Default
|
* 1 Default
Does anyone know if it is possible to only push revision 8 to the remote repository without pushing also my Mytask branch with the changesets 5 and 6?
Any feedback welcome!

Take a look at the answers to this question
about squashing commits. squashing is the key word in this case.
There is a collapse extension that looks like what you want to do as well as other links on that question.

From what I understood by your question, it feels like you want to have the same state of the remote repository as that is of your local repository. Also you don't an extra branch over there, you want a linear history and also a single commit.
Let's look at two things first, merge and rebase.
Initially you cloned stable, you committed your own changesets which are x and y, you pulled changesets from the remote repository which are a,b and c. Now your history looks something like this.
stable -> a -> b -> c
\
\-> x -> y
Merge
You want to have your repository in such a state than you want to have a single head. You opted for merge. You did hg up -r b and hg merge -r y, resolved merge conflicts if it were there. Now your history looks like this.
stable -> a -> b -> c -> d
\ /
\-> x -> y -/
Now you have a new commit d, which is a merge commit. The current state of your repo after updating to d contains changes from both the parent branches.
Rebase
Rebase is an extenison in Mercurial which you can easily use by adding a line to your .hgrc. Lets see what happens when you do hg rebase -s x -d c on the intial state we had. You history looks as follows.
stable -> a -> b -> c -> x -> y
In this case you have a linear history and both your changesets x and y are there on the stable branch.
Conclusion
A strong reason why people tend to use rebase of merge so that they can maintain a linear history, which helps a lot in sharing changes with other repositories.
In your case, you can undo your merge commit either by hg rollback or hg strip if that merge commit has no children. In case of childrens, you have to rebase them on y, then strip the merge commit, then rebase the whole chain from x onto c.
From the DAG in the rebase section(above) you can easily push x and y. If you want a single commit instead, you can use histedit extension or fold command which is part of evolve, both of which can collapse/merge/join/squash/fold those two commits into a single commit and your resulting history will be like this.
stable -> a -> b -> c -> z
Where z contains changes from both x and y changesets.
Related Links
Rebase Extension
Merge
Histedit Extension
fold command in evolve

Related

Move past mercurial commits to a different branch

1 -- 2 -- 3 --------- 7 C
\ \- 4 / B
\------------5----/ A
I ended up with the branching structure above in my mercurial repository.
What I really want is for commits 2 and 3 to be on branch B, as below:
1 -- 2 -- 3 -- 4 B
\ ------------5 A
Is there a good way to retroactively get this structure?
I'll disagree a bit with DaveInCaz's answer, but in the end, agree with the conclusion: you should be able to live with what you have; it's fine.
Technically, in Mercurial, once you make a commit, it's on exactly one branch and the branch it is on is the one branch you were on when you made it. So if you were on branch C and made commits 2 and 3, commits 2 and 3 are on branch C, now and forever.
But that doesn't really matter in terms of getting work done. Commit 4 is on branch B, and commit 4's parent is commit 3, whose parent is commit 2. The fact that they are on some other branch is normally unimportant.
(This setup is quite different from Git: in Git, commits are on all the branches from whose tips they are reachable. Branch tip names can be created and destroyed whenever you like, pointing to any existing commit. This means that the set of branches that some commit is on changes from one day to another. In effect, branch names in Git are devoid of any intrinsic meaning. You can add, remove, or change any name any time. The commits don't change but the set of branch names that reach them do.)
If, for some reason—e.g., some external tool that demands that the branch name of the commits be B rather than C—you really do want or need to move these commits, the answer is that you can't actually move them at all, but you can copy them:
C: 1 -- 2 -- 3 --------- 7
B: \ \- 4 /
A: \------------5----/
can become:
C: 1 -- 2 -- 3 ------------------ 7
|\ \ /
B | \ 4 /
B: | \ /
B | -------- 2b - 3b - 4b /
\ /
A: ---------------------- 5
Note that having copied 2 to 2b and 3 to 3b, you must now also copy 4 to 4b. Both 4 and 4b are on branch B, and branch B now has two heads.
As DaveInCaz noted, you can strip the unwanted commits. If they exist in some other clone that you talk to, they'll come back every time you talk to that clone, unless you strip them there as well.
You already have the structure you want, except that 7 has been added on top of it.
If 7 has not been pushed anywhere (or at least, not to any clones you don't control) you can just hg strip it.
If 7 has been pushed publicly, you can no longer remove it with strip because other clones will still have it. But you can ignore it, and just continue working from 4 or 5 as you please. No changes need to be committed with 7 as a parent.

Can I update my Mercurial branch to the head of what it branches from if there are no changes?

I started a branch from default before I was out for a day and I didn't commit any changes to it. Since then default has had a number of changes added to it. I would like to either update my branch to the latest from default, rebuild my branch (delete and recreate) from default, or somehow pull in the changes.
So in Mercurial how would I do this?
It sounds like you haven't made any commits, which means that the history looks like this:
... [a] --- [b] --- (wc)
\
`--- [c] --- [d]
Here your dirty working copy is based on b and you pulled down c and d.
Since there are no commits, there is nothing to merge. You can simply update:
$ hg update
and your modifications will be merged with the changes in c and d to produce this graph:
... [a] --- [b] --- [c] --- [d] --- (wc)
If you had made one or more commits, but haven't pushed them anywhere, then you could use the rebase extension to rebuild (re-play) the commits onto a different base. There you start with
... [a] --- [b] --- [x] --- [y]
You pull from the main repository to get:
... [a] --- [b] --- [x] --- [y]
\
`--- [c] --- [d]
After running hg rebase you end up with
... [a] --- [b] --- [c] --- [d] --- [x'] --- [y']
where x' and y' are the rebased versions of x and y, respectively. You can repeat this procedure as long as you haven't pushed your branch anywhere (Mercurial tracks the "phase" of each changeset, and when you push a changeset somewhere it moves from the draft phase to the public phase).

Mercurial: See how often a branch merges with and from default

Let´s say I have two branches, default and mybranch, I would like to write a small script to see:
How often mybranch merges with default (changes goes from mybranch to default)
How often mybranch merges from default (changes goes from default into mybranch)
Some pseudocode for #1:
m_lastmergetime = 0
for each m in [all merges with default from mybranch]
m_mergetime = m.date_in_hours
m_timebetweenmerges = m_mergetime - m_lastmergetime
if m_lastmergetime > 0
print m_timebetweenmerges
Sub.steps:
A Mercurial query which matches all merges with default from mybranch
A style for Mercurial which shows the date in hours
Calculate the time between merges as in the above pseudocode using Bash or Python
Case #2 will be pretty similar. I only need:
A Mercurial query which matches all merges with mybranch from default
To find all the changesets on branch X that was merged in from branch Y you would issue the following Mercurial command:
hg log --rev "children(branch(X)) and children(branch(Y)) and branch(X)"
To find everything on branch Y that was merged in from branch X (ie. the opposite direction), simply change the last branch name there to Y:
hg log --rev "children(branch(X)) and children(branch(Y)) and branch(Y)"
^ ^ ^
+------ leave these ----+ |
|
but change this ---------------+
Basically the query says that you want all changesets that:
is a child of any of the changesets on branch X
is also a child of any of the changesets on branch Y (only merge changesets have two parents, and can thus be a child of changesets on two branches at the same time)
is on branch Y (so you merged to branch Y)

Why does Mercurial merge changeset contain items with no conflicts?

Given this repository:
A1 - A2 - A3 - A4 - A5
.. you do hg pull which pulls in two more changesets from the remote repo, giving you two heads:
A1 - A2 - A3 - A4 - A5
\
B3 - B4
Let's say you do hg merge which gets you the M1 merge changeset:
A1 - A2 - A3 - A4 - A5----\
\ M1
B3 - B4 --------/
So now an hg out will list M1...
If you do hg diff -c M1 you see:
Files that were added in B3 and B4
The diff for files that were modified in B3 and B4 for which there were no conflicts (files weren't modified in A3-A5)
Why does the M1 merge changeset include these items? And why would you need to push this changeset to the remote repository?
These changes are already in B3 and B4 which are in the remote repository. I understand why the merge changeset would include files which had changes in both A3-A5 and B3-B4 and resulted in a new merged version of the files. But I don't see why the changeset would include items where there aren't conflicts. Won't the push of M3 duplicate changes that are already in the remote repo?
The simplest way I can think to put it is that B3 and B4 have A2 as the base on which their changes are built. To bring everything back to 1 head instead of 2, the merge represents a changeset that takes all of the changes in B3 and B4 and applies them on top of A5.
Another part of the reason it looks like that is you were on A5 when you merged B4. If you had been on B4 and merged, the merge would show A3, A4, and A5's changes on top of B4 instead (you would be on B4 when you called hg diff -c M1).
And no, pushing the merge won't really duplicate changes in any way you'd notice as redundant. The only time I've seen duplicate changes is when somebody rebases changesets that they've pulled from elsewhere or already pushed.
diff -c only creates a diff between the changeset and its first (left) parent (A5 in your case). Thus, it shows everything that has changed on your branch because of the merge from remote.

merging selected revisions from one branch on another in Mercurial

Is it possible to merge a range of revisions from one branch to another in Mercurial?
e.g.
|r1
|r2
|r3
|\___
| | r5
| | r6
| | r7
| | ...
| | r40
|r41
If I want to merge revisions 6 & 7, but not 5, into the main branch - is this possible?
Such a merge can be trivial, for example, if r5 modified files that are not modified in 6 & 7 (and so its changes, if not needed, can safely be ignored)
What about multiple selected revision ranges from branch A to branch B? e.g. merge 4-7, 20-25 and 30-34?
(this isn't a real case, just an illustration. I'm trying to understand if hg has this revision-range merge feature that I know svn has)
Check out the Transplant extension for mercurial, I think it will do exactly what you want.
https://www.mercurial-scm.org/wiki/TransplantExtension
The simple answer is no.
This is contrary to the spirit of Mercurial.
Your graph implies that 6 depends upon 5 so anything that pulls 6 should pull 5 also otherwise it makes no sense.
It seems to me that you got Mercurial wrong here. If your change tree is linear, it's usually a sign that you are using Mercurial like you would use CVS or SVN.
Your change tree should look more like:
4
/ \
/| |\
/ | | \
5 7 23 \
| | | 25
6 8 24 |
26
And then you could pull either the branch starting at 5 or the one starting at 23.
Now, mistake is human, so there is a "facility" called export, which allow you to create a simple diff file from one changeset.
In your specific case, you would thus:
clone the repository up to r4
create a diff from r6 and one from r7 (export)
apply both (in order) on the new clone (import)
run your unit tests until they pass
commit
pull in r41
merge
run your unit tests until they pass
commit
If you wish, you can also go the extension road. There are several useful extensions for manipulating the changesets. A sample set:
Rebase
Collapse
Here you could for example clone the repository (important, always test these on clones, just in case) and then collapse the ranges you are interested in. Then you can export/import them.