diff --git a/go.mod b/go.mod index 691c9b1a3..c7c2e09fb 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/cli/safeexec v1.0.0 github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 github.com/creack/pty v1.1.11 - github.com/fsmiamoto/git-todo-parser v0.0.4 + github.com/fsmiamoto/git-todo-parser v0.0.5 github.com/fsnotify/fsnotify v1.4.7 github.com/gdamore/tcell/v2 v2.6.0 github.com/go-errors/errors v1.4.2 diff --git a/go.sum b/go.sum index 4f961d11c..cf3f7aab8 100644 --- a/go.sum +++ b/go.sum @@ -28,8 +28,8 @@ github.com/fatih/color v1.7.1-0.20180516100307-2d684516a886/go.mod h1:Zm6kSWBoL9 github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/fsmiamoto/git-todo-parser v0.0.4 h1:fzcGaoAFDHWzJRKw//CSZFrXucsLKplIvOSab3FtWWM= -github.com/fsmiamoto/git-todo-parser v0.0.4/go.mod h1:B+AgTbNE2BARvJqzXygThzqxLIaEWvwr2sxKYYb0Fas= +github.com/fsmiamoto/git-todo-parser v0.0.5 h1:Bhzd/vz/6Qm3udfkd6NO9fWfD3TpwR9ucp3N75/J5I8= +github.com/fsmiamoto/git-todo-parser v0.0.5/go.mod h1:B+AgTbNE2BARvJqzXygThzqxLIaEWvwr2sxKYYb0Fas= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= diff --git a/pkg/app/daemon/daemon.go b/pkg/app/daemon/daemon.go index adc287309..cf10e9a18 100644 --- a/pkg/app/daemon/daemon.go +++ b/pkg/app/daemon/daemon.go @@ -10,6 +10,7 @@ import ( "github.com/fsmiamoto/git-todo-parser/todo" "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/common" + "github.com/jesseduffield/lazygit/pkg/secureexec" "github.com/jesseduffield/lazygit/pkg/utils" "github.com/samber/lo" ) @@ -90,6 +91,15 @@ func getDaemonKind() DaemonKind { return DaemonKind(intValue) } +func getCommentChar() byte { + cmd := secureexec.Command("git", "config", "--get", "--null", "core.commentChar") + if output, err := cmd.Output(); err == nil && len(output) == 2 { + return output[0] + } + + return '#' +} + // An Instruction is a command to be run by lazygit in daemon mode. // It is serialized to json and passed to lazygit via environment variables type Instruction interface { @@ -199,7 +209,7 @@ func (self *ChangeTodoActionsInstruction) SerializedInstructions() string { func (self *ChangeTodoActionsInstruction) run(common *common.Common) error { return handleInteractiveRebase(common, func(path string) error { for _, c := range self.Changes { - if err := utils.EditRebaseTodo(path, c.Sha, todo.Pick, c.NewAction); err != nil { + if err := utils.EditRebaseTodo(path, c.Sha, todo.Pick, c.NewAction, getCommentChar()); err != nil { return err } } @@ -233,7 +243,7 @@ func (self *MoveFixupCommitDownInstruction) SerializedInstructions() string { func (self *MoveFixupCommitDownInstruction) run(common *common.Common) error { return handleInteractiveRebase(common, func(path string) error { - return utils.MoveFixupCommitDown(path, self.OriginalSha, self.FixupSha) + return utils.MoveFixupCommitDown(path, self.OriginalSha, self.FixupSha, getCommentChar()) }) } @@ -257,7 +267,7 @@ func (self *MoveTodoUpInstruction) SerializedInstructions() string { func (self *MoveTodoUpInstruction) run(common *common.Common) error { return handleInteractiveRebase(common, func(path string) error { - return utils.MoveTodoUp(path, self.Sha, todo.Pick) + return utils.MoveTodoUp(path, self.Sha, todo.Pick, getCommentChar()) }) } @@ -281,7 +291,7 @@ func (self *MoveTodoDownInstruction) SerializedInstructions() string { func (self *MoveTodoDownInstruction) run(common *common.Common) error { return handleInteractiveRebase(common, func(path string) error { - return utils.MoveTodoDown(path, self.Sha, todo.Pick) + return utils.MoveTodoDown(path, self.Sha, todo.Pick, getCommentChar()) }) } diff --git a/pkg/commands/git.go b/pkg/commands/git.go index d09ff88b0..3a2349fd5 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -130,7 +130,7 @@ func NewGitCommandAux( branchLoader := git_commands.NewBranchLoader(cmn, cmd, branchCommands.CurrentBranchInfo, configCommands) commitFileLoader := git_commands.NewCommitFileLoader(cmn, cmd) - commitLoader := git_commands.NewCommitLoader(cmn, cmd, dotGitDir, statusCommands.RebaseMode) + commitLoader := git_commands.NewCommitLoader(cmn, cmd, dotGitDir, statusCommands.RebaseMode, gitCommon) reflogCommitLoader := git_commands.NewReflogCommitLoader(cmn, cmd) remoteLoader := git_commands.NewRemoteLoader(cmn, cmd, repo.Remotes) stashLoader := git_commands.NewStashLoader(cmn, cmd) diff --git a/pkg/commands/git_commands/commit_loader.go b/pkg/commands/git_commands/commit_loader.go index 99119f6aa..148c96776 100644 --- a/pkg/commands/git_commands/commit_loader.go +++ b/pkg/commands/git_commands/commit_loader.go @@ -38,6 +38,7 @@ type CommitLoader struct { // When nil, we're yet to obtain the list of existing main branches. // When an empty slice, we've obtained the list and it's empty. mainBranches []string + *GitCommon } // making our dependencies explicit for the sake of easier testing @@ -46,6 +47,7 @@ func NewCommitLoader( cmd oscommands.ICmdObjBuilder, dotGitDir string, getRebaseMode func() (enums.RebaseMode, error), + gitCommon *GitCommon, ) *CommitLoader { return &CommitLoader{ Common: cmn, @@ -55,6 +57,7 @@ func NewCommitLoader( walkFiles: filepath.Walk, dotGitDir: dotGitDir, mainBranches: nil, + GitCommon: gitCommon, } } @@ -304,7 +307,7 @@ func (self *CommitLoader) getInteractiveRebasingCommits() ([]*models.Commit, err commits := []*models.Commit{} - todos, err := todo.Parse(bytes.NewBuffer(bytesContent)) + todos, err := todo.Parse(bytes.NewBuffer(bytesContent), self.config.GetCoreCommentChar()) if err != nil { self.Log.Error(fmt.Sprintf("error occurred while parsing git-rebase-todo file: %s", err.Error())) return nil, nil @@ -346,7 +349,7 @@ func (self *CommitLoader) getConflictedCommit(todos []todo.Todo) string { return "" } - doneTodos, err := todo.Parse(bytes.NewBuffer(bytesContent)) + doneTodos, err := todo.Parse(bytes.NewBuffer(bytesContent), self.config.GetCoreCommentChar()) if err != nil { self.Log.Error(fmt.Sprintf("error occurred while parsing rebase-merge/done file: %s", err.Error())) return "" diff --git a/pkg/commands/git_commands/config.go b/pkg/commands/git_commands/config.go index e22333541..46a00f9db 100644 --- a/pkg/commands/git_commands/config.go +++ b/pkg/commands/git_commands/config.go @@ -99,3 +99,11 @@ func (self *ConfigCommands) Branches() (map[string]*config.Branch, error) { func (self *ConfigCommands) GetGitFlowPrefixes() string { return self.gitConfig.GetGeneral("--local --get-regexp gitflow.prefix") } + +func (self *ConfigCommands) GetCoreCommentChar() byte { + if commentCharStr := self.gitConfig.Get("core.commentChar"); len(commentCharStr) == 1 { + return commentCharStr[0] + } + + return '#' +} diff --git a/pkg/commands/git_commands/rebase.go b/pkg/commands/git_commands/rebase.go index 48bf706e6..5067c167c 100644 --- a/pkg/commands/git_commands/rebase.go +++ b/pkg/commands/git_commands/rebase.go @@ -243,19 +243,19 @@ func (self *RebaseCommands) AmendTo(commits []*models.Commit, commitIndex int) e // EditRebaseTodo sets the action for a given rebase commit in the git-rebase-todo file func (self *RebaseCommands) EditRebaseTodo(commit *models.Commit, action todo.TodoCommand) error { return utils.EditRebaseTodo( - filepath.Join(self.dotGitDir, "rebase-merge/git-rebase-todo"), commit.Sha, commit.Action, action) + filepath.Join(self.dotGitDir, "rebase-merge/git-rebase-todo"), commit.Sha, commit.Action, action, self.config.GetCoreCommentChar()) } // MoveTodoDown moves a rebase todo item down by one position func (self *RebaseCommands) MoveTodoDown(commit *models.Commit) error { fileName := filepath.Join(self.dotGitDir, "rebase-merge/git-rebase-todo") - return utils.MoveTodoDown(fileName, commit.Sha, commit.Action) + return utils.MoveTodoDown(fileName, commit.Sha, commit.Action, self.config.GetCoreCommentChar()) } // MoveTodoDown moves a rebase todo item down by one position func (self *RebaseCommands) MoveTodoUp(commit *models.Commit) error { fileName := filepath.Join(self.dotGitDir, "rebase-merge/git-rebase-todo") - return utils.MoveTodoUp(fileName, commit.Sha, commit.Action) + return utils.MoveTodoUp(fileName, commit.Sha, commit.Action, self.config.GetCoreCommentChar()) } // SquashAllAboveFixupCommits squashes all fixup! commits above the given one diff --git a/pkg/integration/tests/interactive_rebase/drop_with_custom_comment_char.go b/pkg/integration/tests/interactive_rebase/drop_with_custom_comment_char.go new file mode 100644 index 000000000..dbaa77b90 --- /dev/null +++ b/pkg/integration/tests/interactive_rebase/drop_with_custom_comment_char.go @@ -0,0 +1,34 @@ +package interactive_rebase + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var DropWithCustomCommentChar = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Drops a commit with the 'core.commentChar' option set to a custom character", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.SetConfig("core.commentChar", ";") + shell.CreateNCommits(2) + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits().Focus(). + Lines( + Contains("commit 02").IsSelected(), + Contains("commit 01"), + ). + Press(keys.Universal.Remove). + Tap(func() { + t.ExpectPopup().Confirmation(). + Title(Equals("Delete commit")). + Content(Equals("Are you sure you want to delete this commit?")). + Confirm() + }). + Lines( + Contains("commit 01").IsSelected(), + ) + }, +}) diff --git a/pkg/integration/tests/interactive_rebase/move_with_custom_comment_char.go b/pkg/integration/tests/interactive_rebase/move_with_custom_comment_char.go new file mode 100644 index 000000000..eefbcea33 --- /dev/null +++ b/pkg/integration/tests/interactive_rebase/move_with_custom_comment_char.go @@ -0,0 +1,34 @@ +package interactive_rebase + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var MoveWithCustomCommentChar = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Directly moves a commit down and back up with the 'core.commentChar' option set to a custom character", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.SetConfig("core.commentChar", ";") + shell.CreateNCommits(2) + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits().Focus(). + Lines( + Contains("commit 02").IsSelected(), + Contains("commit 01"), + ). + Press(keys.Commits.MoveDownCommit). + Lines( + Contains("commit 01"), + Contains("commit 02").IsSelected(), + ). + Press(keys.Commits.MoveUpCommit). + Lines( + Contains("commit 02").IsSelected(), + Contains("commit 01"), + ) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index c1549df83..99b7f2bdc 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -103,6 +103,7 @@ var tests = []*components.IntegrationTest{ interactive_rebase.AmendNonHeadCommitDuringRebase, interactive_rebase.DropTodoCommitWithUpdateRef, interactive_rebase.DropTodoCommitWithUpdateRefShowBranchHeads, + interactive_rebase.DropWithCustomCommentChar, interactive_rebase.EditFirstCommit, interactive_rebase.EditNonTodoCommitDuringRebase, interactive_rebase.EditTheConflCommit, @@ -110,6 +111,7 @@ var tests = []*components.IntegrationTest{ interactive_rebase.FixupSecondCommit, interactive_rebase.Move, interactive_rebase.MoveInRebase, + interactive_rebase.MoveWithCustomCommentChar, interactive_rebase.PickRescheduled, interactive_rebase.Rebase, interactive_rebase.RewordCommitWithEditorAndFail, diff --git a/pkg/utils/rebase_todo.go b/pkg/utils/rebase_todo.go index 15c06b1c4..08a9ca872 100644 --- a/pkg/utils/rebase_todo.go +++ b/pkg/utils/rebase_todo.go @@ -11,8 +11,8 @@ import ( // Read a git-rebase-todo file, change the action for the given sha to // newAction, and write it back -func EditRebaseTodo(filePath string, sha string, oldAction todo.TodoCommand, newAction todo.TodoCommand) error { - todos, err := ReadRebaseTodoFile(filePath) +func EditRebaseTodo(filePath string, sha string, oldAction todo.TodoCommand, newAction todo.TodoCommand, commentChar byte) error { + todos, err := ReadRebaseTodoFile(filePath, commentChar) if err != nil { return err } @@ -24,7 +24,7 @@ func EditRebaseTodo(filePath string, sha string, oldAction todo.TodoCommand, new // pick and later in a merge) if t.Command == oldAction && equalShas(t.Commit, sha) { t.Command = newAction - return WriteRebaseTodoFile(filePath, todos) + return WriteRebaseTodoFile(filePath, todos, commentChar) } } @@ -36,13 +36,13 @@ func equalShas(a, b string) bool { return strings.HasPrefix(a, b) || strings.HasPrefix(b, a) } -func ReadRebaseTodoFile(fileName string) ([]todo.Todo, error) { +func ReadRebaseTodoFile(fileName string, commentChar byte) ([]todo.Todo, error) { f, err := os.Open(fileName) if err != nil { return nil, err } - todos, err := todo.Parse(f) + todos, err := todo.Parse(f, commentChar) err2 := f.Close() if err == nil { err = err2 @@ -50,12 +50,12 @@ func ReadRebaseTodoFile(fileName string) ([]todo.Todo, error) { return todos, err } -func WriteRebaseTodoFile(fileName string, todos []todo.Todo) error { +func WriteRebaseTodoFile(fileName string, todos []todo.Todo, commentChar byte) error { f, err := os.Create(fileName) if err != nil { return err } - err = todo.Write(f, todos) + err = todo.Write(f, todos, commentChar) err2 := f.Close() if err == nil { err = err2 @@ -73,8 +73,8 @@ func PrependStrToTodoFile(filePath string, linesToPrepend []byte) error { return os.WriteFile(filePath, linesToPrepend, 0o644) } -func MoveTodoDown(fileName string, sha string, action todo.TodoCommand) error { - todos, err := ReadRebaseTodoFile(fileName) +func MoveTodoDown(fileName string, sha string, action todo.TodoCommand, commentChar byte) error { + todos, err := ReadRebaseTodoFile(fileName, commentChar) if err != nil { return err } @@ -82,11 +82,11 @@ func MoveTodoDown(fileName string, sha string, action todo.TodoCommand) error { if err != nil { return err } - return WriteRebaseTodoFile(fileName, rearrangedTodos) + return WriteRebaseTodoFile(fileName, rearrangedTodos, commentChar) } -func MoveTodoUp(fileName string, sha string, action todo.TodoCommand) error { - todos, err := ReadRebaseTodoFile(fileName) +func MoveTodoUp(fileName string, sha string, action todo.TodoCommand, commentChar byte) error { + todos, err := ReadRebaseTodoFile(fileName, commentChar) if err != nil { return err } @@ -94,7 +94,7 @@ func MoveTodoUp(fileName string, sha string, action todo.TodoCommand) error { if err != nil { return err } - return WriteRebaseTodoFile(fileName, rearrangedTodos) + return WriteRebaseTodoFile(fileName, rearrangedTodos, commentChar) } func moveTodoDown(todos []todo.Todo, sha string, action todo.TodoCommand) ([]todo.Todo, error) { @@ -134,8 +134,8 @@ func moveTodoUp(todos []todo.Todo, sha string, action todo.TodoCommand) ([]todo. return rearrangedTodos, nil } -func MoveFixupCommitDown(fileName string, originalSha string, fixupSha string) error { - todos, err := ReadRebaseTodoFile(fileName) +func MoveFixupCommitDown(fileName string, originalSha string, fixupSha string, commentChar byte) error { + todos, err := ReadRebaseTodoFile(fileName, commentChar) if err != nil { return err } @@ -145,7 +145,7 @@ func MoveFixupCommitDown(fileName string, originalSha string, fixupSha string) e return err } - return WriteRebaseTodoFile(fileName, newTodos) + return WriteRebaseTodoFile(fileName, newTodos, commentChar) } func moveFixupCommitDown(todos []todo.Todo, originalSha string, fixupSha string) ([]todo.Todo, error) { diff --git a/vendor/github.com/fsmiamoto/git-todo-parser/todo/parse.go b/vendor/github.com/fsmiamoto/git-todo-parser/todo/parse.go index ab3dd9ea9..51efd305d 100644 --- a/vendor/github.com/fsmiamoto/git-todo-parser/todo/parse.go +++ b/vendor/github.com/fsmiamoto/git-todo-parser/todo/parse.go @@ -16,7 +16,7 @@ var ( ErrMissingRef = errors.New("missing ref") ) -func Parse(f io.Reader) ([]Todo, error) { +func Parse(f io.Reader, commentChar byte) ([]Todo, error) { var result []Todo scanner := bufio.NewScanner(f) @@ -30,7 +30,7 @@ func Parse(f io.Reader) ([]Todo, error) { continue } - cmd, err := parseLine(line) + cmd, err := parseLine(line, commentChar) if err != nil { return nil, fmt.Errorf("failed to parse line %q: %w", line, err) } @@ -45,12 +45,12 @@ func Parse(f io.Reader) ([]Todo, error) { return result, nil } -func parseLine(line string) (Todo, error) { +func parseLine(line string, commentChar byte) (Todo, error) { var todo Todo - if strings.HasPrefix(line, CommentChar) { + if line[0] == commentChar { todo.Command = Comment - todo.Comment = strings.TrimLeft(line, CommentChar) + todo.Comment = line[1:] return todo, nil } @@ -143,8 +143,8 @@ func parseLine(line string) (Todo, error) { todo.Commit = fields[0] fields = fields[1:] - // Trim # and whitespace - todo.Msg = strings.TrimPrefix(strings.Join(fields, " "), CommentChar+" ") + // Trim comment char and whitespace + todo.Msg = strings.TrimPrefix(strings.Join(fields, " "), fmt.Sprintf("%c ", commentChar)) return todo, nil } diff --git a/vendor/github.com/fsmiamoto/git-todo-parser/todo/todo.go b/vendor/github.com/fsmiamoto/git-todo-parser/todo/todo.go index 77bb5dc71..3b2c45290 100644 --- a/vendor/github.com/fsmiamoto/git-todo-parser/todo/todo.go +++ b/vendor/github.com/fsmiamoto/git-todo-parser/todo/todo.go @@ -23,8 +23,6 @@ const ( Comment ) -const CommentChar = "#" - type Todo struct { Command TodoCommand Commit string diff --git a/vendor/github.com/fsmiamoto/git-todo-parser/todo/write.go b/vendor/github.com/fsmiamoto/git-todo-parser/todo/write.go index c96b06cc0..949db420a 100644 --- a/vendor/github.com/fsmiamoto/git-todo-parser/todo/write.go +++ b/vendor/github.com/fsmiamoto/git-todo-parser/todo/write.go @@ -5,9 +5,9 @@ import ( "strings" ) -func Write(f io.Writer, todos []Todo) error { +func Write(f io.Writer, todos []Todo, commentChar byte) error { for _, todo := range todos { - if err := writeTodo(f, todo); err != nil { + if err := writeTodo(f, todo, commentChar); err != nil { return err } } @@ -15,7 +15,7 @@ func Write(f io.Writer, todos []Todo) error { return nil } -func writeTodo(f io.Writer, todo Todo) error { +func writeTodo(f io.Writer, todo Todo, commentChar byte) error { var sb strings.Builder if todo.Command != Comment { sb.WriteString(todo.Command.String()) @@ -26,7 +26,7 @@ func writeTodo(f io.Writer, todo Todo) error { return nil case Comment: - sb.WriteString(CommentChar) + sb.WriteByte(commentChar) sb.WriteString(todo.Comment) case Break: diff --git a/vendor/modules.txt b/vendor/modules.txt index 3012b04de..04937f5b2 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -30,7 +30,7 @@ github.com/emirpasic/gods/utils # github.com/fatih/color v1.9.0 ## explicit; go 1.13 github.com/fatih/color -# github.com/fsmiamoto/git-todo-parser v0.0.4 +# github.com/fsmiamoto/git-todo-parser v0.0.5 ## explicit; go 1.13 github.com/fsmiamoto/git-todo-parser/todo # github.com/fsnotify/fsnotify v1.4.7