ikurotime / gitgud

public

Soportar forks, para que alguien que no sea el dueño pueda abrir pull requests #3

Open rafaelzr opened this on 2026-08-27 12:01
Rrafaelzr commented on 2026-08-27 12:01

Hoy nadie externo puede contribuir, y no por falta de un botón: no hay ningún lugar donde dejar una rama.

  • CanPush (repo_service.go:72) es viewer.ID == repo.OwnerID. Cualquier otro push da 403.
  • Los PRs son entre dos ramas del mismo repo: pull_requests tiene repo_id, base_branch, head_branch, sin noción de un repo de origen distinto.

Los permisos ya son los correctos

No hay que tocar la autorización: un fork le pertenece a quien lo forkea, así que CanPush(fork, forkeador) ya da true, y CanMergePR ya es "solo el dueño del base". El trabajo está en el modelo de datos, git y la UI.

Las mecánicas funcionan (probado)

Todos los bare viven en el mismo filesystem, así que traer los objetos del fork es un fetch por ruta, sin red ni auth:

git clone --bare upstream.git fork.git     # el fork
git push -u origin feature                 # el forkeador, a SU bare
git fetch /ruta/al/fork.git feature        # el upstream mergea
git merge --no-ff FETCH_HEAD && git push origin main

--shared deja el fork casi sin ocupar espacio, pero no en v1: si el upstream corre git gc y poda objetos que solo el fork referencia, el fork se rompe.

Qué cambia

Esquema

  • repositories: fork_of_id INTEGER NULL.
  • pull_requests: head_repo_id INTEGER NOT NULL, backfill = repo_id.

Código

  • GitReader.Compare (ports.go:49) recibe un solo owner, name. La parte más delicada: go-git necesita los dos commits en la misma base de objetos para el merge-base, así que hay que registrar el fork como alternate o traer objetos antes.
  • GitService.Merge: de merge origin/<head> al fetch por ruta de arriba.
  • PullService.Open: validar head contra el repo de origen, no contra el base.
  • RepoService.Fork + CloneBare en GitService.

UI

  • Compare cruzado ?head=usuario:rama; hoy head es un query param plano (pull_handler.go:65).
  • Botón de fork, y head como dueño:rama en el detalle del PR.

Visibilidad: decidirlo explícito

Es donde esto filtra código privado. Un fork privado de un repo público, al abrir un PR, le muestra su diff al dueño del base. Regla simple para v1: el origen tiene que ser al menos tan visible como el base. Y CanView devuelve 404 y no 403 a propósito, está fijado por tests: un PR con origen invisible no puede filtrar ni el nombre de la rama.

Bloqueante

Borrar un fork con PRs abiertos deja un head_repo_id colgando: RepoRepo.Delete es un DELETE pelado y las FK no se aplican (ver issue de PostgreSQL). Resolver eso antes.

Aceptación

La matriz de permisos ya cubierta por tests tiene que seguir pasando sin cambios, más: el forkeador pushea a su fork pero no al base (403); el dueño del base mergea un PR de un fork y el autor no; un fork de un repo privado es 404 para terceros.

Sign in to comment.