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
@@ -24,16 +24,12 @@ struct git_remote {
24
24
  git_vector refspecs;
25
25
  git_vector active_refspecs;
26
26
  git_vector passive_refspecs;
27
- git_transport_cb transport_cb;
28
- void *transport_cb_payload;
29
27
  git_transport *transport;
30
28
  git_repository *repo;
31
29
  git_push *push;
32
- git_remote_callbacks callbacks;
33
30
  git_transfer_progress stats;
34
31
  unsigned int need_pack;
35
32
  git_remote_autotag_option_t download_tags;
36
- int update_fetchhead;
37
33
  int prune_refs;
38
34
  int passed_refspecs;
39
35
  };
@@ -32,6 +32,8 @@
32
32
  # include "win32/w32_util.h"
33
33
  #endif
34
34
 
35
+ static int check_repositoryformatversion(git_config *config);
36
+
35
37
  #define GIT_FILE_CONTENT_PREFIX "gitdir:"
36
38
 
37
39
  #define GIT_BRANCH_MASTER "master"
@@ -109,7 +111,6 @@ void git_repository__cleanup(git_repository *repo)
109
111
 
110
112
  git_cache_clear(&repo->objects);
111
113
  git_attr_cache_flush(repo);
112
- git_submodule_cache_free(repo);
113
114
 
114
115
  set_config(repo, NULL);
115
116
  set_index(repo, NULL);
@@ -489,6 +490,7 @@ int git_repository_open_ext(
489
490
  git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT,
490
491
  link_path = GIT_BUF_INIT;
491
492
  git_repository *repo;
493
+ git_config *config = NULL;
492
494
 
493
495
  if (repo_ptr)
494
496
  *repo_ptr = NULL;
@@ -510,22 +512,36 @@ int git_repository_open_ext(
510
512
  GITERR_CHECK_ALLOC(repo->path_gitlink);
511
513
  }
512
514
 
515
+ /*
516
+ * We'd like to have the config, but git doesn't particularly
517
+ * care if it's not there, so we need to deal with that.
518
+ */
519
+
520
+ error = git_repository_config_snapshot(&config, repo);
521
+ if (error < 0 && error != GIT_ENOTFOUND)
522
+ goto cleanup;
523
+
524
+ if (config && (error = check_repositoryformatversion(config)) < 0)
525
+ goto cleanup;
526
+
513
527
  if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0)
514
528
  repo->is_bare = 1;
515
529
  else {
516
- git_config *config = NULL;
517
530
 
518
- if ((error = git_repository_config_snapshot(&config, repo)) < 0 ||
519
- (error = load_config_data(repo, config)) < 0 ||
520
- (error = load_workdir(repo, config, &parent)) < 0)
521
- git_repository_free(repo);
522
-
523
- git_config_free(config);
531
+ if (config &&
532
+ ((error = load_config_data(repo, config)) < 0 ||
533
+ (error = load_workdir(repo, config, &parent)) < 0))
534
+ goto cleanup;
524
535
  }
525
536
 
526
- if (!error)
527
- *repo_ptr = repo;
537
+ cleanup:
528
538
  git_buf_free(&parent);
539
+ git_config_free(config);
540
+
541
+ if (error < 0)
542
+ git_repository_free(repo);
543
+ else
544
+ *repo_ptr = repo;
529
545
 
530
546
  return error;
531
547
  }
@@ -931,9 +947,14 @@ bool git_repository__reserved_names(
931
947
 
932
948
  static int check_repositoryformatversion(git_config *config)
933
949
  {
934
- int version;
950
+ int version, error;
935
951
 
936
- if (git_config_get_int32(&version, config, "core.repositoryformatversion") < 0)
952
+ error = git_config_get_int32(&version, config, "core.repositoryformatversion");
953
+ /* git ignores this if the config variable isn't there */
954
+ if (error == GIT_ENOTFOUND)
955
+ return 0;
956
+
957
+ if (error < 0)
937
958
  return -1;
938
959
 
939
960
  if (GIT_REPO_VERSION < version) {
@@ -121,7 +121,6 @@ struct git_repository {
121
121
  git_refdb *_refdb;
122
122
  git_config *_config;
123
123
  git_index *_index;
124
- git_submodule_cache *_submodules;
125
124
 
126
125
  git_cache objects;
127
126
  git_attr_cache *attrcache;
@@ -63,6 +63,7 @@ int git_reset_default(
63
63
 
64
64
  assert(delta->status == GIT_DELTA_ADDED ||
65
65
  delta->status == GIT_DELTA_MODIFIED ||
66
+ delta->status == GIT_DELTA_CONFLICTED ||
66
67
  delta->status == GIT_DELTA_DELETED);
67
68
 
68
69
  error = git_index_conflict_remove(index, delta->old_file.path);
@@ -8,6 +8,7 @@
8
8
  #include "common.h"
9
9
  #include "repository.h"
10
10
  #include "commit.h"
11
+ #include "message.h"
11
12
  #include "tree.h"
12
13
  #include "reflog.h"
13
14
  #include "git2/diff.h"
@@ -16,7 +17,12 @@
16
17
  #include "git2/checkout.h"
17
18
  #include "git2/index.h"
18
19
  #include "git2/transaction.h"
20
+ #include "git2/merge.h"
21
+ #include "index.h"
19
22
  #include "signature.h"
23
+ #include "iterator.h"
24
+ #include "merge.h"
25
+ #include "diff.h"
20
26
 
21
27
  static int create_error(int error, const char *msg)
22
28
  {
@@ -49,23 +55,14 @@ static int append_abbreviated_oid(git_buf *out, const git_oid *b_commit)
49
55
 
50
56
  static int append_commit_description(git_buf *out, git_commit* commit)
51
57
  {
52
- const char *message;
53
- size_t pos = 0, len;
58
+ const char *summary = git_commit_summary(commit);
59
+ GITERR_CHECK_ALLOC(summary);
54
60
 
55
61
  if (append_abbreviated_oid(out, git_commit_id(commit)) < 0)
56
62
  return -1;
57
63
 
58
- message = git_commit_message(commit);
59
- len = strlen(message);
60
-
61
- /* TODO: Replace with proper commit short message
62
- * when git_commit_message_short() is implemented.
63
- */
64
- while (pos < len && message[pos] != '\n')
65
- pos++;
66
-
67
64
  git_buf_putc(out, ' ');
68
- git_buf_put(out, message, pos);
65
+ git_buf_puts(out, summary);
69
66
  git_buf_putc(out, '\n');
70
67
 
71
68
  return git_buf_oom(out) ? -1 : 0;
@@ -110,7 +107,7 @@ static int build_tree_from_index(git_tree **out, git_index *index)
110
107
  git_oid i_tree_oid;
111
108
 
112
109
  if ((error = git_index_write_tree(&i_tree_oid, index)) < 0)
113
- return -1;
110
+ return error;
114
111
 
115
112
  return git_tree_lookup(out, git_index_owner(index), &i_tree_oid);
116
113
  }
@@ -296,6 +293,25 @@ cleanup:
296
293
  return error;
297
294
  }
298
295
 
296
+ static git_diff_delta *stash_delta_merge(
297
+ const git_diff_delta *a,
298
+ const git_diff_delta *b,
299
+ git_pool *pool)
300
+ {
301
+ /* Special case for stash: if a file is deleted in the index, but exists
302
+ * in the working tree, we need to stash the workdir copy for the workdir.
303
+ */
304
+ if (a->status == GIT_DELTA_DELETED && b->status == GIT_DELTA_UNTRACKED) {
305
+ git_diff_delta *dup = git_diff__delta_dup(b, pool);
306
+
307
+ if (dup)
308
+ dup->status = GIT_DELTA_MODIFIED;
309
+ return dup;
310
+ }
311
+
312
+ return git_diff__merge_like_cgit(a, b, pool);
313
+ }
314
+
299
315
  static int build_workdir_tree(
300
316
  git_tree **tree_out,
301
317
  git_index *index,
@@ -303,17 +319,19 @@ static int build_workdir_tree(
303
319
  {
304
320
  git_repository *repo = git_index_owner(index);
305
321
  git_tree *b_tree = NULL;
306
- git_diff *diff = NULL;
322
+ git_diff *diff = NULL, *idx_to_wd = NULL;
307
323
  git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
308
324
  struct stash_update_rules data = {0};
309
325
  int error;
310
326
 
311
- opts.flags = GIT_DIFF_IGNORE_SUBMODULES;
327
+ opts.flags = GIT_DIFF_IGNORE_SUBMODULES | GIT_DIFF_INCLUDE_UNTRACKED;
312
328
 
313
329
  if ((error = git_commit_tree(&b_tree, b_commit)) < 0)
314
330
  goto cleanup;
315
331
 
316
- if ((error = git_diff_tree_to_workdir(&diff, repo, b_tree, &opts)) < 0)
332
+ if ((error = git_diff_tree_to_index(&diff, repo, b_tree, index, &opts)) < 0 ||
333
+ (error = git_diff_index_to_workdir(&idx_to_wd, repo, index, &opts)) < 0 ||
334
+ (error = git_diff__merge(diff, idx_to_wd, stash_delta_merge)) < 0)
317
335
  goto cleanup;
318
336
 
319
337
  data.include_changed = true;
@@ -324,6 +342,7 @@ static int build_workdir_tree(
324
342
  error = build_tree_from_index(tree_out, index);
325
343
 
326
344
  cleanup:
345
+ git_diff_free(idx_to_wd);
327
346
  git_diff_free(diff);
328
347
  git_tree_free(b_tree);
329
348
 
@@ -553,6 +572,396 @@ cleanup:
553
572
  return error;
554
573
  }
555
574
 
575
+ static int retrieve_stash_commit(
576
+ git_commit **commit,
577
+ git_repository *repo,
578
+ size_t index)
579
+ {
580
+ git_reference *stash = NULL;
581
+ git_reflog *reflog = NULL;
582
+ int error;
583
+ size_t max;
584
+ const git_reflog_entry *entry;
585
+
586
+ if ((error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE)) < 0)
587
+ goto cleanup;
588
+
589
+ if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE)) < 0)
590
+ goto cleanup;
591
+
592
+ max = git_reflog_entrycount(reflog);
593
+ if (!max || index > max - 1) {
594
+ error = GIT_ENOTFOUND;
595
+ giterr_set(GITERR_STASH, "No stashed state at position %" PRIuZ, index);
596
+ goto cleanup;
597
+ }
598
+
599
+ entry = git_reflog_entry_byindex(reflog, index);
600
+ if ((error = git_commit_lookup(commit, repo, git_reflog_entry_id_new(entry))) < 0)
601
+ goto cleanup;
602
+
603
+ cleanup:
604
+ git_reference_free(stash);
605
+ git_reflog_free(reflog);
606
+ return error;
607
+ }
608
+
609
+ static int retrieve_stash_trees(
610
+ git_tree **out_stash_tree,
611
+ git_tree **out_base_tree,
612
+ git_tree **out_index_tree,
613
+ git_tree **out_index_parent_tree,
614
+ git_tree **out_untracked_tree,
615
+ git_commit *stash_commit)
616
+ {
617
+ git_tree *stash_tree = NULL;
618
+ git_commit *base_commit = NULL;
619
+ git_tree *base_tree = NULL;
620
+ git_commit *index_commit = NULL;
621
+ git_tree *index_tree = NULL;
622
+ git_commit *index_parent_commit = NULL;
623
+ git_tree *index_parent_tree = NULL;
624
+ git_commit *untracked_commit = NULL;
625
+ git_tree *untracked_tree = NULL;
626
+ int error;
627
+
628
+ if ((error = git_commit_tree(&stash_tree, stash_commit)) < 0)
629
+ goto cleanup;
630
+
631
+ if ((error = git_commit_parent(&base_commit, stash_commit, 0)) < 0)
632
+ goto cleanup;
633
+ if ((error = git_commit_tree(&base_tree, base_commit)) < 0)
634
+ goto cleanup;
635
+
636
+ if ((error = git_commit_parent(&index_commit, stash_commit, 1)) < 0)
637
+ goto cleanup;
638
+ if ((error = git_commit_tree(&index_tree, index_commit)) < 0)
639
+ goto cleanup;
640
+
641
+ if ((error = git_commit_parent(&index_parent_commit, index_commit, 0)) < 0)
642
+ goto cleanup;
643
+ if ((error = git_commit_tree(&index_parent_tree, index_parent_commit)) < 0)
644
+ goto cleanup;
645
+
646
+ if (git_commit_parentcount(stash_commit) == 3) {
647
+ if ((error = git_commit_parent(&untracked_commit, stash_commit, 2)) < 0)
648
+ goto cleanup;
649
+ if ((error = git_commit_tree(&untracked_tree, untracked_commit)) < 0)
650
+ goto cleanup;
651
+ }
652
+
653
+ *out_stash_tree = stash_tree;
654
+ *out_base_tree = base_tree;
655
+ *out_index_tree = index_tree;
656
+ *out_index_parent_tree = index_parent_tree;
657
+ *out_untracked_tree = untracked_tree;
658
+
659
+ cleanup:
660
+ git_commit_free(untracked_commit);
661
+ git_commit_free(index_parent_commit);
662
+ git_commit_free(index_commit);
663
+ git_commit_free(base_commit);
664
+ if (error < 0) {
665
+ git_tree_free(stash_tree);
666
+ git_tree_free(base_tree);
667
+ git_tree_free(index_tree);
668
+ git_tree_free(index_parent_tree);
669
+ git_tree_free(untracked_tree);
670
+ }
671
+ return error;
672
+ }
673
+
674
+ static int merge_indexes(
675
+ git_index **out,
676
+ git_repository *repo,
677
+ git_tree *ancestor_tree,
678
+ git_index *ours_index,
679
+ git_index *theirs_index)
680
+ {
681
+ git_iterator *ancestor = NULL, *ours = NULL, *theirs = NULL;
682
+ const git_iterator_flag_t flags = GIT_ITERATOR_DONT_IGNORE_CASE;
683
+ int error;
684
+
685
+ if ((error = git_iterator_for_tree(&ancestor, ancestor_tree, flags, NULL, NULL)) < 0 ||
686
+ (error = git_iterator_for_index(&ours, ours_index, flags, NULL, NULL)) < 0 ||
687
+ (error = git_iterator_for_index(&theirs, theirs_index, flags, NULL, NULL)) < 0)
688
+ goto done;
689
+
690
+ error = git_merge__iterators(out, repo, ancestor, ours, theirs, NULL);
691
+
692
+ done:
693
+ git_iterator_free(ancestor);
694
+ git_iterator_free(ours);
695
+ git_iterator_free(theirs);
696
+ return error;
697
+ }
698
+
699
+ static int merge_index_and_tree(
700
+ git_index **out,
701
+ git_repository *repo,
702
+ git_tree *ancestor_tree,
703
+ git_index *ours_index,
704
+ git_tree *theirs_tree)
705
+ {
706
+ git_iterator *ancestor = NULL, *ours = NULL, *theirs = NULL;
707
+ const git_iterator_flag_t flags = GIT_ITERATOR_DONT_IGNORE_CASE;
708
+ int error;
709
+
710
+ if ((error = git_iterator_for_tree(&ancestor, ancestor_tree, flags, NULL, NULL)) < 0 ||
711
+ (error = git_iterator_for_index(&ours, ours_index, flags, NULL, NULL)) < 0 ||
712
+ (error = git_iterator_for_tree(&theirs, theirs_tree, flags, NULL, NULL)) < 0)
713
+ goto done;
714
+
715
+ error = git_merge__iterators(out, repo, ancestor, ours, theirs, NULL);
716
+
717
+ done:
718
+ git_iterator_free(ancestor);
719
+ git_iterator_free(ours);
720
+ git_iterator_free(theirs);
721
+ return error;
722
+ }
723
+
724
+ static void normalize_apply_options(
725
+ git_stash_apply_options *opts,
726
+ const git_stash_apply_options *given_apply_opts)
727
+ {
728
+ if (given_apply_opts != NULL) {
729
+ memcpy(opts, given_apply_opts, sizeof(git_stash_apply_options));
730
+ } else {
731
+ git_stash_apply_options default_apply_opts = GIT_STASH_APPLY_OPTIONS_INIT;
732
+ memcpy(opts, &default_apply_opts, sizeof(git_stash_apply_options));
733
+ }
734
+
735
+ if ((opts->checkout_options.checkout_strategy & (GIT_CHECKOUT_SAFE | GIT_CHECKOUT_FORCE)) == 0)
736
+ opts->checkout_options.checkout_strategy = GIT_CHECKOUT_SAFE;
737
+
738
+ if (!opts->checkout_options.our_label)
739
+ opts->checkout_options.our_label = "Updated upstream";
740
+
741
+ if (!opts->checkout_options.their_label)
742
+ opts->checkout_options.their_label = "Stashed changes";
743
+ }
744
+
745
+ int git_stash_apply_init_options(git_stash_apply_options *opts, unsigned int version)
746
+ {
747
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
748
+ opts, version, git_stash_apply_options, GIT_STASH_APPLY_OPTIONS_INIT);
749
+ return 0;
750
+ }
751
+
752
+ #define NOTIFY_PROGRESS(opts, progress_type) \
753
+ do { \
754
+ if ((opts).progress_cb && \
755
+ (error = (opts).progress_cb((progress_type), (opts).progress_payload))) { \
756
+ error = (error < 0) ? error : -1; \
757
+ goto cleanup; \
758
+ } \
759
+ } while(false);
760
+
761
+ static int ensure_clean_index(git_repository *repo, git_index *index)
762
+ {
763
+ git_tree *head_tree = NULL;
764
+ git_diff *index_diff = NULL;
765
+ int error = 0;
766
+
767
+ if ((error = git_repository_head_tree(&head_tree, repo)) < 0 ||
768
+ (error = git_diff_tree_to_index(
769
+ &index_diff, repo, head_tree, index, NULL)) < 0)
770
+ goto done;
771
+
772
+ if (git_diff_num_deltas(index_diff) > 0) {
773
+ giterr_set(GITERR_STASH, "%d uncommitted changes exist in the index",
774
+ git_diff_num_deltas(index_diff));
775
+ error = GIT_EUNCOMMITTED;
776
+ }
777
+
778
+ done:
779
+ git_diff_free(index_diff);
780
+ git_tree_free(head_tree);
781
+ return error;
782
+ }
783
+
784
+ static int stage_new_file(const git_index_entry **entries, void *data)
785
+ {
786
+ git_index *index = data;
787
+
788
+ if(entries[0] == NULL)
789
+ return git_index_add(index, entries[1]);
790
+ else
791
+ return git_index_add(index, entries[0]);
792
+ }
793
+
794
+ static int stage_new_files(
795
+ git_index **out,
796
+ git_repository *repo,
797
+ git_tree *parent_tree,
798
+ git_tree *tree)
799
+ {
800
+ git_iterator *iterators[2] = { NULL, NULL };
801
+ git_index *index = NULL;
802
+ int error;
803
+
804
+ if ((error = git_index_new(&index)) < 0 ||
805
+ (error = git_iterator_for_tree(&iterators[0], parent_tree,
806
+ GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
807
+ (error = git_iterator_for_tree(&iterators[1], tree,
808
+ GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0)
809
+ goto done;
810
+
811
+ error = git_iterator_walk(iterators, 2, stage_new_file, index);
812
+
813
+ done:
814
+ if (error < 0)
815
+ git_index_free(index);
816
+ else
817
+ *out = index;
818
+
819
+ git_iterator_free(iterators[0]);
820
+ git_iterator_free(iterators[1]);
821
+
822
+ return error;
823
+ }
824
+
825
+ int git_stash_apply(
826
+ git_repository *repo,
827
+ size_t index,
828
+ const git_stash_apply_options *given_opts)
829
+ {
830
+ git_stash_apply_options opts;
831
+ unsigned int checkout_strategy;
832
+ git_commit *stash_commit = NULL;
833
+ git_tree *stash_tree = NULL;
834
+ git_tree *stash_parent_tree = NULL;
835
+ git_tree *index_tree = NULL;
836
+ git_tree *index_parent_tree = NULL;
837
+ git_tree *untracked_tree = NULL;
838
+ git_index *stash_adds = NULL;
839
+ git_index *repo_index = NULL;
840
+ git_index *unstashed_index = NULL;
841
+ git_index *modified_index = NULL;
842
+ git_index *untracked_index = NULL;
843
+ int error;
844
+
845
+ GITERR_CHECK_VERSION(given_opts, GIT_STASH_APPLY_OPTIONS_VERSION, "git_stash_apply_options");
846
+
847
+ normalize_apply_options(&opts, given_opts);
848
+ checkout_strategy = opts.checkout_options.checkout_strategy;
849
+
850
+ NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_LOADING_STASH);
851
+
852
+ /* Retrieve commit corresponding to the given stash */
853
+ if ((error = retrieve_stash_commit(&stash_commit, repo, index)) < 0)
854
+ goto cleanup;
855
+
856
+ /* Retrieve all trees in the stash */
857
+ if ((error = retrieve_stash_trees(
858
+ &stash_tree, &stash_parent_tree, &index_tree,
859
+ &index_parent_tree, &untracked_tree, stash_commit)) < 0)
860
+ goto cleanup;
861
+
862
+ /* Load repo index */
863
+ if ((error = git_repository_index(&repo_index, repo)) < 0)
864
+ goto cleanup;
865
+
866
+ NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX);
867
+
868
+ if ((error = ensure_clean_index(repo, repo_index)) < 0)
869
+ goto cleanup;
870
+
871
+ /* Restore index if required */
872
+ if ((opts.flags & GIT_STASH_APPLY_REINSTATE_INDEX) &&
873
+ git_oid_cmp(git_tree_id(stash_parent_tree), git_tree_id(index_tree))) {
874
+
875
+ if ((error = merge_index_and_tree(
876
+ &unstashed_index, repo, index_parent_tree, repo_index, index_tree)) < 0)
877
+ goto cleanup;
878
+
879
+ if (git_index_has_conflicts(unstashed_index)) {
880
+ error = GIT_ECONFLICT;
881
+ goto cleanup;
882
+ }
883
+
884
+ /* Otherwise, stage any new files in the stash tree. (Note: their
885
+ * previously unstaged contents are staged, not the previously staged.)
886
+ */
887
+ } else if ((opts.flags & GIT_STASH_APPLY_REINSTATE_INDEX) == 0) {
888
+ if ((error = stage_new_files(
889
+ &stash_adds, repo, stash_parent_tree, stash_tree)) < 0 ||
890
+ (error = merge_indexes(
891
+ &unstashed_index, repo, stash_parent_tree, repo_index, stash_adds)) < 0)
892
+ goto cleanup;
893
+ }
894
+
895
+ NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED);
896
+
897
+ /* Restore modified files in workdir */
898
+ if ((error = merge_index_and_tree(
899
+ &modified_index, repo, stash_parent_tree, repo_index, stash_tree)) < 0)
900
+ goto cleanup;
901
+
902
+ /* If applicable, restore untracked / ignored files in workdir */
903
+ if (untracked_tree) {
904
+ NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED);
905
+
906
+ if ((error = merge_index_and_tree(&untracked_index, repo, NULL, repo_index, untracked_tree)) < 0)
907
+ goto cleanup;
908
+ }
909
+
910
+ if (untracked_index) {
911
+ opts.checkout_options.checkout_strategy |= GIT_CHECKOUT_DONT_UPDATE_INDEX;
912
+
913
+ NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED);
914
+
915
+ if ((error = git_checkout_index(repo, untracked_index, &opts.checkout_options)) < 0)
916
+ goto cleanup;
917
+
918
+ opts.checkout_options.checkout_strategy = checkout_strategy;
919
+ }
920
+
921
+
922
+ /* If there are conflicts in the modified index, then we need to actually
923
+ * check that out as the repo's index. Otherwise, we don't update the
924
+ * index.
925
+ */
926
+
927
+ if (!git_index_has_conflicts(modified_index))
928
+ opts.checkout_options.checkout_strategy |= GIT_CHECKOUT_DONT_UPDATE_INDEX;
929
+
930
+ /* Check out the modified index using the existing repo index as baseline,
931
+ * so that existing modifications in the index can be rewritten even when
932
+ * checking out safely.
933
+ */
934
+ opts.checkout_options.baseline_index = repo_index;
935
+
936
+ NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED);
937
+
938
+ if ((error = git_checkout_index(repo, modified_index, &opts.checkout_options)) < 0)
939
+ goto cleanup;
940
+
941
+ if (unstashed_index && !git_index_has_conflicts(modified_index)) {
942
+ if ((error = git_index_read_index(repo_index, unstashed_index)) < 0)
943
+ goto cleanup;
944
+ }
945
+
946
+ NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_DONE);
947
+
948
+ error = git_index_write(repo_index);
949
+
950
+ cleanup:
951
+ git_index_free(untracked_index);
952
+ git_index_free(modified_index);
953
+ git_index_free(unstashed_index);
954
+ git_index_free(stash_adds);
955
+ git_index_free(repo_index);
956
+ git_tree_free(untracked_tree);
957
+ git_tree_free(index_parent_tree);
958
+ git_tree_free(index_tree);
959
+ git_tree_free(stash_parent_tree);
960
+ git_tree_free(stash_tree);
961
+ git_commit_free(stash_commit);
962
+ return error;
963
+ }
964
+
556
965
  int git_stash_foreach(
557
966
  git_repository *repo,
558
967
  git_stash_cb callback,
@@ -620,7 +1029,7 @@ int git_stash_drop(
620
1029
 
621
1030
  max = git_reflog_entrycount(reflog);
622
1031
 
623
- if (index > max - 1) {
1032
+ if (!max || index > max - 1) {
624
1033
  error = GIT_ENOTFOUND;
625
1034
  giterr_set(GITERR_STASH, "No stashed state at position %" PRIuZ, index);
626
1035
  goto cleanup;
@@ -651,3 +1060,16 @@ cleanup:
651
1060
  git_reflog_free(reflog);
652
1061
  return error;
653
1062
  }
1063
+
1064
+ int git_stash_pop(
1065
+ git_repository *repo,
1066
+ size_t index,
1067
+ const git_stash_apply_options *options)
1068
+ {
1069
+ int error;
1070
+
1071
+ if ((error = git_stash_apply(repo, index, options)) < 0)
1072
+ return error;
1073
+
1074
+ return git_stash_drop(repo, index);
1075
+ }