Skip to content

Commit 25895bf

Browse files
authored
Fix #5302: Create .git/info directory before writing exclude file (#5325)
Fixes #5302 ## Problem When `.git/info` directory does not exist (can happen with bare clones, manual deletion, or certain Git configurations), selecting "Add to .git/info/exclude" fails with: ``` open <repo-path>/.git/info/exclude: no such file or directory ``` The `Exclude` function used `os.OpenFile` with `os.O_CREATE` which creates the file but not its parent directory. ## Fix Added `os.MkdirAll` to create the `.git/info` directory (with 0755 permissions) before attempting to open the exclude file. `MkdirAll` is a no-op if the directory already exists, so existing behavior is unchanged. ## Test Added integration test `file/exclude_without_info_dir` that: 1. Removes `.git/info` directory during repo setup 2. Attempts to exclude a file via the menu 3. Verifies `.git/info/exclude` is created with the correct content Test fails without the fix, passes with it. Tested locally on macOS ARM (Apple Silicon). Unit tests and integration test pass.
2 parents 9b74805 + 30154aa commit 25895bf

3 files changed

Lines changed: 38 additions & 1 deletion

File tree

pkg/commands/git_commands/working_tree.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,11 @@ func (self *WorkingTreeCommands) Ignore(filename string) error {
246246

247247
// Exclude adds a file to the .git/info/exclude for the repo
248248
func (self *WorkingTreeCommands) Exclude(filename string) error {
249-
excludeFile := filepath.Join(self.repoPaths.repoGitDirPath, "info", "exclude")
249+
infoDir := filepath.Join(self.repoPaths.repoGitDirPath, "info")
250+
if err := os.MkdirAll(infoDir, 0o755); err != nil {
251+
return err
252+
}
253+
excludeFile := filepath.Join(infoDir, "exclude")
250254
return self.os.AppendLineToFile(excludeFile, escapeFilename(filename))
251255
}
252256

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package file
2+
3+
import (
4+
"github.com/jesseduffield/lazygit/pkg/config"
5+
. "github.com/jesseduffield/lazygit/pkg/integration/components"
6+
)
7+
8+
var ExcludeWithoutInfoDir = NewIntegrationTest(NewIntegrationTestArgs{
9+
Description: "Exclude a file when .git/info directory does not exist",
10+
ExtraCmdArgs: []string{},
11+
Skip: false,
12+
SetupConfig: func(config *config.AppConfig) {
13+
},
14+
SetupRepo: func(shell *Shell) {
15+
// Remove .git/info directory to reproduce #5302
16+
shell.RunCommand([]string{"rm", "-rf", ".git/info"})
17+
shell.CreateFile("toExclude", "")
18+
},
19+
Run: func(t *TestDriver, keys config.KeybindingConfig) {
20+
t.Views().Files().
21+
IsFocused().
22+
Focus().
23+
NavigateToLine(Contains("toExclude")).
24+
Press(keys.Files.IgnoreFile).
25+
Tap(func() {
26+
t.ExpectPopup().Menu().Title(Equals("Ignore or exclude file")).Select(Contains("Add to .git/info/exclude")).Confirm()
27+
28+
// Should succeed without error, creating .git/info/ directory automatically
29+
t.FileSystem().FileContent(".git/info/exclude", Contains("/toExclude"))
30+
})
31+
},
32+
})

pkg/integration/tests/test_list.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ var tests = []*components.IntegrationTest{
221221
file.DiscardUnstagedRangeSelect,
222222
file.DiscardVariousChanges,
223223
file.DiscardVariousChangesRangeSelect,
224+
file.ExcludeWithoutInfoDir,
224225
file.Gitignore,
225226
file.GitignoreSpecialCharacters,
226227
file.RememberCommitMessageAfterFail,

0 commit comments

Comments
 (0)