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,977 @@
1
+ # Git 內部原理 #
2
+
3
+ 不管你是從前面的章節直接跳到了本章,還是讀完了其餘各章一直到這,你都將在本章見識 Git 的內部工作原理和實現方式。我個人發現學習這些內容對於理解 Git 的用處和強大是非常重要的,不過也有人認為這些內容對於初學者來說可能難以理解且過於複雜。正因如此我把這部分內容放在最後一章,你在學習過程中可以先閱讀這部分,也可以晚點閱讀這部分,這完全取決於你自己。
4
+
5
+ 既然已經讀到這了,就讓我們開始吧。首先要弄明白一點,從根本上來講 Git 是一套內容定址 (content-addressable) 檔案系統,在此之上提供了一個 VCS 使用者介面。馬上你就會學到這意味著什麼。
6
+
7
+ 早期的 Git (主要是 1.5 之前版本) 的使用者介面要比現在複雜得多,這是因為它更側重于成為檔案系統而不是一套更精緻的 VCS 。最近幾年改進了 UI 從而使它跟其他任何系統一樣清晰易用。即便如此,還是經常會有一些陳腔濫調提到早期 Git 的 UI 複雜又難學。
8
+
9
+ 內容定址檔案系統這一層相當酷,在本章中我會先講解這部分。隨後你會學到傳輸機制和最終要使用的各種倉庫管理任務。
10
+
11
+ ## 底層命令 (Plumbing) 和高層命令 (Porcelain) ##
12
+
13
+ 本書講解了使用 `checkout`, `branch`, `remote` 等共約 30 個 Git 命令。然而由於 Git 一開始被設計成供 VCS 使用的工具集,而不是一整套 user-friendly 的 VCS,它還包含了許多底層命令,這些命令用於以 UNIX 風格使用或由腳本呼叫。這些命令一般被稱為 “plumbing” 命令(底層命令),其他的更友好的命令則被稱為 “porcelain” 命令(高層命令)。
14
+
15
+ 本書前八章主要專門討論高層命令。本章將主要討論底層命令以理解 Git 的內部工作機制、演示 Git 如何及為何要以這種方式工作。這些命令主要不是用來從命令列手工使用的,更多的是用來為其他工具和自訂腳本服務的。
16
+
17
+ 當你在一個新目錄或已有目錄內執行 `git init` 時,Git 會創建一個 `.git` 目錄,幾乎所有 Git 儲存和操作的內容都位於該目錄下。如果你要備份或複製一個倉庫,基本上將這一目錄拷貝至其他地方就可以了。本章基本上都討論該目錄下的內容。該目錄結構如下:
18
+
19
+ $ ls
20
+ HEAD
21
+ branches/
22
+ config
23
+ description
24
+ hooks/
25
+ index
26
+ info/
27
+ objects/
28
+ refs/
29
+
30
+ 該目錄下有可能還有其他檔,但這是一個全新的 `git init` 生成的倉庫,所以預設情況下這些就是你能看到的結構。新版本的 Git 不再使用 `branches` 目錄,`description` 檔僅供 GitWeb 程式使用,所以不用關心這些內容。`config` 檔包含了專案特有的配置選項,`info` 目錄保存了一份不希望在 .gitignore 檔中管理的忽略模式 (ignored patterns) 的全域可執行檔。`hooks` 目錄包含了第六章詳細介紹的用戶端或服務端鉤子腳本。
31
+
32
+ 另外還有四個重要的檔案或目錄:`HEAD` 及 `index` 檔,`objects` 及 `refs` 目錄。這些是 Git 的核心部分。`objects` 目錄存放所有資料內容,`refs` 目錄存放指向資料 (分支) 的提交物件的指標,`HEAD` 檔指向當前分支,`index` 檔保存了暫存區域資訊。馬上你將詳細瞭解 Git 是如何操縱這些內容的。
33
+
34
+ ## Git 物件 ##
35
+
36
+ Git 是一套內容定址檔案系統。很不錯。不過這是什麼意思呢?
37
+ 這種說法的意思是,從內部來看,Git 是簡單的 key-value 資料儲存。它允許插入任意類型的內容,並會回傳一個鍵值,通過該鍵值可以在任何時候再取出該內容。可以通過底層命令 `hash-object` 來示範這點,傳一些資料給該命令,它會將資料保存在 `.git` 目錄並回傳表示這些資料的鍵值。首先初使化一個 Git 倉庫並確認 `objects` 目錄是空的:
38
+
39
+ $ mkdir test
40
+ $ cd test
41
+ $ git init
42
+ Initialized empty Git repository in /tmp/test/.git/
43
+ $ find .git/objects
44
+ .git/objects
45
+ .git/objects/info
46
+ .git/objects/pack
47
+ $ find .git/objects -type f
48
+ $
49
+
50
+ Git 初始化了 `objects 目錄`,同時在該目錄下創建了 `pack` 和 `info` 子目錄,但是該目錄下沒有其他常規檔。我們往這個 Git 資料庫裡儲存一些文本:
51
+
52
+ $ echo 'test content' | git hash-object -w --stdin
53
+ d670460b4b4aece5915caf5c68d12f560a9fe3e4
54
+
55
+ 參數 `-w` 指示 `hash-object` 命令儲存 (資料) 物件,若不指定這個參數該命令僅僅回傳鍵值。`--stdin` 指定從標準輸入裝置 (stdin) 來讀取內容,若不指定這個參數,`hash-object` 就需要指定一個要儲存的檔案路徑。該命令輸出長度為 40 個字元的校驗和(checksum hash)。這是個 SHA-1 雜湊值──其值為要儲存的資料加上你馬上會瞭解到的一種頭資訊(header)的校驗和。現在可以查看到 Git 已經儲存了資料:
56
+
57
+ $ find .git/objects -type f
58
+ .git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
59
+
60
+ 可以在 `objects` 目錄下看到一個檔。這便是 Git 儲存資料內容的方式──為每份內容生成一個檔,取得該內容與頭資訊的 SHA-1 校驗和,創建以該校驗和前兩個字元為名稱的子目錄,並以 (校驗和) 剩下 38 個字元為檔命名 (保存至子目錄下)。
61
+
62
+ 通過 `cat-file` 命令可以將資料內容取回。該命令是查看 Git 對象的瑞士軍刀。傳入 `-p` 參數可以讓 `cat-file` 命令輸出資料內容的類型:
63
+
64
+ $ git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4
65
+ test content
66
+
67
+ 可以往 Git 中添加更多內容並取回了。也可以直接添加檔案。比方說可以對一個檔進行簡單的版本控制。首先,創建一個新檔,並把檔案內容儲存到資料庫中:
68
+
69
+ $ echo 'version 1' > test.txt
70
+ $ git hash-object -w test.txt
71
+ 83baae61804e65cc73a7201a7252750c76066a30
72
+
73
+ 接著往該檔中寫入一些新內容並再次保存:
74
+
75
+ $ echo 'version 2' > test.txt
76
+ $ git hash-object -w test.txt
77
+ 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
78
+
79
+ 資料庫中已經將檔案的兩個新版本連同一開始的內容保存下來了:
80
+
81
+ $ find .git/objects -type f
82
+ .git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a
83
+ .git/objects/83/baae61804e65cc73a7201a7252750c76066a30
84
+ .git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
85
+
86
+ 再將檔案修復到第一個版本:
87
+
88
+ $ git cat-file -p 83baae61804e65cc73a7201a7252750c76066a30 > test.txt
89
+ $ cat test.txt
90
+ version 1
91
+
92
+ 或恢復到第二個版本:
93
+
94
+ $ git cat-file -p 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a > test.txt
95
+ $ cat test.txt
96
+ version 2
97
+
98
+ 需要記住的是幾個版本的檔 SHA-1 值可能與實際的值不同,其次,儲存的並不是檔案名而僅僅是檔案內容。這種物件類型稱為 blob 。通過傳遞 SHA-1 值給 `cat-file -t` 命令可以讓 Git 返回任何物件的類型:
99
+
100
+ $ git cat-file -t 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
101
+ blob
102
+
103
+ ### Tree 物件 ###
104
+
105
+ 接下去來看 tree 物件,tree 物件可以儲存檔案名,同時也允許將一組檔案儲存在一起。Git 以一種類似 UNIX 檔案系統但更簡單的方式來儲存內容。所有內容以 tree 或 blob 物件儲存,其中 tree 物件對應於 UNIX 中的目錄,blob 物件則大致對應於 inodes 或檔案內容。一個單獨的 tree 物件包含一條或多條 tree 記錄,每一條記錄含有一個指向 blob 或子 tree 物件的 SHA-1 指標,並附有該物件的許可權模式 (mode)、類型和檔案名資訊。以 simplegit 專案為例,最新的 tree 可能是這個樣子:
106
+
107
+ $ git cat-file -p master^{tree}
108
+ 100644 blob a906cb2a4a904a152e80877d4088654daad0c859 README
109
+ 100644 blob 8f94139338f9404f26296befa88755fc2598c289 Rakefile
110
+ 040000 tree 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0 lib
111
+
112
+ `master^{tree}` 表示 `master` 分支上最新提交指向的 tree 物件。請注意 `lib` 子目錄並非一個 blob 物件,而是一個指向別一個 tree 物件的指標:
113
+
114
+ $ git cat-file -p 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0
115
+ 100644 blob 47c6340d6459e05787f644c2447d2595f5d3a54b simplegit.rb
116
+
117
+ 從概念上來講,Git 保存的資料如圖 9-1 所示。
118
+
119
+ Insert 18333fig0901.png
120
+ Figure 9-1. Git 物件模型的簡化版
121
+
122
+ 你可以自己創建 tree 。通常 Git 根據你的暫存區域或 index 來創建並寫入一個 tree 。因此要創建一個 tree 物件的話首先要通過將一些檔暫存從而創建一個 index 。可以使用 plumbing 命令 `update-index` 為一個單獨檔 ── test.txt 檔的第一個版本 ── 創建一個 index。通過該命令人工地將 test.txt 檔的首個版本加入到了一個新的暫存區域中。由於該檔原先並不在暫存區域中 (甚至就連暫存區域也還沒被創建出來呢),必須傳入 `--add` 參數;由於要添加的檔並不在目前的目錄下而是在資料庫中,必須傳入 `--cacheinfo` 參數。同時指定檔案模式,SHA-1 值和檔案名:
123
+
124
+ $ git update-index --add --cacheinfo 100644 \
125
+ 83baae61804e65cc73a7201a7252750c76066a30 test.txt
126
+
127
+ 在本例中,指定了檔案模式為 `100644`,表明這是一個普通檔。其他可用的模式有:`100755` 表示可執行檔,`120000` 表示符號連結(symbolic link)。檔案模式是從一般的 UNIX 檔案模式中參考來的,但是沒有那麼靈活 ── 上述三種模式僅對 Git 中的檔案 (blobs) 有效 (雖然也有其他模式用於目錄和子模組)。
128
+
129
+ 現在可以用 `write-tree` 命令將暫存區域的內容寫到一個 tree 物件了。無需 `-w` 參數 ── 如果目標 tree 不存在,呼叫 `write-tree` 會自動根據 index 狀態創建一個 tree 物件。
130
+
131
+ $ git write-tree
132
+ d8329fc1cc938780ffdd9f94e0d364e0ea74f579
133
+ $ git cat-file -p d8329fc1cc938780ffdd9f94e0d364e0ea74f579
134
+ 100644 blob 83baae61804e65cc73a7201a7252750c76066a30 test.txt
135
+
136
+ 可以驗證這確實是一個 tree 物件:
137
+
138
+ $ git cat-file -t d8329fc1cc938780ffdd9f94e0d364e0ea74f579
139
+ tree
140
+
141
+ 再根據 test.txt 的第二個版本以及一個新檔創建一個新 tree 物件:
142
+
143
+ $ echo 'new file' > new.txt
144
+ $ git update-index test.txt
145
+ $ git update-index --add new.txt
146
+
147
+ 這時暫存區域中包含了 test.txt 的新版本及一個新檔 new.txt 。創建 (寫) 該 tree 物件 (將暫存區域或 index 狀態寫入到一個 tree 物件),然後瞧瞧它的樣子:
148
+
149
+ $ git write-tree
150
+ 0155eb4229851634a0f03eb265b69f5a2d56f341
151
+ $ git cat-file -p 0155eb4229851634a0f03eb265b69f5a2d56f341
152
+ 100644 blob fa49b077972391ad58037050f2a75f74e3671e92 new.txt
153
+ 100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt
154
+
155
+ 請注意該 tree 物件包含了兩個檔案記錄,且 test.txt 的 SHA 值是早先值的 “第二版” (`1f7a7a`)。來點更有趣的,你將把第一個 tree 物件作為一個子目錄加進該 tree 中。可以用 `read-tree` 命令將 tree 物件讀到暫存區域中去。在這時,通過傳一個 `--prefix` 參數給 `read-tree`,將一個已有的 tree 物件作為一個子 tree 讀到暫存區域中:
156
+
157
+ $ git read-tree --prefix=bak d8329fc1cc938780ffdd9f94e0d364e0ea74f579
158
+ $ git write-tree
159
+ 3c4e9cd789d88d8d89c1073707c3585e41b0e614
160
+ $ git cat-file -p 3c4e9cd789d88d8d89c1073707c3585e41b0e614
161
+ 040000 tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579 bak
162
+ 100644 blob fa49b077972391ad58037050f2a75f74e3671e92 new.txt
163
+ 100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt
164
+
165
+ 如果從剛寫入的新 tree 物件創建一個工作目錄,將得到位於工作目錄頂級的兩個檔和一個名為 `bak` 的子目錄,該子目錄包含了 test.txt 檔的第一個版本。可以將 Git 用來包含這些內容的資料想像成如圖 9-2 所示的樣子。
166
+
167
+ Insert 18333fig0902.png
168
+ Figure 9-2. 當前 Git 資料的內容結構
169
+
170
+ ### Commit 物件 ###
171
+
172
+ 你現在有三個 tree 物件,它們指向了你要跟蹤的專案的不同快照,可是先前的問題依然存在:必須記往三個 SHA-1 值以獲得這些快照。你也沒有關於誰、何時以及為何保存了這些快照的資訊。commit 物件為你保存了這些基本資訊。
173
+
174
+ 要創建一個 commit 物件,使用 `commit-tree` 命令,指定一個 tree 的 SHA-1,如果有任何前繼提交物件,也可以指定。從你寫的第一個 tree 開始:
175
+
176
+ $ echo 'first commit' | git commit-tree d8329f
177
+ fdf4fc3344e67ab068f836878b6c4951e3b15f3d
178
+
179
+ 通過 `cat-file` 查看這個新 commit 物件:
180
+
181
+ $ git cat-file -p fdf4fc3
182
+ tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
183
+ author Scott Chacon <schacon@gmail.com> 1243040974 -0700
184
+ committer Scott Chacon <schacon@gmail.com> 1243040974 -0700
185
+
186
+ first commit
187
+
188
+ commit 物件格式很簡單:指明了該時間點專案快照的頂層樹物件、作者/提交者資訊 (從 Git 組態設定的 `user.name` 和 `user.email` 中獲得) 以及當前時間戳記、一個空行,以及提交注釋資訊。
189
+
190
+ 接著再寫入另外兩個 commit 物件,每一個都指定其之前的那個 commit 物件:
191
+
192
+ $ echo 'second commit' | git commit-tree 0155eb -p fdf4fc3
193
+ cac0cab538b970a37ea1e769cbbde608743bc96d
194
+ $ echo 'third commit' | git commit-tree 3c4e9c -p cac0cab
195
+ 1a410efbd13591db07496601ebc7a059dd55cfe9
196
+
197
+ 每一個 commit 物件都指向了你創建的樹物件快照。出乎意料的是,現在已經有了真實的 Git 歷史了,所以如果執行 `git log` 命令並指定最後那個 commit 物件的 SHA-1 便可以查看歷史:
198
+
199
+ $ git log --stat 1a410e
200
+ commit 1a410efbd13591db07496601ebc7a059dd55cfe9
201
+ Author: Scott Chacon <schacon@gmail.com>
202
+ Date: Fri May 22 18:15:24 2009 -0700
203
+
204
+ third commit
205
+
206
+ bak/test.txt | 1 +
207
+ 1 files changed, 1 insertions(+), 0 deletions(-)
208
+
209
+ commit cac0cab538b970a37ea1e769cbbde608743bc96d
210
+ Author: Scott Chacon <schacon@gmail.com>
211
+ Date: Fri May 22 18:14:29 2009 -0700
212
+
213
+ second commit
214
+
215
+ new.txt | 1 +
216
+ test.txt | 2 +-
217
+ 2 files changed, 2 insertions(+), 1 deletions(-)
218
+
219
+ commit fdf4fc3344e67ab068f836878b6c4951e3b15f3d
220
+ Author: Scott Chacon <schacon@gmail.com>
221
+ Date: Fri May 22 18:09:34 2009 -0700
222
+
223
+ first commit
224
+
225
+ test.txt | 1 +
226
+ 1 files changed, 1 insertions(+), 0 deletions(-)
227
+
228
+ 真棒。你剛剛通過使用低級操作而不是那些普通命令創建了一個 Git 歷史。這基本上就是執行 `git add` 和 `git commit` 命令時 Git 進行的工作 ──保存修改了的檔案的 blob,更新索引,創建 tree 物件,最後創建 commit 物件,這些 commit 物件指向了頂層 tree 物件以及先前的 commit 物件。這三類 Git 物件 ── blob,tree 以及 commit ── 都各自以檔案的方式保存在 `.git/objects` 目錄下。以下所列是目前為止範例目錄的所有物件,每個物件後面的注釋裡標明了它們保存的內容:
229
+
230
+ $ find .git/objects -type f
231
+ .git/objects/01/55eb4229851634a0f03eb265b69f5a2d56f341 # tree 2
232
+ .git/objects/1a/410efbd13591db07496601ebc7a059dd55cfe9 # commit 3
233
+ .git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a # test.txt v2
234
+ .git/objects/3c/4e9cd789d88d8d89c1073707c3585e41b0e614 # tree 3
235
+ .git/objects/83/baae61804e65cc73a7201a7252750c76066a30 # test.txt v1
236
+ .git/objects/ca/c0cab538b970a37ea1e769cbbde608743bc96d # commit 2
237
+ .git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4 # 'test content'
238
+ .git/objects/d8/329fc1cc938780ffdd9f94e0d364e0ea74f579 # tree 1
239
+ .git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 # new.txt
240
+ .git/objects/fd/f4fc3344e67ab068f836878b6c4951e3b15f3d # commit 1
241
+
242
+ 如果你按照以上描述進行了操作,可以得到如圖 9-3 所示的物件圖。
243
+
244
+ Insert 18333fig0903.png
245
+ Figure 9-3. Git 目錄下的所有物件
246
+
247
+ ### 物件儲存 ###
248
+
249
+ 之前我提到當儲存資料內容時,同時會有一個檔頭被儲存起來。我們花些時間來看看 Git 是如何儲存物件的。你將看到如何通過 Ruby 指令碼語言儲存一個 blob 物件 (這裡以字串 “what is up, doc?” 為例) 。使用 `irb` 命令進入 Ruby 互動式模式:
250
+
251
+ $ irb
252
+ >> content = "what is up, doc?"
253
+ => "what is up, doc?"
254
+
255
+ Git 以物件類型為起始內容構造一個檔頭,本例中是一個 blob。然後添加一個空格,接著是資料內容的長度,最後是一個空位元組 (null byte):
256
+
257
+ >> header = "blob #{content.length}\0"
258
+ => "blob 16\000"
259
+
260
+ Git 將檔頭與原始資料內容拼接起來,並計算拼接後的新內容的 SHA-1 校驗和。可以在 Ruby 中使用 `require` 語句導入 SHA1 digest 程式庫,然後呼叫 `Digest::SHA1.hexdigest()` 方法計算字串的 SHA-1 值:
261
+
262
+ >> store = header + content
263
+ => "blob 16\000what is up, doc?"
264
+ >> require 'digest/sha1'
265
+ => true
266
+ >> sha1 = Digest::SHA1.hexdigest(store)
267
+ => "bd9dbf5aae1a3862dd1526723246b20206e5fc37"
268
+
269
+ Git 用 zlib 對資料內容進行壓縮,在 Ruby 中可以用 zlib 程式庫來實現。首先需要導入該程式庫,然後用 `Zlib::Deflate.deflate()` 對資料進行壓縮:
270
+
271
+ >> require 'zlib'
272
+ => true
273
+ >> zlib_content = Zlib::Deflate.deflate(store)
274
+ => "x\234K\312\311OR04c(\317H,Q\310,V(-\320QH\311O\266\a\000_\034\a\235"
275
+
276
+ 最後將用 zlib 壓縮後的內容寫入磁片。需要指定保存物件的路徑 (SHA-1 值的頭兩個字元作為子目錄名稱,剩餘 38 個字元作為檔案名保存至該子目錄中)。在 Ruby 中,如果子目錄不存在可以用 `FileUtils.mkdir_p()` 函數創建它。接著用 `File.open` 方法打開檔案,並用 `write()` 方法將之前壓縮的內容寫入該檔:
277
+
278
+ >> path = '.git/objects/' + sha1[0,2] + '/' + sha1[2,38]
279
+ => ".git/objects/bd/9dbf5aae1a3862dd1526723246b20206e5fc37"
280
+ >> require 'fileutils'
281
+ => true
282
+ >> FileUtils.mkdir_p(File.dirname(path))
283
+ => ".git/objects/bd"
284
+ >> File.open(path, 'w') { |f| f.write zlib_content }
285
+ => 32
286
+
287
+ 這就行了 ── 你已經創建了一個正確的 blob 物件。所有的 Git 物件都以這種方式儲存,惟一的區別是類型不同 ── 除了字串 blob,檔頭起始內容還可以是 commit 或 tree 。不過雖然 blob 幾乎可以是任意內容,commit 和 tree 的資料卻是有固定格式的。
288
+
289
+ ## Git References ##
290
+
291
+ 你可以執行像 `git log 1a410e` 這樣的命令來查看完整的歷史,但是這樣你就要記得 `1a410e` 是你最後一次提交,這樣才能在提交歷史中找到這些物件。你需要一個檔來用一個簡單的名字來記錄這些 SHA-1 值,這樣你就可以用這些指標而不是原來的 SHA-1 值去檢索了。
292
+
293
+ 在 Git 中,這些我們稱之為「引用」(references 或者 refs,譯者注)。你可以在 `.git/refs` 目錄下面找到這些包含 SHA-1 值的檔。在這個專案裡,這個目錄還沒不包含任何檔,但是包含這樣一個簡單的結構:
294
+
295
+ $ find .git/refs
296
+ .git/refs
297
+ .git/refs/heads
298
+ .git/refs/tags
299
+ $ find .git/refs -type f
300
+ $
301
+
302
+ 如果想要創建一個新的引用幫助你記住最後一次提交,技術上你可以這樣做:
303
+
304
+ $ echo "1a410efbd13591db07496601ebc7a059dd55cfe9" > .git/refs/heads/master
305
+
306
+ 現在,你就可以在 Git 命令中使用你剛才創建的引用而不是 SHA-1 值:
307
+
308
+ $ git log --pretty=oneline master
309
+ 1a410efbd13591db07496601ebc7a059dd55cfe9 third commit
310
+ cac0cab538b970a37ea1e769cbbde608743bc96d second commit
311
+ fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
312
+
313
+ 當然,我們並不鼓勵你直接修改這些引用檔。如果你確實需要更新一個引用,Git 提供了一個比較安全的命令 `update-ref`:
314
+
315
+ $ git update-ref refs/heads/master 1a410efbd13591db07496601ebc7a059dd55cfe9
316
+
317
+ 基本上 Git 中的一個分支其實就是一個指向某個工作版本一條 HEAD 記錄的指標或引用。你可以用這條命令創建一個指向第二次提交的分支:
318
+
319
+ $ git update-ref refs/heads/test cac0ca
320
+
321
+ 這樣你的分支將會只包含那次提交以及之前的工作:
322
+
323
+ $ git log --pretty=oneline test
324
+ cac0cab538b970a37ea1e769cbbde608743bc96d second commit
325
+ fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
326
+
327
+ 現在,你的 Git 資料庫應該看起來像圖 9-4 一樣。
328
+
329
+ Insert 18333fig0904.png
330
+ Figure 9-4. 包含分支引用的 Git 目錄物件
331
+
332
+ 每當你執行 `git branch (分支名稱)` 這樣的命令,Git 基本上就是執行 `update-ref` 命令,把你現在所在分支中最後一次提交的 SHA-1 值,添加到你要創建的分支的引用。
333
+
334
+ ### HEAD 標記 ###
335
+
336
+ 現在的問題是,當你執行 `git branch (分支名稱)` 這條命令的時候,Git 怎麼知道最後一次提交的 SHA-1 值呢?答案就是 HEAD 檔。HEAD 檔是一個指向你當前所在分支的引用識別字。這樣的引用識別字——它看起來並不像一個普通的引用——其實並不包含 SHA-1 值,而是一個指向另外一個引用的指標。如果你看一下這個檔,通常你將會看到這樣的內容:
337
+
338
+ $ cat .git/HEAD
339
+ ref: refs/heads/master
340
+
341
+ 如果你執行 `git checkout test`,Git 就會更新這個檔,看起來像這樣:
342
+
343
+ $ cat .git/HEAD
344
+ ref: refs/heads/test
345
+
346
+ 當你再執行 `git commit` 命令,它就創建了一個 commit 物件,把這個 commit 物件的父級設置為 HEAD 指向的引用的 SHA-1 值。
347
+
348
+ 你也可以手動編輯這個檔,但是同樣有一個更安全的方法可以這樣做:`symbolic-ref`。你可以用下面這條命令讀取 HEAD 的值:
349
+
350
+ $ git symbolic-ref HEAD
351
+ refs/heads/master
352
+
353
+ 你也可以設置 HEAD 的值:
354
+
355
+ $ git symbolic-ref HEAD refs/heads/test
356
+ $ cat .git/HEAD
357
+ ref: refs/heads/test
358
+
359
+ 但是你不能設置成 refs 以外的形式:
360
+
361
+ $ git symbolic-ref HEAD test
362
+ fatal: Refusing to point HEAD outside of refs/
363
+
364
+ ### Tags ###
365
+
366
+ 你剛剛已經重溫過了 Git 的三個主要物件類型,現在這是第四種。Tag 物件非常像一個 commit 物件——包含一個標籤,一組資料,一個消息和一個指標。最主要的區別就是 Tag 物件指向一個 commit 而不是一個 tree。它就像是一個分支引用,但是不會變化——永遠指向同一個 commit,僅僅是提供一個更加友好的名字。
367
+
368
+ 正如我們在第二章所討論的,Tag 有兩種類型:annotated 和 lightweight 。你可以類似下面這樣的命令建立一個 lightweight tag:
369
+
370
+ $ git update-ref refs/tags/v1.0 cac0cab538b970a37ea1e769cbbde608743bc96d
371
+
372
+ 這就是 lightweight tag 的全部 —— 一個永遠不會發生變化的分支。 annotated tag 要更複雜一點。如果你創建一個 annotated tag,Git 會創建一個 tag 物件,然後寫入一個 reference 指向這個 tag,而不是直接指向 commit。你可以這樣創建一個 annotated tag(`-a` 參數表明這是一個 annotated tag):
373
+
374
+ $ git tag -a v1.1 1a410efbd13591db07496601ebc7a059dd55cfe9 –m 'test tag'
375
+
376
+ 這是所創建物件的 SHA-1 值:
377
+
378
+ $ cat .git/refs/tags/v1.1
379
+ 9585191f37f7b0fb9444f35a9bf50de191beadc2
380
+
381
+ 現在你可以執行 `cat-file` 命令檢查這個 SHA-1 值:
382
+
383
+ $ git cat-file -p 9585191f37f7b0fb9444f35a9bf50de191beadc2
384
+ object 1a410efbd13591db07496601ebc7a059dd55cfe9
385
+ type commit
386
+ tag v1.1
387
+ tagger Scott Chacon <schacon@gmail.com> Sat May 23 16:48:58 2009 -0700
388
+
389
+ test tag
390
+
391
+ 值得注意的是這個物件指向你所標記的 commit 物件的 SHA-1 值。同時需要注意的是它並不是必須要指向一個 commit 物件;你可以標記任何 Git 物件。例如,在 Git 的原始程式碼裡,管理者添加了一個 GPG 公開金鑰(這是一個 blob 物件)對它做了一個標籤。你可以執行以下命令來查看
392
+
393
+ $ git cat-file blob junio-gpg-pub
394
+
395
+ Git 原始程式碼裡的公開金鑰. Linux kernel 也有一個不是指向 commit 物件的 tag —— 第一個 tag 是在導入原始程式碼的時候創建的,它指向初始 tree (initial tree,譯者注)。
396
+
397
+ ### Remotes ###
398
+
399
+ 你將會看到的第三種 reference 是 remote reference(遠端參照,譯者注)。如果你添加了一個 remote 然後推送代碼過去,Git 會把你最後一次推送到這個 remote 的每個分支的值都記錄在 `refs/remotes` 目錄下。例如,你可以添加一個叫做 `origin` 的 remote 然後把你的 `master` 分支推送上去:
400
+
401
+ $ git remote add origin git@github.com:schacon/simplegit-progit.git
402
+ $ git push origin master
403
+ Counting objects: 11, done.
404
+ Compressing objects: 100% (5/5), done.
405
+ Writing objects: 100% (7/7), 716 bytes, done.
406
+ Total 7 (delta 2), reused 4 (delta 1)
407
+ To git@github.com:schacon/simplegit-progit.git
408
+ a11bef0..ca82a6d master -> master
409
+
410
+ 然後查看 `refs/remotes/origin/master` 這個檔,你就會發現 `origin` remote 中的 `master` 分支就是你最後一次和伺服器的通信。
411
+
412
+ $ cat .git/refs/remotes/origin/master
413
+ ca82a6dff817ec66f44342007202690a93763949
414
+
415
+ Remote references 和分支(`refs/heads` references)的主要區別在於他們是不能被 check out 的。Git 把他們當作是標記了這些分支在伺服器上最後狀態的一種書簽。
416
+
417
+ ## Packfiles ##
418
+
419
+ 我們再來看一下 test Git 倉庫。目前為止,有 11 個物件 ── 4 個 blob,3 個 tree,3 個 commit 以及一個 tag:
420
+
421
+ $ find .git/objects -type f
422
+ .git/objects/01/55eb4229851634a0f03eb265b69f5a2d56f341 # tree 2
423
+ .git/objects/1a/410efbd13591db07496601ebc7a059dd55cfe9 # commit 3
424
+ .git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a # test.txt v2
425
+ .git/objects/3c/4e9cd789d88d8d89c1073707c3585e41b0e614 # tree 3
426
+ .git/objects/83/baae61804e65cc73a7201a7252750c76066a30 # test.txt v1
427
+ .git/objects/95/85191f37f7b0fb9444f35a9bf50de191beadc2 # tag
428
+ .git/objects/ca/c0cab538b970a37ea1e769cbbde608743bc96d # commit 2
429
+ .git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4 # 'test content'
430
+ .git/objects/d8/329fc1cc938780ffdd9f94e0d364e0ea74f579 # tree 1
431
+ .git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 # new.txt
432
+ .git/objects/fd/f4fc3344e67ab068f836878b6c4951e3b15f3d # commit 1
433
+
434
+ Git 用 zlib 壓縮檔案內容,因此這些檔並沒有佔用太多空間,所有檔加起來總共僅用了 925 位元組。接下去你將添加一些大檔以演示 Git 的一個很有意思的功能。將你之前用到過的 Grit 庫中的 repo.rb 檔加進去 ── 這個原始程式碼檔大小約為 12K:
435
+
436
+ $ curl -L https://raw.github.com/mojombo/grit/master/lib/grit/repo.rb > repo.rb
437
+ $ git add repo.rb
438
+ $ git commit -m 'added repo.rb'
439
+ [master 484a592] added repo.rb
440
+ 3 files changed, 459 insertions(+), 2 deletions(-)
441
+ delete mode 100644 bak/test.txt
442
+ create mode 100644 repo.rb
443
+ rewrite test.txt (100%)
444
+
445
+ 如果查看一下生成的 tree,可以看到 repo.rb 檔的 blob 物件的 SHA-1 值:
446
+
447
+ $ git cat-file -p master^{tree}
448
+ 100644 blob fa49b077972391ad58037050f2a75f74e3671e92 new.txt
449
+ 100644 blob 9bc1dc421dcd51b4ac296e3e5b6e2a99cf44391e repo.rb
450
+ 100644 blob e3f094f522629ae358806b17daf78246c27c007b test.txt
451
+
452
+ 然後可以用 `git cat-file` 命令查看這個物件有多大:
453
+
454
+ $ du -b .git/objects/9b/c1dc421dcd51b4ac296e3e5b6e2a99cf44391e
455
+ 4102 .git/objects/9b/c1dc421dcd51b4ac296e3e5b6e2a99cf44391e
456
+
457
+ 稍微修改一下些檔,看會發生些什麼:
458
+
459
+ $ echo '# testing' >> repo.rb
460
+ $ git commit -am 'modified repo a bit'
461
+ [master ab1afef] modified repo a bit
462
+ 1 files changed, 1 insertions(+), 0 deletions(-)
463
+
464
+ 查看這個 commit 生成的 tree,可以看到一些有趣的東西:
465
+
466
+ $ git cat-file -p master^{tree}
467
+ 100644 blob fa49b077972391ad58037050f2a75f74e3671e92 new.txt
468
+ 100644 blob 05408d195263d853f09dca71d55116663690c27c repo.rb
469
+ 100644 blob e3f094f522629ae358806b17daf78246c27c007b test.txt
470
+
471
+ blob 物件與之前的已經不同了。這說明雖然只是往一個 400 行的檔最後加入了一行內容,Git 卻用一個全新的物件來保存新的檔案內容:
472
+
473
+ $ du -b .git/objects/05/408d195263d853f09dca71d55116663690c27c
474
+ 4109 .git/objects/05/408d195263d853f09dca71d55116663690c27c
475
+
476
+ 你的磁片上有了兩個幾乎完全相同的 12K 的物件。如果 Git 只完整保存其中一個,並保存另一個物件的差異內容,豈不更好?
477
+
478
+ 事實上 Git 可以那樣做。Git 往磁片保存物件時預設使用的格式叫鬆散物件 (loose object) 格式。Git 時不時地將這些物件打包至一個叫 packfile 的二進位檔案以節省空間並提高效率。當倉庫中有太多的鬆散物件,或是手動執行 `git gc` 命令,或推送至遠端伺服器時,Git 都會這樣做。手動執行 `git gc` 命令讓 Git 將倉庫中的物件打包,並看看會發生些什麼:
479
+
480
+ $ git gc
481
+ Counting objects: 17, done.
482
+ Delta compression using 2 threads.
483
+ Compressing objects: 100% (13/13), done.
484
+ Writing objects: 100% (17/17), done.
485
+ Total 17 (delta 1), reused 10 (delta 0)
486
+
487
+ 查看一下 objects 目錄,會發現大部分物件都不在了,與此同時出現了兩個新檔:
488
+
489
+ $ find .git/objects -type f
490
+ .git/objects/71/08f7ecb345ee9d0084193f147cdad4d2998293
491
+ .git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
492
+ .git/objects/info/packs
493
+ .git/objects/pack/pack-7a16e4488ae40c7d2bc56ea2bd43e25212a66c45.idx
494
+ .git/objects/pack/pack-7a16e4488ae40c7d2bc56ea2bd43e25212a66c45.pack
495
+
496
+ 仍保留著的幾個物件是未被任何 commit 引用的 blob ── 在此例中是你之前創建的 “what is up, doc?” 和 “test content” 這兩個示例 blob。你從沒將他們添加至任何 commit,所以 Git 認為它們是懸而未決的,不會將它們打包進 packfile 。
497
+
498
+ 剩下的檔是新創建的 packfile 以及一個索引。packfile 檔包含了剛才從檔案系統中移除的所有物件。索引檔包含了 packfile 的偏移資訊(offset),這樣就可以快速定位任意一個指定物件。有意思的是執行 `gc` 命令前磁片上的物件大小約為 8K ,而這個新生成的 packfile 僅為 4K 大小。通過打包物件減少了一半磁片使用空間。
499
+
500
+ Git 是如何做到這點的?Git 打包物件時,會查找命名及尺寸相近的檔,並只保存檔案不同版本之間的差異內容。可以查看一下 packfile ,觀察它是如何節省空間的。`git verify-pack` 命令用於顯示已打包的內容:
501
+
502
+ $ git verify-pack -v \
503
+ .git/objects/pack/pack-7a16e4488ae40c7d2bc56ea2bd43e25212a66c45.idx
504
+ 0155eb4229851634a0f03eb265b69f5a2d56f341 tree 71 76 5400
505
+ 05408d195263d853f09dca71d55116663690c27c blob 12908 3478 874
506
+ 09f01cea547666f58d6a8d809583841a7c6f0130 tree 106 107 5086
507
+ 1a410efbd13591db07496601ebc7a059dd55cfe9 commit 225 151 322
508
+ 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a blob 10 19 5381
509
+ 3c4e9cd789d88d8d89c1073707c3585e41b0e614 tree 101 105 5211
510
+ 484a59275031909e19aadb7c92262719cfcdf19a commit 226 153 169
511
+ 83baae61804e65cc73a7201a7252750c76066a30 blob 10 19 5362
512
+ 9585191f37f7b0fb9444f35a9bf50de191beadc2 tag 136 127 5476
513
+ 9bc1dc421dcd51b4ac296e3e5b6e2a99cf44391e blob 7 18 5193 1 \
514
+ 05408d195263d853f09dca71d55116663690c27c
515
+ ab1afef80fac8e34258ff41fc1b867c702daa24b commit 232 157 12
516
+ cac0cab538b970a37ea1e769cbbde608743bc96d commit 226 154 473
517
+ d8329fc1cc938780ffdd9f94e0d364e0ea74f579 tree 36 46 5316
518
+ e3f094f522629ae358806b17daf78246c27c007b blob 1486 734 4352
519
+ f8f51d7d8a1760462eca26eebafde32087499533 tree 106 107 749
520
+ fa49b077972391ad58037050f2a75f74e3671e92 blob 9 18 856
521
+ fdf4fc3344e67ab068f836878b6c4951e3b15f3d commit 177 122 627
522
+ chain length = 1: 1 object
523
+ pack-7a16e4488ae40c7d2bc56ea2bd43e25212a66c45.pack: ok
524
+
525
+ 如果你還記得的話, `9bc1d` 這個 blob 是 repo.rb 檔的第一個版本,這個 blob 引用了 `05408` 這個 blob,即該檔的第二個版本。命令輸出內容的第三欄顯示的是物件大小,可以看到 `05408` 佔用了 12K 空間,而 `9bc1d` 僅為 7 位元組。非常有趣的是第二個版本才是完整保存檔案內容的物件,而第一個版本是以差異方式保存的 ── 這是因為大部分情況下需要快速訪問的是檔案的最新版本。
526
+
527
+ 最妙的是可以隨時進行重新封包。Git 自動定期對倉庫進行重新封包以節省空間。當然也可以手動執行 `git gc` 命令來這麼做。
528
+
529
+ ## The Refspec ##
530
+
531
+ 這本書讀到這裡,你已經使用過一些簡單的遠端分支到本地引用的映射方式了,這種映射可以更為複雜。
532
+ 假設你像這樣添加了一項遠端倉庫:
533
+
534
+ $ git remote add origin git@github.com:schacon/simplegit-progit.git
535
+
536
+ 它在你的 `.git/config` 檔中添加了一節,指定了遠程的名稱 (`origin`), 遠程倉庫的 URL 地址,和用於獲取(fetch)操作的 Refspec:
537
+
538
+ [remote "origin"]
539
+ url = git@github.com:schacon/simplegit-progit.git
540
+ fetch = +refs/heads/*:refs/remotes/origin/*
541
+
542
+ Refspec 的格式是一個可選的 `+` 號,接著是 `<src>:<dst>` 的格式,這裡 `<src>` 是遠端上的引用格式, `<dst>` 是將要記錄在本地的引用格式。可選的 `+` 號告訴 Git 在即使不能快速演進(fast-forward)的情況下,也去強制更新它。
543
+
544
+ 預設情況下 refspec 會被 `git remote add` 命令所自動產生,Git 會獲取遠端伺服器上 `refs/heads/` 下面的所有引用,並將它寫入到本地的 `refs/remotes/origin/`. 所以,如果遠端伺服器上有一個 `master` 分支,你在本地可以通過下面這種方式來取得它的歷史記錄:
545
+
546
+ $ git log origin/master
547
+ $ git log remotes/origin/master
548
+ $ git log refs/remotes/origin/master
549
+
550
+ 它們的作用都是相同的,因為 Git 把它們都擴展成 `refs/remotes/origin/master`.
551
+
552
+ 如果你想讓 Git 每次只拉取遠端的 `master` 分支,而不是遠端的所有分支,你可以把 fetch 這一行修改成這樣:
553
+
554
+ fetch = +refs/heads/master:refs/remotes/origin/master
555
+
556
+ 這是 `git fetch` 操作對這個遠端的預設 refspec 值。而如果你只想做一次該操作,也可以在命令列上指定這個 refspec. 例如可以這樣拉取遠端的 `master` 分支到本地的 `origin/mymaster` 分支:
557
+
558
+ $ git fetch origin master:refs/remotes/origin/mymaster
559
+
560
+ 你也可以在命令列上指定多個 refspec. 像這樣可以一次獲取遠端的多個分支:
561
+
562
+ $ git fetch origin master:refs/remotes/origin/mymaster \
563
+ topic:refs/remotes/origin/topic
564
+ From git@github.com:schacon/simplegit
565
+ ! [rejected] master -> origin/mymaster (non fast forward)
566
+ * [new branch] topic -> origin/topic
567
+
568
+ 在這個例子中, master 分支因為不是一個可以快速演進的引用而拉取操作被拒絕。你可以在 refspec 之前使用一個 `+` 號來 override 這種行為。
569
+
570
+ 你也可以在設定檔中指定多個 refspec. 如你想在每次獲取時都獲取 master 和 experiment 分支,就添加兩行:
571
+
572
+ [remote "origin"]
573
+ url = git@github.com:schacon/simplegit-progit.git
574
+ fetch = +refs/heads/master:refs/remotes/origin/master
575
+ fetch = +refs/heads/experiment:refs/remotes/origin/experiment
576
+
577
+ 但是這裡不能使用部分萬用字元,像這樣就是不合法的:
578
+
579
+ fetch = +refs/heads/qa*:refs/remotes/origin/qa*
580
+
581
+ 但是你可以使用命名空間來達到這個目的。如果你有一個 QA 團隊,他們推送一系列分支,你想每次獲取 master 分支和 QA 團隊 的所有分支,你可以使用這樣的配置段落(config section):
582
+
583
+ [remote "origin"]
584
+ url = git@github.com:schacon/simplegit-progit.git
585
+ fetch = +refs/heads/master:refs/remotes/origin/master
586
+ fetch = +refs/heads/qa/*:refs/remotes/origin/qa/*
587
+
588
+ 如果你的工作流程很複雜,有QA團隊推送的分支、開發人員推送的分支、和集成人員推送的分支,並且他們在遠端分支上協作,你可以採用這種方式為他們創建各自的命名空間。
589
+
590
+ ### 推送 Refspecs ###
591
+
592
+ 採用命名空間的方式確實很棒,但QA團隊第一次是如何將他們的分支推送到 `qa/` 空間裡面的呢?答案是你可以使用 refspec 來推送。
593
+
594
+ 如果QA團隊想把他們的 `master` 分支推送到遠端的 `qa/master` 分支上,可以這樣執行:
595
+
596
+ $ git push origin master:refs/heads/qa/master
597
+
598
+ 如果他們想讓 Git 每次運行 `git push origin` 時都這樣自動推送,他們可以在設定檔中添加 `push` 值:
599
+
600
+ [remote "origin"]
601
+ url = git@github.com:schacon/simplegit-progit.git
602
+ fetch = +refs/heads/*:refs/remotes/origin/*
603
+ push = refs/heads/master:refs/heads/qa/master
604
+
605
+ 這樣,就會讓 `git push origin` 預設就把本地的 `master` 分支推送到遠端的 `qa/master` 分支上。
606
+
607
+ ### 刪除 References ###
608
+
609
+ 你也可以使用 refspec 來刪除遠端的引用(references),是通過執行這樣的命令:
610
+
611
+ $ git push origin :topic
612
+
613
+ 因為 refspec 的格式是 `<src>:<dst>`, 通過把 `<src>` 部分留空的方式,這個意思是是把遠端的 topic 分支變成空,也就是刪除它。
614
+
615
+ ## 傳輸協議 ##
616
+
617
+ Git 可以用兩種主要的方式跨越兩個倉庫傳輸資料:基於 HTTP 協定之上,和 `file://`, `ssh://`, 和 `git://` 等智慧傳輸協議。這一節帶你快速流覽這兩種主要的協議操作過程。
618
+
619
+ ### 啞協議 ###
620
+
621
+ Git 基於 HTTP 之上傳輸通常被稱為啞協議,這是因為它在服務端不需要有針對 Git 特有的代碼。這個獲取過程僅僅是一系列 GET 請求,用戶端可以假定服務端的 Git 倉庫中的佈局。讓我們以 simplegit 倉庫為例來看看 `http-fetch` 的過程:
622
+
623
+ $ git clone http://github.com/schacon/simplegit-progit.git
624
+
625
+ 它做的第一件事情就是獲取 `info/refs` 檔。這個檔是在服務端運行了 `update-server-info` 所產生的,這也解釋了為什麼在服務端要想使用 HTTP 傳輸,必須要開啟 `post-receive` 鉤子(hook):
626
+
627
+ => GET info/refs
628
+ ca82a6dff817ec66f44342007202690a93763949 refs/heads/master
629
+
630
+ 現在你有一個遠端引用和 SHA 值的列表。下一步是尋找 HEAD 引用,這樣你就知道了在完成後,什麼應該被檢出到工作目錄:
631
+
632
+ => GET HEAD
633
+ ref: refs/heads/master
634
+
635
+ 這說明在完成獲取後,需要檢出(check out) `master` 分支。
636
+ 這時,已經可以開始漫遊操作(walking process)了。因為你的起點是在 `info/refs` 檔中所提到的 `ca82a6` commit 物件,你的開始操作就是獲取它:
637
+
638
+ => GET objects/ca/82a6dff817ec66f44342007202690a93763949
639
+ (179 bytes of binary data)
640
+
641
+ 然後你取回了這個物件 - 這在服務端是一個鬆散格式的物件,你使用的是靜態的 HTTP GET 請求獲取的。可以使用 zlib 解壓縮它,去除檔頭,查看它的 commmit 內容:
642
+
643
+ $ git cat-file -p ca82a6dff817ec66f44342007202690a93763949
644
+ tree cfda3bf379e4f8dba8717dee55aab78aef7f4daf
645
+ parent 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
646
+ author Scott Chacon <schacon@gmail.com> 1205815931 -0700
647
+ committer Scott Chacon <schacon@gmail.com> 1240030591 -0700
648
+
649
+ changed the version number
650
+
651
+ 這樣,就得到了兩個需要進一步獲取的物件 - `cfda3b` 是這個 commit 物件所對應的 tree 物件,和 `085bb3` 是它的父物件:
652
+
653
+ => GET objects/08/5bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
654
+ (179 bytes of data)
655
+
656
+ 這樣就取得了這它的下一步 commit 物件,再抓取 tree 物件:
657
+
658
+ => GET objects/cf/da3bf379e4f8dba8717dee55aab78aef7f4daf
659
+ (404 - Not Found)
660
+
661
+ 哎呀! - 看起來這個 tree 物件在服務端並不以鬆散格式對象存在,所以得到了404回應,代表在 HTTP 服務端沒有找到該物件。這有好幾個原因 - 這個物件可能在替代倉庫裡面,或者在打包檔裡面,Git 會首先檢查任何列出的替代倉庫:
662
+
663
+ => GET objects/info/http-alternates
664
+ (empty file)
665
+
666
+ 如果這回傳了幾個替代倉庫列表,那麼它會去那些地方檢查鬆散格式物件和檔案 - 這是一種在軟體分叉(forks)之間共用物件以節省磁碟的好方法。然而,在這個例子中,沒有替代倉庫。所以你所需要的物件肯定在某個打包檔中。要檢查服務端有哪些打包格式檔,你需要獲取 `objects/info/packs` 檔,這裡面包含有打包檔列表(是的,它也是被 `update-server-info` 所產生的):
667
+
668
+ => GET objects/info/packs
669
+ P pack-816a9b2334da9953e530f27bcac22082a9f5b835.pack
670
+
671
+ 這裡服務端只有一個打包檔,所以你要的物件顯然就在裡面。但是你可以先檢查它的索引檔以確認。這在服務端有多個打包檔時也很有用,因為這樣就可以先檢查你所需要的物件空間是在哪一個打包檔裡面了:
672
+
673
+ => GET objects/pack/pack-816a9b2334da9953e530f27bcac22082a9f5b835.idx
674
+ (4k of binary data)
675
+
676
+ 現在你有了這個打包檔的索引,你可以看看你要的物件是否在裡面 - 因為索引檔列出了這個打包檔所包含的所有物件的SHA值,和該物件存在於打包檔中的偏移量,所以你只需要簡單地獲取整個打包檔:
677
+
678
+ => GET objects/pack/pack-816a9b2334da9953e530f27bcac22082a9f5b835.pack
679
+ (13k of binary data)
680
+
681
+ 現在你也有了這個 tree 物件,你可以繼續在 commit 物件上漫遊。它們全部都在這個你已經下載到的打包檔裡面,所以你不用繼續向服務端請求更多下載了。在這完成之後,由於下載開始時已探明 HEAD 引用是指向 `master` 分支,Git 會將它檢出到工作目錄。
682
+
683
+ 整個過程看起來就像這樣:
684
+
685
+ $ git clone http://github.com/schacon/simplegit-progit.git
686
+ Initialized empty Git repository in /private/tmp/simplegit-progit/.git/
687
+ got ca82a6dff817ec66f44342007202690a93763949
688
+ walk ca82a6dff817ec66f44342007202690a93763949
689
+ got 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
690
+ Getting alternates list for http://github.com/schacon/simplegit-progit.git
691
+ Getting pack list for http://github.com/schacon/simplegit-progit.git
692
+ Getting index for pack 816a9b2334da9953e530f27bcac22082a9f5b835
693
+ Getting pack 816a9b2334da9953e530f27bcac22082a9f5b835
694
+ which contains cfda3bf379e4f8dba8717dee55aab78aef7f4daf
695
+ walk 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
696
+ walk a11bef06a3f659402fe7563abf99ad00de2209e6
697
+
698
+ ### 智慧協議 ###
699
+
700
+ HTTP 方法是很簡單但效率不是很高。使用智慧協定是傳送資料的更常用的方法。這些協定在遠端都有 Git 智慧型程序(process)在服務 - 它可以讀出本地資料並計算出用戶端所需要的,並產生合適的資料給它,這有兩類傳輸資料的程序:一對用於上傳資料和一對用於下載。
701
+
702
+ #### 上傳資料 ####
703
+
704
+ 為了上傳資料至遠端,Git 使用 `send-pack` 和 `receive-pack` 程序。這個 `send-pack` 程序運行在用戶端上,它連接至遠端運行的 `receive-pack` 程序。
705
+
706
+ 舉例來說,你在你的專案上執行了 `git push origin master`, 並且 `origin` 被定義為一個使用 SSH 協議的 URL。Git 會使用 `send-pack` 程序,它會啟動一個基於 SSH 的連接(connection)到伺服器。它嘗試像這樣透過 SSH 在服務端運行命令:
707
+
708
+ $ ssh -x git@github.com "git-receive-pack 'schacon/simplegit-progit.git'"
709
+ 005bca82a6dff817ec66f4437202690a93763949 refs/heads/master report-status delete-refs
710
+ 003e085bb3bcb608e1e84b2432f8ecbe6306e7e7 refs/heads/topic
711
+ 0000
712
+
713
+ 這裡的 `git-receive-pack` 命令會立即對它所擁有的每一個引用回應一行 - 在這個例子中,只有 `master` 分支和它的SHA值。這裡第1行也包含了服務端的能力清單(這裡是 `report-status` 和 `delete-refs`)。
714
+
715
+ 每一行以4位元組的十六進位開始,用於指定整行的長度。你看到第1行以005b開始,這在十六進位中表示91,意味著第1行有91位元組長。下一行以003e起始,表示有62位元組長,所以需要讀剩下的62位元組。再下一行是0000開始,表示伺服器已完成了引用列表過程。
716
+
717
+ 現在它知道了服務端的狀態,你的 `send-pack` 程序會判斷哪些 commit 是它所擁有但服務端沒有的。針對每個引用,這次推送都會告訴對端的 `receive-pack` 這個資訊。舉例說,如果你在更新 `master` 分支,並且增加 `experiment` 分支,這個 `send-pack` 將會是像這樣:
718
+
719
+ 0085ca82a6dff817ec66f44342007202690a93763949 15027957951b64cf874c3557a0f3547bd83b3ff6 refs/heads/master report-status
720
+ 00670000000000000000000000000000000000000000 cdfdb42577e2506715f8cfeacdbabc092bf63e8d refs/heads/experiment
721
+ 0000
722
+
723
+ 這裡全部是’0’的SHA-1值表示之前沒有過這個物件 - 因為你是在添加新的 experiment 引用。如果你在刪除一個引用,你會看到相反的: 就是右邊全部是’0’。
724
+
725
+ Git 針對每個引用發送這樣一行資訊,就是舊的SHA值,新的SHA值,和將要更新的引用的名稱。第1行還會包含有用戶端的能力。下一步,用戶端會發送一個所有那些服務端所沒有的物件的一個打包檔。最後,服務端以成功(或者失敗)來回應:
726
+
727
+ 000Aunpack ok
728
+
729
+ #### 下載資料 ####
730
+
731
+ 當你在下載資料時,`fetch-pack` 和 `upload-pack` 程序就起作用了。用戶端啟動 `fetch-pack` 程序,連接至遠端的 `upload-pack` 程序,以協商後續資料傳輸過程。
732
+
733
+ 在遠端倉庫有不同的方式啟動 `upload-pack` 程序。你可以使用與 `receive-pack` 相同的透過 SSH 管道的方式,也可以通過 Git 後臺來啟動這個進程,它預設監聽在 9418 號埠上。這裡 `fetch-pack` 程序在連接後像這樣向後臺發送資料:
734
+
735
+ 003fgit-upload-pack schacon/simplegit-progit.git\0host=myserver.com\0
736
+
737
+ 它也是以4位元組指定後續位元組長度的方式開始,然後是要執行的命令,和一個空位元組,然後是服務端的主機名稱,再跟隨一個最後的空位元組。Git 後臺程序會檢查這個命令是否可以執行,以及那個倉庫是否存在,以及是否具有公開許可權。如果所有檢查都通過了,它會啟動這個 `upload-pack` 程序並將用戶端的請求移交給它。
738
+
739
+ 如果你透過 SSH 使用獲取(fetch)功能,`fetch-pack` 會像這樣運行:
740
+
741
+ $ ssh -x git@github.com "git-upload-pack 'schacon/simplegit-progit.git'"
742
+
743
+ 不管哪種方式,在 `fetch-pack` 連接之後, `upload-pack` 都會以這種形式回傳:
744
+
745
+ 0088ca82a6dff817ec66f44342007202690a93763949 HEAD\0multi_ack thin-pack \
746
+ side-band side-band-64k ofs-delta shallow no-progress include-tag
747
+ 003fca82a6dff817ec66f44342007202690a93763949 refs/heads/master
748
+ 003e085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 refs/heads/topic
749
+ 0000
750
+
751
+ 這與 `receive-pack` 回應很類似,但是這裡指的能力是不同的。而且它還會指出 HEAD 引用,讓用戶端可以檢查是否是一份 clone。
752
+
753
+ 在這裡,`fetch-pack` 程序檢查它自己所擁有的物件和所有它需要的物件,通過發送 “want” 和所需物件的 SHA 值,發送 “have” 和所有它已擁有的物件的 SHA 值。在列表完成時,再發送 “done” 通知 `upload-pack` 程序開始發送所需物件的打包檔。這個過程看起來像這樣:
754
+
755
+ 0054want ca82a6dff817ec66f44342007202690a93763949 ofs-delta
756
+ 0032have 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
757
+ 0000
758
+ 0009done
759
+
760
+ 這是傳輸協議的一個很基礎的例子,在更複雜的例子中,用戶端可能會支援 `multi_ack` 或者 `side-band` 能力;但是這個例子中展示了智慧協議的基本交互過程。
761
+
762
+ ## 維護及資料復原 ##
763
+
764
+ 偶爾,你可能需要進行一些清理工作 ── 如減小一個倉庫的大小,清理導入的倉庫,或是恢復丟失的資料。本節將描述這類使用場景。
765
+
766
+ ### 維護 ###
767
+
768
+ Git 會不定時地自動執行稱為「auto gc」的命令。大部分情況下該命令什麼都不處理。不過要是存在太多鬆散物件 (loose object, 不在 packfile 中的物件) 或 packfile,Git 會執行 `git gc` 命令。`gc` 指垃圾收集 (garbage collect),此命令會做很多工作:收集所有鬆散物件並將它們存入 packfile,合併這些 packfile 進一個大的 packfile,然後將不被任何 commit 引用並且已存在一段時間 (數月) 的物件刪除。
769
+
770
+ 可以如下手動執行 auto gc 命令:
771
+
772
+ $ git gc --auto
773
+
774
+ 再次強調,這個命令一般什麼都不幹。如果有 7,000 個左右的鬆散對象或是 50 個以上的 packfile,Git 才會真正觸發 gc 命令。你可以修改配置中的 `gc.auto` 和 `gc.autopacklimit` 來調整這兩個設定值。
775
+
776
+ `gc` 還會將所有引用 (references) 併入一個單獨檔。假設倉庫中包含以下分支和標籤:
777
+
778
+ $ find .git/refs -type f
779
+ .git/refs/heads/experiment
780
+ .git/refs/heads/master
781
+ .git/refs/tags/v1.0
782
+ .git/refs/tags/v1.1
783
+
784
+ 這時如果執行 `git gc`, `refs` 下的所有檔都會消失。Git 會將這些檔挪到 `.git/packed-refs` 檔中去以提高效率,該檔是這個樣子的:
785
+
786
+ $ cat .git/packed-refs
787
+ # pack-refs with: peeled
788
+ cac0cab538b970a37ea1e769cbbde608743bc96d refs/heads/experiment
789
+ ab1afef80fac8e34258ff41fc1b867c702daa24b refs/heads/master
790
+ cac0cab538b970a37ea1e769cbbde608743bc96d refs/tags/v1.0
791
+ 9585191f37f7b0fb9444f35a9bf50de191beadc2 refs/tags/v1.1
792
+ ^1a410efbd13591db07496601ebc7a059dd55cfe9
793
+
794
+ 當更新一個引用時,Git 不會修改這個檔,而是在 `refs/heads` 下寫入一個新檔。當查找一個引用的 SHA 時,Git 首先在 `refs` 目錄下查找,如果未找到則到 `packed-refs` 檔中去查找。因此如果在 `refs` 目錄下找不到一個引用,該引用可能存到 `packed-refs` 檔中去了。
795
+
796
+ 請留意檔最後以 `^` 開頭的那一行。這表示該行上一行的那個標籤是一個 annotated 標籤,而該行正是那個標籤所指向的 commit 。
797
+
798
+ ### 資料復原 ###
799
+
800
+ 在使用 Git 的過程中,有時會不小心丟失 commit 資訊。這一般出現在以下情況下:強制刪除了一個分支而後又想重新使用這個分支,hard-reset 了一個分支從而丟棄了分支的部分 commit。如果這真的發生了,有什麼辦法把丟失的 commit 找回來呢?
801
+
802
+ 下面的例子演示了對 test 倉庫 master 分支進行 hard-reset 到一個老版本的 commit 的操作,然後恢復丟失的 commit 。首先查看一下當前的倉庫狀態:
803
+
804
+ $ git log --pretty=oneline
805
+ ab1afef80fac8e34258ff41fc1b867c702daa24b modified repo a bit
806
+ 484a59275031909e19aadb7c92262719cfcdf19a added repo.rb
807
+ 1a410efbd13591db07496601ebc7a059dd55cfe9 third commit
808
+ cac0cab538b970a37ea1e769cbbde608743bc96d second commit
809
+ fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
810
+
811
+ 接著將 `master` 分支移回至中間的一個 commit:
812
+
813
+ $ git reset --hard 1a410efbd13591db07496601ebc7a059dd55cfe9
814
+ HEAD is now at 1a410ef third commit
815
+ $ git log --pretty=oneline
816
+ 1a410efbd13591db07496601ebc7a059dd55cfe9 third commit
817
+ cac0cab538b970a37ea1e769cbbde608743bc96d second commit
818
+ fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
819
+
820
+ 這樣就丟棄了最新的兩個 commit ── 包含這兩個 commit 的分支不存在了。現在要做的是找出最新的那個 commit 的 SHA,然後添加一個指向它的分支。關鍵在於找出最新的 commit 的 SHA ── 你不大可能記住了這個 SHA,是吧?
821
+
822
+ 通常最快捷的辦法是使用 `git reflog` 工具。當你 (在一個倉庫下) 工作時,Git 會在你每次修改了 HEAD 時悄悄地將改動記錄下來。當你提交或修改分支時,reflog 就會更新。`git update-ref` 命令也可以更新 reflog,這是在本章前面的 “Git References” 部分我們使用該命令而不是手工將 SHA 值寫入 ref 文件的理由。任何時間執行 `git reflog` 命令可以查看當前的狀態:
823
+
824
+ $ git reflog
825
+ 1a410ef HEAD@{0}: 1a410efbd13591db07496601ebc7a059dd55cfe9: updating HEAD
826
+ ab1afef HEAD@{1}: ab1afef80fac8e34258ff41fc1b867c702daa24b: updating HEAD
827
+
828
+ 可以看到我們 check out 的兩個 commit ,但沒有更多的相關資訊。執行 `git log -g` 會輸出 reflog 的正常日誌,從而顯示更多有用資訊:
829
+
830
+ $ git log -g
831
+ commit 1a410efbd13591db07496601ebc7a059dd55cfe9
832
+ Reflog: HEAD@{0} (Scott Chacon <schacon@gmail.com>)
833
+ Reflog message: updating HEAD
834
+ Author: Scott Chacon <schacon@gmail.com>
835
+ Date: Fri May 22 18:22:37 2009 -0700
836
+
837
+ third commit
838
+
839
+ commit ab1afef80fac8e34258ff41fc1b867c702daa24b
840
+ Reflog: HEAD@{1} (Scott Chacon <schacon@gmail.com>)
841
+ Reflog message: updating HEAD
842
+ Author: Scott Chacon <schacon@gmail.com>
843
+ Date: Fri May 22 18:15:24 2009 -0700
844
+
845
+ modified repo a bit
846
+
847
+ 看起來弄丟了的 commit 是底下那個,這樣在那個 commit 上創建一個新分支就能把它恢復過來。比方說,可以在那個 commit (ab1afef) 上創建一個名為 `recover-branch` 的分支:
848
+
849
+ $ git branch recover-branch ab1afef
850
+ $ git log --pretty=oneline recover-branch
851
+ ab1afef80fac8e34258ff41fc1b867c702daa24b modified repo a bit
852
+ 484a59275031909e19aadb7c92262719cfcdf19a added repo.rb
853
+ 1a410efbd13591db07496601ebc7a059dd55cfe9 third commit
854
+ cac0cab538b970a37ea1e769cbbde608743bc96d second commit
855
+ fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
856
+
857
+ 酷!這樣有了一個跟原來 `master` 一樣的 `recover-branch` 分支,最新的兩個 commit 又找回來了。
858
+ 接著,假設引起 commit 丟失的原因並沒有記錄在 reflog 中 ── 可以通過刪除 `recover-branch` 和 reflog 來類比這種情況。這樣最新的兩個 commit 不會被任何東西引用到:
859
+
860
+ $ git branch -D recover-branch
861
+ $ rm -Rf .git/logs/
862
+
863
+ 因為 reflog 資料是保存在 `.git/logs/` 目錄下的,這樣就沒有 reflog 了。現在要怎樣恢復 commit 呢?辦法之一是使用 `git fsck` 工具,該工具會檢查倉庫的資料完整性。如果指定 `--full` 選項,該命令顯示所有未被其他物件引用 (指向) 的所有物件:
864
+
865
+ $ git fsck --full
866
+ dangling blob d670460b4b4aece5915caf5c68d12f560a9fe3e4
867
+ dangling commit ab1afef80fac8e34258ff41fc1b867c702daa24b
868
+ dangling tree aea790b9a58f6cf6f2804eeac9f0abbe9631e4c9
869
+ dangling blob 7108f7ecb345ee9d0084193f147cdad4d2998293
870
+
871
+ 本例中,可以從 dangling commit 找到丟失了的 commit。用相同的方法就可以恢復它,即創建一個指向該 SHA 的分支。
872
+
873
+ ### 移除物件 ###
874
+
875
+ Git 有許多過人之處,不過有一個功能有時卻會帶來問題:`git clone` 會將包含每一個檔的所有歷史版本的整個專案下載下來。如果專案包含的僅僅是原始程式碼的話這並沒有什麼壞處,畢竟 Git 可以非常高效地壓縮此類資料。不過如果有人在某個時刻往專案中添加了一個非常大的檔,即便他在後來的提交中將此檔刪掉了,所有的簽出都會下載這個大檔。因為歷史記錄中引用了這個檔,它會一直存在著。
876
+
877
+ 當你將 Subversion 或 Perforce 倉庫轉換導入至 Git 時這會成為一個很嚴重的問題。在此類系統中,(簽出時) 不會下載整個倉庫歷史,所以這種情形不大會有不良後果。如果你從其他系統導入了一個倉庫,或是發覺一個倉庫的尺寸遠超出預計,可以用下面的方法找到並移除大 (尺寸) 物件。
878
+
879
+ 警告:此方法會破壞提交歷史。為了移除對一個大檔的引用,從最早包含該引用的 tree 物件開始之後的所有 commit 物件都會被重寫。如果在剛導入一個倉庫並在其他人在此基礎上開始工作之前這麼做,那沒有什麼問題 ── 否則你不得不通知所有協作者 (貢獻者) 去衍合你新修改的 commit 。
880
+
881
+ 為了演示這點,往 test 倉庫中加入一個大檔,然後在下次提交時將它刪除,接著找到並將這個檔從倉庫中永久刪除。首先,加一個大檔進去:
882
+
883
+ $ curl http://kernel.org/pub/software/scm/git/git-1.6.3.1.tar.bz2 > git.tbz2
884
+ $ git add git.tbz2
885
+ $ git commit -am 'added git tarball'
886
+ [master 6df7640] added git tarball
887
+ 1 files changed, 0 insertions(+), 0 deletions(-)
888
+ create mode 100644 git.tbz2
889
+
890
+ 喔,你並不想往專案中加進一個這麼大的 tar 包。最後還是去掉它:
891
+
892
+ $ git rm git.tbz2
893
+ rm 'git.tbz2'
894
+ $ git commit -m 'oops - removed large tarball'
895
+ [master da3f30d] oops - removed large tarball
896
+ 1 files changed, 0 insertions(+), 0 deletions(-)
897
+ delete mode 100644 git.tbz2
898
+
899
+ 對倉庫進行 `gc` 操作,並查看佔用了空間:
900
+
901
+ $ git gc
902
+ Counting objects: 21, done.
903
+ Delta compression using 2 threads.
904
+ Compressing objects: 100% (16/16), done.
905
+ Writing objects: 100% (21/21), done.
906
+ Total 21 (delta 3), reused 15 (delta 1)
907
+
908
+ 可以執行 `count-objects` 以查看使用了多少空間:
909
+
910
+ $ git count-objects -v
911
+ count: 4
912
+ size: 16
913
+ in-pack: 21
914
+ packs: 1
915
+ size-pack: 2016
916
+ prune-packable: 0
917
+ garbage: 0
918
+
919
+ `size-pack` 是以 KB 為單位表示的 packfiles 的大小,因此已經使用了 2MB 。而在這次提交之前僅用了 2K 左右 ── 顯然在這次提交時刪除檔並沒有真正將其從歷史記錄中刪除。每當有人複製這個倉庫去取得這個小專案時,都不得不複製所有 2MB 資料,而這僅僅因為你曾經不小心加了個大檔。讓我們來解決這個問題。
920
+
921
+ 首先要找出這個檔。在本例中,你知道是哪個文件。假設你並不知道這一點,要如何找出哪個 (些) 文件佔用了這麼多的空間?如果執行 `git gc`,所有物件會存入一個 packfile 檔;執行另一個底層命令 `git verify-pack` 以識別出大物件,對輸出的第三欄資訊即檔案大小進行排序,還可以將輸出定向(pipe)到 `tail` 命令,因為你只關心排在最後的那幾個最大的檔:
922
+
923
+ $ git verify-pack -v .git/objects/pack/pack-3f8c0...bb.idx | sort -k 3 -n | tail -3
924
+ e3f094f522629ae358806b17daf78246c27c007b blob 1486 734 4667
925
+ 05408d195263d853f09dca71d55116663690c27c blob 12908 3478 1189
926
+ 7a9eb2fba2b1811321254ac360970fc169ba2330 blob 2056716 2056872 5401
927
+
928
+ 最底下那個就是那個大檔:2MB 。要查看這到底是哪個檔,可以使用第 7 章中已經簡單使用過的 `rev-list` 命令。若給 `rev-list` 命令傳入 `--objects` 選項,它會列出所有 commit SHA 值,blob SHA 值及相應的檔路徑。可以這樣查看 blob 的檔案名:
929
+
930
+ $ git rev-list --objects --all | grep 7a9eb2fb
931
+ 7a9eb2fba2b1811321254ac360970fc169ba2330 git.tbz2
932
+
933
+ 接下來要將該檔從歷史記錄的所有 tree 中移除。很容易找出哪些 commit 修改了這個檔:
934
+
935
+ $ git log --pretty=oneline --branches -- git.tbz2
936
+ da3f30d019005479c99eb4c3406225613985a1db oops - removed large tarball
937
+ 6df764092f3e7c8f5f94cbe08ee5cf42e92a0289 added git tarball
938
+
939
+ 必須重寫從 `6df76` 開始的所有 commit 才能將檔從 Git 歷史中完全移除。這麼做需要用到第 6 章中用過的 `filter-branch` 命令:
940
+
941
+ $ git filter-branch --index-filter \
942
+ 'git rm --cached --ignore-unmatch git.tbz2' -- 6df7640^..
943
+ Rewrite 6df764092f3e7c8f5f94cbe08ee5cf42e92a0289 (1/2)rm 'git.tbz2'
944
+ Rewrite da3f30d019005479c99eb4c3406225613985a1db (2/2)
945
+ Ref 'refs/heads/master' was rewritten
946
+
947
+ `--index-filter` 選項類似於第 6 章中使用的 `--tree-filter` 選項,但這裡不是傳入一個命令去修改磁碟上 checked out 的檔,而是修改暫存區域或索引。不能用 `rm file` 命令來刪除一個特定檔,而是必須用 `git rm --cached` 來刪除它 ── 也就是說,從索引而不是從磁碟上刪除它。這樣做是出於速度考慮 ── 由於 Git 在執行你的 filter 之前無需將所有版本簽出到磁片上,這個操作會快得多。也可以用 `--tree-filter` 來完成相同的操作。`git rm` 的 `--ignore-unmatch` 選項指定當你試圖刪除的內容並不存在時不顯示錯誤。最後,因為你清楚問題是從哪個 commit 開始的,使用 `filter-branch` 重寫自 `6df7640` 這個 commit 開始的所有歷史記錄。不這麼做的話會重寫所有歷史記錄,花費不必要的更多時間。
948
+
949
+ 現在歷史記錄中已經不包含對那個檔的引用了。不過 reflog 以及執行 `filter-branch` 時 Git 往 `.git/refs/original` 添加的一些 refs 中仍有對它的引用,因此需要將這些引用刪除並對倉庫進行 repack 操作。在進行 repack 前需要將所有對這些 commits 的引用去除:
950
+
951
+ $ rm -Rf .git/refs/original
952
+ $ rm -Rf .git/logs/
953
+ $ git gc
954
+ Counting objects: 19, done.
955
+ Delta compression using 2 threads.
956
+ Compressing objects: 100% (14/14), done.
957
+ Writing objects: 100% (19/19), done.
958
+ Total 19 (delta 3), reused 16 (delta 1)
959
+
960
+ 看一下節省了多少空間。
961
+
962
+ $ git count-objects -v
963
+ count: 8
964
+ size: 2040
965
+ in-pack: 19
966
+ packs: 1
967
+ size-pack: 7
968
+ prune-packable: 0
969
+ garbage: 0
970
+
971
+ repack 後倉庫的大小減小到了 7K ,遠小於之前的 2MB 。從 size 值可以看出大檔物件還在鬆散物件中,其實並沒有消失,不過這沒有關係,重要的是在再進行推送或複製,這個物件不會再傳送出去。如果真的要完全把這個物件刪除,可以運行 `git prune --expire` 命令。
972
+
973
+ ## 總結 ##
974
+
975
+ 現在你應該對 Git 可以作什麼相當瞭解了,並且在一定程度上也知道了 Git 是如何實現的。本章涵蓋了許多 plumbing 命令 ── 這些命令比較底層,且比你在本書其他部分學到的 porcelain 命令要來得簡單。從底層瞭解 Git 的工作原理可以幫助你更好地理解為何 Git 實現了目前的這些功能,也使你能夠針對你的工作流程寫出自己的工具和腳本。
976
+
977
+ Git 作為一套 content-addressable 的檔案系統,是一個非常強大的工具,而不僅僅只是一個 VCS。希望借助於你新學到的 Git 內部原理的知識,你可以自己實做出有趣的應用,並以更進階的方式、更如魚得水的使用 Git。