diff options
author | Sebastian Thiel <byronimo@gmail.com> | 2009-10-26 23:24:56 +0100 |
---|---|---|
committer | Sebastian Thiel <byronimo@gmail.com> | 2009-10-26 23:24:56 +0100 |
commit | 2792e534dd55fe03bca302f87a3ea638a7278bf1 (patch) | |
tree | 28d8f1cc81d8d121a9976204ee10be22996f6e2d /lib/git/refs.py | |
parent | 1b89f39432cdb395f5fbb9553b56595d29e2b773 (diff) | |
parent | 0ef1f89abe5b2334705ee8f1a6da231b0b6c9a50 (diff) | |
download | gitpython-2792e534dd55fe03bca302f87a3ea638a7278bf1.tar.gz |
Merge branch 'index' into improvements
* index:
index.add: Finished implemenation including through tests
When parsing trees, we now store the originan type bits as well, previously we dropped it
cmd.wait: AutoKill wrapped process will automatically raise on errors to unify error handling amongst clients using the process directly. It might be needed to add a flag allowing to easily override that
added head kwarg to reset and commit method, allowing to automatically change the head to the given commit, which makes the methods more versatile
refs.SymoblicRef: implemented direcft setting of the symbolic references commit, which possibly dereferences to the respective head
index.commit: implemented initial version, but in fact some more changes are required to have a nice API. Tests are not yet fully done either
actor: added __eq__, __ne__ and __hash__ methods including simple test
index.remove implemented including throrough test
Implemented index.reset method including test
IndexEntry is now based on a 'minimal' version that is suitable to be fed into UpdateIndex. The Inode and device information is only needed to quickly compare the index against the working tree for changes, hence it should not be that dominant in the API either. More changes to come
Added notes about git-update-ref
Refs can now set the reference they are pointing to in a controlled fashion by writing their ref file directly
Added TagRefernce creation and deletion including tests
Implemented head methods: create, delete, rename, including tests
refs: added create, delete and rename methods where appropriate. Tests are marked, implementation is needed for most of them
Added frame for IndexFile add/remove/commit methods and respective test markers
Added repo.index property including simple test, and additional ideas in the TODO list
Renamed Index to IndexFile, adjusted tests, it will only operate on physical files, not on streams, as Indices are not streamed by any git command ( at least not in raw format )
Diffstat (limited to 'lib/git/refs.py')
-rw-r--r-- | lib/git/refs.py | 216 |
1 files changed, 199 insertions, 17 deletions
diff --git a/lib/git/refs.py b/lib/git/refs.py index a83628ce..26e7c09e 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 @@ -69,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 @@ -80,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 + 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 head points to + 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) + 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") + @classmethod def iter_items(cls, repo, common_path = None, **kwargs): """ @@ -182,6 +214,7 @@ class Reference(LazyMixin, Iterable): # obj.size = object_size # return cls(repo, full_path, obj) + class SymbolicReference(object): """ @@ -217,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 @@ -238,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 @@ -247,7 +291,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() @@ -322,7 +366,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 @@ -383,12 +428,92 @@ class Head(Reference): """ _common_path_default = "refs/heads" + @classmethod + def create(cls, repo, path, commit='HEAD', force=False, **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 + + ``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 + + Returns + Newly created Head + + Note + This does not alter the current HEAD, index or Working Tree + """ + 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, **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. + Default False + """ + force = kwargs.get("force", False) + 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, self, 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:: @@ -426,6 +551,52 @@ class TagReference(Reference): if self.object.type == "tag": return self.object return None + + @classmethod + def create(cls, repo, path, ref='HEAD', message=None, force=False, **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 + + ``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 + + Returns + A new TagReference + """ + 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): + """ + Delete the given existing tag or tags + """ + repo.git.tag("-d", *tags) + + + # provide an alias @@ -459,3 +630,14 @@ class RemoteReference(Head): """ tokens = self.path.split('/') return '/'.join(tokens[3:]) + + @classmethod + 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) |