diff options
Diffstat (limited to 'index/fun.py')
-rw-r--r-- | index/fun.py | 322 |
1 files changed, 0 insertions, 322 deletions
diff --git a/index/fun.py b/index/fun.py deleted file mode 100644 index 9b35bf04..00000000 --- a/index/fun.py +++ /dev/null @@ -1,322 +0,0 @@ -# Contains standalone functions to accompany the index implementation and make it -# more versatile -# NOTE: Autodoc hates it if this is a docstring -from stat import ( - S_IFDIR, - S_IFLNK, - S_ISLNK, - S_IFDIR, - S_ISDIR, - S_IFMT, - S_IFREG, - ) - -S_IFGITLINK = S_IFLNK | S_IFDIR # a submodule - -from cStringIO import StringIO - -from git.util import IndexFileSHA1Writer -from git.exc import UnmergedEntriesError -from git.objects.fun import ( - tree_to_stream, - traverse_tree_recursive, - traverse_trees_recursive - ) - -from typ import ( - BaseIndexEntry, - IndexEntry, - CE_NAMEMASK, - CE_STAGESHIFT - ) -CE_NAMEMASK_INV = ~CE_NAMEMASK - -from util import ( - pack, - unpack - ) - -from gitdb.base import IStream -from gitdb.typ import str_tree_type - -__all__ = ('write_cache', 'read_cache', 'write_tree_from_cache', 'entry_key', - 'stat_mode_to_index_mode', 'S_IFGITLINK') - - -def stat_mode_to_index_mode(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) == S_IFGITLINK: # submodules - return S_IFGITLINK - return S_IFREG | 0644 | (mode & 0100) # blobs with or without executable bit - - -def write_cache(entries, stream, extension_data=None, ShaStreamCls=IndexFileSHA1Writer): - """Write the cache represented by entries to a stream - - :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 - stream = ShaStreamCls(stream) - - tell = stream.tell - write = stream.write - - # header - version = 2 - write("DIRC") - write(pack(">LL", version, len(entries))) - - # body - for entry in entries: - beginoffset = tell() - write(entry[4]) # ctime - write(entry[5]) # mtime - path = entry[3] - 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] & CE_NAMEMASK_INV) # clear possible previous values - write(pack(">LLLLLL20sH", entry[6], entry[7], entry[0], - entry[8], entry[9], entry[10], entry[1], flags)) - write(path) - real_size = ((tell() - beginoffset + 8) & ~7) - write("\0" * ((beginoffset + real_size) - tell())) - # END for each entry - - # write previously cached extensions data - if extension_data is not None: - stream.write(extension_data) - - # write the sha over the content - stream.write_sha() - -def read_header(stream): - """Return tuple(version_long, num_entries) from the given stream""" - type_id = stream.read(4) - if type_id != "DIRC": - raise AssertionError("Invalid index file header: %r" % type_id) - version, num_entries = unpack(">LL", stream.read(4 * 2)) - - # TODO: handle version 3: extended data, see read-cache.c - assert version in (1, 2) - return version, num_entries - -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""" - if len(entry) == 1: - return (entry[0].path, entry[0].stage) - else: - return tuple(entry) - # END handle entry - -def read_cache(stream): - """Read a cache file from the given stream - :return: tuple(version, entries_dict, extension_data, content_sha) - * version is the integer version number - * entries dict is a dictionary which maps IndexEntry instances to a path - at a stage - * extension_data is '' or 4 bytes of type + 4 bytes of size + size bytes - * content_sha is a 20 byte sha on all cache file contents""" - version, num_entries = read_header(stream) - count = 0 - entries = dict() - - read = stream.read - tell = stream.tell - while count < num_entries: - beginoffset = tell() - ctime = unpack(">8s", read(8))[0] - mtime = unpack(">8s", read(8))[0] - (dev, ino, mode, uid, gid, size, sha, flags) = \ - unpack(">LLLLLL20sH", read(20 + 4 * 6 + 2)) - path_size = flags & CE_NAMEMASK - path = read(path_size) - - real_size = ((tell() - beginoffset + 8) & ~7) - data = read((beginoffset + real_size) - tell()) - entry = IndexEntry((mode, sha, flags, path, ctime, mtime, dev, ino, uid, gid, size)) - # entry_key would be the method to use, but we safe the effort - entries[(path, entry.stage)] = entry - count += 1 - # END for each entry - - # the footer contains extension data and a sha on the content so far - # Keep the extension footer,and verify we have a sha in the end - # Extension data format is: - # 4 bytes ID - # 4 bytes length of chunk - # repeated 0 - N times - extension_data = stream.read(~0) - assert len(extension_data) > 19, "Index Footer was not at least a sha on content as it was only %i bytes in size" % len(extension_data) - - content_sha = extension_data[-20:] - - # truncate the sha in the end as we will dynamically create it anyway - extension_data = extension_data[:-20] - - return (version, entries, extension_data, content_sha) - -def write_tree_from_cache(entries, odb, sl, si=0): - """Create a tree from the given sorted list of entries and put the respective - trees into the given object database - - :param entries: **sorted** list of IndexEntries - :param odb: object database to store the trees in - :param si: start index at which we should start creating subtrees - :param sl: slice indicating the range we should process on the entries list - :return: tuple(binsha, list(tree_entry, ...)) a tuple of a sha and a list of - tree entries being a tuple of hexsha, mode, name""" - tree_items = list() - tree_items_append = tree_items.append - ci = sl.start - end = sl.stop - while ci < end: - entry = entries[ci] - if entry.stage != 0: - raise UnmergedEntriesError(entry) - # END abort on unmerged - ci += 1 - rbound = entry.path.find('/', si) - if rbound == -1: - # its not a tree - tree_items_append((entry.binsha, entry.mode, entry.path[si:])) - else: - # find common base range - base = entry.path[si:rbound] - xi = ci - while xi < end: - oentry = entries[xi] - orbound = oentry.path.find('/', si) - if orbound == -1 or oentry.path[si:orbound] != base: - break - # END abort on base mismatch - xi += 1 - # END find common base - - # enter recursion - # ci - 1 as we want to count our current item as well - sha, tree_entry_list = write_tree_from_cache(entries, odb, slice(ci-1, xi), rbound+1) - tree_items_append((sha, S_IFDIR, base)) - - # skip ahead - ci = xi - # END handle bounds - # END for each entry - - # finally create the tree - sio = StringIO() - tree_to_stream(tree_items, sio.write) - sio.seek(0) - - istream = odb.store(IStream(str_tree_type, len(sio.getvalue()), sio)) - return (istream.binsha, tree_items) - -def _tree_entry_to_baseindexentry(tree_entry, stage): - return BaseIndexEntry((tree_entry[1], tree_entry[0], stage <<CE_STAGESHIFT, tree_entry[2])) - -def aggressive_tree_merge(odb, tree_shas): - """ - :return: list of BaseIndexEntries representing the aggressive merge of the given - trees. All valid entries are on stage 0, whereas the conflicting ones are left - on stage 1, 2 or 3, whereas stage 1 corresponds to the common ancestor tree, - 2 to our tree and 3 to 'their' tree. - :param tree_shas: 1, 2 or 3 trees as identified by their binary 20 byte shas - If 1 or two, the entries will effectively correspond to the last given tree - If 3 are given, a 3 way merge is performed""" - out = list() - out_append = out.append - - # one and two way is the same for us, as we don't have to handle an existing - # index, instrea - if len(tree_shas) in (1,2): - for entry in traverse_tree_recursive(odb, tree_shas[-1], ''): - out_append(_tree_entry_to_baseindexentry(entry, 0)) - # END for each entry - return out - # END handle single tree - - if len(tree_shas) > 3: - raise ValueError("Cannot handle %i trees at once" % len(tree_shas)) - - # three trees - for base, ours, theirs in traverse_trees_recursive(odb, tree_shas, ''): - if base is not None: - # base version exists - if ours is not None: - # ours exists - if theirs is not None: - # it exists in all branches, if it was changed in both - # its a conflict, otherwise we take the changed version - # This should be the most common branch, so it comes first - if( base[0] != ours[0] and base[0] != theirs[0] and ours[0] != theirs[0] ) or \ - ( base[1] != ours[1] and base[1] != theirs[1] and ours[1] != theirs[1] ): - # changed by both - out_append(_tree_entry_to_baseindexentry(base, 1)) - out_append(_tree_entry_to_baseindexentry(ours, 2)) - out_append(_tree_entry_to_baseindexentry(theirs, 3)) - elif base[0] != ours[0] or base[1] != ours[1]: - # only we changed it - out_append(_tree_entry_to_baseindexentry(ours, 0)) - else: - # either nobody changed it, or they did. In either - # case, use theirs - out_append(_tree_entry_to_baseindexentry(theirs, 0)) - # END handle modification - else: - - if ours[0] != base[0] or ours[1] != base[1]: - # they deleted it, we changed it, conflict - out_append(_tree_entry_to_baseindexentry(base, 1)) - out_append(_tree_entry_to_baseindexentry(ours, 2)) - # else: - # we didn't change it, ignore - # pass - # END handle our change - # END handle theirs - else: - if theirs is None: - # deleted in both, its fine - its out - pass - else: - if theirs[0] != base[0] or theirs[1] != base[1]: - # deleted in ours, changed theirs, conflict - out_append(_tree_entry_to_baseindexentry(base, 1)) - out_append(_tree_entry_to_baseindexentry(theirs, 3)) - # END theirs changed - #else: - # theirs didnt change - # pass - # END handle theirs - # END handle ours - else: - # all three can't be None - if ours is None: - # added in their branch - out_append(_tree_entry_to_baseindexentry(theirs, 0)) - elif theirs is None: - # added in our branch - out_append(_tree_entry_to_baseindexentry(ours, 0)) - else: - # both have it, except for the base, see whether it changed - if ours[0] != theirs[0] or ours[1] != theirs[1]: - out_append(_tree_entry_to_baseindexentry(ours, 2)) - out_append(_tree_entry_to_baseindexentry(theirs, 3)) - else: - # it was added the same in both - out_append(_tree_entry_to_baseindexentry(ours, 0)) - # END handle two items - # END handle heads - # END handle base exists - # END for each entries tuple - - return out |