将远程切换到特定分支

时间:2014-08-22 02:06:33

标签: git branch

所以我理解分支的价值,但有一件事令我感到困惑。我有我的当地回购。我将更改推送到远程,它有一个post接收挂钩设置为在网站上写入文件。所以我创建了一个分支(new-branch)来尝试一些东西。我编辑文件,提交并推送到遥控器。大。问题是,虽然我的遥控器上的回购是最新的,但我认为它仍然可以掌握,因为新分支的变化并未在网站上反映出来。如何将远程设置为特定分支,以便分支驱动post接收挂钩,而不仅仅是默认为主分支?我只是合并到主人身上,但我还没有准备好,因为我仍在解决分支上的问题。

1 个答案:

答案 0 :(得分:3)

好的,根据评论,听起来你也控制着服务器,这里需要它。 (也就是说,我写的地方"他们"下面,它只是你自己,但戴着不同的帽子,就像它一样。)

让我们从一些背景开始:git push和"裸露"存储库

从git的角度来看,你处理你的回购,他们(无论他们是谁)都在处理他们的回购。因此,当您执行git push,即您已直接向其回购提交的提交时,他们可能正在处理他们的回购。他们可能已经签出了一些分支,并且正在编辑他们的 xyz.html或其他任何内容的副本。

如果你的推文覆盖了他们的xyz.html,那不会让他们烦恼吗?他们正在编辑中!因此,对于传统的存储库,git将拒绝尝试推送到当前已检出的任何分支(一个git status打印为"#在分支上..."或显示为git symbolic-ref --short HEAD)。 1 您只能推送到其他分支机构,根据定义,它们不会正常工作,因此他们不会生气了。

现在,事实上,很多这些集中在这里推动了#34;存储库不要有人在处理它们。他们经常被设置为&#34;裸露&#34;存储库,这意味着它们根本没有工作树。这反过来意味着&#34;他们&#34; - 他们&#34;他们&#34;可能是 - 不可能正在处理他们的xyz.html副本,因为他们首先 拥有<{1}}的工作副本。使用xyz.html克隆,或将常规存储库转换为裸存储库,禁用&#34;无法推送到当前分支&#34;检查,这是他们想要的。


1 特别是,你得到一个&#34;拒绝更新签出的分支&#34;错误。这实际上可以通过--bare在服务器上的(非裸)存储库中进行配置。


自动部署

到目前为止一切顺利;但现在他们可能希望他们的中央推送服务器裸存储库自动部署到网站(或者,对于代码存储库,像Jenkins这样的测试系统,或其他什么) - 关键点是&#34;他们&#34;,无论他们又是谁,都有一个部署一个特定分支的post-receive钩子。但我们只是将裸存储库描述为没有工作树。这种自动部署如何工作?让我们戴上另一顶帽子,成为他们的#34;。

因为这是git,实际上有很多方法,但通常(也许是最好的)方法是使用post-receive脚本来检查哪些分支正在更新,以及是否有趣的(s) )是/被推送到,是否进行部署。

这是一个简单的receive.denyCurrentBranch钩子shell脚本的例子,它有一个未实现的post-receive shell函数来部署推送到deploy

master

Git运行post-receive钩子,其stdin被输入一系列输入线。每行有三个项目。最后一个是引用的全名,因此分支主文件是#! /bin/sh deploy() { local newref=$1 branch=$2 echo deploy invoked: $newref $branch } while read oldsha newsha refname; do case "$refname" in refs/heads/master) deploy $newsha ${refname#refs/heads/};; esac done 。前两个是&#34; old&#34; SHA-1和&#34; new&#34;分别为SHA-1,供参考。

最新的SHA-1值中的一个最多可以是40个refs/heads/master个字符:这意味着正在创建引用(旧的SHA-1全部为零)或删除(新的全部为零) 。否则,引用当前存在并且只是被更新(通常指向新提交):它曾用于指向旧ID,现在指向新ID。

(在 pre -receive hook中,它获得完全相同的东西,你可以拒绝更新引用的尝试。在 post -receive hook中更新已经发生了,你唯一能做的就是报告它或以某种方式利用它。)

在我们的案例中,我们真的不关心旧的价值。如果分支0之前从未存在过,或者之后部署的是什么,那也无关紧要。 (好吧,我们可能想要它,在这种情况下我们可以添加它:毕竟这只是一个shell脚本。)我们甚至不需要新值,因为我们可以使用git命令从存储库中读取它,但它很高兴,特别是如果我们想要检查并抱怨分支是否已被删除。 (如果尝试读取master分支引用失败,则分支可能被删除,但如果服务器起火并且存储库也被半毁坏,则可能发生这种情况。这种情况我们可能不在乎了。:-))

但主要是,我们需要做的是检查分支master中的所有文件,并将它们粘贴到部署区域。事实证明,通过指定备用工作树,即使在裸存储库中,我们也可以使用简单的master执行此操作:

git checkout

(旁注:有时您会将其视为NULL_SHA1=0000000000000000000000000000000000000000 # 40 0s deploy() { local newref=$1 branch=$2 if [ $newref = $NULL_SHA1 ]; then echo "deploy: branch $branch is deleted!" return 1 fi # next bit is stupid, hardcodes "master" even though # we have "$branch", but read on... git --work-tree=/deploy/master checkout master } 或类似。这完全相同。如果您没有指定GIT_WORK_TREE=/deploy/master git checkout master git使用--work-tree,如果& #39; s set。)

如果我们想要的不只是一个($GIT_WORK_TREE)自动部署分支,我们可以在我们脚本中调用master的集合中添加更多分支名称,并修复哑位:check out例如deploy$branch

虽然有很多潜在的故障:

  1. 目标目录(此处为/deploy/$branch)必须存在。
  2. 这种/deploy/master更新了git&#34;当前分支&#34;的概念。一个裸存储库仍然有这样的东西:还有一个名为git checkout的文件包含当前的分支名称。如果我们HEAD我们会改变它;这会影响从这个裸存储库克隆的git checkout otherbranch操作。
  3. Git会确保我们不会破坏修改后的文件。根据部署目录以及在那里完成的工作,这可能不是问题。
  4. Git喜欢优化结帐。
  5. 第1项很容易通过首先执行git clone来解决。

    如果我们只部署mkdir -p,或者我们不介意新克隆可能会自动检出分支master,则第2项不是问题。但我们可以使用不同形式的otherbranch来修复它。我们也可以添加git checkout来修复第3项。

    -f

    这都引入了一个新问题,与第4项一致。

    第4项是最棘手的。当您在结尾处没有结束git --work-tree=/deploy/$branch checkout -f $branch -- . 结账时,以便您切换分支,git会更新其索引(暂存区域)文件以跟踪什么&#39;已经在工作树中。然后,当你执行一个替换前一个-- .的新git checkout时,它可以告诉哪些文件可以单独存在,哪些文件必须被删除(如果有的话),哪些文件必须被重写或添加。一些部署脚本(检查一个分支,如master)取决于此行为。

    如果我们将部署代码切换为使用-- .表单,git 仍会更新索引,但它不会为我们删除文件。这意味着我们需要清理掉掉的文件。 (它也最终制作了一个非常混乱的索引,但通常没关系:这是一个--bare存储库,无论如何都没有人工作。)

    清理正确的文件很棘手。我们可以使用$oldsha方法,比较旧提交和新提交以确定要删除的文件。或者我们可以完全吹掉/deploy/$branch目录:

    rm -rf /deploy/$branch
    mkdir /deploy/$branch
    git --work-tree=/deploy/$branch checkout -f $branch -- .
    

    这通常就足够了。它仍然有一个错误:在删除旧的部署版本的短暂时间内,当部署目录中出现问题时,正在构建新的部署版本。但有时候还行。

    我们可以通过切换顺序来解决这个问题:创建一个新的空目录,填充它,然后mv新目录到位(mv旧目录并且只有rm -rf旧的(移出的)旧的。还有一个小窗口,在此期间部署目录不存在,但它尽可能小。 (好吧,有可能用符号链接完全关闭它:请参阅下面的评论。)

    我们可以做的最后一招,我从未真正测试过:我们可以通过为每个部署目录使用不同的索引文件来完成大部分工作。 Git使用$GIT_INDEX_FILE(如果已设置)或$GIT_DIR/index(如果没有)。因此,如果我们要设置GIT_INDEX_FILE=$GIT_DIR/index.$branch,我们应该获得特定分支名称唯一的文件。然后我们可以回到&#34;检查一个特定的分支&#34; git checkout的形式,如果需要,让它删除文件。

    这最后一种方法打开了不一致性窗口:如果git必须删除十几个文件并更新或创建另外100个文件,那么使用已部署版本的任何内容都有更多机会看到部分删除和/或更新为git checkout进展。但它更简单,并且减少了浪费的动作&#34;在工作树中。

    最后一个具有讽刺意味的冥想,因为它是

    请注意,我们的deploy函数设置了一个工作树 - 或者可能是其中一个,如果我们有多个可部署的分支 - 作为文字git工作树,甚至可能使用$GIT_WORK_TREE。然后它会破坏工作树中的任何内容,将其替换为有趣分支中的最新版本。这正是我们所说的令人烦恼的,回到&#34;背景&#34;部分。它根本不讨厌,它只是我们想要的!

    好吧,有点儿。关于这个工作树,与裸存储库相比,好处是你不能不小心cd /deploy/master,看到.git目录,然后开始使用它。这里没有.git目录。尽管如此,&#34;工作目录的组合在这里&#34;加上#34;裸仓库&#34;几乎在所有意义上,它都是等于&#34;常规的,非裸的存储库&#34;。