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,2669 @@
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
+ #include <git2/sys/repository.h>
27
+ #include <git2/sys/odb_backend.h>
28
+ #include <git2/sys/refdb_backend.h>
29
+ #include <git2/refs.h>
30
+
31
+ extern VALUE rb_mRugged;
32
+ extern VALUE rb_eRuggedError;
33
+ extern VALUE rb_cRuggedIndex;
34
+ extern VALUE rb_cRuggedConfig;
35
+ extern VALUE rb_cRuggedBackend;
36
+ extern VALUE rb_cRuggedRemote;
37
+ extern VALUE rb_cRuggedCommit;
38
+ extern VALUE rb_cRuggedTag;
39
+ extern VALUE rb_cRuggedTree;
40
+ extern VALUE rb_cRuggedReference;
41
+ extern VALUE rb_cRuggedBackend;
42
+
43
+ extern VALUE rb_cRuggedCredPlaintext;
44
+ extern VALUE rb_cRuggedCredSshKey;
45
+ extern VALUE rb_cRuggedCredDefault;
46
+
47
+ VALUE rb_cRuggedRepo;
48
+ VALUE rb_cRuggedOdbObject;
49
+
50
+ static ID id_call;
51
+
52
+ /*
53
+ * call-seq:
54
+ * odb_obj.oid -> hex_oid
55
+ *
56
+ * Return the Object ID (a 40 character SHA1 hash) for this raw
57
+ * object.
58
+ *
59
+ * odb_obj.oid #=> "d8786bfc97485e8d7b19b21fb88c8ef1f199fc3f"
60
+ */
61
+ static VALUE rb_git_odbobj_oid(VALUE self)
62
+ {
63
+ git_odb_object *obj;
64
+ Data_Get_Struct(self, git_odb_object, obj);
65
+ return rugged_create_oid(git_odb_object_id(obj));
66
+ }
67
+
68
+ /*
69
+ * call-seq:
70
+ * odb_obj.data -> buffer
71
+ *
72
+ * Return an ASCII buffer with the raw bytes that form the Git object.
73
+ *
74
+ * odb_obj.data #=> "tree 87ebee8367f9cc5ac04858b3bd5610ca74f04df9\n"
75
+ * #=> "parent 68d041ee999cb07c6496fbdd4f384095de6ca9e1\n"
76
+ * #=> "author Vicent Martí <tanoku@gmail.com> 1326863045 -0800\n"
77
+ * #=> ...
78
+ */
79
+ static VALUE rb_git_odbobj_data(VALUE self)
80
+ {
81
+ git_odb_object *obj;
82
+ Data_Get_Struct(self, git_odb_object, obj);
83
+ return rb_str_new(git_odb_object_data(obj), git_odb_object_size(obj));
84
+ }
85
+
86
+ /*
87
+ * call-seq:
88
+ * odb_obj.size -> size
89
+ *
90
+ * Return the size in bytes of the Git object after decompression. This is
91
+ * also the size of the +obj.data+ buffer.
92
+ *
93
+ * odb_obj.size #=> 231
94
+ */
95
+ static VALUE rb_git_odbobj_size(VALUE self)
96
+ {
97
+ git_odb_object *obj;
98
+ Data_Get_Struct(self, git_odb_object, obj);
99
+ return INT2FIX(git_odb_object_size(obj));
100
+ }
101
+
102
+ /*
103
+ * call-seq:
104
+ * odb_obj.type -> Symbol
105
+ *
106
+ * Return a Ruby symbol representing the basic Git type of this object.
107
+ * Possible values are +:tree+, +:blob+, +:commit+ and +:tag+.
108
+ *
109
+ * odb_obj.type #=> :tag
110
+ */
111
+ static VALUE rb_git_odbobj_type(VALUE self)
112
+ {
113
+ git_odb_object *obj;
114
+ Data_Get_Struct(self, git_odb_object, obj);
115
+ return rugged_otype_new(git_odb_object_type(obj));
116
+ }
117
+
118
+ void rb_git__odbobj_free(void *obj)
119
+ {
120
+ git_odb_object_free((git_odb_object *)obj);
121
+ }
122
+
123
+ VALUE rugged_raw_read(git_repository *repo, const git_oid *oid)
124
+ {
125
+ git_odb *odb;
126
+ git_odb_object *obj;
127
+
128
+ int error;
129
+
130
+ error = git_repository_odb(&odb, repo);
131
+ rugged_exception_check(error);
132
+
133
+ error = git_odb_read(&obj, odb, oid);
134
+ git_odb_free(odb);
135
+ rugged_exception_check(error);
136
+
137
+ return Data_Wrap_Struct(rb_cRuggedOdbObject, NULL, rb_git__odbobj_free, obj);
138
+ }
139
+
140
+ void rb_git_repo__free(git_repository *repo)
141
+ {
142
+ git_repository_free(repo);
143
+ }
144
+
145
+ VALUE rugged_repo_new(VALUE klass, git_repository *repo)
146
+ {
147
+ VALUE rb_repo = Data_Wrap_Struct(klass, NULL, &rb_git_repo__free, repo);
148
+
149
+ #ifdef HAVE_RUBY_ENCODING_H
150
+ /* TODO: set this properly */
151
+ rb_iv_set(rb_repo, "@encoding",
152
+ rb_enc_from_encoding(rb_filesystem_encoding()));
153
+ #endif
154
+
155
+ rb_iv_set(rb_repo, "@config", Qnil);
156
+ rb_iv_set(rb_repo, "@index", Qnil);
157
+
158
+ return rb_repo;
159
+ }
160
+
161
+ static void load_alternates(git_repository *repo, VALUE rb_alternates)
162
+ {
163
+ git_odb *odb = NULL;
164
+ int i, error;
165
+
166
+ if (NIL_P(rb_alternates))
167
+ return;
168
+
169
+ Check_Type(rb_alternates, T_ARRAY);
170
+
171
+ if (RARRAY_LEN(rb_alternates) == 0)
172
+ return;
173
+
174
+ for (i = 0; i < RARRAY_LEN(rb_alternates); ++i)
175
+ Check_Type(rb_ary_entry(rb_alternates, i), T_STRING);
176
+
177
+ error = git_repository_odb(&odb, repo);
178
+ rugged_exception_check(error);
179
+
180
+ for (i = 0; !error && i < RARRAY_LEN(rb_alternates); ++i) {
181
+ VALUE alt = rb_ary_entry(rb_alternates, i);
182
+ error = git_odb_add_disk_alternate(odb, StringValueCStr(alt));
183
+ }
184
+
185
+ git_odb_free(odb);
186
+ rugged_exception_check(error);
187
+ }
188
+
189
+ static void rugged_repo_new_with_backend(git_repository **repo, VALUE rb_path, VALUE rb_backend)
190
+ {
191
+ char *path;
192
+
193
+ git_odb *odb = NULL;
194
+ git_odb_backend *odb_backend = NULL;
195
+ git_refdb *refdb = NULL;
196
+ git_refdb_backend *refdb_backend = NULL;
197
+ git_reference *head = NULL;
198
+ rugged_backend *backend;
199
+
200
+ int error = 0;
201
+
202
+ Check_Type(rb_path, T_STRING);
203
+ path = StringValueCStr(rb_path);
204
+
205
+ if (rb_obj_is_kind_of(rb_backend, rb_cRuggedBackend) == Qfalse) {
206
+ rb_raise(rb_eRuggedError, "Backend must be an instance of Rugged::Backend");
207
+ }
208
+
209
+ Data_Get_Struct(rb_backend, rugged_backend, backend);
210
+
211
+ error = git_odb_new(&odb);
212
+ if (error) goto cleanup;
213
+
214
+ error = backend->odb_backend(&odb_backend, backend, path);
215
+ if (error) goto cleanup;
216
+
217
+ error = git_odb_add_backend(odb, odb_backend, 1);
218
+ if (error) {
219
+ assert(odb_backend->free);
220
+ odb_backend->free(odb_backend);
221
+ goto cleanup;
222
+ }
223
+
224
+ error = git_repository_wrap_odb(repo, odb);
225
+ if (error) goto cleanup;
226
+
227
+ error = git_refdb_new(&refdb, *repo);
228
+ if (error) goto cleanup;
229
+
230
+ error = backend->refdb_backend(&refdb_backend, backend, path);
231
+ if (error) {
232
+ assert(refdb_backend->free);
233
+ refdb_backend->free(refdb_backend);
234
+ goto cleanup;
235
+ }
236
+
237
+ error = git_refdb_set_backend(refdb, refdb_backend);
238
+ if (error) goto cleanup;
239
+
240
+ git_repository_set_refdb(*repo, refdb);
241
+
242
+ error = git_reference_lookup(&head, *repo, "HEAD");
243
+
244
+ if (error == GIT_ENOTFOUND) {
245
+ giterr_clear();
246
+ error = git_reference_symbolic_create(&head, *repo, "HEAD", "refs/heads/master", 0, NULL);
247
+ }
248
+
249
+ if (!error) {
250
+ git_reference_free(head);
251
+ return;
252
+ }
253
+
254
+ cleanup:
255
+ git_repository_free(*repo);
256
+ git_odb_free(odb);
257
+ git_refdb_free(refdb);
258
+
259
+ rugged_exception_check(error);
260
+ }
261
+
262
+ /*
263
+ * call-seq:
264
+ * Repository.bare(path[, alternates]) -> repository OR
265
+ * Repository.bare(path[, options]) -> repository
266
+ *
267
+ * Open a bare Git repository at +path+ and return a +Repository+
268
+ * object representing it.
269
+ *
270
+ * This is faster than Rugged::Repository.new, as it won't attempt to perform
271
+ * any +.git+ directory discovery, won't try to load the config options to
272
+ * determine whether the repository is bare and won't try to load the workdir.
273
+ *
274
+ * Optionally, you can pass a list of alternate object folders or an options Hash.
275
+ *
276
+ * Rugged::Repository.bare(path, ['./other/repo/.git/objects'])
277
+ * Rugged::Repository.bare(path, opts)
278
+ *
279
+ * The following options can be passed in the +options+ Hash:
280
+ *
281
+ * :backend ::
282
+ * A Rugged::Backend instance
283
+ * :alternates ::
284
+ * A list of alternate object folders.
285
+ * Rugged::Repository.bare(path, :alternates => ['./other/repo/.git/objects'])
286
+ */
287
+ static VALUE rb_git_repo_open_bare(int argc, VALUE *argv, VALUE klass)
288
+ {
289
+ git_repository *repo = NULL;
290
+ int error = 0;
291
+ VALUE rb_path, rb_options, rb_alternates = 0;
292
+
293
+ rb_scan_args(argc, argv, "11", &rb_path, &rb_options);
294
+
295
+ if (!NIL_P(rb_options) && TYPE(rb_options) == T_ARRAY)
296
+ rb_alternates = rb_options;
297
+
298
+ if (!NIL_P(rb_options) && TYPE(rb_options) == T_HASH) {
299
+ /* Check for `:backend` */
300
+ VALUE rb_backend = rb_hash_aref(rb_options, CSTR2SYM("backend"));
301
+
302
+ if (!NIL_P(rb_backend)) {
303
+ rugged_repo_new_with_backend(&repo, rb_path, rb_backend);
304
+ }
305
+
306
+ /* Check for `:alternates` */
307
+ rb_alternates = rb_hash_aref(rb_options, CSTR2SYM("alternates"));
308
+ }
309
+
310
+ if (!repo) {
311
+ Check_Type(rb_path, T_STRING);
312
+
313
+ error = git_repository_open_bare(&repo, StringValueCStr(rb_path));
314
+ rugged_exception_check(error);
315
+ }
316
+
317
+ if (rb_alternates) {
318
+ load_alternates(repo, rb_alternates);
319
+ }
320
+
321
+ return rugged_repo_new(klass, repo);
322
+ }
323
+
324
+ /*
325
+ * call-seq:
326
+ * Repository.new(path, options = {}) -> repository
327
+ *
328
+ * Open a Git repository in the given +path+ and return a +Repository+ object
329
+ * representing it. An exception will be thrown if +path+ doesn't point to a
330
+ * valid repository. If you need to create a repository from scratch, use
331
+ * Rugged::Repository.init_at instead.
332
+ *
333
+ * The +path+ must point to either the actual folder (+.git+) of a Git repository,
334
+ * or to the directorly that contains the +.git+ folder.
335
+ *
336
+ * See also Rugged::Repository.discover and Rugged::Repository.bare.
337
+ *
338
+ * The following options can be passed in the +options+ Hash:
339
+ *
340
+ * :alternates ::
341
+ * A list of alternate object folders.
342
+ *
343
+ * Examples:
344
+ *
345
+ * Rugged::Repository.new('test/.git') #=> #<Rugged::Repository:0x108849488>
346
+ * Rugged::Repository.new(path, :alternates => ['./other/repo/.git/objects'])
347
+ */
348
+ static VALUE rb_git_repo_new(int argc, VALUE *argv, VALUE klass)
349
+ {
350
+ git_repository *repo;
351
+ int error = 0;
352
+ VALUE rb_path, rb_options;
353
+
354
+ rb_scan_args(argc, argv, "10:", &rb_path, &rb_options);
355
+ Check_Type(rb_path, T_STRING);
356
+
357
+ error = git_repository_open(&repo, StringValueCStr(rb_path));
358
+ rugged_exception_check(error);
359
+
360
+ if (!NIL_P(rb_options)) {
361
+ /* Check for `:alternates` */
362
+ load_alternates(repo, rb_hash_aref(rb_options, CSTR2SYM("alternates")));
363
+ }
364
+
365
+ return rugged_repo_new(klass, repo);
366
+ }
367
+
368
+ /*
369
+ * call-seq:
370
+ * Repository.init_at(path, is_bare = false, opts = {}) -> repository
371
+ *
372
+ * Initialize a Git repository in +path+. This implies creating all the
373
+ * necessary files on the FS, or re-initializing an already existing
374
+ * repository if the files have already been created.
375
+ *
376
+ * The +is_bare+ (optional, defaults to false) attribute specifies whether
377
+ * the Repository should be created on disk as bare or not.
378
+ * Bare repositories have no working directory and are created in the root
379
+ * of +path+. Non-bare repositories are created in a +.git+ folder and
380
+ * use +path+ as working directory.
381
+ *
382
+ * The following options can be passed in the +options+ Hash:
383
+ *
384
+ * :backend ::
385
+ * A Rugged::Backend instance
386
+ *
387
+ *
388
+ * Rugged::Repository.init_at('repository', :bare) #=> #<Rugged::Repository:0x108849488>
389
+ */
390
+ static VALUE rb_git_repo_init_at(int argc, VALUE *argv, VALUE klass)
391
+ {
392
+ git_repository *repo = NULL;
393
+ VALUE rb_path, rb_is_bare, rb_options;
394
+ int error;
395
+
396
+ rb_scan_args(argc, argv, "11:", &rb_path, &rb_is_bare, &rb_options);
397
+ Check_Type(rb_path, T_STRING);
398
+
399
+ if (!NIL_P(rb_options)) {
400
+ /* Check for `:backend` */
401
+ VALUE rb_backend = rb_hash_aref(rb_options, CSTR2SYM("backend"));
402
+
403
+ if (rb_backend && !NIL_P(rb_backend)) {
404
+ rugged_repo_new_with_backend(&repo, rb_path, rb_backend);
405
+ }
406
+ }
407
+
408
+ if(!repo) {
409
+ error = git_repository_init(&repo, StringValueCStr(rb_path), RTEST(rb_is_bare));
410
+ rugged_exception_check(error);
411
+ }
412
+
413
+ return rugged_repo_new(klass, repo);
414
+ }
415
+
416
+ static void parse_clone_options(git_clone_options *ret, VALUE rb_options, struct rugged_remote_cb_payload *remote_payload)
417
+ {
418
+ VALUE val;
419
+
420
+ if (NIL_P(rb_options))
421
+ return;
422
+
423
+ val = rb_hash_aref(rb_options, CSTR2SYM("bare"));
424
+ if (RTEST(val))
425
+ ret->bare = 1;
426
+
427
+ val = rb_hash_aref(rb_options, CSTR2SYM("checkout_branch"));
428
+ if (!NIL_P(val)) {
429
+ Check_Type(val, T_STRING);
430
+ ret->checkout_branch = StringValueCStr(val);
431
+ }
432
+
433
+ rugged_remote_init_callbacks_and_payload_from_options(rb_options, &ret->fetch_opts.callbacks, remote_payload);
434
+ }
435
+
436
+ /*
437
+ * call-seq:
438
+ * Repository.clone_at(url, local_path[, options]) -> repository
439
+ *
440
+ * Clone a repository from +url+ to +local_path+.
441
+ *
442
+ * The following options can be passed in the +options+ Hash:
443
+ *
444
+ * :bare ::
445
+ * If +true+, the clone will be created as a bare repository.
446
+ * Defaults to +false+.
447
+ *
448
+ * :checkout_branch ::
449
+ * The name of a branch to checkout. Defaults to the remote's +HEAD+.
450
+ *
451
+ * :remote ::
452
+ * The name to give to the "origin" remote. Defaults to <tt>"origin"</tt>.
453
+ *
454
+ * :ignore_cert_errors ::
455
+ * If set to +true+, errors while validating the remote's host certificate will be ignored.
456
+ *
457
+ * :credentials ::
458
+ * The credentials to use for the clone operation. Can be either an instance of one
459
+ * of the Rugged::Credentials types, or a proc returning one of the former.
460
+ * The proc will be called with the +url+, the +username+ from the url (if applicable) and
461
+ * a list of applicable credential types.
462
+ *
463
+ * :progress ::
464
+ * A callback that will be executed with the textual progress received from the remote.
465
+ * This is the text send over the progress side-band (ie. the "counting objects" output).
466
+ *
467
+ * :transfer_progress ::
468
+ * A callback that will be executed to report clone progress information. It will be passed
469
+ * the amount of +total_objects+, +indexed_objects+, +received_objects+, +local_objects+,
470
+ * +total_deltas+, +indexed_deltas+, and +received_bytes+.
471
+ *
472
+ * :update_tips ::
473
+ * A callback that will be executed each time a reference was updated locally. It will be
474
+ * passed the +refname+, +old_oid+ and +new_oid+.
475
+ *
476
+ * Example:
477
+ *
478
+ * Repository.clone_at("https://github.com/libgit2/rugged.git", "./some/dir", {
479
+ * transfer_progress: lambda { |total_objects, indexed_objects, received_objects, local_objects, total_deltas, indexed_deltas, received_bytes|
480
+ * # ...
481
+ * }
482
+ * })
483
+ */
484
+ static VALUE rb_git_repo_clone_at(int argc, VALUE *argv, VALUE klass)
485
+ {
486
+ VALUE url, local_path, rb_options_hash;
487
+ git_clone_options options = GIT_CLONE_OPTIONS_INIT;
488
+ struct rugged_remote_cb_payload remote_payload = { Qnil, Qnil, Qnil, Qnil, 0 };
489
+ git_repository *repo;
490
+ int error;
491
+
492
+ rb_scan_args(argc, argv, "21", &url, &local_path, &rb_options_hash);
493
+ Check_Type(url, T_STRING);
494
+ Check_Type(local_path, T_STRING);
495
+
496
+ parse_clone_options(&options, rb_options_hash, &remote_payload);
497
+
498
+ error = git_clone(&repo, StringValueCStr(url), StringValueCStr(local_path), &options);
499
+
500
+ if (RTEST(remote_payload.exception))
501
+ rb_jump_tag(remote_payload.exception);
502
+ rugged_exception_check(error);
503
+
504
+ return rugged_repo_new(klass, repo);
505
+ }
506
+
507
+ #define RB_GIT_REPO_OWNED_GET(_klass, _object) \
508
+ VALUE rb_data = rb_iv_get(self, "@" #_object); \
509
+ if (NIL_P(rb_data)) { \
510
+ git_repository *repo; \
511
+ git_##_object *data; \
512
+ int error; \
513
+ Data_Get_Struct(self, git_repository, repo); \
514
+ error = git_repository_##_object(&data, repo); \
515
+ rugged_exception_check(error); \
516
+ rb_data = rugged_##_object##_new(_klass, self, data); \
517
+ rb_iv_set(self, "@" #_object, rb_data); \
518
+ } \
519
+ return rb_data; \
520
+
521
+ #define RB_GIT_REPO_OWNED_SET(_klass, _object) \
522
+ VALUE rb_old_data; \
523
+ git_repository *repo; \
524
+ git_##_object *data; \
525
+ if (!rb_obj_is_kind_of(rb_data, _klass))\
526
+ rb_raise(rb_eTypeError, \
527
+ "The given object is not a Rugged::" #_object); \
528
+ if (!NIL_P(rugged_owner(rb_data))) \
529
+ rb_raise(rb_eRuntimeError, \
530
+ "The given object is already owned by another repository"); \
531
+ Data_Get_Struct(self, git_repository, repo); \
532
+ Data_Get_Struct(rb_data, git_##_object, data); \
533
+ git_repository_set_##_object(repo, data); \
534
+ rb_old_data = rb_iv_get(self, "@" #_object); \
535
+ if (!NIL_P(rb_old_data)) rugged_set_owner(rb_old_data, Qnil); \
536
+ rugged_set_owner(rb_data, self); \
537
+ rb_iv_set(self, "@" #_object, rb_data); \
538
+ return Qnil; \
539
+
540
+
541
+ /*
542
+ * call-seq:
543
+ * repo.index = idx
544
+ *
545
+ * Set the index for this +Repository+. +idx+ must be a instance of
546
+ * Rugged::Index. This index will be used internally by all
547
+ * operations that use the Git index on +repo+.
548
+ *
549
+ * Note that it's not necessary to set the +index+ for any repository;
550
+ * by default repositories are loaded with the index file that can be
551
+ * located on the +.git+ folder in the filesystem.
552
+ */
553
+ static VALUE rb_git_repo_set_index(VALUE self, VALUE rb_data)
554
+ {
555
+ RB_GIT_REPO_OWNED_SET(rb_cRuggedIndex, index);
556
+ }
557
+
558
+ /*
559
+ * call-seq:
560
+ * repo.index -> idx
561
+ *
562
+ * Return the default index for this repository.
563
+ */
564
+ static VALUE rb_git_repo_get_index(VALUE self)
565
+ {
566
+ RB_GIT_REPO_OWNED_GET(rb_cRuggedIndex, index);
567
+ }
568
+
569
+ /*
570
+ * call-seq:
571
+ * repo.config = cfg
572
+ *
573
+ * Set the configuration file for this +Repository+. +cfg+ must be a instance of
574
+ * Rugged::Config. This config file will be used internally by all
575
+ * operations that need to lookup configuration settings on +repo+.
576
+ *
577
+ * Note that it's not necessary to set the +config+ for any repository;
578
+ * by default repositories are loaded with their relevant config files
579
+ * on the filesystem, and the corresponding global and system files if
580
+ * they can be found.
581
+ */
582
+ static VALUE rb_git_repo_set_config(VALUE self, VALUE rb_data)
583
+ {
584
+ RB_GIT_REPO_OWNED_SET(rb_cRuggedConfig, config);
585
+ }
586
+
587
+ /*
588
+ * call-seq:
589
+ * repo.config -> cfg
590
+ *
591
+ * Return a Rugged::Config object representing this repository's config.
592
+ */
593
+ static VALUE rb_git_repo_get_config(VALUE self)
594
+ {
595
+ RB_GIT_REPO_OWNED_GET(rb_cRuggedConfig, config);
596
+ }
597
+
598
+ /*
599
+ * call-seq:
600
+ * repo.ident = ident
601
+ *
602
+ * Set the identity to be used for writing reflogs.
603
+ *
604
+ * +ident+ can be either +nil+ or a Hash containing +name+ and/or +email+ entries.
605
+ */
606
+ static VALUE rb_git_repo_set_ident(VALUE self, VALUE rb_ident) {
607
+ VALUE rb_val;
608
+
609
+ git_repository *repo;
610
+ const char *name = NULL, *email = NULL;
611
+
612
+ Data_Get_Struct(self, git_repository, repo);
613
+
614
+ if (!NIL_P(rb_ident)) {
615
+ Check_Type(rb_ident, T_HASH);
616
+
617
+ if (!NIL_P(rb_val = rb_hash_aref(rb_ident, CSTR2SYM("name")))) {
618
+ Check_Type(rb_val, T_STRING);
619
+ name = StringValueCStr(rb_val);
620
+ }
621
+
622
+ if (!NIL_P(rb_val = rb_hash_aref(rb_ident, CSTR2SYM("email")))) {
623
+ Check_Type(rb_val, T_STRING);
624
+ email = StringValueCStr(rb_val);
625
+ }
626
+ }
627
+
628
+ rugged_exception_check(
629
+ git_repository_set_ident(repo, name, email)
630
+ );
631
+
632
+ return Qnil;
633
+ }
634
+
635
+ /*
636
+ * call-seq:
637
+ * repo.ident -> ident
638
+ *
639
+ * Return a Hash containing the identity that is used to write reflogs.
640
+ *
641
+ * +ident+ is a Hash containing +name+ and/or +email+ entries, or `nil`.
642
+ */
643
+ static VALUE rb_git_repo_get_ident(VALUE self)
644
+ {
645
+ VALUE rb_ident = rb_hash_new();
646
+
647
+ git_repository *repo;
648
+ const char *name = NULL, *email = NULL;
649
+
650
+ Data_Get_Struct(self, git_repository, repo);
651
+
652
+ rugged_exception_check(
653
+ git_repository_ident(&name, &email, repo)
654
+ );
655
+
656
+ if (name) {
657
+ rb_hash_aset(rb_ident, CSTR2SYM("name"), rb_str_new_utf8(name));
658
+ }
659
+
660
+ if (email) {
661
+ rb_hash_aset(rb_ident, CSTR2SYM("email"), rb_str_new_utf8(email));
662
+ }
663
+
664
+ return rb_ident;
665
+ }
666
+
667
+ /*
668
+ * call-seq:
669
+ * repo.merge_base(oid1, oid2, ...)
670
+ * repo.merge_base(ref1, ref2, ...)
671
+ * repo.merge_base(commit1, commit2, ...)
672
+ *
673
+ * Find a merge base, given two or more commits or oids.
674
+ * Returns nil if a merge base is not found.
675
+ */
676
+ static VALUE rb_git_repo_merge_base(VALUE self, VALUE rb_args)
677
+ {
678
+ int error = GIT_OK, i;
679
+ git_repository *repo;
680
+ git_oid base, *input_array = xmalloc(sizeof(git_oid) * RARRAY_LEN(rb_args));
681
+ int len = (int)RARRAY_LEN(rb_args);
682
+
683
+ if (len < 2)
684
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 2+)", len);
685
+
686
+ Data_Get_Struct(self, git_repository, repo);
687
+
688
+ for (i = 0; !error && i < len; ++i) {
689
+ error = rugged_oid_get(&input_array[i], repo, rb_ary_entry(rb_args, i));
690
+ }
691
+
692
+ if (error) {
693
+ xfree(input_array);
694
+ rugged_exception_check(error);
695
+ }
696
+
697
+ error = git_merge_base_many(&base, repo, len, input_array);
698
+ xfree(input_array);
699
+
700
+ if (error == GIT_ENOTFOUND)
701
+ return Qnil;
702
+
703
+ rugged_exception_check(error);
704
+
705
+ return rugged_create_oid(&base);
706
+ }
707
+
708
+ /*
709
+ * call-seq:
710
+ * repo.merge_bases(oid1, oid2, ...) -> Array
711
+ * repo.merge_bases(ref1, ref2, ...) -> Array
712
+ * repo.merge_bases(commit1, commit2, ...) -> Array
713
+ *
714
+ * Find all merge bases, given two or more commits or oids.
715
+ * Returns an empty array if no merge bases are found.
716
+ */
717
+ static VALUE rb_git_repo_merge_bases(VALUE self, VALUE rb_args)
718
+ {
719
+ int error = GIT_OK;
720
+ size_t i, len = (size_t)RARRAY_LEN(rb_args);
721
+ git_repository *repo;
722
+ git_oidarray bases = {NULL, 0};
723
+ git_oid *input_array;
724
+
725
+ VALUE rb_bases;
726
+
727
+ if (len < 2)
728
+ rb_raise(rb_eArgError, "wrong number of arguments (%ld for 2+)", RARRAY_LEN(rb_args));
729
+
730
+ Data_Get_Struct(self, git_repository, repo);
731
+
732
+ input_array = xmalloc(sizeof(git_oid) * len);
733
+
734
+ for (i = 0; !error && i < len; ++i) {
735
+ error = rugged_oid_get(&input_array[i], repo, rb_ary_entry(rb_args, i));
736
+ }
737
+
738
+ if (error) {
739
+ xfree(input_array);
740
+ rugged_exception_check(error);
741
+ }
742
+
743
+ error = git_merge_bases_many(&bases, repo, len, input_array);
744
+ xfree(input_array);
745
+
746
+ if (error != GIT_ENOTFOUND)
747
+ rugged_exception_check(error);
748
+
749
+ rb_bases = rb_ary_new2(bases.count);
750
+
751
+ for (i = 0; i < bases.count; ++i) {
752
+ rb_ary_push(rb_bases, rugged_create_oid(&bases.ids[i]));
753
+ }
754
+
755
+ git_oidarray_free(&bases);
756
+
757
+ return rb_bases;
758
+ }
759
+
760
+ /*
761
+ * call-seq:
762
+ * repo.merge_analysis(their_commit) -> Array
763
+ *
764
+ * Analyzes the given commit and determines the opportunities for merging
765
+ * it into the repository's HEAD. Returns an Array containing a combination
766
+ * of the following symbols:
767
+ *
768
+ * :normal ::
769
+ * A "normal" merge is possible, both HEAD and the given commit have
770
+ * diverged from their common ancestor. The divergent commits must be
771
+ * merged.
772
+ *
773
+ * :up_to_date ::
774
+ * The given commit is reachable from HEAD, meaning HEAD is up-to-date
775
+ * and no merge needs to be performed.
776
+ *
777
+ * :fastforward ::
778
+ * The given commit is a fast-forward from HEAD and no merge needs to be
779
+ * performed. HEAD can simply be set to the given commit.
780
+ *
781
+ * :unborn ::
782
+ * The HEAD of the current repository is "unborn" and does not point to
783
+ * a valid commit. No merge can be performed, but the caller may wish
784
+ * to simply set HEAD to the given commit.
785
+ */
786
+ static VALUE rb_git_repo_merge_analysis(int argc, VALUE *argv, VALUE self)
787
+ {
788
+ int error;
789
+ git_repository *repo;
790
+ git_commit *their_commit;
791
+ git_annotated_commit *annotated_commit;
792
+ git_merge_analysis_t analysis;
793
+ git_merge_preference_t preference;
794
+ VALUE rb_their_commit, result;
795
+
796
+ rb_scan_args(argc, argv, "10", &rb_their_commit);
797
+
798
+ Data_Get_Struct(self, git_repository, repo);
799
+
800
+ if (TYPE(rb_their_commit) == T_STRING) {
801
+ rb_their_commit = rugged_object_rev_parse(self, rb_their_commit, 1);
802
+ }
803
+
804
+ if (!rb_obj_is_kind_of(rb_their_commit, rb_cRuggedCommit)) {
805
+ rb_raise(rb_eArgError, "Expected a Rugged::Commit.");
806
+ }
807
+
808
+ Data_Get_Struct(rb_their_commit, git_commit, their_commit);
809
+
810
+ error = git_annotated_commit_lookup(&annotated_commit, repo, git_commit_id(their_commit));
811
+ rugged_exception_check(error);
812
+
813
+ error = git_merge_analysis(&analysis, &preference, repo,
814
+ /* hack as we currently only do one commit */
815
+ (const git_annotated_commit **) &annotated_commit, 1);
816
+ git_annotated_commit_free(annotated_commit);
817
+ rugged_exception_check(error);
818
+
819
+ result = rb_ary_new();
820
+ if (analysis & GIT_MERGE_ANALYSIS_NORMAL)
821
+ rb_ary_push(result, CSTR2SYM("normal"));
822
+ if (analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE)
823
+ rb_ary_push(result, CSTR2SYM("up_to_date"));
824
+ if (analysis & GIT_MERGE_ANALYSIS_FASTFORWARD)
825
+ rb_ary_push(result, CSTR2SYM("fastforward"));
826
+ if (analysis & GIT_MERGE_ANALYSIS_UNBORN)
827
+ rb_ary_push(result, CSTR2SYM("unborn"));
828
+
829
+ return result;
830
+ }
831
+
832
+ /*
833
+ * call-seq:
834
+ * repo.revert_commit(revert_commit, our_commit, options = {}) -> index
835
+ *
836
+ * Reverts the given commit against the given "our" commit, producing an
837
+ * index that reflects the result of the revert.
838
+ */
839
+ static VALUE rb_git_repo_revert_commit(int argc, VALUE *argv, VALUE self)
840
+ {
841
+ VALUE rb_revert_commit, rb_our_commit, rb_options;
842
+ git_commit *revert_commit, *our_commit;
843
+ git_index *index;
844
+ git_repository *repo;
845
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
846
+ unsigned int mainline = 0;
847
+ int error;
848
+
849
+ rb_scan_args(argc, argv, "20:", &rb_revert_commit, &rb_our_commit, &rb_options);
850
+
851
+ if (TYPE(rb_revert_commit) == T_STRING)
852
+ rb_revert_commit = rugged_object_rev_parse(self, rb_revert_commit, 1);
853
+
854
+ if (TYPE(rb_our_commit) == T_STRING)
855
+ rb_our_commit = rugged_object_rev_parse(self, rb_our_commit, 1);
856
+
857
+ if (!rb_obj_is_kind_of(rb_revert_commit, rb_cRuggedCommit) ||
858
+ !rb_obj_is_kind_of(rb_our_commit, rb_cRuggedCommit)) {
859
+ rb_raise(rb_eArgError, "Expected a Rugged::Commit.");
860
+ }
861
+
862
+ if (!NIL_P(rb_options)) {
863
+ VALUE rb_mainline;
864
+
865
+ Check_Type(rb_options, T_HASH);
866
+ rugged_parse_merge_options(&opts, rb_options);
867
+
868
+ rb_mainline = rb_hash_aref(rb_options, CSTR2SYM("mainline"));
869
+ if (!NIL_P(rb_mainline)) {
870
+ Check_Type(rb_mainline, T_FIXNUM);
871
+ mainline = FIX2UINT(rb_mainline);
872
+ }
873
+ }
874
+
875
+ Data_Get_Struct(self, git_repository, repo);
876
+ Data_Get_Struct(rb_revert_commit, git_commit, revert_commit);
877
+ Data_Get_Struct(rb_our_commit, git_commit, our_commit);
878
+
879
+ error = git_revert_commit(&index, repo, revert_commit, our_commit, mainline, &opts);
880
+ if (error == GIT_EMERGECONFLICT)
881
+ return Qnil;
882
+
883
+ rugged_exception_check(error);
884
+
885
+ return rugged_index_new(rb_cRuggedIndex, self, index);
886
+ }
887
+
888
+ /*
889
+ * call-seq:
890
+ * repo.merge_commits(our_commit, their_commit, options = {}) -> index
891
+ *
892
+ * Merges the two given commits, returning a Rugged::Index that reflects
893
+ * the result of the merge.
894
+ *
895
+ * +our_commit+ and +their_commit+ can either be Rugged::Commit objects,
896
+ * or OIDs resolving to the former.
897
+ */
898
+ static VALUE rb_git_repo_merge_commits(int argc, VALUE *argv, VALUE self)
899
+ {
900
+ VALUE rb_our_commit, rb_their_commit, rb_options;
901
+ git_commit *our_commit, *their_commit;
902
+ git_index *index;
903
+ git_repository *repo;
904
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
905
+ int error;
906
+
907
+ rb_scan_args(argc, argv, "20:", &rb_our_commit, &rb_their_commit, &rb_options);
908
+
909
+ if (TYPE(rb_our_commit) == T_STRING) {
910
+ rb_our_commit = rugged_object_rev_parse(self, rb_our_commit, 1);
911
+ }
912
+
913
+ if (!rb_obj_is_kind_of(rb_our_commit, rb_cRuggedCommit)) {
914
+ rb_raise(rb_eArgError, "Expected a Rugged::Commit.");
915
+ }
916
+
917
+ if (TYPE(rb_their_commit) == T_STRING) {
918
+ rb_their_commit = rugged_object_rev_parse(self, rb_their_commit, 1);
919
+ }
920
+
921
+ if (!rb_obj_is_kind_of(rb_their_commit, rb_cRuggedCommit)) {
922
+ rb_raise(rb_eArgError, "Expected a Rugged::Commit.");
923
+ }
924
+
925
+ if (!NIL_P(rb_options)) {
926
+ Check_Type(rb_options, T_HASH);
927
+ rugged_parse_merge_options(&opts, rb_options);
928
+ }
929
+
930
+ Data_Get_Struct(self, git_repository, repo);
931
+ Data_Get_Struct(rb_our_commit, git_commit, our_commit);
932
+ Data_Get_Struct(rb_their_commit, git_commit, their_commit);
933
+
934
+ error = git_merge_commits(&index, repo, our_commit, their_commit, &opts);
935
+ if (error == GIT_EMERGECONFLICT)
936
+ return Qnil;
937
+
938
+ rugged_exception_check(error);
939
+
940
+ return rugged_index_new(rb_cRuggedIndex, self, index);
941
+ }
942
+
943
+ /*
944
+ * call-seq:
945
+ * repo.include?(oid) -> true or false
946
+ * repo.exists?(oid) -> true or false
947
+ *
948
+ * Return whether an object with the given SHA1 OID (represented as
949
+ * a hex string of at least 7 characters) exists in the repository.
950
+ *
951
+ * repo.include?("d8786bfc97485e8d7b19b21fb88c8ef1f199fc3f") #=> true
952
+ * repo.include?("d8786bfc") #=> true
953
+ */
954
+ static VALUE rb_git_repo_exists(VALUE self, VALUE hex)
955
+ {
956
+ git_repository *repo;
957
+ git_odb *odb;
958
+ git_oid oid;
959
+ int error;
960
+
961
+ Data_Get_Struct(self, git_repository, repo);
962
+ Check_Type(hex, T_STRING);
963
+
964
+ error = git_oid_fromstrn(&oid, RSTRING_PTR(hex), RSTRING_LEN(hex));
965
+ rugged_exception_check(error);
966
+
967
+ error = git_repository_odb(&odb, repo);
968
+ rugged_exception_check(error);
969
+
970
+ error = git_odb_exists_prefix(NULL, odb, &oid, RSTRING_LEN(hex));
971
+ git_odb_free(odb);
972
+
973
+ if (error == 0 || error == GIT_EAMBIGUOUS)
974
+ return Qtrue;
975
+
976
+ return Qfalse;
977
+ }
978
+
979
+ /*
980
+ * call-seq:
981
+ * repo.read(oid) -> str
982
+ *
983
+ * Read and return the raw data of the object identified by the given +oid+.
984
+ */
985
+ static VALUE rb_git_repo_read(VALUE self, VALUE hex)
986
+ {
987
+ git_repository *repo;
988
+ git_oid oid;
989
+ int error;
990
+
991
+ Data_Get_Struct(self, git_repository, repo);
992
+ Check_Type(hex, T_STRING);
993
+
994
+ error = git_oid_fromstr(&oid, StringValueCStr(hex));
995
+ rugged_exception_check(error);
996
+
997
+ return rugged_raw_read(repo, &oid);
998
+ }
999
+
1000
+ /*
1001
+ * call-seq:
1002
+ * repo.read_header(oid) -> hash
1003
+ *
1004
+ * Read and return the header information in +repo+'s ODB
1005
+ * for the object identified by the given +oid+.
1006
+ *
1007
+ * Returns a Hash object with the following key/value pairs:
1008
+ *
1009
+ * :type ::
1010
+ * A Symbol denoting the object's type. Possible values are:
1011
+ * +:tree+, +:blob+, +:commit+ or +:tag+.
1012
+ * :len ::
1013
+ * A Number representing the object's length, in bytes.
1014
+ */
1015
+ static VALUE rb_git_repo_read_header(VALUE self, VALUE hex)
1016
+ {
1017
+ git_repository *repo;
1018
+ git_oid oid;
1019
+ git_odb *odb;
1020
+ git_otype type;
1021
+ size_t len;
1022
+ VALUE rb_hash;
1023
+ int error;
1024
+
1025
+ Data_Get_Struct(self, git_repository, repo);
1026
+ Check_Type(hex, T_STRING);
1027
+
1028
+ error = git_oid_fromstr(&oid, StringValueCStr(hex));
1029
+ rugged_exception_check(error);
1030
+
1031
+ error = git_repository_odb(&odb, repo);
1032
+ rugged_exception_check(error);
1033
+
1034
+ error = git_odb_read_header(&len, &type, odb, &oid);
1035
+ git_odb_free(odb);
1036
+ rugged_exception_check(error);
1037
+
1038
+ rb_hash = rb_hash_new();
1039
+ rb_hash_aset(rb_hash, CSTR2SYM("type"), CSTR2SYM(git_object_type2string(type)));
1040
+ rb_hash_aset(rb_hash, CSTR2SYM("len"), INT2FIX(len));
1041
+
1042
+ return rb_hash;
1043
+ }
1044
+
1045
+ /**
1046
+ * call-seq:
1047
+ * repo.expand_oids([oid..], object_type = :any) -> hash
1048
+ *
1049
+ * Expand a list of short oids to their full value, assuming they exist
1050
+ * in the repository. If `object_type` is passed, OIDs are expected to be
1051
+ * of the given type.
1052
+ *
1053
+ * Returns a hash of `{ short_oid => full_oid }` for the short OIDs which
1054
+ * exist in the repository and match the expected object type. Missing OIDs
1055
+ * will not appear in the resulting hash.
1056
+ */
1057
+ static VALUE rb_git_repo_expand_oids(int argc, VALUE *argv, VALUE self)
1058
+ {
1059
+ VALUE rb_result, rb_oids, rb_expected_type;
1060
+
1061
+ git_otype expected_type = GIT_OBJ_ANY;
1062
+
1063
+ git_repository *repo;
1064
+ git_oid oid;
1065
+ git_odb *odb;
1066
+ int i, error;
1067
+
1068
+ Data_Get_Struct(self, git_repository, repo);
1069
+
1070
+ rb_scan_args(argc, argv, "11", &rb_oids, &rb_expected_type);
1071
+
1072
+ Check_Type(rb_oids, T_ARRAY);
1073
+ expected_type = rugged_otype_get(rb_expected_type);
1074
+
1075
+ error = git_repository_odb(&odb, repo);
1076
+ rugged_exception_check(error);
1077
+
1078
+ rb_result = rb_hash_new();
1079
+
1080
+ for (i = 0; i < RARRAY_LEN(rb_oids); ++i) {
1081
+ VALUE hex_oid = rb_ary_entry(rb_oids, i);
1082
+ git_oid found_oid;
1083
+
1084
+ if (TYPE(hex_oid) != T_STRING) {
1085
+ git_odb_free(odb);
1086
+ rb_raise(rb_eTypeError, "Expected a SHA1 OID");
1087
+ }
1088
+
1089
+ error = git_oid_fromstrn(&oid, RSTRING_PTR(hex_oid), RSTRING_LEN(hex_oid));
1090
+ if (error < 0) {
1091
+ git_odb_free(odb);
1092
+ rugged_exception_check(error);
1093
+ }
1094
+
1095
+ error = git_odb_exists_prefix(&found_oid, odb, &oid, RSTRING_LEN(hex_oid));
1096
+
1097
+ if (!error) {
1098
+ if (expected_type != GIT_OBJ_ANY) {
1099
+ size_t found_size;
1100
+ git_otype found_type;
1101
+
1102
+ if (git_odb_read_header(&found_size, &found_type, odb, &found_oid) < 0)
1103
+ continue;
1104
+
1105
+ if (found_type != expected_type)
1106
+ continue;
1107
+ }
1108
+
1109
+ rb_hash_aset(rb_result, hex_oid, rugged_create_oid(&found_oid));
1110
+ }
1111
+ }
1112
+
1113
+ git_odb_free(odb);
1114
+ return rb_result;
1115
+ }
1116
+
1117
+ /*
1118
+ * call-seq:
1119
+ * repo.descendant_of?(commit, ancestor) -> true or false
1120
+ *
1121
+ * +commit+ and +ancestor+ must be String commit OIDs or instances of Rugged::Commit.
1122
+ *
1123
+ * Returns true if +commit+ is a descendant of +ancestor+, or false if not.
1124
+ */
1125
+ static VALUE rb_git_repo_descendant_of(VALUE self, VALUE rb_commit, VALUE rb_ancestor)
1126
+ {
1127
+ int result;
1128
+ int error;
1129
+ git_repository *repo;
1130
+ git_oid commit, ancestor;
1131
+
1132
+ Data_Get_Struct(self, git_repository, repo);
1133
+
1134
+ error = rugged_oid_get(&commit, repo, rb_commit);
1135
+ rugged_exception_check(error);
1136
+
1137
+ error = rugged_oid_get(&ancestor, repo, rb_ancestor);
1138
+ rugged_exception_check(error);
1139
+
1140
+ result = git_graph_descendant_of(repo, &commit, &ancestor);
1141
+ rugged_exception_check(result);
1142
+
1143
+ return result ? Qtrue : Qfalse;
1144
+ }
1145
+
1146
+ /*
1147
+ * call-seq:
1148
+ * Repository.hash_data(str, type) -> oid
1149
+ *
1150
+ * Hash the contents of +str+ as raw bytes (ignoring any encoding
1151
+ * information) and adding the relevant header corresponding to +type+,
1152
+ * and return a hex string representing the result from the hash.
1153
+ *
1154
+ * Repository.hash_data('hello world', :commit) #=> "de5ba987198bcf2518885f0fc1350e5172cded78"
1155
+ *
1156
+ * Repository.hash_data('hello_world', :tag) #=> "9d09060c850defbc7711d08b57def0d14e742f4e"
1157
+ */
1158
+ static VALUE rb_git_repo_hash(VALUE self, VALUE rb_buffer, VALUE rb_type)
1159
+ {
1160
+ int error;
1161
+ git_oid oid;
1162
+
1163
+ Check_Type(rb_buffer, T_STRING);
1164
+
1165
+ error = git_odb_hash(&oid,
1166
+ RSTRING_PTR(rb_buffer),
1167
+ RSTRING_LEN(rb_buffer),
1168
+ rugged_otype_get(rb_type)
1169
+ );
1170
+ rugged_exception_check(error);
1171
+
1172
+ return rugged_create_oid(&oid);
1173
+ }
1174
+
1175
+ /*
1176
+ * call-seq:
1177
+ * Repository.hash_file(path, type) -> oid
1178
+ *
1179
+ * Hash the contents of the file pointed at by +path+, assuming
1180
+ * that it'd be stored in the ODB with the given +type+, and return
1181
+ * a hex string representing the SHA1 OID resulting from the hash.
1182
+ *
1183
+ * Repository.hash_file('foo.txt', :commit) #=> "de5ba987198bcf2518885f0fc1350e5172cded78"
1184
+ *
1185
+ * Repository.hash_file('foo.txt', :tag) #=> "9d09060c850defbc7711d08b57def0d14e742f4e"
1186
+ */
1187
+ static VALUE rb_git_repo_hashfile(VALUE self, VALUE rb_path, VALUE rb_type)
1188
+ {
1189
+ int error;
1190
+ git_oid oid;
1191
+
1192
+ Check_Type(rb_path, T_STRING);
1193
+
1194
+ error = git_odb_hashfile(&oid,
1195
+ StringValueCStr(rb_path),
1196
+ rugged_otype_get(rb_type)
1197
+ );
1198
+ rugged_exception_check(error);
1199
+
1200
+ return rugged_create_oid(&oid);
1201
+ }
1202
+
1203
+ /*
1204
+ * call-seq:
1205
+ * repo.write(buffer, type) -> oid
1206
+ *
1207
+ * Write the data contained in the +buffer+ string as a raw object of the
1208
+ * given +type+ into the repository's object database.
1209
+ *
1210
+ * +type+ can be either +:tag+, +:commit+, +:tree+ or +:blob+.
1211
+ *
1212
+ * Returns the newly created object's oid.
1213
+ */
1214
+ static VALUE rb_git_repo_write(VALUE self, VALUE rb_buffer, VALUE rub_type)
1215
+ {
1216
+ git_repository *repo;
1217
+ git_odb_stream *stream;
1218
+
1219
+ git_odb *odb;
1220
+ git_oid oid;
1221
+ int error;
1222
+
1223
+ git_otype type;
1224
+
1225
+ Data_Get_Struct(self, git_repository, repo);
1226
+ Check_Type(rb_buffer, T_STRING);
1227
+
1228
+ error = git_repository_odb(&odb, repo);
1229
+ rugged_exception_check(error);
1230
+
1231
+ type = rugged_otype_get(rub_type);
1232
+
1233
+ error = git_odb_open_wstream(&stream, odb, RSTRING_LEN(rb_buffer), type);
1234
+ git_odb_free(odb);
1235
+ rugged_exception_check(error);
1236
+
1237
+ error = git_odb_stream_write(stream, RSTRING_PTR(rb_buffer), RSTRING_LEN(rb_buffer));
1238
+ if (!error)
1239
+ error = git_odb_stream_finalize_write(&oid, stream);
1240
+
1241
+ git_odb_stream_free(stream);
1242
+ rugged_exception_check(error);
1243
+
1244
+ return rugged_create_oid(&oid);
1245
+ }
1246
+
1247
+ #define RB_GIT_REPO_GETTER(method) \
1248
+ git_repository *repo; \
1249
+ int error; \
1250
+ Data_Get_Struct(self, git_repository, repo); \
1251
+ error = git_repository_##method(repo); \
1252
+ rugged_exception_check(error); \
1253
+ return error ? Qtrue : Qfalse; \
1254
+
1255
+ /*
1256
+ * call-seq:
1257
+ * repo.bare? -> true or false
1258
+ *
1259
+ * Return whether a repository is bare or not. A bare repository has no
1260
+ * working directory.
1261
+ */
1262
+ static VALUE rb_git_repo_is_bare(VALUE self)
1263
+ {
1264
+ RB_GIT_REPO_GETTER(is_bare);
1265
+ }
1266
+
1267
+ /*
1268
+ * call-seq:
1269
+ * repo.shallow? -> true or false
1270
+ *
1271
+ * Return whether a repository is a shallow clone or not. A shallow clone has
1272
+ * a truncated history and can not be cloned or fetched from, nor can be
1273
+ * pushed from nor into it.
1274
+ */
1275
+ static VALUE rb_git_repo_is_shallow(VALUE self)
1276
+ {
1277
+ RB_GIT_REPO_GETTER(is_shallow);
1278
+ }
1279
+
1280
+ /*
1281
+ * call-seq:
1282
+ * repo.empty? -> true or false
1283
+ *
1284
+ * Return whether a repository is empty or not. An empty repository has just
1285
+ * been initialized and has no commits yet.
1286
+ */
1287
+ static VALUE rb_git_repo_is_empty(VALUE self)
1288
+ {
1289
+ RB_GIT_REPO_GETTER(is_empty);
1290
+ }
1291
+
1292
+ /*
1293
+ * call-seq:
1294
+ * repo.head_detached? -> true or false
1295
+ *
1296
+ * Return whether the +HEAD+ of a repository is detached or not.
1297
+ */
1298
+ static VALUE rb_git_repo_head_detached(VALUE self)
1299
+ {
1300
+ RB_GIT_REPO_GETTER(head_detached);
1301
+ }
1302
+
1303
+ /*
1304
+ * call-seq:
1305
+ * repo.head_unborn? -> true or false
1306
+ *
1307
+ * Return whether the current branch is unborn (+HEAD+ points to a
1308
+ * non-existent branch).
1309
+ */
1310
+ static VALUE rb_git_repo_head_unborn(VALUE self)
1311
+ {
1312
+ RB_GIT_REPO_GETTER(head_unborn);
1313
+ }
1314
+
1315
+ /*
1316
+ * call-seq:
1317
+ * repo.head = str
1318
+ *
1319
+ * Make the repository's +HEAD+ point to the specified reference.
1320
+ */
1321
+ static VALUE rb_git_repo_set_head(VALUE self, VALUE rb_head)
1322
+ {
1323
+ git_repository *repo;
1324
+ int error;
1325
+
1326
+ Data_Get_Struct(self, git_repository, repo);
1327
+
1328
+ Check_Type(rb_head, T_STRING);
1329
+ error = git_repository_set_head(repo, StringValueCStr(rb_head));
1330
+ rugged_exception_check(error);
1331
+
1332
+ return Qnil;
1333
+ }
1334
+
1335
+ /*
1336
+ * call-seq:
1337
+ * repo.head -> ref
1338
+ *
1339
+ * Retrieve and resolve the reference pointed at by the repository's +HEAD+.
1340
+ *
1341
+ * Returns +nil+ if +HEAD+ is missing.
1342
+ */
1343
+ static VALUE rb_git_repo_get_head(VALUE self)
1344
+ {
1345
+ git_repository *repo;
1346
+ git_reference *head;
1347
+ int error;
1348
+
1349
+ Data_Get_Struct(self, git_repository, repo);
1350
+
1351
+ error = git_repository_head(&head, repo);
1352
+ if (error == GIT_ENOTFOUND)
1353
+ return Qnil;
1354
+ else
1355
+ rugged_exception_check(error);
1356
+
1357
+ return rugged_ref_new(rb_cRuggedReference, self, head);
1358
+ }
1359
+
1360
+ /*
1361
+ * call-seq:
1362
+ * repo.path -> path
1363
+ *
1364
+ * Return the full, normalized path to this repository. For non-bare repositories,
1365
+ * this is the path of the actual +.git+ folder, not the working directory.
1366
+ *
1367
+ * repo.path #=> "/home/foo/workthing/.git"
1368
+ */
1369
+ static VALUE rb_git_repo_path(VALUE self)
1370
+ {
1371
+ git_repository *repo;
1372
+ const char *path;
1373
+
1374
+ Data_Get_Struct(self, git_repository, repo);
1375
+ path = git_repository_path(repo);
1376
+
1377
+ return path ? rb_str_new_utf8(path) : Qnil;
1378
+ }
1379
+
1380
+ /*
1381
+ * call-seq:
1382
+ * repo.workdir -> path or nil
1383
+ *
1384
+ * Return the working directory for this repository, or +nil+ if
1385
+ * the repository is bare.
1386
+ *
1387
+ * repo1.bare? #=> false
1388
+ * repo1.workdir #=> "/home/foo/workthing/"
1389
+ *
1390
+ * repo2.bare? #=> true
1391
+ * repo2.workdir #=> nil
1392
+ */
1393
+ static VALUE rb_git_repo_workdir(VALUE self)
1394
+ {
1395
+ git_repository *repo;
1396
+ const char *workdir;
1397
+
1398
+ Data_Get_Struct(self, git_repository, repo);
1399
+ workdir = git_repository_workdir(repo);
1400
+
1401
+ return workdir ? rb_str_new_utf8(workdir) : Qnil;
1402
+ }
1403
+
1404
+ /*
1405
+ * call-seq:
1406
+ * repo.workdir = path
1407
+ *
1408
+ * Sets the working directory of +repo+ to +path+. All internal
1409
+ * operations on +repo+ that affect the working directory will
1410
+ * instead use +path+.
1411
+ *
1412
+ * The +workdir+ can be set on bare repositories to temporarily
1413
+ * turn them into normal repositories.
1414
+ *
1415
+ * repo.bare? #=> true
1416
+ * repo.workdir = "/tmp/workdir"
1417
+ * repo.bare? #=> false
1418
+ * repo.checkout
1419
+ */
1420
+ static VALUE rb_git_repo_set_workdir(VALUE self, VALUE rb_workdir)
1421
+ {
1422
+ git_repository *repo;
1423
+
1424
+ Data_Get_Struct(self, git_repository, repo);
1425
+ Check_Type(rb_workdir, T_STRING);
1426
+
1427
+ rugged_exception_check(
1428
+ git_repository_set_workdir(repo, StringValueCStr(rb_workdir), 0)
1429
+ );
1430
+
1431
+ return Qnil;
1432
+ }
1433
+
1434
+ /*
1435
+ * call-seq:
1436
+ * Repository.discover(path = nil, across_fs = true) -> repository
1437
+ *
1438
+ * Traverse +path+ upwards until a Git working directory with a +.git+
1439
+ * folder has been found, open it and return it as a +Repository+
1440
+ * object.
1441
+ *
1442
+ * If +path+ is +nil+, the current working directory will be used as
1443
+ * a starting point.
1444
+ *
1445
+ * If +across_fs+ is +true+, the traversal won't stop when reaching
1446
+ * a different device than the one that contained +path+ (only applies
1447
+ * to UNIX-based OSses).
1448
+ */
1449
+ static VALUE rb_git_repo_discover(int argc, VALUE *argv, VALUE klass)
1450
+ {
1451
+ git_repository *repo;
1452
+ VALUE rb_path, rb_across_fs;
1453
+ git_buf repository_path = { NULL };
1454
+ int error, across_fs = 0;
1455
+
1456
+ rb_scan_args(argc, argv, "02", &rb_path, &rb_across_fs);
1457
+
1458
+ if (NIL_P(rb_path)) {
1459
+ VALUE rb_dir = rb_const_get(rb_cObject, rb_intern("Dir"));
1460
+ rb_path = rb_funcall(rb_dir, rb_intern("pwd"), 0);
1461
+ }
1462
+
1463
+ if (!NIL_P(rb_across_fs)) {
1464
+ across_fs = rugged_parse_bool(rb_across_fs);
1465
+ }
1466
+
1467
+ Check_Type(rb_path, T_STRING);
1468
+
1469
+ error = git_repository_discover(
1470
+ &repository_path,
1471
+ StringValueCStr(rb_path),
1472
+ across_fs,
1473
+ NULL
1474
+ );
1475
+
1476
+ rugged_exception_check(error);
1477
+
1478
+ error = git_repository_open(&repo, repository_path.ptr);
1479
+ git_buf_free(&repository_path);
1480
+
1481
+ rugged_exception_check(error);
1482
+
1483
+ return rugged_repo_new(klass, repo);
1484
+ }
1485
+
1486
+ static VALUE flags_to_rb(unsigned int flags)
1487
+ {
1488
+ VALUE rb_flags = rb_ary_new();
1489
+
1490
+ if (flags & GIT_STATUS_INDEX_NEW)
1491
+ rb_ary_push(rb_flags, CSTR2SYM("index_new"));
1492
+
1493
+ if (flags & GIT_STATUS_INDEX_MODIFIED)
1494
+ rb_ary_push(rb_flags, CSTR2SYM("index_modified"));
1495
+
1496
+ if (flags & GIT_STATUS_INDEX_DELETED)
1497
+ rb_ary_push(rb_flags, CSTR2SYM("index_deleted"));
1498
+
1499
+ if (flags & GIT_STATUS_WT_NEW)
1500
+ rb_ary_push(rb_flags, CSTR2SYM("worktree_new"));
1501
+
1502
+ if (flags & GIT_STATUS_WT_MODIFIED)
1503
+ rb_ary_push(rb_flags, CSTR2SYM("worktree_modified"));
1504
+
1505
+ if (flags & GIT_STATUS_WT_DELETED)
1506
+ rb_ary_push(rb_flags, CSTR2SYM("worktree_deleted"));
1507
+
1508
+ if (flags & GIT_STATUS_IGNORED)
1509
+ rb_ary_push(rb_flags, CSTR2SYM("ignored"));
1510
+
1511
+ return rb_flags;
1512
+ }
1513
+
1514
+ static int rugged__status_cb(const char *path, unsigned int flags, void *payload)
1515
+ {
1516
+ rb_funcall((VALUE)payload, rb_intern("call"), 2,
1517
+ rb_str_new_utf8(path), flags_to_rb(flags)
1518
+ );
1519
+
1520
+ return GIT_OK;
1521
+ }
1522
+
1523
+ /*
1524
+ * call-seq:
1525
+ * repo.status { |file, status_data| block }
1526
+ * repo.status(path) -> status_data
1527
+ *
1528
+ * Returns the status for one or more files in the working directory
1529
+ * of the repository. This is equivalent to the +git status+ command.
1530
+ *
1531
+ * The returned +status_data+ is always an array containing one or more
1532
+ * status flags as Ruby symbols. Possible flags are:
1533
+ *
1534
+ * - +:index_new+: the file is new in the index
1535
+ * - +:index_modified+: the file has been modified in the index
1536
+ * - +:index_deleted+: the file has been deleted from the index
1537
+ * - +:worktree_new+: the file is new in the working directory
1538
+ * - +:worktree_modified+: the file has been modified in the working directory
1539
+ * - +:worktree_deleted+: the file has been deleted from the working directory
1540
+ *
1541
+ * If a +block+ is given, status information will be gathered for every
1542
+ * single file on the working dir. The +block+ will be called with the
1543
+ * status data for each file.
1544
+ *
1545
+ * repo.status { |file, status_data| puts "#{file} has status: #{status_data.inspect}" }
1546
+ *
1547
+ * results in, for example:
1548
+ *
1549
+ * src/diff.c has status: [:index_new, :worktree_new]
1550
+ * README has status: [:worktree_modified]
1551
+ *
1552
+ * If a +path+ is given instead, the function will return the +status_data+ for
1553
+ * the file pointed to by path, or raise an exception if the path doesn't exist.
1554
+ *
1555
+ * +path+ must be relative to the repository's working directory.
1556
+ *
1557
+ * repo.status('src/diff.c') #=> [:index_new, :worktree_new]
1558
+ */
1559
+ static VALUE rb_git_repo_status(int argc, VALUE *argv, VALUE self)
1560
+ {
1561
+ int error;
1562
+ VALUE rb_path;
1563
+ git_repository *repo;
1564
+
1565
+ Data_Get_Struct(self, git_repository, repo);
1566
+
1567
+ if (rb_scan_args(argc, argv, "01", &rb_path) == 1) {
1568
+ unsigned int flags;
1569
+ Check_Type(rb_path, T_STRING);
1570
+ error = git_status_file(&flags, repo, StringValueCStr(rb_path));
1571
+ rugged_exception_check(error);
1572
+
1573
+ return flags_to_rb(flags);
1574
+ }
1575
+
1576
+ if (!rb_block_given_p())
1577
+ rb_raise(rb_eRuntimeError,
1578
+ "A block was expected for iterating through "
1579
+ "the repository contents.");
1580
+
1581
+ error = git_status_foreach(
1582
+ repo,
1583
+ &rugged__status_cb,
1584
+ (void *)rb_block_proc()
1585
+ );
1586
+
1587
+ rugged_exception_check(error);
1588
+ return Qnil;
1589
+ }
1590
+
1591
+ static int rugged__each_id_cb(const git_oid *id, void *payload)
1592
+ {
1593
+ int *exception = (int *)payload;
1594
+ rb_protect(rb_yield, rugged_create_oid(id), exception);
1595
+ return *exception ? GIT_ERROR : GIT_OK;
1596
+ }
1597
+
1598
+ /*
1599
+ * call-seq:
1600
+ * repo.each_id { |id| block }
1601
+ * repo.each_id -> Iterator
1602
+ *
1603
+ * Call the given +block+ once with every object ID found in +repo+
1604
+ * and all its alternates. Object IDs are passed as 40-character
1605
+ * strings.
1606
+ */
1607
+ static VALUE rb_git_repo_each_id(VALUE self)
1608
+ {
1609
+ git_repository *repo;
1610
+ git_odb *odb;
1611
+ int error, exception = 0;
1612
+
1613
+ if (!rb_block_given_p())
1614
+ return rb_funcall(self, rb_intern("to_enum"), 1, CSTR2SYM("each_id"));
1615
+
1616
+ Data_Get_Struct(self, git_repository, repo);
1617
+
1618
+ error = git_repository_odb(&odb, repo);
1619
+ rugged_exception_check(error);
1620
+
1621
+ error = git_odb_foreach(odb, &rugged__each_id_cb, &exception);
1622
+ git_odb_free(odb);
1623
+
1624
+ if (exception)
1625
+ rb_jump_tag(exception);
1626
+ rugged_exception_check(error);
1627
+
1628
+ return Qnil;
1629
+ }
1630
+
1631
+ static int parse_reset_type(VALUE rb_reset_type)
1632
+ {
1633
+ ID id_reset_type;
1634
+
1635
+ Check_Type(rb_reset_type, T_SYMBOL);
1636
+ id_reset_type = SYM2ID(rb_reset_type);
1637
+
1638
+ if (id_reset_type == rb_intern("soft")) {
1639
+ return GIT_RESET_SOFT;
1640
+ } else if (id_reset_type == rb_intern("mixed")) {
1641
+ return GIT_RESET_MIXED;
1642
+ } else if (id_reset_type == rb_intern("hard")) {
1643
+ return GIT_RESET_HARD;
1644
+ } else {
1645
+ rb_raise(rb_eArgError,
1646
+ "Invalid reset type. Expected `:soft`, `:mixed` or `:hard`");
1647
+ }
1648
+ }
1649
+
1650
+ /*
1651
+ * call-seq:
1652
+ * repo.reset(target, reset_type) -> nil
1653
+ *
1654
+ * Sets the current head to the specified commit oid and optionally
1655
+ * resets the index and working tree to match.
1656
+ * - +target+: Rugged::Commit, Rugged::Tag or rev that resolves to a commit or tag object
1657
+ * - +reset_type+: +:soft+, +:mixed+ or +:hard+
1658
+ *
1659
+ * [:soft]
1660
+ * the head will be moved to the commit.
1661
+ * [:mixed]
1662
+ * will trigger a +:soft+ reset, plus the index will be replaced
1663
+ * with the content of the commit tree.
1664
+ * [:hard]
1665
+ * will trigger a +:mixed+ reset and the working directory will be
1666
+ * replaced with the content of the index. (Untracked and ignored files
1667
+ * will be left alone)
1668
+ *
1669
+ * Examples:
1670
+ *
1671
+ * repo.reset('origin/master', :hard) #=> nil
1672
+ */
1673
+ static VALUE rb_git_repo_reset(VALUE self, VALUE rb_target, VALUE rb_reset_type)
1674
+ {
1675
+ git_repository *repo;
1676
+ int reset_type;
1677
+ git_object *target = NULL;
1678
+ int error;
1679
+
1680
+ Data_Get_Struct(self, git_repository, repo);
1681
+
1682
+ reset_type = parse_reset_type(rb_reset_type);
1683
+ target = rugged_object_get(repo, rb_target, GIT_OBJ_ANY);
1684
+
1685
+ error = git_reset(repo, target, reset_type, NULL);
1686
+
1687
+ git_object_free(target);
1688
+
1689
+ rugged_exception_check(error);
1690
+
1691
+ return Qnil;
1692
+ }
1693
+
1694
+ /*
1695
+ * call-seq:
1696
+ * repo.reset_path(pathspecs, target=nil) -> nil
1697
+ *
1698
+ * Updates entries in the index from the +target+ commit tree, matching
1699
+ * the given +pathspecs+.
1700
+ *
1701
+ * Passing a nil +target+ will result in removing
1702
+ * entries in the index matching the provided pathspecs.
1703
+ *
1704
+ * - +pathspecs+: list of pathspecs to operate on (+String+ or +Array+ of +String+ objects)
1705
+ * - +target+(optional): Rugged::Commit, Rugged::Tag or rev that resolves to a commit or tag object.
1706
+ *
1707
+ * Examples:
1708
+ * reset_path(File.join('subdir','file.txt'), '441034f860c1d5d90e4188d11ae0d325176869a8') #=> nil
1709
+ */
1710
+ static VALUE rb_git_repo_reset_path(int argc, VALUE *argv, VALUE self)
1711
+ {
1712
+ git_repository *repo;
1713
+ git_object *target = NULL;
1714
+ git_strarray pathspecs;
1715
+ VALUE rb_target, rb_paths;
1716
+ int error = 0;
1717
+
1718
+ pathspecs.strings = NULL;
1719
+ pathspecs.count = 0;
1720
+
1721
+ Data_Get_Struct(self, git_repository, repo);
1722
+
1723
+ rb_scan_args(argc, argv, "11", &rb_paths, &rb_target);
1724
+
1725
+ rugged_rb_ary_to_strarray(rb_paths, &pathspecs);
1726
+
1727
+ if (!NIL_P(rb_target))
1728
+ target = rugged_object_get(repo, rb_target, GIT_OBJ_ANY);
1729
+
1730
+ error = git_reset_default(repo, target, &pathspecs);
1731
+
1732
+ xfree(pathspecs.strings);
1733
+ git_object_free(target);
1734
+
1735
+ rugged_exception_check(error);
1736
+
1737
+ return Qnil;
1738
+ }
1739
+
1740
+ /*
1741
+ * call-seq:
1742
+ * repo.close -> nil
1743
+ *
1744
+ * Frees all the resources used by this repository immediately. The repository can
1745
+ * still be used after this call. Resources will be opened as necessary.
1746
+ *
1747
+ * It is not required to call this method explicitly. Repositories are closed
1748
+ * automatically before garbage collection
1749
+ */
1750
+ static VALUE rb_git_repo_close(VALUE self)
1751
+ {
1752
+ git_repository *repo;
1753
+ Data_Get_Struct(self, git_repository, repo);
1754
+
1755
+ git_repository__cleanup(repo);
1756
+
1757
+ return Qnil;
1758
+ }
1759
+
1760
+ /*
1761
+ * call-seq:
1762
+ * repo.namespace = new_namespace
1763
+ *
1764
+ * Sets the active namespace for the repository.
1765
+ * If set to nil, no namespace will be active.
1766
+ */
1767
+ static VALUE rb_git_repo_set_namespace(VALUE self, VALUE rb_namespace)
1768
+ {
1769
+ git_repository *repo;
1770
+ int error;
1771
+
1772
+ Data_Get_Struct(self, git_repository, repo);
1773
+
1774
+ if (!NIL_P(rb_namespace)) {
1775
+ Check_Type(rb_namespace, T_STRING);
1776
+ error = git_repository_set_namespace(repo, StringValueCStr(rb_namespace));
1777
+ } else {
1778
+ error = git_repository_set_namespace(repo, NULL);
1779
+ }
1780
+ rugged_exception_check(error);
1781
+
1782
+ return Qnil;
1783
+ }
1784
+
1785
+ /*
1786
+ * call-seq:
1787
+ * repo.namespace -> str
1788
+ *
1789
+ * Returns the active namespace for the repository.
1790
+ */
1791
+ static VALUE rb_git_repo_get_namespace(VALUE self)
1792
+ {
1793
+ git_repository *repo;
1794
+ const char *namespace;
1795
+
1796
+ Data_Get_Struct(self, git_repository, repo);
1797
+
1798
+ namespace = git_repository_get_namespace(repo);
1799
+ return namespace ? rb_str_new_utf8(namespace) : Qnil;
1800
+ }
1801
+
1802
+ /*
1803
+ * call-seq:
1804
+ * repo.ahead_behind(local, upstream) -> Array
1805
+ *
1806
+ * Returns a 2 element Array containing the number of commits
1807
+ * that the upstream object is ahead and behind the local object.
1808
+ *
1809
+ * +local+ and +upstream+ can either be strings containing SHA1 OIDs or
1810
+ * Rugged::Object instances.
1811
+ */
1812
+ static VALUE rb_git_repo_ahead_behind(VALUE self, VALUE rb_local, VALUE rb_upstream) {
1813
+ git_repository *repo;
1814
+ int error;
1815
+ git_oid local, upstream;
1816
+ size_t ahead, behind;
1817
+ VALUE rb_result;
1818
+
1819
+ Data_Get_Struct(self, git_repository, repo);
1820
+
1821
+ error = rugged_oid_get(&local, repo, rb_local);
1822
+ rugged_exception_check(error);
1823
+
1824
+ error = rugged_oid_get(&upstream, repo, rb_upstream);
1825
+ rugged_exception_check(error);
1826
+
1827
+ error = git_graph_ahead_behind(&ahead, &behind, repo, &local, &upstream);
1828
+ rugged_exception_check(error);
1829
+
1830
+ rb_result = rb_ary_new2(2);
1831
+ rb_ary_push(rb_result, INT2FIX((int) ahead));
1832
+ rb_ary_push(rb_result, INT2FIX((int) behind));
1833
+ return rb_result;
1834
+ }
1835
+
1836
+ /*
1837
+ * call-seq:
1838
+ * repo.default_signature -> signature or nil
1839
+ *
1840
+ * Returns a +Hash+ with the default user +signature+ or +nil+.
1841
+ *
1842
+ * Looks up the +user.name+ and +user.email+ from the configuration and
1843
+ * uses the current time as the timestamp, and creates a new signature
1844
+ * based on that information. It will return +nil+ if either the
1845
+ * +user.name+ or +user.email+ are not set.
1846
+ *
1847
+ * Returns a +Hash+:
1848
+ * - +:name+: the +user.name+ config value
1849
+ * - +:email+: the +user.email+ config value
1850
+ * - +:time+: the current time as a +Time+ instance
1851
+ */
1852
+ static VALUE rb_git_repo_default_signature(VALUE self) {
1853
+ int error;
1854
+ git_repository *repo;
1855
+ git_signature *signature;
1856
+ VALUE rb_signature;
1857
+
1858
+ Data_Get_Struct(self, git_repository, repo);
1859
+
1860
+ error = git_signature_default(&signature, repo);
1861
+
1862
+ if (error == GIT_ENOTFOUND)
1863
+ return Qnil;
1864
+
1865
+ rugged_exception_check(error);
1866
+
1867
+ rb_signature = rugged_signature_new(signature, NULL);
1868
+ git_signature_free(signature);
1869
+ return rb_signature;
1870
+ }
1871
+
1872
+ void rugged__checkout_progress_cb(
1873
+ const char *path,
1874
+ size_t completed_steps,
1875
+ size_t total_steps,
1876
+ void *data
1877
+ ) {
1878
+ struct rugged_cb_payload *payload = data;
1879
+ VALUE args = rb_ary_new2(4);
1880
+ rb_ary_push(args, payload->rb_data);
1881
+ rb_ary_push(args, path == NULL ? Qnil : rb_str_new2(path));
1882
+ rb_ary_push(args, INT2FIX(completed_steps));
1883
+ rb_ary_push(args, INT2FIX(total_steps));
1884
+
1885
+ rb_protect(rugged__block_yield_splat, args, &payload->exception);
1886
+ }
1887
+
1888
+ static int rugged__checkout_notify_cb(
1889
+ git_checkout_notify_t why,
1890
+ const char *path,
1891
+ const git_diff_file *baseline,
1892
+ const git_diff_file *target,
1893
+ const git_diff_file *workdir,
1894
+ void *data
1895
+ ) {
1896
+ struct rugged_cb_payload *payload = data;
1897
+ VALUE args = rb_ary_new2(5);
1898
+ rb_ary_push(args, payload->rb_data);
1899
+
1900
+ switch (why) {
1901
+ case GIT_CHECKOUT_NOTIFY_CONFLICT:
1902
+ rb_ary_push(args, CSTR2SYM("conflict"));
1903
+ break;
1904
+
1905
+ case GIT_CHECKOUT_NOTIFY_DIRTY:
1906
+ rb_ary_push(args, CSTR2SYM("dirty"));
1907
+ break;
1908
+
1909
+ case GIT_CHECKOUT_NOTIFY_UPDATED:
1910
+ rb_ary_push(args, CSTR2SYM("updated"));
1911
+ break;
1912
+
1913
+ case GIT_CHECKOUT_NOTIFY_UNTRACKED:
1914
+ rb_ary_push(args, CSTR2SYM("untracked"));
1915
+ break;
1916
+
1917
+ case GIT_CHECKOUT_NOTIFY_IGNORED:
1918
+ rb_ary_push(args, CSTR2SYM("ignored"));
1919
+ break;
1920
+
1921
+ default:
1922
+ rb_ary_push(args, CSTR2SYM("unknown"));
1923
+ }
1924
+
1925
+ rb_ary_push(args, rb_git_delta_file_fromC(baseline));
1926
+ rb_ary_push(args, rb_git_delta_file_fromC(target));
1927
+ rb_ary_push(args, rb_git_delta_file_fromC(workdir));
1928
+
1929
+ rb_protect(rugged__block_yield_splat, args, &payload->exception);
1930
+
1931
+ return payload->exception ? GIT_ERROR : GIT_OK;
1932
+ }
1933
+
1934
+ /**
1935
+ * The caller has to free the returned git_checkout_options paths strings array.
1936
+ */
1937
+ static void rugged_parse_checkout_options(git_checkout_options *opts, VALUE rb_options)
1938
+ {
1939
+ VALUE rb_value;
1940
+
1941
+ if (NIL_P(rb_options))
1942
+ return;
1943
+
1944
+ Check_Type(rb_options, T_HASH);
1945
+
1946
+ rb_value = rb_hash_aref(rb_options, CSTR2SYM("progress"));
1947
+ if (!NIL_P(rb_value)) {
1948
+ struct rugged_cb_payload *payload = malloc(sizeof(struct rugged_cb_payload));
1949
+ payload->rb_data = rb_value;
1950
+ payload->exception = 0;
1951
+
1952
+ opts->progress_payload = payload;
1953
+ opts->progress_cb = &rugged__checkout_progress_cb;
1954
+ }
1955
+
1956
+ rb_value = rb_hash_aref(rb_options, CSTR2SYM("notify"));
1957
+ if (!NIL_P(rb_value)) {
1958
+ struct rugged_cb_payload *payload = malloc(sizeof(struct rugged_cb_payload));
1959
+ payload->rb_data = rb_value;
1960
+ payload->exception = 0;
1961
+
1962
+ opts->notify_payload = payload;
1963
+ opts->notify_cb = &rugged__checkout_notify_cb;
1964
+ }
1965
+
1966
+ if (!NIL_P(rb_value = rb_hash_aref(rb_options, CSTR2SYM("strategy")))) {
1967
+ int i;
1968
+
1969
+ rb_value = rb_ary_to_ary(rb_value);
1970
+ for (i = 0; i < RARRAY_LEN(rb_value); ++i) {
1971
+ VALUE rb_strategy = rb_ary_entry(rb_value, i);
1972
+
1973
+ if (rb_strategy == CSTR2SYM("safe")) {
1974
+ opts->checkout_strategy |= GIT_CHECKOUT_SAFE;
1975
+ } else if (rb_strategy == CSTR2SYM("force")) {
1976
+ opts->checkout_strategy |= GIT_CHECKOUT_FORCE;
1977
+ } else if (rb_strategy == CSTR2SYM("recreate_missing")) {
1978
+ opts->checkout_strategy |= GIT_CHECKOUT_RECREATE_MISSING;
1979
+ } else if (rb_strategy == CSTR2SYM("allow_conflicts")) {
1980
+ opts->checkout_strategy |= GIT_CHECKOUT_ALLOW_CONFLICTS;
1981
+ } else if (rb_strategy == CSTR2SYM("remove_untracked")) {
1982
+ opts->checkout_strategy |= GIT_CHECKOUT_REMOVE_UNTRACKED;
1983
+ } else if (rb_strategy == CSTR2SYM("remove_ignored")) {
1984
+ opts->checkout_strategy |= GIT_CHECKOUT_REMOVE_IGNORED;
1985
+ } else if (rb_strategy == CSTR2SYM("update_only")) {
1986
+ opts->checkout_strategy |= GIT_CHECKOUT_UPDATE_ONLY;
1987
+ } else if (rb_strategy == CSTR2SYM("dont_update_index")) {
1988
+ opts->checkout_strategy |= GIT_CHECKOUT_DONT_UPDATE_INDEX;
1989
+ } else if (rb_strategy == CSTR2SYM("no_refresh")) {
1990
+ opts->checkout_strategy |= GIT_CHECKOUT_NO_REFRESH;
1991
+ } else if (rb_strategy == CSTR2SYM("disable_pathspec_match")) {
1992
+ opts->checkout_strategy |= GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH;
1993
+ } else if (rb_strategy == CSTR2SYM("skip_locked_directories")) {
1994
+ opts->checkout_strategy |= GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES;
1995
+ } else if (rb_strategy == CSTR2SYM("skip_unmerged")) {
1996
+ opts->checkout_strategy |= GIT_CHECKOUT_SKIP_UNMERGED;
1997
+ } else if (rb_strategy == CSTR2SYM("use_ours")) {
1998
+ opts->checkout_strategy |= GIT_CHECKOUT_USE_OURS;
1999
+ } else if (rb_strategy == CSTR2SYM("use_theirs")) {
2000
+ opts->checkout_strategy |= GIT_CHECKOUT_USE_THEIRS;
2001
+ } else if (rb_strategy == CSTR2SYM("update_submodules")) {
2002
+ opts->checkout_strategy |= GIT_CHECKOUT_UPDATE_SUBMODULES;
2003
+ } else if (rb_strategy == CSTR2SYM("update_submodules_if_changed")) {
2004
+ opts->checkout_strategy |= GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED;
2005
+ } else if (rb_strategy != CSTR2SYM("none")) {
2006
+ rb_raise(rb_eArgError, "Unknown checkout strategy");
2007
+ }
2008
+ }
2009
+ }
2010
+
2011
+ if (!NIL_P(rb_value = rb_hash_aref(rb_options, CSTR2SYM("notify_flags")))) {
2012
+ int i;
2013
+
2014
+ rb_value = rb_ary_to_ary(rb_value);
2015
+ for (i = 0; i < RARRAY_LEN(rb_value); ++i) {
2016
+ VALUE rb_notify_flag = rb_ary_entry(rb_value, i);
2017
+
2018
+ if (rb_notify_flag == CSTR2SYM("conflict")) {
2019
+ opts->notify_flags |= GIT_CHECKOUT_NOTIFY_CONFLICT;
2020
+ } else if (rb_notify_flag == CSTR2SYM("dirty")) {
2021
+ opts->notify_flags |= GIT_CHECKOUT_NOTIFY_DIRTY;
2022
+ } else if (rb_notify_flag == CSTR2SYM("updated")) {
2023
+ opts->notify_flags |= GIT_CHECKOUT_NOTIFY_UPDATED;
2024
+ } else if (rb_notify_flag == CSTR2SYM("untracked")) {
2025
+ opts->notify_flags |= GIT_CHECKOUT_NOTIFY_UNTRACKED;
2026
+ } else if (rb_notify_flag == CSTR2SYM("ignored")) {
2027
+ opts->notify_flags |= GIT_CHECKOUT_NOTIFY_IGNORED;
2028
+ } else if (rb_notify_flag == CSTR2SYM("all")) {
2029
+ opts->notify_flags |= GIT_CHECKOUT_NOTIFY_ALL;
2030
+ } else if (rb_notify_flag != CSTR2SYM("none")) {
2031
+ rb_raise(rb_eArgError, "Unknown checkout notify flag");
2032
+ }
2033
+ }
2034
+ }
2035
+
2036
+ opts->disable_filters = RTEST(rb_hash_aref(rb_options, CSTR2SYM("disable_filters")));
2037
+
2038
+ rb_value = rb_hash_aref(rb_options, CSTR2SYM("dir_mode"));
2039
+ if (!NIL_P(rb_value)) {
2040
+ opts->dir_mode = FIX2UINT(rb_value);
2041
+ }
2042
+
2043
+ rb_value = rb_hash_aref(rb_options, CSTR2SYM("file_mode"));
2044
+ if (!NIL_P(rb_value)) {
2045
+ opts->file_mode = FIX2UINT(rb_value);
2046
+ }
2047
+
2048
+ rb_value = rb_hash_aref(rb_options, CSTR2SYM("file_open_flags"));
2049
+ if (!NIL_P(rb_value)) {
2050
+ opts->file_mode = FIX2INT(rb_value);
2051
+ }
2052
+
2053
+ rb_value = rb_hash_aref(rb_options, CSTR2SYM("target_directory"));
2054
+ if (!NIL_P(rb_value)) {
2055
+ opts->target_directory = StringValueCStr(rb_value);
2056
+ }
2057
+
2058
+ rb_value = rb_hash_aref(rb_options, CSTR2SYM("baseline"));
2059
+ if (!NIL_P(rb_value)) {
2060
+ if (rb_obj_is_kind_of(rb_value, rb_cRuggedTree)) {
2061
+ Data_Get_Struct(rb_value, git_tree, opts->baseline);
2062
+ } else {
2063
+ rb_raise(rb_eTypeError, "Expected a Rugged::Tree.");
2064
+ }
2065
+ }
2066
+
2067
+ rb_value = rb_hash_aref(rb_options, CSTR2SYM("paths"));
2068
+ rugged_rb_ary_to_strarray(rb_value, &opts->paths);
2069
+ }
2070
+
2071
+ /**
2072
+ * call-seq:
2073
+ * repo.checkout_tree(treeish[, options])
2074
+ *
2075
+ * Updates files in the index and working tree to match the content of the
2076
+ * tree pointed at by the +treeish+.
2077
+ *
2078
+ * The following options can be passed in the +options+ Hash:
2079
+ *
2080
+ * :progress ::
2081
+ * A callback that will be executed for checkout progress notifications.
2082
+ * Up to 3 parameters are passed on each execution:
2083
+ *
2084
+ * - The path to the last updated file (or +nil+ on the very first invocation).
2085
+ * - The number of completed checkout steps.
2086
+ * - The number of total checkout steps to be performed.
2087
+ *
2088
+ * :notify ::
2089
+ * A callback that will be executed for each checkout notification types specified
2090
+ * with +:notify_flags+. Up to 5 parameters are passed on each execution:
2091
+ *
2092
+ * - An array containing the +:notify_flags+ that caused the callback execution.
2093
+ * - The path of the current file.
2094
+ * - A hash describing the baseline blob (or +nil+ if it does not exist).
2095
+ * - A hash describing the target blob (or +nil+ if it does not exist).
2096
+ * - A hash describing the workdir blob (or +nil+ if it does not exist).
2097
+ *
2098
+ * :strategy ::
2099
+ * A single symbol or an array of symbols representing the strategies to use when
2100
+ * performing the checkout. Possible values are:
2101
+ *
2102
+ * :none ::
2103
+ * Perform a dry run (default).
2104
+ *
2105
+ * :safe ::
2106
+ * Allow safe updates that cannot overwrite uncommitted data.
2107
+ *
2108
+ * :recreate_missing ::
2109
+ * Allow checkout to recreate missing files.
2110
+ *
2111
+ * :force ::
2112
+ * Allow all updates to force working directory to look like index.
2113
+ *
2114
+ * :allow_conflicts ::
2115
+ * Allow checkout to make safe updates even if conflicts are found.
2116
+ *
2117
+ * :remove_untracked ::
2118
+ * Remove untracked files not in index (that are not ignored).
2119
+ *
2120
+ * :remove_ignored ::
2121
+ * Remove ignored files not in index.
2122
+ *
2123
+ * :update_only ::
2124
+ * Only update existing files, don't create new ones.
2125
+ *
2126
+ * :dont_update_index ::
2127
+ * Normally checkout updates index entries as it goes; this stops that.
2128
+ *
2129
+ * :no_refresh ::
2130
+ * Don't refresh index/config/etc before doing checkout.
2131
+ *
2132
+ * :disable_pathspec_match ::
2133
+ * Treat pathspec as simple list of exact match file paths.
2134
+ *
2135
+ * :skip_locked_directories ::
2136
+ * Ignore directories in use, they will be left empty.
2137
+ *
2138
+ * :skip_unmerged ::
2139
+ * Allow checkout to skip unmerged files (NOT IMPLEMENTED).
2140
+ *
2141
+ * :use_ours ::
2142
+ * For unmerged files, checkout stage 2 from index (NOT IMPLEMENTED).
2143
+ *
2144
+ * :use_theirs ::
2145
+ * For unmerged files, checkout stage 3 from index (NOT IMPLEMENTED).
2146
+ *
2147
+ * :update_submodules ::
2148
+ * Recursively checkout submodules with same options (NOT IMPLEMENTED).
2149
+ *
2150
+ * :update_submodules_if_changed ::
2151
+ * Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED).
2152
+ *
2153
+ * :disable_filters ::
2154
+ * If +true+, filters like CRLF line conversion will be disabled.
2155
+ *
2156
+ * :dir_mode ::
2157
+ * Mode for newly created directories. Default: +0755+.
2158
+ *
2159
+ * :file_mode ::
2160
+ * Mode for newly created files. Default: +0755+ or +0644+.
2161
+ *
2162
+ * :file_open_flags ::
2163
+ * Mode for opening files. Default: <code>IO::CREAT | IO::TRUNC | IO::WRONLY</code>.
2164
+ *
2165
+ * :notify_flags ::
2166
+ * A single symbol or an array of symbols representing the cases in which the +:notify+
2167
+ * callback should be invoked. Possible values are:
2168
+ *
2169
+ * :none ::
2170
+ * Do not invoke the +:notify+ callback (default).
2171
+ *
2172
+ * :conflict ::
2173
+ * Invoke the callback for conflicting paths.
2174
+ *
2175
+ * :dirty ::
2176
+ * Invoke the callback for "dirty" files, i.e. those that do not need an update but
2177
+ * no longer match the baseline.
2178
+ *
2179
+ * :updated ::
2180
+ * Invoke the callback for any file that was changed.
2181
+ *
2182
+ * :untracked ::
2183
+ * Invoke the callback for untracked files.
2184
+ *
2185
+ * :ignored ::
2186
+ * Invoke the callback for ignored files.
2187
+ *
2188
+ * :all ::
2189
+ * Invoke the callback for all these cases.
2190
+ *
2191
+ * :paths ::
2192
+ * A glob string or an array of glob strings specifying which paths should be taken
2193
+ * into account for the checkout operation. +nil+ will match all files.
2194
+ * Default: +nil+.
2195
+ *
2196
+ * :baseline ::
2197
+ * A Rugged::Tree that represents the current, expected contents of the workdir.
2198
+ * Default: +HEAD+.
2199
+ *
2200
+ * :target_directory ::
2201
+ * A path to an alternative workdir directory in which the checkout should be performed.
2202
+ */
2203
+ static VALUE rb_git_checkout_tree(int argc, VALUE *argv, VALUE self)
2204
+ {
2205
+ VALUE rb_treeish, rb_options;
2206
+ git_repository *repo;
2207
+ git_object *treeish;
2208
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
2209
+ struct rugged_cb_payload *payload;
2210
+ int error, exception = 0;
2211
+
2212
+ rb_scan_args(argc, argv, "10:", &rb_treeish, &rb_options);
2213
+
2214
+ if (TYPE(rb_treeish) == T_STRING) {
2215
+ rb_treeish = rugged_object_rev_parse(self, rb_treeish, 1);
2216
+ }
2217
+
2218
+ if (!rb_obj_is_kind_of(rb_treeish, rb_cRuggedCommit) &&
2219
+ !rb_obj_is_kind_of(rb_treeish, rb_cRuggedTag) &&
2220
+ !rb_obj_is_kind_of(rb_treeish, rb_cRuggedTree)) {
2221
+ rb_raise(rb_eTypeError, "Expected Rugged::Commit, Rugged::Tag or Rugged::Tree");
2222
+ }
2223
+
2224
+ Data_Get_Struct(self, git_repository, repo);
2225
+ Data_Get_Struct(rb_treeish, git_object, treeish);
2226
+
2227
+ rugged_parse_checkout_options(&opts, rb_options);
2228
+
2229
+ error = git_checkout_tree(repo, treeish, &opts);
2230
+ xfree(opts.paths.strings);
2231
+
2232
+ if ((payload = opts.notify_payload) != NULL) {
2233
+ exception = payload->exception;
2234
+ xfree(opts.notify_payload);
2235
+ }
2236
+
2237
+ if ((payload = opts.progress_payload) != NULL) {
2238
+ exception = payload->exception;
2239
+ xfree(opts.progress_payload);
2240
+ }
2241
+
2242
+ if (exception)
2243
+ rb_jump_tag(exception);
2244
+
2245
+ rugged_exception_check(error);
2246
+
2247
+ return Qnil;
2248
+ }
2249
+
2250
+ /**
2251
+ * call-seq: repo.checkout_index(index[,options]) -> nil
2252
+ *
2253
+ * Updates files in the index and the working tree to match the content of the
2254
+ * commit pointed at by +index+.
2255
+ *
2256
+ * See Repository#checkout_tree for a list of supported +options+.
2257
+ */
2258
+ static VALUE rb_git_checkout_index(int argc, VALUE *argv, VALUE self)
2259
+ {
2260
+ VALUE rb_index, rb_options;
2261
+ git_repository *repo;
2262
+ git_index *index;
2263
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
2264
+ struct rugged_cb_payload *payload;
2265
+ int error, exception = 0;
2266
+
2267
+ rb_scan_args(argc, argv, "10:", &rb_index, &rb_options);
2268
+
2269
+ if (!rb_obj_is_kind_of(rb_index, rb_cRuggedIndex))
2270
+ rb_raise(rb_eTypeError, "Expected Rugged::Index");
2271
+
2272
+ Data_Get_Struct(self, git_repository, repo);
2273
+ Data_Get_Struct(rb_index, git_index, index);
2274
+
2275
+ rugged_parse_checkout_options(&opts, rb_options);
2276
+
2277
+ error = git_checkout_index(repo, index, &opts);
2278
+ xfree(opts.paths.strings);
2279
+
2280
+ if ((payload = opts.notify_payload) != NULL) {
2281
+ exception = payload->exception;
2282
+ xfree(opts.notify_payload);
2283
+ }
2284
+
2285
+ if ((payload = opts.progress_payload) != NULL) {
2286
+ exception = payload->exception;
2287
+ xfree(opts.progress_payload);
2288
+ }
2289
+
2290
+ if (exception)
2291
+ rb_jump_tag(exception);
2292
+
2293
+ rugged_exception_check(error);
2294
+
2295
+ return Qnil;
2296
+ }
2297
+
2298
+ /**
2299
+ * call-seq: repo.checkout_head([options]) -> nil
2300
+ *
2301
+ * Updates files in the index and the working tree to match the content of the
2302
+ * commit pointed at by +HEAD+.
2303
+ *
2304
+ * See Repository#checkout_tree for a list of supported +options+.
2305
+ */
2306
+ static VALUE rb_git_checkout_head(int argc, VALUE *argv, VALUE self)
2307
+ {
2308
+ VALUE rb_options;
2309
+ git_repository *repo;
2310
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
2311
+ struct rugged_cb_payload *payload;
2312
+ int error, exception = 0;
2313
+
2314
+ rb_scan_args(argc, argv, "00:", &rb_options);
2315
+
2316
+ Data_Get_Struct(self, git_repository, repo);
2317
+
2318
+ rugged_parse_checkout_options(&opts, rb_options);
2319
+
2320
+ error = git_checkout_head(repo, &opts);
2321
+ xfree(opts.paths.strings);
2322
+
2323
+ if ((payload = opts.notify_payload) != NULL) {
2324
+ exception = payload->exception;
2325
+ xfree(opts.notify_payload);
2326
+ }
2327
+
2328
+ if ((payload = opts.progress_payload) != NULL) {
2329
+ exception = payload->exception;
2330
+ xfree(opts.progress_payload);
2331
+ }
2332
+
2333
+ if (exception)
2334
+ rb_jump_tag(exception);
2335
+
2336
+ rugged_exception_check(error);
2337
+
2338
+ return Qnil;
2339
+ }
2340
+
2341
+ /*
2342
+ * call-seq:
2343
+ * repo.path_ignored?(path) -> true or false
2344
+ *
2345
+ * Return whether a path is ignored or not.
2346
+ */
2347
+ static VALUE rb_git_repo_is_path_ignored(VALUE self, VALUE rb_path) {
2348
+ git_repository *repo;
2349
+ const char *path;
2350
+ int error;
2351
+ int ignored;
2352
+
2353
+ Data_Get_Struct(self, git_repository, repo);
2354
+ path = StringValueCStr(rb_path);
2355
+ error = git_ignore_path_is_ignored(&ignored, repo, path);
2356
+ rugged_exception_check(error);
2357
+ return ignored ? Qtrue : Qfalse;
2358
+ }
2359
+
2360
+ static void rugged_parse_cherrypick_options(git_cherrypick_options *opts, VALUE rb_options)
2361
+ {
2362
+ VALUE rb_value;
2363
+
2364
+ if (NIL_P(rb_options))
2365
+ return;
2366
+
2367
+ Check_Type(rb_options, T_HASH);
2368
+
2369
+ rb_value = rb_hash_aref(rb_options, CSTR2SYM("mainline"));
2370
+ if (!NIL_P(rb_value)) {
2371
+ opts->mainline = FIX2UINT(rb_value);
2372
+ }
2373
+ }
2374
+
2375
+ static VALUE rugged_create_attr(const char *attr)
2376
+ {
2377
+ switch (git_attr_value(attr)) {
2378
+ case GIT_ATTR_TRUE_T:
2379
+ return Qtrue;
2380
+
2381
+ case GIT_ATTR_FALSE_T:
2382
+ return Qfalse;
2383
+
2384
+ case GIT_ATTR_VALUE_T:
2385
+ return rb_str_new2(attr);
2386
+
2387
+ case GIT_ATTR_UNSPECIFIED_T:
2388
+ default:
2389
+ return Qnil;
2390
+ }
2391
+ }
2392
+
2393
+ static int foreach_attr_hash(const char *name, const char *value, void *payload)
2394
+ {
2395
+ VALUE rb_hash = (VALUE)payload;
2396
+ rb_hash_aset(rb_hash, rb_str_new2(name), rugged_create_attr(value));
2397
+ return 0;
2398
+ }
2399
+
2400
+ static VALUE rb_git_repo_attributes(int argc, VALUE *argv, VALUE self)
2401
+ {
2402
+ VALUE rb_path, rb_names, rb_options;
2403
+
2404
+ git_repository *repo;
2405
+ int error, options = 0;
2406
+
2407
+ rb_scan_args(argc, argv, "12", &rb_path, &rb_names, &rb_options);
2408
+
2409
+ Data_Get_Struct(self, git_repository, repo);
2410
+ Check_Type(rb_path, T_STRING);
2411
+
2412
+ if (!NIL_P(rb_options)) {
2413
+ Check_Type(rb_options, T_FIXNUM);
2414
+ options = FIX2INT(rb_options);
2415
+ }
2416
+
2417
+ switch (TYPE(rb_names)) {
2418
+ case T_ARRAY:
2419
+ {
2420
+ VALUE rb_result;
2421
+ const char **values;
2422
+ const char **names;
2423
+ long i, num_attr = RARRAY_LEN(rb_names);
2424
+
2425
+ if (num_attr > 32)
2426
+ rb_raise(rb_eRuntimeError, "Too many attributes requested");
2427
+
2428
+ values = alloca(num_attr * sizeof(const char *));
2429
+ names = alloca(num_attr * sizeof(const char *));
2430
+
2431
+ for (i = 0; i < num_attr; ++i) {
2432
+ VALUE attr = rb_ary_entry(rb_names, i);
2433
+ Check_Type(attr, T_STRING);
2434
+ names[i] = StringValueCStr(attr);
2435
+ }
2436
+
2437
+ error = git_attr_get_many(
2438
+ values, repo, options,
2439
+ StringValueCStr(rb_path),
2440
+ (size_t)num_attr, names);
2441
+
2442
+ rugged_exception_check(error);
2443
+
2444
+ rb_result = rb_hash_new();
2445
+ for (i = 0; i < num_attr; ++i) {
2446
+ VALUE attr = rb_ary_entry(rb_names, i);
2447
+ rb_hash_aset(rb_result, attr, rugged_create_attr(values[i]));
2448
+ }
2449
+ return rb_result;
2450
+ }
2451
+
2452
+ case T_STRING:
2453
+ {
2454
+ const char *value;
2455
+
2456
+ error = git_attr_get(
2457
+ &value, repo, options,
2458
+ StringValueCStr(rb_path),
2459
+ StringValueCStr(rb_names));
2460
+
2461
+ rugged_exception_check(error);
2462
+
2463
+ return rugged_create_attr(value);
2464
+ }
2465
+
2466
+ case T_NIL:
2467
+ {
2468
+ VALUE rb_result = rb_hash_new();
2469
+
2470
+ error = git_attr_foreach(
2471
+ repo, options,
2472
+ StringValueCStr(rb_path),
2473
+ &foreach_attr_hash,
2474
+ (void *)rb_result);
2475
+
2476
+ rugged_exception_check(error);
2477
+ return rb_result;
2478
+ }
2479
+
2480
+ default:
2481
+ rb_raise(rb_eTypeError,
2482
+ "Invalid attribute name (expected String or Array)");
2483
+ }
2484
+ }
2485
+
2486
+ /*
2487
+ * call-seq:
2488
+ * repo.cherrypick(commit[, options]) -> nil
2489
+ *
2490
+ * Cherry-pick the given commit and update the index and working
2491
+ * directory accordingly.
2492
+ *
2493
+ * `commit` can be either a string containing a commit id or a
2494
+ * `Rugged::Commit` object.
2495
+ *
2496
+ * The following options can be passed in the +options+ Hash:
2497
+ *
2498
+ * :mainline ::
2499
+ * When cherry-picking a merge, you need to specify the parent number
2500
+ * (starting from 1) which should be considered the mainline.
2501
+ */
2502
+ static VALUE rb_git_repo_cherrypick(int argc, VALUE *argv, VALUE self)
2503
+ {
2504
+ VALUE rb_options, rb_commit;
2505
+
2506
+ git_repository *repo;
2507
+ git_commit *commit;
2508
+ git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT;
2509
+
2510
+ int error;
2511
+
2512
+ rb_scan_args(argc, argv, "10:", &rb_commit, &rb_options);
2513
+
2514
+ if (TYPE(rb_commit) == T_STRING) {
2515
+ rb_commit = rugged_object_rev_parse(self, rb_commit, 1);
2516
+ }
2517
+
2518
+ if (!rb_obj_is_kind_of(rb_commit, rb_cRuggedCommit)) {
2519
+ rb_raise(rb_eArgError, "Expected a Rugged::Commit.");
2520
+ }
2521
+
2522
+ Data_Get_Struct(self, git_repository, repo);
2523
+ Data_Get_Struct(rb_commit, git_commit, commit);
2524
+
2525
+ rugged_parse_cherrypick_options(&opts, rb_options);
2526
+
2527
+ error = git_cherrypick(repo, commit, &opts);
2528
+ rugged_exception_check(error);
2529
+
2530
+ return Qnil;
2531
+ }
2532
+
2533
+ /*
2534
+ * call-seq:
2535
+ * repo.cherrypick_commit(commit, our_commit, [mainline, options]) -> nil
2536
+ *
2537
+ * Cherry-pick the given commit on the given base in-memory and
2538
+ * return an index with the result.
2539
+ *
2540
+ * `commit` can be either a string containing a commit id or a
2541
+ * `Rugged::Commit` object.
2542
+ *
2543
+ * `our_commit` is the base commit, can be either a string containing
2544
+ * a commit id or a `Rugged::Commit` object.
2545
+ *
2546
+ * `mainline` when cherry-picking a merge, this is the parent number
2547
+ * (starting from 1) which should be considered the mainline.
2548
+ */
2549
+ static VALUE rb_git_repo_cherrypick_commit(int argc, VALUE *argv, VALUE self)
2550
+ {
2551
+ VALUE rb_options, rb_commit, rb_our_commit, rb_mainline;
2552
+
2553
+ git_repository *repo;
2554
+ git_commit *commit, *our_commit;
2555
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
2556
+ git_index *index;
2557
+ int error, mainline;
2558
+
2559
+ rb_scan_args(argc, argv, "21:", &rb_commit, &rb_our_commit, &rb_mainline, &rb_options);
2560
+
2561
+ if (TYPE(rb_commit) == T_STRING) {
2562
+ rb_commit = rugged_object_rev_parse(self, rb_commit, 1);
2563
+ }
2564
+ if (TYPE(rb_our_commit) == T_STRING) {
2565
+ rb_our_commit = rugged_object_rev_parse(self, rb_our_commit, 1);
2566
+ }
2567
+
2568
+ if (!rb_obj_is_kind_of(rb_commit, rb_cRuggedCommit)) {
2569
+ rb_raise(rb_eArgError, "Expected a Rugged::Commit.");
2570
+ }
2571
+ if (!rb_obj_is_kind_of(rb_our_commit, rb_cRuggedCommit)) {
2572
+ rb_raise(rb_eArgError, "Expected a Rugged::Commit.");
2573
+ }
2574
+
2575
+ Data_Get_Struct(self, git_repository, repo);
2576
+ Data_Get_Struct(rb_commit, git_commit, commit);
2577
+ Data_Get_Struct(rb_our_commit, git_commit, our_commit);
2578
+
2579
+ rugged_parse_merge_options(&opts, rb_options);
2580
+
2581
+ mainline = NIL_P(rb_mainline) ? 0 : FIX2UINT(rb_mainline);
2582
+ error = git_cherrypick_commit(&index, repo, commit, our_commit, mainline, &opts);
2583
+ rugged_exception_check(error);
2584
+
2585
+ return rugged_index_new(rb_cRuggedIndex, self, index);
2586
+ }
2587
+
2588
+ void Init_rugged_repo(void)
2589
+ {
2590
+ id_call = rb_intern("call");
2591
+
2592
+ rb_cRuggedRepo = rb_define_class_under(rb_mRugged, "Repository", rb_cObject);
2593
+
2594
+ rb_define_singleton_method(rb_cRuggedRepo, "new", rb_git_repo_new, -1);
2595
+ rb_define_singleton_method(rb_cRuggedRepo, "bare", rb_git_repo_open_bare, -1);
2596
+ rb_define_singleton_method(rb_cRuggedRepo, "hash_data", rb_git_repo_hash, 2);
2597
+ rb_define_singleton_method(rb_cRuggedRepo, "hash_file", rb_git_repo_hashfile, 2);
2598
+ rb_define_singleton_method(rb_cRuggedRepo, "init_at", rb_git_repo_init_at, -1);
2599
+ rb_define_singleton_method(rb_cRuggedRepo, "discover", rb_git_repo_discover, -1);
2600
+ rb_define_singleton_method(rb_cRuggedRepo, "clone_at", rb_git_repo_clone_at, -1);
2601
+
2602
+ rb_define_method(rb_cRuggedRepo, "close", rb_git_repo_close, 0);
2603
+
2604
+ rb_define_method(rb_cRuggedRepo, "exists?", rb_git_repo_exists, 1);
2605
+ rb_define_method(rb_cRuggedRepo, "include?", rb_git_repo_exists, 1);
2606
+ rb_define_method(rb_cRuggedRepo, "expand_oids", rb_git_repo_expand_oids, -1);
2607
+ rb_define_method(rb_cRuggedRepo, "descendant_of?", rb_git_repo_descendant_of, 2);
2608
+
2609
+ rb_define_method(rb_cRuggedRepo, "read", rb_git_repo_read, 1);
2610
+ rb_define_method(rb_cRuggedRepo, "read_header", rb_git_repo_read_header, 1);
2611
+ rb_define_method(rb_cRuggedRepo, "write", rb_git_repo_write, 2);
2612
+ rb_define_method(rb_cRuggedRepo, "each_id", rb_git_repo_each_id, 0);
2613
+
2614
+ rb_define_method(rb_cRuggedRepo, "path", rb_git_repo_path, 0);
2615
+ rb_define_method(rb_cRuggedRepo, "workdir", rb_git_repo_workdir, 0);
2616
+ rb_define_method(rb_cRuggedRepo, "workdir=", rb_git_repo_set_workdir, 1);
2617
+ rb_define_method(rb_cRuggedRepo, "status", rb_git_repo_status, -1);
2618
+
2619
+ rb_define_method(rb_cRuggedRepo, "index", rb_git_repo_get_index, 0);
2620
+ rb_define_method(rb_cRuggedRepo, "index=", rb_git_repo_set_index, 1);
2621
+ rb_define_method(rb_cRuggedRepo, "config", rb_git_repo_get_config, 0);
2622
+ rb_define_method(rb_cRuggedRepo, "config=", rb_git_repo_set_config, 1);
2623
+
2624
+ rb_define_method(rb_cRuggedRepo, "ident", rb_git_repo_get_ident, 0);
2625
+ rb_define_method(rb_cRuggedRepo, "ident=", rb_git_repo_set_ident, 1);
2626
+
2627
+ rb_define_method(rb_cRuggedRepo, "bare?", rb_git_repo_is_bare, 0);
2628
+ rb_define_method(rb_cRuggedRepo, "shallow?", rb_git_repo_is_shallow, 0);
2629
+ rb_define_method(rb_cRuggedRepo, "empty?", rb_git_repo_is_empty, 0);
2630
+
2631
+ rb_define_method(rb_cRuggedRepo, "head_detached?", rb_git_repo_head_detached, 0);
2632
+ rb_define_method(rb_cRuggedRepo, "head_unborn?", rb_git_repo_head_unborn, 0);
2633
+ rb_define_method(rb_cRuggedRepo, "head=", rb_git_repo_set_head, 1);
2634
+ rb_define_method(rb_cRuggedRepo, "head", rb_git_repo_get_head, 0);
2635
+
2636
+ rb_define_method(rb_cRuggedRepo, "merge_base", rb_git_repo_merge_base, -2);
2637
+ rb_define_method(rb_cRuggedRepo, "merge_bases", rb_git_repo_merge_bases, -2);
2638
+
2639
+ rb_define_method(rb_cRuggedRepo, "merge_analysis", rb_git_repo_merge_analysis, -1);
2640
+ rb_define_method(rb_cRuggedRepo, "merge_commits", rb_git_repo_merge_commits, -1);
2641
+
2642
+ rb_define_method(rb_cRuggedRepo, "revert_commit", rb_git_repo_revert_commit, -1);
2643
+
2644
+ rb_define_method(rb_cRuggedRepo, "path_ignored?", rb_git_repo_is_path_ignored, 1);
2645
+
2646
+ rb_define_method(rb_cRuggedRepo, "reset", rb_git_repo_reset, 2);
2647
+ rb_define_method(rb_cRuggedRepo, "reset_path", rb_git_repo_reset_path, -1);
2648
+
2649
+ rb_define_method(rb_cRuggedRepo, "namespace=", rb_git_repo_set_namespace, 1);
2650
+ rb_define_method(rb_cRuggedRepo, "namespace", rb_git_repo_get_namespace, 0);
2651
+
2652
+ rb_define_method(rb_cRuggedRepo, "ahead_behind", rb_git_repo_ahead_behind, 2);
2653
+
2654
+ rb_define_method(rb_cRuggedRepo, "default_signature", rb_git_repo_default_signature, 0);
2655
+
2656
+ rb_define_method(rb_cRuggedRepo, "checkout_tree", rb_git_checkout_tree, -1);
2657
+ rb_define_method(rb_cRuggedRepo, "checkout_index", rb_git_checkout_index, -1);
2658
+ rb_define_method(rb_cRuggedRepo, "checkout_head", rb_git_checkout_head, -1);
2659
+
2660
+ rb_define_method(rb_cRuggedRepo, "cherrypick", rb_git_repo_cherrypick, -1);
2661
+ rb_define_method(rb_cRuggedRepo, "cherrypick_commit", rb_git_repo_cherrypick_commit, -1);
2662
+ rb_define_method(rb_cRuggedRepo, "fetch_attributes", rb_git_repo_attributes, -1);
2663
+
2664
+ rb_cRuggedOdbObject = rb_define_class_under(rb_mRugged, "OdbObject", rb_cObject);
2665
+ rb_define_method(rb_cRuggedOdbObject, "data", rb_git_odbobj_data, 0);
2666
+ rb_define_method(rb_cRuggedOdbObject, "len", rb_git_odbobj_size, 0);
2667
+ rb_define_method(rb_cRuggedOdbObject, "type", rb_git_odbobj_type, 0);
2668
+ rb_define_method(rb_cRuggedOdbObject, "oid", rb_git_odbobj_oid, 0);
2669
+ }