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

Теперь, когда я работаю над project1, я вношу некоторые изменения в 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. Поэтому я стараюсь:

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 и имеет простой ответ, который я просто не вижу. Пожалуйста, помогите!

РЕДАКТИРОВАТЬ: Я нашел неудовлетворительный обходной путь: сразу после нажатия изменений в поддереве мне нужно повторно запустить поддерево pull:

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

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

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

Ответ 1

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

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

Ответ 2

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

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

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

выполните команду fetch:

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

а затем выполните следующие действия:

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

Ответ 3

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

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

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

Ответ 4

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

Шаг 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 commit id, верна, но поддерево для этого не было построено. Вы должны избегать того, чтобы зафиксировать Id в репозитории поддерева (библиотеки) в проекте родительского проекта и поддерева. И если вы вообще меняете изменения из родительского репозитория в репозиторий поддерева, следуйте этому утверждению из документации (строка 58): То есть, если вы внесете изменения, которые затрагивает как библиотеку, так и основное приложение, фиксирует ее в две части.

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

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

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

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