如何通过git push将多个分支部署到不同的目录?

在制作中,我维护了两个站点 – 测试和发布。 每个都通过软链接指向不同的目录(例如)

beta_public_html -> /home/scott/myapp/trunk/public public_html -> /home/scott/myapp/branches/1.2.3/public 

我是一个长期的svn用户,转向git。 我习惯通过svn update部署并更改新分支上的软链接,事情非常简单。

现在我要转向git。 我仍然需要两个软链接(它是使用Passenger的Rails应用程序),但现在我希望它们指向两个不同的git分支(“beta”和“release”,比如说)。 我希望能够通过git push(或git pull)更新它们。

问题第1部分:我不确定最好的方法。

我开始这样做的方式是只部署到两个不同的遥控器,例如

 git push ssh://scott@x.com/home/scott/myapp-beta beta git push ssh://scott@x.com/home/scott/myapp-release release 

但这不起作用,因为默认情况下push不会更新工作树。

所以我进入远程目录并第一次运行git reset –hard,它会拉动工作树。 但我再次推动,我无法得到新的推动 – 它只是停留在最初的一个。

(顺便说一句,请注意我似乎无法推送到“myapp-beta.git” – 失败了,我必须推送到目录名。我担心这是问题的一部分,但我不知道我在这里做错了什么。)

所以,如果问题1的答案是我的方法没问题,问题第2部分:我实际上在做什么是错的? 如果我应该使用钩子,有人可以指向我吗?

(对问题1的回答是“运行这七个手动步骤”将不会是一个非常有用的答案,因为svn checkout + ln -s是两步。)

谢谢。 我想回到编写代码。

Git push的文章比一个类似的问题有一个有趣的讨论。

其解决方案和结论之一涉及:

  • 生产服务器上的裸存储库
  • 一个克隆的存储库,带有一个钩子,用于拉动已被推入裸机的存储器

所以在你的情况下,

  • 一个可以推送测试版和发布分支的裸仓库
  • 两个克隆回购’beta’和’释放’带钩从裸露的回购中拉出各自的分支。

简而言之:一步: git push 。 没有更多的管理链接(因为该目录不再代表Git中的分支,与SVN不同)


关于钩子部分,裸露回购中的后接收钩子可以满足您的需求

请参阅Git提示:通过post-receive挂钩自动更新工作树

 $ cd bare $ chmod +x .git/hooks/post-receive with a post-receive hook like #!/bin/sh cd ../../beta env -i git reset --hard cd ../../release env -i git reset --hard 

注意:

post-receive钩子开始时将GIT_DIR环境变量设置为repo/.git文件夹,因此无论你进入什么路径,它都会尝试在那里运行任何以下的git命令。
解决这个问题只是GIT_DIRGIT_DIR

‘env -i’就是这样做的 :它完全忽略了inheritance的环境,只使用提供的变量和值。

解决方案是推送到单个存储库,它将使用updatepost-receive 挂钩

创建两个签出有几个单独的可能性,可以在钩子(在服务器上)使用。 从最轻量级出发:

  • 如果你不需要这两个检出的目录(检出版本)实际上是git存储库,你可以简单地使用git-archive 导出两个快照 (两个分支)

      git archive --format=tar --prefix=public_html/ master | (cd /var/www/html && tar xf -) git archive --format=tar --prefix=beta_public_html/ devel | (cd /var/www/html && tar xf -) 

    其中’master’和’devel’是您想要检出的分支的名称。 请注意,严格说来不需要--format=tar ,因为tar格式是“git archive”的默认格式。 您可能还想删除旧内容(第一行中“ rm -rf public_html/"之前的“ tar xf - ”,使用“ && ”连接,第二行也是如此)。

    导出文件的替代方法是使用低级命令“ git read-tree ”(将树写入索引)和“ git checkout-index ”(将文件从索引复制到工作区)。

    在此解决方案中,您推入的存储库可以(并且应该)是裸的,即没有工作目录本身。

  • 另一种解决方案是将您推入的存储库具有两个工作目录 ,这些目录可以使用contrib/workdir git-new-workdir脚本创建。 每个工作区域都将是该存储库的适当分支的结帐。

    然后updatepost-receive挂钩将取消设置GIT_DIR ,转到那些工作区域( public_htmlbeta_public_html ),并在那里做“ git reset --hard ”。

    在这个解决方案中,“checkouts”会在其中包含一些与git相关的元信息(在隐藏的.git目录中)。

  • 另一种解决方案是拥有两个(附加的)从属存储库 。 然后推入主(主)存储库(通过挂钩)或者推入这两个从属存储库,其中它们的钩子将执行“ git reset --hard ”或等效,或者转到那两个从属存储库并执行“ git pull ”从那里的主人。

    这两个奴隶存储库是非裸的,可以[直接在] public_htmlbeta_public_html 。 在这个解决方案中,“checkouts”将是完整的git存储库本身。

    您可以通过让这些从属存储库具有“替换”(备用对象数据库)指向主存储库(即使用“ git clone --shared ... ”克隆)来改进此设置; 如果没有此对象,则从属数据库开始硬链接到master。 当然,只有当主服务器和从服务器位于同一文件系统上时,才有可能实现这一点。

    请注意,此解决方案允许主存​​储库位于与从属存储库不同的主机上。 (虽然我猜这是你不需要的灵活性)。

  • 最后,您可以代替当前的安装程序部署gitweb或其他一些git Web界面 (请参阅InterfacesFrontendsAndTools和Gitweb wiki页面以获取部分列表),以便您的用户可以在闲暇时浏览存储库的不同版本和不同分支。

    在gitweb中(我猜也在其他git web界面中)由于path_info URL支持,你可以在浏览器中查看文件,并正确地跟踪链接(如果它们是本地的),请参阅git.git的’html’分支中的git.html repo.or.cz上的存储库 。


默认情况下,PS“ git push ”不会更新远程存储库中的工作目录,因为如果有人在非裸存储库中工作,那么这种横向推送可能会非常意外并导致失去工作。

我使用这样的post-receive 钩子来发布我的网站,因为Git在进行推送时不会触及工作目录。 远程存储库是非裸存储库,即它具有工作目录。

 if [ -n $GIT_DIR ]; then # Current dir is "/.git/", but we need to run reset at "/". # Clearing GIT_DIR is needed, or reset will fail with "fatal: Not a git repository: '.'" unset GIT_DIR cd .. fi git reset --hard 

(顺便说一句,请注意我似乎无法推送到“myapp-beta.git” – 失败了,我必须推送到目录名。我担心这是问题的一部分,但我不知道我在这里做错了什么。)

在创建没有工作目录的裸Git存储库( git init --bare )时,将目录命名为“something.git”是一种约定。 拥有非裸存储库时,存储库实际上位于“.git”子目录中,因此完整路径为“something / .git”。 似乎在任何一种情况下你都可以省略“.git”部分,Git会自动检测它。

我并不是真的反对其他解决方案,但我认为这样做有一种不那么强硬的“Git方式”。 这就是我要做的事情:

  1. 在我的服务器上,我建立了一种集中式存储库(由Gitosis或其他类似的东西管理)。
  2. 从客户端,我不断从存储库中取出,进行更改并推回。 分支是自然的,自动管理。
  3. 我将Gitosis所需的分支从服务器的public_html / beta_public_html中拉出来。 我会定期使用Cron作业与Gitosis保持同步。 如果您不喜欢Cron工作的想法,您可以随时使用某种钩子+脚本,就像其他人指出的那样。