tipos tag remove practices etiquetas crear commits best git rebase git-rebase squash

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):

  1. Regrese a la última confirmación en la que queremos formar la confirmación inicial (separar HEAD):

    git checkout <sha1_for_B>

  2. 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>

  3. Modifique el árbol inicial usando el árbol de ''B'':

    git commit --amend

  4. Etiquete temporalmente este nuevo compromiso inicial (o podría recordar el nuevo compromiso sha1 manualmente):

    git tag tmp

  5. Regrese a la rama original (suponga maestro para este ejemplo):

    git checkout master

  6. Repita todas las confirmaciones después de B en la nueva confirmación inicial:

    git rebase --onto tmp <sha1_for_B>

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



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.