检查是否需要在Git中拉
如何检查远程存储库是否已经更改,我需要拉?
现在我使用这个简单的脚本:
git pull --dry-run | grep -q -v 'Already up-to-date.' && changed=1
但是它相当重。
有没有更好的办法? 理想的解决scheme将检查所有远程分支,并返回更改分支的名称和每个提交的新数量。
首先使用git remote update
,使您的远程refs最新。 然后你可以做几件事情之一,比如:
-
git status -uno
会告诉你你正在追踪的分支是前进还是后退。 如果什么都不说,本地和远程都是一样的。 -
git show-branch *master
将显示所有名称以“master”结尾的分支(例如master和origin / master )的提交。
如果你使用-v
和git remote update
( git remote -v update
),你可以看到哪个分支得到了更新,所以你不需要任何进一步的命令。
但是,它看起来像你想要在脚本或程序中做到这一点,结束了一个真/假的价值。 如果是这样,有办法检查您当前的HEAD提交和您正在跟踪的分支的头部之间的关系,但由于有四个可能的结果,您不能将其减less到是/否的答案。 然而,如果你准备做一个pull --rebase
那么你可以把“本地落后”和“本地分化”看作“需要拉动”,而另外两个就是“不需要拉动”。
你可以使用git rev-parse <ref>
获得任何ref的提交ID,所以你可以对master和origin / master进行比较。 如果他们是平等的,分支是一样的。 如果他们不平等,你想知道哪个领先于另一个。 使用git merge-base master origin/master
会告诉你两个分支的共同祖先,如果他们没有分歧,这将是相同的一个或另一个。 如果你得到三个不同的ID,分支已经分歧。
要做到这一点,例如在脚本中,您需要能够引用当前分支以及它正在跟踪的远程分支。 /etc/bash_completion.d
的bash提示设置function有一些有用的代码来获取分支名称。 但是,您可能实际上并不需要获取名称。 Git有一些简洁的指向分支和提交的简写(如git rev-parse --help
)。 特别是,您可以使用@
作为当前分支(假设您不处于分离头状态),使用@{u}
作为其上游分支(例如origin/master
)。 所以git merge-base @ @{u}
会返回当前分支和它的上游分支和git rev-parse @
和git rev-parse @{u}
的提交(hash)将会给你的哈希两个提示。 这可以概括为以下脚本:
#!/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}
。
如果您想要针对与为当前分支configuration的远程分支不同的远程分支进行检查,则可以select显式传递上游分支, UPSTREAM=${1:-'@{u}'}
。 这通常是remotename / branchname的forms。 如果没有给出参数,则该值默认为@{u}
。
该脚本假定您已经完成了git fetch
或git remote update
,以使跟踪分支更新。 我没有将它构build到脚本中,因为它能够以单独的操作执行抓取和比较更加灵活,例如,如果因为您最近已经抓取而无需抓取进行比较。
如果你有一个上游分支
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
列出,那么没有什么可以合并。
请注意,即使你在一个function分支上,这个function也可以工作,因为如果明确地指的是origin/master
而不是隐式地使用Git记住的上游分支 ,
如果这是一个脚本,你可以使用:
git fetch $(git rev-parse HEAD) == $(git rev-parse @{u})
(注意:这个与以前的答案相比,好处是你不需要单独的命令来获取当前的分支名称,“HEAD”和“@ {u}”(当前分支的上游)负责处理它。 “git rev-parse –help”了解更多详情。)
命令
git ls-remote origin -h refs/heads/master
将列出远程的当前头 – 你可以将它与以前的值比较,或者看你在本地回购中是否有SHA。
这是一个Bash git pull --dry-run
比较当前分支的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 命令replace语法进行分组和嵌套。 -
git rev-parse --abbrev-ref @{u}
返回一个缩写的上游引用(例如origin/master
),然后通过pipedsed
命令(例如origin/master
)将其转换为空格分隔的字段。 - 这个string被送入
git ls-remote
,它返回远程分支的头部提交。 该命令将与远程存储库进行通信。 pipe道cut
命令只提取第一个字段(提交散列),删除制表符分隔的引用string。 -
git rev-parse HEAD
返回本地提交散列。 - Bash语法
[ a = b ] && x || y
[ a = b ] && x || y
完成了一行:这是一个Bash string比较=
在一个testing构造[ test ]
,其次是和列表和or列表结构&& true || false
&& true || false
。
我build议你去看脚本https://github.com/badele/gitcheck ,我已经编写了这个脚本检查一次通过所有的git仓库,它显示谁没有提交,谁没有推/拉。
这里是一个示例结果
我认为最好的办法是:
git diff remotes/origin/HEAD
假设你已经注册了这个refspec。 你应该如果你已经克隆了仓库,否则(例如,如果仓库在本地创build,并推送到远程),你需要明确添加refspec。
我基于@jberger的评论基于这个解决scheme。
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
我会按照brool的build议。 以下单行脚本将取出最后一个提交版本的SHA1,并将其与远程原点的SHA1进行比较,并且只在两者不同时才进行更改。 基于git pull
或git fetch
的解决scheme更为轻量化。
[ `git log --pretty=%H ...refs/heads/master^` != `git ls-remote origin -h refs/heads/master |cut -f1` ] && git pull
已经有许多非常丰富和巧妙的答案。 为了提供一些对比,我可以做一个非常简单的线。
# 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
如果你运行这个脚本,它将testing当前分支是否需要一个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
之前commit
。
要将此代码用作钩子,只需复制/粘贴脚本
.git/hooks/pre-commit
和
chmod +x .git/hooks/pre-commit
运行git fetch (remote)
来更新你的远程参考,它会告诉你什么是新的。 然后,当你结账你的本地分行,它会告诉你是否在上游。
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
这里是我的一个bash脚本的版本,它检查预定义文件夹中的所有回购:
https://gist.github.com/henryiii/5841984
它可以区分常见的情况,比如需要拉取和推送,而且是multithreading的,所以抓取一次完成。 它有几个命令,如拉和状态。
把一个符号链接(或脚本)放在你的path中的一个文件夹,然后它作为git all status
(等)。 它仅支持origin / master,但可以编辑或与其他方法组合。
我使用基于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
它也可以在这里使用 。
在阅读了许多答案和多个post后,花了半天的时间尝试各种排列,这就是我所想到的。
如果你在Windows上,你可以使用Git for Windows提供的gitbash(安装或便携)在Windows上运行这个脚本。
这个脚本需要参数
- 本地patheg / d / source / project1 - Git URL例如https://username@bitbucket.org/username/project1.git - 密码 如果密码不能以明文formsinput到命令行, 然后修改脚本来检查GITPASS是否为空,不 replace,让git提示input密码
该脚本将
- find 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开发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
也许这个,如果你想添加任务为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
你现在也可以find一个Phing脚本 。
我需要一个解决scheme来自动更新我的生产环境,感谢我分享的这个脚本,我们非常高兴。
该脚本是用XML编写的,需要Phing 。