rugged 0.25.0b2 → 0.25.0b3

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 (99) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/ext/rugged/extconf.rb +3 -1
  4. data/ext/rugged/rugged.c +1 -1
  5. data/ext/rugged/rugged.h +1 -1
  6. data/ext/rugged/rugged_blob.c +29 -38
  7. data/ext/rugged/rugged_commit.c +215 -78
  8. data/ext/rugged/rugged_rebase.c +18 -11
  9. data/ext/rugged/rugged_remote.c +2 -2
  10. data/ext/rugged/rugged_tree.c +132 -0
  11. data/lib/rugged/version.rb +1 -1
  12. data/vendor/libgit2/CMakeLists.txt +11 -3
  13. data/vendor/libgit2/include/git2.h +1 -0
  14. data/vendor/libgit2/include/git2/blob.h +39 -28
  15. data/vendor/libgit2/include/git2/commit.h +30 -0
  16. data/vendor/libgit2/include/git2/common.h +16 -1
  17. data/vendor/libgit2/include/git2/merge.h +10 -1
  18. data/vendor/libgit2/include/git2/proxy.h +92 -0
  19. data/vendor/libgit2/include/git2/refs.h +11 -0
  20. data/vendor/libgit2/include/git2/remote.h +17 -4
  21. data/vendor/libgit2/include/git2/signature.h +13 -0
  22. data/vendor/libgit2/include/git2/sys/merge.h +177 -0
  23. data/vendor/libgit2/include/git2/sys/remote.h +16 -0
  24. data/vendor/libgit2/include/git2/sys/stream.h +2 -1
  25. data/vendor/libgit2/include/git2/sys/transport.h +3 -1
  26. data/vendor/libgit2/include/git2/tag.h +9 -0
  27. data/vendor/libgit2/include/git2/tree.h +55 -0
  28. data/vendor/libgit2/src/annotated_commit.c +99 -80
  29. data/vendor/libgit2/src/annotated_commit.h +5 -2
  30. data/vendor/libgit2/src/array.h +40 -0
  31. data/vendor/libgit2/src/blame.c +8 -3
  32. data/vendor/libgit2/src/blame_git.c +2 -1
  33. data/vendor/libgit2/src/blob.c +71 -39
  34. data/vendor/libgit2/src/branch.c +2 -1
  35. data/vendor/libgit2/src/checkout.c +66 -42
  36. data/vendor/libgit2/src/commit.c +67 -3
  37. data/vendor/libgit2/src/config_cache.c +2 -1
  38. data/vendor/libgit2/src/config_file.c +32 -27
  39. data/vendor/libgit2/src/curl_stream.c +89 -6
  40. data/vendor/libgit2/src/delta-apply.c +36 -5
  41. data/vendor/libgit2/src/delta-apply.h +12 -0
  42. data/vendor/libgit2/src/describe.c +3 -2
  43. data/vendor/libgit2/src/diff.c +13 -20
  44. data/vendor/libgit2/src/diff_tform.c +5 -3
  45. data/vendor/libgit2/src/filebuf.c +12 -2
  46. data/vendor/libgit2/src/filebuf.h +1 -0
  47. data/vendor/libgit2/src/fnmatch.c +18 -5
  48. data/vendor/libgit2/src/global.c +18 -0
  49. data/vendor/libgit2/src/global.h +1 -0
  50. data/vendor/libgit2/src/ignore.c +11 -3
  51. data/vendor/libgit2/src/index.c +11 -5
  52. data/vendor/libgit2/src/indexer.c +11 -7
  53. data/vendor/libgit2/src/iterator.c +1575 -1468
  54. data/vendor/libgit2/src/iterator.h +52 -69
  55. data/vendor/libgit2/src/merge.c +160 -63
  56. data/vendor/libgit2/src/merge.h +61 -2
  57. data/vendor/libgit2/src/merge_driver.c +397 -0
  58. data/vendor/libgit2/src/merge_driver.h +60 -0
  59. data/vendor/libgit2/src/merge_file.c +11 -49
  60. data/vendor/libgit2/src/netops.c +12 -10
  61. data/vendor/libgit2/src/object.c +3 -6
  62. data/vendor/libgit2/src/object_api.c +19 -1
  63. data/vendor/libgit2/src/odb_loose.c +1 -1
  64. data/vendor/libgit2/src/openssl_stream.c +16 -3
  65. data/vendor/libgit2/src/pack-objects.c +3 -1
  66. data/vendor/libgit2/src/pack.c +5 -9
  67. data/vendor/libgit2/src/path.c +14 -0
  68. data/vendor/libgit2/src/path.h +12 -0
  69. data/vendor/libgit2/src/pathspec.c +1 -1
  70. data/vendor/libgit2/src/posix.c +7 -0
  71. data/vendor/libgit2/src/posix.h +1 -0
  72. data/vendor/libgit2/src/proxy.c +32 -0
  73. data/vendor/libgit2/src/proxy.h +14 -0
  74. data/vendor/libgit2/src/push.c +7 -7
  75. data/vendor/libgit2/src/rebase.c +61 -31
  76. data/vendor/libgit2/src/refdb_fs.c +1 -0
  77. data/vendor/libgit2/src/refs.c +16 -1
  78. data/vendor/libgit2/src/remote.c +20 -6
  79. data/vendor/libgit2/src/repository.c +1 -1
  80. data/vendor/libgit2/src/reset.c +1 -1
  81. data/vendor/libgit2/src/settings.c +23 -1
  82. data/vendor/libgit2/src/signature.c +26 -1
  83. data/vendor/libgit2/src/stransport_stream.c +5 -2
  84. data/vendor/libgit2/src/stream.h +2 -2
  85. data/vendor/libgit2/src/submodule.c +3 -2
  86. data/vendor/libgit2/src/tag.c +8 -2
  87. data/vendor/libgit2/src/transports/http.c +32 -9
  88. data/vendor/libgit2/src/transports/local.c +4 -1
  89. data/vendor/libgit2/src/transports/smart.c +6 -0
  90. data/vendor/libgit2/src/transports/smart.h +1 -0
  91. data/vendor/libgit2/src/transports/smart_protocol.c +61 -17
  92. data/vendor/libgit2/src/transports/winhttp.c +130 -11
  93. data/vendor/libgit2/src/tree.c +329 -98
  94. data/vendor/libgit2/src/tree.h +4 -5
  95. data/vendor/libgit2/src/unix/map.c +5 -0
  96. data/vendor/libgit2/src/win32/map.c +24 -5
  97. data/vendor/libgit2/src/xdiff/xprepare.c +2 -1
  98. metadata +10 -4
  99. data/vendor/libgit2/Makefile.embed +0 -60
@@ -85,9 +85,10 @@ int git_tree_entry_icmp(const git_tree_entry *e1, const git_tree_entry *e2)
85
85
  }
86
86
 
87
87
  /**
88
- * Allocate either from the pool or from the system allocator
88
+ * Allocate a new self-contained entry, with enough space after it to
89
+ * store the filename and the id.
89
90
  */
90
- static git_tree_entry *alloc_entry_base(git_pool *pool, const char *filename, size_t filename_len)
91
+ static git_tree_entry *alloc_entry(const char *filename, size_t filename_len, const git_oid *id)
91
92
  {
92
93
  git_tree_entry *entry = NULL;
93
94
  size_t tree_len;
@@ -95,44 +96,32 @@ static git_tree_entry *alloc_entry_base(git_pool *pool, const char *filename, si
95
96
  TREE_ENTRY_CHECK_NAMELEN(filename_len);
96
97
 
97
98
  if (GIT_ADD_SIZET_OVERFLOW(&tree_len, sizeof(git_tree_entry), filename_len) ||
98
- GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, 1))
99
+ GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, 1) ||
100
+ GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, GIT_OID_RAWSZ))
99
101
  return NULL;
100
102
 
101
- entry = pool ? git_pool_malloc(pool, tree_len) :
102
- git__malloc(tree_len);
103
+ entry = git__calloc(1, tree_len);
103
104
  if (!entry)
104
105
  return NULL;
105
106
 
106
- memset(entry, 0x0, sizeof(git_tree_entry));
107
- memcpy(entry->filename, filename, filename_len);
108
- entry->filename[filename_len] = 0;
109
- entry->filename_len = (uint16_t)filename_len;
110
-
111
- return entry;
112
- }
107
+ {
108
+ char *filename_ptr;
109
+ void *id_ptr;
113
110
 
114
- /**
115
- * Allocate a tree entry, using the poolin the tree which owns
116
- * it. This is useful when reading trees, so we don't allocate a ton
117
- * of small strings but can use the pool.
118
- */
119
- static git_tree_entry *alloc_entry_pooled(git_pool *pool, const char *filename, size_t filename_len)
120
- {
121
- git_tree_entry *entry = NULL;
111
+ filename_ptr = ((char *) entry) + sizeof(git_tree_entry);
112
+ memcpy(filename_ptr, filename, filename_len);
113
+ entry->filename = filename_ptr;
122
114
 
123
- if (!(entry = alloc_entry_base(pool, filename, filename_len)))
124
- return NULL;
115
+ id_ptr = filename_ptr + filename_len + 1;
116
+ git_oid_cpy(id_ptr, id);
117
+ entry->oid = id_ptr;
118
+ }
125
119
 
126
- entry->pooled = true;
120
+ entry->filename_len = (uint16_t)filename_len;
127
121
 
128
122
  return entry;
129
123
  }
130
124
 
131
- static git_tree_entry *alloc_entry(const char *filename)
132
- {
133
- return alloc_entry_base(NULL, filename, strlen(filename));
134
- }
135
-
136
125
  struct tree_key_search {
137
126
  const char *filename;
138
127
  uint16_t filename_len;
@@ -174,7 +163,10 @@ static int homing_search_cmp(const void *key, const void *array_member)
174
163
  * around the area for our target file.
175
164
  */
176
165
  static int tree_key_search(
177
- size_t *at_pos, git_vector *entries, const char *filename, size_t filename_len)
166
+ size_t *at_pos,
167
+ const git_tree *tree,
168
+ const char *filename,
169
+ size_t filename_len)
178
170
  {
179
171
  struct tree_key_search ksearch;
180
172
  const git_tree_entry *entry;
@@ -187,13 +179,15 @@ static int tree_key_search(
187
179
 
188
180
  /* Initial homing search; find an entry on the tree with
189
181
  * the same prefix as the filename we're looking for */
190
- if (git_vector_bsearch2(&homing, entries, &homing_search_cmp, &ksearch) < 0)
182
+
183
+ if (git_array_search(&homing,
184
+ tree->entries, &homing_search_cmp, &ksearch) < 0)
191
185
  return GIT_ENOTFOUND; /* just a signal error; not passed back to user */
192
186
 
193
187
  /* We found a common prefix. Look forward as long as
194
188
  * there are entries that share the common prefix */
195
- for (i = homing; i < entries->length; ++i) {
196
- entry = entries->contents[i];
189
+ for (i = homing; i < tree->entries.size; ++i) {
190
+ entry = git_array_get(tree->entries, i);
197
191
 
198
192
  if (homing_search_cmp(&ksearch, entry) < 0)
199
193
  break;
@@ -213,7 +207,7 @@ static int tree_key_search(
213
207
  i = homing - 1;
214
208
 
215
209
  do {
216
- entry = entries->contents[i];
210
+ entry = git_array_get(tree->entries, i);
217
211
 
218
212
  if (homing_search_cmp(&ksearch, entry) > 0)
219
213
  break;
@@ -234,7 +228,7 @@ static int tree_key_search(
234
228
 
235
229
  void git_tree_entry_free(git_tree_entry *entry)
236
230
  {
237
- if (entry == NULL || entry->pooled)
231
+ if (entry == NULL)
238
232
  return;
239
233
 
240
234
  git__free(entry);
@@ -242,36 +236,26 @@ void git_tree_entry_free(git_tree_entry *entry)
242
236
 
243
237
  int git_tree_entry_dup(git_tree_entry **dest, const git_tree_entry *source)
244
238
  {
245
- size_t total_size;
246
- git_tree_entry *copy;
239
+ git_tree_entry *cpy;
247
240
 
248
241
  assert(source);
249
242
 
250
- GITERR_CHECK_ALLOC_ADD(&total_size, sizeof(git_tree_entry), source->filename_len);
251
- GITERR_CHECK_ALLOC_ADD(&total_size, total_size, 1);
252
-
253
- copy = git__malloc(total_size);
254
- GITERR_CHECK_ALLOC(copy);
255
-
256
- memcpy(copy, source, total_size);
243
+ cpy = alloc_entry(source->filename, source->filename_len, source->oid);
244
+ if (cpy == NULL)
245
+ return -1;
257
246
 
258
- copy->pooled = 0;
247
+ cpy->attr = source->attr;
259
248
 
260
- *dest = copy;
249
+ *dest = cpy;
261
250
  return 0;
262
251
  }
263
252
 
264
253
  void git_tree__free(void *_tree)
265
254
  {
266
255
  git_tree *tree = _tree;
267
- size_t i;
268
- git_tree_entry *e;
269
-
270
- git_vector_foreach(&tree->entries, i, e)
271
- git_tree_entry_free(e);
272
256
 
273
- git_vector_free(&tree->entries);
274
- git_pool_clear(&tree->pool);
257
+ git_odb_object_free(tree->odb_obj);
258
+ git_array_clear(tree->entries);
275
259
  git__free(tree);
276
260
  }
277
261
 
@@ -294,7 +278,7 @@ const char *git_tree_entry_name(const git_tree_entry *entry)
294
278
  const git_oid *git_tree_entry_id(const git_tree_entry *entry)
295
279
  {
296
280
  assert(entry);
297
- return &entry->oid;
281
+ return entry->oid;
298
282
  }
299
283
 
300
284
  git_otype git_tree_entry_type(const git_tree_entry *entry)
@@ -315,7 +299,7 @@ int git_tree_entry_to_object(
315
299
  const git_tree_entry *entry)
316
300
  {
317
301
  assert(entry && object_out);
318
- return git_object_lookup(object_out, repo, &entry->oid, GIT_OBJ_ANY);
302
+ return git_object_lookup(object_out, repo, entry->oid, GIT_OBJ_ANY);
319
303
  }
320
304
 
321
305
  static const git_tree_entry *entry_fromname(
@@ -323,13 +307,10 @@ static const git_tree_entry *entry_fromname(
323
307
  {
324
308
  size_t idx;
325
309
 
326
- /* be safe when we cast away constness - i.e. don't trigger a sort */
327
- assert(git_vector_is_sorted(&tree->entries));
328
-
329
- if (tree_key_search(&idx, (git_vector *)&tree->entries, name, name_len) < 0)
310
+ if (tree_key_search(&idx, tree, name, name_len) < 0)
330
311
  return NULL;
331
312
 
332
- return git_vector_get(&tree->entries, idx);
313
+ return git_array_get(tree->entries, idx);
333
314
  }
334
315
 
335
316
  const git_tree_entry *git_tree_entry_byname(
@@ -344,7 +325,7 @@ const git_tree_entry *git_tree_entry_byindex(
344
325
  const git_tree *tree, size_t idx)
345
326
  {
346
327
  assert(tree);
347
- return git_vector_get(&tree->entries, idx);
328
+ return git_array_get(tree->entries, idx);
348
329
  }
349
330
 
350
331
  const git_tree_entry *git_tree_entry_byid(
@@ -355,8 +336,8 @@ const git_tree_entry *git_tree_entry_byid(
355
336
 
356
337
  assert(tree);
357
338
 
358
- git_vector_foreach(&tree->entries, i, e) {
359
- if (memcmp(&e->oid.id, &id->id, sizeof(id->id)) == 0)
339
+ git_array_foreach(tree->entries, i, e) {
340
+ if (memcmp(&e->oid->id, &id->id, sizeof(id->id)) == 0)
360
341
  return e;
361
342
  }
362
343
 
@@ -365,7 +346,6 @@ const git_tree_entry *git_tree_entry_byid(
365
346
 
366
347
  int git_tree__prefix_position(const git_tree *tree, const char *path)
367
348
  {
368
- const git_vector *entries = &tree->entries;
369
349
  struct tree_key_search ksearch;
370
350
  size_t at_pos, path_len;
371
351
 
@@ -378,21 +358,20 @@ int git_tree__prefix_position(const git_tree *tree, const char *path)
378
358
  ksearch.filename = path;
379
359
  ksearch.filename_len = (uint16_t)path_len;
380
360
 
381
- /* be safe when we cast away constness - i.e. don't trigger a sort */
382
- assert(git_vector_is_sorted(&tree->entries));
383
-
384
361
  /* Find tree entry with appropriate prefix */
385
- git_vector_bsearch2(
386
- &at_pos, (git_vector *)entries, &homing_search_cmp, &ksearch);
362
+ git_array_search(
363
+ &at_pos, tree->entries, &homing_search_cmp, &ksearch);
387
364
 
388
- for (; at_pos < entries->length; ++at_pos) {
389
- const git_tree_entry *entry = entries->contents[at_pos];
365
+ for (; at_pos < tree->entries.size; ++at_pos) {
366
+ const git_tree_entry *entry = git_array_get(tree->entries, at_pos);
390
367
  if (homing_search_cmp(&ksearch, entry) < 0)
391
368
  break;
392
369
  }
393
370
 
394
371
  for (; at_pos > 0; --at_pos) {
395
- const git_tree_entry *entry = entries->contents[at_pos - 1];
372
+ const git_tree_entry *entry =
373
+ git_array_get(tree->entries, at_pos - 1);
374
+
396
375
  if (homing_search_cmp(&ksearch, entry) > 0)
397
376
  break;
398
377
  }
@@ -403,7 +382,7 @@ int git_tree__prefix_position(const git_tree *tree, const char *path)
403
382
  size_t git_tree_entrycount(const git_tree *tree)
404
383
  {
405
384
  assert(tree);
406
- return tree->entries.length;
385
+ return tree->entries.size;
407
386
  }
408
387
 
409
388
  unsigned int git_treebuilder_entrycount(git_treebuilder *bld)
@@ -444,13 +423,18 @@ static int parse_mode(unsigned int *modep, const char *buffer, const char **buff
444
423
  int git_tree__parse(void *_tree, git_odb_object *odb_obj)
445
424
  {
446
425
  git_tree *tree = _tree;
447
- const char *buffer = git_odb_object_data(odb_obj);
448
- const char *buffer_end = buffer + git_odb_object_size(odb_obj);
426
+ const char *buffer;
427
+ const char *buffer_end;
449
428
 
450
- git_pool_init(&tree->pool, 1);
451
- if (git_vector_init(&tree->entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < 0)
429
+ if (git_odb_object_dup(&tree->odb_obj, odb_obj) < 0)
452
430
  return -1;
453
431
 
432
+ buffer = git_odb_object_data(tree->odb_obj);
433
+ buffer_end = buffer + git_odb_object_size(tree->odb_obj);
434
+
435
+ git_array_init_to_size(tree->entries, DEFAULT_TREE_SIZE);
436
+ GITERR_CHECK_ARRAY(tree->entries);
437
+
454
438
  while (buffer < buffer_end) {
455
439
  git_tree_entry *entry;
456
440
  size_t filename_len;
@@ -464,27 +448,21 @@ int git_tree__parse(void *_tree, git_odb_object *odb_obj)
464
448
  return tree_error("Failed to parse tree. Object is corrupted", NULL);
465
449
 
466
450
  filename_len = nul - buffer;
467
- /** Allocate the entry and store it in the entries vector */
451
+ /* Allocate the entry */
468
452
  {
469
- entry = alloc_entry_pooled(&tree->pool, buffer, filename_len);
453
+ entry = git_array_alloc(tree->entries);
470
454
  GITERR_CHECK_ALLOC(entry);
471
455
 
472
- if (git_vector_insert(&tree->entries, entry) < 0)
473
- return -1;
474
-
475
456
  entry->attr = attr;
457
+ entry->filename_len = filename_len;
458
+ entry->filename = buffer;
459
+ entry->oid = (git_oid *) ((char *) buffer + filename_len + 1);
476
460
  }
477
461
 
478
- /* Advance to the ID just after the path */
479
462
  buffer += filename_len + 1;
480
-
481
- git_oid_fromraw(&entry->oid, (const unsigned char *)buffer);
482
463
  buffer += GIT_OID_RAWSZ;
483
464
  }
484
465
 
485
- /* The tree is sorted by definition. Bad inputs give bad outputs */
486
- tree->entries.flags |= GIT_VECTOR_SORTED;
487
-
488
466
  return 0;
489
467
  }
490
468
 
@@ -517,10 +495,9 @@ static int append_entry(
517
495
  if (!valid_entry_name(bld->repo, filename))
518
496
  return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
519
497
 
520
- entry = alloc_entry(filename);
498
+ entry = alloc_entry(filename, strlen(filename), id);
521
499
  GITERR_CHECK_ALLOC(entry);
522
500
 
523
- git_oid_cpy(&entry->oid, id);
524
501
  entry->attr = (uint16_t)filemode;
525
502
 
526
503
  git_strmap_insert(bld->map, entry->filename, entry, error);
@@ -709,10 +686,10 @@ int git_treebuilder_new(
709
686
  if (source != NULL) {
710
687
  git_tree_entry *entry_src;
711
688
 
712
- git_vector_foreach(&source->entries, i, entry_src) {
689
+ git_array_foreach(source->entries, i, entry_src) {
713
690
  if (append_entry(
714
691
  bld, entry_src->filename,
715
- &entry_src->oid,
692
+ entry_src->oid,
716
693
  entry_src->attr) < 0)
717
694
  goto on_error;
718
695
  }
@@ -764,8 +741,9 @@ int git_treebuilder_insert(
764
741
  pos = git_strmap_lookup_index(bld->map, filename);
765
742
  if (git_strmap_valid_index(bld->map, pos)) {
766
743
  entry = git_strmap_value_at(bld->map, pos);
744
+ git_oid_cpy((git_oid *) entry->oid, id);
767
745
  } else {
768
- entry = alloc_entry(filename);
746
+ entry = alloc_entry(filename, strlen(filename), id);
769
747
  GITERR_CHECK_ALLOC(entry);
770
748
 
771
749
  git_strmap_insert(bld->map, entry->filename, entry, error);
@@ -777,7 +755,6 @@ int git_treebuilder_insert(
777
755
  }
778
756
  }
779
757
 
780
- git_oid_cpy(&entry->oid, id);
781
758
  entry->attr = filemode;
782
759
 
783
760
  if (entry_out)
@@ -848,19 +825,20 @@ int git_treebuilder_write(git_oid *oid, git_treebuilder *bld)
848
825
 
849
826
  git_buf_printf(&tree, "%o ", entry->attr);
850
827
  git_buf_put(&tree, entry->filename, entry->filename_len + 1);
851
- git_buf_put(&tree, (char *)entry->oid.id, GIT_OID_RAWSZ);
828
+ git_buf_put(&tree, (char *)entry->oid->id, GIT_OID_RAWSZ);
852
829
 
853
830
  if (git_buf_oom(&tree))
854
831
  error = -1;
855
832
  }
856
833
 
857
- git_vector_free(&entries);
858
834
 
859
835
  if (!error &&
860
836
  !(error = git_repository_odb__weakptr(&odb, bld->repo)))
861
837
  error = git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE);
862
838
 
863
839
  git_buf_free(&tree);
840
+ git_vector_free(&entries);
841
+
864
842
  return error;
865
843
  }
866
844
 
@@ -960,7 +938,7 @@ int git_tree_entry_bypath(
960
938
  return git_tree_entry_dup(entry_out, entry);
961
939
  }
962
940
 
963
- if (git_tree_lookup(&subtree, root->object.repo, &entry->oid) < 0)
941
+ if (git_tree_lookup(&subtree, root->object.repo, entry->oid) < 0)
964
942
  return -1;
965
943
 
966
944
  error = git_tree_entry_bypath(
@@ -984,7 +962,7 @@ static int tree_walk(
984
962
  size_t i;
985
963
  const git_tree_entry *entry;
986
964
 
987
- git_vector_foreach(&tree->entries, i, entry) {
965
+ git_array_foreach(tree->entries, i, entry) {
988
966
  if (preorder) {
989
967
  error = callback(path->ptr, entry, payload);
990
968
  if (error < 0) { /* negative value stops iteration */
@@ -1001,7 +979,7 @@ static int tree_walk(
1001
979
  git_tree *subtree;
1002
980
  size_t path_len = git_buf_len(path);
1003
981
 
1004
- error = git_tree_lookup(&subtree, tree->object.repo, &entry->oid);
982
+ error = git_tree_lookup(&subtree, tree->object.repo, entry->oid);
1005
983
  if (error < 0)
1006
984
  break;
1007
985
 
@@ -1056,3 +1034,256 @@ int git_tree_walk(
1056
1034
  return error;
1057
1035
  }
1058
1036
 
1037
+ static int compare_entries(const void *_a, const void *_b)
1038
+ {
1039
+ const git_tree_update *a = (git_tree_update *) _a;
1040
+ const git_tree_update *b = (git_tree_update *) _b;
1041
+
1042
+ return strcmp(a->path, b->path);
1043
+ }
1044
+
1045
+ static int on_dup_entry(void **old, void *new)
1046
+ {
1047
+ GIT_UNUSED(old); GIT_UNUSED(new);
1048
+
1049
+ giterr_set(GITERR_TREE, "duplicate entries given for update");
1050
+ return -1;
1051
+ }
1052
+
1053
+ /*
1054
+ * We keep the previous tree and the new one at each level of the
1055
+ * stack. When we leave a level we're done with that tree and we can
1056
+ * write it out to the odb.
1057
+ */
1058
+ typedef struct {
1059
+ git_treebuilder *bld;
1060
+ git_tree *tree;
1061
+ char *name;
1062
+ } tree_stack_entry;
1063
+
1064
+ /** Count how many slashes (i.e. path components) there are in this string */
1065
+ GIT_INLINE(size_t) count_slashes(const char *path)
1066
+ {
1067
+ size_t count = 0;
1068
+ const char *slash;
1069
+
1070
+ while ((slash = strchr(path, '/')) != NULL) {
1071
+ count++;
1072
+ path = slash + 1;
1073
+ }
1074
+
1075
+ return count;
1076
+ }
1077
+
1078
+ static bool next_component(git_buf *out, const char *in)
1079
+ {
1080
+ const char *slash = strchr(in, '/');
1081
+
1082
+ git_buf_clear(out);
1083
+
1084
+ if (slash)
1085
+ git_buf_put(out, in, slash - in);
1086
+
1087
+ return !!slash;
1088
+ }
1089
+
1090
+ static int create_popped_tree(tree_stack_entry *current, tree_stack_entry *popped, git_buf *component)
1091
+ {
1092
+ int error;
1093
+ git_oid new_tree;
1094
+
1095
+ git_tree_free(popped->tree);
1096
+ error = git_treebuilder_write(&new_tree, popped->bld);
1097
+ git_treebuilder_free(popped->bld);
1098
+
1099
+ if (error < 0) {
1100
+ git__free(popped->name);
1101
+ return error;
1102
+ }
1103
+
1104
+ /* We've written out the tree, now we have to put the new value into its parent */
1105
+ git_buf_clear(component);
1106
+ git_buf_puts(component, popped->name);
1107
+ git__free(popped->name);
1108
+
1109
+ GITERR_CHECK_ALLOC(component->ptr);
1110
+
1111
+ /* Error out if this would create a D/F conflict in this update */
1112
+ if (current->tree) {
1113
+ const git_tree_entry *to_replace;
1114
+ to_replace = git_tree_entry_byname(current->tree, component->ptr);
1115
+ if (to_replace && git_tree_entry_type(to_replace) != GIT_OBJ_TREE) {
1116
+ giterr_set(GITERR_TREE, "D/F conflict when updating tree");
1117
+ return -1;
1118
+ }
1119
+ }
1120
+
1121
+ return git_treebuilder_insert(NULL, current->bld, component->ptr, &new_tree, GIT_FILEMODE_TREE);
1122
+ }
1123
+
1124
+ int git_tree_create_updated(git_oid *out, git_repository *repo, git_tree *baseline, size_t nupdates, const git_tree_update *updates)
1125
+ {
1126
+ git_array_t(tree_stack_entry) stack = GIT_ARRAY_INIT;
1127
+ tree_stack_entry *root_elem;
1128
+ git_vector entries;
1129
+ int error;
1130
+ size_t i;
1131
+ git_buf component = GIT_BUF_INIT;
1132
+
1133
+ if ((error = git_vector_init(&entries, nupdates, compare_entries)) < 0)
1134
+ return error;
1135
+
1136
+ /* Sort the entries for treversal */
1137
+ for (i = 0 ; i < nupdates; i++) {
1138
+ if ((error = git_vector_insert_sorted(&entries, (void *) &updates[i], on_dup_entry)) < 0)
1139
+ goto cleanup;
1140
+ }
1141
+
1142
+ root_elem = git_array_alloc(stack);
1143
+ GITERR_CHECK_ALLOC(root_elem);
1144
+ memset(root_elem, 0, sizeof(*root_elem));
1145
+
1146
+ if (baseline && (error = git_tree_dup(&root_elem->tree, baseline)) < 0)
1147
+ goto cleanup;
1148
+
1149
+ if ((error = git_treebuilder_new(&root_elem->bld, repo, root_elem->tree)) < 0)
1150
+ goto cleanup;
1151
+
1152
+ for (i = 0; i < nupdates; i++) {
1153
+ const git_tree_update *last_update = i == 0 ? NULL : &updates[i-1];
1154
+ const git_tree_update *update = &updates[i];
1155
+ size_t common_prefix = 0, steps_up, j;
1156
+ const char *path;
1157
+
1158
+ /* Figure out how much we need to change from the previous tree */
1159
+ if (last_update)
1160
+ common_prefix = git_path_common_dirlen(last_update->path, update->path);
1161
+
1162
+ /*
1163
+ * The entries are sorted, so when we find we're no
1164
+ * longer in the same directory, we need to abandon
1165
+ * the old tree (steps up) and dive down to the next
1166
+ * one.
1167
+ */
1168
+ steps_up = last_update == NULL ? 0 : count_slashes(&last_update->path[common_prefix]);
1169
+
1170
+ for (j = 0; j < steps_up; j++) {
1171
+ tree_stack_entry *current, *popped = git_array_pop(stack);
1172
+ assert(popped);
1173
+
1174
+ current = git_array_last(stack);
1175
+ assert(current);
1176
+
1177
+ if ((error = create_popped_tree(current, popped, &component)) < 0)
1178
+ goto cleanup;
1179
+ }
1180
+
1181
+ /* Now that we've created the trees we popped from the stack, let's go back down */
1182
+ path = &update->path[common_prefix];
1183
+ while (next_component(&component, path)) {
1184
+ tree_stack_entry *last, *new_entry;
1185
+ const git_tree_entry *entry;
1186
+
1187
+ last = git_array_last(stack);
1188
+ entry = last->tree ? git_tree_entry_byname(last->tree, component.ptr) : NULL;
1189
+ if (entry && git_tree_entry_type(entry) != GIT_OBJ_TREE) {
1190
+ giterr_set(GITERR_TREE, "D/F conflict when updating tree");
1191
+ error = -1;
1192
+ goto cleanup;
1193
+ }
1194
+
1195
+ new_entry = git_array_alloc(stack);
1196
+ GITERR_CHECK_ALLOC(new_entry);
1197
+ memset(new_entry, 0, sizeof(*new_entry));
1198
+
1199
+ new_entry->tree = NULL;
1200
+ if (entry && (error = git_tree_lookup(&new_entry->tree, repo, git_tree_entry_id(entry))) < 0)
1201
+ goto cleanup;
1202
+
1203
+ if ((error = git_treebuilder_new(&new_entry->bld, repo, new_entry->tree)) < 0)
1204
+ goto cleanup;
1205
+
1206
+ new_entry->name = git__strdup(component.ptr);
1207
+ GITERR_CHECK_ALLOC(new_entry->name);
1208
+
1209
+ /* Get to the start of the next component */
1210
+ path += component.size + 1;
1211
+ }
1212
+
1213
+ /* After all that, we're finally at the place where we want to perform the update */
1214
+ switch (update->action) {
1215
+ case GIT_TREE_UPDATE_UPSERT:
1216
+ {
1217
+ /* Make sure we're replacing something of the same type */
1218
+ tree_stack_entry *last = git_array_last(stack);
1219
+ char *basename = git_path_basename(update->path);
1220
+ const git_tree_entry *e = git_treebuilder_get(last->bld, basename);
1221
+ if (e && git_tree_entry_type(e) != git_object__type_from_filemode(update->filemode)) {
1222
+ git__free(basename);
1223
+ giterr_set(GITERR_TREE, "Cannot replace '%s' with '%s' at '%s'",
1224
+ git_object_type2string(git_tree_entry_type(e)),
1225
+ git_object_type2string(git_object__type_from_filemode(update->filemode)),
1226
+ update->path);
1227
+ error = -1;
1228
+ goto cleanup;
1229
+ }
1230
+
1231
+ error = git_treebuilder_insert(NULL, last->bld, basename, &update->id, update->filemode);
1232
+ git__free(basename);
1233
+ break;
1234
+ }
1235
+ case GIT_TREE_UPDATE_REMOVE:
1236
+ {
1237
+ char *basename = git_path_basename(update->path);
1238
+ error = git_treebuilder_remove(git_array_last(stack)->bld, basename);
1239
+ git__free(basename);
1240
+ break;
1241
+ }
1242
+ default:
1243
+ giterr_set(GITERR_TREE, "unkown action for update");
1244
+ error = -1;
1245
+ goto cleanup;
1246
+ }
1247
+
1248
+ if (error < 0)
1249
+ goto cleanup;
1250
+ }
1251
+
1252
+ /* We're done, go up the stack again and write out the tree */
1253
+ {
1254
+ tree_stack_entry *current = NULL, *popped = NULL;
1255
+ while ((popped = git_array_pop(stack)) != NULL) {
1256
+ current = git_array_last(stack);
1257
+ /* We've reached the top, current is the root tree */
1258
+ if (!current)
1259
+ break;
1260
+
1261
+ if ((error = create_popped_tree(current, popped, &component)) < 0)
1262
+ goto cleanup;
1263
+ }
1264
+
1265
+ /* Write out the root tree */
1266
+ git__free(popped->name);
1267
+ git_tree_free(popped->tree);
1268
+
1269
+ error = git_treebuilder_write(out, popped->bld);
1270
+ git_treebuilder_free(popped->bld);
1271
+ if (error < 0)
1272
+ goto cleanup;
1273
+ }
1274
+
1275
+ cleanup:
1276
+ {
1277
+ tree_stack_entry *e;
1278
+ while ((e = git_array_pop(stack)) != NULL) {
1279
+ git_treebuilder_free(e->bld);
1280
+ git_tree_free(e->tree);
1281
+ git__free(e->name);
1282
+ }
1283
+ }
1284
+
1285
+ git_buf_free(&component);
1286
+ git_array_clear(stack);
1287
+ git_vector_free(&entries);
1288
+ return error;
1289
+ }