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.
@@ -27,30 +27,35 @@ extern char git_buf__oom[];
27
27
  * For the cases where GIT_BUF_INIT cannot be used to do static
28
28
  * initialization.
29
29
  */
30
- void git_buf_init(git_buf *buf, size_t initial_size);
30
+ extern void git_buf_init(git_buf *buf, size_t initial_size);
31
31
 
32
32
  /**
33
- * Grow the buffer to hold at least `target_size` bytes.
33
+ * Attempt to grow the buffer to hold at least `target_size` bytes.
34
34
  *
35
- * If the allocation fails, this will return an error and the buffer
36
- * will be marked as invalid for future operations. The existing
37
- * contents of the buffer will be preserved however.
38
- * @return 0 on success or -1 on failure
35
+ * If the allocation fails, this will return an error. If mark_oom is true,
36
+ * this will mark the buffer as invalid for future operations; if false,
37
+ * existing buffer content will be preserved, but calling code must handle
38
+ * that buffer was not expanded.
39
39
  */
40
- int git_buf_grow(git_buf *buf, size_t target_size);
40
+ extern int git_buf_try_grow(git_buf *buf, size_t target_size, bool mark_oom);
41
41
 
42
42
  /**
43
- * Attempt to grow the buffer to hold at least `target_size` bytes.
43
+ * Grow the buffer to hold at least `target_size` bytes.
44
+ *
45
+ * If the allocation fails, this will return an error and the buffer will be
46
+ * marked as invalid for future operations, invaliding contents.
44
47
  *
45
- * This is just like `git_buf_grow` except that even if the allocation
46
- * fails, the git_buf will still be left in a valid state.
48
+ * @return 0 on success or -1 on failure
47
49
  */
48
- int git_buf_try_grow(git_buf *buf, size_t target_size);
50
+ GIT_INLINE(int) git_buf_grow(git_buf *buf, size_t target_size)
51
+ {
52
+ return git_buf_try_grow(buf, target_size, true);
53
+ }
49
54
 
50
- void git_buf_free(git_buf *buf);
51
- void git_buf_swap(git_buf *buf_a, git_buf *buf_b);
52
- char *git_buf_detach(git_buf *buf);
53
- void git_buf_attach(git_buf *buf, char *ptr, size_t asize);
55
+ extern void git_buf_free(git_buf *buf);
56
+ extern void git_buf_swap(git_buf *buf_a, git_buf *buf_b);
57
+ extern char *git_buf_detach(git_buf *buf);
58
+ extern void git_buf_attach(git_buf *buf, char *ptr, size_t asize);
54
59
 
55
60
  /**
56
61
  * Test if there have been any reallocation failures with this git_buf.
@@ -91,18 +96,6 @@ void git_buf_rtruncate_at_char(git_buf *path, char separator);
91
96
  int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...);
92
97
  int git_buf_join(git_buf *buf, char separator, const char *str_a, const char *str_b);
93
98
 
94
- /**
95
- * Copy string into buf prefixing every character that is contained in the
96
- * esc_chars string with the esc_with string.
97
- */
98
- int git_buf_puts_escaped(
99
- git_buf *buf, const char *string, const char *esc_chars, const char *esc_with);
100
-
101
- GIT_INLINE(int) git_buf_puts_escape_regex(git_buf *buf, const char *string)
102
- {
103
- return git_buf_puts_escaped(buf, string, "^.[]$()|*+?{}\\", "\\");
104
- }
105
-
106
99
  /**
107
100
  * Join two strings as paths, inserting a slash between as needed.
108
101
  * @return 0 on success, -1 on failure
@@ -126,7 +119,7 @@ void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf);
126
119
 
127
120
  #define git_buf_PUTS(buf, str) git_buf_put(buf, str, sizeof(str) - 1)
128
121
 
129
- GIT_INLINE(ssize_t) git_buf_rfind_next(git_buf *buf, char ch)
122
+ GIT_INLINE(ssize_t) git_buf_rfind_next(const git_buf *buf, char ch)
130
123
  {
131
124
  ssize_t idx = (ssize_t)buf->size - 1;
132
125
  while (idx >= 0 && buf->ptr[idx] == ch) idx--;
@@ -134,28 +127,50 @@ GIT_INLINE(ssize_t) git_buf_rfind_next(git_buf *buf, char ch)
134
127
  return idx;
135
128
  }
136
129
 
137
- GIT_INLINE(ssize_t) git_buf_rfind(git_buf *buf, char ch)
130
+ GIT_INLINE(ssize_t) git_buf_rfind(const git_buf *buf, char ch)
138
131
  {
139
132
  ssize_t idx = (ssize_t)buf->size - 1;
140
133
  while (idx >= 0 && buf->ptr[idx] != ch) idx--;
141
134
  return idx;
142
135
  }
143
136
 
137
+ GIT_INLINE(ssize_t) git_buf_find(const git_buf *buf, char ch)
138
+ {
139
+ void *found = memchr(buf->ptr, ch, buf->size);
140
+ return found ? (ssize_t)((const char *)found - buf->ptr) : -1;
141
+ }
142
+
144
143
  /* Remove whitespace from the end of the buffer */
145
144
  void git_buf_rtrim(git_buf *buf);
146
145
 
147
146
  int git_buf_cmp(const git_buf *a, const git_buf *b);
148
147
 
149
- /* Fill buf with the common prefix of a array of strings */
150
- int git_buf_common_prefix(git_buf *buf, const git_strarray *strings);
151
-
152
- /* Check if buffer looks like it contains binary data */
153
- bool git_buf_is_binary(const git_buf *buf);
154
-
155
- /* Unescape all characters in a buffer */
156
- void git_buf_unescape(git_buf *buf);
157
-
158
148
  /* Write data as base64 encoded in buffer */
159
149
  int git_buf_put_base64(git_buf *buf, const char *data, size_t len);
160
150
 
151
+ /*
152
+ * Insert, remove or replace a portion of the buffer.
153
+ *
154
+ * @param buf The buffer to work with
155
+ *
156
+ * @param where The location in the buffer where the transformation
157
+ * should be applied.
158
+ *
159
+ * @param nb_to_remove The number of chars to be removed. 0 to not
160
+ * remove any character in the buffer.
161
+ *
162
+ * @param data A pointer to the data which should be inserted.
163
+ *
164
+ * @param nb_to_insert The number of chars to be inserted. 0 to not
165
+ * insert any character from the buffer.
166
+ *
167
+ * @return 0 or an error code.
168
+ */
169
+ int git_buf_splice(
170
+ git_buf *buf,
171
+ size_t where,
172
+ size_t nb_to_remove,
173
+ const char *data,
174
+ size_t nb_to_insert);
175
+
161
176
  #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.
@@ -41,6 +41,7 @@ void git_cache_free(git_cache *cache)
41
41
  git_cached_obj_decref(cache->nodes[i], cache->free_obj);
42
42
  }
43
43
 
44
+ git_mutex_free(&cache->lock);
44
45
  git__free(cache->nodes);
45
46
  }
46
47
 
@@ -51,7 +52,11 @@ void *git_cache_get(git_cache *cache, const git_oid *oid)
51
52
 
52
53
  memcpy(&hash, oid->id, sizeof(hash));
53
54
 
54
- git_mutex_lock(&cache->lock);
55
+ if (git_mutex_lock(&cache->lock)) {
56
+ giterr_set(GITERR_THREAD, "unable to lock cache mutex");
57
+ return NULL;
58
+ }
59
+
55
60
  {
56
61
  node = cache->nodes[hash & cache->size_mask];
57
62
 
@@ -72,14 +77,18 @@ void *git_cache_try_store(git_cache *cache, void *_entry)
72
77
 
73
78
  memcpy(&hash, &entry->oid, sizeof(uint32_t));
74
79
 
75
- /* increase the refcount on this object, because
76
- * the cache now owns it */
77
- git_cached_obj_incref(entry);
80
+ if (git_mutex_lock(&cache->lock)) {
81
+ giterr_set(GITERR_THREAD, "unable to lock cache mutex");
82
+ return NULL;
83
+ }
78
84
 
79
- git_mutex_lock(&cache->lock);
80
85
  {
81
86
  git_cached_obj *node = cache->nodes[hash & cache->size_mask];
82
87
 
88
+ /* increase the refcount on this object, because
89
+ * the cache now owns it */
90
+ git_cached_obj_incref(entry);
91
+
83
92
  if (node == NULL) {
84
93
  cache->nodes[hash & cache->size_mask] = entry;
85
94
  } else if (git_oid_cmp(&node->oid, &entry->oid) == 0) {
@@ -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.
@@ -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.
@@ -38,8 +38,10 @@
38
38
  /* Define the printf format specifer to use for size_t output */
39
39
  #if defined(_MSC_VER) || defined(__MINGW32__)
40
40
  # define PRIuZ "Iu"
41
+ # define PRIxZ "Ix"
41
42
  #else
42
43
  # define PRIuZ "zu"
44
+ # define PRIxZ "zx"
43
45
  #endif
44
46
 
45
47
  /* Micosoft Visual C/C++ */
@@ -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.
@@ -7,55 +7,686 @@
7
7
 
8
8
  #include <assert.h>
9
9
 
10
- #include "git2/checkout.h"
10
+ #include "checkout.h"
11
+
11
12
  #include "git2/repository.h"
12
13
  #include "git2/refs.h"
13
14
  #include "git2/tree.h"
14
15
  #include "git2/blob.h"
15
16
  #include "git2/config.h"
16
17
  #include "git2/diff.h"
18
+ #include "git2/submodule.h"
17
19
 
18
- #include "common.h"
19
20
  #include "refs.h"
20
- #include "buffer.h"
21
21
  #include "repository.h"
22
22
  #include "filter.h"
23
23
  #include "blob.h"
24
+ #include "diff.h"
25
+ #include "pathspec.h"
26
+ #include "buf_text.h"
27
+
28
+ /* See docs/checkout-internals.md for more information */
29
+
30
+ enum {
31
+ CHECKOUT_ACTION__NONE = 0,
32
+ CHECKOUT_ACTION__REMOVE = 1,
33
+ CHECKOUT_ACTION__UPDATE_BLOB = 2,
34
+ CHECKOUT_ACTION__UPDATE_SUBMODULE = 4,
35
+ CHECKOUT_ACTION__CONFLICT = 8,
36
+ CHECKOUT_ACTION__MAX = 8,
37
+ CHECKOUT_ACTION__DEFER_REMOVE = 16,
38
+ CHECKOUT_ACTION__REMOVE_AND_UPDATE =
39
+ (CHECKOUT_ACTION__UPDATE_BLOB | CHECKOUT_ACTION__REMOVE),
40
+ };
24
41
 
25
- struct checkout_diff_data
26
- {
27
- git_buf *path;
42
+ typedef struct {
43
+ git_repository *repo;
44
+ git_diff_list *diff;
45
+ git_checkout_opts opts;
46
+ bool opts_free_baseline;
47
+ char *pfx;
48
+ git_index *index;
49
+ git_pool pool;
50
+ git_vector removes;
51
+ git_buf path;
28
52
  size_t workdir_len;
29
- git_checkout_opts *checkout_opts;
30
- git_indexer_stats *stats;
31
- git_repository *owner;
32
- bool can_symlink;
33
- bool found_submodules;
34
- bool create_submodules;
35
- int error;
36
- };
53
+ unsigned int strategy;
54
+ int can_symlink;
55
+ bool reload_submodules;
56
+ size_t total_steps;
57
+ size_t completed_steps;
58
+ } checkout_data;
59
+
60
+ static int checkout_notify(
61
+ checkout_data *data,
62
+ git_checkout_notify_t why,
63
+ const git_diff_delta *delta,
64
+ const git_index_entry *wditem)
65
+ {
66
+ git_diff_file wdfile;
67
+ const git_diff_file *baseline = NULL, *target = NULL, *workdir = NULL;
68
+ const char *path = NULL;
69
+
70
+ if (!data->opts.notify_cb)
71
+ return 0;
72
+
73
+ if ((why & data->opts.notify_flags) == 0)
74
+ return 0;
75
+
76
+ if (wditem) {
77
+ memset(&wdfile, 0, sizeof(wdfile));
78
+
79
+ git_oid_cpy(&wdfile.oid, &wditem->oid);
80
+ wdfile.path = wditem->path;
81
+ wdfile.size = wditem->file_size;
82
+ wdfile.flags = GIT_DIFF_FLAG_VALID_OID;
83
+ wdfile.mode = wditem->mode;
84
+
85
+ workdir = &wdfile;
86
+
87
+ path = wditem->path;
88
+ }
89
+
90
+ if (delta) {
91
+ switch (delta->status) {
92
+ case GIT_DELTA_UNMODIFIED:
93
+ case GIT_DELTA_MODIFIED:
94
+ case GIT_DELTA_TYPECHANGE:
95
+ default:
96
+ baseline = &delta->old_file;
97
+ target = &delta->new_file;
98
+ break;
99
+ case GIT_DELTA_ADDED:
100
+ case GIT_DELTA_IGNORED:
101
+ case GIT_DELTA_UNTRACKED:
102
+ target = &delta->new_file;
103
+ break;
104
+ case GIT_DELTA_DELETED:
105
+ baseline = &delta->old_file;
106
+ break;
107
+ }
108
+
109
+ path = delta->old_file.path;
110
+ }
111
+
112
+ return data->opts.notify_cb(
113
+ why, path, baseline, target, workdir, data->opts.notify_payload);
114
+ }
115
+
116
+ static bool checkout_is_workdir_modified(
117
+ checkout_data *data,
118
+ const git_diff_file *baseitem,
119
+ const git_index_entry *wditem)
120
+ {
121
+ git_oid oid;
122
+ const git_index_entry *ie;
123
+
124
+ /* handle "modified" submodule */
125
+ if (wditem->mode == GIT_FILEMODE_COMMIT) {
126
+ git_submodule *sm;
127
+ unsigned int sm_status = 0;
128
+ const git_oid *sm_oid = NULL;
129
+
130
+ if (git_submodule_lookup(&sm, data->repo, wditem->path) < 0 ||
131
+ git_submodule_status(&sm_status, sm) < 0)
132
+ return true;
133
+
134
+ if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status))
135
+ return true;
136
+
137
+ sm_oid = git_submodule_wd_id(sm);
138
+ if (!sm_oid)
139
+ return false;
140
+
141
+ return (git_oid_cmp(&baseitem->oid, sm_oid) != 0);
142
+ }
143
+
144
+ /* Look at the cache to decide if the workdir is modified. If not,
145
+ * we can simply compare the oid in the cache to the baseitem instead
146
+ * of hashing the file.
147
+ */
148
+ if ((ie = git_index_get_bypath(data->index, wditem->path, 0)) != NULL) {
149
+ if (wditem->mtime.seconds == ie->mtime.seconds &&
150
+ wditem->mtime.nanoseconds == ie->mtime.nanoseconds &&
151
+ wditem->file_size == ie->file_size)
152
+ return (git_oid_cmp(&baseitem->oid, &ie->oid) != 0);
153
+ }
154
+
155
+ /* depending on where base is coming from, we may or may not know
156
+ * the actual size of the data, so we can't rely on this shortcut.
157
+ */
158
+ if (baseitem->size && wditem->file_size != baseitem->size)
159
+ return true;
160
+
161
+ if (git_diff__oid_for_file(
162
+ data->repo, wditem->path, wditem->mode,
163
+ wditem->file_size, &oid) < 0)
164
+ return false;
165
+
166
+ return (git_oid_cmp(&baseitem->oid, &oid) != 0);
167
+ }
168
+
169
+ #define CHECKOUT_ACTION_IF(FLAG,YES,NO) \
170
+ ((data->strategy & GIT_CHECKOUT_##FLAG) ? CHECKOUT_ACTION__##YES : CHECKOUT_ACTION__##NO)
171
+
172
+ static int checkout_action_common(
173
+ checkout_data *data,
174
+ int action,
175
+ const git_diff_delta *delta,
176
+ const git_index_entry *wd)
177
+ {
178
+ git_checkout_notify_t notify = GIT_CHECKOUT_NOTIFY_NONE;
179
+
180
+ if (action <= 0)
181
+ return action;
182
+
183
+ if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0)
184
+ action = (action & ~CHECKOUT_ACTION__REMOVE);
185
+
186
+ if ((action & CHECKOUT_ACTION__UPDATE_BLOB) != 0) {
187
+ if (S_ISGITLINK(delta->new_file.mode))
188
+ action = (action & ~CHECKOUT_ACTION__UPDATE_BLOB) |
189
+ CHECKOUT_ACTION__UPDATE_SUBMODULE;
190
+
191
+ notify = GIT_CHECKOUT_NOTIFY_UPDATED;
192
+ }
193
+
194
+ if ((action & CHECKOUT_ACTION__CONFLICT) != 0)
195
+ notify = GIT_CHECKOUT_NOTIFY_CONFLICT;
196
+
197
+ if (notify != GIT_CHECKOUT_NOTIFY_NONE &&
198
+ checkout_notify(data, notify, delta, wd) != 0)
199
+ return GIT_EUSER;
200
+
201
+ return action;
202
+ }
203
+
204
+ static int checkout_action_no_wd(
205
+ checkout_data *data,
206
+ const git_diff_delta *delta)
207
+ {
208
+ int action = CHECKOUT_ACTION__NONE;
209
+
210
+ switch (delta->status) {
211
+ case GIT_DELTA_UNMODIFIED: /* case 12 */
212
+ if (checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL))
213
+ return GIT_EUSER;
214
+ action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, NONE);
215
+ break;
216
+ case GIT_DELTA_ADDED: /* case 2 or 28 (and 5 but not really) */
217
+ case GIT_DELTA_MODIFIED: /* case 13 (and 35 but not really) */
218
+ action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
219
+ break;
220
+ case GIT_DELTA_TYPECHANGE: /* case 21 (B->T) and 28 (T->B)*/
221
+ if (delta->new_file.mode == GIT_FILEMODE_TREE)
222
+ action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
223
+ break;
224
+ case GIT_DELTA_DELETED: /* case 8 or 25 */
225
+ default: /* impossible */
226
+ break;
227
+ }
228
+
229
+ return checkout_action_common(data, action, delta, NULL);
230
+ }
231
+
232
+ static int checkout_action_wd_only(
233
+ checkout_data *data,
234
+ git_iterator *workdir,
235
+ const git_index_entry *wd,
236
+ git_vector *pathspec)
237
+ {
238
+ bool remove = false;
239
+ git_checkout_notify_t notify = GIT_CHECKOUT_NOTIFY_NONE;
240
+
241
+ if (!git_pathspec_match_path(
242
+ pathspec, wd->path,
243
+ (data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
244
+ git_iterator_ignore_case(workdir), NULL))
245
+ return 0;
246
+
247
+ /* check if item is tracked in the index but not in the checkout diff */
248
+ if (data->index != NULL) {
249
+ if (wd->mode != GIT_FILEMODE_TREE) {
250
+ int error;
251
+
252
+ if ((error = git_index_find(NULL, data->index, wd->path)) == 0) {
253
+ notify = GIT_CHECKOUT_NOTIFY_DIRTY;
254
+ remove = ((data->strategy & GIT_CHECKOUT_FORCE) != 0);
255
+ } else if (error != GIT_ENOTFOUND)
256
+ return error;
257
+ } else {
258
+ /* for tree entries, we have to see if there are any index
259
+ * entries that are contained inside that tree
260
+ */
261
+ size_t pos = git_index__prefix_position(data->index, wd->path);
262
+ const git_index_entry *e = git_index_get_byindex(data->index, pos);
263
+
264
+ if (e != NULL && data->diff->pfxcomp(e->path, wd->path) == 0) {
265
+ notify = GIT_CHECKOUT_NOTIFY_DIRTY;
266
+ remove = ((data->strategy & GIT_CHECKOUT_FORCE) != 0);
267
+ }
268
+ }
269
+ }
270
+
271
+ if (notify != GIT_CHECKOUT_NOTIFY_NONE)
272
+ /* found in index */;
273
+ else if (git_iterator_current_is_ignored(workdir)) {
274
+ notify = GIT_CHECKOUT_NOTIFY_IGNORED;
275
+ remove = ((data->strategy & GIT_CHECKOUT_REMOVE_IGNORED) != 0);
276
+ }
277
+ else {
278
+ notify = GIT_CHECKOUT_NOTIFY_UNTRACKED;
279
+ remove = ((data->strategy & GIT_CHECKOUT_REMOVE_UNTRACKED) != 0);
280
+ }
281
+
282
+ if (checkout_notify(data, notify, NULL, wd))
283
+ return GIT_EUSER;
284
+
285
+ if (remove) {
286
+ char *path = git_pool_strdup(&data->pool, wd->path);
287
+ GITERR_CHECK_ALLOC(path);
288
+
289
+ if (git_vector_insert(&data->removes, path) < 0)
290
+ return -1;
291
+ }
292
+
293
+ return 0;
294
+ }
295
+
296
+ static bool submodule_is_config_only(
297
+ checkout_data *data,
298
+ const char *path)
299
+ {
300
+ git_submodule *sm = NULL;
301
+ unsigned int sm_loc = 0;
302
+
303
+ if (git_submodule_lookup(&sm, data->repo, path) < 0 ||
304
+ git_submodule_location(&sm_loc, sm) < 0 ||
305
+ sm_loc == GIT_SUBMODULE_STATUS_IN_CONFIG)
306
+ return true;
307
+
308
+ return false;
309
+ }
310
+
311
+ static int checkout_action_with_wd(
312
+ checkout_data *data,
313
+ const git_diff_delta *delta,
314
+ const git_index_entry *wd)
315
+ {
316
+ int action = CHECKOUT_ACTION__NONE;
317
+
318
+ switch (delta->status) {
319
+ case GIT_DELTA_UNMODIFIED: /* case 14/15 or 33 */
320
+ if (checkout_is_workdir_modified(data, &delta->old_file, wd)) {
321
+ if (checkout_notify(
322
+ data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd))
323
+ return GIT_EUSER;
324
+ action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, NONE);
325
+ }
326
+ break;
327
+ case GIT_DELTA_ADDED: /* case 3, 4 or 6 */
328
+ action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT);
329
+ break;
330
+ case GIT_DELTA_DELETED: /* case 9 or 10 (or 26 but not really) */
331
+ if (checkout_is_workdir_modified(data, &delta->old_file, wd))
332
+ action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT);
333
+ else
334
+ action = CHECKOUT_ACTION_IF(SAFE, REMOVE, NONE);
335
+ break;
336
+ case GIT_DELTA_MODIFIED: /* case 16, 17, 18 (or 36 but not really) */
337
+ if (checkout_is_workdir_modified(data, &delta->old_file, wd))
338
+ action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT);
339
+ else
340
+ action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
341
+ break;
342
+ case GIT_DELTA_TYPECHANGE: /* case 22, 23, 29, 30 */
343
+ if (delta->old_file.mode == GIT_FILEMODE_TREE) {
344
+ if (wd->mode == GIT_FILEMODE_TREE)
345
+ /* either deleting items in old tree will delete the wd dir,
346
+ * or we'll get a conflict when we attempt blob update...
347
+ */
348
+ action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
349
+ else if (wd->mode == GIT_FILEMODE_COMMIT) {
350
+ /* workdir is possibly a "phantom" submodule - treat as a
351
+ * tree if the only submodule info came from the config
352
+ */
353
+ if (submodule_is_config_only(data, wd->path))
354
+ action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
355
+ else
356
+ action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
357
+ } else
358
+ action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT);
359
+ }
360
+ else if (checkout_is_workdir_modified(data, &delta->old_file, wd))
361
+ action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
362
+ else
363
+ action = CHECKOUT_ACTION_IF(SAFE, REMOVE_AND_UPDATE, NONE);
364
+
365
+ /* don't update if the typechange is to a tree */
366
+ if (delta->new_file.mode == GIT_FILEMODE_TREE)
367
+ action = (action & ~CHECKOUT_ACTION__UPDATE_BLOB);
368
+ break;
369
+ default: /* impossible */
370
+ break;
371
+ }
372
+
373
+ return checkout_action_common(data, action, delta, wd);
374
+ }
375
+
376
+ static int checkout_action_with_wd_blocker(
377
+ checkout_data *data,
378
+ const git_diff_delta *delta,
379
+ const git_index_entry *wd)
380
+ {
381
+ int action = CHECKOUT_ACTION__NONE;
382
+
383
+ switch (delta->status) {
384
+ case GIT_DELTA_UNMODIFIED:
385
+ /* should show delta as dirty / deleted */
386
+ if (checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd))
387
+ return GIT_EUSER;
388
+ action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, NONE);
389
+ break;
390
+ case GIT_DELTA_ADDED:
391
+ case GIT_DELTA_MODIFIED:
392
+ action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
393
+ break;
394
+ case GIT_DELTA_DELETED:
395
+ action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT);
396
+ break;
397
+ case GIT_DELTA_TYPECHANGE:
398
+ /* not 100% certain about this... */
399
+ action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
400
+ break;
401
+ default: /* impossible */
402
+ break;
403
+ }
404
+
405
+ return checkout_action_common(data, action, delta, wd);
406
+ }
407
+
408
+ static int checkout_action_with_wd_dir(
409
+ checkout_data *data,
410
+ const git_diff_delta *delta,
411
+ const git_index_entry *wd)
412
+ {
413
+ int action = CHECKOUT_ACTION__NONE;
414
+
415
+ switch (delta->status) {
416
+ case GIT_DELTA_UNMODIFIED: /* case 19 or 24 (or 34 but not really) */
417
+ if (checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL) ||
418
+ checkout_notify(
419
+ data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd))
420
+ return GIT_EUSER;
421
+ break;
422
+ case GIT_DELTA_ADDED:/* case 4 (and 7 for dir) */
423
+ case GIT_DELTA_MODIFIED: /* case 20 (or 37 but not really) */
424
+ if (delta->old_file.mode == GIT_FILEMODE_COMMIT)
425
+ /* expected submodule (and maybe found one) */;
426
+ else if (delta->new_file.mode != GIT_FILEMODE_TREE)
427
+ action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
428
+ break;
429
+ case GIT_DELTA_DELETED: /* case 11 (and 27 for dir) */
430
+ if (delta->old_file.mode != GIT_FILEMODE_TREE &&
431
+ checkout_notify(
432
+ data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd))
433
+ return GIT_EUSER;
434
+ break;
435
+ case GIT_DELTA_TYPECHANGE: /* case 24 or 31 */
436
+ if (delta->old_file.mode == GIT_FILEMODE_TREE) {
437
+ /* For typechange from dir, remove dir and add blob, but it is
438
+ * not safe to remove dir if it contains modified files.
439
+ * However, safely removing child files will remove the parent
440
+ * directory if is it left empty, so we can defer removing the
441
+ * dir and it will succeed if no children are left.
442
+ */
443
+ action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
444
+ if (action != CHECKOUT_ACTION__NONE)
445
+ action |= CHECKOUT_ACTION__DEFER_REMOVE;
446
+ }
447
+ else if (delta->new_file.mode != GIT_FILEMODE_TREE)
448
+ /* For typechange to dir, dir is already created so no action */
449
+ action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
450
+ break;
451
+ default: /* impossible */
452
+ break;
453
+ }
454
+
455
+ return checkout_action_common(data, action, delta, wd);
456
+ }
457
+
458
+ static int checkout_action(
459
+ checkout_data *data,
460
+ git_diff_delta *delta,
461
+ git_iterator *workdir,
462
+ const git_index_entry **wditem_ptr,
463
+ git_vector *pathspec)
464
+ {
465
+ const git_index_entry *wd = *wditem_ptr;
466
+ int cmp = -1, act;
467
+ int (*strcomp)(const char *, const char *) = data->diff->strcomp;
468
+ int (*pfxcomp)(const char *str, const char *pfx) = data->diff->pfxcomp;
469
+
470
+ /* move workdir iterator to follow along with deltas */
471
+
472
+ while (1) {
473
+ if (!wd)
474
+ return checkout_action_no_wd(data, delta);
475
+
476
+ cmp = strcomp(wd->path, delta->old_file.path);
477
+
478
+ /* 1. wd before delta ("a/a" before "a/b")
479
+ * 2. wd prefixes delta & should expand ("a/" before "a/b")
480
+ * 3. wd prefixes delta & cannot expand ("a/b" before "a/b/c")
481
+ * 4. wd equals delta ("a/b" and "a/b")
482
+ * 5. wd after delta & delta prefixes wd ("a/b/c" after "a/b/" or "a/b")
483
+ * 6. wd after delta ("a/c" after "a/b")
484
+ */
485
+
486
+ if (cmp < 0) {
487
+ cmp = pfxcomp(delta->old_file.path, wd->path);
488
+
489
+ if (cmp == 0) {
490
+ if (wd->mode == GIT_FILEMODE_TREE) {
491
+ /* case 2 - entry prefixed by workdir tree */
492
+ if (git_iterator_advance_into(&wd, workdir) < 0)
493
+ goto fail;
494
+
495
+ *wditem_ptr = wd;
496
+ continue;
497
+ }
498
+
499
+ /* case 3 maybe - wd contains non-dir where dir expected */
500
+ if (delta->old_file.path[strlen(wd->path)] == '/') {
501
+ act = checkout_action_with_wd_blocker(data, delta, wd);
502
+ *wditem_ptr =
503
+ git_iterator_advance(&wd, workdir) ? NULL : wd;
504
+ return act;
505
+ }
506
+ }
507
+
508
+ /* case 1 - handle wd item (if it matches pathspec) */
509
+ if (checkout_action_wd_only(data, workdir, wd, pathspec) < 0 ||
510
+ git_iterator_advance(&wd, workdir) < 0)
511
+ goto fail;
512
+
513
+ *wditem_ptr = wd;
514
+ continue;
515
+ }
516
+
517
+ if (cmp == 0) {
518
+ /* case 4 */
519
+ act = checkout_action_with_wd(data, delta, wd);
520
+ *wditem_ptr = git_iterator_advance(&wd, workdir) ? NULL : wd;
521
+ return act;
522
+ }
523
+
524
+ cmp = pfxcomp(wd->path, delta->old_file.path);
525
+
526
+ if (cmp == 0) { /* case 5 */
527
+ if (wd->path[strlen(delta->old_file.path)] != '/')
528
+ return checkout_action_no_wd(data, delta);
529
+
530
+ if (delta->status == GIT_DELTA_TYPECHANGE) {
531
+ if (delta->old_file.mode == GIT_FILEMODE_TREE) {
532
+ act = checkout_action_with_wd(data, delta, wd);
533
+ if (git_iterator_advance_into(&wd, workdir) < 0)
534
+ wd = NULL;
535
+ *wditem_ptr = wd;
536
+ return act;
537
+ }
538
+
539
+ if (delta->new_file.mode == GIT_FILEMODE_TREE ||
540
+ delta->new_file.mode == GIT_FILEMODE_COMMIT ||
541
+ delta->old_file.mode == GIT_FILEMODE_COMMIT)
542
+ {
543
+ act = checkout_action_with_wd(data, delta, wd);
544
+ if (git_iterator_advance(&wd, workdir) < 0)
545
+ wd = NULL;
546
+ *wditem_ptr = wd;
547
+ return act;
548
+ }
549
+ }
550
+
551
+ return checkout_action_with_wd_dir(data, delta, wd);
552
+ }
553
+
554
+ /* case 6 - wd is after delta */
555
+ return checkout_action_no_wd(data, delta);
556
+ }
557
+
558
+ fail:
559
+ *wditem_ptr = NULL;
560
+ return -1;
561
+ }
562
+
563
+ static int checkout_remaining_wd_items(
564
+ checkout_data *data,
565
+ git_iterator *workdir,
566
+ const git_index_entry *wd,
567
+ git_vector *spec)
568
+ {
569
+ int error = 0;
570
+
571
+ while (wd && !error) {
572
+ if (!(error = checkout_action_wd_only(data, workdir, wd, spec)))
573
+ error = git_iterator_advance(&wd, workdir);
574
+ }
575
+
576
+ return error;
577
+ }
578
+
579
+ static int checkout_get_actions(
580
+ uint32_t **actions_ptr,
581
+ size_t **counts_ptr,
582
+ checkout_data *data,
583
+ git_iterator *workdir)
584
+ {
585
+ int error = 0;
586
+ const git_index_entry *wditem;
587
+ git_vector pathspec = GIT_VECTOR_INIT, *deltas;
588
+ git_pool pathpool = GIT_POOL_INIT_STRINGPOOL;
589
+ git_diff_delta *delta;
590
+ size_t i, *counts = NULL;
591
+ uint32_t *actions = NULL;
592
+
593
+ if (data->opts.paths.count > 0 &&
594
+ git_pathspec_init(&pathspec, &data->opts.paths, &pathpool) < 0)
595
+ return -1;
596
+
597
+ if ((error = git_iterator_current(&wditem, workdir)) < 0)
598
+ goto fail;
599
+
600
+ deltas = &data->diff->deltas;
601
+
602
+ *counts_ptr = counts = git__calloc(CHECKOUT_ACTION__MAX+1, sizeof(size_t));
603
+ *actions_ptr = actions = git__calloc(
604
+ deltas->length ? deltas->length : 1, sizeof(uint32_t));
605
+ if (!counts || !actions) {
606
+ error = -1;
607
+ goto fail;
608
+ }
609
+
610
+ git_vector_foreach(deltas, i, delta) {
611
+ int act = checkout_action(data, delta, workdir, &wditem, &pathspec);
612
+
613
+ if (act < 0) {
614
+ error = act;
615
+ goto fail;
616
+ }
617
+
618
+ actions[i] = act;
619
+
620
+ if (act & CHECKOUT_ACTION__REMOVE)
621
+ counts[CHECKOUT_ACTION__REMOVE]++;
622
+ if (act & CHECKOUT_ACTION__UPDATE_BLOB)
623
+ counts[CHECKOUT_ACTION__UPDATE_BLOB]++;
624
+ if (act & CHECKOUT_ACTION__UPDATE_SUBMODULE)
625
+ counts[CHECKOUT_ACTION__UPDATE_SUBMODULE]++;
626
+ if (act & CHECKOUT_ACTION__CONFLICT)
627
+ counts[CHECKOUT_ACTION__CONFLICT]++;
628
+ }
629
+
630
+ error = checkout_remaining_wd_items(data, workdir, wditem, &pathspec);
631
+ if (error < 0)
632
+ goto fail;
633
+
634
+ counts[CHECKOUT_ACTION__REMOVE] += data->removes.length;
635
+
636
+ if (counts[CHECKOUT_ACTION__CONFLICT] > 0 &&
637
+ (data->strategy & GIT_CHECKOUT_ALLOW_CONFLICTS) == 0)
638
+ {
639
+ giterr_set(GITERR_CHECKOUT, "%d conflicts prevent checkout",
640
+ (int)counts[CHECKOUT_ACTION__CONFLICT]);
641
+ error = GIT_EMERGECONFLICT;
642
+ goto fail;
643
+ }
644
+
645
+ git_pathspec_free(&pathspec);
646
+ git_pool_clear(&pathpool);
647
+
648
+ return 0;
649
+
650
+ fail:
651
+ *counts_ptr = NULL;
652
+ git__free(counts);
653
+ *actions_ptr = NULL;
654
+ git__free(actions);
655
+
656
+ git_pathspec_free(&pathspec);
657
+ git_pool_clear(&pathpool);
658
+
659
+ return error;
660
+ }
37
661
 
38
662
  static int buffer_to_file(
663
+ struct stat *st,
39
664
  git_buf *buffer,
40
665
  const char *path,
41
666
  mode_t dir_mode,
42
667
  int file_open_flags,
43
668
  mode_t file_mode)
44
669
  {
45
- int fd, error, error_close;
670
+ int fd, error;
46
671
 
47
672
  if ((error = git_futils_mkpath2file(path, dir_mode)) < 0)
48
673
  return error;
49
674
 
50
- if ((fd = p_open(path, file_open_flags, file_mode)) < 0)
675
+ if ((fd = p_open(path, file_open_flags, file_mode)) < 0) {
676
+ giterr_set(GITERR_OS, "Could not open '%s' for writing", path);
51
677
  return fd;
678
+ }
52
679
 
53
- error = p_write(fd, git_buf_cstr(buffer), git_buf_len(buffer));
54
-
55
- error_close = p_close(fd);
680
+ if ((error = p_write(fd, git_buf_cstr(buffer), git_buf_len(buffer))) < 0) {
681
+ giterr_set(GITERR_OS, "Could not write to '%s'", path);
682
+ (void)p_close(fd);
683
+ } else {
684
+ if ((error = p_close(fd)) < 0)
685
+ giterr_set(GITERR_OS, "Error while closing '%s'", path);
56
686
 
57
- if (!error)
58
- error = error_close;
687
+ if ((error = p_stat(path, st)) < 0)
688
+ giterr_set(GITERR_OS, "Error while statting '%s'", path);
689
+ }
59
690
 
60
691
  if (!error &&
61
692
  (file_mode & 0100) != 0 &&
@@ -66,6 +697,7 @@ static int buffer_to_file(
66
697
  }
67
698
 
68
699
  static int blob_content_to_file(
700
+ struct stat *st,
69
701
  git_blob *blob,
70
702
  const char *path,
71
703
  mode_t entry_filemode,
@@ -73,23 +705,27 @@ static int blob_content_to_file(
73
705
  {
74
706
  int error = -1, nb_filters = 0;
75
707
  mode_t file_mode = opts->file_mode;
76
- bool dont_free_filtered = false;
708
+ bool dont_free_filtered;
77
709
  git_buf unfiltered = GIT_BUF_INIT, filtered = GIT_BUF_INIT;
78
710
  git_vector filters = GIT_VECTOR_INIT;
79
711
 
80
- if (opts->disable_filters ||
712
+ /* Create a fake git_buf from the blob raw data... */
713
+ filtered.ptr = blob->odb_object->raw.data;
714
+ filtered.size = blob->odb_object->raw.len;
715
+ /* ... and make sure it doesn't get unexpectedly freed */
716
+ dont_free_filtered = true;
717
+
718
+ if (!opts->disable_filters &&
719
+ !git_buf_text_is_binary(&filtered) &&
81
720
  (nb_filters = git_filters_load(
82
721
  &filters,
83
722
  git_object_owner((git_object *)blob),
84
723
  path,
85
- GIT_FILTER_TO_WORKTREE)) == 0) {
86
-
87
- /* Create a fake git_buf from the blob raw data... */
88
- filtered.ptr = blob->odb_object->raw.data;
89
- filtered.size = blob->odb_object->raw.len;
90
-
91
- /* ... and make sure it doesn't get unexpectedly freed */
92
- dont_free_filtered = true;
724
+ GIT_FILTER_TO_WORKTREE)) > 0)
725
+ {
726
+ /* reset 'filtered' so it can be a filter target */
727
+ git_buf_init(&filtered, 0);
728
+ dont_free_filtered = false;
93
729
  }
94
730
 
95
731
  if (nb_filters < 0)
@@ -107,7 +743,11 @@ static int blob_content_to_file(
107
743
  if (!file_mode)
108
744
  file_mode = entry_filemode;
109
745
 
110
- error = buffer_to_file(&filtered, path, opts->dir_mode, opts->file_open_flags, file_mode);
746
+ error = buffer_to_file(
747
+ st, &filtered, path, opts->dir_mode, opts->file_open_flags, file_mode);
748
+
749
+ if (!error)
750
+ st->st_mode = entry_filemode;
111
751
 
112
752
  cleanup:
113
753
  git_filters_free(&filters);
@@ -118,7 +758,8 @@ cleanup:
118
758
  return error;
119
759
  }
120
760
 
121
- static int blob_content_to_link(git_blob *blob, const char *path, bool can_symlink)
761
+ static int blob_content_to_link(
762
+ struct stat *st, git_blob *blob, const char *path, int can_symlink)
122
763
  {
123
764
  git_buf linktarget = GIT_BUF_INIT;
124
765
  int error;
@@ -126,327 +767,629 @@ static int blob_content_to_link(git_blob *blob, const char *path, bool can_symli
126
767
  if ((error = git_blob__getbuf(&linktarget, blob)) < 0)
127
768
  return error;
128
769
 
129
- if (can_symlink)
130
- error = p_symlink(git_buf_cstr(&linktarget), path);
131
- else
770
+ if (can_symlink) {
771
+ if ((error = p_symlink(git_buf_cstr(&linktarget), path)) < 0)
772
+ giterr_set(GITERR_CHECKOUT, "Could not create symlink %s\n", path);
773
+ } else {
132
774
  error = git_futils_fake_symlink(git_buf_cstr(&linktarget), path);
775
+ }
776
+
777
+ if (!error) {
778
+ if ((error = p_lstat(path, st)) < 0)
779
+ giterr_set(GITERR_CHECKOUT, "Could not stat symlink %s", path);
780
+
781
+ st->st_mode = GIT_FILEMODE_LINK;
782
+ }
133
783
 
134
784
  git_buf_free(&linktarget);
135
785
 
136
786
  return error;
137
787
  }
138
788
 
789
+ static int checkout_update_index(
790
+ checkout_data *data,
791
+ const git_diff_file *file,
792
+ struct stat *st)
793
+ {
794
+ git_index_entry entry;
795
+
796
+ if (!data->index)
797
+ return 0;
798
+
799
+ memset(&entry, 0, sizeof(entry));
800
+ entry.path = (char *)file->path; /* cast to prevent warning */
801
+ git_index_entry__init_from_stat(&entry, st);
802
+ git_oid_cpy(&entry.oid, &file->oid);
803
+
804
+ return git_index_add(data->index, &entry);
805
+ }
806
+
139
807
  static int checkout_submodule(
140
- struct checkout_diff_data *data,
808
+ checkout_data *data,
141
809
  const git_diff_file *file)
142
810
  {
143
- if (git_futils_mkdir(
144
- file->path, git_repository_workdir(data->owner),
145
- data->checkout_opts->dir_mode, GIT_MKDIR_PATH) < 0)
146
- return -1;
811
+ int error = 0;
812
+ git_submodule *sm;
813
+
814
+ /* Until submodules are supported, UPDATE_ONLY means do nothing here */
815
+ if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0)
816
+ return 0;
817
+
818
+ if ((error = git_futils_mkdir(
819
+ file->path, git_repository_workdir(data->repo),
820
+ data->opts.dir_mode, GIT_MKDIR_PATH)) < 0)
821
+ return error;
822
+
823
+ if ((error = git_submodule_lookup(&sm, data->repo, file->path)) < 0)
824
+ return error;
147
825
 
148
- /* TODO: two cases:
826
+ /* TODO: Support checkout_strategy options. Two circumstances:
149
827
  * 1 - submodule already checked out, but we need to move the HEAD
150
828
  * to the new OID, or
151
829
  * 2 - submodule not checked out and we should recursively check it out
152
830
  *
153
- * Checkout will not execute a pull request on the submodule, but a
154
- * clone command should probably be able to. Do we need a submodule
155
- * callback option?
831
+ * Checkout will not execute a pull on the submodule, but a clone
832
+ * command should probably be able to. Do we need a submodule callback?
156
833
  */
157
834
 
835
+ /* update the index unless prevented */
836
+ if ((data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0) {
837
+ struct stat st;
838
+
839
+ git_buf_truncate(&data->path, data->workdir_len);
840
+ if (git_buf_puts(&data->path, file->path) < 0)
841
+ return -1;
842
+
843
+ if ((error = p_stat(git_buf_cstr(&data->path), &st)) < 0) {
844
+ giterr_set(
845
+ GITERR_CHECKOUT, "Could not stat submodule %s\n", file->path);
846
+ return error;
847
+ }
848
+
849
+ st.st_mode = GIT_FILEMODE_COMMIT;
850
+
851
+ error = checkout_update_index(data, file, &st);
852
+ }
853
+
854
+ return error;
855
+ }
856
+
857
+ static void report_progress(
858
+ checkout_data *data,
859
+ const char *path)
860
+ {
861
+ if (data->opts.progress_cb)
862
+ data->opts.progress_cb(
863
+ path, data->completed_steps, data->total_steps,
864
+ data->opts.progress_payload);
865
+ }
866
+
867
+ static int checkout_safe_for_update_only(const char *path, mode_t expected_mode)
868
+ {
869
+ struct stat st;
870
+
871
+ if (p_lstat(path, &st) < 0) {
872
+ /* if doesn't exist, then no error and no update */
873
+ if (errno == ENOENT || errno == ENOTDIR)
874
+ return 0;
875
+
876
+ /* otherwise, stat error and no update */
877
+ giterr_set(GITERR_OS, "Failed to stat file '%s'", path);
878
+ return -1;
879
+ }
880
+
881
+ /* only safe for update if this is the same type of file */
882
+ if ((st.st_mode & ~0777) == (expected_mode & ~0777))
883
+ return 1;
884
+
158
885
  return 0;
159
886
  }
160
887
 
161
888
  static int checkout_blob(
162
- struct checkout_diff_data *data,
889
+ checkout_data *data,
163
890
  const git_diff_file *file)
164
891
  {
892
+ int error = 0;
165
893
  git_blob *blob;
166
- int error;
894
+ struct stat st;
167
895
 
168
- git_buf_truncate(data->path, data->workdir_len);
169
- if (git_buf_joinpath(data->path, git_buf_cstr(data->path), file->path) < 0)
896
+ git_buf_truncate(&data->path, data->workdir_len);
897
+ if (git_buf_puts(&data->path, file->path) < 0)
170
898
  return -1;
171
899
 
172
- if ((error = git_blob_lookup(&blob, data->owner, &file->oid)) < 0)
900
+ if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) {
901
+ int rval = checkout_safe_for_update_only(
902
+ git_buf_cstr(&data->path), file->mode);
903
+ if (rval <= 0)
904
+ return rval;
905
+ }
906
+
907
+ if ((error = git_blob_lookup(&blob, data->repo, &file->oid)) < 0)
173
908
  return error;
174
909
 
175
910
  if (S_ISLNK(file->mode))
176
911
  error = blob_content_to_link(
177
- blob, git_buf_cstr(data->path), data->can_symlink);
912
+ &st, blob, git_buf_cstr(&data->path), data->can_symlink);
178
913
  else
179
914
  error = blob_content_to_file(
180
- blob, git_buf_cstr(data->path), file->mode, data->checkout_opts);
915
+ &st, blob, git_buf_cstr(&data->path), file->mode, &data->opts);
181
916
 
182
917
  git_blob_free(blob);
183
918
 
919
+ /* if we try to create the blob and an existing directory blocks it from
920
+ * being written, then there must have been a typechange conflict in a
921
+ * parent directory - suppress the error and try to continue.
922
+ */
923
+ if ((data->strategy & GIT_CHECKOUT_ALLOW_CONFLICTS) != 0 &&
924
+ (error == GIT_ENOTFOUND || error == GIT_EEXISTS))
925
+ {
926
+ giterr_clear();
927
+ error = 0;
928
+ }
929
+
930
+ /* update the index unless prevented */
931
+ if (!error && (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
932
+ error = checkout_update_index(data, file, &st);
933
+
934
+ /* update the submodule data if this was a new .gitmodules file */
935
+ if (!error && strcmp(file->path, ".gitmodules") == 0)
936
+ data->reload_submodules = true;
937
+
184
938
  return error;
185
939
  }
186
940
 
187
941
  static int checkout_remove_the_old(
188
- void *cb_data, const git_diff_delta *delta, float progress)
942
+ unsigned int *actions,
943
+ checkout_data *data)
189
944
  {
190
- struct checkout_diff_data *data = cb_data;
191
- git_checkout_opts *opts = data->checkout_opts;
945
+ int error = 0;
946
+ git_diff_delta *delta;
947
+ const char *str;
948
+ size_t i;
949
+ const char *workdir = git_buf_cstr(&data->path);
950
+ uint32_t flg = GIT_RMDIR_EMPTY_PARENTS |
951
+ GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS;
952
+
953
+ git_buf_truncate(&data->path, data->workdir_len);
954
+
955
+ git_vector_foreach(&data->diff->deltas, i, delta) {
956
+ if (actions[i] & CHECKOUT_ACTION__REMOVE) {
957
+ error = git_futils_rmdir_r(delta->old_file.path, workdir, flg);
958
+ if (error < 0)
959
+ return error;
960
+
961
+ data->completed_steps++;
962
+ report_progress(data, delta->old_file.path);
963
+
964
+ if ((actions[i] & CHECKOUT_ACTION__UPDATE_BLOB) == 0 &&
965
+ (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0 &&
966
+ data->index != NULL)
967
+ {
968
+ (void)git_index_remove(data->index, delta->old_file.path, 0);
969
+ }
970
+ }
971
+ }
192
972
 
193
- GIT_UNUSED(progress);
194
- data->stats->processed++;
973
+ git_vector_foreach(&data->removes, i, str) {
974
+ error = git_futils_rmdir_r(str, workdir, flg);
975
+ if (error < 0)
976
+ return error;
195
977
 
196
- if ((delta->status == GIT_DELTA_UNTRACKED &&
197
- (opts->checkout_strategy & GIT_CHECKOUT_REMOVE_UNTRACKED) != 0) ||
198
- (delta->status == GIT_DELTA_TYPECHANGE &&
199
- (opts->checkout_strategy & GIT_CHECKOUT_OVERWRITE_MODIFIED) != 0))
200
- {
201
- data->error = git_futils_rmdir_r(
202
- delta->new_file.path,
203
- git_repository_workdir(data->owner),
204
- GIT_DIRREMOVAL_FILES_AND_DIRS);
978
+ data->completed_steps++;
979
+ report_progress(data, str);
980
+
981
+ if ((data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0 &&
982
+ data->index != NULL)
983
+ {
984
+ if (str[strlen(str) - 1] == '/')
985
+ (void)git_index_remove_directory(data->index, str, 0);
986
+ else
987
+ (void)git_index_remove(data->index, str, 0);
988
+ }
989
+ }
990
+
991
+ return 0;
992
+ }
993
+
994
+ static int checkout_deferred_remove(git_repository *repo, const char *path)
995
+ {
996
+ #if 0
997
+ int error = git_futils_rmdir_r(
998
+ path, git_repository_workdir(repo), GIT_RMDIR_EMPTY_PARENTS);
999
+
1000
+ if (error == GIT_ENOTFOUND) {
1001
+ error = 0;
1002
+ giterr_clear();
205
1003
  }
206
1004
 
207
- return data->error;
1005
+ return error;
1006
+ #else
1007
+ GIT_UNUSED(repo);
1008
+ GIT_UNUSED(path);
1009
+ assert(false);
1010
+ return 0;
1011
+ #endif
208
1012
  }
209
1013
 
210
1014
  static int checkout_create_the_new(
211
- void *cb_data, const git_diff_delta *delta, float progress)
1015
+ unsigned int *actions,
1016
+ checkout_data *data)
212
1017
  {
213
1018
  int error = 0;
214
- struct checkout_diff_data *data = cb_data;
215
- git_checkout_opts *opts = data->checkout_opts;
216
- bool do_checkout = false, do_notify = false;
1019
+ git_diff_delta *delta;
1020
+ size_t i;
1021
+
1022
+ git_vector_foreach(&data->diff->deltas, i, delta) {
1023
+ if (actions[i] & CHECKOUT_ACTION__DEFER_REMOVE) {
1024
+ /* this had a blocker directory that should only be removed iff
1025
+ * all of the contents of the directory were safely removed
1026
+ */
1027
+ if ((error = checkout_deferred_remove(
1028
+ data->repo, delta->old_file.path)) < 0)
1029
+ return error;
1030
+ }
217
1031
 
218
- GIT_UNUSED(progress);
219
- data->stats->processed++;
1032
+ if (actions[i] & CHECKOUT_ACTION__UPDATE_BLOB) {
1033
+ error = checkout_blob(data, &delta->new_file);
1034
+ if (error < 0)
1035
+ return error;
220
1036
 
221
- if (delta->status == GIT_DELTA_MODIFIED ||
222
- delta->status == GIT_DELTA_TYPECHANGE)
223
- {
224
- if ((opts->checkout_strategy & GIT_CHECKOUT_OVERWRITE_MODIFIED) != 0)
225
- do_checkout = true;
226
- else if (opts->skipped_notify_cb != NULL)
227
- do_notify = !data->create_submodules;
228
- }
229
- else if (delta->status == GIT_DELTA_DELETED &&
230
- (opts->checkout_strategy & GIT_CHECKOUT_CREATE_MISSING) != 0)
231
- do_checkout = true;
232
-
233
- if (do_notify) {
234
- if (opts->skipped_notify_cb(
235
- delta->old_file.path, &delta->old_file.oid,
236
- delta->old_file.mode, opts->notify_payload))
237
- {
238
- giterr_clear();
239
- error = GIT_EUSER;
1037
+ data->completed_steps++;
1038
+ report_progress(data, delta->new_file.path);
240
1039
  }
241
1040
  }
242
1041
 
243
- if (do_checkout) {
244
- bool is_submodule = S_ISGITLINK(delta->old_file.mode);
1042
+ return 0;
1043
+ }
245
1044
 
246
- if (is_submodule)
247
- data->found_submodules = true;
1045
+ static int checkout_create_submodules(
1046
+ unsigned int *actions,
1047
+ checkout_data *data)
1048
+ {
1049
+ int error = 0;
1050
+ git_diff_delta *delta;
1051
+ size_t i;
248
1052
 
249
- if (!is_submodule && !data->create_submodules)
250
- error = checkout_blob(data, &delta->old_file);
1053
+ /* initial reload of submodules if .gitmodules was changed */
1054
+ if (data->reload_submodules &&
1055
+ (error = git_submodule_reload_all(data->repo)) < 0)
1056
+ return error;
1057
+
1058
+ git_vector_foreach(&data->diff->deltas, i, delta) {
1059
+ if (actions[i] & CHECKOUT_ACTION__DEFER_REMOVE) {
1060
+ /* this has a blocker directory that should only be removed iff
1061
+ * all of the contents of the directory were safely removed
1062
+ */
1063
+ if ((error = checkout_deferred_remove(
1064
+ data->repo, delta->old_file.path)) < 0)
1065
+ return error;
1066
+ }
251
1067
 
252
- else if (is_submodule && data->create_submodules)
253
- error = checkout_submodule(data, &delta->old_file);
1068
+ if (actions[i] & CHECKOUT_ACTION__UPDATE_SUBMODULE) {
1069
+ int error = checkout_submodule(data, &delta->new_file);
1070
+ if (error < 0)
1071
+ return error;
1072
+
1073
+ data->completed_steps++;
1074
+ report_progress(data, delta->new_file.path);
1075
+ }
254
1076
  }
255
1077
 
256
- if (error)
257
- data->error = error;
1078
+ /* final reload once submodules have been updated */
1079
+ return git_submodule_reload_all(data->repo);
1080
+ }
1081
+
1082
+ static int checkout_lookup_head_tree(git_tree **out, git_repository *repo)
1083
+ {
1084
+ int error = 0;
1085
+ git_reference *ref = NULL;
1086
+ git_object *head;
1087
+
1088
+ if (!(error = git_repository_head(&ref, repo)) &&
1089
+ !(error = git_reference_peel(&head, ref, GIT_OBJ_TREE)))
1090
+ *out = (git_tree *)head;
1091
+
1092
+ git_reference_free(ref);
258
1093
 
259
1094
  return error;
260
1095
  }
261
1096
 
262
- static int retrieve_symlink_capabilities(git_repository *repo, bool *can_symlink)
1097
+ static void checkout_data_clear(checkout_data *data)
263
1098
  {
264
- git_config *cfg;
265
- int error;
1099
+ if (data->opts_free_baseline) {
1100
+ git_tree_free(data->opts.baseline);
1101
+ data->opts.baseline = NULL;
1102
+ }
266
1103
 
267
- if (git_repository_config__weakptr(&cfg, repo) < 0)
268
- return -1;
1104
+ git_vector_free(&data->removes);
1105
+ git_pool_clear(&data->pool);
269
1106
 
270
- error = git_config_get_bool((int *)can_symlink, cfg, "core.symlinks");
1107
+ git__free(data->pfx);
1108
+ data->pfx = NULL;
271
1109
 
272
- /*
273
- * When no "core.symlinks" entry is found in any of the configuration
274
- * store (local, global or system), default value is "true".
275
- */
276
- if (error == GIT_ENOTFOUND) {
277
- *can_symlink = true;
278
- error = 0;
279
- }
1110
+ git_buf_free(&data->path);
280
1111
 
281
- return error;
1112
+ git_index_free(data->index);
1113
+ data->index = NULL;
282
1114
  }
283
1115
 
284
- static void normalize_options(git_checkout_opts *normalized, git_checkout_opts *proposed)
1116
+ static int checkout_data_init(
1117
+ checkout_data *data,
1118
+ git_iterator *target,
1119
+ git_checkout_opts *proposed)
285
1120
  {
286
- assert(normalized);
1121
+ int error = 0;
1122
+ git_config *cfg;
1123
+ git_repository *repo = git_iterator_owner(target);
1124
+
1125
+ memset(data, 0, sizeof(*data));
1126
+
1127
+ if (!repo) {
1128
+ giterr_set(GITERR_CHECKOUT, "Cannot checkout nothing");
1129
+ return -1;
1130
+ }
1131
+
1132
+ if ((error = git_repository__ensure_not_bare(repo, "checkout")) < 0)
1133
+ return error;
1134
+
1135
+ if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
1136
+ return error;
1137
+
1138
+ data->repo = repo;
1139
+
1140
+ GITERR_CHECK_VERSION(
1141
+ proposed, GIT_CHECKOUT_OPTS_VERSION, "git_checkout_opts");
287
1142
 
288
1143
  if (!proposed)
289
- memset(normalized, 0, sizeof(git_checkout_opts));
1144
+ GIT_INIT_STRUCTURE(&data->opts, GIT_CHECKOUT_OPTS_VERSION);
290
1145
  else
291
- memmove(normalized, proposed, sizeof(git_checkout_opts));
1146
+ memmove(&data->opts, proposed, sizeof(git_checkout_opts));
1147
+
1148
+ /* refresh config and index content unless NO_REFRESH is given */
1149
+ if ((data->opts.checkout_strategy & GIT_CHECKOUT_NO_REFRESH) == 0) {
1150
+ if ((error = git_config_refresh(cfg)) < 0)
1151
+ goto cleanup;
292
1152
 
293
- /* Default options */
294
- if (!normalized->checkout_strategy)
295
- normalized->checkout_strategy = GIT_CHECKOUT_DEFAULT;
1153
+ /* if we are checking out the index, don't reload,
1154
+ * otherwise get index and force reload
1155
+ */
1156
+ if ((data->index = git_iterator_get_index(target)) != NULL) {
1157
+ GIT_REFCOUNT_INC(data->index);
1158
+ } else {
1159
+ /* otherwise, grab and reload the index */
1160
+ if ((error = git_repository_index(&data->index, data->repo)) < 0 ||
1161
+ (error = git_index_read(data->index)) < 0)
1162
+ goto cleanup;
1163
+
1164
+ /* clear the REUC when doing a tree or commit checkout */
1165
+ git_index_reuc_clear(data->index);
1166
+ }
1167
+ }
1168
+
1169
+ /* if you are forcing, definitely allow safe updates */
1170
+ if ((data->opts.checkout_strategy & GIT_CHECKOUT_FORCE) != 0)
1171
+ data->opts.checkout_strategy |= GIT_CHECKOUT_SAFE_CREATE;
1172
+ if ((data->opts.checkout_strategy & GIT_CHECKOUT_SAFE_CREATE) != 0)
1173
+ data->opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
1174
+
1175
+ data->strategy = data->opts.checkout_strategy;
296
1176
 
297
1177
  /* opts->disable_filters is false by default */
298
- if (!normalized->dir_mode)
299
- normalized->dir_mode = GIT_DIR_MODE;
300
1178
 
301
- if (!normalized->file_open_flags)
302
- normalized->file_open_flags = O_CREAT | O_TRUNC | O_WRONLY;
303
- }
1179
+ if (!data->opts.dir_mode)
1180
+ data->opts.dir_mode = GIT_DIR_MODE;
304
1181
 
305
- int git_checkout_index(
306
- git_repository *repo,
307
- git_checkout_opts *opts,
308
- git_indexer_stats *stats)
309
- {
310
- git_diff_list *diff = NULL;
311
- git_indexer_stats dummy_stats;
1182
+ if (!data->opts.file_open_flags)
1183
+ data->opts.file_open_flags = O_CREAT | O_TRUNC | O_WRONLY;
312
1184
 
313
- git_diff_options diff_opts = {0};
314
- git_checkout_opts checkout_opts;
1185
+ data->pfx = git_pathspec_prefix(&data->opts.paths);
315
1186
 
316
- struct checkout_diff_data data;
317
- git_buf workdir = GIT_BUF_INIT;
1187
+ error = git_config_get_bool(&data->can_symlink, cfg, "core.symlinks");
1188
+ if (error < 0) {
1189
+ if (error != GIT_ENOTFOUND)
1190
+ goto cleanup;
318
1191
 
319
- int error;
1192
+ /* If "core.symlinks" is not found anywhere, default to true. */
1193
+ data->can_symlink = true;
1194
+ giterr_clear();
1195
+ error = 0;
1196
+ }
320
1197
 
321
- assert(repo);
1198
+ if (!data->opts.baseline) {
1199
+ data->opts_free_baseline = true;
1200
+ error = checkout_lookup_head_tree(&data->opts.baseline, repo);
322
1201
 
323
- if ((error = git_repository__ensure_not_bare(repo, "checkout")) < 0)
1202
+ if (error == GIT_EORPHANEDHEAD) {
1203
+ error = 0;
1204
+ giterr_clear();
1205
+ }
1206
+
1207
+ if (error < 0)
1208
+ goto cleanup;
1209
+ }
1210
+
1211
+ if ((error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 ||
1212
+ (error = git_pool_init(&data->pool, 1, 0)) < 0 ||
1213
+ (error = git_buf_puts(&data->path, git_repository_workdir(repo))) < 0)
1214
+ goto cleanup;
1215
+
1216
+ data->workdir_len = git_buf_len(&data->path);
1217
+
1218
+ cleanup:
1219
+ if (error < 0)
1220
+ checkout_data_clear(data);
1221
+
1222
+ return error;
1223
+ }
1224
+
1225
+ int git_checkout_iterator(
1226
+ git_iterator *target,
1227
+ git_checkout_opts *opts)
1228
+ {
1229
+ int error = 0;
1230
+ git_iterator *baseline = NULL, *workdir = NULL;
1231
+ checkout_data data = {0};
1232
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
1233
+ uint32_t *actions = NULL;
1234
+ size_t *counts = NULL;
1235
+ git_iterator_flag_t iterflags = 0;
1236
+
1237
+ /* initialize structures and options */
1238
+ error = checkout_data_init(&data, target, opts);
1239
+ if (error < 0)
324
1240
  return error;
325
1241
 
326
1242
  diff_opts.flags =
1243
+ GIT_DIFF_INCLUDE_UNMODIFIED |
327
1244
  GIT_DIFF_INCLUDE_UNTRACKED |
1245
+ GIT_DIFF_RECURSE_UNTRACKED_DIRS | /* needed to match baseline */
1246
+ GIT_DIFF_INCLUDE_IGNORED |
328
1247
  GIT_DIFF_INCLUDE_TYPECHANGE |
1248
+ GIT_DIFF_INCLUDE_TYPECHANGE_TREES |
329
1249
  GIT_DIFF_SKIP_BINARY_CHECK;
1250
+ if (data.opts.checkout_strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH)
1251
+ diff_opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH;
1252
+ if (data.opts.paths.count > 0)
1253
+ diff_opts.pathspec = data.opts.paths;
1254
+
1255
+ /* set up iterators */
1256
+
1257
+ iterflags = git_iterator_ignore_case(target) ?
1258
+ GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE;
1259
+
1260
+ if ((error = git_iterator_reset(target, data.pfx, data.pfx)) < 0 ||
1261
+ (error = git_iterator_for_workdir(
1262
+ &workdir, data.repo, iterflags | GIT_ITERATOR_DONT_AUTOEXPAND,
1263
+ data.pfx, data.pfx)) < 0 ||
1264
+ (error = git_iterator_for_tree(
1265
+ &baseline, data.opts.baseline, iterflags, data.pfx, data.pfx)) < 0)
1266
+ goto cleanup;
330
1267
 
331
- if (opts && opts->paths.count > 0)
332
- diff_opts.pathspec = opts->paths;
1268
+ /* Should not have case insensitivity mismatch */
1269
+ assert(git_iterator_ignore_case(workdir) == git_iterator_ignore_case(baseline));
333
1270
 
334
- if ((error = git_diff_workdir_to_index(repo, &diff_opts, &diff)) < 0)
1271
+ /* Generate baseline-to-target diff which will include an entry for
1272
+ * every possible update that might need to be made.
1273
+ */
1274
+ if ((error = git_diff__from_iterators(
1275
+ &data.diff, data.repo, baseline, target, &diff_opts)) < 0)
335
1276
  goto cleanup;
336
1277
 
337
- if ((error = git_buf_puts(&workdir, git_repository_workdir(repo))) < 0)
1278
+ /* Loop through diff (and working directory iterator) building a list of
1279
+ * actions to be taken, plus look for conflicts and send notifications.
1280
+ */
1281
+ if ((error = checkout_get_actions(&actions, &counts, &data, workdir)) < 0)
338
1282
  goto cleanup;
339
1283
 
340
- normalize_options(&checkout_opts, opts);
1284
+ data.total_steps = counts[CHECKOUT_ACTION__REMOVE] +
1285
+ counts[CHECKOUT_ACTION__UPDATE_BLOB] +
1286
+ counts[CHECKOUT_ACTION__UPDATE_SUBMODULE];
341
1287
 
342
- if (!stats)
343
- stats = &dummy_stats;
1288
+ report_progress(&data, NULL); /* establish 0 baseline */
344
1289
 
345
- stats->processed = 0;
346
- /* total based on 3 passes, but it might be 2 if no submodules */
347
- stats->total = (unsigned int)git_diff_num_deltas(diff) * 3;
348
-
349
- memset(&data, 0, sizeof(data));
350
-
351
- data.path = &workdir;
352
- data.workdir_len = git_buf_len(&workdir);
353
- data.checkout_opts = &checkout_opts;
354
- data.stats = stats;
355
- data.owner = repo;
356
-
357
- if ((error = retrieve_symlink_capabilities(repo, &data.can_symlink)) < 0)
1290
+ /* To deal with some order dependencies, perform remaining checkout
1291
+ * in three passes: removes, then update blobs, then update submodules.
1292
+ */
1293
+ if (counts[CHECKOUT_ACTION__REMOVE] > 0 &&
1294
+ (error = checkout_remove_the_old(actions, &data)) < 0)
358
1295
  goto cleanup;
359
1296
 
360
- /* Checkout is best performed with three passes through the diff.
361
- *
362
- * 1. First do removes, because we iterate in alphabetical order, thus
363
- * a new untracked directory will end up sorted *after* a blob that
364
- * should be checked out with the same name.
365
- * 2. Then checkout all blobs.
366
- * 3. Then checkout all submodules in case a new .gitmodules blob was
367
- * checked out during pass #2.
368
- */
1297
+ if (counts[CHECKOUT_ACTION__UPDATE_BLOB] > 0 &&
1298
+ (error = checkout_create_the_new(actions, &data)) < 0)
1299
+ goto cleanup;
369
1300
 
370
- if (!(error = git_diff_foreach(
371
- diff, &data, checkout_remove_the_old, NULL, NULL)) &&
372
- !(error = git_diff_foreach(
373
- diff, &data, checkout_create_the_new, NULL, NULL)) &&
374
- data.found_submodules)
375
- {
376
- data.create_submodules = true;
377
- error = git_diff_foreach(
378
- diff, &data, checkout_create_the_new, NULL, NULL);
379
- }
1301
+ if (counts[CHECKOUT_ACTION__UPDATE_SUBMODULE] > 0 &&
1302
+ (error = checkout_create_submodules(actions, &data)) < 0)
1303
+ goto cleanup;
380
1304
 
381
- stats->processed = stats->total;
1305
+ assert(data.completed_steps == data.total_steps);
382
1306
 
383
1307
  cleanup:
384
1308
  if (error == GIT_EUSER)
385
- error = (data.error != 0) ? data.error : -1;
1309
+ giterr_clear();
1310
+
1311
+ if (!error && data.index != NULL &&
1312
+ (data.strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
1313
+ error = git_index_write(data.index);
386
1314
 
387
- git_diff_list_free(diff);
388
- git_buf_free(&workdir);
1315
+ git_diff_list_free(data.diff);
1316
+ git_iterator_free(workdir);
1317
+ git_iterator_free(baseline);
1318
+ git__free(actions);
1319
+ git__free(counts);
1320
+ checkout_data_clear(&data);
389
1321
 
390
1322
  return error;
391
1323
  }
392
1324
 
393
- int git_checkout_tree(
1325
+ int git_checkout_index(
394
1326
  git_repository *repo,
395
- git_object *treeish,
396
- git_checkout_opts *opts,
397
- git_indexer_stats *stats)
1327
+ git_index *index,
1328
+ git_checkout_opts *opts)
398
1329
  {
399
- git_index *index = NULL;
400
- git_tree *tree = NULL;
401
-
402
1330
  int error;
1331
+ git_iterator *index_i;
403
1332
 
404
- assert(repo && treeish);
1333
+ if ((error = git_repository__ensure_not_bare(repo, "checkout index")) < 0)
1334
+ return error;
405
1335
 
406
- if (git_object_peel((git_object **)&tree, treeish, GIT_OBJ_TREE) < 0) {
407
- giterr_set(GITERR_INVALID, "Provided treeish cannot be peeled into a tree.");
408
- return GIT_ERROR;
409
- }
1336
+ if (!index && (error = git_repository_index__weakptr(&index, repo)) < 0)
1337
+ return error;
1338
+ GIT_REFCOUNT_INC(index);
410
1339
 
411
- if ((error = git_repository_index(&index, repo)) < 0)
412
- goto cleanup;
1340
+ if (!(error = git_iterator_for_index(&index_i, index, 0, NULL, NULL)))
1341
+ error = git_checkout_iterator(index_i, opts);
413
1342
 
414
- if ((error = git_index_read_tree(index, tree, NULL)) < 0)
415
- goto cleanup;
1343
+ git_iterator_free(index_i);
1344
+ git_index_free(index);
416
1345
 
417
- if ((error = git_index_write(index)) < 0)
418
- goto cleanup;
1346
+ return error;
1347
+ }
419
1348
 
420
- error = git_checkout_index(repo, opts, stats);
1349
+ int git_checkout_tree(
1350
+ git_repository *repo,
1351
+ const git_object *treeish,
1352
+ git_checkout_opts *opts)
1353
+ {
1354
+ int error;
1355
+ git_tree *tree = NULL;
1356
+ git_iterator *tree_i = NULL;
421
1357
 
422
- cleanup:
423
- git_index_free(index);
1358
+ if ((error = git_repository__ensure_not_bare(repo, "checkout tree")) < 0)
1359
+ return error;
1360
+
1361
+ if (git_object_peel((git_object **)&tree, treeish, GIT_OBJ_TREE) < 0) {
1362
+ giterr_set(
1363
+ GITERR_CHECKOUT, "Provided object cannot be peeled to a tree");
1364
+ return -1;
1365
+ }
1366
+
1367
+ if (!(error = git_iterator_for_tree(&tree_i, tree, 0, NULL, NULL)))
1368
+ error = git_checkout_iterator(tree_i, opts);
1369
+
1370
+ git_iterator_free(tree_i);
424
1371
  git_tree_free(tree);
1372
+
425
1373
  return error;
426
1374
  }
427
1375
 
428
1376
  int git_checkout_head(
429
1377
  git_repository *repo,
430
- git_checkout_opts *opts,
431
- git_indexer_stats *stats)
1378
+ git_checkout_opts *opts)
432
1379
  {
433
- git_reference *head;
434
1380
  int error;
435
- git_object *tree = NULL;
436
-
437
- assert(repo);
1381
+ git_tree *head = NULL;
1382
+ git_iterator *head_i = NULL;
438
1383
 
439
- if ((error = git_repository_head(&head, repo)) < 0)
1384
+ if ((error = git_repository__ensure_not_bare(repo, "checkout head")) < 0)
440
1385
  return error;
441
-
442
- if ((error = git_reference_peel(&tree, head, GIT_OBJ_TREE)) < 0)
443
- goto cleanup;
444
1386
 
445
- error = git_checkout_tree(repo, tree, opts, stats);
1387
+ if (!(error = checkout_lookup_head_tree(&head, repo)) &&
1388
+ !(error = git_iterator_for_tree(&head_i, head, 0, NULL, NULL)))
1389
+ error = git_checkout_iterator(head_i, opts);
446
1390
 
447
- cleanup:
448
- git_reference_free(head);
449
- git_object_free(tree);
1391
+ git_iterator_free(head_i);
1392
+ git_tree_free(head);
450
1393
 
451
1394
  return error;
452
1395
  }