Проверьте, требуется ли тянуть в Git

Как проверить, изменился ли удаленный репозиторий, и мне нужно потянуть?

Теперь я использую этот простой script:

git pull --dry-run | grep -q -v 'Already up-to-date.' && changed=1

Но он довольно тяжелый.

Есть ли лучший способ? Идеальное решение будет проверять все удаленные ветки и возвращать имена измененных ветвей и количество новых коммитов в каждом из них.

Ответ 1

Сначала используйте git remote update, чтобы git remote update ваши удаленные ссылки. Затем вы можете выполнить одно из следующих действий:

  1. git status -uno сообщит вам, находится ли отслеживаемая вами ветка впереди, позади или разошлась. Если он ничего не говорит, локальные и удаленные одинаковы.

  2. git show-branch *master покажет вам коммиты во всех ветвях, имена которых заканчиваются на "master" (например, master и origin/master).

Если вы используете -v с git remote update git remote -v update (git remote -v update), вы можете увидеть, какие ветки были обновлены, поэтому вам больше не нужны никакие дополнительные команды.

Однако, похоже, что вы хотите сделать это в скрипте или программе и получить значение true/false. Если это так, есть способы проверить взаимосвязь между вашим текущим коммитом HEAD и отслеживаемым заголовком ветки, хотя, поскольку есть четыре возможных результата, вы не можете свести его к ответу да/нет. Однако, если вы готовы сделать pull --rebase тогда вы можете рассматривать "локальный позади" и "локальный разошелся" как "нужно тянуть", а два других - как "не нужно тянуть".

Вы можете получить идентификатор commit любого ref, используя git rev-parse <ref>, чтобы вы могли сделать это для master и origin/master и сравнить их. Если они равны, ветки одинаковы. Если они неравны, вы хотите знать, что впереди другого. Использование git merge-base master origin/master покажет вам общего предка обеих ветвей, и если они не разошлись, это будет то же самое, что и одна или другая. Если вы получаете три разных идентификатора, ветки разошлись.

Чтобы сделать это правильно, например, в скрипте, вы должны иметь возможность ссылаться на текущую ветку, а удаленная ветвь - на отслеживание. Функция подсказки подсказки bash в /etc/bash_completion.d имеет полезный код для получения имен ветвей. Тем не менее, вам, вероятно, не нужно получать имена. В Git есть несколько удобных сокращений для ссылки на ветки и коммиты (как git rev-parse --help в git rev-parse --help). В частности, вы можете использовать @ для текущей ветки (при условии, что вы не находитесь в состоянии отсоединенной головы) и @{u} для его восходящей ветки (например, origin/master). Так что git merge-base @@{u} вернет коммит (хеш), при котором текущая ветвь и его восходящий поток расходятся, а git rev-parse @ и git rev-parse @{u} дадут вам хэши два совета. Это можно обобщить в следующем сценарии:

#!/bin/sh

UPSTREAM=${1:-'@{u}'}
LOCAL=$(git rev-parse @)
REMOTE=$(git rev-parse "$UPSTREAM")
BASE=$(git merge-base @ "$UPSTREAM")

if [ $LOCAL = $REMOTE ]; then
    echo "Up-to-date"
elif [ $LOCAL = $BASE ]; then
    echo "Need to pull"
elif [ $REMOTE = $BASE ]; then
    echo "Need to push"
else
    echo "Diverged"
fi

Примечание. Более старые версии git не разрешали @ самостоятельно, поэтому вам, возможно, придется использовать @{0}.

Строка UPSTREAM=${1:-'@{u}'} позволяет при желании явно передать ветвь восходящего потока на случай, если вы захотите проверить другую удаленную ветвь, отличную от настроенной для текущей ветки. Обычно это будет форма remotename/branchname. Если параметр не указан, по умолчанию используется значение @{u}.

Сценарий предполагает, что сначала вы сделали git fetch или git remote update, чтобы git remote update ветки отслеживания. Я не встроил это в сценарий, потому что он более гибкий, чтобы можно было выполнять выборку и сравнение как отдельные операции, например, если вы хотите сравнить без выборки, потому что вы уже загрузили недавно.

Ответ 2

Если у вас есть ветвь upstream

git fetch <remote>
git status

Если у вас нет ветки восходящего потока

Сравните две ветки:

git fetch <remote>
git log <local_branch_name>..<remote_branch_name> --oneline

Например:

git fetch origin

# See if there are any incoming changes
git log HEAD..origin/master --oneline

(я предполагаю, что origin/master - ваша удаленная ветка отслеживания)

Если в выводе выше указаны какие-либо коммиты, то у вас есть входящие изменения - вам нужно объединиться. Если никакие коммиты не перечислены git log, то слияние нечего.

Обратите внимание, что это будет работать, даже если вы находитесь в ветки функции, у которой нет удаленной системы слежения, поскольку если явно ссылается на origin/master вместо того, чтобы неявно использовать ветвь восходящего потока, отмеченную Git.

Ответ 3

Если это значение для script, вы можете использовать:

git fetch
$(git rev-parse HEAD) == $(git rev-parse @{u})

(Примечание: преимущество этого и предыдущих ответов заключается в том, что вам не нужна отдельная команда для получения текущего имени ветки. "HEAD" и "@{u}" (текущая ветвь вверх по течению) заботятся о См. "git rev-parse --help" для получения более подробной информации.)

Ответ 4

Команда

git ls-remote origin -h refs/heads/master

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

Ответ 5

Здесь Bash однострочный, который сравнивает текущий хеш-код фиксации HEAD с его удаленной ветвью вверх по течению, не требуются тяжелые операции git fetch или git pull --dry-run:

[ $(git rev-parse HEAD) = $(git ls-remote $(git rev-parse --abbrev-ref @{u} | \
sed 's/\// /g') | cut -f1) ] && echo up to date || echo not up to date

Здесь, как эта несколько плотная линия разбита:

  • Команды группируются и вставляются с использованием $(x) Bash command-substitution синтаксиса.
  • git rev-parse --abbrev-ref @{u} возвращает сокращенную восходящую ссылку ref (например, origin/master), которая затем преобразуется в поля, разделенные пробелами, с помощью команды sed с каналами, например. origin master.
  • Эта строка передается в git ls-remote, которая возвращает фиксацию головы удаленной ветки. Эта команда будет связываться с удаленным репозиторием. Командная команда cut извлекает только первое поле (хеш фиксации), удаляя ссылочную строку, разделенную на вкладку.
  • git rev-parse HEAD возвращает локальный хеш фиксации.
  • Синтаксис Bash [ a = b ] && x || y завершает однострочный: это Bash сравнение строк = в пределах test [ test ], за которой следуют конструкторы and-list и or-list && true || false.

Ответ 6

Предлагаю вам перейти к скрипту https://github.com/badele/gitcheck. Я закодировал этот скрипт для проверки в один проход всех ваших репозиториев Git, и он показывает, кто не совершил и кто не нажал/вытащил.

Вот пример результата:

Enter image description here

Ответ 7

Я основывал это решение на комментариях @jberger.

if git checkout master &&
    git fetch origin master &&
    [ `git rev-list HEAD...origin/master --count` != 0 ] &&
    git merge origin/master
then
    echo 'Updated!'
else
    echo 'Not updated.'
fi

Ответ 8

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

# Check return value to see if there are incoming updates.
if ! git diff --quiet remotes/origin/HEAD; then
 # pull or whatever you want to do
fi

Ответ 9

Я думаю, что лучший способ сделать это:

git diff remotes/origin/HEAD

Предположим, что вы зарегистрировали этот refspec. Вы должны, если вы клонировали репозиторий, в противном случае (то есть, если репо было создано локально локально и нажато на удаленный), вам нужно явно указать refspec.

Ответ 10

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

[ `git log --pretty=%H ...refs/heads/master^` != `git ls-remote origin
-h refs/heads/master |cut -f1` ] && git pull

Ответ 11

Нижеприведенный скрипт работает отлично.

changed=0
git remote update && git status -uno | grep -q 'Your branch is behind' && changed=1
if [ $changed = 1 ]; then
    git pull
    echo "Updated successfully";
else
    echo "Up-to-date"
fi

Ответ 12

Запустите git fetch (remote) чтобы обновить ваши удаленные ссылки, он покажет вам, что нового. Затем, когда вы проверите свой локальный филиал, он покажет вам, находится ли он вверх по течению.

Ответ 13

Если вы запустите этот script, он проверит, нужна ли текущая ветка git pull:

#!/bin/bash

git fetch -v --dry-run 2>&1 |
    grep -qE "\[up\s+to\s+date\]\s+$(
        git branch 2>/dev/null |
           sed -n '/^\*/s/^\* //p' |
                sed -r 's:(\+|\*|\$):\\\1:g'
    )\s+" || {
        echo >&2 "Current branch need a 'git pull' before commit"
        exit 1
}

Очень удобно помещать его как pre-commit Git, чтобы избежать

Merge branch 'foobar' of url:/path/to/git/foobar into foobar

когда вы commit до pulling.

Чтобы использовать этот код как крючок, просто скопируйте/вставьте script в

.git/hooks/pre-commit

и

chmod +x .git/hooks/pre-commit

Ответ 14

Здесь моя версия скрипта Bash, который проверяет все репозитории в предопределенной папке:

https://gist.github.com/henryiii/5841984

Он может различать обычные ситуации, такие как необходимость тянуть и необходимость нажимать, и она многопоточная, поэтому выбор происходит сразу. Он имеет несколько команд, таких как pull и status.

Поместите символическую ссылку (или скрипт) в папку на вашем пути, затем она будет работать как git all status (и т.д.). Он поддерживает только origin/master, но его можно редактировать или комбинировать с другим методом.

Ответ 15

Я просто хочу опубликовать это как фактический пост, так как это легко пропустить в комментариях.

Правильный и лучший ответ на этот вопрос дал @Jake Berger, спасибо вам большое, всем это нужно, и все пропускают это в комментариях. Так что для всех, кто борется с этим, это правильный ответ, просто используйте вывод этой команды, чтобы узнать, нужно ли вам делать git pull. если выход равен 0, то, очевидно, ничего не обновить.

@stackoverflow, дайте этому парню колокола. Спасибо @Джейк Бергер

git rev-list HEAD...origin/master --count will give you the total number of "different" commits between the two. – Jake Berger Feb 5 '13 at 19:23

Ответ 16

git ls-remote | cut -f1 | git cat-file --batch-check >&-

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

git pack-refs --all
mine=`mktemp`
sed '/^#/d;/^^/{G;s/.\(.*\)\n.* \(.*\)/\1 \2^{}/;};h' .git/packed-refs | sort -k2 >$mine
for r in `git remote`; do 
    echo Checking $r ...
    git ls-remote $r | sort -k2 | diff -b - $mine | grep ^\<
done

Ответ 17

Использование простого регулярного выражения:

str=$(git status) 
if [[ $str =~ .*Your\ branch\ is\ behind.*by.*commits,\ and\ can\ be\ fast-forwarded ]]; then
    echo 'date "+%Y-%m-%d %H:%M:%S"' "Needs pull"
else
    echo "Code is up to date"
fi

Ответ 18

Я использую версию сценария на основе Стивена Хабермана:

if [ -n "$1" ]; then
    gitbin="git -C $1"
else
    gitbin="git"
fi

# Fetches from all the remotes, although --all can be replaced with origin
$gitbin fetch --all
if [ $($gitbin rev-parse HEAD) != $($gitbin rev-parse @{u}) ]; then
    $gitbin rebase @{u} --preserve-merges
fi

Предполагая, что этот скрипт называется git-fetch-and-rebase, он может быть вызван с необязательным directory name аргументов локального репозитория Git для выполнения операции. Если скрипт вызывается без аргументов, он предполагает, что текущий каталог будет частью репозитория Git.

Примеры:

# Operates on /abc/def/my-git-repo-dir
git-fetch-and-rebase /abc/def/my-git-repo-dir

# Operates on the Git repository which the current working directory is part of
git-fetch-and-rebase

Он также доступен здесь.

Ответ 19

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

Если вы работаете в Windows, вы можете запустить этот скрипт в Windows с помощью Git Bash, предоставляемого Git для Windows (установка или перенос).

Этот скрипт требует аргументов

- local path e.g. /d/source/project1
- Git URL e.g. https://[email protected]/username/project1.git
- password

if a password should not be entered on the command line in plain text,
then modify the script to check if GITPASS is empty; do not
replace and let Git prompt for a password

Сценарий будет

- Find the current branch
- Get the SHA1 of the remote on that branch
- Get the SHA1 of the local on that branch
- Compare them.

Если есть изменение, напечатанное сценарием, вы можете продолжить выборку или вытащить. Сценарий может быть неэффективным, но он выполняет работу для меня.

Обновление - 2015-10-30: stderr to dev null, чтобы предотвратить печать URL с паролем на консоль.

#!/bin/bash

# Shell script to check if a Git pull is required.

LOCALPATH=$1
GITURL=$2
GITPASS=$3

cd $LOCALPATH
BRANCH="$(git rev-parse --abbrev-ref HEAD)"

echo
echo git url = $GITURL
echo branch = $BRANCH

# Bash replace - replace @ with :[email protected] in the GIT URL
GITURL2="${GITURL/@/:[email protected]}"
FOO="$(git ls-remote $GITURL2 -h $BRANCH 2> /dev/null)"
if [ "$?" != "0" ]; then
  echo cannot get remote status
  exit 2
fi
FOO_ARRAY=($FOO)
BAR=${FOO_ARRAY[0]}
echo [$BAR]

LOCALBAR="$(git rev-parse HEAD)"
echo [$LOCALBAR]
echo

if [ "$BAR" == "$LOCALBAR" ]; then
  #read -t10 -n1 -r -p 'Press any key in the next ten seconds...' key
  echo No changes
  exit 0
else
  #read -t10 -n1 -r -p 'Press any key in the next ten seconds...' key
  #echo pressed $key
  echo There are changes between local and remote repositories.
  exit 1
fi

Ответ 20

Возможно, это, если вы хотите добавить задачу как crontab:

#!/bin/bash
dir="/path/to/root"
lock=/tmp/update.lock
msglog="/var/log/update.log"

log()
{
        echo "$(date) ${1:-missing}" >> $msglog
}

if [ -f $lock ]; then
        log "Already run, exiting..."
else
        > $lock
        git -C ~/$dir remote update &> /dev/null
        checkgit=`git -C ~/$dir status`
        if [[ ! "$checkgit" =~ "Your branch is up-to-date" ]]; then
                log "-------------- Update ---------------"
                git -C ~/$dir pull &>> $msglog
                log "-------------------------------------"
        fi
        rm $lock

fi
exit 0

Ответ 21

Для пользователей Windows, которые заканчивают этот вопрос в поисках этого, я изменил часть ответа на сценарий powershell. При необходимости измените настройки, сохраните файл .ps1 и запускайте по требованию или по расписанию, если хотите.

cd C:\<path to repo>
git remote update                           #update remote
$msg = git remote show origin               #capture status
$update = $msg -like '*local out of date*'
if($update.length -gt 0){                   #if local needs update
    Write-Host ('needs update')
    git pull
    git reset --hard origin/master
    Write-Host ('local updated')
} else {
    Write-Host ('no update needed')
}

Ответ 22

Вы также можете найти Phing script, который делает это сейчас.

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

script написан в формате XML и нуждается в Phing.