comandos - git rebase
Revertir una serie de fusiones y confirmaciones en Git(sin reescribir el historial) (4)
Contexto
Uno de mis compañeros de equipo empujó por error algunos compromisos a nuestra rama de desarrollo principal. Somos un equipo pequeño, colocado. Nuestro repositorio remoto está alojado en un servidor interno.
Aquí está la parte superior de nuestro registro de confirmación (todas estas confirmaciones ya se han enviado):
$ git log develop -6 --pretty=oneline --abbrev-commit
faada93 Merge branch ''develop'' of <our_repo_path>.git
244d174 Support classes again
a97a877 Pruned all unused references (again).
8c29252 Merge branch ''develop'' of <our_repo_path>.git
a78b993 Support models & methods - product types & categories
da8b496 Resolved JIRA issue PPF-182
da8b496
es el último compromiso que queríamos mantener en nuestra rama de develop
, por lo que necesitamos revertir los 5 últimos compromisos. Creamos una nueva rama desde 8c29252
para continuar trabajando en una "rama de características".
Intenté muchas cosas, guiado por esta respuesta y este post de Linus , y terminé haciendo lo que puedes ver en mi historial de terminales a continuación. Pero no estoy seguro de si lo que terminé haciendo es "la forma correcta". La información que encontré era compleja; No pude discernir una "mejor solución" para este problema en particular.
Pregunta
¿Fue el enfoque que elegí (ver detalles a continuación) una buena manera de revertir esos 5 compromisos, sin dañar nuestra historia? ¿Hay una manera más fácil o "más correcta" de lograr lo mismo?
Entre otras cosas, consideré crear una nueva rama desde da8b496
( git checkout -b new-develop da8b496
) y abandonar nuestra rama de develop
actual, pero eso no me pareció correcto.
Lo que acabé haciendo (detalles)
Primero, creé una nueva sucursal para los confirmaciones a78b993
y 8c29252
, porque estas confirmaciones contienen el trabajo que queremos mantener y eventualmente se fusionan con nuestra principal sucursal de desarrollo.
$ git checkout -b new-feature-brach 8c29252
Entonces comencé a revertir los compromisos ofensivos en nuestra rama de desarrollo.
Intenté esto primero, pero no funcionó (probablemente porque algunas de las confirmaciones se fusionan):
$ git revert a78b993..HEAD
error: a cherry-pick or revert is already in progress
hint: try "git cherry-pick (--continue | --quit | --abort)"
fatal: revert failed
Así que ... Revertí manualmente cada compromiso en su lugar; uno a uno:
$ git revert -m 1 faada93
[develop 40965a5] Revert "Merge branch ''develop'' of <our_repo_path>.git"
8 files changed, 167 insertions(+), 3 deletions(-)
$ git revert 244d174
[develop 3cebd68] Revert "Support classes again"
45 files changed, 557 insertions(+), 1572 deletions(-)
(list of affected files)
$ git revert a97a877
error: could not revert a97a877... Pruned all unused references (again).
hint: after resolving the conflicts, mark the corrected paths
hint: with ''git add <paths>'' or ''git rm <paths>''
hint: and commit the result with ''git commit''
$ git mergetool
Merging:
exampleFile1.cs
exampleFile2.cs
Deleted merge conflict for ''exampleFile1.cs'':
{local}: deleted
{remote}: modified file
Use (m)odified or (d)eleted file, or (a)bort? m
Deleted merge conflict for ''exampleFile2.cs'':
{local}: deleted
{remote}: modified file
Use (m)odified or (d)eleted file, or (a)bort? m
$ git commit -m "Adding files to be reverted along with the next commit."
[develop 15bc02b] Adding files to be able to revert the next commit in line.
2 files changed, 239 insertions(+)
(list of affected files here)
$ git revert -m 1 8c29252
# On branch develop
# Your branch is ahead of ''origin/develop'' by 3 commits.
# (use "git push" to publish your local commits)
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# exampleFile1.cs.orig
# exampleFile2.cs.orig
nothing added to commit but untracked files present (use "git add" to track)
$ git revert a78b993
[develop 841e77c] Revert "Support models & methods - product types & categories"
2 files changed, 239 deletions(-)
(list of affected files here)
Confirmar registro después de que se hicieron todos los revertidos:
$ git log develop -10 --pretty=oneline --abbrev-commit
841e77c Revert "Support models & methods - product types & categories"
15bc02b Adding files to be able to revert the next commit in line.
3cebd68 Revert "Support classes again"
40965a5 Revert "Merge branch ''develop'' of <our_repo_path>.git"
faada93 Merge branch ''develop'' of <our_repo_path>.git
244d174 Support classes again
a97a877 Pruned all unused references (again).
8c29252 Merge branch ''develop'' of <our_repo_path>.git
a78b993 Support models & methods - product types & categories
da8b496 Resolved JIRA issue PPF-182
Gráfico después de revertir:
$ git log --graph --oneline -8 develop
* 841e77c Revert "Support models & methods - product types & categories"
* 15bc02b Adding files to be able to revert the next commit in line.
* 3cebd68 Revert "Support classes again"
* 40965a5 Revert "Merge branch ''develop'' of <our_repo_path>.git"
* faada93 Merge branch ''develop'' of <our_repo_path>.git
|/
| * a97a877 Pruned all unused references (again).
| * 8c29252 Merge branch ''develop'' of <our_repo_path>.git
| |/
| | * da8b496 Resolved JIRA issue PPF-182
Me parece correcto. Por último, elimino algunos archivos de copia de seguridad que no deseo conservar:
$ git clean -fd
(list of affected files here)
El estado actual es limpio:
$ git status
# On branch develop
# Your branch is ahead of ''origin/develop'' by 4 commits.
# (use "git push" to publish your local commits)
#
nothing to commit, working directory clean
Y luego empujo todo de nuevo al mando a distancia:
git push origin develop
A pesar de que su historial ha cambiado, puede crear ramas que le permitan retroceder y experimentar. Git significa nunca tener que decir "deberías haberlo hecho". Si convergen en una realidad que le guste más, entonces hágalo. De lo contrario, tíralo.
Los siguientes ejemplos crearán nuevas sucursales que dejarán todo lo demás en su repositorio solo.
Alternativa 1: git revertir
Primero crea una rama de rasguño en el punto donde comenzaste tu aventura.
$ git checkout -b tmp-revert faada93
Al especificar un rango de confirmación, git revert
deshará múltiples confirmaciones.
$ git revert da8b496..faada93
Alternativa 2: git commit-tree
Considere el diagrama a continuación de Git Internals - Git Objects , sección 10.2 en la segunda edición de Pro Git por Scott Chacon y Ben Straub. La confirmación superior ("tercera confirmación") tiene un hash SHA1 que comienza en 1a410e
. En el contexto de esta historia, 1a410e^{tree}
se resolvería en 3c4e9c
, es decir, el objeto de árbol inmediatamente a la derecha del tercer commit.
Figura 151 de Pro Git , 2ª ed.
Estudia este modelo para entender cómo git rastrea el contenido . Crear una cuarta cuarta confirmación cuyo árbol sea idéntico a la segunda confirmación (es decir, 0155eb
) agregaría un nuevo objeto de confirmación que compartiría o "señalaría" el árbol y las manchas existentes en lugar de agregar nuevos objetos duplicados.
Siga leyendo para aprender cómo realizar esta costura de bajo nivel con git commit-tree
.
Comience por crear otra rama temporal para trabajar.
$ git checkout -b tmp-ctree faada93
En este punto, desea crear una nueva confirmación donde su árbol (es decir, el código confirmado) es idéntico al de da8b496
, la última confirmación que desea mantener. Este árbol es direccionable directamente en git: da8b496^{tree}
.
git commit-tree
es "fontanería", un comando de bajo nivel en git, a diferencia de "porcelana". Puede parecer incómodo o desconocido de usar, pero en este caso brinda un control preciso del resultado que desea.
Cree un nuevo compromiso sin da8b496
cuyo árbol sea el mismo que el de da8b496
y cuyo padre ( -p
) sea la punta de la rama actual, faada93
en su caso. Tenga en cuenta que git commit-tree
lee el mensaje de confirmación de la nueva confirmación en la entrada estándar, que el comando a continuación suministra con el comando echo
.
$ echo Revert back to da8b496 | / git commit-tree da8b496^{tree} -p $(git rev-parse tmp-ctree) new-commit-sha1
La parte en cursiva de arriba no es parte del comando. Indica que git commit-tree
genera el hash SHA1 del commit recién creado. Al conocer el nuevo SHA1 de confirmación, puede mover la rama a ese punto, por ejemplo ,
$ git merge new-commit-sha1
En el comando anterior, reemplace new-commit-sha1 con la salida de git commit-tree
. (Podría hacer lo mismo con git reset --hard new-commit-sha1
, pero el hard reset es una herramienta git reset --hard new-commit-sha1
donde se evita el uso casual).
Podría rodar todo lo anterior en un solo comando compuesto.
$ git merge --ff-only $(echo Revert back to da8b496 | / git commit-tree da8b496^{tree} -p $(git rev-parse tmp-ctree))
El cambio --ff-only
a git merge
está pensado para evitar sorpresas. Su intención es que el nuevo compromiso sea un avance rápido o un descendiente del actual jefe de rama: ¡su hijo inmediato, de hecho!
Limpiar
Para eliminar las ramas temporales de arriba, cambie a otra y dispare, Sr. McManus. Tus otras ramas serán como las dejaste.
$ git checkout develop $ git branch -D tmp-revert tmp-ctree
Los dos deben ser idénticos, como se puede verificar con
$ git diff tmp-revert tmp-ctree
Para mantener uno, fusionalo en tu rama de develop
.
$ git checkout develop $ git merge --ff-only tmp-ctree $ git push origin develop
Lo que estás tratando de hacer es muy arriesgado.
de hecho, puede revertir y eliminar las confirmaciones que ya ha enviado al repositorio, pero si alguien ya ha realizado los cambios y tiene el commitId que va a eliminar, el repositorio puede volverse "inestable" y git no podrá hacerlo. maneja el tirón y los empujes desde que eliminaste la confirmación que ahora se elimina del historial.
Haga esto (revertir y eliminar la confirmación) solo y solo si nadie ha tirado todavía de esta confirmación.
Puedo sugerir que esto se pueda considerar un duplicado de esta respuesta: haga de la rama git actual una rama maestra
La excelente solución de Jefromi fue:
[git branch better_branch <last good commit>]
git checkout better_branch
git merge --strategy=ours master # keep the content of this branch, but record a merge
git checkout master
git merge better_branch # fast-forward master up to the merge
Usted tiene un pequeño equipo de ubicación conjunta, por lo que la comunicación no es un problema. Haga que el historial de confirmaciones se vea como debería haber sido:
git branch -f develop dab4896
git branch newfeature 8c29252
git push -f origin develop newfeature
y que todos recojan. Has terminado
Este tipo de error es una de las razones para reescribir.