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

StackOverflow https://stackoverflow.com/questions/1994463

  •  22-09-2019
  •  | 
  •  

Вопрос

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

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

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

Это было полезно?

Решение

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

В качестве упомянуто ниже по Кит Ким, GIT 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 создать филиал из линейки предыдущих коммитов?"Как иллюстрация.

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

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

Примечание. По состоянию на 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"(Что указывает на последний коммит, который вы хотите воспроизвести из 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 lingo, так что

git rev-list --reverse --topo-order B^..D | while read rev 
do 
  git cherry-pick $rev || break 
done 

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

Другие советы

По состоянию на git v1.7.2 Cherry Pick может принять ряд коммитов:

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

Предположим, что у вас есть 2 ветви,

«Brancha»: включает в себя коммиты, которые вы хотите скопировать (от «Commita» в «Комплект»

«BranchB»: ветвь, которую вы хотите, чтобы коммиты были переданы из "Brancha"

1)

 git checkout <branchA>

2) Получите идентификаторы "Commica" и "Commineb"

3)

git checkout <branchB>

4)

git cherry-pick <commitA>^..<commitB>

5) В случае, если у вас есть конфликт, решайте его и введите

git cherry-pick --continue

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

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

Теперь, если вы действительно хотите выбрать ряд коммитов, по какой-то причине, элегантный способ сделать это-просто вытащить патч и применить его к новой филиале интеграции:

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

По сути, это то, что делает git-rebase в любом случае, но без необходимости играть в игры. Можете добавить --3way к git-am Если вам нужно слиться. Убедитесь, что в каталоге уже нет других файлов.

Я завернулся Код VONC в короткий сценарий, 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 

В настоящее время я использую это, так как перестраиваю историю проекта, в котором как код 3-й стороны, так и настройки смешаны в одном и том же стволе SVN. Сейчас я разделяю основной код сторонних сторон, сторонние модули и настройки на их собственные филиалы GIT для лучшего понимания настройки в будущем. git-cherry-pick полезен в этой ситуации, так как у меня есть два дерева в том же хранилище, но без общего предка.

Все вышеперечисленные варианты побуждают вас разрешить конфликты слияния. Если вы объединяете изменения, совершенные для команды, трудно разрешить конфликты слияния от разработчиков и продолжить. Тем не менее, «Git Merge» сделает слияние за один выстрел, но вы не можете передать ряд пересмотров в качестве аргумента. Мы должны использовать команды «GIT Diff» и «GIT Apply», чтобы выполнить диапазон слияний Revs. Я заметил, что «GIT Apply» не будет сбой, если в файле Patch есть 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=$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 Commits Master будут объединены в филиал:

master:
1234
2345
3456
4567

В филиале:

git merge -s ours 4567
git merge 2345
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top