Skip to content

More correct reference updating #414

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Sep 5, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/references.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,18 @@ Example::
The Reference type
====================

.. autoclass:: pygit2.Reference

.. autoattribute:: pygit2.Reference.name
.. autoattribute:: pygit2.Reference.shorthand
.. autoattribute:: pygit2.Reference.target
.. autoattribute:: pygit2.Reference.type

.. automethod:: pygit2.Reference.set_target
.. automethod:: pygit2.Reference.delete
.. automethod:: pygit2.Reference.rename
.. automethod:: pygit2.Reference.resolve
.. automethod:: pygit2.Reference.log
.. automethod:: pygit2.Reference.log_append

Example::

Expand Down
10 changes: 10 additions & 0 deletions pygit2/decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
*/
Expand Down
52 changes: 51 additions & 1 deletion pygit2/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,57 @@ def checkout(self, refname=None, **kwargs):
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
Expand Down
127 changes: 45 additions & 82 deletions src/reference.c
Original file line number Diff line number Diff line change
Expand Up @@ -205,10 +205,7 @@ Reference_resolve(Reference *self, PyObject *args)
PyDoc_STRVAR(Reference_target__doc__,
"The reference target: If direct the value will be an Oid object, if it\n"
"is symbolic it will be an string with the full name of the target\n"
"reference.\n"
"\n"
"The target is writable. Setting the Reference's target to another Oid\n"
"object will direct the reference to that Oid instead.");
"reference.\n");

PyObject *
Reference_target__get__(Reference *self)
Expand All @@ -230,48 +227,80 @@ Reference_target__get__(Reference *self)
return to_path(c_name);
}

int
Reference_target__set__(Reference *self, PyObject *py_target)
PyDoc_STRVAR(Reference_set_target__doc__,
"set_target(target, [signature, message])\n"
"\n"
"Set the target of this reference.\n"
"\n"
"Update the reference using the given signature and message.\n"
"These will be used to fill the reflog entry which will be created\n"
"as a result of this update\n"
"\n"
"Arguments:\n"
"\n"
"target\n"
" The new target for this reference\n"
"signature\n"
" The signature to use for the reflog. If left out, the repository's\n"
" default identity will be used.\n"
"message\n"
" Message to use for the reflog.\n");

PyObject *
Reference_set_target(Reference *self, PyObject *args, PyObject *kwds)
{
git_oid oid;
char *c_name;
int err;
git_reference *new_ref;
const git_signature *sig = NULL;
PyObject *py_target = NULL;
Signature *py_signature = NULL;
const char *message = NULL;
char *keywords[] = {"target", "signature", "message", NULL};

CHECK_REFERENCE(self);

if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O!s", keywords,
&py_target, &SignatureType, &py_signature, &message))
return NULL;

CHECK_REFERENCE_INT(self);
if (py_signature)
sig = py_signature->signature;

/* Case 1: Direct */
if (GIT_REF_OID == git_reference_type(self->reference)) {
err = py_oid_to_git_oid_expand(self->repo->repo, py_target, &oid);
if (err < 0)
return err;
goto error;

err = git_reference_set_target(&new_ref, self->reference, &oid, NULL, NULL);
err = git_reference_set_target(&new_ref, self->reference, &oid, sig, message);
if (err < 0)
goto error;

git_reference_free(self->reference);
self->reference = new_ref;
return 0;
Py_RETURN_NONE;
}

/* Case 2: Symbolic */
c_name = py_path_to_c_str(py_target);
if (c_name == NULL)
return -1;
return NULL;

err = git_reference_symbolic_set_target(&new_ref, self->reference, c_name, NULL, NULL);
err = git_reference_symbolic_set_target(&new_ref, self->reference, c_name, sig, message);
free(c_name);
if (err < 0)
goto error;

git_reference_free(self->reference);
self->reference = new_ref;
return 0;

Py_RETURN_NONE;

error:
Error_set(err);
return -1;
return NULL;
}


Expand Down Expand Up @@ -335,72 +364,6 @@ Reference_log(Reference *self)
return (PyObject*)iter;
}

PyDoc_STRVAR(Reference_log_append__doc__,
"log_append(oid, committer, message[, encoding])\n"
"\n"
"Append a reflog entry to the reference. If the oid is None then keep\n"
"the current reference's oid. The message parameter may be None.");

PyObject *
Reference_log_append(Reference *self, PyObject *args)
{
git_signature *committer;
const char *message = NULL;
git_reflog *reflog;
git_oid oid;
const git_oid *ref_oid;
int err;
PyObject *py_oid = NULL;
Signature *py_committer;
PyObject *py_message = NULL;
char *encoding = NULL;
git_repository *repo;

CHECK_REFERENCE(self);

/* Input parameters */
if (!PyArg_ParseTuple(args, "OO!O|s", &py_oid,
&SignatureType, &py_committer,
&py_message, &encoding))
return NULL;

if (py_oid == Py_None)
ref_oid = git_reference_target(self->reference);
else {
err = py_oid_to_git_oid_expand(self->repo->repo, py_oid, &oid);
if (err < 0)
return NULL;
ref_oid = &oid;
}

if (py_message != Py_None) {
message = py_str_to_c_str(py_message, encoding);
if (message == NULL)
return NULL;
}

/* Go */
repo = git_reference_owner(self->reference);
err = git_reflog_read(&reflog, repo, git_reference_name(self->reference));
if (err < 0) {
free((void *)message);
return NULL;
}

committer = (git_signature *)py_committer->signature;
err = git_reflog_append(reflog, ref_oid, committer, message);
if (!err)
err = git_reflog_write(reflog);

git_reflog_free(reflog);
free((void *)message);

if (err < 0)
return NULL;

Py_RETURN_NONE;
}

PyDoc_STRVAR(Reference_get_object__doc__,
"get_object() -> object\n"
"\n"
Expand Down Expand Up @@ -514,15 +477,15 @@ PyMethodDef Reference_methods[] = {
METHOD(Reference, rename, METH_O),
METHOD(Reference, resolve, METH_NOARGS),
METHOD(Reference, log, METH_NOARGS),
METHOD(Reference, log_append, METH_VARARGS),
METHOD(Reference, get_object, METH_NOARGS),
METHOD(Reference, set_target, METH_VARARGS | METH_KEYWORDS),
{NULL}
};

PyGetSetDef Reference_getseters[] = {
GETTER(Reference, name),
GETTER(Reference, shorthand),
GETSET(Reference, target),
GETTER(Reference, target),
GETTER(Reference, type),
{NULL}
};
Expand Down
34 changes: 1 addition & 33 deletions src/repository.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
Expand Down Expand Up @@ -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),
Expand Down
44 changes: 0 additions & 44 deletions test/test_reflog.py

This file was deleted.

Loading