Поддерево в 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 71

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, потому что у нас есть commit 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

Далее мы разделимся на 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". Кажется, это действительно усложнило примирение, поэтому мне нужно было сделать подтяжку поддерева без раздавливания на этот раз, разрешить некоторые конфликты, а затем нажать.

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

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

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

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

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

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