feature: add branch clean subcommand
This commit is contained in:
@@ -70,6 +70,9 @@ más información):
|
|||||||
- `tag`: Crea un tag sobre el ultimo merge siguiendo SemVer (ver [Tag](#tag)).
|
- `tag`: Crea un tag sobre el ultimo merge siguiendo SemVer (ver [Tag](#tag)).
|
||||||
- `release`: Prepara la rama actual para pasarse al siguiente entorno configurado.
|
- `release`: Prepara la rama actual para pasarse al siguiente entorno configurado.
|
||||||
- `branch ls`: Lista las ramas del repositorio, agrupadas por entorno objetivo.
|
- `branch ls`: Lista las ramas del repositorio, agrupadas por entorno objetivo.
|
||||||
|
- `branch clean`: Elimina las ramas alcanzables desde HEAD (locales por defecto, o remotas con
|
||||||
|
`--remote`), dejando un reporte CSV (`branch-clean-report.csv`) con nombre, commit, autor y
|
||||||
|
fecha de cada rama eliminada.
|
||||||
|
|
||||||
El workflow para el cual se penso la herramienta es el siguiente:
|
El workflow para el cual se penso la herramienta es el siguiente:
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
name,commit sha,author,date
|
||||||
|
worktree-kind-watching-hearth,e8c0c0b777d31fa94cd3f01fbf9c68b165d77015,Jonathan Teran Carballo,2026-09-13T23:18:47-03:00
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import typer
|
import typer
|
||||||
|
|
||||||
from git_flow.command.branch import ls
|
from git_flow.command.branch import clean, ls
|
||||||
|
|
||||||
app = typer.Typer(name="branch")
|
app = typer.Typer(name="branch")
|
||||||
app.add_typer(ls.app)
|
app.add_typer(ls.app)
|
||||||
|
app.add_typer(clean.app)
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
import csv
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Annotated
|
||||||
|
|
||||||
|
import typer
|
||||||
|
|
||||||
|
from git_flow import GitFlowError
|
||||||
|
from git_flow.command import base
|
||||||
|
from git_flow.git import Git
|
||||||
|
|
||||||
|
REPORT_FORMAT = "%(refname:short)\t%(objectname)\t%(authorname)\t%(authordate:iso-strict)"
|
||||||
|
REPORT_FILENAME = "branch-clean-report.csv"
|
||||||
|
|
||||||
|
app = typer.Typer()
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class BranchInfo:
|
||||||
|
name: str
|
||||||
|
sha: str
|
||||||
|
author: str
|
||||||
|
date: str
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def clean(
|
||||||
|
remote: Annotated[
|
||||||
|
bool,
|
||||||
|
typer.Option(
|
||||||
|
help="Elimina las ramas remotas alcanzables desde HEAD, en vez de las locales"
|
||||||
|
),
|
||||||
|
] = False,
|
||||||
|
):
|
||||||
|
"""Elimina las ramas alcanzables desde HEAD, dejando un reporte CSV de las ramas eliminadas"""
|
||||||
|
base.ensure_initialized()
|
||||||
|
|
||||||
|
branches = get_remote_branches() if remote else get_local_branches()
|
||||||
|
|
||||||
|
if not branches:
|
||||||
|
base.io_info("No hay ramas alcanzables desde HEAD para eliminar.")
|
||||||
|
return
|
||||||
|
|
||||||
|
base.io_info(
|
||||||
|
"\n".join(f"- {branch.name}" for branch in branches),
|
||||||
|
title=f"Ramas a eliminar ({'remoto' if remote else 'local'})",
|
||||||
|
)
|
||||||
|
|
||||||
|
if not base.io_confirm(f"¿Eliminar {len(branches)} rama(s)?", False):
|
||||||
|
raise GitFlowError("Ejecución abortada")
|
||||||
|
|
||||||
|
write_report(branches)
|
||||||
|
|
||||||
|
for branch in branches:
|
||||||
|
delete_branch(branch, remote)
|
||||||
|
|
||||||
|
base.io_success(f"Se eliminaron {len(branches)} rama(s). Reporte generado en {REPORT_FILENAME}")
|
||||||
|
|
||||||
|
|
||||||
|
def get_local_branches() -> list[BranchInfo]:
|
||||||
|
current_branch = Git.get_current_branch()
|
||||||
|
branches = get_merged_branches("refs/heads/")
|
||||||
|
|
||||||
|
return [b for b in branches if b.name != current_branch and b.name not in base.environments]
|
||||||
|
|
||||||
|
|
||||||
|
def get_remote_branches() -> list[BranchInfo]:
|
||||||
|
if "flow.remote" not in base.flowconfig:
|
||||||
|
raise GitFlowError("El repositorio no tiene configurado un remoto.")
|
||||||
|
|
||||||
|
remote = base.flowconfig["flow.remote"]
|
||||||
|
current_tracking = Git.get_tracking_branch(Git.get_current_branch())
|
||||||
|
branches = get_merged_branches(f"refs/remotes/{remote}/")
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for branch in branches:
|
||||||
|
if branch.name == current_tracking or branch.name == f"{remote}/HEAD":
|
||||||
|
continue
|
||||||
|
|
||||||
|
name = branch.name.removeprefix(f"{remote}/")
|
||||||
|
|
||||||
|
if name in base.environments:
|
||||||
|
continue
|
||||||
|
|
||||||
|
result.append(BranchInfo(name, branch.sha, branch.author, branch.date))
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def get_merged_branches(pattern: str) -> list[BranchInfo]:
|
||||||
|
lines = Git("for-each-ref", pattern, merged="HEAD", format=REPORT_FORMAT).lines()
|
||||||
|
|
||||||
|
return [parse_branch(line) for line in lines]
|
||||||
|
|
||||||
|
|
||||||
|
def parse_branch(line: str) -> BranchInfo:
|
||||||
|
name, sha, author, date = line.split("\t")
|
||||||
|
return BranchInfo(name, sha, author, date)
|
||||||
|
|
||||||
|
|
||||||
|
def write_report(branches: list[BranchInfo]):
|
||||||
|
with open(REPORT_FILENAME, "w", newline="") as f:
|
||||||
|
writer = csv.writer(f)
|
||||||
|
writer.writerow(["name", "commit sha", "author", "date"])
|
||||||
|
|
||||||
|
for branch in branches:
|
||||||
|
writer.writerow([branch.name, branch.sha, branch.author, branch.date])
|
||||||
|
|
||||||
|
|
||||||
|
def delete_branch(branch: BranchInfo, remote: bool):
|
||||||
|
if remote:
|
||||||
|
remote_name = base.flowconfig["flow.remote"]
|
||||||
|
Git("push", remote_name, branch.name, delete=True).exec(
|
||||||
|
print=f"Eliminando rama remota '{branch.name}'"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
Git("branch", branch.name, d=True).exec(
|
||||||
|
print=f"Eliminando rama local '{branch.name}'"
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user