| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent b2a14e4 commit 58e2157
9 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -87,9 +87,12 @@ Index | |||
| 87 | 87 | * A new Index class allows to read and write index files directly, and to perform | |
| 88 | 88 | simple two and three way merges based on an arbitrary index. | |
| 89 | 89 | ||
| 90 | - Refs | ||
| 91 | - ---- | ||
| 92 | - * Will dynmically retrieve their object at the time of query to assure the information | ||
| 90 | + Referernces | ||
| 91 | + ------------ | ||
| 92 | + * References are object that point to a Commit | ||
| 93 | + * SymbolicReference are a pointer to a Reference Object, which itself points to a specific | ||
| 94 | + Commit | ||
| 95 | + * They will dynmically retrieve their object at the time of query to assure the information | ||
| 93 | 96 | is actual. Recently objects would be cached, hence ref object not be safely kept | |
| 94 | 97 | persistent. | |
| 95 | 98 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -58,14 +58,12 @@ Index | |||
| 58 | 58 | creating several tree objects, so in the end it might be slower. | |
| 59 | 59 | Hmm, probably its okay to use the command unless we go c(++) | |
| 60 | 60 | ||
| 61 | - | ||
| 62 | - Head.reset | ||
| 63 | - ---------- | ||
| 64 | - * Should better be an instance method. Problem was that there is no class specifying | ||
| 65 | - the HEAD - in a way reset would always effect the active branch. | ||
| 66 | - Probably it would be okay to have a special type called SymbolicReference | ||
| 67 | - which represents items like HEAD. These could naturally carry the reset | ||
| 68 | - instance method. | ||
| 61 | + Refs | ||
| 62 | + ----- | ||
| 63 | + * If the HEAD is detached as it points to a specific commit, its not technically | ||
| 64 | + a symbolic reference anymore. Currently, we cannot handle this that well | ||
| 65 | + as we do not check for this case. This should be added though as it is | ||
| 66 | + valid to have a detached head in some cases. | ||
| 69 | 67 | ||
| 70 | 68 | Remote | |
| 71 | 69 | ------ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,6 +6,7 @@ | |||
| 6 | 6 | """ | |
| 7 | 7 | Module containing all ref based objects | |
| 8 | 8 | """ | |
| 9 | + import os | ||
| 9 | 10 | from objects.base import Object | |
| 10 | 11 | from objects.utils import get_object_type_by_name | |
| 11 | 12 | from utils import LazyMixin, Iterable | |
@@ -31,6 +32,9 @@ def __init__(self, repo, path, object = None): | |||
| 31 | 32 | ``object`` | |
| 32 | 33 | Object instance, will be retrieved on demand if None | |
| 33 | 34 | """ | |
| 35 | + if not path.startswith(self._common_path_default): | ||
| 36 | + raise ValueError("Cannot instantiate %s Reference from path %s" % ( self.__class__.__name__, path )) | ||
| 37 | + | ||
| 34 | 38 | self.repo = repo | |
| 35 | 39 | self.path = path | |
| 36 | 40 | if object is not None: | |
@@ -75,6 +79,17 @@ def object(self): | |||
| 75 | 79 | # have to be dynamic here as we may be a tag which can point to anything | |
| 76 | 80 | # Our path will be resolved to the hexsha which will be used accordingly | |
| 77 | 81 | return Object.new(self.repo, self.path) | |
| 82 | + | ||
| 83 | + @property | ||
| 84 | + def commit(self): | ||
| 85 | + """ | ||
| 86 | + Returns | ||
| 87 | + Commit object the head points to | ||
| 88 | + """ | ||
| 89 | + commit = self.object | ||
| 90 | + if commit.type != "commit": | ||
| 91 | + raise TypeError("Object of reference %s did not point to a commit" % self) | ||
| 92 | + return commit | ||
| 78 | 93 | ||
| 79 | 94 | @classmethod | |
| 80 | 95 | def iter_items(cls, repo, common_path = None, **kwargs): | |
@@ -112,6 +127,29 @@ def iter_items(cls, repo, common_path = None, **kwargs): | |||
| 112 | 127 | ||
| 113 | 128 | output = repo.git.for_each_ref(common_path, **options) | |
| 114 | 129 | return cls._iter_from_stream(repo, iter(output.splitlines())) | |
| 130 | + | ||
| 131 | + @classmethod | ||
| 132 | + def from_path(cls, repo, path): | ||
| 133 | + """ | ||
| 134 | + Return | ||
| 135 | + Instance of type Reference, Head, Tag, SymbolicReference or HEAD | ||
| 136 | + depending on the given path | ||
| 137 | + """ | ||
| 138 | + if path == 'HEAD': | ||
| 139 | + return HEAD(repo, path) | ||
| 140 | + | ||
| 141 | + if '/' not in path: | ||
| 142 | + return SymbolicReference(repo, path) | ||
| 143 | + | ||
| 144 | + for ref_type in (Head, RemoteReference, TagReference, Reference): | ||
| 145 | + try: | ||
| 146 | + return ref_type(repo, path) | ||
| 147 | + except ValueError: | ||
| 148 | + pass | ||
| 149 | + # END exception handling | ||
| 150 | + # END for each type to try | ||
| 151 | + raise ValueError("Could not find reference type suitable to handle path %r" % path) | ||
| 152 | + | ||
| 115 | 153 | ||
| 116 | 154 | @classmethod | |
| 117 | 155 | def _iter_from_stream(cls, repo, stream): | |
@@ -145,47 +183,91 @@ def _from_string(cls, repo, line): | |||
| 145 | 183 | # return cls(repo, full_path, obj) | |
| 146 | 184 | ||
| 147 | 185 | ||
| 148 | - class Head(Reference): | ||
| 186 | + class SymbolicReference(object): | ||
| 149 | 187 | """ | |
| 150 | - A Head is a named reference to a Commit. Every Head instance contains a name | ||
| 151 | - and a Commit object. | ||
| 152 | - | ||
| 153 | - Examples:: | ||
| 154 | - | ||
| 155 | - >>> repo = Repo("/path/to/repo") | ||
| 156 | - >>> head = repo.heads[0] | ||
| 157 | - | ||
| 158 | - >>> head.name | ||
| 159 | - 'master' | ||
| 160 | - | ||
| 161 | - >>> head.commit | ||
| 162 | - <git.Commit "1c09f116cbc2cb4100fb6935bb162daa4723f455"> | ||
| 163 | - | ||
| 164 | - >>> head.commit.id | ||
| 165 | - '1c09f116cbc2cb4100fb6935bb162daa4723f455' | ||
| 188 | + Represents a special case of a reference such that this reference is symbolic. | ||
| 189 | + It does not point to a specific commit, but to another Head, which itself | ||
| 190 | + specifies a commit. | ||
| 191 | + | ||
| 192 | + A typical example for a symbolic reference is HEAD. | ||
| 166 | 193 | """ | |
| 167 | - _common_path_default = "refs/heads" | ||
| 194 | + __slots__ = ("repo", "name") | ||
| 168 | 195 | ||
| 196 | + def __init__(self, repo, name): | ||
| 197 | + if '/' in name: | ||
| 198 | + raise ValueError("SymbolicReferences are not located within a directory, got %s" % name) | ||
| 199 | + self.repo = repo | ||
| 200 | + self.name = name | ||
| 201 | + | ||
| 202 | + def __str__(self): | ||
| 203 | + return self.name | ||
| 204 | + | ||
| 205 | + def __repr__(self): | ||
| 206 | + return '<git.%s "%s">' % (self.__class__.__name__, self.name) | ||
| 207 | + | ||
| 208 | + def __eq__(self, other): | ||
| 209 | + return self.name == other.name | ||
| 210 | + | ||
| 211 | + def __ne__(self, other): | ||
| 212 | + return not ( self == other ) | ||
| 213 | + | ||
| 214 | + def __hash__(self): | ||
| 215 | + return hash(self.name) | ||
| 216 | + | ||
| 169 | 217 | @property | |
| 170 | - def commit(self): | ||
| 218 | + def reference(self): | ||
| 171 | 219 | """ | |
| 172 | 220 | Returns | |
| 173 | - Commit object the head points to | ||
| 221 | + Reference Object we point to | ||
| 174 | 222 | """ | |
| 175 | - return self.object | ||
| 223 | + fp = open(os.path.join(self.repo.path, self.name), 'r') | ||
| 224 | + try: | ||
| 225 | + tokens = fp.readline().split(' ') | ||
| 226 | + if tokens[0] != 'ref:': | ||
| 227 | + raise TypeError("%s is a detached symbolic reference as it points to %r" % tokens[0]) | ||
| 228 | + return Reference.from_path(self.repo, tokens[1]) | ||
| 229 | + finally: | ||
| 230 | + fp.close() | ||
| 176 | 231 | ||
| 177 | - @classmethod | ||
| 178 | - def reset(cls, repo, commit='HEAD', index=True, working_tree = False, | ||
| 232 | + # alias | ||
| 233 | + ref = reference | ||
| 234 | + | ||
| 235 | + @property | ||
| 236 | + def is_detached(self): | ||
| 237 | + """ | ||
| 238 | + Returns | ||
| 239 | + True if we are a detached reference, hence we point to a specific commit | ||
| 240 | + instead to another reference | ||
| 241 | + """ | ||
| 242 | + try: | ||
| 243 | + self.reference | ||
| 244 | + return False | ||
| 245 | + except TypeError: | ||
| 246 | + return True | ||
| 247 | + | ||
| 248 | + | ||
| 249 | + class HEAD(SymbolicReference): | ||
| 250 | + """ | ||
| 251 | + Special case of a Symbolic Reference as it represents the repository's | ||
| 252 | + HEAD reference. | ||
| 253 | + """ | ||
| 254 | + __slots__ = tuple() | ||
| 255 | + | ||
| 256 | + def __init__(self, repo, name): | ||
| 257 | + if name != 'HEAD': | ||
| 258 | + raise ValueError("HEAD instance must point to 'HEAD', got %s" % name) | ||
| 259 | + super(HEAD, self).__init__(repo, name) | ||
| 260 | + | ||
| 261 | + | ||
| 262 | + def reset(self, commit='HEAD', index=True, working_tree = False, | ||
| 179 | 263 | paths=None, **kwargs): | |
| 180 | 264 | """ | |
| 181 | - Reset the current head to the given commit optionally synchronizing | ||
| 265 | + Reset our HEAD to the given commit optionally synchronizing | ||
| 182 | 266 | the index and working tree. | |
| 183 | 267 | ||
| 184 | - ``repo`` | ||
| 185 | - Repository containing commit | ||
| 186 | - | ||
| 187 | 268 | ``commit`` | |
| 188 | - Commit object, Reference Object or string identifying a revision | ||
| 269 | + Commit object, Reference Object or string identifying a revision we | ||
| 270 | + should reset HEAD to. | ||
| 189 | 271 | ||
| 190 | 272 | ``index`` | |
| 191 | 273 | If True, the index will be set to match the given commit. Otherwise | |
@@ -204,7 +286,7 @@ def reset(cls, repo, commit='HEAD', index=True, working_tree = False, | |||
| 204 | 286 | Additional arguments passed to git-reset. | |
| 205 | 287 | ||
| 206 | 288 | Returns | |
| 207 | - Head pointing to the specified commit | ||
| 289 | + self | ||
| 208 | 290 | """ | |
| 209 | 291 | mode = "--soft" | |
| 210 | 292 | if index: | |
@@ -219,9 +301,32 @@ def reset(cls, repo, commit='HEAD', index=True, working_tree = False, | |||
| 219 | 301 | repo.git.reset(mode, commit, paths, **kwargs) | |
| 220 | 302 | ||
| 221 | 303 | # we always point to the active branch as it is the one changing | |
| 222 | - return repo.active_branch | ||
| 304 | + self | ||
| 305 | + | ||
| 306 | + | ||
| 307 | + class Head(Reference): | ||
| 308 | + """ | ||
| 309 | + A Head is a named reference to a Commit. Every Head instance contains a name | ||
| 310 | + and a Commit object. | ||
| 311 | + | ||
| 312 | + Examples:: | ||
| 313 | + | ||
| 314 | + >>> repo = Repo("/path/to/repo") | ||
| 315 | + >>> head = repo.heads[0] | ||
| 316 | + | ||
| 317 | + >>> head.name | ||
| 318 | + 'master' | ||
| 319 | + | ||
| 320 | + >>> head.commit | ||
| 321 | + <git.Commit "1c09f116cbc2cb4100fb6935bb162daa4723f455"> | ||
| 322 | + | ||
| 323 | + >>> head.commit.id | ||
| 324 | + '1c09f116cbc2cb4100fb6935bb162daa4723f455' | ||
| 325 | + """ | ||
| 326 | + _common_path_default = "refs/heads" | ||
| 327 | + | ||
| 223 | 328 | ||
| 224 | - class TagReference(Head): | ||
| 329 | + class TagReference(Reference): | ||
| 225 | 330 | """ | |
| 226 | 331 | Class representing a lightweight tag reference which either points to a commit | |
| 227 | 332 | or to a tag object. In the latter case additional information, like the signature | |
@@ -230,7 +335,7 @@ class TagReference(Head): | |||
| 230 | 335 | This tag object will always point to a commit object, but may carray additional | |
| 231 | 336 | information in a tag object:: | |
| 232 | 337 | ||
| 233 | - tagref = TagRef.list_items(repo)[0] | ||
| 338 | + tagref = TagReference.list_items(repo)[0] | ||
| 234 | 339 | print tagref.commit.message | |
| 235 | 340 | if tagref.tag is not None: | |
| 236 | 341 | print tagref.tag.message | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -147,15 +147,12 @@ def heads(self): | |||
| 147 | 147 | branches = heads | |
| 148 | 148 | ||
| 149 | 149 | @property | |
| 150 | - def head(self,path="HEAD"): | ||
| 150 | + def head(self): | ||
| 151 | 151 | """ | |
| 152 | 152 | Return | |
| 153 | - Head Object, reference pointing to commit | ||
| 154 | - | ||
| 155 | - ``path`` | ||
| 156 | - path to the head or its name, i.e. master or heads/master | ||
| 153 | + HEAD Object pointing to the current head reference | ||
| 157 | 154 | """ | |
| 158 | - return Head(self,path) | ||
| 155 | + return HEAD(self,'HEAD') | ||
| 159 | 156 | ||
| 160 | 157 | @property | |
| 161 | 158 | def remotes(self): | |
@@ -486,8 +483,7 @@ def active_branch(self): | |||
| 486 | 483 | Returns | |
| 487 | 484 | Head to the active branch | |
| 488 | 485 | """ | |
| 489 | - return Head( self, self.git.symbolic_ref('HEAD').strip() ) | ||
| 490 | - | ||
| 486 | + return self.head.reference | ||
| 491 | 487 | ||
| 492 | 488 | def blame(self, rev, file): | |
| 493 | 489 | """ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -66,50 +66,6 @@ def test_base_object(self): | |||
| 66 | 66 | assert len(s|s) == num_objs | |
| 67 | 67 | assert num_index_objs == 2 | |
| 68 | 68 | ||
| 69 | - | ||
| 70 | - def test_tags(self): | ||
| 71 | - # tag refs can point to tag objects or to commits | ||
| 72 | - s = set() | ||
| 73 | - ref_count = 0 | ||
| 74 | - for ref in chain(self.rorepo.tags, self.rorepo.heads): | ||
| 75 | - ref_count += 1 | ||
| 76 | - assert isinstance(ref, refs.Reference) | ||
| 77 | - assert str(ref) == ref.name | ||
| 78 | - assert repr(ref) | ||
| 79 | - assert ref == ref | ||
| 80 | - assert not ref != ref | ||
| 81 | - s.add(ref) | ||
| 82 | - # END for each ref | ||
| 83 | - assert len(s) == ref_count | ||
| 84 | - assert len(s|s) == ref_count | ||
| 85 | - | ||
| 86 | - def test_heads(self): | ||
| 87 | - # see how it dynmically updates its object | ||
| 88 | - for head in self.rorepo.heads: | ||
| 89 | - head.name | ||
| 90 | - head.path | ||
| 91 | - prev_object = head.object | ||
| 92 | - cur_object = head.object | ||
| 93 | - assert prev_object == cur_object # represent the same git object | ||
| 94 | - assert prev_object is not cur_object # but are different instances | ||
| 95 | - # END for each head | ||
| 96 | - | ||
| 97 | - @with_rw_repo('0.1.6') | ||
| 98 | - def test_head_reset(self, rw_repo): | ||
| 99 | - cur_head = rw_repo.head | ||
| 100 | - new_head_commit = cur_head.commit.parents[0] | ||
| 101 | - reset_head = Head.reset(rw_repo, new_head_commit, index=True) # index only | ||
| 102 | - assert reset_head.commit == new_head_commit | ||
| 103 | - | ||
| 104 | - self.failUnlessRaises(ValueError, Head.reset, rw_repo, new_head_commit, index=False, working_tree=True) | ||
| 105 | - new_head_commit = new_head_commit.parents[0] | ||
| 106 | - reset_head = Head.reset(rw_repo, new_head_commit, index=True, working_tree=True) # index + wt | ||
| 107 | - assert reset_head.commit == new_head_commit | ||
| 108 | - | ||
| 109 | - # paths | ||
| 110 | - Head.reset(rw_repo, new_head_commit, paths = "lib") | ||
| 111 | - | ||
| 112 | - | ||
| 113 | 69 | def test_get_object_type_by_name(self): | |
| 114 | 70 | for tname in base.Object.TYPES: | |
| 115 | 71 | assert base.Object in get_object_type_by_name(tname).mro() | |
@@ -119,7 +75,7 @@ def test_get_object_type_by_name(self): | |||
| 119 | 75 | ||
| 120 | 76 | def test_object_resolution(self): | |
| 121 | 77 | # objects must be resolved to shas so they compare equal | |
| 122 | - assert self.rorepo.head.object == self.rorepo.active_branch.object | ||
| 78 | + assert self.rorepo.head.reference.object == self.rorepo.active_branch.object | ||
| 123 | 79 | ||
| 124 | 80 | @with_bare_rw_repo | |
| 125 | 81 | def test_with_bare_rw_repo(self, bare_rw_repo): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -64,7 +64,7 @@ def test_rev_list_bisect_all(self, git): | |||
| 64 | 64 | assert_equal(sha1, commit.id) | |
| 65 | 65 | ||
| 66 | 66 | def test_count(self): | |
| 67 | - assert self.rorepo.tag('0.1.5').commit.count( ) == 141 | ||
| 67 | + assert self.rorepo.tag('refs/tags/0.1.5').commit.count( ) == 141 | ||
| 68 | 68 | ||
| 69 | 69 | def test_list(self): | |
| 70 | 70 | assert isinstance(Commit.list_items(self.rorepo, '0.1.5', max_count=5)['5117c9c8a4d3af19a9958677e45cda9269de1541'], Commit) | |
| Back | FazBrowse Home | New Git URL |
0 commit comments