rugged 0.24.0b12 → 0.24.0b13

Sign up to get free protection for your applications and to get access to all the features.
@@ -41,6 +41,18 @@ VALUE rugged_ref_new(VALUE klass, VALUE owner, git_reference *ref)
41
41
  return rb_ref;
42
42
  }
43
43
 
44
+
45
+ const char * rugged_refname_from_string_or_ref(VALUE rb_name_or_ref)
46
+ {
47
+ if (rb_obj_is_kind_of(rb_name_or_ref, rb_cRuggedReference))
48
+ rb_name_or_ref = rb_funcall(rb_name_or_ref, rb_intern("canonical_name"), 0);
49
+
50
+ if (TYPE(rb_name_or_ref) != T_STRING)
51
+ rb_raise(rb_eTypeError, "Expecting a String or Rugged::Reference instance");
52
+
53
+ return StringValueCStr(rb_name_or_ref);
54
+ }
55
+
44
56
  /*
45
57
  * call-seq:
46
58
  * Reference.valid_name?(ref_name) -> true or false
@@ -1934,7 +1934,7 @@ static int rugged__checkout_notify_cb(
1934
1934
  /**
1935
1935
  * The caller has to free the returned git_checkout_options paths strings array.
1936
1936
  */
1937
- static void rugged_parse_checkout_options(git_checkout_options *opts, VALUE rb_options)
1937
+ void rugged_parse_checkout_options(git_checkout_options *opts, VALUE rb_options)
1938
1938
  {
1939
1939
  VALUE rb_value;
1940
1940
 
@@ -2530,6 +2530,61 @@ static VALUE rb_git_repo_cherrypick(int argc, VALUE *argv, VALUE self)
2530
2530
  return Qnil;
2531
2531
  }
2532
2532
 
2533
+ /*
2534
+ * call-seq:
2535
+ * repo.cherrypick_commit(commit, our_commit, [mainline, options]) -> nil
2536
+ *
2537
+ * Cherry-pick the given commit on the given base in-memory and
2538
+ * return an index with the result.
2539
+ *
2540
+ * `commit` can be either a string containing a commit id or a
2541
+ * `Rugged::Commit` object.
2542
+ *
2543
+ * `our_commit` is the base commit, can be either a string containing
2544
+ * a commit id or a `Rugged::Commit` object.
2545
+ *
2546
+ * `mainline` when cherry-picking a merge, this is the parent number
2547
+ * (starting from 1) which should be considered the mainline.
2548
+ */
2549
+ static VALUE rb_git_repo_cherrypick_commit(int argc, VALUE *argv, VALUE self)
2550
+ {
2551
+ VALUE rb_options, rb_commit, rb_our_commit, rb_mainline;
2552
+
2553
+ git_repository *repo;
2554
+ git_commit *commit, *our_commit;
2555
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
2556
+ git_index *index;
2557
+ int error, mainline;
2558
+
2559
+ rb_scan_args(argc, argv, "21:", &rb_commit, &rb_our_commit, &rb_mainline, &rb_options);
2560
+
2561
+ if (TYPE(rb_commit) == T_STRING) {
2562
+ rb_commit = rugged_object_rev_parse(self, rb_commit, 1);
2563
+ }
2564
+ if (TYPE(rb_our_commit) == T_STRING) {
2565
+ rb_our_commit = rugged_object_rev_parse(self, rb_our_commit, 1);
2566
+ }
2567
+
2568
+ if (!rb_obj_is_kind_of(rb_commit, rb_cRuggedCommit)) {
2569
+ rb_raise(rb_eArgError, "Expected a Rugged::Commit.");
2570
+ }
2571
+ if (!rb_obj_is_kind_of(rb_our_commit, rb_cRuggedCommit)) {
2572
+ rb_raise(rb_eArgError, "Expected a Rugged::Commit.");
2573
+ }
2574
+
2575
+ Data_Get_Struct(self, git_repository, repo);
2576
+ Data_Get_Struct(rb_commit, git_commit, commit);
2577
+ Data_Get_Struct(rb_our_commit, git_commit, our_commit);
2578
+
2579
+ rugged_parse_merge_options(&opts, rb_options);
2580
+
2581
+ mainline = NIL_P(rb_mainline) ? 0 : FIX2UINT(rb_mainline);
2582
+ error = git_cherrypick_commit(&index, repo, commit, our_commit, mainline, &opts);
2583
+ rugged_exception_check(error);
2584
+
2585
+ return rugged_index_new(rb_cRuggedIndex, self, index);
2586
+ }
2587
+
2533
2588
  void Init_rugged_repo(void)
2534
2589
  {
2535
2590
  id_call = rb_intern("call");
@@ -2603,6 +2658,7 @@ void Init_rugged_repo(void)
2603
2658
  rb_define_method(rb_cRuggedRepo, "checkout_head", rb_git_checkout_head, -1);
2604
2659
 
2605
2660
  rb_define_method(rb_cRuggedRepo, "cherrypick", rb_git_repo_cherrypick, -1);
2661
+ rb_define_method(rb_cRuggedRepo, "cherrypick_commit", rb_git_repo_cherrypick_commit, -1);
2606
2662
  rb_define_method(rb_cRuggedRepo, "fetch_attributes", rb_git_repo_attributes, -1);
2607
2663
 
2608
2664
  rb_cRuggedOdbObject = rb_define_class_under(rb_mRugged, "OdbObject", rb_cObject);
@@ -190,6 +190,29 @@ static VALUE rb_git_walker_simplify_first_parent(VALUE self)
190
190
  return Qnil;
191
191
  }
192
192
 
193
+ /*
194
+ * call-seq:
195
+ * walker.count -> Fixnum
196
+ *
197
+ * Returns the amount of objects a walker iterated over.
198
+ */
199
+ static VALUE rb_git_walker_count(VALUE self)
200
+ {
201
+ git_revwalk *walk;
202
+ git_oid commit_oid;
203
+ int error = 0;
204
+ uint64_t count = 0;
205
+
206
+ Data_Get_Struct(self, git_revwalk, walk);
207
+
208
+ while (((error = git_revwalk_next(&commit_oid, walk)) == 0) && ++count != UINT64_MAX);
209
+
210
+ if (error != GIT_ITEROVER)
211
+ rugged_exception_check(error);
212
+
213
+ return ULONG2NUM(count);
214
+ }
215
+
193
216
  /*
194
217
  * call-seq:
195
218
  * walker.reset -> nil
@@ -492,4 +515,5 @@ void Init_rugged_revwalk(void)
492
515
  rb_define_method(rb_cRuggedWalker, "reset", rb_git_walker_reset, 0);
493
516
  rb_define_method(rb_cRuggedWalker, "sorting", rb_git_walker_sorting, 1);
494
517
  rb_define_method(rb_cRuggedWalker, "simplify_first_parent", rb_git_walker_simplify_first_parent, 0);
518
+ rb_define_method(rb_cRuggedWalker, "count", rb_git_walker_count, 0);
495
519
  }
data/lib/rugged/commit.rb CHANGED
@@ -9,6 +9,10 @@ module Rugged
9
9
  "#<Rugged::Commit:#{object_id} {message: #{message.inspect}, tree: #{tree.inspect}, parents: #{parent_oids}}>"
10
10
  end
11
11
 
12
+ def header_field?(field)
13
+ !!header_field(field)
14
+ end
15
+
12
16
  # Return a diff between this commit and its first parent or another commit or tree.
13
17
  #
14
18
  # See Rugged::Tree#diff for more details.
data/lib/rugged/tag.rb CHANGED
@@ -1,5 +1,24 @@
1
1
  module Rugged
2
2
  class Tag < Rugged::Reference
3
+ GPG_SIGNATURE_PREFIX = "-----BEGIN PGP SIGNATURE-----".freeze
4
+
5
+ def self.extract_signature(repo, oid, prefix=GPG_SIGNATURE_PREFIX)
6
+ object = repo.read(oid)
7
+
8
+ unless object.type == :tag
9
+ raise GitRPC::InvalidObject, "Invalid object type #{object.type}, expected tag"
10
+ end
11
+
12
+ if index = object.data.index(prefix)
13
+ [
14
+ object.data.byteslice(index..-1),
15
+ object.data.byteslice(0...index)
16
+ ]
17
+ else
18
+ [nil, object.data]
19
+ end
20
+ end
21
+
3
22
  def name
4
23
  canonical_name.sub(%r{^refs/tags/}, "")
5
24
  end
@@ -1,3 +1,3 @@
1
1
  module Rugged
2
- Version = VERSION = '0.24.0b12'
2
+ Version = VERSION = '0.24.0b13'
3
3
  end
@@ -257,7 +257,7 @@ IF (WIN32 AND WINHTTP)
257
257
  LINK_DIRECTORIES(${LIBWINHTTP_PATH})
258
258
  ENDIF ()
259
259
 
260
- LINK_LIBRARIES(winhttp rpcrt4 crypt32)
260
+ LINK_LIBRARIES(winhttp rpcrt4 crypt32 ole32)
261
261
  LIST(APPEND LIBGIT2_PC_LIBS "-lwinhttp" "-lrpcrt4" "-lcrypt32" "-lole32")
262
262
  ELSE ()
263
263
  IF (CURL)
@@ -340,7 +340,8 @@ IF (LIBSSH2_FOUND)
340
340
  ADD_DEFINITIONS(-DGIT_SSH)
341
341
  INCLUDE_DIRECTORIES(${LIBSSH2_INCLUDE_DIRS})
342
342
  LINK_DIRECTORIES(${LIBSSH2_LIBRARY_DIRS})
343
- SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} libssh2")
343
+ LIST(APPEND LIBGIT2_PC_LIBS ${LIBSSH2_LDFLAGS})
344
+ #SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} ${LIBSSH2_LDFLAGS}")
344
345
  SET(SSH_LIBRARIES ${LIBSSH2_LIBRARIES})
345
346
 
346
347
  CHECK_LIBRARY_EXISTS("${LIBSSH2_LIBRARIES}" libssh2_userauth_publickey_frommemory "${LIBSSH2_LIBRARY_DIRS}" HAVE_LIBSSH2_MEMORY_CREDENTIALS)
@@ -263,6 +263,18 @@ GIT_EXTERN(int) git_commit_nth_gen_ancestor(
263
263
  */
264
264
  GIT_EXTERN(int) git_commit_header_field(git_buf *out, const git_commit *commit, const char *field);
265
265
 
266
+ /**
267
+ * Extract the signature from a commit
268
+ *
269
+ * @param signature the signature block
270
+ * @param signed_data signed data; this is the commit contents minus the signature block
271
+ * @param repo the repository in which the commit exists
272
+ * @param commit_id the commit from which to extract the data
273
+ * @param field the name of the header field containing the signature
274
+ * block; pass `NULL` to extract the default 'gpgsig'
275
+ */
276
+ GIT_EXTERN(int) git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_repository *repo, git_oid *commit_id, const char *field);
277
+
266
278
  /**
267
279
  * Create new commit in the repository from a list of `git_object` pointers
268
280
  *
@@ -1194,7 +1194,7 @@ typedef enum {
1194
1194
  } git_diff_stats_format_t;
1195
1195
 
1196
1196
  /**
1197
- * Accumlate diff statistics for all patches.
1197
+ * Accumulate diff statistics for all patches.
1198
1198
  *
1199
1199
  * @param out Structure containg the diff statistics.
1200
1200
  * @param diff A git_diff generated by one of the above functions.
@@ -527,10 +527,6 @@ GIT_EXTERN(int) git_merge_trees(
527
527
  * or checked out. If the index is to be converted to a tree, the caller
528
528
  * should resolve any conflicts that arose as part of the merge.
529
529
  *
530
- * The merge performed uses the first common ancestor, unlike the
531
- * `git-merge-recursive` strategy, which may produce an artificial common
532
- * ancestor tree when there are multiple ancestors.
533
- *
534
530
  * The returned index must be freed explicitly with `git_index_free`.
535
531
  *
536
532
  * @param out pointer to store the index result in
@@ -553,10 +549,6 @@ GIT_EXTERN(int) git_merge_commits(
553
549
  * to the index. Callers should inspect the repository's index after this
554
550
  * completes, resolve any conflicts and prepare a commit.
555
551
  *
556
- * The merge performed uses the first common ancestor, unlike the
557
- * `git-merge-recursive` strategy, which may produce an artificial common
558
- * ancestor tree when there are multiple ancestors.
559
- *
560
552
  * For compatibility with git, the repository is put into a merging
561
553
  * state. Once the commit is done (or if the uses wishes to abort),
562
554
  * you should clear this state by calling
@@ -38,16 +38,30 @@ typedef struct {
38
38
  */
39
39
  int quiet;
40
40
 
41
+ /**
42
+ * Used by `git_rebase_init`, this will begin an in-memory rebase,
43
+ * which will allow callers to step through the rebase operations and
44
+ * commit the rebased changes, but will not rewind HEAD or update the
45
+ * repository to be in a rebasing state. This will not interfere with
46
+ * the working directory (if there is one).
47
+ */
48
+ int inmemory;
49
+
41
50
  /**
42
51
  * Used by `git_rebase_finish`, this is the name of the notes reference
43
52
  * used to rewrite notes for rebased commits when finishing the rebase;
44
- * if NULL, the contents of the coniguration option `notes.rewriteRef`
53
+ * if NULL, the contents of the configuration option `notes.rewriteRef`
45
54
  * is examined, unless the configuration option `notes.rewrite.rebase`
46
55
  * is set to false. If `notes.rewriteRef` is also NULL, notes will
47
56
  * not be rewritten.
48
57
  */
49
58
  const char *rewrite_notes_ref;
50
59
 
60
+ /**
61
+ * Options to control how trees are merged during `git_rebase_next`.
62
+ */
63
+ git_merge_options merge_options;
64
+
51
65
  /**
52
66
  * Options to control how files are written during `git_rebase_init`,
53
67
  * `git_checkout_next` and `git_checkout_abort`. Note that a minimum
@@ -101,7 +115,8 @@ typedef enum {
101
115
 
102
116
  #define GIT_REBASE_OPTIONS_VERSION 1
103
117
  #define GIT_REBASE_OPTIONS_INIT \
104
- {GIT_REBASE_OPTIONS_VERSION, 0, NULL, GIT_CHECKOUT_OPTIONS_INIT}
118
+ { GIT_REBASE_OPTIONS_VERSION, 0, 0, NULL, GIT_MERGE_OPTIONS_INIT, \
119
+ GIT_CHECKOUT_OPTIONS_INIT}
105
120
 
106
121
  /** Indicates that a rebase operation is not (yet) in progress. */
107
122
  #define GIT_REBASE_NO_OPERATION SIZE_MAX
@@ -226,6 +241,21 @@ GIT_EXTERN(int) git_rebase_next(
226
241
  git_rebase_operation **operation,
227
242
  git_rebase *rebase);
228
243
 
244
+ /**
245
+ * Gets the index produced by the last operation, which is the result
246
+ * of `git_rebase_next` and which will be committed by the next
247
+ * invocation of `git_rebase_commit`. This is useful for resolving
248
+ * conflicts in an in-memory rebase before committing them. You must
249
+ * call `git_index_free` when you are finished with this.
250
+ *
251
+ * This is only applicable for in-memory rebases; for rebases within
252
+ * a working directory, the changes were applied to the repository's
253
+ * index.
254
+ */
255
+ GIT_EXTERN(int) git_rebase_inmemory_index(
256
+ git_index **index,
257
+ git_rebase *rebase);
258
+
229
259
  /**
230
260
  * Commits the current patch. You must have resolved any conflicts that
231
261
  * were introduced during the patch application from the `git_rebase_next`
@@ -150,7 +150,7 @@ typedef struct git_stash_apply_options {
150
150
  * `GIT_STASH_APPLY_OPTIONS_INIT` here.
151
151
  * @return Zero on success; -1 on failure.
152
152
  */
153
- int git_stash_apply_init_options(
153
+ GIT_EXTERN(int) git_stash_apply_init_options(
154
154
  git_stash_apply_options *opts, unsigned int version);
155
155
 
156
156
  /**
@@ -123,7 +123,7 @@ int git_attr_file__load(
123
123
  break;
124
124
  }
125
125
  case GIT_ATTR_FILE__FROM_FILE: {
126
- int fd;
126
+ int fd = -1;
127
127
 
128
128
  /* For open or read errors, pretend that we got ENOTFOUND. */
129
129
  /* TODO: issue warning when warning API is available */
@@ -133,7 +133,8 @@ int git_attr_file__load(
133
133
  (fd = git_futils_open_ro(entry->fullpath)) < 0 ||
134
134
  (error = git_futils_readbuffer_fd(&content, fd, (size_t)st.st_size)) < 0)
135
135
  nonexistent = true;
136
- else
136
+
137
+ if (fd >= 0)
137
138
  p_close(fd);
138
139
 
139
140
  break;
@@ -1487,8 +1487,10 @@ static int blob_content_to_file(
1487
1487
  if (!data->opts.disable_filters &&
1488
1488
  (error = git_filter_list__load_ext(
1489
1489
  &fl, data->repo, blob, hint_path,
1490
- GIT_FILTER_TO_WORKTREE, &filter_opts)))
1490
+ GIT_FILTER_TO_WORKTREE, &filter_opts))) {
1491
+ p_close(fd);
1491
1492
  return error;
1493
+ }
1492
1494
 
1493
1495
  /* setup the writer */
1494
1496
  memset(&writer, 0, sizeof(struct checkout_stream));
@@ -2519,7 +2521,8 @@ int git_checkout_iterator(
2519
2521
 
2520
2522
  if (data.opts.baseline_index) {
2521
2523
  if ((error = git_iterator_for_index(
2522
- &baseline, data.opts.baseline_index, &baseline_opts)) < 0)
2524
+ &baseline, git_index_owner(data.opts.baseline_index),
2525
+ data.opts.baseline_index, &baseline_opts)) < 0)
2523
2526
  goto cleanup;
2524
2527
  } else {
2525
2528
  if ((error = git_iterator_for_tree(
@@ -2631,7 +2634,7 @@ int git_checkout_index(
2631
2634
  return error;
2632
2635
  GIT_REFCOUNT_INC(index);
2633
2636
 
2634
- if (!(error = git_iterator_for_index(&index_i, index, NULL)))
2637
+ if (!(error = git_iterator_for_index(&index_i, repo, index, NULL)))
2635
2638
  error = git_checkout_iterator(index_i, index, opts);
2636
2639
 
2637
2640
  if (owned)
@@ -564,17 +564,97 @@ int git_commit_nth_gen_ancestor(
564
564
 
565
565
  int git_commit_header_field(git_buf *out, const git_commit *commit, const char *field)
566
566
  {
567
- const char *buf = commit->raw_header;
568
- const char *h, *eol;
567
+ const char *eol, *buf = commit->raw_header;
569
568
 
570
569
  git_buf_sanitize(out);
570
+
571
+ while ((eol = strchr(buf, '\n'))) {
572
+ /* We can skip continuations here */
573
+ if (buf[0] == ' ') {
574
+ buf = eol + 1;
575
+ continue;
576
+ }
577
+
578
+ /* Skip until we find the field we're after */
579
+ if (git__prefixcmp(buf, field)) {
580
+ buf = eol + 1;
581
+ continue;
582
+ }
583
+
584
+ buf += strlen(field);
585
+ /* Check that we're not matching a prefix but the field itself */
586
+ if (buf[0] != ' ') {
587
+ buf = eol + 1;
588
+ continue;
589
+ }
590
+
591
+ buf++; /* skip the SP */
592
+
593
+ git_buf_put(out, buf, eol - buf);
594
+ if (git_buf_oom(out))
595
+ goto oom;
596
+
597
+ /* If the next line starts with SP, it's multi-line, we must continue */
598
+ while (eol[1] == ' ') {
599
+ git_buf_putc(out, '\n');
600
+ buf = eol + 2;
601
+ eol = strchr(buf, '\n');
602
+ if (!eol)
603
+ goto malformed;
604
+
605
+ git_buf_put(out, buf, eol - buf);
606
+ }
607
+
608
+ if (git_buf_oom(out))
609
+ goto oom;
610
+
611
+ return 0;
612
+ }
613
+
614
+ giterr_set(GITERR_OBJECT, "no such field '%s'", field);
615
+ return GIT_ENOTFOUND;
616
+
617
+ malformed:
618
+ giterr_set(GITERR_OBJECT, "malformed header");
619
+ return -1;
620
+ oom:
621
+ giterr_set_oom();
622
+ return -1;
623
+ }
624
+
625
+ int git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_repository *repo, git_oid *commit_id, const char *field)
626
+ {
627
+ git_odb_object *obj;
628
+ git_odb *odb;
629
+ const char *buf;
630
+ const char *h, *eol;
631
+ int error;
632
+
633
+ git_buf_sanitize(signature);
634
+ git_buf_sanitize(signed_data);
635
+
636
+ if (!field)
637
+ field = "gpgsig";
638
+
639
+ if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
640
+ return error;
641
+
642
+ if ((error = git_odb_read(&obj, odb, commit_id)) < 0)
643
+ return error;
644
+
645
+ buf = git_odb_object_data(obj);
646
+
571
647
  while ((h = strchr(buf, '\n')) && h[1] != '\0' && h[1] != '\n') {
572
648
  h++;
573
- if (git__prefixcmp(h, field)) {
649
+ if (git__prefixcmp(buf, field)) {
650
+ if (git_buf_put(signed_data, buf, h - buf) < 0)
651
+ return -1;
652
+
574
653
  buf = h;
575
654
  continue;
576
655
  }
577
656
 
657
+ h = buf;
578
658
  h += strlen(field);
579
659
  eol = strchr(h, '\n');
580
660
  if (h[0] != ' ') {
@@ -586,33 +666,44 @@ int git_commit_header_field(git_buf *out, const git_commit *commit, const char *
586
666
 
587
667
  h++; /* skip the SP */
588
668
 
589
- git_buf_put(out, h, eol - h);
590
- if (git_buf_oom(out))
669
+ git_buf_put(signature, h, eol - h);
670
+ if (git_buf_oom(signature))
591
671
  goto oom;
592
672
 
593
673
  /* If the next line starts with SP, it's multi-line, we must continue */
594
674
  while (eol[1] == ' ') {
595
- git_buf_putc(out, '\n');
675
+ git_buf_putc(signature, '\n');
596
676
  h = eol + 2;
597
677
  eol = strchr(h, '\n');
598
678
  if (!eol)
599
679
  goto malformed;
600
680
 
601
- git_buf_put(out, h, eol - h);
681
+ git_buf_put(signature, h, eol - h);
602
682
  }
603
683
 
604
- if (git_buf_oom(out))
684
+ if (git_buf_oom(signature))
605
685
  goto oom;
606
686
 
607
- return 0;
687
+ git_odb_object_free(obj);
688
+ return git_buf_puts(signed_data, eol+1);
608
689
  }
609
690
 
610
- return GIT_ENOTFOUND;
691
+ giterr_set(GITERR_INVALID, "this commit is not signed");
692
+ error = GIT_ENOTFOUND;
693
+ goto cleanup;
611
694
 
612
695
  malformed:
613
696
  giterr_set(GITERR_OBJECT, "malformed header");
614
- return -1;
697
+ error = -1;
698
+ goto cleanup;
615
699
  oom:
616
700
  giterr_set_oom();
617
- return -1;
701
+ error = -1;
702
+ goto cleanup;
703
+
704
+ cleanup:
705
+ git_odb_object_free(obj);
706
+ git_buf_clear(signature);
707
+ git_buf_clear(signed_data);
708
+ return error;
618
709
  }