rugged 0.23.0b2 → 0.23.0b4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/ext/rugged/rugged_blob.c +39 -0
  3. data/ext/rugged/rugged_diff.c +7 -3
  4. data/ext/rugged/rugged_index.c +2 -2
  5. data/ext/rugged/rugged_remote.c +27 -148
  6. data/ext/rugged/rugged_remote_collection.c +134 -12
  7. data/ext/rugged/rugged_repo.c +74 -5
  8. data/ext/rugged/rugged_submodule.c +18 -208
  9. data/ext/rugged/rugged_submodule_collection.c +148 -0
  10. data/lib/rugged/version.rb +1 -1
  11. data/vendor/libgit2/AUTHORS +1 -0
  12. data/vendor/libgit2/CMakeLists.txt +33 -25
  13. data/vendor/libgit2/deps/winhttp/winhttp.def +29 -29
  14. data/vendor/libgit2/include/git2.h +1 -1
  15. data/vendor/libgit2/include/git2/blob.h +4 -6
  16. data/vendor/libgit2/include/git2/checkout.h +10 -1
  17. data/vendor/libgit2/include/git2/clone.h +6 -7
  18. data/vendor/libgit2/include/git2/commit.h +11 -0
  19. data/vendor/libgit2/include/git2/cred_helpers.h +2 -2
  20. data/vendor/libgit2/include/git2/describe.h +1 -1
  21. data/vendor/libgit2/include/git2/diff.h +68 -11
  22. data/vendor/libgit2/include/git2/errors.h +4 -1
  23. data/vendor/libgit2/include/git2/filter.h +16 -0
  24. data/vendor/libgit2/include/git2/index.h +38 -11
  25. data/vendor/libgit2/include/git2/odb.h +1 -1
  26. data/vendor/libgit2/include/git2/odb_backend.h +2 -2
  27. data/vendor/libgit2/include/git2/remote.h +300 -207
  28. data/vendor/libgit2/include/git2/reset.h +1 -0
  29. data/vendor/libgit2/include/git2/stash.h +135 -4
  30. data/vendor/libgit2/include/git2/status.h +1 -0
  31. data/vendor/libgit2/include/git2/submodule.h +46 -75
  32. data/vendor/libgit2/include/git2/sys/odb_backend.h +1 -1
  33. data/vendor/libgit2/include/git2/sys/stream.h +2 -0
  34. data/vendor/libgit2/include/git2/sys/transport.h +1 -21
  35. data/vendor/libgit2/include/git2/transport.h +27 -0
  36. data/vendor/libgit2/include/git2/types.h +20 -10
  37. data/vendor/libgit2/include/git2/version.h +3 -3
  38. data/vendor/libgit2/libgit2.pc.in +4 -2
  39. data/vendor/libgit2/src/attr.c +2 -1
  40. data/vendor/libgit2/src/attr_file.c +18 -37
  41. data/vendor/libgit2/src/blame.c +2 -2
  42. data/vendor/libgit2/src/blob.c +4 -3
  43. data/vendor/libgit2/src/branch.c +6 -3
  44. data/vendor/libgit2/src/buf_text.c +4 -6
  45. data/vendor/libgit2/src/buf_text.h +1 -2
  46. data/vendor/libgit2/src/buffer.c +8 -6
  47. data/vendor/libgit2/src/buffer.h +1 -1
  48. data/vendor/libgit2/src/cache.c +1 -0
  49. data/vendor/libgit2/src/checkout.c +34 -20
  50. data/vendor/libgit2/src/clone.c +29 -29
  51. data/vendor/libgit2/src/commit.c +65 -0
  52. data/vendor/libgit2/src/common.h +5 -0
  53. data/vendor/libgit2/src/config.c +20 -0
  54. data/vendor/libgit2/src/config.h +6 -0
  55. data/vendor/libgit2/src/config_file.c +2 -2
  56. data/vendor/libgit2/src/crlf.c +39 -17
  57. data/vendor/libgit2/src/curl_stream.c +257 -0
  58. data/vendor/libgit2/src/curl_stream.h +14 -0
  59. data/vendor/libgit2/src/diff.c +223 -88
  60. data/vendor/libgit2/src/diff.h +21 -1
  61. data/vendor/libgit2/src/diff_file.c +23 -13
  62. data/vendor/libgit2/src/diff_file.h +4 -2
  63. data/vendor/libgit2/src/diff_patch.c +266 -71
  64. data/vendor/libgit2/src/diff_patch.h +36 -0
  65. data/vendor/libgit2/src/diff_print.c +156 -126
  66. data/vendor/libgit2/src/diff_tform.c +32 -54
  67. data/vendor/libgit2/src/fetch.c +27 -10
  68. data/vendor/libgit2/src/fetch.h +2 -2
  69. data/vendor/libgit2/src/filebuf.c +1 -1
  70. data/vendor/libgit2/src/fileops.c +6 -2
  71. data/vendor/libgit2/src/filter.c +28 -7
  72. data/vendor/libgit2/src/fnmatch.c +5 -5
  73. data/vendor/libgit2/src/global.c +16 -0
  74. data/vendor/libgit2/src/ident.c +2 -2
  75. data/vendor/libgit2/src/ignore.c +1 -0
  76. data/vendor/libgit2/src/index.c +338 -80
  77. data/vendor/libgit2/src/index.h +4 -1
  78. data/vendor/libgit2/src/indexer.c +19 -5
  79. data/vendor/libgit2/src/iterator.c +118 -11
  80. data/vendor/libgit2/src/iterator.h +25 -0
  81. data/vendor/libgit2/src/merge.c +96 -106
  82. data/vendor/libgit2/src/merge.h +14 -4
  83. data/vendor/libgit2/src/netops.c +3 -3
  84. data/vendor/libgit2/src/odb.c +17 -9
  85. data/vendor/libgit2/src/odb.h +1 -1
  86. data/vendor/libgit2/src/odb_loose.c +2 -2
  87. data/vendor/libgit2/src/odb_pack.c +1 -1
  88. data/vendor/libgit2/src/openssl_stream.c +118 -27
  89. data/vendor/libgit2/src/pack-objects.c +28 -0
  90. data/vendor/libgit2/src/pack-objects.h +1 -0
  91. data/vendor/libgit2/src/pack.c +18 -10
  92. data/vendor/libgit2/src/path.c +16 -11
  93. data/vendor/libgit2/src/path.h +1 -1
  94. data/vendor/libgit2/src/push.c +26 -42
  95. data/vendor/libgit2/src/push.h +2 -34
  96. data/vendor/libgit2/src/rebase.c +1 -1
  97. data/vendor/libgit2/src/refs.c +1 -1
  98. data/vendor/libgit2/src/refspec.c +6 -0
  99. data/vendor/libgit2/src/remote.c +381 -274
  100. data/vendor/libgit2/src/remote.h +0 -4
  101. data/vendor/libgit2/src/repository.c +33 -12
  102. data/vendor/libgit2/src/repository.h +0 -1
  103. data/vendor/libgit2/src/reset.c +1 -0
  104. data/vendor/libgit2/src/stash.c +439 -17
  105. data/vendor/libgit2/src/status.c +6 -0
  106. data/vendor/libgit2/src/stransport_stream.c +58 -21
  107. data/vendor/libgit2/src/stream.h +15 -0
  108. data/vendor/libgit2/src/submodule.c +410 -664
  109. data/vendor/libgit2/src/submodule.h +0 -24
  110. data/vendor/libgit2/src/transaction.c +1 -0
  111. data/vendor/libgit2/src/transports/cred.c +55 -1
  112. data/vendor/libgit2/src/transports/http.c +18 -2
  113. data/vendor/libgit2/src/transports/local.c +60 -59
  114. data/vendor/libgit2/src/transports/smart.h +1 -1
  115. data/vendor/libgit2/src/transports/smart_protocol.c +11 -11
  116. data/vendor/libgit2/src/transports/ssh.c +46 -7
  117. data/vendor/libgit2/src/unix/posix.h +4 -0
  118. data/vendor/libgit2/src/util.c +9 -9
  119. data/vendor/libgit2/src/util.h +9 -0
  120. data/vendor/libgit2/src/win32/posix.h +3 -0
  121. data/vendor/libgit2/src/win32/posix_w32.c +38 -0
  122. data/vendor/libgit2/src/win32/w32_util.h +10 -0
  123. metadata +4 -3
  124. data/vendor/libgit2/include/git2/push.h +0 -94
@@ -0,0 +1,257 @@
1
+ /*
2
+ * Copyright (C) the libgit2 contributors. All rights reserved.
3
+ *
4
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
5
+ * a Linking Exception. For full terms see the included COPYING file.
6
+ */
7
+
8
+ #ifdef GIT_CURL
9
+
10
+ #include <curl/curl.h>
11
+
12
+ #include "stream.h"
13
+ #include "git2/transport.h"
14
+ #include "buffer.h"
15
+ #include "vector.h"
16
+
17
+ typedef struct {
18
+ git_stream parent;
19
+ CURL *handle;
20
+ curl_socket_t socket;
21
+ char curl_error[CURL_ERROR_SIZE + 1];
22
+ git_cert_x509 cert_info;
23
+ git_strarray cert_info_strings;
24
+ } curl_stream;
25
+
26
+ static int seterr_curl(curl_stream *s)
27
+ {
28
+ giterr_set(GITERR_NET, "curl error: %s\n", s->curl_error);
29
+ return -1;
30
+ }
31
+
32
+ static int curls_connect(git_stream *stream)
33
+ {
34
+ curl_stream *s = (curl_stream *) stream;
35
+ long sockextr;
36
+ int failed_cert = 0;
37
+ CURLcode res;
38
+ res = curl_easy_perform(s->handle);
39
+
40
+ if (res != CURLE_OK && res != CURLE_PEER_FAILED_VERIFICATION)
41
+ return seterr_curl(s);
42
+ if (res == CURLE_PEER_FAILED_VERIFICATION)
43
+ failed_cert = 1;
44
+
45
+ if ((res = curl_easy_getinfo(s->handle, CURLINFO_LASTSOCKET, &sockextr)) != CURLE_OK)
46
+ return seterr_curl(s);
47
+
48
+ s->socket = sockextr;
49
+
50
+ if (s->parent.encrypted && failed_cert)
51
+ return GIT_ECERTIFICATE;
52
+
53
+ return 0;
54
+ }
55
+
56
+ static int curls_certificate(git_cert **out, git_stream *stream)
57
+ {
58
+ int error;
59
+ CURLcode res;
60
+ struct curl_slist *slist;
61
+ struct curl_certinfo *certinfo;
62
+ git_vector strings = GIT_VECTOR_INIT;
63
+ curl_stream *s = (curl_stream *) stream;
64
+
65
+ if ((res = curl_easy_getinfo(s->handle, CURLINFO_CERTINFO, &certinfo)) != CURLE_OK)
66
+ return seterr_curl(s);
67
+
68
+ /* No information is available, can happen with SecureTransport */
69
+ if (certinfo->num_of_certs == 0) {
70
+ s->cert_info.cert_type = GIT_CERT_NONE;
71
+ s->cert_info.data = NULL;
72
+ s->cert_info.len = 0;
73
+ return 0;
74
+ }
75
+
76
+ if ((error = git_vector_init(&strings, 8, NULL)) < 0)
77
+ return error;
78
+
79
+ for (slist = certinfo->certinfo[0]; slist; slist = slist->next) {
80
+ char *str = git__strdup(slist->data);
81
+ GITERR_CHECK_ALLOC(str);
82
+ }
83
+
84
+ /* Copy the contents of the vector into a strarray so we can expose them */
85
+ s->cert_info_strings.strings = (char **) strings.contents;
86
+ s->cert_info_strings.count = strings.length;
87
+
88
+ s->cert_info.cert_type = GIT_CERT_STRARRAY;
89
+ s->cert_info.data = &s->cert_info_strings;
90
+ s->cert_info.len = strings.length;
91
+
92
+ *out = (git_cert *) &s->cert_info;
93
+
94
+ return 0;
95
+ }
96
+
97
+ static int curls_set_proxy(git_stream *stream, const char *proxy_url)
98
+ {
99
+ CURLcode res;
100
+ curl_stream *s = (curl_stream *) stream;
101
+
102
+ if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXY, proxy_url)) != CURLE_OK)
103
+ return seterr_curl(s);
104
+
105
+ return 0;
106
+ }
107
+
108
+ static int wait_for(curl_socket_t fd, bool reading)
109
+ {
110
+ int ret;
111
+ fd_set infd, outfd, errfd;
112
+
113
+ FD_ZERO(&infd);
114
+ FD_ZERO(&outfd);
115
+ FD_ZERO(&errfd);
116
+
117
+ FD_SET(fd, &errfd);
118
+ if (reading)
119
+ FD_SET(fd, &infd);
120
+ else
121
+ FD_SET(fd, &outfd);
122
+
123
+ if ((ret = select(fd + 1, &infd, &outfd, &errfd, NULL)) < 0) {
124
+ giterr_set(GITERR_OS, "error in select");
125
+ return -1;
126
+ }
127
+
128
+ return 0;
129
+ }
130
+
131
+ static ssize_t curls_write(git_stream *stream, const char *data, size_t len, int flags)
132
+ {
133
+ int error;
134
+ size_t off = 0, sent;
135
+ CURLcode res;
136
+ curl_stream *s = (curl_stream *) stream;
137
+
138
+ GIT_UNUSED(flags);
139
+
140
+ do {
141
+ if ((error = wait_for(s->socket, false)) < 0)
142
+ return error;
143
+
144
+ res = curl_easy_send(s->handle, data + off, len - off, &sent);
145
+ if (res == CURLE_OK)
146
+ off += sent;
147
+ } while ((res == CURLE_OK || res == CURLE_AGAIN) && off < len);
148
+
149
+ if (res != CURLE_OK)
150
+ return seterr_curl(s);
151
+
152
+ return len;
153
+ }
154
+
155
+ static ssize_t curls_read(git_stream *stream, void *data, size_t len)
156
+ {
157
+ int error;
158
+ size_t read;
159
+ CURLcode res;
160
+ curl_stream *s = (curl_stream *) stream;
161
+
162
+ do {
163
+ if ((error = wait_for(s->socket, true)) < 0)
164
+ return error;
165
+
166
+ res = curl_easy_recv(s->handle, data, len, &read);
167
+ } while (res == CURLE_AGAIN);
168
+
169
+ if (res != CURLE_OK)
170
+ return seterr_curl(s);
171
+
172
+ return read;
173
+ }
174
+
175
+ static int curls_close(git_stream *stream)
176
+ {
177
+ curl_stream *s = (curl_stream *) stream;
178
+
179
+ if (!s->handle)
180
+ return 0;
181
+
182
+ curl_easy_cleanup(s->handle);
183
+ s->handle = NULL;
184
+ s->socket = 0;
185
+
186
+ return 0;
187
+ }
188
+
189
+ static void curls_free(git_stream *stream)
190
+ {
191
+ curl_stream *s = (curl_stream *) stream;
192
+
193
+ curls_close(stream);
194
+ git_strarray_free(&s->cert_info_strings);
195
+ git__free(s);
196
+ }
197
+
198
+ int git_curl_stream_new(git_stream **out, const char *host, const char *port)
199
+ {
200
+ curl_stream *st;
201
+ CURL *handle;
202
+ int iport = 0, error;
203
+
204
+ st = git__calloc(1, sizeof(curl_stream));
205
+ GITERR_CHECK_ALLOC(st);
206
+
207
+ handle = curl_easy_init();
208
+ if (handle == NULL) {
209
+ giterr_set(GITERR_NET, "failed to create curl handle");
210
+ return -1;
211
+ }
212
+
213
+ if ((error = git__strtol32(&iport, port, NULL, 10)) < 0)
214
+ return error;
215
+
216
+ curl_easy_setopt(handle, CURLOPT_URL, host);
217
+ curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, st->curl_error);
218
+ curl_easy_setopt(handle, CURLOPT_PORT, iport);
219
+ curl_easy_setopt(handle, CURLOPT_CONNECT_ONLY, 1);
220
+ curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 1);
221
+ curl_easy_setopt(handle, CURLOPT_CERTINFO, 1);
222
+ curl_easy_setopt(handle, CURLOPT_HTTPPROXYTUNNEL, 1);
223
+
224
+ /* curl_easy_setopt(handle, CURLOPT_VERBOSE, 1); */
225
+
226
+ st->parent.version = GIT_STREAM_VERSION;
227
+ st->parent.encrypted = 0; /* we don't encrypt ourselves */
228
+ st->parent.proxy_support = 1;
229
+ st->parent.connect = curls_connect;
230
+ st->parent.certificate = curls_certificate;
231
+ st->parent.set_proxy = curls_set_proxy;
232
+ st->parent.read = curls_read;
233
+ st->parent.write = curls_write;
234
+ st->parent.close = curls_close;
235
+ st->parent.free = curls_free;
236
+ st->handle = handle;
237
+
238
+ *out = (git_stream *) st;
239
+ return 0;
240
+ }
241
+
242
+ #else
243
+
244
+ #include "stream.h"
245
+
246
+ int git_curl_stream_new(git_stream **out, const char *host, const char *port)
247
+ {
248
+ GIT_UNUSED(out);
249
+ GIT_UNUSED(host);
250
+ GIT_UNUSED(port);
251
+
252
+ giterr_set(GITERR_NET, "curl is not supported in this version");
253
+ return -1;
254
+ }
255
+
256
+
257
+ #endif
@@ -0,0 +1,14 @@
1
+ /*
2
+ * Copyright (C) the libgit2 contributors. All rights reserved.
3
+ *
4
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
5
+ * a Linking Exception. For full terms see the included COPYING file.
6
+ */
7
+ #ifndef INCLUDE_curl_stream_h__
8
+ #define INCLUDE_curl_stream_h__
9
+
10
+ #include "git2/sys/stream.h"
11
+
12
+ extern int git_curl_stream_new(git_stream **out, const char *host, const char *port);
13
+
14
+ #endif
@@ -77,11 +77,24 @@ static int diff_insert_delta(
77
77
  static int diff_delta__from_one(
78
78
  git_diff *diff,
79
79
  git_delta_t status,
80
- const git_index_entry *entry)
80
+ const git_index_entry *oitem,
81
+ const git_index_entry *nitem)
81
82
  {
83
+ const git_index_entry *entry = nitem;
84
+ bool has_old = false;
82
85
  git_diff_delta *delta;
83
86
  const char *matched_pathspec;
84
87
 
88
+ assert((oitem != NULL) ^ (nitem != NULL));
89
+
90
+ if (oitem) {
91
+ entry = oitem;
92
+ has_old = true;
93
+ }
94
+
95
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE))
96
+ has_old = !has_old;
97
+
85
98
  if ((entry->flags & GIT_IDXENTRY_VALID) != 0)
86
99
  return 0;
87
100
 
@@ -111,20 +124,21 @@ static int diff_delta__from_one(
111
124
  assert(status != GIT_DELTA_MODIFIED);
112
125
  delta->nfiles = 1;
113
126
 
114
- if (delta->status == GIT_DELTA_DELETED) {
127
+ if (has_old) {
115
128
  delta->old_file.mode = entry->mode;
116
129
  delta->old_file.size = entry->file_size;
130
+ delta->old_file.flags |= GIT_DIFF_FLAG_EXISTS;
117
131
  git_oid_cpy(&delta->old_file.id, &entry->id);
118
132
  } else /* ADDED, IGNORED, UNTRACKED */ {
119
133
  delta->new_file.mode = entry->mode;
120
134
  delta->new_file.size = entry->file_size;
135
+ delta->new_file.flags |= GIT_DIFF_FLAG_EXISTS;
121
136
  git_oid_cpy(&delta->new_file.id, &entry->id);
122
137
  }
123
138
 
124
139
  delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID;
125
140
 
126
- if (delta->status == GIT_DELTA_DELETED ||
127
- !git_oid_iszero(&delta->new_file.id))
141
+ if (has_old || !git_oid_iszero(&delta->new_file.id))
128
142
  delta->new_file.flags |= GIT_DIFF_FLAG_VALID_ID;
129
143
 
130
144
  return diff_insert_delta(diff, delta, matched_pathspec);
@@ -137,9 +151,10 @@ static int diff_delta__from_two(
137
151
  uint32_t old_mode,
138
152
  const git_index_entry *new_entry,
139
153
  uint32_t new_mode,
140
- git_oid *new_oid,
154
+ const git_oid *new_id,
141
155
  const char *matched_pathspec)
142
156
  {
157
+ const git_oid *old_id = &old_entry->id;
143
158
  git_diff_delta *delta;
144
159
  const char *canonical_path = old_entry->path;
145
160
 
@@ -147,38 +162,45 @@ static int diff_delta__from_two(
147
162
  DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNMODIFIED))
148
163
  return 0;
149
164
 
165
+ if (!new_id)
166
+ new_id = &new_entry->id;
167
+
150
168
  if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
151
169
  uint32_t temp_mode = old_mode;
152
170
  const git_index_entry *temp_entry = old_entry;
171
+ const git_oid *temp_id = old_id;
172
+
153
173
  old_entry = new_entry;
154
174
  new_entry = temp_entry;
155
175
  old_mode = new_mode;
156
176
  new_mode = temp_mode;
177
+ old_id = new_id;
178
+ new_id = temp_id;
157
179
  }
158
180
 
159
181
  delta = diff_delta__alloc(diff, status, canonical_path);
160
182
  GITERR_CHECK_ALLOC(delta);
161
183
  delta->nfiles = 2;
162
184
 
163
- git_oid_cpy(&delta->old_file.id, &old_entry->id);
164
- delta->old_file.size = old_entry->file_size;
165
- delta->old_file.mode = old_mode;
166
- delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID;
185
+ if (!git_index_entry_is_conflict(old_entry)) {
186
+ delta->old_file.size = old_entry->file_size;
187
+ delta->old_file.mode = old_mode;
188
+ git_oid_cpy(&delta->old_file.id, old_id);
189
+ delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID |
190
+ GIT_DIFF_FLAG_EXISTS;
191
+ }
167
192
 
168
- git_oid_cpy(&delta->new_file.id, &new_entry->id);
169
- delta->new_file.size = new_entry->file_size;
170
- delta->new_file.mode = new_mode;
193
+ if (!git_index_entry_is_conflict(new_entry)) {
194
+ git_oid_cpy(&delta->new_file.id, new_id);
195
+ delta->new_file.size = new_entry->file_size;
196
+ delta->new_file.mode = new_mode;
197
+ delta->old_file.flags |= GIT_DIFF_FLAG_EXISTS;
198
+ delta->new_file.flags |= GIT_DIFF_FLAG_EXISTS;
171
199
 
172
- if (new_oid) {
173
- if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE))
174
- git_oid_cpy(&delta->old_file.id, new_oid);
175
- else
176
- git_oid_cpy(&delta->new_file.id, new_oid);
200
+ if (!git_oid_iszero(&new_entry->id))
201
+ delta->new_file.flags |= GIT_DIFF_FLAG_VALID_ID;
177
202
  }
178
203
 
179
- if (new_oid || !git_oid_iszero(&new_entry->id))
180
- delta->new_file.flags |= GIT_DIFF_FLAG_VALID_ID;
181
-
182
204
  return diff_insert_delta(diff, delta, matched_pathspec);
183
205
  }
184
206
 
@@ -327,6 +349,22 @@ static const char *diff_mnemonic_prefix(
327
349
  return pfx;
328
350
  }
329
351
 
352
+ static int diff_entry_cmp(const void *a, const void *b)
353
+ {
354
+ const git_index_entry *entry_a = a;
355
+ const git_index_entry *entry_b = b;
356
+
357
+ return strcmp(entry_a->path, entry_b->path);
358
+ }
359
+
360
+ static int diff_entry_icmp(const void *a, const void *b)
361
+ {
362
+ const git_index_entry *entry_a = a;
363
+ const git_index_entry *entry_b = b;
364
+
365
+ return strcasecmp(entry_a->path, entry_b->path);
366
+ }
367
+
330
368
  static void diff_set_ignore_case(git_diff *diff, bool ignore_case)
331
369
  {
332
370
  if (!ignore_case) {
@@ -335,7 +373,7 @@ static void diff_set_ignore_case(git_diff *diff, bool ignore_case)
335
373
  diff->strcomp = git__strcmp;
336
374
  diff->strncomp = git__strncmp;
337
375
  diff->pfxcomp = git__prefixcmp;
338
- diff->entrycomp = git_index_entry_cmp;
376
+ diff->entrycomp = diff_entry_cmp;
339
377
 
340
378
  git_vector_set_cmp(&diff->deltas, git_diff_delta__cmp);
341
379
  } else {
@@ -344,7 +382,7 @@ static void diff_set_ignore_case(git_diff *diff, bool ignore_case)
344
382
  diff->strcomp = git__strcasecmp;
345
383
  diff->strncomp = git__strncasecmp;
346
384
  diff->pfxcomp = git__prefixcmp_icase;
347
- diff->entrycomp = git_index_entry_icmp;
385
+ diff->entrycomp = diff_entry_icmp;
348
386
 
349
387
  git_vector_set_cmp(&diff->deltas, git_diff_delta__casecmp);
350
388
  }
@@ -532,7 +570,7 @@ int git_diff__oid_for_file(
532
570
  git_oid *out,
533
571
  git_diff *diff,
534
572
  const char *path,
535
- uint16_t mode,
573
+ uint16_t mode,
536
574
  git_off_t size)
537
575
  {
538
576
  git_index_entry entry;
@@ -542,13 +580,14 @@ int git_diff__oid_for_file(
542
580
  entry.file_size = size;
543
581
  entry.path = (char *)path;
544
582
 
545
- return git_diff__oid_for_entry(out, diff, &entry, NULL);
583
+ return git_diff__oid_for_entry(out, diff, &entry, mode, NULL);
546
584
  }
547
585
 
548
586
  int git_diff__oid_for_entry(
549
587
  git_oid *out,
550
588
  git_diff *diff,
551
589
  const git_index_entry *src,
590
+ uint16_t mode,
552
591
  const git_oid *update_match)
553
592
  {
554
593
  int error = 0;
@@ -562,7 +601,7 @@ int git_diff__oid_for_entry(
562
601
  &full_path, git_repository_workdir(diff->repo), entry.path) < 0)
563
602
  return -1;
564
603
 
565
- if (!entry.mode) {
604
+ if (!mode) {
566
605
  struct stat st;
567
606
 
568
607
  diff->perf.stat_calls++;
@@ -578,7 +617,7 @@ int git_diff__oid_for_entry(
578
617
  }
579
618
 
580
619
  /* calculate OID for file if possible */
581
- if (S_ISGITLINK(entry.mode)) {
620
+ if (S_ISGITLINK(mode)) {
582
621
  git_submodule *sm;
583
622
 
584
623
  if (!git_submodule_lookup(&sm, diff->repo, entry.path)) {
@@ -592,7 +631,7 @@ int git_diff__oid_for_entry(
592
631
  */
593
632
  giterr_clear();
594
633
  }
595
- } else if (S_ISLNK(entry.mode)) {
634
+ } else if (S_ISLNK(mode)) {
596
635
  error = git_odb__hashlink(out, full_path.ptr);
597
636
  diff->perf.oid_calculations++;
598
637
  } else if (!git__is_sizet(entry.file_size)) {
@@ -619,10 +658,15 @@ int git_diff__oid_for_entry(
619
658
  /* update index for entry if requested */
620
659
  if (!error && update_match && git_oid_equal(out, update_match)) {
621
660
  git_index *idx;
661
+ git_index_entry updated_entry;
662
+
663
+ memcpy(&updated_entry, &entry, sizeof(git_index_entry));
664
+ updated_entry.mode = mode;
665
+ git_oid_cpy(&updated_entry.id, out);
622
666
 
623
667
  if (!(error = git_repository_index__weakptr(&idx, diff->repo))) {
624
- git_oid_cpy(&entry.id, out);
625
- error = git_index_add(idx, &entry);
668
+ error = git_index_add(idx, &updated_entry);
669
+ diff->index_updated = true;
626
670
  }
627
671
  }
628
672
 
@@ -731,47 +775,54 @@ static int maybe_modified(
731
775
  new_is_workdir)
732
776
  nmode = (nmode & ~MODE_BITS_MASK) | (omode & MODE_BITS_MASK);
733
777
 
778
+ /* if one side is a conflict, mark the whole delta as conflicted */
779
+ if (git_index_entry_is_conflict(oitem) ||
780
+ git_index_entry_is_conflict(nitem)) {
781
+ status = GIT_DELTA_CONFLICTED;
782
+
734
783
  /* support "assume unchanged" (poorly, b/c we still stat everything) */
735
- if ((oitem->flags & GIT_IDXENTRY_VALID) != 0)
784
+ } else if ((oitem->flags & GIT_IDXENTRY_VALID) != 0) {
736
785
  status = GIT_DELTA_UNMODIFIED;
737
786
 
738
787
  /* support "skip worktree" index bit */
739
- else if ((oitem->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE) != 0)
788
+ } else if ((oitem->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE) != 0) {
740
789
  status = GIT_DELTA_UNMODIFIED;
741
790
 
742
791
  /* if basic type of file changed, then split into delete and add */
743
- else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) {
744
- if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE))
792
+ } else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) {
793
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE)) {
745
794
  status = GIT_DELTA_TYPECHANGE;
795
+ }
796
+
746
797
  else if (nmode == GIT_FILEMODE_UNREADABLE) {
747
- if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem)))
748
- error = diff_delta__from_one(diff, GIT_DELTA_UNREADABLE, nitem);
798
+ if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem, NULL)))
799
+ error = diff_delta__from_one(diff, GIT_DELTA_UNREADABLE, NULL, nitem);
749
800
  return error;
750
801
  }
802
+
751
803
  else {
752
- if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem)))
753
- error = diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem);
804
+ if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem, NULL)))
805
+ error = diff_delta__from_one(diff, GIT_DELTA_ADDED, NULL, nitem);
754
806
  return error;
755
807
  }
756
- }
757
808
 
758
809
  /* if oids and modes match (and are valid), then file is unmodified */
759
- else if (git_oid_equal(&oitem->id, &nitem->id) &&
810
+ } else if (git_oid_equal(&oitem->id, &nitem->id) &&
760
811
  omode == nmode &&
761
- !git_oid_iszero(&oitem->id))
812
+ !git_oid_iszero(&oitem->id)) {
762
813
  status = GIT_DELTA_UNMODIFIED;
763
814
 
764
815
  /* if we have an unknown OID and a workdir iterator, then check some
765
816
  * circumstances that can accelerate things or need special handling
766
817
  */
767
- else if (git_oid_iszero(&nitem->id) && new_is_workdir) {
818
+ } else if (git_oid_iszero(&nitem->id) && new_is_workdir) {
768
819
  bool use_ctime = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) != 0);
769
820
  bool use_nanos = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_NANOSECS) != 0);
821
+ git_index *index;
822
+ git_iterator_index(&index, info->new_iter);
770
823
 
771
824
  status = GIT_DELTA_UNMODIFIED;
772
825
 
773
- /* TODO: add check against index file st_mtime to avoid racy-git */
774
-
775
826
  if (S_ISGITLINK(nmode)) {
776
827
  if ((error = maybe_modified_submodule(&status, &noid, diff, info)) < 0)
777
828
  return error;
@@ -790,17 +841,18 @@ static int maybe_modified(
790
841
  !diff_time_eq(&oitem->ctime, &nitem->ctime, use_nanos)) ||
791
842
  oitem->ino != nitem->ino ||
792
843
  oitem->uid != nitem->uid ||
793
- oitem->gid != nitem->gid)
844
+ oitem->gid != nitem->gid ||
845
+ (index && nitem->mtime.seconds >= index->stamp.mtime))
794
846
  {
795
847
  status = GIT_DELTA_MODIFIED;
796
848
  modified_uncertain = true;
797
849
  }
798
- }
799
850
 
800
851
  /* if mode is GITLINK and submodules are ignored, then skip */
801
- else if (S_ISGITLINK(nmode) &&
802
- DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES))
852
+ } else if (S_ISGITLINK(nmode) &&
853
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES)) {
803
854
  status = GIT_DELTA_UNMODIFIED;
855
+ }
804
856
 
805
857
  /* if we got here and decided that the files are modified, but we
806
858
  * haven't calculated the OID of the new item, then calculate it now
@@ -809,8 +861,9 @@ static int maybe_modified(
809
861
  const git_oid *update_check =
810
862
  DIFF_FLAG_IS_SET(diff, GIT_DIFF_UPDATE_INDEX) && omode == nmode ?
811
863
  &oitem->id : NULL;
864
+
812
865
  if ((error = git_diff__oid_for_entry(
813
- &noid, diff, nitem, update_check)) < 0)
866
+ &noid, diff, nitem, nmode, update_check)) < 0)
814
867
  return error;
815
868
 
816
869
  /* if oid matches, then mark unmodified (except submodules, where
@@ -830,8 +883,9 @@ static int maybe_modified(
830
883
  DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_CASECHANGE) &&
831
884
  strcmp(oitem->path, nitem->path) != 0) {
832
885
 
833
- if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem)))
834
- error = diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem);
886
+ if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem, NULL)))
887
+ error = diff_delta__from_one(diff, GIT_DELTA_ADDED, NULL, nitem);
888
+
835
889
  return error;
836
890
  }
837
891
 
@@ -857,6 +911,84 @@ static bool entry_is_prefixed(
857
911
  item->path[pathlen] == '/');
858
912
  }
859
913
 
914
+ static int iterator_current(
915
+ const git_index_entry **entry,
916
+ git_iterator *iterator)
917
+ {
918
+ int error;
919
+
920
+ if ((error = git_iterator_current(entry, iterator)) == GIT_ITEROVER) {
921
+ *entry = NULL;
922
+ error = 0;
923
+ }
924
+
925
+ return error;
926
+ }
927
+
928
+ static int iterator_advance(
929
+ const git_index_entry **entry,
930
+ git_iterator *iterator)
931
+ {
932
+ const git_index_entry *prev_entry = *entry;
933
+ int cmp, error;
934
+
935
+ /* if we're looking for conflicts, we only want to report
936
+ * one conflict for each file, instead of all three sides.
937
+ * so if this entry is a conflict for this file, and the
938
+ * previous one was a conflict for the same file, skip it.
939
+ */
940
+ while ((error = git_iterator_advance(entry, iterator)) == 0) {
941
+ if (!(iterator->flags & GIT_ITERATOR_INCLUDE_CONFLICTS) ||
942
+ !git_index_entry_is_conflict(prev_entry) ||
943
+ !git_index_entry_is_conflict(*entry))
944
+ break;
945
+
946
+ cmp = (iterator->flags & GIT_ITERATOR_IGNORE_CASE) ?
947
+ strcasecmp(prev_entry->path, (*entry)->path) :
948
+ strcmp(prev_entry->path, (*entry)->path);
949
+
950
+ if (cmp)
951
+ break;
952
+ }
953
+
954
+ if (error == GIT_ITEROVER) {
955
+ *entry = NULL;
956
+ error = 0;
957
+ }
958
+
959
+ return error;
960
+ }
961
+
962
+ static int iterator_advance_into(
963
+ const git_index_entry **entry,
964
+ git_iterator *iterator)
965
+ {
966
+ int error;
967
+
968
+ if ((error = git_iterator_advance_into(entry, iterator)) == GIT_ITEROVER) {
969
+ *entry = NULL;
970
+ error = 0;
971
+ }
972
+
973
+ return error;
974
+ }
975
+
976
+ static int iterator_advance_over_with_status(
977
+ const git_index_entry **entry,
978
+ git_iterator_status_t *status,
979
+ git_iterator *iterator)
980
+ {
981
+ int error;
982
+
983
+ if ((error = git_iterator_advance_over_with_status(
984
+ entry, status, iterator)) == GIT_ITEROVER) {
985
+ *entry = NULL;
986
+ error = 0;
987
+ }
988
+
989
+ return error;
990
+ }
991
+
860
992
  static int handle_unmatched_new_item(
861
993
  git_diff *diff, diff_in_progress *info)
862
994
  {
@@ -868,8 +1000,12 @@ static int handle_unmatched_new_item(
868
1000
  /* check if this is a prefix of the other side */
869
1001
  contains_oitem = entry_is_prefixed(diff, info->oitem, nitem);
870
1002
 
1003
+ /* update delta_type if this item is conflicted */
1004
+ if (git_index_entry_is_conflict(nitem))
1005
+ delta_type = GIT_DELTA_CONFLICTED;
1006
+
871
1007
  /* update delta_type if this item is ignored */
872
- if (git_iterator_current_is_ignored(info->new_iter))
1008
+ else if (git_iterator_current_is_ignored(info->new_iter))
873
1009
  delta_type = GIT_DELTA_IGNORED;
874
1010
 
875
1011
  if (nitem->mode == GIT_FILEMODE_TREE) {
@@ -904,18 +1040,17 @@ static int handle_unmatched_new_item(
904
1040
  git_iterator_status_t untracked_state;
905
1041
 
906
1042
  /* attempt to insert record for this directory */
907
- if ((error = diff_delta__from_one(diff, delta_type, nitem)) != 0)
1043
+ if ((error = diff_delta__from_one(diff, delta_type, NULL, nitem)) != 0)
908
1044
  return error;
909
1045
 
910
1046
  /* if delta wasn't created (because of rules), just skip ahead */
911
1047
  last = diff_delta__last_for_item(diff, nitem);
912
1048
  if (!last)
913
- return git_iterator_advance(&info->nitem, info->new_iter);
1049
+ return iterator_advance(&info->nitem, info->new_iter);
914
1050
 
915
1051
  /* iterate into dir looking for an actual untracked file */
916
- if ((error = git_iterator_advance_over_with_status(
917
- &info->nitem, &untracked_state, info->new_iter)) < 0 &&
918
- error != GIT_ITEROVER)
1052
+ if ((error = iterator_advance_over_with_status(
1053
+ &info->nitem, &untracked_state, info->new_iter)) < 0)
919
1054
  return error;
920
1055
 
921
1056
  /* if we found nothing or just ignored items, update the record */
@@ -935,7 +1070,7 @@ static int handle_unmatched_new_item(
935
1070
 
936
1071
  /* try to advance into directory if necessary */
937
1072
  if (recurse_into_dir) {
938
- error = git_iterator_advance_into(&info->nitem, info->new_iter);
1073
+ error = iterator_advance_into(&info->nitem, info->new_iter);
939
1074
 
940
1075
  /* if real error or no error, proceed with iteration */
941
1076
  if (error != GIT_ENOTFOUND)
@@ -946,7 +1081,7 @@ static int handle_unmatched_new_item(
946
1081
  * it or ignore it
947
1082
  */
948
1083
  if (contains_oitem)
949
- return git_iterator_advance(&info->nitem, info->new_iter);
1084
+ return iterator_advance(&info->nitem, info->new_iter);
950
1085
  delta_type = GIT_DELTA_IGNORED;
951
1086
  }
952
1087
  }
@@ -955,10 +1090,12 @@ static int handle_unmatched_new_item(
955
1090
  DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS) &&
956
1091
  git_iterator_current_tree_is_ignored(info->new_iter))
957
1092
  /* item contained in ignored directory, so skip over it */
958
- return git_iterator_advance(&info->nitem, info->new_iter);
1093
+ return iterator_advance(&info->nitem, info->new_iter);
959
1094
 
960
- else if (info->new_iter->type != GIT_ITERATOR_TYPE_WORKDIR)
961
- delta_type = GIT_DELTA_ADDED;
1095
+ else if (info->new_iter->type != GIT_ITERATOR_TYPE_WORKDIR) {
1096
+ if (delta_type != GIT_DELTA_CONFLICTED)
1097
+ delta_type = GIT_DELTA_ADDED;
1098
+ }
962
1099
 
963
1100
  else if (nitem->mode == GIT_FILEMODE_COMMIT) {
964
1101
  /* ignore things that are not actual submodules */
@@ -968,12 +1105,12 @@ static int handle_unmatched_new_item(
968
1105
 
969
1106
  /* if this contains a tracked item, treat as normal TREE */
970
1107
  if (contains_oitem) {
971
- error = git_iterator_advance_into(&info->nitem, info->new_iter);
1108
+ error = iterator_advance_into(&info->nitem, info->new_iter);
972
1109
  if (error != GIT_ENOTFOUND)
973
1110
  return error;
974
1111
 
975
1112
  giterr_clear();
976
- return git_iterator_advance(&info->nitem, info->new_iter);
1113
+ return iterator_advance(&info->nitem, info->new_iter);
977
1114
  }
978
1115
  }
979
1116
  }
@@ -986,7 +1123,7 @@ static int handle_unmatched_new_item(
986
1123
  }
987
1124
 
988
1125
  /* Actually create the record for this item if necessary */
989
- if ((error = diff_delta__from_one(diff, delta_type, nitem)) != 0)
1126
+ if ((error = diff_delta__from_one(diff, delta_type, NULL, nitem)) != 0)
990
1127
  return error;
991
1128
 
992
1129
  /* If user requested TYPECHANGE records, then check for that instead of
@@ -1004,14 +1141,20 @@ static int handle_unmatched_new_item(
1004
1141
  }
1005
1142
  }
1006
1143
 
1007
- return git_iterator_advance(&info->nitem, info->new_iter);
1144
+ return iterator_advance(&info->nitem, info->new_iter);
1008
1145
  }
1009
1146
 
1010
1147
  static int handle_unmatched_old_item(
1011
1148
  git_diff *diff, diff_in_progress *info)
1012
1149
  {
1013
- int error = diff_delta__from_one(diff, GIT_DELTA_DELETED, info->oitem);
1014
- if (error != 0)
1150
+ git_delta_t delta_type = GIT_DELTA_DELETED;
1151
+ int error;
1152
+
1153
+ /* update delta_type if this item is conflicted */
1154
+ if (git_index_entry_is_conflict(info->oitem))
1155
+ delta_type = GIT_DELTA_CONFLICTED;
1156
+
1157
+ if ((error = diff_delta__from_one(diff, delta_type, info->oitem, NULL)) < 0)
1015
1158
  return error;
1016
1159
 
1017
1160
  /* if we are generating TYPECHANGE records then check for that
@@ -1033,10 +1176,10 @@ static int handle_unmatched_old_item(
1033
1176
  */
1034
1177
  if (S_ISDIR(info->nitem->mode) &&
1035
1178
  DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS))
1036
- return git_iterator_advance(&info->nitem, info->new_iter);
1179
+ return iterator_advance(&info->nitem, info->new_iter);
1037
1180
  }
1038
1181
 
1039
- return git_iterator_advance(&info->oitem, info->old_iter);
1182
+ return iterator_advance(&info->oitem, info->old_iter);
1040
1183
  }
1041
1184
 
1042
1185
  static int handle_matched_item(
@@ -1047,9 +1190,8 @@ static int handle_matched_item(
1047
1190
  if ((error = maybe_modified(diff, info)) < 0)
1048
1191
  return error;
1049
1192
 
1050
- if (!(error = git_iterator_advance(&info->oitem, info->old_iter)) ||
1051
- error == GIT_ITEROVER)
1052
- error = git_iterator_advance(&info->nitem, info->new_iter);
1193
+ if (!(error = iterator_advance(&info->oitem, info->old_iter)))
1194
+ error = iterator_advance(&info->nitem, info->new_iter);
1053
1195
 
1054
1196
  return error;
1055
1197
  }
@@ -1085,13 +1227,9 @@ int git_diff__from_iterators(
1085
1227
  if ((error = diff_list_apply_options(diff, opts)) < 0)
1086
1228
  goto cleanup;
1087
1229
 
1088
- if ((error = git_iterator_current(&info.oitem, old_iter)) < 0 &&
1089
- error != GIT_ITEROVER)
1230
+ if ((error = iterator_current(&info.oitem, old_iter)) < 0 ||
1231
+ (error = iterator_current(&info.nitem, new_iter)) < 0)
1090
1232
  goto cleanup;
1091
- if ((error = git_iterator_current(&info.nitem, new_iter)) < 0 &&
1092
- error != GIT_ITEROVER)
1093
- goto cleanup;
1094
- error = 0;
1095
1233
 
1096
1234
  /* run iterators building diffs */
1097
1235
  while (!error && (info.oitem || info.nitem)) {
@@ -1113,10 +1251,6 @@ int git_diff__from_iterators(
1113
1251
  */
1114
1252
  else
1115
1253
  error = handle_matched_item(diff, &info);
1116
-
1117
- /* because we are iterating over two lists, ignore ITEROVER */
1118
- if (error == GIT_ITEROVER)
1119
- error = 0;
1120
1254
  }
1121
1255
 
1122
1256
  diff->perf.stat_calls += old_iter->stat_calls + new_iter->stat_calls;
@@ -1186,6 +1320,8 @@ int git_diff_tree_to_index(
1186
1320
  {
1187
1321
  int error = 0;
1188
1322
  bool index_ignore_case = false;
1323
+ git_iterator_flag_t iflag = GIT_ITERATOR_DONT_IGNORE_CASE |
1324
+ GIT_ITERATOR_INCLUDE_CONFLICTS;
1189
1325
 
1190
1326
  assert(diff && repo);
1191
1327
 
@@ -1195,10 +1331,8 @@ int git_diff_tree_to_index(
1195
1331
  index_ignore_case = index->ignore_case;
1196
1332
 
1197
1333
  DIFF_FROM_ITERATORS(
1198
- git_iterator_for_tree(
1199
- &a, old_tree, GIT_ITERATOR_DONT_IGNORE_CASE, pfx, pfx),
1200
- git_iterator_for_index(
1201
- &b, index, GIT_ITERATOR_DONT_IGNORE_CASE, pfx, pfx)
1334
+ git_iterator_for_tree(&a, old_tree, iflag, pfx, pfx),
1335
+ git_iterator_for_index(&b, index, iflag, pfx, pfx)
1202
1336
  );
1203
1337
 
1204
1338
  /* if index is in case-insensitive order, re-sort deltas to match */
@@ -1222,12 +1356,13 @@ int git_diff_index_to_workdir(
1222
1356
  return error;
1223
1357
 
1224
1358
  DIFF_FROM_ITERATORS(
1225
- git_iterator_for_index(&a, index, 0, pfx, pfx),
1359
+ git_iterator_for_index(
1360
+ &a, index, GIT_ITERATOR_INCLUDE_CONFLICTS, pfx, pfx),
1226
1361
  git_iterator_for_workdir(
1227
1362
  &b, repo, index, NULL, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx)
1228
1363
  );
1229
1364
 
1230
- if (!error && DIFF_FLAG_IS_SET(*diff, GIT_DIFF_UPDATE_INDEX))
1365
+ if (!error && DIFF_FLAG_IS_SET(*diff, GIT_DIFF_UPDATE_INDEX) && (*diff)->index_updated)
1231
1366
  error = git_index_write(index);
1232
1367
 
1233
1368
  return error;