From 747e7c2136398fa6a65c795a4e40f6de4e0d3513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 4 Sep 2014 01:58:05 +0200 Subject: [PATCH] Repository: make head read-only and introduce set_head() Following from the previous commits, make 'head' read-only and provide a method to update head while providing a message. The checkout() codepath which switches branches has been updated to provide a reflog entry which mimics git's. --- pygit2/decl.h | 10 ++++++++ pygit2/repository.py | 52 ++++++++++++++++++++++++++++++++++++++++- src/repository.c | 34 +-------------------------- test/test_repository.py | 4 ++-- 4 files changed, 64 insertions(+), 36 deletions(-) diff --git a/pygit2/decl.h b/pygit2/decl.h index 4e3df66..7e9825b 100644 --- a/pygit2/decl.h +++ b/pygit2/decl.h @@ -30,6 +30,13 @@ typedef struct git_strarray { typedef int64_t git_off_t; +typedef enum { + GIT_REF_INVALID = 0, + GIT_REF_OID = 1, + GIT_REF_SYMBOLIC = 2, + GIT_REF_LISTALL = 3, +} git_ref_t; + typedef enum { GIT_OK = 0, GIT_ERROR = -1, @@ -450,6 +457,9 @@ int git_repository_init_ext( const char *repo_path, git_repository_init_options *opts); +int git_repository_set_head(git_repository *repo, const char *refname, const git_signature *signature, const char *log_message); +int git_repository_set_head_detached(git_repository *repo, const git_oid *commitish, const git_signature *signature, const char *log_message); + /* * git_index */ diff --git a/pygit2/repository.py b/pygit2/repository.py index ac284b9..fa9f799 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -274,7 +274,57 @@ class Repository(_Repository): oid = reference.resolve().target treeish = self[oid] self.checkout_tree(treeish, **kwargs) - self.head = refname + head = self.lookup_reference('HEAD') + if head.type == C.GIT_REF_SYMBOLIC: + from_ = self.head.shorthand + else: + from_ = head.target.hex + + try: + signature = self.default_signature + except: + signature = None + + reflog_text = "checkout: moving from %s to %s" % (from_, reference) + self.set_head(refname, signature, reflog_text) + + # + # Setting HEAD + # + def set_head(self, target, signature=None, message=None): + """Set HEAD to point to the given target + + Arguments: + + target + The new target for HEAD. Can be a string or Oid (to detach) + + signature + Signature to use for the reflog. If not provided, the repository's + default will be used + + message + Message to use for the reflog + """ + + sig_ptr = ffi.new('git_signature **') + if signature: + ffi.buffer(sig_ptr)[:] = signature._pointer[:] + + message_ptr = ffi.NULL + if message_ptr: + message_ptr = to_bytes(message) + + if isinstance(target, Oid): + oid = ffi.new('git_oid *') + ffi.buffer(oid)[:] = target.raw[:] + err = C.git_repository_set_head_detached(self._repo, oid, sig_ptr[0], message_ptr) + check_error(err) + return + + # if it's a string, then it's a reference name + err = C.git_repository_set_head(self._repo, to_bytes(target), sig_ptr[0], message_ptr) + check_error(err) # # Diff diff --git a/src/repository.c b/src/repository.c index 2b99558..c25b8aa 100644 --- a/src/repository.c +++ b/src/repository.c @@ -178,38 +178,6 @@ Repository_head__get__(Repository *self) return wrap_reference(head, self); } -int -Repository_head__set__(Repository *self, PyObject *py_val) -{ - int err; - if (PyObject_TypeCheck(py_val, &OidType)) { - git_oid oid; - py_oid_to_git_oid(py_val, &oid); - err = git_repository_set_head_detached(self->repo, &oid, NULL, NULL); - if (err < 0) { - Error_set(err); - return -1; - } - } else { - const char *refname; - PyObject *trefname; - - refname = py_str_borrow_c_str(&trefname, py_val, NULL); - if (refname == NULL) - return -1; - - err = git_repository_set_head(self->repo, refname, NULL, NULL); - Py_DECREF(trefname); - if (err < 0) { - Error_set_str(err, refname); - return -1; - } - } - - return 0; -} - - PyDoc_STRVAR(Repository_head_is_detached__doc__, "A repository's HEAD is detached when it points directly to a commit\n" "instead of a branch."); @@ -1514,7 +1482,7 @@ PyMethodDef Repository_methods[] = { PyGetSetDef Repository_getseters[] = { GETTER(Repository, path), - GETSET(Repository, head), + GETTER(Repository, head), GETTER(Repository, head_is_detached), GETTER(Repository, head_is_unborn), GETTER(Repository, is_empty), diff --git a/test/test_repository.py b/test/test_repository.py index 566715a..72a5e64 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -76,10 +76,10 @@ class RepositoryTest(utils.BareRepoTestCase): def test_set_head(self): # Test setting a detatched HEAD. - self.repo.head = Oid(hex=PARENT_SHA) + self.repo.set_head(Oid(hex=PARENT_SHA)) self.assertEqual(self.repo.head.target.hex, PARENT_SHA) # And test setting a normal HEAD. - self.repo.head = "refs/heads/master" + self.repo.set_head("refs/heads/master") self.assertEqual(self.repo.head.name, "refs/heads/master") self.assertEqual(self.repo.head.target.hex, HEAD_SHA)