Refer to a mercurial revision relative to a named revision - mercurial

In git, you can do refer to revisions by something like master^^, meaning two revisions before master. Can you do the same in Mercurial in some way (or with some extension)? For example, if I want to do "the revision before tip", something like tip^.
edit: Rafa mentions hg parents which works for 1 level deep. How do I do it for arbitrary levels deep.

For commands that have options to specify a revision, use -r -2 for the revision prior to the tip, -r -3 for the revision before that, etc.
From hg help revs:
A plain integer is treated as a revision number. Negative integers are treated as sequential offsets from the tip, with -1 denoting the tip, -2 denoting the revision prior to the tip, and so forth.
There is also the ParentrevspecExtension that allows you use syntax like: tip^ for the parent of tip, tip^^ for its parent, etc.

As mentionned by Niall C., when the revisions are linear, it is possible to use the negative specification to go back starting at tip (only works with linear history, obviously).
Starting with Mercurial 1.6, there is something similar to Parentrevspec, but in core to designate complex revision specification: revsets.
For example, "p1(tip)" is the first parent of "tip", etc.

You can do "hg parents -r revision"

Related

What are all of Mercurial's built in commit identifiers?

I'm looking for easy ways to move around to different commits, sometimes within a branch (and not necessarily from the latest commit). For example, I'd want a way to always get to the previous commit:
# move to commit before current commit
hg checkout -r ~.1
or move to the top of the branch
hg checkout tip
But I can't figure out things like how to move to the next commit (i.e. the one above the current commit, the negation of ~.1). hg seems to have built in ways of referencing these things (e.g. tip (latest commit), . (current commit), and .~N (N-th previous commit)), but are there any others?
You have to re-read hg help revsets carefully and a) build (if needed) b) use these revsets in hg commands
If you want to use "~" notation, you have to use proper format of revset hg log -r ".~1" for immediate parent and remember "only 1-st parent is evaluated" (mergesets, f.e, have two parents)
"x~n"
The nth first ancestor of x
Top of named branch (branch head) isn't tip (tip - ltest commit in repo, can be in another branch), but branchname per se for hg up
With "x~n" revsets you can use negative numbers also: for n < 0, the nth unambiguous descendant of x.

Mercurial: Most recent change per file

I'm looking for a way to make Mercurial output a table like this:
File Most recent revision changing the file Date of that revision
==== ====================================== =====================
foo.py 44159adb0312 2018-09-16 12:24
... ... ...
This is just like github does it on the "Code" overview page. (screenshot from torvalds/linux):
"Most recent" could refer the date or to the DAG hierarchy relative to the current changeset, or maybe to the current branch. Perhaps the latter is more useful, but in my particular use case, it doesn't make a difference.
I'd also like to be able to provide a list of files or a subdirectory for which I want the table. (I don't necessarily want it for everything)
I am aware that I could do it using a small script, looping over hg log -l 1 <file>, but I was wondering if there is a more efficient / more natural solution.
You won't get around looping over all files. Yet with hg manifest you get that list of files. Then template the output as needed:
for f in $(hg ma); do hg log -l1 $f -T"$f\t\t{rev}:{node|short}\t\t{date|isodate}"; done
This gives output like
.hgignore 38289:f9c426385853 2018-06-09 13:34 +0900
.hgsigs 38289:f9c426385853 2018-06-09 13:34 +0900
.hgtags 38289:f9c426385853 2018-06-09 13:34 +0900
You might want to twiddle more with the output formatting. See the mercurial wiki for a complete overview of output templating.
Git will follow the commit DAG, because that's all it has. In Mercurial, you have (many) more options because you have more data.
Probably the ideal option here is follow(file, .) (combined with first or last as appropriate). But as hg help revset will tell you, you have the following options (I've shrunk the list to the obvious applicable ones):
ancestors(set[, depth])
Use this with the set being . to get ancestors of the current commit, for instance, if you want to do DAG-following a la Git. Or, use ::., which is basically the same.
branch(string or set)
Use this with . to get all commits in the current branch. Combine with other restrictors (e.g., parents) to avoid looking at later commits in the current branch if you're not at the tip of the current branch.
file(pattern)
Use this with a glob pattern to find changesets that affect a given file.
filelog(pattern)
Like file but faster, trading off some accuracy for speed (see documentation for further details).
follow([file[, startrev]])
To quote the documentation:
An alias for "::." (ancestors of the working directory's first parent).
If file pattern is specified, the histories of files matching given
pattern in the revision given by startrev are followed, including
copies.
modifies(pattern)
Use this (with any pattern, not just glob) to find changesets that modify some file or directory. I think this is limited to M type modifications, not addition or removal of files, as there is also adds(pattern) and removes(pattern). Use all three, or-ed together, to find any add/modify/remove operations.
first(set, [n])
last(set, [n])
limit(set[, n[, offset]])
Use this to extract a particular entry out of the revset.
When searching forwards (the default), last(follow(file, .)) seems to work nicely to locate the correct revision. As you noted, you have to do this once per file—it will definitely go faster if you write your own Mercurial plug-in to do this without reloading the rest of the system all the time.
Somehow more efficient / more natural solution can be:
create template|style for desired log output (I can't predict, which way will be better for you)
create alias for hg log -l 1 --template ... or hg log -l 1 --style ...
EDIT
A lot later, more correct solution (from recent discoveries) with hg grep
hg grep "." "set:**.py" --files-with-matches -d -q -T"{files % '{file} {date|age}\n'}"
Part of output in test-repo
hggit/__init__.py 7 weeks ago
hggit/git_handler.py 7 weeks ago
hggit/gitdirstate.py 7 weeks ago
…
You have to modify fileset in order to get results only for part of your tree (for all branches) and, maybe, template in order to fulfill your needs.
I didn't have fileset for selecting "files in branch X" just now, I think, it will be something using revs() predicate
"revs(revs, pattern)"
Evaluate set in the specified revisions. If the
revset match multiple revs, this will return file matching pattern in
any of the revision.
because some not published predicates (according to examples, see # "set:revs('wdir()'..." for referencing working directory) can be used for defining revset and I can't discover/predict the correct form for branch predicate

Mercurial diff including first changeset

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.

In mercurial find all changesets that are outgoing and have a parent that is not outgoing

I have a large number of separate lists of changesets that I pulled from a co-worker. I want to strip these out.
I can identify all the changesets by using:
hg log -r "outgoing() and not author('Brandon Leiran')"
I could use a template to print only the node names and then use this for my strip list, but I'd really like to find only the "base" of each outgoing string of changesets. Can I do this with a revset query? Or something similar?
Since version 1.7, the strip command lets you specify multiple changesets to strip and lets you use revsets. So
$ hg strip "outgoing() and not author('Brandon Leiran')"
will remove all changesets in one command. In other words, you do not need to find the base(s) yourself, strip will handle it for you.
However, if you want the bases to use in some other context, then use the roots function to compute them:
$ hg log -r "roots(outgoing() and not author('Brandon Leiran'))"
I assume you have no "important" (i.e. yours) changesets on top of any of the branches you pulled.
Now if it is so, and the number of separate branches (or lists as you called them) is low, you might probably want to strip the result of this selector several times until it yields no changesets:
min(outgoing() and not author('Brandon Leiran'))
As min returns the changeset with lowest revision number in set, it will be a base of one the branches, which you will strip.

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)