what does 'skipping ancestor revision' mean when using 'graft'? - mercurial

I'm using the graft command to pull 4 changesets from a BranchA into BranchB. When I do, some are grafted and some are not:
PS D:\Dev\ProjectAlpha> hg graft 5f403abb2b24
skipping ancestor revision 15911
PS D:\Dev\ProjectAlpha> hg graft 535ff712c5a3
grafting revision 15925
PS D:\Dev\ProjectAlpha> hg graft cf42b7113e02
grafting revision 15931
PS D:\Dev\ProjectAlpha> hg graft 6920922ee602
skipping ancestor revision 15910
I can't find anything documented that explains this. I can find the source, but maybe it'd be helpful to someone else if someone could come along and explain this.
Is there an order of operations with graft that I need to keep in mind? Am I doing it wrong?

From hg help glossary:
Ancestor
Any changeset that can be reached by an unbroken chain of parent
changesets from a given changeset. More precisely, the ancestors of a
changeset can be defined by two properties: a parent of a changeset is
an ancestor, and a parent of an ancestor is an ancestor. See also:
'Descendant'.
so graft is refusing to make those revisions descendants of your current changeset because they're already ancestors of your current changeset, and having your ancestors be your descendants was shown to not work in the Back to the Future movies. ;)

Related

How to remove a changeset from hg repo without loosing changesets commited after that

I need to remove a changeset say 123b which is somewhere in the middle of several changesets in a branch. I have commited many changesets after 123b. How do I remove the changeset 123b whithout losing recent changes
Say you have changesets:
A-B-C-D-....
(Where your 123b could be represented by, say B here).
If B, C and none of their descendants have been pushed yet (so they only exist in your local repository) then you could first rebase C to A, and then strip B.
Technically, you can't: A changeset includes all of its parent history, so by removing 123b, you destroy all its descendants. Any immediate children are now damaged as they refer to the nonexistent 123b, and any children of those children are indirectly damaged, and so on.
However, there is a bright side. You can replace all of the descendants with new, improved descendants. To do that, use hg histedit. This is a bundled extension that you must enable first. (See, e.g., What are the best and must-have hg / mercurial extensions?) (Alternatively, it's possible to use rebase or graft, but I don't recommend that—histedit is simpler.)
Note that histedit is essentially equivalent to stripping the changeset and all its descendants and then adding new changesets. As such, if you've made the changeset public by sending it to another clone, it will come right back from that other clone later, along with all of its descendants. By default, histedit won't let you edit public changesets, so that you don't make this mistake. See also https://book.mercurial-scm.org/read/changing-history.html.
(There is a better (my opinion) way, but it involves adding a non-bundled extension, specifically the evolve extension. See this answer to a related question. I have not actually used evolve, I just like the theory behind it.)

Mercurial get branch name by changeset

I have tried the
hg log --rev "branch([changeset])"
but what I got is a collection of all the changesets in this branch.
What I do want to get is the name of the target branch (e.g. default) instead of the whole collection.
Is there a way to achieve this goal?
That's... not what revsets are for. You want to do something rather different:
hg log --rev [changeset] --template "{branch}\n"
See hg help templates.

Understanding the Mercurial merge changeset

If I have two heads in my repo - C and D which have the common parent of B - and I do an hg merge then a hg commit, what exactly is in that merge changeset? I am trying to understand what I see what I do hg diff -c xyz where xyz is the id of the merged changeset.
Will the changeset show the diffs of all files modified in C and D vs. the state of those files as they existed in the common parent repository B?
A merge commit has two parents, so when running a diff it is important to understand which parent you are diffing against.
hg diff -c <changeset> shows the diff of the changeset relative to the first parent.
From hg help diff:
diff may generate unexpected results
for merges, as it will default to
comparing against the working
directory's first parent changeset if
no revisions are specified.
To diff against a specific parent, it is best to provide the explicit revision of both the merge changeset and corrent parent (e.g. hg diff -r <parent> -r <merge changeset>
Mercurial implements a directed acyclic graph so in your merge changeset depends on where you're coming from. When you type "hg merge" it assumes "tip" (much in the same way hg pull assumes the root repository from which you cloned your head) If you type "hg heads" you can see which one is the tip. When you do this merge, assuming C as the tip, you are merging in the changes from D.

Mercurial remove changeset, history with merges

I want to remove a changeset from history but hg export does not work for merge changesets (see https://www.mercurial-scm.org/wiki/Export). Is there a way to use hg pull for a range of revisions? For example, say I want to remove revision 999 I want to be able to say:
hg init NewRepo
hg pull ../OldRepo -r 0:1000
hg pull ../OldRepo -r 1000:tip
Any ideas?
Pull can't pull a range of revisions because every mercurial revision that exists in a repository must have all of its ancestor changesets in that revision too. So if you do:
hg pull ../oldRepo -r 1000
you get all of revision 1000's ancestor revisions too.
In general, mercurial is about building an immutable history -- if you regret a changeset you don't remove it you create a new one that undoes the work of the changeset you no longer like. If you can't abide having full history in your repo you need to use something like the strip or convert extension. Both work as advertised, but provide plenty of opportunities to shoot yourself in the foot. If you can stand it, just undo the onerous changeset in a subsequent changeset and move on.

What does a mercurial revision with no parent mean?

I have a Mercurial repository that is in a strange state now. This is what it looks like in TortoiseHG:
I didn't think this would be possible. Revision 54 has a parent of "-1 (000000000000)" (i.e. nothing). There's clearly something I don't understand yet about Mercurial, can anyone let me know what this means - and what must have happened for it to get into this state. As far as I know, it's only had stuff pushed and pulled from it - and nobody has been using any wacky extensions.
Revisions 54 and 55 were just adding tags, but if I 'update -C' to revision 54 I end up with ONLY the .hgtags file.
I've made a clone from revision 53 to fix this. But I'd rather understand what happened here, so I can avoid it happening again.
When you look at the definition of a changeset, you see:
Each changeset has zero, one or two parent changesets:
It has two parent changesets, if the commit was a merge.
It has no parent, if the changeset is a root in the repository.
There may be multiple roots in a repository (normally, there is only one), each representing the start of a branch.
"Updating" back to a changeset which already has a child, changing files and then committing creates a new child changeset, thus starting a new branch. Branches can be named.
So maybe this is what you did:
updating back to 53 (which had already a child '54' of its own back then)
changing files
committing, thus starting a new branch from 54, with no parent
(that would make a second commit with the same parent)
or:
comitting 53 with a --close-branch option,
potentially a new commit (without switching back to another branch) might begin a new one
Ry4an (an actual Mercurial specialist ;) ) chimes in and comments:
--close-branch doesn't do anything except hide a branch from a list, and it's undone next time you commit on that branch. It won't create multiple roots.
VonC is right in his diagnosis, multiple heads.
But no combination of 'update' and 'commit' will get you into that state.
To end up with multiple roots one usually has done a 'hg pull' from repo and used --force to override an "unrelated repositories" warning.
("no parent", meaning the parent ids are set to 00000, see "behind the scene":
(source: red-bean.com)
)
Another way to see this is if you did hg update null after committing rev. 53. For example, consider this sequence:
hg init foo
# create some files
hg addremove
hg commit -m "Revision 0"
# edit, edit, edit
hg commit -m "Revision 1"
hg update null
hg tag -m "Create tag v1.0.0.0" "v1.0.0.0"
At this point, hg log will show revision 2's parent as -1:0000000000. Since hg update null clears out the working directory, the only file in it would be .hgtags (just like you were seeing).
Did you have other tags prior to rev. 53? If my suspicion is correct, they would not be present in your rev. 54 .hgtags.