rugged 0.17.0.b7 → 0.18.0.b1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (310) hide show
  1. data/LICENSE +1 -1
  2. data/README.md +88 -32
  3. data/ext/rugged/extconf.rb +4 -2
  4. data/ext/rugged/rugged.c +72 -10
  5. data/ext/rugged/rugged.h +14 -10
  6. data/ext/rugged/rugged_blob.c +8 -10
  7. data/ext/rugged/rugged_branch.c +11 -14
  8. data/ext/rugged/rugged_commit.c +31 -24
  9. data/ext/rugged/rugged_config.c +2 -2
  10. data/ext/rugged/rugged_index.c +133 -198
  11. data/ext/rugged/rugged_note.c +372 -0
  12. data/ext/rugged/rugged_object.c +50 -22
  13. data/ext/rugged/rugged_reference.c +122 -130
  14. data/ext/rugged/rugged_remote.c +72 -29
  15. data/ext/rugged/rugged_repo.c +402 -20
  16. data/ext/rugged/rugged_revwalk.c +7 -3
  17. data/ext/rugged/rugged_settings.c +110 -0
  18. data/ext/rugged/rugged_signature.c +23 -7
  19. data/ext/rugged/rugged_tag.c +32 -16
  20. data/ext/rugged/rugged_tree.c +44 -15
  21. data/lib/rugged.rb +1 -0
  22. data/lib/rugged/index.rb +8 -0
  23. data/lib/rugged/remote.rb +13 -0
  24. data/lib/rugged/repository.rb +3 -3
  25. data/lib/rugged/version.rb +1 -1
  26. data/test/blob_test.rb +13 -15
  27. data/test/branch_test.rb +32 -67
  28. data/test/commit_test.rb +50 -12
  29. data/test/config_test.rb +12 -11
  30. data/test/coverage/HEAD.json +1 -1
  31. data/test/coverage/cover.rb +40 -21
  32. data/test/errors_test.rb +34 -0
  33. data/test/fixtures/alternate/objects/14/6ae76773c91e3b1d00cf7a338ec55ae58297e2 +0 -0
  34. data/test/fixtures/alternate/objects/14/9c32d47e99d0a3572ff1e70a2e0051bbf347a9 +0 -0
  35. data/test/fixtures/alternate/objects/14/fb3108588f9421bf764041e5e3ac305eb6277f +0 -0
  36. data/test/fixtures/testrepo.git/logs/refs/notes/commits +1 -0
  37. data/test/fixtures/testrepo.git/objects/44/1034f860c1d5d90e4188d11ae0d325176869a8 +1 -0
  38. data/test/fixtures/testrepo.git/objects/60/d415052a33de2150bf68757f6461df4f563ae4 +0 -0
  39. data/test/fixtures/testrepo.git/objects/68/8a8f4ef7496901d15322972f96e212a9e466cc +1 -0
  40. data/test/fixtures/testrepo.git/objects/94/eca2de348d5f672faf56b0decafa5937e3235e +0 -0
  41. data/test/fixtures/testrepo.git/objects/9b/7384fe1676186192842f5d3e129457b62db9e3 +0 -0
  42. data/test/fixtures/testrepo.git/objects/b7/4713326bc972cc15751ed504dca6f6f3b91f7a +3 -0
  43. data/test/fixtures/testrepo.git/refs/notes/commits +1 -0
  44. data/test/index_test.rb +65 -69
  45. data/test/lib_test.rb +76 -11
  46. data/test/note_test.rb +158 -0
  47. data/test/object_test.rb +8 -11
  48. data/test/reference_test.rb +77 -85
  49. data/test/remote_test.rb +86 -8
  50. data/test/repo_pack_test.rb +9 -7
  51. data/test/repo_reset_test.rb +80 -0
  52. data/test/repo_test.rb +176 -53
  53. data/test/tag_test.rb +44 -7
  54. data/test/test_helper.rb +63 -35
  55. data/test/tree_test.rb +34 -13
  56. data/test/walker_test.rb +14 -14
  57. data/vendor/libgit2/Makefile.embed +1 -1
  58. data/vendor/libgit2/deps/http-parser/http_parser.c +974 -578
  59. data/vendor/libgit2/deps/http-parser/http_parser.h +106 -70
  60. data/vendor/libgit2/deps/regex/regcomp.c +7 -6
  61. data/vendor/libgit2/deps/regex/regex_internal.c +1 -1
  62. data/vendor/libgit2/deps/regex/regex_internal.h +12 -3
  63. data/vendor/libgit2/deps/regex/regexec.c +5 -5
  64. data/vendor/libgit2/include/git2.h +5 -1
  65. data/vendor/libgit2/include/git2/attr.h +4 -2
  66. data/vendor/libgit2/include/git2/blob.h +39 -12
  67. data/vendor/libgit2/include/git2/branch.h +123 -35
  68. data/vendor/libgit2/include/git2/checkout.h +206 -48
  69. data/vendor/libgit2/include/git2/clone.h +72 -27
  70. data/vendor/libgit2/include/git2/commit.h +20 -17
  71. data/vendor/libgit2/include/git2/common.h +67 -1
  72. data/vendor/libgit2/include/git2/config.h +81 -60
  73. data/vendor/libgit2/include/git2/cred_helpers.h +53 -0
  74. data/vendor/libgit2/include/git2/diff.h +459 -150
  75. data/vendor/libgit2/include/git2/errors.h +9 -1
  76. data/vendor/libgit2/include/git2/graph.h +41 -0
  77. data/vendor/libgit2/include/git2/ignore.h +7 -6
  78. data/vendor/libgit2/include/git2/index.h +323 -97
  79. data/vendor/libgit2/include/git2/indexer.h +27 -59
  80. data/vendor/libgit2/include/git2/inttypes.h +4 -0
  81. data/vendor/libgit2/include/git2/merge.h +13 -3
  82. data/vendor/libgit2/include/git2/message.h +14 -8
  83. data/vendor/libgit2/include/git2/net.h +9 -7
  84. data/vendor/libgit2/include/git2/notes.h +88 -29
  85. data/vendor/libgit2/include/git2/object.h +16 -6
  86. data/vendor/libgit2/include/git2/odb.h +80 -17
  87. data/vendor/libgit2/include/git2/odb_backend.h +47 -11
  88. data/vendor/libgit2/include/git2/oid.h +26 -17
  89. data/vendor/libgit2/include/git2/pack.h +62 -8
  90. data/vendor/libgit2/include/git2/push.h +131 -0
  91. data/vendor/libgit2/include/git2/refdb.h +103 -0
  92. data/vendor/libgit2/include/git2/refdb_backend.h +109 -0
  93. data/vendor/libgit2/include/git2/reflog.h +30 -21
  94. data/vendor/libgit2/include/git2/refs.h +215 -193
  95. data/vendor/libgit2/include/git2/refspec.h +22 -2
  96. data/vendor/libgit2/include/git2/remote.h +158 -37
  97. data/vendor/libgit2/include/git2/repository.h +150 -31
  98. data/vendor/libgit2/include/git2/reset.h +43 -9
  99. data/vendor/libgit2/include/git2/revparse.h +48 -4
  100. data/vendor/libgit2/include/git2/revwalk.h +25 -10
  101. data/vendor/libgit2/include/git2/signature.h +20 -12
  102. data/vendor/libgit2/include/git2/stash.h +121 -0
  103. data/vendor/libgit2/include/git2/status.h +122 -53
  104. data/vendor/libgit2/include/git2/strarray.h +17 -11
  105. data/vendor/libgit2/include/git2/submodule.h +42 -7
  106. data/vendor/libgit2/include/git2/tag.h +72 -59
  107. data/vendor/libgit2/include/git2/threads.h +4 -2
  108. data/vendor/libgit2/include/git2/trace.h +68 -0
  109. data/vendor/libgit2/include/git2/transport.h +328 -0
  110. data/vendor/libgit2/include/git2/tree.h +149 -120
  111. data/vendor/libgit2/include/git2/types.h +13 -12
  112. data/vendor/libgit2/include/git2/version.h +3 -3
  113. data/vendor/libgit2/src/amiga/map.c +2 -2
  114. data/vendor/libgit2/src/attr.c +58 -48
  115. data/vendor/libgit2/src/attr.h +4 -18
  116. data/vendor/libgit2/src/attr_file.c +30 -6
  117. data/vendor/libgit2/src/attr_file.h +6 -8
  118. data/vendor/libgit2/src/attrcache.h +24 -0
  119. data/vendor/libgit2/src/blob.c +30 -7
  120. data/vendor/libgit2/src/blob.h +1 -1
  121. data/vendor/libgit2/src/branch.c +361 -68
  122. data/vendor/libgit2/src/branch.h +17 -0
  123. data/vendor/libgit2/src/bswap.h +1 -1
  124. data/vendor/libgit2/src/buf_text.c +291 -0
  125. data/vendor/libgit2/src/buf_text.h +122 -0
  126. data/vendor/libgit2/src/buffer.c +27 -101
  127. data/vendor/libgit2/src/buffer.h +54 -39
  128. data/vendor/libgit2/src/cache.c +15 -6
  129. data/vendor/libgit2/src/cache.h +1 -1
  130. data/vendor/libgit2/src/cc-compat.h +3 -1
  131. data/vendor/libgit2/src/checkout.c +1165 -222
  132. data/vendor/libgit2/src/checkout.h +24 -0
  133. data/vendor/libgit2/src/clone.c +171 -86
  134. data/vendor/libgit2/src/commit.c +44 -45
  135. data/vendor/libgit2/src/commit.h +3 -3
  136. data/vendor/libgit2/src/commit_list.c +194 -0
  137. data/vendor/libgit2/src/commit_list.h +49 -0
  138. data/vendor/libgit2/src/common.h +44 -10
  139. data/vendor/libgit2/src/compress.c +1 -1
  140. data/vendor/libgit2/src/compress.h +1 -1
  141. data/vendor/libgit2/src/config.c +211 -124
  142. data/vendor/libgit2/src/config.h +23 -4
  143. data/vendor/libgit2/src/config_cache.c +2 -2
  144. data/vendor/libgit2/src/config_file.c +129 -53
  145. data/vendor/libgit2/src/config_file.h +10 -8
  146. data/vendor/libgit2/src/crlf.c +66 -67
  147. data/vendor/libgit2/src/date.c +12 -12
  148. data/vendor/libgit2/src/delta-apply.c +14 -1
  149. data/vendor/libgit2/src/delta-apply.h +18 -1
  150. data/vendor/libgit2/src/delta.c +40 -107
  151. data/vendor/libgit2/src/delta.h +19 -17
  152. data/vendor/libgit2/src/diff.c +347 -496
  153. data/vendor/libgit2/src/diff.h +27 -1
  154. data/vendor/libgit2/src/diff_output.c +564 -249
  155. data/vendor/libgit2/src/diff_output.h +15 -8
  156. data/vendor/libgit2/src/diff_tform.c +687 -0
  157. data/vendor/libgit2/src/errors.c +27 -36
  158. data/vendor/libgit2/src/fetch.c +13 -351
  159. data/vendor/libgit2/src/fetch.h +13 -3
  160. data/vendor/libgit2/src/fetchhead.c +295 -0
  161. data/vendor/libgit2/src/fetchhead.h +34 -0
  162. data/vendor/libgit2/src/filebuf.c +42 -15
  163. data/vendor/libgit2/src/filebuf.h +4 -2
  164. data/vendor/libgit2/src/fileops.c +466 -113
  165. data/vendor/libgit2/src/fileops.h +154 -28
  166. data/vendor/libgit2/src/filter.c +3 -75
  167. data/vendor/libgit2/src/filter.h +1 -29
  168. data/vendor/libgit2/src/fnmatch.c +1 -1
  169. data/vendor/libgit2/src/fnmatch.h +1 -1
  170. data/vendor/libgit2/src/global.c +54 -10
  171. data/vendor/libgit2/src/global.h +10 -1
  172. data/vendor/libgit2/src/graph.c +178 -0
  173. data/vendor/libgit2/src/hash.c +25 -52
  174. data/vendor/libgit2/src/hash.h +21 -9
  175. data/vendor/libgit2/src/{sha1/sha1.c → hash/hash_generic.c} +20 -12
  176. data/vendor/libgit2/src/hash/hash_generic.h +24 -0
  177. data/vendor/libgit2/src/hash/hash_openssl.h +45 -0
  178. data/vendor/libgit2/src/hash/hash_win32.c +291 -0
  179. data/vendor/libgit2/src/hash/hash_win32.h +140 -0
  180. data/vendor/libgit2/src/hashsig.c +368 -0
  181. data/vendor/libgit2/src/hashsig.h +72 -0
  182. data/vendor/libgit2/src/ignore.c +22 -15
  183. data/vendor/libgit2/src/ignore.h +6 -1
  184. data/vendor/libgit2/src/index.c +770 -171
  185. data/vendor/libgit2/src/index.h +13 -5
  186. data/vendor/libgit2/src/indexer.c +286 -431
  187. data/vendor/libgit2/src/iterator.c +854 -466
  188. data/vendor/libgit2/src/iterator.h +134 -109
  189. data/vendor/libgit2/src/map.h +1 -1
  190. data/vendor/libgit2/src/merge.c +296 -0
  191. data/vendor/libgit2/src/merge.h +22 -0
  192. data/vendor/libgit2/src/message.c +1 -1
  193. data/vendor/libgit2/src/message.h +1 -1
  194. data/vendor/libgit2/src/mwindow.c +35 -30
  195. data/vendor/libgit2/src/mwindow.h +2 -2
  196. data/vendor/libgit2/src/netops.c +162 -98
  197. data/vendor/libgit2/src/netops.h +50 -15
  198. data/vendor/libgit2/src/notes.c +109 -58
  199. data/vendor/libgit2/src/notes.h +2 -1
  200. data/vendor/libgit2/src/object.c +46 -57
  201. data/vendor/libgit2/src/object.h +1 -8
  202. data/vendor/libgit2/src/odb.c +151 -40
  203. data/vendor/libgit2/src/odb.h +5 -1
  204. data/vendor/libgit2/src/odb_loose.c +4 -5
  205. data/vendor/libgit2/src/odb_pack.c +122 -80
  206. data/vendor/libgit2/src/offmap.h +65 -0
  207. data/vendor/libgit2/src/oid.c +12 -4
  208. data/vendor/libgit2/src/oidmap.h +1 -1
  209. data/vendor/libgit2/src/pack-objects.c +88 -61
  210. data/vendor/libgit2/src/pack-objects.h +8 -8
  211. data/vendor/libgit2/src/pack.c +293 -28
  212. data/vendor/libgit2/src/pack.h +49 -4
  213. data/vendor/libgit2/src/path.c +103 -14
  214. data/vendor/libgit2/src/path.h +23 -7
  215. data/vendor/libgit2/src/pathspec.c +168 -0
  216. data/vendor/libgit2/src/pathspec.h +40 -0
  217. data/vendor/libgit2/src/pool.c +29 -4
  218. data/vendor/libgit2/src/pool.h +8 -1
  219. data/vendor/libgit2/src/posix.c +26 -27
  220. data/vendor/libgit2/src/posix.h +2 -3
  221. data/vendor/libgit2/src/pqueue.c +23 -1
  222. data/vendor/libgit2/src/pqueue.h +23 -1
  223. data/vendor/libgit2/src/push.c +653 -0
  224. data/vendor/libgit2/src/push.h +51 -0
  225. data/vendor/libgit2/src/refdb.c +185 -0
  226. data/vendor/libgit2/src/refdb.h +46 -0
  227. data/vendor/libgit2/src/refdb_fs.c +1024 -0
  228. data/vendor/libgit2/src/refdb_fs.h +15 -0
  229. data/vendor/libgit2/src/reflog.c +77 -45
  230. data/vendor/libgit2/src/reflog.h +1 -3
  231. data/vendor/libgit2/src/refs.c +366 -1326
  232. data/vendor/libgit2/src/refs.h +22 -13
  233. data/vendor/libgit2/src/refspec.c +46 -7
  234. data/vendor/libgit2/src/refspec.h +11 -1
  235. data/vendor/libgit2/src/remote.c +758 -120
  236. data/vendor/libgit2/src/remote.h +10 -5
  237. data/vendor/libgit2/src/repo_template.h +6 -6
  238. data/vendor/libgit2/src/repository.c +315 -96
  239. data/vendor/libgit2/src/repository.h +5 -3
  240. data/vendor/libgit2/src/reset.c +99 -81
  241. data/vendor/libgit2/src/revparse.c +157 -84
  242. data/vendor/libgit2/src/revwalk.c +68 -470
  243. data/vendor/libgit2/src/revwalk.h +44 -0
  244. data/vendor/libgit2/src/sha1_lookup.c +1 -1
  245. data/vendor/libgit2/src/sha1_lookup.h +1 -1
  246. data/vendor/libgit2/src/signature.c +68 -200
  247. data/vendor/libgit2/src/signature.h +1 -1
  248. data/vendor/libgit2/src/stash.c +663 -0
  249. data/vendor/libgit2/src/status.c +101 -79
  250. data/vendor/libgit2/src/strmap.h +1 -1
  251. data/vendor/libgit2/src/submodule.c +67 -51
  252. data/vendor/libgit2/src/submodule.h +1 -1
  253. data/vendor/libgit2/src/tag.c +35 -29
  254. data/vendor/libgit2/src/tag.h +1 -1
  255. data/vendor/libgit2/src/thread-utils.c +1 -1
  256. data/vendor/libgit2/src/thread-utils.h +2 -2
  257. data/vendor/libgit2/src/trace.c +39 -0
  258. data/vendor/libgit2/src/trace.h +56 -0
  259. data/vendor/libgit2/src/transport.c +81 -34
  260. data/vendor/libgit2/src/transports/cred.c +60 -0
  261. data/vendor/libgit2/src/transports/cred_helpers.c +49 -0
  262. data/vendor/libgit2/src/transports/git.c +234 -127
  263. data/vendor/libgit2/src/transports/http.c +761 -433
  264. data/vendor/libgit2/src/transports/local.c +460 -64
  265. data/vendor/libgit2/src/transports/smart.c +345 -0
  266. data/vendor/libgit2/src/transports/smart.h +179 -0
  267. data/vendor/libgit2/src/{pkt.c → transports/smart_pkt.c} +131 -12
  268. data/vendor/libgit2/src/transports/smart_protocol.c +856 -0
  269. data/vendor/libgit2/src/transports/winhttp.c +1136 -0
  270. data/vendor/libgit2/src/tree-cache.c +2 -2
  271. data/vendor/libgit2/src/tree-cache.h +1 -1
  272. data/vendor/libgit2/src/tree.c +239 -166
  273. data/vendor/libgit2/src/tree.h +11 -2
  274. data/vendor/libgit2/src/tsort.c +39 -23
  275. data/vendor/libgit2/src/unix/map.c +1 -1
  276. data/vendor/libgit2/src/unix/posix.h +12 -2
  277. data/vendor/libgit2/src/unix/realpath.c +30 -0
  278. data/vendor/libgit2/src/util.c +250 -13
  279. data/vendor/libgit2/src/util.h +71 -14
  280. data/vendor/libgit2/src/vector.c +123 -60
  281. data/vendor/libgit2/src/vector.h +24 -22
  282. data/vendor/libgit2/src/win32/dir.c +1 -1
  283. data/vendor/libgit2/src/win32/dir.h +1 -1
  284. data/vendor/libgit2/src/win32/error.c +77 -0
  285. data/vendor/libgit2/src/win32/error.h +13 -0
  286. data/vendor/libgit2/src/win32/findfile.c +143 -54
  287. data/vendor/libgit2/src/win32/findfile.h +10 -6
  288. data/vendor/libgit2/src/win32/map.c +1 -1
  289. data/vendor/libgit2/src/win32/mingw-compat.h +1 -1
  290. data/vendor/libgit2/src/win32/msvc-compat.h +10 -1
  291. data/vendor/libgit2/src/win32/posix.h +10 -1
  292. data/vendor/libgit2/src/win32/posix_w32.c +132 -63
  293. data/vendor/libgit2/src/win32/precompiled.c +1 -1
  294. data/vendor/libgit2/src/win32/pthread.c +1 -1
  295. data/vendor/libgit2/src/win32/pthread.h +1 -1
  296. data/vendor/libgit2/src/win32/utf-conv.c +5 -5
  297. data/vendor/libgit2/src/win32/utf-conv.h +3 -3
  298. data/vendor/libgit2/src/win32/version.h +20 -0
  299. metadata +308 -252
  300. data/test/fixtures/testrepo.git/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 +0 -0
  301. data/test/fixtures/testrepo.git/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0 +0 -0
  302. data/test/fixtures/testrepo.git/objects/a3/e05719b428a2d0ed7a55c4ce53dcc5768c6d5e +0 -0
  303. data/test/index_test.rb~ +0 -218
  304. data/vendor/libgit2/src/pkt.h +0 -91
  305. data/vendor/libgit2/src/ppc/sha1.c +0 -70
  306. data/vendor/libgit2/src/ppc/sha1.h +0 -26
  307. data/vendor/libgit2/src/protocol.c +0 -110
  308. data/vendor/libgit2/src/protocol.h +0 -21
  309. data/vendor/libgit2/src/sha1.h +0 -33
  310. data/vendor/libgit2/src/transport.h +0 -148
@@ -46,11 +46,12 @@ extern unsigned long git_delta_sizeof_index(struct git_delta_index *index);
46
46
  * returned and *delta_size is updated with its size. The returned buffer
47
47
  * must be freed by the caller.
48
48
  */
49
- extern void *
50
- git_delta_create(const struct git_delta_index *index,
51
- const void *buf, unsigned long bufsize,
52
- unsigned long *delta_size,
53
- unsigned long max_delta_size);
49
+ extern void *git_delta_create(
50
+ const struct git_delta_index *index,
51
+ const void *buf,
52
+ unsigned long bufsize,
53
+ unsigned long *delta_size,
54
+ unsigned long max_delta_size);
54
55
 
55
56
  /*
56
57
  * diff_delta: create a delta from source buffer to target buffer
@@ -60,15 +61,16 @@ git_delta_create(const struct git_delta_index *index,
60
61
  * pointer to the buffer with the delta data is returned and *delta_size is
61
62
  * updated with its size. The returned buffer must be freed by the caller.
62
63
  */
63
- GIT_INLINE(void *)
64
- git_delta(const void *src_buf, unsigned long src_bufsize,
65
- const void *trg_buf, unsigned long trg_bufsize,
66
- unsigned long *delta_size, unsigned long max_delta_size)
64
+ GIT_INLINE(void *) git_delta(
65
+ const void *src_buf, unsigned long src_bufsize,
66
+ const void *trg_buf, unsigned long trg_bufsize,
67
+ unsigned long *delta_size,
68
+ unsigned long max_delta_size)
67
69
  {
68
70
  struct git_delta_index *index = git_delta_create_index(src_buf, src_bufsize);
69
71
  if (index) {
70
- void *delta = git_delta_create(index, trg_buf, trg_bufsize,
71
- delta_size, max_delta_size);
72
+ void *delta = git_delta_create(
73
+ index, trg_buf, trg_bufsize, delta_size, max_delta_size);
72
74
  git_delta_free_index(index);
73
75
  return delta;
74
76
  }
@@ -82,9 +84,10 @@ git_delta(const void *src_buf, unsigned long src_bufsize,
82
84
  * *trg_bufsize is updated with its size. On failure a NULL pointer is
83
85
  * returned. The returned buffer must be freed by the caller.
84
86
  */
85
- extern void *git_delta_patch(const void *src_buf, unsigned long src_size,
86
- const void *delta_buf, unsigned long delta_size,
87
- unsigned long *dst_size);
87
+ extern void *git_delta_patch(
88
+ const void *src_buf, unsigned long src_size,
89
+ const void *delta_buf, unsigned long delta_size,
90
+ unsigned long *dst_size);
88
91
 
89
92
  /* the smallest possible delta size is 4 bytes */
90
93
  #define GIT_DELTA_SIZE_MIN 4
@@ -93,9 +96,8 @@ extern void *git_delta_patch(const void *src_buf, unsigned long src_size,
93
96
  * This must be called twice on the delta data buffer, first to get the
94
97
  * expected source buffer size, and again to get the target buffer size.
95
98
  */
96
- GIT_INLINE(unsigned long)
97
- git_delta_get_hdr_size(const unsigned char **datap,
98
- const unsigned char *top)
99
+ GIT_INLINE(unsigned long) git_delta_get_hdr_size(
100
+ const unsigned char **datap, const unsigned char *top)
99
101
  {
100
102
  const unsigned char *data = *datap;
101
103
  unsigned long cmd, size = 0;
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (C) 2012 the libgit2 contributors
2
+ * Copyright (C) the libgit2 contributors. All rights reserved.
3
3
  *
4
4
  * This file is part of libgit2, distributed under the GNU GPL v2 with
5
5
  * a Linking Exception. For full terms see the included COPYING file.
@@ -10,76 +10,10 @@
10
10
  #include "config.h"
11
11
  #include "attr_file.h"
12
12
  #include "filter.h"
13
+ #include "pathspec.h"
13
14
 
14
- static char *diff_prefix_from_pathspec(const git_strarray *pathspec)
15
- {
16
- git_buf prefix = GIT_BUF_INIT;
17
- const char *scan;
18
-
19
- if (git_buf_common_prefix(&prefix, pathspec) < 0)
20
- return NULL;
21
-
22
- /* diff prefix will only be leading non-wildcards */
23
- for (scan = prefix.ptr; *scan; ++scan) {
24
- if (git__iswildcard(*scan) &&
25
- (scan == prefix.ptr || (*(scan - 1) != '\\')))
26
- break;
27
- }
28
- git_buf_truncate(&prefix, scan - prefix.ptr);
29
-
30
- if (prefix.size <= 0) {
31
- git_buf_free(&prefix);
32
- return NULL;
33
- }
34
-
35
- git_buf_unescape(&prefix);
36
-
37
- return git_buf_detach(&prefix);
38
- }
39
-
40
- static bool diff_pathspec_is_interesting(const git_strarray *pathspec)
41
- {
42
- const char *str;
43
-
44
- if (pathspec == NULL || pathspec->count == 0)
45
- return false;
46
- if (pathspec->count > 1)
47
- return true;
48
-
49
- str = pathspec->strings[0];
50
- if (!str || !str[0] || (!str[1] && (str[0] == '*' || str[0] == '.')))
51
- return false;
52
- return true;
53
- }
54
-
55
- static bool diff_path_matches_pathspec(git_diff_list *diff, const char *path)
56
- {
57
- unsigned int i;
58
- git_attr_fnmatch *match;
59
-
60
- if (!diff->pathspec.length)
61
- return true;
62
-
63
- git_vector_foreach(&diff->pathspec, i, match) {
64
- int result = strcmp(match->pattern, path) ? FNM_NOMATCH : 0;
65
-
66
- if (((diff->opts.flags & GIT_DIFF_DISABLE_PATHSPEC_MATCH) == 0) &&
67
- result == FNM_NOMATCH)
68
- result = p_fnmatch(match->pattern, path, 0);
69
-
70
- /* if we didn't match, look for exact dirname prefix match */
71
- if (result == FNM_NOMATCH &&
72
- (match->flags & GIT_ATTR_FNMATCH_HASWILD) == 0 &&
73
- strncmp(path, match->pattern, match->length) == 0 &&
74
- path[match->length] == '/')
75
- result = 0;
76
-
77
- if (result == 0)
78
- return (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) ? false : true;
79
- }
80
-
81
- return false;
82
- }
15
+ #define DIFF_FLAG_IS_SET(DIFF,FLAG) (((DIFF)->opts.flags & (FLAG)) != 0)
16
+ #define DIFF_FLAG_ISNT_SET(DIFF,FLAG) (((DIFF)->opts.flags & (FLAG)) == 0)
83
17
 
84
18
  static git_diff_delta *diff_delta__alloc(
85
19
  git_diff_list *diff,
@@ -98,7 +32,7 @@ static git_diff_delta *diff_delta__alloc(
98
32
 
99
33
  delta->new_file.path = delta->old_file.path;
100
34
 
101
- if (diff->opts.flags & GIT_DIFF_REVERSE) {
35
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
102
36
  switch (status) {
103
37
  case GIT_DELTA_ADDED: status = GIT_DELTA_DELETED; break;
104
38
  case GIT_DELTA_DELETED: status = GIT_DELTA_ADDED; break;
@@ -110,83 +44,16 @@ static git_diff_delta *diff_delta__alloc(
110
44
  return delta;
111
45
  }
112
46
 
113
- static git_diff_delta *diff_delta__dup(
114
- const git_diff_delta *d, git_pool *pool)
115
- {
116
- git_diff_delta *delta = git__malloc(sizeof(git_diff_delta));
117
- if (!delta)
118
- return NULL;
119
-
120
- memcpy(delta, d, sizeof(git_diff_delta));
121
-
122
- delta->old_file.path = git_pool_strdup(pool, d->old_file.path);
123
- if (delta->old_file.path == NULL)
124
- goto fail;
125
-
126
- if (d->new_file.path != d->old_file.path) {
127
- delta->new_file.path = git_pool_strdup(pool, d->new_file.path);
128
- if (delta->new_file.path == NULL)
129
- goto fail;
130
- } else {
131
- delta->new_file.path = delta->old_file.path;
132
- }
133
-
134
- return delta;
135
-
136
- fail:
137
- git__free(delta);
138
- return NULL;
139
- }
140
-
141
- static git_diff_delta *diff_delta__merge_like_cgit(
142
- const git_diff_delta *a, const git_diff_delta *b, git_pool *pool)
47
+ static int diff_notify(
48
+ const git_diff_list *diff,
49
+ const git_diff_delta *delta,
50
+ const char *matched_pathspec)
143
51
  {
144
- git_diff_delta *dup;
145
-
146
- /* Emulate C git for merging two diffs (a la 'git diff <sha>').
147
- *
148
- * When C git does a diff between the work dir and a tree, it actually
149
- * diffs with the index but uses the workdir contents. This emulates
150
- * those choices so we can emulate the type of diff.
151
- *
152
- * We have three file descriptions here, let's call them:
153
- * f1 = a->old_file
154
- * f2 = a->new_file AND b->old_file
155
- * f3 = b->new_file
156
- */
157
-
158
- /* if f2 == f3 or f2 is deleted, then just dup the 'a' diff */
159
- if (b->status == GIT_DELTA_UNMODIFIED || a->status == GIT_DELTA_DELETED)
160
- return diff_delta__dup(a, pool);
161
-
162
- /* otherwise, base this diff on the 'b' diff */
163
- if ((dup = diff_delta__dup(b, pool)) == NULL)
164
- return NULL;
165
-
166
- /* If 'a' status is uninteresting, then we're done */
167
- if (a->status == GIT_DELTA_UNMODIFIED)
168
- return dup;
169
-
170
- assert(a->status != GIT_DELTA_UNMODIFIED);
171
- assert(b->status != GIT_DELTA_UNMODIFIED);
172
-
173
- /* A cgit exception is that the diff of a file that is only in the
174
- * index (i.e. not in HEAD nor workdir) is given as empty.
175
- */
176
- if (dup->status == GIT_DELTA_DELETED) {
177
- if (a->status == GIT_DELTA_ADDED)
178
- dup->status = GIT_DELTA_UNMODIFIED;
179
- /* else don't overwrite DELETE status */
180
- } else {
181
- dup->status = a->status;
182
- }
183
-
184
- git_oid_cpy(&dup->old_file.oid, &a->old_file.oid);
185
- dup->old_file.mode = a->old_file.mode;
186
- dup->old_file.size = a->old_file.size;
187
- dup->old_file.flags = a->old_file.flags;
52
+ if (!diff->opts.notify_cb)
53
+ return 0;
188
54
 
189
- return dup;
55
+ return diff->opts.notify_cb(
56
+ diff, delta, matched_pathspec, diff->opts.notify_payload);
190
57
  }
191
58
 
192
59
  static int diff_delta__from_one(
@@ -195,16 +62,26 @@ static int diff_delta__from_one(
195
62
  const git_index_entry *entry)
196
63
  {
197
64
  git_diff_delta *delta;
65
+ const char *matched_pathspec;
66
+ int notify_res;
198
67
 
199
68
  if (status == GIT_DELTA_IGNORED &&
200
- (diff->opts.flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
69
+ DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_IGNORED))
201
70
  return 0;
202
71
 
203
72
  if (status == GIT_DELTA_UNTRACKED &&
204
- (diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
73
+ DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNTRACKED))
74
+ return 0;
75
+
76
+ if (entry->mode == GIT_FILEMODE_COMMIT &&
77
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES))
205
78
  return 0;
206
79
 
207
- if (!diff_path_matches_pathspec(diff, entry->path))
80
+ if (!git_pathspec_match_path(
81
+ &diff->pathspec, entry->path,
82
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH),
83
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE),
84
+ &matched_pathspec))
208
85
  return 0;
209
86
 
210
87
  delta = diff_delta__alloc(diff, status, entry->path);
@@ -223,18 +100,22 @@ static int diff_delta__from_one(
223
100
  git_oid_cpy(&delta->new_file.oid, &entry->oid);
224
101
  }
225
102
 
226
- delta->old_file.flags |= GIT_DIFF_FILE_VALID_OID;
103
+ delta->old_file.flags |= GIT_DIFF_FLAG_VALID_OID;
227
104
 
228
105
  if (delta->status == GIT_DELTA_DELETED ||
229
106
  !git_oid_iszero(&delta->new_file.oid))
230
- delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
107
+ delta->new_file.flags |= GIT_DIFF_FLAG_VALID_OID;
231
108
 
232
- if (git_vector_insert(&diff->deltas, delta) < 0) {
109
+ notify_res = diff_notify(diff, delta, matched_pathspec);
110
+
111
+ if (notify_res)
112
+ git__free(delta);
113
+ else if (git_vector_insert(&diff->deltas, delta) < 0) {
233
114
  git__free(delta);
234
115
  return -1;
235
116
  }
236
117
 
237
- return 0;
118
+ return notify_res < 0 ? GIT_EUSER : 0;
238
119
  }
239
120
 
240
121
  static int diff_delta__from_two(
@@ -244,15 +125,22 @@ static int diff_delta__from_two(
244
125
  uint32_t old_mode,
245
126
  const git_index_entry *new_entry,
246
127
  uint32_t new_mode,
247
- git_oid *new_oid)
128
+ git_oid *new_oid,
129
+ const char *matched_pathspec)
248
130
  {
249
131
  git_diff_delta *delta;
132
+ int notify_res;
250
133
 
251
134
  if (status == GIT_DELTA_UNMODIFIED &&
252
- (diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
135
+ DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNMODIFIED))
136
+ return 0;
137
+
138
+ if (old_entry->mode == GIT_FILEMODE_COMMIT &&
139
+ new_entry->mode == GIT_FILEMODE_COMMIT &&
140
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES))
253
141
  return 0;
254
142
 
255
- if ((diff->opts.flags & GIT_DIFF_REVERSE) != 0) {
143
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
256
144
  uint32_t temp_mode = old_mode;
257
145
  const git_index_entry *temp_entry = old_entry;
258
146
  old_entry = new_entry;
@@ -267,28 +155,32 @@ static int diff_delta__from_two(
267
155
  git_oid_cpy(&delta->old_file.oid, &old_entry->oid);
268
156
  delta->old_file.size = old_entry->file_size;
269
157
  delta->old_file.mode = old_mode;
270
- delta->old_file.flags |= GIT_DIFF_FILE_VALID_OID;
158
+ delta->old_file.flags |= GIT_DIFF_FLAG_VALID_OID;
271
159
 
272
160
  git_oid_cpy(&delta->new_file.oid, &new_entry->oid);
273
161
  delta->new_file.size = new_entry->file_size;
274
162
  delta->new_file.mode = new_mode;
275
163
 
276
164
  if (new_oid) {
277
- if ((diff->opts.flags & GIT_DIFF_REVERSE) != 0)
165
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE))
278
166
  git_oid_cpy(&delta->old_file.oid, new_oid);
279
167
  else
280
168
  git_oid_cpy(&delta->new_file.oid, new_oid);
281
169
  }
282
170
 
283
171
  if (new_oid || !git_oid_iszero(&new_entry->oid))
284
- delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
172
+ delta->new_file.flags |= GIT_DIFF_FLAG_VALID_OID;
285
173
 
286
- if (git_vector_insert(&diff->deltas, delta) < 0) {
174
+ notify_res = diff_notify(diff, delta, matched_pathspec);
175
+
176
+ if (notify_res)
177
+ git__free(delta);
178
+ else if (git_vector_insert(&diff->deltas, delta) < 0) {
287
179
  git__free(delta);
288
180
  return -1;
289
181
  }
290
182
 
291
- return 0;
183
+ return notify_res < 0 ? GIT_EUSER : 0;
292
184
  }
293
185
 
294
186
  static git_diff_delta *diff_delta__last_for_item(
@@ -309,6 +201,11 @@ static git_diff_delta *diff_delta__last_for_item(
309
201
  if (git_oid_cmp(&delta->new_file.oid, &item->oid) == 0)
310
202
  return delta;
311
203
  break;
204
+ case GIT_DELTA_UNTRACKED:
205
+ if (diff->strcomp(delta->new_file.path, item->path) == 0 &&
206
+ git_oid_cmp(&delta->new_file.oid, &item->oid) == 0)
207
+ return delta;
208
+ break;
312
209
  case GIT_DELTA_MODIFIED:
313
210
  if (git_oid_cmp(&delta->old_file.oid, &item->oid) == 0 ||
314
211
  git_oid_cmp(&delta->new_file.oid, &item->oid) == 0)
@@ -332,13 +229,34 @@ static char *diff_strdup_prefix(git_pool *pool, const char *prefix)
332
229
  return git_pool_strndup(pool, prefix, len + 1);
333
230
  }
334
231
 
335
- static int diff_delta__cmp(const void *a, const void *b)
232
+ int git_diff_delta__cmp(const void *a, const void *b)
336
233
  {
337
234
  const git_diff_delta *da = a, *db = b;
338
235
  int val = strcmp(da->old_file.path, db->old_file.path);
339
236
  return val ? val : ((int)da->status - (int)db->status);
340
237
  }
341
238
 
239
+ bool git_diff_delta__should_skip(
240
+ const git_diff_options *opts, const git_diff_delta *delta)
241
+ {
242
+ uint32_t flags = opts ? opts->flags : 0;
243
+
244
+ if (delta->status == GIT_DELTA_UNMODIFIED &&
245
+ (flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
246
+ return true;
247
+
248
+ if (delta->status == GIT_DELTA_IGNORED &&
249
+ (flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
250
+ return true;
251
+
252
+ if (delta->status == GIT_DELTA_UNTRACKED &&
253
+ (flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
254
+ return true;
255
+
256
+ return false;
257
+ }
258
+
259
+
342
260
  static int config_bool(git_config *cfg, const char *name, int defvalue)
343
261
  {
344
262
  int val = defvalue;
@@ -353,7 +271,6 @@ static git_diff_list *git_diff_list_alloc(
353
271
  git_repository *repo, const git_diff_options *opts)
354
272
  {
355
273
  git_config *cfg;
356
- size_t i;
357
274
  git_diff_list *diff = git__calloc(1, sizeof(git_diff_list));
358
275
  if (diff == NULL)
359
276
  return NULL;
@@ -361,7 +278,7 @@ static git_diff_list *git_diff_list_alloc(
361
278
  GIT_REFCOUNT_INC(diff);
362
279
  diff->repo = repo;
363
280
 
364
- if (git_vector_init(&diff->deltas, 0, diff_delta__cmp) < 0 ||
281
+ if (git_vector_init(&diff->deltas, 0, git_diff_delta__cmp) < 0 ||
365
282
  git_pool_init(&diff->pool, 1, 0) < 0)
366
283
  goto fail;
367
284
 
@@ -378,11 +295,31 @@ static git_diff_list *git_diff_list_alloc(
378
295
  diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_CTIME;
379
296
  /* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */
380
297
 
381
- if (opts == NULL)
298
+ /* TODO: there are certain config settings where even if we were
299
+ * not given an options structure, we need the diff list to have one
300
+ * so that we can store the altered default values.
301
+ *
302
+ * - diff.ignoreSubmodules
303
+ * - diff.mnemonicprefix
304
+ * - diff.noprefix
305
+ */
306
+
307
+ if (opts == NULL) {
308
+ /* Make sure we default to 3 lines */
309
+ diff->opts.context_lines = 3;
382
310
  return diff;
311
+ }
383
312
 
384
313
  memcpy(&diff->opts, opts, sizeof(git_diff_options));
385
- memset(&diff->opts.pathspec, 0, sizeof(diff->opts.pathspec));
314
+
315
+ if(opts->flags & GIT_DIFF_IGNORE_FILEMODE)
316
+ diff->diffcaps = diff->diffcaps & ~GIT_DIFFCAPS_TRUST_MODE_BITS;
317
+
318
+ /* pathspec init will do nothing for empty pathspec */
319
+ if (git_pathspec_init(&diff->pathspec, &opts->pathspec, &diff->pool) < 0)
320
+ goto fail;
321
+
322
+ /* TODO: handle config diff.mnemonicprefix, diff.noprefix */
386
323
 
387
324
  diff->opts.old_prefix = diff_strdup_prefix(&diff->pool,
388
325
  opts->old_prefix ? opts->old_prefix : DIFF_OLD_PREFIX_DEFAULT);
@@ -392,45 +329,16 @@ static git_diff_list *git_diff_list_alloc(
392
329
  if (!diff->opts.old_prefix || !diff->opts.new_prefix)
393
330
  goto fail;
394
331
 
395
- if (diff->opts.flags & GIT_DIFF_REVERSE) {
396
- char *swap = diff->opts.old_prefix;
332
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
333
+ const char *swap = diff->opts.old_prefix;
397
334
  diff->opts.old_prefix = diff->opts.new_prefix;
398
335
  diff->opts.new_prefix = swap;
399
336
  }
400
337
 
401
338
  /* INCLUDE_TYPECHANGE_TREES implies INCLUDE_TYPECHANGE */
402
- if (diff->opts.flags & GIT_DIFF_INCLUDE_TYPECHANGE_TREES)
339
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES))
403
340
  diff->opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE;
404
341
 
405
- /* only copy pathspec if it is "interesting" so we can test
406
- * diff->pathspec.length > 0 to know if it is worth calling
407
- * fnmatch as we iterate.
408
- */
409
- if (!diff_pathspec_is_interesting(&opts->pathspec))
410
- return diff;
411
-
412
- if (git_vector_init(
413
- &diff->pathspec, (unsigned int)opts->pathspec.count, NULL) < 0)
414
- goto fail;
415
-
416
- for (i = 0; i < opts->pathspec.count; ++i) {
417
- int ret;
418
- const char *pattern = opts->pathspec.strings[i];
419
- git_attr_fnmatch *match = git__calloc(1, sizeof(git_attr_fnmatch));
420
- if (!match)
421
- goto fail;
422
- match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE;
423
- ret = git_attr_fnmatch__parse(match, &diff->pool, NULL, &pattern);
424
- if (ret == GIT_ENOTFOUND) {
425
- git__free(match);
426
- continue;
427
- } else if (ret < 0)
428
- goto fail;
429
-
430
- if (git_vector_insert(&diff->pathspec, match) < 0)
431
- goto fail;
432
- }
433
-
434
342
  return diff;
435
343
 
436
344
  fail:
@@ -441,7 +349,6 @@ fail:
441
349
  static void diff_list_free(git_diff_list *diff)
442
350
  {
443
351
  git_diff_delta *delta;
444
- git_attr_fnmatch *match;
445
352
  unsigned int i;
446
353
 
447
354
  git_vector_foreach(&diff->deltas, i, delta) {
@@ -450,12 +357,7 @@ static void diff_list_free(git_diff_list *diff)
450
357
  }
451
358
  git_vector_free(&diff->deltas);
452
359
 
453
- git_vector_foreach(&diff->pathspec, i, match) {
454
- git__free(match);
455
- diff->pathspec.contents[i] = NULL;
456
- }
457
- git_vector_free(&diff->pathspec);
458
-
360
+ git_pathspec_free(&diff->pathspec);
459
361
  git_pool_clear(&diff->pool);
460
362
  git__free(diff);
461
363
  }
@@ -473,25 +375,40 @@ void git_diff_list_addref(git_diff_list *diff)
473
375
  GIT_REFCOUNT_INC(diff);
474
376
  }
475
377
 
476
- static int oid_for_workdir_item(
378
+ int git_diff__oid_for_file(
477
379
  git_repository *repo,
478
- const git_index_entry *item,
380
+ const char *path,
381
+ uint16_t mode,
382
+ git_off_t size,
479
383
  git_oid *oid)
480
384
  {
481
385
  int result = 0;
482
386
  git_buf full_path = GIT_BUF_INIT;
483
387
 
484
388
  if (git_buf_joinpath(
485
- &full_path, git_repository_workdir(repo), item->path) < 0)
389
+ &full_path, git_repository_workdir(repo), path) < 0)
486
390
  return -1;
487
391
 
392
+ if (!mode) {
393
+ struct stat st;
394
+
395
+ if (p_stat(path, &st) < 0) {
396
+ giterr_set(GITERR_OS, "Could not stat '%s'", path);
397
+ result = -1;
398
+ goto cleanup;
399
+ }
400
+
401
+ mode = st.st_mode;
402
+ size = st.st_size;
403
+ }
404
+
488
405
  /* calculate OID for file if possible */
489
- if (S_ISGITLINK(item->mode)) {
406
+ if (S_ISGITLINK(mode)) {
490
407
  git_submodule *sm;
491
408
  const git_oid *sm_oid;
492
409
 
493
- if (!git_submodule_lookup(&sm, repo, item->path) &&
494
- (sm_oid = git_submodule_wd_oid(sm)) != NULL)
410
+ if (!git_submodule_lookup(&sm, repo, path) &&
411
+ (sm_oid = git_submodule_wd_id(sm)) != NULL)
495
412
  git_oid_cpy(oid, sm_oid);
496
413
  else {
497
414
  /* if submodule lookup failed probably just in an intermediate
@@ -500,23 +417,22 @@ static int oid_for_workdir_item(
500
417
  giterr_clear();
501
418
  memset(oid, 0, sizeof(*oid));
502
419
  }
503
- } else if (S_ISLNK(item->mode))
420
+ } else if (S_ISLNK(mode)) {
504
421
  result = git_odb__hashlink(oid, full_path.ptr);
505
- else if (!git__is_sizet(item->file_size)) {
506
- giterr_set(GITERR_OS, "File size overflow for 32-bit systems");
422
+ } else if (!git__is_sizet(size)) {
423
+ giterr_set(GITERR_OS, "File size overflow (for 32-bits) on '%s'", path);
507
424
  result = -1;
508
425
  } else {
509
426
  git_vector filters = GIT_VECTOR_INIT;
510
427
 
511
- result = git_filters_load(
512
- &filters, repo, item->path, GIT_FILTER_TO_ODB);
428
+ result = git_filters_load(&filters, repo, path, GIT_FILTER_TO_ODB);
513
429
  if (result >= 0) {
514
430
  int fd = git_futils_open_ro(full_path.ptr);
515
431
  if (fd < 0)
516
432
  result = fd;
517
433
  else {
518
434
  result = git_odb__hashfd_filtered(
519
- oid, fd, (size_t)item->file_size, GIT_OBJ_BLOB, &filters);
435
+ oid, fd, (size_t)size, GIT_OBJ_BLOB, &filters);
520
436
  p_close(fd);
521
437
  }
522
438
  }
@@ -524,8 +440,8 @@ static int oid_for_workdir_item(
524
440
  git_filters_free(&filters);
525
441
  }
526
442
 
443
+ cleanup:
527
444
  git_buf_free(&full_path);
528
-
529
445
  return result;
530
446
  }
531
447
 
@@ -542,11 +458,16 @@ static int maybe_modified(
542
458
  git_delta_t status = GIT_DELTA_MODIFIED;
543
459
  unsigned int omode = oitem->mode;
544
460
  unsigned int nmode = nitem->mode;
545
- bool new_is_workdir = (new_iter->type == GIT_ITERATOR_WORKDIR);
461
+ bool new_is_workdir = (new_iter->type == GIT_ITERATOR_TYPE_WORKDIR);
462
+ const char *matched_pathspec;
546
463
 
547
464
  GIT_UNUSED(old_iter);
548
465
 
549
- if (!diff_path_matches_pathspec(diff, oitem->path))
466
+ if (!git_pathspec_match_path(
467
+ &diff->pathspec, oitem->path,
468
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH),
469
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE),
470
+ &matched_pathspec))
550
471
  return 0;
551
472
 
552
473
  /* on platforms with no symlinks, preserve mode of existing symlinks */
@@ -571,7 +492,7 @@ static int maybe_modified(
571
492
 
572
493
  /* if basic type of file changed, then split into delete and add */
573
494
  else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) {
574
- if ((diff->opts.flags & GIT_DIFF_INCLUDE_TYPECHANGE) != 0)
495
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE))
575
496
  status = GIT_DELTA_TYPECHANGE;
576
497
  else {
577
498
  if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 ||
@@ -582,8 +503,7 @@ static int maybe_modified(
582
503
  }
583
504
 
584
505
  /* if oids and modes match, then file is unmodified */
585
- else if (git_oid_cmp(&oitem->oid, &nitem->oid) == 0 &&
586
- omode == nmode)
506
+ else if (git_oid_equal(&oitem->oid, &nitem->oid) && omode == nmode)
587
507
  status = GIT_DELTA_UNMODIFIED;
588
508
 
589
509
  /* if we have an unknown OID and a workdir iterator, then check some
@@ -606,24 +526,32 @@ static int maybe_modified(
606
526
  status = GIT_DELTA_UNMODIFIED;
607
527
 
608
528
  else if (S_ISGITLINK(nmode)) {
529
+ int err;
609
530
  git_submodule *sub;
610
531
 
611
- if ((diff->opts.flags & GIT_DIFF_IGNORE_SUBMODULES) != 0)
532
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES))
612
533
  status = GIT_DELTA_UNMODIFIED;
613
- else if (git_submodule_lookup(&sub, diff->repo, nitem->path) < 0)
614
- return -1;
615
- else if (git_submodule_ignore(sub) == GIT_SUBMODULE_IGNORE_ALL)
534
+ else if ((err = git_submodule_lookup(&sub, diff->repo, nitem->path)) < 0) {
535
+ if (err == GIT_EEXISTS)
536
+ status = GIT_DELTA_UNMODIFIED;
537
+ else
538
+ return err;
539
+ } else if (git_submodule_ignore(sub) == GIT_SUBMODULE_IGNORE_ALL)
616
540
  status = GIT_DELTA_UNMODIFIED;
617
541
  else {
618
542
  unsigned int sm_status = 0;
619
543
  if (git_submodule_status(&sm_status, sub) < 0)
620
544
  return -1;
621
- status = GIT_SUBMODULE_STATUS_IS_UNMODIFIED(sm_status)
545
+
546
+ /* check IS_WD_UNMODIFIED because this case is only used
547
+ * when the new side of the diff is the working directory
548
+ */
549
+ status = GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(sm_status)
622
550
  ? GIT_DELTA_UNMODIFIED : GIT_DELTA_MODIFIED;
623
551
 
624
552
  /* grab OID while we are here */
625
553
  if (git_oid_iszero(&nitem->oid)) {
626
- const git_oid *sm_oid = git_submodule_wd_oid(sub);
554
+ const git_oid *sm_oid = git_submodule_wd_id(sub);
627
555
  if (sm_oid != NULL) {
628
556
  git_oid_cpy(&noid, sm_oid);
629
557
  use_noid = &noid;
@@ -633,114 +561,124 @@ static int maybe_modified(
633
561
  }
634
562
  }
635
563
 
564
+ /* if mode is GITLINK and submodules are ignored, then skip */
565
+ else if (S_ISGITLINK(nmode) &&
566
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES))
567
+ status = GIT_DELTA_UNMODIFIED;
568
+
636
569
  /* if we got here and decided that the files are modified, but we
637
570
  * haven't calculated the OID of the new item, then calculate it now
638
571
  */
639
572
  if (status != GIT_DELTA_UNMODIFIED && git_oid_iszero(&nitem->oid)) {
640
- if (oid_for_workdir_item(diff->repo, nitem, &noid) < 0)
641
- return -1;
642
- else if (omode == nmode && git_oid_equal(&oitem->oid, &noid))
643
- status = GIT_DELTA_UNMODIFIED;
573
+ if (!use_noid) {
574
+ if (git_diff__oid_for_file(diff->repo,
575
+ nitem->path, nitem->mode, nitem->file_size, &noid) < 0)
576
+ return -1;
577
+ use_noid = &noid;
578
+ }
644
579
 
645
- /* store calculated oid so we don't have to recalc later */
646
- use_noid = &noid;
580
+ /* if oid matches, then mark unmodified (except submodules, where
581
+ * the filesystem content may be modified even if the oid still
582
+ * matches between the index and the workdir HEAD)
583
+ */
584
+ if (omode == nmode && !S_ISGITLINK(omode) &&
585
+ git_oid_equal(&oitem->oid, use_noid))
586
+ status = GIT_DELTA_UNMODIFIED;
647
587
  }
648
588
 
649
589
  return diff_delta__from_two(
650
- diff, status, oitem, omode, nitem, nmode, use_noid);
651
- }
652
-
653
- static int git_index_entry_cmp_case(const void *a, const void *b)
654
- {
655
- const git_index_entry *entry_a = a;
656
- const git_index_entry *entry_b = b;
657
-
658
- return strcmp(entry_a->path, entry_b->path);
659
- }
660
-
661
- static int git_index_entry_cmp_icase(const void *a, const void *b)
662
- {
663
- const git_index_entry *entry_a = a;
664
- const git_index_entry *entry_b = b;
665
-
666
- return strcasecmp(entry_a->path, entry_b->path);
590
+ diff, status, oitem, omode, nitem, nmode, use_noid, matched_pathspec);
667
591
  }
668
592
 
669
593
  static bool entry_is_prefixed(
594
+ git_diff_list *diff,
670
595
  const git_index_entry *item,
671
- git_iterator *prefix_iterator,
672
596
  const git_index_entry *prefix_item)
673
597
  {
674
598
  size_t pathlen;
675
599
 
676
- if (!prefix_item ||
677
- ITERATOR_PREFIXCMP(*prefix_iterator, prefix_item->path, item->path))
600
+ if (!item || diff->pfxcomp(item->path, prefix_item->path) != 0)
678
601
  return false;
679
602
 
680
- pathlen = strlen(item->path);
603
+ pathlen = strlen(prefix_item->path);
681
604
 
682
- return (item->path[pathlen - 1] == '/' ||
683
- prefix_item->path[pathlen] == '\0' ||
684
- prefix_item->path[pathlen] == '/');
605
+ return (prefix_item->path[pathlen - 1] == '/' ||
606
+ item->path[pathlen] == '\0' ||
607
+ item->path[pathlen] == '/');
685
608
  }
686
609
 
687
- static int diff_from_iterators(
688
- git_repository *repo,
689
- const git_diff_options *opts, /**< can be NULL for defaults */
610
+ static int diff_list_init_from_iterators(
611
+ git_diff_list *diff,
690
612
  git_iterator *old_iter,
691
- git_iterator *new_iter,
692
- git_diff_list **diff_ptr)
613
+ git_iterator *new_iter)
693
614
  {
694
- const git_index_entry *oitem, *nitem;
695
- git_buf ignore_prefix = GIT_BUF_INIT;
696
- git_diff_list *diff = git_diff_list_alloc(repo, opts);
697
- git_vector_cmp entry_compare;
698
-
699
- if (!diff)
700
- goto fail;
701
-
702
615
  diff->old_src = old_iter->type;
703
616
  diff->new_src = new_iter->type;
704
617
 
705
618
  /* Use case-insensitive compare if either iterator has
706
619
  * the ignore_case bit set */
707
- if (!old_iter->ignore_case && !new_iter->ignore_case) {
708
- entry_compare = git_index_entry_cmp_case;
620
+ if (!git_iterator_ignore_case(old_iter) &&
621
+ !git_iterator_ignore_case(new_iter))
622
+ {
709
623
  diff->opts.flags &= ~GIT_DIFF_DELTAS_ARE_ICASE;
624
+
625
+ diff->strcomp = git__strcmp;
626
+ diff->strncomp = git__strncmp;
627
+ diff->pfxcomp = git__prefixcmp;
628
+ diff->entrycomp = git_index_entry__cmp;
710
629
  } else {
711
- entry_compare = git_index_entry_cmp_icase;
712
630
  diff->opts.flags |= GIT_DIFF_DELTAS_ARE_ICASE;
713
631
 
714
- /* If one of the iterators doesn't have ignore_case set,
715
- * then that's unfortunate because we'll have to spool
716
- * its data, sort it icase, and then use that for our
717
- * merge join to the other iterator that is icase sorted */
718
- if (!old_iter->ignore_case) {
719
- if (git_iterator_spoolandsort(&old_iter, old_iter, git_index_entry_cmp_icase, true) < 0)
720
- goto fail;
721
- } else if (!new_iter->ignore_case) {
722
- if (git_iterator_spoolandsort(&new_iter, new_iter, git_index_entry_cmp_icase, true) < 0)
723
- goto fail;
724
- }
632
+ diff->strcomp = git__strcasecmp;
633
+ diff->strncomp = git__strncasecmp;
634
+ diff->pfxcomp = git__prefixcmp_icase;
635
+ diff->entrycomp = git_index_entry__cmp_icase;
636
+ }
637
+
638
+ return 0;
639
+ }
640
+
641
+ int git_diff__from_iterators(
642
+ git_diff_list **diff_ptr,
643
+ git_repository *repo,
644
+ git_iterator *old_iter,
645
+ git_iterator *new_iter,
646
+ const git_diff_options *opts)
647
+ {
648
+ int error = 0;
649
+ const git_index_entry *oitem, *nitem;
650
+ git_buf ignore_prefix = GIT_BUF_INIT;
651
+ git_diff_list *diff = git_diff_list_alloc(repo, opts);
652
+
653
+ *diff_ptr = NULL;
654
+
655
+ if (!diff || diff_list_init_from_iterators(diff, old_iter, new_iter) < 0)
656
+ goto fail;
657
+
658
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE)) {
659
+ if (git_iterator_set_ignore_case(old_iter, true) < 0 ||
660
+ git_iterator_set_ignore_case(new_iter, true) < 0)
661
+ goto fail;
725
662
  }
726
663
 
727
- if (git_iterator_current(old_iter, &oitem) < 0 ||
728
- git_iterator_current(new_iter, &nitem) < 0)
664
+ if (git_iterator_current(&oitem, old_iter) < 0 ||
665
+ git_iterator_current(&nitem, new_iter) < 0)
729
666
  goto fail;
730
667
 
731
668
  /* run iterators building diffs */
732
669
  while (oitem || nitem) {
670
+ int cmp = oitem ? (nitem ? diff->entrycomp(oitem, nitem) : -1) : 1;
733
671
 
734
672
  /* create DELETED records for old items not matched in new */
735
- if (oitem && (!nitem || entry_compare(oitem, nitem) < 0)) {
673
+ if (cmp < 0) {
736
674
  if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0)
737
675
  goto fail;
738
676
 
739
677
  /* if we are generating TYPECHANGE records then check for that
740
678
  * instead of just generating a DELETE record
741
679
  */
742
- if ((diff->opts.flags & GIT_DIFF_INCLUDE_TYPECHANGE_TREES) != 0 &&
743
- entry_is_prefixed(oitem, new_iter, nitem))
680
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) &&
681
+ entry_is_prefixed(diff, nitem, oitem))
744
682
  {
745
683
  /* this entry has become a tree! convert to TYPECHANGE */
746
684
  git_diff_delta *last = diff_delta__last_for_item(diff, oitem);
@@ -748,22 +686,33 @@ static int diff_from_iterators(
748
686
  last->status = GIT_DELTA_TYPECHANGE;
749
687
  last->new_file.mode = GIT_FILEMODE_TREE;
750
688
  }
689
+
690
+ /* If new_iter is a workdir iterator, then this situation
691
+ * will certainly be followed by a series of untracked items.
692
+ * Unless RECURSE_UNTRACKED_DIRS is set, skip over them...
693
+ */
694
+ if (S_ISDIR(nitem->mode) &&
695
+ DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS))
696
+ {
697
+ if (git_iterator_advance(&nitem, new_iter) < 0)
698
+ goto fail;
699
+ }
751
700
  }
752
701
 
753
- if (git_iterator_advance(old_iter, &oitem) < 0)
702
+ if (git_iterator_advance(&oitem, old_iter) < 0)
754
703
  goto fail;
755
704
  }
756
705
 
757
706
  /* create ADDED, TRACKED, or IGNORED records for new items not
758
707
  * matched in old (and/or descend into directories as needed)
759
708
  */
760
- else if (nitem && (!oitem || entry_compare(oitem, nitem) > 0)) {
709
+ else if (cmp > 0) {
761
710
  git_delta_t delta_type = GIT_DELTA_UNTRACKED;
711
+ bool contains_oitem = entry_is_prefixed(diff, oitem, nitem);
762
712
 
763
713
  /* check if contained in ignored parent directory */
764
714
  if (git_buf_len(&ignore_prefix) &&
765
- ITERATOR_PREFIXCMP(*old_iter, nitem->path,
766
- git_buf_cstr(&ignore_prefix)) == 0)
715
+ diff->pfxcomp(nitem->path, git_buf_cstr(&ignore_prefix)) == 0)
767
716
  delta_type = GIT_DELTA_IGNORED;
768
717
 
769
718
  if (S_ISDIR(nitem->mode)) {
@@ -771,32 +720,48 @@ static int diff_from_iterators(
771
720
  * it or if the user requested the contents of untracked
772
721
  * directories and it is not under an ignored directory.
773
722
  */
774
- bool contains_tracked =
775
- entry_is_prefixed(nitem, old_iter, oitem);
776
- bool recurse_untracked =
723
+ bool recurse_into_dir =
777
724
  (delta_type == GIT_DELTA_UNTRACKED &&
778
- (diff->opts.flags & GIT_DIFF_RECURSE_UNTRACKED_DIRS) != 0);
725
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS)) ||
726
+ (delta_type == GIT_DELTA_IGNORED &&
727
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS));
779
728
 
780
729
  /* do not advance into directories that contain a .git file */
781
- if (!contains_tracked && recurse_untracked) {
730
+ if (!contains_oitem && recurse_into_dir) {
782
731
  git_buf *full = NULL;
783
- if (git_iterator_current_workdir_path(new_iter, &full) < 0)
732
+ if (git_iterator_current_workdir_path(&full, new_iter) < 0)
784
733
  goto fail;
785
734
  if (git_path_contains_dir(full, DOT_GIT))
786
- recurse_untracked = false;
735
+ recurse_into_dir = false;
787
736
  }
788
737
 
789
- if (contains_tracked || recurse_untracked) {
790
- /* if this directory is ignored, remember it as the
791
- * "ignore_prefix" for processing contained items
792
- */
793
- if (delta_type == GIT_DELTA_UNTRACKED &&
794
- git_iterator_current_is_ignored(new_iter))
795
- git_buf_sets(&ignore_prefix, nitem->path);
738
+ /* if directory is ignored, remember ignore_prefix */
739
+ if ((contains_oitem || recurse_into_dir) &&
740
+ delta_type == GIT_DELTA_UNTRACKED &&
741
+ git_iterator_current_is_ignored(new_iter))
742
+ {
743
+ git_buf_sets(&ignore_prefix, nitem->path);
744
+ delta_type = GIT_DELTA_IGNORED;
745
+
746
+ /* skip recursion if we've just learned this is ignored */
747
+ if (DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS))
748
+ recurse_into_dir = false;
749
+ }
796
750
 
797
- if (git_iterator_advance_into_directory(new_iter, &nitem) < 0)
798
- goto fail;
751
+ if (contains_oitem || recurse_into_dir) {
752
+ /* advance into directory */
753
+ error = git_iterator_advance_into(&nitem, new_iter);
799
754
 
755
+ /* if directory is empty, can't advance into it, so skip */
756
+ if (error == GIT_ENOTFOUND) {
757
+ giterr_clear();
758
+ error = git_iterator_advance(&nitem, new_iter);
759
+
760
+ git_buf_clear(&ignore_prefix);
761
+ }
762
+
763
+ if (error < 0)
764
+ goto fail;
800
765
  continue;
801
766
  }
802
767
  }
@@ -816,8 +781,9 @@ static int diff_from_iterators(
816
781
  * checked before container directory exclusions are used to
817
782
  * skip the file.
818
783
  */
819
- else if (delta_type == GIT_DELTA_IGNORED) {
820
- if (git_iterator_advance(new_iter, &nitem) < 0)
784
+ else if (delta_type == GIT_DELTA_IGNORED &&
785
+ DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS)) {
786
+ if (git_iterator_advance(&nitem, new_iter) < 0)
821
787
  goto fail;
822
788
  continue; /* ignored parent directory, so skip completely */
823
789
  }
@@ -825,28 +791,28 @@ static int diff_from_iterators(
825
791
  else if (git_iterator_current_is_ignored(new_iter))
826
792
  delta_type = GIT_DELTA_IGNORED;
827
793
 
828
- else if (new_iter->type != GIT_ITERATOR_WORKDIR)
794
+ else if (new_iter->type != GIT_ITERATOR_TYPE_WORKDIR)
829
795
  delta_type = GIT_DELTA_ADDED;
830
796
 
831
797
  if (diff_delta__from_one(diff, delta_type, nitem) < 0)
832
798
  goto fail;
833
799
 
834
800
  /* if we are generating TYPECHANGE records then check for that
835
- * instead of just generating an ADD/UNTRACKED record
801
+ * instead of just generating an ADDED/UNTRACKED record
836
802
  */
837
803
  if (delta_type != GIT_DELTA_IGNORED &&
838
- (diff->opts.flags & GIT_DIFF_INCLUDE_TYPECHANGE_TREES) != 0 &&
839
- entry_is_prefixed(nitem, old_iter, oitem))
804
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) &&
805
+ contains_oitem)
840
806
  {
841
- /* this entry was a tree! convert to TYPECHANGE */
842
- git_diff_delta *last = diff_delta__last_for_item(diff, oitem);
807
+ /* this entry was prefixed with a tree - make TYPECHANGE */
808
+ git_diff_delta *last = diff_delta__last_for_item(diff, nitem);
843
809
  if (last) {
844
810
  last->status = GIT_DELTA_TYPECHANGE;
845
811
  last->old_file.mode = GIT_FILEMODE_TREE;
846
812
  }
847
813
  }
848
814
 
849
- if (git_iterator_advance(new_iter, &nitem) < 0)
815
+ if (git_iterator_advance(&nitem, new_iter) < 0)
850
816
  goto fail;
851
817
  }
852
818
 
@@ -854,231 +820,116 @@ static int diff_from_iterators(
854
820
  * (or ADDED and DELETED pair if type changed)
855
821
  */
856
822
  else {
857
- assert(oitem && nitem && entry_compare(oitem, nitem) == 0);
823
+ assert(oitem && nitem && cmp == 0);
858
824
 
859
825
  if (maybe_modified(old_iter, oitem, new_iter, nitem, diff) < 0 ||
860
- git_iterator_advance(old_iter, &oitem) < 0 ||
861
- git_iterator_advance(new_iter, &nitem) < 0)
826
+ git_iterator_advance(&oitem, old_iter) < 0 ||
827
+ git_iterator_advance(&nitem, new_iter) < 0)
862
828
  goto fail;
863
829
  }
864
830
  }
865
831
 
866
- git_iterator_free(old_iter);
867
- git_iterator_free(new_iter);
868
- git_buf_free(&ignore_prefix);
869
-
870
832
  *diff_ptr = diff;
871
- return 0;
872
833
 
873
834
  fail:
874
- git_iterator_free(old_iter);
875
- git_iterator_free(new_iter);
835
+ if (!*diff_ptr) {
836
+ git_diff_list_free(diff);
837
+ error = -1;
838
+ }
839
+
876
840
  git_buf_free(&ignore_prefix);
877
841
 
878
- git_diff_list_free(diff);
879
- *diff_ptr = NULL;
880
- return -1;
842
+ return error;
881
843
  }
882
844
 
845
+ #define DIFF_FROM_ITERATORS(MAKE_FIRST, MAKE_SECOND) do { \
846
+ git_iterator *a = NULL, *b = NULL; \
847
+ char *pfx = opts ? git_pathspec_prefix(&opts->pathspec) : NULL; \
848
+ GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); \
849
+ if (!(error = MAKE_FIRST) && !(error = MAKE_SECOND)) \
850
+ error = git_diff__from_iterators(diff, repo, a, b, opts); \
851
+ git__free(pfx); git_iterator_free(a); git_iterator_free(b); \
852
+ } while (0)
883
853
 
884
854
  int git_diff_tree_to_tree(
855
+ git_diff_list **diff,
885
856
  git_repository *repo,
886
- const git_diff_options *opts, /**< can be NULL for defaults */
887
857
  git_tree *old_tree,
888
858
  git_tree *new_tree,
889
- git_diff_list **diff)
859
+ const git_diff_options *opts)
890
860
  {
891
- git_iterator *a = NULL, *b = NULL;
892
- char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
861
+ int error = 0;
893
862
 
894
- assert(repo && old_tree && new_tree && diff);
863
+ assert(diff && repo);
895
864
 
896
- if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||
897
- git_iterator_for_tree_range(&b, repo, new_tree, prefix, prefix) < 0)
898
- return -1;
865
+ DIFF_FROM_ITERATORS(
866
+ git_iterator_for_tree(&a, old_tree, 0, pfx, pfx),
867
+ git_iterator_for_tree(&b, new_tree, 0, pfx, pfx)
868
+ );
899
869
 
900
- git__free(prefix);
901
-
902
- return diff_from_iterators(repo, opts, a, b, diff);
870
+ return error;
903
871
  }
904
872
 
905
- int git_diff_index_to_tree(
873
+ int git_diff_tree_to_index(
874
+ git_diff_list **diff,
906
875
  git_repository *repo,
907
- const git_diff_options *opts,
908
876
  git_tree *old_tree,
909
- git_diff_list **diff)
877
+ git_index *index,
878
+ const git_diff_options *opts)
910
879
  {
911
- git_iterator *a = NULL, *b = NULL;
912
- char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
913
-
914
- assert(repo && diff);
880
+ int error = 0;
915
881
 
916
- if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||
917
- git_iterator_for_index_range(&b, repo, prefix, prefix) < 0)
918
- goto on_error;
882
+ assert(diff && repo);
919
883
 
920
- git__free(prefix);
884
+ if (!index && (error = git_repository_index__weakptr(&index, repo)) < 0)
885
+ return error;
921
886
 
922
- return diff_from_iterators(repo, opts, a, b, diff);
887
+ DIFF_FROM_ITERATORS(
888
+ git_iterator_for_tree(&a, old_tree, 0, pfx, pfx),
889
+ git_iterator_for_index(&b, index, 0, pfx, pfx)
890
+ );
923
891
 
924
- on_error:
925
- git__free(prefix);
926
- git_iterator_free(a);
927
- return -1;
892
+ return error;
928
893
  }
929
894
 
930
- int git_diff_workdir_to_index(
895
+ int git_diff_index_to_workdir(
896
+ git_diff_list **diff,
931
897
  git_repository *repo,
932
- const git_diff_options *opts,
933
- git_diff_list **diff)
898
+ git_index *index,
899
+ const git_diff_options *opts)
934
900
  {
935
- git_iterator *a = NULL, *b = NULL;
936
- int error;
937
-
938
- char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
939
-
940
- assert(repo && diff);
901
+ int error = 0;
941
902
 
942
- if ((error = git_iterator_for_index_range(&a, repo, prefix, prefix)) < 0 ||
943
- (error = git_iterator_for_workdir_range(&b, repo, prefix, prefix)) < 0)
944
- goto on_error;
903
+ assert(diff && repo);
945
904
 
946
- git__free(prefix);
905
+ if (!index && (error = git_repository_index__weakptr(&index, repo)) < 0)
906
+ return error;
947
907
 
948
- return diff_from_iterators(repo, opts, a, b, diff);
908
+ DIFF_FROM_ITERATORS(
909
+ git_iterator_for_index(&a, index, 0, pfx, pfx),
910
+ git_iterator_for_workdir(
911
+ &b, repo, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx)
912
+ );
949
913
 
950
- on_error:
951
- git__free(prefix);
952
- git_iterator_free(a);
953
914
  return error;
954
915
  }
955
916
 
956
917
 
957
- int git_diff_workdir_to_tree(
918
+ int git_diff_tree_to_workdir(
919
+ git_diff_list **diff,
958
920
  git_repository *repo,
959
- const git_diff_options *opts,
960
921
  git_tree *old_tree,
961
- git_diff_list **diff)
962
- {
963
- git_iterator *a = NULL, *b = NULL;
964
- int error;
965
-
966
- char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
967
-
968
- assert(repo && old_tree && diff);
969
-
970
- if ((error = git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix)) < 0 ||
971
- (error = git_iterator_for_workdir_range(&b, repo, prefix, prefix)) < 0)
972
- goto on_error;
973
-
974
- git__free(prefix);
975
-
976
- return diff_from_iterators(repo, opts, a, b, diff);
977
-
978
- on_error:
979
- git__free(prefix);
980
- git_iterator_free(a);
981
- return error;
982
- }
983
-
984
-
985
- bool git_diff_delta__should_skip(
986
- const git_diff_options *opts, const git_diff_delta *delta)
987
- {
988
- uint32_t flags = opts ? opts->flags : 0;
989
-
990
- if (delta->status == GIT_DELTA_UNMODIFIED &&
991
- (flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
992
- return true;
993
-
994
- if (delta->status == GIT_DELTA_IGNORED &&
995
- (flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
996
- return true;
997
-
998
- if (delta->status == GIT_DELTA_UNTRACKED &&
999
- (flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
1000
- return true;
1001
-
1002
- return false;
1003
- }
1004
-
1005
-
1006
- int git_diff_merge(
1007
- git_diff_list *onto,
1008
- const git_diff_list *from)
922
+ const git_diff_options *opts)
1009
923
  {
1010
924
  int error = 0;
1011
- git_pool onto_pool;
1012
- git_vector onto_new;
1013
- git_diff_delta *delta;
1014
- bool ignore_case = false;
1015
- unsigned int i, j;
1016
-
1017
- assert(onto && from);
1018
-
1019
- if (!from->deltas.length)
1020
- return 0;
1021
-
1022
- if (git_vector_init(&onto_new, onto->deltas.length, diff_delta__cmp) < 0 ||
1023
- git_pool_init(&onto_pool, 1, 0) < 0)
1024
- return -1;
1025
925
 
1026
- if ((onto->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0 ||
1027
- (from->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0)
1028
- {
1029
- ignore_case = true;
1030
-
1031
- /* This function currently only supports merging diff lists that
1032
- * are sorted identically. */
1033
- assert((onto->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0 &&
1034
- (from->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0);
1035
- }
1036
-
1037
- for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) {
1038
- git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i);
1039
- const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j);
1040
- int cmp = !f ? -1 : !o ? 1 : STRCMP_CASESELECT(ignore_case, o->old_file.path, f->old_file.path);
1041
-
1042
- if (cmp < 0) {
1043
- delta = diff_delta__dup(o, &onto_pool);
1044
- i++;
1045
- } else if (cmp > 0) {
1046
- delta = diff_delta__dup(f, &onto_pool);
1047
- j++;
1048
- } else {
1049
- delta = diff_delta__merge_like_cgit(o, f, &onto_pool);
1050
- i++;
1051
- j++;
1052
- }
1053
-
1054
- /* the ignore rules for the target may not match the source
1055
- * or the result of a merged delta could be skippable...
1056
- */
1057
- if (git_diff_delta__should_skip(&onto->opts, delta)) {
1058
- git__free(delta);
1059
- continue;
1060
- }
1061
-
1062
- if ((error = !delta ? -1 : git_vector_insert(&onto_new, delta)) < 0)
1063
- break;
1064
- }
926
+ assert(diff && repo);
1065
927
 
1066
- if (!error) {
1067
- git_vector_swap(&onto->deltas, &onto_new);
1068
- git_pool_swap(&onto->pool, &onto_pool);
1069
- onto->new_src = from->new_src;
1070
-
1071
- /* prefix strings also come from old pool, so recreate those.*/
1072
- onto->opts.old_prefix =
1073
- git_pool_strdup_safe(&onto->pool, onto->opts.old_prefix);
1074
- onto->opts.new_prefix =
1075
- git_pool_strdup_safe(&onto->pool, onto->opts.new_prefix);
1076
- }
1077
-
1078
- git_vector_foreach(&onto_new, i, delta)
1079
- git__free(delta);
1080
- git_vector_free(&onto_new);
1081
- git_pool_clear(&onto_pool);
928
+ DIFF_FROM_ITERATORS(
929
+ git_iterator_for_tree(&a, old_tree, 0, pfx, pfx),
930
+ git_iterator_for_workdir(
931
+ &b, repo, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx)
932
+ );
1082
933
 
1083
934
  return error;
1084
935
  }