Как черри подобрать диапазон коммитов и слить в другую ветку?


640

У меня есть следующий макет хранилища:

  • мастер филиал (производство)
  • интеграция
  • за работой

Чего я хочу добиться - это выбрать диапазон коммитов из рабочей ветви и объединить их с интеграцией. Я довольно новичок в git и не могу понять, как именно это сделать (выбор диапазонов коммитов за одну операцию, а не слияние), не испортив репозиторий. Любые указатели или мысли по этому поводу? Спасибо!


Ответы:


809

Когда дело доходит до ряда коммитов, сбор вишни является было не практично.

Как упомянуто ниже по Киту Киму , Гит 1.7.2+ появилась возможность производить выбор диапазона коммитов (но вы все равно должны быть осведомлены о результате вишневого выбора для будущего слияния )

git cherry-pick «научился выбирать диапазон коммитов
(например,« cherry-pick A..B»и« cherry-pick --stdin»), так же как и« », однако git revertони не поддерживают более приятный контроль секвенирования» rebase [-i]».

Дамиан комментирует и предупреждает нас:

В « cherry-pick A..B» форме, Aдолжно быть старшеB .
Если они в неправильном порядке, команда молча провалится .

Если вы хотите , чтобы выбрать диапазон Bчерез D(включительно) , что будет B^..D.
См. « Git create ветка из диапазона предыдущих коммитов? » В качестве иллюстрации.

Как Джубобс упоминает в комментариях :

Это предполагает, что Bэто не корневой коммит; unknown revisionиначе вы получите ошибку " ".

Примечание: начиная с Git 2.9.x / 2.10 (3 квартал 2016 года), вы можете выбрать диапазон коммитов непосредственно для сиротской ветви (пустой заголовок): см. « Как сделать существующую ветку сиротой в git ».


Оригинальный ответ (январь 2010 г.)

Было rebase --ontoбы лучше, если бы вы воспроизводили заданный диапазон коммитов поверх ветки интеграции, как здесь описал Чарльз Бэйли .
(также посмотрите «Вот как вы могли бы перенести ветку темы на основе одной ветки в другую» на странице справки git rebase , чтобы увидеть практический пример 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 rev-list --reverse --topo-order B^..D | while read rev 
do 
  git cherry-pick $rev || break 
done 

Но в любом случае, когда вам нужно «воспроизвести» ряд коммитов, слово «воспроизведение» должно подтолкнуть вас к использованию « rebase» функции Git.


1
Если у вас есть коммиты, у которых есть родители, которым требуется -mопция, как вы справляетесь с этими коммитами? Или есть способ отфильтровать эти коммиты?
августа

@aug -mдолжен обработать их для вас, выбрав основную линию, на которую ссылается -mпараметр, который вы выбрали для этого выбора вишни.
VonC

Дело в том, что если вы выбираете диапазон коммитов cherry, он автоматически выберет родительские коммиты, но затем, когда он достигает нормального коммита, он терпит неудачу и говорит, что commit не является слиянием. Я думаю, мой вопрос лучше сформулирован, как заставить его пропустить -mопцию, только когда он попадает в родительский коммит, когда диапазон коммитов вишневого выбора? Прямо сейчас, если я пройду, -mкак git cherry-pick a87afaaf..asfa789 -m 1это относится ко всем коммитам в пределах диапазона.
августа

@aug Странно, я не воспроизводил проблему. Какая у вас версия git и какое именно сообщение об ошибке вы видите?
VonC

Я использую git версии 2.6.4 (Apple Git-63). Ошибка я вижу , что что - то вроде error: Commit 8fcaf3b61823c14674c841ea88c6067dfda3af48 is a merge but no -m option was given.я на самом деле понял , что вы могли бы просто , git cherry-pick --continueи это было бы хорошо (но это не будет включать родительские фиксации)
август

136

Начиная с git v1.7.2 cherry pick может принимать различные коммиты:

git cherry-pickнаучился выбирать диапазон коммитов (например, cherry-pick A..Bи cherry-pick --stdin) git revert; однако они не поддерживают более приятный контроль секвенирования rebase [-i].


103
Обратите внимание, что cherry-pick A..Bкоммит А не получится (вам это понадобится A~1..B), и если возникнут какие-либо конфликты, git не будет автоматически продолжаться, как ребаз (по крайней мере, с 1.7.3.1)
Гейб Моутарт

3
Также хорошо отметить, что git cherry-pick A..B Cэто не работает так, как вы ожидаете, наивно. Он не будет выбирать все в диапазоне A..Bи совершать C! Для этого нужно сначала разбить на две строки, git cherry-pick A..Bа затем git cherry-pick C. Поэтому, когда у вас есть диапазон, вам нужно выполнить его отдельно.
Микровирус

42

Предположим, что у вас есть 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

продолжить процесс выбора вишни.


Работал как шарм! Большое спасибо!
Снуконе

28

Вы уверены, что не хотите объединять ветви? Если у рабочей ветки есть некоторые недавние коммиты, которые вам не нужны, вы можете просто создать новую ветку с HEAD в нужной точке.

Теперь, если вы действительно хотите по-разному выбрать диапазон коммитов, по любой причине, элегантный способ сделать это - просто извлечь набор патчей и применить его к вашей новой ветви интеграции:

git format-patch A..B
git checkout integration
git am *.patch

Это в основном то, что делает git-rebase, но без необходимости играть в игры. Вы можете добавить --3wayк , git-amесли вам необходимо объединить. Убедитесь, что в каталоге, где вы делаете это, нет других файлов * .patch, если вы будете дословно следовать инструкциям ...


1
Обратите внимание, что, как и в других диапазонах ревизий, его необходимо A^включить A.
Джино Мемпин

9

Я обернул код 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полезно в этой ситуации, так как у меня есть два дерева в одном хранилище, но без общего предка.


3

Все вышеперечисленные параметры предложат вам разрешить конфликты слияния. Если вы объединяете изменения, зафиксированные для команды, трудно разрешить конфликты объединения от разработчиков и продолжить. Однако «git merge» выполнит слияние за один выстрел, но вы не можете передать диапазон ревизий в качестве аргумента. мы должны использовать команды "git diff" и "git apply", чтобы сделать диапазон оборотов слияния. Я заметил, что «git apply» завершится ошибкой, если в файле патча есть diff для слишком большого количества файлов, поэтому мы должны создать патч для каждого файла, а затем применить. Обратите внимание, что скрипт не сможет удалить файлы, которые были удалены в исходной ветке. Это редкий случай, вы можете вручную удалить такие файлы из целевой ветки. Состояние выхода «git apply» не равно нулю, если он не может применить патч,

Ниже приведен сценарий.

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"

2

Я проверил это несколько дней назад после прочтения очень четкого объяснения Вонка.

Мои шаги

Начните

  • Отрасль dev: ABCDEFGHIJ
  • Отрасль target: ABCD
  • Я не хочу EниH

Шаги для копирования функций без шага E и H в ветке dev_feature_wo_E_H

  • git checkout dev
  • git checkout -b dev_feature_wo_E_H
  • git rebase --interactive --rebase-merges --no-ff Dгде я ставлю dropперед Eи Hв редакторе ребаз
  • разрешать конфликты, продолжать и commit

Шаги для копирования ветви dev_feature_wo_E_Hна цель.

  • git checkout target
  • git merge --no-ff --no-commit dev_feature_wo_E_H
  • разрешать конфликты, продолжать и commit

Некоторые замечания

  • Я сделал это из-за слишком много cherry-pickв предыдущие дни
  • git cherry-pick мощный и простой, но

    • это создает дубликаты коммитов
    • и когда я хочу, mergeмне нужно разрешить конфликты начальных коммитов и дублирующих коммитов, так что для одного или двух cherry-pick, это нормально для «выбора вишни», но для большего это слишком многословно, и ветвь станет слишком сложной
  • На мой взгляд, шаги, которые я сделал, более понятны, чем git rebase --onto

1
Хороший пост. Upvoted. Это напоминает мне stackoverflow.com/a/38418941/6309 . Я вспомнил недостатки сбора вишни еще в 2012 году: stackoverflow.com/a/13524494/6309 .
VonC

1

Другим вариантом может быть слияние со стратегией нашей до фиксации перед диапазоном, а затем «нормальное» слияние с последней фиксацией этого диапазона (или ветвью, когда она является последней). Итак, предположим, что только 2345 и 3456 коммитов мастера будут объединены в ветку Feature:

мастер:
1234
2345
+3456
4567

в тематической ветке:

Git Merge - наш 4567
Git Merge 2345
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.