import typer from git_flow import GitFlowError from git_flow.changelog import Changelog from git_flow.command.base import * from git_flow.git import Git app = typer.Typer() @app.command() def merge(): """Realiza el merge de la rama, o crea el PR para hacerlo si tiene un remoto configurado""" ensure_initialized() branch = ensure_right_branch() target, _ = get_branch_env_and_type(branch) if "flow.remote" in flowconfig: remote = flowconfig["flow.remote"] token = ensure_repository_token() run_remote(remote, token, branch, target) else: run_local(branch, target) def run_remote(remote: str, token: str, branch: str, target: str): ensure_clean_worktree(True) Git("switch", target).exec(print="Cambiando a rama destino") Git("pull").exec(print="Obteniendo ultimos cambios del remoto") Git("switch", "-").exec(print="Volviendo a rama a mergear") check_merge_conflicts(target) show_commits_to_merge(target) if confirm(f"¿Crear PR de '{branch}' a '{target}'?"): Git("push", remote, branch, set_upstream=True).exec( print="Subiendo rama al remoto para crear PR" ) create_pull_request(token, branch, target) def run_local(branch: str, target: str): ensure_clean_worktree(False) check_merge_conflicts(target) show_commits_to_merge(target) if confirm(f"¿Mergear rama '{branch}' a '{target}'?"): Git("switch", target).exec(print="Cambiando a rama destino") Git("merge", branch, ff=False).exec(print="Mergeando") def show_commits_to_merge(target): commits = Git("log", target + "..", format="%s").lines() if not commits: raise GitFlowError("No hay cambios a mergear.") print("Cambios a mergear:") for commit in commits: print("- " + commit) def check_merge_conflicts(target: str): merge_conflicts = Git("merge", target, ff=False, commit=False).code( print="Realizando merge de prueba para verificar conflictos" ) Git("merge", abort=True).exec(print="Abortando merge de prueba", check=False) if merge_conflicts: error(f"La rama actual tiene conflictos con {target}") info( f"""Se recomienda mergear {target} a la rama actual, resolver los conflictos localmente, y ejecutar nuevamente este comando""" ) raise GitFlowError("Ejecución abortada.") else: success("No se detectaron merge conflicts.") def create_pull_request(token: str, branch: str, target: str): changelog = Changelog() commits = Git("log", target + "..", format="%s").lines() title = commits[0] if len(commits) == 1 else _get_pr_title_from_branch(branch) panel("Título del PR", title) if confirm("¿Desea cambiar el título del PR?", False): title = prompt("Título del PR") message = get_remote_api(token).create_pull_request( branch, target, title, changelog.generate_content(target, branch), ) success(message) if confirm("¿Desea cambiar a la rama objetivo y bajar los cambios?"): Git("switch", target).exec(print="Cambiando a rama objetivo") Git("pull").exec(print="Obteniendo cambios") def _get_pr_title_from_branch(branch: str) -> str: components = branch.split("/") # / or release/// branch_type = components[-2] branch_desc = components[-1] # detect if branch description has ticket as prefix: = ABC-123-my-branch-name branch_ticket = None first_dash = branch_desc.find("-") second_dash = branch_desc.find("-", first_dash + 1) if first_dash >= 0 and second_dash >= 0: maybe_ticket = branch_desc[:second_dash] if is_valid_ticket(maybe_ticket): branch_ticket = maybe_ticket branch_desc = branch_desc[:second_dash+1] return " ".join(filter(None, [ f"[Pasaje a {components[1]}]" if len(components) == 4 else None, branch_type + ":", branch_ticket, branch_desc.replace("-", " ") ]))