Dlaczego muszę „git push --set-upstream origin <branch>”?


171

Stworzyłem lokalny oddział do testowania Solaris i Sun Studio. Następnie pchnąłem gałąź w górę. Po zatwierdzeniu zmiany i próbie wprowadzenia zmian:

$ git commit blake2.cpp -m "Add workaround for missing _mm_set_epi64x"
[solaris 7ad22ff] Add workaround for missing _mm_set_epi64x
 1 file changed, 5 insertions(+)
$ git push
fatal: The current branch solaris has no upstream branch.
To push the current branch and set the remote as upstream, use

    git push --set-upstream origin solaris

Dlaczego muszę w tym celu zrobić coś specjalnego?

Czy istnieje jakiś rozsądny przypadek użycia, w którym ktoś mógłby utworzyć <branch>, przekazać <branch>do pilota, a następnie twierdzić, że zatwierdzenie <branch>nie powinno być przeznaczone <branch>?


Podążyłem za tym pytaniem i odpowiedzią na Stack Overflow: Wypchnij nowy lokalny oddział do zdalnego repozytorium Git i śledź go . Domyślam się, że to kolejny przypadek niekompletnej lub błędnie zaakceptowanej odpowiedzi. Lub jest to kolejny przypadek, w którym Git wykonuje proste zadanie i utrudnia to.


Oto widok na innym komputerze. Gałąź wyraźnie istnieje, więc została utworzona i wypchnięta:

$ git branch -a
  alignas
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/alignas
  remotes/origin/arm-neon
  remotes/origin/det-sig
  remotes/origin/master
  remotes/origin/solaris


3
Dzięki @Alexi. Niestety, cytowany dup nie wyjaśnia absurdalnego przypadku użycia, który jest reprezentowany domyślnie. (To nie są pytania retoryczne. Naprawdę interesuje mnie powód projektu UX).
jww

1
Zauważ, że jest to konfigurowalne. Jeśli to zrobisz git config --add push.default current, git push automatycznie utworzy gałąź w zdalnym repozytorium, jeśli to konieczne.
Gogowitsch

Odpowiedzi:


311

TL; DR: git branch --set-upstream-to origin/solaris


Odpowiedź na pytanie, które zadałeś - które przeredaguję trochę jako „czy muszę ustawić nadrzędny kanał” - brzmi: nie, w ogóle nie musisz ustawiać nadrzędnego.

Jeśli jednak nie masz programu nadrzędnego dla bieżącej gałęzi, Git zmieni swoje zachowanie na git push, a także na inne polecenia.

Cała historia wypychania tutaj jest długa i nudna i sięga do historii sprzed wersji Git 1.5. Aby go bardzo skrócić, git pushzostał źle wdrożony. 1 Począwszy od wersji 2.0 Git, Git ma teraz pokrętło konfiguracji zapisane push.defaultdomyślnie na simple. W przypadku kilku wersji Gita przed i po 2.0, za każdym razem, gdy byłeś uruchamiany git push, Git wyrzucał dużo hałasu, próbując przekonać Cię do ustawienia push.defaulttylko po to, aby się git pushzamknąć.

Nie wspominasz, z której wersji Gita korzystasz, ani czy skonfigurowałeś push.default, więc musimy zgadnąć. Domyślam się, że używasz wersji Git 2-punktowy coś, a które zostały ustawione push.default, aby simpledostać to, żeby się zamknął. Dokładnie to, którą wersję Gita masz i co jeśli cokolwiek push.defaultustawiłeś, ma znaczenie ze względu na tę długą i nudną historię, ale ostatecznie fakt, że otrzymujesz kolejną skargę od Gita, wskazuje, że Twój Git jest skonfigurowany tak, aby uniknąć jednego z błędów z przeszłości.

Co to jest upstream?

Upstream to po prostu inna nazwa oddziału, zwykle zdalnego śledzenia gałąź, wiąże się z regularnym (lokalnych) oddział.

Każda gałąź ma opcję posiadania jednego (1) zestawu upstream. Oznacza to, że każda gałąź albo ma upstream, albo nie ma upstream. Żadna gałąź nie może mieć więcej niż jednego upstream.

Upstream powinien , ale nie musi, być prawidłową gałęzią (czy to zdalne śledzenie, czy lokalne ). Oznacza to, że jeśli bieżąca gałąź B ma upstream U , powinna działać. Jeśli to nie zadziała - jeśli narzeka, że U nie istnieje - to większość Gita działa tak, jakby nadrzędny strumień w ogóle nie był ustawiony. Kilka poleceń, takich jak , pokaże ustawienia nadrzędne, ale oznaczy je jako „nie ma”.origin/Bmastergit rev-parse U git branch -vv

Po co jest upstream?

Jeśli twój push.defaultjest ustawiony na simplelub upstream, ustawienie nadrzędne sprawi git push, że użyte bez dodatkowych argumentów po prostu zadziała.

To wszystko - po to wszystko git push. Ale to dość znaczące, ponieważ git pushjest to jedno z miejsc, w których zwykła literówka powoduje poważne bóle głowy.

Jeśli twój push.defaultjest ustawiony na nothing, matchinglub current, ustawienie nadrzędnego strumienia nic nie robi git push.

(Wszystko to przy założeniu, że Twoja wersja Git to co najmniej 2.0).

Upstream wpływa git fetch

Jeśli uruchomisz git fetchbez dodatkowych argumentów, Git ustali, z którego pilota pobrać, konsultując się z nadrzędnym gałąź bieżącej gałęzi. Jeśli upstream jest gałęzią ze zdalnym śledzeniem, Git pobiera z tego zdalnego. (Jeśli upstream nie jest ustawiony lub jest gałęzią lokalną, Git próbuje pobrać origin).

Upstream dotyka git mergei git rebasezbyt

Jeśli uruchomisz git mergelub git rebasebez dodatkowych argumentów, Git użyje upstream z bieżącej gałęzi. Więc skraca użycie tych dwóch poleceń.

Upstream wpływa git pull

I tak nigdy nie powinieneś 2 używać git pull, ale jeśli to zrobisz, użyj git pullustawienia nadrzędnego, aby dowiedzieć się, z którego pilota pobrać, a następnie z którą gałąź połączyć lub ponownie bazować. Oznacza to, że git pullrobi to samo, co - ponieważ git fetchfaktycznie działa git fetch - a następnie robi to samo, co git mergelub git rebase, ponieważ faktycznie działa git merge lub git rebase.

(Zwykle powinieneś po prostu wykonać te dwa kroki ręcznie, przynajmniej do czasu, gdy znasz Git na tyle dobrze, że gdy któryś z nich zawiedzie, co w końcu się uda, rozpoznasz, co poszło nie tak i będziesz wiedział, co z tym zrobić.)

Upstream wpływa git status

To może być właściwie najważniejsze. Gdy już masz zestaw nadrzędny, git statusmożesz zgłosić różnicę między bieżącą gałęzią a jej nadrzędną pod względem zatwierdzeń.

Jeśli, jak to jest w normalnym przypadku, znajdujesz się w gałęzi Bz jej upstream ustawioną na i uruchomisz , natychmiast zobaczysz, czy masz zatwierdzenia, które możesz wcisnąć i / lub zatwierdzenia, na których możesz scalić lub zmienić bazę.origin/Bgit status

Dzieje się tak, ponieważ git statusdziała:

  • git rev-list --count @{u}..HEAD: Ile commity masz na Bktóre są nie na ?origin/B
  • git rev-list --count HEAD..@{u}: Ile commity masz na które są nie na ?origin/BB

Utworzenie upstream daje ci wszystkie te rzeczy.

Jak to się masterstało, że ma już zestaw upstream?

Kiedy pierwszy raz klonujesz z jakiegoś pilota, użyj:

$ git clone git://some.host/path/to/repo.git

lub podobnie, ostatnim krokiem, jaki robi Git, jest zasadniczo git checkout master. Spowoduje to sprawdzenie twojego lokalnego oddziału - mastertylko ty nie masz lokalnego oddziału master.

Z drugiej strony, nie ma zdalnego śledzenia oddział nazwany origin/master, ponieważ po prostu go klonować.

Git domyśla się, że musiało to mieć na myśli: „zrób mi nowy lokal, masterktóry wskazuje na to samo zatwierdzenie co zdalne śledzenie origin/master, a kiedy już to robisz, ustaw górny strumień na masterto origin/master”.

Dzieje się tak w przypadku każdej gałęzi git checkout, której jeszcze nie masz. Git tworzy gałąź i sprawia, że ​​„śledzi” (jako upstream) odpowiednią gałąź zdalnego śledzenia.

Ale to nie działa dla nowych oddziałów, czyli oddziałów bez zdalnego śledzenia oddział jeszcze .

Jeśli utworzysz nowy oddział:

$ git checkout -b solaris

na razie nie ma origin/solaris. Lokalny solaris nie może śledzić gałęzi zdalnego śledzenia, origin/solarisponieważ nie istnieje.

Kiedy po raz pierwszy pchasz nową gałąź:

$ git push origin solaris

który tworzy solaris w origin, a tym samym tworzy również origin/solariswe własnym repozytorium Git. Ale jest za późno: masz już lokalną sieć, solarisktóra nie ma upstream . 3

Czy Git nie powinien po prostu ustawić tego teraz jako nadrzędny automatycznie?

Prawdopodobnie. Zobacz „źle zaimplementowany” i przypis 1. Trudno to teraz zmienić : istnieją miliony 4 skryptów, które używają Git, a niektóre mogą zależeć od jego obecnego zachowania. Zmiana zachowania wymaga nowej głównej wersji, nag-ware wymusi ustawienie jakiegoś pola konfiguracyjnego i tak dalej. Krótko mówiąc, Git jest ofiarą własnego sukcesu: wszelkie błędy, które zawiera, można dziś naprawić tylko wtedy, gdy zmiana jest albo w większości niewidoczna, wyraźnie - znacznie lepsza, albo następuje powoli w czasie.

Faktem jest, że nie dzieje się tak dzisiaj, chyba że używasz --set-upstreamlub -upodczas git push. To właśnie mówi wiadomość.

Nie musisz tego robić w ten sposób. Cóż, jak zauważyliśmy powyżej, w ogóle nie musisz tego robić, ale powiedzmy, że chcesz upstream. Już utworzony oddział solarisna origindzięki wcześniejszym naciśnięciu, jak i swoich git branchpokazów wyjściowych, które już mają origin/solaris w lokalnym repozytorium.

Po prostu nie masz tego ustawionego jako upstream dla solaris.

Aby ustawić to teraz, a nie podczas pierwszego naciśnięcia, użyj git branch --set-upstream-to. Polecenie --set-upstream-topodrzędne przyjmuje nazwę dowolnej istniejącej gałęzi, na przykład origin/solaris, i ustawia bieżącą gałąź w górę do tej innej gałęzi.

To wszystko - to wszystko, co robi - ale ma to wszystkie konsekwencje wymienione powyżej. Oznacza to, że możesz po prostu biegać git fetch, następnie rozglądać się, a następnie biegać git mergelub git rebaseodpowiednio, a następnie tworzyć nowe zatwierdzenia i biec git push, bez dodatkowego zamieszania.


1 Prawdę mówiąc, nie było wtedy jasne, czy początkowa implementacja była podatna na błędy. Stało się to jasne dopiero wtedy, gdy każdy nowy użytkownik za każdym razem popełniał te same błędy. Jest teraz „mniej biedny”, co nie znaczy, że jest „wspaniały”.

2 „Nigdy” jest trochę mocne, ale uważam, że nowicjusze Git dużo lepiej rozumieją rzeczy, kiedy oddzielam kroki, zwłaszcza gdy mogę im pokazać, co git fetchfaktycznie zrobiło, i wtedy mogą zobaczyć, co zrobią git mergelub git rebasezrobią dalej.

3 Jeśli uruchomisz swoją pierwszą git push jako — git push -u origin solaristj., Jeśli dodasz -uflagę — Git ustawi origin/solarisjako nadrzędny dla twojej bieżącej gałęzi, jeśli (i tylko jeśli) push się powiedzie. Więc powinieneś zaopatrywać -usię przy pierwszym pchnięciu. W rzeczywistości możesz go podać przy dowolnym późniejszym naciśnięciu i w tym momencie ustawi lub zmieni górny strumień. Ale myślę, że git branch --set-upstream-tojest łatwiej, jeśli zapomniałeś.

4 Mierzone metodą Austina Powersa / Dr Evila, mówiąc po prostu „jeden MILLLL-YUN”.


3
Jeśli typowym przypadkiem jest {utworzenie gałęzi / gałęzi wypychania / gałęzi używanej}, to czy wynikiem wypychania nowej gałęzi lokalnej do zdalnego repozytorium Git i śledzenia jej też nie powinno być coś, co faktycznie działa? A jeśli ktoś chce {utworzyć gałąź / wypchnąć gałąź / nie używać gałęzi}, to czy nie powinien robić czegoś specjalnego, na przykład --set-upstream /dev/null? Dlaczego ciężar spoczywa na zwykłym przypadku? Naprawdę nie rozumiem niektórych z tych decyzji dotyczących inżynierii i użyteczności.
jww

2
@VonC: racja, o to chodzi git push -u, ale naprawdę wygląda na to, że git push -upowinno być domyślne, lub przynajmniej domyślne, jeśli nie ma jeszcze nadrzędnego , a powinno być, git push --no-set-upstreamgdy obecnie nie ma nadrzędnego strumienia i chcesz zachować tak (z niezrozumiałego powodu :-)).
torek

2
„Ciągle zadajesz takie pytania, bo myślę, że skreśliłeś Gita jako„ naprawdę okropny ”. Prosimy o zachowanie tego rodzaju spekulacji dla siebie. Natknąłem się na to pytanie, ponieważ wciąż zadaję sobie tego typu pytania. Nie jestem najlepszym projektantem UX na świecie, ale nawet ja zdaję sobie sprawę, że domyślne zachowanie w tym konkretnym scenariuszu mogłoby być lepsze.
Steven Byks

4
@torek - Dziękuję. Twoja odpowiedź była poza tym fantastyczna; dobrze przemyślane, dobrze zorganizowane i niezwykle pouczające. :-)
Steven Byks

7
Zauważ, że jest to konfigurowalne. Jeśli to zrobisz git config --add push.default current, git push automatycznie utworzy gałąź w zdalnym repozytorium, jeśli to konieczne.
Gogowitsch

43

Różnica między
git push origin <branch>
i
git push --set-upstream origin <branch>
polega na tym, że oba te pliki dobrze trafiają do zdalnego repozytorium, ale zauważasz różnicę, gdy pociągniesz.

Jeśli to zrobisz:
git push origin <branch>
podczas ciągnięcia musisz:
git pull origin <branch>

Ale jeśli to zrobisz:
git push --set-upstream origin <branch>
wtedy podczas ciągnięcia musisz tylko:
git pull

Dodanie do --set-upstreampozwala więc na uniknięcie konieczności określania, z której gałęzi chcesz pobierać za każdym razem, gdy to robisz git pull.


różnica między dwiema wersjami „git push”, których nie wiem, dlaczego chciałbym / musiałbym ich używać. Bezcelowy!
Frank Puck

@FrankPuck to jest Git, rzekomo nadaje się do użytku w trybie offline, ale bez Google lub SO nie można go używać, gdy tylko opuścisz „wydeptaną ścieżkę”. Krótko wyjaśnione: --set-upstreamw git push(w przeciwieństwie do git branchz --set-upstream-to) jest to, co -bjest git checkout(w przeciwieństwie do git branchlub obecnie git switch -c). To jest szaleństwo przez cały czas i nie powinieneś oczekiwać niczego mniej. Oczywiście z git push set-upstreambędziesz chciał określić, remote branchpodczas gdy z git branch --set-upstream-toużywasz remote/branch(znanego również jako commreftreeish 😉).
0xC0000022L

19

Zasadniczo pełne polecenie jest jak git push <remote> <local_ref>:<remote_ref>. Jeśli uruchomisz tylko git push, git nie wie, co dokładnie zrobić, chyba że stworzyłeś konfigurację, która pomoże gitowi podjąć decyzję. W repozytorium git możemy skonfigurować wiele pilotów. Możemy również przekazać lokalną referencję do dowolnego zdalnego referencji. Pełne polecenie jest najprostszym sposobem na wykonanie pchnięcia. Jeśli chcesz wpisać mniej słów, musisz najpierw skonfigurować, na przykład --set-upstream.


0

Rozumiem, że "-u" lub "--set-upstream" pozwala określić górne (zdalne) repozytorium dla gałęzi, w której się znajdujesz, więc następnym razem, gdy uruchomisz "git push", nie będziesz nawet trzeba określić zdalne repozytorium.

Wypchnij i skonfiguruj repozytorium upstream (zdalne) jako źródło:

$ git push -u origin

Następnym razem, gdy będziesz pchać, nie musisz określać zdalnego repozytorium:

$ git push
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.