Как выборочно объединить или выбрать изменения из другой ветки в Git?

1450

Я использую 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
Оформить заказ в этом блоге только для объединения определенных файлов jasonrudolph.com/blog/2009/02/25/…
Ашутош Чамоли

Ответы:

475

Вы используете команду cherry-pick, чтобы получить отдельные коммиты из одной ветви.

Если нужного вам изменения нет в отдельных фиксациях, то используйте метод, показанный здесь, чтобы разделить фиксацию на отдельные фиксации . Грубо говоря, вы используете git rebase -iдля редактирования исходной фиксации, затем git reset HEAD^для выборочного возврата изменений, а затем git commitдля фиксации этого бита в качестве новой фиксации в истории.

Здесь, в журнале Red Hat, есть еще один приятный метод, в котором они используют git add --patchили, возможно, git add --interactiveкоторый позволяет вам добавлять только части фрагмента, если вы хотите разделить различные изменения в отдельном файле (найдите на этой странице «split»).

Разделив изменения, теперь вы можете выбрать те, которые хотите.

1800 ИНФОРМАЦИЯ
источник
14
Насколько я понимаю, это излишне запутанно, чем ответ с более высоким голосом.
Александр Берд
54
Это технически правильный ответ, правильный ответ выглядит «запутанным». --- Ответ с более высоким рейтингом - это быстрый и грязный ответ, который "делает свое дело", и для большинства людей это все, о чем они ((
Иаков
3
@akaihola: ГОЛОВА ^ правильно. Смотрите man git-rev-parse: суффикс ^ к параметру ревизии означает первого родителя этого объекта коммита. Префикс ^ используется для исключения достижимых коммитов из коммита.
Тайлер Рик
13
Я просто хотел поделиться другим подходом, который кажется самым чистым и менее запутанным из всех их: jasonrudolph.com/blog/2009/02/25/… абсолютная простота и удивительность
superuseroi
14
Смущенный дебатами о том, какой подход является «правильным»? Рассмотрим разницу между файлами и коммитами (см. Примечания внизу) . ОП хочет объединить ФАЙЛЫ и не упоминает КОМИТЕТЫ. Ответ с более высоким рейтингом относится к файлам; В принятом ответе используется cherry-pick, специфичный для коммитов. Cherry-pick может быть ключевым для выборочного объединения коммитов, но это может быть очень болезненным для перемещения файлов из одной ветви в другую. Хотя коммиты являются сердцем силы Git, не забывайте, что файлы по-прежнему играют свою роль!
Кей 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"
    
Chris Steinbach
источник
9
Связанная статья Барта J - лучший подход. Понятно, просто, одна команда. Это тот, который я собираюсь использовать. :)
Pistos
256
Это не настоящее слияние. Вы выбираете изменения по файлам, а не по коммиту, и вы потеряете любую существующую информацию о коммите (автор, сообщение). Конечно, это хорошо, если вы хотите объединить все изменения в некоторых файлах, и это нормально, что вы должны заново выполнить все коммиты. Но если файлы содержат как изменения для слияния, так и другие для отмены, один из методов, представленных в других ответах, будет вам полезнее.
акайхола
10
@mykhal и другие: это этапы файлы в индексе автоматически, поэтому если вы проверили foo.cсделать , git reset HEAD foo.cчтобы убрать из буфера , что файл , а затем вы можете дифф его. Я узнал об этом после того, как попробовал и вернулся сюда, чтобы найти ответ на этот вопрос
michiakig
12
чтобы увидеть изменения, которые вы также можете использовать:git diff --cached
OderWat
9
В соответствии с этим ответом git checkout -p <revision> -- <path> будет то же, что и выдача первых трех команд, которые вы описали :)
7hi4g0
338

Чтобы выборочно объединить файлы из одной ветви в другую, запустите

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

Это позволит получить версию file1in 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те файлы , мерзавец , успешно автоматически объединены.

Это означает, что изменения в 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.

alvinabad
источник
10
Осторожно, хотя: если git merge --no-commit branchXэто просто перемотка вперед, указатель будет обновлен, и --no-commit, таким образом, будет игнорироваться
cfi
16
@cfi Как насчет добавления, --no-ffчтобы предотвратить такое поведение?
Эдуардо Коста
5
Я определенно предлагаю обновить этот ответ с помощью опции "--no-ff" Эдуардо. Я прочитал все это (что в остальном было здорово) только для того, чтобы мое слияние стало быстрым.
Funktr0n
7
Это решение дает лучшие результаты и гибкость.
Тьяго Маседо
20
В отличие от ответа с большинством голосов, это решение сохранило мою историю слияний, что важно для меня, так как я переплетаю частичные коммиты назад и вперед через ветви. Я не пробовал все другие предлагаемые решения, поэтому, возможно, некоторые из них делают это также.
ws_e_c421
107

Мне не нравятся вышеуказанные подходы. Использование cherry-pick отлично подходит для выбора одного изменения, но это неприятно, если вы хотите внести все изменения, кроме некоторых плохих. Вот мой подход.

Нет --interactiveаргумента, который вы можете передать git merge.

Вот альтернатива:

У вас есть некоторые изменения в ветке 'feature', и вы хотите, чтобы некоторые, но не все, перешли к 'master' не небрежным образом (т.е. вы не хотите выбирать и фиксировать каждую из них)

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 и измените функцию на $ from, и все готово:

#!/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
nosatalian
источник
Я исправил форматирование - это очень хороший метод, если вы хотите сделать выбор коммитов
1800 ИНФОРМАЦИЯ
Сейчас я использую эту технику, и она, кажется, сработала очень хорошо.
2010 года
4
Возможно, вы захотите изменить git rebase -i $toего git rebase -i $to || $SHELL, чтобы пользователь мог звонить и git --skipт. Д., Если это необходимо, если перебазировка не удалась. Также стоит объединять в цепочки строки &&вместо новых.
сирколинтон
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.
Chronial
источник
10
Это, безусловно, самый простой, самый простой метод, если у вас есть только управляемое количество изменений для объединения. Я надеюсь, что больше людей заметят этот ответ и проголосуют за него. Пример: git checkout --patch exp1 file_to_merge
Тайлер Рик
1
Аналогичный ответ, размещенный на этот вопрос: stackoverflow.com/a/11593308/47185
Тайлер Рик
О, я не знал, что на кассе был патч! Вместо этого я сделал checkout / reset / add -p.
Даниэль С. Собрал
2
Поистине самый простой способ. git checkout -p имя_файла. И лучше всего то, что когда команда запускается, она дает вам ay / n / e /? / ... и т. Д. возможность решить, как объединить файл. Я попробовал с е, и я мог бы даже отредактировать патч перед применением ... Как это круто. Истинный лайнер для объединения отдельных файлов из других веток.
Infoclogged
55

Хотя некоторые из этих ответов довольно хороши, мне кажется, что никто не ответил на первоначальное ограничение OP: выбор определенных файлов из определенных веток. Это решение делает это, но может быть утомительным, если есть много файлов.

Допустим , у вас есть 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.

РЕДАКТИРОВАТЬ : это объединит файлы, но делает умное объединение. Я не смог выяснить, как использовать этот метод для получения информации о различиях в файле (возможно, это все равно будет для экстремальных различий. Раздражающие мелочи, такие как пробелы, объединяются, если вы не используете -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 commit» есть шаги «git stash» и «git merge stash».
Эрик Ху,
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

Cory
источник
3
Совет: сначала используйте git cherryкоманду (сначала обратитесь к руководству), чтобы определить коммиты, которые вы еще не объединили.
akaihola
Это работает .. 1. создал новую ветку 2. создал несколько файлов / сделал некоторые изменения 3. передал 4. извлеките основную ветку 5. запустите git cherry-pick -x hash-of-commit и разрешите конфликты слияния, если вы хороши идти.
RamPrasadBismil
Ваша ссылка больше не работает. Можете ли вы обновить его, пожалуйста?
creep3007
42

Вот как вы можете заменить Myclass.javaфайл в masterветке на Myclass.javaв feature1ветке. Это будет работать, даже если Myclass.javaне существует master.

git checkout master
git checkout feature1 Myclass.java

Обратите внимание, что это перезапишет, а не объединит, и скорее проигнорирует локальные изменения в основной ветке.

maestr0
источник
6
Это не слится. Он просто перезапишет изменения на master с изменениями из ветви feature1.
Skunkwaffle
3
Отлично, я искал этот вид слияния, где theirsперезаписать ours=> +1 Cheers;)
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 patch и тот факт, что содержимое файла исправления предшествует каждому имени файла с помощью a/илиb/ (или более в зависимости от того, как был создан файл исправления), которое необходимо удалить, чтобы оно могло выяснить реальный файл с путем к файлу, к которому должен быть применен патч.

Зайдите на страницу man для git-apply, чтобы узнать больше вариантов.

Шаг третий: нет третьего шага

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

masukomi
источник
1
Это было очень полезно, когда в current_branch было много «дополнительных» изменений, которые нужно было сохранить. Получил различия только изменений, внесенных branch_b как: git diff HEAD ... branch_b (да - три периода делают фокус).
Саад Малик
@masukomi, На шаге 2, разве вы не должны добавить файл патча, созданный на шаге 1, в качестве аргумента?
Спиралис
Для меня все изменения отклонены. Есть идеи почему?
ЛинусГеффарт
Первоначальная мысль @LinusGeffarth заключается в том, что, возможно, вы получили ветви назад при создании патча? будем следить за пределами SO, чтобы увидеть, сможем ли мы понять это.
Масукоми
24

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

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

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

, , , где "branchname" - это то, из чего вы утверждаете, что объединяетесь. Если вы сделаете коммит сразу, он не внесет никаких изменений, но он все равно покажет родословную из другой ветви. Вы можете добавить больше веток / тегов / и т.д. в командной строке, если вам нужно, а также. На данный момент, однако, нет изменений для фиксации, поэтому получите файлы из других ревизий, затем.

git checkout branchname1 -- file1 file2 etc

Если вы объединяли несколько ветвей, повторите при необходимости.

git checkout branchname2 -- file3 file4 etc

Теперь файлы из другой ветки находятся в индексе, готовые к фиксации, с историей.

git commit

и у вас будет много объяснений, которые нужно сделать в этом сообщении коммита.

Пожалуйста, обратите внимание, на случай, если неясно, что это неправильно. Это не в духе того, для чего нужна "ветка", и "вишня" - это более честный способ сделать то, что вы будете делать здесь. Если вы захотите сделать еще одно «слияние» для других файлов в той же ветке, которую вы не перенесли в прошлый раз, это остановит вас сообщением «уже в курсе». Это признак отсутствия ветвления, когда у нас должно быть, в ветке «from» должно быть несколько разных ветвей.

Jejese
источник
3
Ваша первая команда ( git merge --no-ff --no-commit -s outs branchname1) - это именно то, что я искал! Спасибо!
RobM
1
С несколькими ветвями, необходимой историей, необходимостью объединять один и тот же файл и изменять содержимое файла перед отправкой, это, кажется, достойная альтернатива. Например, dev => master, но вы хотите изменить определение хоста или аналогичное, прежде чем нажать на master.
Тимсс
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]"

Просто.

dsieczko
источник
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;' -"

Это означает, что вы используете Beyond Compare. Просто измените программное обеспечение на ваш выбор, если это необходимо. Или вы можете изменить его на трехстороннее автоматическое слияние, если вам не нужно интерактивное выборочное слияние:

[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>

Это даст вам истинную выборочную возможность слияния в виде дерева любого файла в другой ветке.

JimStar
источник
10

Это не совсем то, что вы искали, но это было полезно для меня:

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

Это смесь некоторых ответов.

Фелипе
источник
2
Это действительно полезно и может быть добавлено к тому, что для меня лучший ответ, ответ @ alvinabad. При выполнении: git checkout HEAD file1чтобы сохранить текущую версию и удалить файл file1, можно использовать -pопцию, чтобы выбрать часть файла для объединения. спасибо за трюк!
Саймон С.
Это мой любимый ответ. Просто, к делу и работает
Джесси Реза Хорасани
8

Я бы сделал

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

Таким образом, вы можете ограничить диапазон коммитов для файлового шаблона из ветки.

Похищен у: http://www.gelato.unsw.edu.au/archives/git/0701/37964.html

lumpidu
источник
В некоторых ситуациях это может быть очень удобно. Однако, если изменения находятся в другой ветви, вы можете просто оформить заказ из кончика этой ветви, как в ответе Барта J выше.
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 из ответвления «Feature» (точка ветвления «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]

Затем вы можете просто обновить master до HEAD и удалить временную ветку, и все готово:

# git branch -f master HEAD
# git branch -d temp
брод
источник
7

Как насчет git reset --soft branch? Я удивлен, что никто еще не упомянул об этом.

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

GarryOne
источник
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?

kenorb
источник
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

Теперь вы можете зафиксировать и принудительно нажать (чтобы перезаписать удаленный), если это необходимо.

JBaczuk
источник
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.
code4kix
источник
2

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

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

raratiru
источник