summaryrefslogtreecommitdiff
path: root/git/db/py
diff options
context:
space:
mode:
Diffstat (limited to 'git/db/py')
-rw-r--r--git/db/py/base.py870
-rw-r--r--git/db/py/complex.py186
-rw-r--r--git/db/py/loose.py472
-rw-r--r--git/db/py/mem.py188
-rw-r--r--git/db/py/pack.py378
-rw-r--r--git/db/py/ref.py134
-rw-r--r--git/db/py/resolve.py648
-rw-r--r--git/db/py/submodule.py44
-rw-r--r--git/db/py/transport.py78
9 files changed, 1499 insertions, 1499 deletions
diff --git a/git/db/py/base.py b/git/db/py/base.py
index d34e8b12..6710a0cc 100644
--- a/git/db/py/base.py
+++ b/git/db/py/base.py
@@ -6,29 +6,29 @@
from git.db.interface import *
from git.util import (
- pool,
- join,
- isfile,
- normpath,
- abspath,
- dirname,
- LazyMixin,
- hex_to_bin,
- bin_to_hex,
- expandvars,
- expanduser,
- exists,
- is_git_dir,
- )
+ pool,
+ join,
+ isfile,
+ normpath,
+ abspath,
+ dirname,
+ LazyMixin,
+ hex_to_bin,
+ bin_to_hex,
+ expandvars,
+ expanduser,
+ exists,
+ is_git_dir,
+ )
from git.index import IndexFile
from git.config import GitConfigParser
-from git.exc import (
- BadObject,
- AmbiguousObjectName,
- InvalidGitRepositoryError,
- NoSuchPathError
- )
+from git.exc import (
+ BadObject,
+ AmbiguousObjectName,
+ InvalidGitRepositoryError,
+ NoSuchPathError
+ )
from async import ChannelThreadTask
@@ -37,445 +37,445 @@ import sys
import os
-__all__ = ( 'PureObjectDBR', 'PureObjectDBW', 'PureRootPathDB', 'PureCompoundDB',
- 'PureConfigurationMixin', 'PureRepositoryPathsMixin', 'PureAlternatesFileMixin',
- 'PureIndexDB')
+__all__ = ( 'PureObjectDBR', 'PureObjectDBW', 'PureRootPathDB', 'PureCompoundDB',
+ 'PureConfigurationMixin', 'PureRepositoryPathsMixin', 'PureAlternatesFileMixin',
+ 'PureIndexDB')
class PureObjectDBR(ObjectDBR):
-
- #{ Query Interface
-
- def has_object_async(self, reader):
- task = ChannelThreadTask(reader, str(self.has_object_async), lambda sha: (sha, self.has_object(sha)))
- return pool.add_task(task)
-
- def info_async(self, reader):
- task = ChannelThreadTask(reader, str(self.info_async), self.info)
- return pool.add_task(task)
-
- def stream_async(self, reader):
- # base implementation just uses the stream method repeatedly
- task = ChannelThreadTask(reader, str(self.stream_async), self.stream)
- return pool.add_task(task)
-
- def partial_to_complete_sha_hex(self, partial_hexsha):
- len_partial_hexsha = len(partial_hexsha)
- if len_partial_hexsha % 2 != 0:
- partial_binsha = hex_to_bin(partial_hexsha + "0")
- else:
- partial_binsha = hex_to_bin(partial_hexsha)
- # END assure successful binary conversion
- return self.partial_to_complete_sha(partial_binsha, len(partial_hexsha))
-
- #} END query interface
-
-
+
+ #{ Query Interface
+
+ def has_object_async(self, reader):
+ task = ChannelThreadTask(reader, str(self.has_object_async), lambda sha: (sha, self.has_object(sha)))
+ return pool.add_task(task)
+
+ def info_async(self, reader):
+ task = ChannelThreadTask(reader, str(self.info_async), self.info)
+ return pool.add_task(task)
+
+ def stream_async(self, reader):
+ # base implementation just uses the stream method repeatedly
+ task = ChannelThreadTask(reader, str(self.stream_async), self.stream)
+ return pool.add_task(task)
+
+ def partial_to_complete_sha_hex(self, partial_hexsha):
+ len_partial_hexsha = len(partial_hexsha)
+ if len_partial_hexsha % 2 != 0:
+ partial_binsha = hex_to_bin(partial_hexsha + "0")
+ else:
+ partial_binsha = hex_to_bin(partial_hexsha)
+ # END assure successful binary conversion
+ return self.partial_to_complete_sha(partial_binsha, len(partial_hexsha))
+
+ #} END query interface
+
+
class PureObjectDBW(ObjectDBW):
-
- def __init__(self, *args, **kwargs):
- try:
- super(PureObjectDBW, self).__init__(*args, **kwargs)
- except TypeError:
- pass
- #END handle py 2.6
- self._ostream = None
-
- #{ Edit Interface
- def set_ostream(self, stream):
- cstream = self._ostream
- self._ostream = stream
- return cstream
-
- def ostream(self):
- return self._ostream
-
- def store_async(self, reader):
- task = ChannelThreadTask(reader, str(self.store_async), self.store)
- return pool.add_task(task)
-
- #} END edit interface
-
+
+ def __init__(self, *args, **kwargs):
+ try:
+ super(PureObjectDBW, self).__init__(*args, **kwargs)
+ except TypeError:
+ pass
+ #END handle py 2.6
+ self._ostream = None
+
+ #{ Edit Interface
+ def set_ostream(self, stream):
+ cstream = self._ostream
+ self._ostream = stream
+ return cstream
+
+ def ostream(self):
+ return self._ostream
+
+ def store_async(self, reader):
+ task = ChannelThreadTask(reader, str(self.store_async), self.store)
+ return pool.add_task(task)
+
+ #} END edit interface
+
class PureRootPathDB(RootPathDB):
-
- def __init__(self, root_path):
- self._root_path = root_path
- super(PureRootPathDB, self).__init__(root_path)
-
-
- #{ Interface
- def root_path(self):
- return self._root_path
-
- def db_path(self, rela_path=None):
- if not rela_path:
- return self._root_path
- return join(self._root_path, rela_path)
- #} END interface
-
+
+ def __init__(self, root_path):
+ self._root_path = root_path
+ super(PureRootPathDB, self).__init__(root_path)
+
+
+ #{ Interface
+ def root_path(self):
+ return self._root_path
+
+ def db_path(self, rela_path=None):
+ if not rela_path:
+ return self._root_path
+ return join(self._root_path, rela_path)
+ #} END interface
+
def _databases_recursive(database, output):
- """Fill output list with database from db, in order. Deals with Loose, Packed
- and compound databases."""
- if isinstance(database, CompoundDB):
- compounds = list()
- dbs = database.databases()
- output.extend(db for db in dbs if not isinstance(db, CompoundDB))
- for cdb in (db for db in dbs if isinstance(db, CompoundDB)):
- _databases_recursive(cdb, output)
- else:
- output.append(database)
- # END handle database type
-
+ """Fill output list with database from db, in order. Deals with Loose, Packed
+ and compound databases."""
+ if isinstance(database, CompoundDB):
+ compounds = list()
+ dbs = database.databases()
+ output.extend(db for db in dbs if not isinstance(db, CompoundDB))
+ for cdb in (db for db in dbs if isinstance(db, CompoundDB)):
+ _databases_recursive(cdb, output)
+ else:
+ output.append(database)
+ # END handle database type
+
class PureCompoundDB(CompoundDB, PureObjectDBR, LazyMixin, CachingDB):
- def _set_cache_(self, attr):
- if attr == '_dbs':
- self._dbs = list()
- else:
- super(PureCompoundDB, self)._set_cache_(attr)
-
- #{ PureObjectDBR interface
-
- def has_object(self, sha):
- for db in self._dbs:
- if db.has_object(sha):
- return True
- #END for each db
- return False
-
- def info(self, sha):
- for db in self._dbs:
- try:
- return db.info(sha)
- except BadObject:
- pass
- #END for each db
-
- def stream(self, sha):
- for db in self._dbs:
- try:
- return db.stream(sha)
- except BadObject:
- pass
- #END for each db
+ def _set_cache_(self, attr):
+ if attr == '_dbs':
+ self._dbs = list()
+ else:
+ super(PureCompoundDB, self)._set_cache_(attr)
+
+ #{ PureObjectDBR interface
+
+ def has_object(self, sha):
+ for db in self._dbs:
+ if db.has_object(sha):
+ return True
+ #END for each db
+ return False
+
+ def info(self, sha):
+ for db in self._dbs:
+ try:
+ return db.info(sha)
+ except BadObject:
+ pass
+ #END for each db
+
+ def stream(self, sha):
+ for db in self._dbs:
+ try:
+ return db.stream(sha)
+ except BadObject:
+ pass
+ #END for each db
- def size(self):
- return reduce(lambda x,y: x+y, (db.size() for db in self._dbs), 0)
-
- def sha_iter(self):
- return chain(*(db.sha_iter() for db in self._dbs))
-
- #} END object DBR Interface
-
- #{ Interface
-
- def databases(self):
- return tuple(self._dbs)
+ def size(self):
+ return reduce(lambda x,y: x+y, (db.size() for db in self._dbs), 0)
+
+ def sha_iter(self):
+ return chain(*(db.sha_iter() for db in self._dbs))
+
+ #} END object DBR Interface
+
+ #{ Interface
+
+ def databases(self):
+ return tuple(self._dbs)
- def update_cache(self, force=False):
- # something might have changed, clear everything
- stat = False
- for db in self._dbs:
- if isinstance(db, CachingDB):
- stat |= db.update_cache(force)
- # END if is caching db
- # END for each database to update
- return stat
-
- def partial_to_complete_sha_hex(self, partial_hexsha):
- len_partial_hexsha = len(partial_hexsha)
- if len_partial_hexsha % 2 != 0:
- partial_binsha = hex_to_bin(partial_hexsha + "0")
- else:
- partial_binsha = hex_to_bin(partial_hexsha)
- # END assure successful binary conversion
-
- candidate = None
- for db in self._dbs:
- full_bin_sha = None
- try:
- if hasattr(db, 'partial_to_complete_sha_hex'):
- full_bin_sha = db.partial_to_complete_sha_hex(partial_hexsha)
- else:
- full_bin_sha = db.partial_to_complete_sha(partial_binsha, len_partial_hexsha)
- # END handle database type
- except BadObject:
- continue
- # END ignore bad objects
- if full_bin_sha:
- if candidate and candidate != full_bin_sha:
- raise AmbiguousObjectName(partial_hexsha)
- candidate = full_bin_sha
- # END handle candidate
- # END for each db
- if not candidate:
- raise BadObject(partial_binsha)
- return candidate
-
- def partial_to_complete_sha(self, partial_binsha, hex_len):
- """Simple adaptor to feed into our implementation"""
- return self.partial_to_complete_sha_hex(bin_to_hex(partial_binsha)[:hex_len])
- #} END interface
-
-
+ def update_cache(self, force=False):
+ # something might have changed, clear everything
+ stat = False
+ for db in self._dbs:
+ if isinstance(db, CachingDB):
+ stat |= db.update_cache(force)
+ # END if is caching db
+ # END for each database to update
+ return stat
+
+ def partial_to_complete_sha_hex(self, partial_hexsha):
+ len_partial_hexsha = len(partial_hexsha)
+ if len_partial_hexsha % 2 != 0:
+ partial_binsha = hex_to_bin(partial_hexsha + "0")
+ else:
+ partial_binsha = hex_to_bin(partial_hexsha)
+ # END assure successful binary conversion
+
+ candidate = None
+ for db in self._dbs:
+ full_bin_sha = None
+ try:
+ if hasattr(db, 'partial_to_complete_sha_hex'):
+ full_bin_sha = db.partial_to_complete_sha_hex(partial_hexsha)
+ else:
+ full_bin_sha = db.partial_to_complete_sha(partial_binsha, len_partial_hexsha)
+ # END handle database type
+ except BadObject:
+ continue
+ # END ignore bad objects
+ if full_bin_sha:
+ if candidate and candidate != full_bin_sha:
+ raise AmbiguousObjectName(partial_hexsha)
+ candidate = full_bin_sha
+ # END handle candidate
+ # END for each db
+ if not candidate:
+ raise BadObject(partial_binsha)
+ return candidate
+
+ def partial_to_complete_sha(self, partial_binsha, hex_len):
+ """Simple adaptor to feed into our implementation"""
+ return self.partial_to_complete_sha_hex(bin_to_hex(partial_binsha)[:hex_len])
+ #} END interface
+
+
class PureRepositoryPathsMixin(RepositoryPathsMixin):
- # slots has no effect here, its just to keep track of used attrs
- __slots__ = ("_git_path", '_bare', '_working_tree_dir')
-
- #{ Configuration
- repo_dir = '.git'
- objs_dir = 'objects'
- #} END configuration
-
- #{ Subclass Interface
- def _initialize(self, path):
- epath = abspath(expandvars(expanduser(path or os.getcwd())))
+ # slots has no effect here, its just to keep track of used attrs
+ __slots__ = ("_git_path", '_bare', '_working_tree_dir')
+
+ #{ Configuration
+ repo_dir = '.git'
+ objs_dir = 'objects'
+ #} END configuration
+
+ #{ Subclass Interface
+ def _initialize(self, path):
+ epath = abspath(expandvars(expanduser(path or os.getcwd())))
- if not exists(epath):
- raise NoSuchPathError(epath)
- #END check file
+ if not exists(epath):
+ raise NoSuchPathError(epath)
+ #END check file
- self._working_tree_dir = None
- self._git_path = None
- curpath = epath
-
- # walk up the path to find the .git dir
- while curpath:
- if is_git_dir(curpath):
- self._git_path = curpath
- self._working_tree_dir = os.path.dirname(curpath)
- break
- gitpath = join(curpath, self.repo_dir)
- if is_git_dir(gitpath):
- self._git_path = gitpath
- self._working_tree_dir = curpath
- break
- curpath, dummy = os.path.split(curpath)
- if not dummy:
- break
- # END while curpath
-
- if self._git_path is None:
- raise InvalidGitRepositoryError(epath)
- # END path not found
+ self._working_tree_dir = None
+ self._git_path = None
+ curpath = epath
+
+ # walk up the path to find the .git dir
+ while curpath:
+ if is_git_dir(curpath):
+ self._git_path = curpath
+ self._working_tree_dir = os.path.dirname(curpath)
+ break
+ gitpath = join(curpath, self.repo_dir)
+ if is_git_dir(gitpath):
+ self._git_path = gitpath
+ self._working_tree_dir = curpath
+ break
+ curpath, dummy = os.path.split(curpath)
+ if not dummy:
+ break
+ # END while curpath
+
+ if self._git_path is None:
+ raise InvalidGitRepositoryError(epath)
+ # END path not found
- self._bare = self._working_tree_dir is None
- if hasattr(self, 'config_reader'):
- try:
- self._bare = self.config_reader("repository").getboolean('core','bare')
- except Exception:
- # lets not assume the option exists, although it should
- pass
- #END handle exception
- #END check bare flag
- self._working_tree_dir = self._bare and None or self._working_tree_dir
-
- #} end subclass interface
-
- #{ Object Interface
-
- def __eq__(self, rhs):
- if hasattr(rhs, 'git_dir'):
- return self.git_dir == rhs.git_dir
- return False
-
- def __ne__(self, rhs):
- return not self.__eq__(rhs)
-
- def __hash__(self):
- return hash(self.git_dir)
+ self._bare = self._working_tree_dir is None
+ if hasattr(self, 'config_reader'):
+ try:
+ self._bare = self.config_reader("repository").getboolean('core','bare')
+ except Exception:
+ # lets not assume the option exists, although it should
+ pass
+ #END handle exception
+ #END check bare flag
+ self._working_tree_dir = self._bare and None or self._working_tree_dir
+
+ #} end subclass interface
+
+ #{ Object Interface
+
+ def __eq__(self, rhs):
+ if hasattr(rhs, 'git_dir'):
+ return self.git_dir == rhs.git_dir
+ return False
+
+ def __ne__(self, rhs):
+ return not self.__eq__(rhs)
+
+ def __hash__(self):
+ return hash(self.git_dir)
- def __repr__(self):
- return "%s(%r)" % (type(self).__name__, self.git_dir)
-
- #} END object interface
-
- #{ Interface
-
- @property
- def is_bare(self):
- return self._bare
-
- @property
- def git_dir(self):
- return self._git_path
-
- @property
- def working_tree_dir(self):
- if self._working_tree_dir is None:
- raise AssertionError("Repository at %s is bare and does not have a working tree directory" % self.git_dir)
- #END assertion
- return dirname(self.git_dir)
-
- @property
- def objects_dir(self):
- return join(self.git_dir, self.objs_dir)
-
- @property
- def working_dir(self):
- if self.is_bare:
- return self.git_dir
- else:
- return self.working_tree_dir
- #END handle bare state
-
- def _mk_description():
- def _get_description(self):
- filename = join(self.git_dir, 'description')
- return file(filename).read().rstrip()
-
- def _set_description(self, descr):
- filename = join(self.git_dir, 'description')
- file(filename, 'w').write(descr+'\n')
-
- return property(_get_description, _set_description, "Descriptive text for the content of the repository")
+ def __repr__(self):
+ return "%s(%r)" % (type(self).__name__, self.git_dir)
+
+ #} END object interface
+
+ #{ Interface
+
+ @property
+ def is_bare(self):
+ return self._bare
+
+ @property
+ def git_dir(self):
+ return self._git_path
+
+ @property
+ def working_tree_dir(self):
+ if self._working_tree_dir is None:
+ raise AssertionError("Repository at %s is bare and does not have a working tree directory" % self.git_dir)
+ #END assertion
+ return dirname(self.git_dir)
+
+ @property
+ def objects_dir(self):
+ return join(self.git_dir, self.objs_dir)
+
+ @property
+ def working_dir(self):
+ if self.is_bare:
+ return self.git_dir
+ else:
+ return self.working_tree_dir
+ #END handle bare state
+
+ def _mk_description():
+ def _get_description(self):
+ filename = join(self.git_dir, 'description')
+ return file(filename).read().rstrip()
+
+ def _set_description(self, descr):
+ filename = join(self.git_dir, 'description')
+ file(filename, 'w').write(descr+'\n')
+
+ return property(_get_description, _set_description, "Descriptive text for the content of the repository")
- description = _mk_description()
- del(_mk_description)
-
- #} END interface
-
-
+ description = _mk_description()
+ del(_mk_description)
+
+ #} END interface
+
+
class PureConfigurationMixin(ConfigurationMixin):
-
- #{ Configuration
- system_config_file_name = "gitconfig"
- repo_config_file_name = "config"
- #} END
-
- def __new__(cls, *args, **kwargs):
- """This is just a stupid workaround for the evil py2.6 change which makes mixins quite impossible"""
- return super(PureConfigurationMixin, cls).__new__(cls, *args, **kwargs)
-
- def __init__(self, *args, **kwargs):
- """Verify prereqs"""
- try:
- super(PureConfigurationMixin, self).__init__(*args, **kwargs)
- except TypeError:
- pass
- #END handle code-breaking change in python 2.6
- assert hasattr(self, 'git_dir')
-
- def _path_at_level(self, level ):
- # we do not support an absolute path of the gitconfig on windows ,
- # use the global config instead
- if sys.platform == "win32" and level == "system":
- level = "global"
- #END handle windows
-
- if level == "system":
- return "/etc/%s" % self.system_config_file_name
- elif level == "global":
- return normpath(expanduser("~/.%s" % self.system_config_file_name))
- elif level == "repository":
- return join(self.git_dir, self.repo_config_file_name)
- #END handle level
-
- raise ValueError("Invalid configuration level: %r" % level)
-
- #{ Interface
-
- def config_reader(self, config_level=None):
- files = None
- if config_level is None:
- files = [ self._path_at_level(f) for f in self.config_level ]
- else:
- files = [ self._path_at_level(config_level) ]
- #END handle level
- return GitConfigParser(files, read_only=True)
-
- def config_writer(self, config_level="repository"):
- return GitConfigParser(self._path_at_level(config_level), read_only=False)
-
-
- #} END interface
-
-
+
+ #{ Configuration
+ system_config_file_name = "gitconfig"
+ repo_config_file_name = "config"
+ #} END
+
+ def __new__(cls, *args, **kwargs):
+ """This is just a stupid workaround for the evil py2.6 change which makes mixins quite impossible"""
+ return super(PureConfigurationMixin, cls).__new__(cls, *args, **kwargs)
+
+ def __init__(self, *args, **kwargs):
+ """Verify prereqs"""
+ try:
+ super(PureConfigurationMixin, self).__init__(*args, **kwargs)
+ except TypeError:
+ pass
+ #END handle code-breaking change in python 2.6
+ assert hasattr(self, 'git_dir')
+
+ def _path_at_level(self, level ):
+ # we do not support an absolute path of the gitconfig on windows ,
+ # use the global config instead
+ if sys.platform == "win32" and level == "system":
+ level = "global"
+ #END handle windows
+
+ if level == "system":
+ return "/etc/%s" % self.system_config_file_name
+ elif level == "global":
+ return normpath(expanduser("~/.%s" % self.system_config_file_name))
+ elif level == "repository":
+ return join(self.git_dir, self.repo_config_file_name)
+ #END handle level
+
+ raise ValueError("Invalid configuration level: %r" % level)
+
+ #{ Interface
+
+ def config_reader(self, config_level=None):
+ files = None
+ if config_level is None:
+ files = [ self._path_at_level(f) for f in self.config_level ]
+ else:
+ files = [ self._path_at_level(config_level) ]
+ #END handle level
+ return GitConfigParser(files, read_only=True)
+
+ def config_writer(self, config_level="repository"):
+ return GitConfigParser(self._path_at_level(config_level), read_only=False)
+
+
+ #} END interface
+
+
class PureIndexDB(IndexDB):
- #{ Configuration
- IndexCls = IndexFile
- #} END configuration
-
- @property
- def index(self):
- return self.IndexCls(self)
-
-
+ #{ Configuration
+ IndexCls = IndexFile
+ #} END configuration
+
+ @property
+ def index(self):
+ return self.IndexCls(self)
+
+
class PureAlternatesFileMixin(object):
- """Utility able to read and write an alternates file through the alternates property
- It needs to be part of a type with the git_dir or db_path property.
-
- The file by default is assumed to be located at the default location as imposed
- by the standard git repository layout"""
-
- #{ Configuration
- alternates_filepath = os.path.join('info', 'alternates') # relative path to alternates file
-
- #} END configuration
-
- def __init__(self, *args, **kwargs):
- try:
- super(PureAlternatesFileMixin, self).__init__(*args, **kwargs)
- except TypeError:
- pass
- #END handle py2.6 code breaking changes
- self._alternates_path() # throws on incompatible type
-
- #{ Interface
-
- def _alternates_path(self):
- if hasattr(self, 'git_dir'):
- return join(self.git_dir, 'objects', self.alternates_filepath)
- elif hasattr(self, 'db_path'):
- return self.db_path(self.alternates_filepath)
- else:
- raise AssertionError("This mixin requires a parent type with either the git_dir property or db_path method")
- #END handle path
-
- def _get_alternates(self):
- """The list of alternates for this repo from which objects can be retrieved
+ """Utility able to read and write an alternates file through the alternates property
+ It needs to be part of a type with the git_dir or db_path property.
+
+ The file by default is assumed to be located at the default location as imposed
+ by the standard git repository layout"""
+
+ #{ Configuration
+ alternates_filepath = os.path.join('info', 'alternates') # relative path to alternates file
+
+ #} END configuration
+
+ def __init__(self, *args, **kwargs):
+ try:
+ super(PureAlternatesFileMixin, self).__init__(*args, **kwargs)
+ except TypeError:
+ pass
+ #END handle py2.6 code breaking changes
+ self._alternates_path() # throws on incompatible type
+
+ #{ Interface
+
+ def _alternates_path(self):
+ if hasattr(self, 'git_dir'):
+ return join(self.git_dir, 'objects', self.alternates_filepath)
+ elif hasattr(self, 'db_path'):
+ return self.db_path(self.alternates_filepath)
+ else:
+ raise AssertionError("This mixin requires a parent type with either the git_dir property or db_path method")
+ #END handle path
+
+ def _get_alternates(self):
+ """The list of alternates for this repo from which objects can be retrieved
- :return: list of strings being pathnames of alternates"""
- alternates_path = self._alternates_path()
+ :return: list of strings being pathnames of alternates"""
+ alternates_path = self._alternates_path()
- if os.path.exists(alternates_path):
- try:
- f = open(alternates_path)
- alts = f.read()
- finally:
- f.close()
- return alts.strip().splitlines()
- else:
- return list()
- # END handle path exists
+ if os.path.exists(alternates_path):
+ try:
+ f = open(alternates_path)
+ alts = f.read()
+ finally:
+ f.close()
+ return alts.strip().splitlines()
+ else:
+ return list()
+ # END handle path exists
- def _set_alternates(self, alts):
- """Sets the alternates
+ def _set_alternates(self, alts):
+ """Sets the alternates
- :parm alts:
- is the array of string paths representing the alternates at which
- git should look for objects, i.e. /home/user/repo/.git/objects
+ :parm alts:
+ is the array of string paths representing the alternates at which
+ git should look for objects, i.e. /home/user/repo/.git/objects
- :raise NoSuchPathError:
- :note:
- The method does not check for the existance of the paths in alts
- as the caller is responsible."""
- alternates_path = self._alternates_path()
- if not alts:
- if isfile(alternates_path):
- os.remove(alternates_path)
- else:
- try:
- f = open(alternates_path, 'w')
- f.write("\n".join(alts))
- finally:
- f.close()
- # END file handling
- # END alts handling
+ :raise NoSuchPathError:
+ :note:
+ The method does not check for the existance of the paths in alts
+ as the caller is responsible."""
+ alternates_path = self._alternates_path()
+ if not alts:
+ if isfile(alternates_path):
+ os.remove(alternates_path)
+ else:
+ try:
+ f = open(alternates_path, 'w')
+ f.write("\n".join(alts))
+ finally:
+ f.close()
+ # END file handling
+ # END alts handling
- alternates = property(_get_alternates, _set_alternates, doc="Retrieve a list of alternates paths or set a list paths to be used as alternates")
-
- #} END interface
-
+ alternates = property(_get_alternates, _set_alternates, doc="Retrieve a list of alternates paths or set a list paths to be used as alternates")
+
+ #} END interface
+
diff --git a/git/db/py/complex.py b/git/db/py/complex.py
index 5f4e81e0..9d06f74a 100644
--- a/git/db/py/complex.py
+++ b/git/db/py/complex.py
@@ -4,14 +4,14 @@
# the New BSD License: http://www.opensource.org/licenses/bsd-license.php
from git.db.interface import HighLevelRepository
from base import (
- PureCompoundDB,
- PureObjectDBW,
- PureRootPathDB,
- PureRepositoryPathsMixin,
- PureConfigurationMixin,
- PureAlternatesFileMixin,
- PureIndexDB,
- )
+ PureCompoundDB,
+ PureObjectDBW,
+ PureRootPathDB,
+ PureRepositoryPathsMixin,
+ PureConfigurationMixin,
+ PureAlternatesFileMixin,
+ PureIndexDB,
+ )
from transport import PureTransportDB
from resolve import PureReferencesMixin
@@ -29,90 +29,90 @@ __all__ = ('PureGitODB', 'PurePartialGitDB', 'PureCompatibilityGitDB')
class PureGitODB(PureRootPathDB, PureObjectDBW, PureCompoundDB, PureAlternatesFileMixin):
- """A git-style object-only database, which contains all objects in the 'objects'
- subdirectory.
- :note: The type needs to be initialized on the ./objects directory to function,
- as it deals solely with object lookup. Use a PurePartialGitDB type if you need
- reference and push support."""
- # Configuration
- PackDBCls = PurePackedODB
- LooseDBCls = PureLooseObjectODB
- PureReferenceDBCls = PureReferenceDB
-
- # Directories
- packs_dir = 'pack'
- loose_dir = ''
-
-
- def __init__(self, root_path):
- """Initialize ourselves on a git ./objects directory"""
- super(PureGitODB, self).__init__(root_path)
-
- def _set_cache_(self, attr):
- if attr == '_dbs' or attr == '_loose_db':
- self._dbs = list()
- loose_db = None
- for subpath, dbcls in ((self.packs_dir, self.PackDBCls),
- (self.loose_dir, self.LooseDBCls),
- (self.alternates_filepath, self.PureReferenceDBCls)):
- path = self.db_path(subpath)
- if os.path.exists(path):
- self._dbs.append(dbcls(path))
- if dbcls is self.LooseDBCls:
- loose_db = self._dbs[-1]
- # END remember loose db
- # END check path exists
- # END for each db type
-
- # should have at least one subdb
- if not self._dbs:
- raise InvalidDBRoot(self.root_path())
- # END handle error
-
- # we the first one should have the store method
- assert loose_db is not None and hasattr(loose_db, 'store'), "One database needs store functionality"
-
- # finally set the value
- self._loose_db = loose_db
- else:
- super(PureGitODB, self)._set_cache_(attr)
- # END handle attrs
-
- #{ PureObjectDBW interface
-
- def store(self, istream):
- return self._loose_db.store(istream)
-
- def ostream(self):
- return self._loose_db.ostream()
-
- def set_ostream(self, ostream):
- return self._loose_db.set_ostream(ostream)
-
- #} END objectdbw interface
-
-
-
+ """A git-style object-only database, which contains all objects in the 'objects'
+ subdirectory.
+ :note: The type needs to be initialized on the ./objects directory to function,
+ as it deals solely with object lookup. Use a PurePartialGitDB type if you need
+ reference and push support."""
+ # Configuration
+ PackDBCls = PurePackedODB
+ LooseDBCls = PureLooseObjectODB
+ PureReferenceDBCls = PureReferenceDB
+
+ # Directories
+ packs_dir = 'pack'
+ loose_dir = ''
+
+
+ def __init__(self, root_path):
+ """Initialize ourselves on a git ./objects directory"""
+ super(PureGitODB, self).__init__(root_path)
+
+ def _set_cache_(self, attr):
+ if attr == '_dbs' or attr == '_loose_db':
+ self._dbs = list()
+ loose_db = None
+ for subpath, dbcls in ((self.packs_dir, self.PackDBCls),
+ (self.loose_dir, self.LooseDBCls),
+ (self.alternates_filepath, self.PureReferenceDBCls)):
+ path = self.db_path(subpath)
+ if os.path.exists(path):
+ self._dbs.append(dbcls(path))
+ if dbcls is self.LooseDBCls:
+ loose_db = self._dbs[-1]
+ # END remember loose db
+ # END check path exists
+ # END for each db type
+
+ # should have at least one subdb
+ if not self._dbs:
+ raise InvalidDBRoot(self.root_path())
+ # END handle error
+
+ # we the first one should have the store method
+ assert loose_db is not None and hasattr(loose_db, 'store'), "One database needs store functionality"
+
+ # finally set the value
+ self._loose_db = loose_db
+ else:
+ super(PureGitODB, self)._set_cache_(attr)
+ # END handle attrs
+
+ #{ PureObjectDBW interface
+
+ def store(self, istream):
+ return self._loose_db.store(istream)
+
+ def ostream(self):
+ return self._loose_db.ostream()
+
+ def set_ostream(self, ostream):
+ return self._loose_db.set_ostream(ostream)
+
+ #} END objectdbw interface
+
+
+
class PurePartialGitDB(PureGitODB,
- PureRepositoryPathsMixin, PureConfigurationMixin,
- PureReferencesMixin, PureSubmoduleDB,
- PureIndexDB,
- PureTransportDB # not fully implemented
- # HighLevelRepository Currently not implemented !
- ):
- """Git like database with support for object lookup as well as reference resolution.
- Our rootpath is set to the actual .git directory (bare on unbare).
-
- The root_path will be the git objects directory. Use git_path() to obtain the actual top-level
- git directory."""
- #directories
-
- def __init__(self, root_path):
- """Initialize ourselves on the .git directory, or the .git/objects directory."""
- PureRepositoryPathsMixin._initialize(self, root_path)
- super(PurePartialGitDB, self).__init__(self.objects_dir)
-
-
+ PureRepositoryPathsMixin, PureConfigurationMixin,
+ PureReferencesMixin, PureSubmoduleDB,
+ PureIndexDB,
+ PureTransportDB # not fully implemented
+ # HighLevelRepository Currently not implemented !
+ ):
+ """Git like database with support for object lookup as well as reference resolution.
+ Our rootpath is set to the actual .git directory (bare on unbare).
+
+ The root_path will be the git objects directory. Use git_path() to obtain the actual top-level
+ git directory."""
+ #directories
+
+ def __init__(self, root_path):
+ """Initialize ourselves on the .git directory, or the .git/objects directory."""
+ PureRepositoryPathsMixin._initialize(self, root_path)
+ super(PurePartialGitDB, self).__init__(self.objects_dir)
+
+
class PureCompatibilityGitDB(PurePartialGitDB, RepoCompatibilityInterface):
- """Pure git database with a compatability layer required by 0.3x code"""
-
+ """Pure git database with a compatability layer required by 0.3x code"""
+
diff --git a/git/db/py/loose.py b/git/db/py/loose.py
index 6e72aff0..8267be98 100644
--- a/git/db/py/loose.py
+++ b/git/db/py/loose.py
@@ -3,53 +3,53 @@
# This module is part of GitDB and is released under
# the New BSD License: http://www.opensource.org/licenses/bsd-license.php
from base import (
- PureRootPathDB,
- PureObjectDBR,
- PureObjectDBW
- )
+ PureRootPathDB,
+ PureObjectDBR,
+ PureObjectDBW
+ )
from git.exc import (
- InvalidDBRoot,
- BadObject,
- AmbiguousObjectName
- )
+ InvalidDBRoot,
+ BadObject,
+ AmbiguousObjectName
+ )
from git.stream import (
- DecompressMemMapReader,
- FDCompressedSha1Writer,
- FDStream,
- Sha1Writer
- )
+ DecompressMemMapReader,
+ FDCompressedSha1Writer,
+ FDStream,
+ Sha1Writer
+ )
from git.base import (
- OStream,
- OInfo
- )
+ OStream,
+ OInfo
+ )
from git.util import (
- file_contents_ro_filepath,
- ENOENT,
- hex_to_bin,
- bin_to_hex,
- exists,
- chmod,
- isdir,
- isfile,
- remove,
- mkdir,
- rename,
- dirname,
- basename,
- join
- )
+ file_contents_ro_filepath,
+ ENOENT,
+ hex_to_bin,
+ bin_to_hex,
+ exists,
+ chmod,
+ isdir,
+ isfile,
+ remove,
+ mkdir,
+ rename,
+ dirname,
+ basename,
+ join
+ )
from git.fun import (
- chunk_size,
- loose_object_header_info,
- write_object,
- stream_copy
- )
+ chunk_size,
+ loose_object_header_info,
+ write_object,
+ stream_copy
+ )
import tempfile
import mmap
@@ -61,203 +61,203 @@ __all__ = ( 'PureLooseObjectODB', )
class PureLooseObjectODB(PureRootPathDB, PureObjectDBR, PureObjectDBW):
- """A database which operates on loose object files"""
-
- # CONFIGURATION
- # chunks in which data will be copied between streams
- stream_chunk_size = chunk_size
-
- # On windows we need to keep it writable, otherwise it cannot be removed
- # either
- new_objects_mode = 0444
- if os.name == 'nt':
- new_objects_mode = 0644
-
-
- def __init__(self, root_path):
- super(PureLooseObjectODB, self).__init__(root_path)
- self._hexsha_to_file = dict()
- # Additional Flags - might be set to 0 after the first failure
- # Depending on the root, this might work for some mounts, for others not, which
- # is why it is per instance
- self._fd_open_flags = getattr(os, 'O_NOATIME', 0)
-
- #{ Interface
- def object_path(self, hexsha):
- """
- :return: path at which the object with the given hexsha would be stored,
- relative to the database root"""
- return join(hexsha[:2], hexsha[2:])
-
- def readable_db_object_path(self, hexsha):
- """
- :return: readable object path to the object identified by hexsha
- :raise BadObject: If the object file does not exist"""
- try:
- return self._hexsha_to_file[hexsha]
- except KeyError:
- pass
- # END ignore cache misses
-
- # try filesystem
- path = self.db_path(self.object_path(hexsha))
- if exists(path):
- self._hexsha_to_file[hexsha] = path
- return path
- # END handle cache
- raise BadObject(hexsha)
-
-
- #} END interface
-
- def _map_loose_object(self, sha):
- """
- :return: memory map of that file to allow random read access
- :raise BadObject: if object could not be located"""
- db_path = self.db_path(self.object_path(bin_to_hex(sha)))
- try:
- return file_contents_ro_filepath(db_path, flags=self._fd_open_flags)
- except OSError,e:
- if e.errno != ENOENT:
- # try again without noatime
- try:
- return file_contents_ro_filepath(db_path)
- except OSError:
- raise BadObject(sha)
- # didn't work because of our flag, don't try it again
- self._fd_open_flags = 0
- else:
- raise BadObject(sha)
- # END handle error
- # END exception handling
- try:
- return mmap.mmap(fd, 0, access=mmap.ACCESS_READ)
- finally:
- os.close(fd)
- # END assure file is closed
-
- def set_ostream(self, stream):
- """:raise TypeError: if the stream does not support the Sha1Writer interface"""
- if stream is not None and not isinstance(stream, Sha1Writer):
- raise TypeError("Output stream musst support the %s interface" % Sha1Writer.__name__)
- return super(PureLooseObjectODB, self).set_ostream(stream)
-
- def info(self, sha):
- m = self._map_loose_object(sha)
- try:
- type, size = loose_object_header_info(m)
- return OInfo(sha, type, size)
- finally:
- m.close()
- # END assure release of system resources
-
- def stream(self, sha):
- m = self._map_loose_object(sha)
- type, size, stream = DecompressMemMapReader.new(m, close_on_deletion = True)
- return OStream(sha, type, size, stream)
-
- def has_object(self, sha):
- try:
- self.readable_db_object_path(bin_to_hex(sha))
- return True
- except BadObject:
- return False
- # END check existance
-
- def partial_to_complete_sha_hex(self, partial_hexsha):
- """:return: 20 byte binary sha1 string which matches the given name uniquely
- :param name: hexadecimal partial name
- :raise AmbiguousObjectName:
- :raise BadObject: """
- candidate = None
- for binsha in self.sha_iter():
- if bin_to_hex(binsha).startswith(partial_hexsha):
- # it can't ever find the same object twice
- if candidate is not None:
- raise AmbiguousObjectName(partial_hexsha)
- candidate = binsha
- # END for each object
- if candidate is None:
- raise BadObject(partial_hexsha)
- return candidate
-
- def store(self, istream):
- """note: The sha we produce will be hex by nature"""
- tmp_path = None
- writer = self.ostream()
- if writer is None:
- # open a tmp file to write the data to
- fd, tmp_path = tempfile.mkstemp(prefix='obj', dir=self._root_path)
-
- if istream.binsha is None:
- writer = FDCompressedSha1Writer(fd)
- else:
- writer = FDStream(fd)
- # END handle direct stream copies
- # END handle custom writer
-
- try:
- try:
- if istream.binsha is not None:
- # copy as much as possible, the actual uncompressed item size might
- # be smaller than the compressed version
- stream_copy(istream.read, writer.write, sys.maxint, self.stream_chunk_size)
- else:
- # write object with header, we have to make a new one
- write_object(istream.type, istream.size, istream.read, writer.write,
- chunk_size=self.stream_chunk_size)
- # END handle direct stream copies
- finally:
- if tmp_path:
- writer.close()
- # END assure target stream is closed
- except:
- if tmp_path:
- os.remove(tmp_path)
- raise
- # END assure tmpfile removal on error
-
- hexsha = None
- if istream.binsha:
- hexsha = istream.hexsha
- else:
- hexsha = writer.sha(as_hex=True)
- # END handle sha
-
- if tmp_path:
- obj_path = self.db_path(self.object_path(hexsha))
- obj_dir = dirname(obj_path)
- if not isdir(obj_dir):
- mkdir(obj_dir)
- # END handle destination directory
- # rename onto existing doesn't work on windows
- if os.name == 'nt' and isfile(obj_path):
- remove(obj_path)
- # END handle win322
- rename(tmp_path, obj_path)
-
- # make sure its readable for all ! It started out as rw-- tmp file
- # but needs to be rwrr
- chmod(obj_path, self.new_objects_mode)
- # END handle dry_run
-
- istream.binsha = hex_to_bin(hexsha)
- return istream
-
- def sha_iter(self):
- # find all files which look like an object, extract sha from there
- for root, dirs, files in os.walk(self.root_path()):
- root_base = basename(root)
- if len(root_base) != 2:
- continue
-
- for f in files:
- if len(f) != 38:
- continue
- yield hex_to_bin(root_base + f)
- # END for each file
- # END for each walk iteration
-
- def size(self):
- return len(tuple(self.sha_iter()))
-
+ """A database which operates on loose object files"""
+
+ # CONFIGURATION
+ # chunks in which data will be copied between streams
+ stream_chunk_size = chunk_size
+
+ # On windows we need to keep it writable, otherwise it cannot be removed
+ # either
+ new_objects_mode = 0444
+ if os.name == 'nt':
+ new_objects_mode = 0644
+
+
+ def __init__(self, root_path):
+ super(PureLooseObjectODB, self).__init__(root_path)
+ self._hexsha_to_file = dict()
+ # Additional Flags - might be set to 0 after the first failure
+ # Depending on the root, this might work for some mounts, for others not, which
+ # is why it is per instance
+ self._fd_open_flags = getattr(os, 'O_NOATIME', 0)
+
+ #{ Interface
+ def object_path(self, hexsha):
+ """
+ :return: path at which the object with the given hexsha would be stored,
+ relative to the database root"""
+ return join(hexsha[:2], hexsha[2:])
+
+ def readable_db_object_path(self, hexsha):
+ """
+ :return: readable object path to the object identified by hexsha
+ :raise BadObject: If the object file does not exist"""
+ try:
+ return self._hexsha_to_file[hexsha]
+ except KeyError:
+ pass
+ # END ignore cache misses
+
+ # try filesystem
+ path = self.db_path(self.object_path(hexsha))
+ if exists(path):
+ self._hexsha_to_file[hexsha] = path
+ return path
+ # END handle cache
+ raise BadObject(hexsha)
+
+
+ #} END interface
+
+ def _map_loose_object(self, sha):
+ """
+ :return: memory map of that file to allow random read access
+ :raise BadObject: if object could not be located"""
+ db_path = self.db_path(self.object_path(bin_to_hex(sha)))
+ try:
+ return file_contents_ro_filepath(db_path, flags=self._fd_open_flags)
+ except OSError,e:
+ if e.errno != ENOENT:
+ # try again without noatime
+ try:
+ return file_contents_ro_filepath(db_path)
+ except OSError:
+ raise BadObject(sha)
+ # didn't work because of our flag, don't try it again
+ self._fd_open_flags = 0
+ else:
+ raise BadObject(sha)
+ # END handle error
+ # END exception handling
+ try:
+ return mmap.mmap(fd, 0, access=mmap.ACCESS_READ)
+ finally:
+ os.close(fd)
+ # END assure file is closed
+
+ def set_ostream(self, stream):
+ """:raise TypeError: if the stream does not support the Sha1Writer interface"""
+ if stream is not None and not isinstance(stream, Sha1Writer):
+ raise TypeError("Output stream musst support the %s interface" % Sha1Writer.__name__)
+ return super(PureLooseObjectODB, self).set_ostream(stream)
+
+ def info(self, sha):
+ m = self._map_loose_object(sha)
+ try:
+ type, size = loose_object_header_info(m)
+ return OInfo(sha, type, size)
+ finally:
+ m.close()
+ # END assure release of system resources
+
+ def stream(self, sha):
+ m = self._map_loose_object(sha)
+ type, size, stream = DecompressMemMapReader.new(m, close_on_deletion = True)
+ return OStream(sha, type, size, stream)
+
+ def has_object(self, sha):
+ try:
+ self.readable_db_object_path(bin_to_hex(sha))
+ return True
+ except BadObject:
+ return False
+ # END check existance
+
+ def partial_to_complete_sha_hex(self, partial_hexsha):
+ """:return: 20 byte binary sha1 string which matches the given name uniquely
+ :param name: hexadecimal partial name
+ :raise AmbiguousObjectName:
+ :raise BadObject: """
+ candidate = None
+ for binsha in self.sha_iter():
+ if bin_to_hex(binsha).startswith(partial_hexsha):
+ # it can't ever find the same object twice
+ if candidate is not None:
+ raise AmbiguousObjectName(partial_hexsha)
+ candidate = binsha
+ # END for each object
+ if candidate is None:
+ raise BadObject(partial_hexsha)
+ return candidate
+
+ def store(self, istream):
+ """note: The sha we produce will be hex by nature"""
+ tmp_path = None
+ writer = self.ostream()
+ if writer is None:
+ # open a tmp file to write the data to
+ fd, tmp_path = tempfile.mkstemp(prefix='obj', dir=self._root_path)
+
+ if istream.binsha is None:
+ writer = FDCompressedSha1Writer(fd)
+ else:
+ writer = FDStream(fd)
+ # END handle direct stream copies
+ # END handle custom writer
+
+ try:
+ try:
+ if istream.binsha is not None:
+ # copy as much as possible, the actual uncompressed item size might
+ # be smaller than the compressed version
+ stream_copy(istream.read, writer.write, sys.maxint, self.stream_chunk_size)
+ else:
+ # write object with header, we have to make a new one
+ write_object(istream.type, istream.size, istream.read, writer.write,
+ chunk_size=self.stream_chunk_size)
+ # END handle direct stream copies
+ finally:
+ if tmp_path:
+ writer.close()
+ # END assure target stream is closed
+ except:
+ if tmp_path:
+ os.remove(tmp_path)
+ raise
+ # END assure tmpfile removal on error
+
+ hexsha = None
+ if istream.binsha:
+ hexsha = istream.hexsha
+ else:
+ hexsha = writer.sha(as_hex=True)
+ # END handle sha
+
+ if tmp_path:
+ obj_path = self.db_path(self.object_path(hexsha))
+ obj_dir = dirname(obj_path)
+ if not isdir(obj_dir):
+ mkdir(obj_dir)
+ # END handle destination directory
+ # rename onto existing doesn't work on windows
+ if os.name == 'nt' and isfile(obj_path):
+ remove(obj_path)
+ # END handle win322
+ rename(tmp_path, obj_path)
+
+ # make sure its readable for all ! It started out as rw-- tmp file
+ # but needs to be rwrr
+ chmod(obj_path, self.new_objects_mode)
+ # END handle dry_run
+
+ istream.binsha = hex_to_bin(hexsha)
+ return istream
+
+ def sha_iter(self):
+ # find all files which look like an object, extract sha from there
+ for root, dirs, files in os.walk(self.root_path()):
+ root_base = basename(root)
+ if len(root_base) != 2:
+ continue
+
+ for f in files:
+ if len(f) != 38:
+ continue
+ yield hex_to_bin(root_base + f)
+ # END for each file
+ # END for each walk iteration
+
+ def size(self):
+ return len(tuple(self.sha_iter()))
+
diff --git a/git/db/py/mem.py b/git/db/py/mem.py
index da02dbdd..63ceb756 100644
--- a/git/db/py/mem.py
+++ b/git/db/py/mem.py
@@ -4,109 +4,109 @@
# the New BSD License: http://www.opensource.org/licenses/bsd-license.php
"""Contains the MemoryDatabase implementation"""
from base import (
- PureObjectDBR,
- PureObjectDBW
- )
+ PureObjectDBR,
+ PureObjectDBW
+ )
from loose import PureLooseObjectODB
from git.base import (
- OStream,
- IStream,
- )
+ OStream,
+ IStream,
+ )
from git.exc import (
- BadObject,
- UnsupportedOperation
- )
+ BadObject,
+ UnsupportedOperation
+ )
from git.stream import (
- ZippedStoreShaWriter,
- DecompressMemMapReader,
- )
+ ZippedStoreShaWriter,
+ DecompressMemMapReader,
+ )
from cStringIO import StringIO
__all__ = ("PureMemoryDB", )
class PureMemoryDB(PureObjectDBR, PureObjectDBW):
- """A memory database stores everything to memory, providing fast IO and object
- retrieval. It should be used to buffer results and obtain SHAs before writing
- it to the actual physical storage, as it allows to query whether object already
- exists in the target storage before introducing actual IO
-
- :note: memory is currently not threadsafe, hence the async methods cannot be used
- for storing"""
-
- def __init__(self):
- super(PureMemoryDB, self).__init__()
- self._db = PureLooseObjectODB("path/doesnt/matter")
-
- # maps 20 byte shas to their OStream objects
- self._cache = dict()
-
- def set_ostream(self, stream):
- raise UnsupportedOperation("PureMemoryDB's always stream into memory")
-
- def store(self, istream):
- zstream = ZippedStoreShaWriter()
- self._db.set_ostream(zstream)
-
- istream = self._db.store(istream)
- zstream.close() # close to flush
- zstream.seek(0)
-
- # don't provide a size, the stream is written in object format, hence the
- # header needs decompression
- decomp_stream = DecompressMemMapReader(zstream.getvalue(), close_on_deletion=False)
- self._cache[istream.binsha] = OStream(istream.binsha, istream.type, istream.size, decomp_stream)
-
- return istream
-
- def store_async(self, reader):
- raise UnsupportedOperation("PureMemoryDBs cannot currently be used for async write access")
-
- def has_object(self, sha):
- return sha in self._cache
+ """A memory database stores everything to memory, providing fast IO and object
+ retrieval. It should be used to buffer results and obtain SHAs before writing
+ it to the actual physical storage, as it allows to query whether object already
+ exists in the target storage before introducing actual IO
+
+ :note: memory is currently not threadsafe, hence the async methods cannot be used
+ for storing"""
+
+ def __init__(self):
+ super(PureMemoryDB, self).__init__()
+ self._db = PureLooseObjectODB("path/doesnt/matter")
+
+ # maps 20 byte shas to their OStream objects
+ self._cache = dict()
+
+ def set_ostream(self, stream):
+ raise UnsupportedOperation("PureMemoryDB's always stream into memory")
+
+ def store(self, istream):
+ zstream = ZippedStoreShaWriter()
+ self._db.set_ostream(zstream)
+
+ istream = self._db.store(istream)
+ zstream.close() # close to flush
+ zstream.seek(0)
+
+ # don't provide a size, the stream is written in object format, hence the
+ # header needs decompression
+ decomp_stream = DecompressMemMapReader(zstream.getvalue(), close_on_deletion=False)
+ self._cache[istream.binsha] = OStream(istream.binsha, istream.type, istream.size, decomp_stream)
+
+ return istream
+
+ def store_async(self, reader):
+ raise UnsupportedOperation("PureMemoryDBs cannot currently be used for async write access")
+
+ def has_object(self, sha):
+ return sha in self._cache
- def info(self, sha):
- # we always return streams, which are infos as well
- return self.stream(sha)
-
- def stream(self, sha):
- try:
- ostream = self._cache[sha]
- # rewind stream for the next one to read
- ostream.stream.seek(0)
- return ostream
- except KeyError:
- raise BadObject(sha)
- # END exception handling
-
- def size(self):
- return len(self._cache)
-
- def sha_iter(self):
- return self._cache.iterkeys()
-
-
- #{ Interface
- def stream_copy(self, sha_iter, odb):
- """Copy the streams as identified by sha's yielded by sha_iter into the given odb
- The streams will be copied directly
- :note: the object will only be written if it did not exist in the target db
- :return: amount of streams actually copied into odb. If smaller than the amount
- of input shas, one or more objects did already exist in odb"""
- count = 0
- for sha in sha_iter:
- if odb.has_object(sha):
- continue
- # END check object existance
-
- ostream = self.stream(sha)
- # compressed data including header
- sio = StringIO(ostream.stream.data())
- istream = IStream(ostream.type, ostream.size, sio, sha)
-
- odb.store(istream)
- count += 1
- # END for each sha
- return count
- #} END interface
+ def info(self, sha):
+ # we always return streams, which are infos as well
+ return self.stream(sha)
+
+ def stream(self, sha):
+ try:
+ ostream = self._cache[sha]
+ # rewind stream for the next one to read
+ ostream.stream.seek(0)
+ return ostream
+ except KeyError:
+ raise BadObject(sha)
+ # END exception handling
+
+ def size(self):
+ return len(self._cache)
+
+ def sha_iter(self):
+ return self._cache.iterkeys()
+
+
+ #{ Interface
+ def stream_copy(self, sha_iter, odb):
+ """Copy the streams as identified by sha's yielded by sha_iter into the given odb
+ The streams will be copied directly
+ :note: the object will only be written if it did not exist in the target db
+ :return: amount of streams actually copied into odb. If smaller than the amount
+ of input shas, one or more objects did already exist in odb"""
+ count = 0
+ for sha in sha_iter:
+ if odb.has_object(sha):
+ continue
+ # END check object existance
+
+ ostream = self.stream(sha)
+ # compressed data including header
+ sio = StringIO(ostream.stream.data())
+ istream = IStream(ostream.type, ostream.size, sio, sha)
+
+ odb.store(istream)
+ count += 1
+ # END for each sha
+ return count
+ #} END interface
diff --git a/git/db/py/pack.py b/git/db/py/pack.py
index 75b75468..0d4c533a 100644
--- a/git/db/py/pack.py
+++ b/git/db/py/pack.py
@@ -5,17 +5,17 @@
"""Module containing a database to deal with packs"""
from git.db import CachingDB
from base import (
- PureRootPathDB,
- PureObjectDBR
- )
+ PureRootPathDB,
+ PureObjectDBR
+ )
from git.util import LazyMixin
from git.exc import (
- BadObject,
- UnsupportedOperation,
- AmbiguousObjectName
- )
+ BadObject,
+ UnsupportedOperation,
+ AmbiguousObjectName
+ )
from git.pack import PackEntity
@@ -28,185 +28,185 @@ __all__ = ('PurePackedODB', )
class PurePackedODB(PureRootPathDB, PureObjectDBR, CachingDB, LazyMixin):
- """A database operating on a set of object packs"""
-
- # the type to use when instantiating a pack entity
- PackEntityCls = PackEntity
-
- # sort the priority list every N queries
- # Higher values are better, performance tests don't show this has
- # any effect, but it should have one
- _sort_interval = 500
-
- def __init__(self, root_path):
- super(PurePackedODB, self).__init__(root_path)
- # list of lists with three items:
- # * hits - number of times the pack was hit with a request
- # * entity - Pack entity instance
- # * sha_to_index - PackIndexFile.sha_to_index method for direct cache query
- # self._entities = list() # lazy loaded list
- self._hit_count = 0 # amount of hits
- self._st_mtime = 0 # last modification data of our root path
-
- def _set_cache_(self, attr):
- if attr == '_entities':
- self._entities = list()
- self.update_cache(force=True)
- # END handle entities initialization
-
- def _sort_entities(self):
- self._entities.sort(key=lambda l: l[0], reverse=True)
-
- def _pack_info(self, sha):
- """:return: tuple(entity, index) for an item at the given sha
- :param sha: 20 or 40 byte sha
- :raise BadObject:
- :note: This method is not thread-safe, but may be hit in multi-threaded
- operation. The worst thing that can happen though is a counter that
- was not incremented, or the list being in wrong order. So we safe
- the time for locking here, lets see how that goes"""
- # presort ?
- if self._hit_count % self._sort_interval == 0:
- self._sort_entities()
- # END update sorting
-
- for item in self._entities:
- index = item[2](sha)
- if index is not None:
- item[0] += 1 # one hit for you
- self._hit_count += 1 # general hit count
- return (item[1], index)
- # END index found in pack
- # END for each item
-
- # no hit, see whether we have to update packs
- # NOTE: considering packs don't change very often, we safe this call
- # and leave it to the super-caller to trigger that
- raise BadObject(sha)
-
- #{ Object DB Read
-
- def has_object(self, sha):
- try:
- self._pack_info(sha)
- return True
- except BadObject:
- return False
- # END exception handling
-
- def info(self, sha):
- entity, index = self._pack_info(sha)
- return entity.info_at_index(index)
-
- def stream(self, sha):
- entity, index = self._pack_info(sha)
- return entity.stream_at_index(index)
-
- def sha_iter(self):
- sha_list = list()
- for entity in self.entities():
- index = entity.index()
- sha_by_index = index.sha
- for index in xrange(index.size()):
- yield sha_by_index(index)
- # END for each index
- # END for each entity
-
- def size(self):
- sizes = [item[1].index().size() for item in self._entities]
- return reduce(lambda x,y: x+y, sizes, 0)
-
- #} END object db read
-
- #{ object db write
-
- def store(self, istream):
- """Storing individual objects is not feasible as a pack is designed to
- hold multiple objects. Writing or rewriting packs for single objects is
- inefficient"""
- raise UnsupportedOperation()
-
- def store_async(self, reader):
- # TODO: add PureObjectDBRW before implementing this
- raise NotImplementedError()
-
- #} END object db write
-
-
- #{ Interface
-
- def update_cache(self, force=False):
- """
- Update our cache with the acutally existing packs on disk. Add new ones,
- and remove deleted ones. We keep the unchanged ones
-
- :param force: If True, the cache will be updated even though the directory
- does not appear to have changed according to its modification timestamp.
- :return: True if the packs have been updated so there is new information,
- False if there was no change to the pack database"""
- stat = os.stat(self.root_path())
- if not force and stat.st_mtime <= self._st_mtime:
- return False
- # END abort early on no change
- self._st_mtime = stat.st_mtime
-
- # packs are supposed to be prefixed with pack- by git-convention
- # get all pack files, figure out what changed
- pack_files = set(glob.glob(os.path.join(self.root_path(), "pack-*.pack")))
- our_pack_files = set(item[1].pack().path() for item in self._entities)
-
- # new packs
- for pack_file in (pack_files - our_pack_files):
- # init the hit-counter/priority with the size, a good measure for hit-
- # probability. Its implemented so that only 12 bytes will be read
- entity = self.PackEntityCls(pack_file)
- self._entities.append([entity.pack().size(), entity, entity.index().sha_to_index])
- # END for each new packfile
-
- # removed packs
- for pack_file in (our_pack_files - pack_files):
- del_index = -1
- for i, item in enumerate(self._entities):
- if item[1].pack().path() == pack_file:
- del_index = i
- break
- # END found index
- # END for each entity
- assert del_index != -1
- del(self._entities[del_index])
- # END for each removed pack
-
- # reinitialize prioritiess
- self._sort_entities()
- return True
-
- def entities(self):
- """:return: list of pack entities operated upon by this database"""
- return [ item[1] for item in self._entities ]
-
- def partial_to_complete_sha(self, partial_binsha, canonical_length):
- """:return: 20 byte sha as inferred by the given partial binary sha
- :param partial_binsha: binary sha with less than 20 bytes
- :param canonical_length: length of the corresponding canonical representation.
- It is required as binary sha's cannot display whether the original hex sha
- had an odd or even number of characters
- :raise AmbiguousObjectName:
- :raise BadObject: """
- candidate = None
- for item in self._entities:
- item_index = item[1].index().partial_sha_to_index(partial_binsha, canonical_length)
- if item_index is not None:
- sha = item[1].index().sha(item_index)
- if candidate and candidate != sha:
- raise AmbiguousObjectName(partial_binsha)
- candidate = sha
- # END handle full sha could be found
- # END for each entity
-
- if candidate:
- return candidate
-
- # still not found ?
- raise BadObject(partial_binsha)
-
- #} END interface
+ """A database operating on a set of object packs"""
+
+ # the type to use when instantiating a pack entity
+ PackEntityCls = PackEntity
+
+ # sort the priority list every N queries
+ # Higher values are better, performance tests don't show this has
+ # any effect, but it should have one
+ _sort_interval = 500
+
+ def __init__(self, root_path):
+ super(PurePackedODB, self).__init__(root_path)
+ # list of lists with three items:
+ # * hits - number of times the pack was hit with a request
+ # * entity - Pack entity instance
+ # * sha_to_index - PackIndexFile.sha_to_index method for direct cache query
+ # self._entities = list() # lazy loaded list
+ self._hit_count = 0 # amount of hits
+ self._st_mtime = 0 # last modification data of our root path
+
+ def _set_cache_(self, attr):
+ if attr == '_entities':
+ self._entities = list()
+ self.update_cache(force=True)
+ # END handle entities initialization
+
+ def _sort_entities(self):
+ self._entities.sort(key=lambda l: l[0], reverse=True)
+
+ def _pack_info(self, sha):
+ """:return: tuple(entity, index) for an item at the given sha
+ :param sha: 20 or 40 byte sha
+ :raise BadObject:
+ :note: This method is not thread-safe, but may be hit in multi-threaded
+ operation. The worst thing that can happen though is a counter that
+ was not incremented, or the list being in wrong order. So we safe
+ the time for locking here, lets see how that goes"""
+ # presort ?
+ if self._hit_count % self._sort_interval == 0:
+ self._sort_entities()
+ # END update sorting
+
+ for item in self._entities:
+ index = item[2](sha)
+ if index is not None:
+ item[0] += 1 # one hit for you
+ self._hit_count += 1 # general hit count
+ return (item[1], index)
+ # END index found in pack
+ # END for each item
+
+ # no hit, see whether we have to update packs
+ # NOTE: considering packs don't change very often, we safe this call
+ # and leave it to the super-caller to trigger that
+ raise BadObject(sha)
+
+ #{ Object DB Read
+
+ def has_object(self, sha):
+ try:
+ self._pack_info(sha)
+ return True
+ except BadObject:
+ return False
+ # END exception handling
+
+ def info(self, sha):
+ entity, index = self._pack_info(sha)
+ return entity.info_at_index(index)
+
+ def stream(self, sha):
+ entity, index = self._pack_info(sha)
+ return entity.stream_at_index(index)
+
+ def sha_iter(self):
+ sha_list = list()
+ for entity in self.entities():
+ index = entity.index()
+ sha_by_index = index.sha
+ for index in xrange(index.size()):
+ yield sha_by_index(index)
+ # END for each index
+ # END for each entity
+
+ def size(self):
+ sizes = [item[1].index().size() for item in self._entities]
+ return reduce(lambda x,y: x+y, sizes, 0)
+
+ #} END object db read
+
+ #{ object db write
+
+ def store(self, istream):
+ """Storing individual objects is not feasible as a pack is designed to
+ hold multiple objects. Writing or rewriting packs for single objects is
+ inefficient"""
+ raise UnsupportedOperation()
+
+ def store_async(self, reader):
+ # TODO: add PureObjectDBRW before implementing this
+ raise NotImplementedError()
+
+ #} END object db write
+
+
+ #{ Interface
+
+ def update_cache(self, force=False):
+ """
+ Update our cache with the acutally existing packs on disk. Add new ones,
+ and remove deleted ones. We keep the unchanged ones
+
+ :param force: If True, the cache will be updated even though the directory
+ does not appear to have changed according to its modification timestamp.
+ :return: True if the packs have been updated so there is new information,
+ False if there was no change to the pack database"""
+ stat = os.stat(self.root_path())
+ if not force and stat.st_mtime <= self._st_mtime:
+ return False
+ # END abort early on no change
+ self._st_mtime = stat.st_mtime
+
+ # packs are supposed to be prefixed with pack- by git-convention
+ # get all pack files, figure out what changed
+ pack_files = set(glob.glob(os.path.join(self.root_path(), "pack-*.pack")))
+ our_pack_files = set(item[1].pack().path() for item in self._entities)
+
+ # new packs
+ for pack_file in (pack_files - our_pack_files):
+ # init the hit-counter/priority with the size, a good measure for hit-
+ # probability. Its implemented so that only 12 bytes will be read
+ entity = self.PackEntityCls(pack_file)
+ self._entities.append([entity.pack().size(), entity, entity.index().sha_to_index])
+ # END for each new packfile
+
+ # removed packs
+ for pack_file in (our_pack_files - pack_files):
+ del_index = -1
+ for i, item in enumerate(self._entities):
+ if item[1].pack().path() == pack_file:
+ del_index = i
+ break
+ # END found index
+ # END for each entity
+ assert del_index != -1
+ del(self._entities[del_index])
+ # END for each removed pack
+
+ # reinitialize prioritiess
+ self._sort_entities()
+ return True
+
+ def entities(self):
+ """:return: list of pack entities operated upon by this database"""
+ return [ item[1] for item in self._entities ]
+
+ def partial_to_complete_sha(self, partial_binsha, canonical_length):
+ """:return: 20 byte sha as inferred by the given partial binary sha
+ :param partial_binsha: binary sha with less than 20 bytes
+ :param canonical_length: length of the corresponding canonical representation.
+ It is required as binary sha's cannot display whether the original hex sha
+ had an odd or even number of characters
+ :raise AmbiguousObjectName:
+ :raise BadObject: """
+ candidate = None
+ for item in self._entities:
+ item_index = item[1].index().partial_sha_to_index(partial_binsha, canonical_length)
+ if item_index is not None:
+ sha = item[1].index().sha(item_index)
+ if candidate and candidate != sha:
+ raise AmbiguousObjectName(partial_binsha)
+ candidate = sha
+ # END handle full sha could be found
+ # END for each entity
+
+ if candidate:
+ return candidate
+
+ # still not found ?
+ raise BadObject(partial_binsha)
+
+ #} END interface
diff --git a/git/db/py/ref.py b/git/db/py/ref.py
index d2c77a3a..75bc4fd1 100644
--- a/git/db/py/ref.py
+++ b/git/db/py/ref.py
@@ -8,70 +8,70 @@ import os
__all__ = ('PureReferenceDB', )
class PureReferenceDB(PureCompoundDB):
- """A database consisting of database referred to in a file"""
-
- # Configuration
- # Specifies the object database to use for the paths found in the alternates
- # file. If None, it defaults to the PureGitODB
- ObjectDBCls = None
-
- def __init__(self, ref_file):
- super(PureReferenceDB, self).__init__()
- self._ref_file = ref_file
-
- def _set_cache_(self, attr):
- if attr == '_dbs':
- self._dbs = list()
- self._update_dbs_from_ref_file()
- else:
- super(PureReferenceDB, self)._set_cache_(attr)
- # END handle attrs
-
- def _update_dbs_from_ref_file(self):
- dbcls = self.ObjectDBCls
- if dbcls is None:
- # late import
- import complex
- dbcls = complex.PureGitODB
- # END get db type
-
- # try to get as many as possible, don't fail if some are unavailable
- ref_paths = list()
- try:
- ref_paths = [l.strip() for l in open(self._ref_file, 'r').readlines()]
- except (OSError, IOError):
- pass
- # END handle alternates
-
- ref_paths_set = set(ref_paths)
- cur_ref_paths_set = set(db.root_path() for db in self._dbs)
-
- # remove existing
- for path in (cur_ref_paths_set - ref_paths_set):
- for i, db in enumerate(self._dbs[:]):
- if db.root_path() == path:
- del(self._dbs[i])
- continue
- # END del matching db
- # END for each path to remove
-
- # add new
- # sort them to maintain order
- added_paths = sorted(ref_paths_set - cur_ref_paths_set, key=lambda p: ref_paths.index(p))
- for path in added_paths:
- try:
- db = dbcls(path)
- # force an update to verify path
- if isinstance(db, PureCompoundDB):
- db.databases()
- # END verification
- self._dbs.append(db)
- except Exception, e:
- # ignore invalid paths or issues
- pass
- # END for each path to add
-
- def update_cache(self, force=False):
- # re-read alternates and update databases
- self._update_dbs_from_ref_file()
- return super(PureReferenceDB, self).update_cache(force)
+ """A database consisting of database referred to in a file"""
+
+ # Configuration
+ # Specifies the object database to use for the paths found in the alternates
+ # file. If None, it defaults to the PureGitODB
+ ObjectDBCls = None
+
+ def __init__(self, ref_file):
+ super(PureReferenceDB, self).__init__()
+ self._ref_file = ref_file
+
+ def _set_cache_(self, attr):
+ if attr == '_dbs':
+ self._dbs = list()
+ self._update_dbs_from_ref_file()
+ else:
+ super(PureReferenceDB, self)._set_cache_(attr)
+ # END handle attrs
+
+ def _update_dbs_from_ref_file(self):
+ dbcls = self.ObjectDBCls
+ if dbcls is None:
+ # late import
+ import complex
+ dbcls = complex.PureGitODB
+ # END get db type
+
+ # try to get as many as possible, don't fail if some are unavailable
+ ref_paths = list()
+ try:
+ ref_paths = [l.strip() for l in open(self._ref_file, 'r').readlines()]
+ except (OSError, IOError):
+ pass
+ # END handle alternates
+
+ ref_paths_set = set(ref_paths)
+ cur_ref_paths_set = set(db.root_path() for db in self._dbs)
+
+ # remove existing
+ for path in (cur_ref_paths_set - ref_paths_set):
+ for i, db in enumerate(self._dbs[:]):
+ if db.root_path() == path:
+ del(self._dbs[i])
+ continue
+ # END del matching db
+ # END for each path to remove
+
+ # add new
+ # sort them to maintain order
+ added_paths = sorted(ref_paths_set - cur_ref_paths_set, key=lambda p: ref_paths.index(p))
+ for path in added_paths:
+ try:
+ db = dbcls(path)
+ # force an update to verify path
+ if isinstance(db, PureCompoundDB):
+ db.databases()
+ # END verification
+ self._dbs.append(db)
+ except Exception, e:
+ # ignore invalid paths or issues
+ pass
+ # END for each path to add
+
+ def update_cache(self, force=False):
+ # re-read alternates and update databases
+ self._update_dbs_from_ref_file()
+ return super(PureReferenceDB, self).update_cache(force)
diff --git a/git/db/py/resolve.py b/git/db/py/resolve.py
index 9a31fbd8..8a64d76b 100644
--- a/git/db/py/resolve.py
+++ b/git/db/py/resolve.py
@@ -4,12 +4,12 @@ version assuming compatible interface for reference and object types"""
from git.db.interface import ReferencesMixin
from git.exc import BadObject
from git.refs import (
- SymbolicReference,
- Reference,
- HEAD,
- Head,
- TagReference
- )
+ SymbolicReference,
+ Reference,
+ HEAD,
+ Head,
+ TagReference
+ )
from git.refs.head import HEAD
from git.refs.headref import Head
from git.refs.tag import TagReference
@@ -17,13 +17,13 @@ from git.refs.tag import TagReference
from git.objects.base import Object
from git.objects.commit import Commit
from git.util import (
- join,
- isdir,
- isfile,
- hex_to_bin,
- bin_to_hex,
- is_git_dir
- )
+ join,
+ isdir,
+ isfile,
+ hex_to_bin,
+ bin_to_hex,
+ is_git_dir
+ )
from string import digits
import os
import re
@@ -33,331 +33,331 @@ __all__ = ["PureReferencesMixin"]
#{ Utilities
def short_to_long(odb, hexsha):
- """:return: long hexadecimal sha1 from the given less-than-40 byte hexsha
- or None if no candidate could be found.
- :param hexsha: hexsha with less than 40 byte"""
- try:
- return bin_to_hex(odb.partial_to_complete_sha_hex(hexsha))
- except BadObject:
- return None
- # END exception handling
-
-
+ """:return: long hexadecimal sha1 from the given less-than-40 byte hexsha
+ or None if no candidate could be found.
+ :param hexsha: hexsha with less than 40 byte"""
+ try:
+ return bin_to_hex(odb.partial_to_complete_sha_hex(hexsha))
+ except BadObject:
+ return None
+ # END exception handling
+
+
def name_to_object(repo, name, return_ref=False):
- """
- :return: object specified by the given name, hexshas ( short and long )
- as well as references are supported
- :param return_ref: if name specifies a reference, we will return the reference
- instead of the object. Otherwise it will raise BadObject
- """
- 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
- hexsha = short_to_long(repo.odb, name)
- else:
- hexsha = name
- # END handle short shas
- #END find sha if it matches
-
- # if we couldn't find an object for what seemed to be a short hexsha
- # try to find it as reference anyway, it could be named 'aaa' for instance
- if hexsha is None:
- 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)
- if return_ref:
- return SymbolicReference(repo, base % name)
- #END handle symbolic ref
- break
- except ValueError:
- pass
- # END for each base
- # END handle hexsha
+ """
+ :return: object specified by the given name, hexshas ( short and long )
+ as well as references are supported
+ :param return_ref: if name specifies a reference, we will return the reference
+ instead of the object. Otherwise it will raise BadObject
+ """
+ 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
+ hexsha = short_to_long(repo.odb, name)
+ else:
+ hexsha = name
+ # END handle short shas
+ #END find sha if it matches
+
+ # if we couldn't find an object for what seemed to be a short hexsha
+ # try to find it as reference anyway, it could be named 'aaa' for instance
+ if hexsha is None:
+ 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)
+ if return_ref:
+ return SymbolicReference(repo, base % name)
+ #END handle symbolic ref
+ break
+ except ValueError:
+ pass
+ # END for each base
+ # END handle hexsha
- # didn't find any ref, this is an error
- if return_ref:
- raise BadObject("Couldn't find reference named %r" % name)
- #END handle return ref
+ # didn't find any ref, this is an error
+ if return_ref:
+ raise BadObject("Couldn't find reference named %r" % name)
+ #END handle return ref
- # tried everything ? fail
- if hexsha is None:
- raise BadObject(name)
- # END assert hexsha was found
-
- return Object.new_from_sha(repo, hex_to_bin(hexsha))
+ # tried everything ? fail
+ if hexsha is None:
+ raise BadObject(name)
+ # END assert hexsha was found
+
+ return Object.new_from_sha(repo, hex_to_bin(hexsha))
def deref_tag(tag):
- """Recursively dereference a tag and return the resulting object"""
- while True:
- try:
- tag = tag.object
- except AttributeError:
- break
- # END dereference tag
- return tag
+ """Recursively dereference 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
+ """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
- :raise ValueError: If rev couldn't be parsed
- :raise IndexError: If invalid reflog index is specified"""
-
- # colon search mode ?
- if rev.startswith(':/'):
- # colon search mode
- raise NotImplementedError("commit by message search ( regex )")
- # END handle search
-
- obj = None
- ref = 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
-
- token = rev[start]
-
- if obj is None:
- # token is a rev name
- if start == 0:
- ref = repo.head.ref
- else:
- if token == '@':
- ref = name_to_object(repo, rev[:start], return_ref=True)
- else:
- obj = name_to_object(repo, rev[:start])
- #END handle token
- #END handle refname
-
- if ref is not None:
- obj = ref.commit
- #END handle ref
- # END initialize obj on first token
-
-
- 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
- elif token == '@':
- # try single int
- assert ref is not None, "Require Reference to access reflog"
- revlog_index = None
- try:
- # transform reversed index into the format of our revlog
- revlog_index = -(int(output_type)+1)
- except ValueError:
- # TODO: Try to parse the other date options, using parse_date
- # maybe
- raise NotImplementedError("Support for additional @{...} modes not implemented")
- #END handle revlog index
-
- try:
- entry = ref.log_entry(revlog_index)
- except IndexError:
- raise IndexError("Invalid revlog index: %i" % revlog_index)
- #END handle index out of bound
-
- obj = Object.new_from_sha(repo, hex_to_bin(entry.newhexsha))
-
- # make it pass the following checks
- output_type = None
- 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)
+ """
+ :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
+ :raise ValueError: If rev couldn't be parsed
+ :raise IndexError: If invalid reflog index is specified"""
+
+ # colon search mode ?
+ if rev.startswith(':/'):
+ # colon search mode
+ raise NotImplementedError("commit by message search ( regex )")
+ # END handle search
+
+ obj = None
+ ref = 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
+
+ token = rev[start]
+
+ if obj is None:
+ # token is a rev name
+ if start == 0:
+ ref = repo.head.ref
+ else:
+ if token == '@':
+ ref = name_to_object(repo, rev[:start], return_ref=True)
+ else:
+ obj = name_to_object(repo, rev[:start])
+ #END handle token
+ #END handle refname
+
+ if ref is not None:
+ obj = ref.commit
+ #END handle ref
+ # END initialize obj on first token
+
+
+ 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
+ elif token == '@':
+ # try single int
+ assert ref is not None, "Require Reference to access reflog"
+ revlog_index = None
+ try:
+ # transform reversed index into the format of our revlog
+ revlog_index = -(int(output_type)+1)
+ except ValueError:
+ # TODO: Try to parse the other date options, using parse_date
+ # maybe
+ raise NotImplementedError("Support for additional @{...} modes not implemented")
+ #END handle revlog index
+
+ try:
+ entry = ref.log_entry(revlog_index)
+ except IndexError:
+ raise IndexError("Invalid revlog index: %i" % revlog_index)
+ #END handle index out of bound
+
+ obj = Object.new_from_sha(repo, hex_to_bin(entry.newhexsha))
+
+ # make it pass the following checks
+ output_type = None
+ 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
+ if parsed_to != lr:
+ raise ValueError("Didn't consume complete rev spec %s, consumed part: %s" % (rev, rev[:parsed_to]))
+
+ return obj
#} END utilities
class PureReferencesMixin(ReferencesMixin):
- """Pure-Python refparse implementation"""
-
- re_hexsha_only = re.compile('^[0-9A-Fa-f]{40}$')
- re_hexsha_shortened = re.compile('^[0-9A-Fa-f]{4,40}$')
-
- #{ Configuration
- # Types to use when instatiating references
- TagReferenceCls = TagReference
- HeadCls = Head
- ReferenceCls = Reference
- HEADCls = HEAD
- CommitCls = Commit
- #} END configuration
-
- def resolve(self, name):
- return self.resolve_object(name).binsha
-
- def resolve_object(self, name):
- return rev_parse(self, name)
-
- @property
- def references(self):
- return self.ReferenceCls.list_items(self)
-
- @property
- def heads(self):
- return self.HeadCls.list_items(self)
-
- @property
- def tags(self):
- return self.TagReferenceCls.list_items(self)
-
- def tag(self, name):
- return self.TagReferenceCls(self, self.TagReferenceCls.to_full_path(name))
-
- def commit(self, rev=None):
- if rev is None:
- return self.head.commit
- else:
- return self.resolve_object(str(rev)+"^0")
- #END handle revision
-
- def iter_trees(self, *args, **kwargs):
- return ( c.tree for c in self.iter_commits(*args, **kwargs) )
+ """Pure-Python refparse implementation"""
+
+ re_hexsha_only = re.compile('^[0-9A-Fa-f]{40}$')
+ re_hexsha_shortened = re.compile('^[0-9A-Fa-f]{4,40}$')
+
+ #{ Configuration
+ # Types to use when instatiating references
+ TagReferenceCls = TagReference
+ HeadCls = Head
+ ReferenceCls = Reference
+ HEADCls = HEAD
+ CommitCls = Commit
+ #} END configuration
+
+ def resolve(self, name):
+ return self.resolve_object(name).binsha
+
+ def resolve_object(self, name):
+ return rev_parse(self, name)
+
+ @property
+ def references(self):
+ return self.ReferenceCls.list_items(self)
+
+ @property
+ def heads(self):
+ return self.HeadCls.list_items(self)
+
+ @property
+ def tags(self):
+ return self.TagReferenceCls.list_items(self)
+
+ def tag(self, name):
+ return self.TagReferenceCls(self, self.TagReferenceCls.to_full_path(name))
+
+ def commit(self, rev=None):
+ if rev is None:
+ return self.head.commit
+ else:
+ return self.resolve_object(str(rev)+"^0")
+ #END handle revision
+
+ def iter_trees(self, *args, **kwargs):
+ return ( c.tree for c in self.iter_commits(*args, **kwargs) )
- def tree(self, rev=None):
- if rev is None:
- return self.head.commit.tree
- else:
- return self.resolve_object(str(rev)+"^{tree}")
+ def tree(self, rev=None):
+ if rev is None:
+ return self.head.commit.tree
+ else:
+ return self.resolve_object(str(rev)+"^{tree}")
- def iter_commits(self, rev=None, paths='', **kwargs):
- if rev is None:
- rev = self.head.commit
-
- return self.CommitCls.iter_items(self, rev, paths, **kwargs)
+ def iter_commits(self, rev=None, paths='', **kwargs):
+ if rev is None:
+ rev = self.head.commit
+
+ return self.CommitCls.iter_items(self, rev, paths, **kwargs)
-
- @property
- def head(self):
- return self.HEADCls(self,'HEAD')
-
- def create_head(self, path, commit='HEAD', force=False, logmsg=None ):
- return self.HeadCls.create(self, path, commit, force, logmsg)
-
- def delete_head(self, *heads, **kwargs):
- return self.HeadCls.delete(self, *heads, **kwargs)
-
- def create_tag(self, path, ref='HEAD', message=None, force=False, **kwargs):
- return self.TagReferenceCls.create(self, path, ref, message, force, **kwargs)
-
- def delete_tag(self, *tags):
- return self.TagReferenceCls.delete(self, *tags)
-
+
+ @property
+ def head(self):
+ return self.HEADCls(self,'HEAD')
+
+ def create_head(self, path, commit='HEAD', force=False, logmsg=None ):
+ return self.HeadCls.create(self, path, commit, force, logmsg)
+
+ def delete_head(self, *heads, **kwargs):
+ return self.HeadCls.delete(self, *heads, **kwargs)
+
+ def create_tag(self, path, ref='HEAD', message=None, force=False, **kwargs):
+ return self.TagReferenceCls.create(self, path, ref, message, force, **kwargs)
+
+ def delete_tag(self, *tags):
+ return self.TagReferenceCls.delete(self, *tags)
+
diff --git a/git/db/py/submodule.py b/git/db/py/submodule.py
index 735f90b1..0f2120c5 100644
--- a/git/db/py/submodule.py
+++ b/git/db/py/submodule.py
@@ -9,25 +9,25 @@ from git.db.interface import SubmoduleDB
__all__ = ["PureSubmoduleDB"]
class PureSubmoduleDB(SubmoduleDB):
- """Pure python implementation of submodule functionality"""
-
- @property
- def submodules(self):
- return Submodule.list_items(self)
-
- def submodule(self, name):
- try:
- return self.submodules[name]
- except IndexError:
- raise ValueError("Didn't find submodule named %r" % name)
- # END exception handling
-
- def create_submodule(self, *args, **kwargs):
- return Submodule.add(self, *args, **kwargs)
-
- def iter_submodules(self, *args, **kwargs):
- return RootModule(self).traverse(*args, **kwargs)
-
- def submodule_update(self, *args, **kwargs):
- return RootModule(self).update(*args, **kwargs)
-
+ """Pure python implementation of submodule functionality"""
+
+ @property
+ def submodules(self):
+ return Submodule.list_items(self)
+
+ def submodule(self, name):
+ try:
+ return self.submodules[name]
+ except IndexError:
+ raise ValueError("Didn't find submodule named %r" % name)
+ # END exception handling
+
+ def create_submodule(self, *args, **kwargs):
+ return Submodule.add(self, *args, **kwargs)
+
+ def iter_submodules(self, *args, **kwargs):
+ return RootModule(self).traverse(*args, **kwargs)
+
+ def submodule_update(self, *args, **kwargs):
+ return RootModule(self).update(*args, **kwargs)
+
diff --git a/git/db/py/transport.py b/git/db/py/transport.py
index 00d222b0..7bcaab95 100644
--- a/git/db/py/transport.py
+++ b/git/db/py/transport.py
@@ -5,9 +5,9 @@
"""Implement a transport compatible database which sends objects using the git protocol"""
from git.db.interface import ( TransportDB,
- PushInfo,
- FetchInfo,
- RefSpec )
+ PushInfo,
+ FetchInfo,
+ RefSpec )
from git.refs.remote import RemoteReference
from git.remote import Remote
@@ -16,43 +16,43 @@ from git.remote import Remote
__all__ = ["PureTransportDB"]
class PurePushInfo(PushInfo):
- """TODO: Implementation"""
- __slots__ = tuple()
-
-
-
+ """TODO: Implementation"""
+ __slots__ = tuple()
+
+
+
class PureFetchInfo(FetchInfo):
- """TODO"""
- __slots__ = tuple()
-
+ """TODO"""
+ __slots__ = tuple()
+
class PureTransportDB(TransportDB):
- # The following variables need to be set by the derived class
- #{Configuration
- protocol = None
- RemoteCls = Remote
- #}end configuraiton
-
- #{ Interface
-
- def fetch(self, url, refspecs, progress=None, **kwargs):
- raise NotImplementedError()
-
- def push(self, url, refspecs, progress=None, **kwargs):
- raise NotImplementedError()
-
- @property
- def remotes(self):
- return self.RemoteCls.list_items(self)
-
- def remote(self, name='origin'):
- return self.remotes[name]
-
- def create_remote(self, name, url, **kwargs):
- return self.RemoteCls.create(self, name, url, **kwargs)
-
- def delete_remote(self, remote):
- return self.RemoteCls.remove(self, remote)
-
- #}end interface
+ # The following variables need to be set by the derived class
+ #{Configuration
+ protocol = None
+ RemoteCls = Remote
+ #}end configuraiton
+
+ #{ Interface
+
+ def fetch(self, url, refspecs, progress=None, **kwargs):
+ raise NotImplementedError()
+
+ def push(self, url, refspecs, progress=None, **kwargs):
+ raise NotImplementedError()
+
+ @property
+ def remotes(self):
+ return self.RemoteCls.list_items(self)
+
+ def remote(self, name='origin'):
+ return self.remotes[name]
+
+ def create_remote(self, name, url, **kwargs):
+ return self.RemoteCls.create(self, name, url, **kwargs)
+
+ def delete_remote(self, remote):
+ return self.RemoteCls.remove(self, remote)
+
+ #}end interface