rugged 0.17.0.b7 → 0.18.0.b1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  }