rugged 0.19.0 → 0.28.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (668) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +1 -1
  3. data/README.md +184 -33
  4. data/ext/rugged/extconf.rb +111 -28
  5. data/ext/rugged/rugged.c +327 -89
  6. data/ext/rugged/rugged.h +64 -28
  7. data/ext/rugged/rugged_allocator.c +89 -0
  8. data/ext/rugged/rugged_backend.c +17 -0
  9. data/ext/rugged/rugged_blame.c +278 -0
  10. data/ext/rugged/rugged_blob.c +301 -75
  11. data/ext/rugged/rugged_branch.c +92 -242
  12. data/ext/rugged/rugged_branch_collection.c +388 -0
  13. data/ext/rugged/rugged_commit.c +575 -79
  14. data/ext/rugged/rugged_config.c +129 -36
  15. data/ext/rugged/rugged_cred.c +131 -0
  16. data/ext/rugged/rugged_diff.c +291 -122
  17. data/ext/rugged/rugged_diff_delta.c +16 -22
  18. data/ext/rugged/rugged_diff_hunk.c +35 -51
  19. data/ext/rugged/rugged_diff_line.c +23 -36
  20. data/ext/rugged/rugged_index.c +289 -152
  21. data/ext/rugged/rugged_note.c +50 -60
  22. data/ext/rugged/rugged_object.c +13 -30
  23. data/ext/rugged/rugged_patch.c +400 -0
  24. data/ext/rugged/rugged_rebase.c +397 -0
  25. data/ext/rugged/rugged_reference.c +76 -346
  26. data/ext/rugged/rugged_reference_collection.c +423 -0
  27. data/ext/rugged/rugged_remote.c +438 -461
  28. data/ext/rugged/rugged_remote_collection.c +435 -0
  29. data/ext/rugged/rugged_repo.c +1548 -365
  30. data/ext/rugged/rugged_revwalk.c +378 -99
  31. data/ext/rugged/rugged_settings.c +86 -23
  32. data/ext/rugged/rugged_signature.c +47 -37
  33. data/ext/rugged/rugged_submodule.c +835 -0
  34. data/ext/rugged/rugged_submodule_collection.c +366 -0
  35. data/ext/rugged/rugged_tag.c +88 -210
  36. data/ext/rugged/rugged_tag_collection.c +326 -0
  37. data/ext/rugged/rugged_tree.c +460 -217
  38. data/lib/rugged/attributes.rb +46 -0
  39. data/lib/rugged/blob.rb +33 -0
  40. data/lib/rugged/branch.rb +12 -16
  41. data/lib/rugged/commit.rb +9 -0
  42. data/lib/rugged/console.rb +5 -0
  43. data/lib/rugged/credentials.rb +48 -0
  44. data/lib/rugged/diff/delta.rb +6 -2
  45. data/lib/rugged/diff/hunk.rb +9 -9
  46. data/lib/rugged/diff/line.rb +28 -5
  47. data/lib/rugged/diff.rb +7 -1
  48. data/lib/rugged/index.rb +120 -0
  49. data/lib/rugged/object.rb +5 -0
  50. data/lib/rugged/patch.rb +41 -0
  51. data/lib/rugged/reference.rb +6 -3
  52. data/lib/rugged/remote.rb +5 -9
  53. data/lib/rugged/repository.rb +126 -14
  54. data/lib/rugged/submodule_collection.rb +53 -0
  55. data/lib/rugged/tag.rb +45 -16
  56. data/lib/rugged/tree.rb +163 -1
  57. data/lib/rugged/version.rb +6 -1
  58. data/lib/rugged/walker.rb +5 -0
  59. data/lib/rugged.rb +16 -1
  60. data/vendor/libgit2/AUTHORS +77 -0
  61. data/vendor/libgit2/CMakeLists.txt +317 -0
  62. data/vendor/libgit2/COPYING +993 -0
  63. data/vendor/libgit2/cmake/Modules/AddCFlagIfSupported.cmake +30 -0
  64. data/vendor/libgit2/cmake/Modules/CheckPrototypeDefinition.c.in +29 -0
  65. data/vendor/libgit2/cmake/Modules/CheckPrototypeDefinition.cmake +96 -0
  66. data/vendor/libgit2/cmake/Modules/EnableWarnings.cmake +11 -0
  67. data/vendor/libgit2/cmake/Modules/FindCoreFoundation.cmake +26 -0
  68. data/vendor/libgit2/cmake/Modules/FindGSSAPI.cmake +324 -0
  69. data/vendor/libgit2/cmake/Modules/FindHTTP_Parser.cmake +39 -0
  70. data/vendor/libgit2/cmake/Modules/FindIconv.cmake +45 -0
  71. data/vendor/libgit2/cmake/Modules/FindPkgLibraries.cmake +28 -0
  72. data/vendor/libgit2/cmake/Modules/FindSecurity.cmake +28 -0
  73. data/vendor/libgit2/cmake/Modules/FindStatNsec.cmake +20 -0
  74. data/vendor/libgit2/cmake/Modules/FindmbedTLS.cmake +93 -0
  75. data/vendor/libgit2/cmake/Modules/IdeSplitSources.cmake +22 -0
  76. data/vendor/libgit2/deps/http-parser/CMakeLists.txt +5 -0
  77. data/vendor/libgit2/deps/http-parser/COPYING +23 -0
  78. data/vendor/libgit2/deps/http-parser/http_parser.c +5 -2
  79. data/vendor/libgit2/deps/http-parser/http_parser.h +2 -0
  80. data/vendor/libgit2/deps/regex/CMakeLists.txt +2 -0
  81. data/vendor/libgit2/deps/regex/COPYING +502 -0
  82. data/vendor/libgit2/deps/regex/regex.c +10 -3
  83. data/vendor/libgit2/deps/winhttp/CMakeLists.txt +26 -0
  84. data/vendor/libgit2/deps/winhttp/COPYING.GPL +993 -0
  85. data/vendor/libgit2/deps/winhttp/COPYING.LGPL +502 -0
  86. data/vendor/libgit2/deps/winhttp/urlmon.h +45 -0
  87. data/vendor/libgit2/deps/winhttp/winhttp.def +29 -0
  88. data/vendor/libgit2/deps/winhttp/winhttp.h +594 -0
  89. data/vendor/libgit2/deps/winhttp/winhttp64.def +29 -0
  90. data/vendor/libgit2/deps/zlib/CMakeLists.txt +5 -0
  91. data/vendor/libgit2/deps/zlib/COPYING +27 -0
  92. data/vendor/libgit2/deps/zlib/adler32.c +51 -34
  93. data/vendor/libgit2/deps/zlib/crc32.c +61 -61
  94. data/vendor/libgit2/deps/zlib/crc32.h +1 -1
  95. data/vendor/libgit2/deps/zlib/deflate.c +681 -352
  96. data/vendor/libgit2/deps/zlib/deflate.h +25 -18
  97. data/vendor/libgit2/deps/zlib/gzguts.h +218 -0
  98. data/vendor/libgit2/deps/zlib/infback.c +640 -0
  99. data/vendor/libgit2/deps/zlib/inffast.c +36 -53
  100. data/vendor/libgit2/deps/zlib/inffixed.h +3 -3
  101. data/vendor/libgit2/deps/zlib/inflate.c +167 -86
  102. data/vendor/libgit2/deps/zlib/inflate.h +7 -4
  103. data/vendor/libgit2/deps/zlib/inftrees.c +24 -50
  104. data/vendor/libgit2/deps/zlib/trees.c +55 -96
  105. data/vendor/libgit2/deps/zlib/zconf.h +499 -19
  106. data/vendor/libgit2/deps/zlib/zlib.h +526 -227
  107. data/vendor/libgit2/deps/zlib/zutil.c +39 -32
  108. data/vendor/libgit2/deps/zlib/zutil.h +75 -78
  109. data/vendor/libgit2/include/git2/annotated_commit.h +125 -0
  110. data/vendor/libgit2/include/git2/apply.h +129 -0
  111. data/vendor/libgit2/include/git2/attr.h +36 -21
  112. data/vendor/libgit2/include/git2/blame.h +229 -0
  113. data/vendor/libgit2/include/git2/blob.h +81 -44
  114. data/vendor/libgit2/include/git2/branch.h +81 -42
  115. data/vendor/libgit2/include/git2/buffer.h +128 -0
  116. data/vendor/libgit2/include/git2/checkout.h +141 -67
  117. data/vendor/libgit2/include/git2/cherrypick.h +92 -0
  118. data/vendor/libgit2/include/git2/clone.h +157 -58
  119. data/vendor/libgit2/include/git2/commit.h +231 -12
  120. data/vendor/libgit2/include/git2/common.h +216 -30
  121. data/vendor/libgit2/include/git2/config.h +274 -48
  122. data/vendor/libgit2/include/git2/cred_helpers.h +4 -4
  123. data/vendor/libgit2/include/git2/deprecated.h +253 -0
  124. data/vendor/libgit2/include/git2/describe.h +189 -0
  125. data/vendor/libgit2/include/git2/diff.h +985 -575
  126. data/vendor/libgit2/include/git2/errors.h +93 -52
  127. data/vendor/libgit2/include/git2/filter.h +217 -0
  128. data/vendor/libgit2/include/git2/global.h +44 -0
  129. data/vendor/libgit2/include/git2/graph.h +17 -0
  130. data/vendor/libgit2/include/git2/ignore.h +2 -2
  131. data/vendor/libgit2/include/git2/index.h +269 -94
  132. data/vendor/libgit2/include/git2/indexer.h +44 -12
  133. data/vendor/libgit2/include/git2/mailmap.h +115 -0
  134. data/vendor/libgit2/include/git2/merge.h +501 -64
  135. data/vendor/libgit2/include/git2/message.h +52 -17
  136. data/vendor/libgit2/include/git2/net.h +11 -5
  137. data/vendor/libgit2/include/git2/notes.h +120 -16
  138. data/vendor/libgit2/include/git2/object.h +62 -23
  139. data/vendor/libgit2/include/git2/odb.h +140 -24
  140. data/vendor/libgit2/include/git2/odb_backend.h +56 -12
  141. data/vendor/libgit2/include/git2/oid.h +17 -18
  142. data/vendor/libgit2/include/git2/oidarray.h +40 -0
  143. data/vendor/libgit2/include/git2/pack.h +86 -7
  144. data/vendor/libgit2/include/git2/patch.h +274 -0
  145. data/vendor/libgit2/include/git2/pathspec.h +280 -0
  146. data/vendor/libgit2/include/git2/proxy.h +96 -0
  147. data/vendor/libgit2/include/git2/rebase.h +323 -0
  148. data/vendor/libgit2/include/git2/reflog.h +12 -9
  149. data/vendor/libgit2/include/git2/refs.h +241 -46
  150. data/vendor/libgit2/include/git2/refspec.h +20 -4
  151. data/vendor/libgit2/include/git2/remote.h +636 -209
  152. data/vendor/libgit2/include/git2/repository.h +267 -57
  153. data/vendor/libgit2/include/git2/reset.h +36 -6
  154. data/vendor/libgit2/include/git2/revert.h +91 -0
  155. data/vendor/libgit2/include/git2/revparse.h +27 -16
  156. data/vendor/libgit2/include/git2/revwalk.h +78 -35
  157. data/vendor/libgit2/include/git2/signature.h +32 -5
  158. data/vendor/libgit2/include/git2/stash.h +160 -21
  159. data/vendor/libgit2/include/git2/status.h +92 -30
  160. data/vendor/libgit2/include/git2/submodule.h +226 -133
  161. data/vendor/libgit2/include/git2/sys/alloc.h +101 -0
  162. data/vendor/libgit2/include/git2/sys/commit.h +38 -4
  163. data/vendor/libgit2/include/git2/sys/config.h +68 -9
  164. data/vendor/libgit2/include/git2/sys/diff.h +94 -0
  165. data/vendor/libgit2/include/git2/sys/filter.h +332 -0
  166. data/vendor/libgit2/include/git2/sys/hashsig.h +106 -0
  167. data/vendor/libgit2/include/git2/sys/index.h +6 -5
  168. data/vendor/libgit2/include/git2/sys/mempack.h +86 -0
  169. data/vendor/libgit2/include/git2/sys/merge.h +182 -0
  170. data/vendor/libgit2/include/git2/sys/odb_backend.h +66 -28
  171. data/vendor/libgit2/include/git2/sys/openssl.h +38 -0
  172. data/vendor/libgit2/include/git2/sys/path.h +64 -0
  173. data/vendor/libgit2/include/git2/sys/refdb_backend.h +79 -19
  174. data/vendor/libgit2/include/git2/sys/reflog.h +21 -0
  175. data/vendor/libgit2/include/git2/sys/refs.h +13 -2
  176. data/vendor/libgit2/include/git2/sys/repository.h +64 -1
  177. data/vendor/libgit2/include/git2/sys/stream.h +138 -0
  178. data/vendor/libgit2/include/git2/sys/time.h +31 -0
  179. data/vendor/libgit2/include/git2/sys/transport.h +439 -0
  180. data/vendor/libgit2/include/git2/tag.h +11 -2
  181. data/vendor/libgit2/include/git2/trace.h +1 -1
  182. data/vendor/libgit2/include/git2/transaction.h +121 -0
  183. data/vendor/libgit2/include/git2/transport.h +261 -292
  184. data/vendor/libgit2/include/git2/tree.h +111 -21
  185. data/vendor/libgit2/include/git2/types.h +244 -32
  186. data/vendor/libgit2/include/git2/version.h +5 -2
  187. data/vendor/libgit2/include/git2/worktree.h +255 -0
  188. data/vendor/libgit2/include/git2.h +50 -40
  189. data/vendor/libgit2/libgit2.pc.in +13 -0
  190. data/vendor/libgit2/src/CMakeLists.txt +525 -0
  191. data/vendor/libgit2/src/alloc.c +55 -0
  192. data/vendor/libgit2/src/alloc.h +40 -0
  193. data/vendor/libgit2/src/annotated_commit.c +228 -0
  194. data/vendor/libgit2/src/annotated_commit.h +52 -0
  195. data/vendor/libgit2/src/apply.c +855 -0
  196. data/vendor/libgit2/src/apply.h +25 -0
  197. data/vendor/libgit2/src/array.h +74 -16
  198. data/vendor/libgit2/src/attr.c +239 -408
  199. data/vendor/libgit2/src/attr.h +3 -33
  200. data/vendor/libgit2/src/attr_file.c +424 -156
  201. data/vendor/libgit2/src/attr_file.h +95 -23
  202. data/vendor/libgit2/src/attrcache.c +469 -0
  203. data/vendor/libgit2/src/attrcache.h +37 -5
  204. data/vendor/libgit2/src/bitvec.h +75 -0
  205. data/vendor/libgit2/src/blame.c +532 -0
  206. data/vendor/libgit2/src/blame.h +95 -0
  207. data/vendor/libgit2/src/blame_git.c +668 -0
  208. data/vendor/libgit2/src/blame_git.h +22 -0
  209. data/vendor/libgit2/src/blob.c +233 -129
  210. data/vendor/libgit2/src/blob.h +29 -1
  211. data/vendor/libgit2/src/branch.c +295 -197
  212. data/vendor/libgit2/src/branch.h +2 -0
  213. data/vendor/libgit2/src/buf_text.c +52 -27
  214. data/vendor/libgit2/src/buf_text.h +7 -7
  215. data/vendor/libgit2/src/buffer.c +609 -52
  216. data/vendor/libgit2/src/buffer.h +68 -23
  217. data/vendor/libgit2/src/cache.c +48 -51
  218. data/vendor/libgit2/src/cache.h +6 -4
  219. data/vendor/libgit2/src/cc-compat.h +35 -7
  220. data/vendor/libgit2/src/checkout.c +1827 -483
  221. data/vendor/libgit2/src/checkout.h +4 -1
  222. data/vendor/libgit2/src/cherrypick.c +230 -0
  223. data/vendor/libgit2/src/clone.c +338 -258
  224. data/vendor/libgit2/src/{compress.h → clone.h} +5 -5
  225. data/vendor/libgit2/src/commit.c +711 -124
  226. data/vendor/libgit2/src/commit.h +10 -3
  227. data/vendor/libgit2/src/commit_list.c +21 -14
  228. data/vendor/libgit2/src/commit_list.h +9 -3
  229. data/vendor/libgit2/src/common.h +153 -13
  230. data/vendor/libgit2/src/config.c +871 -242
  231. data/vendor/libgit2/src/config.h +58 -14
  232. data/vendor/libgit2/src/config_backend.h +84 -0
  233. data/vendor/libgit2/src/config_cache.c +44 -18
  234. data/vendor/libgit2/src/config_entries.c +259 -0
  235. data/vendor/libgit2/src/config_entries.h +23 -0
  236. data/vendor/libgit2/src/config_file.c +837 -1113
  237. data/vendor/libgit2/src/config_mem.c +224 -0
  238. data/vendor/libgit2/src/config_parse.c +558 -0
  239. data/vendor/libgit2/src/config_parse.h +64 -0
  240. data/vendor/libgit2/src/crlf.c +290 -195
  241. data/vendor/libgit2/src/date.c +35 -7
  242. data/vendor/libgit2/src/delta.c +275 -71
  243. data/vendor/libgit2/src/delta.h +80 -58
  244. data/vendor/libgit2/src/describe.c +893 -0
  245. data/vendor/libgit2/src/diff.c +330 -1128
  246. data/vendor/libgit2/src/diff.h +25 -67
  247. data/vendor/libgit2/src/diff_driver.c +225 -109
  248. data/vendor/libgit2/src/diff_driver.h +5 -2
  249. data/vendor/libgit2/src/diff_file.c +128 -103
  250. data/vendor/libgit2/src/diff_file.h +17 -12
  251. data/vendor/libgit2/src/diff_generate.c +1622 -0
  252. data/vendor/libgit2/src/diff_generate.h +128 -0
  253. data/vendor/libgit2/src/diff_parse.c +108 -0
  254. data/vendor/libgit2/src/diff_parse.h +20 -0
  255. data/vendor/libgit2/src/diff_print.c +578 -218
  256. data/vendor/libgit2/src/diff_stats.c +362 -0
  257. data/vendor/libgit2/src/diff_tform.c +429 -257
  258. data/vendor/libgit2/src/diff_tform.h +25 -0
  259. data/vendor/libgit2/src/diff_xdiff.c +143 -46
  260. data/vendor/libgit2/src/diff_xdiff.h +12 -5
  261. data/vendor/libgit2/src/errors.c +150 -34
  262. data/vendor/libgit2/src/features.h.in +37 -0
  263. data/vendor/libgit2/src/fetch.c +69 -46
  264. data/vendor/libgit2/src/fetch.h +6 -12
  265. data/vendor/libgit2/src/fetchhead.c +40 -33
  266. data/vendor/libgit2/src/fetchhead.h +5 -4
  267. data/vendor/libgit2/src/filebuf.c +163 -61
  268. data/vendor/libgit2/src/filebuf.h +13 -7
  269. data/vendor/libgit2/src/fileops.c +549 -407
  270. data/vendor/libgit2/src/fileops.h +97 -106
  271. data/vendor/libgit2/src/filter.c +989 -46
  272. data/vendor/libgit2/src/filter.h +21 -70
  273. data/vendor/libgit2/src/fnmatch.c +67 -11
  274. data/vendor/libgit2/src/fnmatch.h +27 -7
  275. data/vendor/libgit2/src/global.c +257 -63
  276. data/vendor/libgit2/src/global.h +19 -0
  277. data/vendor/libgit2/src/graph.c +39 -23
  278. data/vendor/libgit2/src/hash/hash_collisiondetect.h +51 -0
  279. data/vendor/libgit2/src/hash/hash_common_crypto.h +61 -0
  280. data/vendor/libgit2/src/hash/hash_generic.c +3 -3
  281. data/vendor/libgit2/src/hash/hash_generic.h +10 -5
  282. data/vendor/libgit2/src/hash/hash_mbedtls.c +38 -0
  283. data/vendor/libgit2/src/hash/hash_mbedtls.h +24 -0
  284. data/vendor/libgit2/src/hash/hash_openssl.h +26 -8
  285. data/vendor/libgit2/src/hash/hash_win32.c +71 -43
  286. data/vendor/libgit2/src/hash/hash_win32.h +4 -3
  287. data/vendor/libgit2/src/hash/sha1dc/sha1.c +1900 -0
  288. data/vendor/libgit2/src/hash/sha1dc/sha1.h +110 -0
  289. data/vendor/libgit2/src/hash/sha1dc/ubc_check.c +372 -0
  290. data/vendor/libgit2/src/hash/sha1dc/ubc_check.h +52 -0
  291. data/vendor/libgit2/src/hash.c +0 -1
  292. data/vendor/libgit2/src/hash.h +13 -6
  293. data/vendor/libgit2/src/hashsig.c +121 -126
  294. data/vendor/libgit2/src/ident.c +129 -0
  295. data/vendor/libgit2/src/idxmap.c +153 -0
  296. data/vendor/libgit2/src/idxmap.h +41 -0
  297. data/vendor/libgit2/src/ignore.c +362 -123
  298. data/vendor/libgit2/src/ignore.h +16 -4
  299. data/vendor/libgit2/src/index.c +2131 -692
  300. data/vendor/libgit2/src/index.h +138 -6
  301. data/vendor/libgit2/src/indexer.c +866 -266
  302. data/vendor/libgit2/src/indexer.h +16 -0
  303. data/vendor/libgit2/src/integer.h +106 -0
  304. data/vendor/libgit2/src/iterator.c +1888 -967
  305. data/vendor/libgit2/src/iterator.h +130 -67
  306. data/vendor/libgit2/src/khash.h +43 -29
  307. data/vendor/libgit2/src/mailmap.c +485 -0
  308. data/vendor/libgit2/src/mailmap.h +35 -0
  309. data/vendor/libgit2/src/map.h +1 -1
  310. data/vendor/libgit2/src/merge.c +1679 -479
  311. data/vendor/libgit2/src/merge.h +89 -22
  312. data/vendor/libgit2/src/merge_driver.c +426 -0
  313. data/vendor/libgit2/src/merge_driver.h +62 -0
  314. data/vendor/libgit2/src/merge_file.c +238 -101
  315. data/vendor/libgit2/src/message.c +4 -28
  316. data/vendor/libgit2/src/message.h +3 -1
  317. data/vendor/libgit2/src/mwindow.c +123 -15
  318. data/vendor/libgit2/src/mwindow.h +10 -1
  319. data/vendor/libgit2/src/netops.c +178 -499
  320. data/vendor/libgit2/src/netops.h +51 -27
  321. data/vendor/libgit2/src/notes.c +251 -94
  322. data/vendor/libgit2/src/notes.h +5 -2
  323. data/vendor/libgit2/src/object.c +253 -67
  324. data/vendor/libgit2/src/object.h +40 -2
  325. data/vendor/libgit2/src/object_api.c +30 -11
  326. data/vendor/libgit2/src/odb.c +765 -201
  327. data/vendor/libgit2/src/odb.h +40 -8
  328. data/vendor/libgit2/src/odb_loose.c +560 -346
  329. data/vendor/libgit2/src/odb_mempack.c +185 -0
  330. data/vendor/libgit2/src/odb_pack.c +117 -73
  331. data/vendor/libgit2/src/offmap.c +113 -0
  332. data/vendor/libgit2/src/offmap.h +32 -42
  333. data/vendor/libgit2/src/oid.c +45 -25
  334. data/vendor/libgit2/src/oid.h +26 -8
  335. data/vendor/libgit2/src/oidarray.c +34 -0
  336. data/vendor/libgit2/src/oidarray.h +20 -0
  337. data/vendor/libgit2/src/oidmap.c +125 -0
  338. data/vendor/libgit2/src/oidmap.h +30 -17
  339. data/vendor/libgit2/src/pack-objects.c +688 -265
  340. data/vendor/libgit2/src/pack-objects.h +27 -13
  341. data/vendor/libgit2/src/pack.c +418 -202
  342. data/vendor/libgit2/src/pack.h +25 -16
  343. data/vendor/libgit2/src/parse.c +124 -0
  344. data/vendor/libgit2/src/parse.h +61 -0
  345. data/vendor/libgit2/src/patch.c +223 -0
  346. data/vendor/libgit2/src/patch.h +68 -0
  347. data/vendor/libgit2/src/patch_generate.c +901 -0
  348. data/vendor/libgit2/src/patch_generate.h +69 -0
  349. data/vendor/libgit2/src/patch_parse.c +1136 -0
  350. data/vendor/libgit2/src/patch_parse.h +51 -0
  351. data/vendor/libgit2/src/path.c +1247 -241
  352. data/vendor/libgit2/src/path.h +353 -57
  353. data/vendor/libgit2/src/pathspec.c +586 -58
  354. data/vendor/libgit2/src/pathspec.h +37 -15
  355. data/vendor/libgit2/src/pool.c +134 -221
  356. data/vendor/libgit2/src/pool.h +38 -50
  357. data/vendor/libgit2/src/posix.c +76 -10
  358. data/vendor/libgit2/src/posix.h +74 -32
  359. data/vendor/libgit2/src/pqueue.c +79 -117
  360. data/vendor/libgit2/src/pqueue.h +38 -82
  361. data/vendor/libgit2/src/proxy.c +39 -0
  362. data/vendor/libgit2/src/proxy.h +17 -0
  363. data/vendor/libgit2/src/push.c +178 -279
  364. data/vendor/libgit2/src/push.h +93 -4
  365. data/vendor/libgit2/src/reader.c +265 -0
  366. data/vendor/libgit2/src/reader.h +107 -0
  367. data/vendor/libgit2/src/rebase.c +1364 -0
  368. data/vendor/libgit2/src/refdb.c +74 -19
  369. data/vendor/libgit2/src/refdb.h +16 -3
  370. data/vendor/libgit2/src/refdb_fs.c +1472 -603
  371. data/vendor/libgit2/src/refdb_fs.h +4 -0
  372. data/vendor/libgit2/src/reflog.c +40 -330
  373. data/vendor/libgit2/src/reflog.h +8 -2
  374. data/vendor/libgit2/src/refs.c +641 -225
  375. data/vendor/libgit2/src/refs.h +53 -6
  376. data/vendor/libgit2/src/refspec.c +175 -62
  377. data/vendor/libgit2/src/refspec.h +10 -25
  378. data/vendor/libgit2/src/remote.c +1741 -723
  379. data/vendor/libgit2/src/remote.h +17 -5
  380. data/vendor/libgit2/src/repository.c +1505 -421
  381. data/vendor/libgit2/src/repository.h +95 -15
  382. data/vendor/libgit2/src/reset.c +63 -26
  383. data/vendor/libgit2/src/revert.c +232 -0
  384. data/vendor/libgit2/src/revparse.c +94 -80
  385. data/vendor/libgit2/src/revwalk.c +427 -194
  386. data/vendor/libgit2/src/revwalk.h +14 -5
  387. data/vendor/libgit2/src/settings.c +290 -0
  388. data/vendor/libgit2/src/sha1_lookup.c +16 -159
  389. data/vendor/libgit2/src/sha1_lookup.h +5 -4
  390. data/vendor/libgit2/src/signature.c +138 -26
  391. data/vendor/libgit2/src/signature.h +5 -0
  392. data/vendor/libgit2/src/sortedcache.c +395 -0
  393. data/vendor/libgit2/src/sortedcache.h +180 -0
  394. data/vendor/libgit2/src/stash.c +629 -168
  395. data/vendor/libgit2/src/status.c +125 -75
  396. data/vendor/libgit2/src/status.h +4 -2
  397. data/vendor/libgit2/src/stdalloc.c +120 -0
  398. data/vendor/libgit2/src/stdalloc.h +17 -0
  399. data/vendor/libgit2/src/stream.h +86 -0
  400. data/vendor/libgit2/src/streams/mbedtls.c +483 -0
  401. data/vendor/libgit2/src/streams/mbedtls.h +23 -0
  402. data/vendor/libgit2/src/streams/openssl.c +789 -0
  403. data/vendor/libgit2/src/streams/openssl.h +23 -0
  404. data/vendor/libgit2/src/streams/registry.c +118 -0
  405. data/vendor/libgit2/src/streams/registry.h +19 -0
  406. data/vendor/libgit2/src/streams/socket.c +235 -0
  407. data/vendor/libgit2/src/streams/socket.h +23 -0
  408. data/vendor/libgit2/src/streams/stransport.c +323 -0
  409. data/vendor/libgit2/src/streams/stransport.h +21 -0
  410. data/vendor/libgit2/src/streams/tls.c +73 -0
  411. data/vendor/libgit2/src/streams/tls.h +31 -0
  412. data/vendor/libgit2/src/strmap.c +147 -0
  413. data/vendor/libgit2/src/strmap.h +46 -51
  414. data/vendor/libgit2/src/strnlen.h +24 -0
  415. data/vendor/libgit2/src/submodule.c +1633 -877
  416. data/vendor/libgit2/src/submodule.h +83 -21
  417. data/vendor/libgit2/src/sysdir.c +355 -0
  418. data/vendor/libgit2/src/sysdir.h +119 -0
  419. data/vendor/libgit2/src/tag.c +87 -62
  420. data/vendor/libgit2/src/tag.h +4 -1
  421. data/vendor/libgit2/src/thread-utils.c +3 -0
  422. data/vendor/libgit2/src/thread-utils.h +71 -35
  423. data/vendor/libgit2/src/trace.c +4 -4
  424. data/vendor/libgit2/src/trace.h +11 -3
  425. data/vendor/libgit2/src/trailer.c +416 -0
  426. data/vendor/libgit2/src/transaction.c +382 -0
  427. data/vendor/libgit2/src/transaction.h +14 -0
  428. data/vendor/libgit2/src/transport.c +133 -67
  429. data/vendor/libgit2/src/transports/auth.c +75 -0
  430. data/vendor/libgit2/src/transports/auth.h +64 -0
  431. data/vendor/libgit2/src/transports/auth_negotiate.c +277 -0
  432. data/vendor/libgit2/src/transports/auth_negotiate.h +27 -0
  433. data/vendor/libgit2/src/transports/cred.c +296 -68
  434. data/vendor/libgit2/src/transports/cred.h +16 -0
  435. data/vendor/libgit2/src/transports/cred_helpers.c +4 -0
  436. data/vendor/libgit2/src/transports/git.c +108 -90
  437. data/vendor/libgit2/src/transports/http.c +803 -258
  438. data/vendor/libgit2/src/transports/http.h +25 -0
  439. data/vendor/libgit2/src/transports/local.c +265 -169
  440. data/vendor/libgit2/src/transports/smart.c +255 -45
  441. data/vendor/libgit2/src/transports/smart.h +42 -22
  442. data/vendor/libgit2/src/transports/smart_pkt.c +250 -159
  443. data/vendor/libgit2/src/transports/smart_protocol.c +414 -196
  444. data/vendor/libgit2/src/transports/ssh.c +645 -236
  445. data/vendor/libgit2/src/transports/ssh.h +14 -0
  446. data/vendor/libgit2/src/transports/winhttp.c +809 -353
  447. data/vendor/libgit2/src/tree-cache.c +138 -52
  448. data/vendor/libgit2/src/tree-cache.h +14 -7
  449. data/vendor/libgit2/src/tree.c +620 -259
  450. data/vendor/libgit2/src/tree.h +12 -19
  451. data/vendor/libgit2/src/tsort.c +3 -2
  452. data/vendor/libgit2/src/unix/map.c +25 -7
  453. data/vendor/libgit2/src/unix/posix.h +77 -12
  454. data/vendor/libgit2/src/unix/pthread.h +56 -0
  455. data/vendor/libgit2/src/unix/realpath.c +12 -8
  456. data/vendor/libgit2/src/userdiff.h +208 -0
  457. data/vendor/libgit2/src/util.c +349 -165
  458. data/vendor/libgit2/src/util.h +167 -85
  459. data/vendor/libgit2/src/varint.c +43 -0
  460. data/vendor/libgit2/src/varint.h +17 -0
  461. data/vendor/libgit2/src/vector.c +156 -33
  462. data/vendor/libgit2/src/vector.h +41 -5
  463. data/vendor/libgit2/src/win32/dir.c +22 -42
  464. data/vendor/libgit2/src/win32/dir.h +7 -5
  465. data/vendor/libgit2/src/win32/error.c +6 -32
  466. data/vendor/libgit2/src/win32/error.h +4 -2
  467. data/vendor/libgit2/src/win32/findfile.c +62 -69
  468. data/vendor/libgit2/src/win32/findfile.h +5 -13
  469. data/vendor/libgit2/src/win32/git2.rc +44 -0
  470. data/vendor/libgit2/src/win32/map.c +39 -11
  471. data/vendor/libgit2/src/win32/mingw-compat.h +10 -11
  472. data/vendor/libgit2/src/win32/msvc-compat.h +10 -33
  473. data/vendor/libgit2/src/win32/path_w32.c +476 -0
  474. data/vendor/libgit2/src/win32/path_w32.h +104 -0
  475. data/vendor/libgit2/src/win32/posix.h +35 -30
  476. data/vendor/libgit2/src/win32/posix_w32.c +659 -327
  477. data/vendor/libgit2/src/win32/precompiled.h +7 -2
  478. data/vendor/libgit2/src/win32/reparse.h +57 -0
  479. data/vendor/libgit2/src/win32/thread.c +258 -0
  480. data/vendor/libgit2/src/win32/thread.h +64 -0
  481. data/vendor/libgit2/src/win32/utf-conv.c +127 -62
  482. data/vendor/libgit2/src/win32/utf-conv.h +47 -6
  483. data/vendor/libgit2/src/win32/version.h +21 -4
  484. data/vendor/libgit2/src/win32/w32_buffer.c +54 -0
  485. data/vendor/libgit2/src/win32/w32_buffer.h +20 -0
  486. data/vendor/libgit2/src/win32/w32_crtdbg_stacktrace.c +438 -0
  487. data/vendor/libgit2/src/win32/w32_crtdbg_stacktrace.h +129 -0
  488. data/vendor/libgit2/src/win32/w32_stack.c +193 -0
  489. data/vendor/libgit2/src/win32/w32_stack.h +140 -0
  490. data/vendor/libgit2/src/win32/w32_util.c +95 -0
  491. data/vendor/libgit2/src/win32/w32_util.h +170 -0
  492. data/vendor/libgit2/src/win32/win32-compat.h +52 -0
  493. data/vendor/libgit2/src/worktree.c +578 -0
  494. data/vendor/libgit2/src/worktree.h +39 -0
  495. data/vendor/libgit2/src/xdiff/xdiff.h +33 -18
  496. data/vendor/libgit2/src/xdiff/xdiffi.c +578 -88
  497. data/vendor/libgit2/src/xdiff/xdiffi.h +3 -2
  498. data/vendor/libgit2/src/xdiff/xemit.c +106 -45
  499. data/vendor/libgit2/src/xdiff/xemit.h +3 -3
  500. data/vendor/libgit2/src/xdiff/xhistogram.c +5 -4
  501. data/vendor/libgit2/src/xdiff/xinclude.h +3 -2
  502. data/vendor/libgit2/src/xdiff/xmacros.h +2 -2
  503. data/vendor/libgit2/src/xdiff/xmerge.c +167 -48
  504. data/vendor/libgit2/src/xdiff/xpatience.c +42 -10
  505. data/vendor/libgit2/src/xdiff/xprepare.c +14 -14
  506. data/vendor/libgit2/src/xdiff/xprepare.h +2 -2
  507. data/vendor/libgit2/src/xdiff/xtypes.h +2 -2
  508. data/vendor/libgit2/src/xdiff/xutils.c +60 -56
  509. data/vendor/libgit2/src/xdiff/xutils.h +3 -5
  510. data/vendor/libgit2/src/zstream.c +205 -0
  511. data/vendor/libgit2/src/zstream.h +53 -0
  512. metadata +281 -233
  513. data/Rakefile +0 -61
  514. data/ext/rugged/rugged_diff_patch.c +0 -169
  515. data/lib/rugged/diff/patch.rb +0 -28
  516. data/test/blob_test.rb +0 -341
  517. data/test/branch_test.rb +0 -199
  518. data/test/commit_test.rb +0 -104
  519. data/test/config_test.rb +0 -45
  520. data/test/coverage/cover.rb +0 -133
  521. data/test/diff_test.rb +0 -777
  522. data/test/errors_test.rb +0 -34
  523. data/test/fixtures/alternate/objects/14/6ae76773c91e3b1d00cf7a338ec55ae58297e2 +0 -0
  524. data/test/fixtures/alternate/objects/14/9c32d47e99d0a3572ff1e70a2e0051bbf347a9 +0 -0
  525. data/test/fixtures/alternate/objects/14/fb3108588f9421bf764041e5e3ac305eb6277f +0 -0
  526. data/test/fixtures/archive.tar.gz +0 -0
  527. data/test/fixtures/attr/attr0 +0 -1
  528. data/test/fixtures/attr/attr1 +0 -29
  529. data/test/fixtures/attr/attr2 +0 -21
  530. data/test/fixtures/attr/attr3 +0 -4
  531. data/test/fixtures/attr/binfile +0 -1
  532. data/test/fixtures/attr/dir/file +0 -0
  533. data/test/fixtures/attr/file +0 -1
  534. data/test/fixtures/attr/gitattributes +0 -29
  535. data/test/fixtures/attr/gitignore +0 -2
  536. data/test/fixtures/attr/ign +0 -1
  537. data/test/fixtures/attr/macro_bad +0 -1
  538. data/test/fixtures/attr/macro_test +0 -1
  539. data/test/fixtures/attr/root_test1 +0 -1
  540. data/test/fixtures/attr/root_test2 +0 -6
  541. data/test/fixtures/attr/root_test3 +0 -19
  542. data/test/fixtures/attr/root_test4.txt +0 -14
  543. data/test/fixtures/attr/sub/abc +0 -37
  544. data/test/fixtures/attr/sub/dir/file +0 -0
  545. data/test/fixtures/attr/sub/file +0 -1
  546. data/test/fixtures/attr/sub/ign/file +0 -1
  547. data/test/fixtures/attr/sub/ign/sub/file +0 -1
  548. data/test/fixtures/attr/sub/sub/dir +0 -0
  549. data/test/fixtures/attr/sub/sub/file +0 -1
  550. data/test/fixtures/attr/sub/sub/subsub.txt +0 -1
  551. data/test/fixtures/attr/sub/subdir_test1 +0 -2
  552. data/test/fixtures/attr/sub/subdir_test2.txt +0 -1
  553. data/test/fixtures/diff/another.txt +0 -38
  554. data/test/fixtures/diff/readme.txt +0 -36
  555. data/test/fixtures/mergedrepo/conflicts-one.txt +0 -5
  556. data/test/fixtures/mergedrepo/conflicts-two.txt +0 -5
  557. data/test/fixtures/mergedrepo/one.txt +0 -10
  558. data/test/fixtures/mergedrepo/two.txt +0 -12
  559. data/test/fixtures/status/current_file +0 -1
  560. data/test/fixtures/status/ignored_file +0 -1
  561. data/test/fixtures/status/modified_file +0 -2
  562. data/test/fixtures/status/new_file +0 -1
  563. data/test/fixtures/status/staged_changes +0 -2
  564. data/test/fixtures/status/staged_changes_modified_file +0 -3
  565. data/test/fixtures/status/staged_delete_modified_file +0 -1
  566. data/test/fixtures/status/staged_new_file +0 -1
  567. data/test/fixtures/status/staged_new_file_modified_file +0 -2
  568. data/test/fixtures/status/subdir/current_file +0 -1
  569. data/test/fixtures/status/subdir/modified_file +0 -2
  570. data/test/fixtures/status/subdir/new_file +0 -1
  571. data/test/fixtures/status/subdir.txt +0 -2
  572. data/test/fixtures/status//350/277/231 +0 -1
  573. data/test/fixtures/testrepo.git/HEAD +0 -1
  574. data/test/fixtures/testrepo.git/config +0 -13
  575. data/test/fixtures/testrepo.git/description +0 -1
  576. data/test/fixtures/testrepo.git/index +0 -0
  577. data/test/fixtures/testrepo.git/info/exclude +0 -6
  578. data/test/fixtures/testrepo.git/logs/HEAD +0 -3
  579. data/test/fixtures/testrepo.git/logs/refs/heads/master +0 -3
  580. data/test/fixtures/testrepo.git/logs/refs/notes/commits +0 -1
  581. data/test/fixtures/testrepo.git/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1d +0 -0
  582. data/test/fixtures/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 +0 -0
  583. data/test/fixtures/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 +0 -0
  584. data/test/fixtures/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd +0 -0
  585. data/test/fixtures/testrepo.git/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2 +0 -0
  586. data/test/fixtures/testrepo.git/objects/36/060c58702ed4c2a40832c51758d5344201d89a +0 -2
  587. data/test/fixtures/testrepo.git/objects/44/1034f860c1d5d90e4188d11ae0d325176869a8 +0 -1
  588. data/test/fixtures/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 +0 -0
  589. data/test/fixtures/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 +0 -2
  590. data/test/fixtures/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 +0 -2
  591. data/test/fixtures/testrepo.git/objects/60/d415052a33de2150bf68757f6461df4f563ae4 +0 -0
  592. data/test/fixtures/testrepo.git/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96 +0 -0
  593. data/test/fixtures/testrepo.git/objects/68/8a8f4ef7496901d15322972f96e212a9e466cc +0 -1
  594. data/test/fixtures/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a +0 -0
  595. data/test/fixtures/testrepo.git/objects/77/71329dfa3002caf8c61a0ceb62a31d09023f37 +0 -0
  596. data/test/fixtures/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d +0 -0
  597. data/test/fixtures/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479 +0 -0
  598. data/test/fixtures/testrepo.git/objects/94/eca2de348d5f672faf56b0decafa5937e3235e +0 -0
  599. data/test/fixtures/testrepo.git/objects/9b/7384fe1676186192842f5d3e129457b62db9e3 +0 -0
  600. data/test/fixtures/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a +0 -3
  601. data/test/fixtures/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f +0 -2
  602. data/test/fixtures/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd +0 -0
  603. data/test/fixtures/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 +0 -0
  604. data/test/fixtures/testrepo.git/objects/b7/4713326bc972cc15751ed504dca6f6f3b91f7a +0 -3
  605. data/test/fixtures/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 +0 -3
  606. data/test/fixtures/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd +0 -3
  607. data/test/fixtures/testrepo.git/objects/c4/dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b +0 -0
  608. data/test/fixtures/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 +0 -0
  609. data/test/fixtures/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 +0 -0
  610. data/test/fixtures/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 +0 -0
  611. data/test/fixtures/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765 +0 -0
  612. data/test/fixtures/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx +0 -0
  613. data/test/fixtures/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack +0 -0
  614. data/test/fixtures/testrepo.git/packed-refs +0 -2
  615. data/test/fixtures/testrepo.git/refs/heads/master +0 -1
  616. data/test/fixtures/testrepo.git/refs/notes/commits +0 -1
  617. data/test/fixtures/testrepo.git/refs/tags/v0.9 +0 -1
  618. data/test/fixtures/testrepo.git/refs/tags/v1.0 +0 -1
  619. data/test/fixtures/text_file.md +0 -464
  620. data/test/fixtures/unsymlinked.git/HEAD +0 -1
  621. data/test/fixtures/unsymlinked.git/config +0 -6
  622. data/test/fixtures/unsymlinked.git/description +0 -1
  623. data/test/fixtures/unsymlinked.git/info/exclude +0 -2
  624. data/test/fixtures/unsymlinked.git/objects/08/8b64704e0d6b8bd061dea879418cb5442a3fbf +0 -0
  625. data/test/fixtures/unsymlinked.git/objects/13/a5e939bca25940c069fd2169d993dba328e30b +0 -0
  626. data/test/fixtures/unsymlinked.git/objects/19/bf568e59e3a0b363cafb4106226e62d4a4c41c +0 -0
  627. data/test/fixtures/unsymlinked.git/objects/58/1fadd35b4cf320d102a152f918729011604773 +0 -0
  628. data/test/fixtures/unsymlinked.git/objects/5c/87b6791e8b13da658a14d1ef7e09b5dc3bac8c +0 -0
  629. data/test/fixtures/unsymlinked.git/objects/6f/e5f5398af85fb3de8a6aba0339b6d3bfa26a27 +0 -0
  630. data/test/fixtures/unsymlinked.git/objects/7f/ccd75616ec188b8f1b23d67506a334cc34a49d +0 -0
  631. data/test/fixtures/unsymlinked.git/objects/80/6999882bf91d24241e4077906b9017605eb1f3 +0 -0
  632. data/test/fixtures/unsymlinked.git/objects/83/7d176303c5005505ec1e4a30231c40930c0230 +0 -0
  633. data/test/fixtures/unsymlinked.git/objects/a8/595ccca04f40818ae0155c8f9c77a230e597b6 +0 -2
  634. data/test/fixtures/unsymlinked.git/objects/cf/8f1cf5cce859c438d6cc067284cb5e161206e7 +0 -0
  635. data/test/fixtures/unsymlinked.git/objects/d5/278d05c8607ec420bfee4cf219fbc0eeebfd6a +0 -0
  636. data/test/fixtures/unsymlinked.git/objects/f4/e16fb76536591a41454194058d048d8e4dd2e9 +0 -0
  637. data/test/fixtures/unsymlinked.git/objects/f9/e65619d93fdf2673882e0a261c5e93b1a84006 +0 -0
  638. data/test/fixtures/unsymlinked.git/refs/heads/exe-file +0 -1
  639. data/test/fixtures/unsymlinked.git/refs/heads/master +0 -1
  640. data/test/fixtures/unsymlinked.git/refs/heads/reg-file +0 -1
  641. data/test/index_test.rb +0 -333
  642. data/test/lib_test.rb +0 -127
  643. data/test/note_test.rb +0 -158
  644. data/test/object_test.rb +0 -43
  645. data/test/reference_test.rb +0 -207
  646. data/test/remote_test.rb +0 -324
  647. data/test/repo_pack_test.rb +0 -24
  648. data/test/repo_reset_test.rb +0 -82
  649. data/test/repo_test.rb +0 -402
  650. data/test/tag_test.rb +0 -68
  651. data/test/test_helper.rb +0 -92
  652. data/test/tree_test.rb +0 -91
  653. data/test/walker_test.rb +0 -88
  654. data/vendor/libgit2/Makefile.embed +0 -42
  655. data/vendor/libgit2/include/git2/push.h +0 -131
  656. data/vendor/libgit2/include/git2/threads.h +0 -50
  657. data/vendor/libgit2/src/amiga/map.c +0 -48
  658. data/vendor/libgit2/src/bswap.h +0 -97
  659. data/vendor/libgit2/src/compress.c +0 -53
  660. data/vendor/libgit2/src/config_file.h +0 -60
  661. data/vendor/libgit2/src/delta-apply.c +0 -134
  662. data/vendor/libgit2/src/delta-apply.h +0 -50
  663. data/vendor/libgit2/src/diff_patch.c +0 -995
  664. data/vendor/libgit2/src/diff_patch.h +0 -46
  665. data/vendor/libgit2/src/hashsig.h +0 -72
  666. data/vendor/libgit2/src/merge_file.h +0 -71
  667. data/vendor/libgit2/src/win32/pthread.c +0 -144
  668. data/vendor/libgit2/src/win32/pthread.h +0 -50
@@ -5,234 +5,211 @@
5
5
  * a Linking Exception. For full terms see the included COPYING file.
6
6
  */
7
7
 
8
- #include "common.h"
9
8
  #include "config.h"
10
- #include "fileops.h"
9
+
11
10
  #include "filebuf.h"
11
+ #include "sysdir.h"
12
12
  #include "buffer.h"
13
13
  #include "buf_text.h"
14
14
  #include "git2/config.h"
15
15
  #include "git2/sys/config.h"
16
16
  #include "git2/types.h"
17
17
  #include "strmap.h"
18
+ #include "array.h"
19
+ #include "config_parse.h"
20
+ #include "config_entries.h"
18
21
 
19
22
  #include <ctype.h>
20
23
  #include <sys/types.h>
21
24
  #include <regex.h>
22
25
 
23
- GIT__USE_STRMAP;
24
-
25
- typedef struct cvar_t {
26
- struct cvar_t *next;
27
- git_config_entry *entry;
28
- } cvar_t;
29
-
30
- #define CVAR_LIST_HEAD(list) ((list)->head)
31
-
32
- #define CVAR_LIST_TAIL(list) ((list)->tail)
33
-
34
- #define CVAR_LIST_NEXT(var) ((var)->next)
35
-
36
- #define CVAR_LIST_EMPTY(list) ((list)->head == NULL)
26
+ /* Max depth for [include] directives */
27
+ #define MAX_INCLUDE_DEPTH 10
37
28
 
38
- #define CVAR_LIST_APPEND(list, var) do {\
39
- if (CVAR_LIST_EMPTY(list)) {\
40
- CVAR_LIST_HEAD(list) = CVAR_LIST_TAIL(list) = var;\
41
- } else {\
42
- CVAR_LIST_NEXT(CVAR_LIST_TAIL(list)) = var;\
43
- CVAR_LIST_TAIL(list) = var;\
44
- }\
45
- } while(0)
29
+ typedef struct {
30
+ git_config_backend parent;
31
+ /* mutex to coordinate accessing the values */
32
+ git_mutex values_mutex;
33
+ git_config_entries *entries;
34
+ const git_repository *repo;
35
+ git_config_level_t level;
36
+ } diskfile_header;
46
37
 
47
- #define CVAR_LIST_REMOVE_HEAD(list) do {\
48
- CVAR_LIST_HEAD(list) = CVAR_LIST_NEXT(CVAR_LIST_HEAD(list));\
49
- } while(0)
38
+ typedef struct {
39
+ diskfile_header header;
50
40
 
51
- #define CVAR_LIST_REMOVE_AFTER(var) do {\
52
- CVAR_LIST_NEXT(var) = CVAR_LIST_NEXT(CVAR_LIST_NEXT(var));\
53
- } while(0)
41
+ git_array_t(git_config_parser) readers;
54
42
 
55
- #define CVAR_LIST_FOREACH(list, iter)\
56
- for ((iter) = CVAR_LIST_HEAD(list);\
57
- (iter) != NULL;\
58
- (iter) = CVAR_LIST_NEXT(iter))
43
+ bool locked;
44
+ git_filebuf locked_buf;
45
+ git_buf locked_content;
59
46
 
60
- /*
61
- * Inspired by the FreeBSD functions
62
- */
63
- #define CVAR_LIST_FOREACH_SAFE(start, iter, tmp)\
64
- for ((iter) = CVAR_LIST_HEAD(vars);\
65
- (iter) && (((tmp) = CVAR_LIST_NEXT(iter) || 1));\
66
- (iter) = (tmp))
47
+ struct config_file file;
48
+ } diskfile_backend;
67
49
 
68
50
  typedef struct {
69
- git_config_backend parent;
70
-
71
- git_strmap *values;
51
+ diskfile_header header;
72
52
 
73
- struct {
74
- git_buf buffer;
75
- char *read_ptr;
76
- int line_number;
77
- int eof;
78
- } reader;
79
-
80
- char *file_path;
81
- time_t file_mtime;
82
- size_t file_size;
53
+ diskfile_backend *snapshot_from;
54
+ } diskfile_readonly_backend;
83
55
 
56
+ typedef struct {
57
+ const git_repository *repo;
58
+ const char *file_path;
59
+ git_config_entries *entries;
84
60
  git_config_level_t level;
85
- } diskfile_backend;
61
+ unsigned int depth;
62
+ } diskfile_parse_state;
86
63
 
87
- static int config_parse(diskfile_backend *cfg_file, git_config_level_t level);
88
- static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value);
89
- static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);
64
+ static int config_read(git_config_entries *entries, const git_repository *repo, git_config_file *file, git_config_level_t level, int depth);
65
+ static int config_write(diskfile_backend *cfg, const char *orig_key, const char *key, const regex_t *preg, const char *value);
90
66
  static char *escape_value(const char *ptr);
91
67
 
92
- static void set_parse_error(diskfile_backend *backend, int col, const char *error_str)
68
+ static int config_snapshot(git_config_backend **out, git_config_backend *in);
69
+
70
+ static int config_error_readonly(void)
93
71
  {
94
- giterr_set(GITERR_CONFIG, "Failed to parse config file: %s (in %s:%d, column %d)",
95
- error_str, backend->file_path, backend->reader.line_number, col);
72
+ git_error_set(GIT_ERROR_CONFIG, "this backend is read-only");
73
+ return -1;
96
74
  }
97
75
 
98
- static void cvar_free(cvar_t *var)
76
+ /**
77
+ * Take the current values map from the backend and increase its
78
+ * refcount. This is its own function to make sure we use the mutex to
79
+ * avoid the map pointer from changing under us.
80
+ */
81
+ static git_config_entries *diskfile_entries_take(diskfile_header *h)
99
82
  {
100
- if (var == NULL)
101
- return;
83
+ git_config_entries *entries;
84
+
85
+ if (git_mutex_lock(&h->values_mutex) < 0) {
86
+ git_error_set(GIT_ERROR_OS, "failed to lock config backend");
87
+ return NULL;
88
+ }
102
89
 
103
- git__free((char*)var->entry->name);
104
- git__free((char *)var->entry->value);
105
- git__free(var->entry);
106
- git__free(var);
90
+ entries = h->entries;
91
+ git_config_entries_incref(entries);
92
+
93
+ git_mutex_unlock(&h->values_mutex);
94
+
95
+ return entries;
107
96
  }
108
97
 
109
- int git_config_file_normalize_section(char *start, char *end)
98
+ static void config_file_clear(struct config_file *file)
110
99
  {
111
- char *scan;
100
+ struct config_file *include;
101
+ uint32_t i;
112
102
 
113
- if (start == end)
114
- return GIT_EINVALIDSPEC;
103
+ if (file == NULL)
104
+ return;
115
105
 
116
- /* Validate and downcase range */
117
- for (scan = start; *scan; ++scan) {
118
- if (end && scan >= end)
119
- break;
120
- if (isalnum(*scan))
121
- *scan = tolower(*scan);
122
- else if (*scan != '-' || scan == start)
123
- return GIT_EINVALIDSPEC;
106
+ git_array_foreach(file->includes, i, include) {
107
+ config_file_clear(include);
124
108
  }
109
+ git_array_clear(file->includes);
125
110
 
126
- if (scan == start)
127
- return GIT_EINVALIDSPEC;
128
-
129
- return 0;
111
+ git__free(file->path);
130
112
  }
131
113
 
132
- /* Take something the user gave us and make it nice for our hash function */
133
- static int normalize_name(const char *in, char **out)
114
+ static int config_open(git_config_backend *cfg, git_config_level_t level, const git_repository *repo)
134
115
  {
135
- char *name, *fdot, *ldot;
116
+ int res;
117
+ diskfile_backend *b = (diskfile_backend *)cfg;
136
118
 
137
- assert(in && out);
119
+ b->header.level = level;
120
+ b->header.repo = repo;
138
121
 
139
- name = git__strdup(in);
140
- GITERR_CHECK_ALLOC(name);
122
+ if ((res = git_config_entries_new(&b->header.entries)) < 0)
123
+ return res;
141
124
 
142
- fdot = strchr(name, '.');
143
- ldot = strrchr(name, '.');
125
+ if (!git_path_exists(b->file.path))
126
+ return 0;
144
127
 
145
- if (fdot == NULL || fdot == name || ldot == NULL || !ldot[1])
146
- goto invalid;
128
+ if (res < 0 || (res = config_read(b->header.entries, repo, &b->file, level, 0)) < 0) {
129
+ git_config_entries_free(b->header.entries);
130
+ b->header.entries = NULL;
131
+ }
147
132
 
148
- /* Validate and downcase up to first dot and after last dot */
149
- if (git_config_file_normalize_section(name, fdot) < 0 ||
150
- git_config_file_normalize_section(ldot + 1, NULL) < 0)
151
- goto invalid;
133
+ return res;
134
+ }
152
135
 
153
- /* If there is a middle range, make sure it doesn't have newlines */
154
- while (fdot < ldot)
155
- if (*fdot++ == '\n')
156
- goto invalid;
136
+ static int config_is_modified(int *modified, struct config_file *file)
137
+ {
138
+ git_config_file *include;
139
+ git_buf buf = GIT_BUF_INIT;
140
+ git_oid hash;
141
+ uint32_t i;
142
+ int error = 0;
157
143
 
158
- *out = name;
159
- return 0;
144
+ *modified = 0;
160
145
 
161
- invalid:
162
- git__free(name);
163
- giterr_set(GITERR_CONFIG, "Invalid config item name '%s'", in);
164
- return GIT_EINVALIDSPEC;
165
- }
146
+ if ((error = git_futils_readbuffer(&buf, file->path)) < 0)
147
+ goto out;
166
148
 
167
- static void free_vars(git_strmap *values)
168
- {
169
- cvar_t *var = NULL;
149
+ if ((error = git_hash_buf(&hash, buf.ptr, buf.size)) < 0)
150
+ goto out;
170
151
 
171
- if (values == NULL)
172
- return;
152
+ if (!git_oid_equal(&hash, &file->checksum)) {
153
+ *modified = 1;
154
+ goto out;
155
+ }
173
156
 
174
- git_strmap_foreach_value(values, var,
175
- while (var != NULL) {
176
- cvar_t *next = CVAR_LIST_NEXT(var);
177
- cvar_free(var);
178
- var = next;
179
- });
157
+ git_array_foreach(file->includes, i, include) {
158
+ if ((error = config_is_modified(modified, include)) < 0 || *modified)
159
+ goto out;
160
+ }
180
161
 
181
- git_strmap_free(values);
162
+ out:
163
+ git_buf_dispose(&buf);
164
+
165
+ return error;
182
166
  }
183
167
 
184
- static int config_open(git_config_backend *cfg, git_config_level_t level)
168
+ static int config_refresh(git_config_backend *cfg)
185
169
  {
186
- int res;
187
170
  diskfile_backend *b = (diskfile_backend *)cfg;
171
+ git_config_entries *entries = NULL, *tmp;
172
+ git_config_file *include;
173
+ int error, modified;
174
+ uint32_t i;
188
175
 
189
- b->level = level;
176
+ if (b->header.parent.readonly)
177
+ return config_error_readonly();
190
178
 
191
- b->values = git_strmap_alloc();
192
- GITERR_CHECK_ALLOC(b->values);
179
+ error = config_is_modified(&modified, &b->file);
180
+ if (error < 0 && error != GIT_ENOTFOUND)
181
+ goto out;
193
182
 
194
- git_buf_init(&b->reader.buffer, 0);
195
- res = git_futils_readbuffer_updated(
196
- &b->reader.buffer, b->file_path, &b->file_mtime, &b->file_size, NULL);
197
-
198
- /* It's fine if the file doesn't exist */
199
- if (res == GIT_ENOTFOUND)
183
+ if (!modified)
200
184
  return 0;
201
185
 
202
- if (res < 0 || (res = config_parse(b, level)) < 0) {
203
- free_vars(b->values);
204
- b->values = NULL;
186
+ if ((error = git_config_entries_new(&entries)) < 0)
187
+ goto out;
188
+
189
+ /* Reparse the current configuration */
190
+ git_array_foreach(b->file.includes, i, include) {
191
+ config_file_clear(include);
205
192
  }
193
+ git_array_clear(b->file.includes);
206
194
 
207
- git_buf_free(&b->reader.buffer);
208
- return res;
209
- }
195
+ if ((error = config_read(entries, b->header.repo, &b->file, b->header.level, 0)) < 0)
196
+ goto out;
210
197
 
211
- static int config_refresh(git_config_backend *cfg)
212
- {
213
- int res, updated = 0;
214
- diskfile_backend *b = (diskfile_backend *)cfg;
215
- git_strmap *old_values;
198
+ if ((error = git_mutex_lock(&b->header.values_mutex)) < 0) {
199
+ git_error_set(GIT_ERROR_OS, "failed to lock config backend");
200
+ goto out;
201
+ }
216
202
 
217
- res = git_futils_readbuffer_updated(
218
- &b->reader.buffer, b->file_path, &b->file_mtime, &b->file_size, &updated);
219
- if (res < 0 || !updated)
220
- return (res == GIT_ENOTFOUND) ? 0 : res;
203
+ tmp = b->header.entries;
204
+ b->header.entries = entries;
205
+ entries = tmp;
221
206
 
222
- /* need to reload - store old values and prep for reload */
223
- old_values = b->values;
224
- b->values = git_strmap_alloc();
225
- GITERR_CHECK_ALLOC(b->values);
207
+ git_mutex_unlock(&b->header.values_mutex);
226
208
 
227
- if ((res = config_parse(b, b->level)) < 0) {
228
- free_vars(b->values);
229
- b->values = old_values;
230
- } else {
231
- free_vars(old_values);
232
- }
209
+ out:
210
+ git_config_entries_free(entries);
233
211
 
234
- git_buf_free(&b->reader.buffer);
235
- return res;
212
+ return (error == GIT_ENOTFOUND) ? 0 : error;
236
213
  }
237
214
 
238
215
  static void backend_free(git_config_backend *_backend)
@@ -242,817 +219,684 @@ static void backend_free(git_config_backend *_backend)
242
219
  if (backend == NULL)
243
220
  return;
244
221
 
245
- git__free(backend->file_path);
246
- free_vars(backend->values);
222
+ config_file_clear(&backend->file);
223
+ git_config_entries_free(backend->header.entries);
224
+ git_mutex_free(&backend->header.values_mutex);
247
225
  git__free(backend);
248
226
  }
249
227
 
250
- static int file_foreach(
251
- git_config_backend *backend,
252
- const char *regexp,
253
- int (*fn)(const git_config_entry *, void *),
254
- void *data)
228
+ static int config_iterator_new(
229
+ git_config_iterator **iter,
230
+ struct git_config_backend* backend)
255
231
  {
256
- diskfile_backend *b = (diskfile_backend *)backend;
257
- cvar_t *var, *next_var;
258
- const char *key;
259
- regex_t regex;
260
- int result = 0;
261
-
262
- if (!b->values)
263
- return 0;
264
-
265
- if (regexp != NULL) {
266
- if ((result = regcomp(&regex, regexp, REG_EXTENDED)) < 0) {
267
- giterr_set_regex(&regex, result);
268
- regfree(&regex);
269
- return -1;
270
- }
271
- }
272
-
273
- git_strmap_foreach(b->values, key, var,
274
- for (; var != NULL; var = next_var) {
275
- next_var = CVAR_LIST_NEXT(var);
276
-
277
- /* skip non-matching keys if regexp was provided */
278
- if (regexp && regexec(&regex, key, 0, NULL, 0) != 0)
279
- continue;
232
+ diskfile_header *bh = (diskfile_header *) backend;
233
+ git_config_entries *entries;
234
+ int error;
280
235
 
281
- /* abort iterator on non-zero return value */
282
- if (fn(var->entry, data)) {
283
- giterr_clear();
284
- result = GIT_EUSER;
285
- goto cleanup;
286
- }
287
- }
288
- );
236
+ if ((error = git_config_entries_dup(&entries, bh->entries)) < 0)
237
+ return error;
289
238
 
290
- cleanup:
291
- if (regexp != NULL)
292
- regfree(&regex);
239
+ if ((error = git_config_entries_iterator_new(iter, entries)) < 0)
240
+ goto out;
293
241
 
294
- return result;
242
+ out:
243
+ /* Let iterator delete duplicated entries when it's done */
244
+ git_config_entries_free(entries);
245
+ return error;
295
246
  }
296
247
 
297
248
  static int config_set(git_config_backend *cfg, const char *name, const char *value)
298
249
  {
299
- cvar_t *var = NULL, *old_var = NULL;
300
250
  diskfile_backend *b = (diskfile_backend *)cfg;
251
+ git_config_entries *entries;
252
+ git_config_entry *existing;
301
253
  char *key, *esc_value = NULL;
302
- khiter_t pos;
303
- int rval, ret;
304
-
305
- if ((rval = normalize_name(name, &key)) < 0)
306
- return rval;
307
-
308
- /*
309
- * Try to find it in the existing values and update it if it
310
- * only has one value.
311
- */
312
- pos = git_strmap_lookup_index(b->values, key);
313
- if (git_strmap_valid_index(b->values, pos)) {
314
- cvar_t *existing = git_strmap_value_at(b->values, pos);
315
- char *tmp = NULL;
254
+ int error;
316
255
 
317
- git__free(key);
256
+ if ((error = git_config__normalize_name(name, &key)) < 0)
257
+ return error;
318
258
 
319
- if (existing->next != NULL) {
320
- giterr_set(GITERR_CONFIG, "Multivar incompatible with simple set");
321
- return -1;
322
- }
259
+ if ((entries = diskfile_entries_take(&b->header)) == NULL)
260
+ return -1;
323
261
 
262
+ /* Check whether we'd be modifying an included or multivar key */
263
+ if ((error = git_config_entries_get_unique(&existing, entries, key)) < 0) {
264
+ if (error != GIT_ENOTFOUND)
265
+ goto out;
266
+ error = 0;
267
+ } else if ((!existing->value && !value) ||
268
+ (existing->value && value && !strcmp(existing->value, value))) {
324
269
  /* don't update if old and new values already match */
325
- if ((!existing->entry->value && !value) ||
326
- (existing->entry->value && value && !strcmp(existing->entry->value, value)))
327
- return 0;
328
-
329
- if (value) {
330
- tmp = git__strdup(value);
331
- GITERR_CHECK_ALLOC(tmp);
332
- esc_value = escape_value(value);
333
- GITERR_CHECK_ALLOC(esc_value);
334
- }
335
-
336
- git__free((void *)existing->entry->value);
337
- existing->entry->value = tmp;
338
-
339
- ret = config_write(b, existing->entry->name, NULL, esc_value);
340
-
341
- git__free(esc_value);
342
- return ret;
270
+ error = 0;
271
+ goto out;
343
272
  }
344
273
 
345
- var = git__malloc(sizeof(cvar_t));
346
- GITERR_CHECK_ALLOC(var);
347
- memset(var, 0x0, sizeof(cvar_t));
348
- var->entry = git__malloc(sizeof(git_config_entry));
349
- GITERR_CHECK_ALLOC(var->entry);
350
- memset(var->entry, 0x0, sizeof(git_config_entry));
351
-
352
- var->entry->name = key;
353
- var->entry->value = NULL;
354
-
274
+ /* No early returns due to sanity checks, let's write it out and refresh */
355
275
  if (value) {
356
- var->entry->value = git__strdup(value);
357
- GITERR_CHECK_ALLOC(var->entry->value);
358
276
  esc_value = escape_value(value);
359
- GITERR_CHECK_ALLOC(esc_value);
277
+ GIT_ERROR_CHECK_ALLOC(esc_value);
360
278
  }
361
279
 
362
- if (config_write(b, key, NULL, esc_value) < 0) {
363
- git__free(esc_value);
364
- cvar_free(var);
365
- return -1;
366
- }
280
+ if ((error = config_write(b, name, key, NULL, esc_value)) < 0)
281
+ goto out;
367
282
 
283
+ error = config_refresh(cfg);
284
+
285
+ out:
286
+ git_config_entries_free(entries);
368
287
  git__free(esc_value);
369
- git_strmap_insert2(b->values, key, var, old_var, rval);
370
- if (rval < 0)
371
- return -1;
372
- if (old_var != NULL)
373
- cvar_free(old_var);
288
+ git__free(key);
289
+ return error;
290
+ }
374
291
 
375
- return 0;
292
+ /* release the map containing the entry as an equivalent to freeing it */
293
+ static void free_diskfile_entry(git_config_entry *entry)
294
+ {
295
+ git_config_entries *entries = (git_config_entries *) entry->payload;
296
+ git_config_entries_free(entries);
376
297
  }
377
298
 
378
299
  /*
379
300
  * Internal function that actually gets the value in string form
380
301
  */
381
- static int config_get(const git_config_backend *cfg, const char *name, const git_config_entry **out)
302
+ static int config_get(git_config_backend *cfg, const char *key, git_config_entry **out)
382
303
  {
383
- diskfile_backend *b = (diskfile_backend *)cfg;
384
- char *key;
385
- khiter_t pos;
386
- int error;
304
+ diskfile_header *h = (diskfile_header *)cfg;
305
+ git_config_entries *entries = NULL;
306
+ git_config_entry *entry;
307
+ int error = 0;
387
308
 
388
- if ((error = normalize_name(name, &key)) < 0)
309
+ if (!h->parent.readonly && ((error = config_refresh(cfg)) < 0))
389
310
  return error;
390
311
 
391
- pos = git_strmap_lookup_index(b->values, key);
392
- git__free(key);
393
-
394
- /* no error message; the config system will write one */
395
- if (!git_strmap_valid_index(b->values, pos))
396
- return GIT_ENOTFOUND;
397
-
398
- *out = ((cvar_t *)git_strmap_value_at(b->values, pos))->entry;
399
-
400
- return 0;
401
- }
402
-
403
- static int config_get_multivar(
404
- git_config_backend *cfg,
405
- const char *name,
406
- const char *regex_str,
407
- int (*fn)(const git_config_entry *, void *),
408
- void *data)
409
- {
410
- cvar_t *var;
411
- diskfile_backend *b = (diskfile_backend *)cfg;
412
- char *key;
413
- khiter_t pos;
414
- int error;
312
+ if ((entries = diskfile_entries_take(h)) == NULL)
313
+ return -1;
415
314
 
416
- if ((error = normalize_name(name, &key)) < 0)
315
+ if ((error = (git_config_entries_get(&entry, entries, key))) < 0) {
316
+ git_config_entries_free(entries);
417
317
  return error;
418
-
419
- pos = git_strmap_lookup_index(b->values, key);
420
- git__free(key);
421
-
422
- if (!git_strmap_valid_index(b->values, pos))
423
- return GIT_ENOTFOUND;
424
-
425
- var = git_strmap_value_at(b->values, pos);
426
-
427
- if (regex_str != NULL) {
428
- regex_t regex;
429
- int result;
430
-
431
- /* regex matching; build the regex */
432
- result = regcomp(&regex, regex_str, REG_EXTENDED);
433
- if (result < 0) {
434
- giterr_set_regex(&regex, result);
435
- regfree(&regex);
436
- return -1;
437
- }
438
-
439
- /* and throw the callback only on the variables that
440
- * match the regex */
441
- do {
442
- if (regexec(&regex, var->entry->value, 0, NULL, 0) == 0) {
443
- /* early termination by the user is not an error;
444
- * just break and return successfully */
445
- if (fn(var->entry, data) < 0)
446
- break;
447
- }
448
-
449
- var = var->next;
450
- } while (var != NULL);
451
- regfree(&regex);
452
- } else {
453
- /* no regex; go through all the variables */
454
- do {
455
- /* early termination by the user is not an error;
456
- * just break and return successfully */
457
- if (fn(var->entry, data) < 0)
458
- break;
459
-
460
- var = var->next;
461
- } while (var != NULL);
462
318
  }
463
319
 
320
+ entry->free = free_diskfile_entry;
321
+ entry->payload = entries;
322
+ *out = entry;
323
+
464
324
  return 0;
465
325
  }
466
326
 
467
327
  static int config_set_multivar(
468
328
  git_config_backend *cfg, const char *name, const char *regexp, const char *value)
469
329
  {
470
- int replaced = 0;
471
- cvar_t *var, *newvar;
472
330
  diskfile_backend *b = (diskfile_backend *)cfg;
473
331
  char *key;
474
332
  regex_t preg;
475
333
  int result;
476
- khiter_t pos;
477
334
 
478
335
  assert(regexp);
479
336
 
480
- if ((result = normalize_name(name, &key)) < 0)
481
- return result;
482
-
483
- pos = git_strmap_lookup_index(b->values, key);
484
- if (!git_strmap_valid_index(b->values, pos)) {
485
- /* If we don't have it, behave like a normal set */
486
- result = config_set(cfg, name, value);
487
- git__free(key);
337
+ if ((result = git_config__normalize_name(name, &key)) < 0)
488
338
  return result;
489
- }
490
339
 
491
- var = git_strmap_value_at(b->values, pos);
492
-
493
- result = regcomp(&preg, regexp, REG_EXTENDED);
494
- if (result < 0) {
495
- git__free(key);
496
- giterr_set_regex(&preg, result);
497
- regfree(&preg);
498
- return -1;
340
+ result = p_regcomp(&preg, regexp, REG_EXTENDED);
341
+ if (result != 0) {
342
+ git_error_set_regex(&preg, result);
343
+ result = -1;
344
+ goto out;
499
345
  }
500
346
 
501
- for (;;) {
502
- if (regexec(&preg, var->entry->value, 0, NULL, 0) == 0) {
503
- char *tmp = git__strdup(value);
504
- GITERR_CHECK_ALLOC(tmp);
505
-
506
- git__free((void *)var->entry->value);
507
- var->entry->value = tmp;
508
- replaced = 1;
509
- }
347
+ /* If we do have it, set call config_write() and reload */
348
+ if ((result = config_write(b, name, key, &preg, value)) < 0)
349
+ goto out;
510
350
 
511
- if (var->next == NULL)
512
- break;
351
+ result = config_refresh(cfg);
513
352
 
514
- var = var->next;
515
- }
353
+ out:
354
+ git__free(key);
355
+ regfree(&preg);
516
356
 
517
- /* If we've reached the end of the variables and we haven't found it yet, we need to append it */
518
- if (!replaced) {
519
- newvar = git__malloc(sizeof(cvar_t));
520
- GITERR_CHECK_ALLOC(newvar);
521
- memset(newvar, 0x0, sizeof(cvar_t));
522
- newvar->entry = git__malloc(sizeof(git_config_entry));
523
- GITERR_CHECK_ALLOC(newvar->entry);
524
- memset(newvar->entry, 0x0, sizeof(git_config_entry));
357
+ return result;
358
+ }
525
359
 
526
- newvar->entry->name = git__strdup(var->entry->name);
527
- GITERR_CHECK_ALLOC(newvar->entry->name);
360
+ static int config_delete(git_config_backend *cfg, const char *name)
361
+ {
362
+ diskfile_backend *b = (diskfile_backend *)cfg;
363
+ git_config_entries *entries = NULL;
364
+ git_config_entry *entry;
365
+ char *key = NULL;
366
+ int error;
528
367
 
529
- newvar->entry->value = git__strdup(value);
530
- GITERR_CHECK_ALLOC(newvar->entry->value);
368
+ if ((error = git_config__normalize_name(name, &key)) < 0)
369
+ goto out;
531
370
 
532
- newvar->entry->level = var->entry->level;
371
+ if ((entries = diskfile_entries_take(&b->header)) == NULL)
372
+ goto out;
533
373
 
534
- var->next = newvar;
374
+ /* Check whether we'd be modifying an included or multivar key */
375
+ if ((error = git_config_entries_get_unique(&entry, entries, key)) < 0) {
376
+ if (error == GIT_ENOTFOUND)
377
+ git_error_set(GIT_ERROR_CONFIG, "could not find key '%s' to delete", name);
378
+ goto out;
535
379
  }
536
380
 
537
- result = config_write(b, key, &preg, value);
381
+ if ((error = config_write(b, name, entry->name, NULL, NULL)) < 0)
382
+ goto out;
538
383
 
539
- git__free(key);
540
- regfree(&preg);
384
+ if ((error = config_refresh(cfg)) < 0)
385
+ goto out;
541
386
 
542
- return result;
387
+ out:
388
+ git_config_entries_free(entries);
389
+ git__free(key);
390
+ return error;
543
391
  }
544
392
 
545
- static int config_delete(git_config_backend *cfg, const char *name)
393
+ static int config_delete_multivar(git_config_backend *cfg, const char *name, const char *regexp)
546
394
  {
547
- cvar_t *var;
548
395
  diskfile_backend *b = (diskfile_backend *)cfg;
549
- char *key;
396
+ git_config_entries *entries = NULL;
397
+ git_config_entry *entry = NULL;
398
+ regex_t preg = { 0 };
399
+ char *key = NULL;
550
400
  int result;
551
- khiter_t pos;
552
-
553
- if ((result = normalize_name(name, &key)) < 0)
554
- return result;
555
401
 
556
- pos = git_strmap_lookup_index(b->values, key);
557
- git__free(key);
402
+ if ((result = git_config__normalize_name(name, &key)) < 0)
403
+ goto out;
558
404
 
559
- if (!git_strmap_valid_index(b->values, pos)) {
560
- giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name);
561
- return GIT_ENOTFOUND;
405
+ if ((entries = diskfile_entries_take(&b->header)) == NULL) {
406
+ result = -1;
407
+ goto out;
562
408
  }
563
409
 
564
- var = git_strmap_value_at(b->values, pos);
410
+ if ((result = git_config_entries_get(&entry, entries, key)) < 0) {
411
+ if (result == GIT_ENOTFOUND)
412
+ git_error_set(GIT_ERROR_CONFIG, "could not find key '%s' to delete", name);
413
+ goto out;
414
+ }
565
415
 
566
- if (var->next != NULL) {
567
- giterr_set(GITERR_CONFIG, "Cannot delete multivar with a single delete");
568
- return -1;
416
+ if ((result = p_regcomp(&preg, regexp, REG_EXTENDED)) != 0) {
417
+ git_error_set_regex(&preg, result);
418
+ result = -1;
419
+ goto out;
569
420
  }
570
421
 
571
- git_strmap_delete_at(b->values, pos);
422
+ if ((result = config_write(b, name, key, &preg, NULL)) < 0)
423
+ goto out;
572
424
 
573
- result = config_write(b, var->entry->name, NULL, NULL);
425
+ if ((result = config_refresh(cfg)) < 0)
426
+ goto out;
574
427
 
575
- cvar_free(var);
428
+ out:
429
+ git_config_entries_free(entries);
430
+ git__free(key);
431
+ regfree(&preg);
576
432
  return result;
577
433
  }
578
434
 
579
- int git_config_file__ondisk(git_config_backend **out, const char *path)
435
+ static int config_lock(git_config_backend *_cfg)
580
436
  {
581
- diskfile_backend *backend;
582
-
583
- backend = git__calloc(1, sizeof(diskfile_backend));
584
- GITERR_CHECK_ALLOC(backend);
585
-
586
- backend->parent.version = GIT_CONFIG_BACKEND_VERSION;
587
-
588
- backend->file_path = git__strdup(path);
589
- GITERR_CHECK_ALLOC(backend->file_path);
437
+ diskfile_backend *cfg = (diskfile_backend *) _cfg;
438
+ int error;
590
439
 
591
- backend->parent.open = config_open;
592
- backend->parent.get = config_get;
593
- backend->parent.get_multivar = config_get_multivar;
594
- backend->parent.set = config_set;
595
- backend->parent.set_multivar = config_set_multivar;
596
- backend->parent.del = config_delete;
597
- backend->parent.foreach = file_foreach;
598
- backend->parent.refresh = config_refresh;
599
- backend->parent.free = backend_free;
440
+ if ((error = git_filebuf_open(&cfg->locked_buf, cfg->file.path, 0, GIT_CONFIG_FILE_MODE)) < 0)
441
+ return error;
600
442
 
601
- *out = (git_config_backend *)backend;
443
+ error = git_futils_readbuffer(&cfg->locked_content, cfg->file.path);
444
+ if (error < 0 && error != GIT_ENOTFOUND) {
445
+ git_filebuf_cleanup(&cfg->locked_buf);
446
+ return error;
447
+ }
602
448
 
449
+ cfg->locked = true;
603
450
  return 0;
451
+
604
452
  }
605
453
 
606
- static int cfg_getchar_raw(diskfile_backend *cfg)
454
+ static int config_unlock(git_config_backend *_cfg, int success)
607
455
  {
608
- int c;
609
-
610
- c = *cfg->reader.read_ptr++;
456
+ diskfile_backend *cfg = (diskfile_backend *) _cfg;
457
+ int error = 0;
611
458
 
612
- /*
613
- Win 32 line breaks: if we find a \r\n sequence,
614
- return only the \n as a newline
615
- */
616
- if (c == '\r' && *cfg->reader.read_ptr == '\n') {
617
- cfg->reader.read_ptr++;
618
- c = '\n';
459
+ if (success) {
460
+ git_filebuf_write(&cfg->locked_buf, cfg->locked_content.ptr, cfg->locked_content.size);
461
+ error = git_filebuf_commit(&cfg->locked_buf);
619
462
  }
620
463
 
621
- if (c == '\n')
622
- cfg->reader.line_number++;
623
-
624
- if (c == 0) {
625
- cfg->reader.eof = 1;
626
- c = '\n';
627
- }
464
+ git_filebuf_cleanup(&cfg->locked_buf);
465
+ git_buf_dispose(&cfg->locked_content);
466
+ cfg->locked = false;
628
467
 
629
- return c;
468
+ return error;
630
469
  }
631
470
 
632
- #define SKIP_WHITESPACE (1 << 1)
633
- #define SKIP_COMMENTS (1 << 2)
634
-
635
- static int cfg_getchar(diskfile_backend *cfg_file, int flags)
471
+ int git_config_backend_from_file(git_config_backend **out, const char *path)
636
472
  {
637
- const int skip_whitespace = (flags & SKIP_WHITESPACE);
638
- const int skip_comments = (flags & SKIP_COMMENTS);
639
- int c;
640
-
641
- assert(cfg_file->reader.read_ptr);
473
+ diskfile_backend *backend;
642
474
 
643
- do c = cfg_getchar_raw(cfg_file);
644
- while (skip_whitespace && git__isspace(c) &&
645
- !cfg_file->reader.eof);
475
+ backend = git__calloc(1, sizeof(diskfile_backend));
476
+ GIT_ERROR_CHECK_ALLOC(backend);
477
+
478
+ backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
479
+ git_mutex_init(&backend->header.values_mutex);
480
+
481
+ backend->file.path = git__strdup(path);
482
+ GIT_ERROR_CHECK_ALLOC(backend->file.path);
483
+ git_array_init(backend->file.includes);
484
+
485
+ backend->header.parent.open = config_open;
486
+ backend->header.parent.get = config_get;
487
+ backend->header.parent.set = config_set;
488
+ backend->header.parent.set_multivar = config_set_multivar;
489
+ backend->header.parent.del = config_delete;
490
+ backend->header.parent.del_multivar = config_delete_multivar;
491
+ backend->header.parent.iterator = config_iterator_new;
492
+ backend->header.parent.snapshot = config_snapshot;
493
+ backend->header.parent.lock = config_lock;
494
+ backend->header.parent.unlock = config_unlock;
495
+ backend->header.parent.free = backend_free;
646
496
 
647
- if (skip_comments && (c == '#' || c == ';')) {
648
- do c = cfg_getchar_raw(cfg_file);
649
- while (c != '\n');
650
- }
497
+ *out = (git_config_backend *)backend;
651
498
 
652
- return c;
499
+ return 0;
653
500
  }
654
501
 
655
- /*
656
- * Read the next char, but don't move the reading pointer.
657
- */
658
- static int cfg_peek(diskfile_backend *cfg, int flags)
502
+ static int config_set_readonly(git_config_backend *cfg, const char *name, const char *value)
659
503
  {
660
- void *old_read_ptr;
661
- int old_lineno, old_eof;
662
- int ret;
663
-
664
- assert(cfg->reader.read_ptr);
665
-
666
- old_read_ptr = cfg->reader.read_ptr;
667
- old_lineno = cfg->reader.line_number;
668
- old_eof = cfg->reader.eof;
504
+ GIT_UNUSED(cfg);
505
+ GIT_UNUSED(name);
506
+ GIT_UNUSED(value);
669
507
 
670
- ret = cfg_getchar(cfg, flags);
671
-
672
- cfg->reader.read_ptr = old_read_ptr;
673
- cfg->reader.line_number = old_lineno;
674
- cfg->reader.eof = old_eof;
675
-
676
- return ret;
508
+ return config_error_readonly();
677
509
  }
678
510
 
679
- /*
680
- * Read and consume a line, returning it in newly-allocated memory.
681
- */
682
- static char *cfg_readline(diskfile_backend *cfg, bool skip_whitespace)
511
+ static int config_set_multivar_readonly(
512
+ git_config_backend *cfg, const char *name, const char *regexp, const char *value)
683
513
  {
684
- char *line = NULL;
685
- char *line_src, *line_end;
686
- size_t line_len;
514
+ GIT_UNUSED(cfg);
515
+ GIT_UNUSED(name);
516
+ GIT_UNUSED(regexp);
517
+ GIT_UNUSED(value);
687
518
 
688
- line_src = cfg->reader.read_ptr;
519
+ return config_error_readonly();
520
+ }
689
521
 
690
- if (skip_whitespace) {
691
- /* Skip empty empty lines */
692
- while (git__isspace(*line_src))
693
- ++line_src;
694
- }
522
+ static int config_delete_multivar_readonly(git_config_backend *cfg, const char *name, const char *regexp)
523
+ {
524
+ GIT_UNUSED(cfg);
525
+ GIT_UNUSED(name);
526
+ GIT_UNUSED(regexp);
695
527
 
696
- line_end = strchr(line_src, '\n');
528
+ return config_error_readonly();
529
+ }
697
530
 
698
- /* no newline at EOF */
699
- if (line_end == NULL)
700
- line_end = strchr(line_src, 0);
531
+ static int config_delete_readonly(git_config_backend *cfg, const char *name)
532
+ {
533
+ GIT_UNUSED(cfg);
534
+ GIT_UNUSED(name);
701
535
 
702
- line_len = line_end - line_src;
536
+ return config_error_readonly();
537
+ }
703
538
 
704
- line = git__malloc(line_len + 1);
705
- if (line == NULL)
706
- return NULL;
539
+ static int config_lock_readonly(git_config_backend *_cfg)
540
+ {
541
+ GIT_UNUSED(_cfg);
707
542
 
708
- memcpy(line, line_src, line_len);
543
+ return config_error_readonly();
544
+ }
709
545
 
710
- do line[line_len] = '\0';
711
- while (line_len-- > 0 && git__isspace(line[line_len]));
546
+ static int config_unlock_readonly(git_config_backend *_cfg, int success)
547
+ {
548
+ GIT_UNUSED(_cfg);
549
+ GIT_UNUSED(success);
712
550
 
713
- if (*line_end == '\n')
714
- line_end++;
551
+ return config_error_readonly();
552
+ }
715
553
 
716
- if (*line_end == '\0')
717
- cfg->reader.eof = 1;
554
+ static void backend_readonly_free(git_config_backend *_backend)
555
+ {
556
+ diskfile_backend *backend = (diskfile_backend *)_backend;
718
557
 
719
- cfg->reader.line_number++;
720
- cfg->reader.read_ptr = line_end;
558
+ if (backend == NULL)
559
+ return;
721
560
 
722
- return line;
561
+ git_config_entries_free(backend->header.entries);
562
+ git_mutex_free(&backend->header.values_mutex);
563
+ git__free(backend);
723
564
  }
724
565
 
725
- /*
726
- * Consume a line, without storing it anywhere
727
- */
728
- static void cfg_consume_line(diskfile_backend *cfg)
566
+ static int config_readonly_open(git_config_backend *cfg, git_config_level_t level, const git_repository *repo)
729
567
  {
730
- char *line_start, *line_end;
568
+ diskfile_readonly_backend *b = (diskfile_readonly_backend *) cfg;
569
+ diskfile_backend *src = b->snapshot_from;
570
+ diskfile_header *src_header = &src->header;
571
+ git_config_entries *entries;
572
+ int error;
731
573
 
732
- line_start = cfg->reader.read_ptr;
733
- line_end = strchr(line_start, '\n');
734
- /* No newline at EOF */
735
- if(line_end == NULL){
736
- line_end = strchr(line_start, '\0');
737
- }
574
+ if (!src_header->parent.readonly && (error = config_refresh(&src_header->parent)) < 0)
575
+ return error;
738
576
 
739
- if (*line_end == '\n')
740
- line_end++;
577
+ /* We're just copying data, don't care about the level or repo*/
578
+ GIT_UNUSED(level);
579
+ GIT_UNUSED(repo);
741
580
 
742
- if (*line_end == '\0')
743
- cfg->reader.eof = 1;
581
+ if ((entries = diskfile_entries_take(src_header)) == NULL)
582
+ return -1;
583
+ b->header.entries = entries;
744
584
 
745
- cfg->reader.line_number++;
746
- cfg->reader.read_ptr = line_end;
585
+ return 0;
747
586
  }
748
587
 
749
- GIT_INLINE(int) config_keychar(int c)
588
+ static int config_snapshot(git_config_backend **out, git_config_backend *in)
750
589
  {
751
- return isalnum(c) || c == '-';
752
- }
590
+ diskfile_readonly_backend *backend;
753
591
 
754
- static int parse_section_header_ext(diskfile_backend *cfg, const char *line, const char *base_name, char **section_name)
755
- {
756
- int c, rpos;
757
- char *first_quote, *last_quote;
758
- git_buf buf = GIT_BUF_INIT;
759
- int quote_marks;
760
- /*
761
- * base_name is what came before the space. We should be at the
762
- * first quotation mark, except for now, line isn't being kept in
763
- * sync so we only really use it to calculate the length.
764
- */
592
+ backend = git__calloc(1, sizeof(diskfile_readonly_backend));
593
+ GIT_ERROR_CHECK_ALLOC(backend);
765
594
 
766
- first_quote = strchr(line, '"');
767
- last_quote = strrchr(line, '"');
595
+ backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
596
+ git_mutex_init(&backend->header.values_mutex);
768
597
 
769
- if (last_quote - first_quote == 0) {
770
- set_parse_error(cfg, 0, "Missing closing quotation mark in section header");
771
- return -1;
772
- }
598
+ backend->snapshot_from = (diskfile_backend *) in;
773
599
 
774
- git_buf_grow(&buf, strlen(base_name) + last_quote - first_quote + 2);
775
- git_buf_printf(&buf, "%s.", base_name);
600
+ backend->header.parent.readonly = 1;
601
+ backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
602
+ backend->header.parent.open = config_readonly_open;
603
+ backend->header.parent.get = config_get;
604
+ backend->header.parent.set = config_set_readonly;
605
+ backend->header.parent.set_multivar = config_set_multivar_readonly;
606
+ backend->header.parent.del = config_delete_readonly;
607
+ backend->header.parent.del_multivar = config_delete_multivar_readonly;
608
+ backend->header.parent.iterator = config_iterator_new;
609
+ backend->header.parent.lock = config_lock_readonly;
610
+ backend->header.parent.unlock = config_unlock_readonly;
611
+ backend->header.parent.free = backend_readonly_free;
776
612
 
777
- rpos = 0;
778
- quote_marks = 0;
613
+ *out = (git_config_backend *)backend;
614
+
615
+ return 0;
616
+ }
779
617
 
780
- line = first_quote;
781
- c = line[rpos++];
618
+ static int included_path(git_buf *out, const char *dir, const char *path)
619
+ {
620
+ /* From the user's home */
621
+ if (path[0] == '~' && path[1] == '/')
622
+ return git_sysdir_expand_global_file(out, &path[1]);
782
623
 
783
- /*
784
- * At the end of each iteration, whatever is stored in c will be
785
- * added to the string. In case of error, jump to out
786
- */
787
- do {
788
- if (quote_marks == 2) {
789
- set_parse_error(cfg, rpos, "Unexpected text after closing quotes");
790
- git_buf_free(&buf);
791
- return -1;
792
- }
624
+ return git_path_join_unrooted(out, path, dir, NULL);
625
+ }
793
626
 
794
- switch (c) {
795
- case '"':
796
- ++quote_marks;
797
- continue;
627
+ /* Escape the values to write them to the file */
628
+ static char *escape_value(const char *ptr)
629
+ {
630
+ git_buf buf;
631
+ size_t len;
632
+ const char *esc;
798
633
 
799
- case '\\':
800
- c = line[rpos++];
634
+ assert(ptr);
801
635
 
802
- switch (c) {
803
- case '"':
804
- case '\\':
805
- break;
636
+ len = strlen(ptr);
637
+ if (!len)
638
+ return git__calloc(1, sizeof(char));
806
639
 
807
- default:
808
- set_parse_error(cfg, rpos, "Unsupported escape sequence");
809
- git_buf_free(&buf);
810
- return -1;
811
- }
640
+ if (git_buf_init(&buf, len) < 0)
641
+ return NULL;
812
642
 
813
- default:
814
- break;
643
+ while (*ptr != '\0') {
644
+ if ((esc = strchr(git_config_escaped, *ptr)) != NULL) {
645
+ git_buf_putc(&buf, '\\');
646
+ git_buf_putc(&buf, git_config_escapes[esc - git_config_escaped]);
647
+ } else {
648
+ git_buf_putc(&buf, *ptr);
815
649
  }
650
+ ptr++;
651
+ }
816
652
 
817
- git_buf_putc(&buf, c);
818
- } while ((c = line[rpos++]) != ']');
653
+ if (git_buf_oom(&buf))
654
+ return NULL;
819
655
 
820
- *section_name = git_buf_detach(&buf);
821
- return 0;
656
+ return git_buf_detach(&buf);
822
657
  }
823
658
 
824
- static int parse_section_header(diskfile_backend *cfg, char **section_out)
659
+ static int parse_include(git_config_parser *reader,
660
+ diskfile_parse_state *parse_data, const char *file)
825
661
  {
826
- char *name, *name_end;
827
- int name_length, c, pos;
662
+ struct config_file *include;
663
+ git_buf path = GIT_BUF_INIT;
664
+ char *dir;
828
665
  int result;
829
- char *line;
830
-
831
- line = cfg_readline(cfg, true);
832
- if (line == NULL)
833
- return -1;
834
666
 
835
- /* find the end of the variable's name */
836
- name_end = strchr(line, ']');
837
- if (name_end == NULL) {
838
- git__free(line);
839
- set_parse_error(cfg, 0, "Missing ']' in section header");
840
- return -1;
841
- }
667
+ if (!file)
668
+ return 0;
842
669
 
843
- name = (char *)git__malloc((size_t)(name_end - line) + 1);
844
- GITERR_CHECK_ALLOC(name);
670
+ if ((result = git_path_dirname_r(&path, reader->file->path)) < 0)
671
+ return result;
845
672
 
846
- name_length = 0;
847
- pos = 0;
673
+ dir = git_buf_detach(&path);
674
+ result = included_path(&path, dir, file);
675
+ git__free(dir);
848
676
 
849
- /* Make sure we were given a section header */
850
- c = line[pos++];
851
- assert(c == '[');
677
+ if (result < 0)
678
+ return result;
852
679
 
853
- c = line[pos++];
680
+ include = git_array_alloc(reader->file->includes);
681
+ memset(include, 0, sizeof(*include));
682
+ git_array_init(include->includes);
683
+ include->path = git_buf_detach(&path);
854
684
 
855
- do {
856
- if (git__isspace(c)){
857
- name[name_length] = '\0';
858
- result = parse_section_header_ext(cfg, line, name, section_out);
859
- git__free(line);
860
- git__free(name);
861
- return result;
862
- }
685
+ result = config_read(parse_data->entries, parse_data->repo,
686
+ include, parse_data->level, parse_data->depth+1);
863
687
 
864
- if (!config_keychar(c) && c != '.') {
865
- set_parse_error(cfg, pos, "Unexpected character in header");
866
- goto fail_parse;
867
- }
688
+ if (result == GIT_ENOTFOUND) {
689
+ git_error_clear();
690
+ result = 0;
691
+ }
868
692
 
869
- name[name_length++] = (char) tolower(c);
693
+ return result;
694
+ }
870
695
 
871
- } while ((c = line[pos++]) != ']');
696
+ static int do_match_gitdir(
697
+ int *matches,
698
+ const git_repository *repo,
699
+ const char *cfg_file,
700
+ const char *value,
701
+ bool case_insensitive)
702
+ {
703
+ git_buf path = GIT_BUF_INIT;
704
+ int error, fnmatch_flags;
705
+
706
+ if (value[0] == '.' && git_path_is_dirsep(value[1])) {
707
+ git_path_dirname_r(&path, cfg_file);
708
+ git_buf_joinpath(&path, path.ptr, value + 2);
709
+ } else if (value[0] == '~' && git_path_is_dirsep(value[1]))
710
+ git_sysdir_expand_global_file(&path, value + 1);
711
+ else if (!git_path_is_absolute(value))
712
+ git_buf_joinpath(&path, "**", value);
713
+ else
714
+ git_buf_sets(&path, value);
872
715
 
873
- if (line[pos - 1] != ']') {
874
- set_parse_error(cfg, pos, "Unexpected end of file");
875
- goto fail_parse;
716
+ if (git_buf_oom(&path)) {
717
+ error = -1;
718
+ goto out;
876
719
  }
877
720
 
878
- git__free(line);
721
+ if (git_path_is_dirsep(value[strlen(value) - 1]))
722
+ git_buf_puts(&path, "**");
879
723
 
880
- name[name_length] = 0;
881
- *section_out = name;
724
+ fnmatch_flags = FNM_PATHNAME|FNM_LEADING_DIR;
725
+ if (case_insensitive)
726
+ fnmatch_flags |= FNM_IGNORECASE;
882
727
 
883
- return 0;
728
+ if ((error = p_fnmatch(path.ptr, git_repository_path(repo), fnmatch_flags)) < 0)
729
+ goto out;
884
730
 
885
- fail_parse:
886
- git__free(line);
887
- git__free(name);
888
- return -1;
731
+ *matches = (error == 0);
732
+
733
+ out:
734
+ git_buf_dispose(&path);
735
+ return error;
889
736
  }
890
737
 
891
- static int skip_bom(diskfile_backend *cfg)
738
+ static int conditional_match_gitdir(
739
+ int *matches,
740
+ const git_repository *repo,
741
+ const char *cfg_file,
742
+ const char *value)
892
743
  {
893
- git_bom_t bom;
894
- int bom_offset = git_buf_text_detect_bom(&bom,
895
- &cfg->reader.buffer, cfg->reader.read_ptr - cfg->reader.buffer.ptr);
744
+ return do_match_gitdir(matches, repo, cfg_file, value, false);
745
+ }
896
746
 
897
- if (bom == GIT_BOM_UTF8)
898
- cfg->reader.read_ptr += bom_offset;
747
+ static int conditional_match_gitdir_i(
748
+ int *matches,
749
+ const git_repository *repo,
750
+ const char *cfg_file,
751
+ const char *value)
752
+ {
753
+ return do_match_gitdir(matches, repo, cfg_file, value, true);
754
+ }
899
755
 
900
- /* TODO: reference implementation is pretty stupid with BoM */
756
+ static const struct {
757
+ const char *prefix;
758
+ int (*matches)(int *matches, const git_repository *repo, const char *cfg, const char *value);
759
+ } conditions[] = {
760
+ { "gitdir:", conditional_match_gitdir },
761
+ { "gitdir/i:", conditional_match_gitdir_i }
762
+ };
901
763
 
902
- return 0;
903
- }
764
+ static int parse_conditional_include(git_config_parser *reader,
765
+ diskfile_parse_state *parse_data, const char *section, const char *file)
766
+ {
767
+ char *condition;
768
+ size_t i;
769
+ int error = 0, matches;
904
770
 
905
- /*
906
- (* basic types *)
907
- digit = "0".."9"
908
- integer = digit { digit }
909
- alphabet = "a".."z" + "A" .. "Z"
771
+ if (!parse_data->repo || !file)
772
+ return 0;
910
773
 
911
- section_char = alphabet | "." | "-"
912
- extension_char = (* any character except newline *)
913
- any_char = (* any character *)
914
- variable_char = "alphabet" | "-"
774
+ condition = git__substrdup(section + strlen("includeIf."),
775
+ strlen(section) - strlen("includeIf.") - strlen(".path"));
915
776
 
777
+ for (i = 0; i < ARRAY_SIZE(conditions); i++) {
778
+ if (git__prefixcmp(condition, conditions[i].prefix))
779
+ continue;
916
780
 
917
- (* actual grammar *)
918
- config = { section }
781
+ if ((error = conditions[i].matches(&matches,
782
+ parse_data->repo,
783
+ parse_data->file_path,
784
+ condition + strlen(conditions[i].prefix))) < 0)
785
+ break;
919
786
 
920
- section = header { definition }
787
+ if (matches)
788
+ error = parse_include(reader, parse_data, file);
921
789
 
922
- header = "[" section [subsection | subsection_ext] "]"
790
+ break;
791
+ }
923
792
 
924
- subsection = "." section
925
- subsection_ext = "\"" extension "\""
793
+ git__free(condition);
794
+ return error;
795
+ }
926
796
 
927
- section = section_char { section_char }
928
- extension = extension_char { extension_char }
797
+ static int read_on_variable(
798
+ git_config_parser *reader,
799
+ const char *current_section,
800
+ const char *var_name,
801
+ const char *var_value,
802
+ const char *line,
803
+ size_t line_len,
804
+ void *data)
805
+ {
806
+ diskfile_parse_state *parse_data = (diskfile_parse_state *)data;
807
+ git_buf buf = GIT_BUF_INIT;
808
+ git_config_entry *entry;
809
+ const char *c;
810
+ int result = 0;
929
811
 
930
- definition = variable_name ["=" variable_value] "\n"
812
+ GIT_UNUSED(line);
813
+ GIT_UNUSED(line_len);
931
814
 
932
- variable_name = variable_char { variable_char }
933
- variable_value = string | boolean | integer
815
+ if (current_section) {
816
+ /* TODO: Once warnings lang, we should likely warn
817
+ * here. Git appears to warn in most cases if it sees
818
+ * un-namespaced config options.
819
+ */
820
+ git_buf_puts(&buf, current_section);
821
+ git_buf_putc(&buf, '.');
822
+ }
934
823
 
935
- string = quoted_string | plain_string
936
- quoted_string = "\"" plain_string "\""
937
- plain_string = { any_char }
824
+ for (c = var_name; *c; c++)
825
+ git_buf_putc(&buf, git__tolower(*c));
938
826
 
939
- boolean = boolean_true | boolean_false
940
- boolean_true = "yes" | "1" | "true" | "on"
941
- boolean_false = "no" | "0" | "false" | "off"
942
- */
827
+ if (git_buf_oom(&buf))
828
+ return -1;
943
829
 
944
- static int strip_comments(char *line, int in_quotes)
945
- {
946
- int quote_count = in_quotes;
947
- char *ptr;
830
+ entry = git__calloc(1, sizeof(git_config_entry));
831
+ GIT_ERROR_CHECK_ALLOC(entry);
832
+ entry->name = git_buf_detach(&buf);
833
+ entry->value = var_value ? git__strdup(var_value) : NULL;
834
+ entry->level = parse_data->level;
835
+ entry->include_depth = parse_data->depth;
948
836
 
949
- for (ptr = line; *ptr; ++ptr) {
950
- if (ptr[0] == '"' && ptr > line && ptr[-1] != '\\')
951
- quote_count++;
837
+ if ((result = git_config_entries_append(parse_data->entries, entry)) < 0)
838
+ return result;
952
839
 
953
- if ((ptr[0] == ';' || ptr[0] == '#') && (quote_count % 2) == 0) {
954
- ptr[0] = '\0';
955
- break;
956
- }
957
- }
840
+ result = 0;
958
841
 
959
- /* skip any space at the end */
960
- if (ptr > line && git__isspace(ptr[-1])) {
961
- ptr--;
962
- }
963
- ptr[0] = '\0';
842
+ /* Add or append the new config option */
843
+ if (!git__strcmp(entry->name, "include.path"))
844
+ result = parse_include(reader, parse_data, entry->value);
845
+ else if (!git__prefixcmp(entry->name, "includeif.") &&
846
+ !git__suffixcmp(entry->name, ".path"))
847
+ result = parse_conditional_include(reader, parse_data,
848
+ entry->name, entry->value);
964
849
 
965
- return quote_count;
850
+ return result;
966
851
  }
967
852
 
968
- static int config_parse(diskfile_backend *cfg_file, git_config_level_t level)
853
+ static int config_read(
854
+ git_config_entries *entries,
855
+ const git_repository *repo,
856
+ git_config_file *file,
857
+ git_config_level_t level,
858
+ int depth)
969
859
  {
970
- int c;
971
- char *current_section = NULL;
972
- char *var_name;
973
- char *var_value;
974
- cvar_t *var, *existing;
975
- git_buf buf = GIT_BUF_INIT;
976
- int result = 0;
977
- khiter_t pos;
978
-
979
- /* Initialize the reading position */
980
- cfg_file->reader.read_ptr = cfg_file->reader.buffer.ptr;
981
- cfg_file->reader.eof = 0;
982
-
983
- /* If the file is empty, there's nothing for us to do */
984
- if (*cfg_file->reader.read_ptr == '\0')
985
- return 0;
860
+ diskfile_parse_state parse_data;
861
+ git_config_parser reader;
862
+ git_buf contents = GIT_BUF_INIT;
863
+ int error;
986
864
 
987
- skip_bom(cfg_file);
865
+ if (depth >= MAX_INCLUDE_DEPTH) {
866
+ git_error_set(GIT_ERROR_CONFIG, "maximum config include depth reached");
867
+ return -1;
868
+ }
988
869
 
989
- while (result == 0 && !cfg_file->reader.eof) {
870
+ if ((error = git_futils_readbuffer(&contents, file->path)) < 0)
871
+ goto out;
990
872
 
991
- c = cfg_peek(cfg_file, SKIP_WHITESPACE);
873
+ git_parse_ctx_init(&reader.ctx, contents.ptr, contents.size);
992
874
 
993
- switch (c) {
994
- case '\n': /* EOF when peeking, set EOF in the reader to exit the loop */
995
- cfg_file->reader.eof = 1;
996
- break;
875
+ if ((error = git_hash_buf(&file->checksum, contents.ptr, contents.size)) < 0)
876
+ goto out;
997
877
 
998
- case '[': /* section header, new section begins */
999
- git__free(current_section);
1000
- current_section = NULL;
1001
- result = parse_section_header(cfg_file, &current_section);
1002
- break;
878
+ /* Initialize the reading position */
879
+ reader.file = file;
880
+ git_parse_ctx_init(&reader.ctx, contents.ptr, contents.size);
1003
881
 
1004
- case ';':
1005
- case '#':
1006
- cfg_consume_line(cfg_file);
1007
- break;
882
+ /* If the file is empty, there's nothing for us to do */
883
+ if (!reader.ctx.content || *reader.ctx.content == '\0')
884
+ goto out;
1008
885
 
1009
- default: /* assume variable declaration */
1010
- result = parse_variable(cfg_file, &var_name, &var_value);
1011
- if (result < 0)
1012
- break;
1013
-
1014
- var = git__malloc(sizeof(cvar_t));
1015
- GITERR_CHECK_ALLOC(var);
1016
- memset(var, 0x0, sizeof(cvar_t));
1017
- var->entry = git__malloc(sizeof(git_config_entry));
1018
- GITERR_CHECK_ALLOC(var->entry);
1019
- memset(var->entry, 0x0, sizeof(git_config_entry));
1020
-
1021
- git__strtolower(var_name);
1022
- git_buf_printf(&buf, "%s.%s", current_section, var_name);
1023
- git__free(var_name);
1024
-
1025
- if (git_buf_oom(&buf))
1026
- return -1;
1027
-
1028
- var->entry->name = git_buf_detach(&buf);
1029
- var->entry->value = var_value;
1030
- var->entry->level = level;
1031
-
1032
- /* Add or append the new config option */
1033
- pos = git_strmap_lookup_index(cfg_file->values, var->entry->name);
1034
- if (!git_strmap_valid_index(cfg_file->values, pos)) {
1035
- git_strmap_insert(cfg_file->values, var->entry->name, var, result);
1036
- if (result < 0)
1037
- break;
1038
- result = 0;
1039
- } else {
1040
- existing = git_strmap_value_at(cfg_file->values, pos);
1041
- while (existing->next != NULL) {
1042
- existing = existing->next;
1043
- }
1044
- existing->next = var;
1045
- }
886
+ parse_data.repo = repo;
887
+ parse_data.file_path = file->path;
888
+ parse_data.entries = entries;
889
+ parse_data.level = level;
890
+ parse_data.depth = depth;
1046
891
 
1047
- break;
1048
- }
1049
- }
892
+ error = git_config_parse(&reader, NULL, read_on_variable, NULL, NULL, &parse_data);
1050
893
 
1051
- git__free(current_section);
1052
- return result;
894
+ out:
895
+ git_buf_dispose(&contents);
896
+ return error;
1053
897
  }
1054
898
 
1055
- static int write_section(git_filebuf *file, const char *key)
899
+ static int write_section(git_buf *fbuf, const char *key)
1056
900
  {
1057
901
  int result;
1058
902
  const char *dot;
@@ -1067,7 +911,7 @@ static int write_section(git_filebuf *file, const char *key)
1067
911
  char *escaped;
1068
912
  git_buf_put(&buf, key, dot - key);
1069
913
  escaped = escape_value(dot + 1);
1070
- GITERR_CHECK_ALLOC(escaped);
914
+ GIT_ERROR_CHECK_ALLOC(escaped);
1071
915
  git_buf_printf(&buf, " \"%s\"", escaped);
1072
916
  git__free(escaped);
1073
917
  }
@@ -1076,408 +920,288 @@ static int write_section(git_filebuf *file, const char *key)
1076
920
  if (git_buf_oom(&buf))
1077
921
  return -1;
1078
922
 
1079
- result = git_filebuf_write(file, git_buf_cstr(&buf), buf.size);
1080
- git_buf_free(&buf);
923
+ result = git_buf_put(fbuf, git_buf_cstr(&buf), buf.size);
924
+ git_buf_dispose(&buf);
1081
925
 
1082
926
  return result;
1083
927
  }
1084
928
 
1085
- /*
1086
- * This is pretty much the parsing, except we write out anything we don't have
1087
- */
1088
- static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char* value)
929
+ static const char *quotes_for_value(const char *value)
1089
930
  {
1090
- int result, c;
1091
- int section_matches = 0, last_section_matched = 0, preg_replaced = 0, write_trailer = 0;
1092
- const char *pre_end = NULL, *post_start = NULL, *data_start;
1093
- char *current_section = NULL, *section, *name, *ldot;
1094
- git_filebuf file = GIT_FILEBUF_INIT;
931
+ const char *ptr;
1095
932
 
1096
- /* We need to read in our own config file */
1097
- result = git_futils_readbuffer(&cfg->reader.buffer, cfg->file_path);
933
+ if (value[0] == ' ' || value[0] == '\0')
934
+ return "\"";
1098
935
 
1099
- /* Initialise the reading position */
1100
- if (result == GIT_ENOTFOUND) {
1101
- cfg->reader.read_ptr = NULL;
1102
- cfg->reader.eof = 1;
1103
- data_start = NULL;
1104
- git_buf_clear(&cfg->reader.buffer);
1105
- } else if (result == 0) {
1106
- cfg->reader.read_ptr = cfg->reader.buffer.ptr;
1107
- cfg->reader.eof = 0;
1108
- data_start = cfg->reader.read_ptr;
1109
- } else {
1110
- return -1; /* OS error when reading the file */
936
+ for (ptr = value; *ptr; ++ptr) {
937
+ if (*ptr == ';' || *ptr == '#')
938
+ return "\"";
1111
939
  }
1112
940
 
1113
- /* Lock the file */
1114
- if (git_filebuf_open(&file, cfg->file_path, 0) < 0)
1115
- return -1;
1116
-
1117
- skip_bom(cfg);
1118
- ldot = strrchr(key, '.');
1119
- name = ldot + 1;
1120
- section = git__strndup(key, ldot - key);
1121
-
1122
- while (!cfg->reader.eof) {
1123
- c = cfg_peek(cfg, SKIP_WHITESPACE);
941
+ if (ptr[-1] == ' ')
942
+ return "\"";
1124
943
 
1125
- if (c == '\0') { /* We've arrived at the end of the file */
1126
- break;
944
+ return "";
945
+ }
1127
946
 
1128
- } else if (c == '[') { /* section header, new section begins */
1129
- /*
1130
- * We set both positions to the current one in case we
1131
- * need to add a variable to the end of a section. In that
1132
- * case, we want both variables to point just before the
1133
- * new section. If we actually want to replace it, the
1134
- * default case will take care of updating them.
1135
- */
1136
- pre_end = post_start = cfg->reader.read_ptr;
1137
-
1138
- git__free(current_section);
1139
- current_section = NULL;
1140
- if (parse_section_header(cfg, &current_section) < 0)
1141
- goto rewrite_fail;
1142
-
1143
- /* Keep track of when it stops matching */
1144
- last_section_matched = section_matches;
1145
- section_matches = !strcmp(current_section, section);
1146
- }
947
+ struct write_data {
948
+ git_buf *buf;
949
+ git_buf buffered_comment;
950
+ unsigned int in_section : 1,
951
+ preg_replaced : 1;
952
+ const char *orig_section;
953
+ const char *section;
954
+ const char *orig_name;
955
+ const char *name;
956
+ const regex_t *preg;
957
+ const char *value;
958
+ };
959
+
960
+ static int write_line_to(git_buf *buf, const char *line, size_t line_len)
961
+ {
962
+ int result = git_buf_put(buf, line, line_len);
1147
963
 
1148
- else if (c == ';' || c == '#') {
1149
- cfg_consume_line(cfg);
1150
- }
964
+ if (!result && line_len && line[line_len-1] != '\n')
965
+ result = git_buf_printf(buf, "\n");
1151
966
 
1152
- else {
1153
- /*
1154
- * If the section doesn't match, but the last section did,
1155
- * it means we need to add a variable (so skip the line
1156
- * otherwise). If both the section and name match, we need
1157
- * to overwrite the variable (so skip the line
1158
- * otherwise). pre_end needs to be updated each time so we
1159
- * don't loose that information, but we only need to
1160
- * update post_start if we're going to use it in this
1161
- * iteration.
1162
- */
1163
- if (!section_matches) {
1164
- if (!last_section_matched) {
1165
- cfg_consume_line(cfg);
1166
- continue;
1167
- }
1168
- } else {
1169
- int has_matched = 0;
1170
- char *var_name, *var_value;
1171
-
1172
- pre_end = cfg->reader.read_ptr;
1173
- if (parse_variable(cfg, &var_name, &var_value) < 0)
1174
- goto rewrite_fail;
1175
-
1176
- /* First try to match the name of the variable */
1177
- if (strcasecmp(name, var_name) == 0)
1178
- has_matched = 1;
1179
-
1180
- /* If the name matches, and we have a regex to match the
1181
- * value, try to match it */
1182
- if (has_matched && preg != NULL)
1183
- has_matched = (regexec(preg, var_value, 0, NULL, 0) == 0);
1184
-
1185
- git__free(var_name);
1186
- git__free(var_value);
1187
-
1188
- /* if there is no match, keep going */
1189
- if (!has_matched)
1190
- continue;
1191
-
1192
- post_start = cfg->reader.read_ptr;
1193
- }
1194
-
1195
- /* We've found the variable we wanted to change, so
1196
- * write anything up to it */
1197
- git_filebuf_write(&file, data_start, pre_end - data_start);
1198
- preg_replaced = 1;
1199
-
1200
- /* Then replace the variable. If the value is NULL, it
1201
- * means we want to delete it, so don't write anything. */
1202
- if (value != NULL) {
1203
- git_filebuf_printf(&file, "\t%s = %s\n", name, value);
1204
- }
1205
-
1206
- /* multiline variable? we need to keep reading lines to match */
1207
- if (preg != NULL) {
1208
- data_start = post_start;
1209
- continue;
1210
- }
1211
-
1212
- write_trailer = 1;
1213
- break; /* break from the loop */
1214
- }
1215
- }
967
+ return result;
968
+ }
1216
969
 
1217
- /*
1218
- * Being here can mean that
1219
- *
1220
- * 1) our section is the last one in the file and we're
1221
- * adding a variable
1222
- *
1223
- * 2) we didn't find a section for us so we need to create it
1224
- * ourselves.
1225
- *
1226
- * 3) we're setting a multivar with a regex, which means we
1227
- * continue to search for matching values
1228
- *
1229
- * In the last case, if we've already replaced a value, we
1230
- * want to write the rest of the file. Otherwise we need to write
1231
- * out the whole file and then the new variable.
1232
- */
1233
- if (write_trailer) {
1234
- /* Write out rest of the file */
1235
- git_filebuf_write(&file, post_start, cfg->reader.buffer.size - (post_start - data_start));
1236
- } else {
1237
- if (preg_replaced) {
1238
- git_filebuf_printf(&file, "\n%s", data_start);
1239
- } else {
1240
- git_filebuf_write(&file, cfg->reader.buffer.ptr, cfg->reader.buffer.size);
1241
-
1242
- /* And now if we just need to add a variable */
1243
- if (!section_matches && write_section(&file, section) < 0)
1244
- goto rewrite_fail;
1245
-
1246
- /* Sanity check: if we are here, and value is NULL, that means that somebody
1247
- * touched the config file after our intial read. We should probably assert()
1248
- * this, but instead we'll handle it gracefully with an error. */
1249
- if (value == NULL) {
1250
- giterr_set(GITERR_CONFIG,
1251
- "Race condition when writing a config file (a cvar has been removed)");
1252
- goto rewrite_fail;
1253
- }
1254
-
1255
- /* If we are here, there is at least a section line */
1256
- if (cfg->reader.buffer.size > 0 && *(cfg->reader.buffer.ptr + cfg->reader.buffer.size - 1) != '\n')
1257
- git_filebuf_write(&file, "\n", 1);
1258
-
1259
- git_filebuf_printf(&file, "\t%s = %s\n", name, value);
1260
- }
1261
- }
970
+ static int write_line(struct write_data *write_data, const char *line, size_t line_len)
971
+ {
972
+ return write_line_to(write_data->buf, line, line_len);
973
+ }
1262
974
 
1263
- git__free(section);
1264
- git__free(current_section);
975
+ static int write_value(struct write_data *write_data)
976
+ {
977
+ const char *q;
978
+ int result;
1265
979
 
1266
- /* refresh stats - if this errors, then commit will error too */
1267
- (void)git_filebuf_stats(&cfg->file_mtime, &cfg->file_size, &file);
980
+ q = quotes_for_value(write_data->value);
981
+ result = git_buf_printf(write_data->buf,
982
+ "\t%s = %s%s%s\n", write_data->orig_name, q, write_data->value, q);
1268
983
 
1269
- result = git_filebuf_commit(&file, GIT_CONFIG_FILE_MODE);
1270
- git_buf_free(&cfg->reader.buffer);
984
+ /* If we are updating a single name/value, we're done. Setting `value`
985
+ * to `NULL` will prevent us from trying to write it again later (in
986
+ * `write_on_section`) if we see the same section repeated.
987
+ */
988
+ if (!write_data->preg)
989
+ write_data->value = NULL;
1271
990
 
1272
991
  return result;
1273
-
1274
- rewrite_fail:
1275
- git__free(section);
1276
- git__free(current_section);
1277
-
1278
- git_filebuf_cleanup(&file);
1279
- git_buf_free(&cfg->reader.buffer);
1280
- return -1;
1281
992
  }
1282
993
 
1283
- static const char *escapes = "ntb\"\\";
1284
- static const char *escaped = "\n\t\b\"\\";
1285
-
1286
- /* Escape the values to write them to the file */
1287
- static char *escape_value(const char *ptr)
994
+ static int write_on_section(
995
+ git_config_parser *reader,
996
+ const char *current_section,
997
+ const char *line,
998
+ size_t line_len,
999
+ void *data)
1288
1000
  {
1289
- git_buf buf = GIT_BUF_INIT;
1290
- size_t len;
1291
- const char *esc;
1001
+ struct write_data *write_data = (struct write_data *)data;
1002
+ int result = 0;
1292
1003
 
1293
- assert(ptr);
1004
+ GIT_UNUSED(reader);
1294
1005
 
1295
- len = strlen(ptr);
1296
- git_buf_grow(&buf, len);
1006
+ /* If we were previously in the correct section (but aren't anymore)
1007
+ * and haven't written our value (for a simple name/value set, not
1008
+ * a multivar), then append it to the end of the section before writing
1009
+ * the new one.
1010
+ */
1011
+ if (write_data->in_section && !write_data->preg && write_data->value)
1012
+ result = write_value(write_data);
1297
1013
 
1298
- while (*ptr != '\0') {
1299
- if ((esc = strchr(escaped, *ptr)) != NULL) {
1300
- git_buf_putc(&buf, '\\');
1301
- git_buf_putc(&buf, escapes[esc - escaped]);
1302
- } else {
1303
- git_buf_putc(&buf, *ptr);
1304
- }
1305
- ptr++;
1306
- }
1014
+ write_data->in_section = strcmp(current_section, write_data->section) == 0;
1307
1015
 
1308
- if (git_buf_oom(&buf)) {
1309
- git_buf_free(&buf);
1310
- return NULL;
1016
+ /*
1017
+ * If there were comments just before this section, dump them as well.
1018
+ */
1019
+ if (!result) {
1020
+ result = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size);
1021
+ git_buf_clear(&write_data->buffered_comment);
1311
1022
  }
1312
1023
 
1313
- return git_buf_detach(&buf);
1024
+ if (!result)
1025
+ result = write_line(write_data, line, line_len);
1026
+
1027
+ return result;
1314
1028
  }
1315
1029
 
1316
- /* '\"' -> '"' etc */
1317
- static char *fixup_line(const char *ptr, int quote_count)
1030
+ static int write_on_variable(
1031
+ git_config_parser *reader,
1032
+ const char *current_section,
1033
+ const char *var_name,
1034
+ const char *var_value,
1035
+ const char *line,
1036
+ size_t line_len,
1037
+ void *data)
1318
1038
  {
1319
- char *str = git__malloc(strlen(ptr) + 1);
1320
- char *out = str, *esc;
1039
+ struct write_data *write_data = (struct write_data *)data;
1040
+ bool has_matched = false;
1041
+ int error;
1321
1042
 
1322
- if (str == NULL)
1323
- return NULL;
1043
+ GIT_UNUSED(reader);
1044
+ GIT_UNUSED(current_section);
1324
1045
 
1325
- while (*ptr != '\0') {
1326
- if (*ptr == '"') {
1327
- quote_count++;
1328
- } else if (*ptr != '\\') {
1329
- *out++ = *ptr;
1330
- } else {
1331
- /* backslash, check the next char */
1332
- ptr++;
1333
- /* if we're at the end, it's a multiline, so keep the backslash */
1334
- if (*ptr == '\0') {
1335
- *out++ = '\\';
1336
- goto out;
1337
- }
1338
- if ((esc = strchr(escapes, *ptr)) != NULL) {
1339
- *out++ = escaped[esc - escapes];
1340
- } else {
1341
- git__free(str);
1342
- giterr_set(GITERR_CONFIG, "Invalid escape at %s", ptr);
1343
- return NULL;
1344
- }
1345
- }
1346
- ptr++;
1347
- }
1046
+ /*
1047
+ * If there were comments just before this variable, let's dump them as well.
1048
+ */
1049
+ if ((error = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size)) < 0)
1050
+ return error;
1348
1051
 
1349
- out:
1350
- *out = '\0';
1052
+ git_buf_clear(&write_data->buffered_comment);
1351
1053
 
1352
- return str;
1353
- }
1054
+ /* See if we are to update this name/value pair; first examine name */
1055
+ if (write_data->in_section &&
1056
+ strcasecmp(write_data->name, var_name) == 0)
1057
+ has_matched = true;
1354
1058
 
1355
- static int is_multiline_var(const char *str)
1356
- {
1357
- int count = 0;
1358
- const char *end = str + strlen(str);
1359
- while (end > str && end[-1] == '\\') {
1360
- count++;
1361
- end--;
1362
- }
1059
+ /* If we have a regex to match the value, see if it matches */
1060
+ if (has_matched && write_data->preg != NULL)
1061
+ has_matched = (regexec(write_data->preg, var_value, 0, NULL, 0) == 0);
1363
1062
 
1364
- /* An odd number means last backslash wasn't escaped, so it's multiline */
1365
- return (end > str) && (count & 1);
1366
- }
1063
+ /* If this isn't the name/value we're looking for, simply dump the
1064
+ * existing data back out and continue on.
1065
+ */
1066
+ if (!has_matched)
1067
+ return write_line(write_data, line, line_len);
1367
1068
 
1368
- static int parse_multiline_variable(diskfile_backend *cfg, git_buf *value, int in_quotes)
1369
- {
1370
- char *line = NULL, *proc_line = NULL;
1371
- int quote_count;
1069
+ write_data->preg_replaced = 1;
1372
1070
 
1373
- /* Check that the next line exists */
1374
- line = cfg_readline(cfg, false);
1375
- if (line == NULL)
1376
- return -1;
1071
+ /* If value is NULL, we are deleting this value; write nothing. */
1072
+ if (!write_data->value)
1073
+ return 0;
1377
1074
 
1378
- /* We've reached the end of the file, there is input missing */
1379
- if (line[0] == '\0') {
1380
- set_parse_error(cfg, 0, "Unexpected end of file while parsing multine var");
1381
- git__free(line);
1382
- return -1;
1383
- }
1075
+ return write_value(write_data);
1076
+ }
1384
1077
 
1385
- quote_count = strip_comments(line, !!in_quotes);
1078
+ static int write_on_comment(git_config_parser *reader, const char *line, size_t line_len, void *data)
1079
+ {
1080
+ struct write_data *write_data;
1386
1081
 
1387
- /* If it was just a comment, pretend it didn't exist */
1388
- if (line[0] == '\0') {
1389
- git__free(line);
1390
- return parse_multiline_variable(cfg, value, quote_count);
1391
- /* TODO: unbounded recursion. This **could** be exploitable */
1392
- }
1082
+ GIT_UNUSED(reader);
1083
+
1084
+ write_data = (struct write_data *)data;
1085
+ return write_line_to(&write_data->buffered_comment, line, line_len);
1086
+ }
1393
1087
 
1394
- /* Drop the continuation character '\': to closely follow the UNIX
1395
- * standard, this character **has** to be last one in the buf, with
1396
- * no whitespace after it */
1397
- assert(is_multiline_var(value->ptr));
1398
- git_buf_truncate(value, git_buf_len(value) - 1);
1088
+ static int write_on_eof(
1089
+ git_config_parser *reader, const char *current_section, void *data)
1090
+ {
1091
+ struct write_data *write_data = (struct write_data *)data;
1092
+ int result = 0;
1399
1093
 
1400
- proc_line = fixup_line(line, in_quotes);
1401
- if (proc_line == NULL) {
1402
- git__free(line);
1403
- return -1;
1404
- }
1405
- /* add this line to the multiline var */
1406
- git_buf_puts(value, proc_line);
1407
- git__free(line);
1408
- git__free(proc_line);
1094
+ GIT_UNUSED(reader);
1409
1095
 
1410
1096
  /*
1411
- * If we need to continue reading the next line, let's just
1412
- * keep putting stuff in the buffer
1097
+ * If we've buffered comments when reaching EOF, make sure to dump them.
1413
1098
  */
1414
- if (is_multiline_var(value->ptr))
1415
- return parse_multiline_variable(cfg, value, quote_count);
1099
+ if ((result = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size)) < 0)
1100
+ return result;
1416
1101
 
1417
- return 0;
1418
- }
1102
+ /* If we are at the EOF and have not written our value (again, for a
1103
+ * simple name/value set, not a multivar) then we have never seen the
1104
+ * section in question and should create a new section and write the
1105
+ * value.
1106
+ */
1107
+ if ((!write_data->preg || !write_data->preg_replaced) && write_data->value) {
1108
+ /* write the section header unless we're already in it */
1109
+ if (!current_section || strcmp(current_section, write_data->section))
1110
+ result = write_section(write_data->buf, write_data->orig_section);
1419
1111
 
1420
- static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value)
1421
- {
1422
- const char *var_end = NULL;
1423
- const char *value_start = NULL;
1424
- char *line;
1425
- int quote_count;
1112
+ if (!result)
1113
+ result = write_value(write_data);
1114
+ }
1426
1115
 
1427
- line = cfg_readline(cfg, true);
1428
- if (line == NULL)
1429
- return -1;
1116
+ return result;
1117
+ }
1430
1118
 
1431
- quote_count = strip_comments(line, 0);
1119
+ /*
1120
+ * This is pretty much the parsing, except we write out anything we don't have
1121
+ */
1122
+ static int config_write(diskfile_backend *cfg, const char *orig_key, const char *key, const regex_t *preg, const char* value)
1123
+ {
1124
+ int result;
1125
+ char *orig_section, *section, *orig_name, *name, *ldot;
1126
+ git_filebuf file = GIT_FILEBUF_INIT;
1127
+ git_buf buf = GIT_BUF_INIT, contents = GIT_BUF_INIT;
1128
+ git_config_parser reader;
1129
+ struct write_data write_data;
1432
1130
 
1433
- var_end = strchr(line, '=');
1131
+ memset(&reader, 0, sizeof(reader));
1132
+ reader.file = &cfg->file;
1434
1133
 
1435
- if (var_end == NULL)
1436
- var_end = strchr(line, '\0');
1437
- else
1438
- value_start = var_end + 1;
1134
+ if (cfg->locked) {
1135
+ result = git_buf_puts(&contents, git_buf_cstr(&cfg->locked_content));
1136
+ } else {
1137
+ /* Lock the file */
1138
+ if ((result = git_filebuf_open(
1139
+ &file, cfg->file.path, GIT_FILEBUF_HASH_CONTENTS, GIT_CONFIG_FILE_MODE)) < 0) {
1140
+ git_buf_dispose(&contents);
1141
+ return result;
1142
+ }
1439
1143
 
1440
- do var_end--;
1441
- while (var_end>line && git__isspace(*var_end));
1144
+ /* We need to read in our own config file */
1145
+ result = git_futils_readbuffer(&contents, cfg->file.path);
1146
+ }
1442
1147
 
1443
- *var_name = git__strndup(line, var_end - line + 1);
1444
- GITERR_CHECK_ALLOC(*var_name);
1148
+ /* Initialise the reading position */
1149
+ if (result == 0 || result == GIT_ENOTFOUND) {
1150
+ git_parse_ctx_init(&reader.ctx, contents.ptr, contents.size);
1151
+ } else {
1152
+ git_filebuf_cleanup(&file);
1153
+ return -1; /* OS error when reading the file */
1154
+ }
1445
1155
 
1446
- /* If there is no value, boolean true is assumed */
1447
- *var_value = NULL;
1156
+ ldot = strrchr(key, '.');
1157
+ name = ldot + 1;
1158
+ section = git__strndup(key, ldot - key);
1159
+ GIT_ERROR_CHECK_ALLOC(section);
1160
+
1161
+ ldot = strrchr(orig_key, '.');
1162
+ orig_name = ldot + 1;
1163
+ orig_section = git__strndup(orig_key, ldot - orig_key);
1164
+ GIT_ERROR_CHECK_ALLOC(orig_section);
1165
+
1166
+ write_data.buf = &buf;
1167
+ git_buf_init(&write_data.buffered_comment, 0);
1168
+ write_data.orig_section = orig_section;
1169
+ write_data.section = section;
1170
+ write_data.in_section = 0;
1171
+ write_data.preg_replaced = 0;
1172
+ write_data.orig_name = orig_name;
1173
+ write_data.name = name;
1174
+ write_data.preg = preg;
1175
+ write_data.value = value;
1176
+
1177
+ result = git_config_parse(&reader,
1178
+ write_on_section,
1179
+ write_on_variable,
1180
+ write_on_comment,
1181
+ write_on_eof,
1182
+ &write_data);
1183
+ git__free(section);
1184
+ git__free(orig_section);
1185
+ git_buf_dispose(&write_data.buffered_comment);
1448
1186
 
1449
- /*
1450
- * Now, let's try to parse the value
1451
- */
1452
- if (value_start != NULL) {
1453
- while (git__isspace(value_start[0]))
1454
- value_start++;
1455
-
1456
- if (is_multiline_var(value_start)) {
1457
- git_buf multi_value = GIT_BUF_INIT;
1458
- char *proc_line = fixup_line(value_start, 0);
1459
- GITERR_CHECK_ALLOC(proc_line);
1460
- git_buf_puts(&multi_value, proc_line);
1461
- git__free(proc_line);
1462
- if (parse_multiline_variable(cfg, &multi_value, quote_count) < 0 || git_buf_oom(&multi_value)) {
1463
- git__free(*var_name);
1464
- git__free(line);
1465
- git_buf_free(&multi_value);
1466
- return -1;
1467
- }
1468
-
1469
- *var_value = git_buf_detach(&multi_value);
1187
+ if (result < 0) {
1188
+ git_filebuf_cleanup(&file);
1189
+ goto done;
1190
+ }
1470
1191
 
1471
- }
1472
- else if (value_start[0] != '\0') {
1473
- *var_value = fixup_line(value_start, 0);
1474
- GITERR_CHECK_ALLOC(*var_value);
1475
- } else { /* equals sign but missing rhs */
1476
- *var_value = git__strdup("");
1477
- GITERR_CHECK_ALLOC(*var_value);
1478
- }
1192
+ if (cfg->locked) {
1193
+ size_t len = buf.asize;
1194
+ /* Update our copy with the modified contents */
1195
+ git_buf_dispose(&cfg->locked_content);
1196
+ git_buf_attach(&cfg->locked_content, git_buf_detach(&buf), len);
1197
+ } else {
1198
+ git_filebuf_write(&file, git_buf_cstr(&buf), git_buf_len(&buf));
1199
+ result = git_filebuf_commit(&file);
1479
1200
  }
1480
1201
 
1481
- git__free(line);
1482
- return 0;
1202
+ done:
1203
+ git_buf_dispose(&buf);
1204
+ git_buf_dispose(&contents);
1205
+ git_parse_ctx_clear(&reader.ctx);
1206
+ return result;
1483
1207
  }