commonmarker 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of commonmarker might be problematic. Click here for more details.

Files changed (501) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -2
  3. data/README.md +67 -42
  4. data/Rakefile +22 -2
  5. data/commonmarker.gemspec +13 -9
  6. data/ext/commonmarker/cmark/api_test/main.c +35 -0
  7. data/ext/commonmarker/cmark/build/CMakeFiles/CMakeError.log +12 -12
  8. data/ext/commonmarker/cmark/build/CMakeFiles/CMakeOutput.log +141 -141
  9. data/ext/commonmarker/cmark/build/api_test/CMakeFiles/api_test.dir/main.c.o +0 -0
  10. data/ext/commonmarker/cmark/build/api_test/api_test +0 -0
  11. data/ext/commonmarker/cmark/build/src/CMakeFiles/cmark.dir/houdini_html_u.c.o +0 -0
  12. data/ext/commonmarker/cmark/build/src/CMakeFiles/cmark.dir/iterator.c.o +0 -0
  13. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark.dir/houdini_html_u.c.o +0 -0
  14. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark.dir/iterator.c.o +0 -0
  15. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/houdini_html_u.c.o +0 -0
  16. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/iterator.c.o +0 -0
  17. data/ext/commonmarker/cmark/build/src/cmark +0 -0
  18. data/ext/commonmarker/cmark/build/src/libcmark.0.19.0.dylib +0 -0
  19. data/ext/commonmarker/cmark/build/src/libcmark.a +0 -0
  20. data/ext/commonmarker/cmark/build/src/libcmark.dylib +0 -0
  21. data/ext/commonmarker/cmark/src/houdini_html_u.c +26 -13
  22. data/ext/commonmarker/cmark/src/iterator.c +2 -2
  23. data/ext/commonmarker/cmark/test/__pycache__/cmark.cpython-34.pyc +0 -0
  24. data/ext/commonmarker/cmark/test/__pycache__/normalize.cpython-34.pyc +0 -0
  25. data/ext/commonmarker/cmark/test/cmark.pyc +0 -0
  26. data/ext/commonmarker/cmark/test/normalize.pyc +0 -0
  27. data/ext/commonmarker/commonmarker.c +276 -3
  28. data/ext/commonmarker/extconf.rb +3 -1
  29. data/lib/commonmarker.rb +70 -360
  30. data/lib/commonmarker/config.rb +1 -1
  31. data/lib/commonmarker/renderer.rb +91 -0
  32. data/lib/commonmarker/renderer/html_renderer.rb +149 -0
  33. data/lib/commonmarker/version.rb +1 -1
  34. data/test/benchinput.md +148414 -0
  35. data/test/benchmark.rb +13 -9
  36. data/test/progit/Gemfile +5 -0
  37. data/test/progit/README.md +9 -0
  38. data/test/progit/README.original.md +70 -0
  39. data/test/progit/Rakefile +285 -0
  40. data/test/progit/ar/01-introduction/01-chapter1.markdown +264 -0
  41. data/test/progit/ar/02-git-basics/01-chapter2.markdown +1124 -0
  42. data/test/progit/ar/NOTES +18 -0
  43. data/test/progit/ar/README +14 -0
  44. data/test/progit/az/01-introduction/01-chapter1.markdown +257 -0
  45. data/test/progit/az/02-git-basics/01-chapter2.markdown +1127 -0
  46. data/test/progit/az/03-git-branching/01-chapter3.markdown +598 -0
  47. data/test/progit/az/04-git-server/01-chapter4.markdown +861 -0
  48. data/test/progit/az/05-distributed-git/01-chapter5.markdown +897 -0
  49. data/test/progit/az/06-git-tools/01-chapter6.markdown +1126 -0
  50. data/test/progit/az/07-customizing-git/01-chapter7.markdown +937 -0
  51. data/test/progit/az/08-git-and-other-scms/01-chapter8.markdown +690 -0
  52. data/test/progit/az/09-git-internals/01-chapter9.markdown +977 -0
  53. data/test/progit/be/01-introduction/01-chapter1.markdown +257 -0
  54. data/test/progit/be/02-git-basics/01-chapter2.markdown +1126 -0
  55. data/test/progit/ca/01-introduction/01-chapter1.markdown +257 -0
  56. data/test/progit/ca/README.txt +1 -0
  57. data/test/progit/couchapp/Makefile +41 -0
  58. data/test/progit/couchapp/Readme.md +17 -0
  59. data/test/progit/couchapp/_id +1 -0
  60. data/test/progit/couchapp/shows/chapter.js +14 -0
  61. data/test/progit/couchapp/templates/foot.html +7 -0
  62. data/test/progit/couchapp/templates/head.html +51 -0
  63. data/test/progit/couchapp/vendor/markdown/showdown.js +420 -0
  64. data/test/progit/couchapp/vendor/mustache.js/mustache.js +302 -0
  65. data/test/progit/cs/01-introduction/01-chapter1.markdown +259 -0
  66. data/test/progit/cs/02-git-basics/01-chapter2.markdown +1225 -0
  67. data/test/progit/cs/03-git-branching/01-chapter3.markdown +606 -0
  68. data/test/progit/cs/04-git-server/01-chapter4.markdown +871 -0
  69. data/test/progit/cs/05-distributed-git/01-chapter5.markdown +914 -0
  70. data/test/progit/cs/06-git-tools/01-chapter6.markdown +1167 -0
  71. data/test/progit/cs/07-customizing-git/01-chapter7.markdown +940 -0
  72. data/test/progit/cs/08-git-and-other-scms/01-chapter8.markdown +700 -0
  73. data/test/progit/cs/09-git-internals/01-chapter9.markdown +1014 -0
  74. data/test/progit/de/01-introduction/01-chapter1.markdown +445 -0
  75. data/test/progit/de/02-git-basics/01-chapter2.markdown +1589 -0
  76. data/test/progit/de/03-git-branching/01-chapter3.markdown +964 -0
  77. data/test/progit/de/04-git-server/01-chapter4.markdown +1337 -0
  78. data/test/progit/de/05-distributed-git/01-chapter5.markdown +1329 -0
  79. data/test/progit/de/06-git-tools/01-chapter6.markdown +1502 -0
  80. data/test/progit/de/07-customizing-git/01-chapter7.markdown +1361 -0
  81. data/test/progit/de/08-git-and-other-scms/01-chapter8.markdown +919 -0
  82. data/test/progit/de/09-git-internals/01-chapter9.markdown +1361 -0
  83. data/test/progit/de/README.md +626 -0
  84. data/test/progit/ebooks/cover.png +0 -0
  85. data/test/progit/en/01-introduction/01-chapter1.markdown +263 -0
  86. data/test/progit/en/02-git-basics/01-chapter2.markdown +1228 -0
  87. data/test/progit/en/03-git-branching/01-chapter3.markdown +606 -0
  88. data/test/progit/en/04-git-server/01-chapter4.markdown +871 -0
  89. data/test/progit/en/05-distributed-git/01-chapter5.markdown +914 -0
  90. data/test/progit/en/06-git-tools/01-chapter6.markdown +1150 -0
  91. data/test/progit/en/07-customizing-git/01-chapter7.markdown +940 -0
  92. data/test/progit/en/08-git-and-other-scms/01-chapter8.markdown +700 -0
  93. data/test/progit/en/09-git-internals/01-chapter9.markdown +983 -0
  94. data/test/progit/eo/01-introduction/01-chapter1.markdown +257 -0
  95. data/test/progit/eo/02-git-basics/01-chapter2.markdown +1171 -0
  96. data/test/progit/epub/ProGit.css +28 -0
  97. data/test/progit/epub/title.png +0 -0
  98. data/test/progit/es-ni/01-introduction/01-chapter1.markdown +257 -0
  99. data/test/progit/es-ni/02-git-basics/01-chapter2.markdown +1127 -0
  100. data/test/progit/es/01-introduction/01-chapter1.markdown +262 -0
  101. data/test/progit/es/02-git-basics/01-chapter2.markdown +1165 -0
  102. data/test/progit/es/03-git-branching/01-chapter3.markdown +598 -0
  103. data/test/progit/es/04-git-server/01-chapter4.markdown +707 -0
  104. data/test/progit/es/05-distributed-git/01-chapter5.markdown +890 -0
  105. data/test/progit/es/06-git-tools/01-chapter6.markdown +1113 -0
  106. data/test/progit/es/07-customizing-git/01-chapter7.markdown +875 -0
  107. data/test/progit/es/08-git-and-other-scms/01-chapter8.markdown +686 -0
  108. data/test/progit/es/09-git-internals/01-chapter9.markdown +976 -0
  109. data/test/progit/es/NOTES +29 -0
  110. data/test/progit/es/README +3 -0
  111. data/test/progit/es/glosario-Benzirpi.txt +27 -0
  112. data/test/progit/es/omegat-Benzirpi.tmx +29075 -0
  113. data/test/progit/fa/01-introduction/01-chapter1.markdown +262 -0
  114. data/test/progit/fa/03-git-branching/01-chapter3.markdown +608 -0
  115. data/test/progit/fa/04-git-server/01-chapter4.markdown +872 -0
  116. data/test/progit/fa/NOTES.en-fa.md +143 -0
  117. data/test/progit/fa/README.md +7 -0
  118. data/test/progit/fi/01-introduction/01-chapter1.markdown +259 -0
  119. data/test/progit/fi/02-git-basics/01-chapter2.markdown +1171 -0
  120. data/test/progit/fi/NOTES +5 -0
  121. data/test/progit/figures-dia/fig0101.dia +617 -0
  122. data/test/progit/figures-dia/fig0102.dia +921 -0
  123. data/test/progit/figures-dia/fig0103.dia +1468 -0
  124. data/test/progit/figures-dia/fig0104.dia +1432 -0
  125. data/test/progit/figures-dia/fig0105.dia +1924 -0
  126. data/test/progit/figures-dia/fig0106.dia +562 -0
  127. data/test/progit/figures-dia/fig0201.dia +774 -0
  128. data/test/progit/figures-dia/fig0301.dia +2006 -0
  129. data/test/progit/figures-dia/fig0302.dia +2148 -0
  130. data/test/progit/figures-dia/fig0303.dia +719 -0
  131. data/test/progit/figures-dia/fig0304.dia +525 -0
  132. data/test/progit/figures-dia/fig0305.dia +622 -0
  133. data/test/progit/figures-dia/fig0306.dia +622 -0
  134. data/test/progit/figures-dia/fig0307.dia +719 -0
  135. data/test/progit/figures-dia/fig0308.dia +734 -0
  136. data/test/progit/figures-dia/fig0309.dia +831 -0
  137. data/test/progit/figures-dia/fig0310.dia +412 -0
  138. data/test/progit/figures-dia/fig0311.dia +493 -0
  139. data/test/progit/figures-dia/fig0312.dia +596 -0
  140. data/test/progit/figures-dia/fig0313.dia +774 -0
  141. data/test/progit/figures-dia/fig0314.dia +846 -0
  142. data/test/progit/figures-dia/fig0315.dia +787 -0
  143. data/test/progit/figures-dia/fig0316.dia +1078 -0
  144. data/test/progit/figures-dia/fig0317.dia +881 -0
  145. data/test/progit/figures-dia/fig0318.dia +968 -0
  146. data/test/progit/figures-dia/fig0319.dia +957 -0
  147. data/test/progit/figures-dia/fig0320.dia +1637 -0
  148. data/test/progit/figures-dia/fig0321.dia +1494 -0
  149. data/test/progit/figures-dia/fig0322.dia +1142 -0
  150. data/test/progit/figures-dia/fig0323.dia +1377 -0
  151. data/test/progit/figures-dia/fig0324.dia +1603 -0
  152. data/test/progit/figures-dia/fig0325.dia +2003 -0
  153. data/test/progit/figures-dia/fig0326.dia +2013 -0
  154. data/test/progit/figures-dia/fig0327.dia +687 -0
  155. data/test/progit/figures-dia/fig0328.dia +814 -0
  156. data/test/progit/figures-dia/fig0329.dia +793 -0
  157. data/test/progit/figures-dia/fig0330.dia +693 -0
  158. data/test/progit/figures-dia/fig0331.dia +1159 -0
  159. data/test/progit/figures-dia/fig0332.dia +1362 -0
  160. data/test/progit/figures-dia/fig0333.dia +1165 -0
  161. data/test/progit/figures-dia/fig0334.dia +1450 -0
  162. data/test/progit/figures-dia/fig0335.dia +994 -0
  163. data/test/progit/figures-dia/fig0336.dia +786 -0
  164. data/test/progit/figures-dia/fig0337.dia +1546 -0
  165. data/test/progit/figures-dia/fig0338.dia +1755 -0
  166. data/test/progit/figures-dia/fig0339.dia +1882 -0
  167. data/test/progit/figures-dia/fig0501.dia +456 -0
  168. data/test/progit/figures-dia/fig0502.dia +956 -0
  169. data/test/progit/figures-dia/fig0503.dia +915 -0
  170. data/test/progit/figures-dia/fig0504.dia +620 -0
  171. data/test/progit/figures-dia/fig0505.dia +744 -0
  172. data/test/progit/figures-dia/fig0506.dia +747 -0
  173. data/test/progit/figures-dia/fig0507.dia +895 -0
  174. data/test/progit/figures-dia/fig0508.dia +1122 -0
  175. data/test/progit/figures-dia/fig0509.dia +1243 -0
  176. data/test/progit/figures-dia/fig0510.dia +1240 -0
  177. data/test/progit/figures-dia/fig0511.dia +1201 -0
  178. data/test/progit/figures-dia/fig0512.dia +801 -0
  179. data/test/progit/figures-dia/fig0513.dia +1387 -0
  180. data/test/progit/figures-dia/fig0514.dia +1568 -0
  181. data/test/progit/figures-dia/fig0515.dia +1721 -0
  182. data/test/progit/figures-dia/fig0516.dia +997 -0
  183. data/test/progit/figures-dia/fig0517.dia +994 -0
  184. data/test/progit/figures-dia/fig0518.dia +1145 -0
  185. data/test/progit/figures-dia/fig0519.dia +992 -0
  186. data/test/progit/figures-dia/fig0520.dia +1240 -0
  187. data/test/progit/figures-dia/fig0521.dia +801 -0
  188. data/test/progit/figures-dia/fig0522.dia +922 -0
  189. data/test/progit/figures-dia/fig0523.dia +922 -0
  190. data/test/progit/figures-dia/fig0524.dia +1828 -0
  191. data/test/progit/figures-dia/fig0525.dia +2685 -0
  192. data/test/progit/figures-dia/fig0526.dia +717 -0
  193. data/test/progit/figures-dia/fig0527.dia +856 -0
  194. data/test/progit/figures-dia/fig0601.dia +790 -0
  195. data/test/progit/figures-dia/fig0702.dia +795 -0
  196. data/test/progit/figures-dia/fig0703.dia +795 -0
  197. data/test/progit/figures-dia/fig0901.dia +669 -0
  198. data/test/progit/figures-dia/fig0902.dia +834 -0
  199. data/test/progit/figures-dia/fig0903.dia +1483 -0
  200. data/test/progit/figures-dia/fig0904.dia +1728 -0
  201. data/test/progit/figures-dia/makeimages +25 -0
  202. data/test/progit/figures-source/progit.graffle +123108 -0
  203. data/test/progit/figures/18333fig0101-tn.png +0 -0
  204. data/test/progit/figures/18333fig0102-tn.png +0 -0
  205. data/test/progit/figures/18333fig0103-tn.png +0 -0
  206. data/test/progit/figures/18333fig0104-tn.png +0 -0
  207. data/test/progit/figures/18333fig0105-tn.png +0 -0
  208. data/test/progit/figures/18333fig0106-tn.png +0 -0
  209. data/test/progit/figures/18333fig0107-tn.png +0 -0
  210. data/test/progit/figures/18333fig0201-tn.png +0 -0
  211. data/test/progit/figures/18333fig0202-tn.png +0 -0
  212. data/test/progit/figures/18333fig0301-tn.png +0 -0
  213. data/test/progit/figures/18333fig0302-tn.png +0 -0
  214. data/test/progit/figures/18333fig0303-tn.png +0 -0
  215. data/test/progit/figures/18333fig0304-tn.png +0 -0
  216. data/test/progit/figures/18333fig0305-tn.png +0 -0
  217. data/test/progit/figures/18333fig0306-tn.png +0 -0
  218. data/test/progit/figures/18333fig0307-tn.png +0 -0
  219. data/test/progit/figures/18333fig0308-tn.png +0 -0
  220. data/test/progit/figures/18333fig0309-tn.png +0 -0
  221. data/test/progit/figures/18333fig0310-tn.png +0 -0
  222. data/test/progit/figures/18333fig0311-tn.png +0 -0
  223. data/test/progit/figures/18333fig0312-tn.png +0 -0
  224. data/test/progit/figures/18333fig0313-tn.png +0 -0
  225. data/test/progit/figures/18333fig0314-tn.png +0 -0
  226. data/test/progit/figures/18333fig0315-tn.png +0 -0
  227. data/test/progit/figures/18333fig0316-tn.png +0 -0
  228. data/test/progit/figures/18333fig0317-tn.png +0 -0
  229. data/test/progit/figures/18333fig0318-tn.png +0 -0
  230. data/test/progit/figures/18333fig0319-tn.png +0 -0
  231. data/test/progit/figures/18333fig0320-tn.png +0 -0
  232. data/test/progit/figures/18333fig0321-tn.png +0 -0
  233. data/test/progit/figures/18333fig0322-tn.png +0 -0
  234. data/test/progit/figures/18333fig0323-tn.png +0 -0
  235. data/test/progit/figures/18333fig0324-tn.png +0 -0
  236. data/test/progit/figures/18333fig0325-tn.png +0 -0
  237. data/test/progit/figures/18333fig0326-tn.png +0 -0
  238. data/test/progit/figures/18333fig0327-tn.png +0 -0
  239. data/test/progit/figures/18333fig0328-tn.png +0 -0
  240. data/test/progit/figures/18333fig0329-tn.png +0 -0
  241. data/test/progit/figures/18333fig0330-tn.png +0 -0
  242. data/test/progit/figures/18333fig0331-tn.png +0 -0
  243. data/test/progit/figures/18333fig0332-tn.png +0 -0
  244. data/test/progit/figures/18333fig0333-tn.png +0 -0
  245. data/test/progit/figures/18333fig0334-tn.png +0 -0
  246. data/test/progit/figures/18333fig0335-tn.png +0 -0
  247. data/test/progit/figures/18333fig0336-tn.png +0 -0
  248. data/test/progit/figures/18333fig0337-tn.png +0 -0
  249. data/test/progit/figures/18333fig0338-tn.png +0 -0
  250. data/test/progit/figures/18333fig0339-tn.png +0 -0
  251. data/test/progit/figures/18333fig0401-tn.png +0 -0
  252. data/test/progit/figures/18333fig0402-tn.png +0 -0
  253. data/test/progit/figures/18333fig0403-tn.png +0 -0
  254. data/test/progit/figures/18333fig0404-tn.png +0 -0
  255. data/test/progit/figures/18333fig0405-tn.png +0 -0
  256. data/test/progit/figures/18333fig0406-tn.png +0 -0
  257. data/test/progit/figures/18333fig0407-tn.png +0 -0
  258. data/test/progit/figures/18333fig0408-tn.png +0 -0
  259. data/test/progit/figures/18333fig0409-tn.png +0 -0
  260. data/test/progit/figures/18333fig0410-tn.png +0 -0
  261. data/test/progit/figures/18333fig0411-tn.png +0 -0
  262. data/test/progit/figures/18333fig0412-tn.png +0 -0
  263. data/test/progit/figures/18333fig0413-tn.png +0 -0
  264. data/test/progit/figures/18333fig0414-tn.png +0 -0
  265. data/test/progit/figures/18333fig0415-tn.png +0 -0
  266. data/test/progit/figures/18333fig0501-tn.png +0 -0
  267. data/test/progit/figures/18333fig0502-tn.png +0 -0
  268. data/test/progit/figures/18333fig0503-tn.png +0 -0
  269. data/test/progit/figures/18333fig0504-tn.png +0 -0
  270. data/test/progit/figures/18333fig0505-tn.png +0 -0
  271. data/test/progit/figures/18333fig0506-tn.png +0 -0
  272. data/test/progit/figures/18333fig0507-tn.png +0 -0
  273. data/test/progit/figures/18333fig0508-tn.png +0 -0
  274. data/test/progit/figures/18333fig0509-tn.png +0 -0
  275. data/test/progit/figures/18333fig0510-tn.png +0 -0
  276. data/test/progit/figures/18333fig0511-tn.png +0 -0
  277. data/test/progit/figures/18333fig0512-tn.png +0 -0
  278. data/test/progit/figures/18333fig0513-tn.png +0 -0
  279. data/test/progit/figures/18333fig0514-tn.png +0 -0
  280. data/test/progit/figures/18333fig0515-tn.png +0 -0
  281. data/test/progit/figures/18333fig0516-tn.png +0 -0
  282. data/test/progit/figures/18333fig0517-tn.png +0 -0
  283. data/test/progit/figures/18333fig0518-tn.png +0 -0
  284. data/test/progit/figures/18333fig0519-tn.png +0 -0
  285. data/test/progit/figures/18333fig0520-tn.png +0 -0
  286. data/test/progit/figures/18333fig0521-tn.png +0 -0
  287. data/test/progit/figures/18333fig0522-tn.png +0 -0
  288. data/test/progit/figures/18333fig0523-tn.png +0 -0
  289. data/test/progit/figures/18333fig0524-tn.png +0 -0
  290. data/test/progit/figures/18333fig0525-tn.png +0 -0
  291. data/test/progit/figures/18333fig0526-tn.png +0 -0
  292. data/test/progit/figures/18333fig0527-tn.png +0 -0
  293. data/test/progit/figures/18333fig0601-tn.png +0 -0
  294. data/test/progit/figures/18333fig0701-tn.png +0 -0
  295. data/test/progit/figures/18333fig0702-tn.png +0 -0
  296. data/test/progit/figures/18333fig0703-tn.png +0 -0
  297. data/test/progit/figures/18333fig0901-tn.png +0 -0
  298. data/test/progit/figures/18333fig0902-tn.png +0 -0
  299. data/test/progit/figures/18333fig0903-tn.png +0 -0
  300. data/test/progit/figures/18333fig0904-tn.png +0 -0
  301. data/test/progit/fr/01-introduction/01-chapter1.markdown +371 -0
  302. data/test/progit/fr/02-git-basics/01-chapter2.markdown +1378 -0
  303. data/test/progit/fr/03-git-branching/01-chapter3.markdown +781 -0
  304. data/test/progit/fr/04-git-server/01-chapter4.markdown +1141 -0
  305. data/test/progit/fr/05-distributed-git/01-chapter5.markdown +1163 -0
  306. data/test/progit/fr/06-git-tools/01-chapter6.markdown +1356 -0
  307. data/test/progit/fr/07-customizing-git/01-chapter7.markdown +1200 -0
  308. data/test/progit/fr/08-git-and-other-scms/01-chapter8.markdown +832 -0
  309. data/test/progit/fr/09-git-internals/01-chapter9.markdown +1228 -0
  310. data/test/progit/fr/NOTES.fr-fr.markdown +1 -0
  311. data/test/progit/fr/NOTES.fr-fr.md +127 -0
  312. data/test/progit/fr/README.md +43 -0
  313. data/test/progit/fr/glossaire-git.adoc +108 -0
  314. data/test/progit/hi/01-introduction/01-chapter1.markdown +7 -0
  315. data/test/progit/hu/01-introduction/01-chapter1.markdown +257 -0
  316. data/test/progit/id/01-introduction/01-chapter1.markdown +257 -0
  317. data/test/progit/id/02-git-basics/01-chapter2.markdown +1127 -0
  318. data/test/progit/id/03-git-branching/01-chapter3.markdown +598 -0
  319. data/test/progit/it/01-introduction/01-chapter1.markdown +263 -0
  320. data/test/progit/it/02-git-basics/01-chapter2.markdown +1227 -0
  321. data/test/progit/it/03-git-branching/01-chapter3.markdown +598 -0
  322. data/test/progit/it/04-git-server/01-chapter4.markdown +864 -0
  323. data/test/progit/it/05-distributed-git/01-chapter5.markdown +897 -0
  324. data/test/progit/it/06-git-tools/01-chapter6.markdown +1144 -0
  325. data/test/progit/it/07-customizing-git/01-chapter7.markdown +606 -0
  326. data/test/progit/it/08-git-and-other-scms/01-chapter8.markdown +707 -0
  327. data/test/progit/it/09-git-internals/01-chapter9.markdown +1000 -0
  328. data/test/progit/ja/01-introduction/01-chapter1.markdown +260 -0
  329. data/test/progit/ja/02-git-basics/01-chapter2.markdown +1221 -0
  330. data/test/progit/ja/03-git-branching/01-chapter3.markdown +604 -0
  331. data/test/progit/ja/04-git-server/01-chapter4.markdown +863 -0
  332. data/test/progit/ja/05-distributed-git/01-chapter5.markdown +908 -0
  333. data/test/progit/ja/06-git-tools/01-chapter6.markdown +1133 -0
  334. data/test/progit/ja/07-customizing-git/01-chapter7.markdown +936 -0
  335. data/test/progit/ja/08-git-and-other-scms/01-chapter8.markdown +690 -0
  336. data/test/progit/ja/09-git-internals/01-chapter9.markdown +984 -0
  337. data/test/progit/ja/README.md +58 -0
  338. data/test/progit/ja/translation glossaries.txt +33 -0
  339. data/test/progit/ko/01-introduction/01-chapter1.markdown +258 -0
  340. data/test/progit/ko/02-git-basics/01-chapter2.markdown +1181 -0
  341. data/test/progit/ko/03-git-branching/01-chapter3.markdown +612 -0
  342. data/test/progit/ko/04-git-server/01-chapter4.markdown +867 -0
  343. data/test/progit/ko/05-distributed-git/01-chapter5.markdown +913 -0
  344. data/test/progit/ko/06-git-tools/01-chapter6.markdown +1142 -0
  345. data/test/progit/ko/07-customizing-git/01-chapter7.markdown +935 -0
  346. data/test/progit/ko/08-git-and-other-scms/01-chapter8.markdown +688 -0
  347. data/test/progit/ko/09-git-internals/01-chapter9.markdown +976 -0
  348. data/test/progit/ko/README.md +75 -0
  349. data/test/progit/ko/translation_guide.txt +65 -0
  350. data/test/progit/latex/README +27 -0
  351. data/test/progit/latex/config.yml +144 -0
  352. data/test/progit/latex/makepdf +207 -0
  353. data/test/progit/latex/template.tex +155 -0
  354. data/test/progit/makeebooks +125 -0
  355. data/test/progit/makepdfs +47 -0
  356. data/test/progit/mk/01-introduction/01-chapter1.markdown +258 -0
  357. data/test/progit/mk/02-git-basics/01-chapter2.markdown +1125 -0
  358. data/test/progit/mk/03-git-branching/01-chapter3.markdown +598 -0
  359. data/test/progit/mk/05-distributed-git/01-chapter5.markdown +897 -0
  360. data/test/progit/nl/01-introduction/01-chapter1.markdown +296 -0
  361. data/test/progit/nl/02-git-basics/01-chapter2.markdown +1253 -0
  362. data/test/progit/nl/03-git-branching/01-chapter3.markdown +642 -0
  363. data/test/progit/nl/04-git-server/01-chapter4.markdown +902 -0
  364. data/test/progit/nl/05-distributed-git/01-chapter5.markdown +953 -0
  365. data/test/progit/nl/06-git-tools/01-chapter6.markdown +1177 -0
  366. data/test/progit/nl/07-customizing-git/01-chapter7.markdown +974 -0
  367. data/test/progit/nl/08-git-and-other-scms/01-chapter8.markdown +725 -0
  368. data/test/progit/nl/09-git-internals/01-chapter9.markdown +1013 -0
  369. data/test/progit/no-nb/01-introduction/01-chapter1.markdown +261 -0
  370. data/test/progit/no-nb/02-git-basics/01-chapter2.markdown +1225 -0
  371. data/test/progit/no-nb/03-git-branching/01-chapter3.markdown +606 -0
  372. data/test/progit/no-nb/04-git-server/01-chapter4.markdown +867 -0
  373. data/test/progit/no-nb/05-distributed-git/01-chapter5.markdown +914 -0
  374. data/test/progit/no-nb/06-git-tools/01-chapter6.markdown +1144 -0
  375. data/test/progit/no-nb/07-customizing-git/01-chapter7.markdown +936 -0
  376. data/test/progit/no-nb/08-git-and-other-scms/01-chapter8.markdown +689 -0
  377. data/test/progit/no-nb/09-git-internals/01-chapter9.markdown +977 -0
  378. data/test/progit/no-nb/README +2 -0
  379. data/test/progit/pl/01-introduction/01-chapter1.markdown +257 -0
  380. data/test/progit/pl/02-git-basics/02-chapter2.markdown +1128 -0
  381. data/test/progit/pl/03-git-branching/01-chapter3.markdown +598 -0
  382. data/test/progit/pl/04-git-server/01-chapter4.markdown +897 -0
  383. data/test/progit/pl/05-distributed-git/01-chapter5.markdown +1278 -0
  384. data/test/progit/pl/06-git-tools/01-chapter6.markdown +1550 -0
  385. data/test/progit/pl/07-customizing-git/01-chapter7.markdown +1058 -0
  386. data/test/progit/pl/08-git-and-other-scms/01-chapter8.markdown +948 -0
  387. data/test/progit/pl/09-git-internals/01-chapter9.markdown +1382 -0
  388. data/test/progit/pl/translation-guidelines.txt +70 -0
  389. data/test/progit/pt-br/01-introduction/01-chapter1.markdown +256 -0
  390. data/test/progit/pt-br/02-git-basics/01-chapter2.markdown +1127 -0
  391. data/test/progit/pt-br/03-git-branching/01-chapter3.markdown +596 -0
  392. data/test/progit/pt-br/04-git-server/01-chapter4.markdown +888 -0
  393. data/test/progit/pt-br/05-distributed-git/01-chapter5.markdown +896 -0
  394. data/test/progit/pt-br/06-git-tools/01-chapter6.markdown +1122 -0
  395. data/test/progit/pt-br/07-customizing-git/01-chapter7.markdown +932 -0
  396. data/test/progit/pt-br/08-git-and-other-scms/01-chapter8.markdown +691 -0
  397. data/test/progit/pt-br/09-git-internals/01-chapter9.markdown +978 -0
  398. data/test/progit/pt-br/figures-dia/fig0101.dia +617 -0
  399. data/test/progit/pt-br/figures-dia/fig0102.dia +921 -0
  400. data/test/progit/pt-br/figures-dia/fig0103.dia +1468 -0
  401. data/test/progit/pt-br/figures-dia/fig0104.dia +1432 -0
  402. data/test/progit/pt-br/figures-dia/fig0105.dia +1924 -0
  403. data/test/progit/pt-br/figures-dia/fig0106.dia +562 -0
  404. data/test/progit/pt-br/figures-dia/fig0201.dia +776 -0
  405. data/test/progit/pt-br/figures-dia/fig0301.dia +2006 -0
  406. data/test/progit/pt-br/figures-dia/fig0302.dia +2148 -0
  407. data/test/progit/pt-br/figures-dia/fig0316.dia +1079 -0
  408. data/test/progit/pt-br/figures-dia/fig0322.dia +1142 -0
  409. data/test/progit/pt-br/figures-dia/fig0323.dia +1407 -0
  410. data/test/progit/pt-br/figures-dia/fig0324.dia +1603 -0
  411. data/test/progit/pt-br/figures-dia/fig0325.dia +2003 -0
  412. data/test/progit/pt-br/figures-dia/fig0326.dia +2013 -0
  413. data/test/progit/pt-br/figures-dia/fig0336.dia +786 -0
  414. data/test/progit/pt-br/figures-dia/fig0337.dia +1546 -0
  415. data/test/progit/pt-br/figures-dia/fig0338.dia +1755 -0
  416. data/test/progit/pt-br/figures-dia/fig0339.dia +1882 -0
  417. data/test/progit/pt-br/figures-dia/fig0501.dia +456 -0
  418. data/test/progit/pt-br/figures-dia/fig0502.dia +965 -0
  419. data/test/progit/pt-br/figures-dia/fig0503.dia +914 -0
  420. data/test/progit/pt-br/figures-dia/fig0511.dia +1201 -0
  421. data/test/progit/pt-br/figures-dia/fig0515.dia +1721 -0
  422. data/test/progit/pt-br/figures-dia/fig0702.dia +795 -0
  423. data/test/progit/pt-br/figures-dia/fig0703.dia +795 -0
  424. data/test/progit/pt-br/figures-dia/fig0901.dia +669 -0
  425. data/test/progit/pt-br/figures-dia/fig0902.dia +834 -0
  426. data/test/progit/pt-br/figures-dia/fig0903.dia +1483 -0
  427. data/test/progit/pt-br/figures-dia/fig0904.dia +1728 -0
  428. data/test/progit/ro/01-introduction/01-chapter1.markdown +257 -0
  429. data/test/progit/ru/01-introduction/01-chapter1.markdown +259 -0
  430. data/test/progit/ru/02-git-basics/01-chapter2.markdown +1155 -0
  431. data/test/progit/ru/03-git-branching/01-chapter3.markdown +598 -0
  432. data/test/progit/ru/04-git-server/01-chapter4.markdown +854 -0
  433. data/test/progit/ru/05-distributed-git/01-chapter5.markdown +897 -0
  434. data/test/progit/ru/06-git-tools/01-chapter6.markdown +1126 -0
  435. data/test/progit/ru/07-customizing-git/01-chapter7.markdown +938 -0
  436. data/test/progit/ru/08-git-and-other-scms/01-chapter8.markdown +691 -0
  437. data/test/progit/ru/09-git-internals/01-chapter9.markdown +977 -0
  438. data/test/progit/ru/Glossary +38 -0
  439. data/test/progit/ru/README +12 -0
  440. data/test/progit/ru/figures-dia/fig0101.dia +647 -0
  441. data/test/progit/ru/figures-dia/fig0102.dia +1009 -0
  442. data/test/progit/ru/figures-dia/fig0103.dia +1468 -0
  443. data/test/progit/ru/figures-dia/fig0104.dia +1432 -0
  444. data/test/progit/ru/figures-dia/fig0105.dia +1924 -0
  445. data/test/progit/ru/figures-dia/fig0106.dia +561 -0
  446. data/test/progit/ru/figures-dia/fig0201.dia +774 -0
  447. data/test/progit/ru/figures-dia/fig0322.dia +1182 -0
  448. data/test/progit/ru/figures-dia/fig0323.dia +1457 -0
  449. data/test/progit/ru/figures-dia/fig0324.dia +1698 -0
  450. data/test/progit/ru/figures-dia/fig0325.dia +2101 -0
  451. data/test/progit/ru/figures-dia/fig0326.dia +2111 -0
  452. data/test/progit/ru/figures-dia/fig0336.dia +786 -0
  453. data/test/progit/ru/figures-dia/fig0337.dia +1546 -0
  454. data/test/progit/ru/figures-dia/fig0338.dia +1755 -0
  455. data/test/progit/ru/figures-dia/fig0339.dia +1882 -0
  456. data/test/progit/ru/figures-dia/fig0501.dia +477 -0
  457. data/test/progit/ru/figures-dia/fig0502.dia +1063 -0
  458. data/test/progit/ru/figures-dia/fig0503.dia +915 -0
  459. data/test/progit/ru/figures-dia/fig0511.dia +1201 -0
  460. data/test/progit/ru/figures-dia/fig0515.dia +1741 -0
  461. data/test/progit/ru/figures-dia/fig0702.dia +851 -0
  462. data/test/progit/ru/figures-dia/fig0703.dia +851 -0
  463. data/test/progit/sr/01-introduction/01-chapter1.markdown +257 -0
  464. data/test/progit/summary.rb +29 -0
  465. data/test/progit/th/01-introduction/01-chapter1.markdown +257 -0
  466. data/test/progit/th/02-git-basics/01-chapter2.markdown +1126 -0
  467. data/test/progit/th/README.md +47 -0
  468. data/test/progit/tr/01-introduction/01-chapter1.markdown +258 -0
  469. data/test/progit/tr/02-git-basics/01-chapter2.markdown +1129 -0
  470. data/test/progit/tr/03-git-branching/01-chapter3.markdown +598 -0
  471. data/test/progit/tr/04-git-server/01-chapter4.markdown +73 -0
  472. data/test/progit/tr/05-distributed-git/01-chapter5.markdown +215 -0
  473. data/test/progit/uk/01-introduction/01-chapter1.markdown +522 -0
  474. data/test/progit/vi/01-introduction/01-chapter1.markdown +259 -0
  475. data/test/progit/vi/02-git-basics/01-chapter2.markdown +1172 -0
  476. data/test/progit/vi/03-git-branching/01-chapter3.markdown +598 -0
  477. data/test/progit/zh-tw/01-introduction/01-chapter1.markdown +259 -0
  478. data/test/progit/zh-tw/02-git-basics/01-chapter2.markdown +1183 -0
  479. data/test/progit/zh-tw/03-git-branching/01-chapter3.markdown +604 -0
  480. data/test/progit/zh-tw/04-git-server/01-chapter4.markdown +866 -0
  481. data/test/progit/zh-tw/05-distributed-git/01-chapter5.markdown +912 -0
  482. data/test/progit/zh-tw/06-git-tools/01-chapter6.markdown +1139 -0
  483. data/test/progit/zh-tw/07-customizing-git/01-chapter7.markdown +932 -0
  484. data/test/progit/zh-tw/08-git-and-other-scms/01-chapter8.markdown +689 -0
  485. data/test/progit/zh-tw/09-git-internals/01-chapter9.markdown +977 -0
  486. data/test/progit/zh/01-introduction/01-chapter1.markdown +259 -0
  487. data/test/progit/zh/02-git-basics/01-chapter2.markdown +1177 -0
  488. data/test/progit/zh/03-git-branching/01-chapter3.markdown +604 -0
  489. data/test/progit/zh/04-git-server/01-chapter4.markdown +866 -0
  490. data/test/progit/zh/05-distributed-git/01-chapter5.markdown +912 -0
  491. data/test/progit/zh/06-git-tools/01-chapter6.markdown +1125 -0
  492. data/test/progit/zh/07-customizing-git/01-chapter7.markdown +935 -0
  493. data/test/progit/zh/08-git-and-other-scms/01-chapter8.markdown +689 -0
  494. data/test/progit/zh/09-git-internals/01-chapter9.markdown +976 -0
  495. data/test/spec_tests.json +4382 -4070
  496. data/test/test_basics.rb +1 -1
  497. data/test/test_helper.rb +1 -0
  498. data/test/test_maliciousness.rb +4 -2
  499. data/test/test_pathological_inputs.rb +31 -30
  500. data/test/test_spec.rb +5 -4
  501. metadata +972 -4
@@ -0,0 +1,932 @@
1
+ # Git 客製化 #
2
+
3
+ 到目前為止,我闡述了 Git 基本的運作機制和使用方式,介紹了 Git 提供的許多工具來幫助你簡單且有效地使用它。在本章,我將會介紹 Git 的一些重要的組態設定(configuration)和鉤子(hooks)機制以滿足自訂的要求。通過這些工具,它能夠更容易地使 Git 按照你、你的公司或團隊所需要的方式去運作。
4
+
5
+ ## Git 配置 ##
6
+
7
+ 如第一章所言,用 `git config` 配置 Git,要做的第一件事就是設置名字和郵箱地址:
8
+
9
+ $ git config --global user.name "John Doe"
10
+ $ git config --global user.email johndoe@example.com
11
+
12
+ 從現在開始,你會瞭解到一些類似以上但更為有趣的設置選項來自訂 Git。
13
+
14
+ 先過一遍第一章中提到的 Git 配置細節。Git 使用一系列的設定檔來儲存你定義的偏好,它首先會查找 `/etc/gitconfig` 檔,該檔所含的設定值對系統上所有使用者都有效,也對他們所擁有的倉庫都有效(譯注:gitconfig 是全域設定檔), 如果傳遞 `--system` 選項給 `git config` 命令, Git 會讀寫這個檔。
15
+
16
+ 接下來 Git 會尋找每個用戶的 `~/.gitconfig` 檔,它是針對個別使用者的,你可以傳遞 `--global` 選項讓 Git 讀寫該檔。
17
+
18
+ 最後,Git 會尋找你目前使用中的倉庫 Git 目錄下的設定檔(`.git/config`),該文件中的設定值只對這個倉庫有效。以上闡述的三層配置從一般到特殊層層推進,如果定義的值有衝突,後面層級的設定會面寫前面層級的設定值,例如:在 `.git/config` 和 `/etc/gitconfig` 的較量中, `.git/config` 取得了勝利。雖然你也可以直接手動編輯這些設定檔,但是執行 git config 命令將會來得簡單些。
19
+
20
+ ### 用戶端基本配置 ###
21
+
22
+ Git 能夠識別的配置項被分為了兩大類:用戶端和伺服器端,其中大部分屬於用戶端配置,這是基於你個人工作偏好所做的設定。儘管有數不盡的選項,但我只闡述其中經常使用、或者會對你的工作流程產生巨大影響的選項。許多選項只在極端的情況下有用,這裏就不多做介紹了。如果你想觀察你的 Git 版本能識別的選項清單,請執行
23
+
24
+ $ git config --help
25
+
26
+ `git config` 的手冊頁(譯注:以 man 命令的顯示方式)非常細緻地羅列了所有可用的配置項。
27
+
28
+ #### core.editor ####
29
+
30
+ 預設情況下,Git 會使用你所設定的「預設文字編輯器」,否則會使用 Vi 來創建和編輯提交以及標籤資訊,你可以使用 `core.editor` 改變預設編輯器:
31
+
32
+ $ git config --global core.editor emacs
33
+
34
+ 現在無論你的環境變數 editor 被定義成什麼,Git 都會觸發 Emacs 來編輯相關訊息。
35
+
36
+ #### commit.template ####
37
+
38
+ 如果把這個項目指定為你系統上的一個檔,當你提交的時候,Git 會預設使用該檔定義的內容做為預設的提交訊息。例如:你創建了一個範本檔 `$HOME/.gitmessage.txt`,它看起來像這樣:
39
+
40
+ subject line
41
+
42
+ what happened
43
+
44
+ [ticket: X]
45
+
46
+ 如下設定 `commit.template` 可以告訴 Git,把上列內容做為預設訊息,當你執行 `git commit` 的時候,在你的編輯器中顯示:
47
+
48
+ $ git config --global commit.template $HOME/.gitmessage.txt
49
+ $ git commit
50
+
51
+ 然後當你提交時,在編輯器中顯示的提交資訊如下:
52
+
53
+ subject line
54
+
55
+ what happened
56
+
57
+ [ticket: X]
58
+ # Please enter the commit message for your changes. Lines starting
59
+ # with '#' will be ignored, and an empty message aborts the commit.
60
+ # On branch master
61
+ # Changes to be committed:
62
+ # (use "git reset HEAD <file>..." to unstage)
63
+ #
64
+ # modified: lib/test.rb
65
+ #
66
+ ~
67
+ ~
68
+ ".git/COMMIT_EDITMSG" 14L, 297C
69
+
70
+ 如果你對於提交訊息有特定的政策,那就在系統上創建一個範本檔,設定 Git 使用它做為預設值,這樣可以幫助提升你的政策經常被遵守的機會。
71
+
72
+ #### core.pager ####
73
+
74
+ core.pager 指定 Git 執行諸如 log、diff 等命令時所使用的分頁器,你可以設成用 `more` 或者任何你喜歡的分頁器(預設用的是 `less`), 當然你也可以不用分頁器,只要把它設成空字串:
75
+
76
+ $ git config --global core.pager ''
77
+
78
+ 這樣不管命令的輸出量多少,都會在一頁顯示所有內容。
79
+
80
+ #### user.signingkey ####
81
+
82
+ 如果你要創建經簽署的含附注的標籤(signed annotated tags)(正如第二章所述),那麼把你的 GPG 簽署金鑰設為配置項會更好,設置金鑰 ID 如下:
83
+
84
+ $ git config --global user.signingkey <gpg-key-id>
85
+
86
+ 現在你能夠簽署標籤,從而不必每次執行 `git tag` 命令時定義金鑰:
87
+
88
+ $ git tag -s <tag-name>
89
+
90
+ #### core.excludesfile ####
91
+
92
+ 正如第二章所述,你能在專案倉庫的 `.gitignore` 檔裡頭用模式(pattern)來定義那些無需納入 Git 管理的檔案,這樣它們不會出現在未追蹤列表,也不會在你執行 `git add` 後被暫存。然而,如果你想用專案倉庫之外的檔案來定義那些需被忽略的檔的話,可以用 `core.excludesfile` 來通知 Git 該檔所在的位置,檔案內容則和 .gitignore 類似。
93
+
94
+ #### help.autocorrect ####
95
+
96
+ 這個選置項只在 Git 1.6.1 以上(含)版本有效,假如你在 Git 1.6 中錯打了一條命令,它會像這樣顯示:
97
+
98
+ $ git com
99
+ git: 'com' is not a git-command. See 'git --help'.
100
+
101
+ Did you mean this?
102
+ commit
103
+
104
+ 如果你把 help.autocorrect 設置成1(譯注:啟動自動修正),那麼在只有一個命令符合的情況下,Git 會自動執行該命令。
105
+
106
+ ### Git 中的著色 ###
107
+
108
+ Git 能夠為輸出到你終端(terminal)的內容著色,以便你可以憑直觀進行快速、簡單地分析,有許多選項能幫你將顏色調成你喜好的。
109
+
110
+ #### color.ui ####
111
+
112
+ Git 會按照你的需要,自動為大部分的輸出加上顏色。你能明確地規定哪些需要著色、以及怎樣著色,設置 color.ui 為 true 來打開所有的預設終端著色:
113
+
114
+ $ git config --global color.ui true
115
+
116
+ 設置好以後,當輸出到終端時,Git 會為之加上顏色。其他的參數還有 false 和 always,false 意味著不為輸出著色,而 always 則表示在任何情況下都要著色 -- 即使 Git 命令被重定向到文件或 pipe 到另一個命令。Git 1.5.5 版本引進了此項配置,如果你的版本更舊,你必須對顏色有關選項各自進行詳細地設置。
117
+
118
+ 你會很少用到 `color.ui = always`,在大多數情況下,如果你想在被重定向的輸出中插入顏色碼,你可以傳遞 `--color` 旗標給 Git 命令來迫使它這麼做,`color.ui = true` 應該是你的首選。
119
+
120
+ #### `color.*` ####
121
+
122
+ 想要具體指定哪些命令輸出需要被著色,以及怎樣著色,或者 Git 的版本很舊,你就要用到和具體命令有關的顏色配置選項,它們都能被設為 true、false 或 always:
123
+
124
+ color.branch
125
+ color.diff
126
+ color.interactive
127
+ color.status
128
+
129
+ 除此之外,以上每個選項都有子選項,可以被用來覆蓋其父設置,以達到為輸出的各個部分著色的目的。例如,要讓 diff 輸出的改變資訊 (meta information) 以粗體、藍色前景和黑色背景的形式顯示,你可以執行:
130
+
131
+ $ git config --global color.diff.meta "blue black bold"
132
+
133
+ 你能設置的顏色值如下:normal、black、red、green、yellow、blue、magenta、cyan、white。正如以上例子設置的粗體屬性,想要設置字體屬性的話,你的選擇有:bold、dim、ul、blink、reverse。
134
+
135
+ 如果你想配置子選項的話,可以參考 git config 幫助頁。
136
+
137
+ ### 外部的合併與比較工具 ###
138
+
139
+ 雖然 Git 自己實做了 diff,而且到目前為止你一直在使用它,但你能夠設定一個外部的工具來替代它。你還可以設定用一個圖形化的工具來合併和解決衝突,而不必自己手動解決。有一個不錯且免費的工具可以被用來做比較和合併工作,它就是 P4Merge(譯注:Perforce 圖形化合併工具),我會展示它的安裝過程。
140
+
141
+ 所以如果你想試試看的話,因為 P4Merge 可以在所有主流平臺上運行,所以你應該可以嘗試看看。對於向你展示的例子,在 Mac 和 Linux 系統上,我會使用路徑名;在 Windows 上,`/usr/local/bin` 應該被改為你環境中的可執行路徑。
142
+
143
+ 你可以在這裏下載 P4Merge:
144
+
145
+ http://www.perforce.com/product/components/perforce-visual-merge-and-diff-tools
146
+
147
+ 首先,你要設定一個外部包裝腳本(external wrapper scripts)來執行你要的命令,我會使用 Mac 系統上的路徑來指定該腳本的位置;在其他系統上,它應該被放置在二進位檔案 `p4merge` 所在的目錄中。創建一個 merge 包裝腳本,名字叫作 `extMerge`,讓它附帶所有參數呼叫 p4merge 二進位檔案:
148
+
149
+ $ cat /usr/local/bin/extMerge
150
+ #!/bin/sh
151
+ /Applications/p4merge.app/Contents/MacOS/p4merge $*
152
+
153
+ diff 包裝腳本首先確定傳遞過來7個參數,隨後把其中2個傳遞給你的 merge 包裝腳本,預設情況下,Git 會傳遞以下參數給 diff:
154
+
155
+ path old-file old-hex old-mode new-file new-hex new-mode
156
+
157
+ 由於你僅僅需要 `old-file` 和 `new-file` 參數,用 diff 包裝腳本來傳遞它們吧。
158
+
159
+ $ cat /usr/local/bin/extDiff
160
+ #!/bin/sh
161
+ [ $# -eq 7 ] && /usr/local/bin/extMerge "$2" "$5"
162
+
163
+ 你還需要確認一下這兩個腳本是可執行的:
164
+
165
+ $ sudo chmod +x /usr/local/bin/extMerge
166
+ $ sudo chmod +x /usr/local/bin/extDiff
167
+
168
+ 現在來設定使用你自訂的比較和合併工具吧。這需要許多自訂設置:`merge.tool` 通知 Git 使用哪個合併工具;`mergetool.*.cmd` 規定命令如何執行;`mergetool.trustExitCode` 會通知 Git 該程式的退出碼(exit code)是否指示合併操作成功;`diff.external` 通知 Git 用什麼命令做比較。因此,你可以執行以下4條配置命令:
169
+
170
+ $ git config --global merge.tool extMerge
171
+ $ git config --global mergetool.extMerge.cmd \
172
+ 'extMerge "$BASE" "$LOCAL" "$REMOTE" "$MERGED"'
173
+ $ git config --global mergetool.trustExitCode false
174
+ $ git config --global diff.external extDiff
175
+
176
+ 或者直接編輯 `~/.gitconfig` 文件如下:
177
+
178
+ [merge]
179
+ tool = extMerge
180
+ [mergetool "extMerge"]
181
+ cmd = extMerge \"$BASE\" \"$LOCAL\" \"$REMOTE\" \"$MERGED\"
182
+ trustExitCode = false
183
+ [diff]
184
+ external = extDiff
185
+
186
+ 設置完畢後,如果你像這樣執行 diff 命令:
187
+
188
+ $ git diff 32d1776b1^ 32d1776b1
189
+
190
+ 不同於在命令列得到 diff 命令的輸出,Git 觸發了剛剛設置的 P4Merge,它看起來像圖7-1這樣:
191
+
192
+ Insert 18333fig0701.png
193
+ Figure 7-1. P4Merge.
194
+
195
+ 當你設法合併兩個分支,結果卻有衝突時,執行 `git mergetool`,Git 會啟用 P4Merge 讓你通過圖形介面來解決衝突。
196
+
197
+ 設置包裝腳本的好處是你能簡單地改變 diff 和 merge 工具,例如把 `extDiff` 和 `extMerge` 改成 KDiff3,要做的僅僅是編輯 `extMerge` 指令檔:
198
+
199
+ $ cat /usr/local/bin/extMerge
200
+ #!/bin/sh
201
+ /Applications/kdiff3.app/Contents/MacOS/kdiff3 $*
202
+
203
+ 現在 Git 會使用 KDiff3 來做比較、合併和解決衝突。
204
+
205
+ Git 預先設置了許多其他的合併和解決衝突的工具,而你不必設置 cmd。可以把合併工具設置為:kdiff3、opendiff、tkdiff、meld、xxdiff、emerge、vimdiff、gvimdiff。如果你不想用 KDiff3 來做 diff,只是想用它來合併,而且 kdiff3 命令也在你的路徑裏,那麼你可以執行:
206
+
207
+ $ git config --global merge.tool kdiff3
208
+
209
+ 如果執行了以上命令,沒有設置 `extMerge` 和 `extDiff` 檔,Git 會用 KDiff3 做合併,讓平常內建的比較工具來做比較。
210
+
211
+ ### 格式化與空格 ###
212
+
213
+ 格式化與空格是許多開發人員在協同工作時,特別是在跨平臺情況下,遇到的令人頭疼的細小問題。在一些大家合作的工作或提交的補丁中,很容易因為編輯器安靜無聲地加入一些小空格,或者 Windows 程式師在跨平臺專案中的檔案行尾加入了回車分行符號(carriage return)。Git 的一些配置選項可以幫助解決這些問題。
214
+
215
+ #### core.autocrlf ####
216
+
217
+ 如果你在 Windows 上寫程式,或者你不是用 Windows,但和其他在 Windows 上寫程式的人合作,在這些情況下,你可能會遇到換行符號的問題。這是因為 Windows 使用回車(carriage-return)和換行(linefeed)兩個字元來結束一行,而 Mac 和 Linux 只使用一個換行字元。雖然這是小問題,但它會極大地擾亂跨平臺協作。
218
+
219
+ Git 可以在你提交時自動地把換行符號 CRLF 轉換成 LF,而在簽出代碼時把 LF 轉換成 CRLF。用 `core.autocrlf` 來打開此項功能,如果是在Windows 系統上,把它設置成 `true`,這樣當 check out 程式的時候,LF 會被轉換成 CRLF:
220
+
221
+ $ git config --global core.autocrlf true
222
+
223
+ Linux 或 Mac 系統使用 LF 作為行結束符,因此你不希望 Git 在 check out 檔案時進行自動的轉換;但是,當一個以 CRLF 做為換行符號的檔案不小心被引入時,你肯定希望 Git 可以修正它。你可以把 `core.autocrlf` 設置成 input 來告訴 Git 在提交時把 CRLF 轉換成 LF,check out 時不轉換:
224
+
225
+ $ git config --global core.autocrlf input
226
+
227
+ 這樣會在 Windows 系統上的 check out 檔案中保留 CRLF,而在 Mac 和 Linux 系統上,以及倉庫中保留 LF。
228
+
229
+ 如果你是 Windows 程式師,且正在開發僅運行在 Windows 上的專案,可以設置 `false` 取消此功能,把 carriage returns 記錄在倉庫中:
230
+
231
+ $ git config --global core.autocrlf false
232
+
233
+ #### core.whitespace ####
234
+
235
+ Git 預先設置了一些選項來探測和修正空格問題,其中有四個主要選項,有2個預設開啟,2個預設關閉,你可以自由地打開或關閉它們。
236
+
237
+ 預設開啟的2個選項是:`trailing-space` 會查找每行結尾的空格,`space-before-tab` 會查找每行開頭的定位字元前的空格。
238
+
239
+ 預設關閉的2個選項是:`indent-with-non-tab` 會查找8個以上空格(非定位字元)開頭的行,`cr-at-eol` 告訴 Git carriage returns 是合法的。
240
+
241
+ 設置 `core.whitespace`,按照你的需要來打開或關閉選項,設定值之間以逗號分隔。從設定字串裏把設定值去掉,就可以關閉這個設定,或是在設定值前面加上減號 `-` 也可以。例如,如果你想要打開除了 cr-at-eol 之外的所有選項,你可以這麼做:
242
+
243
+ $ git config --global core.whitespace \
244
+ trailing-space,space-before-tab,indent-with-non-tab
245
+
246
+ 當你執行 `git diff` 命令且為輸出著色時,Git 會偵測這些問題,因此你有可能在提交前修復它們。當你用 `git apply` 打補丁時,它也同樣會使用這些設定值來幫助你。你可以要 Git 警告你,如果正準備運用的補丁有特別的空白問題:
247
+
248
+ $ git apply --whitespace=warn <patch>
249
+
250
+ 或者讓 Git 在打上補丁嘗試自動修正此問題:
251
+
252
+ $ git apply --whitespace=fix <patch>
253
+
254
+ 這些選項也能運用於衍合。如果提交了有空格問題的檔但還沒推送到上游,你可以執行帶有 `--whitespace=fix` 選項的 `rebase` 來讓 Git 在重寫補丁時自動修正它們。
255
+
256
+ ### 伺服器端配置 ###
257
+
258
+ Git 伺服器端的配置選項並不多,但仍有一些有趣的選項值得你一看。
259
+
260
+ #### receive.fsckObjects ####
261
+
262
+ Git 預設情況下不會在推送期間檢查所有物件的一致性。Git 雖然會檢查確認每個物件仍然符合它的 SHA-1 checksum,所指向的物件也都是有效的,但是預設 Git 不會在每次推送時都做這種檢查。對於 Git 來說,倉庫或推送的檔越大,這個操作代價就相對越高,每次推送會消耗更多時間。如果想讓 Git 在每次推送時都檢查物件一致性,可以設定 `receive.fsckObjects` 為 true 來強迫它這麼做:
263
+
264
+ $ git config --system receive.fsckObjects true
265
+
266
+ 現在 Git 會在每次推送被接受前檢查庫的完整性,確保有問題的用戶端沒有引入破壞性的資料。
267
+
268
+ #### receive.denyNonFastForwards ####
269
+
270
+ 如果對已經被推送的提交歷史做衍合,繼而再推送;或是要將某個提交推送到遠端分支,而該提交歷史未包含這個遠端分支目前指向的 commit,這樣的推送會被拒絕。這通常是個很好的禁止策略,但有時你在做衍合的時候,你可能很確定自己在做什麼,那就可以在 push 命令後加 `-f` 旗標來強制更新遠端分支。
271
+
272
+ 要禁用這樣的強制更新遠端分支 non-fast-forward references 的功能,可以如下設定 `receive.denyNonFastForwards`:
273
+
274
+ $ git config --system receive.denyNonFastForwards true
275
+
276
+ 稍後你會看到,用伺服器端的 receive hooks 也能達到同樣的目的。這個方法可以做更細緻的控制,例如:拒絕某些特定的使用者強制更新 non-fast-forwards。
277
+
278
+ #### receive.denyDeletes ####
279
+
280
+ 避開 `denyNonFastForwards` 策略的方法之一就是使用者刪除分支,然後推回新的引用(reference)。在更新的 Git 版本中(從1.6.1版本開始),你可以把 receive.denyDeletes 設置為 true:
281
+
282
+ $ git config --system receive.denyDeletes true
283
+
284
+ 這樣會在推送過程中阻止刪除分支和標籤 — 沒有使用者能夠這麼做。要刪除遠端分支,必須從伺服器手動刪除引用檔(ref files)。通過用戶存取控制清單也能這麼做,在本章結尾將會介紹這些有趣的方式。
285
+
286
+ ## Git 屬性 ##
287
+
288
+ 一些設定值(settings)也能指定到特定的路徑,這樣,Git 只對這個特定的子目錄或某些檔案應用這些設定值。這些針對特定路徑的設定值被稱為 Git 屬性(attributes),可以在你目錄中的 `.gitattributes` 檔內進行設置(通常是你專案的根目錄),當你不想讓這些屬性檔和專案檔案一同提交時,也可以在 `.git/info/attributes` 檔進行設置。
289
+
290
+ 使用屬性,你可以對個別檔案或目錄定義不同的合併策略,讓 Git 知道怎樣比較非文字檔,在你提交或簽出(check out)前讓 Git 過濾內容。你將在這個章節裏瞭解到能在自己的專案中使用的屬性,以及一些實例。
291
+
292
+ ### 二進位檔案 ###
293
+
294
+ 你可以用 Git 屬性讓其知道哪些是二進位檔案(以防 Git 沒有識別出來),以及指示怎樣處理這些檔,這點很酷。例如,一些文字檔是由機器產生的,而且無法比較,而一些二進位檔案可以比較 — 你將會瞭解到怎樣讓 Git 識別這些檔。
295
+
296
+ #### 識別二進位檔案 ####
297
+
298
+ 某些檔案看起來像是文字檔,但其實是看做為二進位資料。例如,在 Mac 上的 Xcode 專案含有一個以 `.pbxproj` 結尾的檔,它是由記錄設置項的 IDE 寫到磁碟的 JSON 資料集(純文字 javascript 資料類型)。雖然技術上看它是由 ASCII 字元組成的文字檔,但是你並不想這麼看它,因為它確實是一個輕量級資料庫 — 如果有兩個人改變了它,你沒辦法合併它們,diff 通常也幫不上忙,只有機器才能進行識別和操作,於是,你想把它當成二進位檔案。
299
+
300
+ 讓 Git 把所有 `pbxproj` 檔當成二進位檔案,在 `.gitattributes` 文件中加上下面這行:
301
+
302
+ *.pbxproj -crlf -diff
303
+
304
+ 現在,Git 不會嘗試轉換和修正 CRLF(回車換行)問題;也不會當你在專案中執行 git show 或 git diff 時,嘗試比較不同的內容。在 Git 1.6 及之後的版本中,可以用一個巨集代替 `-crlf -diff`:
305
+
306
+ *.pbxproj binary
307
+
308
+ #### Diffing Binary Files ####
309
+
310
+ 在 Git 1.6 及以上版本中,你能利用 Git 屬性來有效地比較二進位檔案。可以設置 Git 把二進位資料轉換成文本格式,然後用一般 diff 來做比較。
311
+
312
+ ##### MS Word files #####
313
+
314
+ 這個特性很酷,而且鮮為人知,因此我會結合實例來講解。首先,你將使用這項技術來解決最令人頭疼的問題之一:對 Word 文檔進行版本控制。每個人都知道 Word 是最可怕的編輯器,奇怪的是,每個人都在使用它。如果想對 Word 文件進行版本控制,你可以把檔案加入到 Git 倉庫中,每次修改後提交即可。但這樣做有什麼好處?如果你像平常一樣執行 `git diff` 命令,你只能得到如下的結果:
315
+
316
+ $ git diff
317
+ diff --git a/chapter1.doc b/chapter1.doc
318
+ index 88839c4..4afcb7c 100644
319
+ Binary files a/chapter1.doc and b/chapter1.doc differ
320
+
321
+ 你不能直接比較兩個 Word 文件版本,除非人工細看,對吧?Git 屬性能很好地解決此問題,把下面這行加到 `.gitattributes` 文件:
322
+
323
+ *.doc diff=word
324
+
325
+ 當你要看比較結果時,如果檔副檔名是 ”doc”,Git 會使用 ”word” 篩檢程式(filter)。什麼是 ”word” 篩檢程式呢?你必須設置它。下面你將設定 Git 使用 `strings` 程式,把 Word 文檔轉換成可讀的文字檔,之後再進行比較:
326
+
327
+ $ git config diff.word.textconv catdoc
328
+
329
+ This command adds a section to your `.git/config` that looks like this:
330
+
331
+ [diff "word"]
332
+ textconv = catdoc
333
+
334
+ 現在 Git 知道了,如果它要在在兩個快照之間做比較,而其中任何一個檔檔名是以 `.doc` 結尾,它應該要對這些檔執行 ”word” 篩檢程式,也就是定義為執行 `strings` 程式。這樣就可以在比較前把 Word 檔轉換成文字檔。
335
+
336
+ 下面展示了一個實例,我把此書的第一章納入 Git 管理,在一個段落中加入了一些文字後保存,之後執行 `git diff` 命令,得到結果如下:
337
+
338
+ $ git diff
339
+ diff --git a/chapter1.doc b/chapter1.doc
340
+ index c1c8a0a..b93c9e4 100644
341
+ --- a/chapter1.doc
342
+ +++ b/chapter1.doc
343
+ @@ -128,7 +128,7 @@ and data size)
344
+ Since its birth in 2005, Git has evolved and matured to be easy to use
345
+ and yet retain these initial qualities. It’s incredibly fast, it’s
346
+ very efficient with large projects, and it has an incredible branching
347
+ -system for non-linear development.
348
+ +system for non-linear development (See Chapter 3).
349
+
350
+ Git 成功且簡潔地顯示出我增加的文字 ”(See Chapter 3)”。雖然有些瑕疵 -- 在末尾顯示了一些隨機的內容 -- 但確實可以比較了。如果你能找到或自己寫個 Word 到純文字的轉換器的話,效果可能會更好。不過因為 `strings` 可以在大部分 Mac 和 Linux 系統上運行,所以在初次嘗試對各種二進位格式檔進行類似的處理,它是個不錯的選擇。
351
+
352
+ ##### OpenDocument Text files #####
353
+
354
+ The same approach that we used for MS Word files (`*.doc`) can be used for OpenDocument Text files (`*.odt`) created by OpenOffice.org.
355
+
356
+ Add the following line to your `.gitattributes` file:
357
+
358
+ *.odt diff=odt
359
+
360
+ Now set up the `odt` diff filter in `.git/config`:
361
+
362
+ [diff "odt"]
363
+ binary = true
364
+ textconv = /usr/local/bin/odt-to-txt
365
+
366
+ OpenDocument files are actually zip’ped directories containing multiple files (the content in an XML format, stylesheets, images, etc.). We’ll need to write a script to extract the content and return it as plain text. Create a file `/usr/local/bin/odt-to-txt` (you are free to put it into a different directory) with the following content:
367
+
368
+ #! /usr/bin/env perl
369
+ # Simplistic OpenDocument Text (.odt) to plain text converter.
370
+ # Author: Philipp Kempgen
371
+
372
+ if (! defined($ARGV[0])) {
373
+ print STDERR "No filename given!\n";
374
+ print STDERR "Usage: $0 filename\n";
375
+ exit 1;
376
+ }
377
+
378
+ my $content = '';
379
+ open my $fh, '-|', 'unzip', '-qq', '-p', $ARGV[0], 'content.xml' or die $!;
380
+ {
381
+ local $/ = undef; # slurp mode
382
+ $content = <$fh>;
383
+ }
384
+ close $fh;
385
+ $_ = $content;
386
+ s/<text:span\b[^>]*>//g; # remove spans
387
+ s/<text:h\b[^>]*>/\n\n***** /g; # headers
388
+ s/<text:list-item\b[^>]*>\s*<text:p\b[^>]*>/\n -- /g; # list items
389
+ s/<text:list\b[^>]*>/\n\n/g; # lists
390
+ s/<text:p\b[^>]*>/\n /g; # paragraphs
391
+ s/<[^>]+>//g; # remove all XML tags
392
+ s/\n{2,}/\n\n/g; # remove multiple blank lines
393
+ s/\A\n+//; # remove leading blank lines
394
+ print "\n", $_, "\n\n";
395
+
396
+ And make it executable
397
+
398
+ chmod +x /usr/local/bin/odt-to-txt
399
+
400
+ Now `git diff` will be able to tell you what changed in `.odt` files.
401
+
402
+
403
+ ##### Image files #####
404
+
405
+ 你還能用這個方法解決另一個有趣的問題:比較影像檔。方法之一是對 JPEG 檔執行一個篩檢程式,把 EXIF 資訊捉取出來 — EXIF 資訊是記錄在大部分圖像格式裏面的 metadata。如果你下載並安裝了 `exiftool` 程式,可以用它把圖檔的 metadata 轉換成文本,於是至少 diff 可以用文字呈現的方式向你展示發生了哪些修改:
406
+
407
+ $ echo '*.png diff=exif' >> .gitattributes
408
+ $ git config diff.exif.textconv exiftool
409
+
410
+ 如果你把專案中的一個影像檔替換成另一個,然後執行 `git diff` 命令的結果如下:
411
+
412
+ diff --git a/image.png b/image.png
413
+ index 88839c4..4afcb7c 100644
414
+ --- a/image.png
415
+ +++ b/image.png
416
+ @@ -1,12 +1,12 @@
417
+ ExifTool Version Number : 7.74
418
+ -File Size : 70 kB
419
+ -File Modification Date/Time : 2009:04:17 10:12:35-07:00
420
+ +File Size : 94 kB
421
+ +File Modification Date/Time : 2009:04:21 07:02:43-07:00
422
+ File Type : PNG
423
+ MIME Type : image/png
424
+ -Image Width : 1058
425
+ -Image Height : 889
426
+ +Image Width : 1056
427
+ +Image Height : 827
428
+ Bit Depth : 8
429
+ Color Type : RGB with Alpha
430
+
431
+ 你可以很容易看出來,檔案的大小跟影像的尺寸都發生了改變。
432
+
433
+ ### 關鍵字擴展 ###
434
+
435
+ 使用 SVN 或 CVS 的開發人員經常要求關鍵字擴展。這在 Git 中主要的問題是,你無法在一個檔案被提交後再修改它,因為 Git 會先對該檔計算 checksum。然而,你可以在檔案 check out 之後注入(inject)一些文字,然後在提交前再把它移除。Git 屬性提供了兩種方式來進行。
436
+
437
+ 首先,你可以把某個 blob 的 SHA-1 checksum 自動注入檔案的 $Id$ 欄位。如果在一個或多個檔案上設置了此欄位,當下次你 check out 該分支的時候,Git 會用 blob 的 SHA-1 值替換那個欄位。注意,這不是 commit 物件的 SHA,而是 blob 本身的:
438
+
439
+ $ echo '*.txt ident' >> .gitattributes
440
+ $ echo '$Id$' > test.txt
441
+
442
+ 下次 check out 這個檔案的時候,Git 注入了 blob 的 SHA 值:
443
+
444
+ $ rm test.txt
445
+ $ git checkout -- test.txt
446
+ $ cat test.txt
447
+ $Id: 42812b7653c7b88933f8a9d6cad0ca16714b9bb3 $
448
+
449
+ 然而,這個結果的用處有限。如果你在 CVS 或 Subversion 中用過關鍵字替換,你可以包含一個日期值 -- 而這個 SHA 值沒什麼幫助,因為它相當地隨機,也無法區分某個 SHA 跟另一個 SHA 比起來是比較新或是比較舊。
450
+
451
+ 因此,你可以撰寫自己的篩檢程式,在提交或 checkout 文件時替換關鍵字。有兩種篩檢程式,”clean” 和 ”smudge”。在 `.gitattributes` 檔中,你能對特定的路徑設置一個篩檢程式,然後設置處理檔案的腳本,這些腳本會在檔案 check out 前(”smudge”,見圖 7-2)和提交前(”clean”,見圖7-3)被執行。這些篩檢程式能夠做各種有趣的事。
452
+
453
+ Insert 18333fig0702.png
454
+ Figure 7-2. “smudge” filter 在 checkout 時執行
455
+
456
+ Insert 18333fig0703.png
457
+ Figure 7-3. “clean” filter 在檔案被 staged 的時候執行
458
+
459
+ 這裡舉一個簡單的例子:在提交前,用 indent(縮進)程式過濾所有C原始程式碼。在 `.gitattributes` 檔中設置 ”indent” 篩檢程式過濾 `*.c` 文件:
460
+
461
+ *.c filter=indent
462
+
463
+ 然後,通過以下配置,讓 Git 知道 ”indent” 篩檢程式在遇到 ”smudge” 和 ”clean” 時分別該做什麼:
464
+
465
+ $ git config --global filter.indent.clean indent
466
+ $ git config --global filter.indent.smudge cat
467
+
468
+ 於是,當你提交 `*.c` 檔時,indent 程式會被觸發,在把它們 check out 之前,cat 程式會被觸發。但 `cat` 程式在這裡沒什麼實際作用。這樣的組合,使C原始程式碼在提交前被 indent 程式過濾,非常有效。
469
+
470
+ 另一個有趣的例子是類似 RCS 的 $Date$ 關鍵字擴展。為了演示,需要一個小腳本,接受檔案名參數,得到專案的最新提交日期,最後把日期寫入該檔。下面用 Ruby 腳本來實現:
471
+
472
+ #! /usr/bin/env ruby
473
+ data = STDIN.read
474
+ last_date = `git log --pretty=format:"%ad" -1`
475
+ puts data.gsub('$Date$', '$Date: ' + last_date.to_s + '$')
476
+
477
+ 該腳本從 `git log` 命令中得到最新提交日期,找到檔案中所有的 $Date$ 字串,最後把該日期填到 $Date$ 字串中 — 此腳本很簡單,你可以選擇你喜歡的程式設計語言來實現。把該腳本命名為 `expand_date`,放到正確的路徑中,之後需要在 Git 中設置一個篩檢程式(`dater`),讓它在 check ou 檔案時使用 `expand_date`,在提交時用 Perl 清除之:
478
+
479
+ $ git config filter.dater.smudge expand_date
480
+ $ git config filter.dater.clean 'perl -pe "s/\\\$Date[^\\\$]*\\\$/\\\$Date\\\$/"'
481
+
482
+ 這個 Perl 小程式會刪除 $Date$ 字串裡多餘的字元,恢復 $Date$ 原貌。到目前為止,你的篩檢程式已經設置完畢,可以開始測試了。打開一個檔,在檔中輸入 $Date$ 關鍵字,然後設置 Git 屬性:
483
+
484
+ $ echo '# $Date$' > date_test.txt
485
+ $ echo 'date*.txt filter=dater' >> .gitattributes
486
+
487
+ 如果把這些修改提交,之後再 check out,你會發現關鍵字被替換了:
488
+
489
+ $ git add date_test.txt .gitattributes
490
+ $ git commit -m "Testing date expansion in Git"
491
+ $ rm date_test.txt
492
+ $ git checkout date_test.txt
493
+ $ cat date_test.txt
494
+ # $Date: Tue Apr 21 07:26:52 2009 -0700$
495
+
496
+ 雖說這項技術對自訂應用來說很有用,但還是要小心,因為 `.gitattributes` 檔會隨著專案一起提交,而篩檢程式(例如:`dater`)不會,所以,它不會在所有地方都成功運作。當你在設計這些篩檢程式時要注意,即使它們無法正常工作,也要讓整個專案運作下去。
497
+
498
+ ### 匯出倉庫 ###
499
+
500
+ Git 屬性在將專案匯出歸檔(archive)時也能發揮作用。
501
+
502
+ #### export-ignore ####
503
+
504
+ 當產生一個 archive 時,可以告訴 Git 不要匯出某些檔案或目錄。如果你不想在 archive 中包含一個子目錄或檔案,但想將他們納入專案的版本管理中,你能對應地設置 `export-ignore` 屬性。
505
+
506
+ 例如,在 `test/` 子目錄中有一些測試檔,在專案的壓縮包中包含他們是沒有意義的。因此,可以增加下面這行到 Git 屬性檔中:
507
+
508
+ test/ export-ignore
509
+
510
+ 現在,當運行 git archive 來創建專案的壓縮包時,那個目錄不會在 archive 中出現。
511
+
512
+ #### export-subst ####
513
+
514
+ 還能對 archives 做一些簡單的關鍵字替換。在第2章中已經可以看到,可以以 `--pretty=format` 形式的簡碼在任何檔中放入 `$Format:$` 字串。例如,如果想在專案中包含一個叫作 `LAST_COMMIT` 的檔,當運行 `git archive` 時,最後提交日期自動地注入進該檔,可以這樣設置:
515
+
516
+ $ echo 'Last commit date: $Format:%cd$' > LAST_COMMIT
517
+ $ echo "LAST_COMMIT export-subst" >> .gitattributes
518
+ $ git add LAST_COMMIT .gitattributes
519
+ $ git commit -am 'adding LAST_COMMIT file for archives'
520
+
521
+ 執行 `git archive` 後,打開該檔,會發現其內容如下:
522
+
523
+ $ cat LAST_COMMIT
524
+ Last commit date: $Format:Tue Apr 21 08:38:48 2009 -0700$
525
+
526
+ ### 合併策略 ###
527
+
528
+ 通過 Git 屬性,還能對專案中的特定檔案使用不同的合併策略。一個非常有用的選項就是,當一些特定檔案發生衝突,Git 不會嘗試合併他們,而使用你這邊的來覆蓋別人的。
529
+
530
+ 如果專案的一個分支有歧義或比較特別,但你想從該分支合併,而且需要忽略其中某些檔,這樣的合併策略是有用的。例如,你有一個資料庫設置檔 database.xml,在兩個分支中他們是不同的,你想合併一個分支到另一個,而不弄亂該資料庫檔,可以設置屬性如下:
531
+
532
+ database.xml merge=ours
533
+
534
+ 如果合併到另一個分支,database.xml 檔不會有合併衝突,顯示如下:
535
+
536
+ $ git merge topic
537
+ Auto-merging database.xml
538
+ Merge made by recursive.
539
+
540
+ 這樣,database.xml會保持原樣。
541
+
542
+ ## Git Hooks ##
543
+
544
+ 和其他版本控制系統一樣,當某些重要事件發生時,Git 有方法可以觸發自訂腳本。有兩組掛鉤(hooks):用戶端和伺服器端。用戶端掛鉤用於用戶端的操作,如提交和合併。伺服器端掛鉤用於 Git 伺服器端的操作,如接收被推送的提交。你可以為了各種不同的原因使用這些掛鉤,下面會講解其中一些。
545
+
546
+ ### 安裝一個 Hook ###
547
+
548
+ 掛鉤都被儲存在 Git 目錄下的 `hooks` 子目錄中,即大部分專案中預設的 `.git/hooks`。Git 預設會放置一些腳本範例在這個目錄中,除了可以作為掛鉤使用,這些樣本本身是可以獨立使用的。所有的樣本都是 shell 腳本,其中一些還包含了 Perl 的腳本,不過,任何正確命名的可執行腳本都可以正常使用 — 可以用 Ruby 或 Python,或其他。在 Git 1.6 版本之後,這些樣本檔名都是以 .sample 結尾,因此,你必須重新命名。在 Git 1.6 版本之前,這些樣本名都是正確的,但這些樣本不是可執行檔。
549
+
550
+ 把一個正確命名且可執行的檔放入 Git 目錄下的 hooks 子目錄中,可以啟動該掛鉤腳本,之後他一直會被 Git 呼叫。隨後會講解主要的掛鉤腳本。
551
+
552
+ ### 用戶端掛鉤 ###
553
+
554
+ 有許多用戶端掛鉤,以下把他們分為:提交工作流程掛鉤、電子郵件工作流程掛鉤及其他用戶端掛鉤。
555
+
556
+ #### 提交工作流程掛鉤 ####
557
+
558
+ 有四個掛鉤被用來處理提交的過程。`pre-commit` 掛鉤在鍵入提交資訊前運行,被用來檢查即將提交的快照,例如,檢查是否有東西被遺漏,確認測試是否運行,以及檢查代碼。當從該掛鉤返回非零值時,Git 放棄此次提交,但可以用 `git commit --no-verify` 來忽略。該掛鉤可以被用來檢查程式碼樣式(運行類似 lint 的程式),檢查尾部空白(預設掛鉤是這麼做的),檢查新方法(簡體中文版譯注:程式的函數)的說明。
559
+
560
+ `prepare-commit-msg` 掛鉤在提交資訊編輯器顯示之前,預設資訊被創建之後執行。因此,可以有機會在提交作者看到預設資訊前進行編輯。該掛鉤接收一些選項:擁有提交資訊的檔案路徑,提交類型,以及提交的 SHA-1 (如果這是一個 amended 提交)。該掛鉤對通常的提交來說不是很有用,只在自動產生的預設提交資訊的情況下有作用,如提交資訊範本、合併、壓縮和 amended 提交等。可以和提交範本配合使用,以程式設計的方式插入資訊。
561
+
562
+ `commit-msg` 掛鉤接收一個參數,此參數是包含最近提交資訊的暫存檔路徑。如果該掛鉤腳本以非零退出,Git 會放棄提交,因此,可以用來在提交通過前驗證專案狀態或提交資訊。本章上一小節已經展示了使用該掛鉤核對提交資訊是否符合特定的模式。
563
+
564
+ `post-commit` 掛鉤在整個提交過程完成後運行,他不會接收任何參數,但可以執行 `git log -1 HEAD` 來獲得最後的提交資訊。總之,該掛鉤是作為通知之類使用的。
565
+
566
+ 提交工作流程的用戶端掛鉤腳本可以在任何工作流程中使用,他們經常被用來實施某些策略,但值得注意的是,這些腳本在 clone 期間不會被傳送。可以在伺服器端實施策略來拒絕不符合某些策略的推送,但這完全取決於開發者在用戶端使用這些腳本的情況。所以,這些腳本對開發者是有用的,由他們自己設置和維護,而且在任何時候都可以覆蓋或修改這些腳本。
567
+
568
+ #### E-mail 工作流掛鉤 ####
569
+
570
+ 有三個可用的用戶端掛鉤用於 e-mail工作流。當運行 `git am` 命令時,會呼叫他們,因此,如果你沒有在工作流中用到此命令,可以跳過本節。如果你通過 e-mail 接收由 git `format-patch` 產生的補丁,這些掛鉤也許對你有用。
571
+
572
+ 首先執行的是 applypatch-msg 掛鉤,他接收一個參數:包含被建議提交資訊的暫存檔案名。如果該腳本以非零值退出,Git 將放棄此補丁。可以使用這個腳本確認提交資訊是否被正確格式化,或讓腳本把提交訊息編輯為正規化。
573
+
574
+ 下一個當透過 `git am` 應用補丁時執行的是 `pre-applypatch` 掛鉤。該掛鉤不接收參數,在補丁被應用之後執行,因此,可以被用來在提交前檢查快照。你能用此腳本執行測試,檢查工作樹。如果有些什麼遺漏,或測試沒通過,腳本會以非零退出,放棄此次 `git am` 的運行,補丁不會被提交。
575
+
576
+ 最後在 `git am` 操作期間執行的掛鉤是 `post-applypatch`。你可以用他來通知一個小組或該補丁的作者,但無法使用此腳本阻止打補丁的過程。
577
+
578
+ #### 其他用戶端掛鉤 ####
579
+
580
+ `pre-rebase` 掛鉤在衍合前執行,腳本以非零退出可以中止衍合的過程。你可以使用這個掛鉤來禁止衍合已經推送的提交物件,Git 所安裝的 `pre-rebase` 掛鉤範例就是這麼做的,不過它假定 next 是你定義的分支名。因此,你可能要修改樣本,把 next 改成你定義過且穩定的分支名。
581
+
582
+ 在 `git checkout` 成功執行後會執行 `post-checkout` 掛鉤。他可以用來為你的專案環境設置合適的工作目錄。例如:放入大的二進位檔案、自動產生的文檔或其他一切你不想納入版本控制的檔。
583
+
584
+ 最後,在 `merge` 命令成功執行後會執行 `post-merge` 掛鉤。他可以用來在 Git 無法跟蹤的工作樹中恢復資料,例如許可權資料。該掛鉤同樣能夠驗證在 Git 控制之外的檔是否存在,當工作樹改變時,你希望可以複製進來的檔案。
585
+
586
+ ### 伺服器端掛鉤 ###
587
+
588
+ 除了用戶端掛鉤,作為系統管理員,你還可以使用兩個伺服器端的掛鉤對專案實施各種類型的策略。這些掛鉤腳本可以在提交物件推送到伺服器前執行,也可以在推送到伺服器後執行。推送到伺服器前執行的掛鉤(pre hooks)可以在任何時候以非零退出,拒絕推送、傳回錯誤訊息給用戶端;還可以如你所願設置足夠複雜的推送策略。
589
+
590
+ #### pre-receive and post-receive ####
591
+
592
+ 處理來自用戶端的推送(push)操作時最先執行的腳本就是 `pre-receive` 。它從標準輸入(stdin)獲取被推送的引用(references)列表;如果它退出時的返回值不是0,那麼所有推送內容都不會被接受。利用此掛鉤腳本可以實現類似保證被更新的索引(references)都不是 non-fast-forward 類型;抑或檢查執行推送操作的用戶擁有創建、刪除或者推送的許可權,或者他是否對將要修改的每一個檔都有存取權限。
593
+
594
+ `post-receive` 掛鉤在整個過程完結以後執行,可以用來更新其他系統服務或者通知使用者。它接受與 `pre-receive` 相同的標準輸入資料。應用實例包括給某郵寄清單發信,通知即時整合資料的伺服器,或者更新軟體專案的問題追蹤系統 —— 甚至可以通過分析提交資訊來決定某個問題是否應該被開啟、修改或結案。該腳本無法停止推送程序,不過用戶端在它完成之前將保持連接狀態;所以在用它作一些長時間的操作之前請三思。
595
+
596
+ #### update ####
597
+
598
+ update 腳本和 `pre-receive` 腳本十分類似,除了它會為推送者更新的每一個分支運行一次。假如推送者同時向多個分支推送內容,`pre-receive` 只執行一次,相較之下 update 則會為每一個更新的分支運行一次。它不會從標準輸入讀取內容,而是接受三個參數:索引(reference)的名字(分支),推送前索引指向的內容的 SHA-1 值,以及使用者試圖推送內容的 SHA-1 值。如果 update 腳本退出時返回非零值,只有相應的那一個索引會被拒絕;其餘的依然會得到更新。
599
+
600
+ ## Git 強制策略實例 ##
601
+
602
+ 在本節中,我們應用前面學到的知識建立這樣一個 Git 工作流程:檢查提交資訊的格式,只接受純 fast-forward 內容的推送,並且指定專案中的某些特定用戶只能修改某些特定子目錄。我們將撰寫一個用戶端腳本來提示開發人員他們推送的內容是否會被拒絕,以及一個伺服端腳本來實際執行這些策略。
603
+
604
+ 我使用 Ruby 來撰寫這些腳本,一方面因為它是我喜好的指令碼語言(scripting language),也因為我覺得它是最接近偽代碼(pseudocode-looking)的指令碼語言;因而即便你不使用 Ruby 也能大致看懂。不過任何其他語言也一樣適用。所有 Git 自帶的範例腳本都是用 Perl 或 Bash 寫的,所以從這些腳本中能找到相當多的這兩種語言的掛鉤範例。
605
+
606
+ ### 服務端掛鉤 ###
607
+
608
+ 所有服務端的工作都在 hooks(掛鉤)目錄的 update(更新)腳本中制定。update 腳本為每一個得到推送的分支運行一次;它接受推送目標的索引(reference)、該分支原來指向的位置、以及被推送的新內容。如果推送是通過 SSH 進行的,還可以獲取發出此次操作的用戶。如果設定所有操作都通過公鑰授權的單一帳號(比如"git")進行,就有必要通過一個 shell 包裝(wrapper)依據公鑰來判斷用戶的身份,並且設定環境變數來表示該使用者的身份。下面假設嘗試連接的使用者儲存在 `$USER` 環境變數裡,我們的 update 腳本首先搜集一切需要的資訊:
609
+
610
+ #!/usr/bin/env ruby
611
+
612
+ refname = ARGV[0]
613
+ oldrev = ARGV[1]
614
+ newrev = ARGV[2]
615
+ user = ENV['USER']
616
+
617
+ puts "Enforcing Policies... \n(#{refname}) (#{oldrev[0,6]}) (#{newrev[0,6]})"
618
+
619
+ #### 強制特定的提交資訊格式 ####
620
+
621
+ 我們的第一項任務是指定每一條提交資訊都必須遵循某種特殊的格式。只是設定一個目標,假定每一條資訊必須包含一條形似 “ref: 1234” 這樣的字串,因為我們需要把每一次提交連結到專案問題追蹤系統裏面的工作項目。我們要逐一檢查每一條推送上來的提交內容,看看提交資訊是否包含這麼一個字串,然後,如果該提交裡不包含這個字串,以非零返回值退出從而拒絕此次推送。
622
+
623
+ 把 `$newrev` 和 `$oldrev` 變數的值傳給一個叫做 `git rev-list` 的 Git plumbing 命令可以獲取所有提交內容 SHA-1 值的列表。`git rev-list` 基本上是個 `git log` 命令,但它預設只輸出 SHA-1 值而已,沒有其他資訊。所以要獲取由 SHA 值表示的從一次提交到另一次提交之間的所有 SHA 值,可以執行:
624
+
625
+ $ git rev-list 538c33..d14fc7
626
+ d14fc7c847ab946ec39590d87783c69b031bdfb7
627
+ 9f585da4401b0a3999e84113824d15245c13f0be
628
+ 234071a1be950e2a8d078e6141f5cd20c1e61ad3
629
+ dfa04c9ef3d5197182f13fb5b9b1fb7717d2222a
630
+ 17716ec0f1ff5c77eff40b7fe912f9f6cfd0e475
631
+
632
+ 取得這些輸出內容,迴圈遍歷其中每一個提交的 SHA 值,找出與之對應的提交資訊,然後用規則運算式(regular expression)來測試該資訊是否符合某個 pattern。
633
+
634
+ 下面要搞定如何從所有的提交內容中提取出提交資訊。使用另一個叫做 `git cat-file` 的 Git plumbing 工具可以獲得原始的提交資料。我們將在第九章瞭解到這些 plumbing 工具的細節;現在暫時先看一下這條命令會給你什麼:
635
+
636
+ $ git cat-file commit ca82a6
637
+ tree cfda3bf379e4f8dba8717dee55aab78aef7f4daf
638
+ parent 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
639
+ author Scott Chacon <schacon@gmail.com> 1205815931 -0700
640
+ committer Scott Chacon <schacon@gmail.com> 1240030591 -0700
641
+
642
+ changed the version number
643
+
644
+ 通過 SHA-1 值獲得提交內容中的提交資訊的一個簡單辦法是找到提交的第一個空白行,然後取出它之後的所有內容。可以使用 Unix 系統的 `sed` 命令來實現這個效果:
645
+
646
+ $ git cat-file commit ca82a6 | sed '1,/^$/d'
647
+ changed the version number
648
+
649
+ 這條咒語從每一個待提交內容裡提取提交訊息,並且會在提交訊息不符合要求的情況下退出。為了退出腳本和拒絕此次推送,返回一個非零值。整個 method 大致如下:
650
+
651
+ $regex = /\[ref: (\d+)\]/
652
+
653
+ # enforced custom commit message format
654
+ def check_message_format
655
+ missed_revs = `git rev-list #{$oldrev}..#{$newrev}`.split("\n")
656
+ missed_revs.each do |rev|
657
+ message = `git cat-file commit #{rev} | sed '1,/^$/d'`
658
+ if !$regex.match(message)
659
+ puts "[POLICY] Your message is not formatted correctly"
660
+ exit 1
661
+ end
662
+ end
663
+ end
664
+ check_message_format
665
+
666
+ 把這一段放在 `update` 腳本裡,所有包含不符合指定規則的提交都會遭到拒絕。
667
+
668
+ #### 實現基於使用者的存取權限控制清單(ACL)系統 ####
669
+
670
+ 假設你需要添加一個使用存取權限控制列表 (access control list, ACL) 的機制來指定哪些使用者對專案的哪些部分有推送許可權。某些使用者具有全部的存取權,其他人只對某些子目錄或者某些特定的檔案具有推送許可權。要搞定這一點,所有的規則將被寫入一個位於伺服器的原始 Git 倉庫的 `acl` 檔。我們讓 update 掛鉤檢閱這些規則,審視推送的提交內容中需要修改的所有檔案,然後判定執行推送的用戶是否對所有這些檔案都有許可權。
671
+
672
+ 我們首先要創建這個列表。這裡使用的格式和 CVS 的 ACL 機制十分類似:它由若干行構成,第一欄的內容是 `avail` 或者 `unavail`;下一欄是由逗號分隔的使用者清單,列出這條規則會對哪些使用者生效;最後一欄是這條規則會對哪個目錄生效(空白表示開放訪問)。這些欄位由 pipe (`|`) 字元隔開。
673
+
674
+ 下例中,我們指定幾個管理員,幾個對 `doc` 目錄具有許可權的文件作者,以及一個只對 `lib` 和 `tests` 目錄具有許可權的開發人員,ACL 檔看起來像這樣:
675
+
676
+ avail|nickh,pjhyett,defunkt,tpw
677
+ avail|usinclair,cdickens,ebronte|doc
678
+ avail|schacon|lib
679
+ avail|schacon|tests
680
+
681
+ 首先把這些資料讀入到你所能使用的資料結構中。本例中,為保持簡潔,我們暫時只實做 `avail` 的規則(譯注:也就是省略了 unavail 部分)。下面這個 method 產生一個關聯式陣列,它的主鍵是用戶名,對應的值是一個該用戶有寫入許可權的所有目錄組成的陣列:
682
+
683
+ def get_acl_access_data(acl_file)
684
+ # read in ACL data
685
+ acl_file = File.read(acl_file).split("\n").reject { |line| line == '' }
686
+ access = {}
687
+ acl_file.each do |line|
688
+ avail, users, path = line.split('|')
689
+ next unless avail == 'avail'
690
+ users.split(',').each do |user|
691
+ access[user] ||= []
692
+ access[user] << path
693
+ end
694
+ end
695
+ access
696
+ end
697
+
698
+ 針對之前給出的 ACL 規則檔,這個 `get_acl_access_data` method 回傳的資料結構如下:
699
+
700
+ {"defunkt"=>[nil],
701
+ "tpw"=>[nil],
702
+ "nickh"=>[nil],
703
+ "pjhyett"=>[nil],
704
+ "schacon"=>["lib", "tests"],
705
+ "cdickens"=>["doc"],
706
+ "usinclair"=>["doc"],
707
+ "ebronte"=>["doc"]}
708
+
709
+ 搞定了使用者許可權的資料,下面需要找出這次推送的提交之中,哪些位置被修改,從而確保試圖推送的使用者對這些位置都有許可權。
710
+
711
+ 使用 `git log` 的 `--name-only` 選項(在第二章裡簡單的提過)我們可以輕而易舉的找出一次提交裡有哪些被修改的檔案:
712
+
713
+ $ git log -1 --name-only --pretty=format:'' 9f585d
714
+
715
+ README
716
+ lib/test.rb
717
+
718
+ 使用 `get_acl_access_data` 回傳的 ACL 結構來一一核對每一次提交修改的檔案列表,就能判定該用戶是否有許可權推送所有的提交內容:
719
+
720
+ # only allows certain users to modify certain subdirectories in a project
721
+ def check_directory_perms
722
+ access = get_acl_access_data('acl')
723
+
724
+ # see if anyone is trying to push something they can't
725
+ new_commits = `git rev-list #{$oldrev}..#{$newrev}`.split("\n")
726
+ new_commits.each do |rev|
727
+ files_modified = `git log -1 --name-only --pretty=format:'' #{rev}`.split("\n")
728
+ files_modified.each do |path|
729
+ next if path.size == 0
730
+ has_file_access = false
731
+ access[$user].each do |access_path|
732
+ if !access_path || # user has access to everything
733
+ (path.index(access_path) == 0) # access to this path
734
+ has_file_access = true
735
+ end
736
+ end
737
+ if !has_file_access
738
+ puts "[POLICY] You do not have access to push to #{path}"
739
+ exit 1
740
+ end
741
+ end
742
+ end
743
+ end
744
+
745
+ check_directory_perms
746
+
747
+ 以上的大部分內容應該還算容易理解。通過 `git rev-list` 獲取推送到伺服器的提交清單。然後,針對其中每一項,找出它試圖修改的檔案,然後確保執行推送的用戶對這些檔案具有許可權。一個不太容易理解的 Ruby 技巧是 `path.index(access_path) ==0` 這句,如果路徑以 `access_path` 開頭,它會回傳 True——這是為了確保 `access_path` 並不是只在允許的路徑之一,而是所有准許全選的目錄都在該目錄之下。
748
+
749
+ 現在,如果提交資訊的格式不對的話,或是修改的檔案在允許的路徑之外的話,你的用戶就不能推送這些提交。
750
+
751
+ #### 只允許 Fast-Forward 類型的推送 ####
752
+
753
+ 剩下的最後一項任務是指定只接受 fast-forward 的推送。在 Git 1.6 或者較新的版本裡,只需要設定 `receive.denyDeletes` 和 `receive.denyNonFastForwards` 選項就可以了。但是用掛鉤來實做這個功能,便可以在舊版本的 Git 上運作,並且通過一定的修改,它可以做到只針對某些用戶執行,或者更多以後可能用到的規則。
754
+
755
+ 檢查的邏輯是看看是否有任何的提交在舊版本(revision)裡能找到、但在新版本裡卻找不到。如果沒有,那這是一次純 fast-forward 的推送;如果有,那我們拒絕此次推送:
756
+
757
+ # enforces fast-forward only pushes
758
+ def check_fast_forward
759
+ missed_refs = `git rev-list #{$newrev}..#{$oldrev}`
760
+ missed_ref_count = missed_refs.split("\n").size
761
+ if missed_ref_count > 0
762
+ puts "[POLICY] Cannot push a non fast-forward reference"
763
+ exit 1
764
+ end
765
+ end
766
+
767
+ check_fast_forward
768
+
769
+ 一切都設定好了。如果現在執行 `chmod u+x .git/hooks/update` —— 這是包含以上內容的案,我們修改它的許可權,然後嘗試推送一個包含非 fast-forward 類型的索引,會得到類似如下:
770
+
771
+ $ git push -f origin master
772
+ Counting objects: 5, done.
773
+ Compressing objects: 100% (3/3), done.
774
+ Writing objects: 100% (3/3), 323 bytes, done.
775
+ Total 3 (delta 1), reused 0 (delta 0)
776
+ Unpacking objects: 100% (3/3), done.
777
+ Enforcing Policies...
778
+ (refs/heads/master) (8338c5) (c5b616)
779
+ [POLICY] Cannot push a non fast-forward reference
780
+ error: hooks/update exited with error code 1
781
+ error: hook declined to update refs/heads/master
782
+ To git@gitserver:project.git
783
+ ! [remote rejected] master -> master (hook declined)
784
+ error: failed to push some refs to 'git@gitserver:project.git'
785
+
786
+ 這裡有幾個有趣的資訊。首先,我們可以看到掛鉤執行的起點:
787
+
788
+ Enforcing Policies...
789
+ (refs/heads/master) (8338c5) (c5b616)
790
+
791
+ 注意這是你在 update 腳本一開頭的地方印出到標準輸出的東西。所有從腳本印出到 stdout 的東西都會發送到用戶端,這點很重要。
792
+
793
+ 下一個值得注意的部分是錯誤資訊。
794
+
795
+ [POLICY] Cannot push a non fast-forward reference
796
+ error: hooks/update exited with error code 1
797
+ error: hook declined to update refs/heads/master
798
+
799
+ 第一行是我們的腳本輸出的,在往下是 Git 在告訴我們 update 腳本退出時傳回了非零值,因而推送遭到了拒絕。最後一點:
800
+
801
+ To git@gitserver:project.git
802
+ ! [remote rejected] master -> master (hook declined)
803
+ error: failed to push some refs to 'git@gitserver:project.git'
804
+
805
+ 每一個被掛鉤拒絕的索引(reference),你都會看到一條遠端拒絕訊息,解釋它被拒絕是因為一個掛鉤失敗的原因。
806
+
807
+ 而且,如果 ref 標記字串(譯註: 例如 ref: 1234)沒有包含在任何的提交裡,我們將看到前面腳本裡印出的錯誤資訊:
808
+
809
+ [POLICY] Your message is not formatted correctly
810
+
811
+ 又或者某人想修改一個自己不具備許可權的檔,然後推送了一個包含它的提交,他將看到類似的提示。比如,一個文件作者嘗試推送一個修改了 `lib` 目錄下某些東西的提交,他會看到
812
+
813
+ [POLICY] You do not have access to push to lib/test.rb
814
+
815
+ 都做好了。從這裡開始,只要 `update` 腳本存在並且可執行,我們的倉庫永遠都不會遭到回轉(rewound),或者包含不符合要求資訊的提交內容,並且使用者都被鎖在了沙箱裡面。
816
+
817
+ ### 用戶端掛鉤 ###
818
+
819
+ 這種方法的缺點在於使用者推送內容遭到拒絕後幾乎無法避免的抱怨。辛辛苦苦寫成的代碼在最後時刻慘遭拒絕是令人十分沮喪、迷惑的;更可憐的是他們不得不修改提交歷史來解決問題,有時候這可不是隨便哪個人都做得來的。
820
+
821
+ 這種兩難境地的解答是提供一些用戶端的掛鉤,讓使用者可以用來在他們作出伺服器可能會拒絕的事情時給以警告。這樣的話,用戶們就能在提交--問題變得更難修正之前修正問題。由於掛鉤本身不跟隨 clone 的專案副本分發,所以必須通過其他途徑把這些掛鉤分發到用戶的 `.git/hooks` 目錄並設為可執行檔。雖然可以在專案裏或用另一個專案分發這些掛鉤,不過全自動的解決方案是不存在的。
822
+
823
+ 首先,你應該在每次提交前檢查你的提交說明訊息,這樣你才能確保伺服器不會因為不合格式的提交說明訊息而拒絕你的更改。為了達到這個目的,你可以增加 `commit-msg` 掛鉤。如果你使用該掛鉤來讀取第一個參數傳遞的檔案裏的訊息,並且與規定的模式(pattern)作對比,你就可以使 Git 在提交說明訊息不符合條件的情況下,拒絕執行提交。
824
+
825
+ #!/usr/bin/env ruby
826
+ message_file = ARGV[0]
827
+ message = File.read(message_file)
828
+
829
+ $regex = /\[ref: (\d+)\]/
830
+
831
+ if !$regex.match(message)
832
+ puts "[POLICY] Your message is not formatted correctly"
833
+ exit 1
834
+ end
835
+
836
+ 如果這個腳本放在這個位置 (`.git/hooks/commit-msg`) 並且是可執行的, 而你的提交說明訊息沒有做適當的格式化,你會看到:
837
+
838
+ $ git commit -am 'test'
839
+ [POLICY] Your message is not formatted correctly
840
+
841
+ 在這個實例中,提交沒有成功。然而如果你的提交說明訊息符合要求的,Git 會允許你提交:
842
+
843
+ $ git commit -am 'test [ref: 132]'
844
+ [master e05c914] test [ref: 132]
845
+ 1 files changed, 1 insertions(+), 0 deletions(-)
846
+
847
+ 接下來我們要保證沒有修改到 ACL 允許範圍之外的檔案。如果你的專案 `.git` 目錄裡有前面使用過的 ACL 檔,那麼以下的 `pre-commit` 腳本將執行裡面的限制規定:
848
+
849
+ #!/usr/bin/env ruby
850
+
851
+ $user = ENV['USER']
852
+
853
+ # [ insert acl_access_data method from above ]
854
+
855
+ # only allows certain users to modify certain subdirectories in a project
856
+ def check_directory_perms
857
+ access = get_acl_access_data('.git/acl')
858
+
859
+ files_modified = `git diff-index --cached --name-only HEAD`.split("\n")
860
+ files_modified.each do |path|
861
+ next if path.size == 0
862
+ has_file_access = false
863
+ access[$user].each do |access_path|
864
+ if !access_path || (path.index(access_path) == 0)
865
+ has_file_access = true
866
+ end
867
+ if !has_file_access
868
+ puts "[POLICY] You do not have access to push to #{path}"
869
+ exit 1
870
+ end
871
+ end
872
+ end
873
+
874
+ check_directory_perms
875
+
876
+ 這和服務端的腳本幾乎一樣,除了兩個重要區別。第一,ACL 檔的位置不同,因為這個腳本在當前工作目錄執行,而非 Git 目錄。ACL 檔的目錄必須由這個
877
+
878
+ access = get_acl_access_data('acl')
879
+
880
+ 改成這個:
881
+
882
+ access = get_acl_access_data('.git/acl')
883
+
884
+ 另一個重要區別是獲取「被修改檔案清單」的方式。在服務端的時候使用了查看提交紀錄的方式,可是目前的提交都還沒被記錄下來呢,所以這個清單只能從暫存區域獲取。原來是這樣:
885
+
886
+ files_modified = `git log -1 --name-only --pretty=format:'' #{ref}`
887
+
888
+ 現在要用這樣:
889
+
890
+ files_modified = `git diff-index --cached --name-only HEAD`
891
+
892
+ 不同的就只有這兩點——除此之外,該腳本完全相同。一個小陷阱在於它假設在本地執行的帳戶和推送到遠端服務端的相同。如果這二者不一樣,則需要手動設置一下 `$user` 變數。
893
+
894
+ 最後一項任務是檢查確認推送內容中不包含非 fast-forward 類型的索引(reference),不過這個需求比較少見。以下情況會得到一個非 fast-forward 類型的索引,要麼在某個已經推送過的提交上做衍合,要麼從本地不同分支推送到遠端相同的分支上。
895
+
896
+ 既然伺服器會告訴你不能推送非 fast-forward 內容,而且上面的掛鉤也能阻止強制的推送,唯一剩下的潛在問題就是衍合已經推送過的提交內容。
897
+
898
+ 下面是一個檢查這個問題的 pre-rabase 腳本的例子。它獲取一個所有即將重寫的提交內容的清單,然後檢查它們是否在遠端的索引(reference)裡已經存在。一旦發現某個提交可以從遠端索引裡衍變過來,它就放棄衍合操作:
899
+
900
+ #!/usr/bin/env ruby
901
+
902
+ base_branch = ARGV[0]
903
+ if ARGV[1]
904
+ topic_branch = ARGV[1]
905
+ else
906
+ topic_branch = "HEAD"
907
+ end
908
+
909
+ target_shas = `git rev-list #{base_branch}..#{topic_branch}`.split("\n")
910
+ remote_refs = `git branch -r`.split("\n").map { |r| r.strip }
911
+
912
+ target_shas.each do |sha|
913
+ remote_refs.each do |remote_ref|
914
+ shas_pushed = `git rev-list ^#{sha}^@ refs/remotes/#{remote_ref}`
915
+ if shas_pushed.split("\n").include?(sha)
916
+ puts "[POLICY] Commit #{sha} has already been pushed to #{remote_ref}"
917
+ exit 1
918
+ end
919
+ end
920
+ end
921
+
922
+ 這個腳本利用了一個第六章「修訂版本選擇」一節中不曾提到的語法。執行這個命令可以獲得一個所有已經完成推送的提交的列表:
923
+
924
+ git rev-list ^#{sha}^@ refs/remotes/#{remote_ref}
925
+
926
+ `SHA^@` 語法解析該次提交的所有祖先。我們尋找任何一個提交,這個提交可以從遠端最後一次提交衍變獲得(reachable),但從我們嘗試推送的任何一個提交的 SHA 值的任何一個祖先都無法衍變獲得——也就是 fast-forward 的內容。
927
+
928
+ 這個解決方案的缺點在於它可能會很慢而且通常是沒有必要的——只要不用 -f 來強制推送,伺服器會自動給出警告並且拒絕推送內容。然而,這是個不錯的練習,而且理論上能幫助用戶避免一個將來不得不回頭修改的衍合操作。
929
+
930
+ ## 總結 ##
931
+
932
+ 你已經見識過絕大多數通過自訂 Git 用戶端和服務端來適應自己工作流程和專案內容的方式了。你已經學到了各種配置設定(configuration settings)、以檔案為基礎的屬性(file-based attributes)、以及事件掛鉤,你也建置了一個執行強制政策的伺服器。現在,差不多任何你能想像到的工作流程,你應該都能讓 Git 切合你的需要。