Retroactively use Mercurial rename - mercurial

In my project, I haven't been using hg remove, hg mv or hg addremove due to ignorance. Consequently, every time I've renamed or moved a file, the history of that file has been messed up and now when I look at an individual file's history, I will only see a portion of the history.
What I'm looking for is a way to go back and retroactively fix all of those renaming mistakes so that the file history will stay together. What I imagine would be most likely is a way to edit the data in ".hg\store\data" to make this work. I've been experimenting, and I see the lines copy: and copyrev: in the data for the files I've renamed, so I suspect that has something to do with it.
Assume that I have control of the central repository and that there are no clones of it currently.

Summary:
Since you have full control of the repo this can be 100% fixed using normal hg commands.
The principle idea is to insert new changesets in the right places which effectively correct the original ones.
Let's say your history looks like this:
A-B-C-*
(* is your working folder)
and it was in B that you renamed a file in the filesystem without renaming it in hg.
Do this:
hg up A
hg revert -r B --all
hg mv oldfilename newfilename
hg commit -m <message>
The key here is using revert which is used to copy changes from a changeset into your working folder. This only works this way because you have updated to the predecessor of the changeset you are reverting.
at this point your history looks like:
A-B-C
\
B'-*
where B' is the "corrected" variant of B. Continue with:
hg rebase -s C -d B'
and you have:
A-B
\
B'-C-*
You can now clean up by doing:
hg strip B
leaving just:
A-B'-C-*
Of course where I used revisions like B you need to type the actual revision # or hash.
You could also use TortoiseHG or some other GUI to do a lot of these steps.

This answer covers the situation where you do NOT have full control of the repo. Its a little trickier and you can't get quite as clean of a result, but it still can be dealt with using normal hg commands.
The principle idea is to insert new changesets in the right places which effectively correct the original ones, and merge them after the fact.
Let's say your history looks like this:
A-B-C-*
(* is your working folder)
and it was in B that you renamed a file in the filesystem without renaming it in hg.
Do this:
hg up A
hg mv oldfilename newfilename
hg commit -m <message>
at this point your history looks like:
A-B-C
\
B'-*
where B' is the "corrected" variant of B. Continue with:
hg up C
hg merge B'
hg commit
and you have:
A-B-C-D-*
\ /
B'
If you look at the file history of the file in question, it will look something like this:
o D merge
|\
| o B' rename file
| |
o | B change where the file should have been renamed
|
o A some earlier change
/
o ...
|
o ...
So the history is all linked together for the file. Its just a little weird that B looks like it started from nowhere (because it actually did).

Related

How to copy working directory to a new commit

I have the following commit structure:
a->b->c
Now c is an experiment, and it diverges significantly from b (to the point where any changes to b cause conflicts in c). I'd like to keep c, but want to stop maintaing the chain. Because there are so many conflicts between b and c, simple rebasing would be a bunch of work.
So I want to make
a->b
\->c'
where c' is simply a copy of the workspace in c. In git I would use git reset --soft a and just make a new commit for my c', is there something equivalent I can do in mercurial?
Sounds like you want to use revert --all
pseudo commands (letters a,c mean revids of commits ):
update -r a
revert --all -r c
commit -m "new c"
strip -r c
a guide to using revert all using thg GUI can be found here
So I'm going to post what I ended up doing, but hope I get a better answer.
Make a "do nothing" commit on a (in my case, I added a line). We need the fake because rebasing onto a from b will stop saying nothing to rebase.
a->b->c
\->f
hg rebase -s b -d f --keep clones the whole branch:
a->b->c
\->f->b'->c'
hg histedit, fold c' and drop f.
a->b->c
\->c''
hg prune c
a->b
\->c''

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.

'Forgetting' a dead-end branch

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.

How to show list of unapplied changesets in Mercurial

After pushing changesets to a repository called 'A' how can I see the list of changesets waiting to be applied when I am in 'A'?
Expanding on that,
In repo B I push changesets to repo B
I change to repo B
How can I list the changesets pushed in step 1?
Not sure what you mean by "unapplied" changesets, however here's a couple thoughts.
You can easily see what changesets will be pushed to a repository by doing hg outgoing prior to doing the hg push. This will list all of the changesets that will be pushed using default options.
Similarly you can use hg incoming in the destination repository to show what changesets would be pulled from another repo.
As for "unapplied" changesets, if I assume you mean changesets that are newer than the working directory, you could use hg log -r .:tip, which should (I've not had a chance to test it) show all newer revisions, but not actually all recently-pushed ones.
Edit: I've updated the revision set in the -r option to something that should work. Have a look at revsets on the Mercurial manpage for more possibilities.
$ hg summary
parent: 0:9f47fcf4811f
.
branch: default
commit: (clean)
update: 2 new changesets (update) <<<<<
The update bit tells you what (I think) you want.
I had written a different answer, but I ended up with a better way of doing what is needed here (an even better and definitive –for me– solution is at the end of this post, in the [EDIT] section).
Use hg log.
Specifically, issue an hg sum command first. This will give me:
parent: 189:77e9fd7e4554
<some commit message>
branch: default
commit: (clean)
update: 2 new changesets (update)
To see what those 2 new changesets are made of, I use
hg log -r tip -r 2 -v
Obviously, 2 is to be replaced with the number of changesets that hg sum reports.
This works because tip will refer to the most recent (or "unapplied") changeset. By limiting the output to the 2 latest changes (-l 2), the information is shown only for those changesets that I'm interested in. With -v, the list of files affected by the changeset is also shown.
To make things simpler, I have defined a user command in my .bashrc file:
alias hglog="hg log -r tip -l $1"
This allows me to type hg sum (to get the number of pending/unapplied changesets) and then to type hglog x where x is the number of changesets revealed by hg sum.
There is probably a more complete way of doing this, for instance using custom templates, but I guess it's pushing things too far in terms of sophistication.
[EDIT] (Third iteration)
I have reached the most satisfying answer to this question by expanding on the alias idea so that I no longer have to type hg sum. My .bashrc file now contains this:
show_pending_changesets() {
nb=$(hg sum | grep "update:" | sed 's/update: \([0-9]*\) .*/\1/');
if [ `expr $nb + 1 2> /dev/null` ] ; then
hg log -r tip -v -l $nb
else
echo "Nothing new to report"
fi ;
}
...
alias hgwhatsnew=show_pending_changesets
Explanation: I'm using sed to extract the number of changesets from the last line (which is the one that starts with update:) of the output of hg sum. That number is then fed to hg log. All I have to do then is to type hgw and tab-complete it. HTH

mercurial: test whether a branch contains a changeset

I wonder whether there is a mercurial command/extension that just tests whether a given changeset is in a branch. The command would be something like:
hg contains [-r branch] changeset_id
and should check whether the given changeset is in the current/given branch, returning just "Yes" or "No".
I know about the "debugancestor" command, but a "Yes/No" answer is way easier to read.
And if there is, is it possible to check for transplanted changesets as well?
EDIT: The scenario is located in a repo where named branches have multiple heads. Lets say a branch is named "dev-X", having more than 1 head and a longer history, too long at least to track it with various graph visualizations.
I want to figure out whether a changeset X in branch "dev-X" was merged into another head of "dev-X". Therefore I cannot use branch names but only changeset numbers/hashes to specify a branch.
And to top it all, I'm trying to find out whether changeset X was transplanted there, possibly taking more than 1 transplantation step. I know that the necessary info is stored in mercurial (I've seen it when tampering with the mercurial internals), it's just not accessible via the command line interface.
How about this:
hg log -r changeset_id -b branchname
That will give some output if changeid_id includes changes on branch branchname, otherwise no output is returned.
You could wrap it in a bash function if you want:
function contains() {
if [ "$(hg log -r $1 -b $2)" == "" ]
then
echo no
else
echo yes
fi
}
which does this:
$ contains 0 default
yes
$ contains 0 other
no
using 1.6 and later with the power of revision sets all you need is
hg log --rev "ancestors(.) and <revNum>"
eg
hg log --rev "ancestors(.) and 1234"
blank means no, output means yes, its in your history. Some of the other solutions posted here wont work if the changeset was created in a named branch, even if it was merged at some point later.
As mentioned in the comment above I gave it a shot, this is what came out:
http://bitbucket.org/resi/hg-contains/
It should be pretty easy to transform the results from debugancestor into a yes or a no (but there's definitely no built-in way to do that; write a script already!). Be aware that the answer might be wrong if the branch has more than one branch head, though.
(Writing an extension to add a command to do this should also be nigh-trivial, BTW.)
You could always just print out the name of the branch for that revision (it'll be empty if it's default) and then test that against whatever you want (in bash or in a scripting language of some sort):
hg log --template '{branches}' -r <revision name/number>
I've tested most of approaches above, did not work. The extension 'contains' somehow takes wrong revision (I think its a bug), the hg log --rev "ancestors(.) and 1234" work, but I found even more simple approach to do this:
hg merge -P <changeset>
Will show you if anything unmerged remains (it will also include changesets which are not merged parents of the changeset in question)