From dccb5d7932425444321a894fd3d9178d513d42e3 Mon Sep 17 00:00:00 2001 From: XTao Date: Mon, 20 May 2013 16:43:35 +0800 Subject: [PATCH 01/49] Add libgit2 dir. --- .gitmodules | 3 +++ libgit2 | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 libgit2 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..e4a5fcc45 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "libgit2"] + path = libgit2 + url = git://github.com/libgit2/libgit2.git diff --git a/libgit2 b/libgit2 new file mode 160000 index 000000000..5b3d52ce3 --- /dev/null +++ b/libgit2 @@ -0,0 +1 @@ +Subproject commit 5b3d52ce37100c1d63229d195041fac3e6f89d28 From ec976a3e1c88815cceb79b7e00d8d21fdced8661 Mon Sep 17 00:00:00 2001 From: XTao Date: Mon, 20 May 2013 17:26:46 +0800 Subject: [PATCH 02/49] Update libgit2 --- .gitmodules | 4 ++-- libgit2 | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) delete mode 160000 libgit2 diff --git a/.gitmodules b/.gitmodules index e4a5fcc45..ca6a48f0b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "libgit2"] - path = libgit2 +[submodule "vendor/libgit2"] + path = vendor/libgit2 url = git://github.com/libgit2/libgit2.git diff --git a/libgit2 b/libgit2 deleted file mode 160000 index 5b3d52ce3..000000000 --- a/libgit2 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5b3d52ce37100c1d63229d195041fac3e6f89d28 From 29370a820eeab1f7323785105868769cd789903c Mon Sep 17 00:00:00 2001 From: XTao Date: Mon, 20 May 2013 17:35:00 +0800 Subject: [PATCH 03/49] Add support for libgit2 embed. --- setup.py | 30 ++++++++++++++++++++++++------ vendor/libgit2 | 1 + 2 files changed, 25 insertions(+), 6 deletions(-) create mode 160000 vendor/libgit2 diff --git a/setup.py b/setup.py index e38ebec7a..ecaa5ef83 100644 --- a/setup.py +++ b/setup.py @@ -32,6 +32,7 @@ import codecs import os +import shutil from subprocess import Popen, PIPE import sys from distutils.core import setup, Extension, Command @@ -54,16 +55,33 @@ # Use environment variable LIBGIT2 to set your own libgit2 configuration. libgit2_path = os.getenv("LIBGIT2") -if libgit2_path is None: +if libgit2_path: + libgit2_bin = os.path.join(libgit2_path, 'bin') + libgit2_include = os.path.join(libgit2_path, 'include') + libgit2_lib = os.getenv('LIBGIT2_LIB', os.path.join(libgit2_path, 'lib')) +else: if os.name == 'nt': program_files = os.getenv("ProgramFiles") libgit2_path = '%s\libgit2' % program_files else: - libgit2_path = '/usr/local' + # use libgit2 embed + cwd = os.path.dirname(os.path.realpath(__file__)) + libgit2_dir = os.path.join(cwd, 'vendor', 'libgit2') + libgit2_lib_path = cwd + "/libgit2_embed.a" + if not os.path.isfile(libgit2_lib_path): + os.chdir(libgit2_dir) + print(os.getcwd()) + popen = Popen(['make', '-f', 'Makefile.embed'], stdout=PIPE, stderr=PIPE) + stdoutdata, stderrdata = popen.communicate() + if popen.returncode != 0: + print(stderrdata) + sys.exit() + shutil.copy('libgit2.a', libgit2_lib_path) + os.chdir(cwd) + libgit2_bin = '' + libgit2_include = os.path.join(libgit2_dir, 'include') + libgit2_lib = cwd -libgit2_bin = os.path.join(libgit2_path, 'bin') -libgit2_include = os.path.join(libgit2_path, 'include') -libgit2_lib = os.getenv('LIBGIT2_LIB', os.path.join(libgit2_path, 'lib')) pygit2_exts = [os.path.join('src', name) for name in os.listdir('src') if name.endswith('.c')] @@ -188,6 +206,6 @@ def get_file_list(self): Extension('_pygit2', pygit2_exts, include_dirs=[libgit2_include, 'include'], library_dirs=[libgit2_lib], - libraries=['git2']), + libraries=['git2', 'git2_embed']), ], cmdclass=cmdclass) diff --git a/vendor/libgit2 b/vendor/libgit2 new file mode 160000 index 000000000..a50086d17 --- /dev/null +++ b/vendor/libgit2 @@ -0,0 +1 @@ +Subproject commit a50086d174658914d4d6462afbc83b02825b1f5b From 657ec1760b283e0c86d48af9f65fddb253519caa Mon Sep 17 00:00:00 2001 From: XTao Date: Mon, 20 May 2013 17:46:52 +0800 Subject: [PATCH 04/49] Add submodule init to setup.py --- setup.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ecaa5ef83..429429b20 100644 --- a/setup.py +++ b/setup.py @@ -52,6 +52,10 @@ else: u = str +popen = Popen(['git', 'submodule', 'update', '--init'], stdout=PIPE, stderr=PIPE) +if popen.returncode != 0: + print(stderrdata) + sys.exit() # Use environment variable LIBGIT2 to set your own libgit2 configuration. libgit2_path = os.getenv("LIBGIT2") @@ -70,7 +74,6 @@ libgit2_lib_path = cwd + "/libgit2_embed.a" if not os.path.isfile(libgit2_lib_path): os.chdir(libgit2_dir) - print(os.getcwd()) popen = Popen(['make', '-f', 'Makefile.embed'], stdout=PIPE, stderr=PIPE) stdoutdata, stderrdata = popen.communicate() if popen.returncode != 0: From 79c45c2d4b3215ca76981deae38480945afc9a38 Mon Sep 17 00:00:00 2001 From: XTao Date: Tue, 21 May 2013 00:55:53 +0800 Subject: [PATCH 05/49] Add diff options: context_lines and paths. --- src/tree.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/tree.c b/src/tree.c index a3ac026f5..894776f80 100644 --- a/src/tree.c +++ b/src/tree.c @@ -282,6 +282,8 @@ PyDoc_STRVAR(Tree_diff__doc__, " If not given compare diff against working dir. Possible valid\n" " arguments are instances of Tree or Index.\n" "\n" + "context_lines\n" + "\n" "flags\n" " TODO"); @@ -294,15 +296,34 @@ Tree_diff(Tree *self, PyObject *args, PyObject *kwds) git_index* index; git_repository *repo; int err, empty_tree = 0; - char *keywords[] = {"obj", "flags", "empty_tree", NULL}; + char *keywords[] = {"obj", "flags", "empty_tree", "context_lines", "paths", NULL}; Diff *py_diff; PyObject *py_obj = NULL; + PyObject *py_paths = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oii", keywords, - &py_obj, &opts.flags, &empty_tree)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OiiiO", keywords, + &py_obj, &opts.flags, &empty_tree, + &opts.context_lines, &py_paths)) return NULL; + if (py_paths != NULL) { + if (!PyObject_TypeCheck(py_paths, &PyList_Type)) { + PyErr_SetObject(PyExc_TypeError, py_paths); + return NULL; + } + int i, paths_length = 0; + PyObject *py_path = NULL; + paths_length = PyList_Size(py_paths); + opts.pathspec.count = paths_length; + opts.pathspec.strings = (char **) PyMem_Malloc(paths_length * sizeof (char *)); + for (i = 0; i < paths_length; i++) { + py_path = PyList_GetItem(py_paths, i); + opts.pathspec.strings[i] = PyString_AsString(py_path); + } + + } + repo = git_tree_owner(self->tree); if (py_obj == NULL) { if (empty_tree > 0) @@ -319,10 +340,15 @@ Tree_diff(Tree *self, PyObject *args, PyObject *kwds) err = git_diff_tree_to_index(&diff, repo, self->tree, index, &opts); } else { + if (py_paths != NULL) + PyMem_Free(opts.pathspec.strings); PyErr_SetObject(PyExc_TypeError, py_obj); return NULL; } + if (py_paths != NULL) + PyMem_Free(opts.pathspec.strings); + if (err < 0) return Error_set(err); From 43b32ac9c577ef15bac0e3e0bb6bf12ea66a8190 Mon Sep 17 00:00:00 2001 From: XTao Date: Tue, 21 May 2013 00:59:23 +0800 Subject: [PATCH 06/49] Add type check for diff option paths. --- src/tree.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/tree.c b/src/tree.c index 894776f80..ceeb1403e 100644 --- a/src/tree.c +++ b/src/tree.c @@ -315,6 +315,13 @@ Tree_diff(Tree *self, PyObject *args, PyObject *kwds) int i, paths_length = 0; PyObject *py_path = NULL; paths_length = PyList_Size(py_paths); + for (i = 0; i < paths_length; i++) { + py_path = PyList_GetItem(py_paths, i); + if (!PyObject_TypeCheck(py_path, &PyString_Type)) { + PyErr_SetObject(PyExc_TypeError, py_path); + return NULL; + } + } opts.pathspec.count = paths_length; opts.pathspec.strings = (char **) PyMem_Malloc(paths_length * sizeof (char *)); for (i = 0; i < paths_length; i++) { From e7c05db715782b9b7b3da3de4cd39a634d198887 Mon Sep 17 00:00:00 2001 From: XTao Date: Tue, 21 May 2013 10:31:33 +0800 Subject: [PATCH 07/49] Fix setup.py for popen --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 429429b20..9c3cf73e6 100644 --- a/setup.py +++ b/setup.py @@ -53,6 +53,7 @@ u = str popen = Popen(['git', 'submodule', 'update', '--init'], stdout=PIPE, stderr=PIPE) +stdoutdata, stderrdata = popen.communicate() if popen.returncode != 0: print(stderrdata) sys.exit() From 05967d8e69c7fa18f5fdef7f3511725890493d6b Mon Sep 17 00:00:00 2001 From: XTao Date: Tue, 21 May 2013 11:41:05 +0800 Subject: [PATCH 08/49] Fix linking git2 order. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9c3cf73e6..c308159d7 100644 --- a/setup.py +++ b/setup.py @@ -210,6 +210,6 @@ def get_file_list(self): Extension('_pygit2', pygit2_exts, include_dirs=[libgit2_include, 'include'], library_dirs=[libgit2_lib], - libraries=['git2', 'git2_embed']), + libraries=['git2_embed', 'git2']), ], cmdclass=cmdclass) From f859750e33d941581b0153ea731eb9dfecfcd19b Mon Sep 17 00:00:00 2001 From: xutao Date: Mon, 27 May 2013 12:01:17 +0800 Subject: [PATCH 09/49] Update libgit2 repo url to code. --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index ca6a48f0b..fd0c6d2bc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "vendor/libgit2"] path = vendor/libgit2 - url = git://github.com/libgit2/libgit2.git + url = http://code.dapps.douban.com/xutao/libgit2.git From f4b93540319bc6e0db30515c7e6dec04b14e9ef0 Mon Sep 17 00:00:00 2001 From: xutao Date: Tue, 28 May 2013 11:38:05 +0800 Subject: [PATCH 10/49] Add additions and deletions for patch. --- src/diff.c | 7 ++++++- src/types.h | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/diff.c b/src/diff.c index 8e87c70cf..c45f587d6 100644 --- a/src/diff.c +++ b/src/diff.c @@ -48,7 +48,7 @@ diff_get_patch_byindex(git_diff_list* list, size_t idx) const git_diff_delta* delta; const git_diff_range* range; git_diff_patch* patch = NULL; - size_t i, j, hunk_amounts, lines_in_hunk, line_len, header_len; + size_t i, j, hunk_amounts, lines_in_hunk, line_len, header_len, additions, deletions; const char* line, *header; int err; Hunk *py_hunk = NULL; @@ -67,6 +67,9 @@ diff_get_patch_byindex(git_diff_list* list, size_t idx) py_patch->old_oid = git_oid_allocfmt(&delta->old_file.oid); py_patch->new_oid = git_oid_allocfmt(&delta->new_file.oid); + git_diff_patch_line_stats(NULL, &additions, &deletions, patch); + py_patch->additions = additions; + py_patch->deletions = deletions; hunk_amounts = git_diff_patch_num_hunks(patch); py_patch->hunks = PyList_New(hunk_amounts); @@ -129,6 +132,8 @@ PyMemberDef Patch_members[] = { MEMBER(Patch, status, T_CHAR, "status"), MEMBER(Patch, similarity, T_INT, "similarity"), MEMBER(Patch, hunks, T_OBJECT, "hunks"), + MEMBER(Patch, additions, T_INT, "additions"), + MEMBER(Patch, deletions, T_INT, "deletions"), {NULL} }; diff --git a/src/types.h b/src/types.h index a4ac5eca1..c9d558c28 100644 --- a/src/types.h +++ b/src/types.h @@ -113,6 +113,8 @@ typedef struct { char* new_oid; char status; unsigned similarity; + unsigned additions; + unsigned deletions; } Patch; typedef struct { From 0a3f1126f090c629019dd42571f3b7e2c0b189f0 Mon Sep 17 00:00:00 2001 From: fzy_intern Date: Wed, 29 May 2013 19:53:40 +0800 Subject: [PATCH 11/49] Add is_repository method. --- src/pygit2.c | 24 ++++++++++++++++++++++++ test/test_repository.py | 13 +++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/pygit2.c b/src/pygit2.c index 836289106..e4bf017f6 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -64,7 +64,30 @@ extern PyTypeObject RemoteType; extern PyTypeObject NoteType; extern PyTypeObject NoteIterType; +PyDoc_STRVAR(is_repository__doc__, + "is_repository(path) -> Boolean\n" + "\n" + "Test if the path is a git repository."); + +PyObject * +is_repository(PyObject *self, PyObject *args) { + char* path = NULL; + int err; + git_repository *repo; + + if (!PyArg_ParseTuple(args, "s", &path)) { + return NULL; + } + + err = git_repository_open(&repo, path); + + if (err < 0) + Py_RETURN_FALSE; + + git_repository_free(repo); + Py_RETURN_TRUE; +} PyDoc_STRVAR(init_repository__doc__, "init_repository(path, bare)\n" @@ -241,6 +264,7 @@ PyMethodDef module_methods[] = { discover_repository__doc__}, {"hashfile", hashfile, METH_VARARGS, hashfile__doc__}, {"hash", hash, METH_VARARGS, hash__doc__}, + {"is_repository", is_repository, METH_VARARGS, is_repository__doc__}, {NULL} }; diff --git a/test/test_repository.py b/test/test_repository.py index d6e762e4e..7ddc5b2a9 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -42,7 +42,7 @@ from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT from pygit2 import ( init_repository, clone_repository, discover_repository, - Reference, hashfile + Reference, hashfile, is_repository ) from pygit2 import Oid import pygit2 @@ -171,7 +171,6 @@ def test_hashfile(self): written_sha1 = self.repo.create_blob(data) self.assertEqual(hashed_sha1, written_sha1) - class RepositoryTest_II(utils.RepoTestCase): def test_is_empty(self): @@ -241,6 +240,15 @@ def test_merge_base(self): self.assertEqual(commit.hex, 'acecd5ea2924a4b900e7e149496e1f4b57976e51') +class IsRepositoryTest(utils.RepoTestCase): + + def test_is_repository(self): + directory = realpath(self.repo.workdir) + + self.assertRaises(TypeError, is_repository) + self.assertRaises(TypeError, is_repository, '', '') + self.assertFalse(is_repository("sadgasdfasdfasfdasfasdfsafasdfasdf")) + self.assertTrue(is_repository(directory)) class NewRepositoryTest(utils.NoRepoTestCase): @@ -285,6 +293,7 @@ def test_discover_repo(self): self.assertEqual(repo.path, discover_repository(subdir)) + class EmptyRepositoryTest(utils.EmptyRepoTestCase): def test_is_empty(self): From 290b49330bee940b6fe9690a1061e10419382c60 Mon Sep 17 00:00:00 2001 From: xutao Date: Mon, 3 Jun 2013 11:59:16 +0800 Subject: [PATCH 12/49] Remove double linking libgit2. --- setup.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c308159d7..793509893 100644 --- a/setup.py +++ b/setup.py @@ -52,12 +52,14 @@ else: u = str +print('checkout libgit2 source') popen = Popen(['git', 'submodule', 'update', '--init'], stdout=PIPE, stderr=PIPE) stdoutdata, stderrdata = popen.communicate() if popen.returncode != 0: print(stderrdata) sys.exit() +pygit2_libs = ['git2'] # Use environment variable LIBGIT2 to set your own libgit2 configuration. libgit2_path = os.getenv("LIBGIT2") if libgit2_path: @@ -73,8 +75,11 @@ cwd = os.path.dirname(os.path.realpath(__file__)) libgit2_dir = os.path.join(cwd, 'vendor', 'libgit2') libgit2_lib_path = cwd + "/libgit2_embed.a" + if os.path.isfile(libgit2_lib_path): + os.remove(libgit2_lib_path) if not os.path.isfile(libgit2_lib_path): os.chdir(libgit2_dir) + print('build libgit2 embed') popen = Popen(['make', '-f', 'Makefile.embed'], stdout=PIPE, stderr=PIPE) stdoutdata, stderrdata = popen.communicate() if popen.returncode != 0: @@ -85,6 +90,7 @@ libgit2_bin = '' libgit2_include = os.path.join(libgit2_dir, 'include') libgit2_lib = cwd + pygit2_libs = ['git2_embed'] pygit2_exts = [os.path.join('src', name) for name in os.listdir('src') if name.endswith('.c')] @@ -210,6 +216,6 @@ def get_file_list(self): Extension('_pygit2', pygit2_exts, include_dirs=[libgit2_include, 'include'], library_dirs=[libgit2_lib], - libraries=['git2_embed', 'git2']), + libraries=pygit2_libs), ], cmdclass=cmdclass) From 8548d60213c2c871ec02be70c2ad466cbce60547 Mon Sep 17 00:00:00 2001 From: xutao Date: Sun, 9 Jun 2013 10:29:28 +0800 Subject: [PATCH 13/49] Bump libgit2. --- vendor/libgit2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/libgit2 b/vendor/libgit2 index a50086d17..60750fc43 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit a50086d174658914d4d6462afbc83b02825b1f5b +Subproject commit 60750fc43e761b57ade6373f3ede14c5187814dc From 74033c0f72410c6abcf9a50261d0484adbc88c0e Mon Sep 17 00:00:00 2001 From: xutao Date: Sun, 9 Jun 2013 20:38:45 +0800 Subject: [PATCH 14/49] Add support libgit2 development. --- src/config.c | 4 +-- src/pygit2.c | 30 ++++++++++----------- src/remote.c | 36 ++++++++++++++++---------- src/repository.c | 2 +- test/data/testrepo.git/refs/heads/test | 1 + test/test_config.py | 3 ++- test/test_refs.py | 4 --- test/test_remote.py | 15 ++++++++--- test/test_repository.py | 6 ++--- 9 files changed, 57 insertions(+), 44 deletions(-) create mode 100644 test/data/testrepo.git/refs/heads/test diff --git a/src/config.c b/src/config.c index 6fa58d986..22436b0c6 100644 --- a/src/config.c +++ b/src/config.c @@ -315,10 +315,10 @@ Config_add_file(Config *self, PyObject *args, PyObject *kwds) char *keywords[] = {"path", "level", "force", NULL}; int err; char *path; - unsigned int level = 0; + int level = -1; int force = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|Ii", keywords, + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|ii", keywords, &path, &level, &force)) return NULL; diff --git a/src/pygit2.c b/src/pygit2.c index e4bf017f6..a659bba56 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -64,7 +64,7 @@ extern PyTypeObject RemoteType; extern PyTypeObject NoteType; extern PyTypeObject NoteIterType; -PyDoc_STRVAR(is_repository__doc__, +PyDoc_STRVAR(is_repository__doc__, "is_repository(path) -> Boolean\n" "\n" "Test if the path is a git repository."); @@ -75,7 +75,7 @@ is_repository(PyObject *self, PyObject *args) { int err; git_repository *repo; - + if (!PyArg_ParseTuple(args, "s", &path)) { return NULL; } @@ -158,26 +158,26 @@ clone_repository(PyObject *self, PyObject *args) { unsigned int bare; const char *remote_name, *push_url, *fetch_spec; const char *push_spec, *checkout_branch; + const git_error *git_err; int err; + git_clone_options opts = GIT_CLONE_OPTIONS_INIT; - if (!PyArg_ParseTuple(args, "zzIzzzzz", - &url, &path, &bare, &remote_name, &push_url, + if (!PyArg_ParseTuple(args, "zzIzzzzz", + &url, &path, &bare, &remote_name, &push_url, &fetch_spec, &push_spec, &checkout_branch)) return NULL; - git_clone_options opts = { - .version=1, - .bare=bare, - .remote_name=remote_name, - .pushurl=push_url, - .fetch_spec=fetch_spec, - .push_spec=push_spec, - .checkout_branch=checkout_branch - }; + opts.bare = bare; + opts.remote_name = remote_name; + opts.pushurl = push_url; + opts.fetch_spec = fetch_spec; + opts.push_spec = push_spec; + opts.checkout_branch = checkout_branch; err = git_clone(&repo, url, path, &opts); - if (err < 0) + if (err < 0) { return Error_set_str(err, path); + } git_repository_free(repo); Py_RETURN_NONE; @@ -264,7 +264,7 @@ PyMethodDef module_methods[] = { discover_repository__doc__}, {"hashfile", hashfile, METH_VARARGS, hashfile__doc__}, {"hash", hash, METH_VARARGS, hash__doc__}, - {"is_repository", is_repository, METH_VARARGS, is_repository__doc__}, + {"is_repository", is_repository, METH_VARARGS, is_repository__doc__}, {NULL} }; diff --git a/src/remote.c b/src/remote.c index df238a3c6..207d8452d 100644 --- a/src/remote.c +++ b/src/remote.c @@ -134,21 +134,29 @@ PyDoc_STRVAR(Remote_fetchspec__doc__, PyObject * Remote_fetchspec__get__(Remote *self) { - PyObject* py_tuple = NULL; - const git_refspec * refspec; - - refspec = git_remote_fetchspec(self->remote); - if (refspec != NULL) { - py_tuple = Py_BuildValue( - "(ss)", - git_refspec_src(refspec), - git_refspec_dst(refspec) - ); - - return py_tuple; + PyObject *py_tuple = NULL; + PyObject *py_string = NULL; + git_strarray refspecs; + int err; + int index; + + err = git_remote_get_fetch_refspecs(&refspecs, self->remote); + if (err < 0) + return Error_set(err); + + py_tuple = PyTuple_New(refspecs.count); + for (index = 0; index < refspecs.count; index++) { + py_string = to_path(refspecs.strings[index]); + if (py_string == NULL) { + Py_CLEAR(py_tuple); + goto out; + } + PyTuple_SET_ITEM(py_tuple, index, py_string); } - return Error_set(GIT_ENOTFOUND); +out: + git_strarray_free(&refspecs); + return py_tuple; } int @@ -166,7 +174,7 @@ Remote_fetchspec__set__(Remote *self, PyObject* py_tuple) buf = (char*) calloc(length, sizeof(char)); if (buf != NULL) { sprintf(buf, "+%s:%s", src, dst); - err = git_remote_set_fetchspec(self->remote, buf); + err = git_remote_add_fetch(self->remote, buf); free(buf); if (err == GIT_OK) diff --git a/src/repository.c b/src/repository.c index 9f9893a2b..53e428b5f 100644 --- a/src/repository.c +++ b/src/repository.c @@ -829,7 +829,7 @@ Repository_listall_references(Repository *self, PyObject *args) return NULL; /* 2- Get the C result */ - err = git_reference_list(&c_result, self->repo, list_flags); + err = git_reference_list(&c_result, self->repo); if (err < 0) return Error_set(err); diff --git a/test/data/testrepo.git/refs/heads/test b/test/data/testrepo.git/refs/heads/test new file mode 100644 index 000000000..436950fb2 --- /dev/null +++ b/test/data/testrepo.git/refs/heads/test @@ -0,0 +1 @@ +784855caf26449a1914d2cf62d12b9374d76ae78 diff --git a/test/test_config.py b/test/test_config.py index 9c7e13d3d..3e3ba0256 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -112,9 +112,10 @@ def test_read(self): new_file = open(config_filename, "w") new_file.write("[this]\n\tthat = foobar\n\tthat = foobeer\n") + new_file.write("[something \"other\"]\n\there = false") new_file.close() - config.add_file(config_filename, 0) + config.add_file(config_filename, 6) self.assertTrue('this.that' in config) self.assertEqual(len(config.get_multivar('this.that')), 2) l = config.get_multivar('this.that', 'bar') diff --git a/test/test_refs.py b/test/test_refs.py index 3c2af0c21..66a30fa80 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -54,10 +54,6 @@ def test_list_all_references(self): ['refs/heads/i18n', 'refs/heads/master', 'refs/tags/version1']) - # Now we list only the symbolic references - self.assertEqual(repo.listall_references(GIT_REF_SYMBOLIC), - ('refs/tags/version1', )) - def test_head(self): head = self.repo.head self.assertEqual(LAST_COMMIT, self.repo[head.target].hex) diff --git a/test/test_remote.py b/test/test_remote.py index 6e5a150af..b630f6093 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -77,13 +77,20 @@ def test_remote_set_url(self): def test_remote_fetchspec(self): remote = self.repo.remotes[0] - self.assertEqual(REMOTE_FETCHSPEC_SRC, remote.fetchspec[0]) - self.assertEqual(REMOTE_FETCHSPEC_DST, remote.fetchspec[1]) + # FIXME: create spec class + fetchspec = remote.fetchspec[0] + spec = '+%s:%s' % (REMOTE_FETCHSPEC_SRC, REMOTE_FETCHSPEC_DST) + self.assertEqual(spec, fetchspec) + #self.assertEqual(REMOTE_FETCHSPEC_SRC, remote.fetchspec[0]) + #self.assertEqual(REMOTE_FETCHSPEC_DST, remote.fetchspec[1]) new_fetchspec = ('refs/foo/*', 'refs/remotes/foo/*') remote.fetchspec = new_fetchspec - self.assertEqual(new_fetchspec[0], remote.fetchspec[0]) - self.assertEqual(new_fetchspec[1], remote.fetchspec[1]) + spec = '+%s:%s' % new_fetchspec + fetchspec = remote.fetchspec[1] + self.assertEqual(spec, fetchspec) + #self.assertEqual(new_fetchspec[0], remote.fetchspec[0]) + #self.assertEqual(new_fetchspec[1], remote.fetchspec[1]) def test_remote_list(self): diff --git a/test/test_repository.py b/test/test_repository.py index 7ddc5b2a9..f8903488b 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -241,7 +241,7 @@ def test_merge_base(self): 'acecd5ea2924a4b900e7e149496e1f4b57976e51') class IsRepositoryTest(utils.RepoTestCase): - + def test_is_repository(self): directory = realpath(self.repo.workdir) @@ -341,7 +341,7 @@ def test_clone_push_url(self): def test_clone_fetch_spec(self): repo_path = "./test/data/testrepo.git/" repo = clone_repository( - repo_path, self._temp_dir, fetch_spec="refs/heads/test" + repo_path, self._temp_dir, fetch_spec="+refs/heads/master:refs/heads/test" ) self.assertFalse(repo.is_empty) # FIXME: When pygit2 retrieve the fetchspec we passed to git clone. @@ -352,7 +352,7 @@ def test_clone_fetch_spec(self): def test_clone_push_spec(self): repo_path = "./test/data/testrepo.git/" repo = clone_repository( - repo_path, self._temp_dir, push_spec="refs/heads/test" + repo_path, self._temp_dir, push_spec="+refs/heads/master:refs/heads/test" ) self.assertFalse(repo.is_empty) # FIXME: When pygit2 supports retrieving the pushspec parameter, From 241e160958955c70501567e985cbfacc070f91c3 Mon Sep 17 00:00:00 2001 From: xutao Date: Mon, 10 Jun 2013 13:39:24 +0800 Subject: [PATCH 15/49] Fix memory leak. --- src/treebuilder.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/treebuilder.c b/src/treebuilder.c index 1724f14ee..94036a82f 100644 --- a/src/treebuilder.c +++ b/src/treebuilder.c @@ -108,6 +108,7 @@ TreeBuilder_get(TreeBuilder *self, PyObject *py_filename) return NULL; entry = git_treebuilder_get(self->bld, filename); + free(filename); if (entry == NULL) Py_RETURN_NONE; From fb72bfc90e8f22c5e0bbb47c3066efe8f01feadf Mon Sep 17 00:00:00 2001 From: xutao Date: Fri, 19 Jul 2013 23:22:40 +0800 Subject: [PATCH 16/49] Bump libgit2. --- vendor/libgit2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/libgit2 b/vendor/libgit2 index 60750fc43..275d8d55b 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit 60750fc43e761b57ade6373f3ede14c5187814dc +Subproject commit 275d8d55b29c83bd8c165f5eeaafd76c7e0d966b From 6a315176a19badcd67d571a04bdcc7765998de66 Mon Sep 17 00:00:00 2001 From: xutao Date: Sun, 21 Jul 2013 09:23:06 +0800 Subject: [PATCH 17/49] Add some compatibility. --- src/remote.c | 37 +++++++++++++++++++++++-------------- src/tree.c | 15 --------------- test/test_branch.py | 2 +- 3 files changed, 24 insertions(+), 30 deletions(-) diff --git a/src/remote.c b/src/remote.c index 215c6cd28..51e6e50e2 100644 --- a/src/remote.c +++ b/src/remote.c @@ -134,23 +134,32 @@ PyDoc_STRVAR(Remote_fetchspec__doc__, PyObject * Remote_fetchspec__get__(Remote *self) { - PyObject* py_tuple = NULL; - const git_refspec * refspec; - - refspec = git_remote_fetchspec(self->remote); - if (refspec != NULL) { - py_tuple = Py_BuildValue( - "(ss)", - git_refspec_src(refspec), - git_refspec_dst(refspec) - ); - - return py_tuple; + PyObject *py_tuple = NULL; + PyObject *py_string = NULL; + git_strarray refspecs; + int err; + int index; + + err = git_remote_get_fetch_refspecs(&refspecs, self->remote); + if (err < 0) + return Error_set(err); + + py_tuple = PyTuple_New(refspecs.count); + for (index = 0; index < refspecs.count; index++) { + py_string = to_path(refspecs.strings[index]); + if (py_string == NULL) { + Py_CLEAR(py_tuple); + goto out; + } + PyTuple_SET_ITEM(py_tuple, index, py_string); } - return Error_set(GIT_ENOTFOUND); +out: + git_strarray_free(&refspecs); + return py_tuple; } + int Remote_fetchspec__set__(Remote *self, PyObject* py_tuple) { @@ -166,7 +175,7 @@ Remote_fetchspec__set__(Remote *self, PyObject* py_tuple) buf = (char*) calloc(length, sizeof(char)); if (buf != NULL) { sprintf(buf, "+%s:%s", src, dst); - err = git_remote_set_fetchspec(self->remote, buf); + err = git_remote_add_fetch(self->remote, buf); free(buf); if (err == GIT_OK) diff --git a/src/tree.c b/src/tree.c index 4dfe855c5..367bb1b42 100644 --- a/src/tree.c +++ b/src/tree.c @@ -347,21 +347,6 @@ Tree_diff(Tree *self, PyObject *args, PyObject *kwds) index = ((Index *)py_obj)->index; err = git_diff_tree_to_index(&diff, repo, self->tree, index, &opts); - } else { - if (py_paths != NULL) - PyMem_Free(opts.pathspec.strings); - PyErr_SetObject(PyExc_TypeError, py_obj); - else - err = git_diff_tree_to_workdir(&diff, repo, self->tree, &opts); - - } else if (PyObject_TypeCheck(py_obj, &TreeType)) { - tree = ((Tree *)py_obj)->tree; - err = git_diff_tree_to_tree(&diff, repo, self->tree, tree, &opts); - - } else if (PyObject_TypeCheck(py_obj, &IndexType)) { - index = ((Index *)py_obj)->index; - err = git_diff_tree_to_index(&diff, repo, self->tree, index, &opts); - } else { if (py_paths != NULL) PyMem_Free(opts.pathspec.strings); diff --git a/test/test_branch.py b/test/test_branch.py index 8dc62cb1a..a9717b27c 100644 --- a/test/test_branch.py +++ b/test/test_branch.py @@ -138,7 +138,7 @@ def test_lookup_branch_remote(self): def test_listall_branches(self): branches = sorted(self.repo.listall_branches(pygit2.GIT_BRANCH_REMOTE)) - self.assertEqual(branches, ['origin/master']) + self.assertEqual(branches, ['origin/master', 'origin/test']) def test_branch_remote_name(self): self.repo.remotes[0].fetch() From 41fc0ba47d11c2cb666ad505fac1d4ec6d359abf Mon Sep 17 00:00:00 2001 From: xutao Date: Sat, 27 Jul 2013 21:33:38 +0800 Subject: [PATCH 18/49] Add is_changed for commit. --- src/commit.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 147 insertions(+), 1 deletion(-) diff --git a/src/commit.c b/src/commit.c index 74b1ecc16..ebd3c3a6a 100644 --- a/src/commit.c +++ b/src/commit.c @@ -205,6 +205,152 @@ PyGetSetDef Commit_getseters[] = { {NULL} }; +PyDoc_STRVAR(Commit_is_changed__doc__, + "is_changed(paths, [flags, no_merges]) -> Diff\n" + "\n" + "check the paths are changed in current commit\n" + "\n" + "Arguments:\n" + "\n" + "paths: file path list.\n" + "\n" + "no_merges: boolean, escape merge commit or not.\n" + "\n" + "flags: a GIT_DIFF_* constant.\n" + "\n"); + +PyObject * +Commit_is_changed(Commit *self, PyObject *args, PyObject *kwds) +{ + const git_oid *parent_oid; + git_commit *parent; + git_tree* tree = NULL; + git_tree* parent_tree = NULL; + git_diff_list *diff; + git_repository *repo; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + unsigned int i; + unsigned int parent_count; + int err; + int ndeltas; + char *keywords[] = {"paths", "flags", "no_merges", NULL}; + Repository *py_repo; + PyObject *py_paths = NULL; + PyObject *py_no_merges = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iO", keywords, + &py_paths, &opts.flags, &py_no_merges)) + return NULL; + + if (!PyObject_TypeCheck(py_paths, &PyList_Type)) { + PyErr_SetObject(PyExc_TypeError, py_paths); + return NULL; + } + + if (py_no_merges != NULL && + (py_no_merges != Py_None || + !PyObject_TypeCheck(py_no_merges, &PyBool_Type)) + ) { + PyErr_SetObject(PyExc_TypeError, py_no_merges); + return NULL; + } + + int paths_length = 0; + PyObject *py_path = NULL; + paths_length = PyList_Size(py_paths); + if (paths_length <= 0) { + PyErr_SetObject(PyExc_ValueError, py_path); + return NULL; + } + for (i = 0; i < paths_length; i++) { + py_path = PyList_GetItem(py_paths, i); + if (!PyObject_TypeCheck(py_path, &PyString_Type)) { + PyErr_SetObject(PyExc_TypeError, py_path); + return NULL; + } + } + opts.pathspec.count = paths_length; + opts.pathspec.strings = (char **) PyMem_Malloc(paths_length * sizeof (char *)); + for (i = 0; i < paths_length; i++) { + py_path = PyList_GetItem(py_paths, i); + opts.pathspec.strings[i] = PyString_AsString(py_path); + } + + py_repo = self->repo; + repo = py_repo->repo; + + parent_count = git_commit_parentcount(self->commit); + + err = git_commit_tree(&tree, self->commit); + if (err == GIT_ENOTFOUND) + Py_RETURN_NONE; + + if (py_no_merges != NULL && + py_no_merges != Py_None && PyObject_IsTrue(py_no_merges)) { + if (parent_count > 1) { + goto exit_false; + } + } + + if (parent_count > 0) { + for (i=0; i < parent_count; i++) { + parent_oid = git_commit_parent_id(self->commit, i); + if (parent_oid == NULL) { + Error_set(GIT_ENOTFOUND); + goto error_null; + } + + err = git_commit_lookup(&parent, py_repo->repo, parent_oid); + if (err < 0) { + return Error_set_oid(err, parent_oid, GIT_OID_HEXSZ); + } + + err = git_commit_tree(&parent_tree, parent); + if (err == GIT_ENOTFOUND) + goto error; + + err = git_diff_tree_to_tree(&diff, repo, parent_tree, tree, &opts); + if (err < 0) + goto error; + + ndeltas = (int)git_diff_num_deltas(diff); + git_diff_list_free(diff); + if (ndeltas > 0) + goto exit_true; + } + goto exit_false; + } + + err = git_diff_tree_to_tree(&diff, repo, tree, NULL, &opts); + if (err < 0) + return Error_set(err); + ndeltas = (int)git_diff_num_deltas(diff); + git_diff_list_free(diff); + if (ndeltas > 0) + goto exit_true; + +exit_false: + if (py_paths != NULL) + PyMem_Free(opts.pathspec.strings); + Py_RETURN_FALSE; +error_null: + if (py_paths != NULL) + PyMem_Free(opts.pathspec.strings); + return NULL; +error: + if (py_paths != NULL) + PyMem_Free(opts.pathspec.strings); + return Error_set(err); +exit_true: + if (py_paths != NULL) + PyMem_Free(opts.pathspec.strings); + Py_RETURN_TRUE; +} + +PyMethodDef Commit_methods[] = { + METHOD(Commit, is_changed, METH_VARARGS|METH_KEYWORDS), + {NULL} +}; PyDoc_STRVAR(Commit__doc__, "Commit objects."); @@ -236,7 +382,7 @@ PyTypeObject CommitType = { 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ - 0, /* tp_methods */ + Commit_methods, /* tp_methods */ 0, /* tp_members */ Commit_getseters, /* tp_getset */ 0, /* tp_base */ From 79c031813225c02d047d39e3b75af2fc94a99b4c Mon Sep 17 00:00:00 2001 From: xutao Date: Sun, 28 Jul 2013 16:59:13 +0800 Subject: [PATCH 19/49] Add unit test for commit.is_changed() --- src/commit.c | 4 ++-- test/test_commit.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/commit.c b/src/commit.c index ebd3c3a6a..9c379ff6f 100644 --- a/src/commit.c +++ b/src/commit.c @@ -248,7 +248,7 @@ Commit_is_changed(Commit *self, PyObject *args, PyObject *kwds) } if (py_no_merges != NULL && - (py_no_merges != Py_None || + (py_no_merges != Py_None && !PyObject_TypeCheck(py_no_merges, &PyBool_Type)) ) { PyErr_SetObject(PyExc_TypeError, py_no_merges); @@ -259,7 +259,7 @@ Commit_is_changed(Commit *self, PyObject *args, PyObject *kwds) PyObject *py_path = NULL; paths_length = PyList_Size(py_paths); if (paths_length <= 0) { - PyErr_SetObject(PyExc_ValueError, py_path); + PyErr_SetObject(PyExc_ValueError, py_paths); return NULL; } for (i = 0; i < paths_length; i++) { diff --git a/test/test_commit.py b/test/test_commit.py index 13acf982e..0bd4fcd93 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -134,6 +134,20 @@ def test_modify_commit(self): self.assertRaises(AttributeError, setattr, commit, 'tree', None) self.assertRaises(AttributeError, setattr, commit, 'parents', None) + def test_is_changed(self): + commit = self.repo['c2792cfa289ae6321ecf2cd5806c2194b0fd070c'] + self.assertRaises(TypeError, commit.is_changed, b'a') + self.assertEqual(commit.is_changed([b'a']), True) + self.assertEqual(commit.is_changed([b'c']), False) + self.assertEqual(commit.is_changed([b'a'], no_merges=False), True) + self.assertEqual(commit.is_changed([b'c'], no_merges=True), False) + + commit = self.repo['f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87'] + self.assertEqual(commit.is_changed([b'lorem']), True) + self.assertEqual(commit.is_changed([b'a']), False) + self.assertEqual(commit.is_changed([b'lorem'], no_merges=True), True) + self.assertEqual(commit.is_changed([b'a'], no_merges=True), False) + if __name__ == '__main__': unittest.main() From ab539b215bb9014cc2b260e32e984e8ae9344ff5 Mon Sep 17 00:00:00 2001 From: xutao Date: Wed, 31 Jul 2013 10:45:42 +0800 Subject: [PATCH 20/49] Refactor commit.is_changed() --- src/commit.c | 195 +++++++++++++++++++++++++++++++------------- test/test_commit.py | 19 +++-- 2 files changed, 151 insertions(+), 63 deletions(-) diff --git a/src/commit.c b/src/commit.c index 9c379ff6f..55b8f7513 100644 --- a/src/commit.c +++ b/src/commit.c @@ -35,6 +35,110 @@ extern PyTypeObject TreeType; +int +compare_delta_file_path(const git_diff_delta *delta, git_diff_options *opts) +{ + unsigned int i; + int res = -1; + int cmp; + int length = opts->pathspec.count; + char **paths = opts->pathspec.strings; + for (i = 0; i < length; i++) { + cmp = strcmp(delta->old_file.path, paths[i]); + if (cmp != 0) + continue; + res = i; + break; + } + return res; +} + +int +get_diff_paths(git_diff_list *diff, git_diff_options *opts, int *indexs) +{ + const git_diff_delta *delta; + unsigned int i; + int ndeltas; + int err; + int index; + int count = 0; + ndeltas = (int)git_diff_num_deltas(diff); + for (i = 0; i < ndeltas; i++) { + err = git_diff_get_patch(NULL, &delta, diff, i); + if (err < 0) + goto cleanup; + index = compare_delta_file_path(delta, opts); + if (index < 0) + continue; + indexs[index] ++; + count ++; + } + return count; +cleanup: + return err; +} + +int +quick_commit_diff_with_parent(git_repository *repo, git_commit *commit, unsigned int index, + git_diff_options *opts, git_diff_list **diff) +{ + const git_oid *parent_oid; + git_commit *parent; + git_tree* parent_tree = NULL; + git_tree* tree = NULL; + int err; + parent_oid = git_commit_parent_id(commit, index); + if (parent_oid == NULL) { + err = GIT_ENOTFOUND; + goto cleanup; + } + + err = git_commit_lookup(&parent, repo, parent_oid); + if (err < 0) + goto cleanup; + + err = git_commit_tree(&parent_tree, parent); + if (err < 0) + goto cleanup_ptree; + + err = git_commit_tree(&tree, commit); + if (err < 0) + goto cleanup_tree; + + err = git_diff_tree_to_tree(diff, repo, parent_tree, tree, opts); + if (err < 0) + goto cleanup_diff; + +cleanup_diff: + git_tree_free(tree); +cleanup_tree: + git_tree_free(parent_tree); +cleanup_ptree: + git_commit_free(parent); +cleanup: + return err; +} + +int +quick_commit_diff(git_repository *repo, git_commit *commit, + git_diff_options *opts, git_diff_list **diff) +{ + git_tree* tree = NULL; + int err; + + err = git_commit_tree(&tree, commit); + if (err < 0) + goto cleanup_tree; + + err = git_diff_tree_to_tree(diff, repo, NULL, tree, opts); + if (err < 0) + goto cleanup_diff; + +cleanup_diff: + git_tree_free(tree); +cleanup_tree: + return err; +} PyDoc_STRVAR(Commit_message_encoding__doc__, "Message encoding."); @@ -205,6 +309,7 @@ PyGetSetDef Commit_getseters[] = { {NULL} }; + PyDoc_STRVAR(Commit_is_changed__doc__, "is_changed(paths, [flags, no_merges]) -> Diff\n" "\n" @@ -236,7 +341,10 @@ Commit_is_changed(Commit *self, PyObject *args, PyObject *kwds) char *keywords[] = {"paths", "flags", "no_merges", NULL}; Repository *py_repo; PyObject *py_paths = NULL; + PyObject *py_diff_paths = NULL; PyObject *py_no_merges = NULL; + int *path_indexs; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iO", keywords, &py_paths, &opts.flags, &py_no_merges)) @@ -271,80 +379,57 @@ Commit_is_changed(Commit *self, PyObject *args, PyObject *kwds) } opts.pathspec.count = paths_length; opts.pathspec.strings = (char **) PyMem_Malloc(paths_length * sizeof (char *)); + path_indexs = (int *)PyMem_Malloc(paths_length * sizeof (int)); for (i = 0; i < paths_length; i++) { py_path = PyList_GetItem(py_paths, i); opts.pathspec.strings[i] = PyString_AsString(py_path); + path_indexs[i] = 0; } py_repo = self->repo; repo = py_repo->repo; - parent_count = git_commit_parentcount(self->commit); - - err = git_commit_tree(&tree, self->commit); - if (err == GIT_ENOTFOUND) - Py_RETURN_NONE; - if (py_no_merges != NULL && py_no_merges != Py_None && PyObject_IsTrue(py_no_merges)) { - if (parent_count > 1) { - goto exit_false; - } + if (parent_count > 1) + goto cleanup_empty; } if (parent_count > 0) { - for (i=0; i < parent_count; i++) { - parent_oid = git_commit_parent_id(self->commit, i); - if (parent_oid == NULL) { - Error_set(GIT_ENOTFOUND); - goto error_null; - } - - err = git_commit_lookup(&parent, py_repo->repo, parent_oid); - if (err < 0) { - return Error_set_oid(err, parent_oid, GIT_OID_HEXSZ); - } - - err = git_commit_tree(&parent_tree, parent); - if (err == GIT_ENOTFOUND) - goto error; - - err = git_diff_tree_to_tree(&diff, repo, parent_tree, tree, &opts); + for (i = 0; i < parent_count; i++) { + err = quick_commit_diff_with_parent(repo, self->commit, i, &opts, &diff); if (err < 0) - goto error; - - ndeltas = (int)git_diff_num_deltas(diff); + goto cleanup_error; + err = get_diff_paths(diff, &opts, path_indexs); git_diff_list_free(diff); - if (ndeltas > 0) - goto exit_true; + if (err < 0) + goto cleanup_error; } - goto exit_false; + } else { + err = quick_commit_diff(repo, self->commit, &opts, &diff); + if (err < 0) + goto cleanup_error; + err = get_diff_paths(diff, &opts, path_indexs); + git_diff_list_free(diff); + if (err < 0) + goto cleanup_error; } - err = git_diff_tree_to_tree(&diff, repo, tree, NULL, &opts); - if (err < 0) - return Error_set(err); - ndeltas = (int)git_diff_num_deltas(diff); - git_diff_list_free(diff); - if (ndeltas > 0) - goto exit_true; - -exit_false: - if (py_paths != NULL) - PyMem_Free(opts.pathspec.strings); - Py_RETURN_FALSE; -error_null: - if (py_paths != NULL) - PyMem_Free(opts.pathspec.strings); +cleanup_empty: + py_diff_paths = PyList_New(paths_length); + for (i = 0; i < paths_length; i++) { + PyList_SetItem(py_diff_paths, i, Py_BuildValue("i", path_indexs[i])); + } + +cleanup: + PyMem_Free(opts.pathspec.strings); + PyMem_Free(path_indexs); + return py_diff_paths; + +cleanup_error: + PyMem_Free(opts.pathspec.strings); + PyMem_Free(path_indexs); return NULL; -error: - if (py_paths != NULL) - PyMem_Free(opts.pathspec.strings); - return Error_set(err); -exit_true: - if (py_paths != NULL) - PyMem_Free(opts.pathspec.strings); - Py_RETURN_TRUE; } PyMethodDef Commit_methods[] = { diff --git a/test/test_commit.py b/test/test_commit.py index 0bd4fcd93..71ff0c112 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -137,16 +137,19 @@ def test_modify_commit(self): def test_is_changed(self): commit = self.repo['c2792cfa289ae6321ecf2cd5806c2194b0fd070c'] self.assertRaises(TypeError, commit.is_changed, b'a') - self.assertEqual(commit.is_changed([b'a']), True) - self.assertEqual(commit.is_changed([b'c']), False) - self.assertEqual(commit.is_changed([b'a'], no_merges=False), True) - self.assertEqual(commit.is_changed([b'c'], no_merges=True), False) + self.assertEqual(commit.is_changed([b'a']), [1]) + self.assertEqual(commit.is_changed([b'c']), [0]) + self.assertEqual(commit.is_changed([b'a'], no_merges=False), [1]) + self.assertEqual(commit.is_changed([b'c'], no_merges=True), [0]) + self.assertEqual(commit.is_changed([b'a', b'b']), [1, 1]) + self.assertEqual(commit.is_changed([b'a', b'c']), [1, 0]) commit = self.repo['f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87'] - self.assertEqual(commit.is_changed([b'lorem']), True) - self.assertEqual(commit.is_changed([b'a']), False) - self.assertEqual(commit.is_changed([b'lorem'], no_merges=True), True) - self.assertEqual(commit.is_changed([b'a'], no_merges=True), False) + self.assertEqual(commit.is_changed([b'lorem']), [1]) + self.assertEqual(commit.is_changed([b'a']), [0]) + self.assertEqual(commit.is_changed([b'lorem'], no_merges=True), [1]) + self.assertEqual(commit.is_changed([b'a'], no_merges=True), [0]) + self.assertEqual(commit.is_changed([b'lorem', b'a']), [1, 0]) if __name__ == '__main__': From 01a08e2ab7d853194e0b71fd709d107c1673ff2a Mon Sep 17 00:00:00 2001 From: XTao Date: Wed, 31 Jul 2013 21:16:49 +0800 Subject: [PATCH 21/49] Fix commit.is_changed directory path. --- src/commit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commit.c b/src/commit.c index 55b8f7513..2b53edf8f 100644 --- a/src/commit.c +++ b/src/commit.c @@ -44,7 +44,7 @@ compare_delta_file_path(const git_diff_delta *delta, git_diff_options *opts) int length = opts->pathspec.count; char **paths = opts->pathspec.strings; for (i = 0; i < length; i++) { - cmp = strcmp(delta->old_file.path, paths[i]); + cmp = strncmp(delta->old_file.path, paths[i], strlen(paths[i])); if (cmp != 0) continue; res = i; From 90f3a46e93249de91187ec1e417c9fcc7735ae67 Mon Sep 17 00:00:00 2001 From: xutao Date: Wed, 31 Jul 2013 22:52:18 +0800 Subject: [PATCH 22/49] Add no_diff to commit.is_changed() --- src/commit.c | 123 ++++++++++++++++++++++++++++++++++++++++++-- test/test_commit.py | 11 ++++ 2 files changed, 131 insertions(+), 3 deletions(-) diff --git a/src/commit.c b/src/commit.c index 2b53edf8f..8b2699d8f 100644 --- a/src/commit.c +++ b/src/commit.c @@ -140,6 +140,98 @@ quick_commit_diff(git_repository *repo, git_commit *commit, return err; } +int +compare_tree_entry(git_tree *tree, git_tree *parent_tree, git_diff_options *opts, int *indexs) +{ + const git_tree_entry *entry; + const git_tree_entry *parent_entry; + int length = opts->pathspec.count; + char **paths = opts->pathspec.strings; + int i; + int re; + int count = 0; + for (i = 0; i < length; i++) { + entry = git_tree_entry_byname(tree, paths[i]); + if (parent_tree == NULL) { + if (entry == NULL) + continue; + } else { + parent_entry = git_tree_entry_byname(parent_tree, paths[i]); + if (entry == NULL && parent_entry == NULL) + continue; + if (entry != NULL && parent_entry != NULL) { + re = memcmp(git_tree_entry_id(entry)->id, git_tree_entry_id(parent_entry)->id, + GIT_OID_RAWSZ); + if (re == 0) + continue; + } + } + indexs[i] ++; + count ++; + } + return count; +} + + +int +commit_tree_with_parent(git_repository *repo, git_commit *commit, unsigned int index, + git_diff_options *opts, int *indexs) +{ + const git_oid *parent_oid; + git_commit *parent; + git_tree* parent_tree = NULL; + git_tree* tree = NULL; + int err; + int count; + parent_oid = git_commit_parent_id(commit, index); + if (parent_oid == NULL) { + err = GIT_ENOTFOUND; + goto cleanup; + } + + err = git_commit_lookup(&parent, repo, parent_oid); + if (err < 0) + goto cleanup; + + err = git_commit_tree(&parent_tree, parent); + if (err < 0) + goto cleanup_ptree; + + err = git_commit_tree(&tree, commit); + if (err < 0) + goto cleanup_tree; + + err = compare_tree_entry(tree, parent_tree, opts, indexs); + +cleanup_entry: + git_tree_free(tree); +cleanup_tree: + git_tree_free(parent_tree); +cleanup_ptree: + git_commit_free(parent); +cleanup: + return err; +} + +int +commit_tree(git_repository *repo, git_commit *commit, + git_diff_options *opts, int *indexs) +{ + git_tree* tree = NULL; + int err; + err = git_commit_tree(&tree, commit); + if (err < 0) + goto cleanup; + + err = compare_tree_entry(tree, NULL, opts, indexs); + +cleanup_entry: + git_tree_free(tree); +cleanup: + return err; +} + + PyDoc_STRVAR(Commit_message_encoding__doc__, "Message encoding."); PyObject * @@ -338,16 +430,17 @@ Commit_is_changed(Commit *self, PyObject *args, PyObject *kwds) unsigned int parent_count; int err; int ndeltas; - char *keywords[] = {"paths", "flags", "no_merges", NULL}; + char *keywords[] = {"paths", "flags", "no_merges", "no_diff", NULL}; Repository *py_repo; PyObject *py_paths = NULL; PyObject *py_diff_paths = NULL; PyObject *py_no_merges = NULL; + PyObject *py_no_diff = NULL; int *path_indexs; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iO", keywords, - &py_paths, &opts.flags, &py_no_merges)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iOO", keywords, + &py_paths, &opts.flags, &py_no_merges, &py_no_diff)) return NULL; if (!PyObject_TypeCheck(py_paths, &PyList_Type)) { @@ -363,6 +456,14 @@ Commit_is_changed(Commit *self, PyObject *args, PyObject *kwds) return NULL; } + if (py_no_diff != NULL && + (py_no_diff != Py_None && + !PyObject_TypeCheck(py_no_diff, &PyBool_Type)) + ) { + PyErr_SetObject(PyExc_TypeError, py_no_diff); + return NULL; + } + int paths_length = 0; PyObject *py_path = NULL; paths_length = PyList_Size(py_paths); @@ -395,6 +496,22 @@ Commit_is_changed(Commit *self, PyObject *args, PyObject *kwds) goto cleanup_empty; } + if (py_no_diff != NULL && + py_no_diff != Py_None && PyObject_IsTrue(py_no_diff)) { + if (parent_count > 0) { + for (i = 0; i < parent_count; i++) { + err = commit_tree_with_parent(repo, self->commit, i, &opts, path_indexs); + if (err < 0) + goto cleanup_error; + } + } else { + err = commit_tree(repo, self->commit, &opts, path_indexs); + if (err < 0) + goto cleanup_error; + } + goto cleanup_empty; + } + if (parent_count > 0) { for (i = 0; i < parent_count; i++) { err = quick_commit_diff_with_parent(repo, self->commit, i, &opts, &diff); diff --git a/test/test_commit.py b/test/test_commit.py index 71ff0c112..22daf2210 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -143,6 +143,12 @@ def test_is_changed(self): self.assertEqual(commit.is_changed([b'c'], no_merges=True), [0]) self.assertEqual(commit.is_changed([b'a', b'b']), [1, 1]) self.assertEqual(commit.is_changed([b'a', b'c']), [1, 0]) + self.assertEqual(commit.is_changed([b'a'], no_diff=True), [1]) + self.assertEqual(commit.is_changed([b'c'], no_diff=True), [0]) + self.assertEqual(commit.is_changed([b'a'], no_merges=False, no_diff=True), [1]) + self.assertEqual(commit.is_changed([b'c'], no_merges=True, no_diff=True), [0]) + self.assertEqual(commit.is_changed([b'a', b'b'], no_diff=True), [1, 1]) + self.assertEqual(commit.is_changed([b'a', b'c'], no_diff=True), [1, 0]) commit = self.repo['f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87'] self.assertEqual(commit.is_changed([b'lorem']), [1]) @@ -150,6 +156,11 @@ def test_is_changed(self): self.assertEqual(commit.is_changed([b'lorem'], no_merges=True), [1]) self.assertEqual(commit.is_changed([b'a'], no_merges=True), [0]) self.assertEqual(commit.is_changed([b'lorem', b'a']), [1, 0]) + self.assertEqual(commit.is_changed([b'lorem'], no_diff=True), [1]) + self.assertEqual(commit.is_changed([b'a'], no_diff=True), [0]) + self.assertEqual(commit.is_changed([b'lorem'], no_merges=True, no_diff=True), [1]) + self.assertEqual(commit.is_changed([b'a'], no_merges=True, no_diff=True), [0]) + self.assertEqual(commit.is_changed([b'lorem', b'a'], no_diff=True), [1, 0]) if __name__ == '__main__': From e176ef33594ac1c2f53f253dfbe706d099d11f02 Mon Sep 17 00:00:00 2001 From: xutao Date: Thu, 1 Aug 2013 21:16:06 +0800 Subject: [PATCH 23/49] Fix no_diff bug. --- src/commit.c | 36 ++++++++++++++++++++++++++++++------ test/test_commit.py | 4 ++-- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/commit.c b/src/commit.c index 8b2699d8f..1996fe3ad 100644 --- a/src/commit.c +++ b/src/commit.c @@ -148,28 +148,52 @@ compare_tree_entry(git_tree *tree, git_tree *parent_tree, git_diff_options *opts int length = opts->pathspec.count; char **paths = opts->pathspec.strings; int i; + int err; int re; - int count = 0; for (i = 0; i < length; i++) { - entry = git_tree_entry_byname(tree, paths[i]); + if (indexs[i] > 0) + continue; + err = git_tree_entry_bypath((git_tree_entry **)&entry, tree, paths[i]); + if (err < 0) { + if (err != GIT_ENOTFOUND) + goto cleanup; + entry = NULL; + } if (parent_tree == NULL) { if (entry == NULL) continue; + git_tree_entry_free((git_tree_entry *)entry); } else { - parent_entry = git_tree_entry_byname(parent_tree, paths[i]); + err = git_tree_entry_bypath((git_tree_entry **)&parent_entry, parent_tree, paths[i]); + if (err < 0) { + if (err != GIT_ENOTFOUND) + goto cleanup_error; + parent_entry = NULL; + } if (entry == NULL && parent_entry == NULL) continue; if (entry != NULL && parent_entry != NULL) { re = memcmp(git_tree_entry_id(entry)->id, git_tree_entry_id(parent_entry)->id, GIT_OID_RAWSZ); - if (re == 0) + if (re == 0) { + git_tree_entry_free((git_tree_entry *)parent_entry); + git_tree_entry_free((git_tree_entry *)entry); continue; + } } + if (entry != NULL) + git_tree_entry_free((git_tree_entry *)entry); + if (parent_entry != NULL) + git_tree_entry_free((git_tree_entry *)parent_entry); } indexs[i] ++; - count ++; } - return count; + return 0; + +cleanup_error: + git_tree_entry_free((git_tree_entry *)entry); +cleanup: + return err; } diff --git a/test/test_commit.py b/test/test_commit.py index 22daf2210..2456df801 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -139,13 +139,13 @@ def test_is_changed(self): self.assertRaises(TypeError, commit.is_changed, b'a') self.assertEqual(commit.is_changed([b'a']), [1]) self.assertEqual(commit.is_changed([b'c']), [0]) - self.assertEqual(commit.is_changed([b'a'], no_merges=False), [1]) + self.assertEqual(commit.is_changed([b'a'], no_merges=True), [1]) self.assertEqual(commit.is_changed([b'c'], no_merges=True), [0]) self.assertEqual(commit.is_changed([b'a', b'b']), [1, 1]) self.assertEqual(commit.is_changed([b'a', b'c']), [1, 0]) self.assertEqual(commit.is_changed([b'a'], no_diff=True), [1]) self.assertEqual(commit.is_changed([b'c'], no_diff=True), [0]) - self.assertEqual(commit.is_changed([b'a'], no_merges=False, no_diff=True), [1]) + self.assertEqual(commit.is_changed([b'a'], no_merges=True, no_diff=True), [1]) self.assertEqual(commit.is_changed([b'c'], no_merges=True, no_diff=True), [0]) self.assertEqual(commit.is_changed([b'a', b'b'], no_diff=True), [1, 1]) self.assertEqual(commit.is_changed([b'a', b'c'], no_diff=True), [1, 0]) From 8d904811d7b8be62de45570ba998d6c902159525 Mon Sep 17 00:00:00 2001 From: xutao Date: Thu, 1 Aug 2013 21:43:48 +0800 Subject: [PATCH 24/49] Refactor commit.is_changed() --- src/commit.c | 63 ++++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/src/commit.c b/src/commit.c index 1996fe3ad..2fb72bc93 100644 --- a/src/commit.c +++ b/src/commit.c @@ -36,7 +36,7 @@ extern PyTypeObject TreeType; int -compare_delta_file_path(const git_diff_delta *delta, git_diff_options *opts) +compare_delta_path(const git_diff_delta *delta, git_diff_options *opts) { unsigned int i; int res = -1; @@ -54,7 +54,7 @@ compare_delta_file_path(const git_diff_delta *delta, git_diff_options *opts) } int -get_diff_paths(git_diff_list *diff, git_diff_options *opts, int *indexs) +diff_path_bytree(git_diff_list *diff, git_diff_options *opts, int *indexs) { const git_diff_delta *delta; unsigned int i; @@ -67,7 +67,7 @@ get_diff_paths(git_diff_list *diff, git_diff_options *opts, int *indexs) err = git_diff_get_patch(NULL, &delta, diff, i); if (err < 0) goto cleanup; - index = compare_delta_file_path(delta, opts); + index = compare_delta_path(delta, opts); if (index < 0) continue; indexs[index] ++; @@ -79,7 +79,7 @@ get_diff_paths(git_diff_list *diff, git_diff_options *opts, int *indexs) } int -quick_commit_diff_with_parent(git_repository *repo, git_commit *commit, unsigned int index, +diff_tree_with_parent(git_repository *repo, git_commit *commit, unsigned int index, git_diff_options *opts, git_diff_list **diff) { const git_oid *parent_oid; @@ -120,7 +120,7 @@ quick_commit_diff_with_parent(git_repository *repo, git_commit *commit, unsigned } int -quick_commit_diff(git_repository *repo, git_commit *commit, +diff_tree(git_repository *repo, git_commit *commit, git_diff_options *opts, git_diff_list **diff) { git_tree* tree = NULL; @@ -141,7 +141,7 @@ quick_commit_diff(git_repository *repo, git_commit *commit, } int -compare_tree_entry(git_tree *tree, git_tree *parent_tree, git_diff_options *opts, int *indexs) +diff_path_byentry(git_tree *tree, git_tree *parent_tree, git_diff_options *opts, int *indexs) { const git_tree_entry *entry; const git_tree_entry *parent_entry; @@ -196,9 +196,26 @@ compare_tree_entry(git_tree *tree, git_tree *parent_tree, git_diff_options *opts return err; } +int +diff_entry(git_repository *repo, git_commit *commit, + git_diff_options *opts, int *indexs) +{ + git_tree* tree = NULL; + int err; + err = git_commit_tree(&tree, commit); + if (err < 0) + goto cleanup; + + err = diff_path_byentry(tree, NULL, opts, indexs); + +cleanup_entry: + git_tree_free(tree); +cleanup: + return err; +} int -commit_tree_with_parent(git_repository *repo, git_commit *commit, unsigned int index, +diff_entry_with_parent(git_repository *repo, git_commit *commit, unsigned int index, git_diff_options *opts, int *indexs) { const git_oid *parent_oid; @@ -225,7 +242,7 @@ commit_tree_with_parent(git_repository *repo, git_commit *commit, unsigned int i if (err < 0) goto cleanup_tree; - err = compare_tree_entry(tree, parent_tree, opts, indexs); + err = diff_path_byentry(tree, parent_tree, opts, indexs); cleanup_entry: git_tree_free(tree); @@ -237,24 +254,6 @@ commit_tree_with_parent(git_repository *repo, git_commit *commit, unsigned int i return err; } -int -commit_tree(git_repository *repo, git_commit *commit, - git_diff_options *opts, int *indexs) -{ - git_tree* tree = NULL; - int err; - err = git_commit_tree(&tree, commit); - if (err < 0) - goto cleanup; - - err = compare_tree_entry(tree, NULL, opts, indexs); - -cleanup_entry: - git_tree_free(tree); -cleanup: - return err; -} - PyDoc_STRVAR(Commit_message_encoding__doc__, "Message encoding."); @@ -524,12 +523,12 @@ Commit_is_changed(Commit *self, PyObject *args, PyObject *kwds) py_no_diff != Py_None && PyObject_IsTrue(py_no_diff)) { if (parent_count > 0) { for (i = 0; i < parent_count; i++) { - err = commit_tree_with_parent(repo, self->commit, i, &opts, path_indexs); + err = diff_entry_with_parent(repo, self->commit, i, &opts, path_indexs); if (err < 0) goto cleanup_error; } } else { - err = commit_tree(repo, self->commit, &opts, path_indexs); + err = diff_entry(repo, self->commit, &opts, path_indexs); if (err < 0) goto cleanup_error; } @@ -538,19 +537,19 @@ Commit_is_changed(Commit *self, PyObject *args, PyObject *kwds) if (parent_count > 0) { for (i = 0; i < parent_count; i++) { - err = quick_commit_diff_with_parent(repo, self->commit, i, &opts, &diff); + err = diff_tree_with_parent(repo, self->commit, i, &opts, &diff); if (err < 0) goto cleanup_error; - err = get_diff_paths(diff, &opts, path_indexs); + err = diff_path_bytree(diff, &opts, path_indexs); git_diff_list_free(diff); if (err < 0) goto cleanup_error; } } else { - err = quick_commit_diff(repo, self->commit, &opts, &diff); + err = diff_tree(repo, self->commit, &opts, &diff); if (err < 0) goto cleanup_error; - err = get_diff_paths(diff, &opts, path_indexs); + err = diff_path_bytree(diff, &opts, path_indexs); git_diff_list_free(diff); if (err < 0) goto cleanup_error; From 64e3340e5a2baadcda67d2c72dd1600dab24c2f0 Mon Sep 17 00:00:00 2001 From: xutao Date: Thu, 1 Aug 2013 23:21:16 +0800 Subject: [PATCH 25/49] Add thread to commit.is_changed() Dark magic. --- src/commit.c | 188 ++++++++++++++++++++++++++++++++++++++++++-- test/test_commit.py | 5 ++ 2 files changed, 185 insertions(+), 8 deletions(-) diff --git a/src/commit.c b/src/commit.c index 2fb72bc93..cc52d1fd3 100644 --- a/src/commit.c +++ b/src/commit.c @@ -79,7 +79,7 @@ diff_path_bytree(git_diff_list *diff, git_diff_options *opts, int *indexs) } int -diff_tree_with_parent(git_repository *repo, git_commit *commit, unsigned int index, +diff_tree_byparent(git_repository *repo, git_commit *commit, unsigned int index, git_diff_options *opts, git_diff_list **diff) { const git_oid *parent_oid; @@ -140,8 +140,104 @@ diff_tree(git_repository *repo, git_commit *commit, return err; } +struct diff_thread_t +{ + int id; + pthread_t thread; + git_tree *tree; + git_tree *parent_tree; + char *path; + int *indexs; +}; + +typedef struct diff_thread_t diff_thread_t; + +void +diff_path_byentry_func(void *arg) +{ + git_tree *tree; + git_tree *parent_tree; + char *path; + const git_tree_entry *entry; + const git_tree_entry *parent_entry; + diff_thread_t *thread = (diff_thread_t *)arg; + tree = thread->tree; + parent_tree = thread->parent_tree; + path = thread->path; + int err; + int re; + + err = git_tree_entry_bypath((git_tree_entry **)&entry, tree, path); + if (err < 0) { + if (err != GIT_ENOTFOUND) + goto cleanup; + entry = NULL; + } + if (parent_tree == NULL) { + if (entry == NULL) + goto cleanup; + git_tree_entry_free((git_tree_entry *)entry); + } else { + err = git_tree_entry_bypath((git_tree_entry **)&parent_entry, parent_tree, path); + if (err < 0) { + if (err != GIT_ENOTFOUND) + goto cleanup_error; + parent_entry = NULL; + } + if (entry == NULL && parent_entry == NULL) + goto cleanup; + if (entry != NULL && parent_entry != NULL) { + re = memcmp(git_tree_entry_id(entry)->id, git_tree_entry_id(parent_entry)->id, + GIT_OID_RAWSZ); + if (re == 0) { + git_tree_entry_free((git_tree_entry *)parent_entry); + git_tree_entry_free((git_tree_entry *)entry); + goto cleanup; + } + } + if (entry != NULL) + git_tree_entry_free((git_tree_entry *)entry); + if (parent_entry != NULL) + git_tree_entry_free((git_tree_entry *)parent_entry); + } + thread->indexs[thread->id] ++; + pthread_exit(0); +cleanup_error: + git_tree_entry_free((git_tree_entry *)entry); +cleanup: + pthread_exit(0); +} + +int +diff_path_byentry_threaded(git_tree *tree, git_tree *parent_tree, + git_diff_options *opts, int *indexs) +{ + int length = opts->pathspec.count; + char **paths = opts->pathspec.strings; + int i; + int err; + diff_thread_t *threads; + diff_thread_t *thread; + threads = (diff_thread_t *)calloc(length, sizeof (diff_thread_t)); + for (i = 0; i < length; i++) { + thread = &threads[i]; + thread->id = i; + thread->tree = tree; + thread->parent_tree = parent_tree; + thread->path = paths[i]; + thread->indexs = indexs; + err = pthread_create(&thread->thread, NULL, (void *) &diff_path_byentry_func, (void *) thread); + } + for (i = 0; i < length; i++) { + thread = &threads[i]; + err = pthread_join(thread->thread, NULL); + } + return 0; +} + int -diff_path_byentry(git_tree *tree, git_tree *parent_tree, git_diff_options *opts, int *indexs) +diff_path_byentry(git_tree *tree, git_tree *parent_tree, + git_diff_options *opts, int *indexs) { const git_tree_entry *entry; const git_tree_entry *parent_entry; @@ -215,7 +311,7 @@ diff_entry(git_repository *repo, git_commit *commit, } int -diff_entry_with_parent(git_repository *repo, git_commit *commit, unsigned int index, +diff_entry_byparent(git_repository *repo, git_commit *commit, unsigned int index, git_diff_options *opts, int *indexs) { const git_oid *parent_oid; @@ -254,6 +350,64 @@ diff_entry_with_parent(git_repository *repo, git_commit *commit, unsigned int in return err; } +int +diff_entry_threaded(git_repository *repo, git_commit *commit, + git_diff_options *opts, int *indexs) +{ + git_tree* tree = NULL; + int err; + err = git_commit_tree(&tree, commit); + if (err < 0) + goto cleanup; + + err = diff_path_byentry_threaded(tree, NULL, opts, indexs); + +cleanup_entry: + git_tree_free(tree); +cleanup: + return err; +} + +int +diff_entry_byparent_threaded(git_repository *repo, git_commit *commit, unsigned int index, + git_diff_options *opts, int *indexs) +{ + const git_oid *parent_oid; + git_commit *parent; + git_tree* parent_tree = NULL; + git_tree* tree = NULL; + int err; + int count; + parent_oid = git_commit_parent_id(commit, index); + if (parent_oid == NULL) { + err = GIT_ENOTFOUND; + goto cleanup; + } + + err = git_commit_lookup(&parent, repo, parent_oid); + if (err < 0) + goto cleanup; + + err = git_commit_tree(&parent_tree, parent); + if (err < 0) + goto cleanup_ptree; + + err = git_commit_tree(&tree, commit); + if (err < 0) + goto cleanup_tree; + + err = diff_path_byentry_threaded(tree, parent_tree, opts, indexs); + +cleanup_entry: + git_tree_free(tree); +cleanup_tree: + git_tree_free(parent_tree); +cleanup_ptree: + git_commit_free(parent); +cleanup: + return err; +} + PyDoc_STRVAR(Commit_message_encoding__doc__, "Message encoding."); @@ -453,17 +607,19 @@ Commit_is_changed(Commit *self, PyObject *args, PyObject *kwds) unsigned int parent_count; int err; int ndeltas; - char *keywords[] = {"paths", "flags", "no_merges", "no_diff", NULL}; + char *keywords[] = {"paths", "flags", "no_merges", "no_diff", "thread", NULL}; Repository *py_repo; PyObject *py_paths = NULL; PyObject *py_diff_paths = NULL; PyObject *py_no_merges = NULL; PyObject *py_no_diff = NULL; + PyObject *py_thread = NULL; int *path_indexs; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iOO", keywords, - &py_paths, &opts.flags, &py_no_merges, &py_no_diff)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iOOO", keywords, + &py_paths, &opts.flags, &py_no_merges, + &py_no_diff, &py_thread)) return NULL; if (!PyObject_TypeCheck(py_paths, &PyList_Type)) { @@ -519,11 +675,27 @@ Commit_is_changed(Commit *self, PyObject *args, PyObject *kwds) goto cleanup_empty; } + if (py_thread != NULL && + py_thread != Py_None && PyObject_IsTrue(py_thread)) { + if (parent_count > 0) { + for (i = 0; i < parent_count; i++) { + err = diff_entry_byparent_threaded(repo, self->commit, i, &opts, path_indexs); + if (err < 0) + goto cleanup_error; + } + } else { + err = diff_entry_threaded(repo, self->commit, &opts, path_indexs); + if (err < 0) + goto cleanup_error; + } + goto cleanup_empty; + } + if (py_no_diff != NULL && py_no_diff != Py_None && PyObject_IsTrue(py_no_diff)) { if (parent_count > 0) { for (i = 0; i < parent_count; i++) { - err = diff_entry_with_parent(repo, self->commit, i, &opts, path_indexs); + err = diff_entry_byparent(repo, self->commit, i, &opts, path_indexs); if (err < 0) goto cleanup_error; } @@ -537,7 +709,7 @@ Commit_is_changed(Commit *self, PyObject *args, PyObject *kwds) if (parent_count > 0) { for (i = 0; i < parent_count; i++) { - err = diff_tree_with_parent(repo, self->commit, i, &opts, &diff); + err = diff_tree_byparent(repo, self->commit, i, &opts, &diff); if (err < 0) goto cleanup_error; err = diff_path_bytree(diff, &opts, path_indexs); diff --git a/test/test_commit.py b/test/test_commit.py index 2456df801..a8342a838 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -150,6 +150,9 @@ def test_is_changed(self): self.assertEqual(commit.is_changed([b'a', b'b'], no_diff=True), [1, 1]) self.assertEqual(commit.is_changed([b'a', b'c'], no_diff=True), [1, 0]) + self.assertEqual(commit.is_changed([b'a', b'b'], thread=True), [1, 1]) + self.assertEqual(commit.is_changed([b'a', b'c'], thread=True), [1, 0]) + commit = self.repo['f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87'] self.assertEqual(commit.is_changed([b'lorem']), [1]) self.assertEqual(commit.is_changed([b'a']), [0]) @@ -162,6 +165,8 @@ def test_is_changed(self): self.assertEqual(commit.is_changed([b'a'], no_merges=True, no_diff=True), [0]) self.assertEqual(commit.is_changed([b'lorem', b'a'], no_diff=True), [1, 0]) + self.assertEqual(commit.is_changed([b'lorem', b'a'], thread=True), [1, 0]) + if __name__ == '__main__': unittest.main() From 68ee8a6143edd38daa1bce7a9480b943e8dfe018 Mon Sep 17 00:00:00 2001 From: xutao Date: Fri, 2 Aug 2013 15:23:50 +0800 Subject: [PATCH 26/49] Fix is_changed memory leaks. --- src/commit.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/commit.c b/src/commit.c index cc52d1fd3..6545b8674 100644 --- a/src/commit.c +++ b/src/commit.c @@ -232,6 +232,7 @@ diff_path_byentry_threaded(git_tree *tree, git_tree *parent_tree, thread = &threads[i]; err = pthread_join(thread->thread, NULL); } + free(threads); return 0; } From f9c5a468e31a6f8fcdaa5ff59020b3163173fb2a Mon Sep 17 00:00:00 2001 From: xutao Date: Tue, 6 Aug 2013 11:27:27 +0800 Subject: [PATCH 27/49] Add append_log to reference. --- src/reference.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/reference.c b/src/reference.c index 4d11cb5c5..d0456d932 100644 --- a/src/reference.c +++ b/src/reference.c @@ -40,6 +40,7 @@ extern PyObject *GitError; extern PyTypeObject RefLogEntryType; +extern PyTypeObject SignatureType; void RefLogIter_dealloc(RefLogIter *self) @@ -313,6 +314,54 @@ Reference_log(Reference *self) } +PyDoc_STRVAR(Reference_append_log__doc__, + "append_log(committer, message) -> Boolean\n" + "\n" + "Append reflog to the current reference."); + +PyObject * +Reference_append_log(Reference *self, PyObject *args, PyObject *kwds) +{ + git_signature *committer; + const char *message = NULL; + git_reflog *reflog; + int err; + Signature *py_committer; + PyObject *py_message = NULL; + char *keywords[] = {"committer", "message", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O", keywords, + &SignatureType, &py_committer, + &py_message)) + return NULL; + + // FIXME: encoding + message = py_str_to_c_str(py_message, NULL); + if (message == NULL) + return NULL; + + CHECK_REFERENCE(self); + + err = git_reflog_read(&reflog, self->reference); + if (err < 0) { + free((void *)message); + return NULL; + } + + committer = (git_signature *)py_committer->signature; + if (!(err = git_reflog_append(reflog, + git_reference_target(self->reference), + committer, + message))) + err = git_reflog_write(reflog); + + git_reflog_free(reflog); + free((void *)message); + + Py_RETURN_TRUE; +} + + PyDoc_STRVAR(Reference_get_object__doc__, "get_object() -> object\n" "\n" @@ -426,6 +475,7 @@ PyMethodDef Reference_methods[] = { METHOD(Reference, rename, METH_O), METHOD(Reference, resolve, METH_NOARGS), METHOD(Reference, log, METH_NOARGS), + METHOD(Reference, append_log, METH_VARARGS|METH_KEYWORDS), METHOD(Reference, get_object, METH_NOARGS), {NULL} }; From 298f941036192f80aaa90ed2eaab2a24bc4d5e77 Mon Sep 17 00:00:00 2001 From: Brodie Rao Date: Wed, 14 Aug 2013 20:11:19 -0700 Subject: [PATCH 28/49] tag: add get_object() method This maps to git_tag_peel(). --- docs/objects.rst | 2 ++ src/tag.c | 27 ++++++++++++++++++++++++++- test/test_tag.py | 5 +++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/docs/objects.rst b/docs/objects.rst index 36edf958f..6ee3e1ed0 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -267,6 +267,8 @@ A tag is a static label for a commit. See references for more information. .. autoattribute:: pygit2.Tag.tagger .. autoattribute:: pygit2.Tag.message +.. automethod:: pygit2.Tag.get_object + Creating tags -------------------- diff --git a/src/tag.c b/src/tag.c index bc4eaada7..35b9af578 100644 --- a/src/tag.c +++ b/src/tag.c @@ -27,6 +27,7 @@ #define PY_SSIZE_T_CLEAN #include +#include "object.h" #include "error.h" #include "types.h" #include "utils.h" @@ -47,6 +48,25 @@ Tag_target__get__(Tag *self) } +PyDoc_STRVAR(Tag_get_object__doc__, + "get_object() -> object\n" + "\n" + "Retrieves the object the current reference is pointing to."); + +PyObject * +Tag_get_object(Tag *self) +{ + int err; + git_object* obj; + + err = git_tag_peel(&obj, self->tag); + if (err < 0) + return Error_set(err); + + return wrap_object(obj, self->repo); +} + + PyDoc_STRVAR(Tag_name__doc__, "Tag name."); PyObject * @@ -94,6 +114,11 @@ Tag__message__get__(Tag *self) return PyBytes_FromString(git_tag_message(self->tag)); } +PyMethodDef Tag_methods[] = { + METHOD(Tag, get_object, METH_NOARGS), + {NULL} +}; + PyGetSetDef Tag_getseters[] = { GETTER(Tag, target), GETTER(Tag, name), @@ -134,7 +159,7 @@ PyTypeObject TagType = { 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ - 0, /* tp_methods */ + Tag_methods, /* tp_methods */ 0, /* tp_members */ Tag_getseters, /* tp_getset */ 0, /* tp_base */ diff --git a/test/test_tag.py b/test/test_tag.py index 12d89f055..9530eb5cc 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -89,6 +89,11 @@ def test_modify_tag(self): self.assertRaises(AttributeError, setattr, tag, 'tagger', tagger) self.assertRaises(AttributeError, setattr, tag, 'message', message) + def test_get_object(self): + repo = self.repo + tag = repo[TAG_SHA] + self.assertEqual(repo[tag.target].oid, tag.get_object().oid) + if __name__ == '__main__': unittest.main() From 68969ad088756730ca20d29c516824eb01244377 Mon Sep 17 00:00:00 2001 From: xutao Date: Fri, 16 Aug 2013 16:06:36 +0800 Subject: [PATCH 29/49] Add blob.binary --- src/blob.c | 12 ++++++++++++ src/reference.c | 3 +++ test/test_blob.py | 1 + 3 files changed, 16 insertions(+) diff --git a/src/blob.c b/src/blob.c index 465d8d651..1d41a2283 100644 --- a/src/blob.c +++ b/src/blob.c @@ -41,12 +41,24 @@ Blob_size__get__(Blob *self) } +PyDoc_STRVAR(Blob_binary__doc__, "Binary."); + +PyObject * +Blob_binary__get__(Blob *self) +{ + if (git_blob_is_binary(self->blob)) + Py_RETURN_TRUE; + Py_RETURN_FALSE; +} + + PyDoc_STRVAR(Blob_data__doc__, "The contents of the blob, a bytes string. This is the same as\n" "Blob.read_raw()"); PyGetSetDef Blob_getseters[] = { GETTER(Blob, size), + GETTER(Blob, binary), {"data", (getter)Object_read_raw, NULL, Blob_data__doc__, NULL}, {NULL} }; diff --git a/src/reference.c b/src/reference.c index d0456d932..78291ea09 100644 --- a/src/reference.c +++ b/src/reference.c @@ -358,6 +358,9 @@ Reference_append_log(Reference *self, PyObject *args, PyObject *kwds) git_reflog_free(reflog); free((void *)message); + if (err < 0) + return NULL; + Py_RETURN_TRUE; } diff --git a/test/test_blob.py b/test/test_blob.py index 8105455f7..813594ff0 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -53,6 +53,7 @@ def test_read_blob(self): sha = blob.oid.hex self.assertEqual(sha, BLOB_SHA) self.assertTrue(isinstance(blob, pygit2.Blob)) + self.assertFalse(blob.binary) self.assertEqual(pygit2.GIT_OBJ_BLOB, blob.type) self.assertEqual(BLOB_CONTENT, blob.data) self.assertEqual(len(BLOB_CONTENT), blob.size) From 6d87567e0f029528b7fc89e8d49e6898ab5b7ab5 Mon Sep 17 00:00:00 2001 From: Brodie Rao Date: Fri, 16 Aug 2013 10:56:15 -0700 Subject: [PATCH 30/49] tag: fix incorrect doc string wording for get_object() --- src/tag.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tag.c b/src/tag.c index 35b9af578..c5fdd0511 100644 --- a/src/tag.c +++ b/src/tag.c @@ -51,7 +51,7 @@ Tag_target__get__(Tag *self) PyDoc_STRVAR(Tag_get_object__doc__, "get_object() -> object\n" "\n" - "Retrieves the object the current reference is pointing to."); + "Retrieves the object the current tag is pointing to."); PyObject * Tag_get_object(Tag *self) From b55650e093bd7507eec25ce35debd37497368c4b Mon Sep 17 00:00:00 2001 From: Brodie Rao Date: Mon, 19 Aug 2013 11:53:48 -0700 Subject: [PATCH 31/49] commit: rename Commit._message to Commit.raw_message --- docs/objects.rst | 1 + src/commit.c | 6 +++--- test/test_commit.py | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/objects.rst b/docs/objects.rst index 6ee3e1ed0..6cfd80b3d 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -216,6 +216,7 @@ committer and others. .. autoattribute:: pygit2.Commit.committer .. autoattribute:: pygit2.Commit.message .. autoattribute:: pygit2.Commit.message_encoding +.. autoattribute:: pygit2.Commit.raw_message .. autoattribute:: pygit2.Commit.tree .. autoattribute:: pygit2.Commit.parents .. autoattribute:: pygit2.Commit.commit_time diff --git a/src/commit.c b/src/commit.c index 74b1ecc16..725a3c44e 100644 --- a/src/commit.c +++ b/src/commit.c @@ -64,10 +64,10 @@ Commit_message__get__(Commit *commit) } -PyDoc_STRVAR(Commit__message__doc__, "Message (bytes)."); +PyDoc_STRVAR(Commit_raw_message__doc__, "Message (bytes)."); PyObject * -Commit__message__get__(Commit *commit) +Commit_raw_message__get__(Commit *commit) { return PyBytes_FromString(git_commit_message(commit->commit)); } @@ -195,7 +195,7 @@ Commit_parents__get__(Commit *self) PyGetSetDef Commit_getseters[] = { GETTER(Commit, message_encoding), GETTER(Commit, message), - GETTER(Commit, _message), + GETTER(Commit, raw_message), GETTER(Commit, commit_time), GETTER(Commit, commit_time_offset), GETTER(Commit, committer), diff --git a/test/test_commit.py b/test/test_commit.py index 13acf982e..85f3d2350 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -113,7 +113,7 @@ def test_new_commit_encoding(self): self.assertEqual(GIT_OBJ_COMMIT, commit.type) self.assertEqual('iso-8859-1', commit.message_encoding) - self.assertEqual(message, commit.message) + self.assertEqual(message.encode(encoding), commit.raw_message) self.assertEqual(12346, commit.commit_time) self.assertEqualSignature(committer, commit.committer) self.assertEqualSignature(author, commit.author) From aec44c9ca2978d91c231491929f5f750fb3e4aa8 Mon Sep 17 00:00:00 2001 From: Brodie Rao Date: Mon, 19 Aug 2013 13:38:57 -0700 Subject: [PATCH 32/49] signature: rename Signature._name/_email to Signature.raw_name/raw_email --- docs/objects.rst | 2 ++ src/signature.c | 12 ++++++------ test/test_signature.py | 24 ++++++++++++++++++------ 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/docs/objects.rst b/docs/objects.rst index 6ee3e1ed0..2458511b7 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -232,7 +232,9 @@ objects:: .. autoattribute:: pygit2.Signature.name +.. autoattribute:: pygit2.Signature.raw_name .. autoattribute:: pygit2.Signature.email +.. autoattribute:: pygit2.Signature.raw_email .. autoattribute:: pygit2.Signature.time .. autoattribute:: pygit2.Signature.offset diff --git a/src/signature.c b/src/signature.c index 24e02a83e..61fd0746c 100644 --- a/src/signature.c +++ b/src/signature.c @@ -107,19 +107,19 @@ Signature__encoding__get__(Signature *self) } -PyDoc_STRVAR(Signature__name__doc__, "Name (bytes)."); +PyDoc_STRVAR(Signature_raw_name__doc__, "Name (bytes)."); PyObject * -Signature__name__get__(Signature *self) +Signature_raw_name__get__(Signature *self) { return to_bytes(self->signature->name); } -PyDoc_STRVAR(Signature__email__doc__, "Email (bytes)."); +PyDoc_STRVAR(Signature_raw_email__doc__, "Email (bytes)."); PyObject * -Signature__email__get__(Signature *self) +Signature_raw_email__get__(Signature *self) { return to_bytes(self->signature->email); } @@ -162,8 +162,8 @@ Signature_offset__get__(Signature *self) PyGetSetDef Signature_getseters[] = { GETTER(Signature, _encoding), - GETTER(Signature, _name), - GETTER(Signature, _email), + GETTER(Signature, raw_name), + GETTER(Signature, raw_email), GETTER(Signature, name), GETTER(Signature, email), GETTER(Signature, time), diff --git a/test/test_signature.py b/test/test_signature.py index cf21b33f1..4131c6c83 100644 --- a/test/test_signature.py +++ b/test/test_signature.py @@ -41,8 +41,12 @@ def test_default(self): 'Foo', 'foo@example.com', 1322174594, 60) encoding = signature._encoding self.assertEqual(encoding, 'ascii') - self.assertEqual(signature.name, signature._name.decode(encoding)) - self.assertEqual(signature.name.encode(encoding), signature._name) + self.assertEqual(signature.name, signature.raw_name.decode(encoding)) + self.assertEqual(signature.name.encode(encoding), signature.raw_name) + self.assertEqual(signature.email, + signature.raw_email.decode(encoding)) + self.assertEqual(signature.email.encode(encoding), + signature.raw_email) def test_ascii(self): self.assertRaises(UnicodeEncodeError, @@ -53,16 +57,24 @@ def test_latin1(self): signature = Signature( 'Foo Ibáñez', 'foo@example.com', encoding=encoding) self.assertEqual(encoding, signature._encoding) - self.assertEqual(signature.name, signature._name.decode(encoding)) - self.assertEqual(signature.name.encode(encoding), signature._name) + self.assertEqual(signature.name, signature.raw_name.decode(encoding)) + self.assertEqual(signature.name.encode(encoding), signature.raw_name) + self.assertEqual(signature.email, + signature.raw_email.decode(encoding)) + self.assertEqual(signature.email.encode(encoding), + signature.raw_email) def test_now(self): encoding = 'utf-8' signature = Signature( 'Foo Ibáñez', 'foo@example.com', encoding=encoding) self.assertEqual(encoding, signature._encoding) - self.assertEqual(signature.name, signature._name.decode(encoding)) - self.assertEqual(signature.name.encode(encoding), signature._name) + self.assertEqual(signature.name, signature.raw_name.decode(encoding)) + self.assertEqual(signature.name.encode(encoding), signature.raw_name) + self.assertEqual(signature.email, + signature.raw_email.decode(encoding)) + self.assertEqual(signature.email.encode(encoding), + signature.raw_email) self.assertTrue(abs(signature.time - time.time()) < 5) From 87f0d1db117eaf08cabf8e672b5bdd7b8f604bcd Mon Sep 17 00:00:00 2001 From: xutao Date: Wed, 21 Aug 2013 13:55:30 +0800 Subject: [PATCH 33/49] Add patch.binary --- src/diff.c | 19 ++++++++++++++++++- src/types.h | 2 +- test/test_diff.py | 2 ++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/diff.c b/src/diff.c index 489e7f9eb..2ed1160c2 100644 --- a/src/diff.c +++ b/src/diff.c @@ -81,6 +81,7 @@ diff_get_patch_byindex(git_diff_list* list, size_t idx) py_patch->new_file_path = delta->new_file.path; py_patch->status = git_diff_status_char(delta->status); py_patch->similarity = delta->similarity; + py_patch->flags = delta->flags; py_patch->old_oid = git_oid_allocfmt(&delta->old_file.oid); py_patch->new_oid = git_oid_allocfmt(&delta->new_file.oid); @@ -160,6 +161,22 @@ PyMemberDef Patch_members[] = { {NULL} }; +PyDoc_STRVAR(Patch_binary__doc__, "Binary."); + +PyObject * +Patch_binary__get__(Patch *self) +{ + if (!(self->flags & GIT_DIFF_FLAG_NOT_BINARY) && + (self->flags & GIT_DIFF_FLAG_BINARY)) + Py_RETURN_TRUE; + Py_RETURN_FALSE; +} + +PyGetSetDef Patch_getseters[] = { + GETTER(Patch, binary), + {NULL} +}; + PyDoc_STRVAR(Patch__doc__, "Diff patch object."); PyTypeObject PatchType = { @@ -192,7 +209,7 @@ PyTypeObject PatchType = { 0, /* tp_iternext */ 0, /* tp_methods */ Patch_members, /* tp_members */ - 0, /* tp_getset */ + Patch_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ diff --git a/src/types.h b/src/types.h index 757ea4eb4..683a05bd2 100644 --- a/src/types.h +++ b/src/types.h @@ -115,6 +115,7 @@ typedef struct { unsigned similarity; unsigned additions; unsigned deletions; + unsigned flags; } Patch; typedef struct { @@ -126,7 +127,6 @@ typedef struct { int new_lines; } Hunk; - /* git_tree_walk , git_treebuilder*/ SIMPLE_TYPE(TreeBuilder, git_treebuilder, bld) diff --git a/test/test_diff.py b/test/test_diff.py index 697b8be19..1366d1305 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -176,6 +176,8 @@ def _test(diff): self.assertEqual(patch.old_file_path, 'a') self.assertEqual(patch.new_file_path, 'a') + self.assertEqual(patch.binary, False) + _test(commit_a.tree.diff_to_tree(commit_b.tree)) _test(self.repo.diff(COMMIT_SHA1_1, COMMIT_SHA1_2)) From af68816d4abed88cf72d7c977119cb9152c1d8d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 24 Aug 2013 11:59:44 +0200 Subject: [PATCH 34/49] Make pygit2.TreeBuilder() to fail (issue #265) --- src/pygit2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pygit2.c b/src/pygit2.c index a4729df63..20fd27a1d 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -281,7 +281,7 @@ moduleinit(PyObject* m) INIT_TYPE(TreeType, &ObjectType, NULL) INIT_TYPE(TreeEntryType, NULL, NULL) INIT_TYPE(TreeIterType, NULL, NULL) - INIT_TYPE(TreeBuilderType, NULL, PyType_GenericNew) + INIT_TYPE(TreeBuilderType, NULL, NULL) INIT_TYPE(BlobType, &ObjectType, NULL) INIT_TYPE(TagType, &ObjectType, NULL) ADD_TYPE(m, Object) From d4cde25d3c86772e665427a7a418f51215e4022b Mon Sep 17 00:00:00 2001 From: XTao Date: Mon, 26 Aug 2013 02:24:03 +0800 Subject: [PATCH 35/49] Change libgit2 to github. --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index fd0c6d2bc..44fa6fdf6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "vendor/libgit2"] path = vendor/libgit2 - url = http://code.dapps.douban.com/xutao/libgit2.git + url = https://github.com/libgit2/libgit2.git From 4544aae776e14df9007c59a0a3a398ab3908ff28 Mon Sep 17 00:00:00 2001 From: xutao Date: Tue, 27 Aug 2013 15:56:54 +0800 Subject: [PATCH 36/49] Add paths to diff. --- pygit2/repository.py | 6 ++-- src/index.c | 11 +++++-- src/tree.c | 78 +++++++++++++++++++++++--------------------- src/utils.c | 50 ++++++++++++++++++++++++++++ 4 files changed, 103 insertions(+), 42 deletions(-) diff --git a/pygit2/repository.py b/pygit2/repository.py index fbb77ced3..888e4b640 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -134,7 +134,7 @@ def checkout(self, refname=None, strategy=GIT_CHECKOUT_SAFE_CREATE): # Diff # def diff(self, a=None, b=None, cached=False, flags=GIT_DIFF_NORMAL, - context_lines=3, interhunk_lines=0): + context_lines=3, interhunk_lines=0, paths=None): """ Show changes between the working tree and the index or a tree, changes between the index and a tree, changes between two trees, or @@ -192,8 +192,8 @@ def treeish_to_tree(obj): a = treeish_to_tree(a) or a b = treeish_to_tree(b) or b - opt_keys = ['flags', 'context_lines', 'interhunk_lines'] - opt_values = [flags, context_lines, interhunk_lines] + opt_keys = ['flags', 'context_lines', 'interhunk_lines', 'paths'] + opt_values = [flags, context_lines, interhunk_lines, paths] # Case 1: Diff tree to tree if isinstance(a, Tree) and isinstance(b, Tree): diff --git a/src/index.c b/src/index.c index 3c73f2178..9751b1549 100644 --- a/src/index.c +++ b/src/index.c @@ -136,10 +136,15 @@ Index_diff_to_workdir(Index *self, PyObject *args) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff; + PyObject *py_paths = NULL; int err; - if (!PyArg_ParseTuple(args, "|IHH", &opts.flags, &opts.context_lines, - &opts.interhunk_lines)) + if (!PyArg_ParseTuple(args, "|IHHO", &opts.flags, &opts.context_lines, + &opts.interhunk_lines, &py_paths)) + return NULL; + + err = py_list_to_opts(py_paths, &opts); + if (err < 0) return NULL; err = git_diff_index_to_workdir( @@ -148,6 +153,8 @@ Index_diff_to_workdir(Index *self, PyObject *args) self->index, &opts); + free_opts_pathspec(py_paths, &opts); + if (err < 0) return Error_set(err); diff --git a/src/tree.c b/src/tree.c index 367bb1b42..e56b4ffd4 100644 --- a/src/tree.c +++ b/src/tree.c @@ -308,29 +308,9 @@ Tree_diff(Tree *self, PyObject *args, PyObject *kwds) &opts.context_lines, &py_paths)) return NULL; - if (py_paths != NULL) { - if (!PyObject_TypeCheck(py_paths, &PyList_Type)) { - PyErr_SetObject(PyExc_TypeError, py_paths); - return NULL; - } - int i, paths_length = 0; - PyObject *py_path = NULL; - paths_length = PyList_Size(py_paths); - for (i = 0; i < paths_length; i++) { - py_path = PyList_GetItem(py_paths, i); - if (!PyObject_TypeCheck(py_path, &PyString_Type)) { - PyErr_SetObject(PyExc_TypeError, py_path); - return NULL; - } - } - opts.pathspec.count = paths_length; - opts.pathspec.strings = (char **) PyMem_Malloc(paths_length * sizeof (char *)); - for (i = 0; i < paths_length; i++) { - py_path = PyList_GetItem(py_paths, i); - opts.pathspec.strings[i] = PyString_AsString(py_path); - } - - } + err = py_list_to_opts(py_paths, &opts); + if (err < 0) + return NULL; repo = git_tree_owner(self->tree); if (py_obj == NULL) { @@ -348,14 +328,12 @@ Tree_diff(Tree *self, PyObject *args, PyObject *kwds) err = git_diff_tree_to_index(&diff, repo, self->tree, index, &opts); } else { - if (py_paths != NULL) - PyMem_Free(opts.pathspec.strings); + free_opts_pathspec(py_paths, &opts); PyErr_SetObject(PyExc_TypeError, py_obj); return NULL; } - if (py_paths != NULL) - PyMem_Free(opts.pathspec.strings); + free_opts_pathspec(py_paths, &opts); if (err < 0) return Error_set(err); @@ -372,7 +350,7 @@ Tree_diff(Tree *self, PyObject *args, PyObject *kwds) PyDoc_STRVAR(Tree_diff_to_workdir__doc__, - "diff_to_workdir([flags, context_lines, interhunk_lines]) -> Diff\n" + "diff_to_workdir([flags, context_lines, interhunk_lines, paths]) -> Diff\n" "\n" "Show the changes between the :py:class:`~pygit2.Tree` and the workdir.\n" "\n" @@ -392,14 +370,22 @@ Tree_diff_to_workdir(Tree *self, PyObject *args) git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff; Repository *py_repo; + PyObject *py_paths = NULL; int err; - if (!PyArg_ParseTuple(args, "|IHH", &opts.flags, &opts.context_lines, - &opts.interhunk_lines)) + if (!PyArg_ParseTuple(args, "|IHHO", &opts.flags, &opts.context_lines, + &opts.interhunk_lines, &py_paths)) + return NULL; + + err = py_list_to_opts(py_paths, &opts); + if (err < 0) return NULL; py_repo = self->repo; err = git_diff_tree_to_workdir(&diff, py_repo->repo, self->tree, &opts); + + free_opts_pathspec(py_paths, &opts); + if (err < 0) return Error_set(err); @@ -408,7 +394,7 @@ Tree_diff_to_workdir(Tree *self, PyObject *args) PyDoc_STRVAR(Tree_diff_to_index__doc__, - "diff_to_index(index, [flags, context_lines, interhunk_lines]) -> Diff\n" + "diff_to_index(index, [flags, context_lines, interhunk_lines, paths]) -> Diff\n" "\n" "Show the changes between the index and a given :py:class:`~pygit2.Tree`.\n" "\n" @@ -430,18 +416,27 @@ Tree_diff_to_index(Tree *self, PyObject *args, PyObject *kwds) git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff; Repository *py_repo; + PyObject *py_paths = NULL; int err; Index *py_idx = NULL; - if (!PyArg_ParseTuple(args, "O!|IHH", &IndexType, &py_idx, &opts.flags, + if (!PyArg_ParseTuple(args, "O!|IHHO", &IndexType, &py_idx, &opts.flags, &opts.context_lines, - &opts.interhunk_lines)) + &opts.interhunk_lines, + &py_paths)) + return NULL; + + err = py_list_to_opts(py_paths, &opts); + if (err < 0) return NULL; py_repo = self->repo; err = git_diff_tree_to_index(&diff, py_repo->repo, self->tree, py_idx->index, &opts); + + free_opts_pathspec(py_paths, &opts); + if (err < 0) return Error_set(err); @@ -450,7 +445,7 @@ Tree_diff_to_index(Tree *self, PyObject *args, PyObject *kwds) PyDoc_STRVAR(Tree_diff_to_tree__doc__, - "diff_to_tree([tree, flags, context_lines, interhunk_lines, swap]) -> Diff\n" + "diff_to_tree([tree, flags, context_lines, interhunk_lines, swap, paths]) -> Diff\n" "\n" "Show the changes between two trees\n" "\n" @@ -478,14 +473,20 @@ Tree_diff_to_tree(Tree *self, PyObject *args, PyObject *kwds) Repository *py_repo; int err, swap = 0; char *keywords[] = {"obj", "flags", "context_lines", "interhunk_lines", - "swap", NULL}; + "swap", "paths", NULL}; + PyObject *py_paths = NULL; Tree *py_tree = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!IHHi", keywords, + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!IHHiO", keywords, &TreeType, &py_tree, &opts.flags, &opts.context_lines, - &opts.interhunk_lines, &swap)) + &opts.interhunk_lines, &swap, + &py_paths)) + return NULL; + + err = py_list_to_opts(py_paths, &opts); + if (err < 0) return NULL; py_repo = self->repo; @@ -498,6 +499,9 @@ Tree_diff_to_tree(Tree *self, PyObject *args, PyObject *kwds) } err = git_diff_tree_to_tree(&diff, py_repo->repo, from, to, &opts); + + free_opts_pathspec(py_paths, &opts); + if (err < 0) return Error_set(err); diff --git a/src/utils.c b/src/utils.c index 31f0472aa..8b9a79d1b 100644 --- a/src/utils.c +++ b/src/utils.c @@ -60,3 +60,53 @@ py_str_to_c_str(PyObject *value, const char *encoding) Py_TYPE(value)->tp_name); return NULL; } + +int +py_list_to_opts(PyObject *py_paths, git_diff_options *opts) +{ + int i; + int paths_length = 0; + PyObject *py_path = NULL; + + if (py_paths == NULL) + return 1; + + if (py_paths == Py_None) + return 1; + + if (!PyObject_TypeCheck(py_paths, &PyList_Type)) { + PyErr_SetObject(PyExc_TypeError, py_paths); + return -1; + } + + paths_length = PyList_Size(py_paths); + for (i = 0; i < paths_length; i++) { + py_path = PyList_GetItem(py_paths, i); + if (!PyObject_TypeCheck(py_path, &PyString_Type)) { + PyErr_SetObject(PyExc_TypeError, py_path); + return -1; + } + } + + opts->pathspec.count = paths_length; + opts->pathspec.strings = (char **) PyMem_Malloc(paths_length * sizeof (char *)); + for (i = 0; i < paths_length; i++) { + py_path = PyList_GetItem(py_paths, i); + opts->pathspec.strings[i] = PyString_AsString(py_path); + } + + return 0; +} + +int +free_opts_pathspec(PyObject *py_paths, git_diff_options *opts) +{ + if (py_paths == NULL) + return 1; + + if (py_paths == Py_None) + return 1; + + PyMem_Free(opts->pathspec.strings); + return 0; +} From 5f5825735cfd4b34e3635c2965b1703b01eb6230 Mon Sep 17 00:00:00 2001 From: xutao Date: Tue, 27 Aug 2013 15:57:55 +0800 Subject: [PATCH 37/49] Add paths to docs. --- pygit2/repository.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pygit2/repository.py b/pygit2/repository.py index 888e4b640..aee797331 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -156,6 +156,9 @@ def diff(self, a=None, b=None, cached=False, flags=GIT_DIFF_NORMAL, the maximum number of unchanged lines between hunk boundaries before the hunks will be merged into a one + paths + paths to diff + Examples:: # Changes in the working tree not yet staged for the next commit From 179c7db9400b18242cc10f4832b181f6bd5ec0ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 29 Aug 2013 17:18:01 +0200 Subject: [PATCH 38/49] Don't forget to remove the temporary directory in the config tests When overriding the tear-down function, we must remember to call the parent's function or we won't clean up the temporary directory for the test. --- test/test_config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_config.py b/test/test_config.py index a8afe86cb..8740a64c9 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -46,6 +46,7 @@ def foreach_test_wrapper(key, name, lst): class ConfigTest(utils.RepoTestCase): def tearDown(self): + super(ConfigTest, self).tearDown() try: os.remove(CONFIG_FILENAME) except OSError: From 74b1628e5170a0406db62d726b61933de4825b56 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Thu, 29 Aug 2013 23:22:36 +1000 Subject: [PATCH 39/49] test utils: implement a repo context manager The current hierarchy of test cases that create and blow away test repositories in the ``setUp`` and ``tearDown`` methods is inadequate for testing scenarios where multiple repositories must be edited, e.g. testing push. Extract the temporary repository behaviour into a context manager class ``TemporaryRepository`` which can be used via the ``with`` keyword. The context manager's ``__enter__` method returns creates the specified repository in a temporary directory and returns its path. The temporary directory is removed in ``__exit__``. Update the existing test case base classes to use the context manager and invoke ``__enter__`` and ``__exit__`` in the ``setUp`` and ``tearDown`` methods respectively. Finally, make some small tweaks to a handful of tests to get them working under this new system. --- test/test_blob.py | 5 ++-- test/test_repository.py | 6 ++-- test/utils.py | 65 +++++++++++++++++++++++------------------ 3 files changed, 42 insertions(+), 34 deletions(-) diff --git a/test/test_blob.py b/test/test_blob.py index 8105455f7..2f5259649 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -93,13 +93,12 @@ def test_create_blob_fromworkdir(self): def test_create_blob_outside_workdir(self): - path = join(dirname(__file__), 'data', self.repo_dir + '.tar') + path = __file__ self.assertRaises(KeyError, self.repo.create_blob_fromworkdir, path) def test_create_blob_fromdisk(self): - path = join(dirname(__file__), 'data', self.repo_dir + '.tar') - blob_oid = self.repo.create_blob_fromdisk(path) + blob_oid = self.repo.create_blob_fromdisk(__file__) blob = self.repo[blob_oid] self.assertTrue(isinstance(blob, pygit2.Blob)) diff --git a/test/test_repository.py b/test/test_repository.py index 87c9fc704..ab0b22c89 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -143,7 +143,7 @@ def test_lookup_commit_prefix(self): def test_get_path(self): directory = realpath(self.repo.path) - expected = realpath(join(self._temp_dir, 'testrepo.git')) + expected = realpath(self.repo_path) self.assertEqual(directory, expected) def test_get_workdir(self): @@ -179,12 +179,12 @@ def test_is_bare(self): def test_get_path(self): directory = realpath(self.repo.path) - expected = realpath(join(self._temp_dir, 'testrepo', '.git')) + expected = realpath(join(self.repo_path, '.git')) self.assertEqual(directory, expected) def test_get_workdir(self): directory = realpath(self.repo.workdir) - expected = realpath(join(self._temp_dir, 'testrepo')) + expected = realpath(self.repo_path) self.assertEqual(directory, expected) def test_checkout_ref(self): diff --git a/test/utils.py b/test/utils.py index 10a4e1719..fb7ca58f9 100644 --- a/test/utils.py +++ b/test/utils.py @@ -65,6 +65,27 @@ def rmtree(path): shutil.rmtree(path, onerror=onerror) +class TemporaryRepository(object): + def __init__(self, repo_spec): + self.repo_spec = repo_spec + + def __enter__(self): + container, name = self.repo_spec + repo_path = os.path.join(os.path.dirname(__file__), 'data', name) + self.temp_dir = tempfile.mkdtemp() + temp_repo_path = os.path.join(self.temp_dir, name) + if container == 'tar': + tar = tarfile.open('.'.join((repo_path, 'tar'))) + tar.extractall(self.temp_dir) + tar.close() + else: + shutil.copytree(repo_path, temp_repo_path) + return temp_repo_path + + def __exit__(self, exc_type, exc_value, traceback): + rmtree(self.temp_dir) + + class NoRepoTestCase(unittest.TestCase): def setUp(self): @@ -103,45 +124,33 @@ def assertEqualSignature(self, a, b): self.assertEqual(a.offset, b.offset) -class BareRepoTestCase(NoRepoTestCase): - - repo_dir = 'testrepo.git' - +class AutoRepoTestCase(NoRepoTestCase): def setUp(self): - super(BareRepoTestCase, self).setUp() - - repo_dir = self.repo_dir - repo_path = os.path.join(os.path.dirname(__file__), 'data', repo_dir) - temp_repo_path = os.path.join(self._temp_dir, repo_dir) - - shutil.copytree(repo_path, temp_repo_path) - - self.repo = pygit2.Repository(temp_repo_path) + super(AutoRepoTestCase, self).setUp() + self.repo_ctxtmgr = TemporaryRepository(self.repo_spec) + self.repo_path = self.repo_ctxtmgr.__enter__() + self.repo = pygit2.Repository(self.repo_path) + def tearDown(self): + self.repo_ctxtmgr.__exit__(None, None, None) + super(AutoRepoTestCase, self).tearDown() -class RepoTestCase(NoRepoTestCase): - repo_dir = 'testrepo' +class BareRepoTestCase(AutoRepoTestCase): - def setUp(self): - super(RepoTestCase, self).setUp() + repo_spec = 'git', 'testrepo.git' - repo_dir = self.repo_dir - repo_path = os.path.join(os.path.dirname(__file__), 'data', repo_dir) - temp_repo_path = os.path.join(self._temp_dir, repo_dir, '.git') - tar = tarfile.open(repo_path + '.tar') - tar.extractall(self._temp_dir) - tar.close() +class RepoTestCase(AutoRepoTestCase): - self.repo = pygit2.Repository(temp_repo_path) + repo_spec = 'tar', 'testrepo' -class DirtyRepoTestCase(RepoTestCase): +class DirtyRepoTestCase(AutoRepoTestCase): - repo_dir = 'dirtyrepo' + repo_spec = 'tar', 'dirtyrepo' -class EmptyRepoTestCase(RepoTestCase): +class EmptyRepoTestCase(AutoRepoTestCase): - repo_dir = 'emptyrepo' + repo_spec = 'tar', 'emptyrepo' From 134d87ab2a06459cb6c273cdbc82cc5a0aa5aa3c Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Sun, 25 Aug 2013 19:51:20 +1000 Subject: [PATCH 40/49] implement push support Implement push support via Remote.push which is called with a single refspec and raises GitError (with an appropriate message where possible) if the push fails. Note that local push to non-bare repository is currently not supported by libgit2. --- docs/remotes.rst | 1 + src/remote.c | 66 +++++++++++++++++++++++++++++++++++++++++++++ test/test_remote.py | 40 +++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) diff --git a/docs/remotes.rst b/docs/remotes.rst index 91a69d055..4b9692fcd 100644 --- a/docs/remotes.rst +++ b/docs/remotes.rst @@ -15,4 +15,5 @@ The Remote type .. autoattribute:: pygit2.Remote.refspec_count .. automethod:: pygit2.Remote.get_refspec .. automethod:: pygit2.Remote.fetch +.. automethod:: pygit2.Remote.push .. automethod:: pygit2.Remote.save diff --git a/src/remote.c b/src/remote.c index 9faf11435..e482d8b1c 100644 --- a/src/remote.c +++ b/src/remote.c @@ -218,10 +218,76 @@ Remote_save(Remote *self, PyObject *args) } +int +push_status_foreach_callback(const char *ref, const char *msg, void *data) +{ + const char **msg_dst = (const char **)data; + if (msg != NULL && *msg_dst == NULL) + *msg_dst = msg; + return 0; +} + +PyDoc_STRVAR(Remote_push__doc__, + "push(refspec)\n" + "\n" + "Push the given refspec to the remote. Raises ``GitError`` on error."); + +PyObject * +Remote_push(Remote *self, PyObject *args) +{ + git_push *push = NULL; + const char *refspec = NULL; + const char *msg = NULL; + int err; + + if (!PyArg_ParseTuple(args, "s", &refspec)) + return NULL; + + err = git_push_new(&push, self->remote); + if (err < 0) + return Error_set(err); + + err = git_push_add_refspec(push, refspec); + if (err < 0) + goto error; + + err = git_push_finish(push); + if (err < 0) + goto error; + + if (!git_push_unpack_ok(push)) { + git_push_free(push); + PyErr_SetString(GitError, "Remote failed to unpack objects"); + return NULL; + } + + err = git_push_status_foreach(push, push_status_foreach_callback, &msg); + if (err < 0) + goto error; + if (msg != NULL) { + git_push_free(push); + PyErr_SetString(GitError, msg); + return NULL; + } + + err = git_push_update_tips(push); + if (err < 0) + goto error; + + git_push_free(push); + Py_RETURN_NONE; + +error: + git_push_free(push); + return Error_set(err); +} + + PyMethodDef Remote_methods[] = { METHOD(Remote, fetch, METH_NOARGS), METHOD(Remote, save, METH_NOARGS), METHOD(Remote, get_refspec, METH_O), + METHOD(Remote, push, METH_VARARGS), {NULL} }; diff --git a/test/test_remote.py b/test/test_remote.py index a4a125966..b6a9edc5d 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -124,5 +124,45 @@ def test_fetch(self): self.assertEqual(stats['received_objects'], REMOTE_REPO_OBJECTS) +class PushTestCase(unittest.TestCase): + def setUp(self): + self.origin_ctxtmgr = utils.TemporaryRepository(('git', 'testrepo.git')) + self.clone_ctxtmgr = utils.TemporaryRepository(('git', 'testrepo.git')) + self.origin = pygit2.Repository(self.origin_ctxtmgr.__enter__()) + self.clone = pygit2.Repository(self.clone_ctxtmgr.__enter__()) + self.remote = self.clone.create_remote('origin', self.origin.path) + + def tearDown(self): + self.origin_ctxtmgr.__exit__(None, None, None) + self.clone_ctxtmgr.__exit__(None, None, None) + + def test_push_fast_forward_commits_to_remote_succeeds(self): + tip = self.clone[self.clone.head.target] + oid = self.clone.create_commit( + 'refs/heads/master', tip.author, tip.author, 'empty commit', + tip.tree.oid, [tip.oid] + ) + self.remote.push('refs/heads/master') + self.assertEqual(self.origin[self.origin.head.target].oid, oid) + + def test_push_when_up_to_date_succeeds(self): + self.remote.push('refs/heads/master') + origin_tip = self.origin[self.origin.head.target].oid + clone_tip = self.clone[self.clone.head.target].oid + self.assertEqual(origin_tip, clone_tip) + + def test_push_non_fast_forward_commits_to_remote_fails(self): + tip = self.origin[self.origin.head.target] + oid = self.origin.create_commit( + 'refs/heads/master', tip.author, tip.author, 'some commit', + tip.tree.oid, [tip.oid] + ) + tip = self.clone[self.clone.head.target] + oid = self.clone.create_commit( + 'refs/heads/master', tip.author, tip.author, 'other commit', + tip.tree.oid, [tip.oid] + ) + self.assertRaises(pygit2.GitError, self.remote.push, 'refs/heads/master') + if __name__ == '__main__': unittest.main() From 73941b567e5f56979ec02f3160a93329f8389500 Mon Sep 17 00:00:00 2001 From: Huang Huang Date: Fri, 30 Aug 2013 16:25:54 +0800 Subject: [PATCH 41/49] add diff.size --- src/diff.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/diff.c b/src/diff.c index 2ed1160c2..7ee7b9eb0 100644 --- a/src/diff.c +++ b/src/diff.c @@ -271,6 +271,14 @@ PyTypeObject DiffIterType = { (iternextfunc) DiffIter_iternext, /* tp_iternext */ }; +PyDoc_STRVAR(Diff_size__doc__, "Diff size similar to 'git diff --numstat'."); + +int +Diff_size__get__(Diff *self) +{ + num = git_diff_num_deltas(self->list); + return num; +} PyDoc_STRVAR(Diff_patch__doc__, "Patch diff string."); @@ -462,6 +470,7 @@ Diff_dealloc(Diff *self) PyGetSetDef Diff_getseters[] = { GETTER(Diff, patch), + GETTER(Diff, size), {NULL} }; From e86f2f8b1500723a8ff26a19916e221a33543a41 Mon Sep 17 00:00:00 2001 From: Huang Huang Date: Fri, 30 Aug 2013 16:47:39 +0800 Subject: [PATCH 42/49] using PyObject* and PyLong_FromSize_t --- src/diff.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/diff.c b/src/diff.c index 7ee7b9eb0..bdc9db402 100644 --- a/src/diff.c +++ b/src/diff.c @@ -271,13 +271,13 @@ PyTypeObject DiffIterType = { (iternextfunc) DiffIter_iternext, /* tp_iternext */ }; -PyDoc_STRVAR(Diff_size__doc__, "Diff size similar to 'git diff --numstat'."); -int +PyDoc_STRVAR(Diff_size__doc__, "Returns the number of deltas/patches in this diff."); + +PyObject * Diff_size__get__(Diff *self) { - num = git_diff_num_deltas(self->list); - return num; + return PyLong_FromSize_t(git_diff_num_deltas(self->list)); } PyDoc_STRVAR(Diff_patch__doc__, "Patch diff string."); From 2d55a846f3f2395090ee16d4ae3de716b49ec528 Mon Sep 17 00:00:00 2001 From: Huang Huang Date: Fri, 30 Aug 2013 17:41:35 +0800 Subject: [PATCH 43/49] for add diff.size --- test/test_diff.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_diff.py b/test/test_diff.py index 1366d1305..52ca9e8ad 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -255,6 +255,7 @@ def test_diff_patch(self): commit_b = self.repo[COMMIT_SHA1_2] diff = commit_a.tree.diff_to_tree(commit_b.tree) + self.assertEqual(diff.size, len([patch for patch in diff])) self.assertEqual(diff.patch, PATCH) def test_diff_oids(self): From d5c0a23630d2471e4fe3772d39343201f7cd130a Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Sun, 1 Sep 2013 12:58:55 -0400 Subject: [PATCH 44/49] Doc fixes: change head.oid to head.target in examples --- docs/recipes/git-log.rst | 4 ++-- src/repository.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/recipes/git-log.rst b/docs/recipes/git-log.rst index e9c10b736..b234e0f61 100644 --- a/docs/recipes/git-log.rst +++ b/docs/recipes/git-log.rst @@ -16,7 +16,7 @@ Show HEAD commit .. code-block:: python - >>> commit = repo[repo.head.oid] + >>> commit = repo[repo.head.target] >>> commit.message 'commit message' @@ -30,7 +30,7 @@ Traverse commit history .. code-block:: python - >>> last = repo[repo.head.oid] + >>> last = repo[repo.head.target] >>> for commit in repo.walk(last.oid, pygit2.GIT_SORT_TIME): >>> print(commit.message) # or some other operation diff --git a/src/repository.c b/src/repository.c index 935c1665f..2ce42ab29 100644 --- a/src/repository.c +++ b/src/repository.c @@ -591,9 +591,9 @@ PyDoc_STRVAR(Repository_walk__doc__, " >>> from pygit2 import Repository\n" " >>> from pygit2 import GIT_SORT_TOPOLOGICAL, GIT_SORT_REVERSE\n" " >>> repo = Repository('.git')\n" - " >>> for commit in repo.walk(repo.head.oid, GIT_SORT_TOPOLOGICAL):\n" + " >>> for commit in repo.walk(repo.head.target, GIT_SORT_TOPOLOGICAL):\n" " ... print commit.message\n" - " >>> for commit in repo.walk(repo.head.oid, GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE):\n" + " >>> for commit in repo.walk(repo.head.target, GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE):\n" " ... print commit.message\n" " >>>\n"); From ce131ca233992474bd5017df5e0f56c1e60d7253 Mon Sep 17 00:00:00 2001 From: xutao Date: Fri, 20 Sep 2013 22:46:44 +0800 Subject: [PATCH 45/49] Bump libgit2. --- vendor/libgit2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/libgit2 b/vendor/libgit2 index 275d8d55b..5a284edca 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit 275d8d55b29c83bd8c165f5eeaafd76c7e0d966b +Subproject commit 5a284edca42dd75cf62b3fe75020819fcfcea9f3 From f50ccb15c7aeb83a995671d03ecc26346dec0a0a Mon Sep 17 00:00:00 2001 From: xutao Date: Fri, 20 Sep 2013 22:59:53 +0800 Subject: [PATCH 46/49] Fix finalize_write argument order. --- src/repository.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/repository.c b/src/repository.c index 935c1665f..26ee8a952 100644 --- a/src/repository.c +++ b/src/repository.c @@ -431,7 +431,7 @@ Repository_write(Repository *self, PyObject *args) return Error_set(err); stream->write(stream, buffer, buflen); - err = stream->finalize_write(&oid, stream); + err = stream->finalize_write(stream, &oid); stream->free(stream); return git_oid_to_python(&oid); } From 68968ad3af356df680dc66a0aa8f5574c957ab6e Mon Sep 17 00:00:00 2001 From: xutao Date: Sat, 21 Sep 2013 00:58:34 +0800 Subject: [PATCH 47/49] Fix multivar interface. --- src/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.c b/src/config.c index 6eb16391d..e7e0a864c 100644 --- a/src/config.c +++ b/src/config.c @@ -370,7 +370,7 @@ Config_get_multivar(Config *self, PyObject *args) return NULL; list = PyList_New(0); - err = git_config_get_multivar(self->config, name, regex, + err = git_config_get_multivar_foreach(self->config, name, regex, Config_get_multivar_fn_wrapper, (void *)list); From 95b9f0ea72ab3de2586762417aba5afc5093accc Mon Sep 17 00:00:00 2001 From: xutao Date: Sat, 21 Sep 2013 00:58:54 +0800 Subject: [PATCH 48/49] Rename orphan to unborn. --- docs/references.rst | 2 +- src/repository.c | 10 +++++----- test/test_repository.py | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/references.rst b/docs/references.rst index 4db5eae7a..c3e4c3dd5 100644 --- a/docs/references.rst +++ b/docs/references.rst @@ -38,7 +38,7 @@ Example. These two lines are equivalent:: .. autoattribute:: pygit2.Repository.head .. autoattribute:: pygit2.Repository.head_is_detached -.. autoattribute:: pygit2.Repository.head_is_orphaned +.. autoattribute:: pygit2.Repository.head_is_unborn Branches ==================== diff --git a/src/repository.c b/src/repository.c index dd1e4e6b9..b8f18c162 100644 --- a/src/repository.c +++ b/src/repository.c @@ -208,14 +208,14 @@ Repository_head_is_detached__get__(Repository *self) } -PyDoc_STRVAR(Repository_head_is_orphaned__doc__, - "An orphan branch is one named from HEAD but which doesn't exist in the\n" +PyDoc_STRVAR(Repository_head_is_unborn__doc__, + "An unborn branch is one named from HEAD but which doesn't exist in the\n" "refs namespace, because it doesn't have any commit to point to."); PyObject * -Repository_head_is_orphaned__get__(Repository *self) +Repository_head_is_unborn__get__(Repository *self) { - if (git_repository_head_orphan(self->repo) > 0) + if (git_repository_head_unborn(self->repo) > 0) Py_RETURN_TRUE; Py_RETURN_FALSE; @@ -1460,7 +1460,7 @@ PyGetSetDef Repository_getseters[] = { GETTER(Repository, path), GETSET(Repository, head), GETTER(Repository, head_is_detached), - GETTER(Repository, head_is_orphaned), + GETTER(Repository, head_is_unborn), GETTER(Repository, is_empty), GETTER(Repository, is_bare), GETTER(Repository, config), diff --git a/test/test_repository.py b/test/test_repository.py index de7ac0b19..272ef388d 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -68,7 +68,7 @@ def test_head(self): head = self.repo.head self.assertEqual(HEAD_SHA, head.target.hex) self.assertEqual(type(head), Reference) - self.assertFalse(self.repo.head_is_orphaned) + self.assertFalse(self.repo.head_is_unborn) self.assertFalse(self.repo.head_is_detached) def test_read(self): @@ -305,7 +305,7 @@ def test_is_base(self): self.assertFalse(self.repo.is_bare) def test_head(self): - self.assertTrue(self.repo.head_is_orphaned) + self.assertTrue(self.repo.head_is_unborn) self.assertFalse(self.repo.head_is_detached) From 687d5dca2e9c214ecea5f2aaa5664d60dad65521 Mon Sep 17 00:00:00 2001 From: xutao Date: Fri, 27 Sep 2013 18:40:57 +0800 Subject: [PATCH 49/49] Fix repository.write --- src/repository.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/repository.c b/src/repository.c index b8f18c162..28a242347 100644 --- a/src/repository.c +++ b/src/repository.c @@ -430,9 +430,10 @@ Repository_write(Repository *self, PyObject *args) if (err < 0) return Error_set(err); - stream->write(stream, buffer, buflen); - err = stream->finalize_write(stream, &oid); - stream->free(stream); + err = git_odb_stream_write(stream, buffer, buflen); + if (!err) + err = git_odb_stream_finalize_write(&oid, stream); + git_odb_stream_free(stream); return git_oid_to_python(&oid); }