rugged 0.23.0b2 → 0.23.0b4

Sign up to get free protection for your applications and to get access to all the features.
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
+ }