En parte, elegir un compromiso con Git

501

Estoy trabajando en 2 ramas diferentes: lanzamiento y desarrollo .

Noté que todavía necesito integrar algunos cambios comprometidos con la rama de lanzamiento nuevamente en la rama de desarrollo .

El problema es que no necesito todo el commit, solo algunos trozos en ciertos archivos, así que un simple

git cherry-pick bc66559

No hace el truco.

Cuando hago un

git show bc66559

Puedo ver la diferencia pero realmente no conozco una buena manera de aplicarla parcialmente a mi árbol de trabajo actual.

Oliver
fuente

Respuestas:

793

Lo principal que querrás aquí es git add -p( -pes sinónimo de --patch). Esto proporciona una forma interactiva de registrar el contenido, permitiéndole decidir si cada trozo debe entrar e incluso permitiéndole editar manualmente el parche si es necesario.

Para usarlo en combinación con cherry-pick:

git cherry-pick -n <commit> # get your patch, but don't commit (-n = --no-commit)
git reset                   # unstage the changes from the cherry-picked commit
git add -p                  # make all your choices (add the changes you do want)
git commit                  # make the commit!

(¡Gracias a Tim Henigan por recordarme que git-cherry-pick tiene una opción --no-commit, y gracias a Felix Rabe por señalar que necesita reiniciar! Si solo quiere dejar algunas cosas fuera del commit , podría usar git reset <path>...para desestabilizar solo esos archivos).

Por supuesto, puede proporcionar rutas específicas add -psi es necesario. Si está comenzando con un parche, puede reemplazarlo cherry-pickpor apply.


Si realmente quieres un git cherry-pick -p <commit>(esa opción no existe), puedes usar

git checkout -p <commit>

Eso diferenciará la confirmación actual contra la confirmación que especifique, y le permitirá aplicar trozos de esa diferencia individualmente. Esta opción puede ser más útil si la confirmación que está realizando tiene conflictos de fusión en parte de la confirmación que no le interesa. (Tenga en cuenta, sin embargo, que checkoutdifiere de cherry-pick: checkoutintenta aplicar <commit>los contenidos por completo, cherry-pickaplica la diferencia de el commit especificado de su padre. Esto significa que checkoutpuede aplicar más que solo ese commit, que podría ser más de lo que desea).

Cascabel
fuente
En realidad, si sigo los consejos con git 1.7.5.4, 'git add -p' dice 'Sin cambios' porque ya está todo en el índice. Necesito hacer un 'git reset HEAD' antes de 'git add': ¿hay alguna forma de evitar ese reinicio con alguna opción?
Felix Rabe
@FelixRabe: Estoy realmente sorprendido de que, en algún momento, cherry-pick -naparentemente no dejaron los cambios organizados; la convención es definitivamente que las --no-commitopciones se detienen justo antes de la confirmación, es decir, con todos los cambios organizados. Agregaré el reinicio a la respuesta.
Cascabel
1
@Blixt Es decir, si las confirmaciones que están en la otra rama, pero no su rama actual son ABCDEF, git checkout -p <F>no no simplemente conseguir que los cambios de F, se hace que ABCDEF todo en puré juntos y le permite descifrar qué parte de que desea . Reducir eso a solo algunos de los cambios de F es un dolor. Por otro lado, git cherry-pick -n <F>solo obtienes los cambios de F, y si alguno de esos cambios entra en conflicto, te lo ayuda de manera útil para que puedas descubrir cómo fusionarte correctamente.
Cascabel
Tuve problemas con esto cuando el cambio fue una adición de archivo. El git reseteliminaría los archivos almacenados y add -psimplemente diría 'nada que agregar'.
Ian Grainger el
36

Suponiendo que los cambios que desea están en la cabecera de la rama desde la que desea los cambios, use git checkout

para un solo archivo:

git checkout branch_that_has_the_changes_you_want path/to/file.rb

para múltiples archivos solo en cadena:

git checkout branch_that_has_the_changes_you_want path/to/file.rb path/to/other_file.rb
Jay Swain
fuente
55
Eso copia todo el archivo: no solo los cambios.
Jeremy List
1
Esta respuesta, al igual que cualquier otra respuesta aquí hasta ahora, tiene un gran inconveniente: no conserva al autor original del cambio, sino que se compromete bajo su nombre. Si un cambio es bueno, estás robando el crédito de alguien, si un cambio es malo, te estás prendiendo fuego. Parece que no hay forma de evitar tener git cherry-pick -p, y lástima que todavía no esté allí.
pfalcon
15

Sobre la base de la respuesta de Mike Monkiewicz , también puede especificar uno o más archivos para pagar desde el sha1 / branch suministrado.

git checkout -p bc66559 -- path/to/file.java 

Esto le permitirá elegir de manera interactiva los cambios que desea aplicar a su versión actual del archivo.

Christian.D
fuente
55
Esto es problemático si la versión actual del archivo es significativamente diferente de esa versión. Se le solicitarán numerosos cambios irrelevantes (que no aparecen en la confirmación). Además, los cambios que realmente desea pueden aparecer en una "forma encubierta" como una diferencia con la diferencia actual, no con la original. Es posible que la diferencia original que desea entre en conflicto y deba fusionarse correctamente; No tendrás esa oportunidad aquí.
Kaz
1

Si desea especificar una lista de archivos en la línea de comando y hacer todo en un solo comando atómico, intente:

git apply --3way <(git show -- list-of-files)

--3way: Si un parche no se aplica limpiamente, Git creará un conflicto de fusión para que puedas ejecutarlo git mergetool. Omitir --3wayhará que Git renuncie a los parches que no se aplican limpiamente.

nyanpasu64
fuente
0

Si "seleccionar en parte la cereza" significa "dentro de los archivos, elegir algunos cambios pero descartar otros", se puede hacer incorporando git stash:

  1. Haz la selección completa de cerezas.
  2. git reset HEAD^ para convertir todo el compromiso seleccionado en cambios de trabajo no organizados.
  3. Ahora git stash save --patch: seleccione interactivamente material no deseado para guardar.
  4. Git revierte los cambios escondidos de tu copia de trabajo.
  5. git commit
  6. Bote el alijo de cambios no deseados: git stash drop.

Consejo: si le da un nombre al alijo de cambios no deseados: git stash save --patch junksi olvida hacer (6) ahora, más tarde reconocerá el alijo por lo que es.

Kaz
fuente
Si simplemente elige, luego reinicie ... no tendrá nada que esconder. Como se dijo anteriormente, tienes que hacer una selección de cereza con --no-commit, o restablecer --hard HEAD ~. Tenga en cuenta que está procesando negativamente (seleccionando lo que no desea). La solución aceptada anteriormente permite un enfoque positivo o negativo.
Pierre-Olivier Vares
0

Use git format-patchpara cortar la parte del compromiso que le interesa y git amaplicarlo a otra rama

git format-patch <sha> -- path/to/file
git checkout other-branch
git am *.patch
adrock20
fuente