检查Git中是否需要拉

时间:2010-07-15 17:32:28

标签: git bash shell

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

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

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

但它相当沉重。

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

24 个答案:

答案 0 :(得分:772)

首先使用git remote update,让您的遥控器更新。然后你可以做以下几件事之一,例如:

  1. git status -uno会告诉您正在跟踪的分支是在前方,后方还是分歧。如果没有说什么,本地和远程都是一样的。

  2. git show-branch *master将显示名称以“master”结尾的所有分支中的提交(例如 master origin / master

  3. 如果您使用-v git remote updategit remote -v update),您可以看到哪些分支已更新,因此您不需要任何其他命令。

    但是,看起来您希望在脚本或程序中执行此操作并最终得到true / false值。如果是这样,有很多方法可以检查当前 HEAD 提交与您正在跟踪的分支的负责人之间的关系,尽管由于有四种可能的结果,您无法将其减少为是/没有答案。但是,如果你准备做pull --rebase那么你可以将“本地落后”和“本地分歧”视为“需要拉动”,另外两个则称为“不需要拉动”

    您可以使用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 fetchgit remote update,以使跟踪分支更新。我没有将它构建到脚本中,因为它能够更灵活地进行提取和比较作为单独的操作,例如,如果你想比较而不是因为你最近已经提取而没有提取。

答案 1 :(得分:123)

如果您有上游分支

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 :(得分:53)

如果是脚本,可以使用:

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

(注意:这与前面的答案的好处是你不需要单独的命令来获取当前的分支名称。“HEAD”和“@ {u}”(当前分支的上游)负责有关详细信息,请参阅“git rev-parse --help”。)

答案 3 :(得分:34)

命令

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

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

答案 4 :(得分:29)

这是一个Bash单行程序,它将当前分支的HEAD提交哈希与其远程上游分支进行比较,不需要繁重的git fetchgit 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}返回缩写的上游引用(例如origin/master),然后通过管道sed命令将其转换为空格分隔的字段,例如origin mastergit ls-remote
  • 此字符串被送入cut,返回远程分支的头部提交。此命令将与远程存储库通信。管道git rev-parse HEAD命令仅提取第一个字段(提交哈希),删除以制表符分隔的引用字符串。
  • [ a = b ] && x || y返回本地提交哈希。
  • Bash语法=完成了单行:这是测试构造[ test ]中的Bash string-comparison && true || false,后跟and-list和or-list构造{{1}}。

答案 5 :(得分:19)

我建议你去看看脚本https://github.com/badele/gitcheck。我编写了这个脚本,用于检查所有Git存储库的一次传递,并显示谁没有提交,谁没有推/拉。

这是一个示例结果:

Enter image description here

答案 6 :(得分:10)

我将这个解决方案基于@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

答案 7 :(得分:9)

There are many very feature rich and ingenious answers already. To provide some contrast, I could make do with a very simple line.

# 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

答案 8 :(得分:8)

我认为最好的方法是:

git diff remotes/origin/HEAD

假设您已注册此refspec。如果您已经克隆了存储库,则应该这样做(即,如果在本地创建了repo并将其推送到远程),则需要显式添加refspec。

答案 9 :(得分:6)

我会按照布鲁尔建议的方式行事。以下单行脚本获取上次提交的版本的SHA1,并将其与远程源的SHA1进行比较,并仅在它们不同时才进行更改。 而且基于git pullgit fetch的解决方案更加轻量级。

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

答案 10 :(得分: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

答案 11 :(得分:5)

如果您运行此脚本,它将测试当前分支是否需要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
}

将它作为Git钩子预先提交以避免

非常方便
Merge branch 'foobar' of url:/path/to/git/foobar into foobar

commit之前的pulling

要将此代码用作挂钩,只需在

中复制/粘贴脚本即可
.git/hooks/pre-commit

chmod +x .git/hooks/pre-commit

答案 12 :(得分:5)

我只想将其发布为实际帖子,因为在评论中很容易错过。

@Jake Berger给出了这个问题的正确和最佳答案,非常感谢老兄,每个人都需要这个,每个人都在评论中错过了这个。 所以对于每个在这里苦苦挣扎的人来说都是正确答案,只需使用此命令的输出来知道是否需要执行git pull。如果输出为0,那么显然没有什么可以更新的。

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

    <script type="text/javascript" id="lightbox-js">
    function openModal(i) {
      document.getElementById('myModal-'+i).style.display = "block";
      document.getElementById('pageHeader').style.zIndex = "0";
      document.getElementById('pageFooter').style.zIndex = "0";
      document.getElementById('pageContainer').style.textAlign = "left";
    }

    function closeModal(i) {
      document.getElementById('myModal-'+i).style.display = "none";
      document.getElementById('pageHeader').style.zIndex = "10";
      document.getElementById('pageFooter').style.zIndex = "9";
      document.getElementById('pageContainer').style.textAlign = "center";
    }

    var slideIndex = 1;
    showSlides(slideIndex);

    function plusSlides(n) {
      showSlides(slideIndex += n);
    }

    function currentSlide(n) {
      showSlides(slideIndex = n);
    }

    function showSlides(n) {
      var i;
      var slides = document.getElementsByClassName("mySlides");
      var dots = document.getElementsByClassName("demo");
      var captionText = document.getElementById("caption");
      if (n > slides.length) {slideIndex = 1}
      if (n < 1) {slideIndex = slides.length}
      for (i = 0; i < slides.length; i++) {
          slides[i].style.display = "none";
      }
      for (i = 0; i < dots.length; i++) {
          dots[i].className = dots[i].className.replace(" active", "");
      }
      slides[slideIndex-1].style.display = "block";
      dots[slideIndex-1].className += " active";
      captionText.innerHTML = dots[slideIndex-1].alt;
    }
</script>

答案 13 :(得分:4)

运行git fetch (remote)更新远程参考,它会显示新内容。然后,当您结帐当地分行时,它会告诉您它是否在上游。

答案 14 :(得分:3)

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

#!/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

答案 15 :(得分:2)

这是我的Bash脚本版本,用于检查预定义文件夹中的所有存储库:

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

它可以区分常见情况,例如需要拉和需要推送,并且它是多线程的,因此获取会立即发生。它有几个命令,如pull和status。

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

答案 16 :(得分:1)

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

将列出任何不在您的仓库中的遥控器中引用的所有内容。要捕获远程ref对你已经拥有的东西的更改(例如重置以前的提交)需要更多:

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 :(得分: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

答案 18 :(得分:0)

我使用基于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存储库的可选参数directory name调用它来执行操作。如果在没有参数的情况下调用脚本,它会假定当前目录是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

它也可用here

答案 19 :(得分:0)

After reading many answers and multiple posts, and spending half a day trying various permutations, this is what I have come up with.

If you are on Windows, you may run this script in Windows using Git Bash provided by Git for Windows (installation or portable).

This script requires arguments

- 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

The script will

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

If there is a change as printed by the script, then you may proceed to fetch or pull. The script may not be efficient, but it gets the job done for me.

Update - 2015-10-30: stderr to dev null to prevent printing the URL with the password to the console.

#!/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

答案 20 :(得分: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

答案 21 :(得分: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')
}

答案 22 :(得分:0)

因为Neils的回答对我有很大帮助,所以这是一个没有依赖项的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

答案 23 :(得分:-2)

您还可以找到a Phing script现在就这样做的人。

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

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