rdavila-rugged 0.24.0b13

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