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,974 @@
1
+ <!-- Attentie heren en dames vertalers.
2
+ Ik zou het volgende willen voorstellen:
3
+ Er zijn bepaalde termen die voor de gemiddelde Nederlandse computer gebruiker
4
+ veel beter klinken (of bekender voorkomen) als de orginele Engelse term. In het
5
+ begin zullen deze termen niet vaak voorkomen, maar in de meer diepgaandere
6
+ hoofdstukken komen deze steeds meer voor. Termen als "Committen", "Mergen"
7
+ en "Applyen" klinken beter dan "Plegen" of "Toepassen", "Samenvoegen" en
8
+ "Toepassen" (wat bovendien slecht valt te onderscheiden van de
9
+ commit-toepassing). De mensen die dit boek lezen zijn, naar mijn bescheiden
10
+ inschatting, al redelijk op de hoogte van versiebeheer en passen (zie ik in
11
+ de praktijk) deze termen al toe. Een nieuwe terminologie introduceren lijkt
12
+ me dan ook niet noodzakelijk.
13
+ Verder blijven er altijd kreten over als "directory", wat vertaald zou kunnen
14
+ worden als "map", maar bij het Engelse werkwoord to map krijgen we dan weer het
15
+ probleem: hoe dit weer te vertalen? Daarom zou ik willen voorstellen om deze
16
+ basis-termen toch onvertaald te laten.
17
+
18
+ Twijfelgevallen zullen altijd blijven zoals de term "file", daarvan wordt in de
19
+ praktijk zowel de term file als bestand gebruikt. Ik denk dat we hier moeten
20
+ kijken hoe het in de context past.
21
+ Maar ook een term als "tool" en (ik zit zelf nog op een mooie Nederlandse term
22
+ te broeden) "plumbing", hierbij stel ik voor om eenmalig een Nederlandse
23
+ vertaling te geven, tussen haakjes de Engelse term te geven en in het vervolg
24
+ de Engelse term te gebruiken. Wederom is de context hier belangrijk.
25
+
26
+ Verder stel ik ook voor om de regels op https://onzetaal.nl/taaladvies zoveel
27
+ mogelijk te volgen. Bijvoorbeeld de regels omtrent het spellen van Engelse
28
+ werkwoorden die in het Nederlands gebruikt worden.
29
+
30
+ Let wel: ik wil niemand tot iets verplichten, maar ik denk dat we moeten
31
+ streven naar een zo duidelijk mogelijke en best bij de praktijk aansluitende
32
+ vertaling moeten proberen te maken.
33
+
34
+ Veel succes en plezier bij het vertalen...
35
+ -->
36
+ <!-- SHA-1 of last checked en-version: 4cefec -->
37
+ # Git op maat maken #
38
+
39
+ Tot zover heb ik de fundamentele werking van Git behandeld en hoe het te gebruiken, en ik heb een aantal tools geïntroduceerd die Git tot je beschikking stelt om je het makkelijk en efficiënt te laten gebruiken. In dit hoofdstuk zal ik wat operaties doorlopen die je kunt gebruiken om Git op een maat gemaakte manier te laten werken middels het introduceren van een aantal belangrijke configuratie-instellingen en het inhaak-systeem (hooks). Met deze tools is het makkelijk om Git precies te laten werken op de manier zoals jij, je bedrijf, of je groep het nodig hebben.
40
+
41
+ ## Git configuratie ##
42
+
43
+ Zoals je kort in Hoofdstuk 1 gezien hebt, kun je Git configuratie instellingen specificeren met het `git config` commando. Een van de eerste dingen die je deed was je naam en e-mail adres instellen:
44
+
45
+ $ git config --global user.name "John Doe"
46
+ $ git config --global user.email johndoe@example.com
47
+
48
+ Hierna zal je een paar van de meer interessante opties gaan zien, die je op vergelijkbare manier kunt instellen om je Git op maat te maken.
49
+
50
+ Je zag al wat eenvoudige Git configuratie details in het eerste hoofdstuk, en die zal ik hier snel nog eens laten zien. Git gebruikt een aantal configuratie bestanden om het niet-standaard gedrag dat je wilt te bepalen. De eerste plek waar Git kijkt voor deze waarden is in een `/etc/gitconfig` bestand, deze bevat de waarden voor alle gebruikers op het systeem en al hun repositories. Als je de optie `--system` aan `git config` meegeeft, leest en schrijft Git naar dit bestand.
51
+
52
+ De volgende plaats waar Git kijkt is het `~/.gitconfig` bestand, wat specifiek is voor elke gebruiker. Je kunt er voor zorgen dat Git naar dit bestand leest en schrijft door de `--global` optie mee te geven.
53
+
54
+ Als laatste kijkt Git naar configuratie waarden in het config bestand in de Git directory (`.git/config`) van de repository dat op dat moment gebruikt wordt. Deze waarden zijn specifiek voor die ene repository. Ieder niveau overschrijft de waarden van de vorige, dus waarden in `.git/config` hebben voorrang op die in `/etc/gitconfig` bijvoorbeeld. Je kunt die waarden ook instellen door het bestand handmatig aan te passen en de correcte syntax te gebruiken, maar het is normaalgesproken eenvoudiger het `git config` commando uit te voeren.
55
+
56
+ ### Basis client configuratie ###
57
+
58
+ De configuratie opties die herkend worden door Git vallen in twee categorieën: de client kant en de server kant. De meerderheid van de opties zijn voor de client kant: de configuratie van jouw persoonlijke voorkeuren. Alhoewel er massa's opties beschikbaar zijn, zal ik er maar een paar behandelen die ofwel veelgebruikt zijn ofwel je workflow significant kunnen beïnvloeden. Veel opties zijn alleen van toepassing in uitzonderlijke gevallen, die ik nu niet zal behandelen. Als je een lijst van alle opties wilt zien, die door jouw versie van Git worden herkend kan je dit uitvoeren
59
+
60
+ $ git config --help
61
+
62
+ De gebruikershandleiding voor `git config` toont alle beschikbare opties in groot detail.
63
+
64
+ #### core.editor ####
65
+
66
+ Standaard zal Git de tekst editor gebruiken die je zelf ingesteld hebt als standaard en anders valt Git terug op de Vi editor om je commit en tag boodschappen te maken of te wijzigen. Om die instelling naar iets anders om te zetten, kun je de `core.editor` instelling gebruiken:
67
+
68
+ $ git config --global core.editor emacs
69
+
70
+ Vanaf nu maakt het niet meer uit wat je als je standaard shell editor waarde ingesteld hebt, Git zal Emacs starten om boodschappen aan te passen.
71
+
72
+ #### commit.template ####
73
+
74
+ Als je hier het een pad instelt dat naar een bestand op je systeem wijst, zal Git dat bestand als de standaard boodschap gebruiken bij elke commit. Bijvoorbeeld, stel dat je een sjabloon bestand `$HOME/.gitmessage.txt` maakt die er zo uitziet:
75
+
76
+ onderwerp regel
77
+
78
+ wat er gebeurd is
79
+
80
+ [ticket: X]
81
+
82
+ Om Git te vertellen dat het dit moet gebruiken als standaard boodschap dat in de editor moet verschijnen als je `git commit` uitvoert, stel je de `commit.template` configuratie waarde in:
83
+
84
+ $ git config --global commit.template $HOME/.gitmessage.txt
85
+ $ git commit
86
+
87
+ Daarna zal je editor zoiets als dit openen als je standaard commit boodschap bij een commit:
88
+
89
+ onderwerp regel
90
+
91
+ wat er gebeurd is
92
+
93
+ [ticket: X]
94
+ # Please enter the commit message for your changes. Lines starting
95
+ # with '#' will be ignored, and an empty message aborts the commit.
96
+ # On branch master
97
+ # Changes to be committed:
98
+ # (use "git reset HEAD <file>..." to unstage)
99
+ #
100
+ # modified: lib/test.rb
101
+ #
102
+ ~
103
+ ~
104
+ ".git/COMMIT_EDITMSG" 14L, 297C
105
+
106
+ Als je een beleid hebt voor commit boodschappen, dan vergroot het plaatsen van een sjabloon op je systeem en het configureren ervan als standaard te gebruiken binnen Git de kans dat het beleid ook daadwerkelijk wordt gevolgd.
107
+
108
+ #### core.pager ####
109
+
110
+ De instelling voor 'core.pager' bepaalt welke pagineer tool gebruikt wordt door Git om de uitvoer van bijvoorbeeld `log` en `diff` weer te geven. Je kunt het instellen als `more` of als je favoriete pagineer tool (standaard is het `less`), of je kunt het uitzetten door het als een lege tekst in te stellen:
111
+
112
+ $ git config --global core.pager ''
113
+
114
+ Als je dat uitvoert zal Git de gehele tekst van alle commando's ononderbroken tonen, ongeacht de lengte van die uitvoer.
115
+
116
+ #### user.signingkey ####
117
+
118
+ Als je gebruik maakt van ondertekende tags (zoals besproken in Hoofdstuk 2), dan maakt het instellen van een GPG signeersleutel als configuratie instelling je leven een stuk eenvoudiger. Stel je sleutel ID zo in:
119
+
120
+ $ git config --global user.signingkey <gpg-key-id>
121
+
122
+ Nu kun je tags signeren zonder steeds je sleutel op te hoeven geven bij het `git tag` commando:
123
+
124
+ $ git tag -s <tag-name>
125
+
126
+ #### core.excludesfile ####
127
+
128
+ Je kunt patronen in het `.gitignore` bestand van je project zetten zodat Git ze niet ziet als untracked bestanden en niet zal proberen ze te stagen als je `git add` op ze uitvoert, zoals besproken in Hoofdstuk 2. Maar als je wilt dat een ander bestand buiten je project die waarden bevat of additionele waarden wilt, kan je Git vertellen met de `core.excludesfile`-waarde waar dat bestand is. Stel het eenvoudigweg in als een pad naar een bestand met een inhoud dat vergelijkbaar is met wat in een `.gitignore` bestand zou staan.
129
+
130
+ #### help.autocorrect ####
131
+
132
+ Deze optie is alleen beschikbaar in Git 1.6.1. en later. Als je in Git een commando verkeerd intypt, toont het je zoiets als dit:
133
+
134
+ $ git com
135
+ git: 'com' is not a git-command. See 'git --help'.
136
+
137
+ Did you mean this?
138
+ commit
139
+
140
+ Als je `het.autocorrect` op 1 instelt, zal Git automatisch het commando uitvoeren als het slechts één passend commando heeft in dit scenario.
141
+
142
+ ### Kleuren in Git ###
143
+
144
+ Git kan zijn uitvoer op de terminal in kleur tonen, wat kan helpen om de uitvoer snel en eenvoudig visueel te verwerken. Er zijn een aantal opties die kunnen helpen om de kleuren naar jouw voorkeur in te stellen.
145
+
146
+ #### color.ui ####
147
+
148
+ Git zal automatisch het meeste van zijn uitvoer in kleur tonen als je het vraagt. Je kunt erg specifiek worden in wat je gekleurd wilt hebben en hoe; maar om alle standaard kleuren in de terminal aan te zetten, stel dan `color.ui` in op true:
149
+
150
+ $ git config --global color.ui true
151
+
152
+ Als deze waarde ingesteld is, zal Git zijn uitvoer in kleur tonen zodra deze naar een terminal gaat. Andere mogelijke opties zijn false wat de uitvoer nooit kleurt, en always, wat de kleuren altijd weergeeft zelfs als je Git commando's uitvoert naar een bestand of deze naar een ander commando doorsluist (zgn. piping).
153
+
154
+ Je zult zelden `color.ui = always` willen. In de meeste scenario's, als je kleuren in je omgeleide uitvoer wil, kan je een `--color` vlag aan het Git commando meegeven om het te forceren kleuren te gebruiken. De `color.ui = true` instelling is degene die je vrijwel altijd wilt gebruiken.
155
+
156
+ #### `color.*` ####
157
+
158
+ Als je meer specifiek wil zijn welke commando's gekleurd moeten zijn en hoe, dan voorziet Git in woordspecifieke kleuren instellingen. Elk van deze kan worden ingesteld op `true`, `false` of `always`:
159
+
160
+ color.branch
161
+ color.diff
162
+ color.interactive
163
+ color.status
164
+
165
+ Daarnaast heeft elk van deze ook sub-instellingen die je kunt gebruiken om specifieke kleuren in te stellen voor delen van de uitvoer, als je iedere kleur wilt wijzigen. Bijvoorbeeld, om de meta-informatie in je diff uitvoer in blauwe voorgrond, zwarte achtergrond en vetgedrukt in te stellen, kun je dit uitvoeren
166
+
167
+ $ git config --global color.diff.meta “blue black bold”
168
+
169
+ Je kunt de kleur instellen op een van de volgende waarden: ’normal’, ’black’, ’red’, ’green’, ’yellow’, ’blue’, ’magenta’, ’cyan’, of ’white’. Als je een attribuut wil hebben, zoals vetgedrukt in het vorige voorbeeld, kun je kiezen uit ’bold’, ’dim’, ’ul’, ’blink’ en ’reverse’.
170
+
171
+ Zie de manpage van `git config` voor alle sub-instellingen die je kunt instellen, als je dat wilt.
172
+
173
+ ### Externe merge en diff tools ###
174
+
175
+ Alhoewel Git een interne implementatie van diff heeft, deze heb je tot nu toe gebruikt, kan je in plaats daarvan een externe tool instellen. Je kunt ook een grafisch merge conflict-oplossings tool instellen, in plaats van handmatig de conflicten op te moeten lossen. Ik zal nu demonstreren hoe je het Perforce Visuele Merge Tool (P4Merge) in moet stellen, om je diff en merge oplossingen te doen, omdat het een fijne grafische tool is en omdat het gratis is.
176
+
177
+ Als je dit wilt proberen, P4Merge werkt op alle grote platformen, dus je zou het moeten kunnen doen. Ik zal in de voorbeelden paden gebruiken die op Mac en Linux systemen werken; voor Windows moet je `/usr/local/bin` veranderen in een pad naar een uitvoerbaar bestand op jouw machine.
178
+
179
+ Je kunt P4Merge hier downloaden:
180
+
181
+ http://www.perforce.com/product/components/perforce-visual-merge-and-diff-tools
182
+
183
+ Om te beginnen ga je externe wrapper scripts instellen om de commando's uit te voeren. Ik zal het Mac pad gebruiken voor de applicatie; in andere systemen zal het moeten wijzen naar de plaats waar de `p4merge` binary geïnstalleerd is. Maak merge wrapper script, genaamd `extMerge`, die jouw applicatie met alle meegegeven argumenten aanroept:
184
+
185
+ $ cat /usr/local/bin/extMerge
186
+ #!/bin/sh
187
+ /Applications/p4merge.app/Contents/MacOS/p4merge $*
188
+
189
+ De diff wrapper controleert dat er precies zeven argumenten meegegeven zijn, en geeft twee ervan aan het merge script. Standaard geeft Git de volgende argumenten aan het diff programma mee:
190
+
191
+ pad oud-bestand oude-hex oude-modus nieuwe-bestand nieuwe-hex nieuwe-modus
192
+
193
+ Omdat je alleen de `oude-bestand` en `nieuwe-bestand` argumenten wilt, zul je het wrapper script gebruiken om de juiste parameters door te geven.
194
+
195
+ $ cat /usr/local/bin/extDiff
196
+ #!/bin/sh
197
+ [ $# -eq 7 ] && /usr/local/bin/extMerge "$2" "$5"
198
+
199
+ Je moet er ook voor zorgen dat deze scripts uitvoerbaar zijn:
200
+
201
+ $ sudo chmod +x /usr/local/bin/extMerge
202
+ $ sudo chmod +x /usr/local/bin/extDiff
203
+
204
+ Nu kun je het config bestand instellen om de zelfgemaakte merge en diff tools te gebruiken. Dit wordt gedaan met een aantal instellingen: `merge.tool` om Git te vertellen welke strategie hij moet gebruiken, `mergetool.*.cmd` om te specificeren hoe het commando moet worden uitgevoerd, `mergetool.trustExitCode` om Git te vertellen of de exit code van dat programma een succesvolle merge betekent of niet, en `diff.external` om Git te vertellen welk commando het moet uitvoeren voor diffs. Dus, je kunt de vier configuratie commando's uitvoeren
205
+
206
+ $ git config --global merge.tool extMerge
207
+ $ git config --global mergetool.extMerge.cmd \
208
+ 'extMerge "$BASE" "$LOCAL" "$REMOTE" "$MERGED"'
209
+ $ git config --global mergetool.trustExitCode false
210
+ $ git config --global diff.external extDiff
211
+
212
+ of je kunt je `~/.gitconfig` bestand aanpassen en deze regels toevoegen:
213
+
214
+ [merge]
215
+ tool = extMerge
216
+ [mergetool "extMerge"]
217
+ cmd = extMerge \"$BASE\" \"$LOCAL\" \"$REMOTE\" \"$MERGED\"
218
+ trustExitCode = false
219
+ [diff]
220
+ external = extDiff
221
+
222
+ Nadat dit alles gebeurd is, kan je diff commando's zoals deze uitvoeren:
223
+
224
+ $ git diff 32d1776b1^ 32d1776b1
225
+
226
+ in plaats van de uitvoer van diff op de commando regel, wordt een instantie van P4Merge gestart door Git, en dat ziet er ongeveer uit als in Figuur 7-1.
227
+
228
+ Insert 18333fig0701.png
229
+ Figuur 7-1. P4Merge.
230
+
231
+ Als je twee branches probeert te mergen en je krijgt vervolgens merge conflicten, kan je het `git mergetool` commando uitvoeren. P4Merge wordt dan opgestart om je het conflict op te laten lossen met behulp van de GUI tool.
232
+
233
+ Het aardige van deze wrapper opstelling is dat je de diff en merge tools eenvoudig aan kunt passen. Bijvoorbeeld, om je `extDiff` en `extMerge` tools in te stellen zodat ze bijvoorbeeld de KDiff3 tool uitvoeren, is het enige dat je moet doen het `extMerge` bestand aanpassen:
234
+
235
+ $ cat /usr/local/bin/extMerge
236
+ #!/bin/sh
237
+ /Applications/kdiff3.app/Contents/MacOS/kdiff3 $*
238
+
239
+ Nu zal Git de KDiff3 tool gebruiken voor het tonen van diff en het oplossen van merge conflicten.
240
+
241
+ Git is 'af fabriek' al ingesteld om een aantal andere mergeconflict-oplossings tools te gebruiken zonder dat je de cmd configuratie op hoeft te zetten. Je kunt je merge tool op kdiff3 instellen, opendiff, tkdiff, meld, xxdiff, emerge, vimdiff of gvimdiff. Als je niet geïnteresseerd bent in het gebruik van KDiff3 als diff, maar het liever alleen wilt gebruiken voor merge conflict oplossing, en het kdiff3 commando zit in je pad, dan kun je dit uitvoeren
242
+
243
+ $ git config --global merge.tool kdiff3
244
+
245
+ Als je dit uitvoert in plaats van de `extMerge` en `extDiff` bestanden in te stellen, zal Git KDiff3 gebruiken voor conflict oplossing en het normale Git diff tool voor diffs.
246
+
247
+ ### Opmaak en witruimten ###
248
+
249
+ Problemen met opmaak en witruimten zijn één van de meest frustrerende en subtiele problemen die veel ontwikkelaars tegenkomen bij het samenwerken, in het bijzonder over verschillende platformen. Het is heel eenvoudig voor patches en ander werk om subtiele witruimte veranderingen te introduceren, omdat editors ze stiekum introduceren of omdat Windows programmeurs carriage returns aan het eind van de regels toevoegen van bestanden die ze bewerken in gemengde platformprojecten. Git heeft een aantal configuratie opties om met deze problemen te helpen.
250
+
251
+ #### core.autocrlf ####
252
+
253
+ Als je op Windows programmeert, of een ander systeem gebruikt maar samenwerkt met mensen die op Windows werken, zal je op enig moment tegen regeleinde problemen aanlopen. Dat komt omdat Windows zowel een carriage-return als een linefeed karakter gebruikt voor regeleindes in zijn bestanden, terwijl Mac en Linux systemen alleen het linefeed karakter gebruiken. Dit is een subtiel maar verschrikkelijk irritant feit van het werken met gemengde platformen.
254
+
255
+ Git kan hiermee omgaan door CRLF regeleinden automatisch om te zetten naar LF zodra je commit, en vice versa op het moment dat je code uitcheckt op je bestandssysteem. Je kunt deze functionaliteit aanzetten met de `core.autocrlf` instelling. Als je op een Windows machine zit, stel het dan in op `true` – dit verandert LF regeleinden in CRLF zodra je code uitcheckt:
256
+
257
+ $ git config --global core.autocrlf true
258
+
259
+ Als je op een Linux of Mac systeem werkt (die LF regeleinden gebruiken) dan wil je niet dat Git ze automatisch verandert op het moment dat Git bestanden uitcheckt. Maar als een bestand met CRLF regeleinden onverhoopt toch geïntroduceerd wordt, dan wil je waarschijnlijk dat Git dit repareert. Je kunt Git vertellen dat je wilt dat hij CRLF in LF veranderd tijdens het committen, maar niet de andere kant op door het instellen van `core.autocrlf` op input:
260
+
261
+ $ git config --global core.autocrlf input
262
+
263
+ Deze instelling zal CRLF regeleinden in Windows checkouts gebruiken, en LF regeleinden in Mac en Linux systemen en in de repository.
264
+
265
+ Als je een Windows programmeur bent die aan een project voor alleen Windows werkt dan kun je deze functionaliteit uitzetten, waardoor de carriage-returns in de repository worden opgeslagen door de configuratie waarde op `false` te zetten:
266
+
267
+ $ git config --global core.autocrlf false
268
+
269
+ #### core.whitespace ####
270
+
271
+ Git is standaard ingericht om een aantal witruimte problemen te detecteren en te repareren. Het kan op vier veelvoorkomende witruimte problemen letten – twee staan er standaard aan en kunnen uitgezet worden, en twee staan standaard uit, maar kunnen aangezet worden.
272
+
273
+ De twee die standaard aan staan zijn `trailing-space`, waarmee wordt gekeken of er spaties aan het eind van een regel staan, en `space-before-tab`, wat kijkt of er spaties voor tabs staan aan het begin van een regel.
274
+
275
+ De twee die standaard uit staan maar aangezet kunnen worden, zijn `indent-with-non-tab` die kijkt naar regels die met acht of meer spaties beginnen in plaats van tabs, en `cr-at-eol`, wat Git vertelt dat carriage-returns aan het eind van een regel geaccepteerd mogen worden.
276
+
277
+ Je kunt Git vertellen welke van deze je aan wilt zetten door `core.whitespace` de waardes te geven die je aan of uit wilt zetten, gescheiden door komma's. Je kunt waarden uitzetten door ze weg te laten uit de instelling tekst of door een `-` vooraf te laten gaan aan de waarde. Bijvoorbeeld, als je alles aan wil behalve `cr-ar-eol`, dan kan je dit doen:
278
+
279
+ $ git config --global core.whitespace \
280
+ trailing-space,space-before-tab,indent-with-non-tab
281
+
282
+ Git zal deze problemen detecteren zodra je een `git diff` commando uitvoert en ze proberen in te kleuren zodat je ze mogelijk kunt repareren voordat je ze commit. Het zal deze waarden ook gebruiken om je te helpen met patches toe te passen met `git apply`. Als je patches gaat applyen, kan je Git vragen om je te waarschuwen als hij patches toepast waarin deze specifieke witruimte-problemen zitten:
283
+
284
+ $ git apply --whitespace=warn <patch>
285
+
286
+ Of je kunt Git vragen om automatisch deze problemen te repareren alvorens de patch te applyen:
287
+
288
+ $ git apply --whitespace=fix <patch>
289
+
290
+ Deze opties zijn ook op het `git rebase` commando van toepassing. Als je witruimte problemen gecommit hebt maar ze nog niet stroomopwaarts gepushed hebt, kun je een `rebase` uitvoeren met de `--whitespace=fix` optie om Git automatisch witruimte problemen te laten repareren terwijl het de patches herschrijft.
291
+
292
+ ### Server configuratie ###
293
+
294
+ Er zijn lang niet zoveel configuratie opties beschikbaar voor de server kant van Git, maar er zijn er een paar interessante bij waar je misschien op gewezen wilt worden.
295
+
296
+ #### receive.fsckObjects ####
297
+
298
+ Standaard zal Git niet alle objecten die tijdens een push ontvangen worden op consistentie controleren. Alhoewel Git kan controleren of ieder object nog steeds bij zijn SHA-1 checksum past en naar geldige objecten wijst, doet gebeurt dat niet standaard bij iedere push. Het is een relatief dure operatie en kan veel extra tijd kosten bij iedere push, afhankelijk van de grootte van het repository of de push. Als je wilt dat Git ieder object op consistentie controleert bij elke push, dan kun je dit afdwingen door `receive.fsckObjects` op true te zetten:
299
+
300
+ $ git config --system receive.fsckObjects true
301
+
302
+ Nu zal Git de integriteit van de repository controleren voordat een push geaccepteerd wordt, om er zeker van te zijn dat defecte clients geen corrupte gegevens introduceren.
303
+
304
+ #### receive.denyNonFastForwards ####
305
+
306
+ Als je commits rebased die je al gepusht hebt en dan nog eens pusht, of op een andere manier een commit probeert te pushen naar een remote branch die niet de commit bevat waarnaar de remote branch op dat moment wijst, dan wordt dat afgewezen. Dit is over het algemeen goed beleid, maar in het geval van de rebase kan je besluiten dat je weet waar je mee bezig bent en kan je de remote branch geforceerd vernieuwen door een `-f` vlag met je push commando mee te geven.
307
+
308
+ Om de mogelijkheid van het geforceerd vernieuwen van remote branches naar niet fast-forward referenties uit te schakelen, stel je `receive.denyNonFastForwards` in:
309
+
310
+ $ git config --system receive.denyNonFastForwards true
311
+
312
+ Een andere manier waarop je dit kunt doen is het instellen van ontvangst hooks op de server wat we zo meteen gaan behandelen. Die aanpak stelt je in staat meer complexe dingen te doen, zoals het weigeren van niet fast-forwards afkomstig van een bepaalde groep gebruikers.
313
+
314
+ #### receive.denyDeletes ####
315
+
316
+ Een van de manieren waarop een gebruiker het `denyNonFastForwards` beleid kan omzeilen is door de branch te verwijderen en het dan opnieuw terug pushen met de nieuwe referenties. In nieuwere versies van Git (beginnend bij versie 1.6.1), kun je `receive.denyDeletes` op true zetten:
317
+
318
+ $ git config --system receive.denyDeletes true
319
+
320
+ Dit zal systeembreed branch en tag verwijdering door middel van een push weigeren - geen enkele gebruiker mag het meer. Om remote branches te verwijderen, moet je de ref bestanden handmatig verwijderen van de server. Er zijn ook interessantere manieren om dit per gebruiker af te dwingen door middel van ACL's, zoals je zult leren aan het eind van dit hoofdstuk.
321
+
322
+ ## Git attributen ##
323
+
324
+ Een aantal van deze instellingen kan ook gedaan worden voor een pad, zodat Git die instellingen alleen toepast voor een subdirectory of subset van bestanden. Deze pad-specifieke instellingen worden Git attributen genoemd en worden in een `.gitattribute` bestand in een van je directories gezet (normaliter in de hoofddirectory van je project) of in het `.git/info/attributes` bestand als je niet wilt dat het attributes bestand gecommit wordt met je project.
325
+
326
+ Door attributen te gebruiken kun je dingen doen als het specificeren van aparte merge strategieën voor individuele bestanden of directories in je project, Git vertellen hoe niet-tekst bestanden moeten worden gediff'ed, of Git inhoud laten filteren voordat je het in- of uitcheckt van Git. In deze paragraaf zal je iets leren over de attributen die je kun instellen op de paden in je Git project en een paar voorbeelden zien hoe je deze eigenschappen in de praktijk gebruikt.
327
+
328
+ ### Binaire bestanden ###
329
+
330
+ Een leuke truc waarvoor je Git attributen kunt gebruiken is het vertellen aan Git welke bestanden binair zijn (in die gevallen waarin hij het niet zelf kan ontdekken) en Git dan speciale instructies geven hoe die bestanden te behandelen. Bijvoorbeeld, sommige tekstbestanden worden gegenereerd en zijn niet te diff'en, of sommige binaire bestanden kunnen wel gediff'ed worden – je zult zien hoe je Git vertelt welke soort het is.
331
+
332
+ #### Binaire bestanden identificeren ####
333
+
334
+ Sommige bestanden zien eruit als tekstbestanden, maar moeten toch behandeld worden als binaire gegevens. Bijvoorbeeld, Xcode projecten op de Mac bevatten een bestand dat eindigt in `.pbxproj`, wat eigenlijk een JSON (javascript gegevens in platte tekst formaat) gegevensset is, dat geschreven wordt naar de schijf door de IDE en waarin je bouw-instellingen enzovoorts opgeslagen zijn. Alhoewel het technisch gezien een tekstbestand is, omdat het volledig ASCII is, zul je het niet als zodanig willen behandelen omdat het eigenlijk een lichtgewicht gegevensbank is – je kunt de inhoud niet mergen als twee mensen het gewijzigd hebben, en diffs zijn over het algemeen niet behulpzaam. Het bestand is bedoeld om gebruikt te worden door een machine. In essentie wil je het behandelen als een binair bestand.
335
+
336
+ Om Git te vertellen dat hij alle `pbxproj` bestanden als binaire gegevens moet behandelen, voeg je de volgende regel toe aan je `.gitattributes` bestand:
337
+
338
+ *.pbxproj -crlf -diff
339
+
340
+ Nu zal Git niet proberen om CRLF problemen te corrigeren of repareren; noch zal het proberen een diff te berekenen of te tonen voor de veranderingen in dit bestand als je `git show` of `git diff` uitvoert op je project. In de 1.6 serie van Git, kun je ook het ingebouwde macro `binary` gebruiken die `-crlf -diff` vervangt:
341
+
342
+ *.pbxproj binary
343
+
344
+ #### Binaire bestanden diff'en ####
345
+
346
+ In Git kan je de functionaliteit van Git attributen gebruiken om binaire bestanden uiteindelijk toch te diff'en. Je doet dit door Git te vertellen hoe het de binaire gegevens naar tekst formaat moet omzetten, die dan via de normale diff vergeleken kan worden. Maar de vraag is hoe je de *binaire* gegevens naar tekst omzet. De beste oplossing is om een tool te vinden die deze conversie van binair naar tekstformaat voor je doet. Helaas kunnen weinig binaire formaten als mens-leesbaar formaat gerepresenteerd worden (probeer maar eens de gegevens van een geluidsfragment om te zetten naar tekst). Als dit het geval is en je bent er niet in geslaagd om een tekst-representatie te krijgen van de inhoud van het bestand, is het vaak relatief eenvoudig om een menselijk leesbare omschrijving van de inhoud of metadata te verkrijgen. Metadata geeft je niet een volledige representatie van de inhoud van het bestand, maar het is in elk geval beter dan niets.
347
+
348
+ We gaan beide beschreven varianten gebruiken om bruikbare diff's te krijgen voor vaakgebruikte binaire formaten.
349
+
350
+ Even een terzijde: Er zijn verscheidene binaire formaten met een tekstinhoud waar moeilijk een bruikbare converter voor te vinden is. In die gevallen kan je proberen een tekst uit het bestand extraheren met het `strings` programma. Sommige van deze bestanden zouden een UTF-16 encoding of andere "codepages" kunnen gebruiken en dan kan `strings` niets bruikbaars in die bestanden vinden. Resultaten in het verleden geven geen garantie voor de toekomst. Echter `strings` wordt geleverd bij de meeste Mac en Linux systemen, dus het zou een goede eerste kandidaat zijn om dit te proberen bij veel binaire formaten.
351
+
352
+ #### MS Word bestanden ####
353
+
354
+ Ten eerste zullen we de beschreven techniek gebruiken om een van de meest ergerlijke problemen in de menselijke geschiedenis op te lossen: Word documenten onder versiebeheer brengen. Iedereen weet dat Word een van de slechtste editors verkrijgbaar is; maar, vreemd genoeg, iedereen gebruikt het. Als je Word documenten in versie beheer wilt krijgen, kan je ze in een Git repostiory gooien en om de zoveel tijd een commit uitvoeren, maar wat voor nut heeft dit? Als je `git diff` uitvoert, zou je iets als dit zien:
355
+
356
+
357
+ $ git diff
358
+ diff --git a/chapter1.doc b/chapter1.doc
359
+ index 88839c4..4afcb7c 100644
360
+ Binary files a/chapter1.doc and b/chapter1.doc differ
361
+
362
+ Je kunt niet direct twee versies vergelijken tenzij je ze uitcheckt en handmatig doorneemt, toch? Maar gelukkig kan je dit toch redelijk goed met Git attributen doen. Zet de volgende regel in je `.gitattributes` bestand:
363
+
364
+ *.doc diff=word
365
+
366
+ Dit vertelt Git dat alle bestanden die in het patroon (.doc) passen het "word" filter moeten gebruiken als je een diff probeert te bekijken die wijzigingen bevat. Wat is dat "word" filter eigenlijk? Je moet het zelf opzetten. Hier ga je Git configureren om het `catdoc` programma te gebruiken, die specifiek geschreven is om tekst uit binaire MS Word documenten te extraheren (je kunt het krijgen op `http://www.wagner.pp.ru/~vitus/software/catdoc/`), om Word documenten in leesbare tekstbestanden te converteren, en die vervolgens juist diff'en:
367
+
368
+ $ git config diff.word.textconv catdoc
369
+
370
+ Dit commando voegt een sectie toe aan je `.git/config`, wat er ongeveer zo uitziet:
371
+
372
+ [diff "word"]
373
+ textconv = catdoc
374
+
375
+ Nu weet Git dat wanneer het probeert een diff uit te voeren tussen twee snapshots, en bestandsnamen eindigen op `.doc` dat het deze bestanden door de "word" filter moet halen, die gedefinieerd is als het `catdoc` programma. Effectief maakt het twee gewone tekst-gebaseerde versies van je Word bestanden vooraleer te proberen ze met diff te vergelijken.
376
+
377
+ Hier is een voorbeeld. Ik heb hoofdstuk 1 van dit boek in Git gezet, wat tekst in een paragraaf toegevoegd en daarna het document gesaved. Daarna voerde ik `git diff` uit om te zien wat er gewijzigd was:
378
+
379
+ $ git diff
380
+ diff --git a/chapter1.doc b/chapter1.doc
381
+ index c1c8a0a..b93c9e4 100644
382
+ --- a/chapter1.doc
383
+ +++ b/chapter1.doc
384
+ @@ -128,7 +128,7 @@ and data size)
385
+ Since its birth in 2005, Git has evolved and matured to be easy to use
386
+ and yet retain these initial qualities. It’s incredibly fast, it’s
387
+ very efficient with large projects, and it has an incredible branching
388
+ -system for non-linear development.
389
+ +system for non-linear development (See Chapter 3).
390
+
391
+ Git vertelt me succesvol en kort en bondig dat ik de tekst "(See Chapter 3)" heb toegevoegd, wat juist is. Werkt perfect!
392
+
393
+ ##### OpenDocument tekst bestanden #####
394
+
395
+ Dezelfde aanpak die we hebben gebruikt vooro MS Word bestanden (`*.doc`) kan worden gebruikt bij OpenDocument Tekst bestanden (`*.odt`) wat gemaakt is door OpenOffice.org.
396
+
397
+ Voeg de volgende regel toe aan je `.gitattributes` bestand:
398
+
399
+ *.odt diff=odt
400
+
401
+ Zet nu het `odt` diff filter op in `.git/config`:
402
+
403
+ [diff "odt"]
404
+ binary = true
405
+ textconv = /usr/local/bin/odt-to-txt
406
+
407
+ OpenDocument bestanden zijn eigenlijk gezipte directories met daarin meerdere bestanden (de inhoud ervan in een XML formaat, stylesheets, plaatjes, etc.). Ze zullen een script moeten schrijven om de inhoud te extraheren en dit terug te geven als platte tekst. Maak een bestand `/usr/local/bin/odt-to-txt` (het staat je vrij om dit in een andere directory neer te zetten) met de volgende inhoud:
408
+
409
+ #! /usr/bin/env perl
410
+ # Simplistic OpenDocument Text (.odt) to plain text converter.
411
+ # Author: Philipp Kempgen
412
+
413
+ if (! defined($ARGV[0])) {
414
+ print STDERR "No filename given!\n";
415
+ print STDERR "Usage: $0 filename\n";
416
+ exit 1;
417
+ }
418
+
419
+ my $content = '';
420
+ open my $fh, '-|', 'unzip', '-qq', '-p', $ARGV[0], 'content.xml' or die $!;
421
+ {
422
+ local $/ = undef; # slurp mode
423
+ $content = <$fh>;
424
+ }
425
+ close $fh;
426
+ $_ = $content;
427
+ s/<text:span\b[^>]*>//g; # remove spans
428
+ s/<text:h\b[^>]*>/\n\n***** /g; # headers
429
+ s/<text:list-item\b[^>]*>\s*<text:p\b[^>]*>/\n -- /g; # list items
430
+ s/<text:list\b[^>]*>/\n\n/g; # lists
431
+ s/<text:p\b[^>]*>/\n /g; # paragraphs
432
+ s/<[^>]+>//g; # remove all XML tags
433
+ s/\n{2,}/\n\n/g; # remove multiple blank lines
434
+ s/\A\n+//; # remove leading blank lines
435
+ print "\n", $_, "\n\n";
436
+
437
+ En maak het uitvoerbaar
438
+
439
+ chmod +x /usr/local/bin/odt-to-txt
440
+
441
+ Nu zal `git diff` je kunnen vertellen wat er gewijzigd is in `.odt` bestanden.
442
+
443
+
444
+ ##### Beeldbestanden #####
445
+
446
+ Een ander interessant probleem dat je hiermee kunt oplossen is het diff'en van beeldbestanden. Een manier om dit te doen is PNG bestanden door een filter te halen dat de EXIF informatie eruit peutert - dat is metadata die wordt opgeslagen bij de meeste beeldbestand formaten. Als je het `exiftool` programma downloadt en installeert, kan je dit gebruiken om je plaatjes naar tekst over de metadata om te zetten, zodat de diff op z'n minst een tekstuele representatie van eventuele wijzigingen laat zien:
447
+
448
+ $ echo '*.png diff=exif' >> .gitattributes
449
+ $ git config diff.exif.textconv exiftool
450
+
451
+ Als je een plaatje in je project vervangt en `git diff` uitvoert, dan zie je zoiets als dit:
452
+
453
+ diff --git a/image.png b/image.png
454
+ index 88839c4..4afcb7c 100644
455
+ --- a/image.png
456
+ +++ b/image.png
457
+ @@ -1,12 +1,12 @@
458
+ ExifTool Version Number : 7.74
459
+ -File Size : 70 kB
460
+ -File Modification Date/Time : 2009:04:21 07:02:45-07:00
461
+ +File Size : 94 kB
462
+ +File Modification Date/Time : 2009:04:21 07:02:43-07:00
463
+ File Type : PNG
464
+ MIME Type : image/png
465
+ -Image Width : 1058
466
+ -Image Height : 889
467
+ +Image Width : 1056
468
+ +Image Height : 827
469
+ Bit Depth : 8
470
+ Color Type : RGB with Alpha
471
+
472
+ Je kunt eenvoudig zien dat zowel de bestandsgrootte als de beeld dimensies gewijzigd zijn.
473
+
474
+ ### Keyword expansie ###
475
+
476
+ Keyword expansie zoals in SVN of CVS gebruikt wordt, wordt vaak gevraagd door ontwikkelaars die gewend zijn aan die systemen. Het grote probleem in Git is dat je een bestand niet mag wijzigen met informatie over de commit nadat je het gecommit hebt, omdat Git eerst de checksum van het bestand maakt. Maar, je kunt tekst in een bestand injecteren zodra het uitgechecked wordt en dit weer verwijderen voordat het aan een commit toegevoegd wordt. Met Git attributen zijn er twee manieren om dit te doen.
477
+
478
+ Als eerste kun je de SHA-1 checksum van een blob automatisch in een `$Id$` veld in het bestand stoppen. Als je dit attribuut in een bestand of aantal bestanden zet, dan zal Git de volgende keer dat je die branch uitcheckt dat veld vervangen met de SHA-1 van de blob. Het is belangrijk om op te merken dat het niet de SHA van de commit is, maar van de blob zelf:
479
+
480
+ $ echo '*.txt ident' >> .gitattributes
481
+ $ echo '$Id$' > test.txt
482
+
483
+ De volgende keer dat je dit bestand uitcheckt, injecteert Git de SHA van de blob:
484
+
485
+ $ rm text.txt
486
+ $ git checkout -- text.txt
487
+ $ cat test.txt
488
+ $Id: 42812b7653c7b88933f8a9d6cad0ca16714b9bb3 $
489
+
490
+ Echter dit resultaat heeft slechts een beperkte nut. Als je sleutelwoord vervanging (keyword substitution) in CVS of Subversion gebruikt hebt, kun je een datumstempel toevoegen – de SHA is niet zo bruikbaar, omdat het vrij willekeurig is en je kunt niet zeggen of de ene SHA ouder of nieuwer is dan de andere.
491
+
492
+ Je kunt echter je eigen filters voor het doen van vervanging bij commit/checkout schrijven. Dit zijn de "kuis" ("clean") en "besmeer" ("smudge") filters. In het `.gitattributes` bestand kan je voor bepaalde paden een filter instellen en dan scripts instellen die bestanden bewerkt vlak voordat ze uitgechecked worden ("smudge", zie Figuur 7-2) en vlak voordat ze gecommit worden ("clean", zie Figuur 7-3). De filters kunnen ingesteld worden om allerlei leuke dingen doen.
493
+
494
+ Insert 18333fig0702.png
495
+ Figuur 7-2. Het “smudge” filter wordt bij checkout uitgevoerd.
496
+
497
+ Insert 18333fig0703.png
498
+ Figuur 7-3. Het “clean” filter wordt uitgevoerd zodra bestanden worden gestaged.
499
+
500
+ De originele commit boodschap van deze functionaliteit geeft een eenvoudig voorbeeld hoe je al je C broncode door het `indent` programma kunt laten bewerken alvorens te committen. Je kunt het instellen door het filter attribuut in je `.gitattributes` bestand te zetten zodat `*.c` bestanden door de "indent" filter gehaald worden:
501
+
502
+ *.c filter=indent
503
+
504
+ Vervolgens vertel je Git wat het "indent" filter doet bij smudge en clean:
505
+
506
+ $ git config --global filter.indent.clean indent
507
+ $ git config --global filter.indent.smudge cat
508
+
509
+ In dit geval zal Git, als je bestanden commit die met `*.c` overeenkomen, ze door het indent programma halen alvorens ze te committen, en ze door het `cat` programma halen alvorens ze op de schijf uit te checken. Het `cat` programma is eigenlijk een no-op: het geeft de gegevens onveranderd door. Deze combinatie zal effectief alle C broncode bestanden door `indent` filteren alvorens te committen.
510
+
511
+ Een ander interessant voorbeeld is `$Date$` sleutelwoord expansie, in RCS stijl. Om dit goed te doen, moet je een klein script hebben dat een bestandsnaam pakt, de laatste commit datum voor dit project opzoekt, en de datum in dat bestand toevoegt. Hier volgt een klein Ruby script dat dat doet:
512
+
513
+ #! /usr/bin/env ruby
514
+ data = STDIN.read
515
+ last_date = `git log --pretty=format:"%ad" -1`
516
+ puts data.gsub('$Date$', '$Date: ' + last_date.to_s + '$')
517
+
518
+ Het enige dat het script doet is de laatste commit datum uit het `git log` commando halen, het in iedere `$Date$` tekst stoppen die het in stdin ziet, en de resultaten afdrukken – het moet eenvoudig na te bouwen zijn in de taal waar je het beste in thuisbent. Je kunt dit bestand `expand_date` noemen en het in je pad stoppen. Nu moet je een filter in Git instellen (noem het `dater`), en het vertellen jouw `expand_date` filter te gebruiken om de bestanden tijdens checkout te 'besmeren'. Je zult een Perl expressie gebruiken om dat op te ruimen tijdens een commit:
519
+
520
+ $ git config filter.dater.smudge expand_date
521
+ $ git config filter.dater.clean 'perl -pe "s/\\\$Date[^\\\$]*\\\$/\\\$Date\\\$/"'
522
+
523
+ Dit stukje Perl haalt alles weg dat het in een `$Date$` tekst ziet, om weer op het uitgangspunt uit te komen. Nu je filter klaar is, kun je het testen door een bestand aan te maken met het `$Date$` sleutelwoord en dan een Git attribuut voor dat bestand in te stellen, die het nieuwe filter aanroept.
524
+
525
+ $ echo '# $Date$' > date_test.txt
526
+ $ echo 'date*.txt filter=dater' >> .gitattributes
527
+
528
+ Als je die veranderingen commit en het bestand opnieuw uitcheckt, zal je zien dat het sleutelwoord correct vervangen wordt:
529
+
530
+ $ git add date_test.txt .gitattributes
531
+ $ git commit -m "Testing date expansion in Git"
532
+ $ rm date_test.txt
533
+ $ git checkout date_test.txt
534
+ $ cat date_test.txt
535
+ # $Date: Tue Apr 21 07:26:52 2009 -0700$
536
+
537
+ Je ziet hier hoe krachtig deze techniek is voor gebruik in eigengemaakte toepassingen. Je moet wel voorzichtig zijn, om dat het `.gitattributes` bestand ook gecommit wordt en meegestuurd wordt met het project, maar het filter (in dit geval `dater`) niet; dus het zal niet overal werken. Als je deze filters ontwerpt, zouden ze in staat moeten zijn om netjes te falen en het project nog steeds goed te laten werken.
538
+
539
+ ### Je repository exporteren ###
540
+
541
+ De Git attribute gegevens staan je ook toe om interessante dingen te doen als je een archief van je project exporteert.
542
+
543
+ #### export-ignore ####
544
+
545
+ Je kunt Git vertellen dat sommige bestanden of directories niet geëxporteerd moeten worden bij het genereren van een archief. Als er een subdirectory of bestand is waarvan je niet wilt dat het wordt meegenomen in je archief bestand, maar dat je wel in je project ingecheckt wil hebben, dan kun je die bestanden bepalen met behulp van het `export-ignore` attribuut.
546
+
547
+ Bijvoorbeeld, stel dat je wat testbestanden in een `test/` subdirectory hebt, en dat het geen zin heeft om die in de tarball export van je project mee te nemen. Dan kan je de volgende regel in je Git attributes bestand toevoegen:
548
+
549
+ test/ export-ignore
550
+
551
+ Als je nu `git archive` uitvoert om een tarball van je project te maken, zal die map niet meegenomen worden in het archief.
552
+
553
+ #### export-subst ####
554
+
555
+ Iets anders dat je kunt doen met je archieven is eenvoudige sleutelwoord vervanging. Git staat je toe om de tekst `$Format:$` met een van de `--pretty=format` formaat afkortingen in één of meer bestanden te zetten. Veel voorbeelden van die formaatafkortingen zag je al in Hoofdstuk 2. Bijvoorbeeld, als je een bestand genaamd `LAST_COMMIT` wilt meenemen in je project, waarin de laatste commit datum automatisch wordt geïnjecteerd als `git archive` loopt, kun je het bestand als volgt instellen:
556
+
557
+ $ echo 'Last commit date: $Format:%cd$' > LAST_COMMIT
558
+ $ echo "LAST_COMMIT export-subst" >> .gitattributes
559
+ $ git add LAST_COMMIT .gitattributes
560
+ $ git commit -am 'adding LAST_COMMIT file for archives'
561
+
562
+ Als je `git archive` uitvoert, zal de inhoud van dat bestand, als mensen het archief bestand openen, er zo uit zien:
563
+
564
+ $ cat LAST_COMMIT
565
+ Last commit date: $Format:Tue Apr 21 08:38:48 2009 -0700$
566
+
567
+ ### Merge strategieën ###
568
+
569
+ Je kunt Git attributen ook gebruiken om Git te vertellen dat het verschillende merge strategieën moet gebruiken voor specifieke bestanden in je project. Een erg handige toepassing is Git te vertellen dat het bepaalde bestanden niet moet proberen te mergen als ze conflicten hebben, maar jouw versie moeten gebruiken in plaats van die van de ander.
570
+
571
+ Dit is handig als een branch in jouw project uiteen is gelopen of gespecialiseerd is, maar je wel in staat wilt zijn om veranderingen daarvan te mergen, en je wilt bepaalde bestanden negeren. Stel dat je een database instellingen bestand hebt dat database.xml heet en dat in twee branches verschillend is, en je wilt de andere branch mergen zonder jouw database bestand overhoop te halen. Je kunt dan een attribuut als volgt instellen:
572
+
573
+ database.xml merge=ours
574
+
575
+ Als je in de andere branch merged, dan zul je in plaats van merge conflicten met je database.xml bestand zoiets als dit zien:
576
+
577
+ $ git merge topic
578
+ Auto-merging database.xml
579
+ Merge made by recursive.
580
+
581
+ In dit geval blijft database.xml staan op de versie die je origineel al had.
582
+
583
+ ## Git haken (hooks) ##
584
+
585
+ Zoals vele andere Versie Beheer Systemen, heeft Git een manier om eigengemaakte scripts aan te roepen wanneer bepaalde belangrijke acties plaatsvinden. Er zijn twee groepen van dit soort hooks: aan de client kant en aan de server kant. De hooks aan de client kant zijn voor client operaties zoals committen en mergen. De hooks voor de server kant zijn voor Git server operaties zoals het ontvangen van gepushte commits. Je kunt deze hooks om allerlei redenen gebruiken, en je zult hier over een aantal ervan kunnen lezen.
586
+
587
+ ### Een hook installeren ###
588
+
589
+ De hooks zijn allemaal opgeslagen in de `hooks` subdirectory van de Git directory. In de meeste projecten is dat `.git/hooks`. Standaard voorziet Git deze map van een aantal voorbeeld scripts, waarvan de meeste op zich al bruikbaar zijn, maar ze documenteren ook de invoer waarden van elke script. Alle scripts zijn als shell script geschreven met hier en daar wat Perl, maar iedere executable met de juiste naam zal prima werken – je kunt ze in Ruby of Python of wat je wilt schrijven. De namen van de scripts eindigen op .sample; je zult ze van naam moeten veranderen.
590
+
591
+ Om een hook script aan te zetten, zet je een bestand met de juiste naam en dat uitvoerbaar is in de `hooks` subdirectory van je Git directory. Vanaf dat moment zou het aangeroepen moeten worden. Ik zal de meestgebruikte hook bestandsnamen hier behandelen.
592
+
593
+ ### Hooks aan de client-kant ###
594
+
595
+ Er zijn veel hooks aan de client-kant. Deze paragraaf verdeelt ze in commit-workflow hooks, e-mail-workflow scripts, en de rest van de client-kant scripts
596
+
597
+ #### Commit-workflow hooks ####
598
+
599
+ De eerste vier hooks hebben te maken met het commit proces. De `pre-commit` hook wordt eerst uitgevoerd, nog voor je een commit boodschap intypt. Het wordt gebruikt om het snapshot dat op het punt staat gecommit te worden te inspecteren, om te zien of je iets bent vergeten, om er zeker van te zijn dat tests uitgevoerd worden of om wat je maar wilt te onderzoeken in de code. Als deze hook met een exit-waarde anders dan nul eindigt breekt de commit af, alhoewel je dit kunt omzeilen met `git commit --no-verify`. Je kunt dingen doen als op code stijl controleren (door lint of iets dergelijks uit te voeren), op 'trailing whitespaces' te controleren (de standaard hook doet precies dat), of om de juiste documentatie op nieuwe methodes te controleren.
600
+
601
+ De `prepare-commit-msg` hook wordt uitgevoerd voordat de commit boodschap editor gestart wordt, maar nadat de standaard boodschap aangemaakt is. Het stelt je in staat om de standaard boodschap aan te passen voordat de commit auteur het ziet. Deze hook accepteert een aantal opties: het pad naar het bestand dat de huidige commit boodschap bevat, het type van de commit, en de SHA-1 van de commit als het een verbeterde (amended) commit betreft. Deze hook is voor normale commits niet zo bruikbaar, maar het is juist bruikbaar voor commits waarbij de standaard boodschap automatisch gegenereerd wordt, zoals sjabloon commit boodschappen, merge commits, gesquashte commits en amended commits. Je kan het samen met een commit sjabloon gebruiken om informatie programmatisch in te voegen.
602
+
603
+ De `commit-msg` hook accepteert één parameter, wat, nogmaals, het pad naar een tijdelijk bestand is dat de huidige commit boodschap bevat. Als dit script eindigt met een waarde anders dan nul, dan zal Git het commit proces afbreken, je kunt deze gebruiken om je project-status of de commit boodschap te valideren alvorens een commit toe te staan. In het laatste gedeelte van dit hoofdstuk, zal ik met deze hook demonstreren hoe te controleren dat de commit boodschap aan een bepaald patroon voldoet.
604
+
605
+ Nadat het hele commit proces afgerond is, zal de `post-commit` hook uitgevoerd worden. Het accepteert geen parameters, maar je kunt de laatste commit eenvoudig ophalen door `git log -1 HEAD` uit te voeren. Over het algemeen wordt dit script gebruikt om notificaties of iets dergelijks uit te sturen.
606
+
607
+ De commit-workflow scripts aan de client-kant kunnen gebruikt worden in vrijwel iedere workflow. Ze worden vaak gebruikt om een bepaald beleid af te dwingen, maar het is belangrijk om op te merken dat deze scripts niet overgedragen worden bij het clonen. Je kunt beleid afdwingen aan de server kant door pushes of commits te weigeren die niet voldoen aan een bepaald beleid, maar het is aan de ontwikkelaar om deze scripts aan de client kant te gebruiken. Dus, deze scripts zijn er om ontwikkelaars te helpen en ze moeten door hen ingesteld en onderhouden worden, alhoewel ze door hen op elk moment aangepast of omzeild kunnen worden.
608
+
609
+ #### E-mail workflow hooks ####
610
+
611
+ Je kunt drie client-kant hooks instellen voor een e-mail gebaseerde workflow. Ze worden allemaal aangeroepen door het `git am` commando dus als je dat commando niet gebruikt in je workflow, dan kun je gerust doorgaan naar de volgende paragraaf. Als je patches aanneemt via e-mail die door `git format-patch` geprepareerd zijn, dan zullen sommige van deze scripts nuttig zijn voor je.
612
+
613
+ De eerste hook die uitgevoerd wordt is `applypatch-msg`. Het accepteert één enkel argument: de naam van het tijdelijke bestand dat de voorgedragen commit boodschap bevat. Git breekt de patch als dit script met een waarde ongelijk aan nul eindigt. Je kunt dit gebruiken om je ervan te verzekeren dat een commit boodschap juist geformatteerd is, of om de boodschap te normaliseren door het script de boodschap aan te laten passen.
614
+
615
+ De volgende hook die wordt uitgevoerd tijdens het toepassen van patches via `git am` is `pre-applypatch`. Dit neemt geen argumenten aan en wordt uitgevoerd nadat de patch is ge-applyed, zodat je het kunt gebruiken om het snapshot te inspecteren alvorens de commit te doen. Je kunt tests uitvoeren of de werkdirectory op een andere manier inspecteren met behulp van dit script. Als er iets mist of één van de tests faalt, dan zal eindigen met niet nul het `git am` script afbreken zonder de patch te committen.
616
+
617
+ De laatste hook die uitgevoerd wordt tijdens een `git am` operatie is de `post-applypatch`. Je kunt dat gebruiken om een groep te notificeren of de auteur van de patch die je zojuist gepulled hebt. Je kunt het patch proces niet stoppen met behulp van dit script.
618
+
619
+ #### Andere client hooks ####
620
+
621
+ De `pre-rebase` hook wordt uitgevoerd voordat je ook maar iets rebased, en kan het proces afbreken door met een waarde anders dan nul te eindigen. Je kunt deze hook gebruiken om te voorkomen dat commits die al gepusht zijn gerebased worden. De voorbeeld `pre-rebase` hook die Git installeert doet dit, alhoewel deze er vanuit gaat dat "next" de naam is van de branch die je publiceert. Je zult dat waarschijnlijk moeten veranderen in de naam van je stabiele gepubliceerde branch.
622
+
623
+ Nadat je een succesvolle `git checkout` uitgevoerd hebt, wordt de `post-checkout` hook uitgevoerd; je kunt het gebruiken om je werkdirectory goed in te stellen voor je project omgeving. Dit kan het invoegen van grote binaire bestanden die je niet in versie beheer wil hebben inhouden, of het automatisch genereren van documentatie of iets in die geest.
624
+
625
+ Als laatste wordt de `post-merge` hook uitgevoerd na een succesvolle `merge` commando. Je kunt deze gebruiken om gegevens in de werkstructuur terug te zetten die Git niet kan ophalen, bijvoorbeeld permissie gegevens. Ook kan deze hook gebruikt worden om te valideren of bestanden, die buiten het beheer van Git liggen, in je werkstructuur zitten die je wellicht erin wilt kopiëren als de werk-tree wijzigt.
626
+
627
+ ### Hooks aan de server-kant ###
628
+
629
+ Naast de hooks aan de client-kant, kun je als systeem beheerder ook een paar belangrijke hooks aan de server-kant gebruiken om vrijwel elk beleid op het project af te dwingen. Deze scripts worden voor en na de pushes op de server uitgevoerd. De pre hooks kunnen op elk gewenst moment met een getal anders dan nul eindigen om de push te weigeren en een foutmelding naar de client te sturen; je kunt een push beleid instellen dat zo complex is als je zelf wenst.
630
+
631
+ #### pre-receive en post-receive ####
632
+
633
+ Het eerste script dat uitgevoerd wordt tijdens het afhandelen van een push van een client is `pre-receive`. Het leest een lijst van referenties die worden gepusht van stdin; als het eindigt met een andere waarde dan nul, worden ze geen van allen geaccepteerd. Je kunt deze hook gebruiken om dingen te doen als valideren dat geen van de vernieuwde referenties een non-fast-forward is, of om te controleren dat de gebruiker die de push uitvoert creatie, verwijder, of push toegang heeft, of toegang om wijzigingen te pushen voor alle bestanden die ze proberen aan te passen met de push.
634
+
635
+ De `post-receive` hook wordt uitgevoerd nadat het hele proces afgerond is, en het kan gebruikt worden om andere services te vernieuwen of gebruikers te notificeren. Het leest dezelfde stdin gegevens als de `pre-receive` hook. Voorbeelden zijn een e-mail sturen naar een lijst, een continue integratie server notificeren of het vernieuwen van een ticket-volg systeem. Je kunt zelfs de commit boodschappen doorlopen om te zien of er nog tickets zijn die moeten worden geopend, aangepast of afgesloten worden. Dit script kan het push proces niet stoppen, maar de client verbreekt de connectie niet totdat het afgerond is, wees dus een voorzichtig als je iets probeert te doen dat een lange tijd in beslag kan nemen.
636
+
637
+ #### update ####
638
+
639
+ Het update script is vergelijkbaar met het `pre-receive` script, behalve dat het uitgevoerd wordt voor iedere branch die de pusher probeert te vernieuwen. Als de pusher naar meerdere branches probeert te pushen wordt `pre-receive` slechts één keer uitgevoerd, daarentegen loopt update bij iedere branch waar ze naar pushen. In plaats van stdin te lezen, accepteert dit script drie argumenten: de naam van de referentie (branch), de SHA-1 waar die referentie naar wees vóór de push, en de SHA-1 die de gebruiker probeert te pushen. Als het update script met een andere waarde dan nul eindigt, wordt alleen die referentie geweigerd; andere referenties kunnen nog steeds vernieuwd worden.
640
+
641
+ ## Een voorbeeld van Git-afgedwongen beleid ##
642
+
643
+ In deze paragraaf ga je gebruiken wat je geleerd hebt om een Git workflow te maken, die controleert op een aangepast commit boodschap formaat, afdwingt om alleen fast-forward pushes te accepteren en alleen bepaalde gebruikers toestaat om bepaalde subdirectories te wijzigen in een project. Je zult client scripts maken die de ontwikkelaar helpen te ontdekken of hun push geweigerd zal worden en server scripts die het beleid afdwingen.
644
+
645
+ Ik heb Ruby gebruikt om ze te schrijven, zowel omdat het mijn voorkeur script taal is, als omdat ik vind dat het de meest pseudo code uitziende taal is van de scripttalen; dus je zou in staat moeten zijn om de code redelijk te kunnen volgen zelfs als je geen Ruby gebruikt. Maar elke taal zou prima werken. Alle voorbeeld hook scripts die met Git meegeleverd worden zijn Perl of Bash scripts, dus je kunt ook genoeg voorbeelden van hooks in die talen vinden door naar die bestanden te kijken.
646
+
647
+ ### Server-kant hook ###
648
+
649
+ Al het werk aan de server kant zal in het update bestand in je hooks directory gaan zitten. Het update bestand zal eens per gepushte branch uitgevoerd worden en accepteert de referentie waarnaar gepusht wordt, de oude revisie waar die branch was en de nieuwe gepushte revisie. Je hebt ook toegang tot de gebruiker die de push doet als de push via SSH gedaan wordt. Als je iedereen hebt toegestaan om connectie te maken als één gebruiker (zoals "git") via publieke sleutel authenticatie, dan moet je wellicht die gebruiker een shell wrapper geven die bepaalt welke gebruiker er connectie maakt op basis van de publieke sleutel, en dan een omgevingsvariabele instellen waarin die gebruiker wordt gespecificeerd. Ik ga er vanuit dat de gebruiker in de `$USER` omgevingsvariabele staat, dus begint je update script met het verzamelen van alle gegevens die het nodig heeft:
650
+
651
+ #!/usr/bin/env ruby
652
+
653
+ refname = ARGV[0]
654
+ oldrev = ARGV[1]
655
+ newrev = ARGV[2]
656
+ user = ENV['USER']
657
+
658
+ puts "Enforcing Policies... \n(#{refname}) (#{oldrev[0,6]}) (#{newrev[0,6]})"
659
+
660
+ #### Een specifiek commit-bericht formaat afdwingen ####
661
+
662
+ Je eerste uitdaging is afdwingen dat elke commit bericht moet voldoen aan een specifiek formaat. Laten we zeggen dat ieder bericht een stuk tekst moet bevatten dat eruit ziet als "ref: 1234", omdat je wilt dat iedere commit gekoppeld is aan een werkonderdeel in je ticket systeem. Je moet dus kijken naar iedere commit die gepusht wordt, zien of die tekst in de commit boodschap zit en als de tekst in één van de commits ontbreekt, met niet nul eindigen zodat de push geweigerd wordt.
663
+
664
+ Je kunt de lijst met alle SHA-1 waarden van alle commits die gepusht worden verkrijgen door de `$newrev` en `$oldrev` waarden te pakken en ze aan een Git plumbing commando genaamd `git rev-list` te geven. Dit is min of meer het `git log` commando, maar standaard voert het alleen de SHA-1 waarden uit en geen andere informatie. Dus, om een lijst te krijgen van alle commit SHA's die worden geïntroduceerd tussen één commit SHA en een andere, kun je zoiets als dit uitvoeren:
665
+
666
+ $ git rev-list 538c33..d14fc7
667
+ d14fc7c847ab946ec39590d87783c69b031bdfb7
668
+ 9f585da4401b0a3999e84113824d15245c13f0be
669
+ 234071a1be950e2a8d078e6141f5cd20c1e61ad3
670
+ dfa04c9ef3d5197182f13fb5b9b1fb7717d2222a
671
+ 17716ec0f1ff5c77eff40b7fe912f9f6cfd0e475
672
+
673
+ Je kunt die uitvoer pakken, door elk van die commit SHA's heen lopen, de boodschap daarvan pakken en die boodschap testen tegen een reguliere expressie die op een bepaald patroon zoekt.
674
+
675
+ Je moet uit zien te vinden hoe je de commit boodschap kunt krijgen van alle te testen commits. Om de echte commit gegevens te krijgen, kun je een andere plumbing commando genaamd `git cat-file` gebruiken. Ik zal al plumbing commando's in detail behandelen in Hoofdstuk 9, maar voor nu is dit wat het commando je geeft:
676
+
677
+ $ git cat-file commit ca82a6
678
+ tree cfda3bf379e4f8dba8717dee55aab78aef7f4daf
679
+ parent 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
680
+ author Scott Chacon <schacon@gmail.com> 1205815931 -0700
681
+ committer Scott Chacon <schacon@gmail.com> 1240030591 -0700
682
+
683
+ changed the version number
684
+
685
+ Een simpele manier om de commit boodschap uit een commit waarvan je de SHA-1 waarde hebt te krijgen, is naar de eerste lege regel gaan en alles wat daarna komt pakken. Je kunt dat doen met het `sed` commando op Unix systemen:
686
+
687
+ $ git cat-file commit ca82a6 | sed '1,/^$/d'
688
+ changed the version number
689
+
690
+ Je kunt die toverspreuk gebruiken om de commit boodschap te pakken van iedere commit die geprobeerd wordt te pushen en eindigen als je ziet dat er iets is wat niet past. Om het script te eindigen en de push te weigeren, eindig je met niet nul. De hele methode ziet er zo uit:
691
+
692
+ $regex = /\[ref: (\d+)\]/
693
+
694
+ # eigen commit bericht formaat afgedwongen
695
+ def check_message_format
696
+ missed_revs = `git rev-list #{$oldrev}..#{$newrev}`.split("\n")
697
+ missed_revs.each do |rev|
698
+ message = `git cat-file commit #{rev} | sed '1,/^$/d'`
699
+ if !$regex.match(message)
700
+ puts "[POLICY] Your message is not formatted correctly"
701
+ exit 1
702
+ end
703
+ end
704
+ end
705
+ check_message_format
706
+
707
+ Door dat in je `update` script te stoppen, zullen updates geweigerd worden die commits bevatten met berichten die niet aan jouw beleid voldoen.
708
+
709
+ #### Een gebruiker-gebaseerd ACL systeem afdwingen ####
710
+
711
+ Stel dat je een mechanisme wil toevoegen dat gebruik maakt van een toegangscontrole lijst (ACL) die specificeert welke gebruikers zijn toegestaan om wijzigingen te pushen naar bepaalde delen van je project. Sommige mensen hebben volledige toegang, en anderen hebben alleen toestemming om wijzigingen te pushen naar bepaalde subdirectories of specifieke bestanden. Om dit af te dwingen zul je die regels schrijven in een bestand genaamd `acl` dat in je bare Git repository op de server zit. Je zult de `update` hook naar die regels laten kijken, zien welke bestanden worden geïntroduceerd voor elke commit die gepusht wordt en bepalen of de gebruiker die de push doet toestemming heeft om al die bestanden te wijzigen.
712
+
713
+ Het eerste dat je zult doen is de ACL schrijven. Hier zul je een formaat gebruiken wat erg lijkt op het CVS ACL mechanisme: het gebruikt een serie regels, waarbij het eerste veld `avail` of `unavail` is, het volgende veld een komma gescheiden lijst van de gebruikers is waarvoor de regel geldt en het laatste veld het pad is waarvoor deze regel geldt (leeg betekent open toegang). Alle velden worden gescheiden door een pipe (`|`) karakter.
714
+
715
+ In dit geval heb je een aantal beheerders, een aantal documentatie schrijvers met toegang tot de `doc` map, en één ontwikkelaar die alleen toegang heeft tot de `lib` en `test` mappen, en je ACL bestand ziet er zo uit:
716
+
717
+ avail|nickh,pjhyett,defunkt,tpw
718
+ avail|usinclair,cdickens,ebronte|doc
719
+ avail|schacon|lib
720
+ avail|schacon|tests
721
+
722
+ Je begint met deze gegevens in een structuur in te lezen die je kunt gebruiken. In dit geval, om het voorbeeld eenvoudig te houden, zul je alleen de `avail` richtlijnen handhaven. Hier is een methode die je een associatieve array teruggeeft, waarbij de sleutel de gebruikersnaam is en de waarde een array van paden waar die gebruiker toegang tot heeft:
723
+
724
+ def get_acl_access_data(acl_file)
725
+ # lees ACL gegevens
726
+ acl_file = File.read(acl_file).split("\n").reject { |line| line == '' }
727
+ access = {}
728
+ acl_file.each do |line|
729
+ avail, users, path = line.split('|')
730
+ next unless avail == 'avail'
731
+ users.split(',').each do |user|
732
+ access[user] ||= []
733
+ access[user] << path
734
+ end
735
+ end
736
+ access
737
+ end
738
+
739
+ Gegeven het ACL bestand dat je eerder bekeken hebt, zal deze `get_acl_access_data` methode een gegevensstructuur opleveren die er als volgt uit ziet:
740
+
741
+ {"defunkt"=>[nil],
742
+ "tpw"=>[nil],
743
+ "nickh"=>[nil],
744
+ "pjhyett"=>[nil],
745
+ "schacon"=>["lib", "tests"],
746
+ "cdickens"=>["doc"],
747
+ "usinclair"=>["doc"],
748
+ "ebronte"=>["doc"]}
749
+
750
+ Nu je de rechten bepaald hebt, moet je bepalen welke paden de commits die gepusht worden hebben aangepast, zodat je kunt controleren dat de gebruiker die de push doet daar ook toegang tot heeft.
751
+
752
+ Je kunt eenvoudig zien welke bestanden gewijzigd zijn in een enkele commit met de `--name-only` optie op het `git log` commando (kort besproken in Hoofdstuk 2):
753
+
754
+ $ git log -1 --name-only --pretty=format:'' 9f585d
755
+
756
+ README
757
+ lib/test.rb
758
+
759
+ Als je gebruik maakt van de ACL structuur die wordt teruggegeven door de `get_acl_access_data` methode en dat gebruikt met de bestanden in elk van de commits, dan kun je bepalen of de gebruiker toegang heeft om al hun commits te pushen:
760
+
761
+ # staat alleen bepaalde gebruikers toe om bepaalde subdirectories in een project te wijzigen
762
+ def check_directory_perms
763
+ access = get_acl_access_data('acl')
764
+
765
+ # kijk of iemand iets probeert te pushen waar ze niet bij mogen komen
766
+ new_commits = `git rev-list #{$oldrev}..#{$newrev}`.split("\n")
767
+ new_commits.each do |rev|
768
+ files_modified = `git log -1 --name-only --pretty=format:'' #{rev}`.split("\n")
769
+ files_modified.each do |path|
770
+ next if path.size == 0
771
+ has_file_access = false
772
+ access[$user].each do |access_path|
773
+ if !access_path || # gebruiker heeft overal toegang tot
774
+ (path.index(access_path) == 0) # toegang tot dit pad
775
+ has_file_access = true
776
+ end
777
+ end
778
+ if !has_file_access
779
+ puts "[POLICY] You do not have access to push to #{path}"
780
+ exit 1
781
+ end
782
+ end
783
+ end
784
+ end
785
+
786
+ check_directory_perms
787
+
788
+ Het meeste hiervan zou makkelijk te volgen moeten zijn. Je krijgt een lijst met commits die gepusht worden naar je server met `git rev-list`. Daarna vind je, voor iedere commit, de bestanden die aangepast worden en stelt vast of de gebruiker die pusht toegang heeft tot alle paden die worden aangepast. Een Ruby-isme dat wellicht niet duidelijk is, is `path.index(access_path) == 0`, wat waar is als het pad begint met `access_path` – dit zorgt ervoor dat `access_path` niet slechts in één van de toegestane paden zit, maar dat het voorkomt in alle toegestane paden.
789
+
790
+ Nu kunnen je gebruikers geen commits pushen met slecht vormgegeven berichten of met aangepaste bestanden buiten hun toegewezen paden.
791
+
792
+ #### Fast-forward-only pushes afdwingen ####
793
+
794
+ Het laatste om af te dwingen is fast-forward-only pushes. Om dit te regelen, kan je simpelweg de `receive.denyDeletes` en `receive.denyNonFastForwards` instellingen aanpassen. Maar dit afdwingen met behulp van een hook werkt ook, en je kunt het aanpassen zodat het alleen gebeurt bij bepaalde gebruikers of om elke reden die je later bedenkt.
795
+
796
+ De logica om dit te controleren, is te kijken of er een commit is die bereikbaar is vanuit de oudere revisie, maar niet bereikbaar is vanuit de nieuwere. Als er geen zijn dan was het een fast-forward push, anders weiger je het:
797
+
798
+ # dwingt fast-forward-only pushes af
799
+ def check_fast_forward
800
+ missed_refs = `git rev-list #{$newrev}..#{$oldrev}`
801
+ missed_ref_count = missed_refs.split("\n").size
802
+ if missed_ref_count > 0
803
+ puts "[POLICY] Cannot push a non fast-forward reference"
804
+ exit 1
805
+ end
806
+ end
807
+
808
+ check_fast_forward
809
+
810
+ Alles is ingesteld. Als je `chmod u+x .git/hooks/update` uitvoert, wat het bestand is waarin je al deze code gestopt hebt, en dan probeert een non-fast-forwarded referentie te pushen krijg je zoiets als dit:
811
+
812
+
813
+ $ git push -f origin master
814
+ Counting objects: 5, done.
815
+ Compressing objects: 100% (3/3), done.
816
+ Writing objects: 100% (3/3), 323 bytes, done.
817
+ Total 3 (delta 1), reused 0 (delta 0)
818
+ Unpacking objects: 100% (3/3), done.
819
+ Enforcing Policies...
820
+ (refs/heads/master) (8338c5) (c5b616)
821
+ [POLICY] Cannot push a non-fast-forward reference
822
+ error: hooks/update exited with error code 1
823
+ error: hook declined to update refs/heads/master
824
+ To git@gitserver:project.git
825
+ ! [remote rejected] master -> master (hook declined)
826
+ error: failed to push some refs to 'git@gitserver:project.git'
827
+
828
+ Er zijn hier een aantal interessante dingen. Ten eerste, zie je dit als de hook start met uitvoeren.
829
+
830
+ Enforcing Policies...
831
+ (refs/heads/master) (fb8c72) (c56860)
832
+
833
+ Merk op dat je dat afgedrukt hebt naar stdout aan het begin van je update script. Het is belangrijk om te zien dat alles dat je script naar stdout uitvoert, naar de client overgebracht wordt.
834
+
835
+ Het volgende dat je op zal vallen is de foutmelding.
836
+
837
+ [POLICY] Cannot push a non fast-forward reference
838
+ error: hooks/update exited with error code 1
839
+ error: hook declined to update refs/heads/master
840
+
841
+ De eerste regel was door jou afgedrukt, de andere twee komen van Git die je vertelt dat het update script met niet nul geëindigd is en dat die degene is die je push weigerde. Tot slot heb je dit:
842
+
843
+ To git@gitserver:project.git
844
+ ! [remote rejected] master -> master (hook declined)
845
+ error: failed to push some refs to 'git@gitserver:project.git'
846
+
847
+ Je zult een remote afwijs bericht zien voor elke referentie die door de hook wordt geweigerd, en het vertelt je dat het specifiek was geweigerd wegens een hook fout.
848
+
849
+ Daarnaast zal je een foutmelding zien als in één van je commits de referentie markering ontbreekt.
850
+
851
+ [POLICY] Your message is not formatted correctly
852
+
853
+ Of als iemand een bestand probeert aan te passen waar ze geen toegang tot hebben en een commit proberen te pushen waar het in zit, dan zullen ze iets vergelijkbaars zien. Bijvoorbeeld, als een documentatie schrijver een commit probeert te pushen dat iets wijzigt dat in de `lib` map zit, dan zien ze
854
+
855
+ [POLICY] You do not have access to push to lib/test.rb
856
+
857
+ Dat is alles. Vanaf nu, zolang als het `update` script aanwezig en uitvoerbaar is, zal je repository nooit teruggedraaid worden en zal nooit een commit bericht bevatten waar het patroon niet in zit, en je gebruikers zullen in hun bewegingsruimte beperkt zijn.
858
+
859
+ ### Hooks aan de client-kant ###
860
+
861
+ Het nadeel van deze aanpak is het zeuren dat geheid zal beginnen zodra de commits van je gebruikers geweigerd worden. Het feit dat hun zorgzaam vervaardigde werk op het laatste moment pas geweigerd wordt kan enorm frustrerend en verwarrend zijn en daarnaast zullen ze hun geschiedenis moeten aanpassen om het te corrigeren, wat niet altijd geschikt is voor de wat zwakkeren van hart.
862
+
863
+ Het antwoord op dit dilemma is een aantal client-kant hooks te leveren, die gebruikers kunnen gebruiken om hen te waarschuwen dat ze iets doen dat de server waarschijnlijk gaat weigeren. Op die manier kunnen ze alle problemen corrigeren voordat ze gaan committen en voordat die problemen lastiger te herstellen zijn. Omdat haken niet overgebracht worden bij het klonen van een project, moet je deze scripts op een andere manier distribueren en je gebruikers ze in hun `.git/hooks` map laten zetten en ze uitvoerbaar maken. Je kunt deze hooks in je project of in een apart project distribueren, maar er is geen manier om ze automatisch in te laten stellen.
864
+
865
+ Om te beginnen zou je de commit boodschap moeten controleren vlak voordat iedere commit opgeslagen wordt, zodat je weet dat de server je wijzigingen niet gaat weigeren omdat de commit boodschap een verkeerd formaat heeft. Om dit te doen, kun je de `commit-msg` hook toevoegen. Als je dat de commit boodschap laat lezen uit het bestand dat als eerste argument opgegeven wordt, en dat vergelijkt met het patroon dan kun je Git dwingen om de commit af te breken als het niet juist is:
866
+
867
+ #!/usr/bin/env ruby
868
+ message_file = ARGV[0]
869
+ message = File.read(message_file)
870
+
871
+ $regex = /\[ref: (\d+)\]/
872
+
873
+ if !$regex.match(message)
874
+ puts "[POLICY] Your message is not formatted correctly"
875
+ exit 1
876
+ end
877
+
878
+ Als dat script op z'n plaats staat (in `.git/hooks/commit-msg`), uitvoerbaar is en je commit met een verkeerd geformateerd bericht, dan zie je dit:
879
+
880
+ $ git commit -am 'test'
881
+ [POLICY] Your message is not formatted correctly
882
+
883
+ In dat geval is er geen commit gedaan. Maar als je bericht het juiste patroon bevat, dan staat Git je toe te committen:
884
+
885
+ $ git commit -am 'test [ref: 132]'
886
+ [master e05c914] test [ref: 132]
887
+ 1 files changed, 1 insertions(+), 0 deletions(-)
888
+
889
+ Vervolgens wil je er zeker van zijn dat je geen bestanden buiten je ACL scope aanpast. Als de `.git` directory van je project een kopie van het ACL bestand bevat dat je eerder gebruikte, dan zal het volgende `pre-commit` script die beperkingen voor je controleren:
890
+
891
+ #!/usr/bin/env ruby
892
+
893
+ $user = ENV['USER']
894
+
895
+ # [ insert acl_access_data method from above ]
896
+
897
+ # staat alleen bepaalde gebruikers toe bepaalde mappen aan te passen
898
+ def check_directory_perms
899
+ access = get_acl_access_data('.git/acl')
900
+
901
+ files_modified = `git diff-index --cached --name-only HEAD`.split("\n")
902
+ files_modified.each do |path|
903
+ next if path.size == 0
904
+ has_file_access = false
905
+ access[$user].each do |access_path|
906
+ if !access_path || (path.index(access_path) == 0)
907
+ has_file_access = true
908
+ end
909
+ if !has_file_access
910
+ puts "[POLICY] You do not have access to push to #{path}"
911
+ exit 1
912
+ end
913
+ end
914
+ end
915
+
916
+ check_directory_perms
917
+
918
+ Dit is grofweg hetzelfde script als aan de server kant, maar met twee belangrijke verschillen. Als eerste staat het ACL bestand op een andere plek, omdat dit script vanuit je werkdirectory draait, en niet vanuit je Git directory. Je moet het pad naar het ACL bestand wijzigen van dit
919
+
920
+ access = get_acl_access_data('acl')
921
+
922
+ naar dit:
923
+
924
+ access = get_acl_access_data('.git/acl')
925
+
926
+ Het andere belangrijke verschil is de manier waarop je een lijst krijgt met bestanden die gewijzigd is. Omdat de server kant methode naar de log van commits kijkt en nu je commit nog niet opgeslagen is, moet je de bestandslijst in plaats daarvan uit het staging area halen. In plaats van
927
+
928
+ files_modified = `git log -1 --name-only --pretty=format:'' #{ref}`
929
+
930
+ moet je dit gebruiken
931
+
932
+ files_modified = `git diff-index --cached --name-only HEAD`
933
+
934
+ Maar dat zijn de enige twee verschillen - verder werkt het script op dezelfde manier. Een aandachtspunt is dat het van je verlangt dat je lokaal werkt als dezelfde gebruiker die pusht naar de remote machine. Als dat anders is, moet je de `$user` variabele handmatig instellen.
935
+
936
+ Het laatste wat je moet doen is het controleren dat je niet probeert non-fast-forward referenties te pushen, maar dat komt minder voor. Om een referentie te krijgen dat non-fast-forward is, moet je voorbij een commit rebasen die je al gepusht hebt, of een andere lokale branch naar dezelfde remote branch proberen te pushen.
937
+
938
+ Omdat de server je zal vertellen dat je geen non-fast-forward push kunt doen, en de hook de push tegenhoudt, is het enige ding wat je kunt proberen af te vangen het abusievelijk rebasen van commits die je al gepusht hebt.
939
+
940
+ Hier is een voorbeeld pre-rebase script dat daarop controleert. Het haalt een lijst met alle commits die je op het punt staat te herschrijven, en controleert of ze al ergens bestaan in één van je remote referenties. Als het er een ziet die bereikbaar is vanuit een van je remote referenties, dan stopt het de rebase:
941
+
942
+ #!/usr/bin/env ruby
943
+
944
+ base_branch = ARGV[0]
945
+ if ARGV[1]
946
+ topic_branch = ARGV[1]
947
+ else
948
+ topic_branch = "HEAD"
949
+ end
950
+
951
+ target_shas = `git rev-list #{base_branch}..#{topic_branch}`.split("\n")
952
+ remote_refs = `git branch -r`.split("\n").map { |r| r.strip }
953
+
954
+ target_shas.each do |sha|
955
+ remote_refs.each do |remote_ref|
956
+ shas_pushed = `git rev-list ^#{sha}^@ refs/remotes/#{remote_ref}`
957
+ if shas_pushed.split(“\n”).include?(sha)
958
+ puts "[POLICY] Commit #{sha} has already been pushed to #{remote_ref}"
959
+ exit 1
960
+ end
961
+ end
962
+ end
963
+
964
+ Dit script gebruikt een syntax dat niet behandeld is in de Revisie Selectie paragraaf van Hoofdstuk 6. Je krijgt een lijst van commits die al gepusht zijn door dit uit te voeren:
965
+
966
+ git rev-list ^#{sha}^@ refs/remotes/#{remote_ref}
967
+
968
+ De `SHA^@` syntax wordt vervangen door alle ouders van die commit. Je bent op zoek naar een commit die bereikbaar is vanuit de laatste commit op de remote en die niet bereikbaar is vanuit enige ouder van alle SHA's die je probeert te pushen – wat betekent dat het een fast-forward is.
969
+
970
+ Het grote nadeel van deze aanpak is dat het erg traag kan zijn en vaak onnodig is, als je de push niet probeert te forceren met de `-f` optie, dan zal de server je al waarschuwen en de push niet accepteren. Maar, het is een aardige oefening en kan je in theorie helpen om een rebase te voorkomen die je later zult moeten herstellen.
971
+
972
+ ## Samenvatting ##
973
+
974
+ We hebben nu de meeste manieren behandeld waarin je jouw Git client en server aan kunt passen om aan jouw workflow en projecten te voldoen. Je hebt allerhande configuratie instellingen geleerd, bestands-gebaseerde attributen, en gebeurtenis hooks (event hooks) en je hebt een voorbeeld gemaakt van een server die beleid afdwingt. Je zou nu in staat moeten zijn om Git binnen iedere workflow die je kunt verzinnen te laten werken.