Как вишня выбрать диапазон коммитов и слиться в другую ветку?
У меня есть следующий макет репозитория:
- master branch (производство)
- интеграция
- работающего
то, что я хочу достичь, - это выбрать диапазон коммитов из рабочей ветви и объединить его в ветвь интеграции. Я довольно новичок в git, и я не могу понять, как именно это сделать (вишневый выбор диапазонов фиксации в одной операции, а не слияние), не испортив репозиторий. Любые указатели или мысли об этом? Спасибо!
7 ответов:
когда дело доходит до ряда коммитов, вишневый сбор
ибыл не практично.как указанных ниже by Кит Ким, Git 1.7.2 + ввел возможность вишневого выбора диапазона коммитов (но вам все равно нужно знать о последствия вишневого сбора для будущего слияния)
git cherry-pick " научился выбирать диапазон коммитов
(например,"cherry-pick A..B" и "cherry-pick --stdin"), так же как и"git revert"; эти не поддерживают более славный sequencing контроль"rebase [-i]" не имеет, хотя.Дэмианкомментарии и предупреждает нас:
в "
cherry-pick A..B" формыAдолжен быть старшеB.
если они неправильный порядок команда будет молча терпеть неудачу.если вы хотите забрать ряд
BчерезD(включительно) что будетB^..D.
Смотрите "Git создать ветку из диапазона предыдущих коммитов?" в качестве иллюстрации.как Jubobs упоминает в комментариях:
это предполагает, что
Bне является корневой фиксацией; вы получите "unknown revision" ошибка в противном случае.Примечание: начиная с Git 2.9.х/2.10 (3 квартал 2016), вы можете выбрать диапазон фиксации непосредственно на сиротская ветвь (пустая голова): см. "как сделать существующую ветку сиротой в git".
оригинальный ответ (январь 2010 года)
A
rebase --ontoбыло бы лучше, когда вы воспроизводите данный диапазон фиксации поверх своей ветви интеграции, как Чарльз Бейли описан здесь.
(кроме того, найдите "вот как вы бы пересадили ветку темы на основе одной ветви в другую" в git rebase man page, чтобы увидеть практический примерgit rebase --onto)если ваша текущая ветка интеграции:
# Checkout a new temporary branch at the current location git checkout -b tmp # Move the integration branch to the head of the new patchset git branch -f integration last_SHA-1_of_working_branch_range # Rebase the patchset onto tmp, the old location of integration git rebase --onto tmp first_SHA-1_of_working_branch_range~1 integrationчто будет воспроизводить все между:
- после того, как родитель
first_SHA-1_of_working_branch_range(отсюда~1): на первый коммит, который вы хотите воспроизвести- до "
integration" (который указывает на последний коммит, который вы хотите воспроизвести, сworkingфилиала)на "
tmp" (что указывает на то, гдеintegrationуказывал раньше)если есть какой-либо конфликт, когда один из этих коммитов воспроизводится:
- либо решить ее и запустить"
git rebase --continue".- или пропустите этот патч, а вместо этого запустите "
git rebase --skip"- или отменить все вещи с "
git rebase --abort" (и положил обратноintegrationфилиала поtmpфилиала)после этого
rebase --onto,integrationвернется при последней фиксации ветви интеграции (то есть"tmpфилиал" + все переигранные коммиты)с вишневым сбором или
rebase --onto, не забывайте, что это имеет последствия для последующих слияний, как описано здесь.
чистая "
cherry-pick" решения здесь обсуждается, и будет включать что-то вроде:если вы хотите использовать патч-подход, то "git format-patch|git am" и "Git cherry" - это ваши варианты.
В настоящее времяgit cherry-pickпринимает только одну фиксацию, но если вы хотите выбрать диапазонBчерезDчто будетB^..Dна жаргоне git, так чтоgit rev-list --reverse --topo-order B^..D | while read rev do git cherry-pick $rev || break doneно в любом случае, когда вам нужно "воспроизвести" диапазон коммитов, слово "replay" должно подтолкнуть вас к использованию "
rebase" особенность Git.
начиная с git v1.7. 2 cherry pick может принимать ряд коммитов:
git cherry-pickнаучился выбирать диапазон коммитов (напримерcherry-pick A..Bиcherry-pick --stdin), так жеgit revert; они не поддерживают более хороший контроль последовательностиrebase [-i]есть, правда.
вы уверены, что не хотите на самом деле объединить ветки? Если рабочая ветвь имеет некоторые недавние фиксации, которые вы не хотите, вы можете просто создать новую ветвь с головкой в нужной точке.
Теперь, если вы действительно хотите выбрать ряд коммитов, по какой-либо причине, элегантный способ сделать это-просто вытащить набор патчей и применить его к своей новой ветви интеграции:
git format-patch A..B git checkout integration git am *.patchэто по существу то, что git-rebase делает в любом случае, но без нужно играть в игры. Вы можете добавить
--3waytogit-amЕсли вам нужно объединить. Убедитесь, что нет других *.файлы исправлений уже находятся в каталоге, где вы это делаете, если вы будете следовать инструкциям дословно...
предположим, что у вас есть 2 филиала,
"branchA": включает коммиты, которые вы хотите скопировать (от "commitA"до " commitB"
"branchB": ветвь, которую вы хотите, чтобы коммиты были переданы из"branchA"
1)
git checkout <branchA>2) получить идентификаторы "commitA " и"commitB"
3)
git checkout <branchB>4)
git cherry-pick <commitA>^..<commitB>5) в случае, если у вас есть конфликт, решить его и введите
git cherry-pick --continueпродолжить процесс сбора вишни.
я завернул код VonC в короткий сценарий bash,
git-multi-cherry-pick, для легкого запуска:#!/bin/bash if [ -z ]; then echo "Equivalent to running git-cherry-pick on each of the commits in the range specified."; echo ""; echo "Usage: start^..end"; echo ""; exit 1; fi git rev-list --reverse --topo-order | while read rev do git cherry-pick $rev || break doneВ настоящее время я использую это, поскольку я перестраиваю историю проекта, который имел как сторонний код, так и настройки, смешанные вместе в одной магистрали svn. Теперь я разделяю основной сторонний код, сторонние модули и настройки на свои собственные ветви git для лучшего понимания настроек в будущем.
git-cherry-pickполезно в этой ситуации, так как у меня есть два дерева в одном хранилище, но без общего предка.
все вышеперечисленные параметры предложат вам разрешить конфликты слияния. Если вы объединяете изменения, совершенные для команды, трудно получить разрешение конфликтов слияния от разработчиков и продолжить. Тем не менее, "git merge" будет выполнять слияние за один выстрел, но вы не можете передать диапазон ревизий в качестве аргумента. мы должны использовать команды "git diff" и "Git apply" для выполнения диапазона слияния оборотов. Я заметил, что "Git apply" потерпит неудачу, если файл патча имеет diff для слишком большого количества файлов, поэтому мы должны создайте исправление для каждого файла, а затем примените его. Обратите внимание, что скрипт не сможет удалить файлы, удаленные в исходной ветке. Это редкий случай, вы можете вручную удалить такие файлы из целевой ветви. Статус выхода "git apply" не равен нулю, если он не может применить патч, однако если вы используете опцию-3way, он вернется к слиянию 3-х способов, и вам не нужно беспокоиться об этом сбое.
Ниже приведен скрипт.
enter code here #!/bin/bash # This script will merge the diff between two git revisions to checked out branch # Make sure to cd to git source area and checkout the target branch # Make sure that checked out branch is clean run "git reset --hard HEAD" START= END= echo Start version: $START echo End version: $END mkdir -p ~/temp echo > /tmp/status #get files git --no-pager diff --name-only ${START}..${END} > ~/temp/files echo > ~/temp/error.log # merge every file for file in `cat ~/temp/files` do git --no-pager diff --binary ${START}..${END} $file > ~/temp/git-diff if [ $? -ne 0 ] then # Diff usually fail if the file got deleted echo Skipping the merge: git diff command failed for $file >> ~/temp/error.log echo Skipping the merge: git diff command failed for $file echo "STATUS: FAILED $file" >> /tmp/status echo "STATUS: FAILED $file" # skip the merge for this file and continue the merge for others rm -f ~/temp/git-diff continue fi git apply --ignore-space-change --ignore-whitespace --3way --allow-binary-replacement ~/temp/git-diff if [ $? -ne 0 ] then # apply failed, but it will fall back to 3-way merge, you can ignore this failure echo "git apply command filed for $file" fi echo STATUS=`git status -s $file` if [ ! "$STATUS" ] then # status is null if the merged diffs are already present in the target file echo "STATUS:NOT_MERGED $file" echo "STATUS: NOT_MERGED $file$" >> /tmp/status else # 3 way merge is successful echo STATUS: $STATUS echo "STATUS: $STATUS" >> /tmp/status fi done echo GIT merge failed for below listed files cat ~/temp/error.log echo "Git merge status per file is available in /tmp/status"
Comments