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.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +619 -0
  4. data/ext/rugged/extconf.rb +105 -0
  5. data/ext/rugged/rugged.c +527 -0
  6. data/ext/rugged/rugged.h +185 -0
  7. data/ext/rugged/rugged_backend.c +34 -0
  8. data/ext/rugged/rugged_blame.c +292 -0
  9. data/ext/rugged/rugged_blob.c +638 -0
  10. data/ext/rugged/rugged_branch.c +195 -0
  11. data/ext/rugged/rugged_branch_collection.c +408 -0
  12. data/ext/rugged/rugged_commit.c +691 -0
  13. data/ext/rugged/rugged_config.c +404 -0
  14. data/ext/rugged/rugged_cred.c +148 -0
  15. data/ext/rugged/rugged_diff.c +686 -0
  16. data/ext/rugged/rugged_diff_delta.c +105 -0
  17. data/ext/rugged/rugged_diff_hunk.c +103 -0
  18. data/ext/rugged/rugged_diff_line.c +83 -0
  19. data/ext/rugged/rugged_index.c +1255 -0
  20. data/ext/rugged/rugged_note.c +376 -0
  21. data/ext/rugged/rugged_object.c +383 -0
  22. data/ext/rugged/rugged_patch.c +245 -0
  23. data/ext/rugged/rugged_reference.c +396 -0
  24. data/ext/rugged/rugged_reference_collection.c +446 -0
  25. data/ext/rugged/rugged_remote.c +691 -0
  26. data/ext/rugged/rugged_remote_collection.c +457 -0
  27. data/ext/rugged/rugged_repo.c +2669 -0
  28. data/ext/rugged/rugged_revwalk.c +495 -0
  29. data/ext/rugged/rugged_settings.c +155 -0
  30. data/ext/rugged/rugged_signature.c +106 -0
  31. data/ext/rugged/rugged_submodule.c +852 -0
  32. data/ext/rugged/rugged_submodule_collection.c +384 -0
  33. data/ext/rugged/rugged_tag.c +251 -0
  34. data/ext/rugged/rugged_tag_collection.c +347 -0
  35. data/ext/rugged/rugged_tree.c +919 -0
  36. data/lib/rugged.rb +23 -0
  37. data/lib/rugged/attributes.rb +41 -0
  38. data/lib/rugged/blob.rb +28 -0
  39. data/lib/rugged/branch.rb +19 -0
  40. data/lib/rugged/commit.rb +54 -0
  41. data/lib/rugged/console.rb +9 -0
  42. data/lib/rugged/credentials.rb +43 -0
  43. data/lib/rugged/diff.rb +20 -0
  44. data/lib/rugged/diff/delta.rb +53 -0
  45. data/lib/rugged/diff/hunk.rb +18 -0
  46. data/lib/rugged/diff/line.rb +47 -0
  47. data/lib/rugged/index.rb +13 -0
  48. data/lib/rugged/object.rb +7 -0
  49. data/lib/rugged/patch.rb +36 -0
  50. data/lib/rugged/reference.rb +7 -0
  51. data/lib/rugged/remote.rb +4 -0
  52. data/lib/rugged/repository.rb +227 -0
  53. data/lib/rugged/submodule_collection.rb +48 -0
  54. data/lib/rugged/tag.rb +50 -0
  55. data/lib/rugged/tree.rb +38 -0
  56. data/lib/rugged/version.rb +3 -0
  57. data/lib/rugged/walker.rb +5 -0
  58. 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
+ }