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

531

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

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

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

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

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

  • 14
    Пожалуйста, обратите внимание: "git pull --dry-run" не работает так, как, вероятно, ожидали. Похоже, что git pull передает неизвестные параметры непосредственно git fetch. Результатом является то, что нормальный git pull.
  • 23
    «pull» - это всего лишь короткий способ одновременно выполнить «fetch» и «merge», если вам нужно проверить состояние удаленного репо, вы действительно моделируете «fetch». Так что git fetch -v --dry-run - это то, что вам нужно.
Теги:

22 ответа

752
Лучший ответ

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

  • 3
    @takeshin Полагаю, вы могли бы объединить git ls-remote origin -h refs /heads / master, как предложено @brool, с git rev-list --max-count = 1 origin / master. Если они возвращают один и тот же хэш, удаленная ветвь не изменилась с тех пор, как вы в последний раз обновляли свои удаленные ссылки (с извлечением, извлечением, удаленным обновлением и т. Д.). Это имело бы то преимущество, что вам не пришлось бы удалять содержимое все коммиты сразу, но могут оставить это на более удобное время. Однако, поскольку удаленное обновление не является разрушительным, вы все равно можете это сделать.
  • 1
    Вы также можете попробовать git status -s -u no , что дает более короткий вывод, чем git status -u no .
Показать ещё 24 комментария
113

Если у вас есть ветвь 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.

  • 2
    Это просто идеально.
  • 2
    Даже более короткая запись git fetch; git log HEAD.. --oneline можно использовать, если для локальной ветки существует удаленная ветка по умолчанию.
Показать ещё 8 комментариев
47

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

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

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

  • 0
    Я обнаружил @ {u} независимо и обновил свой ответ, прежде чем увидел твой.
  • 1
    Будет ли git rev-parse @{u} отображать последний коммит без git fetch ?
Показать ещё 4 комментария
35

Команда

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

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

  • 1
    Какой-нибудь пример скрипта для сравнения этих значений?
  • 14
    git rev-list HEAD...origin/master --count выдаст вам общее количество «разных» коммитов между ними.
Показать ещё 4 комментария
27

Здесь 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.
  • 1
    Я бы не использовал / g для sed, если вы используете косые черты в именах веток. Это только "sed 's / \ // /".
19

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

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

Изображение 3886

  • 6
    аккуратный, думая о переписывании в чистом виде
  • 1
    Теперь вы также можете использовать gitcheck непосредственно из контейнера докера (с вашими файлами на вашем хосте). Для получения дополнительной информации смотрите проект gitcheck github.
Показать ещё 1 комментарий
9

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

# 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
  • 2
    В первоначальном ответе не было '!' в случае если Возвращаемое значение из git diff равно нулю, когда нет изменений.
  • 1
    Работает ли это для разных веток?
9

Я основывал это решение на комментариях @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
  • 0
    Ссылаясь на ваш предыдущий комментарий , на данный момент я не могу дать вам точный ответ. В то время, когда я делал эти комментарии, я погружался в глубины мерзавца и особенно отдаленных и разногласий. С тех пор прошло несколько месяцев, и большая часть этих знаний похоронена в моем мозгу. ;) Если вы ищете количество «разных» коммитов между ними, тогда ... похоже, что это правильная часть вашего решения.
  • 1
    Благодарю. Это было чисто.
8

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

git diff remotes/origin/HEAD

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

6

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

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
6

Я бы сделал так, как предложил бройл. Следующая однострочная 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
  • 0
    Эта команда не выполняется, если репозиторий git клонирован с «--depth 1» (для ограничения размера загрузки). Знаете ли вы, если есть способ это исправить?
  • 0
    Журнал git возвращает много строк и выдает ошибку «bash: [: слишком много аргументов». Я бы переключился на git rev-parse --verify HEAD
Показать ещё 2 комментария
4

Если вы запустите этот 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
4

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

  • 0
    Я думаю, что у него уже есть локальная ветка, поэтому ему нужно что-то еще, чтобы показать, позади ли он и т. Д. Он может сделать это с помощью git status.
  • 0
    Правда, после того, как вы загрузили пульты, git status также покажет это.
Показать ещё 4 комментария
2

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

Правильный и лучший ответ на этот вопрос дал @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
2

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

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

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

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

1

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

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
  • 0
    Это не будет работать. Состояние git - это только локальная проверка, и поэтому она будет сообщать вам только о том, отстает ли ваша ветвь, если вы уже обновили свои удаленные определения.
1
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
0

Для пользователей 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')
}
0

Возможно, это, если вы хотите добавить задачу как 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
0

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

Если вы работаете в 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 :password@ in the GIT URL
GITURL2="${GITURL/@/:$GITPASS@}"
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
0

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

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

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

-2

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

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

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

  • 0
    Может быть, это не очень хорошая идея, чтобы скопировать сценарий сюда, я все еще обновляю его ...

Ещё вопросы

Сообщество Overcoder
Наверх
Меню