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