Чи можна "git pull --all" оновити всі мої локальні відділення?


473

У мене часто є щонайменше 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", але мені не вдалося змусити його працювати. Здається, зробити "вибір - всі", потім оновити (перемотати вперед або об'єднати) поточну робочу гілку, але не інші локальні гілки.

Я все ще тримаюсь вручну, переходячи до кожної місцевої гілки та оновлюючи її.


8
Ви хочете автоматичне оновлення локальних гілок відстеження лише у випадку швидкого перемотування вперед? Іпу слід, тому що злиття може мати конфлікт, з яким вам доведеться вирішити ...
Якуб Нарбскі

34
Якщо припустити, що консервативний час у 300 доларів США буде вирішеним, цей єдиний випуск коштував компаніям 23 232 800 доларів США, використовуючи кількість переглядів 77 476. Тепер розглянемо це питання stackoverflow.com/questions/179123/… та всі інші. Ого.
Люк Пуплетт

16
@Luke Ти перший, кого я чув, зазначив, як витрачений час на намагання git робити те, що ми хочемо, коштує компаніям гроші. Ці прості речі повинні бути автоматичними і повинні бути такими простими, що мені не потрібно відкривати браузер, щоб читати форуми, IMO.
Самуїл

13
@LukePuplett Є майже ~ 9 разів більше питань щодо git на SO в порівнянні з Mercurial, і більшість колишніх здається "як мені зробити <проста операція> в git?". Це вказує на те, що git або погано розроблений, погано задокументований, неінтуїтивний, або всі три.
Ян Кемп

26
@IanKemp Я не впевнений, що це безпечно висловити це твердження, не знаючи демографії SO. Якщо Mercurial тут не настільки часто використовується, або якщо його користувачі використовують інші форуми, щоб запитати про нього, я би сподівався побачити той же результат. :) У Javascript є ~ 51 раз більше запитань у порівнянні з Асамблеєю - тому, можливо, не завжди точно судити про інструменти саме за цими видами показників.
danShumway

Відповіді:


188

Поведінка, яку ви описуєте pull --all, точно так, як очікувалося, хоча й не обов'язково корисно. Параметр передається разом з git fetch, який потім отримує всі рефлекси з усіх віддалених, а не лише потрібний; pullпотім об'єднує (або у вашому випадку відновлює) відповідну окрему гілку.

Якщо ви хочете перевірити інші відділення, вам доведеться перевірити їх. І так, для злиття (та звільнення) абсолютно потрібне робоче дерево, тому їх неможливо зробити, не перевіривши інші гілки. Ви можете загорнути описані вами кроки в сценарій / псевдонім, якщо хочете, хоча я б запропонував об'єднати команди з &&тим, щоб одна з них вийшла з ладу, вона не намагатиметься оратись.


2
Якщо ви наводите приклад командного рядка, я б проголосував. У мене ця проблема є на github. Я створив відділення в інтерфейсі. Тепер мені потрібен мій місцевий, щоб показати гілку. git pull - всі; git branch ... argh ... команда: git branch -a
mariotti

@mariotti Залежить від того, що ти намагаєшся зробити, і це не зовсім зрозуміло з вашого коментаря. Можливо, вам буде найкраще задати нове запитання.
Каскабель

1
Або @Jefromi .. наведіть приклад. Я насправді погодився з вами.
mariotti

3
@mariotti Суть цієї відповіді полягає в тому, що вбудовані команди насправді не виконують те, про що вимагав ОП, тому послідовність кроків, які вони мали, є необхідною. Ці кроки можна автоматизувати (див., Наприклад, відповідь Джона), але їх потрібно зробити. Отже, якщо те, що ви намагаєтеся зробити, точно таке ж, як і ОП, насправді не можна навести приклад, і якщо ви намагаєтесь зробити щось інше, то вам слід задати нове запитання - ось як працює StackOverflow! (І ваш коментар незрозумілий, але я найкраще здогадуюсь, що ви хочете чогось іншого від ОП, тож так, нове питання.)
Каскабель

Так, дещо щось інше. Але ваша відповідь була просто ідеальною для контексту. І мені, можливо, не потрібно більше питати лише через вашу відповідь. Просто: У прийнятій відповіді використовується git-up, який є просто інтерфейсом для командного рядка git (я припускаю). Я сподівався, що ви зможете зробити це явним у кількох рядках команд git. Поточна відповідь НЕ git.
mariotti

206

Я використовую syncсубкоманди в ступиці автоматизувати цей процес . У мене є alias git=hubсвій .bash_profile, тому команда, яку я вводя, - це:

git sync

Це оновлення всіх локальних відділень, які мають відповідні гілки вище за течією. На чоловіковій сторінці:

  • Якщо місцева філія застаріла, перемотайте її вперед;
  • Якщо місцева філія містить нерозважливу роботу, попередити про це;
  • Якщо гілка здається об'єднаною, а її гілка вище за течією була видалена, видаліть її.

Він також обробляє приховування / зняття неподілених змін на поточній гілці.

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


15
Що з Windows?
Фіолетова жирафа

6
@ TrentonD.Adams фіксує дати та дати автора - це різні поняття. База даних змінить дату введення, але не дату автора (за винятком конфліктів, коли також змінюється дата автора). Дата автора відображає, коли дерево комітету було автором, і воно не повинно змінюватися під час безконфліктної бази. Дата фіксації змінюється, оскільки повторне базування завжди створює нове зобов’язання. Тому дати фіксації завжди будуть у правильному порядку.
Дев

16
Щоб вимкнути поведінку автоматичного перезапускання git-up, запустіть git config --global git-up.rebase.auto false.
Dan Loewenherz

18
@MaxYankov Подання загальної історії загалом слід уникати, нічого поганого в тому, щоб перезавантажувати місцеві комісії під час витягування.
Dev

23
Звільнення місцевих комісій переписує історію та робить її простішою, ніж є насправді. За допомогою ребазування ви можете опинитися з кодом, який автоматично злився, але не компілює, або, що ще гірше, компілює, але не працює. Об’єднання визнає ваш спосіб роботи: ви впровадили зміни та протестували їх перед тим, як включити зміни інших людей, а об'єднання злиття є дуже корисним моментом: саме там ви переконайтеся, що різні шагеди грають разом разом. Звільнення змушує виглядати так, що цей процес ніколи не відбувається, що просто не відповідає дійсності і є дуже небезпечною практикою.
Макс Янков

39

Мені відомо, що це питання майже 3 роки, але я задав собі це саме запитання і не знайшов жодного готового рішення. Отже, я створив власний скрипт оболонки git command shell.

Ось і йдеться, git-ffwd-updateсценарій робить наступне ...

  1. він видає a 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 $@

1
Дякую за цей сценарій Чи можливо хтось може перетворити цей скрипт у пакет Windows?
Саарико

@ Саріко, чому ти не хочеш використовувати git у звичайній оболонці Windows? Якщо ви використовуєте щось на зразок cygwin, цей сценарій повинен просто працювати добре ... (хоча я його не перевіряв)
muhqu

@RyanWilcox дякую, я використовую його, як і кожен (робочий день) ... ;-), можливо, ви захочете ознайомитись з моїми точковими файлами для отримання більше скриптів і псевдонімів, пов’язаних з git: github.com/muhqu/dotfiles
muhqu

@muhqu Я намагаюся використовувати ваш сценарій, і я не знаю, чому він працював уперше, але він не працює "як очікувалося" прямо зараз. Наприклад, погляньте на це . Чому master ще 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. Відредагували; додаткові функції / виправлення, що залежать від OP.
Фред Фоо

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>(без будь-якого +), ми отримуємо підбірку, яка оновлює локальну посилання лише тоді, коли її можна швидко переслати.

Примітка . Це передбачає, що локальні та віддалені гілки названі однаковими (і що ви хочете відслідковувати всі гілки), вона повинна реально використовувати інформацію про те, які локальні гілки у вас є і що вони налаштовані для відстеження.


1
"Оновлення гілок, лише якщо їх можна переадресувати" - яке значення має швидке перемотування вперед? Якщо я хочу отримати найновіші джерела у всіх своїх галузях, то чому я повинен дбати про швидку переадресацію чи ні? Такі речі, як це, змушує мене сміятися з Гіта та його Фанбоя. Ви не можете зробити це просто однією командою. Натомість вам потрібно виконати c*nкроки (замість 1), де cє деяка кількість повторних команд і nкількість гілок.
jww

@jww Це не допомагає "сміятися з Git та його Fanboi" [sic], коли це VCS, який використовує більшість у світі. Але я відступаюсь ... Я вважаю, що в контексті такого типу "глобального" сценарію доцільно не намагатися вносити зміни до поточних гілок, якщо у них є конфлікти злиття.
Віль

Це було корисно, дякую. Єдине , що я зробив не так, як було то , що він створив філію локально для кожного віддаленого філії ( в тому числі , в яких я не зацікавлений), тому я змінив , 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", які суто відслідковують віддалений. Якщо ви хочете лише переглядати і переглядати, ви можете перевірити гілку віддаленого відстеження безпосередньо на відокремленій ГОЛОВІ з " 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

Дякую! ти врятував мені годину гри з
башем

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

Ось гарна відповідь: Як отримати всі гіти гіта

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

Чому ви пропонуєте зробити git fetchі git pull, замість того , щоб просто git 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
    }

Чи можете ви надати еквівалентний файл bat bat?
Джеффі

1
@Jaffy Не впевнений, скільки часу у мене на руках, і я не дуже добре володію партією, але я можу дати їй піти. Я опублікую свій прогрес тут , може, інші можуть наступити і допомогти?
philosowaffle

3

Якщо ви працюєте в Windows, ви можете використовувати PyGitUp, який є клоном git-upдля Python. Ви можете встановити його з допомогою піп з 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 pullз Git 2.9 (спасибі @VonC, дивіться його відповідь тут )

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

1
Псевдонім вам не потрібен. Простий мерзотник смикання, з правого конфігурацією: 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 / remote / 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-merge-base.
Норман Рамзі

2
Я боюся, що я не розумію модель достатньо добре, щоб використовувати сценарій, який так надано. Досить змусити людину захотіти перейти на ртутний.
Норман Рамзі

Я особисто знайшов статтю Томмі Віртанена "Git for computer science" досить корисною для ознайомлення з моделлю та термінологією 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

Трохи інший сценарій, який відповідає лише тим, хто швидко пересилає гілки, імена яких відповідають їхнім відділенням. Він також оновлює поточну гілку, якщо можлива швидка перемотка вперед.

Переконайтеся, що всі білі гілки ваших гілок правильно встановлені за допомогою запуску 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;

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. Псевдонім - це скорочення до "швидкого переходу вперед (гілки)".


Це хороше рішення, хоча я думаю , що я буду використовувати в hubsoluction propsed по @John для його кращого виходу.
Didier L

Це швидко, просто і насправді працює! Мене бентежить, хоча 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

Вибачте, я також, здається, придумав те саме ім'я, що й інший інструмент, наведений вище.


2
Ти той, що написав це? Якщо так, будь ласка, розкрийте свою приналежність, тобто повідомте нам, як ви пов’язані з нею. Будь ласка, прочитайте детальніше про це для отримання додаткової інформації. Конкретно не кажіть - показуйте! ; Розкажіть, які частини вашого сценарію і як / чому це вирішує проблему.
Кіл

1
Так, я це написав. Я включив вищезгадане джерело для швидкої копіювання-вставлення у ваш .bashrc або .zshrc.
Стімп

Це приємне рішення і працює добре. Ніхто не приймав повідомлення?
Ville

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

Додайте трохи пояснень для того, щоб забити свою відповідь
дурний дев

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'боку:

  • /*/=> Шукати рядок з a *.

  • {$q => Якщо він знаходиться в останньому рядку, вийдіть (нам нічого не потрібно робити, тому що поточна гілка є останньою у списку).

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

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


Ви можете уникнути будь-якого божевілля нескінченних рядків і &&встановивши set -eвгорі сценарію.
mcepl

0

Чи можна "git pull --all" оновити всі мої локальні відділення?

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

Переваги цього інструменту:

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

1
Ви можете уникнути попадання в мережу, використовуючи git fetch . refspec. .Каже витягти з поточного сховища , а не з віддалених один.
хугомг

-1

Станом на git 2,9:

git pull --rebase --autostash

Дивіться https://git-scm.com/docs/git-rebase

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


-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 (яку я використовую), вона може працювати.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.