Bisakah "git pull --all" memperbarui semua cabang lokal saya?

473

Saya sering memiliki setidaknya 3 cabang terpencil: master, pementasan dan produksi. Saya memiliki 3 cabang lokal yang melacak cabang-cabang terpencil itu.

Memperbarui semua cabang lokal saya membosankan:

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

Saya ingin bisa melakukan "git pull -all", tapi saya belum bisa membuatnya bekerja. Tampaknya melakukan "fetch - all", kemudian memperbarui (memajukan atau menggabungkan) cabang yang sedang bekerja, tetapi bukan cabang lokal lainnya.

Saya masih terjebak secara manual beralih ke setiap cabang lokal dan memperbarui.

Mpoisot
sumber
8
Apakah Anda ingin memperbarui cabang pelacakan lokal secara otomatis hanya dalam kasus fast-forward? Ya, seharusnya, karena penggabungan dapat menimbulkan konflik yang harus Anda selesaikan ...
Jakub Narębski
34
Dengan asumsi $ 300 konservatif dalam waktu konsultasi untuk dipusingkan dengan ini, masalah tunggal ini telah menelan biaya perusahaan $ 23.242.800 menggunakan jumlah pandangan 77.476. Sekarang pertimbangkan pertanyaan ini stackoverflow.com/questions/179123/… dan yang lainnya. Wow.
Luke Puplett
16
@ Lukas Anda adalah orang pertama yang saya dengar menunjukkan bagaimana waktu yang dihabiskan untuk membuat git melakukan apa yang kita inginkan memerlukan biaya dari perusahaan. Hal-hal sederhana ini harus otomatis dan harus sangat sederhana. Saya tidak perlu membuka browser untuk membaca forum, IMO.
Samuel
13
@LukePuplett Ada hampir ~ 9 kali lebih banyak pertanyaan tentang git pada SO dibandingkan dengan Mercurial, dan mayoritas yang sebelumnya tampaknya adalah "bagaimana saya melakukan <operasi sederhana> di git?". Itu menunjukkan bahwa git dirancang dengan buruk, kurang didokumentasikan, tidak intuitif, atau ketiganya.
Ian Kemp
26
@IanKemp Saya tidak yakin aman untuk membuat klaim itu tanpa mengetahui demografi SO. Jika Mercurial tidak seperti yang biasa digunakan di sini, atau jika para penggunanya menggunakan forum lain untuk menanyakannya, saya berharap akan melihat hasil yang sama. :) Ada ~ 51 kali lebih banyak pertanyaan tentang Javascript dibandingkan dengan Majelis - jadi mungkin tidak selalu akurat untuk menilai alat hanya dengan jenis metrik ini.
danShumway

Jawaban:

188

Perilaku yang Anda jelaskan pull --allpersis seperti yang diharapkan, meskipun tidak selalu berguna. Opsi ini diteruskan ke git fetch, yang kemudian mengambil semua referensi dari semua remote, bukan hanya yang dibutuhkan; pullkemudian menggabungkan (atau dalam kasus Anda, rebases) cabang tunggal yang sesuai.

Jika Anda ingin memeriksa cabang lain, Anda harus memeriksanya. Dan ya, penggabungan (dan rebasing) benar - benar membutuhkan pohon kerja, sehingga mereka tidak dapat dilakukan tanpa memeriksa cabang lainnya. Anda dapat membungkus langkah-langkah yang dijelaskan ke dalam skrip / alias jika Anda mau, meskipun saya sarankan untuk bergabung dengan perintah &&sehingga jika salah satu dari mereka gagal, itu tidak akan mencoba untuk membajak.

Cascabel
sumber
2
Jika Anda memberikan contoh baris perintah, saya akan memilih. Saya memiliki masalah ini di github. Saya membuat cabang di UI. Sekarang saya perlu lokal saya untuk menunjukkan cabang. git pull --semua; git branch ... argh ... perintah: git branch -a
mariotti
@mariotti Tergantung apa yang Anda coba lakukan, dan itu tidak benar-benar jelas dari komentar Anda. Anda mungkin lebih baik mengajukan pertanyaan baru.
Cascabel
1
Atau @Jefromi .. berikan contoh. Saya sebenarnya setuju dengan Anda.
mariotti
3
@mariotti Inti dari jawaban ini adalah bahwa perintah bawaan tidak benar-benar melakukan apa yang diminta OP, sehingga urutan langkah-langkah yang mereka miliki diperlukan. Dimungkinkan untuk mengotomatiskan langkah-langkah tersebut (lihat misalnya jawaban John) tetapi itu harus dilakukan. Jadi jika apa yang Anda coba lakukan persis sama dengan OP, sebenarnya tidak ada contoh untuk diberikan, dan jika Anda mencoba melakukan sesuatu yang berbeda, maka Anda harus mengajukan pertanyaan baru - begitulah cara kerja StackOverflow! (Dan komentar Anda tidak jelas, tetapi tebakan terbaik saya adalah Anda menginginkan sesuatu yang berbeda dari OP di sini, jadi ya, pertanyaan baru.)
Cascabel
Ya, sesuatu yang agak berbeda. Tetapi jawaban Anda sempurna untuk konteksnya. Dan saya mungkin tidak perlu bertanya lagi hanya karena jawaban Anda. Hanya: Jawaban yang diterima menggunakan git-up, yang hanya merupakan antarmuka untuk perintah git (saya berasumsi). Saya berharap Anda bisa membuatnya eksplisit dalam beberapa baris perintah git. Jawaban saat ini BUKAN git.
mariotti
206

Saya menggunakan syncsubcommand dari hub untuk mengotomatisasi ini. Saya miliki alias git=hubdi saya .bash_profile, jadi perintah yang saya ketik adalah:

git sync

Ini memperbarui semua cabang lokal yang memiliki cabang upstream yang cocok. Dari halaman manual:

  • Jika cabang lokal sudah usang, maju cepat;
  • Jika cabang lokal berisi pekerjaan yang tidak dicuci, peringatkan tentang hal itu;
  • Jika cabang tampaknya digabung dan cabang hulu telah dihapus, hapuslah.

Ini juga menangani stashing / unstashing perubahan yang tidak dikomit pada cabang saat ini.

Saya dulu menggunakan alat serupa yang disebut git-up , tetapi tidak lagi dipertahankan, dan git syncmelakukan hal yang persis sama.

John
sumber
15
Bagaimana dengan Windows?
Violet Giraffe
6
@ TrentonD.Adams melakukan tanggal dan tanggal penulis adalah konsep yang berbeda. Rebase akan mengubah tanggal komitmen tetapi bukan tanggal penulis (kecuali dalam konflik, di mana tanggal penulis juga berubah). Tanggal penulis mencerminkan kapan pohon komit dibuat dan tidak boleh berubah selama rebase tanpa konflik. Tanggal komit berubah karena rebase selalu membuat komit baru. Jadi tanggal komit akan selalu dalam urutan yang benar.
Dev
16
Untuk mematikan perilaku rebing otomatis git-up, jalankan git config --global git-up.rebase.auto false.
Dan Loewenherz
18
@MaxYankov Riwayat bersama Rebasing secara umum harus dihindari, tidak ada yang salah dengan rebasing komit lokal selama tarikan.
Dev
23
Memulihkan komitmen lokal adalah menulis ulang sejarah dan membuatnya lebih sederhana dari yang sebenarnya. Dengan rebase, Anda mungkin menemukan diri Anda dengan kode yang digabungkan secara otomatis tetapi tidak mengkompilasi, atau lebih buruk, mengkompilasi tetapi tidak berfungsi. Penggabungan mengakui cara Anda bekerja: Anda menerapkan perubahan dan mengujinya sebelum memasukkan perubahan orang lain, dan menggabungkan komit adalah poin yang sangat berguna: itulah tempat di mana Anda memastikan bahwa chageset yang berbeda bermain dengan baik bersama-sama. Rebasing membuatnya tampak seperti proses ini tidak pernah terjadi, yang sama sekali tidak benar dan merupakan praktik yang sangat berbahaya.
Max Yankov
39

Saya tahu pertanyaan ini hampir 3 tahun, tetapi saya bertanya pada diri sendiri pertanyaan yang sama dan tidak menemukan solusi yang sudah jadi. Jadi, saya membuat skrip shell perintah git kustom sendiri.

Ini dia, git-ffwd-updateskrip melakukan yang berikut ...

  1. itu mengeluarkan git remote updateuntuk mengambil revs akhir
  2. lalu gunakan git remote showuntuk mendapatkan daftar cabang lokal yang melacak cabang jarak jauh (mis. cabang yang dapat digunakan dengan git pull)
  3. kemudian memeriksa dengan git rev-list --count <REMOTE_BRANCH>..<LOCAL_BRANCH>berapa banyak komit cabang lokal di belakang remote (dan di depan sebaliknya)
  4. jika cabang lokal adalah 1 atau lebih berkomitmen di depan, itu TIDAK dapat diteruskan dan perlu digabung atau diubah kembali dengan tangan
  5. jika cabang lokal 0 berkomitmen di depan dan 1 atau lebih berkomitmen di belakang, itu dapat diteruskan dengan cepat git branch -f <LOCAL_BRANCH> -t <REMOTE_BRANCH>

skrip dapat disebut seperti:

$ 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

Naskah lengkap, harus disimpan sebagai git-ffwd-updatedan harus di 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
sumber
1
Terima kasih atas skrip ini. Apakah mungkin seseorang dapat mengonversi skrip tersebut ke batch Windows?
Saariko
@ Saariko mengapa kamu tidak menggunakan git di shell windows normal? Jika Anda menggunakan sesuatu seperti cygwin, skrip ini seharusnya berfungsi dengan baik ... (walaupun saya belum mengujinya)
muhqu
@RyanWilcox terima kasih, saya menggunakannya seperti setiap hari (kerja-) ... ;-) Anda mungkin ingin melihat file dot saya untuk skrip dan alias git terkait lainnya: github.com/muhqu/dotfiles
muhqu
@ muhqu Saya mencoba menggunakan skrip Anda dan saya tidak tahu mengapa itu bekerja pertama kali tetapi tidak berfungsi "seperti yang diharapkan" sekarang. Sebagai contoh, lihat ini . Mengapa master masih berkomitmen setelah saya menjalankan skrip Anda?
BPL
1
@muhqu Pada versi git yang lebih baru, -t dan -l seharusnya tidak digunakan bersama dalam satu git branchpanggilan. Saya menghapus -l untuk mengubah panggilan menjadi git branch -f $LB -t $ARB >/dev/null;dan sekarang skrip berfungsi sebagaimana mestinya.
Radek Liska
24

Tidak terlalu sulit untuk mengotomatisasi:

#!/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
sumber
4
Mungkin lebih baik tidak menggunakan alias dalam skrip. Ini juga tidak benar-benar mengambil apa pun, hanya rebases ke konten yang sudah diambil. Anda harus mengubah git rebase origin/$branchke git pull, sehingga itu akan diambil dari cabang pelacakan yang sesuai (mungkin berasal dari asal) dan menggabungkan atau rebase seperti yang ditentukan oleh konfigurasi.
Cascabel
@ Jefromi: Saya sudah lupa fetch. Telah diedit; fitur tambahan / perbaikan apa pun yang ada pada OP.
Fred Foo
8
Saya masih berpikir Anda mungkin ingin menggunakan pull(atau memeriksa branch.<branch>.rebase), sehingga Anda tidak sengaja rebase cabang yang disetel untuk menarik secara normal (bergabung).
Cascabel
1
Pertimbangkan untuk menggunakan set -ealih-alih || exit 1membuat juru bahasa keluar pada kesalahan pertama.
crishoj
18

Ini masih tidak otomatis, karena saya berharap ada opsi untuk - dan harus ada pengecekan untuk memastikan bahwa ini hanya dapat terjadi untuk pembaruan fast-forward (yang mengapa melakukan penarikan secara manual jauh lebih aman !!), tetapi selain peringatan Anda dapat:

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

untuk memperbarui posisi cabang lokal Anda tanpa harus memeriksanya.

Catatan: Anda akan kehilangan posisi cabang saat ini dan memindahkannya ke tempat asal cabang itu, yang berarti bahwa jika Anda perlu menggabungkan Anda akan kehilangan data!

Matt Connolly
sumber
1
Inilah solusi yang saya cari. Saya biasanya tidak memiliki perubahan yang belum dicopot pada beberapa cabang, dan hanya ingin memperbarui berbagai cabang lokal saya agar sesuai dengan remote. Solusi ini jauh lebih bagus daripada metode delete / checkout saya yang biasa!
Dave Knight
1
digabungkan menjadi satu perintah:git fetch origin other-branch:other-branch
fabb
12

Ada banyak jawaban di sini tetapi tidak ada yang digunakan git-fetchuntuk memperbarui referensi lokal secara langsung, yang jauh lebih sederhana daripada memeriksa cabang, dan lebih aman daripada git-update-ref.

Di sini kami gunakan git-fetchuntuk memperbarui cabang tidak lancar dan git pull --ff-onlyuntuk cabang saat ini. Itu:

  • Tidak perlu memeriksa cabang
  • Memperbarui cabang hanya jika mereka dapat diteruskan dengan cepat
  • Akan melaporkan ketika tidak bisa maju cepat

dan ini dia:

#!/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

Dari halaman manual untuk 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.

Dengan menentukan git fetch <remote> <ref>:<ref>(tanpa ada +) kami mendapatkan pengambilan yang memperbarui referensi lokal hanya ketika itu dapat diteruskan dengan cepat.

Catatan : ini mengasumsikan cabang lokal dan jauh dinamai sama (dan bahwa Anda ingin melacak semua cabang), itu harus benar-benar menggunakan informasi tentang cabang lokal yang Anda miliki dan apa yang diatur untuk dilacak.

quornian
sumber
1
"Memperbarui cabang hanya jika mereka dapat diteruskan dengan cepat" - apa pentingnya fast-forward? Jika saya ingin sumber terbaru di semua cabang saya, lalu mengapa saya harus peduli dengan penerusan cepat atau tidak? Hal-hal seperti ini yang membuat saya menertawakan Git dan Fanboi-nya. Anda tidak dapat melakukan ini hanya dengan satu perintah. Alih-alih, Anda perlu melakukan c*nlangkah - langkah (bukan 1), di mana cada sejumlah perintah yang diulang dan njumlah cabang.
jww
@jww Tidak membantu "menertawakan Git dan Fanboi-nya" [sic] ketika itu adalah VCS yang digunakan sebagian besar dunia. Tapi saya ngelantur ... Saya pikir dalam konteks naskah "tarikan global" semacam ini, sebaiknya jangan mencoba membuat perubahan pada cabang yang tidak mutakhir jika mereka menggabungkan konflik.
Ville
Ini sangat membantu, terima kasih. Satunya hal yang saya tidak suka adalah bahwa ia menciptakan cabang lokal untuk setiap cabang terpencil (termasuk yang di mana aku tidak tertarik), jadi aku berubah git branch -r | grep -v ' -> ' | while read remotebranchuntuk git branch -r | grep -v ' -> ' | grep -f <(git branch | cut -c 3- | awk '{print "\\S*/"$0"$"}') | while read remotebranchmembatasi ke cabang-cabang saya sudah punya lokal. Saya juga menambahkan git fetch --prunedi awal untuk memperbarui daftar cabang jauh sebelum melakukan apa pun, yang menghindari beberapa peringatan.
Nate Cook
11

Masalah ini belum terpecahkan (belum), setidaknya tidak mudah / tanpa skrip: lihat posting ini di milis git oleh Junio ​​C Hamano yang menjelaskan situasi dan memberikan panggilan untuk solusi sederhana.

Alasan utamanya adalah Anda tidak perlu ini:

Dengan git yang tidak kuno (yaitu v1.5.0 atau lebih baru), tidak ada alasan untuk memiliki "dev" lokal yang murni melacak remote lagi. Jika Anda hanya ingin melihat dan melihat, Anda dapat memeriksa cabang pelacakan jarak jauh langsung pada KEPALA yang terpisah dengan " git checkout origin/dev".

Yang berarti bahwa satu-satunya kasus yang kami butuhkan agar nyaman bagi pengguna adalah menangani cabang lokal yang "melacak" cabang jarak jauh ketika Anda memang memiliki perubahan lokal, atau ketika Anda berencana untuk memilikinya.

Jika Anda memiliki perubahan lokal pada "dev" yang ditandai untuk melacak penghapusan "dev", dan jika Anda berada di cabang yang berbeda dari "dev", maka kita tidak boleh melakukan apa pun setelah " git fetch" memperbarui pelacakan jarak jauh "dev" . Lagipula itu tidak akan maju cepat

Panggilan untuk solusi adalah untuk opsi atau skrip eksternal untuk memangkas cabang-cabang lokal yang mengikuti cabang-cabang pelacakan jarak jauh, daripada membuat mereka tetap up-to-date dengan penerusan cepat, seperti poster asli yang diminta.

Jadi bagaimana " git branch --prune --remote=<upstream>" yang beralih ke cabang lokal, dan jika

(1) itu bukan cabang saat ini; dan
(2) ditandai untuk melacak beberapa cabang yang diambil dari <upstream>; dan
(3) tidak memiliki komitmen sendiri;

lalu hapus cabang itu? " git remote --prune-local-forks <upstream>" juga baik-baik saja; Saya tidak peduli perintah mana yang mengimplementasikan fitur sebanyak itu.

Catatan: pada git 2.10 tidak ada solusi seperti itu. Perhatikan bahwagit remote prunesub-perintah, dangit fetch --prunetentang menghapus cabang pelacakan jarak jauh untuk cabang yang tidak lagi ada di jarak jauh, bukan tentang menghapus cabang lokal yang melacak cabang pelacakan jarak jauh (yang cabang pelacakan jarak jauh adalah cabang hulu).

Jakub Narębski
sumber
Daripada hanya memposting tautan, silakan posting konten yang sebenarnya, menggunakan tautan sebagai referensi. Tautan itu sekarang mati. Sayang sekali, kedengarannya menjanjikan. (Saya menyadari jawaban ini berasal dari 2009, jadi ini hanya catatan untuk referensi di masa mendatang.)
michael
terima kasih (dan wow, respons cepat setelah bertahun-tahun). Saya sekarang melihat bahwa utas ini adalah " panggilan untuk solusi sederhana" sebagai lawan dari salah baca asli saya, " menyediakan solusi sederhana".
michael
@ Michaelich: diperluas ... hmm, sekarang saya melihat bahwa posting itu bukan tentang solusi yang diminta, tapi itu tentang masalah (dengan asumsi kasus masalah XY).
Jakub Narębski
Hmm, mengintip dengan kepala terpisah harus dibuat lebih mudah, terutama itu harus menunjukkan informasi yang berguna dalam status dan alllow untuk memajukan ruang kerja dengan beberapa umpan balik (seperti komitmen yang ditarik). Maka itu akan menjadi pengganti cabang lokal yang hanya bisa dibaca.
eckes
9

Ada banyak jawaban yang bisa diterima di sini, tetapi beberapa pipa ledeng mungkin agak buram bagi yang belum tahu. Berikut adalah contoh yang jauh lebih sederhana yang dapat dengan mudah disesuaikan:

$ 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 "$@"

Jika Anda menambahkan ~/bin/gitke Anda PATH(dengan asumsi file tersebut ~/bin/git/git-update-all), Anda bisa menjalankan:

$ git update-all
michael
sumber
Terima kasih! Anda menyelamatkan saya satu jam bermain dengan bash ...
8ctopus
5

Tambahkan skrip ini ke .profiledi 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};
};
Mike
sumber
1
Tidakkah seharusnya ini menyembunyikan semua perubahan terlebih dahulu, dan kemudian mengembalikannya?
Mel
5

Inilah jawaban yang bagus: Cara mengambil semua cabang git

for remote in `git branch -r`; do git branch --track $remote; done
git pull --all
milkovsky
sumber
Mengapa Anda menyarankan untuk melakukan git fetchdan git pull, bukan hanya git pull?
syntagma
Terima kasih. Tampaknya menarik mengambil semua cabang dari semua remote. Mengubahnya
milkovsky
8
Ini akan mengambil semua remote, tetapi hanya akan menggabungkan cabang saat ini. Jika Anda memiliki 10 remote Anda harus secara manual checkout masing-masing dan bergabung.
mpoisot
Melakukan hal ini akan membuat semua cabang jarak jauh secara lokal dengan origin/awalan
Yassine ElBadaoui
3

Sebuah skrip yang saya tulis untuk GitBash saya . Selesaikan hal-hal berikut:

  • Secara default tarikan dari asal untuk semua cabang yang diatur untuk melacak asal, memungkinkan Anda untuk menentukan jarak jauh yang berbeda jika diinginkan.
  • Jika cabang Anda saat ini dalam keadaan kotor maka simpanan perubahan Anda dan akan mencoba untuk mengembalikan perubahan ini di akhir.
  • Untuk setiap cabang lokal yang diatur untuk melacak cabang jarak jauh akan:
    • git checkout branch
    • git pull origin
  • Akhirnya, akan mengembalikan Anda ke cabang semula dan mengembalikan status.

** Saya menggunakan ini tetapi belum diuji secara menyeluruh, gunakan dengan risiko sendiri. Lihat contoh skrip ini dalam file .bash_alias di sini .

    # 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
    }
philosowaffle
sumber
Bisakah Anda memberikan file bat Windows yang setara?
Jaffy
1
@ Jaffy Tidak yakin berapa banyak waktu yang saya miliki di tangan saya dan saya tidak super lancar dalam batch tetapi saya bisa mencobanya. Saya akan memposting kemajuan saya di sini , mungkin orang lain dapat turun tangan dan membantu?
Philosowaffle
3

Jika Anda menggunakan Windows, Anda dapat menggunakan PyGitUp yang merupakan tiruan dari git-upuntuk Python. Anda dapat menginstalnya menggunakan pip dengan pip install --user git-upatau melalui Scoop menggunakanscoop install git-up

[4]

Simon Hartcher
sumber
3

Hanya memposting jawaban yang diperbarui. git-uptidak lagi dipertahankan dan jika Anda membaca dokumentasinya, mereka menyebutkan bahwa fungsinya sekarang tersedia di git .

Pada Git 2.9, git pull --rebase --autostash pada dasarnya melakukan hal yang sama.

Karenanya, jika Anda memperbarui ke Git 2.9 atau lebih baru, Anda dapat menggunakan alias ini daripada menginstal git-up:

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

Anda juga dapat mengatur ini untuk setiap git pullper Git 2.9 (terima kasih @VonC silakan lihat jawabannya di sini )

git config --global pull.rebase true
git config --global rebase.autoStash true
agustus
sumber
1
Anda tidak perlu alias. Tarikan git sederhana sudah cukup, dengan konfigurasi yang tepat: stackoverflow.com/a/40067353/6309
VonC
Terima kasih luar biasa @VonC Saya memperbarui jawaban saya :) juga mungkin mengirimkan PR ke git-updokumentasi karena mereka tidak menyebutkan itu
Agustus
Ini tidak memperbarui semua cabang lokal sekaligus, itulah sebabnya saya terutama digunakan git-up.
ray
Dokumentasi diperbarui pada git-up:)
Agustus
3

Saya menemukan masalah yang sama dari pertanyaan ini ...

Bingung sendiri tentang hal itu, saya melakukan fungsi alias kecil di dalam .bashrcfile saya :

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

Bekerja untuk saya (:

Francisco Neto
sumber
2

Jika ref / head / master dapat diteruskan dengan cepat ke ref / remote / foo / master , output dari

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

harus mengembalikan id SHA1 yang merujuk ke / kepala / master poin. Dengan ini, Anda dapat menyusun skrip yang secara otomatis memperbarui semua cabang lokal yang tidak memiliki komitmen pengalihan yang diterapkan padanya.

Script shell kecil ini (saya menyebutnya git-can-ff ) menggambarkan bagaimana hal itu dapat dilakukan.

#!/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
sumber
Apa yang Anda maksudkan dengan komentar itu?
hillu
Saya sendiri tidak mampu menulis naskah yang disarankan Hillu, dan saya tidak cukup yakin dengan pengetahuan git saya untuk menggunakan git-merge-base.
Norman Ramsey
2
Saya khawatir saya tidak memahami model dengan cukup baik untuk mengeksploitasi skrip yang disediakan dengan baik. Sudah cukup membuat seseorang ingin beralih ke mercurcial.
Norman Ramsey
Saya pribadi menemukan artikel Tommi Virtanen "Git untuk ilmuwan komputer" cukup membantu dalam membiasakan diri dengan model dan terminologi git.
hillu
2

Untuk menyelesaikan jawaban oleh Matt Connolly, ini adalah cara yang lebih aman untuk memperbarui referensi cabang lokal yang dapat diteruskan dengan cepat, tanpa memeriksa cabang. Itu tidak memperbarui cabang yang tidak dapat diteruskan dengan cepat (yaitu yang telah menyimpang), dan itu tidak memperbarui cabang yang saat ini diperiksa (karena itu copy pekerjaan harus diperbarui juga).

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
sumber
2

Skrip yang sedikit berbeda yang hanya memajukan cabang yang namanya cocok dengan cabang hulu mereka. Ini juga memperbarui cabang saat ini jika fast-forward dimungkinkan.

Pastikan semua cabang hulu cabang Anda disetel dengan benar dengan menjalankan git branch -vv. Atur cabang hulu dengangit branch -u origin/yourbanchname

Salin-tempel ke file dan 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
sumber
2

Jalur cepat berikut meneruskan semua cabang yang memiliki cabang hulu jika mungkin, dan mencetak kesalahan jika tidak:

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

Bagaimana cara kerjanya?

Ini menggunakan format khusus dengan git branchperintah. Untuk setiap cabang yang memiliki cabang hulu, ia mencetak garis dengan pola berikut:

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

Ini dapat disalurkan langsung ke sh(dengan asumsi bahwa nama-nama cabang terbentuk dengan baik). Abaikan| sh untuk melihat apa yang dilakukannya.

Peringatan

One-liner tidak akan menghubungi remote Anda. Terbitkan git fetchatau git fetch --allsebelum menjalankannya.

Cabang yang keluar saat ini tidak akan diperbarui dengan pesan seperti

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

Untuk ini, Anda bisa menggunakan cara biasa git pull --ff-only.

Alias

Tambahkan berikut ini ke Anda .gitconfigsehingga git fftmelakukan perintah ini:

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

Lihat juga .gitconfig. Alias ​​adalah singkatan untuk "pelacakan maju cepat (cabang)".

krlmlr
sumber
Ini adalah solusi yang bagus, meskipun saya pikir saya akan menggunakan yang hubsoluction propsed oleh @ John untuk output yang lebih baik.
Didier L
Ini cepat, sederhana, dan benar-benar berfungsi! Saya bingung oleh git pushkarena itu benar-benar berlawanan dari apa yang Anda harapkan. Apa rahasianya
BrandonLWhite
@BrandonLWhite: Saya tidak mengerti pertanyaannya. Dari apa yang Anda harapkan git push?
krlmlr
git pushtelah mengunggah semantik - Saya memiliki beberapa komit secara lokal yang ingin saya kirim ke hulu. git pullmemiliki semantik unduhan - Saya ingin mendapatkan beberapa komit jarak jauh hulu ke cabang lokal saya. Karena kita sedang berbicara tentang mengunduh komit baru dari jarak jauh ke lokal, git pulladalah pilihan yang jelas. Tapi tidak, trik ini digunakan git push. Bagaimana cara git pushmenghasilkan perubahan jarak jauh ke cabang lokal saya ?!
BrandonLWhite
git pushjuga dapat digunakan untuk memperbarui cabang lokal, selama ini adalah pembaruan maju cepat.
krlmlr
1

Script dari @ larsmans, sedikit membaik:

#!/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

Ini, setelah selesai, membuat copy pekerjaan diperiksa dari cabang yang sama seperti sebelum script dipanggil.

The git pullVersi:

#!/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
sumber
1

Sepertinya banyak orang lain yang menyumbangkan solusi serupa, tetapi saya pikir saya akan membagikan apa yang saya hasilkan dan mengundang orang lain untuk berkontribusi. Solusi ini memiliki keluaran penuh warna yang bagus, dengan anggun menangani direktori kerja Anda saat ini, dan cepat karena tidak melakukan checkout, dan membiarkan direktori kerja Anda tetap aktif. Juga, ini hanyalah skrip shell tanpa dependensi selain git. (hanya diuji pada OSX sejauh ini)

#!/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

Maaf saya juga tampaknya telah datang dengan nama yang sama dengan alat lain di atas.

Stimp
sumber
2
Apakah Anda yang menulis ini? Jika demikian, harap ungkapkan afiliasi Anda, yaitu beri tahu kami bagaimana Anda terkait dengannya. Silakan baca lebih lanjut tentang ini untuk informasi lebih lanjut. Khususnya Jangan katakan - tunjukkan! ; Beri tahu kami bagian mana dari skrip Anda dan bagaimana / mengapa itu memecahkan masalah.
Keale
1
Ya saya yang menulisnya. Saya telah menyertakan sumber di atas untuk salin-tempel cepat ke .bashrc atau .zshrc Anda.
Stimp
Ini adalah solusi yang bagus dan berfungsi dengan baik. Tidak ada yang memperhatikan?
Ville
1

Ini dapat dilakukan menggunakan skrip di bawah ini ... Pertama-tama akan mengambil semua cabang dan checkout satu per satu dan memperbarui dengan sendirinya.

#!/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
sumber
Tambahkan beberapa penjelasan untuk menilai jawaban Anda
bodoh-dev
1

Anda tidak dapat melakukannya dengan hanya satu perintah git tetapi Anda dapat mengotomatiskannya dengan satu garis bash.

Untuk memperbarui semua cabang dengan satu baris dengan aman, inilah yang saya lakukan:

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
  • Jika tidak bisa memajukan satu cabang atau menemukan kesalahan, itu akan berhenti dan meninggalkan Anda di cabang itu sehingga Anda dapat mengambil kembali kontrol dan menggabungkan secara manual.

  • Jika semua cabang dapat diteruskan dengan cepat, cabang itu akan berakhir dengan cabang tempat Anda berada saat ini, membuat Anda tetap berada di tempat sebelum memperbarui.

Penjelasan:

Untuk keterbacaan yang lebih baik, dapat dibagi menjadi beberapa baris:

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 && ... => Mengambil semua referensi dari semua remote dan melanjutkan dengan perintah berikutnya jika tidak ada kesalahan.

  2. git branch | sed '/*/{$q;h;d};$G' | tr -d '*'=> Dari output git branch, sedambil baris dengan a *dan pindahkan ke akhir (sehingga cabang saat ini akan diperbarui terakhir). Kemudian trcukup hapus *.

  3. for branch in $(...) ; do git checkout $branch && git merge --ff-only || break ; done=> Untuk setiap nama cabang yang diperoleh dari perintah sebelumnya, checkout cabang ini dan cobalah untuk bergabung dengan fast-forward. Jika gagal, breakdipanggil dan perintah berhenti di sini.

Tentu saja, Anda dapat menggantinya git merge --ff-onlydengan git rebaseapa yang Anda inginkan.

Akhirnya, Anda bisa memasukkannya ke bashrc Anda sebagai 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'

Atau jika Anda takut mengacaukan 'dan ", atau Anda lebih memilih untuk menjaga keterbacaan sintaksis di editor Anda, Anda dapat mendeklarasikannya sebagai fungsi:

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
}

Bonus:

Bagi yang ingin penjelasan pada sed '/*/{$q;h;d};$G'bagian:

  • /*/=> Cari baris dengan a *.

  • {$q => Jika ada di baris terakhir, keluar (kita tidak perlu melakukan apa-apa karena cabang saat ini sudah yang terakhir dalam daftar).

  • ;h;d} => Kalau tidak, simpan baris di buffer terus dan hapus di posisi daftar saat ini.

  • ;$G => Ketika mencapai baris terakhir, tambahkan konten buffer penahan.

Bouty
sumber
Anda dapat menghindari semua kegilaan dari garis tak berujung dan &&dengan mengatur set -edi bagian atas skrip.
mcepl
0

Bisakah "git pull --all" memperbarui semua cabang lokal saya?

Tidak, tidak bisa. Untuk fast-forwarding, saya hanya menulis alat kecil untuk melakukannya. https://github.com/changyuheng/git-fast-forward-all

Keuntungan alat ini:

  1. Mendukung banyak remote dalam satu repositori. (saat hub syncini tidak mendukung beberapa remote.)
  2. Mendukung memiliki nama yang berbeda di cabang lokal dan branche pelacakan jarak jauh yang sesuai.
  3. Jauh lebih cepat daripada skrip lain yang mengambil remote untuk setiap cabang tunggal.
  4. Tidak ada penguraian / pengeditan regex yang rawan kesalahan.
changyuheng
sumber
1
Anda dapat menghindari memukul jaringan dengan menggunakan git fetch . refspec. Kata .mengatakan untuk mengambil dari repositori saat ini bukan dari yang jauh.
hugomg
-1

Mulai git 2.9:

git pull --rebase --autostash

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

Secara otomatis membuat simpanan sementara sebelum operasi dimulai, dan menerapkannya setelah operasi berakhir. Ini berarti Anda dapat menjalankan rebase di atas meja kerja yang kotor. Namun, gunakan dengan hati-hati: aplikasi simpanan terakhir setelah rebase yang berhasil dapat mengakibatkan konflik non-sepele.

nha
sumber
-1

Bahkan, dengan git version 1.8.3.1, ia berfungsi:

[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

Di cabang utama, Anda dapat memperbarui semua cabang lainnya. @Cascabel

Saya tidak tahu versi mana yang memecah / memperbaikinya, pada 2.17 (yang saya gunakan), ia bisa bekerja.

daixiang0
sumber