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
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (C) 2009-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.
@@ -31,7 +31,8 @@ struct git_filebuf {
31
31
 
32
32
  int (*write)(struct git_filebuf *file, void *source, size_t len);
33
33
 
34
- git_hash_ctx *digest;
34
+ bool compute_digest;
35
+ git_hash_ctx digest;
35
36
 
36
37
  unsigned char *buffer;
37
38
  unsigned char *z_buf;
@@ -82,5 +83,6 @@ int git_filebuf_commit_at(git_filebuf *lock, const char *path, mode_t mode);
82
83
  void git_filebuf_cleanup(git_filebuf *lock);
83
84
  int git_filebuf_hash(git_oid *oid, git_filebuf *file);
84
85
  int git_filebuf_flush(git_filebuf *file);
86
+ int git_filebuf_stats(time_t *mtime, size_t *size, git_filebuf *file);
85
87
 
86
88
  #endif
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (C) 2009-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.
@@ -14,7 +14,8 @@
14
14
  int git_futils_mkpath2file(const char *file_path, const mode_t mode)
15
15
  {
16
16
  return git_futils_mkdir(
17
- file_path, NULL, mode, GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST);
17
+ file_path, NULL, mode,
18
+ GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR);
18
19
  }
19
20
 
20
21
  int git_futils_mktmp(git_buf *path_out, const char *filename)
@@ -120,7 +121,7 @@ mode_t git_futils_canonical_mode(mode_t raw_mode)
120
121
 
121
122
  int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len)
122
123
  {
123
- ssize_t read_size;
124
+ ssize_t read_size = 0;
124
125
 
125
126
  git_buf_clear(buf);
126
127
 
@@ -142,10 +143,11 @@ int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len)
142
143
  }
143
144
 
144
145
  int git_futils_readbuffer_updated(
145
- git_buf *buf, const char *path, time_t *mtime, int *updated)
146
+ git_buf *buf, const char *path, time_t *mtime, size_t *size, int *updated)
146
147
  {
147
148
  git_file fd;
148
149
  struct stat st;
150
+ bool changed = false;
149
151
 
150
152
  assert(buf && path && *path);
151
153
 
@@ -162,16 +164,25 @@ int git_futils_readbuffer_updated(
162
164
  }
163
165
 
164
166
  /*
165
- * If we were given a time, we only want to read the file if it
166
- * has been modified.
167
+ * If we were given a time and/or a size, we only want to read the file
168
+ * if it has been modified.
167
169
  */
168
- if (mtime != NULL && *mtime >= st.st_mtime) {
170
+ if (size && *size != (size_t)st.st_size)
171
+ changed = true;
172
+ if (mtime && *mtime != st.st_mtime)
173
+ changed = true;
174
+ if (!size && !mtime)
175
+ changed = true;
176
+
177
+ if (!changed) {
169
178
  p_close(fd);
170
179
  return 0;
171
180
  }
172
181
 
173
182
  if (mtime != NULL)
174
183
  *mtime = st.st_mtime;
184
+ if (size != NULL)
185
+ *size = (size_t)st.st_size;
175
186
 
176
187
  if (git_futils_readbuffer_fd(buf, fd, (size_t)st.st_size) < 0) {
177
188
  p_close(fd);
@@ -188,7 +199,7 @@ int git_futils_readbuffer_updated(
188
199
 
189
200
  int git_futils_readbuffer(git_buf *buf, const char *path)
190
201
  {
191
- return git_futils_readbuffer_updated(buf, path, NULL, NULL);
202
+ return git_futils_readbuffer_updated(buf, path, NULL, NULL, NULL);
192
203
  }
193
204
 
194
205
  int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode)
@@ -240,6 +251,7 @@ int git_futils_mkdir(
240
251
  mode_t mode,
241
252
  uint32_t flags)
242
253
  {
254
+ int error = -1;
243
255
  git_buf make_path = GIT_BUF_INIT;
244
256
  ssize_t root = 0;
245
257
  char lastch, *tail;
@@ -260,6 +272,10 @@ int git_futils_mkdir(
260
272
  }
261
273
 
262
274
  /* if we are not supposed to made the last element, truncate it */
275
+ if ((flags & GIT_MKDIR_SKIP_LAST2) != 0) {
276
+ git_buf_rtruncate_at_char(&make_path, '/');
277
+ flags |= GIT_MKDIR_SKIP_LAST;
278
+ }
263
279
  if ((flags & GIT_MKDIR_SKIP_LAST) != 0)
264
280
  git_buf_rtruncate_at_char(&make_path, '/');
265
281
 
@@ -287,12 +303,46 @@ int git_futils_mkdir(
287
303
  *tail = '\0';
288
304
 
289
305
  /* make directory */
290
- if (p_mkdir(make_path.ptr, mode) < 0 &&
291
- (errno != EEXIST || (flags & GIT_MKDIR_EXCL) != 0))
292
- {
293
- giterr_set(GITERR_OS, "Failed to make directory '%s'",
294
- make_path.ptr);
295
- goto fail;
306
+ if (p_mkdir(make_path.ptr, mode) < 0) {
307
+ int already_exists = 0;
308
+
309
+ switch (errno) {
310
+ case EEXIST:
311
+ if (!lastch && (flags & GIT_MKDIR_VERIFY_DIR) != 0 &&
312
+ !git_path_isdir(make_path.ptr)) {
313
+ giterr_set(
314
+ GITERR_OS, "Existing path is not a directory '%s'",
315
+ make_path.ptr);
316
+ error = GIT_ENOTFOUND;
317
+ goto fail;
318
+ }
319
+
320
+ already_exists = 1;
321
+ break;
322
+ case ENOSYS:
323
+ /* Solaris can generate this error if you try to mkdir
324
+ * a path which is already a mount point. In that case,
325
+ * the path does already exist; but it's not implied by
326
+ * the definition of the error, so let's recheck */
327
+ if (git_path_isdir(make_path.ptr)) {
328
+ already_exists = 1;
329
+ break;
330
+ }
331
+
332
+ /* Fall through */
333
+ errno = ENOSYS;
334
+ default:
335
+ giterr_set(GITERR_OS, "Failed to make directory '%s'",
336
+ make_path.ptr);
337
+ goto fail;
338
+ }
339
+
340
+ if (already_exists && (flags & GIT_MKDIR_EXCL) != 0) {
341
+ giterr_set(GITERR_OS, "Directory already exists '%s'",
342
+ make_path.ptr);
343
+ error = GIT_EEXISTS;
344
+ goto fail;
345
+ }
296
346
  }
297
347
 
298
348
  /* chmod if requested */
@@ -314,7 +364,7 @@ int git_futils_mkdir(
314
364
 
315
365
  fail:
316
366
  git_buf_free(&make_path);
317
- return -1;
367
+ return error;
318
368
  }
319
369
 
320
370
  int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
@@ -322,138 +372,372 @@ int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
322
372
  return git_futils_mkdir(path, base, mode, GIT_MKDIR_PATH);
323
373
  }
324
374
 
325
- static int _rmdir_recurs_foreach(void *opaque, git_buf *path)
375
+ typedef struct {
376
+ const char *base;
377
+ size_t baselen;
378
+ uint32_t flags;
379
+ int error;
380
+ } futils__rmdir_data;
381
+
382
+ static int futils__error_cannot_rmdir(const char *path, const char *filemsg)
326
383
  {
327
- git_directory_removal_type removal_type = *(git_directory_removal_type *)opaque;
384
+ if (filemsg)
385
+ giterr_set(GITERR_OS, "Could not remove directory. File '%s' %s",
386
+ path, filemsg);
387
+ else
388
+ giterr_set(GITERR_OS, "Could not remove directory '%s'", path);
328
389
 
329
- if (git_path_isdir(path->ptr) == true) {
330
- if (git_path_direach(path, _rmdir_recurs_foreach, opaque) < 0)
331
- return -1;
390
+ return -1;
391
+ }
332
392
 
333
- if (p_rmdir(path->ptr) < 0) {
334
- if (removal_type == GIT_DIRREMOVAL_ONLY_EMPTY_DIRS && (errno == ENOTEMPTY || errno == EEXIST))
335
- return 0;
393
+ static int futils__rm_first_parent(git_buf *path, const char *ceiling)
394
+ {
395
+ int error = GIT_ENOTFOUND;
396
+ struct stat st;
336
397
 
337
- giterr_set(GITERR_OS, "Could not remove directory '%s'", path->ptr);
338
- return -1;
339
- }
398
+ while (error == GIT_ENOTFOUND) {
399
+ git_buf_rtruncate_at_char(path, '/');
400
+
401
+ if (!path->size || git__prefixcmp(path->ptr, ceiling) != 0)
402
+ error = 0;
403
+ else if (p_lstat_posixly(path->ptr, &st) == 0) {
404
+ if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
405
+ error = p_unlink(path->ptr);
406
+ else if (!S_ISDIR(st.st_mode))
407
+ error = -1; /* fail to remove non-regular file */
408
+ } else if (errno != ENOTDIR)
409
+ error = -1;
410
+ }
340
411
 
341
- return 0;
412
+ if (error)
413
+ futils__error_cannot_rmdir(path->ptr, "cannot remove parent");
414
+
415
+ return error;
416
+ }
417
+
418
+ static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path)
419
+ {
420
+ struct stat st;
421
+ futils__rmdir_data *data = opaque;
422
+
423
+ if ((data->error = p_lstat_posixly(path->ptr, &st)) < 0) {
424
+ if (errno == ENOENT)
425
+ data->error = 0;
426
+ else if (errno == ENOTDIR) {
427
+ /* asked to remove a/b/c/d/e and a/b is a normal file */
428
+ if ((data->flags & GIT_RMDIR_REMOVE_BLOCKERS) != 0)
429
+ data->error = futils__rm_first_parent(path, data->base);
430
+ else
431
+ futils__error_cannot_rmdir(
432
+ path->ptr, "parent is not directory");
433
+ }
434
+ else
435
+ futils__error_cannot_rmdir(path->ptr, "cannot access");
342
436
  }
343
437
 
344
- if (removal_type == GIT_DIRREMOVAL_FILES_AND_DIRS) {
345
- if (p_unlink(path->ptr) < 0) {
346
- giterr_set(GITERR_OS, "Could not remove directory. File '%s' cannot be removed", path->ptr);
347
- return -1;
438
+ else if (S_ISDIR(st.st_mode)) {
439
+ int error = git_path_direach(path, futils__rmdir_recurs_foreach, data);
440
+ if (error < 0)
441
+ return (error == GIT_EUSER) ? data->error : error;
442
+
443
+ data->error = p_rmdir(path->ptr);
444
+
445
+ if (data->error < 0) {
446
+ if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) != 0 &&
447
+ (errno == ENOTEMPTY || errno == EEXIST))
448
+ data->error = 0;
449
+ else
450
+ futils__error_cannot_rmdir(path->ptr, NULL);
348
451
  }
452
+ }
349
453
 
350
- return 0;
454
+ else if ((data->flags & GIT_RMDIR_REMOVE_FILES) != 0) {
455
+ data->error = p_unlink(path->ptr);
456
+
457
+ if (data->error < 0)
458
+ futils__error_cannot_rmdir(path->ptr, "cannot be removed");
351
459
  }
352
460
 
353
- if (removal_type == GIT_DIRREMOVAL_EMPTY_HIERARCHY) {
354
- giterr_set(GITERR_OS, "Could not remove directory. File '%s' still present", path->ptr);
355
- return -1;
461
+ else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0)
462
+ data->error = futils__error_cannot_rmdir(path->ptr, "still present");
463
+
464
+ return data->error;
465
+ }
466
+
467
+ static int futils__rmdir_empty_parent(void *opaque, git_buf *path)
468
+ {
469
+ futils__rmdir_data *data = opaque;
470
+ int error;
471
+
472
+ if (git_buf_len(path) <= data->baselen)
473
+ return GIT_ITEROVER;
474
+
475
+ error = p_rmdir(git_buf_cstr(path));
476
+
477
+ if (error) {
478
+ int en = errno;
479
+
480
+ if (en == ENOENT || en == ENOTDIR) {
481
+ giterr_clear();
482
+ error = 0;
483
+ } else if (en == ENOTEMPTY || en == EEXIST) {
484
+ giterr_clear();
485
+ error = GIT_ITEROVER;
486
+ } else {
487
+ futils__error_cannot_rmdir(git_buf_cstr(path), NULL);
488
+ }
356
489
  }
357
490
 
358
- return 0;
491
+ return error;
359
492
  }
360
493
 
361
494
  int git_futils_rmdir_r(
362
- const char *path, const char *base, git_directory_removal_type removal_type)
495
+ const char *path, const char *base, uint32_t flags)
363
496
  {
364
497
  int error;
365
498
  git_buf fullpath = GIT_BUF_INIT;
366
-
367
- assert(removal_type == GIT_DIRREMOVAL_EMPTY_HIERARCHY
368
- || removal_type == GIT_DIRREMOVAL_FILES_AND_DIRS
369
- || removal_type == GIT_DIRREMOVAL_ONLY_EMPTY_DIRS);
499
+ futils__rmdir_data data;
370
500
 
371
501
  /* build path and find "root" where we should start calling mkdir */
372
502
  if (git_path_join_unrooted(&fullpath, path, base, NULL) < 0)
373
503
  return -1;
374
504
 
375
- error = _rmdir_recurs_foreach(&removal_type, &fullpath);
505
+ data.base = base ? base : "";
506
+ data.baselen = base ? strlen(base) : 0;
507
+ data.flags = flags;
508
+ data.error = 0;
509
+
510
+ error = futils__rmdir_recurs_foreach(&data, &fullpath);
511
+
512
+ /* remove now-empty parents if requested */
513
+ if (!error && (flags & GIT_RMDIR_EMPTY_PARENTS) != 0) {
514
+ error = git_path_walk_up(
515
+ &fullpath, base, futils__rmdir_empty_parent, &data);
516
+
517
+ if (error == GIT_ITEROVER)
518
+ error = 0;
519
+ }
376
520
 
377
521
  git_buf_free(&fullpath);
378
522
 
379
523
  return error;
380
524
  }
381
525
 
382
- int git_futils_find_system_file(git_buf *path, const char *filename)
526
+ int git_futils_cleanupdir_r(const char *path)
527
+ {
528
+ int error;
529
+ git_buf fullpath = GIT_BUF_INIT;
530
+ futils__rmdir_data data;
531
+
532
+ if ((error = git_buf_put(&fullpath, path, strlen(path))) < 0)
533
+ goto clean_up;
534
+
535
+ data.base = "";
536
+ data.baselen = 0;
537
+ data.flags = GIT_RMDIR_REMOVE_FILES;
538
+ data.error = 0;
539
+
540
+ if (!git_path_exists(path)) {
541
+ giterr_set(GITERR_OS, "Path does not exist: %s" , path);
542
+ error = GIT_ERROR;
543
+ goto clean_up;
544
+ }
545
+
546
+ if (!git_path_isdir(path)) {
547
+ giterr_set(GITERR_OS, "Path is not a directory: %s" , path);
548
+ error = GIT_ERROR;
549
+ goto clean_up;
550
+ }
551
+
552
+ error = git_path_direach(&fullpath, futils__rmdir_recurs_foreach, &data);
553
+ if (error == GIT_EUSER)
554
+ error = data.error;
555
+
556
+ clean_up:
557
+ git_buf_free(&fullpath);
558
+ return error;
559
+ }
560
+
561
+
562
+ static int git_futils_guess_system_dirs(git_buf *out)
383
563
  {
384
564
  #ifdef GIT_WIN32
385
- // try to find git.exe/git.cmd on path
386
- if (!win32_find_system_file_using_path(path, filename))
387
- return 0;
565
+ return git_win32__find_system_dirs(out);
566
+ #else
567
+ return git_buf_sets(out, "/etc");
568
+ #endif
569
+ }
388
570
 
389
- // try to find msysgit installation path using registry
390
- if (!win32_find_system_file_using_registry(path, filename))
391
- return 0;
571
+ static int git_futils_guess_global_dirs(git_buf *out)
572
+ {
573
+ #ifdef GIT_WIN32
574
+ return git_win32__find_global_dirs(out);
392
575
  #else
393
- if (git_buf_joinpath(path, "/etc", filename) < 0)
394
- return -1;
576
+ return git_buf_sets(out, getenv("HOME"));
577
+ #endif
578
+ }
395
579
 
396
- if (git_path_exists(path->ptr) == true)
397
- return 0;
580
+ static int git_futils_guess_xdg_dirs(git_buf *out)
581
+ {
582
+ #ifdef GIT_WIN32
583
+ return git_win32__find_xdg_dirs(out);
584
+ #else
585
+ const char *env = NULL;
586
+
587
+ if ((env = getenv("XDG_CONFIG_HOME")) != NULL)
588
+ return git_buf_joinpath(out, env, "git");
589
+ else if ((env = getenv("HOME")) != NULL)
590
+ return git_buf_joinpath(out, env, ".config/git");
591
+
592
+ git_buf_clear(out);
593
+ return 0;
398
594
  #endif
595
+ }
399
596
 
400
- git_buf_clear(path);
401
- giterr_set(GITERR_OS, "The system file '%s' doesn't exist", filename);
402
- return GIT_ENOTFOUND;
597
+ typedef int (*git_futils_dirs_guess_cb)(git_buf *out);
598
+
599
+ static git_buf git_futils__dirs[GIT_FUTILS_DIR__MAX] =
600
+ { GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT };
601
+
602
+ static git_futils_dirs_guess_cb git_futils__dir_guess[GIT_FUTILS_DIR__MAX] = {
603
+ git_futils_guess_system_dirs,
604
+ git_futils_guess_global_dirs,
605
+ git_futils_guess_xdg_dirs,
606
+ };
607
+
608
+ static int git_futils_check_selector(git_futils_dir_t which)
609
+ {
610
+ if (which < GIT_FUTILS_DIR__MAX)
611
+ return 0;
612
+ giterr_set(GITERR_INVALID, "config directory selector out of range");
613
+ return -1;
403
614
  }
404
615
 
405
- int git_futils_find_global_file(git_buf *path, const char *filename)
616
+ int git_futils_dirs_get(const git_buf **out, git_futils_dir_t which)
406
617
  {
407
- #ifdef GIT_WIN32
408
- struct win32_path root;
409
- static const wchar_t *tmpls[4] = {
410
- L"%HOME%\\",
411
- L"%HOMEDRIVE%%HOMEPATH%\\",
412
- L"%USERPROFILE%\\",
413
- NULL,
414
- };
415
- const wchar_t **tmpl;
416
-
417
- for (tmpl = tmpls; *tmpl != NULL; tmpl++) {
418
- /* try to expand environment variable, skipping if not set */
419
- if (win32_expand_path(&root, *tmpl) != 0 || root.path[0] == L'%')
420
- continue;
618
+ assert(out);
421
619
 
422
- /* try to look up file under path */
423
- if (!win32_find_file(path, &root, filename))
424
- return 0;
620
+ *out = NULL;
425
621
 
426
- /* No error if file not found under %HOME%, b/c we don't trust it,
427
- * but do error if another var is set and yet file is not found.
428
- */
429
- if (tmpl != tmpls)
430
- break;
431
- }
622
+ GITERR_CHECK_ERROR(git_futils_check_selector(which));
432
623
 
433
- giterr_set(GITERR_OS, "The global file '%s' doesn't exist", filename);
434
- git_buf_clear(path);
624
+ if (!git_buf_len(&git_futils__dirs[which]))
625
+ GITERR_CHECK_ERROR(
626
+ git_futils__dir_guess[which](&git_futils__dirs[which]));
435
627
 
436
- return GIT_ENOTFOUND;
437
- #else
438
- const char *home = getenv("HOME");
628
+ *out = &git_futils__dirs[which];
629
+ return 0;
630
+ }
439
631
 
440
- if (home == NULL) {
441
- giterr_set(GITERR_OS, "Global file lookup failed. "
442
- "Cannot locate the user's home directory");
443
- return GIT_ENOTFOUND;
444
- }
632
+ int git_futils_dirs_get_str(char *out, size_t outlen, git_futils_dir_t which)
633
+ {
634
+ const git_buf *path = NULL;
445
635
 
446
- if (git_buf_joinpath(path, home, filename) < 0)
447
- return -1;
636
+ GITERR_CHECK_ERROR(git_futils_check_selector(which));
637
+ GITERR_CHECK_ERROR(git_futils_dirs_get(&path, which));
448
638
 
449
- if (git_path_exists(path->ptr) == false) {
450
- giterr_set(GITERR_OS, "The global file '%s' doesn't exist", filename);
451
- git_buf_clear(path);
452
- return GIT_ENOTFOUND;
639
+ if (!out || path->size >= outlen) {
640
+ giterr_set(GITERR_NOMEMORY, "Buffer is too short for the path");
641
+ return GIT_EBUFS;
453
642
  }
454
643
 
644
+ git_buf_copy_cstr(out, outlen, path);
455
645
  return 0;
456
- #endif
646
+ }
647
+
648
+ #define PATH_MAGIC "$PATH"
649
+
650
+ int git_futils_dirs_set(git_futils_dir_t which, const char *search_path)
651
+ {
652
+ const char *expand_path = NULL;
653
+ git_buf merge = GIT_BUF_INIT;
654
+
655
+ GITERR_CHECK_ERROR(git_futils_check_selector(which));
656
+
657
+ if (search_path != NULL)
658
+ expand_path = strstr(search_path, PATH_MAGIC);
659
+
660
+ /* init with default if not yet done and needed (ignoring error) */
661
+ if ((!search_path || expand_path) &&
662
+ !git_buf_len(&git_futils__dirs[which]))
663
+ git_futils__dir_guess[which](&git_futils__dirs[which]);
664
+
665
+ /* if $PATH is not referenced, then just set the path */
666
+ if (!expand_path)
667
+ return git_buf_sets(&git_futils__dirs[which], search_path);
668
+
669
+ /* otherwise set to join(before $PATH, old value, after $PATH) */
670
+ if (expand_path > search_path)
671
+ git_buf_set(&merge, search_path, expand_path - search_path);
672
+
673
+ if (git_buf_len(&git_futils__dirs[which]))
674
+ git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR,
675
+ merge.ptr, git_futils__dirs[which].ptr);
676
+
677
+ expand_path += strlen(PATH_MAGIC);
678
+ if (*expand_path)
679
+ git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR, merge.ptr, expand_path);
680
+
681
+ git_buf_swap(&git_futils__dirs[which], &merge);
682
+ git_buf_free(&merge);
683
+
684
+ return git_buf_oom(&git_futils__dirs[which]) ? -1 : 0;
685
+ }
686
+
687
+ void git_futils_dirs_free(void)
688
+ {
689
+ int i;
690
+ for (i = 0; i < GIT_FUTILS_DIR__MAX; ++i)
691
+ git_buf_free(&git_futils__dirs[i]);
692
+ }
693
+
694
+ static int git_futils_find_in_dirlist(
695
+ git_buf *path, const char *name, git_futils_dir_t which, const char *label)
696
+ {
697
+ size_t len;
698
+ const char *scan, *next = NULL;
699
+ const git_buf *syspath;
700
+
701
+ GITERR_CHECK_ERROR(git_futils_dirs_get(&syspath, which));
702
+
703
+ for (scan = git_buf_cstr(syspath); scan; scan = next) {
704
+ for (next = strchr(scan, GIT_PATH_LIST_SEPARATOR);
705
+ next && next > scan && next[-1] == '\\';
706
+ next = strchr(next + 1, GIT_PATH_LIST_SEPARATOR))
707
+ /* find unescaped separator or end of string */;
708
+
709
+ len = next ? (size_t)(next++ - scan) : strlen(scan);
710
+ if (!len)
711
+ continue;
712
+
713
+ GITERR_CHECK_ERROR(git_buf_set(path, scan, len));
714
+ GITERR_CHECK_ERROR(git_buf_joinpath(path, path->ptr, name));
715
+
716
+ if (git_path_exists(path->ptr))
717
+ return 0;
718
+ }
719
+
720
+ git_buf_clear(path);
721
+ giterr_set(GITERR_OS, "The %s file '%s' doesn't exist", label, name);
722
+ return GIT_ENOTFOUND;
723
+ }
724
+
725
+ int git_futils_find_system_file(git_buf *path, const char *filename)
726
+ {
727
+ return git_futils_find_in_dirlist(
728
+ path, filename, GIT_FUTILS_DIR_SYSTEM, "system");
729
+ }
730
+
731
+ int git_futils_find_global_file(git_buf *path, const char *filename)
732
+ {
733
+ return git_futils_find_in_dirlist(
734
+ path, filename, GIT_FUTILS_DIR_GLOBAL, "global");
735
+ }
736
+
737
+ int git_futils_find_xdg_file(git_buf *path, const char *filename)
738
+ {
739
+ return git_futils_find_in_dirlist(
740
+ path, filename, GIT_FUTILS_DIR_XDG, "global/xdg");
457
741
  }
458
742
 
459
743
  int git_futils_fake_symlink(const char *old, const char *new)
@@ -545,8 +829,33 @@ typedef struct {
545
829
  mode_t dirmode;
546
830
  } cp_r_info;
547
831
 
832
+ #define GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT (1u << 10)
833
+
834
+ static int _cp_r_mkdir(cp_r_info *info, git_buf *from)
835
+ {
836
+ int error = 0;
837
+
838
+ /* create root directory the first time we need to create a directory */
839
+ if ((info->flags & GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT) == 0) {
840
+ error = git_futils_mkdir(
841
+ info->to_root, NULL, info->dirmode,
842
+ (info->flags & GIT_CPDIR_CHMOD_DIRS) ? GIT_MKDIR_CHMOD : 0);
843
+
844
+ info->flags |= GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT;
845
+ }
846
+
847
+ /* create directory with root as base to prevent excess chmods */
848
+ if (!error)
849
+ error = git_futils_mkdir(
850
+ from->ptr + info->from_prefix, info->to_root,
851
+ info->dirmode, info->mkdir_flags);
852
+
853
+ return error;
854
+ }
855
+
548
856
  static int _cp_r_callback(void *ref, git_buf *from)
549
857
  {
858
+ int error = 0;
550
859
  cp_r_info *info = ref;
551
860
  struct stat from_st, to_st;
552
861
  bool exists = false;
@@ -568,24 +877,22 @@ static int _cp_r_callback(void *ref, git_buf *from)
568
877
  } else
569
878
  exists = true;
570
879
 
571
- if (git_path_lstat(from->ptr, &from_st) < 0)
572
- return -1;
880
+ if ((error = git_path_lstat(from->ptr, &from_st)) < 0)
881
+ return error;
573
882
 
574
883
  if (S_ISDIR(from_st.st_mode)) {
575
- int error = 0;
576
884
  mode_t oldmode = info->dirmode;
577
885
 
578
886
  /* if we are not chmod'ing, then overwrite dirmode */
579
- if ((info->flags & GIT_CPDIR_CHMOD) == 0)
887
+ if ((info->flags & GIT_CPDIR_CHMOD_DIRS) == 0)
580
888
  info->dirmode = from_st.st_mode;
581
889
 
582
890
  /* make directory now if CREATE_EMPTY_DIRS is requested and needed */
583
891
  if (!exists && (info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) != 0)
584
- error = git_futils_mkdir(
585
- info->to.ptr, NULL, info->dirmode, info->mkdir_flags);
892
+ error = _cp_r_mkdir(info, from);
586
893
 
587
894
  /* recurse onto target directory */
588
- if (!exists || S_ISDIR(to_st.st_mode))
895
+ if (!error && (!exists || S_ISDIR(to_st.st_mode)))
589
896
  error = git_path_direach(from, _cp_r_callback, info);
590
897
 
591
898
  if (oldmode != 0)
@@ -613,15 +920,22 @@ static int _cp_r_callback(void *ref, git_buf *from)
613
920
 
614
921
  /* Make container directory on demand if needed */
615
922
  if ((info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0 &&
616
- git_futils_mkdir(
617
- info->to.ptr, NULL, info->dirmode, info->mkdir_flags) < 0)
618
- return -1;
923
+ (error = _cp_r_mkdir(info, from)) < 0)
924
+ return error;
619
925
 
620
926
  /* make symlink or regular file */
621
927
  if (S_ISLNK(from_st.st_mode))
622
- return cp_link(from->ptr, info->to.ptr, (size_t)from_st.st_size);
623
- else
624
- return git_futils_cp(from->ptr, info->to.ptr, from_st.st_mode);
928
+ error = cp_link(from->ptr, info->to.ptr, (size_t)from_st.st_size);
929
+ else {
930
+ mode_t usemode = from_st.st_mode;
931
+
932
+ if ((info->flags & GIT_CPDIR_SIMPLE_TO_MODE) != 0)
933
+ usemode = (usemode & 0111) ? 0777 : 0666;
934
+
935
+ error = git_futils_cp(from->ptr, info->to.ptr, usemode);
936
+ }
937
+
938
+ return error;
625
939
  }
626
940
 
627
941
  int git_futils_cp_r(
@@ -634,7 +948,7 @@ int git_futils_cp_r(
634
948
  git_buf path = GIT_BUF_INIT;
635
949
  cp_r_info info;
636
950
 
637
- if (git_buf_sets(&path, from) < 0)
951
+ if (git_buf_joinpath(&path, from, "") < 0) /* ensure trailing slash */
638
952
  return -1;
639
953
 
640
954
  info.to_root = to;
@@ -645,12 +959,16 @@ int git_futils_cp_r(
645
959
 
646
960
  /* precalculate mkdir flags */
647
961
  if ((flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0) {
962
+ /* if not creating empty dirs, then use mkdir to create the path on
963
+ * demand right before files are copied.
964
+ */
648
965
  info.mkdir_flags = GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST;
649
- if ((flags & GIT_CPDIR_CHMOD) != 0)
966
+ if ((flags & GIT_CPDIR_CHMOD_DIRS) != 0)
650
967
  info.mkdir_flags |= GIT_MKDIR_CHMOD_PATH;
651
968
  } else {
969
+ /* otherwise, we will do simple mkdir as directories are encountered */
652
970
  info.mkdir_flags =
653
- ((flags & GIT_CPDIR_CHMOD) != 0) ? GIT_MKDIR_CHMOD : 0;
971
+ ((flags & GIT_CPDIR_CHMOD_DIRS) != 0) ? GIT_MKDIR_CHMOD : 0;
654
972
  }
655
973
 
656
974
  error = _cp_r_callback(&info, &path);
@@ -660,3 +978,38 @@ int git_futils_cp_r(
660
978
 
661
979
  return error;
662
980
  }
981
+
982
+ int git_futils_filestamp_check(
983
+ git_futils_filestamp *stamp, const char *path)
984
+ {
985
+ struct stat st;
986
+
987
+ /* if the stamp is NULL, then always reload */
988
+ if (stamp == NULL)
989
+ return 1;
990
+
991
+ if (p_stat(path, &st) < 0)
992
+ return GIT_ENOTFOUND;
993
+
994
+ if (stamp->mtime == (git_time_t)st.st_mtime &&
995
+ stamp->size == (git_off_t)st.st_size &&
996
+ stamp->ino == (unsigned int)st.st_ino)
997
+ return 0;
998
+
999
+ stamp->mtime = (git_time_t)st.st_mtime;
1000
+ stamp->size = (git_off_t)st.st_size;
1001
+ stamp->ino = (unsigned int)st.st_ino;
1002
+
1003
+ return 1;
1004
+ }
1005
+
1006
+ void git_futils_filestamp_set(
1007
+ git_futils_filestamp *target, const git_futils_filestamp *source)
1008
+ {
1009
+ assert(target);
1010
+
1011
+ if (source)
1012
+ memcpy(target, source, sizeof(*target));
1013
+ else
1014
+ memset(target, 0, sizeof(*target));
1015
+ }