Jakie są różnice między git prune, git prune, git fetch --prune itp

358

Moja sytuacja jest taka ... ktoś pracujący nad tym samym repozytorium usunął oddział z repozytorium lokalnego i zdalnego ...

Większość osób, które pytały o tego rodzaju problem w przypadku przepełnienia stosu lub innych witryn, nadal ma problem z odgałęzieniami git branch -ana liście rozgałęzień zdalnego śledzenia na dole:

* master
  develop
  feature_blah
  remotes/origin/master
  remotes/origin/develop
  remotes/origin/feature_blah
  remotes/origin/random_branch_I_want_deleted

Jednak w MOJEJ sytuacji gałąź, która nie powinna tam być, jest lokalna:

* master
  develop
  feature_blah
  random_branch_I_want_deleted
  remotes/origin/master
  remotes/origin/develop
  remotes/origin/feature_blah

Gdy wykonam jedną z poniższych czynności, nie zostanie ona usunięta lokalnie:

$ git prune

Próbowałem także:

$ git remote prune origin
$ git fetch --prune

Więcej przydatnych informacji: Kiedy sprawdzam, git remote show origintak to wygląda:

* remote origin
Fetch URL: utilities:homeconnections_ui.git
Push  URL: utilities:homeconnections_ui.git
HEAD branch: master
Remote branches:
 master                        tracked
 develop                       tracked
 feature_blah                  tracked
 other123                      tracked
 other444                      tracked
 other999                      tracked
Local branches configured for 'git pull':
 develop                      merges with remote develop
 feature_blah                 merges with remote other999
 master                       merges with remote master
 random_branch_I_want_deleted merges with remote random_branch_I_want_deleted
Local refs configured for 'git push':
 develop         pushes to develop     (local out of date)
 master          pushes to master      (up to date)
 feature_blah    pushes to feature_blah(up to date)

Zauważ, że jest to tylko sekcja zatytułowana Local branches configured for 'git pull':

Dlaczego?

gogogadgetinternet
źródło
git branch -d the_local_branch
krsteeve,
1
Dzięki, ale jestem ciekawy, dlaczego tak się stało.
gogogadgetinternet
Istniała subtelna różnica w przypadku hierarchii gałęzi ( x/y): została ona naprawiona (patrz moja odpowiedź poniżej )
VonC

Odpowiedzi:

664

Nie obwiniam cię za frustrację z tego powodu. Najlepszym sposobem na to jest spojrzenie. Istnieją potencjalnie trzy wersje każdej zdalnej gałęzi:

  1. Rzeczywista gałąź w zdalnym repozytorium
    (np. Zdalne repo na https://example.com/repo.git , refs/heads/master)
  2. Twoja migawka tego oddziału lokalnie (przechowywana pod refs/remotes/...)
    (np. Lokalne repozytorium refs/remotes/origin/master)
  3. I oddział lokalny, który może śledzić oddział zdalny
    (np. Lokalne repozytorium refs/heads/master)

Zacznijmy od git prune. Usuwa to obiekty , do których już nie ma odniesień, nie usuwa też odniesień. W twoim przypadku masz oddział lokalny. Oznacza to, że istnieje nazwa random_branch_I_want_deletedo nazwie odnosząca się do niektórych obiektów reprezentujących historię tej gałęzi. Zatem z definicji git prunenie zostanie usunięty random_branch_I_want_deleted. Naprawdę, git pruneto sposób na usunięcie danych, które zgromadziły się w Git, ale do których nic nie ma odniesienia. Zasadniczo nie wpływa to na widok żadnych gałęzi.

git remote prune origini git fetch --pruneoba działają w oparciu o referencje w obszarze refs/remotes/...(będę się nazywać tymi referencjami zdalnymi). Nie wpływa na lokalne oddziały. Ta git remotewersja jest przydatna, jeśli chcesz usunąć odwołania zdalne tylko pod określonym pilotem. W przeciwnym razie obaj robią dokładnie to samo. Tak, w skrócie, git remote prunei git fetch --prunedziałają na numer 2 powyżej. Na przykład, jeśli usunąłeś gałąź za pomocą GUI web git i nie chcesz, aby pojawiała się na liście lokalnych gałęzi ( git branch -r), to jest to polecenie, którego powinieneś użyć.

Aby usunąć oddział lokalny, powinieneś użyć git branch -d(lub -Djeśli nie jest on nigdzie scalony). FWIW, nie ma polecenia git, aby automatycznie usunąć lokalne gałęzie śledzenia, jeśli gałąź zdalna zniknie.

John Szakmeister
źródło
22
Dzięki temu lepiej jest odpowiedzieć na ogólne pytanie, wyjaśniając istotne różnice. Odpowiada również na dodatkowe pytania, które miałem od powyższego.
gogogadgetinternet
14
To polecenie wyświetli listę wszystkich gałęzi lokalnych, które nie mają odpowiadającej gałęzi zdalnej. Państwo może rura to xargs git branch -D, ale zauważ, że wszystkie utworzone ale nigdy zepchnięte na serwerze nowych oddziałów będzie usunięty, więc bieżnika ostrożnie: git branch -r | awk '{print $1}' | egrep -v -f /dev/fd/0 <(git branch -vv | grep origin) | awk '{print $1}'
Jason Walton
4
@ Seed Nie, nie ma. :-( Usuwa tylko lokalne odniesienia do zdalnego śledzenia. Właśnie to dwukrotnie sprawdziłem w wersji 2.7.0.
John Szakmeister
1
@ BlueRaja-DannyPflughoeft Bądź ostrożny z tym podejściem. Na przykład, w zależności od tego, jak robisz swoje stabilne gałęzie, mogą się wydawać, że zostały scalone z gałęzią główną, a ty ostatecznie je usuniesz. Nie jest to duża strata, ponieważ nie usuwasz ich z serwera, ale jeśli masz dla niego jakąś specjalną konfigurację, to zostanie utracona, gdy gałąź zostanie usunięta.
John Szakmeister
1
@Cloud Nie do końca prawda. Referencje można spakować (zobacz packed-refsplik w tym .gitobszarze), więc niekoniecznie jest to prosta kwestia usunięcia ich za pomocą eksploratora plików. Lepiej używać poleceń, aby upewnić się, że oba są obsługiwane poprawnie.
John Szakmeister,
55

git remote prunei git fetch --prunezrób to samo: usuń odnośniki do gałęzi, które nie istnieją na pilocie, jak powiedziałeś. Drugie polecenie łączy się z pilotem i pobiera jego bieżące gałęzie przed przycinaniem.

Nie dotyka jednak lokalnych oddziałów, które sprawdziłeś, a które możesz po prostu usunąć

git branch -d  random_branch_I_want_deleted

Zastąp -d, -Djeśli gałąź nie jest scalona w innym miejscu

git prune robi coś innego, czyści nieosiągalne obiekty, te commity, które nie są osiągalne w żadnej gałęzi ani tagu, a zatem nie są już potrzebne.

CharlesB
źródło
1
Wiem, że wydaje się to oczywiste, ale git pruneszuka nie tylko gałęzi i tagów, ale także wszystkich innych referencji.
Więc w moim przypadku, dlaczego nie miałbyś pracować z suszonymi śliwkami? Ponieważ nie obchodzą go lokalne oddziały, ale zdalne referencje? Dzięki za zwięzłe informacje.
gogogadgetinternet
@hvd jakie są referencje poza gałęziami i tagami?
CharlesB
@gogogadgetinternet tak dokładnie. (zakładając, że miałeś na myśli git remote prune)
CharlesB
4
IMO polega na konwencji nazewnictwa git, polegającej na używaniu „przycinania” zarówno do zbierania obiektów, jak i do czyszczenia referencji. To zamieszanie. Ale to tylko jedna z wielu zagadek interfejsu użytkownika. :-)
torek
14

W przypadku, gdyby każdy był zainteresowany. Oto szybki skrypt powłoki, który usunie wszystkie lokalne gałęzie, które nie są śledzone zdalnie. Słowo ostrzeżenia: pozbywa się każdego oddziału, który nie jest śledzony zdalnie, niezależnie od tego, czy został scalony, czy nie.

Jeśli zauważysz jakieś problemy, daj mi znać, a ja to naprawię (itp.)

Zapisz go w pliku o nazwie git-rm-ntb(nazywaj go cokolwiek) PATHi uruchom:

git-rm-ntb <remote1:optional> <remote2:optional> ...

clean()
{
  REMOTES="$@";
  if [ -z "$REMOTES" ]; then
    REMOTES=$(git remote);
  fi
  REMOTES=$(echo "$REMOTES" | xargs -n1 echo)
  RBRANCHES=()
  while read REMOTE; do
    CURRBRANCHES=($(git ls-remote $REMOTE | awk '{print $2}' | grep 'refs/heads/' | sed 's:refs/heads/::'))
    RBRANCHES=("${CURRBRANCHES[@]}" "${RBRANCHES[@]}")
  done < <(echo "$REMOTES" )
  [[ $RBRANCHES ]] || exit
  LBRANCHES=($(git branch | sed 's:\*::' | awk '{print $1}'))
  for i in "${LBRANCHES[@]}"; do
    skip=
    for j in "${RBRANCHES[@]}"; do
      [[ $i == $j ]] && { skip=1; echo -e "\033[32m Keeping $i \033[0m"; break; }
    done
    [[ -n $skip ]] || { echo -e "\033[31m $(git branch -D $i) \033[0m"; }
  done
}

clean $@
D.Mill
źródło
Dzięki! Miły akcent z kolorowym wyjściem ;-)
BVengerov
2
Czy $ (git branch -d $ i) nie byłoby bezpieczniej usuwać tylko scalone gałęzie?
user2012677
Bardzo pomocny, dziękuję bardzo !!!
Robin Hartland
Bezpieczniejsze opcje omówiono w stackoverflow.com/questions/7726949/...
Michael Freidgeim,
13

Zauważ, że jedna różnica między git remote --prunei git fetch --prunejest naprawiana, z zatwierdzeniem 10a6cc8 , przez Tom Miller ( tmiller) (dla git 1.9 / 2.0, Q1 2014):

Kiedy mamy gałąź zdalnego śledzenia o nazwie „ frotz/nitfol” z poprzedniego pobierania, a nadrzędny ma teraz gałąź o nazwie „** frotz ” **, fetchnie można usunąć „ frotz/nitfol” za pomocą „ git fetch --prune” z nadrzędnego.
git poinformuje użytkownika, aby użył „ git remote prune” do rozwiązania problemu.

Tak więc: gdy repozytorium nadrzędne ma gałąź („frotz”) o tej samej nazwie co hierarchia gałęzi („frotz / xxx”, możliwa konwencja nazewnictwa gałęzi ), powiodło się git remote --prune(w oczyszczeniu gałęzi zdalnego śledzenia z repozytorium) , ale git fetch --prunezawiodło.

Nigdy więcej:

Zmień sposób działania „ fetch --prune”, przenosząc operację przycinania przed operacją pobierania.
W ten sposób, zamiast ostrzegać użytkownika o konflikcie, automatycznie go naprawia.

VonC
źródło