From bb24f67e64b4ebe11c4d3ce7df021a6ad7ca98f2 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 16 Oct 2009 16:09:07 +0200 Subject: Fixed object bug that would cause object ids not to be resolved to sha's as this was assumed - now there is a test for it as well repo: removed diff and commit_diff methods, added 'head' property returning the current head as Reference object --- lib/git/refs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index a4d7bbb1..4445f252 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -72,7 +72,7 @@ class Reference(LazyMixin, Iterable): """ # have to be dynamic here as we may be a tag which can point to anything # Our path will be resolved to the hexsha which will be used accordingly - return Object(self.repo, self.path) + return Object.new(self.repo, self.path) @classmethod def iter_items(cls, repo, common_path = "refs", **kwargs): -- cgit v1.2.1 From a8f8582274cd6a368a79e569e2995cee7d6ea9f9 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 18 Oct 2009 18:01:52 +0200 Subject: added initial frame for remote handling- remotes are somewhat related to either parsing the command output or to reading the repo configuration which would be faster --- lib/git/refs.py | 46 ++++++++++++++++++---------------------------- 1 file changed, 18 insertions(+), 28 deletions(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index 4445f252..1a58bb05 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -15,6 +15,7 @@ class Reference(LazyMixin, Iterable): Represents a named reference to any object """ __slots__ = ("repo", "path") + _common_path_default = "refs" def __init__(self, repo, path, object = None): """ @@ -75,7 +76,7 @@ class Reference(LazyMixin, Iterable): return Object.new(self.repo, self.path) @classmethod - def iter_items(cls, repo, common_path = "refs", **kwargs): + def iter_items(cls, repo, common_path = None, **kwargs): """ Find all refs in the repository @@ -84,7 +85,9 @@ class Reference(LazyMixin, Iterable): ``common_path`` Optional keyword argument to the path which is to be shared by all - returned Ref objects + returned Ref objects. + Defaults to class specific portion if None assuring that only + refs suitable for the actual class are returned. ``kwargs`` Additional options given as keyword arguments, will be passed @@ -100,7 +103,10 @@ class Reference(LazyMixin, Iterable): options = {'sort': "committerdate", 'format': "%(refname)%00%(objectname)%00%(objecttype)%00%(objectsize)"} - + + if common_path is None: + common_path = cls._common_path_default + options.update(kwargs) output = repo.git.for_each_ref(common_path, **options) @@ -157,7 +163,8 @@ class Head(Reference): >>> head.commit.id '1c09f116cbc2cb4100fb6935bb162daa4723f455' """ - + _common_path_default = "refs/heads" + @property def commit(self): """ @@ -166,20 +173,6 @@ class Head(Reference): """ return self.object - @classmethod - def iter_items(cls, repo, common_path = "refs/heads", **kwargs): - """ - Returns - Iterator yielding Head items - - For more documentation, please refer to git.base.Ref.list_items - """ - return super(Head,cls).iter_items(repo, common_path, **kwargs) - - def __repr__(self): - return '' % self.name - - class TagRef(Reference): """ @@ -197,6 +190,7 @@ class TagRef(Reference): """ __slots__ = tuple() + _common_path_default = "refs/tags" @property def commit(self): @@ -223,16 +217,12 @@ class TagRef(Reference): return self.object return None - @classmethod - def iter_items(cls, repo, common_path = "refs/tags", **kwargs): - """ - Returns - Iterator yielding commit items - - For more documentation, please refer to git.base.Ref.list_items - """ - return super(TagRef,cls).iter_items(repo, common_path, **kwargs) - # provide an alias Tag = TagRef + +class RemoteRef(Head): + """ + Represents a reference pointing to a remote head. + """ + _common_path_default = "refs/remotes" -- cgit v1.2.1 From a07cdbae1d485fd715a5b6eca767f211770fea4d Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 19 Oct 2009 18:06:19 +0200 Subject: Added remote module and test cases - about to implement remote option handling --- lib/git/refs.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index 1a58bb05..618babc2 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -226,3 +226,26 @@ class RemoteRef(Head): Represents a reference pointing to a remote head. """ _common_path_default = "refs/remotes" + + @property + def remote_name(self): + """ + Returns + Name of the remote we are a reference of, such as 'origin' for a reference + named 'origin/master' + """ + tokens = self.path.split('/') + # /refs/remotes// + return tokens[2] + + @property + def remote_branch(self): + """ + Returns + Name of the remote branch itself, i.e. master. + + NOTE: The returned name is usually not qualified enough to uniquely identify + a branch + """ + tokens = self.path.split('/') + return '/'.join(tokens[3:]) -- cgit v1.2.1 From 0b3ecf2dcace76b65765ddf1901504b0b4861b08 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 19 Oct 2009 22:49:52 +0200 Subject: commit.count: is an instance method now repo: added head , tag and iter_trees methods for completeness changes: headlines now sorted chronologically --- lib/git/refs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index 618babc2..3428915e 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -174,7 +174,7 @@ class Head(Reference): return self.object -class TagRef(Reference): +class TagReference(Head): """ Class representing a lightweight tag reference which either points to a commit or to a tag object. In the latter case additional information, like the signature @@ -219,9 +219,9 @@ class TagRef(Reference): # provide an alias -Tag = TagRef +Tag = TagReference -class RemoteRef(Head): +class RemoteReference(Head): """ Represents a reference pointing to a remote head. """ -- cgit v1.2.1 From 989671780551b7587d57e1d7cb5eb1002ade75b4 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 19 Oct 2009 23:44:18 +0200 Subject: Implemneted IterableLists for refs, commits and remote objects including simple tests --- lib/git/refs.py | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index 3428915e..8c9d6672 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -16,6 +16,7 @@ class Reference(LazyMixin, Iterable): """ __slots__ = ("repo", "path") _common_path_default = "refs" + _id_attribute_ = "name" def __init__(self, repo, path, object = None): """ -- cgit v1.2.1 From 20c34a929a8b2871edd4fd44a38688e8977a4be6 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 22 Oct 2009 17:40:04 +0200 Subject: =?UTF-8?q?Added=20reset=20method=20to=20Head=20-=20its=20a=20clas?= =?UTF-8?q?s=20method=20due=20to=20the=20very=20general=20nature=20of=20th?= =?UTF-8?q?e=20command.=20Yet=20I=20don't=20really=20like=20the=20way=20yo?= =?UTF-8?q?u=20have=20to=20call=20it=20as=20repo=20has=20to=20be=20?= =?UTF-8?q?=E1=B9=95assed=20as=20first=20arg?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/git/refs.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index 8c9d6672..9a03b6f5 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -174,6 +174,52 @@ class Head(Reference): """ return self.object + @classmethod + def reset(cls, repo, commit='HEAD', index=True, working_tree = False, + paths=None, **kwargs): + """ + Reset the current head to the given commit optionally synchronizing + the index and working tree. + + ``repo`` + Repository containing commit + + ``commit`` + Commit object, Reference Object or string identifying a revision + + ``index`` + If True, the index will be set to match the given commit. Otherwise + it will not be touched. + + ``working_tree`` + If True, the working tree will be forcefully adjusted to match the given + commit, possibly overwriting uncommitted changes without warning. + If working_tree is True, index must be true as well + + ``paths`` + Single path or list of paths relative to the git root directory + that are to be reset. This allow to partially reset individual files. + + ``kwargs`` + Additional arguments passed to git-reset. + + Returns + Head pointing to the specified commit + """ + mode = "--soft" + if index: + mode = "--mixed" + + if working_tree: + mode = "--hard" + if not index: + raise ValueError( "Cannot reset the working tree if the index is not reset as well") + # END working tree handling + + repo.git.reset(mode, commit, paths, **kwargs) + + # we always point to the active branch as it is the one changing + return repo.active_branch class TagReference(Head): """ -- cgit v1.2.1 From 58e2157ad3aa9d75ef4abb90eb2d1f01fba0ba2b Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 22 Oct 2009 23:20:16 +0200 Subject: Added SymbolicReference and HEAD type to better represent these special types of references and allow special handling Head.reset now is an instance method of HEAD type Concatenated all reference specific tests into test_refs started to fix tests breaking now because of changed interface --- lib/git/refs.py | 169 +++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 137 insertions(+), 32 deletions(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index 9a03b6f5..97d0a6eb 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -6,6 +6,7 @@ """ Module containing all ref based objects """ +import os from objects.base import Object from objects.utils import get_object_type_by_name from utils import LazyMixin, Iterable @@ -31,6 +32,9 @@ class Reference(LazyMixin, Iterable): ``object`` Object instance, will be retrieved on demand if None """ + if not path.startswith(self._common_path_default): + raise ValueError("Cannot instantiate %s Reference from path %s" % ( self.__class__.__name__, path )) + self.repo = repo self.path = path if object is not None: @@ -75,6 +79,17 @@ class Reference(LazyMixin, Iterable): # have to be dynamic here as we may be a tag which can point to anything # Our path will be resolved to the hexsha which will be used accordingly return Object.new(self.repo, self.path) + + @property + def commit(self): + """ + Returns + Commit object the head points to + """ + commit = self.object + if commit.type != "commit": + raise TypeError("Object of reference %s did not point to a commit" % self) + return commit @classmethod def iter_items(cls, repo, common_path = None, **kwargs): @@ -112,6 +127,29 @@ class Reference(LazyMixin, Iterable): output = repo.git.for_each_ref(common_path, **options) return cls._iter_from_stream(repo, iter(output.splitlines())) + + @classmethod + def from_path(cls, repo, path): + """ + Return + Instance of type Reference, Head, Tag, SymbolicReference or HEAD + depending on the given path + """ + if path == 'HEAD': + return HEAD(repo, path) + + if '/' not in path: + return SymbolicReference(repo, path) + + for ref_type in (Head, RemoteReference, TagReference, Reference): + try: + return ref_type(repo, path) + except ValueError: + pass + # END exception handling + # END for each type to try + raise ValueError("Could not find reference type suitable to handle path %r" % path) + @classmethod def _iter_from_stream(cls, repo, stream): @@ -145,47 +183,91 @@ class Reference(LazyMixin, Iterable): # return cls(repo, full_path, obj) -class Head(Reference): +class SymbolicReference(object): """ - A Head is a named reference to a Commit. Every Head instance contains a name - and a Commit object. - - Examples:: - - >>> repo = Repo("/path/to/repo") - >>> head = repo.heads[0] - - >>> head.name - 'master' - - >>> head.commit - - - >>> head.commit.id - '1c09f116cbc2cb4100fb6935bb162daa4723f455' + Represents a special case of a reference such that this reference is symbolic. + It does not point to a specific commit, but to another Head, which itself + specifies a commit. + + A typical example for a symbolic reference is HEAD. """ - _common_path_default = "refs/heads" + __slots__ = ("repo", "name") + def __init__(self, repo, name): + if '/' in name: + raise ValueError("SymbolicReferences are not located within a directory, got %s" % name) + self.repo = repo + self.name = name + + def __str__(self): + return self.name + + def __repr__(self): + return '' % (self.__class__.__name__, self.name) + + def __eq__(self, other): + return self.name == other.name + + def __ne__(self, other): + return not ( self == other ) + + def __hash__(self): + return hash(self.name) + @property - def commit(self): + def reference(self): """ Returns - Commit object the head points to + Reference Object we point to """ - return self.object + fp = open(os.path.join(self.repo.path, self.name), 'r') + try: + tokens = fp.readline().split(' ') + if tokens[0] != 'ref:': + raise TypeError("%s is a detached symbolic reference as it points to %r" % tokens[0]) + return Reference.from_path(self.repo, tokens[1]) + finally: + fp.close() - @classmethod - def reset(cls, repo, commit='HEAD', index=True, working_tree = False, + # alias + ref = reference + + @property + def is_detached(self): + """ + Returns + True if we are a detached reference, hence we point to a specific commit + instead to another reference + """ + try: + self.reference + return False + except TypeError: + return True + + +class HEAD(SymbolicReference): + """ + Special case of a Symbolic Reference as it represents the repository's + HEAD reference. + """ + __slots__ = tuple() + + def __init__(self, repo, name): + if name != 'HEAD': + raise ValueError("HEAD instance must point to 'HEAD', got %s" % name) + super(HEAD, self).__init__(repo, name) + + + def reset(self, commit='HEAD', index=True, working_tree = False, paths=None, **kwargs): """ - Reset the current head to the given commit optionally synchronizing + Reset our HEAD to the given commit optionally synchronizing the index and working tree. - ``repo`` - Repository containing commit - ``commit`` - Commit object, Reference Object or string identifying a revision + Commit object, Reference Object or string identifying a revision we + should reset HEAD to. ``index`` If True, the index will be set to match the given commit. Otherwise @@ -204,7 +286,7 @@ class Head(Reference): Additional arguments passed to git-reset. Returns - Head pointing to the specified commit + self """ mode = "--soft" if index: @@ -219,9 +301,32 @@ class Head(Reference): repo.git.reset(mode, commit, paths, **kwargs) # we always point to the active branch as it is the one changing - return repo.active_branch + self + + +class Head(Reference): + """ + A Head is a named reference to a Commit. Every Head instance contains a name + and a Commit object. + + Examples:: + + >>> repo = Repo("/path/to/repo") + >>> head = repo.heads[0] + + >>> head.name + 'master' + + >>> head.commit + + + >>> head.commit.id + '1c09f116cbc2cb4100fb6935bb162daa4723f455' + """ + _common_path_default = "refs/heads" + -class TagReference(Head): +class TagReference(Reference): """ Class representing a lightweight tag reference which either points to a commit or to a tag object. In the latter case additional information, like the signature @@ -230,7 +335,7 @@ class TagReference(Head): This tag object will always point to a commit object, but may carray additional information in a tag object:: - tagref = TagRef.list_items(repo)[0] + tagref = TagReference.list_items(repo)[0] print tagref.commit.message if tagref.tag is not None: print tagref.tag.message -- cgit v1.2.1 From b7a5c05875a760c0bf83af6617c68061bda6cfc5 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 22 Oct 2009 23:31:26 +0200 Subject: Adjusted tests to deal with API changes --- lib/git/refs.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index 97d0a6eb..aeee0da1 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -222,7 +222,7 @@ class SymbolicReference(object): """ fp = open(os.path.join(self.repo.path, self.name), 'r') try: - tokens = fp.readline().split(' ') + tokens = fp.readline().rstrip().split(' ') if tokens[0] != 'ref:': raise TypeError("%s is a detached symbolic reference as it points to %r" % tokens[0]) return Reference.from_path(self.repo, tokens[1]) @@ -298,10 +298,9 @@ class HEAD(SymbolicReference): raise ValueError( "Cannot reset the working tree if the index is not reset as well") # END working tree handling - repo.git.reset(mode, commit, paths, **kwargs) + self.repo.git.reset(mode, commit, paths, **kwargs) - # we always point to the active branch as it is the one changing - self + return self class Head(Reference): -- cgit v1.2.1 From d1bd99c0a376dec63f0f050aeb0c40664260da16 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 23 Oct 2009 00:13:09 +0200 Subject: SymbolicReferences can now change they references safely as I think and well controlled, including test. Added commit method which will return the commit for detached symbolic refs or for normal symbolic refs which is quite convenient --- lib/git/refs.py | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 65 insertions(+), 6 deletions(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index aeee0da1..a83628ce 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -7,7 +7,7 @@ Module containing all ref based objects """ import os -from objects.base import Object +from objects import Object, Commit from objects.utils import get_object_type_by_name from utils import LazyMixin, Iterable @@ -214,13 +214,36 @@ class SymbolicReference(object): def __hash__(self): return hash(self.name) + def _get_path(self): + return os.path.join(self.repo.path, self.name) + @property - def reference(self): + def commit(self): + """ + Returns: + Commit object we point to, works for detached and non-detached + SymbolicReferences + """ + # we partially reimplement it to prevent unnecessary file access + fp = open(self._get_path(), 'r') + value = fp.read().rstrip() + fp.close() + tokens = value.split(" ") + + # it is a detached reference + if len(tokens) == 1 and len(tokens[0]) == 40: + return Commit(self.repo, tokens[0]) + + # must be a head ! Git does not allow symbol refs to other things than heads + # Otherwise it would have detached it + return Head(self.repo, tokens[1]).commit + + def _get_reference(self): """ Returns Reference Object we point to """ - fp = open(os.path.join(self.repo.path, self.name), 'r') + fp = open(self._get_path(), 'r') try: tokens = fp.readline().rstrip().split(' ') if tokens[0] != 'ref:': @@ -229,6 +252,41 @@ class SymbolicReference(object): finally: fp.close() + def _set_reference(self, ref): + """ + Set ourselves to the given ref. It will stay a symbol if the ref is a Head. + Otherwise we try to get a commit from it using our interface. + + Strings are allowed but will be checked to be sure we have a commit + """ + write_value = None + if isinstance(ref, Head): + write_value = "ref: %s" % ref.path + elif isinstance(ref, Commit): + write_value = ref.id + else: + try: + write_value = ref.commit.id + except AttributeError: + sha = str(ref) + try: + obj = Object.new(self.repo, sha) + if obj.type != "commit": + raise TypeError("Invalid object type behind sha: %s" % sha) + write_value = obj.id + except Exception: + raise ValueError("Could not extract object from %s" % ref) + # END end try string + # END try commit attribute + fp = open(self._get_path(), "w") + try: + fp.write(write_value) + finally: + fp.close() + # END writing + + reference = property(_get_reference, _set_reference, doc="Returns the Reference we point to") + # alias ref = reference @@ -251,11 +309,12 @@ class HEAD(SymbolicReference): Special case of a Symbolic Reference as it represents the repository's HEAD reference. """ + _HEAD_NAME = 'HEAD' __slots__ = tuple() - def __init__(self, repo, name): - if name != 'HEAD': - raise ValueError("HEAD instance must point to 'HEAD', got %s" % name) + def __init__(self, repo, name=_HEAD_NAME): + if name != self._HEAD_NAME: + raise ValueError("HEAD instance must point to %r, got %r" % (self._HEAD_NAME, name)) super(HEAD, self).__init__(repo, name) -- cgit v1.2.1 From a7a4388eeaa4b6b94192dce67257a34c4a6cbd26 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 23 Oct 2009 12:14:22 +0200 Subject: Added frame for IndexFile add/remove/commit methods and respective test markers --- lib/git/refs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index a83628ce..c8857e97 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -322,7 +322,8 @@ class HEAD(SymbolicReference): paths=None, **kwargs): """ Reset our HEAD to the given commit optionally synchronizing - the index and working tree. + the index and working tree. The reference we refer to will be set to + commit as well. ``commit`` Commit object, Reference Object or string identifying a revision we -- cgit v1.2.1 From 2c23ca3cd9b9bbeaca1b79068dee1eae045be5b6 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 23 Oct 2009 14:46:18 +0200 Subject: refs: added create, delete and rename methods where appropriate. Tests are marked, implementation is needed for most of them --- lib/git/refs.py | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 2 deletions(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index c8857e97..29468d1c 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -182,6 +182,7 @@ class Reference(LazyMixin, Iterable): # obj.size = object_size # return cls(repo, full_path, obj) + class SymbolicReference(object): """ @@ -384,12 +385,80 @@ class Head(Reference): """ _common_path_default = "refs/heads" + @classmethod + def create(cls, repo, path, commit=HEAD(), **kwargs ): + """ + Create a new head. + ``repo`` + Repository to create the head in + + ``path`` + The name or path of the head, i.e. 'new_branch' or + feature/feature1. The prefix refs/heads is implied. + + ``commit`` + Commit to which the new head should point, defaults to the + current HEAD + + ``**kwargs`` + Additional keyword arguments to be passed to git-branch, i.e. + track, no-track, l, f to force creation even if branch with that + name already exists. + + Returns + Newly created Head + + Note + This does not alter the current HEAD, index or Working Tree + """ + raise NotImplementedError("todo") + + + @classmethod + def delete(cls, repo, *heads, force=False): + """ + Delete the given heads + + ``force`` + If True, the heads will be deleted even if they are not yet merged into + the main development stream + """ + flag = "-d" + if force: + flag = "-D" + repo.git.branch(flag, *heads) + + + def rename(self, new_path, force=False): + """ + Rename self to a new path + + ``new_path`` + Either a simple name or a path, i.e. new_name or features/new_name. + The prefix refs/heads is implied + + ``force`` + If True, the rename will succeed even if a head with the target name + already exists. + + Returns + self + """ + flag = "-m" + if force: + flag = "-M" + + self.repo.git.branch(flag, new_path) + self.path = "%s/%s" % (self._common_path_default, new_path) + return self + + class TagReference(Reference): """ Class representing a lightweight tag reference which either points to a commit - or to a tag object. In the latter case additional information, like the signature - or the tag-creator, is available. + ,a tag object or any other object. In the latter case additional information, + like the signature or the tag-creator, is available. This tag object will always point to a commit object, but may carray additional information in a tag object:: @@ -427,6 +496,43 @@ class TagReference(Reference): if self.object.type == "tag": return self.object return None + + @classmethod + def create(cls, repo, path, ref, message=None, **kwargs): + """ + Create a new tag object. + + ``path`` + The name of the tag, i.e. 1.0 or releases/1.0. + The prefix refs/tags is implied + + ``ref`` + A reference to the object you want to tag. It can be a commit, tree or + blob. + + ``message`` + If not None, the message will be used in your tag object. This will also + create an additional tag object that allows to obtain that information, i.e.:: + tagref.tag.message + + ``**kwargs`` + Additional keyword arguments to be passed to git-tag, i.e. f to force creation + of a tag. + + Returns + A new TagReference + """ + raise NotImplementedError("todo") + + @classmethod + def delete(cls, repo, *tags): + """ + Delete the given existing tag or tags + """ + repo.git.tag("-d", *tags) + + + # provide an alias @@ -460,3 +566,10 @@ class RemoteReference(Head): """ tokens = self.path.split('/') return '/'.join(tokens[3:]) + + @classmethod + def delete(cls, repo, *remotes): + """ + Delete the given remote references. + """ + repo.git.branch("-d", "-r", *remotes) -- cgit v1.2.1 From ddc5496506f0484e4f1331261aa8782c7e606bf2 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 23 Oct 2009 15:22:07 +0200 Subject: Implemented head methods: create, delete, rename, including tests --- lib/git/refs.py | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index 29468d1c..7b58f215 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -19,7 +19,7 @@ class Reference(LazyMixin, Iterable): _common_path_default = "refs" _id_attribute_ = "name" - def __init__(self, repo, path, object = None): + def __init__(self, repo, path): """ Initialize this instance ``repo`` @@ -29,16 +29,12 @@ class Reference(LazyMixin, Iterable): Path relative to the .git/ directory pointing to the ref in question, i.e. refs/heads/master - ``object`` - Object instance, will be retrieved on demand if None """ if not path.startswith(self._common_path_default): raise ValueError("Cannot instantiate %s Reference from path %s" % ( self.__class__.__name__, path )) self.repo = repo self.path = path - if object is not None: - self.object = object def __str__(self): return self.name @@ -386,7 +382,7 @@ class Head(Reference): _common_path_default = "refs/heads" @classmethod - def create(cls, repo, path, commit=HEAD(), **kwargs ): + def create(cls, repo, path, commit='HEAD', force=False, **kwargs ): """ Create a new head. ``repo`` @@ -399,11 +395,13 @@ class Head(Reference): ``commit`` Commit to which the new head should point, defaults to the current HEAD + + ``force`` + if True, force creation even if branch with that name already exists. ``**kwargs`` Additional keyword arguments to be passed to git-branch, i.e. - track, no-track, l, f to force creation even if branch with that - name already exists. + track, no-track, l Returns Newly created Head @@ -411,18 +409,28 @@ class Head(Reference): Note This does not alter the current HEAD, index or Working Tree """ - raise NotImplementedError("todo") + if cls is not Head: + raise TypeError("Only Heads can be created explicitly, not objects of type %s" % cls.__name__) + + args = ( path, commit ) + if force: + kwargs['f'] = True + + repo.git.branch(*args, **kwargs) + return cls(repo, "%s/%s" % ( cls._common_path_default, path)) @classmethod - def delete(cls, repo, *heads, force=False): + def delete(cls, repo, *heads, **kwargs): """ Delete the given heads ``force`` If True, the heads will be deleted even if they are not yet merged into - the main development stream + the main development stream. + Default False """ + force = kwargs.get("force", False) flag = "-d" if force: flag = "-D" @@ -448,7 +456,7 @@ class Head(Reference): if force: flag = "-M" - self.repo.git.branch(flag, new_path) + self.repo.git.branch(flag, self, new_path) self.path = "%s/%s" % (self._common_path_default, new_path) return self @@ -568,7 +576,7 @@ class RemoteReference(Head): return '/'.join(tokens[3:]) @classmethod - def delete(cls, repo, *remotes): + def delete(cls, repo, *remotes, **kwargs): """ Delete the given remote references. """ -- cgit v1.2.1 From 1047b41e2e925617474e2e7c9927314f71ce7365 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 23 Oct 2009 15:45:23 +0200 Subject: Added TagRefernce creation and deletion including tests Added RemoteReference deletion and test --- lib/git/refs.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index 7b58f215..0efee52e 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -506,7 +506,7 @@ class TagReference(Reference): return None @classmethod - def create(cls, repo, path, ref, message=None, **kwargs): + def create(cls, repo, path, ref='HEAD', message=None, force=False, **kwargs): """ Create a new tag object. @@ -523,14 +523,23 @@ class TagReference(Reference): create an additional tag object that allows to obtain that information, i.e.:: tagref.tag.message + ``force`` + If True, to force creation of a tag even though that tag already exists. + ``**kwargs`` - Additional keyword arguments to be passed to git-tag, i.e. f to force creation - of a tag. + Additional keyword arguments to be passed to git-tag Returns A new TagReference """ - raise NotImplementedError("todo") + args = ( path, ref ) + if message: + kwargs['m'] = message + if force: + kwargs['f'] = True + + repo.git.tag(*args, **kwargs) + return TagReference(repo, "%s/%s" % (cls._common_path_default, path)) @classmethod def delete(cls, repo, *tags): @@ -579,5 +588,9 @@ class RemoteReference(Head): def delete(cls, repo, *remotes, **kwargs): """ Delete the given remote references. + + Note + kwargs are given for compatability with the base class method as we + should not narrow the signature. """ repo.git.branch("-d", "-r", *remotes) -- cgit v1.2.1 From 9b9776e88f7abb59cebac8733c04cccf6eee1c60 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 23 Oct 2009 16:07:45 +0200 Subject: Refs can now set the reference they are pointing to in a controlled fashion by writing their ref file directly --- lib/git/refs.py | 48 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 6 deletions(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index 0efee52e..47c37af6 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -65,8 +65,7 @@ class Reference(LazyMixin, Iterable): return '/'.join(tokens[2:]) - @property - def object(self): + def _get_object(self): """ Returns The object our ref currently refers to. Refs can be cached, they will @@ -76,17 +75,54 @@ class Reference(LazyMixin, Iterable): # Our path will be resolved to the hexsha which will be used accordingly return Object.new(self.repo, self.path) - @property - def commit(self): + def _set_object(self, ref, type=None): """ + Set our reference to point to the given ref. It will be converted + to a specific hexsha. + + ``type`` + If not None, string type of that the object must have, other we raise + a type error. Only used internally + Returns - Commit object the head points to + Object we have set. This is used internally only to reduce the amount + of calls to the git command + """ + obj = Object.new(self.repo, ref) + if type is not None and obj.type != type: + raise TypeError("Reference %r cannot point to object of type %r" % (self,obj.type)) + + full_ref_path = os.path.join(self.repo.path, self.path) + fp = open(full_ref_path, "w") + try: + fp.write(str(obj)) + finally: + fp.close() + return obj + + object = property(_get_object, _set_object, doc="Return the object our ref currently refers to") + + def _set_commit(self, commit): + """ + Set ourselves to point to the given commit. + + Raise + ValueError if commit does not actually point to a commit + """ + self._set_object(commit, type="commit") + + def _get_commit(self): + """ + Returns + Commit object the reference points to """ commit = self.object if commit.type != "commit": raise TypeError("Object of reference %s did not point to a commit" % self) return commit + commit = property(_get_commit, _set_commit, doc="Return Commit object the reference points to") + @classmethod def iter_items(cls, repo, common_path = None, **kwargs): """ @@ -244,7 +280,7 @@ class SymbolicReference(object): try: tokens = fp.readline().rstrip().split(' ') if tokens[0] != 'ref:': - raise TypeError("%s is a detached symbolic reference as it points to %r" % tokens[0]) + raise TypeError("%s is a detached symbolic reference as it points to %r" % (self, tokens[0])) return Reference.from_path(self.repo, tokens[1]) finally: fp.close() -- cgit v1.2.1 From 0725af77afc619cdfbe3cec727187e442cceaf97 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 26 Oct 2009 11:21:59 +0100 Subject: refs.SymoblicRef: implemented direcft setting of the symbolic references commit, which possibly dereferences to the respective head --- lib/git/refs.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index 47c37af6..26e7c09e 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -118,7 +118,7 @@ class Reference(LazyMixin, Iterable): """ commit = self.object if commit.type != "commit": - raise TypeError("Object of reference %s did not point to a commit" % self) + raise TypeError("Object of reference %s did not point to a commit, but to %r" % (self, commit)) return commit commit = property(_get_commit, _set_commit, doc="Return Commit object the reference points to") @@ -250,8 +250,7 @@ class SymbolicReference(object): def _get_path(self): return os.path.join(self.repo.path, self.name) - @property - def commit(self): + def _get_commit(self): """ Returns: Commit object we point to, works for detached and non-detached @@ -271,6 +270,18 @@ class SymbolicReference(object): # Otherwise it would have detached it return Head(self.repo, tokens[1]).commit + def _set_commit(self, commit): + """ + Set our commit, possibly dereference our symbolic reference first. + """ + if self.is_detached: + return self._set_reference(commit) + + # set the commit on our reference + self._get_reference().commit = commit + + commit = property(_get_commit, _set_commit, doc="Query or set commits directly") + def _get_reference(self): """ Returns -- cgit v1.2.1 From 22757ed7b58862cccef64fdc09f93ea1ac72b1d2 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 27 Oct 2009 11:58:20 +0100 Subject: put _make_file helper method into TestBase class remote: prepared FetchInfo class to be returned by fetch and pull. About to implement tests --- lib/git/refs.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index 26e7c09e..52e128b5 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -263,11 +263,13 @@ class SymbolicReference(object): tokens = value.split(" ") # it is a detached reference - if len(tokens) == 1 and len(tokens[0]) == 40: + if self.repo.re_hexsha_only.match(tokens[0]): return Commit(self.repo, tokens[0]) # must be a head ! Git does not allow symbol refs to other things than heads # Otherwise it would have detached it + if tokens[0] != "ref:": + raise ValueError("Failed to parse symbolic refernce: wanted 'ref: ', got %r" % value) return Head(self.repo, tokens[1]).commit def _set_commit(self, commit): -- cgit v1.2.1 From ef592d384ad3e0ead5e516f3b2c0f31e6f1e7cab Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 27 Oct 2009 19:10:39 +0100 Subject: Reference._from_string will now create the appropriate type, not just the type of the actual class. This could result in a symbolic reference returned even though you technically requested a reference - this issue must still be addressed. --- lib/git/refs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index 52e128b5..b032285f 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -208,7 +208,7 @@ class Reference(LazyMixin, Iterable): # our path on demand - due to perstent commands it is fast. # This reduces the risk that the object does not match # the changed ref anymore in case it changes in the meanwhile - return cls(repo, full_path) + return cls.from_path(repo, full_path) # obj = get_object_type_by_name(type_name)(repo, hexsha) # obj.size = object_size -- cgit v1.2.1 From 764cc6e344bd034360485018eb750a0e155ca1f6 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 27 Oct 2009 22:05:44 +0100 Subject: renamed remote_branch to remote_head, improved errror message --- lib/git/refs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index b032285f..9b089a25 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -31,7 +31,7 @@ class Reference(LazyMixin, Iterable): """ if not path.startswith(self._common_path_default): - raise ValueError("Cannot instantiate %s Reference from path %s" % ( self.__class__.__name__, path )) + raise ValueError("Cannot instantiate %s from path %s" % ( self.__class__.__name__, path )) self.repo = repo self.path = path @@ -622,10 +622,10 @@ class RemoteReference(Head): return tokens[2] @property - def remote_branch(self): + def remote_head(self): """ Returns - Name of the remote branch itself, i.e. master. + Name of the remote head itself, i.e. master. NOTE: The returned name is usually not qualified enough to uniquely identify a branch -- cgit v1.2.1 From e70f3218e910d2b3dcb8a5ab40c65b6bd7a8e9a8 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 3 Nov 2009 14:04:32 +0100 Subject: Intermediate commit with a few added and improved tests as well as many fixes --- lib/git/refs.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index 9b089a25..cea3e720 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -167,6 +167,9 @@ class Reference(LazyMixin, Iterable): Instance of type Reference, Head, Tag, SymbolicReference or HEAD depending on the given path """ + if not path: + raise ValueError("Cannot create Reference from %r" % path) + if path == 'HEAD': return HEAD(repo, path) -- cgit v1.2.1 From dbc18b92362f60afc05d4ddadd6e73902ae27ec7 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 3 Nov 2009 15:52:45 +0100 Subject: repo: added create_* and delete_* methods for refs ( head, tag, remote ) as a convenient shortcut to using the classes manually --- lib/git/refs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index cea3e720..ddf98fc7 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -434,7 +434,7 @@ class Head(Reference): _common_path_default = "refs/heads" @classmethod - def create(cls, repo, path, commit='HEAD', force=False, **kwargs ): + def create(cls, repo, path, commit='HEAD', force=False, **kwargs ): """ Create a new head. ``repo`` @@ -560,7 +560,7 @@ class TagReference(Reference): @classmethod def create(cls, repo, path, ref='HEAD', message=None, force=False, **kwargs): """ - Create a new tag object. + Create a new tag reference. ``path`` The name of the tag, i.e. 1.0 or releases/1.0. -- cgit v1.2.1 From 3cb5ba18ab1a875ef6b62c65342de476be47871b Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 3 Nov 2009 16:35:33 +0100 Subject: object: renamed id attribute to sha as it in fact is always being rewritten as sha, even if the passed in id was a ref. This is done to assure objects are uniquely identified and will compare correctly --- lib/git/refs.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index ddf98fc7..84347057 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -312,17 +312,17 @@ class SymbolicReference(object): if isinstance(ref, Head): write_value = "ref: %s" % ref.path elif isinstance(ref, Commit): - write_value = ref.id + write_value = ref.sha else: try: - write_value = ref.commit.id + write_value = ref.commit.sha except AttributeError: sha = str(ref) try: obj = Object.new(self.repo, sha) if obj.type != "commit": raise TypeError("Invalid object type behind sha: %s" % sha) - write_value = obj.id + write_value = obj.sha except Exception: raise ValueError("Could not extract object from %s" % ref) # END end try string @@ -428,7 +428,7 @@ class Head(Reference): >>> head.commit - >>> head.commit.id + >>> head.commit.sha '1c09f116cbc2cb4100fb6935bb162daa4723f455' """ _common_path_default = "refs/heads" -- cgit v1.2.1 From 572ace094208c28ab1a8641aedb038456d13f70b Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 3 Nov 2009 17:44:13 +0100 Subject: Now using git-update-ref and git-symbolic-ref to update references with reflog support. This should be manually implemented though for more performance, what it does is relatively easy --- lib/git/refs.py | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index 84347057..cd36a052 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -13,7 +13,8 @@ from utils import LazyMixin, Iterable class Reference(LazyMixin, Iterable): """ - Represents a named reference to any object + Represents a named reference to any object. Subclasses may apply restrictions though, + i.e. Heads can only point to commits. """ __slots__ = ("repo", "path") _common_path_default = "refs" @@ -75,30 +76,16 @@ class Reference(LazyMixin, Iterable): # Our path will be resolved to the hexsha which will be used accordingly return Object.new(self.repo, self.path) - def _set_object(self, ref, type=None): + def _set_object(self, ref): """ Set our reference to point to the given ref. It will be converted to a specific hexsha. - ``type`` - If not None, string type of that the object must have, other we raise - a type error. Only used internally - - Returns - Object we have set. This is used internally only to reduce the amount - of calls to the git command + Note: + TypeChecking is done by the git command """ - obj = Object.new(self.repo, ref) - if type is not None and obj.type != type: - raise TypeError("Reference %r cannot point to object of type %r" % (self,obj.type)) - - full_ref_path = os.path.join(self.repo.path, self.path) - fp = open(full_ref_path, "w") - try: - fp.write(str(obj)) - finally: - fp.close() - return obj + # do it safely by specifying the old value + self.repo.git.update_ref(self.path, ref, self._get_object().sha) object = property(_get_object, _set_object, doc="Return the object our ref currently refers to") @@ -109,7 +96,7 @@ class Reference(LazyMixin, Iterable): Raise ValueError if commit does not actually point to a commit """ - self._set_object(commit, type="commit") + self._set_object(commit) def _get_commit(self): """ @@ -327,6 +314,15 @@ class SymbolicReference(object): raise ValueError("Could not extract object from %s" % ref) # END end try string # END try commit attribute + + # if we are writing a ref, use symbolic ref to get the reflog and more + # checking + # Otherwise we detach it and have to do it manually + if write_value.startswith('ref:'): + self.repo.git.symbolic_ref(self.name, write_value[5:]) + return + # END non-detached handling + fp = open(self._get_path(), "w") try: fp.write(write_value) -- cgit v1.2.1 From 9b426893ce331f71165c82b1a86252cd104ae3db Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 3 Nov 2009 18:04:40 +0100 Subject: Reference.from_path now only creates references, not symbolic refs. SymbolicReference.from_path creates only symbolic refs. This change was not detected by a test, hence there is room for improvement on the testing field --- lib/git/refs.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index cd36a052..9c88c5a1 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -151,18 +151,12 @@ class Reference(LazyMixin, Iterable): def from_path(cls, repo, path): """ Return - Instance of type Reference, Head, Tag, SymbolicReference or HEAD + Instance of type Reference, Head, or Tag depending on the given path """ if not path: raise ValueError("Cannot create Reference from %r" % path) - if path == 'HEAD': - return HEAD(repo, path) - - if '/' not in path: - return SymbolicReference(repo, path) - for ref_type in (Head, RemoteReference, TagReference, Reference): try: return ref_type(repo, path) @@ -218,7 +212,10 @@ class SymbolicReference(object): def __init__(self, repo, name): if '/' in name: + # NOTE: Actually they can be looking like ordinary refs. Theoretically we handle this + # case incorrectly raise ValueError("SymbolicReferences are not located within a directory, got %s" % name) + # END error handling self.repo = repo self.name = name @@ -348,6 +345,23 @@ class SymbolicReference(object): except TypeError: return True + @classmethod + def from_path(cls, repo, path): + """ + Return + Instance of SymbolicReference or HEAD + depending on the given path + """ + if not path: + raise ValueError("Cannot create Symbolic Reference from %r" % path) + + if path == 'HEAD': + return HEAD(repo, path) + + if '/' not in path: + return SymbolicReference(repo, path) + + raise ValueError("Could not find symbolic reference type suitable to handle path %r" % path) class HEAD(SymbolicReference): """ -- cgit v1.2.1 From 43ab2afba68fd0e1b5d138ed99ffc788dc685e36 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 3 Nov 2009 20:59:24 +0100 Subject: refs: iter_items now imlemented natively for additional performance. We did not implement the crazy sorting feature found in git-for-each-ref though --- lib/git/refs.py | 94 +++++++++++++++++++++++++++------------------------------ 1 file changed, 45 insertions(+), 49 deletions(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index 9c88c5a1..352c14f4 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -124,28 +124,57 @@ class Reference(LazyMixin, Iterable): Defaults to class specific portion if None assuring that only refs suitable for the actual class are returned. - ``kwargs`` - Additional options given as keyword arguments, will be passed - to git-for-each-ref - Returns - git.Ref[] + git.Reference[] - List is sorted by committerdate - The returned objects are compatible to the Ref base, but represent the - actual type, such as Head or Tag + List is lexigraphically sorted + The returned objects represent actual subclasses, such as Head or TagReference """ - - options = {'sort': "committerdate", - 'format': "%(refname)%00%(objectname)%00%(objecttype)%00%(objectsize)"} - if common_path is None: common_path = cls._common_path_default - options.update(kwargs) - - output = repo.git.for_each_ref(common_path, **options) - return cls._iter_from_stream(repo, iter(output.splitlines())) + rela_paths = set() + + # walk loose refs + # Currently we do not follow links + for root, dirs, files in os.walk(os.path.join(repo.path, common_path)): + for f in files: + abs_path = os.path.join(root, f) + rela_paths.add(abs_path.replace(repo.path + '/', "")) + # END for each file in root directory + # END for each directory to walk + + # read packed refs + packed_refs_path = os.path.join(repo.path, 'packed-refs') + if os.path.isfile(packed_refs_path): + fp = open(packed_refs_path, 'r') + try: + for line in fp.readlines(): + if line.startswith('#'): + continue + # 439689865b9c6e2a0dad61db22a0c9855bacf597 refs/heads/hello + line = line.rstrip() + first_space = line.find(' ') + if first_space == -1: + continue + + rela_path = line[first_space+1:] + if rela_path.startswith(common_path): + rela_paths.add(rela_path) + # END relative path matches common path + # END for each line in packed-refs + finally: + fp.close() + # END packed refs reading + + # return paths in sorted order + for path in sorted(rela_paths): + if path.endswith('/HEAD'): + continue + # END skip remote heads + yield cls.from_path(repo, path) + # END for each sorted relative refpath + @classmethod def from_path(cls, repo, path): @@ -167,39 +196,6 @@ class Reference(LazyMixin, Iterable): raise ValueError("Could not find reference type suitable to handle path %r" % path) - @classmethod - def _iter_from_stream(cls, repo, stream): - """ Parse out ref information into a list of Ref compatible objects - Returns git.Ref[] list of Ref objects """ - heads = [] - - for line in stream: - heads.append(cls._from_string(repo, line)) - - return heads - - @classmethod - def _from_string(cls, repo, line): - """ Create a new Ref instance from the given string. - Format - name: [a-zA-Z_/]+ - - id: [0-9A-Fa-f]{40} - Returns git.Head """ - full_path, hexsha, type_name, object_size = line.split("\x00") - - # No, we keep the object dynamic by allowing it to be retrieved by - # our path on demand - due to perstent commands it is fast. - # This reduces the risk that the object does not match - # the changed ref anymore in case it changes in the meanwhile - return cls.from_path(repo, full_path) - - # obj = get_object_type_by_name(type_name)(repo, hexsha) - # obj.size = object_size - # return cls(repo, full_path, obj) - - - class SymbolicReference(object): """ Represents a special case of a reference such that this reference is symbolic. -- cgit v1.2.1 From 4748e813980e1316aa364e0830a4dc082ff86eb0 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 3 Nov 2009 21:43:17 +0100 Subject: added all new files to reference.rst and corrected the worst mistakes. There are still a few errors left that I cannot fix as it complains about whitespace in the end ... that is exactly what I hate restructured text for, its just a ... anyway. --- lib/git/refs.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index 352c14f4..1900c6ce 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -3,9 +3,8 @@ # # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php -""" -Module containing all ref based objects -""" +""" Module containing all ref based objects """ + import os from objects import Object, Commit from objects.utils import get_object_type_by_name -- cgit v1.2.1 From ace1fed6321bb8dd6d38b2f58d7cf815fa16db7a Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 4 Nov 2009 19:36:29 +0100 Subject: head.checkout method added including test --- lib/git/refs.py | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index 1900c6ce..5b94ea07 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -517,7 +517,39 @@ class Head(Reference): self.path = "%s/%s" % (self._common_path_default, new_path) return self - + def checkout(self, force=False, **kwargs): + """ + Checkout this head by setting the HEAD to this reference, by updating the index + to reflect the tree we point to and by updating the working tree to reflect + the latest index. + + The command will fail if changed working tree files would be overwritten. + + ``force`` + If True, changes to the index and the working tree will be discarded. + If False, GitCommandError will be raised in that situation. + + ``**kwargs`` + Additional keyword arguments to be passed to git checkout, i.e. + b='new_branch' to create a new branch at the given spot. + + Returns + The active branch after the checkout operation, usually self unless + a new branch has been created. + + Note + By default it is only allowed to checkout heads - everything else + will leave the HEAD detached which is allowed and possible, but remains + a special state that some tools might not be able to handle. + """ + args = list() + kwargs['f'] = force + if kwargs['f'] == False: + kwargs.pop('f') + + self.repo.git.checkout(self, **kwargs) + return self.repo.active_branch + class TagReference(Reference): """ -- cgit v1.2.1