summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS1
-rw-r--r--doc/tutorial.txt7
-rw-r--r--lib/git/blob.py24
-rw-r--r--lib/git/commit.py33
-rw-r--r--lib/git/repo.py68
-rw-r--r--lib/git/tree.py79
-rw-r--r--test/git/test_repo.py14
-rw-r--r--test/git/test_tree.py68
8 files changed, 180 insertions, 114 deletions
diff --git a/AUTHORS b/AUTHORS
index 114959f8..954c889b 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -3,3 +3,4 @@ Alan Briolat
Florian Apolloner <florian _at_ apolloner.eu>
David Aguilar <davvid _at_ gmail.com>
Jelmer Vernooij <jelmer _at_ samba.org>
+Steve Frécinaux <code _at_ istique.net>
diff --git a/doc/tutorial.txt b/doc/tutorial.txt
index 5ad56129..42e015b6 100644
--- a/doc/tutorial.txt
+++ b/doc/tutorial.txt
@@ -124,8 +124,8 @@ 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.contents[-2]
- <GitPython.Tree "e5445b9db4a9f08d5b4de4e29e61dffda2f386ba">
+ >>> contents = tree["lib"]
+ <GitPython.Tree "c1c7214dde86f76bc3e18806ac1f47c38b2b7a3">
>>> contents.name
'test'
@@ -134,7 +134,8 @@ additional attributes.
'040000'
There is a convenience method that allows you to get a named sub-object
-from a tree.
+from a tree with a syntax similar to how paths are written in an unix
+system.
>>> tree/"lib"
<GitPython.Tree "c1c7214dde86f76bc3e18806ac1f47c38b2b7a30">
diff --git a/lib/git/blob.py b/lib/git/blob.py
index 0b4b19c2..80d237d7 100644
--- a/lib/git/blob.py
+++ b/lib/git/blob.py
@@ -14,29 +14,33 @@ from commit import Commit
class Blob(object):
DEFAULT_MIME_TYPE = "text/plain"
- def __init__(self, repo, **kwargs):
+ def __init__(self, repo, id, mode=None, name=None):
"""
Create an unbaked Blob containing just the specified attributes
``repo``
is the Repo
- ``atts``
- is a dict of instance variable data
+ ``id``
+ is the git object id
+
+ ``mode``
+ is the file mode
+
+ ``name``
+ is the file name
Returns
GitPython.Blob
"""
- self.id = None
- self.mode = None
- self.name = None
+ self.repo = repo
+ self.id = id
+ self.mode = mode
+ self.name = name
+
self._size = None
self.data_stored = None
- self.repo = repo
- for k, v in kwargs.items():
- setattr(self, k, v)
-
@property
def size(self):
"""
diff --git a/lib/git/commit.py b/lib/git/commit.py
index 18e36c00..e92293e4 100644
--- a/lib/git/commit.py
+++ b/lib/git/commit.py
@@ -9,12 +9,13 @@ import time
from actor import Actor
from lazy import LazyMixin
-import tree
+from tree import Tree
import diff
import stats
class Commit(LazyMixin):
- def __init__(self, repo, **kwargs):
+ 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
@@ -42,29 +43,29 @@ class Commit(LazyMixin):
``message``
is the first line of the commit message
+ ``parents``
+ is the list of the parents of the commit
+
Returns
GitPython.Commit
"""
LazyMixin.__init__(self)
self.repo = repo
- self.id = None
- self.tree = None
- self.author = None
- self.authored_date = None
- self.committer = None
- self.committed_date = None
- self.message = None
+ self.id = id
self.parents = None
-
- for k, v in kwargs.items():
- setattr(self, k, v)
+ self.tree = None
+ self.author = author
+ self.authored_date = authored_date
+ self.committer = committer
+ self.committed_date = committed_date
+ self.message = message
if self.id:
- if 'parents' in kwargs:
- self.parents = map(lambda p: Commit(repo, id=p), kwargs['parents'])
- if 'tree' in kwargs:
- self.tree = tree.Tree(repo, id=kwargs['tree'])
+ if parents is not None:
+ self.parents = [Commit(repo, p) for p in parents]
+ if tree is not None:
+ self.tree = Tree(repo, id=tree)
def __bake__(self):
temp = Commit.find_all(self.repo, self.id, max_count=1)[0]
diff --git a/lib/git/repo.py b/lib/git/repo.py
index 55f73f66..fdfb7928 100644
--- a/lib/git/repo.py
+++ b/lib/git/repo.py
@@ -62,20 +62,19 @@ class Repo(object):
self.git = Git(self.wd)
- @property
- def description(self):
- """
- The project's description. Taken verbatim from GIT_REPO/description
+ # Description property
+ def _get_description(self):
+ filename = os.path.join(self.path, 'description')
+ return file(filename).read().rstrip()
- Returns
- str
- """
- try:
- f = open(os.path.join(self.path, 'description'))
- result = f.read()
- return result.rstrip()
- finally:
- f.close()
+ def _set_description(self, descr):
+ filename = os.path.join(self.path, 'description')
+ file(filename, 'w').write(descr+'\n')
+
+ description = property(_get_description, _set_description,
+ doc="the project's description")
+ del _get_description
+ del _set_description
@property
def heads(self):
@@ -199,24 +198,22 @@ class Repo(object):
diff_refs = list(set(other_repo_refs) - set(repo_refs))
return map(lambda ref: Commit.find_all(other_repo, ref, max_count=1)[0], diff_refs)
- def tree(self, treeish = 'master', paths = []):
+ def tree(self, treeish = 'master'):
"""
The Tree object for the given treeish reference
``treeish``
is the reference (default 'master')
- ``paths``
- is an optional Array of directory paths to restrict the tree (default [])
Examples::
- repo.tree('master', ['lib/'])
+ repo.tree('master')
Returns
``GitPython.Tree``
"""
- return Tree.construct(self, treeish, paths)
+ return Tree(self, id=treeish)
def blob(self, id):
"""
@@ -377,25 +374,22 @@ class Repo(object):
kwargs['prefix'] = prefix
self.git.archive(treeish, "| gzip", **kwargs)
- def enable_daemon_serve(self):
- """
- Enable git-daemon serving of this repository by writing the
- git-daemon-export-ok file to its git directory
-
- Returns
- None
- """
- touch(os.path.join(self.path, DAEMON_EXPORT_FILE))
-
- def disable_daemon_serve(self):
- """
- Disable git-daemon serving of this repository by ensuring there is no
- git-daemon-export-ok file in its git directory
-
- Returns
- None
- """
- return os.remove(os.path.join(self.path, DAEMON_EXPORT_FILE))
+ def _get_daemon_export(self):
+ filename = os.path.join(self.path, self.DAEMON_EXPORT_FILE)
+ return os.path.exists(filename)
+
+ def _set_daemon_export(self, value):
+ filename = os.path.join(self.path, self.DAEMON_EXPORT_FILE)
+ fileexists = os.path.exists(filename)
+ if value and not fileexists:
+ touch(filename)
+ elif not value and fileexists:
+ os.unlink(filename)
+
+ daemon_export = property(_get_daemon_export, _set_daemon_export,
+ doc="git-daemon export of this repository")
+ del _get_daemon_export
+ del _set_daemon_export
def _get_alternates(self):
"""
diff --git a/lib/git/tree.py b/lib/git/tree.py
index f1aa0b3b..dbd78ac4 100644
--- a/lib/git/tree.py
+++ b/lib/git/tree.py
@@ -9,41 +9,29 @@ from lazy import LazyMixin
import blob
class Tree(LazyMixin):
- def __init__(self, repo, **kwargs):
+ def __init__(self, repo, id, mode=None, name=None):
LazyMixin.__init__(self)
self.repo = repo
- self.id = None
- self.mode = None
- self.name = None
- self.contents = None
-
- for k, v in kwargs.items():
- setattr(self, k, v)
-
- def __bake__(self):
- temp = Tree.construct(self.repo, self.id)
- self.contents = temp.contents
-
- @classmethod
- def construct(cls, repo, treeish, paths = []):
- output = repo.git.ls_tree(treeish, *paths)
- return Tree(repo, id=treeish).construct_initialize(repo, treeish, output)
-
- def construct_initialize(self, repo, id, text):
- self.repo = repo
self.id = id
- self.contents = []
- self.__baked__ = False
-
- for line in text.splitlines():
- self.contents.append(self.content_from_string(self.repo, line))
-
- self.contents = [c for c in self.contents if c is not None]
-
- self.__bake_it__()
- return self
+ self.mode = mode
+ self.name = name
+ self._contents = None
- def content_from_string(self, repo, text):
+ def __bake__(self):
+ # Ensure the treeish references directly a tree
+ treeish = self.id
+ if not treeish.endswith(':'):
+ treeish = treeish + ':'
+
+ # Read the tree contents.
+ self._contents = {}
+ for line in self.repo.git.ls_tree(self.id).splitlines():
+ obj = self.content_from_string(self.repo, line)
+ if obj is not None:
+ self._contents[obj.name] = obj
+
+ @staticmethod
+ def content_from_string(repo, text):
"""
Parse a content item and create the appropriate object
@@ -84,8 +72,7 @@ class Tree(LazyMixin):
Returns
``GitPython.Blob`` or ``GitPython.Tree`` or ``None`` if not found
"""
- contents = [c for c in self.contents if c.name == file]
- return contents and contents[0] or None
+ return self.get(file)
@property
def basename(self):
@@ -93,3 +80,29 @@ class Tree(LazyMixin):
def __repr__(self):
return '<GitPython.Tree "%s">' % self.id
+
+ # Implement the basics of the dict protocol:
+ # directories/trees can be seen as object dicts.
+ def __getitem__(self, key):
+ return self._contents[key]
+
+ def __iter__(self):
+ return iter(self._contents)
+
+ def __len__(self, keys):
+ return len(self._contents)
+
+ def __contains__(self, key):
+ return key in self._contents
+
+ def get(self, key):
+ return self._contents.get(key)
+
+ def items(self):
+ return self._contents.items()
+
+ def keys(self):
+ return self._contents.keys()
+
+ def values(self):
+ return self._contents.values()
diff --git a/test/git/test_repo.py b/test/git/test_repo.py
index 13846555..989dd4fe 100644
--- a/test/git/test_repo.py
+++ b/test/git/test_repo.py
@@ -25,7 +25,9 @@ class TestRepo(object):
Repo("repos/foobar")
def test_description(self):
- assert_equal("Unnamed repository; edit this file to name it for gitweb.", self.repo.description)
+ txt = "Test repository"
+ self.repo.description = txt
+ assert_equal(self.repo.description, txt)
def test_heads_should_return_array_of_head_objects(self):
for head in self.repo.heads:
@@ -96,8 +98,8 @@ class TestRepo(object):
tree = self.repo.tree('master')
- assert_equal(4, len([c for c in tree.contents if isinstance(c, Blob)]))
- assert_equal(3, len([c for c in tree.contents if isinstance(c, Tree)]))
+ assert_equal(4, len([c for c in tree.values() if isinstance(c, Blob)]))
+ assert_equal(3, len([c for c in tree.values() if isinstance(c, Tree)]))
assert_true(git.called)
assert_equal(git.call_args, (('ls_tree', 'master'), {}))
@@ -194,10 +196,12 @@ class TestRepo(object):
@patch('git.utils', 'touch')
def test_enable_daemon_serve(self, touch):
- self.repo.enable_daemon_serve
+ self.repo.daemon_serve = False
+ assert_false(self.repo.daemon_serve)
def test_disable_daemon_serve(self):
- self.repo.disable_daemon_serve
+ self.repo.daemon_serve = True
+ assert_true(self.repo.daemon_serve)
# @patch(os.path, 'exists')
# @patch('__builtin__', 'open')
diff --git a/test/git/test_tree.py b/test/git/test_tree.py
index 957b8962..34aa4d61 100644
--- a/test/git/test_tree.py
+++ b/test/git/test_tree.py
@@ -10,7 +10,6 @@ from git import *
class TestTree(object):
def setup(self):
self.repo = Repo(GIT_REPO)
- self.tree = Tree(self.repo)
@patch(Git, '_call_process')
def test_contents_should_cache(self, git):
@@ -18,9 +17,9 @@ class TestTree(object):
tree = self.repo.tree('master')
- child = tree.contents[-1]
- child.contents
- child.contents
+ child = tree['grit']
+ child.items()
+ child.items()
assert_true(git.called)
assert_equal(2, git.call_count)
@@ -28,7 +27,7 @@ class TestTree(object):
def test_content_from_string_tree_should_return_tree(self):
text = fixture('ls_tree_a').splitlines()[-1]
- tree = self.tree.content_from_string(None, text)
+ tree = Tree.content_from_string(None, text)
assert_equal(Tree, tree.__class__)
assert_equal("650fa3f0c17f1edb4ae53d8dcca4ac59d86e6c44", tree.id)
@@ -38,7 +37,7 @@ class TestTree(object):
def test_content_from_string_tree_should_return_blob(self):
text = fixture('ls_tree_b').split("\n")[0]
- tree = self.tree.content_from_string(None, text)
+ tree = Tree.content_from_string(None, text)
assert_equal(Blob, tree.__class__)
assert_equal("aa94e396335d2957ca92606f909e53e7beaf3fbb", tree.id)
@@ -48,12 +47,12 @@ class TestTree(object):
def test_content_from_string_tree_should_return_commit(self):
text = fixture('ls_tree_commit').split("\n")[1]
- tree = self.tree.content_from_string(None, text)
+ tree = Tree.content_from_string(None, text)
assert_none(tree)
@raises(TypeError)
def test_content_from_string_invalid_type_should_raise(self):
- self.tree.content_from_string(None, "040000 bogus 650fa3f0c17f1edb4ae53d8dcca4ac59d86e6c44 test")
+ Tree.content_from_string(None, "040000 bogus 650fa3f0c17f1edb4ae53d8dcca4ac59d86e6c44 test")
@patch(Blob, 'size')
@patch(Git, '_call_process')
@@ -96,6 +95,55 @@ class TestTree(object):
assert_true(git.called)
assert_equal(git.call_args, (('ls_tree', 'master'), {}))
+ @patch(Blob, 'size')
+ @patch(Git, '_call_process')
+ def test_dict(self, blob, git):
+ git.return_value = fixture('ls_tree_a')
+ blob.return_value = 1
+
+ tree = self.repo.tree('master')
+
+ assert_equal('aa06ba24b4e3f463b3c4a85469d0fb9e5b421cf8', tree['lib'].id)
+ assert_equal('8b1e02c0fb554eed2ce2ef737a68bb369d7527df', tree['README.txt'].id)
+
+ assert_true(git.called)
+ assert_equal(git.call_args, (('ls_tree', 'master'), {}))
+
+ @patch(Blob, 'size')
+ @patch(Git, '_call_process')
+ def test_dict_with_zero_length_file(self, blob, git):
+ git.return_value = fixture('ls_tree_a')
+ blob.return_value = 0
+
+ tree = self.repo.tree('master')
+
+ assert_not_none(tree['README.txt'])
+ assert_equal('8b1e02c0fb554eed2ce2ef737a68bb369d7527df', tree['README.txt'].id)
+
+ assert_true(git.called)
+ assert_equal(git.call_args, (('ls_tree', 'master'), {}))
+
+ @patch(Git, '_call_process')
+ def test_dict_with_commits(self, git):
+ git.return_value = fixture('ls_tree_commit')
+
+ tree = self.repo.tree('master')
+
+ assert_none(tree.get('bar'))
+ assert_equal('2afb47bcedf21663580d5e6d2f406f08f3f65f19', tree['foo'].id)
+ assert_equal('f623ee576a09ca491c4a27e48c0dfe04be5f4a2e', tree['baz'].id)
+
+ assert_true(git.called)
+ assert_equal(git.call_args, (('ls_tree', 'master'), {}))
+
+ @patch(Git, '_call_process')
+ @raises(KeyError)
+ def test_dict_with_non_existant_file(self, git):
+ git.return_value = fixture('ls_tree_commit')
+
+ tree = self.repo.tree('master')
+ tree['bar']
+
def test_repr(self):
- self.tree = Tree(self.repo, **{'id': 'abc'})
- assert_equal('<GitPython.Tree "abc">', repr(self.tree))
+ tree = Tree(self.repo, id='abc')
+ assert_equal('<GitPython.Tree "abc">', repr(tree))