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,1139 @@
1
+ # Git 工具 #
2
+
3
+ 現在,你已經學習了管理或者維護 Git 倉庫,實現代碼控制所需的大多數日常命令和工作流程。你已經完成了跟蹤和提交檔案的基本任務,並且發揮了暫存區(staging area)和羽量級的特性分支及合併的威力。
4
+
5
+ 接下來你將領略到一些 Git 可以實現的非常強大的功能,這些功能你可能並不會在日常操作中使用,但在某些時候你也許會需要。
6
+
7
+ ## 選擇修訂版本 ##
8
+
9
+ Git 允許你通過幾種方法來指明特定的或者一定範圍內的提交。瞭解它們並不是必需的,但是瞭解一下總沒壞處。
10
+
11
+ ### 單個修訂版本 ###
12
+
13
+ 顯然你可以使用給出的 SHA-1 值來指明一次提交,不過也有更加人性化的方法來做同樣的事。本節概述了指明單個提交的諸多方法。
14
+
15
+ ### 簡短的 SHA ###
16
+
17
+ Git 很聰明,它能夠通過你提供的前幾個字元來識別你想要的那次提交,只要你提供的那部分 SHA-1 不短於四個字元,並且沒有歧義——也就是說,當前倉庫中只有一個物件以這段 SHA-1 開頭。
18
+
19
+ 例如,想要查看一次指定的提交,假設你執行 `git log` 命令並找到你增加了功能的那次提交:
20
+
21
+ $ git log
22
+ commit 734713bc047d87bf7eac9674765ae793478c50d3
23
+ Author: Scott Chacon <schacon@gmail.com>
24
+ Date: Fri Jan 2 18:32:33 2009 -0800
25
+
26
+ fixed refs handling, added gc auto, updated tests
27
+
28
+ commit d921970aadf03b3cf0e71becdaab3147ba71cdef
29
+ Merge: 1c002dd... 35cfb2b...
30
+ Author: Scott Chacon <schacon@gmail.com>
31
+ Date: Thu Dec 11 15:08:43 2008 -0800
32
+
33
+ Merge commit 'phedders/rdocs'
34
+
35
+ commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b
36
+ Author: Scott Chacon <schacon@gmail.com>
37
+ Date: Thu Dec 11 14:58:32 2008 -0800
38
+
39
+ added some blame and merge stuff
40
+
41
+ 假設是 `1c002dd`.... 。如果你想 `git show` 這次提交,下面命令的作用是相同的(假設簡短的版本沒有歧義):
42
+
43
+ $ git show 1c002dd4b536e7479fe34593e72e6c6c1819e53b
44
+ $ git show 1c002dd4b536e7479f
45
+ $ git show 1c002d
46
+
47
+ Git 可以為你的 SHA-1 值生成出簡短且唯一的縮寫。如果你傳遞 `--abbrev-commit` 給 `git log` 命令,輸出結果裡就會使用簡短且唯一的值;它預設使用七個字元來表示,不過必要時為了避免 SHA-1 的歧義,會增加字元數:
48
+
49
+ $ git log --abbrev-commit --pretty=oneline
50
+ ca82a6d changed the version number
51
+ 085bb3b removed unnecessary test code
52
+ a11bef0 first commit
53
+
54
+ 通常在一個專案中,使用八到十個字元來避免 SHA-1 歧義已經足夠了。最大的 Git 專案之一,Linux 內核,目前也只需要最長 40 個字元中的 12 個字元來保持唯一性。
55
+
56
+ ### 關於 SHA-1 的簡短說明 ###
57
+
58
+ 許多人可能會擔心一個問題:在隨機的偶然情況下,在他們的倉庫裡會出現兩個具有相同 SHA-1 值的物件。那會怎麼樣呢?
59
+
60
+ 如果你真的向倉庫裡提交了一個跟之前的某個物件具有相同 SHA-1 值的物件,Git 將會發現之前的那個物件已經存在在 Git 資料庫中,並認為它已經被寫入了。如果什麼時候你想再次檢出那個物件時,你會總是得到先前的那個物件的資料。
61
+
62
+ 不過,你應該瞭解到,這種情況發生的概率是多麼微小。SHA-1 摘要長度是 20 位元組,也就是 160 位元。為了保證有 50% 的概率出現一次衝突,需要 2^80 個隨機雜湊的物件(計算衝突機率的公式是 p = (n(n-1)/2) * (1/2^160))。2^80 是 1.2 x 10^24,也就是一億億億,那是地球上沙粒總數的 1200 倍。
63
+
64
+ 現在舉例說一下怎樣才能產生一次 SHA-1 衝突。如果地球上 65 億的人類都在程式設計,每人每秒都在產生相當於整個 Linux 內核歷史(一百萬個 Git 物件)的代碼,並將之提交到一個巨大的 Git 倉庫裡面,那將花費 5 年的時間才會產生足夠的物件,使其擁有 50% 的概率產生一次 SHA-1 物件衝突。這要比你程式設計團隊的成員同一個晚上在互不相干的意外中被狼襲擊並殺死的機率還要小。
65
+
66
+ ### 分支引用 (Branch References) ###
67
+
68
+ 指明一次提交的最直接的方法是有一個指向它的分支引用。這樣,你就可以在任何需要一個提交物件或者 SHA-1 值的 Git 命令中使用該分支名稱了。如果你想要顯示一個分支的最後一次提交的物件,例如假設 topic1 分支指向 ca82a6d,那麼下面的命令是相等的:
69
+
70
+ $ git show ca82a6dff817ec66f44342007202690a93763949
71
+ $ git show topic1
72
+
73
+ 如果你想知道某個分支指向哪個特定的 SHA,或者想看任何一個例子中被簡寫的 SHA-1,你可以使用一個叫做 `rev-parse` 的 Git plumbing 工具。在第 9 章你可以看到關於 plumbing 工具的更多信息;簡單來說,`rev-parse` 是為了底層操作而不是日常操作設計的。不過,有時你想看 Git 現在到底處於什麼狀態時,它可能會很有用。現在,你可以對你的分支執行 `rev-parse`:
74
+
75
+ $ git rev-parse topic1
76
+ ca82a6dff817ec66f44342007202690a93763949
77
+
78
+ ### 引用日誌(RefLog)裡的簡稱 ###
79
+
80
+ 在你工作的同時,Git 在後臺的工作之一就是保存一份引用日誌(reflog)——一份記錄最近幾個月你的 HEAD 和分支引用的日誌。
81
+
82
+ 你可以使用 `git reflog` 來查看引用日誌:
83
+
84
+ $ git reflog
85
+ 734713b HEAD@{0}: commit: fixed refs handling, added gc auto, updated
86
+ d921970 HEAD@{1}: merge phedders/rdocs: Merge made by recursive.
87
+ 1c002dd HEAD@{2}: commit: added some blame and merge stuff
88
+ 1c36188 HEAD@{3}: rebase -i (squash): updating HEAD
89
+ 95df984 HEAD@{4}: commit: # This is a combination of two commits.
90
+ 1c36188 HEAD@{5}: rebase -i (squash): updating HEAD
91
+ 7e05da5 HEAD@{6}: rebase -i (pick): updating HEAD
92
+
93
+ 每次你的分支頂端因為某些原因被修改時,Git 就會為你將資訊保存在這個臨時歷史記錄裡面。你也可以使用這份資料來指明更早的分支。如果你想查看倉庫中 HEAD 在五次前的值,你可以使用引用日誌的輸出中的 @{n} 引用:
94
+
95
+ $ git show HEAD@{5}
96
+
97
+ 你也可以使用這個語法來查看一定時間前分支指向哪裡。例如,想看你的 `master` 分支昨天在哪,你可以輸入
98
+
99
+ $ git show master@{yesterday}
100
+
101
+ 它就會顯示昨天分支的頂端在哪。這項技術只對還在你引用日誌裡的資料有用,所以不能用來查看比幾個月前還早的提交。
102
+
103
+ 想要看類似於 `git log` 輸出格式的引用日誌資訊,你可以執行 `git log -g`:
104
+
105
+ $ git log -g master
106
+ commit 734713bc047d87bf7eac9674765ae793478c50d3
107
+ Reflog: master@{0} (Scott Chacon <schacon@gmail.com>)
108
+ Reflog message: commit: fixed refs handling, added gc auto, updated
109
+ Author: Scott Chacon <schacon@gmail.com>
110
+ Date: Fri Jan 2 18:32:33 2009 -0800
111
+
112
+ fixed refs handling, added gc auto, updated tests
113
+
114
+ commit d921970aadf03b3cf0e71becdaab3147ba71cdef
115
+ Reflog: master@{1} (Scott Chacon <schacon@gmail.com>)
116
+ Reflog message: merge phedders/rdocs: Merge made by recursive.
117
+ Author: Scott Chacon <schacon@gmail.com>
118
+ Date: Thu Dec 11 15:08:43 2008 -0800
119
+
120
+ Merge commit 'phedders/rdocs'
121
+
122
+ 需要注意的是,日誌引用資訊只存在於本地——這是一個你在倉庫裡做過什麼的日誌。這些引用不會和其他人的倉庫拷貝裡的相同;當你新 clone 一個倉庫的時候,引用日誌是空的,因為你在倉庫裡還沒有操作。只有你克隆了一個專案至少兩個月,`git show HEAD@{2.months.ago}` 才會有用——如果你是五分鐘前克隆的倉庫,將不會有結果回傳。
123
+
124
+ ### 祖先引用 (Ancestry References) ###
125
+
126
+ 另一種指明某次提交的常用方法是通過它的祖先。如果你在引用最後加上一個 `^`,Git 將其理解為此次提交的父提交。 假設你的專案歷史是這樣的:
127
+
128
+ $ git log --pretty=format:'%h %s' --graph
129
+ * 734713b fixed refs handling, added gc auto, updated tests
130
+ * d921970 Merge commit 'phedders/rdocs'
131
+ |\
132
+ | * 35cfb2b Some rdoc changes
133
+ * | 1c002dd added some blame and merge stuff
134
+ |/
135
+ * 1c36188 ignore *.gem
136
+ * 9b29157 add open3_detach to gemspec file list
137
+
138
+ 那麼,想看上一次提交,你可以使用 `HEAD^`,意思是「HEAD 的父提交」:
139
+
140
+ $ git show HEAD^
141
+ commit d921970aadf03b3cf0e71becdaab3147ba71cdef
142
+ Merge: 1c002dd... 35cfb2b...
143
+ Author: Scott Chacon <schacon@gmail.com>
144
+ Date: Thu Dec 11 15:08:43 2008 -0800
145
+
146
+ Merge commit 'phedders/rdocs'
147
+
148
+ 你也可以在 `^` 後添加一個數字——例如,`d921970^2` 意思是「d921970 的第二父提交」。這種語法只在合併提交時有用,因為合併提交可能有多個父提交。第一父提交是你合併時所在分支,而第二父提交是你所合併進來的分支:
149
+
150
+ $ git show d921970^
151
+ commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b
152
+ Author: Scott Chacon <schacon@gmail.com>
153
+ Date: Thu Dec 11 14:58:32 2008 -0800
154
+
155
+ added some blame and merge stuff
156
+
157
+ $ git show d921970^2
158
+ commit 35cfb2b795a55793d7cc56a6cc2060b4bb732548
159
+ Author: Paul Hedderly <paul+git@mjr.org>
160
+ Date: Wed Dec 10 22:22:03 2008 +0000
161
+
162
+ Some rdoc changes
163
+
164
+ 另外一個指明祖先提交的方法是 `~`。這也是指向第一父提交,所以 `HEAD~` 和 `HEAD^` 是相等的。當你指定數字的時候就明顯不一樣了。`HEAD~2` 是指「第一父提交的第一父提交」,也就是「祖父提交」——它會根據你指定的次數檢索第一父提交。例如,在上面列出的歷史記錄裡面,`HEAD~3` 會是
165
+
166
+ $ git show HEAD~3
167
+ commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d
168
+ Author: Tom Preston-Werner <tom@mojombo.com>
169
+ Date: Fri Nov 7 13:47:59 2008 -0500
170
+
171
+ ignore *.gem
172
+
173
+ 也可以寫成 HEAD^^^,同樣是第一父提交的第一父提交的第一父提交:
174
+
175
+ $ git show HEAD^^^
176
+ commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d
177
+ Author: Tom Preston-Werner <tom@mojombo.com>
178
+ Date: Fri Nov 7 13:47:59 2008 -0500
179
+
180
+ ignore *.gem
181
+
182
+ 你也可以混合使用這些語法——你可以通過 `HEAD~3^2` 指明先前引用的第二父提交(假設它是一個合併提交),依此類推。
183
+
184
+ ### 提交範圍 ###
185
+
186
+ 現在你已經可以指明單次的提交,讓我們來看看怎樣指明一定範圍的提交。這在你管理分支的時候尤顯重要——如果你有很多分支,你可以指明範圍來圈定一些問題的答案,比如:「這個分支上我有哪些工作還沒合併到主分支的?」
187
+
188
+ #### 雙點 ####
189
+
190
+ 最常用的指明範圍的方法是雙點的語法。這種語法主要是讓 Git 區分出可從一個分支中獲得而不能從另一個分支中獲得的提交。例如,假設你有類似於圖 6-1 的提交歷史。
191
+
192
+ Insert 18333fig0601.png
193
+ Figure 6-1. 範圍選擇的提交歷史實例
194
+
195
+ 你想要查看你的試驗分支(experiment)上哪些沒有被提交到主分支,那麼你就可以使用 `master..experiment` 來讓 Git 顯示這些提交的日誌——這句話的意思是「所有可從 experiment 分支中獲得而不能從 master 分支中獲得的提交」。為了使例子簡單明瞭,我使用了圖示中提交物件的字母,來代替它們在實際的日誌輸出裏的顯示順序:
196
+
197
+ $ git log master..experiment
198
+ D
199
+ C
200
+
201
+ 另一方面,如果你想看相反的——所有在 `master` 而不在 `experiment` 中的分支——你可以交換分支的名字。experiment..master 顯示所有可在 master 獲得而在 experiment 中不能獲得的提交:
202
+
203
+ $ git log experiment..master
204
+ F
205
+ E
206
+
207
+ 這在你想將 `experiment` 分支維持在最新狀態,並預覽你將合併的提交的時候特別有用。這個語法的另一種常見用途是查看你將把什麼推送到遠端:
208
+
209
+ $ git log origin/master..HEAD
210
+
211
+ 這條命令顯示任何在你當前分支上而不在遠端 `origin` 上的 `master` 分支上的提交。如果你執行 `git push` 並且你的當前分支正在追蹤 `origin/master`,被 `git log origin/master..HEAD` 列出的提交就是將被傳輸到伺服器上的提交。
212
+ 你也可以省略語法中的一邊讓 Git 來假定它是 HEAD。例如,輸入 git log origin/master.. 將得到和上面的例子一樣的結果—— Git 使用 HEAD 來代替不存在的一邊。
213
+
214
+ #### 多點 ####
215
+
216
+ 雙點語法就像速記一樣有用;但是你也許會想針對兩個以上的分支來指明修訂版本,比如查看哪些提交被包含在某些分支中的一個,但是不在你當前的分支上。Git 允許你在引用前使用 `^` 字元或者 `--not` 指明你不希望提交被包含其中的分支。因此下面三個命令是等同的:
217
+
218
+ $ git log refA..refB
219
+ $ git log ^refA refB
220
+ $ git log refB --not refA
221
+
222
+ 這樣很好,因為它允許你在查詢中指定多於兩個的引用,而這是雙點語法所做不到的。例如,如果你想查找所有從 `refA` 或 `refB` 包含的但是不被 `refC` 包含的提交,你可以輸入下面中的一個
223
+
224
+ $ git log refA refB ^refC
225
+ $ git log refA refB --not refC
226
+
227
+ 這建立了一個非常強大的修訂版本查詢系統,應該可以幫助你了解你的分支裡有些什麼東西。
228
+
229
+ #### 三點 ####
230
+
231
+ 最後一種主要的範圍選擇語法是三點語法,這個可以指定被兩個引用中的一個包含但又不被兩者同時包含的分支。回過頭來看一下圖6-1裡所列的提交歷史的例子。
232
+ 如果你想查看 `master` 或者 `experiment` 中包含的但不是兩者共有的引用,你可以執行
233
+
234
+ $ git log master...experiment
235
+ F
236
+ E
237
+ D
238
+ C
239
+
240
+ 這個再次給出你普通的 `log` 輸出但是只顯示那四次提交的資訊,按照傳統的提交日期排列。
241
+
242
+ 這種情形下,`log` 命令的一個常用參數是 `--left-right`,它會顯示每個提交到底處於哪一側的分支。這使得資料更加有用。
243
+
244
+ $ git log --left-right master...experiment
245
+ < F
246
+ < E
247
+ > D
248
+ > C
249
+
250
+ 有了以上工具,讓 Git 知道你要察看哪些提交就容易得多了。
251
+
252
+ ## 互動式暫存 ##
253
+
254
+ Git 提供了很多腳本來輔助某些命令列任務。這裡,你將看到一些互動式命令,它們幫助你方便地構建只包含特定組合和部分檔案的提交。在你修改了一大批檔案然後決定將這些變更分佈在幾個有聚焦的提交而不是單個又大又亂的提交時,這些工具非常有用。用這種方法,你可以確保你的提交在邏輯上劃分為相應的變更集合,以便於和你一起工作的開發者審閱。
255
+ 如果你執行 `git add` 時加上 `-i` 或者 `--interactive` 選項,Git 就進入了一個互動式的 shell 模式,顯示一些類似於下面的資訊:
256
+
257
+ $ git add -i
258
+ staged unstaged path
259
+ 1: unchanged +0/-1 TODO
260
+ 2: unchanged +1/-1 index.html
261
+ 3: unchanged +5/-1 lib/simplegit.rb
262
+
263
+ *** Commands ***
264
+ 1: status 2: update 3: revert 4: add untracked
265
+ 5: patch 6: diff 7: quit 8: help
266
+ What now>
267
+
268
+ 你會看到這個命令以一個完全不同的視圖顯示了你的暫存區——主要是你通過 `git status` 得到的那些資訊但是稍微簡潔但資訊更加豐富一些。它在左側列出了你暫存的變更,在右側列出了未被暫存的變更。
269
+
270
+ 在這之後是一個命令區。這裡你可以做很多事情,包括暫存檔案(stage)、撤回檔案(unstage)、暫存部分檔案、加入未被追蹤的文件、查看暫存文件的差別。
271
+
272
+ ### 暫存和撤回檔案 ###
273
+
274
+ 如果你在 `What now>` 的提示後輸入 `2` 或者 `u`,這個腳本會提示你那些檔你想要暫存:
275
+
276
+ What now> 2
277
+ staged unstaged path
278
+ 1: unchanged +0/-1 TODO
279
+ 2: unchanged +1/-1 index.html
280
+ 3: unchanged +5/-1 lib/simplegit.rb
281
+ Update>>
282
+
283
+ 如果想暫存 TODO 和 index.html,你可以輸入相應的編號:
284
+
285
+ Update>> 1,2
286
+ staged unstaged path
287
+ * 1: unchanged +0/-1 TODO
288
+ * 2: unchanged +1/-1 index.html
289
+ 3: unchanged +5/-1 lib/simplegit.rb
290
+ Update>>
291
+
292
+ 每個檔旁邊的 `*` 表示選中的檔將被暫存。如果你在 update>> 提示後直接敲入 Enter,Git會替你把所有選中的內容暫存:
293
+
294
+ Update>>
295
+ updated 2 paths
296
+
297
+ *** Commands ***
298
+ 1: status 2: update 3: revert 4: add untracked
299
+ 5: patch 6: diff 7: quit 8: help
300
+ What now> 1
301
+ staged unstaged path
302
+ 1: +0/-1 nothing TODO
303
+ 2: +1/-1 nothing index.html
304
+ 3: unchanged +5/-1 lib/simplegit.rb
305
+
306
+ 現在你可以看到 TODO 和 index.html 檔被暫存了,同時 simplegit.rb 檔仍然未被暫存。如果這時你想要撤回 TODO 檔,就使用 `3` 或者 `r`(代表 revert,恢復)選項:
307
+
308
+ *** Commands ***
309
+ 1: status 2: update 3: revert 4: add untracked
310
+ 5: patch 6: diff 7: quit 8: help
311
+ What now> 3
312
+ staged unstaged path
313
+ 1: +0/-1 nothing TODO
314
+ 2: +1/-1 nothing index.html
315
+ 3: unchanged +5/-1 lib/simplegit.rb
316
+ Revert>> 1
317
+ staged unstaged path
318
+ * 1: +0/-1 nothing TODO
319
+ 2: +1/-1 nothing index.html
320
+ 3: unchanged +5/-1 lib/simplegit.rb
321
+ Revert>> [enter]
322
+ reverted one path
323
+
324
+ 再次查看 Git 的狀態,你會看到你已經撤回了 TODO 檔
325
+
326
+ *** Commands ***
327
+ 1: status 2: update 3: revert 4: add untracked
328
+ 5: patch 6: diff 7: quit 8: help
329
+ What now> 1
330
+ staged unstaged path
331
+ 1: unchanged +0/-1 TODO
332
+ 2: +1/-1 nothing index.html
333
+ 3: unchanged +5/-1 lib/simplegit.rb
334
+
335
+ 要查看你暫存內容的差異,你可以使用 `6` 或者 `d`(表示diff)命令。它會顯示你暫存檔的列表,你可以選擇其中的幾個,顯示其被暫存的差異。這跟你在命令列下指定 `git diff --cached` 非常相似:
336
+
337
+ *** Commands ***
338
+ 1: status 2: update 3: revert 4: add untracked
339
+ 5: patch 6: diff 7: quit 8: help
340
+ What now> 6
341
+ staged unstaged path
342
+ 1: +1/-1 nothing index.html
343
+ Review diff>> 1
344
+ diff --git a/index.html b/index.html
345
+ index 4d07108..4335f49 100644
346
+ --- a/index.html
347
+ +++ b/index.html
348
+ @@ -16,7 +16,7 @@ Date Finder
349
+
350
+ <p id="out">...</p>
351
+
352
+ -<div id="footer">contact : support@github.com</div>
353
+ +<div id="footer">contact : email.support@github.com</div>
354
+
355
+ <script type="text/javascript">
356
+
357
+ 通過這些基本命令,你可以使用互動式增加模式更加方便地處理暫存區。
358
+
359
+ ### 暫存補丁 (Staging Patches) ###
360
+
361
+ 只讓 Git 將檔案的某些部分暫存,而忽略其他部份也是有可能的。例如,你對 simplegit.rb 檔作了兩處修改但是只想暫存其中一個而忽略另一個,在 Git 中實現這一點非常容易。在互動式的提示符下,輸入 `5` 或者 `p`(表示 patch,補丁)。Git 會詢問哪些檔你希望部分暫存;然後對於被選中檔案的每一節,他會逐個顯示檔案的差異區塊並詢問你是否希望暫存他們:
362
+
363
+ diff --git a/lib/simplegit.rb b/lib/simplegit.rb
364
+ index dd5ecc4..57399e0 100644
365
+ --- a/lib/simplegit.rb
366
+ +++ b/lib/simplegit.rb
367
+ @@ -22,7 +22,7 @@ class SimpleGit
368
+ end
369
+
370
+ def log(treeish = 'master')
371
+ - command("git log -n 25 #{treeish}")
372
+ + command("git log -n 30 #{treeish}")
373
+ end
374
+
375
+ def blame(path)
376
+ Stage this hunk [y,n,a,d,/,j,J,g,e,?]?
377
+
378
+ 此處你有很多選擇。輸入 `?` 可以顯示清單:
379
+
380
+ Stage this hunk [y,n,a,d,/,j,J,g,e,?]? ?
381
+ y - stage this hunk
382
+ n - do not stage this hunk
383
+ a - stage this and all the remaining hunks in the file
384
+ d - do not stage this hunk nor any of the remaining hunks in the file
385
+ g - select a hunk to go to
386
+ / - search for a hunk matching the given regex
387
+ j - leave this hunk undecided, see next undecided hunk
388
+ J - leave this hunk undecided, see next hunk
389
+ k - leave this hunk undecided, see previous undecided hunk
390
+ K - leave this hunk undecided, see previous hunk
391
+ s - split the current hunk into smaller hunks
392
+ e - manually edit the current hunk
393
+ ? - print help
394
+
395
+ 如果你想暫存各個區塊,通常你會輸入 `y` 或者 `n`,但是暫存特定檔裡的全部區塊或者暫時跳過對一個區塊的處理同樣也很有用。如果你暫存了檔案的一個部分而保留另外一個部分不被暫存,你的狀態輸出看起來會是這樣:
396
+
397
+ What now> 1
398
+ staged unstaged path
399
+ 1: unchanged +0/-1 TODO
400
+ 2: +1/-1 nothing index.html
401
+ 3: +1/-1 +4/-0 lib/simplegit.rb
402
+
403
+ simplegit.rb 的狀態非常有意思。它顯示有幾行被暫存了,有幾行沒有。你部分地暫存了這個檔。這時候,你可以退出互動式腳本然後執行 `git commit` 來提交部分暫存的檔。
404
+
405
+ 最後,你也可以不通過互動式增加的模式來實現檔案部分暫存——你可以在命令列下使用 `git add -p` 或者 `git add --patch` 來啟動同樣的腳本。
406
+
407
+ ## 儲藏 (Stashing) ##
408
+
409
+ 經常有這樣的事情發生,當你正在進行專案中某一部分的工作,裡面的東西處於一個比較雜亂的狀態,而你想轉到其他分支上進行一些工作。問題是,你不想只為了待會要回到這個工作點,就把做到一半的工作進行提交。解決這個問題的辦法就是 `git stash` 命令。
410
+
411
+ 「儲藏」可以獲取你工作目錄的 dirty state——也就是你修改過的被追蹤檔和暫存的變更——並將它保存到一個未完成變更的堆疊(stack)中,隨時可以重新應用。
412
+
413
+ ### 儲藏你的工作 ###
414
+
415
+ 為了演示這一功能,你可以進入你的專案,在一些檔上進行工作,有可能還暫存其中一個變更。如果你執行 `git status`,你可以看到你的 dirty state:
416
+
417
+ $ git status
418
+ # On branch master
419
+ # Changes to be committed:
420
+ # (use "git reset HEAD <file>..." to unstage)
421
+ #
422
+ # modified: index.html
423
+ #
424
+ # Changes not staged for commit:
425
+ # (use "git add <file>..." to update what will be committed)
426
+ #
427
+ # modified: lib/simplegit.rb
428
+ #
429
+
430
+ 現在你想切換分支,但是你還不想提交你正在進行中的工作;所以你儲藏這些變更。為了往堆疊推送一個新的儲藏,執行 `git stash`:
431
+
432
+ $ git stash
433
+ Saved working directory and index state \
434
+ "WIP on master: 049d078 added the index file"
435
+ HEAD is now at 049d078 added the index file
436
+ (To restore them type "git stash apply")
437
+
438
+ 你的工作目錄就乾淨了:
439
+
440
+ $ git status
441
+ # On branch master
442
+ nothing to commit, working directory clean
443
+
444
+ 這時,你可以方便地切換到其他分支工作;你的變更都保存在堆疊上。要查看現有的儲藏,你可以使用 `git stash list`:
445
+
446
+ $ git stash list
447
+ stash@{0}: WIP on master: 049d078 added the index file
448
+ stash@{1}: WIP on master: c264051 Revert "added file_size"
449
+ stash@{2}: WIP on master: 21d80a5 added number to log
450
+
451
+ 在這個案例中,之前已經進行了兩次儲藏,所以你可以取得三個不同的儲藏。你可以重新應用你剛剛的儲藏,所採用的命令就是原本 stash 命令輸出的輔助訊息裡提示的:`git stash apply`。如果你想應用較舊的儲藏,你可以通過名字指定它,像這樣:`git stash apply stash@{2}`。如果你不指明,Git 預設使用最近的儲藏並嘗試應用它:
452
+
453
+ $ git stash apply
454
+ # On branch master
455
+ # Changes not staged for commit:
456
+ # (use "git add <file>..." to update what will be committed)
457
+ #
458
+ # modified: index.html
459
+ # modified: lib/simplegit.rb
460
+ #
461
+
462
+ 你可以看到 Git 重新修改了你所儲藏的那些當時尚未提交的檔。在這個案例裡,你嘗試應用儲藏的工作目錄是乾淨的,並且屬於同一分支;但是一個乾淨的工作目錄和應用到相同的分支上並不是應用儲藏的必要條件。你可以在其中一個分支上保留一份儲藏,隨後切換到另外一個分支,再重新應用這些變更。在工作目錄裡包含已修改、未提交的檔時,你也可以應用儲藏——Git 會給出合併衝突,如果有任何變更無法乾淨地被應用。
463
+
464
+ 對檔案的變更被重新應用,但是被暫存的檔沒有重新被暫存。想那樣的話,你必須在執行 `git stash apply` 命令時帶上一個 `--index` 的選項來重新應用被暫存的變更。如果你是這麼做的,你應該已經回到你原來的位置:
465
+
466
+ $ git stash apply --index
467
+ # On branch master
468
+ # Changes to be committed:
469
+ # (use "git reset HEAD <file>..." to unstage)
470
+ #
471
+ # modified: index.html
472
+ #
473
+ # Changes not staged for commit:
474
+ # (use "git add <file>..." to update what will be committed)
475
+ #
476
+ # modified: lib/simplegit.rb
477
+ #
478
+
479
+ apply 選項只嘗試應用儲藏的工作——儲藏的內容仍然在堆疊上。要移除它,你可以執行 `git stash drop`,加上你希望移除的儲藏的名字:
480
+
481
+ $ git stash list
482
+ stash@{0}: WIP on master: 049d078 added the index file
483
+ stash@{1}: WIP on master: c264051 Revert "added file_size"
484
+ stash@{2}: WIP on master: 21d80a5 added number to log
485
+ $ git stash drop stash@{0}
486
+ Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43)
487
+
488
+ 你也可以執行 `git stash pop` 來重新應用儲藏,同時立刻將其從堆疊中移走。
489
+
490
+ ### 取消儲藏 (Un-applying a Stash) ###
491
+
492
+ 在某些使用情境下,你可能想要應用儲藏的變更,做一些工作,然後又要把來自原儲藏的變更取消。Git 並未提供類似 `stash unapply` 的命令,但是達成相同效果是可能的,只要取得該儲藏關連的補丁然後反向應用它就行了:
493
+
494
+ $ git stash show -p stash@{0} | git apply -R
495
+
496
+ 同樣的,如果你沒有指定某個儲藏,Git 會預設為最近的儲藏:
497
+
498
+ $ git stash show -p | git apply -R
499
+
500
+ 你可能會想要新建一個別名,在你的 git 增加一個 `stash-unapply` 命令,這樣更有效率。例如:
501
+
502
+ $ git config --global alias.stash-unapply '!git stash show -p | git apply -R'
503
+ $ git stash apply
504
+ $ #... work work work
505
+ $ git stash-unapply
506
+
507
+ ### 從儲藏中創建分支 ###
508
+
509
+ 如果你儲藏了一些工作,暫時不去理會,然後繼續在你儲藏工作的分支上工作,你在重新應用工作時可能會碰到一些問題。如果嘗試應用的變更是針對一個你那之後修改過的檔,你會碰到一個合併衝突並且必須去化解它。如果你想用更方便的方法來重新檢驗你儲藏的變更,你可以執行 `git stash branch`,這會創建一個新的分支,檢出你儲藏工作時所處的提交,重新應用你的工作,如果成功,將會丟棄儲藏。
510
+
511
+ $ git stash branch testchanges
512
+ Switched to a new branch "testchanges"
513
+ # On branch testchanges
514
+ # Changes to be committed:
515
+ # (use "git reset HEAD <file>..." to unstage)
516
+ #
517
+ # modified: index.html
518
+ #
519
+ # Changes not staged for commit:
520
+ # (use "git add <file>..." to update what will be committed)
521
+ #
522
+ # modified: lib/simplegit.rb
523
+ #
524
+ Dropped refs/stash@{0} (f0dfc4d5dc332d1cee34a634182e168c4efc3359)
525
+
526
+ 這是一個很棒的捷徑來恢復儲藏的工作然後在新的分支上繼續當時的工作。
527
+
528
+ ## 重寫歷史 ##
529
+
530
+ 很多時候,在 Git 上工作的時候,你也許會由於某種原因想要修訂你的提交歷史。Git 的一個卓越之處就是它允許你在最後可能的時刻再作決定。你可以在你即將提交暫存區時決定什麼檔歸入哪一次提交,你可以使用 stash 命令來決定你暫時擱置的工作,你可以重寫已經發生的提交以使它們看起來是另外一種樣子。這個包括改變提交的次序、改變說明或者修改提交中包含的檔,將提交歸併(squash)、拆分或者完全刪除——這一切在你尚未開始將你的工作和別人共用前都是可以的。
531
+
532
+ 在這一節中,你會學到如何完成這些很有用的任務,使得你的提交歷史在你將其共用給別人之前變成你想要的樣子。
533
+
534
+ ### 改變最後一次提交 ###
535
+
536
+ 改變最後一次提交也許是最常見的重寫歷史的行為。對於你的最近一次提交,你經常想做兩件基本事情:改變提交說明,或者經由增加、改變、移除檔案而改變你剛記錄的快照。
537
+
538
+ 如果你只想修改最近一次提交說明,這非常簡單:
539
+
540
+ $ git commit --amend
541
+
542
+ 這會把你帶入文字編輯器,裡面包含了你最近一次提交的說明訊息,供你修改。當你保存並退出編輯器,這個編輯器會寫入一個新的提交,裡面包含了那個說明,並且讓它成為你的新的最後提交。
543
+
544
+ 如果你完成提交後又想修改被提交的快照,增加或者修改其中的檔案,可能因為你最初提交時,忘了添加一個新建的檔,這個過程基本上一樣。你通過修改檔案然後對其執行 `git add` 或對一個已被記錄的檔執行 `git rm`,隨後的 `git commit --amend` 會獲取你當前的暫存區並將它作為新提交對應的快照。
545
+
546
+ 使用這項技術的時候你必須小心,因為修正會改變提交的SHA-1值。這個很像是一次非常小的 rebase——不要在你最近一次提交被推送後還去修正它。
547
+
548
+ ### 修改多個提交訊息 ###
549
+
550
+ 要修改歷史中更早的提交,你必須採用更複雜的工具。Git 沒有一個修改歷史的工具,但是你可以使用 rebase 工具來衍合一系列的提交到它們原來所在的 HEAD 上而不是移到新的上。依靠這個互動式的 rebase 工具,你就可以停留在每一次提交後,如果你想修改或改變說明、增加檔案或做任何事情。在 `git rebase` 增加 `-i` 選項可以對話模式執行 rebase。你必須告訴 rebase 命令要衍合到哪次提交,來指明你想要重寫的提交要回溯到多遠。
551
+
552
+ 例如,你想修改最近三次的提交說明,或者其中任意一次,你必須給 `git rebase -i` 提供一個參數,指明你想要修改的提交的父提交,例如 `HEAD~2^` 或者 `HEAD~3`。可能記住 `~3` 更加容易,因為你想修改最近三次提交;但是請記住你事實上所指的是四次提交之前,即你想修改的提交的父提交。
553
+
554
+ $ git rebase -i HEAD~3
555
+
556
+ 再次提醒這是一個衍合命令—— `HEAD~3..HEAD` 範圍內的每一次提交都會被重寫,無論你是否修改說明。不要涵蓋你已經推送到中心伺服器的提交——這麼做會使其他開發者產生混亂,因為你提供了同樣變更的不同版本。
557
+
558
+ 執行這個命令會在你的文字編輯器提供一個提交列表,看起來像下面這樣:
559
+
560
+ pick f7f3f6d changed my name a bit
561
+ pick 310154e updated README formatting and added blame
562
+ pick a5f4a0d added cat-file
563
+
564
+ # Rebase 710f0f8..a5f4a0d onto 710f0f8
565
+ #
566
+ # Commands:
567
+ # p, pick = use commit
568
+ # r, reword = use commit, but edit the commit message
569
+ # e, edit = use commit, but stop for amending
570
+ # s, squash = use commit, but meld into previous commit
571
+ # f, fixup = like "squash", but discard this commit's log message
572
+ # x, exec = run command (the rest of the line) using shell
573
+ #
574
+ # These lines can be re-ordered; they are executed from top to bottom.
575
+ #
576
+ # If you remove a line here THAT COMMIT WILL BE LOST.
577
+ #
578
+ # However, if you remove everything, the rebase will be aborted.
579
+ #
580
+ # Note that empty commits are commented out
581
+
582
+ 很重要的一點是你得注意這些提交的順序與你通常通過 `log` 命令看到的是相反的。如果你執行 `log`,你會看到下面這樣的結果:
583
+
584
+ $ git log --pretty=format:"%h %s" HEAD~3..HEAD
585
+ a5f4a0d added cat-file
586
+ 310154e updated README formatting and added blame
587
+ f7f3f6d changed my name a bit
588
+
589
+ 請注意這裡的順序是相反的。互動式的 rebase 給了你一個即將執行的腳本。它會從你在命令列上指明的提交開始(`HEAD~3`)然後自上至下重播每次提交裡引入的變更。它將最早的列在頂上而不是最近的,因為這是第一個需要重播的。
590
+
591
+ 你需要修改這個腳本來讓它停留在你想修改的變更上。要做到這一點,你只要將你想修改的每一次提交前面的 pick 改為 edit。例如,只想修改第三次提交說明的話,你就像下面這樣修改文件:
592
+
593
+ edit f7f3f6d changed my name a bit
594
+ pick 310154e updated README formatting and added blame
595
+ pick a5f4a0d added cat-file
596
+
597
+ 當你存檔並退出編輯器,Git 會倒回至列表中的最後一次提交,然後把你送到命令列中,同時顯示以下資訊:
598
+
599
+ $ git rebase -i HEAD~3
600
+ Stopped at 7482e0d... updated the gemspec to hopefully work better
601
+ You can amend the commit now, with
602
+
603
+ git commit --amend
604
+
605
+ Once you’re satisfied with your changes, run
606
+
607
+ git rebase --continue
608
+
609
+ 這些指示很明確地告訴了你該幹什麼。輸入
610
+
611
+ $ git commit --amend
612
+
613
+ 修改提交說明,退出編輯器。然後,執行
614
+
615
+ $ git rebase --continue
616
+
617
+ 這個命令會自動應用其他兩次提交,你就完成任務了。如果你將更多行的 pick 改為 edit ,你就能對你想修改的提交重複這些步驟。Git 每次都會停下,讓你修正提交,完成後繼續執行。
618
+
619
+ ### 重排(Reordering) 提交 ###
620
+
621
+ 你也可以使用互動式的衍合來徹底重排或刪除提交。如果你想刪除 ”added cat-file” 這個提交並且修改其他兩次提交引入的順序,你將 rebase 腳本從這個
622
+
623
+ pick f7f3f6d changed my name a bit
624
+ pick 310154e updated README formatting and added blame
625
+ pick a5f4a0d added cat-file
626
+
627
+ 改為這個:
628
+
629
+ pick 310154e updated README formatting and added blame
630
+ pick f7f3f6d changed my name a bit
631
+
632
+ 當你存檔並退出編輯器,Git 將分支倒回至這些提交的父提交,應用 `310154e`,然後 `f7f3f6d`,接著停止。你有效地修改了這些提交的順序並且徹底刪除了 ”added cat-file” 這次提交。
633
+
634
+ ### 擠壓(Squashing) 提交 ###
635
+
636
+ 互動式的衍合工具還可以將一系列提交擠壓為單一提交。腳本在 rebase 的資訊裡放了一些有用的指示:
637
+
638
+ #
639
+ # Commands:
640
+ # p, pick = use commit
641
+ # r, reword = use commit, but edit the commit message
642
+ # e, edit = use commit, but stop for amending
643
+ # s, squash = use commit, but meld into previous commit
644
+ # f, fixup = like "squash", but discard this commit's log message
645
+ # x, exec = run command (the rest of the line) using shell
646
+ #
647
+ # These lines can be re-ordered; they are executed from top to bottom.
648
+ #
649
+ # If you remove a line here THAT COMMIT WILL BE LOST.
650
+ #
651
+ # However, if you remove everything, the rebase will be aborted.
652
+ #
653
+ # Note that empty commits are commented out
654
+
655
+ 如果不用 ”pick” 或者 ”edit”,而是指定 ”squash”,Git 會同時應用那個變更和它之前的變更並將提交說明歸併。因此,如果你想將這三個提交合併為單一提交,你可以將腳本修改成這樣:
656
+
657
+ pick f7f3f6d changed my name a bit
658
+ squash 310154e updated README formatting and added blame
659
+ squash a5f4a0d added cat-file
660
+
661
+ 當你儲存並退出編輯器,Git 會應用全部三次變更然後將你送回編輯器來歸併三次提交說明。
662
+
663
+ # This is a combination of 3 commits.
664
+ # The first commit's message is:
665
+ changed my name a bit
666
+
667
+ # This is the 2nd commit message:
668
+
669
+ updated README formatting and added blame
670
+
671
+ # This is the 3rd commit message:
672
+
673
+ added cat-file
674
+
675
+ 當你儲存之後,你就擁有了一個包含前三次提交的全部變更的單一提交。
676
+
677
+ ### 拆分(Splitting) 提交 ###
678
+
679
+ 拆分提交就是撤銷一次提交,然後多次部分地暫存或提交直到結束。例如,假設你想將三次提交中的中間一次拆分。將「updated README formatting and added blame」拆分成兩次提交:第一次為「updated README formatting」,第二次為「added blame」。你可以在 `rebase -i` 腳本中修改你想拆分的提交前的指令為 ”edit”:
680
+
681
+ pick f7f3f6d changed my name a bit
682
+ edit 310154e updated README formatting and added blame
683
+ pick a5f4a0d added cat-file
684
+
685
+ 然後,這個腳本就將你帶入命令列,你重置那次提交,提取被重置的變更,從中創建多次提交。當你儲存並退出編輯器,Git 倒回到列表中第一次提交的父提交,應用第一次提交(`f7f3f6d`),應用第二次提交(`310154e`),然後將你帶到控制台。那裡你可以用 `git reset HEAD^` 對那次提交進行一次混合的重置,這將撤銷那次提交並且將修改的檔從暫存區撤回。此時你可以暫存並提交檔案,直到你擁有多次提交,結束後,執行 `git rebase --continue`。
686
+
687
+ $ git reset HEAD^
688
+ $ git add README
689
+ $ git commit -m 'updated README formatting'
690
+ $ git add lib/simplegit.rb
691
+ $ git commit -m 'added blame'
692
+ $ git rebase --continue
693
+
694
+ Git 在腳本中應用了最後一次提交(`a5f4a0d`),你的歷史看起來就像這樣了:
695
+
696
+ $ git log -4 --pretty=format:"%h %s"
697
+ 1c002dd added cat-file
698
+ 9b29157 added blame
699
+ 35cfb2b updated README formatting
700
+ f3cc40e changed my name a bit
701
+
702
+ 再次提醒,這會修改你列表中的提交的 SHA 值,所以請確保這個列表裡不包含你已經推送到共用倉庫的提交。
703
+
704
+ ### 核彈級選項: filter-branch ###
705
+
706
+ 如果你想用腳本的方式修改大量的提交,還有一個重寫歷史的選項可以用——例如,全域性地修改電子郵寄地址或者將一個檔從所有提交中刪除。這個命令是 `filter-branch`,這會大面積地修改你的歷史,所以你很有可能不該去用它,除非你的專案尚未公開,沒有其他人在你準備修改的提交的基礎上工作。儘管如此,這個可以非常有用。你會學習一些常見用法,借此對它的能力有所認識。
707
+
708
+ #### 從所有提交中刪除一個檔 ####
709
+
710
+ 這個經常發生。有些人不經思考使用 `git add .`,意外地提交了一個巨大的二進位檔案,你想將它從所有地方刪除。也許你不小心提交了一個包含密碼的檔,而你想讓你的專案成為 open source。`filter-branch` 大概會是你用來清理整個歷史的工具。要從整個歷史中刪除一個名叫 password.txt 的檔,你可以在 `filter-branch` 上使用 `--tree-filter` 選項:
711
+
712
+ $ git filter-branch --tree-filter 'rm -f passwords.txt' HEAD
713
+ Rewrite 6b9b3cf04e7c5686a9cb838c3f36a8cb6a0fc2bd (21/21)
714
+ Ref 'refs/heads/master' was rewritten
715
+
716
+ `--tree-filter` 選項會在每次 checkout 專案時先執行指定的命令然後重新提交結果。在這個例子中,你會在所有快照中刪除一個名叫 password.txt 的檔,無論它是否存在。如果你想刪除所有不小心提交上去的編輯器備份檔案,你可以執行類似 `git filter-branch --tree-filter "find * -type f -name '*~' -delete" HEAD` 的命令。
717
+
718
+ 你可以觀察到 Git 重寫目錄樹並且提交,然後將分支指標移到末尾。一個比較好的辦法是在一個測試分支上做這件事,然後在你確定結果真的是你所要的之後,再 hard-reset 你的主分支。要在你所有的分支上運行 `filter-branch` 的話,你可以傳遞一個 `--all` 參數給該命令。
719
+
720
+ #### 將一個子目錄設置為新的根目錄 ####
721
+
722
+ 假設你完成了從另外一個代碼控制系統的導入工作,得到了一些沒有意義的子目錄(trunk, tags 等等)。如果你想讓 trunk 子目錄成為每一次提交的新的專案根目錄,`filter-branch` 也可以幫你做到:
723
+
724
+ $ git filter-branch --subdirectory-filter trunk HEAD
725
+ Rewrite 856f0bf61e41a27326cdae8f09fe708d679f596f (12/12)
726
+ Ref 'refs/heads/master' was rewritten
727
+
728
+ 現在你的專案根目錄就是 trunk 子目錄了。Git 會自動地刪除不對這個子目錄產生影響的提交。
729
+
730
+ #### 全域性地更換電子郵寄地址 ####
731
+
732
+ 另一個常見的案例是你在開始時忘了執行 `git config` 來設置你的姓名和電子郵寄地址,也許你想開源一個專案,把你所有的工作電子郵寄地址修改為個人位址。無論哪種情況你都可以用 `filter-branch` 來更換多次提交裡的電子郵寄地址。你必須小心一些,只改變屬於你的電子郵寄地址,所以你使用 `--commit-filter`:
733
+
734
+ $ git filter-branch --commit-filter '
735
+ if [ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ];
736
+ then
737
+ GIT_AUTHOR_NAME="Scott Chacon";
738
+ GIT_AUTHOR_EMAIL="schacon@example.com";
739
+ git commit-tree "$@";
740
+ else
741
+ git commit-tree "$@";
742
+ fi' HEAD
743
+
744
+ 這樣會巡迴並重寫所有提交使之擁有你的新地址。因為提交裡包含了它們的父提交的 SHA-1 值,這個命令會修改你的歷史中的所有提交,而不僅僅是包含了匹配的電子郵寄地址的那些。
745
+
746
+ ## 使用 Git 做 Debug ##
747
+
748
+ Git 也提供了一些工具來幫助你 debug 專案中遇到的問題。由於 Git 被設計為可應用於幾乎任何類型的專案,這些工具是通用型的,但是在遇到問題時經常可以幫助你找到 bug 在哪裏。
749
+
750
+ ### 檔案標注 (File Annotation) ###
751
+
752
+ 如果你在追查程式碼中的 bug,想要知道這是什麼時候、為什麼被引進來的,檔案標注會是你的最佳工具。它會顯示檔案中對每一行進行修改的最近一次提交。因此,如果你發現自己程式碼中的一個 method 有 bug,你可以用 `git blame` 來標注該檔案,查看那個 method 的每一行分別是由誰在哪一天修改的。下面這個例子使用了 `-L` 選項來限制輸出範圍在第12至22行:
753
+
754
+ $ git blame -L 12,22 simplegit.rb
755
+ ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 12) def show(tree = 'master')
756
+ ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 13) command("git show #{tree}")
757
+ ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 14) end
758
+ ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 15)
759
+ 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 16) def log(tree = 'master')
760
+ 79eaf55d (Scott Chacon 2008-04-06 10:15:08 -0700 17) command("git log #{tree}")
761
+ 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 18) end
762
+ 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 19)
763
+ 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 20) def blame(path)
764
+ 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 21) command("git blame #{path}")
765
+ 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 22) end
766
+
767
+ 請注意第一欄是最後一次修改該行的那次提交的 SHA-1 部份值。接下去的兩欄是從那次提交中取出的值——作者姓名和日期——所以你可以方便地獲知誰在什麼時候修改了這一行。在這後面是行號和檔案內容。請注意 `^4832fe2` 提交的那些行,這些指的是檔案最初提交(original commit)的那些行。那個提交是檔案第一次被加入這個專案時存在的,自那以後未被修改過。這會帶來小小的困惑,因為你已經至少看到了 Git 使用 `^` 來修飾一個提交的 SHA值 的三種不同的意義,但這裡確實就是這個意思。
768
+
769
+ 另一件很酷的事情是,Git 並不會明確地記錄對檔案所做的重命名(rename)動作。它會記錄快照,然後根據實際狀況嘗試找出隱藏在背後的重命名動作。這其中有一個很有意思的特性,就是你可以讓它找出所有的程式碼移動。如果你在 `git blame` 後加上 `-C`,Git 會分析你所標注的檔案,然後嘗試找出其中代碼片段的原始出處,如果它是從其他地方拷貝過來的話。最近,我在對 `GITServerHandler.m` 這個檔案做程式碼重構(code refactoring),將它分解為多個檔案,其中一個是 `GITPackUpload.m`。通過對 `GITPackUpload.m` 執行帶 `-C` 參數的 blame 命令,我可以看到程式碼片段的原始出處:
770
+
771
+ $ git blame -C -L 141,153 GITPackUpload.m
772
+ f344f58d GITServerHandler.m (Scott 2009-01-04 141)
773
+ f344f58d GITServerHandler.m (Scott 2009-01-04 142) - (void) gatherObjectShasFromC
774
+ f344f58d GITServerHandler.m (Scott 2009-01-04 143) {
775
+ 70befddd GITServerHandler.m (Scott 2009-03-22 144) //NSLog(@"GATHER COMMI
776
+ ad11ac80 GITPackUpload.m (Scott 2009-03-24 145)
777
+ ad11ac80 GITPackUpload.m (Scott 2009-03-24 146) NSString *parentSha;
778
+ ad11ac80 GITPackUpload.m (Scott 2009-03-24 147) GITCommit *commit = [g
779
+ ad11ac80 GITPackUpload.m (Scott 2009-03-24 148)
780
+ ad11ac80 GITPackUpload.m (Scott 2009-03-24 149) //NSLog(@"GATHER COMMI
781
+ ad11ac80 GITPackUpload.m (Scott 2009-03-24 150)
782
+ 56ef2caf GITServerHandler.m (Scott 2009-01-05 151) if(commit) {
783
+ 56ef2caf GITServerHandler.m (Scott 2009-01-05 152) [refDict setOb
784
+ 56ef2caf GITServerHandler.m (Scott 2009-01-05 153)
785
+
786
+ 這真的非常有用。通常,你會把你拷貝代碼的那次提交作為原始提交,因為這是你在這個檔中第一次接觸到那幾行。Git可以告訴你編寫那些行的原始提交,即便是在另一個檔裡。
787
+
788
+ ### 二分法查找 (Binary Search) ###
789
+
790
+ 當你知道問題在哪裡的時候,標注檔案會有幫助。如果你不知道,並且自從上次程式碼可用的狀態之後已經經歷了上百次的提交,你可能就要求助於 `git bisect` 命令了。`bisect` 會在你的提交歷史中進行二分查找,來儘快地確定哪一次提交引入了錯誤。
791
+
792
+ 例如你剛剛推送了一個代碼發佈版本到產品環境中,得到一些在你開發環境中沒有發生的錯誤報告,而你對代碼為什麼會表現成那樣百思不得其解。你回到你的代碼中,還好你可以重現那個錯誤,但是找不到問題在哪裡。你可以對代碼執行 `bisect` 來尋找。首先你執行 `git bisect start` 啟動,然後你用 `git bisect bad` 來告訴系統當前的提交已經有問題了。然後你必須告訴 bisect 已知的最後一次正常狀態是哪次提交,使用 `git bisect good [good_commit]`:
793
+
794
+ $ git bisect start
795
+ $ git bisect bad
796
+ $ git bisect good v1.0
797
+ Bisecting: 6 revisions left to test after this
798
+ [ecb6e1bc347ccecc5f9350d878ce677feb13d3b2] error handling on repo
799
+
800
+ Git 發現在你標記為正常的提交(v1.0)和當前的錯誤版本之間有大約12次提交,於是它 check out 中間的一個。在這裡,你可以進行測試,檢查問題是否存在於這次提交。如果是,那麼它是在這個中間提交之前的某一次引入的;如果否,那麼問題是在中間提交之後引入的。假設這裡是沒有錯誤的,那麼你就通過 `git bisect good` 來告訴 Git 然後繼續你的旅程:
801
+
802
+ $ git bisect good
803
+ Bisecting: 3 revisions left to test after this
804
+ [b047b02ea83310a70fd603dc8cd7a6cd13d15c04] secure this thing
805
+
806
+ 現在你在另外一個提交上了,在你剛剛測試通過的和一個錯誤提交的中點處。你再次執行測試然後發現這次提交是錯誤的,因此你通過 `git bisect bad` 來告訴 Git:
807
+
808
+ $ git bisect bad
809
+ Bisecting: 1 revisions left to test after this
810
+ [f71ce38690acf49c1f3c9bea38e09d82a5ce6014] drop exceptions table
811
+
812
+ 這次提交是好的,那麼 Git 就獲得了確定問題引入位置所需的所有資訊。它告訴你第一個錯誤提交的 SHA-1 值,並且顯示一些提交說明,以及哪些檔在那次提交裡被修改過,這樣你可以找出 bug 被引入的根源:
813
+
814
+ $ git bisect good
815
+ b047b02ea83310a70fd603dc8cd7a6cd13d15c04 is first bad commit
816
+ commit b047b02ea83310a70fd603dc8cd7a6cd13d15c04
817
+ Author: PJ Hyett <pjhyett@example.com>
818
+ Date: Tue Jan 27 14:48:32 2009 -0800
819
+
820
+ secure this thing
821
+
822
+ :040000 040000 40ee3e7821b895e52c1695092db9bdc4c61d1730
823
+ f24d3c6ebcfc639b1a3814550e62d60b8e68a8e4 M config
824
+
825
+ 當你完成之後,你應該執行 `git bisect reset` 來重設你的 HEAD 到你開始前的地方,否則你會處於一個詭異的狀態:
826
+
827
+ $ git bisect reset
828
+
829
+ 這是個強大的工具,可以幫助你檢查上百的提交,在幾分鐘內找出 bug 引入的位置。事實上,如果你有一個腳本程式會在專案工作正常時返回0,錯誤時返回非0的話,你可以完全自動地執行 `git bisect`。首先你需要提供已知的錯誤和正確提交來告訴它二分查找的範圍。你可以通過 `bisect start` 命令來列出它們,先列出已知的錯誤提交再列出已知的正確提交:
830
+
831
+ $ git bisect start HEAD v1.0
832
+ $ git bisect run test-error.sh
833
+
834
+ 這樣會自動地在每一個 checked-out 提交裡執行 test-error.sh 直到 Git 找出第一個破損的提交。你也可以執行像 `make` 或者 `make tests`,或者任何你所能執行的自動化測試。
835
+
836
+ ## 子模組 (Submodules) ##
837
+
838
+ 經常有這樣的事情,當你在一個專案上工作時,你需要在其中使用另外一個專案。也許它是一個協力廠商開發的程式庫(Library),或者是你另外開發給多個父專案使用的子專案。在這個情境下產生了一個常見的問題:你想將這兩個專案分開處理,但是又需要在其中一個中使用另外一個。
839
+
840
+ 這裡有一個例子。假設你在開發一個網站,並提供 Atom 訂閱(Atom feeds)。你不想自己編寫產生 Atom 的程式,而是決定使用一個 Library。你可能必須從 CPAN install 或者 Ruby gem 之類的共用庫(shared library)將那段程式 include 進來,或者將原始程式碼複製到你的專案樹中。如果採用包含程式庫的辦法,那麼不管用什麼辦法都很難對這個程式庫做客製化(customize),部署它就更加困難了,因為你必須確保每個客戶都擁有那個程式庫。把程式碼包含到你自己的專案中帶來的問題是,當上游被修改時,任何你進行的客製化的修改都很難歸併(merge)。
841
+
842
+ Git 通過子模組處理這個問題。子模組允許你將一個 Git 倉庫當作另外一個 Git 倉庫的子目錄。這允許你 clone 另外一個倉庫到你的專案中並且保持你的提交相對獨立。
843
+
844
+ ### 子模組初步 ###
845
+
846
+ 假設你想把 Rack library(一個 Ruby 的 web 伺服器閘道介面)加入到你的專案中,可能既要保持你自己的變更,又要延續上游的變更。首先你要把外部的倉庫 clone 到你的子目錄中。你通過 `git submodule add` 命令將外部專案加為子模組:
847
+
848
+ $ git submodule add git://github.com/chneukirchen/rack.git rack
849
+ Initialized empty Git repository in /opt/subtest/rack/.git/
850
+ remote: Counting objects: 3181, done.
851
+ remote: Compressing objects: 100% (1534/1534), done.
852
+ remote: Total 3181 (delta 1951), reused 2623 (delta 1603)
853
+ Receiving objects: 100% (3181/3181), 675.42 KiB | 422 KiB/s, done.
854
+ Resolving deltas: 100% (1951/1951), done.
855
+
856
+ 現在你就在專案裡的 `rack` 子目錄下有了一個 Rack 專案。你可以進入那個子目錄,進行變更,加入你自己的遠端可寫倉庫來推送你的變更,從原始倉庫拉取(pull)和歸併等等。如果你在加入子模組後立刻運行 `git status`,你會看到下面兩項:
857
+
858
+ $ git status
859
+ # On branch master
860
+ # Changes to be committed:
861
+ # (use "git reset HEAD <file>..." to unstage)
862
+ #
863
+ # new file: .gitmodules
864
+ # new file: rack
865
+ #
866
+
867
+ 首先你注意到有一個 `.gitmodules` 文件。這是一個設定檔,保存了專案 URL 和你拉取到的本地子目錄
868
+
869
+ $ cat .gitmodules
870
+ [submodule "rack"]
871
+ path = rack
872
+ url = git://github.com/chneukirchen/rack.git
873
+
874
+ 如果你有多個子模組,這個檔裡會有多個條目。很重要的一點是這個文件跟其他文件一樣也是處於版本控制之下的,就像你的 `.gitignore` 檔一樣。它跟專案裡的其他檔一樣可以被推送和拉取。這是其他 clone 此專案的人獲知子模組專案來源的途徑。
875
+
876
+ `git status` 的輸出裡所列的另一項 rack 。如果你在它上面執行 `git diff`,會發現一些有趣的東西:
877
+
878
+ $ git diff --cached rack
879
+ diff --git a/rack b/rack
880
+ new file mode 160000
881
+ index 0000000..08d709f
882
+ --- /dev/null
883
+ +++ b/rack
884
+ @@ -0,0 +1 @@
885
+ +Subproject commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433
886
+
887
+ 儘管 rack 是你工作目錄裡的子目錄,但 Git 把它視作一個子模組,當你不在那個目錄裡的時候,Git 並不會追蹤記錄它的內容。取而代之的是,Git 將它記錄成來自那個倉庫的一個特殊的提交。當你在那個子目錄裡修改並提交時,子專案會通知那裡的 HEAD 已經發生變更並記錄你當前正在工作的那個提交;通過那樣的方法,當其他人 clone 此專案,他們可以重新創建一致的環境。
888
+
889
+ 這是關於子模組的重要一點:你記錄他們當前確切所處的提交。你不能記錄一個子模組的 `master` 或者其他的符號引用(symbolic reference)。
890
+
891
+ 當你提交時,會看到類似如下:
892
+
893
+ $ git commit -m 'first commit with submodule rack'
894
+ [master 0550271] first commit with submodule rack
895
+ 2 files changed, 4 insertions(+), 0 deletions(-)
896
+ create mode 100644 .gitmodules
897
+ create mode 160000 rack
898
+
899
+ 注意 rack 條目的 160000 模式。這在 Git 中是一個特殊模式,基本意思是你將一個提交記錄為一個目錄項而不是子目錄或者檔案。
900
+
901
+ 你可以將 `rack` 目錄當作一個獨立的專案,保持一個指向子目錄的最新提交的指標然後反復地更新上層專案。所有的 Git 命令都在兩個子目錄裡獨立工作:
902
+
903
+ $ git log -1
904
+ commit 0550271328a0038865aad6331e620cd7238601bb
905
+ Author: Scott Chacon <schacon@gmail.com>
906
+ Date: Thu Apr 9 09:03:56 2009 -0700
907
+
908
+ first commit with submodule rack
909
+ $ cd rack/
910
+ $ git log -1
911
+ commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433
912
+ Author: Christian Neukirchen <chneukirchen@gmail.com>
913
+ Date: Wed Mar 25 14:49:04 2009 +0100
914
+
915
+ Document version change
916
+
917
+ ### Clone 一個帶子模組的專案 ###
918
+
919
+ 這裡你將 clone 一個帶子模組的專案。當你接收到這樣一個專案,你將得到了包含子專案的目錄,但裡面沒有檔案:
920
+
921
+ $ git clone git://github.com/schacon/myproject.git
922
+ Initialized empty Git repository in /opt/myproject/.git/
923
+ remote: Counting objects: 6, done.
924
+ remote: Compressing objects: 100% (4/4), done.
925
+ remote: Total 6 (delta 0), reused 0 (delta 0)
926
+ Receiving objects: 100% (6/6), done.
927
+ $ cd myproject
928
+ $ ls -l
929
+ total 8
930
+ -rw-r--r-- 1 schacon admin 3 Apr 9 09:11 README
931
+ drwxr-xr-x 2 schacon admin 68 Apr 9 09:11 rack
932
+ $ ls rack/
933
+ $
934
+
935
+ `rack` 目錄存在了,但是是空的。你必須執行兩個命令:`git submodule init` 來初始化你的本地設定檔,`git submodule update` 來從那個專案拉取所有資料並 check out 你上層專案裡所列的合適的提交:
936
+
937
+ $ git submodule init
938
+ Submodule 'rack' (git://github.com/chneukirchen/rack.git) registered for path 'rack'
939
+ $ git submodule update
940
+ Initialized empty Git repository in /opt/myproject/rack/.git/
941
+ remote: Counting objects: 3181, done.
942
+ remote: Compressing objects: 100% (1534/1534), done.
943
+ remote: Total 3181 (delta 1951), reused 2623 (delta 1603)
944
+ Receiving objects: 100% (3181/3181), 675.42 KiB | 173 KiB/s, done.
945
+ Resolving deltas: 100% (1951/1951), done.
946
+ Submodule path 'rack': checked out '08d709f78b8c5b0fbeb7821e37fa53e69afcf433'
947
+
948
+ 現在你的 `rack` 子目錄就處於你先前提交的確切狀態了。如果另外一個開發者變更了 rack 的代碼並提交,你拉取那個引用然後歸併之,你會得到有點怪怪的東西:
949
+
950
+ $ git merge origin/master
951
+ Updating 0550271..85a3eee
952
+ Fast forward
953
+ rack | 2 +-
954
+ 1 files changed, 1 insertions(+), 1 deletions(-)
955
+ [master*]$ git status
956
+ # On branch master
957
+ # Changes not staged for commit:
958
+ # (use "git add <file>..." to update what will be committed)
959
+ # (use "git checkout -- <file>..." to discard changes in working directory)
960
+ #
961
+ # modified: rack
962
+ #
963
+
964
+ 你歸併進來的僅僅是一個指向你的子模組的指標;但是它並不更新你子模組目錄裡的代碼,所以看起來你的工作目錄處於一個臨時狀態(dirty state):
965
+
966
+ $ git diff
967
+ diff --git a/rack b/rack
968
+ index 6c5e70b..08d709f 160000
969
+ --- a/rack
970
+ +++ b/rack
971
+ @@ -1 +1 @@
972
+ -Subproject commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0
973
+ +Subproject commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433
974
+
975
+ 事情就是這樣,因為你所擁有的子模組的指標,並沒有對應到子模組目錄的真實狀態。為了修復這一點,你必須再次運行 `git submodule update`:
976
+
977
+ $ git submodule update
978
+ remote: Counting objects: 5, done.
979
+ remote: Compressing objects: 100% (3/3), done.
980
+ remote: Total 3 (delta 1), reused 2 (delta 0)
981
+ Unpacking objects: 100% (3/3), done.
982
+ From git@github.com:schacon/rack
983
+ 08d709f..6c5e70b master -> origin/master
984
+ Submodule path 'rack': checked out '6c5e70b984a60b3cecd395edd5b48a7575bf58e0'
985
+
986
+ 每次你從主專案中拉取一個子模組的變更都必須這樣做。看起來很怪但是管用。
987
+
988
+ 一個常見問題是當開發者對子模組做了一個本地的變更但是並沒有推送到公共伺服器。然後他們提交了一個指向那個非公開狀態的指標然後推送上層專案。當其他開發者試圖運行 `git submodule update`,那個子模組系統會找不到所引用的提交,因為它只存在于第一個開發者的系統中。如果發生那種情況,你會看到類似這樣的錯誤:
989
+
990
+ $ git submodule update
991
+ fatal: reference isn’t a tree: 6c5e70b984a60b3cecd395edd5b48a7575bf58e0
992
+ Unable to checkout '6c5e70b984a60b3cecd395edd5ba7575bf58e0' in submodule path 'rack'
993
+
994
+ 你不得不去查看誰最後變更了子模組:
995
+
996
+ $ git log -1 rack
997
+ commit 85a3eee996800fcfa91e2119372dd4172bf76678
998
+ Author: Scott Chacon <schacon@gmail.com>
999
+ Date: Thu Apr 9 09:19:14 2009 -0700
1000
+
1001
+ added a submodule reference I will never make public. hahahahaha!
1002
+
1003
+ 然後,你給那個傢伙發電子郵件說他一通。
1004
+
1005
+ ### 上層專案 ###
1006
+
1007
+ 有時候,開發者想按照他們的分組獲取一個大專案的子目錄的子集。如果你是從 CVS 或者 Subversion 遷移過來的話這個很常見,在那些系統中你已經定義了一個模組或者子目錄的集合,而你想延續這種類型的工作流程。
1008
+
1009
+ 在 Git 中實現這個的一個好辦法是你將每一個子目錄都做成獨立的 Git 倉庫,然後創建一個上層專案的 Git 倉庫包含多個子模組。這個辦法的一個優勢是,你可以在上層專案中通過標籤和分支更為明確地定義專案之間的關係。
1010
+
1011
+ ### 子模組的問題 ###
1012
+
1013
+ 使用子模組並非沒有任何缺點。首先,你在子模組目錄中工作時必須相對小心。當你執行 `git submodule update`,它會 check out 專案的指定版本,但是不在分支內。這叫做獲得一個分離的頭(detached head)——這意味著 HEAD 檔直接指向一次提交,而不是一個符號引用(symbolic reference)。問題在於你通常並不想在一個分離的頭的環境下工作,因為太容易丟失變更了。如果你先執行了一次 `submodule update`,然後在那個子模組目錄裡不創建分支就進行提交,然後再次從上層專案裡執行 `git submodule update` 同時不進行提交,Git 會毫無提示地覆蓋你的變更。技術上講你不會丟失工作,但是你將失去指向它的分支,因此會很難取得。
1014
+
1015
+ 為了避免這個問題,當你在子模組目錄裡工作時應使用 `git checkout -b work` 或類似的命令來 創建一個分支。當你再次在子模組裡更新的時候,它仍然會覆蓋你的工作,但是至少你擁有一個可以回溯的指標。
1016
+
1017
+ 切換帶有子模組的分支同樣也很有技巧。如果你創建一個新的分支,增加了一個子模組,然後切換回不帶該子模組的分支,你仍然會擁有一個未被追蹤的子模組的目錄 :
1018
+
1019
+ $ git checkout -b rack
1020
+ Switched to a new branch "rack"
1021
+ $ git submodule add git@github.com:schacon/rack.git rack
1022
+ Initialized empty Git repository in /opt/myproj/rack/.git/
1023
+ ...
1024
+ Receiving objects: 100% (3184/3184), 677.42 KiB | 34 KiB/s, done.
1025
+ Resolving deltas: 100% (1952/1952), done.
1026
+ $ git commit -am 'added rack submodule'
1027
+ [rack cc49a69] added rack submodule
1028
+ 2 files changed, 4 insertions(+), 0 deletions(-)
1029
+ create mode 100644 .gitmodules
1030
+ create mode 160000 rack
1031
+ $ git checkout master
1032
+ Switched to branch "master"
1033
+ $ git status
1034
+ # On branch master
1035
+ # Untracked files:
1036
+ # (use "git add <file>..." to include in what will be committed)
1037
+ #
1038
+ # rack/
1039
+
1040
+ 你將不得不將它移走或者刪除,這樣的話當你切換回去的時候必須重新 clone 它——你可能會丟失你未推送的本地的變更或分支。
1041
+
1042
+ 最後一個需要注意的是關於從子目錄切換到子模組。如果你已經追蹤了你專案中的一些檔案,但是想把它們移到子模組去,你必須非常小心,否則 Git 會生你的氣。假設你的專案中有一個子目錄裡放了 rack 的檔,然後你想將它轉換為子模組。如果你刪除子目錄然後執行 `submodule add`,Git 會向你大吼:
1043
+
1044
+ $ rm -Rf rack/
1045
+ $ git submodule add git@github.com:schacon/rack.git rack
1046
+ 'rack' already exists in the index
1047
+
1048
+ 你必須先將 `rack` 目錄撤回(unstage)。然後你才能加入子模組:
1049
+
1050
+ $ git rm -r rack
1051
+ $ git submodule add git@github.com:schacon/rack.git rack
1052
+ Initialized empty Git repository in /opt/testsub/rack/.git/
1053
+ remote: Counting objects: 3184, done.
1054
+ remote: Compressing objects: 100% (1465/1465), done.
1055
+ remote: Total 3184 (delta 1952), reused 2770 (delta 1675)
1056
+ Receiving objects: 100% (3184/3184), 677.42 KiB | 88 KiB/s, done.
1057
+ Resolving deltas: 100% (1952/1952), done.
1058
+
1059
+ 現在假設你在一個分支裡那樣做了。如果你嘗試切換回一個仍然在目錄裡保留那些檔而不是子模組的分支時——你會得到下面的錯誤:
1060
+
1061
+ $ git checkout master
1062
+ error: Untracked working tree file 'rack/AUTHORS' would be overwritten by merge.
1063
+
1064
+ 你必須先移除 `rack` 子模組的目錄才能切換到不包含它的分支:
1065
+
1066
+ $ mv rack /tmp/
1067
+ $ git checkout master
1068
+ Switched to branch "master"
1069
+ $ ls
1070
+ README rack
1071
+
1072
+ 然後,當你切換回來,你會得到一個空的 `rack` 目錄。你可以執行 `git submodule update` 重新 clone,也可以將 `/tmp/rack` 目錄重新移回空目錄。
1073
+
1074
+ ## 子樹合併 ##
1075
+
1076
+ 現在你已經看到了子模組系統的麻煩之處,讓我們來看一下解決相同問題的另一途徑。當 Git 歸併(merge)時,它會檢查需要歸併的內容然後選擇一個合適的歸併策略。如果你歸併的分支是兩個,Git使用一個「遞迴 (recursive)」策略。如果你歸併的分支超過兩個,Git採用「章魚」策略。這些策略是自動選擇的,因為遞迴策略可以處理複雜的三路歸併情況——比如多於一個共同祖先——但是它只能處理兩個分支的歸併。章魚歸併可以處理多個分支,但是必須更加小心以避免衝突帶來的麻煩,因此它被選中作為歸併兩個以上分支的預設策略。
1077
+
1078
+ 實際上,你也可以選擇其他策略。其中的一個就是「子樹歸併 (subtree merge)」,你可以用它來處理子專案問題。這裡你會看到如何換用子樹歸併的方法來實現前一節裡所做的 rack 的嵌入。
1079
+
1080
+ 子樹歸併的想法是你擁有兩個專案,其中一個專案映射到另外一個專案的子目錄中,反過來也一樣。當你指定一個子樹歸併,Git 可以聰明地探知其中一個是另外一個的子樹從而實現正確的歸併——這相當神奇。
1081
+
1082
+ 首先你將 Rack 應用加入到專案中。你將 Rack 專案當作你專案中的一個遠端參照,然後將它 check out 到它自身的分支:
1083
+
1084
+ $ git remote add rack_remote git@github.com:schacon/rack.git
1085
+ $ git fetch rack_remote
1086
+ warning: no common commits
1087
+ remote: Counting objects: 3184, done.
1088
+ remote: Compressing objects: 100% (1465/1465), done.
1089
+ remote: Total 3184 (delta 1952), reused 2770 (delta 1675)
1090
+ Receiving objects: 100% (3184/3184), 677.42 KiB | 4 KiB/s, done.
1091
+ Resolving deltas: 100% (1952/1952), done.
1092
+ From git@github.com:schacon/rack
1093
+ * [new branch] build -> rack_remote/build
1094
+ * [new branch] master -> rack_remote/master
1095
+ * [new branch] rack-0.4 -> rack_remote/rack-0.4
1096
+ * [new branch] rack-0.9 -> rack_remote/rack-0.9
1097
+ $ git checkout -b rack_branch rack_remote/master
1098
+ Branch rack_branch set up to track remote branch refs/remotes/rack_remote/master.
1099
+ Switched to a new branch "rack_branch"
1100
+
1101
+ 現在在你的 `rack_branch` 分支中就有了 Rack 專案的根目錄,而你自己的專案在 `master` 分支中。如果你先 check out 其中一個然後另外一個,你會看到它們有不同的專案根目錄:
1102
+
1103
+ $ ls
1104
+ AUTHORS KNOWN-ISSUES Rakefile contrib lib
1105
+ COPYING README bin example test
1106
+ $ git checkout master
1107
+ Switched to branch "master"
1108
+ $ ls
1109
+ README
1110
+
1111
+ 要將 Rack 專案當作子目錄拉取到你的 `master` 專案中。你可以在 Git 中用 `git read-tree` 來實現。你會在第9章學到更多與 `read-tree` 和它的朋友相關的東西,目前你會知道它讀取一個分支的根目錄樹到當前的暫存區和工作目錄。你只要切換回你的 `master` 分支,然後拉取 `rack_branch` 分支到你主專案的 `master` 分支的 `rack` 子目錄:
1112
+
1113
+ $ git read-tree --prefix=rack/ -u rack_branch
1114
+
1115
+ 當你提交的時候,看起來就像你在那個子目錄下擁有全部 Rack 的檔案——就像你從一個 tarball 裡拷貝的一樣。有意思的是你可以比較容易地歸併其中一個分支的變更到另外一個。因此,如果 Rack 專案更新了,你可以通過切換到那個分支並執行拉取來獲得上游的變更:
1116
+
1117
+ $ git checkout rack_branch
1118
+ $ git pull
1119
+
1120
+ 然後,你可以將那些變更歸併回你的 master 分支。你可以使用 `git merge -s subtree`,它會工作的很好;但是 Git 同時會把歷史歸併到一起,這可能不是你想要的。為了拉取變更並預置提交說明,需要在 `-s subtree` 策略選項的同時使用 `--squash` 和 `--no-commit` 選項。
1121
+
1122
+ $ git checkout master
1123
+ $ git merge --squash -s subtree --no-commit rack_branch
1124
+ Squash commit -- not updating HEAD
1125
+ Automatic merge went well; stopped before committing as requested
1126
+
1127
+ 所有 Rack 專案的變更都被歸併進來,而且可以進行本地提交。你也可以做相反的事情——在你主分支的 `rack` 目錄裡進行變更然後歸併回 `rack_branch` 分支,然後將它們提交給維護者或者推送到上游。
1128
+
1129
+ 為了得到 `rack` 子目錄和你 `rack_branch` 分支的區別——以決定你是否需要歸併它們——你不能使用一般的 `diff` 命令。而是對你想比較的分支執行 `git diff-tree`:
1130
+
1131
+ $ git diff-tree -p rack_branch
1132
+
1133
+ 或者,為了比較你的 `rack` 子目錄和伺服器上你拉取時的 `master` 分支,你可以執行
1134
+
1135
+ $ git diff-tree -p rack_remote/master
1136
+
1137
+ ## 總結 ##
1138
+
1139
+ 你已經看到了很多高級的工具,允許你更加精確地操控你的提交和暫存區(staging area)。當你碰到問題時,你應該可以很容易找出是哪個分支、什麼時候、由誰引入了它們。如果你想在專案中使用子專案,你也已經學會了一些方法來滿足這些需求。到此,你應該能夠在 Git 命令列下完成大部分的日常事務,並且感到比較順手。