diff options
author | Sebastian Thiel <byronimo@gmail.com> | 2010-07-06 00:35:30 +0200 |
---|---|---|
committer | Sebastian Thiel <byronimo@gmail.com> | 2010-07-06 00:35:30 +0200 |
commit | f963881e53a9f0a2746a11cb9cdfa82eb1f90d8c (patch) | |
tree | ea69501e471b2bc724b641cb360f1b15c095c9d6 /lib/git | |
parent | a4287f65878000b42d11704692f9ea3734014b4c (diff) | |
download | gitpython-f963881e53a9f0a2746a11cb9cdfa82eb1f90d8c.tar.gz |
Initial version of the rev-parse routine, which doesn't work too bad, but its still rather slow and many tests are not yet implemented
Diffstat (limited to 'lib/git')
-rw-r--r-- | lib/git/exc.py | 4 | ||||
m--------- | lib/git/ext/gitdb | 0 | ||||
-rw-r--r-- | lib/git/objects/base.py | 11 | ||||
-rw-r--r-- | lib/git/refs.py | 19 | ||||
-rw-r--r-- | lib/git/repo.py | 165 |
5 files changed, 196 insertions, 3 deletions
diff --git a/lib/git/exc.py b/lib/git/exc.py index 93919d5e..d2cb8d7e 100644 --- a/lib/git/exc.py +++ b/lib/git/exc.py @@ -1,10 +1,12 @@ -# errors.py +# exc.py # Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors # # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php """ Module containing all exceptions thrown througout the git package, """ +from gitdb.exc import * + class InvalidGitRepositoryError(Exception): """ Thrown if the given repository appears to have an invalid format. """ diff --git a/lib/git/ext/gitdb b/lib/git/ext/gitdb -Subproject 6c8721a7d5d32e54bb4ffd3725ed23ac5d76a59 +Subproject 46bf4710e0f7184ac4875e8037de30b5081bfda diff --git a/lib/git/objects/base.py b/lib/git/objects/base.py index d4a46788..21b9b1ea 100644 --- a/lib/git/objects/base.py +++ b/lib/git/objects/base.py @@ -53,6 +53,17 @@ class Object(LazyMixin): inst = get_object_type_by_name(typename)(repo, hex_to_bin(hexsha)) inst.size = size return inst + + @classmethod + def new_from_sha(cls, repo, sha1): + """ + :return: new object instance of a type appropriate to represent the given + binary sha1 + :param sha1: 20 byte binary sha1""" + oinfo = repo.odb.info(sha1) + inst = get_object_type_by_name(oinfo.type)(repo, oinfo.binsha) + inst.size = oinfo.size + return inst def _set_self_from_args_(self, args_dict): """Initialize attributes on self from the given dict that was retrieved diff --git a/lib/git/refs.py b/lib/git/refs.py index 343a0afb..a466e419 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -68,7 +68,7 @@ class SymbolicReference(object): :return: In case of symbolic references, the shortest assumable name is the path itself.""" - return self.path + return self.path def _abs_path(self): return join_path_native(self.repo.git_dir, self.path) @@ -109,6 +109,19 @@ class SymbolicReference(object): # I believe files are closing themselves on destruction, so it is # alright. + @classmethod + def dereference_recursive(cls, repo, ref_path): + """ + :return: hexsha stored in the reference at the given ref_path, recursively dereferencing all + intermediate references as required + :param repo: the repository containing the reference at ref_path""" + while True: + ref = cls(repo, ref_path) + hexsha, ref_path = ref._get_ref_info() + if hexsha is not None: + return hexsha + # END recursive dereferencing + def _get_ref_info(self): """Return: (sha, target_ref_path) if available, the sha the file at rela_path points to, or None. target_ref_path is the reference we @@ -795,6 +808,10 @@ class TagReference(Reference): raise ValueError( "Tag %s points to a Blob or Tree - have never seen that before" % self ) @property + def tree(self): + return self.commit.tree + + @property def tag(self): """ :return: Tag object this tag ref points to or None in case diff --git a/lib/git/repo.py b/lib/git/repo.py index 62202364..8e97adee 100644 --- a/lib/git/repo.py +++ b/lib/git/repo.py @@ -12,12 +12,13 @@ from index import IndexFile from objects import * from config import GitConfigParser from remote import Remote - +from string import digits from db import ( GitCmdObjectDB, GitDB ) +from gitdb.exc import BadObject from gitdb.util import ( join, isdir, @@ -70,6 +71,7 @@ class Repo(object): # precompiled regex re_whitespace = re.compile(r'\s+') re_hexsha_only = re.compile('^[0-9A-Fa-f]{40}$') + re_hexsha_shortened = re.compile('^[0-9A-Fa-f]{7:40}$') re_author_committer_start = re.compile(r'^(author|committer)') re_tab_full_line = re.compile(r'^\t(.*)$') @@ -698,6 +700,167 @@ class Repo(object): self.git.archive(treeish, **kwargs) return self + + def rev_parse(self, rev): + """ + :return: Object at the given revision, either Commit, Tag, Tree or Blob + :param rev: git-rev-parse compatible revision specification, please see + http://www.kernel.org/pub/software/scm/git/docs/git-rev-parse.html + for details + :note: Currently there is no access to the rev-log, rev-specs may only contain + topological tokens such ~ and ^. + :raise BadObject: if the given revision could not be found""" + if '@' in rev: + raise ValueError("There is no rev-log support yet") + + + # colon search mode ? + if rev.startswith(':/'): + # colon search mode + raise NotImplementedError("commit by message search ( regex )") + # END handle search + + # return object specified by the given name + def name_to_object(name): + hexsha = None + + # is it a hexsha ? + if self.re_hexsha_shortened.match(name): + if len(name) != 40: + # find long sha for short sha + raise NotImplementedError("short sha parsing") + else: + hexsha = name + # END handle short shas + else: + for base in ('%s', 'refs/%s', 'refs/tags/%s', 'refs/heads/%s', 'refs/remotes/%s', 'refs/remotes/%s/HEAD'): + try: + hexsha = SymbolicReference.dereference_recursive(self, base % name) + break + except ValueError: + pass + # END for each base + # END handle hexsha + + # tried everything ? fail + if hexsha is None: + raise BadObject(name) + # END assert hexsha was found + + return Object.new_from_sha(self, hex_to_bin(hexsha)) + # END object by name + + obj = None + output_type = "commit" + start = 0 + parsed_to = 0 + lr = len(rev) + while start < lr and start != -1: + if rev[start] not in "^~:": + start += 1 + continue + # END handle start + + if obj is None: + # token is a rev name + obj = name_to_object(rev[:start]) + # END initialize obj on first token + + token = rev[start] + start += 1 + + # try to parse {type} + if start < lr and rev[start] == '{': + end = rev.find('}', start) + if end == -1: + raise ValueError("Missing closing brace to define type in %s" % rev) + output_type = rev[start+1:end] # exclude brace + + # handle type + if output_type == 'commit': + pass # default + elif output_type == 'tree': + try: + obj = obj.tree + except AttributeError: + pass # error raised later + # END exception handling + elif output_type in ('', 'blob'): + while True: + try: + obj = obj.object + except AttributeError: + break + # END dereference tag + else: + raise ValueError("Invalid output type: %s ( in %s )" % (output_type, rev)) + # END handle output type + + if obj.type != output_type: + raise ValueError("Could not accomodate requested object type %s, got %s" % (output_type, obj.type)) + # END verify ouput type + + start = end+1 # skip brace + parsed_to = start + continue + # END parse type + + # try to parse a number + num = 0 + if token != ":": + while start < lr: + if rev[start] in digits: + num = num * 10 + int(rev[start]) + start += 1 + else: + break + # END handle number + # END number parse loop + + # no explicit number given, 1 is the default + if num == 0: + num = 1 + # END set default num + # END number parsing only if non-blob mode + + + parsed_to = start + # handle hiererarchy walk + try: + if token == "~": + for item in xrange(num): + obj = obj.parents[0] + # END for each history item to walk + elif token == "^": + # must be n'th parent + obj = obj.parents[num-1] + elif token == ":": + if obj.type != "tree": + obj = obj.tree + # END get tree type + obj = obj[rev[start:]] + parsed_to = lr + else: + raise "Invalid token: %r" % token + # END end handle tag + except (IndexError, AttributeError): + raise BadObject("Invalid Revision") + # END exception handling + # END parse loop + + # still no obj ? Its probably a simple name + if obj is None: + obj = name_to_object(rev) + parsed_to = lr + # END handle simple name + + if obj is None: + raise ValueError("Revision specifier could not be parsed: %s" % rev) + + if parsed_to != lr: + raise ValueError("Didn't consume complete rev spec %s, consumed part: %s" % (rev, rev[:parsed_to])) + + return obj def __repr__(self): return '<git.Repo "%s">' % self.git_dir |