Recently one of my coworkers grafted a commit that changed one (out of three) subrepositories on .hgsubstate. The graft instead changed all three to match the original changeset(even if they were changes from previous one):
changeset main:
f60c22f43d335e95a95301aee58a092b63800e4b external/a
26e615cf033cffa9fba77d2369e3802cc2b9a95e external/b
ca46ca7e5243439de09a2d14ffa60432c6c56d74 external/c
diff of changeset grafted:
7fe8fcdd7648e14bee5889a4b3155fde49f09de4 external/a
5b90cf021b0006c8681247a73e9940f835a959f4 external/b
-8479ff0a18dc684db7f0771ace700915c51e92e6 external/c
+69e97bdab56155fee6ab4d0d21bbf36b34b040f8 external/c
final graft:
7fe8fcdd7648e14bee5889a4b3155fde49f09de4 external/a
5b90cf021b0006c8681247a73e9940f835a959f4 external/b
69e97bdab56155fee6ab4d0d21bbf36b34b040f8 external/c
It seems that graft is replacing the file wholesale, not just it's specific changes. This is counter to my intuition of what graft should do. Worse, it's doing it even if i use -t internal:fail it doesn't let me merge anything.
I would at least like to know why that unfortunate merge is happening so we can avoid it in the future. I tried to reproduce the problem on a toy example, but i failed; since there are approximately 350 commits/merges separating both changesets, it's very likely i missed the problematic part.
My question is: Can i make mercurial output the steps it's taking to do this graft? Or somehow identify the source of the problematic merge?
A graft operation is a 3-way merge that uses the graft parent revision as the base. Nothing else in the graph should matter.
Source: https://groups.google.com/forum/#!topic/mercurial_general/3pHTx8gT208
It seems that my problem lies somewhere else, but I can close this question now.
Related
This question already has an answer here:
How do I find the merge that moved my changeset to another branch?
(1 answer)
Closed 9 years ago.
I am looking for a way with TortoiseHg (or plain hg if no other possibility exists) to locate all the changes to a particular file.
I've tried using a revision set query:
merge() and file("path/to/filename.cs")
but that didn't get me what I'm looking for. It returns an empty set. I assume this is because merge() only returns merges, and file() only (appears to) returns non-merges, so the intersection is empty.
I've also tried modifies(pattern) but that doesn't appear sufficiently different from file(pattern) (looks to me like file() is the union of adds() and modifies()). contains(pattern) doesn't return any elements at all.
So, is it possible to get a list of changesets in which a particular file has been modified in any way?
It looks like you're running into the "how did my change get into this branch" problem. When you merge a changeset into a branch, Mercurial doesn't record that as a change. Instead, it makes a note that the merged changeset is now part of the destination branch. The only time a merge will show that a file was modified is if there was a merge conflict, which results in a new changeset.
Finding what merge brought a changeset into a branch is a two step process. First, you have to find the changeset where the change you're interested in occurred.
hg log -r "file('<PATTERN>')
Once you have that revision, find the first descendant changeset in the destination branch:
hg log -r "first(branch(<BRANCH>) and descendants(<REVISION>))"
hg log -r "branch(BRANCH) and file('PATTERN')"
Revset also can be used and entered into Filter Tooolbar in THG Workbench
hg log grep name_of_your_file
Does the trick for me in terms of finding all the change sets that have resulted in a change to a given file. Of course if you would like something pretty:
thg log name_of_your_file
I have recently encountered the need to generate a Mercurial diff of all changes up to a particular changeset which includes the first changeset of the repo. I realize this kind of stretches the definition of a diff, but this is for uploading a new project to a code review tool.
Let's assume the following changesets:
p83jdps99shjhwop8 - second feature 12:00 PM
hs7783909dnns9097 - first feature - 11:00 AM
a299sdnnas78s9923 - original app setup - 10:00 AM
If I need a "diff" of all changes that have been committed, the only way that I can seem to achieve this is with the following diff command...
diff -r 00:p83jdps99shjhwop8
In this case the first changeset in the argument param (here - 00) takes the regexp form of 0[0]+
This seems to be exactly what we need based on a few tests, but I have had trouble tracking down documentation on this scenario (maybe I just can't devise the right Google query). As a result, I am unsure if this will work universally, or if it happens to be specific to my setup or the repos I have tested by chance.
Is there a suggested way to achieve what I am trying to accomplish? If not, is what I described above documented anywhere?
It appears this actually is documented, but you need to do some digging...
https://www.mercurial-scm.org/wiki/ChangeSetID
https://www.mercurial-scm.org/wiki/Nodeid
So the special nodeid you're referring to is the 'nullid'.
2 digits may not be adequate to identify the nullid as such (as it may be ambiguous if other hashes start with 2 zeros), so you may be better off specifying 4 0's or more.
Eg: hg diff -r 00:<hash of initial add changeset> has resulted in the abort: 00changelog.i#00: ambiguous identifier! error.
I'm a little confused about what you need. The diff between an empty repository and the revision tip is just the content of every file at tip-- in other words, it's the state of your project at tip. In diff format, that'll consist exclusively of + lines.
Anyway, if you want a way to refer to the initial state of the repository, the documented notation for it is null (see hg help revisions). So, to get a diff between the initial (empty) state and the state of your repository at tip, you'd just say
hg diff -r null -r tip
But hg diff gives you a diff between two points in your revision graph. So this will only give you the ancestors of tip: If there are branches (named or unnamed) that have not been merged to an ancestor of tip, you will not see them.
3--6
/
0--1--2--5--7 (tip)
\ /
4
In the above example, the range from null to 7 does not include revisions 3 and 6.
BRIEF
How do I tag multiple changesets in Mercurial with the same tag? Possibly on the same branch, possibly on different branches.
E.g. something like a tag that says whether the full QA test ran.
I can create multiple instances of a tag by editing the file, but the hg tools nearly always ignore all but the first.
DETAIL
I am looking for what I call changeset "attributes" - a concept that I have used in other CVS and DVCS, but which I cannot seem to find in Mercurial.
Basically, an attribute is very much like a tag, but where a tag is only supposed to refer to a single changeset, an attribute may apply to multiple changesets.
Q: does anyone know how to do this?
Similarly: is there a way to attach a description to a ChangeSet, after the changeset has been created. Note that i do not want to rewrite history: I do not want to delete or change or replace the original checkin message. I just want to add some more stuff - and have that more stuff appear in queries like hg log. E.g. "I forgot to add a file to commit df..a3 - look instead to commit 8f..77 where I checked in the missing files.
EXCRUCIATING DETAIL
I know - you can do hg tag -f to force a tag to apply to more than one changeset. But so many other hg tag related features really only work with a single changesrty per tag. Or at least only one changeset per line of descent - i.e. per head.
So you can leave a tag defined forever and ever. I like placing the date or other context in such a tag - e.g. tests-pass-2012-01-14.
Or you can have a "floating tag", that moves upwards - e.g. "most recent rev where all the slow tests pass", which I might call simply "tests-pass".
(By the way, you may apply such attributes or tags after the checkin - especially if you have a slow QA process, perhaps a quick smoke test, followed by a slower full set of tests that may take a week to complete. So you checkin, and then, later, go back and apply the attribute, the uniqified dated tag. And you may later need to go back and modify such a tag, e.g. if more tests are added, so that a changeset that used to pass all tests no longer does. E.g. all-tests-pass-2012-01-14 and all-tests-pass-2012-01-15 may apply to the same changeset.)
But it is onerous to have to uniqify such fixed tags. Hence what I call an attribute: a tag that applies to multiple changesets, Which is version controlled. So you might apply all-tests-pass to rev 105, and then later to 106 and 107. But then you realize that new tests fail on 106, so you repally.
Then the attribute history might look like
105:
tagged all-tests-pass on 2012-01-14-10h00 (in changeset XXX)
tagged all-tests-pass on 2012-01-15-10h00 (in changeset YYY)
106:
tagged all-tests-pass on 2012-01-14-13h00 (in changeset XXX)
tagged not-all-tests-pass on 2012-01-15-13h00 (in changeset YYY)
107:
tagged all-tests-pass on 2012-01-14-14h00 (in changeset XXX)
tagged all-tests-pass on 2012-01-15-10h00 (in changeset YYY)
and a revset query like
105::107 and current_attribute_tag(all-tests-pass)
= returns 105 and 107 on the latest, at or after YYY is in the repo
= but returns 105, 106, 107 if cloned so as not to include YYY
while
105::107 and attribute_tag_at_any_time(all-tests-pass)
=returns 105 106 and 107 at any time if the repo holds XXX
===
I would like to be able to do things like
run hg bisect, but only on changesets tagged tests-pass.
exclude certain log messages from hg log and glog
etc.
===
By the way, I reject phases and bookmarks for this purpose because they are not version controlled. And I want these attribute tags to be VCed, so that I can follow something like to ebb and flow of all-tests-pass, as mentioned above.
branches are almost what I want, because Mercurial branches are really changeset attributes, not branches. But I don't think that the branch associated with a changeset can be changed after the commit.
(I really wish that you could switch changesets to a branch after you commit them. I call this wished for feature "retroactive branching".)
===
Here's a classic example of why I might want attributes: have you ever forgotten to add a file to the VCS? And then have a changeset that fails to build? Add the file in a subsequent changest? ...
I would like to be able to retroactively mark a changeset as will-not-build--missing-files. And then have bisect not even bother looking at such changesets.
Have a look at this: Custom revision properties in Mercurial?
There's no native support for attributes. You could write an extension (there's a dictionary of extra properties that get saved with a changeset).
Or you could hack it together with multiple changesets per tag (as you suggested).
Or you could hack it together with a new all-tests-pass branch, and have your CI server merge to that branch when all tests pass (then bisects are being tip of default and tip of all-tests-pass).
But the short answer remains that there's no existing native way to do it.
We have switched to Mercurial recently. All had been going well until we had two incidents of committed changes going missing. Examining the logs has not made us any wiser.
Below is an example. The files committed at (1) revert to a previous state at (2) even though those files are not mentioned in the merge.
What can I check to understand why the files reverted?
There are three interesting changesets in this graph that can influence the (2) merge:
Teal changeset: not shown, but looks like it's just below the graph. This is the first parent of (2)
Blue changeset: number five from the bottom, labelled "Fix test". This is the second parent of (2).
Common ancestor of the parents: also not shown, will be further below. Strangely, it looks like the teal changeset could be the common ancestor, but Mercurial will now allow you to make such a degenerate merge under normal circumstances.
When Mercurial does a merge, these are the only three changesets that matter: the two heads you merge and their common ancestor. In a three-way merge the logic is now:
ancestor parent1 parent2 => merge
X X Y Y (clean)
X Y X Y (clean)
X Y Y Y (clean)
X Y Z W (conflict)
Read the table like this: "if the ancestor was X, and the first parent was also X and the second parent was Y, then the merge will contain Y". In other words: a three-way merge favors change and will let a modification win.
You can find the ancestor with
$ hg log -r "ancestor(p1(changeset-2), p2(changeset-2))"
where changeset-2 is the one marked with (2) above. When you say
The files committed at (1) revert to a previous state at (2) even though those files are not mentioned in the merge.
then it's important to understand that "a merge" is just a snapshot that shows how to mix two other changesets. The change made "in" a merge is the difference between this snapshot and its two parent changesets:
$ hg status --rev "p1(changeset-2):changeset-2"
$ hg status --rev "p2(changeset-2):changeset-2"
This shows how the merge changeset is different from its first and second parent, respectively. I'm sure the files are mentioned in one of those lists — unless the merge isn't the culprit after all.
When you examine the three changesets and the differences between them, then you will probably see that someone has to resolve a conflict (the fourth line in the merge table above) and picked the wrong file at some step along the way.
The merge at 2 is between a very old branch (dark blue, forked from the mainline/green branch just after commit 1) and an even older branch (light blue, hasn't been in sync with mainline since before commit 1)
It seems likely that the merge at 2 picked the wrong version of the file - can't tell from here if that was the tool picking the wrong version of the file, or the user manually selecting the wrong version.
Edited to add:
To help track down exactly what changed at 2, you can use hg diff -r REV1 -r REV2, which will show you the line-by-line differences between any two revisions.
When you know that the badness was introduced sometime between point 1 and point 2, hg bisect may help you track down the exact source of the badness:
hg bisect [-gbsr] [-U] [-c CMD] [REV]
subdivision search of changesets
This command helps to find changesets which introduce problems. To use,
mark the earliest changeset you know exhibits the problem as bad, then mark
the latest changeset which is free from the problem as good.
Bisect will update your working directory to a revision for testing
(unless the -U/--noupdate option is specified). Once you have
performed tests, mark the working directory as good or bad, and bisect
will either update to another candidate changeset or announce that it
has found the bad revision.
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)