From afb66f78a91743f9bb324042ea4a2550f6c03591 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sat, 19 Sep 2026 17:03:41 +0200 Subject: [PATCH] Sort remote branches with the same committer date in stack order Force-pushing a stack of branches puts the same commits on the remote, so the remote branches panel shows the same alphabetical order for a stack that the local branches panel did. Sort them by ancestry too, which needs the tip hash and committer date of each remote branch. Co-Authored-By: Claude Opus 5 (1M context) --- pkg/commands/git_commands/remote_loader.go | 36 +++++++++---- .../sort_remote_branches_in_stack_order.go | 52 +++++++++++++++++++ pkg/integration/tests/test_list.go | 1 + 3 files changed, 79 insertions(+), 10 deletions(-) create mode 100644 pkg/integration/tests/branch/sort_remote_branches_in_stack_order.go diff --git a/pkg/commands/git_commands/remote_loader.go b/pkg/commands/git_commands/remote_loader.go index 21d426201..2a0d9dd29 100644 --- a/pkg/commands/git_commands/remote_loader.go +++ b/pkg/commands/git_commands/remote_loader.go @@ -116,27 +116,32 @@ func (self *RemoteLoader) getRemoteBranchesByRemoteName() (map[string][]*models. // Returns all remote branches, sorted the way the config asks for func (self *RemoteLoader) getRemoteBranches() ([]*models.RemoteBranch, error) { - var sortOrder string - switch strings.ToLower(self.UserConfig().Git.RemoteBranchSortOrder) { - case "alphabetical": - sortOrder = "refname" - case "date": + sortByDate := strings.ToLower(self.UserConfig().Git.RemoteBranchSortOrder) == "date" + sortOrder := "refname" + if sortByDate { sortOrder = "-committerdate" - default: - sortOrder = "refname" + } + + // Asking for the tip of a branch makes git read its commit, so only do it + // when we are going to sort by ancestry below + format := "%(refname)" + if sortByDate { + format += "%00%(objectname)%00%(committerdate:unix)" } cmdArgs := NewGitCmd("for-each-ref"). Arg(fmt.Sprintf("--sort=%s", sortOrder)). - Arg("--format=%(refname)"). + Arg(fmt.Sprintf("--format=%s", format)). Arg("refs/remotes"). ToArgv() remoteBranches := []*models.RemoteBranch{} + tips := map[string]refTip{} err := self.cmd.New(cmdArgs).DontLog().RunAndProcessLines(func(line string) (bool, error) { - line = strings.TrimSpace(line) + fields := strings.Split(strings.TrimSpace(line), "\x00") + refName := fields[0] - split := strings.SplitN(line, "/", 4) + split := strings.SplitN(refName, "/", 4) if len(split) != 4 { return false, nil } @@ -152,11 +157,22 @@ func (self *RemoteLoader) getRemoteBranches() ([]*models.RemoteBranch, error) { Name: name, RemoteName: remoteName, }) + if len(fields) == 3 { + tips[refName] = refTip{hash: fields[1], committerDate: fields[2]} + } return false, nil }) if err != nil { return nil, err } + if sortByDate { + if err := sortRefsWithEqualDatesByAncestry( + self.cmd, self.version, remoteBranches, (*models.RemoteBranch).FullRefName, tips, + ); err != nil { + self.Log.Errorf("Failed to sort remote branches by ancestry: %v", err) + } + } + return remoteBranches, nil } diff --git a/pkg/integration/tests/branch/sort_remote_branches_in_stack_order.go b/pkg/integration/tests/branch/sort_remote_branches_in_stack_order.go new file mode 100644 index 000000000..2a652a309 --- /dev/null +++ b/pkg/integration/tests/branch/sort_remote_branches_in_stack_order.go @@ -0,0 +1,52 @@ +package branch + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var SortRemoteBranchesInStackOrder = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Sort remote branches that share a committer date so that a branch comes before the ones it is based on", + ExtraCmdArgs: []string{}, + Skip: false, + GitVersion: AtLeast("2.41.0"), + SetupConfig: func(config *config.AppConfig) { + config.GetUserConfig().Git.RemoteBranchSortOrder = "date" + }, + SetupRepo: func(shell *Shell) { + // Rebasing a stack of branches gives every commit it creates the same + // committer date; give these commits one date for the same effect. + date := "2024-01-01 10:00:00" + + shell. + EmptyCommitWithDate("base", date). + NewBranch("branch-c"). + EmptyCommitWithDate("c", date). + NewBranch("branch-a"). + EmptyCommitWithDate("a", date). + NewBranch("branch-b"). + EmptyCommitWithDate("b", date). + NewBranchFrom("unrelated", "master"). + EmptyCommitWithDate("unrelated", date). + Checkout("master"). + CloneIntoRemote("origin") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Remotes(). + Focus(). + Lines( + Contains("origin").IsSelected(), + ). + PressEnter() + + t.Views().RemoteBranches(). + IsFocused(). + Lines( + Contains("branch-b").IsSelected(), + Contains("branch-a"), + Contains("branch-c"), + Contains("unrelated"), + Contains("master"), + ) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index c7584bb35..04b01d475 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -88,6 +88,7 @@ var tests = []*components.IntegrationTest{ branch.SortLocalBranches, branch.SortLocalBranchesInStackOrder, branch.SortRemoteBranches, + branch.SortRemoteBranchesInStackOrder, branch.SquashMerge, branch.Suggestions, branch.UnsetUpstream,