Может ли "git pull -all" обновить все мои локальные ветки?

У меня часто есть как минимум три удаленных ветки: мастер, постановка и производство. У меня есть 3 локальных ветки, которые отслеживают эти удаленные ветки.

Обновление всех моих локальных ветвей утомительно:

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

Мне бы хотелось, чтобы я смог сделать "git pull -all", но мне не удалось заставить его работать. Кажется, что он делает "fetch -all", затем обновляет (ускоряет или сжимает) текущую рабочую ветвь, но не другие локальные ветки.

Я все еще застрял в ручном переключении на каждую локальную ветвь и обновляю.

Ответ 1

Поведение, которое вы описываете для pull --all, точно так, как ожидалось, хотя и не обязательно полезно. Опция передается вместе с git fetch, которая затем извлекает все ссылки из всех пультов, а не только необходимые; pull затем объединяет (или в вашем случае, переустанавливает) соответствующую отдельную ветвь.

Если вы хотите проверить другие ветки, вам придется проверить их. И да, для слияния (и перезагрузки) абсолютно требуется дерево работы, поэтому они не могут быть выполнены без проверки других ветвей. Вы можете завершить описанные шаги в script/alias, если хотите, хотя я бы предложил присоединиться к командам с &&, чтобы один из них потерпел неудачу, он не будет пытаться пахать.

Ответ 2

Я использую субкоманду sync концентратора hub, чтобы автоматизировать это. У меня есть alias git=hub в моем .bash_profile, поэтому я набираю команду:

git sync

Это обновляет все локальные ветки, которые имеют соответствующую ветку восходящего направления. Со страницы руководства:

  • Если локальная ветвь устарела, перенесите ее вперед;
  • Если локальная ветвь содержит невыпущенную работу, предупредите об этом;
  • Если ветвь кажется объединенной и ее восходящая ветвь удалена, удалите ее.

Он также обрабатывает сохранение/удаление незафиксированных изменений в текущей ветки.

Раньше я использовал похожий инструмент под названием git-up, но он больше не поддерживается, и git sync делает почти то же самое.

Ответ 3

Я знаю, что этому вопросу почти 3 года, но я задавал себе тот же вопрос и не нашел никакого готового решения. Итак, я создал собственный сценарий командной оболочки git самостоятельно.

Вот и все, git-ffwd-update делает следующее...

  1. он выдает git remote update для получения последних оборотов
  2. затем использует git remote show для получения списка локальных веток, которые отслеживают удаленную ветку (например, ветки, которые можно использовать с git pull)
  3. затем он проверяет с помощью git rev-list --count <REMOTE_BRANCH>..<LOCAL_BRANCH> сколько коммитов локальная ветвь находится позади удаленной (и впереди наоборот)
  4. если локальная ветвь впереди 1 или более коммитов, она НЕ может быть быстрой -f или перенаправлена и должна быть объединена или перебазирована вручную
  5. если локальная ветвь имеет значение 0, фиксирующее впереди, и 1 или более, фиксирующуюся сзади, это может быть быстро -f или переадресовано git branch -f <LOCAL_BRANCH> -t <REMOTE_BRANCH>

скрипт можно назвать так:

$ 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

Полный сценарий должен быть сохранен как git-ffwd-update и должен находиться в PATH.

#!/bin/bash

main() {
  REMOTES="[email protected]";
  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 [email protected]

Ответ 4

Не так сложно автоматизировать:

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

set -x
git fetch --all
for branch in "[email protected]"; do
    git checkout "$branch"      || exit 1
    git rebase "origin/$branch" || exit 1
done

Ответ 5

Это все еще не автоматическое, поскольку я хотел бы иметь возможность для - и должна быть какая-то проверка, чтобы убедиться, что это может произойти только для быстрых обновлений (поэтому вручную делать притяжение намного безопаснее!!), но оговорки в стороне вы можете:

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

чтобы обновить положение вашего локального ветки, не проверяя его.

Примечание: вы потеряете свою текущую позицию ветки и переместите ее туда, где находится ветвь происхождения, а это означает, что если вам нужно слить, вы потеряете данные!

Ответ 6

Эта проблема еще не решена (по крайней мере, не легко/без скриптов): см. this post в git списке рассылки Junio ​​C Hamano, объясняющем ситуацию и предоставляющем вызов для простого решения.

Основная причина состоит в том, что вам не нужно это:

С git, который не является древним (т.е. v1.5.0 или новее), нет оснований для имеют локальные "dev" , которые просто отслеживают удаленный. Если вы только хотите для поиска и просмотра, вы можете напрямую проверить ветвь удаленного отслеживания на отдельной головке с "git checkout origin/dev".

Это означает, что единственные случаи, которые нам необходимы, чтобы сделать его удобным для пользователей должны обрабатывать эти локальные ветки, которые "отслеживают" удаленные, когда вы это делаете имеют локальные изменения или когда вы планируете иметь некоторые.

Если у вас есть локальные изменения на "dev" , которые отмечены для отслеживания удаления "dev" , и если вы находитесь на ветке, отличной от "dev" , тогда мы не должны делать что-либо после того, как "git fetch" обновляет "dev" удаленного отслеживания. Это не будет ускоренным переходом вперед

Призыв к решению был для опции или внешних script для обрезать локальные ветки, которые следуют за ветками удаленного отслеживания, а не поддерживать их в актуальном состоянии путем быстрой пересылки, как и оригинал плаката.

Итак, как насчет "git branch --prune --remote=<upstream>", который повторяется локальные ветки, а если

(1) это не текущая ветвь; и
(2) он отмечен для отслеживания некоторой ветки, взятой из <upstream> ; и
(3) он не имеет никаких коммитов сам по себе;

затем удалите эту ветку? "git remote --prune-local-forks <upstream>" - также хорошо; Меня не волнует, какая команда реализует функцию, которая много.

Примечание: с git 2.10 такого решения не существует. Обратите внимание, что подкоманда git remote prune и git fetch --prune относятся к удалению ветки удаленного отслеживания для ветки, которая больше не существует на удаленном компьютере, а не об удалении локальной ветки, которая отслеживает ветвь удаленного отслеживания (для которой ветвь удаленного отслеживания является ветвью вверх по течению).

Ответ 7

Здесь много ответов, но нет, которые используют git-fetch для непосредственного обновления локальной ссылки, что намного проще, чем проверка ветвей, и более безопасная, чем git-update-ref.

Здесь мы используем git-fetch для обновления не текущих ветвей и git pull --ff-only для текущей ветки. Это:

  • Не требует проверки ветвей
  • Обновления ветвей, только если они могут быть быстро переадресованы
  • Будет сообщать, когда он не может выполнить ускоренную перемотку вперед

и вот оно:

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

Из man-страницы для 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.

Указав git fetch <remote> <ref>:<ref> (без каких-либо +), мы получим выборку, которая обновляет локальный номер ссылки только тогда, когда ее можно быстро переслать.

Примечание: предполагается, что локальная и удаленная ветки называются одинаковыми (и что вы хотите отслеживать все ветки), она должна действительно использовать информацию о том, какие локальные ветки у вас есть и что они установлены до отслеживания.

Ответ 8

Здесь есть много приемлемых ответов, но некоторые из сантехники могут быть немного непрозрачными для непосвященных. Здесь гораздо более простой пример, который можно легко настроить:

$ 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 "[email protected]"

Если вы добавите ~/bin/git в свой PATH (при условии, что файл ~/bin/git/git-update-all), вы можете просто запустить:

$ git update-all

Ответ 10

Добавьте этот скрипт в .profile на 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};
};

Ответ 11

A script Я написал для своего GitBash. Выполняет следующее:

  • По умолчанию тянет из источника для всех ветвей, которые настроены для отслеживания координат, позволяет указать желаемый другой пульт.
  • Если ваша текущая ветка находится в грязном состоянии, она задерживает ваши изменения и попытается восстановить эти изменения в конце.
  • Для каждой локальной ветки, настроенной для отслеживания удаленной ветки, будет:
    • git checkout branch
    • git pull origin
  • Наконец, вернем вас в исходное состояние ветки и восстановления.

** Я использую это, но не тестировал полностью, используйте на свой страх и риск. См. Пример этого script в файле .bash_alias здесь.

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

Ответ 12

Если вы используете Windows, вы можете использовать PyGitUp, который является клоном git-up для Python. Вы можете установить его с помощью pip с помощью pip install --user git-up или через Scoop, используя scoop install git-up

[4]

Ответ 13

Просто отправьте обновленный ответ. git-up больше не поддерживается, и если вы читаете документацию, они отмечают, что функциональность теперь доступна в git.

По состоянию на Git 2.9, Git pull --rebase --autostash делает в основном то же самое.

Соответственно, если вы обновляетесь до Git 2.9 или новее, вы можете использовать этот псевдоним вместо установки git -up:

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

Вы также можете установить это для каждого git pull как и для Git 2.9 (спасибо @VonC, см. его ответ здесь)

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

Ответ 14

Я сталкивался с той же проблемой этого вопроса...

Задумавшись над этим, я сделал маленькую функцию псевдонима внутри моего файла .bashrc:

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

работал для меня (:

Ответ 15

Если refs/heads/master можно быстро переадресовать на refs/remotes/foo/master, вывод

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

должен вернуть идентификатор SHA1, на который указывает refs/heads/master. С этим вы можете собрать script, который автоматически обновляет все локальные ветки, у которых не было никаких отвлекающих коммитов.

Эта небольшая оболочка script (я называю ее git -can-ff) показывает, как это можно сделать.

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

Ответ 16

Чтобы завершить ответ Мэттом Коннолли, это более безопасный способ обновить локальные ссылки на ветку, которые можно быстро перенаправить, не проверяя ветвь. Он не обновляет ветки, которые не могут быть быстро перенаправлены (т.е. Расходились), и не обновляет ветвь, которая в настоящее время проверена (потому что тогда рабочая копия также должна быть обновлена).

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

Ответ 17

Немного отличается script, что только ветки с быстрым переходом, имена которых совпадают с их ветвью вверх по течению. Он также обновляет текущую ветку, если возможна быстрая перемотка вперед.

Убедитесь, что все ветки ваших ветвей установлены правильно, запустив git branch -vv. Установите ветвь восходящего потока с помощью git branch -u origin/yourbanchname

Скопировать-вставить в файл и 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;

Ответ 18

script из @larsmans, немного улучшилось:

#!/bin/sh

set -x
CURRENT=`git rev-parse --abbrev-ref HEAD`
git fetch --all
for branch in "[email protected]"; 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

Это, после завершения, оставляет рабочую копию проверенной из той же ветки, как это было до вызова script.

Версия git pull:

#!/bin/sh

set -x
CURRENT=`git rev-parse --abbrev-ref HEAD`
git fetch --all
for branch in "[email protected]"; 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

Ответ 19

Похоже, что многие другие способствовали подобным решениям, но я думал, что разделю то, что я придумал, и пригласить других вносить свой вклад. Это решение имеет приятный красочный выход, изящно обрабатывает ваш текущий рабочий каталог и быстро, потому что он не делает никаких проверок и оставляет ваш рабочий каталог в такт. Кроме того, это всего лишь оболочка script без зависимостей, отличных от git. (тестируется только на 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 [email protected]{u}&>/dev/null; then
        REMOTE=$(git rev-parse --quiet --verify [email protected]{u})
        BASE=$(git merge-base $branch [email protected]{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

Извините, я тоже, похоже, придумал то же имя, что и другой инструмент выше.

Ответ 20

Это можно сделать, используя ниже script... Сначала он будет получать все ветки и проверку по одному и обновлять сам по себе.

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

Ответ 21

Следующая однострочная строка ускоряет пересылку всех ветвей, которые имеют ветку восходящего потока, если это возможно, и печатает ошибку в противном случае:

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

Как это работает?

Он использует пользовательский формат с командой git branch. Для каждой ветки с восходящей веткой она печатает строку со следующим шаблоном:

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

Это может быть передано непосредственно в sh (при условии, что имена ветвей правильно сформированы). Опустить | sh | sh чтобы увидеть, что он делает.

Предостережения

Однострочник не будет связываться с вашими пультами. git fetch --all git fetch или git fetch --all перед запуском.

В настоящее время извлеченная ветвь не будет обновлена с сообщением как

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

Для этого вы можете прибегнуть к обычному git pull --ff-only.

кличка

Добавьте следующее в ваш .gitconfig чтобы git fft выполняла эту команду:

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

Смотрите также мой .gitconfig. Псевдоним - это сокращение от "ускоренного отслеживания (ветки)".

Ответ 22

  Может ли git pull --all обновить все мои локальные ветки?

Нет, не может. Для быстрой перемотки я просто написал небольшой инструмент для этого. https://github.com/changyuheng/git-fast-forward-all

Преимущества этого инструмента:

  1. Поддерживает несколько пультов в одном хранилище. (hub sync на данный момент не поддерживает несколько пультов.)
  2. Поддерживает разные имена в локальной ветке и соответствующую ветку удаленного отслеживания.
  3. Гораздо быстрее, чем другие сценарии, которые выбирают удаленно для каждой ветки.
  4. Нет подверженного ошибкам синтаксического анализа/редактирования регулярных выражений.

Ответ 23

По состоянию на git 2.9:

git pull --rebase --autostash

См. https://git-scm.com/docs/git-rebase

Автоматическое создание временного тайника перед началом операции, и применять его после окончания операции. Это означает, что вы можете запускать rebase на грязной worktree. Однако, используйте с осторожностью: последний тайник приложение после успешной перезагрузки может привести к нетривиальному конфликты.

Ответ 24

На самом деле, с git version 1.8.3.1 он работает:

[[email protected] test]# git br
* master
  release/0.1
  update
[[email protected] 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.
[[email protected] test]# git --version
git version 1.8.3.1

В главной ветке вы можете обновить все остальные ветки. @Cascabel

Я не знаю, какую версию сломать/исправить его, в 2.17 (который я использую), он может работать.