summaryrefslogtreecommitdiff
path: root/lib/git
diff options
context:
space:
mode:
Diffstat (limited to 'lib/git')
-rw-r--r--lib/git/index/base.py97
-rw-r--r--lib/git/index/typ.py34
-rw-r--r--lib/git/index/util.py10
-rw-r--r--lib/git/objects/tree.py2
4 files changed, 83 insertions, 60 deletions
diff --git a/lib/git/index/base.py b/lib/git/index/base.py
index 5d3e7760..7354c0a8 100644
--- a/lib/git/index/base.py
+++ b/lib/git/index/base.py
@@ -9,15 +9,29 @@ import binascii
import tempfile
import os
import sys
-import stat
import subprocess
import glob
from cStringIO import StringIO
-from typ import *
+from stat import (
+ S_ISLNK,
+ S_ISDIR,
+ S_IFMT,
+ S_IFDIR,
+ S_IFLNK,
+ S_IFREG
+ )
+
+from typ import (
+ BaseIndexEntry,
+ IndexEntry,
+ CE_NAMEMASK,
+ CE_STAGESHIFT
+ )
+
from util import (
TemporaryFileSwap,
- clear_cache,
+ post_clear_cache,
default_index,
pack,
unpack
@@ -75,7 +89,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable):
"""
__slots__ = ( "repo", "version", "entries", "_extension_data", "_file_path" )
_VERSION = 2 # latest version we support
- S_IFGITLINK = 0160000
+ S_IFGITLINK = 0160000 # a submodule
def __init__(self, repo, file_path=None):
"""
@@ -141,12 +155,12 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable):
mtime = unpack(">8s", stream.read(8))[0]
(dev, ino, mode, uid, gid, size, sha, flags) = \
unpack(">LLLLLL20sH", stream.read(20 + 4 * 6 + 2))
- path_size = flags & 0x0fff
+ path_size = flags & CE_NAMEMASK
path = stream.read(path_size)
real_size = ((stream.tell() - beginoffset + 8) & ~7)
data = stream.read((beginoffset + real_size) - stream.tell())
- return IndexEntry((mode, binascii.hexlify(sha), flags >> 12, path, ctime, mtime, dev, ino, uid, gid, size))
+ return IndexEntry((mode, binascii.hexlify(sha), flags, path, ctime, mtime, dev, ino, uid, gid, size))
@classmethod
def _read_header(cls, stream):
@@ -198,7 +212,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable):
# body
entries_sorted = self.entries.values()
- entries_sorted.sort(key=lambda e: (e[3], e[2])) # use path/stage as sort key
+ entries_sorted.sort(key=lambda e: (e[3], e.stage)) # use path/stage as sort key
for entry in entries_sorted:
self._write_cache_entry(stream, entry)
# END for each entry
@@ -226,17 +240,18 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable):
def _write_cache_entry(cls, stream, entry):
""" Write an IndexEntry to a stream """
beginoffset = stream.tell()
- stream.write(entry[4]) # ctime
- stream.write(entry[5]) # mtime
+ write = stream.write
+ write(entry[4]) # ctime
+ write(entry[5]) # mtime
path = entry[3]
- plen = len(path) & 0x0fff # path length
+ plen = len(path) & CE_NAMEMASK # path length
assert plen == len(path), "Path %s too long to fit into index" % entry[3]
- flags = plen | (entry[2] << 12)# stage and path length are 2 byte flags
- stream.write(pack(">LLLLLL20sH", entry[6], entry[7], entry[0],
+ flags = plen | entry[2]
+ write(pack(">LLLLLL20sH", entry[6], entry[7], entry[0],
entry[8], entry[9], entry[10], binascii.unhexlify(entry[1]), flags))
- stream.write(path)
+ write(path)
real_size = ((stream.tell() - beginoffset + 8) & ~7)
- stream.write("\0" * ((beginoffset + real_size) - stream.tell()))
+ write("\0" * ((beginoffset + real_size) - stream.tell()))
def write(self, file_path = None, ignore_tree_extension_data=False):
"""
@@ -272,7 +287,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable):
if file_path is not None:
self._file_path = file_path
- @clear_cache
+ @post_clear_cache
@default_index
def merge_tree(self, rhs, base=None):
"""Merge the given rhs treeish into the current index, possibly taking
@@ -383,24 +398,14 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable):
return index
@classmethod
- def _index_mode_to_tree_index_mode(cls, index_mode):
- """
- Cleanup a index_mode value.
- This will return a index_mode that can be stored in a tree object.
-
- ``index_mode``
- Index_mode to clean up.
- """
- if stat.S_ISLNK(index_mode):
- return stat.S_IFLNK
- elif stat.S_ISDIR(index_mode):
- return stat.S_IFDIR
- elif stat.S_IFMT(index_mode) == cls.S_IFGITLINK:
+ def _stat_mode_to_index_mode(cls, mode):
+ """Convert the given mode from a stat call to the corresponding index mode
+ and return it"""
+ if S_ISLNK(mode): # symlinks
+ return S_IFLNK
+ if S_ISDIR(mode) or S_IFMT(mode) == cls.S_IFGITLINK: # submodules
return cls.S_IFGITLINK
- ret = stat.S_IFREG | 0644
- ret |= (index_mode & 0111)
- return ret
-
+ return S_IFREG | 644 | (mode & 0100) # blobs with or without executable bit
# UTILITIES
def _iter_expand_paths(self, paths):
@@ -479,7 +484,9 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable):
only if they match a given list of paths.
"""
for entry in self.entries.itervalues():
- mode = self._index_mode_to_tree_index_mode(entry.mode)
+ # TODO: is it necessary to convert the mode ? We did that when adding
+ # it to the index, right ?
+ mode = self._stat_mode_to_index_mode(entry.mode)
blob = Blob(self.repo, entry.sha, mode, entry.path)
blob.size = entry.size
output = (entry.stage, blob)
@@ -636,7 +643,6 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable):
# END for each item
return (paths, entries)
- @clear_cache
@default_index
def add(self, items, force=True, fprogress=lambda *args: None, path_rewriter=None):
"""Add files from the working tree, specific blobs or BaseIndexEntries
@@ -739,7 +745,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable):
"""Store file at filepath in the database and return the base index entry"""
st = os.lstat(filepath) # handles non-symlinks as well
stream = None
- if stat.S_ISLNK(st.st_mode):
+ if S_ISLNK(st.st_mode):
stream = StringIO(os.readlink(filepath))
else:
stream = open(filepath, 'rb')
@@ -759,13 +765,6 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable):
for filepath in self._iter_expand_paths(paths):
entries_added.append(store_path(filepath))
# END for each filepath
-
- # add the new entries to this instance, and write it
- for entry in entries_added:
- self.entries[(entry.path, 0)] = IndexEntry.from_base(entry)
-
- # finally write the changed index
- self.write()
# END path handling
@@ -823,6 +822,14 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable):
self._flush_stdin_and_wait(proc, ignore_stdout=True)
entries_added.extend(entries)
# END if there are base entries
+
+ # FINALIZE
+ # add the new entries to this instance, and write it
+ for entry in entries_added:
+ self.entries[(entry.path, 0)] = IndexEntry.from_base(entry)
+
+ # finally write the changed index
+ self.write()
return entries_added
@@ -840,7 +847,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable):
# END for each item
return paths
- @clear_cache
+ @post_clear_cache
@default_index
def remove(self, items, working_tree=False, **kwargs):
"""
@@ -893,7 +900,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable):
# rm 'path'
return [ p[4:-1] for p in removed_paths ]
- @clear_cache
+ @post_clear_cache
@default_index
def move(self, items, skip_errors=False, **kwargs):
"""
@@ -1127,7 +1134,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable):
# END paths handling
assert "Should not reach this point"
- @clear_cache
+ @post_clear_cache
@default_index
def reset(self, commit='HEAD', working_tree=False, paths=None, head=False, **kwargs):
"""
diff --git a/lib/git/index/typ.py b/lib/git/index/typ.py
index abb0b131..a8588792 100644
--- a/lib/git/index/typ.py
+++ b/lib/git/index/typ.py
@@ -7,6 +7,15 @@ from util import (
__all__ = ('BlobFilter', 'BaseIndexEntry', 'IndexEntry')
+#{ Invariants
+CE_NAMEMASK = 0x0fff
+CE_STAGEMASK = 0x3000
+CE_EXTENDED = 0x4000
+CE_VALID = 0x8000
+CE_STAGESHIFT = 12
+
+#} END invariants
+
class BlobFilter(object):
"""
Predicate to be used by iter_blobs allowing to filter only return blobs which
@@ -64,17 +73,22 @@ class BaseIndexEntry(tuple):
:note: For more information, see http://www.kernel.org/pub/software/scm/git/docs/git-read-tree.html
"""
- return self[2]
+ return (self[2] & CE_STAGEMASK) >> CE_STAGESHIFT
@property
def path(self):
""":return: our path relative to the repository working tree root"""
return self[3]
+ @property
+ def flags(self):
+ """:return: flags stored with this entry"""
+ return self[2]
+
@classmethod
def from_blob(cls, blob, stage = 0):
""":return: Fully equipped BaseIndexEntry at the given stage"""
- return cls((blob.mode, blob.sha, stage, blob.path))
+ return cls((blob.mode, blob.sha, stage << CE_STAGESHIFT, blob.path))
class IndexEntry(BaseIndexEntry):
@@ -121,22 +135,24 @@ class IndexEntry(BaseIndexEntry):
def size(self):
""":return: Uncompressed size of the blob """
return self[10]
-
+
@classmethod
- def from_base(cls, base):
+ def from_base(cls, base, actual_value=False):
"""
:return:
Minimal entry as created from the given BaseIndexEntry instance.
Missing values will be set to null-like values
- :param base: Instance of type BaseIndexEntry"""
+ :param base: Instance of type BaseIndexEntry
+ :param actual_value: if set, instead of writing 0 values for fields that
+ don't exist in the BaseEntry, the actual values will be written in."""
time = pack(">LL", 0, 0)
- return IndexEntry((base.mode, base.sha, base.stage, base.path, time, time, 0, 0, 0, 0, 0))
+ return IndexEntry((base.mode, base.sha, base.flags, base.path, time, time, 0, 0, 0, 0, 0))
@classmethod
- def from_blob(cls, blob):
- """:return: Minimal entry resembling the given blob objecft"""
+ def from_blob(cls, blob, stage = 0):
+ """:return: Minimal entry resembling the given blob object"""
time = pack(">LL", 0, 0)
- return IndexEntry((blob.mode, blob.sha, 0, blob.path, time, time, 0, 0, 0, 0, blob.size))
+ return IndexEntry((blob.mode, blob.sha, stage << CE_STAGESHIFT, blob.path, time, time, 0, 0, 0, 0, blob.size))
diff --git a/lib/git/index/util.py b/lib/git/index/util.py
index cf09b095..c9fb1922 100644
--- a/lib/git/index/util.py
+++ b/lib/git/index/util.py
@@ -3,7 +3,7 @@ import struct
import tempfile
import os
-__all__ = ( 'TemporaryFileSwap', 'clear_cache', 'default_index' )
+__all__ = ( 'TemporaryFileSwap', 'post_clear_cache', 'default_index' )
#{ Aliases
pack = struct.pack
@@ -36,7 +36,7 @@ class TemporaryFileSwap(object):
#{ Decorators
-def clear_cache(func):
+def post_clear_cache(func):
"""Decorator for functions that alter the index using the git command. This would
invalidate our possibly existing entries dictionary which is why it must be
deleted to allow it to be lazily reread later.
@@ -45,14 +45,14 @@ def clear_cache(func):
This decorator will not be required once all functions are implemented
natively which in fact is possible, but probably not feasible performance wise.
"""
- def clear_cache_if_not_raised(self, *args, **kwargs):
+ def post_clear_cache_if_not_raised(self, *args, **kwargs):
rval = func(self, *args, **kwargs)
self._delete_entries_cache()
return rval
# END wrapper method
- clear_cache_if_not_raised.__name__ = func.__name__
- return clear_cache_if_not_raised
+ post_clear_cache_if_not_raised.__name__ = func.__name__
+ return post_clear_cache_if_not_raised
def default_index(func):
"""Decorator assuring the wrapped method may only run if we are the default
diff --git a/lib/git/objects/tree.py b/lib/git/objects/tree.py
index 0da62060..eb8aa9eb 100644
--- a/lib/git/objects/tree.py
+++ b/lib/git/objects/tree.py
@@ -121,7 +121,7 @@ class Tree(base.IndexObject, diff.Diffable, utils.Traversable, utils.Serializabl
__slots__ = "_cache"
# actual integer ids for comparison
- commit_id = 016
+ commit_id = 016 # equals stat.S_IFDIR | stat.S_IFLNK - a directory link
blob_id = 010
symlink_id = 012
tree_id = 004