commonmarker 0.0.1 → 0.1.0

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

Potentially problematic release.


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

Files changed (501) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -2
  3. data/README.md +67 -42
  4. data/Rakefile +22 -2
  5. data/commonmarker.gemspec +13 -9
  6. data/ext/commonmarker/cmark/api_test/main.c +35 -0
  7. data/ext/commonmarker/cmark/build/CMakeFiles/CMakeError.log +12 -12
  8. data/ext/commonmarker/cmark/build/CMakeFiles/CMakeOutput.log +141 -141
  9. data/ext/commonmarker/cmark/build/api_test/CMakeFiles/api_test.dir/main.c.o +0 -0
  10. data/ext/commonmarker/cmark/build/api_test/api_test +0 -0
  11. data/ext/commonmarker/cmark/build/src/CMakeFiles/cmark.dir/houdini_html_u.c.o +0 -0
  12. data/ext/commonmarker/cmark/build/src/CMakeFiles/cmark.dir/iterator.c.o +0 -0
  13. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark.dir/houdini_html_u.c.o +0 -0
  14. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark.dir/iterator.c.o +0 -0
  15. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/houdini_html_u.c.o +0 -0
  16. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/iterator.c.o +0 -0
  17. data/ext/commonmarker/cmark/build/src/cmark +0 -0
  18. data/ext/commonmarker/cmark/build/src/libcmark.0.19.0.dylib +0 -0
  19. data/ext/commonmarker/cmark/build/src/libcmark.a +0 -0
  20. data/ext/commonmarker/cmark/build/src/libcmark.dylib +0 -0
  21. data/ext/commonmarker/cmark/src/houdini_html_u.c +26 -13
  22. data/ext/commonmarker/cmark/src/iterator.c +2 -2
  23. data/ext/commonmarker/cmark/test/__pycache__/cmark.cpython-34.pyc +0 -0
  24. data/ext/commonmarker/cmark/test/__pycache__/normalize.cpython-34.pyc +0 -0
  25. data/ext/commonmarker/cmark/test/cmark.pyc +0 -0
  26. data/ext/commonmarker/cmark/test/normalize.pyc +0 -0
  27. data/ext/commonmarker/commonmarker.c +276 -3
  28. data/ext/commonmarker/extconf.rb +3 -1
  29. data/lib/commonmarker.rb +70 -360
  30. data/lib/commonmarker/config.rb +1 -1
  31. data/lib/commonmarker/renderer.rb +91 -0
  32. data/lib/commonmarker/renderer/html_renderer.rb +149 -0
  33. data/lib/commonmarker/version.rb +1 -1
  34. data/test/benchinput.md +148414 -0
  35. data/test/benchmark.rb +13 -9
  36. data/test/progit/Gemfile +5 -0
  37. data/test/progit/README.md +9 -0
  38. data/test/progit/README.original.md +70 -0
  39. data/test/progit/Rakefile +285 -0
  40. data/test/progit/ar/01-introduction/01-chapter1.markdown +264 -0
  41. data/test/progit/ar/02-git-basics/01-chapter2.markdown +1124 -0
  42. data/test/progit/ar/NOTES +18 -0
  43. data/test/progit/ar/README +14 -0
  44. data/test/progit/az/01-introduction/01-chapter1.markdown +257 -0
  45. data/test/progit/az/02-git-basics/01-chapter2.markdown +1127 -0
  46. data/test/progit/az/03-git-branching/01-chapter3.markdown +598 -0
  47. data/test/progit/az/04-git-server/01-chapter4.markdown +861 -0
  48. data/test/progit/az/05-distributed-git/01-chapter5.markdown +897 -0
  49. data/test/progit/az/06-git-tools/01-chapter6.markdown +1126 -0
  50. data/test/progit/az/07-customizing-git/01-chapter7.markdown +937 -0
  51. data/test/progit/az/08-git-and-other-scms/01-chapter8.markdown +690 -0
  52. data/test/progit/az/09-git-internals/01-chapter9.markdown +977 -0
  53. data/test/progit/be/01-introduction/01-chapter1.markdown +257 -0
  54. data/test/progit/be/02-git-basics/01-chapter2.markdown +1126 -0
  55. data/test/progit/ca/01-introduction/01-chapter1.markdown +257 -0
  56. data/test/progit/ca/README.txt +1 -0
  57. data/test/progit/couchapp/Makefile +41 -0
  58. data/test/progit/couchapp/Readme.md +17 -0
  59. data/test/progit/couchapp/_id +1 -0
  60. data/test/progit/couchapp/shows/chapter.js +14 -0
  61. data/test/progit/couchapp/templates/foot.html +7 -0
  62. data/test/progit/couchapp/templates/head.html +51 -0
  63. data/test/progit/couchapp/vendor/markdown/showdown.js +420 -0
  64. data/test/progit/couchapp/vendor/mustache.js/mustache.js +302 -0
  65. data/test/progit/cs/01-introduction/01-chapter1.markdown +259 -0
  66. data/test/progit/cs/02-git-basics/01-chapter2.markdown +1225 -0
  67. data/test/progit/cs/03-git-branching/01-chapter3.markdown +606 -0
  68. data/test/progit/cs/04-git-server/01-chapter4.markdown +871 -0
  69. data/test/progit/cs/05-distributed-git/01-chapter5.markdown +914 -0
  70. data/test/progit/cs/06-git-tools/01-chapter6.markdown +1167 -0
  71. data/test/progit/cs/07-customizing-git/01-chapter7.markdown +940 -0
  72. data/test/progit/cs/08-git-and-other-scms/01-chapter8.markdown +700 -0
  73. data/test/progit/cs/09-git-internals/01-chapter9.markdown +1014 -0
  74. data/test/progit/de/01-introduction/01-chapter1.markdown +445 -0
  75. data/test/progit/de/02-git-basics/01-chapter2.markdown +1589 -0
  76. data/test/progit/de/03-git-branching/01-chapter3.markdown +964 -0
  77. data/test/progit/de/04-git-server/01-chapter4.markdown +1337 -0
  78. data/test/progit/de/05-distributed-git/01-chapter5.markdown +1329 -0
  79. data/test/progit/de/06-git-tools/01-chapter6.markdown +1502 -0
  80. data/test/progit/de/07-customizing-git/01-chapter7.markdown +1361 -0
  81. data/test/progit/de/08-git-and-other-scms/01-chapter8.markdown +919 -0
  82. data/test/progit/de/09-git-internals/01-chapter9.markdown +1361 -0
  83. data/test/progit/de/README.md +626 -0
  84. data/test/progit/ebooks/cover.png +0 -0
  85. data/test/progit/en/01-introduction/01-chapter1.markdown +263 -0
  86. data/test/progit/en/02-git-basics/01-chapter2.markdown +1228 -0
  87. data/test/progit/en/03-git-branching/01-chapter3.markdown +606 -0
  88. data/test/progit/en/04-git-server/01-chapter4.markdown +871 -0
  89. data/test/progit/en/05-distributed-git/01-chapter5.markdown +914 -0
  90. data/test/progit/en/06-git-tools/01-chapter6.markdown +1150 -0
  91. data/test/progit/en/07-customizing-git/01-chapter7.markdown +940 -0
  92. data/test/progit/en/08-git-and-other-scms/01-chapter8.markdown +700 -0
  93. data/test/progit/en/09-git-internals/01-chapter9.markdown +983 -0
  94. data/test/progit/eo/01-introduction/01-chapter1.markdown +257 -0
  95. data/test/progit/eo/02-git-basics/01-chapter2.markdown +1171 -0
  96. data/test/progit/epub/ProGit.css +28 -0
  97. data/test/progit/epub/title.png +0 -0
  98. data/test/progit/es-ni/01-introduction/01-chapter1.markdown +257 -0
  99. data/test/progit/es-ni/02-git-basics/01-chapter2.markdown +1127 -0
  100. data/test/progit/es/01-introduction/01-chapter1.markdown +262 -0
  101. data/test/progit/es/02-git-basics/01-chapter2.markdown +1165 -0
  102. data/test/progit/es/03-git-branching/01-chapter3.markdown +598 -0
  103. data/test/progit/es/04-git-server/01-chapter4.markdown +707 -0
  104. data/test/progit/es/05-distributed-git/01-chapter5.markdown +890 -0
  105. data/test/progit/es/06-git-tools/01-chapter6.markdown +1113 -0
  106. data/test/progit/es/07-customizing-git/01-chapter7.markdown +875 -0
  107. data/test/progit/es/08-git-and-other-scms/01-chapter8.markdown +686 -0
  108. data/test/progit/es/09-git-internals/01-chapter9.markdown +976 -0
  109. data/test/progit/es/NOTES +29 -0
  110. data/test/progit/es/README +3 -0
  111. data/test/progit/es/glosario-Benzirpi.txt +27 -0
  112. data/test/progit/es/omegat-Benzirpi.tmx +29075 -0
  113. data/test/progit/fa/01-introduction/01-chapter1.markdown +262 -0
  114. data/test/progit/fa/03-git-branching/01-chapter3.markdown +608 -0
  115. data/test/progit/fa/04-git-server/01-chapter4.markdown +872 -0
  116. data/test/progit/fa/NOTES.en-fa.md +143 -0
  117. data/test/progit/fa/README.md +7 -0
  118. data/test/progit/fi/01-introduction/01-chapter1.markdown +259 -0
  119. data/test/progit/fi/02-git-basics/01-chapter2.markdown +1171 -0
  120. data/test/progit/fi/NOTES +5 -0
  121. data/test/progit/figures-dia/fig0101.dia +617 -0
  122. data/test/progit/figures-dia/fig0102.dia +921 -0
  123. data/test/progit/figures-dia/fig0103.dia +1468 -0
  124. data/test/progit/figures-dia/fig0104.dia +1432 -0
  125. data/test/progit/figures-dia/fig0105.dia +1924 -0
  126. data/test/progit/figures-dia/fig0106.dia +562 -0
  127. data/test/progit/figures-dia/fig0201.dia +774 -0
  128. data/test/progit/figures-dia/fig0301.dia +2006 -0
  129. data/test/progit/figures-dia/fig0302.dia +2148 -0
  130. data/test/progit/figures-dia/fig0303.dia +719 -0
  131. data/test/progit/figures-dia/fig0304.dia +525 -0
  132. data/test/progit/figures-dia/fig0305.dia +622 -0
  133. data/test/progit/figures-dia/fig0306.dia +622 -0
  134. data/test/progit/figures-dia/fig0307.dia +719 -0
  135. data/test/progit/figures-dia/fig0308.dia +734 -0
  136. data/test/progit/figures-dia/fig0309.dia +831 -0
  137. data/test/progit/figures-dia/fig0310.dia +412 -0
  138. data/test/progit/figures-dia/fig0311.dia +493 -0
  139. data/test/progit/figures-dia/fig0312.dia +596 -0
  140. data/test/progit/figures-dia/fig0313.dia +774 -0
  141. data/test/progit/figures-dia/fig0314.dia +846 -0
  142. data/test/progit/figures-dia/fig0315.dia +787 -0
  143. data/test/progit/figures-dia/fig0316.dia +1078 -0
  144. data/test/progit/figures-dia/fig0317.dia +881 -0
  145. data/test/progit/figures-dia/fig0318.dia +968 -0
  146. data/test/progit/figures-dia/fig0319.dia +957 -0
  147. data/test/progit/figures-dia/fig0320.dia +1637 -0
  148. data/test/progit/figures-dia/fig0321.dia +1494 -0
  149. data/test/progit/figures-dia/fig0322.dia +1142 -0
  150. data/test/progit/figures-dia/fig0323.dia +1377 -0
  151. data/test/progit/figures-dia/fig0324.dia +1603 -0
  152. data/test/progit/figures-dia/fig0325.dia +2003 -0
  153. data/test/progit/figures-dia/fig0326.dia +2013 -0
  154. data/test/progit/figures-dia/fig0327.dia +687 -0
  155. data/test/progit/figures-dia/fig0328.dia +814 -0
  156. data/test/progit/figures-dia/fig0329.dia +793 -0
  157. data/test/progit/figures-dia/fig0330.dia +693 -0
  158. data/test/progit/figures-dia/fig0331.dia +1159 -0
  159. data/test/progit/figures-dia/fig0332.dia +1362 -0
  160. data/test/progit/figures-dia/fig0333.dia +1165 -0
  161. data/test/progit/figures-dia/fig0334.dia +1450 -0
  162. data/test/progit/figures-dia/fig0335.dia +994 -0
  163. data/test/progit/figures-dia/fig0336.dia +786 -0
  164. data/test/progit/figures-dia/fig0337.dia +1546 -0
  165. data/test/progit/figures-dia/fig0338.dia +1755 -0
  166. data/test/progit/figures-dia/fig0339.dia +1882 -0
  167. data/test/progit/figures-dia/fig0501.dia +456 -0
  168. data/test/progit/figures-dia/fig0502.dia +956 -0
  169. data/test/progit/figures-dia/fig0503.dia +915 -0
  170. data/test/progit/figures-dia/fig0504.dia +620 -0
  171. data/test/progit/figures-dia/fig0505.dia +744 -0
  172. data/test/progit/figures-dia/fig0506.dia +747 -0
  173. data/test/progit/figures-dia/fig0507.dia +895 -0
  174. data/test/progit/figures-dia/fig0508.dia +1122 -0
  175. data/test/progit/figures-dia/fig0509.dia +1243 -0
  176. data/test/progit/figures-dia/fig0510.dia +1240 -0
  177. data/test/progit/figures-dia/fig0511.dia +1201 -0
  178. data/test/progit/figures-dia/fig0512.dia +801 -0
  179. data/test/progit/figures-dia/fig0513.dia +1387 -0
  180. data/test/progit/figures-dia/fig0514.dia +1568 -0
  181. data/test/progit/figures-dia/fig0515.dia +1721 -0
  182. data/test/progit/figures-dia/fig0516.dia +997 -0
  183. data/test/progit/figures-dia/fig0517.dia +994 -0
  184. data/test/progit/figures-dia/fig0518.dia +1145 -0
  185. data/test/progit/figures-dia/fig0519.dia +992 -0
  186. data/test/progit/figures-dia/fig0520.dia +1240 -0
  187. data/test/progit/figures-dia/fig0521.dia +801 -0
  188. data/test/progit/figures-dia/fig0522.dia +922 -0
  189. data/test/progit/figures-dia/fig0523.dia +922 -0
  190. data/test/progit/figures-dia/fig0524.dia +1828 -0
  191. data/test/progit/figures-dia/fig0525.dia +2685 -0
  192. data/test/progit/figures-dia/fig0526.dia +717 -0
  193. data/test/progit/figures-dia/fig0527.dia +856 -0
  194. data/test/progit/figures-dia/fig0601.dia +790 -0
  195. data/test/progit/figures-dia/fig0702.dia +795 -0
  196. data/test/progit/figures-dia/fig0703.dia +795 -0
  197. data/test/progit/figures-dia/fig0901.dia +669 -0
  198. data/test/progit/figures-dia/fig0902.dia +834 -0
  199. data/test/progit/figures-dia/fig0903.dia +1483 -0
  200. data/test/progit/figures-dia/fig0904.dia +1728 -0
  201. data/test/progit/figures-dia/makeimages +25 -0
  202. data/test/progit/figures-source/progit.graffle +123108 -0
  203. data/test/progit/figures/18333fig0101-tn.png +0 -0
  204. data/test/progit/figures/18333fig0102-tn.png +0 -0
  205. data/test/progit/figures/18333fig0103-tn.png +0 -0
  206. data/test/progit/figures/18333fig0104-tn.png +0 -0
  207. data/test/progit/figures/18333fig0105-tn.png +0 -0
  208. data/test/progit/figures/18333fig0106-tn.png +0 -0
  209. data/test/progit/figures/18333fig0107-tn.png +0 -0
  210. data/test/progit/figures/18333fig0201-tn.png +0 -0
  211. data/test/progit/figures/18333fig0202-tn.png +0 -0
  212. data/test/progit/figures/18333fig0301-tn.png +0 -0
  213. data/test/progit/figures/18333fig0302-tn.png +0 -0
  214. data/test/progit/figures/18333fig0303-tn.png +0 -0
  215. data/test/progit/figures/18333fig0304-tn.png +0 -0
  216. data/test/progit/figures/18333fig0305-tn.png +0 -0
  217. data/test/progit/figures/18333fig0306-tn.png +0 -0
  218. data/test/progit/figures/18333fig0307-tn.png +0 -0
  219. data/test/progit/figures/18333fig0308-tn.png +0 -0
  220. data/test/progit/figures/18333fig0309-tn.png +0 -0
  221. data/test/progit/figures/18333fig0310-tn.png +0 -0
  222. data/test/progit/figures/18333fig0311-tn.png +0 -0
  223. data/test/progit/figures/18333fig0312-tn.png +0 -0
  224. data/test/progit/figures/18333fig0313-tn.png +0 -0
  225. data/test/progit/figures/18333fig0314-tn.png +0 -0
  226. data/test/progit/figures/18333fig0315-tn.png +0 -0
  227. data/test/progit/figures/18333fig0316-tn.png +0 -0
  228. data/test/progit/figures/18333fig0317-tn.png +0 -0
  229. data/test/progit/figures/18333fig0318-tn.png +0 -0
  230. data/test/progit/figures/18333fig0319-tn.png +0 -0
  231. data/test/progit/figures/18333fig0320-tn.png +0 -0
  232. data/test/progit/figures/18333fig0321-tn.png +0 -0
  233. data/test/progit/figures/18333fig0322-tn.png +0 -0
  234. data/test/progit/figures/18333fig0323-tn.png +0 -0
  235. data/test/progit/figures/18333fig0324-tn.png +0 -0
  236. data/test/progit/figures/18333fig0325-tn.png +0 -0
  237. data/test/progit/figures/18333fig0326-tn.png +0 -0
  238. data/test/progit/figures/18333fig0327-tn.png +0 -0
  239. data/test/progit/figures/18333fig0328-tn.png +0 -0
  240. data/test/progit/figures/18333fig0329-tn.png +0 -0
  241. data/test/progit/figures/18333fig0330-tn.png +0 -0
  242. data/test/progit/figures/18333fig0331-tn.png +0 -0
  243. data/test/progit/figures/18333fig0332-tn.png +0 -0
  244. data/test/progit/figures/18333fig0333-tn.png +0 -0
  245. data/test/progit/figures/18333fig0334-tn.png +0 -0
  246. data/test/progit/figures/18333fig0335-tn.png +0 -0
  247. data/test/progit/figures/18333fig0336-tn.png +0 -0
  248. data/test/progit/figures/18333fig0337-tn.png +0 -0
  249. data/test/progit/figures/18333fig0338-tn.png +0 -0
  250. data/test/progit/figures/18333fig0339-tn.png +0 -0
  251. data/test/progit/figures/18333fig0401-tn.png +0 -0
  252. data/test/progit/figures/18333fig0402-tn.png +0 -0
  253. data/test/progit/figures/18333fig0403-tn.png +0 -0
  254. data/test/progit/figures/18333fig0404-tn.png +0 -0
  255. data/test/progit/figures/18333fig0405-tn.png +0 -0
  256. data/test/progit/figures/18333fig0406-tn.png +0 -0
  257. data/test/progit/figures/18333fig0407-tn.png +0 -0
  258. data/test/progit/figures/18333fig0408-tn.png +0 -0
  259. data/test/progit/figures/18333fig0409-tn.png +0 -0
  260. data/test/progit/figures/18333fig0410-tn.png +0 -0
  261. data/test/progit/figures/18333fig0411-tn.png +0 -0
  262. data/test/progit/figures/18333fig0412-tn.png +0 -0
  263. data/test/progit/figures/18333fig0413-tn.png +0 -0
  264. data/test/progit/figures/18333fig0414-tn.png +0 -0
  265. data/test/progit/figures/18333fig0415-tn.png +0 -0
  266. data/test/progit/figures/18333fig0501-tn.png +0 -0
  267. data/test/progit/figures/18333fig0502-tn.png +0 -0
  268. data/test/progit/figures/18333fig0503-tn.png +0 -0
  269. data/test/progit/figures/18333fig0504-tn.png +0 -0
  270. data/test/progit/figures/18333fig0505-tn.png +0 -0
  271. data/test/progit/figures/18333fig0506-tn.png +0 -0
  272. data/test/progit/figures/18333fig0507-tn.png +0 -0
  273. data/test/progit/figures/18333fig0508-tn.png +0 -0
  274. data/test/progit/figures/18333fig0509-tn.png +0 -0
  275. data/test/progit/figures/18333fig0510-tn.png +0 -0
  276. data/test/progit/figures/18333fig0511-tn.png +0 -0
  277. data/test/progit/figures/18333fig0512-tn.png +0 -0
  278. data/test/progit/figures/18333fig0513-tn.png +0 -0
  279. data/test/progit/figures/18333fig0514-tn.png +0 -0
  280. data/test/progit/figures/18333fig0515-tn.png +0 -0
  281. data/test/progit/figures/18333fig0516-tn.png +0 -0
  282. data/test/progit/figures/18333fig0517-tn.png +0 -0
  283. data/test/progit/figures/18333fig0518-tn.png +0 -0
  284. data/test/progit/figures/18333fig0519-tn.png +0 -0
  285. data/test/progit/figures/18333fig0520-tn.png +0 -0
  286. data/test/progit/figures/18333fig0521-tn.png +0 -0
  287. data/test/progit/figures/18333fig0522-tn.png +0 -0
  288. data/test/progit/figures/18333fig0523-tn.png +0 -0
  289. data/test/progit/figures/18333fig0524-tn.png +0 -0
  290. data/test/progit/figures/18333fig0525-tn.png +0 -0
  291. data/test/progit/figures/18333fig0526-tn.png +0 -0
  292. data/test/progit/figures/18333fig0527-tn.png +0 -0
  293. data/test/progit/figures/18333fig0601-tn.png +0 -0
  294. data/test/progit/figures/18333fig0701-tn.png +0 -0
  295. data/test/progit/figures/18333fig0702-tn.png +0 -0
  296. data/test/progit/figures/18333fig0703-tn.png +0 -0
  297. data/test/progit/figures/18333fig0901-tn.png +0 -0
  298. data/test/progit/figures/18333fig0902-tn.png +0 -0
  299. data/test/progit/figures/18333fig0903-tn.png +0 -0
  300. data/test/progit/figures/18333fig0904-tn.png +0 -0
  301. data/test/progit/fr/01-introduction/01-chapter1.markdown +371 -0
  302. data/test/progit/fr/02-git-basics/01-chapter2.markdown +1378 -0
  303. data/test/progit/fr/03-git-branching/01-chapter3.markdown +781 -0
  304. data/test/progit/fr/04-git-server/01-chapter4.markdown +1141 -0
  305. data/test/progit/fr/05-distributed-git/01-chapter5.markdown +1163 -0
  306. data/test/progit/fr/06-git-tools/01-chapter6.markdown +1356 -0
  307. data/test/progit/fr/07-customizing-git/01-chapter7.markdown +1200 -0
  308. data/test/progit/fr/08-git-and-other-scms/01-chapter8.markdown +832 -0
  309. data/test/progit/fr/09-git-internals/01-chapter9.markdown +1228 -0
  310. data/test/progit/fr/NOTES.fr-fr.markdown +1 -0
  311. data/test/progit/fr/NOTES.fr-fr.md +127 -0
  312. data/test/progit/fr/README.md +43 -0
  313. data/test/progit/fr/glossaire-git.adoc +108 -0
  314. data/test/progit/hi/01-introduction/01-chapter1.markdown +7 -0
  315. data/test/progit/hu/01-introduction/01-chapter1.markdown +257 -0
  316. data/test/progit/id/01-introduction/01-chapter1.markdown +257 -0
  317. data/test/progit/id/02-git-basics/01-chapter2.markdown +1127 -0
  318. data/test/progit/id/03-git-branching/01-chapter3.markdown +598 -0
  319. data/test/progit/it/01-introduction/01-chapter1.markdown +263 -0
  320. data/test/progit/it/02-git-basics/01-chapter2.markdown +1227 -0
  321. data/test/progit/it/03-git-branching/01-chapter3.markdown +598 -0
  322. data/test/progit/it/04-git-server/01-chapter4.markdown +864 -0
  323. data/test/progit/it/05-distributed-git/01-chapter5.markdown +897 -0
  324. data/test/progit/it/06-git-tools/01-chapter6.markdown +1144 -0
  325. data/test/progit/it/07-customizing-git/01-chapter7.markdown +606 -0
  326. data/test/progit/it/08-git-and-other-scms/01-chapter8.markdown +707 -0
  327. data/test/progit/it/09-git-internals/01-chapter9.markdown +1000 -0
  328. data/test/progit/ja/01-introduction/01-chapter1.markdown +260 -0
  329. data/test/progit/ja/02-git-basics/01-chapter2.markdown +1221 -0
  330. data/test/progit/ja/03-git-branching/01-chapter3.markdown +604 -0
  331. data/test/progit/ja/04-git-server/01-chapter4.markdown +863 -0
  332. data/test/progit/ja/05-distributed-git/01-chapter5.markdown +908 -0
  333. data/test/progit/ja/06-git-tools/01-chapter6.markdown +1133 -0
  334. data/test/progit/ja/07-customizing-git/01-chapter7.markdown +936 -0
  335. data/test/progit/ja/08-git-and-other-scms/01-chapter8.markdown +690 -0
  336. data/test/progit/ja/09-git-internals/01-chapter9.markdown +984 -0
  337. data/test/progit/ja/README.md +58 -0
  338. data/test/progit/ja/translation glossaries.txt +33 -0
  339. data/test/progit/ko/01-introduction/01-chapter1.markdown +258 -0
  340. data/test/progit/ko/02-git-basics/01-chapter2.markdown +1181 -0
  341. data/test/progit/ko/03-git-branching/01-chapter3.markdown +612 -0
  342. data/test/progit/ko/04-git-server/01-chapter4.markdown +867 -0
  343. data/test/progit/ko/05-distributed-git/01-chapter5.markdown +913 -0
  344. data/test/progit/ko/06-git-tools/01-chapter6.markdown +1142 -0
  345. data/test/progit/ko/07-customizing-git/01-chapter7.markdown +935 -0
  346. data/test/progit/ko/08-git-and-other-scms/01-chapter8.markdown +688 -0
  347. data/test/progit/ko/09-git-internals/01-chapter9.markdown +976 -0
  348. data/test/progit/ko/README.md +75 -0
  349. data/test/progit/ko/translation_guide.txt +65 -0
  350. data/test/progit/latex/README +27 -0
  351. data/test/progit/latex/config.yml +144 -0
  352. data/test/progit/latex/makepdf +207 -0
  353. data/test/progit/latex/template.tex +155 -0
  354. data/test/progit/makeebooks +125 -0
  355. data/test/progit/makepdfs +47 -0
  356. data/test/progit/mk/01-introduction/01-chapter1.markdown +258 -0
  357. data/test/progit/mk/02-git-basics/01-chapter2.markdown +1125 -0
  358. data/test/progit/mk/03-git-branching/01-chapter3.markdown +598 -0
  359. data/test/progit/mk/05-distributed-git/01-chapter5.markdown +897 -0
  360. data/test/progit/nl/01-introduction/01-chapter1.markdown +296 -0
  361. data/test/progit/nl/02-git-basics/01-chapter2.markdown +1253 -0
  362. data/test/progit/nl/03-git-branching/01-chapter3.markdown +642 -0
  363. data/test/progit/nl/04-git-server/01-chapter4.markdown +902 -0
  364. data/test/progit/nl/05-distributed-git/01-chapter5.markdown +953 -0
  365. data/test/progit/nl/06-git-tools/01-chapter6.markdown +1177 -0
  366. data/test/progit/nl/07-customizing-git/01-chapter7.markdown +974 -0
  367. data/test/progit/nl/08-git-and-other-scms/01-chapter8.markdown +725 -0
  368. data/test/progit/nl/09-git-internals/01-chapter9.markdown +1013 -0
  369. data/test/progit/no-nb/01-introduction/01-chapter1.markdown +261 -0
  370. data/test/progit/no-nb/02-git-basics/01-chapter2.markdown +1225 -0
  371. data/test/progit/no-nb/03-git-branching/01-chapter3.markdown +606 -0
  372. data/test/progit/no-nb/04-git-server/01-chapter4.markdown +867 -0
  373. data/test/progit/no-nb/05-distributed-git/01-chapter5.markdown +914 -0
  374. data/test/progit/no-nb/06-git-tools/01-chapter6.markdown +1144 -0
  375. data/test/progit/no-nb/07-customizing-git/01-chapter7.markdown +936 -0
  376. data/test/progit/no-nb/08-git-and-other-scms/01-chapter8.markdown +689 -0
  377. data/test/progit/no-nb/09-git-internals/01-chapter9.markdown +977 -0
  378. data/test/progit/no-nb/README +2 -0
  379. data/test/progit/pl/01-introduction/01-chapter1.markdown +257 -0
  380. data/test/progit/pl/02-git-basics/02-chapter2.markdown +1128 -0
  381. data/test/progit/pl/03-git-branching/01-chapter3.markdown +598 -0
  382. data/test/progit/pl/04-git-server/01-chapter4.markdown +897 -0
  383. data/test/progit/pl/05-distributed-git/01-chapter5.markdown +1278 -0
  384. data/test/progit/pl/06-git-tools/01-chapter6.markdown +1550 -0
  385. data/test/progit/pl/07-customizing-git/01-chapter7.markdown +1058 -0
  386. data/test/progit/pl/08-git-and-other-scms/01-chapter8.markdown +948 -0
  387. data/test/progit/pl/09-git-internals/01-chapter9.markdown +1382 -0
  388. data/test/progit/pl/translation-guidelines.txt +70 -0
  389. data/test/progit/pt-br/01-introduction/01-chapter1.markdown +256 -0
  390. data/test/progit/pt-br/02-git-basics/01-chapter2.markdown +1127 -0
  391. data/test/progit/pt-br/03-git-branching/01-chapter3.markdown +596 -0
  392. data/test/progit/pt-br/04-git-server/01-chapter4.markdown +888 -0
  393. data/test/progit/pt-br/05-distributed-git/01-chapter5.markdown +896 -0
  394. data/test/progit/pt-br/06-git-tools/01-chapter6.markdown +1122 -0
  395. data/test/progit/pt-br/07-customizing-git/01-chapter7.markdown +932 -0
  396. data/test/progit/pt-br/08-git-and-other-scms/01-chapter8.markdown +691 -0
  397. data/test/progit/pt-br/09-git-internals/01-chapter9.markdown +978 -0
  398. data/test/progit/pt-br/figures-dia/fig0101.dia +617 -0
  399. data/test/progit/pt-br/figures-dia/fig0102.dia +921 -0
  400. data/test/progit/pt-br/figures-dia/fig0103.dia +1468 -0
  401. data/test/progit/pt-br/figures-dia/fig0104.dia +1432 -0
  402. data/test/progit/pt-br/figures-dia/fig0105.dia +1924 -0
  403. data/test/progit/pt-br/figures-dia/fig0106.dia +562 -0
  404. data/test/progit/pt-br/figures-dia/fig0201.dia +776 -0
  405. data/test/progit/pt-br/figures-dia/fig0301.dia +2006 -0
  406. data/test/progit/pt-br/figures-dia/fig0302.dia +2148 -0
  407. data/test/progit/pt-br/figures-dia/fig0316.dia +1079 -0
  408. data/test/progit/pt-br/figures-dia/fig0322.dia +1142 -0
  409. data/test/progit/pt-br/figures-dia/fig0323.dia +1407 -0
  410. data/test/progit/pt-br/figures-dia/fig0324.dia +1603 -0
  411. data/test/progit/pt-br/figures-dia/fig0325.dia +2003 -0
  412. data/test/progit/pt-br/figures-dia/fig0326.dia +2013 -0
  413. data/test/progit/pt-br/figures-dia/fig0336.dia +786 -0
  414. data/test/progit/pt-br/figures-dia/fig0337.dia +1546 -0
  415. data/test/progit/pt-br/figures-dia/fig0338.dia +1755 -0
  416. data/test/progit/pt-br/figures-dia/fig0339.dia +1882 -0
  417. data/test/progit/pt-br/figures-dia/fig0501.dia +456 -0
  418. data/test/progit/pt-br/figures-dia/fig0502.dia +965 -0
  419. data/test/progit/pt-br/figures-dia/fig0503.dia +914 -0
  420. data/test/progit/pt-br/figures-dia/fig0511.dia +1201 -0
  421. data/test/progit/pt-br/figures-dia/fig0515.dia +1721 -0
  422. data/test/progit/pt-br/figures-dia/fig0702.dia +795 -0
  423. data/test/progit/pt-br/figures-dia/fig0703.dia +795 -0
  424. data/test/progit/pt-br/figures-dia/fig0901.dia +669 -0
  425. data/test/progit/pt-br/figures-dia/fig0902.dia +834 -0
  426. data/test/progit/pt-br/figures-dia/fig0903.dia +1483 -0
  427. data/test/progit/pt-br/figures-dia/fig0904.dia +1728 -0
  428. data/test/progit/ro/01-introduction/01-chapter1.markdown +257 -0
  429. data/test/progit/ru/01-introduction/01-chapter1.markdown +259 -0
  430. data/test/progit/ru/02-git-basics/01-chapter2.markdown +1155 -0
  431. data/test/progit/ru/03-git-branching/01-chapter3.markdown +598 -0
  432. data/test/progit/ru/04-git-server/01-chapter4.markdown +854 -0
  433. data/test/progit/ru/05-distributed-git/01-chapter5.markdown +897 -0
  434. data/test/progit/ru/06-git-tools/01-chapter6.markdown +1126 -0
  435. data/test/progit/ru/07-customizing-git/01-chapter7.markdown +938 -0
  436. data/test/progit/ru/08-git-and-other-scms/01-chapter8.markdown +691 -0
  437. data/test/progit/ru/09-git-internals/01-chapter9.markdown +977 -0
  438. data/test/progit/ru/Glossary +38 -0
  439. data/test/progit/ru/README +12 -0
  440. data/test/progit/ru/figures-dia/fig0101.dia +647 -0
  441. data/test/progit/ru/figures-dia/fig0102.dia +1009 -0
  442. data/test/progit/ru/figures-dia/fig0103.dia +1468 -0
  443. data/test/progit/ru/figures-dia/fig0104.dia +1432 -0
  444. data/test/progit/ru/figures-dia/fig0105.dia +1924 -0
  445. data/test/progit/ru/figures-dia/fig0106.dia +561 -0
  446. data/test/progit/ru/figures-dia/fig0201.dia +774 -0
  447. data/test/progit/ru/figures-dia/fig0322.dia +1182 -0
  448. data/test/progit/ru/figures-dia/fig0323.dia +1457 -0
  449. data/test/progit/ru/figures-dia/fig0324.dia +1698 -0
  450. data/test/progit/ru/figures-dia/fig0325.dia +2101 -0
  451. data/test/progit/ru/figures-dia/fig0326.dia +2111 -0
  452. data/test/progit/ru/figures-dia/fig0336.dia +786 -0
  453. data/test/progit/ru/figures-dia/fig0337.dia +1546 -0
  454. data/test/progit/ru/figures-dia/fig0338.dia +1755 -0
  455. data/test/progit/ru/figures-dia/fig0339.dia +1882 -0
  456. data/test/progit/ru/figures-dia/fig0501.dia +477 -0
  457. data/test/progit/ru/figures-dia/fig0502.dia +1063 -0
  458. data/test/progit/ru/figures-dia/fig0503.dia +915 -0
  459. data/test/progit/ru/figures-dia/fig0511.dia +1201 -0
  460. data/test/progit/ru/figures-dia/fig0515.dia +1741 -0
  461. data/test/progit/ru/figures-dia/fig0702.dia +851 -0
  462. data/test/progit/ru/figures-dia/fig0703.dia +851 -0
  463. data/test/progit/sr/01-introduction/01-chapter1.markdown +257 -0
  464. data/test/progit/summary.rb +29 -0
  465. data/test/progit/th/01-introduction/01-chapter1.markdown +257 -0
  466. data/test/progit/th/02-git-basics/01-chapter2.markdown +1126 -0
  467. data/test/progit/th/README.md +47 -0
  468. data/test/progit/tr/01-introduction/01-chapter1.markdown +258 -0
  469. data/test/progit/tr/02-git-basics/01-chapter2.markdown +1129 -0
  470. data/test/progit/tr/03-git-branching/01-chapter3.markdown +598 -0
  471. data/test/progit/tr/04-git-server/01-chapter4.markdown +73 -0
  472. data/test/progit/tr/05-distributed-git/01-chapter5.markdown +215 -0
  473. data/test/progit/uk/01-introduction/01-chapter1.markdown +522 -0
  474. data/test/progit/vi/01-introduction/01-chapter1.markdown +259 -0
  475. data/test/progit/vi/02-git-basics/01-chapter2.markdown +1172 -0
  476. data/test/progit/vi/03-git-branching/01-chapter3.markdown +598 -0
  477. data/test/progit/zh-tw/01-introduction/01-chapter1.markdown +259 -0
  478. data/test/progit/zh-tw/02-git-basics/01-chapter2.markdown +1183 -0
  479. data/test/progit/zh-tw/03-git-branching/01-chapter3.markdown +604 -0
  480. data/test/progit/zh-tw/04-git-server/01-chapter4.markdown +866 -0
  481. data/test/progit/zh-tw/05-distributed-git/01-chapter5.markdown +912 -0
  482. data/test/progit/zh-tw/06-git-tools/01-chapter6.markdown +1139 -0
  483. data/test/progit/zh-tw/07-customizing-git/01-chapter7.markdown +932 -0
  484. data/test/progit/zh-tw/08-git-and-other-scms/01-chapter8.markdown +689 -0
  485. data/test/progit/zh-tw/09-git-internals/01-chapter9.markdown +977 -0
  486. data/test/progit/zh/01-introduction/01-chapter1.markdown +259 -0
  487. data/test/progit/zh/02-git-basics/01-chapter2.markdown +1177 -0
  488. data/test/progit/zh/03-git-branching/01-chapter3.markdown +604 -0
  489. data/test/progit/zh/04-git-server/01-chapter4.markdown +866 -0
  490. data/test/progit/zh/05-distributed-git/01-chapter5.markdown +912 -0
  491. data/test/progit/zh/06-git-tools/01-chapter6.markdown +1125 -0
  492. data/test/progit/zh/07-customizing-git/01-chapter7.markdown +935 -0
  493. data/test/progit/zh/08-git-and-other-scms/01-chapter8.markdown +689 -0
  494. data/test/progit/zh/09-git-internals/01-chapter9.markdown +976 -0
  495. data/test/spec_tests.json +4382 -4070
  496. data/test/test_basics.rb +1 -1
  497. data/test/test_helper.rb +1 -0
  498. data/test/test_maliciousness.rb +4 -2
  499. data/test/test_pathological_inputs.rb +31 -30
  500. data/test/test_spec.rb +5 -4
  501. metadata +972 -4
@@ -0,0 +1,932 @@
1
+ # Customizando o Git #
2
+
3
+ Até agora, eu mostrei o básico de como o Git funciona, como usá-lo e apresentei algumas ferramentas que o Git provê para ajudar a usá-lo de forma fácil e eficiente. Neste capítulo, eu mostrarei algumas operações que você pode usar para fazer operações com o Git de uma maneira mais customizada, introduzindo várias configurações importantes e um sistemas de hooks. Com essas ferramentas, será fácil trabalhar com o Git da melhor forma para você, sua empresa ou qualquer grupo.
4
+
5
+ ## Configuração do Git ##
6
+
7
+ Como você viu brevemente no Capítulo 1, você pode configurar o Git com o comando `git config`. Uma das primeiras coisas que você fez foi configurar seu nome e endereço de email:
8
+
9
+ $ git config --global user.name "John Doe"
10
+ $ git config --global user.email johndoe@example.com
11
+
12
+ Agora você vai aprender algumas opções mais interessantes que você pode definir dessa maneira para customizar o uso do Git.
13
+
14
+ Você viu alguns detalhes simples de configuração do Git no primeiro capítulo, mas vou passar por eles de novo rapidamente. Git usa uma série de arquivos de configuração para determinar comportamentos não-padrão que você pode querer utilizar. O primeiro lugar que o Git procura por estes valores é no arquivo `/etc/gitconfig`, que contém os valores para todos os usuários do sistema e todos os seus repositórios. Se você passar a opção `--system` para `git config`, ele lê e escreve a partir deste arquivo especificamente.
15
+
16
+ O próximo lugar que o Git olha é no arquivo `~/.gitconfig`, que é específico para cada usuário. Você pode fazer o Git ler e escrever neste arquivo, passando a opção `--global`.
17
+
18
+ Finalmente, Git procura por valores de configuração no arquivo de configuração no diretório Git (`.git/config`) de qualquer repositório que você esteja usando atualmente. Estes valores são específicos para esse repositório. Cada nível substitui valores no nível anterior, então, valores em `.git/config` sobrepõem valores em `/etc/gitconfig`. Você também pode definir esses valores manualmente, editando o arquivo e inserindo a sintaxe correta mas, é geralmente mais fácil executar o comando `git config`.
19
+
20
+ ### Configuração Básica do Cliente ###
21
+
22
+ As opções de configuração reconhecidas pelo Git se dividem em duas categorias: lado cliente e lado servidor. A maioria das opções são do lado cliente e utilizadas para configurar suas preferências pessoais de trabalho. Apesar de haverem muitas opções disponíveis, só cobrirei as que são comumente usadas ​​ou podem afetar significativamente o fluxo de trabalho. Muitas opções são úteis apenas em casos extremos que não mostraremos aqui. Se você quiser ver uma lista de todas as opções que a sua versão do Git reconhece, você pode executar
23
+
24
+ $ git config --help
25
+
26
+ A página do manual do `git config` lista todas as opções disponíveis com um pouco de detalhe.
27
+
28
+ #### core.editor ####
29
+
30
+ Por padrão, o Git usa o editor de texto que você definiu como padrão no Shell ou então reverte para o editor Vi para criar e editar suas mensagens de commit e tags. Para alterar esse padrão, você pode usar a opção `core.editor`:
31
+
32
+ $ git config --global core.editor emacs
33
+
34
+ Agora, não importa o que esteja definido como seu editor padrão, o Git usará o editor Emacs.
35
+
36
+ #### commit.template ####
37
+
38
+ Se você ajustar esta opção como um caminho de um arquivo em seu sistema, o Git vai usar esse arquivo como o padrão de mensagem quando você fizer um commit. Por exemplo, suponha que você crie um arquivo de modelo em `$HOME/.gitmessage.txt` que se parece com este:
39
+
40
+ subject line
41
+
42
+ what happened
43
+
44
+ [ticket: X]
45
+
46
+ Para dizer ao Git para usá-lo como a mensagem padrão que aparece em seu editor quando você executar o `git commit`, defina o valor de configuração `commit.template`:
47
+
48
+ $ git config --global commit.template $HOME/.gitmessage.txt
49
+ $ git commit
50
+
51
+ Então, o editor irá abrir com algo parecido com isto quando você fizer um commit:
52
+
53
+ subject line
54
+
55
+ what happened
56
+
57
+ [ticket: X]
58
+ # Please enter the commit message for your changes. Lines starting
59
+ # with '#' will be ignored, and an empty message aborts the commit.
60
+ # On branch master
61
+ # Changes to be committed:
62
+ # (use "git reset HEAD <file>..." to unstage)
63
+ #
64
+ # modified: lib/test.rb
65
+ #
66
+ ~
67
+ ~
68
+ ".git/COMMIT_EDITMSG" 14L, 297C
69
+
70
+ Se você tiver uma política de mensagens de commit, colocando um modelo para essa política em seu sistema e configurando o Git para usá-lo por padrão pode ajudar a aumentar a chance de que a política seja seguida regularmente.
71
+
72
+ #### core.pager ####
73
+
74
+ A configuração core.pager determina qual pager é usado quando a saída do Git possui várias páginas, como quando são usados os comandos `log` e `diff`. Você pode configurá-lo para `more` ou para o seu pager favorito (por padrão, é `less`), ou você pode desativá-lo, definindo uma string em branco:
75
+
76
+ $ git config --global core.pager ''
77
+
78
+ Se você executar isso, Git irá paginar toda a saída de todos os comandos, não importando quão longo eles sejam.
79
+
80
+ #### user.signingkey ####
81
+
82
+ Se você estiver fazendo annotated tags assinadas (como discutido no Capítulo 2), definir a sua chave de assinatura GPG como uma configuração torna as coisas mais fáceis. Defina o ID da chave assim:
83
+
84
+ $ git config --global user.signingkey <gpg-key-id>
85
+
86
+ Agora, você pode assinar tags sem ter de especificar a sua chave toda hora com o comando `git tag`:
87
+
88
+ $ git tag -s <tag-name>
89
+
90
+ #### core.excludesfile ####
91
+
92
+ Você pode colocar padrões em seu arquivo de projeto `.gitignore` para que o Git veja-os como arquivos untracked ou tentar coloca-los como stagged quando executar o `git add` sobre eles, como discutido no Capítulo 2. No entanto, se você quiser que outro arquivo fora do seu projeto mantenha esses valores ou tenham valores extras, você pode dizer ao Git onde o arquivo com a opção `core.excludesfile` está. Basta configurá-lo para o caminho de um arquivo que tem conteúdo semelhante ao que um arquivo `.gitignore` teria.
93
+
94
+ #### help.autocorrect ####
95
+
96
+ Esta opção está disponível apenas no Git 1.6.1 e posteriores. Se você digitar um comando no Git 1.6, ele mostrará algo como isto:
97
+
98
+ $ git com
99
+ git: 'com' is not a git-command. See 'git --help'.
100
+
101
+ Did you mean this?
102
+ commit
103
+
104
+ Se você definir `help.autocorrect` para 1, Git automaticamente executará o comando se houver apenas uma possibilidade neste cenário.
105
+
106
+ ### Cores no Git ###
107
+
108
+ Git pode colorir a sua saída para o terminal, o que pode ajudá-lo visualmente a analisar a saída mais rápido e facilmente. Um número de opções pode ajudar a definir a colorização de sua preferência.
109
+
110
+ #### color.ui ####
111
+
112
+ Git automaticamente coloriza a maioria de sua saída, se você pedir para ele. Você pode ser muito específico sobre o que você quer e como colorir; mas para ativar a coloração padrão do terminal, defina `color.ui` para true:
113
+
114
+ $ git config --global color.ui true
115
+
116
+ Quando esse valor é definido, Git coloriza a saída do terminal. Outras configurações possíveis são false, que nunca coloriza a saída, e always, que coloriza sempre, mesmo que você esteja redirecionando comandos do Git para um arquivo ou através de um pipe para outro comando. Esta configuração foi adicionado na versão 1.5.5 do Git, se você tem uma versão mais antiga, você terá que especificar todas as configurações de cores individualmente.
117
+
118
+ Você dificilmente vai querer usar `color.ui = always`. Na maioria dos cenários, se você quiser códigos coloridos em sua saída redirecionada, você pode passar a opção `--color` para forçar o comando Git a usar códigos de cores. O `color.ui = true` é o que provavelmente você vai querer usar.
119
+
120
+ #### `color.*` ####
121
+
122
+ Se você quiser ser mais específico sobre quais e como os comandos são colorizados, ou se você tem uma versão mais antiga do Git, o Git oferece configurações específicas para colorir. Cada uma destas pode ser ajustada para `true`, `false`, ou `always`:
123
+
124
+ color.branch
125
+ color.diff
126
+ color.interactive
127
+ color.status
128
+
129
+ Além disso, cada uma delas tem sub-opções que você pode usar para definir cores específicas para partes da saída, se você quiser substituir cada cor. Por exemplo, para definir a informação meta na sua saída do diff para texto azul, fundo preto e texto em negrito, você pode executar
130
+
131
+ $ git config --global color.diff.meta “blue black bold”
132
+
133
+ Você pode definir a cor para qualquer um dos seguintes valores: normal, black, red, green, yellow, blue, magenta, cyan, ou white. Se você quiser um atributo como negrito no exemplo anterior, você pode escolher entre bold, dim, ul, blink, e reverse.
134
+
135
+ Veja a página de manual (manpage) do `git config` para saber todas as sub-opções que você pode configurar.
136
+
137
+ ### Ferramenta Externa de Merge e Diff ###
138
+
139
+ Embora o Git tenha uma implementação interna do diff, que é o que você estava usando, você pode configurar uma ferramenta externa. Você pode configurar uma ferramenta gráfica de merge para resolução de conflitos, em vez de ter de resolver conflitos manualmente. Vou demonstrar a configuração do Perforce Visual Merge Tool (P4Merge) para fazer suas diffs e fazer merge de resoluções, porque é uma boa ferramenta gráfica e é gratuita.
140
+
141
+ Se você quiser experimentar, P4Merge funciona em todas as principais plataformas, então você deve ser capaz de usá-lo. Vou usar nomes de caminho nos exemplos que funcionam em sistemas Mac e Linux; para Windows, você vai ter que mudar `/usr/local/bin` para um caminho executável em seu ambiente.
142
+
143
+ Você pode baixar P4Merge aqui:
144
+
145
+ http://www.perforce.com/product/components/perforce-visual-merge-and-diff-tools
146
+
147
+ Para começar, você vai configurar um script para executar seus comandos. Vou usar o caminho para o executável Mac; em outros sistemas, este será onde o seu binário do `p4merge` está instalado. Configure um script chamado `extMerge` que chama seu binário com todos os argumentos necessários:
148
+
149
+ $ cat /usr/local/bin/extMerge
150
+ #!/bin/sh/Applications/p4merge.app/Contents/MacOS/p4merge $*
151
+
152
+ Um wrapper diff verifica se sete argumentos são fornecidos e passa dois deles para o seu script de merge. Por padrão, o Git passa os seguintes argumentos para o programa diff:
153
+
154
+ path old-file old-hex old-mode new-file new-hex new-mode
155
+
156
+ Já que você só quer os argumentos `old-file` e `new-file`, você pode usar o script para passar o que você precisa.
157
+
158
+ $ cat /usr/local/bin/extDiff
159
+ #!/bin/sh
160
+ [ $# -eq 7 ] && /usr/local/bin/extMerge "$2" "$5"
161
+
162
+ Você também precisa ter certeza de que essas ferramentas são executáveis:
163
+
164
+ $ sudo chmod +x /usr/local/bin/extMerge
165
+ $ sudo chmod +x /usr/local/bin/extDiff
166
+
167
+ Agora você pode configurar o arquivo de configuração para usar a sua ferramenta de diff customizada. Existem algumas configurações personalizadas: `merge.tool` para dizer ao Git qual a estratégia a utilizar, `mergetool.*.cmd` para especificar como executar o comando, `mergetool.trustExitCode` para dizer ao Git se o código de saída do programa indica uma resolução de merge com sucesso ou não, e `diff.external` para dizer ao Git o comando a ser executado para diffs. Assim, você pode executar quatro comandos de configuração
168
+
169
+ $ git config --global merge.tool extMerge
170
+ $ git config --global mergetool.extMerge.cmd \
171
+ 'extMerge "$BASE" "$LOCAL" "$REMOTE" "$MERGED"'
172
+ $ git config --global mergetool.trustExitCode false
173
+ $ git config --global diff.external extDiff
174
+
175
+ ou você pode editar o seu arquivo `~/.gitconfig` para adicionar estas linhas.:
176
+
177
+ [merge]
178
+ tool = extMerge
179
+ [mergetool "extMerge"]
180
+ cmd = extMerge \"$BASE\" \"$LOCAL\" \"$REMOTE\" \"$MERGED\"
181
+ trustExitCode = false
182
+ [diff]
183
+ external = extDiff
184
+
185
+ Depois que tudo isso seja definido, se você executar comandos diff como este:
186
+
187
+ $ git diff 32d1776b1^ 32d1776b1
188
+
189
+ Em vez de ter a saída do diff na linha de comando, Git inicia o P4Merge, como mostra a Figura 7-1.
190
+
191
+ Insert 18333fig0701.png
192
+ Figura 7-1. P4Merge
193
+
194
+ Se você tentar mesclar dois branches e, posteriormente, ter conflitos de mesclagem, você pode executar o comando `git mergetool`, que iniciará o P4Merge para deixá-lo resolver os conflitos através dessa ferramenta gráfica.
195
+
196
+ A coisa boa sobre esta configuração é que você pode mudar o seu diff e ferramentas de merge facilmente. Por exemplo, para mudar suas ferramentas `extdiff` e `extMerge` para executar a ferramenta KDiff3 no lugar delas, tudo que você tem a fazer é editar seu arquivo `extMerge`:
197
+
198
+ $ cat /usr/local/bin/extMerge
199
+ #!/bin/sh/Applications/kdiff3.app/Contents/MacOS/kdiff3 $*
200
+
201
+ Agora, o Git irá utilizar a ferramenta KDiff3 para visualizar diffs e resolução de conflitos de merge.
202
+
203
+ O Git vem pré-configurado para usar uma série de outras ferramentas de resolução de merge sem ter que definir a configuração cmd. Você pode definir a sua ferramenta de mesclagem para kdiff3, opendiff, tkdiff, meld, xxdiff, emerge, vimdiff, ou gvimdiff. Se você não estiver interessado em usar o KDiff3 para diff mas quer usá-lo apenas para a resolução de merges, e o comando kdiff3 está no seu path, então você pode executar
204
+
205
+ $ git config --global merge.tool kdiff3
206
+
207
+ Se você executar isto ao invés de configurar os arquivos `extMerge` e `extDiff`, Git irá usar o KDiff3 para resolução de merges e a ferramenta diff padrão do Git.
208
+
209
+ ### Formatação e Espaços em Branco ###
210
+
211
+ Formatação e problemas de espaço em branco são alguns dos problemas mais frustrantes e sutis que muitos desenvolvedores encontram ao colaborar, especialmente em ambientes multi-plataforma. É muito fácil que patches ou outros trabalhos de colabores introduzam mudanças sutis como espaços em branco porque os editores os inserem silenciosamente ou programadores Windows adicionam quebras de linha em projetos multi-plataforma. Git tem algumas opções de configuração para ajudar com estas questões.
212
+
213
+ #### core.autocrlf ####
214
+
215
+ Se você está programando no Windows ou outro sistema, mas trabalha com pessoas que estão programando em Windows, você provavelmente vai encontrar problemas de quebra de linha em algum momento. Isso porque o Windows usa tanto o caráter carriage-return e um carácter linefeed para novas linhas em seus arquivos, enquanto os sistemas Mac e Linux usam apenas o carácter linefeed. Este é um fato sutil, mas extremamente irritante em trabalhos multi-plataforma.
216
+
217
+ O Git pode lidar com isso auto-convertendo finais de linha CRLF para LF quando você faz um commit, e vice-versa, quando se faz um checkout de código em seu sistema de arquivos. Você pode ativar esta funcionalidade com a configuração `core.autocrlf`. Se você estiver em uma máquina Windows, defina-o `true` — este converte terminações LF em CRLF quando você faz um checkout do código:
218
+
219
+ $ git config --global core.autocrlf true
220
+
221
+ Se você estiver em um sistema Linux ou Mac que usam os finais de linha LF, então você não irá querer que o Git automaticamente converta-os quando você fizer o check-out dos arquivos, no entanto, se um arquivo com terminações CRLF acidentalmente for introduzido, então você pode querer que o Git corrija-o. Você pode dizer ao Git para converter CRLF para LF no commit, mas não o contrário definindo `core.autocrlf` para entrada:
222
+
223
+ $ git config --global core.autocrlf input
224
+
225
+ Esta configuração deve deixá-lo com terminações CRLF em checkouts Windows, mas terminações LF em sistemas Mac e Linux e no repositório.
226
+
227
+ Se você é um programador Windows fazendo um projeto somente para Windows, então você pode desativar essa funcionalidade, registrando os CRLF no repositório, definindo o valor de configuração para `false`:
228
+
229
+ $ git config --global core.autocrlf false
230
+
231
+ #### core.whitespace ####
232
+
233
+ Git vem pré-configurado para detectar e corrigir alguns problemas de espaço em branco. Ele pode olhar por quatro problemas principais relacionados a espaços em branco — duas são ativadas por padrão e podem ser desativadas, e duas não são ativadas por padrão, mas podem ser ativadas.
234
+
235
+ As duas que são ativadas por padrão são `trailing-space`, que procura por espaços no final de uma linha, e `space-before-tab`, que procura por espaços antes de tabulações no início de uma linha.
236
+
237
+ As duas que estão desativadas por padrão, mas podem ser ativadas são `indent-with-non-tab`, que procura por linhas que começam com oito ou mais espaços em vez de tabulações, e `cr-at-eol`, que diz ao Git que carriage returns no final das linhas estão OK.
238
+
239
+ Você pode dizer ao Git quais destes você quer habilitado alterando a opção `core.whitespace` para os valores que deseja on ou off, separados por vírgulas. Você pode desabilitar as configurações, quer deixando-as fora da string de definição ou adicionando um `-` na frente do valor. Por exemplo, se você quiser tudo, menos `cr-at-eol`, você pode fazer isso:
240
+
241
+ $ git config --global core.whitespace \
242
+ trailing-space,space-before-tab,indent-with-non-tab
243
+
244
+ Git irá detectar esses problemas quando você executar um comando `git diff` e tentar colori-los de modo que você pode, eventualmente, corrigi-los antes de fazer o commit. Ele também irá usar esses valores para ajudar quando você aplicar patches com `git apply`. Quando você estiver aplicando patches, você pode pedir ao Git para avisá-lo se estiver aplicando patches com problemas de espaço em branco:
245
+
246
+ $ git apply --whitespace=warn <patch>
247
+
248
+ Ou você pode deixar o Git tentar corrigir automaticamente o problema antes de aplicar o patch:
249
+
250
+ $ git apply --whitespace=fix <patch>
251
+
252
+ Essas opções se aplicam ao comando git rebase também. Se você commitou problemas de espaço em branco, mas ainda não fez um push, você pode executar um `rebase` com a opção `--whitespace=fix` para que o Git automaticamente corrija problemas de espaço em branco, como faz com os patches.
253
+
254
+ ### Configuração do Servidor ###
255
+
256
+ Não existem muitas opções de configuração disponíveis para o lado servidor do Git, mas há algumas interessantes que você pode querer aprender.
257
+
258
+ #### receive.fsckObjects ####
259
+
260
+ Por padrão, o Git não verifica a consistência de todos os objetos que ele recebe durante um push. Embora o Git possa certificar-se de que cada objeto ainda corresponde ao seu SHA-1 checksum e aponta para objetos válidos, ele não faz isso por padrão em cada push. Esta é uma operação relativamente custosa e pode adicionar uma grande quantidade de tempo para cada push, de acordo com o tamanho do repositório ou do push. Se você quiser que o Git verifique a consistência dos objetos em cada push, você pode forçá-lo a fazê-lo definindo `receive.fsckObjects` como true:
261
+
262
+ $ git config --system receive.fsckObjects true
263
+
264
+ Agora, o Git irá verificar a integridade do seu repositório antes que cada push seja aceito para garantir que clientes defeituosos não estejam introduzindo dados corrompidos.
265
+
266
+ #### receive.denyNonFastForwards ####
267
+
268
+ Se você fizer o rebase de commits já enviados com push e então tentar fazer outro push, ou tentar fazer um push de um commit para um branch remoto que não contenha o commit que o branch remoto atualmente aponta, sua ação será negada. Isso geralmente é uma boa política; mas, no caso do rebase, você pode determinar que você saiba o que está fazendo e pode forçar a atualização do branch remoto com um `-f` no seu comando push.
269
+
270
+ Para desativar a capacidade de forçar updates em branches remotos para referências não fast-forward, defina `receive.denyNonFastForwards`:
271
+
272
+ $ git config --system receive.denyNonFastForwards true
273
+
274
+ A outra forma de fazer isso é através dos hooks em lado servidor, que eu vou falar daqui a pouco. Essa abordagem permite que você faça coisas mais complexas como negar não fast-forwards para um determinado conjunto de usuários.
275
+
276
+ #### receive.denyDeletes ####
277
+
278
+ Uma das soluções para a política `denyNonFastForwards` é o usuário excluir o branch e depois fazer um push de volta com a nova referência. Nas versões mais recentes do Git (a partir da versão 1.6.1), você pode definir `receive.denyDeletes` como true:
279
+
280
+ $ git config --system receive.denyDeletes true
281
+
282
+ Isto nega exclusão de branchs e tags em um push — nenhum usuário pode fazê-lo. Para remover branches remotas, você deve remover os arquivos ref do servidor manualmente. Existem também formas mais interessantes de fazer isso de acordo com o usuário através de ACLs, como você vai aprender no final deste capítulo.
283
+
284
+ ## Atributos Git ##
285
+
286
+ Algumas dessas configurações também podem ser especificadas para um path, de modo que o Git aplique essas configurações só para um subdiretório ou conjunto de arquivos. Essas configurações de path específicas são chamadas atributos Git e são definidas em um arquivo `.gitattributes` ou em um de seus diretórios (normalmente a raiz de seu projeto) ou no arquivo `.git/info/attributes` se você não desejar que o arquivo de atributos seja commitado com o seu projeto.
287
+
288
+ Usando atributos, você pode fazer coisas como especificar estratégias de merge separadas para arquivos individuais ou pastas no seu projeto, dizer ao Git como fazer diff de arquivos não textuais, ou mandar o Git filtrar conteúdos antes de fazer o checkout para dentro ou fora do Git. Nesta seção, você vai aprender sobre alguns dos atributos que podem ser configurados em seus paths de seu projeto Git e ver alguns exemplos de como usar esse recurso na prática.
289
+
290
+ ### Arquivos Binários ###
291
+
292
+ Um truque legal para o qual você pode usar atributos Git é dizendo ao Git quais arquivos são binários (em casos que de outra forma ele não pode ser capaz de descobrir) e dando ao Git instruções especiais sobre como lidar com esses arquivos. Por exemplo, alguns arquivos de texto podem ser gerados por máquina e não é possível usar diff neles, enquanto que em alguns arquivos binários pode ser usado o diff — você verá como dizer ao Git qual é qual.
293
+
294
+ #### Identificando Arquivos Binários ####
295
+
296
+ Alguns arquivos parecem com arquivos de texto, mas para todos os efeitos devem ser tratados como dados binários. Por exemplo, projetos Xcode no Mac contém um arquivo que termina em `.pbxproj`, que é basicamente um conjunto de dados de JSON (formato de dados em texto simples JavaScript), escrito no disco pela IDE que registra as configurações de buils e assim por diante. Embora seja tecnicamente um arquivo de texto, porque é tudo ASCII, você não quer tratá-lo como tal, porque ele é na verdade um banco de dados leve — você não pode fazer um merge do conteúdo, se duas pessoas o mudaram, e diffs geralmente não são úteis. O arquivo é para ser lido pelo computador. Em essência, você quer tratá-lo como um arquivo binário.
297
+
298
+ Para dizer ao Git para tratar todos os arquivos `pbxproj` como dados binários, adicione a seguinte linha ao seu arquivo `.gitattributes`:
299
+
300
+ *.pbxproj -crlf -diff
301
+
302
+ Agora, o Git não vai tentar converter ou corrigir problemas CRLF; nem vai tentar calcular ou imprimir um diff para mudanças nesse arquivo quando você executar show ou git diff em seu projeto. Na série 1.6 do Git, você também pode usar uma macro que significa `-crlf -diff`:
303
+
304
+ *.pbxproj binary
305
+
306
+ #### Diff de Arquivos Binários ####
307
+
308
+ Na série 1.6 do Git, você pode usar a funcionalidade de atributos do Git para fazer diff de arquivos binários. Você faz isso dizendo ao Git como converter os dados binários em um formato de texto que pode ser comparado através do diff normal.
309
+
310
+ ##### Arquivos do MS Word #####
311
+
312
+ Como este é um recurso muito legal e não muito conhecido, eu vou mostrar alguns exemplos. Primeiro, você vai usar esta técnica para resolver um dos problemas mais irritantes conhecidos pela humanidade: controlar a versão de documentos Word. Todo mundo sabe que o Word é o editor mais horrível que existe, mas, estranhamente, todo mundo o usa. Se você quiser controlar a versão de documentos do Word, você pode colocá-los em um repositório Git e fazer um commit de vez em quando; mas o que de bom tem isso? Se você executar `git diff` normalmente, você só verá algo como isto:
313
+
314
+ $ git diff
315
+ diff --git a/chapter1.doc b/chapter1.doc
316
+ index 88839c4..4afcb7c 100644
317
+ Binary files a/chapter1.doc and b/chapter1.doc differ
318
+
319
+ Você não pode comparar diretamente duas versões, a menos que você verifique-as manualmente, certo? Acontece que você pode fazer isso muito bem usando atributos Git. Coloque a seguinte linha no seu arquivo `.gitattributes`:
320
+
321
+ *.doc diff=word
322
+
323
+ Isto diz ao Git que qualquer arquivo que corresponde a esse padrão (.doc) deve usar o filtro "word" quando você tentar ver um diff que contém alterações. O que é o filtro "word"? Você tem que configurá-lo. Aqui você vai configurar o Git para usar o programa `strings` para converter documentos do Word em arquivos de texto legível, o que poderá ser visto corretamente no diff:
324
+
325
+ $ git config diff.word.textconv strings
326
+
327
+ Este comando adiciona uma seção no seu `.git/config` que se parece com isto:
328
+ [diff "word"]
329
+ textconv = strings
330
+
331
+ Nota: Há diferentes tipos de arquivos `.doc`, alguns usam uma codificação UTF-16 ou outras "páginas de código" e `strings` não vão encontrar nada de útil lá. Seu resultado pode variar.
332
+
333
+ Agora o Git sabe que se tentar fazer uma comparação entre os dois snapshots, e qualquer um dos arquivos terminam em `.doc`, ele deve executar esses arquivos através do filtro "word", que é definido como o programa `strings`. Isso cria versões em texto de arquivos do Word antes de tentar o diff.
334
+
335
+ Aqui está um exemplo. Eu coloquei um capítulo deste livro em Git, acrescentei algum texto a um parágrafo, e salvei o documento. Então, eu executei `git diff` para ver o que mudou:
336
+
337
+ $ git diff
338
+ diff --git a/chapter1.doc b/chapter1.doc
339
+ index c1c8a0a..b93c9e4 100644
340
+ --- a/chapter1.doc
341
+ +++ b/chapter1.doc
342
+ @@ -8,7 +8,8 @@ re going to cover Version Control Systems (VCS) and Git basics
343
+ re going to cover how to get it and set it up for the first time if you don
344
+ t already have it on your system.
345
+ In Chapter Two we will go over basic Git usage - how to use Git for the 80%
346
+ -s going on, modify stuff and contribute changes. If the book spontaneously
347
+ +s going on, modify stuff and contribute changes. If the book spontaneously
348
+ +Let's see if this works.
349
+
350
+ Git com sucesso e de forma sucinta me diz que eu adicionei a string "Let’s see if this works", o que é correto. Não é perfeito — ele acrescenta um monte de coisas aleatórias no final — mas certamente funciona. Se você pode encontrar ou escrever um conversor de Word em texto simples que funciona bem o suficiente, esta solução provavelmente será incrivelmente eficaz. No entanto, `strings` está disponível na maioria dos sistemas Mac e Linux, por isso pode ser uma primeira boa tentativa para fazer isso com muitos formatos binários.
351
+
352
+ ##### Documentos de Texto OpenDocument #####
353
+
354
+ A mesma abordagem que usamos para arquivos do MS Word (`*.doc`) pode ser usada para arquivos de texto OpenDocument (`*.odt`) criados pelo OpenOffice.org.
355
+
356
+ Adicione a seguinte linha ao seu arquivo `.gitattributes`:
357
+
358
+ *.odt diff=odt
359
+
360
+ Agora configure o filtro diff `odt` em `.git/config`:
361
+
362
+ [diff "odt"]
363
+ binary = true
364
+ textconv = /usr/local/bin/odt-to-txt
365
+
366
+ Arquivos OpenDocument são na verdade diretórios zipados contendo vários arquivos (o conteúdo em um formato XML, folhas de estilo, imagens, etc.) Vamos precisar escrever um script para extrair o conteúdo e devolvê-lo como texto simples. Crie o arquivo `/usr/local/bin/odt-to-txt` (você é pode colocá-lo em um diretório diferente) com o seguinte conteúdo:
367
+
368
+ #! /usr/bin/env perl
369
+ # Simplistic OpenDocument Text (.odt) to plain text converter.
370
+ # Author: Philipp Kempgen
371
+
372
+ if (! defined($ARGV[0])) {
373
+ print STDERR "No filename given!\n";
374
+ print STDERR "Usage: $0 filename\n";
375
+ exit 1;
376
+ }
377
+
378
+ my $content = '';
379
+ open my $fh, '-|', 'unzip', '-qq', '-p', $ARGV[0], 'content.xml' or die $!;
380
+ {
381
+ local $/ = undef; # slurp mode
382
+ $content = <$fh>;
383
+ }
384
+ close $fh;
385
+ $_ = $content;
386
+ s/<text:span\b[^>]*>//g; # remove spans
387
+ s/<text:h\b[^>]*>/\n\n***** /g; # headers
388
+ s/<text:list-item\b[^>]*>\s*<text:p\b[^>]*>/\n -- /g; # list items
389
+ s/<text:list\b[^>]*>/\n\n/g; # lists
390
+ s/<text:p\b[^>]*>/\n /g; # paragraphs
391
+ s/<[^>]+>//g; # remove all XML tags
392
+ s/\n{2,}/\n\n/g; # remove multiple blank lines
393
+ s/\A\n+//; # remove leading blank lines
394
+ print "\n", $_, "\n\n";
395
+
396
+ E torne-o executável
397
+
398
+ chmod +x /usr/local/bin/odt-to-txt
399
+
400
+ Agora `git diff` será capaz de dizer o que mudou em arquivos `.odt`.
401
+
402
+ Outro problema interessante que você pode resolver desta forma envolve o diff de arquivos de imagem. Uma maneira de fazer isso é passar arquivos PNG através de um filtro que extrai suas informações EXIF — metadados que são gravados com a maioria dos formatos de imagem. Se você baixar e instalar o programa `exiftool`, você pode usá-lo para converter suas imagens em texto sobre os metadados, assim pelo menos o diff vai mostrar uma representação textual de todas as mudanças que aconteceram:
403
+
404
+ $ echo '*.png diff=exif' >> .gitattributes
405
+ $ git config diff.exif.textconv exiftool
406
+
407
+ Se você substituir uma imagem em seu projeto e executar o `git diff`, você verá algo como isto:
408
+
409
+ diff --git a/image.png b/image.png
410
+ index 88839c4..4afcb7c 100644
411
+ --- a/image.png
412
+ +++ b/image.png
413
+ @@ -1,12 +1,12 @@
414
+ ExifTool Version Number : 7.74
415
+ -File Size : 70 kB
416
+ -File Modification Date/Time : 2009:04:21 07:02:45-07:00
417
+ +File Size : 94 kB
418
+ +File Modification Date/Time : 2009:04:21 07:02:43-07:00
419
+ File Type : PNG
420
+ MIME Type : image/png
421
+ -Image Width : 1058
422
+ -Image Height : 889
423
+ +Image Width : 1056
424
+ +Image Height : 827
425
+ Bit Depth : 8
426
+ Color Type : RGB with Alpha
427
+
428
+ Você pode facilmente ver que o tamanho do arquivo e as dimensões da imagem sofreram alterações.
429
+
430
+ ### Expansão de Palavra-chave ###
431
+
432
+ Expansão de Palavra-chave no estilo SVN ou CVS são frequentemente solicitados pelos desenvolvedores acostumados com estes sistemas. O principal problema disso no Git é que você não pode modificar um arquivo com informações sobre o commit depois que você já fez o commit, porque o Git cria os checksums dos arquivos primeiro. No entanto, você pode injetar texto em um arquivo quando é feito o checkout dele e removê-lo novamente antes de ser adicionado a um commit. Atributos Git oferecem duas maneiras de fazer isso.
433
+
434
+ Primeiro, você pode injetar o SHA-1 checksum de um blob em um campo `$Id$` no arquivo automaticamente. Se você definir esse atributo em um arquivo ou conjunto de arquivos, então da próxima vez que você fizer o checkout do branch, o Git irá substituir o campo com o SHA-1 do blob. É importante notar que não é o SHA do commit, mas do blob em si:
435
+
436
+ $ echo '*.txt ident' >> .gitattributes
437
+ $ echo '$Id$' > test.txt
438
+
439
+ Da próxima vez que você fizer o checkout desse arquivo, o Git injetará o SHA do blob:
440
+
441
+ $ rm test.txt
442
+ $ git checkout -- test.txt
443
+ $ cat test.txt
444
+ $Id: 42812b7653c7b88933f8a9d6cad0ca16714b9bb3 $
445
+
446
+ No entanto, este resultado é de uso limitado. Se você já usou a substituição de palavras em CVS ou Subversion, você pode incluir uma datestamp — o SHA não é lá muito útil, porque é bastante aleatório e você não pode dizer se um SHA é mais velho ou mais novo que o outro.
447
+
448
+ Acontece que você pode escrever seus próprios filtros para fazer substituições em arquivos no commit/checkout. Estes são os filtros "clean" e "smudge". No arquivo `.gitattributes`, você pode definir um filtro para determinados paths e configurar os scripts que irão processar os arquivos antes que seja feito um checkout ("smudge", ver Figura 7-2) e pouco antes do commit ("clean", veja a Figura 7-3). Estes filtros podem ser configurados para fazer todo tipo de coisas divertidas.
449
+
450
+ Insert 18333fig0702.png
451
+ Figura 7-2. O filtro “smudge” é rodado no checkout.
452
+
453
+ Insert 18333fig0703.png
454
+ Figura 7-3. O filtro “clean” é rodado quando arquivos passam para o estado staged.
455
+
456
+ A mensagem original do commit para esta funcionalidade dá um exemplo simples de como passar todo o seu código fonte C através do programa `indent` antes de fazer o commit. Você pode configurá-lo, definindo o atributo de filtro no arquivo `.gitattributes` para filtrar arquivos `*.c` com o filtro "indent":
457
+
458
+ *.c filter=indent
459
+
460
+ Então, diga ao Git o que o filtro "indent" faz em smudge e clean:
461
+
462
+ $ git config --global filter.indent.clean indent
463
+ $ git config --global filter.indent.smudge cat
464
+
465
+ Neste caso, quando você commitar os arquivos que correspondem a `*.c`, Git irá passá-los através do programa indent antes de commmitá-los e depois passá-los através do programa `cat` antes de fazer o checkout de volta para o disco. O programa `cat` é basicamente um no-op: ele mostra os mesmos dados que ele recebe. Esta combinação efetivamente filtra todos os arquivos de código fonte C através do `indent` antes de fazer o commit.
466
+
467
+ Outro exemplo interessante é a expansão da palavra-chave `$Date$`, estilo RCS. Para fazer isso corretamente, você precisa de um pequeno script que recebe um nome de arquivo, descobre a última data de commit deste projeto, e insere a data no arquivo. Aqui há um pequeno script Ruby que faz isso:
468
+
469
+ #! /usr/bin/env ruby
470
+ data = STDIN.read
471
+ last_date = `git log --pretty=format:"%ad" -1`
472
+ puts data.gsub('$Date$', '$Date: ' + last_date.to_s + '$')
473
+
474
+ Tudo o que o script faz é obter a última data de commit do comando `git log`, coloca ele em qualquer string `$Date$` que vê no stdin, e imprime os resultados — deve ser simples de fazer em qualquer linguagem que você esteja confortável. Você pode nomear este arquivo `expand_date` e colocá-lo em seu path. Agora, você precisa configurar um filtro no Git (chamaremos de `dater`) e diremos para usar o seu filtro `expand_date` para o smudge dos arquivos no checkout. Você vai usar uma expressão Perl para o clean no commit:
475
+
476
+ $ git config filter.dater.smudge expand_date
477
+ $ git config filter.dater.clean 'perl -pe "s/\\\$Date[^\\\$]*\\\$/\\\$Date\\\$/"'
478
+
479
+ Este trecho Perl retira qualquer coisa que vê em uma string `$Date$`, para voltar para onde você começou. Agora que o seu filtro está pronto, você pode testá-lo através da criação de um arquivo com a sua palavra-chave `$Date$` e então criar um atributo Git para esse arquivo que envolve o novo filtro:
480
+
481
+ $ echo '# $Date$' > date_test.txt
482
+ $ echo 'date*.txt filter=dater' >> .gitattributes
483
+
484
+ Se você fizer o commit dessas alterações e fizer o checkout do arquivo novamente, você verá a palavra-chave corretamente substituída:
485
+
486
+ $ git add date_test.txt .gitattributes
487
+ $ git commit -m "Testing date expansion in Git"
488
+ $ rm date_test.txt
489
+ $ git checkout date_test.txt
490
+ $ cat date_test.txt
491
+ # $Date: Tue Apr 21 07:26:52 2009 -0700$
492
+
493
+ Você pode ver o quão poderosa esta técnica pode ser para aplicações customizadas. Você tem que ter cuidado, porém, porque o arquivo `.gitattributes` está sendo commitado e mantido no projeto, mas o `dater` não é; assim, ele não vai funcionar em todos os lugares. Ao projetar esses filtros, eles devem ser capazes de falhar e ainda assim manter o projeto funcionando corretamente.
494
+
495
+ ### Exportando Seu Repositório ###
496
+
497
+ Dados de atributo Git também permitem que você faça algumas coisas interessantes ao exportar um arquivo do seu projeto.
498
+
499
+ #### export-ignore ####
500
+
501
+ Você pode dizer para o Git não exportar determinados arquivos ou diretórios ao gerar um arquivo. Se existe uma subpasta ou arquivo que você não deseja incluir em seu arquivo, mas que você quer dentro de seu projeto, você pode determinar estes arquivos através do atributo `export-ignore`.
502
+
503
+ Por exemplo, digamos que você tenha alguns arquivos de teste em um subdiretório `test/`, e não faz sentido incluí-los na exportação do tarball do seu projeto. Você pode adicionar a seguinte linha ao seu arquivo de atributos Git:
504
+
505
+ test/ export-ignore
506
+
507
+ Agora, quando você executar git archive para criar um arquivo tar do seu projeto, aquele diretório não será incluído no arquivo.
508
+
509
+ #### export-subst ####
510
+
511
+ Outra coisa que você pode fazer para seus arquivos é uma simples substituição de palavra. Git permite colocar a string `$Format:$` em qualquer arquivo com qualquer um dos códigos de formatação`--pretty=format`, muitos dos quais você viu no Capítulo 2. Por exemplo, se você quiser incluir um arquivo chamado `LAST_COMMIT` em seu projeto, e a última data de commit foi injetada automaticamente quando `git archive` foi executado, você pode configurar o arquivo como este:
512
+
513
+ $ echo 'Last commit date: $Format:%cd$' > LAST_COMMIT
514
+ $ echo "LAST_COMMIT export-subst" >> .gitattributes
515
+ $ git add LAST_COMMIT .gitattributes
516
+ $ git commit -am 'adding LAST_COMMIT file for archives'
517
+
518
+ Quando você executar `git archive`, o conteúdo do arquivo quando aberto será parecido com este:
519
+
520
+ $ cat LAST_COMMIT
521
+ Last commit date: $Format:Tue Apr 21 08:38:48 2009 -0700$
522
+
523
+ ### Estratégias de Merge ###
524
+
525
+ Você também pode usar atributos Git para dizer ao Git para utilizar estratégias diferentes para mesclar arquivos específicos em seu projeto. Uma opção muito útil é dizer ao Git para não tentar mesclar arquivos específicos quando eles têm conflitos, mas sim para usar o seu lado do merge ao invés do da outra pessoa.
526
+
527
+ Isso é útil se um branch em seu projeto divergiu ou é especializado, mas você quer ser capaz de fazer o merge de alterações de volta a partir dele, e você deseja ignorar determinados arquivos. Digamos que você tenha um arquivo de configurações de banco de dados chamado database.xml que é diferente em dois branches, e você deseja mesclar em seu outro branch sem bagunçar o arquivo de banco de dados. Você pode configurar um atributo como este:
528
+
529
+ database.xml merge=ours
530
+
531
+ Se você fizer o merge em outro branch, em vez de ter conflitos de merge com o arquivo database.xml, você verá algo como isto:
532
+
533
+ $ git merge topic
534
+ Auto-merging database.xml
535
+ Merge made by recursive.
536
+
537
+ Neste caso, database.xml fica em qualquer versão que você tinha originalmente.
538
+
539
+ ## Hooks do Git ##
540
+
541
+ Como muitos outros sistemas de controle de versão, Git tem uma maneira para disparar scripts personalizados quando certas ações importantes ocorrerem. Existem dois grupos desses hooks: lado cliente e lado servidor. Os hooks do lado cliente são para operações do cliente, tais como commit e merge. Os hooks do lado servidor são para operações de servidor, como recebimento de um push. Você pode usar estes hooks para todo tipo de coisa, e você vai aprender sobre alguns deles aqui.
542
+
543
+ ### Instalando um Hook ###
544
+
545
+ Os hooks são todos armazenados no subdiretório `hooks` do diretório Git. Na maioria dos projetos, é em `.git/hooks`. Por padrão, o Git preenche este diretório com um monte de scripts de exemplo, muitos dos quais são úteis por si só, mas eles também documentam os valores de entrada de cada script. Todos os exemplos são escritos como shell scripts, com um pouco de Perl, mas todos os scripts executáveis ​​devidamente nomeados irão funcionar bem — você pode escrevê-los em Ruby ou Python ou em que você quiser. Para as versões do Git superiores a 1.6, esses hooks de exemplo terminam com .sample; você precisa renomeá-los. Para versões anteriores a 1.6 do Git, os arquivos de exemplo são nomeados corretamente, mas não são executáveis.
546
+
547
+ Para ativar um script de hook, coloque um arquivo no subdiretório `hooks` do seu diretório Git que é nomeado de forma adequada e é executável. A partir desse ponto, ele deve ser chamado. Eu vou cobrir a maior parte dos nomes dos arquivos de hook importantes aqui.
548
+
549
+ ### Hooks do Lado Cliente ###
550
+
551
+ Há um monte de hooks do lado do cliente. Esta seção divide eles em committing-workflow hooks, e-mail-workflow scripts, e o resto dos scripts do lado cliente.
552
+
553
+ #### Committing-Workflow Hooks ####
554
+
555
+ Os primeiros quatro hooks têm a ver com o processo de commit. O hook `pre-commit` é executado primeiro, antes mesmo de digitar uma mensagem de confirmação. É usado para inspecionar o snapshot que está prestes a ser commitado, para ver se você se esqueceu de alguma coisa, para ter certeza que os testes rodem, ou para analisar o que você precisa inspecionar no código. Retornando um valor diferente de zero a partir deste hook aborta o commit, mas você pode ignorá-lo com `git commit --no-verify`. Você pode fazer coisas como checar o estilo do código (executar lint ou algo equivalente), verificar o espaço em branco (o hook padrão faz exatamente isso), ou verificar a documentação apropriada sobre novos métodos.
556
+
557
+ O hook `prepare-commit-msg` é executado antes que o editor de mensagem de commit seja iniciado, mas depois que a mensagem padrão seja criada. Ele permite que você edite a mensagem padrão antes que autor do commit a veja. Este hook tem algumas opções: o caminho para o arquivo que contém a mensagem de confirmação até agora, o tipo de commit, e o SHA-1 do commit se este é um commit amended. Este hook geralmente não é útil para o commit normal, mas sim, para commits onde a mensagem padrão é gerada automaticamente, tal como um template de mensagem de commit, commits de merge, squashed commits, e amended commits. Você pode usá-lo em conjunto com um modelo de commit para inserir informações programaticamente.
558
+
559
+ O hook `commit-msg` tem um parâmetro, que novamente, é o caminho para um arquivo temporário que contém a mensagem atual de commit. Se este script não retornar zero, Git aborta o processo de commit, de modo que você pode usá-lo para validar o seu estado de projeto ou mensagem de commit antes de permitir que um commit prossiga. Na última seção deste capítulo, vou demonstrar usando este hook como verificar se a sua mensagem de commit está em conformidade com um padrão desejado.
560
+
561
+ Depois que todo o processo de commit esteja concluído, o hook `post-commit` é executado. Ele não recebe nenhum parâmetro, mas você pode facilmente obter o último commit executando `git log -1 HEAD`. Geralmente, esse script é usado para notificação ou algo similar.
562
+
563
+ Os scripts committing-workflow do lado cliente podem ser usados ​​em praticamente qualquer fluxo de trabalho. Eles são muitas vezes utilizados para reforçar certas políticas, embora seja importante notar que estes scripts não são transferidos durante um clone. Você pode aplicar a política do lado servidor para rejeitar um push de um commit que não corresponda a alguma política, mas é inteiramente de responsabilidade do desenvolvedor usar esses scripts no lado cliente. Portanto, estes são scripts para ajudar os desenvolvedores, e eles devem ser criados e mantidos por eles, embora eles possam ser substituídos ou modificados por eles a qualquer momento.
564
+
565
+ #### E-mail Workflow Hooks ####
566
+
567
+ Você pode configurar três hooks do lado cliente para um fluxo de trabalho baseado em e-mail. Eles são todos invocados pelo comando `git am`, por isso, se você não está usando este comando em seu fluxo de trabalho, você pode pular para a próxima seção. Se você estiver recebendo patches por e-mail preparados por `git format-patch`, então alguns deles podem ser úteis para você.
568
+
569
+ O primeiro hook que é executado é `applypatch msg`. Ele recebe um único argumento: o nome do arquivo temporário que contém a mensagem de commit. Git aborta o patch se este script retornar valor diferente de zero. Você pode usar isso para se certificar de que uma mensagem de commit está formatada corretamente ou para normalizar a mensagem através do script.
570
+
571
+ O próximo hook a ser executado durante a aplicação de patches via `git am` é `pre-applypatch`. Ele não tem argumentos e é executado após a aplicação do patch, então, você pode usá-lo para inspecionar o snapshot antes de fazer o commit. Você pode executar testes ou inspecionar a árvore de trabalho com esse script. Se algo estiver faltando ou os testes não passarem, retornando um valor diferente de zero também aborta o script `git am` sem commmitar o patch.
572
+
573
+ O último hook a ser executado durante um `git am` é `post-applypatch`. Você pode usá-lo para notificar um grupo ou o autor do patch que você aplicou em relação ao que você fez. Você não pode parar o processo de patch com esse script.
574
+
575
+ #### Outros Hooks de Cliente ####
576
+
577
+ O hook `pre-rebase` é executado antes de um rebase e pode interromper o processo terminando com valor diferente de zero. Você pode usar esse hook para não permitir rebasing de commits que já foram atualizados com um push. O hook `pre-rebase` de exemplo que o Git instala faz isso, embora ele assuma que o próximo é o nome do branch que você publicar. É provável que você precise mudar isso para seu branch estável ou publicado.
578
+
579
+ Depois de executar um `git checkout` com sucesso, o hook `post-checkout` é executado, você pode usá-lo para configurar o diretório de trabalho adequadamente para o seu ambiente de projeto. Isso pode significar mover arquivos binários grandes que você não quer controlar a versão, documentação auto-gerada, ou algo parecido.
580
+
581
+ Finalmente, o hook `post-merge` roda depois de um `merge` executado com sucesso. Você pode usá-lo para restaurar dados na árvore de trabalho que o GIT não pode rastrear, como dados de permissões. Este hook pode igualmente validar a presença de arquivos externos ao controle do Git que você pode querer copiado quando a árvore de trabalho mudar.
582
+
583
+ ### Hooks do Lado Servidor ###
584
+
585
+ Além dos Hooks do lado do cliente, você pode usar alguns hooks importantes do lado servidor como administrador do sistema para aplicar quase qualquer tipo de política para o seu projeto. Esses scripts são executados antes e depois um push para o servidor. Os "pre hooks" podem retornar valor diferente de zero em qualquer momento para rejeitar um push, assim como imprimir uma mensagem de erro para o cliente, você pode configurar uma política de push tão complexa quanto você queira.
586
+
587
+ #### pre-receive e post-receive ####
588
+
589
+ O primeiro script a ser executado ao tratar um push de um cliente é o `pre-receive`. É preciso uma lista de referências que estão no push a partir do stdin; se ele não retornar zero, nenhum deles são aceitos. Você pode usar esse hook para fazer coisas como verificar se nenhuma das referências atualizadas não são fast-forwards; ou para verificar se o usuário que está fazendo o push tem acesso para criar, apagar, ou fazer push de atualizações para todos os arquivos que ele está modificando com o push.
590
+
591
+ O hook `post-receive` roda depois que todo o processo esteja concluído e pode ser usado para atualizar outros serviços ou notificar os usuários. Ele recebe os mesmos dados do stdin que o hook `pre-receive`. Exemplos incluem envio de e-mails, notificar um servidor de integração contínua, ou atualização de um sistema de ticket-tracking — você pode até analisar as mensagens de confirmação para ver se algum ticket precisa ser aberto, modificado ou fechado. Este script não pode parar o processo de push, mas o cliente não se disconecta até que tenha concluído; por isso, tenha cuidado quando você tentar fazer algo que possa levar muito tempo.
592
+
593
+ #### update ####
594
+
595
+ O script update é muito semelhante ao script `pre-receive`, exceto que ele é executado uma vez para cada branch que o usuário está tentando atualizar. Se o usuário está tentando fazer um push para vários branchs, `pre-receive` é executado apenas uma vez, enquanto que update é executado uma vez por branch do push. Em vez de ler do stdin, este script recebe três argumentos: o nome da referência (branch), o SHA-1, que apontava para a referência antes do push, e o SHA-1 do push que o usuário está tentando fazer. Se o script update retornar um valor diferente de zero, apenas a referência é rejeitada; outras referências ainda podem ser atualizadas.
596
+
597
+ ## Um exemplo de Política Git Forçada ##
598
+
599
+ Nesta seção, você vai usar o que aprendeu para estabelecer um fluxo de trabalho Git que verifica um formato de mensagem personalizado para commit, e força o uso apenas de push fast-forward, e permite que apenas alguns usuários possam modificar determinados subdiretórios em um projeto. Você vai construir scripts cliente que ajudam ao desenvolvedor saber se seu push será rejeitado e scripts de servidor que fazem valer as políticas.
600
+
601
+ Eu usei Ruby para escrever estes, isso porque é a minha linguagem de script preferida e porque eu sinto que é a linguagem de script que mais parece com pseudocódigo; assim você deve ser capaz de seguir o código, mesmo que você não use Ruby. No entanto, qualquer linguagem funcionará bem. Todos os exemplos de scripts de hooks distribuídos com o Git são feitos em Perl ou Bash, então você também pode ver vários exemplos de hooks nessas linguagens olhando os exemplos.
602
+
603
+ ### Hook do Lado Servidor ###
604
+
605
+ Todo o trabalho do lado servidor irá para o arquivo update no seu diretório de hooks. O arquivo update é executado uma vez por branch de cada push e leva a referência do push para a revisão antiga onde o branch estava, e a nova revisão do push. Você também terá acesso ao usuário que está realizando o push, se o push está sendo executado através de SSH. Se você permitiu que todos se conectem com um único usuário (como "git"), através de autenticação de chave pública, você pode ter que dar ao usuário um "shell wrapper" que determina qual usuário está se conectando com base na chave pública, e definir uma variável de ambiente especificando o usuário. Aqui eu assumo que o usuário de conexão está na variável de ambiente `$USER`, então, seu script de atualização começa reunindo todas as informações que você precisa:
606
+
607
+ #!/usr/bin/env ruby
608
+
609
+ $refname = ARGV[0]
610
+ $oldrev = ARGV[1]
611
+ $newrev = ARGV[2]
612
+ $user = ENV['USER']
613
+
614
+ puts "Enforcing Policies... \n(#{$refname}) (#{$oldrev[0,6]}) (#{$newrev[0,6]})"
615
+
616
+ Sim, eu estou usando variáveis ​​globais. Não me julgue — é mais fácil para demonstrar desta maneira.
617
+
618
+ #### Impondo um Formato Específico de Mensagens de Commit ####
619
+
620
+ Seu primeiro desafio é impor que cada mensagem de commit deve aderir a um formato específico. Só para se ter uma meta, vamos supor que cada mensagem tem de incluir uma string que parece com "ref: 1234" porque você quer que cada commit tenha um link para um item de trabalho no seu sistema de chamados. Você deve olhar para cada commit do push, ver se essa sequência está na mensagem de commit, e, se a string estiver ausente de qualquer um dos commits, retornar zero para que o push seja rejeitado.
621
+
622
+ Você pode obter uma lista dos valores SHA-1 de todos os commits de um push, através dos valores `$newrev` e `$oldrev` e passando-os para um comando Git plumbing chamado `git rev-list`. Este é basicamente o comando `git log`, mas por padrão ele mostra apenas os valores SHA-1 e nenhuma outra informação. Assim, para obter uma lista de todos os SHAs de commits introduzidos entre um commit SHA e outro, você pode executar algo como abaixo:
623
+
624
+ $ git rev-list 538c33..d14fc7
625
+ d14fc7c847ab946ec39590d87783c69b031bdfb7
626
+ 9f585da4401b0a3999e84113824d15245c13f0be
627
+ 234071a1be950e2a8d078e6141f5cd20c1e61ad3
628
+ dfa04c9ef3d5197182f13fb5b9b1fb7717d2222a
629
+ 17716ec0f1ff5c77eff40b7fe912f9f6cfd0e475
630
+
631
+ Você pode pegar essa saída, percorrer cada um dos SHAs dos commits, pegar a mensagem para ele, e testar a mensagem contra uma expressão regular que procura um padrão.
632
+
633
+ Você tem que descobrir como pegar a mensagem de confirmação de cada um dos commits para testar. Para obter os dados brutos do commit, você pode usar um outro comando plumbing chamado `git cat-file`. Eu vou falar de todos estes comandos plumbing em detalhes no Capítulo 9; mas, por agora, aqui está o resultado do comando:
634
+
635
+ $ git cat-file commit ca82a6
636
+ tree cfda3bf379e4f8dba8717dee55aab78aef7f4daf
637
+ parent 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
638
+ author Scott Chacon <schacon@gmail.com> 1205815931 -0700
639
+ committer Scott Chacon <schacon@gmail.com> 1240030591 -0700
640
+
641
+ changed the verison number
642
+
643
+ Uma maneira simples de obter a mensagem de confirmação de um commit quando você tem o valor SHA-1 é ir para a primeira linha em branco e retirar tudo depois dela. Você pode fazer isso com o comando `sed` em sistemas Unix:
644
+
645
+ $ git cat-file commit ca82a6 | sed '1,/^$/d'
646
+ changed the verison number
647
+
648
+ Você pode usar isso para pegar a mensagem de cada commit do push e sair se você ver algo que não corresponde. Para sair do script e rejeitar o push, retorne um valor diferente de zero. Todo o método se parece com este:
649
+
650
+ $regex = /\[ref: (\d+)\]/
651
+
652
+ # enforced custom commit message format
653
+ def check_message_format
654
+ missed_revs = `git rev-list #{$oldrev}..#{$newrev}`.split("\n")
655
+ missed_revs.each do |rev|
656
+ message = `git cat-file commit #{rev} | sed '1,/^$/d'`
657
+ if !$regex.match(message)
658
+ puts "[POLICY] Your message is not formatted correctly"
659
+ exit 1
660
+ end
661
+ end
662
+ end
663
+ check_message_format
664
+
665
+ Colocar isso no seu script `update` rejeitará atualizações que contenham commits que tem mensagens que não aderem à sua regra.
666
+
667
+ #### Impondo um Sistema ACL Baseado em Usuário ####
668
+
669
+ Suponha que você queira adicionar um mecanismo que utiliza uma lista de controle de acesso (ACL) que especifica quais usuários têm permissão para fazer push com mudanças para partes de seus projetos. Algumas pessoas têm acesso total, e outras só têm acesso a alterar determinados subdiretórios ou arquivos específicos. Para impor isso, você vai escrever essas regras em um arquivo chamado `acl` que ficará em seu repositório Git no servidor. O hook `update` verificará essas regras, verá quais arquivos estão sendo introduzidos nos commits do push, e determinará se o usuário que está fazendo o push tem acesso para atualizar todos os arquivos.
670
+
671
+ A primeira coisa que você deve fazer é escrever o seu ACL. Aqui você vai usar um formato muito parecido com o mecanismo de ACL CVS: ele usa uma série de linhas, onde o primeiro campo é `avail` ou `unavail`, o próximo campo é uma lista delimitada por vírgula dos usuários para que a regra se aplica, e o último campo é o caminho para o qual a regra se aplica (branco significando acesso em aberto). Todos esses campos são delimitados por um caractere pipe (`|`).
672
+
673
+ Neste caso, você tem alguns administradores, alguns escritores de documentação com acesso ao diretório `doc`, e um desenvolvedor que só tem acesso aos diretórios `lib` e `tests`, seu arquivo ACL fica assim:
674
+
675
+ avail|nickh,pjhyett,defunkt,tpw
676
+ avail|usinclair,cdickens,ebronte|doc
677
+ avail|schacon|lib
678
+ avail|schacon|tests
679
+
680
+ Você começa lendo esses dados em uma estrutura que você pode usar. Neste caso, para manter o exemplo simples, você só vai cumprir as diretrizes do `avail`. Aqui está um método que lhe dá um array associativo onde a chave é o nome do usuário e o valor é um conjunto de paths que o usuário tem acesso de escrita:
681
+
682
+ def get_acl_access_data(acl_file)
683
+ # read in ACL data
684
+ acl_file = File.read(acl_file).split("\n").reject { |line| line == '' }
685
+ access = {}
686
+ acl_file.each do |line|
687
+ avail, users, path = line.split('|')
688
+ next unless avail == 'avail'
689
+ users.split(',').each do |user|
690
+ access[user] ||= []
691
+ access[user] << path
692
+ end
693
+ end
694
+ access
695
+ end
696
+
697
+ No arquivo ACL que você viu anteriormente, o método `get_acl_access_data` retorna uma estrutura de dados que se parece com esta:
698
+
699
+ {"defunkt"=>[nil],
700
+ "tpw"=>[nil],
701
+ "nickh"=>[nil],
702
+ "pjhyett"=>[nil],
703
+ "schacon"=>["lib", "tests"],
704
+ "cdickens"=>["doc"],
705
+ "usinclair"=>["doc"],
706
+ "ebronte"=>["doc"]}
707
+
708
+ Agora que você tem as permissões organizadas, é preciso determinar quais os paths que os commits do push modificam, de modo que você pode ter certeza que o usuário que está fazendo o push tem acesso a todos eles.
709
+
710
+ Você pode muito facilmente ver quais arquivos foram modificados em um único commit com a opção `--name-only` do comando `git log` (mencionado brevemente no Capítulo 2):
711
+
712
+ $ git log -1 --name-only --pretty=format:'' 9f585d
713
+
714
+ README
715
+ lib/test.rb
716
+
717
+ Se você usar a estrutura ACL retornada pelo método `get_acl_access_data` e verificar a relação dos arquivos listados em cada um dos commits, você pode determinar se o usuário tem acesso ao push de todos os seus commits:
718
+
719
+ # only allows certain users to modify certain subdirectories in a project
720
+ def check_directory_perms
721
+ access = get_acl_access_data('acl')
722
+
723
+
724
+ # see if anyone is trying to push something they can't
725
+ new_commits = `git rev-list #{$oldrev}..#{$newrev}`.split("\n")
726
+ new_commits.each do |rev|
727
+ files_modified = `git log -1 --name-only --pretty=format:'' #{rev}`.split("\n")
728
+ files_modified.each do |path|
729
+ next if path.size == 0
730
+ has_file_access = false
731
+ access[$user].each do |access_path|
732
+ if !access_path || # user has access to everything
733
+ (path.index(access_path) == 0) # access to this path
734
+ has_file_access = true
735
+ end
736
+ end
737
+ if !has_file_access
738
+ puts "[POLICY] You do not have access to push to #{path}"
739
+ exit 1
740
+ end
741
+ end
742
+ end
743
+ end
744
+
745
+ check_directory_perms
746
+
747
+ A maior parte do código deve ser fácil de acompanhar. Você receberá uma lista de novos commits do push com `git rev-list`. Então, para cada um desses, você acha quais arquivos são modificados e verifica se o usuário que está fazendo o push tem acesso a todos os paths sendo modificados. Um Rubyism que pode não ser claro é `path.index(access_path) == 0`, que é verdadeiro se o caminho começa com `access_path` — isso garante que `access_path` não esta apenas em um dos caminhos permitidos, mas um path permitido começa com cada path acessado.
748
+
749
+ Agora seus usuários não podem fazer o push de qualquer commit com mensagens mal formadas ou com arquivos modificados fora de seus paths designados.
750
+
751
+ #### Impondo Apenas Fast-Forward Pushes ####
752
+
753
+ A única coisa que resta é impor apenas fast-forward pushes. Nas versões Git 1.6 ou mais recentes, você pode definir as configurações `receive.denyDeletes` e `receive.denyNonFastForwards`. Mas utilizar um hook irá funcionar em versões mais antigas do Git, e você pode modificá-lo para impor a diretiva apenas para determinados usuários ou fazer qualquer outra coisa que você queira.
754
+
755
+ A lógica para verificar isso é ver se algum commit é acessível a partir da revisão mais antiga que não é acessível a partir da versão mais recente. Se não houver nenhum, então foi um push Fast-Forward; caso contrário, você nega ele:
756
+
757
+ # enforces fast-forward only pushes
758
+ def check_fast_forward
759
+ missed_refs = `git rev-list #{$newrev}..#{$oldrev}`
760
+ missed_ref_count = missed_refs.split("\n").size
761
+ if missed_ref_count > 0
762
+ puts "[POLICY] Cannot push a non fast-forward reference"
763
+ exit 1
764
+ end
765
+ end
766
+
767
+ check_fast_forward
768
+
769
+ Tudo está configurado. Se você executar `chmod u+x .git/hooks/update`, que é o arquivo no qual você deve ter colocado todo este código, e então tentar fazer um push de uma referência não fast-forwarded, você verá algo como isto:
770
+
771
+ $ git push -f origin master
772
+ Counting objects: 5, done.
773
+ Compressing objects: 100% (3/3), done.
774
+ Writing objects: 100% (3/3), 323 bytes, done.
775
+ Total 3 (delta 1), reused 0 (delta 0)
776
+ Unpacking objects: 100% (3/3), done.
777
+ Enforcing Policies...
778
+ (refs/heads/master) (8338c5) (c5b616)
779
+ [POLICY] Cannot push a non fast-forward reference
780
+ error: hooks/update exited with error code 1
781
+ error: hook declined to update refs/heads/master
782
+ To git@gitserver:project.git
783
+ ! [remote rejected] master -> master (hook declined)
784
+ error: failed to push some refs to 'git@gitserver:project.git'
785
+
786
+ Há algumas coisas interessantes aqui. Primeiro, você vê quando o hook começa a funcionar.
787
+
788
+ Enforcing Policies...
789
+ (refs/heads/master) (8338c5) (c5b616)
790
+
791
+ Observe que você imprimiu aquilo no stdout no início do seu script de atualização. É importante notar que qualquer coisa que seu script imprima no stdout será transferido para o cliente.
792
+
793
+ A próxima coisa que você vai notar é a mensagem de erro.
794
+
795
+ [POLICY] Cannot push a non fast-forward reference
796
+ error: hooks/update exited with error code 1
797
+ error: hook declined to update refs/heads/master
798
+
799
+ A primeira linha foi impressa por você, as outras duas foram pelo Git dizendo que o script de atualização não retornou zero e é isso que está impedindo seu push. Por último, você verá isso:
800
+
801
+ To git@gitserver:project.git
802
+ ! [remote rejected] master -> master (hook declined)
803
+ error: failed to push some refs to 'git@gitserver:project.git'
804
+
805
+ Você verá uma mensagem de rejeição remota para cada referência que o seu hook impediu, e ele diz que ele foi recusado especificamente por causa de uma falha de hook.
806
+
807
+ Além disso, se o marcador ref não existir em nenhum dos seus commits, você verá a mensagem de erro que você está imprimindo para ele.
808
+
809
+ [POLICY] Your message is not formatted correctly
810
+
811
+ Ou se alguém tentar editar um arquivo que não têm acesso e fazer um push de um commit que o contém, ele verá algo semelhante. Por exemplo, se um autor de documentação tenta fazer um push de um commit modificando algo no diretório `lib`, ele verá
812
+
813
+ [POLICY] You do not have access to push to lib/test.rb
814
+
815
+ Isto é tudo. A partir de agora, desde que o script `update` esteja lá e seja executável, seu repositório nunca será rebobinado e nunca terá uma mensagem de commit sem o seu padrão nela, e os usuários terão restrições.
816
+
817
+ ### Hooks do Lado Cliente ###
818
+
819
+ A desvantagem desta abordagem é a choraminga que resultará inevitavelmente quando os pushes de commits de seus usuários forem rejeitados. Tendo seu trabalho cuidadosamente elaborada rejeitado no último minuto pode ser extremamente frustrante e confuso; e, além disso, eles vão ter que editar seu histórico para corrigi-lo, o que nem sempre é para os fracos de coração.
820
+
821
+ A resposta para este dilema é fornecer alguns hooks do lado cliente que os usuários possam usar para notificá-los quando eles estão fazendo algo que o servidor provavelmente rejeitará. Dessa forma, eles podem corrigir quaisquer problemas antes de fazer o commit e antes desses problemas se tornarem mais difíceis de corrigir. Já que hooks não são transferidos com um clone de um projeto, você deve distribuir esses scripts de alguma outra forma e, então, usuários devem copiá-los para seu diretório `.git/hooks` e torná-los executáveis. Você pode distribuir esses hooks dentro do projeto ou em um projeto separado, mas não há maneiras de configurá-los automaticamente.
822
+
823
+ Para começar, você deve verificar a sua mensagem de confirmação antes que cada commit seja gravado, então você saberá que o servidor não irá rejeitar as alterações devido a mensagens de commit mal formatadas. Para fazer isso, você pode adicionar o hook `commit-msg`. Se fizer ele ler as mensagens do arquivo passado como o primeiro argumento e comparar ele com o padrão, você pode forçar o Git a abortar o commit se eles não corresponderem:
824
+
825
+ #!/usr/bin/env ruby
826
+ message_file = ARGV[0]
827
+ message = File.read(message_file)
828
+
829
+ $regex = /\[ref: (\d+)\]/
830
+
831
+ if !$regex.match(message)
832
+ puts "[POLICY] Your message is not formatted correctly"
833
+ exit 1
834
+ end
835
+
836
+ Se esse script está no lugar correto (em `.git/hooks/commit-msg`) e é executável, e você fizer um commit com uma mensagem que não está formatada corretamente, você verá isso:
837
+
838
+ $ git commit -am 'test'
839
+ [POLICY] Your message is not formatted correctly
840
+
841
+ Nenhum commit foi concluído nessa instância. No entanto, se a mensagem conter o padrão adequado, o Git permite o commit:
842
+
843
+ $ git commit -am 'test [ref: 132]'
844
+ [master e05c914] test [ref: 132]
845
+ 1 files changed, 1 insertions(+), 0 deletions(-)
846
+
847
+ Em seguida, você quer ter certeza de que você não está modificando os arquivos que estão fora do seu escopo ACL. Se o seu diretório de projeto `.git` contém uma cópia do arquivo ACL que você usou anteriormente, então o seguinte script `pre-commit` irá impor essas restrições para você:
848
+
849
+ #!/usr/bin/env ruby
850
+
851
+ $user = ENV['USER']
852
+
853
+ # [ insert acl_access_data method from above ]
854
+
855
+ # only allows certain users to modify certain subdirectories in a project
856
+ def check_directory_perms
857
+ access = get_acl_access_data('.git/acl')
858
+
859
+ files_modified = `git diff-index --cached --name-only HEAD`.split("\n")
860
+ files_modified.each do |path|
861
+ next if path.size == 0
862
+ has_file_access = false
863
+ access[$user].each do |access_path|
864
+ if !access_path || (path.index(access_path) == 0)
865
+ has_file_access = true
866
+ end
867
+ if !has_file_access
868
+ puts "[POLICY] You do not have access to push to #{path}"
869
+ exit 1
870
+ end
871
+ end
872
+ end
873
+
874
+ check_directory_perms
875
+
876
+ Este é aproximadamente o mesmo script da parte do lado servidor, mas com duas diferenças importantes. Primeiro, o arquivo ACL está em um lugar diferente, porque este script é executado a partir do seu diretório de trabalho, e não de seu diretório Git. Você tem que mudar o path para o arquivo ACL, disso
877
+
878
+ access = get_acl_access_data('acl')
879
+
880
+ para isso:
881
+
882
+ access = get_acl_access_data('.git/acl')
883
+
884
+ A outra diferença importante é a forma como você obtem uma lista dos arquivos que foram alterados. Como o método do lado servidor olha no log de ​​commits e, neste momento, o commit não foi gravado ainda, você deve pegar sua lista de arquivos da área staging. Em vez de
885
+
886
+ files_modified = `git log -1 --name-only --pretty=format:'' #{ref}`
887
+
888
+ você deve usar
889
+
890
+ files_modified = `git diff-index --cached --name-only HEAD`
891
+
892
+ Mas essas são as duas únicas diferenças — caso contrário, o script funciona da mesma maneira. Uma ressalva é que ele espera que você esteja executando localmente como o mesmo usuário que você fez o push para a máquina remota. Se ele for diferente, você deve definir a variável `$user` manualmente.
893
+
894
+ A última coisa que você tem que fazer é verificar se você não está tentando fazer o push de referências não fast-forwarded, mas isso é um pouco menos comum. Para obter uma referência que não é um fast-forward, você tem que fazer um rebase depois de um commit que já foi enviado por um push ou tentar fazer o push de um branch local diferente até o mesmo branch remoto.
895
+
896
+ Como o servidor vai dizer que você não pode fazer um push não fast-forward de qualquer maneira, e o hook impede pushes forçados, a única coisa acidental que você pode tentar deter são commits de rebase que já foram enviados por um push.
897
+
898
+ Aqui está um exemplo de script pré-rebase que verifica isso. Ele recebe uma lista de todos os commits que você está prestes a reescrever e verifica se eles existem em qualquer uma das suas referências remotas. Se ele vê um que é acessível a partir de uma de suas referências remotas, ele aborta o rebase:
899
+
900
+ #!/usr/bin/env ruby
901
+
902
+ base_branch = ARGV[0]
903
+ if ARGV[1]
904
+ topic_branch = ARGV[1]
905
+ else
906
+ topic_branch = "HEAD"
907
+ end
908
+
909
+ target_shas = `git rev-list #{base_branch}..#{topic_branch}`.split("\n")
910
+ remote_refs = `git branch -r`.split("\n").map { |r| r.strip }
911
+
912
+ target_shas.each do |sha|
913
+ remote_refs.each do |remote_ref|
914
+ shas_pushed = `git rev-list ^#{sha}^@ refs/remotes/#{remote_ref}`
915
+ if shas_pushed.split(“\n”).include?(sha)
916
+ puts "[POLICY] Commit #{sha} has already been pushed to #{remote_ref}"
917
+ exit 1
918
+ end
919
+ end
920
+ end
921
+
922
+ Este script utiliza uma sintaxe que não foi coberta na seção Seleção de Revisão do Capítulo 6. Você obterá uma lista de commits que já foram foram enviados em um push executando isto:
923
+
924
+ git rev-list ^#{sha}^@ refs/remotes/#{remote_ref}
925
+
926
+ A sintaxe `SHA^@` resolve para todos os pais daquele commit. Você está à procura de qualquer commit que é acessível a partir do último commit no remoto e que não é acessível a partir de qualquer pai de qualquer um dos SHAs que você está tentando fazer o push — o que significa que é um fast-forward.
927
+
928
+ A principal desvantagem desta abordagem é que ela pode ser muito lenta e muitas vezes é desnecessária — se você não tentar forçar o push com `-f`, o servidor irá avisá-lo e não aceitará o push. No entanto, é um exercício interessante e pode, em teoria, ajudar a evitar um rebase que você possa mais tarde ter que voltar atrás e corrigir.
929
+
930
+ ## Sumário ##
931
+
932
+ Você viu a maior parte das principais formas que você pode usar para personalizar o seu cliente e servidor Git para melhor atender a seu fluxo de trabalho e projetos. Você aprendeu sobre todos os tipos de configurações, atributos baseados em arquivos, e hooks de eventos, e você construiu um exemplo de política aplicada ao servidor. Agora você deve ser capaz de usar o Git em quase qualquer fluxo de trabalho que você possa sonhar.