From e07d089be0f214117e160753a3f06f13a4e5e35b Mon Sep 17 00:00:00 2001
From: sergemedvid <mserge@newtonideas.com>
Date: Wed, 14 Sep 2022 15:11:24 +0300
Subject: [PATCH] Sort branches and tags by date descending (#21136)

This fixes #5709 and #17316 by changing the order of listed branches
and tags to show the ones with latest commits atop.
It's achieved with changing underlying "show-ref" git command with
"for-each-ref" as suggested in https://stackoverflow.com/a/5188364
Also, it's passing format string so the output matches "show-ref"
command output.

close #5709
close #17316
---
 modules/git/repo_branch_nogogit.go | 10 +++++-----
 modules/git/repo_branch_test.go    |  4 ++--
 modules/git/repo_tag_nogogit.go    |  2 +-
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/modules/git/repo_branch_nogogit.go b/modules/git/repo_branch_nogogit.go
index 2983a35ca..f5fb244cb 100644
--- a/modules/git/repo_branch_nogogit.go
+++ b/modules/git/repo_branch_nogogit.go
@@ -63,7 +63,7 @@ func (repo *Repository) IsBranchExist(name string) bool {
 // GetBranchNames returns branches from the repository, skipping skip initial branches and
 // returning at most limit branches, or all branches if limit is 0.
 func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) {
-	return callShowRef(repo.Ctx, repo.Path, BranchPrefix, "--heads", skip, limit)
+	return callShowRef(repo.Ctx, repo.Path, BranchPrefix, BranchPrefix+" --sort=-committerdate", skip, limit)
 }
 
 // WalkReferences walks all the references from the repository
@@ -77,9 +77,9 @@ func (repo *Repository) WalkReferences(refType ObjectType, skip, limit int, walk
 	var arg string
 	switch refType {
 	case ObjectTag:
-		arg = "--tags"
+		arg = TagPrefix + " --sort=-taggerdate"
 	case ObjectBranch:
-		arg = "--heads"
+		arg = BranchPrefix + " --sort=-committerdate"
 	default:
 		arg = ""
 	}
@@ -107,9 +107,9 @@ func walkShowRef(ctx context.Context, repoPath, arg string, skip, limit int, wal
 
 	go func() {
 		stderrBuilder := &strings.Builder{}
-		args := []string{"show-ref"}
+		args := []string{"for-each-ref", "--format=%(objectname) %(refname)"}
 		if arg != "" {
-			args = append(args, arg)
+			args = append(args, strings.Fields(arg)...)
 		}
 		err := NewCommand(ctx, args...).Run(&RunOpts{
 			Dir:    repoPath,
diff --git a/modules/git/repo_branch_test.go b/modules/git/repo_branch_test.go
index 56f738709..58a738e28 100644
--- a/modules/git/repo_branch_test.go
+++ b/modules/git/repo_branch_test.go
@@ -22,14 +22,14 @@ func TestRepository_GetBranches(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Len(t, branches, 2)
 	assert.EqualValues(t, 3, countAll)
-	assert.ElementsMatch(t, []string{"branch1", "branch2"}, branches)
+	assert.ElementsMatch(t, []string{"master", "branch2"}, branches)
 
 	branches, countAll, err = bareRepo1.GetBranchNames(0, 0)
 
 	assert.NoError(t, err)
 	assert.Len(t, branches, 3)
 	assert.EqualValues(t, 3, countAll)
-	assert.ElementsMatch(t, []string{"branch1", "branch2", "master"}, branches)
+	assert.ElementsMatch(t, []string{"master", "branch2", "branch1"}, branches)
 
 	branches, countAll, err = bareRepo1.GetBranchNames(5, 1)
 
diff --git a/modules/git/repo_tag_nogogit.go b/modules/git/repo_tag_nogogit.go
index 9a574666f..c2df8b14f 100644
--- a/modules/git/repo_tag_nogogit.go
+++ b/modules/git/repo_tag_nogogit.go
@@ -26,7 +26,7 @@ func (repo *Repository) IsTagExist(name string) bool {
 // GetTags returns all tags of the repository.
 // returning at most limit tags, or all if limit is 0.
 func (repo *Repository) GetTags(skip, limit int) (tags []string, err error) {
-	tags, _, err = callShowRef(repo.Ctx, repo.Path, TagPrefix, "--tags", skip, limit)
+	tags, _, err = callShowRef(repo.Ctx, repo.Path, TagPrefix, TagPrefix+" --sort=-taggerdate", skip, limit)
 	return tags, err
 }