#!/usr/bin/python2 # -*- coding: utf-8 -*- import os, time import collections from pygit2 import init_repository, Signature, GIT_FILEMODE_TREE, GIT_FILEMODE_BLOB from binascii import b2a_hex class Fit: def __init__(self, path): self.repo = init_repository(path, True) def get_file(self, oid): """ Returns the actual data of a git object """ return self.repo[oid].data def get_modules(self): """ returns a list of all modules """ return [x[0] for x in self._list()] def get_module(self, module): """ gets all entries for a module grouped by years """ years = self._list(module) return [(year[0], self._list(os.path.join(module, year[0]))) for year in years] def add_file(self, data, path): """ Inserts the given file in the git tree and commits the changes """ try: commit = self.repo.head.get_object() parents = commit.parents root = commit.tree.id except: parents = [] root = None blob_oid = self.repo.create_blob(data) tree = self._insert_node(blob_oid, path, root) author = committer = Signature('Fit', 'Fit@fit.de', int(time.time()), 120) commit = self.repo.create_commit( 'HEAD', author, committer, 'added %s' % path, tree, [p.id for p in parents] ) # save the actual head sha for dump git http protocol # similiar to `git update-server-info` info_refs_path = os.path.join(self.repo.path, 'info', 'refs') with open(info_refs_path, 'w') as f: f.write('%s\trefs/heads/master\n' % b2a_hex(str(commit)).decode('ascii')) return b2a_hex(str(blob_oid)).decode('ascii') def _insert_node(self, node_oid, path, root_oid): """ Inserts a new Node in a Git Tree graph """ if root_oid: root = self.repo.TreeBuilder(root_oid) current_node = self.repo[root_oid] else: root = self.repo.TreeBuilder() current_node = self.repo[root.write()] # entire path dir_path = path.split(os.sep)[:-1] # search for existing nodes in path existing_builders = [(os.sep, root)] for dir_entry in dir_path: try: new_oid = current_node[dir_entry].oid current_node = self.repo[new_oid] existing_builders.append(( dir_entry, self.repo.TreeBuilder(current_node) )) except KeyError: break # directories to create new_path = dir_path[len(existing_builders)-1:] # inserts blob object filename = os.path.basename(path) if len(new_path) > 0: builder = self.repo.TreeBuilder() pre = filename else: last_dir = existing_builders.pop() builder = last_dir[1] pre = last_dir[0] builder.insert(filename, node_oid, GIT_FILEMODE_BLOB) current_tree_oid = builder.write() # create new nodes bottom-up for our node if len(new_path) > 0: pre = new_path.pop(0) for entry in reversed(new_path): builder = self.repo.TreeBuilder() builder.insert(entry, current_tree_oid, GIT_FILEMODE_TREE) current_tree_oid = builder.write() # connect existing nodes with created nodes for name, builder in reversed(existing_builders): builder.insert(pre, current_tree_oid, GIT_FILEMODE_TREE) current_tree_oid = builder.write() pre = name return current_tree_oid def _list(self, path=None): """ Lists all entries for a given path """ try: tree = self.repo.head.get_object().tree if path: for p in path.split('/'): tree = self.repo[tree[p].id] return [(x.name, x.hex) for x in tree] except: return []