def apply_stack(repo_dir, stack, branch, default_base): """Apply a stack of patches on a repository""" assert len(stack) > 0, "Empty stack" # Start by updating the repository clean(repo_dir) def has_revision(revision): try: hg.identify(revision) return True except hglib.error.CommandError: return False with hglib.open(repo_dir) as hg: # Find the base revision to apply all the patches onto # Use first parent from first patch if all its parents are available # Otherwise fallback on tip parents = stack[0]["parents"] assert len(parents) > 0, "No parents found for first patch" if all(map(has_revision, parents)): base = parents[0] else: # Some repositories need to have the exact parent to apply if default_base is None: raise Exception( "Parents are not available, cannot apply this stack") base = default_base # Update to base revision logger.info(f"Will apply stack on {base}") hg.update(base, clean=True) # Apply all the patches in the stack for rev in stack: node = rev["node"] logger.info(f"Applying patch for {node}") patch = get_hgmo_patch(branch, node) hg.import_(patches=io.BytesIO(patch.encode("utf-8")), user="******")
def apply_stack(repo_dir, stack, branch): """Apply a stack of patches on a repository""" assert len(stack) > 0, "Empty stack" def has_revision(revision): try: hg.identify(revision) return True except hglib.error.CommandError: return False def apply_patches(base, patches): # Update to base revision logger.info(f"Updating {repo_dir} to {base}") hg.update(base, clean=True) # Then apply each patch in the stack logger.info(f"Applying {len(patches)}...") try: for node, patch in patches: hg.import_(patches=io.BytesIO(patch.encode("utf-8")), user="******") except hglib.error.CommandError as e: logger.warning(f"Failed to apply patch {node}: {e.err}") return False return True # Find the base revision to apply all the patches onto # Use first parent from first patch if all its parents are available # Otherwise fallback on tip def get_base(): parents = stack[0]["parents"] assert len(parents) > 0, "No parents found for first patch" if all(map(has_revision, parents)): return parents[0] return "tip" def stack_nodes(): return get_revs(hg, f"-{len(stack)}") with hglib.open(repo_dir) as hg: if branch != "try": hg.pull( source=f"https://hg.mozilla.org/{branch}/".encode("ascii"), rev=stack[-1]["pushhead"].encode("ascii"), ) return [rev["node"].encode("ascii") for rev in stack] else: # Start by cleaning the repo, without pulling clean(hg, repo_dir, pull=False) logger.info("Repository cleaned") # Get initial base revision base = get_base() # Load all the patches in the stack patches = [(rev["node"], get_hgmo_patch(branch, rev["node"])) for rev in stack] logger.info(f"Loaded {len(patches)} patches for the stack") # Apply all the patches in the stack on current base if apply_patches(base, patches): logger.info(f"Stack applied successfully on {base}") return stack_nodes() # We tried to apply on the valid parent and failed: cannot try another revision if base != "tip": raise Exception(f"Failed to apply on valid parent {base}") # We tried to apply on tip, let's try to find the valid parent after pulling clean(hg, repo_dir, pull=True) # Check if the valid base is now available new_base = get_base() if base == new_base: raise Exception("No valid parent found for the stack") if not apply_patches(new_base, patches): raise Exception("Failed to apply stack on second try") logger.info( f"Stack applied successfully on second try on {new_base}") return stack_nodes()