Fix gitignore path collisions (#5245)

### PR Description

Paths added to ignore/exclude files need to be prefixed with a forward
slash to point to a specific file in the directory tree.

Without that prefix a file at root called `file` (added to `.gitignore`
as `file`) would match with `./file` and `./src/file`.

Example flow:
1. User creates a directory called `tests` in root
2. User sees that it's tracked
3. User ignores the directory in Lazygit
4. Changes in `src/tests` are now missing - because `tests` without `/`
matches also directories lower in the tree
This commit is contained in:
Stefan Haller 2026-01-28 21:12:35 +01:00 committed by GitHub
commit 819212b758
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 6 additions and 6 deletions

View file

@ -233,10 +233,10 @@ func (self *WorkingTreeCommands) DiscardUnstagedFileChanges(file *models.File) e
return self.cmd.New(cmdArgs).Run()
}
// Escapes special characters in a filename for gitignore and exclude files
// Escapes special characters in a filename for gitignore and exclude files, and prepends `/`
func escapeFilename(filename string) string {
re := regexp.MustCompile(`^[!#]|[\[\]*]`)
return re.ReplaceAllString(filename, `\${0}`)
return "/" + re.ReplaceAllString(filename, `\${0}`)
}
// Ignore adds a file to the gitignore for the repo

View file

@ -50,7 +50,7 @@ var Gitignore = NewIntegrationTest(NewIntegrationTestArgs{
t.ExpectPopup().Menu().Title(Equals("Ignore or exclude file")).Select(Contains("Add to .git/info/exclude")).Confirm()
t.FileSystem().FileContent(".gitignore", Equals(""))
t.FileSystem().FileContent(".git/info/exclude", Contains("toExclude"))
t.FileSystem().FileContent(".git/info/exclude", Contains("/toExclude"))
}).
SelectNextItem().
Press(keys.Files.IgnoreFile).
@ -58,8 +58,8 @@ var Gitignore = NewIntegrationTest(NewIntegrationTestArgs{
Tap(func() {
t.ExpectPopup().Menu().Title(Equals("Ignore or exclude file")).Select(Contains("Add to .gitignore")).Confirm()
t.FileSystem().FileContent(".gitignore", Equals("toIgnore\n"))
t.FileSystem().FileContent(".git/info/exclude", Contains("toExclude"))
t.FileSystem().FileContent(".gitignore", Equals("/toIgnore\n"))
t.FileSystem().FileContent(".git/info/exclude", Contains("/toExclude"))
})
},
})

View file

@ -61,6 +61,6 @@ var GitignoreSpecialCharacters = NewIntegrationTest(NewIntegrationTestArgs{
Equals(" ?? abc_def"),
)
t.FileSystem().FileContent(".gitignore", Equals("\\#file\nfile#abc\n\\!file\nfile!abc\nabc\\*def\nfile\\[x\\]\n"))
t.FileSystem().FileContent(".gitignore", Equals("/\\#file\n/file#abc\n/\\!file\n/file!abc\n/abc\\*def\n/file\\[x\\]\n"))
},
})