Поддерево в Git - поддерево вверх-к-дата, но не может продавить
Я использую поддерево Git с несколькими проектами, над которыми я работаю, чтобы поделиться некоторым базовым кодом между ними. Базовый код часто обновляется, и обновления могут происходить в любом из проектов, причем все они в конечном итоге обновляются.
я столкнулся с проблемой, когда git сообщает, что мое поддерево обновлено, но нажатие отклоняется. Например:
#! git subtree pull --prefix=public/shared project-shared master
From github.com:****
* branch master -> FETCH_HEAD
Already up-to-date.
Если я нажимаю, я должен получить сообщение, что нет ничего, чтобы нажать... Верно? Верно? : (
#! git subtree push --prefix=public/shared project-shared master
git push using: project-shared master
To git@github.com:***
! [rejected] 72a6157733c4e0bf22f72b443e4ad3be0bc555ce -> master (non-fast-forward)
error: failed to push some refs to 'git@github.com:***'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
что может быть причиной этого? Почему не нажимать?
10 ответов:
Я нашел ответ на этот комментарий блога https://coderwall.com/p/ssxp5q
Если вы столкнетесь с "обновления были отклонены, потому что кончик текущая ветка позади. Слияние удаленных изменений (например," git pull") " проблема, когда вы нажимаете (из-за какой бы ни была причина, esp завинчивается с историей git) тогда вам нужно будет вложить git команды, так что вы можете заставить на Heroku. например, учитывая приведенный выше пример:
git push heroku `git subtree split --prefix pythonapp master`:master --force
в windows вложенная команда не работает:
git push heroku `git subtree split --prefix pythonapp master`:master --force
вы можете просто запустить вложенный бит первый:
git subtree split --prefix pythonapp master
это будет (после большого количества чисел) возвращать токен, например
157a66d050d7a6188f243243264c765f18bc85fb956
используйте это в содержащей команде, например:
git push heroku 157a66d050d7a6188f243243264c765f18bc85fb956:master --force
использовать
--onto
флаг:# DOESN'T WORK: git subtree push --prefix=public/shared project-shared master --onto=project-shared/master
[EDIT: к сожалению
subtree push
не вперед--onto
в базовыйsplit
, так что операция должна быть выполнена в двух командах! Сделав это, я вижу, что мои команды идентичны тем, которые содержатся в одном из других ответов, но объяснение отличается, поэтому я все равно оставлю его здесь.]git push project-shared $(git subtree split --prefix=public/shared --onto=project-shared/master):master
или если вы не используете bash:
git subtree split --prefix=public/shared --onto=project-shared/master # This will print an ID, say 0123456789abcdef0123456789abcdef, # which you then use as follows: git push project-shared 01234567:master
я потратил часы, изучая источник git-поддерева, чтобы понять это один, так что я надеюсь, что вы оцените его ;)
subtree push
начинается с бегаsubtree split
, который переписывает историю фиксации в формат, который должен быть готов к нажатию. То, как он это делает, это полосыpublic/shared/
С передней части любого пути, который имеет его, и удаляет любую информацию о файлах, которые его не имеют. это означает, что даже если вы вытаскиваете не раздавленные, все вышестоящие коммиты суб-репозитория игнорируются, поскольку они называют файлы своими голыми путями. (Commits это не касается никаких файлов подpublic/shared/
или коммиты слияния, идентичные родительским, также сворачиваются. [EDIT: кроме того, с тех пор я нашел некоторое обнаружение сквоша, поэтому теперь я думаю, что это только в том случае, если вы вытащили не раздавленный, а затем упрощенная фиксация слияния, описанная в еще одном ответе, позволяет выбрать не раздавленный путь и отбросить раздавленный путь.]) Результатом является то, что материал, который он пытается подтолкнуть, содержит любую работу, которую кто-то совершил в текущем репозитории хоста вы отталкиваетесь, но не работаете с людьми, связанными непосредственно с суб-репозиторием или через другой репозиторий хоста.однако, если вы используете
--onto
, затем все вышестоящие коммиты записываются как OK для использования дословно, поэтому, когда процесс перезаписи сталкивается с ними как с одним из родителей слияния, которое он хочет переписать, он будет держать их вместо того, чтобы пытаться переписать их обычным способом.
для приложения типа "GitHub pages", где вы развертываете поддерево" dist " в ветвь gh-pages, решение может выглядеть примерно так
git push origin `git subtree split --prefix dist master`:gh-pages --force
я упоминаю об этом, так как он немного отличается от примеров heroku, приведенных выше. Вы можете видеть, что моя папка" dist " существует в главной ветви моего РЕПО, а затем я нажимаю ее как поддерево на ветку gh-pages, которая также находится в origin.
я сталкивался с этой проблемой и раньше, и вот как я ее решил.
я узнал, что у меня была ветвь, которая не была прикреплена к местной главной ветви. Эта ветвь существует, и она просто висит в пустоте. В вашем случае, его, вероятно, называют
project-shared
. Если это так и когда вы делаетеgit branch
вы можете увидеть местнуюproject-shared
филиал, то вы можете 'добавить' новые коммиты к существующейproject-shared
ветку делать а:
git subtree split --prefix=public/shared --onto public-shared --branch public-shared
как я понял это
git subtree
начнет создавать новую ветку от--onto
в этом случае его локальногоpublic-shared
филиала. Тогда ветка означает создание ветки, которая просто заменяет старуюpublic-shared
филиала.это сохранит все предыдущие SHA из
public-shared
филиала. Наконец, вы можете сделать
git push project-shared project-shared:master
при условии, что у вас есть
project-shared
удаленный, а также; это будет толкать локальный висит в пустотеproject-shared
филиала кmaster
отделение дистанционногоproject-shared
.
это из-за ограничения оригинального алгоритма. При обработке коммитов слияния исходный алгоритм использует упрощенные критерии для отсечения несвязанных родителей. В частности, он проверяет, есть ли родитель, который имеет такое же дерево. Если такой родитель найден, он свернет фиксацию слияния и вместо этого использует родительскую фиксацию, предполагая, что другие родители имеют изменения, не связанные с поддеревом. В некоторых случаях это приведет к отбрасыванию части истории, которая имеет фактическое значение изменения в поддереве. В частности, он будет отбрасывать последовательности коммитов, которые будут касаться поддерева, но приведут к тому же значению поддерева.
давайте посмотрим пример (который вы можете легко воспроизвести), чтобы лучше понять, как это работает. Рассмотрим следующую историю (формат строки: commit [tree] subject):
% git log --graph --decorate --pretty=oneline --pretty="%h [%t] %s" * E [z] Merge branch 'master' into side-branch |\ | * D [z] add dir/file2.txt * | C [y] Revert "change dir/file1.txt" * | B [x] change dir/file1.txt |/ * A [w] add dir/file1.txt
в этом примере мы разбиваем на
dir
. КоммитыD
иE
у одного и того же дереваz
, потому что у нас есть commitC
, раскрывшие совершенияB
, так чтоB-C
последовательность ничего не делает дляdir
даже если это изменения к нему.теперь давайте сделаем разделение. Сначала мы разделились на совершение
C
.% git log `git subtree split -P dir C` ... * C' [y'] Revert "change dir/file1.txt" * B' [x'] change dir/file1.txt * A' [w'] add dir/file1.txt
Далее мы разделимся на commit
E
.% git log `git subtree split -P dir E` ... * D' [z'] add dir/file2.txt * A' [w'] add dir/file1.txt
Да, мы потеряли два коммита. Это приводит к ошибке при попытке нажать второй раскол, так как у него нет тех двух коммитов, которые уже попали в начало координат.
обычно вы можете терпеть это ошибка при использовании
push --force
, так как отброшенные коммиты обычно не будут иметь критической информации в них. В долгосрочной перспективе ошибка должна быть исправлена, поэтому история разделения фактически будет иметь все коммиты, которые касаютсяdir
, как ожидалось. Я ожидал бы, что исправление будет включать более глубокий анализ родительских коммитов для скрытых зависимостей.для справки, вот часть исходного кода, ответственная за поведение.
copy_or_skip() ... for parent in $newparents; do ptree=$(toptree_for_commit $parent) || exit $? [ -z "$ptree" ] && continue if [ "$ptree" = "$tree" ]; then # an identical parent could be used in place of this rev. identical="$parent" else nonidentical="$parent" fi ... if [ -n "$identical" ]; then echo $identical else copy_commit $rev $tree "$p" || exit $? fi
ответ Эрика Вудраффа не помог мне, но следующее:
Я обычно "git subtree pull" с опцией "--squash". Кажется, это действительно усложнило примирение, поэтому мне нужно было сделать подтяжку поддерева без раздавливания на этот раз, разрешить некоторые конфликты, а затем нажать.
Я должен добавить, что раздавленная тяга не выявила никаких конфликтов, сказала мне, что все в порядке.
другое [более простое] решение состоит в том, чтобы продвинуть голову пульта дистанционного управления, сделав еще одну фиксацию, если вы можете. После того, как вы вытащите эту продвинутую голову в локальное поддерево, вы сможете снова оттолкнуться от нее.