2011-06-11 7 views
34

He querido utilizar un comando git que guarda un alijo sin modificar mi árbol de trabajo, como una copia de seguridad liviana que está a salvo de cualquier reinicio de git o lo que sea que pueda hacer para arruinar mi índice. Básicamente, el equivalente funcional de "git stash save & & git stash apply" excepto que la copia de trabajo nunca se toca, ya que esto puede hacer que ciertos editores de texto/IDEs estén malhumorados.Git comando para guardar un alijo sin modificar el árbol de trabajo?

Algo como esto se está acercando a lo que quiero, pero no del todo:

git update-ref refs/stash `git stash create "Stash message"` 

Esto funciona funcionalmente, pero el problema que estoy teniendo es que ningún mensaje alijo aparece en la "lista alijo git", aunque el commit actual de stash tiene mi mensaje en él. Considerando lo grande que puede ser un alijo, los mensajes ocultos son muy importantes.

Respuesta

26

Gracias a punta de Carlos, me prepararon rápidamente un script bash para hacer exactamente lo que quería (que estaba corriendo en cuestiones de aplicación de la presente, ya que sólo un alias). Se necesita un mensaje oculto opcional como guardar guardado git. Si no se proporciona ninguno, usará el mensaje predeterminado generado por git stash.

#!/bin/sh 
# 
# git-stash-push 
# Push working tree onto the stash without modifying working tree. 
# First argument (optional) is the stash message. 
if [ -n "$1" ]; then 
     git update-ref -m "$1" refs/stash "$(git stash create \"$1\")" 
else 
     HASH=`git stash create` 
     MESSAGE=`git log --no-walk --pretty="tformat:%-s" "$HASH"` 
     git update-ref -m "$MESSAGE" refs/stash "$HASH" 
fi 

Editar: Como se señaló en un comentario más abajo, el ahorro de este script como git-stash-push algún lugar de su trayectoria es suficiente para poder invocar tecleando git stash-push.

Lo bueno de esto es que incluso si deja un alijo hecho con este método, ¡aún podrá ver el mensaje de alijo usando git log [commit-hash] del compromiso pendiente!

Editar: desde Git 2.6.0 puede agregar --create-reflog-update-ref y luego git stash list va a mostrar esto incluso si git stash no se había utilizado anteriormente.

+5

Si llama a ese archivo 'git-stash-push' y lo coloca en algún lugar de su RUTA, no hay necesidad de crear un alias para él. 'git stash-push' encontrará (y llamará)' git-stash-push' –

+0

¿De verdad? Interesante, lo intentaré. – Eliot

+0

Una advertencia con el script anterior, parece que no funciona correctamente cuando el alijo está vacío. – Eliot

12

Debe pasar el mensaje a update-ref, no stash create como stash create no recibe un mensaje (no actualiza ninguna referencia, por lo que no tiene ninguna entrada de reflog para rellenar).

git update-ref -m "Stash message" refs/stash "$(git stash create)" 
+0

Gracias, eso es lo que estaba buscando! Sin embargo, eres incorrecto sobre git stash create sin tomar un mensaje. El mensaje que se le proporciona se convierte en el mensaje de confirmación. Así que para emular perfectamente el commit de stash creado por git stash save, creo que el siguiente comando hará el trabajo: 'git update-ref -m '' Mensaje de ocultación" refs/stash "$ (git stash create 'Stash message')" ' . ¿Puedes pensar en una forma inteligente de agregar esto directamente como un alias git? Si no, puedo convertirlo en un simple script de bash. – Eliot

+0

@Eliot: El mensaje que aparece en 'git stash list' proviene del reflog, no del mensaje de confirmación. A eso me estaba refiriendo. Tiene razón en que 'create' si sucede, lleva un mensaje, pero esta no es una característica documentada, por lo que no sé si se ha diseñado deliberadamente para que funcione o si solo se trata de un artefacto de la implementación. –

+0

Sí, buen punto. Lo descubrí accidentalmente Encontré algunos documentos en la wiki de git sobre el uso de argumentos en alias, ¡así que estoy listo para empezar! – Eliot

2

Inspirado por la solución de Eliot, que extendieron su guión un poco:

#!/bin/sh 
# 
# git-stash-push 
# Push working tree onto the stash without modifying working tree. 
# First argument (optional) is the stash message. 
# 
# If the working dir is clean, no stash will be generated/saved. 
# 
# Options: 
# -c "changes" mode, do not stash if there are no changes since the 
#  last stash. 
if [ "$1" == "-c" ]; then 
     CHECK_CHANGES=1 
     shift 
fi 


if [ -n "$1" ]; then 
     MESSAGE=$1 
     HASH=$(git stash create "$MESSAGE") 
else 
     MESSAGE=`git log --no-walk --pretty="tformat:%-s" "HEAD"` 
     MESSAGE="Based on: $MESSAGE" 
     HASH=$(git stash create) 
fi 

if [ "$CHECK_CHANGES" ]; then 
     # "check for changes" mode: only stash if there are changes 
     # since the last stash 

     # check if nothing has changed since last stash 
     CHANGES=$(git diff [email protected]{0}) 
     if [ -z "$CHANGES" ] ; then 
       echo "Nothing changed since last stash." 
       exit 0 
     fi 
fi 

if [ -n "$HASH" ]; then 
     git update-ref -m "$MESSAGE" refs/stash "$HASH" 
     echo "Working directory stashed." 
else 
     echo "Working tree clean, nothing to do." 
fi 

he implementado los siguientes cambios en el guión de Eliot:

  1. Cuando directorio de trabajo es limpio, el guión salir con gracia
  2. Cuando se utiliza el interruptor -c, si no hay cambios en comparación con el último alijo, la secuencia de comandos saldrá. Esto es útil si usa este script como una "máquina del tiempo", haciendo un alijo automático cada 10 minutos. Si nada ha cambiado, no se crea ningún nuevo alijo. Sin este cambio, puede terminar con n depósitos consecutivos que son iguales.

No es que a fin de que el interruptor -c funcione correctamente, al menos un alijo debe existir, de lo contrario el guión lanza un error en git diff [email protected]{0} y no hará nada.

que utilizar este script como una "máquina del tiempo", snapshotting cada 10 minutos utilizando el siguiente bucle de bash:

while true ; do date ; git stash-push ; sleep 600 ; done 
Cuestiones relacionadas