Как выборочно объединить или выбрать изменения из другой ветки в Git? - PullRequest
1311 голосов
/ 16 января 2009

Я использую git для нового проекта, который имеет две параллельные - но в настоящее время экспериментальные - ветви разработки:

  • master: импорт существующей кодовой базы плюс несколько модов, в которых я, как правило, уверен
  • exp1: экспериментальная ветка № 1
  • exp2: экспериментальная ветка № 2

exp1 и exp2 представляют два очень разных архитектурных подхода. Пока я не продвинусь дальше, у меня нет никакого способа узнать, какой из них (если любой) будет работать. Когда я делаю успехи в одной ветке, у меня иногда есть правки, которые были бы полезны в другой ветке, и я хотел бы объединить только те.

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

Подходы, которые я рассмотрел:

  1. git merge --no-commit с последующей ручной постановкой большого количества правок, которые я не хочу делать общими для ветвей.

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

  3. Вариант вышеизложенного. Оставьте пока ветки exp и используйте два дополнительных локальных репозитория для экспериментов. Это значительно упрощает ручное копирование файлов.

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

Ответы [ 25 ]

15 голосов
/ 04 ноября 2013

Я нашел в этом посте , в котором содержится самый простой ответ. Просто сделай:

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

Пример:

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

См. Сообщение для получения дополнительной информации.

15 голосов
/ 18 февраля 2010

Я знаю, что немного опоздал, но это мой рабочий процесс объединения отдельных файлов.

#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
14 голосов
/ 30 октября 2013

Самый простой способ - установить репо в ту ветвь, с которой вы хотите объединиться, и запустить,

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

Если вы запустите

git status

вы увидите файл уже в постановке ...

Затем запустите

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

Simple.

12 голосов
/ 03 июля 2014

Странно, что у 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>

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

9 голосов
/ 09 февраля 2016

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

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

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

8 голосов
/ 19 июня 2012

У меня была точно такая же проблема, как упомянуто вами выше. Но я нашел этот мерзавец более ясным в объяснении ответа.

Команда по вышеуказанной ссылке:

#You are in the branch you want to merge to
git checkout <branch_you_want_to_merge_from> <file_paths...>
7 голосов
/ 09 декабря 2011

Мне нравится ответ '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 голосов
/ 26 января 2011

Я бы сделал

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

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

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

6 голосов
/ 27 мая 2011

Я знаю, что этот вопрос старый и есть много других ответов, но я написал свой собственный скрипт под названием '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 голосов
/ 25 февраля 2016

Вы можете использовать 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?

...