rugged 0.18.0.gh.de28323 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (283) hide show
  1. data/README.md +9 -4
  2. data/Rakefile +1 -1
  3. data/ext/rugged/extconf.rb +10 -0
  4. data/ext/rugged/rugged.c +153 -86
  5. data/ext/rugged/rugged.h +44 -33
  6. data/ext/rugged/rugged_blob.c +288 -60
  7. data/ext/rugged/rugged_branch.c +82 -57
  8. data/ext/rugged/rugged_commit.c +83 -86
  9. data/ext/rugged/rugged_config.c +68 -68
  10. data/ext/rugged/rugged_diff.c +509 -0
  11. data/ext/rugged/rugged_diff_delta.c +94 -0
  12. data/ext/rugged/rugged_diff_hunk.c +100 -0
  13. data/ext/rugged/rugged_diff_line.c +79 -0
  14. data/ext/rugged/rugged_diff_patch.c +169 -0
  15. data/ext/rugged/rugged_index.c +539 -8
  16. data/ext/rugged/rugged_note.c +74 -80
  17. data/ext/rugged/rugged_object.c +63 -8
  18. data/ext/rugged/rugged_reference.c +231 -145
  19. data/ext/rugged/rugged_remote.c +509 -53
  20. data/ext/rugged/rugged_repo.c +572 -236
  21. data/ext/rugged/rugged_revwalk.c +59 -36
  22. data/ext/rugged/rugged_settings.c +7 -9
  23. data/ext/rugged/rugged_signature.c +7 -11
  24. data/ext/rugged/rugged_tag.c +93 -39
  25. data/ext/rugged/rugged_tree.c +321 -58
  26. data/lib/rugged.rb +1 -0
  27. data/lib/rugged/commit.rb +16 -1
  28. data/lib/rugged/console.rb +9 -0
  29. data/lib/rugged/diff.rb +19 -0
  30. data/lib/rugged/diff/delta.rb +54 -0
  31. data/lib/rugged/diff/hunk.rb +23 -0
  32. data/lib/rugged/diff/line.rb +29 -0
  33. data/lib/rugged/diff/patch.rb +28 -0
  34. data/lib/rugged/repository.rb +36 -39
  35. data/lib/rugged/version.rb +1 -1
  36. data/test/blob_test.rb +308 -1
  37. data/test/branch_test.rb +7 -0
  38. data/test/commit_test.rb +7 -10
  39. data/test/coverage/cover.rb +9 -1
  40. data/test/diff_test.rb +777 -0
  41. data/test/fixtures/archive.tar.gz +0 -0
  42. data/test/fixtures/attr/attr0 +1 -0
  43. data/test/fixtures/attr/attr1 +29 -0
  44. data/test/fixtures/attr/attr2 +21 -0
  45. data/test/fixtures/attr/attr3 +4 -0
  46. data/test/fixtures/attr/binfile +1 -0
  47. data/test/fixtures/attr/dir/file +0 -0
  48. data/test/fixtures/attr/file +1 -0
  49. data/test/fixtures/attr/gitattributes +29 -0
  50. data/test/fixtures/attr/gitignore +2 -0
  51. data/test/fixtures/attr/ign +1 -0
  52. data/test/fixtures/attr/macro_bad +1 -0
  53. data/test/fixtures/attr/macro_test +1 -0
  54. data/test/fixtures/attr/root_test1 +1 -0
  55. data/test/fixtures/attr/root_test2 +6 -0
  56. data/test/fixtures/attr/root_test3 +19 -0
  57. data/test/fixtures/attr/root_test4.txt +14 -0
  58. data/test/fixtures/attr/sub/abc +37 -0
  59. data/test/fixtures/attr/sub/dir/file +0 -0
  60. data/test/fixtures/attr/sub/file +1 -0
  61. data/test/fixtures/attr/sub/ign/file +1 -0
  62. data/test/fixtures/attr/sub/ign/sub/file +1 -0
  63. data/test/fixtures/attr/sub/sub/dir +0 -0
  64. data/test/fixtures/attr/sub/sub/file +1 -0
  65. data/test/fixtures/attr/sub/sub/subsub.txt +1 -0
  66. data/test/fixtures/attr/sub/subdir_test1 +2 -0
  67. data/test/fixtures/attr/sub/subdir_test2.txt +1 -0
  68. data/test/fixtures/diff/another.txt +38 -0
  69. data/test/fixtures/diff/readme.txt +36 -0
  70. data/test/fixtures/mergedrepo/conflicts-one.txt +5 -0
  71. data/test/fixtures/mergedrepo/conflicts-two.txt +5 -0
  72. data/test/fixtures/mergedrepo/one.txt +10 -0
  73. data/test/fixtures/mergedrepo/two.txt +12 -0
  74. data/test/fixtures/status/current_file +1 -0
  75. data/test/fixtures/status/ignored_file +1 -0
  76. data/test/fixtures/status/modified_file +2 -0
  77. data/test/fixtures/status/new_file +1 -0
  78. data/test/fixtures/status/staged_changes +2 -0
  79. data/test/fixtures/status/staged_changes_modified_file +3 -0
  80. data/test/fixtures/status/staged_delete_modified_file +1 -0
  81. data/test/fixtures/status/staged_new_file +1 -0
  82. data/test/fixtures/status/staged_new_file_modified_file +2 -0
  83. data/test/fixtures/status/subdir.txt +2 -0
  84. data/test/fixtures/status/subdir/current_file +1 -0
  85. data/test/fixtures/status/subdir/modified_file +2 -0
  86. data/test/fixtures/status/subdir/new_file +1 -0
  87. data/test/fixtures/status//350/277/231 +1 -0
  88. data/test/fixtures/testrepo.git/config +5 -0
  89. data/test/fixtures/testrepo.git/objects/77/71329dfa3002caf8c61a0ceb62a31d09023f37 +0 -0
  90. data/test/fixtures/text_file.md +464 -0
  91. data/test/fixtures/unsymlinked.git/HEAD +1 -0
  92. data/test/fixtures/unsymlinked.git/config +6 -0
  93. data/test/fixtures/unsymlinked.git/description +1 -0
  94. data/test/fixtures/unsymlinked.git/info/exclude +2 -0
  95. data/test/fixtures/unsymlinked.git/objects/08/8b64704e0d6b8bd061dea879418cb5442a3fbf +0 -0
  96. data/test/fixtures/unsymlinked.git/objects/13/a5e939bca25940c069fd2169d993dba328e30b +0 -0
  97. data/test/fixtures/unsymlinked.git/objects/19/bf568e59e3a0b363cafb4106226e62d4a4c41c +0 -0
  98. data/test/fixtures/unsymlinked.git/objects/58/1fadd35b4cf320d102a152f918729011604773 +0 -0
  99. data/test/fixtures/unsymlinked.git/objects/5c/87b6791e8b13da658a14d1ef7e09b5dc3bac8c +0 -0
  100. data/test/fixtures/unsymlinked.git/objects/6f/e5f5398af85fb3de8a6aba0339b6d3bfa26a27 +0 -0
  101. data/test/fixtures/unsymlinked.git/objects/7f/ccd75616ec188b8f1b23d67506a334cc34a49d +0 -0
  102. data/test/fixtures/unsymlinked.git/objects/80/6999882bf91d24241e4077906b9017605eb1f3 +0 -0
  103. data/test/fixtures/unsymlinked.git/objects/83/7d176303c5005505ec1e4a30231c40930c0230 +0 -0
  104. data/test/fixtures/unsymlinked.git/objects/a8/595ccca04f40818ae0155c8f9c77a230e597b6 +2 -0
  105. data/test/fixtures/unsymlinked.git/objects/cf/8f1cf5cce859c438d6cc067284cb5e161206e7 +0 -0
  106. data/test/fixtures/unsymlinked.git/objects/d5/278d05c8607ec420bfee4cf219fbc0eeebfd6a +0 -0
  107. data/test/fixtures/unsymlinked.git/objects/f4/e16fb76536591a41454194058d048d8e4dd2e9 +0 -0
  108. data/test/fixtures/unsymlinked.git/objects/f9/e65619d93fdf2673882e0a261c5e93b1a84006 +0 -0
  109. data/test/fixtures/unsymlinked.git/refs/heads/exe-file +1 -0
  110. data/test/fixtures/unsymlinked.git/refs/heads/master +1 -0
  111. data/test/fixtures/unsymlinked.git/refs/heads/reg-file +1 -0
  112. data/test/index_test.rb +120 -0
  113. data/test/reference_test.rb +38 -3
  114. data/test/remote_test.rb +224 -3
  115. data/test/repo_reset_test.rb +2 -0
  116. data/test/repo_test.rb +147 -10
  117. data/test/test_helper.rb +5 -2
  118. data/vendor/libgit2/include/git2/attr.h +3 -3
  119. data/vendor/libgit2/include/git2/blob.h +11 -17
  120. data/vendor/libgit2/include/git2/branch.h +3 -2
  121. data/vendor/libgit2/include/git2/checkout.h +7 -0
  122. data/vendor/libgit2/include/git2/clone.h +3 -0
  123. data/vendor/libgit2/include/git2/commit.h +61 -66
  124. data/vendor/libgit2/include/git2/common.h +73 -42
  125. data/vendor/libgit2/include/git2/config.h +57 -71
  126. data/vendor/libgit2/include/git2/cred_helpers.h +2 -2
  127. data/vendor/libgit2/include/git2/diff.h +179 -30
  128. data/vendor/libgit2/include/git2/errors.h +3 -3
  129. data/vendor/libgit2/include/git2/index.h +225 -146
  130. data/vendor/libgit2/include/git2/indexer.h +2 -22
  131. data/vendor/libgit2/include/git2/inttypes.h +9 -9
  132. data/vendor/libgit2/include/git2/merge.h +123 -5
  133. data/vendor/libgit2/include/git2/odb.h +59 -38
  134. data/vendor/libgit2/include/git2/odb_backend.h +45 -104
  135. data/vendor/libgit2/include/git2/oid.h +30 -19
  136. data/vendor/libgit2/include/git2/pack.h +21 -3
  137. data/vendor/libgit2/include/git2/refdb.h +0 -35
  138. data/vendor/libgit2/include/git2/refs.h +93 -31
  139. data/vendor/libgit2/include/git2/refspec.h +17 -0
  140. data/vendor/libgit2/include/git2/remote.h +60 -20
  141. data/vendor/libgit2/include/git2/repository.h +48 -70
  142. data/vendor/libgit2/include/git2/reset.h +3 -3
  143. data/vendor/libgit2/include/git2/revparse.h +22 -0
  144. data/vendor/libgit2/include/git2/stash.h +1 -1
  145. data/vendor/libgit2/include/git2/status.h +131 -56
  146. data/vendor/libgit2/include/git2/strarray.h +2 -2
  147. data/vendor/libgit2/include/git2/submodule.h +16 -16
  148. data/vendor/libgit2/include/git2/sys/commit.h +46 -0
  149. data/vendor/libgit2/include/git2/sys/config.h +71 -0
  150. data/vendor/libgit2/include/git2/sys/index.h +179 -0
  151. data/vendor/libgit2/include/git2/sys/odb_backend.h +86 -0
  152. data/vendor/libgit2/include/git2/sys/refdb_backend.h +158 -0
  153. data/vendor/libgit2/include/git2/sys/refs.h +38 -0
  154. data/vendor/libgit2/include/git2/sys/repository.h +106 -0
  155. data/vendor/libgit2/include/git2/tag.h +44 -18
  156. data/vendor/libgit2/include/git2/trace.h +1 -2
  157. data/vendor/libgit2/include/git2/transport.h +74 -0
  158. data/vendor/libgit2/include/git2/tree.h +12 -22
  159. data/vendor/libgit2/include/git2/types.h +33 -0
  160. data/vendor/libgit2/include/git2/version.h +2 -2
  161. data/vendor/libgit2/src/array.h +66 -0
  162. data/vendor/libgit2/src/attr.c +26 -13
  163. data/vendor/libgit2/src/attr_file.c +3 -2
  164. data/vendor/libgit2/src/attr_file.h +3 -3
  165. data/vendor/libgit2/src/attrcache.h +4 -4
  166. data/vendor/libgit2/src/blob.c +13 -9
  167. data/vendor/libgit2/src/blob.h +2 -2
  168. data/vendor/libgit2/src/branch.c +67 -49
  169. data/vendor/libgit2/src/cache.c +224 -54
  170. data/vendor/libgit2/src/cache.h +33 -20
  171. data/vendor/libgit2/src/checkout.c +145 -85
  172. data/vendor/libgit2/src/clone.c +62 -50
  173. data/vendor/libgit2/src/commit.c +74 -40
  174. data/vendor/libgit2/src/commit.h +2 -3
  175. data/vendor/libgit2/src/commit_list.c +14 -8
  176. data/vendor/libgit2/src/config.c +119 -36
  177. data/vendor/libgit2/src/config.h +3 -0
  178. data/vendor/libgit2/src/config_cache.c +24 -7
  179. data/vendor/libgit2/src/config_file.c +9 -6
  180. data/vendor/libgit2/src/crlf.c +4 -2
  181. data/vendor/libgit2/src/date.c +3 -3
  182. data/vendor/libgit2/src/delta.c +1 -1
  183. data/vendor/libgit2/src/diff.c +681 -303
  184. data/vendor/libgit2/src/diff.h +34 -2
  185. data/vendor/libgit2/src/diff_driver.c +405 -0
  186. data/vendor/libgit2/src/diff_driver.h +49 -0
  187. data/vendor/libgit2/src/diff_file.c +447 -0
  188. data/vendor/libgit2/src/diff_file.h +58 -0
  189. data/vendor/libgit2/src/diff_patch.c +995 -0
  190. data/vendor/libgit2/src/diff_patch.h +46 -0
  191. data/vendor/libgit2/src/diff_print.c +430 -0
  192. data/vendor/libgit2/src/diff_tform.c +464 -203
  193. data/vendor/libgit2/src/diff_xdiff.c +166 -0
  194. data/vendor/libgit2/src/diff_xdiff.h +28 -0
  195. data/vendor/libgit2/src/fetch.c +11 -4
  196. data/vendor/libgit2/src/fileops.c +85 -61
  197. data/vendor/libgit2/src/fileops.h +4 -0
  198. data/vendor/libgit2/src/global.c +10 -2
  199. data/vendor/libgit2/src/global.h +0 -8
  200. data/vendor/libgit2/src/hash/hash_generic.h +3 -3
  201. data/vendor/libgit2/src/hash/hash_win32.h +4 -4
  202. data/vendor/libgit2/src/hashsig.c +0 -1
  203. data/vendor/libgit2/src/ignore.c +68 -28
  204. data/vendor/libgit2/src/ignore.h +10 -1
  205. data/vendor/libgit2/src/index.c +666 -84
  206. data/vendor/libgit2/src/index.h +6 -0
  207. data/vendor/libgit2/src/indexer.c +10 -28
  208. data/vendor/libgit2/src/iterator.c +427 -283
  209. data/vendor/libgit2/src/iterator.h +58 -4
  210. data/vendor/libgit2/src/merge.c +1892 -32
  211. data/vendor/libgit2/src/merge.h +132 -5
  212. data/vendor/libgit2/src/merge_file.c +174 -0
  213. data/vendor/libgit2/src/merge_file.h +71 -0
  214. data/vendor/libgit2/src/mwindow.c +1 -1
  215. data/vendor/libgit2/src/notes.c +45 -48
  216. data/vendor/libgit2/src/object.c +89 -127
  217. data/vendor/libgit2/src/object.h +0 -1
  218. data/vendor/libgit2/src/object_api.c +129 -0
  219. data/vendor/libgit2/src/odb.c +156 -59
  220. data/vendor/libgit2/src/odb.h +5 -2
  221. data/vendor/libgit2/src/odb_loose.c +31 -17
  222. data/vendor/libgit2/src/odb_pack.c +39 -43
  223. data/vendor/libgit2/src/oid.c +62 -27
  224. data/vendor/libgit2/src/oid.h +33 -0
  225. data/vendor/libgit2/src/oidmap.h +4 -6
  226. data/vendor/libgit2/src/pack-objects.c +54 -22
  227. data/vendor/libgit2/src/pack.c +98 -56
  228. data/vendor/libgit2/src/pack.h +3 -1
  229. data/vendor/libgit2/src/pathspec.c +26 -1
  230. data/vendor/libgit2/src/pathspec.h +14 -0
  231. data/vendor/libgit2/src/pool.c +5 -0
  232. data/vendor/libgit2/src/posix.c +2 -2
  233. data/vendor/libgit2/src/posix.h +3 -0
  234. data/vendor/libgit2/src/push.c +13 -10
  235. data/vendor/libgit2/src/refdb.c +82 -62
  236. data/vendor/libgit2/src/refdb.h +16 -16
  237. data/vendor/libgit2/src/refdb_fs.c +386 -133
  238. data/vendor/libgit2/src/reflog.c +3 -1
  239. data/vendor/libgit2/src/refs.c +247 -221
  240. data/vendor/libgit2/src/refs.h +2 -1
  241. data/vendor/libgit2/src/refspec.c +18 -1
  242. data/vendor/libgit2/src/refspec.h +3 -1
  243. data/vendor/libgit2/src/remote.c +434 -253
  244. data/vendor/libgit2/src/remote.h +5 -3
  245. data/vendor/libgit2/src/repository.c +197 -111
  246. data/vendor/libgit2/src/repository.h +26 -5
  247. data/vendor/libgit2/src/reset.c +1 -1
  248. data/vendor/libgit2/src/revparse.c +84 -79
  249. data/vendor/libgit2/src/revwalk.c +1 -1
  250. data/vendor/libgit2/src/signature.c +22 -10
  251. data/vendor/libgit2/src/stash.c +5 -2
  252. data/vendor/libgit2/src/status.c +311 -107
  253. data/vendor/libgit2/src/status.h +23 -0
  254. data/vendor/libgit2/src/submodule.c +21 -13
  255. data/vendor/libgit2/src/tag.c +42 -31
  256. data/vendor/libgit2/src/tag.h +2 -3
  257. data/vendor/libgit2/src/thread-utils.h +105 -3
  258. data/vendor/libgit2/src/trace.c +1 -2
  259. data/vendor/libgit2/src/trace.h +3 -3
  260. data/vendor/libgit2/src/transport.c +18 -6
  261. data/vendor/libgit2/src/transports/cred.c +103 -1
  262. data/vendor/libgit2/src/transports/local.c +19 -9
  263. data/vendor/libgit2/src/transports/smart_protocol.c +32 -12
  264. data/vendor/libgit2/src/transports/ssh.c +519 -0
  265. data/vendor/libgit2/src/transports/winhttp.c +3 -1
  266. data/vendor/libgit2/src/tree.c +26 -28
  267. data/vendor/libgit2/src/tree.h +3 -3
  268. data/vendor/libgit2/src/unix/posix.h +2 -0
  269. data/vendor/libgit2/src/util.c +43 -6
  270. data/vendor/libgit2/src/util.h +40 -12
  271. data/vendor/libgit2/src/vector.c +3 -5
  272. data/vendor/libgit2/src/vector.h +9 -0
  273. data/vendor/libgit2/src/win32/dir.c +1 -1
  274. data/vendor/libgit2/src/win32/error.c +2 -0
  275. data/vendor/libgit2/src/win32/findfile.c +3 -6
  276. data/vendor/libgit2/src/win32/posix_w32.c +85 -59
  277. data/vendor/libgit2/src/win32/pthread.c +16 -8
  278. data/vendor/libgit2/src/win32/pthread.h +7 -4
  279. metadata +407 -306
  280. data/test/coverage/HEAD.json +0 -1
  281. data/vendor/libgit2/include/git2/refdb_backend.h +0 -109
  282. data/vendor/libgit2/src/diff_output.c +0 -1819
  283. data/vendor/libgit2/src/diff_output.h +0 -93
@@ -19,6 +19,7 @@ typedef enum {
19
19
  GIT_ITERATOR_TYPE_TREE = 1,
20
20
  GIT_ITERATOR_TYPE_INDEX = 2,
21
21
  GIT_ITERATOR_TYPE_WORKDIR = 3,
22
+ GIT_ITERATOR_TYPE_FS = 4,
22
23
  } git_iterator_type_t;
23
24
 
24
25
  typedef enum {
@@ -78,14 +79,35 @@ extern int git_iterator_for_index(
78
79
  const char *start,
79
80
  const char *end);
80
81
 
82
+ extern int git_iterator_for_workdir_ext(
83
+ git_iterator **out,
84
+ git_repository *repo,
85
+ const char *repo_workdir,
86
+ git_iterator_flag_t flags,
87
+ const char *start,
88
+ const char *end);
89
+
81
90
  /* workdir iterators will match the ignore_case value from the index of the
82
91
  * repository, unless you override with a non-zero flag value
83
92
  */
84
- extern int git_iterator_for_workdir(
93
+ GIT_INLINE(int) git_iterator_for_workdir(
85
94
  git_iterator **out,
86
95
  git_repository *repo,
87
96
  git_iterator_flag_t flags,
88
97
  const char *start,
98
+ const char *end)
99
+ {
100
+ return git_iterator_for_workdir_ext(out, repo, NULL, flags, start, end);
101
+ }
102
+
103
+ /* for filesystem iterators, you have to explicitly pass in the ignore_case
104
+ * behavior that you desire
105
+ */
106
+ extern int git_iterator_for_filesystem(
107
+ git_iterator **out,
108
+ const char *root,
109
+ git_iterator_flag_t flags,
110
+ const char *start,
89
111
  const char *end);
90
112
 
91
113
  extern void git_iterator_free(git_iterator *iter);
@@ -131,9 +153,9 @@ GIT_INLINE(int) git_iterator_advance(
131
153
  *
132
154
  * If the current item is not a tree, this is a no-op.
133
155
  *
134
- * For working directory iterators only, a tree (i.e. directory) can be
135
- * empty. In that case, this function returns GIT_ENOTFOUND and does not
136
- * advance. That can't happen for tree and index iterators.
156
+ * For filesystem and working directory iterators, a tree (i.e. directory)
157
+ * can be empty. In that case, this function returns GIT_ENOTFOUND and
158
+ * does not advance. That can't happen for tree and index iterators.
137
159
  */
138
160
  GIT_INLINE(int) git_iterator_advance_into(
139
161
  const git_index_entry **entry, git_iterator *iter)
@@ -141,18 +163,50 @@ GIT_INLINE(int) git_iterator_advance_into(
141
163
  return iter->cb->advance_into(entry, iter);
142
164
  }
143
165
 
166
+ /**
167
+ * Advance into a tree or skip over it if it is empty.
168
+ *
169
+ * Because `git_iterator_advance_into` may return GIT_ENOTFOUND if the
170
+ * directory is empty (only with filesystem and working directory
171
+ * iterators) and a common response is to just call `git_iterator_advance`
172
+ * when that happens, this bundles the two into a single simple call.
173
+ */
174
+ GIT_INLINE(int) git_iterator_advance_into_or_over(
175
+ const git_index_entry **entry, git_iterator *iter)
176
+ {
177
+ int error = iter->cb->advance_into(entry, iter);
178
+ if (error == GIT_ENOTFOUND) {
179
+ giterr_clear();
180
+ error = iter->cb->advance(entry, iter);
181
+ }
182
+ return error;
183
+ }
184
+
185
+ /* Seek is currently unimplemented */
144
186
  GIT_INLINE(int) git_iterator_seek(
145
187
  git_iterator *iter, const char *prefix)
146
188
  {
147
189
  return iter->cb->seek(iter, prefix);
148
190
  }
149
191
 
192
+ /**
193
+ * Go back to the start of the iteration.
194
+ *
195
+ * This resets the iterator to the start of the iteration. It also allows
196
+ * you to reset the `start` and `end` pathname boundaries of the iteration
197
+ * when doing so.
198
+ */
150
199
  GIT_INLINE(int) git_iterator_reset(
151
200
  git_iterator *iter, const char *start, const char *end)
152
201
  {
153
202
  return iter->cb->reset(iter, start, end);
154
203
  }
155
204
 
205
+ /**
206
+ * Check if the iterator is at the end
207
+ *
208
+ * @return 0 if not at end, >0 if at end
209
+ */
156
210
  GIT_INLINE(int) git_iterator_at_end(git_iterator *iter)
157
211
  {
158
212
  return iter->cb->at_end(iter);
@@ -5,48 +5,58 @@
5
5
  * a Linking Exception. For full terms see the included COPYING file.
6
6
  */
7
7
 
8
+ #include "common.h"
9
+ #include "posix.h"
10
+ #include "buffer.h"
8
11
  #include "repository.h"
9
12
  #include "revwalk.h"
10
- #include "buffer.h"
13
+ #include "commit_list.h"
11
14
  #include "merge.h"
15
+ #include "path.h"
16
+ #include "refs.h"
17
+ #include "object.h"
18
+ #include "iterator.h"
12
19
  #include "refs.h"
20
+ #include "diff.h"
21
+ #include "checkout.h"
22
+ #include "tree.h"
23
+ #include "merge_file.h"
24
+ #include "blob.h"
25
+ #include "hashsig.h"
26
+ #include "oid.h"
27
+ #include "index.h"
28
+ #include "filebuf.h"
29
+
30
+ #include "git2/types.h"
13
31
  #include "git2/repository.h"
32
+ #include "git2/object.h"
33
+ #include "git2/commit.h"
14
34
  #include "git2/merge.h"
35
+ #include "git2/refs.h"
15
36
  #include "git2/reset.h"
16
- #include "commit_list.h"
17
-
18
- int git_repository_merge_cleanup(git_repository *repo)
19
- {
20
- int error = 0;
21
- git_buf merge_head_path = GIT_BUF_INIT,
22
- merge_mode_path = GIT_BUF_INIT,
23
- merge_msg_path = GIT_BUF_INIT;
24
-
25
- assert(repo);
26
-
27
- if (git_buf_joinpath(&merge_head_path, repo->path_repository, GIT_MERGE_HEAD_FILE) < 0 ||
28
- git_buf_joinpath(&merge_mode_path, repo->path_repository, GIT_MERGE_MODE_FILE) < 0 ||
29
- git_buf_joinpath(&merge_msg_path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0)
30
- return -1;
37
+ #include "git2/checkout.h"
38
+ #include "git2/signature.h"
39
+ #include "git2/config.h"
40
+ #include "git2/tree.h"
41
+ #include "git2/sys/index.h"
31
42
 
32
- if (git_path_isfile(merge_head_path.ptr)) {
33
- if ((error = p_unlink(merge_head_path.ptr)) < 0)
34
- goto cleanup;
35
- }
43
+ #define GIT_MERGE_INDEX_ENTRY_EXISTS(X) ((X).mode != 0)
36
44
 
37
- if (git_path_isfile(merge_mode_path.ptr))
38
- (void)p_unlink(merge_mode_path.ptr);
45
+ typedef enum {
46
+ TREE_IDX_ANCESTOR = 0,
47
+ TREE_IDX_OURS = 1,
48
+ TREE_IDX_THEIRS = 2
49
+ } merge_tree_index_t;
39
50
 
40
- if (git_path_isfile(merge_msg_path.ptr))
41
- (void)p_unlink(merge_msg_path.ptr);
51
+ /* Tracks D/F conflicts */
52
+ struct merge_diff_df_data {
53
+ const char *df_path;
54
+ const char *prev_path;
55
+ git_merge_diff *prev_conflict;
56
+ };
42
57
 
43
- cleanup:
44
- git_buf_free(&merge_msg_path);
45
- git_buf_free(&merge_mode_path);
46
- git_buf_free(&merge_head_path);
47
58
 
48
- return error;
49
- }
59
+ /* Merge base computation */
50
60
 
51
61
  int git_merge_base_many(git_oid *out, git_repository *repo, const git_oid input_array[], size_t length)
52
62
  {
@@ -86,6 +96,7 @@ int git_merge_base_many(git_oid *out, git_repository *repo, const git_oid input_
86
96
  goto cleanup;
87
97
 
88
98
  if (!result) {
99
+ giterr_set(GITERR_MERGE, "No merge base found");
89
100
  error = GIT_ENOTFOUND;
90
101
  goto cleanup;
91
102
  }
@@ -131,7 +142,7 @@ int git_merge_base(git_oid *out, git_repository *repo, const git_oid *one, const
131
142
 
132
143
  if (!result) {
133
144
  git_revwalk_free(walk);
134
- giterr_clear();
145
+ giterr_set(GITERR_MERGE, "No merge base found");
135
146
  return GIT_ENOTFOUND;
136
147
  }
137
148
 
@@ -177,7 +188,7 @@ int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_l
177
188
  return -1;
178
189
 
179
190
  if (git_commit_list_parse(walk, one) < 0)
180
- return -1;
191
+ return -1;
181
192
 
182
193
  one->flags |= PARENT1;
183
194
  if (git_pqueue_insert(&list, one) < 0)
@@ -294,3 +305,1852 @@ cleanup:
294
305
 
295
306
  return error;
296
307
  }
308
+
309
+ GIT_INLINE(int) index_entry_cmp(const git_index_entry *a, const git_index_entry *b)
310
+ {
311
+ int value = 0;
312
+
313
+ if (a->path == NULL)
314
+ return (b->path == NULL) ? 0 : 1;
315
+
316
+ if ((value = a->mode - b->mode) == 0 &&
317
+ (value = git_oid__cmp(&a->oid, &b->oid)) == 0)
318
+ value = strcmp(a->path, b->path);
319
+
320
+ return value;
321
+ }
322
+
323
+ /* Conflict resolution */
324
+
325
+ static int merge_conflict_resolve_trivial(
326
+ int *resolved,
327
+ git_merge_diff_list *diff_list,
328
+ const git_merge_diff *conflict)
329
+ {
330
+ int ours_empty, theirs_empty;
331
+ int ours_changed, theirs_changed, ours_theirs_differ;
332
+ git_index_entry const *result = NULL;
333
+ int error = 0;
334
+
335
+ assert(resolved && diff_list && conflict);
336
+
337
+ *resolved = 0;
338
+
339
+ if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE ||
340
+ conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED)
341
+ return 0;
342
+
343
+ if (conflict->our_status == GIT_DELTA_RENAMED ||
344
+ conflict->their_status == GIT_DELTA_RENAMED)
345
+ return 0;
346
+
347
+ ours_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry);
348
+ theirs_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry);
349
+
350
+ ours_changed = (conflict->our_status != GIT_DELTA_UNMODIFIED);
351
+ theirs_changed = (conflict->their_status != GIT_DELTA_UNMODIFIED);
352
+ ours_theirs_differ = ours_changed && theirs_changed &&
353
+ index_entry_cmp(&conflict->our_entry, &conflict->their_entry);
354
+
355
+ /*
356
+ * Note: with only one ancestor, some cases are not distinct:
357
+ *
358
+ * 16: ancest:anc1/anc2, head:anc1, remote:anc2 = result:no merge
359
+ * 3: ancest:(empty)^, head:head, remote:(empty) = result:no merge
360
+ * 2: ancest:(empty)^, head:(empty), remote:remote = result:no merge
361
+ *
362
+ * Note that the two cases that take D/F conflicts into account
363
+ * specifically do not need to be explicitly tested, as D/F conflicts
364
+ * would fail the *empty* test:
365
+ *
366
+ * 3ALT: ancest:(empty)+, head:head, remote:*empty* = result:head
367
+ * 2ALT: ancest:(empty)+, head:*empty*, remote:remote = result:remote
368
+ *
369
+ * Note that many of these cases need not be explicitly tested, as
370
+ * they simply degrade to "all different" cases (eg, 11):
371
+ *
372
+ * 4: ancest:(empty)^, head:head, remote:remote = result:no merge
373
+ * 7: ancest:ancest+, head:(empty), remote:remote = result:no merge
374
+ * 9: ancest:ancest+, head:head, remote:(empty) = result:no merge
375
+ * 11: ancest:ancest+, head:head, remote:remote = result:no merge
376
+ */
377
+
378
+ /* 5ALT: ancest:*, head:head, remote:head = result:head */
379
+ if (ours_changed && !ours_empty && !ours_theirs_differ)
380
+ result = &conflict->our_entry;
381
+ /* 6: ancest:ancest+, head:(empty), remote:(empty) = result:no merge */
382
+ else if (ours_changed && ours_empty && theirs_empty)
383
+ *resolved = 0;
384
+ /* 8: ancest:ancest^, head:(empty), remote:ancest = result:no merge */
385
+ else if (ours_empty && !theirs_changed)
386
+ *resolved = 0;
387
+ /* 10: ancest:ancest^, head:ancest, remote:(empty) = result:no merge */
388
+ else if (!ours_changed && theirs_empty)
389
+ *resolved = 0;
390
+ /* 13: ancest:ancest+, head:head, remote:ancest = result:head */
391
+ else if (ours_changed && !theirs_changed)
392
+ result = &conflict->our_entry;
393
+ /* 14: ancest:ancest+, head:ancest, remote:remote = result:remote */
394
+ else if (!ours_changed && theirs_changed)
395
+ result = &conflict->their_entry;
396
+ else
397
+ *resolved = 0;
398
+
399
+ if (result != NULL &&
400
+ GIT_MERGE_INDEX_ENTRY_EXISTS(*result) &&
401
+ (error = git_vector_insert(&diff_list->staged, (void *)result)) >= 0)
402
+ *resolved = 1;
403
+
404
+ /* Note: trivial resolution does not update the REUC. */
405
+
406
+ return error;
407
+ }
408
+
409
+ static int merge_conflict_resolve_one_removed(
410
+ int *resolved,
411
+ git_merge_diff_list *diff_list,
412
+ const git_merge_diff *conflict)
413
+ {
414
+ int ours_empty, theirs_empty;
415
+ int ours_changed, theirs_changed;
416
+ int error = 0;
417
+
418
+ assert(resolved && diff_list && conflict);
419
+
420
+ *resolved = 0;
421
+
422
+ if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE ||
423
+ conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED)
424
+ return 0;
425
+
426
+ ours_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry);
427
+ theirs_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry);
428
+
429
+ ours_changed = (conflict->our_status != GIT_DELTA_UNMODIFIED);
430
+ theirs_changed = (conflict->their_status != GIT_DELTA_UNMODIFIED);
431
+
432
+ /* Removed in both */
433
+ if (ours_changed && ours_empty && theirs_empty)
434
+ *resolved = 1;
435
+ /* Removed in ours */
436
+ else if (ours_empty && !theirs_changed)
437
+ *resolved = 1;
438
+ /* Removed in theirs */
439
+ else if (!ours_changed && theirs_empty)
440
+ *resolved = 1;
441
+
442
+ if (*resolved)
443
+ git_vector_insert(&diff_list->resolved, (git_merge_diff *)conflict);
444
+
445
+ return error;
446
+ }
447
+
448
+
449
+ static int merge_conflict_resolve_one_renamed(
450
+ int *resolved,
451
+ git_merge_diff_list *diff_list,
452
+ const git_merge_diff *conflict)
453
+ {
454
+ int ours_renamed, theirs_renamed;
455
+ int ours_changed, theirs_changed;
456
+ git_index_entry *merged;
457
+ int error = 0;
458
+
459
+ assert(resolved && diff_list && conflict);
460
+
461
+ *resolved = 0;
462
+
463
+ if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ||
464
+ !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry))
465
+ return 0;
466
+
467
+ ours_renamed = (conflict->our_status == GIT_DELTA_RENAMED);
468
+ theirs_renamed = (conflict->their_status == GIT_DELTA_RENAMED);
469
+
470
+ if (!ours_renamed && !theirs_renamed)
471
+ return 0;
472
+
473
+ /* Reject one file in a 2->1 conflict */
474
+ if (conflict->type == GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1 ||
475
+ conflict->type == GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2 ||
476
+ conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED)
477
+ return 0;
478
+
479
+ ours_changed = (git_oid__cmp(&conflict->ancestor_entry.oid, &conflict->our_entry.oid) != 0);
480
+ theirs_changed = (git_oid__cmp(&conflict->ancestor_entry.oid, &conflict->their_entry.oid) != 0);
481
+
482
+ /* if both are modified (and not to a common target) require a merge */
483
+ if (ours_changed && theirs_changed &&
484
+ git_oid__cmp(&conflict->our_entry.oid, &conflict->their_entry.oid) != 0)
485
+ return 0;
486
+
487
+ if ((merged = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry))) == NULL)
488
+ return -1;
489
+
490
+ if (ours_changed)
491
+ memcpy(merged, &conflict->our_entry, sizeof(git_index_entry));
492
+ else
493
+ memcpy(merged, &conflict->their_entry, sizeof(git_index_entry));
494
+
495
+ if (ours_renamed)
496
+ merged->path = conflict->our_entry.path;
497
+ else
498
+ merged->path = conflict->their_entry.path;
499
+
500
+ *resolved = 1;
501
+
502
+ git_vector_insert(&diff_list->staged, merged);
503
+ git_vector_insert(&diff_list->resolved, (git_merge_diff *)conflict);
504
+
505
+ return error;
506
+ }
507
+
508
+ static int merge_conflict_resolve_automerge(
509
+ int *resolved,
510
+ git_merge_diff_list *diff_list,
511
+ const git_merge_diff *conflict,
512
+ unsigned int automerge_flags)
513
+ {
514
+ git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT,
515
+ ours = GIT_MERGE_FILE_INPUT_INIT,
516
+ theirs = GIT_MERGE_FILE_INPUT_INIT;
517
+ git_merge_file_result result = GIT_MERGE_FILE_RESULT_INIT;
518
+ git_index_entry *index_entry;
519
+ git_odb *odb = NULL;
520
+ git_oid automerge_oid;
521
+ int error = 0;
522
+
523
+ assert(resolved && diff_list && conflict);
524
+
525
+ *resolved = 0;
526
+
527
+ if (automerge_flags == GIT_MERGE_AUTOMERGE_NONE)
528
+ return 0;
529
+
530
+ /* Reject D/F conflicts */
531
+ if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE)
532
+ return 0;
533
+
534
+ /* Reject link/file conflicts. */
535
+ if ((S_ISLNK(conflict->ancestor_entry.mode) ^ S_ISLNK(conflict->our_entry.mode)) ||
536
+ (S_ISLNK(conflict->ancestor_entry.mode) ^ S_ISLNK(conflict->their_entry.mode)))
537
+ return 0;
538
+
539
+ /* Reject name conflicts */
540
+ if (conflict->type == GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1 ||
541
+ conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED)
542
+ return 0;
543
+
544
+ if ((conflict->our_status & GIT_DELTA_RENAMED) == GIT_DELTA_RENAMED &&
545
+ (conflict->their_status & GIT_DELTA_RENAMED) == GIT_DELTA_RENAMED &&
546
+ strcmp(conflict->ancestor_entry.path, conflict->their_entry.path) != 0)
547
+ return 0;
548
+
549
+ if ((error = git_repository_odb(&odb, diff_list->repo)) < 0 ||
550
+ (error = git_merge_file_input_from_index_entry(&ancestor, diff_list->repo, &conflict->ancestor_entry)) < 0 ||
551
+ (error = git_merge_file_input_from_index_entry(&ours, diff_list->repo, &conflict->our_entry)) < 0 ||
552
+ (error = git_merge_file_input_from_index_entry(&theirs, diff_list->repo, &conflict->their_entry)) < 0 ||
553
+ (error = git_merge_files(&result, &ancestor, &ours, &theirs, automerge_flags)) < 0 ||
554
+ !result.automergeable ||
555
+ (error = git_odb_write(&automerge_oid, odb, result.data, result.len, GIT_OBJ_BLOB)) < 0)
556
+ goto done;
557
+
558
+ if ((index_entry = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry))) == NULL)
559
+ GITERR_CHECK_ALLOC(index_entry);
560
+
561
+ index_entry->path = git_pool_strdup(&diff_list->pool, result.path);
562
+ GITERR_CHECK_ALLOC(index_entry->path);
563
+
564
+ index_entry->file_size = result.len;
565
+ index_entry->mode = result.mode;
566
+ git_oid_cpy(&index_entry->oid, &automerge_oid);
567
+
568
+ git_vector_insert(&diff_list->staged, index_entry);
569
+ git_vector_insert(&diff_list->resolved, (git_merge_diff *)conflict);
570
+
571
+ *resolved = 1;
572
+
573
+ done:
574
+ git_merge_file_input_free(&ancestor);
575
+ git_merge_file_input_free(&ours);
576
+ git_merge_file_input_free(&theirs);
577
+ git_merge_file_result_free(&result);
578
+ git_odb_free(odb);
579
+
580
+ return error;
581
+ }
582
+
583
+ static int merge_conflict_resolve(
584
+ int *out,
585
+ git_merge_diff_list *diff_list,
586
+ const git_merge_diff *conflict,
587
+ unsigned int automerge_flags)
588
+ {
589
+ int resolved = 0;
590
+ int error = 0;
591
+
592
+ *out = 0;
593
+
594
+ if ((error = merge_conflict_resolve_trivial(&resolved, diff_list, conflict)) < 0)
595
+ goto done;
596
+
597
+ if (automerge_flags != GIT_MERGE_AUTOMERGE_NONE) {
598
+ if (!resolved && (error = merge_conflict_resolve_one_removed(&resolved, diff_list, conflict)) < 0)
599
+ goto done;
600
+
601
+ if (!resolved && (error = merge_conflict_resolve_one_renamed(&resolved, diff_list, conflict)) < 0)
602
+ goto done;
603
+
604
+ if (!resolved && (error = merge_conflict_resolve_automerge(&resolved, diff_list, conflict, automerge_flags)) < 0)
605
+ goto done;
606
+ }
607
+
608
+ *out = resolved;
609
+
610
+ done:
611
+ return error;
612
+ }
613
+
614
+ /* Rename detection and coalescing */
615
+
616
+ struct merge_diff_similarity {
617
+ unsigned char similarity;
618
+ size_t other_idx;
619
+ };
620
+
621
+ static int index_entry_similarity_exact(
622
+ git_repository *repo,
623
+ git_index_entry *a,
624
+ size_t a_idx,
625
+ git_index_entry *b,
626
+ size_t b_idx,
627
+ void **cache,
628
+ const git_merge_tree_opts *opts)
629
+ {
630
+ GIT_UNUSED(repo);
631
+ GIT_UNUSED(a_idx);
632
+ GIT_UNUSED(b_idx);
633
+ GIT_UNUSED(cache);
634
+ GIT_UNUSED(opts);
635
+
636
+ if (git_oid__cmp(&a->oid, &b->oid) == 0)
637
+ return 100;
638
+
639
+ return 0;
640
+ }
641
+
642
+ static int index_entry_similarity_calc(
643
+ void **out,
644
+ git_repository *repo,
645
+ git_index_entry *entry,
646
+ const git_merge_tree_opts *opts)
647
+ {
648
+ git_blob *blob;
649
+ git_diff_file diff_file = {{{0}}};
650
+ git_off_t blobsize;
651
+ int error;
652
+
653
+ *out = NULL;
654
+
655
+ if ((error = git_blob_lookup(&blob, repo, &entry->oid)) < 0)
656
+ return error;
657
+
658
+ git_oid_cpy(&diff_file.oid, &entry->oid);
659
+ diff_file.path = entry->path;
660
+ diff_file.size = entry->file_size;
661
+ diff_file.mode = entry->mode;
662
+ diff_file.flags = 0;
663
+
664
+ blobsize = git_blob_rawsize(blob);
665
+
666
+ /* file too big for rename processing */
667
+ if (!git__is_sizet(blobsize))
668
+ return 0;
669
+
670
+ error = opts->metric->buffer_signature(out, &diff_file,
671
+ git_blob_rawcontent(blob), (size_t)blobsize,
672
+ opts->metric->payload);
673
+
674
+ git_blob_free(blob);
675
+
676
+ return error;
677
+ }
678
+
679
+ static int index_entry_similarity_inexact(
680
+ git_repository *repo,
681
+ git_index_entry *a,
682
+ size_t a_idx,
683
+ git_index_entry *b,
684
+ size_t b_idx,
685
+ void **cache,
686
+ const git_merge_tree_opts *opts)
687
+ {
688
+ int score = 0;
689
+ int error = 0;
690
+
691
+ if (GIT_MODE_TYPE(a->mode) != GIT_MODE_TYPE(b->mode))
692
+ return 0;
693
+
694
+ /* update signature cache if needed */
695
+ if (!cache[a_idx] && (error = index_entry_similarity_calc(&cache[a_idx], repo, a, opts)) < 0)
696
+ return error;
697
+ if (!cache[b_idx] && (error = index_entry_similarity_calc(&cache[b_idx], repo, b, opts)) < 0)
698
+ return error;
699
+
700
+ /* some metrics may not wish to process this file (too big / too small) */
701
+ if (!cache[a_idx] || !cache[b_idx])
702
+ return 0;
703
+
704
+ /* compare signatures */
705
+ if (opts->metric->similarity(
706
+ &score, cache[a_idx], cache[b_idx], opts->metric->payload) < 0)
707
+ return -1;
708
+
709
+ /* clip score */
710
+ if (score < 0)
711
+ score = 0;
712
+ else if (score > 100)
713
+ score = 100;
714
+
715
+ return score;
716
+ }
717
+
718
+ static int merge_diff_mark_similarity(
719
+ git_repository *repo,
720
+ git_merge_diff_list *diff_list,
721
+ struct merge_diff_similarity *similarity_ours,
722
+ struct merge_diff_similarity *similarity_theirs,
723
+ int (*similarity_fn)(git_repository *, git_index_entry *, size_t, git_index_entry *, size_t, void **, const git_merge_tree_opts *),
724
+ void **cache,
725
+ const git_merge_tree_opts *opts)
726
+ {
727
+ size_t i, j;
728
+ git_merge_diff *conflict_src, *conflict_tgt;
729
+ int similarity;
730
+
731
+ git_vector_foreach(&diff_list->conflicts, i, conflict_src) {
732
+ /* Items can be the source of a rename iff they have an item in the
733
+ * ancestor slot and lack an item in the ours or theirs slot. */
734
+ if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->ancestor_entry) ||
735
+ (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->our_entry) &&
736
+ GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->their_entry)))
737
+ continue;
738
+
739
+ git_vector_foreach(&diff_list->conflicts, j, conflict_tgt) {
740
+ size_t our_idx = diff_list->conflicts.length + j;
741
+ size_t their_idx = (diff_list->conflicts.length * 2) + j;
742
+
743
+ if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->ancestor_entry))
744
+ continue;
745
+
746
+ if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->our_entry) &&
747
+ !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->our_entry)) {
748
+ similarity = similarity_fn(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->our_entry, our_idx, cache, opts);
749
+
750
+ if (similarity == GIT_EBUFS)
751
+ continue;
752
+ else if (similarity < 0)
753
+ return similarity;
754
+
755
+ if (similarity > similarity_ours[i].similarity &&
756
+ similarity > similarity_ours[j].similarity) {
757
+ /* Clear previous best similarity */
758
+ if (similarity_ours[i].similarity > 0)
759
+ similarity_ours[similarity_ours[i].other_idx].similarity = 0;
760
+
761
+ if (similarity_ours[j].similarity > 0)
762
+ similarity_ours[similarity_ours[j].other_idx].similarity = 0;
763
+
764
+ similarity_ours[i].similarity = similarity;
765
+ similarity_ours[i].other_idx = j;
766
+
767
+ similarity_ours[j].similarity = similarity;
768
+ similarity_ours[j].other_idx = i;
769
+ }
770
+ }
771
+
772
+ if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->their_entry) &&
773
+ !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->their_entry)) {
774
+ similarity = similarity_fn(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->their_entry, their_idx, cache, opts);
775
+
776
+ if (similarity > similarity_theirs[i].similarity &&
777
+ similarity > similarity_theirs[j].similarity) {
778
+ /* Clear previous best similarity */
779
+ if (similarity_theirs[i].similarity > 0)
780
+ similarity_theirs[similarity_theirs[i].other_idx].similarity = 0;
781
+
782
+ if (similarity_theirs[j].similarity > 0)
783
+ similarity_theirs[similarity_theirs[j].other_idx].similarity = 0;
784
+
785
+ similarity_theirs[i].similarity = similarity;
786
+ similarity_theirs[i].other_idx = j;
787
+
788
+ similarity_theirs[j].similarity = similarity;
789
+ similarity_theirs[j].other_idx = i;
790
+ }
791
+ }
792
+ }
793
+ }
794
+
795
+ return 0;
796
+ }
797
+
798
+ /*
799
+ * Rename conflicts:
800
+ *
801
+ * Ancestor Ours Theirs
802
+ *
803
+ * 0a A A A No rename
804
+ * b A A* A No rename (ours was rewritten)
805
+ * c A A A* No rename (theirs rewritten)
806
+ * 1a A A B[A] Rename or rename/edit
807
+ * b A B[A] A (automergeable)
808
+ * 2 A B[A] B[A] Both renamed (automergeable)
809
+ * 3a A B[A] Rename/delete
810
+ * b A B[A] (same)
811
+ * 4a A B[A] B Rename/add [B~ours B~theirs]
812
+ * b A B B[A] (same)
813
+ * 5 A B[A] C[A] Both renamed ("1 -> 2")
814
+ * 6 A C[A] Both renamed ("2 -> 1")
815
+ * B C[B] [C~ours C~theirs] (automergeable)
816
+ */
817
+ static void merge_diff_mark_rename_conflict(
818
+ git_merge_diff_list *diff_list,
819
+ struct merge_diff_similarity *similarity_ours,
820
+ bool ours_renamed,
821
+ size_t ours_source_idx,
822
+ struct merge_diff_similarity *similarity_theirs,
823
+ bool theirs_renamed,
824
+ size_t theirs_source_idx,
825
+ git_merge_diff *target,
826
+ const git_merge_tree_opts *opts)
827
+ {
828
+ git_merge_diff *ours_source = NULL, *theirs_source = NULL;
829
+
830
+ if (ours_renamed)
831
+ ours_source = diff_list->conflicts.contents[ours_source_idx];
832
+
833
+ if (theirs_renamed)
834
+ theirs_source = diff_list->conflicts.contents[theirs_source_idx];
835
+
836
+ /* Detect 2->1 conflicts */
837
+ if (ours_renamed && theirs_renamed) {
838
+ /* Both renamed to the same target name. */
839
+ if (ours_source_idx == theirs_source_idx)
840
+ ours_source->type = GIT_MERGE_DIFF_BOTH_RENAMED;
841
+ else {
842
+ ours_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1;
843
+ theirs_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1;
844
+ }
845
+ } else if (ours_renamed) {
846
+ /* If our source was also renamed in theirs, this is a 1->2 */
847
+ if (similarity_theirs[ours_source_idx].similarity >= opts->rename_threshold)
848
+ ours_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2;
849
+
850
+ else if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->their_entry)) {
851
+ ours_source->type = GIT_MERGE_DIFF_RENAMED_ADDED;
852
+ target->type = GIT_MERGE_DIFF_RENAMED_ADDED;
853
+ }
854
+
855
+ else if (!GIT_MERGE_INDEX_ENTRY_EXISTS(ours_source->their_entry))
856
+ ours_source->type = GIT_MERGE_DIFF_RENAMED_DELETED;
857
+
858
+ else if (ours_source->type == GIT_MERGE_DIFF_MODIFIED_DELETED)
859
+ ours_source->type = GIT_MERGE_DIFF_RENAMED_MODIFIED;
860
+ } else if (theirs_renamed) {
861
+ /* If their source was also renamed in ours, this is a 1->2 */
862
+ if (similarity_ours[theirs_source_idx].similarity >= opts->rename_threshold)
863
+ theirs_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2;
864
+
865
+ else if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->our_entry)) {
866
+ theirs_source->type = GIT_MERGE_DIFF_RENAMED_ADDED;
867
+ target->type = GIT_MERGE_DIFF_RENAMED_ADDED;
868
+ }
869
+
870
+ else if (!GIT_MERGE_INDEX_ENTRY_EXISTS(theirs_source->our_entry))
871
+ theirs_source->type = GIT_MERGE_DIFF_RENAMED_DELETED;
872
+
873
+ else if (theirs_source->type == GIT_MERGE_DIFF_MODIFIED_DELETED)
874
+ theirs_source->type = GIT_MERGE_DIFF_RENAMED_MODIFIED;
875
+ }
876
+ }
877
+
878
+ GIT_INLINE(void) merge_diff_coalesce_rename(
879
+ git_index_entry *source_entry,
880
+ git_delta_t *source_status,
881
+ git_index_entry *target_entry,
882
+ git_delta_t *target_status)
883
+ {
884
+ /* Coalesce the rename target into the rename source. */
885
+ memcpy(source_entry, target_entry, sizeof(git_index_entry));
886
+ *source_status = GIT_DELTA_RENAMED;
887
+
888
+ memset(target_entry, 0x0, sizeof(git_index_entry));
889
+ *target_status = GIT_DELTA_UNMODIFIED;
890
+ }
891
+
892
+ static void merge_diff_list_coalesce_renames(
893
+ git_merge_diff_list *diff_list,
894
+ struct merge_diff_similarity *similarity_ours,
895
+ struct merge_diff_similarity *similarity_theirs,
896
+ const git_merge_tree_opts *opts)
897
+ {
898
+ size_t i;
899
+ bool ours_renamed = 0, theirs_renamed = 0;
900
+ size_t ours_source_idx = 0, theirs_source_idx = 0;
901
+ git_merge_diff *ours_source, *theirs_source, *target;
902
+
903
+ for (i = 0; i < diff_list->conflicts.length; i++) {
904
+ target = diff_list->conflicts.contents[i];
905
+
906
+ ours_renamed = 0;
907
+ theirs_renamed = 0;
908
+
909
+ if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->our_entry) &&
910
+ similarity_ours[i].similarity >= opts->rename_threshold) {
911
+ ours_source_idx = similarity_ours[i].other_idx;
912
+
913
+ ours_source = diff_list->conflicts.contents[ours_source_idx];
914
+
915
+ merge_diff_coalesce_rename(
916
+ &ours_source->our_entry,
917
+ &ours_source->our_status,
918
+ &target->our_entry,
919
+ &target->our_status);
920
+
921
+ similarity_ours[ours_source_idx].similarity = 0;
922
+ similarity_ours[i].similarity = 0;
923
+
924
+ ours_renamed = 1;
925
+ }
926
+
927
+ /* insufficient to determine direction */
928
+ if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->their_entry) &&
929
+ similarity_theirs[i].similarity >= opts->rename_threshold) {
930
+ theirs_source_idx = similarity_theirs[i].other_idx;
931
+
932
+ theirs_source = diff_list->conflicts.contents[theirs_source_idx];
933
+
934
+ merge_diff_coalesce_rename(
935
+ &theirs_source->their_entry,
936
+ &theirs_source->their_status,
937
+ &target->their_entry,
938
+ &target->their_status);
939
+
940
+ similarity_theirs[theirs_source_idx].similarity = 0;
941
+ similarity_theirs[i].similarity = 0;
942
+
943
+ theirs_renamed = 1;
944
+ }
945
+
946
+ merge_diff_mark_rename_conflict(diff_list,
947
+ similarity_ours, ours_renamed, ours_source_idx,
948
+ similarity_theirs, theirs_renamed, theirs_source_idx,
949
+ target, opts);
950
+ }
951
+ }
952
+
953
+ static int merge_diff_empty(const git_vector *conflicts, size_t idx)
954
+ {
955
+ git_merge_diff *conflict = conflicts->contents[idx];
956
+
957
+ return (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) &&
958
+ !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) &&
959
+ !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry));
960
+ }
961
+
962
+ static void merge_diff_list_count_candidates(
963
+ git_merge_diff_list *diff_list,
964
+ size_t *src_count,
965
+ size_t *tgt_count)
966
+ {
967
+ git_merge_diff *entry;
968
+ size_t i;
969
+
970
+ *src_count = 0;
971
+ *tgt_count = 0;
972
+
973
+ git_vector_foreach(&diff_list->conflicts, i, entry) {
974
+ if (GIT_MERGE_INDEX_ENTRY_EXISTS(entry->ancestor_entry) &&
975
+ (!GIT_MERGE_INDEX_ENTRY_EXISTS(entry->our_entry) ||
976
+ !GIT_MERGE_INDEX_ENTRY_EXISTS(entry->their_entry)))
977
+ src_count++;
978
+ else if (!GIT_MERGE_INDEX_ENTRY_EXISTS(entry->ancestor_entry))
979
+ tgt_count++;
980
+ }
981
+ }
982
+
983
+ int git_merge_diff_list__find_renames(
984
+ git_repository *repo,
985
+ git_merge_diff_list *diff_list,
986
+ const git_merge_tree_opts *opts)
987
+ {
988
+ struct merge_diff_similarity *similarity_ours, *similarity_theirs;
989
+ void **cache = NULL;
990
+ size_t cache_size = 0;
991
+ size_t src_count, tgt_count, i;
992
+ int error = 0;
993
+
994
+ assert(diff_list && opts);
995
+
996
+ if ((opts->flags & GIT_MERGE_TREE_FIND_RENAMES) == 0)
997
+ return 0;
998
+
999
+ similarity_ours = git__calloc(diff_list->conflicts.length,
1000
+ sizeof(struct merge_diff_similarity));
1001
+ GITERR_CHECK_ALLOC(similarity_ours);
1002
+
1003
+ similarity_theirs = git__calloc(diff_list->conflicts.length,
1004
+ sizeof(struct merge_diff_similarity));
1005
+ GITERR_CHECK_ALLOC(similarity_theirs);
1006
+
1007
+ /* Calculate similarity between items that were deleted from the ancestor
1008
+ * and added in the other branch.
1009
+ */
1010
+ if ((error = merge_diff_mark_similarity(repo, diff_list, similarity_ours,
1011
+ similarity_theirs, index_entry_similarity_exact, NULL, opts)) < 0)
1012
+ goto done;
1013
+
1014
+ if (diff_list->conflicts.length <= opts->target_limit) {
1015
+ cache_size = diff_list->conflicts.length * 3;
1016
+ cache = git__calloc(cache_size, sizeof(void *));
1017
+ GITERR_CHECK_ALLOC(cache);
1018
+
1019
+ merge_diff_list_count_candidates(diff_list, &src_count, &tgt_count);
1020
+
1021
+ if (src_count > opts->target_limit || tgt_count > opts->target_limit) {
1022
+ /* TODO: report! */
1023
+ } else {
1024
+ if ((error = merge_diff_mark_similarity(
1025
+ repo, diff_list, similarity_ours, similarity_theirs,
1026
+ index_entry_similarity_inexact, cache, opts)) < 0)
1027
+ goto done;
1028
+ }
1029
+ }
1030
+
1031
+ /* For entries that are appropriately similar, merge the new name's entry
1032
+ * into the old name.
1033
+ */
1034
+ merge_diff_list_coalesce_renames(diff_list, similarity_ours, similarity_theirs, opts);
1035
+
1036
+ /* And remove any entries that were merged and are now empty. */
1037
+ git_vector_remove_matching(&diff_list->conflicts, merge_diff_empty);
1038
+
1039
+ done:
1040
+ if (cache != NULL) {
1041
+ for (i = 0; i < cache_size; ++i) {
1042
+ if (cache[i] != NULL)
1043
+ opts->metric->free_signature(cache[i], opts->metric->payload);
1044
+ }
1045
+
1046
+ git__free(cache);
1047
+ }
1048
+
1049
+ git__free(similarity_ours);
1050
+ git__free(similarity_theirs);
1051
+
1052
+ return error;
1053
+ }
1054
+
1055
+ /* Directory/file conflict handling */
1056
+
1057
+ GIT_INLINE(const char *) merge_diff_path(
1058
+ const git_merge_diff *conflict)
1059
+ {
1060
+ if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry))
1061
+ return conflict->ancestor_entry.path;
1062
+ else if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry))
1063
+ return conflict->our_entry.path;
1064
+ else if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry))
1065
+ return conflict->their_entry.path;
1066
+
1067
+ return NULL;
1068
+ }
1069
+
1070
+ GIT_INLINE(bool) merge_diff_any_side_added_or_modified(
1071
+ const git_merge_diff *conflict)
1072
+ {
1073
+ if (conflict->our_status == GIT_DELTA_ADDED ||
1074
+ conflict->our_status == GIT_DELTA_MODIFIED ||
1075
+ conflict->their_status == GIT_DELTA_ADDED ||
1076
+ conflict->their_status == GIT_DELTA_MODIFIED)
1077
+ return true;
1078
+
1079
+ return false;
1080
+ }
1081
+
1082
+ GIT_INLINE(bool) path_is_prefixed(const char *parent, const char *child)
1083
+ {
1084
+ size_t child_len = strlen(child);
1085
+ size_t parent_len = strlen(parent);
1086
+
1087
+ if (child_len < parent_len ||
1088
+ strncmp(parent, child, parent_len) != 0)
1089
+ return 0;
1090
+
1091
+ return (child[parent_len] == '/');
1092
+ }
1093
+
1094
+ GIT_INLINE(int) merge_diff_detect_df_conflict(
1095
+ struct merge_diff_df_data *df_data,
1096
+ git_merge_diff *conflict)
1097
+ {
1098
+ const char *cur_path = merge_diff_path(conflict);
1099
+
1100
+ /* Determine if this is a D/F conflict or the child of one */
1101
+ if (df_data->df_path &&
1102
+ path_is_prefixed(df_data->df_path, cur_path))
1103
+ conflict->type = GIT_MERGE_DIFF_DF_CHILD;
1104
+ else if(df_data->df_path)
1105
+ df_data->df_path = NULL;
1106
+ else if (df_data->prev_path &&
1107
+ merge_diff_any_side_added_or_modified(df_data->prev_conflict) &&
1108
+ merge_diff_any_side_added_or_modified(conflict) &&
1109
+ path_is_prefixed(df_data->prev_path, cur_path)) {
1110
+ conflict->type = GIT_MERGE_DIFF_DF_CHILD;
1111
+
1112
+ df_data->prev_conflict->type = GIT_MERGE_DIFF_DIRECTORY_FILE;
1113
+ df_data->df_path = df_data->prev_path;
1114
+ }
1115
+
1116
+ df_data->prev_path = cur_path;
1117
+ df_data->prev_conflict = conflict;
1118
+
1119
+ return 0;
1120
+ }
1121
+
1122
+ /* Conflict handling */
1123
+
1124
+ GIT_INLINE(int) merge_diff_detect_type(
1125
+ git_merge_diff *conflict)
1126
+ {
1127
+ if (conflict->our_status == GIT_DELTA_ADDED &&
1128
+ conflict->their_status == GIT_DELTA_ADDED)
1129
+ conflict->type = GIT_MERGE_DIFF_BOTH_ADDED;
1130
+ else if (conflict->our_status == GIT_DELTA_MODIFIED &&
1131
+ conflict->their_status == GIT_DELTA_MODIFIED)
1132
+ conflict->type = GIT_MERGE_DIFF_BOTH_MODIFIED;
1133
+ else if (conflict->our_status == GIT_DELTA_DELETED &&
1134
+ conflict->their_status == GIT_DELTA_DELETED)
1135
+ conflict->type = GIT_MERGE_DIFF_BOTH_DELETED;
1136
+ else if (conflict->our_status == GIT_DELTA_MODIFIED &&
1137
+ conflict->their_status == GIT_DELTA_DELETED)
1138
+ conflict->type = GIT_MERGE_DIFF_MODIFIED_DELETED;
1139
+ else if (conflict->our_status == GIT_DELTA_DELETED &&
1140
+ conflict->their_status == GIT_DELTA_MODIFIED)
1141
+ conflict->type = GIT_MERGE_DIFF_MODIFIED_DELETED;
1142
+ else
1143
+ conflict->type = GIT_MERGE_DIFF_NONE;
1144
+
1145
+ return 0;
1146
+ }
1147
+
1148
+ GIT_INLINE(int) index_entry_dup(
1149
+ git_index_entry *out,
1150
+ git_pool *pool,
1151
+ const git_index_entry *src)
1152
+ {
1153
+ if (src != NULL) {
1154
+ memcpy(out, src, sizeof(git_index_entry));
1155
+
1156
+ if ((out->path = git_pool_strdup(pool, src->path)) == NULL)
1157
+ return -1;
1158
+ }
1159
+
1160
+ return 0;
1161
+ }
1162
+
1163
+ GIT_INLINE(int) merge_delta_type_from_index_entries(
1164
+ const git_index_entry *ancestor,
1165
+ const git_index_entry *other)
1166
+ {
1167
+ if (ancestor == NULL && other == NULL)
1168
+ return GIT_DELTA_UNMODIFIED;
1169
+ else if (ancestor == NULL && other != NULL)
1170
+ return GIT_DELTA_ADDED;
1171
+ else if (ancestor != NULL && other == NULL)
1172
+ return GIT_DELTA_DELETED;
1173
+ else if (S_ISDIR(ancestor->mode) ^ S_ISDIR(other->mode))
1174
+ return GIT_DELTA_TYPECHANGE;
1175
+ else if(S_ISLNK(ancestor->mode) ^ S_ISLNK(other->mode))
1176
+ return GIT_DELTA_TYPECHANGE;
1177
+ else if (git_oid__cmp(&ancestor->oid, &other->oid) ||
1178
+ ancestor->mode != other->mode)
1179
+ return GIT_DELTA_MODIFIED;
1180
+
1181
+ return GIT_DELTA_UNMODIFIED;
1182
+ }
1183
+
1184
+ static git_merge_diff *merge_diff_from_index_entries(
1185
+ git_merge_diff_list *diff_list,
1186
+ const git_index_entry **entries)
1187
+ {
1188
+ git_merge_diff *conflict;
1189
+ git_pool *pool = &diff_list->pool;
1190
+
1191
+ if ((conflict = git_pool_malloc(pool, sizeof(git_merge_diff))) == NULL)
1192
+ return NULL;
1193
+
1194
+ if (index_entry_dup(&conflict->ancestor_entry, pool, entries[TREE_IDX_ANCESTOR]) < 0 ||
1195
+ index_entry_dup(&conflict->our_entry, pool, entries[TREE_IDX_OURS]) < 0 ||
1196
+ index_entry_dup(&conflict->their_entry, pool, entries[TREE_IDX_THEIRS]) < 0)
1197
+ return NULL;
1198
+
1199
+ conflict->our_status = merge_delta_type_from_index_entries(
1200
+ entries[TREE_IDX_ANCESTOR], entries[TREE_IDX_OURS]);
1201
+ conflict->their_status = merge_delta_type_from_index_entries(
1202
+ entries[TREE_IDX_ANCESTOR], entries[TREE_IDX_THEIRS]);
1203
+
1204
+ return conflict;
1205
+ }
1206
+
1207
+ /* Merge trees */
1208
+
1209
+ static int merge_index_insert_conflict(
1210
+ git_merge_diff_list *diff_list,
1211
+ struct merge_diff_df_data *merge_df_data,
1212
+ const git_index_entry *tree_items[3])
1213
+ {
1214
+ git_merge_diff *conflict;
1215
+
1216
+ if ((conflict = merge_diff_from_index_entries(diff_list, tree_items)) == NULL ||
1217
+ merge_diff_detect_type(conflict) < 0 ||
1218
+ merge_diff_detect_df_conflict(merge_df_data, conflict) < 0 ||
1219
+ git_vector_insert(&diff_list->conflicts, conflict) < 0)
1220
+ return -1;
1221
+
1222
+ return 0;
1223
+ }
1224
+
1225
+ static int merge_index_insert_unmodified(
1226
+ git_merge_diff_list *diff_list,
1227
+ const git_index_entry *tree_items[3])
1228
+ {
1229
+ int error = 0;
1230
+ git_index_entry *entry;
1231
+
1232
+ entry = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry));
1233
+ GITERR_CHECK_ALLOC(entry);
1234
+
1235
+ if ((error = index_entry_dup(entry, &diff_list->pool, tree_items[0])) >= 0)
1236
+ error = git_vector_insert(&diff_list->staged, entry);
1237
+
1238
+ return error;
1239
+ }
1240
+
1241
+ int git_merge_diff_list__find_differences(
1242
+ git_merge_diff_list *diff_list,
1243
+ const git_tree *ancestor_tree,
1244
+ const git_tree *our_tree,
1245
+ const git_tree *their_tree)
1246
+ {
1247
+ git_iterator *iterators[3] = {0};
1248
+ const git_index_entry *items[3] = {0}, *best_cur_item, *cur_items[3];
1249
+ git_vector_cmp entry_compare = git_index_entry__cmp;
1250
+ struct merge_diff_df_data df_data = {0};
1251
+ int cur_item_modified;
1252
+ size_t i, j;
1253
+ int error = 0;
1254
+
1255
+ assert(diff_list && our_tree && their_tree);
1256
+
1257
+ if ((error = git_iterator_for_tree(&iterators[TREE_IDX_ANCESTOR], (git_tree *)ancestor_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
1258
+ (error = git_iterator_for_tree(&iterators[TREE_IDX_OURS], (git_tree *)our_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
1259
+ (error = git_iterator_for_tree(&iterators[TREE_IDX_THEIRS], (git_tree *)their_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0)
1260
+ goto done;
1261
+
1262
+ /* Set up the iterators */
1263
+ for (i = 0; i < 3; i++) {
1264
+ error = git_iterator_current(&items[i], iterators[i]);
1265
+ if (error < 0 && error != GIT_ITEROVER)
1266
+ goto done;
1267
+ }
1268
+
1269
+ while (true) {
1270
+ for (i = 0; i < 3; i++)
1271
+ cur_items[i] = NULL;
1272
+
1273
+ best_cur_item = NULL;
1274
+ cur_item_modified = 0;
1275
+
1276
+ /* Find the next path(s) to consume from each iterator */
1277
+ for (i = 0; i < 3; i++) {
1278
+ if (items[i] == NULL) {
1279
+ cur_item_modified = 1;
1280
+ continue;
1281
+ }
1282
+
1283
+ if (best_cur_item == NULL) {
1284
+ best_cur_item = items[i];
1285
+ cur_items[i] = items[i];
1286
+ } else {
1287
+ int path_diff = entry_compare(items[i], best_cur_item);
1288
+
1289
+ if (path_diff < 0) {
1290
+ /*
1291
+ * Found an item that sorts before our current item, make
1292
+ * our current item this one.
1293
+ */
1294
+ for (j = 0; j < i; j++)
1295
+ cur_items[j] = NULL;
1296
+
1297
+ cur_item_modified = 1;
1298
+ best_cur_item = items[i];
1299
+ cur_items[i] = items[i];
1300
+ } else if (path_diff > 0) {
1301
+ /* No entry for the current item, this is modified */
1302
+ cur_item_modified = 1;
1303
+ } else if (path_diff == 0) {
1304
+ cur_items[i] = items[i];
1305
+
1306
+ if (!cur_item_modified)
1307
+ cur_item_modified = index_entry_cmp(best_cur_item, items[i]);
1308
+ }
1309
+ }
1310
+ }
1311
+
1312
+ if (best_cur_item == NULL)
1313
+ break;
1314
+
1315
+ if (cur_item_modified)
1316
+ error = merge_index_insert_conflict(diff_list, &df_data, cur_items);
1317
+ else
1318
+ error = merge_index_insert_unmodified(diff_list, cur_items);
1319
+ if (error < 0)
1320
+ goto done;
1321
+
1322
+ /* Advance each iterator that participated */
1323
+ for (i = 0; i < 3; i++) {
1324
+ if (cur_items[i] == NULL)
1325
+ continue;
1326
+
1327
+ error = git_iterator_advance(&items[i], iterators[i]);
1328
+ if (error < 0 && error != GIT_ITEROVER)
1329
+ goto done;
1330
+ }
1331
+ }
1332
+
1333
+ done:
1334
+ for (i = 0; i < 3; i++)
1335
+ git_iterator_free(iterators[i]);
1336
+
1337
+ if (error == GIT_ITEROVER)
1338
+ error = 0;
1339
+
1340
+ return error;
1341
+ }
1342
+
1343
+ git_merge_diff_list *git_merge_diff_list__alloc(git_repository *repo)
1344
+ {
1345
+ git_merge_diff_list *diff_list = git__calloc(1, sizeof(git_merge_diff_list));
1346
+
1347
+ if (diff_list == NULL)
1348
+ return NULL;
1349
+
1350
+ diff_list->repo = repo;
1351
+
1352
+ if (git_vector_init(&diff_list->staged, 0, NULL) < 0 ||
1353
+ git_vector_init(&diff_list->conflicts, 0, NULL) < 0 ||
1354
+ git_vector_init(&diff_list->resolved, 0, NULL) < 0 ||
1355
+ git_pool_init(&diff_list->pool, 1, 0) < 0)
1356
+ return NULL;
1357
+
1358
+ return diff_list;
1359
+ }
1360
+
1361
+ void git_merge_diff_list__free(git_merge_diff_list *diff_list)
1362
+ {
1363
+ if (!diff_list)
1364
+ return;
1365
+
1366
+ git_vector_free(&diff_list->staged);
1367
+ git_vector_free(&diff_list->conflicts);
1368
+ git_vector_free(&diff_list->resolved);
1369
+ git_pool_clear(&diff_list->pool);
1370
+ git__free(diff_list);
1371
+ }
1372
+
1373
+ static int merge_tree_normalize_opts(
1374
+ git_repository *repo,
1375
+ git_merge_tree_opts *opts,
1376
+ const git_merge_tree_opts *given)
1377
+ {
1378
+ git_config *cfg = NULL;
1379
+ int error = 0;
1380
+
1381
+ assert(repo && opts);
1382
+
1383
+ if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
1384
+ return error;
1385
+
1386
+ if (given != NULL)
1387
+ memcpy(opts, given, sizeof(git_merge_tree_opts));
1388
+ else {
1389
+ git_merge_tree_opts init = GIT_MERGE_TREE_OPTS_INIT;
1390
+ memcpy(opts, &init, sizeof(init));
1391
+
1392
+ opts->flags = GIT_MERGE_TREE_FIND_RENAMES;
1393
+ opts->rename_threshold = GIT_MERGE_TREE_RENAME_THRESHOLD;
1394
+ }
1395
+
1396
+ if (!opts->target_limit) {
1397
+ int32_t limit = 0;
1398
+
1399
+ opts->target_limit = GIT_MERGE_TREE_TARGET_LIMIT;
1400
+
1401
+ if (git_config_get_int32(&limit, cfg, "merge.renameLimit") < 0) {
1402
+ giterr_clear();
1403
+
1404
+ if (git_config_get_int32(&limit, cfg, "diff.renameLimit") < 0)
1405
+ giterr_clear();
1406
+ }
1407
+
1408
+ if (limit > 0)
1409
+ opts->target_limit = limit;
1410
+ }
1411
+
1412
+ /* assign the internal metric with whitespace flag as payload */
1413
+ if (!opts->metric) {
1414
+ opts->metric = git__malloc(sizeof(git_diff_similarity_metric));
1415
+ GITERR_CHECK_ALLOC(opts->metric);
1416
+
1417
+ opts->metric->file_signature = git_diff_find_similar__hashsig_for_file;
1418
+ opts->metric->buffer_signature = git_diff_find_similar__hashsig_for_buf;
1419
+ opts->metric->free_signature = git_diff_find_similar__hashsig_free;
1420
+ opts->metric->similarity = git_diff_find_similar__calc_similarity;
1421
+
1422
+ if (opts->flags & GIT_DIFF_FIND_IGNORE_WHITESPACE)
1423
+ opts->metric->payload = (void *)GIT_HASHSIG_IGNORE_WHITESPACE;
1424
+ else if (opts->flags & GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE)
1425
+ opts->metric->payload = (void *)GIT_HASHSIG_NORMAL;
1426
+ else
1427
+ opts->metric->payload = (void *)GIT_HASHSIG_SMART_WHITESPACE;
1428
+ }
1429
+
1430
+ return 0;
1431
+ }
1432
+
1433
+
1434
+ static int merge_index_insert_reuc(
1435
+ git_index *index,
1436
+ size_t idx,
1437
+ const git_index_entry *entry)
1438
+ {
1439
+ const git_index_reuc_entry *reuc;
1440
+ int mode[3] = { 0, 0, 0 };
1441
+ git_oid const *oid[3] = { NULL, NULL, NULL };
1442
+ size_t i;
1443
+
1444
+ if (!GIT_MERGE_INDEX_ENTRY_EXISTS(*entry))
1445
+ return 0;
1446
+
1447
+ if ((reuc = git_index_reuc_get_bypath(index, entry->path)) != NULL) {
1448
+ for (i = 0; i < 3; i++) {
1449
+ mode[i] = reuc->mode[i];
1450
+ oid[i] = &reuc->oid[i];
1451
+ }
1452
+ }
1453
+
1454
+ mode[idx] = entry->mode;
1455
+ oid[idx] = &entry->oid;
1456
+
1457
+ return git_index_reuc_add(index, entry->path,
1458
+ mode[0], oid[0], mode[1], oid[1], mode[2], oid[2]);
1459
+ }
1460
+
1461
+ int index_from_diff_list(git_index **out, git_merge_diff_list *diff_list)
1462
+ {
1463
+ git_index *index;
1464
+ size_t i;
1465
+ git_index_entry *entry;
1466
+ git_merge_diff *conflict;
1467
+ int error = 0;
1468
+
1469
+ *out = NULL;
1470
+
1471
+ if ((error = git_index_new(&index)) < 0)
1472
+ return error;
1473
+
1474
+ git_vector_foreach(&diff_list->staged, i, entry) {
1475
+ if ((error = git_index_add(index, entry)) < 0)
1476
+ goto on_error;
1477
+ }
1478
+
1479
+ git_vector_foreach(&diff_list->conflicts, i, conflict) {
1480
+ const git_index_entry *ancestor =
1481
+ GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ?
1482
+ &conflict->ancestor_entry : NULL;
1483
+
1484
+ const git_index_entry *ours =
1485
+ GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ?
1486
+ &conflict->our_entry : NULL;
1487
+
1488
+ const git_index_entry *theirs =
1489
+ GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ?
1490
+ &conflict->their_entry : NULL;
1491
+
1492
+ if ((error = git_index_conflict_add(index, ancestor, ours, theirs)) < 0)
1493
+ goto on_error;
1494
+ }
1495
+
1496
+ /* Add each rename entry to the rename portion of the index. */
1497
+ git_vector_foreach(&diff_list->conflicts, i, conflict) {
1498
+ const char *ancestor_path, *our_path, *their_path;
1499
+
1500
+ if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry))
1501
+ continue;
1502
+
1503
+ ancestor_path = conflict->ancestor_entry.path;
1504
+
1505
+ our_path =
1506
+ GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ?
1507
+ conflict->our_entry.path : NULL;
1508
+
1509
+ their_path =
1510
+ GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ?
1511
+ conflict->their_entry.path : NULL;
1512
+
1513
+ if ((our_path && strcmp(ancestor_path, our_path) != 0) ||
1514
+ (their_path && strcmp(ancestor_path, their_path) != 0)) {
1515
+ if ((error = git_index_name_add(index, ancestor_path, our_path, their_path)) < 0)
1516
+ goto on_error;
1517
+ }
1518
+ }
1519
+
1520
+ /* Add each entry in the resolved conflict to the REUC independently, since
1521
+ * the paths may differ due to renames. */
1522
+ git_vector_foreach(&diff_list->resolved, i, conflict) {
1523
+ const git_index_entry *ancestor =
1524
+ GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ?
1525
+ &conflict->ancestor_entry : NULL;
1526
+
1527
+ const git_index_entry *ours =
1528
+ GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ?
1529
+ &conflict->our_entry : NULL;
1530
+
1531
+ const git_index_entry *theirs =
1532
+ GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ?
1533
+ &conflict->their_entry : NULL;
1534
+
1535
+ if (ancestor != NULL &&
1536
+ (error = merge_index_insert_reuc(index, TREE_IDX_ANCESTOR, ancestor)) < 0)
1537
+ goto on_error;
1538
+
1539
+ if (ours != NULL &&
1540
+ (error = merge_index_insert_reuc(index, TREE_IDX_OURS, ours)) < 0)
1541
+ goto on_error;
1542
+
1543
+ if (theirs != NULL &&
1544
+ (error = merge_index_insert_reuc(index, TREE_IDX_THEIRS, theirs)) < 0)
1545
+ goto on_error;
1546
+ }
1547
+
1548
+ *out = index;
1549
+ return 0;
1550
+
1551
+ on_error:
1552
+ git_index_free(index);
1553
+
1554
+ return error;
1555
+ }
1556
+
1557
+ int git_merge_trees(
1558
+ git_index **out,
1559
+ git_repository *repo,
1560
+ const git_tree *ancestor_tree,
1561
+ const git_tree *our_tree,
1562
+ const git_tree *their_tree,
1563
+ const git_merge_tree_opts *given_opts)
1564
+ {
1565
+ git_merge_diff_list *diff_list;
1566
+ git_merge_tree_opts opts;
1567
+ git_merge_diff *conflict;
1568
+ git_vector changes;
1569
+ size_t i;
1570
+ int error = 0;
1571
+
1572
+ assert(out && repo && our_tree && their_tree);
1573
+
1574
+ *out = NULL;
1575
+
1576
+ if ((error = merge_tree_normalize_opts(repo, &opts, given_opts)) < 0)
1577
+ return error;
1578
+
1579
+ diff_list = git_merge_diff_list__alloc(repo);
1580
+ GITERR_CHECK_ALLOC(diff_list);
1581
+
1582
+ if ((error = git_merge_diff_list__find_differences(diff_list, ancestor_tree, our_tree, their_tree)) < 0 ||
1583
+ (error = git_merge_diff_list__find_renames(repo, diff_list, &opts)) < 0)
1584
+ goto done;
1585
+
1586
+ memcpy(&changes, &diff_list->conflicts, sizeof(git_vector));
1587
+ git_vector_clear(&diff_list->conflicts);
1588
+
1589
+ git_vector_foreach(&changes, i, conflict) {
1590
+ int resolved = 0;
1591
+
1592
+ if ((error = merge_conflict_resolve(&resolved, diff_list, conflict, opts.automerge_flags)) < 0)
1593
+ goto done;
1594
+
1595
+ if (!resolved)
1596
+ git_vector_insert(&diff_list->conflicts, conflict);
1597
+ }
1598
+
1599
+ if (!given_opts || !given_opts->metric)
1600
+ git__free(opts.metric);
1601
+
1602
+ error = index_from_diff_list(out, diff_list);
1603
+
1604
+ done:
1605
+ git_merge_diff_list__free(diff_list);
1606
+
1607
+ return error;
1608
+ }
1609
+
1610
+ /* Merge setup / cleanup */
1611
+
1612
+ static int write_orig_head(
1613
+ git_repository *repo,
1614
+ const git_merge_head *our_head)
1615
+ {
1616
+ git_filebuf file = GIT_FILEBUF_INIT;
1617
+ git_buf file_path = GIT_BUF_INIT;
1618
+ char orig_oid_str[GIT_OID_HEXSZ + 1];
1619
+ int error = 0;
1620
+
1621
+ assert(repo && our_head);
1622
+
1623
+ git_oid_tostr(orig_oid_str, GIT_OID_HEXSZ+1, &our_head->oid);
1624
+
1625
+ if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_ORIG_HEAD_FILE)) == 0 &&
1626
+ (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE)) == 0 &&
1627
+ (error = git_filebuf_printf(&file, "%s\n", orig_oid_str)) == 0)
1628
+ error = git_filebuf_commit(&file, 0666);
1629
+
1630
+ if (error < 0)
1631
+ git_filebuf_cleanup(&file);
1632
+
1633
+ git_buf_free(&file_path);
1634
+
1635
+ return error;
1636
+ }
1637
+
1638
+ static int write_merge_head(
1639
+ git_repository *repo,
1640
+ const git_merge_head *heads[],
1641
+ size_t heads_len)
1642
+ {
1643
+ git_filebuf file = GIT_FILEBUF_INIT;
1644
+ git_buf file_path = GIT_BUF_INIT;
1645
+ char merge_oid_str[GIT_OID_HEXSZ + 1];
1646
+ size_t i;
1647
+ int error = 0;
1648
+
1649
+ assert(repo && heads);
1650
+
1651
+ if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_HEAD_FILE)) < 0 ||
1652
+ (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE)) < 0)
1653
+ goto cleanup;
1654
+
1655
+ for (i = 0; i < heads_len; i++) {
1656
+ git_oid_tostr(merge_oid_str, GIT_OID_HEXSZ+1, &heads[i]->oid);
1657
+
1658
+ if ((error = git_filebuf_printf(&file, "%s\n", merge_oid_str)) < 0)
1659
+ goto cleanup;
1660
+ }
1661
+
1662
+ error = git_filebuf_commit(&file, 0666);
1663
+
1664
+ cleanup:
1665
+ if (error < 0)
1666
+ git_filebuf_cleanup(&file);
1667
+
1668
+ git_buf_free(&file_path);
1669
+
1670
+ return error;
1671
+ }
1672
+
1673
+ static int write_merge_mode(git_repository *repo, unsigned int flags)
1674
+ {
1675
+ git_filebuf file = GIT_FILEBUF_INIT;
1676
+ git_buf file_path = GIT_BUF_INIT;
1677
+ int error = 0;
1678
+
1679
+ /* For future expansion */
1680
+ GIT_UNUSED(flags);
1681
+
1682
+ assert(repo);
1683
+
1684
+ if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MODE_FILE)) < 0 ||
1685
+ (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE)) < 0)
1686
+ goto cleanup;
1687
+
1688
+ error = git_filebuf_commit(&file, 0666);
1689
+
1690
+ cleanup:
1691
+ if (error < 0)
1692
+ git_filebuf_cleanup(&file);
1693
+
1694
+ git_buf_free(&file_path);
1695
+
1696
+ return error;
1697
+ }
1698
+
1699
+ struct merge_msg_entry {
1700
+ const git_merge_head *merge_head;
1701
+ bool written;
1702
+ };
1703
+
1704
+ static int msg_entry_is_branch(
1705
+ const struct merge_msg_entry *entry,
1706
+ git_vector *entries)
1707
+ {
1708
+ GIT_UNUSED(entries);
1709
+
1710
+ return (entry->written == 0 &&
1711
+ entry->merge_head->remote_url == NULL &&
1712
+ entry->merge_head->ref_name != NULL &&
1713
+ git__strncmp(GIT_REFS_HEADS_DIR, entry->merge_head->ref_name, strlen(GIT_REFS_HEADS_DIR)) == 0);
1714
+ }
1715
+
1716
+ static int msg_entry_is_tracking(
1717
+ const struct merge_msg_entry *entry,
1718
+ git_vector *entries)
1719
+ {
1720
+ GIT_UNUSED(entries);
1721
+
1722
+ return (entry->written == 0 &&
1723
+ entry->merge_head->remote_url == NULL &&
1724
+ entry->merge_head->ref_name != NULL &&
1725
+ git__strncmp(GIT_REFS_REMOTES_DIR, entry->merge_head->ref_name, strlen(GIT_REFS_REMOTES_DIR)) == 0);
1726
+ }
1727
+
1728
+ static int msg_entry_is_tag(
1729
+ const struct merge_msg_entry *entry,
1730
+ git_vector *entries)
1731
+ {
1732
+ GIT_UNUSED(entries);
1733
+
1734
+ return (entry->written == 0 &&
1735
+ entry->merge_head->remote_url == NULL &&
1736
+ entry->merge_head->ref_name != NULL &&
1737
+ git__strncmp(GIT_REFS_TAGS_DIR, entry->merge_head->ref_name, strlen(GIT_REFS_TAGS_DIR)) == 0);
1738
+ }
1739
+
1740
+ static int msg_entry_is_remote(
1741
+ const struct merge_msg_entry *entry,
1742
+ git_vector *entries)
1743
+ {
1744
+ if (entry->written == 0 &&
1745
+ entry->merge_head->remote_url != NULL &&
1746
+ entry->merge_head->ref_name != NULL &&
1747
+ git__strncmp(GIT_REFS_HEADS_DIR, entry->merge_head->ref_name, strlen(GIT_REFS_HEADS_DIR)) == 0)
1748
+ {
1749
+ struct merge_msg_entry *existing;
1750
+
1751
+ /* Match only branches from the same remote */
1752
+ if (entries->length == 0)
1753
+ return 1;
1754
+
1755
+ existing = git_vector_get(entries, 0);
1756
+
1757
+ return (git__strcmp(existing->merge_head->remote_url,
1758
+ entry->merge_head->remote_url) == 0);
1759
+ }
1760
+
1761
+ return 0;
1762
+ }
1763
+
1764
+ static int msg_entry_is_oid(
1765
+ const struct merge_msg_entry *merge_msg_entry)
1766
+ {
1767
+ return (merge_msg_entry->written == 0 &&
1768
+ merge_msg_entry->merge_head->ref_name == NULL &&
1769
+ merge_msg_entry->merge_head->remote_url == NULL);
1770
+ }
1771
+
1772
+ static int merge_msg_entry_written(
1773
+ const struct merge_msg_entry *merge_msg_entry)
1774
+ {
1775
+ return (merge_msg_entry->written == 1);
1776
+ }
1777
+
1778
+ static int merge_msg_entries(
1779
+ git_vector *v,
1780
+ const struct merge_msg_entry *entries,
1781
+ size_t len,
1782
+ int (*match)(const struct merge_msg_entry *entry, git_vector *entries))
1783
+ {
1784
+ size_t i;
1785
+ int matches, total = 0;
1786
+
1787
+ git_vector_clear(v);
1788
+
1789
+ for (i = 0; i < len; i++) {
1790
+ if ((matches = match(&entries[i], v)) < 0)
1791
+ return matches;
1792
+ else if (!matches)
1793
+ continue;
1794
+
1795
+ git_vector_insert(v, (struct merge_msg_entry *)&entries[i]);
1796
+ total++;
1797
+ }
1798
+
1799
+ return total;
1800
+ }
1801
+
1802
+ static int merge_msg_write_entries(
1803
+ git_filebuf *file,
1804
+ git_vector *entries,
1805
+ const char *item_name,
1806
+ const char *item_plural_name,
1807
+ size_t ref_name_skip,
1808
+ const char *source,
1809
+ char sep)
1810
+ {
1811
+ struct merge_msg_entry *entry;
1812
+ size_t i;
1813
+ int error = 0;
1814
+
1815
+ if (entries->length == 0)
1816
+ return 0;
1817
+
1818
+ if (sep && (error = git_filebuf_printf(file, "%c ", sep)) < 0)
1819
+ goto done;
1820
+
1821
+ if ((error = git_filebuf_printf(file, "%s ",
1822
+ (entries->length == 1) ? item_name : item_plural_name)) < 0)
1823
+ goto done;
1824
+
1825
+ git_vector_foreach(entries, i, entry) {
1826
+ if (i > 0 &&
1827
+ (error = git_filebuf_printf(file, "%s", (i == entries->length - 1) ? " and " : ", ")) < 0)
1828
+ goto done;
1829
+
1830
+ if ((error = git_filebuf_printf(file, "'%s'", entry->merge_head->ref_name + ref_name_skip)) < 0)
1831
+ goto done;
1832
+
1833
+ entry->written = 1;
1834
+ }
1835
+
1836
+ if (source)
1837
+ error = git_filebuf_printf(file, " of %s", source);
1838
+
1839
+ done:
1840
+ return error;
1841
+ }
1842
+
1843
+ static int merge_msg_write_branches(
1844
+ git_filebuf *file,
1845
+ git_vector *entries,
1846
+ char sep)
1847
+ {
1848
+ return merge_msg_write_entries(file, entries,
1849
+ "branch", "branches", strlen(GIT_REFS_HEADS_DIR), NULL, sep);
1850
+ }
1851
+
1852
+ static int merge_msg_write_tracking(
1853
+ git_filebuf *file,
1854
+ git_vector *entries,
1855
+ char sep)
1856
+ {
1857
+ return merge_msg_write_entries(file, entries,
1858
+ "remote-tracking branch", "remote-tracking branches", 0, NULL, sep);
1859
+ }
1860
+
1861
+ static int merge_msg_write_tags(
1862
+ git_filebuf *file,
1863
+ git_vector *entries,
1864
+ char sep)
1865
+ {
1866
+ return merge_msg_write_entries(file, entries,
1867
+ "tag", "tags", strlen(GIT_REFS_TAGS_DIR), NULL, sep);
1868
+ }
1869
+
1870
+ static int merge_msg_write_remotes(
1871
+ git_filebuf *file,
1872
+ git_vector *entries,
1873
+ char sep)
1874
+ {
1875
+ const char *source;
1876
+
1877
+ if (entries->length == 0)
1878
+ return 0;
1879
+
1880
+ source = ((struct merge_msg_entry *)entries->contents[0])->merge_head->remote_url;
1881
+
1882
+ return merge_msg_write_entries(file, entries,
1883
+ "branch", "branches", strlen(GIT_REFS_HEADS_DIR), source, sep);
1884
+ }
1885
+
1886
+ static int write_merge_msg(
1887
+ git_repository *repo,
1888
+ const git_merge_head *heads[],
1889
+ size_t heads_len)
1890
+ {
1891
+ git_filebuf file = GIT_FILEBUF_INIT;
1892
+ git_buf file_path = GIT_BUF_INIT;
1893
+ char oid_str[GIT_OID_HEXSZ + 1];
1894
+ struct merge_msg_entry *entries;
1895
+ git_vector matching = GIT_VECTOR_INIT;
1896
+ size_t i;
1897
+ char sep = 0;
1898
+ int error = 0;
1899
+
1900
+ assert(repo && heads);
1901
+
1902
+ entries = git__calloc(heads_len, sizeof(struct merge_msg_entry));
1903
+ GITERR_CHECK_ALLOC(entries);
1904
+
1905
+ if (git_vector_init(&matching, heads_len, NULL) < 0)
1906
+ return -1;
1907
+
1908
+ for (i = 0; i < heads_len; i++)
1909
+ entries[i].merge_head = heads[i];
1910
+
1911
+ if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 ||
1912
+ (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE)) < 0 ||
1913
+ (error = git_filebuf_write(&file, "Merge ", 6)) < 0)
1914
+ goto cleanup;
1915
+
1916
+ /*
1917
+ * This is to emulate the format of MERGE_MSG by core git.
1918
+ *
1919
+ * Core git will write all the commits specified by OID, in the order
1920
+ * provided, until the first named branch or tag is reached, at which
1921
+ * point all branches will be written in the order provided, then all
1922
+ * tags, then all remote tracking branches and finally all commits that
1923
+ * were specified by OID that were not already written.
1924
+ *
1925
+ * Yes. Really.
1926
+ */
1927
+ for (i = 0; i < heads_len; i++) {
1928
+ if (!msg_entry_is_oid(&entries[i]))
1929
+ break;
1930
+
1931
+ git_oid_fmt(oid_str, &entries[i].merge_head->oid);
1932
+ oid_str[GIT_OID_HEXSZ] = '\0';
1933
+
1934
+ if ((error = git_filebuf_printf(&file, "%scommit '%s'", (i > 0) ? "; " : "", oid_str)) < 0)
1935
+ goto cleanup;
1936
+
1937
+ entries[i].written = 1;
1938
+ }
1939
+
1940
+ if (i)
1941
+ sep = ';';
1942
+
1943
+ if ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_branch)) < 0 ||
1944
+ (error = merge_msg_write_branches(&file, &matching, sep)) < 0)
1945
+ goto cleanup;
1946
+
1947
+ if (matching.length)
1948
+ sep =',';
1949
+
1950
+ if ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_tracking)) < 0 ||
1951
+ (error = merge_msg_write_tracking(&file, &matching, sep)) < 0)
1952
+ goto cleanup;
1953
+
1954
+ if (matching.length)
1955
+ sep =',';
1956
+
1957
+ if ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_tag)) < 0 ||
1958
+ (error = merge_msg_write_tags(&file, &matching, sep)) < 0)
1959
+ goto cleanup;
1960
+
1961
+ if (matching.length)
1962
+ sep =',';
1963
+
1964
+ /* We should never be called with multiple remote branches, but handle
1965
+ * it in case we are... */
1966
+ while ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_remote)) > 0) {
1967
+ if ((error = merge_msg_write_remotes(&file, &matching, sep)) < 0)
1968
+ goto cleanup;
1969
+
1970
+ if (matching.length)
1971
+ sep =',';
1972
+ }
1973
+
1974
+ if (error < 0)
1975
+ goto cleanup;
1976
+
1977
+ for (i = 0; i < heads_len; i++) {
1978
+ if (merge_msg_entry_written(&entries[i]))
1979
+ continue;
1980
+
1981
+ git_oid_fmt(oid_str, &entries[i].merge_head->oid);
1982
+ oid_str[GIT_OID_HEXSZ] = '\0';
1983
+
1984
+ if ((error = git_filebuf_printf(&file, "; commit '%s'", oid_str)) < 0)
1985
+ goto cleanup;
1986
+ }
1987
+
1988
+ if ((error = git_filebuf_printf(&file, "\n")) < 0 ||
1989
+ (error = git_filebuf_commit(&file, 0666)) < 0)
1990
+ goto cleanup;
1991
+
1992
+ cleanup:
1993
+ if (error < 0)
1994
+ git_filebuf_cleanup(&file);
1995
+
1996
+ git_buf_free(&file_path);
1997
+
1998
+ git_vector_free(&matching);
1999
+ git__free(entries);
2000
+
2001
+ return error;
2002
+ }
2003
+
2004
+ int git_merge__setup(
2005
+ git_repository *repo,
2006
+ const git_merge_head *our_head,
2007
+ const git_merge_head *heads[],
2008
+ size_t heads_len,
2009
+ unsigned int flags)
2010
+ {
2011
+ int error = 0;
2012
+
2013
+ assert (repo && our_head && heads);
2014
+
2015
+ if ((error = write_orig_head(repo, our_head)) == 0 &&
2016
+ (error = write_merge_head(repo, heads, heads_len)) == 0 &&
2017
+ (error = write_merge_mode(repo, flags)) == 0) {
2018
+ error = write_merge_msg(repo, heads, heads_len);
2019
+ }
2020
+
2021
+ return error;
2022
+ }
2023
+
2024
+ int git_repository_merge_cleanup(git_repository *repo)
2025
+ {
2026
+ int error = 0;
2027
+ git_buf merge_head_path = GIT_BUF_INIT,
2028
+ merge_mode_path = GIT_BUF_INIT,
2029
+ merge_msg_path = GIT_BUF_INIT;
2030
+
2031
+ assert(repo);
2032
+
2033
+ if (git_buf_joinpath(&merge_head_path, repo->path_repository, GIT_MERGE_HEAD_FILE) < 0 ||
2034
+ git_buf_joinpath(&merge_mode_path, repo->path_repository, GIT_MERGE_MODE_FILE) < 0 ||
2035
+ git_buf_joinpath(&merge_msg_path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0)
2036
+ return -1;
2037
+
2038
+ if (git_path_isfile(merge_head_path.ptr)) {
2039
+ if ((error = p_unlink(merge_head_path.ptr)) < 0)
2040
+ goto cleanup;
2041
+ }
2042
+
2043
+ if (git_path_isfile(merge_mode_path.ptr))
2044
+ (void)p_unlink(merge_mode_path.ptr);
2045
+
2046
+ if (git_path_isfile(merge_msg_path.ptr))
2047
+ (void)p_unlink(merge_msg_path.ptr);
2048
+
2049
+ cleanup:
2050
+ git_buf_free(&merge_msg_path);
2051
+ git_buf_free(&merge_mode_path);
2052
+ git_buf_free(&merge_head_path);
2053
+
2054
+ return error;
2055
+ }
2056
+
2057
+ /* Merge heads are the input to merge */
2058
+
2059
+ static int merge_head_init(
2060
+ git_merge_head **out,
2061
+ git_repository *repo,
2062
+ const char *ref_name,
2063
+ const char *remote_url,
2064
+ const git_oid *oid)
2065
+ {
2066
+ git_merge_head *head;
2067
+ int error = 0;
2068
+
2069
+ assert(out && oid);
2070
+
2071
+ *out = NULL;
2072
+
2073
+ head = git__calloc(1, sizeof(git_merge_head));
2074
+ GITERR_CHECK_ALLOC(head);
2075
+
2076
+ if (ref_name) {
2077
+ head->ref_name = git__strdup(ref_name);
2078
+ GITERR_CHECK_ALLOC(head->ref_name);
2079
+ }
2080
+
2081
+ if (remote_url) {
2082
+ head->remote_url = git__strdup(remote_url);
2083
+ GITERR_CHECK_ALLOC(head->remote_url);
2084
+ }
2085
+
2086
+ git_oid_cpy(&head->oid, oid);
2087
+
2088
+ if ((error = git_commit_lookup(&head->commit, repo, &head->oid)) < 0) {
2089
+ git_merge_head_free(head);
2090
+ return error;
2091
+ }
2092
+
2093
+ *out = head;
2094
+ return error;
2095
+ }
2096
+
2097
+ int git_merge_head_from_ref(
2098
+ git_merge_head **out,
2099
+ git_repository *repo,
2100
+ git_reference *ref)
2101
+ {
2102
+ git_reference *resolved;
2103
+ int error = 0;
2104
+
2105
+ assert(out && repo && ref);
2106
+
2107
+ *out = NULL;
2108
+
2109
+ if ((error = git_reference_resolve(&resolved, ref)) < 0)
2110
+ return error;
2111
+
2112
+ error = merge_head_init(out, repo, git_reference_name(ref), NULL,
2113
+ git_reference_target(resolved));
2114
+
2115
+ git_reference_free(resolved);
2116
+ return error;
2117
+ }
2118
+
2119
+ int git_merge_head_from_oid(
2120
+ git_merge_head **out,
2121
+ git_repository *repo,
2122
+ const git_oid *oid)
2123
+ {
2124
+ assert(out && repo && oid);
2125
+
2126
+ return merge_head_init(out, repo, NULL, NULL, oid);
2127
+ }
2128
+
2129
+ int git_merge_head_from_fetchhead(
2130
+ git_merge_head **out,
2131
+ git_repository *repo,
2132
+ const char *branch_name,
2133
+ const char *remote_url,
2134
+ const git_oid *oid)
2135
+ {
2136
+ assert(repo && branch_name && remote_url && oid);
2137
+
2138
+ return merge_head_init(out, repo, branch_name, remote_url, oid);
2139
+ }
2140
+
2141
+ void git_merge_head_free(git_merge_head *head)
2142
+ {
2143
+ if (head == NULL)
2144
+ return;
2145
+
2146
+ if (head->commit != NULL)
2147
+ git_object_free((git_object *)head->commit);
2148
+
2149
+ if (head->ref_name != NULL)
2150
+ git__free(head->ref_name);
2151
+
2152
+ if (head->remote_url != NULL)
2153
+ git__free(head->remote_url);
2154
+
2155
+ git__free(head);
2156
+ }