[PATCH] Allow multiple .hgignore files
Alexis S. L. Carvalho
alexis at cecm.usp.br
Thu Feb 15 11:32:23 UTC 2007
Thus spake tailgunner at smtp.ru:
> Allow multiple .hgignore files in a single repository
I don't really have a strong opinion here, so can you argue a bit why we
want this?
> Note that non-repository-root .hgignore files are treated differently
> from root one.
>
>
> diff -r 01855c47da37 -r 0f35560bc179 mercurial/dirstate.py
> --- a/mercurial/dirstate.py Wed Feb 14 01:05:09 2007 +0300
> +++ b/mercurial/dirstate.py Wed Feb 14 01:05:09 2007 +0300
> @@ -41,7 +41,7 @@ class dirstate(object):
> '''return the contents of .hgignore files as a list of patterns.
>
> the files parsed for patterns include:
> - .hgignore in the repository root
> + all .hgignore files in the repository
> any additional files specified in the [ui] section of ~/.hgrc
>
> trailing white space is dropped.
> @@ -55,7 +55,11 @@ class dirstate(object):
> syntax: glob # defaults following lines to non-rooted globs
> re:pattern # non-rooted regular expression
> glob:pattern # non-rooted glob
> - pattern # pattern of the current default type'''
> + pattern # pattern of the current default type
> +
> + Every pattern has the name of its containing directory prepended to
> + it (this name is empty string for .hgignore in the repository root
> + and ~/.hgrc ignore files). '''
IIUC the desired behaviour is that a
syntax: glob
*.c
in foo/.hgignore will match foo/file.c but not foo/bar/file.c and not
dir/foo/file.c , right? And I guess you're still able to match files in
subdirs using either "**.c" globs or some regexps.
> syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:'}
> def parselines(fp):
> for line in fp:
> @@ -67,10 +71,21 @@ class dirstate(object):
> line = line[:i].rstrip()
> if line: yield line
> repoignore = self.wjoin('.hgignore')
> - files = [repoignore]
> - files.extend(self.ui.hgignorefiles())
> + # .hgignore in repository root is considered always (we don't even
> + # check if it exists prior to opening it)
> + files = [(repoignore, "")]
> + # ...but the rest of .hgignore files are considered only if they are
> + # managed by Mercurial
> + for f in self.map.keys():
> + if f == ".hgignore":
> + pass # skip root .hgignore
> + elif f.endswith(".hgignore"):
You probably want '/.hgignore' here.
> + cd = f[:len(f) - len(".hgignore")]
> + files.append((self.wjoin(f), cd))
> + for f in self.ui.hgignorefiles():
> + files.append((self.wjoin(f), ""))
> pats = {}
> - for f in files:
> + for f, cd in files:
> try:
> pats[f] = []
> fp = open(f)
> @@ -84,7 +99,7 @@ class dirstate(object):
> self.ui.warn(_("%s: ignoring invalid "
> "syntax '%s'\n") % (f, s))
> continue
> - pat = syntax + line
> + pat = syntax + cd + line
> for s in syntaxes.values():
> if line.startswith(s):
> pat = line
> break
I don't think this will work. AFAICS in a foo/.hgignore:
- if syntax is "relglob:" (i.e. we had a "syntax: glob" before), a "*.c"
will be turned into relglob:foo/*.c , which will match not only
foo/file.c , but also dir/foo/file.c
- if syntax is "relre:" (i.e. we had a "syntax: regexp" before), you
really want to use re.escape(cd). Even after that, this will still
match dir/foo/file.c
- if line is something like "relglob:*.c" or "relre:*.c" your changes
won't have any effect and we'll match '*.c' everywhere.
To handle the first two problems, you can try something like this
(completely untested):
if not cd:
pat = syntax + line
elif syntax == 'relglob:':
pat = 'glob:' + cd + line
elif syntax == 'relre:':
pat = 're:' + re.escape(cd) + line
To handle "relglob:*.c" lines, you can try extracting the syntax from
the line before these conditionals.
It would also be nice to have some tests - but as I said, I'm not sure
we want this...
Alexis
More information about the Mercurial-devel
mailing list