├── LICENSE ├── ebooks ├── Git Community Book.pdf ├── Git Magic.pdf └── git-internals.pdf ├── gerrit-start.md ├── git-branch.md ├── git-commit.md ├── git-for-windows.md ├── git-primer.md ├── git-process.md ├── git-tools.md ├── images ├── file_lifecycle.png ├── gerrit-clone-with-commit-msg-hook.png ├── gerrit-http-password.png ├── gerrit-review.png ├── gerrit-sign-in.png ├── gerrit-ssh-public-keys.png ├── git_actions.jpg ├── git_areas.png ├── gitflow_feature_branches.svg ├── gitflow_hotfix_branches.svg ├── gitflow_main_branches.svg ├── gitflow_release_branches.svg ├── merge-without-ff@2x.png ├── why-git.png ├── workflow-gitflow.jpg └── workflow-github.png ├── pull-request.md ├── readme.md ├── slides ├── MyriadPro-Bold.otf ├── MyriadPro-Regular.otf ├── ci_jenkins_gerrit.graphml ├── gerrit │ ├── gerrit.html │ ├── gerrit.md │ ├── git-bash.html │ ├── git_bash.md │ ├── images │ │ ├── ci_overview.png │ │ ├── clone-with-commit-msg-hook.png │ │ ├── commit-and-push-to-gerrit.png │ │ ├── gerrit-add-sshkey.png │ │ ├── gerrit-clone-with-commit-msg-hook.png │ │ ├── gerrit-clone.png │ │ ├── gerrit-http-password.png │ │ ├── gerrit-review.png │ │ ├── gerrit-sign-in.png │ │ ├── gerrit-ssh-public-keys.png │ │ ├── git-bash-add-unix-tools-to-path.png │ │ ├── git-bash-checkout-and-commit-unix-style.png │ │ ├── git-bash-extra-options.png │ │ ├── git-bash-use-windows-console.png │ │ ├── git-jenkins-process.png │ │ ├── github-new-sshkey.png │ │ ├── intro-quick-central-gerrit.png │ │ ├── qr-code.png │ │ └── qr-git-bash.png │ ├── index.html │ ├── index.md │ └── theme │ │ ├── base.html │ │ ├── css │ │ ├── print.css │ │ └── screen.css │ │ └── js │ │ └── slides.js ├── gitflow-model.src.key └── workflow-gitflow.pdf ├── why-git.md ├── workflow-forking.md └── workflow-gitflow.md /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /ebooks/Git Community Book.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/ebooks/Git Community Book.pdf -------------------------------------------------------------------------------- /ebooks/Git Magic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/ebooks/Git Magic.pdf -------------------------------------------------------------------------------- /ebooks/git-internals.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/ebooks/git-internals.pdf -------------------------------------------------------------------------------- /gerrit-start.md: -------------------------------------------------------------------------------- 1 | # Gerrit 起步 2 | 3 | ## 1. 登录 Web 控制台 4 | [Gerrit Web Console](http://172.16.100.90/gerrit/) 已经集成了大家的jira账号,可以直接登录(Sign In) 5 | 6 | ![Gerrit Sign In](images/gerrit-sign-in.png) 7 | 8 | ## 2. 配置 SSH(HTTP) 访问认证 9 | 10 | 进入[Settings](http://172.16.100.90/gerrit/#/settings/)界面 11 | 12 | ### SSH Public Keys 13 | 14 | ![Gerrit SSH Public Keys](images/gerrit-ssh-public-keys.png) 15 | 16 | 点"Add Key ..."按钮,把本机ssh密钥对中的公钥填入对话框。 17 | 18 | ☞ 进一步阅读: [Use Public Key Authentication with SSH](https://www.linode.com/docs/security/use-public-key-authentication-with-ssh) 19 | 20 | 完成以上配置后,我们就可以与 Gerrit 进行对话了 21 | 22 | ➜ learn-git git:(master) ✗ ssh -p29418 liyan@172.16.100.90 23 | 24 | **** Welcome to Gerrit Code Review **** 25 | 26 | Hi liyan, you have successfully connected over SSH. 27 | 28 | Unfortunately, interactive shells are disabled. 29 | To clone a hosted Git repository, use: 30 | 31 | git clone ssh://liyan@172.16.100.90:29418/REPOSITORY_NAME.git 32 | 33 | Connection to 172.16.100.90 closed. 34 | 35 | ### HTTP Password 36 | 37 | ![Gerrit HTTP Password](images/gerrit-http-password.png) 38 | 39 | On Gerrit installations that do not support SSH authentication, the user must authenticate via HTTP/HTTPS. 40 | 41 | 参考[链接](https://gerrit-documentation.storage.googleapis.com/Documentation/2.13.3/user-upload.html#http) 42 | 43 | 设置之后,用户可以通过HTTP协议访问仓库 44 | 45 | git clone http://it.feiliu.com/gerrit/a/sandbox/hello_world 46 | 47 | ## 3. 执行一下 `gerrit` 命令 48 | 49 | 配置好 `SSH Public Keys` 之后,我们就可以使用 Gerrit 提供的服务了 50 | 51 | ssh -p29418 172.16.100.90 gerrit 52 | 53 | 可以看到这个命令的使用提示信息。为了减少输入,我设置了一个别名 54 | 55 | alias gerrit="ssh -p29418 172.16.100.90 gerrit" 56 | 57 | ## 4. 在沙箱工程中提交评审内容 58 | 59 | 使用 Gerrit 和 使用其它 Git 服务基本一样,其中存在的细微差别我会着重强调 60 | 61 | ### Clone sandbox project 62 | 63 | ![Clone with commit-msg hook](images/gerrit-clone-with-commit-msg-hook.png) 64 | 65 | 在 Web Console 中 拷贝 clone 命名,注意 必须选择`Clone with commit-msg hook`方式! 66 | 67 | ➜ /tmp git clone ssh://liyan@172.16.100.90:29418/sandbox/hello_world && scp -p -P 29418 liyan@172.16.100.90:hooks/commit-msg hello_world/.git/hooks/ 68 | Cloning into 'hello_world'... 69 | remote: Counting objects: 3, done 70 | remote: Finding sources: 100% (3/3) 71 | remote: Total 3 (delta 0), reused 3 (delta 0) 72 | Receiving objects: 100% (3/3), done. 73 | commit-msg 100% 4682 1.6MB/s 00:00 74 | 75 | ☞ 进一步阅读: Gerrit 在实现 Git 服务的基础上,添加了代码 review 的功能,因此在clone命令之后,还从服务器上又拷贝了一个 `commit-msg` 的钩子工具。 76 | 77 | ### 本地提交 78 | 79 | ➜ /tmp cd hello_world 80 | ➜ hello_world git:(master) git status 81 | On branch master 82 | Your branch is up-to-date with 'origin/master'. 83 | nothing to commit, working tree clean 84 | ➜ hello_world git:(master) st README.md #对文件进行修订 85 | ➜ hello_world git:(master) ✗ git add README.md 86 | ➜ hello_world git:(master) ✗ git commit -m 'commit for review' 87 | [master 72f756d] commit for review 88 | 1 file changed, 9 insertions(+), 2 deletions(-) 89 | 90 | ### Submit changes for review 91 | 92 | ☞ 检查 Change-Id , 这个增加的Change-Id字段是实现 Gerrit 代码评审功能所必须的。如果没有加入commit-msg钩子或不具备执行权限,将不生成此字段。这样commit的代码只能直接提交到主干,无法进行review 93 | 94 | ➜ hello_world git:(master) git log 95 | commit 72f756d6c5ee1a37ab965a18dc20a071ef8535b9 96 | Author: twotwo 97 | Date: Wed Feb 15 16:58:54 2017 +0800 98 | 99 | commit for review 100 | 101 | Change-Id: I648d97f05ef616d10e426d1ac6cff9ace07f39a8 102 | 103 | commit 12a550cf75e5b1347cf750ba35b440c9c9d049f8 104 | Author: twotwo 105 | Date: Tue Dec 13 18:30:00 2016 +0800 106 | 107 | add README.md 108 | 109 | Change-Id: I55be6e211eaf80a72a5e6c363745a9a43c64d362 110 | (END) 111 | 112 | 提交 Review 113 | 114 | ➜ hello_world git:(master) git push -u origin HEAD:refs/for/master 115 | Counting objects: 3, done. 116 | Delta compression using up to 8 threads. 117 | Compressing objects: 100% (2/2), done. 118 | Writing objects: 100% (3/3), 613 bytes | 0 bytes/s, done. 119 | Total 3 (delta 0), reused 0 (delta 0) 120 | remote: Processing changes: new: 1, refs: 1, done 121 | remote: 122 | remote: New Changes: 123 | remote: http://172.16.100.90/gerrit/2 commit for review 124 | remote: 125 | To ssh://172.16.100.90:29418/sandbox/hello_world 126 | * [new branch] HEAD -> refs/for/master 127 | 128 | ## 5. 在 Web Console 中进行代码评审 129 | 130 | ### Verify Changes 131 | 132 | 在 Web Console 的 [My Reviews](http://172.16.100.90/gerrit/#/dashboard/self) 的 Outgoing reviews 中,出现了这条提交信息 133 | 134 | Review 这个版本与上个版本直接的不同 135 | 136 | ![Diff Files one by one](images/gerrit-review.png) 137 | 138 | ### Submit the Change 139 | 140 | 在 Web Console 中, Code Review 进行打分,累计得分足够后确认提交。 141 | 142 | 143 | ## 6. 在沙箱中练习 144 | 145 | 为了更好的练习,我建议每个人都创建一个个人专用的 Gerrit 工程,即在远端生成一个 git 仓库 146 | 147 | ➜ learn-git git:(master) ✗ gerrit create-project users/liyan 148 | 149 | 然后,我们把这个工程 `clone` 到本地 150 | 151 | ➜ /tmp git clone ssh://liyan@172.16.100.90:29418/sandbox/hello_world && scp -p -P 29418 liyan@172.16.100.90:hooks/commit-msg hello_world/.git/hooks/ 152 | Cloning into 'hello_world'... 153 | remote: Counting objects: 3, done 154 | remote: Finding sources: 100% (3/3) 155 | remote: Total 3 (delta 0), reused 3 (delta 0) 156 | Receiving objects: 100% (3/3), 380 bytes | 0 bytes/s, done. 157 | commit-msg 100% 4682 1.9MB/s 00:00 158 | 159 | ⚠️请在这个项目里进行练习! 160 | -------------------------------------------------------------------------------- /git-branch.md: -------------------------------------------------------------------------------- 1 | # Git 分支篇 2 | 3 | - `Git Branching` Git 分支模型介绍 4 | - `Feature Branch` 功能分支工作流 5 | - 一个例子 6 | 7 | ## Git Branching 8 | Git 的分支模型是一个“Killing Feature”,它以一种难以置信的轻量方式处理分支,因此,鼓励在工作流程中频繁地使用分支与合并。 9 | 理解和精通这一特性,你便会意识到 Git 是如此的强大而又独特,并且从此真正改变你的开发方式。 10 | 11 | - ☞ 为啥建分支这么快? **分支本质上就是一个41字节的文件** 12 | - ☞ 为啥合并这么快? **三方合并机制** 13 | - ☞ **本地的分支合并操作与远端的分支管理** 14 | 15 | ## Feature Branch 16 | `功能分支工作流`仍然基于中央仓库,并且master分支还是代表了正式项目的历史。 但不是直接提交本地历史到各自的本地master分支,开发者每次在开始新功能前先创建一个新分支。 功能分支应该有个有描述性的名字,比如animated-menu-items或issue-#1061,这样可以让分支有个清楚且高聚焦的用途。 17 | 18 | 在master分支和功能分支之间,Git是没有技术上的区别,所以开发者可以用和集中式工作流中完全一样的方式编辑、暂存和提交修改到功能分支上。 19 | 20 | 另外,功能分支也可以 push 到中央仓库中。这样不修改正式代码就可以和其它开发者分享提交的功能。 21 | 由于master是仅有的一个『特殊』分支,在中央仓库上存多个功能分支不会有任何问题。 22 | 当然,这样做也可以很方便地备份各自的本地提交。 23 | 24 | 25 | ## 举个例子 26 | 27 | 在learn-git的编写中,采用功能分支工作流(以master为主干),基本流程为 28 | 29 | - 以master为基础创建一个分支来开发新需求 (Step 1, checkout -b dev-li3huo) 30 | - 然后在这个分支上开展工作(Step 2, commit on dev-li3huo) 31 | - 完成后再合并到主干并推送到远端(merge & push) 32 | 33 | 结果当我正在Step 2的工作过程中,忽然有朋友反馈意见,线上内容需要紧急修补。 34 | 为了应对这种: 35 | 36 | - 保存当前工作状态(Step 3, stash) 如果工作面不是太狼藉,把当前结果提交掉最干净了 37 | - 以主分支为基础,为紧急修复任务新建一个分支,并在其中修复它(和Step1,2 操作完全一致, 只是分支名称从dev-li3huo变成hotfix) 38 | - 在hotfix测试通过之后,切换回主干分支,然后合并这个修补分支(Step 4, merge hotfix & delete it) 39 | - 将改动推送到远程主干分支(Step 5, push -u origin master) 40 | - 切换回你最初工作的分支上,继续工作(Step 1, checkout dev-li3huo) 41 | - 三方合并与冲突处理(Step 6, merge dev-li3huo & delete it) 42 | 43 | 44 | ### Step 1, checkout -b dev-li3huo 45 | 首先,每次开发新功能,都应该新建一个单独的分支 46 | 47 | # 创建之前先同步一下主干最新代码 48 | ➜ learn-git git:(master) git checkout master 49 | Already on 'master' 50 | Your branch is up-to-date with 'origin/master'. 51 | ➜ learn-git git:(master) git pull 52 | Already up-to-date. 53 | 54 | # 新建一个开发分支 55 | ➜ learn-git git:(master) git checkout -b dev-li3huo 56 | Switched to a new branch 'dev-li3huo' 57 | ➜ learn-git git:(dev-li3huo) 58 | 59 | ☞ 'git checkout' is used to switch branches 60 | ☞ 'git pull' update for the repository cloned from, 61 | then merge one of them into current branch 62 | ☞ 'git checkout -b' is used to create and then switch branches 63 | 64 | ### Step 2, commit on dev-li3huo 65 | 在`dev-li3huo`下的开发告一段落,就可以提交commit了 66 | 67 | ➜ learn-git git:(dev-li3huo) ✗ git add . 68 | ➜ learn-git git:(dev-li3huo) ✗ git status 69 | On branch dev-li3huo 70 | Changes to be committed: 71 | (use "git reset HEAD ..." to unstage) 72 | 73 | new file: git-process.md 74 | 75 | # 提交(在编辑器中填写提交信息) 76 | ➜ learn-git git:(dev-li3huo) ✗ git commit --verbose 77 | [dev-li3huo 470a772] Git 标准使用流程 78 | 1 file changed, 94 insertions(+) 79 | create mode 100644 git-process.md 80 | 81 | #### :point_right: 提交信息的格式 82 | 83 | 提交commit时,必须给出完整扼要的提交信息,下面是一个范本([提交信息书写规则](git-commit.md)) 84 | 85 | 86 | Present-tense summary under 50 characters 87 | 88 | * More information about commit (under 72 characters). 89 | * More information about commit (under 72 characters). 90 | 91 | http://project.management-system.com/ticket/123 92 | 93 | 第一行是不超过50个字的提要,然后空一行,罗列出改动原因、主要变动、以及需要注意的问题。最后,提供对应的网址(比如Bug ticket) 94 | 95 | ### Step 3, stash 96 | 参考[7.3 Git 工具 - 储藏与清理](https://git-scm.com/book/zh/v2/Git-工具-储藏与清理#_git_stashing) 97 | 98 | 有时,当你在项目的一部分上已经工作一段时间后,所有东西都进入了混乱的状态,而这时你想要切换到另一个分支做一点别的事情。 问题是,你不想仅仅因为过会儿回到这一点而为做了一半的工作创建一次提交。 针对这个问题的答案是 git stash 命令。 99 | 100 | # 保存工作面,之后就可以切回主分支了 101 | $ git stash 102 | Saved working directory and index state \ 103 | "WIP on master: 049d078 added the index file" 104 | HEAD is now at 049d078 added the index file 105 | (To restore them type "git stash apply") 106 | 107 | # 修复完工后把工作面还原成一个新分支 108 | $ git stash branch tmpchanges 109 | Switched to a new branch "tmpchanges" 110 | 111 | ### Step 4, merge hotfix & delete it 112 | 113 | ➜ learn-git git:(hotfix) ✗ git checkout master 114 | ➜ learn-git git:(master) ✗ git merge hotfix 115 | ... 116 | # master也指向了hotfix, 可以清除掉了 117 | ➜ learn-git git:(master) ✗ git branch -d hotfix 118 | 119 | 120 | ### Step 5, push -u origin master 121 | 122 | ➜ learn-git git:(master) git push -u origin master 123 | 124 | ### Step 6, merge dev-li3huo & delete it 125 | 126 | ## More Actions 127 | 128 | ### 经常与远程版本库保持同步 129 | 分支的开发过程中,要经常与主干保持同步 130 | 131 | ➜ learn-git git:(dev-li3huo) ✗ git fetch origin 132 | remote: Counting objects: 4, done. 133 | remote: Compressing objects: 100% (1/1), done. 134 | remote: Total 4 (delta 3), reused 4 (delta 3), pack-reused 0 135 | Unpacking objects: 100% (4/4), done. 136 | From https://github.com/twotwo/learn-git 137 | 27177fd..934621e master -> origin/master 138 | 139 | ☞ 'git fetch' fetches down all the information that is in that repository 140 | that is not in your current one and stores it in your local database 141 | 142 | 143 | ### 变基操作 144 | 145 | ☞ 'git rebase' reapply commits on top of another base tip 146 | 147 | 148 | #### 1. 开发分支以master为基础应用所有更新 149 | 分支开发完成后,本地有一大堆的commit。这些提交对于主干往往没有太多意义。我们希望只有一个(或最多两三个)commit,这样不仅清晰,也容易管理。 150 | 151 | 那么,怎样才能将多个commit合并呢?这就要用到 git rebase 命令。 152 | 153 | ➜ learn-git git:(dev-li3huo) git rebase master 154 | 155 | #### 2. 把 rebase的内容一次性的合并到主干 156 | 157 | ➜ learn-git git:(dev-li3huo) git checkout master 158 | ➜ learn-git git:(master) git merge dev-li3huo 159 | Updating 27177fd..63ccd06 160 | Fast-forward 161 | git-process.md | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 162 | 1 file changed, 151 insertions(+) 163 | create mode 100644 git-process.md 164 | 165 | #### 3. 合并后的主干推送到远程仓库 166 | 合并commit后,就可以推送当前分支到远程仓库了 167 | 168 | ➜ learn-git git:(master) git push -u origin master 169 | To https://github.com/twotwo/learn-git.git 170 | ! [rejected] master -> master (non-fast-forward) 171 | error: failed to push some refs to 'https://github.com/twotwo/learn-git.git' 172 | hint: Updates were rejected because the tip of your current branch is behind 173 | hint: its remote counterpart. Integrate the remote changes (e.g. 174 | hint: 'git pull ...') before pushing again. 175 | hint: See the 'Note about fast-forwards' in 'git push --help' for details. 176 | 177 | git push命令要加上force参数,因为rebase以后,分支历史改变了,跟远程分支不一定兼容,有可能要强行推送 178 | 179 | ➜ learn-git git:(master) git push -u origin master --force 180 | Counting objects: 12, done. 181 | Delta compression using up to 8 threads. 182 | Compressing objects: 100% (12/12), done. 183 | Writing objects: 100% (12/12), 4.55 KiB | 0 bytes/s, done. 184 | Total 12 (delta 7), reused 0 (delta 0) 185 | remote: Resolving deltas: 100% (7/7), completed with 1 local objects. 186 | To https://github.com/twotwo/learn-git.git 187 | + 934621e...63ccd06 master -> master (forced update) 188 | Branch master set up to track remote branch master from origin. 189 | 190 | ## 参考 191 | - 《Pro Git V2》 3. Git 分支 192 | 193 | * [3.1 分支简介](https://git-scm.com/book/zh/v2/Git-分支-分支简介) 分支的基本原理,建议掌握一下,否则估计会搞不懂下面的例子 194 | * [3.2 新建与合并](https://git-scm.com/book/zh/v2/Git-分支-分支的新建与合并) 一个应用的例子 195 | * [3.4 常见工作流介绍](https://git-scm.com/book/zh/v2/Git-分支-分支开发工作流) 196 | 197 | - `长期分支` 适用于对稳定性要求很高、复杂庞大的项目 198 | - `特性分支` 短期分支,对任何规模的项目都适用 - Git 特有的工作流 199 | 200 | * [3.5 远程分支](https://git-scm.com/book/zh/v2/Git-分支-远程分支) 远程分支的管理 201 | 202 | - `跟踪分支` git checkout --track origin/remote_branch 203 | - `拉取` pull == fetch + merge到跟踪分支 204 | - `删除远程分支` git push origin --delete remote_branch 205 | 206 | * [3.6 rebase](https://git-scm.com/book/zh/v2/Git-分支-变基) 一种更高级的合并方法,一下看不懂的可以放放;看不懂的时候尽量别用,滥用产生的副作用很大! 207 | 208 | - 确保在向远程分支推送时能保持提交历史的整洁 209 | - `变基的风险` 不要对在你的仓库外有副本的分支执行变基 210 | - `变基 vs. 合并` 总的原则是,只对尚未推送或分享给别人的本地修改执行变基操作清理历史,从不对已推送至别处的提交执行变基操作,这样,你才能享受到两种方式带来的便利 211 | 212 | - [功能分支工作流 by Git](https://github.com/oldratlee/translations/blob/master/git-workflows-and-tutorials/workflow-feature-branch.md) 213 | -------------------------------------------------------------------------------- /git-commit.md: -------------------------------------------------------------------------------- 1 | # 提交信息书写规则 2 | 3 | ## 参考 4 | - [如何书写提交信息](http://chris.beams.io/posts/git-commit/) 当项目越来越大,提交信息越来越复杂的时候,如何书写好提交信息就变得至关重要,这篇文章的作者总结出7条准则。 5 | - [Commit message 和 change log编写规范-阮一峰](http://www.ruanyifeng.com/blog/2016/01/commit_message_change_log.html) 良好的 commit log 好处大大的多。 [AngularJS Git Commit Message Conventions](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#heading=h.uyo6cb12dt6w) -------------------------------------------------------------------------------- /git-for-windows.md: -------------------------------------------------------------------------------- 1 | # Git for Windows 2 | Git for Windows是Git官方提供的Windows版本,包括命令行和图形界面。 3 | 4 | [git-for-windows](https://git-for-windows.github.io/) 5 | 6 | 7 | Git for Windows focuses on offering a lightweight, native set of tools 8 | that bring the full feature set of the Git SCM to Windows while 9 | providing appropriate user interfaces for experienced Git users and novices alike. 10 | 11 | ![示意图](https://git-for-windows.github.io/img/gw1web_thumb.png) 12 | 13 | ##git bash 14 | 推荐*Git for Windows*是为了让大家也能windows的终端下书写命令行。 15 | 这里有一篇参考文章[初学git:用git bash往github push代码](http://www.cnblogs.com/zichi/p/4703999.html) 16 | -------------------------------------------------------------------------------- /git-primer.md: -------------------------------------------------------------------------------- 1 | # Git 入门篇 2 | - `Git Overview` 概览,介绍 Git 的一些基本概念 3 | - `Centralized Workflow` 一个用集中式工作流程工作的例子,从config、init/clone、commit到push、pull,介绍在使用 Git 中需要掌握的命令 4 | 5 | ## Git Overview 6 | 7 | ### Git项目的目录结构 8 | ![Git Areas](images/git_areas.png) 9 | 10 | 一个本地的 Git 项目可以分成三个区域 11 | 12 | * Git 仓库(Repository) .git目录。保存项目的元数据和对象数据库的地方 13 | * 工作目录(Working Directory) 对项目的某个版本独立提取出来的内容,文件夹中除去.git的其他文件 14 | * 暂存区域(Staging Area) .git/index文件,也被称作“索引” 15 | 16 | ### 本地文件的三种状态 17 | 本地文件可能处在以下三种状态 18 | 19 | * 已提交(committed)文件已经保存到本地Repository中 20 | * 已修改(modified) 文件中的数据相对Repository已经发生了变化 21 | * 已暂存(staged) 对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中 22 | 23 | ![File Lifecycle](images/file_lifecycle.png) 24 | 25 | 文件的状态变化周期说明 26 | 27 | ## Centralized Workflow 28 | ![Git Actions](images/git_actions.jpg) 29 | 30 | ### 账号配置 31 | 参考1 [初次运行 Git 的配置](https://git-scm.com/book/zh/v2/起步-初次运行-Git-前的配置) 32 | 33 | 参考2 [Git 设置和取消代理](https://gist.github.com/laispace/666dd7b27e9116faece6) 34 | 35 | 参考3 [Git 设置编辑器](https://help.github.com/articles/associating-text-editors-with-git/) 36 | 37 | #### git config 38 | $ git help config 39 | 40 | ### Set Your Identity 41 | $ git config --global user.name "twotwo" 42 | $ git config --global user.email twotwo@li3huo.com 43 | 44 | ### Set/Unset Your Proxy: http&https 45 | $ git config --global http.proxy 'socks5://127.0.0.1:1080' 46 | $ git config --global --unset http.proxy 47 | 48 | ### Use Sublime as Your Editor 49 | $ ln -s "/Applications/Sublime Text 2.app/Contents/SharedSupport/bin/subl" ~/app/bin/ 50 | $ git config --global core.editor "subl -n -w" 51 | 52 | 53 | ### 获取与创建项目 54 | 参考[Git 获取与创建项目](https://git-scm.com/book/zh/v2/Git-命令-获取与创建项目) 55 | 56 | *创建项目* 运行 git init 就可以将一个目录转变成一个 Git 仓库,这样就可以开始对它进行版本管理了 57 | #### git init 58 | 59 | ➜ w2016 mkdir scm 60 | ➜ w2016 cd scm 61 | ➜ scm git init 62 | Initialized empty Git repository in /opt/local/ide/workspaces/w2016/scm/.git/ 63 | 64 | *获取项目* 65 | #### git clone & git commit 66 | 67 | $ git clone https://github.com/twotwo/learn-git.git 68 | $ git remote -v 69 | origin https://github.com/twotwo/learn-git.git (fetch) 70 | origin https://github.com/twotwo/learn-git.git (push) 71 | 72 | ### 提交 73 | 74 | *检查变更情况* 75 | #### git status 76 | 77 | $ git status 78 | modified: primer.md 79 | 80 | #### git add 81 | 82 | $ git add primer.md 83 | 84 | #### git commit 85 | 86 | $ git commit -m "update primer.md" 87 | [master 85c4f11] update primer.md 88 | 1 file changed, 24 insertions(+), 7 deletions(-) 89 | 90 | 91 | ### Pushing to Remote 92 | 93 | #### git remote 94 | 95 | ➜ scm git:(master) ✗ git remote -v 96 | ➜ scm git:(master) ✗ git remote add origin https://github.com/twotwo/scm.git 97 | 98 | 99 | ### 参考 100 | * [一个初始化、提交和同步的例子](http://wiki.eclipse.org/EGit/Git_For_Eclipse_Users#Worked_example) 101 | * [集中式工作流 by Git](https://github.com/oldratlee/translations/blob/master/git-workflows-and-tutorials/workflow-centralized.md) 102 | -------------------------------------------------------------------------------- /git-process.md: -------------------------------------------------------------------------------- 1 | # Git 流程篇 2 | 3 | ## Process Models 4 | 5 | ### -------------------------------------------------------------------------------- /git-tools.md: -------------------------------------------------------------------------------- 1 | # Git 工具篇 2 | > 子贡问为仁。子曰:“工欲善其事,必先利其器。居是邦也,事其大夫之贤者,友其士之仁者。” 3 | > 4 | > -- 孔子(春秋)《论语·卫灵公》 5 | 6 | 要想理解 Git,最好就是就在终端下亲手敲入相关的命令,同时在大脑和肌肉层面形成记忆。Mac 和 Linux 系统下 Git 安装完毕就可以使用,Windows下也有[Git for windows](https://msysgit.github.io/)。 7 | 8 | 一开始的命令的确很多,别无它法,熟能生巧,多练习即可能够掌握日常使用的一些命令,再配合[`常用命令的alias`](https://git-scm.com/book/tr/v2/Git-Basics-Git-Aliases)或者强大的 [`zsh 终端`](http://www.ixirong.com/2015/04/27/strong-bash-use-oh-my-zsh/)都能显著的提升效率。 9 | 10 | 常用的IDE中一般都集成了git的图形界面,如Eclispe(EGit)/XCode/Idea等。可以在一个项目组内同一使用。 11 | 12 | ## 工具列表 13 | * [Git](https://git-scm.com/downloads) Linux/Mac OS X下的命令行工具,原汁原味 14 | * [Git for Windows](git-for-windows.md) Windows下的命令行工具,程序员赶快换Mac吧 15 | * [EGit](http://wiki.eclipse.org/EGit/User_Guide) Eclipse自带的GUI Git Client 16 | * [Gerrit](http://wiki.li3huo.com/Gerrit) Web界面下的代码审核平台,基于git,跑在tomcat下 17 | * [SourceTree](https://www.sourcetreeapp.com/) 免费,功能齐全,Mac+Window 版本,集成 Github 等服务 18 | -------------------------------------------------------------------------------- /images/file_lifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/images/file_lifecycle.png -------------------------------------------------------------------------------- /images/gerrit-clone-with-commit-msg-hook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/images/gerrit-clone-with-commit-msg-hook.png -------------------------------------------------------------------------------- /images/gerrit-http-password.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/images/gerrit-http-password.png -------------------------------------------------------------------------------- /images/gerrit-review.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/images/gerrit-review.png -------------------------------------------------------------------------------- /images/gerrit-sign-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/images/gerrit-sign-in.png -------------------------------------------------------------------------------- /images/gerrit-ssh-public-keys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/images/gerrit-ssh-public-keys.png -------------------------------------------------------------------------------- /images/git_actions.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/images/git_actions.jpg -------------------------------------------------------------------------------- /images/git_areas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/images/git_areas.png -------------------------------------------------------------------------------- /images/gitflow_feature_branches.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/gitflow_hotfix_branches.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/gitflow_main_branches.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/gitflow_release_branches.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/merge-without-ff@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/images/merge-without-ff@2x.png -------------------------------------------------------------------------------- /images/why-git.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/images/why-git.png -------------------------------------------------------------------------------- /images/workflow-gitflow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/images/workflow-gitflow.jpg -------------------------------------------------------------------------------- /images/workflow-github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/images/workflow-github.png -------------------------------------------------------------------------------- /pull-request.md: -------------------------------------------------------------------------------- 1 | # `Pull Request` 2 | 3 | 4 | - [解析`Pull Request`](#解析pull-request) 5 | - [工作方式](#beer-工作方式) 6 | - [在功能分支工作流中使用`Pull Request`](#在功能分支工作流中使用pull-request) 7 | - [在`Gitflow`工作流中使用`Pull Request`](#在gitflow工作流中使用pull-request) 8 | - [在`Forking`工作流中使用`Pull Request`](#在forking工作流中使用pull-request) 9 | - [示例](#beer-示例) 10 | 1. [小红`fork`正式项目](#小红fork正式项目) 11 | 1. [小红克隆她的`Bitbucket`仓库](#小红克隆她的bitbucket仓库) 12 | 1. [小红开发新功能](#小红开发新功能) 13 | 1. [小红`push`功能到她的`Bitbucket`仓库中](#小红push功能到她的bitbucket仓库中) 14 | 1. [小红发起`Pull Request`](#小红发起pull-request) 15 | 1. [小明review `Pull Request`](#小明review-pull-request) 16 | 1. [小红补加提交](#小红补加提交) 17 | - [下一站](#beer-下一站) 18 | 19 | `Pull Requests`是 [Bitbucket](https://bitbucket.org/){:target="_blank"} 上方便开发者之间协作的功能。 20 | 提供了一个用户友好的`Web`界面,在集成提交的变更到正式项目前可以对变更进行讨论。 21 | 22 | Pull Request 本质是由第三方提供的独立于 Git 的额外工具。 23 | 24 | ![](images/pull-request-bitbucket.png) 25 | 26 | 开发者向团队成员通知功能开发已经完成,`Pull Requests`是最简单的用法。 27 | 开发者完成功能开发后,通过`Bitbucket`账号发起一个`Pull Request`。 28 | 这样让涉及这个功能的所有人知道要去做`Code Review`和合并到`master`分支。 29 | 30 | 但是,`Pull Request`远不止一个简单的通知,而是为讨论提交的功能的一个专门论坛。 31 | 如果变更有任何问题,团队成员反馈在`Pull Request`中,甚至`push`新的提交微调功能。 32 | 所有的这些活动都直接跟踪在`Pull Request`中。 33 | 34 | ![](images/pull-request-overview.png) 35 | 36 | 相比其它的协作模型,这种分享提交的形式有助于打造一个更流畅的工作流。 37 | `SVN`和`Git`都能通过一个简单的脚本收到通知邮件;但是,讨论变更时,开发者通常只能去回复邮件。 38 | 这样做会变得杂乱,尤其还要涉及后面的几个提交时。 39 | `Pull Requests`把所有相关功能整合到一个和`Bitbucket`仓库界面集成的用户友好`Web`界面中。 40 | 41 | ### 解析`Pull Request` 42 | 43 | 当要发起一个`Pull Request`,你所要做的就是请求(`Request`)另一个开发者(比如项目的维护者) 44 | 来`pull`你仓库中一个分支到他的仓库中。这意味着你要提供4个信息以发起`Pull Request`: 45 | 源仓库、源分支、目的仓库、目的分支。 46 | 47 | ![](images/pull-request-anatomy.png) 48 | 49 | 这几值多数`Bitbucket`都会设置上合适的缺省值。但取决你用的协作工作流,你的团队可能会要指定不同的值。 50 | 上图显示了一个`Pull Request`请求合并一个功能分支到正式的`master`分支上,但可以有多种不同的`Pull Request`用法。 51 | 52 | :beer: 工作方式 53 | --------------------- 54 | 55 | `Pull Request`可以和[功能分支工作流](workflow-feature-branch.md)、[`Gitflow`工作流](workflow-gitflow.md)或[`Forking`工作流](workflow-forking.md)一起使用。 56 | 但一个`Pull Request`要求要么分支不同要么仓库不同,所以不能用于[集中式工作流](workflow-centralized.md)。 57 | 在不同的工作流中使用`Pull Request`会有一些不同,但基本的过程是这样的: 58 | 59 | 1. 开发者在本地仓库中新建一个专门的分支开发功能。 60 | 1. 开发者`push`分支修改到公开的`Bitbucket`仓库中。 61 | 1. 开发者通过`Bitbucket`发起一个`Pull Request`。 62 | 1. 团队的其它成员`review` `code`,讨论并修改。 63 | 1. 项目维护者合并功能到官方仓库中并关闭`Pull Request`。 64 | 65 | 本文后面内容说明,`Pull Request`在不同协作工作流中如何应用。 66 | 67 | ### 在功能分支工作流中使用`Pull Request` 68 | 69 | 功能分支工作流用一个共享的`Bitbucket`仓库来管理协作,开发者在专门的分支上开发功能。 70 | 但不是立即合并到`master`分支上,而是在合并到主代码库之前开发者应该开一个`Pull Request`发起功能的讨论。 71 | 72 | ![](images/pull-request-feature-branch.png) 73 | 74 | 功能分支工作流只有一个公开的仓库,所以`Pull Request`的目的仓库和源仓库总是同一个。 75 | 通常开发者会指定他的功能分支作为源分支,`master`分支作为目的分支。 76 | 77 | 收到`Pull Request`后,项目维护者要决定如何做。如果功能没问题,就简单地合并到`master`分支,关闭`Pull Request`。 78 | 但如果提交的变更有问题,他可以在`Pull Request`中反馈。之后新加的提交也会评论之后接着显示出来。 79 | 80 | 在功能还没有完全开发完的时候,也可能发起一个`Pull Request`。 81 | 比如开发者在实现某个需求时碰到了麻烦,他可以发一个包含正在进行中工作的`Pull Request`。 82 | 其它的开发者可以在`Pull Request`提供建议,或者甚至直接添加提交来解决问题。 83 | 84 | ### 在`Gitflow`工作流中使用`Pull Request` 85 | 86 | `Gitflow`工作流和功能分支工作流类似,但围绕项目发布定义一个严格的分支模型。 87 | 在`Gitflow`工作流中使用`Pull Request`让开发者在发布分支或是维护分支上工作时, 88 | 可以有个方便的地方对关于发布分支或是维护分支的问题进行交流。 89 | 90 | ![](images/gitflow-workflow-pull-request.png) 91 | 92 | `Gitflow`工作流中`Pull Request`的使用过程和上一节中完全一致: 93 | 当一个功能、发布或是热修复分支需要`Review`时,开发者简单发起一个`Pull Request`, 94 | 团队的其它成员会通过`Bitbucket`收到通知。 95 | 96 | 新功能一般合并到`develop`分支,而发布和热修复则要同时合并到`develop`分支和`master`分支上。 97 | `Pull Request`可能用做所有合并的正式管理。 98 | 99 | ### 在`Forking`工作流中使用`Pull Request` 100 | 101 | 在`Forking`工作流中,开发者`push`完成的功能到他自己的仓库中,而不是共享仓库。 102 | 然后,他发起一个`Pull Request`,让项目维护者知道他的功能已经可以`Review`了。 103 | 104 | 在这个工作流,`Pull Request`的通知功能非常有用, 105 | 因为项目维护者不可能知道其它开发者在他们自己的仓库添加了提交。 106 | 107 | ![](images/pull-request-forking-workflow-1.png) 108 | 109 | 由于各个开发有自己的公开仓库,`Pull Request`的源仓库和目标仓库不是同一个。 110 | 源仓库是开发者的公开仓库,源分支是包含了修改的分支。 111 | 如果开发者要合并修改到正式代码库中,那么目标仓库是正式仓库,目标分支是`master`分支。 112 | 113 | `Pull Request`也可以用于正式项目之外的其它开发者之间的协作。 114 | 比如,如果一个开发者和一个团队成员一起开发一个功能,他们可以发起一个`Pull Request`, 115 | 用团队成员的`Bitbucket`仓库作为目标,而不是正式项目的仓库。 116 | 然后使用相同的功能分支作为源和目标分支。 117 | 118 | ![](images/pull-request-forking-workflow-2.png) 119 | 120 | 2个开发者之间可以在`Pull Request`中讨论和开发功能。 121 | 完成开发后,他们可以发起另一个`Pull Request`,请求合并功能到正式的`master`分支。 122 | 在`Forking`工作流中,这样的灵活性让`Pull Request`成为一个强有力的协作工具。 -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # README 2 | Git 培训材料,培养团队掌握基于 Git 的协作开发技能,规范相关开发流程 3 | 4 | ##目标定位 5 | 介绍 Git 基本使用及团队协作流程,让团队掌握版本管理、代码审核、软件发布等基本开发流程。 6 | 7 | *如果你要有一些资源,希望和我一起,把这个搞起来,很简单,`Fork - 修改 - Pull Request` 就 ok。* 8 | 9 | ## 1、Git 起步 10 | 11 | - [Why Git](why-git.md) Git 是什么? 相比 SVN 有哪些不同? 为啥要用 Git 替换 SVN ? 12 | - [工具篇](git-tools.md) Git 协作开发使用到的工具和服务 13 | - [入门篇](git-primer.md) Git 基础知识介绍,及一个 **集中式工作流程** 的例子 14 | 15 | ### 起步 - 总结 16 | 读完本章,你应该基本了解 Git 是什么、与集中式版本控制系统有何区别等问题; 17 | 并且有了一份能够工作的 Git 版本。 18 | 19 | 接下来我们将学习 Git 的杀手级特性:分支模型,并以此为基础的其余版本管理工作流。 20 | 21 | 我们用 [Pro Git v2 中文版](https://git-scm.com/book/zh/v2) 作为 Git 学习的参考教材,这是 Git 学习的权威材料,大家要抽出时间看一遍。 22 | 23 | ## 2、Git 工作流 24 | 25 | - [Git 分支](git-branch.md) Git 分支模型介绍,及一个 **功能分支工作流** 的例子 26 | - [Gitflow 工作流](workflow-gitflow.md) 经典模型,体现了工作流的经验和精髓。裁剪后可以适用于各种规模的企业以及开源项目开发 27 | - [Forking 工作流](workflow-forking.md) GitHub风格,开源项目的理想工作流,适用于松散组织的团队 28 | 29 | ### 工作流 - 总结 30 | 本周讲解了 Git 分支与合并的基础知识。 你现在应该能自如地创建并切换至新分支、在不同分支之间切换以及合并本地分支。 31 | 在了解了这几种基本工作流后,根据当前开发特点总结出团队代码管理模式 32 | 33 | 更全面的理解Git 工作流,推荐阅读[Comparing Workflows](https://www.atlassian.com/git/tutorials/comparing-workflows/) atlassian的 Git 工作流对比,这里还有@oldratlee 的[译文](https://github.com/oldratlee/translations/tree/master/git-workflows-and-tutorials) 34 | 35 | * `Centralized Workflow` **集中式工作流** ,用和 SVN 类似的方式完成代码管理 36 | * `Feature Branch Workflow` **功能分支工作流** 是 Git 的基础工作流,是企业项目工作流、开源项目工作流的核心子流程 37 | * `Gitflow Workflow` **Gitflow 工作流** 是 Git 应用中最经典的模型,体现了工作流的经验和精髓 38 | * `Forking Workflow` **Forking 工作流** 是 GitHub 的风格,开源项目的理想工作流,适用于松散组织的团队 39 | 40 | ## 3、基于 Gerrit 的代码评审实践 41 | - [Gerrit 服务搭建](http://wiki.li3huo.com/Gerrit#Setup_the_Server) 42 | - [Gerrit 使用入门](gerrit-start.md) 43 | 44 | ## 4、其它 45 | - [提交信息书写规则](git-commit.md) Git协作开发使用到的工具、服务总结 46 | - [有关 git 的学习资料](https://github.com/xirong/my-git) @xirong 整理的 Git 资源的汇总,[xirong](http://www.ixirong.com/about/){:target="_blank"} 是一位阿里前端/全站工程师 47 | - [软件实践杂记](https://github.com/oldratlee/software-practice-miscellany) @oldratlee 李鼎(哲良) 整理的SCM相关内容 48 | - [git-recipes](https://github.com/geeeeeeeeek/git-recipes/wiki) @童仲毅 整理翻译的一些优秀文章 49 | - [Git-flight-rules](https://github.com/k88hudson/git-flight-rules) 一些日常使用中的场景,比如提交错了分支、提交时的用户名邮箱不对、丢弃某些提交、未提交的代码直接提交到另外一个分支等等,很实用 50 | 51 | 52 | -------------------------------------------------------------------------------- /slides/MyriadPro-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/slides/MyriadPro-Bold.otf -------------------------------------------------------------------------------- /slides/MyriadPro-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/slides/MyriadPro-Regular.otf -------------------------------------------------------------------------------- /slides/gerrit/gerrit.html: -------------------------------------------------------------------------------- 1 | 2 | 24 | 25 | 26 | 27 | 28 | Gerrit 使用入门 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 |
48 |
49 |
50 |
51 | 52 | 53 |
54 |
55 |
56 | 57 |

Gerrit 使用入门

58 | 59 | 60 |

Generate HTML5 slideshows by landslide

61 | 62 | 63 |
    64 |
  • V1.0
  • 65 |
  • liyan 2017-11-15
  • 66 |
67 |

Back to Git 协作开发上手指南

68 |

1. Gerrit 是什么

69 |

2. Gerrit 怎么用

70 | 71 |
72 |
73 |

Presenter Notes

74 |
75 | 76 |
77 |
78 |
79 | 80 | 83 | 84 | 87 |
88 |
89 |
90 | 91 | 92 |
93 |
94 |
95 | 96 |

1. Gerrit 简介

97 | 98 | 99 |

Gerrit 是一个用 Java 写的开源软件:

100 |
    101 |
  • 首先,Gerrit 提供了具备访问控制的 Git 版本管理服务;
  • 102 |
  • 然后,Gerrit 又在 Git 基础上提供了web方式的代码评审功能;
  • 103 |
  • 最有趣的一点,这个代码评审功能是可选的,团队可以决定一个项目是否需要加入评审逻辑
  • 104 |
105 | 106 |
107 |
108 |

Presenter Notes

109 |
110 | 111 |
112 |
113 |
114 | 115 | 118 | 119 | 122 |
123 |
124 |
125 | 126 | 127 |
128 |
129 |
130 | 131 |

1. Gerrit 简介

132 | 133 | 134 |

CI Builder, we use Jenkins / Reviewer, review process is optional

135 |

Gerrit Overview

136 | 137 |
138 |
139 |

Presenter Notes

140 |
141 | 142 |
143 |
144 |
145 | 146 | 149 | 150 | 153 |
154 |
155 |
156 | 157 | 158 |
159 |
160 |
161 | 162 |

2. Gerrit 初体验

163 | 164 | 165 |

使用 Gerrit 的最简步骤

166 |
    167 |
  • 设置访问权限
  • 168 |
  • Submit a Change
  • 169 |
170 | 171 |
172 |
173 |

Presenter Notes

174 |
175 | 176 |
177 |
178 |
179 | 180 | 183 | 184 | 187 |
188 |
189 |
190 | 191 | 192 |
193 |
194 |
195 | 196 |

2.1 设置访问权限

197 | 198 | 199 |

如果需要对代码进行修改或者对代码进行评审,就必须到 Web 控制台上进行设置

200 |

登录 Web 控制台

201 |

Gerrit Web Console 已经集成了账号管理系统,大家可以使用自己的jira账号直接登录(Sign In)

202 |

点击Gerrit 页面右上角“Sign In”链接后,出现如下登录界面

203 |

Gerrit Sign In

204 | 205 |
206 |
207 |

Presenter Notes

208 |
209 | 210 |
211 |
212 |
213 | 214 | 217 | 218 | 221 |
222 |
223 |
224 | 225 | 226 |
227 |
228 |
229 | 230 |

配置 SSH(HTTP) 访问认证

231 | 232 | 233 |

进入Settings界面

234 |

SSH Public Keys

235 |

Gerrit SSH Public Keys

236 |

点"Add Key ..."按钮,把本机ssh密钥对中的公钥填入对话框。

237 |

☞ 进一步阅读: Use Public Key Authentication with SSH

238 | 239 |
240 |
241 |

Presenter Notes

242 |
243 | 244 |
245 |
246 |
247 | 248 | 251 | 252 | 255 |
256 |
257 |
258 | 259 | 260 |
261 |
262 |
263 | 264 |

用命令行直接访问 Gerrit

265 | 266 | 267 |

完成以上配置后,我们就可以与 Gerrit 进行对话了

268 |
➜  learn-git git:(master) ✗ ssh -p29418 liyan@172.16.100.90
269 | 
270 |   ****    Welcome to Gerrit Code Review    ****
271 | 
272 |   Hi liyan, you have successfully connected over SSH.
273 | 
274 |   Unfortunately, interactive shells are disabled.
275 |   To clone a hosted Git repository, use:
276 | 
277 |   git clone ssh://liyan@172.16.100.90:29418/REPOSITORY_NAME.git
278 | 
279 | Connection to 172.16.100.90 closed.
280 | 
281 | 282 |
283 |
284 |

Presenter Notes

285 |
286 | 287 |
288 |
289 |
290 | 291 | 294 | 295 | 298 |
299 |
300 |
301 | 302 | 303 |
304 |
305 |
306 | 307 |

HTTP Password

308 | 309 | 310 |

Gerrit HTTP Password

311 |

On Gerrit installations that do not support SSH authentication, the user must authenticate via HTTP/HTTPS.

312 |

参考链接

313 |

设置之后,用户可以通过HTTP协议访问仓库

314 |
git clone http://it.feiliu.com/gerrit/a/sandbox/hello_world
315 | 
316 |

HTTP Digest Authentication Removed at 2.14

317 | 318 |
319 |
320 |

Presenter Notes

321 |
322 | 323 |
324 |
325 |
326 | 327 | 330 | 331 | 334 |
335 |
336 |
337 | 338 | 339 |
340 |
341 |
342 | 343 |

执行一下 gerrit 命令

344 | 345 | 346 |

配置好 SSH Public Keys 之后,我们就可以使用 Gerrit 提供的服务了

347 |
ssh -p29418 172.16.100.130 gerrit
348 | 
349 |

可以看到这个命令的使用提示信息。为了减少输入,我设置了一个别名

350 |
alias gerrit="ssh -p29418 172.16.100.90 gerrit"
351 | 
352 | 353 |
354 |
355 |

Presenter Notes

356 |
357 | 358 |
359 |
360 |
361 | 362 | 365 | 366 | 369 |
370 |
371 |
372 | 373 | 374 |
375 |
376 |
377 | 378 |

2.2 Submit a Change

379 | 380 | 381 |

接下来咱们在沙箱工程中提交一个变更

382 |

使用 Gerrit 和 使用其它 Git 服务基本一样,其中存在的细微差别我会着重强调

383 |

拷贝 clone 命令

384 |

Clone with commit-msg hook

385 |

在 Web Console 中 拷贝 clone 命令,注意 必须选择Clone with commit-msg hook方式!

386 |
➜  /tmp git clone ssh://liyan@172.16.100.130:29418/sandbox/hello_world && scp -p -P 29418 liyan@172.16.100.130:hooks/commit-msg hello_world/.git/hooks/
387 | 
388 | 389 |
390 |
391 |

Presenter Notes

392 |
393 | 394 |
395 |
396 |
397 | 398 | 401 | 402 | 405 |
406 |
407 |
408 | 409 | 410 |
411 |
412 |
413 | 414 |

2.2 Submit a Change

415 | 416 | 417 |

执行 clone 命令

418 |

Execute Clone Command

419 |

☞ 进一步阅读: Gerrit 在实现 Git 服务的基础上,添加了代码 review 的功能,因此在clone命令之后,还从服务器上又拷贝了一个 commit-msg 的钩子工具。

420 | 421 |
422 |
423 |

Presenter Notes

424 |
425 | 426 |
427 |
428 |
429 | 430 | 433 | 434 | 437 |
438 |
439 |
440 | 441 | 442 |
443 |
444 |
445 | 446 |

2.2 Submit a Change

447 | 448 | 449 |

提交本地 && 推送到 Gerrit

450 |

Execute Clone Command

451 | 452 |
453 |
454 |

Presenter Notes

455 |
456 | 457 |
458 |
459 |
460 | 461 | 464 | 465 | 468 |
469 |
470 |
471 | 472 | 473 |
474 |
475 |
476 | 477 |

3. Gerrit 进阶: Submit changes for review

478 | 479 | 480 |

☞ 检查 Change-Id , 这个增加的Change-Id字段是实现 Gerrit 代码评审功能所必须的。如果没有加入commit-msg钩子或不具备执行权限,将不生成此字段。这样commit的代码只能直接提交到主干,无法进行review

481 |
➜  hello_world git:(master) git log
482 | commit 72f756d6c5ee1a37ab965a18dc20a071ef8535b9
483 | Author: twotwo <twotwo.li@gmail.com>
484 | Date:   Wed Feb 15 16:58:54 2017 +0800
485 | 
486 |     commit for review
487 | 
488 |     Change-Id: I648d97f05ef616d10e426d1ac6cff9ace07f39a8
489 | 
490 | commit 12a550cf75e5b1347cf750ba35b440c9c9d049f8
491 | Author: twotwo <twotwo.li@gmail.com>
492 | Date:   Tue Dec 13 18:30:00 2016 +0800
493 | 
494 |     add README.md
495 | 
496 |     Change-Id: I55be6e211eaf80a72a5e6c363745a9a43c64d362
497 | (END)
498 | 
499 | 500 |
501 |
502 |

Presenter Notes

503 |
504 | 505 |
506 |
507 |
508 | 509 | 512 | 513 | 516 |
517 |
518 |
519 | 520 | 521 |
522 |
523 |
524 | 525 | 526 |

提交 Review

527 |
➜  hello_world git:(master) git push -u origin HEAD:refs/for/master
528 | Counting objects: 3, done.
529 | Delta compression using up to 8 threads.
530 | Compressing objects: 100% (2/2), done.
531 | Writing objects: 100% (3/3), 613 bytes | 0 bytes/s, done.
532 | Total 3 (delta 0), reused 0 (delta 0)
533 | remote: Processing changes: new: 1, refs: 1, done    
534 | remote: 
535 | remote: New Changes:
536 | remote:   http://172.16.100.90/gerrit/2 commit for review
537 | remote: 
538 | To ssh://172.16.100.90:29418/sandbox/hello_world
539 |  * [new branch]      HEAD -> refs/for/master
540 | 
541 | 542 |
543 |
544 |

Presenter Notes

545 |
546 | 547 |
548 |
549 |
550 | 551 | 554 | 555 | 558 |
559 |
560 |
561 | 562 | 563 |
564 |
565 |
566 | 567 |

4. 在 Web Console 中进行代码评审

568 | 569 | 570 |

Verify Changes

571 |

在 Web Console 的 My Reviews 的 Outgoing reviews 中,出现了这条提交信息

572 |

Review 这个版本与上个版本直接的不同

573 |

Diff Files one by one

574 |

Submit the Change

575 |

在 Web Console 中, Code Review 进行打分,累计得分足够后确认提交。

576 | 577 |
578 |
579 |

Presenter Notes

580 |
581 | 582 |
583 |
584 |
585 | 586 | 589 | 590 | 593 |
594 |
595 |
596 | 597 | 598 |
599 |
600 |
601 | 602 |

5. 学习效果自我评估

603 | 604 | 605 |

按照 2. Gerrit 初体验 中的“设置访问权限”指示,请先设置好自己的访问权限,然后 clone 沙箱工程到本地

606 |
➜  /tmp git clone ssh://liyan@172.16.100.130:29418/sandbox/hello_world && scp -p -P 29418 liyan@172.16.100.130:hooks/commit-msg hello_world/.git/hooks/
607 | 
608 |

609 |

⚠️请在这个项目里进行练习!

610 | 611 |
612 |
613 |

Presenter Notes

614 |
615 | 616 |
617 |
618 |
619 | 620 | 623 | 624 | 627 |
628 |
629 |
630 | 631 |
632 |
633 | 634 | 730 | 731 | 777 | 778 | 779 | -------------------------------------------------------------------------------- /slides/gerrit/gerrit.md: -------------------------------------------------------------------------------- 1 | # Gerrit 使用入门 2 | .notes: Generate HTML5 slideshows by landslide 3 | 4 | 5 | 6 | * V1.0 7 | * liyan 2017-11-15 8 | 9 | Back to [Git 协作开发上手指南](./index.html) 10 | 11 | ## 1. Gerrit 是什么 12 | ## 2. Gerrit 怎么用 13 | 14 | --- 15 | 16 | ## 1. Gerrit 简介 17 | 18 | Gerrit 是一个用 Java 写的开源软件: 19 | 20 | * 首先,Gerrit 提供了具备访问控制的 Git 版本管理服务; 21 | * 然后,Gerrit 又在 Git 基础上提供了web方式的代码评审功能; 22 | * 最有趣的一点,这个代码评审功能是可选的,团队可以决定一个项目是否需要加入评审逻辑 23 | 24 | --- 25 | 26 | ## 1. Gerrit 简介 27 | 28 | .notes: CI Builder, we use Jenkins / Reviewer, review process is optional 29 | 30 | ![Gerrit Overview](images/intro-quick-central-gerrit.png) 31 | 32 | --- 33 | 34 | ## 2. Gerrit 初体验 35 | 36 | 使用 Gerrit 的最简步骤 37 | 38 | * 设置访问权限 39 | * Submit a Change 40 | 41 | --- 42 | 43 | ## 2.1 设置访问权限 44 | 45 | 如果需要对代码进行修改或者对代码进行评审,就必须到 Web 控制台上进行设置 46 | 47 | ### 登录 Web 控制台 48 | [Gerrit Web Console](http://172.16.100.130/gerrit/) 已经集成了账号管理系统,大家可以使用自己的jira账号直接登录(Sign In) 49 | 50 | 点击Gerrit 页面右上角“Sign In”链接后,出现如下登录界面 51 | 52 | ![Gerrit Sign In](images/gerrit-sign-in.png) 53 | 54 | --- 55 | 56 | ## 配置 SSH(HTTP) 访问认证 57 | 58 | 进入[Settings](http://172.16.100.130/gerrit/#/settings/)界面 59 | 60 | ### SSH Public Keys 61 | 62 | ![Gerrit SSH Public Keys](images/gerrit-ssh-public-keys.png) 63 | 64 | 点"Add Key ..."按钮,把本机ssh密钥对中的公钥填入对话框。 65 | 66 | ☞ 进一步阅读: [Use Public Key Authentication with SSH](https://www.linode.com/docs/security/use-public-key-authentication-with-ssh) 67 | 68 | --- 69 | 70 | ## 用命令行直接访问 Gerrit 71 | 72 | 完成以上配置后,我们就可以与 Gerrit 进行对话了 73 | 74 | ➜ learn-git git:(master) ✗ ssh -p29418 liyan@172.16.100.90 75 | 76 | **** Welcome to Gerrit Code Review **** 77 | 78 | Hi liyan, you have successfully connected over SSH. 79 | 80 | Unfortunately, interactive shells are disabled. 81 | To clone a hosted Git repository, use: 82 | 83 | git clone ssh://liyan@172.16.100.90:29418/REPOSITORY_NAME.git 84 | 85 | Connection to 172.16.100.90 closed. 86 | 87 | --- 88 | 89 | ### HTTP Password 90 | 91 | ![Gerrit HTTP Password](images/gerrit-http-password.png) 92 | 93 | On Gerrit installations that do not support SSH authentication, the user must authenticate via HTTP/HTTPS. 94 | 95 | 参考[链接](https://gerrit-documentation.storage.googleapis.com/Documentation/2.13.3/user-upload.html#http) 96 | 97 | 设置之后,用户可以通过HTTP协议访问仓库 98 | 99 | git clone http://it.feiliu.com/gerrit/a/sandbox/hello_world 100 | 101 | HTTP Digest Authentication Removed at 2.14 102 | 103 | --- 104 | 105 | ## 执行一下 `gerrit` 命令 106 | 107 | 配置好 `SSH Public Keys` 之后,我们就可以使用 Gerrit 提供的服务了 108 | 109 | ssh -p29418 172.16.100.130 gerrit 110 | 111 | 可以看到这个命令的使用提示信息。为了减少输入,我设置了一个别名 112 | 113 | alias gerrit="ssh -p29418 172.16.100.90 gerrit" 114 | 115 | --- 116 | 117 | ## 2.2 Submit a Change 118 | 119 | 接下来咱们在沙箱工程中提交一个变更 120 | 121 | 使用 Gerrit 和 使用其它 Git 服务基本一样,其中存在的细微差别我会着重强调 122 | 123 | ### 拷贝 clone 命令 124 | 125 | ![Clone with commit-msg hook](images/gerrit-clone-with-commit-msg-hook.png) 126 | 127 | 在 Web Console 中 拷贝 clone 命令,注意 必须选择`Clone with commit-msg hook`方式! 128 | 129 | ➜ /tmp git clone ssh://liyan@172.16.100.130:29418/sandbox/hello_world && scp -p -P 29418 liyan@172.16.100.130:hooks/commit-msg hello_world/.git/hooks/ 130 | 131 | --- 132 | 133 | ## 2.2 Submit a Change 134 | ### 执行 clone 命令 135 | 136 | ![Execute Clone Command](images/clone-with-commit-msg-hook.png) 137 | 138 | ☞ 进一步阅读: Gerrit 在实现 Git 服务的基础上,添加了代码 review 的功能,因此在clone命令之后,还从服务器上又拷贝了一个 `commit-msg` 的钩子工具。 139 | 140 | --- 141 | 142 | ## 2.2 Submit a Change 143 | 144 | ### 提交本地 && 推送到 Gerrit 145 | 146 | ![Execute Clone Command](images/commit-and-push-to-gerrit.png) 147 | 148 | --- 149 | 150 | ## 3. Gerrit 进阶: Submit changes for review 151 | 152 | ☞ 检查 Change-Id , 这个增加的Change-Id字段是实现 Gerrit 代码评审功能所必须的。如果没有加入commit-msg钩子或不具备执行权限,将不生成此字段。这样commit的代码只能直接提交到主干,无法进行review 153 | 154 | ➜ hello_world git:(master) git log 155 | commit 72f756d6c5ee1a37ab965a18dc20a071ef8535b9 156 | Author: twotwo 157 | Date: Wed Feb 15 16:58:54 2017 +0800 158 | 159 | commit for review 160 | 161 | Change-Id: I648d97f05ef616d10e426d1ac6cff9ace07f39a8 162 | 163 | commit 12a550cf75e5b1347cf750ba35b440c9c9d049f8 164 | Author: twotwo 165 | Date: Tue Dec 13 18:30:00 2016 +0800 166 | 167 | add README.md 168 | 169 | Change-Id: I55be6e211eaf80a72a5e6c363745a9a43c64d362 170 | (END) 171 | 172 | --- 173 | 提交 Review 174 | 175 | ➜ hello_world git:(master) git push -u origin HEAD:refs/for/master 176 | Counting objects: 3, done. 177 | Delta compression using up to 8 threads. 178 | Compressing objects: 100% (2/2), done. 179 | Writing objects: 100% (3/3), 613 bytes | 0 bytes/s, done. 180 | Total 3 (delta 0), reused 0 (delta 0) 181 | remote: Processing changes: new: 1, refs: 1, done 182 | remote: 183 | remote: New Changes: 184 | remote: http://172.16.100.90/gerrit/2 commit for review 185 | remote: 186 | To ssh://172.16.100.90:29418/sandbox/hello_world 187 | * [new branch] HEAD -> refs/for/master 188 | 189 | --- 190 | 191 | ## 4. 在 Web Console 中进行代码评审 192 | 193 | ### Verify Changes 194 | 195 | 在 Web Console 的 [My Reviews](http://172.16.100.90/gerrit/#/dashboard/self) 的 Outgoing reviews 中,出现了这条提交信息 196 | 197 | Review 这个版本与上个版本直接的不同 198 | 199 | ![Diff Files one by one](images/gerrit-review.png) 200 | 201 | ### Submit the Change 202 | 203 | 在 Web Console 中, Code Review 进行打分,累计得分足够后确认提交。 204 | 205 | --- 206 | 207 | ## 5. 学习效果自我评估 208 | 209 | 按照 2. Gerrit 初体验 中的“设置访问权限”指示,请先设置好自己的访问权限,然后 `clone` 沙箱工程到本地 210 | 211 | ➜ /tmp git clone ssh://liyan@172.16.100.130:29418/sandbox/hello_world && scp -p -P 29418 liyan@172.16.100.130:hooks/commit-msg hello_world/.git/hooks/ 212 | 213 | ![]() 214 | 215 | ⚠️请在这个项目里进行练习! 216 | -------------------------------------------------------------------------------- /slides/gerrit/git_bash.md: -------------------------------------------------------------------------------- 1 | # 玩转 Git BASH 2 | .notes: Generate HTML5 slideshows by landslide 3 | 4 | 5 | 6 | * V1.0 7 | * liyan 2017-11-27 8 | 9 | ## 从前有个工程师,想用 git … 10 | 11 | 12 | 13 | ![QR Code](images/qr-git-bash.png) 14 | 15 | --- 16 | 17 | ## 小王的学习路径 18 | 19 | Back to [Git 协作开发上手指南](./index.html) 20 | 21 | * 在开发机上安装 git 环境: [玩转 Git Bash](./git-bash.html) 本篇文章 22 | * 使用 gerrit 进行协同开发 23 | * 在测试机上同步测试 24 | 25 | ## 本文内容包括 26 | 27 | ### 1. 安装 Git for Windows 需要注意的内容 28 | ### 2. 如何配置 SSH Public Key 访问外部的git服务器 29 | ### 3. 如何配置本地仓库 30 | ### 4. 在 Gerrit SandBox 练习 git 基本操作 31 | 32 | --- 33 | 34 | ## 关于 Git for Windows 35 | 36 | Git for Windows是Git官方提供的Windows版本,包括命令行和图形界面。 37 | 38 | [git-for-windows](https://git-for-windows.github.io/) 39 | 40 | ![示意图](https://git-for-windows.github.io/img/gw1web_thumb.png) 41 | 42 | --- 43 | 44 | ## 1. 安装 45 | *Git for Windows*是官方推荐的windows环境的Git工具集,包括Git GUI和Git Bash两部分。 46 | 47 | 在安装工程中,有以下一些选项比较重要 48 | 49 | --- 50 | 51 | ## 1.1 Adjusting your PATH environment 52 | 53 | 请选择 Use Git and optional Unix tools 54 | ![add-unix-tools-to-path](images/git-bash-add-unix-tools-to-path.png) 55 | 56 | --- 57 | ## 1.2 配置换行符为Unix风格 58 | .notes: Configuring the line ending conversions 59 | 60 | 请选择 checkout as-is, commit Unix-style line endings 61 | ![checkout-and-commit-unix-style](images/git-bash-checkout-and-commit-unix-style.png) 62 | 63 | --- 64 | ## 1.3 配置终端模拟器为wondow终端 65 | .notes: Configuring the terminal emulator to use the Git Bash 66 | 67 | 请选择 Use Windows' default console window 68 | ![use-windows-console](images/git-bash-use-windows-console.png) 69 | 70 | --- 71 | ## 1.4 Configuring extra options 72 | 73 | 请勾选以下内容 74 | 75 | * Enable file system caching 76 | * Enable Git Credential Manager 77 | ![extra-options](images/git-bash-extra-options.png) 78 | 79 | --- 80 | ## 2. 配置 SSH Public Key 81 | ### 2.1 生成SSH RSA 密钥对 82 | 83 | $ ssh-keygen -q -t rsa -C "testtest@feiliu.com" 84 | Enter file in which to save the key (/c/Users/liyan/.ssh/id_rsa): 85 | Enter passphrase (empty for no passphrase): 86 | Enter same passphrase again: 87 | 88 | liyan@hummer MINGW32 ~/.ssh 89 | $ ll ~/.ssh/ 90 | total 9 91 | -rw-r--r-- 1 liyan 197121 1679 Nov 24 17:10 id_rsa 92 | -rw-r--r-- 1 liyan 197121 401 Nov 24 17:10 id_rsa.pub 93 | -rw-r--r-- 1 liyan 197121 627 Jun 12 2014 known_hosts 94 | 95 | 96 | --- 97 | ## 2.2 获取RSA公钥 98 | 99 | $ cat ~/.ssh/id_rsa.pub 100 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDGIzTh6BKYWI1AjKeTvvsAQm/bxL6iJ9tFXiXnqjXx 101 | MGDIfno8JMi6CAX0IUC1tj7G1jNuQsmKi2E7P+Ql5SXHIJpboTk8UGqzVq62GOZTaZTV5oZ9TUgMyTv2 102 | n51jP0T7W9J+0R8g7zneQfFZtKZ0ryCDRCxweZNGFYWku96bBvBYDRiqG/3bbmAh3NRvWx5zKBv6ieat 103 | gjZpxNaVR0kwd2XX19rFTu4FFDE4RCuKQFlFUVM7uyhvDRWAsha87mitW2WD4xIKVcTzwOmBrGMPOKiv 104 | FfM3TzkUn0eTFOhJi0U6ynmH7uHAuVHhXWMGmbtWN9e8G8ba82beNPPtMugT testtest@feiliu.com 105 | 106 | --- 107 | ## 2.3 把公钥配置到远端服务 108 | ### 2.3.1 把公钥配置到github 109 | 110 | ![github-new-sshkey](images/github-new-sshkey.png) 111 | 112 | --- 113 | ## 2.3.2 把公钥配置到gerrit 114 | 115 | ![gerrit-add-sshkey](images/gerrit-add-sshkey.png) 116 | 117 | --- 118 | ## 2.4 访问测试 119 | ### 2.4.1 访问github账号 120 | 121 | $ ssh -T git@github.com 122 | Hi twotwo! You've successfully authenticated, but GitHub does not provide shell access. 123 | 124 | ### 2.4.1 访问gerrit账号 125 | 126 | $ ssh -p29418 -l testtest 172.16.100.130 gerrit version 127 | gerrit version 2.13.3 128 | 129 | --- 130 | 131 | ## 3. 配置本地仓库 132 | 有两种方式可以获得一个本地仓库,这里我们从gerrit沙箱中clone一个项目到本地 133 | 134 | * 在Gerrit页面上获取clone命名 135 | * 执行成功后设置本地仓库参数 136 | * 本地仓库提交到Gerrit 137 | --- 138 | 139 | ## 3.1 在Gerrit页面上获取clone命名 140 | 141 | ![gerrit-clone](images/gerrit-clone.png) 142 | 143 | --- 144 | 145 | ## 3.2 执行成功后设置本地仓库参数 146 | 147 | $ git config user.email 148 | liyan@feiliu.com 149 | 150 | $ git config user.email testtest@feiliu.com 151 | 152 | $ git config user.email 153 | testtest@feiliu.com 154 | 155 | --- 156 | 157 | ## 3.3 本地仓库提交到Gerrit 158 | 159 | $ git add testtest.md 160 | 161 | $ git commit -m 'my first commit' 162 | [master c4dfa72] my first commit 163 | 1 file changed, 1 insertion(+) 164 | create mode 100644 testtest.md 165 | 166 | $ git push origin master 167 | Counting objects: 3, done. 168 | Delta compression using up to 2 threads. 169 | Compressing objects: 100% (2/2), done. 170 | Writing objects: 100% (3/3), 312 bytes | 312.00 KiB/s, done. 171 | Total 3 (delta 1), reused 0 (delta 0) 172 | remote: Resolving deltas: 100% (1/1) 173 | remote: Processing changes: refs: 1, done 174 | To ssh://172.16.100.130:29418/sandbox/hello_world 175 | 134e713..c4dfa72 master -> master 176 | 177 | $ git log 178 | 179 | $ gitk #图形界面 180 | 181 | --- 182 | ## 4. git 基本操作 练习 183 | 184 | 向本地的另外一个目录中再次克隆上面的工程,练习获取远端的更新、解决冲突等操作 -------------------------------------------------------------------------------- /slides/gerrit/images/ci_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/slides/gerrit/images/ci_overview.png -------------------------------------------------------------------------------- /slides/gerrit/images/clone-with-commit-msg-hook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/slides/gerrit/images/clone-with-commit-msg-hook.png -------------------------------------------------------------------------------- /slides/gerrit/images/commit-and-push-to-gerrit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/slides/gerrit/images/commit-and-push-to-gerrit.png -------------------------------------------------------------------------------- /slides/gerrit/images/gerrit-add-sshkey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/slides/gerrit/images/gerrit-add-sshkey.png -------------------------------------------------------------------------------- /slides/gerrit/images/gerrit-clone-with-commit-msg-hook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/slides/gerrit/images/gerrit-clone-with-commit-msg-hook.png -------------------------------------------------------------------------------- /slides/gerrit/images/gerrit-clone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/slides/gerrit/images/gerrit-clone.png -------------------------------------------------------------------------------- /slides/gerrit/images/gerrit-http-password.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/slides/gerrit/images/gerrit-http-password.png -------------------------------------------------------------------------------- /slides/gerrit/images/gerrit-review.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/slides/gerrit/images/gerrit-review.png -------------------------------------------------------------------------------- /slides/gerrit/images/gerrit-sign-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/slides/gerrit/images/gerrit-sign-in.png -------------------------------------------------------------------------------- /slides/gerrit/images/gerrit-ssh-public-keys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/slides/gerrit/images/gerrit-ssh-public-keys.png -------------------------------------------------------------------------------- /slides/gerrit/images/git-bash-add-unix-tools-to-path.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/slides/gerrit/images/git-bash-add-unix-tools-to-path.png -------------------------------------------------------------------------------- /slides/gerrit/images/git-bash-checkout-and-commit-unix-style.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/slides/gerrit/images/git-bash-checkout-and-commit-unix-style.png -------------------------------------------------------------------------------- /slides/gerrit/images/git-bash-extra-options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/slides/gerrit/images/git-bash-extra-options.png -------------------------------------------------------------------------------- /slides/gerrit/images/git-bash-use-windows-console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/slides/gerrit/images/git-bash-use-windows-console.png -------------------------------------------------------------------------------- /slides/gerrit/images/git-jenkins-process.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/slides/gerrit/images/git-jenkins-process.png -------------------------------------------------------------------------------- /slides/gerrit/images/github-new-sshkey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/slides/gerrit/images/github-new-sshkey.png -------------------------------------------------------------------------------- /slides/gerrit/images/intro-quick-central-gerrit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/slides/gerrit/images/intro-quick-central-gerrit.png -------------------------------------------------------------------------------- /slides/gerrit/images/qr-code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/slides/gerrit/images/qr-code.png -------------------------------------------------------------------------------- /slides/gerrit/images/qr-git-bash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/slides/gerrit/images/qr-git-bash.png -------------------------------------------------------------------------------- /slides/gerrit/index.html: -------------------------------------------------------------------------------- 1 | 2 | 24 | 25 | 26 | 27 | 28 | 软件开发的工作方法 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 |
48 |
49 |
50 |
51 | 52 | 53 |
54 |
55 |
56 | 57 |

软件开发的工作方法

58 | 59 | 60 |

Generate HTML5 slideshows by landslide

61 | 62 | 63 |
    64 |
  • V1.0
  • 65 |
  • liyan 2017-11-15
  • 66 |
67 |

从前,有一枚野生的程序猿,加入了一支攻城狮团队 …

68 | 69 | 70 |

QR Code

71 | 72 |
73 |
74 |

Presenter Notes

75 |
76 | 77 |
78 |
79 |
80 | 81 | 84 | 85 | 88 |
89 |
90 |
91 | 92 | 93 |
94 |
95 |
96 | 97 |

怎样快速成为一只合格的攻城狮呢?

98 | 99 | 100 |

要摆脱野生状态,小白需要掌握必要的工作方法

101 |
    102 |
  • 软件开发的工作方法 //本篇文章侧重点为持续集成
      103 |
    • 持续集成
    • 104 |
    • 代码评审
    • 105 |
    • 工作日志
    • 106 |
    107 |
  • 108 |
  • 学习如何在Windows下使用 Git Bash
  • 109 |
  • 学习更多 gerrit 进行协作开发的知识 Gerrit 使用入门
  • 110 |
  • 更多的学习与练习
      111 |
    • 工作日志与例会
    • 112 |
    • JIRA、WIKI的使用
    • 113 |
    • 正式项目第一步: 搭建测试环境
    • 114 |
    115 |
  • 116 |
117 | 118 |
119 |
120 |

Presenter Notes

121 |
122 | 123 |
124 |
125 |
126 | 127 | 130 | 131 | 134 |
135 |
136 |
137 | 138 | 139 |
140 |
141 |
142 | 143 |

本文主要内容

144 | 145 | 146 |

1. 持续集成简介

147 |

2. 环境与流程

148 |

3. Gerrit与代码评审

149 |

4. 持续集成实践

150 | 157 |

5. 总结

158 | 159 |
160 |
161 |

Presenter Notes

162 |
163 | 164 |
165 |
166 |
167 | 168 | 171 | 172 | 175 |
176 |
177 |
178 | 179 | 180 |
181 |
182 |
183 | 184 |

1. 持续集成简介

185 | 186 | 187 |
    188 |
  • WHAT
      189 |
    • CI 是一种软件工程流程。最早是指频繁地(一天多次)把所有工程师的协作工作结果集成到共用主线(mainline)上
    • 190 |
    • CI 会搭配自动单元测试,能使缺陷更容易被发现和改正
    • 191 |
    • 现代CI服务器处理UT,往往还会包括持续交付和持续部署
    • 192 |
    193 |
  • 194 |
  • WHY
      195 |
    • 快速发现错误
    • 196 |
    • 降低集成成本
    • 197 |
    • 提高可靠性
    • 198 |
    199 |
  • 200 |
  • HOW
      201 |
    • 提交
    • 202 |
    • 构建: 编译&测试&打包
    • 203 |
    • 部署
    • 204 |
    205 |
  • 206 |
207 | 208 |
209 |
210 |

Presenter Notes

211 |
212 | 213 |
214 |
215 |
216 | 217 | 220 | 221 | 224 |
225 |
226 |
227 | 228 | 229 |
230 |
231 |
232 | 233 |

2. 环境与流程

234 | 235 | 236 |

2.1 支持持续集成的开发环境

237 |
    238 |
  • 开发机: 攻城狮的生产工具,一般都还是安装的Windows系统
  • 239 |
  • 测试服务器: 若干安装CentOS的服务器,用来测试服务,团队范围内公用
  • 240 |
  • 生产服务器: 也叫业务服务器,操作要谨慎!
  • 241 |
  • Jenkins: 团队的CI服务器,用来支持持续集成流程;
  • 242 |
  • Gerrit: 团队代码仓库和代码评审平台;
  • 243 |
244 |

CI Overview

245 | 246 |
247 |
248 |

Presenter Notes

249 |
250 | 251 |
252 |
253 |
254 | 255 | 258 | 259 | 262 |
263 |
264 |
265 | 266 | 267 |
268 |
269 |
270 | 271 |

2.2 开发流程: 从开发到发布

272 | 273 | 274 |

Jenkins Process

275 | 276 |
277 |
278 |

Presenter Notes

279 |
280 | 281 |
282 |
283 |
284 | 285 | 288 | 289 | 292 |
293 |
294 |
295 | 296 | 297 |
298 |
299 |
300 | 301 |

3. Gerrit与代码评审

302 | 303 | 304 |
    305 |
  • Gerrit Web 控制台
  • 306 |
  • 代码评审
  • 307 |
308 | 309 |
310 |
311 |

Presenter Notes

312 |
313 | 314 |
315 |
316 |
317 | 318 | 321 | 322 | 325 |
326 |
327 |
328 | 329 | 330 |
331 |
332 |
333 | 334 |

3.1 Gerrit Web 控制台

335 | 336 | 337 |

登录 Web 控制台

338 |

Gerrit Web Console 已经集成了账号管理系统,大家可以使用自己的jira账号直接登录(Sign In)

339 |

点击Gerrit 页面右上角“Sign In”链接后,出现如下登录界面

340 |

Gerrit Sign In

341 | 342 |
343 |
344 |

Presenter Notes

345 |
346 | 347 |
348 |
349 |
350 | 351 | 354 | 355 | 358 |
359 |
360 |
361 | 362 | 363 |
364 |
365 |
366 | 367 |

4. 持续集成实践

368 | 369 | 370 |

示例

371 |
    372 |
  • Node.js项目部署参考
      373 |
    • https://blog.hobbytrace.com/use-jenkins-to-automatically-deploy-node-js-apps/
    • 374 |
    • https://codeforgeek.com/2016/04/continuous-integration-deployment-jenkins-node-js/
    • 375 |
    • https://blog.couchbase.com/create-continuous-deployment-pipeline-nodejs-jenkins/
    • 376 |
    377 |
  • 378 |
379 | 380 |
381 |
382 |

Presenter Notes

383 |
384 | 385 |
386 |
387 |
388 | 389 | 392 | 393 | 396 |
397 |
398 |
399 | 400 | 401 |
402 |
403 |
404 | 405 |

5. 总结

406 | 407 | 408 |

参考内容

409 |
413 | 414 |
415 |
416 |

Presenter Notes

417 |
418 | 419 |
420 |
421 |
422 | 423 | 426 | 427 | 430 |
431 |
432 |
433 | 434 |
435 |
436 | 437 | 497 | 498 | 544 | 545 | 546 | -------------------------------------------------------------------------------- /slides/gerrit/index.md: -------------------------------------------------------------------------------- 1 | # 软件开发的工作方法 2 | .notes: Generate HTML5 slideshows by landslide 3 | 4 | 5 | 6 | * V1.0 7 | * liyan 2017-11-15 8 | 9 | ## 从前,有一枚野生的程序猿,加入了一支攻城狮团队 … 10 | 11 | 12 | ![QR Code](images/qr-code.png) 13 | 14 | --- 15 | 16 | ## 怎样快速成为一只合格的攻城狮呢? 17 | .notes: 要摆脱野生状态,小白需要掌握必要的工作方法 18 | 19 | * 软件开发的工作方法 //本篇文章侧重点为持续集成 20 | - 持续集成 21 | - 代码评审 22 | - 工作日志 23 | * 学习如何在Windows下[使用 Git Bash](./git-bash.html) 24 | * 学习更多 gerrit 进行协作开发的知识 [Gerrit 使用入门](./gerrit.html) 25 | * 更多的学习与练习 26 | - 工作日志与例会 27 | - JIRA、WIKI的使用 28 | - 正式项目第一步: 搭建测试环境 29 | 30 | 31 | --- 32 | 33 | ## 本文主要内容 34 | 35 | ### 1. 持续集成简介 36 | 37 | ### 2. 环境与流程 38 | 39 | ### 3. Gerrit与代码评审 40 | 41 | ### 4. 持续集成实践 42 | * Android项目: [渠道适配SDK](http://172.16.100.50/jenkins/job/sdk-agent-android/) 43 | * iOS项目: [sdk v4](http://172.16.100.65/job/sdk_v4_ios/) 44 | * Java项目: [渠道适配服务](http://172.16.100.50/jenkins/job/sdk-agent/) 45 | * Node.js项目: 46 | * PHP项目 47 | ### 5. 总结 48 | 49 | 50 | --- 51 | 52 | ## 1. 持续集成简介 53 | 54 | * WHAT 55 | - CI 是一种软件工程流程。最早是指频繁地(一天多次)把所有工程师的协作工作结果集成到共用主线(mainline)上 56 | - CI 会搭配自动单元测试,能使缺陷更容易被发现和改正 57 | - 现代CI服务器处理UT,往往还会包括持续交付和持续部署 58 | * WHY 59 | - 快速发现错误 60 | - 降低集成成本 61 | - 提高可靠性 62 | * HOW 63 | - 提交 64 | - 构建: 编译&测试&打包 65 | - 部署 66 | 67 | --- 68 | 69 | ## 2. 环境与流程 70 | 71 | 2.1 支持持续集成的开发环境 72 | 73 | * 开发机: 攻城狮的生产工具,一般都还是安装的Windows系统 74 | * 测试服务器: 若干安装CentOS的服务器,用来测试服务,团队范围内公用 75 | * 生产服务器: 也叫业务服务器,操作要谨慎! 76 | * Jenkins: 团队的CI服务器,用来支持持续集成流程; 77 | * Gerrit: 团队代码仓库和代码评审平台; 78 | 79 | ![CI Overview](images/ci_overview.png) 80 | 81 | --- 82 | 83 | ## 2.2 开发流程: 从开发到发布 84 | 85 | ![Jenkins Process](images/git-jenkins-process.png) 86 | 87 | --- 88 | 89 | ## 3. Gerrit与代码评审 90 | 91 | * Gerrit Web 控制台 92 | * 代码评审 93 | 94 | --- 95 | 96 | ## 3.1 Gerrit Web 控制台 97 | 98 | ### 登录 Web 控制台 99 | [Gerrit Web Console](http://172.16.100.130/gerrit/) 已经集成了账号管理系统,大家可以使用自己的jira账号直接登录(Sign In) 100 | 101 | 点击Gerrit 页面右上角“Sign In”链接后,出现如下登录界面 102 | 103 | ![Gerrit Sign In](images/gerrit-sign-in.png) 104 | 105 | --- 106 | 107 | ## 4. 持续集成实践 108 | 109 | 示例 110 | 111 | * Node.js项目部署参考 112 | - https://blog.hobbytrace.com/use-jenkins-to-automatically-deploy-node-js-apps/ 113 | - https://codeforgeek.com/2016/04/continuous-integration-deployment-jenkins-node-js/ 114 | - https://blog.couchbase.com/create-continuous-deployment-pipeline-nodejs-jenkins/ 115 | 116 | --- 117 | 118 | ## 5. 总结 119 | 120 | 参考内容 121 | 122 | * [Continuous Integration](http://wiki.li3huo.com/Continuous_Integration) 123 | * [Continuous Integration and deployment with Jenkins and Node.js](https://codeforgeek.com/2016/04/continuous-integration-deployment-jenkins-node-js/) 124 | 125 | -------------------------------------------------------------------------------- /slides/gerrit/theme/base.html: -------------------------------------------------------------------------------- 1 | 2 | 24 | 25 | 26 | 27 | 28 | {{ head_title }} 29 | 30 | {% if embed %} 31 | 34 | 37 | {% else %} 38 | 39 | 40 | {% endif %} 41 | {% for css in user_css %} 42 | {% if embed %} 43 | 46 | {% else %} 47 | 48 | {% endif %} 49 | {% endfor %} 50 | 51 | 52 | {% if embed %} 53 | 56 | {% else %} 57 | 58 | {% endif %} 59 | {% for js in user_js %} 60 | {% if embed %} 61 | 64 | {% else %} 65 | 66 | {% endif %} 67 | {% endfor %} 68 | {% if math_output %} 69 | 74 | 75 | {% endif %} 76 | 77 | 78 | 79 |
80 |
81 |
82 |
83 |
84 |
85 | {% for slide in slides %} 86 | 87 |
88 |
89 |
90 | {% if slide.header %} 91 |
{{ slide.header }}
92 | {% endif %} 93 | {% if slide.content %} 94 |
{{ slide.content }}
95 | {% endif %} 96 |
97 |
98 |

Presenter Notes

99 |
100 | {% if slide.presenter_notes %} 101 | {{ slide.presenter_notes }} 102 | {% endif %} 103 |
104 |
105 |
106 | {% if slide.source %} 107 | 110 | {% endif %} 111 | 114 |
115 |
116 |
117 | {% endfor %} 118 |
119 |
120 | {% if toc %} 121 | 141 | {% endif %} 142 | 188 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /slides/gerrit/theme/css/print.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | @page { 7 | size: landscape; 8 | } 9 | 10 | body { 11 | font: 100% "Lucida Grande", "Trebuchet MS", Verdana, sans-serif; 12 | padding: 0; 13 | margin: 0; 14 | } 15 | 16 | div.slide { 17 | min-width: 800px; 18 | min-height: 600px; 19 | padding: 1em; 20 | overflow: hidden; 21 | page-break-after: always; 22 | border: 1px solid black; 23 | border-radius: 20px; 24 | } 25 | 26 | div.slide div.inner { 27 | width: 800px; 28 | height: 600px; 29 | margin: auto; 30 | } 31 | 32 | h1 { 33 | font-size: 2.4em; 34 | } 35 | 36 | h2 { 37 | font-size: 1.4em; 38 | } 39 | 40 | h3 { 41 | margin: 1em 0; 42 | } 43 | 44 | ul { 45 | margin: 0; 46 | padding: 0; 47 | } 48 | 49 | p, li, pre { 50 | margin: 1em 0; 51 | } 52 | 53 | li { 54 | margin-left: 2em; 55 | } 56 | 57 | a { 58 | color: #000000; 59 | } 60 | 61 | pre, code { 62 | max-width: 800px; 63 | background: #eee; 64 | font-family: Monaco, monospace; 65 | font-size: 90%; 66 | } 67 | 68 | pre { 69 | padding: .2em .5em; 70 | overflow: hidden; 71 | border-radius: .8em; 72 | } 73 | 74 | code { 75 | padding: 0 .2em; 76 | } 77 | 78 | .slide header:only-child h1 { 79 | line-height: 180%; 80 | text-align: center; 81 | display: table-cell; 82 | vertical-align: middle; 83 | height: 600px; 84 | width: 800px; 85 | font-size: 48px; 86 | margin-top:100px; 87 | margin-bottom:100px; 88 | } 89 | 90 | #toc, #help, .slide aside, .slide footer, .slide .notes, 91 | .presenter_notes, #current_presenter_notes, #presenter_note { 92 | display: none; 93 | } 94 | -------------------------------------------------------------------------------- /slides/gerrit/theme/css/screen.css: -------------------------------------------------------------------------------- 1 | body { 2 | font: 14px "Lucida Grande", "Trebuchet MS", Verdana, sans-serif; 3 | background: #778; 4 | padding: 0; 5 | margin: 0; 6 | overflow: hidden; 7 | } 8 | 9 | div.presentation { 10 | position: absolute; 11 | width: 100%; 12 | display: table-cell; 13 | vertical-align: middle; 14 | height: 100%; 15 | background: inherit; 16 | } 17 | 18 | div.presentation > h1 { 19 | display: none; 20 | } 21 | 22 | div.slides, body.expose div.slides.nocontext { 23 | width: 100%; 24 | height: 100%; 25 | left: 0; 26 | top: 0; 27 | position: absolute; 28 | display: block; 29 | } 30 | 31 | div.slides.nocontext { 32 | width: 900px; 33 | margin: 0 auto; 34 | overflow: hidden; 35 | position: relative; 36 | left: auto; 37 | top: auto; 38 | } 39 | 40 | div.slide { 41 | display: inline; 42 | position: absolute; 43 | overflow: hidden; 44 | width: 900px; 45 | height: 700px; 46 | margin-top: -350px; 47 | margin-left: -400px; 48 | left: 50%; 49 | top: 50%; 50 | background: -webkit-gradient(linear, left bottom, left top, from(#bbd), to(#fff)); 51 | background-color: #eee; 52 | background: -moz-linear-gradient(bottom, #bbd, #fff); 53 | -webkit-transition: margin 0.25s ease-in-out; 54 | -moz-transition: margin 0.25s ease-in-out; 55 | -o-transition: margin 0.25s ease-in-out; 56 | border-top-left-radius: 20px; 57 | -moz-border-radius-topleft: 20px; 58 | -webkit-border-top-left-radius: 20px; 59 | border-top-right-radius: 20px; 60 | -moz-border-radius-topright: 20px; 61 | -webkit-border-top-right-radius: 20px; 62 | border-bottom-right-radius: 20px; 63 | -moz-border-radius-bottomright: 20px; 64 | -webkit-border-bottom-right-radius: 20px; 65 | border-bottom-left-radius: 20px; 66 | -moz-border-radius-bottomleft: 20px; 67 | -webkit-border-bottom-left-radius: 20px; 68 | } 69 | 70 | /* Expose */ 71 | 72 | body.expose div.slides { 73 | float: left; 74 | position: relative; 75 | overflow: auto; 76 | margin-bottom: 10px; 77 | } 78 | 79 | body.expose div.slide { 80 | display: block; 81 | float: left; 82 | position: relative; 83 | left: auto !important; 84 | top: auto !important; 85 | margin: 10px !important; 86 | -webkit-transition: none; 87 | -moz-transition: none; 88 | -o-transition: none; 89 | -moz-transform: scale(.33, .33); 90 | -moz-transform-origin: 0 0; 91 | -webkit-transform: scale(.33, .33); 92 | -webkit-transform-origin: 0 0; 93 | -o-transform: scale(.33, .33); 94 | -o-transform-origin: 0 0; 95 | -webkit-transition: none; 96 | -moz-transition: none; 97 | -o-transition: none; 98 | cursor: pointer; 99 | } 100 | 101 | body.expose div.slide:hover { 102 | background: -webkit-gradient(linear, left bottom, left top, from(#bdd), to(#fff)); 103 | background-color: #eee; 104 | background: -moz-linear-gradient(bottom, #bdd, #fff); 105 | } 106 | 107 | body.expose .slide-wrapper { 108 | float: left; 109 | position: relative; 110 | margin: .5%; 111 | width: 300px; 112 | height: 233px; 113 | } 114 | 115 | body.expose .slide footer { 116 | } 117 | 118 | body.expose .slide .inner { 119 | } 120 | 121 | body.expose .slide.far-past, 122 | body.expose .slide.past, 123 | body.expose .slide.future, 124 | body.expose .slide.far-future { 125 | margin-left: 0; 126 | } 127 | 128 | body.expose .slide.current { 129 | background: -webkit-gradient(linear, left bottom, left top, from(#ddb), to(#fff)); 130 | background-color: #eee; 131 | background: -moz-linear-gradient(bottom, #ddb, #fff); 132 | border: 16px solid #fff; 133 | -moz-transform: scale(.315, .315); 134 | -moz-transform-origin: 0 0; 135 | -webkit-transform: scale(.315, .315); 136 | -webkit-transform-origin: 0 0; 137 | -o-transform: scale(.315, .315); 138 | -o-transform-origin: 0 0; 139 | } 140 | 141 | /* Presenter Mode */ 142 | 143 | body.presenter_view div.slide { 144 | display: inline; 145 | position: absolute; 146 | overflow: hidden; 147 | -moz-transform: scale(.5, .5); 148 | -moz-transform-origin: 0 0; 149 | -webkit-transform: scale(.5, .5); 150 | -webkit-transform-origin: 0 0; 151 | -o-transform: scale(.5, .5); 152 | -o-transform-origin: 0 0; 153 | margin-top: -300px; 154 | } 155 | 156 | body.presenter_view .slide.far-past { 157 | display: block; 158 | margin-left: -1500px; 159 | } 160 | 161 | body.presenter_view .slide.past { 162 | display: block; 163 | margin-left: -975px; 164 | } 165 | 166 | body.presenter_view .slide.current { 167 | display: block; 168 | margin-left: -475px; 169 | border: 8px solid maroon; 170 | z-index: 2; 171 | } 172 | 173 | body.presenter_view .slide.future { 174 | display: block; 175 | margin-left: 25px; 176 | z-index: 1; 177 | } 178 | 179 | body.presenter_view .slide.far-future { 180 | display: block; 181 | margin-left: 525px; 182 | } 183 | 184 | body.presenter_view div#current_presenter_notes { 185 | visibility: visible; 186 | display: block; 187 | position: absolute; 188 | overflow: auto; 189 | vertical-align: middle; 190 | left: 50%; 191 | top: 50%; 192 | margin-left: -475px; 193 | margin-top: 100px; 194 | z-index: 2; 195 | width: 950px; 196 | border-style: solid; 197 | height: 30%; 198 | background-color: silver; 199 | } 200 | 201 | body.presenter_view div#current_presenter_notes section { 202 | font-family: "Lucida Grande", "Trebuchet MS", Verdana, sans-serif; 203 | color: black; 204 | text-shadow: rgba(0, 0, 0, 0.2) 0 2px 5px; 205 | display: block; 206 | overflow: visible; 207 | position: relative; 208 | background-color: #fffeff; 209 | height: 120px; 210 | margin-right: 30px; 211 | margin-top: 60px; 212 | margin-left: 30px; 213 | padding-right: 10px; 214 | padding-left: 10px; 215 | padding-top: 10px; 216 | } 217 | 218 | body.presenter_view div#current_presenter_notes section p { 219 | margin: 0; 220 | } 221 | 222 | body.presenter_view div#current_presenter_notes h1 { 223 | font-size: 50%; 224 | display: block; 225 | } 226 | 227 | div#current_presenter_notes { 228 | display: none; 229 | } 230 | 231 | div.slide div.presenter_notes, div.slides div.presenter_notes { 232 | display: none; 233 | } 234 | 235 | /* Slide styles */ 236 | 237 | div.slide p { 238 | font-size: 20px; 239 | } 240 | 241 | .slide.far-past { 242 | display: block; 243 | margin-left: -2400px; 244 | } 245 | 246 | .slide.past { 247 | display: block; 248 | margin-left: -1400px; 249 | } 250 | 251 | .slide.current { 252 | display: block; 253 | margin-left: -450px; 254 | } 255 | 256 | .slide.future { 257 | display: block; 258 | margin-left: 500px; 259 | } 260 | 261 | .slide.far-future { 262 | display: block; 263 | margin-left: 1500px; 264 | } 265 | 266 | body.three-d div.slides { 267 | -webkit-transform: translateX(50px) scale(0.8) rotateY(10deg); 268 | -moz-transform: translateX(50px) scale(0.8) rotateY(10deg); 269 | -o-transform: translateX(50px) scale(0.8) rotateY(10deg); 270 | } 271 | 272 | 273 | /* Content */ 274 | 275 | header:not(:only-child) { 276 | font-family: "Lucida Grande", "Trebuchet MS", Verdana, sans-serif; 277 | font-weight: normal; 278 | font-size: 50px; 279 | letter-spacing: -.05em; 280 | color: black; 281 | text-shadow: rgba(0, 0, 0, 0.2) 0 2px 5px; 282 | position: absolute; 283 | left: 30px; 284 | top: 25px; 285 | margin: 0; 286 | padding: 0; 287 | } 288 | 289 | header h1, header h2, header h3, header h4, header h5, header h6 { 290 | display: inline; 291 | font-size: 100%; 292 | font-weight: normal; 293 | padding: 0; 294 | margin: 0; 295 | } 296 | 297 | header h2:first-child { 298 | margin-top: 0; 299 | } 300 | 301 | section, .slide header:only-child h1 { 302 | font-family: "Lucida Grande", "Trebuchet MS", Verdana, sans-serif; 303 | color: #3f3f3f; 304 | text-shadow: rgba(0, 0, 0, 0.2) 0 2px 5px; 305 | margin-left: 30px; 306 | margin-right: 30px; 307 | margin-top: 100px; 308 | display: block; 309 | overflow: hidden; 310 | } 311 | 312 | img { display: block; margin: auto; } 313 | 314 | section img.align-center { 315 | display: block; 316 | margin-left: auto; 317 | margin-right: auto; 318 | } 319 | 320 | section img.align-right { 321 | display: block; 322 | margin-left: auto; 323 | margin-right: 0; 324 | } 325 | 326 | section img.align-left { 327 | display: block; 328 | margin-right: auto; 329 | margin-left: 0; 330 | } 331 | 332 | a { 333 | color: inherit; 334 | display: inline-block; 335 | text-decoration: none; 336 | line-height: 110%; 337 | border-bottom: 2px solid #3f3f3f; 338 | } 339 | 340 | pre, code, tt { 341 | font-family: Monaco, Consolas, 'Bitstream Vera Sans Mono', 'Lucida Console', FreeMono, Courier, monospace; 342 | } 343 | 344 | pre, .gist .gist-file .gist-data { 345 | font-size: 18px; 346 | max-height: 485px; 347 | padding-top: 0.25em !important; 348 | padding-right: 0.5em !important; 349 | padding-left: 0.5em !important; 350 | overflow: auto; 351 | } 352 | 353 | /* render a nice scrollbar in overflowed pre area's */ 354 | ::-webkit-scrollbar { 355 | height: 8px; 356 | width: 8px; 357 | } 358 | ::-webkit-scrollbar-thumb { 359 | background: -webkit-gradient(linear, left bottom, left top, from(#777), to(#bbd)); 360 | -webkit-border-radius: 1ex; 361 | } 362 | ::-webkit-scrollbar-corner { 363 | background: #dedede; 364 | } 365 | 366 | blockquote { 367 | border-left: solid 8px #778; 368 | padding: .1ex 1ex; 369 | font-style: italic; 370 | font-size: 20px; 371 | } 372 | 373 | li { 374 | padding: 10px 0; 375 | font-size: 20px; 376 | } 377 | 378 | li pre { margin-left: 0em; } 379 | 380 | .slide header:only-child h1 { 381 | line-height: 180%; 382 | text-align: center; 383 | display: table-cell; 384 | vertical-align: middle; 385 | height: 700px; 386 | width: 900px; 387 | font-size: 50px; 388 | margin-top:100px; 389 | margin-bottom:100px; 390 | } 391 | 392 | .sidebar { 393 | clear: both; 394 | background: -webkit-gradient(linear, top right, bottom right, from(#dde), to(#fff)); 395 | -webkit-transition: margin 0.25s ease-in-out; 396 | background-color: #eee; 397 | background: -moz-linear-gradient(right, #dde, #fff); 398 | border-right: 5px solid #ccd; 399 | z-index: 9999999; 400 | height: 100%; 401 | overflow: hidden; 402 | top: 0; 403 | position: absolute; 404 | display: block; 405 | margin: 0; 406 | margin-left: -400px; 407 | padding: 10px 16px; 408 | overflow: auto; 409 | -webkit-transition: margin 0.2s ease-in-out; 410 | -moz-transition: margin 0.2s ease-in-out; 411 | -o-transition: margin 0.2s ease-in-out; 412 | } 413 | 414 | .sidebar h2 { 415 | text-shadow: rgba(0, 0, 0, 0.2) 0 2px 5px; 416 | margin: 0 0 16px; 417 | padding: 0; 418 | } 419 | 420 | .sidebar table { 421 | width: 100%; 422 | margin: 0; 423 | padding: 0; 424 | border-collapse: collapse; 425 | } 426 | 427 | .sidebar table caption { 428 | display: none; 429 | } 430 | 431 | .sidebar tr { 432 | margin: 2px 0; 433 | border-bottom: 1px solid #ccc; 434 | } 435 | 436 | .sidebar th { 437 | text-align: left; 438 | font-weight: normal; 439 | max-width: 300px; 440 | overflow: hidden; 441 | } 442 | 443 | .sidebar tr.sub th { 444 | text-indent: 20px; 445 | } 446 | 447 | .sidebar td { 448 | text-align: right; 449 | min-width: 20px; 450 | } 451 | 452 | .sidebar a { 453 | display: block; 454 | text-decoration: none; 455 | border-bottom: none; 456 | padding: 4px 0; 457 | } 458 | 459 | .sidebar tr.active { 460 | background: #ff0; 461 | } 462 | 463 | aside { 464 | display: none; 465 | } 466 | aside.source { 467 | position: absolute; 468 | bottom: 6px; 469 | left: 10px; 470 | text-indent: 10px; 471 | } 472 | aside.page_number { 473 | position: absolute; 474 | bottom: 6px; 475 | right: 10px; 476 | text-indent: 10px; 477 | } 478 | 479 | .notes { 480 | display: none; 481 | padding: 10px; 482 | background: #ccc; 483 | border-radius: 10px; 484 | -moz-border-radius: 10px; 485 | -webkit-border-radius: 10px; 486 | } 487 | div.slide p.notes { 488 | font-size: 90%; 489 | } 490 | 491 | /* Pygments default theme */ 492 | .hll { background-color: #ffffcc } 493 | .c { color: #408080; font-style: italic } /* Comment */ 494 | .err { border: 1px solid #FF0000 } /* Error */ 495 | .k { color: #008000; font-weight: bold } /* Keyword */ 496 | .o { color: #666666 } /* Operator */ 497 | .cm { color: #408080; font-style: italic } /* Comment.Multiline */ 498 | .cp { color: #BC7A00 } /* Comment.Preproc */ 499 | .c1 { color: #408080; font-style: italic } /* Comment.Single */ 500 | .cs { color: #408080; font-style: italic } /* Comment.Special */ 501 | .gd { color: #A00000 } /* Generic.Deleted */ 502 | .ge { font-style: italic } /* Generic.Emph */ 503 | .gr { color: #FF0000 } /* Generic.Error */ 504 | .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 505 | .gi { color: #00A000 } /* Generic.Inserted */ 506 | .go { color: #808080 } /* Generic.Output */ 507 | .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ 508 | .gs { font-weight: bold } /* Generic.Strong */ 509 | .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 510 | .gt { color: #0040D0 } /* Generic.Traceback */ 511 | .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ 512 | .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ 513 | .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ 514 | .kp { color: #008000 } /* Keyword.Pseudo */ 515 | .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ 516 | .kt { color: #B00040 } /* Keyword.Type */ 517 | .m { color: #666666 } /* Literal.Number */ 518 | .s { color: #BA2121 } /* Literal.String */ 519 | .na { color: #7D9029 } /* Name.Attribute */ 520 | .nb { color: #008000 } /* Name.Builtin */ 521 | .nc { color: #0000FF; font-weight: bold } /* Name.Class */ 522 | .no { color: #880000 } /* Name.Constant */ 523 | .nd { color: #AA22FF } /* Name.Decorator */ 524 | .ni { color: #999999; font-weight: bold } /* Name.Entity */ 525 | .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ 526 | .nf { color: #0000FF } /* Name.Function */ 527 | .nl { color: #A0A000 } /* Name.Label */ 528 | .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ 529 | .nt { color: #008000; font-weight: bold } /* Name.Tag */ 530 | .nv { color: #19177C } /* Name.Variable */ 531 | .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ 532 | .w { color: #bbbbbb } /* Text.Whitespace */ 533 | .mf { color: #666666 } /* Literal.Number.Float */ 534 | .mh { color: #666666 } /* Literal.Number.Hex */ 535 | .mi { color: #666666 } /* Literal.Number.Integer */ 536 | .mo { color: #666666 } /* Literal.Number.Oct */ 537 | .sb { color: #BA2121 } /* Literal.String.Backtick */ 538 | .sc { color: #BA2121 } /* Literal.String.Char */ 539 | .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ 540 | .s2 { color: #BA2121 } /* Literal.String.Double */ 541 | .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ 542 | .sh { color: #BA2121 } /* Literal.String.Heredoc */ 543 | .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ 544 | .sx { color: #008000 } /* Literal.String.Other */ 545 | .sr { color: #BB6688 } /* Literal.String.Regex */ 546 | .s1 { color: #BA2121 } /* Literal.String.Single */ 547 | .ss { color: #19177C } /* Literal.String.Symbol */ 548 | .bp { color: #008000 } /* Name.Builtin.Pseudo */ 549 | .vc { color: #19177C } /* Name.Variable.Class */ 550 | .vg { color: #19177C } /* Name.Variable.Global */ 551 | .vi { color: #19177C } /* Name.Variable.Instance */ 552 | .il { color: #666666 } /* Literal.Number.Integer.Long */ 553 | .lineno { padding-right: 10px } /* A few space after linenos */ 554 | 555 | #blank { 556 | position: absolute; 557 | top: 0; 558 | left: 0; 559 | background-color: black; 560 | width: 100%; 561 | height: 100%; 562 | z-index: 64; 563 | display: none; 564 | } 565 | 566 | .image-reference { 567 | display: inline; 568 | } 569 | -------------------------------------------------------------------------------- /slides/gerrit/theme/js/slides.js: -------------------------------------------------------------------------------- 1 | function main() { 2 | // Since we don't have the fallback of attachEvent and 3 | // other IE only stuff we won't try to run JS for IE. 4 | // It will run though when using Google Chrome Frame 5 | if (document.all) { return; } 6 | 7 | var currentSlideNo; 8 | var notesOn = false; 9 | var expanded = false; 10 | var hiddenContext = false; 11 | var blanked = false; 12 | var slides = document.getElementsByClassName('slide'); 13 | var touchStartX = 0; 14 | var spaces = /\s+/, a1 = ['']; 15 | var tocOpened = false; 16 | var helpOpened = false; 17 | var overviewActive = false; 18 | var modifierKeyDown = false; 19 | var scale = 1; 20 | var showingPresenterView = false; 21 | var presenterViewWin = null; 22 | var isPresenterView = false; 23 | 24 | var str2array = function(s) { 25 | if (typeof s == 'string' || s instanceof String) { 26 | if (s.indexOf(' ') < 0) { 27 | a1[0] = s; 28 | return a1; 29 | } else { 30 | return s.split(spaces); 31 | } 32 | } 33 | return s; 34 | }; 35 | 36 | var trim = function(str) { 37 | return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); 38 | }; 39 | 40 | var addClass = function(node, classStr) { 41 | classStr = str2array(classStr); 42 | var cls = ' ' + node.className + ' '; 43 | for (var i = 0, len = classStr.length, c; i < len; ++i) { 44 | c = classStr[i]; 45 | if (c && cls.indexOf(' ' + c + ' ') < 0) { 46 | cls += c + ' '; 47 | } 48 | } 49 | node.className = trim(cls); 50 | }; 51 | 52 | var removeClass = function(node, classStr) { 53 | var cls; 54 | if (!node) { 55 | throw 'no node provided'; 56 | } 57 | if (classStr !== undefined) { 58 | classStr = str2array(classStr); 59 | cls = ' ' + node.className + ' '; 60 | for (var i = 0, len = classStr.length; i < len; ++i) { 61 | cls = cls.replace(' ' + classStr[i] + ' ', ' '); 62 | } 63 | cls = trim(cls); 64 | } else { 65 | cls = ''; 66 | } 67 | if (node.className != cls) { 68 | node.className = cls; 69 | } 70 | }; 71 | 72 | var getSlideEl = function(slideNo) { 73 | if (slideNo > 0) { 74 | return slides[slideNo - 1]; 75 | } else { 76 | return null; 77 | } 78 | }; 79 | 80 | var getSlideTitle = function(slideNo) { 81 | var el = getSlideEl(slideNo); 82 | if (el) { 83 | var headers = el.getElementsByTagName('header'); 84 | if (headers.length > 0) { 85 | return el.getElementsByTagName('header')[0].innerText; 86 | } 87 | } 88 | return null; 89 | }; 90 | 91 | var getSlidePresenterNote = function(slideNo) { 92 | var el = getSlideEl(slideNo); 93 | if (el) { 94 | var n = el.getElementsByClassName('presenter_notes'); 95 | if (n.length > 0) { 96 | return n[0]; 97 | } 98 | } 99 | return null; 100 | }; 101 | 102 | var changeSlideElClass = function(slideNo, className) { 103 | var el = getSlideEl(slideNo); 104 | if (el) { 105 | removeClass(el, 'far-past past current future far-future'); 106 | addClass(el, className); 107 | } 108 | }; 109 | 110 | var updateSlideClasses = function(updateOther) { 111 | window.location.hash = (isPresenterView ? "presenter" : "slide") + currentSlideNo; 112 | 113 | for (var i=1; i 1) { 178 | currentSlideNo--; 179 | } 180 | updateSlideClasses(true); 181 | }; 182 | 183 | var showNotes = function() { 184 | var notes = getSlideEl(currentSlideNo).getElementsByClassName('notes'); 185 | for (var i = 0, len = notes.length; i < len; i++) { 186 | notes.item(i).style.display = (notesOn) ? 'none':'block'; 187 | } 188 | notesOn = !notesOn; 189 | }; 190 | 191 | var showSlideNumbers = function() { 192 | var asides = document.getElementsByClassName('page_number'); 193 | var hidden = asides[0].style.display != 'block'; 194 | for (var i = 0; i < asides.length; i++) { 195 | asides.item(i).style.display = hidden ? 'block' : 'none'; 196 | } 197 | }; 198 | 199 | var showSlideSources = function() { 200 | var asides = document.getElementsByClassName('source'); 201 | var hidden = asides[0].style.display != 'block'; 202 | for (var i = 0; i < asides.length; i++) { 203 | asides.item(i).style.display = hidden ? 'block' : 'none'; 204 | } 205 | }; 206 | 207 | var showToc = function() { 208 | if (helpOpened) { 209 | showHelp(); 210 | } 211 | var toc = document.getElementById('toc'); 212 | if (toc) { 213 | toc.style.marginLeft = tocOpened ? '-' + (toc.clientWidth + 20) + 'px' : '0px'; 214 | tocOpened = !tocOpened; 215 | } 216 | updateOverview(); 217 | }; 218 | 219 | var showHelp = function() { 220 | if (tocOpened) { 221 | showToc(); 222 | } 223 | 224 | var help = document.getElementById('help'); 225 | 226 | if (help) { 227 | help.style.marginLeft = helpOpened ? '-' + (help.clientWidth + 20) + 'px' : '0px'; 228 | helpOpened = !helpOpened; 229 | } 230 | }; 231 | 232 | var showPresenterView = function() { 233 | if (isPresenterView) { return; } 234 | 235 | if (showingPresenterView) { 236 | presenterViewWin.close(); 237 | presenterViewWin = null; 238 | showingPresenterView = false; 239 | } else { 240 | presenterViewWin = open(window.location.pathname + "#presenter" + currentSlideNo, 'presenter_notes', 241 | 'directories=no,location=no,toolbar=no,menubar=no,copyhistory=no'); 242 | showingPresenterView = true; 243 | } 244 | }; 245 | 246 | var switch3D = function() { 247 | if (document.body.className.indexOf('three-d') == -1) { 248 | document.getElementsByClassName('presentation')[0].style.webkitPerspective = '1000px'; 249 | document.body.className += ' three-d'; 250 | } else { 251 | window.setTimeout('document.getElementsByClassName(\'presentation\')[0].style.webkitPerspective = \'0\';', 2000); 252 | document.body.className = document.body.className.replace(/three-d/, ''); 253 | } 254 | }; 255 | 256 | var toggleOverview = function() { 257 | if (!overviewActive) { 258 | addClass(document.body, 'expose'); 259 | overviewActive = true; 260 | setScale(1); 261 | } else { 262 | removeClass(document.body, 'expose'); 263 | overviewActive = false; 264 | if (expanded) { 265 | setScale(scale); // restore scale 266 | } 267 | } 268 | processContext(); 269 | updateOverview(); 270 | }; 271 | 272 | var updateOverview = function() { 273 | try { 274 | var presentation = document.getElementsByClassName('presentation')[0]; 275 | } catch (e) { 276 | return; 277 | } 278 | 279 | if (isPresenterView) { 280 | var action = overviewActive ? removeClass : addClass; 281 | action(document.body, 'presenter_view'); 282 | } 283 | 284 | var toc = document.getElementById('toc'); 285 | 286 | if (!toc) { 287 | return; 288 | } 289 | 290 | if (!tocOpened || !overviewActive) { 291 | presentation.style.marginLeft = '0px'; 292 | presentation.style.width = '100%'; 293 | } else { 294 | presentation.style.marginLeft = toc.clientWidth + 'px'; 295 | presentation.style.width = (presentation.clientWidth - toc.clientWidth) + 'px'; 296 | } 297 | }; 298 | 299 | var computeScale = function() { 300 | var cSlide = document.getElementsByClassName('current')[0]; 301 | var sx = cSlide.clientWidth / window.innerWidth; 302 | var sy = cSlide.clientHeight / window.innerHeight; 303 | return 1 / Math.max(sx, sy); 304 | }; 305 | 306 | var setScale = function(scale) { 307 | var presentation = document.getElementsByClassName('slides')[0]; 308 | var transform = 'scale(' + scale + ')'; 309 | presentation.style.MozTransform = transform; 310 | presentation.style.WebkitTransform = transform; 311 | presentation.style.OTransform = transform; 312 | presentation.style.msTransform = transform; 313 | presentation.style.transform = transform; 314 | }; 315 | 316 | var expandSlides = function() { 317 | if (overviewActive) { 318 | return; 319 | } 320 | if (expanded) { 321 | setScale(1); 322 | expanded = false; 323 | } else { 324 | scale = computeScale(); 325 | setScale(scale); 326 | expanded = true; 327 | } 328 | }; 329 | 330 | var showContext = function() { 331 | try { 332 | var presentation = document.getElementsByClassName('slides')[0]; 333 | removeClass(presentation, 'nocontext'); 334 | } catch (e) {} 335 | }; 336 | 337 | var hideContext = function() { 338 | try { 339 | var presentation = document.getElementsByClassName('slides')[0]; 340 | addClass(presentation, 'nocontext'); 341 | } catch (e) {} 342 | }; 343 | 344 | var processContext = function() { 345 | if (hiddenContext) { 346 | hideContext(); 347 | } else { 348 | showContext(); 349 | } 350 | }; 351 | 352 | var toggleContext = function() { 353 | hiddenContext = !hiddenContext; 354 | processContext(); 355 | }; 356 | 357 | var toggleBlank = function() { 358 | blank_elem = document.getElementById('blank'); 359 | 360 | blank_elem.style.display = blanked ? 'none' : 'block'; 361 | 362 | blanked = !blanked; 363 | }; 364 | 365 | var isModifierKey = function(keyCode) { 366 | switch (keyCode) { 367 | case 16: // shift 368 | case 17: // ctrl 369 | case 18: // alt 370 | case 91: // command 371 | return true; 372 | break; 373 | default: 374 | return false; 375 | break; 376 | } 377 | }; 378 | 379 | var checkModifierKeyUp = function(event) { 380 | if (isModifierKey(event.keyCode)) { 381 | modifierKeyDown = false; 382 | } 383 | }; 384 | 385 | var checkModifierKeyDown = function(event) { 386 | if (isModifierKey(event.keyCode)) { 387 | modifierKeyDown = true; 388 | } 389 | }; 390 | 391 | var handleBodyKeyDown = function(event) { 392 | switch (event.keyCode) { 393 | case 13: // Enter 394 | if (overviewActive) { 395 | toggleOverview(); 396 | } 397 | break; 398 | case 27: // ESC 399 | toggleOverview(); 400 | break; 401 | case 37: // left arrow 402 | case 33: // page up 403 | event.preventDefault(); 404 | prevSlide(); 405 | break; 406 | case 39: // right arrow 407 | case 32: // space 408 | case 34: // page down 409 | event.preventDefault(); 410 | nextSlide(); 411 | break; 412 | case 50: // 2 413 | if (!modifierKeyDown) { 414 | showNotes(); 415 | } 416 | break; 417 | case 51: // 3 418 | if (!modifierKeyDown && !overviewActive) { 419 | switch3D(); 420 | } 421 | break; 422 | case 190: // . 423 | case 48: // 0 424 | case 66: // b 425 | if (!modifierKeyDown && !overviewActive) { 426 | toggleBlank(); 427 | } 428 | break; 429 | case 67: // c 430 | if (!modifierKeyDown && !overviewActive) { 431 | toggleContext(); 432 | } 433 | break; 434 | case 69: // e 435 | if (!modifierKeyDown && !overviewActive) { 436 | expandSlides(); 437 | } 438 | break; 439 | case 72: // h 440 | showHelp(); 441 | break; 442 | case 78: // n 443 | if (!modifierKeyDown && !overviewActive) { 444 | showSlideNumbers(); 445 | } 446 | break; 447 | case 80: // p 448 | if (!modifierKeyDown && !overviewActive) { 449 | showPresenterView(); 450 | } 451 | break; 452 | case 83: // s 453 | if (!modifierKeyDown && !overviewActive) { 454 | showSlideSources(); 455 | } 456 | break; 457 | case 84: // t 458 | showToc(); 459 | break; 460 | } 461 | }; 462 | 463 | var handleWheel = function(event) { 464 | if (tocOpened || helpOpened || overviewActive) { 465 | return; 466 | } 467 | 468 | var delta = 0; 469 | 470 | if (!event) { 471 | event = window.event; 472 | } 473 | 474 | if (event.wheelDelta) { 475 | delta = event.wheelDelta/120; 476 | if (window.opera) delta = -delta; 477 | } else if (event.detail) { 478 | delta = -event.detail/3; 479 | } 480 | 481 | if (delta && delta <0) { 482 | nextSlide(); 483 | } else if (delta) { 484 | prevSlide(); 485 | } 486 | }; 487 | 488 | var addSlideClickListeners = function() { 489 | for (var i=0; i < slides.length; i++) { 490 | var slide = slides.item(i); 491 | slide.num = i + 1; 492 | slide.addEventListener('click', function(e) { 493 | if (overviewActive) { 494 | currentSlideNo = this.num; 495 | toggleOverview(); 496 | updateSlideClasses(true); 497 | e.preventDefault(); 498 | } 499 | return false; 500 | }, true); 501 | } 502 | }; 503 | 504 | var addRemoteWindowControls = function() { 505 | window.addEventListener("message", function(e) { 506 | if (e.data.indexOf("slide#") != -1) { 507 | currentSlideNo = Number(e.data.replace('slide#', '')); 508 | updateSlideClasses(false); 509 | } 510 | }, false); 511 | }; 512 | 513 | var addTouchListeners = function() { 514 | document.addEventListener('touchstart', function(e) { 515 | touchStartX = e.touches[0].pageX; 516 | }, false); 517 | document.addEventListener('touchend', function(e) { 518 | var pixelsMoved = touchStartX - e.changedTouches[0].pageX; 519 | var SWIPE_SIZE = 150; 520 | if (pixelsMoved > SWIPE_SIZE) { 521 | nextSlide(); 522 | } 523 | else if (pixelsMoved < -SWIPE_SIZE) { 524 | prevSlide(); 525 | } 526 | }, false); 527 | }; 528 | 529 | var addTocLinksListeners = function() { 530 | var toc = document.getElementById('toc'); 531 | if (toc) { 532 | var tocLinks = toc.getElementsByTagName('a'); 533 | for (var i=0; i < tocLinks.length; i++) { 534 | tocLinks.item(i).addEventListener('click', function(e) { 535 | currentSlideNo = Number(this.attributes['href'].value.replace('#slide', '')); 536 | updateSlideClasses(true); 537 | e.preventDefault(); 538 | }, true); 539 | } 540 | } 541 | }; 542 | 543 | // initialize 544 | 545 | (function() { 546 | if (window.location.hash == "") { 547 | currentSlideNo = 1; 548 | } else if (window.location.hash.indexOf("#presenter") != -1) { 549 | currentSlideNo = Number(window.location.hash.replace('#presenter', '')); 550 | isPresenterView = true; 551 | showingPresenterView = true; 552 | presenterViewWin = window; 553 | addClass(document.body, 'presenter_view'); 554 | } else { 555 | currentSlideNo = Number(window.location.hash.replace('#slide', '')); 556 | } 557 | 558 | document.addEventListener('keyup', checkModifierKeyUp, false); 559 | document.addEventListener('keydown', handleBodyKeyDown, false); 560 | document.addEventListener('keydown', checkModifierKeyDown, false); 561 | document.addEventListener('DOMMouseScroll', handleWheel, false); 562 | 563 | window.onmousewheel = document.onmousewheel = handleWheel; 564 | window.onresize = expandSlides; 565 | 566 | for (var i = 0, el; el = slides[i]; i++) { 567 | addClass(el, 'slide far-future'); 568 | } 569 | updateSlideClasses(false); 570 | 571 | // add support for finger events (filter it by property detection?) 572 | addTouchListeners(); 573 | 574 | addTocLinksListeners(); 575 | 576 | addSlideClickListeners(); 577 | 578 | addRemoteWindowControls(); 579 | })(); 580 | } 581 | -------------------------------------------------------------------------------- /slides/gitflow-model.src.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/slides/gitflow-model.src.key -------------------------------------------------------------------------------- /slides/workflow-gitflow.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twotwo/learn-git/80088ad949e9345193f86800e844c3fbcb24083d/slides/workflow-gitflow.pdf -------------------------------------------------------------------------------- /why-git.md: -------------------------------------------------------------------------------- 1 | * Git 是啥? 2 | * 相比 SVN 有什么不同 ? 3 | * 使用 SVN 用的好好的,为什么要用 Git ? 4 | 5 | 6 | ## First, what is Git ? 7 | 8 | Git is a free and open source distributed version control system (SCM) created by Linus Torvalds in 2005 ( Linux OS founder). It is designed to manage everything for small or very large projects with speed and efficiency. Majors organisations like Google, Facebook, Microsoft uses Git daily. 9 | 10 | ## Git 与 SVN 的不同之处 11 | 12 | ### 去中心化带来的巨大变化 13 | 14 | - 引入本地版本库 15 | 16 | 本地开发版本+远端版本库 vs 本地开发版本+本地版本库+远端版本库 17 | 18 | - 结构复杂度 + 1, 带来 概念、命令 复杂度增加 19 | 20 | - 降低了对远端版本库的依赖,增强了版本管理的可扩展性和性能 21 | 22 | 1. 本地管理无需任何授权 23 | 2. 分支、合并操作简便 24 | 3. 支持各种管理流程,扩展性强 25 | 26 | ### 基于对象方式的管理 27 | 28 | - 先进的内容寻址文件系统,所有的历史信息都以对象形式存在本地版本库中 29 | 30 | 参考 [Git 内部原理](https://git-scm.com/book/zh/v2/Git-内部原理-底层命令和高层命令) 31 | 32 | - 工作目录下只存放当前的版本,干净! 33 | 34 | ### 一目了然的暂存区(Index) 35 | 36 | 查看即将提交的内容、进行部分提交等 37 | 38 | ## Git 相比 SVN 的一些优势 39 | 40 | ![Git vs SVN](images/why-git.png) 41 | 42 | :point_right: *素材主要来自 [@oldratlee](https://github.com/oldratlee) 的 [repo](https://github.com/oldratlee/software-practice-miscellany/blob/master/git/README.md)* 43 | 44 | ### 1. 历史记录更清晰完整 45 | #### :beer: 合并对提交过程的保留 46 | 47 | - `git`:合并操作保留原有的提交过程(即保留了合并来源的作者、提交次数、分离提交的内容)。 48 | - `svn`:合并操作把来源多个提交合并成了一个合并提交,即在提交历史中Crash了自然的提交过程。 49 | 50 | 保留原有的提交过程,可以无需繁琐追踪历史就方便的 51 | 52 | 1. 跟踪修改过程。 53 | 1. 直接从提交中就可以看到原提交的作者信息,体现了对作者的尊重。 54 | 1. 自然的提交过程。这极大方便了代码细节演进过程的查看。 55 | 1. 极大方便查出那行提交是什么时间、谁做出的。 56 | `svn`因为合并Crash了自然的提交过程,要追踪很痛苦。 57 | 58 | #### :beer: 可以修正错误的提交 59 | 60 | - `git`:可以修正提交。 61 | 使用功能分支工作流,在自己的分支可以方便修正提交而不会影响大家。 62 | - `svn`:一旦提交就到服务器上,实际使用中就是不能修改。 63 | (`svn`可以在服务器上修改,因为过程复杂需要权限实际上从不会这样做。) 64 | 65 | 实际使用中会有误提交的情况(如提交了一个不该提交的日志文件),对于`svn`来说,就是让大家一遍又一遍看到这个垃圾文件。 66 | 67 | 没有干净的提交,严重影响了`Code Review`,增加成本。 68 | 69 | 另外对于想了解演进过程的同学,垃圾提交影响了了解效果。 70 | 71 | ### 2. 对协作开发支持更强 72 | 协作开发、版本发布更方便,周边系统支持更完备 73 | 74 | #### :beer: 廉价好用的本地分支 75 | 76 | - `git`:有本地分支 77 | - `svn`:无本地分支 78 | 79 | `git`可以方便创建本地分支,且创建分支的时间是`O(1)`,即瞬间就创建好了。由于分支可以是本地的,也就不存在`svn`目录权限的问题。 80 | 81 | 可以从想要工作点闪电般创建本地分支,本地实验不确定的修改,创建分支如此之廉价,`git`推荐创建分支来隔离修改。 82 | 83 | #### :beer: 更强大智能的合并能力 84 | 85 | - `git`:重命名(无论文件还有目录)提交 可以合并上 文件重命名前的这些文件的提交。 86 | - `svn`:重命名(无论文件还有目录)提交后,你本地/或是分支上 有文件重命名前的这些文件的修改或提交,在做合并操作时,恭喜:see_no_evil:,你会碰上传说中难搞的***树冲突***! 87 | 88 | 因为惧怕`svn`***树冲突***,在包名调整(重命名目录)或类名调整(重命名文件)前,我不得不先向一起开发的组员广播: 89 | 90 | 1. 提交你的修改 91 | 1. 暂停相关类的修改 92 | 1. 我开始做调整 93 | 1. 等我修改好后:scream:,你再开始修改 94 | 95 | OMG~ :confounded:~~ 96 | 97 | 因为这个过程烦琐,结果就是影响了大家去做这样重构操作的积极性,进而影响项目的代码质量改进! 98 | 99 | 别忘了,如果你的项目是开源的,全球的人可以给你提交,可没有办法向全球的同学广播 :kissing: 100 | 101 | #### :beer: 原生支持`tag` 102 | 103 | - `svn`在模型上是没有分支和`tag`的。`tag`是通过目录权限限制(对开发只读)来保证不变。 104 | - `git`从模型上就支持`tag`,保证只读。 105 | 106 | 内心是不是有强烈的安全感? :sparkles: 107 | 108 | 109 | #### :beer: 完整配套的开发过程设施 110 | 111 | 与`git`配套的`github`、`gitlab`(我们公司搭建了)提供了: 112 | 113 | - `Markdown`:高效的文档编写和查看。 114 | - `Issue` & `Milestone`:问题记录&跟踪,任务分配,版本规划&管理 115 | - `Wiki`系统:体系的文档 116 | - 评论:可以对代码提交(即是Code Review)& Issue做评论。 117 | 这个有记录交流的过程。 118 | 119 | 记住,上面的一切和代码一起集中管理,是以代码为中心的,可以方便的工程中的代码。 120 | 121 | 可运行并完成功能的代码(且叫目标代码) 才是整体项目真正生效的产出。 122 | 123 | 一切不为 目标代码 服务 的东东都是 **流氓**! 124 | \# 是不是想到很多东西(比如下压式的排期计划)会觉得自己是生效的产出,好像剩下的事就是 码农搬砖一样把代码码好。 125 | 126 | ### 3. 性能更优 127 | 128 | #### :beer: 提交 129 | 130 | - `git`提交是个本地操作,相对`svn`闪电一般。 131 | - `git`提供了暂存区,可以方便指定提交内容,而不是全部。 132 | PS: `git`可以只提交一个文件修改的一部分而非全部(`Git add –p`),使用相对繁琐些。(实际上开发中我很少这么做 :grin:) 133 | 134 | 这让开发者更愿意整理提交,让每个提交更内聚自包含。进而有利于 135 | 136 | - `Code Review` 137 | - 线上`Bug`的快速准确的回滚式修复 138 | 139 | #### :beer: 查看日志 140 | 141 | 查看日志是个频繁的操作。 142 | 143 | - `git`:本地包含了完整的日志,闪电的速度(并且无需网络)。 144 | - `svn`:需要从服务拉取。 145 | 146 | 一旦用了`git`后,等待`svn`日志(包括查看2个版本间的`diff`)过程简直让我发狂。 147 | 148 | #### :beer: 存储 149 | 150 | Git 版本库所需的存储空间约为 SVN 的1/30 151 | 152 | 153 | 154 | ## 参考内容 155 | - [Why is Git better than Subversion?](http://stackoverflow.com/questions/871/why-is-git-better-than-subversion) stackoverflow 上关于svn和git的区别的讨论,靠前的帖子推荐都细读一下 156 | - [What are the differences between SVN and Git? ](https://help.github.com/articles/what-are-the-differences-between-subversion-and-git/) github 上通过版本库结构、历史、子项目(submudle)的不同来对比两者 157 | - [Git Svn Comparison](https://git.wiki.kernel.org/index.php/GitSvnComparsion) 最权威的Git SVN 对比说明 158 | - [蒋鑫:为什么 Git 比 SVN 好?](http://www.worldhello.net/2012/04/12/why-git-is-better-than-svn.html) 对 Git 的误解和两者的使用场景 -------------------------------------------------------------------------------- /workflow-forking.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Process 4 | ![Github Forking工作流](images/workflow-github.png) 5 | 6 | ## 参考 7 | 8 | - [Github 跨 Repository 开发流程](http://www.ruanyifeng.com/blog/2015/08/git-use-process.html) @阮一峰 2015年8月5日 Gitflow 工作流 9 | - [Forking工作流](https://github.com/oldratlee/translations/blob/master/git-workflows-and-tutorials/workflow-forking.md) @oldratlee -------------------------------------------------------------------------------- /workflow-gitflow.md: -------------------------------------------------------------------------------- 1 | # Gitflow 工作流 2 | 3 | Gitflow 工作流总览 4 | 5 | ![Gitflow 工作流](images/workflow-gitflow.jpg) 6 | 7 | ## 1. Decentralized but centralized 8 | Git 作为去中心化的版本管理工具,依然有一个中心版本库,我们把这个中心版本库称为`origin`。对于开发者来说,origin是处在远端的一个服务。 9 | 10 | ## 2. The Main Branches 11 | 中心版本库`origin`上有两个永久的分支: 12 | 13 | * `master` 存储正式发布的历史,每个发布版本都打上对应版本的tag 14 | * `develop` 存储开发中的下一个版本,是功能的集成分支,可以作为每日构建的源 15 | ![Gitflow 主分支](images/gitflow_main_branches.svg) 16 | 17 | ### 2.1 两个分支的关系 18 | * `develop` 分支创建自 `master`; 19 | * `develop` 分支到达发布阶段后会创建出专门的发布分支,发布分支验收通过后的版本合并入 `master` ,并在 `master` 上打上对应版本的tag;此发布版本与 `develop` 分支合并,成为新的开发版本的基线 20 | 21 | ## 3. Supporing Branches 22 | 除了两条主分支外,还有一些支持分支,用来满足开发过程中的**多功能同步开发**、**产品发布**和**线上问题快速修复**等场景。这些支持分支的生命周期比较短,在对应场景结束的时候都会被删除。 23 | 24 | * Feature branches 多功能同步开发专用,每个功能一个分支 25 | * Release branches 产品发布专用,验收通过合并入master 26 | * Hotfix branches 线上问题debug分支,结束后分别合并入master和develop 27 | 28 | ### 3.1 Feature branches 29 | * May branch off from: `develop` 30 | * Must merge back into: `develop` 31 | * Branch naming convention: anything except `master`, `develop`, `release-*`, or `hotfix-*` 32 | 33 | ![Gitflow 功能分支](images/gitflow_feature_branches.svg) 34 | 35 | # 从 develop 上创建功能分支 36 | $ git checkout -b myfeature develop 37 | Switched to a new branch "myfeature" 38 | # 把开发完毕的功能分支合并回 develop 39 | $ git checkout develop 40 | Switched to branch 'develop' 41 | $ git merge --no-ff myfeature 42 | Updating ea1b82a..05e9557 43 | (Summary of changes) 44 | $ git branch -d myfeature 45 | Deleted branch myfeature (was 05e9557). 46 | $ git push origin develop 47 | ![git merge --no-ff](images/merge-without-ff@2x.png) 48 | 49 | 功能分支加上develop分支就是功能分支工作流的用法。但Gitflow工作流没有在这里止步。 50 | 51 | ### 3.2 Release branches 52 | * May branch off from: `develop` 53 | * Must merge back into: `develop` and `master` 54 | * Branch naming convention: `release-*` 55 | 56 | ![Gitflow 发布分支](images/gitflow_release_branches.svg) 57 | 58 | 一旦develop分支上有了做一次发布(或者说快到了既定的发布日)的足够功能,就从develop分支上fork一个发布分支。 新建的分支用于开始发布循环,所以从这个时间点开始之后新的功能不能再加到这个分支上—— 这个分支只应该做Bug修复、文档生成和其它面向发布任务。 一旦对外发布的工作都完成了,发布分支合并到master分支并分配一个版本号打好Tag。 另外,这些从新建发布分支以来的做的修改要合并回develop分支。 59 | 60 | 使用一个用于发布准备的专门分支,使得一个团队可以在完善当前的发布版本的同时,另一个团队可以继续开发下个版本的功能。 这也打造定义良好的开发阶段(比如,可以很轻松地说,『这周我们要做准备发布版本4.0』,并且在仓库的目录结构中可以实际看到)。 61 | 62 | # 从 develop 上创建发布分支 63 | $ git checkout -b release-1.2 develop 64 | Switched to a new branch "release-1.2" 65 | $ ./set-version.sh 1.2 # 设置产品版本 66 | Files modified successfully, version bumped to 1.2. 67 | $ git commit -a -m "Bumped version number to 1.2" 68 | [release-1.2 74d9424] Bumped version number to 1.2 69 | 1 files changed, 1 insertions(+), 1 deletions(-) 70 | # 结束发布分支: merge、tag、delete 71 | $ git checkout master 72 | Switched to branch 'master' 73 | $ git merge --no-ff release-1.2 74 | Merge made by recursive. 75 | (Summary of changes) 76 | $ git tag -a 1.2 77 | 78 | ### 3.3 Hotfix branches 79 | * May branch off from: `master` 80 | * Must merge back into: `develop` and `master` 81 | * Branch naming convention: `hotfix-*` 82 | 83 | ![Gitflow 维护分支](images/gitflow_hotfix_branches.svg) 84 | 85 | 维护分支用于生成快速给产品发布版本(production releases)打补丁,这是唯一可以直接从master分支fork出来的分支。 修复完成,修改应该马上合并回master分支和develop分支(当前的发布分支),master分支应该用新的版本号打好Tag。 86 | 87 | 为Bug修复使用专门分支,让团队可以处理掉问题而不用打断其它工作或是等待下一个发布循环。 你可以把维护分支想成是一个直接在master分支上处理的临时发布。 88 | 89 | # 从 master 上创建维护分支 90 | $ git checkout -b hotfix-1.2.1 master 91 | Switched to a new branch "hotfix-1.2.1" 92 | $ ./set-version.sh 1.2.1 # 设置产品版本,版本号第三位升级 93 | Files modified successfully, version bumped to 1.2.1. 94 | $ git commit -a -m "Bumped version number to 1.2.1" 95 | [hotfix-1.2.1 41e61bb] Bumped version number to 1.2.1 96 | 1 files changed, 1 insertions(+), 1 deletions(-) 97 | # 修复bug & 提交更新 98 | $ git commit -m "Fixed severe production problem" 99 | [hotfix-1.2.1 abbe5d6] Fixed severe production problem 100 | 5 files changed, 32 insertions(+), 17 deletions(-) 101 | # 结束发布分支: merge back&tag on master、merge back develop、delete 102 | $ git checkout master 103 | Switched to branch 'master' 104 | $ git merge --no-ff hotfix-1.2.1 105 | Merge made by recursive. 106 | (Summary of changes) 107 | $ git tag -a 1.2.1 108 | ... 109 | $ git branch -d hotfix-1.2.1 110 | Deleted branch hotfix-1.2.1 (was abbe5d6). 111 | 112 | ## 4. 参考 113 | - [A successful Git branching model](http://nvie.com/posts/a-successful-git-branching-model/) 介绍日常推荐的分支开发模型,基于此模型可以通过这个小游戏来进行学习 [Learn Git Branch](http://pcottle.github.io/learnGitBranching/) 114 | - [Gitflow 工作流](https://github.com/oldratlee/translations/blob/master/git-workflows-and-tutorials/workflow-gitflow.md) @oldratlee 整理的 Gitflow 工作流 --------------------------------------------------------------------------------