ソースを参照

Add support for branch.sort configuration

Implements sorting functionality for branch listing based on the
branch.sort configuration variable. Supports refname (default),
committerdate, and authordate sorting with optional reverse order.
Jelmer Vernooij 1 ヶ月 前
コミット
9f2e78f8ee
2 ファイル変更159 行追加1 行削除
  1. 47 1
      dulwich/porcelain.py
  2. 112 0
      tests/test_porcelain.py

+ 47 - 1
dulwich/porcelain.py

@@ -2338,9 +2338,55 @@ def branch_list(repo):
 
     Args:
       repo: Path to the repository
+    Returns:
+      List of branch names (without refs/heads/ prefix)
     """
     with open_repo_closing(repo) as r:
-        return r.refs.keys(base=LOCAL_BRANCH_PREFIX)
+        branches = list(r.refs.keys(base=LOCAL_BRANCH_PREFIX))
+
+        # Check for branch.sort configuration
+        config = r.get_config_stack()
+        try:
+            sort_key = config.get((b"branch",), b"sort").decode()
+        except KeyError:
+            # Default is refname (alphabetical)
+            sort_key = "refname"
+
+        # Parse sort key
+        reverse = False
+        if sort_key.startswith("-"):
+            reverse = True
+            sort_key = sort_key[1:]
+
+        # Apply sorting
+        if sort_key == "refname":
+            # Simple alphabetical sort (default)
+            branches.sort(reverse=reverse)
+        elif sort_key in ("committerdate", "authordate"):
+            # Sort by date
+            def get_commit_date(branch_name):
+                ref = LOCAL_BRANCH_PREFIX + branch_name
+                sha = r.refs[ref]
+                commit = r.object_store[sha]
+                if sort_key == "committerdate":
+                    return commit.commit_time
+                else:  # authordate
+                    return commit.author_time
+
+            # Sort branches by date
+            # Note: Python's sort naturally orders smaller values first (ascending)
+            # For dates, this means oldest first by default
+            # Use a stable sort with branch name as secondary key for consistent ordering
+            if reverse:
+                # For reverse sort, we want newest dates first but alphabetical names second
+                branches.sort(key=lambda b: (-get_commit_date(b), b))
+            else:
+                branches.sort(key=lambda b: (get_commit_date(b), b))
+        else:
+            # Unknown sort key, fall back to default
+            branches.sort()
+
+        return branches
 
 
 def active_branch(repo):

+ 112 - 0
tests/test_porcelain.py

@@ -4739,6 +4739,118 @@ class BranchListTests(PorcelainTestCase):
         porcelain.branch_create(self.repo, b"foo")
         self.assertEqual({b"master", b"foo"}, set(porcelain.branch_list(self.repo)))
 
+    def test_sort_by_refname(self) -> None:
+        """Test branch.sort=refname (default alphabetical)."""
+        [c1] = build_commit_graph(self.repo.object_store, [[1]])
+        self.repo[b"HEAD"] = c1.id
+
+        # Create branches in non-alphabetical order
+        porcelain.branch_create(self.repo, b"zebra")
+        porcelain.branch_create(self.repo, b"alpha")
+        porcelain.branch_create(self.repo, b"beta")
+
+        # Set branch.sort to refname (though it's the default)
+        config = self.repo.get_config()
+        config.set((b"branch",), b"sort", b"refname")
+        config.write_to_path()
+
+        # Should be sorted alphabetically
+        branches = porcelain.branch_list(self.repo)
+        self.assertEqual([b"alpha", b"beta", b"master", b"zebra"], branches)
+
+    def test_sort_by_refname_reverse(self) -> None:
+        """Test branch.sort=-refname (reverse alphabetical)."""
+        [c1] = build_commit_graph(self.repo.object_store, [[1]])
+        self.repo[b"HEAD"] = c1.id
+
+        # Create branches
+        porcelain.branch_create(self.repo, b"zebra")
+        porcelain.branch_create(self.repo, b"alpha")
+        porcelain.branch_create(self.repo, b"beta")
+
+        # Set branch.sort to -refname
+        config = self.repo.get_config()
+        config.set((b"branch",), b"sort", b"-refname")
+        config.write_to_path()
+
+        # Should be sorted reverse alphabetically
+        branches = porcelain.branch_list(self.repo)
+        self.assertEqual([b"zebra", b"master", b"beta", b"alpha"], branches)
+
+    def test_sort_by_committerdate(self) -> None:
+        """Test branch.sort=committerdate."""
+        # Use build_commit_graph to create proper commits with specific times
+        c1, c2, c3 = build_commit_graph(
+            self.repo.object_store,
+            [[1], [2], [3]],
+            attrs={
+                1: {"commit_time": 1000},  # oldest
+                2: {"commit_time": 2000},  # newest
+                3: {"commit_time": 1500},  # middle
+            },
+        )
+
+        self.repo[b"HEAD"] = c1.id
+
+        # Create branches pointing to different commits
+        self.repo.refs[b"refs/heads/master"] = c1.id  # master points to oldest
+        self.repo.refs[b"refs/heads/oldest"] = c1.id
+        self.repo.refs[b"refs/heads/newest"] = c2.id
+        self.repo.refs[b"refs/heads/middle"] = c3.id
+
+        # Set branch.sort to committerdate
+        config = self.repo.get_config()
+        config.set((b"branch",), b"sort", b"committerdate")
+        config.write_to_path()
+
+        # Should be sorted by commit time (oldest first)
+        branches = porcelain.branch_list(self.repo)
+        self.assertEqual([b"master", b"oldest", b"middle", b"newest"], branches)
+
+    def test_sort_by_committerdate_reverse(self) -> None:
+        """Test branch.sort=-committerdate."""
+        # Use build_commit_graph to create proper commits with specific times
+        c1, c2, c3 = build_commit_graph(
+            self.repo.object_store,
+            [[1], [2], [3]],
+            attrs={
+                1: {"commit_time": 1000},  # oldest
+                2: {"commit_time": 2000},  # newest
+                3: {"commit_time": 1500},  # middle
+            },
+        )
+
+        self.repo[b"HEAD"] = c1.id
+
+        # Create branches pointing to different commits
+        self.repo.refs[b"refs/heads/master"] = c1.id  # master points to oldest
+        self.repo.refs[b"refs/heads/oldest"] = c1.id
+        self.repo.refs[b"refs/heads/newest"] = c2.id
+        self.repo.refs[b"refs/heads/middle"] = c3.id
+
+        # Set branch.sort to -committerdate
+        config = self.repo.get_config()
+        config.set((b"branch",), b"sort", b"-committerdate")
+        config.write_to_path()
+
+        # Should be sorted by commit time (newest first)
+        branches = porcelain.branch_list(self.repo)
+        self.assertEqual([b"newest", b"middle", b"master", b"oldest"], branches)
+
+    def test_sort_default(self) -> None:
+        """Test default sorting (no config)."""
+        [c1] = build_commit_graph(self.repo.object_store, [[1]])
+        self.repo[b"HEAD"] = c1.id
+
+        # Create branches in non-alphabetical order
+        porcelain.branch_create(self.repo, b"zebra")
+        porcelain.branch_create(self.repo, b"alpha")
+        porcelain.branch_create(self.repo, b"beta")
+
+        # No config set - should default to alphabetical
+        branches = porcelain.branch_list(self.repo)
+        self.assertEqual([b"alpha", b"beta", b"master", b"zebra"], branches)
+
 
 class BranchCreateTests(PorcelainTestCase):
     def test_branch_exists(self) -> None: