使用Git push部署项目

时间:2008-11-10 21:04:51

标签: git deployment webserver githooks

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

19 个答案:

答案 0 :(得分:287)

我在this script找到了this site,它似乎运作良好。

  1. 将.git目录复制到您的网络服务器
  2. 在本地副本上,修改.git / config文件并将您的Web服务器添加为远程:

    [remote "production"]
        url = username@webserver:/path/to/htdocs/.git
    
  3. 在服务器上,将.git / hooks / post-update替换为this file(在下面的答案中)

  4. 添加对文件的执行访问权限(同样,在服务器上):

    chmod +x .git/hooks/post-update
    
  5. 现在,只需在本地推送到您的网络服务器,它就会自动更新工作副本:

    git push production
    

答案 1 :(得分:77)

使用以下更新后文件:

  1. 将.git目录复制到您的网络服务器
  2. 在本地副本上,修改.git / config文件并将您的Web服务器添加为远程:

    [remote "production"]
        url = username@webserver:/path/to/htdocs/.git
    
  3. 在服务器上,将.git / hooks / post-update替换为下面的文件

  4. 添加对文件的执行访问权限(同样,在服务器上):

    chmod +x .git/hooks/post-update
    
  5. 现在,只需在本地推送到您的网络服务器,它就会自动更新工作副本:

    git push production
    
  6. #!/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
    

答案 2 :(得分:59)

经过许多错误的启动和死胡同,我终于能够通过“{git push remote ”来部署网站代码了,感谢this article

作者的更新后脚本只有一行,他的解决方案不需要.htaccess配置来隐藏Git仓库,就像其他人一样。

如果您在Amazon EC2实例上部署此功能,可能会遇到一些绊脚石;

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

2)如果您没有在/ etc / ssh / ssh_config中预先配置 amazon-private-key .pem的位置作为IdentityFile参数,或者在〜/ .ssh / config使用“[Host] - HostName - IdentityFile - User”布局描述here ......

...但是如果在〜/ .ssh / config中配置Host并且与HostName不同,则Git push将失败。 (那可能是一个Git bug)

答案 3 :(得分:21)

不要在服务器上安装git或在那里复制.git文件夹。要从git clone更新服务器,您可以使用以下命令:

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

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

这会复制所有已签入的文件。 rsync使用安装在服务器上的ssh。

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

答案 4 :(得分:12)

基本上你需要做的就是:

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

我的应用程序中的这些行是一个名为deploy的可执行文件。

因此,当我想进行部署时,请输入./deploy myserver mybranch

答案 5 :(得分:9)

我这样做的方法是在部署服务器上有一个裸Git存储库,我推送更改。然后我登录到部署服务器,切换到实际的Web服务器docs目录,并执行git pull。我没有使用任何钩子来尝试自动执行此操作,这似乎比它的价值更麻烦。

答案 6 :(得分:8)

在Git 2.3中添加的

receive.denyCurrentBranch updateInstead是可能的。

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

使用push-to-checkout hook and handling of unborn branches进行了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

这确实存在on the GitHub announcement提到的以下缺点:

  • 您的服务器将包含一个.git目录,其中包含项目的完整历史记录。您可能希望确保无法向用户提供服务!
  • 在部署期间,用户可能会暂时遇到处于不一致状态的站点,旧版本的某些文件和新版本的其他文件,甚至是半写文件。如果这对您的项目来说是个问题,那么推送部署可能不适合您。
  • 如果您的项目需要&#34; build&#34;一步,然后你必须明确地设置它,也许是通过githooks。

但所有这些都超出了Git的范围,必须由外部代码处理。所以从这个意义上说,这与Git钩子一起,是最终的解决方案。

答案 7 :(得分:5)

更新:我现在正在使用密钥代理ssh -A ... Lloyd Moore 解决方案。推送到一个主仓库,然后从所有机器上并行地从中取出它会更快,并且需要在这些机器上进行更少的设置。


这里没有看到这个解决方案。如果服务器上安装了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'

(BTW:/ path/to/project.git与实际工作目录/ path / to / project不同)

答案 8 :(得分:4)

对于部署方案

在我们的场景中,我们将代码存储在github / bitbucket上,并希望部署到实时服务器。 在这种情况下,以下组合适用于我们(这是这里高度赞成的答案的混音)

  1. .git目录复制到您的网络服务器
  2. 在您的本地副本git remote add live ssh://user@host:port/folder
  3. 在遥控器上:git config receive.denyCurrentBranch ignore
  4. 在远程:nano .git/hooks/post-receive并添加此内容:

    #!/bin/sh GIT_WORK_TREE=/var/www/vhosts/example.org git checkout -f

  5. 在遥控器上:chmod +x .git/hooks/post-receive

  6. 现在您可以使用git push live
  7. 推送

    注释

    • 此解决方案适用于较旧的git版本(使用1.7和1.9测试)
    • 你需要先确保推送到github / bitbucket,这样你才能获得一致的回购
    • 如果您的.git文件夹位于文档根目录中,请确保通过添加到.htaccesssource)将其隐藏起来:

      RedirectMatch 404 /\..*$

答案 9 :(得分:4)

我们使用capistrano来管理部署。 我们构建capistrano以在临时服务器上部署,然后在我们所有的服务器上运行rsync。

cap deploy
cap deploy:start_rsync (when the staging is ok)

使用capistrano,我们可以在bug的情况下轻松回滚

cap deploy:rollback
cap deploy:start_rsync

答案 10 :(得分:2)

Giddyup是与语言无关的 just-add-water git hooks,可通过git push自动部署。它还允许您使用自定义启动/停止挂钩来重新启动Web服务器,预热缓存等。

https://github.com/mpalmer/giddyup

查看examples

答案 11 :(得分:1)

听起来你的服务器上应该有两份副本。一个可以推送/拉出的裸副本,你可以在完成后推送你的更改,然后你将它克隆到你的web目录并设置一个cronjob来每天更新你的web目录中的git pull或者如此。

答案 12 :(得分:1)

作为补充答案,我想提供另一种选择。我正在使用git-ftp,它工作正常。

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

易于使用,仅输入:

git ftp push

并且git会自动上传项目文件。

此致

答案 13 :(得分:1)

我使用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

答案 14 :(得分:1)

我对Christians解决方案的看法。

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存档提取到系统临时文件夹中的deploy dir。
  • rsync更改为服务器
  • 从临时文件夹中删除部署目录。

答案 15 :(得分:1)

你可以想象设置一个git钩子,当说提交说“稳定”分支时,它会提取更改并将它们应用到PHP站点。最大的缺点是如果出现问题你将无法控制太多并且会增加测试时间 - 但是当你合并说你的主干分支进入稳定分支时你可以知道将涉及多少工作您可能遇到多少冲突。重要的是要密切关注任何特定于站点的文件(例如配置文件),除非您只打算运行一个站点。

或者您是否考虑过将更改推送到网站?

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

答案 16 :(得分:0)

我最终创建了自己的基本部署工具,它会自动从存储库中删除新的更新 - https://github.com/jesalg/SlimJim - 基本上它会监听github post-receive-hook并使用代理来触发更新脚本。

答案 17 :(得分:0)

鉴于您有多个开发人员访问同一存储库的环境,以下指南可能有所帮助。

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

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

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

答案 18 :(得分:0)

我使用两种解决方案进行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 =&#39; ^ $ {GIT_BRANCH1} $&#39。 匹配&#34; master &#34;的分支名称的过滤器或&#34; dev *&#34;如果推送的分支匹配,则生成工作树。 这样就可以将开发版和主版本部署到不同的地方。

DEPLOY SOLUTION 1仅删除作为repo一部分的文件,并通过提交删除。它比部署解决方案2更快。

部署解决方案2的优点是,它将从生产目录中删除任何新文件,这些文件是在服务器端添加的,无论它是否已添加到存储库中。回购总是干净利落的。它比部署解决方案1慢。