Mercurial: Any way to "merge and discard changes"? - mercurial

I have a graphlog that looks something like this:
(snip)
| |
| o 1) Other Dev: Commit
| | \
o | | 2) Me: Commit
/ | | |
| | o | 3) Other Dev: Commits with an error
| |/| |
| o |/ 4) Me: Merge and commit
| /|
|/ |
o | 5) Me: Realize there were bugs in the commit and take earlier version to merge with
| o 6) Other Dev: Fixes error
o / 7) Me: committing some changes
|/
o 8) Me: Merge fixed tip
At (8), everything is as it should be with the exception of the dangling extra head at (4). To get rid of it I have to merge (4) -.-> (8) but, since there is nothing in (4) that I need, I can safely discard all of it's changes. I could do this merge manually file-by-file (and usually this isn't that big a deal) but for my own edification - is there a simple one-line way to say "merge (4) with (8) and always take (8)"?

Yes. The internal:local builtin merge tool.
$ hg merge 4 --tool internal:local
Similarly there's internal:other that picks the other version of files as the merged version.
Here's an example to clarify what's going on, start off with a repo with a single file:
$ echo a >> a
$ hg ci -Am.
adding a
$ echo a >> a
$ hg ci -Am.
Branch it and put a conflict in a:
$ hg up 0
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ echo b >> a
Also add another file just in the merged in branch:
$ echo b >> b
$ hg ci -Am.
adding b
created new head
Go back and merge the anonymous head:
$ hg up 1
1 files updated, 0 files merged, 1 files removed, 0 files unresolved
$ hg merge 2 --tool internal:local
1 files updated, 1 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
Naturally at this point without the merge tool we'd get a conflict on a. But using the merge tool, we're telling Mercurial to take the version of the first parent on every file that the merged with cset has also touched.
$ hg st
M a
M b
$ cat a
a
a
$ cat b
b

Related

Mercurial uncommited changes merged when changing branches

I've accidently switched branches while having uncommitted changes. It seems that mercurial has automatically merged my changes, but there are conflicts left behind.
How can I go back to having my named branch checked out and commit my changes there?
It seems that mercurial has automatically merged my changes
In seems only for you (and your Mercurial), because for this toy-repo
# 2[tip] e173e81c9fff 2016-07-08 10:09 +0500 lazybadger
| Creating branch
|
o 1 befd1e011923 2016-07-08 10:06 +0500 lazybadger
| Edit
|
o 0 45f15df3f7c9 2016-07-08 10:05 +0500 lazybadger
Start
and dirty working dir at rev. 1
>hg id -nib
befd1e011923+ 1+ default
>hg st
M 1.txt
update to tip doesn't change content of file in working directory
>hg up Feature
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
(note "0 files updated, 0 files merged") and working directory is still dirty
>hg id -nib
e173e81c9fff+ 2+ Feature
>hg st
M 1.txt
PS - you can just disable update to another changeset if your working directory isn't clean: use -c option as mandatory for all hg up (create alias)

(How) can I collapse commits branching out

My revision graph looks like:
F
|
E
|\
| |
C |
| D
| |
B |
| |
|/
|
A
I want to collapse all commits from B to F into a single one. A is not mine, and I don't want to touch it. I'd like to have in the end:
X
|
A
Is this possible ? I've tried various collapse and rebase commands but could not achieve this.
You can accomplish this with the mq extension for the patch queue. You will need to remove the merge changeset (since I don't think you can qimport a merge) and reorder the patches (which may require some hand merging of the patch file).
hg qimport --rev F --name F
hg qpop
hg strip -r tip -- this removes the merge changeset
hg qimport -r C -n C
hg qpop
hg qimport -r B -n B
hg qpop
hg qimport -r D -n D -- now you have all 4 changes (D, B, C, F) in the patch queue
At this point you can hg qpush --all to apply all the patches and you will need to resolve all the conflicts that result in rejected patches. This is to manually redo the work that the merge changeset E had previously accomplished. Once that is completed via editing and hg qref commands, qpop all the patches except for D.
hg qfold B C F -- this will merge the B, C, and F patches into patch D
hg qfin D -- this will convert patch D into a finalized changeset
A few more notes:
As always, back up your work before you start (a copy of the folder is fine). You can also use your diff tools after the steps to compare the original and the result to ensure that you didn't miss anything.
If you have pushed any of those changesets to another repo, you will need to delete them there (either with hg strip or changing their phase to secret).
If the merge changeset E did a lot of work (i.e. there were conflicts between B+C and D) the extra step between 9. and 10. will be messy since your typical merge tools are not available.
The follow up question I have for you though: Why? What do you hope to accomplish? Branching and merging with smaller changesets is standard operating procedure with DVCS. After a few more changes, all those changes will scroll into history and never come up again. Worrying about a perfect history graph is really unnecessary.

How do I diff one branch with my default branch

I switched to a branch on my local repo and noticed it gave me message showing x files updated. This surprised me as I didn't know there were any differences on that branch. How do I compare that branch with the default branch to see what has changed?
Use hg diff -r BRANCH1:BRANCH2, where BRANCH1 and BRANCH2 are the names of the branches. This will show you the differences between the heads of the two branches.
You got the message about "x files updated" because there were files changed on the original branch, not necessarily because there were files changed on the other branch. Mercurial shows you the union of the sets of changed files from both branches.
To just list the files with differences, add the --stat option:
hg diff --stat -r BRANCH1:BRANCH2
This gives output like this:
mypath/file1.cpp | 1 -
mypath/file2.cpp | 143 ++++++++++
mypath/file3.cpp | 18 +-
3 files changed, 160 insertions(+), 2 deletions(-)
Or to clean up the output a bit, pipe it through sed to remove everything after the pipe symbols:
hg diff --stat -r BRANCH1:BRANCH2 | sed "s/|.*$//g"
This gives you just a list of the changed files and the summary line at the end:
mypath/file1.cpp
mypath/file2.cpp
mypath/file3.cpp
3 files changed, 160 insertions(+), 2 deletions(-)
To view a diff of branch otherbranch with the current branch:
hg diff -r otherbranch

Mercurial Rebase Scenario

I've read the RebaseProject page and tried a non-trivial example
(not rebasing a complete branch). It's similar to the case rebase D on
I of the scenario B.
Here's the situation before the rebase:
default : 0 ----- 2
\
feature : 1 ----- 3
Now I'd like to rebase 3 on 2, giving:
default : 0 ----- 2 ----- 3
\
feature : 1
Unfortunately the exact commands aren't given in the RebaseProject
page, but from my understanding of the usage synopsis it should be:
hg rebase --source 3 --dest 2
But somehow my understanding must be flawed, because I get a rebase combined with a merge:
default : 0 ----- 2 ----- 3
\ /
feature : 1 -------
Why is that?
Commands to reproduce the scenario:
hg init
touch a
hg add a
hg commit -m "added a"
hg branch feature
touch b
hg add b
hg commit -m "added b on feature"
hg up -C default
touch c
hg add c
hg commit -m "added c on default"
hg up -C feature
echo "feature" >> a
hg commit -m "changed a on feature"
hg rebase --source 3 --dest 2
Your scenario looks very similar to part rebase G onto I of scenario B of the Rebase Project Scenarios:
Scenario B
...
...
rebase G onto I
In your scenario, D == 1, I == 2 and G == 3. After rebasing, 3 maintains its relationship to 1 just like G' maintained its relationship to D. This is because D is not an ancestor of I and:
Note: Rebase drops a parent relationship only if the parent is an ancestor of target.
You really want to remove that relationship, then, according to the docs, you need a development version to get the --detach option:
Using a development version is available the new --detach option that drops this relationship.

How to remove accidental branch in TortoiseHg?

(I am a relative newcomer to TortoiseHg, so bear with me :-) I use TortoiseHg on two machines to talk to my remote source repository. I made changes on one machine, committed them, and attempted to push them to the remote repository BUT I forgot to first do a pull to get the latest code first. The push gave me a few lines of output, suggesting I may have forgotten to pull first (true!) and mentioned something like "abort: push creates new remote branches...".
So I did a pull, which added several nodes to the head of my graph in the repository explorer. The problem is that the push I tried to do is now showing as a branch in the repository explorer. Looking from the server side (codeplex), it shows no sign of my attempted push, indicating this accidental branch is still local on my machine.
How could I remove this accidental branch? I tried selecting that node in the graph then doing "revert" but it did not seem to do anything. I am wondering if it would be simplest to just discard my directory tree on my local machine and do a completely new, clean pull from the server...?
First make sure you have committed all your local changes. Then merge the branches by calling hg merge and commit the result.
This should bring you back to a single branch, reuniting the two heads.
I had a branch I didn't want, but found I could not merge the branch (or it was very difficult for me to figure out how to merge) because it contained a conflict based on a file name case change.
Ultimately, I decided to hg commit --close-branch. Since the branch had existed only on my local repo, and not the repo I had cloned from, the subsequent hg push didn't even bother to push the closed branch out to the master repo! Very convenient. At that point, all I had to do to eliminate it entirely was delete my local repo and re-clone from the "master".
Do a "Merge With" and check "Discard all changes from merge target (other) revision". Of course you should make sure that the target shown as the merge target is really the one you want to throw away before you click the Merge button.
In the Repository explorer, choose the first rev of your local changes, then right click on the tip of the branch you just pulled and choose "Rebase on top of selected" or "Modify history->Rebase on top of selected" depending upon your client version. This will "re-base" your revs on the pulled ones.
Additionally, to help avoid it in the future...
In Repository Explorer, choose Tools->Settings. In the top left drop-down, choose "User global settings", so this applies to all repositories. Then choose Synchronize on the left. In "After Pull Operation" choose "rebase". This will cause your local revisions to be "rebased" upon the revisions you just pulled, rather than leaving them in a different branch.
This is how I do it and is probably what you typically want.
For more info, see the rebase project and the rebase extension.
This is how to do it with the command-line tool. I guess it can be easily mapped to TortoiseHg (although I'm not sure, since I never use it...) Anyway, since it should be done just once in a while, I think there is no problem on using the terminal here.
An example setup
Suppose your remote repository is something like this:
# changeset: 3:a4c18a1fba12
| tag: tip
| summary: commit 4
|
o changeset: 2:91c5dbfba15c
| summary: commit 3
|
o changeset: 1:4c77cb7952ea
| summary: commit 2
|
o changeset: 0:085dae46f27e
summary: commit 1
Locally, you did not have commit 4, so you committed something directly over commit 3:
# changeset: 3:39526003350f
| tag: tip
| summary: commit 4 made locally
|
o changeset: 2:91c5dbfba15c
| summary: commit 3
|
o changeset: 1:4c77cb7952ea
| summary: commit 2
|
o changeset: 0:085dae46f27e
summary: commit 1
So you try to push it, and get this message:
$ hg push
pushing to ssh://hg#bitbucket.org/brandizzi/mercurial-test-repo
searching for changes
remote has heads on branch 'default' that are not known locally: a4c18a1fba12
abort: push creates new remote head 39526003350f!
(pull and merge or see "hg help push" for details about pushing new heads)
As requested, you pull it:
$ hg pull
pulling from ssh://hg#bitbucket.org/brandizzi/mercurial-test-repo
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files (+1 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)
You have it now...
o changeset: 4:a4c18a1fba12
| summary: commit 4
|
| # changeset: 3:39526003350f
|/ summary: commit 4 made locally
|
o changeset: 2:91c5dbfba15c
| summary: commit 3
|
o changeset: 1:4c77cb7952ea
| summary: commit 2
|
o changeset: 0:085dae46f27e
summary: commit 1
... but you would rather not merge as requested. You want to have this instead:
o changeset: 4:a4c18a1fba12
| summary: commit 4 made locally
|
o changeset: 3:a4c18a1fba12
| summary: commit 4
|
o changeset: 2:91c5dbfba15c
| summary: commit 3
|
o changeset: 1:4c77cb7952ea
| summary: commit 2
|
o changeset: 0:085dae46f27e
summary: commit 1
And then you want to push it to the remote repo.
How do you get that?
Solving it
To get that, you cannot have pushed the "commit 4 made locally". Also, there is no way to put it after the new remote commits. Said that, we can get what we asked.
Said that, you just need to rebase your local commit onto the new remote commit:
$ hg rebase --source 3 --dest 4
If you are lucky, that will be enough.
Handling conflicts
If you are unlucky, you may have some conflicts:
$ hg rebase --source 3 --dest 4
rebasing 3:39526003350f "commit 4 made locally"
merging test.txt
warning: conflicts while merging test.txt! (edit, then use 'hg resolve --mark')
unresolved conflicts (see hg resolve, then hg rebase --continue)
Then, just resolve the conflicts (by manually editing them):
$ hg st
M test.txt
$ nano test.txt # Edit and save
...mark the file as resolved...
$ hg resolve --mark
(no more unresolved files)
continue: hg rebase --continue
...and proceed with the rebase:
$ hg rebase --continue
rebasing 3:39526003350f "commit 4 made locally"
saved backup bundle to /home/adam/software/mercurial-test-repo/.hg/strip-backup/39526003350f-64863882-backup.hg
Here is your new history:
# changeset: 4:ca31fe8a15f0
| summary: commit 4 made locally
|
o changeset: 3:a4c18a1fba12
| summary: commit 4
|
o changeset: 2:91c5dbfba15c
| summary: commit 3
|
o changeset: 1:4c77cb7952ea
| summary: commit 2
|
o changeset: 0:085dae46f27e
summary: commit 1
Now, push it:
$ hg push
pushing to ssh://hg#bitbucket.org/brandizzi/mercurial-test-repo
searching for changes
remote: adding changesets
remote: adding manifests
remote: adding file changes
remote: added 1 changesets with 1 changes to 1 files
Those days, it is not as complex as it used to be, right? :)