rugged 0.19.0 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (453) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +1 -1
  3. data/README.md +151 -25
  4. data/ext/rugged/extconf.rb +48 -27
  5. data/ext/rugged/rugged.c +107 -35
  6. data/ext/rugged/rugged.h +44 -8
  7. data/ext/rugged/rugged_blame.c +292 -0
  8. data/ext/rugged/rugged_blob.c +84 -31
  9. data/ext/rugged/rugged_branch.c +90 -223
  10. data/ext/rugged/rugged_branch_collection.c +445 -0
  11. data/ext/rugged/rugged_commit.c +190 -9
  12. data/ext/rugged/rugged_config.c +1 -1
  13. data/ext/rugged/rugged_cred.c +125 -0
  14. data/ext/rugged/rugged_diff.c +267 -94
  15. data/ext/rugged/rugged_diff_delta.c +14 -3
  16. data/ext/rugged/rugged_diff_hunk.c +31 -28
  17. data/ext/rugged/rugged_diff_line.c +21 -17
  18. data/ext/rugged/rugged_index.c +326 -6
  19. data/ext/rugged/rugged_note.c +39 -34
  20. data/ext/rugged/rugged_object.c +9 -9
  21. data/ext/rugged/rugged_patch.c +245 -0
  22. data/ext/rugged/rugged_reference.c +67 -332
  23. data/ext/rugged/rugged_reference_collection.c +490 -0
  24. data/ext/rugged/rugged_remote.c +447 -351
  25. data/ext/rugged/rugged_remote_collection.c +285 -0
  26. data/ext/rugged/rugged_repo.c +752 -203
  27. data/ext/rugged/rugged_revwalk.c +119 -27
  28. data/ext/rugged/rugged_settings.c +48 -1
  29. data/ext/rugged/rugged_signature.c +30 -16
  30. data/ext/rugged/rugged_tag.c +86 -191
  31. data/ext/rugged/rugged_tag_collection.c +279 -0
  32. data/ext/rugged/rugged_tree.c +159 -22
  33. data/lib/rugged/branch.rb +8 -17
  34. data/lib/rugged/credentials.rb +43 -0
  35. data/lib/rugged/diff/delta.rb +1 -2
  36. data/lib/rugged/diff/hunk.rb +4 -9
  37. data/lib/rugged/diff/line.rb +23 -3
  38. data/lib/rugged/diff.rb +3 -1
  39. data/lib/rugged/patch.rb +26 -0
  40. data/lib/rugged/reference.rb +1 -3
  41. data/lib/rugged/remote.rb +0 -9
  42. data/lib/rugged/repository.rb +70 -13
  43. data/lib/rugged/tag.rb +23 -18
  44. data/lib/rugged/tree.rb +7 -0
  45. data/lib/rugged/version.rb +1 -1
  46. data/lib/rugged.rb +8 -1
  47. data/vendor/libgit2/AUTHORS +75 -0
  48. data/vendor/libgit2/CMakeLists.txt +490 -0
  49. data/vendor/libgit2/COPYING +920 -0
  50. data/vendor/libgit2/Makefile.embed +25 -7
  51. data/vendor/libgit2/cmake/Modules/AddCFlagIfSupported.cmake +16 -0
  52. data/vendor/libgit2/cmake/Modules/FindHTTP_Parser.cmake +39 -0
  53. data/vendor/libgit2/cmake/Modules/FindIconv.cmake +43 -0
  54. data/vendor/libgit2/cmake/Modules/FindLIBSSH2.cmake +44 -0
  55. data/vendor/libgit2/deps/http-parser/LICENSE-MIT +23 -0
  56. data/vendor/libgit2/deps/regex/regex.c +10 -3
  57. data/vendor/libgit2/include/git2/attr.h +2 -1
  58. data/vendor/libgit2/include/git2/blame.h +213 -0
  59. data/vendor/libgit2/include/git2/blob.h +57 -29
  60. data/vendor/libgit2/include/git2/branch.h +56 -44
  61. data/vendor/libgit2/include/git2/buffer.h +112 -0
  62. data/vendor/libgit2/include/git2/checkout.h +72 -37
  63. data/vendor/libgit2/include/git2/cherrypick.h +87 -0
  64. data/vendor/libgit2/include/git2/clone.h +153 -56
  65. data/vendor/libgit2/include/git2/commit.h +82 -12
  66. data/vendor/libgit2/include/git2/common.h +31 -19
  67. data/vendor/libgit2/include/git2/config.h +124 -19
  68. data/vendor/libgit2/include/git2/cred_helpers.h +1 -1
  69. data/vendor/libgit2/include/git2/diff.h +636 -494
  70. data/vendor/libgit2/include/git2/errors.h +48 -15
  71. data/vendor/libgit2/include/git2/filter.h +149 -0
  72. data/vendor/libgit2/include/git2/graph.h +14 -0
  73. data/vendor/libgit2/include/git2/index.h +95 -54
  74. data/vendor/libgit2/include/git2/indexer.h +15 -9
  75. data/vendor/libgit2/include/git2/merge.h +402 -39
  76. data/vendor/libgit2/include/git2/message.h +9 -14
  77. data/vendor/libgit2/include/git2/net.h +5 -0
  78. data/vendor/libgit2/include/git2/notes.h +6 -6
  79. data/vendor/libgit2/include/git2/object.h +34 -2
  80. data/vendor/libgit2/include/git2/odb.h +77 -14
  81. data/vendor/libgit2/include/git2/odb_backend.h +50 -6
  82. data/vendor/libgit2/include/git2/oid.h +4 -8
  83. data/vendor/libgit2/include/git2/pack.h +58 -4
  84. data/vendor/libgit2/include/git2/patch.h +277 -0
  85. data/vendor/libgit2/include/git2/pathspec.h +263 -0
  86. data/vendor/libgit2/include/git2/push.h +55 -5
  87. data/vendor/libgit2/include/git2/reflog.h +11 -8
  88. data/vendor/libgit2/include/git2/refs.h +219 -33
  89. data/vendor/libgit2/include/git2/refspec.h +3 -4
  90. data/vendor/libgit2/include/git2/remote.h +216 -76
  91. data/vendor/libgit2/include/git2/repository.h +85 -40
  92. data/vendor/libgit2/include/git2/reset.h +15 -4
  93. data/vendor/libgit2/include/git2/revert.h +86 -0
  94. data/vendor/libgit2/include/git2/revparse.h +27 -16
  95. data/vendor/libgit2/include/git2/revwalk.h +44 -6
  96. data/vendor/libgit2/include/git2/signature.h +17 -3
  97. data/vendor/libgit2/include/git2/stash.h +8 -12
  98. data/vendor/libgit2/include/git2/status.h +67 -18
  99. data/vendor/libgit2/include/git2/submodule.h +100 -85
  100. data/vendor/libgit2/include/git2/sys/commit.h +38 -4
  101. data/vendor/libgit2/include/git2/sys/config.h +44 -3
  102. data/vendor/libgit2/include/git2/sys/diff.h +91 -0
  103. data/vendor/libgit2/include/git2/sys/filter.h +301 -0
  104. data/vendor/libgit2/include/git2/sys/index.h +0 -2
  105. data/vendor/libgit2/include/git2/sys/mempack.h +85 -0
  106. data/vendor/libgit2/include/git2/sys/odb_backend.h +33 -11
  107. data/vendor/libgit2/include/git2/sys/refdb_backend.h +51 -5
  108. data/vendor/libgit2/include/git2/sys/reflog.h +21 -0
  109. data/vendor/libgit2/include/git2/sys/refs.h +2 -2
  110. data/vendor/libgit2/include/git2/sys/repository.h +19 -1
  111. data/vendor/libgit2/include/git2/transport.h +216 -75
  112. data/vendor/libgit2/include/git2/tree.h +32 -11
  113. data/vendor/libgit2/include/git2/types.h +124 -10
  114. data/vendor/libgit2/include/git2/version.h +4 -2
  115. data/vendor/libgit2/include/git2.h +41 -40
  116. data/vendor/libgit2/libgit2.pc.in +10 -0
  117. data/vendor/libgit2/src/array.h +16 -8
  118. data/vendor/libgit2/src/attr.c +140 -402
  119. data/vendor/libgit2/src/attr.h +1 -33
  120. data/vendor/libgit2/src/attr_file.c +298 -135
  121. data/vendor/libgit2/src/attr_file.h +61 -22
  122. data/vendor/libgit2/src/attrcache.c +449 -0
  123. data/vendor/libgit2/src/attrcache.h +38 -6
  124. data/vendor/libgit2/src/bitvec.h +75 -0
  125. data/vendor/libgit2/src/blame.c +488 -0
  126. data/vendor/libgit2/src/blame.h +93 -0
  127. data/vendor/libgit2/src/blame_git.c +624 -0
  128. data/vendor/libgit2/src/blame_git.h +20 -0
  129. data/vendor/libgit2/src/blob.c +137 -92
  130. data/vendor/libgit2/src/blob.h +9 -0
  131. data/vendor/libgit2/src/branch.c +175 -156
  132. data/vendor/libgit2/src/buf_text.c +29 -14
  133. data/vendor/libgit2/src/buf_text.h +5 -4
  134. data/vendor/libgit2/src/buffer.c +158 -15
  135. data/vendor/libgit2/src/buffer.h +33 -20
  136. data/vendor/libgit2/src/cc-compat.h +8 -2
  137. data/vendor/libgit2/src/checkout.c +1081 -287
  138. data/vendor/libgit2/src/checkout.h +1 -1
  139. data/vendor/libgit2/src/cherrypick.c +226 -0
  140. data/vendor/libgit2/src/clone.c +297 -245
  141. data/vendor/libgit2/src/{compress.h → clone.h} +4 -8
  142. data/vendor/libgit2/src/commit.c +313 -101
  143. data/vendor/libgit2/src/commit.h +6 -3
  144. data/vendor/libgit2/src/commit_list.c +3 -3
  145. data/vendor/libgit2/src/commit_list.h +1 -1
  146. data/vendor/libgit2/src/common.h +74 -1
  147. data/vendor/libgit2/src/config.c +570 -145
  148. data/vendor/libgit2/src/config.h +39 -6
  149. data/vendor/libgit2/src/config_cache.c +32 -18
  150. data/vendor/libgit2/src/config_file.c +711 -424
  151. data/vendor/libgit2/src/config_file.h +4 -3
  152. data/vendor/libgit2/src/crlf.c +149 -109
  153. data/vendor/libgit2/src/date.c +35 -7
  154. data/vendor/libgit2/src/delta.c +1 -1
  155. data/vendor/libgit2/src/diff.c +719 -414
  156. data/vendor/libgit2/src/diff.h +52 -7
  157. data/vendor/libgit2/src/diff_driver.c +183 -85
  158. data/vendor/libgit2/src/diff_driver.h +1 -1
  159. data/vendor/libgit2/src/diff_file.c +65 -84
  160. data/vendor/libgit2/src/diff_file.h +12 -10
  161. data/vendor/libgit2/src/diff_patch.c +274 -333
  162. data/vendor/libgit2/src/diff_patch.h +10 -9
  163. data/vendor/libgit2/src/diff_print.c +381 -179
  164. data/vendor/libgit2/src/diff_stats.c +336 -0
  165. data/vendor/libgit2/src/diff_tform.c +393 -215
  166. data/vendor/libgit2/src/diff_xdiff.c +117 -42
  167. data/vendor/libgit2/src/errors.c +59 -4
  168. data/vendor/libgit2/src/fetch.c +40 -34
  169. data/vendor/libgit2/src/fetch.h +2 -5
  170. data/vendor/libgit2/src/fetchhead.c +14 -7
  171. data/vendor/libgit2/src/filebuf.c +13 -24
  172. data/vendor/libgit2/src/filebuf.h +3 -3
  173. data/vendor/libgit2/src/fileops.c +104 -296
  174. data/vendor/libgit2/src/fileops.h +23 -94
  175. data/vendor/libgit2/src/filter.c +642 -43
  176. data/vendor/libgit2/src/filter.h +7 -66
  177. data/vendor/libgit2/src/fnmatch.c +46 -3
  178. data/vendor/libgit2/src/fnmatch.h +24 -3
  179. data/vendor/libgit2/src/global.c +158 -42
  180. data/vendor/libgit2/src/global.h +9 -0
  181. data/vendor/libgit2/src/graph.c +34 -20
  182. data/vendor/libgit2/src/hash/hash_generic.h +0 -1
  183. data/vendor/libgit2/src/hash/hash_openssl.h +0 -1
  184. data/vendor/libgit2/src/hash/hash_win32.c +14 -29
  185. data/vendor/libgit2/src/hash.h +0 -2
  186. data/vendor/libgit2/src/hashsig.c +97 -117
  187. data/vendor/libgit2/src/ident.c +125 -0
  188. data/vendor/libgit2/src/ignore.c +159 -110
  189. data/vendor/libgit2/src/ignore.h +13 -3
  190. data/vendor/libgit2/src/index.c +803 -445
  191. data/vendor/libgit2/src/index.h +43 -6
  192. data/vendor/libgit2/src/indexer.c +475 -157
  193. data/vendor/libgit2/src/iterator.c +198 -55
  194. data/vendor/libgit2/src/iterator.h +28 -4
  195. data/vendor/libgit2/src/map.h +1 -0
  196. data/vendor/libgit2/src/merge.c +849 -142
  197. data/vendor/libgit2/src/merge.h +11 -4
  198. data/vendor/libgit2/src/merge_file.c +183 -78
  199. data/vendor/libgit2/src/merge_file.h +0 -57
  200. data/vendor/libgit2/src/message.c +4 -28
  201. data/vendor/libgit2/src/mwindow.c +117 -8
  202. data/vendor/libgit2/src/mwindow.h +9 -1
  203. data/vendor/libgit2/src/netops.c +164 -59
  204. data/vendor/libgit2/src/netops.h +37 -1
  205. data/vendor/libgit2/src/notes.c +9 -18
  206. data/vendor/libgit2/src/notes.h +1 -1
  207. data/vendor/libgit2/src/object.c +78 -2
  208. data/vendor/libgit2/src/odb.c +191 -59
  209. data/vendor/libgit2/src/odb.h +2 -1
  210. data/vendor/libgit2/src/odb_loose.c +66 -51
  211. data/vendor/libgit2/src/odb_mempack.c +182 -0
  212. data/vendor/libgit2/src/odb_pack.c +151 -61
  213. data/vendor/libgit2/src/oid.c +30 -19
  214. data/vendor/libgit2/src/oid.h +13 -10
  215. data/vendor/libgit2/src/pack-objects.c +198 -147
  216. data/vendor/libgit2/src/pack-objects.h +7 -0
  217. data/vendor/libgit2/src/pack.c +272 -101
  218. data/vendor/libgit2/src/pack.h +15 -1
  219. data/vendor/libgit2/src/path.c +359 -117
  220. data/vendor/libgit2/src/path.h +110 -20
  221. data/vendor/libgit2/src/pathspec.c +583 -57
  222. data/vendor/libgit2/src/pathspec.h +36 -15
  223. data/vendor/libgit2/src/pool.c +4 -5
  224. data/vendor/libgit2/src/posix.c +45 -2
  225. data/vendor/libgit2/src/posix.h +13 -5
  226. data/vendor/libgit2/src/pqueue.c +73 -119
  227. data/vendor/libgit2/src/pqueue.h +35 -82
  228. data/vendor/libgit2/src/push.c +116 -48
  229. data/vendor/libgit2/src/push.h +5 -0
  230. data/vendor/libgit2/src/refdb.c +45 -6
  231. data/vendor/libgit2/src/refdb.h +13 -3
  232. data/vendor/libgit2/src/refdb_fs.c +1130 -551
  233. data/vendor/libgit2/src/reflog.c +36 -327
  234. data/vendor/libgit2/src/reflog.h +6 -1
  235. data/vendor/libgit2/src/refs.c +345 -142
  236. data/vendor/libgit2/src/refs.h +9 -2
  237. data/vendor/libgit2/src/refspec.c +90 -62
  238. data/vendor/libgit2/src/refspec.h +7 -24
  239. data/vendor/libgit2/src/remote.c +815 -415
  240. data/vendor/libgit2/src/remote.h +3 -4
  241. data/vendor/libgit2/src/repository.c +360 -207
  242. data/vendor/libgit2/src/repository.h +16 -10
  243. data/vendor/libgit2/src/reset.c +28 -13
  244. data/vendor/libgit2/src/revert.c +228 -0
  245. data/vendor/libgit2/src/revparse.c +29 -30
  246. data/vendor/libgit2/src/revwalk.c +141 -96
  247. data/vendor/libgit2/src/revwalk.h +6 -1
  248. data/vendor/libgit2/src/settings.c +140 -0
  249. data/vendor/libgit2/src/sha1_lookup.c +71 -0
  250. data/vendor/libgit2/src/sha1_lookup.h +5 -0
  251. data/vendor/libgit2/src/signature.c +38 -10
  252. data/vendor/libgit2/src/sortedcache.c +378 -0
  253. data/vendor/libgit2/src/sortedcache.h +178 -0
  254. data/vendor/libgit2/src/stash.c +98 -116
  255. data/vendor/libgit2/src/status.c +88 -60
  256. data/vendor/libgit2/src/status.h +2 -2
  257. data/vendor/libgit2/src/strmap.c +32 -0
  258. data/vendor/libgit2/src/strmap.h +14 -1
  259. data/vendor/libgit2/src/strnlen.h +23 -0
  260. data/vendor/libgit2/src/submodule.c +1073 -615
  261. data/vendor/libgit2/src/submodule.h +89 -21
  262. data/vendor/libgit2/src/sysdir.c +252 -0
  263. data/vendor/libgit2/src/sysdir.h +101 -0
  264. data/vendor/libgit2/src/tag.c +31 -20
  265. data/vendor/libgit2/src/thread-utils.h +98 -17
  266. data/vendor/libgit2/src/trace.h +0 -2
  267. data/vendor/libgit2/src/transport.c +76 -6
  268. data/vendor/libgit2/src/transports/cred.c +164 -61
  269. data/vendor/libgit2/src/transports/git.c +41 -48
  270. data/vendor/libgit2/src/transports/http.c +65 -109
  271. data/vendor/libgit2/src/transports/local.c +88 -65
  272. data/vendor/libgit2/src/transports/smart.c +91 -19
  273. data/vendor/libgit2/src/transports/smart.h +13 -5
  274. data/vendor/libgit2/src/transports/smart_pkt.c +24 -14
  275. data/vendor/libgit2/src/transports/smart_protocol.c +268 -113
  276. data/vendor/libgit2/src/transports/ssh.c +284 -186
  277. data/vendor/libgit2/src/transports/winhttp.c +279 -198
  278. data/vendor/libgit2/src/tree-cache.c +21 -23
  279. data/vendor/libgit2/src/tree-cache.h +1 -0
  280. data/vendor/libgit2/src/tree.c +109 -92
  281. data/vendor/libgit2/src/tree.h +2 -3
  282. data/vendor/libgit2/src/unix/map.c +7 -1
  283. data/vendor/libgit2/src/unix/posix.h +0 -1
  284. data/vendor/libgit2/src/userdiff.h +208 -0
  285. data/vendor/libgit2/src/util.c +16 -112
  286. data/vendor/libgit2/src/util.h +107 -3
  287. data/vendor/libgit2/src/vector.c +72 -17
  288. data/vendor/libgit2/src/vector.h +32 -5
  289. data/vendor/libgit2/src/win32/dir.c +15 -40
  290. data/vendor/libgit2/src/win32/dir.h +3 -2
  291. data/vendor/libgit2/src/win32/error.c +5 -31
  292. data/vendor/libgit2/src/win32/findfile.c +47 -66
  293. data/vendor/libgit2/src/win32/findfile.h +1 -12
  294. data/vendor/libgit2/src/win32/git2.rc +40 -0
  295. data/vendor/libgit2/src/win32/map.c +7 -1
  296. data/vendor/libgit2/src/win32/mingw-compat.h +3 -0
  297. data/vendor/libgit2/src/win32/posix.h +17 -11
  298. data/vendor/libgit2/src/win32/posix_w32.c +424 -292
  299. data/vendor/libgit2/src/win32/precompiled.h +6 -2
  300. data/vendor/libgit2/src/win32/pthread.c +141 -18
  301. data/vendor/libgit2/src/win32/pthread.h +50 -8
  302. data/vendor/libgit2/src/win32/reparse.h +57 -0
  303. data/vendor/libgit2/src/win32/utf-conv.c +117 -60
  304. data/vendor/libgit2/src/win32/utf-conv.h +81 -6
  305. data/vendor/libgit2/src/win32/version.h +21 -4
  306. data/vendor/libgit2/src/win32/w32_util.c +139 -0
  307. data/vendor/libgit2/src/win32/w32_util.h +54 -0
  308. data/vendor/libgit2/src/zstream.c +156 -0
  309. data/vendor/libgit2/src/zstream.h +39 -0
  310. metadata +84 -167
  311. data/Rakefile +0 -61
  312. data/ext/rugged/rugged_diff_patch.c +0 -169
  313. data/lib/rugged/diff/patch.rb +0 -28
  314. data/test/blob_test.rb +0 -341
  315. data/test/branch_test.rb +0 -199
  316. data/test/commit_test.rb +0 -104
  317. data/test/config_test.rb +0 -45
  318. data/test/coverage/cover.rb +0 -133
  319. data/test/diff_test.rb +0 -777
  320. data/test/errors_test.rb +0 -34
  321. data/test/fixtures/alternate/objects/14/6ae76773c91e3b1d00cf7a338ec55ae58297e2 +0 -0
  322. data/test/fixtures/alternate/objects/14/9c32d47e99d0a3572ff1e70a2e0051bbf347a9 +0 -0
  323. data/test/fixtures/alternate/objects/14/fb3108588f9421bf764041e5e3ac305eb6277f +0 -0
  324. data/test/fixtures/archive.tar.gz +0 -0
  325. data/test/fixtures/attr/attr0 +0 -1
  326. data/test/fixtures/attr/attr1 +0 -29
  327. data/test/fixtures/attr/attr2 +0 -21
  328. data/test/fixtures/attr/attr3 +0 -4
  329. data/test/fixtures/attr/binfile +0 -1
  330. data/test/fixtures/attr/dir/file +0 -0
  331. data/test/fixtures/attr/file +0 -1
  332. data/test/fixtures/attr/gitattributes +0 -29
  333. data/test/fixtures/attr/gitignore +0 -2
  334. data/test/fixtures/attr/ign +0 -1
  335. data/test/fixtures/attr/macro_bad +0 -1
  336. data/test/fixtures/attr/macro_test +0 -1
  337. data/test/fixtures/attr/root_test1 +0 -1
  338. data/test/fixtures/attr/root_test2 +0 -6
  339. data/test/fixtures/attr/root_test3 +0 -19
  340. data/test/fixtures/attr/root_test4.txt +0 -14
  341. data/test/fixtures/attr/sub/abc +0 -37
  342. data/test/fixtures/attr/sub/dir/file +0 -0
  343. data/test/fixtures/attr/sub/file +0 -1
  344. data/test/fixtures/attr/sub/ign/file +0 -1
  345. data/test/fixtures/attr/sub/ign/sub/file +0 -1
  346. data/test/fixtures/attr/sub/sub/dir +0 -0
  347. data/test/fixtures/attr/sub/sub/file +0 -1
  348. data/test/fixtures/attr/sub/sub/subsub.txt +0 -1
  349. data/test/fixtures/attr/sub/subdir_test1 +0 -2
  350. data/test/fixtures/attr/sub/subdir_test2.txt +0 -1
  351. data/test/fixtures/diff/another.txt +0 -38
  352. data/test/fixtures/diff/readme.txt +0 -36
  353. data/test/fixtures/mergedrepo/conflicts-one.txt +0 -5
  354. data/test/fixtures/mergedrepo/conflicts-two.txt +0 -5
  355. data/test/fixtures/mergedrepo/one.txt +0 -10
  356. data/test/fixtures/mergedrepo/two.txt +0 -12
  357. data/test/fixtures/status/current_file +0 -1
  358. data/test/fixtures/status/ignored_file +0 -1
  359. data/test/fixtures/status/modified_file +0 -2
  360. data/test/fixtures/status/new_file +0 -1
  361. data/test/fixtures/status/staged_changes +0 -2
  362. data/test/fixtures/status/staged_changes_modified_file +0 -3
  363. data/test/fixtures/status/staged_delete_modified_file +0 -1
  364. data/test/fixtures/status/staged_new_file +0 -1
  365. data/test/fixtures/status/staged_new_file_modified_file +0 -2
  366. data/test/fixtures/status/subdir/current_file +0 -1
  367. data/test/fixtures/status/subdir/modified_file +0 -2
  368. data/test/fixtures/status/subdir/new_file +0 -1
  369. data/test/fixtures/status/subdir.txt +0 -2
  370. data/test/fixtures/status//350/277/231 +0 -1
  371. data/test/fixtures/testrepo.git/HEAD +0 -1
  372. data/test/fixtures/testrepo.git/config +0 -13
  373. data/test/fixtures/testrepo.git/description +0 -1
  374. data/test/fixtures/testrepo.git/index +0 -0
  375. data/test/fixtures/testrepo.git/info/exclude +0 -6
  376. data/test/fixtures/testrepo.git/logs/HEAD +0 -3
  377. data/test/fixtures/testrepo.git/logs/refs/heads/master +0 -3
  378. data/test/fixtures/testrepo.git/logs/refs/notes/commits +0 -1
  379. data/test/fixtures/testrepo.git/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1d +0 -0
  380. data/test/fixtures/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 +0 -0
  381. data/test/fixtures/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 +0 -0
  382. data/test/fixtures/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd +0 -0
  383. data/test/fixtures/testrepo.git/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2 +0 -0
  384. data/test/fixtures/testrepo.git/objects/36/060c58702ed4c2a40832c51758d5344201d89a +0 -2
  385. data/test/fixtures/testrepo.git/objects/44/1034f860c1d5d90e4188d11ae0d325176869a8 +0 -1
  386. data/test/fixtures/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 +0 -0
  387. data/test/fixtures/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 +0 -2
  388. data/test/fixtures/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 +0 -2
  389. data/test/fixtures/testrepo.git/objects/60/d415052a33de2150bf68757f6461df4f563ae4 +0 -0
  390. data/test/fixtures/testrepo.git/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96 +0 -0
  391. data/test/fixtures/testrepo.git/objects/68/8a8f4ef7496901d15322972f96e212a9e466cc +0 -1
  392. data/test/fixtures/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a +0 -0
  393. data/test/fixtures/testrepo.git/objects/77/71329dfa3002caf8c61a0ceb62a31d09023f37 +0 -0
  394. data/test/fixtures/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d +0 -0
  395. data/test/fixtures/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479 +0 -0
  396. data/test/fixtures/testrepo.git/objects/94/eca2de348d5f672faf56b0decafa5937e3235e +0 -0
  397. data/test/fixtures/testrepo.git/objects/9b/7384fe1676186192842f5d3e129457b62db9e3 +0 -0
  398. data/test/fixtures/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a +0 -3
  399. data/test/fixtures/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f +0 -2
  400. data/test/fixtures/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd +0 -0
  401. data/test/fixtures/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 +0 -0
  402. data/test/fixtures/testrepo.git/objects/b7/4713326bc972cc15751ed504dca6f6f3b91f7a +0 -3
  403. data/test/fixtures/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 +0 -3
  404. data/test/fixtures/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd +0 -3
  405. data/test/fixtures/testrepo.git/objects/c4/dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b +0 -0
  406. data/test/fixtures/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 +0 -0
  407. data/test/fixtures/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 +0 -0
  408. data/test/fixtures/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 +0 -0
  409. data/test/fixtures/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765 +0 -0
  410. data/test/fixtures/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx +0 -0
  411. data/test/fixtures/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack +0 -0
  412. data/test/fixtures/testrepo.git/packed-refs +0 -2
  413. data/test/fixtures/testrepo.git/refs/heads/master +0 -1
  414. data/test/fixtures/testrepo.git/refs/notes/commits +0 -1
  415. data/test/fixtures/testrepo.git/refs/tags/v0.9 +0 -1
  416. data/test/fixtures/testrepo.git/refs/tags/v1.0 +0 -1
  417. data/test/fixtures/text_file.md +0 -464
  418. data/test/fixtures/unsymlinked.git/HEAD +0 -1
  419. data/test/fixtures/unsymlinked.git/config +0 -6
  420. data/test/fixtures/unsymlinked.git/description +0 -1
  421. data/test/fixtures/unsymlinked.git/info/exclude +0 -2
  422. data/test/fixtures/unsymlinked.git/objects/08/8b64704e0d6b8bd061dea879418cb5442a3fbf +0 -0
  423. data/test/fixtures/unsymlinked.git/objects/13/a5e939bca25940c069fd2169d993dba328e30b +0 -0
  424. data/test/fixtures/unsymlinked.git/objects/19/bf568e59e3a0b363cafb4106226e62d4a4c41c +0 -0
  425. data/test/fixtures/unsymlinked.git/objects/58/1fadd35b4cf320d102a152f918729011604773 +0 -0
  426. data/test/fixtures/unsymlinked.git/objects/5c/87b6791e8b13da658a14d1ef7e09b5dc3bac8c +0 -0
  427. data/test/fixtures/unsymlinked.git/objects/6f/e5f5398af85fb3de8a6aba0339b6d3bfa26a27 +0 -0
  428. data/test/fixtures/unsymlinked.git/objects/7f/ccd75616ec188b8f1b23d67506a334cc34a49d +0 -0
  429. data/test/fixtures/unsymlinked.git/objects/80/6999882bf91d24241e4077906b9017605eb1f3 +0 -0
  430. data/test/fixtures/unsymlinked.git/objects/83/7d176303c5005505ec1e4a30231c40930c0230 +0 -0
  431. data/test/fixtures/unsymlinked.git/objects/a8/595ccca04f40818ae0155c8f9c77a230e597b6 +0 -2
  432. data/test/fixtures/unsymlinked.git/objects/cf/8f1cf5cce859c438d6cc067284cb5e161206e7 +0 -0
  433. data/test/fixtures/unsymlinked.git/objects/d5/278d05c8607ec420bfee4cf219fbc0eeebfd6a +0 -0
  434. data/test/fixtures/unsymlinked.git/objects/f4/e16fb76536591a41454194058d048d8e4dd2e9 +0 -0
  435. data/test/fixtures/unsymlinked.git/objects/f9/e65619d93fdf2673882e0a261c5e93b1a84006 +0 -0
  436. data/test/fixtures/unsymlinked.git/refs/heads/exe-file +0 -1
  437. data/test/fixtures/unsymlinked.git/refs/heads/master +0 -1
  438. data/test/fixtures/unsymlinked.git/refs/heads/reg-file +0 -1
  439. data/test/index_test.rb +0 -333
  440. data/test/lib_test.rb +0 -127
  441. data/test/note_test.rb +0 -158
  442. data/test/object_test.rb +0 -43
  443. data/test/reference_test.rb +0 -207
  444. data/test/remote_test.rb +0 -324
  445. data/test/repo_pack_test.rb +0 -24
  446. data/test/repo_reset_test.rb +0 -82
  447. data/test/repo_test.rb +0 -402
  448. data/test/tag_test.rb +0 -68
  449. data/test/test_helper.rb +0 -92
  450. data/test/tree_test.rb +0 -91
  451. data/test/walker_test.rb +0 -88
  452. data/vendor/libgit2/src/amiga/map.c +0 -48
  453. data/vendor/libgit2/src/compress.c +0 -53
@@ -13,6 +13,7 @@
13
13
  #include "pathspec.h"
14
14
  #include "index.h"
15
15
  #include "odb.h"
16
+ #include "submodule.h"
16
17
 
17
18
  #define DIFF_FLAG_IS_SET(DIFF,FLAG) (((DIFF)->opts.flags & (FLAG)) != 0)
18
19
  #define DIFF_FLAG_ISNT_SET(DIFF,FLAG) (((DIFF)->opts.flags & (FLAG)) == 0)
@@ -20,7 +21,7 @@
20
21
  (VAL) ? ((DIFF)->opts.flags | (FLAG)) : ((DIFF)->opts.flags & ~(VAL))
21
22
 
22
23
  static git_diff_delta *diff_delta__alloc(
23
- git_diff_list *diff,
24
+ git_diff *diff,
24
25
  git_delta_t status,
25
26
  const char *path)
26
27
  {
@@ -48,26 +49,41 @@ static git_diff_delta *diff_delta__alloc(
48
49
  return delta;
49
50
  }
50
51
 
51
- static int diff_notify(
52
- const git_diff_list *diff,
53
- const git_diff_delta *delta,
54
- const char *matched_pathspec)
52
+ static int diff_insert_delta(
53
+ git_diff *diff, git_diff_delta *delta, const char *matched_pathspec)
55
54
  {
56
- if (!diff->opts.notify_cb)
57
- return 0;
55
+ int error = 0;
56
+
57
+ if (diff->opts.notify_cb) {
58
+ error = diff->opts.notify_cb(
59
+ diff, delta, matched_pathspec, diff->opts.notify_payload);
60
+
61
+ if (error) {
62
+ git__free(delta);
63
+
64
+ if (error > 0) /* positive value means to skip this delta */
65
+ return 0;
66
+ else /* negative value means to cancel diff */
67
+ return giterr_set_after_callback_function(error, "git_diff");
68
+ }
69
+ }
70
+
71
+ if ((error = git_vector_insert(&diff->deltas, delta)) < 0)
72
+ git__free(delta);
58
73
 
59
- return diff->opts.notify_cb(
60
- diff, delta, matched_pathspec, diff->opts.notify_payload);
74
+ return error;
61
75
  }
62
76
 
63
77
  static int diff_delta__from_one(
64
- git_diff_list *diff,
65
- git_delta_t status,
78
+ git_diff *diff,
79
+ git_delta_t status,
66
80
  const git_index_entry *entry)
67
81
  {
68
82
  git_diff_delta *delta;
69
83
  const char *matched_pathspec;
70
- int notify_res;
84
+
85
+ if ((entry->flags & GIT_IDXENTRY_VALID) != 0)
86
+ return 0;
71
87
 
72
88
  if (status == GIT_DELTA_IGNORED &&
73
89
  DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_IGNORED))
@@ -77,15 +93,11 @@ static int diff_delta__from_one(
77
93
  DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNTRACKED))
78
94
  return 0;
79
95
 
80
- if (entry->mode == GIT_FILEMODE_COMMIT &&
81
- DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES))
82
- return 0;
83
-
84
- if (!git_pathspec_match_path(
96
+ if (!git_pathspec__match(
85
97
  &diff->pathspec, entry->path,
86
98
  DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH),
87
- DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE),
88
- &matched_pathspec))
99
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE),
100
+ &matched_pathspec, NULL))
89
101
  return 0;
90
102
 
91
103
  delta = diff_delta__alloc(diff, status, entry->path);
@@ -93,38 +105,30 @@ static int diff_delta__from_one(
93
105
 
94
106
  /* This fn is just for single-sided diffs */
95
107
  assert(status != GIT_DELTA_MODIFIED);
108
+ delta->nfiles = 1;
96
109
 
97
110
  if (delta->status == GIT_DELTA_DELETED) {
98
111
  delta->old_file.mode = entry->mode;
99
112
  delta->old_file.size = entry->file_size;
100
- git_oid_cpy(&delta->old_file.oid, &entry->oid);
113
+ git_oid_cpy(&delta->old_file.id, &entry->id);
101
114
  } else /* ADDED, IGNORED, UNTRACKED */ {
102
115
  delta->new_file.mode = entry->mode;
103
116
  delta->new_file.size = entry->file_size;
104
- git_oid_cpy(&delta->new_file.oid, &entry->oid);
117
+ git_oid_cpy(&delta->new_file.id, &entry->id);
105
118
  }
106
119
 
107
- delta->old_file.flags |= GIT_DIFF_FLAG_VALID_OID;
120
+ delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID;
108
121
 
109
122
  if (delta->status == GIT_DELTA_DELETED ||
110
- !git_oid_iszero(&delta->new_file.oid))
111
- delta->new_file.flags |= GIT_DIFF_FLAG_VALID_OID;
112
-
113
- notify_res = diff_notify(diff, delta, matched_pathspec);
123
+ !git_oid_iszero(&delta->new_file.id))
124
+ delta->new_file.flags |= GIT_DIFF_FLAG_VALID_ID;
114
125
 
115
- if (notify_res)
116
- git__free(delta);
117
- else if (git_vector_insert(&diff->deltas, delta) < 0) {
118
- git__free(delta);
119
- return -1;
120
- }
121
-
122
- return notify_res < 0 ? GIT_EUSER : 0;
126
+ return diff_insert_delta(diff, delta, matched_pathspec);
123
127
  }
124
128
 
125
129
  static int diff_delta__from_two(
126
- git_diff_list *diff,
127
- git_delta_t status,
130
+ git_diff *diff,
131
+ git_delta_t status,
128
132
  const git_index_entry *old_entry,
129
133
  uint32_t old_mode,
130
134
  const git_index_entry *new_entry,
@@ -133,18 +137,12 @@ static int diff_delta__from_two(
133
137
  const char *matched_pathspec)
134
138
  {
135
139
  git_diff_delta *delta;
136
- int notify_res;
137
140
  const char *canonical_path = old_entry->path;
138
141
 
139
142
  if (status == GIT_DELTA_UNMODIFIED &&
140
143
  DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNMODIFIED))
141
144
  return 0;
142
145
 
143
- if (old_entry->mode == GIT_FILEMODE_COMMIT &&
144
- new_entry->mode == GIT_FILEMODE_COMMIT &&
145
- DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES))
146
- return 0;
147
-
148
146
  if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
149
147
  uint32_t temp_mode = old_mode;
150
148
  const git_index_entry *temp_entry = old_entry;
@@ -156,40 +154,32 @@ static int diff_delta__from_two(
156
154
 
157
155
  delta = diff_delta__alloc(diff, status, canonical_path);
158
156
  GITERR_CHECK_ALLOC(delta);
157
+ delta->nfiles = 2;
159
158
 
160
- git_oid_cpy(&delta->old_file.oid, &old_entry->oid);
159
+ git_oid_cpy(&delta->old_file.id, &old_entry->id);
161
160
  delta->old_file.size = old_entry->file_size;
162
161
  delta->old_file.mode = old_mode;
163
- delta->old_file.flags |= GIT_DIFF_FLAG_VALID_OID;
162
+ delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID;
164
163
 
165
- git_oid_cpy(&delta->new_file.oid, &new_entry->oid);
164
+ git_oid_cpy(&delta->new_file.id, &new_entry->id);
166
165
  delta->new_file.size = new_entry->file_size;
167
166
  delta->new_file.mode = new_mode;
168
167
 
169
168
  if (new_oid) {
170
169
  if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE))
171
- git_oid_cpy(&delta->old_file.oid, new_oid);
170
+ git_oid_cpy(&delta->old_file.id, new_oid);
172
171
  else
173
- git_oid_cpy(&delta->new_file.oid, new_oid);
172
+ git_oid_cpy(&delta->new_file.id, new_oid);
174
173
  }
175
174
 
176
- if (new_oid || !git_oid_iszero(&new_entry->oid))
177
- delta->new_file.flags |= GIT_DIFF_FLAG_VALID_OID;
178
-
179
- notify_res = diff_notify(diff, delta, matched_pathspec);
180
-
181
- if (notify_res)
182
- git__free(delta);
183
- else if (git_vector_insert(&diff->deltas, delta) < 0) {
184
- git__free(delta);
185
- return -1;
186
- }
175
+ if (new_oid || !git_oid_iszero(&new_entry->id))
176
+ delta->new_file.flags |= GIT_DIFF_FLAG_VALID_ID;
187
177
 
188
- return notify_res < 0 ? GIT_EUSER : 0;
178
+ return diff_insert_delta(diff, delta, matched_pathspec);
189
179
  }
190
180
 
191
181
  static git_diff_delta *diff_delta__last_for_item(
192
- git_diff_list *diff,
182
+ git_diff *diff,
193
183
  const git_index_entry *item)
194
184
  {
195
185
  git_diff_delta *delta = git_vector_last(&diff->deltas);
@@ -199,21 +189,21 @@ static git_diff_delta *diff_delta__last_for_item(
199
189
  switch (delta->status) {
200
190
  case GIT_DELTA_UNMODIFIED:
201
191
  case GIT_DELTA_DELETED:
202
- if (git_oid__cmp(&delta->old_file.oid, &item->oid) == 0)
192
+ if (git_oid__cmp(&delta->old_file.id, &item->id) == 0)
203
193
  return delta;
204
194
  break;
205
195
  case GIT_DELTA_ADDED:
206
- if (git_oid__cmp(&delta->new_file.oid, &item->oid) == 0)
196
+ if (git_oid__cmp(&delta->new_file.id, &item->id) == 0)
207
197
  return delta;
208
198
  break;
209
199
  case GIT_DELTA_UNTRACKED:
210
200
  if (diff->strcomp(delta->new_file.path, item->path) == 0 &&
211
- git_oid__cmp(&delta->new_file.oid, &item->oid) == 0)
201
+ git_oid__cmp(&delta->new_file.id, &item->id) == 0)
212
202
  return delta;
213
203
  break;
214
204
  case GIT_DELTA_MODIFIED:
215
- if (git_oid__cmp(&delta->old_file.oid, &item->oid) == 0 ||
216
- git_oid__cmp(&delta->new_file.oid, &item->oid) == 0)
205
+ if (git_oid__cmp(&delta->old_file.id, &item->id) == 0 ||
206
+ git_oid__cmp(&delta->new_file.id, &item->id) == 0)
217
207
  return delta;
218
208
  break;
219
209
  default:
@@ -247,6 +237,11 @@ GIT_INLINE(const char *) diff_delta__path(const git_diff_delta *delta)
247
237
  return str;
248
238
  }
249
239
 
240
+ const char *git_diff_delta__path(const git_diff_delta *delta)
241
+ {
242
+ return diff_delta__path(delta);
243
+ }
244
+
250
245
  int git_diff_delta__cmp(const void *a, const void *b)
251
246
  {
252
247
  const git_diff_delta *da = a, *db = b;
@@ -261,6 +256,26 @@ int git_diff_delta__casecmp(const void *a, const void *b)
261
256
  return val ? val : ((int)da->status - (int)db->status);
262
257
  }
263
258
 
259
+ GIT_INLINE(const char *) diff_delta__i2w_path(const git_diff_delta *delta)
260
+ {
261
+ return delta->old_file.path ?
262
+ delta->old_file.path : delta->new_file.path;
263
+ }
264
+
265
+ int git_diff_delta__i2w_cmp(const void *a, const void *b)
266
+ {
267
+ const git_diff_delta *da = a, *db = b;
268
+ int val = strcmp(diff_delta__i2w_path(da), diff_delta__i2w_path(db));
269
+ return val ? val : ((int)da->status - (int)db->status);
270
+ }
271
+
272
+ int git_diff_delta__i2w_casecmp(const void *a, const void *b)
273
+ {
274
+ const git_diff_delta *da = a, *db = b;
275
+ int val = strcasecmp(diff_delta__i2w_path(da), diff_delta__i2w_path(db));
276
+ return val ? val : ((int)da->status - (int)db->status);
277
+ }
278
+
264
279
  bool git_diff_delta__should_skip(
265
280
  const git_diff_options *opts, const git_diff_delta *delta)
266
281
  {
@@ -282,26 +297,6 @@ bool git_diff_delta__should_skip(
282
297
  }
283
298
 
284
299
 
285
- static int config_bool(git_config *cfg, const char *name, int defvalue)
286
- {
287
- int val = defvalue;
288
-
289
- if (git_config_get_bool(&val, cfg, name) < 0)
290
- giterr_clear();
291
-
292
- return val;
293
- }
294
-
295
- static int config_int(git_config *cfg, const char *name, int defvalue)
296
- {
297
- int val = defvalue;
298
-
299
- if (git_config_get_int32(&val, cfg, name) < 0)
300
- giterr_clear();
301
-
302
- return val;
303
- }
304
-
305
300
  static const char *diff_mnemonic_prefix(
306
301
  git_iterator_type_t type, bool left_side)
307
302
  {
@@ -323,13 +318,38 @@ static const char *diff_mnemonic_prefix(
323
318
  return pfx;
324
319
  }
325
320
 
326
- static git_diff_list *diff_list_alloc(
321
+ static void diff_set_ignore_case(git_diff *diff, bool ignore_case)
322
+ {
323
+ if (!ignore_case) {
324
+ diff->opts.flags &= ~GIT_DIFF_IGNORE_CASE;
325
+
326
+ diff->strcomp = git__strcmp;
327
+ diff->strncomp = git__strncmp;
328
+ diff->pfxcomp = git__prefixcmp;
329
+ diff->entrycomp = git_index_entry_cmp;
330
+
331
+ git_vector_set_cmp(&diff->deltas, git_diff_delta__cmp);
332
+ } else {
333
+ diff->opts.flags |= GIT_DIFF_IGNORE_CASE;
334
+
335
+ diff->strcomp = git__strcasecmp;
336
+ diff->strncomp = git__strncasecmp;
337
+ diff->pfxcomp = git__prefixcmp_icase;
338
+ diff->entrycomp = git_index_entry_icmp;
339
+
340
+ git_vector_set_cmp(&diff->deltas, git_diff_delta__casecmp);
341
+ }
342
+
343
+ git_vector_sort(&diff->deltas);
344
+ }
345
+
346
+ static git_diff *diff_list_alloc(
327
347
  git_repository *repo,
328
348
  git_iterator *old_iter,
329
349
  git_iterator *new_iter)
330
350
  {
331
351
  git_diff_options dflt = GIT_DIFF_OPTIONS_INIT;
332
- git_diff_list *diff = git__calloc(1, sizeof(git_diff_list));
352
+ git_diff *diff = git__calloc(1, sizeof(git_diff));
333
353
  if (!diff)
334
354
  return NULL;
335
355
 
@@ -343,51 +363,37 @@ static git_diff_list *diff_list_alloc(
343
363
 
344
364
  if (git_vector_init(&diff->deltas, 0, git_diff_delta__cmp) < 0 ||
345
365
  git_pool_init(&diff->pool, 1, 0) < 0) {
346
- git_diff_list_free(diff);
366
+ git_diff_free(diff);
347
367
  return NULL;
348
368
  }
349
369
 
350
370
  /* Use case-insensitive compare if either iterator has
351
371
  * the ignore_case bit set */
352
- if (!git_iterator_ignore_case(old_iter) &&
353
- !git_iterator_ignore_case(new_iter)) {
354
- diff->opts.flags &= ~GIT_DIFF_DELTAS_ARE_ICASE;
355
-
356
- diff->strcomp = git__strcmp;
357
- diff->strncomp = git__strncmp;
358
- diff->pfxcomp = git__prefixcmp;
359
- diff->entrycomp = git_index_entry__cmp;
360
- } else {
361
- diff->opts.flags |= GIT_DIFF_DELTAS_ARE_ICASE;
362
-
363
- diff->strcomp = git__strcasecmp;
364
- diff->strncomp = git__strncasecmp;
365
- diff->pfxcomp = git__prefixcmp_icase;
366
- diff->entrycomp = git_index_entry__cmp_icase;
367
-
368
- git_vector_set_cmp(&diff->deltas, git_diff_delta__casecmp);
369
- }
372
+ diff_set_ignore_case(
373
+ diff,
374
+ git_iterator_ignore_case(old_iter) ||
375
+ git_iterator_ignore_case(new_iter));
370
376
 
371
377
  return diff;
372
378
  }
373
379
 
374
380
  static int diff_list_apply_options(
375
- git_diff_list *diff,
381
+ git_diff *diff,
376
382
  const git_diff_options *opts)
377
383
  {
378
- git_config *cfg;
384
+ git_config *cfg = NULL;
379
385
  git_repository *repo = diff->repo;
380
386
  git_pool *pool = &diff->pool;
381
387
  int val;
382
388
 
383
389
  if (opts) {
384
390
  /* copy user options (except case sensitivity info from iterators) */
385
- bool icase = DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE);
391
+ bool icase = DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE);
386
392
  memcpy(&diff->opts, opts, sizeof(diff->opts));
387
- DIFF_FLAG_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE, icase);
393
+ DIFF_FLAG_SET(diff, GIT_DIFF_IGNORE_CASE, icase);
388
394
 
389
395
  /* initialize pathspec from options */
390
- if (git_pathspec_init(&diff->pathspec, &opts->pathspec, pool) < 0)
396
+ if (git_pathspec__vinit(&diff->pathspec, &opts->pathspec, pool) < 0)
391
397
  return -1;
392
398
  }
393
399
 
@@ -396,24 +402,24 @@ static int diff_list_apply_options(
396
402
  diff->opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE;
397
403
 
398
404
  /* flag INCLUDE_UNTRACKED_CONTENT implies INCLUDE_UNTRACKED */
399
- if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_UNTRACKED_CONTENT))
405
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_SHOW_UNTRACKED_CONTENT))
400
406
  diff->opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
401
407
 
402
408
  /* load config values that affect diff behavior */
403
- if (git_repository_config__weakptr(&cfg, repo) < 0)
404
- return -1;
409
+ if ((val = git_repository_config_snapshot(&cfg, repo)) < 0)
410
+ return val;
405
411
 
406
- if (!git_repository__cvar(&val, repo, GIT_CVAR_SYMLINKS) && val)
412
+ if (!git_config__cvar(&val, cfg, GIT_CVAR_SYMLINKS) && val)
407
413
  diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_HAS_SYMLINKS;
408
414
 
409
- if (!git_repository__cvar(&val, repo, GIT_CVAR_IGNORESTAT) && val)
410
- diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_ASSUME_UNCHANGED;
415
+ if (!git_config__cvar(&val, cfg, GIT_CVAR_IGNORESTAT) && val)
416
+ diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_IGNORE_STAT;
411
417
 
412
418
  if ((diff->opts.flags & GIT_DIFF_IGNORE_FILEMODE) == 0 &&
413
- !git_repository__cvar(&val, repo, GIT_CVAR_FILEMODE) && val)
419
+ !git_config__cvar(&val, cfg, GIT_CVAR_FILEMODE) && val)
414
420
  diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_MODE_BITS;
415
421
 
416
- if (!git_repository__cvar(&val, repo, GIT_CVAR_TRUSTCTIME) && val)
422
+ if (!git_config__cvar(&val, cfg, GIT_CVAR_TRUSTCTIME) && val)
417
423
  diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_CTIME;
418
424
 
419
425
  /* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */
@@ -423,10 +429,35 @@ static int diff_list_apply_options(
423
429
 
424
430
  /* If not given explicit `opts`, check `diff.xyz` configs */
425
431
  if (!opts) {
426
- diff->opts.context_lines = config_int(cfg, "diff.context", 3);
432
+ int context = git_config__get_int_force(cfg, "diff.context", 3);
433
+ diff->opts.context_lines = context >= 0 ? (uint16_t)context : 3;
427
434
 
428
- if (config_bool(cfg, "diff.ignoreSubmodules", 0))
429
- diff->opts.flags |= GIT_DIFF_IGNORE_SUBMODULES;
435
+ /* add other defaults here */
436
+ }
437
+
438
+ /* Reverse src info if diff is reversed */
439
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
440
+ git_iterator_type_t tmp_src = diff->old_src;
441
+ diff->old_src = diff->new_src;
442
+ diff->new_src = tmp_src;
443
+ }
444
+
445
+ /* Unset UPDATE_INDEX unless diffing workdir and index */
446
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_UPDATE_INDEX) &&
447
+ (!(diff->old_src == GIT_ITERATOR_TYPE_WORKDIR ||
448
+ diff->new_src == GIT_ITERATOR_TYPE_WORKDIR) ||
449
+ !(diff->old_src == GIT_ITERATOR_TYPE_INDEX ||
450
+ diff->new_src == GIT_ITERATOR_TYPE_INDEX)))
451
+ diff->opts.flags &= ~GIT_DIFF_UPDATE_INDEX;
452
+
453
+ /* if ignore_submodules not explicitly set, check diff config */
454
+ if (diff->opts.ignore_submodules <= 0) {
455
+ const git_config_entry *entry;
456
+ git_config__lookup_entry(&entry, cfg, "diff.ignoresubmodules", true);
457
+
458
+ if (entry && git_submodule_parse_ignore(
459
+ &diff->opts.ignore_submodules, entry->value) < 0)
460
+ giterr_clear();
430
461
  }
431
462
 
432
463
  /* if either prefix is not set, figure out appropriate value */
@@ -434,9 +465,9 @@ static int diff_list_apply_options(
434
465
  const char *use_old = DIFF_OLD_PREFIX_DEFAULT;
435
466
  const char *use_new = DIFF_NEW_PREFIX_DEFAULT;
436
467
 
437
- if (config_bool(cfg, "diff.noprefix", 0)) {
468
+ if (git_config__get_bool_force(cfg, "diff.noprefix", 0))
438
469
  use_old = use_new = "";
439
- } else if (config_bool(cfg, "diff.mnemonicprefix", 0)) {
470
+ else if (git_config__get_bool_force(cfg, "diff.mnemonicprefix", 0)) {
440
471
  use_old = diff_mnemonic_prefix(diff->old_src, true);
441
472
  use_new = diff_mnemonic_prefix(diff->new_src, false);
442
473
  }
@@ -450,37 +481,31 @@ static int diff_list_apply_options(
450
481
  /* strdup prefix from pool so we're not dependent on external data */
451
482
  diff->opts.old_prefix = diff_strdup_prefix(pool, diff->opts.old_prefix);
452
483
  diff->opts.new_prefix = diff_strdup_prefix(pool, diff->opts.new_prefix);
453
- if (!diff->opts.old_prefix || !diff->opts.new_prefix)
454
- return -1;
455
484
 
456
485
  if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
457
- const char *swap = diff->opts.old_prefix;
458
- diff->opts.old_prefix = diff->opts.new_prefix;
459
- diff->opts.new_prefix = swap;
486
+ const char *tmp_prefix = diff->opts.old_prefix;
487
+ diff->opts.old_prefix = diff->opts.new_prefix;
488
+ diff->opts.new_prefix = tmp_prefix;
460
489
  }
461
490
 
462
- return 0;
491
+ git_config_free(cfg);
492
+
493
+ /* check strdup results for error */
494
+ return (!diff->opts.old_prefix || !diff->opts.new_prefix) ? -1 : 0;
463
495
  }
464
496
 
465
- static void diff_list_free(git_diff_list *diff)
497
+ static void diff_list_free(git_diff *diff)
466
498
  {
467
- git_diff_delta *delta;
468
- unsigned int i;
469
-
470
- git_vector_foreach(&diff->deltas, i, delta) {
471
- git__free(delta);
472
- diff->deltas.contents[i] = NULL;
473
- }
474
- git_vector_free(&diff->deltas);
499
+ git_vector_free_deep(&diff->deltas);
475
500
 
476
- git_pathspec_free(&diff->pathspec);
501
+ git_pathspec__vfree(&diff->pathspec);
477
502
  git_pool_clear(&diff->pool);
478
503
 
479
504
  git__memzero(diff, sizeof(*diff));
480
505
  git__free(diff);
481
506
  }
482
507
 
483
- void git_diff_list_free(git_diff_list *diff)
508
+ void git_diff_free(git_diff *diff)
484
509
  {
485
510
  if (!diff)
486
511
  return;
@@ -488,79 +513,112 @@ void git_diff_list_free(git_diff_list *diff)
488
513
  GIT_REFCOUNT_DEC(diff, diff_list_free);
489
514
  }
490
515
 
491
- void git_diff_list_addref(git_diff_list *diff)
516
+ void git_diff_addref(git_diff *diff)
492
517
  {
493
518
  GIT_REFCOUNT_INC(diff);
494
519
  }
495
520
 
496
521
  int git_diff__oid_for_file(
497
- git_repository *repo,
522
+ git_oid *out,
523
+ git_diff *diff,
498
524
  const char *path,
499
525
  uint16_t mode,
500
- git_off_t size,
501
- git_oid *oid)
526
+ git_off_t size)
527
+ {
528
+ git_index_entry entry;
529
+
530
+ memset(&entry, 0, sizeof(entry));
531
+ entry.mode = mode;
532
+ entry.file_size = size;
533
+ entry.path = (char *)path;
534
+
535
+ return git_diff__oid_for_entry(out, diff, &entry, NULL);
536
+ }
537
+
538
+ int git_diff__oid_for_entry(
539
+ git_oid *out,
540
+ git_diff *diff,
541
+ const git_index_entry *src,
542
+ const git_oid *update_match)
502
543
  {
503
- int result = 0;
544
+ int error = 0;
504
545
  git_buf full_path = GIT_BUF_INIT;
546
+ git_index_entry entry = *src;
547
+ git_filter_list *fl = NULL;
548
+
549
+ memset(out, 0, sizeof(*out));
505
550
 
506
551
  if (git_buf_joinpath(
507
- &full_path, git_repository_workdir(repo), path) < 0)
552
+ &full_path, git_repository_workdir(diff->repo), entry.path) < 0)
508
553
  return -1;
509
554
 
510
- if (!mode) {
555
+ if (!entry.mode) {
511
556
  struct stat st;
512
557
 
513
- if (p_stat(path, &st) < 0) {
514
- giterr_set(GITERR_OS, "Could not stat '%s'", path);
515
- result = -1;
516
- goto cleanup;
558
+ diff->perf.stat_calls++;
559
+
560
+ if (p_stat(full_path.ptr, &st) < 0) {
561
+ error = git_path_set_error(errno, entry.path, "stat");
562
+ git_buf_free(&full_path);
563
+ return error;
517
564
  }
518
565
 
519
- mode = st.st_mode;
520
- size = st.st_size;
566
+ git_index_entry__init_from_stat(
567
+ &entry, &st, (diff->diffcaps & GIT_DIFFCAPS_TRUST_MODE_BITS) != 0);
521
568
  }
522
569
 
523
570
  /* calculate OID for file if possible */
524
- if (S_ISGITLINK(mode)) {
571
+ if (S_ISGITLINK(entry.mode)) {
525
572
  git_submodule *sm;
526
- const git_oid *sm_oid;
527
573
 
528
- if (!git_submodule_lookup(&sm, repo, path) &&
529
- (sm_oid = git_submodule_wd_id(sm)) != NULL)
530
- git_oid_cpy(oid, sm_oid);
531
- else {
574
+ if (!git_submodule_lookup(&sm, diff->repo, entry.path)) {
575
+ const git_oid *sm_oid = git_submodule_wd_id(sm);
576
+ if (sm_oid)
577
+ git_oid_cpy(out, sm_oid);
578
+ git_submodule_free(sm);
579
+ } else {
532
580
  /* if submodule lookup failed probably just in an intermediate
533
581
  * state where some init hasn't happened, so ignore the error
534
582
  */
535
583
  giterr_clear();
536
- memset(oid, 0, sizeof(*oid));
537
584
  }
538
- } else if (S_ISLNK(mode)) {
539
- result = git_odb__hashlink(oid, full_path.ptr);
540
- } else if (!git__is_sizet(size)) {
541
- giterr_set(GITERR_OS, "File size overflow (for 32-bits) on '%s'", path);
542
- result = -1;
543
- } else {
544
- git_vector filters = GIT_VECTOR_INIT;
545
-
546
- result = git_filters_load(&filters, repo, path, GIT_FILTER_TO_ODB);
547
- if (result >= 0) {
548
- int fd = git_futils_open_ro(full_path.ptr);
549
- if (fd < 0)
550
- result = fd;
551
- else {
552
- result = git_odb__hashfd_filtered(
553
- oid, fd, (size_t)size, GIT_OBJ_BLOB, &filters);
554
- p_close(fd);
555
- }
585
+ } else if (S_ISLNK(entry.mode)) {
586
+ error = git_odb__hashlink(out, full_path.ptr);
587
+ diff->perf.oid_calculations++;
588
+ } else if (!git__is_sizet(entry.file_size)) {
589
+ giterr_set(GITERR_OS, "File size overflow (for 32-bits) on '%s'",
590
+ entry.path);
591
+ error = -1;
592
+ } else if (!(error = git_filter_list_load(
593
+ &fl, diff->repo, NULL, entry.path,
594
+ GIT_FILTER_TO_ODB, GIT_FILTER_OPT_ALLOW_UNSAFE)))
595
+ {
596
+ int fd = git_futils_open_ro(full_path.ptr);
597
+ if (fd < 0)
598
+ error = fd;
599
+ else {
600
+ error = git_odb__hashfd_filtered(
601
+ out, fd, (size_t)entry.file_size, GIT_OBJ_BLOB, fl);
602
+ p_close(fd);
603
+ diff->perf.oid_calculations++;
556
604
  }
557
605
 
558
- git_filters_free(&filters);
606
+ git_filter_list_free(fl);
559
607
  }
560
608
 
561
- cleanup:
609
+ /* update index for entry if requested */
610
+ if (!error && update_match && git_oid_equal(out, update_match)) {
611
+ git_index *idx;
612
+
613
+ if (!(error = git_repository_index(&idx, diff->repo))) {
614
+ memcpy(&entry.id, out, sizeof(entry.id));
615
+ error = git_index_add(idx, &entry);
616
+ git_index_free(idx);
617
+ }
618
+ }
619
+
562
620
  git_buf_free(&full_path);
563
- return result;
621
+ return error;
564
622
  }
565
623
 
566
624
  static bool diff_time_eq(
@@ -576,7 +634,6 @@ typedef struct {
576
634
  git_iterator *new_iter;
577
635
  const git_index_entry *oitem;
578
636
  const git_index_entry *nitem;
579
- git_buf ignore_prefix;
580
637
  } diff_in_progress;
581
638
 
582
639
  #define MODE_BITS_MASK 0000777
@@ -584,45 +641,54 @@ typedef struct {
584
641
  static int maybe_modified_submodule(
585
642
  git_delta_t *status,
586
643
  git_oid *found_oid,
587
- git_diff_list *diff,
644
+ git_diff *diff,
588
645
  diff_in_progress *info)
589
646
  {
590
647
  int error = 0;
591
648
  git_submodule *sub;
592
649
  unsigned int sm_status = 0;
593
- const git_oid *sm_oid;
650
+ git_submodule_ignore_t ign = diff->opts.ignore_submodules;
594
651
 
595
652
  *status = GIT_DELTA_UNMODIFIED;
596
653
 
597
- if (!DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES) &&
598
- !(error = git_submodule_lookup(
599
- &sub, diff->repo, info->nitem->path)) &&
600
- git_submodule_ignore(sub) != GIT_SUBMODULE_IGNORE_ALL &&
601
- !(error = git_submodule_status(&sm_status, sub)))
602
- {
603
- /* check IS_WD_UNMODIFIED because this case is only used
604
- * when the new side of the diff is the working directory
605
- */
606
- if (!GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(sm_status))
607
- *status = GIT_DELTA_MODIFIED;
654
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES) ||
655
+ ign == GIT_SUBMODULE_IGNORE_ALL)
656
+ return 0;
608
657
 
609
- /* grab OID while we are here */
610
- if (git_oid_iszero(&info->nitem->oid) &&
611
- (sm_oid = git_submodule_wd_id(sub)) != NULL)
612
- git_oid_cpy(found_oid, sm_oid);
613
- }
658
+ if ((error = git_submodule_lookup(
659
+ &sub, diff->repo, info->nitem->path)) < 0) {
614
660
 
615
- /* GIT_EEXISTS means a dir with .git in it was found - ignore it */
616
- if (error == GIT_EEXISTS) {
617
- giterr_clear();
618
- error = 0;
661
+ /* GIT_EEXISTS means dir with .git in it was found - ignore it */
662
+ if (error == GIT_EEXISTS) {
663
+ giterr_clear();
664
+ error = 0;
665
+ }
666
+ return error;
619
667
  }
620
668
 
669
+ if (ign <= 0 && git_submodule_ignore(sub) == GIT_SUBMODULE_IGNORE_ALL)
670
+ /* ignore it */;
671
+ else if ((error = git_submodule__status(
672
+ &sm_status, NULL, NULL, found_oid, sub, ign)) < 0)
673
+ /* return error below */;
674
+
675
+ /* check IS_WD_UNMODIFIED because this case is only used
676
+ * when the new side of the diff is the working directory
677
+ */
678
+ else if (!GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(sm_status))
679
+ *status = GIT_DELTA_MODIFIED;
680
+
681
+ /* now that we have a HEAD OID, check if HEAD moved */
682
+ else if ((sm_status & GIT_SUBMODULE_STATUS_IN_WD) != 0 &&
683
+ !git_oid_equal(&info->oitem->id, found_oid))
684
+ *status = GIT_DELTA_MODIFIED;
685
+
686
+ git_submodule_free(sub);
621
687
  return error;
622
688
  }
623
689
 
624
690
  static int maybe_modified(
625
- git_diff_list *diff,
691
+ git_diff *diff,
626
692
  diff_in_progress *info)
627
693
  {
628
694
  git_oid noid;
@@ -632,13 +698,15 @@ static int maybe_modified(
632
698
  unsigned int omode = oitem->mode;
633
699
  unsigned int nmode = nitem->mode;
634
700
  bool new_is_workdir = (info->new_iter->type == GIT_ITERATOR_TYPE_WORKDIR);
701
+ bool modified_uncertain = false;
635
702
  const char *matched_pathspec;
703
+ int error = 0;
636
704
 
637
- if (!git_pathspec_match_path(
705
+ if (!git_pathspec__match(
638
706
  &diff->pathspec, oitem->path,
639
707
  DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH),
640
- DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE),
641
- &matched_pathspec))
708
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE),
709
+ &matched_pathspec, NULL))
642
710
  return 0;
643
711
 
644
712
  memset(&noid, 0, sizeof(noid));
@@ -655,9 +723,8 @@ static int maybe_modified(
655
723
  nmode = (nmode & ~MODE_BITS_MASK) | (omode & MODE_BITS_MASK);
656
724
 
657
725
  /* support "assume unchanged" (poorly, b/c we still stat everything) */
658
- if ((diff->diffcaps & GIT_DIFFCAPS_ASSUME_UNCHANGED) != 0)
659
- status = (oitem->flags_extended & GIT_IDXENTRY_INTENT_TO_ADD) ?
660
- GIT_DELTA_MODIFIED : GIT_DELTA_UNMODIFIED;
726
+ if ((oitem->flags & GIT_IDXENTRY_VALID) != 0)
727
+ status = GIT_DELTA_UNMODIFIED;
661
728
 
662
729
  /* support "skip worktree" index bit */
663
730
  else if ((oitem->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE) != 0)
@@ -668,23 +735,22 @@ static int maybe_modified(
668
735
  if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE))
669
736
  status = GIT_DELTA_TYPECHANGE;
670
737
  else {
671
- if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 ||
672
- diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem) < 0)
673
- return -1;
674
- return 0;
738
+ if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem)))
739
+ error = diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem);
740
+ return error;
675
741
  }
676
742
  }
677
743
 
678
744
  /* if oids and modes match (and are valid), then file is unmodified */
679
- else if (git_oid_equal(&oitem->oid, &nitem->oid) &&
745
+ else if (git_oid_equal(&oitem->id, &nitem->id) &&
680
746
  omode == nmode &&
681
- !git_oid_iszero(&oitem->oid))
747
+ !git_oid_iszero(&oitem->id))
682
748
  status = GIT_DELTA_UNMODIFIED;
683
749
 
684
750
  /* if we have an unknown OID and a workdir iterator, then check some
685
751
  * circumstances that can accelerate things or need special handling
686
752
  */
687
- else if (git_oid_iszero(&nitem->oid) && new_is_workdir) {
753
+ else if (git_oid_iszero(&nitem->id) && new_is_workdir) {
688
754
  bool use_ctime = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) != 0);
689
755
  bool use_nanos = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_NANOSECS) != 0);
690
756
 
@@ -693,22 +759,28 @@ static int maybe_modified(
693
759
  /* TODO: add check against index file st_mtime to avoid racy-git */
694
760
 
695
761
  if (S_ISGITLINK(nmode)) {
696
- if (maybe_modified_submodule(&status, &noid, diff, info) < 0)
697
- return -1;
762
+ if ((error = maybe_modified_submodule(&status, &noid, diff, info)) < 0)
763
+ return error;
698
764
  }
699
765
 
700
766
  /* if the stat data looks different, then mark modified - this just
701
767
  * means that the OID will be recalculated below to confirm change
702
768
  */
703
- else if (omode != nmode ||
704
- oitem->file_size != nitem->file_size ||
705
- !diff_time_eq(&oitem->mtime, &nitem->mtime, use_nanos) ||
769
+ else if (omode != nmode || oitem->file_size != nitem->file_size) {
770
+ status = GIT_DELTA_MODIFIED;
771
+ modified_uncertain =
772
+ (oitem->file_size <= 0 && nitem->file_size > 0);
773
+ }
774
+ else if (!diff_time_eq(&oitem->mtime, &nitem->mtime, use_nanos) ||
706
775
  (use_ctime &&
707
776
  !diff_time_eq(&oitem->ctime, &nitem->ctime, use_nanos)) ||
708
777
  oitem->ino != nitem->ino ||
709
778
  oitem->uid != nitem->uid ||
710
779
  oitem->gid != nitem->gid)
780
+ {
711
781
  status = GIT_DELTA_MODIFIED;
782
+ modified_uncertain = true;
783
+ }
712
784
  }
713
785
 
714
786
  /* if mode is GITLINK and submodules are ignored, then skip */
@@ -719,11 +791,15 @@ static int maybe_modified(
719
791
  /* if we got here and decided that the files are modified, but we
720
792
  * haven't calculated the OID of the new item, then calculate it now
721
793
  */
722
- if (status != GIT_DELTA_UNMODIFIED && git_oid_iszero(&nitem->oid)) {
794
+ if (modified_uncertain && git_oid_iszero(&nitem->id)) {
723
795
  if (git_oid_iszero(&noid)) {
724
- if (git_diff__oid_for_file(diff->repo,
725
- nitem->path, nitem->mode, nitem->file_size, &noid) < 0)
726
- return -1;
796
+ const git_oid *update_check =
797
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_UPDATE_INDEX) ?
798
+ &oitem->id : NULL;
799
+
800
+ if ((error = git_diff__oid_for_entry(
801
+ &noid, diff, nitem, update_check)) < 0)
802
+ return error;
727
803
  }
728
804
 
729
805
  /* if oid matches, then mark unmodified (except submodules, where
@@ -731,7 +807,7 @@ static int maybe_modified(
731
807
  * matches between the index and the workdir HEAD)
732
808
  */
733
809
  if (omode == nmode && !S_ISGITLINK(omode) &&
734
- git_oid_equal(&oitem->oid, &noid))
810
+ git_oid_equal(&oitem->id, &noid))
735
811
  status = GIT_DELTA_UNMODIFIED;
736
812
  }
737
813
 
@@ -741,7 +817,7 @@ static int maybe_modified(
741
817
  }
742
818
 
743
819
  static bool entry_is_prefixed(
744
- git_diff_list *diff,
820
+ git_diff *diff,
745
821
  const git_index_entry *item,
746
822
  const git_index_entry *prefix_item)
747
823
  {
@@ -757,74 +833,8 @@ static bool entry_is_prefixed(
757
833
  item->path[pathlen] == '/');
758
834
  }
759
835
 
760
- static int diff_scan_inside_untracked_dir(
761
- git_diff_list *diff, diff_in_progress *info, git_delta_t *delta_type)
762
- {
763
- int error = 0;
764
- git_buf base = GIT_BUF_INIT;
765
- bool is_ignored;
766
-
767
- *delta_type = GIT_DELTA_IGNORED;
768
- git_buf_sets(&base, info->nitem->path);
769
-
770
- /* advance into untracked directory */
771
- if ((error = git_iterator_advance_into(&info->nitem, info->new_iter)) < 0) {
772
-
773
- /* skip ahead if empty */
774
- if (error == GIT_ENOTFOUND) {
775
- giterr_clear();
776
- error = git_iterator_advance(&info->nitem, info->new_iter);
777
- }
778
-
779
- goto done;
780
- }
781
-
782
- /* look for actual untracked file */
783
- while (info->nitem != NULL &&
784
- !diff->pfxcomp(info->nitem->path, git_buf_cstr(&base))) {
785
- is_ignored = git_iterator_current_is_ignored(info->new_iter);
786
-
787
- /* need to recurse into non-ignored directories */
788
- if (!is_ignored && S_ISDIR(info->nitem->mode)) {
789
- error = git_iterator_advance_into(&info->nitem, info->new_iter);
790
-
791
- if (!error)
792
- continue;
793
- else if (error == GIT_ENOTFOUND) {
794
- error = 0;
795
- is_ignored = true; /* treat empty as ignored */
796
- } else
797
- break; /* real error, must stop */
798
- }
799
-
800
- /* found a non-ignored item - treat parent dir as untracked */
801
- if (!is_ignored) {
802
- *delta_type = GIT_DELTA_UNTRACKED;
803
- break;
804
- }
805
-
806
- if ((error = git_iterator_advance(&info->nitem, info->new_iter)) < 0)
807
- break;
808
- }
809
-
810
- /* finish off scan */
811
- while (info->nitem != NULL &&
812
- !diff->pfxcomp(info->nitem->path, git_buf_cstr(&base))) {
813
- if ((error = git_iterator_advance(&info->nitem, info->new_iter)) < 0)
814
- break;
815
- }
816
-
817
- done:
818
- git_buf_free(&base);
819
-
820
- if (error == GIT_ITEROVER)
821
- error = 0;
822
-
823
- return error;
824
- }
825
-
826
836
  static int handle_unmatched_new_item(
827
- git_diff_list *diff, diff_in_progress *info)
837
+ git_diff *diff, diff_in_progress *info)
828
838
  {
829
839
  int error = 0;
830
840
  const git_index_entry *nitem = info->nitem;
@@ -834,24 +844,13 @@ static int handle_unmatched_new_item(
834
844
  /* check if this is a prefix of the other side */
835
845
  contains_oitem = entry_is_prefixed(diff, info->oitem, nitem);
836
846
 
837
- /* check if this is contained in an ignored parent directory */
838
- if (git_buf_len(&info->ignore_prefix)) {
839
- if (diff->pfxcomp(nitem->path, git_buf_cstr(&info->ignore_prefix)) == 0)
840
- delta_type = GIT_DELTA_IGNORED;
841
- else
842
- git_buf_clear(&info->ignore_prefix);
843
- }
847
+ /* update delta_type if this item is ignored */
848
+ if (git_iterator_current_is_ignored(info->new_iter))
849
+ delta_type = GIT_DELTA_IGNORED;
844
850
 
845
- if (S_ISDIR(nitem->mode)) {
851
+ if (nitem->mode == GIT_FILEMODE_TREE) {
846
852
  bool recurse_into_dir = contains_oitem;
847
853
 
848
- /* if not already inside an ignored dir, check if this is ignored */
849
- if (delta_type != GIT_DELTA_IGNORED &&
850
- git_iterator_current_is_ignored(info->new_iter)) {
851
- delta_type = GIT_DELTA_IGNORED;
852
- git_buf_sets(&info->ignore_prefix, nitem->path);
853
- }
854
-
855
854
  /* check if user requests recursion into this type of dir */
856
855
  recurse_into_dir = contains_oitem ||
857
856
  (delta_type == GIT_DELTA_UNTRACKED &&
@@ -860,12 +859,14 @@ static int handle_unmatched_new_item(
860
859
  DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS));
861
860
 
862
861
  /* do not advance into directories that contain a .git file */
863
- if (recurse_into_dir) {
862
+ if (recurse_into_dir && !contains_oitem) {
864
863
  git_buf *full = NULL;
865
864
  if (git_iterator_current_workdir_path(&full, info->new_iter) < 0)
866
865
  return -1;
867
- if (full && git_path_contains_dir(full, DOT_GIT))
866
+ if (full && git_path_contains(full, DOT_GIT)) {
867
+ /* TODO: warning if not a valid git repository */
868
868
  recurse_into_dir = false;
869
+ }
869
870
  }
870
871
 
871
872
  /* still have to look into untracked directories to match core git -
@@ -873,12 +874,13 @@ static int handle_unmatched_new_item(
873
874
  */
874
875
  if (!recurse_into_dir &&
875
876
  delta_type == GIT_DELTA_UNTRACKED &&
876
- DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_FAST_UNTRACKED_DIRS))
877
+ DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS))
877
878
  {
878
879
  git_diff_delta *last;
880
+ git_iterator_status_t untracked_state;
879
881
 
880
882
  /* attempt to insert record for this directory */
881
- if ((error = diff_delta__from_one(diff, delta_type, nitem)) < 0)
883
+ if ((error = diff_delta__from_one(diff, delta_type, nitem)) != 0)
882
884
  return error;
883
885
 
884
886
  /* if delta wasn't created (because of rules), just skip ahead */
@@ -887,11 +889,14 @@ static int handle_unmatched_new_item(
887
889
  return git_iterator_advance(&info->nitem, info->new_iter);
888
890
 
889
891
  /* iterate into dir looking for an actual untracked file */
890
- if (diff_scan_inside_untracked_dir(diff, info, &delta_type) < 0)
891
- return -1;
892
+ if ((error = git_iterator_advance_over_with_status(
893
+ &info->nitem, &untracked_state, info->new_iter)) < 0 &&
894
+ error != GIT_ITEROVER)
895
+ return error;
892
896
 
893
- /* it iteration changed delta type, the update the record */
894
- if (delta_type == GIT_DELTA_IGNORED) {
897
+ /* if we found nothing or just ignored items, update the record */
898
+ if (untracked_state == GIT_ITERATOR_STATUS_IGNORED ||
899
+ untracked_state == GIT_ITERATOR_STATUS_EMPTY) {
895
900
  last->status = GIT_DELTA_IGNORED;
896
901
 
897
902
  /* remove the record if we don't want ignored records */
@@ -922,32 +927,35 @@ static int handle_unmatched_new_item(
922
927
  }
923
928
  }
924
929
 
925
- /* In core git, the next two checks are effectively reversed --
926
- * i.e. when an file contained in an ignored directory is explicitly
927
- * ignored, it shows up as an ignored file in the diff list, even though
928
- * other untracked files in the same directory are skipped completely.
929
- *
930
- * To me, this seems odd. If the directory is ignored and the file is
931
- * untracked, we should skip it consistently, regardless of whether it
932
- * happens to match a pattern in the ignore file.
933
- *
934
- * To match the core git behavior, reverse the following two if checks
935
- * so that individual file ignores are checked before container
936
- * directory exclusions are used to skip the file.
937
- */
938
930
  else if (delta_type == GIT_DELTA_IGNORED &&
939
- DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS))
931
+ DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS) &&
932
+ git_iterator_current_tree_is_ignored(info->new_iter))
940
933
  /* item contained in ignored directory, so skip over it */
941
934
  return git_iterator_advance(&info->nitem, info->new_iter);
942
935
 
943
- else if (git_iterator_current_is_ignored(info->new_iter))
944
- delta_type = GIT_DELTA_IGNORED;
945
-
946
936
  else if (info->new_iter->type != GIT_ITERATOR_TYPE_WORKDIR)
947
937
  delta_type = GIT_DELTA_ADDED;
948
938
 
939
+ else if (nitem->mode == GIT_FILEMODE_COMMIT) {
940
+ /* ignore things that are not actual submodules */
941
+ if (git_submodule_lookup(NULL, info->repo, nitem->path) != 0) {
942
+ giterr_clear();
943
+ delta_type = GIT_DELTA_IGNORED;
944
+
945
+ /* if this contains a tracked item, treat as normal TREE */
946
+ if (contains_oitem) {
947
+ error = git_iterator_advance_into(&info->nitem, info->new_iter);
948
+ if (error != GIT_ENOTFOUND)
949
+ return error;
950
+
951
+ giterr_clear();
952
+ return git_iterator_advance(&info->nitem, info->new_iter);
953
+ }
954
+ }
955
+ }
956
+
949
957
  /* Actually create the record for this item if necessary */
950
- if ((error = diff_delta__from_one(diff, delta_type, nitem)) < 0)
958
+ if ((error = diff_delta__from_one(diff, delta_type, nitem)) != 0)
951
959
  return error;
952
960
 
953
961
  /* If user requested TYPECHANGE records, then check for that instead of
@@ -969,10 +977,10 @@ static int handle_unmatched_new_item(
969
977
  }
970
978
 
971
979
  static int handle_unmatched_old_item(
972
- git_diff_list *diff, diff_in_progress *info)
980
+ git_diff *diff, diff_in_progress *info)
973
981
  {
974
982
  int error = diff_delta__from_one(diff, GIT_DELTA_DELETED, info->oitem);
975
- if (error < 0)
983
+ if (error != 0)
976
984
  return error;
977
985
 
978
986
  /* if we are generating TYPECHANGE records then check for that
@@ -1001,7 +1009,7 @@ static int handle_unmatched_old_item(
1001
1009
  }
1002
1010
 
1003
1011
  static int handle_matched_item(
1004
- git_diff_list *diff, diff_in_progress *info)
1012
+ git_diff *diff, diff_in_progress *info)
1005
1013
  {
1006
1014
  int error = 0;
1007
1015
 
@@ -1016,7 +1024,7 @@ static int handle_matched_item(
1016
1024
  }
1017
1025
 
1018
1026
  int git_diff__from_iterators(
1019
- git_diff_list **diff_ptr,
1027
+ git_diff **diff_ptr,
1020
1028
  git_repository *repo,
1021
1029
  git_iterator *old_iter,
1022
1030
  git_iterator *new_iter,
@@ -1024,7 +1032,7 @@ int git_diff__from_iterators(
1024
1032
  {
1025
1033
  int error = 0;
1026
1034
  diff_in_progress info;
1027
- git_diff_list *diff;
1035
+ git_diff *diff;
1028
1036
 
1029
1037
  *diff_ptr = NULL;
1030
1038
 
@@ -1034,10 +1042,9 @@ int git_diff__from_iterators(
1034
1042
  info.repo = repo;
1035
1043
  info.old_iter = old_iter;
1036
1044
  info.new_iter = new_iter;
1037
- git_buf_init(&info.ignore_prefix, 0);
1038
1045
 
1039
1046
  /* make iterators have matching icase behavior */
1040
- if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE)) {
1047
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE)) {
1041
1048
  if ((error = git_iterator_set_ignore_case(old_iter, true)) < 0 ||
1042
1049
  (error = git_iterator_set_ignore_case(new_iter, true)) < 0)
1043
1050
  goto cleanup;
@@ -1081,13 +1088,13 @@ int git_diff__from_iterators(
1081
1088
  error = 0;
1082
1089
  }
1083
1090
 
1091
+ diff->perf.stat_calls += old_iter->stat_calls + new_iter->stat_calls;
1092
+
1084
1093
  cleanup:
1085
1094
  if (!error)
1086
1095
  *diff_ptr = diff;
1087
1096
  else
1088
- git_diff_list_free(diff);
1089
-
1090
- git_buf_free(&info.ignore_prefix);
1097
+ git_diff_free(diff);
1091
1098
 
1092
1099
  return error;
1093
1100
  }
@@ -1102,7 +1109,7 @@ cleanup:
1102
1109
  } while (0)
1103
1110
 
1104
1111
  int git_diff_tree_to_tree(
1105
- git_diff_list **diff,
1112
+ git_diff **diff,
1106
1113
  git_repository *repo,
1107
1114
  git_tree *old_tree,
1108
1115
  git_tree *new_tree,
@@ -1117,7 +1124,7 @@ int git_diff_tree_to_tree(
1117
1124
  * currently case insensitive, unless the user explicitly asked
1118
1125
  * for case insensitivity
1119
1126
  */
1120
- if (opts && (opts->flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0)
1127
+ if (opts && (opts->flags & GIT_DIFF_IGNORE_CASE) != 0)
1121
1128
  iflag = GIT_ITERATOR_IGNORE_CASE;
1122
1129
 
1123
1130
  DIFF_FROM_ITERATORS(
@@ -1128,53 +1135,50 @@ int git_diff_tree_to_tree(
1128
1135
  return error;
1129
1136
  }
1130
1137
 
1138
+ static int diff_load_index(git_index **index, git_repository *repo)
1139
+ {
1140
+ int error = git_repository_index__weakptr(index, repo);
1141
+
1142
+ /* reload the repository index when user did not pass one in */
1143
+ if (!error && git_index_read(*index, false) < 0)
1144
+ giterr_clear();
1145
+
1146
+ return error;
1147
+ }
1148
+
1131
1149
  int git_diff_tree_to_index(
1132
- git_diff_list **diff,
1150
+ git_diff **diff,
1133
1151
  git_repository *repo,
1134
1152
  git_tree *old_tree,
1135
1153
  git_index *index,
1136
1154
  const git_diff_options *opts)
1137
1155
  {
1138
1156
  int error = 0;
1139
- bool reset_index_ignore_case = false;
1157
+ bool index_ignore_case = false;
1140
1158
 
1141
1159
  assert(diff && repo);
1142
1160
 
1143
- if (!index && (error = git_repository_index__weakptr(&index, repo)) < 0)
1161
+ if (!index && (error = diff_load_index(&index, repo)) < 0)
1144
1162
  return error;
1145
1163
 
1146
- if (index->ignore_case) {
1147
- git_index__set_ignore_case(index, false);
1148
- reset_index_ignore_case = true;
1149
- }
1164
+ index_ignore_case = index->ignore_case;
1150
1165
 
1151
1166
  DIFF_FROM_ITERATORS(
1152
- git_iterator_for_tree(&a, old_tree, 0, pfx, pfx),
1153
- git_iterator_for_index(&b, index, 0, pfx, pfx)
1167
+ git_iterator_for_tree(
1168
+ &a, old_tree, GIT_ITERATOR_DONT_IGNORE_CASE, pfx, pfx),
1169
+ git_iterator_for_index(
1170
+ &b, index, GIT_ITERATOR_DONT_IGNORE_CASE, pfx, pfx)
1154
1171
  );
1155
1172
 
1156
- if (reset_index_ignore_case) {
1157
- git_index__set_ignore_case(index, true);
1158
-
1159
- if (!error) {
1160
- git_diff_list *d = *diff;
1161
-
1162
- d->opts.flags |= GIT_DIFF_DELTAS_ARE_ICASE;
1163
- d->strcomp = git__strcasecmp;
1164
- d->strncomp = git__strncasecmp;
1165
- d->pfxcomp = git__prefixcmp_icase;
1166
- d->entrycomp = git_index_entry__cmp_icase;
1167
-
1168
- git_vector_set_cmp(&d->deltas, git_diff_delta__casecmp);
1169
- git_vector_sort(&d->deltas);
1170
- }
1171
- }
1173
+ /* if index is in case-insensitive order, re-sort deltas to match */
1174
+ if (!error && index_ignore_case)
1175
+ diff_set_ignore_case(*diff, true);
1172
1176
 
1173
1177
  return error;
1174
1178
  }
1175
1179
 
1176
1180
  int git_diff_index_to_workdir(
1177
- git_diff_list **diff,
1181
+ git_diff **diff,
1178
1182
  git_repository *repo,
1179
1183
  git_index *index,
1180
1184
  const git_diff_options *opts)
@@ -1183,7 +1187,7 @@ int git_diff_index_to_workdir(
1183
1187
 
1184
1188
  assert(diff && repo);
1185
1189
 
1186
- if (!index && (error = git_repository_index__weakptr(&index, repo)) < 0)
1190
+ if (!index && (error = diff_load_index(&index, repo)) < 0)
1187
1191
  return error;
1188
1192
 
1189
1193
  DIFF_FROM_ITERATORS(
@@ -1192,12 +1196,14 @@ int git_diff_index_to_workdir(
1192
1196
  &b, repo, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx)
1193
1197
  );
1194
1198
 
1199
+ if (!error && DIFF_FLAG_IS_SET(*diff, GIT_DIFF_UPDATE_INDEX))
1200
+ error = git_index_write(index);
1201
+
1195
1202
  return error;
1196
1203
  }
1197
1204
 
1198
-
1199
1205
  int git_diff_tree_to_workdir(
1200
- git_diff_list **diff,
1206
+ git_diff **diff,
1201
1207
  git_repository *repo,
1202
1208
  git_tree *old_tree,
1203
1209
  const git_diff_options *opts)
@@ -1215,16 +1221,46 @@ int git_diff_tree_to_workdir(
1215
1221
  return error;
1216
1222
  }
1217
1223
 
1218
- size_t git_diff_num_deltas(git_diff_list *diff)
1224
+ int git_diff_tree_to_workdir_with_index(
1225
+ git_diff **diff,
1226
+ git_repository *repo,
1227
+ git_tree *old_tree,
1228
+ const git_diff_options *opts)
1229
+ {
1230
+ int error = 0;
1231
+ git_diff *d1 = NULL, *d2 = NULL;
1232
+ git_index *index = NULL;
1233
+
1234
+ assert(diff && repo);
1235
+
1236
+ if ((error = diff_load_index(&index, repo)) < 0)
1237
+ return error;
1238
+
1239
+ if (!(error = git_diff_tree_to_index(&d1, repo, old_tree, index, opts)) &&
1240
+ !(error = git_diff_index_to_workdir(&d2, repo, index, opts)))
1241
+ error = git_diff_merge(d1, d2);
1242
+
1243
+ git_diff_free(d2);
1244
+
1245
+ if (error) {
1246
+ git_diff_free(d1);
1247
+ d1 = NULL;
1248
+ }
1249
+
1250
+ *diff = d1;
1251
+ return error;
1252
+ }
1253
+
1254
+ size_t git_diff_num_deltas(const git_diff *diff)
1219
1255
  {
1220
1256
  assert(diff);
1221
- return (size_t)diff->deltas.length;
1257
+ return diff->deltas.length;
1222
1258
  }
1223
1259
 
1224
- size_t git_diff_num_deltas_of_type(git_diff_list *diff, git_delta_t type)
1260
+ size_t git_diff_num_deltas_of_type(const git_diff *diff, git_delta_t type)
1225
1261
  {
1226
1262
  size_t i, count = 0;
1227
- git_diff_delta *delta;
1263
+ const git_diff_delta *delta;
1228
1264
 
1229
1265
  assert(diff);
1230
1266
 
@@ -1235,20 +1271,42 @@ size_t git_diff_num_deltas_of_type(git_diff_list *diff, git_delta_t type)
1235
1271
  return count;
1236
1272
  }
1237
1273
 
1274
+ const git_diff_delta *git_diff_get_delta(const git_diff *diff, size_t idx)
1275
+ {
1276
+ assert(diff);
1277
+ return git_vector_get(&diff->deltas, idx);
1278
+ }
1279
+
1280
+ int git_diff_is_sorted_icase(const git_diff *diff)
1281
+ {
1282
+ return (diff->opts.flags & GIT_DIFF_IGNORE_CASE) != 0;
1283
+ }
1284
+
1285
+ int git_diff_get_perfdata(git_diff_perfdata *out, const git_diff *diff)
1286
+ {
1287
+ assert(out);
1288
+ GITERR_CHECK_VERSION(out, GIT_DIFF_PERFDATA_VERSION, "git_diff_perfdata");
1289
+ out->stat_calls = diff->perf.stat_calls;
1290
+ out->oid_calculations = diff->perf.oid_calculations;
1291
+ return 0;
1292
+ }
1293
+
1238
1294
  int git_diff__paired_foreach(
1239
- git_diff_list *head2idx,
1240
- git_diff_list *idx2wd,
1295
+ git_diff *head2idx,
1296
+ git_diff *idx2wd,
1241
1297
  int (*cb)(git_diff_delta *h2i, git_diff_delta *i2w, void *payload),
1242
1298
  void *payload)
1243
1299
  {
1244
- int cmp;
1300
+ int cmp, error = 0;
1245
1301
  git_diff_delta *h2i, *i2w;
1246
1302
  size_t i, j, i_max, j_max;
1247
1303
  int (*strcomp)(const char *, const char *) = git__strcmp;
1248
- bool icase_mismatch;
1304
+ bool h2i_icase, i2w_icase, icase_mismatch;
1249
1305
 
1250
1306
  i_max = head2idx ? head2idx->deltas.length : 0;
1251
1307
  j_max = idx2wd ? idx2wd->deltas.length : 0;
1308
+ if (!i_max && !j_max)
1309
+ return 0;
1252
1310
 
1253
1311
  /* At some point, tree-to-index diffs will probably never ignore case,
1254
1312
  * even if that isn't true now. Index-to-workdir diffs may or may not
@@ -1258,24 +1316,35 @@ int git_diff__paired_foreach(
1258
1316
  * Therefore the main thing we need to do here is make sure the diffs
1259
1317
  * are traversed in a compatible order. To do this, we temporarily
1260
1318
  * resort a mismatched diff to get the order correct.
1319
+ *
1320
+ * In order to traverse renames in the index->workdir, we need to
1321
+ * ensure that we compare the index name on both sides, so we
1322
+ * always sort by the old name in the i2w list.
1261
1323
  */
1324
+ h2i_icase = head2idx != NULL &&
1325
+ (head2idx->opts.flags & GIT_DIFF_IGNORE_CASE) != 0;
1326
+
1327
+ i2w_icase = idx2wd != NULL &&
1328
+ (idx2wd->opts.flags & GIT_DIFF_IGNORE_CASE) != 0;
1329
+
1262
1330
  icase_mismatch =
1263
- (head2idx != NULL && idx2wd != NULL &&
1264
- ((head2idx->opts.flags ^ idx2wd->opts.flags) & GIT_DIFF_DELTAS_ARE_ICASE));
1265
-
1266
- /* force case-sensitive delta sort */
1267
- if (icase_mismatch) {
1268
- if (head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) {
1269
- git_vector_set_cmp(&head2idx->deltas, git_diff_delta__cmp);
1270
- git_vector_sort(&head2idx->deltas);
1271
- } else {
1272
- git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__cmp);
1273
- git_vector_sort(&idx2wd->deltas);
1274
- }
1331
+ (head2idx != NULL && idx2wd != NULL && h2i_icase != i2w_icase);
1332
+
1333
+ if (icase_mismatch && h2i_icase) {
1334
+ git_vector_set_cmp(&head2idx->deltas, git_diff_delta__cmp);
1335
+ git_vector_sort(&head2idx->deltas);
1275
1336
  }
1276
- else if (head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE)
1337
+
1338
+ if (i2w_icase && !icase_mismatch) {
1277
1339
  strcomp = git__strcasecmp;
1278
1340
 
1341
+ git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__i2w_casecmp);
1342
+ git_vector_sort(&idx2wd->deltas);
1343
+ } else if (idx2wd != NULL) {
1344
+ git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__i2w_cmp);
1345
+ git_vector_sort(&idx2wd->deltas);
1346
+ }
1347
+
1279
1348
  for (i = 0, j = 0; i < i_max || j < j_max; ) {
1280
1349
  h2i = head2idx ? GIT_VECTOR_GET(&head2idx->deltas, i) : NULL;
1281
1350
  i2w = idx2wd ? GIT_VECTOR_GET(&idx2wd->deltas, j) : NULL;
@@ -1284,30 +1353,266 @@ int git_diff__paired_foreach(
1284
1353
  strcomp(h2i->new_file.path, i2w->old_file.path);
1285
1354
 
1286
1355
  if (cmp < 0) {
1287
- if (cb(h2i, NULL, payload))
1288
- return GIT_EUSER;
1289
- i++;
1356
+ i++; i2w = NULL;
1290
1357
  } else if (cmp > 0) {
1291
- if (cb(NULL, i2w, payload))
1292
- return GIT_EUSER;
1293
- j++;
1358
+ j++; h2i = NULL;
1294
1359
  } else {
1295
- if (cb(h2i, i2w, payload))
1296
- return GIT_EUSER;
1297
1360
  i++; j++;
1298
1361
  }
1362
+
1363
+ if ((error = cb(h2i, i2w, payload)) != 0) {
1364
+ giterr_set_after_callback(error);
1365
+ break;
1366
+ }
1299
1367
  }
1300
1368
 
1301
1369
  /* restore case-insensitive delta sort */
1302
- if (icase_mismatch) {
1303
- if (head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) {
1304
- git_vector_set_cmp(&head2idx->deltas, git_diff_delta__casecmp);
1305
- git_vector_sort(&head2idx->deltas);
1370
+ if (icase_mismatch && h2i_icase) {
1371
+ git_vector_set_cmp(&head2idx->deltas, git_diff_delta__casecmp);
1372
+ git_vector_sort(&head2idx->deltas);
1373
+ }
1374
+
1375
+ /* restore idx2wd sort by new path */
1376
+ if (idx2wd != NULL) {
1377
+ git_vector_set_cmp(&idx2wd->deltas,
1378
+ i2w_icase ? git_diff_delta__casecmp : git_diff_delta__cmp);
1379
+ git_vector_sort(&idx2wd->deltas);
1380
+ }
1381
+
1382
+ return error;
1383
+ }
1384
+
1385
+ int git_diff__commit(
1386
+ git_diff **diff,
1387
+ git_repository *repo,
1388
+ const git_commit *commit,
1389
+ const git_diff_options *opts)
1390
+ {
1391
+ git_commit *parent = NULL;
1392
+ git_diff *commit_diff = NULL;
1393
+ git_tree *old_tree = NULL, *new_tree = NULL;
1394
+ size_t parents;
1395
+ int error = 0;
1396
+
1397
+ if ((parents = git_commit_parentcount(commit)) > 1) {
1398
+ char commit_oidstr[GIT_OID_HEXSZ + 1];
1399
+
1400
+ error = -1;
1401
+ giterr_set(GITERR_INVALID, "Commit %s is a merge commit",
1402
+ git_oid_tostr(commit_oidstr, GIT_OID_HEXSZ + 1, git_commit_id(commit)));
1403
+ goto on_error;
1404
+ }
1405
+
1406
+ if (parents > 0)
1407
+ if ((error = git_commit_parent(&parent, commit, 0)) < 0 ||
1408
+ (error = git_commit_tree(&old_tree, parent)) < 0)
1409
+ goto on_error;
1410
+
1411
+ if ((error = git_commit_tree(&new_tree, commit)) < 0 ||
1412
+ (error = git_diff_tree_to_tree(&commit_diff, repo, old_tree, new_tree, opts)) < 0)
1413
+ goto on_error;
1414
+
1415
+ *diff = commit_diff;
1416
+
1417
+ on_error:
1418
+ git_tree_free(new_tree);
1419
+ git_tree_free(old_tree);
1420
+ git_commit_free(parent);
1421
+
1422
+ return error;
1423
+ }
1424
+
1425
+ int git_diff_format_email__append_header_tobuf(
1426
+ git_buf *out,
1427
+ const git_oid *id,
1428
+ const git_signature *author,
1429
+ const char *summary,
1430
+ size_t patch_no,
1431
+ size_t total_patches,
1432
+ bool exclude_patchno_marker)
1433
+ {
1434
+ char idstr[GIT_OID_HEXSZ + 1];
1435
+ char date_str[GIT_DATE_RFC2822_SZ];
1436
+ int error = 0;
1437
+
1438
+ git_oid_fmt(idstr, id);
1439
+ idstr[GIT_OID_HEXSZ] = '\0';
1440
+
1441
+ if ((error = git__date_rfc2822_fmt(date_str, sizeof(date_str), &author->when)) < 0)
1442
+ return error;
1443
+
1444
+ error = git_buf_printf(out,
1445
+ "From %s Mon Sep 17 00:00:00 2001\n" \
1446
+ "From: %s <%s>\n" \
1447
+ "Date: %s\n" \
1448
+ "Subject: ",
1449
+ idstr,
1450
+ author->name, author->email,
1451
+ date_str);
1452
+
1453
+ if (error < 0)
1454
+ return error;
1455
+
1456
+ if (!exclude_patchno_marker) {
1457
+ if (total_patches == 1) {
1458
+ error = git_buf_puts(out, "[PATCH] ");
1306
1459
  } else {
1307
- git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__casecmp);
1308
- git_vector_sort(&idx2wd->deltas);
1460
+ error = git_buf_printf(out, "[PATCH %"PRIuZ"/%"PRIuZ"] ", patch_no, total_patches);
1461
+ }
1462
+
1463
+ if (error < 0)
1464
+ return error;
1465
+ }
1466
+
1467
+ error = git_buf_printf(out, "%s\n\n", summary);
1468
+
1469
+ return error;
1470
+ }
1471
+
1472
+ int git_diff_format_email__append_patches_tobuf(
1473
+ git_buf *out,
1474
+ git_diff *diff)
1475
+ {
1476
+ size_t i, deltas;
1477
+ int error = 0;
1478
+
1479
+ deltas = git_diff_num_deltas(diff);
1480
+
1481
+ for (i = 0; i < deltas; ++i) {
1482
+ git_patch *patch = NULL;
1483
+
1484
+ if ((error = git_patch_from_diff(&patch, diff, i)) >= 0)
1485
+ error = git_patch_to_buf(out, patch);
1486
+
1487
+ git_patch_free(patch);
1488
+
1489
+ if (error < 0)
1490
+ break;
1491
+ }
1492
+
1493
+ return error;
1494
+ }
1495
+
1496
+ int git_diff_format_email(
1497
+ git_buf *out,
1498
+ git_diff *diff,
1499
+ const git_diff_format_email_options *opts)
1500
+ {
1501
+ git_diff_stats *stats = NULL;
1502
+ char *summary = NULL, *loc = NULL;
1503
+ bool ignore_marker;
1504
+ unsigned int format_flags = 0;
1505
+ int error;
1506
+
1507
+ assert(out && diff && opts);
1508
+ assert(opts->summary && opts->id && opts->author);
1509
+
1510
+ GITERR_CHECK_VERSION(opts, GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION, "git_format_email_options");
1511
+
1512
+ if ((ignore_marker = opts->flags & GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER) == false) {
1513
+ if (opts->patch_no > opts->total_patches) {
1514
+ giterr_set(GITERR_INVALID, "patch %"PRIuZ" out of range. max %"PRIuZ, opts->patch_no, opts->total_patches);
1515
+ return -1;
1516
+ }
1517
+
1518
+ if (opts->patch_no == 0) {
1519
+ giterr_set(GITERR_INVALID, "invalid patch no %"PRIuZ". should be >0", opts->patch_no);
1520
+ return -1;
1309
1521
  }
1310
1522
  }
1311
1523
 
1524
+ /* the summary we receive may not be clean.
1525
+ * it could potentially contain new line characters
1526
+ * or not be set, sanitize, */
1527
+ if ((loc = strpbrk(opts->summary, "\r\n")) != NULL) {
1528
+ size_t offset = 0;
1529
+
1530
+ if ((offset = (loc - opts->summary)) == 0) {
1531
+ giterr_set(GITERR_INVALID, "summary is empty");
1532
+ error = -1;
1533
+ }
1534
+
1535
+ summary = git__calloc(offset + 1, sizeof(char));
1536
+ GITERR_CHECK_ALLOC(summary);
1537
+ strncpy(summary, opts->summary, offset);
1538
+ }
1539
+
1540
+ error = git_diff_format_email__append_header_tobuf(out,
1541
+ opts->id, opts->author, summary == NULL ? opts->summary : summary,
1542
+ opts->patch_no, opts->total_patches, ignore_marker);
1543
+
1544
+ if (error < 0)
1545
+ goto on_error;
1546
+
1547
+ format_flags = GIT_DIFF_STATS_FULL | GIT_DIFF_STATS_INCLUDE_SUMMARY;
1548
+
1549
+ if ((error = git_buf_puts(out, "---\n")) < 0 ||
1550
+ (error = git_diff_get_stats(&stats, diff)) < 0 ||
1551
+ (error = git_diff_stats_to_buf(out, stats, format_flags, 0)) < 0 ||
1552
+ (error = git_buf_putc(out, '\n')) < 0 ||
1553
+ (error = git_diff_format_email__append_patches_tobuf(out, diff)) < 0)
1554
+ goto on_error;
1555
+
1556
+ error = git_buf_puts(out, "--\nlibgit2 " LIBGIT2_VERSION "\n\n");
1557
+
1558
+ on_error:
1559
+ git__free(summary);
1560
+ git_diff_stats_free(stats);
1561
+
1562
+ return error;
1563
+ }
1564
+
1565
+ int git_diff_commit_as_email(
1566
+ git_buf *out,
1567
+ git_repository *repo,
1568
+ git_commit *commit,
1569
+ size_t patch_no,
1570
+ size_t total_patches,
1571
+ git_diff_format_email_flags_t flags,
1572
+ const git_diff_options *diff_opts)
1573
+ {
1574
+ git_diff *diff = NULL;
1575
+ git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
1576
+ int error;
1577
+
1578
+ assert (out && repo && commit);
1579
+
1580
+ opts.flags = flags;
1581
+ opts.patch_no = patch_no;
1582
+ opts.total_patches = total_patches;
1583
+ opts.id = git_commit_id(commit);
1584
+ opts.summary = git_commit_summary(commit);
1585
+ opts.author = git_commit_author(commit);
1586
+
1587
+ if ((error = git_diff__commit(&diff, repo, commit, diff_opts)) < 0)
1588
+ return error;
1589
+
1590
+ error = git_diff_format_email(out, diff, &opts);
1591
+
1592
+ git_diff_free(diff);
1593
+ return error;
1594
+ }
1595
+
1596
+ int git_diff_init_options(git_diff_options *opts, unsigned int version)
1597
+ {
1598
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
1599
+ opts, version, git_diff_options, GIT_DIFF_OPTIONS_INIT);
1600
+ return 0;
1601
+ }
1602
+
1603
+ int git_diff_find_init_options(
1604
+ git_diff_find_options *opts, unsigned int version)
1605
+ {
1606
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
1607
+ opts, version, git_diff_find_options, GIT_DIFF_FIND_OPTIONS_INIT);
1608
+ return 0;
1609
+ }
1610
+
1611
+ int git_diff_format_email_init_options(
1612
+ git_diff_format_email_options *opts, unsigned int version)
1613
+ {
1614
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
1615
+ opts, version, git_diff_format_email_options,
1616
+ GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT);
1312
1617
  return 0;
1313
1618
  }