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

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

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

但它相当重。

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


当前回答

下面是一个Bash一行代码,比较了当前分支的HEAD提交哈希和它的远程上游分支,不需要大量的git获取或git拉取——需要干运行操作:

[ $(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

以下是这条有点密集的线是如何被分解的:

Commands are grouped and nested using $(x) Bash command-substitution syntax. git rev-parse --abbrev-ref @{u} returns an abbreviated upstream ref (e.g. origin/master), which is then converted into space-separated fields by the piped sed command, e.g. origin master. This string is fed into git ls-remote which returns the head commit of the remote branch. This command will communicate with the remote repository. The piped cut command extracts just the first field (the commit hash), removing the tab-separated reference string. git rev-parse HEAD returns the local commit hash. The Bash syntax [ a = b ] && x || y completes the one-liner: this is a Bash string-comparison = within a test construct [ test ], followed by and-list and or-list constructs && true || false.

其他回答

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

由brol给出的是检查遥控器中是否发生了变化的最简单的方法。

从局部头部开始:

$ git log -1 --oneline @
9e1ff307c779 (HEAD -> master, tag: v5.15-rc4, origin/master, origin/HEAD) Linux 5.15-rc4

我看到我拉的原点在那个标签处是最新的。Git状态也是这么说的。但这只是最新的本地数据,取数据后的(快进)合并。

检查远程HEAD是否去了某个地方,还有master,也许还有一些新标签:

$ git ls-remote origin HEAD master --tags 'v5.1[56]-rc[345]*'

84b3e42564accd94c2680e3ba42717c32c8b5fc4        HEAD
84b3e42564accd94c2680e3ba42717c32c8b5fc4        refs/heads/master
71a6dc2a869beafceef1ce46a9ebefd52288f1d7        refs/tags/v5.15-rc3
5816b3e6577eaa676ceb00a848f0fd65fe2adc29        refs/tags/v5.15-rc3^{}
f3cee05630e772378957a74a209aad059714cbd2        refs/tags/v5.15-rc4
9e1ff307c779ce1f0f810c7ecce3d95bbae40896        refs/tags/v5.15-rc4^{}

HEAD仍然在同一个分支上,但不再是同一个提交。本地@提交保留在v5.15-rc4标记中。这与kernel.org git的摘要表顶部的信息大致相同:

Branch: master <commit message> <author> age: 2 hours

只有ls-remote收集的信息更少-但只有我知道我是9e1ff…即v5.15-rc4。

除了命名引用(HEAD, master)或标签,还可以从任何repo中获得头部或分支的列表:

$ git ls-remote --heads git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git 

af06aff2a0007976513134dfe993d55992dd866a        refs/heads/akpm
20bcee8e95f783c11a7bea1b6a40c70a37873e0a        refs/heads/akpm-base
a25006a77348ba06c7bc96520d331cd9dd370715        refs/heads/master
4d5a088c93cea1c821d02a2217c592391d9682e2        refs/heads/pending-fixes
4de593fb965fc2bd11a0b767e0c65ff43540a6e4        refs/heads/stable

这里用URL代替“origin”。


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

如果你这么问,就拔。

我如何检查远程存储库是否最终完成了一些事情,我想要拉?

然后取回,检查,合并。


使用单个git命令:

$ git rev-list -1  master
9e1ff307c779ce1f0f810c7ecce3d95bbae40896
$ git rev-list -1  @
9e1ff307c779ce1f0f810c7ecce3d95bbae40896

这本身说明不了什么,但假设我知道我什么也没做,那么:

$ git ls-remote origin HEAD master
60a9483534ed0d99090a2ee1d4bb0b8179195f51        HEAD
60a9483534ed0d99090a2ee1d4bb0b8179195f51        refs/heads/master

会告诉我遥控器换了。自从上次编辑以来确实是这样。kernel.org说年龄:46分钟。关于最后一次在master上提交。

git取回后:

$ git rev-list -1 master     
9e1ff307c779ce1f0f810c7ecce3d95bbae40896

$ git rev-list -1 FETCH_HEAD
60a9483534ed0d99090a2ee1d4bb0b8179195f51

$ git log --oneline ..FETCH_HEAD        
60a9483534ed (origin/master, origin/HEAD) Merge tag 'warning-fixes-20211005' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs
f6274b06e326 Merge tag 'linux-kselftest-fixes-5.15-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest
ef31499a87cf fscache: Remove an unused static variable
d9e3f82279bf fscache: Fix some kerneldoc warnings shown up by W=1
bc868036569e 9p: Fix a bunch of kerneldoc warnings shown up by W=1
dcb442b13364 afs: Fix kerneldoc warning shown up by W=1
c0b27c486970 nfs: Fix kerneldoc warning shown up by W=1
84b3e42564ac Merge tag 'media/v5.15-3' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
b60be028fc1a Merge tag 'ovl-fixes-5.15-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs
df5c18838ea8 Merge tag 'mips-fixes_5.15_1' of git://git.kernel.org/pub/scm/linux/kernel/git/mips/linux
206704a1fe0b media: atomisp: restore missing 'return' statement
740da9d7ca4e MIPS: Revert "add support for buggy MT7621S core detection"
1dc1eed46f9f ovl: fix IOCB_DIRECT if underlying fs doesn't support direct IO
2f9602870886 selftests: drivers/dma-buf: Fix implicit declaration warns
a295aef603e1 ovl: fix missing negative dentry check in ovl_rename()

现在我在本地拥有所有信息,但还没有合并。我还下载了所有的对象。git显示HASH或git diff HASH工作。

在这种情况下,合并几乎没有任何操作:快进到最后一次提交,没有额外的(真正的)合并,更不用说冲突了。这可以通过——ff-only来确保:

$ git merge --ff-only FETCH_HEAD
Updating 9e1ff307c779..60a9483534ed
Fast-forward
...
... 

那我怎么知道什么时候拉呢?只要这两个散列是/将是不同的:更新9e1ff307c779..60 a9483534ed快进。它们不可能是一样的,那就是“没什么可更新的”。

最新的reflog提交也是这样说的:

$ git log -10 --oneline -g
60a9483534ed (HEAD -> master, origin/master, origin/HEAD) HEAD@{0}: merge 60a9483534ed0d99090a2ee1d4bb0b8179195f51: Fast-forward
9e1ff307c779 (tag: v5.15-rc4) HEAD@{1}: pull: Fast-forward

在这种情况下,新标签的出现可能是最好的触发器和目标;返回到git ls-remote origin——tags PATTERN。


...别告诉我git remote show是另一个方法:

show给出关于遥控器的一些信息。 使用-n选项,git ls-remote不会首先查询远程头;取而代之的是使用缓存信息。

首先使用git远程更新,使您的远程参考更新到最新。然后你可以做以下几件事中的一件,比如:

Git status -uno将告诉你你正在跟踪的分支是在前面、后面还是已经偏离。如果没有显示,则本地和远程是相同的。 Git show-branch *master会显示所有以master结尾的分支中的提交(例如master和origin/master)。

如果你在git远程更新(git remote -v update)中使用-v,你可以看到哪些分支被更新了,所以你真的不需要任何进一步的命令。

However, it looks like you want to do this in a script or program and end up with a true/false value. If so, there are ways to check the relationship between your current HEAD commit and the head of the branch you're tracking, although since there are four possible outcomes you can't reduce it to a yes/no answer. However, if you're prepared to do a pull --rebase then you can treat "local is behind" and "local has diverged" as "need to pull", and the other two ("local is ahead" and "same") as "don't need to pull".

你可以使用git rev-parse <ref>获得任何ref的提交id,所以你可以对master和origin/master这样做并比较它们。如果它们相等,那么分支是相同的。如果它们不相等,你想知道哪个在前面。使用git merge-base master origin/master会告诉你两个分支的共同祖先,如果它们没有分叉,这将是一个或另一个相同的。如果您得到三个不同的id,则分支已经分离。

To do this properly, eg in a script, you need to be able to refer to the current branch, and the remote branch it's tracking. The bash prompt-setting function in /etc/bash_completion.d has some useful code for getting branch names. However, you probably don't actually need to get the names. Git has some neat shorthands for referring to branches and commits (as documented in git rev-parse --help). In particular, you can use @ for the current branch (assuming you're not in a detached-head state) and @{u} for its upstream branch (eg origin/master). So git merge-base @ @{u} will return the (hash of the) commit at which the current branch and its upstream diverge and git rev-parse @ and git rev-parse @{u} will give you the hashes of the two tips. This can be summarized in the following script:

#!/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 ls-remote origin -h refs/heads/master

将列出远程上的当前头—您可以将其与以前的值进行比较,或者查看您的本地回购中是否有SHA。

如果你想把task添加为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

我使用了一个基于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

这里也有。