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
@@ -0,0 +1,44 @@
1
+ /*
2
+ * Copyright (C) the libgit2 contributors. All rights reserved.
3
+ *
4
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
5
+ * a Linking Exception. For full terms see the included COPYING file.
6
+ */
7
+ #ifndef INCLUDE_revwalk_h__
8
+ #define INCLUDE_revwalk_h__
9
+
10
+ #include "git2/revwalk.h"
11
+ #include "oidmap.h"
12
+ #include "commit_list.h"
13
+ #include "pqueue.h"
14
+ #include "pool.h"
15
+ #include "vector.h"
16
+
17
+ GIT__USE_OIDMAP;
18
+
19
+ struct git_revwalk {
20
+ git_repository *repo;
21
+ git_odb *odb;
22
+
23
+ git_oidmap *commits;
24
+ git_pool commit_pool;
25
+
26
+ git_commit_list *iterator_topo;
27
+ git_commit_list *iterator_rand;
28
+ git_commit_list *iterator_reverse;
29
+ git_pqueue iterator_time;
30
+
31
+ int (*get_next)(git_commit_list_node **, git_revwalk *);
32
+ int (*enqueue)(git_revwalk *, git_commit_list_node *);
33
+
34
+ unsigned walking:1;
35
+ unsigned int sorting;
36
+
37
+ /* merge base calculation */
38
+ git_commit_list_node *one;
39
+ git_vector twos;
40
+ };
41
+
42
+ git_commit_list_node *git_revwalk__commit_lookup(git_revwalk *walk, const git_oid *oid);
43
+
44
+ #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.
@@ -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.
@@ -22,62 +22,28 @@ void git_signature_free(git_signature *sig)
22
22
  git__free(sig);
23
23
  }
24
24
 
25
- static const char *skip_leading_spaces(const char *buffer, const char *buffer_end)
26
- {
27
- while (*buffer == ' ' && buffer < buffer_end)
28
- buffer++;
29
-
30
- return buffer;
31
- }
32
-
33
- static const char *skip_trailing_spaces(const char *buffer_start, const char *buffer_end)
34
- {
35
- while (*buffer_end == ' ' && buffer_end > buffer_start)
36
- buffer_end--;
37
-
38
- return buffer_end;
39
- }
40
-
41
25
  static int signature_error(const char *msg)
42
26
  {
43
- giterr_set(GITERR_INVALID, "Failed to process signature - %s", msg);
27
+ giterr_set(GITERR_INVALID, "Failed to parse signature - %s", msg);
44
28
  return -1;
45
29
  }
46
30
 
47
- static int process_trimming(const char *input, char **storage, const char *input_end, int fail_when_empty)
31
+ static bool contains_angle_brackets(const char *input)
48
32
  {
49
- const char *left, *right;
50
- size_t trimmed_input_length;
51
-
52
- assert(storage);
53
-
54
- left = skip_leading_spaces(input, input_end);
55
- right = skip_trailing_spaces(input, input_end - 1);
56
-
57
- if (right < left) {
58
- if (fail_when_empty)
59
- return signature_error("input is either empty of contains only spaces");
60
-
61
- right = left - 1;
62
- }
63
-
64
- trimmed_input_length = right - left + 1;
65
-
66
- *storage = git__malloc(trimmed_input_length + 1);
67
- GITERR_CHECK_ALLOC(*storage);
68
-
69
- memcpy(*storage, left, trimmed_input_length);
70
- (*storage)[trimmed_input_length] = 0;
71
-
72
- return 0;
33
+ return strchr(input, '<') != NULL || strchr(input, '>') != NULL;
73
34
  }
74
35
 
75
- static bool contains_angle_brackets(const char *input)
36
+ static char *extract_trimmed(const char *ptr, size_t len)
76
37
  {
77
- if (strchr(input, '<') != NULL)
78
- return true;
38
+ while (len && ptr[0] == ' ') {
39
+ ptr++; len--;
40
+ }
79
41
 
80
- return strchr(input, '>') != NULL;
42
+ while (len && ptr[len - 1] == ' ') {
43
+ len--;
44
+ }
45
+
46
+ return git__substrdup(ptr, len);
81
47
  }
82
48
 
83
49
  int git_signature_new(git_signature **sig_out, const char *name, const char *email, git_time_t time, int offset)
@@ -88,28 +54,28 @@ int git_signature_new(git_signature **sig_out, const char *name, const char *ema
88
54
 
89
55
  *sig_out = NULL;
90
56
 
57
+ if (contains_angle_brackets(name) ||
58
+ contains_angle_brackets(email)) {
59
+ return signature_error(
60
+ "Neither `name` nor `email` should contain angle brackets chars.");
61
+ }
62
+
91
63
  p = git__calloc(1, sizeof(git_signature));
92
64
  GITERR_CHECK_ALLOC(p);
93
65
 
94
- if (process_trimming(name, &p->name, name + strlen(name), 1) < 0 ||
95
- process_trimming(email, &p->email, email + strlen(email), 1) < 0)
96
- {
66
+ p->name = extract_trimmed(name, strlen(name));
67
+ p->email = extract_trimmed(email, strlen(email));
68
+
69
+ if (p->name == NULL || p->email == NULL ||
70
+ p->name[0] == '\0' || p->email[0] == '\0') {
97
71
  git_signature_free(p);
98
72
  return -1;
99
73
  }
100
74
 
101
- if (contains_angle_brackets(p->email) ||
102
- contains_angle_brackets(p->name))
103
- {
104
- git_signature_free(p);
105
- return signature_error("Neither `name` nor `email` should contain angle brackets chars.");
106
- }
107
-
108
75
  p->when.time = time;
109
76
  p->when.offset = offset;
110
77
 
111
78
  *sig_out = p;
112
-
113
79
  return 0;
114
80
  }
115
81
 
@@ -153,169 +119,71 @@ int git_signature_now(git_signature **sig_out, const char *name, const char *ema
153
119
  return 0;
154
120
  }
155
121
 
156
- static int timezone_error(const char *msg)
157
- {
158
- giterr_set(GITERR_INVALID, "Failed to parse TZ offset - %s", msg);
159
- return -1;
160
- }
161
-
162
- static int parse_timezone_offset(const char *buffer, int *offset_out)
163
- {
164
- int dec_offset;
165
- int mins, hours, offset;
166
-
167
- const char *offset_start;
168
- const char *offset_end;
169
-
170
- offset_start = buffer;
171
-
172
- if (*offset_start == '\n') {
173
- *offset_out = 0;
174
- return 0;
175
- }
176
-
177
- if (offset_start[0] != '-' && offset_start[0] != '+')
178
- return timezone_error("does not start with '+' or '-'");
179
-
180
- if (offset_start[1] < '0' || offset_start[1] > '9')
181
- return timezone_error("expected initial digit");
182
-
183
- if (git__strtol32(&dec_offset, offset_start + 1, &offset_end, 10) < 0)
184
- return timezone_error("not a valid number");
185
-
186
- if (offset_end - offset_start != 5)
187
- return timezone_error("invalid length");
188
-
189
- if (dec_offset > 1400)
190
- return timezone_error("value too large");
191
-
192
- hours = dec_offset / 100;
193
- mins = dec_offset % 100;
194
-
195
- if (hours > 14) // see http://www.worldtimezone.com/faq.html
196
- return timezone_error("hour value too large");
197
-
198
- if (mins > 59)
199
- return timezone_error("minutes value too large");
200
-
201
- offset = (hours * 60) + mins;
202
-
203
- if (offset_start[0] == '-')
204
- offset *= -1;
205
-
206
- *offset_out = offset;
207
-
208
- return 0;
209
- }
210
-
211
- static int process_next_token(const char **buffer_out, char **storage,
212
- const char *token_end, const char *right_boundary)
213
- {
214
- int error = process_trimming(*buffer_out, storage, token_end, 0);
215
- if (error < 0)
216
- return error;
217
-
218
- *buffer_out = token_end + 1;
219
-
220
- if (*buffer_out > right_boundary)
221
- return signature_error("signature is too short");
222
-
223
- return 0;
224
- }
225
-
226
- static const char *scan_for_previous_token(const char *buffer, const char *left_boundary)
227
- {
228
- const char *start;
229
-
230
- if (buffer <= left_boundary)
231
- return NULL;
232
-
233
- start = skip_trailing_spaces(left_boundary, buffer);
234
-
235
- /* Search for previous occurence of space */
236
- while (start[-1] != ' ' && start > left_boundary)
237
- start--;
238
-
239
- return start;
240
- }
241
-
242
- static int parse_time(git_time_t *time_out, const char *buffer)
243
- {
244
- int time;
245
- int error;
246
-
247
- if (*buffer == '+' || *buffer == '-') {
248
- giterr_set(GITERR_INVALID, "Failed while parsing time. '%s' actually looks like a timezone offset.", buffer);
249
- return -1;
250
- }
251
-
252
- error = git__strtol32(&time, buffer, &buffer, 10);
253
-
254
- if (!error)
255
- *time_out = (git_time_t)time;
256
-
257
- return error;
258
- }
259
-
260
122
  int git_signature__parse(git_signature *sig, const char **buffer_out,
261
123
  const char *buffer_end, const char *header, char ender)
262
124
  {
263
125
  const char *buffer = *buffer_out;
264
- const char *line_end, *name_end, *email_end, *tz_start, *time_start;
265
- int error = 0;
126
+ const char *email_start, *email_end;
266
127
 
267
- memset(sig, 0x0, sizeof(git_signature));
128
+ memset(sig, 0, sizeof(git_signature));
268
129
 
269
- if ((line_end = memchr(buffer, ender, buffer_end - buffer)) == NULL)
130
+ if ((buffer_end = memchr(buffer, ender, buffer_end - buffer)) == NULL)
270
131
  return signature_error("no newline given");
271
132
 
272
133
  if (header) {
273
134
  const size_t header_len = strlen(header);
274
135
 
275
- if (memcmp(buffer, header, header_len) != 0)
136
+ if (buffer + header_len >= buffer_end || memcmp(buffer, header, header_len) != 0)
276
137
  return signature_error("expected prefix doesn't match actual");
277
138
 
278
139
  buffer += header_len;
279
140
  }
280
141
 
281
- if (buffer > line_end)
282
- return signature_error("signature too short");
283
-
284
- if ((name_end = strchr(buffer, '<')) == NULL)
285
- return signature_error("character '<' not allowed in signature");
142
+ email_start = git__memrchr(buffer, '<', buffer_end - buffer);
143
+ email_end = git__memrchr(buffer, '>', buffer_end - buffer);
286
144
 
287
- if ((email_end = strchr(name_end, '>')) == NULL)
288
- return signature_error("character '>' not allowed in signature");
289
-
290
- if (email_end < name_end)
145
+ if (!email_start || !email_end || email_end <= email_start)
291
146
  return signature_error("malformed e-mail");
292
147
 
293
- error = process_next_token(&buffer, &sig->name, name_end, line_end);
294
- if (error < 0)
295
- return error;
296
-
297
- error = process_next_token(&buffer, &sig->email, email_end, line_end);
298
- if (error < 0)
299
- return error;
300
-
301
- tz_start = scan_for_previous_token(line_end - 1, buffer);
302
-
303
- if (tz_start == NULL)
304
- goto clean_exit; /* No timezone nor date */
305
-
306
- time_start = scan_for_previous_token(tz_start - 1, buffer);
307
- if (time_start == NULL || parse_time(&sig->when.time, time_start) < 0) {
308
- /* The tz_start might point at the time */
309
- parse_time(&sig->when.time, tz_start);
310
- goto clean_exit;
311
- }
312
-
313
- if (parse_timezone_offset(tz_start, &sig->when.offset) < 0) {
314
- sig->when.time = 0; /* Bogus timezone, we reset the time */
148
+ email_start += 1;
149
+ sig->name = extract_trimmed(buffer, email_start - buffer - 1);
150
+ sig->email = extract_trimmed(email_start, email_end - email_start);
151
+
152
+ /* Do we even have a time at the end of the signature? */
153
+ if (email_end + 2 < buffer_end) {
154
+ const char *time_start = email_end + 2;
155
+ const char *time_end;
156
+
157
+ if (git__strtol64(&sig->when.time, time_start, &time_end, 10) < 0)
158
+ return signature_error("invalid Unix timestamp");
159
+
160
+ /* do we have a timezone? */
161
+ if (time_end + 1 < buffer_end) {
162
+ int offset, hours, mins;
163
+ const char *tz_start, *tz_end;
164
+
165
+ tz_start = time_end + 1;
166
+
167
+ if ((tz_start[0] != '-' && tz_start[0] != '+') ||
168
+ git__strtol32(&offset, tz_start + 1, &tz_end, 10) < 0)
169
+ return signature_error("malformed timezone");
170
+
171
+ hours = offset / 100;
172
+ mins = offset % 100;
173
+
174
+ /*
175
+ * only store timezone if it's not overflowing;
176
+ * see http://www.worldtimezone.com/faq.html
177
+ */
178
+ if (hours < 14 && mins < 59) {
179
+ sig->when.offset = (hours * 60) + mins;
180
+ if (tz_start[0] == '-')
181
+ sig->when.offset = -sig->when.offset;
182
+ }
183
+ }
315
184
  }
316
185
 
317
- clean_exit:
318
- *buffer_out = line_end + 1;
186
+ *buffer_out = buffer_end + 1;
319
187
  return 0;
320
188
  }
321
189
 
@@ -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.
@@ -0,0 +1,663 @@
1
+ /*
2
+ * Copyright (C) the libgit2 contributors. All rights reserved.
3
+ *
4
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
5
+ * a Linking Exception. For full terms see the included COPYING file.
6
+ */
7
+
8
+ #include "common.h"
9
+ #include "repository.h"
10
+ #include "commit.h"
11
+ #include "tree.h"
12
+ #include "reflog.h"
13
+ #include "git2/diff.h"
14
+ #include "git2/stash.h"
15
+ #include "git2/status.h"
16
+ #include "git2/checkout.h"
17
+ #include "signature.h"
18
+
19
+ static int create_error(int error, const char *msg)
20
+ {
21
+ giterr_set(GITERR_STASH, "Cannot stash changes - %s", msg);
22
+ return error;
23
+ }
24
+
25
+ static int retrieve_head(git_reference **out, git_repository *repo)
26
+ {
27
+ int error = git_repository_head(out, repo);
28
+
29
+ if (error == GIT_EORPHANEDHEAD)
30
+ return create_error(error, "You do not have the initial commit yet.");
31
+
32
+ return error;
33
+ }
34
+
35
+ static int append_abbreviated_oid(git_buf *out, const git_oid *b_commit)
36
+ {
37
+ char *formatted_oid;
38
+
39
+ formatted_oid = git_oid_allocfmt(b_commit);
40
+ GITERR_CHECK_ALLOC(formatted_oid);
41
+
42
+ git_buf_put(out, formatted_oid, 7);
43
+ git__free(formatted_oid);
44
+
45
+ return git_buf_oom(out) ? -1 : 0;
46
+ }
47
+
48
+ static int append_commit_description(git_buf *out, git_commit* commit)
49
+ {
50
+ const char *message;
51
+ size_t pos = 0, len;
52
+
53
+ if (append_abbreviated_oid(out, git_commit_id(commit)) < 0)
54
+ return -1;
55
+
56
+ message = git_commit_message(commit);
57
+ len = strlen(message);
58
+
59
+ /* TODO: Replace with proper commit short message
60
+ * when git_commit_message_short() is implemented.
61
+ */
62
+ while (pos < len && message[pos] != '\n')
63
+ pos++;
64
+
65
+ git_buf_putc(out, ' ');
66
+ git_buf_put(out, message, pos);
67
+ git_buf_putc(out, '\n');
68
+
69
+ return git_buf_oom(out) ? -1 : 0;
70
+ }
71
+
72
+ static int retrieve_base_commit_and_message(
73
+ git_commit **b_commit,
74
+ git_buf *stash_message,
75
+ git_repository *repo)
76
+ {
77
+ git_reference *head = NULL;
78
+ int error;
79
+
80
+ if ((error = retrieve_head(&head, repo)) < 0)
81
+ return error;
82
+
83
+ if (strcmp("HEAD", git_reference_name(head)) == 0)
84
+ error = git_buf_puts(stash_message, "(no branch): ");
85
+ else
86
+ error = git_buf_printf(
87
+ stash_message,
88
+ "%s: ",
89
+ git_reference_name(head) + strlen(GIT_REFS_HEADS_DIR));
90
+ if (error < 0)
91
+ goto cleanup;
92
+
93
+ if ((error = git_commit_lookup(
94
+ b_commit, repo, git_reference_target(head))) < 0)
95
+ goto cleanup;
96
+
97
+ if ((error = append_commit_description(stash_message, *b_commit)) < 0)
98
+ goto cleanup;
99
+
100
+ cleanup:
101
+ git_reference_free(head);
102
+ return error;
103
+ }
104
+
105
+ static int build_tree_from_index(git_tree **out, git_index *index)
106
+ {
107
+ int error;
108
+ git_oid i_tree_oid;
109
+
110
+ if ((error = git_index_write_tree(&i_tree_oid, index)) < 0)
111
+ return -1;
112
+
113
+ return git_tree_lookup(out, git_index_owner(index), &i_tree_oid);
114
+ }
115
+
116
+ static int commit_index(
117
+ git_commit **i_commit,
118
+ git_index *index,
119
+ git_signature *stasher,
120
+ const char *message,
121
+ const git_commit *parent)
122
+ {
123
+ git_tree *i_tree = NULL;
124
+ git_oid i_commit_oid;
125
+ git_buf msg = GIT_BUF_INIT;
126
+ int error;
127
+
128
+ if ((error = build_tree_from_index(&i_tree, index)) < 0)
129
+ goto cleanup;
130
+
131
+ if ((error = git_buf_printf(&msg, "index on %s\n", message)) < 0)
132
+ goto cleanup;
133
+
134
+ if ((error = git_commit_create(
135
+ &i_commit_oid,
136
+ git_index_owner(index),
137
+ NULL,
138
+ stasher,
139
+ stasher,
140
+ NULL,
141
+ git_buf_cstr(&msg),
142
+ i_tree,
143
+ 1,
144
+ &parent)) < 0)
145
+ goto cleanup;
146
+
147
+ error = git_commit_lookup(i_commit, git_index_owner(index), &i_commit_oid);
148
+
149
+ cleanup:
150
+ git_tree_free(i_tree);
151
+ git_buf_free(&msg);
152
+ return error;
153
+ }
154
+
155
+ struct cb_data {
156
+ git_index *index;
157
+
158
+ int error;
159
+
160
+ bool include_changed;
161
+ bool include_untracked;
162
+ bool include_ignored;
163
+ };
164
+
165
+ static int update_index_cb(
166
+ const git_diff_delta *delta,
167
+ float progress,
168
+ void *payload)
169
+ {
170
+ struct cb_data *data = (struct cb_data *)payload;
171
+ const char *add_path = NULL;
172
+
173
+ GIT_UNUSED(progress);
174
+
175
+ switch (delta->status) {
176
+ case GIT_DELTA_IGNORED:
177
+ if (data->include_ignored)
178
+ add_path = delta->new_file.path;
179
+ break;
180
+
181
+ case GIT_DELTA_UNTRACKED:
182
+ if (data->include_untracked)
183
+ add_path = delta->new_file.path;
184
+ break;
185
+
186
+ case GIT_DELTA_ADDED:
187
+ case GIT_DELTA_MODIFIED:
188
+ if (data->include_changed)
189
+ add_path = delta->new_file.path;
190
+ break;
191
+
192
+ case GIT_DELTA_DELETED:
193
+ if (!data->include_changed)
194
+ break;
195
+ if (git_index_find(NULL, data->index, delta->old_file.path) == 0)
196
+ data->error = git_index_remove(
197
+ data->index, delta->old_file.path, 0);
198
+ break;
199
+
200
+ default:
201
+ /* Unimplemented */
202
+ giterr_set(
203
+ GITERR_INVALID,
204
+ "Cannot update index. Unimplemented status (%d)",
205
+ delta->status);
206
+ data->error = -1;
207
+ break;
208
+ }
209
+
210
+ if (add_path != NULL)
211
+ data->error = git_index_add_bypath(data->index, add_path);
212
+
213
+ return data->error;
214
+ }
215
+
216
+ static int build_untracked_tree(
217
+ git_tree **tree_out,
218
+ git_index *index,
219
+ git_commit *i_commit,
220
+ uint32_t flags)
221
+ {
222
+ git_tree *i_tree = NULL;
223
+ git_diff_list *diff = NULL;
224
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
225
+ struct cb_data data = {0};
226
+ int error;
227
+
228
+ git_index_clear(index);
229
+
230
+ data.index = index;
231
+
232
+ if (flags & GIT_STASH_INCLUDE_UNTRACKED) {
233
+ opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED |
234
+ GIT_DIFF_RECURSE_UNTRACKED_DIRS;
235
+ data.include_untracked = true;
236
+ }
237
+
238
+ if (flags & GIT_STASH_INCLUDE_IGNORED) {
239
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED;
240
+ data.include_ignored = true;
241
+ }
242
+
243
+ if ((error = git_commit_tree(&i_tree, i_commit)) < 0)
244
+ goto cleanup;
245
+
246
+ if ((error = git_diff_tree_to_workdir(
247
+ &diff, git_index_owner(index), i_tree, &opts)) < 0)
248
+ goto cleanup;
249
+
250
+ if ((error = git_diff_foreach(
251
+ diff, update_index_cb, NULL, NULL, &data)) < 0)
252
+ {
253
+ if (error == GIT_EUSER)
254
+ error = data.error;
255
+ goto cleanup;
256
+ }
257
+
258
+ error = build_tree_from_index(tree_out, index);
259
+
260
+ cleanup:
261
+ git_diff_list_free(diff);
262
+ git_tree_free(i_tree);
263
+ return error;
264
+ }
265
+
266
+ static int commit_untracked(
267
+ git_commit **u_commit,
268
+ git_index *index,
269
+ git_signature *stasher,
270
+ const char *message,
271
+ git_commit *i_commit,
272
+ uint32_t flags)
273
+ {
274
+ git_tree *u_tree = NULL;
275
+ git_oid u_commit_oid;
276
+ git_buf msg = GIT_BUF_INIT;
277
+ int error;
278
+
279
+ if ((error = build_untracked_tree(&u_tree, index, i_commit, flags)) < 0)
280
+ goto cleanup;
281
+
282
+ if ((error = git_buf_printf(&msg, "untracked files on %s\n", message)) < 0)
283
+ goto cleanup;
284
+
285
+ if ((error = git_commit_create(
286
+ &u_commit_oid,
287
+ git_index_owner(index),
288
+ NULL,
289
+ stasher,
290
+ stasher,
291
+ NULL,
292
+ git_buf_cstr(&msg),
293
+ u_tree,
294
+ 0,
295
+ NULL)) < 0)
296
+ goto cleanup;
297
+
298
+ error = git_commit_lookup(u_commit, git_index_owner(index), &u_commit_oid);
299
+
300
+ cleanup:
301
+ git_tree_free(u_tree);
302
+ git_buf_free(&msg);
303
+ return error;
304
+ }
305
+
306
+ static int build_workdir_tree(
307
+ git_tree **tree_out,
308
+ git_index *index,
309
+ git_commit *b_commit)
310
+ {
311
+ git_repository *repo = git_index_owner(index);
312
+ git_tree *b_tree = NULL;
313
+ git_diff_list *diff = NULL, *diff2 = NULL;
314
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
315
+ struct cb_data data = {0};
316
+ int error;
317
+
318
+ if ((error = git_commit_tree(&b_tree, b_commit)) < 0)
319
+ goto cleanup;
320
+
321
+ if ((error = git_diff_tree_to_index(&diff, repo, b_tree, NULL, &opts)) < 0)
322
+ goto cleanup;
323
+
324
+ if ((error = git_diff_index_to_workdir(&diff2, repo, NULL, &opts)) < 0)
325
+ goto cleanup;
326
+
327
+ if ((error = git_diff_merge(diff, diff2)) < 0)
328
+ goto cleanup;
329
+
330
+ data.index = index;
331
+ data.include_changed = true;
332
+
333
+ if ((error = git_diff_foreach(
334
+ diff, update_index_cb, NULL, NULL, &data)) < 0)
335
+ {
336
+ if (error == GIT_EUSER)
337
+ error = data.error;
338
+ goto cleanup;
339
+ }
340
+
341
+
342
+ if ((error = build_tree_from_index(tree_out, index)) < 0)
343
+ goto cleanup;
344
+
345
+ cleanup:
346
+ git_diff_list_free(diff);
347
+ git_diff_list_free(diff2);
348
+ git_tree_free(b_tree);
349
+
350
+ return error;
351
+ }
352
+
353
+ static int commit_worktree(
354
+ git_oid *w_commit_oid,
355
+ git_index *index,
356
+ git_signature *stasher,
357
+ const char *message,
358
+ git_commit *i_commit,
359
+ git_commit *b_commit,
360
+ git_commit *u_commit)
361
+ {
362
+ int error = 0;
363
+ git_tree *w_tree = NULL, *i_tree = NULL;
364
+ const git_commit *parents[] = { NULL, NULL, NULL };
365
+
366
+ parents[0] = b_commit;
367
+ parents[1] = i_commit;
368
+ parents[2] = u_commit;
369
+
370
+ if ((error = git_commit_tree(&i_tree, i_commit)) < 0)
371
+ goto cleanup;
372
+
373
+ if ((error = git_index_read_tree(index, i_tree)) < 0)
374
+ goto cleanup;
375
+
376
+ if ((error = build_workdir_tree(&w_tree, index, b_commit)) < 0)
377
+ goto cleanup;
378
+
379
+ error = git_commit_create(
380
+ w_commit_oid,
381
+ git_index_owner(index),
382
+ NULL,
383
+ stasher,
384
+ stasher,
385
+ NULL,
386
+ message,
387
+ w_tree,
388
+ u_commit ? 3 : 2,
389
+ parents);
390
+
391
+ cleanup:
392
+ git_tree_free(i_tree);
393
+ git_tree_free(w_tree);
394
+ return error;
395
+ }
396
+
397
+ static int prepare_worktree_commit_message(
398
+ git_buf* msg,
399
+ const char *user_message)
400
+ {
401
+ git_buf buf = GIT_BUF_INIT;
402
+ int error;
403
+
404
+ if ((error = git_buf_set(&buf, git_buf_cstr(msg), git_buf_len(msg))) < 0)
405
+ return error;
406
+
407
+ git_buf_clear(msg);
408
+
409
+ if (!user_message)
410
+ git_buf_printf(msg, "WIP on %s", git_buf_cstr(&buf));
411
+ else {
412
+ const char *colon;
413
+
414
+ if ((colon = strchr(git_buf_cstr(&buf), ':')) == NULL)
415
+ goto cleanup;
416
+
417
+ git_buf_puts(msg, "On ");
418
+ git_buf_put(msg, git_buf_cstr(&buf), colon - buf.ptr);
419
+ git_buf_printf(msg, ": %s\n", user_message);
420
+ }
421
+
422
+ error = (git_buf_oom(msg) || git_buf_oom(&buf)) ? -1 : 0;
423
+
424
+ cleanup:
425
+ git_buf_free(&buf);
426
+
427
+ return error;
428
+ }
429
+
430
+ static int update_reflog(
431
+ git_oid *w_commit_oid,
432
+ git_repository *repo,
433
+ git_signature *stasher,
434
+ const char *message)
435
+ {
436
+ git_reference *stash = NULL;
437
+ git_reflog *reflog = NULL;
438
+ int error;
439
+
440
+ if ((error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, w_commit_oid, 1)) < 0)
441
+ goto cleanup;
442
+
443
+ if ((error = git_reflog_read(&reflog, stash)) < 0)
444
+ goto cleanup;
445
+
446
+ if ((error = git_reflog_append(reflog, w_commit_oid, stasher, message)) < 0)
447
+ goto cleanup;
448
+
449
+ if ((error = git_reflog_write(reflog)) < 0)
450
+ goto cleanup;
451
+
452
+ cleanup:
453
+ git_reference_free(stash);
454
+ git_reflog_free(reflog);
455
+ return error;
456
+ }
457
+
458
+ static int is_dirty_cb(const char *path, unsigned int status, void *payload)
459
+ {
460
+ GIT_UNUSED(path);
461
+ GIT_UNUSED(status);
462
+ GIT_UNUSED(payload);
463
+
464
+ return 1;
465
+ }
466
+
467
+ static int ensure_there_are_changes_to_stash(
468
+ git_repository *repo,
469
+ bool include_untracked_files,
470
+ bool include_ignored_files)
471
+ {
472
+ int error;
473
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
474
+
475
+ opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
476
+ if (include_untracked_files)
477
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
478
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
479
+
480
+ if (include_ignored_files)
481
+ opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED;
482
+
483
+ error = git_status_foreach_ext(repo, &opts, is_dirty_cb, NULL);
484
+
485
+ if (error == GIT_EUSER)
486
+ return 0;
487
+
488
+ if (!error)
489
+ return create_error(GIT_ENOTFOUND, "There is nothing to stash.");
490
+
491
+ return error;
492
+ }
493
+
494
+ static int reset_index_and_workdir(
495
+ git_repository *repo,
496
+ git_commit *commit,
497
+ bool remove_untracked)
498
+ {
499
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
500
+
501
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
502
+
503
+ if (remove_untracked)
504
+ opts.checkout_strategy |= GIT_CHECKOUT_REMOVE_UNTRACKED;
505
+
506
+ return git_checkout_tree(repo, (git_object *)commit, &opts);
507
+ }
508
+
509
+ int git_stash_save(
510
+ git_oid *out,
511
+ git_repository *repo,
512
+ git_signature *stasher,
513
+ const char *message,
514
+ uint32_t flags)
515
+ {
516
+ git_index *index = NULL;
517
+ git_commit *b_commit = NULL, *i_commit = NULL, *u_commit = NULL;
518
+ git_buf msg = GIT_BUF_INIT;
519
+ int error;
520
+
521
+ assert(out && repo && stasher);
522
+
523
+ if ((error = git_repository__ensure_not_bare(repo, "stash save")) < 0)
524
+ return error;
525
+
526
+ if ((error = retrieve_base_commit_and_message(&b_commit, &msg, repo)) < 0)
527
+ goto cleanup;
528
+
529
+ if ((error = ensure_there_are_changes_to_stash(
530
+ repo,
531
+ (flags & GIT_STASH_INCLUDE_UNTRACKED) != 0,
532
+ (flags & GIT_STASH_INCLUDE_IGNORED) != 0)) < 0)
533
+ goto cleanup;
534
+
535
+ if ((error = git_repository_index(&index, repo)) < 0)
536
+ goto cleanup;
537
+
538
+ if ((error = commit_index(
539
+ &i_commit, index, stasher, git_buf_cstr(&msg), b_commit)) < 0)
540
+ goto cleanup;
541
+
542
+ if ((flags & (GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED)) &&
543
+ (error = commit_untracked(
544
+ &u_commit, index, stasher, git_buf_cstr(&msg),
545
+ i_commit, flags)) < 0)
546
+ goto cleanup;
547
+
548
+ if ((error = prepare_worktree_commit_message(&msg, message)) < 0)
549
+ goto cleanup;
550
+
551
+ if ((error = commit_worktree(
552
+ out, index, stasher, git_buf_cstr(&msg),
553
+ i_commit, b_commit, u_commit)) < 0)
554
+ goto cleanup;
555
+
556
+ git_buf_rtrim(&msg);
557
+
558
+ if ((error = update_reflog(out, repo, stasher, git_buf_cstr(&msg))) < 0)
559
+ goto cleanup;
560
+
561
+ if ((error = reset_index_and_workdir(
562
+ repo,
563
+ ((flags & GIT_STASH_KEEP_INDEX) != 0) ? i_commit : b_commit,
564
+ (flags & GIT_STASH_INCLUDE_UNTRACKED) != 0)) < 0)
565
+ goto cleanup;
566
+
567
+ cleanup:
568
+
569
+ git_buf_free(&msg);
570
+ git_commit_free(i_commit);
571
+ git_commit_free(b_commit);
572
+ git_commit_free(u_commit);
573
+ git_index_free(index);
574
+
575
+ return error;
576
+ }
577
+
578
+ int git_stash_foreach(
579
+ git_repository *repo,
580
+ git_stash_cb callback,
581
+ void *payload)
582
+ {
583
+ git_reference *stash;
584
+ git_reflog *reflog = NULL;
585
+ int error;
586
+ size_t i, max;
587
+ const git_reflog_entry *entry;
588
+
589
+ error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE);
590
+ if (error == GIT_ENOTFOUND)
591
+ return 0;
592
+ if (error < 0)
593
+ goto cleanup;
594
+
595
+ if ((error = git_reflog_read(&reflog, stash)) < 0)
596
+ goto cleanup;
597
+
598
+ max = git_reflog_entrycount(reflog);
599
+ for (i = 0; i < max; i++) {
600
+ entry = git_reflog_entry_byindex(reflog, i);
601
+
602
+ if (callback(i,
603
+ git_reflog_entry_message(entry),
604
+ git_reflog_entry_id_new(entry),
605
+ payload)) {
606
+ error = GIT_EUSER;
607
+ break;
608
+ }
609
+ }
610
+
611
+ cleanup:
612
+ git_reference_free(stash);
613
+ git_reflog_free(reflog);
614
+ return error;
615
+ }
616
+
617
+ int git_stash_drop(
618
+ git_repository *repo,
619
+ size_t index)
620
+ {
621
+ git_reference *stash;
622
+ git_reflog *reflog = NULL;
623
+ size_t max;
624
+ int error;
625
+
626
+ if ((error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE)) < 0)
627
+ return error;
628
+
629
+ if ((error = git_reflog_read(&reflog, stash)) < 0)
630
+ goto cleanup;
631
+
632
+ max = git_reflog_entrycount(reflog);
633
+
634
+ if (index > max - 1) {
635
+ error = GIT_ENOTFOUND;
636
+ giterr_set(GITERR_STASH, "No stashed state at position %" PRIuZ, index);
637
+ goto cleanup;
638
+ }
639
+
640
+ if ((error = git_reflog_drop(reflog, index, true)) < 0)
641
+ goto cleanup;
642
+
643
+ if ((error = git_reflog_write(reflog)) < 0)
644
+ goto cleanup;
645
+
646
+ if (max == 1) {
647
+ error = git_reference_delete(stash);
648
+ git_reference_free(stash);
649
+ stash = NULL;
650
+ } else if (index == 0) {
651
+ const git_reflog_entry *entry;
652
+
653
+ entry = git_reflog_entry_byindex(reflog, 0);
654
+
655
+ git_reference_free(stash);
656
+ error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, &entry->oid_cur, 1);
657
+ }
658
+
659
+ cleanup:
660
+ git_reference_free(stash);
661
+ git_reflog_free(reflog);
662
+ return error;
663
+ }