Skip to content

Commit 1ddf05a

Browse files
committedJul 4, 2011
Merge branch 'remote-fixes' into 0.3
2 parents e9bd048 + a92ab80 commit 1ddf05a

File tree

8 files changed

+562
-453
lines changed

8 files changed

+562
-453
lines changed
 

‎.gitmodules

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
[submodule "gitdb"]
2-
path = git/ext/gitdb
3-
url = git://github.com/gitpython-developers/gitdb.git
1+
[submodule "gitdb"]
2+
path = git/ext/gitdb
3+
url = git://github.com/gitpython-developers/gitdb.git

‎git/ext/gitdb

Submodule gitdb updated 1 file

‎git/refs/reference.py

+47-4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,18 @@
1313

1414
__all__ = ["Reference"]
1515

16+
#{ Utilities
17+
def require_remote_ref_path(func):
18+
"""A decorator raising a TypeError if we are not a valid remote, based on the path"""
19+
def wrapper(self, *args):
20+
if not self.path.startswith(self._remote_common_path_default + "/"):
21+
raise ValueError("ref path does not point to a remote reference: %s" % path)
22+
return func(self, *args)
23+
#END wrapper
24+
wrapper.__name__ = func.__name__
25+
return wrapper
26+
#}END utilites
27+
1628

1729
class Reference(SymbolicReference, LazyMixin, Iterable):
1830
"""Represents a named reference to any object. Subclasses may apply restrictions though,
@@ -22,20 +34,24 @@ class Reference(SymbolicReference, LazyMixin, Iterable):
2234
_resolve_ref_on_create = True
2335
_common_path_default = "refs"
2436

25-
def __init__(self, repo, path):
37+
def __init__(self, repo, path, check_path = True):
2638
"""Initialize this instance
2739
:param repo: Our parent repository
2840
2941
:param path:
3042
Path relative to the .git/ directory pointing to the ref in question, i.e.
31-
refs/heads/master"""
32-
if not path.startswith(self._common_path_default+'/'):
33-
raise ValueError("Cannot instantiate %r from path %s" % ( self.__class__.__name__, path ))
43+
refs/heads/master
44+
:param check_path: if False, you can provide any path. Otherwise the path must start with the
45+
default path prefix of this type."""
46+
if check_path and not path.startswith(self._common_path_default+'/'):
47+
raise ValueError("Cannot instantiate %r from path %s" % (self.__class__.__name__, path))
3448
super(Reference, self).__init__(repo, path)
3549

3650

3751
def __str__(self):
3852
return self.name
53+
54+
#{ Interface
3955

4056
def set_object(self, object, logmsg = None):
4157
"""Special version which checks if the head-log needs an update as well"""
@@ -82,3 +98,30 @@ def iter_items(cls, repo, common_path = None):
8298
"""Equivalent to SymbolicReference.iter_items, but will return non-detached
8399
references as well."""
84100
return cls._iter_items(repo, common_path)
101+
102+
#}END interface
103+
104+
105+
#{ Remote Interface
106+
107+
@property
108+
@require_remote_ref_path
109+
def remote_name(self):
110+
"""
111+
:return:
112+
Name of the remote we are a reference of, such as 'origin' for a reference
113+
named 'origin/master'"""
114+
tokens = self.path.split('/')
115+
# /refs/remotes/<remote name>/<branch_name>
116+
return tokens[2]
117+
118+
@property
119+
@require_remote_ref_path
120+
def remote_head(self):
121+
""":return: Name of the remote head itself, i.e. master.
122+
:note: The returned name is usually not qualified enough to uniquely identify
123+
a branch"""
124+
tokens = self.path.split('/')
125+
return '/'.join(tokens[3:])
126+
127+
#} END remote interface

‎git/refs/remote.py

+1-19
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
class RemoteReference(Head):
1212
"""Represents a reference pointing to a remote head."""
13-
_common_path_default = "refs/remotes"
13+
_common_path_default = Head._remote_common_path_default
1414

1515

1616
@classmethod
@@ -22,24 +22,6 @@ def iter_items(cls, repo, common_path = None, remote=None):
2222
# END handle remote constraint
2323
return super(RemoteReference, cls).iter_items(repo, common_path)
2424

25-
@property
26-
def remote_name(self):
27-
"""
28-
:return:
29-
Name of the remote we are a reference of, such as 'origin' for a reference
30-
named 'origin/master'"""
31-
tokens = self.path.split('/')
32-
# /refs/remotes/<remote name>/<branch_name>
33-
return tokens[2]
34-
35-
@property
36-
def remote_head(self):
37-
""":return: Name of the remote head itself, i.e. master.
38-
:note: The returned name is usually not qualified enough to uniquely identify
39-
a branch"""
40-
tokens = self.path.split('/')
41-
return '/'.join(tokens[3:])
42-
4325
@classmethod
4426
def delete(cls, repo, *refs, **kwargs):
4527
"""Delete the given remote references.

‎git/refs/symbolic.py

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class SymbolicReference(object):
3333
_resolve_ref_on_create = False
3434
_points_to_commits_only = True
3535
_common_path_default = ""
36+
_remote_common_path_default = "refs/remotes"
3637
_id_attribute_ = "name"
3738

3839
def __init__(self, repo, path):

‎git/remote.py

+28-2
Original file line numberDiff line numberDiff line change
@@ -271,18 +271,44 @@ def _from_line(cls, repo, line, fetch_line):
271271
ref_type = None
272272
if remote_local_ref == "FETCH_HEAD":
273273
ref_type = SymbolicReference
274-
elif ref_type_name == "branch":
274+
elif ref_type_name in ("remote-tracking", "branch"):
275+
# note: remote-tracking is just the first part of the 'remote-tracking branch' token.
276+
# We don't parse it correctly, but its enough to know what to do, and its new in git 1.7something
275277
ref_type = RemoteReference
276278
elif ref_type_name == "tag":
277279
ref_type = TagReference
278280
else:
279281
raise TypeError("Cannot handle reference type: %r" % ref_type_name)
282+
#END handle ref type
280283

281284
# create ref instance
282285
if ref_type is SymbolicReference:
283286
remote_local_ref = ref_type(repo, "FETCH_HEAD")
284287
else:
285-
remote_local_ref = Reference.from_path(repo, join_path(ref_type._common_path_default, remote_local_ref.strip()))
288+
# determine prefix. Tags are usually pulled into refs/tags, they may have subdirectories.
289+
# It is not clear sometimes where exactly the item is, unless we have an absolute path as indicated
290+
# by the 'ref/' prefix. Otherwise even a tag could be in refs/remotes, which is when it will have the
291+
# 'tags/' subdirectory in its path.
292+
# We don't want to test for actual existence, but try to figure everything out analytically.
293+
ref_path = None
294+
remote_local_ref = remote_local_ref.strip()
295+
if remote_local_ref.startswith(Reference._common_path_default + "/"):
296+
# always use actual type if we get absolute paths
297+
# Will always be the case if something is fetched outside of refs/remotes (if its not a tag)
298+
ref_path = remote_local_ref
299+
if ref_type is not TagReference and not remote_local_ref.startswith(RemoteReference._common_path_default + "/"):
300+
ref_type = Reference
301+
#END downgrade remote reference
302+
elif ref_type is TagReference and 'tags/' in remote_local_ref:
303+
# even though its a tag, it is located in refs/remotes
304+
ref_path = join_path(RemoteReference._common_path_default, remote_local_ref)
305+
else:
306+
ref_path = join_path(ref_type._common_path_default, remote_local_ref)
307+
#END obtain refpath
308+
309+
# even though the path could be within the git conventions, we make
310+
# sure we respect whatever the user wanted, and disabled path checking
311+
remote_local_ref = ref_type(repo, ref_path, check_path=False)
286312
# END create ref instance
287313

288314
note = ( note and note.strip() ) or ''

‎git/test/test_refs.py

+5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ def test_from_path(self):
2424
assert isinstance(instance, ref_type)
2525
# END for each name
2626
# END for each type
27+
28+
# invalid path
29+
self.failUnlessRaises(ValueError, TagReference, self.rorepo, "refs/invalid/tag")
30+
# works without path check
31+
TagReference(self.rorepo, "refs/invalid/tag", check_path=False)
2732

2833
def test_tag_base(self):
2934
tag_object_refs = list()

‎git/test/test_remote.py

+476-424
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.