Преглед изворни кода

Add dulwich.porcelain.update_head. Fixes #439

Jelmer Vernooij пре 7 година
родитељ
комит
5296c2cc9f
4 измењених фајлова са 75 додато и 1 уклоњено
  1. 4 0
      NEWS
  2. 9 1
      dulwich/objectspec.py
  3. 29 0
      dulwich/porcelain.py
  4. 33 0
      dulwich/tests/test_porcelain.py

+ 4 - 0
NEWS

@@ -7,6 +7,10 @@
 
   * Fix headers of empty chunks in unified diffs. (Taras Postument, #543)
 
+ IMPROVEMENTS
+
+  * Add ``dulwich.porcelain.update_head``. (Jelmer Vernooij, #439)
+
 0.18.2	2017-08-01
 
  TEST FIXES

+ 9 - 1
dulwich/objectspec.py

@@ -172,7 +172,15 @@ def parse_commit(repo, committish):
     :raise ValueError: If the range can not be parsed
     """
     committish = to_bytes(committish)
-    return repo[committish]  # For now..
+    try:
+        return repo[committish]
+    except KeyError:
+        pass
+    try:
+        return repo[parse_ref(repo, committish)]
+    except KeyError:
+        pass
+    raise KeyError(committish)
 
 
 # TODO: parse_path_in_tree(), which handles e.g. v1.0:Documentation

+ 29 - 0
dulwich/porcelain.py

@@ -25,6 +25,7 @@ Currently implemented:
  * add
  * branch{_create,_delete,_list}
  * check-ignore
+ * checkout
  * clone
  * commit
  * commit-tree
@@ -100,7 +101,9 @@ from dulwich.objects import (
     pretty_format_tree_entry,
     )
 from dulwich.objectspec import (
+    parse_commit,
     parse_object,
+    parse_ref,
     parse_reftuples,
     parse_tree,
     )
@@ -1148,3 +1151,29 @@ def check_ignore(repo, paths, no_index=False):
                 continue
             if ignore_manager.is_ignored(path):
                 yield path
+
+
+def update_head(repo, target, detached=False, new_branch=None):
+    """Update HEAD to point at a new branch/commit.
+
+    Note that this does not actually update the working tree.
+
+    :param repo: Path to the repository
+    :param detach: Create a detached head
+    :param target: Branch or committish to switch to
+    :param new_branch: New branch to create
+    """
+    with open_repo_closing(repo) as r:
+        if new_branch is not None:
+            to_set = b"refs/heads/" + new_branch.encode(DEFAULT_ENCODING)
+        else:
+            to_set = b"HEAD"
+        if detached:
+            # TODO(jelmer): Provide some way so that the actual ref gets
+            # updated rather than what it points to, so the delete isn't necessary.
+            del r.refs[to_set]
+            r.refs[to_set] = parse_commit(r, target).id
+        else:
+            r.refs.set_symbolic_ref(to_set, parse_ref(r, target))
+        if new_branch is not None:
+            r.refs.set_symbolic_ref(b"HEAD", to_set)

+ 33 - 0
dulwich/tests/test_porcelain.py

@@ -1134,3 +1134,36 @@ class CheckIgnoreTests(PorcelainTestCase):
         self.assertEqual(
             ['foo'],
             list(porcelain.check_ignore(self.repo, ['foo'], no_index=True)))
+
+
+class UpdateHeadTests(PorcelainTestCase):
+
+    def test_set_to_branch(self):
+        [c1] = build_commit_graph(self.repo.object_store, [[1]])
+        self.repo.refs[b"refs/heads/blah"] = c1.id
+        porcelain.update_head(self.repo, "blah")
+        self.assertEqual(c1.id, self.repo.head())
+        self.assertEqual(b'ref: refs/heads/blah',
+                         self.repo.refs.read_ref('HEAD'))
+
+    def test_set_to_branch_detached(self):
+        [c1] = build_commit_graph(self.repo.object_store, [[1]])
+        self.repo.refs[b"refs/heads/blah"] = c1.id
+        porcelain.update_head(self.repo, "blah", detached=True)
+        self.assertEqual(c1.id, self.repo.head())
+        self.assertEqual(c1.id, self.repo.refs.read_ref(b'HEAD'))
+
+    def test_set_to_commit_detached(self):
+        [c1] = build_commit_graph(self.repo.object_store, [[1]])
+        self.repo.refs[b"refs/heads/blah"] = c1.id
+        porcelain.update_head(self.repo, c1.id, detached=True)
+        self.assertEqual(c1.id, self.repo.head())
+        self.assertEqual(c1.id, self.repo.refs.read_ref(b'HEAD'))
+
+    def test_set_new_branch(self):
+        [c1] = build_commit_graph(self.repo.object_store, [[1]])
+        self.repo.refs[b"refs/heads/blah"] = c1.id
+        porcelain.update_head(self.repo, "blah", new_branch="bar")
+        self.assertEqual(c1.id, self.repo.head())
+        self.assertEqual(b'ref: refs/heads/bar',
+                         self.repo.refs.read_ref(b'HEAD'))