Skip to content

Commit 1ddf05a

Browse files
committed
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

Lines changed: 3 additions & 3 deletions
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

Lines changed: 47 additions & 4 deletions
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

Lines changed: 1 addition & 19 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 28 additions & 2 deletions
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

Lines changed: 5 additions & 0 deletions
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()

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy