Подтвердить что ты не робот

Git -subtree не сохраняет историю, поэтому я не могу подталкивать изменения поддерева, как я могу исправить это/избежать этой проблемы в будущем?

Я использовал расширение git -subtree (https://github.com/apenwarr/git-subtree) для управления подпроектами в рамках нашего основного проекта. Это делает именно то, что я хочу, кроме того, что он терпит неудачу, когда я пытаюсь разделить изменения, внесенные в подпроект из нашего основного проекта.

например. ранее я сделал

git subtree add -P Some/Sub/Dir --squash [email protected]:lib.git master

чтобы внести в код библиотеки код Some/Sub/Dir в нашем основном проекте. Все здесь отлично поработало, поэтому я переместил свои изменения в наш основной основной проект с открытым репозиторией git. Затем я решил внести изменения в свою локальную версию lib в Some/Sub/Dir, зафиксировать ее, а затем разбить ее, чтобы вернуть ее обратно в lib.git repo

git subtree split -P Some/Sub/Dir -b some_branch

все работает так, как ожидалось. Мне больше не нужна локальная копия репо, я удалил ее.

После клонирования новой копии репо из нашего центрального репо я внесла некоторые изменения в lib в Some/Sub/Dir и решил, что хочу разбить эти изменения и отбросить их обратно в репозиторий lib.git. Я пытаюсь использовать ту же команду разделения разделов, что и раньше, но на этот раз я получаю следующий вывод:

1/      3 (0)
2/      3 (1)
3/      3 (1)
fatal: bad object d76a03f0ec7e20724bcfa253e6a03683211a7bb1

d76a03f0ec7e20724bcfa253e6a03683211a7bb1 происходит, когда я добавил поддерево:

commit 43b3eb7d69d5eb64241eddb12e5bd74fd0215083
Author: Ian Bond <[email protected]>
Date:   Fri Apr 22 15:06:50 2011 -0400

    Squashed 'Subtree/librepoLib/' content from commit d76a03f

    git-subtree-dir: Subtree/librepoLib
    git-subtree-split: d76a03f0ec7e20724bcfa253e6a03683211a7bb1

который фактически ссылается на фиксацию в репозитории lib.git.


То, что я смог собрать вместе (и я git noob, поэтому я могу ошибаться, что-то игнорировать или использовать неправильную терминологию здесь), заключается в том, что 'git subtree add --squash' введет всю историю с удаленного lib.git repo в текущее репо, отбросит его в отдельную фиксацию, а затем добавит эту фиксацию в рабочую ветвь. История фиксации lib.git остается в текущем репо, однако они оборваны, потому что на самом деле они не упоминаются иначе, чем через текст компиляции сквоша. Пока эти болтающиеся фиксации остаются, git -subtree может использовать их для выполнения расщеплений, однако, поскольку push или pull не содержат оборванных объектов (или если я запускаю gc и полностью обрезаю оборванные объекты), эти оборванные коммиты потерянный и git -subtree больше не имеет необходимой информации для выполнения разделения.

Я добавил a script, который полностью воспроизведет проблемы, которые у меня были.


Мои вопросы:

1). Что я могу сделать, чтобы справиться с существующей ситуацией, когда у меня теперь есть поддеревья, которые я хочу объединить обратно в исходное репо, но больше не имеют никакой истории, которая связывает их вместе. Моя нынешняя мысль - сделать что-то вроде:

git subtree split -P Some/Sub/Dir 43b3eb7^.. --ignore-joins -b splitBranch

чтобы разделить всю историю, так как "git поддерево добавить" и объединить его обратно в исходное репо (которое, к счастью, не изменилось с момента добавления). Это лучший способ пойти? Любые рекомендации о том, как я должен выполнить слияние?

2) Можно ли что-нибудь сделать, чтобы git -subtree работала так, как ожидалось? Я считаю, что если я опускаю параметр -squash на "git subtree add", тогда все будет работать, однако это приводит к тому, что в мое репо будет вложено множество несвязанной истории. Есть ли способ сохранить необходимые коммиты (желательно, не сохраняя всю историю библиотеки)?

4b9b3361

Ответ 1

Цель git subtree split - создать некоторые новые коммиты (представляющие "локальные" изменения, первоначально созданные в локальном каталоге поддеревьев) поверх исходной истории поддеревьев. Поскольку он непосредственно включает исходную историю поддеревьев (в качестве родительского коммита первого перезаписанного локального коммита, который касается поддерева), операция разделения не может быть выполнена без наличия исходной истории поддеревья.

Подумайте, что вы будете делать с историей, создаваемой git subtree split. Вероятно, вы захотите нажать его в хранилище, где вы можете объединить его в остальную часть истории "вверх по течению". Чтобы эта операция слияния имела смысл, история разбиения должна основываться на самой оригинальной истории 1.

Вероятно, самый надежный способ упорядочить для пользователей исходную историю поддеревья - опубликовать URL-адрес для репозитория восходящего потока поддеревьев в вашей документации и дать им определение удаленного для него (совершенно нормально иметь "несвязанные" пульты в одном хранилище). Например.

Если вам нужно работать с "восходящим" тегом Some/Sub/Dir (чтобы вытащить внешние изменения или вытеснить локальные изменения), пожалуйста, определите и обновите удаленный файл для репозитория библиотек перед использованием git subtree:

git remote add lib [email protected]:the-lib-repository &&
git fetch lib

Вам нужно было бы сделать что-то подобное, даже если вы не использовали --squash, так как пользователям нужно было знать, где можно получить новые восходящие коммиты (и где (в конечном счете) для ввода новых сплит-сгенерированных коммитов).

Использование --squash дает вам "чистую" историю в вашем основном проекте и означает, что только те пользователи, которые должны иметь дело с поддеревьями "вверх по течению", фактически должны иметь свои объекты в своих репозиториях.


Похоже, вы хорошо понимаете объектную модель. Вы правы, что история, в которую втягивается git subtree add --squash, станет болтающейся 2 но что git subtree split может использовать ее до тех пор, пока она не будет удалена.

(со ссылкой на ваше воспроизведение script)
Вы можете успешно разделить ваш repoMainClone только потому, что локальные клоны автоматически фиксируют (или копируют) все файлы в .git/objects/ (таким образом, получая доступ к repoMain s копиям висячего (или почти висящего 2) из repoLib) вместо обычного транспорта "пакетного протокола" (который ограничивал бы перенесенные объекты только теми, которые необходимы для переданных ссылок, т.е. ничего не пропускает из repoLib). Ваш repoMainPull является фактически эквивалентным клонированием file://"$(pwd)"/repoMain repoMainCloneFile (URL file:// заставляет локальные клоны использовать переносы на основе пакетов вместо того, чтобы просто связывать/копировать все).


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

Ваш предложенный git subtree split -P Some/Sub/Dir 43b3eb7^.. --ignore-joins … (где 43b3eb7 является синтетическим фиксатором, результатом которого является git subtree add --squash …), создаст несвязанную историю (за исключением того, что он должен быть 43b3eb7.., поскольку 43b3eb7^ означает "первый родительский элемент 43b3eb7" и 43b3eb7 не имеет родителей). Я не уверен, что git subtree split был разработан таким образом, чтобы использовать такие диапазоны. Документация для git subtree split просто говорит <commit>, но никогда не упоминает ее цели. Чтение кода показывает, что оно по умолчанию относится к HEAD, что может указывать на то, что он предназначен для одного фиксажа, определяющего "подсказку" истории, которая должна обрабатываться для разделения. Кроме того, включение вывода отладки показывает сообщение incorrect order:, которое может указывать на то, что использование аргумента диапазона приводит к тому, что операция split в непредвиденной ситуации (ожидается, что обработала всех родителей фиксации перед обработкой самого коммита, но диапазон гарантирует, что 43b3eb7 (который является родителем слияния с поддеревом) никогда не обрабатывается). Я думаю, вы можете просто использовать --ignore-splits и оставить свой диапазон, если хотите генерировать "несвязанную" историю и попытаться использовать ее каким-то образом: git subtree split -P Some/Sub/Dir --ignore-joins ….

2 Они фактически не свисают сразу после git subtree add --squash, потому что они по-прежнему ссылаются на FETCH_HEAD. Однако, как только несвязанная выборка сделана, они станут действительно свисающими.

Ответ 2

@Ответ Криса Джонсена очень прав, он объясняет, почему сплит работает в клоне, а не в ситуации натяжения.

Для работы, заданной в вопросе и объясненной в сноске 2 ответа @Chris Johnsen, я могу подтвердить, что git subtree split -P Some/Sub/Dir -b splitBranch --ignore-joins и git subtree split -P Some/Sub/Dir -b splitBranch 43b3eb7.. производятся на основании того же фиксации и той же ветки, которые могут отражать изменения, сделанные в локальное репо, но не может быть перенесено в исходное репо repoLib, потому что у них нет общего accesstor, хотя git diff показывает d76a03f0ec7e2 и 43b3eb7d69d одинаковые.

Итак, для того, чтобы заставить поддерево push работать в ситуации pull, исходное repoLib-репозиционирование repoLib должно быть добавлено и извлечено, чтобы получить d76a03f0ec7e2 exeited для создания ветки, имеющей общий accesstor с исходным repoLib.

Оригинальное воспроизведение script не может выполняться плавно под linux, вот новое: http://pastebin.com/3NAQKEz9