检查Git中是否需要拉动

检查Git中是否需要拉动,git,bash,shell,Git,Bash,Shell,如何检查远程存储库是否已更改,是否需要拉取 现在我使用这个简单的脚本: git pull --dry-run | grep -q -v 'Already up-to-date.' && changed=1 但是它相当重 有更好的办法吗?理想的解决方案是检查所有远程分支,并返回已更改分支的名称以及每个分支中新提交的数量。命令 git ls-remote origin -h refs/heads/master 将在遥控器上列出当前头部-您可以将其与以前的值进行比较,或者查看您的本

如何检查远程存储库是否已更改,是否需要拉取

现在我使用这个简单的脚本:

git pull --dry-run | grep -q -v 'Already up-to-date.' && changed=1
但是它相当重

有更好的办法吗?理想的解决方案是检查所有远程分支,并返回已更改分支的名称以及每个分支中新提交的数量。

命令

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

将在遥控器上列出当前头部-您可以将其与以前的值进行比较,或者查看您的本地回购中是否有SHA。

我认为最好的方法是:

git diff remotes/origin/HEAD
假设已注册此参照规范。如果您已经克隆了存储库,您应该这样做,否则,也就是说,如果repo是在本地从头创建的,并推送到远程,您需要显式地添加refspec。

首先使用,以使远程ref更新。然后您可以执行以下几项操作之一,例如:

git状态-uno将告诉您正在跟踪的分支是在前面、后面还是已经分叉。如果它什么也没说,本地和远程是一样的

git show branch*master将向您显示名称以“master”结尾的所有分支中的提交,例如master和origin/master

如果将-v与git remote update git remote-v update一起使用,您可以看到哪些分支得到了更新,因此您实际上不需要任何进一步的命令

但是,看起来您希望在脚本或程序中执行此操作,并以真/假值结束。如果是这样的话,有很多方法可以检查您当前的负责人提交和您正在跟踪的分支机构负责人之间的关系,尽管有四种可能的结果,您不能将其归结为是/否。然而,如果你准备做一个拉-再基,那么你可以把本地的落后和本地的分歧视为需要拉,其他两个视为不需要拉

您可以使用git rev parse获取任何ref的提交id,因此您可以对master和origin/master执行此操作,并对它们进行比较。如果它们相等,则分支相同。如果它们不相等,你想知道哪个在前面。使用git merge-base-master-origin/master将告诉您两个分支的共同祖先,如果它们没有分支,那么这将与其中一个相同。如果您得到三个不同的ID,则分支已分叉

要正确执行此操作,例如在脚本中,您需要能够引用当前分支及其跟踪的远程分支。/etc/bash_completion.d中的bash提示符设置函数有一些用于获取分支名称的有用代码。但是,您可能实际上不需要获取名称。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获取或git远程更新,以使跟踪分支保持最新。我没有将其构建到脚本中,因为它更灵活,可以将抓取和比较作为单独的操作来执行,例如,如果您想在不进行抓取的情况下进行比较,因为您最近已经进行了抓取。

运行git fetch remote以更新远程引用,它将向您显示新内容。然后,当您签出本地分支机构时,它将显示它是否位于上游分支机构之后。

如果您有上游分支机构 我假设origin/master是您的远程跟踪分支

如果上面的输出中列出了任何提交,那么您将有传入的更改-您需要合并。如果git日志没有列出提交,那么就没有要合并的内容


请注意,即使您在没有跟踪遥控器的功能分支上,这也会起作用,因为if显式地引用origin/master,而不是隐式地使用Git记住的上游分支。

我会按照brool建议的方式来做。下面的一行脚本获取您上一个提交版本的SHA1,并将其与远程源代码的SHA1进行比较,并且仅当它们不同时才提取更改。 而且基于git pull或git fetch的解决方案更加轻量级

将列出任何远程中引用的、不在您的repo中的所有内容。要捕获对已有内容的远程引用更改(例如重置为以前的提交),需要花费更多的时间:

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
我 建议你去看剧本。我已经编写了这个脚本,用于一次性签入所有Git存储库,它显示了谁没有提交,谁没有推/拉

下面是一个示例结果:


我基于@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

下面是我的Bash脚本版本,它检查预定义文件夹中的所有存储库:

它可以区分常见的情况,如需要拉取和需要推取,并且它是多线程的,因此提取一次完成。它有几个命令,比如pull和status


将符号链接或脚本放在路径中的文件夹中,然后作为git all status等。。它只支持origin/master,但可以编辑或与其他方法组合使用。

如果这是用于脚本,则可以使用:

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

注意:与前面的答案相比,这个答案的好处是不需要单独的命令来获取当前分支名称。负责人和{u}目前分行的上游负责。有关更多详细信息,请参阅git rev parse-help。

如果运行此脚本,它将测试当前分支是否需要git pull:

将其作为Git钩子预提交非常方便,以避免

Merge branch 'foobar' of url:/path/to/git/foobar into foobar
当你在拉之前承诺

要将此代码用作钩子,只需将脚本复制/粘贴到

.git/hooks/pre-commit


下面是一个Bash one liner,它将当前分支的HEAD commit散列与其远程上游分支进行比较,不需要大量的git获取或git pull—干运行操作:

[ $(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}返回一个缩写的上游ref,例如origin/master,然后通过管道sed命令(例如origin master)将其转换为空格分隔的字段。 这个字符串被输入git ls remote,后者返回远程分支的头提交。此命令将与远程存储库通信。管道剪切命令只提取提交散列的第一个字段,删除制表符分隔的引用字符串。 git rev parse HEAD返回本地提交散列。 Bash语法[a=b]&&x | | y完成了一行代码:这是一个测试构造[test]中的Bash=,后跟and list和or list构造&&true | | false。
我使用了一个基于Stephen Haberman答案的脚本版本:

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,则可以使用本地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

它也可以使用。

已经有很多功能丰富且巧妙的答案。为了提供一些对比,我可以用一条非常简单的线来凑合

# 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

在阅读了许多答案和多篇帖子,并花了半天时间尝试各种排列之后,这就是我想到的

如果您在Windows上,则可以使用Git for Windows installation或portable提供的Git Bash在Windows中运行此脚本

此脚本需要参数

- local path e.g. /d/source/project1 - Git URL e.g. https://username@bitbucket.org/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 如果有脚本打印的更改,则可以继续获取或拉取。脚本可能效率不高,但它为我完成了任务

更新-2015-10-30:stderr到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
你现在也可以找到是谁干的

我需要一个解决方案来自动更新我的生产环境,多亏了我分享的这个脚本,我们非常高兴


脚本是用XML编写的,需要。

如果要将任务添加为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

下面的脚本非常好用

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
使用简单的regexp:

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

对于最终在这个问题上寻找答案的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')
}

我只是想把这篇文章作为一篇真实的文章来发表,因为在评论中很容易漏掉这篇文章

这个问题的正确和最好的答案是@Jake Berger,非常感谢你,伙计,每个人都需要这个,每个人在评论中都忽略了这个。 因此,对于所有与此相关的人来说,这里是正确的答案,只需使用此命令的输出来了解您是否需要执行git pull。如果输出为0,则显然没有要更新的内容

@stackoverflow,给这家伙一个铃铛。 谢谢@Jake Berger

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

所有这些复杂的建议,而解决方案是如此简单:

#!/bin/bash

BRANCH="<your branch name>"
LAST_UPDATE=`git show --no-notes --format=format:"%H" $BRANCH | head -n 1`
LAST_COMMIT=`git show --no-notes --format=format:"%H" origin/$BRANCH | head -n 1`

git remote update
if [ $LAST_COMMIT != $LAST_UPDATE ]; then
        echo "Updating your branch $BRANCH"
        git pull --no-edit
else
        echo "No updates available"
fi

因为Neils answer帮了我很大的忙,这里有一个没有依赖项的Python翻译:

import os
import logging
import subprocess

def check_for_updates(directory:str) -> None:
    """Check git repo state in respect to remote"""
    git_cmd = lambda cmd: subprocess.run(
        ["git"] + cmd,
        cwd=directory,
        stdout=subprocess.PIPE,
        check=True,
        universal_newlines=True).stdout.rstrip("\n")

    origin = git_cmd(["config", "--get", "remote.origin.url"])
    logging.debug("Git repo origin: %r", origin)
    for line in git_cmd(["fetch"]):
        logging.debug(line)
    local_sha = git_cmd(["rev-parse", "@"])
    remote_sha = git_cmd(["rev-parse", "@{u}"])
    base_sha = git_cmd(["merge-base", "@", "@{u}"])
    if local_sha == remote_sha:
        logging.info("Repo is up to date")
    elif local_sha == base_sha:
        logging.info("You need to pull")
    elif remote_sha == base_sha:
        logging.info("You need to push")
    else:
        logging.info("Diverged")

check_for_updates(os.path.dirname(__file__))

hth

这一行在zsh适用于我,来自@Stephen Haberman的答案

git fetch; [ $(git rev-parse HEAD) = $(git rev-parse @{u}) ] \
    && echo "Up to date" || echo "Not up to date"

我想他已经检查了当地的分支机构,所以他需要一些其他的东西来显示它是否落后等等。他可以用git状态来做这件事。没错,在你拿到遥控器后,git状态也会显示出来。这是git pull-dry run的心情,

但是我认为每分钟运行一个cron脚本太难了。@takeshin:如果不上网,你不能检查远程存储库。如果没有任何新的获取,那么除了检查状态之外,它不会做更多的事情。如果您需要对远程更新做出快速、轻量级的反应,您可能需要考虑将某种通知挂接到远程存储库。@takeshin:如果您想每分钟都检查远程回购,我认为您错过了DVCS的要点。整个想法是能够在一段时间内独立开发,然后顺利地将其整合在一起。它不像cvs、svn、p4等,您必须始终在存储库中最新的内容之上工作。如果您确实需要其他人正在处理的内容,那么您应该使用不同的通信机制(如电子邮件)来告诉您何时可以提取。是否有任何示例脚本来比较这些值?git rev list HEAD…origin/master-count将为您提供这两个文件之间不同提交的总数。@jberger澄清,这将只显示您落后的提交数量,而不是领先和落后的提交数量,并且它仅在您首先使用git fetch或git remote update时有效。git状态也显示了计数,顺便说一句。@Dennis我想。。是原始/主文件中的提交,减去头,即后面的提交数量。鉴于是领先和领先的i.e。据我所知,这是唯一一个实际检查源站是否有更新但不隐式执行获取的解决方案。@takeshin我想您可以按照@brool和git rev list的建议将git ls remote origin-h refs/heads/master组合在一起-最大计数=1 origin/master。如果它们返回相同的散列,则自上次使用pull、fetch、remote update等更新远程引用以来,远程分支没有发生更改。这样做的好处是,您不必立即删除所有提交的内容,但可以将其留到更方便的时间。但是,由于远程更新是非破坏性的,您也可以这样做。您还可以尝试git status-s-u no,它的输出比git status-u no短。@mhulse,git remote-v update。查看git remote-help的输出以获得更全面的解释。@ChrisMaes很有道理。较旧版本的git需要更明确的语法。我对我拥有的各种系统进行了实验,发现{u}可以与git 1.8.3.2一起工作,但@不能。但是@适用于1.8.5.4。故事的寓意:git不断改进,值得拥有最新版本。现在@需要一个说明符。您可以使用@{0}而不是@。请注意:git pull-dry-run可能无法按预期工作。看起来,git pull将未知选项直接传递给git fetch。结果是正常的git pull。pull只是一种立即执行获取和合并的短方法,如果您需要检查远程repo状态,那么您实际上是在模拟获取。所以git-fetch-v-dry-run是您所需要的;git日志头-如果本地分支有默认远程分支,则可以使用oneline。@philpirozhkov如果有默认远程分支,我认为一个简单的git状态就可以了。我的答案是任意两个分支的通用答案,其中一个分支可能跟踪另一个分支,也可能不跟踪另一个分支。git rev list HEAD…origin/master-count将为您提供这两个分支之间不同提交的总数。简短而简单。我最喜欢的解决方案是只显示新提交两次,我如何在Ubuntu批处理文件中使用它,以便在该命令显示需要拉取的情况下运行其他命令?参考你之前的,目前我无法给你一个明确的答案。在我发表这些评论的时候,我正潜入git的深处,尤其是远程和差异。从那以后已经几个月了,很多知识都埋藏在我的脑子里;如果您正在查找两者之间的“不同”提交数,那么。。。似乎是您解决方案的有效部分。谢谢。这是clean.neat,考虑在纯shellNow中重写它,您还可以直接从docker容器中使用gitcheck,将您的文件保存在主机中。有关更多信息,请参阅bash中的gitcheck github项目类似工具。git mrepo-c这将显示所有挂起的提交。如果使用-depth 1克隆git存储库以限制下载大小,则此命令将失败。你知道,如果有办法修复它吗?git日志返回了许多行,并给出了一个错误提示:[:参数太多,我会切换到git rev parse-验证head这是bash完成的简单字符串比较。如果出现问题,我建议您检查语法,即键入错误。首先运行git log-pretty=%H…refs/heads/master^获取上一个提交版本的SHA1,然后运行git ls remote origin-H refs/heads/master | cut-f1获取远程源的SHA1。这两个是git命令,与bash无关。bash在squar中做什么
方括号用于比较第一个命令和第二个命令的输出,如果它们相等,则返回true并运行git pull。如果它们相等,则返回true并运行git pull。我知道我在吹毛求疵,但为了避免别人的困惑,这应该是,如果他们不平等的话。另外,无论出于什么原因,第一个git命令对我来说都不起作用。我在Git2.4.1上。所以我只是使用git log-pretty=%H master | head-n1。但我不确定这是否完全相同。我独立发现了{u},并在看到你的答案之前更新了我的答案。git rev parse{u}会在没有git fetch的情况下显示最新的提交吗?这就是问题!尽管如此,您的逻辑使用==这意味着如果上游没有任何更改。我用过!=检查我的应用程序是否有来自上游的更改。别忘了先去拿git!我添加了git fetch,因为它确实需要回答原始问题。@是HEAD btw的缩写。Windows用户需要在@{u}周围加上单引号,例如git rev parse“@{u}”缺少原始答案“!”在if中。git diff的返回值为零,当没有更改时。虽然我需要用origin/master或其他修订替换remotes/origin/HEAD,但我不会在sed上使用/g,如果在分支名称中使用斜杠。这只是sed的//\//。@wjordan当远程存储库无法访问或处于维护状态时,您的解决方案将失败,并将触发最新版本。这将不起作用。git状态只是一个本地检查,因此如果您已经更新了远程定义,它只会告诉您分支是否落后。最后提交和最后更新总是相等的,即使有更改。此解决方案很好且简单,put需要在代码之前执行git远程更新,要获取最新的源提交信息,git远程更新是否应该在git show命令之前追加?
#!/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
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
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
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')
}
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
#!/bin/bash

BRANCH="<your branch name>"
LAST_UPDATE=`git show --no-notes --format=format:"%H" $BRANCH | head -n 1`
LAST_COMMIT=`git show --no-notes --format=format:"%H" origin/$BRANCH | head -n 1`

git remote update
if [ $LAST_COMMIT != $LAST_UPDATE ]; then
        echo "Updating your branch $BRANCH"
        git pull --no-edit
else
        echo "No updates available"
fi
import os
import logging
import subprocess

def check_for_updates(directory:str) -> None:
    """Check git repo state in respect to remote"""
    git_cmd = lambda cmd: subprocess.run(
        ["git"] + cmd,
        cwd=directory,
        stdout=subprocess.PIPE,
        check=True,
        universal_newlines=True).stdout.rstrip("\n")

    origin = git_cmd(["config", "--get", "remote.origin.url"])
    logging.debug("Git repo origin: %r", origin)
    for line in git_cmd(["fetch"]):
        logging.debug(line)
    local_sha = git_cmd(["rev-parse", "@"])
    remote_sha = git_cmd(["rev-parse", "@{u}"])
    base_sha = git_cmd(["merge-base", "@", "@{u}"])
    if local_sha == remote_sha:
        logging.info("Repo is up to date")
    elif local_sha == base_sha:
        logging.info("You need to pull")
    elif remote_sha == base_sha:
        logging.info("You need to push")
    else:
        logging.info("Diverged")

check_for_updates(os.path.dirname(__file__))
git fetch; [ $(git rev-parse HEAD) = $(git rev-parse @{u}) ] \
    && echo "Up to date" || echo "Not up to date"