使用Git push部署项目

是否可以使用git push部署一个网站?我有一种预感,它与使用git钩子在服务器端执行git reset --hard有关,但我该如何实现这一点呢?

125177 次浏览

我的方法是在我的部署服务器上有一个裸露的Git存储库,我在那里推送更改。然后我登录到部署服务器,更改到实际的web服务器文档目录,并执行git拉取。我没有使用任何钩子来自动地做这个,这似乎比它的价值更麻烦。

听起来你应该在你的服务器上有两个副本。一个裸拷贝,你可以推/拉,当你完成时,你会推你的改变,然后你会把这个克隆到你的web目录,并设置一个cronjob来每天从你的web目录更新git pull。

你可以设置一个git钩子,当一个“稳定”分支提交时,它会拉出更改并将其应用到PHP站点。最大的缺点是,如果出现问题,你将没有太多的控制,这将增加你的测试时间-但你可以了解当你将主干分支合并到稳定分支时将涉及多少工作,以了解你五月遇到多少冲突。重要的是要留意任何特定于站点的文件。配置文件),除非您只想运行一个站点。

或者,你有没有考虑过把改变推到网站上?

有关git钩子的信息,请参阅githooks文档。

我在这个网站上找到了这个脚本,它似乎工作得很好。

  1. 复制你的。git目录到你的web服务器
  2. 在你的本地副本上,修改你的。git/config文件,并添加你的web服务器作为远程服务器:

    [remote "production"]
    url = username@webserver:/path/to/htdocs/.git
    
  3. On the server, replace .git/hooks/post-update with this file (in the answer below)

  4. Add execute access to the file (again, on the server):

    chmod +x .git/hooks/post-update
    
  5. Now, just locally push to your web server and it should automatically update the working copy:

    git push production
    

从本质上讲,你所需要做的就是:

server = $1
branch = $2
git push $server $branch
ssh <username>@$server "cd /path/to/www; git pull"

在我的应用程序中,我将这些行作为名为deploy的可执行文件。

所以当我想要进行部署时,我键入./deploy myserver mybranch

使用下面的后更新文件:

  1. 复制你的。git目录到你的web服务器
  2. 在你的本地副本上,修改你的。git/config文件,并添加你的web服务器作为远程服务器:

    [remote "production"]
    url = username@webserver:/path/to/htdocs/.git
    
  3. On the server, replace .git/hooks/post-update with file below

  4. Add execute access to the file (again, on the server):

    chmod +x .git/hooks/post-update
    
  5. Now, just locally push to your web server and it should automatically update the working copy:

    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 远程”来部署网站代码。

作者的更新后脚本只有一行长,他的解决方案不需要.htaccess配置来隐藏Git回购。

< / p >

如果您在Amazon EC2实例上部署它,则会遇到几个绊脚石;

1)如果使用sudo创建裸目标存储库,必须将repo的所有者更改为ec2-user,否则推送将失败。(尝试“chown ec2-user:ec2-user 回购”。)

2)如果你没有预先配置你的amazon-private-key的位置,推送将会失败。可以在/etc/ssh/ssh_config中作为IdentityFile参数,也可以在~/。ssh/config使用“[Host] - HostName - IdentityFile - User”布局描述在这里

...但是,如果在~/中配置了Host。ssh/config和不同于HostName的Git推送将失败。(这可能是Git的bug)

不要在服务器上安装git或复制。git文件夹。要从git克隆版本更新服务器,您可以使用以下命令:

git ls-files -z | rsync --files-from - --copy-links -av0 . user@server.com:/var/www/project

您可能必须删除从项目中删除的文件。

这将复制所有签入文件。Rsync使用的是安装在服务器上的SSH。

你在服务器上安装的软件越少,他就越安全,管理它的配置和记录它就越容易。也不需要在服务器上保留一个完整的git克隆。这只会使正确地保护所有内容变得更加复杂。

对于有多个开发人员访问同一个存储库的环境,下面的指导方针可能会有所帮助。

确保你有一个所有开发人员都属于的unix组,并将.git存储库的所有权授予该组。

  1. 在服务器存储库的.git/config中设置sharerepository = true。(这告诉git允许提交和部署所需的多个用户。

  2. 将每个用户的bashrc文件中的umask设置为相同的- 002是一个很好的开始

Giddyup是语言不可知的只加水 git钩子,通过git push自动部署。它还允许你有自定义启动/停止钩子重新启动web服务器,加热缓存等。

https://github.com/mpalmer/giddyup

查看例子

更新:我现在使用劳埃德·摩尔解决方案与关键代理ssh -A ...。推入一个主回购,然后从所有机器上并行地从主回购中提取,这稍微快一些,在这些机器上需要的设置也更少。


这里没有解。如果服务器上安装了git,只需通过SSH推送即可。

在本地的.git/配置中需要以下条目

[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'

(顺便说一句:/ /项目/路径。Git与实际的工作目录/路径/到/项目不同)

我最终创建了自己的基本部署工具,它会自动从repo - https://github.com/jesalg/SlimJim -下拉新的更新,基本上它会监听github的接收后钩子,并使用代理来触发更新脚本。

我们使用capistrano来管理部署。 我们构建capistrano部署在一个登台服务器上,然后在我们所有的服务器上运行rsync
cap deploy
cap deploy:start_rsync (when the staging is ok)

使用capistrano,我们可以在出现bug时轻松回滚

cap deploy:rollback
cap deploy:start_rsync

我对基督徒解决方案的看法。

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
  • 存档主分支到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 config --local receive.denyCurrentBranch updateInstead

在Git 2.3中添加,这可能是一个很好的可能性:https://github.com/git/git/blob/v2.3.0/Documentation/config.txt#L2155

您在服务器存储库上设置它,如果工作树是干净的,它也会更新工作树。

在2.4中对push-to-checkout钩子和未出生分支的处理进行了进一步的改进。

示例用法:

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-ftp,它工作得很好。

https://github.com/git-ftp/git-ftp

使用方便,只需打字:

git ftp push

git会自动上传项目文件。

问候

部署场景

在我们的场景中,我们将代码存储在github/bitbucket上,并希望部署到活动服务器上。 在这种情况下,下列组合适用于我们(这是一个被高度好评的答案的混合):

  1. 复制你的.git目录到你的web服务器
  2. 在你的本地拷贝git remote add live ssh://user@host:port/folder
  3. 在远程:git config receive.denyCurrentBranch ignore
  4. 在远程:nano .git/hooks/post-receive和添加以下内容:

    < p > <代码> # !/bin/sh GIT_WORK_TREE=/var/www/vhosts/example.org git checkout -f

  5. . GIT_WORK_TREE=/var/www/vhosts/example.org
  6. 在远程:chmod +x .git/hooks/post-receive

  7. 现在你可以用git push live来推它

笔记

  • 此解决方案适用于较旧的git版本(使用1.7和1.9进行测试)
  • 你需要确保先推送到github/bitbucket,这样你就会有一个一致的实时回购
  • 如果你的.git文件夹在文档根目录中,请确保你通过添加.htaccess ()来隐藏它:

    RedirectMatch 404 /\..*$ < / p >

对于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

这两种解决方案都基于本文中提供的早期解决方案。

< p >注意, BRANCH_REGEX = ' ^ $ {GIT_BRANCH1}。< em >美元” 过滤匹配"master"或"dev*"字符串的分支名称,如果推送的分支匹配,则部署工作树。 这使得将开发版本和主版本部署到不同的位置成为可能

DEPLOY SOLUTION 1仅删除文件,这些文件是repo的一部分,并由提交删除。比部署方案2快。

DEPLOY SOLUTION 2的优势在于,它将从生产目录中删除添加到服务器端的任何新文件,无论这些文件是否添加到repo。它将永远是回购的干净的欺骗。比部署方案1慢。