Частично вишневый коммит с Git

501

Я работаю над 2 разными ветками: релиз и разработка .

Я заметил, что мне все еще нужно интегрировать некоторые изменения, которые были внесены в ветку релиза, обратно в ветку разработки .

Проблема в том, что мне не нужны все коммиты, только некоторые фрагменты в определенных файлах, поэтому просто

git cherry-pick bc66559

не делает трюк.

Когда я делаю

git show bc66559

Я могу видеть разницу, но не знаю, как правильно применить ее частично к моему текущему рабочему дереву.

Оливер
источник

Ответы:

793

Ядро вещь , которую вы собираетесь хотите здесь git add -p( -pэто синоним --patch). Это обеспечивает интерактивный способ регистрации контента, позволяя вам решить, должен ли каждый ханк входить, и даже позволяет вам вручную редактировать патч, если это необходимо.

Чтобы использовать его в сочетании с вишней:

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!

(Спасибо Тиму Хенигану за напоминание о том, что у git-cherry-pick есть опция --no-commit, и спасибо Феликсу Рабу за то, что он указал, что вам нужно выполнить сброс! Если вы хотите оставить некоторые вещи вне коммита Вы можете использовать git reset <path>...для удаления только эти файлы.)

Конечно, вы можете указать конкретные пути, add -pесли это необходимо. Если вы начинаете с патчем вы могли бы заменить cherry-pickс apply.


Если вы действительно хотите git cherry-pick -p <commit>(эта опция не существует), вы можете использовать

git checkout -p <commit>

Это будет отличать текущий коммит от указанного вами коммита и позволит вам применять фрагменты из этого дифференцирования индивидуально. Эта опция может быть более полезной, если коммит, который вы извлекаете, имеет конфликты слияния в части коммита, в которой вы не заинтересованы. (Обратите внимание, однако, что он checkoutотличается от cherry-pick: checkoutпытается полностью применить <commit>содержимое, cherry-pickприменяет разность указанный коммит от его родителя. Это означает, что checkoutможет применить больше, чем просто коммит, который может быть больше, чем вы хотите.)

Cascabel
источник
На самом деле, если я последую совету с git 1.7.5.4, «git add -p» скажет «Без изменений», потому что все это уже в индексе. Мне нужно сделать 'git reset HEAD' перед 'git add' - любой способ избежать этого сброса с помощью какой-либо опции?
Феликс Рабе
@FelixRabe: Я на самом деле удивлен, что в какой-то момент, cherry-pick -nочевидно , не оставил изменения поэтапно - соглашение определенно таково, что --no-commitопции останавливаются непосредственно перед фиксацией, то есть со всеми стадиями изменений. Я добавлю сброс в ответ.
Каскабель
1
@Blixt То есть, если коммиты, которые находятся в другой ветви, но не в вашей текущей ветви, являются ABCDEF, git checkout -p <F>они не просто получают изменения от F, но и объединяют ABCDEF и позволяют вам разобраться, какую часть вы хотите , Сравнивать это с некоторыми изменениями в F - это боль. С другой стороны, git cherry-pick -n <F>вы получаете только изменения от F - и если некоторые из этих изменений конфликтуют, он услужливо говорит вам, чтобы вы могли понять, как правильно объединить.
Каскабель
У меня были проблемы с этим, когда изменение было добавлением файла. Он git resetудалял подготовленные файлы и add -pпросто говорил «нечего добавить».
Ян Грейнджер
36

Предполагая, что необходимые изменения находятся во главе ветки, из которой вы хотите внести изменения, используйте git checkout

для одного файла:

git checkout branch_that_has_the_changes_you_want path/to/file.rb

для нескольких файлов просто цепочка:

git checkout branch_that_has_the_changes_you_want path/to/file.rb path/to/other_file.rb
Джей Суэйн
источник
5
Это копирует весь файл: не только изменения.
Джереми Лист
1
Этот ответ, как и любой другой ответ здесь, имеет большой недостаток: он не сохраняет первоначального автора изменения, а фиксирует его под вашим именем. Если изменения хороши, ты крадешь чью-то заслугу, если изменения плохи, ты поджигаешь себя. Кажется, нет никакого способа обойтись без git cherry-pick -p, и, к сожалению, его до сих пор нет.
Пфальк
15

Опираясь на ответ Майка Монкевича, вы также можете указать один или несколько файлов для извлечения из предоставленной ветки sha1 /.

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

Это позволит вам в интерактивном режиме выбрать изменения, которые вы хотите применить к вашей текущей версии файла.

Christian.D
источник
5
Это проблематично, если текущая версия файла значительно отличается от этой версии. Вам будет предложено множество несущественных изменений (которые не появляются в коммите). Кроме того, изменения, которые вы действительно хотите, могут отображаться в «замаскированной форме» как разница с текущим, а не с исходной разницей. Вполне возможно, что оригинальное различие, которое вы хотите, конфликтует и должно быть правильно объединено; у вас не будет такой возможности здесь.
Каз
1

Если вы хотите указать список файлов в командной строке и выполнить все это одной атомарной командой, попробуйте:

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

--3way: Если патч не применяется корректно, Git создаст конфликт слияния, чтобы вы могли его запустить git mergetool. Пропуск --3wayзаставит Git отказаться от патчей, которые не применяются чисто.

nyanpasu64
источник
0

Если «частично вишня» означает «в файлах, выбирая некоторые изменения, но отбрасывая другие», это можно сделать, введя git stash:

  1. Сделайте полную вишню.
  2. git reset HEAD^ преобразовать весь выбранный вишней коммит в непроцессированные рабочие изменения.
  3. Теперь git stash save --patch: интерактивно выберите нежелательный материал для хранения.
  4. Git откатывает спрятанные изменения из вашей рабочей копии.
  5. git commit
  6. Выбросить тайник нежелательных изменений git stash drop.

Совет: если вы дадите имя хранилищу нежелательных изменений: git stash save --patch junkтогда, если вы забудете сделать (6) сейчас, позже вы узнаете, что это за хранилище.

Kaz
источник
Если вы просто выберете вишню, а затем перезагрузите ... вы ничего не будете копить. Как уже было сказано выше, вы должны либо выполнить вишневый выбор с помощью --no-commit, либо выполнить сброс --hard HEAD ~. Обратите внимание, что вы действуете отрицательно (выбирая то, что вы не хотите). Вышеупомянутое решение допускает либо позитивный, либо негативный подход.
Пьер-Оливье Варес
0

Используйте, git format-patchчтобы выделить часть коммита, который вам нужен, и git amприменить его к другой ветке.

git format-patch <sha> -- path/to/file
git checkout other-branch
git am *.patch
adrock20
источник