git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: gitignore + commit with excludes = bug

From
Matheus Tavares <matheus.bernardino@usp.br>
Date
May 4, 2021, 14:06 UTC
Message-ID
<789bcc9cd074cb94f706b9d432e29b52bf6a33c3.1620134775.git.matheus.bernardino@usp.br>
In-Reply-To
<e4c3d482-03b4-141c-e47b-cb3e175da3ab@iee.email>
Hi, Paul and Philip
On Tue, May 4, 2021 at 9:55 AM Philip Oakley <philipoakley@iee.email> wrote:
Show 42 quoted lines
>
> Hi Paul,
>
> On 01/05/2021 19:37, Paul Jackson wrote:
> > Hello,
> >
> > I stumbled upon what I believe is a bug in git.
> > See the following reproduction steps:
> >
> > mkdir test
> > cd test
> > git init
> > echo 1 > ignored
> > echo 1 > not-ignored
> > echo ignored > .gitignore
> > git add -A -- ':!ignored' || echo 'ERROR!!!'
> >
> > In these steps, I ignore the "ignored" file twice - first time in
> > .gitignore, and second time in the "git add" command. I didn't expect
> > this to be a problem, but I'm getting the following error message:
> >
> > The following paths are ignored by one of your .gitignore files:
> > ignored
> >
> > It looks as if git thinks I wanted to include, not exclude "ignored"
> > in "git add".
> I was intrigued by this. The man pages can be hard to decipher, and it
> may be an 'as designed' feature, but still not intuitive
>
> It took a while to find the relevant parts of the man pages.
>
> The `-A` option of `add` is under
> https://git-scm.com/docs/git-add#Documentation/git-add.txt---no-ignore-removal
> which has caveats for whether a pathspec is given.
>
> The `exclude` magic pathspec is under
> https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-exclude
> and again has caveats and a double negative regarding whether the
> `exclude` pathspec counts as a path spec.
>
> I _think_ that it is saying that the `exclude` pathspec is ignored for
> the purpose of the `-A` (all) condition for git add.

Hmm, I think the issue is not really related to `-A`. In fact, if we reproduce Paul's original example without `-A`, we still get the warning.

The problem seems to be at `dir.c:exclude_matches_pathspec()`, which creates the list of ignored files that is later used by `git add` to presented the "The following paths are ignored..." warning.

This function ignores the `exclude` magic, so a path 'x' incorrectly matches both ':x' and ':!x'. And thus, we end up warning the user about 'x' being ignored even when the user had ran `git add ':!x'`.

I think something like this, might solve the problem:
diff --git a/dir.c b/dir.c
index 3474e67e8f..165ca6a543 100644
--- a/dir.c
+++ b/dir.c
@@ -2042,6 +2042,25 @@ static int exclude_matches_pathspec(const char *path, int pathlen,
 		const struct pathspec_item *item = &pathspec->items[i];
 		int len = item->nowildcard_len;
 
+		if (!(item->magic & PATHSPEC_EXCLUDE))
+			continue;
+
+		if (len == pathlen &&
+		    !ps_strncmp(item, item->match, path, pathlen))
+			return 0;
+		if (len > pathlen &&
+		    item->match[pathlen] == '/' &&
+		    !ps_strncmp(item, item->match, path, pathlen))
+			return 0;
+	}
+
+	for (i = 0; i < pathspec->nr; i++) {
+		const struct pathspec_item *item = &pathspec->items[i];
+		int len = item->nowildcard_len;
+
+		if (item->magic & PATHSPEC_EXCLUDE)
+			continue;
+
 		if (len == pathlen &&
 		    !ps_strncmp(item, item->match, path, pathlen))
 			return 1;
---

I had to split the original loop into two and handle the `exclude`
pathspecs first because we cannot let the original loop return early
when one of the `non-exclude` pathspecs matches the path. Otherwise, we
would still incorrectly warn the user on executions like
`git add ignored ':!ignored'`.

(We might also want to extract the matching part to its own function to
avoid repeating this code on the two loops.)
Previous: Philip OakleyNext: Paul Jackson
Message 3 of 9 in “gitignore + commit with excludes = bug”
  1. Paul JacksonMay 1, 2021
  2. Philip OakleyMay 4, 2021
  3. Matheus TavaresMay 4, 2021
  4. Paul JacksonMay 9, 2021
  5. Philip OakleyMay 10, 2021
  6. Elijah NewrenMay 10, 2021
  7. Matheus Tavares BernardinoMay 10, 2021
  8. Elijah NewrenMay 10, 2021
  9. Matheus Tavares BernardinoMay 11, 2021

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.