diff --git a/pygit2/decl.h b/pygit2/decl.h index 5573ecd83..4e3df66b5 100644 --- a/pygit2/decl.h +++ b/pygit2/decl.h @@ -136,6 +136,7 @@ int git_remote_add_push(git_remote *remote, const char *refspec); int git_remote_add_fetch(git_remote *remote, const char *refspec); int git_remote_save(const git_remote *remote); int git_remote_set_callbacks(git_remote *remote, const git_remote_callbacks *callbacks); +int git_remote_init_callbacks(git_remote_callbacks *opts, unsigned int version); size_t git_remote_refspec_count(git_remote *remote); const git_refspec * git_remote_get_refspec(git_remote *remote, size_t n); diff --git a/pygit2/remote.py b/pygit2/remote.py index b2fdec24d..821c9fe5b 100644 --- a/pygit2/remote.py +++ b/pygit2/remote.py @@ -127,20 +127,6 @@ def __init__(self, repo, ptr): self._remote = ptr self._stored_exception = None - # Build the callback structure - callbacks = ffi.new('git_remote_callbacks *') - callbacks.version = 1 - callbacks.sideband_progress = self._sideband_progress_cb - callbacks.transfer_progress = self._transfer_progress_cb - callbacks.update_tips = self._update_tips_cb - callbacks.credentials = self._credentials_cb - # We need to make sure that this handle stays alive - self._self_handle = ffi.new_handle(self) - callbacks.payload = self._self_handle - - err = C.git_remote_set_callbacks(self._remote, callbacks) - check_error(err) - def __del__(self): C.git_remote_free(self._remote) @@ -205,17 +191,46 @@ def fetch(self, signature=None, message=None): Perform a fetch against this remote. """ + # Get the default callbacks first + defaultcallbacks = ffi.new('git_remote_callbacks *') + err = C.git_remote_init_callbacks(defaultcallbacks, 1) + check_error(err) + + # Build custom callback structure + callbacks = ffi.new('git_remote_callbacks *') + callbacks.version = 1 + callbacks.sideband_progress = self._sideband_progress_cb + callbacks.transfer_progress = self._transfer_progress_cb + callbacks.update_tips = self._update_tips_cb + callbacks.credentials = self._credentials_cb + # We need to make sure that this handle stays alive + self._self_handle = ffi.new_handle(self) + callbacks.payload = self._self_handle + + err = C.git_remote_set_callbacks(self._remote, callbacks) + try: + check_error(err) + finally: + self._self_handle = None + if signature: ptr = signature._pointer[:] else: ptr = ffi.NULL self._stored_exception = None - err = C.git_remote_fetch(self._remote, ptr, to_bytes(message)) - if self._stored_exception: - raise self._stored_exception - check_error(err) + try: + err = C.git_remote_fetch(self._remote, ptr, to_bytes(message)) + if self._stored_exception: + raise self._stored_exception + + check_error(err) + finally: + # Even on error, clear stored callbacks and reset to default + self._self_handle = None + err = C.git_remote_set_callbacks(self._remote, defaultcallbacks) + check_error(err) return TransferProgress(C.git_remote_stats(self._remote)) diff --git a/test/test_commit.py b/test/test_commit.py index f7a42b680..00f81cd19 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -30,6 +30,7 @@ from __future__ import absolute_import from __future__ import unicode_literals import unittest +import sys from pygit2 import GIT_OBJ_COMMIT, Signature, Oid from . import utils @@ -46,6 +47,16 @@ class CommitTest(utils.BareRepoTestCase): + @unittest.skipIf(__pypy__ is not None, "skip refcounts checks in pypy") + def test_commit_refcount(self): + commit = self.repo[COMMIT_SHA] + start = sys.getrefcount(commit) + tree = commit.tree + del tree + end = sys.getrefcount(commit) + self.assertEqual(start, end) + + def test_read_commit(self): commit = self.repo[COMMIT_SHA] self.assertEqual(COMMIT_SHA, str(commit.id)) diff --git a/test/test_remote.py b/test/test_remote.py index 57814a386..d98527b64 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -30,9 +30,15 @@ import unittest import pygit2 +import sys from pygit2 import Oid from . import utils +try: + import __pypy__ +except ImportError: + __pypy__ = None + REMOTE_NAME = 'origin' REMOTE_URL = 'git://github.com/libgit2/pygit2.git' REMOTE_FETCHSPEC_SRC = 'refs/heads/*' @@ -163,6 +169,14 @@ def test_remote_save(self): self.assertEqual('http://example.com/test.git', self.repo.remotes[0].url) + @unittest.skipIf(__pypy__ is not None, "skip refcounts checks in pypy") + def test_remote_refcount(self): + start = sys.getrefcount(self.repo) + remote = self.repo.remotes[0] + del remote + end = sys.getrefcount(self.repo) + self.assertEqual(start, end) + def test_add_refspec(self): remote = self.repo.create_remote('test_add_refspec', REMOTE_URL) remote.add_push('refs/heads/*:refs/heads/test_refspec/*') diff --git a/test/test_repository.py b/test/test_repository.py index cda846c08..c37b1309f 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -37,6 +37,7 @@ import tempfile import os from os.path import join, realpath +import sys # Import from pygit2 from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT @@ -45,6 +46,11 @@ import pygit2 from . import utils +try: + import __pypy__ +except ImportError: + __pypy__ = None + HEAD_SHA = '784855caf26449a1914d2cf62d12b9374d76ae78' PARENT_SHA = 'f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87' # HEAD^ @@ -150,6 +156,15 @@ def test_lookup_commit_prefix(self): commit.message) self.assertRaises(ValueError, self.repo.__getitem__, too_short_prefix) + @unittest.skipIf(__pypy__ is not None, "skip refcounts checks in pypy") + def test_lookup_commit_refcount(self): + start = sys.getrefcount(self.repo) + commit_sha = '5fe808e8953c12735680c257f56600cb0de44b10' + commit = self.repo[commit_sha] + del commit + end = sys.getrefcount(self.repo) + self.assertEqual(start, end) + def test_get_path(self): directory = realpath(self.repo.path) expected = realpath(self.repo_path)