2010-12-29 9 views
7

Simple Version:Usando git filter-branch para eliminar compromete por su mensaje de entrega

Si tengo una rama "foo-555", con un montón de confirmaciones con mensajes como:

  • foo 555: bla
  • foo 123: bla bla
  • foo 555: bla, bla, bla
  • foo 321: blahblah

y quiero eliminar todas las confirmaciones que no comienzan con "foo 555:", ¿hay alguna manera de hacerlo utilizando git filter-branch (o cualquier otra herramienta para el caso)?

Versión original (más detallada):

En nuestro repositorio tenemos una convención donde cada mensaje de registro comienza con un cierto patrón:

Redmine # 555: SOME_MESSAGE

También hacemos un poco de rebase para traer los posibles cambios de la rama de publicación a la rama de un tema específico. En otras palabras, podría tener la rama "foo-555", pero antes de fusionarla en la rama "prelanzamiento" necesito obtener las confirmaciones previas a la publicación que foo-555 no (para que el foo- 555 puede fusionarse rápidamente para prelanzamiento).

Sin embargo, debido a que el prelanzamiento a veces cambia, a veces terminamos con situaciones en las que ingresa un compromiso de prelanzamiento, pero luego ese compromiso posterior se elimina del prelanzamiento. Es fácil identificar las confirmaciones que provienen de la versión preliminar, ya que el número de su mensaje de confirmación no coincidirá con el número de la sucursal; por ejemplo, si veo "Redmine # 123: ..." en mi sucursal de foo-555, sé que no es una confirmación de mi sucursal.

Así que ahora la pregunta: me gustaría eliminar todas las confirmaciones que "no pertenecen" a una sucursal; en otras palabras, cualquier commit que:

  • está en mi rama foo-555, pero no en la rama de pre-lanzamiento (pre-release..foo-555)
  • tiene un mensaje Eso no comprometen comience con "Redmine # 555"

pero, por supuesto, "555" puede variar de una rama a otra. ¿Hay alguna forma de utilizar filter-branch (o cualquier otra herramienta) para lograr esto? Actualmente, la única forma en que puedo verlo es ir a una base de datos interactiva ("git rebase -i") y eliminar manualmente todas las confirmaciones "incorrectas".

+0

¿No puedes cereza recoger las confirmaciones que desee en la rama pertinente? –

+0

* Podemos *, pero digamos que tengo 10 555 confirmaciones y otras 10 confirmaciones; Tendría que reiniciar y luego hacer 10 selecciones de cereza (contra un comando de filtro de rama ... si tal cosa es posible). – machineghost

Respuesta

4

escribir un script para eliminar las líneas con Redmine #555:

#!/bin/sh 

mv $1 $1.$$ 
grep -v 'Redmine #555' < $1.$$ > $1 
rm -f $1.$$ 

Por supuesto que puede hacer que le apetezca (por ejemplo echo un guión de comandos para ed).

A continuación, poner en marcha su rebase con EDITOR conjunto a su script:

EDITOR=/path/to/script git rebase -i REVISION 

Por supuesto que todavía no se garantiza para completar - que puede haber errores durante el rebase causado al dejar fuera las revisiones. Todavía puede repararlos y git rebase --continue manualmente.

+0

Parece que funcionaría, pero ... ¿no hay forma de usar Git (sin scripts de shell) y automatizar el proceso a través de "git filter-branch --commit-filter"? – machineghost

+1

La ayuda para '--commit-filter' menciona específicamente que' skip_commit' omite el compromiso pero ** no los cambios ** y dice que use 'git-rebase' en su lugar. 'filter-branch' considera tus revisiones como una secuencia de estados y te permite permutar cada commit, pero los cambios no se propagan a los hijos. 'rebase' considera tus revisiones como una pila de parches y cualquier modificación midstream * does * se propaga a futuras revisiones. Pero eso puede causar fallas y por eso no puede ser completamente automático. –

+0

Ahhhhhhh. ¡Gracias! – machineghost

8

Aquí hay una solución rápida que usa filter-branch en lugar de volver a basar. No hay interactividad o necesidad de resolver conflictos.

git filter-branch --commit-filter ' 
    if [ `git rev-list --all --grep "<log-pattern>" | grep -c "$GIT_COMMIT"` -gt 0 ] 
    then 
     skip_commit "[email protected]"; 
    else 
     git commit-tree "[email protected]"; 
    fi' HEAD 

Es probable que desee a continuación, limpiar con:

git reflog expire --expire=now 
git gc --prune=now 
+1

Para repositorios grandes con muchas confirmaciones, esto puede tardar un tiempo (por ejemplo, varios segundos por filtro de confirmación en FreeBSD '-CURRENT', que tiene cientos de miles de confirmaciones). Una alternativa mucho más rápida para la condición 'git rev-list --all [...]' es 'git show $ GIT_COMMIT | grep -c "" '. – trombonehero

Cuestiones relacionadas