summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/git/actor.py3
-rw-r--r--lib/git/blob.py17
-rw-r--r--lib/git/cmd.py54
-rw-r--r--lib/git/commit.py81
-rw-r--r--lib/git/errors.py14
-rw-r--r--lib/git/head.py21
-rw-r--r--lib/git/repo.py80
-rw-r--r--lib/git/stats.py32
-rw-r--r--lib/git/tag.py25
-rw-r--r--test/git/test_repo.py4
10 files changed, 245 insertions, 86 deletions
diff --git a/lib/git/actor.py b/lib/git/actor.py
index cdcc02f8..bc1a4479 100644
--- a/lib/git/actor.py
+++ b/lib/git/actor.py
@@ -7,6 +7,9 @@
import re
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."""
def __init__(self, name, email):
self.name = name
self.email = email
diff --git a/lib/git/blob.py b/lib/git/blob.py
index a15c5466..82a41f73 100644
--- a/lib/git/blob.py
+++ b/lib/git/blob.py
@@ -12,6 +12,7 @@ from actor import Actor
from commit import Commit
class Blob(object):
+ """A Blob encapsulates a git blob object"""
DEFAULT_MIME_TYPE = "text/plain"
def __init__(self, repo, id, mode=None, name=None):
@@ -48,6 +49,9 @@ class Blob(object):
Returns
int
+
+ NOTE
+ The size will be cached after the first access
"""
if self._size is None:
self._size = int(self.repo.git.cat_file(self.id, s=True).rstrip())
@@ -60,6 +64,9 @@ class Blob(object):
Returns
str
+
+ NOTE
+ The data will be cached after the first access.
"""
self.data_stored = self.data_stored or self.repo.git.cat_file(self.id, p=True, with_raw_output=True)
return self.data_stored
@@ -71,6 +78,9 @@ class Blob(object):
Returns
str
+
+ NOTE
+ Defaults to 'text/plain' in case the actual file type is unknown.
"""
guesses = None
if self.name:
@@ -79,6 +89,10 @@ class Blob(object):
@property
def basename(self):
+ """
+ Returns
+ The basename of the Blobs file name
+ """
return os.path.basename(self.name)
@classmethod
@@ -88,6 +102,9 @@ class Blob(object):
Returns
list: [git.Commit, list: [<line>]]
+ A list of tuples associating a Commit object with a list of lines that
+ changed within the given commit. The Commit objects will be given in order
+ of appearance.
"""
data = repo.git.blame(commit, '--', file, p=True)
commits = {}
diff --git a/lib/git/cmd.py b/lib/git/cmd.py
index a1b85cdc..aef53350 100644
--- a/lib/git/cmd.py
+++ b/lib/git/cmd.py
@@ -22,19 +22,47 @@ if sys.platform == 'win32':
class Git(object):
"""
- The Git class manages communication with the Git binary
+ The Git class manages communication with the Git binary.
+
+ It provides a convenient interface to calling the Git binary, such as in::
+
+ g = Git( git_dir )
+ g.init() # calls 'git init' program
+ rval = g.ls_files() # calls 'git ls-files' program
+
+ ``Debugging``
+ Set the GIT_PYTHON_TRACE environment variable print each invocation
+ of the command to stdout.
+ Set its value to 'full' to see details about the returned values.
"""
def __init__(self, git_dir=None):
+ """
+ Initialize this instance with:
+
+ ``git_dir``
+ Git directory we should work in. If None, we always work in the current
+ directory as returned by os.getcwd()
+ """
super(Git, self).__init__()
self.git_dir = git_dir
def __getattr__(self, name):
+ """
+ A convenience method as it allows to call the command as if it was
+ an object.
+ Returns
+ Callable object that will execute call _call_process with your arguments.
+ """
if name[:1] == '_':
raise AttributeError(name)
return lambda *args, **kwargs: self._call_process(name, *args, **kwargs)
@property
def get_dir(self):
+ """
+ Returns
+ Git directory we are working on
+ """
return self.git_dir
def execute(self, command,
@@ -49,7 +77,9 @@ class Git(object):
the returned information (stdout)
``command``
- The command argument list to execute
+ The command argument list to execute.
+ It should be a string, or a sequence of program arguments. The
+ program to execute is the first item in the args sequence or string.
``istream``
Standard input filehandle passed to subprocess.Popen.
@@ -68,11 +98,18 @@ class Git(object):
``with_raw_output``
Whether to avoid stripping off trailing whitespace.
- Returns
- str(output) # extended_output = False (Default)
- tuple(int(status), str(output)) # extended_output = True
+ Returns::
+
+ str(output) # extended_output = False (Default)
+ tuple(int(status), str(stdout), str(stderr)) # extended_output = True
+
+ Raise
+ GitCommandError
+
+ NOTE
+ If you add additional keyword arguments to the signature of this method,
+ you must update the execute_kwargs tuple housed in this module.
"""
-
if GIT_PYTHON_TRACE and not GIT_PYTHON_TRACE == 'full':
print ' '.join(command)
@@ -146,7 +183,8 @@ class Git(object):
the result as a String
``method``
- is the command
+ is the command. Contained "_" characters will be converted to dashes,
+ such as in 'ls_files' to call 'ls-files'.
``args``
is the list of arguments
@@ -156,7 +194,7 @@ class Git(object):
This function accepts the same optional keyword arguments
as execute().
- Examples
+ Examples::
git.rev_list('master', max_count=10, header=True)
Returns
diff --git a/lib/git/commit.py b/lib/git/commit.py
index ba7a7102..edfe47ca 100644
--- a/lib/git/commit.py
+++ b/lib/git/commit.py
@@ -14,38 +14,44 @@ import diff
import stats
class Commit(LazyMixin):
+ """
+ Wraps a git Commit object.
+
+ This class will act lazily on some of its attributes and will query the
+ value on demand only if it involves calling the git binary.
+ """
def __init__(self, repo, id, tree=None, author=None, authored_date=None,
committer=None, committed_date=None, message=None, parents=None):
"""
- Instantiate a new Commit
+ Instantiate a new Commit. All keyword arguments taking None as default will
+ be implicitly set if id names a valid sha.
+
+ The parameter documentation indicates the type of the argument after a colon ':'.
``id``
- is the id of the commit
+ is the sha id of the commit
- ``parents``
- is a list of commit ids (will be converted into Commit instances)
+ ``parents`` : list( Commit, ... )
+ is a list of commit ids
- ``tree``
- is the correspdonding tree id (will be converted into a Tree object)
+ ``tree`` : Tree
+ is the corresponding tree id
- ``author``
- is the author string
+ ``author`` : Actor
+ is the author string ( will be implicitly converted into an Actor object )
- ``authored_date``
+ ``authored_date`` : (tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, tm_wday, tm_yday, tm_isdst )
is the authored DateTime
- ``committer``
+ ``committer`` : Actor
is the committer string
- ``committed_date``
+ ``committed_date`` : (tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, tm_wday, tm_yday, tm_isdst)
is the committed DateTime
- ``message``
+ ``message`` : string
is the commit message
- ``parents``
- is the list of the parents of the commit
-
Returns
git.Commit
"""
@@ -74,6 +80,10 @@ class Commit(LazyMixin):
return self.id != other.id
def __bake__(self):
+ """
+ Called by LazyMixin superclass when the first uninitialized member needs
+ to be set as it is queried.
+ """
temp = Commit.find_all(self.repo, self.id, max_count=1)[0]
self.parents = temp.parents
self.tree = temp.tree
@@ -85,10 +95,18 @@ class Commit(LazyMixin):
@property
def id_abbrev(self):
+ """
+ Returns
+ First 7 bytes of the commit's sha id as an abbreviation of the full string.
+ """
return self.id[0:7]
@property
def summary(self):
+ """
+ Returns
+ First line of the commit message.
+ """
return self.message.split('\n', 1)[0]
@classmethod
@@ -122,10 +140,11 @@ class Commit(LazyMixin):
is the ref from which to begin (SHA1 or name)
``path``
- is an optinal path
+ is an optinal path, if set only Commits that include the path
+ will be considered
- ``options``
- is a Hash of optional arguments to git where
+ ``kwargs``
+ optional keyword arguments to git where
``max_count`` is the maximum number of commits to fetch
``skip`` is the number of commits to skip
@@ -147,7 +166,7 @@ class Commit(LazyMixin):
is the Repo
``text``
- is the text output from the git command (raw format)
+ is the text output from the git-rev-list command (raw format)
Returns
git.Commit[]
@@ -180,7 +199,7 @@ class Commit(LazyMixin):
@classmethod
def diff(cls, repo, a, b=None, paths=None):
"""
- Show diffs between two trees:
+ Creates diffs between a tree and the index or between two trees:
``repo``
is the Repo
@@ -194,10 +213,13 @@ class Commit(LazyMixin):
given paths.
``paths``
- is a list of paths to limit the diff.
+ is a list of paths to limit the diff to.
Returns
- git.Diff[]
+ git.Diff[]::
+
+ between tree and the index if only a is given
+ between two trees if a and b are given and are commits
"""
paths = paths or []
@@ -216,6 +238,12 @@ class Commit(LazyMixin):
@property
def diffs(self):
+ """
+ Returns
+ git.Diff[]
+ Diffs between this commit and its first parent or all changes if this
+ commit is the first commit and has no parent.
+ """
if not self.parents:
d = self.repo.git.show(self.id, '-M', full_index=True, pretty='raw')
if re.search(r'diff --git a', d):
@@ -230,6 +258,13 @@ class Commit(LazyMixin):
@property
def stats(self):
+ """
+ Create a git stat from changes between this commit and its first parent
+ or from all changes done if this is the very first commit.
+
+ Return
+ git.Stats
+ """
if not self.parents:
text = self.repo.git.diff_tree(self.id, '--', numstat=True, root=True)
text2 = ""
@@ -254,7 +289,7 @@ class Commit(LazyMixin):
Parse out the actor (author or committer) info
Returns
- [str (actor name and email), time (acted at time)]
+ [Actor, gmtime(acted at time)]
"""
m = re.search(r'^.+? (.*) (\d+) .*$', line)
actor, epoch = m.groups()
diff --git a/lib/git/errors.py b/lib/git/errors.py
index bf882d33..2632d5f3 100644
--- a/lib/git/errors.py
+++ b/lib/git/errors.py
@@ -3,14 +3,24 @@
#
# 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):
- pass
+ """
+ Thrown if the given repository appears to have an invalid format.
+ """
class NoSuchPathError(Exception):
- pass
+ """
+ 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
diff --git a/lib/git/head.py b/lib/git/head.py
index 86686f17..639cee40 100644
--- a/lib/git/head.py
+++ b/lib/git/head.py
@@ -28,16 +28,13 @@ class Head(object):
def __init__(self, name, commit):
"""
- Instantiate a new Head
+ Initialize a newly instanced Head
`name`
is the name of the head
`commit`
- is the Commit that the head points to
-
- Returns
- git.Head
+ is the Commit object that the head points to
"""
self.name = name
self.commit = commit
@@ -45,16 +42,19 @@ class Head(object):
@classmethod
def find_all(cls, repo, **kwargs):
"""
- Find all Heads
+ Find all Heads in the repository
`repo`
is the Repo
`kwargs`
- is a dict of options
+ Additional options given as keyword arguments, will be passed
+ to git-for-each-ref
Returns
git.Head[]
+
+ List is sorted by committerdate
"""
options = {'sort': "committerdate",
@@ -67,12 +67,12 @@ class Head(object):
@classmethod
def list_from_string(cls, repo, text):
"""
- Parse out head information into an array of baked head objects
+ Parse out head information into a list of head objects
``repo``
is the Repo
``text``
- is the text output from the git command
+ is the text output from the git-for-each-ref command
Returns
git.Head[]
@@ -95,7 +95,8 @@ class Head(object):
``line``
is the formatted head information
- Format
+ Format::
+
name: [a-zA-Z_/]+
<null byte>
id: [0-9A-Fa-f]{40}
diff --git a/lib/git/repo.py b/lib/git/repo.py
index b7ffcb61..1c4b4095 100644
--- a/lib/git/repo.py
+++ b/lib/git/repo.py
@@ -18,6 +18,11 @@ from commit import Commit
from tree import Tree
class Repo(object):
+ """
+ Represents a git repository and allows you to query references,
+ gather commit information, generate diffs, create and clone repositories query
+ the log.
+ """
DAEMON_EXPORT_FILE = 'git-daemon-export-ok'
def __init__(self, path=None):
@@ -32,6 +37,9 @@ class Repo(object):
repo = Repo("/Users/mtrier/Development/git-python")
repo = Repo("/Users/mtrier/Development/git-python.git")
+ Raises
+ InvalidGitRepositoryError or NoSuchPathError
+
Returns
``git.Repo``
"""
@@ -110,13 +118,15 @@ class Repo(object):
is the branch/commit name (default 'master')
``path``
- is an optional path
+ is an optional path to limit the returned commits to
+ Commits that do not contain that path will not be returned.
``max_count``
is the maximum number of commits to return (default 10)
``skip``
- is the number of commits to skip (default 0)
+ is the number of commits to skip (default 0) which will effectively
+ move your commit-window by the given number.
Returns
``git.Commit[]``
@@ -126,7 +136,7 @@ class Repo(object):
return Commit.find_all(self, start, path, **options)
- def commits_between(self, frm, to, path = ''):
+ def commits_between(self, frm, to):
"""
The Commits objects that are reachable via ``to`` but not via ``frm``
Commits are returned in chronological order.
@@ -137,9 +147,6 @@ class Repo(object):
``to``
is the branch/commit name of the older item
- ``path``
- is an optional path
-
Returns
``git.Commit[]``
"""
@@ -154,7 +161,8 @@ class Repo(object):
is the branch/commit name (default 'master')
``path``
- is an optinal path
+ is an optinal path to limit the returned commits to.
+
``since``
is a string represeting a date/time
@@ -174,10 +182,11 @@ class Repo(object):
is the branch/commit name (default 'master')
``path``
- is an optinal path
+ is an optional path
+ Commits that do not contain the path will not contribute to the count.
Returns
- int
+ ``int``
"""
return Commit.count(self, start, path)
@@ -189,17 +198,17 @@ class Repo(object):
is the SHA1 identifier of the commit
``path``
- is an optinal path
+ is an optional path, if set the returned commit must contain the path.
Returns
- git.Commit
+ ``git.Commit``
"""
options = {'max_count': 1}
commits = Commit.find_all(self, id, path, **options)
if not commits:
- raise ValueError, 'Invalid identifier %s' % id
+ raise ValueError, "Invalid identifier %s, or given path '%s' too restrictive" % ( id, path )
return commits[0]
def commit_deltas_from(self, other_repo, ref='master', other_ref='master'):
@@ -207,7 +216,7 @@ class Repo(object):
Returns a list of commits that is in ``other_repo`` but not in self
Returns
- ``git.Commit[]``
+ git.Commit[]
"""
repo_refs = self.git.rev_list(ref, '--').strip().splitlines()
other_repo_refs = other_repo.git.rev_list(other_ref, '--').strip().splitlines()
@@ -246,7 +255,11 @@ class Repo(object):
def log(self, commit='master', path=None, **kwargs):
"""
- The commit log for a treeish
+ The Commit for a treeish, and all commits leading to it.
+
+ ``kwargs``
+ keyword arguments specifying flags to be used in git-log command,
+ i.e.: max_count=1 to limit the amount of commits returned
Returns
``git.Commit[]``
@@ -270,6 +283,9 @@ class Repo(object):
``paths``
is an optional list of file paths on which to restrict the diff
+
+ Returns
+ ``str``
"""
return self.git.diff(a, b, '--', *paths)
@@ -296,7 +312,7 @@ class Repo(object):
already exists. Creates the directory with a mode=0755.
``kwargs``
- is any additional options to the git init command
+ keyword arguments serving as additional options to the git init command
Examples::
@@ -321,8 +337,8 @@ class Repo(object):
``path``
is the full path of the new repo (traditionally ends with /<name>.git)
- ``options``
- is any additional options to the git clone command
+ ``kwargs``
+ keyword arguments to be given to the git clone command
Returns
``git.Repo`` (the newly forked repo)
@@ -340,7 +356,7 @@ class Repo(object):
is the treeish name/id (default 'master')
``prefix``
- is the optional prefix
+ is the optional prefix to prepend to each filename in the archive
Examples::
@@ -351,10 +367,10 @@ class Repo(object):
<String containing tar archive for commit a87ff14>
>>> repo.archive_tar('master', 'myproject/')
- <String containing tar archive and prefixed with 'myproject/'>
+ <String containing tar bytes archive, whose files are prefixed with 'myproject/'>
Returns
- str (containing tar archive)
+ str (containing bytes of tar archive)
"""
options = {}
if prefix:
@@ -369,7 +385,7 @@ class Repo(object):
is the treeish name/id (default 'master')
``prefix``
- is the optional prefix
+ is the optional prefix to prepend to each filename in the archive
Examples::
@@ -383,7 +399,7 @@ class Repo(object):
<String containing tar.gz archive and prefixed with 'myproject/'>
Returns
- str (containing tar.gz archive)
+ str (containing the bytes of tar.gz archive)
"""
kwargs = {}
if prefix:
@@ -408,16 +424,16 @@ class Repo(object):
os.unlink(filename)
daemon_export = property(_get_daemon_export, _set_daemon_export,
- doc="git-daemon export of this repository")
+ doc="If True, git-daemon may export this repository")
del _get_daemon_export
del _set_daemon_export
def _get_alternates(self):
"""
- The list of alternates for this repo
+ The list of alternates for this repo from which objects can be retrieved
Returns
- list[str] (pathnames of alternates)
+ list of strings being pathnames of alternates
"""
alternates_path = os.path.join(self.path, 'objects', 'info', 'alternates')
@@ -436,8 +452,12 @@ class Repo(object):
Sets the alternates
``alts``
- is the Array of String paths representing the alternates
+ is the array of string paths representing the alternates at which
+ git should look for objects, i.e. /home/user/repo/.git/objects
+ Raises
+ NoSuchPathError
+
Returns
None
"""
@@ -454,17 +474,19 @@ class Repo(object):
finally:
f.close()
- alternates = property(_get_alternates, _set_alternates)
+ alternates = property(_get_alternates, _set_alternates, doc="Retrieve a list of alternates paths or set a list paths to be used as alternates")
@property
def is_dirty(self):
"""
- Return the status of the working directory.
+ Return the status of the index.
Returns
- ``True``, if the working directory has any uncommitted changes,
+ ``True``, if the index has any uncommitted changes,
otherwise ``False``
+ NOTE
+ Working tree changes that have not been staged will not be detected !
"""
if self.bare:
# Bare repositories with no associated working directory are
diff --git a/lib/git/stats.py b/lib/git/stats.py
index 74a23126..307e2f2f 100644
--- a/lib/git/stats.py
+++ b/lib/git/stats.py
@@ -5,6 +5,32 @@
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
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
+
+ """
def __init__(self, repo, total, files):
self.repo = repo
self.total = total
@@ -12,6 +38,12 @@ class Stats(object):
@classmethod
def list_from_string(cls, repo, text):
+ """
+ Create a Stat object from output retrieved by git-diff.
+
+ Returns
+ git.Stat
+ """
hsh = {'total': {'insertions': 0, 'deletions': 0, 'lines': 0, 'files': 0}, 'files': {}}
for line in text.splitlines():
(raw_insertions, raw_deletions, filename) = line.split("\t")
diff --git a/lib/git/tag.py b/lib/git/tag.py
index f7bc140e..8413ce73 100644
--- a/lib/git/tag.py
+++ b/lib/git/tag.py
@@ -9,16 +9,13 @@ from commit import Commit
class Tag(object):
def __init__(self, name, commit):
"""
- Instantiate a new Tag
+ Initialize a newly instantiated Tag
``name``
is the name of the head
``commit``
is the Commit that the head points to
-
- Returns
- ``git.Tag``
"""
self.name = name
self.commit = commit
@@ -26,16 +23,19 @@ class Tag(object):
@classmethod
def find_all(cls, repo, **kwargs):
"""
- Find all Tags
+ Find all Tags in the repository
``repo``
is the Repo
``kwargs``
- is a dict of options
+ Additional options given as keyword arguments, will be passed
+ to git-for-each-ref
Returns
``git.Tag[]``
+
+ List is sorted by committerdate
"""
options = {'sort': "committerdate",
'format': "%(refname)%00%(objectname)"}
@@ -47,16 +47,16 @@ class Tag(object):
@classmethod
def list_from_string(cls, repo, text):
"""
- Parse out tag information into an array of baked Tag objects
+ Parse out tag information into an array of Tag objects
``repo``
is the Repo
``text``
- is the text output from the git command
+ is the text output from the git-for-each command
Returns
- ``git.Tag[]``
+ git.Tag[]
"""
tags = []
for line in text.splitlines():
@@ -74,13 +74,14 @@ class Tag(object):
``line``
is the formatted tag information
- Format
+ Format::
+
name: [a-zA-Z_/]+
<null byte>
id: [0-9A-Fa-f]{40}
-
+
Returns
- ``git.Tag``
+ git.Tag
"""
full_name, ids = line.split("\x00")
name = full_name.split("/")[-1]
diff --git a/test/git/test_repo.py b/test/git/test_repo.py
index 1c5b50d9..a9d3beaf 100644
--- a/test/git/test_repo.py
+++ b/test/git/test_repo.py
@@ -193,10 +193,10 @@ class TestRepo(object):
assert_true(git.called)
def test_archive_tar(self):
- self.repo.archive_tar()
+ assert self.repo.archive_tar()
def test_archive_tar_gz(self):
- self.repo.archive_tar_gz()
+ assert self.repo.archive_tar_gz()
@patch('git.utils.touch')
def test_enable_daemon_serve(self, touch):