Чому я не можу просунути це сучасне піддерево Git?


88

Я використовую піддерево 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.

Що може бути причиною цього? Чому натискання не вдається?


чому ти не робиш те, що тобі каже git: git pull?
AlexWien

8
Git pull не має нічого спільного з моїм запитанням. Але так, git pull дає мені сучасне повідомлення, оскільки в репозиторії походження немає нічого нового.
mateusz

1
Здається, гілка, на якій ви перебуваєте, знаходиться за тим місцем, де знаходиться головна HEAD - наприклад, історія ... -> A -> B -> C, де віддалена головна HEAD знаходиться в C, а ваша поточна гілка знаходиться в A (або якийсь нащадок A, не зв’язаний із C). Я б подивився свою історію git, щоб бути впевненим.
Mark Leighton Fisher

У мене така сама проблема. Я використовував --rejoinдля пришвидшення натискань на піддерево. Коли я переходжу через натискання піддерева вручну, працюючи subtree split --rejoin, гілка розділеного піддерева має історію лише до останнього повторного приєднання, коли вона зазвичай містить всю історію піддерева. Для мене це безпосередня причина помилки, що не перемотується вперед. Я все ще не впевнений, чому розділення піддерева генерує усічену історію.
hurrymaplelad

Відповіді:


98

Відповідь я знайшов у цьому коментарі до блогу https://coderwall.com/p/ssxp5q

Якщо ви зіткнулися з проблемою "Оновлення було відхилено, оскільки кінчик вашої поточної гілки позаду. Об'єднайте віддалені зміни (наприклад," git pull ")", коли ви натискаєте (з будь-якої причини, особливо, перекручуючи історію git) тоді вам потрібно буде вкласти команди git, щоб ви могли змусити натиснути на heroku. наприклад, враховуючи наведений вище приклад:

git push heroku `git subtree split --prefix pythonapp master`:master --force

2
Допоміг мені! Дякую.
Майкл Форрест,

8
Як запустити цю команду в Windows? Я отримуюerror: unknown option 'prefix'
плов

3
Дивно, це щойно врятувало мені день :)
bpipat

2
Правда, але, мабуть, більшість випадків використання піддерева у такий спосіб є одностороннім натисканням на героку. Як правило, heroku не вважається дефакто git-репо для декількох користувачів, яких можна натискати та витягувати.
Ерік Вудрафф

11
Мені було важко визначити, які стосунки herokuі pythonappє у відповіді. Переклавши це з мови, специфічної для Heroku:git push <your subtree's origin> `git subtree split --prefix=Path/to/subtree master`:master --force
aednichols

39

У вікнах вкладена команда не працює:

git push heroku `git subtree split --prefix pythonapp master`:master --force

Ви можете просто запустити вкладений біт спочатку:

git subtree split --prefix pythonapp master

Це (після великої кількості цифр) поверне маркер, наприклад

157a66d050d7a6188f243243264c765f18bc85fb956

Використовуйте це в команді, що містить, наприклад:

git push heroku 157a66d050d7a6188f243243264c765f18bc85fb956:master --force

Це набагато чистіше рішення. Працює для мене!
Infinity

Коли я запускаю git subtree split --prefix subtreedirectoryname master, я отримую фатальний неоднозначний аргумент 'master': невідома редакція або шлях не в робочому дереві
Demodave

Це чудове рішення, я просто продовжував отримувати, git subtreeце не команда раніше. Після використання вашого рішення я зміг розгорнути папку на heroku замість усього мого репо
pakman198,

Чудове рішення та економія часу
Nisharg Shah

29

Використовуйте --ontoпрапор:

# DOESN'T WORK: git subtree push --prefix=public/shared project-shared master --onto=project-shared/master

[РЕДАКТУВАТИ: на жаль 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/, або об'єднані коміти, ідентичні батьківським, також згортаються. [РЕДАКТУВАТИ: Крім того, я з тих пір знайшов деяке виявлення сквошу, тому зараз я думаю, що це лише у тому випадку, якщо ви потягнули не розчавлене, а потім спрощене згортання коміту злиття, описане в черговій відповіді, встигає вибрати незминаний шлях і відкиньте зім'ятий шлях.]) Результат полягає в тому, що матеріал, який він намагається проштовхнути, закінчується будь-якою роботою, яку хтось присвятив поточному сховищу хоста, з якого ви натискаєте, але не працюючим людям, зафіксованим безпосередньо до підрепозиторію або через інший хост сховище.

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


2
Я рекомендую цю відповідь як ту, яка мені вже двічі допомогла. І опис - це те, що мені потрібно було зрозуміти, як працює розкол та відштовхування в потрібне місце. Дякую.
Каміль Возняк

2
Що таке "спільний проект"?
Колін Д

1
@ColinD "project-shared" - це назва пульта дистанційного керування. Це ім’я, яке ви вибрали, налаштовуючи місце для вибору та натискання. Ви можете отримати список пультів дистанційного керування за допомогою git remote -v.
entheh

@entheh години, які ви витратили на це, jsut зробили мені день. Дякую!
червень

1
Я виявив, що ця помилка трапляється завжди, коли у вашому довідковому сховищі є каталог, ім'я якого збігається з префіксом, наприклад, ви додаєте сховище підсистем як "libxxx" в головне сховище, а саме ваше сховище також має підкаталог з назвою "libxxx ". У цьому випадку git буде неправильно розглядати коміти в підрепозиторії як коміти в основному сховищі та намагатиметься розділити їх. --ontoОпція допомагає мерзотнику , щоб визначити , фіксації , які вже знаходяться в сховищі до південь.
Cosyn

14

Для програми типу "Сторінки GitHub", де ви розгортаєте піддерево "dist" у гілці gh-сторінок, рішення може виглядати приблизно так

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

Я згадую це, оскільки це виглядає дещо інакше, ніж наведені вище приклади героку. Ви можете бачити, що моя папка "dist" існує у головній гілці мого репо, а потім я підштовхую її як піддерево до гілки gh-сторінок, яка також є початковою.


3

Я також раніше стикався з цією проблемою, і ось як я її вирішив.

Я дізнався, що у мене є відділення, яке не приєднане до місцевого головного відділення. Ця гілка існує, і вона просто висить у порожнечі. У вашому випадку це, мабуть, називається project-shared. Припускаючи, що це так, і коли ви робите a, 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філії. Нарешті, ви можете зробити a

git push project-shared project-shared:master

Якщо припустити, що у вас також є project-sharedпульт; це приведе локальний висячий у порожній project-shared гілці до masterгілки пульта дистанційного керування project-shared.


3

Це пов’язано з обмеженням оригінального алгоритму. При обробці комітів злиття оригінальний алгоритм використовує спрощені критерії відключення непов’язаних батьків. Зокрема, він перевіряє, чи є батько, який має те саме дерево. Якщо такий батько знайдений, він буде згортати коміт злиття та використовувати замість нього батьківський коміт, припускаючи, що інші батьки мають зміни, не пов'язані з піддеревом. У деяких випадках це може призвести до скидання частин історії, яка фактично змінила піддерево. Зокрема, це скидає послідовності комітів, які торкаються піддерева, але призводять до того самого значення піддерева.

Давайте подивимось приклад (який ви можете легко відтворити), щоб краще зрозуміти, як це працює. Розглянемо таку історію (формат рядка: коміт [дерево] тема):

% 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

чи виправлена ​​помилка станом на 2020-05-09 (сб)? @klimkin
halfmoonhalf

виправлено помилку. див .: contrib / subtree: виправлена ​​помилка пропущеного злиття підрозділу піддерева. github.com/git/git/commit/…
halfmoonhalf

0

Відповідь Еріка Вудраффа мені не допомогла, але наступне:

Я зазвичай 'git subtree pull' з опцією '--squash'. Здається, це ускладнило ситуацію, щоб примирити ситуацію, тому мені потрібно було провести піддерево, не здавлюючи цього разу, вирішити деякі конфлікти, а потім натиснути.

Треба додати, що розчавлене потяг не виявило жодних конфліктів, сказав мені, що все в порядку.


0

Іншим [простішим] рішенням є просування голови пульта, здійснивши ще один коміт, якщо можете. Після того, як ви втягнете цю вдосконалену голову в місцеве піддерево, ви зможете натиснути з неї знову.


1
Саме так я вирішив ту саму проблему з віддаленим сховищем не синхронізовано з локальним піддеревом.
Айдас Бендорайтіс,

0

Ви можете змусити локальні зміни вдалити до віддаленого репозиторію піддерева

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

0

Швидкий PowerShell для користувачів Windows на основі рішення Кріса Джордана

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

0

Отже, це те, що я написав, спираючись на те, що сказав @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

0

Була і ця проблема. Ось що я зробив на основі основних відповідей:

Дано:

#! 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.

Введіть це:

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

Зверніть увагу, що маркер, який виводиться за допомогою "git subtree push", використовується в "git push".

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.