He preguntado antes sobre cómo aplastar los dos primeros commits en un repositorio git.
Si bien las soluciones son bastante interesantes y no tan alucinantes como otras cosas en git, siguen siendo un poco proverbiales si necesitas repetir el procedimiento muchas veces durante el desarrollo de tu proyecto.
Por lo tanto, prefiero pasar por el dolor solo una vez, y luego poder usar para siempre el rebase interactivo estándar.
Lo que quiero hacer, entonces, es tener un commit inicial vacío que exista únicamente con el propósito de ser el primero. Sin código, sin nada. Solo ocupando espacio para que pueda ser la base para rebase.
Mi pregunta es, teniendo un repositorio existente, ¿cómo hago para insertar una nueva confirmación vacía antes de la primera y hacer avanzar a todos los demás?
Respuestas:
Hay 2 pasos para lograr esto:
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:
Envuelva un compromiso a su alrededor:
Crea una referencia a ella:
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:Tenga en cuenta que en versiones muy antiguas de Git que carecen del
--orphan
cambiocheckout
, debe reemplazar la primera línea con esto:2. Reescribe el historial para comenzar desde esta confirmación vacía
Tiene dos opciones aquí: rebase o una reescritura de historial limpia.
Rebasando
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: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, lanewroot
confirmación ) Luegogit 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 comienzanewroot
.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. Congit 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.fuente
--onto newroot
opción es redundante; puede prescindir de él porque el argumento que lo pasanewroot
es el mismo que el argumento anteriornewroot
.git-checkout
tenía ese cambio. Lo actualicé para mencionar ese enfoque primero, gracias por el puntero.git rebase --merge -s recursive -X theirs --onto newroot --root master
para resolver todos los conflictos automáticamente (vea esta respuesta). @AlexanderKuzinFusión de las respuestas de Aristóteles Pagaltzis y Uwe Kleine-König y el comentario de Richard Bronosky.
(solo para poner todo en un solo lugar)
fuente
git rebase newroot master
, por error.Me gusta la respuesta de Aristóteles. Pero descubrió que para un repositorio grande (> 5000 confirmaciones) la rama de filtro funciona mejor que el rebase por varias razones 1) es más rápido 2) no requiere intervención humana cuando hay un conflicto de fusión. 3) puede reescribir las etiquetas, preservándolas. Tenga en cuenta que filter-branch funciona porque no hay dudas sobre el contenido de cada commit; es exactamente lo mismo que antes de este 'rebase'.
Mis pasos son:
Tenga en cuenta que las opciones '--tag-name-filter cat' significan que las etiquetas se reescribirán para apuntar a los commits recién creados.
fuente
Usé fragmentos de la respuesta de Aristóteles y Kent con éxito:
Esto también reescribirá todas las ramas (no solo
master
) además de las etiquetas.fuente
refs/original/
y elimina cada referencia. Las referencias que elimina ya deberían estar referenciadas por alguna otra rama, por lo que realmente no desaparecen, solorefs/original/
se eliminan.timedatectl set-time '2017-01-01 00:00:00'
darnewroot
una marca de tiempo antigua.git rebase --root --onto $emptyrootcommit
debería hacer el truco fácilmente
fuente
$emptyrootcommit
es una variable de shell que se expande a nada, seguramente?Creo que usar
git replace
ygit filter-branch
es una mejor solución que usar ungit rebase
:La idea detrás de esto es:
git filter-branch
Aquí hay un script para los 2 primeros pasos:
Puede ejecutar este script sin riesgo (incluso si hacer una copia de seguridad antes de realizar una acción que nunca antes hizo es una buena idea;)), y si el resultado no es el esperado, simplemente elimine los archivos creados en la carpeta
.git/refs/replace
e intente nuevamente; )Una vez que haya verificado que el estado del repositorio es el que espera, ejecute el siguiente comando para actualizar el historial de todas las ramas :
Ahora, debe ver 2 historias, la antigua y la nueva (consulte la ayuda
filter-branch
para obtener más información). Puede comparar los 2 y verificar nuevamente si todo está bien. Si está satisfecho, elimine los archivos que ya no necesita:Puede volver a su
master
sucursal y eliminar la sucursal temporal:Ahora, todo debe hacerse;)
fuente
Me emocioné y escribí una versión 'idempotente' de este lindo script ... siempre insertará el mismo commit vacío, y si lo ejecutas dos veces, no cambia tus hashes de commit cada vez. Entonces, aquí está mi opinión sobre git-insert-empty-root :
¿Vale la pena la complejidad extra? tal vez no, pero usaré este.
Esto DEBERÍA también permitir realizar esta operación en varias copias clonadas del repositorio, y terminar con los mismos resultados, por lo que aún son compatibles ... pruebas ... sí, funciona, pero también necesita eliminar y agregar su controles remotos de nuevo, por ejemplo:
fuente
Para agregar una confirmación vacía al comienzo de un repositorio, si olvidó crear una confirmación vacía inmediatamente después de "git init":
fuente
Bueno, esto es lo que se me ocurrió:
fuente
Aquí está mi
bash
script basado en la respuesta de Kent con mejoras:master
cuando termina;git checkout --orphan
solo funciona con una bifurcación, no en estado de cabeza separada, por lo que se desprotegió el tiempo suficiente para que la nueva raíz se confirme y luego se eliminó;filter-branch
(Kent dejó un marcador de posición allí para el reemplazo manual);filter-branch
operación reescribe solo las sucursales locales, no los remotos tambiénfuente
Para cambiar la confirmación de raíz:
Primero, cree el commit que desea como el primero.
Segundo, cambie el orden de los commits usando:
git rebase -i --root
Aparecerá un editor con las confirmaciones hasta la confirmación raíz, como:
recoger 1234 mensaje raíz antiguo
recoger 0294 Una confirmación en el medio
seleccione 5678 commit que desea poner en la raíz
Luego, puede poner el commit que desee primero, colocándolo en la primera línea. En el ejemplo:
seleccione 5678 commit que desea poner en la raíz
recoger 1234 mensaje raíz antiguo
recoger 0294 Una confirmación en el medio
Salga del editor, el orden de confirmación habrá cambiado.
PD: para cambiar los usos del editor git, ejecuta:
git config --global core.editor name_of_the_editor_program_you_want_to_use
fuente
Combinando lo último y lo mejor. Sin efectos secundarios, sin conflictos, manteniendo etiquetas.
Tenga en cuenta que en GitHub perderá los datos de ejecución de CI y PR podría estropearse a menos que también se arreglen otras ramas.
fuente
Siguiendo la respuesta Aristóteles Pagaltzis y otros pero usando comandos más simples
Tenga en cuenta que su repositorio no debe contener modificaciones locales en espera de ser confirmadas.
Note
git checkout --orphan
funcionará en nuevas versiones de git, supongo.Tenga en cuenta que la mayoría de las veces
git status
da pistas útiles.fuente
Comience un nuevo repositorio.
Establezca su fecha de regreso a la fecha de inicio que desee.
Haga todo de la forma que desee, ajustando el tiempo del sistema para que refleje cuándo hubiera deseado hacerlo de esa manera. Extraiga archivos del repositorio existente según sea necesario para evitar una gran cantidad de escritura innecesaria.
Cuando llegue hoy, cambie los repositorios y ya está.
Si solo estás loco (establecido) pero razonablemente inteligente (probablemente, porque tienes que tener una cierta cantidad de inteligencia para pensar ideas locas como esta), escribirás el proceso.
Eso también lo hará más agradable cuando decidas que quieres que el pasado haya sucedido de otra manera dentro de una semana a partir de ahora.
fuente
Sé que esta publicación es antigua, pero esta página es la primera cuando busco en Google "insertar commit git".
¿Por qué complicar las cosas simples?
Tienes ABC y quieres ABZC.
git rebase -i trunk
(o cualquier cosa antes de B)git add ..
git commit
(git commit --amend
que editará B y no creará Z)[Puede hacer tantos
git commit
como desee aquí para insertar más confirmaciones. Por supuesto, puede tener problemas con el paso 5, pero resolver el conflicto de fusión con git es una habilidad que debería tener. Si no, ¡practica!]git rebase --continue
Simple, ¿no es así?
Si comprende
git rebase
, agregar un commit 'root' no debería ser un problema.Diviértete con git!
fuente
git rebase
no puede hacer esto.