Skip to content

Commit dad9bc3

Browse files
committed
Merge remote-tracking branch 'mduggan/remote-refcount-fix'
2 parents 82b3429 + 2f2d400 commit dad9bc3

File tree

5 files changed

+74
-18
lines changed

5 files changed

+74
-18
lines changed

pygit2/decl.h

+1
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ int git_remote_add_push(git_remote *remote, const char *refspec);
136136
int git_remote_add_fetch(git_remote *remote, const char *refspec);
137137
int git_remote_save(const git_remote *remote);
138138
int git_remote_set_callbacks(git_remote *remote, const git_remote_callbacks *callbacks);
139+
int git_remote_init_callbacks(git_remote_callbacks *opts, unsigned int version);
139140
size_t git_remote_refspec_count(git_remote *remote);
140141
const git_refspec * git_remote_get_refspec(git_remote *remote, size_t n);
141142

pygit2/remote.py

+33-18
Original file line numberDiff line numberDiff line change
@@ -127,20 +127,6 @@ def __init__(self, repo, ptr):
127127
self._remote = ptr
128128
self._stored_exception = None
129129

130-
# Build the callback structure
131-
callbacks = ffi.new('git_remote_callbacks *')
132-
callbacks.version = 1
133-
callbacks.sideband_progress = self._sideband_progress_cb
134-
callbacks.transfer_progress = self._transfer_progress_cb
135-
callbacks.update_tips = self._update_tips_cb
136-
callbacks.credentials = self._credentials_cb
137-
# We need to make sure that this handle stays alive
138-
self._self_handle = ffi.new_handle(self)
139-
callbacks.payload = self._self_handle
140-
141-
err = C.git_remote_set_callbacks(self._remote, callbacks)
142-
check_error(err)
143-
144130
def __del__(self):
145131
C.git_remote_free(self._remote)
146132

@@ -205,17 +191,46 @@ def fetch(self, signature=None, message=None):
205191
Perform a fetch against this remote.
206192
"""
207193

194+
# Get the default callbacks first
195+
defaultcallbacks = ffi.new('git_remote_callbacks *')
196+
err = C.git_remote_init_callbacks(defaultcallbacks, 1)
197+
check_error(err)
198+
199+
# Build custom callback structure
200+
callbacks = ffi.new('git_remote_callbacks *')
201+
callbacks.version = 1
202+
callbacks.sideband_progress = self._sideband_progress_cb
203+
callbacks.transfer_progress = self._transfer_progress_cb
204+
callbacks.update_tips = self._update_tips_cb
205+
callbacks.credentials = self._credentials_cb
206+
# We need to make sure that this handle stays alive
207+
self._self_handle = ffi.new_handle(self)
208+
callbacks.payload = self._self_handle
209+
210+
err = C.git_remote_set_callbacks(self._remote, callbacks)
211+
try:
212+
check_error(err)
213+
finally:
214+
self._self_handle = None
215+
208216
if signature:
209217
ptr = signature._pointer[:]
210218
else:
211219
ptr = ffi.NULL
212220

213221
self._stored_exception = None
214-
err = C.git_remote_fetch(self._remote, ptr, to_bytes(message))
215-
if self._stored_exception:
216-
raise self._stored_exception
217222

218-
check_error(err)
223+
try:
224+
err = C.git_remote_fetch(self._remote, ptr, to_bytes(message))
225+
if self._stored_exception:
226+
raise self._stored_exception
227+
228+
check_error(err)
229+
finally:
230+
# Even on error, clear stored callbacks and reset to default
231+
self._self_handle = None
232+
err = C.git_remote_set_callbacks(self._remote, defaultcallbacks)
233+
check_error(err)
219234

220235
return TransferProgress(C.git_remote_stats(self._remote))
221236

test/test_commit.py

+11
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from __future__ import absolute_import
3131
from __future__ import unicode_literals
3232
import unittest
33+
import sys
3334

3435
from pygit2 import GIT_OBJ_COMMIT, Signature, Oid
3536
from . import utils
@@ -46,6 +47,16 @@
4647

4748
class CommitTest(utils.BareRepoTestCase):
4849

50+
@unittest.skipIf(__pypy__ is not None, "skip refcounts checks in pypy")
51+
def test_commit_refcount(self):
52+
commit = self.repo[COMMIT_SHA]
53+
start = sys.getrefcount(commit)
54+
tree = commit.tree
55+
del tree
56+
end = sys.getrefcount(commit)
57+
self.assertEqual(start, end)
58+
59+
4960
def test_read_commit(self):
5061
commit = self.repo[COMMIT_SHA]
5162
self.assertEqual(COMMIT_SHA, str(commit.id))

test/test_remote.py

+14
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,15 @@
3030

3131
import unittest
3232
import pygit2
33+
import sys
3334
from pygit2 import Oid
3435
from . import utils
3536

37+
try:
38+
import __pypy__
39+
except ImportError:
40+
__pypy__ = None
41+
3642
REMOTE_NAME = 'origin'
3743
REMOTE_URL = 'git://github.com/libgit2/pygit2.git'
3844
REMOTE_FETCHSPEC_SRC = 'refs/heads/*'
@@ -163,6 +169,14 @@ def test_remote_save(self):
163169
self.assertEqual('http://example.com/test.git',
164170
self.repo.remotes[0].url)
165171

172+
@unittest.skipIf(__pypy__ is not None, "skip refcounts checks in pypy")
173+
def test_remote_refcount(self):
174+
start = sys.getrefcount(self.repo)
175+
remote = self.repo.remotes[0]
176+
del remote
177+
end = sys.getrefcount(self.repo)
178+
self.assertEqual(start, end)
179+
166180
def test_add_refspec(self):
167181
remote = self.repo.create_remote('test_add_refspec', REMOTE_URL)
168182
remote.add_push('refs/heads/*:refs/heads/test_refspec/*')

test/test_repository.py

+15
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import tempfile
3838
import os
3939
from os.path import join, realpath
40+
import sys
4041

4142
# Import from pygit2
4243
from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT
@@ -45,6 +46,11 @@
4546
import pygit2
4647
from . import utils
4748

49+
try:
50+
import __pypy__
51+
except ImportError:
52+
__pypy__ = None
53+
4854

4955
HEAD_SHA = '784855caf26449a1914d2cf62d12b9374d76ae78'
5056
PARENT_SHA = 'f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87' # HEAD^
@@ -150,6 +156,15 @@ def test_lookup_commit_prefix(self):
150156
commit.message)
151157
self.assertRaises(ValueError, self.repo.__getitem__, too_short_prefix)
152158

159+
@unittest.skipIf(__pypy__ is not None, "skip refcounts checks in pypy")
160+
def test_lookup_commit_refcount(self):
161+
start = sys.getrefcount(self.repo)
162+
commit_sha = '5fe808e8953c12735680c257f56600cb0de44b10'
163+
commit = self.repo[commit_sha]
164+
del commit
165+
end = sys.getrefcount(self.repo)
166+
self.assertEqual(start, end)
167+
153168
def test_get_path(self):
154169
directory = realpath(self.repo.path)
155170
expected = realpath(self.repo_path)

0 commit comments

Comments
 (0)