From 160cf64abc273f023a5a0a8683f634c2e9e653fd Mon Sep 17 00:00:00 2001 From: Hugh Cole-Baker <sigmaris@gmail.com> Date: Mon, 10 Oct 2011 23:48:35 +0100 Subject: [PATCH] Add support for using short hex string prefixes Allows Repository_getitem, Repository_read(_raw), Repository_create_commit, Repository_create_tag and Object_read_raw to use short hex strings to lookup objects. Also test getting objects from Repository using prefixes, looking up commit trees and parents by hex prefix, and looking up tag targets by prefix. Also stop raising TypeError if passing a too-short hex prefix to the lookup functions, instead use ValueError. --- pygit2.c | 45 ++++++++++++++++++++++++----------------- test/test_commit.py | 11 +++++++--- test/test_repository.py | 22 ++++++++++++++++++++ test/test_tag.py | 9 +++++++-- 4 files changed, 63 insertions(+), 24 deletions(-) diff --git a/pygit2.c b/pygit2.c index 6a65f3a..c1bf4df 100644 --- a/pygit2.c +++ b/pygit2.c @@ -190,9 +190,10 @@ Error_set_py_obj(int err, PyObject *py_obj) assert(err < 0); - if (err == GIT_ENOTOID && !PyString_Check(py_obj)) { + if (err == GIT_ENOTOID && !PyString_Check(py_obj) + && !PyUnicode_Check(py_obj)) { PyErr_Format(PyExc_TypeError, - "Git object id must be byte string, not: %.200s", + "Git object id must be byte or a text string, not: %.200s", Py_TYPE(py_obj)->tp_name); return NULL; } @@ -209,17 +210,19 @@ Error_set_py_obj(int err, PyObject *py_obj) } static PyObject * -lookup_object(Repository *repo, const git_oid *oid, git_otype type) +lookup_object_prefix(Repository *repo, const git_oid *oid, size_t len, + git_otype type) { int err; char hex[GIT_OID_HEXSZ + 1]; git_object *obj; Object *py_obj = NULL; - err = git_object_lookup(&obj, repo->repo, oid, type); + err = git_object_lookup_prefix(&obj, repo->repo, oid, + (unsigned int)len, type); if (err < 0) { git_oid_fmt(hex, oid); - hex[GIT_OID_HEXSZ] = '\0'; + hex[len] = '\0'; return Error_set_str(err, hex); } @@ -248,6 +251,12 @@ lookup_object(Repository *repo, const git_oid *oid, git_otype type) return (PyObject*)py_obj; } +static PyObject * +lookup_object(Repository *repo, const git_oid *oid, git_otype type) +{ + return lookup_object_prefix(repo, oid, GIT_OID_HEXSZ, type); +} + static git_otype int_to_loose_object_type(int type_id) { @@ -415,18 +424,18 @@ Repository_getitem(Repository *self, PyObject *value) size_t len; len = py_str_to_git_oid(value, &oid); - TODO_SUPPORT_SHORT_HEXS(len) if (len == 0) return NULL; - return lookup_object(self, &oid, GIT_OBJ_ANY); + return lookup_object_prefix(self, &oid, len, GIT_OBJ_ANY); } static int Repository_read_raw(git_odb_object **obj, git_repository *repo, - const git_oid *oid) + const git_oid *oid, size_t len) { - return git_odb_read(obj, git_repository_database(repo), oid); + return git_odb_read_prefix(obj, git_repository_database(repo), + oid, (unsigned int)len); } static PyObject * @@ -438,11 +447,10 @@ Repository_read(Repository *self, PyObject *py_hex) size_t len; len = py_str_to_git_oid(py_hex, &oid); - TODO_SUPPORT_SHORT_HEXS(len) if (len == 0) return NULL; - err = Repository_read_raw(&obj, self->repo, &oid); + err = Repository_read_raw(&obj, self->repo, &oid, len); if (err < 0) return Error_set_py_obj(err, py_hex); @@ -659,13 +667,12 @@ Repository_create_commit(Repository *self, PyObject *args) return NULL; len = py_str_to_git_oid(py_oid, &oid); - TODO_SUPPORT_SHORT_HEXS(len) if (len == 0) return NULL; message = py_str_to_c_str(py_message); - err = git_tree_lookup(&tree, self->repo, &oid); + err = git_tree_lookup_prefix(&tree, self->repo, &oid, (unsigned int)len); if (err < 0) return Error_set(err); @@ -679,12 +686,12 @@ Repository_create_commit(Repository *self, PyObject *args) for (i = 0; i < parent_count; i++) { py_parent = PyList_GET_ITEM(py_parents, i); len = py_str_to_git_oid(py_parent, &oid); - TODO_SUPPORT_SHORT_HEXS(len) if (len == 0) { git_tree_close(tree); return free_parents(parents, i); } - if (git_commit_lookup(&parents[i], self->repo, &oid)) { + if (git_commit_lookup_prefix(&parents[i], self->repo, &oid, + (unsigned int)len)) { git_tree_close(tree); return free_parents(parents, i); } @@ -721,14 +728,14 @@ Repository_create_tag(Repository *self, PyObject *args) return NULL; len = py_str_to_git_oid(py_oid, &oid); - TODO_SUPPORT_SHORT_HEXS(len) if (len == 0) return NULL; - err = git_object_lookup(&target, self->repo, &oid, target_type); + err = git_object_lookup_prefix(&target, self->repo, &oid, + (unsigned int)len, target_type); if (err < 0) { git_oid_fmt(hex, &oid); - hex[GIT_OID_HEXSZ] = '\0'; + hex[len] = '\0'; return Error_set_str(err, hex); } @@ -1041,7 +1048,7 @@ Object_read_raw(Object *self) oid = git_object_id(self->obj); assert(oid); - err = Repository_read_raw(&obj, self->repo->repo, oid); + err = Repository_read_raw(&obj, self->repo->repo, oid, GIT_OID_HEXSZ); if (err < 0) { aux = git_oid_to_py_str(oid); Error_set_py_obj(err, aux); diff --git a/test/test_commit.py b/test/test_commit.py index f67fd74..6635959 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -71,10 +71,15 @@ class CommitTest(utils.BareRepoTestCase): committer = ('John Doe', 'jdoe@example.com', 12346, 0) author = ('J. David Ibáñez', 'jdavid@example.com', 12345, 0) tree = '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' + tree_prefix = tree[:5] + too_short_prefix = tree[:3] - parents = [COMMIT_SHA] - sha = repo.create_commit(None, author, committer, message, tree, - parents) + parents = [COMMIT_SHA[:5]] + self.assertRaises(ValueError, repo.create_commit, None, author, + committer, message, too_short_prefix, parents) + + sha = repo.create_commit(None, author, committer, message, + tree_prefix, parents) commit = repo[sha] self.assertEqual(GIT_OBJ_COMMIT, commit.type) diff --git a/test/test_repository.py b/test/test_repository.py index 89edcbe..c62537f 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -58,6 +58,10 @@ class RepositoryTest(utils.BareRepoTestCase): a2 = self.repo.read('7f129fd57e31e935c6d60a0c794efe4e6927664b') self.assertEqual((GIT_OBJ_BLOB, 'a contents 2\n'), a2) + + a_hex_prefix = A_HEX_SHA[:4] + a3 = self.repo.read(a_hex_prefix) + self.assertEqual((GIT_OBJ_BLOB, 'a contents\n'), a3) def test_write(self): data = b"hello world" @@ -82,6 +86,12 @@ class RepositoryTest(utils.BareRepoTestCase): self.assertEqual(A_HEX_SHA, a.hex) self.assertEqual(GIT_OBJ_BLOB, a.type) + def test_lookup_blob_prefix(self): + a = self.repo[A_HEX_SHA[:5]] + self.assertEqual(b'a contents\n', a.read_raw()) + self.assertEqual(A_HEX_SHA, a.hex) + self.assertEqual(GIT_OBJ_BLOB, a.type) + def test_lookup_commit(self): commit_sha = '5fe808e8953c12735680c257f56600cb0de44b10' commit = self.repo[commit_sha] @@ -91,6 +101,18 @@ class RepositoryTest(utils.BareRepoTestCase): 'This commit has some additional text.\n'), commit.message) + def test_lookup_commit_prefix(self): + commit_sha = '5fe808e8953c12735680c257f56600cb0de44b10' + commit_sha_prefix = commit_sha[:7] + too_short_prefix = commit_sha[:3] + commit = self.repo[commit_sha_prefix] + self.assertEqual(commit_sha, commit.hex) + self.assertEqual(GIT_OBJ_COMMIT, commit.type) + self.assertEqual(('Second test data commit.\n\n' + 'This commit has some additional text.\n'), + commit.message) + self.assertRaises(ValueError, self.repo.__getitem__, too_short_prefix) + def test_get_path(self): directory = realpath(self.repo.path) expected = realpath(join(self._temp_dir, 'testrepo.git')) diff --git a/test/test_tag.py b/test/test_tag.py index ad786f0..a138882 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -62,8 +62,13 @@ class TagTest(utils.BareRepoTestCase): message = 'Tag a blob.\n' tagger = ('John Doe', 'jdoe@example.com', 12347, 0) - sha = self.repo.create_tag(name, target, pygit2.GIT_OBJ_BLOB, tagger, - message) + target_prefix = target[:5] + too_short_prefix = target[:3] + self.assertRaises(ValueError, self.repo.create_tag, name, + too_short_prefix, pygit2.GIT_OBJ_BLOB, tagger, + message) + sha = self.repo.create_tag(name, target_prefix, pygit2.GIT_OBJ_BLOB, + tagger, message) tag = self.repo[sha] self.assertEqual('3ee44658fd11660e828dfc96b9b5c5f38d5b49bb', tag.hex)