Git Объединить папку в репозиторий с использованием указанной папки предка - PullRequest
0 голосов
/ 09 мая 2020

У меня есть репозиторий git, назовите его Repo1:

Repo1
    Folder1
    Other stuff...

, и у меня есть две папки, которые содержат подмножество файлов в Repo1. Папка Baseline:

Baseline
    Folder1

... и папка ChangeSet:

ChangeSet
    Folder1

Baseline содержит файлы из Repo1, которые представляют общего предка любые файлы в ChangeSet.

Я хотел бы сделать 3-стороннее слияние изменений из ChangeSet в Repo1. Я рассмотрел создание временного репозитория, содержащего два коммита, первый для базовой линии, а второй для набора изменений, а затем слияние с --allow-unrelated-histories:

git merge <remote> --no-commit --allow-unrelated-histories

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

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

Заранее спасибо.

Изменить: из ответа ниже, Changeset, вероятно, был неправильным выбором слов для обновленных файлов папка. Наверное, лучше было бы Snapshot

Ответы [ 2 ]

1 голос
/ 09 мая 2020

Изменить: Вы на правильном пути в своем собственном ответе: выбор вишни почти наверняка является способом go для вашего фактического случая. Уловка состоит в том, чтобы поместить их исходное дерево в качестве «сиротской ветки» (независимая фиксация), а затем вставить их патч в качестве второй фиксации в этой ветке, а затем вернуть go в свою собственную ветку и использовать git cherry-pick. Выбор вишни внутренне реализован как полное трехстороннее слияние, при этом база слияния является родительским элементом фиксации, выбранной вишневой, а фиксация --theirs является фиксацией, которую вы называете.

Инструкции

В вашем исходном репозитории (или добавленном рабочем дереве для этого репозитория, если вы не хотите связываться с вашим основным рабочим деревом), выполните:

git checkout --orphan xxx         # use any name you like here
git read-tree -m -u 4b825dc642cb6eb9a060e54bf8d69288fbee4904

The ha sh ID здесь - это пустое дерево . Логически использование --empty должно работать здесь, но не работает. Или вместо дерева чтения используйте:

git rm -r .

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

Ваше рабочее дерево теперь должно быть пустым, а git status скажет:

On branch xxx

No commits yet

nothing to commit (create/copy files and use "git add" to track)

Если ваше рабочее дерево не пусто, оно ранее содержало неотслеживаемые файлы и все еще содержит. Вы должны переместить или удалить их (или, опять же, вы можете сделать все это в добавленном рабочем дереве).

Теперь сделайте то, что вы предложили в своем собственном ответе:

# copy my Baseline folder changes in
git add .
git commit -m "baseline"

( примечание: не используйте git commit -a; он не делает того, что вы хотите).

Я понял, что «набор изменений» означает «различие, которое вы примените», а не «новый набор файлов. ". Набор изменений - неправильное слово для описания нового снимка, но если это новый снимок, пора снова очистить дерево работы:

git rm -r .

, чтобы использовать версию, которую проще ввести. Затем снова почти прямо из вашего собственного ответа:

# copy my ChangeSet folder changes in
git add .
git commit -m "code"

Теперь вы можете git checkout master и git cherry-pick xxx. Замените любое имя ветки, которое вы используете для хранения двух коммитов.

[Исходный ответ ниже.]


Я бы хотел выполнить 3-стороннее слияние изменений с ChangeSet на Repo1. Я рассмотрел возможность создания временного репозитория, содержащего два коммита,

У вас хотя бы один недостаток. Слияние имеет три входов, а не два:

первый для базовой линии и второй для набора изменений, а затем слияние с --allow-unrelated-stories:

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

Два других, которые вам понадобятся:

  • один с базовым уровнем. -plus-changes: это их код или --theirs сторона слияния и
  • с вашим кодом: это сторона --ours слияния. Это коммит, который вы получите как HEAD в результате выполнения git checkout.

Оба этих коммита исторически должны происходить от базового уровня. Таким образом, Git может сравнить снимок базы слияния - в данном случае базовый уровень - с каждым из двух снимков кончика ветки : вашего кода и их-code-as- изменен-их-набором изменений.

Следовательно:

# create initial commit in initial repository:
git init         # create new empty repository
...              # copy baseline into place
git add .
git commit

# add their changeset as a new commit on a branch:
git checkout -b theirs
... apply the changeset, perhaps with "git apply" ...
git add -u       # or git add . again, or similar
git commit

# add your version of the code as a new commit on master:
git checkout master
... copy your code into place ...
git add .        # or similar
git commit

Теперь вы можете запустить git merge theirs. Три входных параметра - это базовая фиксация слияния, текущая фиксация - вершина master, также известная как HEAD - и фиксация, которую вы называете: окончательная фиксация ветки theirs.

* Команда 1090 * самостоятельно находит базовый коммит слияния. В данном случае это базовые файлы в начальной фиксации. Команда git merge теперь создает два набора изменений:

  • baseline vs HEAD: это то, что вы изменили;
  • baseline vs theirs: this это то, что они изменили.

Обратите внимание, что это второе сравнение дает набор изменений, который вы использовали для создания theirs коммита и его снимка. Это может показаться напрасной тратой усилий - почему бы просто не передать Git набор изменений напрямую? - но ведь именно так построен Git: Git действительно нужен этот снимок, поэтому вы должны его создать.

Что делать, если у вас уже есть репозиторий, и вы хотите выполнять работу прямо там?

В этом случае вы в некоторой степени связаны (в смысле «проблематично c ситуация »). Git находит базу слияния самостоятельно. Вы не можете просто сказать Git: выполнить слияние, делая вид, что фиксация C является базой слияния для некоторой произвольной фиксации C. 1

Один вариант - переписать весь ваш репозиторий в структуру, которая позволяет это. Как правило, это плохая идея, если вы действительно не хотите переключиться на новую историю, отбросив также все клоны.

Другой вариант - создать второй репозиторий или независимый подграф в вашем репозитории. Это отлично работает: используйте git checkout --orphan и git read-tree --empty -u, чтобы получить чистый лист для новых отключенных ветвей (конечно, не называйте основной master). Затем вы можете ie новую фиксацию слияния в исходную историю в вашем основном графике. Это немного сложно.

Третий - использовать git replace для вставки родительского графа, чтобы в вашем репозитории появилась новая root фиксация. Это тоже немного сложно. Он эквивалентен второму методу, за исключением того, что он оставляет меньше следов того, как вы это сделали: независимо от того, оставляете ли вы заменяющую фиксацию на месте или нет, она не копируется при операциях клонирования, поэтому другие пытаются выяснить, как вы сделали то, что вы сделали , вероятно, будет озадачен.

Последний вариант - тот, который вы описали сами:

... Я мог бы использовать git-merge-file для слияния любых небинарных файлов, которые могут существовать во всех трех местах, а затем обработка всех двоичных конфликтов, добавление, удаление файлов и т. д. c. я сам ...

Этот метод также отлично работает, и вы можете автоматизировать многие из них с помощью сценария; это все еще немного болезненнее, чем сделать это Git.


1 На самом деле вы можете сделать это, используя git merge-recursive. Однако эта команда не предназначена для запуска пользователем. Нет документации, объясняющей , как его запускать, и аргументы сложны: некоторые из них предоставляются как переменные среды! Не делай этого так.

0 голосов
/ 10 мая 2020

Я еще не написал весь сценарий, но вот что, я думаю, сработает.

Во-первых, сделайте то, что я сказал здесь:

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

Итак, я создам временный репозиторий, содержащий мою фиксацию для базового уровня, например:

Тогда мне просто нужно выбрать его в свой Repo1 репозиторий, например this :

Исходный ответ с использованием патча - который может быть не идеальным:

git --git-dir='path\to\Temp\.git' format-patch -1 --stdout HEAD | git apply --3way

Обновленный ответ с использованием прямой выборки:

git fetch 'path\to\Temp'
git cherry-pick -n FETCH_HEAD
...