Проверьте, нужно ли тянуть в Git - PullRequest
578 голосов
/ 15 июля 2010

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

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

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

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

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

Ответы [ 22 ]

783 голосов
/ 19 июля 2010

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

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

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

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

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

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

Чтобы сделать это правильно, например, в скрипте, вы должны иметь возможность ссылаться на текущую ветку и отслеживаемую удаленную ветку.Функция настройки приглашения bash в /etc/bash_completion.d имеет некоторый полезный код для получения имен веток.Тем не менее, вам, вероятно, не нужно получать имена.В Git есть несколько удобных сокращений для ссылок на ветки и коммиты (как описано в 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, чтобы обновлять ветви отслеживания.Я не встроил это в сценарий, потому что он более гибок, чтобы можно было выполнять выборку и сравнение как отдельные операции, например, если вы хотите сравнить без выборки, потому что вы уже загрузили недавно.

124 голосов
/ 09 октября 2012

Если у вас ветвь вверх по течению

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 .

55 голосов
/ 30 июля 2013

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

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

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

36 голосов
/ 15 июля 2010

Команда

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

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

30 голосов
/ 04 августа 2014

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

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

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

Enter image description here

10 голосов
/ 19 июня 2013

Я основал это решение на комментариях @ 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
9 голосов
/ 06 октября 2015

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

# 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 голосов
/ 16 июля 2010

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

git diff remotes/origin/HEAD

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

6 голосов
/ 10 марта 2013

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

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