Odbudowywanie oddziału, który jest publiczny

6

Nie rozumiem, jak używać git-rebasei rozważam następujący przykład.

Zacznijmy repozytorium w ~/tmp/repo:

$ git init

Następnie dodaj plik foo

$ echo "hello world" > foo

który jest następnie dodawany i zatwierdzany:

$ git add foo
$ git commit -m "Added foo"

Następnie uruchomiłem zdalne repozytorium. W ~/tmp/bare.gitpobiegłem

$ git init --bare

Aby połączyć się repoz bare.gittym, pobiegłem

$ git remote add origin ../bare.git/
$ git push --set-upstream origin master

Następnie pozwala rozgałęzić, dodać plik i ustawić upstream dla nowego oddziału b1:

$ git checkout -b b1
$ echo "bar" > foo2
$ git add foo2
$ git commit -m "add foo2 in b1"
$ git push --set-upstream origin b1

Teraz nadszedł czas, aby wrócić masteri zmienić coś tam:

$ echo "change foo" > foo
$ git commit -a -m "changed foo in master"
$ git push

W tym momencie masterplik foozawiera zmienione foo , podczas gdy w b1nim jest jeszcze witaj świecie . Wreszcie chcę zsynchronizować b1z postępem dokonanym w master.

$ git checkout b1
$ git fetch origin
$ git rebase origin/master

W tym momencie git stzwraca:

# On branch b1
# Your branch and 'origin/b1' have diverged,
# and have 2 and 1 different commit each, respectively.
#   (use "git pull" to merge the remote branch into yours)
#
nothing to commit, working directory clean

W tym momencie zawartość foow branży b1jest zmiana foo również. Co oznacza to ostrzeżenie? Spodziewałem się, że powinienem zrobić git push, git sugeruje zrobić git pull... Zgodnie z tą odpowiedzią , to mniej więcej tak, aw swoim komentarzu @FrerichRaabe wyraźnie mówi, że nie muszę tego robić. Co tu się dzieje? Jakie jest niebezpieczeństwo, jak należy postępować? Jak zachować spójność historii? Jaka jest wzajemna zależność między przypadkiem opisanym powyżej a następującym cytatem:

Nie zmieniaj baz danych, które zostały przekazane do publicznego repozytorium.

zaczerpnięte z książki pro git .

Myślę, że jest to jakoś powiązane, a jeśli nie, chciałbym wiedzieć, dlaczego. Jaki jest związek między powyższym scenariuszem a procedurą opisaną w tym poście .

Dror
źródło
Powodem git sttego jest to, że git wie, że twój lokalny b1oddział śledzi origin/b1, więc na tym chcesz się opierać. Uciekłeś git rebase origin/masterchociaż, więc rebased ( „odtwarzane”) swoje b1rewizje na górze origin/master.
Frerich Raabe
@FrerichRaabe: Pozwól mi spróbować sformułować. Można powiedzieć, że przyjmując origin/masteri mastersą aktualizowane, mam rebase origin/b1na origin/master, a następnie zrobić git pullgdy b1jest wyrejestrowany wyciągnąć rebase do lokalnego repozytorium?
Dror
Nie, nie potrzebujesz git pulli nigdy nie opierasz zdalnej gałęzi (np. origin/master) Na czymkolwiek innym.
Frerich Raabe
@FrerichRaabe Więc jaka jest właściwa droga tutaj? Otrzymane ostrzeżenia oznaczają, że po zmianie bazy origin/b1i b1nie są takie same. Jest to raczej oczywiste, ale jaki jest właściwy sposób, aby to naprawić? Czy też naprawienie go oznacza zepsucie historii, jak wyjaśnił @heavyd?
Dror
1
Dodałbym, o ile jedno repo jest jedynym repo pchającym / wyciągającym z publicznego zdalnego repo, które jest bezpieczne. Mam problemy, w których jako jedyny pracuję nad projektem, ale pracuję na dwóch różnych maszynach. Jeśli oddziały kiedykolwiek się rozejdą w tej sytuacji, nadal możesz mieć bałagan.
heavyd

Odpowiedzi:

7

Powodem, dla którego nie chcesz zmieniać bazy zatwierdzeń, które zostały wypchnięte do publicznego repozytorium, jest to, że git-rebasepolecenie zmienia historię.

Co to znaczy i dlaczego jest złe? Po pierwsze, sugerowałbym przeczytanie tego rozdziału książki Git. Z tego dowiesz się, że zatwierdzenia składają się ze wskaźnika do obiektu drzewa (migawki plików) i wskaźnika do zatwierdzenia nadrzędnego. Teraz, kiedy „zmieniasz historię”, wprowadzając zmiany na początku nowych zatwierdzeń, zmieniasz wskaźnik nadrzędny już zatwierdzonych zmian, co z kolei zmienia identyfikator twoich zobowiązań.

Powodem tego jest to, że jeśli publicznie udostępniasz swoje commity, a inni rozpoczynają dodatkową pracę na podstawie tych commits, to zmieniasz te commity, twoje drzewa nie są już zsynchronizowane.

Możesz to wszystko zobaczyć, wydając niektóre git-logpolecenia podczas wykonywania przykładu. Uruchomiłem je tuż przed uruchomieniem polecenia rebase:

$ git log --pretty=oneline origin/master
9b077261d1619803213201d5c7cefb757eb66b67 Changed foo in master
911ce5b247e79682ec9f73ad9a15fd3167b7e76d Added foo

$ git log --pretty=oneline origin/b1
63a57ef54e301314a9dab38de0cd9d88c59a5fba added foo2 in b1
911ce5b247e79682ec9f73ad9a15fd3167b7e76d Added foo

$ git log --pretty=oneline b1
63a57ef54e301314a9dab38de0cd9d88c59a5fba added foo2 in b1
911ce5b247e79682ec9f73ad9a15fd3167b7e76d Added foo

A teraz po wykonaniu rebase origin/masteri origin/b1są takie same, ale b1teraz:

$ git log --pretty=oneline b1
6687c64c37db0ee21a4d87e45d6ccb0913b8686d added foo2 in b1
9b077261d1619803213201d5c7cefb757eb66b67 Changed foo in master
911ce5b247e79682ec9f73ad9a15fd3167b7e76d Added foo

Zauważysz, że zatwierdzenie „dodane foo2 w b1” ma inny identyfikator niż w poprzednich poleceniach dziennika. Jeśli zatwierdzisz tę zmianę w swoim publicznym repozytorium, masz teraz dwa zatwierdzenia, które wykonują w nich tę samą pracę i powoduje problemy.

Załóżmy teraz, że zamiast polegać na dodaniu b1 na master, po prostu połączyłeś master z b1, twój log wyglądałby następująco:

$ git checkout b1
$ git merge origin/master
$ git log --pretty=oneline b1
518eb2dc6b2da0ff43ddd6837332031cc00eaad1 Merge remote-tracking branch 'origin/master' into b1
9b077261d1619803213201d5c7cefb757eb66b67 Changed foo in master
63a57ef54e301314a9dab38de0cd9d88c59a5fba added foo2 in b1
911ce5b247e79682ec9f73ad9a15fd3167b7e76d Added foo

Zauważysz dodatkowe zatwierdzenie, które reprezentuje scalenie dwóch poprzednich zatwierdzeń. Tę historię można teraz udostępnić i wszyscy będą zadowoleni.

git-log --graph może także pomóc rzucić dodatkowe światło na to, co się dzieje.

ciężki
źródło
Czy możesz odnieść się do metody opisanej w łączonym poście? Ponadto, co się stanie, jeśli wiem, że origin/b1jest on używany wyłącznie przeze mnie - zakładając, że jest to pewnego rodzaju kopia zapasowa b1. Jaki wpływ ma to założenie na sytuację? Czy ma sens w tym przypadku robić git push --forcecoś podobnego zaraz po zmianie bazy?
Dror
Szczerze mówiąc, obieg pracy opisany w twoim poście to dużo dodatkowej pracy. Powinieneś być w stanie to zrobić po prostu uruchamiając, git merge mastergdy foogałąź jest wyewidencjonowana. Ponownie spróbuj tego sam i git-logsprawdź, co się dzieje.
heavyd
Rzeczywiście, ale istnieje jedna poważna wada związana z tym połączeniem - zwykle masterjest uważana za gałąź stabilną, podczas gdy foo(lub jakakolwiek inna gałąź) jest niestabilna. Łączenie stabilnego w niestabilny jest niepożądane, dlatego dodatkowa praca. Próbuję dowiedzieć się, czy istnieją skróty.
Dror
1
Czy możesz wyjaśnić, dlaczego niepożądane jest połączenie stabilnego z niestabilnym? To nie powinno destabilizować gałęzi bardziej niż jest już.
heavyd 30.10.13
Może „niepożądane” było zbyt mocne. Jeśli zawsze łączysz niestabilną w stabilną, to pod koniec dnia, kiedy gałąź jest gotowa i stabilna dla siebie, tylko jedno (!) Połączenie z powrotem w master. Daje to czystszą historię. Być może lepsze sformułowanie byłoby takie, że takie podejście ma powyższą zaletę.
Dror
2

Późno na przyjęciu, ale oto odpowiedź na potomstwo:

git rebasejest przeznaczony do użytku lokalnego. Ponownie zapisuje historię, co pozwala na bardzo ładną „linię główną”, ale jest niebezpieczny w środowisku wielu użytkowników

Jeśli:

  • jako jedyny korzystasz ze zdalnego repozytorium,
  • I używasz go tylko w jednym obszarze roboczym,

Wtedy może to mieć sens, zmuszając do ponownego napisania historii. Zrób rebase, a następnie:

git push -f origin remotebranch

Jeśli któreś z tych założeń nie zostanie spełnione, możesz żałować tego działania :-)

Przeczytaj więcej w tym poście na blogu

Omri Spector
źródło