¿Es posible empujar un escondite de git a un repositorio remoto?

205

En git, ¿es posible crear un alijo, empujar el alijo a un repositorio remoto, recuperar el alijo en otra computadora y aplicar el alijo?

O son mis opciones:

  • Cree un parche y cópielo a la otra computadora, o
  • ¿Crear una rama menor y comprometer el trabajo incompleto a esa rama?
Andrew Grimm
fuente

Respuestas:

68

No es posible obtenerlo a través de fetch más o menos, la especificación de referencia del espejo sí lo es fetch = +refs/*:refs/*, y aunque se oculta refs/stashno se envía. ¡Un explícito refs/stash:refs/stashtampoco tiene efecto!

De todos modos, sería confuso ya que eso no obtendría todos los escondites, solo el último; la lista de escondites es el reflog de la referencia refs/stashes.

u0b34a0f6ae
fuente
44
Puede obtener el último alijo de un control remoto de git, pero no en su alijo, solo en otra referencia. Algo así como git fetch some-remote +refs/stash:refs/remotes/some-remote/stashel git stash apply some-remote/stash. Pero no puede obtener escondites más antiguos porque están almacenados en el registro que no es recuperable. Ver stackoverflow.com/questions/2248680/…
sj26
75

Nota: Acabo de reescribir esta respuesta con 24 horas más de git-fu en mi haber :) En mi historial de shell, todo el shebang ahora tiene tres frases ingeniosas. Sin embargo, los he condensado para su conveniencia.

De esta manera, espero que puedas ver cómo hice las cosas, en lugar de tener que copiar / pegar cosas a ciegas.


Aquí está paso a paso.

Supongamos que es fuente en ~ / OLDREPO que contiene escondites. Cree un clon de PRUEBA que no contenga escondites:

cd ~/OLDREPO
git clone . /tmp/TEST

Empuja todos los escondites como ramas temporales:

git send-pack /tmp/TEST $(for sha in $(git rev-list -g stash); \
    do echo $sha:refs/heads/stash_$sha; done)

Bucle en el extremo receptor para transformar de nuevo en escondites:

cd /tmp/TEST/
for a in $(git rev-list --no-walk --glob='refs/heads/stash_*'); 
do 
    git checkout $a && 
    git reset HEAD^ && 
    git stash save "$(git log --format='%s' -1 HEAD@{1})"
done

Limpia tus ramas temporales si quieres

git branch -D $(git branch|cut -c3-|grep ^stash_)

Haz una lista de git stash y obtendrás algo como esto:

stash@{0}: On (no branch): On testing: openmp import
stash@{1}: On (no branch): On testing: zfsrc
stash@{2}: On (no branch): WIP on sehe: 7006283 fixed wrong path to binary in debianized init script (reported as part of issue
stash@{3}: On (no branch): WIP on debian-collab: c5c8037 zfs_pool_alert should be installed by default
stash@{4}: On (no branch): WIP on xattrs: 3972694 removed braindead leftover -O0 flag
stash@{5}: On (no branch): WIP on testing: 3972694 removed braindead leftover -O0 flag
stash@{6}: On (no branch): WIP on testing: db9f77e fuse_unmount_all could be starved for the mtx lock
stash@{7}: On (no branch): WIP on xattrs: db9f77e fuse_unmount_all could be starved for the mtx lock
stash@{8}: On (no branch): WIP on testing: 28716d4 fixed implicit declaration of stat64
stash@{9}: On (no branch): WIP on emmanuel: bee6660 avoid unrelated changes

En el repositorio original, el mismo parecía

stash@{0}: WIP on emmanuel: bee6660 avoid unrelated changes
stash@{1}: WIP on testing: 28716d4 fixed implicit declaration of stat64
stash@{2}: WIP on xattrs: db9f77e fuse_unmount_all could be starved for the mtx lock
stash@{3}: WIP on testing: db9f77e fuse_unmount_all could be starved for the mtx lock
stash@{4}: WIP on testing: 3972694 removed braindead leftover -O0 flag
stash@{5}: WIP on xattrs: 3972694 removed braindead leftover -O0 flag
stash@{6}: WIP on debian-collab: c5c8037 zfs_pool_alert should be installed by default
stash@{7}: WIP on sehe: 7006283 fixed wrong path to binary in debianized init script (reported as part of issue #57)
stash@{8}: On testing: zfsrc
stash@{9}: On testing: openmp import
sehe
fuente
1
Estoy aprendiendo mucho en poco tiempo, y siento que probablemente debería simplemente usar muchos comandos en mi enfoque anterior, lo que intentaré hacer más adelante.
Sehe
9
Esto funcionó bien para mí, excepto que necesitaba un paso git add .antes del git stash save ...paso ya que se git stashniega a guardar nuevos archivos a menos que hayan sido organizados. Además, canalizar el resultado a git rev-list ...través de tacinvierte el orden de los escondites para que salgan en el mismo orden.
Alan Krueger
1
@sehe Excelente script !! Dos sugerencias: 1): invierta la lista de referencia final para que las reservas estén en el mismo orden en el repositorio de destino que en el original. 2) Finalice el forbucle final con git branch -D stash_$a(limpiar a medida que se crean las reservas) para que si algo sale mal y volvemos a intentarlo, no reprocesamos las confirmaciones que ya se almacenaron correctamente.
Keith Robertson
1
Muchas gracias por tomarse el tiempo para explicar lo que hizo en lugar de "simplemente publicar la solución".
Marjan Venema
1
La solución puede mejorarse aún más: si reemplaza git stash save "$(git log --format='%s' -1 HEAD@{1})"con git update-ref --create-reflog -m "$(git show -s --format=%B $rev)" refs/stash $rev, obtendrá el mensaje de escondite original ( update-refes lo que hay git stash savedetrás de escena).
Sebastian Schrader
31

Llego un poco tarde a la fiesta, pero creo que encontré algo que funciona para mí con respecto a esto y que podría hacerlo también para usted si sus circunstancias son las mismas o similares.

Estoy trabajando en una función en su propia rama. La rama no se fusionó en master y se presionó hasta que finalizó o hice compromisos que me siento cómodo mostrando al público. Entonces, lo que hago cuando quiero transferir cambios no organizados a otra computadora es:

  • Realice una confirmación, con un mensaje de confirmación como " [non-commit] FOR TRANSFER ONLY", con el contenido que desea transferir.
  • Inicie sesión en la otra computadora.
  • Entonces hazlo:

    git pull ssh+git://<username>@<domain>/path/to/project/ rb:lb

    La URL puede diferir para usted si accede a su repositorio de una manera diferente. Esto extraerá los cambios de esa URL desde la rama remota "rb" a la rama local "lb". Tenga en cuenta que tengo un servidor ssh ejecutándose en mi propia computadora, y puedo acceder al repositorio de esa manera.

  • git reset HEAD^(implica --mixed)

    Esto restablece el HEAD para apuntar al estado antes del compromiso "[sin compromiso]".

From git-reset (1): " --mixed: restablece el índice pero no el árbol de trabajo (es decir, los archivos modificados se conservan pero no se marcan para confirmar) [...]"

Por lo tanto, tendrá sus cambios en los archivos al final, pero no se realizan confirmaciones para dominar y no es necesario un alijo.

Sin embargo, esto requerirá que lo haga git reset --hard HEAD^en el repositorio en el que realizó el "[no compromiso]", ya que ese compromiso es basura.

Victor Zamanian
fuente
Esto es mucho más sucio que simplemente crear una nueva rama de características y luego eliminarla después ...
Taegost
@Taegost depende de su entorno, supongo. Podría ser algo de CI / CD que impida simplemente empujar las ramas aguas arriba de todas formas. Pero sí, dependiendo de lo que prefiera, es posible que desee crear una rama para lograr lo mismo.
Victor Zamanian
22

Es un poco tarde, pero esta respuesta podría ayudar a alguien. Quería saber esto porque quería poder impulsar una función / error / lo que sea en progreso y trabajar desde el mismo punto en otra computadora.

Lo que funciona para mí es confirmar mi código en progreso (en una rama en la que estoy trabajando solo). Cuando llegue a mi otra computadora, haga un tirón, luego deshaga la confirmación con:

git reset --soft HEAD^

Continúa trabajando como eras, con todos tus cambios en progreso allí, sin compromiso y sin etapas.

Espero eso ayude.

Sir Robert
fuente
Cuando trato de hacer esto, el Origen aún mantiene el Compromiso, que no se confirmó. Cerca pero sin cigarro para mí.
rezwits
@rezwits Sí, el control remoto lo mantiene, pero es bastante fácil simplemente eliminar la rama temporal del origen.
Sir Robert
De hecho, ¡eso es lo que he estado haciendo!
rezwits
19

Parece que hay un truco muy bueno para resolver esto. puede usar git diff > file.diff(y confirmar el archivo), luego restaurar los cambios usando git apply file.diff(desde cualquier lugar) para lograr el mismo resultado.

Esto fue explicado aquí también.

Daniel Dubovski
fuente
55
si tiene archivos sin seguimiento: 1. git add. 2. git diff HEAD> file.diff
trickpatty
Enviar mensajes de diferencia a usted mismo no permite compromisos ni huellas en el repositorio. (por ejemplo: Note to self a través de la aplicación de escritorio Signal) o correo electrónico.
John Mee
9

Iría con un segundo enfoque, aunque no tengo idea de por qué no puedes comprometerlo a la rama maestra / destacada. Es posible hacer la recolección de cerezas también.

Eimantas
fuente
27
No hay ninguna razón técnica para no comprometerme con master / destacado, solo quiero decir "Esto no es un compromiso real, solo está ahorrando mi trabajo para poder obtenerlo en otra máquina".
Andrew Grimm
4

AFAIK toda la idea de esconder es esconder algo no tan importante debajo de la alfombra local . Nadie debería saber sobre tu basura favorita ;-) El único "pero" es: ¿Pero si me desarrollo en un par de estaciones de trabajo? Entonces scpes mucho mejor.

argent_smith
fuente
9
Algo así de divertido debería ser un comentario. ;-)
Andrew Grimm
2
Total git-ssh-newbie aquí, pero ¿puedes usar scp con github entonces?
Koen
No, la interfaz git-ssh de github está programada para que nunca tenga una consola / consola ssh. Solo puede ejecutar el proceso git del lado del servidor.
argent_smith
1
Entonces, ¿scp no es realmente una opción para este escenario si su rama maestra está en github? ¿Alguna otra sugerencia para transferir un alijo en ese caso?
Koen
1
He tratado de enfatizar que la transferencia de alijo no es posible en absoluto, AFAIK.
argent_smith
2

La respuesta actualmente aceptada es técnicamente correcta, no puede decirle directamente a Git que empuje todas sus reservas a un control remoto y luego tire todo a sus reservas locales en otra computadora.

Y aunque la respuesta votada en la actualidad debería funcionar, no me gustó que cree un montón de ramas temporales, y que requiera verificar manualmente la confirmación de almacenamiento y guardarla como un alijo, lo que puede conducir a problemas como este comentario mencionado , y conduce a un duplicado On (no branch): On testing:. ¡Seguramente debe haber una mejor manera!

Por lo tanto, si bien no puede empujar escondites directamente, un escondite es solo una confirmación (en realidad dos confirmaciones), y según la git pushpágina de manual, puede empujar confirmaciones:

A <src>menudo es el nombre de la rama que desea empujar, pero puede ser cualquier "expresión SHA-1" arbitraria ...

Elegí empujar los escondites para refs/stashes/*no abarrotar mi control remoto con ramas adicionales. Entonces puedo hacer eso con:

git push origin stash@{0}:refs/stashes/$(git rev-parse --short stash@{0})

(El rev-parsecomando obtiene el hash corto del alijo, que será único para el repositorio).

A continuación, necesito buscar el alijo de la otra computadora. Git solo busca ramas de forma predeterminada, por lo que necesito buscar las reservas específicamente:

git fetch origin refs/stashes/*:refs/stashes/*

Ahora, para convertir el alijo, vuelva a comprometerse en un alijo real. Como se mencionó, si bien podría verificar el compromiso de ocultamiento, restablecimiento y ocultamiento como de costumbre, no me gusta que requiera pasos adicionales o que no pueda mantener el estado de índice para el ocultamiento. Estaba buscando en línea una forma de hacerlo automáticamente, pero mi búsqueda fué fallando. Finalmente busqué en la página del manual git stash, donde encontré esto:

create
Cree un alijo (que es un objeto de confirmación regular) y devuelva su nombre de objeto, sin almacenarlo en ningún lugar del espacio de nombres de referencia. Esto está destinado a ser útil para los scripts. Probablemente no sea el comando que desea usar; ver "guardar" arriba.

store
Almacene un alijo dado creado a través de git stash create (que es una confirmación de fusión pendiente) en la reserva de reserva, actualizando el registro de reserva. Esto está destinado a ser útil para los scripts. Probablemente no sea el comando que desea usar; ver "guardar" arriba.

Como ya tengo el commit, storesuena como lo que quiero. Entonces puedo hacer:

git stash store --message "$(git show --no-patch --format=format:%s <SHA>)" <SHA>

Reemplazar <SHA>con el alijo que acaba de ser traído.

(El git showcomando obtiene el mensaje de confirmación del depósito oculto, para usarlo como mensaje para el registro oculto).

El alijo ahora aparece como normal en mi repositorio local:

$ git stash list
stash@{0}: On master: temp
...

Para limpiar el control remoto, las reservas se pueden eliminar del control remoto de la siguiente manera:

git push origin :refs/stashes/<SHA>

Este método también tiene el beneficio de ser idempotente: si ejecuta el pushcomando nuevamente, informará Everything up-to-date. El fetchcomando también se puede ejecutar de forma segura repetidamente. Si bien stash storesaltará el almacenamiento del alijo si es el mismo que el alijo más reciente, no evita duplicados de alijos más antiguos. Sin embargo, esto se puede solucionar, como lo hago en mi git-rstashscript, ver a continuación.


Para completar, también puede empujar fácilmente todos los escondites (con ):

for i in $(seq 0 $(expr $(git rev-list --walk-reflogs --count stash) - 1))
do
  git push origin stash@{$i}:refs/stashes/$(git rev-parse --short stash@{$i})
done

o importar todos los escondites recuperados:

for stash in $(ls .git/refs/stashes)
do
  git stash store --message "$(git show --no-patch --format=format:%s $stash)" $stash
done

He creado un script que se puede llamar como un subcomando (por ejemplo git rstash push 0) para que no tenga que recordar todo esto. git-rstashSe puede encontrar aquí.

Scott Weldon
fuente
1

Lo siguiente no funciona con el alijo, sino con los cambios no confirmados en el directorio de trabajo. Crea una rama, confirma automáticamente todos los cambios actuales y empuja al control remoto:

commit_and_push_ ( ) {
    # This will:
    #  1. checkout a new branch stash-XXX
    #  2. commit the current changes in that branch
    #  3. push the branch to the remote
    local locbr=${1:-autostash-XXX}
    git checkout -b $locbr
    git add .
    git commit -a -m "Automatically created commit"
    git push origin $locbr
    echo "Autocommitted changes in branch $locbr ..."
}

Usar como:

commit_and_push_ my-temp-branch
commit_and_push_
blueFast
fuente
0

Simplemente crearía una nueva rama oculta y la eliminaría siempre que esa rama no sea necesaria.

git add . // Add work-in-progress job
git checkout -b stash-branch // Create and checkout to stash-branch
git commit -m 'WIP: job description' // Commit message
git push origin stash-branch // Push to remote
git pull origin stash-branch // Pull the stash-branch
git checkout master // Checkout to working branch
git rebase stash-branch // Rebase the stash-branch
git reset --soft // Equivalent to stash!!
git branch -d stash-branch // Delete when not needed from local
git push -d origin stash-branch // Delete when not needed from remote
Bhojendra Rauniyar
fuente
-9

Solo usa Dropbox como lo hizo este tipo. De esa manera, no tiene que preocuparse por empujar escondites ya que todo su código estaría respaldado.

http://blog.sapegin.me/all/github-vs-dropbox

Ingeniero Tecnológico de Nueva York
fuente
2
Antes de que alguien tenga una reacción instintiva, no estoy diciendo que use Dropbox en lugar de Github, sino que almacene el código que no está listo para una confirmación en Dropbox que aún estaría bajo control de versiones localmente.
NYC Tech Engineer
44
tomará demasiado tiempo copiar todo el proyecto a la nube remota.
Stav Alfi