Repository: jesseduffield/lazygit (language: go)

Issue:
ignoring files starting with `#` will just add a comment
if the file starts with # or !, they will just become a comment in the `.gitignore`file

Steps to reproduce the behavior:

1. touch '#abc'
2. select the file in working tree
3. ignore by pressing i and then i
4. the file is not being ignored

**Version info:**
commit=91cb1ff29a01fdf643ccc38b97fd816b2a097bec, build date=2025-01-11T04:06:07Z, build source=binaryRelease, version=0.45.0, os=windows, arch=amd64, git version=2.47.1.windows.2
git version 2.47.1.windows.2


Discussion / hints:

Fix patch:
diff --git a/pkg/commands/git_commands/working_tree.go b/pkg/commands/git_commands/working_tree.go
index c967ca576f6..6a4c2210da4 100644
--- a/pkg/commands/git_commands/working_tree.go
+++ b/pkg/commands/git_commands/working_tree.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"os"
 	"path/filepath"
+	"regexp"
 
 	"github.com/go-errors/errors"
 	"github.com/jesseduffield/lazygit/pkg/commands/models"
@@ -230,15 +231,21 @@ 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
+func escapeFilename(filename string) string {
+	re := regexp.MustCompile(`^[!#]|[\[\]*]`)
+	return re.ReplaceAllString(filename, `\${0}`)
+}
+
 // Ignore adds a file to the gitignore for the repo
 func (self *WorkingTreeCommands) Ignore(filename string) error {
-	return self.os.AppendLineToFile(".gitignore", filename)
+	return self.os.AppendLineToFile(".gitignore", escapeFilename(filename))
 }
 
 // Exclude adds a file to the .git/info/exclude for the repo
 func (self *WorkingTreeCommands) Exclude(filename string) error {
 	excludeFile := filepath.Join(self.repoPaths.repoGitDirPath, "info", "exclude")
-	return self.os.AppendLineToFile(excludeFile, filename)
+	return self.os.AppendLineToFile(excludeFile, escapeFilename(filename))
 }
 
 // WorktreeFileDiff returns the diff of a file


Test patch:
diff --git a/pkg/integration/tests/file/gitignore_special_characters.go b/pkg/integration/tests/file/gitignore_special_characters.go
new file mode 100644
index 00000000000..84aa57ec33b
--- /dev/null
+++ b/pkg/integration/tests/file/gitignore_special_characters.go
@@ -0,0 +1,66 @@
+package file
+
+import (
+	"github.com/jesseduffield/lazygit/pkg/config"
+	. "github.com/jesseduffield/lazygit/pkg/integration/components"
+)
+
+var GitignoreSpecialCharacters = NewIntegrationTest(NewIntegrationTestArgs{
+	Description:  "Ignore files with special characters in their names",
+	ExtraCmdArgs: []string{},
+	Skip:         false,
+	SetupConfig: func(config *config.AppConfig) {
+	},
+	SetupRepo: func(shell *Shell) {
+		shell.CreateFile(".gitignore", "")
+		shell.CreateFile("#file", "")
+		shell.CreateFile("file#abc", "")
+		shell.CreateFile("!file", "")
+		shell.CreateFile("file!abc", "")
+		shell.CreateFile("abc*def", "")
+		shell.CreateFile("abc_def", "")
+		shell.CreateFile("file[x]", "")
+	},
+	Run: func(t *TestDriver, keys config.KeybindingConfig) {
+		excludeFile := func(fileName string) {
+			t.Views().Files().
+				NavigateToLine(Contains(fileName)).
+				Press(keys.Files.IgnoreFile)
+
+			t.ExpectPopup().Menu().
+				Title(Equals("Ignore or exclude file")).
+				Select(Contains("Add to .gitignore")).
+				Confirm()
+		}
+
+		t.Views().Files().
+			Focus().
+			Lines(
+				Equals("▼ /"),
+				Equals("  ?? !file"),
+				Equals("  ?? #file"),
+				Equals("  ?? .gitignore"),
+				Equals("  ?? abc*def"),
+				Equals("  ?? abc_def"),
+				Equals("  ?? file!abc"),
+				Equals("  ?? file#abc"),
+				Equals("  ?? file[x]"),
+			)
+
+		excludeFile("#file")
+		excludeFile("file#abc")
+		excludeFile("!file")
+		excludeFile("file!abc")
+		excludeFile("abc*def")
+		excludeFile("file[x]")
+
+		t.Views().Files().
+			Lines(
+				Equals("▼ /"),
+				Equals("  ?? .gitignore"),
+				Equals("  ?? abc_def"),
+			)
+
+		t.FileSystem().FileContent(".gitignore", Equals("\\#file\nfile#abc\n\\!file\nfile!abc\nabc\\*def\nfile\\[x\\]\n"))
+	},
+})
diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go
index c79dcd2f8ee..cbcf4ab5a94 100644
--- a/pkg/integration/tests/test_list.go
+++ b/pkg/integration/tests/test_list.go
@@ -197,6 +197,7 @@ var tests = []*components.IntegrationTest{
 	file.DiscardVariousChanges,
 	file.DiscardVariousChangesRangeSelect,
 	file.Gitignore,
+	file.GitignoreSpecialCharacters,
 	file.RememberCommitMessageAfterFail,
 	file.RenameSimilarityThresholdChange,
 	file.RenamedFiles,