import os, time import collections from pygit2 import init_repository, Signature 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._get_last_commit() parents = [commit.oid] root = commit.tree.oid 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, 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(commit).decode('ascii')) return b2a_hex(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[str(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, 0100644) 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, 040000) current_tree_oid = builder.write() # connect existing nodes with created nodes for name, builder in reversed(existing_builders): builder.insert(pre, current_tree_oid, 040000) current_tree_oid = builder.write() pre = name return current_tree_oid def _get_last_commit(self): head = self.repo.lookup_reference('HEAD').resolve() return self.repo[head.oid] def _list(self, path=None): """ Lists all entries for a given path """ try: commit = self._get_last_commit() tree = commit.tree if path: for p in path.split('/'): tree = tree[p].to_object() return [(x.name, x.hex) for x in tree] except: return [] #fit = Fit('static/fit.git') #fit.add_file('main.c', 'alp3/2007/main.c') #fit.add_file('main.c', 'alp3/2008/main.c') #fit.add_file('main.c', 'alp3/2009/main.c') # #print('All modules') #for name in fit.get_modules(): # print('\t' + name) # #print('Module: alp4') #for (year,items) in fit.get_module('alp4'): # print('\t' + year) # for name in items: # print('\t\t' + name) # #for name, oid in fit.list('tags/aws'): # print(name) # #print("All files") #for name, oid in fit.list_directories(): # print(name) #tags = ','.join(map(lambda x: x[0], fit.get_file_with_tags(oid))) #print("* %s (%s)" % (name, tags)) #print(fit.get_file(oid) + "\n") # #print("\nAll tags") #for name, oid in fit.get_all_tags(): # files = ','.join(map(lambda x: x[0], fit.get_files_for_tags([name]))) # print("* %s (%s)" % (name, files))