summaryrefslogtreecommitdiff
path: root/lib/git
diff options
context:
space:
mode:
authorSebastian Thiel <byronimo@gmail.com>2010-07-06 20:21:52 +0200
committerSebastian Thiel <byronimo@gmail.com>2010-07-06 20:21:52 +0200
commit9059525a75b91e6eb6a425f1edcc608739727168 (patch)
tree3c3985fc1bc5efe0d9a19ceafdfc8d482297192b /lib/git
parent73959f3a2d4f224fbda03c8a8850f66f53d8cb3b (diff)
downloadgitpython-9059525a75b91e6eb6a425f1edcc608739727168.tar.gz
Made repo.py a package to allow better localization of functions and utilities - the repo module got rather large
Diffstat (limited to 'lib/git')
-rw-r--r--lib/git/index/__init__.py2
-rw-r--r--lib/git/refs.py3
-rw-r--r--lib/git/repo/__init__.py3
-rw-r--r--lib/git/repo/base.py (renamed from lib/git/repo.py)265
-rw-r--r--lib/git/repo/fun.py224
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