rdavila-rugged 0.24.0b13
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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +619 -0
- data/ext/rugged/extconf.rb +105 -0
- data/ext/rugged/rugged.c +527 -0
- data/ext/rugged/rugged.h +185 -0
- data/ext/rugged/rugged_backend.c +34 -0
- data/ext/rugged/rugged_blame.c +292 -0
- data/ext/rugged/rugged_blob.c +638 -0
- data/ext/rugged/rugged_branch.c +195 -0
- data/ext/rugged/rugged_branch_collection.c +408 -0
- data/ext/rugged/rugged_commit.c +691 -0
- data/ext/rugged/rugged_config.c +404 -0
- data/ext/rugged/rugged_cred.c +148 -0
- data/ext/rugged/rugged_diff.c +686 -0
- data/ext/rugged/rugged_diff_delta.c +105 -0
- data/ext/rugged/rugged_diff_hunk.c +103 -0
- data/ext/rugged/rugged_diff_line.c +83 -0
- data/ext/rugged/rugged_index.c +1255 -0
- data/ext/rugged/rugged_note.c +376 -0
- data/ext/rugged/rugged_object.c +383 -0
- data/ext/rugged/rugged_patch.c +245 -0
- data/ext/rugged/rugged_reference.c +396 -0
- data/ext/rugged/rugged_reference_collection.c +446 -0
- data/ext/rugged/rugged_remote.c +691 -0
- data/ext/rugged/rugged_remote_collection.c +457 -0
- data/ext/rugged/rugged_repo.c +2669 -0
- data/ext/rugged/rugged_revwalk.c +495 -0
- data/ext/rugged/rugged_settings.c +155 -0
- data/ext/rugged/rugged_signature.c +106 -0
- data/ext/rugged/rugged_submodule.c +852 -0
- data/ext/rugged/rugged_submodule_collection.c +384 -0
- data/ext/rugged/rugged_tag.c +251 -0
- data/ext/rugged/rugged_tag_collection.c +347 -0
- data/ext/rugged/rugged_tree.c +919 -0
- data/lib/rugged.rb +23 -0
- data/lib/rugged/attributes.rb +41 -0
- data/lib/rugged/blob.rb +28 -0
- data/lib/rugged/branch.rb +19 -0
- data/lib/rugged/commit.rb +54 -0
- data/lib/rugged/console.rb +9 -0
- data/lib/rugged/credentials.rb +43 -0
- data/lib/rugged/diff.rb +20 -0
- data/lib/rugged/diff/delta.rb +53 -0
- data/lib/rugged/diff/hunk.rb +18 -0
- data/lib/rugged/diff/line.rb +47 -0
- data/lib/rugged/index.rb +13 -0
- data/lib/rugged/object.rb +7 -0
- data/lib/rugged/patch.rb +36 -0
- data/lib/rugged/reference.rb +7 -0
- data/lib/rugged/remote.rb +4 -0
- data/lib/rugged/repository.rb +227 -0
- data/lib/rugged/submodule_collection.rb +48 -0
- data/lib/rugged/tag.rb +50 -0
- data/lib/rugged/tree.rb +38 -0
- data/lib/rugged/version.rb +3 -0
- data/lib/rugged/walker.rb +5 -0
- metadata +146 -0
@@ -0,0 +1,347 @@
|
|
1
|
+
/*
|
2
|
+
* The MIT License
|
3
|
+
*
|
4
|
+
* Copyright (c) 2014 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_cRuggedRepo;
|
29
|
+
extern VALUE rb_cRuggedTag;
|
30
|
+
|
31
|
+
VALUE rb_cRuggedTagCollection;
|
32
|
+
|
33
|
+
/*
|
34
|
+
* call-seq:
|
35
|
+
* TagCollection.new(repo) -> refs
|
36
|
+
*/
|
37
|
+
static VALUE rb_git_tag_collection_initialize(VALUE self, VALUE repo)
|
38
|
+
{
|
39
|
+
rugged_set_owner(self, repo);
|
40
|
+
return self;
|
41
|
+
}
|
42
|
+
|
43
|
+
/*
|
44
|
+
* call-seq:
|
45
|
+
* tags[name] -> tag
|
46
|
+
*
|
47
|
+
* Lookup a tag in +repo+, with the given +name+.
|
48
|
+
*
|
49
|
+
* +name+ can be a short or canonical tag name
|
50
|
+
* (e.g. +v0.1.0+ or +refs/tags/v0.1.0+).
|
51
|
+
*
|
52
|
+
* Returns the looked up tag, or +nil+ if the tag doesn't exist.
|
53
|
+
*/
|
54
|
+
static VALUE rb_git_tag_collection_aref(VALUE self, VALUE rb_name)
|
55
|
+
{
|
56
|
+
git_reference *tag;
|
57
|
+
git_repository *repo;
|
58
|
+
int error;
|
59
|
+
VALUE rb_repo = rugged_owner(self);
|
60
|
+
|
61
|
+
rugged_check_repo(rb_repo);
|
62
|
+
Data_Get_Struct(rb_repo, git_repository, repo);
|
63
|
+
|
64
|
+
Check_Type(rb_name, T_STRING);
|
65
|
+
|
66
|
+
error = git_reference_lookup(&tag, repo, StringValueCStr(rb_name));
|
67
|
+
if (error == GIT_ENOTFOUND || error == GIT_EINVALIDSPEC) {
|
68
|
+
char *canonical_ref = xmalloc((RSTRING_LEN(rb_name) + strlen("refs/tags/") + 1) * sizeof(char));
|
69
|
+
strcpy(canonical_ref, "refs/tags/");
|
70
|
+
strcat(canonical_ref, StringValueCStr(rb_name));
|
71
|
+
|
72
|
+
error = git_reference_lookup(&tag, repo, canonical_ref);
|
73
|
+
xfree(canonical_ref);
|
74
|
+
if (error == GIT_ENOTFOUND)
|
75
|
+
return Qnil;
|
76
|
+
}
|
77
|
+
|
78
|
+
rugged_exception_check(error);
|
79
|
+
|
80
|
+
return rugged_ref_new(rb_cRuggedTag, rb_repo, tag);
|
81
|
+
}
|
82
|
+
|
83
|
+
/*
|
84
|
+
* call-seq:
|
85
|
+
* tags.delete(name) -> nil
|
86
|
+
*
|
87
|
+
* Delete the tag reference identified by +name+.
|
88
|
+
*/
|
89
|
+
static VALUE rb_git_tag_collection_delete(VALUE self, VALUE rb_name)
|
90
|
+
{
|
91
|
+
VALUE rb_repo = rugged_owner(self);
|
92
|
+
git_repository *repo;
|
93
|
+
int error;
|
94
|
+
|
95
|
+
rugged_check_repo(rb_repo);
|
96
|
+
Data_Get_Struct(rb_repo, git_repository, repo);
|
97
|
+
|
98
|
+
Check_Type(rb_name, T_STRING);
|
99
|
+
|
100
|
+
error = git_tag_delete(repo, StringValueCStr(rb_name));
|
101
|
+
rugged_exception_check(error);
|
102
|
+
return Qnil;
|
103
|
+
}
|
104
|
+
|
105
|
+
/*
|
106
|
+
* call-seq:
|
107
|
+
* tags.create(name, target[, force = false][, annotation = nil]) -> oid
|
108
|
+
*
|
109
|
+
* Create a new tag with the specified +name+ on +target+ in +repo+.
|
110
|
+
*
|
111
|
+
* If +annotation+ is not +nil+, it will cause the creation of an annotated tag object.
|
112
|
+
* +annotation+ has to contain the following key value pairs:
|
113
|
+
*
|
114
|
+
* :tagger ::
|
115
|
+
* An optional Hash containing a git signature. Defaults to the signature
|
116
|
+
* from the configuration if only `:message` is given. Will cause the
|
117
|
+
* creation of an annotated tag object if present.
|
118
|
+
*
|
119
|
+
* :message ::
|
120
|
+
* An optional string containing the message for the new tag.
|
121
|
+
*
|
122
|
+
* Returns the OID of the newly created tag.
|
123
|
+
*/
|
124
|
+
static VALUE rb_git_tag_collection_create(int argc, VALUE *argv, VALUE self)
|
125
|
+
{
|
126
|
+
git_oid tag_oid;
|
127
|
+
git_repository *repo = NULL;
|
128
|
+
git_object *target = NULL;
|
129
|
+
int error, force = 0;
|
130
|
+
|
131
|
+
VALUE rb_repo = rugged_owner(self), rb_name, rb_target, rb_force, rb_annotation;
|
132
|
+
|
133
|
+
rb_scan_args(argc, argv, "21:", &rb_name, &rb_target, &rb_force, &rb_annotation);
|
134
|
+
|
135
|
+
rugged_check_repo(rb_repo);
|
136
|
+
Data_Get_Struct(rb_repo, git_repository, repo);
|
137
|
+
|
138
|
+
Check_Type(rb_name, T_STRING);
|
139
|
+
|
140
|
+
if (!NIL_P(rb_force))
|
141
|
+
force = rugged_parse_bool(rb_force);
|
142
|
+
|
143
|
+
target = rugged_object_get(repo, rb_target, GIT_OBJ_ANY);
|
144
|
+
|
145
|
+
if (NIL_P(rb_annotation)) {
|
146
|
+
error = git_tag_create_lightweight(
|
147
|
+
&tag_oid,
|
148
|
+
repo,
|
149
|
+
StringValueCStr(rb_name),
|
150
|
+
target,
|
151
|
+
force
|
152
|
+
);
|
153
|
+
} else {
|
154
|
+
git_signature *tagger = rugged_signature_get(
|
155
|
+
rb_hash_aref(rb_annotation, CSTR2SYM("tagger")), repo
|
156
|
+
);
|
157
|
+
VALUE rb_message = rb_hash_aref(rb_annotation, CSTR2SYM("message"));
|
158
|
+
|
159
|
+
Check_Type(rb_message, T_STRING);
|
160
|
+
|
161
|
+
error = git_tag_create(
|
162
|
+
&tag_oid,
|
163
|
+
repo,
|
164
|
+
StringValueCStr(rb_name),
|
165
|
+
target,
|
166
|
+
tagger,
|
167
|
+
StringValueCStr(rb_message),
|
168
|
+
force
|
169
|
+
);
|
170
|
+
|
171
|
+
git_signature_free(tagger);
|
172
|
+
}
|
173
|
+
|
174
|
+
git_object_free(target);
|
175
|
+
rugged_exception_check(error);
|
176
|
+
|
177
|
+
return rb_git_tag_collection_aref(self, rb_name);
|
178
|
+
}
|
179
|
+
|
180
|
+
/*
|
181
|
+
* call-seq:
|
182
|
+
* tags.create_annotation(name, target, annotation) -> annotation
|
183
|
+
*
|
184
|
+
* Create a new annotated tag object with the specified +name+ on +target+ in
|
185
|
+
* +repo+.
|
186
|
+
*
|
187
|
+
* Unlike the +create+ method, +create_annotation+ simply creates a tag
|
188
|
+
* object. It does not write a tag ref.
|
189
|
+
*
|
190
|
+
* +annotation+ must have the following keys:
|
191
|
+
*
|
192
|
+
* :tagger ::
|
193
|
+
* An optional Hash containing a git signature. Defaults to the signature
|
194
|
+
* from the configuration if only `:message` is given. Will cause the
|
195
|
+
* creation of an annotated tag object if present.
|
196
|
+
*
|
197
|
+
* :message ::
|
198
|
+
* An optional string containing the message for the new tag.
|
199
|
+
*
|
200
|
+
* Returns an instance of Rugged::Tag::Annotation representing the newly
|
201
|
+
* created annotation.
|
202
|
+
*/
|
203
|
+
static VALUE rb_git_tag_collection_create_annotation(VALUE self, VALUE rb_name, VALUE rb_target, VALUE rb_annotation)
|
204
|
+
{
|
205
|
+
git_oid tag_oid;
|
206
|
+
git_repository *repo = NULL;
|
207
|
+
git_object *target = NULL, *tag = NULL;
|
208
|
+
git_signature *tagger = NULL;
|
209
|
+
VALUE rb_message;
|
210
|
+
int error;
|
211
|
+
|
212
|
+
VALUE rb_repo = rugged_owner(self);
|
213
|
+
rugged_check_repo(rb_repo);
|
214
|
+
Data_Get_Struct(rb_repo, git_repository, repo);
|
215
|
+
|
216
|
+
Check_Type(rb_name, T_STRING);
|
217
|
+
|
218
|
+
target = rugged_object_get(repo, rb_target, GIT_OBJ_ANY);
|
219
|
+
|
220
|
+
rb_message = rb_hash_aref(rb_annotation, CSTR2SYM("message"));
|
221
|
+
Check_Type(rb_message, T_STRING);
|
222
|
+
|
223
|
+
tagger = rugged_signature_get(
|
224
|
+
rb_hash_aref(rb_annotation, CSTR2SYM("tagger")), repo
|
225
|
+
);
|
226
|
+
|
227
|
+
error = git_tag_annotation_create(
|
228
|
+
&tag_oid,
|
229
|
+
repo,
|
230
|
+
StringValueCStr(rb_name),
|
231
|
+
target,
|
232
|
+
tagger,
|
233
|
+
StringValueCStr(rb_message)
|
234
|
+
);
|
235
|
+
|
236
|
+
git_object_free(target);
|
237
|
+
git_signature_free(tagger);
|
238
|
+
|
239
|
+
rugged_exception_check(error);
|
240
|
+
|
241
|
+
error = git_object_lookup(&tag, repo, &tag_oid, GIT_OBJ_TAG);
|
242
|
+
rugged_exception_check(error);
|
243
|
+
|
244
|
+
return rugged_object_new(rb_repo, tag);
|
245
|
+
}
|
246
|
+
|
247
|
+
static VALUE each_tag(int argc, VALUE *argv, VALUE self, int tag_names_only)
|
248
|
+
{
|
249
|
+
git_repository *repo;
|
250
|
+
git_strarray tags;
|
251
|
+
size_t i;
|
252
|
+
int error, exception = 0;
|
253
|
+
VALUE rb_repo = rugged_owner(self), rb_pattern;
|
254
|
+
const char *pattern = NULL;
|
255
|
+
|
256
|
+
rb_scan_args(argc, argv, "01", &rb_pattern);
|
257
|
+
|
258
|
+
if (!rb_block_given_p()) {
|
259
|
+
VALUE symbol = tag_names_only ? CSTR2SYM("each_name") : CSTR2SYM("each");
|
260
|
+
return rb_funcall(self, rb_intern("to_enum"), 2, symbol, rb_pattern);
|
261
|
+
}
|
262
|
+
|
263
|
+
if (!NIL_P(rb_pattern)) {
|
264
|
+
Check_Type(rb_pattern, T_STRING);
|
265
|
+
pattern = StringValueCStr(rb_pattern);
|
266
|
+
}
|
267
|
+
|
268
|
+
rugged_check_repo(rb_repo);
|
269
|
+
Data_Get_Struct(rb_repo, git_repository, repo);
|
270
|
+
|
271
|
+
error = git_tag_list_match(&tags, pattern ? pattern : "", repo);
|
272
|
+
rugged_exception_check(error);
|
273
|
+
|
274
|
+
if (tag_names_only) {
|
275
|
+
for (i = 0; !exception && i < tags.count; ++i)
|
276
|
+
rb_protect(rb_yield, rb_str_new_utf8(tags.strings[i]), &exception);
|
277
|
+
} else {
|
278
|
+
for (i = 0; !exception && i < tags.count; ++i) {
|
279
|
+
rb_protect(rb_yield, rb_git_tag_collection_aref(self,
|
280
|
+
rb_str_new_utf8(tags.strings[i])), &exception);
|
281
|
+
}
|
282
|
+
}
|
283
|
+
|
284
|
+
git_strarray_free(&tags);
|
285
|
+
|
286
|
+
if (exception)
|
287
|
+
rb_jump_tag(exception);
|
288
|
+
|
289
|
+
return Qnil;
|
290
|
+
}
|
291
|
+
|
292
|
+
/*
|
293
|
+
* call-seq:
|
294
|
+
* tags.each_name([pattern]) { |name| block } -> nil
|
295
|
+
* tags.each_name([pattern]) -> enumerator
|
296
|
+
*
|
297
|
+
* Iterate through all the tag names in +repo+. Iteration
|
298
|
+
* can be optionally filtered to the ones matching the given
|
299
|
+
* +pattern+, a standard Unix filename glob.
|
300
|
+
*
|
301
|
+
* If +pattern+ is empty or not given, all tag names will be returned.
|
302
|
+
*
|
303
|
+
* The given block will be called once with the name for each tag.
|
304
|
+
*
|
305
|
+
* If no block is given, an enumerator will be returned.
|
306
|
+
*/
|
307
|
+
static VALUE rb_git_tag_collection_each_name(int argc, VALUE *argv, VALUE self)
|
308
|
+
{
|
309
|
+
return each_tag(argc, argv, self, 1);
|
310
|
+
}
|
311
|
+
|
312
|
+
/*
|
313
|
+
* call-seq:
|
314
|
+
* tags.each([pattern]) { |name| block } -> nil
|
315
|
+
* tags.each([pattern]) -> enumerator
|
316
|
+
*
|
317
|
+
* Iterate through all the tags in +repo+. Iteration
|
318
|
+
* can be optionally filtered to the ones matching the given
|
319
|
+
* +pattern+, a standard Unix filename glob.
|
320
|
+
*
|
321
|
+
* If +pattern+ is empty or not given, all tag names will be returned.
|
322
|
+
*
|
323
|
+
* The given block will be called once with the name for each tag.
|
324
|
+
*
|
325
|
+
* If no block is given, an enumerator will be returned.
|
326
|
+
*/
|
327
|
+
static VALUE rb_git_tag_collection_each(int argc, VALUE *argv, VALUE self)
|
328
|
+
{
|
329
|
+
return each_tag(argc, argv, self, 0);
|
330
|
+
}
|
331
|
+
|
332
|
+
void Init_rugged_tag_collection(void)
|
333
|
+
{
|
334
|
+
rb_cRuggedTagCollection = rb_define_class_under(rb_mRugged, "TagCollection", rb_cObject);
|
335
|
+
rb_include_module(rb_cRuggedTagCollection, rb_mEnumerable);
|
336
|
+
|
337
|
+
rb_define_method(rb_cRuggedTagCollection, "initialize", rb_git_tag_collection_initialize, 1);
|
338
|
+
|
339
|
+
rb_define_method(rb_cRuggedTagCollection, "create", rb_git_tag_collection_create, -1);
|
340
|
+
rb_define_method(rb_cRuggedTagCollection, "create_annotation", rb_git_tag_collection_create_annotation, 3);
|
341
|
+
rb_define_method(rb_cRuggedTagCollection, "[]", rb_git_tag_collection_aref, 1);
|
342
|
+
|
343
|
+
rb_define_method(rb_cRuggedTagCollection, "each", rb_git_tag_collection_each, -1);
|
344
|
+
rb_define_method(rb_cRuggedTagCollection, "each_name", rb_git_tag_collection_each_name, -1);
|
345
|
+
|
346
|
+
rb_define_method(rb_cRuggedTagCollection, "delete", rb_git_tag_collection_delete, 1);
|
347
|
+
}
|
@@ -0,0 +1,919 @@
|
|
1
|
+
/*
|
2
|
+
* The MIT License
|
3
|
+
*
|
4
|
+
* Copyright (c) 2014 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_cRuggedObject;
|
29
|
+
extern VALUE rb_cRuggedRepo;
|
30
|
+
extern VALUE rb_cRuggedDiff;
|
31
|
+
extern VALUE rb_cRuggedIndex;
|
32
|
+
extern VALUE rb_cRuggedCommit;
|
33
|
+
|
34
|
+
VALUE rb_cRuggedTree;
|
35
|
+
VALUE rb_cRuggedTreeBuilder;
|
36
|
+
|
37
|
+
static VALUE rb_git_treeentry_fromC(const git_tree_entry *entry)
|
38
|
+
{
|
39
|
+
VALUE rb_entry;
|
40
|
+
VALUE type;
|
41
|
+
|
42
|
+
if (!entry)
|
43
|
+
return Qnil;
|
44
|
+
|
45
|
+
rb_entry = rb_hash_new();
|
46
|
+
|
47
|
+
rb_hash_aset(rb_entry, CSTR2SYM("name"), rb_str_new_utf8(git_tree_entry_name(entry)));
|
48
|
+
rb_hash_aset(rb_entry, CSTR2SYM("oid"), rugged_create_oid(git_tree_entry_id(entry)));
|
49
|
+
|
50
|
+
rb_hash_aset(rb_entry, CSTR2SYM("filemode"), INT2FIX(git_tree_entry_filemode(entry)));
|
51
|
+
|
52
|
+
switch(git_tree_entry_type(entry)) {
|
53
|
+
case GIT_OBJ_TREE:
|
54
|
+
type = CSTR2SYM("tree");
|
55
|
+
break;
|
56
|
+
|
57
|
+
case GIT_OBJ_BLOB:
|
58
|
+
type = CSTR2SYM("blob");
|
59
|
+
break;
|
60
|
+
|
61
|
+
case GIT_OBJ_COMMIT:
|
62
|
+
type = CSTR2SYM("commit");
|
63
|
+
break;
|
64
|
+
|
65
|
+
default:
|
66
|
+
type = Qnil;
|
67
|
+
break;
|
68
|
+
}
|
69
|
+
rb_hash_aset(rb_entry, CSTR2SYM("type"), type);
|
70
|
+
|
71
|
+
return rb_entry;
|
72
|
+
}
|
73
|
+
|
74
|
+
/*
|
75
|
+
* Rugged Tree
|
76
|
+
*/
|
77
|
+
|
78
|
+
/*
|
79
|
+
* call-seq:
|
80
|
+
* tree.count -> count
|
81
|
+
* tree.length -> count
|
82
|
+
*
|
83
|
+
* Return the number of entries contained in the tree.
|
84
|
+
*
|
85
|
+
* Note that this only applies to entries in the root of the tree,
|
86
|
+
* not any other entries contained in sub-folders.
|
87
|
+
*/
|
88
|
+
static VALUE rb_git_tree_entrycount(VALUE self)
|
89
|
+
{
|
90
|
+
git_tree *tree;
|
91
|
+
Data_Get_Struct(self, git_tree, tree);
|
92
|
+
|
93
|
+
return INT2FIX(git_tree_entrycount(tree));
|
94
|
+
}
|
95
|
+
|
96
|
+
struct rugged_treecount_cb_payload
|
97
|
+
{
|
98
|
+
int count;
|
99
|
+
int limit;
|
100
|
+
};
|
101
|
+
|
102
|
+
static int rugged__treecount_cb(const char *root, const git_tree_entry *entry, void *data)
|
103
|
+
{
|
104
|
+
struct rugged_treecount_cb_payload *payload = data;
|
105
|
+
|
106
|
+
if (payload->limit >= 0 && payload->count >= payload->limit) {
|
107
|
+
return -1;
|
108
|
+
} else if(git_tree_entry_type(entry) == GIT_OBJ_TREE) {
|
109
|
+
return 0;
|
110
|
+
} else {
|
111
|
+
++(payload->count);
|
112
|
+
return 1;
|
113
|
+
}
|
114
|
+
}
|
115
|
+
|
116
|
+
/*
|
117
|
+
* call-seq:
|
118
|
+
* tree.count_recursive(limit=nil) -> count
|
119
|
+
*
|
120
|
+
* `limit` - The maximum number of blobs to the count in the repository.
|
121
|
+
* Rugged will stop walking the tree after `limit` items to avoid long
|
122
|
+
* execution times.
|
123
|
+
*
|
124
|
+
* Return the number of blobs (up to the limit) contained in the tree and
|
125
|
+
* all subtrees.
|
126
|
+
*/
|
127
|
+
static VALUE rb_git_tree_entrycount_recursive(int argc, VALUE* argv, VALUE self)
|
128
|
+
{
|
129
|
+
git_tree *tree;
|
130
|
+
int error;
|
131
|
+
struct rugged_treecount_cb_payload payload;
|
132
|
+
VALUE rb_limit;
|
133
|
+
|
134
|
+
Data_Get_Struct(self, git_tree, tree);
|
135
|
+
|
136
|
+
rb_scan_args(argc, argv, "01", &rb_limit);
|
137
|
+
|
138
|
+
payload.limit = -1;
|
139
|
+
payload.count = 0;
|
140
|
+
|
141
|
+
if (!NIL_P(rb_limit)) {
|
142
|
+
Check_Type(rb_limit, T_FIXNUM);
|
143
|
+
payload.limit = FIX2INT(rb_limit);
|
144
|
+
}
|
145
|
+
|
146
|
+
|
147
|
+
error = git_tree_walk(tree, GIT_TREEWALK_PRE, &rugged__treecount_cb, (void *)&payload);
|
148
|
+
|
149
|
+
if (error && giterr_last()->klass == GITERR_CALLBACK) {
|
150
|
+
giterr_clear();
|
151
|
+
error = 0;
|
152
|
+
}
|
153
|
+
|
154
|
+
rugged_exception_check(error);
|
155
|
+
|
156
|
+
return INT2FIX(payload.count);
|
157
|
+
}
|
158
|
+
|
159
|
+
/*
|
160
|
+
* call-seq:
|
161
|
+
* tree[e] -> entry
|
162
|
+
* tree.get_entry(e) -> entry
|
163
|
+
*
|
164
|
+
* Return one of the entries from a tree as a +Hash+. If +e+ is a number, the +e+nth entry
|
165
|
+
* from the tree will be returned. If +e+ is a string, the entry with that name
|
166
|
+
* will be returned.
|
167
|
+
*
|
168
|
+
* If the entry doesn't exist, +nil+ will be returned.
|
169
|
+
*
|
170
|
+
* tree[3] #=> {:name => "foo.txt", :type => :blob, :oid => "d8786bfc97485e8d7b19b21fb88c8ef1f199fc3f", :filemode => 0}
|
171
|
+
* tree['bar.txt'] #=> {:name => "bar.txt", :type => :blob, :oid => "de5ba987198bcf2518885f0fc1350e5172cded78", :filemode => 0}
|
172
|
+
* tree['baz.txt'] #=> nil
|
173
|
+
*/
|
174
|
+
static VALUE rb_git_tree_get_entry(VALUE self, VALUE entry_id)
|
175
|
+
{
|
176
|
+
git_tree *tree;
|
177
|
+
Data_Get_Struct(self, git_tree, tree);
|
178
|
+
|
179
|
+
if (TYPE(entry_id) == T_FIXNUM)
|
180
|
+
return rb_git_treeentry_fromC(git_tree_entry_byindex(tree, FIX2INT(entry_id)));
|
181
|
+
|
182
|
+
else if (TYPE(entry_id) == T_STRING)
|
183
|
+
return rb_git_treeentry_fromC(git_tree_entry_byname(tree, StringValueCStr(entry_id)));
|
184
|
+
|
185
|
+
else
|
186
|
+
rb_raise(rb_eTypeError, "entry_id must be either an index or a filename");
|
187
|
+
}
|
188
|
+
|
189
|
+
/*
|
190
|
+
* call-seq:
|
191
|
+
* tree.get_entry_by_oid(rb_oid) -> entry
|
192
|
+
*
|
193
|
+
* Return one of the entries from a tree as a +Hash+, based off the oid SHA.
|
194
|
+
*
|
195
|
+
* If the entry doesn't exist, +nil+ will be returned.
|
196
|
+
*
|
197
|
+
* This does a full traversal of the every element in the tree, so this method
|
198
|
+
* is not especially fast.
|
199
|
+
*
|
200
|
+
* tree.get_entry_by_oid("d8786bfc97485e8d7b19b21fb88c8ef1f199fc3f")
|
201
|
+
* #=> {:name => "foo.txt", :type => :blob, :oid => "d8786bfc97485e8d7b19b21fb88c8ef1f199fc3f", :filemode => 0}
|
202
|
+
*
|
203
|
+
*/
|
204
|
+
static VALUE rb_git_tree_get_entry_by_oid(VALUE self, VALUE rb_oid)
|
205
|
+
{
|
206
|
+
git_tree *tree;
|
207
|
+
git_oid oid;
|
208
|
+
Data_Get_Struct(self, git_tree, tree);
|
209
|
+
|
210
|
+
Check_Type(rb_oid, T_STRING);
|
211
|
+
rugged_exception_check(git_oid_fromstr(&oid, StringValueCStr(rb_oid)));
|
212
|
+
|
213
|
+
return rb_git_treeentry_fromC(git_tree_entry_byid(tree, &oid));
|
214
|
+
}
|
215
|
+
|
216
|
+
/*
|
217
|
+
* call-seq:
|
218
|
+
* tree.each { |entry| block }
|
219
|
+
* tree.each -> enumerator
|
220
|
+
*
|
221
|
+
* Call +block+ with each of the entries of the subtree as a +Hash+. If no +block+
|
222
|
+
* is given, an +enumerator+ is returned instead.
|
223
|
+
*
|
224
|
+
* Note that only the entries in the root of the tree are yielded; if you need to
|
225
|
+
* list also entries in subfolders, use +tree.walk+ instead.
|
226
|
+
*
|
227
|
+
* tree.each { |entry| puts entry.inspect }
|
228
|
+
*
|
229
|
+
* generates:
|
230
|
+
*
|
231
|
+
* {:name => "foo.txt", :type => :blob, :oid => "d8786bfc97485e8d7b19b21fb88c8ef1f199fc3f", :filemode => 0}
|
232
|
+
* {:name => "bar.txt", :type => :blob, :oid => "de5ba987198bcf2518885f0fc1350e5172cded78", :filemode => 0}
|
233
|
+
* ...
|
234
|
+
*/
|
235
|
+
static VALUE rb_git_tree_each(VALUE self)
|
236
|
+
{
|
237
|
+
git_tree *tree;
|
238
|
+
size_t i, count;
|
239
|
+
Data_Get_Struct(self, git_tree, tree);
|
240
|
+
|
241
|
+
if (!rb_block_given_p())
|
242
|
+
return rb_funcall(self, rb_intern("to_enum"), 0);
|
243
|
+
|
244
|
+
count = git_tree_entrycount(tree);
|
245
|
+
|
246
|
+
for (i = 0; i < count; ++i) {
|
247
|
+
const git_tree_entry *entry = git_tree_entry_byindex(tree, i);
|
248
|
+
rb_yield(rb_git_treeentry_fromC(entry));
|
249
|
+
}
|
250
|
+
|
251
|
+
return Qnil;
|
252
|
+
}
|
253
|
+
|
254
|
+
static int rugged__treewalk_cb(const char *root, const git_tree_entry *entry, void *payload)
|
255
|
+
{
|
256
|
+
int *exception = (int *)payload;
|
257
|
+
|
258
|
+
VALUE rb_result, rb_args = rb_ary_new2(2);
|
259
|
+
|
260
|
+
rb_ary_push(rb_args, rb_str_new_utf8(root));
|
261
|
+
rb_ary_push(rb_args, rb_git_treeentry_fromC(entry));
|
262
|
+
|
263
|
+
rb_result = rb_protect(rb_yield_splat, rb_args, exception);
|
264
|
+
|
265
|
+
if (*exception)
|
266
|
+
return -1;
|
267
|
+
|
268
|
+
/* skip entry when 'false' is returned */
|
269
|
+
if (TYPE(rb_result) == T_FALSE)
|
270
|
+
return 1;
|
271
|
+
|
272
|
+
/* otherwise continue normal iteration */
|
273
|
+
return 0;
|
274
|
+
}
|
275
|
+
|
276
|
+
/*
|
277
|
+
* call-seq:
|
278
|
+
* tree.walk(mode) { |root, entry| block }
|
279
|
+
* tree.walk(mode) -> Iterator
|
280
|
+
*
|
281
|
+
* Walk +tree+ with the given mode (either +:preorder+ or +:postorder+) and yield
|
282
|
+
* to +block+ every entry in +tree+ and all its subtrees, as a +Hash+. The +block+
|
283
|
+
* also takes a +root+, the relative path in the traversal, starting from the root
|
284
|
+
* of the original tree.
|
285
|
+
*
|
286
|
+
* If the +block+ returns a falsy value, that entry and its sub-entries (in the case
|
287
|
+
* of a folder) will be skipped for the iteration.
|
288
|
+
*
|
289
|
+
* If no +block+ is given, an +Iterator+ is returned instead.
|
290
|
+
*
|
291
|
+
* tree.walk(:postorder) { |root, entry| puts "#{root}#{entry[:name]} [#{entry[:oid]}]" }
|
292
|
+
*
|
293
|
+
* generates:
|
294
|
+
*
|
295
|
+
* USAGE.rb [02bae86c91f96b5fdb6b1cf06f5aa3612139e318]
|
296
|
+
* ext [23f135b3c576b6ac4785821888991d7089f35db1]
|
297
|
+
* ext/rugged [25c88faa9302e34e16664eb9c990deb2bcf77849]
|
298
|
+
* ext/rugged/extconf.rb [40c1aa8a8cec8ca444ed5758e3f00ecff093070a]
|
299
|
+
* ...
|
300
|
+
*/
|
301
|
+
static VALUE rb_git_tree_walk(VALUE self, VALUE rb_mode)
|
302
|
+
{
|
303
|
+
git_tree *tree;
|
304
|
+
int error, mode = 0, exception = 0;
|
305
|
+
ID id_mode;
|
306
|
+
|
307
|
+
Data_Get_Struct(self, git_tree, tree);
|
308
|
+
|
309
|
+
if (!rb_block_given_p())
|
310
|
+
return rb_funcall(self, rb_intern("to_enum"), 2, CSTR2SYM("walk"), rb_mode);
|
311
|
+
|
312
|
+
Check_Type(rb_mode, T_SYMBOL);
|
313
|
+
id_mode = SYM2ID(rb_mode);
|
314
|
+
|
315
|
+
if (id_mode == rb_intern("preorder"))
|
316
|
+
mode = GIT_TREEWALK_PRE;
|
317
|
+
else if (id_mode == rb_intern("postorder"))
|
318
|
+
mode = GIT_TREEWALK_POST;
|
319
|
+
else
|
320
|
+
rb_raise(rb_eTypeError,
|
321
|
+
"Invalid iteration mode. Expected `:preorder` or `:postorder`");
|
322
|
+
|
323
|
+
error = git_tree_walk(tree, mode, &rugged__treewalk_cb, (void *)&exception);
|
324
|
+
|
325
|
+
if (exception)
|
326
|
+
rb_jump_tag(exception);
|
327
|
+
|
328
|
+
rugged_exception_check(error);
|
329
|
+
|
330
|
+
return Qnil;
|
331
|
+
}
|
332
|
+
|
333
|
+
/*
|
334
|
+
* call-seq:
|
335
|
+
* tree.path(path) -> entry
|
336
|
+
*
|
337
|
+
* Retrieve and return a tree entry by its relative path.
|
338
|
+
*/
|
339
|
+
static VALUE rb_git_tree_path(VALUE self, VALUE rb_path)
|
340
|
+
{
|
341
|
+
int error;
|
342
|
+
git_tree *tree;
|
343
|
+
git_tree_entry *entry;
|
344
|
+
VALUE rb_entry;
|
345
|
+
Data_Get_Struct(self, git_tree, tree);
|
346
|
+
Check_Type(rb_path, T_STRING);
|
347
|
+
|
348
|
+
error = git_tree_entry_bypath(&entry, tree, StringValueCStr(rb_path));
|
349
|
+
rugged_exception_check(error);
|
350
|
+
|
351
|
+
rb_entry = rb_git_treeentry_fromC(entry);
|
352
|
+
git_tree_entry_free(entry);
|
353
|
+
|
354
|
+
return rb_entry;
|
355
|
+
}
|
356
|
+
|
357
|
+
/*
|
358
|
+
* call-seq:
|
359
|
+
* Tree.diff(repo, tree, diffable[, options]) -> diff
|
360
|
+
*
|
361
|
+
* Returns a diff between the `tree` and the diffable object that was given.
|
362
|
+
* +diffable+ can either be a +Rugged::Commit+, a +Rugged::Tree+, a +Rugged::Index+,
|
363
|
+
* or +nil+.
|
364
|
+
*
|
365
|
+
* The +tree+ object will be used as the "old file" side of the diff, while the
|
366
|
+
* parent tree or the +diffable+ object will be used for the "new file" side.
|
367
|
+
*
|
368
|
+
* If +tree+ or +diffable+ are nil, they will be treated as an empty tree. Passing
|
369
|
+
* both as `nil` will raise an exception.
|
370
|
+
*
|
371
|
+
* The following options can be passed in the +options+ Hash:
|
372
|
+
*
|
373
|
+
* :paths ::
|
374
|
+
* An array of paths / fnmatch patterns to constrain the diff to a specific
|
375
|
+
* set of files. Also see +:disable_pathspec_match+.
|
376
|
+
*
|
377
|
+
* :max_size ::
|
378
|
+
* An integer specifying the maximum byte size of a file before a it will
|
379
|
+
* be treated as binary. The default value is 512MB.
|
380
|
+
*
|
381
|
+
* :context_lines ::
|
382
|
+
* The number of unchanged lines that define the boundary of a hunk (and
|
383
|
+
* to display before and after the actual changes). The default is 3.
|
384
|
+
*
|
385
|
+
* :interhunk_lines ::
|
386
|
+
* The maximum number of unchanged lines between hunk boundaries before the hunks
|
387
|
+
* will be merged into a one. The default is 0.
|
388
|
+
*
|
389
|
+
* :old_prefix ::
|
390
|
+
* The virtual "directory" to prefix to old filenames in hunk headers.
|
391
|
+
* The default is "a".
|
392
|
+
*
|
393
|
+
* :new_prefix ::
|
394
|
+
* The virtual "directory" to prefix to new filenames in hunk headers.
|
395
|
+
* The default is "b".
|
396
|
+
*
|
397
|
+
* :reverse ::
|
398
|
+
* If true, the sides of the diff will be reversed.
|
399
|
+
*
|
400
|
+
* :force_text ::
|
401
|
+
* If true, all files will be treated as text, disabling binary attributes & detection.
|
402
|
+
*
|
403
|
+
* :ignore_whitespace ::
|
404
|
+
* If true, all whitespace will be ignored.
|
405
|
+
*
|
406
|
+
* :ignore_whitespace_change ::
|
407
|
+
* If true, changes in amount of whitespace will be ignored.
|
408
|
+
*
|
409
|
+
* :ignore_whitespace_eol ::
|
410
|
+
* If true, whitespace at end of line will be ignored.
|
411
|
+
*
|
412
|
+
* :ignore_submodules ::
|
413
|
+
* if true, submodules will be excluded from the diff completely.
|
414
|
+
*
|
415
|
+
* :patience ::
|
416
|
+
* If true, the "patience diff" algorithm will be used (currenlty unimplemented).
|
417
|
+
*
|
418
|
+
* :include_ignored ::
|
419
|
+
* If true, ignored files will be included in the diff.
|
420
|
+
*
|
421
|
+
* :include_untracked ::
|
422
|
+
* If true, untracked files will be included in the diff.
|
423
|
+
*
|
424
|
+
* :include_unmodified ::
|
425
|
+
* If true, unmodified files will be included in the diff.
|
426
|
+
*
|
427
|
+
* :recurse_untracked_dirs ::
|
428
|
+
* Even if +:include_untracked+ is true, untracked directories will only be
|
429
|
+
* marked with a single entry in the diff. If this flag is set to true,
|
430
|
+
* all files under ignored directories will be included in the diff, too.
|
431
|
+
*
|
432
|
+
* :disable_pathspec_match ::
|
433
|
+
* If true, the given +:paths+ will be applied as exact matches, instead of
|
434
|
+
* as fnmatch patterns.
|
435
|
+
*
|
436
|
+
* :deltas_are_icase ::
|
437
|
+
* If true, filename comparisons will be made with case-insensitivity.
|
438
|
+
*
|
439
|
+
* :include_untracked_content ::
|
440
|
+
* if true, untracked content will be contained in the the diff patch text.
|
441
|
+
*
|
442
|
+
* :skip_binary_check ::
|
443
|
+
* If true, diff deltas will be generated without spending time on binary
|
444
|
+
* detection. This is useful to improve performance in cases where the actual
|
445
|
+
* file content difference is not needed.
|
446
|
+
*
|
447
|
+
* :include_typechange ::
|
448
|
+
* If true, type changes for files will not be interpreted as deletion of
|
449
|
+
* the "old file" and addition of the "new file", but will generate
|
450
|
+
* typechange records.
|
451
|
+
*
|
452
|
+
* :include_typechange_trees ::
|
453
|
+
* Even if +:include_typechange+ is true, blob -> tree changes will still
|
454
|
+
* usually be handled as a deletion of the blob. If this flag is set to true,
|
455
|
+
* blob -> tree changes will be marked as typechanges.
|
456
|
+
*
|
457
|
+
* :ignore_filemode ::
|
458
|
+
* If true, file mode changes will be ignored.
|
459
|
+
*
|
460
|
+
* :recurse_ignored_dirs ::
|
461
|
+
* Even if +:include_ignored+ is true, ignored directories will only be
|
462
|
+
* marked with a single entry in the diff. If this flag is set to true,
|
463
|
+
* all files under ignored directories will be included in the diff, too.
|
464
|
+
*
|
465
|
+
* Examples:
|
466
|
+
*
|
467
|
+
* # Emulating `git diff <treeish>`
|
468
|
+
* tree = Rugged::Tree.lookup(repo, "d70d245ed97ed2aa596dd1af6536e4bfdb047b69")
|
469
|
+
* diff = tree.diff(repo.index)
|
470
|
+
* diff.merge!(tree.diff)
|
471
|
+
*
|
472
|
+
* # Tree-to-Tree Diff
|
473
|
+
* tree = Rugged::Tree.lookup(repo, "d70d245ed97ed2aa596dd1af6536e4bfdb047b69")
|
474
|
+
* other_tree = Rugged::Tree.lookup(repo, "7a9e0b02e63179929fed24f0a3e0f19168114d10")
|
475
|
+
* diff = tree.diff(other_tree)
|
476
|
+
*/
|
477
|
+
static VALUE rb_git_tree_diff_(int argc, VALUE *argv, VALUE self)
|
478
|
+
{
|
479
|
+
git_tree *tree = NULL;
|
480
|
+
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
481
|
+
git_repository *repo = NULL;
|
482
|
+
git_diff *diff = NULL;
|
483
|
+
VALUE rb_self, rb_repo, rb_other, rb_options;
|
484
|
+
int error;
|
485
|
+
|
486
|
+
rb_scan_args(argc, argv, "22", &rb_repo, &rb_self, &rb_other, &rb_options);
|
487
|
+
rugged_parse_diff_options(&opts, rb_options);
|
488
|
+
|
489
|
+
Data_Get_Struct(rb_repo, git_repository, repo);
|
490
|
+
|
491
|
+
if (!NIL_P(rb_self)) {
|
492
|
+
if (!rb_obj_is_kind_of(rb_self, rb_cRuggedTree))
|
493
|
+
rb_raise(rb_eTypeError,
|
494
|
+
"At least a Rugged::Tree object is required for diffing");
|
495
|
+
|
496
|
+
Data_Get_Struct(rb_self, git_tree, tree);
|
497
|
+
}
|
498
|
+
|
499
|
+
if (NIL_P(rb_other)) {
|
500
|
+
if (tree == NULL) {
|
501
|
+
xfree(opts.pathspec.strings);
|
502
|
+
rb_raise(rb_eTypeError, "Need 'old' or 'new' for diffing");
|
503
|
+
}
|
504
|
+
|
505
|
+
error = git_diff_tree_to_tree(&diff, repo, tree, NULL, &opts);
|
506
|
+
} else {
|
507
|
+
if (TYPE(rb_other) == T_STRING)
|
508
|
+
rb_other = rugged_object_rev_parse(rb_repo, rb_other, 1);
|
509
|
+
|
510
|
+
if (rb_obj_is_kind_of(rb_other, rb_cRuggedCommit)) {
|
511
|
+
git_tree *other_tree;
|
512
|
+
git_commit *commit;
|
513
|
+
|
514
|
+
Data_Get_Struct(rb_other, git_commit, commit);
|
515
|
+
error = git_commit_tree(&other_tree, commit);
|
516
|
+
|
517
|
+
if (!error) {
|
518
|
+
error = git_diff_tree_to_tree(&diff, repo, tree, other_tree, &opts);
|
519
|
+
git_tree_free(other_tree);
|
520
|
+
}
|
521
|
+
} else if (rb_obj_is_kind_of(rb_other, rb_cRuggedTree)) {
|
522
|
+
git_tree *other_tree;
|
523
|
+
|
524
|
+
Data_Get_Struct(rb_other, git_tree, other_tree);
|
525
|
+
error = git_diff_tree_to_tree(&diff, repo, tree, other_tree, &opts);
|
526
|
+
} else if (rb_obj_is_kind_of(rb_other, rb_cRuggedIndex)) {
|
527
|
+
git_index *index;
|
528
|
+
Data_Get_Struct(rb_other, git_index, index);
|
529
|
+
error = git_diff_tree_to_index(&diff, repo, tree, index, &opts);
|
530
|
+
} else {
|
531
|
+
xfree(opts.pathspec.strings);
|
532
|
+
rb_raise(rb_eTypeError, "A Rugged::Commit, Rugged::Tree or Rugged::Index instance is required");
|
533
|
+
}
|
534
|
+
}
|
535
|
+
|
536
|
+
xfree(opts.pathspec.strings);
|
537
|
+
rugged_exception_check(error);
|
538
|
+
|
539
|
+
return rugged_diff_new(rb_cRuggedDiff, rb_repo, diff);
|
540
|
+
}
|
541
|
+
|
542
|
+
/*
|
543
|
+
* call-seq:
|
544
|
+
* tree.diff_workdir([options]) -> diff
|
545
|
+
*
|
546
|
+
* Returns a diff between a tree and the current workdir.
|
547
|
+
*
|
548
|
+
* The +tree+ object will be used as the "old file" side of the diff, while the
|
549
|
+
* content of the current workdir will be used for the "new file" side.
|
550
|
+
*
|
551
|
+
* See Rugged::Tree#diff for a list of options that can be passed.
|
552
|
+
*/
|
553
|
+
static VALUE rb_git_tree_diff_workdir(int argc, VALUE *argv, VALUE self)
|
554
|
+
{
|
555
|
+
git_tree *tree;
|
556
|
+
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
557
|
+
git_repository *repo;
|
558
|
+
git_diff *diff;
|
559
|
+
VALUE owner, rb_options;
|
560
|
+
int error;
|
561
|
+
|
562
|
+
rb_scan_args(argc, argv, "00:", &rb_options);
|
563
|
+
rugged_parse_diff_options(&opts, rb_options);
|
564
|
+
|
565
|
+
Data_Get_Struct(self, git_tree, tree);
|
566
|
+
owner = rugged_owner(self);
|
567
|
+
Data_Get_Struct(owner, git_repository, repo);
|
568
|
+
|
569
|
+
error = git_diff_tree_to_workdir(&diff, repo, tree, &opts);
|
570
|
+
|
571
|
+
xfree(opts.pathspec.strings);
|
572
|
+
rugged_exception_check(error);
|
573
|
+
|
574
|
+
return rugged_diff_new(rb_cRuggedDiff, owner, diff);
|
575
|
+
}
|
576
|
+
|
577
|
+
void rugged_parse_merge_options(git_merge_options *opts, VALUE rb_options)
|
578
|
+
{
|
579
|
+
if (!NIL_P(rb_options)) {
|
580
|
+
VALUE rb_value;
|
581
|
+
Check_Type(rb_options, T_HASH);
|
582
|
+
|
583
|
+
rb_value = rb_hash_aref(rb_options, CSTR2SYM("rename_threshold"));
|
584
|
+
if (!NIL_P(rb_value)) {
|
585
|
+
Check_Type(rb_value, T_FIXNUM);
|
586
|
+
opts->rename_threshold = FIX2UINT(rb_value);
|
587
|
+
}
|
588
|
+
|
589
|
+
rb_value = rb_hash_aref(rb_options, CSTR2SYM("target_limit"));
|
590
|
+
if (!NIL_P(rb_value)) {
|
591
|
+
Check_Type(rb_value, T_FIXNUM);
|
592
|
+
opts->target_limit = FIX2UINT(rb_value);
|
593
|
+
}
|
594
|
+
|
595
|
+
rb_value = rb_hash_aref(rb_options, CSTR2SYM("favor"));
|
596
|
+
if (!NIL_P(rb_value)) {
|
597
|
+
ID id_favor;
|
598
|
+
|
599
|
+
Check_Type(rb_value, T_SYMBOL);
|
600
|
+
id_favor = SYM2ID(rb_value);
|
601
|
+
|
602
|
+
if (id_favor == rb_intern("normal")) {
|
603
|
+
opts->file_favor = GIT_MERGE_FILE_FAVOR_NORMAL;
|
604
|
+
} else if (id_favor == rb_intern("ours")) {
|
605
|
+
opts->file_favor = GIT_MERGE_FILE_FAVOR_OURS;
|
606
|
+
} else if (id_favor == rb_intern("theirs")) {
|
607
|
+
opts->file_favor = GIT_MERGE_FILE_FAVOR_THEIRS;
|
608
|
+
} else if (id_favor == rb_intern("union")) {
|
609
|
+
opts->file_favor = GIT_MERGE_FILE_FAVOR_UNION;
|
610
|
+
} else {
|
611
|
+
rb_raise(rb_eTypeError,
|
612
|
+
"Invalid favor mode. Expected `:normal`, `:ours`, `:theirs` or `:union`");
|
613
|
+
}
|
614
|
+
}
|
615
|
+
|
616
|
+
if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("renames")))) {
|
617
|
+
opts->flags |= GIT_MERGE_FIND_RENAMES;
|
618
|
+
}
|
619
|
+
|
620
|
+
if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("fail_on_conflict")))) {
|
621
|
+
opts->flags |= GIT_MERGE_FAIL_ON_CONFLICT;
|
622
|
+
}
|
623
|
+
|
624
|
+
if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("skip_reuc")))) {
|
625
|
+
opts->flags |= GIT_MERGE_SKIP_REUC;
|
626
|
+
}
|
627
|
+
|
628
|
+
if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("no_recursive")))) {
|
629
|
+
opts->flags |= GIT_MERGE_NO_RECURSIVE;
|
630
|
+
}
|
631
|
+
}
|
632
|
+
}
|
633
|
+
|
634
|
+
/*
|
635
|
+
* tree.merge(other_tree[, ancestor_tree[, options]]) -> Rugged::Index
|
636
|
+
* tree.merge(other_tree[, options]) -> Rugged::Index
|
637
|
+
*
|
638
|
+
* Merges two trees and returns the a Rugged::Index object that reflects
|
639
|
+
* the result of the merge.
|
640
|
+
*
|
641
|
+
* The following options can be passed in the +options+ Hash:
|
642
|
+
*
|
643
|
+
* :renames ::
|
644
|
+
* If true, looking for renames will be enabled (`--find-renames`).
|
645
|
+
*
|
646
|
+
* :rename_threshold ::
|
647
|
+
* An integer specifying the minimum similarity of a file to be
|
648
|
+
* seen as an eligible rename source (default 50).
|
649
|
+
*
|
650
|
+
* :target_limit ::
|
651
|
+
* An integer specifying the maximum byte size of a file before a it will
|
652
|
+
* be treated as binary. The default value is 512MB.
|
653
|
+
*
|
654
|
+
* :favor ::
|
655
|
+
* Specifies how and if conflicts are auto-resolved by favoring a specific
|
656
|
+
* file output. Can be one of `:normal`, `:ours`, `:theirs` or `:union`.
|
657
|
+
*
|
658
|
+
*/
|
659
|
+
static VALUE rb_git_tree_merge(int argc, VALUE *argv, VALUE self)
|
660
|
+
{
|
661
|
+
VALUE rb_other_tree, rb_ancestor_tree, rb_options;
|
662
|
+
VALUE rb_repo = rugged_owner(self);
|
663
|
+
|
664
|
+
git_tree *tree, *other_tree, *ancestor_tree;
|
665
|
+
git_repository *repo;
|
666
|
+
git_index *index;
|
667
|
+
git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
|
668
|
+
int error;
|
669
|
+
|
670
|
+
if (rb_scan_args(argc, argv, "12", &rb_other_tree, &rb_ancestor_tree, &rb_options) == 2) {
|
671
|
+
if (TYPE(rb_ancestor_tree) == T_HASH) {
|
672
|
+
rb_options = rb_ancestor_tree;
|
673
|
+
rb_ancestor_tree = Qnil;
|
674
|
+
}
|
675
|
+
}
|
676
|
+
|
677
|
+
if (!NIL_P(rb_options)) {
|
678
|
+
Check_Type(rb_options, T_HASH);
|
679
|
+
rugged_parse_merge_options(&opts, rb_options);
|
680
|
+
}
|
681
|
+
|
682
|
+
if (!rb_obj_is_kind_of(rb_other_tree, rb_cRuggedTree))
|
683
|
+
rb_raise(rb_eTypeError, "Expecting a Rugged::Tree instance");
|
684
|
+
else if (!NIL_P(rb_ancestor_tree) && !rb_obj_is_kind_of(rb_ancestor_tree, rb_cRuggedTree))
|
685
|
+
rb_raise(rb_eTypeError, "Expecting a Rugged::Tree instance");
|
686
|
+
|
687
|
+
Data_Get_Struct(self, git_tree, tree);
|
688
|
+
Data_Get_Struct(rb_repo, git_repository, repo);
|
689
|
+
Data_Get_Struct(rb_other_tree, git_tree, other_tree);
|
690
|
+
|
691
|
+
if (!NIL_P(rb_ancestor_tree))
|
692
|
+
Data_Get_Struct(rb_ancestor_tree, git_tree, ancestor_tree);
|
693
|
+
else
|
694
|
+
ancestor_tree = NULL;
|
695
|
+
|
696
|
+
error = git_merge_trees(&index, repo, ancestor_tree, tree, other_tree, &opts);
|
697
|
+
if (error == GIT_EMERGECONFLICT)
|
698
|
+
return Qnil;
|
699
|
+
|
700
|
+
rugged_exception_check(error);
|
701
|
+
|
702
|
+
return rugged_index_new(rb_cRuggedIndex, rb_repo, index);
|
703
|
+
}
|
704
|
+
|
705
|
+
static void rb_git_treebuilder_free(git_treebuilder *bld)
|
706
|
+
{
|
707
|
+
git_treebuilder_free(bld);
|
708
|
+
}
|
709
|
+
|
710
|
+
/*
|
711
|
+
* call-seq:
|
712
|
+
* Tree::Builder.new(repository, [tree])
|
713
|
+
*
|
714
|
+
* Create a new Rugged::Tree::Builder instance to write a tree to
|
715
|
+
* the given +repository+.
|
716
|
+
*
|
717
|
+
* If an optional +tree+ is given, the returned Tree::Builder will be
|
718
|
+
* initialized with the entry of +tree+. Otherwise, the Tree::Builder
|
719
|
+
* will be empty and has to be filled manually.
|
720
|
+
*/
|
721
|
+
static VALUE rb_git_treebuilder_new(int argc, VALUE *argv, VALUE klass)
|
722
|
+
{
|
723
|
+
git_treebuilder *builder;
|
724
|
+
git_repository *repo;
|
725
|
+
git_tree *tree = NULL;
|
726
|
+
VALUE rb_object, rb_builder, rb_repo;
|
727
|
+
int error;
|
728
|
+
|
729
|
+
if (rb_scan_args(argc, argv, "11", &rb_repo, &rb_object) == 2) {
|
730
|
+
if (!rb_obj_is_kind_of(rb_object, rb_cRuggedTree))
|
731
|
+
rb_raise(rb_eTypeError, "A Rugged::Tree instance is required");
|
732
|
+
|
733
|
+
Data_Get_Struct(rb_object, git_tree, tree);
|
734
|
+
}
|
735
|
+
|
736
|
+
rugged_check_repo(rb_repo);
|
737
|
+
Data_Get_Struct(rb_repo, git_repository, repo);
|
738
|
+
|
739
|
+
error = git_treebuilder_new(&builder, repo, tree);
|
740
|
+
rugged_exception_check(error);
|
741
|
+
|
742
|
+
rb_builder = Data_Wrap_Struct(klass, NULL, &rb_git_treebuilder_free, builder);
|
743
|
+
rugged_set_owner(rb_builder, rb_repo);
|
744
|
+
|
745
|
+
return rb_builder;
|
746
|
+
}
|
747
|
+
|
748
|
+
/*
|
749
|
+
* call-seq:
|
750
|
+
* builder.clear -> nil
|
751
|
+
*
|
752
|
+
* Clear all entries in +builder+.
|
753
|
+
*/
|
754
|
+
static VALUE rb_git_treebuilder_clear(VALUE self)
|
755
|
+
{
|
756
|
+
git_treebuilder *builder;
|
757
|
+
Data_Get_Struct(self, git_treebuilder, builder);
|
758
|
+
git_treebuilder_clear(builder);
|
759
|
+
return Qnil;
|
760
|
+
}
|
761
|
+
|
762
|
+
/*
|
763
|
+
* call-seq:
|
764
|
+
* builder[path] -> entry
|
765
|
+
*
|
766
|
+
* Return an entry from +builder+ based on its relative path.
|
767
|
+
*/
|
768
|
+
static VALUE rb_git_treebuilder_get(VALUE self, VALUE path)
|
769
|
+
{
|
770
|
+
git_treebuilder *builder;
|
771
|
+
Data_Get_Struct(self, git_treebuilder, builder);
|
772
|
+
|
773
|
+
Check_Type(path, T_STRING);
|
774
|
+
|
775
|
+
return rb_git_treeentry_fromC(git_treebuilder_get(builder, StringValueCStr(path)));
|
776
|
+
}
|
777
|
+
|
778
|
+
/*
|
779
|
+
* call-seq:
|
780
|
+
* builder << entry -> nil
|
781
|
+
* builder.insert(entry) -> nil
|
782
|
+
*
|
783
|
+
* Inser a new entry into +builder+.
|
784
|
+
*/
|
785
|
+
static VALUE rb_git_treebuilder_insert(VALUE self, VALUE rb_entry)
|
786
|
+
{
|
787
|
+
git_treebuilder *builder;
|
788
|
+
VALUE rb_path, rb_oid, rb_attr;
|
789
|
+
git_oid oid;
|
790
|
+
int error;
|
791
|
+
|
792
|
+
Data_Get_Struct(self, git_treebuilder, builder);
|
793
|
+
Check_Type(rb_entry, T_HASH);
|
794
|
+
|
795
|
+
rb_path = rb_hash_aref(rb_entry, CSTR2SYM("name"));
|
796
|
+
Check_Type(rb_path, T_STRING);
|
797
|
+
|
798
|
+
rb_oid = rb_hash_aref(rb_entry, CSTR2SYM("oid"));
|
799
|
+
Check_Type(rb_oid, T_STRING);
|
800
|
+
rugged_exception_check(git_oid_fromstr(&oid, StringValueCStr(rb_oid)));
|
801
|
+
|
802
|
+
rb_attr = rb_hash_aref(rb_entry, CSTR2SYM("filemode"));
|
803
|
+
Check_Type(rb_attr, T_FIXNUM);
|
804
|
+
|
805
|
+
error = git_treebuilder_insert(NULL,
|
806
|
+
builder,
|
807
|
+
StringValueCStr(rb_path),
|
808
|
+
&oid,
|
809
|
+
FIX2INT(rb_attr));
|
810
|
+
|
811
|
+
rugged_exception_check(error);
|
812
|
+
return Qnil;
|
813
|
+
}
|
814
|
+
|
815
|
+
/*
|
816
|
+
* call-seq:
|
817
|
+
* builder.remove(path) -> true or false
|
818
|
+
*
|
819
|
+
* Remove an entry from +builder+ by its relative +path+.
|
820
|
+
*
|
821
|
+
* Returns +true+ if the entry was successfully removed,
|
822
|
+
* or +false+ if the entry was not found.
|
823
|
+
*/
|
824
|
+
static VALUE rb_git_treebuilder_remove(VALUE self, VALUE path)
|
825
|
+
{
|
826
|
+
git_treebuilder *builder;
|
827
|
+
int error;
|
828
|
+
|
829
|
+
Data_Get_Struct(self, git_treebuilder, builder);
|
830
|
+
Check_Type(path, T_STRING);
|
831
|
+
|
832
|
+
error = git_treebuilder_remove(builder, StringValueCStr(path));
|
833
|
+
if (error == GIT_ENOTFOUND) {
|
834
|
+
return Qfalse;
|
835
|
+
} else if (error == GIT_ERROR && giterr_last()->klass == GITERR_TREE) {
|
836
|
+
return Qfalse;
|
837
|
+
}
|
838
|
+
|
839
|
+
rugged_exception_check(error);
|
840
|
+
return Qtrue;
|
841
|
+
}
|
842
|
+
|
843
|
+
/*
|
844
|
+
* call-seq:
|
845
|
+
* builder.write -> oid
|
846
|
+
*
|
847
|
+
* Write +builder+'s content as a tree to the repository
|
848
|
+
* that owns the builder and return the +oid+ for the
|
849
|
+
* newly created tree.
|
850
|
+
*/
|
851
|
+
static VALUE rb_git_treebuilder_write(VALUE self)
|
852
|
+
{
|
853
|
+
git_treebuilder *builder;
|
854
|
+
git_oid written_id;
|
855
|
+
int error;
|
856
|
+
|
857
|
+
Data_Get_Struct(self, git_treebuilder, builder);
|
858
|
+
|
859
|
+
error = git_treebuilder_write(&written_id, builder);
|
860
|
+
rugged_exception_check(error);
|
861
|
+
|
862
|
+
return rugged_create_oid(&written_id);
|
863
|
+
}
|
864
|
+
|
865
|
+
static int treebuilder_cb(const git_tree_entry *entry, void *opaque)
|
866
|
+
{
|
867
|
+
VALUE proc = (VALUE)opaque;
|
868
|
+
VALUE ret = rb_funcall(proc, rb_intern("call"), 1, rb_git_treeentry_fromC(entry));
|
869
|
+
return rugged_parse_bool(ret);
|
870
|
+
}
|
871
|
+
|
872
|
+
/*
|
873
|
+
* call-seq:
|
874
|
+
* builder.reject! { |entry| block } -> nil
|
875
|
+
*
|
876
|
+
* Deletes every tree +entry+ from +builder+ for which
|
877
|
+
* the given +block+ evaluates to true.
|
878
|
+
*/
|
879
|
+
static VALUE rb_git_treebuilder_filter(VALUE self)
|
880
|
+
{
|
881
|
+
git_treebuilder *builder;
|
882
|
+
|
883
|
+
rb_need_block();
|
884
|
+
Data_Get_Struct(self, git_treebuilder, builder);
|
885
|
+
|
886
|
+
git_treebuilder_filter(builder, &treebuilder_cb, (void *)rb_block_proc());
|
887
|
+
return Qnil;
|
888
|
+
}
|
889
|
+
|
890
|
+
void Init_rugged_tree(void)
|
891
|
+
{
|
892
|
+
/*
|
893
|
+
* Tree
|
894
|
+
*/
|
895
|
+
rb_cRuggedTree = rb_define_class_under(rb_mRugged, "Tree", rb_cRuggedObject);
|
896
|
+
rb_define_method(rb_cRuggedTree, "count", rb_git_tree_entrycount, 0);
|
897
|
+
rb_define_method(rb_cRuggedTree, "count_recursive", rb_git_tree_entrycount_recursive, -1);
|
898
|
+
rb_define_method(rb_cRuggedTree, "length", rb_git_tree_entrycount, 0);
|
899
|
+
rb_define_method(rb_cRuggedTree, "get_entry", rb_git_tree_get_entry, 1);
|
900
|
+
rb_define_method(rb_cRuggedTree, "get_entry_by_oid", rb_git_tree_get_entry_by_oid, 1);
|
901
|
+
rb_define_method(rb_cRuggedTree, "path", rb_git_tree_path, 1);
|
902
|
+
rb_define_method(rb_cRuggedTree, "diff_workdir", rb_git_tree_diff_workdir, -1);
|
903
|
+
rb_define_method(rb_cRuggedTree, "[]", rb_git_tree_get_entry, 1);
|
904
|
+
rb_define_method(rb_cRuggedTree, "each", rb_git_tree_each, 0);
|
905
|
+
rb_define_method(rb_cRuggedTree, "walk", rb_git_tree_walk, 1);
|
906
|
+
rb_define_method(rb_cRuggedTree, "merge", rb_git_tree_merge, -1);
|
907
|
+
|
908
|
+
rb_define_singleton_method(rb_cRuggedTree, "diff", rb_git_tree_diff_, -1);
|
909
|
+
|
910
|
+
rb_cRuggedTreeBuilder = rb_define_class_under(rb_cRuggedTree, "Builder", rb_cObject);
|
911
|
+
rb_define_singleton_method(rb_cRuggedTreeBuilder, "new", rb_git_treebuilder_new, -1);
|
912
|
+
rb_define_method(rb_cRuggedTreeBuilder, "clear", rb_git_treebuilder_clear, 0);
|
913
|
+
rb_define_method(rb_cRuggedTreeBuilder, "[]", rb_git_treebuilder_get, 1);
|
914
|
+
rb_define_method(rb_cRuggedTreeBuilder, "insert", rb_git_treebuilder_insert, 1);
|
915
|
+
rb_define_method(rb_cRuggedTreeBuilder, "<<", rb_git_treebuilder_insert, 1);
|
916
|
+
rb_define_method(rb_cRuggedTreeBuilder, "remove", rb_git_treebuilder_remove, 1);
|
917
|
+
rb_define_method(rb_cRuggedTreeBuilder, "write", rb_git_treebuilder_write, 0);
|
918
|
+
rb_define_method(rb_cRuggedTreeBuilder, "reject!", rb_git_treebuilder_filter, 0);
|
919
|
+
}
|