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,912 @@
1
+ # 分散式 Git #
2
+
3
+ 為了便於專案中的所有開發者分享代碼,我們準備好了一台伺服器存放遠端 Git 倉庫。經過前面幾章的學習,我們已經學會了一些基本的本地工作流程中所需用到的命令。接下來,我們要學習下如何利用 Git 來組織和完成分散式工作流程。
4
+
5
+ 特別是,當作為項目貢獻者時,我們該怎麼做才能方便維護者採納更新;或者作為專案維護者時,又該怎樣有效管理大量貢獻者的提交。
6
+
7
+ ## 分散式工作流程 ##
8
+
9
+ 同傳統的集中式版本控制系統(CVCS)不同,開發者之間的協作方式因著 Git 的分散式特性而變得更為靈活多樣。在集中式系統上,每個開發者就像是連接在集線器上的節點,彼此的工作方式大體相像。而在 Git 網路中,每個開發者同時扮演著節點和集線器的角色,這就是說,每一個開發者都可以將自己的代碼貢獻到另外一個開發者的倉庫中,或者建立自己的公共倉庫,讓其他開發者基於自己的工作開始,為自己的倉庫貢獻代碼。於是,Git 的分散式協作便可以衍生出種種不同的工作流程,我會在接下來的章節介紹幾種常見的應用方式,並分別討論各自的優缺點。你可以選擇其中的一種,或者結合起來,應用到你自己的專案中。
10
+
11
+ ### 集中式工作流 ###
12
+
13
+ 通常,集中式工作流程使用的都是單點協作模型。一個存放代碼倉庫的中心伺服器,可以接受所有開發者提交的代碼。所有的開發者都是普通的節點,作為中心集線器的消費者,平時的工作就是和中心倉庫同步資料(見圖 5-1)。
14
+
15
+ Insert 18333fig0501.png
16
+ 圖 5-1. 集中式工作流
17
+
18
+ 如果兩個開發者從中心倉庫克隆代碼下來,同時作了一些修訂,那麼只有第一個開發者可以順利地把資料推送到共用伺服器。第二個開發者在提交他的修訂之前,必須先下載合併伺服器上的資料,解決衝突之後才能推送資料到共用伺服器上。在 Git 中這麼用也決無問題,這就好比是在用 Subversion(或其他 CVCS)一樣,可以很好地工作。
19
+
20
+ 如果你的團隊不是很大,或者大家都已經習慣了使用集中式工作流程,完全可以採用這種簡單的模式。只需要配置好一台中心伺服器,並給每個人推送資料的許可權,就可以開展工作了。但如果提交代碼時有衝突, Git 根本就不會讓用戶覆蓋他人代碼,它直接駁回第二個人的提交操作。這就等於告訴提交者,你所作的修訂無法通過快近(fast-forward)來合併,你必須先拉取最新資料下來,手工解決衝突合併後,才能繼續推送新的提交。
21
+ 絕大多數人都熟悉和瞭解這種模式的工作方式,所以使用也非常廣泛。
22
+
23
+ ### 集成管理員工作流 ###
24
+
25
+ 由於 Git 允許使用多個遠端倉庫,開發者便可以建立自己的公共倉庫,往裡面寫資料並共用給他人,而同時又可以從別人的倉庫中提取他們的更新過來。這種情形通常都會有個代表著官方發佈的專案倉庫(blessed repository),開發者們由此倉庫克隆出一個自己的公共倉庫(developer public),然後將自己的提交推送上去,請求官方倉庫的維護者拉取更新合併到主項目。維護者在自己的本地也有個克隆倉庫(integration manager),他可以將你的公共倉庫作為遠端倉庫添加進來,經過測試無誤後合併到主幹分支,然後再推送到官方倉庫。工作流程看起來就像圖 5-2 所示:
26
+
27
+ 1. 專案維護者可以推送資料到公共倉庫 blessed repository。
28
+ 2. 貢獻者克隆此倉庫,修訂或編寫新代碼。
29
+ 3. 貢獻者推送資料到自己的公共倉庫 developer public。
30
+ 4. 貢獻者給維護者發送郵件,請求拉取自己的最新修訂。
31
+ 5. 維護者在自己本地的 integration manger 倉庫中,將貢獻者的倉庫加為遠程倉庫,合併更新並做測試。
32
+ 6. 維護者將合併後的更新推送到主倉庫 blessed repository。
33
+
34
+ Insert 18333fig0502.png
35
+ 圖 5-2. 集成管理員工作流
36
+
37
+ 在 GitHub 網站上使用得最多的就是這種工作流。人們可以複製(fork 亦即克隆)某個項目到自己的列表中,成為自己的公共倉庫。隨後將自己的更新提交到這個倉庫,所有人都可以看到你的每次更新。這麼做最主要的優點在於,你可以按照自己的節奏繼續工作,而不必等待維護者處理你提交的更新;而維護者也可以按照自己的節奏,任何時候都可以過來處理接納你的貢獻。
38
+
39
+ ### 司令官與副官工作流 ###
40
+
41
+ 這其實是上一種工作流的變體。一般超大型的專案才會用到這樣的工作方式,像是擁有數百協作開發者的 Linux 內核專案就是如此。各個集成管理員分別負責集成專案中的特定部分,所以稱為副官(lieutenant)。而所有這些集成管理員頭上還有一位負責統籌的總集成管理員,稱為司令官(dictator)。司令官維護的倉庫用於提供所有協作者拉取最新集成的項目代碼。整個流程看起來如圖 5-3 所示:
42
+
43
+ 1. 一般的開發者在自己的特性分支上工作,並不定期地根據主幹分支(dictator 上的 master)衍合。
44
+ 2. 副官(lieutenant)將普通開發者的特性分支合併到自己的 master 分支中。
45
+ 3. 司令官(dictator)將所有副官的 master 分支併入自己的 master 分支。
46
+ 4. 司令官(dictator)將集成後的 master 分支推送到共用倉庫 blessed repository 中,以便所有其他開發者以此為基礎進行衍合。
47
+
48
+ Insert 18333fig0503.png
49
+ 圖 5-3. 司令官與副官工作流
50
+
51
+ 這種工作流程並不常用,只有當專案極為龐雜,或者需要多級別管理時,才會體現出優勢。利用這種方式,專案總負責人(即司令官)可以把大量分散的集成工作委託給不同的小組負責人分別處理,最後再統籌起來,如此各人的職責清晰明確,也不易出錯(譯注:此乃分而治之)。
52
+
53
+ 以上介紹的是常見的分散式系統可以應用的工作流程,當然不止於 Git。在實際的開發工作中,你可能會遇到各種為了滿足特定需求而有所變化的工作方式。我想現在你應該已經清楚,接下來自己需要用哪種方式開展工作了。下節我還會再舉些例子,看看各式工作流中的每個角色具體應該如何操作。
54
+
55
+ ## 為專案作貢獻 ##
56
+
57
+ 接下來,我們來學習一下作為專案貢獻者,會有哪些常見的工作模式。
58
+
59
+ 不過要說清楚整個協作過程真的很難,Git 如此靈活,人們的協作方式便可以各式各樣,沒有固定不變的範式可循,而每個專案的具體情況又多少會有些不同,比如說參與者的規模,所選擇的工作流程,每個人的提交許可權,以及 Git 以外貢獻等等,都會影響到具體操作的細節。
60
+
61
+ 首當其衝的是參與者規模。專案中有多少開發者是經常提交代碼的?經常又是多久呢?大多數兩至三人的小團隊,一天大約只有幾次提交,如果不是什麼熱門項目的話就更少了。可要是在大公司裡,或者大項目中,參與者可以多到上千,每天都會有十幾個上百個補丁提交上來。這種差異帶來的影響是顯著的,越是多的人參與進來,就越難保證每次合併正確無誤。你正在工作的代碼,可能會因為合併進來其他人的更新而變得過時,甚至受創無法運行。而已經提交上去的更新,也可能在等著審核合併的過程中變得過時。那麼,我們該怎樣做才能確保代碼是最新的,提交的補丁也是可用的呢?
62
+
63
+ 接下來便是專案所採用的工作流。是集中式的,每個開發者都具有等同的寫許可權?專案是否有專人負責檢查所有補丁?是不是所有補丁都做過同行複閱(peer-review)再通過審核的?你是否參與審核過程?如果使用副官系統,那你是不是限定于只能向此副官提交?
64
+
65
+ 還有你的提交許可權。有或沒有向主專案提交更新的許可權,結果完全不同,直接決定最終採用怎樣的工作流。如果不能直接提交更新,那該如何貢獻自己的代碼呢?是不是該有個什麼策略?你每次貢獻代碼會有多少量?提交頻率呢?
66
+
67
+ 所有以上這些問題都會或多或少影響到最終採用的工作流。接下來,我會在一系列由簡入繁的具體用例中,逐一闡述。此後在實踐時,應該可以借鑒這裡的例子,略作調整,以滿足實際需要構建自己的工作流。
68
+
69
+ ### 提交指南 ###
70
+
71
+ 開始分析特定用例之前,先來瞭解下如何撰寫提交說明。一份好的提交指南可以説明協作者更輕鬆更有效地配合。Git 項目本身就提供了一份文檔(Git 專案原始程式碼目錄中 `Documentation/SubmittingPatches`),列數了大量提示,從如何編撰提交說明到提交補丁,不一而足。
72
+
73
+ 首先,請不要在更新中提交多餘的白字元(whitespace)。Git 有種檢查此類問題的方法,在提交之前,先運行 `git diff --check`,會把可能的多餘白字元修正列出來。下面的示例,我已經把終端中顯示為紅色的白字元用 `X` 替換掉:
74
+
75
+ $ git diff --check
76
+ lib/simplegit.rb:5: trailing whitespace.
77
+ + @git_dir = File.expand_path(git_dir)XX
78
+ lib/simplegit.rb:7: trailing whitespace.
79
+ + XXXXXXXXXXX
80
+ lib/simplegit.rb:26: trailing whitespace.
81
+ + def command(git_cmd)XXXX
82
+
83
+ 這樣在提交之前你就可以看到這類問題,及時解決以免困擾其他開發者。
84
+
85
+ 接下來,請將每次提交限定於完成一次邏輯功能。並且可能的話,適當地分解為多次小更新,以便每次小型提交都更易於理解。請不要在週末窮追猛打一次性解決五個問題,而最後拖到週一再提交。就算是這樣也請盡可能利用暫存區域,將之前的改動分解為每次修復一個問題,再分別提交和加注說明。如果針對兩個問題改動的是同一個檔,可以試試看 `git add --patch` 的方式將部分內容置入暫存區域(我們會在第六章再詳細介紹)。無論是五次小提交還是混雜在一起的大提交,最終分支末端的專案快照應該還是一樣的,但分解開來之後,更便於其他開發者複閱。這麼做也方便自己將來取消某個特定問題的修復。我們將在第六章介紹一些重寫提交歷史,同暫存區域交互的技巧和工具,以便最終得到一個乾淨有意義,且易於理解的提交歷史。
86
+
87
+ 最後需要謹記的是提交說明的撰寫。寫得好可以讓大家協作起來更輕鬆。一般來說,提交說明最好限制在一行以內,50 個字元以下,簡明扼要地描述更新內容,空開一行後,再展開詳細注解。Git 專案本身需要開發者撰寫詳盡注解,包括本次修訂的因由,以及前後不同實現之間的比較,我們也該借鑒這種做法。另外,提交說明應該用祈使現在式語態,比如,不要說成 “I added tests for” 或 “Adding tests for” 而應該用 “Add tests for”。
88
+ 下面是來自 tpope.net 的 Tim Pope 原創的提交說明格式模版,供參考:
89
+
90
+ 本次更新的簡要描述(50 個字元以內)
91
+
92
+ 如果必要,此處展開詳盡闡述。段落寬度限定在 72 個字元以內。
93
+ 某些情況下,第一行的簡要描述將用作郵件標題,其餘部分作為郵件正文。
94
+ 其間的空行是必要的,以區分兩者(當然沒有正文另當別論)。
95
+ 如果並在一起,rebase 這樣的工具就可能會迷惑。
96
+
97
+ 另起空行後,再進一步補充其他說明。
98
+
99
+ - 可以使用這樣的條目列舉式。
100
+
101
+ - 一般以單個空格緊跟短劃線或者星號作為每項條目的起始符。每個條目間用一空行隔開。
102
+ 不過這裡按自己項目的約定,可以略作變化。
103
+
104
+ 如果你的提交說明都用這樣的格式來書寫,好多事情就可以變得十分簡單。Git 專案本身就是這樣要求的,我強烈建議你到 Git 專案倉庫下運行 `git log --no-merges` 看看,所有提交歷史的說明是怎樣撰寫的。(譯注:如果現在還沒有克隆 git 項目原始程式碼,是時候 `git clone git://git.kernel.org/pub/scm/git/git.git` 了。)
105
+
106
+ 為簡單起見,在接下來的例子(及本書隨後的所有演示)中,我都不會用這種格式,而使用 `-m` 選項提交 `git commit`。不過請還是按照我之前講的做,別學我這裡偷懶的方式。
107
+
108
+ ### 私有的小型團隊 ###
109
+
110
+ 我們從最簡單的情況開始,一個私有專案,與你一起協作的還有另外一到兩位開發者。這裡說私有,是指原始程式碼不公開,其他人無法訪問項目倉庫。而你和其他開發者則都具有推送資料到倉庫的許可權。
111
+
112
+ 這種情況下,你們可以用 Subversion 或其他集中式版本控制系統類似的工作流來協作。你仍然可以得到 Git 帶來的其他好處:離線提交,快速分支與合併等等,但工作流程還是差不多的。主要區別在於,合併操作發生在用戶端而非伺服器上。
113
+ 讓我們來看看,兩個開發者一起使用同一個共用倉庫,會發生些什麼。第一個人,John,克隆了倉庫,作了些更新,在本地提交。(下面的例子中省略了常規提示,用 `...` 代替以節約版面。)
114
+
115
+ # John's Machine
116
+ $ git clone john@githost:simplegit.git
117
+ Initialized empty Git repository in /home/john/simplegit/.git/
118
+ ...
119
+ $ cd simplegit/
120
+ $ vim lib/simplegit.rb
121
+ $ git commit -am 'removed invalid default value'
122
+ [master 738ee87] removed invalid default value
123
+ 1 files changed, 1 insertions(+), 1 deletions(-)
124
+
125
+ 第二個開發者,Jessica,一樣這麼做:克隆倉庫,提交更新:
126
+
127
+ # Jessica's Machine
128
+ $ git clone jessica@githost:simplegit.git
129
+ Initialized empty Git repository in /home/jessica/simplegit/.git/
130
+ ...
131
+ $ cd simplegit/
132
+ $ vim TODO
133
+ $ git commit -am 'add reset task'
134
+ [master fbff5bc] add reset task
135
+ 1 files changed, 1 insertions(+), 0 deletions(-)
136
+
137
+ 現在,Jessica 將她的工作推送到伺服器上:
138
+
139
+ # Jessica's Machine
140
+ $ git push origin master
141
+ ...
142
+ To jessica@githost:simplegit.git
143
+ 1edee6b..fbff5bc master -> master
144
+
145
+ John 也嘗試推送自己的工作上去:
146
+
147
+ # John's Machine
148
+ $ git push origin master
149
+ To john@githost:simplegit.git
150
+ ! [rejected] master -> master (non-fast forward)
151
+ error: failed to push some refs to 'john@githost:simplegit.git'
152
+
153
+ John 的推送操作被駁回,因為 Jessica 已經推送了新的資料上去。請注意,特別是你用慣了 Subversion 的話,這裡其實修改的是兩個檔,而不是同一個檔的同一個地方。Subversion 會在伺服器端自動合併提交上來的更新,而 Git 則必須先在本地合併後才能推送。於是,John 不得不先把 Jessica 的更新拉下來:
154
+
155
+ $ git fetch origin
156
+ ...
157
+ From john@githost:simplegit
158
+ + 049d078...fbff5bc master -> origin/master
159
+
160
+ 此刻,John 的本地倉庫如圖 5-4 所示:
161
+
162
+ Insert 18333fig0504.png
163
+ 圖 5-4. John 的倉庫歷史
164
+
165
+ 雖然 John 下載了 Jessica 推送到伺服器的最近更新(fbff5),但目前只是 `origin/master` 指標指向它,而當前的本地分支 `master` 仍然指向自己的更新(738ee),所以需要先把她的提交合併過來,才能繼續推送資料:
166
+
167
+ $ git merge origin/master
168
+ Merge made by recursive.
169
+ TODO | 1 +
170
+ 1 files changed, 1 insertions(+), 0 deletions(-)
171
+
172
+ 還好,合併過程非常順利,沒有衝突,現在 John 的提交歷史如圖 5-5 所示:
173
+
174
+ Insert 18333fig0505.png
175
+ 圖 5-5. 合併 origin/master 後 John 的倉庫歷史
176
+
177
+ 現在,John 應該再測試一下代碼是否仍然正常工作,然後將合併結果(72bbc)推送到伺服器上:
178
+
179
+ $ git push origin master
180
+ ...
181
+ To john@githost:simplegit.git
182
+ fbff5bc..72bbc59 master -> master
183
+
184
+ 最終,John 的提交歷史變為圖 5-6 所示:
185
+
186
+ Insert 18333fig0506.png
187
+ 圖 5-6. 推送後 John 的倉庫歷史
188
+
189
+ 而在這段時間,Jessica 已經開始在另一個特性分支工作了。她創建了 `issue54` 並提交了三次更新。她還沒有下載 John 提交的合併結果,所以提交歷史如圖 5-7 所示:
190
+
191
+ Insert 18333fig0507.png
192
+ 圖 5-7. Jessica 的提交歷史
193
+
194
+ Jessica 想要先和伺服器上的資料同步,所以先下載資料:
195
+
196
+ # Jessica's Machine
197
+ $ git fetch origin
198
+ ...
199
+ From jessica@githost:simplegit
200
+ fbff5bc..72bbc59 master -> origin/master
201
+
202
+ 於是 Jessica 的本地倉庫歷史多出了 John 的兩次提交(738ee 和 72bbc),如圖 5-8 所示:
203
+
204
+ Insert 18333fig0508.png
205
+ 圖 5-8. 獲取 John 的更新之後 Jessica 的提交歷史
206
+
207
+ 此時,Jessica 在特性分支上的工作已經完成,但她想在推送資料之前,先確認下要並進來的資料究竟是什麼,於是運行 `git log` 查看:
208
+
209
+ $ git log --no-merges origin/master ^issue54
210
+ commit 738ee872852dfaa9d6634e0dea7a324040193016
211
+ Author: John Smith <jsmith@example.com>
212
+ Date: Fri May 29 16:01:27 2009 -0700
213
+
214
+ removed invalid default value
215
+
216
+ 現在,Jessica 可以將特性分支上的工作並到 `master` 分支,然後再併入 John 的工作(`origin/master`)到自己的 `master` 分支,最後再推送回伺服器。當然,得先切回主分支才能集成所有資料:
217
+
218
+ $ git checkout master
219
+ Switched to branch "master"
220
+ Your branch is behind 'origin/master' by 2 commits, and can be fast-forwarded.
221
+
222
+ 要合併 `origin/master` 或 `issue54` 分支,誰先誰後都沒有關係,因為它們都在上游(upstream)(譯注:想像分叉的更新像是匯流成河的源頭,所以上游 upstream 是指最新的提交),所以無所謂先後順序,最終合併後的內容快照都是一樣的,而僅是提交歷史看起來會有些先後差別。Jessica 選擇先合併 `issue54`:
223
+
224
+ $ git merge issue54
225
+ Updating fbff5bc..4af4298
226
+ Fast forward
227
+ README | 1 +
228
+ lib/simplegit.rb | 6 +++++-
229
+ 2 files changed, 6 insertions(+), 1 deletions(-)
230
+
231
+ 正如所見,沒有衝突發生,僅是一次簡單快進。現在 Jessica 開始合併 John 的工作(`origin/master`):
232
+
233
+ $ git merge origin/master
234
+ Auto-merging lib/simplegit.rb
235
+ Merge made by recursive.
236
+ lib/simplegit.rb | 2 +-
237
+ 1 files changed, 1 insertions(+), 1 deletions(-)
238
+
239
+ 所有的合併都非常乾淨。現在 Jessica 的提交歷史如圖 5-9 所示:
240
+
241
+ Insert 18333fig0509.png
242
+ 圖 5-9. 合併 John 的更新後 Jessica 的提交歷史
243
+
244
+ 現在 Jessica 已經可以在自己的 `master` 分支中訪問 `origin/master` 的最新改動了,所以她應該可以成功推送最後的合併結果到伺服器上(假設 John 此時沒再推送新資料上來):
245
+
246
+ $ git push origin master
247
+ ...
248
+ To jessica@githost:simplegit.git
249
+ 72bbc59..8059c15 master -> master
250
+
251
+ 至此,每個開發者都提交了若干次,且成功合併了對方的工作成果,最新的提交歷史如圖 5-10 所示:
252
+
253
+ Insert 18333fig0510.png
254
+ 圖 5-10. Jessica 推送資料後的提交歷史
255
+
256
+ 以上就是最簡單的協作方式之一:先在自己的特性分支中工作一段時間,完成後合併到自己的 `master` 分支;然後下載合併 `origin/master` 上的更新(如果有的話),再推回遠端伺服器。一般的協作流程如圖 5-11 所示:
257
+
258
+ Insert 18333fig0511.png
259
+ 圖 5-11. 多使用者共用倉庫協作方式的一般工作流程時序
260
+
261
+ ### 私有團隊間協作 ###
262
+
263
+ 現在我們來看更大一點規模的私有團隊協作。如果有幾個小組分頭負責若干特性的開發和集成,那他們之間的協作過程是怎樣的。
264
+
265
+ 假設 John 和 Jessica 一起負責開發某項特性 A,而同時 Jessica 和 Josie 一起負責開發另一項功能 B。公司使用典型的集成管理員式工作流,每個組都有一名管理員負責集成本組代碼,及更新專案主倉庫的 `master` 分支。所有開發都在代表小組的分支上進行。
266
+
267
+ 讓我們跟隨 Jessica 的視角看看她的工作流程。她參與開發兩項特性,同時和不同小組的開發者一起協作。克隆生成本地倉庫後,她打算先著手開發特性 A。於是創建了新的 `featureA` 分支,繼而編寫代碼:
268
+
269
+ # Jessica's Machine
270
+ $ git checkout -b featureA
271
+ Switched to a new branch "featureA"
272
+ $ vim lib/simplegit.rb
273
+ $ git commit -am 'add limit to log function'
274
+ [featureA 3300904] add limit to log function
275
+ 1 files changed, 1 insertions(+), 1 deletions(-)
276
+
277
+ 此刻,她需要分享目前的進展給 John,於是她將自己的 `featureA` 分支提交到伺服器。由於 Jessica 沒有許可權推送資料到主倉庫的 `master` 分支(只有集成管理員有此許可權),所以只能將此分支推上去同 John 共用協作:
278
+
279
+ $ git push origin featureA
280
+ ...
281
+ To jessica@githost:simplegit.git
282
+ * [new branch] featureA -> featureA
283
+
284
+ Jessica 發郵件給 John 讓他上來看看 `featureA` 分支上的進展。在等待他的回饋之前,Jessica 決定繼續工作,和 Josie 一起開發 `featureB` 上的特性 B。當然,先創建此分支,分叉點以伺服器上的 `master` 為起點:
285
+
286
+ # Jessica's Machine
287
+ $ git fetch origin
288
+ $ git checkout -b featureB origin/master
289
+ Switched to a new branch "featureB"
290
+
291
+ 隨後,Jessica 在 `featureB` 上提交了若干更新:
292
+
293
+ $ vim lib/simplegit.rb
294
+ $ git commit -am 'made the ls-tree function recursive'
295
+ [featureB e5b0fdc] made the ls-tree function recursive
296
+ 1 files changed, 1 insertions(+), 1 deletions(-)
297
+ $ vim lib/simplegit.rb
298
+ $ git commit -am 'add ls-files'
299
+ [featureB 8512791] add ls-files
300
+ 1 files changed, 5 insertions(+), 0 deletions(-)
301
+
302
+ 現在 Jessica 的更新歷史如圖 5-12 所示:
303
+
304
+ Insert 18333fig0512.png
305
+ 圖 5-12. Jessica 的更新歷史
306
+
307
+ Jessica 正準備推送自己的進展上去,卻收到 Josie 的來信,說是她已經將自己的工作推到伺服器上的 `featureBee` 分支了。這樣,Jessica 就必須先將 Josie 的代碼合併到自己本地分支中,才能再一起推送回伺服器。她用 `git fetch` 下載 Josie 的最新代碼:
308
+
309
+ $ git fetch origin
310
+ ...
311
+ From jessica@githost:simplegit
312
+ * [new branch] featureBee -> origin/featureBee
313
+
314
+ 然後 Jessica 使用 `git merge` 將此分支合併到自己分支中:
315
+
316
+ $ git merge origin/featureBee
317
+ Auto-merging lib/simplegit.rb
318
+ Merge made by recursive.
319
+ lib/simplegit.rb | 4 ++++
320
+ 1 files changed, 4 insertions(+), 0 deletions(-)
321
+
322
+ 合併很順利,但另外有個小問題:她要推送自己的 `featureB` 分支到伺服器上的 `featureBee` 分支上去。當然,她可以使用冒號(:)格式指定目標分支:
323
+
324
+ $ git push origin featureB:featureBee
325
+ ...
326
+ To jessica@githost:simplegit.git
327
+ fba9af8..cd685d1 featureB -> featureBee
328
+
329
+ 我們稱此為_refspec_。更多有關於 Git refspec 的討論和使用方式會在第九章作詳細闡述。
330
+
331
+ 接下來,John 發郵件給 Jessica 告訴她,他看了之後作了些修改,已經推回伺服器 `featureA` 分支,請她過目下。於是 Jessica 運行 `git fetch` 下載最新資料:
332
+
333
+ $ git fetch origin
334
+ ...
335
+ From jessica@githost:simplegit
336
+ 3300904..aad881d featureA -> origin/featureA
337
+
338
+ 接下來便可以用 `git log` 查看更新了些什麼:
339
+
340
+ $ git log origin/featureA ^featureA
341
+ commit aad881d154acdaeb2b6b18ea0e827ed8a6d671e6
342
+ Author: John Smith <jsmith@example.com>
343
+ Date: Fri May 29 19:57:33 2009 -0700
344
+
345
+ changed log output to 30 from 25
346
+
347
+ 最後,她將 John 的工作合併到自己的 `featureA` 分支中:
348
+
349
+ $ git checkout featureA
350
+ Switched to branch "featureA"
351
+ $ git merge origin/featureA
352
+ Updating 3300904..aad881d
353
+ Fast forward
354
+ lib/simplegit.rb | 10 +++++++++-
355
+ 1 files changed, 9 insertions(+), 1 deletions(-)
356
+
357
+ Jessica 稍做一番修整後同步到伺服器:
358
+
359
+ $ git commit -am 'small tweak'
360
+ [featureA 774b3ed] small tweak
361
+ 1 files changed, 1 insertions(+), 1 deletions(-)
362
+ $ git push origin featureA
363
+ ...
364
+ To jessica@githost:simplegit.git
365
+ 3300904..774b3ed featureA -> featureA
366
+
367
+ 現在的 Jessica 提交歷史如圖 5-13 所示:
368
+
369
+ Insert 18333fig0513.png
370
+ 圖 5-13. 在特性分支中提交更新後的提交歷史
371
+
372
+ 現在,Jessica,Josie 和 John 通知集成管理員伺服器上的 `featureA` 及 `featureBee` 分支已經準備好,可以併入主線了。在管理員完成集成工作後,主分支上便多出一個新的合併提交(5399e),用 fetch 命令更新到本地後,提交歷史如圖 5-14 所示:
373
+
374
+ Insert 18333fig0514.png
375
+ 圖 5-14. 合併特性分支後的 Jessica 提交歷史
376
+
377
+ 許多開發小組改用 Git 就是因為它允許多個小組間並行工作,而在稍後恰當時機再行合併。通過共用遠端分支的方式,無需干擾整體專案代碼便可以開展工作,因此使用 Git 的小型團隊間協作可以變得非常靈活自由。以上工作流程的時序如圖 5-15 所示:
378
+
379
+ Insert 18333fig0515.png
380
+ 圖 5-15. 團隊間協作工作流程基本時序
381
+
382
+ ### 公開的小型專案 ###
383
+
384
+ 上面說的是私有專案協作,但要給公開專案作貢獻,情況就有些不同了。因為你沒有直接更新主倉庫分支的許可權,得尋求其它方式把工作成果交給專案維護人。下面會介紹兩種方法,第一種使用 git 託管服務商提供的倉庫複製功能,一般稱作 fork,比如 repo.or.cz 和 GitHub 都支援這樣的操作,而且許多項目管理員都希望大家使用這樣的方式。另一種方法是通過電子郵件寄送檔補丁。
385
+
386
+ 但不管哪種方式,起先我們總需要克隆原始倉庫,而後創建特性分支開展工作。基本工作流程如下:
387
+
388
+ $ git clone (url)
389
+ $ cd project
390
+ $ git checkout -b featureA
391
+ $ (work)
392
+ $ git commit
393
+ $ (work)
394
+ $ git commit
395
+
396
+ 你可能想到用 `rebase -i` 將所有更新先變作單個提交,又或者想重新安排提交之間的差異補丁,以方便專案維護者審閱 -- 有關互動式衍合操作的細節見第六章。
397
+
398
+ 在完成了特性分支開發,提交給專案維護者之前,先到原始專案的頁面上點擊“Fork”按鈕,創建一個自己可寫的公共倉庫(譯注:即下面的 url 部分,參照後續的例子,應該是 `git://githost/simplegit.git`)。然後將此倉庫添加為本地的第二個遠端倉庫,姑且稱為 `myfork`:
399
+
400
+ $ git remote add myfork (url)
401
+
402
+ 你需要將本地更新推送到這個倉庫。要是將遠端 master 合併到本地再推回去,還不如把整個特性分支推上去來得乾脆直接。而且,假若項目維護者未採納你的貢獻的話(不管是直接合併還是 cherry pick),都不用回退(rewind)自己的 master 分支。但若維護者合併或 cherry-pick 了你的工作,最後總還可以從他們的更新中同步這些代碼。好吧,現在先把 featureA 分支整個推上去:
403
+
404
+ $ git push myfork featureA
405
+
406
+ 然後通知專案管理員,讓他來抓取你的代碼。通常我們把這件事叫做 pull request。可以直接用 GitHub 等網站提供的 “pull request” 按鈕自動發送請求通知;或手工把 `git request-pull` 命令輸出結果電郵給專案管理員。
407
+
408
+ `request-pull` 命令接受兩個參數,第一個是本地特性分支開始前的原始分支,第二個是請求對方來抓取的 Git 倉庫 URL(譯注:即下面 `myfork` 所指的,自己可寫的公共倉庫)。比如現在Jessica 準備要給 John 發一個 pull requst,她之前在自己的特性分支上提交了兩次更新,並把分支整個推到了伺服器上,所以運行該命令會看到:
409
+
410
+ $ git request-pull origin/master myfork
411
+ The following changes since commit 1edee6b1d61823a2de3b09c160d7080b8d1b3a40:
412
+ John Smith (1):
413
+ added a new function
414
+
415
+ are available in the git repository at:
416
+
417
+ git://githost/simplegit.git featureA
418
+
419
+ Jessica Smith (2):
420
+ add limit to log function
421
+ change log output to 30 from 25
422
+
423
+ lib/simplegit.rb | 10 +++++++++-
424
+ 1 files changed, 9 insertions(+), 1 deletions(-)
425
+
426
+ 輸出的內容可以直接發郵件給管理者,他們就會明白這是從哪次提交開始旁支出去的,該到哪裡去抓取新的代碼,以及新的代碼增加了哪些功能等等。
427
+
428
+ 像這樣隨時保持自己的 `master` 分支和官方 `origin/master` 同步,並將自己的工作限制在特性分支上的做法,既方便又靈活,採納和丟棄都輕而易舉。就算原始主幹發生變化,我們也能重新衍合提供新的補丁。比如現在要開始第二項特性的開發,不要在原來已推送的特性分支上繼續,還是按原始 `master` 開始:
429
+
430
+ $ git checkout -b featureB origin/master
431
+ $ (work)
432
+ $ git commit
433
+ $ git push myfork featureB
434
+ $ (email maintainer)
435
+ $ git fetch origin
436
+
437
+ 現在,A、B 兩個特性分支各不相擾,如同竹筒裡的兩顆豆子,佇列中的兩個補丁,你隨時都可以分別從頭寫過,或者衍合,或者修改,而不用擔心特性代碼的交叉混雜。如圖 5-16 所示:
438
+
439
+ Insert 18333fig0516.png
440
+ 圖 5-16. featureB 以後的提交歷史
441
+
442
+ 假設項目管理員接納了許多別人提交的補丁後,準備要採納你提交的第一個分支,卻發現因為代碼基準不一致,合併工作無法正確乾淨地完成。這就需要你再次衍合到最新的 `origin/master`,解決相關衝突,然後重新提交你的修改:
443
+
444
+ $ git checkout featureA
445
+ $ git rebase origin/master
446
+ $ git push -f myfork featureA
447
+
448
+ 自然,這會重寫提交歷史,如圖 5-17 所示:
449
+
450
+ Insert 18333fig0517.png
451
+ 圖 5-17. featureA 重新衍合後的提交歷史
452
+
453
+ 注意,此時推送分支必須使用 `-f` 選項(譯注:表示 force,不作檢查強制重寫)替換遠端已有的 `featureA` 分支,因為新的 commit 並非原來的後續更新。當然你也可以直接推送到另一個新的分支上去,比如稱作 `featureAv2`。
454
+
455
+ 再考慮另一種情形:管理員看過第二個分支後覺得思路新穎,但想請你改下具體實現。我們只需以當前 `origin/master` 分支為基準,開始一個新的特性分支 `featureBv2`,然後把原來的 `featureB` 的更新拿過來,解決衝突,按要求重新實現部分代碼,然後將此特性分支推送上去:
456
+
457
+ $ git checkout -b featureBv2 origin/master
458
+ $ git merge --no-commit --squash featureB
459
+ $ (change implementation)
460
+ $ git commit
461
+ $ git push myfork featureBv2
462
+
463
+ 這裡的 `--squash` 選項將目標分支上的所有更改全拿來應用到當前分支上,而 `--no-commit` 選項告訴 Git 此時無需自動生成和記錄(合併)提交。這樣,你就可以在原來代碼基礎上,繼續工作,直到最後一起提交。
464
+
465
+ 好了,現在可以請管理員抓取 `featureBv2` 上的最新代碼了,如圖 5-18 所示:
466
+
467
+ Insert 18333fig0518.png
468
+ 圖 5-18. featureBv2 之後的提交歷史
469
+
470
+ ### 公開的大型專案 ###
471
+
472
+ 許多大型專案都會立有一套自己的接受補丁流程,你應該注意下其中細節。但多數專案都允許通過開發者郵寄清單接受補丁,現在我們來看具體例子。
473
+
474
+ 整個工作流程類似上面的情形:為每個補丁創建獨立的特性分支,而不同之處在於如何提交這些補丁。不需要創建自己可寫的公共倉庫,也不用將自己的更新推送到自己的伺服器,你只需將每次提交的差異內容以電子郵件的方式依次發送到郵寄清單中即可。
475
+
476
+ $ git checkout -b topicA
477
+ $ (work)
478
+ $ git commit
479
+ $ (work)
480
+ $ git commit
481
+
482
+ 如此一番後,有了兩個提交要發到郵寄清單。我們可以用 `git format-patch` 命令來生成 mbox 格式的檔然後作為附件發送。每個提交都會封裝為一個 `.patch` 尾碼的 mbox 檔,但其中只包含一封郵件,郵件標題就是提交消息(譯注:額外有首碼,看例子),郵件內容包含補丁正文和 Git 版本號。這種方式的妙處在於接受補丁時仍可保留原來的提交消息,請看接下來的例子:
483
+
484
+ $ git format-patch -M origin/master
485
+ 0001-add-limit-to-log-function.patch
486
+ 0002-changed-log-output-to-30-from-25.patch
487
+
488
+ `format-patch` 命令依次創建補丁文件,並輸出檔案名。上面的 `-M` 選項允許 Git 檢查是否有對檔重命名的提交。我們來看看補丁檔的內容:
489
+
490
+ $ cat 0001-add-limit-to-log-function.patch
491
+ From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001
492
+ From: Jessica Smith <jessica@example.com>
493
+ Date: Sun, 6 Apr 2008 10:17:23 -0700
494
+ Subject: [PATCH 1/2] add limit to log function
495
+
496
+ Limit log functionality to the first 20
497
+
498
+ ---
499
+ lib/simplegit.rb | 2 +-
500
+ 1 files changed, 1 insertions(+), 1 deletions(-)
501
+
502
+ diff --git a/lib/simplegit.rb b/lib/simplegit.rb
503
+ index 76f47bc..f9815f1 100644
504
+ --- a/lib/simplegit.rb
505
+ +++ b/lib/simplegit.rb
506
+ @@ -14,7 +14,7 @@ class SimpleGit
507
+ end
508
+
509
+ def log(treeish = 'master')
510
+ - command("git log #{treeish}")
511
+ + command("git log -n 20 #{treeish}")
512
+ end
513
+
514
+ def ls_tree(treeish = 'master')
515
+ --
516
+ 1.6.2.rc1.20.g8c5b.dirty
517
+
518
+ 如果有額外資訊需要補充,但又不想放在提交消息中說明,可以編輯這些補丁檔,在第一個 `---` 行之前添加說明,但不要修改下面的補丁正文,比如例子中的 `Limit log functionality to the first 20` 部分。這樣,其它開發者能閱讀,但在採納補丁時不會將此合併進來。
519
+
520
+ 你可以用郵件用戶端軟體發送這些補丁檔,也可以直接在命令列發送。有些所謂智慧的郵件用戶端軟體會自作主張幫你調整格式,所以粘貼補丁到郵件正文時,有可能會丟失分行符號和若干空格。Git 提供了一個通過 IMAP 發送補丁檔的工具。接下來我會演示如何通過 Gmail 的 IMAP 伺服器發送。另外,在 Git 原始程式碼中有個 `Documentation/SubmittingPatches` 檔,可以仔細讀讀,看看其它郵件程式的相關導引。
521
+
522
+ 首先在 `~/.gitconfig` 檔中配置 imap 項。每個選項都可用 `git config` 命令分別設置,當然直接編輯檔添加以下內容更便捷:
523
+
524
+ [imap]
525
+ folder = "[Gmail]/Drafts"
526
+ host = imaps://imap.gmail.com
527
+ user = user@gmail.com
528
+ pass = p4ssw0rd
529
+ port = 993
530
+ sslverify = false
531
+
532
+ 如果你的 IMAP 伺服器沒有啟用 SSL,就無需配置最後那兩行,並且 host 應該以 `imap://` 開頭而不再是有 `s` 的 `imaps://`。
533
+ 保存設定檔後,就能用 `git send-email` 命令把補丁作為郵件依次發送到指定的 IMAP 伺服器上的資料夾中(譯注:這裡就是 Gmail 的 `[Gmail]/Drafts` 資料夾。但如果你的語言設置不是英文,此處的資料夾 Drafts 字樣會變為對應的語言。):
534
+
535
+ $ cat *.patch |git imap-send
536
+ Resolving imap.gmail.com... ok
537
+ Connecting to [74.125.142.109]:993... ok
538
+ Logging in...
539
+ sending 2 messages
540
+ 100% (2/2) done
541
+
542
+ At this point, you should be able to go to your Drafts folder, change the To field to the mailing list you’re sending the patch to, possibly CC the maintainer or person responsible for that section, and send it off.
543
+
544
+ You can also send the patches through an SMTP server. As before, you can set each value separately with a series of `git config` commands, or you can add them manually in the sendemail section in your `~/.gitconfig` file:
545
+
546
+ [sendemail]
547
+ smtpencryption = tls
548
+ smtpserver = smtp.gmail.com
549
+ smtpuser = user@gmail.com
550
+ smtpserverport = 587
551
+
552
+ After this is done, you can use `git send-email` to send your patches:
553
+
554
+ $ git send-email *.patch
555
+ 0001-added-limit-to-log-function.patch
556
+ 0002-changed-log-output-to-30-from-25.patch
557
+ Who should the emails appear to be from? [Jessica Smith <jessica@example.com>]
558
+ Emails will be sent from: Jessica Smith <jessica@example.com>
559
+ Who should the emails be sent to? jessica@example.com
560
+ Message-ID to be used as In-Reply-To for the first email? y
561
+
562
+ 接下來,Git 會根據每個補丁依次輸出類似下面的日誌:
563
+
564
+ (mbox) Adding cc: Jessica Smith <jessica@example.com> from
565
+ \line 'From: Jessica Smith <jessica@example.com>'
566
+ OK. Log says:
567
+ Sendmail: /usr/sbin/sendmail -i jessica@example.com
568
+ From: Jessica Smith <jessica@example.com>
569
+ To: jessica@example.com
570
+ Subject: [PATCH 1/2] added limit to log function
571
+ Date: Sat, 30 May 2009 13:29:15 -0700
572
+ Message-Id: <1243715356-61726-1-git-send-email-jessica@example.com>
573
+ X-Mailer: git-send-email 1.6.2.rc1.20.g8c5b.dirty
574
+ In-Reply-To: <y>
575
+ References: <y>
576
+
577
+ Result: OK
578
+
579
+ ### 小結 ###
580
+
581
+ 本節主要介紹了常見 Git 專案協作的工作流程,還有一些説明處理這些工作的命令和工具。接下來我們要看看如何維護 Git 項目,並成為一個合格的項目管理員,或是集成經理。
582
+
583
+ ## 專案的管理 ##
584
+
585
+ 既然是相互協作,在貢獻代碼的同時,也免不了要維護管理自己的專案。像是怎麼處理別人用 `format-patch` 生成的補丁,或是集成遠端倉庫上某個分支上的變化等等。但無論是管理代碼倉庫,還是幫忙審核收到的補丁,都需要同貢獻者約定某種長期可持續的工作方式。
586
+
587
+ ### 使用特性分支進行工作 ###
588
+
589
+ 如果想要集成新的代碼進來,最好局限在特性分支上做。臨時的特性分支可以讓你隨意嘗試,進退自如。比如碰上無法正常工作的補丁,可以先擱在那邊,直到有時間仔細核查修復為止。創建的分支可以用相關的主題關鍵字命名,比如 `ruby_client` 或者其它類似的描述性詞語,以幫助將來回憶。Git 專案本身還時常把分支名稱分置於不同命名空間下,比如 `sc/ruby_client` 就說明這是 `sc` 這個人貢獻的。
590
+ 現在從當前主幹分支為基礎,新建臨時分支:
591
+
592
+ $ git branch sc/ruby_client master
593
+
594
+ 另外,如果你希望立即轉到分支上去工作,可以用 `checkout -b`:
595
+
596
+ $ git checkout -b sc/ruby_client master
597
+
598
+ 好了,現在已經準備妥當,可以試著將別人貢獻的代碼合併進來了。之後評估一下有沒有問題,最後再決定是不是真的要併入主幹。
599
+
600
+ ### 採納來自郵件的補丁 ###
601
+
602
+ 如果收到一個通過電郵發來的補丁,你應該先把它應用到特性分支上進行評估。有兩種應用補丁的方法:`git apply` 或者 `git am`。
603
+
604
+ #### 使用 apply 命令應用補丁 ####
605
+
606
+ 如果收到的補丁文件是用 `git diff` 或由其它 Unix 的 `diff` 命令生成,就該用 `git apply` 命令來應用補丁。假設補丁文件存在 `/tmp/patch-ruby-client.patch`,可以這樣運行:
607
+
608
+ $ git apply /tmp/patch-ruby-client.patch
609
+
610
+ 這會修改當前工作目錄下的檔,效果基本與運行 `patch -p1` 打補丁一樣,但它更為嚴格,且不會出現混亂。如果是 `git diff` 格式描述的補丁,此命令還會相應地添加,刪除,重命名檔。當然,普通的 `patch` 命令是不會這麼做的。另外請注意,`git apply` 是一個事務性操作的命令,也就是說,要麼所有補丁都打上去,要麼全部放棄。所以不會出現 `patch` 命令那樣,一部分檔打上了補丁而另一部分卻沒有,這樣一種不上不下的修訂狀態。所以總的來說,`git apply` 要比 `patch` 嚴謹許多。因為僅僅是更新當前的檔,所以此命令不會自動生成提交物件,你得手工緩存相應檔的更新狀態並執行提交命令。
611
+
612
+ 在實際打補丁之前,可以先用 `git apply --check` 查看補丁是否能夠乾淨順利地應用到當前分支中:
613
+
614
+ $ git apply --check 0001-seeing-if-this-helps-the-gem.patch
615
+ error: patch failed: ticgit.gemspec:1
616
+ error: ticgit.gemspec: patch does not apply
617
+
618
+ 如果沒有任何輸出,表示我們可以順利採納該補丁。如果有問題,除了報告錯誤資訊之外,該命令還會返回一個非零的狀態,所以在 shell 腳本裡可用於檢測狀態。
619
+
620
+ #### 使用 am 命令應用補丁 ####
621
+
622
+ 如果貢獻者也用 Git,且擅於製作 `format-patch` 補丁,那你的合併工作將會非常輕鬆。因為這些補丁中除了檔內容差異外,還包含了作者資訊和提交消息。所以請鼓勵貢獻者用 `format-patch` 生成補丁。對於傳統的 `diff` 命令生成的補丁,則只能用 `git apply` 處理。
623
+
624
+ 對於 `format-patch` 製作的新式補丁,應當使用 `git am` 命令。從技術上來說,`git am` 能夠讀取 mbox 格式的檔。這是種簡單的純文字檔,可以包含多封電郵,格式上用 From 加空格以及隨便什麼輔助資訊所組成的行作為分隔行,以區分每封郵件,就像這樣:
625
+
626
+ From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001
627
+ From: Jessica Smith <jessica@example.com>
628
+ Date: Sun, 6 Apr 2008 10:17:23 -0700
629
+ Subject: [PATCH 1/2] add limit to log function
630
+
631
+ Limit log functionality to the first 20
632
+
633
+ 這是 `format-patch` 命令輸出的開頭幾行,也是一個有效的 mbox 檔案格式。如果有人用 `git send-email` 給你發了一個補丁,你可以將此郵件下載到本地,然後運行 `git am` 命令來應用這個補丁。如果你的郵件用戶端能將多封電郵匯出為 mbox 格式的檔,就可以用 `git am` 一次性應用所有匯出的補丁。
634
+
635
+ 如果貢獻者將 `format-patch` 生成的補丁檔上傳到類似 Request Ticket 一樣的任務處理系統,那麼可以先下載到本地,繼而使用 `git am` 應用該補丁:
636
+
637
+ $ git am 0001-limit-log-function.patch
638
+ Applying: add limit to log function
639
+
640
+ 你會看到它被乾淨地應用到本地分支,並自動創建了新的提交物件。作者資訊取自郵件頭 `From` 和 `Date`,提交消息則取自 `Subject` 以及正文中補丁之前的內容。來看具體實例,採納之前展示的那個 mbox 電郵補丁後,最新的提交對象為:
641
+
642
+ $ git log --pretty=fuller -1
643
+ commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0
644
+ Author: Jessica Smith <jessica@example.com>
645
+ AuthorDate: Sun Apr 6 10:17:23 2008 -0700
646
+ Commit: Scott Chacon <schacon@gmail.com>
647
+ CommitDate: Thu Apr 9 09:19:06 2009 -0700
648
+
649
+ add limit to log function
650
+
651
+ Limit log functionality to the first 20
652
+
653
+ `Commit` 部分顯示的是採納補丁的人,以及採納的時間。而 `Author` 部分則顯示的是原作者,以及創建補丁的時間。
654
+
655
+ 有時,我們也會遇到打不上補丁的情況。這多半是因為主幹分支和補丁的基礎分支相差太遠,但也可能是因為某些依賴補丁還未應用。這種情況下,`git am` 會報錯並詢問該怎麼做:
656
+
657
+ $ git am 0001-seeing-if-this-helps-the-gem.patch
658
+ Applying: seeing if this helps the gem
659
+ error: patch failed: ticgit.gemspec:1
660
+ error: ticgit.gemspec: patch does not apply
661
+ Patch failed at 0001.
662
+ When you have resolved this problem run "git am --resolved".
663
+ If you would prefer to skip this patch, instead run "git am --skip".
664
+ To restore the original branch and stop patching run "git am --abort".
665
+
666
+ Git 會在有衝突的檔裡加入衝突解決標記,這同合併或衍合操作一樣。解決的辦法也一樣,先編輯檔消除衝突,然後暫存檔,最後運行 `git am --resolved` 提交修正結果:
667
+
668
+ $ (fix the file)
669
+ $ git add ticgit.gemspec
670
+ $ git am --resolved
671
+ Applying: seeing if this helps the gem
672
+
673
+ 如果想讓 Git 更智慧地處理衝突,可以用 `-3` 選項進行三方合併。如果當前分支未包含該補丁的基礎代碼或其祖先,那麼三方合併就會失敗,所以該選項預設為關閉狀態。一般來說,如果該補丁是基於某個公開的提交製作而成的話,你總是可以通過同步來獲取這個共同祖先,所以用三方合併選項可以解決很多麻煩:
674
+
675
+ $ git am -3 0001-seeing-if-this-helps-the-gem.patch
676
+ Applying: seeing if this helps the gem
677
+ error: patch failed: ticgit.gemspec:1
678
+ error: ticgit.gemspec: patch does not apply
679
+ Using index info to reconstruct a base tree...
680
+ Falling back to patching base and 3-way merge...
681
+ No changes -- Patch already applied.
682
+
683
+ 像上面的例子,對於打過的補丁我又再打一遍,自然會產生衝突,但因為加上了 `-3` 選項,所以它很聰明地告訴我,無需更新,原有的補丁已經應用。
684
+
685
+ 對於一次應用多個補丁時所用的 mbox 格式檔,可以用 `am` 命令的交互模式選項 `-i`,這樣就會在打每個補丁前停住,詢問該如何操作:
686
+
687
+ $ git am -3 -i mbox
688
+ Commit Body is:
689
+ --------------------------
690
+ seeing if this helps the gem
691
+ --------------------------
692
+ Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all
693
+
694
+ 在多個補丁要打的情況下,這是個非常好的辦法,一方面可以預覽下補丁內容,同時也可以有選擇性的接納或跳過某些補丁。
695
+
696
+ 打完所有補丁後,如果測試下來新特性可以正常工作,那就可以安心地將當前特性分支合併到長期分支中去了。
697
+
698
+ ### 檢出遠端分支 ###
699
+
700
+ 如果貢獻者有自己的 Git 倉庫,並將修改推送到此倉庫中,那麼當你拿到倉庫的訪問位址和對應分支的名稱後,就可以加為遠端分支,然後在本地進行合併。
701
+
702
+ 比如,Jessica 發來一封郵件,說在她代碼庫中的 `ruby-client` 分支上已經實現了某個非常棒的新功能,希望我們能幫忙測試一下。我們可以先把她的倉庫加為遠端倉庫,然後抓取資料,完了再將她所說的分支檢出到本地來測試:
703
+
704
+ $ git remote add jessica git://github.com/jessica/myproject.git
705
+ $ git fetch jessica
706
+ $ git checkout -b rubyclient jessica/ruby-client
707
+
708
+ 若是不久她又發來郵件,說還有個很棒的功能實現在另一分支上,那我們只需重新抓取下最新資料,然後檢出那個分支到本地就可以了,無需重複設置遠端倉庫。
709
+
710
+ 這種做法便於同別人保持長期的合作關係。但前提是要求貢獻者有自己的伺服器,而我們也需要為每個人建一個遠端分支。有些貢獻者提交代碼補丁並不是很頻繁,所以通過郵件接收補丁效率會更高。同時我們自己也不會希望建上百來個分支,卻只從每個分支取一兩個補丁。但若是用腳本程式來管理,或直接使用代碼倉庫託管服務,就可以簡化此過程。當然,選擇何種方式取決於你和貢獻者的喜好。
711
+
712
+ 使用遠端分支的另外一個好處是能夠得到提交歷史。不管代碼合併是不是會有問題,至少我們知道該分支的歷史分叉點,所以默認會從共同祖先開始自動進行三方合併,無需 `-3` 選項,也不用像打補丁那樣祈禱存在共同的基準點。
713
+
714
+ 如果只是臨時合作,只需用 `git pull` 命令抓取遠端倉庫上的資料,合併到本地臨時分支就可以了。一次性的抓取動作自然不會把該倉庫位址加為遠程倉庫。
715
+
716
+ $ git pull git://github.com/onetimeguy/project.git
717
+ From git://github.com/onetimeguy/project
718
+ * branch HEAD -> FETCH_HEAD
719
+ Merge made by recursive.
720
+
721
+ ### 決斷代碼取捨 ###
722
+
723
+ 現在特性分支上已合併好了貢獻者的代碼,是時候決斷取捨了。本節將回顧一些之前學過的命令,以看清將要合併到主幹的是哪些代碼,從而理解它們到底做了些什麼,是否真的要併入。
724
+
725
+ 一般我們會先看下,特性分支上都有哪些新增的提交。比如在 `contrib` 特性分支上打了兩個補丁,僅查看這兩個補丁的提交資訊,可以用 `--not` 選項指定要遮罩的分支 `master`,這樣就會剔除重複的提交歷史:
726
+
727
+ $ git log contrib --not master
728
+ commit 5b6235bd297351589efc4d73316f0a68d484f118
729
+ Author: Scott Chacon <schacon@gmail.com>
730
+ Date: Fri Oct 24 09:53:59 2008 -0700
731
+
732
+ seeing if this helps the gem
733
+
734
+ commit 7482e0d16d04bea79d0dba8988cc78df655f16a0
735
+ Author: Scott Chacon <schacon@gmail.com>
736
+ Date: Mon Oct 22 19:38:36 2008 -0700
737
+
738
+ updated the gemspec to hopefully work better
739
+
740
+ 還可以查看每次提交的具體修改。請牢記,在 `git log` 後加 `-p` 選項將展示每次提交的內容差異。
741
+
742
+ 如果想看當前分支同其他分支合併時的完整內容差異,有個小竅門:
743
+
744
+ $ git diff master
745
+
746
+ 雖然能得到差異內容,但請記住,結果有可能和我們的預期不同。一旦主幹 `master` 在特性分支創建之後有所修改,那麼通過 `diff` 命令來比較的,是最新主幹上的提交快照。顯然,這不是我們所要的。比方在 `master` 分支中某個檔裡添了一行,然後運行上面的命令,簡單的比較最新快照所得到的結論只能是,特性分支中刪除了這一行。
747
+
748
+ 這個很好理解:如果 `master` 是特性分支的直接祖先,不會產生任何問題;如果它們的提交歷史在不同的分叉上,那麼產生的內容差異,看起來就像是增加了特性分支上的新代碼,同時刪除了 `master` 分支上的新代碼。
749
+
750
+ 實際上我們真正想要看的,是新加入到特性分支的代碼,也就是合併時會併入主幹的代碼。所以,準確地講,我們應該比較特性分支和它同 `master` 分支的共同祖先之間的差異。
751
+
752
+ 我們可以手工定位它們的共同祖先,然後與之比較:
753
+
754
+ $ git merge-base contrib master
755
+ 36c7dba2c95e6bbb78dfa822519ecfec6e1ca649
756
+ $ git diff 36c7db
757
+
758
+ 但這麼做很麻煩,所以 Git 提供了便捷的 `...` 語法。對於 `diff` 命令,可以把 `...` 加在原始分支(擁有共同祖先)和當前分支之間:
759
+
760
+ $ git diff master...contrib
761
+
762
+ 現在看到的,就是實際將要引入的新代碼。這是一個非常有用的命令,應該牢記。
763
+
764
+ ### 代碼集成 ###
765
+
766
+ 一旦特性分支準備停當,接下來的問題就是如何集成到更靠近主線的分支中。此外還要考慮維護專案的總體步驟是什麼。雖然有很多選擇,不過我們這裡只介紹其中一部分。
767
+
768
+ #### 合併流程 ####
769
+
770
+ 一般最簡單的情形,是在 `master` 分支中維護穩定代碼,然後在特性分支上開發新功能,或是審核測試別人貢獻的代碼,接著將它併入主幹,最後刪除這個特性分支,如此反復。來看示例,假設當前代碼庫中有兩個分支,分別為 `ruby_client` 和 `php_client`,如圖 5-19 所示。然後先把 `ruby_client` 合併進主幹,再合併 `php_client`,最後的提交歷史如圖 5-20 所示。
771
+
772
+ Insert 18333fig0519.png
773
+ 圖 5-19. 多個特性分支
774
+
775
+ Insert 18333fig0520.png
776
+ 圖 5-20. 合併特性分支之後
777
+
778
+ 這是最簡單的流程,所以在處理大一些的項目時可能會有問題。
779
+
780
+ 對於大型專案,至少需要維護兩個長期分支 `master` 和 `develop`。新代碼(圖 5-21 中的 `ruby_client`)將首先併入 `develop` 分支(圖 5-22 中的 `C8`),經過一個階段,確認 `develop` 中的代碼已穩定到可發行時,再將 `master` 分支快進到穩定點(圖 5-23 中的 `C8`)。而平時這兩個分支都會被推送到公開的代碼庫。
781
+
782
+ Insert 18333fig0521.png
783
+ 圖 5-21. 特性分支合併前
784
+
785
+ Insert 18333fig0522.png
786
+ 圖 5-22. 特性分支合併後
787
+
788
+ Insert 18333fig0523.png
789
+ 圖 5-23. 特性分支發佈後
790
+
791
+ 這樣,在人們克隆倉庫時就有兩種選擇:既可檢出最新穩定版本,確保正常使用;也能檢出開發版本,試用最前沿的新特性。
792
+ 你也可以擴展這個概念,先將所有新代碼合併到臨時特性分支,等到該分支穩定下來並通過測試後,再併入 `develop` 分支。然後,讓時間檢驗一切,如果這些代碼確實可以正常工作相當長一段時間,那就有理由相信它已經足夠穩定,可以放心併入主幹分支發佈。
793
+
794
+ #### 大專案的合併流程 ####
795
+
796
+ Git 專案本身有四個長期分支:用於發佈的 `master` 分支、用於合併基本穩定特性的 `next` 分支、用於合併仍需改進特性的 `pu` 分支(pu 是 proposed updates 的縮寫),以及用於除錯維護的 `maint` 分支(maint 取自 maintenance)。維護者可以按照之前介紹的方法,將貢獻者的代碼引入為不同的特性分支(如圖 5-24 所示),然後測試評估,看哪些特性能穩定工作,哪些還需改進。穩定的特性可以併入 `next` 分支,然後再推送到公共倉庫,以供其他人試用。
797
+
798
+ Insert 18333fig0524.png
799
+ 圖 5-24. 管理複雜的並行貢獻
800
+
801
+ 仍需改進的特性可以先併入 `pu` 分支。直到它們完全穩定後再併入 `master`。同時一併檢查下 `next` 分支,將足夠穩定的特性也併入 `master`。所以一般來說,`master` 始終是在快進,`next` 偶爾做下衍合,而 `pu` 則是頻繁衍合,如圖 5-25 所示:
802
+
803
+ Insert 18333fig0525.png
804
+ 圖 5-25. 將特性併入長期分支
805
+
806
+ 併入 `master` 後的特性分支,已經無需保留分支索引,放心刪除好了。Git 專案還有一個 `maint` 分支,它是以最近一次發行版本為基礎分化而來的,用於維護除錯補丁。所以克隆 Git 項目倉庫後會得到這四個分支,通過檢出不同分支可以瞭解各自進展,或是試用前沿特性,或是貢獻代碼。而維護者則通過管理這些分支,逐步有序地併入協力廠商貢獻。
807
+
808
+ #### 衍合與挑揀(cherry-pick)的流程 ####
809
+
810
+ 一些維護者更喜歡衍合或者挑揀貢獻者的代碼,而不是簡單的合併,因為這樣能夠保持線性的提交歷史。如果你完成了一個特性的開發,並決定將它引入到主幹代碼中,你可以轉到那個特性分支然後執行衍合命令,好在你的主幹分支上(也可能是`develop`分支之類的)重新提交這些修改。如果這些代碼工作得很好,你就可以快進`master`分支,得到一個線性的提交歷史。
811
+
812
+ 另一個引入代碼的方法是挑揀。挑揀類似於針對某次特定提交的衍合。它首先提取某次提交的補丁,然後試著應用在當前分支上。如果某個特性分支上有多個commits,但你只想引入其中之一就可以使用這種方法。也可能僅僅是因為你喜歡用挑揀,討厭衍合。假設你有一個類似圖 5-26 的工程。
813
+
814
+ Insert 18333fig0526.png
815
+ 圖 5-26. 挑揀(cherry-pick)之前的歷史
816
+
817
+ 如果你希望拉取`e43a6`到你的主幹分支,可以這樣:
818
+
819
+ $ git cherry-pick e43a6fd3e94888d76779ad79fb568ed180e5fcdf
820
+ Finished one cherry-pick.
821
+ [master]: created a0a41a9: "More friendly message when locking the index fails."
822
+ 3 files changed, 17 insertions(+), 3 deletions(-)
823
+
824
+ 這將會引入`e43a6`的代碼,但是會得到不同的SHA-1值,因為應用日期不同。現在你的歷史看起來像圖 5-27.
825
+
826
+ Insert 18333fig0527.png
827
+ 圖 5-27. 挑揀(cherry-pick)之後的歷史
828
+
829
+ 現在,你可以刪除這個特性分支並丟棄你不想引入的那些commit。
830
+
831
+ ### 給發行版本簽名 ###
832
+
833
+ 你可以刪除上次發佈的版本並重新打標籤,也可以像第二章所說的那樣建立一個新的標籤。如果你決定以維護者的身份給發行版本簽名,應該這樣做:
834
+
835
+ $ git tag -s v1.5 -m 'my signed 1.5 tag'
836
+ You need a passphrase to unlock the secret key for
837
+ user: "Scott Chacon <schacon@gmail.com>"
838
+ 1024-bit DSA key, ID F721C45A, created 2009-02-09
839
+
840
+ 完成簽名之後,如何分發PGP公開金鑰(public key)是個問題。(譯者注:分發公開金鑰是為了驗證標籤)。還好,Git的設計者想到了解決辦法:可以把key(既公開金鑰)作為blob變數寫入Git庫,然後把它的內容直接寫在標籤裡。`gpg --list-keys`命令可以顯示出你所擁有的key:
841
+
842
+ $ gpg --list-keys
843
+ /Users/schacon/.gnupg/pubring.gpg
844
+ ---------------------------------
845
+ pub 1024D/F721C45A 2009-02-09 [expires: 2010-02-09]
846
+ uid Scott Chacon <schacon@gmail.com>
847
+ sub 2048g/45D02282 2009-02-09 [expires: 2010-02-09]
848
+
849
+ 然後,匯出key的內容並經由管道符傳遞給`git hash-object`,之後鑰匙會以blob類型寫入Git中,最後返回這個blob量的SHA-1值:
850
+
851
+ $ gpg -a --export F721C45A | git hash-object -w --stdin
852
+ 659ef797d181633c87ec71ac3f9ba29fe5775b92
853
+
854
+ 現在你的Git已經包含了這個key的內容了,可以通過不同的SHA-1值指定不同的key來創建標籤。
855
+
856
+ $ git tag -a maintainer-pgp-pub 659ef797d181633c87ec71ac3f9ba29fe5775b92
857
+
858
+ 在運行`git push --tags`命令之後,`maintainer-pgp-pub`標籤就會公佈給所有人。如果有人想要校驗標籤,他可以使用如下命令導入你的key:
859
+
860
+ $ git show maintainer-pgp-pub | gpg --import
861
+
862
+ 人們可以用這個key校驗你簽名的所有標籤。另外,你也可以在標籤資訊裡寫入一個操作嚮導,用戶只需要運行`git show <tag>`查看標籤資訊,然後按照你的嚮導就能完成校驗。
863
+
864
+ ### 生成內部版本號 ###
865
+
866
+ 因為Git不會為每次提交自動附加類似'v123'的遞增序列,所以如果你想要得到一個便於理解的提交號可以運行`git describe`命令。Git將會返回一個字串,由三部分組成:最近一次標定的版本號,加上自那次標定之後的提交次數,再加上一段所描述的提交的SHA-1值:
867
+
868
+ $ git describe master
869
+ v1.6.2-rc1-20-g8c5b85c
870
+
871
+ 這個字串可以作為快照的名字,方便人們理解。如果你的Git是你自己下載源碼然後編譯安裝的,你會發現`git --version`命令的輸出和這個字串差不多。如果在一個剛剛打完標籤的提交上運行`describe`命令,只會得到這次標定的版本號,而沒有後面兩項資訊。
872
+
873
+ `git describe`命令只適用於有標注的標籤(通過`-a`或者`-s`選項創建的標籤),所以發行版本的標籤都應該是帶有標注的,以保證`git describe`能夠正確的執行。你也可以把這個字串作為`checkout`或者`show`命令的目標,因為他們最終都依賴於一個簡短的SHA-1值,當然如果這個SHA-1值失效他們也跟著失效。最近Linux內核為了保證SHA-1值的唯一性,將位數由8位擴展到10位,這就導致擴展之前的`git describe`輸出完全失效了。
874
+
875
+ ### 準備發佈 ###
876
+
877
+ 現在可以發佈一個新的版本了。首先要將代碼的壓縮包歸檔,方便那些可憐的還沒有使用Git的人們。可以使用`git archive`:
878
+
879
+ $ git archive master --prefix='project/' | gzip > `git describe master`.tar.gz
880
+ $ ls *.tar.gz
881
+ v1.6.2-rc1-20-g8c5b85c.tar.gz
882
+
883
+ 這個壓縮包解壓出來的是一個資料夾,裡面是你項目的最新代碼快照。你也可以用類似的方法建立一個zip壓縮包,在`git archive`加上`--format=zip`選項:
884
+
885
+ $ git archive master --prefix='project/' --format=zip > `git describe master`.zip
886
+
887
+ 現在你有了一個tar.gz壓縮包和一個zip壓縮包,可以把他們上傳到你網站上或者用e-mail發給別人。
888
+
889
+ ### 製作簡報 ###
890
+
891
+ 是時候通知郵寄清單裡的朋友們來檢驗你的成果了。使用`git shortlog`命令可以方便快捷的製作一份修改日誌(changelog),告訴大家上次發佈之後又增加了哪些特性和修復了哪些bug。實際上這個命令能夠統計給定範圍內的所有提交;假如你上一次發佈的版本是v1.0.1,下面的命令將給出自從上次發佈之後的所有提交的簡介:
892
+
893
+ $ git shortlog --no-merges master --not v1.0.1
894
+ Chris Wanstrath (8):
895
+ Add support for annotated tags to Grit::Tag
896
+ Add packed-refs annotated tag support.
897
+ Add Grit::Commit#to_patch
898
+ Update version and History.txt
899
+ Remove stray `puts`
900
+ Make ls_tree ignore nils
901
+
902
+ Tom Preston-Werner (4):
903
+ fix dates in history
904
+ dynamic version method
905
+ Version bump to 1.0.2
906
+ Regenerated gemspec for version 1.0.2
907
+
908
+ 這就是自從v1.0.1版本以來的所有提交的簡介,內容按照作者分組,以便你能快速的發e-mail給他們。
909
+
910
+ ## 小結 ##
911
+
912
+ 你學會了如何使用Git為專案做貢獻,也學會了如何使用Git維護你的專案。恭喜!你已經成為一名高效的開發者。在下一章你將學到更強大的工具來處理更加複雜的問題,之後你會變成一位Git大師。