summaryrefslogtreecommitdiff
path: root/lib/git
diff options
context:
space:
mode:
authorSebastian Thiel <byronimo@gmail.com>2010-07-06 00:35:30 +0200
committerSebastian Thiel <byronimo@gmail.com>2010-07-06 00:35:30 +0200
commitf963881e53a9f0a2746a11cb9cdfa82eb1f90d8c (patch)
treeea69501e471b2bc724b641cb360f1b15c095c9d6 /lib/git
parenta4287f65878000b42d11704692f9ea3734014b4c (diff)
downloadgitpython-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.py4
m---------lib/git/ext/gitdb0
-rw-r--r--lib/git/objects/base.py11
-rw-r--r--lib/git/refs.py19
-rw-r--r--lib/git/repo.py165
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