Cómo aplastar a todos los git commits en uno?

¿Cómo aplastas tu repository completo hasta el primer compromiso?

Puedo volver a la base del primer commit, pero eso me dejaría con 2 commits. ¿Hay alguna manera de hacer reference a la confirmación antes de la primera?

Quizás la forma más fácil es simplemente crear un nuevo repository con el estado actual de la copy de trabajo. Si desea conservar todos los posts de confirmación, primero puede hacer git log > original.log y luego editarlo para su post de confirmación inicial en el nuevo repository:

 rm -rf .git git init git add . git commit 

o

 git log > original.log # edit original.log as desinetworking rm -rf .git git init git add . git commit -F original.log 

En versiones recientes de git, puedes usar git rebase --root -i .

Para cada compromiso, excepto el primero, cambie la pick para squash .

Actualizar

He hecho un alias git squash-all .
Ejemplo de uso : git squash-all "a brand new start" .

 [alias] squash-all = "!f(){ git reset $(git commit-tree HEAD^{tree} -m \"${1:-A new start}\");};f" 

Advertencia : recuerde proporcionar un comentario; de lo contrario, se utilizará el post de confirmación pnetworkingeterminado "Un nuevo inicio".

O puede crear el alias con el siguiente command:

 git config --global alias.squash-all '!f(){ git reset $(git commit-tree HEAD^{tree} -m "${1:-A new start}");};f' 

Un trazador de líneas

 git reset $(git commit-tree HEAD^{tree} -m "A new start") 

Nota : aquí " A new start " es solo un ejemplo, no dude en usar su propio idioma.

TL; DR

No necesita squash, use git commit-tree para crear un commit huérfano e ir con él.

Explique

  1. crear una única confirmación a través de git commit-tree

    Lo que git commit-tree HEAD^{tree} -m "A new start" hace es:

    Crea un nuevo object de compromiso basado en el object de tree proporcionado y emite el nuevo ID de object de compromiso en stdout. El post de logging se lee de la input estándar, a less que se den opciones -m o -F.

    La expresión HEAD^{tree} significa el object de tree correspondiente a HEAD , es decir, la punta de su twig actual. ver Árbol-Objetos y Commit-Objetos .

  2. restablecer la twig actual a la nueva confirmación

    Luego, git reset simplemente restablece la twig actual al object commit recién creado.

De esta forma, no se toca nada en el espacio de trabajo, ni hay necesidad de rebase / squash, lo que lo hace realmente rápido. Y el time necesario es irrelevante para el tamaño del repository o la profundidad del historial.

Variación: Nueva Repo desde una Plantilla de Proyecto

Esto es útil para crear el "compromiso inicial" en un nuevo proyecto usando otro repository como la plantilla / archetype / seed / skeleton. Por ejemplo:

 cd my-new-project git init git fetch --depth=1 -n https://github.com/toolbear/panda.git git reset --hard $(git commit-tree FETCH_HEAD^{tree} -m "initial commit") 

Esto evita agregar el repository de plantilla como un control remoto ( origin o no) y queuepsa el historial del repository de la plantilla en su confirmación inicial.

Si todo lo que quieres hacer es aplastar todas tus confirmaciones hasta la confirmación raíz, entonces mientras

 git rebase --interactive --root 

puede funcionar, no es práctico para un gran número de confirmaciones (por ejemplo, cientos de confirmaciones), porque la operación de rebase probablemente se ejecutará muy lentamente para generar la list de confirmación del editor de rebase interactivo, así como ejecutar la propia rebase.

Aquí hay dos soluciones más rápidas y eficientes cuando elimina un gran número de confirmaciones:

Solución alternativa n. ° 1: twigs huérfanas

Simplemente puede crear una nueva sucursal huérfana en la punta (es decir, la confirmación más reciente) de su sucursal actual. Esta twig huérfana forma la confirmación raíz inicial de un tree de historial de compromisos completamente nuevo e independiente, que es en realidad equivalente a aplastar todas sus confirmaciones:

 git checkout --orphan new-master master git commit -m "Enter commit message for your new initial commit" # Overwrite the old master branch reference with the new one git branch -M new-master master 

Documentación:

  • git-checkout (1) Página manual .

Solución alternativa n. ° 2: reinicio por software

Otra solución eficiente es simplemente usar un restablecimiento mixto o blando a la confirmación <root> :

 git branch beforeReset git reset --soft <root> git commit --amend # Verify that the new amended root is no different # from the previous branch state git diff beforeReset 

Documentación:

  • git-reset (1) Página manual .
 echo "message" | git commit-tree HEAD^{tree} 

Esto creará una confirmación huérfana con el tree de HEAD y dará su nombre (SHA-1) en stdout. Entonces solo reinicia tu twig allí.

 git reset SHA-1 

La forma más fácil es usar la update-ref order del 'plomería' para eliminar la twig actual.

No puedes usar git branch -D ya que tiene una válvula de security para evitar que elimines la twig actual.

Esto te devuelve al estado de 'compromiso inicial' donde puedes comenzar con una nueva confirmación inicial.

 git update-ref -d refs/heads/master git commit -m "New initial commit" 

Así es como terminé haciendo esto, por si acaso funciona para otra persona:

Recuerde que siempre hay riesgo al hacer cosas como esta, y nunca es una mala idea crear una twig de save antes de comenzar.

Comience registrando

 git log --oneline 

Desplácese hasta la primera confirmación, copie SHA

 git reset --soft <#sha#> 

Reemplace <#sha#> con el SHA copydo del logging

 git status 

Asegúrate de que todo esté verde; de ​​lo contrario, ejecuta git add -A

 git commit --amend 

Enmendar todos los cambios actuales al primer compromiso actual

Ahora fuerza empuje esta twig y sobrescribirá lo que está allí.

Primero, aplaste todas sus confirmaciones en una única confirmación mediante git rebase --interactive . Ahora te quedan dos compromisos para aplastar. Para hacerlo, lea cualquiera de

  • ¿Cómo combino los primeros dos commits de un repository de Git?
  • git: cómo aplastar los primeros dos commits?

Leí algo sobre el uso de injertos, pero nunca lo investigué demasiado.

De todos modos, puedes aplastar esos dos últimos commits manualmente con algo como esto:

 git reset HEAD~1 git add -A git commit --amend 

Para aplastar usando injertos

Agregue un file .git/info/grafts , coloque allí el hash de confirmación que desea que se convierta en su raíz

git log ahora comenzará a partir de ese compromiso

Para que sea 'real' ejecuta git filter-branch

Esta respuesta mejora en un par más arriba (por favor vótelos), suponiendo que además de crear un compromiso (no-padres sin historial), también desea conservar todos los datos de compromiso de ese compromiso:

  • Autor (nombre y correo electrónico)
  • Fecha autorizada
  • Commiter (nombre y correo electrónico)
  • Fecha comprometida
  • Commmit log message

Por supuesto, el commit-SHA del commit nuevo / único cambiará, ya que representa un nuevo (no) historial, convirtiéndose en un parentless / root-commit.

Esto puede hacerse leyendo el git log y configurando algunas variables para git commit-tree . Suponiendo que desea crear una única confirmación desde el master en una nueva bifurcación de one-commit sucursal, conservando los datos de confirmación arriba:

 git checkout -b one-commit master ## create new branch to reset git reset --hard \ $(eval "$(git log master -n1 --format='\ COMMIT_MESSAGE="%B" \ GIT_AUTHOR_NAME="%an" \ GIT_AUTHOR_EMAIL="%ae" \ GIT_AUTHOR_DATE="%ad" \ GIT_COMMITTER_NAME="%cn" \ GIT_COMMITTER_EMAIL="%ce" \ GIT_COMMITTER_DATE="%cd"')" 'git commit-tree master^{tree} <<COMMITMESSAGE $COMMIT_MESSAGE COMMITMESSAGE ')