2
2
=== 使用强制策略的一个例子
3
3
4
4
(((policy example)))
5
- 在本节中,你将应用前面学到的知识建立这样一个 Git 工作流程:检查提交信息的格式,并且指定特定用户只能修改项目中特定的子目录 。
6
- 你将完成一个客户端脚本来提示开发人员他们的推送是否会被拒绝 ,以及一个服务器端脚本来实际执行这些策略。
5
+ 在本节中,你将应用前面学到的知识建立这样一个 Git 工作流程:检查提交信息的格式,并且指定只能由特定用户修改项目中特定的子目录 。
6
+ 你将编写一个客户端脚本来提示开发人员他们的推送是否会被拒绝 ,以及一个服务器端脚本来实际执行这些策略。
7
7
8
8
我们待会展示的脚本是用 Ruby 写的,部分是由于我习惯用它写脚本,另外也因为 Ruby 简单易懂,即便你没写过它也能看明白。
9
- 不过任何其他语言也一样适用。所有 Git 自带的示例脚本都是用 Perl 或 Bash 写的,所以你能从它们中找到相当多的这两种语言的钩子示例。
9
+ 不过任何其他语言也一样适用。所有 Git 自带的示例钩子脚本都是用 Perl 或 Bash 写的,所以你能从它们中找到相当多的这两种语言的钩子示例。
10
10
11
11
==== 服务器端钩子
12
12
18
18
* 用户准备推送的修订版本(revision)
19
19
20
20
如果推送是通过 SSH 进行的,还可以获知进行此次推送的用户的信息。
21
- 如果你允许所有操作都通过公匙授权的单一帐号(比如“git”)进行,就有必要提供一个 shell 包装脚本,依据公匙判断用户的身份,并且存储到一个环境变量中 。
21
+ 如果你允许所有操作都通过公匙授权的单一帐号(比如“git”)进行,就有必要通过一个 shell 包装脚本依据公匙来判断用户的身份,并且相应地设定环境变量来表示该用户的身份 。
22
22
下面就假设 `$USER` 环境变量里存储了当前连接的用户的身份,你的 update 脚本首先搜集一切需要的信息:
23
23
24
24
[source,ruby]
@@ -40,13 +40,13 @@ puts "(#{$refname}) (#{$oldrev[0,6]}) (#{$newrev[0,6]})"
40
40
[[_enforcing_commit_message_format]]
41
41
===== 指定特殊的提交信息格式
42
42
43
- 你的第一项任务是指定每一条提交信息都必须遵循某种特殊的格式 。
43
+ 你的第一项任务是要求每一条提交信息都必须遵循某种特殊的格式 。
44
44
作为目标,假定每一条信息必须包含一条形似“ref: 1234”的字符串,因为你想把每一次提交对应到问题追踪系统(ticketing system)中的某个事项。
45
45
你要逐一检查每一条推送上来的提交内容,看看提交信息是否包含这么一个字符串,然后,如果某个提交里不包含这个字符串,以非零返回值退出从而拒绝此次推送。
46
46
47
47
把 `$newrev` 和 `$oldrev` 变量的值传给一个叫做 `git rev-list` 的 Git 底层命令,你可以获取所有提交的 SHA-1 值列表。
48
48
`git rev-list` 基本类似 `git log` 命令,但它默认只输出 SHA-1 值而已,没有其他信息。
49
- 所以要获取由 SHA 值表示的从一次提交到另一次提交之间的所有 SHA 值,可以运行 :
49
+ 所以要获取由一次提交到另一次提交之间的所有 SHA 值,可以像这样运行 :
50
50
51
51
[source,console]
52
52
----
@@ -58,10 +58,10 @@ dfa04c9ef3d5197182f13fb5b9b1fb7717d2222a
58
58
17716ec0f1ff5c77eff40b7fe912f9f6cfd0e475
59
59
----
60
60
61
- 你可以截取这些输出内容,循环遍历其中每一个 SHA 值,找出与之对应的提交信息,然后用正则表达式来测试该信息 。
61
+ 你可以截取这些输出内容,循环遍历其中每一个 SHA 值,找出与之对应的提交信息,然后用正则表达式来测试该信息包含的内容 。
62
62
63
63
下一步要实现从每个提交中提取出提交信息。
64
- 使用另一个叫做 `git cat-file` 的底层命令可以获得原始的提交数据 。
64
+ 使用另一个叫做 `git cat-file` 的底层命令来获得原始的提交数据 。
65
65
我们将在 <<_git_internals>> 了解到这些底层命令的细节;现在暂时先看一下这条命令的输出:
66
66
67
67
[source,console]
@@ -85,7 +85,7 @@ changed the version number
85
85
----
86
86
87
87
你可以用这条咒语从每一个待推送的提交里提取提交信息,然后在提取的内容不符合要求时退出。
88
- 为了退出脚本和拒绝此次推送,这里需要以非零值退出 。
88
+ 为了退出脚本和拒绝此次推送,返回非零值 。
89
89
整个脚本大致如下:
90
90
91
91
[source,ruby]
@@ -112,12 +112,12 @@ check_message_format
112
112
113
113
假设你需要添加一个使用访问权限控制列表的机制,来指定哪些用户对项目的哪些部分有推送权限。
114
114
某些用户具有全部的访问权,其他人只对某些子目录或者特定的文件具有推送权限。
115
- 为了实现这一点,你要把相关的规则写入一个名为 `acl` 的文件里,并把它放到服务器上的 Git 裸仓库中, 。
115
+ 为了实现这一点,你要把相关的规则写入位于服务器原始 Git 仓库的 acl 文件中 。
116
116
你还需要让 `update` 钩子检阅这些规则,审视推送的提交内容中被修改的所有文件,然后决定执行推送的用户是否对所有这些文件都有权限。
117
117
118
118
先从写一个 ACL 文件开始吧。
119
119
这里使用的格式和 CVS 的 ACL 机制十分类似:它由若干行构成,第一项内容是 `avail` 或者 `unavail`,接着是逗号分隔的适用该规则的用户列表,最后一项是适用该规则的路径(该项空缺表示没有路径限制)。
120
- 各项由 `|` 字符隔开 。
120
+ 各项由管道符 `|` 隔开 。
121
121
122
122
在本例中,你会有几个管理员,一些对 `doc` 目录具有权限的文档作者,以及一位仅对 `lib` 和 `tests` 目录具有权限的开发人员,相应的 ACL 文件如下:
123
123
@@ -130,7 +130,7 @@ avail|schacon|tests
130
130
----
131
131
132
132
首先把这些数据读入你要用到的数据结构里。
133
- 在本例中,为简明扼要起见 ,我们暂时只实现 `avail` 的规则。
133
+ 在本例中,为保持简洁 ,我们暂时只实现 `avail` 的规则。
134
134
下面这个方法生成一个关联数组,它的键是用户名,值是一个由该用户有写权限的所有目录组成的数组:
135
135
136
136
[source,ruby]
165
165
"ebronte"=>["doc"]}
166
166
----
167
167
168
- 既然拿到了用户权限的数据,接下来你需要找出提交都涉及了哪些路径 ,从而才能保证推送者对所有这些路径都有权限。
168
+ 既然拿到了用户权限的数据,接下来你需要找出提交都修改了哪些路径 ,从而才能保证推送者对所有这些路径都有权限。
169
169
170
170
使用 `git log` 的 `--name-only` 选项(在第二章里简单地提过),我们可以轻而易举的找出一次提交里修改的文件:
171
171
@@ -210,7 +210,7 @@ check_directory_perms
210
210
----
211
211
212
212
通过 `git rev-list` 获取推送到服务器的所有提交。
213
- 然后 ,对于每一个提交,找出它修改的文件,然后确保推送者对这些文件都具有权限 。
213
+ 接着 ,对于每一个提交,找出它修改的文件,然后确保推送者具有这些文件的推送权限 。
214
214
215
215
现在你的用户没法推送带有不正确的提交信息的内容,也不能在准许他们访问范围之外的位置做出修改。
216
216
@@ -257,7 +257,7 @@ error: hooks/update exited with error code 1
257
257
error: hook declined to update refs/heads/master
258
258
----
259
259
260
- 第一行是我们的脚本输出的,再往下是 Git 在告诉我们 update 脚本退出时返回了非零值因而推送遭到了拒绝。
260
+ 第一行是我们的脚本输出的,剩下两行是 Git 在告诉我们 update 脚本退出时返回了非零值因而推送遭到了拒绝。
261
261
最后一点:
262
262
263
263
[source]
@@ -281,17 +281,17 @@ error: failed to push some refs to 'git@gitserver:project.git'
281
281
282
282
==== 客户端钩子
283
283
284
- 这种方法的缺点在于,用户发现自己推送的提交遭到拒绝后,往往就会向你抱怨 。
285
- 辛辛苦苦写成的代码在最后时刻惨遭拒绝是十分让人沮丧且具有迷惑性的;更可怜的是他们不得不修改提交历史来解决问题,这可不是一个让人心安的方法 。
284
+ 这种方法的缺点在于,用户推送的提交遭到拒绝后无法避免的抱怨 。
285
+ 辛辛苦苦写成的代码在最后时刻惨遭拒绝是十分让人沮丧且具有迷惑性的;更可怜的是他们不得不修改提交历史来解决问题,这个方法并不能让每一个人满意 。
286
286
287
- 逃离这种两难境地的法宝是给用户一些客户端的钩子,在他们犯错的事情的时候给以警告 。
288
- 然后呢,用户们就能在提交之前亡羊补牢 。
287
+ 逃离这种两难境地的法宝是给用户一些客户端的钩子,在他们犯错的时候给以警告 。
288
+ 然后呢,用户们就能趁问题尚未变得更难修复,在提交前消除这个隐患 。
289
289
由于钩子本身不跟随克隆的项目副本分发,所以你必须通过其他途径把这些钩子分发到用户的 `.git/hooks` 目录并设为可执行文件。
290
290
虽然你可以在相同或单独的项目里加入并分发这些钩子,但是 Git 不会自动替你设置它。
291
291
292
292
首先,你应该在每次提交前核查你的提交信息,这样才能确保服务器不会因为不合条件的提交信息而拒绝你的更改。
293
293
为了达到这个目的,你可以增加 `commit-msg` 钩子。
294
- 如果你使用该钩子来读取作为第一个参数传递的提交信息,然后与规定的格式作比较,你就可以使 Git 在提交信息格式不对的情况下,拒绝提交 。
294
+ 如果你使用该钩子来读取作为第一个参数传递的提交信息,然后与规定的格式作比较,你就可以使 Git 在提交信息格式不对的情况下拒绝提交 。
295
295
296
296
[source,ruby]
297
297
----
@@ -307,7 +307,7 @@ if !$regex.match(message)
307
307
end
308
308
----
309
309
310
- 如果这个脚本位于正确的位置 (`.git/hooks/commit-msg`) 并且是可执行的, 并且你的提交信息格式不对 ,你会看到:
310
+ 如果这个脚本位于正确的位置 (`.git/hooks/commit-msg`) 并且是可执行的,你提交信息的格式又是不正确的 ,你会看到:
311
311
312
312
[source,console]
313
313
----
@@ -359,7 +359,7 @@ check_directory_perms
359
359
----
360
360
361
361
这和服务器端的脚本几乎一样,除了两个重要区别。
362
- 第一,ACL 文件的位置不同,因为这个脚本在当前工作目录运行,而非 Git 目录。
362
+ 第一,ACL 文件的位置不同,因为这个脚本在当前工作目录运行,而非 `.git` 目录。
363
363
ACL 文件的路径必须从
364
364
365
365
[source,ruby]
@@ -401,7 +401,7 @@ files_modified = `git diff-index --cached --name-only HEAD`
401
401
402
402
下面是一个检查这个问题的 `pre-rebase` 脚本示例。
403
403
它获取所有待重写的提交的列表,然后检查它们是否存在于远程引用中。
404
- 一旦它发现其中一个提交是在某个远程引用中可达的 (reachable),它就放弃此次变基 :
404
+ 一旦发现其中一个提交是在某个远程引用中可达的 (reachable),它就终止此次变基 :
405
405
406
406
[source,ruby]
407
407
----
@@ -427,8 +427,7 @@ target_shas.each do |sha|
427
427
end
428
428
end
429
429
----
430
- 这个脚本利用了一个第六章“修订版本选择”一节中不曾提到的语法。
431
- 通过运行这个命令可以获得一系列之前推送过的提交:
430
+ 这个脚本利用了一个第六章“修订版本选择”一节中不曾提到的语法。通过运行这个命令可以获得一系列之前推送过的提交:
432
431
433
432
[source,ruby]
434
433
----
437
436
----
438
437
439
438
`SHA^@` 语法会被解析成该提交的所有父提交。
440
- 该命令会列出远程分支最新的提交可达的,但在所有我们尝试推送的提交的 SHA 值的所有父提交中不可达的提交——也就是快进的提交。
439
+ 该命令会列出在远程分支最新的提交中可达的,却在所有我们尝试推送的提交的 SHA 值的所有父提交中不可达的提交——也就是快进的提交。
441
440
442
441
这个解决方案主要的问题在于它有可能很慢而且常常没有必要——只要你不用 `-f` 来强制推送,服务器就会自动给出警告并且拒绝接受推送。
443
442
然而,这是个不错的练习,而且理论上能帮助你避免一次以后可能不得不回头修补的变基。
0 commit comments