summaryrefslogtreecommitdiff
path: root/lib/git/refs.py
diff options
context:
space:
mode:
authorSebastian Thiel <byronimo@gmail.com>2010-11-18 23:11:32 +0100
committerSebastian Thiel <byronimo@gmail.com>2010-11-18 23:11:32 +0100
commitf1545bd9cd6953c5b39c488bf7fe179676060499 (patch)
tree8bd4b8829768fe0195d41a3aa067aa1ac7435605 /lib/git/refs.py
parenta1d1d2cb421f16bd277d7c4ce88398ff0f5afb29 (diff)
parent7cf2d5fcf0a3db793678dd6ba9fc1c24d4eeb36a (diff)
downloadgitpython-f1545bd9cd6953c5b39c488bf7fe179676060499.tar.gz
Merge branch 'submodule'
Diffstat (limited to 'lib/git/refs.py')
-rw-r--r--lib/git/refs.py133
1 files changed, 126 insertions, 7 deletions
diff --git a/lib/git/refs.py b/lib/git/refs.py
index af7284ff..fcf5fd10 100644
--- a/lib/git/refs.py
+++ b/lib/git/refs.py
@@ -29,6 +29,11 @@ from gitdb.util import (
hex_to_bin
)
+from config import (
+ GitConfigParser,
+ SectionConstraint
+ )
+
from exc import GitCommandError
__all__ = ("SymbolicReference", "Reference", "HEAD", "Head", "TagReference",
@@ -219,12 +224,30 @@ class SymbolicReference(object):
# END end try string
# END try commit attribute
+ # maintain the orig-head if we are currently checked-out
+ head = HEAD(self.repo)
+ try:
+ if head.ref == self:
+ try:
+ # TODO: implement this atomically, if we fail below, orig_head is at an incorrect spot
+ # Enforce the creation of ORIG_HEAD
+ SymbolicReference.create(self.repo, head.orig_head().name, self.commit, force=True)
+ except ValueError:
+ pass
+ #END exception handling
+ # END if we are checked-out
+ except TypeError:
+ pass
+ # END handle detached heads
+
# 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
+ # Otherwise we detach it and have to do it manually. Besides, this works
+ # recursively automaitcally, but should be replaced with a python implementation
+ # soon
if write_value.startswith('ref:'):
self.repo.git.symbolic_ref(self.path, write_value[5:])
- return
+ return
# END non-detached handling
path = self._abs_path()
@@ -238,10 +261,10 @@ class SymbolicReference(object):
finally:
fp.close()
# END writing
-
- reference = property(_get_reference, _set_reference, doc="Returns the Reference we point to")
- # alias
+
+ # aliased reference
+ reference = property(_get_reference, _set_reference, doc="Returns the Reference we point to")
ref = reference
def is_valid(self):
@@ -272,7 +295,7 @@ class SymbolicReference(object):
@classmethod
def to_full_path(cls, path):
"""
- :return: string with a full path name which can be used to initialize
+ :return: string with a full repository-relative path which can be used to initialize
a Reference instance, for instance by using ``Reference.from_path``"""
if isinstance(path, SymbolicReference):
path = path.path
@@ -489,6 +512,8 @@ class SymbolicReference(object):
@classmethod
def from_path(cls, repo, path):
"""
+ :param path: full .git-directory-relative path name to the Reference to instantiate
+ :note: use to_full_path() if you only have a partial path of a known Reference Type
:return:
Instance of type Reference, Head, or Tag
depending on the given path"""
@@ -546,7 +571,6 @@ class Reference(SymbolicReference, LazyMixin, Iterable):
:note:
TypeChecking is done by the git command"""
- # check for existence, touch it if required
abs_path = self._abs_path()
existed = True
if not isfile(abs_path):
@@ -611,6 +635,7 @@ class HEAD(SymbolicReference):
"""Special case of a Symbolic Reference as it represents the repository's
HEAD reference."""
_HEAD_NAME = 'HEAD'
+ _ORIG_HEAD_NAME = 'ORIG_HEAD'
__slots__ = tuple()
def __init__(self, repo, path=_HEAD_NAME):
@@ -618,6 +643,27 @@ class HEAD(SymbolicReference):
raise ValueError("HEAD instance must point to %r, got %r" % (self._HEAD_NAME, path))
super(HEAD, self).__init__(repo, path)
+ def orig_head(self):
+ """:return: SymbolicReference pointing at the ORIG_HEAD, which is maintained
+ to contain the previous value of HEAD"""
+ return SymbolicReference(self.repo, self._ORIG_HEAD_NAME)
+
+ def _set_reference(self, ref):
+ """If someone changes the reference through us, we must manually update
+ the ORIG_HEAD if we are detached. The underlying implementation can only
+ handle un-detached heads as it has to check whether the current head
+ is the checked-out one"""
+ if self.is_detached:
+ prev_commit = self.commit
+ super(HEAD, self)._set_reference(ref)
+ SymbolicReference.create(self.repo, self._ORIG_HEAD_NAME, prev_commit, force=True)
+ else:
+ super(HEAD, self)._set_reference(ref)
+ # END handle detached mode
+
+ # aliased reference
+ reference = property(SymbolicReference._get_reference, _set_reference, doc="Returns the Reference we point to")
+ ref = reference
def reset(self, commit='HEAD', index=True, working_tree = False,
paths=None, **kwargs):
@@ -699,6 +745,8 @@ class Head(Reference):
>>> head.commit.hexsha
'1c09f116cbc2cb4100fb6935bb162daa4723f455'"""
_common_path_default = "refs/heads"
+ k_config_remote = "remote"
+ k_config_remote_ref = "merge" # branch to merge from remote
@classmethod
def create(cls, repo, path, commit='HEAD', force=False, **kwargs):
@@ -745,6 +793,44 @@ class Head(Reference):
flag = "-D"
repo.git.branch(flag, *heads)
+
+ def set_tracking_branch(self, remote_reference):
+ """Configure this branch to track the given remote reference. This will alter
+ this branch's configuration accordingly.
+ :param remote_reference: The remote reference to track or None to untrack
+ any references
+ :return: self"""
+ if remote_reference is not None and not isinstance(remote_reference, RemoteReference):
+ raise ValueError("Incorrect parameter type: %r" % remote_reference)
+ # END handle type
+
+ writer = self.config_writer()
+ if remote_reference is None:
+ writer.remove_option(self.k_config_remote)
+ writer.remove_option(self.k_config_remote_ref)
+ if len(writer.options()) == 0:
+ writer.remove_section()
+ # END handle remove section
+ else:
+ writer.set_value(self.k_config_remote, remote_reference.remote_name)
+ writer.set_value(self.k_config_remote_ref, Head.to_full_path(remote_reference.remote_head))
+ # END handle ref value
+
+ return self
+
+
+ def tracking_branch(self):
+ """:return: The remote_reference we are tracking, or None if we are
+ not a tracking branch"""
+ reader = self.config_reader()
+ if reader.has_option(self.k_config_remote) and reader.has_option(self.k_config_remote_ref):
+ ref = Head(self.repo, Head.to_full_path(reader.get_value(self.k_config_remote_ref)))
+ remote_refpath = RemoteReference.to_full_path(join_path(reader.get_value(self.k_config_remote), ref.name))
+ return RemoteReference(self.repo, remote_refpath)
+ # END handle have tracking branch
+
+ # we are not a tracking branch
+ return None
def rename(self, new_path, force=False):
"""Rename self to a new path
@@ -798,6 +884,29 @@ class Head(Reference):
self.repo.git.checkout(self, **kwargs)
return self.repo.active_branch
+ #{ Configruation
+
+ def _config_parser(self, read_only):
+ if read_only:
+ parser = self.repo.config_reader()
+ else:
+ parser = self.repo.config_writer()
+ # END handle parser instance
+
+ return SectionConstraint(parser, 'branch "%s"' % self.name)
+
+ def config_reader(self):
+ """:return: A configuration parser instance constrained to only read
+ this instance's values"""
+ return self._config_parser(read_only=True)
+
+ def config_writer(self):
+ """:return: A configuration writer instance with read-and write acccess
+ to options of this head"""
+ return self._config_parser(read_only=False)
+
+ #} END configuration
+
class TagReference(Reference):
"""Class representing a lightweight tag reference which either points to a commit
@@ -891,6 +1000,16 @@ class RemoteReference(Head):
"""Represents a reference pointing to a remote head."""
_common_path_default = "refs/remotes"
+
+ @classmethod
+ def iter_items(cls, repo, common_path = None, remote=None):
+ """Iterate remote references, and if given, constrain them to the given remote"""
+ common_path = common_path or cls._common_path_default
+ if remote is not None:
+ common_path = join_path(common_path, str(remote))
+ # END handle remote constraint
+ return super(RemoteReference, cls).iter_items(repo, common_path)
+
@property
def remote_name(self):
"""