소스 검색

Add parse_remote_ref helper function to dulwich.refs

This function parses remote references (e.g., refs/remotes/origin/main)
into their component parts (remote name and branch name). This is useful
for setting up branch tracking configuration.
Jelmer Vernooij 1 개월 전
부모
커밋
1f6e4f1b31
2개의 변경된 파일58개의 추가작업 그리고 0개의 파일을 삭제
  1. 27 0
      dulwich/refs.py
  2. 31 0
      tests/test_refs.py

+ 27 - 0
dulwich/refs.py

@@ -102,6 +102,33 @@ def check_ref_format(refname: Ref) -> bool:
     return True
 
 
+def parse_remote_ref(ref: bytes) -> tuple[bytes, bytes]:
+    """Parse a remote ref into remote name and branch name.
+    
+    Args:
+      ref: Remote ref like b"refs/remotes/origin/main"
+      
+    Returns:
+      Tuple of (remote_name, branch_name)
+      
+    Raises:
+      ValueError: If ref is not a valid remote ref
+    """
+    if not ref.startswith(LOCAL_REMOTE_PREFIX):
+        raise ValueError(f"Not a remote ref: {ref!r}")
+    
+    # Remove the prefix
+    remainder = ref[len(LOCAL_REMOTE_PREFIX):]
+    
+    # Split into remote name and branch name
+    parts = remainder.split(b"/", 1)
+    if len(parts) != 2:
+        raise ValueError(f"Invalid remote ref format: {ref!r}")
+    
+    remote_name, branch_name = parts
+    return (remote_name, branch_name)
+
+
 class RefsContainer:
     """A container for refs."""
 

+ 31 - 0
tests/test_refs.py

@@ -36,6 +36,7 @@ from dulwich.refs import (
     SymrefLoop,
     _split_ref_line,
     check_ref_format,
+    parse_remote_ref,
     parse_symref_value,
     read_packed_refs,
     read_packed_refs_with_peeled,
@@ -894,6 +895,36 @@ class ParseSymrefValueTests(TestCase):
         self.assertRaises(ValueError, parse_symref_value, b"foobar")
 
 
+class ParseRemoteRefTests(TestCase):
+    def test_valid(self) -> None:
+        # Test simple case
+        remote, branch = parse_remote_ref(b"refs/remotes/origin/main")
+        self.assertEqual(b"origin", remote)
+        self.assertEqual(b"main", branch)
+        
+        # Test with branch containing slashes
+        remote, branch = parse_remote_ref(b"refs/remotes/upstream/feature/new-ui")
+        self.assertEqual(b"upstream", remote)
+        self.assertEqual(b"feature/new-ui", branch)
+    
+    def test_invalid_not_remote_ref(self) -> None:
+        # Not a remote ref
+        with self.assertRaises(ValueError) as cm:
+            parse_remote_ref(b"refs/heads/main")
+        self.assertIn("Not a remote ref", str(cm.exception))
+    
+    def test_invalid_format(self) -> None:
+        # Missing branch name
+        with self.assertRaises(ValueError) as cm:
+            parse_remote_ref(b"refs/remotes/origin")
+        self.assertIn("Invalid remote ref format", str(cm.exception))
+        
+        # Just the prefix
+        with self.assertRaises(ValueError) as cm:
+            parse_remote_ref(b"refs/remotes/")
+        self.assertIn("Invalid remote ref format", str(cm.exception))
+
+
 class StripPeeledRefsTests(TestCase):
     all_refs: ClassVar[dict[bytes, bytes]] = {
         b"refs/heads/master": b"8843d7f92416211de9ebb963ff4ce28125932878",