Is the mercurial default glob matching syntax configurable? - mercurial

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?

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

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

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.

.hgignore regex syntax to ignore a specific file (e.g. "core") anywhere

Suppose I have a working directory like this:
t.c
core
multicore
test1/core
I want to ignore all "core" files.
If I use "/core$" (4) will get ignored but not (2).
If I use "^core$" (2) will get ignored but not (4)
If I use "core$" (2) and (4) will get ignored but so will (3) which is not what I want.
How do you do this?
planetmaker's answer, "use glob syntax", is simpler and is what I would usually recommend. There is, however, a regexp answer, and a minor flaw in the glob syntax version.
Mercurial uses Python regular expressions, so we have the (alt1|alt2|...) syntax available. Note that these are grouped.1 We can and should use (?:...) to avoid grouping when required, but for .hgignore, the grouping is irrelevant, so it is simpler (and much more readable) to just use the parentheses, and I do so where possible below.
We could just write:
^core$
/core$
to ignore the file core with nothing coming before it (first pattern) and to ignore a file with a name like test1/core (second pattern). This is a fine, but we can compress it a bit more using the alternation syntax. The leading ^ works even in an alternate within a group, as long as it is still, in effect, leading, so:
(^|/)core$
means the same thing and accomplish the job using regexp syntax.
Annoyingly, all of these patterns ignore all files in any directory named core (whether or not we use regexp vs glob syntax):
$ rm core
$ mkdir core
$ touch core/keepme
$ cat .hgignore
syntax: glob
core
$ hg status -A
? .hgignore
? multicore
? t.c
I core/keepme
I test1/core
The problem is that as soon as we say ignore (some pattern that matches a directory named core), if there are files in that directory that are currently untracked, Mercurial ignores them too. You can forcibly add the file—as with Git, once a file is tracked, any ignore-file pattern that matches it becomes irrelevant—but this does not help with additional files we stick into the directory:
$ hg add core/keepme
$ touch core/keep-me-too
$ hg status -A
A core/keepme
? .hgignore
? multicore
? t.c
I core/keep-me-too
I test1/core
Here, regular expressions can prove to be the answer. Python (and Perl) regexps allow "negative lookbehind", i.e., you can say "as long as some pattern does not appear". Hence we can replace the existing .hgignore contents with:
$ cat .hgignore
(?<!^core/).*/core$
and now we have this status:
$ hg status -A
A core/keepme
? .hgignore
? core/keep-me-too
? multicore
? t.c
I test1/core
This particular regular expression depends on the wanted core directory being named core at the top level (^core). If we wanted to keep core directories named core (top level) and a/subsys/core, we would write:
(?<!(^core|^a/subsys/core)/).*/core$
as our regular expression.
Constructing these regexps is something of an art form, and rarely worth a lot of effort. Glob syntax is almost always simpler, and as long as it suffices, I prefer it. It was once significantly slower than regexp syntax but this was fixed back around Mercurial 3.1.
1Grouped, here, means that in Python code, we may use the .groups() method to obtain the parts of the string matched by these parts of the regular expressions. Non-grouped (?:...) expressions do not affect the way .groups() gathers the parts of the strings. As in the paragraph to which this is a footnote, this is more a concern when writing Python (or Perl, or whatever) code, not when using these patterns in .hgignore or other parts of Mercurial.
Try to give the filename using glob syntax:
syntax: glob
core
It gives:
~/hg-test$ hg st -A
M .hgignore
? multicore
I core
I dir1/core

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 ignore all files except specific file names

I have a large file system in which almost every folder has a file called content.txt
I want to track every file named content.txt and automatically ignore everything else. I want the repo to automatically track new files named content.txt so I don't want to ignore everything in the .hgignore and then manually add.
Anyone know how to do this?
It has to be regexp mode, not glob
You must debug path-part of regexp, but "all except content.txt" draft is re:.*\.(?!content.txt) as hope
Alternative solution can be
* ignore all
* add content.txt files pattern to commit command (-I option), see hg help commit and hg help patterns
hg commit -I '**content.txt'
Edit
re:.*/(?!content.txt)
Try this:
syntax: regexp
\.(?!txt$)[^.]+$ # "*." is followed by "txt" and we're at the end
(?<!\.)txt$ # "txt" follows a "."
(?<!/)content\. # "content." follows path separator
(?<!content)\. # "." follows "content"
I left in the comments I made while experimenting, to make sense of it all. (That's glob syntax in the first one.)