rugged 0.22.2 → 0.23.0b1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (155) hide show
  1. checksums.yaml +4 -4
  2. data/ext/rugged/rugged.c +1 -2
  3. data/ext/rugged/rugged_branch_collection.c +6 -44
  4. data/ext/rugged/rugged_config.c +7 -3
  5. data/ext/rugged/rugged_diff_delta.c +1 -1
  6. data/ext/rugged/rugged_diff_line.c +1 -1
  7. data/ext/rugged/rugged_object.c +2 -2
  8. data/ext/rugged/rugged_reference_collection.c +12 -56
  9. data/ext/rugged/rugged_remote.c +4 -33
  10. data/ext/rugged/rugged_remote_collection.c +1 -1
  11. data/ext/rugged/rugged_repo.c +10 -36
  12. data/ext/rugged/rugged_settings.c +3 -3
  13. data/ext/rugged/rugged_tree.c +1 -1
  14. data/lib/rugged/version.rb +1 -1
  15. data/vendor/libgit2/CMakeLists.txt +10 -3
  16. data/vendor/libgit2/COPYING +15 -21
  17. data/vendor/libgit2/include/git2/annotated_commit.h +20 -3
  18. data/vendor/libgit2/include/git2/branch.h +20 -16
  19. data/vendor/libgit2/include/git2/checkout.h +32 -18
  20. data/vendor/libgit2/include/git2/cherrypick.h +2 -2
  21. data/vendor/libgit2/include/git2/clone.h +4 -10
  22. data/vendor/libgit2/include/git2/config.h +66 -12
  23. data/vendor/libgit2/include/git2/describe.h +3 -2
  24. data/vendor/libgit2/include/git2/diff.h +3 -3
  25. data/vendor/libgit2/include/git2/errors.h +1 -0
  26. data/vendor/libgit2/include/git2/filter.h +21 -5
  27. data/vendor/libgit2/include/git2/index.h +32 -0
  28. data/vendor/libgit2/include/git2/merge.h +20 -3
  29. data/vendor/libgit2/include/git2/oid.h +1 -1
  30. data/vendor/libgit2/include/git2/pack.h +13 -0
  31. data/vendor/libgit2/include/git2/patch.h +3 -6
  32. data/vendor/libgit2/include/git2/rebase.h +8 -12
  33. data/vendor/libgit2/include/git2/refs.h +19 -29
  34. data/vendor/libgit2/include/git2/remote.h +5 -11
  35. data/vendor/libgit2/include/git2/repository.h +44 -15
  36. data/vendor/libgit2/include/git2/reset.h +19 -10
  37. data/vendor/libgit2/include/git2/revert.h +2 -2
  38. data/vendor/libgit2/include/git2/submodule.h +3 -9
  39. data/vendor/libgit2/include/git2/sys/config.h +3 -1
  40. data/vendor/libgit2/include/git2/sys/filter.h +10 -2
  41. data/vendor/libgit2/include/git2/sys/hashsig.h +49 -22
  42. data/vendor/libgit2/include/git2/transport.h +1 -1
  43. data/vendor/libgit2/include/git2/types.h +10 -3
  44. data/vendor/libgit2/include/git2/version.h +3 -2
  45. data/vendor/libgit2/src/annotated_commit.c +28 -0
  46. data/vendor/libgit2/src/array.h +19 -8
  47. data/vendor/libgit2/src/attr.c +95 -35
  48. data/vendor/libgit2/src/attr_file.c +51 -17
  49. data/vendor/libgit2/src/attr_file.h +37 -10
  50. data/vendor/libgit2/src/attrcache.c +13 -7
  51. data/vendor/libgit2/src/attrcache.h +1 -0
  52. data/vendor/libgit2/src/blame.c +26 -2
  53. data/vendor/libgit2/src/blame_git.c +6 -2
  54. data/vendor/libgit2/src/blob.c +6 -8
  55. data/vendor/libgit2/src/branch.c +55 -43
  56. data/vendor/libgit2/src/buf_text.c +13 -6
  57. data/vendor/libgit2/src/buffer.c +110 -25
  58. data/vendor/libgit2/src/buffer.h +18 -0
  59. data/vendor/libgit2/src/checkout.c +164 -92
  60. data/vendor/libgit2/src/checkout.h +0 -7
  61. data/vendor/libgit2/src/cherrypick.c +13 -7
  62. data/vendor/libgit2/src/clone.c +23 -25
  63. data/vendor/libgit2/src/commit.c +3 -3
  64. data/vendor/libgit2/src/common.h +23 -1
  65. data/vendor/libgit2/src/config.c +137 -19
  66. data/vendor/libgit2/src/config.h +2 -2
  67. data/vendor/libgit2/src/config_cache.c +2 -1
  68. data/vendor/libgit2/src/config_file.c +39 -18
  69. data/vendor/libgit2/src/config_file.h +1 -1
  70. data/vendor/libgit2/src/crlf.c +1 -1
  71. data/vendor/libgit2/src/delta-apply.c +3 -2
  72. data/vendor/libgit2/src/delta.c +25 -6
  73. data/vendor/libgit2/src/describe.c +2 -0
  74. data/vendor/libgit2/src/diff.c +8 -5
  75. data/vendor/libgit2/src/diff_driver.c +39 -18
  76. data/vendor/libgit2/src/diff_file.c +1 -1
  77. data/vendor/libgit2/src/diff_patch.c +12 -6
  78. data/vendor/libgit2/src/diff_tform.c +21 -24
  79. data/vendor/libgit2/src/filebuf.c +14 -12
  80. data/vendor/libgit2/src/fileops.c +61 -18
  81. data/vendor/libgit2/src/fileops.h +11 -2
  82. data/vendor/libgit2/src/filter.c +351 -99
  83. data/vendor/libgit2/src/filter.h +17 -0
  84. data/vendor/libgit2/src/global.c +38 -9
  85. data/vendor/libgit2/src/hash/hash_generic.c +1 -1
  86. data/vendor/libgit2/src/hashsig.c +28 -16
  87. data/vendor/libgit2/src/ignore.c +2 -2
  88. data/vendor/libgit2/src/index.c +159 -42
  89. data/vendor/libgit2/src/index.h +29 -0
  90. data/vendor/libgit2/src/indexer.c +11 -2
  91. data/vendor/libgit2/src/integer.h +96 -0
  92. data/vendor/libgit2/src/iterator.c +5 -3
  93. data/vendor/libgit2/src/khash.h +41 -29
  94. data/vendor/libgit2/src/merge.c +48 -35
  95. data/vendor/libgit2/src/merge.h +0 -1
  96. data/vendor/libgit2/src/merge_file.c +13 -0
  97. data/vendor/libgit2/src/mwindow.c +1 -1
  98. data/vendor/libgit2/src/notes.c +1 -1
  99. data/vendor/libgit2/src/odb.c +13 -11
  100. data/vendor/libgit2/src/odb_loose.c +22 -10
  101. data/vendor/libgit2/src/odb_mempack.c +4 -2
  102. data/vendor/libgit2/src/offmap.h +3 -2
  103. data/vendor/libgit2/src/oid.c +1 -1
  104. data/vendor/libgit2/src/oidmap.h +2 -1
  105. data/vendor/libgit2/src/openssl_stream.c +6 -3
  106. data/vendor/libgit2/src/pack-objects.c +273 -12
  107. data/vendor/libgit2/src/pack-objects.h +10 -0
  108. data/vendor/libgit2/src/pack.c +17 -6
  109. data/vendor/libgit2/src/pack.h +1 -3
  110. data/vendor/libgit2/src/path.c +68 -38
  111. data/vendor/libgit2/src/pathspec.c +3 -0
  112. data/vendor/libgit2/src/pool.c +9 -8
  113. data/vendor/libgit2/src/posix.c +11 -1
  114. data/vendor/libgit2/src/push.c +15 -17
  115. data/vendor/libgit2/src/push.h +1 -6
  116. data/vendor/libgit2/src/rebase.c +77 -35
  117. data/vendor/libgit2/src/refdb_fs.c +2 -2
  118. data/vendor/libgit2/src/refs.c +107 -81
  119. data/vendor/libgit2/src/refs.h +2 -2
  120. data/vendor/libgit2/src/refspec.c +3 -0
  121. data/vendor/libgit2/src/remote.c +19 -21
  122. data/vendor/libgit2/src/repository.c +258 -67
  123. data/vendor/libgit2/src/repository.h +31 -16
  124. data/vendor/libgit2/src/reset.c +28 -12
  125. data/vendor/libgit2/src/revert.c +12 -7
  126. data/vendor/libgit2/src/revwalk.c +3 -5
  127. data/vendor/libgit2/src/revwalk.h +1 -1
  128. data/vendor/libgit2/src/sortedcache.c +5 -3
  129. data/vendor/libgit2/src/stash.c +3 -5
  130. data/vendor/libgit2/src/strmap.h +2 -1
  131. data/vendor/libgit2/src/submodule.c +5 -6
  132. data/vendor/libgit2/src/tag.c +7 -5
  133. data/vendor/libgit2/src/transaction.c +1 -1
  134. data/vendor/libgit2/src/transports/cred.c +5 -2
  135. data/vendor/libgit2/src/transports/git.c +2 -3
  136. data/vendor/libgit2/src/transports/local.c +15 -34
  137. data/vendor/libgit2/src/transports/smart.c +1 -1
  138. data/vendor/libgit2/src/transports/smart_pkt.c +58 -18
  139. data/vendor/libgit2/src/transports/smart_protocol.c +2 -2
  140. data/vendor/libgit2/src/transports/winhttp.c +2 -2
  141. data/vendor/libgit2/src/tree.c +7 -5
  142. data/vendor/libgit2/src/tsort.c +3 -1
  143. data/vendor/libgit2/src/util.c +25 -0
  144. data/vendor/libgit2/src/util.h +31 -27
  145. data/vendor/libgit2/src/vector.c +2 -7
  146. data/vendor/libgit2/src/win32/dir.c +5 -3
  147. data/vendor/libgit2/src/win32/git2.rc +8 -4
  148. data/vendor/libgit2/src/win32/mingw-compat.h +7 -0
  149. data/vendor/libgit2/src/win32/msvc-compat.h +3 -0
  150. data/vendor/libgit2/src/win32/posix.h +1 -3
  151. data/vendor/libgit2/src/win32/posix_w32.c +31 -7
  152. data/vendor/libgit2/src/win32/utf-conv.c +1 -3
  153. data/vendor/libgit2/src/zstream.c +1 -1
  154. metadata +5 -5
  155. data/vendor/libgit2/src/bswap.h +0 -97
@@ -13,7 +13,7 @@ int git_buf_text_puts_escaped(
13
13
  const char *esc_with)
14
14
  {
15
15
  const char *scan;
16
- size_t total = 0, esc_len = strlen(esc_with), count;
16
+ size_t total = 0, esc_len = strlen(esc_with), count, alloclen;
17
17
 
18
18
  if (!string)
19
19
  return 0;
@@ -29,7 +29,8 @@ int git_buf_text_puts_escaped(
29
29
  scan += count;
30
30
  }
31
31
 
32
- if (git_buf_grow(buf, buf->size + total + 1) < 0)
32
+ GITERR_CHECK_ALLOC_ADD(&alloclen, total, 1);
33
+ if (git_buf_grow_by(buf, alloclen) < 0)
33
34
  return -1;
34
35
 
35
36
  for (scan = string; *scan; ) {
@@ -65,6 +66,7 @@ int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src)
65
66
  const char *scan = src->ptr;
66
67
  const char *scan_end = src->ptr + src->size;
67
68
  const char *next = memchr(scan, '\r', src->size);
69
+ size_t new_size;
68
70
  char *out;
69
71
 
70
72
  assert(tgt != src);
@@ -73,8 +75,10 @@ int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src)
73
75
  return git_buf_set(tgt, src->ptr, src->size);
74
76
 
75
77
  /* reduce reallocs while in the loop */
76
- if (git_buf_grow(tgt, src->size + 1) < 0)
78
+ GITERR_CHECK_ALLOC_ADD(&new_size, src->size, 1);
79
+ if (git_buf_grow(tgt, new_size) < 0)
77
80
  return -1;
81
+
78
82
  out = tgt->ptr;
79
83
  tgt->size = 0;
80
84
 
@@ -110,6 +114,7 @@ int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src)
110
114
  const char *end = start + src->size;
111
115
  const char *scan = start;
112
116
  const char *next = memchr(scan, '\n', src->size);
117
+ size_t alloclen;
113
118
 
114
119
  assert(tgt != src);
115
120
 
@@ -117,13 +122,14 @@ int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src)
117
122
  return git_buf_set(tgt, src->ptr, src->size);
118
123
 
119
124
  /* attempt to reduce reallocs while in the loop */
120
- if (git_buf_grow(tgt, src->size + (src->size >> 4) + 1) < 0)
125
+ GITERR_CHECK_ALLOC_ADD(&alloclen, src->size, src->size >> 4);
126
+ GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
127
+ if (git_buf_grow(tgt, alloclen) < 0)
121
128
  return -1;
122
129
  tgt->size = 0;
123
130
 
124
131
  for (; next; scan = next + 1, next = memchr(scan, '\n', end - scan)) {
125
132
  size_t copylen = next - scan;
126
- size_t needsize = tgt->size + copylen + 2 + 1;
127
133
 
128
134
  /* if we find mixed line endings, bail */
129
135
  if (next > start && next[-1] == '\r') {
@@ -131,7 +137,8 @@ int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src)
131
137
  return GIT_PASSTHROUGH;
132
138
  }
133
139
 
134
- if (tgt->asize < needsize && git_buf_grow(tgt, needsize) < 0)
140
+ GITERR_CHECK_ALLOC_ADD(&alloclen, copylen, 3);
141
+ if (git_buf_grow_by(tgt, alloclen) < 0)
135
142
  return -1;
136
143
 
137
144
  if (next > scan) {
@@ -63,6 +63,14 @@ int git_buf_try_grow(
63
63
  /* round allocation up to multiple of 8 */
64
64
  new_size = (new_size + 7) & ~7;
65
65
 
66
+ if (new_size < buf->size) {
67
+ if (mark_oom)
68
+ buf->ptr = git_buf__oom;
69
+
70
+ giterr_set_oom();
71
+ return -1;
72
+ }
73
+
66
74
  new_ptr = git__realloc(new_ptr, new_size);
67
75
 
68
76
  if (!new_ptr) {
@@ -93,6 +101,18 @@ int git_buf_grow(git_buf *buffer, size_t target_size)
93
101
  return git_buf_try_grow(buffer, target_size, true, true);
94
102
  }
95
103
 
104
+ int git_buf_grow_by(git_buf *buffer, size_t additional_size)
105
+ {
106
+ size_t newsize;
107
+
108
+ if (GIT_ADD_SIZET_OVERFLOW(&newsize, buffer->size, additional_size)) {
109
+ buffer->ptr = git_buf__oom;
110
+ return -1;
111
+ }
112
+
113
+ return git_buf_try_grow(buffer, newsize, true, true);
114
+ }
115
+
96
116
  void git_buf_free(git_buf *buf)
97
117
  {
98
118
  if (!buf) return;
@@ -127,11 +147,14 @@ void git_buf_clear(git_buf *buf)
127
147
 
128
148
  int git_buf_set(git_buf *buf, const void *data, size_t len)
129
149
  {
150
+ size_t alloclen;
151
+
130
152
  if (len == 0 || data == NULL) {
131
153
  git_buf_clear(buf);
132
154
  } else {
133
155
  if (data != buf->ptr) {
134
- ENSURE_SIZE(buf, len + 1);
156
+ GITERR_CHECK_ALLOC_ADD(&alloclen, len, 1);
157
+ ENSURE_SIZE(buf, alloclen);
135
158
  memmove(buf->ptr, data, len);
136
159
  }
137
160
 
@@ -160,7 +183,9 @@ int git_buf_sets(git_buf *buf, const char *string)
160
183
 
161
184
  int git_buf_putc(git_buf *buf, char c)
162
185
  {
163
- ENSURE_SIZE(buf, buf->size + 2);
186
+ size_t new_size;
187
+ GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, 2);
188
+ ENSURE_SIZE(buf, new_size);
164
189
  buf->ptr[buf->size++] = c;
165
190
  buf->ptr[buf->size] = '\0';
166
191
  return 0;
@@ -168,7 +193,10 @@ int git_buf_putc(git_buf *buf, char c)
168
193
 
169
194
  int git_buf_putcn(git_buf *buf, char c, size_t len)
170
195
  {
171
- ENSURE_SIZE(buf, buf->size + len + 1);
196
+ size_t new_size;
197
+ GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
198
+ GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
199
+ ENSURE_SIZE(buf, new_size);
172
200
  memset(buf->ptr + buf->size, c, len);
173
201
  buf->size += len;
174
202
  buf->ptr[buf->size] = '\0';
@@ -178,8 +206,13 @@ int git_buf_putcn(git_buf *buf, char c, size_t len)
178
206
  int git_buf_put(git_buf *buf, const char *data, size_t len)
179
207
  {
180
208
  if (len) {
209
+ size_t new_size;
210
+
181
211
  assert(data);
182
- ENSURE_SIZE(buf, buf->size + len + 1);
212
+
213
+ GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
214
+ GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
215
+ ENSURE_SIZE(buf, new_size);
183
216
  memmove(buf->ptr + buf->size, data, len);
184
217
  buf->size += len;
185
218
  buf->ptr[buf->size] = '\0';
@@ -201,8 +234,13 @@ int git_buf_encode_base64(git_buf *buf, const char *data, size_t len)
201
234
  size_t extra = len % 3;
202
235
  uint8_t *write, a, b, c;
203
236
  const uint8_t *read = (const uint8_t *)data;
237
+ size_t blocks = (len / 3) + !!extra, alloclen;
204
238
 
205
- ENSURE_SIZE(buf, buf->size + 4 * ((len / 3) + !!extra) + 1);
239
+ GITERR_CHECK_ALLOC_ADD(&blocks, blocks, 1);
240
+ GITERR_CHECK_ALLOC_MULTIPLY(&alloclen, blocks, 4);
241
+ GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, buf->size);
242
+
243
+ ENSURE_SIZE(buf, alloclen);
206
244
  write = (uint8_t *)&buf->ptr[buf->size];
207
245
 
208
246
  /* convert each run of 3 bytes into 4 output bytes */
@@ -253,10 +291,12 @@ int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len)
253
291
  {
254
292
  size_t i;
255
293
  int8_t a, b, c, d;
256
- size_t orig_size = buf->size;
294
+ size_t orig_size = buf->size, new_size;
257
295
 
258
296
  assert(len % 4 == 0);
259
- ENSURE_SIZE(buf, buf->size + (len / 4 * 3) + 1);
297
+ GITERR_CHECK_ALLOC_ADD(&new_size, (len / 4 * 3), buf->size);
298
+ GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
299
+ ENSURE_SIZE(buf, new_size);
260
300
 
261
301
  for (i = 0; i < len; i += 4) {
262
302
  if ((a = BASE64_DECODE_VALUE(base64[i])) < 0 ||
@@ -284,7 +324,13 @@ static const char b85str[] =
284
324
 
285
325
  int git_buf_encode_base85(git_buf *buf, const char *data, size_t len)
286
326
  {
287
- ENSURE_SIZE(buf, buf->size + (5 * ((len / 4) + !!(len % 4))) + 1);
327
+ size_t blocks = (len / 4) + !!(len % 4), alloclen;
328
+
329
+ GITERR_CHECK_ALLOC_MULTIPLY(&alloclen, blocks, 5);
330
+ GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, buf->size);
331
+ GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
332
+
333
+ ENSURE_SIZE(buf, alloclen);
288
334
 
289
335
  while (len) {
290
336
  uint32_t acc = 0;
@@ -317,9 +363,11 @@ int git_buf_encode_base85(git_buf *buf, const char *data, size_t len)
317
363
 
318
364
  int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
319
365
  {
366
+ size_t expected_size, new_size;
320
367
  int len;
321
- const size_t expected_size = buf->size + (strlen(format) * 2);
322
368
 
369
+ GITERR_CHECK_ALLOC_MULTIPLY(&expected_size, strlen(format), 2);
370
+ GITERR_CHECK_ALLOC_ADD(&expected_size, expected_size, buf->size);
323
371
  ENSURE_SIZE(buf, expected_size);
324
372
 
325
373
  while (1) {
@@ -345,7 +393,9 @@ int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
345
393
  break;
346
394
  }
347
395
 
348
- ENSURE_SIZE(buf, buf->size + len + 1);
396
+ GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
397
+ GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
398
+ ENSURE_SIZE(buf, new_size);
349
399
  }
350
400
 
351
401
  return 0;
@@ -450,6 +500,20 @@ void git_buf_attach(git_buf *buf, char *ptr, size_t asize)
450
500
  }
451
501
  }
452
502
 
503
+ void git_buf_attach_notowned(git_buf *buf, const char *ptr, size_t size)
504
+ {
505
+ if (git_buf_is_allocated(buf))
506
+ git_buf_free(buf);
507
+
508
+ if (!size) {
509
+ git_buf_init(buf, 0);
510
+ } else {
511
+ buf->ptr = (char *)ptr;
512
+ buf->asize = 0;
513
+ buf->size = size;
514
+ }
515
+ }
516
+
453
517
  int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
454
518
  {
455
519
  va_list ap;
@@ -472,16 +536,20 @@ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
472
536
  continue;
473
537
 
474
538
  segment_len = strlen(segment);
475
- total_size += segment_len;
539
+
540
+ GITERR_CHECK_ALLOC_ADD(&total_size, total_size, segment_len);
541
+
476
542
  if (segment_len == 0 || segment[segment_len - 1] != separator)
477
- ++total_size; /* space for separator */
543
+ GITERR_CHECK_ALLOC_ADD(&total_size, total_size, 1);
478
544
  }
479
545
  va_end(ap);
480
546
 
481
547
  /* expand buffer if needed */
482
548
  if (total_size == 0)
483
549
  return 0;
484
- if (git_buf_grow(buf, buf->size + total_size + 1) < 0)
550
+
551
+ GITERR_CHECK_ALLOC_ADD(&total_size, total_size, 1);
552
+ if (git_buf_grow_by(buf, total_size) < 0)
485
553
  return -1;
486
554
 
487
555
  out = buf->ptr + buf->size;
@@ -542,6 +610,7 @@ int git_buf_join(
542
610
  {
543
611
  size_t strlen_a = str_a ? strlen(str_a) : 0;
544
612
  size_t strlen_b = strlen(str_b);
613
+ size_t alloc_len;
545
614
  int need_sep = 0;
546
615
  ssize_t offset_a = -1;
547
616
 
@@ -559,7 +628,10 @@ int git_buf_join(
559
628
  if (str_a >= buf->ptr && str_a < buf->ptr + buf->size)
560
629
  offset_a = str_a - buf->ptr;
561
630
 
562
- if (git_buf_grow(buf, strlen_a + strlen_b + need_sep + 1) < 0)
631
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, strlen_a, strlen_b);
632
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, need_sep);
633
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1);
634
+ if (git_buf_grow(buf, alloc_len) < 0)
563
635
  return -1;
564
636
  assert(buf->ptr);
565
637
 
@@ -587,7 +659,10 @@ int git_buf_join3(
587
659
  const char *str_b,
588
660
  const char *str_c)
589
661
  {
590
- size_t len_a = strlen(str_a), len_b = strlen(str_b), len_c = strlen(str_c);
662
+ size_t len_a = strlen(str_a),
663
+ len_b = strlen(str_b),
664
+ len_c = strlen(str_c),
665
+ len_total;
591
666
  int sep_a = 0, sep_b = 0;
592
667
  char *tgt;
593
668
 
@@ -607,7 +682,12 @@ int git_buf_join3(
607
682
  sep_b = (str_b[len_b - 1] != separator);
608
683
  }
609
684
 
610
- if (git_buf_grow(buf, len_a + sep_a + len_b + sep_b + len_c + 1) < 0)
685
+ GITERR_CHECK_ALLOC_ADD(&len_total, len_a, sep_a);
686
+ GITERR_CHECK_ALLOC_ADD(&len_total, len_total, len_b);
687
+ GITERR_CHECK_ALLOC_ADD(&len_total, len_total, sep_b);
688
+ GITERR_CHECK_ALLOC_ADD(&len_total, len_total, len_c);
689
+ GITERR_CHECK_ALLOC_ADD(&len_total, len_total, 1);
690
+ if (git_buf_grow(buf, len_total) < 0)
611
691
  return -1;
612
692
 
613
693
  tgt = buf->ptr;
@@ -660,22 +740,27 @@ int git_buf_splice(
660
740
  const char *data,
661
741
  size_t nb_to_insert)
662
742
  {
663
- assert(buf &&
664
- where <= git_buf_len(buf) &&
665
- where + nb_to_remove <= git_buf_len(buf));
743
+ char *splice_loc;
744
+ size_t new_size, alloc_size;
745
+
746
+ assert(buf && where <= buf->size && nb_to_remove <= buf->size - where);
747
+
748
+ splice_loc = buf->ptr + where;
666
749
 
667
750
  /* Ported from git.git
668
751
  * https://github.com/git/git/blob/16eed7c/strbuf.c#L159-176
669
752
  */
670
- ENSURE_SIZE(buf, buf->size + nb_to_insert - nb_to_insert + 1);
753
+ GITERR_CHECK_ALLOC_ADD(&new_size, (buf->size - nb_to_remove), nb_to_insert);
754
+ GITERR_CHECK_ALLOC_ADD(&alloc_size, new_size, 1);
755
+ ENSURE_SIZE(buf, alloc_size);
671
756
 
672
- memmove(buf->ptr + where + nb_to_insert,
673
- buf->ptr + where + nb_to_remove,
674
- buf->size - where - nb_to_remove);
757
+ memmove(splice_loc + nb_to_insert,
758
+ splice_loc + nb_to_remove,
759
+ buf->size - where - nb_to_remove);
675
760
 
676
- memcpy(buf->ptr + where, data, nb_to_insert);
761
+ memcpy(splice_loc, data, nb_to_insert);
677
762
 
678
- buf->size = buf->size + nb_to_insert - nb_to_remove;
763
+ buf->size = new_size;
679
764
  buf->ptr[buf->size] = '\0';
680
765
  return 0;
681
766
  }
@@ -36,6 +36,18 @@ GIT_INLINE(bool) git_buf_is_allocated(const git_buf *buf)
36
36
  */
37
37
  extern void git_buf_init(git_buf *buf, size_t initial_size);
38
38
 
39
+ /**
40
+ * Resize the buffer allocation to make more space.
41
+ *
42
+ * This will attempt to grow the buffer to accommodate the additional size.
43
+ * It is similar to `git_buf_grow`, but performs the new size calculation,
44
+ * checking for overflow.
45
+ *
46
+ * Like `git_buf_grow`, if this is a user-supplied buffer, this will allocate
47
+ * a new buffer.
48
+ */
49
+ extern int git_buf_grow_by(git_buf *buffer, size_t additional_size);
50
+
39
51
  /**
40
52
  * Attempt to grow the buffer to hold at least `target_size` bytes.
41
53
  *
@@ -62,6 +74,12 @@ extern void git_buf_swap(git_buf *buf_a, git_buf *buf_b);
62
74
  extern char *git_buf_detach(git_buf *buf);
63
75
  extern void git_buf_attach(git_buf *buf, char *ptr, size_t asize);
64
76
 
77
+ /* Populates a `git_buf` where the contents are not "owned" by the
78
+ * buffer, and calls to `git_buf_free` will not free the given buf.
79
+ */
80
+ extern void git_buf_attach_notowned(
81
+ git_buf *buf, const char *ptr, size_t size);
82
+
65
83
  /**
66
84
  * Test if there have been any reallocation failures with this git_buf.
67
85
  *
@@ -17,6 +17,7 @@
17
17
  #include "git2/diff.h"
18
18
  #include "git2/submodule.h"
19
19
  #include "git2/sys/index.h"
20
+ #include "git2/sys/filter.h"
20
21
 
21
22
  #include "refs.h"
22
23
  #include "repository.h"
@@ -28,6 +29,11 @@
28
29
  #include "buf_text.h"
29
30
  #include "merge_file.h"
30
31
  #include "path.h"
32
+ #include "attr.h"
33
+ #include "pool.h"
34
+ #include "strmap.h"
35
+
36
+ GIT__USE_STRMAP
31
37
 
32
38
  /* See docs/checkout-internals.md for more information */
33
39
 
@@ -68,7 +74,8 @@ typedef struct {
68
74
  size_t total_steps;
69
75
  size_t completed_steps;
70
76
  git_checkout_perfdata perfdata;
71
- git_buf last_mkdir;
77
+ git_strmap *mkdir_map;
78
+ git_attr_session attr_session;
72
79
  } checkout_data;
73
80
 
74
81
  typedef struct {
@@ -248,13 +255,13 @@ static int checkout_action_no_wd(
248
255
  error = checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL);
249
256
  if (error)
250
257
  return error;
251
- *action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, NONE);
258
+ *action = CHECKOUT_ACTION_IF(RECREATE_MISSING, UPDATE_BLOB, NONE);
252
259
  break;
253
260
  case GIT_DELTA_ADDED: /* case 2 or 28 (and 5 but not really) */
254
261
  *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
255
262
  break;
256
263
  case GIT_DELTA_MODIFIED: /* case 13 (and 35 but not really) */
257
- *action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, CONFLICT);
264
+ *action = CHECKOUT_ACTION_IF(RECREATE_MISSING, UPDATE_BLOB, CONFLICT);
258
265
  break;
259
266
  case GIT_DELTA_TYPECHANGE: /* case 21 (B->T) and 28 (T->B)*/
260
267
  if (delta->new_file.mode == GIT_FILEMODE_TREE)
@@ -1190,7 +1197,7 @@ static int checkout_verify_paths(
1190
1197
 
1191
1198
  if (action & ~CHECKOUT_ACTION__REMOVE) {
1192
1199
  if (!git_path_isvalid(repo, delta->new_file.path, flags)) {
1193
- giterr_set(GITERR_CHECKOUT, "Cannot checkout to invalid path '%s'", delta->new_file.path);
1200
+ giterr_set(GITERR_CHECKOUT, "Cannot checkout to invalid path '%s'", delta->old_file.path);
1194
1201
  return -1;
1195
1202
  }
1196
1203
  }
@@ -1291,25 +1298,6 @@ fail:
1291
1298
  return error;
1292
1299
  }
1293
1300
 
1294
- static int checkout_mkdir(
1295
- checkout_data *data,
1296
- const char *path,
1297
- const char *base,
1298
- mode_t mode,
1299
- unsigned int flags)
1300
- {
1301
- struct git_futils_mkdir_perfdata mkdir_perfdata = {0};
1302
-
1303
- int error = git_futils_mkdir_withperf(
1304
- path, base, mode, flags, &mkdir_perfdata);
1305
-
1306
- data->perfdata.mkdir_calls += mkdir_perfdata.mkdir_calls;
1307
- data->perfdata.stat_calls += mkdir_perfdata.stat_calls;
1308
- data->perfdata.chmod_calls += mkdir_perfdata.chmod_calls;
1309
-
1310
- return error;
1311
- }
1312
-
1313
1301
  static bool should_remove_existing(checkout_data *data)
1314
1302
  {
1315
1303
  int ignorecase = 0;
@@ -1325,31 +1313,43 @@ static bool should_remove_existing(checkout_data *data)
1325
1313
  #define MKDIR_REMOVE_EXISTING \
1326
1314
  MKDIR_NORMAL | GIT_MKDIR_REMOVE_FILES | GIT_MKDIR_REMOVE_SYMLINKS
1327
1315
 
1316
+ static int checkout_mkdir(
1317
+ checkout_data *data,
1318
+ const char *path,
1319
+ const char *base,
1320
+ mode_t mode,
1321
+ unsigned int flags)
1322
+ {
1323
+ struct git_futils_mkdir_options mkdir_opts = {0};
1324
+ int error;
1325
+
1326
+ mkdir_opts.dir_map = data->mkdir_map;
1327
+ mkdir_opts.pool = &data->pool;
1328
+
1329
+ error = git_futils_mkdir_ext(
1330
+ path, base, mode, flags, &mkdir_opts);
1331
+
1332
+ data->perfdata.mkdir_calls += mkdir_opts.perfdata.mkdir_calls;
1333
+ data->perfdata.stat_calls += mkdir_opts.perfdata.stat_calls;
1334
+ data->perfdata.chmod_calls += mkdir_opts.perfdata.chmod_calls;
1335
+
1336
+ return error;
1337
+ }
1338
+
1328
1339
  static int mkpath2file(
1329
1340
  checkout_data *data, const char *path, unsigned int mode)
1330
1341
  {
1331
- git_buf *mkdir_path = &data->tmp;
1332
1342
  struct stat st;
1333
1343
  bool remove_existing = should_remove_existing(data);
1344
+ unsigned int flags =
1345
+ (remove_existing ? MKDIR_REMOVE_EXISTING : MKDIR_NORMAL) |
1346
+ GIT_MKDIR_SKIP_LAST;
1334
1347
  int error;
1335
1348
 
1336
- if ((error = git_buf_sets(mkdir_path, path)) < 0)
1349
+ if ((error = checkout_mkdir(
1350
+ data, path, data->opts.target_directory, mode, flags)) < 0)
1337
1351
  return error;
1338
1352
 
1339
- git_buf_rtruncate_at_char(mkdir_path, '/');
1340
-
1341
- if (!data->last_mkdir.size ||
1342
- data->last_mkdir.size != mkdir_path->size ||
1343
- memcmp(mkdir_path->ptr, data->last_mkdir.ptr, mkdir_path->size) != 0) {
1344
-
1345
- if ((error = checkout_mkdir(
1346
- data, mkdir_path->ptr, data->opts.target_directory, mode,
1347
- remove_existing ? MKDIR_REMOVE_EXISTING : MKDIR_NORMAL)) < 0)
1348
- return error;
1349
-
1350
- git_buf_swap(&data->last_mkdir, mkdir_path);
1351
- }
1352
-
1353
1353
  if (remove_existing) {
1354
1354
  data->perfdata.stat_calls++;
1355
1355
 
@@ -1372,39 +1372,37 @@ static int mkpath2file(
1372
1372
  return error;
1373
1373
  }
1374
1374
 
1375
- static int buffer_to_file(
1376
- checkout_data *data,
1377
- struct stat *st,
1378
- git_buf *buf,
1379
- const char *path,
1380
- mode_t file_mode)
1381
- {
1382
- int error;
1383
-
1384
- if ((error = mkpath2file(data, path, data->opts.dir_mode)) < 0)
1385
- return error;
1375
+ struct checkout_stream {
1376
+ git_writestream base;
1377
+ const char *path;
1378
+ int fd;
1379
+ int open;
1380
+ };
1386
1381
 
1387
- if ((error = git_futils_writebuffer(
1388
- buf, path, data->opts.file_open_flags, file_mode)) < 0)
1389
- return error;
1382
+ static int checkout_stream_write(
1383
+ git_writestream *s, const char *buffer, size_t len)
1384
+ {
1385
+ struct checkout_stream *stream = (struct checkout_stream *)s;
1386
+ int ret;
1390
1387
 
1391
- if (st) {
1392
- data->perfdata.stat_calls++;
1388
+ if ((ret = p_write(stream->fd, buffer, len)) < 0)
1389
+ giterr_set(GITERR_OS, "Could not write to '%s'", stream->path);
1393
1390
 
1394
- if ((error = p_stat(path, st)) < 0) {
1395
- giterr_set(GITERR_OS, "Error statting '%s'", path);
1396
- return error;
1397
- }
1398
- }
1391
+ return ret;
1392
+ }
1399
1393
 
1400
- if (GIT_PERMS_IS_EXEC(file_mode)) {
1401
- data->perfdata.chmod_calls++;
1394
+ static int checkout_stream_close(git_writestream *s)
1395
+ {
1396
+ struct checkout_stream *stream = (struct checkout_stream *)s;
1397
+ assert(stream && stream->open);
1402
1398
 
1403
- if ((error = p_chmod(path, file_mode)) < 0)
1404
- giterr_set(GITERR_OS, "Failed to set permissions on '%s'", path);
1405
- }
1399
+ stream->open = 0;
1400
+ return p_close(stream->fd);
1401
+ }
1406
1402
 
1407
- return error;
1403
+ static void checkout_stream_free(git_writestream *s)
1404
+ {
1405
+ GIT_UNUSED(s);
1408
1406
  }
1409
1407
 
1410
1408
  static int blob_content_to_file(
@@ -1412,36 +1410,83 @@ static int blob_content_to_file(
1412
1410
  struct stat *st,
1413
1411
  git_blob *blob,
1414
1412
  const char *path,
1415
- const char * hint_path,
1413
+ const char *hint_path,
1416
1414
  mode_t entry_filemode)
1417
1415
  {
1416
+ int flags = data->opts.file_open_flags;
1418
1417
  mode_t file_mode = data->opts.file_mode ?
1419
1418
  data->opts.file_mode : entry_filemode;
1420
- git_buf out = GIT_BUF_INIT;
1419
+ git_filter_options filter_opts = GIT_FILTER_OPTIONS_INIT;
1420
+ struct checkout_stream writer;
1421
+ mode_t mode;
1421
1422
  git_filter_list *fl = NULL;
1423
+ int fd;
1422
1424
  int error = 0;
1423
1425
 
1424
1426
  if (hint_path == NULL)
1425
1427
  hint_path = path;
1426
1428
 
1427
- if (!data->opts.disable_filters)
1428
- error = git_filter_list_load(
1429
- &fl, git_blob_owner(blob), blob, hint_path,
1430
- GIT_FILTER_TO_WORKTREE, GIT_FILTER_OPT_DEFAULT);
1429
+ if ((error = mkpath2file(data, path, data->opts.dir_mode)) < 0)
1430
+ return error;
1431
+
1432
+ if (flags <= 0)
1433
+ flags = O_CREAT | O_TRUNC | O_WRONLY;
1434
+ if (!(mode = file_mode))
1435
+ mode = GIT_FILEMODE_BLOB;
1436
+
1437
+ if ((fd = p_open(path, flags, mode)) < 0) {
1438
+ giterr_set(GITERR_OS, "Could not open '%s' for writing", path);
1439
+ return fd;
1440
+ }
1441
+
1442
+ filter_opts.attr_session = &data->attr_session;
1443
+ filter_opts.temp_buf = &data->tmp;
1444
+
1445
+ if (!data->opts.disable_filters &&
1446
+ (error = git_filter_list__load_ext(
1447
+ &fl, data->repo, blob, hint_path,
1448
+ GIT_FILTER_TO_WORKTREE, &filter_opts)))
1449
+ return error;
1450
+
1451
+ /* setup the writer */
1452
+ memset(&writer, 0, sizeof(struct checkout_stream));
1453
+ writer.base.write = checkout_stream_write;
1454
+ writer.base.close = checkout_stream_close;
1455
+ writer.base.free = checkout_stream_free;
1456
+ writer.path = path;
1457
+ writer.fd = fd;
1458
+ writer.open = 1;
1431
1459
 
1432
- if (!error)
1433
- error = git_filter_list_apply_to_blob(&out, fl, blob);
1460
+ error = git_filter_list_stream_blob(fl, blob, (git_writestream *)&writer);
1461
+
1462
+ assert(writer.open == 0);
1434
1463
 
1435
1464
  git_filter_list_free(fl);
1436
1465
 
1437
- if (!error) {
1438
- error = buffer_to_file(data, st, &out, path, file_mode);
1439
- st->st_mode = entry_filemode;
1466
+ if (error < 0)
1467
+ return error;
1468
+
1469
+ if (GIT_PERMS_IS_EXEC(mode)) {
1470
+ data->perfdata.chmod_calls++;
1440
1471
 
1441
- git_buf_free(&out);
1472
+ if ((error = p_chmod(path, mode)) < 0) {
1473
+ giterr_set(GITERR_OS, "Failed to set permissions on '%s'", path);
1474
+ return error;
1475
+ }
1442
1476
  }
1443
1477
 
1444
- return error;
1478
+ if (st) {
1479
+ data->perfdata.stat_calls++;
1480
+
1481
+ if ((error = p_stat(path, st)) < 0) {
1482
+ giterr_set(GITERR_OS, "Error statting '%s'", path);
1483
+ return error;
1484
+ }
1485
+
1486
+ st->st_mode = entry_filemode;
1487
+ }
1488
+
1489
+ return 0;
1445
1490
  }
1446
1491
 
1447
1492
  static int blob_content_to_link(
@@ -1959,6 +2004,7 @@ static int checkout_write_merge(
1959
2004
  git_merge_file_result result = {0};
1960
2005
  git_filebuf output = GIT_FILEBUF_INIT;
1961
2006
  git_filter_list *fl = NULL;
2007
+ git_filter_options filter_opts = GIT_FILTER_OPTIONS_INIT;
1962
2008
  int error = 0;
1963
2009
 
1964
2010
  if (data->opts.checkout_strategy & GIT_CHECKOUT_CONFLICT_STYLE_DIFF3)
@@ -2008,8 +2054,12 @@ static int checkout_write_merge(
2008
2054
  in_data.ptr = (char *)result.ptr;
2009
2055
  in_data.size = result.len;
2010
2056
 
2011
- if ((error = git_filter_list_load(&fl, data->repo, NULL, git_buf_cstr(&path_workdir),
2012
- GIT_FILTER_TO_WORKTREE, GIT_FILTER_OPT_DEFAULT)) < 0 ||
2057
+ filter_opts.attr_session = &data->attr_session;
2058
+ filter_opts.temp_buf = &data->tmp;
2059
+
2060
+ if ((error = git_filter_list__load_ext(
2061
+ &fl, data->repo, NULL, git_buf_cstr(&path_workdir),
2062
+ GIT_FILTER_TO_WORKTREE, &filter_opts)) < 0 ||
2013
2063
  (error = git_filter_list_apply_to_data(&out_data, fl, &in_data)) < 0)
2014
2064
  goto done;
2015
2065
  } else {
@@ -2212,12 +2262,17 @@ static void checkout_data_clear(checkout_data *data)
2212
2262
  git__free(data->pfx);
2213
2263
  data->pfx = NULL;
2214
2264
 
2215
- git_buf_free(&data->last_mkdir);
2265
+ git_strmap_free(data->mkdir_map);
2266
+
2216
2267
  git_buf_free(&data->path);
2217
2268
  git_buf_free(&data->tmp);
2218
2269
 
2219
2270
  git_index_free(data->index);
2220
2271
  data->index = NULL;
2272
+
2273
+ git_strmap_free(data->mkdir_map);
2274
+
2275
+ git_attr_session__free(&data->attr_session);
2221
2276
  }
2222
2277
 
2223
2278
  static int checkout_data_init(
@@ -2291,11 +2346,17 @@ static int checkout_data_init(
2291
2346
  }
2292
2347
  }
2293
2348
 
2294
- /* if you are forcing, definitely allow safe updates */
2349
+ /* if you are forcing, allow all safe updates, plus recreate missing */
2295
2350
  if ((data->opts.checkout_strategy & GIT_CHECKOUT_FORCE) != 0)
2296
- data->opts.checkout_strategy |= GIT_CHECKOUT_SAFE_CREATE;
2297
- if ((data->opts.checkout_strategy & GIT_CHECKOUT_SAFE_CREATE) != 0)
2298
- data->opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
2351
+ data->opts.checkout_strategy |= GIT_CHECKOUT_SAFE |
2352
+ GIT_CHECKOUT_RECREATE_MISSING;
2353
+
2354
+ /* if the repository does not actually have an index file, then this
2355
+ * is an initial checkout (perhaps from clone), so we allow safe updates
2356
+ */
2357
+ if (!data->index->on_disk &&
2358
+ (data->opts.checkout_strategy & GIT_CHECKOUT_SAFE) != 0)
2359
+ data->opts.checkout_strategy |= GIT_CHECKOUT_RECREATE_MISSING;
2299
2360
 
2300
2361
  data->strategy = data->opts.checkout_strategy;
2301
2362
 
@@ -2329,25 +2390,27 @@ static int checkout_data_init(
2329
2390
 
2330
2391
  if ((data->opts.checkout_strategy &
2331
2392
  (GIT_CHECKOUT_CONFLICT_STYLE_MERGE | GIT_CHECKOUT_CONFLICT_STYLE_DIFF3)) == 0) {
2332
- const char *conflict_style;
2393
+ git_config_entry *conflict_style = NULL;
2333
2394
  git_config *cfg = NULL;
2334
2395
 
2335
2396
  if ((error = git_repository_config__weakptr(&cfg, repo)) < 0 ||
2336
- (error = git_config_get_string(&conflict_style, cfg, "merge.conflictstyle")) < 0 ||
2397
+ (error = git_config_get_entry(&conflict_style, cfg, "merge.conflictstyle")) < 0 ||
2337
2398
  error == GIT_ENOTFOUND)
2338
2399
  ;
2339
2400
  else if (error)
2340
2401
  goto cleanup;
2341
- else if (strcmp(conflict_style, "merge") == 0)
2402
+ else if (strcmp(conflict_style->value, "merge") == 0)
2342
2403
  data->opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_MERGE;
2343
- else if (strcmp(conflict_style, "diff3") == 0)
2404
+ else if (strcmp(conflict_style->value, "diff3") == 0)
2344
2405
  data->opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_DIFF3;
2345
2406
  else {
2346
2407
  giterr_set(GITERR_CHECKOUT, "unknown style '%s' given for 'merge.conflictstyle'",
2347
2408
  conflict_style);
2348
2409
  error = -1;
2410
+ git_config_entry_free(conflict_style);
2349
2411
  goto cleanup;
2350
2412
  }
2413
+ git_config_entry_free(conflict_style);
2351
2414
  }
2352
2415
 
2353
2416
  if ((error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 ||
@@ -2355,11 +2418,14 @@ static int checkout_data_init(
2355
2418
  (error = git_vector_init(&data->update_conflicts, 0, NULL)) < 0 ||
2356
2419
  (error = git_pool_init(&data->pool, 1, 0)) < 0 ||
2357
2420
  (error = git_buf_puts(&data->path, data->opts.target_directory)) < 0 ||
2358
- (error = git_path_to_dir(&data->path)) < 0)
2421
+ (error = git_path_to_dir(&data->path)) < 0 ||
2422
+ (error = git_strmap_alloc(&data->mkdir_map)) < 0)
2359
2423
  goto cleanup;
2360
2424
 
2361
2425
  data->workdir_len = git_buf_len(&data->path);
2362
2426
 
2427
+ git_attr_session__init(&data->attr_session, data->repo);
2428
+
2363
2429
  cleanup:
2364
2430
  if (error < 0)
2365
2431
  checkout_data_clear(data);
@@ -2367,6 +2433,9 @@ cleanup:
2367
2433
  return error;
2368
2434
  }
2369
2435
 
2436
+ #define CHECKOUT_INDEX_DONT_WRITE_MASK \
2437
+ (GIT_CHECKOUT_DONT_UPDATE_INDEX | GIT_CHECKOUT_DONT_WRITE_INDEX)
2438
+
2370
2439
  int git_checkout_iterator(
2371
2440
  git_iterator *target,
2372
2441
  git_index *index,
@@ -2468,9 +2537,12 @@ int git_checkout_iterator(
2468
2537
 
2469
2538
  assert(data.completed_steps == data.total_steps);
2470
2539
 
2540
+ if (data.opts.perfdata_cb)
2541
+ data.opts.perfdata_cb(&data.perfdata, data.opts.perfdata_payload);
2542
+
2471
2543
  cleanup:
2472
2544
  if (!error && data.index != NULL &&
2473
- (data.strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
2545
+ (data.strategy & CHECKOUT_INDEX_DONT_WRITE_MASK) == 0)
2474
2546
  error = git_index_write(data.index);
2475
2547
 
2476
2548
  git_diff_free(data.diff);