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,1126 @@
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 достаточно умён для того, чтобы понять какой коммит вы имеете в виду по первым нескольким символам (частичному хешу), конечно, если их не меньше четырёх и они однозначны, то есть если хеш только одного объекта в вашем репозитории начинается с этих символов.
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`, то её вывод будет использовать сокращённые значения, сохраняя их уникальными; по умолчанию будут использоваться семь символов, но при необходимости длина будет увеличена для сохранения однозначности хешей:
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
+ В общем случае восемь-десять символов более чем достаточно для уникальности внутри проекта. В одном из самых больших проектов, использующих Git, ядре Linux только начинает появляться необходимость использовать 12 символов из 40 возможных для сохранения уникальности.
55
+
56
+ ### Небольшое замечание о SHA-1 ###
57
+
58
+ Многие люди интересуются, что произойдёт, если они в какой-то момент, по некоторой случайности, получат два объекта в репозитории, которые будут иметь два одинаковых значения SHA-1 хеша. Что тогда?
59
+
60
+ Если вы вдруг закоммитите объект, SHA-1 хеш которого такой же, как у некоторого предыдущего объекта в вашем репозитории, 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 коллизию. Если бы все 6.5 миллиардов людей на Земле программировали и каждую секунду каждый из них производил количество кода, эквивалентное всей истории ядра Linux (1 миллион Git-объектов) и отправлял его в один огромный Git-репозиторий, то потребовалось бы 5 лет для того, чтобы заполнить репозиторий достаточно для того, чтобы получить 50% вероятность единичной SHA-1 коллизии. Более вероятно, что каждый член вашей команды программистов будет атакован и убит волками в несвязанных друг с другом случаях в одну и ту же ночь.
65
+
66
+ ### Ссылки на ветки ###
67
+
68
+ Для самого прямого метода указать коммит необходимо, чтобы этот коммит имел ветку, ссылающуюся на него. Тогда вы можете использовать имя ветки в любой команде Git'а, которая ожидает коммит или значение SHA-1. Например, если вы хотите посмотреть последний коммит в ветке, следующие команды эквивалентны, предполагая, что ветка `topic1` ссылается на `ca82a6d`:
69
+
70
+ $ git show ca82a6dff817ec66f44342007202690a93763949
71
+ $ git show topic1
72
+
73
+ Чтобы посмотреть, на какой именно SHA указывает ветка, или понять для какого-то из приведённых примеров, к каким SHA он сводится, можно использовать служебную (plumbing) утилиту Git'а, которая называется `rev-parse`. Вы можете заглянуть в главу 9 для получения более подробной информации о служебных утилитах; в основном `rev-parse` нужна для выполнения низкоуровневых операций и не предназначена для использования в повседневной работе. Однако, она может пригодиться, если вам необходимо разобраться, что происходит на самом деле. Сейчас вы можете попробовать применить `rev-parse` к своей ветке.
74
+
75
+ $ git rev-parse topic1
76
+ ca82a6dff817ec66f44342007202690a93763949
77
+
78
+ ### RefLog-сокращения ###
79
+
80
+ Одна из вещей, которую Git делает в фоновом режиме, пока вы работаете, это запоминание ссылочного лога — лога того, где находились 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}` так же, как показано в выводе команды `reflog`:
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
+ Важно отметить, что информация в ссылочном логе строго локальная — это лог того, чем вы занимались со своим репозиторием. Ссылки не будут теми же самыми в чьей-то чужой копии репозитория; и после того как вы только что склонировали репозиторий, ссылочный лог будет пустым, так как вы ещё ничего не делали со своим репозиторием. Команда `git show HEAD@{2.months.ago}` сработает, только если вы склонировали свой проект как минимум два месяца назад. Если вы склонировали его пять минут назад, то вы ничего не получите.
123
+
124
+ ### Ссылки на предков ###
125
+
126
+ Ещё один основной способ указать коммит — указать коммит через его предков. Если поставить `^` в конце ссылки, для Git'а это будет означать родителя этого коммита.
127
+ Допустим, история вашего проекта выглядит следующим образом:
128
+
129
+ $ git log --pretty=format:'%h %s' --graph
130
+ * 734713b fixed refs handling, added gc auto, updated tests
131
+ * d921970 Merge commit 'phedders/rdocs'
132
+ |\
133
+ | * 35cfb2b Some rdoc changes
134
+ * | 1c002dd added some blame and merge stuff
135
+ |/
136
+ * 1c36188 ignore *.gem
137
+ * 9b29157 add open3_detach to gemspec file list
138
+
139
+ В этом случае вы можете посмотреть предыдущий коммит, указав `HEAD^`, что означает "родитель HEAD":
140
+
141
+ $ git show HEAD^
142
+ commit d921970aadf03b3cf0e71becdaab3147ba71cdef
143
+ Merge: 1c002dd... 35cfb2b...
144
+ Author: Scott Chacon <schacon@gmail.com>
145
+ Date: Thu Dec 11 15:08:43 2008 -0800
146
+
147
+ Merge commit 'phedders/rdocs'
148
+
149
+ Вы также можете указать некоторое число после `^`. Например, `d921970^2` означает "второй родитель коммита d921970". Такой синтаксис полезен только для коммитов-слияний, которые имеют больше, чем одного родителя. Первый родитель — это ветка, на которой вы находились во время слияния, а второй — коммит на ветке, которая была слита:
150
+
151
+ $ git show d921970^
152
+ commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b
153
+ Author: Scott Chacon <schacon@gmail.com>
154
+ Date: Thu Dec 11 14:58:32 2008 -0800
155
+
156
+ added some blame and merge stuff
157
+
158
+ $ git show d921970^2
159
+ commit 35cfb2b795a55793d7cc56a6cc2060b4bb732548
160
+ Author: Paul Hedderly <paul+git@mjr.org>
161
+ Date: Wed Dec 10 22:22:03 2008 +0000
162
+
163
+ Some rdoc changes
164
+
165
+ Другое основное обозначение для указания на предков — это `~`. Это тоже ссылка на первого родителя, поэтому `HEAD~` и `HEAD^` эквивалентны. Различия становятся очевидными, только когда вы указываете число. `HEAD~2` означает первого родителя первого родителя HEAD или прародителя — это переход по первым родителям указанное количество раз. Например, для показанной выше истории, `HEAD~3` будет
166
+
167
+ $ git show HEAD~3
168
+ commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d
169
+ Author: Tom Preston-Werner <tom@mojombo.com>
170
+ Date: Fri Nov 7 13:47:59 2008 -0500
171
+
172
+ ignore *.gem
173
+
174
+ То же самое можно записать как `HEAD^^^`, что опять же означает первого родителя первого родителя первого родителя:
175
+
176
+ $ git show HEAD^^^
177
+ commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d
178
+ Author: Tom Preston-Werner <tom@mojombo.com>
179
+ Date: Fri Nov 7 13:47:59 2008 -0500
180
+
181
+ ignore *.gem
182
+
183
+ Кроме того, можно комбинировать эти обозначения. Например, можно получить второго родителя для предыдущей ссылки (мы предполагаем, что это коммит-слияние) написав `HEAD~3^2`, ну и так далее.
184
+
185
+ ### Диапазон коммитов ###
186
+
187
+ Теперь, когда вы умеете задавать отдельные коммиты, разберёмся, как указать диапазон коммитов. Это особенно полезно при управлении ветками — если у вас много веток, вы можете использовать обозначения диапазонов, чтобы ответить на вопросы типа "Какие в этой ветке есть коммиты, которые не были слиты в основную ветку?"
188
+
189
+ #### Две точки ####
190
+
191
+ Наиболее распространённый способ задать диапазон коммитов — это запись с двумя точками. По существу, таким образом вы просите Git взять набор коммитов, достижимых из одного коммита, но не достижимых из другого. Например, пускай ваша история коммитов выглядит так, как показано на рисунке 6-1.
192
+
193
+ Insert 18333fig0601.png
194
+ Рисунок 6-1. Пример истории для выбора набора коммитов.
195
+
196
+ Допустим, вы хотите посмотреть, что в вашей ветке `experiment` ещё не было слито в ветку `master`. Можно попросить Git показать вам лог только таких коммитов с помощью `master..experiment` — эта запись означает "все коммиты, достижимые из experiment, которые недостижимы из master". Для краткости и большей понятности в примерах мы будем использовать буквы для обозначения коммитов на диаграмме вместо настоящего вывода лога в том порядке, в каком они будут отображены:
197
+
198
+ $ git log master..experiment
199
+ D
200
+ C
201
+
202
+ С другой стороны, если вы хотите получить обратное — все коммиты в `master`, которых нет в `experiment`, можно переставить имена веток. Запись `experiment..master` покажет всё, что есть в `master`, но недостижимо из `experiment`:
203
+
204
+ $ git log experiment..master
205
+ F
206
+ E
207
+
208
+ Такое полезно, если вы хотите, чтобы ветка `experiment` была обновлённой, и хотите посмотреть, что вы собираетесь в неё слить. Ещё один частый случай использования этого синтаксиса — посмотреть, что вы собираетесь отправить на удалённый сервер:
209
+
210
+ $ git log origin/master..HEAD
211
+
212
+ Эта команда покажет вам все коммиты в текущей ветке, которых нет в ветке `master` на сервере `origin`. Если бы вы выполнили `git push`, при условии, что текущая ветка отслеживает `origin/master`, то коммиты, которые перечислены в выводе `git log origin/master..HEAD` это те коммиты, которые были бы отправлены на сервер.
213
+ Кроме того, можно опустить одну из сторон в такой записи — Git подставит туда HEAD. Например, вы можете получить такой же результат, как и в предыдущем примере, набрав `git log origin/master..` — Git подставит HEAD сам, если одна из сторон отсутствует.
214
+
215
+ #### Множество вершин ####
216
+
217
+ Запись с двумя точками полезна как сокращение, но, возможно, вы захотите указать больше двух веток, чтобы указать нужную ревизию. Например, чтобы посмотреть, какие коммиты находятся в одной из нескольких веток, но не в текущей. Git позволяет сделать это с помощью использования либо символа `^`, либо `--not` перед любыми ссылками, коммиты, достижимые из которых, вы не хотите видеть. Таким образом, следующие три команды эквивалентны:
218
+
219
+ $ git log refA..refB
220
+ $ git log ^refA refB
221
+ $ git log refB --not refA
222
+
223
+ Это удобно, потому что с помощью такого синтаксиса можно указать более двух ссылок в своём запросе, чего вы не сможете сделать с помощью двух точек. Например, если вы хотите увидеть все коммиты достижимые из `refA` или `refB`, но не из `refC`, можно набрать одну из таких команд:
224
+
225
+ $ git log refA refB ^refC
226
+ $ git log refA refB --not refC
227
+
228
+ Всё это делает систему выбора ревизий очень мощной, что должно помочь вам определять, что содержится в ваших ветках.
229
+
230
+ #### Три точки ####
231
+
232
+ Последняя основная запись для выбора диапазона коммитов — это запись с тремя точками, которая означает те коммиты, которые достижимы по одной из двух ссылок, но не по обеим одновременно. Вернёмся к примеру истории коммитов на рисунке 6-1.
233
+ Если вы хотите увидеть, что находится в `master` или `experiment`, но не в обоих сразу, выполните
234
+
235
+ $ git log master...experiment
236
+ F
237
+ E
238
+ D
239
+ C
240
+
241
+ Повторимся, что это даст вам стандартный `log`-вывод, но покажет только информацию об этих четырёх коммитах, упорядоченных по дате коммита, как и обычно.
242
+
243
+ В этом случае вместе с командой `log` обычно используют параметр `--left-right`, который показывает, на какой стороне диапазона находится каждый коммит. Это помогает сделать данные полезнее:
244
+
245
+ $ git log --left-right master...experiment
246
+ < F
247
+ < E
248
+ > D
249
+ > C
250
+
251
+ С помощью этих инструментов вы можете намного легче объяснить Git'у, какой коммит или коммиты вы хотите изучить.
252
+
253
+ ## Интерактивное индексирование ##
254
+
255
+ Вместе с Git'ом поставляется пара сценариев (script), облегчающих выполнение некоторых задач в командной строке. Сейчас мы посмотрим на несколько интерактивных команд, которые помогут вам легко смастерить свои коммиты так, чтобы включить в них только определённые части файлов. Эти инструменты сильно помогают в случае, когда вы поменяли кучу файлов, а потом решили, что хотите, чтобы эти изменения были в нескольких сфокусированных коммитах, а не в одном большом путанном коммите. Так вы сможете убедиться, что ваши коммиты — это логически разделённые наборы изменений, которые будет легко просматривать другим разработчикам, работающим с вами.
256
+ Если вы выполните `git add` с опцией `-i` или `--interactive`, Git перейдёт в режим интерактивной оболочки и покажет что-то похожее на это:
257
+
258
+ $ git add -i
259
+ staged unstaged path
260
+ 1: unchanged +0/-1 TODO
261
+ 2: unchanged +1/-1 index.html
262
+ 3: unchanged +5/-1 lib/simplegit.rb
263
+
264
+ *** Commands ***
265
+ 1: status 2: update 3: revert 4: add untracked
266
+ 5: patch 6: diff 7: quit 8: help
267
+ What now>
268
+
269
+ Как видите, эта команда показывает содержимое индекса, но в другом виде — по сути, ту же информацию вы получили бы при вызове `git status`, но здесь она в более сжатом и информативном виде. `git add -i` показывает проиндексированные изменения слева, а непроиндексированные — справа.
270
+
271
+ Затем идёт раздел Commands (команды). Тут можно сделать многие вещи, включая добавление файлов в индекс, удаление файлов из индекса, индексирование файлов частями, добавление неотслеживаемых файлов и просмотр дельт (diff) проиндексированных изменений.
272
+
273
+ ### Добавление и удаление файлов из индекса ###
274
+
275
+ Если набрать `2` или `u` в приглашении `What now>`, сценарий спросит, какие файлы вы хотите добавить в индекс:
276
+
277
+ What now> 2
278
+ staged unstaged path
279
+ 1: unchanged +0/-1 TODO
280
+ 2: unchanged +1/-1 index.html
281
+ 3: unchanged +5/-1 lib/simplegit.rb
282
+ Update>>
283
+
284
+ Чтобы проиндексировать файлы TODO и index.html, нужно набрать их номера:
285
+
286
+ Update>> 1,2
287
+ staged unstaged path
288
+ * 1: unchanged +0/-1 TODO
289
+ * 2: unchanged +1/-1 index.html
290
+ 3: unchanged +5/-1 lib/simplegit.rb
291
+ Update>>
292
+
293
+ Символ `*` рядом с каждым файлом означает, что файл выбран для индексирования. Если вы сейчас ничего не будете вводить, а нажмёте Enter в приглашении `Update>>`, то Git возьмёт всё, что уже выбрано, и добавит в индекс:
294
+
295
+ Update>>
296
+ updated 2 paths
297
+
298
+ *** Commands ***
299
+ 1: status 2: update 3: revert 4: add untracked
300
+ 5: patch 6: diff 7: quit 8: help
301
+ What now> 1
302
+ staged unstaged path
303
+ 1: +0/-1 nothing TODO
304
+ 2: +1/-1 nothing index.html
305
+ 3: unchanged +5/-1 lib/simplegit.rb
306
+
307
+ Как видите, теперь файлы TODO и index.html проиндексированы (staged), а файл simplegit.rb — всё ещё нет. Если в этот момент вы хотите удалить файл TODO из индекса, используйте опцию `3` или `r` (revert):
308
+
309
+ *** Commands ***
310
+ 1: status 2: update 3: revert 4: add untracked
311
+ 5: patch 6: diff 7: quit 8: help
312
+ What now> 3
313
+ staged unstaged path
314
+ 1: +0/-1 nothing TODO
315
+ 2: +1/-1 nothing index.html
316
+ 3: unchanged +5/-1 lib/simplegit.rb
317
+ Revert>> 1
318
+ staged unstaged path
319
+ * 1: +0/-1 nothing TODO
320
+ 2: +1/-1 nothing index.html
321
+ 3: unchanged +5/-1 lib/simplegit.rb
322
+ Revert>> [enter]
323
+ reverted one path
324
+
325
+ Взглянув на статус снова, вы увидите, что файл TODO удалён из индекса:
326
+
327
+ *** Commands ***
328
+ 1: status 2: update 3: revert 4: add untracked
329
+ 5: patch 6: diff 7: quit 8: help
330
+ What now> 1
331
+ staged unstaged path
332
+ 1: unchanged +0/-1 TODO
333
+ 2: +1/-1 nothing index.html
334
+ 3: unchanged +5/-1 lib/simplegit.rb
335
+
336
+ Чтобы посмотреть дельту для проиндексированных изменений, используйте команду `6` или `d` (diff). Она покажет вам список проиндексированных файлов, и вы сможете выбрать те, для которых хотите посмотреть дельту. Это почти то же, что указать `git diff --cached` в командной строке:
337
+
338
+ *** Commands ***
339
+ 1: status 2: update 3: revert 4: add untracked
340
+ 5: patch 6: diff 7: quit 8: help
341
+ What now> 6
342
+ staged unstaged path
343
+ 1: +1/-1 nothing index.html
344
+ Review diff>> 1
345
+ diff --git a/index.html b/index.html
346
+ index 4d07108..4335f49 100644
347
+ --- a/index.html
348
+ +++ b/index.html
349
+ @@ -16,7 +16,7 @@ Date Finder
350
+
351
+ <p id="out">...</p>
352
+
353
+ -<div id="footer">contact : support@github.com</div>
354
+ +<div id="footer">contact : email.support@github.com</div>
355
+
356
+ <script type="text/javascript">
357
+
358
+ С помощью этих базовых команд вы можете использовать интерактивный режим для `git add`, чтобы немного проще работать со своим индексом.
359
+
360
+ ### Индексирование по частям ###
361
+
362
+ Для Git'а также возможно индексировать определённые части файлов, а не всё сразу. Например, если вы сделали несколько изменений в файле simplegit.rb и хотите проиндексировать одно из них, а другое — нет, то сделать такое в Git'е очень легко. В строке приглашения интерактивного режима наберите `5` или `p` (patch). Git спросит, какие файлы вы хотите индексировать частями; затем для каждой части изменений в выбранных файлах один за другим будут показываться куски дельт файла, и вас будут спрашивать, хотите ли вы занести их в индекс:
363
+
364
+ diff --git a/lib/simplegit.rb b/lib/simplegit.rb
365
+ index dd5ecc4..57399e0 100644
366
+ --- a/lib/simplegit.rb
367
+ +++ b/lib/simplegit.rb
368
+ @@ -22,7 +22,7 @@ class SimpleGit
369
+ end
370
+
371
+ def log(treeish = 'master')
372
+ - command("git log -n 25 #{treeish}")
373
+ + command("git log -n 30 #{treeish}")
374
+ end
375
+
376
+ def blame(path)
377
+ Stage this hunk [y,n,a,d,/,j,J,g,e,?]?
378
+
379
+ На этой стадии у вас много вариантов действий. Набрав `?`, вы получите список возможных действий:
380
+
381
+ Stage this hunk [y,n,a,d,/,j,J,g,e,?]? ?
382
+ y - stage this hunk (добавить этот кусок в индекс)
383
+ n - do not stage this hunk (не добавлять этот кусок в индекс)
384
+ a - stage this and all the remaining hunks in the file (добавить этот и все оставшиеся куски в этом файле в индекс)
385
+ d - do not stage this hunk nor any of the remaining hunks in the file (не добавлять в индекс ни этот, ни последующие куски в этом файле)
386
+ g - select a hunk to go to (выбрать кусок и перейти к нему)
387
+ / - search for a hunk matching the given regex (поиск куска по регулярному выражению)
388
+ j - leave this hunk undecided, see next undecided hunk (отложить решение для этого куска, перейти к следующему отложенному куску)
389
+ J - leave this hunk undecided, see next hunk (отложить решение для этого куска, перейти к следующему куску)
390
+ k - leave this hunk undecided, see previous undecided hunk (отложить решение для этого куска, перейти к предыдущему отложенному куску)
391
+ K - leave this hunk undecided, see previous hunk (отложить решение для этого куска, перейти к предыдущему куску)
392
+ s - split the current hunk into smaller hunks (разбить текущий кусок на меньшие части)
393
+ e - manually edit the current hunk (отредактировать текущий кусок вручную)
394
+ ? - print help (вывести справку)
395
+
396
+ Как правило, вы будете использовать `y` или `n` для индексирования каждого куска, но индексирование всех кусков сразу в некоторых файлах или откладывание решения на потом также может оказаться полезным. Если вы добавите в индекс одну часть файла, а другую часть — нет, вывод статуса будет выглядеть так:
397
+
398
+ What now> 1
399
+ staged unstaged path
400
+ 1: unchanged +0/-1 TODO
401
+ 2: +1/-1 nothing index.html
402
+ 3: +1/-1 +4/-0 lib/simplegit.rb
403
+
404
+ Статус файла simplegit.rb выглядит любопытно. Он показывает, что часть строк в индексе, а часть — не в индексе. Мы частично проиндексировали этот файл. Теперь вы можете выйти из интерактивного сценария и выполнить `git commit`, чтобы создать коммит из этих частично проиндексированных файлов.
405
+
406
+ В заключение скажем, что нет необходимости входить в интерактивный режим `git add`, чтобы выполнять индексирование частями — вы можете запустить тот же сценарий, набрав `git add -p` или `git add --patch` в командной строке.
407
+
408
+ ## Прятанье ##
409
+
410
+ Часто возникает такая ситуация, что пока вы работаете над частью своего проекта, всё находится в беспорядочном состоянии, а вам нужно переключить ветки, чтобы немного поработать над чем-то другим. Проблема в том, что вы не хотите делать коммит с наполовину доделанной работой только для того, чтобы позже можно было вернуться в это же состояние. Ответ на эту проблему — команда `git stash`.
411
+
412
+ Прятанье поглощает грязное состояние рабочего каталога, то есть изменённые отслеживаемые файлы и изменения в индексе, и сохраняет их в стек незавершённых изменений, которые вы потом в любое время можете снова применить.
413
+
414
+ ### Прятанье своих трудов ###
415
+
416
+ Чтобы продемонстрировать, как это работает, предположим, что вы идёте к своему проекту и начинаете работать над парой файлов и, возможно, добавляете в индекс одно из изменений. Если вы выполните `git status`, вы увидите грязное состояние проекта:
417
+
418
+ $ git status
419
+ # On branch master
420
+ # Changes to be committed:
421
+ # (use "git reset HEAD <file>..." to unstage)
422
+ #
423
+ # modified: index.html
424
+ #
425
+ # Changes not staged for commit:
426
+ # (use "git add <file>..." to update what will be committed)
427
+ #
428
+ # modified: lib/simplegit.rb
429
+ #
430
+
431
+ Теперь вы хотите поменять ветку, но не хотите делать коммит с тем, над чем вы ещё работаете; тогда вы прячете эти изменения. Чтобы создать новую "заначку", выполните `git stash`:
432
+
433
+ $ git stash
434
+ Saved working directory and index state \
435
+ "WIP on master: 049d078 added the index file"
436
+ HEAD is now at 049d078 added the index file
437
+ (To restore them type "git stash apply")
438
+
439
+ Ваш рабочий каталог чист:
440
+
441
+ $ git status
442
+ # On branch master
443
+ nothing to commit, working directory clean
444
+
445
+ В данный момент вы легко можете переключить ветки и поработать где-то ещё; ваши изменения сохранены в стеке. Чтобы посмотреть, что у вас есть припрятанного, используйте `git stash list`:
446
+
447
+ $ git stash list
448
+ stash@{0}: WIP on master: 049d078 added the index file
449
+ stash@{1}: WIP on master: c264051... Revert "added file_size"
450
+ stash@{2}: WIP on master: 21d80a5... added number to log
451
+
452
+ В нашем случае две "заначки" были сделаны ранее, так что у вас теперь три разных припрятанных работы. Вы можете снова применить ту, которую только что спрятали, с помощью команды, показанной в справке в выводе первоначальной команды `stash`: `git stash apply`. Если вы хотите применить одну из старых заначек, можете сделать это, указав её имя так: `git stash apply stash@{2}`. Если не указывать ничего, Git будет подразумевать, что вы хотите применить последнюю спрятанную работу:
453
+
454
+ $ git stash apply
455
+ # On branch master
456
+ # Changes not staged for commit:
457
+ # (use "git add <file>..." to update what will be committed)
458
+ #
459
+ # modified: index.html
460
+ # modified: lib/simplegit.rb
461
+ #
462
+
463
+ Как видите, Git восстановил изменения в файлах, которые вы отменили, когда использовали команду `stash`. В нашем случае у вас был чистый рабочий каталог, когда вы восстанавливали спрятанные изменения, и к тому же вы делали это на той же ветке, на которой находились во время прятанья. Но наличие чистого рабочего каталога и применение на той же ветке не обязательны для `git stash apply`. Вы можете спрятать изменения на одной ветке, переключиться позже на другую ветку и попытаться восстановить изменения. У вас в рабочем каталоге также могут быть изменённые и недокоммиченные файлы во время применения спрятанного — Git выдаст вам конфликты слияния, если что-то уже не может быть применено чисто.
464
+
465
+ Изменения в файлах были восстановлены, но файлы в индексе — нет. Чтобы добиться такого, необходимо выполнить команду `git stash apply` с опцией `--index`, тогда команда попытается применить изменения в индексе. Если бы вы выполнили команду так, а не как раньше, то получили бы исходное состояние:
466
+
467
+ $ git stash apply --index
468
+ # On branch master
469
+ # Changes to be committed:
470
+ # (use "git reset HEAD <file>..." to unstage)
471
+ #
472
+ # modified: index.html
473
+ #
474
+ # Changes not staged for commit:
475
+ # (use "git add <file>..." to update what will be committed)
476
+ #
477
+ # modified: lib/simplegit.rb
478
+ #
479
+
480
+ Всё, что делает опция apply — это пытается применить спрятанную работу — то, что вы спрятали, всё ещё будет находиться в стеке. Чтобы удалить спрятанное, выполните `git stash drop` с именем "заначки", которую нужно удалить:
481
+
482
+ $ git stash list
483
+ stash@{0}: WIP on master: 049d078 added the index file
484
+ stash@{1}: WIP on master: c264051... Revert "added file_size"
485
+ stash@{2}: WIP on master: 21d80a5... added number to log
486
+ $ git stash drop stash@{0}
487
+ Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43)
488
+
489
+ Также можно выполнить `git stash pop`, чтобы применить спрятанные изменения и сразу же удалить их из стека.
490
+
491
+ ### Откат применения спрятанных изменений ###
492
+
493
+ При некоторых сценариях использования, может понадобиться применить спрятанные изменения, поработать, а потом отменить изменения, внесённые командой `stash apply`. Git не предоставляет команды `stash unapply`, но можно добиться того же эффекта получив сначала патч для спрятанных изменений, а потом применив его в перевёрнутом виде:
494
+
495
+ $ git stash show -p stash@{0} | git apply -R
496
+
497
+ Снова, если вы не указываете параметр для `stash`, Git подразумевает то, что было спрятано последним:
498
+
499
+ $ git stash show -p | git apply -R
500
+
501
+ Если хотите, сделайте псевдоним и добавьте в свой Git команду `stash-unapply`. Например, так:
502
+
503
+ $ git config --global alias.stash-unapply '!git stash show -p | git apply -R'
504
+ $ git stash apply
505
+ $ #... work work work
506
+ $ git stash-unapply
507
+
508
+ ### Создание ветки из спрятанных изменений ###
509
+
510
+ Если вы спрятали какие-то наработки и оставили их на время, а в это время продолжили работать на той же ветке, то у вас могут возникнуть трудности с восстановлением спрятанной работы. Если `apply` попытается изменить файл, который вы редактировали после прятанья, то возникнет конфликт слияния, который надо будет разрешить. Если нужен более простой способ снова потестировать спрятанную работу, можно выполнить команду `git stash branch`, которая создаст вам новую ветку с началом из того коммита, на котором вы находились во время прятанья, восстановит в ней вашу работу и затем удалит спрятанное, если оно применилось успешно:
511
+
512
+ $ git stash branch testchanges
513
+ Switched to a new branch "testchanges"
514
+ # On branch testchanges
515
+ # Changes to be committed:
516
+ # (use "git reset HEAD <file>..." to unstage)
517
+ #
518
+ # modified: index.html
519
+ #
520
+ # Changes not staged for commit:
521
+ # (use "git add <file>..." to update what will be committed)
522
+ #
523
+ # modified: lib/simplegit.rb
524
+ #
525
+ Dropped refs/stash@{0} (f0dfc4d5dc332d1cee34a634182e168c4efc3359)
526
+
527
+ Это сокращение удобно для того, чтобы легко восстановить свою работу, а затем поработать над ней в новой ветке.
528
+
529
+ ## Перезапись истории ##
530
+
531
+ Неоднократно во время работы с Git'ом, вам может захотеться по какой-либо причине исправить свою историю коммитов. Одна из чудесных особенностей Git'а заключается в том, что он даёт возможность принять решение в самый последний момент. Используя индекс, вы можете решить, какие файлы пойдут в какие коммиты, непосредственно перед тем, как сделать коммит. Вы можете воспользоваться командой `stash`, решив, что над чем-то ещё не стоило начинать работать. А также вы можете переписать уже сделанные коммиты так, будто они были сделаны как-то по-другому. В частности, это может быть изменение порядка следования коммитов, редактирование сообщений или модифицирование файлов в коммите, уплотнение и разделение коммитов, а также полное удаление некоторых коммитов — но только до того, как вы поделитесь наработками с другими.
532
+
533
+ В этом разделе вы узнаете, как делать подобные полезные вещи, чтобы перед публикацией приводить историю коммитов в желаемый вид.
534
+
535
+ ### Изменение последнего коммита ###
536
+
537
+ Изменение последнего коммита — это, вероятно, наиболее типичный случай переписывания истории, который вы будете делать. Как правило, вам от вашего последнего коммита понадобятся две основные вещи: изменить сообщение коммита или изменить только что записанный снимок состояния, добавив, изменив или удалив из него файлы.
538
+
539
+ Если вы всего лишь хотите изменить сообщение последнего коммита — это очень просто:
540
+
541
+ $ git commit --amend
542
+
543
+ Выполнив это, вы попадёте в свой текстовый редактор, в котором будет находиться сообщение последнего коммита, готовое к тому, чтобы его отредактировали. Когда вы сохраните текст и закроете редактор, Git создаст новый коммит с вашим сообщением и сделает его новым последним коммитом.
544
+
545
+ Если вы сделали коммит и затем хотите изменить снимок состояния в коммите, добавив или изменив файлы, допустим, потому что вы забыли добавить только что созданный файл, когда делали коммит, то процесс выглядит в основном так же. Вы добавляете в индекс изменения, которые хотите, редактируя файл и выполняя для него `git add` или выполняя `git rm` для отслеживаемого файла, и затем `git commit --amend` возьмёт текущий индекс и сделает его снимком состояния нового коммита.
546
+
547
+ Будьте осторожны, используя этот приём, потому что `git commit --amend` меняет SHA-1 коммита. Тут как с маленьким перемещением (rebase) — не правьте последний коммит, если вы его уже куда-то отправили.
548
+
549
+ ### Изменение сообщений нескольких коммитов ###
550
+
551
+ Чтобы изменить коммит, находящийся глубоко в истории, вам придётся перейти к использованию более сложных инструментов. В Git'е нет специального инструмента для редактирования истории, но вы можете использовать `rebase` для перемещения ряда коммитов на то же самое место, где они были изначально, а не куда-то в другое место. Используя инструмент для интерактивного перемещения, вы можете останавливаться на каждом коммите, который хотите изменить, и редактировать сообщение, добавлять файлы или делать что-то ещё. Интерактивное перемещение можно запустить, добавив опцию `-i` к `git rebase`. Необходимо указать, насколько далёкие в истории коммиты вы хотите переписать, сообщив команде, на какой коммит выполняется перемещение.
552
+
553
+ Например, если вы хотите изменить сообщения последних трёх коммитов или сообщения для только некоторых коммитов в этой группе, вам надо передать в `git rebase -i` в качестве аргумента родителя последнего коммита, который вы хотите изменить, то есть `HEAD~2^` или `HEAD~3`. Наверное, проще запомнить `~3`, потому что вы пытаетесь отредактировать три последних коммита, но имейте в виду, что на самом деле вы обозначили четвёртый сверху коммит — родительский коммит для того, который хотите отредактировать:
554
+
555
+ $ git rebase -i HEAD~3
556
+
557
+ Снова напомним, что это команда для перемещения, то есть все коммиты в диапазоне `HEAD~3..HEAD` будут переписаны вне зависимости от того, меняли ли вы в них сообщение или нет. Не трогайте те коммиты, которые вы уже отправили на центральный сервер — сделав так, вы запутаете других разработчиков, дав им разные версии одних и тех же изменений.
558
+
559
+ Запуск этой команды выдаст вам в текстовом редакторе список коммитов, который будет выглядеть следующим образом:
560
+
561
+ pick f7f3f6d changed my name a bit
562
+ pick 310154e updated README formatting and added blame
563
+ pick a5f4a0d added cat-file
564
+
565
+ # Rebase 710f0f8..a5f4a0d onto 710f0f8
566
+ #
567
+ # Commands:
568
+ # p, pick = use commit
569
+ # e, edit = use commit, but stop for amending
570
+ # s, squash = use commit, but meld into previous commit
571
+ #
572
+ # If you remove a line here THAT COMMIT WILL BE LOST.
573
+ # However, if you remove everything, the rebase will be aborted.
574
+ #
575
+
576
+ Важно отметить, что эти коммиты выведены в обратном порядке по сравнению с тем, как вы их обычно видите, используя команду `log`. Запустив `log`, вы получите что-то вроде этого:
577
+
578
+ $ git log --pretty=format:"%h %s" HEAD~3..HEAD
579
+ a5f4a0d added cat-file
580
+ 310154e updated README formatting and added blame
581
+ f7f3f6d changed my name a bit
582
+
583
+ Обратите внимание на обратный порядок. Интерактивное перемещение выдаёт сценарий, который будет выполнен. Он начнётся с коммита, который вы указали в командной строке (`HEAD~3`), и воспроизведёт изменения, сделанные каждым из этих коммитов, сверху вниз. Наверху указан самый старый коммит, а не самый новый, потому что он будет воспроизведён первым.
584
+
585
+ Вам надо отредактировать сценарий так, чтобы он останавливался на коммитах, которые вы хотите отредактировать. Чтобы сделать это, замените слово pick на слово edit для каждого коммита, на котором сценарий должен остановиться. Например, чтобы изменить сообщение только для третьего коммита, отредактируйте файл так, чтобы он выглядел следующим образом:
586
+
587
+ edit f7f3f6d changed my name a bit
588
+ pick 310154e updated README formatting and added blame
589
+ pick a5f4a0d added cat-file
590
+
591
+ Когда вы сохраните и выйдете из редактора, Git откатит вас назад к последнему коммиту в списке и выкинет вас в командную строку, выдав следующее сообщение:
592
+
593
+ $ git rebase -i HEAD~3
594
+ Stopped at 7482e0d... updated the gemspec to hopefully work better
595
+ You can amend the commit now, with
596
+
597
+ git commit --amend
598
+
599
+ Once you’re satisfied with your changes, run
600
+
601
+ git rebase --continue
602
+
603
+ В этой инструкции в точности сказано, что надо сделать. Наберите
604
+
605
+ $ git commit --amend
606
+
607
+ Измените сообщение коммита и выйдите из редактора. Теперь выполните
608
+
609
+ $ git rebase --continue
610
+
611
+ Данная команда применит оставшиеся два коммита автоматически и закончит на этом. Если вы измените pick на edit для большего количества строк, то вы повторите эти шаги для каждого коммита, где вы напишете edit. Каждый раз Git будет останавливаться, давая вам исправить коммит, а потом, когда вы закончите, будет продолжать.
612
+
613
+ ### Переупорядочение коммитов ###
614
+
615
+ Интерактивное перемещение можно также использовать для изменения порядка следования и для полного удаления коммитов. Если вы хотите удалить коммит "added cat-file" и поменять порядок, в котором идут два других коммита, измените сценарий для rebase с такого
616
+
617
+ pick f7f3f6d changed my name a bit
618
+ pick 310154e updated README formatting and added blame
619
+ pick a5f4a0d added cat-file
620
+
621
+ на такой:
622
+
623
+ pick 310154e updated README formatting and added blame
624
+ pick f7f3f6d changed my name a bit
625
+
626
+ Когда вы сохраните и выйдете из редактора, Git откатит вашу ветку к родительскому для этих трёх коммиту, применит `310154e`, затем `f7f3f6d`, а потом остановится. Вы фактически поменяли порядок следования коммитов и полностью удалили коммит "added cat-file".
627
+
628
+ ### Уплотнение коммитов ###
629
+
630
+ С помощью интерактивного перемещения также возможно взять несколько коммитов и сплющить их в один коммит. Сценарий выдаёт полезное сообщение с инструкциями для перемещения:
631
+
632
+ #
633
+ # Commands:
634
+ # p, pick = use commit
635
+ # e, edit = use commit, but stop for amending
636
+ # s, squash = use commit, but meld into previous commit
637
+ #
638
+ # If you remove a line here THAT COMMIT WILL BE LOST.
639
+ # However, if you remove everything, the rebase will be aborted.
640
+ #
641
+
642
+ Если вместо "pick" или "edit" указать "squash", Git применит изменения и из этого коммита, и из предыдущего, а затем даст вам объединить сообщения для коммитов. Итак, чтобы сделать один коммит из трёх наших коммитов, надо сделать так, чтобы сценарий выглядел следующим образом:
643
+
644
+ pick f7f3f6d changed my name a bit
645
+ squash 310154e updated README formatting and added blame
646
+ squash a5f4a0d added cat-file
647
+
648
+ Когда вы сохраните и выйдете из редактора, Git применит все три изменения, а затем опять выдаст вам редактор для того, чтобы объединить сообщения трёх коммитов:
649
+
650
+ # This is a combination of 3 commits.
651
+ # The first commit's message is:
652
+ changed my name a bit
653
+
654
+ # This is the 2nd commit message:
655
+
656
+ updated README formatting and added blame
657
+
658
+ # This is the 3rd commit message:
659
+
660
+ added cat-file
661
+
662
+ Когда вы это сохраните, у вас будет один коммит, который вносит изменения такие же, как три бывших коммита.
663
+
664
+ ### Разбиение коммита ###
665
+
666
+ Разбиение коммита — это отмена коммита, а затем индексирование изменений частями и добавление коммитов столько раз, сколько коммитов вы хотите получить. Предположим, что вы хотите разбить средний из наших трёх коммитов. Вместо "updated README formatting and added blame" вы хотите получить два отдельных коммита: "updated README formatting" в качестве первого и "added blame" в качестве второго. Вы можете сделать это в сценарии `rebase -i`, поставив "edit" в инструкции для коммита, который хотите разбить:
667
+
668
+ pick f7f3f6d changed my name a bit
669
+ edit 310154e updated README formatting and added blame
670
+ pick a5f4a0d added cat-file
671
+
672
+ Когда вы сохраните и выйдете из редактора, Git откатится к родителю первого коммита в списке, применит первый коммит (`f7f3f6d`), применит второй (`310154e`) и выбросит вас в консоль. Здесь вы можете сбросить последний коммит в смешанном режиме с помощью `git reset HEAD^` — это в сущности отменит этот коммит и оставит изменённые файлы непроиндексированными. Теперь вы можете взять сброшенные изменения и создать из них несколько коммитов. Просто добавляйте файлы в индекс и делайте коммиты, пока не сделаете несколько штук. Затем, когда закончите, выполните `git rebase --continue`:
673
+
674
+ $ git reset HEAD^
675
+ $ git add README
676
+ $ git commit -m 'updated README formatting'
677
+ $ git add lib/simplegit.rb
678
+ $ git commit -m 'added blame'
679
+ $ git rebase --continue
680
+
681
+ Когда Git применит последний коммит (`a5f4a0d`) в сценарии, история будет выглядеть так:
682
+
683
+ $ git log -4 --pretty=format:"%h %s"
684
+ 1c002dd added cat-file
685
+ 9b29157 added blame
686
+ 35cfb2b updated README formatting
687
+ f3cc40e changed my name a bit
688
+
689
+ Повторимся ещё раз, что эта операция меняет SHA всех коммитов в списке, так что убедитесь, что ни один из коммитов в этом списке вы ещё не успели отправить в общий репозиторий.
690
+
691
+ ### Крайнее средство: filter-branch ###
692
+
693
+ Есть ещё один вариант переписывания истории, который можно использовать, если надо переписать большое количество коммитов в автоматизируемой форме — например, везде поменять свой e-mail адрес или удалить файл из каждого коммита — это команда `filter-branch`. Она может переписать огромные периоды вашей истории, так что, возможно, вообще не стоит использовать её, если ваш проект успел стать публичным и другие люди уже работают на основе коммитов, которые вы собрались переписать. Однако, она может быть весьма полезной. Мы посмотрим на некоторые типичные варианты использования команды так, чтобы вы получили представление о тех вещах, на которые она способна.
694
+
695
+ #### Удаление файла изо всех коммитов ####
696
+
697
+ Такое случается довольно часто. Кто-нибудь случайно добавляет в коммит огромный бинарный файл, необдуманно выполнив `git add .`, и вы хотите удалить его отовсюду. Или, может быть, вы нечаянно добавили в коммит файл содержащий пароль, а теперь хотите сделать код этого проекта открытым. `filter-branch` — это тот инструмент, который вы наверняка захотите использовать, чтобы прочесать всю историю. Чтобы удалить файл с именем passwords.txt изо всей истории, используйте опцию `--tree-filter` для `filter-branch`:
698
+
699
+ $ git filter-branch --tree-filter 'rm -f passwords.txt' HEAD
700
+ Rewrite 6b9b3cf04e7c5686a9cb838c3f36a8cb6a0fc2bd (21/21)
701
+ Ref 'refs/heads/master' was rewritten
702
+
703
+ Опция `--tree-filter` выполняет указанную команду после выгрузки каждой версии проекта и затем заново делает коммит из результата. В нашем случае мы удалили файл с именем passwords.txt из каждого снимка состояния независимо от того, существовал ли он там или нет. Если вы хотите удалить все случайно добавленные резервные копии, сделанные вашим текстовым редактором, выполните что-то типа `git filter-branch --tree-filter "find * -type f -name '*~' -delete" HEAD`.
704
+
705
+ Вы увидите, как Git переписывает деревья и коммиты, а в конце переставляет указатель ветки. Как правило, хороший вариант — делать это в тестовой ветке, а затем жёстко сбрасывать ветку master с помощью `reset --hard`, когда вы поймёте, что результат — это то, чего вы действительно добивались. Чтобы запустить `filter-branch` для всех веток, можно передать команде параметр `--all`.
706
+
707
+ #### Сделать подкаталог новым корнем ####
708
+
709
+ Предположим, вы импортировали репозиторий из другой системы контроля версий, и в нём есть бессмысленные каталоги (trunk, tags, и др.). Если вы хотите сделать `trunk` новым корнем проекта, команда `filter-branch` может помочь вам сделать и это:
710
+
711
+ $ git filter-branch --subdirectory-filter trunk HEAD
712
+ Rewrite 856f0bf61e41a27326cdae8f09fe708d679f596f (12/12)
713
+ Ref 'refs/heads/master' was rewritten
714
+
715
+ Теперь всюду корневой каталог проекта будет в подкаталоге `trunk`. Git также автоматически удалит все коммиты, которые не затрагивают данный подкаталог.
716
+
717
+ #### Глобальное изменение e-mail адреса ####
718
+
719
+ Ещё один типичный случай — это, когда вы забыли выполнить `git config`, чтобы задать своё имя и e-mail адрес, перед тем как начать работать. Или, возможно, вы хотите открыть код своего проекта с работы и поменять все свои рабочие e-mail'ы на свой личный адрес. В любом случае с помощью `filter-branch` вы с таким же успехом можете поменять адреса почты в нескольких коммитах за один раз. Вам надо быть аккуратным, чтобы не поменять и чужие адреса, поэтому используйте `--commit-filter`:
720
+
721
+ $ git filter-branch --commit-filter '
722
+ if [ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ];
723
+ then
724
+ GIT_AUTHOR_NAME="Scott Chacon";
725
+ GIT_AUTHOR_EMAIL="schacon@example.com";
726
+ git commit-tree "$@";
727
+ else
728
+ git commit-tree "$@";
729
+ fi' HEAD
730
+
731
+ Эта команда проходит по всем коммитам и переписывает их так, чтобы там был указан новый адрес. Так как коммиты содержат значения SHA-1 своих родителей, эта команда поменяет все SHA в вашей истории, а не только те, в которых есть указанный e-mail адрес.
732
+
733
+ ## Отладка с помощью Git ##
734
+
735
+ Git также предоставляет несколько инструментов, призванных помочь вам в отладке ваших проектов. Так как Git сконструирован так, чтобы работать с практически любыми типами проектов, эти инструменты довольно общие, но зачастую они могут помочь отловить ошибку или её виновника, если что-то пошло не так.
736
+
737
+ ### Аннотация файла ###
738
+
739
+ Если вы отловили ошибку в коде и хотите узнать, когда и по какой причине она была внесена, то аннотация файла — лучший инструмент для этого случая. Он покажет вам, какие коммиты модифицировали каждую строку файла в последний раз. Так что, если вы видите, что какой-то метод в коде содержит ошибку, то можно сделать аннотацию нужного файла с помощью `git blame`, чтобы посмотреть, когда и кем каждая строка метода была в последний раз отредактирована. В этом примере используется опция `-L`, чтобы ограничить вывод строками с 12ой по 22ую:
740
+
741
+ $ git blame -L 12,22 simplegit.rb
742
+ ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 12) def show(tree = 'master')
743
+ ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 13) command("git show #{tree}")
744
+ ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 14) end
745
+ ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 15)
746
+ 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 16) def log(tree = 'master')
747
+ 79eaf55d (Scott Chacon 2008-04-06 10:15:08 -0700 17) command("git log #{tree}")
748
+ 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 18) end
749
+ 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 19)
750
+ 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 20) def blame(path)
751
+ 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 21) command("git blame #{path}")
752
+ 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 22) end
753
+
754
+ Заметьте, что первое поле — это частичная SHA-1 коммита, в котором последний раз менялась строка. Следующие два поля — это значения, полученные из этого коммита — имя автора и дата создания коммита. Так что вы легко можете понять, кто и когда менял данную строку. Затем идут номера строк и содержимое файла. Также обратите внимание на строки с `^4832fe2`, это те строки, которые находятся здесь со времён первого коммита для этого файла. Это коммит, в котором этот файл был впервые добавлен в проект, и с тех пор те строки не менялись. Это всё несколько сбивает с толку, потому что только что вы увидели по крайней мере три разных способа изменить SHA коммита с помощью `^`, но тут вот такое значение.
755
+
756
+ Ещё одна крутая вещь в Git'е — это то, что он не отслеживает переименования файлов в явном виде. Он записывает снимки состояний, а затем пытается выяснить, что было переименовано неявно уже после того, как это случилось. Одна из интересных функций, возможная благодаря этому, заключается в том, что вы можете попросить дополнительно выявить все виды перемещений кода. Если вы передадите `-C` в `git blame`, Git проанализирует аннотируемый файл и попытается выявить, откуда фрагменты кода в нём появились изначально, если они были скопированы откуда-то. Недавно я занимался разбиением файла `GITServerHandler.m` на несколько файлов, один из которых был `GITPackUpload.m`. Вызвав `blame` с опцией `-C` для `GITPackUpload.m`, я могу понять откуда части кода здесь появились:
757
+
758
+ $ git blame -C -L 141,153 GITPackUpload.m
759
+ f344f58d GITServerHandler.m (Scott 2009-01-04 141)
760
+ f344f58d GITServerHandler.m (Scott 2009-01-04 142) - (void) gatherObjectShasFromC
761
+ f344f58d GITServerHandler.m (Scott 2009-01-04 143) {
762
+ 70befddd GITServerHandler.m (Scott 2009-03-22 144) //NSLog(@"GATHER COMMI
763
+ ad11ac80 GITPackUpload.m (Scott 2009-03-24 145)
764
+ ad11ac80 GITPackUpload.m (Scott 2009-03-24 146) NSString *parentSha;
765
+ ad11ac80 GITPackUpload.m (Scott 2009-03-24 147) GITCommit *commit = [g
766
+ ad11ac80 GITPackUpload.m (Scott 2009-03-24 148)
767
+ ad11ac80 GITPackUpload.m (Scott 2009-03-24 149) //NSLog(@"GATHER COMMI
768
+ ad11ac80 GITPackUpload.m (Scott 2009-03-24 150)
769
+ 56ef2caf GITServerHandler.m (Scott 2009-01-05 151) if(commit) {
770
+ 56ef2caf GITServerHandler.m (Scott 2009-01-05 152) [refDict setOb
771
+ 56ef2caf GITServerHandler.m (Scott 2009-01-05 153)
772
+
773
+ Это действительно удобно. Стандартно вам бы выдали в качестве начального коммита тот коммит, в котором вы скопировали код, так как это первый коммит, в котором вы поменяли эти строки в данном файле. А сейчас Git выдал вам изначальный коммит, в котором эти строки были написаны, несмотря на то, что это было в другом файле.
774
+
775
+ ### Бинарный поиск ###
776
+
777
+ Аннотирование файла помогает, когда вы знаете, где у вас ошибка, и есть с чего начинать. Если вы не знаете, что у вас сломалось, и с тех пор, когда код работал, были сделаны десятки или сотни коммитов, вы наверняка обратитесь за помощью к `git bisect`. Команда `bisect` выполняет бинарный поиск по истории коммитов, и призвана помочь как можно быстрее определить, в каком коммите была внесена ошибка.
778
+
779
+ Положим, вы только что отправили новую версию вашего кода в производство, и теперь вы периодически получаете отчёты о какой-то ошибке, которая не проявлялась, пока вы работали над кодом, и вы не представляете, почему код ведёт себя так. Вы возвращаетесь к своему коду, и у вас получается воспроизвести ошибку, но вы не понимаете, что не так. Вы можете использовать `bisect`, чтобы выяснить это. Сначала выполните `git bisect start`, чтобы запустить процесс, а затем `git bisect bad`, чтобы сказать системе, что текущий коммит, на котором вы сейчас находитесь, сломан. Затем, необходимо сказать `bisect`, когда было последнее известное хорошее состояние с помощью `git bisect good [хороший_коммит]`:
780
+
781
+ $ git bisect start
782
+ $ git bisect bad
783
+ $ git bisect good v1.0
784
+ Bisecting: 6 revisions left to test after this
785
+ [ecb6e1bc347ccecc5f9350d878ce677feb13d3b2] error handling on repo
786
+
787
+ Git выяснил, что между коммитом, который вы указали как последний хороший коммит (v1.0), и текущей плохой версией было сделано примерно 12 коммитов, и он выгрузил вам версию из середины. В этот момент вы можете провести свои тесты и посмотреть, проявляется ли проблема в этом коммите. Если да, то она была внесена где-то раньше этого среднего коммита; если нет, то проблема появилась где-то после коммита в середине. Положим, что оказывается, что проблема здесь не проявилась, и вы сообщаете об этом Git'у, набрав `git bisect good`, и продолжаете свой путь:
788
+
789
+ $ git bisect good
790
+ Bisecting: 3 revisions left to test after this
791
+ [b047b02ea83310a70fd603dc8cd7a6cd13d15c04] secure this thing
792
+
793
+ Теперь вы на другом коммите, посередине между тем, который только что был протестирован и вашим плохим коммитом. Вы снова проводите тесты и выясняете, что текущий коммит сломан. Так что вы говорите об этом Git'у с помощью `git bisect bad`:
794
+
795
+ $ git bisect bad
796
+ Bisecting: 1 revisions left to test after this
797
+ [f71ce38690acf49c1f3c9bea38e09d82a5ce6014] drop exceptions table
798
+
799
+ Этот коммит хороший, и теперь у Git'а есть вся необходимая информация, чтобы определить, где проблема была внесена впервые. Он выдаёт вам SHA-1 первого плохого коммита и некоторую информацию о нём, а также какие файлы были изменены в этом коммите, так что вы сможете понять, что случилось, что могло внести эту ошибку:
800
+
801
+ $ git bisect good
802
+ b047b02ea83310a70fd603dc8cd7a6cd13d15c04 is first bad commit
803
+ commit b047b02ea83310a70fd603dc8cd7a6cd13d15c04
804
+ Author: PJ Hyett <pjhyett@example.com>
805
+ Date: Tue Jan 27 14:48:32 2009 -0800
806
+
807
+ secure this thing
808
+
809
+ :040000 040000 40ee3e7821b895e52c1695092db9bdc4c61d1730
810
+ f24d3c6ebcfc639b1a3814550e62d60b8e68a8e4 M config
811
+
812
+ Если вы закончили, необходимо выполнить `git bisect reset`, чтобы сбросить HEAD туда, где он был до начала бинарного поиска, иначе вы окажетесь в странном состоянии:
813
+
814
+ $ git bisect reset
815
+
816
+ Это мощный инструмент, который поможет вам за считанные минуты проверить сотни коммитов в поисках появившейся ошибки. На самом деле, если у вас есть сценарий (script), который возвращает на выходе 0, если проект хороший и не 0, если проект плохой, то вы можете полностью автоматизировать `git bisect`. Для начала ему снова надо задать область бинарного поиска, задав известные хороший и плохой коммиты. Если хотите, можете сделать это, указав команде `bisect start` известный плохой коммит первым, а хороший вторым:
817
+
818
+ $ git bisect start HEAD v1.0
819
+ $ git bisect run test-error.sh
820
+
821
+ Сделав так, вы получите, что `test-error.sh` будет автоматически запускаться на каждом выгруженном коммите, пока Git не найдёт первый сломанный коммит. Вы также можете запускать что-нибудь типа `make` или `make tests` или что-то там ещё, что запускает ваши автоматические тесты.
822
+
823
+ ## Подмодули ##
824
+
825
+ Зачастую случается так, что во время работы над некоторым проектом появляется необходимость использовать внутри него ещё какой-то проект. Возможно, библиотеку, разрабатываемую сторонними разработчиками или разрабатываемую вами обособленно и используемую в нескольких родительских проектах. Типичная проблема, возникающая при использовании подобного сценария, это, как сделать так, чтобы иметь возможность рассматривать эти два проекта как отдельные, всё же имея возможность использовать один проект внутри другого.
826
+
827
+ Вот пример. Предположим, вы разрабатываете веб-сайт и создаёте Atom-ленты. И вместо того, чтобы писать собственный код, генерирующий Atom, вы решили использовать библиотеку. Вы, вероятно, должны либо подключить нужный код с помощью разделяемой библиотеки, такой как устанавливаемый модуль CPAN или пакет RubyGem, либо скопировать исходный код в дерево собственного проекта. Проблема с подключением библиотеки в том, что библиотеку сложно хоть как-то модифицировать под свои нужды, и зачастую её сложнее распространять. Ведь вы вынуждены удостовериться в том, что эта библиотека доступна на каждом клиенте. Проблема с включением кода в ваш собственный проект в том, что любые изменения, вносимые вами, могут конфликтовать с изменениями, которые появятся в основном проекте, и эти изменения будет сложно слить.
828
+
829
+ Git решает эту задачу, используя подмодули (submodule). Подмодули позволяют содержать один Git-репозиторий как подкаталог другого Git-репозитория. Это даёт возможность клонировать ещё один репозиторий внутрь проекта, храня коммиты для этого репозитория отдельно.
830
+
831
+ ### Начало использования подмодулей ###
832
+
833
+ Предположим, вы хотите добавить библиотеку Rack (интерфейс шлюза веб-сервера Ruby) в свой проект, возможно, внося свои собственные изменения в него, но продолжая сливать их с изменениями основного проекта. Первое, что вам требуется сделать, это клонировать внешний репозиторий в подкаталог. Добавление внешних проектов в качестве подмодулей делается командой `git submodule add`:
834
+
835
+ $ git submodule add git://github.com/chneukirchen/rack.git rack
836
+ Initialized empty Git repository in /opt/subtest/rack/.git/
837
+ remote: Counting objects: 3181, done.
838
+ remote: Compressing objects: 100% (1534/1534), done.
839
+ remote: Total 3181 (delta 1951), reused 2623 (delta 1603)
840
+ Receiving objects: 100% (3181/3181), 675.42 KiB | 422 KiB/s, done.
841
+ Resolving deltas: 100% (1951/1951), done.
842
+
843
+ Теперь у вас внутри проекта в подкаталоге с именем `rack` находится проект Rack. Вы можете переходить в этот подкаталог, вносить изменения, добавить ваш собственный доступный для записи внешний репозиторий для отправки в него своих изменений, извлекать и сливать из исходного репозитория и многое другое. Если вы выполните `git status` сразу после добавления подмодуля, то увидите две вещи:
844
+
845
+ $ git status
846
+ # On branch master
847
+ # Changes to be committed:
848
+ # (use "git reset HEAD <file>..." to unstage)
849
+ #
850
+ # new file: .gitmodules
851
+ # new file: rack
852
+ #
853
+
854
+ Вначале вы заметите файл `.gitmodules`. Это конфигурационный файл, который содержит соответствие между URL проекта и локальным подкаталогом, в который был загружен подмодуль:
855
+
856
+ $ cat .gitmodules
857
+ [submodule "rack"]
858
+ path = rack
859
+ url = git://github.com/chneukirchen/rack.git
860
+
861
+ Если у вас несколько подмодулей, то в этом файле будет несколько записей. Важно обратить внимание на то, что этот файл находится под версионным контролем вместе с другими вашими файлами, так же как и файл `.gitignore`. Он отправляется при выполнении `push` и загружается при выполнении `pull` вместе с остальными файлами проекта. Так другие люди, которые клонируют этот проект, узнают, откуда взять проекты-подмодули.
862
+
863
+ В следующем листинге вывода `git status` присутствует элемент `rack`. Если вы выполните `git diff` для него, то увидите кое-что интересное:
864
+
865
+ $ git diff --cached rack
866
+ diff --git a/rack b/rack
867
+ new file mode 160000
868
+ index 0000000..08d709f
869
+ --- /dev/null
870
+ +++ b/rack
871
+ @@ -0,0 +1 @@
872
+ +Subproject commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433
873
+
874
+ Хотя `rack` является подкаталогом в вашем рабочем каталоге, Git видит его как подмодуль и не отслеживает его содержимое, если вы не находитесь в нём. Вместо этого, Git записывает его как один конкретный коммит из этого репозитория. Если вы производите изменения в этом подкаталоге и делаете коммит, основной проект замечает, что HEAD в подмодуле был изменён, и регистрирует тот хеш коммита, над которым вы в данный момент завершили работу в подмодуле. Таким образом, если кто-то склонирует этот проект, он сможет воссоздать окружение в точности.
875
+
876
+ Это важная особенность подмодулей – вы запоминаете их как определенный коммит (состояние), в котором они находятся. Вы не можете записать подмодуль под ссылкой `master` или какой-либо другой символьной ссылкой.
877
+
878
+ Если вы создадите коммит, то увидите что-то вроде этого:
879
+
880
+ $ git commit -m 'first commit with submodule rack'
881
+ [master 0550271] first commit with submodule rack
882
+ 2 files changed, 4 insertions(+), 0 deletions(-)
883
+ create mode 100644 .gitmodules
884
+ create mode 160000 rack
885
+
886
+ Обратите внимание на режим 160000 для элемента rack. Это специальный режим в Git'е, который по существу означает, что в качестве записи в каталоге сохраняется коммит, а не подкаталог или файл.
887
+
888
+ Вы можете обращаться с каталогом `rack` как с отдельным проектом и время от времени обновлять свой "надпроект" с помощью указателя на самый последний коммит в этом подпроекте. Все команды Git'а в этих двух каталогах работают независимо друг от друга:
889
+
890
+ $ git log -1
891
+ commit 0550271328a0038865aad6331e620cd7238601bb
892
+ Author: Scott Chacon <schacon@gmail.com>
893
+ Date: Thu Apr 9 09:03:56 2009 -0700
894
+
895
+ first commit with submodule rack
896
+ $ cd rack/
897
+ $ git log -1
898
+ commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433
899
+ Author: Christian Neukirchen <chneukirchen@gmail.com>
900
+ Date: Wed Mar 25 14:49:04 2009 +0100
901
+
902
+ Document version change
903
+
904
+ ### Клонирование проекта с подмодулями ###
905
+
906
+ Сейчас мы склонируем проект, содержащий подмодуль. После получения такого проекта в вашей копии будут каталоги, содержащие подмодули, но пока что без единого файла в них:
907
+
908
+ $ git clone git://github.com/schacon/myproject.git
909
+ Initialized empty Git repository in /opt/myproject/.git/
910
+ remote: Counting objects: 6, done.
911
+ remote: Compressing objects: 100% (4/4), done.
912
+ remote: Total 6 (delta 0), reused 0 (delta 0)
913
+ Receiving objects: 100% (6/6), done.
914
+ $ cd myproject
915
+ $ ls -l
916
+ total 8
917
+ -rw-r--r-- 1 schacon admin 3 Apr 9 09:11 README
918
+ drwxr-xr-x 2 schacon admin 68 Apr 9 09:11 rack
919
+ $ ls rack/
920
+ $
921
+
922
+ Каталог `rack` присутствует, но он пустой. Необходимо выполнить две команды: `git submodule init` для инициализации вашего локального файла конфигурации и `git submodule update` для получения всех данных из подмодуля и перехода к соответствующему коммиту, указанному в вашем основном проекте:
923
+
924
+ $ git submodule init
925
+ Submodule 'rack' (git://github.com/chneukirchen/rack.git) registered for path 'rack'
926
+ $ git submodule update
927
+ Initialized empty Git repository in /opt/myproject/rack/.git/
928
+ remote: Counting objects: 3181, done.
929
+ remote: Compressing objects: 100% (1534/1534), done.
930
+ remote: Total 3181 (delta 1951), reused 2623 (delta 1603)
931
+ Receiving objects: 100% (3181/3181), 675.42 KiB | 173 KiB/s, done.
932
+ Resolving deltas: 100% (1951/1951), done.
933
+ Submodule path 'rack': checked out '08d709f78b8c5b0fbeb7821e37fa53e69afcf433'
934
+
935
+ Теперь ваш подкаталог `rack` точно в том состоянии, в котором он был, когда вы раньше делали коммит. Если другой разработчик внесёт изменения в код rack и затем сделает коммит, а вы потом обновите эту ссылку и сольёте её, то вы получите что-то странное:
936
+
937
+ $ git merge origin/master
938
+ Updating 0550271..85a3eee
939
+ Fast forward
940
+ rack | 2 +-
941
+ 1 files changed, 1 insertions(+), 1 deletions(-)
942
+ [master*]$ git status
943
+ # On branch master
944
+ # Changes not staged for commit:
945
+ # (use "git add <file>..." to update what will be committed)
946
+ # (use "git checkout -- <file>..." to discard changes in working directory)
947
+ #
948
+ # modified: rack
949
+ #
950
+
951
+ Вы слили то, что по существу является изменением указателя на подмодуль. Но при этом обновления кода в каталоге подмодуля не произошло, так что всё выглядит так, как будто вы имеете грязное состояние в своём рабочем каталоге:
952
+
953
+ $ git diff
954
+ diff --git a/rack b/rack
955
+ index 6c5e70b..08d709f 160000
956
+ --- a/rack
957
+ +++ b/rack
958
+ @@ -1 +1 @@
959
+ -Subproject commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0
960
+ +Subproject commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433
961
+
962
+ Это всё из-за того, что ваш указатель на подмодуль не соответствует тому, что на самом деле находится в каталоге подмодуля. Чтобы исправить это, необходимо снова выполнить `git submodule update`:
963
+
964
+ $ git submodule update
965
+ remote: Counting objects: 5, done.
966
+ remote: Compressing objects: 100% (3/3), done.
967
+ remote: Total 3 (delta 1), reused 2 (delta 0)
968
+ Unpacking objects: 100% (3/3), done.
969
+ From git@github.com:schacon/rack
970
+ 08d709f..6c5e70b master -> origin/master
971
+ Submodule path 'rack': checked out '6c5e70b984a60b3cecd395edd5b48a7575bf58e0'
972
+
973
+ Вы вынуждены делать так каждый раз, когда вы получаете изменения подмодуля в главном проекте. Это странно, но это работает.
974
+
975
+ Распространённая проблема возникает, когда разработчик делает изменения в своей локальной копии подмодуля, но не отправляет их на общий сервер. Затем он создаёт коммит содержащий указатель на это непубличное состояние и отправляет его в основной проект. Когда другие разработчики пытаются выполнить `git submodule update`, система работы с подмодулями не может найти указанный коммит, потому что он существует только в системе первого разработчика. Если такое случится, вы увидите ошибку вроде этой:
976
+
977
+ $ git submodule update
978
+ fatal: reference isn’t a tree: 6c5e70b984a60b3cecd395edd5b48a7575bf58e0
979
+ Unable to checkout '6c5e70b984a60b3cecd395edd5ba7575bf58e0' in submodule path 'rack'
980
+
981
+ Вам надо посмотреть, кто последним менял подмодуль:
982
+
983
+ $ git log -1 rack
984
+ commit 85a3eee996800fcfa91e2119372dd4172bf76678
985
+ Author: Scott Chacon <schacon@gmail.com>
986
+ Date: Thu Apr 9 09:19:14 2009 -0700
987
+
988
+ added a submodule reference I will never make public. hahahahaha!
989
+
990
+ А затем отправить этому человеку письмо со своими возмущениями.
991
+
992
+ ### Суперпроекты ###
993
+
994
+ Иногда разработчики хотят объединить подкаталоги крупного проекта в нечто связанное в зависимости от того, в какой они команде. Это типично для людей, перешедших с CVS или Subversion, где они определяли модуль или набор подкаталогов, и они хотят сохранить данный тип рабочего процесса.
995
+
996
+ Хороший способ сделать такое в Git'е — это сделать каждый из подкаталогов отдельным Git-репозиторием, и создать Git-репозиторий для суперпроекта, который будет содержать несколько подмодулей. Преимущество такого подхода в том, что вы можете более гибко определять отношения между проектами при помощи меток и ветвей в суперпроектах.
997
+
998
+ ### Проблемы с подмодулями ###
999
+
1000
+ Однако, использование подмодулей не обходится без загвоздок. Во-первых, вы должны быть относительно осторожны, работая в каталоге подмодуля. Когда вы выполняете команду `git submodule update`, она возвращает определённую версию проекта, но не внутри ветви. Это называется состоянием с отделённым HEAD (detached HEAD) — это означает, что файл HEAD указывает на конкретный коммит, а не на символическую ссылку. Проблема в том, что вы, скорее всего, не хотите работать в окружении с отделённым HEAD, потому что так легко потерять изменения. Если вы сделаете первоначальный `submodule update`, сделаете коммит в каталоге подмодуля, не создавая ветки для работы в ней, и затем вновь выполните `git submodule update` из основного проекта, без создания коммита в суперпроекте, Git затрёт ваши изменения без предупреждения. Технически вы не потеряете проделанную работу, но у вас не будет ветки, указывающей на неё, так что будет несколько сложновато её восстановить.
1001
+
1002
+ Для предотвращения этой проблемы создавайте ветвь, когда работаете в каталоге подмодуля с использованием команды `git checkout -b work` или какой-нибудь аналогичной. Когда вы сделаете обновление подмодуля командой `submodule update` в следующий раз, она всё же откатит вашу работу, но, по крайней мере, у вас будет указатель для возврата назад.
1003
+
1004
+ Переключение веток с подмодулями в них также может быть мудрёным. Если вы создадите новую ветку, добавите туда подмодуль и затем переключитесь обратно, туда где не было этого подмодуля, вы всё ещё будете иметь каталог подмодуля в виде неотслеживаемого каталога:
1005
+
1006
+ $ git checkout -b rack
1007
+ Switched to a new branch "rack"
1008
+ $ git submodule add git@github.com:schacon/rack.git rack
1009
+ Initialized empty Git repository in /opt/myproj/rack/.git/
1010
+ ...
1011
+ Receiving objects: 100% (3184/3184), 677.42 KiB | 34 KiB/s, done.
1012
+ Resolving deltas: 100% (1952/1952), done.
1013
+ $ git commit -am 'added rack submodule'
1014
+ [rack cc49a69] added rack submodule
1015
+ 2 files changed, 4 insertions(+), 0 deletions(-)
1016
+ create mode 100644 .gitmodules
1017
+ create mode 160000 rack
1018
+ $ git checkout master
1019
+ Switched to branch "master"
1020
+ $ git status
1021
+ # On branch master
1022
+ # Untracked files:
1023
+ # (use "git add <file>..." to include in what will be committed)
1024
+ #
1025
+ # rack/
1026
+
1027
+ Вы будете вынуждены либо переместить каталог подмодуля в другое место, либо удалить его. В случае удаления вам потребуется клонировать его снова при переключении обратно, и тогда вы можете потерять локальные изменения или ветки, которые не были отправлены в основной репозиторий.
1028
+
1029
+ Последняя проблема, которая возникает у многих, и о которой стоит предостеречь, возникает при переходе от подкаталогов к подмодулям. Если вы держали некоторые файлы под версионным контролем в своём проекте, а сейчас хотите перенести их в подмодуль, вам надо быть осторожным, иначе Git разозлится на вас. Допустим, вы держите файлы rack в подкаталоге проекта, и вы хотите вынести его в подмодуль. Если вы просто удалите подкаталог и затем выполните `submodule add`, Git наорёт на вас:
1030
+
1031
+ $ rm -Rf rack/
1032
+ $ git submodule add git@github.com:schacon/rack.git rack
1033
+ 'rack' already exists in the index
1034
+
1035
+ Вначале вам следует убрать каталог `rack` из индекса (убрать из-под версионного контроля). Потом можете добавить подмодуль:
1036
+
1037
+ $ git rm -r rack
1038
+ $ git submodule add git@github.com:schacon/rack.git rack
1039
+ Initialized empty Git repository in /opt/testsub/rack/.git/
1040
+ remote: Counting objects: 3184, done.
1041
+ remote: Compressing objects: 100% (1465/1465), done.
1042
+ remote: Total 3184 (delta 1952), reused 2770 (delta 1675)
1043
+ Receiving objects: 100% (3184/3184), 677.42 KiB | 88 KiB/s, done.
1044
+ Resolving deltas: 100% (1952/1952), done.
1045
+
1046
+ Теперь, предположим, вы сделали это в ветке. Если вы попытаетесь переключиться обратно на ту ветку, где эти файлы всё ещё в актуальном дереве, а не в подмодуле, то вы получите такую ошибку:
1047
+
1048
+ $ git checkout master
1049
+ error: Untracked working tree file 'rack/AUTHORS' would be overwritten by merge.
1050
+
1051
+ Вам следует переместить каталог подмодуля `rack`, перед тем, как вы сможете переключиться на ветку, которая не содержит его:
1052
+
1053
+ $ mv rack /tmp/
1054
+ $ git checkout master
1055
+ Switched to branch "master"
1056
+ $ ls
1057
+ README rack
1058
+
1059
+ Затем, когда вы переключитесь обратно, вы получите пустой каталог `rack`. Вы сможете либо выполнить `git submodule update` для повторного клонирования, или вернуть содержимое вашего каталога `/tmp/rack` обратно в пустой каталог.
1060
+
1061
+ ## Слияние поддеревьев ##
1062
+
1063
+ Теперь, когда вы увидели сложности системы подмодулей, давайте посмотрим на альтернативный путь решения той же проблемы. Когда Git выполняет слияние, он смотрит на то, что требуется слить воедино, и потом выбирает подходящую стратегию слияния. Если вы сливаете две ветви, Git использует _рекурсивную_ (recursive) стратегию. Если вы объединяете более двух ветвей, Git выбирает стратегию _осьминога_ (octopus). Эти стратегии выбираются за вас автоматически потому, что рекурсивная стратегия может обрабатывать сложные трёхсторонние ситуации слияния — например, более чем один общий предок — но она может сливать только две ветви. Слияние методом осьминога может справиться с множеством веток, но является более осторожным, чтобы предотвратить сложные конфликты, так что этот метод является стратегией по умолчанию при слиянии более двух веток.
1064
+
1065
+ Однако, существуют другие стратегии, которые вы также можете выбрать. Одна из них — слияние _поддеревьев_ (subtree), и вы можете использовать его для решения задачи с подпроектами. Сейчас вы увидите, как выполнить то же встраивание rack, как и в предыдущем разделе, но с использованием стратегии слияния поддеревьев.
1066
+
1067
+ Идея слияния поддеревьев состоит в том, что у вас есть два проекта, и один из проектов отображается в подкаталог другого и наоборот. Если вы зададите в качестве стратегии слияния метод subtree, то Git будет достаточно умён, чтобы понять, что один из проектов является поддеревом другого и выполнит слияние в соответствии с этим. И это довольно удивительно.
1068
+
1069
+ Сначала добавим приложение Rack в проект. Добавим проект Rack как внешнюю ссылку в свой проект, и затем поместим его в отдельную ветку:
1070
+
1071
+ $ git remote add rack_remote git@github.com:schacon/rack.git
1072
+ $ git fetch rack_remote
1073
+ warning: no common commits
1074
+ remote: Counting objects: 3184, done.
1075
+ remote: Compressing objects: 100% (1465/1465), done.
1076
+ remote: Total 3184 (delta 1952), reused 2770 (delta 1675)
1077
+ Receiving objects: 100% (3184/3184), 677.42 KiB | 4 KiB/s, done.
1078
+ Resolving deltas: 100% (1952/1952), done.
1079
+ From git@github.com:schacon/rack
1080
+ * [new branch] build -> rack_remote/build
1081
+ * [new branch] master -> rack_remote/master
1082
+ * [new branch] rack-0.4 -> rack_remote/rack-0.4
1083
+ * [new branch] rack-0.9 -> rack_remote/rack-0.9
1084
+ $ git checkout -b rack_branch rack_remote/master
1085
+ Branch rack_branch set up to track remote branch refs/remotes/rack_remote/master.
1086
+ Switched to a new branch "rack_branch"
1087
+
1088
+ Теперь у нас есть корень проекта Rack в ветке `rack_branch` и наш проект в ветке `master`. Если вы переключитесь на одну ветку, а затем на другую, то увидите, что содержимое их корневых каталогов различно:
1089
+
1090
+ $ ls
1091
+ AUTHORS KNOWN-ISSUES Rakefile contrib lib
1092
+ COPYING README bin example test
1093
+ $ git checkout master
1094
+ Switched to branch "master"
1095
+ $ ls
1096
+ README
1097
+
1098
+ Допустим, вы хотите поместить проект Rack в подкаталог своего проекта в ветке `master`. Вы можете сделать это в Git'е командой `git read-tree`. Вы узнаете больше про команду `read-tree` и её друзей в главе 9, а пока достаточно знать, что она считывает корень дерева одной ветки в индекс и рабочий каталог. Вам достаточно переключиться обратно на ветку `master` и вытянуть ветку `rack_branch` в подкаталог `rack` основного проекта из ветки `master`:
1099
+
1100
+ $ git read-tree --prefix=rack/ -u rack_branch
1101
+
1102
+ После того как вы сделаете коммит, все файлы проекта Rack будут находиться в этом подкаталоге — будто вы скопировали их туда из архива. Интересно то, что вы можете довольно легко слить изменения из одной ветки в другую. Так что если проект Rack изменится, вы сможете вытянуть изменения из основного проекта, переключившись в его ветку и выполнив `git pull`:
1103
+
1104
+ $ git checkout rack_branch
1105
+ $ git pull
1106
+
1107
+ Затем вы можете слить эти изменения обратно в свою главную ветку. Можно использовать `git merge -s subtree` — это сработает правильно, но тогда Git, кроме того, объединит вместе истории, чего вы, вероятно, не хотите. Чтобы получить изменения и заполнить сообщение коммита, используйте опции `--squash` и `--no-commit` вместе с опцией стратегии `-s subtree`:
1108
+
1109
+ $ git checkout master
1110
+ $ git merge --squash -s subtree --no-commit rack_branch
1111
+ Squash commit -- not updating HEAD
1112
+ Automatic merge went well; stopped before committing as requested
1113
+
1114
+ Все изменения из проекта Rack слиты и готовы для локальной фиксации. Вы также можете сделать наоборот — внести изменения в подкаталог `rack` вашей ветки `master`, и затем слить их в ветку `rack_branch`, чтобы позже представить их мейнтейнерам или отправить их в основной репозиторий проекта с помощью `git push`.
1115
+
1116
+ Для получения разности между тем, что у вас есть в подкаталоге `rack` и кодом в вашей ветке `rack_branch`, чтобы увидеть нужно ли вам объединять их, вы не можете использовать нормальную команду `diff`. Вместо этого вы должны выполнить `git diff-tree` с веткой, с которой вы хотите сравнить:
1117
+
1118
+ $ git diff-tree -p rack_branch
1119
+
1120
+ Или, для сравнения того, что в вашем подкаталоге `rack` с тем, что было в ветке `master` на сервере во время последнего обновления, можно выполнить:
1121
+
1122
+ $ git diff-tree -p rack_remote/master
1123
+
1124
+ ## Итоги ##
1125
+
1126
+ Вы познакомились с рядом продвинутых инструментов, которые позволяют вам манипулировать вашими коммитами и индексом более совершенно. Если вы заметите проблему, то без труда сможете определить, каким коммитом она внесена, когда и кем. Если вы хотите использовать подпроекты в своём проекте — вы узнали несколько путей, как приспособиться к этим нуждам. К этому моменту вы должны уметь делать в Git'е большинство тех вещей, которые вам понадобятся для повседневной работы в командной строке, и при этом вы будете чувствовать себя комфортно.