Skip to content
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

add hash and equality operations on git objects #853

Merged
merged 5 commits into from
Dec 28, 2018
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
62 changes: 60 additions & 2 deletions src/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <git2.h>
#include "error.h"
#include "types.h"
#include "utils.h"
Expand All @@ -39,6 +40,7 @@ extern PyTypeObject CommitType;
extern PyTypeObject BlobType;
extern PyTypeObject TagType;

PyTypeObject ObjectType;

void
Object_dealloc(Object* self)
Expand Down Expand Up @@ -178,6 +180,62 @@ Object_peel(Object *self, PyObject *py_type)
return wrap_object(peeled, self->repo);
}

Py_hash_t
Object_hash(Object *object)
{
const git_oid *oid = git_object_id(object->obj);
PyObject *py_oid = git_oid_to_py_str(oid);
Py_hash_t ret = PyObject_Hash(py_oid);
Py_DECREF(py_oid);
return ret;
}

PyObject *
Object_richcompare(PyObject *o1, PyObject *o2, int op)
{
PyObject *res;
Object *obj1;
Object *obj2;

if (!PyObject_TypeCheck(o2, &ObjectType)) {
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}

switch (op) {
case Py_NE:
obj1 = (Object *) o1;
obj2 = (Object *) o2;
if (git_oid_equal(git_object_id(obj1->obj), git_object_id(obj2->obj))) {
res = Py_False;
} else {
res = Py_True;
}
break;
case Py_EQ:
obj1 = (Object *) o1;
obj2 = (Object *) o2;
if (git_oid_equal(git_object_id(obj1->obj), git_object_id(obj2->obj))) {
res = Py_True;
} else {
res = Py_False;
}
break;
case Py_LT:
case Py_LE:
case Py_GT:
case Py_GE:
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
default:
PyErr_Format(PyExc_RuntimeError, "Unexpected '%d' op", op);
return NULL;
}

Py_INCREF(res);
return res;
}

PyGetSetDef Object_getseters[] = {
GETTER(Object, oid),
GETTER(Object, id),
Expand Down Expand Up @@ -211,7 +269,7 @@ PyTypeObject ObjectType = {
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
(hashfunc)Object_hash, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
Expand All @@ -221,7 +279,7 @@ PyTypeObject ObjectType = {
Object__doc__, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
Object_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
Expand Down
40 changes: 40 additions & 0 deletions test/test_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,46 @@

class ObjectTest(utils.RepoTestCase):

def test_equality(self):
# get a commit object twice and see if it equals itself
commit_id = self.repo.lookup_reference('refs/heads/master').target
commit_a = self.repo[commit_id]
commit_b = self.repo[commit_id]

assert commit_a is not commit_b
assert commit_a == commit_b
assert not(commit_a != commit_b)

def test_hashing(self):
# get a commit object twice and compare hashes
commit_id = self.repo.lookup_reference('refs/heads/master').target
commit_a = self.repo[commit_id]
commit_b = self.repo[commit_id]

assert hash(commit_a)

assert commit_a is not commit_b
assert commit_a == commit_b
# if the commits are equal then their hash *must* be equal
# but different objects can have the same commit
assert hash(commit_a) == hash(commit_b)

# sanity check that python container types work as expected
s = set()
s.add(commit_a)
s.add(commit_b)
assert len(s) == 1
assert commit_a in s
assert commit_b in s

d = {}
d[commit_a] = True
assert commit_b in d
assert d[commit_b]

l = [commit_a]
assert commit_b in l

def test_peel_commit(self):
# start by looking up the commit
commit_id = self.repo.lookup_reference('refs/heads/master').target
Expand Down