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.
This commit is contained in:
Carlos Martín Nieto
2014-09-04 01:58:05 +02:00
parent 70410349ff
commit 747e7c2136
4 changed files with 64 additions and 36 deletions

View File

@@ -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
*/

View File

@@ -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

View File

@@ -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),

View File

@@ -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)