Skip to content

Commit 6883a55

Browse files
committed
Merge pull request #572 from libgit2/cmn/rebase
Introduce rebasing
2 parents de817e2 + 0eee042 commit 6883a55

File tree

141 files changed

+617
-2
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

141 files changed

+617
-2
lines changed

ext/rugged/rugged.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,7 @@ void Init_rugged(void)
487487
Init_rugged_blame();
488488
Init_rugged_cred();
489489
Init_rugged_backend();
490+
Init_rugged_rebase();
490491

491492
/*
492493
* Sort the repository contents in no particular ordering;

ext/rugged/rugged.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ void Init_rugged_diff_line(void);
7575
void Init_rugged_blame(void);
7676
void Init_rugged_cred(void);
7777
void Init_rugged_backend(void);
78+
void Init_rugged_rebase(void);
7879

7980
VALUE rb_git_object_init(git_otype type, int argc, VALUE *argv, VALUE self);
8081

@@ -98,6 +99,7 @@ VALUE rb_git_delta_file_fromC(const git_diff_file *file);
9899

99100
void rugged_parse_diff_options(git_diff_options *opts, VALUE rb_options);
100101
void rugged_parse_merge_options(git_merge_options *opts, VALUE rb_options);
102+
void rugged_parse_checkout_options(git_checkout_options *opts, VALUE rb_options);
101103

102104
void rugged_cred_extract(git_cred **cred, int allowed_types, VALUE rb_credential);
103105

@@ -107,6 +109,7 @@ git_otype rugged_otype_get(VALUE rb_type);
107109
git_signature *rugged_signature_get(VALUE rb_person, git_repository *repo);
108110
git_object *rugged_object_get(git_repository *repo, VALUE object_value, git_otype type);
109111
int rugged_oid_get(git_oid *oid, git_repository *repo, VALUE p);
112+
const char * rugged_refname_from_string_or_ref(VALUE rb_name_or_ref);
110113

111114
void rugged_rb_ary_to_strarray(VALUE rb_array, git_strarray *str_array);
112115
VALUE rugged_strarray_to_rb_ary(git_strarray *str_array);

ext/rugged/rugged_rebase.c

Lines changed: 355 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,355 @@
1+
/*
2+
* The MIT License
3+
*
4+
* Copyright (c) 2016 GitHub, Inc
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
* THE SOFTWARE.
23+
*/
24+
25+
#include "rugged.h"
26+
27+
extern VALUE rb_mRugged;
28+
extern VALUE rb_cRuggedIndex;
29+
extern VALUE rb_cRuggedRepo;
30+
31+
VALUE rb_cRuggedRebase;
32+
33+
static VALUE rebase_operation_type(git_rebase_operation *operation);
34+
35+
static void parse_rebase_options(git_rebase_options *ret, VALUE rb_options)
36+
{
37+
VALUE val;
38+
39+
if (NIL_P(rb_options))
40+
return;
41+
42+
val = rb_hash_aref(rb_options, CSTR2SYM("quiet"));
43+
ret->quiet = RTEST(val);
44+
45+
val = rb_hash_aref(rb_options, CSTR2SYM("inmemory"));
46+
ret->inmemory = RTEST(val);
47+
48+
val = rb_hash_aref(rb_options, CSTR2SYM("rewrite_notes_ref"));
49+
if (!NIL_P(val)) {
50+
Check_Type(val, T_STRING);
51+
ret->rewrite_notes_ref = StringValueCStr(val);
52+
}
53+
54+
rugged_parse_checkout_options(&ret->checkout_options, rb_options);
55+
rugged_parse_merge_options(&ret->merge_options, rb_options);
56+
}
57+
58+
void rb_git_rebase__free(git_rebase *rebase)
59+
{
60+
git_rebase_free(rebase);
61+
}
62+
63+
VALUE rugged_rebase_new(VALUE klass, VALUE owner, git_rebase *rebase)
64+
{
65+
VALUE rb_rebase = Data_Wrap_Struct(klass, NULL, &rb_git_rebase__free, rebase);
66+
rugged_set_owner(rb_rebase, owner);
67+
return rb_rebase;
68+
}
69+
70+
/*
71+
* call-seq:
72+
* Rebase.new(repo, branch, upstream[, onto][, options]) -> Rebase
73+
*
74+
* Initialize a new rebase operation. This will put +repo+ in a
75+
* rebase state.
76+
*
77+
* +branch+ is the branch to be rebased, and +upstream+ is the branch
78+
* which is to be the new base. If +onto+ is specified, this will be
79+
* the one base, and +upstream+ is used to determine which commits
80+
* from +branch+ to use for the rebase.
81+
*
82+
* You can pass merge and checkout options for the options hash, plus
83+
* a few rebase-specific ones:
84+
*
85+
* :quiet ::
86+
* If true, a flag will be set to ask tools to activate quiet
87+
* mode. This does not do anything for libgit2/rugged but can be
88+
* used to interact with other implementations.
89+
*
90+
* :inmemory ::
91+
* Do not put the repository in a rebase state but perform all the
92+
* operations in-memory. In case of conflicts, the RebaseOperation
93+
* returned by #next will contain the index which can be used to
94+
* resolve conflicts.
95+
*
96+
* :rewrite_notes_ref ::
97+
* Name of the notes reference used to rewrite notes for rebased
98+
* commits when finishing the rebase. If +nil+, it will be taken
99+
* from the configuration.
100+
*/
101+
static VALUE rb_git_rebase_new(int argc, VALUE* argv, VALUE klass)
102+
{
103+
int error;
104+
const char* str_branch = NULL, *str_upstream = NULL, *str_onto = NULL;
105+
git_rebase *rebase;
106+
git_repository *repo;
107+
git_annotated_commit *branch = NULL, *upstream = NULL, *onto = NULL;
108+
VALUE rb_repo, rb_branch, rb_upstream, rb_onto, rb_options;
109+
git_rebase_options options = GIT_REBASE_OPTIONS_INIT;
110+
111+
rb_scan_args(argc, argv, "31:", &rb_repo, &rb_branch, &rb_upstream, &rb_onto, &rb_options);
112+
Data_Get_Struct(rb_repo, git_repository, repo);
113+
str_branch = rugged_refname_from_string_or_ref(rb_branch);
114+
str_upstream = rugged_refname_from_string_or_ref(rb_upstream);
115+
Check_Type(rb_branch, T_STRING);
116+
Check_Type(rb_upstream, T_STRING);
117+
if (!NIL_P(rb_onto))
118+
str_onto = rugged_refname_from_string_or_ref(rb_onto);
119+
120+
parse_rebase_options(&options, rb_options);
121+
122+
if ((error = git_annotated_commit_from_revspec(&branch, repo, str_branch)) < 0 ||
123+
(error = git_annotated_commit_from_revspec(&upstream, repo, str_upstream)) < 0)
124+
goto cleanup;
125+
126+
if (!NIL_P(rb_onto)) {
127+
if ((error = git_annotated_commit_from_revspec(&onto, repo, str_onto)) < 0)
128+
goto cleanup;
129+
}
130+
131+
error = git_rebase_init(&rebase, repo, branch, upstream, onto, &options);
132+
133+
cleanup:
134+
git_annotated_commit_free(branch);
135+
git_annotated_commit_free(upstream);
136+
git_annotated_commit_free(onto);
137+
138+
rugged_exception_check(error);
139+
140+
return rugged_rebase_new(klass, rb_repo, rebase);
141+
}
142+
143+
/*
144+
* call-seq:
145+
* Rebase.next() -> operation or nil
146+
*
147+
* Perform the next step in the rebase. The returned operation is a
148+
* Hash with its details or nil if there are no more operations to
149+
* perform. The Hash contains some of the following entries:
150+
*
151+
* :type ::
152+
* The type of operation being done. Can be one of +:pick+,
153+
* +:reword+, +:edit+, +:squash+, +:fixup+ or +:exec+.
154+
*
155+
* :id ::
156+
* The id of the commit being cherry-picked. Exists for all but
157+
* +:exec+ operations.
158+
*
159+
* :exec ::
160+
* If the operatin is +:exec+ this is what the user asked to be
161+
* executed.
162+
*/
163+
static VALUE rb_git_rebase_next(VALUE self)
164+
{
165+
int error;
166+
git_rebase *rebase;
167+
git_rebase_operation *operation;
168+
VALUE hash, val;
169+
170+
Data_Get_Struct(self, git_rebase, rebase);
171+
error = git_rebase_next(&operation, rebase);
172+
if (error == GIT_ITEROVER)
173+
return Qnil;
174+
175+
rugged_exception_check(error);
176+
177+
/* Create the operation hash out of the relevant details */
178+
hash = rb_hash_new();
179+
180+
val = rebase_operation_type(operation);
181+
rb_hash_aset(hash, CSTR2SYM("type"), val);
182+
183+
if (operation->type != GIT_REBASE_OPERATION_EXEC) {
184+
val = rugged_create_oid(&operation->id);
185+
rb_hash_aset(hash, CSTR2SYM("id"), val);
186+
}
187+
188+
if (operation->exec) {
189+
val = rb_str_new_utf8(operation->exec);
190+
rb_hash_aset(hash, CSTR2SYM("exec"), val);
191+
}
192+
193+
return hash;
194+
}
195+
/*
196+
* call-seq:
197+
* Rebase.inmemory_index -> Index
198+
*
199+
* Gets the index produced by the last operation, which is the result
200+
* of +next+ and which will be committed by the next invocation of
201+
* +commit+. This is useful for resolving conflicts in an in-memory
202+
* rebase before committing them.
203+
*
204+
* This is only applicable for in-memory rebases; for rebases within
205+
* a working directory, the changes were applied to the repository's
206+
* index.
207+
*/
208+
static VALUE rb_git_rebase_inmemory_index(VALUE self)
209+
{
210+
git_rebase *rebase;
211+
git_index *index;
212+
213+
Data_Get_Struct(self, git_rebase, rebase);
214+
rugged_exception_check(git_rebase_inmemory_index(&index, rebase));
215+
216+
return rugged_index_new(rb_cRuggedIndex, self, index);
217+
}
218+
219+
/*
220+
* call-seq:
221+
* Rebase.commit(author = nil, committer, message = nil)
222+
*
223+
* Commit the current patch. Any conflicts must have been resolved.
224+
*
225+
* If +author+ is +nil+, the existing author for the commit will be
226+
* used. If +message+ is +nil+, the existing message will be used.
227+
*/
228+
static VALUE rb_git_rebase_commit(int argc, VALUE *argv, VALUE self)
229+
{
230+
int error;
231+
git_oid id;
232+
git_rebase *rebase;
233+
git_signature *author = NULL, *committer;
234+
const char *message = NULL;
235+
VALUE rb_options, rb_author, rb_committer, rb_message;
236+
237+
Data_Get_Struct(self, git_rebase, rebase);
238+
rb_scan_args(argc, argv, ":", &rb_options);
239+
240+
rb_author = rb_hash_aref(rb_options, CSTR2SYM("author"));
241+
rb_committer = rb_hash_aref(rb_options, CSTR2SYM("committer"));
242+
rb_message = rb_hash_aref(rb_options, CSTR2SYM("message"));
243+
244+
if (!NIL_P(rb_message)) {
245+
Check_Type(rb_message, T_STRING);
246+
message = StringValueCStr(rb_message);
247+
}
248+
249+
if (NIL_P(rb_committer))
250+
rb_raise(rb_eArgError, "Expected non-nil committer");
251+
else
252+
committer = rugged_signature_get(rb_committer, NULL);
253+
254+
if (!NIL_P(rb_author))
255+
author = rugged_signature_get(rb_author, NULL);
256+
257+
error = git_rebase_commit(&id, rebase, author, committer, NULL, message);
258+
git_signature_free(author);
259+
git_signature_free(committer);
260+
261+
rugged_exception_check(error);
262+
263+
return rugged_create_oid(&id);
264+
}
265+
266+
/*
267+
* call-seq:
268+
* Rebase.abort()
269+
*
270+
* Abort the rebase currently in process, resetting the repository
271+
* and working directory to their state before the rebase began.
272+
*/
273+
static VALUE rb_git_rebase_abort(VALUE self)
274+
{
275+
git_rebase *rebase;
276+
277+
Data_Get_Struct(self, git_rebase, rebase);
278+
rugged_exception_check(git_rebase_abort(rebase));
279+
280+
return Qnil;
281+
}
282+
283+
/*
284+
* call-seq:
285+
* Rebase.finish()
286+
*
287+
* Finish the rebase currently in progress once all patches have been
288+
* applied.
289+
*/
290+
static VALUE rb_git_rebase_finish(VALUE self, VALUE rb_sig)
291+
{
292+
git_rebase *rebase;
293+
git_signature *sig;
294+
int error;
295+
296+
Data_Get_Struct(self, git_rebase, rebase);
297+
sig = rugged_signature_get(rb_sig, NULL);
298+
error = git_rebase_finish(rebase, sig);
299+
git_signature_free(sig);
300+
301+
rugged_exception_check(error);
302+
303+
return Qnil;
304+
}
305+
306+
static VALUE rebase_operation_type(git_rebase_operation *operation)
307+
{
308+
VALUE rb_type;
309+
310+
switch (operation->type) {
311+
case GIT_REBASE_OPERATION_PICK:
312+
rb_type = CSTR2SYM("pick");
313+
break;
314+
case GIT_REBASE_OPERATION_REWORD:
315+
rb_type = CSTR2SYM("reword");
316+
break;
317+
case GIT_REBASE_OPERATION_EDIT:
318+
rb_type = CSTR2SYM("edit");
319+
break;
320+
case GIT_REBASE_OPERATION_SQUASH:
321+
rb_type = CSTR2SYM("squash");
322+
break;
323+
case GIT_REBASE_OPERATION_FIXUP:
324+
rb_type = CSTR2SYM("fixup");
325+
break;
326+
case GIT_REBASE_OPERATION_EXEC:
327+
rb_type = CSTR2SYM("exec");
328+
break;
329+
default:
330+
rb_raise(rb_eTypeError, "Invalid rebase operation type found");
331+
break;
332+
}
333+
334+
return rb_type;
335+
}
336+
337+
void Init_rugged_rebase(void)
338+
{
339+
rb_cRuggedRebase = rb_define_class_under(rb_mRugged, "Rebase", rb_cObject);
340+
341+
rb_define_singleton_method(rb_cRuggedRebase, "new", rb_git_rebase_new, -1);
342+
rb_define_method(rb_cRuggedRebase, "next", rb_git_rebase_next, 0);
343+
rb_define_method(rb_cRuggedRebase, "inmemory_index", rb_git_rebase_inmemory_index, 0);
344+
rb_define_method(rb_cRuggedRebase, "commit", rb_git_rebase_commit, -1);
345+
rb_define_method(rb_cRuggedRebase, "abort", rb_git_rebase_abort, 0);
346+
rb_define_method(rb_cRuggedRebase, "finish", rb_git_rebase_finish, 1);
347+
}
348+
349+
350+
351+
352+
353+
354+
355+

0 commit comments

Comments
 (0)