remove - git tag commits
Squash los dos primeros se compromete en Git? (9)
Esta pregunta ya tiene una respuesta aquí:
Con git rebase --interactive <commit>
puede aplastar cualquier número de confirmaciones en una sola.
Eso es genial, a menos que quieras aplastar los compromisos en el compromiso inicial. Eso parece imposible de hacer.
¿Hay alguna manera de lograrlo?
Relacionado moderadamente:
En una pregunta relacionada, logré encontrar un enfoque diferente a la necesidad de aplastar contra el primer commit, que es, bueno, convertirlo en el segundo.
Si estás interesado: git: ¿cómo insertar un commit como el primero, cambiando todos los demás?
Actualización de julio de 2012 ( git 1.7.12+ )
Ahora puede rebasar todos los confirmaciones hasta la raíz y seleccionar la segunda confirmación Y
para ser aplastada con la primera X
git rebase -i --root master
pick sha1 X
squash sha1 Y
pick sha1 Z
git rebase [-i] --root $tip
Este comando ahora se puede usar para volver a escribir todo el historial que va desde "
$tip
" hasta la confirmación de la raíz.
Consulte commit df5df20c1308f936ea542c86df1e9c6974168472 en GitHub de Chris Webb ( arachsys
) .
Respuesta original (febrero 2009)
Creo que encontrarás diferentes recetas para eso en la pregunta de SO " ¿Cómo puedo combinar los dos primeros confirmaciones de un repositorio de git? "
Charles Bailey proporcionó allí la respuesta más detallada , recordándonos que un compromiso es un árbol completo (no solo difiere de un estado anterior).
Y aquí el compromiso antiguo (el "compromiso inicial") y el nuevo compromiso (resultado del aplastamiento) no tendrán un antepasado común.
Eso significa que no puede " commit --amend
" el cometer inicial en uno nuevo, y luego volver a crear en el nuevo cometer inicial el historial del cometer inicial anterior (muchos conflictos)
(La última oración ya no es cierta con git rebase -i --root <aBranch>
)
Más bien (con A
la "confirmación inicial" original, y B
una confirmación posterior debe ser aplastada en la inicial):
Regrese a la última confirmación en la que queremos formar la confirmación inicial (separar HEAD):
git checkout <sha1_for_B>
Restablezca el puntero de rama a la confirmación inicial, pero dejando el índice y el árbol de trabajo intactos:
git reset --soft <sha1_for_A>
Modifique el árbol inicial usando el árbol de ''B'':
git commit --amend
Etiquete temporalmente este nuevo compromiso inicial (o podría recordar el nuevo compromiso sha1 manualmente):
git tag tmp
Regrese a la rama original (suponga maestro para este ejemplo):
git checkout master
Repita todas las confirmaciones después de B en la nueva confirmación inicial:
git rebase --onto tmp <sha1_for_B>
Eliminar la etiqueta temporal:
git tag -d tmp
De esa manera, la " rebase --onto
" no introduce conflictos durante la fusión, ya que vuelve a basar la historia realizada después de la última confirmación ( B
) para ser aplastada en la inicial (que era A
) a tmp
(que representa la nueva inicial aplastada commit): el avance rápido trivial se fusiona solamente.
Eso funciona para " AB
", pero también para " A-...-...-...-B
" (cualquier número de confirmaciones puede ser aplastado en la inicial de esta manera)
Aplastar la primera y la segunda confirmación daría lugar a que se reescribiera la primera confirmación. Si tiene más de una rama basada en la primera confirmación, cortaría esa rama.
Considere el siguiente ejemplo:
a---b---HEAD
/
/
''---d
Aplastar a y b en un nuevo "ab" de comillas daría como resultado dos árboles distintos, lo que en la mayoría de los casos no es deseable ya que git-merge y git-rebase ya no funcionarán en las dos ramas.
ab---HEAD
a---d
Si realmente quieres esto, se puede hacer. Eche un vistazo a git-filter-branch para obtener una herramienta potente (y peligrosa) para la reescritura de historial.
Esto aplastará el segundo commit en el primero:
ABC-... -> AB-C-...
git filter-branch --commit-filter ''
if [ "$GIT_COMMIT" = <sha1ofA> ];
then
skip_commit "$@";
else
git commit-tree "$@";
fi
'' HEAD
El mensaje de confirmación para AB se tomará de B (aunque preferiría de A).
Tiene el mismo efecto que la respuesta de Uwe Kleine-König, pero también funciona para A no inicial.
Hay una manera más fácil de hacer esto. Supongamos que estás en la rama master
Cree una nueva rama huérfana que eliminará todo el historial de confirmaciones:
$ git checkout --orphan new_branch
Agregue su mensaje de confirmación inicial:
$ git commit -a
Deshazte de la antigua rama maestra sin combinar:
$ git branch -D master
new_branch
nombre de tu rama actual new_branch
a master
:
$ git branch -m master
He reelaborado el guión de VonC para hacer todo automáticamente y no pedirme nada. Le das dos SHA1 de confirmación y aplastará todo entre ellos en una confirmación llamada "historial aplastado":
#!/bin/sh
# Go back to the last commit that we want
# to form the initial commit (detach HEAD)
git checkout $2
# reset the branch pointer to the initial commit (= $1),
# but leaving the index and working tree intact.
git reset --soft $1
# amend the initial tree using the tree from $2
git commit --amend -m "squashed history"
# remember the new commit sha1
TARGET=`git rev-list HEAD --max-count=1`
# go back to the original branch (assume master for this example)
git checkout master
# Replay all the commits after $2 onto the new initial commit
git rebase --onto $TARGET $2
Para lo que vale, evito este problema creando siempre un primer "no-op", en el que lo único en el repositorio es un .gitignore vacío:
https://github.com/DarwinAwardWinner/git-custom-commands/blob/master/bin/git-myinit
De esa manera, nunca hay ninguna razón para meterse con el primer commit.
Podría usar rebase interactivo para modificar los dos últimos confirmaciones antes de que hayan sido empujados a un control remoto
git rebase HEAD^^ -i
Puedes usar git filter-branch para eso. p.ej
git filter-branch --parent-filter /
''if test $GIT_COMMIT != <sha1ofB>; then cat; fi''
Esto da como resultado que AB-C elimine el registro de confirmación de A.
Si simplemente desea aplastar todas las confirmaciones en una única confirmación inicial, simplemente reinicie el repositorio y modifique la primera confirmación:
git reset hash-of-first-commit
git add -A
git commit --amend
Git reset dejará el árbol de trabajo intacto, por lo que todo sigue allí. Así que solo agregue los archivos con los comandos de git add, y corrija la primera confirmación con estos cambios. Sin embargo, en comparación con rebase, perderé la capacidad de combinar los comentarios de git.