¿Aplastar las dos primeras confirmaciones en Git? [duplicar]


Esta pregunta ya tiene una respuesta aquí:

Con git rebase --interactive <commit> puede aplastar cualquier número de confirmaciones juntas en una sola.

Todo eso es genial a menos que quieras aplastar los commits en el commit inicial. Eso parece imposible de hacer.

¿Hay alguna manera de lograr ¿eso?


Moderadamente relacionado:

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

Si estás interesado: git: ¿cómo insertar un commit como el primero, cambiando todos los demás?

Author: Community, 2009-02-28

9 answers

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

Ahora puedes rebase todas las confirmaciones hasta root, 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 reescribir todo el historial que lleva desde "$tip" hasta la confirmación de root.

Ver commit df5df20c1308f936ea542c86df1e9c6974168472 en GitHub desde Chris Webb (arachsys).


Respuesta original (febrero 2009)

Creo que encontrarás diferentes recetas para eso en la pregunta SO "¿Cómo combino las dos primeras confirmaciones de un repositorio git?"

Charles Bailey proporcionó allí la respuesta más detallada , recordándonos que un commit es un árbol completo (no solo difunde de estados anteriores).
Y aquí el antiguo commit (el "commit inicial") y el nuevo commit (resultado del squashing) no tendrán un ancestro común.
Eso significa que no puede "commit --amend " el commit inicial en uno nuevo, y luego rebase en el nuevo commit inicial el historial del commit inicial anterior (muchos conflictos)

(Esa última frase ya no es verdadera con git rebase -i --root <aBranch>)

Más bien (con A el "commit inicial" original, y B un commit posterior necesitaba ser aplastado en el inicial):

  1. Volver al último commit del que queremos formar el commit inicial (separar HEAD):

    git checkout <sha1_for_B>
    
  2. Restablece el puntero de la 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. Etiqueta temporalmente esta nueva confirmación inicial (o podrías recordar la nueva confirmación sha1 manualmente):

    git tag tmp
    
  5. Vuelve a la rama original (asume master para este ejemplo):

    git checkout master
    
  6. Reproducir todas las confirmaciones después de B en el nuevo commit inicial:

    git rebase --onto tmp <sha1_for_B>
    
  7. Elimine la etiqueta temporal:

    git tag -d tmp
    

De esa manera, el "rebase --onto" no introduce conflictos durante la fusión, ya que rebasa la historia hecha después de la última confirmación (B) que se aplastará en la inicial (que fue A) a tmp (representando la nueva confirmación inicial aplastada): solo se fusionan las fusiones triviales de avance rápido.

Que funciona para " A-B", pero también "A-...-...-...-B " (cualquier número de las confirmaciones pueden ser aplastadas en la inicial de esta manera)

 590
Author: VonC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:36

He rediseñado el script de VonC para hacer todo automáticamente y no pedirme nada. Le das dos SHA1 de commit y aplastará todo entre ellos en un commit llamado "historia aplastada":

#!/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
 28
Author: fonsinchen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-25 06:08:36

Por si sirve de algo, evito este problema creando siempre un primer commit "no-op", en el que la única cosa en el repositorio es una vacía .gitignore:

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.

 21
Author: Ryan Thompson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-07 20:30:37

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 ahí. Así que simplemente agrega los archivos usando los comandos git add, y modifica la primera confirmación con estos cambios. Sin embargo, en comparación con rebase-i, perderás la capacidad de combinar los comentarios de git.

 7
Author: Mike Looijmans,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-12 08:57:43

Esto aplastará el segundo commit en el primero:

A-B-C-... -> 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 la A no inicial.

 5
Author: Antony Hatchkins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-25 06:12:44

Aplastar la primera y la segunda confirmación resultaría en que la primera confirmación se reescribiera. Si tienes más de una rama basada en el primer commit, cortarías esa rama.

Considere el siguiente ejemplo:

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

Aplastar a y b en un nuevo commit "ab" resultaría en dos árboles distintos que en la mayoría de los casos no es deseable ya que git-merge y git-rebase ya no funcionarán a través de las dos ramas.

ab---HEAD

a---d

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

 3
Author: hillu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-03-01 14:41:54

Puedes usar git filter-branch para eso. por ejemplo,

git filter-branch --parent-filter \
'if test $GIT_COMMIT != <sha1ofB>; then cat; fi'

Esto resulta en AB-C desechando el registro de confirmación de A.

 3
Author: Uwe Kleine-König,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-25 06:13:20

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

git rebase HEAD^^ -i
 1
Author: todd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-27 18:48:07

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

Crea una nueva rama huérfana que eliminará todo el historial de confirmaciones:

$ git checkout --orphan new_branch

Añade tu mensaje de confirmación inicial:

$ git commit -a

Deshazte de la antigua rama maestra no fusionada:

$ git branch -D master

Cambie el nombre de su rama actual new_branch a master:

$ git branch -m master
 -4
Author: decandia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-25 21:15:12