盲目更新git repo,可能会更新到其他分支



作为部署自动化的一部分,我有一个本地git存储库,它是用形式的命令创建的

git clone --single-branch -b $BRANCH $REMOTE $PATH

自动化有时需要从远程获取进一步的更改,而这可能涉及分支的更改。自动化没有状态;它知道分支应该是什么,但不知道它是什么。此外,应该在这个存储库中永远不会有任何本地更改,但会发生错误。

我正在寻找一个命令或命令序列,它将具有与清除存储库并重新克隆存储库相同的总体效果,但在常见情况下,它将最大限度地减少重新下载的数据量(即$BRANCH没有更改,git pull会进行快速合并)。

获得大部分方法很容易:只需确认一些事情,然后运行git fetch等。作为一种原型shell脚本:

cd ... # the place you want to test.
       # note that `git-sh-setup` (below) demands
       # that this be the top of the repository.
git rev-parse --verify HEAD >/dev/null || exit 1

(这验证了它实际上是一个git存储库)

branch=$(git symbolic-ref --short HEAD) || exit 1

(这将测试当前签出的分支(如果有的话);如果HEAD分离,则使用fatal: ref HEAD is not a symbolic ref失败;使用-q进行细化,如果需要,则使用不同的错误)

if ! git rev-parse --verify refs/remotes/$remote/$branch >/dev/null 2>&1; then
    echo "$remote/$branch does not exist" 1>&2
    exit 1
fi

(这确保了有一个遥控器/原点/主控器或其他什么,拼写refs/remotes/只是为了表现出普遍的偏执狂——可以随意从中删除refs/remotes/

ahead=$(git rev-list --count refs/remotes/$remote/$branch..$branch)
if [ $ahead -gt 0 ]; then
    echo "$branch is ahead of $remote/$branch by $ahead commit(s)" 1>&2
    exit 1
fi

(这确保本地$branch上没有上游不存在的提交)

. $(git --exec-path)/git-sh-setup # for require_clean_work_tree
require_clean_work_tree "update from remote"

(这在一定程度上是可选的,这取决于您希望对要求树是干净的要求有多严格,但接下来的部分假设为"干净")

git fetch $remote +refs/heads/$branch:refs/remotes/$remote/$branch

(这会更新$remote/$branch,以匹配现在远程上的实际内容,而不考虑存储库中存储的任何fetch =行)

git reset --hard refs/remotes/$remote/$branch

(最后一步将$branch移动到与新更新的$remote/$branch相同的提交,并将工作目录内容替换为来自新更新的后获取分支的内容)。

如果上面的一些步骤失败("退出1"位,出于您的目的可能需要修改),那么您就可以重新克隆了。


如果您愿意让本地存储库增长(具有更多分支),您可以放弃上面的大部分内容,只需使用适当的refspec运行git fetch,和/或将该refspec添加到与远程相关联的fetch =行中。(添加git clean -fdx以删除任何不应存在的文件,并注意git reset --hard步骤将删除1任何不应出现的提交。)

特别是,git clone --single-branch所做的是修改给定远程的获取行。例如:

git clone --single-branch -b foo git://...

克隆repo并像往常一样签出分支foo,但不将.git/config条目留给origin读取:

[remote "origin"]
    url = git://...
    fetch = +refs/heads/*:refs/remotes/origin/*

它取代了fetch行,因此您可以改为:

    fetch = +refs/heads/foo:refs/remotes/origin/foo

这意味着未来的git fetch操作——请记住,git pull只是git fetch,后面跟着merge或rebase——将自己限制为带来更新本地refs/remotes/origin/foo所需的对象,这些对象反映了远程上的refs/heads/foo(分支foo),而不是refs/remotes/origin/*(通过第一个*反映远程上的所有分支)。

如果您现在希望使用+refs/heads/bar:refs/remotes/origin/bar进行更新(就像您克隆了该分支一样),您可以简单地将其添加或替换为[remote "origin"]下的(或)fetch =行。

如果分支共享大量历史记录,这将通过重新克隆节省大量数据传输,当然,您最终会在本地存储库中同时使用foobar对象。如果分支机构共享的历史很少,则重新开始的成本也很低(当然,除非您将切换回)。


1除了分支回流中留下的痕迹。

最新更新