Mercurial: How to jump back to previous hash? Equivalent of `cd -` - mercurial

In mercurial, is there a quick way to jump back to the previous hash without typing that hash?
For example if I am at hash abc123 and then do hg update def456, is there a quick generic mercurial command to jump back to abc123 without having to remember I was at abc123?
I'm looking for something analogous to bash's cd - which takes you to the last directory you cd'd out of without having to type out that directory explicitly.
If there isn't one, is it easy to write a little extension where I can type something like hg update - and it will do this? It would be very useful as I jump around a lot.
Thanks!

Mercurial has - at least by default - no provisions to memorize the history of the checked-out revisions.
I also wouldn't count on a completely brain dead way to exist.
I'd first try to hack this feature by means of a hook to hg update where I check the parameter for the special case of '-' and replace it by a stored value. And if update is called without that special value, I'd try to store it in that very file I retrieve it from. But I cannot guarantee that such approach will work.

Related

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

auto-accepting a Mercurial change chunk

I have a very large repo with thousands of files that can regularly get updated by automatic processes that are out of my control (this is for Unity 3D, for what it's worth).
For example, if I upgrade Unity to a new version, it will reimport all textures and maybe add a line in thousands of .meta files that correspond to a new serialized data that didn't exist previously.
Obviously reviewing thousands of files is terrible. Most of the time though, I can quickly identify a particular diff, and would just like to automatically check all the files that have the same diff, commit to get them out of the way, and see what's left: other diffs that I might not know about.
For example I just commited 4000+ files that all contained this diff:
So the pattern would be easy to find:
- textureFormat: -5
+ textureFormat: -1
I suppose I could write a script, or a TortoiseHg tool to do that, I just have no idea where to begin. I'd need to iterate over all changed files/chunks, match a pattern, commit the chunks...
I know of no tool to do exactly what you want. However I believe it's relatively easy to write a small bash script for such or use the command line:
hg diff --nodates --noprefix -U 0 | grep '^+' | grep -v '+++' | sort | uniq -c
will list you the inserted lines of the current diff in descending order of the number of occurences, thus the most frequently occurring diff first.
With that list you get a list of files which match the newly inserted pattern, for instance
hg files "set:grep('^ textureFormat: -1')"
should give you all files with that pattern (whether it's new or not, though). You probably want to check those files, whether their diff contains anything else:
hg diff "set:grep('^ textureFormat: -1')"
Now you can make use of the results and even exclude single files, if the diff output didn't suit you:
hg commit "set:grep('^ textureFormat: -1') and not 'unwantedFilename.cpp'"
In the above commands I made use of the fileset capability and of hg grep which accepts regular expressions. Check hg help grep, hg help fileset and hg help patterns for a more in-depth explanation.

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.

ISO Mercurial "attributes" - tags that apply to more than one changeset, applied after the changeset

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.

Is the mercurial default glob matching syntax configurable?

To run a command on a single file, I recently realized I can do this:
hg log relglob:UniqueFilename
instead of:
hg log some/really/deep/directory/hierarchy/UniqueFilename
I'd like to take this one step further and make relglob the default matching syntax. Is this possible?
No it's not possible with a configuration option. You could change the match default from relpath to relglob: http://hg.intevation.org/mercurial/crew/file/8bc4ad7e34c8/mercurial/cmdutil.py#l272
But I'm really not sure it is a good idea, for example what if you have several files with the same name?