Як вибірково об'єднати або вибрати зміни з іншої гілки в Git?


1449

Я використовую git для нового проекту, який має дві паралельні, але наразі експериментальні галузі розвитку:

  • master: імпорт існуючої бази коду плюс кілька модів, в чому я взагалі впевнений
  • exp1: експериментальна галузь №1
  • exp2: експериментальна галузь №2

exp1і exp2представляють два дуже різні архітектурні підходи. Поки я не просуваюсь далі, я не можу знати, який з них (якщо є) буде працювати. Коли я досягаю прогресу в одній галузі, іноді в мене є зміни, які були б корисні в іншій галузі, і я хотів би об'єднати лише ці.

Який найкращий спосіб об’єднати вибіркові зміни від однієї галузі розвитку до іншої, залишаючи після себе все інше?

Я розглядав підходи:

  1. git merge --no-commit з подальшим ручним видаленням великої кількості правок, які я не хочу робити спільними між гілками.

  2. Ручне копіювання загальних файлів у тимчасовий каталог з подальшим git checkoutпереходом до іншої гілки, а потім додаткове ручне копіювання із тимчасового каталогу в робоче дерево.

  3. Варіант вище. Покиньте відмовитися від expгілок і використайте два додаткових локальних сховища для експериментів. Це робить ручне копіювання файлів набагато простішим.

Всі три ці підходи здаються виснажливими та схильними до помилок. Я сподіваюся, що є кращий підхід; щось схоже на параметр шляху фільтра, що зробить git-mergeбільш вибірковим.


5
Якщо зміни у ваших експериментальних гілках добре організовані в окремі коміти, краще продумати терміни об’єднання вибіркових комітетів замість селективних файлів. Більшість відповідей нижче припускають, що це так.
akaihola

2
Чи не вдалося б комбінація з git merge -s ours --no-commitнаступними деякими git read-treeбути хорошим рішенням для цього? Див stackoverflow.com/questions/1214906 / ...
VonC

34
Пізніші питання має один рядок, добре написаний відповідь: stackoverflow.com/questions/10784523 / ...
Brahn

Відповіді:


475

Ви використовуєте команду cherry-pick для отримання окремих комісій з однієї гілки.

Якщо потрібні зміни (зміни) не містяться в окремих комісіях, то використовуйте показаний тут метод, щоб розділити комісію на окремі коміти . Грубо кажучи, ви використовуєте git rebase -iдля отримання оригінальної фіксації редагування, а потім git reset HEAD^для вибіркового відновлення змін, а потім git commitдля здійснення цього біта як нової фіксації в історії.

Існує ще один приємний метод тут у журналі Red Hat, де вони використовують git add --patchабо, можливо, git add --interactiveякий дозволяє вам додати лише частини частини, якщо ви хочете розділити різні зміни в окремий файл (шукайте на цій сторінці "розділити").

Розділивши зміни, тепер ви можете вибирати лише ті, що хочете.


14
З мого розуміння, це зайве перекрученість, ніж відповідь, що голосує вище.
Олександр Птах

54
Це технічно правильна відповідь, правильна відповідь здається "заплутаною". --- Вища відповідь - це просто швидка та брудна відповідь "робить хитрість", що для більшості людей - це все, про що вони йдуть (:
Яків

3
@akaihola: HEAD ^ правильно. Див. Man git-rev-parse: суфікс ^ до параметра ревізії означає перший з батьків цього об’єкта фіксації. Префікс ^ нотація використовується для виключення об'єктів, доступних з коміту.
Тайлер Рік

13
Я просто хотів поділитися іншим підходом, який видається найчистішим і менш заплутаним із них усіх: jasonrudolph.com/blog/2009/02/25/… повна простота і приголомшливість
superuseroi

14
Плутати дискусія щодо того, який підхід є "правильним"? Розгляньте різницю між файлами та комітетами (див. Зауваження внизу) . ОП хоче об'єднати ФАЙЛИ і не згадує КОМІТИ. Відповідь з більш високим голосом характерна для файлів; у прийнятій відповіді використовується вишня, яка є специфічною для комітетів. Вибір вишень може бути ключовим для об'єктивного об'єднання комітетів, але для переміщення файлів з однієї гілки в іншу може бути дуже болісно. Хоча коміти є основою сили git, не забувайте, що файли все ще грають роль!
Kay V

970

У мене була та сама проблема, про яку ви згадували вище. Але я зрозумів це зрозуміліше, пояснюючи відповідь.

Підсумок:

  • Ознайомтесь із шляхами із гілки, яку потрібно об’єднати,

    $ git checkout source_branch -- <paths>...
    

    Підказка: Це також працює без --подібного, що бачили у пов’язаному пості.

  • або вибірково зливати хунки

    $ git checkout -p source_branch -- <paths>...
    

    Крім того, використовуйте скидання, а потім додайте параметр -p,

    $ git reset <paths>...
    $ git add -p <paths>...
    
  • Нарешті покладіть

    $ git commit -m "'Merge' these changes"
    

9
Пов'язана стаття Барта Дж - найкращий підхід. Чітка, проста, одна команда. Це той, який я збираюся використовувати. :)
Пістос

256
Це не справжнє злиття. Ви вибираєте зміни за файлом, а не за фіксацією, і ви втратите будь-яку наявну інформацію про введення (автор, повідомлення). Зрозуміло, це добре, якщо ви хочете об'єднати всі зміни в деяких файлах, і це нормально, що ви повинні повторно виконати всі зобов'язання. Але якщо файли містять як зміни для злиття, так і інші для відмови, один з методів, передбачених в інших відповідях, буде служити вам краще.
akaihola

10
@mykhal та інші: цей етап автоматично встановлює файли в індексі, тож, якщо ви вийшли з foo.cцього git reset HEAD foo.cпункту , виконайте завдання, щоб видалити цей файл, і ви зможете його розмежувати. Я дізнався це, спробувавши його, і повернувся сюди, щоб шукати відповідь на це
michiakig

12
щоб побачити зміни, які ви також можете використати:git diff --cached
OderWat

9
Відповідно до цієї відповіді git checkout -p <revision> -- <path> буде те саме, що випускаєте перші три описані вами команди :)
7hi4g0

337

Щоб вибірково об'єднати файли з однієї гілки в іншу гілку, запустіть

git merge --no-ff --no-commit branchX

де branchXзнаходиться гілка, з якої ви хочете об'єднатись у поточну гілку.

Ця --no-commitопція зможе поетапно встановити файли, об'єднані Git, фактично не виконуючи їх. Це дасть вам можливість змінювати об'єднані файли, як тільки ви хочете, а потім виконувати їх самостійно.

Залежно від способу об'єднання файлів, є чотири випадки:

1) Ви хочете справжнього злиття.

У цьому випадку ви приймаєте об'єднані файли так, як Git автоматично їх об'єднав, а потім зв'язав.

2) Є деякі файли, які ви не хочете об’єднувати.

Наприклад, ви хочете зберегти версію в поточній гілці і проігнорувати версію в гілці, з якої ви з’єднуєтесь.

Щоб вибрати версію в поточній гілці, запустіть:

git checkout HEAD file1

Це отримає версію file1в поточній гілці і замінить автоматизований file1Git.

3) Якщо ви хочете, щоб версія в branchX (а не справжнє злиття).

Виконати:

git checkout branchX file1

Це отримає версію file1в branchXі замінить file1автоматичне об'єднання Git.

4) Останній випадок, якщо ви хочете вибрати лише конкретні злиття в file1.

У цьому випадку ви можете редагувати модифіковану file1безпосередньо, оновлювати її до того, на що хочете file1стати версією , а потім виконувати.

Якщо Git не зможе автоматично об'єднати файл, він повідомить про файл як " незаряджений " та створить копію, де потрібно буде вирішити конфлікти вручну.



Щоб пояснити далі на прикладі, скажімо, що ви хочете об'єднатись branchXу поточну гілку:

git merge --no-ff --no-commit branchX

Потім виконується git statusкоманда для перегляду стану модифікованих файлів.

Наприклад:

git status

# On branch master
# Changes to be committed:
#
#       modified:   file1
#       modified:   file2
#       modified:   file3
# Unmerged paths:
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#       both modified:      file4
#

Де file1, file2і file3чи файли git успішно об'єднані автоматично.

Що це означає, що зміни в masterі branchXдля всіх цих трьох файлів були об'єднані разом без будь - яких конфліктів.

Ви можете перевірити, як було зроблено об'єднання, запустивши git diff --cached;

git diff --cached file1
git diff --cached file2
git diff --cached file3

Якщо ви вважаєте, що якесь злиття є небажаним, ви можете

  1. редагуйте файл безпосередньо
  2. зберегти
  3. git commit

Якщо ви не хочете об'єднатись file1і хочете зберегти версію в поточній галузі

Біжи

git checkout HEAD file1

Якщо ви не хочете об'єднатись file2і хочете лише версію вbranchX

Біжи

git checkout branchX file2

Якщо ви хочете file3бути об'єднані автоматично, нічого не робіть.

Git вже з’єднав його в цей момент.


file4вище - невдале злиття Git. Це означає, що в обох гілках відбуваються зміни, що відбуваються в одній лінії. Тут потрібно буде вирішити конфлікти вручну. Ви можете відмовитись від об'єднаного файлу, відредагувавши файл безпосередньо, або виконавши команду оформлення замовлення для версії, у якій ви хочете file4стати.


Нарешті, не забудьте git commit.


10
Але git merge --no-commit branchXбудьте обережні: Якщо показник просто перемотка вперед, покажчик буде оновлений, і, таким чином,
ініціатива

16
@cfi Що робити з додаванням, --no-ffщоб запобігти такій поведінці?
Едуардо Коста

5
Я точно пропоную оновити цю відповідь варіантом Едуардо "--no-ff". Я перечитав всю річ (що інакше було чудово) лише для того, щоб моє злиття швидко перейшло.
Funktr0n

7
Це рішення дає найкращі результати та гнучкість.
Тіаго Македосе

20
На відміну від відповіді з найбільшою кількістю голосів, це рішення зберегло мою історію злиття, що для мене важливо, оскільки я переплітаю часткові дії вперед і назад по галузях. Я не пробував всіх інших запропонованих рішень, тому, можливо, деякі з них роблять і це.
ws_e_c421

107

Мені не подобаються вищезазначені підходи. Використання вишеньки чудово підходить для вибору однієї зміни, але це біль, якщо ви хочете внести всі зміни, крім деяких поганих. Ось мій підхід.

Немає жодного --interactiveаргументу, який можна передати git merge.

Ось альтернатива:

У вас є деякі зміни у "функції" гілки, і ви хочете, щоб деякі, але не всі вони перейшли до "майстра" неохайно (тобто ви не хочете, щоб вишня вибирала та вчиняла кожну)

git checkout feature
git checkout -b temp
git rebase -i master

# Above will drop you in an editor and pick the changes you want ala:
pick 7266df7 First change
pick 1b3f7df Another change
pick 5bbf56f Last change

# Rebase b44c147..5bbf56f onto b44c147
#
# Commands:
# pick = use commit
# edit = use commit, but stop for amending
# squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

git checkout master
git pull . temp
git branch -d temp

Тож просто загорніть це в сценарій оболонки, перетворіть master на $ to і перейдіть у $ на, і ви добре:

#!/bin/bash
# git-interactive-merge
from=$1
to=$2
git checkout $from
git checkout -b ${from}_tmp
git rebase -i $to
# Above will drop you in an editor and pick the changes you want
git checkout $to
git pull . ${from}_tmp
git branch -d ${from}_tmp

Я зафіксував форматування - це досить приємний метод, якщо ви хочете зробити підбірку комісій
1800 ІНФОРМАЦІЯ

Зараз я використовую цю техніку, і, здається, вона спрацювала дуже добре.
dylanfm

4
Ви можете перейти git rebase -i $toдо git rebase -i $to || $SHELL, щоб користувач міг зателефонувати git --skipтощо, якщо потрібно, якщо база не працює. Також варто прив'язувати рядки разом із &&новими рядками.
sircolinton

2
На жаль, схоже, посилання у відповіді є мертвим.
ThomasW

Посилання не тільки мертве, але воно має WOT погане попередження. Тому я її зняв.
Жан-Франсуа Корбетт

93

Є й інший шлях:

git checkout -p

Це поєднання між git checkoutі, git add -pможливо, цілком саме те, що ви шукаєте:

   -p, --patch
       Interactively select hunks in the difference between the <tree-ish>
       (or the index, if unspecified) and the working tree. The chosen
       hunks are then applied in reverse to the working tree (and if a
       <tree-ish> was specified, the index).

       This means that you can use git checkout -p to selectively discard
       edits from your current working tree. See the “Interactive Mode”
       section of git-add(1) to learn how to operate the --patch mode.

10
Це, безумовно, найпростіший і найпростіший метод, якщо у вас є лише керована кількість змін, для яких слід об'єднатись. Я сподіваюся, що більше людей помітять цю відповідь і відхилять її. Приклад: git checkout --patch exp1 file_to_merge
Тайлер Рік

1
Аналогічна відповідь розміщена на це питання: stackoverflow.com/a/11593308/47185
Тайлер Рік

О, я не знав, що в касі є патч! Я зробив замовлення / скидання / add -p замість цього.
Даніель К. Собрал

2
Воістину найпростіший метод. git checkout -p ім'я файлу-галузь. І найкраще, коли команда виконується, вона дає вам ay / n / e /? / ... тощо. можливість вирішити, як об’єднати файл. Я спробував з e, і я міг навіть відредагувати патч перед нанесенням ... Як це круто. Справжній вкладиш для об'єднання вибіркових файлів з інших гілок.
infoclogged

55

Хоча деякі з цих відповідей досить хороші, я відчуваю, що жоден насправді не відповів на початкове обмеження ОП: вибір конкретних файлів з певних гілок. Це рішення це робить, але може бути втомливим, якщо файлів багато.

Припустимо , у вас є master, exp1і exp2гілки. Ви хочете об'єднати один файл з кожної експериментальної гілки в головний. Я б зробив щось подібне:

git checkout master
git checkout exp1 path/to/file_a
git checkout exp2 path/to/file_b

# save these files as a stash
git stash
# merge stash with master
git merge stash

Це дасть вам різний файл для кожного з потрібних файлів. Нічого більше. Нічого менше. Корисно, що у вас різно різні зміни файлів між версіями - у моєму випадку, зміна програми з Rails 2 на Rails 3.

EDIT : це злиття файлів, але розумне злиття. Я не зміг розібратися, як використовувати цей метод для отримання різної інформації у файлі (можливо, це все ще буде для надзвичайних відмінностей. Набридливі дрібниці, наприклад пробіл, зливаються назад, якщо ви не використовуєте цю -s recursive -X ignore-all-spaceопцію)


4
Також зверніть увагу: ви можете робити декілька файлів із заданої гілки всі вбудовані, наприкладgit checkout exp1 path/to/file_a path/to/file_x
EMiller

2
Це прекрасно. Я зробив, git checkout feature <path>/*щоб отримати групи файлів.
Ізервуд

Це працює нормально, але додано два зайвих об’єкта фіксації. Не велика угода, але трохи безладна
MightyPork

@MightyPork ти маєш рацію. На жаль, оскільки я писав це так давно, я вже не впевнений, чому кроки "git stash" та "git merge stash" там, а не "git commit".
Ерік Ху

2
О, це зрозуміло, я думаю. Таким чином він об'єднує один файл, не обов'язково перезаписуючи попередні зміни на цільовій гілці.
MightyPork

48

Відповідь ІНФОРМАЦІЇ 1800 року абсолютно правильна. Однак, як "git noob", "використовувати git cherry-pick" мені було недостатньо, щоб зрозуміти це без трохи більше копання в Інтернеті, тож я подумав, що опублікую більш детальний посібник у випадку, якщо хтось ще в подібний човен.

У моєму випадку було використання вибіркового залучення змін із чужої гілки github до моєї власної. Якщо у вас вже є локальна гілка зі змінами, вам потрібно лише виконати кроки 2 та 5-7.

  1. Створіть (якщо не створено) локальну гілку із змінами, які ви хочете внести.

    $ git branch mybranch <base branch>

  2. Переключись на нього.

    $ git checkout mybranch

  3. Виберіть потрібні зміни з акаунта іншої людини. Якщо ви ще цього не зробили, ви хочете додати їх як віддалений.

    $ git remote add repos-w-changes <git url>

  4. Спустіть все зі своєї гілки.

    $ git pull repos-w-changes branch-i-want

  5. Перегляньте журнали фіксації, щоб побачити, які зміни потрібно:

    $ git log

  6. Поверніться назад до гілки, в яку потрібно втягнути зміни.

    $ git checkout originalbranch

  7. Вишня вибирайте свої зобов'язання, один за одним, з хешами.

    $ git cherry-pick -x hash-of-commit

Порада для капелюхів: http://www.sourcemage.org/Git_Guide


3
Порада: спочатку скористайтеся git cherryкомандою (див. Посібник спочатку) для виявлення комісій, які ви ще не об'єднали.
akaihola

Це працює .. 1. створив нову гілку 2. створив деякі файли / вніс деякі зміни 3. здійснити 4. перевірити головну гілку 5. Запустити git cherry-pick -x хеш-фіксації та вирішити конфлікти злиття ти хороший йти.
RamPrasadBismil

Ваше посилання більше не працює. Чи можете ви її оновити?
creep3007

42

Ось як можна замінити Myclass.javaфайл у masterвідділенні на Myclass.javaна feature1відділення. Він буде працювати, навіть якщо Myclass.javaйого не існує master.

git checkout master
git checkout feature1 Myclass.java

Зверніть увагу, що це замінить - не зливається - і ігнорує локальні зміни в головній гілці.


6
Це не злиється. Він просто перезаписає зміни на майстер зі змінами з гілки особливості1.
Skunkwaffle

3
Ідеально, я шукав такого роду злиття, де theirsперезапис ours=> +1 Ура;)
olibre

1
Іноді все, що ви хочете зробити, - це замінити весь файл, тому я цього хотів, але вам потрібно переконатися, що ви хочете втратити всі зміни, які ви внесли до цього файлу.
MagicLAMP

1
Найяскравіше рішення, враховуючи, що ОП спеціально хотіла замінити весь файл еквівалентом на іншу гілку:2. Manual copying of common files into a temp directory followed by ...copying out of the temp directory into the working tree.
Brent Faust,

29

Простий спосіб - насправді об'єднати конкретні файли з двох гілок, а не просто замінити конкретні файли на інші з іншої гілки.

Крок перший: Відрізьте гілки

git diff branch_b > my_patch_file.patch

Створює файл виправлення різниці між поточною гілкою та branch_b

Крок другий: застосуйте виправлення до файлів, що відповідають шаблону

git apply -p1 --include=pattern/matching/the/path/to/file/or/folder my_patch_file.patch

корисні примітки щодо варіантів

Ви можете використовувати *як підстановку в шаблоні включення.

Штрихи не потрібно уникати.

Крім того, ви можете використовувати --exclude замість цього і застосувати його до всього, крім файлів, що відповідають шаблону, або обернути патч на -R

-P1 варіант є пережитком команди * Unix патч і той факт , що вміст файлу патча випереджати кожне ім'я файлу з a/або b/(або більше , в залежності від того, як був створений файл заплатки) , який вам потрібно роздягнутися , щоб він міг зрозуміти, реальний файл до шляху до файлу, до якого потрібно застосувати патч.

Перегляньте довідкову сторінку на git-застосуйте додаткові варіанти.

Крок третій: кроку третього немає

Очевидно, ви хочете здійснити свої зміни, але хто може сказати, що у вас немає інших пов'язаних налаштувань, які ви хочете зробити, перш ніж взяти на себе зобов’язання.


1
Це було дуже корисно, коли у current_branch було багато "додаткових" змін, які потрібно було зберегти. Отримав відмінність лише змін, внесених bran_b як: git diff HEAD ... branch_b (так - три періоди робить магічний трюк).
Саад Малик

@masukomi, На кроці 2, чи не слід додати патч-файл, створений на кроці 1, як аргумент?
Спіраліс

Для мене всі зміни відхиляються. Будь-яка ідея чому?
LinusGeffarth

Початкова думка @LinusGeffarth полягає в тому, що, можливо, ви отримали гілки назад, коли робите патч? буде подальше спостереження поза межами SO, щоб побачити, чи зможемо ми з'ясувати це.
masukomi

24

Ось як ви можете отримати історію, щоб слідкувати за декількома файлами з іншої гілки з мінімальними суєтами, навіть якщо більш "просте" злиття принесло б набагато більше змін, які ви не хочете.

По-перше, ви зробите незвичайний крок, заздалегідь оголосивши, що те, що ви збираєтесь зробити, - це злиття, без git робити нічого взагалі для файлів у вашому робочому каталозі:

git merge --no-ff --no-commit -s ours branchname1

. . . де "назва гілки" - це те, з чого ви стверджуєте, що зливаєтесь. Якби ви взяли на себе зобов’язання відразу, це не змінило б, але це все одно демонструватиме походження з іншої галузі. Ви можете додати більше гілок / тегів / тощо. до командного рядка, якщо вам потрібно. На сьогоднішній день змін не потрібно здійснювати, тому перейдіть до файлів з інших версій, наступних.

git checkout branchname1 -- file1 file2 etc

Якщо ви зливалися з декількох інших галузей, повторіть, якщо потрібно.

git checkout branchname2 -- file3 file4 etc

Тепер файли з іншої гілки знаходяться в індексі, готові до фіксації, з історією.

git commit

і вам доведеться багато пояснити, щоб зробити в цьому повідомленні про вчинення.

Зауважте, хоча, якщо не було зрозуміло, що це заплутано. Це не в дусі, для чого потрібна "гілка", а вишня - це більш чесний спосіб робити те, що ви робили, тут. Якщо ви хотіли зробити ще одне "об'єднання" для інших файлів у тій самій гілці, які ви не принесли останній раз, це зупинить вас повідомленням "вже оновленого". Це симптом нерозгалуження, коли ми повинні мати, в гілці "від" повинно бути більше однієї іншої гілки.


3
Ваша перша команда ( git merge --no-ff --no-commit -s outs branchname1) - саме те, що я шукав! Дякую!
RobM

1
З кількома гілками, необхідною історією, потрібно об'єднати один (-и) файл (і) та змінити вміст файлу перед тим, як натиснути, це здається гідною альтернативою. Наприклад, dev => master, але ви хочете змінити визначення хосту або подібне, перш ніж натискати на master.
timss

15

Я знаю, що я трохи спізнююсь, але це мій робочий процес для об'єднання вибіркових файлів.

#make a new branch ( this will be temporary)
git checkout -b newbranch
# grab the changes 
git merge --no-commit  featurebranch
# unstage those changes
git reset HEAD
(you can now see the files from the merge are unstaged)
# now you can chose which files are to be merged.
git add -p
# remember to "git add" any new files you wish to keep
git commit

Я використав для цього невелику варіацію. Замість злиття я вишневий. Це робить свою роботу. Єдиним недоліком такого підходу є те, що ви втрачаєте посилання на оригінальний хеш-код.
Метт Флоренція

15

Найпростіший спосіб - встановити репо для тієї гілки, з якою ви хочете об'єднатися, а потім запустіть,

git checkout [branch with file] [path to file you would like to merge]

Якщо ти біжиш

git status

ви побачите файл, який вже налаштовано ...

Потім бігайте

git commit -m "Merge changes on '[branch]' to [file]"

Простий.


3
Це майже найкраща відповідь, яку я знайшов. будь ласка, дивіться jasonrudolph.com/blog/2009/02/25/… Так чітко, стисло і це просто працює!
superuseroi

1
що повністю замінить вміст файлів із вихідної гілки, а не злиття
Amare

Я збирався відповісти так, я думав, що я вигадую нові речі, на які ще не відповів! Але це найпростіший спосіб зробити це. Це повинно бути зверху!
Ірфанді Джип

15

Я знайшов цю публікацію, яка містить найпростішу відповідь. Просто роби:

$ #git checkout <branch from which you want files> <file paths>

Приклад:

$ #pulling .gitignore file from branchB into current branch
$ git checkout branchB .gitignore

Докладнішу інформацію див. У публікації.


3
Це насправді не зливається, він перезаписує файл на поточній гілці.
Ігор Раліч

1
@igrali Це корисний коментар, але порівняно зі складністю "правильних" способів це зробити, це вдале вирішення. Треба бути дуже обережним.
Owensmartin

12

Дивно, що у git досі немає такого зручного інструменту "поза коробкою". Я дуже використовую його, коли оновлюю стару гілку версії (яка все ще має багато користувачів програмного забезпечення) лише деякими помилками з поточної гілки версії. У цьому випадку часто потрібно швидко отримати лише кілька рядків коду з файлу в магістралі, ігноруючи безліч інших змін (які не повинні переходити в стару версію) ... І звичайно інтерактивне тристороннє злиття У цьому випадку потрібна, git checkout --patch <branch> <file path>не використовується для цієї вибіркової мети злиття.

Ви можете це зробити легко:

Просто додайте цей рядок до [alias]розділу у вашому глобальному .gitconfigчи локальному .git/configфайлі:

[alias]
    mergetool-file = "!sh -c 'git show $1:$2 > $2.theirs; git show $(git merge-base $1 $(git rev-parse HEAD)):$2 > $2.base; /C/BCompare3/BCompare.exe $2.theirs $2 $2.base $2; rm -f $2.theirs; rm -f $2.base;' -"

Це означає, що ви використовуєте функцію "Більше порівняння". Просто перейдіть на програмне забезпечення на ваш вибір, якщо це потрібно. Або ви можете змінити його на тристороннє автоматичне злиття, якщо вам не потрібно інтерактивне селективне об’єднання:

[alias]
    mergetool-file = "!sh -c 'git show $1:$2 > $2.theirs; git show $(git merge-base $1 $(git rev-parse HEAD)):$2 > $2.base; git merge-file $2 $2.base $2.theirs; rm -f $2.theirs; rm -f $2.base;' -"

Потім використовуйте так:

git mergetool-file <source branch> <file path>

Це дасть вам справжню селективну можливість злиття дерева з будь-яким файлом в іншій галузі.


10

Це не зовсім те, що ви шукали, але мені було корисно:

git checkout -p <branch> -- <paths> ...

Це суміш деяких відповідей.


2
Це справді корисно, і можна було б додати до того, що для мене найкраща відповідь, відповідь @ alvinabad. При цьому: git checkout HEAD file1щоб зберегти поточну версію та вимкнути файл file1, можна скористатися -pопцією, щоб вибрати частину файлу для об'єднання. дякую за трюк!
Саймон К.

Це моя відповідь. Простий, до речі і працює
Джессі Реза Хорасані

8

Я б зробив

git diff commit1..commit2 filepattern | git-apply - index && git

Таким чином, ви можете обмежити діапазон комісій для файлового файлу з гілки.

Викрадено з: http://www.gelato.unsw.edu.au/archives/git/0701/37964.html


У деяких ситуаціях це може бути дуже зручно. Однак якщо зміни є в іншій гілці, ви можете просто здійснити реєстрацію з кінця цієї гілки, як у відповіді Барта Дж вище.
cdunn2001

Хто такий Барт Джес?
Чорний

8

У мене була та сама проблема, про яку ви згадували вище. Але я знайшов цей git блог більш зрозумілим у поясненні відповіді.

Команда з вищевказаного посилання:

#You are in the branch you want to merge to
git checkout <branch_you_want_to_merge_from> <file_paths...>

ти це тестував? я впевнений, що файли будуть замінені на <branch_you_want_to_merge_from>, а не об'єднати
Amare

7

Мені подобається відповідь "git-interactive-merge" вище, але є одна простіша. Дозвольте git зробити це для вас, використовуючи інтерактивну комбінацію бази даних:

      A---C1---o---C2---o---o feature
     /
----o---o---o---o master

Отже, ви хочете, щоб C1 та C2 з гілки "особливості" (точка відгалуження "A"), але поки що нічого іншого.

# git branch temp feature
# git checkout master
# git rebase -i --onto HEAD A temp

Що, як зазначено вище, пересилає вас до інтерактивного редактора, де ви вибираєте рядки "вибору" для C1 та C2 (як вище). Збережіть і вийдіть, а потім він продовжить ребазацію і дасть вам "temp" гілки, а також HEAD у master + C1 + C2:

      A---C1---o---C2---o---o feature
     /
----o---o---o---o-master--C1---C2 [HEAD, temp]

Тоді ви можете просто оновити майстер на HEAD та видалити темп-гілку, і ви готові йти:

# git branch -f master HEAD
# git branch -d temp

7

Про що git reset --soft branch? Я здивований, що ще ніхто цього не згадував.

Для мене це найпростіший спосіб вибіркового вибору змін з іншої гілки, оскільки ця команда поміщає в моє робоче дерево всі зміни різниці, і я можу легко вибрати або повернути, яку саме мені потрібно. Таким чином я маю повний контроль над скоєними файлами.


6

Я знаю, що це питання давнє, і є багато інших відповідей, але я написав власний сценарій під назвою "pmerge", щоб частково об'єднати каталоги. Це незавершена робота, і я все ще навчаюсь сценаріям git та bash.

Ця команда використовує, git merge --no-commitа потім не застосовує зміни, які не відповідають вказаному шляху.

Використання: git pmerge branch path
Приклад:git merge develop src/

Я її не перевіряв широко. У робочому каталозі не повинно бути будь-яких неподілених змін та непотрібних файлів.

#!/bin/bash

E_BADARGS=65

if [ $# -ne 2 ]
then
    echo "Usage: `basename $0` branch path"
    exit $E_BADARGS
fi

git merge $1 --no-commit
IFS=$'\n'
# list of changes due to merge | replace nulls w newlines | strip lines to just filenames | ensure lines are unique
for f in $(git status --porcelain -z -uno | tr '\000' '\n' | sed -e 's/^[[:graph:]][[:space:]]\{1,\}//' | uniq); do
    [[ $f == $2* ]] && continue
    if git reset $f >/dev/null 2>&1; then
        # reset failed... file was previously unversioned
        echo Deleting $f
        rm $f
    else
        echo Reverting $f
        git checkout -- $f >/dev/null 2>&1
    fi
done
unset IFS

4

Ви можете використовувати read-treeдля читання або об'єднання даного віддаленого дерева у поточний індекс, наприклад:

git remote add foo git@example.com/foo.git
git fetch foo
git read-tree --prefix=my-folder/ -u foo/master:trunk/their-folder

Щоб здійснити злиття, використовуйте -mзамість цього.

Дивіться також: Як злити підкаталог у git?


3

Простий підхід для вибіркового злиття / введення файлів:

git checkout dstBranch git merge srcBranch // make changes, including resolving conflicts to single files git add singleFile1 singleFile2 git commit -m "message specific to a few files" git reset --hard # blow away uncommitted changes


3

Якщо у вас не так багато файлів, які змінилися, це не дасть вам зайвих комісій.

1. Тиражувати гілку тимчасово
$ git checkout -b temp_branch

2. Скиньте останній розшук
$ git reset --hard HEAD~n , коли nкількість комісій потрібно повернути назад

3. Оформити кожен файл з оригінальної гілки
$ git checkout origin/original_branch filename.ext

Тепер ви можете скористатися і примусити натиснути (перезаписати віддалений), якщо потрібно.


3

Якщо вам потрібно лише об'єднати певний каталог і залишити все інше недоторканим і при цьому зберегти історію, ви можете, можливо, спробувати це ... створити новий target-branchвимикач, masterперш ніж експериментувати.

Наведені нижче кроки передбачають, що у вас є дві гілки target-branchта source-branch, а каталог, dir-to-mergeякий ви хочете об'єднати, знаходиться в source-branch. Припустімо також, що у вас є інші каталоги, як dir-to-retainу цілі, які ви не хочете змінювати та зберігати історію. Крім того, припускає, що в конфлікті є конфлікти злиття dir-to-merge.

git checkout target-branch
git merge --no-ff --no-commit -X theirs source-branch
# the option "-X theirs", will pick theirs when there is a conflict. 
# the options "--no--ff --no-commit" prevent a commit after a merge, and give you an opportunity to fix other directories you want to retain, before you commit this merge.

# the above, would have messed up the other directories that you want to retain.
# so you need to reset them for every directory that you want to retain.
git reset HEAD dir-to-retain
# verify everything and commit.

2

Коли між поточними елементами двох гілок змінилося лише декілька файлів, я вручну об'єдную ці зміни, переглядаючи різні файли.

git difftoll <branch-1>..<branch-2>

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