From dec4663129f72321a14efd6de63f14a7419e3ed2 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 23 Nov 2010 09:14:17 +0100 Subject: Split ref implementation up into multiple files, to make room for the log implementation --- refs/reference.py | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 refs/reference.py (limited to 'refs/reference.py') diff --git a/refs/reference.py b/refs/reference.py new file mode 100644 index 00000000..1f97b92e --- /dev/null +++ b/refs/reference.py @@ -0,0 +1,111 @@ +from symbolic import SymbolicReference +import os +from git.util import ( + LazyMixin, + Iterable, + ) + +from gitdb.util import ( + isfile, + hex_to_bin + ) + +__all__ = ["Reference"] + + +class Reference(SymbolicReference, LazyMixin, Iterable): + """Represents a named reference to any object. Subclasses may apply restrictions though, + i.e. Heads can only point to commits.""" + __slots__ = tuple() + _common_path_default = "refs" + + def __init__(self, repo, path): + """Initialize this instance + :param repo: Our parent repository + + :param path: + Path relative to the .git/ directory pointing to the ref in question, i.e. + refs/heads/master""" + if not path.startswith(self._common_path_default+'/'): + raise ValueError("Cannot instantiate %r from path %s" % ( self.__class__.__name__, path )) + super(Reference, self).__init__(repo, path) + + + def __str__(self): + return self.name + + def _get_object(self): + """ + :return: + The object our ref currently refers to. Refs can be cached, they will + always point to the actual object as it gets re-created on each query""" + # 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_from_sha(self.repo, hex_to_bin(self.dereference_recursive(self.repo, self.path))) + + def _set_object(self, ref): + """ + Set our reference to point to the given ref. It will be converted + to a specific hexsha. + If the reference does not exist, it will be created. + + :note: + TypeChecking is done by the git command""" + abs_path = self._abs_path() + existed = True + if not isfile(abs_path): + existed = False + open(abs_path, 'wb').write(Object.NULL_HEX_SHA) + # END quick create + + # do it safely by specifying the old value + try: + self.repo.git.update_ref(self.path, ref, (existed and self._get_object().hexsha) or None) + except: + if not existed: + os.remove(abs_path) + # END remove file on error if it didn't exist before + raise + # END exception handling + + object = property(_get_object, _set_object, doc="Return the object our ref currently refers to") + + @property + def name(self): + """:return: (shortest) Name of this reference - it may contain path components""" + # first two path tokens are can be removed as they are + # refs/heads or refs/tags or refs/remotes + tokens = self.path.split('/') + if len(tokens) < 3: + return self.path # could be refs/HEAD + return '/'.join(tokens[2:]) + + + @classmethod + def create(cls, repo, path, commit='HEAD', force=False ): + """Create a new reference. + + :param repo: Repository to create the reference in + :param path: + The relative path of the reference, i.e. 'new_branch' or + feature/feature1. The path prefix 'refs/' is implied if not + given explicitly + + :param commit: + Commit to which the new reference should point, defaults to the + current HEAD + + :param force: + if True, force creation even if a reference with that name already exists. + Raise OSError otherwise + + :return: Newly created Reference + + :note: This does not alter the current HEAD, index or Working Tree""" + return cls._create(repo, path, True, commit, force) + + @classmethod + def iter_items(cls, repo, common_path = None): + """Equivalent to SymbolicReference.iter_items, but will return non-detached + references as well.""" + return cls._iter_items(repo, common_path) -- cgit v1.2.3 From 739fa140235cc9d65c632eaf1f5cacc944d87cfb Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 23 Nov 2010 09:34:44 +0100 Subject: Fixed remaining tests - lets hope that everything is indeed working correctly - as imports changed, every line of code needs to be run to assure all names can be resolved --- refs/reference.py | 1 + 1 file changed, 1 insertion(+) (limited to 'refs/reference.py') diff --git a/refs/reference.py b/refs/reference.py index 1f97b92e..a76e2d5d 100644 --- a/refs/reference.py +++ b/refs/reference.py @@ -1,5 +1,6 @@ from symbolic import SymbolicReference import os +from git.objects import Object from git.util import ( LazyMixin, Iterable, -- cgit v1.2.3 From a17c43d0662bab137903075f2cff34bcabc7e1d1 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 24 Nov 2010 12:30:51 +0100 Subject: Made previously protected methods public to introduce a method with reflog support which cannot be exposed using the respective property. Ref-Creation is now fully implemented in python. For details, see doc/source/changes.rst --- refs/reference.py | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) (limited to 'refs/reference.py') diff --git a/refs/reference.py b/refs/reference.py index a76e2d5d..446196c3 100644 --- a/refs/reference.py +++ b/refs/reference.py @@ -18,6 +18,7 @@ class Reference(SymbolicReference, LazyMixin, Iterable): """Represents a named reference to any object. Subclasses may apply restrictions though, i.e. Heads can only point to commits.""" __slots__ = tuple() + _resolve_ref_on_create = True _common_path_default = "refs" def __init__(self, repo, path): @@ -52,7 +53,7 @@ class Reference(SymbolicReference, LazyMixin, Iterable): :note: TypeChecking is done by the git command""" - abs_path = self._abs_path() + abs_path = self.abspath existed = True if not isfile(abs_path): existed = False @@ -81,31 +82,7 @@ class Reference(SymbolicReference, LazyMixin, Iterable): return self.path # could be refs/HEAD return '/'.join(tokens[2:]) - @classmethod - def create(cls, repo, path, commit='HEAD', force=False ): - """Create a new reference. - - :param repo: Repository to create the reference in - :param path: - The relative path of the reference, i.e. 'new_branch' or - feature/feature1. The path prefix 'refs/' is implied if not - given explicitly - - :param commit: - Commit to which the new reference should point, defaults to the - current HEAD - - :param force: - if True, force creation even if a reference with that name already exists. - Raise OSError otherwise - - :return: Newly created Reference - - :note: This does not alter the current HEAD, index or Working Tree""" - return cls._create(repo, path, True, commit, force) - - @classmethod def iter_items(cls, repo, common_path = None): """Equivalent to SymbolicReference.iter_items, but will return non-detached references as well.""" -- cgit v1.2.3 From ec0657cf5de9aeb5629cc4f4f38b36f48490493e Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 24 Nov 2010 15:56:49 +0100 Subject: Unified object and commit handling which should make the reflog handling much easier. There is some bug in it though, it still needs fixing --- refs/reference.py | 37 +------------------------------------ 1 file changed, 1 insertion(+), 36 deletions(-) (limited to 'refs/reference.py') diff --git a/refs/reference.py b/refs/reference.py index 446196c3..e7cdfdee 100644 --- a/refs/reference.py +++ b/refs/reference.py @@ -18,6 +18,7 @@ class Reference(SymbolicReference, LazyMixin, Iterable): """Represents a named reference to any object. Subclasses may apply restrictions though, i.e. Heads can only point to commits.""" __slots__ = tuple() + _points_to_commits_only = False _resolve_ref_on_create = True _common_path_default = "refs" @@ -36,42 +37,6 @@ class Reference(SymbolicReference, LazyMixin, Iterable): def __str__(self): return self.name - def _get_object(self): - """ - :return: - The object our ref currently refers to. Refs can be cached, they will - always point to the actual object as it gets re-created on each query""" - # 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_from_sha(self.repo, hex_to_bin(self.dereference_recursive(self.repo, self.path))) - - def _set_object(self, ref): - """ - Set our reference to point to the given ref. It will be converted - to a specific hexsha. - If the reference does not exist, it will be created. - - :note: - TypeChecking is done by the git command""" - abs_path = self.abspath - existed = True - if not isfile(abs_path): - existed = False - open(abs_path, 'wb').write(Object.NULL_HEX_SHA) - # END quick create - - # do it safely by specifying the old value - try: - self.repo.git.update_ref(self.path, ref, (existed and self._get_object().hexsha) or None) - except: - if not existed: - os.remove(abs_path) - # END remove file on error if it didn't exist before - raise - # END exception handling - - object = property(_get_object, _set_object, doc="Return the object our ref currently refers to") - @property def name(self): """:return: (shortest) Name of this reference - it may contain path components""" -- cgit v1.2.3 From c946bf260d3f7ca54bffb796a82218dce0eb703f Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 24 Nov 2010 17:51:22 +0100 Subject: Added tests for creation and adjustments of reference, verifying the log gets written --- refs/reference.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'refs/reference.py') diff --git a/refs/reference.py b/refs/reference.py index e7cdfdee..c44ebf00 100644 --- a/refs/reference.py +++ b/refs/reference.py @@ -37,6 +37,36 @@ class Reference(SymbolicReference, LazyMixin, Iterable): def __str__(self): return self.name + def set_object(self, object, msg = None): + """Special version which checks if the head-log needs an update as well""" + oldbinsha = None + if msg is not None: + head = self.repo.head + if not head.is_detached and head.ref == self: + oldbinsha = self.commit.binsha + #END handle commit retrieval + #END handle message is set + + super(Reference, self).set_object(object, msg) + + if oldbinsha is not None: + # /* from refs.c in git-source + # * Special hack: If a branch is updated directly and HEAD + # * points to it (may happen on the remote side of a push + # * for example) then logically the HEAD reflog should be + # * updated too. + # * A generic solution implies reverse symref information, + # * but finding all symrefs pointing to the given branch + # * would be rather costly for this rare event (the direct + # * update of a branch) to be worth it. So let's cheat and + # * check with HEAD only which should cover 99% of all usage + # * scenarios (even 100% of the default ones). + # */ + self.repo.head.log_append(oldbinsha, msg) + #END check if the head + + # NOTE: Don't have to overwrite properties as the will only work without a the log + @property def name(self): """:return: (shortest) Name of this reference - it may contain path components""" -- cgit v1.2.3 From 86523260c495d9a29aa5ab29d50d30a5d1981a0c Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 24 Nov 2010 17:55:43 +0100 Subject: Renamed msg named parameter to logmsg, as it describes the purpose of the message much better Added test for deletion of reflog file when the corresponding ref is deleted --- refs/reference.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'refs/reference.py') diff --git a/refs/reference.py b/refs/reference.py index c44ebf00..1a745ee9 100644 --- a/refs/reference.py +++ b/refs/reference.py @@ -37,17 +37,17 @@ class Reference(SymbolicReference, LazyMixin, Iterable): def __str__(self): return self.name - def set_object(self, object, msg = None): + def set_object(self, object, logmsg = None): """Special version which checks if the head-log needs an update as well""" oldbinsha = None - if msg is not None: + if logmsg is not None: head = self.repo.head if not head.is_detached and head.ref == self: oldbinsha = self.commit.binsha #END handle commit retrieval #END handle message is set - super(Reference, self).set_object(object, msg) + super(Reference, self).set_object(object, logmsg) if oldbinsha is not None: # /* from refs.c in git-source @@ -62,7 +62,7 @@ class Reference(SymbolicReference, LazyMixin, Iterable): # * check with HEAD only which should cover 99% of all usage # * scenarios (even 100% of the default ones). # */ - self.repo.head.log_append(oldbinsha, msg) + self.repo.head.log_append(oldbinsha, logmsg) #END check if the head # NOTE: Don't have to overwrite properties as the will only work without a the log -- cgit v1.2.3