Почему я не могу отправить это обновленное поддерево 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 [email protected]:***
! [rejected]        72a6157733c4e0bf22f72b443e4ad3be0bc555ce -> master (non-fast-forward)
error: failed to push some refs to '[email protected]:***'
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.

Что может быть причиной этого? Почему толчок не работает?


person mateusz    schedule 07.12.2012    source источник
comment
почему бы вам не сделать то, что говорит вам git: git pull ?   -  person AlexWien    schedule 07.12.2012
comment
Git pull не имеет ничего общего с моим вопросом. Но да, git pull дает мне актуальное сообщение, так как в исходном репозитории нет ничего нового.   -  person mateusz    schedule 07.12.2012
comment
Похоже, что ветвь, на которой вы находитесь, находится позади основной HEAD - например, история ... -> A -> B -> C, где удаленная главная HEAD находится в C, но ваша текущая ветвь находится в A (или некоторый потомок A, не связанный с C). Я бы посмотрел на свою историю git, чтобы быть уверенным.   -  person Mark Leighton Fisher    schedule 07.12.2012
comment
У меня такая же проблема. Я использовал --rejoin для ускорения отправки поддеревьев. Когда я прохожу через нажатие поддерева вручную, запуская subtree split --rejoin, ветвь разделенного поддерева имеет историю только до последнего повторного соединения, тогда как обычно она содержит всю историю поддерева. Для меня это непосредственная причина ошибки без быстрой перемотки вперед. Я все еще не уверен, почему разделение поддерева создает усеченную историю.   -  person hurrymaplelad    schedule 12.12.2014


Ответы (12)


Я нашел ответ в этом комментарии в блоге https://coderwall.com/p/ssxp5q.

Если вы столкнулись с проблемой «Обновления были отклонены, потому что кончик вашей текущей ветки отстал. Слейте удаленные изменения (например,« git pull »)» при нажатии (по какой-либо причине, например, с git history) тогда вам нужно будет вложить команды git, чтобы вы могли принудительно нажать на героку. например, учитывая приведенный выше пример:

git push heroku `git subtree split --prefix pythonapp master`:master --force
person Eric Woodruff    schedule 25.03.2013
comment
Мне не нужно --force. гит 1.8.2.3. Я думаю, что сейчас есть некоторые ошибки вокруг поддерева; в исходной ошибке говорилось, что мне нужно push -f, что для меня не имело смысла, - person Dustin Getz; 13.08.2013
comment
Как запустить эту команду в Windows? Я получаю error: unknown option 'prefix' - person pilau; 09.10.2013
comment
Удивительно, это просто спасло мой день :) - person bpipat; 17.03.2014
comment
Несмотря на то, что он обеспечивает обходной путь, он не объясняет поведение. - person klimkin; 06.02.2015
comment
Не приведет ли это решение (с --force) к потере изменений других людей (которые они внесли ранее)? Не только недавние, но и все другие изменения (из любого другого репозитория хоста) до начала времени? Скажи мне, если я ошибаюсь в этом. Но см. мой ответ на stackoverflow.com/a/30715198/1783475 для более безопасного решения с использованием --onto. - person entheh; 09.06.2015
comment
Верно, но, вероятно, большинство применений поддерева таким образом — это односторонний толчок к героку. Как правило, heroku не считается де-факто репозиторием git, из которого несколько пользователей могут отправлять и получать данные. - person Eric Woodruff; 09.06.2015
comment
@klimkin Мне понравился этот ответ, потому что он не был полон сложных объяснений, от которых у меня кружилась голова. Просто сработало :) - person GreenAsJade; 04.03.2016
comment
Мне было трудно определить, какое отношение имеют heroku и pythonapp в ответе. Переводя это с языка, характерного для Heroku: git push <your subtree's origin> `git subtree split --prefix=Path/to/subtree master`:master --force - person aednichols; 14.04.2016
comment
По какой-то очень странной причине эта команда git push heroku git subtree split --prefix pythonapp master:master --force полностью удаляет ветвь поддерева, и я не знаю, почему - person Andrew Miracle; 03.05.2021

В 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
person Chris Jordan    schedule 06.03.2015
comment
Это гораздо более чистое решение. Работает на меня! - person Infinity; 19.04.2017
comment
Когда я запускаю git subtree split --prefix subtreedirectoryname master, я получаю фатальный неоднозначный аргумент «мастер»: неизвестная ревизия или путь не в рабочем дереве - person Demodave; 26.09.2018
comment
Это отличное решение, я просто продолжал получать git subtree не команду раньше. После использования вашего решения я смог развернуть папку на героку вместо всего моего репо. - person pakman198; 01.11.2019
comment
Отличное решение и экономия времени - person Nisharg Shah; 27.05.2020
comment
по какой-то очень странной причине эта команда удаляет мою ветку поддерева при втором нажатии - person Andrew Miracle; 03.05.2021

Используйте флаг --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, чтобы понять это, так что надеюсь, вы оцените его ;)

subtree push начинается с запуска subtree split, который переписывает вашу историю коммитов в формат, который должен быть готов к отправке. Это делается следующим образом: он убирает public/shared/ с начала любого пути, в котором он есть, и удаляет всю информацию о файлах, в которых его нет. Это означает, что даже если вы извлекаете не сжатые файлы, все коммиты восходящего подрепозитория игнорируются, поскольку они именуют файлы по их голым путям. (Коммиты, которые не касаются файлов под public/shared/, или сливаются коммиты, идентичные родителю, также сворачиваются.[EDIT: Кроме того, с тех пор я нашел некоторое обнаружение сквоша, так что теперь я думаю, что это только в том случае, если вы вытащили не сквош, а затем упрощенное сворачивание коммита слияния, описанное в еще одному ответу удается выбрать не сжатый путь и отбросить сжатый путь.]) В результате материал, который он пытается отправить, в конечном итоге содержит любую работу, которую кто-то передал в текущий репозиторий хоста, из которого вы выталкиваете, но не работает люди, переданные непосредственно в подрепозиторий или через другой хост-репозиторий.

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

person entheh    schedule 08.06.2015
comment
Я рекомендую этот ответ как тот, который уже дважды помог мне. А описание — это то, что мне было нужно, чтобы понять, как работает разделение и перемещение в нужное место. Спасибо. - person Kamil Wozniak; 03.02.2016
comment
Что такое совместное использование проекта? - person Colin D; 28.03.2016
comment
@ColinD project-shared — это имя пульта. Это имя вы выберете при настройке места для извлечения и отправки. Вы можете получить список пультов с помощью git remote -v. - person entheh; 30.03.2016
comment
@entheh часы, которые вы потратили на это, просто сделали мой день. Спасибо! - person jun; 21.04.2016
comment
Я обнаружил, что эта ошибка всегда возникает, когда в вашем подрепозитории есть каталог, имя которого совпадает с префиксом, например. вы добавляете подрепозиторий как libxxx в основной репозиторий, а сам ваш подрепозиторий также имеет подкаталог с именем libxxx. В этом случае git будет неправильно обрабатывать коммиты в подрепозитории как коммиты в основном репозитории и пытаться их разделить. Параметр --onto помогает git идентифицировать коммиты, которые уже находятся в подрепозитории. - person Cosyn; 25.08.2016
comment
Смогут ли люди прийти в себя? Какие бы замечательные вещи ни делал Git, они немедленно компенсируются потерей производительности, заставляющей его делать то, что вы хотите. Кроме того, Git катастрофически не работает для компонентно-ориентированных проектов. Его конкуренты работают намного лучше. - person ATL_DEV; 24.09.2018

Для приложения типа «страницы GitHub», где вы развертываете поддерево «dist» в ветку gh-pages, решение может выглядеть примерно так:

git push origin `git subtree split --prefix dist master`:gh-pages --force

Я упоминаю об этом, так как он немного отличается от приведенных выше примеров героку. Вы можете видеть, что моя папка «dist» существует в основной ветке моего репозитория, а затем я помещаю ее как поддерево в ветку gh-pages, которая также находится в источнике.

person Colin D    schedule 23.06.2016

Я тоже сталкивался с этой проблемой раньше, и вот как я ее решил.

Я обнаружил, что у меня есть ветка, которая не была прикреплена к локальной главной ветке. Эта ветка существует и просто висит в пустоте. В вашем случае это, вероятно, называется 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.

person snw    schedule 08.08.2013

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

Давайте посмотрим на пример (который вы можете легко воспроизвести), чтобы лучше понять, как это работает. Рассмотрим следующую историю (формат строки: 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, потому что у нас есть коммит C, который отменяет коммит 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

Затем мы разделяем коммит 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
person klimkin    schedule 06.02.2015
comment
исправлена ​​ли ошибка по состоянию на 09.05.2020 (сб)? @климкин - person halfmoonhalf; 09.05.2020
comment
ошибка исправлена. см.: contrib/subtree: исправить ошибку разделения поддерева, пропущенного при слиянии. github.com/git/git/commit/ - person halfmoonhalf; 09.05.2020

Была и эта проблема. Вот что я сделал, основываясь на лучших ответах:

Данный:

#! git subtree push --prefix=public/shared project-shared master
git push using:  project-shared master
To [email protected]:***
! [rejected]        72a6157733c4e0bf22f72b443e4ad3be0bc555ce -> master (non-fast-forward)
error: failed to push some refs to '[email protected]:***'
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.

Введите это:

git push <origin> 72a6157733c4e0bf22f72b443e4ad3be0bc555ce:<branch> --force

Обратите внимание, что токен, который выводится 'git subtree push', используется в 'git push'.

person Amos    schedule 21.03.2019

Ответ Эрика Вудраффа мне не помог, но помогло следующее:

Обычно я использую git subtree pull с параметром --squash. Похоже, это усложняло согласование, поэтому мне нужно было выполнить извлечение поддерева без сжатия на этот раз, разрешить некоторые конфликты, а затем отправить.

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

person Zsolt Szatmari    schedule 20.03.2015

Другое [более простое] решение — продвинуть голову пульта, сделав еще один коммит, если сможете. После того, как вы перетащите эту расширенную голову в локальное поддерево, вы сможете снова оттолкнуть ее.

person 9swampy    schedule 27.05.2015
comment
Именно так я решил ту же проблему с удаленным репозиторием, не синхронизированным с локальным поддеревом. - person Aidas Bendoraitis; 26.01.2018

Вы можете принудительно отправить локальные изменения в репозиторий удаленного поддерева.

git push subtree_remote_address.git `git subtree split --prefix=subtree_folder`:refs/heads/branch --force
person FunkyKat    schedule 13.03.2017

Быстрый powershell для пользователей Windows на основе решения Криса Джордана

$id = git subtree split --prefix pythonapp master
Write-Host "Id is: $id"
Invoke-Expression "git push heroku $id`:master --force"
person Rtype    schedule 26.01.2018

Вот что я написал, основываясь на том, что сказал @entheh.

for /f "delims=" %%b in ('git subtree split --prefix [name-of-your-directory-on-the-file-system-you-setup] -b [name-of-your-subtree-branch]') do @set token=%%b
git push [alias-for-your-subtree] %token%:[name-of-your-subtree-branch] --force

pause
person Demodave    schedule 11.01.2019