如何通过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_DIR
了GIT_DIR
。
‘env -i’就是这样做的 :它完全忽略了inheritance的环境,只使用提供的变量和值。
解决方案是推送到单个存储库,它将使用update
或post-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脚本创建。 每个工作区域都将是该存储库的适当分支的结帐。然后
update
或post-receive
挂钩将取消设置GIT_DIR
,转到那些工作区域(public_html
和beta_public_html
),并在那里做“git reset --hard
”。在这个解决方案中,“checkouts”会在其中包含一些与git相关的元信息(在隐藏的
.git
目录中)。 -
另一种解决方案是拥有两个(附加的)从属存储库 。 然后推入主(主)存储库(通过挂钩)或者推入这两个从属存储库,其中它们的钩子将执行“
git reset --hard
”或等效,或者转到那两个从属存储库并执行“git pull
”从那里的主人。这两个奴隶存储库是非裸的,可以[直接在]
public_html
和beta_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方式”。 这就是我要做的事情:
- 在我的服务器上,我建立了一种集中式存储库(由Gitosis或其他类似的东西管理)。
- 从客户端,我不断从存储库中取出,进行更改并推回。 分支是自然的,自动管理。
- 我将Gitosis所需的分支从服务器的public_html / beta_public_html中拉出来。 我会定期使用Cron作业与Gitosis保持同步。 如果您不喜欢Cron工作的想法,您可以随时使用某种钩子+脚本,就像其他人指出的那样。