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

473

У меня часто есть как минимум 3 удаленных филиала: мастер, постановка и производство. У меня есть 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», затем обновляет (перемотает вперед или объединяет) текущую рабочую ветку, но не другие локальные ветви.

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

mpoisot
источник
8
Хотите автоматическое обновление локальных веток отслеживания только в случае ускоренной перемотки? Ypu следует, потому что слияние может иметь конфликт, который вы должны решить ...
Якуб Наребски
34
Если предположить, что на консультацию нужно потратить 300 долларов, эта единственная проблема обошлась компаниям в 23 242 800 долларов при подсчете 77 476 просмотров. Теперь рассмотрим этот вопрос stackoverflow.com/questions/179123/… и все остальные. Ух ты.
Люк Пуплетт
16
@Luke Ты первый человек, о котором я слышал, что время, потраченное на то, чтобы заставить мерзавца делать то, что мы хотим, стоит компаниям денег. Эти простые вещи должны быть автоматическими и настолько простыми, что мне не нужно открывать браузер для чтения форумов, IMO.
Самуил
13
@LukePuplett Существует почти в 9 раз больше вопросов о git на SO по сравнению с Mercurial, и большинство из них, похоже, задаются вопросом «как мне выполнить <простую операцию> в git?». Это указывает на то, что git либо плохо разработан, плохо документирован, не интуитивен, либо все три.
Ян Кемп
26
@IanKemp Я не уверен, что делать это заявление безопасно, не зная демографию SO. Если Mercurial не так часто используется здесь, или если его пользователи используют другие форумы, чтобы спросить об этом, я ожидаю увидеть тот же результат. :) В Javascript примерно в 51 раз больше вопросов по сравнению со сборкой - поэтому не всегда можно судить об инструментах только по таким метрикам.
DanShumway

Ответы:

188

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

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

Cascabel
источник
2
Если вы приведете пример командной строки, я бы проголосовал. У меня есть эта проблема на GitHub. Я создал ветку на пользовательском интерфейсе. Теперь мне нужен мой местный, чтобы показать ветку. git pull - все; git branch ... argh ... команда: git branch -a
mariotti
@mariotti Зависит от того, что вы пытаетесь сделать, и это не совсем понятно из вашего комментария. Возможно, вам лучше задать новый вопрос.
Каскабель
1
Или @Jefromi .. приведите пример. Я действительно с тобой согласен.
Мариотти
3
@mariotti Смысл этого ответа заключается в том, что встроенные команды на самом деле не выполняют то, что запрашивал OP, поэтому необходима последовательность шагов, которые они выполнили. Можно автоматизировать эти шаги (см., Например, ответ Джона), но они должны быть выполнены. Так что, если вы пытаетесь сделать то же самое, что и OP, на самом деле нет примера, который можно привести, и если вы пытаетесь сделать что-то другое, вам следует задать новый вопрос - вот как работает StackOverflow! (И ваш комментарий неясен, но я думаю, что вы хотите что-то отличное от ОП здесь, так что да, новый вопрос.)
Каскабель
Да, что-то другое. Но ваш ответ был просто идеальным для контекста. И мне больше не нужно спрашивать только из-за твоего ответа. Просто: принятый ответ использует git-up, который является просто интерфейсом для командной строки git (я полагаю). Я надеялся, что вы можете сделать это явным в нескольких строках команд git. Текущий ответ НЕ мерзавец.
Мариотти
206

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

git sync

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

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

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

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

Джон
источник
15
А как насчет Windows?
Фиолетовый Жираф
6
@ TrentonD.Adams даты фиксации и даты автора являются разными понятиями. Перебазировка изменит дату фиксации, но не дату автора (за исключением конфликтов, где также изменяется дата автора). Дата автора отражает, когда дерево коммита было создано, и не должно меняться во время бесконфликтного перебазирования. Дата фиксации меняется, потому что rebase всегда создает новый коммит. Поэтому даты коммитов всегда будут в правильном порядке.
Дев
16
Чтобы отключить режим автоматической перебазировки Git-Up, запустите git config --global git-up.rebase.auto false.
Дэн Лёвенхерц
18
@MaxYankov Как правило, избегать перебазирования общей истории, нет ничего плохого в перебазировании локальных коммитов во время вытягивания.
Дев
23
Перебазирование локальных коммитов переписывает историю и делает ее проще, чем есть на самом деле. С rebase вы можете оказаться с кодом, который автоматически объединяется, но не компилируется, или, что еще хуже, компилируется, но не работает. Объединение подтверждает то, как вы работали: вы внедрили изменения и протестировали их, прежде чем включать изменения других людей, и фиксация слияния является очень полезным моментом: именно здесь вы можете убедиться, что различные наборы chagesets хорошо сочетаются друг с другом. Перебазирование делает вид, что этот процесс никогда не происходит, что просто не соответствует действительности и является очень опасной практикой.
Макс Янков
39

Я знаю, что этому вопросу почти 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 или более коммитов, она НЕ может быть быстро продвинута и должна быть объединена или перебазирована вручную
  5. если локальная ветвь имеет 0 коммитов вперед и 1 или более коммитов позади, она может быть быстро переслана 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="$@";
  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
источник
1
Спасибо за этот сценарий. Возможно ли, что кто-то может преобразовать этот скрипт в пакет Windows?
Саарико
@Saariko, почему ты не хочешь использовать git на обычной оболочке Windows? Если вы используете что-то вроде cygwin, этот скрипт должен работать нормально ... (хотя я не проверял это)
muhqu
@RyanWilcox спасибо, я использую его как каждый (рабочий) день ... ;-) вы можете посмотреть мои точечные файлы для получения дополнительных скриптов и псевдонимов, связанных с git: github.com/muhqu/dotfiles
Muhqu
@muhqu Я пытаюсь использовать твой сценарий, и я не знаю, почему он работал в первый раз, но сейчас он не работает "как положено". Например, посмотрите на это . Почему после выполнения сценария мастер остается на 78 коммитах?
BPL
1
@muhqu В более новых версиях git -t и -l не должны использоваться вместе в одном git branchвызове. Я удалил -l, чтобы изменить вызов, git branch -f $LB -t $ARB >/dev/null;и теперь скрипт работает как надо.
Радек Лиска
24

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

#!/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
Вероятно, лучше не использовать псевдонимы в сценариях. Это также фактически ничего не извлекает, просто перезагружает на уже извлеченный контент. Вы должны изменить git rebase origin/$branchна git pullтак, чтобы он получал данные из соответствующей ветви отслеживания (предположительно в исходной точке) и выполнял слияние или перебазирование, как определено в конфигурации.
Каскабель
@Jefromi: я забыл fetch. Отредактировал; дополнительные функции / исправления, какие бы ни были до ОП.
Фред Фу
8
Я все еще думаю, что вы можете захотеть использовать pull(или проверить branch.<branch>.rebase), чтобы случайно не перебазировать ветку, которая настроена на обычное натяжение (слияние).
Каскабель
1
Попробуйте использовать set -eвместо, || exit 1чтобы заставить интерпретатор завершить работу при первой ошибке.
crishoj
18

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

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

обновить положение вашего местного отделения без необходимости проверять его.

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

Мэтт Коннолли
источник
1
Это именно то решение, которое я искал. Я обычно не помещаю изменения в несколько веток и просто хочу обновить свои локальные ветки, чтобы они соответствовали удаленным. Это решение намного приятнее, чем мой обычный метод удаления / повторной проверки!
Дэйв Найт,
1
объединены в одну команду:git fetch origin other-branch:other-branch
fabb
12

Здесь много ответов, но ни один из них не используется 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

Из справочной страницы для 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>(без такового +), мы получаем выборку, которая обновляет локальный ref только тогда, когда он может быть быстро переслан.

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

quornian
источник
1
«Обновляет ветки только в том случае, если их можно быстро пересылать» - в чем смысл быстрой перемотки? Если мне нужны последние источники во всех моих ветках, то почему я должен заботиться о быстрой пересылке или нет? Такие вещи заставляют меня смеяться над Git и его Fanboi. Вы не можете сделать это просто одной командой. Вместо этого вам нужно выполнить c*nшаги (вместо 1), где cнекоторое количество повторяющихся команд и nколичество ветвей.
jww
@jww Это не помогает "смеяться над Git и его Fanboi" [sic], когда это VCS, который использует большинство стран мира. Но я отвлекся ... Я думаю, что в контексте такого рода сценария "глобального извлечения" разумно не пытаться вносить изменения в несуществующие ветви, если они имеют конфликты слияния.
Ville
Это было полезно, спасибо. Единственное , что я сделал не так, как было то , что он создал филиал локально для каждого удаленного филиала ( в том числе , в которых я не заинтересован), поэтому я изменил , 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 remotebranchограничить его ветвей у меня уже есть на месте. Также я добавил git fetch --pruneв начале, чтобы обновить список удаленных веток, прежде чем делать что-либо, что позволяет избежать некоторых предупреждений.
Нэйт Кук,
11

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

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

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

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

Если у вас есть локальные изменения в «dev», помеченные для отслеживания удаления «dev», и если вы находитесь на ветке, отличной от «dev», то мы не должны ничего делать после « git fetch» обновления удаленного отслеживания «dev» , В любом случае он не будет двигаться вперед

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

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

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

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

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

оборота Якуб Наребски
источник
Вместо того, чтобы публиковать ссылки, публикуйте актуальный контент, используя ссылки в качестве ссылок. Эта ссылка сейчас мертва. Жаль, звучало многообещающе. (Я понимаю, что этот ответ был с 2009 года, так что это просто примечание для дальнейшего использования.)
Майкл
спасибо (и вау, быстрый ответ через столько лет). Теперь я вижу, что этот поток является « призывом к простому решению», в отличие от моего первоначального неправильного прочтения, « предоставляет простое решение».
Майкл
@michael_n: расширенный ... хм, теперь я вижу, что пост был не совсем о запрошенном решении, но о проблеме (предполагая случай проблемы XY).
Якуб Наребски
Хм, заглядывание с отсоединенной головой должно быть легче, особенно это должно показывать полезную информацию в статусе и позволяет быстро переместиться вперед по рабочей области с некоторой обратной связью (например, вытащенные коммиты). Тогда это будет замена только для чтения локальных веток.
eckes
9

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

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

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

$ git update-all
Майкл
источник
Спасибо! ты спас мне час игры с bash ...
8ctopus
5

Добавьте этот скрипт в .profileMac 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
Не должно ли это сначала спрятать все изменения, а затем восстановить их?
Мел
5

Вот хороший ответ: Как получить все ветки git

for remote in `git branch -r`; do git branch --track $remote; done
git pull --all
Мильковский
источник
Почему вы предлагаете сделать git fetchи git pull, вместо того , чтобы просто git pull?
синтагма
Спасибо. Кажется, что pull извлекает все ветки со всех пультов. Поменял его
милковский
8
Это выберет все пульты, но объединит только текущую ветку. Если у вас есть 10 пультов, вам нужно будет вручную оформить заказ и объединить.
mpoisot
Делая это, вы создадите все удаленные ветки локально с origin/префиксом
Yassine ElBadaoui
3

Скрипт, который я написал для моего GitBash . Выполняется следующее:

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

** Я использую это, но не проверил полностью, используйте на свой страх и риск. Смотрите пример этого скрипта в файле .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
    }
philosowaffle
источник
Можете ли вы предоставить эквивалентный файл Windows bat?
Джаффи
1
@Jaffy Не уверен, сколько времени у меня на руках, и я не очень беглый в партии, но я могу попробовать. Я опубликую свой прогресс здесь , может быть, другие могут вмешаться и помочь?
Philowaffle
3

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

[4]

Саймон Хартчер
источник
3

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

Начиная с Git 2.9, git pull --rebase --autostash делает то же самое.

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

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

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

git config --global pull.rebase true
git config --global rebase.autoStash true
август
источник
1
Вам не нужен псевдоним. Достаточно простого git pull с правильной конфигурацией: stackoverflow.com/a/40067353/6309
VonC
Великий вызов из спасибо @VonC Я обновил свой ответ :) также может представить PR в git-upдокументации , потому что они не упоминают , что
авг
Это не обновляет все локальные ветви сразу, поэтому я в основном использовал git-up.
Луч
Документация обновляется git-up:)
авг
3

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

Задумываясь об этом, я сделал небольшую функцию псевдонима внутри .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"
}

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

Франциско Нето
источник
2

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

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

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

Этот небольшой скрипт оболочки (я назвал его 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
hillu
источник
Что вы подразумеваете под этим комментарием?
Хиллу
Я сам не способен написать сценарий, который предлагает Хиллу, и я недостаточно уверен в своих знаниях git, чтобы использовать git-merge-base.
Норман Рэмси
2
Боюсь, я не понимаю модель достаточно хорошо, чтобы использовать сценарий, любезно предоставленный. Этого достаточно, чтобы человек захотел перейти на ртутный.
Норман Рэмси
Мне лично показалось, что статья Томми Виртанена «Git для компьютерных ученых» весьма полезна для знакомства с моделью и терминологией Git.
Hillu
2

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

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

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

Убедитесь, что все ветки upstream ваших веток настроены правильно, запустив git branch -vv. Установите ветку upstream с помощью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;
qwertzguy
источник
2

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

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

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

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

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

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

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

Однострочник не будет связываться с вашими пультами. Выпустите 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. Псевдоним - это сокращение от «ускоренного отслеживания (ветки)».

krlmlr
источник
Это хорошее решение, хотя я думаю , что я буду использовать в hubsoluction propsed по @John для его лучшего выхода.
Дидье Л
Это быстро, просто и действительно работает! Я озадачен тем, что git pushэто полностью противоположно тому, что вы ожидаете. В чем секрет?
BrandonLWhite
@BrandonLWhite: я не понимаю вопроса. Чего вы ожидаете git push?
krlmlr
git pushимеет семантику загрузки - у меня есть некоторые коммиты локально, которые я хочу отправить в апстрим. git pullимеет семантику загрузки - я хочу получить некоторые восходящие удаленные коммиты в мою локальную ветку. Поскольку мы говорим о загрузке новых коммитов с удаленного на локальный, git pullэто очевидный выбор. Но нет, этот трюк использует git push. Как это git pushприводит к удалению изменений в моем местном филиале ?!
BrandonLWhite
git pushтакже может использоваться для обновления локальных ветвей, если это обновление с ускоренной передачей.
krlmlr
1

Сценарий от @larsmans, немного улучшен:

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

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

git pullВерсия:

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

Похоже, что многие другие предложили подобные решения, но я подумал, что поделюсь тем, что я придумал, и приглашаю других внести свой вклад. Это решение имеет приятный красочный вывод, изящно обрабатывает ваш текущий рабочий каталог и является быстрым, потому что оно не делает никаких проверок и оставляет ваш рабочий каталог в такте. Кроме того, это всего лишь сценарий оболочки без каких-либо зависимостей, кроме 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 $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

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

Stimp
источник
2
Вы тот, кто написал это? Если это так, пожалуйста, сообщите о своей принадлежности, то есть сообщите нам, как вы к ней относитесь. Пожалуйста, прочитайте больше об этом для получения дополнительной информации. Конкретно не говори - покажи! ; Расскажите нам, какие части вашего сценария и как / почему он решает проблему.
Кил
1
Да, я написал это. Я включил приведенный выше источник для быстрой вставки в ваш .bashrc или .zshrc.
Стимп
Это хорошее решение и работает хорошо. Никто не обратил внимания?
Вилле
1

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

#!/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
Викаш Сингх
источник
Добавьте некоторые объяснения для оценки вашего ответа
дурак dev
1

Вы не можете сделать это только одной командой git, но вы можете автоматизировать это с помощью одной строки bash.

Чтобы безопасно обновить все ветки одной строкой, вот что я делаю:

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
  • Если он не может перемотать одну ветку вперед или возникнет ошибка, он остановится и оставит вас в этой ветке, чтобы вы могли вернуть управление и объединить вручную.

  • Если все ветви можно быстро переадресовать, это закончится веткой, в которой вы были в данный момент, и оставит вас там, где вы были до обновления.

Пояснения:

Для лучшей читаемости его можно разбить на несколько строк:

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 && ... => Извлекает все ссылки со всех пультов и продолжает выполнение следующей команды, если ошибки не было.

  2. git branch | sed '/*/{$q;h;d};$G' | tr -d '*'=> Из вывода git branch, sedвозьмите строку с a *и переместите ее в конец (чтобы текущая ветвь была обновлена ​​последней). Затем trпросто удалите *.

  3. for branch in $(...) ; do git checkout $branch && git merge --ff-only || break ; done=> Для каждого имени ветви, полученного из предыдущей команды, извлеките эту ветку и попробуйте выполнить слияние с перемоткой вперед. Если это не удается, breakвызывается и команда останавливается здесь.

Конечно, вы можете заменить git merge --ff-onlyс , git rebaseесли это то , что вы хотите.

Наконец, вы можете поместить его в свой bashrc как псевдоним:

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'

Или, если вы боитесь испортить «и», или вы просто предпочитаете сохранить синтаксическую читабельность в вашем редакторе, вы можете объявить его как функцию:

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
}

Бонус:

Для тех, кто хотел бы получить объяснение со sed '/*/{$q;h;d};$G'стороны:

  • /*/=> Поиск строки с *.

  • {$q => Если это в последней строке, выйти (нам не нужно ничего делать, потому что текущая ветвь уже является последней в списке).

  • ;h;d} => В противном случае сохраните строку в буфере удержания и удалите ее в текущей позиции списка.

  • ;$G => Когда он достигнет последней строки, добавьте содержимое буфера удержания.

Bouty
источник
Вы можете избежать всего сумасшествия бесконечных линий и &&установив set -eв верхней части сценария.
mcepl
0

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

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

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

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

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

git pull --rebase --autostash

Смотрите https://git-scm.com/docs/git-rebase

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

Nha
источник
-1

На самом деле, с Git version 1.8.3.1, это работает:

[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

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

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

daixiang0
источник