是否可以使用 部署网站git push
?我有一种预感,它与使用git hooks在服务器端执行 agit reset --hard
有关,但我将如何实现这一点?
19 回答
- 将您的 .git 目录复制到您的 Web 服务器
在您的本地副本上,修改您的 .git/config 文件并将您的 Web 服务器添加为远程:
[remote "production"] url = username@webserver:/path/to/htdocs/.git
在服务器上,用这个文件替换 .git/hooks/post-update (在下面的答案中)
添加对文件的执行访问权限(同样,在服务器上):
chmod +x .git/hooks/post-update
现在,只需在本地推送到您的 Web 服务器,它就会自动更新工作副本:
git push production
使用下面的更新后文件:
- 将您的 .git 目录复制到您的 Web 服务器
在您的本地副本上,修改您的 .git/config 文件并将您的 Web 服务器添加为远程:
[remote "production"] url = username@webserver:/path/to/htdocs/.git
在服务器上,将 .git/hooks/post-update 替换为以下文件
添加对文件的执行访问权限(同样,在服务器上):
chmod +x .git/hooks/post-update
现在,只需在本地推送到您的 Web 服务器,它就会自动更新工作副本:
git push production
#!/bin/sh
#
# This hook does two things:
#
# 1. update the "info" files that allow the list of references to be
# queries over dumb transports such as http
#
# 2. if this repository looks like it is a non-bare repository, and
# the checked-out branch is pushed to, then update the working copy.
# This makes "push" function somewhat similarly to darcs and bzr.
#
# To enable this hook, make this file executable by "chmod +x post-update".
git-update-server-info
is_bare=$(git-config --get --bool core.bare)
if [ -z "$is_bare" ]
then
# for compatibility's sake, guess
git_dir_full=$(cd $GIT_DIR; pwd)
case $git_dir_full in */.git) is_bare=false;; *) is_bare=true;; esac
fi
update_wc() {
ref=$1
echo "Push to checked out branch $ref" >&2
if [ ! -f $GIT_DIR/logs/HEAD ]
then
echo "E:push to non-bare repository requires a HEAD reflog" >&2
exit 1
fi
if (cd $GIT_WORK_TREE; git-diff-files -q --exit-code >/dev/null)
then
wc_dirty=0
else
echo "W:unstaged changes found in working copy" >&2
wc_dirty=1
desc="working copy"
fi
if git diff-index --cached HEAD@{1} >/dev/null
then
index_dirty=0
else
echo "W:uncommitted, staged changes found" >&2
index_dirty=1
if [ -n "$desc" ]
then
desc="$desc and index"
else
desc="index"
fi
fi
if [ "$wc_dirty" -ne 0 -o "$index_dirty" -ne 0 ]
then
new=$(git rev-parse HEAD)
echo "W:stashing dirty $desc - see git-stash(1)" >&2
( trap 'echo trapped $$; git symbolic-ref HEAD "'"$ref"'"' 2 3 13 15 ERR EXIT
git-update-ref --no-deref HEAD HEAD@{1}
cd $GIT_WORK_TREE
git stash save "dirty $desc before update to $new";
git-symbolic-ref HEAD "$ref"
)
fi
# eye candy - show the WC updates :)
echo "Updating working copy" >&2
(cd $GIT_WORK_TREE
git-diff-index -R --name-status HEAD >&2
git-reset --hard HEAD)
}
if [ "$is_bare" = "false" ]
then
active_branch=`git-symbolic-ref HEAD`
export GIT_DIR=$(cd $GIT_DIR; pwd)
GIT_WORK_TREE=${GIT_WORK_TREE-..}
for ref
do
if [ "$ref" = "$active_branch" ]
then
update_wc $ref
fi
done
fi
经过许多错误的开始和死胡同,感谢这篇文章,我终于能够使用“git push remote ”部署网站代码。
作者的更新后脚本只有一行,他的解决方案不需要 .htaccess 配置来像其他人那样隐藏 Git 存储库。
如果您将其部署在 Amazon EC2 实例上,则会遇到一些障碍;
1) 如果使用 sudo 创建裸目标存储库,则必须将存储库的所有者更改为 ec2-user 否则推送将失败。(尝试“chown ec2-user:ec2-user repo。”)
2) 如果您没有在 /etc/ssh/ ssh_config作为 IdentityFile 参数或在 ~/.ssh/config 中使用“[ Host] - HostName - IdentityFile - User"此处描述的布局...
...但是,如果 Host 在 ~/.ssh/config 中配置并且与 HostName 不同,则 Git 推送将失败。(这可能是一个 Git 错误)
不要在服务器上安装 git 或复制 .git 文件夹。要从 git clone 更新服务器,您可以使用以下命令:
git ls-files -z | rsync --files-from - --copy-links -av0 . user@server.com:/var/www/project
您可能必须删除从项目中删除的文件。
这会复制所有签入的文件。rsync 使用无论如何都安装在服务器上的 ssh。
您在服务器上安装的软件越少,他就越安全,并且越容易管理其配置和记录它。也不需要在服务器上保留完整的 git clone。它只会使正确保护一切变得更加复杂。
本质上,您需要做的就是以下几点:
server = $1
branch = $2
git push $server $branch
ssh <username>@$server "cd /path/to/www; git pull"
我在我的应用程序中将这些行作为一个名为deploy
.
所以当我想进行部署时,我输入./deploy myserver mybranch
.
git config --local receive.denyCurrentBranch updateInstead
在 Git 2.3 中添加,这可能是一个很好的可能性:https ://github.com/git/git/blob/v2.3.0/Documentation/config.txt#L2155
您在服务器存储库上设置它,如果它是干净的,它还会更新工作树。
push-to-checkout
2.4对未出生分支的钩子和处理进行了进一步的改进。
示例用法:
git init server
cd server
touch a
git add .
git commit -m 0
git config --local receive.denyCurrentBranch updateInstead
cd ..
git clone server local
cd local
touch b
git add .
git commit -m 1
git push origin master:master
cd ../server
ls
输出:
a
b
这确实有以下GitHub公告中提到的缺点:
- 您的服务器将包含一个 .git 目录,其中包含您项目的整个历史记录。您可能需要额外确保它不能提供给用户!
- 在部署过程中,用户可能会瞬间遇到站点状态不一致的情况,有的文件在旧版本,有的在新版本,甚至是写了一半的文件。如果这对您的项目来说是个问题,那么推送部署可能不适合您。
- 如果您的项目需要“构建”步骤,那么您必须明确设置它,也许通过 githooks。
但所有这些都超出了 Git 的范围,必须由外部代码处理。所以从这个意义上说,这与 Git 挂钩是最终的解决方案。
我这样做的方式是在我的部署服务器上有一个裸 Git 存储库,用于推送更改。然后我登录到部署服务器,切换到实际的 Web 服务器 docs 目录,然后执行 git pull。我不使用任何钩子来尝试自动执行此操作,这似乎比它的价值更麻烦。
更新:我现在将Lloyd Moore解决方案与 key agent 一起使用ssh -A ...
。推送到主存储库,然后从所有机器并行提取它会更快一些,并且需要在这些机器上进行更少的设置。
在这里没有看到这个解决方案。如果服务器上安装了 git,只需通过 ssh 推送。
您需要在本地 .git/config 中添加以下条目
[remote "amazon"]
url = amazon:/path/to/project.git
fetch = +refs/heads/*:refs/remotes/amazon/*
但是,嘿,那是什么amazon:
?在您的本地 ~/.ssh/config 中,您需要添加以下条目:
Host amazon
Hostname <YOUR_IP>
User <USER>
IdentityFile ~/.ssh/amazon-private-key
现在你可以打电话了
git push amazon master
ssh <USER>@<YOUR_IP> 'cd /path/to/project && git pull'
(顺便说一句:/path/to/project.git 与实际工作目录/path/to/project 不同)
我们使用capistrano来管理部署。我们构建 capistrano 以部署在临时服务器上,然后与我们所有的服务器运行 rsync。
cap deploy
cap deploy:start_rsync (when the staging is ok)
使用 capistrano,我们可以在出现错误时轻松回滚
cap deploy:rollback
cap deploy:start_rsync
对于部署场景
在我们的场景中,我们将代码存储在 github/bitbucket 上,并希望部署到实时服务器。在这种情况下,以下组合对我们有用(这是此处高度赞成的答案的混合):
- 将您的目录复制
.git
到您的网络服务器 - 在您的本地副本上
git remote add live ssh://user@host:port/folder
- 在远程:
git config receive.denyCurrentBranch ignore
在远程:
nano .git/hooks/post-receive
并添加此内容:#!/bin/sh GIT_WORK_TREE=/var/www/vhosts/example.org git checkout -f
在远程:
chmod +x .git/hooks/post-receive
- 现在你可以用
git push live
笔记
- 此解决方案适用于较旧的 git 版本(使用 1.7 和 1.9 测试)
- 你需要确保先推送到 github/bitbucket,这样你就可以在 live 上有一个一致的 repo
如果您的
.git
文件夹位于文档根目录中,请确保通过添加.htaccess
(源)将其从外部隐藏:RedirectMatch 404 /\..*$
Giddyup是与语言无关的just-add-water git 挂钩,可通过 git push 自动部署。它还允许您使用自定义启动/停止挂钩来重新启动 Web 服务器、预热缓存等。
https://github.com/mpalmer/giddyup
查看示例。
听起来您的服务器上应该有两个副本。一个裸副本,您可以从中推送/拉取,完成后您将推送您的更改,然后您将其克隆到您的 Web 目录并设置一个 cronjob 以每天从您的 Web 目录更新 git pull 或所以。
您可以想象设置一个 git 钩子,当说提交说“稳定”分支时,它会提取更改并将它们应用到 PHP 站点。最大的缺点是,如果出现问题,您将无法进行太多控制,并且会增加测试时间 - 但是您可以了解当您将主干分支合并到稳定分支时将涉及多少工作以了解你可能会遇到多少冲突。密切关注任何特定于站点的文件(例如配置文件)非常重要,除非您只打算只运行一个站点。
或者,您是否考虑过将更改推送到网站?
有关 git 挂钩的信息,请参阅githooks文档。
我对基督徒解决方案的看法。
git archive --prefix=deploy/ master | tar -x -C $TMPDIR | rsync $TMPDIR/deploy/ --copy-links -av username@server.com:/home/user/my_app && rm -rf $TMPDIR/deploy
- 将 master 分支归档到 tar
- 将 tar 存档提取到系统临时文件夹中的部署目录中。
- rsync 更改为服务器
- 从临时文件夹中删除部署目录。
我正在使用toroid.org的以下解决方案,它有一个更简单的钩子脚本。
在服务器上:
$ mkdir website.git && cd website.git
$ git init --bare
Initialized empty Git repository in /home/ams/website.git/
并在服务器上安装钩子:
$ mkdir /var/www/www.example.org
$ cat > hooks/post-receive
#!/bin/sh
GIT_WORK_TREE=/var/www/www.example.org git checkout -f
GIT_WORK_TREE=/var/www/www git clean -f -d # clean directory from removed files
$ chmod +x hooks/post-receive
在您的客户上:
$ mkdir website && cd website
$ git init
Initialized empty Git repository in /home/ams/website/.git/
$ echo 'Hello, world!' > index.html
$ git add index.html
$ git commit -q -m "The humble beginnings of my web site."
$ git remote add web ssh://server.example.org/home/ams/website.git
$ git push web +master:refs/heads/master
然后发布,只需键入
$ git push web
网站上有完整的描述:http: //toroid.org/ams/git-website-howto
作为补充答案,我想提供一个替代方案。我正在使用 git-ftp,它工作正常。
https://github.com/git-ftp/git-ftp
易于使用,只需键入:
git ftp push
git会自动上传项目文件。
问候
鉴于您有多个开发人员访问同一存储库的环境,以下指南可能会有所帮助。
确保您有一个所有开发人员都属于的 unix 组,并将 .git 存储库的所有权授予该组。
在服务器存储库的 .git/config 中设置 sharedrepository = true。(这告诉 git 允许提交和部署所需的多个用户。
将每个用户的 bashrc 文件中的 umask 设置为相同 - 002 是一个好的开始
我最终创建了自己的基本部署工具,该工具会自动从 repo 中提取新更新 - https://github.com/jesalg/SlimJim - 基本上它会监听 github post-receive-hook 并使用代理来触发更新脚本。
我对 post-receive hook 使用了两种解决方案:
部署解决方案 1
#!/bin/bash
# /git-repo/hooks/post-receive - file content on server (chmod as 755 to be executed)
# DEPLOY SOLUTION 1
export GIT_DIR=/git/repo-bare.git
export GIT_BRANCH1=master
export GIT_TARGET1=/var/www/html
export GIT_BRANCH2=dev
export GIT_TARGET2=/var/www/dev
echo "GIT DIR: $GIT_DIR/"
echo "GIT TARGET1: $GIT_TARGET1/"
echo "GIT BRANCH1: $GIT_BRANCH1/"
echo "GIT TARGET2: $GIT_TARGET2/"
echo "GIT BRANCH2: $GIT_BRANCH2/"
echo ""
cd $GIT_DIR/
while read oldrev newrev refname
do
branch=$(git rev-parse --abbrev-ref $refname)
BRANCH_REGEX='^${GIT_BRANCH1}.*$'
if [[ $branch =~ $BRANCH_REGEX ]] ; then
export GIT_WORK_TREE=$GIT_TARGET1/.
echo "Checking out branch: $branch";
echo "Checking out to workdir: $GIT_WORK_TREE";
git checkout -f $branch
fi
BRANCH_REGEX='^${GIT_BRANCH2}.*$'
if [[ $branch =~ $BRANCH_REGEX ]] ; then
export GIT_WORK_TREE=$GIT_TARGET2/.
echo "Checking out branch: $branch";
echo "Checking out to workdir: $GIT_WORK_TREE";
git checkout -f $branch
fi
done
部署解决方案 2
#!/bin/bash
# /git-repo/hooks/post-receive - file content on server (chmod as 755 to be executed)
# DEPLOY SOLUTION 2
export GIT_DIR=/git/repo-bare.git
export GIT_BRANCH1=master
export GIT_TARGET1=/var/www/html
export GIT_BRANCH2=dev
export GIT_TARGET2=/var/www/dev
export GIT_TEMP_DIR1=/tmp/deploy1
export GIT_TEMP_DIR2=/tmp/deploy2
echo "GIT DIR: $GIT_DIR/"
echo "GIT TARGET1: $GIT_TARGET1/"
echo "GIT BRANCH1: $GIT_BRANCH1/"
echo "GIT TARGET2: $GIT_TARGET2/"
echo "GIT BRANCH2: $GIT_BRANCH2/"
echo "GIT TEMP DIR1: $GIT_TEMP_DIR1/"
echo "GIT TEMP DIR2: $GIT_TEMP_DIR2/"
echo ""
cd $GIT_DIR/
while read oldrev newrev refname
do
branch=$(git rev-parse --abbrev-ref $refname)
BRANCH_REGEX='^${GIT_BRANCH1}.*$'
if [[ $branch =~ $BRANCH_REGEX ]] ; then
export GIT_WORK_TREE=$GIT_TARGET1/.
echo "Checking out branch: $branch";
echo "Checking out to workdir: $GIT_WORK_TREE";
# DEPLOY SOLUTION 2:
cd $GIT_DIR/; mkdir -p $GIT_TEMP_DIR1;
export GIT_WORK_TREE=$GIT_TEMP_DIR1/.
git checkout -f $branch
export GIT_WORK_TREE=$GIT_TARGET1/.
rsync $GIT_TEMP_DIR1/. -v -q --delete --delete-after -av $GIT_TARGET1/.
rm -rf $GIT_TEMP_DIR1
fi
BRANCH_REGEX='^${GIT_BRANCH2}.*$'
if [[ $branch =~ $BRANCH_REGEX ]] ; then
export GIT_WORK_TREE=$GIT_TARGET2/.
echo "Checking out branch: $branch";
echo "Checking out to workdir: $GIT_WORK_TREE";
# DEPLOY SOLUTION 2:
cd $GIT_DIR/; mkdir -p $GIT_TEMP_DIR2;
export GIT_WORK_TREE=$GIT_TEMP_DIR2/.
git checkout -f $branch
export GIT_WORK_TREE=$GIT_TARGET2/.
rsync $GIT_TEMP_DIR2/. -v -q --delete --delete-after -av $GIT_TARGET2/.
rm -rf $GIT_TEMP_DIR2
fi
done
这两种解决方案都基于此线程中可用的早期解决方案。
注意,BRANCH_REGEX='^${GIT_BRANCH1}。$' 过滤与“master”或“dev*”字符串匹配的分支名称,如果推送的分支匹配,则部署工作树。这使得将开发版本和主版本部署到不同的地方成为可能。
DEPLOY SOLUTION 1 仅删除作为 repo 一部分的文件,并被提交删除。它比部署解决方案 2 更快。
DEPLOY SOLUTION 2 的优点是,它将从生产目录中删除任何新文件,这些文件是在服务器端添加的,无论它是否添加到 repo 中。它将永远是回购的干净骗局。它比部署解决方案 1 慢。