summaryrefslogtreecommitdiff
path: root/lib/git/objects/tree.py
diff options
context:
space:
mode:
authorSebastian Thiel <byronimo@gmail.com>2010-06-14 12:48:28 +0200
committerSebastian Thiel <byronimo@gmail.com>2010-06-14 12:51:47 +0200
commitd9240918aa03e49feabe43af619019805ac76786 (patch)
tree2db7501a00292f8ba4a99cc1381fe161bd039b03 /lib/git/objects/tree.py
parentfe5289ed8311fecf39913ce3ae86b1011eafe5f7 (diff)
downloadgitpython-d9240918aa03e49feabe43af619019805ac76786.tar.gz
tree: added TreeModifier, allowing to adjust existing trees safely and or fast, while staying compatible with serialization which requires it to be sorted
Diffstat (limited to 'lib/git/objects/tree.py')
-rw-r--r--lib/git/objects/tree.py100
1 files changed, 91 insertions, 9 deletions
diff --git a/lib/git/objects/tree.py b/lib/git/objects/tree.py
index 67aea1cb..0da62060 100644
--- a/lib/git/objects/tree.py
+++ b/lib/git/objects/tree.py
@@ -21,6 +21,84 @@ def sha_to_hex(sha):
return hexsha
+class TreeModifier(object):
+ """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'
+
+ def __init__(self, cache):
+ self._cache = cache
+
+ def _index_by_name(self, name):
+ """:return: index of an item with name, or -1 if not found"""
+ for i, t in enumerate(self._cache):
+ if t[2] == name:
+ return i
+ # END found item
+ # END for each item in cache
+ return -1
+
+ #{ Interface
+ def set_done(self):
+ """Call this method once you are done modifying the tree information.
+ It may be called several times, but be aware that each call will cause
+ a sort operation
+ :return self:"""
+ self._cache.sort(key=lambda t: t[2]) # sort by name
+ return self
+ #} END interface
+
+ #{ Mutators
+ def add(self, hexsha, mode, name, force=False):
+ """Add the given item to the tree. If an item with the given name already
+ 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 hexsha: The 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
+ any existing item with the same name, no matter which information it has
+ :return: self"""
+ if '/' in name:
+ raise ValueError("Name must not contain '/' characters")
+ if len(hexsha) != 40:
+ raise ValueError("Hexsha required, got %r" % hexsha)
+ if (mode >> 12) not in Tree._map_id_to_type:
+ raise ValueError("Invalid object type according to mode %o" % mode)
+
+ index = self._index_by_name(name)
+ item = (hexsha, mode, name)
+ if index == -1:
+ self._cache.append(item)
+ else:
+ if force:
+ self._cache[index] = item
+ else:
+ ex_item = self._cache[index]
+ if ex_item[0] != hexsha or ex_item[1] != mode:
+ raise ValueError("Item %r existed with different properties" % name)
+ # END handle mismatch
+ # END handle force
+ # END handle name exists
+ return self
+
+ def add_unchecked(self, hexsha, mode, name):
+ """Add the given item to the tree, its correctness is assumed, which
+ puts the caller into responsibility to assure the input is correct.
+ For more information on the parameters, see ``add``"""
+ self._cache.append((hexsha, mode, name))
+
+ def __delitem__(self, name):
+ """Deletes an item with the given name if it exists"""
+ index = self._index_by_name(name)
+ if index > -1:
+ del(self._cache[index])
+
+ #} END mutators
+
+
class Tree(base.IndexObject, diff.Diffable, utils.Traversable, utils.Serializable):
"""
Tress represent a ordered list of Blobs and other Trees. Hence it can be
@@ -42,8 +120,8 @@ class Tree(base.IndexObject, diff.Diffable, utils.Traversable, utils.Serializabl
type = "tree"
__slots__ = "_cache"
- # using ascii codes for comparison
- commit_id = 016
+ # actual integer ids for comparison
+ commit_id = 016
blob_id = 010
symlink_id = 012
tree_id = 004
@@ -56,7 +134,7 @@ class Tree(base.IndexObject, diff.Diffable, utils.Traversable, utils.Serializabl
}
- def __init__(self, repo, sha, mode=0, path=None):
+ def __init__(self, repo, sha, mode=tree_id<<12, path=None):
super(Tree, self).__init__(repo, sha, mode, path)
@classmethod
@@ -133,7 +211,6 @@ class Tree(base.IndexObject, diff.Diffable, utils.Traversable, utils.Serializabl
yield (sha_to_hex(sha), mode, name)
# END for each byte in data stream
-
def __div__(self, file):
"""
Find the named object in this tree's contents
@@ -198,6 +275,13 @@ class Tree(base.IndexObject, diff.Diffable, utils.Traversable, utils.Serializabl
"""
return [ i for i in self if i.type == "blob" ]
+ @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 TreeModifier(self._cache)
def traverse( self, predicate = lambda i,d: True,
prune = lambda i,d: False, depth = -1, branch_first=True,
@@ -253,11 +337,9 @@ class Tree(base.IndexObject, diff.Diffable, utils.Traversable, utils.Serializabl
def _serialize(self, stream, presort=False):
"""Serialize this tree into the stream. Please note that we will assume
- our tree data to be in a sorted state. If this is not the case, set the
- presort flag True
- :param presort: if True, default False, sort our tree information before
- writing it to the stream. This should be done if the cache changed
- in the meanwhile"""
+ our tree data to be in a sorted state. If this is not the case, serialization
+ will not generate a correct tree representation as these are assumed to be sorted
+ by algorithms"""
ord_zero = ord('0')
bit_mask = 7 # 3 bits set
hex_to_bin = binascii.a2b_hex