和 @Leniy 说到利用 git 自动部署的事情, @Leniy 问了句, 怎么配, 既然有人有兴趣, 我就扯几句.

git 无疑是一个很赞的版本管理系统, 简单易用不伤手. 而如果在 VPS 上部署一个 git, 每次提交后, 直接部署到环境中, 不用 ssh 登进去顶着 XXXms 的延迟穷倒腾. 说明一下, 我只是举个栗子, 实现了"自动部署"的要求, 看官可以做的远不止这些.

首先, 环境要求:

  • 要求客户端和服务端都有个 git 环境.
  • 要求能 push 东西到服务端
  • 要求能修改服务端的环境--ftp环境和ssh环境都可以接受其实, 不过安全起见, 最好让 git 的目录是 web 根目录的父目录

其次, 原理:

git 的自动部署利用的是 hooks (中文版), 还可以参考这个.

简单说就是在服务端的 hooks 的文件夹中, 有若干文件, 在你执行各种操作的时候, 会触发特定的bash, 然后git会执行这些bash.

在 GIT_DIR/hooks/ 中有各种 xxx.sample 文件, 看官若有兴趣, 可以研究下人家怎么写的.

因为是测试, 我都在本地写, 但其实一个是客户端, 一个是服务端, 两边都有操作. 看官需要稍稍分辨下是在哪端.

好, 废话不说, 介绍个完整的耍耍.

1. 初始化服务端 git

我在服务端 $HOME/ 目录下建立 repo 文件夹, 初始化一个 test.git

[yu@argcv-com ~]$ cd ~
[yu@argcv-com ~]$ mkdir repo
[yu@argcv-com ~]$ cd repo/
[yu@argcv-com repo]$ git init --bare test.git
Initialized empty Git repository in /home/yu/repo/test.git/
[yu@argcv-com repo]$ 

2. 初始化客户端

我在/tmp/client下建立个客户端

[yu@argcv-com ~]$ cd /tmp/
[yu@argcv-com tmp]$ mkdir client
[yu@argcv-com tmp]$ cd client/
[yu@argcv-com client]$ git init
Initialized empty Git repository in /tmp/client/.git/
[yu@argcv-com client]$ git remote add origin yu@localhost:repo/test.git
[yu@argcv-com client]$ touch README.md
[yu@argcv-com client]$ git add .
[yu@argcv-com client]$ git commit -m "init"
[master (root-commit) 00da9a9] init
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 README.md
[yu@argcv-com client]$ git push origin --all 
Counting objects: 3, done.
Writing objects: 100% (3/3), 210 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To yu@localhost:repo/test.git
 * [new branch]      master -> master
[yu@argcv-com client]$ 

3. 初始化服务端待部署的环境

服务端部署在 /tmp/deploy/ 下

[yu@argcv-com ~]$ cd /tmp/
[yu@argcv-com tmp]$ mkdir deploy
[yu@argcv-com tmp]$ cd deploy/
[yu@argcv-com deploy]$ git clone $HOME/repo/test.git .
Cloning into '.'...
done.

特别提醒下, 此时在/tmp/deploy下有隐藏文件夹.git

安全起见, 不要让你的web的/目录和它在同一层, 而应该让它成为/tmp/deploy的一个子目录

4. 服务端添加 hooks

刚才说过, 服务端的git在 $HOME/repo/test.git/ 文件夹下

添加文件 $HOME/repo/test.git/hooks/post-receive, 做出部署

注意, hooks 下有好几个触发器, 分别对应在不同阶段的时候采取的动作.

比如 update, 对应的其实是在更新前的动作, 这个 bash 可以用来做 backup, 而如果用它来写自动部署, 那么它每次部署都会慢一拍... 这会很囧的

[yu@argcv-com repo]$ pwd
/home/yu/repo
[yu@argcv-com repo]$ cd test.git/
[yu@argcv-com test.git]$ cd hooks/
[yu@argcv-com hooks]$ cat > post-receive <<'EOF'
#!/bin/sh
unset GIT_DIR
NowPath=`pwd`
echo "now path is :"$NowPath
DeployPath="/tmp/deploy"
echo "deploy path is :"$DeployPath
cd $DeployPath
echo "cd deploy path"
git add . -A && git stash # remove local changes
git pull origin master # pull data from master
# the follow line is also ok:
# git add . && git fetch origin && git reset --hard origin/master
echo "deploy done"
cd $NowPath
echo "fine"
# --- Finished
exit 0
EOF
[yu@argcv-com hooks]$ chmod +x post-receive

hook 其实就是个 bash, 不言自明, 到待部署的目录, 执行 git pull或者 fetch && reset , 完成部署. 之所以要"git add . -A"是因为你可能写一些 untracked 的文件, 而这些文件若没有被 add, 那么版本控制可能无法对它进行修改.

当然, 你也可以执行 backup 之类的动作.

注意和普通 bash 一样, 加上可执行.

和 Makefile 一样, 在每一行, 目录都是固定的, 需要解除环境变量 GIT_DIR 环境变量才能自由移动当前位置.

当然, 其实我也可以用

(cd $DeployPath && git add . -A && git stash && git pull origin master )

来替换, 如果比较简单的话.

关于数据文件.

stash, reset 等操作会清理掉所有修改.

一般认为, 我们不应该把数据文件和代码文件放在一起. 在工程设计上, 我们本来就应该把数据文件放在别的地方. 因此这个逻辑并没有太多大问题. 但实际操作中, 可能会出于兼容啊, 第三方上游代码啊, 拍脑袋啊, 配置原因啊等, 我们必须要在工程下放点啥. 那么对我们而言, 需要考虑在工程里配置下 gitignore 文件, 那么在它白名单下的文件将会被无视.

5. 测试

首先, 客户端添加文件, 加入commit, 并push

[yu@argcv-com client]$ touch newfile
[yu@argcv-com client]$ git add .
[yu@argcv-com client]$ git commit -m "add new file"
[master 68efdcf] add new file
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 newfile
[yu@argcv-com client]$ git push origin
Counting objects: 4, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 384 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: now path is :/home/yu/repo/test.git
remote: deploy path is :/tmp/deploy
remote: cd deploy path
remote: From /home/yu/repo/test
remote:    6627165..5ac80ec  master     -> origin/master
remote: Updating 6627165..5ac80ec
remote: Fast-forward
remote:  newfile | 0
remote:  1 file changed, 0 insertions(+), 0 deletions(-)
remote:  create mode 100644 newfile
remote: deploy done
remote: fine
To yu@localhost:repo/test.git
   b3a32f2..5ac80ec  master -> master
[yu@argcv-com client]$ 

我们可以看到remote端提示

再查看服务端deploy的文件

[yu@argcv-com deploy]$ ls -al
total 0
drwxrwxr-x  3 yu   yu   100 Apr 26 17:29 .
drwxrwxrwt 22 root root 460 Apr 26 17:29 ..
drwxrwxr-x  8 yu   yu   280 Apr 26 17:29 .git
-rw-rw-r--  1 yu   yu     0 Apr 26 17:29 newfile
-rw-rw-r--  1 yu   yu     0 Apr 26 17:13 README.md

基本原理就是这样了.

6. 相关介绍

用户权限问题

在实际使用的时候, 有人反馈说会有 Permission Denied 之类的事情.

这种事情大多发生在你 Web Service 使用的用户和 git push 的用户不一致的情况下. 一般解决思路是这样的.

首先把 git 所在用户加入 web 用户组, 比如命令

sudo usermod -a -G web git

若你把目标目录放在了 /var/www/your_git 下, 那么你要检查下 /var, /var/www, /var/www, /var/www/your_git 这几个目录的权限是否开了足够权限.

一般情况下, 所有父目录对 git 用户至少要有读权限, 而 git 目录需要写权限. 对我而言, /var 是公共空间, 一般都能看到就不详述, /var/www/ 下面可能会管理若干工程, 那么一个可以考虑的方法是把它的 owner 设置为 root 用户, web 用户组. 然后我们可以通过控制同组权限的方法来给 /var/www 目录加上合适的限制. 比如在此处, 我会建议你给 /var/www 设置权限为 750 (sudo chmod 750 /var/www). FAQ: How do directory permissions in Linux work?, 然后给 /var/www/your_git 为 770 即可.

然后还要考虑是否有 SELinux 在挡道, 一般修改权限后 sudo restorecon -R /var/www 下就可以了.

你也可以尝试以 git 的身份查看下 web 目录, 命令是

sudo -u git > /var/www/your_git/test_file

这个命令会尝试以 git 的身份在 /var/www/your_git/ 的目录下写个名为 test_file 的空文件.

控制更新频率

一个比较好的实践是开多个 branch. 比如平时是 push 到远程的 master branch, 那么你可以开个名为 publish 的 branch. 前面的脚本改成使用 "origin/publish".

那么你执行

git push origin master

远程不会有任何更新. 而你执行

git push origin master:publish

那么远程的 publish 这时候会更新, 然后 hook 会把 publish 的最新结果放到运行环境.

webhooks and CI tools

github, gitlab 托管的工程. 服务器自己内置了各种 hooks, 对用户而言, 出于安全考虑, 我们很难接触底层的脚本. 一般我们被推荐使用 webhooks. 我们需要在自己服务器上提供一个对外部暴露的 web 接口. 配置一下工程后, 每当有特定的 event 来临, 我们会收到一个 push event, 包含了许多相关信息, 我们可以借此执行后续操作.

此外, 我们也可以善用 travis ci 等工具, 它们其实也是基于 webhooks 的更加高层的应用. 即便是公开工程, 也允许我们设置一些 private environment variables. 然后每当部署成功后, 可以执行一些脚本.

若我们既想托管到 github, 又不想浪费之前写过的部署脚本, 我们还可以在 travis 的配置文件中写一下 deploy 脚本, 把最新的代码再 push 到我们文件.

deploy:  # 只有前面测试脚本通过后才执行它
  - provider: script
    keep-history: true
    skip-cleanup: false
    script: echo $ID_RSA > $HOME/id_rsa && chmod 0600 $HOME/id_rsa && GIT_SSH_COMMAND="ssh -i $HOME/id_rsa" git push git@myserver:proj master --force
    on:
      branch: master # 只有 master 文件才会执行 push

比如可以用类似上面的脚本. 我们在 travis 的 global environment variable 里面写一下 ID_RSA 的内容为你的私钥, 然后直接 push 到自己的服务器即可. 然后以后你只需要往 github push 代码就好

gitlab 等也有内置 ci, 此外, 我们还可以自己搭建 ci 服务. 比如 drone. 但无论如何, 基本逻辑就是一条, 当我们 push 一下代码, 下面就交给各种 "当 foo 的时候就 bar" 的 hooks 吧, 别自找麻烦了.

7. 总结

这篇的内容本意是简单粗暴地介绍一点解耦方面的考虑. 我们如何监控一个事件的发生? 除了没日没夜发心跳包去骚扰对方的状态管理工具外, 我们还可以选择另一种方法, 让事件发生的同时, 让事件来告诉我们.

这其实是一个很常见的做法. 在程序中有 callback 在库中有 libevent. 因为当需要面对大量监听的时候, 轮询是没有未来的 :(. 而本文提到的 hooks 其实就是 git 提供的一个通用的工具, 让我们在更高抽象层上也能利用这类模型. 希望可以让各位能更高效地摆脱重复劳动.

Categories: Code

Yu

Ideals are like the stars: we never reach them, but like the mariners of the sea, we chart our course by them.

44 Comments

Leniy · July 12, 2017 at 18:14

Google Chrome 45.0.2454.101 Google Chrome 45.0.2454.101 Windows 10 x64 Edition Windows 10 x64 Edition

文章又更新了啊,收到邮件推送了

    yu · July 13, 2017 at 08:32

    Google Chrome 59.0.3071.115 Google Chrome 59.0.3071.115 Mac OS X  10.12.5 Mac OS X 10.12.5

    @Leniy 没有啊?? 我程序 bug 了吧囧

    yu · July 13, 2017 at 09:26

    Google Chrome 59.0.3071.115 Google Chrome 59.0.3071.115 Mac OS X  10.12.5 Mac OS X 10.12.5

    @Leniy 更加奇怪的是, 今天之前我的 email 是坏的, 我配置的 smtp 的 ssl 有问题, 理论上是完全发不出邮件的囧

      Leniy · July 14, 2017 at 16:53

      Google Chrome 45.0.2454.101 Google Chrome 45.0.2454.101 Windows 10 x64 Edition Windows 10 x64 Edition

      @yu 知道原因了,有人在github引用了你的文章,然后自动@我了

        yu · July 15, 2017 at 00:20

        Google Chrome 59.0.3071.115 Google Chrome 59.0.3071.115 Mac OS X  10.12.5 Mac OS X 10.12.5

        @Leniy 挖, 加 cite 啊, 多谢

bsc · December 4, 2016 at 22:10

Google Chrome 54.0.2840.98 Google Chrome 54.0.2840.98 Mac OS X  10.12.1 Mac OS X 10.12.1

大神

    bsc · December 4, 2016 at 23:09

    Google Chrome 54.0.2840.98 Google Chrome 54.0.2840.98 Mac OS X  10.12.1 Mac OS X 10.12.1

    不知道怎么搞的 我本地文件755 push之后变成700

      yu · December 5, 2016 at 14:06

      Google Chrome 54.0.2840.98 Google Chrome 54.0.2840.98 Mac OS X  10.12.1 Mac OS X 10.12.1

      @bsc

      参考这一篇: http://git.661346.n2.nabble.com/file-mode-tt6467904.html#a6469081

      This is by design. While the git data structure can technically store
      unix mode bits in its trees, it was found early on in git’s history that
      respecting anything beyond a simple executable bit ended up being more
      cumbersome for git’s normal use cases (i.e., people storing code or
      other shared files in a repository).

      We could add in a config option to respect file modes, but it has
      generally been seen as not worthwhile. It solves only a part of the
      general metadata problem, as it omits owner and group names or ids, as
      well as extended metadata like ACLs.

      If modes are important to you, the suggested fixes are one of:

      1. Use a tool like “metastore” that can be called from git hooks, and
      will save and restore file permissions in a file that is tracked in
      the repository. Do note that when using such a tool there is a race
      condition in protecting files (i.e., git will create your file as
      644, and then metastore will correct it to 600; in the meantime,
      somebody could read your file).

      2. Depending on exactly what you’re storing, it may make sense to keep
      your repository in another directory, protected by permissions, and
      then use a separate tool to deploy your files from the repository
      to their ultimate location (e.g., a Makefile or other install
      tool).

      一个简单的方法是在 post-receive 脚本中加上寻找目录改成 755 .. 比如

      find . -type d -exec chmod 755 {} +
      find . -type f -exec chmod 644 {} +
      restorecon -R .
      

      另外,因为我的 — 无论是 mac 还是 centos, 貌似都是默认 755, 尴尬表示我恰好相反, 好像不太好测试改配置的方法

      yu · December 5, 2016 at 14:12

      Google Chrome 54.0.2840.98 Google Chrome 54.0.2840.98 Mac OS X  10.12.1 Mac OS X 10.12.1

      @bsc
      google 了下,

      http://unix.stackexchange.com/questions/1314/how-to-set-default-file-permissions-for-all-folders-files-in-a-directory

      这个如何?
      你 apply 到部署位置?

        bsc · December 5, 2016 at 14:51

        Google Chrome 54.0.2840.98 Google Chrome 54.0.2840.98 Mac OS X  10.12.1 Mac OS X 10.12.1

        @yu 多谢

mydansun · October 1, 2016 at 11:31

Google Chrome 53.0.2785.124 Google Chrome 53.0.2785.124 Android 6.0.1 Android 6.0.1

谢谢博主,也就是说其实fetch + reset和checkout f的作用是一样的。而add. + stash是可以把修改留下来(虽然我们后面并没有用命令恢复他)

    yu · October 1, 2016 at 13:04

    Google Chrome 53.0.2785.116 Google Chrome 53.0.2785.116 Mac OS X  10.11.6 Mac OS X 10.11.6

    @mydansun checkout -f 和 stash 一样,是切换到本地的 HEAD 指针所在位置。即最近一次的 commit, 然后还需要执行 pull remote 的某个 branch( master for example) 才能更新. 若你在当前工作树下没有做过 commit 操作,那么 pull 和 reset 效果应该是一样的,但是若此处另外有修改并且打上了 commit, 那么 checkout -f 和 stash 一样,应该是到 commit 的另一个叶子节点,然后再 pull 的话,等于是 merge,而 fetch + reset 是完全放弃本地的其它 commit,完全使用线上的最新 commit, 不执行merge 操作

    yu · October 1, 2016 at 13:09

    Google Chrome 53.0.2785.116 Google Chrome 53.0.2785.116 Mac OS X  10.11.6 Mac OS X 10.11.6

    @mydansun in brief, checkout -f 和 stash 的区别是是否暂存 dirty data, 它们的作用是切换到它们自己工作树下的 HEAD 所在的 commit, 然后需要执行 pull or reset 操作才可以更新. pull 和 reset 都是 apply 最新内容,区别是 pull 等价于 fetch + merge, reset 是不merge直接使用 remote 指定位置的内容。之所以要 fetch 是因为 reset 没有 fetch操作,它以为的 “remote 最新内容”其实是存在本地的以前的缓存,因此需要更新下缓存先

      mydansun · October 3, 2016 at 22:26

      Google Chrome 53.0.2785.116 Google Chrome 53.0.2785.116 Windows 10 x64 Edition Windows 10 x64 Edition

      @yu 感谢博主这么详细的解答!

      mydansun · October 7, 2016 at 10:00

      Google Chrome 53.0.2785.116 Google Chrome 53.0.2785.116 Windows 10 x64 Edition Windows 10 x64 Edition

      @yu 博主你好,我参考了其他资料,下面脚本应该可以实现只当用户push指定branch的时候进行代码部署

      while read oldrev newrev ref
      do
            branch=`echo $ref | cut -d/ -f3`
            echo "--- Current branch is : "$branch
            if [ "master" == "$branch" ]; then
                  unset GIT_DIR
                  NowPath=`pwd`
                  echo "--- Now path is :"$NowPath
                  DeployPath="/home/nginx/www"
                  echo "--- Deploy path is :"$DeployPath
                  cd $DeployPath
                  echo "--- Go to deploy path"
                  git add . && git fetch origin && git reset --hard origin/master
                  echo "--- Deploy done"
                  cd $NowPath
                  echo "--- Fine"
            fi
      done
      exit 0
      

        yu · October 7, 2016 at 11:12

        Google Chrome 53.0.2785.143 Google Chrome 53.0.2785.143 Mac OS X  10.11.6 Mac OS X 10.11.6

        @mydansun 棒呆。要是我模仿 官方 example 的话, 我大概会用 $1, $2, $3 来取 revision id 吧

          mydansun · October 7, 2016 at 23:08

          Google Chrome 53.0.2785.116 Google Chrome 53.0.2785.116 Windows 10 x64 Edition Windows 10 x64 Edition

          @yu 我试了在恢复里直接加pre标签发现不管用,这个是bash的还是要标记一下hhh

            yu · October 7, 2016 at 23:09

            Google Chrome 53.0.2785.143 Google Chrome 53.0.2785.143 Mac OS X  10.11.6 Mac OS X 10.11.6

            @mydansun

            [code]
            your code here
            [ /code]
            

              mydansun · October 8, 2016 at 10:09

              Google Chrome 53.0.2785.116 Google Chrome 53.0.2785.116 Windows 10 x64 Edition Windows 10 x64 Edition

              @yu 那既然已经git reset –hard了,为什么还要git add -A呢?这还是不太明白

                yu · October 8, 2016 at 12:48

                Google Chrome 53.0.2785.143 Google Chrome 53.0.2785.143 Mac OS X  10.11.6 Mac OS X 10.11.6

                @mydansun

                $ git init
                Initialized empty Git repository in /private/tmp/tmp/.git/
                $ echo "first commit" >> A.txt
                $ git add .
                $ git commit -m "first commit"
                [master (root-commit) e4af30a] first commit
                 1 file changed, 1 insertion(+)
                 create mode 100644 A.txt
                $ echo "dirty data to A.txt" >> A.txt
                $ echo "dirty data to B.txt" >> B.txt
                $ git status
                On branch master
                Changes not staged for commit:
                  (use "git add <file>..." to update what will be committed)
                  (use "git checkout -- <file>..." to discard changes in working directory)
                
                	modified:   A.txt
                
                Untracked files:
                  (use "git add <file>..." to include in what will be committed)
                
                	B.txt
                
                no changes added to commit (use "git add" and/or "git commit -a")
                $ git reset --hard master
                HEAD is now at e4af30a first commit
                $ git status
                

                guess what?

                  mydansun · October 15, 2016 at 00:35

                  Google Chrome 53.0.2785.143 Google Chrome 53.0.2785.143 Windows 10 x64 Edition Windows 10 x64 Edition

                  @yu B文件消失?

                    yu · October 15, 2016 at 00:55

                    Google Chrome 53.0.2785.143 Google Chrome 53.0.2785.143 Mac OS X  10.12.0 Mac OS X 10.12.0

                    @mydansun you may have a try

mydansun · October 1, 2016 at 00:41

Google Chrome 53.0.2785.116 Google Chrome 53.0.2785.116 Windows 10 x64 Edition Windows 10 x64 Edition

博主能不能详细解释一下git add . -A && git stash的用处呢,如果说我部署的网站用户在使用过程中会上传一些图片进去,我这边自动部署之后这些新上传的图片还会存在么?

    yu · October 1, 2016 at 07:10

    Google Chrome 53.0.2785.116 Google Chrome 53.0.2785.116 Mac OS X  10.11.6 Mac OS X 10.11.6

    @mydansun git add . -A && git stash 的用途是清理掉工作目录下本地的所有修改。

    stash 是暂存当前变更,然后让你当前状态到上一次 commit 的状态,然后 pull 是最近一次 commit 和代码库中的 master 合并。

    也可以用

    git add . && git fetch origin && git reset --hard origin/master
    

    它是完全放弃当前 commit,将内容完全设置为代码库中最新更新。

    stash 其实还是把你的变更放到工作树下的,而 reset 是完全不存放。关于 stash 还可以参考这个链接 https://git-scm.com/book/zh/v1/Git-%E5%B7%A5%E5%85%B7-%E5%82%A8%E8%97%8F%EF%BC%88Stashing%EF%BC%89

    如果说我部署的网站用户在使用过程中会上传一些图片进去,我这边自动部署之后这些新上传的图片还会存在么?

    若:

    1. 你的网站用户生成的文件是在代码目录下面
    2. 你没有配置 gitignore

    那么,是的,会全部清除 (stash 其实还是可以滚回来的,但是可能不是你要的效果)

    那么,怎么处理这件事呢?

    有若干种解法,任意一种都是可以的:

    1. 代码和数据不要放在一起,这个是最理想的架构。你可以在架构上把它配置在其它目录,或者存到其它云服务/数据库中。当然这个改动有点大,你要是想随便凑活下,可能不打算这么做。
    2. 用 symbol link 的方法,具体是,假如你打算存在 your-root/user-data 目录下。那么你在其它地方创建 user-data 目录,然后 ln -sf /real/path/to/user-data /web-root/user-data 即可
    3. 配置 .gitignore, 忽略这个目录即可,可以参考这个链接: https://help.github.com/articles/ignoring-files/ 或者这个链接 https://git-scm.com/docs/gitignore , 其实很简单,就是在.git 那个目录下增加名为 .gitignore 的文本文件, 然后可以很方便的通过正则过滤掉一些文件

      mydansun · October 1, 2016 at 07:17

      Google Chrome 53.0.2785.124 Google Chrome 53.0.2785.124 Android 6.0.1 Android 6.0.1

      @yu 多谢博主这么快就回复了还有个问题,这个方案和网上其他的很多checkout -f有什么区别呢,希望能帮我解答一下。。谢谢啦

        yu · October 1, 2016 at 09:25

        Chrome 53.0.2785.109 Chrome 53.0.2785.109 iPad iOS 10.0.2 iPad iOS 10.0.2

        @mydansun checkout, stash, pull, merge, reset 这些都是基本操作, 为什么不试试好好读下给你的链接呢?

          mydansun · October 1, 2016 at 10:24

          Google Chrome 53.0.2785.124 Google Chrome 53.0.2785.124 Android 6.0.1 Android 6.0.1

          @yu 。我是手机就没有看别的页面了,还是谢谢博主之前的详细解答,之后的部分我自己回去多了解。

            yu · October 1, 2016 at 10:44

            Google Chrome 53.0.2785.116 Google Chrome 53.0.2785.116 Mac OS X  10.11.6 Mac OS X 10.11.6

            @mydansun 简单描述差错颇多,RTFM 才是真理。
            抱歉刚跑步回来…
            简单描述是, git 的工作树是一个由一个个 commit 组成的树。在你切换到那个工作树目录下的时候,那么一般情况,比如你的那个 git 目录中内容若有修改,那么当前状态就是你的 git HEAD 指针指向某个叶子节点,即你在 remote 目录下最后一次 commit 的状态,但是真实的目录状况和 HEAD 略有区别。 add. + stash 的作用是求当前状态和 HEAD 指针的 diff,然后把这个 diff 保存到暂存的 stack 中,然后把目录还原为 HEAD 的状态。这时候你可以继续 pull, 那么作用是取出远程的 repo 状况,然后merge。 checkout -f 的作用是直接切换为 HEAD 的状态(或者其它指定状态),当前的没有 commit 的内容不再保留。然后就 normalize 为 stash 后一样的效果,不过因为没有保存 diff,删除的 diff 不可以pop出来。fetch + reset 是放弃当前的所有变更,直接强行切换为远程的 master (或者指定 branch/sha1 commit) 某个状态。

            这些都取决于实际需求,而官方文档描述比我说得详细清晰多了。

denny · September 6, 2016 at 15:47

Chromium 51.0.2704.79 Chromium 51.0.2704.79 Ubuntu x64 Ubuntu x64

谢谢,非常有用,部署完成了=D

azhun · December 11, 2015 at 11:27

Google Chrome 48.0.2564.22 Google Chrome 48.0.2564.22 Mac OS X  10.11.1 Mac OS X 10.11.1

我有个问题想请教一下,post-receive这个文件应该放在哪个地方。现在有三个git,一个是我本地开发的本地仓库,我称为local,一个是web端的部署的本地仓库,我称为deploy,一个是git server,我称为server。您能否说明一下,post-receive这个文件是local还是deploy还是server上的?

    yu · December 11, 2015 at 12:13

    Google Chrome 47.0.2526.80 Google Chrome 47.0.2526.80 Mac OS X  10.11.1 Mac OS X 10.11.1

    @azhun server.

    server 收到 push 请求,然后做 post process.

    你在 local 发 push 请求会看到一些 remote: 开头的提示, 这些都是 server 的各种脚本发给 stdout 的

xhq · December 6, 2015 at 13:17

Google Chrome 46.0.2490.86 Google Chrome 46.0.2490.86 Windows 8 x64 Edition Windows 8 x64 Edition

这是我遇到问题并解决的过程,希望对大家有帮助 http://wp.iyouths.org/191.html ,多多交流

    yu · December 6, 2015 at 16:05

    Google Chrome 47.0.2526.73 Google Chrome 47.0.2526.73 Mac OS X  10.11.1 Mac OS X 10.11.1

    @xhq 权限问题, 参考 “6.相关介绍 -> 用户权限问题”

    首先, 一般情况下修改一个目录的所属一般是一件很不妥的事情. 个人还是建议修改目录权限, 加入同一个 group. 实现组内共享会比较好.

    其次, SELinux 经常会是一个问题. 但是出于安全考虑, 一般还是不建议关闭.

花舞花落泪 · May 6, 2013 at 09:19

Google Chrome 26.0.1410.64 Google Chrome 26.0.1410.64 Windows 7 Windows 7

奇思妙想啊….

    yu · May 6, 2013 at 10:17

    Google Chrome 26.0.1410.63 Google Chrome 26.0.1410.63 GNU/Linux x64 GNU/Linux x64

    额。。的确是奇思妙想,可惜不是我的思,这东西设计之初提供了这些hook就是为了给人这么折腾的

      花舞花落泪 · May 7, 2013 at 09:53

      Google Chrome 26.0.1410.64 Google Chrome 26.0.1410.64 Windows 8 x64 Edition Windows 8 x64 Edition

      接口提供了,怎么用就是用户的事了。

Leniy · April 27, 2013 at 07:51

Google Chrome 26.0.1410.64 Google Chrome 26.0.1410.64 Windows 7 Windows 7

[leniy@16:50:27 ~]$ git –version
git version 1.7.2.5

我的主机有git,但不是vps。可以部署么

    yu · April 27, 2013 at 08:13

    Google Chrome 26.0.1410.63 Google Chrome 26.0.1410.63 GNU/Linux x64 GNU/Linux x64

    看起来可以ssh?
    那么应该没问题。
    对服务端环境需要两个:
    1. 建立个可执行的bash
    2. 可以push上去
    其中,push上去可以通过https或者ssh的方式。
    git remote add < 随便什么名字> your_ssh_name@host
    然后各种上传下载操作应该是可以的。

      Leniy · April 27, 2013 at 08:15

      Google Chrome 26.0.1410.64 Google Chrome 26.0.1410.64 Windows 7 Windows 7

      好的,回家试试看。公司电脑只有win7,没法git

        yu · April 27, 2013 at 11:27

        Google Chrome 26.0.1410.63 Google Chrome 26.0.1410.63 GNU/Linux x64 GNU/Linux x64

        windows 也有,不过gui不怎么给力就是。
        shell版本的还是很好的

          Leniy · April 27, 2013 at 12:21

          Google Chrome 26.0.1410.64 Google Chrome 26.0.1410.64 Windows 7 Windows 7

          公司电脑上装着这个呢。不过很久不用竟然忘记了

            Leniy · April 27, 2013 at 12:30

            Google Chrome 26.0.1410.64 Google Chrome 26.0.1410.64 Windows 7 Windows 7

            不过这个是github专用的呀

              yu · April 27, 2013 at 14:11

              Google Chrome 26.0.1410.63 Google Chrome 26.0.1410.63 GNU/Linux x64 GNU/Linux x64

              它有shell可以自行决定位置的.
              git remote add xxx@host
              git push
              就可以了

                Leniy · April 27, 2013 at 14:57

                Google Chrome 26.0.1410.64 Google Chrome 26.0.1410.64 Windows 7 Windows 7

                果然还有个Git Shell快捷方式

Leave a Reply

Your email address will not be published. Required fields are marked *