Merge remote-tracking branch 'carlos/reference-update'
This commit is contained in:
@@ -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::
|
||||
|
||||
|
@@ -30,6 +30,13 @@ typedef struct git_strarray {
|
||||
typedef int64_t git_off_t;
|
||||
typedef int64_t git_time_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,
|
||||
@@ -462,6 +469,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
|
||||
*/
|
||||
|
@@ -275,7 +275,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
|
||||
|
127
src/reference.c
127
src/reference.c
@@ -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)
|
||||
@@ -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_INT(self);
|
||||
CHECK_REFERENCE(self);
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O!s", keywords,
|
||||
&py_target, &SignatureType, &py_signature, &message))
|
||||
return NULL;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
@@ -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}
|
||||
};
|
||||
|
@@ -177,38 +177,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.");
|
||||
@@ -1447,7 +1415,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),
|
||||
|
@@ -1,44 +0,0 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
# as published by the Free Software Foundation.
|
||||
#
|
||||
# In addition to the permissions in the GNU General Public License,
|
||||
# the authors give you unlimited permission to link the compiled
|
||||
# version of this file into combinations with other programs,
|
||||
# and to distribute those combinations without any restriction
|
||||
# coming from the use of this file. (The General Public License
|
||||
# restrictions do apply in other respects; for example, they cover
|
||||
# modification of the file, and distribution when not linked into
|
||||
# a combined executable.)
|
||||
#
|
||||
# This file is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; see the file COPYING. If not, write to
|
||||
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
||||
# Boston, MA 02110-1301, USA.
|
||||
|
||||
"""Tests for reference log."""
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from pygit2 import Signature
|
||||
from . import utils
|
||||
|
||||
|
||||
class ReflogTest(utils.RepoTestCase):
|
||||
|
||||
def test_log_append(self):
|
||||
repo = self.repo
|
||||
master = repo.lookup_reference("refs/heads/master")
|
||||
signature = Signature('xtao', 'xutao@douban.com')
|
||||
master.log_append(None, signature, 'reflog')
|
||||
self.assertTrue('reflog' in [entry.message for entry in master.log()])
|
@@ -31,7 +31,7 @@ from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
import unittest
|
||||
|
||||
from pygit2 import GitError, GIT_REF_OID, GIT_REF_SYMBOLIC
|
||||
from pygit2 import GitError, GIT_REF_OID, GIT_REF_SYMBOLIC, Signature
|
||||
from . import utils
|
||||
|
||||
|
||||
@@ -77,13 +77,13 @@ class ReferencesTest(utils.RepoTestCase):
|
||||
def test_reference_set_sha(self):
|
||||
NEW_COMMIT = '5ebeeebb320790caf276b9fc8b24546d63316533'
|
||||
reference = self.repo.lookup_reference('refs/heads/master')
|
||||
reference.target = NEW_COMMIT
|
||||
reference.set_target(NEW_COMMIT)
|
||||
self.assertEqual(reference.target.hex, NEW_COMMIT)
|
||||
|
||||
def test_reference_set_sha_prefix(self):
|
||||
NEW_COMMIT = '5ebeeebb320790caf276b9fc8b24546d63316533'
|
||||
reference = self.repo.lookup_reference('refs/heads/master')
|
||||
reference.target = NEW_COMMIT[0:6]
|
||||
reference.set_target(NEW_COMMIT[0:6])
|
||||
self.assertEqual(reference.target.hex, NEW_COMMIT)
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ class ReferencesTest(utils.RepoTestCase):
|
||||
def test_set_target(self):
|
||||
reference = self.repo.lookup_reference('HEAD')
|
||||
self.assertEqual(reference.target, 'refs/heads/master')
|
||||
reference.target = 'refs/heads/i18n'
|
||||
reference.set_target('refs/heads/i18n')
|
||||
self.assertEqual(reference.target, 'refs/heads/i18n')
|
||||
|
||||
def test_get_shorthand(self):
|
||||
@@ -109,6 +109,16 @@ class ReferencesTest(utils.RepoTestCase):
|
||||
reference = self.repo.create_reference('refs/remotes/origin/master', LAST_COMMIT)
|
||||
self.assertEqual(reference.shorthand, 'origin/master')
|
||||
|
||||
def test_set_target_with_message(self):
|
||||
reference = self.repo.lookup_reference('HEAD')
|
||||
self.assertEqual(reference.target, 'refs/heads/master')
|
||||
sig = Signature('foo', 'bar')
|
||||
msg = 'Hello log'
|
||||
reference.set_target('refs/heads/i18n', signature=sig, message=msg)
|
||||
self.assertEqual(reference.target, 'refs/heads/i18n')
|
||||
self.assertEqual(list(reference.log())[0].message, msg)
|
||||
self.assertEqualSignature(list(reference.log())[0].committer, sig)
|
||||
|
||||
def test_delete(self):
|
||||
repo = self.repo
|
||||
|
||||
@@ -124,8 +134,6 @@ class ReferencesTest(utils.RepoTestCase):
|
||||
self.assertRaises(GitError, getattr, reference, 'name')
|
||||
self.assertRaises(GitError, getattr, reference, 'type')
|
||||
self.assertRaises(GitError, getattr, reference, 'target')
|
||||
self.assertRaises(GitError, setattr, reference, 'target', LAST_COMMIT)
|
||||
self.assertRaises(GitError, setattr, reference, 'target', "a/b/c")
|
||||
self.assertRaises(GitError, reference.delete)
|
||||
self.assertRaises(GitError, reference.resolve)
|
||||
self.assertRaises(GitError, reference.rename, "refs/tags/version2")
|
||||
|
@@ -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)
|
||||
|
||||
|
Reference in New Issue
Block a user