Breaking Mercurial file "links"? - mercurial

We seem to have messed up some of our HG configuration.
When merging we see a few of these entries:
warning: detected divergent renames of core/src/main/java/x/y/z/MyFile.java to:
core/src/main/java/x/y/z/NewFileA.java
core/src/main/java/x/y/z/NewFileB.java
MyFile should not be linked to the "new files". How can you break this link?

You cannot break the link after the fact. The link was created when you (most likely) did
$ hg rename MyFile.java NewFileA.java
in one branch and someone did
$ hg rename MyFile.java NewFileB.java
in another. When merging the two branches, you will end up with both NewFileA.java and NewFileB.java. Some might consider that a conflict, hence the warning.
It's also possible that you did
$ hg copy MyFile.java NewFileA.java
$ hg copy MyFile.java NewFileB.java
and did so on purpose. In that case there's no conflict and the warning is unneeded. Mercurial cannot distinguish the two cases internally since hg rename X Y is the same as hg copy X Y; hg remove X. See my other answer for information about what hg copy really does.
So, don't worry, nothing is messed up. After you merge you wont see the warning again — the warning only appears if you merge with a branch where MyFile.java still exists.

Unless there's some negative repercussion that I'm not aware of, you can break the link by doing a remove or forget of the inadvertent copy target file, committing that, and then re-adding the file (with the same content). Changes to the original copy source will not be merged to the re-added file.
In Mercurial 3.7.2 I have been able to reproduce subsequent merges (not just the first merge) applying changes to the copy target, which is counter to the documentation, although I have not dug into this further. Here I am referring to merges to the branch where the copy was committed, not the other direction.

Related

Mercurial: How to re-include ignored files after the merge

In mercurial, the merge ignored some files (possibly a human mistake). How I can 're-include' the ignored files?
Scenario: the merge target(rev #47) has 5 files, but the merge(rev #50) has included just 1 file and others are ignored.
Apparently, when I check the resulting code, the code from Rev# 47 is missing.
You probably did the merge in a way you didn't intend, e.g. telling hg to only accept changes from one branch, discarding the changes from the merged rev47.
The easiest way is to start with a repo where the merge did not happen and re-do the merge. This is not possible, if it is a shared repo and that merge is already shared with others. If it everything local, but if that repo is your only copy, make a new clone without the merge: hg clone -r49 OLDREPO .
Thus if you cannot simply re-do the merge on a new repo without that merge, you'll have to go a longer way which is summarized in this answer. Note the screenshot in the accepted answer and DO NOT discard the changes from the merge target.

How to diff two revisions of a file that got renamed in between and Mercurial doesn't know about the renaming?

I accidentally renamed a file outside of Mercurial. When I committed the change, Mercurial treated the change as two unrelated files (ie. a remove and a add). I need to go back to diff the two revisions but I don't know how to do so when Mercurial sees them as two respective files across different revisions. What can I do to diff the files?
You didn't say what operating system you were using. The following will work with bash on Linux:
diff <(hg cat -r rev1 file1) <(hg cat -r rev2 file2)
You can replace diff with another program like vimdiff if you want a visual diff.
If you want to actually fix the history so that Mercurial is aware of the rename (and can use that information in future merges if needed), there's a way to do so documented on the Tips and Tricks page on the Mercurial wiki.
Current contents copied here for ease of use (and in case the link gets broken later):
Steps:
Update your working directory to before you did the rename
Do an actual "hg rename" which will create a new head
Merge that new head into the revision where you did the "manual" rename (not the head revision!)
Then finally merge the head revision into this merge result.
Advice:
Make a clone first and work on that, just in case!
After finishing the steps, use a file compare tool to check that the original and the clone are identical
Check the file history of any moved file to make sure it is now restored
That being said, if all you want to do is compare the contents at the point in time, you can definitely accomplish that without making Mercurial aware of the rename (as mentioned in Stephen Rasku's answer). In fact, you can use a combination of "hg cat" and an external comparison tool to compare any files, not just ones that Mercurial knows about.
Fix history:
Update to first changeset with new-filename, save file outside WC
Update to parent of bad replacement changeset, replace file correctly (with rename tracking), commit, got second head
Rebase all changesets from old anonymous branch on top of fresh good changeset
--close-branch on bad-replacement changeset or delete this unwanted changeset or leave inactive head intact

Mercurial: how to amend the last commit?

I'm looking for a counter-part of git commit --amend in Mercurial, i.e. a way to modify the commit which my working copy is linked to. I'm only interested in the last commit, not an arbitrary earlier commit.
The requirements for this amend-procedure are:
if possible, it should not require any extensions. It must not require non-default extensions, i.e. extensions which do not come with an official Mercurial installation.
if the commit to amend is one head of my current branch, no new head should be created. If the commit is not head, a new head may be created.
the procedure should be safe in a way that if for whatever reasons the amending fails, I want to have the same working copy and repository state restored as before the amending. With other words, if the amending itself can fail, there should be a fail-safe procedure to restore the working copy and repository state. I'm referring to "failures" which lie in the nature of the amend-procedure (like e.g. conflicts), not to file-system-related problems (like access restrictions, not being able to lock a file for writing, ...)
Update (1):
the procedure must be automatable, so it can be performed by a GUI client without any user interaction required.
Update (2):
files in the working directory must not be touched (there may be file system locks on certain modified files). This especially means, that a possible approach may at no point require a clean working directory.
With the release of Mercurial 2.2, you can use the --amend option with hg commit to update the last commit with the current working directory
From the command line reference:
The --amend flag can be used to amend the parent of the working directory with a new commit that contains the changes in the parent in addition to those currently reported by hg status, if there are any. The old commit is stored in a backup bundle in .hg/strip-backup (see hg help bundle and hg help unbundle on how to restore it).
Message, user and date are taken from the amended commit unless specified. When a message isn't specified on the command line, the editor will open with the message of the amended commit.
The great thing is that this mechanism is "safe", because it relies on the relatively new "Phases" feature to prevent updates that would change history that's already been made available outside of the local repository.
You have 3 options to edit commits in Mercurial:
hg strip --keep --rev -1 undo the last (1) commit(s), so you can do it again (see this answer for more information).
Using the MQ extension, which is shipped with Mercurial
Even if it isn't shipped with Mercurial, the Histedit extension is worth mentioning
You can also have a look on the Editing History page of the Mercurial wiki.
In short, editing history is really hard and discouraged. And if you've already pushed your changes, there's barely nothing you can do, except if you have total control of all the other clones.
I'm not really familiar with the git commit --amend command, but AFAIK, Histedit is what seems to be the closest approach, but sadly it isn't shipped with Mercurial. MQ is really complicated to use, but you can do nearly anything with it.
GUI equivalent for hg commit --amend:
This also works from TortoiseHG's GUI (I'm using v2.5):
Swich to the 'Commit' view or, in the workbench view, select the 'working directory' entry.
The 'Commit' button has an option named 'Amend current revision' (click the button's drop-down arrow to find it).
||
||
\/
Caveat emptor:
This extra option will only be enabled if the mercurial version is at least
2.2.0, and if the current revision is not public, is not a patch and has no
children. [...]
Clicking the button will call
'commit --amend' to 'amend' the revision.
More info about this on the THG dev channel
I'm tuning into what krtek has written. More specifically solution 1:
Assumptions:
you've committed one (!) changeset but have not pushed it yet
you want to modify this changeset (e.g. add, remove or change files and/or the commit message)
Solution:
use hg rollback to undo the last commit
commit again with the new changes in place
The rollback really undoes the last operation. Its way of working is quite simple: normal operations in HG will only append to files; this includes a commit. Mercurial keeps track of the file lengths of the last transaction and can therefore completely undo one step by truncating the files back to their old lengths.
Assuming that you have not yet propagated your changes, here is what you can do.
Add to your .hgrc:
[extensions]
mq =
In your repository:
hg qimport -r0:tip
hg qpop -a
Of course you need not start with revision zero or pop all patches, for the last just one pop (hg qpop) suffices (see below).
remove the last entry in the .hg/patches/series file, or the patches you do not like. Reordering is possible too.
hg qpush -a; hg qfinish -a
remove the .diff files (unapplied patches) still in .hg/patches (should be one in your case).
If you don't want to take back all of your patch, you can edit it by using hg qimport -r0:tip (or similar), then edit stuff and use hg qrefresh to merge the changes into the topmost patch on your stack. Read hg help qrefresh.
By editing .hg/patches/series, you can even remove several patches, or reorder some. If your last revision is 99, you may just use hg qimport -r98:tip; hg qpop; [edit series file]; hg qpush -a; hg qfinish -a.
Of course, this procedure is highly discouraged and risky. Make a backup of everything before you do this!
As a sidenote, I've done it zillions of times on private-only repositories.
Recent versions of Mercurial include the evolve extension which provides the hg amend command. This allows amending a commit without losing the pre-amend history in your version control.
hg amend [OPTION]... [FILE]...
aliases: refresh
combine a changeset with updates and replace it with a new one
Commits a new changeset incorporating both the changes to the given files
and all the changes from the current parent changeset into the repository.
See 'hg commit' for details about committing changes.
If you don't specify -m, the parent's message will be reused.
Behind the scenes, Mercurial first commits the update as a regular child
of the current parent. Then it creates a new commit on the parent's
parents with the updated contents. Then it changes the working copy parent
to this new combined changeset. Finally, the old changeset and its update
are hidden from 'hg log' (unless you use --hidden with log).
See https://www.mercurial-scm.org/doc/evolution/user-guide.html#example-3-amend-a-changeset-with-evolve for a complete description of the evolve extension.
Might not solve all the problems in the original question, but since this seems to be the de facto post on how mercurial can amend to previous commit, I'll add my 2 cents worth of information.
If you are like me, and only wish to modify the previous commit message (fix a typo etc) without adding any files, this will work
hg commit -X 'glob:**' --amend
Without any include or exclude patterns hg commit will by default include all files in working directory. Applying pattern -X 'glob:**' will exclude all possible files, allowing only to modify the commit message.
Functionally it is same as git commit --amend when there are no files in index/stage.
Another solution could be use the uncommit command to exclude specific file from current commit.
hg uncommit [file/directory]
This is very helpful when you want to keep current commit and deselect some files from commit (especially helpful for files/directories have been deleted).

Why does mercurial merge committed changes in the working copy?

I've been using Mercurial for a few weeks now and don't understand why when Mercurial comes to merge committed changes from two repositories it does it in the working copy?
Surely the merge could happen without the use of the working copy removing the need to shelf changes etc.
It just doesn't seem necessary to involve the working copy. Am I missing something?
There is only one working copy per repository, by definition:
The working directory is the top-level directory in a repository, in which
the plain versions of files are available to read, edit and build.
Unless your file system descends from Schrödinger's cat, you cannot have two versions of the same file at the same time, thus you cannot have two working copies.
Nevertheless, it's indeed theoretically possible to use something like a ephemeral clone (per #Ry4an) to act as the working copy of a merge, resolve conflicts there, commit, then make it disappear. You'd get a beautiful merge changeset and your intact working copy.
I can think of several ways to achieve this:
Petition hg team to do it in core
Write an extension to implement the ephemeral clone or some other way
Shelve with a temporary changeset
Shelve with MQ
I would strongly recommend #4, as I would for almost all workflow scenarios. It took me a few good days to grok MQ, but once I did I've never had to turn back.
In an MQ workflow, your working copy is always the current patch. So for the merge situation you would do:
hg qrefresh
hg qpop -a
hg update -r<merge first parent>
hg merge [-r<merge second parent>]
hg commit
hg update qparent
hg qgo <working copy patch>
You don't have to pop all patches in #2. I always do that whenever I need to deal with real changesets to avoid mixing them up with patches.
Solution #3 is really the same as #4, since a patch is a temporary changeset by definition (this is really the only thing you need for understanding MQ). It's just different commands:
hg commit -A
hg update -r<merge first parent>
hg merge [-r<merge second parent>]
hg commit
hg update -r<working copy changeset parent>
hg revert -a -r<working copy changeset>
hg strip <working copy changeset>
If you want to keep the working copy changeset and continue to commit, simply update to it in #5.
From your question it seems like you already know #4 but don't like shelving. I think shelving is good because merging is a fundamentally different task than coding (changing working copy), and shelving makes the context switch explicit and safe.
I didn't write Mercurial, so I can't say why they did it that way, but here are some of the positive results of that decision:
you can look over the results of the merge before you commit it
you can edit the results of the merge before you commit it
you're encouraged to commit frequently
If you really want to do a merge and have stuff in your working dir that you can't bear to commit don't bother with shelve just do:
cd ..
hg clone myrepo myrepo-mergeclone
hg -R myrepo-mergeclone merge
hg -R myrepo-mergeclone push myrepo
On the same file system clone is near instantaneous and uses hardlinks under the covers so it takes up almost no space past that of the temporary working copy.
As mentioned in the chapter "Merge" of HgInit:
The merge command, hg merge, took the two heads and combined them.
Then it left the result in my working directory.
It did not commit it. That gives me a chance to check that the merge is correct.
Such check can include conflicts in merge, that the user has to review:
In KDiff3, you see four panes
The top left is the original file.
Top center shows Rose her version.
Top right shows Rose my version.
The bottom pane is an editor where Rose constructs a merged file with the conflicts resolved.
So you need a working directory (a view for the merge) in order to resolve fully a merge.

TortoiseHG: cannot partially commit a merge

I tried to merge two heads in Mercurial. After merging, I didn't commit and did some more changes. Then I tried to commit and got the following message:
abort: cannot partially commit a merge (do not specify files or patterns)
I'm using TortoiseHG as visual shell, and Beyond Compare for comparing and merging. And I'm relatively new to all of them.
What should I do to finish commit successfully?
Mercurial/TortoiseHg is correct in telling you that you should not partially commit a merge. Partial means that you do not commit all files at once.
The underlying reason for this message is that is gives you the wrong results. When you merge two changesets in Mercurial, you are creating a new changeset with two parent changesets. This merge changeset shows others how you want everybody else to combine the two changesets.
Let us imagine that you start with changesets A and B and want to merge them. This creates a graph like this:
... --- [A]
\
[M]
/
... --- [B]
Pretend that we added the line A! to a.txt in the A changeset and that we added B! to b.txt in the B changeset. Just two independent changes that does not conflict. If Mercurial allowed you to do a partial commit, then you could do this:
hg merge
hg commit -m 'Added A!' a.txt # creates M
hg commit -m 'Added B!' b.txt # creates M'
and the result is this graph:
... --- [A]
\
[M] --- [M']
/
... --- [B]
If you look at b.txt along the path B, M, M', then you will see that the line with B! was introduced in B, removed in M and reintroduced in M'!
This is not what you want from a merge changeset: a partial merge throws away changes from one branch just to introduce them again in a followup commit. Mercurial trusts you when you create M: it really believes that M contains the correct mix of A and B. In particular, if the B! line is removed in M, then it will remain gone when you merge M with other changesets.
So Mercurial is trying to protect you from creating a bad history by not allowing partial merges.
I think you have aliases in hgrc file. Try to remove [alias] section and commit again.
What should I do to finish commit
successfully?
One of the benefits of Mercurial (or git or any other DVCS) is that you can perform a commit at any point in your development process and it will be both fast and private. It will be fast because you should be committing to a local copy of the repository residing on your hard drive, and it will be private because no-one will see your change set until you push them to the server (or other master repository).
Therefore, to partially answer your question, the appropriate thing to do would have been to commit the merge without your addition changes, and then apply and commit your next wave of changes. If you are using TortoiseHG to peform the merge it will actually prompt you to commit the merge before leaving the GUI because this is the intended HG workflow.
That being said, I made some changes on a named branch (ie: new head), merged it back into the default branch, exited the TortoiseHG GUI without committing, made some more changes, and then committed with no problem. I will ask some clarifying questions below your original inquiry.
I had this problem because I had deleted some files. I reverted the files to restore them, then was able to commit. I then deleted the files and did a second commit.
I had the same problem and I was NOT specifying any files and using command-line.
The problem was the [default] section in my .hgrc
[defaults]
commit = -X project/web.config
So it added specific files to every commit-operation by default.
Review your defaults section for potential problems.
I had the same problem and the command I gave was
hg commit -m Merge with 1234
I figured it out after sometime that the commit message "Merge with 1234" has to be given in quotes as the command takes "with" and "1234" as file name params.
please check it in your case.