¿Cómo envío la confirmación modificada al repositorio Git remoto?

663

Cuando trabajé un poco con mi código fuente, hice mi compromiso habitual y luego ingresé a un repositorio remoto. Pero luego noté que olvidé organizar mis importaciones en el código fuente. Entonces hago el comando de modificación para reemplazar el commit anterior:

> git commit --amend

Lamentablemente, la confirmación no se puede devolver al repositorio. Se rechaza así:

> git push origin
To //my.remote.repo.com/stuff.git/
 ! [rejected]        master -> master (non-fast forward)
error: failed to push some refs to '//my.remote.repo.com/stuff.git/'

¿Qué tengo que hacer? (Puedo acceder al repositorio remoto).

Spoike
fuente
¿Qué pasa si mi --amend fue solo para cambiar el mensaje de confirmación? ¿Alguna forma de editar el último mensaje de confirmación solo, si ya fue empujado a remoto? Lo hice en Github y recibí el mismo mensaje sobre el avance no rápido. Luego apliqué una solución a continuación, pero la fusión acaba de agregar más mensajes de confirmación en la parte superior ..
77
@faB: creo que es una pregunta frecuente. Un mensaje de confirmación se codifica junto con la confirmación, por lo que al cambiarlo se cambia el revid (hash). Si no está claro: no, no puedes. IIRC puede almacenar información fuera de banda en notas (para que pueda anotar confirmaciones existentes sin alterarlas). Para etiquetar confirmaciones específicas, use etiquetas
sehe
1
Pronto (git1.8.5, Q4 2013) podrá hacerlo con git push -forcemás cuidado .
VonC
3
Aquí está el estilo vaquero. No aprenda más ni busque formas de deshacer la modificación git anterior. Simplemente agregue un código de marcador de posición, quiero decir, agregue algún comentario, limpie un poco de código o simplemente agregue algunos guiones, guiones, guiones ... Ahora haga una confirmación real y empújela al control remoto. Hecho !
nehem
@ user58777 Si su -amend fue solo para cambiar el mensaje de confirmación y no ha realizado ninguna confirmación local adicional desde entonces, puede restablecer su rama local a la confirmación remota que presionó antes de modificar el mensaje de confirmación.
Scott Ahten

Respuestas:

504

En realidad, una vez empujó con --forcey .gitrepositorio y regañaron por Linus BIG TIME . En general, esto creará muchos problemas para otras personas. Una respuesta simple es "No lo hagas".

Veo que otros dieron la receta para hacerlo de todos modos, así que no los repetiré aquí. Pero aquí hay un consejo para recuperarse de la situación después de haber eliminado la confirmación modificada con --force (o + master).

  1. Úselo git reflogpara encontrar el compromiso anterior que modificó (llámelo old, y llamaremos al nuevo compromiso que creó modificando new).
  2. Crea una fusión entre oldy new, registrando el árbol de new, como git checkout new && git merge -s ours old.
  3. Combina eso con tu maestro con git merge master
  4. Actualiza tu master con el resultado con git push . HEAD:master
  5. Empuje el resultado.

Entonces las personas que tenían la desgracia de haber basado su trabajo en el commit que obliterado por la que se modifica y forzando un empuje verá la fusión resultante verá que usted a favor de newmás old. Sus fusiones posteriores no verán los conflictos entre oldy newque resultaron de su modificación, por lo que no tienen que sufrir.

João Pimentel Ferreira
fuente
17
Soy muy consciente de lo que sucede cuando fuerza forzar una confirmación modificada (destruyendo el historial). Afortunadamente, fui el único desarrollador en el proyecto con el repositorio remoto en una unidad de red, por lo que no fue un gran problema. Nunca pensé en fusionar un commit de modificación, así que votaré esto.
Spoike
6262
En nuestra empresa, forzamos a empujar con bastante regularidad ... en ramas de características desarrolladas por individuos.
Ondra Žižka
2
El regaño de Linus se debió a que borraste la historia con la opción de fuerza, no porque no debieras hacerlo. La solución de GabrielleV funciona bien, porque no cambia la historia.
user411279
2
Por favor, dado que el autor (gitter) de esta respuesta parece que ya no existe, ¿alguien podría ayudar a aclarar el ítem número 1: encontrar la confirmación anterior? Si no tiene una copia de seguridad, ¿dónde la encontraría? ¿Modificar y forzar el empuje no lo destruiría? ¿Tal vez se refiere a obtenerlo de un amigo / colaborador que todavía lo tiene en el árbol?
Dr Beco
2
Dr. Breco, puede usarlo git reflogpara encontrarlo
Simon Zyx
269

Estás viendo una característica de seguridad de Git. Git se niega a actualizar la rama remota con su rama, porque la confirmación principal de su rama no es un descendiente directo de la confirmación principal actual de la rama a la que está presionando.

Si este no fuera el caso, entonces dos personas que presionan al mismo repositorio aproximadamente al mismo tiempo no sabrían que hay una nueva confirmación entrando al mismo tiempo y el que presionó al último perdería el trabajo del empujador anterior sin ninguno de los dos. ellos dándose cuenta de esto.

Si sabe que usted es la única persona que presiona y desea presionar una confirmación modificada o una confirmación que revierte la rama, puede 'forzar' a Git a actualizar la rama remota mediante el -finterruptor.

git push -f origin master

Incluso esto puede no funcionar ya que Git permite que los repositorios remotos rechacen los empujes no rápidos en el extremo utilizando la variable de configuración receive.denynonfastforwards. Si este es el caso, el motivo de rechazo se verá así (tenga en cuenta la parte 'remoto rechazado'):

 ! [remote rejected] master -> master (non-fast forward)

Para evitar esto, debe cambiar la configuración del repositorio remoto o, como un truco sucio, puede eliminar y volver a crear la rama de esta manera:

git push origin :master
git push origin master

En general, el último parámetro git pushutiliza el formato <local_ref>:<remote_ref>, donde local_refes el nombre de la rama en el repositorio local y remote_refes el nombre de la rama en el repositorio remoto. Este par de comandos usa dos shorthands. :mastertiene un local_ref nulo, lo que significa empujar una rama nula hacia el lado remoto master, es decir, eliminar la rama remota. Un nombre de rama sin ningún :medio empuja la rama local con el nombre dado a la rama remota con el mismo nombre. masteren esta situación es la abreviatura de master:master.

CB Bailey
fuente
2
esto no funcionó con github, me dio el siguiente mensaje: maestro [rechazado a distancia] (se prohíbe la eliminación de la rama actual)
vedang
No quería forzar el empuje (que sabía que resolvería el problema), pero ahora supongo que no tengo otra opción.
Vedang
1
Esta es la única solución que funcionó para mi repositorio alojado con assembla.
Justin
1
¿eliminar la rama maestra remota liberará el espacio en el repositorio remoto?
Mr_and_Mrs_D
1
@Mr_and_Mrs_D: No inmediatamente, pero después de una git gcvez que los reflogs hayan expirado, se eliminarán los objetos viejos. Nadie que clone el repositorio obtendrá objetos a los que ya no se pueda acceder tan pronto como se haya actualizado la rama.
CB Bailey
211

Discurso rápido: el hecho de que nadie haya publicado la respuesta simple aquí demuestra la desesperada hostilidad del usuario exhibida por la CLI de Git.

De todos modos, la forma "obvia" de hacer esto, suponiendo que no haya intentado forzar el empuje, es tirar primero. Esto tira del cambio que modificó (y ya no tiene) para que lo tenga nuevamente.

Una vez que haya resuelto cualquier conflicto, puede presionar nuevamente.

Entonces:

git pull

Si obtiene errores en la extracción, tal vez algo está mal en la configuración de su repositorio local (tuve una referencia incorrecta en la sección de la rama .git / config).

Y después

git push

Tal vez obtendrá un compromiso adicional con el tema que habla sobre una "fusión trivial".

GabrieleV
fuente
2
Sí, escribí sobre esto, vea stackoverflow.com/questions/253055/… ;)
Spoike
10
Esto realmente no funciona como esperaba. Crea dos nuevos commits. Uno que es una réplica del anterior, pero con los cambios enmendados. Y una fusión se compromete con un diff vacío. Todavía dejo el viejo commit sin cambios, revelando datos posiblemente confidenciales que estaba tratando de modificar. Creo git push -fo git resetes la única forma de ir aquí.
Thnee
40
Aunque técnicamente responde al problema, en realidad no aborda el problema. Como dijiste, generará un commit adicional, pero la razón principal por la que las personas modifican un commit es para evitar crear uno nuevo. Entonces, si el póster siguiera sus instrucciones, no obtendría el resultado deseado. Tendría el mismo sentido no modificar el compromiso en primer lugar.
Dan Jones
102

Respuesta corta: no empuje los compromisos modificados a un repositorio público.

Respuesta larga: algunos comandos de Git, como git commit --amendy git rebase, en realidad reescriben el gráfico del historial. Esto está bien siempre y cuando no haya publicado sus cambios, pero una vez que lo haga, realmente no debería estar revisando el historial, porque si alguien ya recibió sus cambios, entonces cuando intenten volver a hacerlo, podría fallar . En lugar de enmendar una confirmación, debe hacer una nueva confirmación con los cambios.

Sin embargo, si realmente desea impulsar una confirmación modificada, puede hacerlo así:

$ git push origin +master:master

El +signo inicial obligará a que se produzca el impulso, incluso si no resulta en una confirmación de "avance rápido". (Una confirmación de avance rápido ocurre cuando los cambios que está presionando son un descendiente directo de los cambios que ya están en el repositorio público).

mipadi
fuente
55
¿Cómo es esto diferente (mejor o peor) que git push -f? ¡Gracias!
bentford
11
@ Bentford: Básicamente es lo mismo que git push -f.
mipadi
54

Aquí hay una manera muy simple y limpia de impulsar sus cambios después de que ya haya realizado un commit --amend:

git reset --soft HEAD^
git stash
git push -f origin master
git stash pop
git commit -a
git push origin master

Que hace lo siguiente:

  • Restablecer la cabeza de la rama a la confirmación principal.
  • Guarda este último compromiso.
  • Forzar el empuje al control remoto. El control remoto ahora no tiene la última confirmación.
  • Abre tu escondite.
  • Comprometerse limpiamente.
  • Empuje al control remoto.

Recuerde cambiar "origen" y "maestro" si aplica esto a una rama o control remoto diferente.

Faiza
fuente
3
2 observaciones: - asegúrese de cambiar el nombre de la sucursal si está trabajando en otra - tuve que usar git addantes de mi confirmación para incluir los cambios.
SylvainB
1
En Windows CMD, el primer comando debe ser escapado: git reset --soft "HEAD^". El resto funciona bien.
MrMister
2
"Una manera muy simple y limpia .." cit. Este procedimiento incluye empuje forzado. A la luz de todas las críticas en las respuestas anteriores, no estoy seguro de si este procedimiento es realmente limpio.
Na13-c
24

Lo resolví descartando mi confirmación local modificada y agregando los nuevos cambios en la parte superior:

# Rewind to commit before conflicting
git reset --soft HEAD~1

# Pull the remote version
git pull

# Add the new commit on top
git add ...
git commit
git push
bara
fuente
2
Esta es la versión más simple!
mknaf
Agregar otra confirmación de 'cambio' es mejor que meterse con la reescritura del historial. Estoy de acuerdo con @mknaf
sdkks
8

Yo tuve el mismo problema.

  • Modificó accidentalmente el último commit que ya fue empujado
  • Hice muchos cambios a nivel local, cometió unas cinco veces
  • Intenté presionar, obtuve un error, entró en pánico, fusionó el control remoto, recibí muchos archivos que no son mis, empujé, fallé, etc.

Como Git-novato, pensé que era completo FUBAR .

Solución: algo así como @bara sugirió + creó una rama de respaldo local

# Rewind to commit just before the pushed-and-amended one.
# Replace <hash> with the needed hash.
# --soft means: leave all the changes there, so nothing is lost.
git reset --soft <hash>

# Create new branch, just for a backup, still having all changes in it.
# The branch was feature/1234, new one - feature/1234-gone-bad
git checkout -b feature/1234-gone-bad

# Commit all the changes (all the mess) not to lose it & not to carry around
git commit -a -m "feature/1234 backup"

# Switch back to the original branch
git checkout feature/1234

# Pull the from remote (named 'origin'), thus 'repairing' our main problem
git pull origin/feature/1234

# Now you have a clean-and-non-diverged branch and a backup of the local changes.
# Check the needed files from the backup branch
git checkout feature/1234-gone-bad -- the/path/to/file.php

Tal vez no sea una solución rápida y limpia, y perdí mi historial (1 commit en lugar de 5), pero salvó un día de trabajo.

davisca
fuente
6

Si no ha insertado el código en su rama remota (GitHub / Bitbucket), puede cambiar el mensaje de confirmación en la línea de comando como se muestra a continuación.

 git commit --amend -m "Your new message"

Si está trabajando en una rama específica, haga esto:

git commit --amend -m "BRANCH-NAME: new message"

Si ya ha introducido el código con un mensaje incorrecto, debe tener cuidado al cambiar el mensaje. es decir, después de cambiar el mensaje de confirmación e intentar presionarlo nuevamente, terminará teniendo problemas. Para suavizarlo, siga los siguientes pasos.

Lea la respuesta completa antes de hacerlo.

git commit --amend -m "BRANCH-NAME : your new message"

git push -f origin BRANCH-NAME                # Not a best practice. Read below why?

Nota importante: cuando usa el empuje de fuerza directamente, puede terminar con problemas de código que otros desarrolladores están trabajando en la misma rama. Entonces, para evitar esos conflictos, debe extraer el código de su rama antes de hacer que la fuerza empuje :

 git commit --amend -m "BRANCH-NAME : your new message"
 git pull origin BRANCH-NAME
 git push -f origin BRANCH-NAME

Esta es la mejor práctica al cambiar el mensaje de confirmación, si ya se envió.

Prabhakar Undurthi
fuente
1
Si ha retirado con éxito la confirmación en su último ejemplo, ¿por qué necesita forzar el empuje? ¿No sería suficiente un empuje estándar? Gracias
Thomas
La pregunta hecha por Thomas es de hecho muy válida. Yo no necesitaba forzar el empujón después del tirón.
Na13-c
Por favor, no lo llame "la mejor práctica" porque hay una manera de evitar --force , vea la respuesta aceptada
Farid
5

Si sabe que nadie ha retirado su confirmación no modificada, use la --force-with-leaseopción de git push.

En TortoiseGit, puede hacer lo mismo en las opciones "Empujar ..." "Forzar: puede descartar" y marcar "cambios conocidos".

Forzar (puede descartar cambios conocidos) permite que el repositorio remoto acepte una inserción segura y sin avance rápido. Esto puede hacer que el repositorio remoto pierda confirmaciones; úsalo con cuidado. Esto puede evitar perder cambios desconocidos de otras personas en el control remoto. Comprueba si la rama del servidor apunta a la misma confirmación que la rama de seguimiento remoto (cambios conocidos). En caso afirmativo, se realizará un empuje forzado. De lo contrario, será rechazado. Como git no tiene etiquetas de seguimiento remoto, las etiquetas no se pueden sobrescribir con esta opción.

ShawnPluma
fuente
4

Obtiene este error porque el control remoto de Git ya tiene estos archivos de confirmación. Tienes que forzar a empujar la rama para que esto funcione:

git push -f origin branch_name

También asegúrese de extraer el código del control remoto ya que otra persona de su equipo podría haber pasado a la misma rama.

git pull origin branch_name

Este es uno de los casos en los que tenemos que forzar el envío de la confirmación al control remoto.

Praveen Dhawan
fuente
¿Por qué esta respuesta no explica los principales comentarios planteados en respuestas anteriores?
Na13-c
2

Aquí hay una manera muy simple y limpia de impulsar sus cambios después de que ya haya realizado un git add "your files"y git commit --amend:

git push origin master -f

o:

git push origin master --force
agrietado
fuente
Escuché que eso es malo, y estoy seguro de que lo es. Hay una razón (buena) para que git falle de forma predeterminada (y requiera --force), estoy seguro.
Rolf
1

Tuve que solucionar este problema al extraer del repositorio remoto y lidiar con los conflictos de fusión que surgieron, cometer y luego presionar. Pero siento que hay una mejor manera.

Spoike
fuente
Realmente no. El problema podría ser que no ha actualizado su copia local desde el repositorio remoto. Git no lo presionará porque es posible que deba lidiar con las fusiones manualmente. En mi otra respuesta, tengo un comando (y una explicación) que obligará a presionar, pero tenga cuidado porque puede eliminar los cambios en el control remoto.
mipadi
1

Seguí haciendo lo que Git me dijo que hiciera. Entonces:

  • No se puede presionar debido a la confirmación modificada.
  • Hago un tirón según lo sugerido.
  • La fusión falla. entonces lo arreglo manualmente.
  • Cree una nueva confirmación (etiquetada "fusionar") y empújela.
  • ¡Parece funcionar!

Nota: La confirmación modificada fue la última.

Rolf
fuente
1
Si tuviera más puntos de reputación, votaría negativamente, así que preguntaré aquí cortésmente, ¿cuál de ustedes fue el que sufrió? ¿El que enmendó? El primero, ¿quién sacó y trabajó en una rama con commit modificado? ¿Antes de la enmienda o después? Acabo de aclarar cada modificación mía porque no te entendí ... Afortunadamente no había mucho ...
Bartis Áron
1

Lo siguiente funcionó para mí cuando cambié el Autor y el Comprador de una confirmación.

git push -f origin master

Git fue lo suficientemente inteligente como para darse cuenta de que se trataba de confirmaciones de deltas idénticas que solo diferían en la sección de metainformación.

Tanto los jefes locales como los remotos señalaron los compromisos en cuestión.

MadPhysicist
fuente
0

Aquí, cómo arreglé una edición en una confirmación anterior:

  1. Guarda tu trabajo hasta ahora.
  2. Guarde sus cambios por el momento si se realiza: git stashahora su copia de trabajo está limpia en el estado de su última confirmación.
  3. Haz las ediciones y arreglos.
  4. Confirmar los cambios en modo "enmendar" :git commit --all --amend
  5. Su editor aparecerá pidiendo un mensaje de registro (por defecto, el mensaje de registro anterior). Guarde y salga del editor cuando esté satisfecho con él.

    Los nuevos cambios se agregan al antiguo commit. Compruébelo usted mismo con git logygit diff HEAD^

  6. Vuelva a aplicar sus cambios escondidos, si se hicieron: git stash apply

Harshal Wani
fuente