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

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

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

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

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

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

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

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

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

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

22 Solutions collect form web for “Как выборочно объединить или выбрать изменения из другого филиала в Git?”

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

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

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

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

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

Резюме:

  • Проверьте путь (ы) от ветки, которую хотите объединить,

     $ git checkout source_branch -- ... 
  • или выборочно объединить куски

     $ git checkout -p source_branch -- ... 

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

     $ git reset ... $ git add -p ... 
  • Наконец, совершите

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

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

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

где branchX – это ветка, которую вы хотите объединить в текущую ветку.

Опция --no-commit будет обрабатывать файлы, которые были объединены Git без их фактической --no-commit . Это даст вам возможность модифицировать объединенные файлы, однако вы хотите, а затем сами их выполнить.

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

1) Вы хотите истинное слияние.

В этом случае вы принимаете объединенные файлы так, как Git автоматически объединяет их, а затем фиксирует их.

2) Есть файлы, которые вы не хотите сливать.

Например, вы хотите сохранить версию в текущей ветке и игнорировать версию в ветви, из которой вы сходите.

Чтобы выбрать версию в текущей ветке, запустите:

 git checkout HEAD file1 

Это приведет к извлечению версии file1 в текущую ветвь и перезаписывает file1 загруженный Git.

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 ..." 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 был объединен автоматически, ничего не делайте.

Гит уже слил его в этот момент.

file4 выше – неудачное слияние Git. Это означает, что в обеих ветвях есть изменения, которые происходят в одной строке. Здесь вам нужно будет разрешить конфликты вручную. Вы можете отменить слияние, отредактировав файл напрямую или запустив команду checkout для версии в ветке, которую вы хотите, чтобы file4 стал.

Наконец, не забудьте сделать git commit .

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

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

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

У вас есть некоторые изменения в функции «branch», и вы хотите, чтобы некоторые из них, но не все из них, были «мастером» небрежным способом (т. Е. Вы не хотите, чтобы вишневый выбор и фиксация каждого из них)

 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 

Так что просто оберните это в скрипт оболочки, смените мастер на $ 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 

Есть еще один способ:

 git checkout -p 

Это сочетание между git checkout и git add -p и вполне может быть именно тем, что вы ищете:

  -p, --patch Interactively select hunks in the difference between the  (or the index, if unspecified) and the working tree. The chosen hunks are then applied in reverse to the working tree (and if a  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. 

Хотя некоторые из этих ответов довольно хороши, я чувствую, что никто на самом деле не ответил на первоначальное ограничение 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 

Это даст вам diff-файлы для каждого из файлов, которые вы хотите. Больше ничего. Не меньше. Полезно, что у вас радикально разные изменения файлов между версиями – в моем случае, изменение приложения из Rails 2 в Rails 3.

EDIT : это объединит файлы, но сделает интеллектуальное слияние. Мне не удалось выяснить, как использовать этот метод для получения информации о различиях в файлах (может быть, это по-прежнему будет для крайних различий. Раздражающие мелкие вещи, такие как пробелы, снова объединяются, если вы не используете -s recursive -X ignore-all-space )

Ответ 1800 Ответ полностью верен. Однако, как git noob, «использовать git cherry-pick» было недостаточно для того, чтобы понять это без лишних поисков в Интернете, поэтому я решил опубликовать более подробное руководство, если кто-то еще подобная лодка.

В моем случае было желание выборочно вытащить изменения из чужой ветки github в мою собственную. Если у вас уже есть локальная ветка с изменениями, вам нужно выполнить шаги 2 и 5-7.

  1. Создайте (если не создан) локальный ветвь с изменениями, которые вы хотите внести.

    $ git branch mybranch

  2. Включите его.

    $ git checkout mybranch

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

    $ git remote add repos-w-changes

  4. Вытащите все из своей ветки.

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

  5. Просмотрите журналы фиксации, чтобы узнать, какие изменения вы хотите:

    $ git log

  6. Вернитесь к ветке, в которую хотите внести изменения.

    $ git checkout originalbranch

  7. Черри выбирает свои коммиты, один за другим, с помощью hashей.

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

Кончик шляпы: http://www.sourcemage.org/Git_Guide

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

 git checkout master git checkout feature1 Myclass.java 

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

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

Шаг первый: Разверните ветви

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

полезные примечания по вариантам

Вы можете использовать * в качестве шаблона в шаблоне include.

Слэши не нужно избегать.

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

Параметр -p1 представляет собой задержку с помощью команды * unix patch и тот факт, что содержимое файла патча добавляет каждое имя файла с a/ или b/ (или больше в зависимости от того, как был создан файл патча), который вам нужно снять, чтобы он может определить реальный файл на пути к файлу, к которому должен применяться патч.

Посмотрите справочную страницу git-apply для получения дополнительных параметров.

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

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

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

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

 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» должно быть несколько разных ветвей.

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

 #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 

Я нашел этот пост самым простым ответом. Просто выполните:

 $ #git checkout   

Пример:

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

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

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

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

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

 git status 

вы увидите, что файл уже поставлен …

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

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

Просто.

Странно, что у git до сих пор нет такого удобного инструмента «из коробки». Я использую его сильно, обновляя ветку старой версии (которая по-прежнему имеет много пользователей программного обеспечения) только с некоторыми исправлениями из ветви текущей версии. В этом случае часто требуется быстро получить только некоторые строки кода из файла в trunk, игнорируя множество других изменений (которые не должны входить в старую версию) … И, конечно, интерактивное трехстороннее слияние необходимо в этом случае, git checkout --patch не используется для этой цели селективного слияния.

Вы можете сделать это легко:

Просто добавьте эту строку в раздел [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   

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

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

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

 #You are in the branch you want to merge to git checkout   

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

 git checkout -p  --  ... 

Это сочетание некоторых ответов.

Я бы сделал

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

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

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

Мне нравится ответ «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 

Что, как указано выше, приводит вас к интерактивному редактору, где вы выбираете строки «pick» для C1 и C2 (как указано выше). Сохраните и закройте, а затем он продолжит переустановку и даст вам ветку «temp», а также HEAD в master + C1 + C2:

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

Затем вы можете просто обновить мастер до HEAD и удалить ветвь temp, и вам хорошо идти:

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

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

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

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

git difftoll ..

Простой подход для выборочного слияния / фиксации по файлу:

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

  • Объединить DLL в EXE?
  • Как объединить два репозитория Git?
  • XSLT: простой способ объединить XML-файлы
  • Любой достойный текстовый механизм diff / merge для .NET?
  • WinMerge и Diffmerge
  • Встраивание DLL в скомпилированный исполняемый файл
  • Как объединить два столбца Excel в один?
  • Объединение нескольких PDF-файлов с помощью iTextSharp в c # .net
  • Есть ли свободные инструменты Xml Diff / Merge?
  • Interesting Posts

    Где я могу создать и использовать ScheduledThreadPoolExecutor, TimerTask или Handler?

    Только чтение ограниченного количества столбцов

    Как я могу сделать сборку Visual Studio очень многословной?

    Как туннель Windows Remote Dektop через две машины

    Как отключить режим частного просмотра / инкогнито в браузере X?

    Как сделать задержку в Java?

    Как сжать основную папку Windows?

    Утилита командной строки Linux для определения битрейта mp3?

    Как динамически добавлять предложения в autocompletetextview с сохранением статуса символа

    Подключение к общей папке Vista из Windows 7

    Как поместить отдельные метки для участка рассеяния

    Разница между require, include, require_once и include_once?

    Как я могу получить нумерацию страниц Word для начала на третьей странице, чтобы она отображалась в TOC как страница 1

    Bypass GeneratedValue в спящем режиме (данные объединить не в db?)

    Импорт данных из файла JSON в R

    Давайте будем гением компьютера.