Aplaste los primeros dos commits en Git?

Con git rebase --interactive <commit> puedes aplastar cualquier cantidad de commits en uno solo.

Eso es genial, a less que quieras aplastar las confirmaciones en la confirmación inicial. Eso parece imposible de hacer.

¿Hay alguna forma de lograrlo?


Moderadamente relacionado:

En una pregunta relacionada, me las arreglé para llegar a un enfoque diferente a la necesidad de aplastar contra el primer compromiso, que es, bueno, para que sea el segundo.

Si te interesa: git: ¿cómo insert un compromiso como el primero, cambiando todos los demás?

Actualización de julio de 2012 ( git 1.7.12+ )

Ahora puede volver a establecer la base de todos los commits hasta la raíz, y seleccionar el segundo commit Y para ser aplastado con la primera X

 git rebase -i --root master pick sha1 X squash sha1 Y pick sha1 Z 
 git rebase [-i] --root $tip 

Este command ahora se puede usar para reescribir 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 de 2009)

Creo que encontrarás diferentes recetas para eso en la pregunta de SO " ¿Cómo combino los primeros dos commits de un repository de git? "

Charles Bailey proporcionó la respuesta más detallada , recordándonos que una confirmación es un tree completo (no solo difiere de un estado anterior).
Y aquí el antiguo compromiso (el "compromiso inicial") y el nuevo compromiso (resultado del aplastamiento) no tendrán antepasado común.
Eso significa que no se puede " commit --amend " el compromiso inicial en uno nuevo, y luego rebase en la nueva confirmación inicial el historial de la confirmación inicial (muchos conflictos)

(Esa ú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):

  1. Regrese al último compromiso que queremos para formar la confirmación inicial (separar HEAD):

     git checkout <sha1_for_B> 
  2. Restablezca el puntero de bifurcación a la confirmación inicial, pero dejando el índice y el tree de trabajo intactos:

     git reset --soft <sha1_for_A> 
  3. Modifíquese el tree inicial usando el tree de 'B':

     git commit --amend 
  4. Etiquete temporalmente esta nueva confirmación inicial (o podría recordar la nueva confirmación sha1 manualmente):

     git tag tmp 
  5. Vuelve a la twig original (asume el maestro para este ejemplo):

     git checkout master 
  6. Reproduce todas las confirmaciones después de B en la nueva confirmación inicial:

     git rebase --onto tmp <sha1_for_B> 
  7. Eliminar la label temporal:

     git tag -d tmp 

De esta forma, el " rebase --onto " no introduce conflictos durante la fusión, ya que rebasesa 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): avance rápido trivial solo se fusiona.

Eso funciona para " AB ", pero también " A-...-...-...-B " (cualquier cantidad de confirmaciones se puede aplastar en la inicial de esta manera)

Repasé el guión de VonC para hacer todo automáticamente y no pedirme nada. Le das dos SHA1 de compromiso 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 

Por lo que vale, evito este problema creando siempre una primera confirmación "no operativa", en la que lo único en el repository 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 compromiso.

Esto comprimirá el segundo compromiso en el primero:

ABC-... -> AB-C-...

 git filter-branch --commit-filter ' if [ "$GIT_COMMIT" = <sha1ofA> ]; then skip_commit "[email protected]"; else git commit-tree "[email protected]"; fi ' HEAD 

El post 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.

Si simplemente desea aplastar todas las confirmaciones en una única confirmación inicial, simplemente reinicie el repository y modifique la primera confirmación:

 git reset hash-of-first-commit git add -A git commit --amend 

El reinicio de Git dejará intacto el tree de trabajo, por lo que todo sigue allí. Así que simplemente agregue los files usando los commands de agregar git y modifique la primera confirmación con estos cambios. Comparado con rebase -i, perderás la capacidad de fusionar los comentarios de git.

Limpiar el primer y segundo commit daría como resultado la reescritura del primer commit. Si tiene más de una twig basada en la primera confirmación, cortaría esa twig.

Considere el siguiente ejemplo:

 a---b---HEAD \ \ '---d 

Limpiar ayb en un nuevo compromiso "ab" daría como resultado dos treees distintos que en la mayoría de los casos no son deseables, ya que git-merge y git-rebase ya no funcionarán en las dos twigs.

 ab---HEAD a---d 

Si realmente quieres esto, se puede hacer. Eche un vistazo a git-filter-branch para get una herramienta poderosa (y peligrosa) para reescribir el historial.

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 arroje el logging de confirmación de A.

Podría usar rebase interactive para modificar las dos últimas confirmaciones antes de que se hayan enviado a un control remoto

 git rebase HEAD^^ -i 

Hay una manera más fácil de hacer esto. Supongamos que estás en la twig master

Crea una nueva sucursal huérfana que eliminará todo el historial de commits:

 $ git checkout --orphan new_branch 

Agregue su post de confirmación inicial:

 $ git commit -a 

Deshágase de la antigua twig maestra no fusionada:

 $ git branch -D master 

new_branch nombre de tu new_branch twig new_branch a master :

 $ git branch -m master