Is it possible to use rooted glob patterns in .hgignore? - mercurial

I prefer to use glob syntax in my .hgignore files, but now I need to create a rooted pattern. I want to ignore my /static directory, but not /assets/static. The line
static/
ignores both. Is there a way to do rooted glob patterns? If not, I can switch to regex, but glob just seems so much more natural for matching pathnames.

You cannot root your glob patterns — this is documented in the manpage. You can, however, switch back and forth between the glob and regexp syntax:
syntax: glob
*.pyc
syntax: regexp
^static/
syntax: glob
*~

According to hgignore (5), it's not possible with globs:
Neither glob nor regexp patterns are rooted. A glob-syntax pattern of the form *.c will match a file ending in .c in any directory, and a regexp pattern of the form .c$ will do the same. To root a regexp pattern, start it with ^.

Related

.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

Ignoring the same filename in root and subdirectories

In a Mac project, there are .DS_Store files at multiple levels, like so:
project/.DS_Store
project/subdir/.DS_Store
project/subdir/other_file.txt
project/.hgignore
When I use the following for a .hgignore file, the top-level .DS_Store doesn't get ignored. Is there any way to ignore both .DS_Store files with a single glob line in the .hgignore file? It seems like this should be easy, and adding another .DS_Store line feels clumsy.
syntax: glob
**/.DS_Store
The following works, but I prefer the readability of the glob syntax for ignore files:
syntax: regexp
.*\.DS_Store
Use the glob expression, but don't limit it to being inside a folder, like this:
syntax:glob
.DS_Store
That will match any file with the exact name of .DS_Store anywhere in the repo.

What's the difference between glob's directory/ and directory/* and directory/** and directory/**/*?

I'm using gulp building my development workflow. Gulp and its plugins use glob heavily.
I'm confused about the differences between the followings:
directory/
directory/*
directory/**
directory/**/*
I'm not able to make gulp do the things I expect.
Grunt has a pretty good explanation of how globs work http://gruntjs.com/configuring-tasks#globbing-patterns
* matches any number of characters, but not /
** matches any number of characters, including /, as long as it's the only thing
in a path part. So a/**/b will match a/x/y/b, but a/**b will not.
directory/ = this isn't a glob and would evaluate as expected.
directory/* = will match anything in the directory, but not sub-directories.
directory/** = will match anything in the directory and all subdirectories.
directory/**/* = will match anything in the directory and all subdirectories.
(good for adding a prefix like an extension to the end)

.gitignore un-ignore equivalent in .hgignore

In the .gitignore syntax there is an option where you can have a ! at the beginning of the file and it will un-ignore any previously ignored files.
So I can have the following:
logs/*
!logs/stuff.txt
And logs/stuff.txt will still be tracked.
What is the Mercurial .hgignore equivalent for this?
The equivalent is just adding the file. Once a file has been hg added the .hgignore file has absolutely no effect on how it's treated, so you ignore logs/* (be sure you're in glob mode, not regex mode) and then hg add logs/stuff.txt.
Using regexp syntax you can write both rules in one:
syntax: regexp
logs\/(?!stuff.txt$)

mercurial and ignore pattern

I'm trying to get mercurial to ignore diffs. I performed the following according to Mercurial ignore file:
$ echo "*.diff" >> .hgignore
Status now give me an error (I added the '...' for readability):
$ hg status
abort: .../.hgignore: invalid pattern (relre): *.diff
Searching the web for the error message returned a few non-relevant results. Any ideas on how to ignore diff files?
Write first:
syntax: glob
Fully .hgignore
syntax: glob
*.diff
The default regex. You can write all the regular expression, then switch to syntax glob. Example:
\.pyc$
test\.py$
syntax: glob
*.diff
If you want to exclude all but a few, it is better to use a regular expression.