diff options
author | Sebastian Thiel <byronimo@gmail.com> | 2010-11-16 11:05:31 +0100 |
---|---|---|
committer | Sebastian Thiel <byronimo@gmail.com> | 2010-11-16 11:06:12 +0100 |
commit | 9f73e8ba55f33394161b403bf7b8c2e0e05f47b0 (patch) | |
tree | f537d42a36e2424a240bcf4f1a4440e3e3acf4a4 | |
parent | af5abca21b56fcf641ff916bd567680888c364aa (diff) | |
download | gitpython-9f73e8ba55f33394161b403bf7b8c2e0e05f47b0.tar.gz |
remote: added methods to set and query the tracking branch status of normal heads, including test.
Config: SectionConstraint was updated with additional callable methods, the complete ConfigParser interface should be covered now
Remote: refs methods is much more efficient now as it will set the search path to the directory containing the remote refs - previously it used the remotes/ base directory and pruned the search result
-rw-r--r-- | lib/git/config.py | 3 | ||||
-rw-r--r-- | lib/git/refs.py | 78 | ||||
-rw-r--r-- | lib/git/remote.py | 6 | ||||
-rw-r--r-- | test/git/test_refs.py | 24 |
4 files changed, 103 insertions, 8 deletions
diff --git a/lib/git/config.py b/lib/git/config.py index 073efd63..0528f318 100644 --- a/lib/git/config.py +++ b/lib/git/config.py @@ -74,7 +74,8 @@ class SectionConstraint(object): It supports all ConfigParser methods that operate on an option""" __slots__ = ("_config", "_section_name") - _valid_attrs_ = ("get_value", "set_value", "get", "set", "getint", "getfloat", "getboolean", "has_option") + _valid_attrs_ = ("get_value", "set_value", "get", "set", "getint", "getfloat", "getboolean", "has_option", + "remove_section", "remove_option", "options") def __init__(self, config, section): self._config = config diff --git a/lib/git/refs.py b/lib/git/refs.py index 3dc73d03..39c5ff29 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", @@ -701,6 +706,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): @@ -747,6 +754,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 @@ -800,6 +845,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 @@ -893,6 +961,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): """ diff --git a/lib/git/remote.py b/lib/git/remote.py index 135e37d7..5124c603 100644 --- a/lib/git/remote.py +++ b/lib/git/remote.py @@ -468,11 +468,7 @@ class Remote(LazyMixin, Iterable): you to omit the remote path portion, i.e.:: remote.refs.master # yields RemoteReference('/refs/remotes/origin/master')""" out_refs = IterableList(RemoteReference._id_attribute_, "%s/" % self.name) - for ref in RemoteReference.list_items(self.repo): - if ref.remote_name == self.name: - out_refs.append(ref) - # END if names match - # END for each ref + out_refs.extend(RemoteReference.list_items(self.repo, remote=self.name)) assert out_refs, "Remote %s did not have any references" % self.name return out_refs diff --git a/test/git/test_refs.py b/test/git/test_refs.py index 5f13d0b7..4cfd952e 100644 --- a/test/git/test_refs.py +++ b/test/git/test_refs.py @@ -63,8 +63,9 @@ class TestRefs(TestBase): assert len(s) == ref_count assert len(s|s) == ref_count - def test_heads(self): - for head in self.rorepo.heads: + @with_rw_repo('HEAD', bare=False) + def test_heads(self, rwrepo): + for head in rwrepo.heads: assert head.name assert head.path assert "refs/heads" in head.path @@ -72,6 +73,23 @@ class TestRefs(TestBase): cur_object = head.object assert prev_object == cur_object # represent the same git object assert prev_object is not cur_object # but are different instances + + writer = head.config_writer() + tv = "testopt" + writer.set_value(tv, 1) + assert writer.get_value(tv) == 1 + del(writer) + assert head.config_reader().get_value(tv) == 1 + head.config_writer().remove_option(tv) + + # after the clone, we might still have a tracking branch setup + head.set_tracking_branch(None) + assert head.tracking_branch() is None + remote_ref = rwrepo.remotes[0].refs[0] + assert head.set_tracking_branch(remote_ref) is head + assert head.tracking_branch() == remote_ref + head.set_tracking_branch(None) + assert head.tracking_branch() is None # END for each head def test_refs(self): @@ -208,6 +226,8 @@ class TestRefs(TestBase): refs = remote.refs RemoteReference.delete(rw_repo, *refs) remote_refs_so_far += len(refs) + for ref in refs: + assert ref.remote_name == remote.name # END for each ref to delete assert remote_refs_so_far |