From 160081b9a7ca191afbec077c4bf970cfd9070d2c Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 29 Jun 2010 18:28:31 +0200 Subject: Updated and fixed sphinx API docs, which included one quick skim-through --- doc/reference.rst | 63 +++++++++++++++++++++++++++++++---------------- lib/git/cmd.py | 2 +- lib/git/index/base.py | 26 ++++++++++--------- lib/git/index/fun.py | 4 ++- lib/git/index/typ.py | 14 +++++------ lib/git/objects/blob.py | 3 ++- lib/git/objects/commit.py | 2 +- lib/git/objects/tree.py | 14 ++++++----- lib/git/objects/utils.py | 8 +++--- lib/git/refs.py | 25 +++++++++++++------ lib/git/remote.py | 27 ++++++++++++-------- lib/git/utils.py | 9 ++++--- 12 files changed, 124 insertions(+), 73 deletions(-) diff --git a/doc/reference.rst b/doc/reference.rst index 9cc32b71..90df10f8 100644 --- a/doc/reference.rst +++ b/doc/reference.rst @@ -3,13 +3,6 @@ API Reference ============= -Actor ------ - -.. automodule:: git.actor - :members: - :undoc-members: - Objects.Base ------------ @@ -45,12 +38,54 @@ Objects.Tree :members: :undoc-members: +Objects.Functions +----------------- + +.. automodule:: git.objects.fun + :members: + :undoc-members: + +Objects.Submodule +----------------- + +.. automodule:: git.objects.submodule + :members: + :undoc-members: + Objects.Utils ------------- .. automodule:: git.objects.utils :members: :undoc-members: + +Index.Base +------------- + +.. automodule:: git.index.base + :members: + :undoc-members: + +Index.Functions +--------------- + +.. automodule:: git.index.fun + :members: + :undoc-members: + +Index.Types +----------- + +.. automodule:: git.index.typ + :members: + :undoc-members: + +Index.Util +------------- + +.. automodule:: git.index.util + :members: + :undoc-members: GitCmd ------ @@ -81,13 +116,6 @@ Errors :members: :undoc-members: -Index ------- - -.. automodule:: git.index - :members: - :undoc-members: - Refs ---- @@ -110,13 +138,6 @@ Repo :members: :undoc-members: -Stats ------ - -.. automodule:: git.stats - :members: - :undoc-members: - Utils ----- diff --git a/lib/git/cmd.py b/lib/git/cmd.py index d0f2a19e..e9c1ce24 100644 --- a/lib/git/cmd.py +++ b/lib/git/cmd.py @@ -272,7 +272,7 @@ class Git(object): This merely is a workaround as data will be copied from the output pipe to the given output stream directly. - :param **subprocess_kwargs: + :param subprocess_kwargs: Keyword arguments to be passed to subprocess.Popen. Please note that some of the valid kwargs are already set by this method, the ones you specify may not be the same ones. diff --git a/lib/git/index/base.py b/lib/git/index/base.py index 03da52b7..1a8bee93 100644 --- a/lib/git/index/base.py +++ b/lib/git/index/base.py @@ -4,7 +4,7 @@ # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php """Module containing Index implementation, allowing to perform all kinds of index -manipulations such as querying and merging. """ +manipulations such as querying and merging.""" import tempfile import os import sys @@ -75,22 +75,26 @@ __all__ = ( 'IndexFile', 'CheckoutError' ) class IndexFile(LazyMixin, diff.Diffable, Serializable): - """Implements an Index that can be manipulated using a native implementation in + """ + Implements an Index that can be manipulated using a native implementation in order to save git command function calls wherever possible. - + It provides custom merging facilities allowing to merge without actually changing your index or your working tree. This way you can perform own test-merges based on the index only without having to deal with the working copy. This is useful in case of partial working trees. ``Entries`` + The index contains an entries dict whose keys are tuples of type IndexEntry to facilitate access. You may read the entries dict or manipulate it using IndexEntry instance, i.e.:: + index.entries[index.entry_key(index_entry_instance)] = index_entry_instance - Otherwise changes to it will be lost when changing the index using its methods. - """ + + Make sure you use index.write() once you are done manipulating the index directly + before operating on it using the git command""" __slots__ = ("repo", "version", "entries", "_extension_data", "_file_path") _VERSION = 2 # latest version we support S_IFGITLINK = 0160000 # a submodule @@ -250,7 +254,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable): :param repo: The repository treeish are located in. - :param *tree_sha: + :param tree_sha: 20 byte or 40 byte tree sha or tree objects :return: @@ -276,7 +280,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable): :param repo: The repository treeish are located in. - :param *treeish: + :param treeish: One, two or three Tree Objects, Commits or 40 byte hexshas. The result changes according to the amount of trees. If 1 Tree is given, it will just be read into a new index @@ -287,7 +291,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable): being the common ancestor of tree 2 and tree 3. Tree 2 is the 'current' tree, tree 3 is the 'other' one - :param **kwargs: + :param kwargs: Additional arguments passed to git-read-tree :return: @@ -790,7 +794,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable): removing the respective file. This may fail if there are uncommited changes in it. - :param **kwargs: + :param kwargs: Additional keyword arguments to be passed to git-rm, such as 'r' to allow recurive removal of @@ -828,7 +832,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable): :param skip_errors: If True, errors such as ones resulting from missing source files will be skpped. - :param **kwargs: + :param kwargs: Additional arguments you would like to pass to git-mv, such as dry_run or force. @@ -924,7 +928,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable): explicit paths are given. Otherwise progress information will be send prior and after a file has been checked out - :param **kwargs: + :param kwargs: Additional arguments to be pasesd to git-checkout-index :return: diff --git a/lib/git/index/fun.py b/lib/git/index/fun.py index ef950761..cc18c65c 100644 --- a/lib/git/index/fun.py +++ b/lib/git/index/fun.py @@ -51,8 +51,10 @@ def write_cache(entries, stream, extension_data=None, ShaStreamCls=IndexFileSHA1 :param entries: **sorted** list of entries :param stream: stream to wrap into the AdapterStreamCls - it is used for final output. + :param ShaStreamCls: Type to use when writing to the stream. It produces a sha while writing to it, before the data is passed on to the wrapped stream + :param extension_data: any kind of data to write as a trailer, it must begin a 4 byte identifier, followed by its size ( 4 bytes )""" # wrap the stream into a compatible writer @@ -103,7 +105,7 @@ def read_header(stream): def entry_key(*entry): """:return: Key suitable to be used for the index.entries dictionary - :param *entry: One instance of type BaseIndexEntry or the path and the stage""" + :param entry: One instance of type BaseIndexEntry or the path and the stage""" if len(entry) == 1: return (entry[0].path, entry[0].stage) else: diff --git a/lib/git/index/typ.py b/lib/git/index/typ.py index 7654b402..3a01cd65 100644 --- a/lib/git/index/typ.py +++ b/lib/git/index/typ.py @@ -78,10 +78,10 @@ class BaseIndexEntry(tuple): def stage(self): """Stage of the entry, either: - 0 = default stage - 1 = stage before a merge or common ancestor entry in case of a 3 way merge - 2 = stage of entries from the 'left' side of the merge - 3 = stage of entries from the right side of the merge + * 0 = default stage + * 1 = stage before a merge or common ancestor entry in case of a 3 way merge + * 2 = stage of entries from the 'left' side of the merge + * 3 = stage of entries from the right side of the merge :note: For more information, see http://www.kernel.org/pub/software/scm/git/docs/git-read-tree.html """ @@ -112,10 +112,10 @@ class IndexEntry(BaseIndexEntry): See the properties for a mapping between names and tuple indices. """ @property def ctime(self): - """:return: - Tuple(int_time_seconds_since_epoch, int_nano_seconds) of the - file's creation time """ + :return: + Tuple(int_time_seconds_since_epoch, int_nano_seconds) of the + file's creation time""" return unpack(">LL", self[4]) @property diff --git a/lib/git/objects/blob.py b/lib/git/objects/blob.py index 8263e9a2..d0ef54c7 100644 --- a/lib/git/objects/blob.py +++ b/lib/git/objects/blob.py @@ -28,7 +28,8 @@ class Blob(base.IndexObject): @property def mime_type(self): - """ :return:String describing the mime type of this file (based on the filename) + """ + :return: String describing the mime type of this file (based on the filename) :note: Defaults to 'text/plain' in case the actual file type is unknown. """ guesses = None if self.path: diff --git a/lib/git/objects/commit.py b/lib/git/objects/commit.py index f88bb0e8..11263e07 100644 --- a/lib/git/objects/commit.py +++ b/lib/git/objects/commit.py @@ -182,11 +182,11 @@ class Commit(base.Object, Iterable, Diffable, Traversable, Serializable): def iter_parents(self, paths='', **kwargs): """Iterate _all_ parents of this commit. + :param paths: Optional path or list of paths limiting the Commits to those that contain at least one of the paths :param kwargs: All arguments allowed by git-rev-list - :return: Iterator yielding Commit objects which are parents of self """ # skip ourselves skip = kwargs.get("skip", 1) diff --git a/lib/git/objects/tree.py b/lib/git/objects/tree.py index 056d3da9..64725128 100644 --- a/lib/git/objects/tree.py +++ b/lib/git/objects/tree.py @@ -23,8 +23,8 @@ from gitdb.util import ( __all__ = ("TreeModifier", "Tree") class TreeModifier(object): - """A utility class providing methods to alter the underlying cache in a list-like - fashion. + """A utility class providing methods to alter the underlying cache in a list-like fashion. + Once all adjustments are complete, the _cache, which really is a refernce to the cache of a tree, will be sorted. Assuring it will be in a serializable state""" __slots__ = '_cache' @@ -57,6 +57,7 @@ class TreeModifier(object): exists, nothing will be done, but a ValueError will be raised if the sha and mode of the existing item do not match the one you add, unless force is True + :param sha: The 20 or 40 byte sha of the item to add :param mode: int representing the stat compatible mode of the item :param force: If True, an item with your name and information will overwrite @@ -203,10 +204,11 @@ class Tree(IndexObject, diff.Diffable, utils.Traversable, utils.Serializable): @property def cache(self): - """:return: An object allowing to modify the internal cache. This can be used - to change the tree's contents. When done, make sure you call ``set_done`` - on the tree modifier, or serialization behaviour will be incorrect. - See the ``TreeModifier`` for more information on how to alter the cache""" + """ + :return: An object allowing to modify the internal cache. This can be used + to change the tree's contents. When done, make sure you call ``set_done`` + on the tree modifier, or serialization behaviour will be incorrect. + See the ``TreeModifier`` for more information on how to alter the cache""" return TreeModifier(self._cache) def traverse( self, predicate = lambda i,d: True, diff --git a/lib/git/objects/utils.py b/lib/git/objects/utils.py index c0ddd6e6..fd648f09 100644 --- a/lib/git/objects/utils.py +++ b/lib/git/objects/utils.py @@ -103,15 +103,15 @@ def verify_utctz(offset): def parse_date(string_date): """ Parse the given date as one of the following + * Git internal format: timestamp offset * RFC 2822: Thu, 07 Apr 2005 22:13:13 +0200. * ISO 8601 2005-04-07T22:13:13 - The T can be a space as well + The T can be a space as well - :return: Tuple(int(timestamp), int(offset), both in seconds since epoch + :return: Tuple(int(timestamp), int(offset)), both in seconds since epoch :raise ValueError: If the format could not be understood - :note: Date can also be YYYY.MM.DD, MM/DD/YYYY and DD.MM.YYYY - """ + :note: Date can also be YYYY.MM.DD, MM/DD/YYYY and DD.MM.YYYY""" # git time try: if string_date.count(' ') == 1 and string_date.rfind(':') == -1: diff --git a/lib/git/refs.py b/lib/git/refs.py index 8258ca8d..c8d67d3f 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -64,7 +64,8 @@ class SymbolicReference(object): @property def name(self): - """:return: + """ + :return: In case of symbolic references, the shortest assumable name is the path itself.""" return self.path @@ -244,7 +245,8 @@ class SymbolicReference(object): @property def is_detached(self): - """:return: + """ + :return: True if we are a detached reference, hence we point to a specific commit instead to another reference""" try: @@ -256,8 +258,9 @@ class SymbolicReference(object): @classmethod def to_full_path(cls, path): - """:return: string with a full path name which can be used to initialize - a Reference instance, for instance by using ``Reference.from_path``""" + """ + :return: string with a full path name which can be used to initialize + a Reference instance, for instance by using ``Reference.from_path``""" if isinstance(path, SymbolicReference): path = path.path full_ref_path = path @@ -369,6 +372,7 @@ class SymbolicReference(object): :raise OSError: If a (Symbolic)Reference with the same name but different contents already exists. + :note: This does not alter the current HEAD, index or Working Tree""" return cls._create(repo, path, False, reference, force) @@ -563,17 +567,21 @@ class Reference(SymbolicReference, LazyMixin, Iterable): @classmethod def create(cls, repo, path, commit='HEAD', force=False ): """Create a new reference. + :param repo: Repository to create the reference in :param path: The relative path of the reference, i.e. 'new_branch' or feature/feature1. The path prefix 'refs/' is implied if not given explicitly + :param commit: Commit to which the new reference should point, defaults to the current HEAD + :param force: if True, force creation even if a reference with that name already exists. Raise OSError otherwise + :return: Newly created Reference :note: This does not alter the current HEAD, index or Working Tree""" @@ -666,15 +674,18 @@ class Head(Reference): :param path: The name or path of the head, i.e. 'new_branch' or feature/feature1. The prefix refs/heads is implied. + :param commit: Commit to which the new head should point, defaults to the current HEAD + :param force: if True, force creation even if branch with that name already exists. - :param **kwargs: + :param kwargs: Additional keyword arguments to be passed to git-branch, i.e. track, no-track, l + :return: Newly created Head :note: This does not alter the current HEAD, index or Working Tree""" if cls is not Head: @@ -734,7 +745,7 @@ class Head(Reference): If True, changes to the index and the working tree will be discarded. If False, GitCommandError will be raised in that situation. - :param **kwargs: + :param kwargs: Additional keyword arguments to be passed to git checkout, i.e. b='new_branch' to create a new branch at the given spot. @@ -818,7 +829,7 @@ class TagReference(Reference): :param force: If True, to force creation of a tag even though that tag already exists. - :param **kwargs: + :param kwargs: Additional keyword arguments to be passed to git-tag :return: A new TagReference""" diff --git a/lib/git/remote.py b/lib/git/remote.py index 9c46a027..1558eeb3 100644 --- a/lib/git/remote.py +++ b/lib/git/remote.py @@ -52,8 +52,10 @@ class _SectionConstraint(object): class RemoteProgress(object): - """Handler providing an interface to parse progress information emitted by git-push - and git-fetch and to dispatch callbacks allowing subclasses to react to the progress.""" + """ + Handler providing an interface to parse progress information emitted by git-push + and git-fetch and to dispatch callbacks allowing subclasses to react to the progress. + """ BEGIN, END, COUNTING, COMPRESSING, WRITING = [ 1 << x for x in range(5) ] STAGE_MASK = BEGIN|END OP_MASK = COUNTING|COMPRESSING|WRITING @@ -168,7 +170,8 @@ class RemoteProgress(object): class PushInfo(object): - """Carries information about the result of a push operation of a single head:: + """ + Carries information about the result of a push operation of a single head:: info = remote.push()[0] info.flags # bitflags providing more information about the result @@ -179,7 +182,8 @@ class PushInfo(object): # the remote_ref_string. It can be a TagReference as well. info.old_commit # commit at which the remote_ref was standing before we pushed # it to local_ref.commit. Will be None if an error was indicated - info.summary # summary line providing human readable english text about the push""" + info.summary # summary line providing human readable english text about the push + """ __slots__ = ('local_ref', 'remote_ref_string', 'flags', 'old_commit', '_remote', 'summary') NEW_TAG, NEW_HEAD, NO_MATCH, REJECTED, REMOTE_REJECTED, REMOTE_FAILURE, DELETED, \ @@ -201,7 +205,8 @@ class PushInfo(object): @property def remote_ref(self): - """:return: + """ + :return: Remote Reference or TagReference in the local repository corresponding to the remote_ref_string kept in this instance.""" # translate heads to a local remote, tags stay as they are @@ -266,7 +271,8 @@ class PushInfo(object): class FetchInfo(object): - """Carries information about the results of a fetch operation of a single head:: + """ + Carries information about the results of a fetch operation of a single head:: info = remote.fetch()[0] info.ref # Symbolic Reference or RemoteReference to the changed @@ -276,7 +282,8 @@ class FetchInfo(object): # is 0 if ref is SymbolicReference info.note # additional notes given by git-fetch intended for the user info.old_commit # if info.flags & info.FORCED_UPDATE|info.FAST_FORWARD, - # field is set to the previous location of ref, otherwise None""" + # field is set to the previous location of ref, otherwise None + """ __slots__ = ('ref','old_commit', 'flags', 'note') NEW_TAG, NEW_HEAD, HEAD_UPTODATE, TAG_UPDATE, REJECTED, FORCED_UPDATE, \ @@ -501,7 +508,7 @@ class Remote(LazyMixin, Iterable): :param repo: Repository instance that is to receive the new remote :param name: Desired name of the remote :param url: URL which corresponds to the remote's name - :param **kwargs: + :param kwargs: Additional arguments to be passed to the git-remote add command :return: New Remote instance @@ -644,7 +651,7 @@ class Remote(LazyMixin, Iterable): Taken from the git manual :param progress: See 'push' method - :param **kwargs: Additional arguments to be passed to git-fetch + :param kwargs: Additional arguments to be passed to git-fetch :return: IterableList(FetchInfo, ...) list of FetchInfo instances providing detailed information about the fetch results @@ -675,7 +682,7 @@ class Remote(LazyMixin, Iterable): progress information until the method returns. If None, progress information will be discarded - :param **kwargs: Additional arguments to be passed to git-push + :param kwargs: Additional arguments to be passed to git-push :return: IterableList(PushInfo, ...) iterable list of PushInfo instances, each one informing about an individual head which had been updated on the remote diff --git a/lib/git/utils.py b/lib/git/utils.py index e49fcc2a..54c3414e 100644 --- a/lib/git/utils.py +++ b/lib/git/utils.py @@ -269,7 +269,8 @@ class BlockingLockFile(LockFile): class IterableList(list): - """List of iterable objects allowing to query an object by id or by named index:: + """ + List of iterable objects allowing to query an object by id or by named index:: heads = repo.heads heads.master @@ -317,11 +318,13 @@ class Iterable(object): @classmethod def list_items(cls, repo, *args, **kwargs): - """Find all items of this type - subclasses can specify args and kwargs differently. + """ + Find all items of this type - subclasses can specify args and kwargs differently. If no args are given, subclasses are obliged to return all items if no additional arguments arg given. - :note: Favor the iter_items method as it will + :note: Favor the iter_items method as it will + :return:list(Item,...) list of item instances""" out_list = IterableList( cls._id_attribute_ ) out_list.extend(cls.iter_items(repo, *args, **kwargs)) -- cgit v1.2.3 From 791765c0dc2d00a9ffa4bc857d09f615cfe3a759 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 29 Jun 2010 18:59:01 +0200 Subject: Removed repo tests which for some reason left the 'repos' directory around, replaced them by a real test which actually executes code, and puts everything into the tmp directory --- lib/git/repo.py | 7 ++++++- test/git/test_repo.py | 53 +++++++++++++-------------------------------------- 2 files changed, 19 insertions(+), 41 deletions(-) diff --git a/lib/git/repo.py b/lib/git/repo.py index 9b25653f..2df2cb6c 100644 --- a/lib/git/repo.py +++ b/lib/git/repo.py @@ -669,7 +669,12 @@ class Repo(object): path = prev_path # END reset previous working dir # END bad windows handling - return Repo(path, odbt = odbt) + + # 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) + return Repo(os.path.abspath(path), odbt = odbt) def archive(self, ostream, treeish=None, prefix=None, **kwargs): diff --git a/test/git/test_repo.py b/test/git/test_repo.py index a3ff564d..6554e737 100644 --- a/test/git/test_repo.py +++ b/test/git/test_repo.py @@ -131,7 +131,20 @@ class TestRepo(TestBase): assert os.path.isdir(r.git_dir) self._test_empty_repo(r) + + # test clone + clone_path = path + "_clone" + rc = r.clone(clone_path) + self._test_empty_repo(rc) + shutil.rmtree(git_dir_abs) + try: + shutil.rmtree(clone_path) + except OSError: + # when relative paths are used, the clone may actually be inside + # of the parent directory + pass + # END exception handling # END for each path os.makedirs(git_dir_rela) @@ -151,46 +164,6 @@ class TestRepo(TestBase): def test_bare_property(self): self.rorepo.bare - @patch_object(Repo, '__init__') - @patch_object(Git, '_call_process') - def test_init_with_options(self, git, repo): - git.return_value = True - repo.return_value = None - - r = Repo.init("repos/foo/bar.git", **{'bare' : True,'template': "/baz/sweet"}) - assert isinstance(r, Repo) - - assert_true(git.called) - assert_true(repo.called) - - @patch_object(Repo, '__init__') - @patch_object(Git, '_call_process') - def test_clone(self, git, repo): - git.return_value = None - repo.return_value = None - - self.rorepo.clone("repos/foo/bar.git") - - assert_true(git.called) - path = os.path.join(absolute_project_path(), '.git') - assert_equal(git.call_args, (('clone', path, 'repos/foo/bar.git'), {})) - assert_true(repo.called) - - @patch_object(Repo, '__init__') - @patch_object(Git, '_call_process') - def test_clone_with_options(self, git, repo): - git.return_value = None - repo.return_value = None - - self.rorepo.clone("repos/foo/bar.git", **{'template': '/awesome'}) - - assert_true(git.called) - path = os.path.join(absolute_project_path(), '.git') - assert_equal(git.call_args, (('clone', path, 'repos/foo/bar.git'), - { 'template': '/awesome'})) - assert_true(repo.called) - - def test_daemon_export(self): orig_val = self.rorepo.daemon_export self.rorepo.daemon_export = not orig_val -- cgit v1.2.3 From 77cd6659b64cb1950a82e6a3cccdda94f15ae739 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 29 Jun 2010 20:00:46 +0200 Subject: Renamed modules utils to util, and errors to exc to be more conforming to the submodules's naming conventions --- CHANGES | 4 + lib/git/__init__.py | 4 +- lib/git/cmd.py | 4 +- lib/git/config.py | 2 +- lib/git/diff.py | 4 +- lib/git/errors.py | 56 ------- lib/git/exc.py | 56 +++++++ lib/git/index/base.py | 6 +- lib/git/index/fun.py | 4 +- lib/git/objects/__init__.py | 2 +- lib/git/objects/base.py | 4 +- lib/git/objects/commit.py | 4 +- lib/git/objects/tag.py | 2 +- lib/git/objects/tree.py | 6 +- lib/git/objects/util.py | 365 ++++++++++++++++++++++++++++++++++++++++++++ lib/git/objects/utils.py | 365 -------------------------------------------- lib/git/refs.py | 4 +- lib/git/remote.py | 4 +- lib/git/repo.py | 2 +- lib/git/util.py | 340 +++++++++++++++++++++++++++++++++++++++++ lib/git/utils.py | 340 ----------------------------------------- test/git/test_base.py | 2 +- test/git/test_remote.py | 2 +- test/git/test_repo.py | 2 +- test/git/test_util.py | 105 +++++++++++++ test/git/test_utils.py | 105 ------------- 26 files changed, 899 insertions(+), 895 deletions(-) delete mode 100644 lib/git/errors.py create mode 100644 lib/git/exc.py create mode 100644 lib/git/objects/util.py delete mode 100644 lib/git/objects/utils.py create mode 100644 lib/git/util.py delete mode 100644 lib/git/utils.py create mode 100644 test/git/test_util.py delete mode 100644 test/git/test_utils.py diff --git a/CHANGES b/CHANGES index 19c99f08..66e0a42d 100644 --- a/CHANGES +++ b/CHANGES @@ -17,6 +17,10 @@ CHANGES They may only be initialized using their binary shas, reference names or revision specs are not allowed anymore. * The .data attribute was removed from the Object type, it is only available on the Blob type. + * For consistency with naming conventions used in sub-modules like gitdb, the following modules have been renamed + * git.utils -> git.util + * git.errors -> git.exc + * git.objects.utils -> git.objects.util 0.2 Beta 2 diff --git a/lib/git/__init__.py b/lib/git/__init__.py index 7860a3c1..5ebeaa7d 100644 --- a/lib/git/__init__.py +++ b/lib/git/__init__.py @@ -28,12 +28,12 @@ from git.config import GitConfigParser from git.objects import * from git.refs import * from git.diff import * -from git.errors import * +from git.exc import * from git.cmd import Git from git.repo import Repo from git.remote import * from git.index import * -from git.utils import ( +from git.util import ( LockFile, BlockingLockFile, Stats diff --git a/lib/git/cmd.py b/lib/git/cmd.py index e9c1ce24..cd848e05 100644 --- a/lib/git/cmd.py +++ b/lib/git/cmd.py @@ -5,8 +5,8 @@ # the BSD License: http://www.opensource.org/licenses/bsd-license.php import os, sys -from utils import * -from errors import GitCommandError +from util import * +from exc import GitCommandError from subprocess import ( call, diff --git a/lib/git/config.py b/lib/git/config.py index e4af57f0..92d64aea 100644 --- a/lib/git/config.py +++ b/lib/git/config.py @@ -13,7 +13,7 @@ import inspect import cStringIO from git.odict import OrderedDict -from git.utils import LockFile +from git.util import LockFile __all__ = ('GitConfigParser', ) diff --git a/lib/git/diff.py b/lib/git/diff.py index 79a0c3c1..788bf238 100644 --- a/lib/git/diff.py +++ b/lib/git/diff.py @@ -6,8 +6,8 @@ import re from objects.blob import Blob -from objects.utils import mode_str_to_int -from errors import GitCommandError +from objects.util import mode_str_to_int +from exc import GitCommandError from gitdb.util import hex_to_bin diff --git a/lib/git/errors.py b/lib/git/errors.py deleted file mode 100644 index 93919d5e..00000000 --- a/lib/git/errors.py +++ /dev/null @@ -1,56 +0,0 @@ -# errors.py -# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors -# -# This module is part of GitPython and is released under -# the BSD License: http://www.opensource.org/licenses/bsd-license.php -""" Module containing all exceptions thrown througout the git package, """ - -class InvalidGitRepositoryError(Exception): - """ Thrown if the given repository appears to have an invalid format. """ - - -class NoSuchPathError(OSError): - """ Thrown if a path could not be access by the system. """ - - -class GitCommandError(Exception): - """ Thrown if execution of the git command fails with non-zero status code. """ - def __init__(self, command, status, stderr=None): - self.stderr = stderr - self.status = status - self.command = command - - def __str__(self): - return ("'%s' returned exit status %i: %s" % - (' '.join(str(i) for i in self.command), self.status, self.stderr)) - - -class CheckoutError( Exception ): - """Thrown if a file could not be checked out from the index as it contained - changes. - - The .failed_files attribute contains a list of relative paths that failed - to be checked out as they contained changes that did not exist in the index. - - The .failed_reasons attribute contains a string informing about the actual - cause of the issue. - - The .valid_files attribute contains a list of relative paths to files that - were checked out successfully and hence match the version stored in the - index""" - def __init__(self, message, failed_files, valid_files, failed_reasons): - Exception.__init__(self, message) - self.failed_files = failed_files - self.failed_reasons = failed_reasons - self.valid_files = valid_files - - def __str__(self): - return Exception.__str__(self) + ":%s" % self.failed_files - - -class CacheError(Exception): - """Base for all errors related to the git index, which is called cache internally""" - -class UnmergedEntriesError(CacheError): - """Thrown if an operation cannot proceed as there are still unmerged - entries in the cache""" diff --git a/lib/git/exc.py b/lib/git/exc.py new file mode 100644 index 00000000..93919d5e --- /dev/null +++ b/lib/git/exc.py @@ -0,0 +1,56 @@ +# errors.py +# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors +# +# This module is part of GitPython and is released under +# the BSD License: http://www.opensource.org/licenses/bsd-license.php +""" Module containing all exceptions thrown througout the git package, """ + +class InvalidGitRepositoryError(Exception): + """ Thrown if the given repository appears to have an invalid format. """ + + +class NoSuchPathError(OSError): + """ Thrown if a path could not be access by the system. """ + + +class GitCommandError(Exception): + """ Thrown if execution of the git command fails with non-zero status code. """ + def __init__(self, command, status, stderr=None): + self.stderr = stderr + self.status = status + self.command = command + + def __str__(self): + return ("'%s' returned exit status %i: %s" % + (' '.join(str(i) for i in self.command), self.status, self.stderr)) + + +class CheckoutError( Exception ): + """Thrown if a file could not be checked out from the index as it contained + changes. + + The .failed_files attribute contains a list of relative paths that failed + to be checked out as they contained changes that did not exist in the index. + + The .failed_reasons attribute contains a string informing about the actual + cause of the issue. + + The .valid_files attribute contains a list of relative paths to files that + were checked out successfully and hence match the version stored in the + index""" + def __init__(self, message, failed_files, valid_files, failed_reasons): + Exception.__init__(self, message) + self.failed_files = failed_files + self.failed_reasons = failed_reasons + self.valid_files = valid_files + + def __str__(self): + return Exception.__str__(self) + ":%s" % self.failed_files + + +class CacheError(Exception): + """Base for all errors related to the git index, which is called cache internally""" + +class UnmergedEntriesError(CacheError): + """Thrown if an operation cannot proceed as there are still unmerged + entries in the cache""" diff --git a/lib/git/index/base.py b/lib/git/index/base.py index 1a8bee93..4b3197a2 100644 --- a/lib/git/index/base.py +++ b/lib/git/index/base.py @@ -36,7 +36,7 @@ from util import ( import git.objects import git.diff as diff -from git.errors import ( +from git.exc import ( GitCommandError, CheckoutError ) @@ -48,9 +48,9 @@ from git.objects import ( Commit, ) -from git.objects.utils import Serializable +from git.objects.util import Serializable -from git.utils import ( +from git.util import ( IndexFileSHA1Writer, LazyMixin, LockedFD, diff --git a/lib/git/index/fun.py b/lib/git/index/fun.py index cc18c65c..fac559c5 100644 --- a/lib/git/index/fun.py +++ b/lib/git/index/fun.py @@ -5,8 +5,8 @@ more versatile from stat import S_IFDIR from cStringIO import StringIO -from git.utils import IndexFileSHA1Writer -from git.errors import UnmergedEntriesError +from git.util import IndexFileSHA1Writer +from git.exc import UnmergedEntriesError from git.objects.fun import ( tree_to_stream, traverse_tree_recursive, diff --git a/lib/git/objects/__init__.py b/lib/git/objects/__init__.py index 209d8b51..85c7e38c 100644 --- a/lib/git/objects/__init__.py +++ b/lib/git/objects/__init__.py @@ -8,7 +8,7 @@ from blob import * from tree import * from commit import * from submodule import * -from utils import Actor +from util import Actor __all__ = [ name for name, obj in locals().items() if not (name.startswith('_') or inspect.ismodule(obj)) ] \ No newline at end of file diff --git a/lib/git/objects/base.py b/lib/git/objects/base.py index 97b7898c..d4a46788 100644 --- a/lib/git/objects/base.py +++ b/lib/git/objects/base.py @@ -3,8 +3,8 @@ # # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php -from git.utils import LazyMixin, join_path_native, stream_copy -from utils import get_object_type_by_name +from git.util import LazyMixin, join_path_native, stream_copy +from util import get_object_type_by_name from gitdb.util import ( hex_to_bin, bin_to_hex, diff --git a/lib/git/objects/commit.py b/lib/git/objects/commit.py index 11263e07..132d794b 100644 --- a/lib/git/objects/commit.py +++ b/lib/git/objects/commit.py @@ -4,7 +4,7 @@ # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php -from git.utils import ( +from git.util import ( Iterable, Stats, ) @@ -17,7 +17,7 @@ import base from gitdb.util import ( hex_to_bin ) -from utils import ( +from util import ( Traversable, Serializable, get_user_id, diff --git a/lib/git/objects/tag.py b/lib/git/objects/tag.py index 702eae35..ea480fc2 100644 --- a/lib/git/objects/tag.py +++ b/lib/git/objects/tag.py @@ -6,7 +6,7 @@ """ Module containing all object based types. """ import base from gitdb.util import hex_to_bin -from utils import ( +from util import ( get_object_type_by_name, parse_actor_and_date ) diff --git a/lib/git/objects/tree.py b/lib/git/objects/tree.py index 64725128..d6e16a31 100644 --- a/lib/git/objects/tree.py +++ b/lib/git/objects/tree.py @@ -3,7 +3,7 @@ # # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php -import utils +import util from base import IndexObject from blob import Blob @@ -101,7 +101,7 @@ class TreeModifier(object): #} END mutators -class Tree(IndexObject, diff.Diffable, utils.Traversable, utils.Serializable): +class Tree(IndexObject, diff.Diffable, util.Traversable, util.Serializable): """Tree objects represent an ordered list of Blobs and other Trees. ``Tree as a list``:: @@ -214,7 +214,7 @@ class Tree(IndexObject, diff.Diffable, utils.Traversable, utils.Serializable): def traverse( self, predicate = lambda i,d: True, prune = lambda i,d: False, depth = -1, branch_first=True, visit_once = False, ignore_self=1 ): - """For documentation, see utils.Traversable.traverse + """For documentation, see util.Traversable.traverse Trees are set to visit_once = False to gain more performance in the traversal""" return super(Tree, self).traverse(predicate, prune, depth, branch_first, visit_once, ignore_self) diff --git a/lib/git/objects/util.py b/lib/git/objects/util.py new file mode 100644 index 00000000..fd648f09 --- /dev/null +++ b/lib/git/objects/util.py @@ -0,0 +1,365 @@ +# util.py +# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors +# +# This module is part of GitPython and is released under +# the BSD License: http://www.opensource.org/licenses/bsd-license.php +"""Module for general utility functions""" +import re +from collections import deque as Deque +import platform + +from string import digits +import time +import os + +__all__ = ('get_object_type_by_name', 'get_user_id', 'parse_date', 'parse_actor_and_date', + 'ProcessStreamAdapter', 'Traversable', 'altz_to_utctz_str', 'utctz_to_altz', + 'verify_utctz') + +#{ Functions + +def mode_str_to_int(modestr): + """ + :param modestr: string like 755 or 644 or 100644 - only the last 6 chars will be used + :return: + String identifying a mode compatible to the mode methods ids of the + stat module regarding the rwx permissions for user, group and other, + special flags and file system flags, i.e. whether it is a symlink + for example.""" + mode = 0 + for iteration, char in enumerate(reversed(modestr[-6:])): + mode += int(char) << iteration*3 + # END for each char + return mode + +def get_object_type_by_name(object_type_name): + """ + :return: type suitable to handle the given object type name. + Use the type to create new instances. + + :param object_type_name: Member of TYPES + + :raise ValueError: In case object_type_name is unknown""" + if object_type_name == "commit": + import commit + return commit.Commit + elif object_type_name == "tag": + import tag + return tag.TagObject + elif object_type_name == "blob": + import blob + return blob.Blob + elif object_type_name == "tree": + import tree + return tree.Tree + else: + raise ValueError("Cannot handle unknown object type: %s" % object_type_name) + + +def get_user_id(): + """:return: string identifying the currently active system user as name@node + :note: user can be set with the 'USER' environment variable, usually set on windows""" + ukn = 'UNKNOWN' + username = os.environ.get('USER', ukn) + if username == ukn and hasattr(os, 'getlogin'): + username = os.getlogin() + # END get username from login + return "%s@%s" % (username, platform.node()) + + +def utctz_to_altz(utctz): + """we convert utctz to the timezone in seconds, it is the format time.altzone + returns. Git stores it as UTC timezon which has the opposite sign as well, + which explains the -1 * ( that was made explicit here ) + :param utctz: git utc timezone string, i.e. +0200""" + return -1 * int(float(utctz)/100*3600) + +def altz_to_utctz_str(altz): + """As above, but inverses the operation, returning a string that can be used + in commit objects""" + utci = -1 * int((altz / 3600)*100) + utcs = str(abs(utci)) + utcs = "0"*(4-len(utcs)) + utcs + prefix = (utci < 0 and '-') or '+' + return prefix + utcs + + +def verify_utctz(offset): + """:raise ValueError: if offset is incorrect + :return: offset""" + fmt_exc = ValueError("Invalid timezone offset format: %s" % offset) + if len(offset) != 5: + raise fmt_exc + if offset[0] not in "+-": + raise fmt_exc + if offset[1] not in digits or \ + offset[2] not in digits or \ + offset[3] not in digits or \ + offset[4] not in digits: + raise fmt_exc + # END for each char + return offset + +def parse_date(string_date): + """ + Parse the given date as one of the following + + * Git internal format: timestamp offset + * RFC 2822: Thu, 07 Apr 2005 22:13:13 +0200. + * ISO 8601 2005-04-07T22:13:13 + The T can be a space as well + + :return: Tuple(int(timestamp), int(offset)), both in seconds since epoch + :raise ValueError: If the format could not be understood + :note: Date can also be YYYY.MM.DD, MM/DD/YYYY and DD.MM.YYYY""" + # git time + try: + if string_date.count(' ') == 1 and string_date.rfind(':') == -1: + timestamp, offset = string_date.split() + timestamp = int(timestamp) + return timestamp, utctz_to_altz(verify_utctz(offset)) + else: + offset = "+0000" # local time by default + if string_date[-5] in '-+': + offset = verify_utctz(string_date[-5:]) + string_date = string_date[:-6] # skip space as well + # END split timezone info + + # now figure out the date and time portion - split time + date_formats = list() + splitter = -1 + if ',' in string_date: + date_formats.append("%a, %d %b %Y") + splitter = string_date.rfind(' ') + else: + # iso plus additional + date_formats.append("%Y-%m-%d") + date_formats.append("%Y.%m.%d") + date_formats.append("%m/%d/%Y") + date_formats.append("%d.%m.%Y") + + splitter = string_date.rfind('T') + if splitter == -1: + splitter = string_date.rfind(' ') + # END handle 'T' and ' ' + # END handle rfc or iso + + assert splitter > -1 + + # split date and time + time_part = string_date[splitter+1:] # skip space + date_part = string_date[:splitter] + + # parse time + tstruct = time.strptime(time_part, "%H:%M:%S") + + for fmt in date_formats: + try: + dtstruct = time.strptime(date_part, fmt) + fstruct = time.struct_time((dtstruct.tm_year, dtstruct.tm_mon, dtstruct.tm_mday, + tstruct.tm_hour, tstruct.tm_min, tstruct.tm_sec, + dtstruct.tm_wday, dtstruct.tm_yday, tstruct.tm_isdst)) + return int(time.mktime(fstruct)), utctz_to_altz(offset) + except ValueError: + continue + # END exception handling + # END for each fmt + + # still here ? fail + raise ValueError("no format matched") + # END handle format + except Exception: + raise ValueError("Unsupported date format: %s" % string_date) + # END handle exceptions + + +# precompiled regex +_re_actor_epoch = re.compile(r'^.+? (.*) (\d+) ([+-]\d+).*$') + +def parse_actor_and_date(line): + """Parse out the actor (author or committer) info from a line like:: + + author Tom Preston-Werner 1191999972 -0700 + + :return: [Actor, int_seconds_since_epoch, int_timezone_offset]""" + m = _re_actor_epoch.search(line) + actor, epoch, offset = m.groups() + return (Actor._from_string(actor), int(epoch), utctz_to_altz(offset)) + + +#} END functions + + +#{ Classes + +class Actor(object): + """Actors hold information about a person acting on the repository. They + can be committers and authors or anything with a name and an email as + mentioned in the git log entries.""" + # precompiled regex + name_only_regex = re.compile( r'<(.+)>' ) + name_email_regex = re.compile( r'(.*) <(.+?)>' ) + + def __init__(self, name, email): + self.name = name + self.email = email + + def __eq__(self, other): + return self.name == other.name and self.email == other.email + + def __ne__(self, other): + return not (self == other) + + def __hash__(self): + return hash((self.name, self.email)) + + def __str__(self): + return self.name + + def __repr__(self): + return '">' % (self.name, self.email) + + @classmethod + def _from_string(cls, string): + """Create an Actor from a string. + :param string: is the string, which is expected to be in regular git format + + John Doe + + :return: Actor """ + m = cls.name_email_regex.search(string) + if m: + name, email = m.groups() + return Actor(name, email) + else: + m = cls.name_only_regex.search(string) + if m: + return Actor(m.group(1), None) + else: + # assume best and use the whole string as name + return Actor(string, None) + # END special case name + # END handle name/email matching + + +class ProcessStreamAdapter(object): + """Class wireing all calls to the contained Process instance. + + Use this type to hide the underlying process to provide access only to a specified + stream. The process is usually wrapped into an AutoInterrupt class to kill + it if the instance goes out of scope.""" + __slots__ = ("_proc", "_stream") + def __init__(self, process, stream_name): + self._proc = process + self._stream = getattr(process, stream_name) + + def __getattr__(self, attr): + return getattr(self._stream, attr) + + +class Traversable(object): + """Simple interface to perforam depth-first or breadth-first traversals + into one direction. + Subclasses only need to implement one function. + Instances of the Subclass must be hashable""" + __slots__ = tuple() + + @classmethod + def _get_intermediate_items(cls, item): + """ + Returns: + List of items connected to the given item. + Must be implemented in subclass + """ + raise NotImplementedError("To be implemented in subclass") + + + def traverse( self, predicate = lambda i,d: True, + prune = lambda i,d: False, depth = -1, branch_first=True, + visit_once = True, ignore_self=1, as_edge = False ): + """:return: iterator yieling of items found when traversing self + + :param predicate: f(i,d) returns False if item i at depth d should not be included in the result + + :param prune: + f(i,d) return True if the search should stop at item i at depth d. + Item i will not be returned. + + :param depth: + define at which level the iteration should not go deeper + if -1, there is no limit + if 0, you would effectively only get self, the root of the iteration + i.e. if 1, you would only get the first level of predessessors/successors + + :param branch_first: + if True, items will be returned branch first, otherwise depth first + + :param visit_once: + if True, items will only be returned once, although they might be encountered + several times. Loops are prevented that way. + + :param ignore_self: + if True, self will be ignored and automatically pruned from + the result. Otherwise it will be the first item to be returned. + If as_edge is True, the source of the first edge is None + + :param as_edge: + if True, return a pair of items, first being the source, second the + destinatination, i.e. tuple(src, dest) with the edge spanning from + source to destination""" + visited = set() + stack = Deque() + stack.append( ( 0 ,self, None ) ) # self is always depth level 0 + + def addToStack( stack, item, branch_first, depth ): + lst = self._get_intermediate_items( item ) + if not lst: + return + if branch_first: + stack.extendleft( ( depth , i, item ) for i in lst ) + else: + reviter = ( ( depth , lst[i], item ) for i in range( len( lst )-1,-1,-1) ) + stack.extend( reviter ) + # END addToStack local method + + while stack: + d, item, src = stack.pop() # depth of item, item, item_source + + if visit_once and item in visited: + continue + + if visit_once: + visited.add(item) + + rval = ( as_edge and (src, item) ) or item + if prune( rval, d ): + continue + + skipStartItem = ignore_self and ( item == self ) + if not skipStartItem and predicate( rval, d ): + yield rval + + # only continue to next level if this is appropriate ! + nd = d + 1 + if depth > -1 and nd > depth: + continue + + addToStack( stack, item, branch_first, nd ) + # END for each item on work stack + + +class Serializable(object): + """Defines methods to serialize and deserialize objects from and into a data stream""" + + def _serialize(self, stream): + """Serialize the data of this object into the given data stream + :note: a serialized object would ``_deserialize`` into the same objet + :param stream: a file-like object + :return: self""" + raise NotImplementedError("To be implemented in subclass") + + def _deserialize(self, stream): + """Deserialize all information regarding this object from the stream + :param stream: a file-like object + :return: self""" + raise NotImplementedError("To be implemented in subclass") diff --git a/lib/git/objects/utils.py b/lib/git/objects/utils.py deleted file mode 100644 index fd648f09..00000000 --- a/lib/git/objects/utils.py +++ /dev/null @@ -1,365 +0,0 @@ -# util.py -# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors -# -# This module is part of GitPython and is released under -# the BSD License: http://www.opensource.org/licenses/bsd-license.php -"""Module for general utility functions""" -import re -from collections import deque as Deque -import platform - -from string import digits -import time -import os - -__all__ = ('get_object_type_by_name', 'get_user_id', 'parse_date', 'parse_actor_and_date', - 'ProcessStreamAdapter', 'Traversable', 'altz_to_utctz_str', 'utctz_to_altz', - 'verify_utctz') - -#{ Functions - -def mode_str_to_int(modestr): - """ - :param modestr: string like 755 or 644 or 100644 - only the last 6 chars will be used - :return: - String identifying a mode compatible to the mode methods ids of the - stat module regarding the rwx permissions for user, group and other, - special flags and file system flags, i.e. whether it is a symlink - for example.""" - mode = 0 - for iteration, char in enumerate(reversed(modestr[-6:])): - mode += int(char) << iteration*3 - # END for each char - return mode - -def get_object_type_by_name(object_type_name): - """ - :return: type suitable to handle the given object type name. - Use the type to create new instances. - - :param object_type_name: Member of TYPES - - :raise ValueError: In case object_type_name is unknown""" - if object_type_name == "commit": - import commit - return commit.Commit - elif object_type_name == "tag": - import tag - return tag.TagObject - elif object_type_name == "blob": - import blob - return blob.Blob - elif object_type_name == "tree": - import tree - return tree.Tree - else: - raise ValueError("Cannot handle unknown object type: %s" % object_type_name) - - -def get_user_id(): - """:return: string identifying the currently active system user as name@node - :note: user can be set with the 'USER' environment variable, usually set on windows""" - ukn = 'UNKNOWN' - username = os.environ.get('USER', ukn) - if username == ukn and hasattr(os, 'getlogin'): - username = os.getlogin() - # END get username from login - return "%s@%s" % (username, platform.node()) - - -def utctz_to_altz(utctz): - """we convert utctz to the timezone in seconds, it is the format time.altzone - returns. Git stores it as UTC timezon which has the opposite sign as well, - which explains the -1 * ( that was made explicit here ) - :param utctz: git utc timezone string, i.e. +0200""" - return -1 * int(float(utctz)/100*3600) - -def altz_to_utctz_str(altz): - """As above, but inverses the operation, returning a string that can be used - in commit objects""" - utci = -1 * int((altz / 3600)*100) - utcs = str(abs(utci)) - utcs = "0"*(4-len(utcs)) + utcs - prefix = (utci < 0 and '-') or '+' - return prefix + utcs - - -def verify_utctz(offset): - """:raise ValueError: if offset is incorrect - :return: offset""" - fmt_exc = ValueError("Invalid timezone offset format: %s" % offset) - if len(offset) != 5: - raise fmt_exc - if offset[0] not in "+-": - raise fmt_exc - if offset[1] not in digits or \ - offset[2] not in digits or \ - offset[3] not in digits or \ - offset[4] not in digits: - raise fmt_exc - # END for each char - return offset - -def parse_date(string_date): - """ - Parse the given date as one of the following - - * Git internal format: timestamp offset - * RFC 2822: Thu, 07 Apr 2005 22:13:13 +0200. - * ISO 8601 2005-04-07T22:13:13 - The T can be a space as well - - :return: Tuple(int(timestamp), int(offset)), both in seconds since epoch - :raise ValueError: If the format could not be understood - :note: Date can also be YYYY.MM.DD, MM/DD/YYYY and DD.MM.YYYY""" - # git time - try: - if string_date.count(' ') == 1 and string_date.rfind(':') == -1: - timestamp, offset = string_date.split() - timestamp = int(timestamp) - return timestamp, utctz_to_altz(verify_utctz(offset)) - else: - offset = "+0000" # local time by default - if string_date[-5] in '-+': - offset = verify_utctz(string_date[-5:]) - string_date = string_date[:-6] # skip space as well - # END split timezone info - - # now figure out the date and time portion - split time - date_formats = list() - splitter = -1 - if ',' in string_date: - date_formats.append("%a, %d %b %Y") - splitter = string_date.rfind(' ') - else: - # iso plus additional - date_formats.append("%Y-%m-%d") - date_formats.append("%Y.%m.%d") - date_formats.append("%m/%d/%Y") - date_formats.append("%d.%m.%Y") - - splitter = string_date.rfind('T') - if splitter == -1: - splitter = string_date.rfind(' ') - # END handle 'T' and ' ' - # END handle rfc or iso - - assert splitter > -1 - - # split date and time - time_part = string_date[splitter+1:] # skip space - date_part = string_date[:splitter] - - # parse time - tstruct = time.strptime(time_part, "%H:%M:%S") - - for fmt in date_formats: - try: - dtstruct = time.strptime(date_part, fmt) - fstruct = time.struct_time((dtstruct.tm_year, dtstruct.tm_mon, dtstruct.tm_mday, - tstruct.tm_hour, tstruct.tm_min, tstruct.tm_sec, - dtstruct.tm_wday, dtstruct.tm_yday, tstruct.tm_isdst)) - return int(time.mktime(fstruct)), utctz_to_altz(offset) - except ValueError: - continue - # END exception handling - # END for each fmt - - # still here ? fail - raise ValueError("no format matched") - # END handle format - except Exception: - raise ValueError("Unsupported date format: %s" % string_date) - # END handle exceptions - - -# precompiled regex -_re_actor_epoch = re.compile(r'^.+? (.*) (\d+) ([+-]\d+).*$') - -def parse_actor_and_date(line): - """Parse out the actor (author or committer) info from a line like:: - - author Tom Preston-Werner 1191999972 -0700 - - :return: [Actor, int_seconds_since_epoch, int_timezone_offset]""" - m = _re_actor_epoch.search(line) - actor, epoch, offset = m.groups() - return (Actor._from_string(actor), int(epoch), utctz_to_altz(offset)) - - -#} END functions - - -#{ Classes - -class Actor(object): - """Actors hold information about a person acting on the repository. They - can be committers and authors or anything with a name and an email as - mentioned in the git log entries.""" - # precompiled regex - name_only_regex = re.compile( r'<(.+)>' ) - name_email_regex = re.compile( r'(.*) <(.+?)>' ) - - def __init__(self, name, email): - self.name = name - self.email = email - - def __eq__(self, other): - return self.name == other.name and self.email == other.email - - def __ne__(self, other): - return not (self == other) - - def __hash__(self): - return hash((self.name, self.email)) - - def __str__(self): - return self.name - - def __repr__(self): - return '">' % (self.name, self.email) - - @classmethod - def _from_string(cls, string): - """Create an Actor from a string. - :param string: is the string, which is expected to be in regular git format - - John Doe - - :return: Actor """ - m = cls.name_email_regex.search(string) - if m: - name, email = m.groups() - return Actor(name, email) - else: - m = cls.name_only_regex.search(string) - if m: - return Actor(m.group(1), None) - else: - # assume best and use the whole string as name - return Actor(string, None) - # END special case name - # END handle name/email matching - - -class ProcessStreamAdapter(object): - """Class wireing all calls to the contained Process instance. - - Use this type to hide the underlying process to provide access only to a specified - stream. The process is usually wrapped into an AutoInterrupt class to kill - it if the instance goes out of scope.""" - __slots__ = ("_proc", "_stream") - def __init__(self, process, stream_name): - self._proc = process - self._stream = getattr(process, stream_name) - - def __getattr__(self, attr): - return getattr(self._stream, attr) - - -class Traversable(object): - """Simple interface to perforam depth-first or breadth-first traversals - into one direction. - Subclasses only need to implement one function. - Instances of the Subclass must be hashable""" - __slots__ = tuple() - - @classmethod - def _get_intermediate_items(cls, item): - """ - Returns: - List of items connected to the given item. - Must be implemented in subclass - """ - raise NotImplementedError("To be implemented in subclass") - - - def traverse( self, predicate = lambda i,d: True, - prune = lambda i,d: False, depth = -1, branch_first=True, - visit_once = True, ignore_self=1, as_edge = False ): - """:return: iterator yieling of items found when traversing self - - :param predicate: f(i,d) returns False if item i at depth d should not be included in the result - - :param prune: - f(i,d) return True if the search should stop at item i at depth d. - Item i will not be returned. - - :param depth: - define at which level the iteration should not go deeper - if -1, there is no limit - if 0, you would effectively only get self, the root of the iteration - i.e. if 1, you would only get the first level of predessessors/successors - - :param branch_first: - if True, items will be returned branch first, otherwise depth first - - :param visit_once: - if True, items will only be returned once, although they might be encountered - several times. Loops are prevented that way. - - :param ignore_self: - if True, self will be ignored and automatically pruned from - the result. Otherwise it will be the first item to be returned. - If as_edge is True, the source of the first edge is None - - :param as_edge: - if True, return a pair of items, first being the source, second the - destinatination, i.e. tuple(src, dest) with the edge spanning from - source to destination""" - visited = set() - stack = Deque() - stack.append( ( 0 ,self, None ) ) # self is always depth level 0 - - def addToStack( stack, item, branch_first, depth ): - lst = self._get_intermediate_items( item ) - if not lst: - return - if branch_first: - stack.extendleft( ( depth , i, item ) for i in lst ) - else: - reviter = ( ( depth , lst[i], item ) for i in range( len( lst )-1,-1,-1) ) - stack.extend( reviter ) - # END addToStack local method - - while stack: - d, item, src = stack.pop() # depth of item, item, item_source - - if visit_once and item in visited: - continue - - if visit_once: - visited.add(item) - - rval = ( as_edge and (src, item) ) or item - if prune( rval, d ): - continue - - skipStartItem = ignore_self and ( item == self ) - if not skipStartItem and predicate( rval, d ): - yield rval - - # only continue to next level if this is appropriate ! - nd = d + 1 - if depth > -1 and nd > depth: - continue - - addToStack( stack, item, branch_first, nd ) - # END for each item on work stack - - -class Serializable(object): - """Defines methods to serialize and deserialize objects from and into a data stream""" - - def _serialize(self, stream): - """Serialize the data of this object into the given data stream - :note: a serialized object would ``_deserialize`` into the same objet - :param stream: a file-like object - :return: self""" - raise NotImplementedError("To be implemented in subclass") - - def _deserialize(self, stream): - """Deserialize all information regarding this object from the stream - :param stream: a file-like object - :return: self""" - raise NotImplementedError("To be implemented in subclass") diff --git a/lib/git/refs.py b/lib/git/refs.py index c8d67d3f..343a0afb 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -10,8 +10,8 @@ from objects import ( Object, Commit ) -from objects.utils import get_object_type_by_name -from utils import ( +from objects.util import get_object_type_by_name +from util import ( LazyMixin, Iterable, join_path, diff --git a/lib/git/remote.py b/lib/git/remote.py index 1558eeb3..1598e55a 100644 --- a/lib/git/remote.py +++ b/lib/git/remote.py @@ -5,10 +5,10 @@ # the BSD License: http://www.opensource.org/licenses/bsd-license.php """Module implementing a remote object allowing easy access to git remotes""" -from errors import GitCommandError +from exc import GitCommandError from objects import Commit -from git.utils import ( +from git.util import ( LazyMixin, Iterable, IterableList diff --git a/lib/git/repo.py b/lib/git/repo.py index 2df2cb6c..d9b943cd 100644 --- a/lib/git/repo.py +++ b/lib/git/repo.py @@ -4,7 +4,7 @@ # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php -from errors import InvalidGitRepositoryError, NoSuchPathError +from exc import InvalidGitRepositoryError, NoSuchPathError from cmd import Git from objects import Actor from refs import * diff --git a/lib/git/util.py b/lib/git/util.py new file mode 100644 index 00000000..54c3414e --- /dev/null +++ b/lib/git/util.py @@ -0,0 +1,340 @@ +# utils.py +# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors +# +# This module is part of GitPython and is released under +# the BSD License: http://www.opensource.org/licenses/bsd-license.php + +import os +import sys +import time +import tempfile + +from gitdb.util import ( + make_sha, + LockedFD, + file_contents_ro, + LazyMixin, + to_hex_sha, + to_bin_sha + ) + +__all__ = ( "stream_copy", "join_path", "to_native_path_windows", "to_native_path_linux", + "join_path_native", "Stats", "IndexFileSHA1Writer", "Iterable", "IterableList", + "BlockingLockFile", "LockFile" ) + +def stream_copy(source, destination, chunk_size=512*1024): + """Copy all data from the source stream into the destination stream in chunks + of size chunk_size + + :return: amount of bytes written""" + br = 0 + while True: + chunk = source.read(chunk_size) + destination.write(chunk) + br += len(chunk) + if len(chunk) < chunk_size: + break + # END reading output stream + return br + +def join_path(a, *p): + """Join path tokens together similar to os.path.join, but always use + '/' instead of possibly '\' on windows.""" + path = a + for b in p: + if b.startswith('/'): + path += b[1:] + elif path == '' or path.endswith('/'): + path += b + else: + path += '/' + b + return path + +def to_native_path_windows(path): + return path.replace('/','\\') + +def to_native_path_linux(path): + return path.replace('\\','/') + +if sys.platform.startswith('win'): + to_native_path = to_native_path_windows +else: + # no need for any work on linux + def to_native_path_linux(path): + return path + to_native_path = to_native_path_linux + +def join_path_native(a, *p): + """As join path, but makes sure an OS native path is returned. This is only + needed to play it safe on my dear windows and to assure nice paths that only + use '\'""" + return to_native_path(join_path(a, *p)) + + +class Stats(object): + """ + Represents stat information as presented by git at the end of a merge. It is + created from the output of a diff operation. + + ``Example``:: + + c = Commit( sha1 ) + s = c.stats + s.total # full-stat-dict + s.files # dict( filepath : stat-dict ) + + ``stat-dict`` + + A dictionary with the following keys and values:: + + deletions = number of deleted lines as int + insertions = number of inserted lines as int + lines = total number of lines changed as int, or deletions + insertions + + ``full-stat-dict`` + + In addition to the items in the stat-dict, it features additional information:: + + files = number of changed files as int""" + __slots__ = ("total", "files") + + def __init__(self, total, files): + self.total = total + self.files = files + + @classmethod + def _list_from_string(cls, repo, text): + """Create a Stat object from output retrieved by git-diff. + + :return: git.Stat""" + hsh = {'total': {'insertions': 0, 'deletions': 0, 'lines': 0, 'files': 0}, 'files': dict()} + for line in text.splitlines(): + (raw_insertions, raw_deletions, filename) = line.split("\t") + insertions = raw_insertions != '-' and int(raw_insertions) or 0 + deletions = raw_deletions != '-' and int(raw_deletions) or 0 + hsh['total']['insertions'] += insertions + hsh['total']['deletions'] += deletions + hsh['total']['lines'] += insertions + deletions + hsh['total']['files'] += 1 + hsh['files'][filename.strip()] = {'insertions': insertions, + 'deletions': deletions, + 'lines': insertions + deletions} + return Stats(hsh['total'], hsh['files']) + + +class IndexFileSHA1Writer(object): + """Wrapper around a file-like object that remembers the SHA1 of + the data written to it. It will write a sha when the stream is closed + or if the asked for explicitly usign write_sha. + + Only useful to the indexfile + + :note: Based on the dulwich project""" + __slots__ = ("f", "sha1") + + def __init__(self, f): + self.f = f + self.sha1 = make_sha("") + + def write(self, data): + self.sha1.update(data) + return self.f.write(data) + + def write_sha(self): + sha = self.sha1.digest() + self.f.write(sha) + return sha + + def close(self): + sha = self.write_sha() + self.f.close() + return sha + + def tell(self): + return self.f.tell() + + +class LockFile(object): + """Provides methods to obtain, check for, and release a file based lock which + should be used to handle concurrent access to the same file. + + As we are a utility class to be derived from, we only use protected methods. + + Locks will automatically be released on destruction""" + __slots__ = ("_file_path", "_owns_lock") + + def __init__(self, file_path): + self._file_path = file_path + self._owns_lock = False + + def __del__(self): + self._release_lock() + + def _lock_file_path(self): + """:return: Path to lockfile""" + return "%s.lock" % (self._file_path) + + def _has_lock(self): + """:return: True if we have a lock and if the lockfile still exists + :raise AssertionError: if our lock-file does not exist""" + if not self._owns_lock: + return False + + return True + + def _obtain_lock_or_raise(self): + """Create a lock file as flag for other instances, mark our instance as lock-holder + + :raise IOError: if a lock was already present or a lock file could not be written""" + if self._has_lock(): + return + lock_file = self._lock_file_path() + if os.path.isfile(lock_file): + raise IOError("Lock for file %r did already exist, delete %r in case the lock is illegal" % (self._file_path, lock_file)) + + try: + fd = os.open(lock_file, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0) + os.close(fd) + except OSError,e: + raise IOError(str(e)) + + self._owns_lock = True + + def _obtain_lock(self): + """The default implementation will raise if a lock cannot be obtained. + Subclasses may override this method to provide a different implementation""" + return self._obtain_lock_or_raise() + + def _release_lock(self): + """Release our lock if we have one""" + if not self._has_lock(): + return + + # if someone removed our file beforhand, lets just flag this issue + # instead of failing, to make it more usable. + lfp = self._lock_file_path() + try: + os.remove(lfp) + except OSError: + pass + self._owns_lock = False + + +class BlockingLockFile(LockFile): + """The lock file will block until a lock could be obtained, or fail after + a specified timeout. + + :note: If the directory containing the lock was removed, an exception will + be raised during the blocking period, preventing hangs as the lock + can never be obtained.""" + __slots__ = ("_check_interval", "_max_block_time") + def __init__(self, file_path, check_interval_s=0.3, max_block_time_s=sys.maxint): + """Configure the instance + + :parm check_interval_s: + Period of time to sleep until the lock is checked the next time. + By default, it waits a nearly unlimited time + + :parm max_block_time_s: Maximum amount of seconds we may lock""" + super(BlockingLockFile, self).__init__(file_path) + self._check_interval = check_interval_s + self._max_block_time = max_block_time_s + + def _obtain_lock(self): + """This method blocks until it obtained the lock, or raises IOError if + it ran out of time or if the parent directory was not available anymore. + If this method returns, you are guranteed to own the lock""" + starttime = time.time() + maxtime = starttime + float(self._max_block_time) + while True: + try: + super(BlockingLockFile, self)._obtain_lock() + except IOError: + # synity check: if the directory leading to the lockfile is not + # readable anymore, raise an execption + curtime = time.time() + if not os.path.isdir(os.path.dirname(self._lock_file_path())): + msg = "Directory containing the lockfile %r was not readable anymore after waiting %g seconds" % (self._lock_file_path(), curtime - starttime) + raise IOError(msg) + # END handle missing directory + + if curtime >= maxtime: + msg = "Waited %g seconds for lock at %r" % ( maxtime - starttime, self._lock_file_path()) + raise IOError(msg) + # END abort if we wait too long + time.sleep(self._check_interval) + else: + break + # END endless loop + + +class IterableList(list): + """ + List of iterable objects allowing to query an object by id or by named index:: + + heads = repo.heads + heads.master + heads['master'] + heads[0] + + It requires an id_attribute name to be set which will be queried from its + contained items to have a means for comparison. + + A prefix can be specified which is to be used in case the id returned by the + items always contains a prefix that does not matter to the user, so it + can be left out.""" + __slots__ = ('_id_attr', '_prefix') + + def __new__(cls, id_attr, prefix=''): + return super(IterableList,cls).__new__(cls) + + def __init__(self, id_attr, prefix=''): + self._id_attr = id_attr + self._prefix = prefix + + def __getattr__(self, attr): + attr = self._prefix + attr + for item in self: + if getattr(item, self._id_attr) == attr: + return item + # END for each item + return list.__getattribute__(self, attr) + + def __getitem__(self, index): + if isinstance(index, int): + return list.__getitem__(self,index) + + try: + return getattr(self, index) + except AttributeError: + raise IndexError( "No item found with id %r" % (self._prefix + index) ) + + +class Iterable(object): + """Defines an interface for iterable items which is to assure a uniform + way to retrieve and iterate items within the git repository""" + __slots__ = tuple() + _id_attribute_ = "attribute that most suitably identifies your instance" + + @classmethod + def list_items(cls, repo, *args, **kwargs): + """ + Find all items of this type - subclasses can specify args and kwargs differently. + If no args are given, subclasses are obliged to return all items if no additional + arguments arg given. + + :note: Favor the iter_items method as it will + + :return:list(Item,...) list of item instances""" + out_list = IterableList( cls._id_attribute_ ) + out_list.extend(cls.iter_items(repo, *args, **kwargs)) + return out_list + + + @classmethod + def iter_items(cls, repo, *args, **kwargs): + """For more information about the arguments, see list_items + :return: iterator yielding Items""" + raise NotImplementedError("To be implemented by Subclass") + + diff --git a/lib/git/utils.py b/lib/git/utils.py deleted file mode 100644 index 54c3414e..00000000 --- a/lib/git/utils.py +++ /dev/null @@ -1,340 +0,0 @@ -# utils.py -# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors -# -# This module is part of GitPython and is released under -# the BSD License: http://www.opensource.org/licenses/bsd-license.php - -import os -import sys -import time -import tempfile - -from gitdb.util import ( - make_sha, - LockedFD, - file_contents_ro, - LazyMixin, - to_hex_sha, - to_bin_sha - ) - -__all__ = ( "stream_copy", "join_path", "to_native_path_windows", "to_native_path_linux", - "join_path_native", "Stats", "IndexFileSHA1Writer", "Iterable", "IterableList", - "BlockingLockFile", "LockFile" ) - -def stream_copy(source, destination, chunk_size=512*1024): - """Copy all data from the source stream into the destination stream in chunks - of size chunk_size - - :return: amount of bytes written""" - br = 0 - while True: - chunk = source.read(chunk_size) - destination.write(chunk) - br += len(chunk) - if len(chunk) < chunk_size: - break - # END reading output stream - return br - -def join_path(a, *p): - """Join path tokens together similar to os.path.join, but always use - '/' instead of possibly '\' on windows.""" - path = a - for b in p: - if b.startswith('/'): - path += b[1:] - elif path == '' or path.endswith('/'): - path += b - else: - path += '/' + b - return path - -def to_native_path_windows(path): - return path.replace('/','\\') - -def to_native_path_linux(path): - return path.replace('\\','/') - -if sys.platform.startswith('win'): - to_native_path = to_native_path_windows -else: - # no need for any work on linux - def to_native_path_linux(path): - return path - to_native_path = to_native_path_linux - -def join_path_native(a, *p): - """As join path, but makes sure an OS native path is returned. This is only - needed to play it safe on my dear windows and to assure nice paths that only - use '\'""" - return to_native_path(join_path(a, *p)) - - -class Stats(object): - """ - Represents stat information as presented by git at the end of a merge. It is - created from the output of a diff operation. - - ``Example``:: - - c = Commit( sha1 ) - s = c.stats - s.total # full-stat-dict - s.files # dict( filepath : stat-dict ) - - ``stat-dict`` - - A dictionary with the following keys and values:: - - deletions = number of deleted lines as int - insertions = number of inserted lines as int - lines = total number of lines changed as int, or deletions + insertions - - ``full-stat-dict`` - - In addition to the items in the stat-dict, it features additional information:: - - files = number of changed files as int""" - __slots__ = ("total", "files") - - def __init__(self, total, files): - self.total = total - self.files = files - - @classmethod - def _list_from_string(cls, repo, text): - """Create a Stat object from output retrieved by git-diff. - - :return: git.Stat""" - hsh = {'total': {'insertions': 0, 'deletions': 0, 'lines': 0, 'files': 0}, 'files': dict()} - for line in text.splitlines(): - (raw_insertions, raw_deletions, filename) = line.split("\t") - insertions = raw_insertions != '-' and int(raw_insertions) or 0 - deletions = raw_deletions != '-' and int(raw_deletions) or 0 - hsh['total']['insertions'] += insertions - hsh['total']['deletions'] += deletions - hsh['total']['lines'] += insertions + deletions - hsh['total']['files'] += 1 - hsh['files'][filename.strip()] = {'insertions': insertions, - 'deletions': deletions, - 'lines': insertions + deletions} - return Stats(hsh['total'], hsh['files']) - - -class IndexFileSHA1Writer(object): - """Wrapper around a file-like object that remembers the SHA1 of - the data written to it. It will write a sha when the stream is closed - or if the asked for explicitly usign write_sha. - - Only useful to the indexfile - - :note: Based on the dulwich project""" - __slots__ = ("f", "sha1") - - def __init__(self, f): - self.f = f - self.sha1 = make_sha("") - - def write(self, data): - self.sha1.update(data) - return self.f.write(data) - - def write_sha(self): - sha = self.sha1.digest() - self.f.write(sha) - return sha - - def close(self): - sha = self.write_sha() - self.f.close() - return sha - - def tell(self): - return self.f.tell() - - -class LockFile(object): - """Provides methods to obtain, check for, and release a file based lock which - should be used to handle concurrent access to the same file. - - As we are a utility class to be derived from, we only use protected methods. - - Locks will automatically be released on destruction""" - __slots__ = ("_file_path", "_owns_lock") - - def __init__(self, file_path): - self._file_path = file_path - self._owns_lock = False - - def __del__(self): - self._release_lock() - - def _lock_file_path(self): - """:return: Path to lockfile""" - return "%s.lock" % (self._file_path) - - def _has_lock(self): - """:return: True if we have a lock and if the lockfile still exists - :raise AssertionError: if our lock-file does not exist""" - if not self._owns_lock: - return False - - return True - - def _obtain_lock_or_raise(self): - """Create a lock file as flag for other instances, mark our instance as lock-holder - - :raise IOError: if a lock was already present or a lock file could not be written""" - if self._has_lock(): - return - lock_file = self._lock_file_path() - if os.path.isfile(lock_file): - raise IOError("Lock for file %r did already exist, delete %r in case the lock is illegal" % (self._file_path, lock_file)) - - try: - fd = os.open(lock_file, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0) - os.close(fd) - except OSError,e: - raise IOError(str(e)) - - self._owns_lock = True - - def _obtain_lock(self): - """The default implementation will raise if a lock cannot be obtained. - Subclasses may override this method to provide a different implementation""" - return self._obtain_lock_or_raise() - - def _release_lock(self): - """Release our lock if we have one""" - if not self._has_lock(): - return - - # if someone removed our file beforhand, lets just flag this issue - # instead of failing, to make it more usable. - lfp = self._lock_file_path() - try: - os.remove(lfp) - except OSError: - pass - self._owns_lock = False - - -class BlockingLockFile(LockFile): - """The lock file will block until a lock could be obtained, or fail after - a specified timeout. - - :note: If the directory containing the lock was removed, an exception will - be raised during the blocking period, preventing hangs as the lock - can never be obtained.""" - __slots__ = ("_check_interval", "_max_block_time") - def __init__(self, file_path, check_interval_s=0.3, max_block_time_s=sys.maxint): - """Configure the instance - - :parm check_interval_s: - Period of time to sleep until the lock is checked the next time. - By default, it waits a nearly unlimited time - - :parm max_block_time_s: Maximum amount of seconds we may lock""" - super(BlockingLockFile, self).__init__(file_path) - self._check_interval = check_interval_s - self._max_block_time = max_block_time_s - - def _obtain_lock(self): - """This method blocks until it obtained the lock, or raises IOError if - it ran out of time or if the parent directory was not available anymore. - If this method returns, you are guranteed to own the lock""" - starttime = time.time() - maxtime = starttime + float(self._max_block_time) - while True: - try: - super(BlockingLockFile, self)._obtain_lock() - except IOError: - # synity check: if the directory leading to the lockfile is not - # readable anymore, raise an execption - curtime = time.time() - if not os.path.isdir(os.path.dirname(self._lock_file_path())): - msg = "Directory containing the lockfile %r was not readable anymore after waiting %g seconds" % (self._lock_file_path(), curtime - starttime) - raise IOError(msg) - # END handle missing directory - - if curtime >= maxtime: - msg = "Waited %g seconds for lock at %r" % ( maxtime - starttime, self._lock_file_path()) - raise IOError(msg) - # END abort if we wait too long - time.sleep(self._check_interval) - else: - break - # END endless loop - - -class IterableList(list): - """ - List of iterable objects allowing to query an object by id or by named index:: - - heads = repo.heads - heads.master - heads['master'] - heads[0] - - It requires an id_attribute name to be set which will be queried from its - contained items to have a means for comparison. - - A prefix can be specified which is to be used in case the id returned by the - items always contains a prefix that does not matter to the user, so it - can be left out.""" - __slots__ = ('_id_attr', '_prefix') - - def __new__(cls, id_attr, prefix=''): - return super(IterableList,cls).__new__(cls) - - def __init__(self, id_attr, prefix=''): - self._id_attr = id_attr - self._prefix = prefix - - def __getattr__(self, attr): - attr = self._prefix + attr - for item in self: - if getattr(item, self._id_attr) == attr: - return item - # END for each item - return list.__getattribute__(self, attr) - - def __getitem__(self, index): - if isinstance(index, int): - return list.__getitem__(self,index) - - try: - return getattr(self, index) - except AttributeError: - raise IndexError( "No item found with id %r" % (self._prefix + index) ) - - -class Iterable(object): - """Defines an interface for iterable items which is to assure a uniform - way to retrieve and iterate items within the git repository""" - __slots__ = tuple() - _id_attribute_ = "attribute that most suitably identifies your instance" - - @classmethod - def list_items(cls, repo, *args, **kwargs): - """ - Find all items of this type - subclasses can specify args and kwargs differently. - If no args are given, subclasses are obliged to return all items if no additional - arguments arg given. - - :note: Favor the iter_items method as it will - - :return:list(Item,...) list of item instances""" - out_list = IterableList( cls._id_attribute_ ) - out_list.extend(cls.iter_items(repo, *args, **kwargs)) - return out_list - - - @classmethod - def iter_items(cls, repo, *args, **kwargs): - """For more information about the arguments, see list_items - :return: iterator yielding Items""" - raise NotImplementedError("To be implemented by Subclass") - - diff --git a/test/git/test_base.py b/test/git/test_base.py index 1b01cda3..938647d0 100644 --- a/test/git/test_base.py +++ b/test/git/test_base.py @@ -11,7 +11,7 @@ import os from test.testlib import * from git import * from itertools import chain -from git.objects.utils import get_object_type_by_name +from git.objects.util import get_object_type_by_name from gitdb.util import hex_to_bin import tempfile diff --git a/test/git/test_remote.py b/test/git/test_remote.py index 9d4da034..f609b683 100644 --- a/test/git/test_remote.py +++ b/test/git/test_remote.py @@ -6,7 +6,7 @@ from test.testlib import * from git import * -from git.utils import IterableList +from git.util import IterableList import tempfile import shutil import os diff --git a/test/git/test_repo.py b/test/git/test_repo.py index 6554e737..551140a1 100644 --- a/test/git/test_repo.py +++ b/test/git/test_repo.py @@ -7,7 +7,7 @@ import os, sys from test.testlib import * from git import * -from git.utils import join_path_native +from git.util import join_path_native import tempfile import shutil from cStringIO import StringIO diff --git a/test/git/test_util.py b/test/git/test_util.py new file mode 100644 index 00000000..6453bc19 --- /dev/null +++ b/test/git/test_util.py @@ -0,0 +1,105 @@ +# test_utils.py +# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors +# +# This module is part of GitPython and is released under +# the BSD License: http://www.opensource.org/licenses/bsd-license.php + +import os +import tempfile + +from test.testlib import * +from git.util import * +from git.objects.util import * +from git import * +from git.cmd import dashify + +import time + + +class TestUtils(TestCase): + def setup(self): + self.testdict = { + "string": "42", + "int": 42, + "array": [ 42 ], + } + + def test_it_should_dashify(self): + assert_equal('this-is-my-argument', dashify('this_is_my_argument')) + assert_equal('foo', dashify('foo')) + + + def test_lock_file(self): + my_file = tempfile.mktemp() + lock_file = LockFile(my_file) + assert not lock_file._has_lock() + # release lock we don't have - fine + lock_file._release_lock() + + # get lock + lock_file._obtain_lock_or_raise() + assert lock_file._has_lock() + + # concurrent access + other_lock_file = LockFile(my_file) + assert not other_lock_file._has_lock() + self.failUnlessRaises(IOError, other_lock_file._obtain_lock_or_raise) + + lock_file._release_lock() + assert not lock_file._has_lock() + + other_lock_file._obtain_lock_or_raise() + self.failUnlessRaises(IOError, lock_file._obtain_lock_or_raise) + + # auto-release on destruction + del(other_lock_file) + lock_file._obtain_lock_or_raise() + lock_file._release_lock() + + def test_blocking_lock_file(self): + my_file = tempfile.mktemp() + lock_file = BlockingLockFile(my_file) + lock_file._obtain_lock() + + # next one waits for the lock + start = time.time() + wait_time = 0.1 + wait_lock = BlockingLockFile(my_file, 0.05, wait_time) + self.failUnlessRaises(IOError, wait_lock._obtain_lock) + elapsed = time.time() - start + assert elapsed <= wait_time + 0.02 # some extra time it may cost + + def test_user_id(self): + assert '@' in get_user_id() + + def test_parse_date(self): + # test all supported formats + def assert_rval(rval, veri_time, offset=0): + assert len(rval) == 2 + assert isinstance(rval[0], int) and isinstance(rval[1], int) + assert rval[0] == veri_time + assert rval[1] == offset + + # now that we are here, test our conversion functions as well + utctz = altz_to_utctz_str(offset) + assert isinstance(utctz, basestring) + assert utctz_to_altz(verify_utctz(utctz)) == offset + # END assert rval utility + + rfc = ("Thu, 07 Apr 2005 22:13:11 +0000", 0) + iso = ("2005-04-07T22:13:11 -0200", 7200) + iso2 = ("2005-04-07 22:13:11 +0400", -14400) + iso3 = ("2005.04.07 22:13:11 -0000", 0) + alt = ("04/07/2005 22:13:11", 0) + alt2 = ("07.04.2005 22:13:11", 0) + veri_time = 1112904791 # the time this represents + for date, offset in (rfc, iso, iso2, iso3, alt, alt2): + assert_rval(parse_date(date), veri_time, offset) + # END for each date type + + # and failure + self.failUnlessRaises(ValueError, parse_date, 'invalid format') + self.failUnlessRaises(ValueError, parse_date, '123456789 -02000') + self.failUnlessRaises(ValueError, parse_date, ' 123456789 -0200') + + diff --git a/test/git/test_utils.py b/test/git/test_utils.py deleted file mode 100644 index 963e2b55..00000000 --- a/test/git/test_utils.py +++ /dev/null @@ -1,105 +0,0 @@ -# test_utils.py -# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors -# -# This module is part of GitPython and is released under -# the BSD License: http://www.opensource.org/licenses/bsd-license.php - -import os -import tempfile - -from test.testlib import * -from git.utils import * -from git.objects.utils import * -from git import * -from git.cmd import dashify - -import time - - -class TestUtils(TestCase): - def setup(self): - self.testdict = { - "string": "42", - "int": 42, - "array": [ 42 ], - } - - def test_it_should_dashify(self): - assert_equal('this-is-my-argument', dashify('this_is_my_argument')) - assert_equal('foo', dashify('foo')) - - - def test_lock_file(self): - my_file = tempfile.mktemp() - lock_file = LockFile(my_file) - assert not lock_file._has_lock() - # release lock we don't have - fine - lock_file._release_lock() - - # get lock - lock_file._obtain_lock_or_raise() - assert lock_file._has_lock() - - # concurrent access - other_lock_file = LockFile(my_file) - assert not other_lock_file._has_lock() - self.failUnlessRaises(IOError, other_lock_file._obtain_lock_or_raise) - - lock_file._release_lock() - assert not lock_file._has_lock() - - other_lock_file._obtain_lock_or_raise() - self.failUnlessRaises(IOError, lock_file._obtain_lock_or_raise) - - # auto-release on destruction - del(other_lock_file) - lock_file._obtain_lock_or_raise() - lock_file._release_lock() - - def test_blocking_lock_file(self): - my_file = tempfile.mktemp() - lock_file = BlockingLockFile(my_file) - lock_file._obtain_lock() - - # next one waits for the lock - start = time.time() - wait_time = 0.1 - wait_lock = BlockingLockFile(my_file, 0.05, wait_time) - self.failUnlessRaises(IOError, wait_lock._obtain_lock) - elapsed = time.time() - start - assert elapsed <= wait_time + 0.02 # some extra time it may cost - - def test_user_id(self): - assert '@' in get_user_id() - - def test_parse_date(self): - # test all supported formats - def assert_rval(rval, veri_time, offset=0): - assert len(rval) == 2 - assert isinstance(rval[0], int) and isinstance(rval[1], int) - assert rval[0] == veri_time - assert rval[1] == offset - - # now that we are here, test our conversion functions as well - utctz = altz_to_utctz_str(offset) - assert isinstance(utctz, basestring) - assert utctz_to_altz(verify_utctz(utctz)) == offset - # END assert rval utility - - rfc = ("Thu, 07 Apr 2005 22:13:11 +0000", 0) - iso = ("2005-04-07T22:13:11 -0200", 7200) - iso2 = ("2005-04-07 22:13:11 +0400", -14400) - iso3 = ("2005.04.07 22:13:11 -0000", 0) - alt = ("04/07/2005 22:13:11", 0) - alt2 = ("07.04.2005 22:13:11", 0) - veri_time = 1112904791 # the time this represents - for date, offset in (rfc, iso, iso2, iso3, alt, alt2): - assert_rval(parse_date(date), veri_time, offset) - # END for each date type - - # and failure - self.failUnlessRaises(ValueError, parse_date, 'invalid format') - self.failUnlessRaises(ValueError, parse_date, '123456789 -02000') - self.failUnlessRaises(ValueError, parse_date, ' 123456789 -0200') - - -- cgit v1.2.3 From 18be0972304dc7f1a2a509595de7da689bddbefa Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 29 Jun 2010 20:16:37 +0200 Subject: Removed blob.data property as there is no real reason for an exception to the rule of trying not to cache possibly heavy data. The data_stream method should be used instead --- CHANGES | 38 +++++++++++++++++++++++--------------- lib/git/objects/blob.py | 12 +----------- test/git/test_base.py | 2 -- test/git/test_blob.py | 8 -------- 4 files changed, 24 insertions(+), 36 deletions(-) diff --git a/CHANGES b/CHANGES index 66e0a42d..61626f47 100644 --- a/CHANGES +++ b/CHANGES @@ -1,26 +1,34 @@ ======= CHANGES ======= + 0.3 === - * ConcurrentWriteOperation was removed, and replaced by LockedFD - * IndexFile.get_entries_key was renamed to entry_key - * IndexEntry instances contained in IndexFile.entries now use binary sha's. Use - the .hexsha property to obtain the hexadecimal version. The .sha property - was removed to make the use of the respective sha more explicit. - * IndexFile.write_tree: removed missing_ok keyword, its always True now - Instead of raising GitCommandError it raises UnmergedEntriesError - * diff.Diff.null_hex_sha renamed to NULL_HEX_SHA, to be conforming with - the naming in the Object base class - * Object instances, and everything derived from it, now use binary sha's internally. The 'sha' member was removed, in favor of the 'binsha' member. - An 'hexsha' property is available for convenient conversions. - They may only be initialized using their binary shas, reference names or revision specs are not allowed anymore. - * The .data attribute was removed from the Object type, it is only available - on the Blob type. - * For consistency with naming conventions used in sub-modules like gitdb, the following modules have been renamed +Renamed Modules +--------------- +* For consistency with naming conventions used in sub-modules like gitdb, the following modules have been renamed * git.utils -> git.util * git.errors -> git.exc * git.objects.utils -> git.objects.util + +General +------- +* Object instances, and everything derived from it, now use binary sha's internally. The 'sha' member was removed, in favor of the 'binsha' member. + An 'hexsha' property is available for convenient conversions. + They may only be initialized using their binary shas, reference names or revision specs are not allowed anymore. +* IndexEntry instances contained in IndexFile.entries now use binary sha's. Use the .hexsha property to obtain the hexadecimal version. The .sha property was removed to make the use of the respective sha more explicit. +* If objects are instantiated explicitly, a binary sha is required to identify the object, where previously any rev-spec could be used. The ref-spec compatible +version still exists as Object.new or Repo.commit|Repo.tree respectively. +* The .data attribute was removed from the Object type, to obtain plain data, use the data_stream property instead. +* ConcurrentWriteOperation was removed, and replaced by LockedFD +* IndexFile.get_entries_key was renamed to entry_key +* IndexFile.write_tree: removed missing_ok keyword, its always True now + Instead of raising GitCommandError it raises UnmergedEntriesError. + This is required as the pure-python implementation doesn't support the missing_ok keyword yet. +* diff.Diff.null_hex_sha renamed to NULL_HEX_SHA, to be conforming with + the naming in the Object base class + + 0.2 Beta 2 diff --git a/lib/git/objects/blob.py b/lib/git/objects/blob.py index d0ef54c7..32f8c61c 100644 --- a/lib/git/objects/blob.py +++ b/lib/git/objects/blob.py @@ -14,17 +14,7 @@ class Blob(base.IndexObject): DEFAULT_MIME_TYPE = "text/plain" type = "blob" - __slots__ = "data" - - def _set_cache_(self, attr): - if attr == "data": - ostream = self.repo.odb.stream(self.binsha) - self.size = ostream.size - self.data = ostream.read() - # assert ostream.type == self.type, _assertion_msg_format % (self.binsha, ostream.type, self.type) - else: - super(Blob, self)._set_cache_(attr) - # END handle data + __slots__ = tuple() @property def mime_type(self): diff --git a/test/git/test_base.py b/test/git/test_base.py index 938647d0..db13feae 100644 --- a/test/git/test_base.py +++ b/test/git/test_base.py @@ -42,8 +42,6 @@ class TestBase(TestBase): assert item.hexsha == hexsha assert item.type == typename assert item.size - if isinstance(item, Blob): - assert item.data assert item == item assert not item != item assert str(item) == item.hexsha diff --git a/test/git/test_blob.py b/test/git/test_blob.py index e5fca0a6..623ed179 100644 --- a/test/git/test_blob.py +++ b/test/git/test_blob.py @@ -10,14 +10,6 @@ from gitdb.util import hex_to_bin class TestBlob(TestBase): - def test_should_cache_data(self): - bid = 'a802c139d4767c89dcad79d836d05f7004d39aac' - blob = Blob(self.rorepo, hex_to_bin(bid)) - blob.data - assert blob.data - blob.size - blob.size - def test_mime_type_should_return_mime_type_for_known_types(self): blob = Blob(self.rorepo, **{'binsha': Blob.NULL_BIN_SHA, 'path': 'foo.png'}) assert_equal("image/png", blob.mime_type) -- cgit v1.2.3 From 0369384c8b79c44c5369f1b6c05046899f8886da Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 29 Jun 2010 21:49:31 +0200 Subject: revised tutorial to match the changed usage, added basic information about object databases --- doc/intro.rst | 12 ++++----- doc/reference.rst | 12 ++++----- doc/tutorial.rst | 79 +++++++++++++++++++++++++++++++++---------------------- 3 files changed, 59 insertions(+), 44 deletions(-) diff --git a/doc/intro.rst b/doc/intro.rst index d4106b19..51187569 100644 --- a/doc/intro.rst +++ b/doc/intro.rst @@ -4,20 +4,18 @@ Overview / Install ================== -GitPython is a python library used to interact with Git repositories. +GitPython is a python library used to interact with git repositories, high-level like git-porcelain, or low-level like git-plumbing. -GitPython was a port of the grit_ library in Ruby created by -Tom Preston-Werner and Chris Wanstrath, but grew beyond its heritage through its improved design and performance. +It provides abstractions of git objects for easy access of repository data, and additionally allows you to access the git repository more directly using either a pure python implementation, or the faster, but more resource intensive git command implementation. -.. _grit: http://grit.rubyforge.org +The object database implementation is optimized for handling large quantities of objects and large datasets, which is achieved by using low-level structures and data streaming. Requirements ============ -* Git_ tested with 1.5.3.7 -* Requires Git_ 1.7.0 or newer +* Tested with `Git`_ 1.7.0 or newer * `Python Nose`_ - used for running the tests -* `Mock by Michael Foord`_ used for tests. Requires 0.5 +* `Mock by Michael Foord`_ used for tests. Requires version 0.5 .. _Git: http://git-scm.com/ .. _Python Nose: http://code.google.com/p/python-nose/ diff --git a/doc/reference.rst b/doc/reference.rst index 90df10f8..5952fe2d 100644 --- a/doc/reference.rst +++ b/doc/reference.rst @@ -52,15 +52,15 @@ Objects.Submodule :members: :undoc-members: -Objects.Utils +Objects.Util ------------- -.. automodule:: git.objects.utils +.. automodule:: git.objects.util :members: :undoc-members: Index.Base -------------- +---------- .. automodule:: git.index.base :members: @@ -138,9 +138,9 @@ Repo :members: :undoc-members: -Utils ------ +Util +---- -.. automodule:: git.utils +.. automodule:: git.util :members: :undoc-members: diff --git a/doc/tutorial.rst b/doc/tutorial.rst index 37dd8d32..033bbad4 100644 --- a/doc/tutorial.rst +++ b/doc/tutorial.rst @@ -8,7 +8,7 @@ GitPython Tutorial ================== -GitPython provides object model access to your git repository. This tutorial is composed of multiple sections, each of which explain a real-life usecase. +GitPython provides object model access to your git repository. This tutorial is composed of multiple sections, each of which explains a real-life usecase. Initialize a Repo object ************************ @@ -17,10 +17,12 @@ The first step is to create a ``Repo`` object to represent your repository:: from git import * repo = Repo("/Users/mtrier/Development/git-python") + assert repo.bare == False -In the above example, the directory ``/Users/mtrier/Development/git-python`` is my working repository and contains the ``.git`` directory. You can also initialize GitPython with a bare repository:: +In the above example, the directory ``/Users/mtrier/Development/git-python`` is my working repository and contains the ``.git`` directory. You can also initialize GitPython with a *bare* repository:: repo = Repo.create("/var/git/git-python.git") + assert repo.bare == True A repo object provides high-level access to your data, it allows you to create and delete heads, tags and remotes and access the configuration of the repository:: @@ -43,6 +45,25 @@ Archive the repository contents to a tar file:: repo.archive(open("repo.tar",'w')) + +Object Databases +**************** +``Repo`` instances are powered by its object database instance which will be used when extracting any data, or when writing new objects. + +The type of the database determines certain performance characteristics, such as the quantity of objects that can be read per second, the resource usage when reading large data files, as well as the average memory footprint of your application. + +GitDB +===== +The GitDB is a pure-python implementation of the git object database. It is the default database to use in GitPython 0.3. Its uses less memory when handling huge files, but will be 2 to 5 times slower when extracting large quantities small of objects from densely packed repositories:: + + repo = Repo("path/to/repo", odbt=GitDB) + +GitCmdObjectDB +============== +The git command database uses persistent git-cat-file instances to read repository information. These operate very fast under all conditions, but will consume additional memory for the process itself. When extracting large files, memory usage will be much higher than the one of the ``GitDB``:: + + repo = Repo("path/to/repo", odbt=GitCmdObjectDB) + Examining References ******************** @@ -88,11 +109,11 @@ Change the symbolic reference to switch branches cheaply ( without adjusting the Understanding Objects ********************* -An Object is anything storable in git's object database. Objects contain information about their type, their uncompressed size as well as the actual data. Each object is uniquely identified by a SHA1 hash, being 40 hexadecimal characters in size or 20 bytes in size. +An Object is anything storable in git's object database. Objects contain information about their type, their uncompressed size as well as the actual data. Each object is uniquely identified by a binary SHA1 hash, being 20 bytes in size. Git only knows 4 distinct object types being Blobs, Trees, Commits and Tags. -In Git-Pyhton, all objects can be accessed through their common base, compared and hashed, as shown in the following example:: +In Git-Python, all objects can be accessed through their common base, compared and hashed. They are usually not instantiated directly, but through references or specialized repository functions:: hc = repo.head.commit hct = hc.tree @@ -100,34 +121,32 @@ In Git-Pyhton, all objects can be accessed through their common base, compared hc != repo.tags[0] hc == repo.head.reference.commit -Basic fields are:: +Common fields are:: hct.type 'tree' hct.size 166 - hct.sha + hct.hexsha 'a95eeb2a7082212c197cabbf2539185ec74ed0e8' - hct.data # returns string with pure uncompressed data - '...' - len(hct.data) == hct.size + hct.binsha + 'binary 20 byte sha1' -Index Objects are objects that can be put into git's index. These objects are trees and blobs which additionally know about their path in the filesystem as well as their mode:: +Index Objects are objects that can be put into git's index. These objects are trees, blobs and submodules which additionally know about their path in the filesystem as well as their mode:: hct.path # root tree has no path '' hct.trees[0].path # the first subdirectory has one though 'dir' - htc.mode # trees have mode 0 - 0 + htc.mode # trees have the mode of a linux directory + 040000 '%o' % htc.blobs[0].mode # blobs have a specific mode though comparable to a standard linux fs 100644 Access blob data (or any object data) directly or using streams:: - htc.data # binary tree data as string ( inefficient ) - htc.blobs[0].data_stream # stream object to read data from - htc.blobs[0].stream_data(my_stream) # write data to given stream + htc.blobs[0].data_stream.read() # stream object to read data from + htc.blobs[0].stream_data(open("blob_data", "w")) # write data to given stream The Commit object @@ -153,11 +172,11 @@ The above will return commits 21-30 from the commit list.:: headcommit = repo.head.commit - headcommit.sha + headcommit.hexsha '207c0c4418115df0d30820ab1a9acd2ea4bf4431' headcommit.parents - [] + (,) headcommit.tree @@ -178,7 +197,7 @@ The above will return commits 21-30 from the commit list.:: 'cleaned up a lot of test information. Fixed escaping so it works with subprocess.' -Note: date time is represented in a ``seconds since epock`` format. Conversion to human readable form can be accomplished with the various time module methods:: +Note: date time is represented in a ``seconds since epoch`` format. Conversion to human readable form can be accomplished with the various `time module `_ methods:: import time time.asctime(time.gmtime(headcommit.committed_date)) @@ -187,8 +206,6 @@ Note: date time is represented in a ``seconds since epock`` format. Conversion t time.strftime("%a, %d %b %Y %H:%M", time.gmtime(headcommit.committed_date)) 'Wed, 7 May 2008 05:56' -.. _struct_time: http://docs.python.org/library/time.html - You can traverse a commit's ancestry by chaining calls to ``parents``:: headcommit.parents[0].parents[0].parents[0] @@ -203,7 +220,7 @@ A tree records pointers to the contents of a directory. Let's say you want the r tree = repo.heads.master.commit.tree - tree.sha + tree.hexsha 'a006b5b1a8115185a228b7514cdcd46fed90dc92' Once you have a tree, you can get the contents:: @@ -230,13 +247,13 @@ Its useful to know that a tree behaves like a list with the ability to query en 'dir/file' blob.abspath '/Users/mtrier/Development/git-python/dir/file' - >>>tree['dir/file'].sha == blob.sha + >>>tree['dir/file'].binsha == blob.binsha There is a convenience method that allows you to get a named sub-object from a tree with a syntax similar to how paths are written in an unix system:: tree/"lib" - tree/"dir/file" == blob.sha + tree/"dir/file" == blob You can also get a tree directly from the repository if you know its name:: @@ -252,7 +269,7 @@ As trees only allow direct access to their direct entries, use the traverse met tree.traverse() - for entry in traverse(): do_something_with(entry) + for entry in tree.traverse(): do_something_with(entry) The Index Object @@ -263,15 +280,15 @@ The git index is the stage containing changes to be written with the next commit Access objects and add/remove entries. Commit the changes:: - for stage,blob in index.iter_blobs(): do_something(...) - Access blob objects - for (path,stage),entry in index.entries.iteritems: pass - Access the entries directly + for stage, blob in index.iter_blobs(): do_something(...) + # Access blob objects + for (path, stage), entry in index.entries.iteritems: pass + # Access the entries directly index.add(['my_new_file']) # add a new file to the index index.remove(['dir/existing_file']) new_commit = index.commit("my commit message") -Create new indices from other trees or as result of a merge. Write that result to a new index:: +Create new indices from other trees or as result of a merge. Write that result to a new index file:: tmp_index = Index.from_tree(repo, 'HEAD~1') # load a tree into a temporary index merge_index = Index.from_tree(repo, 'base', 'HEAD', 'some_branch') # merge two trees three-way @@ -303,7 +320,7 @@ Change configuration for a specific remote only:: Obtaining Diff Information ************************** -Diffs can generally be obtained by Subclasses of ``Diffable`` as they provide the ``diff`` method. This operation yields a DiffIndex allowing you to easily access diff information about paths. +Diffs can generally be obtained by subclasses of ``Diffable`` as they provide the ``diff`` method. This operation yields a DiffIndex allowing you to easily access diff information about paths. Diffs can be made between the Index and Trees, Index and the working tree, trees and trees as well as trees and the working copy. If commits are involved, their tree will be used implicitly:: @@ -346,7 +363,7 @@ The return value will by default be a string of the standard output channel prod Keyword arguments translate to short and long keyword arguments on the commandline. The special notion ``git.command(flag=True)`` will create a flag without value like ``command --flag``. -If ``None`` is found in the arguments, it will be dropped silently. Lists and tuples passed as arguments will be unpacked to individual arguments. Objects are converted to strings using the str(...) function. +If ``None`` is found in the arguments, it will be dropped silently. Lists and tuples passed as arguments will be unpacked recursively to individual arguments. Objects are converted to strings using the str(...) function. And even more ... ***************** -- cgit v1.2.3 From fde6522c40a346c8b1d588a2b8d4dd362ae1f58f Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 29 Jun 2010 23:47:26 +0200 Subject: Added whatsnew and put it into the index Set project version to 0.3.0 beta --- doc/conf.py | 6 +++--- doc/index.rst | 3 ++- doc/whatsnew.rst | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 doc/whatsnew.rst diff --git a/doc/conf.py b/doc/conf.py index 0196759d..0dda5b0f 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -43,16 +43,16 @@ master_doc = 'index' # General information about the project. project = u'GitPython' -copyright = u'Copyright (C) 2008, 2009 Michael Trier and contributors' +copyright = u'Copyright (C) 2008, 2009 Michael Trier and contributors, 2010 Sebastian Thiel' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '0.2.0' +version = '0.3.0' # The full version, including alpha/beta/rc tags. -release = '0.2.0 Beta' +release = '0.3.0 Beta 1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/index.rst b/doc/index.rst index d05a8c98..6bb8d1fe 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -6,9 +6,10 @@ GitPython Documentation ======================= .. toctree:: - :maxdepth: 3 + :maxdepth: 2 intro + whatsnew tutorial reference roadmap diff --git a/doc/whatsnew.rst b/doc/whatsnew.rst new file mode 100644 index 00000000..9a49e0b9 --- /dev/null +++ b/doc/whatsnew.rst @@ -0,0 +1,59 @@ + +################ +Whats New in 0.3 +################ +GitPython 0.3 is the first step in creating a hybrid which uses a pure python implementations for all simple git features which can be implemented without significant performance penalties. Everything else is still performed using the git command, which is nicely integrated and easy to use. + +Its biggest strength, being the support for all git features through the git command itself, is a weakness as well considering the possibly vast amount of times the git command is being started up. Depending on the actual command being performaned, the git repository will be initialized on many of these invocations, causing additional overhead for possibly tiny operations. + +Keeping as many major operations in the python world will result in improved caching benefits as certain data structures just have to be initialized once and can be reused multiple times. This mode of operation may improve performance when altering the git database on a low level, and is clearly beneficial on operating systems where command invocations are very slow. + +**************** +Object Databases +**************** +An object database provides a simple interface to query object information or to write new object data. Objects are generally identified by their 20 byte binary sha1 value during query. + +GitPython uses the ``gitdb`` project to provide a pure-python implementation of the git database, which includes reading and writing loose objects, reading pack files and handling alternate repositories. + +The great thing about this is that ``Repo`` objects can use any object database, hence it easily supports different implementations with different performance characteristics. If you are thinking in extremes, you can implement your own database representation, which may be more efficient for what you want to do specifically, like handling big files more efficiently. + +************************ +Reduced Memory Footprint +************************ +Objects, such as commits, tags, trees and blobs now use 20 byte sha1 signatures internally, reducing their memory demands by 20 bytes per object, allowing you to keep more objects in memory at the same time. + +The internal caches of tree objects were improved to use less memory as well. + +################## +Upgrading from 0.2 +################## +GitPython 0.2 essentially behaves like GitPython 0.3 with a Repository using the ``GitCmdObjectDB`` instead of the ``GitDB`` as object database backend. Additionally it can be used more conveniently through implicit conversions and provides a feature set strikingly similar to 0.3. + +************************** +Why you should not upgrade +************************** +GitPython 0.3 in most cases will not run faster than GitPython 0.2, the opposite might be the case at it uses the pure python implementation by default. +There have been a few renames which will need additional adjustments by your code. + +Generally, if you only read git repositories, version 0.2 is sufficient and very well performing. + +********************** +Why you should upgrade +********************** +GitPython 0.2 has reached its end of line, and it is unlikely to receive more than contributed patches. 0.3 is the main development branch which will lead into the future. + +GitPython 0.3 provides memory usage optimization and is very flexible in the way it uses to access the object database. With minimal effort, 0.3 will be running as fast as 0.2. It marks the first step of more versions to come, and will improve over time. + +GitPython 0.3 is especially suitable for everyone who needs not only read, but also write access to a git repository. It is optimized to keep the memory consumption as low as possible, especially when handling large data sets. GitPython 0.3 operates on streams, not on possibly huge chunks of data. + + +************** +Guided Upgrade +************** +This guide should help to make the upgrade as painless as possible, hence it points out where to start, and what to look out for. + +* Have a look at the CHANGES log file and read all important changes about 0.3 for an overview. +* Start applying the renames, generally the ``utils`` modules are now called ``util``, ``errors`` is called ``exc``. +* Search for occurrences of the ``sha`` property of object instances. A similar value can be obtained through the new ``hexsha`` property. The native sha1 value is the ``binsha`` though. +* Search for code which instantiates objects directly. Their initializer now requires a 20 byte binary Sha1, rev-specs cannot be used anymore. For a similar effect, either convert your hexadecimal shas to binary shas beforehand ( ``binascii.unhexlify`` for instance ), or use higher level functions such as ``Object.new``, ``Repo.commit`` or ``Repo.tree``. The latter ones takes rev-specs and hexadecimal sha1 hashes. + -- cgit v1.2.3 From 28a33ca17ac5e0816a3e24febb47ffcefa663980 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 1 Jul 2010 15:45:25 +0200 Subject: Added further information about the required submodules, and how to install them. Incremeneted version to 0.3.0 beta1 --- VERSION | 2 +- doc/conf.py | 4 ++++ doc/intro.rst | 25 +++++++++++++++++++++++-- lib/git/ext/gitdb | 2 +- setup.py | 16 +++++----------- 5 files changed, 34 insertions(+), 15 deletions(-) mode change 100644 => 100755 setup.py diff --git a/VERSION b/VERSION index 8d453c41..18622057 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2.0-beta1 +0.3.0-beta1 diff --git a/doc/conf.py b/doc/conf.py index 0dda5b0f..766ecb75 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -92,6 +92,10 @@ pygments_style = 'sphinx' # Options for HTML output # ----------------------- +html_theme_options = { + "stickysidebar": "true" +} + # The style sheet to use for HTML and HTML Help pages. A file of that name # must exist either in Sphinx' static/ path, or in one of the custom paths # given in html_static_path. diff --git a/doc/intro.rst b/doc/intro.rst index 51187569..c96766fb 100644 --- a/doc/intro.rst +++ b/doc/intro.rst @@ -13,13 +13,21 @@ The object database implementation is optimized for handling large quantities of Requirements ============ -* Tested with `Git`_ 1.7.0 or newer +* `Git`_ 1.7.0 or newer + It should also work with older versions, but it may be that some operations + involving remotes will not work as expected. +* `GitDB`_ - a pure python git database implementation + + * `async`_ - asynchronous task scheduling + * `Python Nose`_ - used for running the tests * `Mock by Michael Foord`_ used for tests. Requires version 0.5 .. _Git: http://git-scm.com/ .. _Python Nose: http://code.google.com/p/python-nose/ .. _Mock by Michael Foord: http://www.voidspace.org.uk/python/mock.html +.. _GitDB: http://pypi.python.org/pypi/gitdb +.. _async: http://pypi.python.org/pypi/async Installing GitPython ==================== @@ -49,6 +57,8 @@ script: .. sourcecode:: none # python setup.py install + +.. note:: In this case, you have to manually install `GitDB`_ and `async`_ as well. It would be recommended to use the :ref:`git source repository ` in that case. Getting Started =============== @@ -63,6 +73,8 @@ API Reference An organized section of the GitPthon API is at :ref:`api_reference_toplevel`. +.. _source-code-label: + Source Code =========== @@ -75,7 +87,16 @@ and cloned using:: $ git clone git://gitorious.org/git-python/mainline.git git-python $ git clone git://github.com/Byron/GitPython.git git-python - + +Initialize all submodules to obtain the required dependencies with:: + + $ cd git-python + $ git submodule update --init --recursive + +Finally verify the installation by running the `nose powered `_ unit tests:: + + $ nosetests + Mailing List ============ http://groups.google.com/group/git-python diff --git a/lib/git/ext/gitdb b/lib/git/ext/gitdb index c265c97f..7562fdd9 160000 --- a/lib/git/ext/gitdb +++ b/lib/git/ext/gitdb @@ -1 +1 @@ -Subproject commit c265c97f9130d2225b923b427736796c0a0d957c +Subproject commit 7562fdd96ab995f6c25fc102ef40a285283c844e diff --git a/setup.py b/setup.py old mode 100644 new mode 100755 index 05547213..bfc2be0a --- a/setup.py +++ b/setup.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python try: from setuptools import setup, find_packages except ImportError: @@ -54,22 +55,15 @@ setup(name = "GitPython", cmdclass={'build_py': build_py, 'sdist': sdist}, version = VERSION, description = "Python Git Library", - author = "Michael Trier", - author_email = "mtrier@gmail.com", + author = "Sebastian Thiel, Michael Trier", + author_email = "byronimo@gmail.com, mtrier@gmail.com", url = "http://gitorious.org/projects/git-python/", packages = find_packages('lib'), package_dir = {'':'lib'}, license = "BSD License", + requires=('gitdb (>=0.5)',), long_description = """\ -GitPython is a python library used to interact with Git repositories. - -GitPython provides object model access to your git repository. Once you have -created a repository object, you can traverse it to find parent commit(s), -trees, blobs, etc. - -GitPython is a port of the grit library in Ruby created by -Tom Preston-Werner and Chris Wanstrath. -""", +GitPython is a python library used to interact with Git repositories""", classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", -- cgit v1.2.3 From d2ebc6193f7205fd1686678a5707262cb1c59bb0 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 1 Jul 2010 16:11:43 +0200 Subject: Moved all source files into the source folder, separating the build from the source directory --- doc/.gitignore | 1 + doc/Makefile | 42 +++--- doc/conf.py | 195 ------------------------ doc/index.rst | 23 --- doc/intro.rst | 112 -------------- doc/reference.rst | 146 ------------------ doc/roadmap.rst | 6 - doc/source/conf.py | 195 ++++++++++++++++++++++++ doc/source/index.rst | 23 +++ doc/source/intro.rst | 112 ++++++++++++++ doc/source/reference.rst | 146 ++++++++++++++++++ doc/source/roadmap.rst | 6 + doc/source/tutorial.rst | 374 +++++++++++++++++++++++++++++++++++++++++++++++ doc/source/whatsnew.rst | 59 ++++++++ doc/tutorial.rst | 374 ----------------------------------------------- doc/whatsnew.rst | 59 -------- 16 files changed, 937 insertions(+), 936 deletions(-) create mode 100644 doc/.gitignore delete mode 100644 doc/conf.py delete mode 100644 doc/index.rst delete mode 100644 doc/intro.rst delete mode 100644 doc/reference.rst delete mode 100644 doc/roadmap.rst create mode 100644 doc/source/conf.py create mode 100644 doc/source/index.rst create mode 100644 doc/source/intro.rst create mode 100644 doc/source/reference.rst create mode 100644 doc/source/roadmap.rst create mode 100644 doc/source/tutorial.rst create mode 100644 doc/source/whatsnew.rst delete mode 100644 doc/tutorial.rst delete mode 100644 doc/whatsnew.rst diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 00000000..567609b1 --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/doc/Makefile b/doc/Makefile index dbc6dec2..39fe377f 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -9,7 +9,7 @@ PAPER = # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +ALLSPHINXOPTS = -d build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source .PHONY: help clean html web pickle htmlhelp latex changes linkcheck @@ -24,52 +24,52 @@ help: @echo " linkcheck to check all external links for integrity" clean: - -rm -rf _build/* + -rm -rf build/* html: - mkdir -p _build/html _build/doctrees - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html + mkdir -p build/html build/doctrees + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) build/html @echo - @echo "Build finished. The HTML pages are in _build/html." + @echo "Build finished. The HTML pages are in build/html." pickle: - mkdir -p _build/pickle _build/doctrees - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle + mkdir -p build/pickle build/doctrees + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) build/pickle @echo @echo "Build finished; now you can process the pickle files." web: pickle json: - mkdir -p _build/json _build/doctrees - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) _build/json + mkdir -p build/json build/doctrees + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) build/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: - mkdir -p _build/htmlhelp _build/doctrees - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp + mkdir -p build/htmlhelp build/doctrees + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) build/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in _build/htmlhelp." + ".hhp project file in build/htmlhelp." latex: - mkdir -p _build/latex _build/doctrees - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex + mkdir -p build/latex build/doctrees + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) build/latex @echo - @echo "Build finished; the LaTeX files are in _build/latex." + @echo "Build finished; the LaTeX files are in build/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: - mkdir -p _build/changes _build/doctrees - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes + mkdir -p build/changes build/doctrees + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) build/changes @echo - @echo "The overview file is in _build/changes." + @echo "The overview file is in build/changes." linkcheck: - mkdir -p _build/linkcheck _build/doctrees - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck + mkdir -p build/linkcheck build/doctrees + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) build/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ - "or in _build/linkcheck/output.txt." + "or in build/linkcheck/output.txt." diff --git a/doc/conf.py b/doc/conf.py deleted file mode 100644 index 766ecb75..00000000 --- a/doc/conf.py +++ /dev/null @@ -1,195 +0,0 @@ -# -*- coding: utf-8 -*- -# -# GitPython documentation build configuration file, created by -# sphinx-quickstart on Sat Jan 24 11:51:01 2009. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# The contents of this file are pickled, so don't put values in the namespace -# that aren't pickleable (module imports are okay, they're removed automatically). -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sys, os - -# If your extensions are in another directory, add it here. If the directory -# is relative to the documentation root, use os.path.abspath to make it -# absolute, like shown here. -#sys.path.append(os.path.abspath('.')) -sys.path.insert(0, os.path.abspath('../lib')) - -# General configuration -# --------------------- - -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'GitPython' -copyright = u'Copyright (C) 2008, 2009 Michael Trier and contributors, 2010 Sebastian Thiel' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '0.3.0' -# The full version, including alpha/beta/rc tags. -release = '0.3.0 Beta 1' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of documents that shouldn't be included in the build. -#unused_docs = [] - -# List of directories, relative to source directory, that shouldn't be searched -# for source files. -exclude_trees = ['_build'] - -# The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - - -# Options for HTML output -# ----------------------- - -html_theme_options = { - "stickysidebar": "true" -} - -# The style sheet to use for HTML and HTML Help pages. A file of that name -# must exist either in Sphinx' static/ path, or in one of the custom paths -# given in html_static_path. -html_style = 'default.css' - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['static'] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_use_modindex = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, the reST sources are included in the HTML build as _sources/. -#html_copy_source = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = '' - -# Output file base name for HTML help builder. -htmlhelp_basename = 'gitpythondoc' - - -# Options for LaTeX output -# ------------------------ - -# The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' - -# The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, document class [howto/manual]). -latex_documents = [ - ('index', 'GitPython.tex', ur'GitPython Documentation', - ur'Michael Trier', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# Additional stuff for the LaTeX preamble. -#latex_preamble = '' - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_use_modindex = True diff --git a/doc/index.rst b/doc/index.rst deleted file mode 100644 index 6bb8d1fe..00000000 --- a/doc/index.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. GitPython documentation master file, created by sphinx-quickstart on Sat Jan 24 11:51:01 2009. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -GitPython Documentation -======================= - -.. toctree:: - :maxdepth: 2 - - intro - whatsnew - tutorial - reference - roadmap - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - diff --git a/doc/intro.rst b/doc/intro.rst deleted file mode 100644 index c96766fb..00000000 --- a/doc/intro.rst +++ /dev/null @@ -1,112 +0,0 @@ -.. _intro_toplevel: - -================== -Overview / Install -================== - -GitPython is a python library used to interact with git repositories, high-level like git-porcelain, or low-level like git-plumbing. - -It provides abstractions of git objects for easy access of repository data, and additionally allows you to access the git repository more directly using either a pure python implementation, or the faster, but more resource intensive git command implementation. - -The object database implementation is optimized for handling large quantities of objects and large datasets, which is achieved by using low-level structures and data streaming. - -Requirements -============ - -* `Git`_ 1.7.0 or newer - It should also work with older versions, but it may be that some operations - involving remotes will not work as expected. -* `GitDB`_ - a pure python git database implementation - - * `async`_ - asynchronous task scheduling - -* `Python Nose`_ - used for running the tests -* `Mock by Michael Foord`_ used for tests. Requires version 0.5 - -.. _Git: http://git-scm.com/ -.. _Python Nose: http://code.google.com/p/python-nose/ -.. _Mock by Michael Foord: http://www.voidspace.org.uk/python/mock.html -.. _GitDB: http://pypi.python.org/pypi/gitdb -.. _async: http://pypi.python.org/pypi/async - -Installing GitPython -==================== - -Installing GitPython is easily done using -`setuptools`_. Assuming it is -installed, just run the following from the command-line: - -.. sourcecode:: none - - # easy_install GitPython - -This command will download the latest version of GitPython from the -`Python Package Index `_ and install it -to your system. More information about ``easy_install`` and pypi can be found -here: - -* `setuptools`_ -* `install setuptools `_ -* `pypi `_ - -.. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools - -Alternatively, you can install from the distribution using the ``setup.py`` -script: - -.. sourcecode:: none - - # python setup.py install - -.. note:: In this case, you have to manually install `GitDB`_ and `async`_ as well. It would be recommended to use the :ref:`git source repository ` in that case. - -Getting Started -=============== - -* :ref:`tutorial-label` - This tutorial provides a walk-through of some of - the basic functionality and concepts used in GitPython. It, however, is not - exhaustive so you are encouraged to spend some time in the - :ref:`api_reference_toplevel`. - -API Reference -============= - -An organized section of the GitPthon API is at :ref:`api_reference_toplevel`. - -.. _source-code-label: - -Source Code -=========== - -GitPython's git repo is available on Gitorious and GitHub, which can be browsed at: - - * http://gitorious.org/projects/git-python/ - * http://github.com/Byron/GitPython - -and cloned using:: - - $ git clone git://gitorious.org/git-python/mainline.git git-python - $ git clone git://github.com/Byron/GitPython.git git-python - -Initialize all submodules to obtain the required dependencies with:: - - $ cd git-python - $ git submodule update --init --recursive - -Finally verify the installation by running the `nose powered `_ unit tests:: - - $ nosetests - -Mailing List -============ -http://groups.google.com/group/git-python - -Issue Tracker -============= -http://byronimo.lighthouseapp.com/projects/51787-gitpython/milestones - -License Information -=================== -GitPython is licensed under the New BSD License. See the LICENSE file for -more information. - diff --git a/doc/reference.rst b/doc/reference.rst deleted file mode 100644 index 5952fe2d..00000000 --- a/doc/reference.rst +++ /dev/null @@ -1,146 +0,0 @@ -.. _api_reference_toplevel: - -API Reference -============= - -Objects.Base ------------- - -.. automodule:: git.objects.base - :members: - :undoc-members: - -Objects.Blob ------------- - -.. automodule:: git.objects.blob - :members: - :undoc-members: - -Objects.Commit --------------- - -.. automodule:: git.objects.commit - :members: - :undoc-members: - -Objects.Tag ------------ - -.. automodule:: git.objects.tag - :members: - :undoc-members: - -Objects.Tree ------------- - -.. automodule:: git.objects.tree - :members: - :undoc-members: - -Objects.Functions ------------------ - -.. automodule:: git.objects.fun - :members: - :undoc-members: - -Objects.Submodule ------------------ - -.. automodule:: git.objects.submodule - :members: - :undoc-members: - -Objects.Util -------------- - -.. automodule:: git.objects.util - :members: - :undoc-members: - -Index.Base ----------- - -.. automodule:: git.index.base - :members: - :undoc-members: - -Index.Functions ---------------- - -.. automodule:: git.index.fun - :members: - :undoc-members: - -Index.Types ------------ - -.. automodule:: git.index.typ - :members: - :undoc-members: - -Index.Util -------------- - -.. automodule:: git.index.util - :members: - :undoc-members: - -GitCmd ------- - -.. automodule:: git.cmd - :members: - :undoc-members: - - -Config ------- - -.. automodule:: git.config - :members: - :undoc-members: - -Diff ----- - -.. automodule:: git.diff - :members: - :undoc-members: - -Errors ------- - -.. automodule:: git.errors - :members: - :undoc-members: - - -Refs ----- - -.. automodule:: git.refs - :members: - :undoc-members: - -Remote ------- - -.. automodule:: git.remote - :members: - :undoc-members: - -Repo ----- - -.. automodule:: git.repo - :members: - :undoc-members: - -Util ----- - -.. automodule:: git.util - :members: - :undoc-members: diff --git a/doc/roadmap.rst b/doc/roadmap.rst deleted file mode 100644 index a6bdc3a0..00000000 --- a/doc/roadmap.rst +++ /dev/null @@ -1,6 +0,0 @@ - -####### -Roadmap -####### -The full list of milestones including associated tasks can be found on lighthouse: http://byronimo.lighthouseapp.com/projects/51787-gitpython/milestones - diff --git a/doc/source/conf.py b/doc/source/conf.py new file mode 100644 index 00000000..21304eaa --- /dev/null +++ b/doc/source/conf.py @@ -0,0 +1,195 @@ +# -*- coding: utf-8 -*- +# +# GitPython documentation build configuration file, created by +# sphinx-quickstart on Sat Jan 24 11:51:01 2009. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# The contents of this file are pickled, so don't put values in the namespace +# that aren't pickleable (module imports are okay, they're removed automatically). +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If your extensions are in another directory, add it here. If the directory +# is relative to the documentation root, use os.path.abspath to make it +# absolute, like shown here. +#sys.path.append(os.path.abspath('.')) +sys.path.insert(0, os.path.abspath('../../lib')) + +# General configuration +# --------------------- + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['.templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'GitPython' +copyright = u'Copyright (C) 2008, 2009 Michael Trier and contributors, 2010 Sebastian Thiel' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.3.0' +# The full version, including alpha/beta/rc tags. +release = '0.3.0 Beta 1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of documents that shouldn't be included in the build. +#unused_docs = [] + +# List of directories, relative to source directory, that shouldn't be searched +# for source files. +exclude_trees = ['build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + + +# Options for HTML output +# ----------------------- + +html_theme_options = { + "stickysidebar": "true" +} + +# The style sheet to use for HTML and HTML Help pages. A file of that name +# must exist either in Sphinx' static/ path, or in one of the custom paths +# given in html_static_path. +html_style = 'default.css' + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['.static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_use_modindex = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, the reST sources are included in the HTML build as _sources/. +#html_copy_source = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = '' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'gitpythondoc' + + +# Options for LaTeX output +# ------------------------ + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, document class [howto/manual]). +latex_documents = [ + ('index', 'GitPython.tex', ur'GitPython Documentation', + ur'Michael Trier', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_use_modindex = True diff --git a/doc/source/index.rst b/doc/source/index.rst new file mode 100644 index 00000000..6bb8d1fe --- /dev/null +++ b/doc/source/index.rst @@ -0,0 +1,23 @@ +.. GitPython documentation master file, created by sphinx-quickstart on Sat Jan 24 11:51:01 2009. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +GitPython Documentation +======================= + +.. toctree:: + :maxdepth: 2 + + intro + whatsnew + tutorial + reference + roadmap + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/doc/source/intro.rst b/doc/source/intro.rst new file mode 100644 index 00000000..c96766fb --- /dev/null +++ b/doc/source/intro.rst @@ -0,0 +1,112 @@ +.. _intro_toplevel: + +================== +Overview / Install +================== + +GitPython is a python library used to interact with git repositories, high-level like git-porcelain, or low-level like git-plumbing. + +It provides abstractions of git objects for easy access of repository data, and additionally allows you to access the git repository more directly using either a pure python implementation, or the faster, but more resource intensive git command implementation. + +The object database implementation is optimized for handling large quantities of objects and large datasets, which is achieved by using low-level structures and data streaming. + +Requirements +============ + +* `Git`_ 1.7.0 or newer + It should also work with older versions, but it may be that some operations + involving remotes will not work as expected. +* `GitDB`_ - a pure python git database implementation + + * `async`_ - asynchronous task scheduling + +* `Python Nose`_ - used for running the tests +* `Mock by Michael Foord`_ used for tests. Requires version 0.5 + +.. _Git: http://git-scm.com/ +.. _Python Nose: http://code.google.com/p/python-nose/ +.. _Mock by Michael Foord: http://www.voidspace.org.uk/python/mock.html +.. _GitDB: http://pypi.python.org/pypi/gitdb +.. _async: http://pypi.python.org/pypi/async + +Installing GitPython +==================== + +Installing GitPython is easily done using +`setuptools`_. Assuming it is +installed, just run the following from the command-line: + +.. sourcecode:: none + + # easy_install GitPython + +This command will download the latest version of GitPython from the +`Python Package Index `_ and install it +to your system. More information about ``easy_install`` and pypi can be found +here: + +* `setuptools`_ +* `install setuptools `_ +* `pypi `_ + +.. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools + +Alternatively, you can install from the distribution using the ``setup.py`` +script: + +.. sourcecode:: none + + # python setup.py install + +.. note:: In this case, you have to manually install `GitDB`_ and `async`_ as well. It would be recommended to use the :ref:`git source repository ` in that case. + +Getting Started +=============== + +* :ref:`tutorial-label` - This tutorial provides a walk-through of some of + the basic functionality and concepts used in GitPython. It, however, is not + exhaustive so you are encouraged to spend some time in the + :ref:`api_reference_toplevel`. + +API Reference +============= + +An organized section of the GitPthon API is at :ref:`api_reference_toplevel`. + +.. _source-code-label: + +Source Code +=========== + +GitPython's git repo is available on Gitorious and GitHub, which can be browsed at: + + * http://gitorious.org/projects/git-python/ + * http://github.com/Byron/GitPython + +and cloned using:: + + $ git clone git://gitorious.org/git-python/mainline.git git-python + $ git clone git://github.com/Byron/GitPython.git git-python + +Initialize all submodules to obtain the required dependencies with:: + + $ cd git-python + $ git submodule update --init --recursive + +Finally verify the installation by running the `nose powered `_ unit tests:: + + $ nosetests + +Mailing List +============ +http://groups.google.com/group/git-python + +Issue Tracker +============= +http://byronimo.lighthouseapp.com/projects/51787-gitpython/milestones + +License Information +=================== +GitPython is licensed under the New BSD License. See the LICENSE file for +more information. + diff --git a/doc/source/reference.rst b/doc/source/reference.rst new file mode 100644 index 00000000..5952fe2d --- /dev/null +++ b/doc/source/reference.rst @@ -0,0 +1,146 @@ +.. _api_reference_toplevel: + +API Reference +============= + +Objects.Base +------------ + +.. automodule:: git.objects.base + :members: + :undoc-members: + +Objects.Blob +------------ + +.. automodule:: git.objects.blob + :members: + :undoc-members: + +Objects.Commit +-------------- + +.. automodule:: git.objects.commit + :members: + :undoc-members: + +Objects.Tag +----------- + +.. automodule:: git.objects.tag + :members: + :undoc-members: + +Objects.Tree +------------ + +.. automodule:: git.objects.tree + :members: + :undoc-members: + +Objects.Functions +----------------- + +.. automodule:: git.objects.fun + :members: + :undoc-members: + +Objects.Submodule +----------------- + +.. automodule:: git.objects.submodule + :members: + :undoc-members: + +Objects.Util +------------- + +.. automodule:: git.objects.util + :members: + :undoc-members: + +Index.Base +---------- + +.. automodule:: git.index.base + :members: + :undoc-members: + +Index.Functions +--------------- + +.. automodule:: git.index.fun + :members: + :undoc-members: + +Index.Types +----------- + +.. automodule:: git.index.typ + :members: + :undoc-members: + +Index.Util +------------- + +.. automodule:: git.index.util + :members: + :undoc-members: + +GitCmd +------ + +.. automodule:: git.cmd + :members: + :undoc-members: + + +Config +------ + +.. automodule:: git.config + :members: + :undoc-members: + +Diff +---- + +.. automodule:: git.diff + :members: + :undoc-members: + +Errors +------ + +.. automodule:: git.errors + :members: + :undoc-members: + + +Refs +---- + +.. automodule:: git.refs + :members: + :undoc-members: + +Remote +------ + +.. automodule:: git.remote + :members: + :undoc-members: + +Repo +---- + +.. automodule:: git.repo + :members: + :undoc-members: + +Util +---- + +.. automodule:: git.util + :members: + :undoc-members: diff --git a/doc/source/roadmap.rst b/doc/source/roadmap.rst new file mode 100644 index 00000000..a6bdc3a0 --- /dev/null +++ b/doc/source/roadmap.rst @@ -0,0 +1,6 @@ + +####### +Roadmap +####### +The full list of milestones including associated tasks can be found on lighthouse: http://byronimo.lighthouseapp.com/projects/51787-gitpython/milestones + diff --git a/doc/source/tutorial.rst b/doc/source/tutorial.rst new file mode 100644 index 00000000..033bbad4 --- /dev/null +++ b/doc/source/tutorial.rst @@ -0,0 +1,374 @@ +.. _tutorial_toplevel: + +.. highlight:: python + +.. _tutorial-label: + +================== +GitPython Tutorial +================== + +GitPython provides object model access to your git repository. This tutorial is composed of multiple sections, each of which explains a real-life usecase. + +Initialize a Repo object +************************ + +The first step is to create a ``Repo`` object to represent your repository:: + + from git import * + repo = Repo("/Users/mtrier/Development/git-python") + assert repo.bare == False + +In the above example, the directory ``/Users/mtrier/Development/git-python`` is my working repository and contains the ``.git`` directory. You can also initialize GitPython with a *bare* repository:: + + repo = Repo.create("/var/git/git-python.git") + assert repo.bare == True + +A repo object provides high-level access to your data, it allows you to create and delete heads, tags and remotes and access the configuration of the repository:: + + repo.config_reader() # get a config reader for read-only access + repo.config_writer() # get a config writer to change configuration + +Query the active branch, query untracked files or whether the repository data has been modified:: + + repo.is_dirty() + False + repo.untracked_files() + ['my_untracked_file'] + +Clone from existing repositories or initialize new empty ones:: + + cloned_repo = repo.clone("to/this/path") + new_repo = repo.init("path/for/new/repo") + +Archive the repository contents to a tar file:: + + repo.archive(open("repo.tar",'w')) + + +Object Databases +**************** +``Repo`` instances are powered by its object database instance which will be used when extracting any data, or when writing new objects. + +The type of the database determines certain performance characteristics, such as the quantity of objects that can be read per second, the resource usage when reading large data files, as well as the average memory footprint of your application. + +GitDB +===== +The GitDB is a pure-python implementation of the git object database. It is the default database to use in GitPython 0.3. Its uses less memory when handling huge files, but will be 2 to 5 times slower when extracting large quantities small of objects from densely packed repositories:: + + repo = Repo("path/to/repo", odbt=GitDB) + +GitCmdObjectDB +============== +The git command database uses persistent git-cat-file instances to read repository information. These operate very fast under all conditions, but will consume additional memory for the process itself. When extracting large files, memory usage will be much higher than the one of the ``GitDB``:: + + repo = Repo("path/to/repo", odbt=GitCmdObjectDB) + +Examining References +******************** + +References are the tips of your commit graph from which you can easily examine the history of your project:: + + heads = repo.heads + master = heads.master # lists can be accessed by name for convenience + master.commit # the commit pointed to by head called master + master.rename("new_name") # rename heads + +Tags are (usually immutable) references to a commit and/or a tag object:: + + tags = repo.tags + tagref = tags[0] + tagref.tag # tags may have tag objects carrying additional information + tagref.commit # but they always point to commits + repo.delete_tag(tagref) # delete or + repo.create_tag("my_tag") # create tags using the repo for convenience + +A symbolic reference is a special case of a reference as it points to another reference instead of a commit:: + + head = repo.head # the head points to the active branch/ref + master = head.reference # retrieve the reference the head points to + master.commit # from here you use it as any other reference + +Modifying References +******************** +You can easily create and delete reference types or modify where they point to:: + + repo.delete_head('master') # delete an existing head + master = repo.create_head('master') # create a new one + master.commit = 'HEAD~10' # set branch to another commit without changing index or working tree + +Create or delete tags the same way except you may not change them afterwards:: + + new_tag = repo.create_tag('my_tag', 'my message') + repo.delete_tag(new_tag) + +Change the symbolic reference to switch branches cheaply ( without adjusting the index or the working copy ):: + + new_branch = repo.create_head('new_branch') + repo.head.reference = new_branch + +Understanding Objects +********************* +An Object is anything storable in git's object database. Objects contain information about their type, their uncompressed size as well as the actual data. Each object is uniquely identified by a binary SHA1 hash, being 20 bytes in size. + +Git only knows 4 distinct object types being Blobs, Trees, Commits and Tags. + +In Git-Python, all objects can be accessed through their common base, compared and hashed. They are usually not instantiated directly, but through references or specialized repository functions:: + + hc = repo.head.commit + hct = hc.tree + hc != hct + hc != repo.tags[0] + hc == repo.head.reference.commit + +Common fields are:: + + hct.type + 'tree' + hct.size + 166 + hct.hexsha + 'a95eeb2a7082212c197cabbf2539185ec74ed0e8' + hct.binsha + 'binary 20 byte sha1' + +Index Objects are objects that can be put into git's index. These objects are trees, blobs and submodules which additionally know about their path in the filesystem as well as their mode:: + + hct.path # root tree has no path + '' + hct.trees[0].path # the first subdirectory has one though + 'dir' + htc.mode # trees have the mode of a linux directory + 040000 + '%o' % htc.blobs[0].mode # blobs have a specific mode though comparable to a standard linux fs + 100644 + +Access blob data (or any object data) directly or using streams:: + + htc.blobs[0].data_stream.read() # stream object to read data from + htc.blobs[0].stream_data(open("blob_data", "w")) # write data to given stream + + +The Commit object +***************** + +Commit objects contain information about a specific commit. Obtain commits using references as done in `Examining References`_ or as follows. + +Obtain commits at the specified revision:: + + repo.commit('master') + repo.commit('v0.1') + repo.commit('HEAD~10') + +Iterate 100 commits:: + + repo.iter_commits('master', max_count=100) + +If you need paging, you can specify a number of commits to skip:: + + repo.iter_commits('master', max_count=10, skip=20) + +The above will return commits 21-30 from the commit list.:: + + headcommit = repo.head.commit + + headcommit.hexsha + '207c0c4418115df0d30820ab1a9acd2ea4bf4431' + + headcommit.parents + (,) + + headcommit.tree + + + headcommit.author + "> + + headcommit.authored_date # seconds since epoch + 1256291446 + + headcommit.committer + "> + + headcommit.committed_date + 1256291446 + + headcommit.message + 'cleaned up a lot of test information. Fixed escaping so it works with + subprocess.' + +Note: date time is represented in a ``seconds since epoch`` format. Conversion to human readable form can be accomplished with the various `time module `_ methods:: + + import time + time.asctime(time.gmtime(headcommit.committed_date)) + 'Wed May 7 05:56:02 2008' + + time.strftime("%a, %d %b %Y %H:%M", time.gmtime(headcommit.committed_date)) + 'Wed, 7 May 2008 05:56' + +You can traverse a commit's ancestry by chaining calls to ``parents``:: + + headcommit.parents[0].parents[0].parents[0] + +The above corresponds to ``master^^^`` or ``master~3`` in git parlance. + +The Tree object +*************** + +A tree records pointers to the contents of a directory. Let's say you want the root tree of the latest commit on the master branch:: + + tree = repo.heads.master.commit.tree + + + tree.hexsha + 'a006b5b1a8115185a228b7514cdcd46fed90dc92' + +Once you have a tree, you can get the contents:: + + tree.trees # trees are subdirectories + [] + + tree.blobs # blobs are files + [, + , + , + ] + +Its useful to know that a tree behaves like a list with the ability to query entries by name:: + + tree[0] == tree['dir'] # access by index and by sub-path + + for entry in tree: do_something_with(entry) + + blob = tree[0][0] + blob.name + 'file' + blob.path + 'dir/file' + blob.abspath + '/Users/mtrier/Development/git-python/dir/file' + >>>tree['dir/file'].binsha == blob.binsha + +There is a convenience method that allows you to get a named sub-object from a tree with a syntax similar to how paths are written in an unix system:: + + tree/"lib" + + tree/"dir/file" == blob + +You can also get a tree directly from the repository if you know its name:: + + repo.tree() + + + repo.tree("c1c7214dde86f76bc3e18806ac1f47c38b2b7a30") + + repo.tree('0.1.6') + + +As trees only allow direct access to their direct entries, use the traverse method to obtain an iterator to traverse entries recursively:: + + tree.traverse() + + for entry in tree.traverse(): do_something_with(entry) + + +The Index Object +**************** +The git index is the stage containing changes to be written with the next commit or where merges finally have to take place. You may freely access and manipulate this information using the IndexFile Object:: + + index = repo.index + +Access objects and add/remove entries. Commit the changes:: + + for stage, blob in index.iter_blobs(): do_something(...) + # Access blob objects + for (path, stage), entry in index.entries.iteritems: pass + # Access the entries directly + index.add(['my_new_file']) # add a new file to the index + index.remove(['dir/existing_file']) + new_commit = index.commit("my commit message") + +Create new indices from other trees or as result of a merge. Write that result to a new index file:: + + tmp_index = Index.from_tree(repo, 'HEAD~1') # load a tree into a temporary index + merge_index = Index.from_tree(repo, 'base', 'HEAD', 'some_branch') # merge two trees three-way + merge_index.write("merged_index") + +Handling Remotes +**************** + +Remotes are used as alias for a foreign repository to ease pushing to and fetching from them:: + + test_remote = repo.create_remote('test', 'git@server:repo.git') + repo.delete_remote(test_remote) # create and delete remotes + origin = repo.remotes.origin # get default remote by name + origin.refs # local remote references + o = origin.rename('new_origin') # rename remotes + o.fetch() # fetch, pull and push from and to the remote + o.pull() + o.push() + +You can easily access configuration information for a remote by accessing options as if they where attributes:: + + o.url + 'git@server:dummy_repo.git' + +Change configuration for a specific remote only:: + + o.config_writer.set("pushurl", "other_url") + +Obtaining Diff Information +************************** + +Diffs can generally be obtained by subclasses of ``Diffable`` as they provide the ``diff`` method. This operation yields a DiffIndex allowing you to easily access diff information about paths. + +Diffs can be made between the Index and Trees, Index and the working tree, trees and trees as well as trees and the working copy. If commits are involved, their tree will be used implicitly:: + + hcommit = repo.head.commit + idiff = hcommit.diff() # diff tree against index + tdiff = hcommit.diff('HEAD~1') # diff tree against previous tree + wdiff = hcommit.diff(None) # diff tree against working tree + + index = repo.index + index.diff() # diff index against itself yielding empty diff + index.diff(None) # diff index against working copy + index.diff('HEAD') # diff index against current HEAD tree + +The item returned is a DiffIndex which is essentially a list of Diff objects. It provides additional filtering to ease finding what you might be looking for:: + + for diff_added in wdiff.iter_change_type('A'): do_something_with(diff_added) + +Switching Branches +****************** +To switch between branches, you effectively need to point your HEAD to the new branch head and reset your index and working copy to match. A simple manual way to do it is the following one:: + + repo.head.reference = repo.heads.other_branch + repo.head.reset(index=True, working_tree=True) + +The previous approach would brutally overwrite the user's changes in the working copy and index though and is less sophisticated than a git-checkout for instance which generally prevents you from destroying your work. Use the safer approach as follows:: + + repo.heads.master.checkout() # checkout the branch using git-checkout + repo.heads.other_branch.checkout() + +Using git directly +****************** +In case you are missing functionality as it has not been wrapped, you may conveniently use the git command directly. It is owned by each repository instance:: + + git = repo.git + git.checkout('head', b="my_new_branch") # default command + git.for_each_ref() # '-' becomes '_' when calling it + +The return value will by default be a string of the standard output channel produced by the command. + +Keyword arguments translate to short and long keyword arguments on the commandline. +The special notion ``git.command(flag=True)`` will create a flag without value like ``command --flag``. + +If ``None`` is found in the arguments, it will be dropped silently. Lists and tuples passed as arguments will be unpacked recursively to individual arguments. Objects are converted to strings using the str(...) function. + +And even more ... +***************** + +There is more functionality in there, like the ability to archive repositories, get stats and logs, blame, and probably a few other things that were not mentioned here. + +Check the unit tests for an in-depth introduction on how each function is supposed to be used. + diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst new file mode 100644 index 00000000..7a5ef53d --- /dev/null +++ b/doc/source/whatsnew.rst @@ -0,0 +1,59 @@ + +################ +Whats New in 0.3 +################ +GitPython 0.3 is the first step in creating a hybrid which uses a pure python implementations for all simple git features which can be implemented without significant performance penalties. Everything else is still performed using the git command, which is nicely integrated and easy to use. + +Its biggest strength, being the support for all git features through the git command itself, is a weakness as well considering the possibly vast amount of times the git command is being started up. Depending on the actual command being performed, the git repository will be initialized on many of these invocations, causing additional overhead for possibly tiny operations. + +Keeping as many major operations in the python world will result in improved caching benefits as certain data structures just have to be initialized once and can be reused multiple times. This mode of operation may improve performance when altering the git database on a low level, and is clearly beneficial on operating systems where command invocations are very slow. + +**************** +Object Databases +**************** +An object database provides a simple interface to query object information or to write new object data. Objects are generally identified by their 20 byte binary sha1 value during query. + +GitPython uses the ``gitdb`` project to provide a pure-python implementation of the git database, which includes reading and writing loose objects, reading pack files and handling alternate repositories. + +The great thing about this is that ``Repo`` objects can use any object database, hence it easily supports different implementations with different performance characteristics. If you are thinking in extremes, you can implement your own database representation, which may be more efficient for what you want to do specifically, like handling big files more efficiently. + +************************ +Reduced Memory Footprint +************************ +Objects, such as commits, tags, trees and blobs now use 20 byte sha1 signatures internally, reducing their memory demands by 20 bytes per object, allowing you to keep more objects in memory at the same time. + +The internal caches of tree objects were improved to use less memory as well. + +################## +Upgrading from 0.2 +################## +GitPython 0.2 essentially behaves like GitPython 0.3 with a Repository using the ``GitCmdObjectDB`` instead of the ``GitDB`` as object database backend. Additionally it can be used more conveniently through implicit conversions and provides a feature set strikingly similar to 0.3. + +************************** +Why you should not upgrade +************************** +GitPython 0.3 in most cases will not run faster than GitPython 0.2, the opposite might be the case at it uses the pure python implementation by default. +There have been a few renames which will need additional adjustments in your code. + +Generally, if you only read git repositories, version 0.2 is sufficient and very well performing. + +********************** +Why you should upgrade +********************** +GitPython 0.2 has reached its end of line, and it is unlikely to receive more than contributed patches. 0.3 is the main development branch which will lead into the future. + +GitPython 0.3 provides memory usage optimization and is very flexible in the way it uses to access the object database. With minimal effort, 0.3 will be running as fast as 0.2. It marks the first step of more versions to come, and will improve over time. + +GitPython 0.3 is especially suitable for everyone who needs not only read, but also write access to a git repository. It is optimized to keep the memory consumption as low as possible, especially when handling large data sets. GitPython 0.3 operates on streams, not on possibly huge chunks of data. + + +************** +Guided Upgrade +************** +This guide should help to make the upgrade as painless as possible, hence it points out where to start, and what to look out for. + +* Have a look at the CHANGES log file and read all important changes about 0.3 for an overview. +* Start applying the renames, generally the ``utils`` modules are now called ``util``, ``errors`` is called ``exc``. +* Search for occurrences of the ``sha`` property of object instances. A similar value can be obtained through the new ``hexsha`` property. The native sha1 value is the ``binsha`` though. +* Search for code which instantiates objects directly. Their initializer now requires a 20 byte binary Sha1, rev-specs cannot be used anymore. For a similar effect, either convert your hexadecimal shas to binary shas beforehand ( ``binascii.unhexlify`` for instance ), or use higher level functions such as ``Object.new``, ``Repo.commit`` or ``Repo.tree``. The latter ones takes rev-specs and hexadecimal sha1 hashes. + diff --git a/doc/tutorial.rst b/doc/tutorial.rst deleted file mode 100644 index 033bbad4..00000000 --- a/doc/tutorial.rst +++ /dev/null @@ -1,374 +0,0 @@ -.. _tutorial_toplevel: - -.. highlight:: python - -.. _tutorial-label: - -================== -GitPython Tutorial -================== - -GitPython provides object model access to your git repository. This tutorial is composed of multiple sections, each of which explains a real-life usecase. - -Initialize a Repo object -************************ - -The first step is to create a ``Repo`` object to represent your repository:: - - from git import * - repo = Repo("/Users/mtrier/Development/git-python") - assert repo.bare == False - -In the above example, the directory ``/Users/mtrier/Development/git-python`` is my working repository and contains the ``.git`` directory. You can also initialize GitPython with a *bare* repository:: - - repo = Repo.create("/var/git/git-python.git") - assert repo.bare == True - -A repo object provides high-level access to your data, it allows you to create and delete heads, tags and remotes and access the configuration of the repository:: - - repo.config_reader() # get a config reader for read-only access - repo.config_writer() # get a config writer to change configuration - -Query the active branch, query untracked files or whether the repository data has been modified:: - - repo.is_dirty() - False - repo.untracked_files() - ['my_untracked_file'] - -Clone from existing repositories or initialize new empty ones:: - - cloned_repo = repo.clone("to/this/path") - new_repo = repo.init("path/for/new/repo") - -Archive the repository contents to a tar file:: - - repo.archive(open("repo.tar",'w')) - - -Object Databases -**************** -``Repo`` instances are powered by its object database instance which will be used when extracting any data, or when writing new objects. - -The type of the database determines certain performance characteristics, such as the quantity of objects that can be read per second, the resource usage when reading large data files, as well as the average memory footprint of your application. - -GitDB -===== -The GitDB is a pure-python implementation of the git object database. It is the default database to use in GitPython 0.3. Its uses less memory when handling huge files, but will be 2 to 5 times slower when extracting large quantities small of objects from densely packed repositories:: - - repo = Repo("path/to/repo", odbt=GitDB) - -GitCmdObjectDB -============== -The git command database uses persistent git-cat-file instances to read repository information. These operate very fast under all conditions, but will consume additional memory for the process itself. When extracting large files, memory usage will be much higher than the one of the ``GitDB``:: - - repo = Repo("path/to/repo", odbt=GitCmdObjectDB) - -Examining References -******************** - -References are the tips of your commit graph from which you can easily examine the history of your project:: - - heads = repo.heads - master = heads.master # lists can be accessed by name for convenience - master.commit # the commit pointed to by head called master - master.rename("new_name") # rename heads - -Tags are (usually immutable) references to a commit and/or a tag object:: - - tags = repo.tags - tagref = tags[0] - tagref.tag # tags may have tag objects carrying additional information - tagref.commit # but they always point to commits - repo.delete_tag(tagref) # delete or - repo.create_tag("my_tag") # create tags using the repo for convenience - -A symbolic reference is a special case of a reference as it points to another reference instead of a commit:: - - head = repo.head # the head points to the active branch/ref - master = head.reference # retrieve the reference the head points to - master.commit # from here you use it as any other reference - -Modifying References -******************** -You can easily create and delete reference types or modify where they point to:: - - repo.delete_head('master') # delete an existing head - master = repo.create_head('master') # create a new one - master.commit = 'HEAD~10' # set branch to another commit without changing index or working tree - -Create or delete tags the same way except you may not change them afterwards:: - - new_tag = repo.create_tag('my_tag', 'my message') - repo.delete_tag(new_tag) - -Change the symbolic reference to switch branches cheaply ( without adjusting the index or the working copy ):: - - new_branch = repo.create_head('new_branch') - repo.head.reference = new_branch - -Understanding Objects -********************* -An Object is anything storable in git's object database. Objects contain information about their type, their uncompressed size as well as the actual data. Each object is uniquely identified by a binary SHA1 hash, being 20 bytes in size. - -Git only knows 4 distinct object types being Blobs, Trees, Commits and Tags. - -In Git-Python, all objects can be accessed through their common base, compared and hashed. They are usually not instantiated directly, but through references or specialized repository functions:: - - hc = repo.head.commit - hct = hc.tree - hc != hct - hc != repo.tags[0] - hc == repo.head.reference.commit - -Common fields are:: - - hct.type - 'tree' - hct.size - 166 - hct.hexsha - 'a95eeb2a7082212c197cabbf2539185ec74ed0e8' - hct.binsha - 'binary 20 byte sha1' - -Index Objects are objects that can be put into git's index. These objects are trees, blobs and submodules which additionally know about their path in the filesystem as well as their mode:: - - hct.path # root tree has no path - '' - hct.trees[0].path # the first subdirectory has one though - 'dir' - htc.mode # trees have the mode of a linux directory - 040000 - '%o' % htc.blobs[0].mode # blobs have a specific mode though comparable to a standard linux fs - 100644 - -Access blob data (or any object data) directly or using streams:: - - htc.blobs[0].data_stream.read() # stream object to read data from - htc.blobs[0].stream_data(open("blob_data", "w")) # write data to given stream - - -The Commit object -***************** - -Commit objects contain information about a specific commit. Obtain commits using references as done in `Examining References`_ or as follows. - -Obtain commits at the specified revision:: - - repo.commit('master') - repo.commit('v0.1') - repo.commit('HEAD~10') - -Iterate 100 commits:: - - repo.iter_commits('master', max_count=100) - -If you need paging, you can specify a number of commits to skip:: - - repo.iter_commits('master', max_count=10, skip=20) - -The above will return commits 21-30 from the commit list.:: - - headcommit = repo.head.commit - - headcommit.hexsha - '207c0c4418115df0d30820ab1a9acd2ea4bf4431' - - headcommit.parents - (,) - - headcommit.tree - - - headcommit.author - "> - - headcommit.authored_date # seconds since epoch - 1256291446 - - headcommit.committer - "> - - headcommit.committed_date - 1256291446 - - headcommit.message - 'cleaned up a lot of test information. Fixed escaping so it works with - subprocess.' - -Note: date time is represented in a ``seconds since epoch`` format. Conversion to human readable form can be accomplished with the various `time module `_ methods:: - - import time - time.asctime(time.gmtime(headcommit.committed_date)) - 'Wed May 7 05:56:02 2008' - - time.strftime("%a, %d %b %Y %H:%M", time.gmtime(headcommit.committed_date)) - 'Wed, 7 May 2008 05:56' - -You can traverse a commit's ancestry by chaining calls to ``parents``:: - - headcommit.parents[0].parents[0].parents[0] - -The above corresponds to ``master^^^`` or ``master~3`` in git parlance. - -The Tree object -*************** - -A tree records pointers to the contents of a directory. Let's say you want the root tree of the latest commit on the master branch:: - - tree = repo.heads.master.commit.tree - - - tree.hexsha - 'a006b5b1a8115185a228b7514cdcd46fed90dc92' - -Once you have a tree, you can get the contents:: - - tree.trees # trees are subdirectories - [] - - tree.blobs # blobs are files - [, - , - , - ] - -Its useful to know that a tree behaves like a list with the ability to query entries by name:: - - tree[0] == tree['dir'] # access by index and by sub-path - - for entry in tree: do_something_with(entry) - - blob = tree[0][0] - blob.name - 'file' - blob.path - 'dir/file' - blob.abspath - '/Users/mtrier/Development/git-python/dir/file' - >>>tree['dir/file'].binsha == blob.binsha - -There is a convenience method that allows you to get a named sub-object from a tree with a syntax similar to how paths are written in an unix system:: - - tree/"lib" - - tree/"dir/file" == blob - -You can also get a tree directly from the repository if you know its name:: - - repo.tree() - - - repo.tree("c1c7214dde86f76bc3e18806ac1f47c38b2b7a30") - - repo.tree('0.1.6') - - -As trees only allow direct access to their direct entries, use the traverse method to obtain an iterator to traverse entries recursively:: - - tree.traverse() - - for entry in tree.traverse(): do_something_with(entry) - - -The Index Object -**************** -The git index is the stage containing changes to be written with the next commit or where merges finally have to take place. You may freely access and manipulate this information using the IndexFile Object:: - - index = repo.index - -Access objects and add/remove entries. Commit the changes:: - - for stage, blob in index.iter_blobs(): do_something(...) - # Access blob objects - for (path, stage), entry in index.entries.iteritems: pass - # Access the entries directly - index.add(['my_new_file']) # add a new file to the index - index.remove(['dir/existing_file']) - new_commit = index.commit("my commit message") - -Create new indices from other trees or as result of a merge. Write that result to a new index file:: - - tmp_index = Index.from_tree(repo, 'HEAD~1') # load a tree into a temporary index - merge_index = Index.from_tree(repo, 'base', 'HEAD', 'some_branch') # merge two trees three-way - merge_index.write("merged_index") - -Handling Remotes -**************** - -Remotes are used as alias for a foreign repository to ease pushing to and fetching from them:: - - test_remote = repo.create_remote('test', 'git@server:repo.git') - repo.delete_remote(test_remote) # create and delete remotes - origin = repo.remotes.origin # get default remote by name - origin.refs # local remote references - o = origin.rename('new_origin') # rename remotes - o.fetch() # fetch, pull and push from and to the remote - o.pull() - o.push() - -You can easily access configuration information for a remote by accessing options as if they where attributes:: - - o.url - 'git@server:dummy_repo.git' - -Change configuration for a specific remote only:: - - o.config_writer.set("pushurl", "other_url") - -Obtaining Diff Information -************************** - -Diffs can generally be obtained by subclasses of ``Diffable`` as they provide the ``diff`` method. This operation yields a DiffIndex allowing you to easily access diff information about paths. - -Diffs can be made between the Index and Trees, Index and the working tree, trees and trees as well as trees and the working copy. If commits are involved, their tree will be used implicitly:: - - hcommit = repo.head.commit - idiff = hcommit.diff() # diff tree against index - tdiff = hcommit.diff('HEAD~1') # diff tree against previous tree - wdiff = hcommit.diff(None) # diff tree against working tree - - index = repo.index - index.diff() # diff index against itself yielding empty diff - index.diff(None) # diff index against working copy - index.diff('HEAD') # diff index against current HEAD tree - -The item returned is a DiffIndex which is essentially a list of Diff objects. It provides additional filtering to ease finding what you might be looking for:: - - for diff_added in wdiff.iter_change_type('A'): do_something_with(diff_added) - -Switching Branches -****************** -To switch between branches, you effectively need to point your HEAD to the new branch head and reset your index and working copy to match. A simple manual way to do it is the following one:: - - repo.head.reference = repo.heads.other_branch - repo.head.reset(index=True, working_tree=True) - -The previous approach would brutally overwrite the user's changes in the working copy and index though and is less sophisticated than a git-checkout for instance which generally prevents you from destroying your work. Use the safer approach as follows:: - - repo.heads.master.checkout() # checkout the branch using git-checkout - repo.heads.other_branch.checkout() - -Using git directly -****************** -In case you are missing functionality as it has not been wrapped, you may conveniently use the git command directly. It is owned by each repository instance:: - - git = repo.git - git.checkout('head', b="my_new_branch") # default command - git.for_each_ref() # '-' becomes '_' when calling it - -The return value will by default be a string of the standard output channel produced by the command. - -Keyword arguments translate to short and long keyword arguments on the commandline. -The special notion ``git.command(flag=True)`` will create a flag without value like ``command --flag``. - -If ``None`` is found in the arguments, it will be dropped silently. Lists and tuples passed as arguments will be unpacked recursively to individual arguments. Objects are converted to strings using the str(...) function. - -And even more ... -***************** - -There is more functionality in there, like the ability to archive repositories, get stats and logs, blame, and probably a few other things that were not mentioned here. - -Check the unit tests for an in-depth introduction on how each function is supposed to be used. - diff --git a/doc/whatsnew.rst b/doc/whatsnew.rst deleted file mode 100644 index 9a49e0b9..00000000 --- a/doc/whatsnew.rst +++ /dev/null @@ -1,59 +0,0 @@ - -################ -Whats New in 0.3 -################ -GitPython 0.3 is the first step in creating a hybrid which uses a pure python implementations for all simple git features which can be implemented without significant performance penalties. Everything else is still performed using the git command, which is nicely integrated and easy to use. - -Its biggest strength, being the support for all git features through the git command itself, is a weakness as well considering the possibly vast amount of times the git command is being started up. Depending on the actual command being performaned, the git repository will be initialized on many of these invocations, causing additional overhead for possibly tiny operations. - -Keeping as many major operations in the python world will result in improved caching benefits as certain data structures just have to be initialized once and can be reused multiple times. This mode of operation may improve performance when altering the git database on a low level, and is clearly beneficial on operating systems where command invocations are very slow. - -**************** -Object Databases -**************** -An object database provides a simple interface to query object information or to write new object data. Objects are generally identified by their 20 byte binary sha1 value during query. - -GitPython uses the ``gitdb`` project to provide a pure-python implementation of the git database, which includes reading and writing loose objects, reading pack files and handling alternate repositories. - -The great thing about this is that ``Repo`` objects can use any object database, hence it easily supports different implementations with different performance characteristics. If you are thinking in extremes, you can implement your own database representation, which may be more efficient for what you want to do specifically, like handling big files more efficiently. - -************************ -Reduced Memory Footprint -************************ -Objects, such as commits, tags, trees and blobs now use 20 byte sha1 signatures internally, reducing their memory demands by 20 bytes per object, allowing you to keep more objects in memory at the same time. - -The internal caches of tree objects were improved to use less memory as well. - -################## -Upgrading from 0.2 -################## -GitPython 0.2 essentially behaves like GitPython 0.3 with a Repository using the ``GitCmdObjectDB`` instead of the ``GitDB`` as object database backend. Additionally it can be used more conveniently through implicit conversions and provides a feature set strikingly similar to 0.3. - -************************** -Why you should not upgrade -************************** -GitPython 0.3 in most cases will not run faster than GitPython 0.2, the opposite might be the case at it uses the pure python implementation by default. -There have been a few renames which will need additional adjustments by your code. - -Generally, if you only read git repositories, version 0.2 is sufficient and very well performing. - -********************** -Why you should upgrade -********************** -GitPython 0.2 has reached its end of line, and it is unlikely to receive more than contributed patches. 0.3 is the main development branch which will lead into the future. - -GitPython 0.3 provides memory usage optimization and is very flexible in the way it uses to access the object database. With minimal effort, 0.3 will be running as fast as 0.2. It marks the first step of more versions to come, and will improve over time. - -GitPython 0.3 is especially suitable for everyone who needs not only read, but also write access to a git repository. It is optimized to keep the memory consumption as low as possible, especially when handling large data sets. GitPython 0.3 operates on streams, not on possibly huge chunks of data. - - -************** -Guided Upgrade -************** -This guide should help to make the upgrade as painless as possible, hence it points out where to start, and what to look out for. - -* Have a look at the CHANGES log file and read all important changes about 0.3 for an overview. -* Start applying the renames, generally the ``utils`` modules are now called ``util``, ``errors`` is called ``exc``. -* Search for occurrences of the ``sha`` property of object instances. A similar value can be obtained through the new ``hexsha`` property. The native sha1 value is the ``binsha`` though. -* Search for code which instantiates objects directly. Their initializer now requires a 20 byte binary Sha1, rev-specs cannot be used anymore. For a similar effect, either convert your hexadecimal shas to binary shas beforehand ( ``binascii.unhexlify`` for instance ), or use higher level functions such as ``Object.new``, ``Repo.commit`` or ``Repo.tree``. The latter ones takes rev-specs and hexadecimal sha1 hashes. - -- cgit v1.2.3 From c3bd05b426a0e3dec8224244c3c9c0431d1ff130 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 1 Jul 2010 16:35:11 +0200 Subject: Added doc-index, which helps to keep documentation of prior but still somewhat supported versions alive --- doc/doc_index/0.1/_sources/index.txt | 23 + doc/doc_index/0.1/_sources/intro.txt | 83 +++ doc/doc_index/0.1/_sources/reference.txt | 95 +++ doc/doc_index/0.1/_sources/tutorial.txt | 211 ++++++ doc/doc_index/0.1/_static/basic.css | 417 ++++++++++++ doc/doc_index/0.1/_static/default.css | 230 +++++++ doc/doc_index/0.1/_static/doctools.js | 232 +++++++ doc/doc_index/0.1/_static/file.png | Bin 0 -> 392 bytes doc/doc_index/0.1/_static/jquery.js | 32 + doc/doc_index/0.1/_static/minus.png | Bin 0 -> 199 bytes doc/doc_index/0.1/_static/plus.png | Bin 0 -> 199 bytes doc/doc_index/0.1/_static/pygments.css | 61 ++ doc/doc_index/0.1/_static/searchtools.js | 467 +++++++++++++ doc/doc_index/0.1/genindex.html | 293 ++++++++ doc/doc_index/0.1/index.html | 155 +++++ doc/doc_index/0.1/intro.html | 184 +++++ doc/doc_index/0.1/modindex.html | 152 +++++ doc/doc_index/0.1/objects.inv | 89 +++ doc/doc_index/0.1/reference.html | 1073 ++++++++++++++++++++++++++++++ doc/doc_index/0.1/search.html | 97 +++ doc/doc_index/0.1/searchindex.js | 1 + doc/doc_index/0.1/tutorial.html | 352 ++++++++++ doc/doc_index/0.2/_sources/index.txt | 22 + doc/doc_index/0.2/_sources/intro.txt | 93 +++ doc/doc_index/0.2/_sources/reference.txt | 125 ++++ doc/doc_index/0.2/_sources/roadmap.txt | 6 + doc/doc_index/0.2/_sources/tutorial.txt | 357 ++++++++++ doc/doc_index/0.2/_static/basic.css | 417 ++++++++++++ doc/doc_index/0.2/_static/default.css | 230 +++++++ doc/doc_index/0.2/_static/doctools.js | 232 +++++++ doc/doc_index/0.2/_static/file.png | Bin 0 -> 392 bytes doc/doc_index/0.2/_static/jquery.js | 32 + doc/doc_index/0.2/_static/minus.png | Bin 0 -> 199 bytes doc/doc_index/0.2/_static/plus.png | Bin 0 -> 199 bytes doc/doc_index/0.2/_static/pygments.css | 61 ++ doc/doc_index/0.2/_static/searchtools.js | 467 +++++++++++++ doc/doc_index/0.2/genindex.html | 89 +++ doc/doc_index/0.2/index.html | 161 +++++ doc/doc_index/0.2/intro.html | 196 ++++++ doc/doc_index/0.2/objects.inv | 3 + doc/doc_index/0.2/reference.html | 181 +++++ doc/doc_index/0.2/roadmap.html | 97 +++ doc/doc_index/0.2/search.html | 91 +++ doc/doc_index/0.2/searchindex.js | 1 + doc/doc_index/0.2/tutorial.html | 454 +++++++++++++ doc/doc_index/index.html | 16 + 46 files changed, 7578 insertions(+) create mode 100644 doc/doc_index/0.1/_sources/index.txt create mode 100644 doc/doc_index/0.1/_sources/intro.txt create mode 100644 doc/doc_index/0.1/_sources/reference.txt create mode 100644 doc/doc_index/0.1/_sources/tutorial.txt create mode 100644 doc/doc_index/0.1/_static/basic.css create mode 100644 doc/doc_index/0.1/_static/default.css create mode 100644 doc/doc_index/0.1/_static/doctools.js create mode 100644 doc/doc_index/0.1/_static/file.png create mode 100644 doc/doc_index/0.1/_static/jquery.js create mode 100644 doc/doc_index/0.1/_static/minus.png create mode 100644 doc/doc_index/0.1/_static/plus.png create mode 100644 doc/doc_index/0.1/_static/pygments.css create mode 100644 doc/doc_index/0.1/_static/searchtools.js create mode 100644 doc/doc_index/0.1/genindex.html create mode 100644 doc/doc_index/0.1/index.html create mode 100644 doc/doc_index/0.1/intro.html create mode 100644 doc/doc_index/0.1/modindex.html create mode 100644 doc/doc_index/0.1/objects.inv create mode 100644 doc/doc_index/0.1/reference.html create mode 100644 doc/doc_index/0.1/search.html create mode 100644 doc/doc_index/0.1/searchindex.js create mode 100644 doc/doc_index/0.1/tutorial.html create mode 100644 doc/doc_index/0.2/_sources/index.txt create mode 100644 doc/doc_index/0.2/_sources/intro.txt create mode 100644 doc/doc_index/0.2/_sources/reference.txt create mode 100644 doc/doc_index/0.2/_sources/roadmap.txt create mode 100644 doc/doc_index/0.2/_sources/tutorial.txt create mode 100644 doc/doc_index/0.2/_static/basic.css create mode 100644 doc/doc_index/0.2/_static/default.css create mode 100644 doc/doc_index/0.2/_static/doctools.js create mode 100644 doc/doc_index/0.2/_static/file.png create mode 100644 doc/doc_index/0.2/_static/jquery.js create mode 100644 doc/doc_index/0.2/_static/minus.png create mode 100644 doc/doc_index/0.2/_static/plus.png create mode 100644 doc/doc_index/0.2/_static/pygments.css create mode 100644 doc/doc_index/0.2/_static/searchtools.js create mode 100644 doc/doc_index/0.2/genindex.html create mode 100644 doc/doc_index/0.2/index.html create mode 100644 doc/doc_index/0.2/intro.html create mode 100644 doc/doc_index/0.2/objects.inv create mode 100644 doc/doc_index/0.2/reference.html create mode 100644 doc/doc_index/0.2/roadmap.html create mode 100644 doc/doc_index/0.2/search.html create mode 100644 doc/doc_index/0.2/searchindex.js create mode 100644 doc/doc_index/0.2/tutorial.html create mode 100644 doc/doc_index/index.html diff --git a/doc/doc_index/0.1/_sources/index.txt b/doc/doc_index/0.1/_sources/index.txt new file mode 100644 index 00000000..19191272 --- /dev/null +++ b/doc/doc_index/0.1/_sources/index.txt @@ -0,0 +1,23 @@ +.. GitPython documentation master file, created by sphinx-quickstart on Sat Jan 24 11:51:01 2009. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +GitPython Documentation +======================= + +Contents: + +.. toctree:: + :maxdepth: 3 + + intro + tutorial + reference + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/doc/doc_index/0.1/_sources/intro.txt b/doc/doc_index/0.1/_sources/intro.txt new file mode 100644 index 00000000..c99e5a4c --- /dev/null +++ b/doc/doc_index/0.1/_sources/intro.txt @@ -0,0 +1,83 @@ +.. _intro_toplevel: + +================== +Overview / Install +================== + +GitPython is a python library used to interact with Git repositories. + +GitPython is a port of the grit_ library in Ruby created by +Tom Preston-Werner and Chris Wanstrath. + +.. _grit: http://grit.rubyforge.org + +Requirements +============ + +* Git_ tested with 1.5.3.7 +* `Python Nose`_ - used for running the tests +* `Mock by Michael Foord`_ used for tests. Requires 0.5 or higher + +.. _Git: http://git-scm.com/ +.. _Python Nose: http://code.google.com/p/python-nose/ +.. _Mock by Michael Foord: http://www.voidspace.org.uk/python/mock/ + +Installing GitPython +==================== + +Installing GitPython is easily done using +`setuptools`_. Assuming it is +installed, just run the following from the command-line: + +.. sourcecode:: none + + # easy_install GitPython + +This command will download the latest version of GitPython from the +`Python Package Index `_ and install it +to your system. More information about ``easy_install`` and pypi can be found +here: + +* `setuptools`_ +* `install setuptools `_ +* `pypi `_ + +.. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools + +Alternatively, you can install from the distribution using the ``setup.py`` +script: + +.. sourcecode:: none + + # python setup.py install + +Getting Started +=============== + +* :ref:`tutorial_toplevel` - This tutorial provides a walk-through of some of + the basic functionality and concepts used in GitPython. It, however, is not + exhaustive so you are encouraged to spend some time in the + :ref:`api_reference_toplevel`. + +API Reference +============= + +An organized section of the GitPthon API is at :ref:`api_reference_toplevel`. + +Source Code +=========== + +GitPython's git repo is available on Gitorious, which can be browsed at: + +http://gitorious.org/git-python + +and cloned from: + +git://gitorious.org/git-python/mainline.git + +License Information +=================== + +GitPython is licensed under the New BSD License. See the LICENSE file for +more information. + diff --git a/doc/doc_index/0.1/_sources/reference.txt b/doc/doc_index/0.1/_sources/reference.txt new file mode 100644 index 00000000..078cbdf6 --- /dev/null +++ b/doc/doc_index/0.1/_sources/reference.txt @@ -0,0 +1,95 @@ +.. _api_reference_toplevel: + +API Reference +============= + +Actor +----- + +.. automodule:: git.actor + :members: + :undoc-members: + +Blob +---- + +.. automodule:: git.blob + :members: + :undoc-members: + +Git +--- + +.. automodule:: git.cmd + :members: + :undoc-members: + +Commit +------ + +.. automodule:: git.commit + :members: + :undoc-members: + +Diff +---- + +.. automodule:: git.diff + :members: + :undoc-members: + +Errors +------ + +.. automodule:: git.errors + :members: + :undoc-members: + +Head +---- + +.. automodule:: git.head + :members: + :undoc-members: + +Lazy +---- + +.. automodule:: git.lazy + :members: + :undoc-members: + +Repo +---- + +.. automodule:: git.repo + :members: + :undoc-members: + +Stats +----- + +.. automodule:: git.stats + :members: + :undoc-members: + +Tag +--- + +.. automodule:: git.tag + :members: + :undoc-members: + +Tree +---- + +.. automodule:: git.tree + :members: + :undoc-members: + +Utils +----- + +.. automodule:: git.utils + :members: + :undoc-members: diff --git a/doc/doc_index/0.1/_sources/tutorial.txt b/doc/doc_index/0.1/_sources/tutorial.txt new file mode 100644 index 00000000..838fd68e --- /dev/null +++ b/doc/doc_index/0.1/_sources/tutorial.txt @@ -0,0 +1,211 @@ +.. _tutorial_toplevel: + +================== +GitPython Tutorial +================== + +GitPython provides object model access to your git repository. Once you have +created a repository object, you can traverse it to find parent commit(s), +trees, blobs, etc. + +Initialize a Repo object +************************ + +The first step is to create a ``Repo`` object to represent your repository. + + >>> from git import * + >>> repo = Repo("/Users/mtrier/Development/git-python") + +In the above example, the directory ``/Users/mtrier/Development/git-python`` +is my working repository and contains the ``.git`` directory. You can also +initialize GitPython with a bare repository. + + >>> repo = Repo.create("/var/git/git-python.git") + +Getting a list of commits +************************* + +From the ``Repo`` object, you can get a list of ``Commit`` +objects. + + >>> repo.commits() + [, + , + , + ] + +Called without arguments, ``Repo.commits`` returns a list of up to ten commits +reachable by the master branch (starting at the latest commit). You can ask +for commits beginning at a different branch, commit, tag, etc. + + >>> repo.commits('mybranch') + >>> repo.commits('40d3057d09a7a4d61059bca9dca5ae698de58cbe') + >>> repo.commits('v0.1') + +You can specify the maximum number of commits to return. + + >>> repo.commits('master', max_count=100) + +If you need paging, you can specify a number of commits to skip. + + >>> repo.commits('master', max_count=10, skip=20) + +The above will return commits 21-30 from the commit list. + +The Commit object +***************** + +Commit objects contain information about a specific commit. + + >>> head = repo.commits()[0] + + >>> head.id + '207c0c4418115df0d30820ab1a9acd2ea4bf4431' + + >>> head.parents + [] + + >>> head.tree + + + >>> head.author + "> + + >>> head.authored_date + (2008, 5, 7, 5, 0, 56, 2, 128, 0) + + >>> head.committer + "> + + >>> head.committed_date + (2008, 5, 7, 5, 0, 56, 2, 128, 0) + + >>> head.message + 'cleaned up a lot of test information. Fixed escaping so it works with + subprocess.' + +Note: date time is represented in a `struct_time`_ format. Conversion to +human readable form can be accomplished with the various time module methods. + + >>> import time + >>> time.asctime(head.committed_date) + 'Wed May 7 05:56:02 2008' + + >>> time.strftime("%a, %d %b %Y %H:%M", head.committed_date) + 'Wed, 7 May 2008 05:56' + +.. _struct_time: http://docs.python.org/library/time.html + +You can traverse a commit's ancestry by chaining calls to ``parents``. + + >>> repo.commits()[0].parents[0].parents[0].parents[0] + +The above corresponds to ``master^^^`` or ``master~3`` in git parlance. + +The Tree object +*************** + +A tree records pointers to the contents of a directory. Let's say you want +the root tree of the latest commit on the master branch. + + >>> tree = repo.commits()[0].tree + + + >>> tree.id + 'a006b5b1a8115185a228b7514cdcd46fed90dc92' + +Once you have a tree, you can get the contents. + + >>> contents = tree.values() + [, + , + , + ] + +The tree is implements a dictionary protocol so it can be used and acts just +like a dictionary with some additional properties. + + >>> tree.items() + [('lib', ), + ('LICENSE', ), + ('doc', ), + ('MANIFEST.in', ), + ('.gitignore', ), + ('test', ), + ('VERSION', ), + ('AUTHORS', ), + ('README', ), + ('ez_setup.py', ), + ('setup.py', ), + ('CHANGES', )] + +This tree contains three ``Blob`` objects and one ``Tree`` object. The trees +are subdirectories and the blobs are files. Trees below the root have +additional attributes. + + >>> contents = tree["lib"] + + + >>> contents.name + 'test' + + >>> contents.mode + '040000' + +There is a convenience method that allows you to get a named sub-object +from a tree with a syntax similar to how paths are written in an unix +system. + + >>> tree/"lib" + + +You can also get a tree directly from the repository if you know its name. + + >>> repo.tree() + + + >>> repo.tree("c1c7214dde86f76bc3e18806ac1f47c38b2b7a30") + + +The Blob object +*************** + +A blob represents a file. Trees often contain blobs. + + >>> blob = tree['urls.py'] + + +A blob has certain attributes. + + >>> blob.name + 'urls.py' + + >>> blob.mode + '100644' + + >>> blob.mime_type + 'text/x-python' + + >>> blob.size + 415 + +You can get the data of a blob as a string. + + >>> blob.data + "from django.conf.urls.defaults import *\nfrom django.conf..." + +You can also get a blob directly from the repo if you know its name. + + >>> repo.blob("b19574431a073333ea09346eafd64e7b1908ef49") + + +What Else? +********** + +There is more stuff in there, like the ability to tar or gzip repos, stats, +log, blame, and probably a few other things. Additionally calls to the git +instance are handled through a ``__getattr__`` construct, which makes +available any git commands directly, with a nice conversion of Python dicts +to command line parameters. + +Check the unit tests, they're pretty exhaustive. diff --git a/doc/doc_index/0.1/_static/basic.css b/doc/doc_index/0.1/_static/basic.css new file mode 100644 index 00000000..a04d6545 --- /dev/null +++ b/doc/doc_index/0.1/_static/basic.css @@ -0,0 +1,417 @@ +/** + * Sphinx stylesheet -- basic theme + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +img { + border: 0; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li div.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable dl, table.indextable dd { + margin-top: 0; + margin-bottom: 0; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +/* -- general body styles --------------------------------------------------- */ + +a.headerlink { + visibility: hidden; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.field-list ul { + padding-left: 1em; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px 7px 0 7px; + background-color: #ffe; + width: 40%; + float: right; +} + +p.sidebar-title { + font-weight: bold; +} + +/* -- topics ---------------------------------------------------------------- */ + +div.topic { + border: 1px solid #ccc; + padding: 7px 7px 0 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +div.admonition dl { + margin-bottom: 0; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + border: 0; + border-collapse: collapse; +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 0; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +table.field-list td, table.field-list th { + border: 0 !important; +} + +table.footnote td, table.footnote th { + border: 0 !important; +} + +th { + text-align: left; + padding-right: 5px; +} + +/* -- other body styles ----------------------------------------------------- */ + +dl { + margin-bottom: 15px; +} + +dd p { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +dt:target, .highlight { + background-color: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.refcount { + color: #060; +} + +.optional { + font-size: 1.3em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; +} + +td.linenos pre { + padding: 5px 0px; + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + margin-left: 0.5em; +} + +table.highlighttable td { + padding: 0 0.5em 0 0.5em; +} + +tt.descname { + background-color: transparent; + font-weight: bold; + font-size: 1.2em; +} + +tt.descclassname { + background-color: transparent; +} + +tt.xref, a tt { + background-color: transparent; + font-weight: bold; +} + +h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { + background-color: transparent; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} diff --git a/doc/doc_index/0.1/_static/default.css b/doc/doc_index/0.1/_static/default.css new file mode 100644 index 00000000..37257440 --- /dev/null +++ b/doc/doc_index/0.1/_static/default.css @@ -0,0 +1,230 @@ +/** + * Sphinx stylesheet -- default theme + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: sans-serif; + font-size: 100%; + background-color: #11303d; + color: #000; + margin: 0; + padding: 0; +} + +div.document { + background-color: #1c4e63; +} + +div.documentwrapper { + float: left; + width: 100%; +} + +div.bodywrapper { + margin: 0 0 0 230px; +} + +div.body { + background-color: #ffffff; + color: #000000; + padding: 0 20px 30px 20px; +} + +div.footer { + color: #ffffff; + width: 100%; + padding: 9px 0 9px 0; + text-align: center; + font-size: 75%; +} + +div.footer a { + color: #ffffff; + text-decoration: underline; +} + +div.related { + background-color: #133f52; + line-height: 30px; + color: #ffffff; +} + +div.related a { + color: #ffffff; +} + +div.sphinxsidebar { +} + +div.sphinxsidebar h3 { + font-family: 'Trebuchet MS', sans-serif; + color: #ffffff; + font-size: 1.4em; + font-weight: normal; + margin: 0; + padding: 0; +} + +div.sphinxsidebar h3 a { + color: #ffffff; +} + +div.sphinxsidebar h4 { + font-family: 'Trebuchet MS', sans-serif; + color: #ffffff; + font-size: 1.3em; + font-weight: normal; + margin: 5px 0 0 0; + padding: 0; +} + +div.sphinxsidebar p { + color: #ffffff; +} + +div.sphinxsidebar p.topless { + margin: 5px 10px 10px 10px; +} + +div.sphinxsidebar ul { + margin: 10px; + padding: 0; + color: #ffffff; +} + +div.sphinxsidebar a { + color: #98dbcc; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +/* -- body styles ----------------------------------------------------------- */ + +a { + color: #355f7c; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +div.body p, div.body dd, div.body li { + text-align: justify; + line-height: 130%; +} + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: 'Trebuchet MS', sans-serif; + background-color: #f2f2f2; + font-weight: normal; + color: #20435c; + border-bottom: 1px solid #ccc; + margin: 20px -20px 10px -20px; + padding: 3px 0 3px 10px; +} + +div.body h1 { margin-top: 0; font-size: 200%; } +div.body h2 { font-size: 160%; } +div.body h3 { font-size: 140%; } +div.body h4 { font-size: 120%; } +div.body h5 { font-size: 110%; } +div.body h6 { font-size: 100%; } + +a.headerlink { + color: #c60f0f; + font-size: 0.8em; + padding: 0 4px 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + background-color: #c60f0f; + color: white; +} + +div.body p, div.body dd, div.body li { + text-align: justify; + line-height: 130%; +} + +div.admonition p.admonition-title + p { + display: inline; +} + +div.admonition p { + margin-bottom: 5px; +} + +div.admonition pre { + margin-bottom: 5px; +} + +div.admonition ul, div.admonition ol { + margin-bottom: 5px; +} + +div.note { + background-color: #eee; + border: 1px solid #ccc; +} + +div.seealso { + background-color: #ffc; + border: 1px solid #ff6; +} + +div.topic { + background-color: #eee; +} + +div.warning { + background-color: #ffe4e4; + border: 1px solid #f66; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +pre { + padding: 5px; + background-color: #eeffcc; + color: #333333; + line-height: 120%; + border: 1px solid #ac9; + border-left: none; + border-right: none; +} + +tt { + background-color: #ecf0f3; + padding: 0 1px 0 1px; + font-size: 0.95em; +} + +.warning tt { + background: #efc2c2; +} + +.note tt { + background: #d6d6d6; +} \ No newline at end of file diff --git a/doc/doc_index/0.1/_static/doctools.js b/doc/doc_index/0.1/_static/doctools.js new file mode 100644 index 00000000..9447678c --- /dev/null +++ b/doc/doc_index/0.1/_static/doctools.js @@ -0,0 +1,232 @@ +/// XXX: make it cross browser + +/** + * make the code below compatible with browsers without + * an installed firebug like debugger + */ +if (!window.console || !console.firebug) { + var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", + "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"]; + window.console = {}; + for (var i = 0; i < names.length; ++i) + window.console[names[i]] = function() {} +} + +/** + * small helper function to urldecode strings + */ +jQuery.urldecode = function(x) { + return decodeURIComponent(x).replace(/\+/g, ' '); +} + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s == 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +} + +/** + * small function to check if an array contains + * a given item. + */ +jQuery.contains = function(arr, item) { + for (var i = 0; i < arr.length; i++) { + if (arr[i] == item) + return true; + } + return false; +} + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node) { + if (node.nodeType == 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && !jQuery.className.has(node.parentNode, className)) { + var span = document.createElement("span"); + span.className = className; + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this) + }); + } + } + return this.each(function() { + highlight(this); + }); +} + +/** + * Small JavaScript module for the documentation. + */ +var Documentation = { + + init : function() { + this.fixFirefoxAnchorBug(); + this.highlightSearchWords(); + this.initModIndex(); + }, + + /** + * i18n support + */ + TRANSLATIONS : {}, + PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; }, + LOCALE : 'unknown', + + // gettext and ngettext don't access this so that the functions + // can savely bound to a different name (_ = Documentation.gettext) + gettext : function(string) { + var translated = Documentation.TRANSLATIONS[string]; + if (typeof translated == 'undefined') + return string; + return (typeof translated == 'string') ? translated : translated[0]; + }, + + ngettext : function(singular, plural, n) { + var translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated == 'undefined') + return (n == 1) ? singular : plural; + return translated[Documentation.PLURALEXPR(n)]; + }, + + addTranslations : function(catalog) { + for (var key in catalog.messages) + this.TRANSLATIONS[key] = catalog.messages[key]; + this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); + this.LOCALE = catalog.locale; + }, + + /** + * add context elements like header anchor links + */ + addContextElements : function() { + $('div[id] > :header:first').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this headline')). + appendTo(this); + }); + $('dt[id]').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this definition')). + appendTo(this); + }); + }, + + /** + * workaround a firefox stupidity + */ + fixFirefoxAnchorBug : function() { + if (document.location.hash && $.browser.mozilla) + window.setTimeout(function() { + document.location.href += ''; + }, 10); + }, + + /** + * highlight the search words provided in the url in the text + */ + highlightSearchWords : function() { + var params = $.getQueryParameters(); + var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; + if (terms.length) { + var body = $('div.body'); + window.setTimeout(function() { + $.each(terms, function() { + body.highlightText(this.toLowerCase(), 'highlight'); + }); + }, 10); + $('') + .appendTo($('.sidebar .this-page-menu')); + } + }, + + /** + * init the modindex toggle buttons + */ + initModIndex : function() { + var togglers = $('img.toggler').click(function() { + var src = $(this).attr('src'); + var idnum = $(this).attr('id').substr(7); + console.log($('tr.cg-' + idnum).toggle()); + if (src.substr(-9) == 'minus.png') + $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); + else + $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); + }).css('display', ''); + if (DOCUMENTATION_OPTIONS.COLLAPSE_MODINDEX) { + togglers.click(); + } + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords : function() { + $('.sidebar .this-page-menu li.highlight-link').fadeOut(300); + $('span.highlight').removeClass('highlight'); + }, + + /** + * make the url absolute + */ + makeURL : function(relativeURL) { + return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; + }, + + /** + * get the current relative url + */ + getCurrentURL : function() { + var path = document.location.pathname; + var parts = path.split(/\//); + $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { + if (this == '..') + parts.pop(); + }); + var url = parts.join('/'); + return path.substring(url.lastIndexOf('/') + 1, path.length - 1); + } +}; + +// quick alias for translations +_ = Documentation.gettext; + +$(document).ready(function() { + Documentation.init(); +}); diff --git a/doc/doc_index/0.1/_static/file.png b/doc/doc_index/0.1/_static/file.png new file mode 100644 index 00000000..d18082e3 Binary files /dev/null and b/doc/doc_index/0.1/_static/file.png differ diff --git a/doc/doc_index/0.1/_static/jquery.js b/doc/doc_index/0.1/_static/jquery.js new file mode 100644 index 00000000..82b98e1d --- /dev/null +++ b/doc/doc_index/0.1/_static/jquery.js @@ -0,0 +1,32 @@ +/* + * jQuery 1.2.6 - New Wave Javascript + * + * Copyright (c) 2008 John Resig (jquery.com) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * $Date: 2008-05-24 14:22:17 -0400 (Sat, 24 May 2008) $ + * $Rev: 5685 $ + */ +(function(){var _jQuery=window.jQuery,_$=window.$;var jQuery=window.jQuery=window.$=function(selector,context){return new jQuery.fn.init(selector,context);};var quickExpr=/^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/,isSimple=/^.[^:#\[\.]*$/,undefined;jQuery.fn=jQuery.prototype={init:function(selector,context){selector=selector||document;if(selector.nodeType){this[0]=selector;this.length=1;return this;}if(typeof selector=="string"){var match=quickExpr.exec(selector);if(match&&(match[1]||!context)){if(match[1])selector=jQuery.clean([match[1]],context);else{var elem=document.getElementById(match[3]);if(elem){if(elem.id!=match[3])return jQuery().find(selector);return jQuery(elem);}selector=[];}}else +return jQuery(context).find(selector);}else if(jQuery.isFunction(selector))return jQuery(document)[jQuery.fn.ready?"ready":"load"](selector);return this.setArray(jQuery.makeArray(selector));},jquery:"1.2.6",size:function(){return this.length;},length:0,get:function(num){return num==undefined?jQuery.makeArray(this):this[num];},pushStack:function(elems){var ret=jQuery(elems);ret.prevObject=this;return ret;},setArray:function(elems){this.length=0;Array.prototype.push.apply(this,elems);return this;},each:function(callback,args){return jQuery.each(this,callback,args);},index:function(elem){var ret=-1;return jQuery.inArray(elem&&elem.jquery?elem[0]:elem,this);},attr:function(name,value,type){var options=name;if(name.constructor==String)if(value===undefined)return this[0]&&jQuery[type||"attr"](this[0],name);else{options={};options[name]=value;}return this.each(function(i){for(name in options)jQuery.attr(type?this.style:this,name,jQuery.prop(this,options[name],type,i,name));});},css:function(key,value){if((key=='width'||key=='height')&&parseFloat(value)<0)value=undefined;return this.attr(key,value,"curCSS");},text:function(text){if(typeof text!="object"&&text!=null)return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text));var ret="";jQuery.each(text||this,function(){jQuery.each(this.childNodes,function(){if(this.nodeType!=8)ret+=this.nodeType!=1?this.nodeValue:jQuery.fn.text([this]);});});return ret;},wrapAll:function(html){if(this[0])jQuery(html,this[0].ownerDocument).clone().insertBefore(this[0]).map(function(){var elem=this;while(elem.firstChild)elem=elem.firstChild;return elem;}).append(this);return this;},wrapInner:function(html){return this.each(function(){jQuery(this).contents().wrapAll(html);});},wrap:function(html){return this.each(function(){jQuery(this).wrapAll(html);});},append:function(){return this.domManip(arguments,true,false,function(elem){if(this.nodeType==1)this.appendChild(elem);});},prepend:function(){return this.domManip(arguments,true,true,function(elem){if(this.nodeType==1)this.insertBefore(elem,this.firstChild);});},before:function(){return this.domManip(arguments,false,false,function(elem){this.parentNode.insertBefore(elem,this);});},after:function(){return this.domManip(arguments,false,true,function(elem){this.parentNode.insertBefore(elem,this.nextSibling);});},end:function(){return this.prevObject||jQuery([]);},find:function(selector){var elems=jQuery.map(this,function(elem){return jQuery.find(selector,elem);});return this.pushStack(/[^+>] [^+>]/.test(selector)||selector.indexOf("..")>-1?jQuery.unique(elems):elems);},clone:function(events){var ret=this.map(function(){if(jQuery.browser.msie&&!jQuery.isXMLDoc(this)){var clone=this.cloneNode(true),container=document.createElement("div");container.appendChild(clone);return jQuery.clean([container.innerHTML])[0];}else +return this.cloneNode(true);});var clone=ret.find("*").andSelf().each(function(){if(this[expando]!=undefined)this[expando]=null;});if(events===true)this.find("*").andSelf().each(function(i){if(this.nodeType==3)return;var events=jQuery.data(this,"events");for(var type in events)for(var handler in events[type])jQuery.event.add(clone[i],type,events[type][handler],events[type][handler].data);});return ret;},filter:function(selector){return this.pushStack(jQuery.isFunction(selector)&&jQuery.grep(this,function(elem,i){return selector.call(elem,i);})||jQuery.multiFilter(selector,this));},not:function(selector){if(selector.constructor==String)if(isSimple.test(selector))return this.pushStack(jQuery.multiFilter(selector,this,true));else +selector=jQuery.multiFilter(selector,this);var isArrayLike=selector.length&&selector[selector.length-1]!==undefined&&!selector.nodeType;return this.filter(function(){return isArrayLike?jQuery.inArray(this,selector)<0:this!=selector;});},add:function(selector){return this.pushStack(jQuery.unique(jQuery.merge(this.get(),typeof selector=='string'?jQuery(selector):jQuery.makeArray(selector))));},is:function(selector){return!!selector&&jQuery.multiFilter(selector,this).length>0;},hasClass:function(selector){return this.is("."+selector);},val:function(value){if(value==undefined){if(this.length){var elem=this[0];if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,values=[],options=elem.options,one=elem.type=="select-one";if(index<0)return null;for(var i=one?index:0,max=one?index+1:options.length;i=0||jQuery.inArray(this.name,value)>=0);else if(jQuery.nodeName(this,"select")){var values=jQuery.makeArray(value);jQuery("option",this).each(function(){this.selected=(jQuery.inArray(this.value,values)>=0||jQuery.inArray(this.text,values)>=0);});if(!values.length)this.selectedIndex=-1;}else +this.value=value;});},html:function(value){return value==undefined?(this[0]?this[0].innerHTML:null):this.empty().append(value);},replaceWith:function(value){return this.after(value).remove();},eq:function(i){return this.slice(i,i+1);},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments));},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem);}));},andSelf:function(){return this.add(this.prevObject);},data:function(key,value){var parts=key.split(".");parts[1]=parts[1]?"."+parts[1]:"";if(value===undefined){var data=this.triggerHandler("getData"+parts[1]+"!",[parts[0]]);if(data===undefined&&this.length)data=jQuery.data(this[0],key);return data===undefined&&parts[1]?this.data(parts[0]):data;}else +return this.trigger("setData"+parts[1]+"!",[parts[0],value]).each(function(){jQuery.data(this,key,value);});},removeData:function(key){return this.each(function(){jQuery.removeData(this,key);});},domManip:function(args,table,reverse,callback){var clone=this.length>1,elems;return this.each(function(){if(!elems){elems=jQuery.clean(args,this.ownerDocument);if(reverse)elems.reverse();}var obj=this;if(table&&jQuery.nodeName(this,"table")&&jQuery.nodeName(elems[0],"tr"))obj=this.getElementsByTagName("tbody")[0]||this.appendChild(this.ownerDocument.createElement("tbody"));var scripts=jQuery([]);jQuery.each(elems,function(){var elem=clone?jQuery(this).clone(true)[0]:this;if(jQuery.nodeName(elem,"script"))scripts=scripts.add(elem);else{if(elem.nodeType==1)scripts=scripts.add(jQuery("script",elem).remove());callback.call(obj,elem);}});scripts.each(evalScript);});}};jQuery.fn.init.prototype=jQuery.fn;function evalScript(i,elem){if(elem.src)jQuery.ajax({url:elem.src,async:false,dataType:"script"});else +jQuery.globalEval(elem.text||elem.textContent||elem.innerHTML||"");if(elem.parentNode)elem.parentNode.removeChild(elem);}function now(){return+new Date;}jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},i=1,length=arguments.length,deep=false,options;if(target.constructor==Boolean){deep=target;target=arguments[1]||{};i=2;}if(typeof target!="object"&&typeof target!="function")target={};if(length==i){target=this;--i;}for(;i-1;}},swap:function(elem,options,callback){var old={};for(var name in options){old[name]=elem.style[name];elem.style[name]=options[name];}callback.call(elem);for(var name in options)elem.style[name]=old[name];},css:function(elem,name,force){if(name=="width"||name=="height"){var val,props={position:"absolute",visibility:"hidden",display:"block"},which=name=="width"?["Left","Right"]:["Top","Bottom"];function getWH(){val=name=="width"?elem.offsetWidth:elem.offsetHeight;var padding=0,border=0;jQuery.each(which,function(){padding+=parseFloat(jQuery.curCSS(elem,"padding"+this,true))||0;border+=parseFloat(jQuery.curCSS(elem,"border"+this+"Width",true))||0;});val-=Math.round(padding+border);}if(jQuery(elem).is(":visible"))getWH();else +jQuery.swap(elem,props,getWH);return Math.max(0,val);}return jQuery.curCSS(elem,name,force);},curCSS:function(elem,name,force){var ret,style=elem.style;function color(elem){if(!jQuery.browser.safari)return false;var ret=defaultView.getComputedStyle(elem,null);return!ret||ret.getPropertyValue("color")=="";}if(name=="opacity"&&jQuery.browser.msie){ret=jQuery.attr(style,"opacity");return ret==""?"1":ret;}if(jQuery.browser.opera&&name=="display"){var save=style.outline;style.outline="0 solid black";style.outline=save;}if(name.match(/float/i))name=styleFloat;if(!force&&style&&style[name])ret=style[name];else if(defaultView.getComputedStyle){if(name.match(/float/i))name="float";name=name.replace(/([A-Z])/g,"-$1").toLowerCase();var computedStyle=defaultView.getComputedStyle(elem,null);if(computedStyle&&!color(elem))ret=computedStyle.getPropertyValue(name);else{var swap=[],stack=[],a=elem,i=0;for(;a&&color(a);a=a.parentNode)stack.unshift(a);for(;i]*?)\/>/g,function(all,front,tag){return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?all:front+">";});var tags=jQuery.trim(elem).toLowerCase(),div=context.createElement("div");var wrap=!tags.indexOf("",""]||!tags.indexOf("",""]||tags.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
"]||!tags.indexOf("",""]||(!tags.indexOf("",""]||!tags.indexOf("",""]||jQuery.browser.msie&&[1,"div
","
"]||[0,"",""];div.innerHTML=wrap[1]+elem+wrap[2];while(wrap[0]--)div=div.lastChild;if(jQuery.browser.msie){var tbody=!tags.indexOf(""&&tags.indexOf("=0;--j)if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length)tbody[j].parentNode.removeChild(tbody[j]);if(/^\s/.test(elem))div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]),div.firstChild);}elem=jQuery.makeArray(div.childNodes);}if(elem.length===0&&(!jQuery.nodeName(elem,"form")&&!jQuery.nodeName(elem,"select")))return;if(elem[0]==undefined||jQuery.nodeName(elem,"form")||elem.options)ret.push(elem);else +ret=jQuery.merge(ret,elem);});return ret;},attr:function(elem,name,value){if(!elem||elem.nodeType==3||elem.nodeType==8)return undefined;var notxml=!jQuery.isXMLDoc(elem),set=value!==undefined,msie=jQuery.browser.msie;name=notxml&&jQuery.props[name]||name;if(elem.tagName){var special=/href|src|style/.test(name);if(name=="selected"&&jQuery.browser.safari)elem.parentNode.selectedIndex;if(name in elem&¬xml&&!special){if(set){if(name=="type"&&jQuery.nodeName(elem,"input")&&elem.parentNode)throw"type property can't be changed";elem[name]=value;}if(jQuery.nodeName(elem,"form")&&elem.getAttributeNode(name))return elem.getAttributeNode(name).nodeValue;return elem[name];}if(msie&¬xml&&name=="style")return jQuery.attr(elem.style,"cssText",value);if(set)elem.setAttribute(name,""+value);var attr=msie&¬xml&&special?elem.getAttribute(name,2):elem.getAttribute(name);return attr===null?undefined:attr;}if(msie&&name=="opacity"){if(set){elem.zoom=1;elem.filter=(elem.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(value)+''=="NaN"?"":"alpha(opacity="+value*100+")");}return elem.filter&&elem.filter.indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity=([^)]*)/)[1])/100)+'':"";}name=name.replace(/-([a-z])/ig,function(all,letter){return letter.toUpperCase();});if(set)elem[name]=value;return elem[name];},trim:function(text){return(text||"").replace(/^\s+|\s+$/g,"");},makeArray:function(array){var ret=[];if(array!=null){var i=array.length;if(i==null||array.split||array.setInterval||array.call)ret[0]=array;else +while(i)ret[--i]=array[i];}return ret;},inArray:function(elem,array){for(var i=0,length=array.length;i*",this).remove();while(this.firstChild)this.removeChild(this.firstChild);}},function(name,fn){jQuery.fn[name]=function(){return this.each(fn,arguments);};});jQuery.each(["Height","Width"],function(i,name){var type=name.toLowerCase();jQuery.fn[type]=function(size){return this[0]==window?jQuery.browser.opera&&document.body["client"+name]||jQuery.browser.safari&&window["inner"+name]||document.compatMode=="CSS1Compat"&&document.documentElement["client"+name]||document.body["client"+name]:this[0]==document?Math.max(Math.max(document.body["scroll"+name],document.documentElement["scroll"+name]),Math.max(document.body["offset"+name],document.documentElement["offset"+name])):size==undefined?(this.length?jQuery.css(this[0],type):null):this.css(type,size.constructor==String?size:size+"px");};});function num(elem,prop){return elem[0]&&parseInt(jQuery.curCSS(elem[0],prop,true),10)||0;}var chars=jQuery.browser.safari&&parseInt(jQuery.browser.version)<417?"(?:[\\w*_-]|\\\\.)":"(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",quickChild=new RegExp("^>\\s*("+chars+"+)"),quickID=new RegExp("^("+chars+"+)(#)("+chars+"+)"),quickClass=new RegExp("^([#.]?)("+chars+"*)");jQuery.extend({expr:{"":function(a,i,m){return m[2]=="*"||jQuery.nodeName(a,m[2]);},"#":function(a,i,m){return a.getAttribute("id")==m[2];},":":{lt:function(a,i,m){return im[3]-0;},nth:function(a,i,m){return m[3]-0==i;},eq:function(a,i,m){return m[3]-0==i;},first:function(a,i){return i==0;},last:function(a,i,m,r){return i==r.length-1;},even:function(a,i){return i%2==0;},odd:function(a,i){return i%2;},"first-child":function(a){return a.parentNode.getElementsByTagName("*")[0]==a;},"last-child":function(a){return jQuery.nth(a.parentNode.lastChild,1,"previousSibling")==a;},"only-child":function(a){return!jQuery.nth(a.parentNode.lastChild,2,"previousSibling");},parent:function(a){return a.firstChild;},empty:function(a){return!a.firstChild;},contains:function(a,i,m){return(a.textContent||a.innerText||jQuery(a).text()||"").indexOf(m[3])>=0;},visible:function(a){return"hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden";},hidden:function(a){return"hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden";},enabled:function(a){return!a.disabled;},disabled:function(a){return a.disabled;},checked:function(a){return a.checked;},selected:function(a){return a.selected||jQuery.attr(a,"selected");},text:function(a){return"text"==a.type;},radio:function(a){return"radio"==a.type;},checkbox:function(a){return"checkbox"==a.type;},file:function(a){return"file"==a.type;},password:function(a){return"password"==a.type;},submit:function(a){return"submit"==a.type;},image:function(a){return"image"==a.type;},reset:function(a){return"reset"==a.type;},button:function(a){return"button"==a.type||jQuery.nodeName(a,"button");},input:function(a){return/input|select|textarea|button/i.test(a.nodeName);},has:function(a,i,m){return jQuery.find(m[3],a).length;},header:function(a){return/h\d/i.test(a.nodeName);},animated:function(a){return jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length;}}},parse:[/^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,new RegExp("^([:.#]*)("+chars+"+)")],multiFilter:function(expr,elems,not){var old,cur=[];while(expr&&expr!=old){old=expr;var f=jQuery.filter(expr,elems,not);expr=f.t.replace(/^\s*,\s*/,"");cur=not?elems=f.r:jQuery.merge(cur,f.r);}return cur;},find:function(t,context){if(typeof t!="string")return[t];if(context&&context.nodeType!=1&&context.nodeType!=9)return[];context=context||document;var ret=[context],done=[],last,nodeName;while(t&&last!=t){var r=[];last=t;t=jQuery.trim(t);var foundToken=false,re=quickChild,m=re.exec(t);if(m){nodeName=m[1].toUpperCase();for(var i=0;ret[i];i++)for(var c=ret[i].firstChild;c;c=c.nextSibling)if(c.nodeType==1&&(nodeName=="*"||c.nodeName.toUpperCase()==nodeName))r.push(c);ret=r;t=t.replace(re,"");if(t.indexOf(" ")==0)continue;foundToken=true;}else{re=/^([>+~])\s*(\w*)/i;if((m=re.exec(t))!=null){r=[];var merge={};nodeName=m[2].toUpperCase();m=m[1];for(var j=0,rl=ret.length;j=0;if(!not&&pass||not&&!pass)tmp.push(r[i]);}return tmp;},filter:function(t,r,not){var last;while(t&&t!=last){last=t;var p=jQuery.parse,m;for(var i=0;p[i];i++){m=p[i].exec(t);if(m){t=t.substring(m[0].length);m[2]=m[2].replace(/\\/g,"");break;}}if(!m)break;if(m[1]==":"&&m[2]=="not")r=isSimple.test(m[3])?jQuery.filter(m[3],r,true).r:jQuery(r).not(m[3]);else if(m[1]==".")r=jQuery.classFilter(r,m[2],not);else if(m[1]=="["){var tmp=[],type=m[3];for(var i=0,rl=r.length;i=0)^not)tmp.push(a);}r=tmp;}else if(m[1]==":"&&m[2]=="nth-child"){var merge={},tmp=[],test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[3]=="even"&&"2n"||m[3]=="odd"&&"2n+1"||!/\D/.test(m[3])&&"0n+"+m[3]||m[3]),first=(test[1]+(test[2]||1))-0,last=test[3]-0;for(var i=0,rl=r.length;i=0)add=true;if(add^not)tmp.push(node);}r=tmp;}else{var fn=jQuery.expr[m[1]];if(typeof fn=="object")fn=fn[m[2]];if(typeof fn=="string")fn=eval("false||function(a,i){return "+fn+";}");r=jQuery.grep(r,function(elem,i){return fn(elem,i,m,r);},not);}}return{r:r,t:t};},dir:function(elem,dir){var matched=[],cur=elem[dir];while(cur&&cur!=document){if(cur.nodeType==1)matched.push(cur);cur=cur[dir];}return matched;},nth:function(cur,result,dir,elem){result=result||1;var num=0;for(;cur;cur=cur[dir])if(cur.nodeType==1&&++num==result)break;return cur;},sibling:function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType==1&&n!=elem)r.push(n);}return r;}});jQuery.event={add:function(elem,types,handler,data){if(elem.nodeType==3||elem.nodeType==8)return;if(jQuery.browser.msie&&elem.setInterval)elem=window;if(!handler.guid)handler.guid=this.guid++;if(data!=undefined){var fn=handler;handler=this.proxy(fn,function(){return fn.apply(this,arguments);});handler.data=data;}var events=jQuery.data(elem,"events")||jQuery.data(elem,"events",{}),handle=jQuery.data(elem,"handle")||jQuery.data(elem,"handle",function(){if(typeof jQuery!="undefined"&&!jQuery.event.triggered)return jQuery.event.handle.apply(arguments.callee.elem,arguments);});handle.elem=elem;jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];handler.type=parts[1];var handlers=events[type];if(!handlers){handlers=events[type]={};if(!jQuery.event.special[type]||jQuery.event.special[type].setup.call(elem)===false){if(elem.addEventListener)elem.addEventListener(type,handle,false);else if(elem.attachEvent)elem.attachEvent("on"+type,handle);}}handlers[handler.guid]=handler;jQuery.event.global[type]=true;});elem=null;},guid:1,global:{},remove:function(elem,types,handler){if(elem.nodeType==3||elem.nodeType==8)return;var events=jQuery.data(elem,"events"),ret,index;if(events){if(types==undefined||(typeof types=="string"&&types.charAt(0)=="."))for(var type in events)this.remove(elem,type+(types||""));else{if(types.type){handler=types.handler;types=types.type;}jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];if(events[type]){if(handler)delete events[type][handler.guid];else +for(handler in events[type])if(!parts[1]||events[type][handler].type==parts[1])delete events[type][handler];for(ret in events[type])break;if(!ret){if(!jQuery.event.special[type]||jQuery.event.special[type].teardown.call(elem)===false){if(elem.removeEventListener)elem.removeEventListener(type,jQuery.data(elem,"handle"),false);else if(elem.detachEvent)elem.detachEvent("on"+type,jQuery.data(elem,"handle"));}ret=null;delete events[type];}}});}for(ret in events)break;if(!ret){var handle=jQuery.data(elem,"handle");if(handle)handle.elem=null;jQuery.removeData(elem,"events");jQuery.removeData(elem,"handle");}}},trigger:function(type,data,elem,donative,extra){data=jQuery.makeArray(data);if(type.indexOf("!")>=0){type=type.slice(0,-1);var exclusive=true;}if(!elem){if(this.global[type])jQuery("*").add([window,document]).trigger(type,data);}else{if(elem.nodeType==3||elem.nodeType==8)return undefined;var val,ret,fn=jQuery.isFunction(elem[type]||null),event=!data[0]||!data[0].preventDefault;if(event){data.unshift({type:type,target:elem,preventDefault:function(){},stopPropagation:function(){},timeStamp:now()});data[0][expando]=true;}data[0].type=type;if(exclusive)data[0].exclusive=true;var handle=jQuery.data(elem,"handle");if(handle)val=handle.apply(elem,data);if((!fn||(jQuery.nodeName(elem,'a')&&type=="click"))&&elem["on"+type]&&elem["on"+type].apply(elem,data)===false)val=false;if(event)data.shift();if(extra&&jQuery.isFunction(extra)){ret=extra.apply(elem,val==null?data:data.concat(val));if(ret!==undefined)val=ret;}if(fn&&donative!==false&&val!==false&&!(jQuery.nodeName(elem,'a')&&type=="click")){this.triggered=true;try{elem[type]();}catch(e){}}this.triggered=false;}return val;},handle:function(event){var val,ret,namespace,all,handlers;event=arguments[0]=jQuery.event.fix(event||window.event);namespace=event.type.split(".");event.type=namespace[0];namespace=namespace[1];all=!namespace&&!event.exclusive;handlers=(jQuery.data(this,"events")||{})[event.type];for(var j in handlers){var handler=handlers[j];if(all||handler.type==namespace){event.handler=handler;event.data=handler.data;ret=handler.apply(this,arguments);if(val!==false)val=ret;if(ret===false){event.preventDefault();event.stopPropagation();}}}return val;},fix:function(event){if(event[expando]==true)return event;var originalEvent=event;event={originalEvent:originalEvent};var props="altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target timeStamp toElement type view wheelDelta which".split(" ");for(var i=props.length;i;i--)event[props[i]]=originalEvent[props[i]];event[expando]=true;event.preventDefault=function(){if(originalEvent.preventDefault)originalEvent.preventDefault();originalEvent.returnValue=false;};event.stopPropagation=function(){if(originalEvent.stopPropagation)originalEvent.stopPropagation();originalEvent.cancelBubble=true;};event.timeStamp=event.timeStamp||now();if(!event.target)event.target=event.srcElement||document;if(event.target.nodeType==3)event.target=event.target.parentNode;if(!event.relatedTarget&&event.fromElement)event.relatedTarget=event.fromElement==event.target?event.toElement:event.fromElement;if(event.pageX==null&&event.clientX!=null){var doc=document.documentElement,body=document.body;event.pageX=event.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc.clientLeft||0);event.pageY=event.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc.clientTop||0);}if(!event.which&&((event.charCode||event.charCode===0)?event.charCode:event.keyCode))event.which=event.charCode||event.keyCode;if(!event.metaKey&&event.ctrlKey)event.metaKey=event.ctrlKey;if(!event.which&&event.button)event.which=(event.button&1?1:(event.button&2?3:(event.button&4?2:0)));return event;},proxy:function(fn,proxy){proxy.guid=fn.guid=fn.guid||proxy.guid||this.guid++;return proxy;},special:{ready:{setup:function(){bindReady();return;},teardown:function(){return;}},mouseenter:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseover",jQuery.event.special.mouseenter.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseover",jQuery.event.special.mouseenter.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseenter";return jQuery.event.handle.apply(this,arguments);}},mouseleave:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseout",jQuery.event.special.mouseleave.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseout",jQuery.event.special.mouseleave.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseleave";return jQuery.event.handle.apply(this,arguments);}}}};jQuery.fn.extend({bind:function(type,data,fn){return type=="unload"?this.one(type,data,fn):this.each(function(){jQuery.event.add(this,type,fn||data,fn&&data);});},one:function(type,data,fn){var one=jQuery.event.proxy(fn||data,function(event){jQuery(this).unbind(event,one);return(fn||data).apply(this,arguments);});return this.each(function(){jQuery.event.add(this,type,one,fn&&data);});},unbind:function(type,fn){return this.each(function(){jQuery.event.remove(this,type,fn);});},trigger:function(type,data,fn){return this.each(function(){jQuery.event.trigger(type,data,this,true,fn);});},triggerHandler:function(type,data,fn){return this[0]&&jQuery.event.trigger(type,data,this[0],false,fn);},toggle:function(fn){var args=arguments,i=1;while(i=0){var selector=url.slice(off,url.length);url=url.slice(0,off);}callback=callback||function(){};var type="GET";if(params)if(jQuery.isFunction(params)){callback=params;params=null;}else{params=jQuery.param(params);type="POST";}var self=this;jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(res,status){if(status=="success"||status=="notmodified")self.html(selector?jQuery("
").append(res.responseText.replace(//g,"")).find(selector):res.responseText);self.each(callback,[res.responseText,status,res]);}});return this;},serialize:function(){return jQuery.param(this.serializeArray());},serializeArray:function(){return this.map(function(){return jQuery.nodeName(this,"form")?jQuery.makeArray(this.elements):this;}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password/i.test(this.type));}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:val.constructor==Array?jQuery.map(val,function(val,i){return{name:elem.name,value:val};}):{name:elem.name,value:val};}).get();}});jQuery.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(i,o){jQuery.fn[o]=function(f){return this.bind(o,f);};});var jsc=now();jQuery.extend({get:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data=null;}return jQuery.ajax({type:"GET",url:url,data:data,success:callback,dataType:type});},getScript:function(url,callback){return jQuery.get(url,null,callback,"script");},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json");},post:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data={};}return jQuery.ajax({type:"POST",url:url,data:data,success:callback,dataType:type});},ajaxSetup:function(settings){jQuery.extend(jQuery.ajaxSettings,settings);},ajaxSettings:{url:location.href,global:true,type:"GET",timeout:0,contentType:"application/x-www-form-urlencoded",processData:true,async:true,data:null,username:null,password:null,accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(s){s=jQuery.extend(true,s,jQuery.extend(true,{},jQuery.ajaxSettings,s));var jsonp,jsre=/=\?(&|$)/g,status,data,type=s.type.toUpperCase();if(s.data&&s.processData&&typeof s.data!="string")s.data=jQuery.param(s.data);if(s.dataType=="jsonp"){if(type=="GET"){if(!s.url.match(jsre))s.url+=(s.url.match(/\?/)?"&":"?")+(s.jsonp||"callback")+"=?";}else if(!s.data||!s.data.match(jsre))s.data=(s.data?s.data+"&":"")+(s.jsonp||"callback")+"=?";s.dataType="json";}if(s.dataType=="json"&&(s.data&&s.data.match(jsre)||s.url.match(jsre))){jsonp="jsonp"+jsc++;if(s.data)s.data=(s.data+"").replace(jsre,"="+jsonp+"$1");s.url=s.url.replace(jsre,"="+jsonp+"$1");s.dataType="script";window[jsonp]=function(tmp){data=tmp;success();complete();window[jsonp]=undefined;try{delete window[jsonp];}catch(e){}if(head)head.removeChild(script);};}if(s.dataType=="script"&&s.cache==null)s.cache=false;if(s.cache===false&&type=="GET"){var ts=now();var ret=s.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+ts+"$2");s.url=ret+((ret==s.url)?(s.url.match(/\?/)?"&":"?")+"_="+ts:"");}if(s.data&&type=="GET"){s.url+=(s.url.match(/\?/)?"&":"?")+s.data;s.data=null;}if(s.global&&!jQuery.active++)jQuery.event.trigger("ajaxStart");var remote=/^(?:\w+:)?\/\/([^\/?#]+)/;if(s.dataType=="script"&&type=="GET"&&remote.test(s.url)&&remote.exec(s.url)[1]!=location.host){var head=document.getElementsByTagName("head")[0];var script=document.createElement("script");script.src=s.url;if(s.scriptCharset)script.charset=s.scriptCharset;if(!jsonp){var done=false;script.onload=script.onreadystatechange=function(){if(!done&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){done=true;success();complete();head.removeChild(script);}};}head.appendChild(script);return undefined;}var requestDone=false;var xhr=window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();if(s.username)xhr.open(type,s.url,s.async,s.username,s.password);else +xhr.open(type,s.url,s.async);try{if(s.data)xhr.setRequestHeader("Content-Type",s.contentType);if(s.ifModified)xhr.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]||"Thu, 01 Jan 1970 00:00:00 GMT");xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");xhr.setRequestHeader("Accept",s.dataType&&s.accepts[s.dataType]?s.accepts[s.dataType]+", */*":s.accepts._default);}catch(e){}if(s.beforeSend&&s.beforeSend(xhr,s)===false){s.global&&jQuery.active--;xhr.abort();return false;}if(s.global)jQuery.event.trigger("ajaxSend",[xhr,s]);var onreadystatechange=function(isTimeout){if(!requestDone&&xhr&&(xhr.readyState==4||isTimeout=="timeout")){requestDone=true;if(ival){clearInterval(ival);ival=null;}status=isTimeout=="timeout"&&"timeout"||!jQuery.httpSuccess(xhr)&&"error"||s.ifModified&&jQuery.httpNotModified(xhr,s.url)&&"notmodified"||"success";if(status=="success"){try{data=jQuery.httpData(xhr,s.dataType,s.dataFilter);}catch(e){status="parsererror";}}if(status=="success"){var modRes;try{modRes=xhr.getResponseHeader("Last-Modified");}catch(e){}if(s.ifModified&&modRes)jQuery.lastModified[s.url]=modRes;if(!jsonp)success();}else +jQuery.handleError(s,xhr,status);complete();if(s.async)xhr=null;}};if(s.async){var ival=setInterval(onreadystatechange,13);if(s.timeout>0)setTimeout(function(){if(xhr){xhr.abort();if(!requestDone)onreadystatechange("timeout");}},s.timeout);}try{xhr.send(s.data);}catch(e){jQuery.handleError(s,xhr,null,e);}if(!s.async)onreadystatechange();function success(){if(s.success)s.success(data,status);if(s.global)jQuery.event.trigger("ajaxSuccess",[xhr,s]);}function complete(){if(s.complete)s.complete(xhr,status);if(s.global)jQuery.event.trigger("ajaxComplete",[xhr,s]);if(s.global&&!--jQuery.active)jQuery.event.trigger("ajaxStop");}return xhr;},handleError:function(s,xhr,status,e){if(s.error)s.error(xhr,status,e);if(s.global)jQuery.event.trigger("ajaxError",[xhr,s,e]);},active:0,httpSuccess:function(xhr){try{return!xhr.status&&location.protocol=="file:"||(xhr.status>=200&&xhr.status<300)||xhr.status==304||xhr.status==1223||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpNotModified:function(xhr,url){try{var xhrRes=xhr.getResponseHeader("Last-Modified");return xhr.status==304||xhrRes==jQuery.lastModified[url]||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpData:function(xhr,type,filter){var ct=xhr.getResponseHeader("content-type"),xml=type=="xml"||!type&&ct&&ct.indexOf("xml")>=0,data=xml?xhr.responseXML:xhr.responseText;if(xml&&data.documentElement.tagName=="parsererror")throw"parsererror";if(filter)data=filter(data,type);if(type=="script")jQuery.globalEval(data);if(type=="json")data=eval("("+data+")");return data;},param:function(a){var s=[];if(a.constructor==Array||a.jquery)jQuery.each(a,function(){s.push(encodeURIComponent(this.name)+"="+encodeURIComponent(this.value));});else +for(var j in a)if(a[j]&&a[j].constructor==Array)jQuery.each(a[j],function(){s.push(encodeURIComponent(j)+"="+encodeURIComponent(this));});else +s.push(encodeURIComponent(j)+"="+encodeURIComponent(jQuery.isFunction(a[j])?a[j]():a[j]));return s.join("&").replace(/%20/g,"+");}});jQuery.fn.extend({show:function(speed,callback){return speed?this.animate({height:"show",width:"show",opacity:"show"},speed,callback):this.filter(":hidden").each(function(){this.style.display=this.oldblock||"";if(jQuery.css(this,"display")=="none"){var elem=jQuery("<"+this.tagName+" />").appendTo("body");this.style.display=elem.css("display");if(this.style.display=="none")this.style.display="block";elem.remove();}}).end();},hide:function(speed,callback){return speed?this.animate({height:"hide",width:"hide",opacity:"hide"},speed,callback):this.filter(":visible").each(function(){this.oldblock=this.oldblock||jQuery.css(this,"display");this.style.display="none";}).end();},_toggle:jQuery.fn.toggle,toggle:function(fn,fn2){return jQuery.isFunction(fn)&&jQuery.isFunction(fn2)?this._toggle.apply(this,arguments):fn?this.animate({height:"toggle",width:"toggle",opacity:"toggle"},fn,fn2):this.each(function(){jQuery(this)[jQuery(this).is(":hidden")?"show":"hide"]();});},slideDown:function(speed,callback){return this.animate({height:"show"},speed,callback);},slideUp:function(speed,callback){return this.animate({height:"hide"},speed,callback);},slideToggle:function(speed,callback){return this.animate({height:"toggle"},speed,callback);},fadeIn:function(speed,callback){return this.animate({opacity:"show"},speed,callback);},fadeOut:function(speed,callback){return this.animate({opacity:"hide"},speed,callback);},fadeTo:function(speed,to,callback){return this.animate({opacity:to},speed,callback);},animate:function(prop,speed,easing,callback){var optall=jQuery.speed(speed,easing,callback);return this[optall.queue===false?"each":"queue"](function(){if(this.nodeType!=1)return false;var opt=jQuery.extend({},optall),p,hidden=jQuery(this).is(":hidden"),self=this;for(p in prop){if(prop[p]=="hide"&&hidden||prop[p]=="show"&&!hidden)return opt.complete.call(this);if(p=="height"||p=="width"){opt.display=jQuery.css(this,"display");opt.overflow=this.style.overflow;}}if(opt.overflow!=null)this.style.overflow="hidden";opt.curAnim=jQuery.extend({},prop);jQuery.each(prop,function(name,val){var e=new jQuery.fx(self,opt,name);if(/toggle|show|hide/.test(val))e[val=="toggle"?hidden?"show":"hide":val](prop);else{var parts=val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),start=e.cur(true)||0;if(parts){var end=parseFloat(parts[2]),unit=parts[3]||"px";if(unit!="px"){self.style[name]=(end||1)+unit;start=((end||1)/e.cur(true))*start;self.style[name]=start+unit;}if(parts[1])end=((parts[1]=="-="?-1:1)*end)+start;e.custom(start,end,unit);}else +e.custom(start,val,"");}});return true;});},queue:function(type,fn){if(jQuery.isFunction(type)||(type&&type.constructor==Array)){fn=type;type="fx";}if(!type||(typeof type=="string"&&!fn))return queue(this[0],type);return this.each(function(){if(fn.constructor==Array)queue(this,type,fn);else{queue(this,type).push(fn);if(queue(this,type).length==1)fn.call(this);}});},stop:function(clearQueue,gotoEnd){var timers=jQuery.timers;if(clearQueue)this.queue([]);this.each(function(){for(var i=timers.length-1;i>=0;i--)if(timers[i].elem==this){if(gotoEnd)timers[i](true);timers.splice(i,1);}});if(!gotoEnd)this.dequeue();return this;}});var queue=function(elem,type,array){if(elem){type=type||"fx";var q=jQuery.data(elem,type+"queue");if(!q||array)q=jQuery.data(elem,type+"queue",jQuery.makeArray(array));}return q;};jQuery.fn.dequeue=function(type){type=type||"fx";return this.each(function(){var q=queue(this,type);q.shift();if(q.length)q[0].call(this);});};jQuery.extend({speed:function(speed,easing,fn){var opt=speed&&speed.constructor==Object?speed:{complete:fn||!fn&&easing||jQuery.isFunction(speed)&&speed,duration:speed,easing:fn&&easing||easing&&easing.constructor!=Function&&easing};opt.duration=(opt.duration&&opt.duration.constructor==Number?opt.duration:jQuery.fx.speeds[opt.duration])||jQuery.fx.speeds.def;opt.old=opt.complete;opt.complete=function(){if(opt.queue!==false)jQuery(this).dequeue();if(jQuery.isFunction(opt.old))opt.old.call(this);};return opt;},easing:{linear:function(p,n,firstNum,diff){return firstNum+diff*p;},swing:function(p,n,firstNum,diff){return((-Math.cos(p*Math.PI)/2)+0.5)*diff+firstNum;}},timers:[],timerId:null,fx:function(elem,options,prop){this.options=options;this.elem=elem;this.prop=prop;if(!options.orig)options.orig={};}});jQuery.fx.prototype={update:function(){if(this.options.step)this.options.step.call(this.elem,this.now,this);(jQuery.fx.step[this.prop]||jQuery.fx.step._default)(this);if(this.prop=="height"||this.prop=="width")this.elem.style.display="block";},cur:function(force){if(this.elem[this.prop]!=null&&this.elem.style[this.prop]==null)return this.elem[this.prop];var r=parseFloat(jQuery.css(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.curCSS(this.elem,this.prop))||0;},custom:function(from,to,unit){this.startTime=now();this.start=from;this.end=to;this.unit=unit||this.unit||"px";this.now=this.start;this.pos=this.state=0;this.update();var self=this;function t(gotoEnd){return self.step(gotoEnd);}t.elem=this.elem;jQuery.timers.push(t);if(jQuery.timerId==null){jQuery.timerId=setInterval(function(){var timers=jQuery.timers;for(var i=0;ithis.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var done=true;for(var i in this.options.curAnim)if(this.options.curAnim[i]!==true)done=false;if(done){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(jQuery.css(this.elem,"display")=="none")this.elem.style.display="block";}if(this.options.hide)this.elem.style.display="none";if(this.options.hide||this.options.show)for(var p in this.options.curAnim)jQuery.attr(this.elem.style,p,this.options.orig[p]);}if(done)this.options.complete.call(this.elem);return false;}else{var n=t-this.startTime;this.state=n/this.options.duration;this.pos=jQuery.easing[this.options.easing||(jQuery.easing.swing?"swing":"linear")](this.state,n,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update();}return true;}};jQuery.extend(jQuery.fx,{speeds:{slow:600,fast:200,def:400},step:{scrollLeft:function(fx){fx.elem.scrollLeft=fx.now;},scrollTop:function(fx){fx.elem.scrollTop=fx.now;},opacity:function(fx){jQuery.attr(fx.elem.style,"opacity",fx.now);},_default:function(fx){fx.elem.style[fx.prop]=fx.now+fx.unit;}}});jQuery.fn.offset=function(){var left=0,top=0,elem=this[0],results;if(elem)with(jQuery.browser){var parent=elem.parentNode,offsetChild=elem,offsetParent=elem.offsetParent,doc=elem.ownerDocument,safari2=safari&&parseInt(version)<522&&!/adobeair/i.test(userAgent),css=jQuery.curCSS,fixed=css(elem,"position")=="fixed";if(elem.getBoundingClientRect){var box=elem.getBoundingClientRect();add(box.left+Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),box.top+Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));add(-doc.documentElement.clientLeft,-doc.documentElement.clientTop);}else{add(elem.offsetLeft,elem.offsetTop);while(offsetParent){add(offsetParent.offsetLeft,offsetParent.offsetTop);if(mozilla&&!/^t(able|d|h)$/i.test(offsetParent.tagName)||safari&&!safari2)border(offsetParent);if(!fixed&&css(offsetParent,"position")=="fixed")fixed=true;offsetChild=/^body$/i.test(offsetParent.tagName)?offsetChild:offsetParent;offsetParent=offsetParent.offsetParent;}while(parent&&parent.tagName&&!/^body|html$/i.test(parent.tagName)){if(!/^inline|table.*$/i.test(css(parent,"display")))add(-parent.scrollLeft,-parent.scrollTop);if(mozilla&&css(parent,"overflow")!="visible")border(parent);parent=parent.parentNode;}if((safari2&&(fixed||css(offsetChild,"position")=="absolute"))||(mozilla&&css(offsetChild,"position")!="absolute"))add(-doc.body.offsetLeft,-doc.body.offsetTop);if(fixed)add(Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));}results={top:top,left:left};}function border(elem){add(jQuery.curCSS(elem,"borderLeftWidth",true),jQuery.curCSS(elem,"borderTopWidth",true));}function add(l,t){left+=parseInt(l,10)||0;top+=parseInt(t,10)||0;}return results;};jQuery.fn.extend({position:function(){var left=0,top=0,results;if(this[0]){var offsetParent=this.offsetParent(),offset=this.offset(),parentOffset=/^body|html$/i.test(offsetParent[0].tagName)?{top:0,left:0}:offsetParent.offset();offset.top-=num(this,'marginTop');offset.left-=num(this,'marginLeft');parentOffset.top+=num(offsetParent,'borderTopWidth');parentOffset.left+=num(offsetParent,'borderLeftWidth');results={top:offset.top-parentOffset.top,left:offset.left-parentOffset.left};}return results;},offsetParent:function(){var offsetParent=this[0].offsetParent;while(offsetParent&&(!/^body|html$/i.test(offsetParent.tagName)&&jQuery.css(offsetParent,'position')=='static'))offsetParent=offsetParent.offsetParent;return jQuery(offsetParent);}});jQuery.each(['Left','Top'],function(i,name){var method='scroll'+name;jQuery.fn[method]=function(val){if(!this[0])return;return val!=undefined?this.each(function(){this==window||this==document?window.scrollTo(!i?val:jQuery(window).scrollLeft(),i?val:jQuery(window).scrollTop()):this[method]=val;}):this[0]==window||this[0]==document?self[i?'pageYOffset':'pageXOffset']||jQuery.boxModel&&document.documentElement[method]||document.body[method]:this[0][method];};});jQuery.each(["Height","Width"],function(i,name){var tl=i?"Left":"Top",br=i?"Right":"Bottom";jQuery.fn["inner"+name]=function(){return this[name.toLowerCase()]()+num(this,"padding"+tl)+num(this,"padding"+br);};jQuery.fn["outer"+name]=function(margin){return this["inner"+name]()+num(this,"border"+tl+"Width")+num(this,"border"+br+"Width")+(margin?num(this,"margin"+tl)+num(this,"margin"+br):0);};});})(); \ No newline at end of file diff --git a/doc/doc_index/0.1/_static/minus.png b/doc/doc_index/0.1/_static/minus.png new file mode 100644 index 00000000..da1c5620 Binary files /dev/null and b/doc/doc_index/0.1/_static/minus.png differ diff --git a/doc/doc_index/0.1/_static/plus.png b/doc/doc_index/0.1/_static/plus.png new file mode 100644 index 00000000..b3cb3742 Binary files /dev/null and b/doc/doc_index/0.1/_static/plus.png differ diff --git a/doc/doc_index/0.1/_static/pygments.css b/doc/doc_index/0.1/_static/pygments.css new file mode 100644 index 00000000..1f2d2b61 --- /dev/null +++ b/doc/doc_index/0.1/_static/pygments.css @@ -0,0 +1,61 @@ +.hll { background-color: #ffffcc } +.c { color: #408090; font-style: italic } /* Comment */ +.err { border: 1px solid #FF0000 } /* Error */ +.k { color: #007020; font-weight: bold } /* Keyword */ +.o { color: #666666 } /* Operator */ +.cm { color: #408090; font-style: italic } /* Comment.Multiline */ +.cp { color: #007020 } /* Comment.Preproc */ +.c1 { color: #408090; font-style: italic } /* Comment.Single */ +.cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ +.gd { color: #A00000 } /* Generic.Deleted */ +.ge { font-style: italic } /* Generic.Emph */ +.gr { color: #FF0000 } /* Generic.Error */ +.gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.gi { color: #00A000 } /* Generic.Inserted */ +.go { color: #303030 } /* Generic.Output */ +.gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ +.gs { font-weight: bold } /* Generic.Strong */ +.gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.gt { color: #0040D0 } /* Generic.Traceback */ +.kc { color: #007020; font-weight: bold } /* Keyword.Constant */ +.kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ +.kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ +.kp { color: #007020 } /* Keyword.Pseudo */ +.kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ +.kt { color: #902000 } /* Keyword.Type */ +.m { color: #208050 } /* Literal.Number */ +.s { color: #4070a0 } /* Literal.String */ +.na { color: #4070a0 } /* Name.Attribute */ +.nb { color: #007020 } /* Name.Builtin */ +.nc { color: #0e84b5; font-weight: bold } /* Name.Class */ +.no { color: #60add5 } /* Name.Constant */ +.nd { color: #555555; font-weight: bold } /* Name.Decorator */ +.ni { color: #d55537; font-weight: bold } /* Name.Entity */ +.ne { color: #007020 } /* Name.Exception */ +.nf { color: #06287e } /* Name.Function */ +.nl { color: #002070; font-weight: bold } /* Name.Label */ +.nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ +.nt { color: #062873; font-weight: bold } /* Name.Tag */ +.nv { color: #bb60d5 } /* Name.Variable */ +.ow { color: #007020; font-weight: bold } /* Operator.Word */ +.w { color: #bbbbbb } /* Text.Whitespace */ +.mf { color: #208050 } /* Literal.Number.Float */ +.mh { color: #208050 } /* Literal.Number.Hex */ +.mi { color: #208050 } /* Literal.Number.Integer */ +.mo { color: #208050 } /* Literal.Number.Oct */ +.sb { color: #4070a0 } /* Literal.String.Backtick */ +.sc { color: #4070a0 } /* Literal.String.Char */ +.sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ +.s2 { color: #4070a0 } /* Literal.String.Double */ +.se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ +.sh { color: #4070a0 } /* Literal.String.Heredoc */ +.si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ +.sx { color: #c65d09 } /* Literal.String.Other */ +.sr { color: #235388 } /* Literal.String.Regex */ +.s1 { color: #4070a0 } /* Literal.String.Single */ +.ss { color: #517918 } /* Literal.String.Symbol */ +.bp { color: #007020 } /* Name.Builtin.Pseudo */ +.vc { color: #bb60d5 } /* Name.Variable.Class */ +.vg { color: #bb60d5 } /* Name.Variable.Global */ +.vi { color: #bb60d5 } /* Name.Variable.Instance */ +.il { color: #208050 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/doc/doc_index/0.1/_static/searchtools.js b/doc/doc_index/0.1/_static/searchtools.js new file mode 100644 index 00000000..e0226258 --- /dev/null +++ b/doc/doc_index/0.1/_static/searchtools.js @@ -0,0 +1,467 @@ +/** + * helper function to return a node containing the + * search summary for a given text. keywords is a list + * of stemmed words, hlwords is the list of normal, unstemmed + * words. the first one is used to find the occurance, the + * latter for highlighting it. + */ + +jQuery.makeSearchSummary = function(text, keywords, hlwords) { + var textLower = text.toLowerCase(); + var start = 0; + $.each(keywords, function() { + var i = textLower.indexOf(this.toLowerCase()); + if (i > -1) + start = i; + }); + start = Math.max(start - 120, 0); + var excerpt = ((start > 0) ? '...' : '') + + $.trim(text.substr(start, 240)) + + ((start + 240 - text.length) ? '...' : ''); + var rv = $('
').text(excerpt); + $.each(hlwords, function() { + rv = rv.highlightText(this, 'highlight'); + }); + return rv; +} + +/** + * Porter Stemmer + */ +var PorterStemmer = function() { + + var step2list = { + ational: 'ate', + tional: 'tion', + enci: 'ence', + anci: 'ance', + izer: 'ize', + bli: 'ble', + alli: 'al', + entli: 'ent', + eli: 'e', + ousli: 'ous', + ization: 'ize', + ation: 'ate', + ator: 'ate', + alism: 'al', + iveness: 'ive', + fulness: 'ful', + ousness: 'ous', + aliti: 'al', + iviti: 'ive', + biliti: 'ble', + logi: 'log' + }; + + var step3list = { + icate: 'ic', + ative: '', + alize: 'al', + iciti: 'ic', + ical: 'ic', + ful: '', + ness: '' + }; + + var c = "[^aeiou]"; // consonant + var v = "[aeiouy]"; // vowel + var C = c + "[^aeiouy]*"; // consonant sequence + var V = v + "[aeiou]*"; // vowel sequence + + var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + + +/** + * Search Module + */ +var Search = { + + _index : null, + _queued_query : null, + _pulse_status : -1, + + init : function() { + var params = $.getQueryParameters(); + if (params.q) { + var query = params.q[0]; + $('input[name="q"]')[0].value = query; + this.performSearch(query); + } + }, + + /** + * Sets the index + */ + setIndex : function(index) { + var q; + this._index = index; + if ((q = this._queued_query) !== null) { + this._queued_query = null; + Search.query(q); + } + }, + + hasIndex : function() { + return this._index !== null; + }, + + deferQuery : function(query) { + this._queued_query = query; + }, + + stopPulse : function() { + this._pulse_status = 0; + }, + + startPulse : function() { + if (this._pulse_status >= 0) + return; + function pulse() { + Search._pulse_status = (Search._pulse_status + 1) % 4; + var dotString = ''; + for (var i = 0; i < Search._pulse_status; i++) + dotString += '.'; + Search.dots.text(dotString); + if (Search._pulse_status > -1) + window.setTimeout(pulse, 500); + }; + pulse(); + }, + + /** + * perform a search for something + */ + performSearch : function(query) { + // create the required interface elements + this.out = $('#search-results'); + this.title = $('

' + _('Searching') + '

').appendTo(this.out); + this.dots = $('').appendTo(this.title); + this.status = $('

').appendTo(this.out); + this.output = $('