Hgignore everything except, regardless of directory - mercurial

I've seen the posts for hgignore everything except, but I can't seem to transform the regex to work with subdirectories as well.
syntax: regexp
(?<!\.cfm)$
Works for the root directory but not subdirs/subsubdirs. I don't want to manually specify those.
This is what I see without the ignore:
>hg stat
? document.cfm
? document.txt
? subdir1\document.cfm
? subdir1\document.txt
? subdir1\subsubdir1\document.cfm
? subdir1\subsubdir1\document.txt
? subdir2\document.cfm
? subdir2\document.txt
This is what I see with the ignore:
>hg stat
M .hgignore
? document.cfm
This is what I want to see:
>hg stat
? document.cfm
? subdir1\document.cfm
? subdir1\subsubdir1\document.cfm
? subdir2\document.cfm

Adding a file overrides ignores, so for the .hgignore if you've already added it you don't need to worry about including it. That makes this much easier.
syntax: regexp
(?<!\.cfm)$
assuming that what you were trying to do is ignore everything except .cfm files. Example:
% hg stat --all
A .hgignore
I adir/file.cfm
In general, don't overcomplicate the regex lines -- if you're (un)ignoring two different categories of things use two lines.

The desired action is impossible. Python only does fixed-width negative lookups. Closing this question. Thanks to anyone who looked into it.

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

.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 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.)

Add all not tracked files at once

$ hg status
M ...
M ...
M ...
? ...
? ...
? ...
I need to add all not tracked (? marked) files. Is it possible?
I can do "hg add *" but I will get many messages unwanted 'file already tracked'.
Just use hg add with no *.
When no files are given, it adds all untracked files, that is, all files with ? in front of the output of hg status.
Files that are ignored because of .hgignore will not be added by hg add without filenames.

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?