Як вишнево вибрати діапазон комітетів і об'єднатись в іншу гілку?


640

У мене є такий макет сховища:

  • головна галузь (виробництво)
  • інтеграція
  • робочий

Я хочу досягти, щоб вишня вибирала коло робочих відділень і об'єднувала їх у галузь інтеграції. Я досить новий для git, і я не можу зрозуміти, як саме це зробити (вишневий вибір фіксованих діапазонів за одну операцію, а не злиття), не псуючи сховище. Будь-які вказівки чи думки з цього приводу? Дякую!


Відповіді:


808

Якщо мова йде про цілий ряд комітетів, збирання вишні є не було практичним.

Як згадано нижче по Кіту Кіму , Гіт 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" , щоб переглянути практичний приклад 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.


1
Якщо у вас є коміти, у яких є батьки, яким потрібен -mваріант, як ви обробляєте ці зобов'язання? Або є спосіб фільтрації цих комісій?
Август

@aug -mповинен обробляти їх за вас, вибравши основну лінію , на яку посилається -mпараметр, який ви вибрали для цього вибору.
VonC

Річ у тім, що якщо вишня вибирає діапазон комітетів, він вишневий вибір вибирає з батьків, але тоді, коли він потрапляє на звичайний фіксатор, він не вдається і каже, що фіксація не є злиттям. Я думаю, що моє питання краще сформульовано, як змусити його передавати -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 вишневий вибір може приймати цілий ряд зобов’язань:

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


103
Зауважте, що cherry-pick A..Bви не A~1..Bприймете команду A (вам знадобиться для цього), і якщо є якісь конфлікти, git не буде автоматично продовжуватися, як це робиться перезавантаження (принаймні станом на 1.7.3.1)
Gabe Moothart

3
Також добре зазначити, що git cherry-pick A..B Cне працює так, як ви наївно очікували. Це не вибере все в діапазоні A..Bта зобов’язане C! Щоб зробити це, вам потрібно розділити на два рядки, перший git cherry-pick A..Bі потім git cherry-pick C. Отже, щоразу, коли у вас є діапазон, його потрібно виконувати окремо.
MicroVirus

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

щоб продовжити процес збору вишні.


Працювали як шарм! Дуже дякую!
snukone

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" не вдасться, якщо файл патча відрізняється занадто великою кількістю файлів, тому нам доведеться створити патч на файл, а потім застосувати. Зауважте, що скрипт не зможе видалити файли, видалені у відділенні джерела. Це рідкісний випадок, ви можете вручну видалити такі файли з цільової гілки. Статус виходу "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
Гарний пост. Отримано. Це нагадує мені stackoverflow.com/a/38418941/6309 . Я відновив недоліки збору вишні ще у 2012 році: stackoverflow.com/a/13524494/6309 .
VonC

1

Іншим варіантом може бути злиття з нашою стратегією до фіксації перед діапазоном, а потім "звичайне" злиття з останньою коміткою цього діапазону (або гілкою, коли вона є останньою). Тож припустимо, що лише 2345 та 3456 комітетів майстра мають бути об'єднані у особливість:

майстер:
1234 рік
2345 рік
3456
4567

у галузі:

git merge -s our 4567
git merge 2345
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.