简体   繁体   English

盲目更新git repo,可能是另一个分支

[英]Updating a git repo blindly, possibly to a different branch

As part of deployment automation, I have a local git repository that was created with a command of the form 作为部署自动化的一部分,我有一个使用表单命令创建的本地git存储库

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

From time to time it is necessary for the automation to pull further changes from the remote, and this may involve a change of branch. 自动化有时需要从远程执行进一步的更改,这可能涉及更改分支。 The automation has no state; 自动化没有状态; it knows what the branch is supposed to be, but not what it was. 它知道分支应该是什么,但不知道它是什么。 Also, there should never be any local changes in this repository, but mistakes happen. 此外,此存储库中永远不应存在任何本地更改,但会发生错误。

I am looking for a command, or sequence of commands, that will have the same overall effect as wiping out the repository and re-cloning it, but which will minimize the amount of data re-downloaded in the common case (ie $BRANCH has not changed and git pull would have done a fast-forward merge). 我正在寻找一个命令或命令序列,它将具有与擦除存储库并重新克隆它相同的整体效果,但这将最大限度地减少在常见情况下重新下载的数据量(即$BRANCH具有没有改变, git pull会做一个快进合并)。

Getting most of the way there is easy: just confirm a few things and then run git fetch etc. As a sort of proto-type shell script: 大部分方法都很简单:只需确认一些内容然后运行git fetch等。作为一种proto-type 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

(this verifies that it is in fact a git repository) (这验证它实际上是一个git存储库)

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

(this tests which branch is currently checked-out, if any; it fails with fatal: ref HEAD is not a symbolic ref if HEAD is detached; refine with -q and a different error if needed) (这测试当前检出哪个分支,如果有的话;它失败并且fatal: ref HEAD is not a symbolic ref如果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

(this makes sure there is a remotes/origin/master or whatever, spelling out refs/remotes/ just to be generally paranoid—feel free to remove refs/remotes/ from these) (这确保有一个遥控器/原点/主人或其他什么,拼写出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

(this makes sure there are no commits on $branch locally that are not present in the upstream) (这可以确保本地$ branch上没有提交上游不存在的提交)

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

(this is somewhat optional, it depends on how strict you want to be about requiring that the tree be clean, but the next bits assume "clean") (这在某种程度上是可选的,它取决于您希望树要求干净的严格程度,但下一位假设“干净”)

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

(this updates $remote/$branch to match what's actually on the remote now, regardless of any fetch = lines stored in the repository) (这会更新$ remote / $ branch以匹配当前在遥控器上的实际内容,无论存储在存储库中的任何fetch =行)

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

(this last step moves $branch , which we've confirmed we're on, to point to the same commit as the newly updated $remote/$branch , and also replaces the work-directory contents with those from the newly updated post-fetch branch). (这最后一步移动$branch ,我们已经确认我们已经确认,它指向与新更新的$remote/$branch相同的提交,并且还将工作目录内容替换为来自新更新的post的内容 -获取分支)。

If some of the above steps fail (the "exit 1" bits, which might need modification for your purposes) then you fall back to re-cloning. 如果上述某些步骤失败(“退出1”位,可能需要根据您的目的进行修改),那么您将重新进行重新克隆。


If you're willing to let the local repository grow (with more branches), you can ditch much of the above and simply run git fetch with the appropriate refspec, and/or add that refspec to the fetch = lines associated with the remote. 如果你愿意让本地存储库增长(有更多分支),你可以放弃上面的大部分内容,只需使用适当的refspec运行git fetch ,和/或将refspec添加到与远程关联的fetch =行。 (Add a git clean -fdx to remove any files that should not be present, and note that the git reset --hard step will remove 1 any commits that should not be present.) (添加git clean -fdx删除不应该出现的任何文件,并注意git reset --hard一步将删除1项 ,不应该存在任何承诺 。)

In particular, what git clone --single-branch does is modify the fetch line for the given remote. 特别是, git clone --single-branch所做的是修改给定远程的获取行 For instance: 例如:

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

clones the repo and checks out branch foo as usual, but instead of leaving the .git/config entries for origin reading: 克隆repo并像往常一样检出分支foo ,但不是将.git/config条目留给origin读取:

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

it replaces the fetch line so that you have instead: 它取代了fetch行,所以你改为:

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

This means future git fetch operations—remember that git pull is just git fetch followed by either merge or rebase—limit themselves to bringing over objects needed to update your local refs/remotes/origin/foo reflecting refs/heads/foo (branch foo) on the remote, rather than those for refs/remotes/origin/* (reflecting all branches on the remote through the first * ). 这意味着未来的git fetch操作 - 请记住, git pull只是git fetch然后是merge或rebase-limit本身,用于引入更新本地refs/remotes/origin/foo所需的对象,反映refs/heads/foo (branch foo)在遥控器上,而不是那些refs/remotes/origin/* (通过第一个*反映refs/remotes/origin/*上的所有分支)。

If you wish to now update with +refs/heads/bar:refs/remotes/origin/bar (as if you'd cloned that branch), you can simply add or replace that as a (or the) fetch = line under [remote "origin"] . 如果您现在希望使用+refs/heads/bar:refs/remotes/origin/bar更新+refs/heads/bar:refs/remotes/origin/bar (就像您克隆了该分支一样),您只需在[remote "origin"]或] fetch =行下添加或替换它即可。 [remote "origin"]

If branches share a lot of history, this will save a lot of data-transfer over re-cloning, though of course you'll wind up with both foo and bar objects in the local repository. 如果分支共享大量历史记录,这将通过重新克隆来节省大量数据传输,但当然您将在本地存储库中使用foobar对象。 If branches share very little history, there's little cost to starting over (unless you ever switch back of course). 如果分支的历史记录很少,那么重新开始就没什么代价(除非你当然转回去 )。


1 Except for traces left in the branch's reflogs. 1除了分支的reflog中留下的痕迹。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM