1 /* 2 * libgit2 "general" example - shows basic libgit2 concepts 3 * 4 * Written by the libgit2 contributors 5 * 6 * To the extent possible under law, the author(s) have dedicated all copyright 7 * and related and neighboring rights to this software to the public domain 8 * worldwide. This software is distributed without any warranty. 9 * 10 * You should have received a copy of the CC0 Public Domain Dedication along 11 * with this software. If not, see 12 * <https://creativecommons.org/publicdomain/zero/1.0/>. 13 */ 14 /** 15 * [**libgit2**][lg] is a portable, pure C implementation of the Git core 16 * methods provided as a re-entrant linkable library with a solid API, 17 * allowing you to write native speed custom Git applications in any 18 * language which supports C bindings. 19 * 20 * This file is an example of using that API in a real, compilable C file. 21 * As the API is updated, this file will be updated to demonstrate the new 22 * functionality. 23 * 24 * If you're trying to write something in C using [libgit2][lg], you should 25 * also check out the generated [API documentation][ap]. We try to link to 26 * the relevant sections of the API docs in each section in this file. 27 * 28 * **libgit2** (for the most part) only implements the core plumbing 29 * functions, not really the higher level porcelain stuff. For a primer on 30 * Git Internals that you will need to know to work with Git at this level, 31 * check out [Chapter 10][pg] of the Pro Git book. 32 * 33 * [lg]: http://libgit2.github.com 34 * [ap]: http://libgit2.github.com/libgit2 35 * [pg]: https://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-Porcelain 36 * 37 * License: $(LINK2 https://creativecommons.org/publicdomain/zero/1.0/, CC0 1.0 Universal) 38 */ 39 module libgit2.example.general; 40 41 42 /* 43 * ### Includes 44 * 45 * Including the `git2.h` header will include all the other libgit2 headers 46 * that you need. It should be the only thing you need to include in order 47 * to compile properly and get all the libgit2 API. 48 */ 49 private static import core.stdc.config; 50 private static import core.stdc.stdio; 51 private static import core.stdc.stdlib; 52 private static import core.stdc.time; 53 private static import libgit2.blob; 54 private static import libgit2.commit; 55 private static import libgit2.common; 56 private static import libgit2.config; 57 private static import libgit2.deprecated_; 58 private static import libgit2.errors; 59 private static import libgit2.global; 60 private static import libgit2.index; 61 private static import libgit2.object; 62 private static import libgit2.odb; 63 private static import libgit2.oid; 64 private static import libgit2.refs; 65 private static import libgit2.repository; 66 private static import libgit2.revwalk; 67 private static import libgit2.signature; 68 private static import libgit2.strarray; 69 private static import libgit2.tag; 70 private static import libgit2.tree; 71 private static import libgit2.types; 72 73 /** 74 * Almost all libgit2 functions return 0 on success or negative on error. 75 * This is not production quality error checking, but should be sufficient 76 * as an example. 77 */ 78 nothrow @nogc 79 private void check_error(int error_code, const (char)* action) 80 81 in 82 { 83 } 84 85 do 86 { 87 const (libgit2.errors.git_error)* error = libgit2.errors.git_error_last(); 88 89 if (!error_code) { 90 return; 91 } 92 93 core.stdc.stdio.printf("Error %d %s - %s\n", error_code, action, ((error) && (error.message)) ? (error.message) : ("???")); 94 95 core.stdc.stdlib.exit(1); 96 } 97 98 extern (C) 99 nothrow @nogc 100 public int lg2_general(libgit2.types.git_repository* repo, int argc, char** argv) 101 102 in 103 { 104 } 105 106 do 107 { 108 /** 109 * Initialize the library, this will set up any global state which libgit2 needs 110 * including threading and crypto 111 */ 112 libgit2.global.git_libgit2_init(); 113 114 /** 115 * ### Opening the Repository 116 * 117 * There are a couple of methods for opening a repository, this being the 118 * simplest. There are also [methods][me] for specifying the index file 119 * and work tree locations, here we assume they are in the normal places. 120 * 121 * (Try running this program against tests/resources/testrepo.git.) 122 * 123 * [me]: http://libgit2.github.com/libgit2/#HEAD/group/repository 124 */ 125 const char* repo_path = (argc > 1) ? (argv[1]) : ("/opt/libgit2-test/.git"); 126 127 int error = libgit2.repository.git_repository_open(&repo, repo_path); 128 .check_error(error, "opening repository"); 129 130 libgit2.oid.git_oid oid; 131 .oid_parsing(&oid); 132 .object_database(repo, &oid); 133 .commit_writing(repo); 134 .commit_parsing(repo); 135 .tag_parsing(repo); 136 .tree_parsing(repo); 137 .blob_parsing(repo); 138 .revwalking(repo); 139 .index_walking(repo); 140 .reference_listing(repo); 141 .config_files(repo_path, repo); 142 143 /** 144 * Finally, when you're done with the repository, you can free it as well. 145 */ 146 libgit2.repository.git_repository_free(repo); 147 148 return 0; 149 } 150 151 /** 152 * ### SHA-1 Value Conversions 153 */ 154 nothrow @nogc 155 private void oid_parsing(libgit2.oid.git_oid* oid) 156 157 in 158 { 159 } 160 161 do 162 { 163 core.stdc.stdio.printf("*Hex to Raw*\n"); 164 165 const char* hex = "4a202b346bb0fb0db7eff3cffeb3c70babbd2045"; 166 167 /** 168 * For our first example, we will convert a 40 character hex value to the 169 * 20 byte raw SHA1 value. 170 * 171 * The `libgit2.oid.git_oid` is the structure that keeps the SHA value. We will use 172 * this throughout the example for storing the value of the current SHA 173 * key we're working with. 174 */ 175 version (GIT_EXPERIMENTAL_SHA256) { 176 libgit2.oid.git_oid_fromstr(oid, hex, libgit2.oid.git_oid_t.GIT_OID_SHA1); 177 } else { 178 libgit2.oid.git_oid_fromstr(oid, hex); 179 } 180 181 /* 182 * Once we've converted the string into the oid value, we can get the raw 183 * value of the SHA by accessing `oid.id` 184 * 185 * Next we will convert the 20 byte raw SHA1 value to a human readable 40 186 * char hex value. 187 */ 188 core.stdc.stdio.printf("\n*Raw to Hex*\n"); 189 char[libgit2.oid.GIT_OID_SHA1_HEXSIZE + 1] out_ = '\0'; 190 191 /** 192 * If you have a oid, you can easily get the hex value of the SHA as well. 193 */ 194 libgit2.oid.git_oid_fmt(&(out_[0]), oid); 195 196 core.stdc.stdio.printf("SHA hex string: %s\n", &(out_[0])); 197 } 198 199 /** 200 * ### Working with the Object Database 201 * 202 * **libgit2** provides [direct access][odb] to the object database. The 203 * object database is where the actual objects are stored in Git. For 204 * working with raw objects, we'll need to get this structure from the 205 * repository. 206 * 207 * [odb]: http://libgit2.github.com/libgit2/#HEAD/group/odb 208 */ 209 nothrow @nogc 210 private void object_database(libgit2.types.git_repository* repo, libgit2.oid.git_oid* oid) 211 212 in 213 { 214 } 215 216 do 217 { 218 libgit2.types.git_odb* odb; 219 libgit2.repository.git_repository_odb(&odb, repo); 220 221 /** 222 * #### Raw Object Reading 223 */ 224 225 core.stdc.stdio.printf("\n*Raw Object Read*\n"); 226 227 /** 228 * We can read raw objects directly from the object database if we have 229 * the oid (SHA) of the object. This allows us to access objects without 230 * knowing their type and inspect the raw bytes unparsed. 231 */ 232 libgit2.types.git_odb_object* obj; 233 int error = libgit2.odb.git_odb_read(&obj, odb, oid); 234 .check_error(error, "finding object in repository"); 235 236 /** 237 * A raw object only has three properties - the type (commit, blob, tree 238 * or tag), the size of the raw data and the raw, unparsed data itself. 239 * For a commit or tag, that raw data is human readable plain ASCII 240 * text. For a blob it is just file contents, so it could be text or 241 * binary data. For a tree it is a special binary format, so it's unlikely 242 * to be hugely helpful as a raw object. 243 */ 244 const (ubyte)* data = cast(const (ubyte)*)(libgit2.odb.git_odb_object_data(obj)); 245 libgit2.types.git_object_t otype = libgit2.odb.git_odb_object_type(obj); 246 247 /** 248 * We provide methods to convert from the object type which is an enum, to 249 * a string representation of that value (and vice-versa). 250 */ 251 const (char)* str_type = libgit2.object.git_object_type2string(otype); 252 core.stdc.stdio.printf("object length and type: %d, %s\nobject data: %s\n", cast(int)(libgit2.odb.git_odb_object_size(obj)), str_type, data); 253 254 /** 255 * For proper memory management, close the object when you are done with 256 * it or it will leak memory. 257 */ 258 libgit2.odb.git_odb_object_free(obj); 259 260 /** 261 * #### Raw Object Writing 262 */ 263 264 core.stdc.stdio.printf("\n*Raw Object Write*\n"); 265 266 /** 267 * You can also write raw object data to Git. This is pretty cool because 268 * it gives you direct access to the key/value properties of Git. Here 269 * we'll write a new blob object that just contains a simple string. 270 * Notice that we have to specify the object type as the `libgit2.deprecated_.git_object_t` enum. 271 */ 272 enum test_data = "test data"; 273 libgit2.odb.git_odb_write(oid, odb, test_data.ptr, test_data.length, libgit2.types.git_object_t.GIT_OBJECT_BLOB); 274 275 /** 276 * Now that we've written the object, we can check out what SHA1 was 277 * generated when the object was written to our database. 278 */ 279 char[libgit2.oid.GIT_OID_SHA1_HEXSIZE + 1] oid_hex = '\0'; 280 libgit2.oid.git_oid_fmt(&(oid_hex[0]), oid); 281 core.stdc.stdio.printf("Written Object: %s\n", &(oid_hex[0])); 282 283 /** 284 * Free the object database after usage. 285 */ 286 libgit2.odb.git_odb_free(odb); 287 } 288 289 /** 290 * #### Writing Commits 291 * 292 * libgit2 provides a couple of methods to create commit objects easily as 293 * well. There are four different create signatures, we'll just show one 294 * of them here. You can read about the other ones in the [commit API 295 * docs][cd]. 296 * 297 * [cd]: http://libgit2.github.com/libgit2/#HEAD/group/commit 298 */ 299 nothrow @nogc 300 private void commit_writing(libgit2.types.git_repository* repo) 301 302 in 303 { 304 } 305 306 do 307 { 308 core.stdc.stdio.printf("\n*Commit Writing*\n"); 309 310 /** 311 * Creating signatures for an authoring identity and time is simple. You 312 * will need to do this to specify who created a commit and when. Default 313 * values for the name and email should be found in the `user.name` and 314 * `user.email` configuration options. See the `config` section of this 315 * example file to see how to access config values. 316 */ 317 libgit2.types.git_signature* author; 318 libgit2.signature.git_signature_new(&author, "Scott Chacon", "schacon@gmail.com", 123456789, 60); 319 libgit2.types.git_signature* committer; 320 libgit2.signature.git_signature_new(&committer, "Scott A Chacon", "scott@github.com", 987654321, 90); 321 322 libgit2.oid.git_oid tree_id; 323 libgit2.oid.git_oid parent_id; 324 325 /** 326 * Commit objects need a tree to point to and optionally one or more 327 * parents. Here we're creating oid objects to create the commit with, 328 * but you can also use 329 */ 330 version (GIT_EXPERIMENTAL_SHA256) { 331 libgit2.oid.git_oid_fromstr(&tree_id, "f60079018b664e4e79329a7ef9559c8d9e0378d1", libgit2.oid.git_oid_t.GIT_OID_SHA1); 332 libgit2.oid.git_oid_fromstr(&parent_id, "5b5b025afb0b4c913b4c338a42934a3863bf3644", libgit2.oid.git_oid_t.GIT_OID_SHA1); 333 } else { 334 libgit2.oid.git_oid_fromstr(&tree_id, "f60079018b664e4e79329a7ef9559c8d9e0378d1"); 335 libgit2.oid.git_oid_fromstr(&parent_id, "5b5b025afb0b4c913b4c338a42934a3863bf3644"); 336 } 337 338 libgit2.types.git_tree* tree; 339 libgit2.tree.git_tree_lookup(&tree, repo, &tree_id); 340 libgit2.types.git_commit* parent; 341 libgit2.commit.git_commit_lookup(&parent, repo, &parent_id); 342 343 /** 344 * Here we actually create the commit object with a single call with all 345 * the values we need to create the commit. The SHA key is written to the 346 * `commit_id` variable here. 347 */ 348 libgit2.oid.git_oid commit_id; 349 libgit2.commit.git_commit_create_v(&commit_id, /* out id */ 350 repo, null, /* do not update the HEAD */ 351 author, committer, null, /* use default message encoding */ 352 "example commit", tree, 1, parent); 353 354 /** 355 * Now we can take a look at the commit SHA we've generated. 356 */ 357 char[libgit2.oid.GIT_OID_SHA1_HEXSIZE + 1] oid_hex = '\0'; 358 libgit2.oid.git_oid_fmt(&(oid_hex[0]), &commit_id); 359 core.stdc.stdio.printf("New Commit: %s\n", &(oid_hex[0])); 360 361 /** 362 * Free all objects used in the meanwhile. 363 */ 364 libgit2.tree.git_tree_free(tree); 365 libgit2.commit.git_commit_free(parent); 366 libgit2.signature.git_signature_free(author); 367 libgit2.signature.git_signature_free(committer); 368 } 369 370 /** 371 * ### Object Parsing 372 * 373 * libgit2 has methods to parse every object type in Git so you don't have 374 * to work directly with the raw data. This is much faster and simpler 375 * than trying to deal with the raw data yourself. 376 */ 377 378 /** 379 * #### Commit Parsing 380 * 381 * [Parsing commit objects][pco] is simple and gives you access to all the 382 * data in the commit - the author (name, email, datetime), committer 383 * (same), tree, message, encoding and parent(s). 384 * 385 * [pco]: http://libgit2.github.com/libgit2/#HEAD/group/commit 386 */ 387 nothrow @nogc 388 private void commit_parsing(libgit2.types.git_repository* repo) 389 390 in 391 { 392 } 393 394 do 395 { 396 core.stdc.stdio.printf("\n*Commit Parsing*\n"); 397 398 libgit2.oid.git_oid oid; 399 400 version (GIT_EXPERIMENTAL_SHA256) { 401 libgit2.oid.git_oid_fromstr(&oid, "8496071c1b46c854b31185ea97743be6a8774479", libgit2.oid.git_oid_t.GIT_OID_SHA1); 402 } else { 403 libgit2.oid.git_oid_fromstr(&oid, "8496071c1b46c854b31185ea97743be6a8774479"); 404 } 405 406 libgit2.types.git_commit* commit; 407 int error = libgit2.commit.git_commit_lookup(&commit, repo, &oid); 408 .check_error(error, "looking up commit"); 409 410 /** 411 * Each of the properties of the commit object are accessible via methods, 412 * including commonly needed variations, such as `libgit2.commit.git_commit_time` which 413 * returns the author time and `libgit2.commit.git_commit_message` which gives you the 414 * commit message (as a null-terminated string). 415 */ 416 const (char)* message = libgit2.commit.git_commit_message(commit); 417 const (libgit2.types.git_signature)* author = libgit2.commit.git_commit_author(commit); 418 const (libgit2.types.git_signature)* cmtter = libgit2.commit.git_commit_committer(commit); 419 420 core.stdc.time.time_t time = cast(core.stdc.time.time_t)(libgit2.commit.git_commit_time(commit)); 421 422 /** 423 * The author and committer methods return [libgit2.types.git_signature] structures, 424 * which give you name, email and `when`, which is a `libgit2.types.git_time` structure, 425 * giving you a timestamp and timezone offset. 426 */ 427 core.stdc.stdio.printf("Author: %s (%s)\nCommitter: %s (%s)\nDate: %s\nMessage: %s\n", author.name, author.email, cmtter.name, cmtter.email, core.stdc.time.ctime(&time), message); 428 429 /** 430 * Commits can have zero or more parents. The first (root) commit will 431 * have no parents, most commits will have one (i.e. the commit it was 432 * based on) and merge commits will have two or more. Commits can 433 * technically have any number, though it's rare to have more than two. 434 */ 435 uint parents = libgit2.commit.git_commit_parentcount(commit); 436 437 char[libgit2.oid.GIT_OID_SHA1_HEXSIZE + 1] oid_hex; 438 libgit2.types.git_commit* parent; 439 440 for (uint p = 0; p < parents; p++) { 441 oid_hex[] = 0; 442 443 libgit2.commit.git_commit_parent(&parent, commit, p); 444 libgit2.oid.git_oid_fmt(&(oid_hex[0]), libgit2.commit.git_commit_id(parent)); 445 core.stdc.stdio.printf("Parent: %s\n", &(oid_hex[0])); 446 libgit2.commit.git_commit_free(parent); 447 } 448 449 libgit2.commit.git_commit_free(commit); 450 } 451 452 /** 453 * #### Tag Parsing 454 * 455 * You can parse and create tags with the [tag management API][core.stdc.time.tm], which 456 * functions very similarly to the commit lookup, parsing and creation 457 * methods, since the objects themselves are very similar. 458 * 459 * [core.stdc.time.tm]: http://libgit2.github.com/libgit2/#HEAD/group/tag 460 */ 461 nothrow @nogc 462 private void tag_parsing(libgit2.types.git_repository* repo) 463 464 in 465 { 466 } 467 468 do 469 { 470 core.stdc.stdio.printf("\n*Tag Parsing*\n"); 471 472 libgit2.oid.git_oid oid; 473 474 /** 475 * We create an oid for the tag object if we know the SHA and look it up 476 * the same way that we would a commit (or any other object). 477 */ 478 version (GIT_EXPERIMENTAL_SHA256) { 479 libgit2.oid.git_oid_fromstr(&oid, "b25fa35b38051e4ae45d4222e795f9df2e43f1d1", libgit2.oid.git_oid_t.GIT_OID_SHA1); 480 } else { 481 libgit2.oid.git_oid_fromstr(&oid, "b25fa35b38051e4ae45d4222e795f9df2e43f1d1"); 482 } 483 484 libgit2.types.git_tag* tag; 485 int error = libgit2.tag.git_tag_lookup(&tag, repo, &oid); 486 .check_error(error, "looking up tag"); 487 488 /** 489 * Now that we have the tag object, we can extract the information it 490 * generally contains: the target (usually a commit object), the type of 491 * the target object (usually 'commit'), the name ('v1.0'), the tagger (a 492 * git_signature - name, email, timestamp), and the tag message. 493 */ 494 libgit2.types.git_commit* commit; 495 libgit2.tag.git_tag_target(cast(libgit2.types.git_object**)(&commit), tag); 496 497 /* "test" */ 498 const (char)* name = libgit2.tag.git_tag_name(tag); 499 500 /* libgit2.types.git_object_t.GIT_OBJECT_COMMIT (object_t enum) */ 501 libgit2.types.git_object_t type = libgit2.tag.git_tag_target_type(tag); 502 503 /* "tag message\n" */ 504 const (char)* message = libgit2.tag.git_tag_message(tag); 505 506 core.stdc.stdio.printf("Tag Name: %s\nTag Type: %s\nTag Message: %s\n", name, libgit2.object.git_object_type2string(type), message); 507 508 /** 509 * Free both the commit and tag after usage. 510 */ 511 libgit2.commit.git_commit_free(commit); 512 libgit2.tag.git_tag_free(tag); 513 } 514 515 /** 516 * #### Tree Parsing 517 * 518 * [Tree parsing][tp] is a bit different than the other objects, in that 519 * we have a subtype which is the tree entry. This is not an actual 520 * object type in Git, but a useful structure for parsing and traversing 521 * tree entries. 522 * 523 * [tp]: http://libgit2.github.com/libgit2/#HEAD/group/tree 524 */ 525 nothrow @nogc 526 private void tree_parsing(libgit2.types.git_repository* repo) 527 528 in 529 { 530 } 531 532 do 533 { 534 core.stdc.stdio.printf("\n*Tree Parsing*\n"); 535 536 libgit2.oid.git_oid oid; 537 538 /** 539 * Create the oid and lookup the tree object just like the other objects. 540 */ 541 version (GIT_EXPERIMENTAL_SHA256) { 542 libgit2.oid.git_oid_fromstr(&oid, "f60079018b664e4e79329a7ef9559c8d9e0378d1", libgit2.oid.git_oid_t.GIT_OID_SHA1); 543 } else { 544 libgit2.oid.git_oid_fromstr(&oid, "f60079018b664e4e79329a7ef9559c8d9e0378d1"); 545 } 546 547 libgit2.types.git_tree* tree; 548 libgit2.tree.git_tree_lookup(&tree, repo, &oid); 549 550 /** 551 * Getting the count of entries in the tree so you can iterate over them 552 * if you want to. 553 */ 554 /* 2 */ 555 size_t cnt = libgit2.tree.git_tree_entrycount(tree); 556 557 core.stdc.stdio.printf("tree entries: %d\n", cast(int)(cnt)); 558 559 const (libgit2.types.git_tree_entry)* entry = libgit2.tree.git_tree_entry_byindex(tree, 0); 560 561 /* "README" */ 562 core.stdc.stdio.printf("Entry name: %s\n", libgit2.tree.git_tree_entry_name(entry)); 563 564 /** 565 * You can also access tree entries by name if you know the name of the 566 * entry you're looking for. 567 */ 568 entry = libgit2.tree.git_tree_entry_byname(tree, "README"); 569 570 /* "README" */ 571 libgit2.tree.git_tree_entry_name(entry); 572 573 /** 574 * Once you have the entry object, you can access the content or subtree 575 * (or commit, in the case of submodules) that it points to. You can also 576 * get the mode if you want. 577 */ 578 libgit2.types.git_object* obj; 579 580 /* blob */ 581 libgit2.tree.git_tree_entry_to_object(&obj, repo, entry); 582 583 /** 584 * Remember to close the looked-up object and tree once you are done using it 585 */ 586 libgit2.object.git_object_free(obj); 587 libgit2.tree.git_tree_free(tree); 588 } 589 590 /** 591 * #### Blob Parsing 592 * 593 * The last object type is the simplest and requires the least parsing 594 * help. Blobs are just file contents and can contain anything, there is 595 * no structure to it. The main advantage to using the [simple blob 596 * api][ba] is that when you're creating blobs you don't have to calculate 597 * the size of the content. There is also a helper for reading a file 598 * from disk and writing it to the db and getting the oid back so you 599 * don't have to do all those steps yourself. 600 * 601 * [ba]: http://libgit2.github.com/libgit2/#HEAD/group/blob 602 */ 603 nothrow @nogc 604 private void blob_parsing(libgit2.types.git_repository* repo) 605 606 in 607 { 608 } 609 610 do 611 { 612 core.stdc.stdio.printf("\n*Blob Parsing*\n"); 613 614 libgit2.oid.git_oid oid; 615 616 version (GIT_EXPERIMENTAL_SHA256) { 617 libgit2.oid.git_oid_fromstr(&oid, "1385f264afb75a56a5bec74243be9b367ba4ca08", libgit2.oid.git_oid_t.GIT_OID_SHA1); 618 } else { 619 libgit2.oid.git_oid_fromstr(&oid, "1385f264afb75a56a5bec74243be9b367ba4ca08"); 620 } 621 622 libgit2.types.git_blob* blob; 623 libgit2.blob.git_blob_lookup(&blob, repo, &oid); 624 625 /** 626 * You can access a buffer with the raw contents of the blob directly. 627 * Note that this buffer may not be contain ASCII data for certain blobs 628 * (e.g. binary files): do not consider the buffer a null-terminated 629 * string, and use the `libgit2.blob.git_blob_rawsize` attribute to find out its exact 630 * size in bytes 631 */ 632 /* 8 */ 633 core.stdc.stdio.printf("Blob Size: %ld\n", cast(core.stdc.config.c_long)(libgit2.blob.git_blob_rawsize(blob))); 634 635 /* "content" */ 636 libgit2.blob.git_blob_rawcontent(blob); 637 638 /** 639 * Free the blob after usage. 640 */ 641 libgit2.blob.git_blob_free(blob); 642 } 643 644 /** 645 * ### Revwalking 646 * 647 * The libgit2 [revision walking api][rw] provides methods to traverse the 648 * directed graph created by the parent pointers of the commit objects. 649 * Since all commits point back to the commit that came directly before 650 * them, you can walk this parentage as a graph and find all the commits 651 * that were ancestors of (reachable from) a given starting point. This 652 * can allow you to create `git log` type functionality. 653 * 654 * [rw]: http://libgit2.github.com/libgit2/#HEAD/group/revwalk 655 */ 656 nothrow @nogc 657 private void revwalking(libgit2.types.git_repository* repo) 658 659 in 660 { 661 } 662 663 do 664 { 665 core.stdc.stdio.printf("\n*Revwalking*\n"); 666 667 libgit2.oid.git_oid oid; 668 669 version (GIT_EXPERIMENTAL_SHA256) { 670 libgit2.oid.git_oid_fromstr(&oid, "5b5b025afb0b4c913b4c338a42934a3863bf3644", libgit2.oid.git_oid_t.GIT_OID_SHA1); 671 } else { 672 libgit2.oid.git_oid_fromstr(&oid, "5b5b025afb0b4c913b4c338a42934a3863bf3644"); 673 } 674 675 /** 676 * To use the revwalker, create a new walker, tell it how you want to sort 677 * the output and then push one or more starting points onto the walker. 678 * If you want to emulate the output of `git log` you would push the SHA 679 * of the commit that HEAD points to into the walker and then start 680 * traversing them. You can also 'hide' commits that you want to stop at 681 * or not see any of their ancestors. So if you want to emulate `git log 682 * branch1..branch2`, you would push the oid of `branch2` and hide the oid 683 * of `branch1`. 684 */ 685 libgit2.types.git_revwalk* walk; 686 libgit2.revwalk.git_revwalk_new(&walk, repo); 687 libgit2.revwalk.git_revwalk_sorting(walk, libgit2.revwalk.git_sort_t.GIT_SORT_TOPOLOGICAL | libgit2.revwalk.git_sort_t.GIT_SORT_REVERSE); 688 libgit2.revwalk.git_revwalk_push(walk, &oid); 689 690 /** 691 * Now that we have the starting point pushed onto the walker, we start 692 * asking for ancestors. It will return them in the sorting order we asked 693 * for as commit oids. We can then lookup and parse the committed pointed 694 * at by the returned OID; note that this operation is specially fast 695 * since the raw contents of the commit object will be cached in memory 696 */ 697 libgit2.types.git_commit* wcommit; 698 699 while ((libgit2.revwalk.git_revwalk_next(&oid, walk)) == 0) { 700 int error = libgit2.commit.git_commit_lookup(&wcommit, repo, &oid); 701 .check_error(error, "looking up commit during revwalk"); 702 703 const (char)* cmsg = libgit2.commit.git_commit_message(wcommit); 704 const (libgit2.types.git_signature)* cauth = libgit2.commit.git_commit_author(wcommit); 705 core.stdc.stdio.printf("%s (%s)\n", cmsg, cauth.email); 706 707 libgit2.commit.git_commit_free(wcommit); 708 } 709 710 /** 711 * Like the other objects, be sure to free the revwalker when you're done 712 * to prevent memory leaks. Also, make sure that the repository being 713 * walked it not deallocated while the walk is in progress, or it will 714 * result in undefined behavior 715 */ 716 libgit2.revwalk.git_revwalk_free(walk); 717 } 718 719 /** 720 * ### Index File Manipulation * 721 * The [index file API][gi] allows you to read, traverse, update and write 722 * the Git index file (sometimes thought of as the staging area). 723 * 724 * [gi]: http://libgit2.github.com/libgit2/#HEAD/group/index 725 */ 726 nothrow @nogc 727 private void index_walking(libgit2.types.git_repository* repo) 728 729 in 730 { 731 } 732 733 do 734 { 735 core.stdc.stdio.printf("\n*Index Walking*\n"); 736 737 /** 738 * You can either open the index from the standard location in an open 739 * repository, as we're doing here, or you can open and manipulate any 740 * index file with `git_index_open_bare()`. The index for the repository 741 * will be located and loaded from disk. 742 */ 743 libgit2.types.git_index* index; 744 libgit2.repository.git_repository_index(&index, repo); 745 746 /** 747 * For each entry in the index, you can get a bunch of information 748 * including the SHA (oid), path and mode which map to the tree objects 749 * that are written out. It also has filesystem properties to help 750 * determine what to inspect for changes (ctime, mtime, dev, ino, uid, 751 * gid, file_size and flags) All these properties are exported publicly in 752 * the `libgit2.index.git_index_entry` struct 753 */ 754 size_t ecount = libgit2.index.git_index_entrycount(index); 755 756 for (size_t i = 0; i < ecount; ++i) { 757 const (libgit2.index.git_index_entry)* e = libgit2.index.git_index_get_byindex(index, i); 758 759 core.stdc.stdio.printf("path: %s\n", e.path); 760 core.stdc.stdio.printf("mtime: %d\n", cast(int)(e.mtime.seconds)); 761 core.stdc.stdio.printf("fs: %d\n", cast(int)(e.file_size)); 762 } 763 764 libgit2.index.git_index_free(index); 765 } 766 767 /** 768 * ### References 769 * 770 * The [reference API][ref] allows you to list, resolve, create and update 771 * references such as branches, tags and remote references (everything in 772 * the .git/refs directory). 773 * 774 * [ref]: http://libgit2.github.com/libgit2/#HEAD/group/reference 775 */ 776 nothrow @nogc 777 private void reference_listing(libgit2.types.git_repository* repo) 778 779 in 780 { 781 } 782 783 do 784 { 785 core.stdc.stdio.printf("\n*Reference Listing*\n"); 786 787 /** 788 * Here we will implement something like `git for-each-ref` simply listing 789 * out all available references and the object SHA they resolve to. 790 * 791 * Now that we have the list of reference names, we can lookup each ref 792 * one at a time and resolve them to the SHA, then print both values out. 793 */ 794 795 libgit2.strarray.git_strarray ref_list; 796 libgit2.refs.git_reference_list(&ref_list, repo); 797 798 libgit2.types.git_reference* ref_; 799 800 for (uint i = 0; i < ref_list.count; ++i) { 801 char[libgit2.oid.GIT_OID_SHA1_HEXSIZE + 1] oid_hex = libgit2.oid.GIT_OID_SHA1_HEXZERO; 802 const (char)* refname = ref_list.strings[i]; 803 libgit2.refs.git_reference_lookup(&ref_, repo, refname); 804 805 switch (libgit2.refs.git_reference_type(ref_)) { 806 case libgit2.types.git_reference_t.GIT_REFERENCE_DIRECT: 807 libgit2.oid.git_oid_fmt(&(oid_hex[0]), libgit2.refs.git_reference_target(ref_)); 808 core.stdc.stdio.printf("%s [%s]\n", refname, &(oid_hex[0])); 809 810 break; 811 812 case libgit2.types.git_reference_t.GIT_REFERENCE_SYMBOLIC: 813 core.stdc.stdio.printf("%s => %s\n", refname, libgit2.refs.git_reference_symbolic_target(ref_)); 814 815 break; 816 817 default: 818 core.stdc.stdio.fprintf(core.stdc.stdio.stderr, "Unexpected reference type\n"); 819 core.stdc.stdlib.exit(1); 820 } 821 822 libgit2.refs.git_reference_free(ref_); 823 } 824 825 libgit2.strarray.git_strarray_dispose(&ref_list); 826 } 827 828 /** 829 * ### Config Files 830 * 831 * The [config API][config] allows you to list and update config values 832 * in any of the accessible config file locations (system, global, local). 833 * 834 * [config]: http://libgit2.github.com/libgit2/#HEAD/group/config 835 */ 836 nothrow @nogc 837 private void config_files(const (char)* repo_path, libgit2.types.git_repository* repo) 838 839 in 840 { 841 } 842 843 do 844 { 845 core.stdc.stdio.printf("\n*Config Listing*\n"); 846 847 /** 848 * Open a config object so we can read global values from it. 849 */ 850 char[256] config_path; 851 core.stdc.stdio.sprintf(&(config_path[0]), "%s/config", repo_path); 852 libgit2.types.git_config* cfg; 853 .check_error(libgit2.config.git_config_open_ondisk(&cfg, &(config_path[0])), "opening config"); 854 855 int autocorrect; 856 857 if (libgit2.config.git_config_get_int32(&autocorrect, cfg, "help.autocorrect") == 0) { 858 core.stdc.stdio.printf("Autocorrect: %d\n", autocorrect); 859 } 860 861 libgit2.types.git_config* snap_cfg; 862 .check_error(libgit2.repository.git_repository_config_snapshot(&snap_cfg, repo), "config snapshot"); 863 const (char)* email; 864 libgit2.config.git_config_get_string(&email, snap_cfg, "user.email"); 865 core.stdc.stdio.printf("Email: %s\n", email); 866 867 int error_code = libgit2.config.git_config_get_int32(&autocorrect, cfg, "help.autocorrect"); 868 869 switch (error_code) { 870 case 0: 871 core.stdc.stdio.printf("Autocorrect: %d\n", autocorrect); 872 873 break; 874 875 case libgit2.errors.git_error_code.GIT_ENOTFOUND: 876 core.stdc.stdio.printf("Autocorrect: Undefined\n"); 877 878 break; 879 880 default: 881 .check_error(error_code, "get_int32 failed"); 882 883 break; 884 } 885 886 libgit2.config.git_config_free(cfg); 887 888 .check_error(libgit2.repository.git_repository_config_snapshot(&snap_cfg, repo), "config snapshot"); 889 error_code = libgit2.config.git_config_get_string(&email, snap_cfg, "user.email"); 890 891 switch (error_code) { 892 case 0: 893 core.stdc.stdio.printf("Email: %s\n", email); 894 895 break; 896 897 case libgit2.errors.git_error_code.GIT_ENOTFOUND: 898 core.stdc.stdio.printf("Email: Undefined\n"); 899 900 break; 901 902 default: 903 .check_error(error_code, "get_string failed"); 904 905 break; 906 } 907 908 libgit2.config.git_config_free(snap_cfg); 909 }