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.
- data/README.md +9 -4
- data/Rakefile +1 -1
- data/ext/rugged/extconf.rb +10 -0
- data/ext/rugged/rugged.c +153 -86
- data/ext/rugged/rugged.h +44 -33
- data/ext/rugged/rugged_blob.c +288 -60
- data/ext/rugged/rugged_branch.c +82 -57
- data/ext/rugged/rugged_commit.c +83 -86
- data/ext/rugged/rugged_config.c +68 -68
- data/ext/rugged/rugged_diff.c +509 -0
- data/ext/rugged/rugged_diff_delta.c +94 -0
- data/ext/rugged/rugged_diff_hunk.c +100 -0
- data/ext/rugged/rugged_diff_line.c +79 -0
- data/ext/rugged/rugged_diff_patch.c +169 -0
- data/ext/rugged/rugged_index.c +539 -8
- data/ext/rugged/rugged_note.c +74 -80
- data/ext/rugged/rugged_object.c +63 -8
- data/ext/rugged/rugged_reference.c +231 -145
- data/ext/rugged/rugged_remote.c +509 -53
- data/ext/rugged/rugged_repo.c +572 -236
- data/ext/rugged/rugged_revwalk.c +59 -36
- data/ext/rugged/rugged_settings.c +7 -9
- data/ext/rugged/rugged_signature.c +7 -11
- data/ext/rugged/rugged_tag.c +93 -39
- data/ext/rugged/rugged_tree.c +321 -58
- data/lib/rugged.rb +1 -0
- data/lib/rugged/commit.rb +16 -1
- data/lib/rugged/console.rb +9 -0
- data/lib/rugged/diff.rb +19 -0
- data/lib/rugged/diff/delta.rb +54 -0
- data/lib/rugged/diff/hunk.rb +23 -0
- data/lib/rugged/diff/line.rb +29 -0
- data/lib/rugged/diff/patch.rb +28 -0
- data/lib/rugged/repository.rb +36 -39
- data/lib/rugged/version.rb +1 -1
- data/test/blob_test.rb +308 -1
- data/test/branch_test.rb +7 -0
- data/test/commit_test.rb +7 -10
- data/test/coverage/cover.rb +9 -1
- data/test/diff_test.rb +777 -0
- data/test/fixtures/archive.tar.gz +0 -0
- data/test/fixtures/attr/attr0 +1 -0
- data/test/fixtures/attr/attr1 +29 -0
- data/test/fixtures/attr/attr2 +21 -0
- data/test/fixtures/attr/attr3 +4 -0
- data/test/fixtures/attr/binfile +1 -0
- data/test/fixtures/attr/dir/file +0 -0
- data/test/fixtures/attr/file +1 -0
- data/test/fixtures/attr/gitattributes +29 -0
- data/test/fixtures/attr/gitignore +2 -0
- data/test/fixtures/attr/ign +1 -0
- data/test/fixtures/attr/macro_bad +1 -0
- data/test/fixtures/attr/macro_test +1 -0
- data/test/fixtures/attr/root_test1 +1 -0
- data/test/fixtures/attr/root_test2 +6 -0
- data/test/fixtures/attr/root_test3 +19 -0
- data/test/fixtures/attr/root_test4.txt +14 -0
- data/test/fixtures/attr/sub/abc +37 -0
- data/test/fixtures/attr/sub/dir/file +0 -0
- data/test/fixtures/attr/sub/file +1 -0
- data/test/fixtures/attr/sub/ign/file +1 -0
- data/test/fixtures/attr/sub/ign/sub/file +1 -0
- data/test/fixtures/attr/sub/sub/dir +0 -0
- data/test/fixtures/attr/sub/sub/file +1 -0
- data/test/fixtures/attr/sub/sub/subsub.txt +1 -0
- data/test/fixtures/attr/sub/subdir_test1 +2 -0
- data/test/fixtures/attr/sub/subdir_test2.txt +1 -0
- data/test/fixtures/diff/another.txt +38 -0
- data/test/fixtures/diff/readme.txt +36 -0
- data/test/fixtures/mergedrepo/conflicts-one.txt +5 -0
- data/test/fixtures/mergedrepo/conflicts-two.txt +5 -0
- data/test/fixtures/mergedrepo/one.txt +10 -0
- data/test/fixtures/mergedrepo/two.txt +12 -0
- data/test/fixtures/status/current_file +1 -0
- data/test/fixtures/status/ignored_file +1 -0
- data/test/fixtures/status/modified_file +2 -0
- data/test/fixtures/status/new_file +1 -0
- data/test/fixtures/status/staged_changes +2 -0
- data/test/fixtures/status/staged_changes_modified_file +3 -0
- data/test/fixtures/status/staged_delete_modified_file +1 -0
- data/test/fixtures/status/staged_new_file +1 -0
- data/test/fixtures/status/staged_new_file_modified_file +2 -0
- data/test/fixtures/status/subdir.txt +2 -0
- data/test/fixtures/status/subdir/current_file +1 -0
- data/test/fixtures/status/subdir/modified_file +2 -0
- data/test/fixtures/status/subdir/new_file +1 -0
- data/test/fixtures/status//350/277/231 +1 -0
- data/test/fixtures/testrepo.git/config +5 -0
- data/test/fixtures/testrepo.git/objects/77/71329dfa3002caf8c61a0ceb62a31d09023f37 +0 -0
- data/test/fixtures/text_file.md +464 -0
- data/test/fixtures/unsymlinked.git/HEAD +1 -0
- data/test/fixtures/unsymlinked.git/config +6 -0
- data/test/fixtures/unsymlinked.git/description +1 -0
- data/test/fixtures/unsymlinked.git/info/exclude +2 -0
- data/test/fixtures/unsymlinked.git/objects/08/8b64704e0d6b8bd061dea879418cb5442a3fbf +0 -0
- data/test/fixtures/unsymlinked.git/objects/13/a5e939bca25940c069fd2169d993dba328e30b +0 -0
- data/test/fixtures/unsymlinked.git/objects/19/bf568e59e3a0b363cafb4106226e62d4a4c41c +0 -0
- data/test/fixtures/unsymlinked.git/objects/58/1fadd35b4cf320d102a152f918729011604773 +0 -0
- data/test/fixtures/unsymlinked.git/objects/5c/87b6791e8b13da658a14d1ef7e09b5dc3bac8c +0 -0
- data/test/fixtures/unsymlinked.git/objects/6f/e5f5398af85fb3de8a6aba0339b6d3bfa26a27 +0 -0
- data/test/fixtures/unsymlinked.git/objects/7f/ccd75616ec188b8f1b23d67506a334cc34a49d +0 -0
- data/test/fixtures/unsymlinked.git/objects/80/6999882bf91d24241e4077906b9017605eb1f3 +0 -0
- data/test/fixtures/unsymlinked.git/objects/83/7d176303c5005505ec1e4a30231c40930c0230 +0 -0
- data/test/fixtures/unsymlinked.git/objects/a8/595ccca04f40818ae0155c8f9c77a230e597b6 +2 -0
- data/test/fixtures/unsymlinked.git/objects/cf/8f1cf5cce859c438d6cc067284cb5e161206e7 +0 -0
- data/test/fixtures/unsymlinked.git/objects/d5/278d05c8607ec420bfee4cf219fbc0eeebfd6a +0 -0
- data/test/fixtures/unsymlinked.git/objects/f4/e16fb76536591a41454194058d048d8e4dd2e9 +0 -0
- data/test/fixtures/unsymlinked.git/objects/f9/e65619d93fdf2673882e0a261c5e93b1a84006 +0 -0
- data/test/fixtures/unsymlinked.git/refs/heads/exe-file +1 -0
- data/test/fixtures/unsymlinked.git/refs/heads/master +1 -0
- data/test/fixtures/unsymlinked.git/refs/heads/reg-file +1 -0
- data/test/index_test.rb +120 -0
- data/test/reference_test.rb +38 -3
- data/test/remote_test.rb +224 -3
- data/test/repo_reset_test.rb +2 -0
- data/test/repo_test.rb +147 -10
- data/test/test_helper.rb +5 -2
- data/vendor/libgit2/include/git2/attr.h +3 -3
- data/vendor/libgit2/include/git2/blob.h +11 -17
- data/vendor/libgit2/include/git2/branch.h +3 -2
- data/vendor/libgit2/include/git2/checkout.h +7 -0
- data/vendor/libgit2/include/git2/clone.h +3 -0
- data/vendor/libgit2/include/git2/commit.h +61 -66
- data/vendor/libgit2/include/git2/common.h +73 -42
- data/vendor/libgit2/include/git2/config.h +57 -71
- data/vendor/libgit2/include/git2/cred_helpers.h +2 -2
- data/vendor/libgit2/include/git2/diff.h +179 -30
- data/vendor/libgit2/include/git2/errors.h +3 -3
- data/vendor/libgit2/include/git2/index.h +225 -146
- data/vendor/libgit2/include/git2/indexer.h +2 -22
- data/vendor/libgit2/include/git2/inttypes.h +9 -9
- data/vendor/libgit2/include/git2/merge.h +123 -5
- data/vendor/libgit2/include/git2/odb.h +59 -38
- data/vendor/libgit2/include/git2/odb_backend.h +45 -104
- data/vendor/libgit2/include/git2/oid.h +30 -19
- data/vendor/libgit2/include/git2/pack.h +21 -3
- data/vendor/libgit2/include/git2/refdb.h +0 -35
- data/vendor/libgit2/include/git2/refs.h +93 -31
- data/vendor/libgit2/include/git2/refspec.h +17 -0
- data/vendor/libgit2/include/git2/remote.h +60 -20
- data/vendor/libgit2/include/git2/repository.h +48 -70
- data/vendor/libgit2/include/git2/reset.h +3 -3
- data/vendor/libgit2/include/git2/revparse.h +22 -0
- data/vendor/libgit2/include/git2/stash.h +1 -1
- data/vendor/libgit2/include/git2/status.h +131 -56
- data/vendor/libgit2/include/git2/strarray.h +2 -2
- data/vendor/libgit2/include/git2/submodule.h +16 -16
- data/vendor/libgit2/include/git2/sys/commit.h +46 -0
- data/vendor/libgit2/include/git2/sys/config.h +71 -0
- data/vendor/libgit2/include/git2/sys/index.h +179 -0
- data/vendor/libgit2/include/git2/sys/odb_backend.h +86 -0
- data/vendor/libgit2/include/git2/sys/refdb_backend.h +158 -0
- data/vendor/libgit2/include/git2/sys/refs.h +38 -0
- data/vendor/libgit2/include/git2/sys/repository.h +106 -0
- data/vendor/libgit2/include/git2/tag.h +44 -18
- data/vendor/libgit2/include/git2/trace.h +1 -2
- data/vendor/libgit2/include/git2/transport.h +74 -0
- data/vendor/libgit2/include/git2/tree.h +12 -22
- data/vendor/libgit2/include/git2/types.h +33 -0
- data/vendor/libgit2/include/git2/version.h +2 -2
- data/vendor/libgit2/src/array.h +66 -0
- data/vendor/libgit2/src/attr.c +26 -13
- data/vendor/libgit2/src/attr_file.c +3 -2
- data/vendor/libgit2/src/attr_file.h +3 -3
- data/vendor/libgit2/src/attrcache.h +4 -4
- data/vendor/libgit2/src/blob.c +13 -9
- data/vendor/libgit2/src/blob.h +2 -2
- data/vendor/libgit2/src/branch.c +67 -49
- data/vendor/libgit2/src/cache.c +224 -54
- data/vendor/libgit2/src/cache.h +33 -20
- data/vendor/libgit2/src/checkout.c +145 -85
- data/vendor/libgit2/src/clone.c +62 -50
- data/vendor/libgit2/src/commit.c +74 -40
- data/vendor/libgit2/src/commit.h +2 -3
- data/vendor/libgit2/src/commit_list.c +14 -8
- data/vendor/libgit2/src/config.c +119 -36
- data/vendor/libgit2/src/config.h +3 -0
- data/vendor/libgit2/src/config_cache.c +24 -7
- data/vendor/libgit2/src/config_file.c +9 -6
- data/vendor/libgit2/src/crlf.c +4 -2
- data/vendor/libgit2/src/date.c +3 -3
- data/vendor/libgit2/src/delta.c +1 -1
- data/vendor/libgit2/src/diff.c +681 -303
- data/vendor/libgit2/src/diff.h +34 -2
- data/vendor/libgit2/src/diff_driver.c +405 -0
- data/vendor/libgit2/src/diff_driver.h +49 -0
- data/vendor/libgit2/src/diff_file.c +447 -0
- data/vendor/libgit2/src/diff_file.h +58 -0
- data/vendor/libgit2/src/diff_patch.c +995 -0
- data/vendor/libgit2/src/diff_patch.h +46 -0
- data/vendor/libgit2/src/diff_print.c +430 -0
- data/vendor/libgit2/src/diff_tform.c +464 -203
- data/vendor/libgit2/src/diff_xdiff.c +166 -0
- data/vendor/libgit2/src/diff_xdiff.h +28 -0
- data/vendor/libgit2/src/fetch.c +11 -4
- data/vendor/libgit2/src/fileops.c +85 -61
- data/vendor/libgit2/src/fileops.h +4 -0
- data/vendor/libgit2/src/global.c +10 -2
- data/vendor/libgit2/src/global.h +0 -8
- data/vendor/libgit2/src/hash/hash_generic.h +3 -3
- data/vendor/libgit2/src/hash/hash_win32.h +4 -4
- data/vendor/libgit2/src/hashsig.c +0 -1
- data/vendor/libgit2/src/ignore.c +68 -28
- data/vendor/libgit2/src/ignore.h +10 -1
- data/vendor/libgit2/src/index.c +666 -84
- data/vendor/libgit2/src/index.h +6 -0
- data/vendor/libgit2/src/indexer.c +10 -28
- data/vendor/libgit2/src/iterator.c +427 -283
- data/vendor/libgit2/src/iterator.h +58 -4
- data/vendor/libgit2/src/merge.c +1892 -32
- data/vendor/libgit2/src/merge.h +132 -5
- data/vendor/libgit2/src/merge_file.c +174 -0
- data/vendor/libgit2/src/merge_file.h +71 -0
- data/vendor/libgit2/src/mwindow.c +1 -1
- data/vendor/libgit2/src/notes.c +45 -48
- data/vendor/libgit2/src/object.c +89 -127
- data/vendor/libgit2/src/object.h +0 -1
- data/vendor/libgit2/src/object_api.c +129 -0
- data/vendor/libgit2/src/odb.c +156 -59
- data/vendor/libgit2/src/odb.h +5 -2
- data/vendor/libgit2/src/odb_loose.c +31 -17
- data/vendor/libgit2/src/odb_pack.c +39 -43
- data/vendor/libgit2/src/oid.c +62 -27
- data/vendor/libgit2/src/oid.h +33 -0
- data/vendor/libgit2/src/oidmap.h +4 -6
- data/vendor/libgit2/src/pack-objects.c +54 -22
- data/vendor/libgit2/src/pack.c +98 -56
- data/vendor/libgit2/src/pack.h +3 -1
- data/vendor/libgit2/src/pathspec.c +26 -1
- data/vendor/libgit2/src/pathspec.h +14 -0
- data/vendor/libgit2/src/pool.c +5 -0
- data/vendor/libgit2/src/posix.c +2 -2
- data/vendor/libgit2/src/posix.h +3 -0
- data/vendor/libgit2/src/push.c +13 -10
- data/vendor/libgit2/src/refdb.c +82 -62
- data/vendor/libgit2/src/refdb.h +16 -16
- data/vendor/libgit2/src/refdb_fs.c +386 -133
- data/vendor/libgit2/src/reflog.c +3 -1
- data/vendor/libgit2/src/refs.c +247 -221
- data/vendor/libgit2/src/refs.h +2 -1
- data/vendor/libgit2/src/refspec.c +18 -1
- data/vendor/libgit2/src/refspec.h +3 -1
- data/vendor/libgit2/src/remote.c +434 -253
- data/vendor/libgit2/src/remote.h +5 -3
- data/vendor/libgit2/src/repository.c +197 -111
- data/vendor/libgit2/src/repository.h +26 -5
- data/vendor/libgit2/src/reset.c +1 -1
- data/vendor/libgit2/src/revparse.c +84 -79
- data/vendor/libgit2/src/revwalk.c +1 -1
- data/vendor/libgit2/src/signature.c +22 -10
- data/vendor/libgit2/src/stash.c +5 -2
- data/vendor/libgit2/src/status.c +311 -107
- data/vendor/libgit2/src/status.h +23 -0
- data/vendor/libgit2/src/submodule.c +21 -13
- data/vendor/libgit2/src/tag.c +42 -31
- data/vendor/libgit2/src/tag.h +2 -3
- data/vendor/libgit2/src/thread-utils.h +105 -3
- data/vendor/libgit2/src/trace.c +1 -2
- data/vendor/libgit2/src/trace.h +3 -3
- data/vendor/libgit2/src/transport.c +18 -6
- data/vendor/libgit2/src/transports/cred.c +103 -1
- data/vendor/libgit2/src/transports/local.c +19 -9
- data/vendor/libgit2/src/transports/smart_protocol.c +32 -12
- data/vendor/libgit2/src/transports/ssh.c +519 -0
- data/vendor/libgit2/src/transports/winhttp.c +3 -1
- data/vendor/libgit2/src/tree.c +26 -28
- data/vendor/libgit2/src/tree.h +3 -3
- data/vendor/libgit2/src/unix/posix.h +2 -0
- data/vendor/libgit2/src/util.c +43 -6
- data/vendor/libgit2/src/util.h +40 -12
- data/vendor/libgit2/src/vector.c +3 -5
- data/vendor/libgit2/src/vector.h +9 -0
- data/vendor/libgit2/src/win32/dir.c +1 -1
- data/vendor/libgit2/src/win32/error.c +2 -0
- data/vendor/libgit2/src/win32/findfile.c +3 -6
- data/vendor/libgit2/src/win32/posix_w32.c +85 -59
- data/vendor/libgit2/src/win32/pthread.c +16 -8
- data/vendor/libgit2/src/win32/pthread.h +7 -4
- metadata +407 -306
- data/test/coverage/HEAD.json +0 -1
- data/vendor/libgit2/include/git2/refdb_backend.h +0 -109
- data/vendor/libgit2/src/diff_output.c +0 -1819
- 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
|
-
|
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
|
135
|
-
* empty. In that case, this function returns GIT_ENOTFOUND and
|
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);
|
data/vendor/libgit2/src/merge.c
CHANGED
@@ -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 "
|
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 "
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
-
|
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
|
-
|
38
|
-
|
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
|
-
|
41
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
+
}
|