Czy „git pull --all” może zaktualizować wszystkie moje lokalne oddziały?

473

Często mam co najmniej 3 odległe gałęzie: master, inscenizację i produkcję. Mam 3 lokalne oddziały, które śledzą te odległe gałęzie.

Aktualizacja wszystkich moich lokalnych oddziałów jest żmudna:

git fetch --all
git rebase origin/master
git checkout staging
git rebase origin/staging
git checkout production
git rebase origin/production

Chciałbym móc po prostu wykonać „git pull-all”, ale nie byłem w stanie zmusić go do działania. Wygląda na to, że wykonuje „pobieranie - wszystko”, a następnie aktualizuje (przewija do przodu lub łączy) bieżącą działającą gałąź, ale nie inne lokalne gałęzie.

Nadal utknąłem ręcznie, przełączając się do każdego lokalnego oddziału i aktualizując.

mpoisot
źródło
8
Czy chcesz automatyczną aktualizację lokalnych oddziałów śledzenia tylko w przypadku szybkiego przewijania? Ypu powinno, ponieważ scalenie może mieć konflikt, który musiałbyś rozwiązać ...
Jakub Narębski
34
Zakładając, że konserwatywny czas na konsultacje wyniesie 300 USD, ten jeden problem kosztował firmy 23 242 800 USD, używając liczby wyświetleń 77 476. Teraz rozważ to pytanie stackoverflow.com/questions/179123/… i wszystkie pozostałe. Łał.
Luke Puplett
16
@Luke Jesteś pierwszą osobą, którą usłyszałem, zwracając uwagę na to, jak czas spędzony na próbowaniu zmuszenia gita do robienia tego, co chcemy, kosztuje firmy. Te proste rzeczy powinny być automatyczne i tak proste, że nie muszę otwierać przeglądarki, aby czytać fora, IMO.
Samuel
13
@LukePuplett Istnieje prawie ~ 9 razy więcej pytań na temat git na SO w porównaniu do Mercurial, a większość z tych pierwszych wydaje się brzmiać „jak wykonać <prosta operacja> w git?”. Oznacza to, że git jest źle zaprojektowany, źle udokumentowany, nieintuicyjny lub wszystkie trzy.
Ian Kemp,
26
@IanKemp Nie jestem pewien, czy można bezpiecznie zgłosić takie roszczenie bez znajomości danych demograficznych SO. Jeśli Mercurial nie jest tutaj tak często używany lub jeśli jego użytkownicy korzystają z innych forów, aby o niego zapytać, oczekiwałbym tego samego rezultatu. :) Jest około 51 razy więcej pytań o JavaScript w porównaniu do asemblera - więc nie zawsze może być dokładne ocenianie narzędzi tylko na podstawie tego rodzaju wskaźników.
danShumway,

Odpowiedzi:

188

Opisane zachowanie pull --alljest dokładnie zgodne z oczekiwaniami, choć niekoniecznie przydatne. Przekazywana jest opcja git fetch, która następnie pobiera wszystkie referencje ze wszystkich pilotów, zamiast tylko potrzebnej; pullnastępnie scala (lub w twoim przypadku zmienia bazę) odpowiedni pojedynczy oddział.

Jeśli chcesz sprawdzić inne oddziały, musisz je sprawdzić. I tak, łączenie (i przekształcanie) absolutnie wymaga drzewa roboczego, więc nie można tego zrobić bez sprawdzenia innych gałęzi. Jeśli chcesz, możesz zawinąć opisane kroki w skrypt / alias, chociaż sugeruję łączenie poleceń z nimi &&, aby w razie niepowodzenia jednego z nich nie próbowało się przeorać.

Cascabel
źródło
2
Jeśli podasz przykładową linię poleceń, głosowałbym za. Mam ten problem na github. Utworzyłem oddział w interfejsie użytkownika. Teraz potrzebuję mojego lokalnego, aby pokazać oddział. git pull --all; git branch ... argh ... polecenie: git branch -a
mariotti
@mariotti Zależy od tego, co próbujesz zrobić, i nie jest to tak naprawdę jasne z twojego komentarza. Najlepiej jest zadać nowe pytanie.
Cascabel
1
Lub @Jefromi .. podaj przykład. Właściwie zgodziłem się z tobą.
mariotti,
3
@mariotti Istotą tej odpowiedzi jest to, że wbudowane polecenia tak naprawdę nie robią tego, o co poprosił OP, więc niezbędna jest ich kolejność. Możliwe jest zautomatyzowanie tych kroków (patrz na przykład odpowiedź Johna), ale muszą one zostać wykonane. Więc jeśli to, co próbujesz zrobić, jest dokładnie takie samo jak OP, nie ma tak naprawdę żadnego przykładu, a jeśli próbujesz zrobić coś innego, powinieneś zadać nowe pytanie - tak działa StackOverflow! (A twój komentarz jest niejasny, ale przypuszczam, że chcesz tutaj czegoś innego niż OP, więc tak, nowe pytanie.)
Cascabel,
Tak, coś innego. Ale twoja odpowiedź była idealna do kontekstu. I może nie muszę już pytać tylko z powodu twojej odpowiedzi. Po prostu: zaakceptowana odpowiedź używa git-up, który jest po prostu interfejsem do wiersza poleceń git (zakładam). Miałem nadzieję, że możesz to wyrazić w kilku wierszach poleceń git. Obecna odpowiedź NIE jest git.
mariotti,
206

Używam synckomendy z piastą zautomatyzować ten. Mam alias git=hubw sobie .bash_profile, więc polecenie, które wpisuję to:

git sync

To aktualizuje wszystkie lokalne gałęzie, które mają pasującą gałąź nadrzędną. Ze strony podręcznika:

  • Jeśli lokalny oddział jest nieaktualny, przewiń go do przodu;
  • Jeśli lokalny oddział zawiera niedokończoną pracę, należy o tym ostrzec;
  • Jeśli gałąź wydaje się scalona, ​​a jej gałąź nadrzędna została usunięta, usuń ją.

Obsługuje również ukrywanie / odblokowywanie niezatwierdzonych zmian w bieżącym oddziale.

Kiedyś korzystałem z podobnego narzędzia o nazwie git-up , ale nie jest ono już utrzymywane i git syncrobi prawie dokładnie to samo.

Jan
źródło
15
Co z Windows?
Violet Giraffe
6
@ TrentonD.Adams daty zatwierdzenia i daty autora są różnymi pojęciami. Zmiana bazy zmieni datę zatwierdzenia, ale nie datę autora (z wyjątkiem konfliktów, w których również zmienia się data autora). Data autora odzwierciedla datę utworzenia drzewa zatwierdzenia i nie powinna się zmieniać podczas nieokreślonego bazy. Data zatwierdzenia zmienia się, ponieważ rebase zawsze tworzy nowe zatwierdzenie. Dlatego daty zatwierdzenia będą zawsze w odpowiedniej kolejności.
Dev
16
Aby wyłączyć automatyczne ponowne uruchamianie programu git-up, uruchom git config --global git-up.rebase.auto false.
Dan Loewenherz
18
@MaxYankov Zasadniczo należy unikać ponownego udostępniania wspólnej historii, nie ma nic złego w ponownym wycofywaniu lokalnych zatwierdzeń podczas ściągania.
Dev
23
Odbudowywanie lokalnych zatwierdzeń polega na przepisywaniu historii i upraszczaniu jej w rzeczywistości. Z rebase możesz znaleźć się z kodem, który scalił się automatycznie, ale nie kompiluje, lub gorzej, kompiluje, ale nie działa. Scalanie potwierdza sposób, w jaki działałeś: zaimplementowałeś zmiany i przetestowałeś je przed wprowadzeniem zmian innych osób, a scalanie zatwierdzenia jest bardzo użytecznym punktem: w tym miejscu upewniasz się, że różne zestawy szans dobrze się ze sobą bawią. Rebasing sprawia, że ​​wygląda na to, że ten proces nigdy się nie dzieje, co jest po prostu nieprawdą i jest bardzo niebezpieczną praktyką.
Max Yankov,
39

Wiem, że to pytanie ma prawie 3 lata, ale zadałem sobie to samo pytanie i nie znalazłem gotowego rozwiązania. Więc stworzyłem własny skrypt powłoki poleceń git.

Oto git-ffwd-updateskrypt:

  1. wydaje a, git remote updateaby pobrać ostatnie obroty
  2. następnie używa, git remote showaby uzyskać listę lokalnych gałęzi, które śledzą gałąź zdalną (np. gałęzie, których można używać git pull)
  3. następnie sprawdza, git rev-list --count <REMOTE_BRANCH>..<LOCAL_BRANCH>ile zatwierdzeń oddział lokalny znajduje się za pilotem (i na odwrót)
  4. jeśli oddział lokalny ma 1 lub więcej zatwierdzeń z wyprzedzeniem, NIE MOŻE być on szybko przekazywany i musi zostać scalony lub ponownie bazowany ręcznie
  5. jeśli oddział lokalny ma 0 zatwierdzeń z wyprzedzeniem i 1 lub więcej zatwierdzeń z tyłu, może zostać szybko przekazany przez git branch -f <LOCAL_BRANCH> -t <REMOTE_BRANCH>

skrypt można wywołać w następujący sposób:

$ git ffwd-update
Fetching origin
 branch bigcouch was 10 commit(s) behind of origin/bigcouch. resetting local branch to remote
 branch develop was 3 commit(s) behind of origin/develop. resetting local branch to remote
 branch master is 6 commit(s) behind and 1 commit(s) ahead of origin/master. could not be fast-forwarded

Pełny skrypt powinien zostać zapisany git-ffwd-updatei musi być włączony PATH.

#!/bin/bash

main() {
  REMOTES="$@";
  if [ -z "$REMOTES" ]; then
    REMOTES=$(git remote);
  fi
  REMOTES=$(echo "$REMOTES" | xargs -n1 echo)
  CLB=$(git rev-parse --abbrev-ref HEAD);
  echo "$REMOTES" | while read REMOTE; do
    git remote update $REMOTE
    git remote show $REMOTE -n \
    | awk '/merges with remote/{print $5" "$1}' \
    | while read RB LB; do
      ARB="refs/remotes/$REMOTE/$RB";
      ALB="refs/heads/$LB";
      NBEHIND=$(( $(git rev-list --count $ALB..$ARB 2>/dev/null) +0));
      NAHEAD=$(( $(git rev-list --count $ARB..$ALB 2>/dev/null) +0));
      if [ "$NBEHIND" -gt 0 ]; then
        if [ "$NAHEAD" -gt 0 ]; then
          echo " branch $LB is $NBEHIND commit(s) behind and $NAHEAD commit(s) ahead of $REMOTE/$RB. could not be fast-forwarded";
        elif [ "$LB" = "$CLB" ]; then
          echo " branch $LB was $NBEHIND commit(s) behind of $REMOTE/$RB. fast-forward merge";
          git merge -q $ARB;
        else
          echo " branch $LB was $NBEHIND commit(s) behind of $REMOTE/$RB. resetting local branch to remote";
          git branch -f $LB -t $ARB >/dev/null;
        fi
      fi
    done
  done
}

main $@
muhqu
źródło
1
Dziękuję za ten skrypt. Czy to możliwe, że ktoś może przekonwertować ten skrypt na pakiet Windows?
Saariko,
@ Saariko, dlaczego nie chcesz używać git na normalnej powłoce systemu Windows? Jeśli używasz czegoś takiego jak cygwin, ten skrypt powinien działać dobrze ... (choć go nie testowałem)
muhqu
@RyanWilcox dzięki, używam go jak każdego (roboczego) dnia ... ;-) możesz rzucić
muhqu
@muhqu Próbuję użyć twojego skryptu i nie wiem, dlaczego zadziałał za pierwszym razem, ale teraz nie działa "zgodnie z oczekiwaniami". Na przykład spójrz na to . Dlaczego master nadal 78 zatwierdza po uruchomieniu skryptu?
BPL
1
@muhqu W nowszych wersjach git -t i -l nie powinny być używane razem w ramach jednego git branchwywołania. Usunąłem opcję -l, aby zmienić wywołanie na, git branch -f $LB -t $ARB >/dev/null;a teraz skrypt działa tak, jak powinien.
Radek Liska
24

Zautomatyzowanie nie jest takie trudne:

#!/bin/sh
# Usage: fetchall.sh branch ...

set -x
git fetch --all
for branch in "$@"; do
    git checkout "$branch"      || exit 1
    git rebase "origin/$branch" || exit 1
done
Fred Foo
źródło
4
Prawdopodobnie najlepiej nie używać aliasów w skryptach. To również niczego nie pobiera, po prostu opiera się na już pobranej treści. Powinieneś zmienić git rebase origin/$branchna git pull, aby pobierał z odpowiedniej gałęzi śledzenia (przypuszczalnie na początku) i łączył lub rebase zgodnie z konfiguracją.
Cascabel,
@Jefromi: Zapomniałem fetch. Edytowałem; dodatkowe funkcje / poprawki, cokolwiek zależy od OP.
Fred Foo
8
Nadal uważam, że możesz chcieć użyć pull(lub sprawdzić branch.<branch>.rebase), aby przypadkowo nie zmienić podstawy gałęzi skonfigurowanej do normalnego ściągania (scalania).
Cascabel,
1
Rozważ użycie set -ezamiast, || exit 1aby interpreter zakończył działanie po pierwszym błędzie.
crishoj
18

To wciąż nie jest automatyczne, ponieważ chciałbym, aby istniała opcja - i powinna być pewna kontrola, aby upewnić się, że może się to zdarzyć tylko w przypadku aktualizacji do przodu (dlatego ręczne wykonanie ściągania jest znacznie bezpieczniejsze !!), ale poza tym możesz:

git fetch origin
git update-ref refs/heads/other-branch origin/other-branch

zaktualizować pozycję lokalnego oddziału bez konieczności sprawdzania go.

Uwaga: stracisz aktualną pozycję oddziału i przeniesiesz ją do miejsca, w którym znajduje się gałąź pochodzenia, co oznacza, że ​​jeśli chcesz połączyć, stracisz dane!

Matt Connolly
źródło
1
Właśnie tego szukałem. Zwykle nie mam nieprzypisanych zmian w wielu gałęziach i chcę po prostu zaktualizować różne lokalne gałęzie, aby pasowały do ​​pilota. To rozwiązanie jest o wiele ładniejsze niż moja zwykła metoda usuwania / ponownej kasy!
Dave Knight
1
połączone w jedno polecenie:git fetch origin other-branch:other-branch
fabb
12

Istnieje wiele odpowiedzi tutaj, ale żadna z nich nie służy git-fetchdo bezpośredniej aktualizacji lokalnego ref, co jest o wiele prostsze niż sprawdzanie oddziałów i bezpieczniejsze niż git-update-ref.

Tutaj używamy git-fetchdo aktualizacji oddziałów nieprądowych i git pull --ff-onlydla bieżącego oddziału. To:

  • Nie wymaga sprawdzania oddziałów
  • Aktualizuje oddziały tylko wtedy, gdy można je szybko przesłać dalej
  • Zgłosi się, gdy nie będzie mógł przewinąć do przodu

a oto:

#!/bin/bash
currentbranchref="$(git symbolic-ref HEAD 2>&-)"
git branch -r | grep -v ' -> ' | while read remotebranch
do
    # Split <remote>/<branch> into remote and branchref parts
    remote="${remotebranch%%/*}"
    branchref="refs/heads/${remotebranch#*/}"

    if [ "$branchref" == "$currentbranchref" ]
    then
        echo "Updating current branch $branchref from $remote..."
        git pull --ff-only
    else
        echo "Updating non-current ref $branchref from $remote..."
        git fetch "$remote" "$branchref:$branchref"
    fi
done

Z strony podręcznika dla git-fetch :

   <refspec>
       The format of a <refspec> parameter is an optional plus +, followed by the source ref <src>,
       followed by a colon :, followed by the destination ref <dst>.

       The remote ref that matches <src> is fetched, and if <dst> is not empty string, the local ref
       that matches it is fast-forwarded using <src>. If the optional plus + is used, the local ref is
       updated even if it does not result in a fast-forward update.

Określając git fetch <remote> <ref>:<ref> (bez żadnego +) otrzymujemy pobranie, które aktualizuje referencję lokalną tylko wtedy, gdy można ją szybko przesłać dalej.

Uwaga : zakłada się, że lokalne i zdalne gałęzie mają takie same nazwy (i że chcesz śledzić wszystkie gałęzie), to naprawdę powinno używać informacji o tym, jakie lokalne gałęzie masz i jakie są skonfigurowane do śledzenia.

Kwornian
źródło
1
„Aktualizuje oddziały tylko wtedy, gdy można je szybko przewijać do przodu - jakie jest znaczenie szybkiego przewijania do przodu? Jeśli chcę mieć najnowsze źródła we wszystkich moich oddziałach, dlaczego miałbym się przejmować szybkim przesyłaniem dalej, czy nie? Takie rzeczy sprawiają, że śmieję się z Git i jego Fanboi. Nie możesz tego zrobić za pomocą jednego polecenia. Zamiast tego musisz wykonać c*nkroki (zamiast 1), gdzie cjest pewna liczba powtarzanych poleceń i nliczba gałęzi.
jww
@jww Nie pomaga „śmiać się z Gita i jego Fanboi” [sic], gdy jest to VCS, z którego korzysta większość świata. Ale dygresję ... Myślę, że w kontekście tego rodzaju skryptu „globalnego ściągania” rozsądne jest, aby nie podejmować prób wprowadzania zmian w gałęziach innych niż bieżące, jeśli występują konflikty scalania.
Ville
To było pomocne, dziękuję. Jedyne co mi się nie podoba to, że stworzyli oddział lokalnie na każdym zdalnym oddziału (w tym te, w których nie jestem zainteresowany), więc zmieniłem git branch -r | grep -v ' -> ' | while read remotebranchaby git branch -r | grep -v ' -> ' | grep -f <(git branch | cut -c 3- | awk '{print "\\S*/"$0"$"}') | while read remotebranchograniczyć je do oddziałów, które już posiadam lokalnie. Dodałem też git fetch --prunena początku, aby zaktualizować listę zdalnych gałęzi przed zrobieniem czegokolwiek, co pozwala uniknąć ostrzeżeń.
Nate Cook
11

Ten problem nie został (jeszcze) rozwiązany, przynajmniej nie łatwo / bez skryptów: zobacz ten post na liście mailowej git autorstwa Junio ​​C. Hamano, wyjaśniając sytuację i podając wezwanie do prostego rozwiązania.

Głównym powodem jest to, że nie powinieneś tego potrzebować:

W przypadku git, który nie jest starożytny (tj. Wersja 1.5.0 lub nowsza), nie ma powodu, aby mieć lokalnego „dev”, który już tylko śledziłby zdalnie. Jeśli chcesz tylko spojrzeć i zobaczyć, możesz sprawdzić gałąź zdalnego śledzenia bezpośrednio na odłączonym HEAD za pomocą „ git checkout origin/dev”.

Co oznacza, że ​​jedynymi przypadkami, które musimy uczynić wygodnymi dla użytkowników, są te lokalne oddziały, które „śledzą” zdalne, gdy wprowadzasz lokalne zmiany lub planujesz je wprowadzić.

Jeśli masz lokalne zmiany w „dev”, które są zaznaczone, aby śledzić usunięcie „dev”, a jeśli jesteś w gałęzi innej niż „dev”, to nie powinniśmy nic robić po „ git fetch” aktualizacji zdalnego śledzenia „dev” . I tak nie będzie szybko przewijać do przodu

Zaproszenie do rozwiązania polegało na wybraniu opcji lub zewnętrznego skryptu do przycinania lokalnych oddziałów, które następują teraz po oddziałach zdalnego śledzenia, zamiast utrzymywania ich na bieżąco poprzez szybkie przekazywanie, tak jak zażądano oryginalnego plakatu.

A co powiesz na „ git branch --prune --remote=<upstream>”, które iteruje lokalne oddziały, a jeśli tak

(1) nie jest to obecny oddział; i
(2) jest zaznaczony do śledzenia gałęzi pobranej z <upstream>; oraz
(3) nie ma żadnych zobowiązań;

następnie usunąć tę gałąź? „ git remote --prune-local-forks <upstream>” też jest w porządku; Nie dbam o to, które polecenie tak bardzo implementuje tę funkcję.

Uwaga: od wersji 2.10 takiego rozwiązania nie ma. Zauważ, żegit remote prunepodkomendagit fetch --prunedotyczy usuwania gałęzi zdalnego śledzenia dla gałęzi, która już nie istnieje na pilocie, a nie usuwania lokalnej gałęzi, która śledzi gałąź zdalnego śledzenia (dla której gałąź zdalnego śledzenia jest odgałęzieniem powyżej).

Jakub Narębski
źródło
Zamiast publikować tylko linki, prosimy zamieszczać rzeczywistą treść, używając linków jako odniesień. Ten link jest teraz martwy. Szkoda, brzmiało obiecująco. (Zdaję sobie sprawę, że ta odpowiedź pochodzi z 2009 roku, więc jest to tylko notatka do wykorzystania w przyszłości.)
Michael
dzięki (i wow, szybka odpowiedź po tylu latach). Teraz widzę, że ten wątek stanowi „ wezwanie do prostego rozwiązania”, w przeciwieństwie do mojego pierwotnego błędnego odczytania, „ zapewnia proste rozwiązanie”.
Michael
@ michael_n: rozwinięty ... hmm, teraz widzę, że post nie dotyczył dokładnie żądanego rozwiązania, ale dotyczył problemu (przy założeniu przypadku XY).
Jakub Narębski
Hmm, zerkanie z odłączoną głową powinno być łatwiejsze, szczególnie powinno pokazywać przydatne informacje W statusie i pozwalać na szybkie przewijanie obszaru roboczego z pewnymi opiniami (jak wyciągnięte zatwierdzenia). Wtedy zastąpiłoby to tylko lokalne oddziały.
eckes
9

Istnieje wiele akceptowalnych odpowiedzi, ale niektóre instalacje mogą być nieco nieprzejrzyste dla niewtajemniczonych. Oto znacznie prostszy przykład, który można łatwo dostosować:

$ cat ~/bin/git/git-update-all
#!/bin/bash
# Update all local branches, checking out each branch in succession.
# Eventually returns to the original branch. Use "-n" for dry-run.
git_update_all() {
  local run br
  br=$(git name-rev --name-only HEAD 2>/dev/null)
  [ "$1" = "-n" ] && shift && run=echo

  for x in $( git branch | cut -c3- ) ; do
     $run git checkout $x && $run git pull --ff-only || return 2
  done

  [ ${#br} -gt 0 ] && $run git checkout "$br"
}

git_update_all "$@"

Jeśli dodasz ~/bin/gitdo PATH(zakładając, że plik jest ~/bin/git/git-update-all), możesz po prostu uruchomić:

$ git update-all
Michał
źródło
dzięki! oszczędziłeś mi godzinę gry z bash ...
8ctopus
5

Dodaj ten skrypt do .profilesystemu Mac OS X:

# Usage:
#   `git-pull-all` to pull all your local branches from origin
#   `git-pull-all remote` to pull all your local branches from a named remote

function git-pull-all() {
    START=$(git symbolic-ref --short -q HEAD);
    for branch in $(git branch | sed 's/^.//'); do
        git checkout $branch;
        git pull ${1:-origin} $branch || break;
    done;
    git checkout $START;
};

function git-push-all() {
    git push --all ${1:-origin};
};
Mikrofon
źródło
1
Czy nie powinno to najpierw przechować wszystkich zmian, a następnie je przywrócić?
Mel
5

Oto dobra odpowiedź: Jak pobrać wszystkie gałęzie git

for remote in `git branch -r`; do git branch --track $remote; done
git pull --all
Milkovsky
źródło
Dlaczego sugerujesz zrobić, git fetcha git pullnie tylko git pull?
syntagma
Dzięki. Wygląda na to, że pull pobiera wszystkie gałęzie ze wszystkich pilotów. Zmieniono
Milkovsky,
8
Spowoduje to pobranie wszystkich pilotów, ale połączy tylko bieżącą gałąź. Jeśli masz 10 pilotów, musisz ręcznie sprawdzić każdy z nich i scalić.
mpoisot,
W ten sposób wszystkie zdalne oddziały zostaną utworzone lokalnie z origin/prefiksem
Yassine ElBadaoui
3

Skrypt, który napisałem dla mojego GitBash . Wykonuje następujące czynności:

  • Domyślnie pobiera z początku dla wszystkich gałęzi, które są skonfigurowane do śledzenia pochodzenia, pozwala określić innego pilota w razie potrzeby.
  • Jeśli twoja obecna gałąź jest w stanie brudnym, to ukrywa twoje zmiany i spróbuje je przywrócić na końcu.
  • Dla każdego oddziału lokalnego skonfigurowanego do śledzenia oddziału zdalnego:
    • git checkout branch
    • git pull origin
  • Wreszcie powróci do pierwotnego oddziału i przywróci stan.

** Używam tego, ale nie przetestowałem dokładnie, używam na własne ryzyko. Zobacz przykład tego skryptu w pliku .bash_alias tutaj .

    # Do a pull on all branches that are tracking a remote branches, will from origin by default.
    # If current branch is dirty, will stash changes and reply after pull.
    # Usage: pullall [remoteName]
    alias pullall=pullAll
    function pullAll (){
     # if -h then show help
     if [[ $1 == '-h' ]]
    then
      echo "Description: Pulls new changes from upstream on all branches that are tracking remotes."
      echo 
      echo "Usage: "
      echo "- Default: pullall"
      echo "- Specify upstream to pull from: pullall [upstreamName]"
      echo "- Help: pull-all -h"
    else

     # default remote to origin
     remote="origin"
     if [ $1 != "" ]
     then
       remote=$1
     fi

     # list all branches that are tracking remote
     # git branch -vv : list branches with their upstreams
     # grep origin : keep only items that have upstream of origin
     # sed "s/^.."... : remove leading *
     # sed "s/^"..... : remove leading white spaces
     # cut -d" "..... : cut on spaces, take first item
     # cut -d splits on space, -f1 grabs first item
     branches=($(git branch -vv | grep $remote | sed "s/^[ *]*//" | sed "s/^[ /t]*//" | cut -d" " -f1))

     # get starting branch name
     startingBranch=$(git rev-parse --abbrev-ref HEAD)

     # get starting stash size
     startingStashSize=$(git stash list | wc -l)

     echo "Saving starting branch state: $startingBranch"
     git stash

     # get the new stash size
     newStashSize=$(git stash list | wc -l)

     # for each branch in the array of remote tracking branches
     for branch in ${branches[*]}
     do
       echo "Switching to $branch"
       git checkout $branch

       echo "Pulling $remote"
       git pull $remote

     done

     echo "Switching back to $startingBranch"
     git checkout $startingBranch

     # compare before and after stash size to see if anything was stashed
     if [ "$startingStashSize" -lt "$newStashSize" ]
     then
       echo "Restoring branch state"
       git stash pop
     fi
    fi
    }
filozoficzna
źródło
Czy możesz podać równoważny plik nietoperza Windows?
Jaffy,
1
@Jaffy Nie jestem pewien, ile mam czasu na ręce i nie jestem biegły w partii, ale mogę spróbować. Zamieszczę tutaj swoje postępy , może inni mogą wkroczyć i pomóc?
filozofowaffle
3

Jeśli korzystasz z systemu Windows, możesz użyć PyGitUp, który jest klonem git-upPythona. Możesz zainstalować go za pomocą pip z pip install --user git-uplub za pomocą Scoop za pomocąscoop install git-up

[4]

Simon Hartcher
źródło
3

Po prostu opublikowałem zaktualizowaną odpowiedź. git-upnie jest już utrzymywany, a jeśli czytasz dokumentację, wspominają, że funkcjonalność jest teraz dostępna w git .

Począwszy od Git 2.9, git pull --rebase --autostash robi w zasadzie to samo.

W związku z tym, jeśli aktualizujesz do wersji Git 2.9 lub nowszej, możesz użyć tego aliasu zamiast instalować git-up:

git config --global alias.up 'pull --rebase --autostash'

Możesz również ustawić to dla każdego git pullGita 2.9 (dzięki @VonC proszę zobaczyć jego odpowiedź tutaj )

git config --global pull.rebase true
git config --global rebase.autoStash true
sierpień
źródło
1
Nie potrzebujesz aliasu. Wystarczy proste ściągnięcie git, z odpowiednią konfiguracją: stackoverflow.com/a/40067353/6309
VonC
Świetne połączenia się dzięki @VonC zaktualizowałem moją odpowiedź :) może również złożyć PR do git-updokumentacji, ponieważ nie wspomnieć, że
sie
To nie aktualizuje wszystkich lokalnych oddziałów naraz, dlatego głównie używałem git-up.
ray
Zaktualizowano dokumentację git-up:)
sierpnia
3

Natknąłem się na ten sam numer tego pytania ...

Zastanawiając się nad tym, zrobiłem małą funkcję aliasu w moim .bashrcpliku:

gitPullAll() {
    for branch in `git branch | sed -E 's/^\*/ /' | awk '{print $1}'`; do
        git checkout $branch
        git pull -p
        printf "\n"
    done
    echo "Done"
}

Pracował dla mnie (:

Francisco Neto
źródło
2

Jeśli refs / heads / master można szybko przewinąć do refs / remotes / foo / master , wyjście z

git merge-base refs/heads/master refs/remotes/foo/master

powinien zwrócić identyfikator SHA1, który odnosi się do / heads / master points. Dzięki temu możesz utworzyć skrypt, który automatycznie aktualizuje wszystkie lokalne oddziały, które nie miały przypisanych poleceń przekierowania.

Ten mały skrypt powłoki (nazwałem go git-can-ff ) ilustruje, jak można to zrobić.

#!/bin/sh

set -x

usage() {
    echo "usage: $(basename $0) <from-ref> <to-ref>" >&2
    exit 2
}

[ $# -ne 2 ] && usage

FROM_REF=$1
TO_REF=$2

FROM_HASH=$(git show-ref --hash $FROM_REF)
TO_HASH=$(git show-ref --hash $TO_REF)
BASE_HASH=$(git merge-base $FROM_REF $TO_REF)

if [ "$BASE_HASH" = "$FROM_HASH" -o \
     "$BASE_HASH" = "$FROM_REF" ]; then
    exit 0
else
    exit 1
fi
Hillu
źródło
Co sugerujesz przez ten komentarz?
hillu
Sam nie jestem w stanie napisać skryptu, który sugeruje Hillu, i nie jestem wystarczająco pewny swojej wiedzy git, aby użyć git-merge-base.
Norman Ramsey,
2
Obawiam się, że nie rozumiem modelu wystarczająco dobrze, aby wykorzystać skrypt, który jest tak uprzejmy. Wystarczy, aby ktoś chciał przejść na merkurcial.
Norman Ramsey
Osobiście uznałem artykuł Tommiego Virtanena „Git dla informatyków” za bardzo pomocny w zapoznaniu się z modelem i terminologią gita.
hillu
2

Aby uzupełnić odpowiedź Matta Connolly'ego, jest to bezpieczniejszy sposób aktualizowania odniesień do lokalnego oddziału, który można szybko przesłać bez konieczności sprawdzania oddziału. Nie aktualizuje gałęzi, których nie można szybko przesłać (tj. Które się rozeszły), i nie aktualizuje gałęzi, która jest obecnie wyewidencjonowana (ponieważ wtedy kopia robocza również powinna zostać zaktualizowana).

git fetch

head="$(git symbolic-ref HEAD)"
git for-each-ref --format="%(refname) %(upstream)" refs/heads | while read ref up; do
    if [ -n "$up" -a "$ref" != "$head" ]; then
        mine="$(git rev-parse "$ref")"
        theirs="$(git rev-parse "$up")"
        base="$(git merge-base "$ref" "$up")"
        if [ "$mine" != "$theirs" -a "$mine" == "$base" ]; then
            git update-ref "$ref" "$theirs"
        fi
    fi
done
Bruno De Fraine
źródło
2

Nieco inny skrypt, który tylko przewija gałęzie, których nazwy pasują do ich gałęzi upstream. Aktualizuje również bieżący oddział, jeśli możliwe jest szybkie przewijanie do przodu.

Upewnij się, że wszystkie odgałęzienia gałęzi są ustawione poprawnie, uruchamiając git branch -vv. Ustaw gałąź upstream za pomocągit branch -u origin/yourbanchname

Skopiuj-wklej do pliku i chmod 755:

#!/bin/sh

curbranch=$(git rev-parse --abbrev-ref HEAD)

for branch in $(git for-each-ref refs/heads --format="%(refname:short)"); do
        upbranch=$(git config --get branch.$branch.merge | sed 's:refs/heads/::');
        if [ "$branch" = "$upbranch" ]; then
                if [ "$branch" = "$curbranch" ]; then
                        echo Fast forwarding current branch $curbranch
                        git merge --ff-only origin/$upbranch
                else
                        echo Fast forwarding $branch with origin/$upbranch
                        git fetch . origin/$upbranch:$branch
                fi
        fi
done;
qwertzguy
źródło
2

Następujący jednowierszowy przesyła dalej wszystkie gałęzie, które mają gałąź nadrzędną, jeśli to możliwe, aw przeciwnym razie drukuje błąd:

git branch \
  --format "%(if)%(upstream:short)%(then)git push . %(upstream:short):%(refname:short)%(end)" |
  sh

Jak to działa?

Korzysta z niestandardowego formatu git branchpolecenia. Dla każdej gałęzi, która ma gałąź wyższą, drukuje linię o następującym wzorze:

git push . <remote-ref>:<branch>

Można to przesłać bezpośrednio sh(zakładając, że nazwy gałęzi są dobrze uformowane). Pomiń| sh aby zobaczyć, co robi.

Ostrzeżenia

One-liner nie skontaktuje się z Twoimi pilotami. Wydaj a git fetchlub git fetch --allprzed jego uruchomieniem.

Aktualnie wyewidencjonowany oddział nie zostanie zaktualizowany komunikatem typu

! [remote rejected] origin/master -> master (branch is currently checked out)

W tym celu możesz skorzystać z regularnego git pull --ff-only.

Alias

Dodaj następującą do .gitconfigtak, że git fftwykonuje tę komendę:

[alias]
        fft = !sh -c 'git branch --format \"%(if)%(upstream:short)%(then)git push . %(upstream:short):%(refname:short)%(end)\" | sh' -

Zobacz także moje .gitconfig. Alias ​​to skrót od „szybkiego przewijania do przodu (gałęzie)”.

krlmlr
źródło
Jest to rozwiązanie dobre, choć myślę, że będę używał do hubsoluction propsed przez @John za to lepsze wyjście.
Didier L,
Jest to szybkie, proste i faktycznie działa! Zaskakuje mnie to, git pushże jest całkowicie przeciwne do tego, czego można się spodziewać. Jaki jest sekret?
BrandonLWhite
@BrandonLWhite: Nie rozumiem pytania. Czego oczekujesz git push?
krlmlr
git pushma semantykę przesyłania - mam pewne zatwierdzenia lokalnie, które chcę wysłać w górę. git pullma semantykę pobierania - chcę uzyskać jakieś zdalne polecenia zatwierdzające w moim lokalnym oddziale. Ponieważ mówimy o pobieraniu nowych zatwierdzeń ze zdalnego do lokalnego, git pulljest oczywistym wyborem. Ale nie, ta sztuczka wykorzystuje git push. W jaki sposób git pushpowodują zdalne zmiany w moim oddziale lokalnym ?!
BrandonLWhite
git pushmoże być również użyty do aktualizacji lokalnych oddziałów, o ile jest to aktualizacja do przodu.
krlmlr
1

Skrypt z @larsmans, nieco ulepszony:

#!/bin/sh

set -x
CURRENT=`git rev-parse --abbrev-ref HEAD`
git fetch --all
for branch in "$@"; do
  if ["$branch" -ne "$CURRENT"]; then
    git checkout "$branch" || exit 1
    git rebase "origin/$branch" || exit 1
  fi
done
git checkout "$CURRENT" || exit 1
git rebase "origin/$CURRENT" || exit 1

To po zakończeniu pozostawia kopię roboczą wypisaną z tego samego oddziału co przed wywołaniem skryptu.

git pullWersja:

#!/bin/sh

set -x
CURRENT=`git rev-parse --abbrev-ref HEAD`
git fetch --all
for branch in "$@"; do
  if ["$branch" -ne "$CURRENT"]; then
    git checkout "$branch" || exit 1
    git pull || exit 1
  fi
done
git checkout "$CURRENT" || exit 1
git pull || exit 1
Saran
źródło
1

Wygląda na to, że wiele innych wniosło podobne rozwiązania, ale pomyślałem, że podzielę się tym, co wymyśliłem i zaprosię innych do współpracy. To rozwiązanie ma ładne kolorowe wyjście, z wdziękiem obsługuje bieżący katalog roboczy i jest szybkie, ponieważ nie wykonuje żadnych transakcji i pozostawia katalog roboczy w takcie. Jest to również skrypt powłoki bez żadnych zależności innych niż git. (do tej pory testowane tylko na OSX)

#!/usr/bin/env bash

gitup(){    
RED='\033[33;31m'
YELLO='\033[33;33m'
GREEN='\033[33;32m'
NC='\033[0m' # No Color

HEAD=$(git rev-parse HEAD)
CHANGED=$(git status --porcelain | wc -l)

echo "Fetching..."
git fetch --all --prune &>/dev/null
for branch in `git for-each-ref --format='%(refname:short)' refs/heads`; do

    LOCAL=$(git rev-parse --quiet --verify $branch)
    if [ "$HEAD" = "$LOCAL" ] && [ $CHANGED -gt 0 ]; then
        echo -e "${YELLO}WORKING${NC}\t\t$branch"
    elif git rev-parse --verify --quiet $branch@{u}&>/dev/null; then
        REMOTE=$(git rev-parse --quiet --verify $branch@{u})
        BASE=$(git merge-base $branch $branch@{u})

        if [ "$LOCAL" = "$REMOTE" ]; then
           echo -e "${GREEN}OK${NC}\t\t$branch" 
        elif [ "$LOCAL" = "$BASE" ]; then
            if [ "$HEAD" = "$LOCAL" ]; then
                git merge $REMOTE&>/dev/null
            else
                git branch -f $branch $REMOTE
            fi
            echo -e "${GREEN}UPDATED${NC}\t\t$branch"
        elif [ "$REMOTE" = "$BASE" ]; then
            echo -e "${RED}AHEAD${NC}\t\t$branch"
        else
            echo -e "${RED}DIVERGED${NC}\t\t$branch"
        fi
    else
        echo -e "${RED}NO REMOTE${NC}\t$branch"
    fi
done
}

https://github.com/davestimpert/gitup

Niestety wydaje mi się, że wymyśliłem tę samą nazwę, co inne narzędzie powyżej.

Stimp
źródło
2
Czy to ty to napisałeś? Jeśli tak, proszę ujawnić swoją przynależność, tj. Powiedzieć nam, jak jesteś z nią związany. Proszę przeczytać więcej na ten temat , aby uzyskać więcej informacji. Szczególnie nie mów - pokaż! ; Powiedz nam, jakie części skryptu i jak / dlaczego rozwiązuje problem.
Keale
1
Tak, napisałem to. Podałem powyższe źródło do szybkiego skopiowania i wklejenia do twojego .bashrc lub .zshrc.
Stimp
To dobre rozwiązanie i działa dobrze. Nikt nie zwrócił uwagi?
Ville
1

Można to zrobić za pomocą poniższego skryptu ... Najpierw pobierze wszystkie gałęzie i pobierze je pojedynczo, a następnie zaktualizuje samo.

#!/bin/bash
git branch -r | grep -v '\->' | while read remote; do git branch --track 
"${remote#origin/}" "$remote"; done

set -x
CURRENT=`git rev-parse --abbrev-ref HEAD`
git fetch --all
branch_name=$(git branch | awk '{print $1" "}' | grep -v '*' | xargs)
for branch in $branch_name; do
   git checkout "$branch" || exit 1
   git rebase "origin/$branch" || exit 1
   git pull origin $branch|| exit 1
done
git checkout "$CURRENT" || exit 1
git pull || exit 1
Vikash Singh
źródło
Dodaj wyjaśnienie dotyczące punktacji swojej odpowiedzi
fool-dev
1

Nie możesz tego zrobić za pomocą jednego polecenia git, ale możesz zautomatyzować to za pomocą jednej linii bash.

Aby bezpiecznie zaktualizować wszystkie gałęzie za pomocą jednej linii, oto co robię:

git fetch --all && for branch in $(git branch | sed '/*/{$q;h;d};$G' | tr -d '*') ; do git checkout $branch && git merge --ff-only || break ; done
  • Jeśli nie może przewinąć do przodu jednej gałęzi lub napotka błąd, zatrzyma się i pozostawi cię w tej gałęzi, abyś mógł odzyskać kontrolę i scalić ręcznie.

  • Jeśli wszystkie gałęzie mogą być przewijane do przodu, zakończy się na gałęzi, w której się znajdowałeś, pozostawiając cię tam, gdzie byłeś przed aktualizacją.

Objaśnienia:

Dla lepszej czytelności można go podzielić na kilka wierszy:

git fetch --all && \
for branch in $(git branch | sed '/*/{$q;h;d};$G' | tr -d '*')
    do git checkout $branch && \
    git merge --ff-only || break
done
  1. git fetch --all && ... => Pobiera wszystkie referencje ze wszystkich pilotów i kontynuuje następną komendę, jeśli nie wystąpił błąd.

  2. git branch | sed '/*/{$q;h;d};$G' | tr -d '*'=> Z wyjścia git branch, sedpodejmuje linii z *i przenieść go do końca (tak, że obecny oddział będzie aktualizowana na końcu). Następnie trpo prostu usuń *.

  3. for branch in $(...) ; do git checkout $branch && git merge --ff-only || break ; done=> Dla każdej nazwy gałęzi uzyskanej z poprzedniego polecenia, sprawdź tę gałąź i spróbuj połączyć się z przewijaniem do przodu. Jeśli się nie powiedzie, breakjest wywoływane, a polecenie zatrzymuje się tutaj.

Oczywiście, można zastąpić git merge --ff-onlyze git rebasejeśli to, co chcesz.

Wreszcie możesz umieścić go w swoim bashrc jako alias:

alias git-pull-all='git fetch --all && for branch in $(git branch | sed '\''/*/{$q;h;d};$G'\'' | tr -d "*") ; do git checkout $branch && git merge --ff-only || break ; done'

Lub jeśli boisz się zepsuć „i” lub po prostu wolisz zachować czytelność składniową w swoim edytorze, możesz zadeklarować to jako funkcję:

git-pull-all()
{
    git fetch --all && for branch in $(git branch | sed '/*/{$q;h;d};$G' | tr -d '*') ; do git checkout $branch && git merge --ff-only || break ; done
}

Premia:

Dla tych, którzy chcieliby wyjaśnienia ze sed '/*/{$q;h;d};$G'strony:

  • /*/=> Wyszukaj linię za pomocą *.

  • {$q => Jeśli znajduje się w ostatnim wierszu, wyjdź (nie musimy nic robić, ponieważ bieżąca gałąź jest już ostatnią na liście).

  • ;h;d} => W przeciwnym razie zapisz linię w buforze wstrzymania i usuń ją w bieżącej pozycji na liście.

  • ;$G => Gdy dojdzie do ostatniego wiersza, dodaj zawartość bufora wstrzymania.

Bouty
źródło
Możesz uniknąć szaleństwa niekończących się linii i &&ustawiając się set -ew górnej części skryptu.
mcepl
0

Czy „git pull --all” może zaktualizować wszystkie moje lokalne oddziały?

Nie, nie może. Do szybkiego przewijania napisałem tylko małe narzędzie do tego. https://github.com/changyuheng/git-fast-forward-all

Zalety tego narzędzia:

  1. Obsługuje wiele pilotów w jednym repozytorium. ( hub syncobecnie nie obsługuje wielu pilotów).
  2. Obsługuje posiadanie różnych nazw w lokalnym oddziale i odpowiadających gałęzi zdalnego śledzenia.
  3. Znacznie szybciej niż inne skrypty, które pobierają zdalnie dla każdej gałęzi.
  4. Brak podatnego na błędy parsowania / edycji wyrażeń regularnych.
changyuheng
źródło
Możesz uniknąć uderzenia w sieć za pomocą git fetch . refspec. .Mówi się pobrać z aktualnego repozytorium zamiast z jednego pilota.
hugomg
-1

Od wersji 2.9:

git pull --rebase --autostash

Zobacz https://git-scm.com/docs/git-rebase

Automatycznie utwórz tymczasową skrytkę przed rozpoczęciem operacji i zastosuj ją po zakończeniu operacji. Oznacza to, że możesz uruchomić rebase na brudnym drzewie roboczym. Używaj jednak z rozwagą: ostateczna aplikacja skrytki po udanej zmianie bazy może doprowadzić do nietrywialnych konfliktów.

nha
źródło
-1

W rzeczywistości z git version 1.8.3.1działa:

[root@test test]# git br
* master
  release/0.1
  update
[root@test test]# git pull --rebase
remote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 9 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (9/9), done.
From http://xxx/scm/csdx/test-git
   d32ca6d..2caa393  release/0.1 -> origin/release/0.1
Current branch master is up to date.
[root@test test]# git --version
git version 1.8.3.1

W gałęzi głównej możesz zaktualizować wszystkie inne gałęzie. @Cascabel

Nie wiem, która wersja go zepsuje / naprawi, w 2.17 (której używam) może działać.

daixiang0
źródło