[PATCH] Allow multiple .hgignore files
tailgunner at smtp.ru
tailgunner at smtp.ru
Thu Feb 15 22:20:08 UTC 2007
On Thu, 15 Feb 2007 09:32:23 -0200
"Alexis S. L. Carvalho" <alexis at cecm.usp.br> wrote:
> 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?
>
I got used to this functionality in Subversion which allowed per-direcory ignore lists,
and I miss it. Sometimes it's handy to define ignore list locally, without polluting
global .hgignore file. I'd really like to be able to split my large .hgignore into 3-4 pieces.
>
> 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?
Not really. The goal is to match foo/bar/file.c, but not dir/foo/file.c, so that
foo/.hgignore adds its contents to the global .hgignore for the tree
rooted at foo.
>
>
> > 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.
>
Yep
> > + 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.
>
I guess you're right. I should rework the patch.
> It would also be nice to have some tests - but as I said, I'm not sure
> we want this...
>
If this feature is acceptable, tests are no problem. Thanks for the review.
More information about the Mercurial-devel
mailing list