Skip to content

Commit 747e7c2

Browse files
committed
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.
1 parent 7041034 commit 747e7c2

File tree

4 files changed

+64
-36
lines changed

4 files changed

+64
-36
lines changed

pygit2/decl.h

+10
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ typedef struct git_strarray {
3030

3131
typedef int64_t git_off_t;
3232

33+
typedef enum {
34+
GIT_REF_INVALID = 0,
35+
GIT_REF_OID = 1,
36+
GIT_REF_SYMBOLIC = 2,
37+
GIT_REF_LISTALL = 3,
38+
} git_ref_t;
39+
3340
typedef enum {
3441
GIT_OK = 0,
3542
GIT_ERROR = -1,
@@ -450,6 +457,9 @@ int git_repository_init_ext(
450457
const char *repo_path,
451458
git_repository_init_options *opts);
452459

460+
int git_repository_set_head(git_repository *repo, const char *refname, const git_signature *signature, const char *log_message);
461+
int git_repository_set_head_detached(git_repository *repo, const git_oid *commitish, const git_signature *signature, const char *log_message);
462+
453463
/*
454464
* git_index
455465
*/

pygit2/repository.py

+51-1
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,57 @@ def checkout(self, refname=None, **kwargs):
274274
oid = reference.resolve().target
275275
treeish = self[oid]
276276
self.checkout_tree(treeish, **kwargs)
277-
self.head = refname
277+
head = self.lookup_reference('HEAD')
278+
if head.type == C.GIT_REF_SYMBOLIC:
279+
from_ = self.head.shorthand
280+
else:
281+
from_ = head.target.hex
282+
283+
try:
284+
signature = self.default_signature
285+
except:
286+
signature = None
287+
288+
reflog_text = "checkout: moving from %s to %s" % (from_, reference)
289+
self.set_head(refname, signature, reflog_text)
290+
291+
#
292+
# Setting HEAD
293+
#
294+
def set_head(self, target, signature=None, message=None):
295+
"""Set HEAD to point to the given target
296+
297+
Arguments:
298+
299+
target
300+
The new target for HEAD. Can be a string or Oid (to detach)
301+
302+
signature
303+
Signature to use for the reflog. If not provided, the repository's
304+
default will be used
305+
306+
message
307+
Message to use for the reflog
308+
"""
309+
310+
sig_ptr = ffi.new('git_signature **')
311+
if signature:
312+
ffi.buffer(sig_ptr)[:] = signature._pointer[:]
313+
314+
message_ptr = ffi.NULL
315+
if message_ptr:
316+
message_ptr = to_bytes(message)
317+
318+
if isinstance(target, Oid):
319+
oid = ffi.new('git_oid *')
320+
ffi.buffer(oid)[:] = target.raw[:]
321+
err = C.git_repository_set_head_detached(self._repo, oid, sig_ptr[0], message_ptr)
322+
check_error(err)
323+
return
324+
325+
# if it's a string, then it's a reference name
326+
err = C.git_repository_set_head(self._repo, to_bytes(target), sig_ptr[0], message_ptr)
327+
check_error(err)
278328

279329
#
280330
# Diff

src/repository.c

+1-33
Original file line numberDiff line numberDiff line change
@@ -178,38 +178,6 @@ Repository_head__get__(Repository *self)
178178
return wrap_reference(head, self);
179179
}
180180

181-
int
182-
Repository_head__set__(Repository *self, PyObject *py_val)
183-
{
184-
int err;
185-
if (PyObject_TypeCheck(py_val, &OidType)) {
186-
git_oid oid;
187-
py_oid_to_git_oid(py_val, &oid);
188-
err = git_repository_set_head_detached(self->repo, &oid, NULL, NULL);
189-
if (err < 0) {
190-
Error_set(err);
191-
return -1;
192-
}
193-
} else {
194-
const char *refname;
195-
PyObject *trefname;
196-
197-
refname = py_str_borrow_c_str(&trefname, py_val, NULL);
198-
if (refname == NULL)
199-
return -1;
200-
201-
err = git_repository_set_head(self->repo, refname, NULL, NULL);
202-
Py_DECREF(trefname);
203-
if (err < 0) {
204-
Error_set_str(err, refname);
205-
return -1;
206-
}
207-
}
208-
209-
return 0;
210-
}
211-
212-
213181
PyDoc_STRVAR(Repository_head_is_detached__doc__,
214182
"A repository's HEAD is detached when it points directly to a commit\n"
215183
"instead of a branch.");
@@ -1514,7 +1482,7 @@ PyMethodDef Repository_methods[] = {
15141482

15151483
PyGetSetDef Repository_getseters[] = {
15161484
GETTER(Repository, path),
1517-
GETSET(Repository, head),
1485+
GETTER(Repository, head),
15181486
GETTER(Repository, head_is_detached),
15191487
GETTER(Repository, head_is_unborn),
15201488
GETTER(Repository, is_empty),

test/test_repository.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,10 @@ def test_head(self):
7676

7777
def test_set_head(self):
7878
# Test setting a detatched HEAD.
79-
self.repo.head = Oid(hex=PARENT_SHA)
79+
self.repo.set_head(Oid(hex=PARENT_SHA))
8080
self.assertEqual(self.repo.head.target.hex, PARENT_SHA)
8181
# And test setting a normal HEAD.
82-
self.repo.head = "refs/heads/master"
82+
self.repo.set_head("refs/heads/master")
8383
self.assertEqual(self.repo.head.name, "refs/heads/master")
8484
self.assertEqual(self.repo.head.target.hex, HEAD_SHA)
8585

0 commit comments

Comments
 (0)