Podsumowanie
Domyślnie git pull
tworzy zatwierdzenia scalania, które zwiększają szum i złożoność historii kodu. Ponadto pull
ułatwia nie myśleć o tym, w jaki sposób zmiany mogą wpływać na nadchodzące zmiany.
git pull
Komenda jest bezpieczny tak długo, jak to tylko wykonuje scala fast-forward. Jeśli git pull
jest skonfigurowany tylko do wykonywania połączeń szybkiego przewijania do przodu, a gdy scalanie szybkiego przewijania nie jest możliwe, Git zakończy działanie z błędem. To da ci możliwość przestudiowania nadchodzących zatwierdzeń, zastanowienia się, w jaki sposób mogą one wpłynąć na twoje lokalne zatwierdzenia, i wybrania najlepszego sposobu działania (scalenie, zmiana bazy, resetowanie itp.).
Dzięki Git 2.0 i nowszym możesz uruchomić:
git config --global pull.ff only
aby zmienić domyślne zachowanie na szybkie przewijanie do przodu. W wersjach Git od 1.6.6 do 1.9.x musisz przyzwyczaić się do pisania:
git pull --ff-only
Jednak w przypadku wszystkich wersji Git zalecam skonfigurowanie git up
aliasu:
git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'
i używanie git up
zamiast git pull
. Wolę ten alias niżgit pull --ff-only
ponieważ:
- działa ze wszystkimi (nie starożytnymi) wersjami Gita,
- pobiera wszystkie odgałęzienia (nie tylko odgałęzienie, nad którym aktualnie pracujesz) i
- oczyszcza stare
origin/*
gałęzie, które już nie istnieją pod prąd.
Problemy z git pull
git pull
nie jest złe, jeśli jest właściwie używane. Kilka ostatnich zmian w Git ułatwiło git pull
prawidłowe korzystanie , ale niestety domyślne zachowanie zwykłego git pull
ma kilka problemów:
- wprowadza niepotrzebne nieliniowości w historii
- ułatwia przypadkowe przywrócenie zatwierdzeń, które zostały celowo wyparto pod prąd
- modyfikuje katalog roboczy w nieprzewidywalny sposób
- wstrzymywanie tego, co robisz, aby przejrzeć czyjąś pracę, jest denerwujące
git pull
- utrudnia to prawidłowe ustawienie bazy na zdalnej gałęzi
- nie usuwa gałęzi usuniętych w zdalnym repozytorium
Problemy te opisano bardziej szczegółowo poniżej.
Historia nieliniowa
Domyślnie git pull
polecenie jest równoważne z uruchomieniem, git fetch
po którym następuje git merge @{u}
. Jeśli w lokalnym repozytorium znajdują się nieprzypisane zatwierdzenia, część scalania git pull
tworzy zatwierdzenie scalania.
Nie ma nic złego w zatwierdzeniach scalania, ale mogą być niebezpieczne i powinny być traktowane z szacunkiem:
- Zatwierdzenia scalania są z natury trudne do zbadania. Aby zrozumieć, co robi scalanie, musisz zrozumieć różnice między wszystkimi rodzicami. Konwencjonalny plik różnicowy nie oddaje dobrze tej wielowymiarowej informacji. Natomiast seria normalnych zatwierdzeń jest łatwa do przejrzenia.
- Rozwiązywanie konfliktów scalania jest trudne, a błędy często pozostają niewykryte przez długi czas, ponieważ zatwierdzenia scalania są trudne do sprawdzenia.
- Połączenia mogą po cichu zastąpić efekty zwykłych zatwierdzeń. Kod nie jest już sumą przyrostowych zatwierdzeń, co prowadzi do nieporozumień na temat tego, co faktycznie się zmieniło.
- Scalanie zatwierdzeń może zakłócać niektóre schematy ciągłej integracji (np. Auto-kompilacja tylko ścieżki pierwszego rodzica zgodnie z przyjętą konwencją, że drugi rodzic wskazuje niekompletne prace w toku).
Oczywiście jest czas i miejsce na fuzje, ale zrozumienie, kiedy fuzje powinny i nie powinny być używane, może poprawić użyteczność repozytorium.
Zauważ, że celem Git jest ułatwienie dzielenia się i korzystania z ewolucji bazy kodu, a nie dokładne zapisywanie historii dokładnie w miarę jej rozwoju. (Jeśli się nie zgadzasz, rozważ rebase
polecenie i powód, dla którego zostało utworzone). Zatwierdzenia scalania utworzone przez git pull
nie przekazują użytecznej semantyki innym użytkownikom - po prostu mówią, że ktoś inny przepchnął się do repozytorium, zanim skończysz wprowadzać zmiany. Dlaczego owe zatwierdzenia scalania są, jeśli nie mają znaczenia dla innych i mogą być niebezpieczne?
Możliwe jest skonfigurowanie git pull
bazowania zamiast scalania, ale ma to również problemy (omówione później). Zamiast tego git pull
powinien być skonfigurowany tak, aby wykonywał tylko szybkie przewijanie do przodu.
Ponowne wprowadzenie przywróconych zobowiązań
Załóżmy, że ktoś zmienia gałąź i popycha ją siła. Zasadniczo nie powinno tak się zdarzyć, ale czasami jest konieczne (np. Aby usunąć plik dziennika 50GiB, który został przypadkowo zatwierdzony i wypchnięty). Scalenie wykonane przez git pull
spowoduje scalenie nowej wersji gałęzi upstream ze starą wersją, która nadal istnieje w lokalnym repozytorium. Jeśli popchniesz wynik, widelce i pochodnie zaczną nadciągać.
Niektórzy mogą twierdzić, że prawdziwym problemem jest wymuszanie aktualizacji. Tak, generalnie wskazane jest unikanie wypychania siły, gdy tylko jest to możliwe, ale czasami są one nieuniknione. Programiści muszą być przygotowani na radzenie sobie z aktualizacjami wymuszonymi, ponieważ czasem będą się one zdarzały. Oznacza to, że nie ślepo łączy się stare zobowiązania za pomocą zwykłego git pull
.
Zaskocz modyfikacje katalogu roboczego
Nie ma możliwości przewidzenia, jak będzie wyglądał katalog roboczy lub indeks, dopóki nie git pull
zostanie wykonane. Mogą istnieć konflikty scalania, które musisz rozwiązać, zanim będziesz mógł zrobić cokolwiek innego, może wprowadzić plik dziennika 50GiB do katalogu roboczego, ponieważ ktoś przypadkowo go wypchnął, może zmienić nazwę katalogu, w którym pracujesz itp.
git remote update -p
(lub git fetch --all -p
) pozwala spojrzeć na zobowiązania innych osób, zanim zdecydujesz się na scalenie lub zmianę bazy, co pozwoli ci stworzyć plan przed podjęciem działań.
Trudność w sprawdzaniu zobowiązań innych osób
Załóżmy, że jesteś w trakcie wprowadzania zmian i ktoś inny chce, abyś sprawdził niektóre zatwierdzone przez siebie zmiany. git pull
Operacja scalania (lub rebase) modyfikuje katalog roboczy i indeks, co oznacza, że katalog roboczy i indeks muszą być czyste.
Możesz użyć, git stash
a następnie git pull
, ale co robisz, gdy skończysz sprawdzanie? Aby wrócić do miejsca, w którym byłeś, musisz cofnąć scalenie utworzone przez git pull
i zastosować skrytkę.
git remote update -p
(lub git fetch --all -p
) nie modyfikuje katalogu roboczego lub indeksu, więc można go bezpiecznie uruchomić w dowolnym momencie - nawet jeśli wprowadziłeś zmiany etapowe i / lub niestabilne. Możesz wstrzymać to, co robisz i sprawdzić czyjeś zatwierdzenie, nie martwiąc się o ukrywanie lub dokończenie zatwierdzenia, nad którym pracujesz.git pull
nie daje ci takiej elastyczności.
Powrót do zdalnego oddziału
Typowym wzorcem użycia Gita jest git pull
wprowadzenie najnowszych zmian, a następnie git rebase @{u}
wyeliminowanie wprowadzonego zatwierdzenia scalania git pull
. To wspólne wystarczy, że Git ma kilka opcji konfiguracyjnych, aby zmniejszyć te dwa kroki do jednego kroku mówiąc git pull
wykonać rebase zamiast seryjnej (patrz branch.<branch>.rebase
, branch.autosetuprebase
i pull.rebase
opcje).
Niestety, jeśli masz nieprzypisane zatwierdzenie scalania, które chcesz zachować (np. Zatwierdzenie scalające wypchniętą gałąź funkcji do master
), ani rebase-pull ( git pull
z branch.<branch>.rebase
ustawionym na true
), ani merge-pull ( git pull
zachowanie domyślne ), po którym następuje rebase będzie działać. Jest tak, ponieważ git rebase
eliminuje scalenia (linearyzuje DAG) bez --preserve-merges
opcji. Operacji rebase-pull nie można skonfigurować w celu zachowania scalania, a połączenie-pull, po którym następuje git rebase -p @{u}
, nie eliminuje scalenia spowodowanego przez pull-pull. Aktualizacja: Dodano Git v1.8.5 git pull --rebase=preserve
i git config pull.rebase preserve
. Powodują git pull
to git rebase --preserve-merges
po pobraniu zatwierdzeń poprzedzających. (Dzięki funkaster za heads-up!)
Czyszczenie usuniętych gałęzi
git pull
nie przycina gałęzi zdalnego śledzenia odpowiadających gałęziom, które zostały usunięte ze zdalnego repozytorium. Na przykład, jeśli ktoś usunie gałąź foo
ze zdalnego repozytorium, nadal zobaczysz origin/foo
.
Prowadzi to do przypadkowego wskrzeszenia zabitych gałęzi przez użytkowników, którzy uważają, że nadal są aktywni.
Lepsza alternatywa: użyj git up
zamiastgit pull
Zamiast tego git pull
zalecam utworzenie i używanie następującego git up
aliasu:
git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'
Ten alias pobiera wszystkie najnowsze zatwierdzenia ze wszystkich gałęzi upstream (przycinanie martwych gałęzi) i próbuje przewinąć gałąź lokalną do najnowszego zatwierdzenia w gałęzi upstream. Jeśli się powiedzie, nie będzie żadnych lokalnych zatwierdzeń, więc nie będzie ryzyka konfliktu scalania. Przewijanie do przodu nie powiedzie się, jeśli istnieją lokalne (niepodawane) zatwierdzenia, co daje możliwość przejrzenia zatwierdzeń przed podjęciem działania.
To wciąż modyfikuje katalog roboczy w nieprzewidywalny sposób, ale tylko wtedy, gdy nie masz żadnych lokalnych zmian. W przeciwieństwie do git pull
, git up
nigdy nie spowoduje wyświetlenia monitu oczekującego rozwiązania konfliktu scalania.
Inna opcja: git pull --ff-only --all -p
Poniższa wersja stanowi alternatywę dla powyższego git up
aliasu:
git config --global alias.up 'pull --ff-only --all -p'
Ta wersja git up
ma takie samo zachowanie jak poprzedni git up
alias, z wyjątkiem:
- komunikat o błędzie jest nieco bardziej tajemniczy, jeśli lokalny oddział nie jest skonfigurowany z odgałęzieniem wyższym
- opiera się na nieudokumentowanej funkcji (
-p
argument przekazywany fetch
), która może ulec zmianie w przyszłych wersjach Gita
Jeśli korzystasz z Git 2.0 lub nowszej wersji
W wersji Git 2.0 i nowszych możesz skonfigurować git pull
domyślną funkcję tylko szybkiego przewijania do przodu:
git config --global pull.ff only
To powoduje, git pull
że tak się zachowuje git pull --ff-only
, ale nadal nie pobiera wszystkich zatwierdzeń wcześniejszych ani nie usuwa starych origin/*
gałęzi, więc nadal wolę git up
.