diff options
Diffstat (limited to 'lib/git')
-rw-r--r-- | lib/git/index/__init__.py | 2 | ||||
-rw-r--r-- | lib/git/refs.py | 3 | ||||
-rw-r--r-- | lib/git/repo/__init__.py | 3 | ||||
-rw-r--r-- | lib/git/repo/base.py (renamed from lib/git/repo.py) | 265 | ||||
-rw-r--r-- | lib/git/repo/fun.py | 224 |
5 files changed, 260 insertions, 237 deletions
diff --git a/lib/git/index/__init__.py b/lib/git/index/__init__.py index 13f874b0..fe4a7f59 100644 --- a/lib/git/index/__init__.py +++ b/lib/git/index/__init__.py @@ -1,4 +1,4 @@ -"""Initialize the index module""" +"""Initialize the index package""" from base import * from typ import *
\ No newline at end of file diff --git a/lib/git/refs.py b/lib/git/refs.py index 8b773ae7..be094d01 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -208,9 +208,8 @@ class SymbolicReference(object): try: write_value = ref.commit.hexsha except AttributeError: - sha = str(ref) try: - obj = Object.new_from_sha(self.repo, hex_to_bin(sha)) + obj = self.repo.rev_parse(ref+"^{}") # optionally deref tags if obj.type != "commit": raise TypeError("Invalid object type behind sha: %s" % sha) write_value = obj.hexsha diff --git a/lib/git/repo/__init__.py b/lib/git/repo/__init__.py new file mode 100644 index 00000000..8902a254 --- /dev/null +++ b/lib/git/repo/__init__.py @@ -0,0 +1,3 @@ +"""Initialize the Repo package""" + +from base import *
\ No newline at end of file diff --git a/lib/git/repo.py b/lib/git/repo/base.py index e9dfabcd..976a68bf 100644 --- a/lib/git/repo.py +++ b/lib/git/repo/base.py @@ -4,28 +4,32 @@ # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php -from exc import InvalidGitRepositoryError, NoSuchPathError -from cmd import Git -from objects import Actor -from refs import * -from index import IndexFile -from objects import * -from config import GitConfigParser -from remote import Remote -from string import digits -from db import ( +from git.exc import InvalidGitRepositoryError, NoSuchPathError +from git.cmd import Git +from git.objects import Actor +from git.refs import * +from git.index import IndexFile +from git.objects import * +from git.config import GitConfigParser +from git.remote import Remote +from git.db import ( GitCmdObjectDB, GitDB ) -from gitdb.exc import BadObject + from gitdb.util import ( join, - isdir, isfile, - join, hex_to_bin ) + +from fun import ( + rev_parse, + is_git_dir, + touch + ) + import os import sys import re @@ -33,23 +37,6 @@ import re __all__ = ('Repo', ) -def touch(filename): - fp = open(filename, "a") - fp.close() - -def is_git_dir(d): - """ This is taken from the git setup.c:is_git_directory - function.""" - - if isdir(d) and \ - isdir(join(d, 'objects')) and \ - isdir(join(d, 'refs')): - headref = join(d, 'HEAD') - return isfile(headref) or \ - (os.path.islink(headref) and - os.readlink(headref).startswith('refs')) - return False - class Repo(object): """Represents a git repository and allows you to query references, @@ -112,7 +99,7 @@ class Repo(object): self.git_dir = curpath self._working_tree_dir = os.path.dirname(curpath) break - gitpath = os.path.join(curpath, '.git') + gitpath = join(curpath, '.git') if is_git_dir(gitpath): self.git_dir = gitpath self._working_tree_dir = curpath @@ -142,7 +129,7 @@ class Repo(object): self.git = Git(self.working_dir) # special handling, in special times - args = [os.path.join(self.git_dir, 'objects')] + args = [join(self.git_dir, 'objects')] if issubclass(odbt, GitCmdObjectDB): args.append(self.git) self.odb = odbt(*args) @@ -163,11 +150,11 @@ class Repo(object): # Description property def _get_description(self): - filename = os.path.join(self.git_dir, 'description') + filename = join(self.git_dir, 'description') return file(filename).read().rstrip() def _set_description(self, descr): - filename = os.path.join(self.git_dir, 'description') + filename = join(self.git_dir, 'description') file(filename, 'w').write(descr+'\n') description = property(_get_description, _set_description, @@ -396,11 +383,11 @@ class Repo(object): return Commit.iter_items(self, rev, paths, **kwargs) def _get_daemon_export(self): - filename = os.path.join(self.git_dir, self.DAEMON_EXPORT_FILE) + filename = join(self.git_dir, self.DAEMON_EXPORT_FILE) return os.path.exists(filename) def _set_daemon_export(self, value): - filename = os.path.join(self.git_dir, self.DAEMON_EXPORT_FILE) + filename = join(self.git_dir, self.DAEMON_EXPORT_FILE) fileexists = os.path.exists(filename) if value and not fileexists: touch(filename) @@ -416,7 +403,7 @@ class Repo(object): """The list of alternates for this repo from which objects can be retrieved :return: list of strings being pathnames of alternates""" - alternates_path = os.path.join(self.git_dir, 'objects', 'info', 'alternates') + alternates_path = join(self.git_dir, 'objects', 'info', 'alternates') if os.path.exists(alternates_path): try: @@ -439,9 +426,9 @@ class Repo(object): :note: The method does not check for the existance of the paths in alts as the caller is responsible.""" - alternates_path = os.path.join(self.git_dir, 'objects', 'info', 'alternates') + alternates_path = join(self.git_dir, 'objects', 'info', 'alternates') if not alts: - if os.path.isfile(alternates_path): + if isfile(alternates_path): os.remove(alternates_path) else: try: @@ -469,7 +456,7 @@ class Repo(object): default_args = ('--abbrev=40', '--full-index', '--raw') if index: # diff index against HEAD - if os.path.isfile(self.index.path) and self.head.is_valid() and \ + if isfile(self.index.path) and self.head.is_valid() and \ len(self.git.diff('HEAD', '--cached', *default_args)): return True # END index handling @@ -677,7 +664,7 @@ class Repo(object): # our git command could have a different working dir than our actual # environment, hence we prepend its working dir if required if not os.path.isabs(path) and self.git.working_dir: - path = os.path.join(self.git._working_dir, path) + path = join(self.git._working_dir, path) return Repo(os.path.abspath(path), odbt = odbt) @@ -696,203 +683,13 @@ class Repo(object): if treeish is None: treeish = self.active_branch if prefix and 'prefix' not in kwargs: - kwargs['prefix'] = prefix + kwargs['prefix'] = prefix kwargs['output_stream'] = ostream self.git.archive(treeish, **kwargs) return self + + rev_parse = rev_parse - 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 ? Try the most common ones, which is 7 to 40 - 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: - # it could also be a very short ( less than 7 ) hexsha, which - # wasnt tested in the first run - if len(name) < 7 and self.re_hexsha_domain.match(name): - raise NotImplementedError() - # END try short name - raise BadObject(name) - # END assert hexsha was found - - return Object.new_from_sha(self, hex_to_bin(hexsha)) - # END object by name - - def deref_tag(tag): - while True: - try: - tag = tag.object - except AttributeError: - break - # END dereference tag - return tag - - def to_commit(obj): - if obj.type == 'tag': - obj = deref_tag(obj) - - if obj.type != "commit": - raise ValueError("Cannot convert object %r to type commit" % obj) - # END verify type - return obj - # END commit converter - - obj = None - output_type = "commit" - start = 0 - parsed_to = 0 - lr = len(rev) - while start < lr: - 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 = to_commit(obj).tree - except (AttributeError, ValueError): - pass # error raised later - # END exception handling - elif output_type in ('', 'blob'): - if obj.type == 'tag': - obj = deref_tag(obj) - else: - # cannot do anything for non-tags - pass - # END handle tag - else: - raise ValueError("Invalid output type: %s ( in %s )" % (output_type, rev)) - # END handle output type - - # empty output types don't require any specific type, its just about dereferencing tags - if output_type and obj.type != output_type: - raise ValueError("Could not accomodate requested object type %r, 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 != ":": - found_digit = False - while start < lr: - if rev[start] in digits: - num = num * 10 + int(rev[start]) - start += 1 - found_digit = True - else: - break - # END handle number - # END number parse loop - - # no explicit number given, 1 is the default - # It could be 0 though - if not found_digit: - num = 1 - # END set default num - # END number parsing only if non-blob mode - - - parsed_to = start - # handle hiererarchy walk - try: - if token == "~": - obj = to_commit(obj) - for item in xrange(num): - obj = obj.parents[0] - # END for each history item to walk - elif token == "^": - obj = to_commit(obj) - # must be n'th parent - if num: - 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 ValueError("Invalid token: %r" % token) - # END end handle tag - except (IndexError, AttributeError): - raise BadObject("Invalid Revision in %s" % rev) - # 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 diff --git a/lib/git/repo/fun.py b/lib/git/repo/fun.py new file mode 100644 index 00000000..ab2eb8be --- /dev/null +++ b/lib/git/repo/fun.py @@ -0,0 +1,224 @@ +"""Package with general repository related functions""" + +from gitdb.exc import BadObject +from git.refs import SymbolicReference +from git.objects import Object +from gitdb.util import ( + join, + isdir, + isfile, + hex_to_bin + ) + +from string import digits + +__all__ = ('rev_parse', 'is_git_dir', 'touch') + +def touch(filename): + fp = open(filename, "a") + fp.close() + +def is_git_dir(d): + """ This is taken from the git setup.c:is_git_directory + function.""" + if isdir(d) and \ + isdir(join(d, 'objects')) and \ + isdir(join(d, 'refs')): + headref = join(d, 'HEAD') + return isfile(headref) or \ + (os.path.islink(headref) and + os.readlink(headref).startswith('refs')) + return False + +def name_to_object(repo, name): + """:return: object specified by the given name, hexshas ( short and long ) + as well as references are supported""" + hexsha = None + + # is it a hexsha ? Try the most common ones, which is 7 to 40 + if repo.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(repo, base % name) + break + except ValueError: + pass + # END for each base + # END handle hexsha + + # tried everything ? fail + if hexsha is None: + # it could also be a very short ( less than 7 ) hexsha, which + # wasnt tested in the first run + if len(name) < 7 and repo.re_hexsha_domain.match(name): + raise NotImplementedError() + # END try short name + raise BadObject(name) + # END assert hexsha was found + + return Object.new_from_sha(repo, hex_to_bin(hexsha)) + +def deref_tag(tag): + """Recursively dereerence a tag and return the resulting object""" + while True: + try: + tag = tag.object + except AttributeError: + break + # END dereference tag + return tag + +def to_commit(obj): + """Convert the given object to a commit if possible and return it""" + if obj.type == 'tag': + obj = deref_tag(obj) + + if obj.type != "commit": + raise ValueError("Cannot convert object %r to type commit" % obj) + # END verify type + return obj + +def rev_parse(repo, 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 + + obj = None + output_type = "commit" + start = 0 + parsed_to = 0 + lr = len(rev) + while start < lr: + if rev[start] not in "^~:": + start += 1 + continue + # END handle start + + if obj is None: + # token is a rev name + obj = name_to_object(repo, 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 = to_commit(obj).tree + except (AttributeError, ValueError): + pass # error raised later + # END exception handling + elif output_type in ('', 'blob'): + if obj.type == 'tag': + obj = deref_tag(obj) + else: + # cannot do anything for non-tags + pass + # END handle tag + else: + raise ValueError("Invalid output type: %s ( in %s )" % (output_type, rev)) + # END handle output type + + # empty output types don't require any specific type, its just about dereferencing tags + if output_type and obj.type != output_type: + raise ValueError("Could not accomodate requested object type %r, 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 != ":": + found_digit = False + while start < lr: + if rev[start] in digits: + num = num * 10 + int(rev[start]) + start += 1 + found_digit = True + else: + break + # END handle number + # END number parse loop + + # no explicit number given, 1 is the default + # It could be 0 though + if not found_digit: + num = 1 + # END set default num + # END number parsing only if non-blob mode + + + parsed_to = start + # handle hiererarchy walk + try: + if token == "~": + obj = to_commit(obj) + for item in xrange(num): + obj = obj.parents[0] + # END for each history item to walk + elif token == "^": + obj = to_commit(obj) + # must be n'th parent + if num: + 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 ValueError("Invalid token: %r" % token) + # END end handle tag + except (IndexError, AttributeError): + raise BadObject("Invalid Revision in %s" % rev) + # END exception handling + # END parse loop + + # still no obj ? Its probably a simple name + if obj is None: + obj = name_to_object(repo, 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 |