Có thể kéo git kéo - tất cả cập nhật tất cả các chi nhánh địa phương của tôi?


473

Tôi thường có ít nhất 3 chi nhánh từ xa: bậc thầy, dàn dựng và sản xuất. Tôi có 3 chi nhánh địa phương theo dõi những chi nhánh từ xa.

Cập nhật tất cả các chi nhánh địa phương của tôi là tẻ nhạt:

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

Tôi rất thích có thể thực hiện một "git pull -all", nhưng tôi đã không thể làm cho nó hoạt động. Nó dường như thực hiện một "tìm nạp - tất cả", sau đó cập nhật (chuyển tiếp nhanh hoặc sáp nhập) nhánh làm việc hiện tại, nhưng không phải là các nhánh cục bộ khác.

Tôi vẫn bị mắc kẹt khi chuyển đổi thủ công đến từng chi nhánh địa phương và cập nhật.


8
Bạn có muốn cập nhật tự động các nhánh theo dõi cục bộ chỉ trong trường hợp chuyển tiếp nhanh không? Ypu nên, vì sự hợp nhất có thể có mâu thuẫn mà bạn sẽ phải giải quyết ...
Jakub Narębski

34
Giả sử có 300 đô la bảo thủ trong thời gian tư vấn để giải quyết vấn đề này, vấn đề duy nhất này đã khiến các công ty tốn 23.242.800 đô la khi sử dụng số lượt xem là 77.476. Bây giờ hãy xem xét câu hỏi stackoverflow.com/questions/179123/ này và tất cả những câu hỏi khác. Ồ
Luke Puplett

16
@Luke Bạn là người đầu tiên tôi được nghe chỉ ra thời gian cố gắng làm cho git làm những gì chúng tôi muốn chi phí cho các công ty. Những điều đơn giản này nên tự động và đơn giản đến mức tôi không phải mở trình duyệt để đọc diễn đàn, IMO.
Samuel

13
@LukePuplett Có gần gấp 9 lần số câu hỏi về git trên SO so với Mercurial, và phần lớn trước đây dường như là "làm thế nào để tôi thực hiện <thao tác đơn giản> trong git?". Điều đó chỉ ra rằng git hoặc được thiết kế tồi, tài liệu kém, không trực quan hoặc cả ba.
Ian Kemp

26
@IanKemp Tôi không chắc chắn an toàn khi đưa ra yêu cầu đó mà không biết nhân khẩu học của SO. Nếu Mercurial không được sử dụng phổ biến ở đây hoặc nếu người dùng của nó sử dụng các diễn đàn khác để hỏi về nó, tôi sẽ thấy kết quả tương tự. :) Có gấp 51 lần số câu hỏi trên Javascript so với hội - vì vậy có thể không phải lúc nào cũng chính xác để đánh giá các công cụ chỉ bằng các loại số liệu này.
danShumway

Câu trả lời:


188

Hành vi bạn mô tả pull --allchính xác như mong đợi, mặc dù không nhất thiết phải hữu ích. Tùy chọn được truyền cùng với git fetch, sau đó tìm nạp tất cả các ref từ tất cả các điều khiển từ xa, thay vì chỉ một cái cần thiết; pullsau đó hợp nhất (hoặc trong trường hợp của bạn, khởi động lại) nhánh đơn thích hợp.

Nếu bạn muốn kiểm tra các chi nhánh khác, bạn sẽ phải kiểm tra chúng. Và đúng vậy, việc hợp nhất (và nổi loạn) hoàn toàn đòi hỏi một cây công việc, vì vậy chúng không thể được thực hiện mà không kiểm tra các nhánh khác. Bạn có thể kết thúc các bước được mô tả của mình thành một tập lệnh / bí danh nếu bạn muốn, mặc dù tôi khuyên bạn nên tham gia các lệnh &&để một trong số chúng thất bại, nó sẽ không cố gắng tiếp tục.


2
Nếu bạn đưa ra một dòng lệnh ví dụ, tôi sẽ bỏ phiếu. Tôi có vấn đề này trên github. Tôi đã tạo một nhánh trên UI. Bây giờ tôi cần địa phương của tôi để hiển thị chi nhánh. git kéo --all; chi nhánh git ... argh ... lệnh: git chi nhánh -a
mariotti

@mariotti Phụ thuộc vào những gì bạn đang cố gắng thực hiện và nó không thực sự rõ ràng từ nhận xét của bạn. Bạn có thể tốt nhất nên hỏi một câu hỏi mới.
Cascabel

1
Hoặc @Jefromi .. đưa ra một ví dụ. Tôi thực sự đã đồng ý với bạn.
mariotti

3
@mariotti Điểm của câu trả lời này là các lệnh tích hợp không thực sự làm những gì OP yêu cầu, vì vậy trình tự các bước họ có là cần thiết. Có thể tự động hóa các bước đó (xem ví dụ câu trả lời của John) nhưng chúng phải được thực hiện. Vì vậy, nếu những gì bạn đang cố gắng thực hiện giống hệt như OP, thì thực sự không có ví dụ nào để đưa ra và nếu bạn đang cố gắng làm điều gì đó khác biệt, thì bạn nên hỏi một câu hỏi mới - đó là cách StackOverflow hoạt động! (Và nhận xét của bạn không rõ ràng, nhưng phỏng đoán tốt nhất của tôi là bạn muốn một cái gì đó khác với OP ở đây, vì vậy, câu hỏi mới.)
Cascabel 2/11/2016

Vâng, một cái gì đó khác nhau. Nhưng câu trả lời của bạn chỉ hoàn hảo cho bối cảnh. Và tôi có thể không cần hỏi nữa chỉ vì câu trả lời của bạn. Chỉ: Câu trả lời được chấp nhận sử dụng git-up, đơn giản chỉ là giao diện cho dòng lệnh git (tôi giả sử). Tôi đã hy vọng bạn có thể làm cho nó rõ ràng trong một vài dòng lệnh git. Câu trả lời hiện tại là KHÔNG git.
mariotti

206

Tôi sử dụng synctiểu ban của trung tâm để tự động hóa việc này. Tôi có alias git=hubtrong tôi .bash_profile, vì vậy lệnh tôi gõ là:

git sync

Điều này cập nhật tất cả các chi nhánh địa phương có một chi nhánh ngược dòng phù hợp. Từ trang người đàn ông:

  • Nếu chi nhánh địa phương đã lỗi thời, hãy nhanh chóng chuyển tiếp nó;
  • Nếu chi nhánh địa phương chứa công việc không được đánh giá cao, hãy cảnh báo về nó;
  • Nếu nhánh có vẻ hợp nhất và nhánh ngược dòng của nó bị xóa, hãy xóa nó.

Nó cũng xử lý stashing / unstashing những thay đổi không được cam kết trên nhánh hiện tại.

Tôi đã từng sử dụng một công cụ tương tự gọi là git-up , nhưng nó không còn được duy trì và git syncthực hiện gần như chính xác điều tương tự.


15
Còn Windows thì sao?
Violet Hươu cao cổ

6
@ TrentonD.Adams cam kết ngày và ngày tác giả là các khái niệm khác nhau. Một rebase sẽ thay đổi ngày cam kết nhưng không phải ngày tác giả (ngoại trừ trong các xung đột, trong đó ngày tác giả cũng thay đổi). Ngày tác giả phản ánh khi cây cam kết được tác giả và không nên thay đổi trong một cuộc nổi loạn không được giải thích. Ngày cam kết thay đổi vì rebase luôn tạo ra một cam kết mới. Vì vậy, ngày cam kết sẽ luôn luôn theo đúng thứ tự.
Dev

16
Để tắt hành vi khởi động lại tự động của git-up, hãy chạy git config --global git-up.rebase.auto false.
Dan Loewenherz 16/07/2015

18
@MaxYankov Nói chung lịch sử chia sẻ là cần tránh, không có gì sai với việc đánh bại các cam kết địa phương trong một lần kéo.
Dev

23
Làm lại các cam kết cục bộ là viết lại lịch sử và làm cho nó đơn giản hơn thực tế. Với rebase, bạn có thể thấy mình có mã được hợp nhất tự động nhưng không biên dịch, hoặc tệ hơn là biên dịch nhưng không hoạt động. Hợp nhất thừa nhận cách bạn đã làm việc: bạn đã thực hiện các thay đổi và kiểm tra chúng trước khi kết hợp các thay đổi của người khác và cam kết hợp nhất là một điểm rất hữu ích: đó là nơi bạn đảm bảo rằng các bộ máy khác nhau chơi tốt với nhau. Rebasing làm cho nó có vẻ như quá trình này không bao giờ xảy ra, điều này đơn giản là không đúng sự thật và là một thực tế rất nguy hiểm.
Max Yankov

39

Tôi biết câu hỏi này đã gần 3 tuổi, nhưng tôi đã tự hỏi mình câu hỏi tương tự và không tìm thấy bất kỳ giải pháp làm sẵn nào. Vì vậy, tôi đã tự tạo một tập lệnh shell git tùy chỉnh.

Nó đây rồi, git-ffwd-updatekịch bản thực hiện như sau ...

  1. nó phát hành a git remote updateđể lấy số vòng quay
  2. sau đó sử dụng git remote showđể có được danh sách các nhánh cục bộ theo dõi một nhánh từ xa (ví dụ: các nhánh có thể được sử dụng với git pull)
  3. sau đó nó kiểm tra xem có git rev-list --count <REMOTE_BRANCH>..<LOCAL_BRANCH>bao nhiêu cam kết chi nhánh địa phương đằng sau điều khiển từ xa (và ngược lại)
  4. nếu chi nhánh địa phương là 1 hoặc nhiều cam kết phía trước, nó KHÔNG thể được chuyển tiếp nhanh và cần được sáp nhập hoặc từ chối bằng tay
  5. nếu chi nhánh địa phương là 0 cam kết phía trước và 1 hoặc nhiều cam kết phía sau, nó có thể được chuyển tiếp nhanh bằng git branch -f <LOCAL_BRANCH> -t <REMOTE_BRANCH>

kịch bản có thể được gọi như:

$ 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

Kịch bản đầy đủ, nên được lưu dưới dạng git-ffwd-updatevà cần phải có trên 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 $@

1
Cảm ơn bạn cho kịch bản này. Có ai đó có thể chuyển đổi tập lệnh đó sang lô windows không?
Saariko

@Saariko tại sao bạn không sử dụng git trên vỏ cửa sổ bình thường? Nếu bạn sử dụng một cái gì đó như cygwin thì tập lệnh này sẽ hoạt động tốt ... (mặc dù tôi chưa thử nghiệm nó)
muhqu

@RyanWilcox cảm ơn, tôi đang sử dụng nó như mọi ngày (công việc-) ... ;-) bạn có thể muốn xem các tập tin dấu chấm của tôi để biết thêm các tập lệnh và bí danh liên quan đến git: github.com/muhqu/dotfiles
muhqu

@muhqu Tôi đang cố gắng sử dụng tập lệnh của bạn và tôi không biết tại sao lần đầu tiên nó hoạt động nhưng nó không hoạt động "như mong đợi" ngay bây giờ. Ví dụ, hãy xem điều này . Tại sao chủ vẫn còn 78 cam kết sau khi tôi chạy tập lệnh của bạn?
BPL

1
@muhqu Trên các phiên bản git mới hơn, -t và -l không được sử dụng cùng nhau trong một git branchcuộc gọi. Tôi đã xóa -l để thay đổi cuộc gọi thành git branch -f $LB -t $ARB >/dev/null;và bây giờ tập lệnh hoạt động như bình thường.
Radek Liska

24

Không quá khó để tự động hóa:

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

4
Có lẽ tốt nhất không sử dụng bí danh trong các tập lệnh. Điều này cũng không thực sự tìm nạp bất cứ thứ gì, chỉ phản hồi lại nội dung đã được tìm nạp. Bạn nên thay đổi git rebase origin/$branchthành git pull, để nó sẽ tìm nạp từ nhánh theo dõi thích hợp (có lẽ là về nguồn gốc) và hợp nhất hoặc rebase như được xác định bởi cấu hình.
Cascabel

@Jefromi: Tôi đã quên mất fetch. Đã chỉnh sửa; tính năng bổ sung / sửa chữa bất cứ điều gì tùy thuộc vào OP.
Fred Foo

8
Tôi vẫn nghĩ rằng bạn có thể muốn sử dụng pull(hoặc kiểm tra branch.<branch>.rebase), để bạn không vô tình khởi động lại một nhánh được thiết lập để kéo bình thường (hợp nhất).
Cascabel

1
Cân nhắc sử dụng set -ethay vì || exit 1làm cho trình thông dịch thoát khỏi lỗi đầu tiên.
crishoj

18

Điều này vẫn không tự động, vì tôi muốn có một tùy chọn cho - và cần có một số kiểm tra để đảm bảo rằng điều này chỉ có thể xảy ra đối với các cập nhật chuyển tiếp nhanh (đó là lý do tại sao thực hiện thao tác kéo an toàn hơn rất nhiều !!), nhưng hãy cẩn thận, bạn có thể:

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

để cập nhật vị trí của chi nhánh địa phương của bạn mà không cần phải kiểm tra nó.

Lưu ý: bạn sẽ mất vị trí chi nhánh hiện tại của mình và di chuyển nó đến vị trí của chi nhánh gốc, điều đó có nghĩa là nếu bạn cần hợp nhất, bạn sẽ mất dữ liệu!


1
Đây chính xác là giải pháp tôi đang tìm kiếm. Tôi thường không có các thay đổi chưa được xử lý trên nhiều chi nhánh và chỉ muốn cập nhật các chi nhánh địa phương khác nhau để phù hợp với điều khiển từ xa. Giải pháp này đẹp hơn nhiều so với phương pháp xóa / kiểm tra lại thông thường của tôi!
Dave Knight

1
kết hợp thành một lệnh:git fetch origin other-branch:other-branch
fabb

12

Có rất nhiều câu trả lời ở đây nhưng không có câu trả lời nào được sử dụng git-fetchđể cập nhật trực tiếp các ref cục bộ, điều này đơn giản hơn nhiều so với việc kiểm tra các chi nhánh và an toàn hơn git-update-ref.

Ở đây chúng tôi sử dụng git-fetchđể cập nhật các chi nhánh không hiện tại và git pull --ff-onlycho các chi nhánh hiện tại. Nó:

  • Không yêu cầu kiểm tra chi nhánh
  • Chỉ cập nhật các chi nhánh nếu chúng có thể được chuyển tiếp nhanh
  • Sẽ báo cáo khi không thể chuyển tiếp nhanh

và đây là:

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

Từ trang hướng dẫn cho 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.

Bằng cách chỉ định git fetch <remote> <ref>:<ref>(không có bất kỳ +), chúng tôi nhận được một bản cập nhật chỉ cập nhật các tham chiếu cục bộ khi nó có thể được chuyển tiếp nhanh.

Lưu ý : điều này giả sử các nhánh cục bộ và từ xa được đặt tên giống nhau (và bạn muốn theo dõi tất cả các nhánh), nó thực sự nên sử dụng thông tin về những nhánh địa phương nào bạn có và những gì chúng được thiết lập để theo dõi.


1
"Chỉ cập nhật các chi nhánh nếu chúng có thể được chuyển tiếp nhanh" - tầm quan trọng của chuyển tiếp nhanh là gì? Nếu tôi muốn các nguồn mới nhất trong tất cả các chi nhánh của mình, thì tại sao tôi phải quan tâm đến việc chuyển tiếp nhanh hay không? Những thứ như thế này khiến tôi bật cười với Git và Fanboi của nó. Bạn không thể làm điều này chỉ bằng một lệnh. Thay vào đó, bạn cần thực hiện c*ncác bước (thay vì 1), trong đó ccó một số lệnh lặp lại và nlà số nhánh.
jww

@jww Nó không giúp "cười với Git và Fanboi của nó" [sic] khi đó là phần lớn VCS trên thế giới sử dụng. Nhưng tôi lạc đề ... Tôi nghĩ trong bối cảnh của loại kịch bản "kéo toàn cầu" này, thật khôn ngoan khi không cố gắng thay đổi các nhánh không hiện tại nếu chúng có xung đột hợp nhất.
Ville

Điều này rất hữu ích, cảm ơn bạn. Chỉ có điều tôi không thích là nó tạo ra một chi nhánh tại địa phương cho mỗi chi nhánh từ xa (kể cả những người trong đó tôi không quan tâm), vì vậy tôi đã thay đổi git branch -r | grep -v ' -> ' | while read remotebranchđể git branch -r | grep -v ' -> ' | grep -f <(git branch | cut -c 3- | awk '{print "\\S*/"$0"$"}') | while read remotebranchhạn chế nó đến chi nhánh Tôi đã có tại địa phương. Ngoài ra tôi đã thêm một git fetch --prunetừ đầu để cập nhật danh sách các chi nhánh từ xa trước khi làm bất cứ điều gì, điều này tránh một số cảnh báo.
Nate Cook

11

Vấn đề này chưa được giải quyết (chưa), ít nhất là không dễ dàng / không có kịch bản: xem bài đăng này trên danh sách gửi thư git của Junio ​​C Hamano giải thích tình huống và cung cấp cuộc gọi cho một giải pháp đơn giản.

Lý do chính là bạn không cần điều này:

Với git không cổ (tức là v1.5.0 hoặc mới hơn), không có lý do gì để có "dev" cục bộ mà hoàn toàn theo dõi điều khiển từ xa nữa. Nếu bạn chỉ muốn đi tìm và xem, bạn có thể kiểm tra chi nhánh theo dõi từ xa trực tiếp trên một ĐẦU tách rời với " git checkout origin/dev".

Điều đó có nghĩa là các trường hợp duy nhất chúng tôi cần để thuận tiện cho người dùng là xử lý các nhánh cục bộ này "theo dõi" các chi nhánh từ xa khi bạn có thay đổi cục bộ hoặc khi bạn dự định có một số.

Nếu bạn có các thay đổi cục bộ trên "dev" được đánh dấu để theo dõi "dev" và nếu bạn ở một nhánh khác với "dev", thì chúng ta không nên làm gì sau khi " git fetch" cập nhật "dev" theo dõi từ xa . Nó sẽ không nhanh chóng chuyển tiếp

Lời kêu gọi giải pháp là một tùy chọn hoặc tập lệnh bên ngoài để cắt tỉa các nhánh cục bộ theo sau các nhánh theo dõi từ xa, thay vì giữ cho chúng được cập nhật bằng cách chuyển tiếp nhanh, như áp phích ban đầu được yêu cầu.

Vậy làm thế nào về " git branch --prune --remote=<upstream>" lặp đi lặp lại trên các chi nhánh địa phương, và nếu

(1) nó không phải là chi nhánh hiện tại; và
(2) nó được đánh dấu để theo dõi một số nhánh được lấy từ <thượng nguồn>; và
(3) nó không có bất kỳ cam kết nào;

Sau đó loại bỏ chi nhánh đó? " git remote --prune-local-forks <upstream>" cũng tốt; Tôi không quan tâm đến lệnh nào thực hiện tính năng đó nhiều.

Lưu ý: kể từ git 2.10, không có giải pháp nào như vậy tồn tại. Lưu ý rằnggit remote prunetiểu ban vàgit fetch --prunevề việc loại bỏ nhánh theo dõi từ xa cho nhánh không còn tồn tại trên remote, không phải về việc loại bỏ nhánh cục bộ theo dõi nhánh theo dõi từ xa (mà nhánh theo dõi từ xa là nhánh ngược dòng).


Thay vì chỉ đăng liên kết, vui lòng đăng nội dung thực tế, sử dụng liên kết làm tài liệu tham khảo. Liên kết đó bây giờ đã chết. Quá tệ, nghe có vẻ hứa hẹn. (Tôi nhận ra câu trả lời này là từ năm 2009, vì vậy đây chỉ là một ghi chú để tham khảo trong tương lai.)
michael

cảm ơn (và wow, phản ứng nhanh sau nhiều năm). Bây giờ tôi thấy rằng chủ đề này là " kêu gọi một giải pháp đơn giản" trái ngược với cách đọc sai ban đầu của tôi, " cung cấp một giải pháp đơn giản".
michael

@michael_n: đã mở rộng ... hmm, bây giờ tôi thấy rằng bài đăng không chính xác về giải pháp được yêu cầu, nhưng đó là về vấn đề (giả sử trường hợp vấn đề XY).
Jakub Narębski

Hmm, nhìn trộm với đầu tách rời nên được thực hiện dễ dàng hơn, đặc biệt là nó sẽ hiển thị thông tin hữu ích Trong trạng thái và ám chỉ để chuyển nhanh không gian làm việc với một số phản hồi (như cam kết đã kéo). Sau đó, nó sẽ là một sự thay thế cho các chi nhánh địa phương chỉ đọc.
eckes

9

Có rất nhiều câu trả lời chấp nhận được ở đây, nhưng một số hệ thống ống nước có thể hơi mờ đối với người không quen. Đây là một ví dụ đơn giản hơn nhiều có thể dễ dàng được tùy chỉnh:

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

Nếu bạn thêm ~/bin/gitvào PATH(giả sử tệp là ~/bin/git/git-update-all), bạn có thể chạy:

$ git update-all

cảm ơn! bạn đã tiết kiệm cho tôi một giờ chơi với bash ...
8ctopus

5

Thêm kịch bản này để .profiletrên 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};
};

1
Không nên bỏ qua tất cả các thay đổi trước, sau đó khôi phục chúng?
Mel

5

Đây là một câu trả lời hay: Làm thế nào để lấy tất cả các nhánh git

for remote in `git branch -r`; do git branch --track $remote; done
git pull --all

Tại sao bạn đề nghị làm git fetchgit pull, thay vì chỉ git pull?
cú pháp

Cảm ơn. Dường như pull lấy tất cả các nhánh từ tất cả các điều khiển từ xa. Thay đổi nó
milkovsky

8
Điều này sẽ tìm nạp tất cả các điều khiển từ xa, nhưng nó sẽ chỉ hợp nhất các chi nhánh hiện tại. Nếu bạn có 10 điều khiển từ xa, bạn sẽ cần kiểm tra thủ công từng cái và hợp nhất.
mpoisot

Làm điều này sẽ tạo ra tất cả các chi nhánh từ xa cục bộ với origin/tiền tố
Yassine ElBadaoui

3

Một kịch bản tôi đã viết cho GitBash của tôi . Hoàn thành những điều sau:

  • Theo mặc định, kéo từ gốc cho tất cả các nhánh được thiết lập để theo dõi nguồn gốc, cho phép bạn chỉ định một điều khiển từ xa khác nếu muốn.
  • Nếu chi nhánh hiện tại của bạn ở trạng thái bẩn thì nó sẽ thay đổi các thay đổi của bạn và sẽ cố gắng khôi phục các thay đổi này vào cuối.
  • Đối với mỗi nhánh cục bộ được thiết lập để theo dõi một nhánh từ xa sẽ:
    • git checkout branch
    • git pull origin
  • Cuối cùng, sẽ đưa bạn trở lại chi nhánh ban đầu và khôi phục trạng thái.

** Tôi sử dụng cái này nhưng chưa kiểm tra kỹ, sử dụng có nguy cơ. Xem ví dụ về tập lệnh này trong tệp .bash_alias tại đây .

    # 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
    }

Bạn có thể cung cấp tệp bat Windows tương đương?
Jaffy 16/07/2015

1
@Jaffy Không chắc tôi có bao nhiêu thời gian trên tay và tôi không thông thạo hàng loạt nhưng tôi có thể thử. Tôi sẽ đăng tiến bộ của tôi ở đây , có lẽ những người khác có thể bước vào và giúp đỡ?
philosowaff 16/07/2015

3

Nếu bạn đang dùng Windows, bạn có thể sử dụng PyGitUp , một bản sao của git-upPython. Bạn có thể cài đặt nó bằng pip với pip install --user git-uphoặc thông qua Scoop bằngscoop install git-up

[4]


3

Chỉ cần đăng một câu trả lời cập nhật. git-upkhông còn được duy trì và nếu bạn đọc tài liệu, họ đề cập đến chức năng hiện có sẵn trong git .

Kể từ Git 2.9, git pull --rebase --autostash về cơ bản cũng tương tự như vậy.

Theo đó, nếu bạn cập nhật lên Git 2.9 trở lên, bạn có thể sử dụng bí danh này thay vì cài đặt git-up:

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

Bạn cũng có thể đặt cái này cho mọi thứ git pullkể từ Git 2.9 (cảm ơn @VonC vui lòng xem câu trả lời của anh ấy ở đây )

git config --global pull.rebase true
git config --global rebase.autoStash true

1
Bạn không cần một bí danh. Một lần kéo git đơn giản là đủ, với cấu hình đúng: stackoverflow.com/a/40067353/6309
VonC

Lớn gọi ra nhờ @VonC tôi cập nhật câu trả lời của tôi :) cũng có thể nộp một PR đến git-uptài liệu bởi vì họ không đề cập đến đó
Tháng Tám

Điều này không cập nhật tất cả các chi nhánh địa phương cùng một lúc, đó là lý do tại sao tôi chủ yếu sử dụng git-up.
ray

Tài liệu được cập nhật trên git-up:)
Tháng Tám

3

Tôi đã gặp vấn đề tương tự của câu hỏi này ...

Tự hỏi về nó, tôi đã thực hiện một chức năng bí danh nhỏ trong .bashrctệp của mình :

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

Đã làm cho tôi (:


2

Nếu refs / Heads / master có thể được chuyển nhanh sang refs / remote / foo / master , thì đầu ra của

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

nên trả về id SHA1 mà refs / Heads / master trỏ tới. Với điều này, bạn có thể tập hợp một tập lệnh tự động cập nhật tất cả các nhánh cục bộ không có cam kết chuyển hướng được áp dụng cho chúng.

Kịch bản shell nhỏ này (tôi gọi nó là git-can-ff ) minh họa cách nó có thể được thực hiện.

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

Bạn ngụ ý gì bởi bình luận đó?
hillu

Bản thân tôi không có khả năng viết kịch bản Hillu gợi ý, và tôi không đủ tự tin về kiến ​​thức git của mình để sử dụng git-merge-base.
Norman Ramsey

2
Tôi sợ rằng tôi không hiểu rõ mô hình đủ để khai thác kịch bản nên được cung cấp một cách tử tế. Nó đủ để khiến một người muốn chuyển sang dùng đồng bóng.
Norman Ramsey

Cá nhân tôi thấy bài báo "Git cho các nhà khoa học máy tính" của Tommi Virtanen khá hữu ích trong việc làm quen với mô hình và thuật ngữ của git.
hillu

2

Để hoàn thành câu trả lời của Matt Connolly, đây là cách an toàn hơn để cập nhật các tham chiếu chi nhánh địa phương có thể được chuyển tiếp nhanh mà không cần kiểm tra chi nhánh. Nó không cập nhật các nhánh không thể chuyển tiếp nhanh (nghĩa là đã chuyển hướng) và nó không cập nhật các nhánh hiện đang được kiểm tra (vì sau đó cũng nên cập nhật bản sao làm việc).

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

2

Một tập lệnh hơi khác chỉ các nhánh chuyển tiếp nhanh, tên của chúng khớp với nhánh ngược dòng của chúng. Nó cũng cập nhật chi nhánh hiện tại nếu có thể chuyển tiếp nhanh.

Đảm bảo rằng tất cả các nhánh ngược dòng của chi nhánh của bạn được đặt chính xác bằng cách chạy git branch -vv. Đặt nhánh ngược dòng vớigit branch -u origin/yourbanchname

Sao chép-dán vào một tập tin và 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;

2

Một lớp lót sau nhanh chóng chuyển tiếp tất cả các nhánh có nhánh ngược dòng nếu có thể và in lỗi khác:

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

Làm thế nào nó hoạt động?

Nó sử dụng một định dạng tùy chỉnh với git branchlệnh. Đối với mỗi nhánh có một nhánh ngược dòng, nó sẽ in một dòng với mẫu sau:

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

Điều này có thể được dẫn trực tiếp vào sh(giả sử rằng tên chi nhánh được hình thành tốt). Bỏ qua | shđể xem những gì nó đang làm.

Hãy cẩn thận

Một lớp lót sẽ không liên lạc với điều khiển từ xa của bạn. Phát hành một git fetchhoặc git fetch --alltrước khi chạy nó.

Chi nhánh thanh toán hiện tại sẽ không được cập nhật với một thông báo như

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

Đối với điều này, bạn có thể dùng đến thường xuyên git pull --ff-only.

Bí danh

Thêm phần sau vào của bạn .gitconfigđể git fftthực hiện lệnh này:

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

Xem thêm của tôi .gitconfig. Bí danh là một cách viết tắt của "theo dõi chuyển tiếp nhanh (các nhánh)".


Đây là một giải pháp tốt, mặc dù tôi nghĩ rằng tôi sẽ sử dụng phầnhub giải quyết được đạo cụ bởi @John cho sản phẩm tốt hơn.
Didier L

Điều này là nhanh chóng, đơn giản, và thực sự hoạt động! Mặc dù vậy, tôi cảm thấy bối rối git pushvì nó hoàn toàn trái ngược với những gì bạn mong đợi. Bí mật là gì?
BrandonLWhite

@BrandonLWhite: Tôi không hiểu câu hỏi. Bạn mong đợi điều git pushgì?
krlmlr

git pushđã tải lên ngữ nghĩa - Tôi có một số cam kết tại địa phương tôi muốn gửi ngược dòng. git pullđã tải xuống ngữ nghĩa - Tôi muốn nhận một số cam kết từ xa ngược dòng vào chi nhánh địa phương của tôi. Vì chúng ta đang nói về việc tải xuống các cam kết mới từ xa đến cục bộ, git pulllà lựa chọn rõ ràng. Nhưng không, thủ thuật này sử dụng git push. Làm thế nào git pushdẫn đến việc kéo các thay đổi từ xa đến chi nhánh địa phương của tôi?!
BrandonLWhite

git pushcũng có thể được sử dụng để cập nhật các chi nhánh địa phương, miễn là đây là bản cập nhật nhanh.
krlmlr

1

Kịch bản từ @larsmans, một chút được cải thiện:

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

Điều này, sau khi nó kết thúc, để lại bản sao làm việc được kiểm tra từ cùng một nhánh như trước khi tập lệnh được gọi.

Các git pullphiên bản:

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

1

Có vẻ như nhiều người khác đã đóng góp các giải pháp tương tự, nhưng tôi nghĩ tôi sẽ chia sẻ những gì tôi đã đưa ra và mời những người khác đóng góp. Giải pháp này có đầu ra đầy màu sắc đẹp mắt, xử lý duyên dáng thư mục làm việc hiện tại của bạn và nhanh chóng vì nó không thực hiện bất kỳ kiểm tra nào và để lại thư mục làm việc của bạn một cách khéo léo. Ngoài ra, nó chỉ là một kịch bản shell không có phụ thuộc nào ngoài git. (chỉ được thử nghiệm trên OSX cho đến nay)

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

Xin lỗi tôi dường như cũng đã đưa ra cùng tên với các công cụ khác ở trên.


2
Bạn có phải là người đã viết cái này không? Nếu vậy, xin vui lòng tiết lộ liên kết của bạn tức là cho chúng tôi biết bạn có liên quan đến nó như thế nào. Xin vui lòng đọc thêm về điều này để biết thêm thông tin. Cụ thể Đừng nói - hiển thị! ; Hãy cho chúng tôi biết những phần nào trong kịch bản của bạn và làm thế nào / tại sao nó giải quyết vấn đề.
Keale

1
Có tôi đã viết nó. Tôi đã bao gồm nguồn ở trên để sao chép nhanh dán vào .bashrc hoặc .zshrc của bạn.
Stimp

Đây là một giải pháp tốt đẹp và hoạt động tốt. Không ai đã thông báo?
Ville

1

Nó có thể được thực hiện bằng cách sử dụng tập lệnh bên dưới ... Đầu tiên, nó sẽ tìm nạp tất cả các nhánh và kiểm tra từng cái một và tự cập nhật.

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

Thêm một số lời giải thích cho việc ghi điểm câu trả lời của bạn
đánh lừa

1

Bạn không thể làm điều đó chỉ với một lệnh git nhưng bạn có thể tự động hóa nó bằng một dòng bash.

Để cập nhật an toàn tất cả các chi nhánh với một dòng, đây là những gì tôi làm:

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
  • Nếu nó không thể chuyển tiếp nhanh một nhánh hoặc gặp lỗi, nó sẽ dừng và để bạn ở nhánh đó để bạn có thể lấy lại quyền kiểm soát và hợp nhất thủ công.

  • Nếu tất cả các chi nhánh có thể được chuyển tiếp nhanh, nó sẽ kết thúc với chi nhánh bạn hiện đang ở, để lại cho bạn vị trí trước khi cập nhật.

Giải thích:

Để dễ đọc hơn, nó có thể được chia thành nhiều dòng:

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 && ... => Lấy tất cả các ref từ tất cả các điều khiển từ xa và tiếp tục với lệnh tiếp theo nếu không có lỗi.

  2. git branch | sed '/*/{$q;h;d};$G' | tr -d '*'=> Từ đầu ra của git branch, sedlấy dòng bằng a *và di chuyển đến cuối (để nhánh hiện tại sẽ được cập nhật lần cuối). Sau đó, trchỉ cần loại bỏ *.

  3. for branch in $(...) ; do git checkout $branch && git merge --ff-only || break ; done=> Đối với mỗi tên nhánh thu được từ lệnh trước, hãy kiểm tra nhánh này và thử hợp nhất với chuyển tiếp nhanh. Nếu thất bại, breakđược gọi và lệnh dừng ở đây.

Tất nhiên, bạn có thể thay thế git merge --ff-onlybằng git rebasenếu đó là những gì bạn muốn.

Cuối cùng, bạn có thể đặt nó trong bashrc dưới dạng bí danh:

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'

Hoặc nếu bạn sợ làm phiền với 'và ", hoặc đơn giản là bạn muốn giữ khả năng đọc cú pháp trong trình chỉnh sửa của mình, bạn có thể khai báo nó như một hàm:

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
}

Tặng kem:

Đối với những người muốn giải thích về sed '/*/{$q;h;d};$G'phần này:

  • /*/=> Tìm kiếm dòng có a *.

  • {$q => Nếu nó ở dòng cuối cùng, hãy thoát (chúng tôi không cần làm gì cả vì chi nhánh hiện tại đã là chi nhánh cuối cùng trong danh sách).

  • ;h;d} => Nếu không, lưu dòng trong bộ đệm giữ và xóa nó trong vị trí danh sách hiện tại.

  • ;$G => Khi đến dòng cuối cùng, nối thêm nội dung của bộ đệm giữ.


Bạn có thể tránh tất cả sự điên rồ của những dòng bất tận và &&bằng cách đặt set -eở đầu tập lệnh.
mcepl

0

Có thể kéo git kéo - tất cả cập nhật tất cả các chi nhánh địa phương của tôi?

Không, nó không thể. Để chuyển tiếp nhanh, tôi chỉ cần viết một công cụ nhỏ để làm như vậy. https://github.com/changyuheng/git-fast-forward-all

Ưu điểm của công cụ này:

  1. Hỗ trợ nhiều điều khiển từ xa trong một kho lưu trữ. ( hub synckhông hỗ trợ nhiều điều khiển từ xa tại thời điểm này.)
  2. Hỗ trợ có các tên khác nhau trên nhánh cục bộ và nhánh theo dõi từ xa tương ứng.
  3. Nhanh hơn nhiều so với các tập lệnh khác tìm nạp từ xa cho mỗi nhánh.
  4. Không có phân tích / chỉnh sửa regex dễ bị lỗi.

1
Bạn có thể tránh nhấn mạng bằng cách sử dụng git fetch . refspec. Các .nói để lấy từ kho lưu trữ hiện tại thay vì từ một từ xa.
hugomg

-1

Kể từ git 2.9:

git pull --rebase --autostash

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

Tự động tạo một ngăn tạm thời trước khi hoạt động bắt đầu và áp dụng nó sau khi hoạt động kết thúc. Điều này có nghĩa là bạn có thể chạy rebase trên một bàn làm việc bẩn. Tuy nhiên, sử dụng cẩn thận: ứng dụng stash cuối cùng sau khi rebase thành công có thể dẫn đến xung đột không hề nhỏ.


-1

Trong thực tế, với git version 1.8.3.1, nó hoạt động:

[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

Trong nhánh chính, bạn có thể cập nhật tất cả các nhánh khác. @ Nhãn

Tôi không biết phiên bản nào phá vỡ / sửa chữa nó, trong 2.17 (mà tôi sử dụng), nó có thể hoạt động.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.