Dlaczego nie mogę przekazać tego aktualnego poddrzewa Git?

88

Używam poddrzewa Git z kilkoma projektami, nad którymi pracuję, aby udostępnić między nimi kod podstawowy. Kod podstawowy jest często aktualizowany, a aktualizacje mogą mieć miejsce w każdym z projektów, ostatecznie wszystkie z nich zostaną zaktualizowane.

Napotkałem problem polegający na tym, że git zgłasza, że ​​moje poddrzewo jest aktualne, ale wypychanie zostaje odrzucone. Na przykład:

#! git subtree pull --prefix=public/shared project-shared master
From github.com:****
* branch            master     -> FETCH_HEAD
Already up-to-date.

Jeśli nacisnę, powinienem otrzymać wiadomość, że nie ma co naciskać ... Tak? DOBRZE? :(

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

Co mogłoby być tego przyczyną? Dlaczego pchanie zawodzi?

mateusz
źródło
dlaczego nie robisz tego, co mówi git: git pull?
AlexWien
8
Git pull nie ma nic wspólnego z moim pytaniem. Ale tak, git pull daje mi aktualną wiadomość, ponieważ w repozytorium pochodzenia nie ma nic nowego.
mateusz
1
Wygląda na to, że gałąź, na której się znajdujesz, znajduje się za miejscem, w którym znajduje się główny HEAD - na przykład historia ... -> A -> B -> C, gdzie zdalny master HEAD jest w C, ale twoja obecna gałąź jest w A (lub jakiś potomek A niepołączony z C). Dla pewności spojrzałbym na historię mojej git.
Mark Leighton Fisher
Mam ten sam problem. Używam --rejoindo przyspieszania wypychania poddrzewa. Kiedy ręcznie przechodzę przez wypychanie poddrzewa, działając subtree split --rejoin, podzielona gałąź poddrzewa ma tylko historię z powrotem do ostatniego połączenia, kiedy zwykle zawiera całą historię poddrzewa. Dla mnie jest to bezpośrednia przyczyna błędu braku przewijania do przodu. Nadal nie jestem pewien, dlaczego podział poddrzewa generuje skróconą historię.
hurrymaplelad,

Odpowiedzi:

98

Znalazłem odpowiedź w tym komentarzu na blogu https://coderwall.com/p/ssxp5q

Jeśli napotkasz problem „Aktualizacje zostały odrzucone, ponieważ końcówka Twojej bieżącej gałęzi jest opóźniona. Scal zmiany zdalne (np.„ Git pull ”)” podczas pchania (z jakiegoś powodu, esp pieprzenie się z historią git) wtedy będziesz musiał zagnieździć polecenia git, aby móc wymusić wypchnięcie na heroku. np. biorąc pod uwagę powyższy przykład:

git push heroku `git subtree split --prefix pythonapp master`:master --force
Eric Woodruff
źródło
2
Pomogli mi! Dzięki.
Michael Forrest
8
Jak uruchomić to polecenie w systemie Windows? Dostajęerror: unknown option 'prefix'
pilau
3
Niesamowite, to właśnie uratowało mi dzień :)
bpipat
2
To prawda, ale prawdopodobnie większość zastosowań poddrzewa w ten sposób jest jednokierunkowym popychaniem do heroku. Zasadniczo heroku nie jest uważane za defacto git repo, z którego może wypychać i wyciągać wielu użytkowników.
Eric Woodruff
11
Trudno mi było określić, jaki związek herokui jaki pythonappmam w odpowiedzi. Tłumaczenie tego z języka specyficznego dla git push <your subtree's origin> `git subtree split --prefix=Path/to/subtree master`:master --force
Heroku
39

W systemie Windows zagnieżdżone polecenie nie działa:

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

Możesz po prostu najpierw uruchomić zagnieżdżony bit:

git subtree split --prefix pythonapp master

To (po wielu liczbach) zwróci token, np

157a66d050d7a6188f243243264c765f18bc85fb956

Użyj tego w poleceniu zawierającym, np:

git push heroku 157a66d050d7a6188f243243264c765f18bc85fb956:master --force
Chris Jordan
źródło
To znacznie czystsze rozwiązanie. Pracuje dla mnie!
Infinity
Kiedy uruchamiam git subtree split --prefix subtreedirectoryname master, otrzymuję fatalny niejednoznaczny argument `` master '': nieznana wersja lub ścieżka nie znajduje się w drzewie roboczym
Demodave
To świetne rozwiązanie, po prostu otrzymałem wcześniej git subtreenie jest to polecenie. Po użyciu twojego rozwiązania udało mi się wdrożyć folder na heroku zamiast całego mojego repozytorium
pakman198
Świetne rozwiązanie i oszczędność czasu
Nisharg Shah
29

Użyj --ontoflagi:

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

[EDYCJA: niestety subtree pushnie przesyła dalej --ontodo bazowego split, więc operację należy wykonać w dwóch poleceniach! Po wykonaniu tej czynności widzę, że moje polecenia są identyczne z tymi w jednej z pozostałych odpowiedzi, ale wyjaśnienie jest inne, więc i tak zostawię je tutaj.]

git push project-shared $(git subtree split --prefix=public/shared --onto=project-shared/master):master

Lub jeśli nie używasz basha:

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

Spędziłem godziny, przeglądając źródło poddrzewa git, aby znaleźć to rozwiązanie, więc mam nadzieję, że to docenisz;)

subtree pushzaczyna się od uruchomienia subtree split, które przepisuje historię zmian do formatu, który powinien być gotowy do wysłania. Sposób, w jaki to robi, polega na odcięciu public/shared/początku każdej ścieżki, która go zawiera, i usunięciu wszelkich informacji o plikach, które go nie mają. Oznacza to, że nawet jeśli wyciągniesz niezgniecione, wszystkie zatwierdzenia z repozytorium wyższego szczebla zostaną zignorowane, ponieważ nazywają pliki według ich gołych ścieżek. (Zatwierdzenia, które nie dotykają żadnych plików wpublic/shared/lub zatwierdzenia scalające, które są identyczne z rodzicami, również są zwinięte. [EDYCJA: Poza tym, od tamtej pory znalazłem wykrywanie squasha, więc teraz myślę, że to tylko wtedy, gdy wyciągnąłeś niezgnieciony, a następnie uproszczone zwijanie zatwierdzenia scalającego opisane w kolejnej odpowiedzi pozwala wybrać niezgniecioną ścieżkę i odrzucić zgniecioną ścieżkę.]) Rezultat jest taki, że rzeczy, które próbuje wypchnąć, kończą się zawieraniem pracy, którą ktoś poświęcił na bieżące repozytorium hosta, z którego wypychasz, ale nie pracy osób zaangażowanych bezpośrednio do repozytorium podrzędnego lub za pośrednictwem innego hosta magazyn.

Jeśli jednak użyjesz --onto, wszystkie zatwierdzone zmiany są rejestrowane jako OK, aby używać ich dosłownie, więc gdy proces przepisywania napotka je jako jeden z rodziców scalania, chce je przepisać, zachowa je zamiast próbować je przepisać w zwykły sposób.

entheh
źródło
2
Rekomenduję tę odpowiedź jako tę, która pomogła mi już dwukrotnie. A opis jest tym, czego potrzebowałem, aby zrozumieć, jak działa rozdzielanie i pchanie we właściwe miejsce. Dziękuję Ci.
Kamil Wozniak
2
Co to jest „współdzielony projekt”?
Colin D
1
@ColinD „projekt współdzielony” to nazwa pilota. Jest to nazwa, którą wybierzesz podczas konfigurowania miejsca pobierania i wysyłania. Możesz uzyskać listę pilotów za pomocą git remote -v.
entheh
@entheh godziny, które poświęciłeś na to, tylko uczyniły mój dzień. Dzięki!
cze
1
Znalazłem ten błąd zawsze kiedy twoje repozytorium podrzędne ma katalog, którego nazwa jest taka sama jak przedrostek, np. Dodajesz repozytorium podrzędne jako "libxxx" do repozytorium głównego, a samo repozytorium podrzędne ma również podkatalog o nazwie "libxxx ”. W tym przypadku git niepoprawnie potraktuje zatwierdzenia w repozytorium podrzędnym jako zatwierdzenia w repozytorium głównym i spróbuje je podzielić. Ta --ontoopcja pomaga gitowi zidentyfikować zatwierdzenia, które są już w repozytorium podrzędnym.
Cosyn
14

W przypadku aplikacji typu „Strony GitHub”, w której wdrażasz poddrzewo „dist” w gałęzi gh-pages, rozwiązanie może wyglądać mniej więcej tak

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

Wspominam o tym, ponieważ wygląda to nieco inaczej niż przykłady heroku podane powyżej. Widzisz, że mój folder „dist” istnieje w głównej gałęzi mojego repozytorium, a następnie przesyłam go jako poddrzewo do gałęzi gh-pages, która również znajduje się w źródle.

Colin D.
źródło
3

Z tym problemem spotkałem się już wcześniej i oto jak go rozwiązałem.

Dowiedziałem się, że mam oddział, który nie był dołączony do lokalnego oddziału głównego. Ta gałąź istnieje i po prostu wisi w próżni. W twoim przypadku prawdopodobnie nazywa się project-shared. Zakładając, że tak jest i kiedy robisz a git branch, możesz zobaczyć lokalny project-sharedoddział, możesz `` dołączyć '' nowe zatwierdzenia do istniejącego project-sharedoddziału, wykonując:

git subtree split --prefix=public/shared --onto public-shared --branch public-shared

Zrozumiałem, git subtreeże zacznę tworzenie nowego oddziału --onto, w tym przypadku jest to public-sharedoddział lokalny . Wówczas gałąź oznacza utworzenie gałęzi, która po prostu zastępuje starą public-sharedgałąź.

Pozwoli to zachować wszystkie poprzednie SHA public-sharedgałęzi. Wreszcie możesz zrobić

git push project-shared project-shared:master

Zakładając, że masz również project-sharedpilota; spowoduje to przesunięcie lokalnego wiszącego w pustej project-shared gałęzi do mastergałęzi pilota project-shared.

snw
źródło
3

Wynika to z ograniczeń oryginalnego algorytmu. Podczas obsługi zatwierdzeń scalających oryginalny algorytm używa uproszczonych kryteriów odcinania niepowiązanych rodziców. W szczególności sprawdza, czy istnieje rodzic, który ma to samo drzewo. Gdyby taki rodzic znalazł, zwinąłby zatwierdzenie scalające i zamiast tego użyłby zatwierdzenia rodzica, zakładając, że inni rodzice mają zmiany niezwiązane z poddrzewem. W niektórych przypadkach spowodowałoby to porzucenie części historii, która ma rzeczywiste zmiany w poddrzewie. W szczególności porzuciłby sekwencje zatwierdzeń, które dotknęłyby poddrzewa, ale skutkowałyby tą samą wartością poddrzewa.

Zobaczmy przykład (który możesz łatwo odtworzyć), aby lepiej zrozumieć, jak to działa. Weź pod uwagę następującą historię (format linii to: zatwierdzenie [drzewo] temat):

% 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

W tym przykładzie dzielimy się dalej dir . Zatwierdza Di Ema to samo drzewo z, ponieważ mamy zatwierdzenie C, które cofnęło zatwierdzenie B, więc B-Csekwencja nie robi nic, dirmimo że ma w niej zmiany.

Teraz zróbmy podział. Najpierw podzieliliśmy się po zatwierdzeniu 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

Następnie podzieliliśmy się po zatwierdzeniu E.

% git log `git subtree split -P dir E` ...
* D' [z'] add dir/file2.txt
* A' [w'] add dir/file1.txt

Tak, straciliśmy dwa zatwierdzenia. Powoduje to błąd podczas próby wypchnięcia drugiego podziału, ponieważ nie ma on tych dwóch zatwierdzeń, które już dotarły do ​​źródła.

Zwykle możesz tolerować ten błąd używając push --force, ponieważ porzucone zatwierdzenia generalnie nie zawierają krytycznych informacji. Na dłuższą metę błąd musi zostać naprawiony, więc historia splitu faktycznie zawierałaby wszystkie zatwierdzenia, które dotykają dir, zgodnie z oczekiwaniami. Spodziewałbym się, że poprawka obejmie głębszą analizę zatwierdzeń rodzica dla ukrytych zależności.

Dla odniesienia, oto część oryginalnego kodu odpowiedzialna za zachowanie.

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
klimkin
źródło
czy błąd został naprawiony na dzień 2020-05-09 (sobota)? @klimkin
halfmoonhalf
błąd został naprawiony. zobacz: contrib / subtree: fix "subtree split" bug z pominięciem scalania. github.com/git/git/commit/…
halfmoonhalf
0

Odpowiedź Erica Woodruffa mi nie pomogła, ale następujące:

Zwykle używam opcji „git subtree pull” z opcją „--squash”. Wygląda na to, że utrudniło to pogodzenie sprawy, więc musiałem tym razem wyciągnąć poddrzewo bez zgniatania, rozwiązać niektóre konflikty, a następnie naciskać.

Muszę dodać, że zgniecione szarpnięcie nie ujawniło żadnych konfliktów, powiedziało mi, że wszystko w porządku.

Zsolt Szatmari
źródło
0

Innym [prostszym] rozwiązaniem jest przesunięcie głowy pilota poprzez wykonanie kolejnego zatwierdzenia, jeśli możesz. Po wciągnięciu tej zaawansowanej głowy do lokalnego poddrzewa będziesz mógł ponownie z niej wypchnąć.

9swampy
źródło
1
Dokładnie w ten sposób rozwiązałem ten sam problem ze zdalnym repozytorium niezsynchronizowanym z lokalnym poddrzewem.
Aidas Bendoraitis
0

Możesz wymusić wypychanie lokalnych zmian do zdalnego repozytorium poddrzewa

git push subtree_remote_address.git `git subtree split --prefix=subtree_folder`:refs/heads/branch --force
FunkyKat
źródło
0

Szybka powłoka PowerShell dla użytkowników systemu Windows oparta na rozwiązaniu Chrisa Jordana

$id = git subtree split --prefix pythonapp master
Write-Host "Id is: $id"
Invoke-Expression "git push heroku $id`:master --force"
Rtype
źródło
0

Oto, co napisałem na podstawie tego, co powiedział @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
Demodave
źródło
0

Miałem też ten problem. Oto, co zrobiłem na podstawie najlepszych odpowiedzi:

Dany:

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

Wpisz to:

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

Zwróć uwagę, że token wyprowadzany przez „git subtree push” jest używany w „git push”.

Amos
źródło