Частично вишневый коммит с Git
Я работаю над 2 разными ветками: релиз и разработка.
Я заметил, что мне все еще нужно интегрировать некоторые изменения, которые были внесены в ветку релиза, обратно в ветку разработки.
Проблема в том, что мне не нужны все коммиты, только некоторые фрагменты в определенных файлах, поэтому просто
git cherry-pick bc66559
не делает трюк.
Когда я делаю
git show bc66559
Я могу видеть разницу, но не знаю, как правильно применить ее частично к моему текущему рабочему дереву.
8 ответов
Главное, что вам здесь нужно, это 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
применяет diff указанного коммита от его родителя. Это означает, что checkout
может применить больше, чем просто этот коммит, который может быть больше, чем вы хотите.)
Я знаю, что отвечаю на старый вопрос, но похоже, что есть новый способ сделать это с интерактивной проверкой:
git checkout -p bc66559
Можно ли в интерактивном режиме выбрать фрагменты из другого комита git?
Предполагая, что необходимые изменения находятся во главе ветки, из которой вы хотите внести изменения, используйте 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
Опираясь на ответ Майка Монкевича, вы также можете указать один или несколько файлов для извлечения из предоставленной ветки sha1/.
git checkout -p bc66559 -- path/to/file.java
Это позволит вам в интерактивном режиме выбрать изменения, которые вы хотите применить к вашей текущей версии файла.
Если вы хотите указать список файлов в командной строке и выполнить все это одной атомарной командой, попробуйте:
git apply --3way <(git show -- list-of-files)
--3way
: Если патч не применяется корректно, Git создаст конфликт слияния, чтобы вы могли запустить git mergetool
, Опуская --3way
заставит Git отказаться от патчей, которые не применяются чисто.
Использовать git format-patch
чтобы вырезать часть коммита, которая вам небезразлична, и git am
применить его к другой ветке
git format-patch <sha> -- path/to/file
git checkout other-branch
git am *.patch
Если "частично вишня" означает "в файлах, выбирая некоторые изменения, но отбрасывая другие", это можно сделать, введя git stash
:
- Сделайте полную вишню.
git reset HEAD^
преобразовать весь выбранный вишневый коммит в неустановленные рабочие изменения.- Сейчас
git stash save --patch
: интерактивно выберите нежелательный материал для хранения. - Git откатывает спрятанные изменения из вашей рабочей копии.
git commit
- Выбросьте тайник нежелательных изменений:
git stash drop
,
Совет: если вы дадите имя ненужным изменениям: git stash save --patch junk
тогда, если вы забудете сделать (7) сейчас, позже вы узнаете тайник, какой он есть.
На самом деле, лучшим решением этого вопроса является использование
checkout
хвалить
git checkout <branch> <path1>,<path2> ..
Например, предположим, что вы находитесь в master и хотите, чтобы изменения с dev1 на
project1/Controller/WebController1.java
а также
project1/Service/WebService1.java
, вы можете использовать это:
git checkout dev1 project1/Controller/WebController1.java project1/Service/WebService1.java
Это означает, что основная ветвь обновляется только с dev1 по этим двум путям.