У меня следующая схема репозитория:
Я хочу добиться того, чтобы выбрать ряд коммитов из рабочей ветки и объединить их с интеграционной веткой. Я довольно новичок в git и не могу понять, как именно это сделать (выборка диапазона коммитов за одну операцию, а не слияние), не испортив репозиторий. Есть какие-нибудь советы или мысли по этому поводу? Спасибо!
Когда речь идет о ряде коммитов, "вишневая" выборка is *непрактична.
Как упоминалось ниже Китом Кимом, в Git 1.7.2+ появилась возможность вишневого выбора диапазона коммитов (но вы все еще должны знать о последствиях вишневого выбора для будущего слияния)
git cherry-pick" научился выбирать диапазон коммитов (например, "
cherry-pick A..B
" и "cherry-pick --stdin
"), также как и "git revert
"; они не поддерживают более приятный контроль последовательности "rebase [-i]
", однако.
damian comments и предупреждает нас:
В форме "
cherry-pick A..B
",A
должно быть старше, чемB
.
Если они расположены в неправильном порядке, команда молча провалится.
Если вы хотите выбрать диапазон от B
до D
(включительно), то это будет B^...D
.
Смотрите "Git create branch from range of previous commits?" в качестве иллюстрации.
Как упоминает Jubobs в комментариях:
Это предполагает, что
B
не является корневым коммитом; в противном случае вы'получите ошибку "неизвестная ревизия
".
Примечание: начиная с Git 2.9.x/2.10 (Q3 2016), вы можете выбирать диапазон коммитов непосредственно на ветке-сироте (пустая голова): см. "Как сделать существующую ветку сиротой в git".
Оригинальный ответ (январь 2010 года)
Лучше было бы использовать 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
" (который указывает на последний коммит, который вы хотите воспроизвести, из рабочей
ветви)до "tmp
" (который указывает на то место, куда ранее указывал integration
)
Если при воспроизведении одного из этих коммитов возникнет конфликт:
git rebase --continue
".git rebase --skip
"git rebase --abort
" (и вернуть ветку integration
на ветку tmp
).После этого rebase --onto
, integration
вернется на последний коммит ветки integration (то есть "tmp
" ветка + все воспроизведенные коммиты).
При использовании cherry-picking или rebase --onto
, не забывайте, что это имеет последствия для последующих слияний, как описано здесь.
Чистое решение "cherry-pick
" обсуждается здесь, и включает в себя что-то вроде:
Если вы хотите использовать патч, то "git format-patch|git am" и "git cherry" - это ваши варианты.
В настоящее времяgit cherry-pick
принимает только один коммит, но если вы хотите выбрать диапазон отB
доD
, то на жаргоне git это будетB^..D
, так что
git rev-list --reverse --topo-order B^..D | while read rev
do
git cherry-pick $rev || break
done
Но в любом случае, когда вам нужно "воспроизвести" диапазон коммитов, слово "воспроизвести" должно подтолкнуть вас к использованию "rebase
" функции Git.
Как ЖКТ В1.7.2 вишня выбор может принимать диапазон коммитов:
ГИТ берут уроки, чтобы выбрать диапазон коммитов (например,
вишневого подобрать..Б " и " вишня-выбрать --стандартный ввод), так и
ГИТ вернуться; они не поддерживают приятнее последовательности контроля перебазировать [-я]
и, хотя.
Предположим, что у вас есть 2 филиалов,
на "бранча и" : включает в себя коммиты, которые вы хотите скопировать (от "commitA" до "и commitB и"
на "branchB" в : филиал вы хотите обязуется быть переданы от "бранча и"
git checkout <branchA>
получить идентификаторы и quot commitA" и "commitB и";
git checkout <branchB>
git cherry-pick <commitA>^..<commitB>
git cherry-pick --continue
продолжать подбирать процесс.
Вы уверены, что не хотите объединять ветви? Если в рабочей ветке есть несколько последних коммитов, которые вам не нужны, вы можете просто создать новую ветку с HEAD в нужной вам точке.
Теперь, если вы действительно хотите по какой-то причине выбрать ряд коммитов, элегантный способ сделать это - просто взять набор исправлений и применить его к новой интеграционной ветке:
git format-patch A..B
git checkout integration
git am *.patch
По сути, это то, что в любом случае делает git-rebase, но без необходимости играть в игры. Вы можете добавить --3way
в git-am
, если вам нужно слияние. Убедитесь, что в каталоге, где вы это делаете, нет других *.patch файлов, если вы следуете инструкциям дословно...
Я обернул VonC'код в короткий bash-скрипт, git-multi-cherry-pick
, для удобства запуска:
#!/bin/bash
if [ -z $1 ]; then
echo "Equivalent to running git-cherry-pick on each of the commits in the range specified.";
echo "";
echo "Usage: $0 start^..end";
echo "";
exit 1;
fi
git rev-list --reverse --topo-order $1 | while read rev
do
git cherry-pick $rev || break
done
В настоящее время я использую это при восстановлении истории проекта, в котором в одном стволе svn были смешаны сторонний код и пользовательские настройки. Теперь я разделяю основной сторонний код, сторонние модули и настройки на собственные ветки git для лучшего понимания настроек в будущем. В этой ситуации помогает git-cherry-pick
, поскольку у меня есть два дерева в одном хранилище, но без общего предка.
Все вышеперечисленные варианты предложит вам для разрешения конфликтов слияния. Если вы сливаете изменения помогут команде, трудно решаются конфликты слияния от застройщиков и продолжить. Однако, на "ГИТ слияния" получат слиться в один выстрел, но вы не можете пройти ряд изменений в качестве аргумента. мы должны использовать "в ГИТ дифф" и "ГИТ применить" и команды для выполнения серии слияние оборотов. Я заметил, что "ГИТ применить", которая будет выполнена, если файл Patch и diff для слишком много файлов, поэтому мы должны создать патч в файл, а затем применить. Обратите внимание, что скрипт не сможет удалить файлы, которые были удалены в исходной ветви. Это редкий случай, можно вручную удалить файлы из целевой ветви. Выход из состояния "и ГИТ применить" не ноль, если он не способен применить патч, однако если вы работаете в -3-полосном варианте он упадет обратно до 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=$1
END=$2
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"
Другой вариант может заключаться в слиянии с стратегией наших фиксация перед диапазоном, а затем 'обычных' слияние с последнего коммита этого диапазона (или филиала, если она последняя). Итак, предположим, всего 2345 3456 и обязывает мастера быть объединены в отдельную ветку: в <предварительно> мастер: 1234 2345 3456 4567 </пред> в отдельную ветку: в <предварительно> ГИТ слияния -с нашей 4567 ГИТ слияния 2345 </пред>