Конфликт git-subtree при извлечении из центрального репо

У меня есть несколько проектов, которые зависят от одной и той же библиотеки, для которых я хотел бы поддерживать отдельный репозиторий git для управления с помощью git-subtree в каждом проекте. Так, например, в каждом проекте я могу сделать:

project1$  git subtree add --prefix=lib1 /path/to/lib1.git master
project2$  git subtree add --prefix=lib1 /path/to/lib1.git master

Теперь, в ходе работы над проектом1, я вношу некоторые изменения в lib1, скажем, lib1/file1.c, и отправляю это обратно в центральное хранилище:

project1$  git add lib1/file1.c
project1$  git commit -m "updates to lib1"
project1$  git subtree push --prefix=lib1 /path/to/lib1.git master

Все идет нормально. Но теперь я хотел бы обновить копию lib1 проекта2. Итак, я пытаюсь:

project2$  git subtree pull --prefix=lib1 /path/to/lib1.git master
Auto-merging lib1/file1.c
CONFLICT (content): Merge conflict in lib1/file1.c
Automatic merge failed; fix conflicts and then commit the result.

В чем дело? Я точно знаю, что ни в один из файлов lib1 в проекте2 не было внесено никаких изменений, так почему здесь должен быть конфликт?

Конфликты полупустые, как и те, о которых сообщается в этот вопрос. Все загружается/проталкивается в одной системе (OS X), поэтому я знаю, что нет проблем с окончаниями строк, как это предлагается там.

Конечно, это распространенный вариант использования git-subtree, и у него есть простой ответ, которого я просто не вижу. Пожалуйста помоги!

EDIT: я нашел неудовлетворительный обходной путь: сразу после отправки изменений в поддерево мне нужно повторно запустить извлечение поддерева:

project1$  git subtree push --prefix=lib1 /path/to/lib1.git master
project1$  git subtree pull --prefix=lib1 /path/to/lib1.git master

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

Итак, теперь мой вопрос: почему это работает? Есть ли ошибка в том, как git-subtree отслеживает push-уведомления, или я что-то упустил?


person user2509951    schedule 21.06.2013    source источник
comment
Тем не менее, я бы посмотрел на шестнадцатеричный дамп затронутых строк. CR мог как-то проникнуть, например. путем вставки текста из какого-либо другого источника.   -  person chirlu    schedule 21.06.2013
comment
Я проверял hexdump файла на каждом шаге, нигде нет CR (0d), только LF (0a). Так что это не проблема, хотя было хорошо проверить.   -  person user2509951    schedule 22.06.2013
comment
Может, конечно, какой-нибудь баг. В конце концов, поддерево Git относительно новое. Если вы можете придумать воспроизводимый тестовый пример, вы можете написать в список рассылки git.   -  person chirlu    schedule 22.06.2013
comment
Кто-нибудь когда-нибудь находил лучший обходной путь для этого или более новая версия git когда-либо исправляла это?   -  person dreid    schedule 03.02.2014


Ответы (4)


Что ж, похоже, это ошибка в git-subtree. Я оценил его для своих нужд и сдался. По сути, git-subtree push изменяет сообщения фиксации и, следовательно, SHA1 изменений фиксации. Вам нужно потянуть, чтобы объединить дополнительные коммиты, которые вносят точно такие же изменения, но имеют разные хэши SHA1 из-за измененных сообщений фиксации. GIT правильно обрабатывает двойные слияния (повторное слияние одних и тех же изменений), поэтому он молча отмечает слияние.

Кто-то должен это исправить!

person Borg    schedule 25.06.2013
comment
Это объясняет, почему система «тяни-толкай» работает именно так, спасибо. Разочаровывает тот факт, что поддерево не может автоматически справиться с таким простым сценарием. Есть ли другой способ сделать push, который позволит избежать изменения SHA1, или мы вернемся к использованию подмодуля? - person user2509951; 26.06.2013
comment
Вроде еще не исправили. У меня сейчас такая же проблема. - person zhekaus; 08.04.2016

Я действительно нашел правильный способ сделать это путем проб и ошибок.

После этой команды:

project1$  git subtree push --prefix=lib1 /path/to/lib1.git master

выполнить команду выборки:

project2$  git fetch /path/to/lib1.git master

а затем сделайте свою тягу:

project2$  git subtree pull --prefix=lib1 /path/to/lib1.git master
person dreid    schedule 03.02.2014
comment
Это должен быть принятый ответ. Именно то, что мне было нужно. - person hidefromkgb; 27.11.2017

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

Есть две вещи, которые вы можете сделать. В одном из других ответов предлагалось выполнить извлечение поддерева git сразу после нажатия. Это будет работать, но в итоге вы получите две копии каждого коммита, потому что технически существует два набора изменений: исходные (автоматически сгенерированные с помощью git-subtree push/split) и те, что в вашем объединенном проекте, и вы слияние их вместе. Ярлыком для этого метода является опция --rejoin для разделения/передачи, которая сразу же добавляет дополнительную фиксацию слияния.

Второй вариант — использовать --squash. Это также создает новые коммиты слияния, но, поскольку вы объединяете все изменения из вышестоящего репозитория как одну фиксацию, а не по одной на исходную фиксацию, это создает меньше беспорядка в вашей истории. Большинству людей больше нравится --squash.

person apenwarr    schedule 03.02.2014

Внутри вашего основного проекта старайтесь всегда тянуть и нажимать с помощью команд сквоша:

шаг 1: вытягивание поддерева с помощью сквоша

 git subtree pull --prefix=mainProjectFolder/subtreeFolder http://bitbucket.org/repo.git master --squash

шаг 2: толчок поддерева с помощью сквоша

 git subtree push --prefix=mainProjectFolder/subtreeFolder http://bitbucket.org/repo.git master --squash

флаг squash позволит избежать создания новых идентификаторов SHA1 для одного и того же коммита в разных репозиториях.

Объяснение. Чтобы решить эту проблему, вы можете принять соглашение об использовании флага сквоша при отправке и извлечении вашего поддерева. Проблема, описанная @Borg в отношении идентификаторов фиксации SHA1, верна, но на самом деле поддерево не было создано для этого. Вам следует избегать сохранения идентификатора фиксации репозитория поддерева (библиотеки) как в родительском проекте, так и в проекте поддерева. И если вы вообще отправляете изменения из родительского репозитория в репозиторий поддерева, следуйте этому утверждению из документации (строка 58): То есть, если вы вносите изменение, затрагивающее и библиотеку, и основное приложение, зафиксируйте его двумя частями.

Также это видео объясняет, когда не следует использовать поддеревья. Перетащите прямо к 11:00 минут, чтобы обнаружить, что поддеревья не являются правильным решением для вас, если:

у вас постоянные обновления репозитория,

или, если у вас много зависимостей,

или, если все в команде должны выучить поддеревья.

person ishanbakshi    schedule 13.07.2016