def tree_to_github( tree: git.Tree, target_subdir: str, gh_repo: GithubRepo ) -> Dict[str, InputGitTreeElement]: """Extrae los contenidos de un commit de Git en formato Tree de Github. Returns: un diccionario donde las claves son rutas en el repo, y los valores el InputGitTreeElement que los modifica. """ odb = tree.repo.odb target_subdir = target_subdir.rstrip("/") + "/" entries = traverse_tree_recursive(odb, tree.binsha, target_subdir) contents = {} for sha, mode, path in entries: # TODO: get exclusion list from repos.yml if path.endswith("README.md"): continue fileobj = io.BytesIO() stream_copy(odb.stream(sha), fileobj) fileobj.seek(0) try: text = fileobj.read().decode("utf-8") input_elem = InputGitTreeElement(path, f"{mode:o}", "blob", text) except UnicodeDecodeError: # POST /trees solo permite texto, hay que crear un blob para binario. fileobj.seek(0) data = base64.b64encode(fileobj.read()) blob = gh_repo.create_git_blob(data.decode("ascii"), "base64") input_elem = InputGitTreeElement(path, f"{mode:o}", "blob", sha=blob.sha) finally: contents[path] = input_elem return contents
def test_tree_traversal_single(self): max_count = 50 count = 0 odb = self.rorepo.odb for commit in self.rorepo.commit("29eb123beb1c55e5db4aa652d843adccbd09ae18").traverse(): if count >= max_count: break count += 1 entries = traverse_tree_recursive(odb, commit.tree.binsha, '') assert entries
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
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