Hay 2 pasos para lograr esto:
- Crear una nueva confirmación vacía
- Reescribe el historial para comenzar desde esta confirmación vacía
Colocaremos el nuevo commit vacío en una rama temporal newroot
por conveniencia.
1. Crear una nueva confirmación vacía
Hay varias formas de hacer esto.
Usando solo plomería
El enfoque más limpio es usar la tubería de Git para crear una confirmación directamente, lo que evita tocar la copia de trabajo o el índice o qué rama está desprotegida, etc.
Cree un objeto de árbol para un directorio vacío:
tree=`git hash-object -wt tree --stdin < /dev/null`
Envuelva un compromiso a su alrededor:
commit=`git commit-tree -m 'root commit' $tree`
Crea una referencia a ella:
git branch newroot $commit
Por supuesto, puede reorganizar todo el procedimiento en una sola línea si conoce bien su caparazón.
Sin fontanería
Con los comandos de porcelana regulares, no puede crear una confirmación vacía sin verificar la newroot
rama y actualizar el índice y la copia de trabajo repetidamente, sin una buena razón. Pero algunos pueden encontrar esto más fácil de entender:
git checkout --orphan newroot
git rm -rf .
git clean -fd
git commit --allow-empty -m 'root commit'
Tenga en cuenta que en versiones muy antiguas de Git que carecen del --orphan
cambio checkout
, debe reemplazar la primera línea con esto:
git symbolic-ref HEAD refs/heads/newroot
2. Reescribe el historial para comenzar desde esta confirmación vacía
Tiene dos opciones aquí: rebase o una reescritura de historial limpia.
Rebasando
git rebase --onto newroot --root master
Esto tiene la virtud de la simplicidad. Sin embargo, también actualizará el nombre y la fecha del committer en cada último commit en la rama.
Además, con algunos historiales de casos extremos, incluso puede fallar debido a conflictos de fusión, a pesar del hecho de que está volviendo a crear una confirmación que no contiene nada.
Reescribir la historia
El enfoque más limpio es reescribir la rama. A diferencia de git rebase
, deberá buscar qué confirmación inicia su rama:
git replace <currentroot> --graft newroot
git filter-branch master
La reescritura ocurre en el segundo paso, obviamente; Es el primer paso que necesita explicación. Lo que git replace
hace es decirle a Git que cada vez que vea una referencia a un objeto que desea reemplazar, Git debería buscar el reemplazo de ese objeto.
Con el --graft
interruptor, le estás diciendo algo ligeramente diferente de lo normal. Está diciendo que todavía no tiene un objeto de reemplazo, pero desea reemplazar el <currentroot>
objeto de confirmación con una copia exacta de sí mismo, excepto que la confirmación principal (s) de la sustitución debe ser la que usted enumeró (es decir, la newroot
confirmación ) Luego git replace
continúa y crea este commit para usted, y luego declara ese commit como el reemplazo de su commit original.
Ahora, si haces un git log
, verás que las cosas ya se ven como quieres: la rama comienza newroot
.
Sin embargo, tenga en cuenta que en git replace
realidad no modifica el historial , ni se propaga fuera de su repositorio. Simplemente agrega una redirección local a su repositorio de un objeto a otro. Lo que esto significa es que nadie más ve el efecto de este reemplazo, solo usted.
Por eso filter-branch
es necesario el paso. Con git replace
usted cree una copia exacta con confirmaciones primarias ajustadas para la confirmación raíz; git filter-branch
luego repite este proceso para todas las siguientes confirmaciones también. Ahí es donde la historia se reescribe para que puedas compartirla.