diff options
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/.gitignore | 3 | ||||
| -rw-r--r-- | examples/Makefile | 4 | ||||
| -rw-r--r-- | examples/README.md | 11 | ||||
| -rw-r--r-- | examples/diff.c | 71 | ||||
| -rw-r--r-- | examples/general.c | 395 | ||||
| -rw-r--r-- | examples/network/Makefile | 11 | ||||
| -rw-r--r-- | examples/network/clone.c | 122 | ||||
| -rw-r--r-- | examples/network/common.h | 10 | ||||
| -rw-r--r-- | examples/network/fetch.c | 150 | ||||
| -rw-r--r-- | examples/network/git2.c | 6 | ||||
| -rw-r--r-- | examples/network/index-pack.c | 162 | ||||
| -rw-r--r-- | examples/network/ls-remote.c | 19 | ||||
| -rw-r--r-- | examples/rev-list.c | 120 | ||||
| -rw-r--r-- | examples/showindex.c | 96 | ||||
| -rwxr-xr-x | examples/test/test-rev-list.sh | 95 |
15 files changed, 870 insertions, 405 deletions
diff --git a/examples/.gitignore b/examples/.gitignore index 4c34e4ab5..e8e0820a5 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -1,2 +1,5 @@ general showindex +diff +rev-list +*.dSYM diff --git a/examples/Makefile b/examples/Makefile index fe99c75cb..2c18731fd 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -1,9 +1,9 @@ .PHONY: all CC = gcc -CFLAGS = -g -I../include -I../src +CFLAGS = -g -I../include -I../src -Wall -Wextra -Wmissing-prototypes -Wno-missing-field-initializers LFLAGS = -L../build -lgit2 -lz -APPS = general showindex diff +APPS = general showindex diff rev-list all: $(APPS) diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 000000000..f2b6d7d23 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,11 @@ +libgit2 examples +================ + +These examples are meant as thin, easy-to-read snippets for Docurium +(https://github.com/github/docurium) rather than full-blown +implementations of Git commands. They are not vetted as carefully +for bugs, error handling, or cross-platform compatibility as the +rest of the code in libgit2, so copy with some caution. + +For HTML versions, check "Examples" at http://libgit2.github.com/libgit2 + diff --git a/examples/diff.c b/examples/diff.c index 1b4ab549b..2ef405665 100644 --- a/examples/diff.c +++ b/examples/diff.c @@ -3,7 +3,7 @@ #include <stdlib.h> #include <string.h> -void check(int error, const char *message) +static void check(int error, const char *message) { if (error) { fprintf(stderr, "%s (%d)\n", message, error); @@ -11,32 +11,14 @@ void check(int error, const char *message) } } -int resolve_to_tree(git_repository *repo, const char *identifier, git_tree **tree) +static int resolve_to_tree( + git_repository *repo, const char *identifier, git_tree **tree) { int err = 0; - size_t len = strlen(identifier); - git_oid oid; git_object *obj = NULL; - /* try to resolve as OID */ - if (git_oid_fromstrn(&oid, identifier, len) == 0) - git_object_lookup_prefix(&obj, repo, &oid, len, GIT_OBJ_ANY); - - /* try to resolve as reference */ - if (obj == NULL) { - git_reference *ref, *resolved; - if (git_reference_lookup(&ref, repo, identifier) == 0) { - git_reference_resolve(&resolved, ref); - git_reference_free(ref); - if (resolved) { - git_object_lookup(&obj, repo, git_reference_oid(resolved), GIT_OBJ_ANY); - git_reference_free(resolved); - } - } - } - - if (obj == NULL) - return GIT_ENOTFOUND; + if ((err = git_revparse_single(&obj, repo, identifier)) < 0) + return err; switch (git_object_type(obj)) { case GIT_OBJ_TREE: @@ -61,16 +43,18 @@ char *colors[] = { "\033[36m" /* cyan */ }; -int printer( - void *data, - git_diff_delta *delta, - git_diff_range *range, +static int printer( + const git_diff_delta *delta, + const git_diff_range *range, char usage, const char *line, - size_t line_len) + size_t line_len, + void *data) { int *last_color = data, color = 0; + (void)delta; (void)range; (void)line_len; + if (*last_color >= 0) { switch (usage) { case GIT_DIFF_LINE_ADDITION: color = 3; break; @@ -93,7 +77,7 @@ int printer( return 0; } -int check_uint16_param(const char *arg, const char *pattern, uint16_t *val) +static int check_uint16_param(const char *arg, const char *pattern, uint16_t *val) { size_t len = strlen(pattern); uint16_t strval; @@ -107,16 +91,16 @@ int check_uint16_param(const char *arg, const char *pattern, uint16_t *val) return 1; } -int check_str_param(const char *arg, const char *pattern, char **val) +static int check_str_param(const char *arg, const char *pattern, const char **val) { size_t len = strlen(pattern); if (strncmp(arg, pattern, len)) return 0; - *val = (char *)(arg + len); + *val = (const char *)(arg + len); return 1; } -void usage(const char *message, const char *arg) +static void usage(const char *message, const char *arg) { if (message && arg) fprintf(stderr, "%s: %s\n", message, arg); @@ -128,10 +112,9 @@ void usage(const char *message, const char *arg) int main(int argc, char *argv[]) { - char path[GIT_PATH_MAX]; git_repository *repo = NULL; git_tree *t1 = NULL, *t2 = NULL; - git_diff_options opts = {0}; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff; int i, color = -1, compact = 0, cached = 0; char *a, *dir = ".", *treeish1 = NULL, *treeish2 = NULL; @@ -185,9 +168,7 @@ int main(int argc, char *argv[]) /* open repo */ - check(git_repository_discover(path, sizeof(path), dir, 0, "/"), - "Could not discover repository"); - check(git_repository_open(&repo, path), + check(git_repository_open_ext(&repo, dir, 0, NULL), "Could not open repository"); if (treeish1) @@ -202,30 +183,30 @@ int main(int argc, char *argv[]) /* nothing */ if (t1 && t2) - check(git_diff_tree_to_tree(repo, &opts, t1, t2, &diff), "Diff"); + check(git_diff_tree_to_tree(&diff, repo, t1, t2, &opts), "Diff"); else if (t1 && cached) - check(git_diff_index_to_tree(repo, &opts, t1, &diff), "Diff"); + check(git_diff_tree_to_index(&diff, repo, t1, NULL, &opts), "Diff"); else if (t1) { git_diff_list *diff2; - check(git_diff_index_to_tree(repo, &opts, t1, &diff), "Diff"); - check(git_diff_workdir_to_index(repo, &opts, &diff2), "Diff"); + check(git_diff_tree_to_index(&diff, repo, t1, NULL, &opts), "Diff"); + check(git_diff_index_to_workdir(&diff2, repo, NULL, &opts), "Diff"); check(git_diff_merge(diff, diff2), "Merge diffs"); git_diff_list_free(diff2); } else if (cached) { check(resolve_to_tree(repo, "HEAD", &t1), "looking up HEAD"); - check(git_diff_index_to_tree(repo, &opts, t1, &diff), "Diff"); + check(git_diff_tree_to_index(&diff, repo, t1, NULL, &opts), "Diff"); } else - check(git_diff_workdir_to_index(repo, &opts, &diff), "Diff"); + check(git_diff_index_to_workdir(&diff, repo, NULL, &opts), "Diff"); if (color >= 0) fputs(colors[0], stdout); if (compact) - check(git_diff_print_compact(diff, &color, printer), "Displaying diff"); + check(git_diff_print_compact(diff, printer, &color), "Displaying diff"); else - check(git_diff_print_patch(diff, &color, printer), "Displaying diff"); + check(git_diff_print_patch(diff, printer, &color), "Displaying diff"); if (color >= 0) fputs(colors[0], stdout); diff --git a/examples/general.c b/examples/general.c index 4585a4af5..adc7ed8d2 100644 --- a/examples/general.c +++ b/examples/general.c @@ -1,64 +1,85 @@ -// [**libgit2**][lg] is a portable, pure C implementation of the Git core methods -// provided as a re-entrant linkable library with a solid API, allowing you -// to write native speed custom Git applications in any language which -// supports C bindings. +// [**libgit2**][lg] is a portable, pure C implementation of the Git core +// methods provided as a re-entrant linkable library with a solid API, +// allowing you to write native speed custom Git applications in any +// language which supports C bindings. // // This file is an example of using that API in a real, compilable C file. -// As the API is updated, this file will be updated to demonstrate the -// new functionality. +// As the API is updated, this file will be updated to demonstrate the new +// functionality. // -// If you're trying to write something in C using [libgit2][lg], you will also want -// to check out the generated [API documentation][ap] and the [Usage Guide][ug]. We've -// tried to link to the relevant sections of the API docs in each section in this file. +// If you're trying to write something in C using [libgit2][lg], you should +// also check out the generated [API documentation][ap]. We try to link to +// the relevant sections of the API docs in each section in this file. // -// **libgit2** only implements the core plumbing functions, not really the higher -// level porcelain stuff. For a primer on Git Internals that you will need to know -// to work with Git at this level, check out [Chapter 9][pg] of the Pro Git book. +// **libgit2** (for the most part) only implements the core plumbing +// functions, not really the higher level porcelain stuff. For a primer on +// Git Internals that you will need to know to work with Git at this level, +// check out [Chapter 9][pg] of the Pro Git book. // // [lg]: http://libgit2.github.com // [ap]: http://libgit2.github.com/libgit2 -// [ug]: http://libgit2.github.com/api.html // [pg]: http://progit.org/book/ch9-0.html // ### Includes -// Including the `git2.h` header will include all the other libgit2 headers that you need. -// It should be the only thing you need to include in order to compile properly and get -// all the libgit2 API. +// Including the `git2.h` header will include all the other libgit2 headers +// that you need. It should be the only thing you need to include in order +// to compile properly and get all the libgit2 API. #include <git2.h> #include <stdio.h> +// Almost all libgit2 functions return 0 on success or negative on error. +// This is not production quality error checking, but should be sufficient +// as an example. +static void check_error(int error_code, const char *action) +{ + if (!error_code) + return; + + const git_error *error = giterr_last(); + + printf("Error %d %s - %s\n", error_code, action, + (error && error->message) ? error->message : "???"); + + exit(1); +} + int main (int argc, char** argv) { // ### Opening the Repository - // There are a couple of methods for opening a repository, this being the simplest. - // There are also [methods][me] for specifying the index file and work tree locations, here - // we are assuming they are in the normal places. + // There are a couple of methods for opening a repository, this being the + // simplest. There are also [methods][me] for specifying the index file + // and work tree locations, here we assume they are in the normal places. + // + // (Try running this program against tests-clar/resources/testrepo.git.) // // [me]: http://libgit2.github.com/libgit2/#HEAD/group/repository + int error; + const char *repo_path = (argc > 1) ? argv[1] : "/opt/libgit2-test/.git"; git_repository *repo; - if (argc > 1) { - git_repository_open(&repo, argv[1]); - } else { - git_repository_open(&repo, "/opt/libgit2-test/.git"); - } + + error = git_repository_open(&repo, repo_path); + check_error(error, "opening repository"); // ### SHA-1 Value Conversions - // For our first example, we will convert a 40 character hex value to the 20 byte raw SHA1 value. + // For our first example, we will convert a 40 character hex value to the + // 20 byte raw SHA1 value. printf("*Hex to Raw*\n"); - char hex[] = "fd6e612585290339ea8bf39c692a7ff6a29cb7c3"; + char hex[] = "4a202b346bb0fb0db7eff3cffeb3c70babbd2045"; - // The `git_oid` is the structure that keeps the SHA value. We will use this throughout the example - // for storing the value of the current SHA key we're working with. + // The `git_oid` is the structure that keeps the SHA value. We will use + // this throughout the example for storing the value of the current SHA + // key we're working with. git_oid oid; git_oid_fromstr(&oid, hex); - // Once we've converted the string into the oid value, we can get the raw value of the SHA. - printf("Raw 20 bytes: [%.20s]\n", (&oid)->id); + // Once we've converted the string into the oid value, we can get the raw + // value of the SHA by accessing `oid.id` - // Next we will convert the 20 byte raw SHA1 value to a human readable 40 char hex value. + // Next we will convert the 20 byte raw SHA1 value to a human readable 40 + // char hex value. printf("\n*Raw to Hex*\n"); char out[41]; out[40] = '\0'; @@ -68,10 +89,12 @@ int main (int argc, char** argv) printf("SHA hex string: %s\n", out); // ### Working with the Object Database - // **libgit2** provides [direct access][odb] to the object database. - // The object database is where the actual objects are stored in Git. For + + // **libgit2** provides [direct access][odb] to the object database. The + // object database is where the actual objects are stored in Git. For // working with raw objects, we'll need to get this structure from the // repository. + // // [odb]: http://libgit2.github.com/libgit2/#HEAD/group/odb git_odb *odb; git_repository_odb(&odb, repo); @@ -83,84 +106,93 @@ int main (int argc, char** argv) git_otype otype; const unsigned char *data; const char *str_type; - int error; - // We can read raw objects directly from the object database if we have the oid (SHA) - // of the object. This allows us to access objects without knowing thier type and inspect - // the raw bytes unparsed. + // We can read raw objects directly from the object database if we have + // the oid (SHA) of the object. This allows us to access objects without + // knowing thier type and inspect the raw bytes unparsed. error = git_odb_read(&obj, odb, &oid); - - // A raw object only has three properties - the type (commit, blob, tree or tag), the size - // of the raw data and the raw, unparsed data itself. For a commit or tag, that raw data - // is human readable plain ASCII text. For a blob it is just file contents, so it could be - // text or binary data. For a tree it is a special binary format, so it's unlikely to be - // hugely helpful as a raw object. + check_error(error, "finding object in repository"); + + // A raw object only has three properties - the type (commit, blob, tree + // or tag), the size of the raw data and the raw, unparsed data itself. + // For a commit or tag, that raw data is human readable plain ASCII + // text. For a blob it is just file contents, so it could be text or + // binary data. For a tree it is a special binary format, so it's unlikely + // to be hugely helpful as a raw object. data = (const unsigned char *)git_odb_object_data(obj); otype = git_odb_object_type(obj); - // We provide methods to convert from the object type which is an enum, to a string - // representation of that value (and vice-versa). + // We provide methods to convert from the object type which is an enum, to + // a string representation of that value (and vice-versa). str_type = git_object_type2string(otype); printf("object length and type: %d, %s\n", (int)git_odb_object_size(obj), str_type); - // For proper memory management, close the object when you are done with it or it will leak - // memory. + // For proper memory management, close the object when you are done with + // it or it will leak memory. git_odb_object_free(obj); // #### Raw Object Writing printf("\n*Raw Object Write*\n"); - // You can also write raw object data to Git. This is pretty cool because it gives you - // direct access to the key/value properties of Git. Here we'll write a new blob object - // that just contains a simple string. Notice that we have to specify the object type as - // the `git_otype` enum. + // You can also write raw object data to Git. This is pretty cool because + // it gives you direct access to the key/value properties of Git. Here + // we'll write a new blob object that just contains a simple string. + // Notice that we have to specify the object type as the `git_otype` enum. git_odb_write(&oid, odb, "test data", sizeof("test data") - 1, GIT_OBJ_BLOB); - // Now that we've written the object, we can check out what SHA1 was generated when the - // object was written to our database. + // Now that we've written the object, we can check out what SHA1 was + // generated when the object was written to our database. git_oid_fmt(out, &oid); printf("Written Object: %s\n", out); // ### Object Parsing - // libgit2 has methods to parse every object type in Git so you don't have to work directly - // with the raw data. This is much faster and simpler than trying to deal with the raw data - // yourself. + + // libgit2 has methods to parse every object type in Git so you don't have + // to work directly with the raw data. This is much faster and simpler + // than trying to deal with the raw data yourself. // #### Commit Parsing - // [Parsing commit objects][pco] is simple and gives you access to all the data in the commit - // - the // author (name, email, datetime), committer (same), tree, message, encoding and parent(s). + + // [Parsing commit objects][pco] is simple and gives you access to all the + // data in the commit - the author (name, email, datetime), committer + // (same), tree, message, encoding and parent(s). + // // [pco]: http://libgit2.github.com/libgit2/#HEAD/group/commit printf("\n*Commit Parsing*\n"); git_commit *commit; - git_oid_fromstr(&oid, "f0877d0b841d75172ec404fc9370173dfffc20d1"); + git_oid_fromstr(&oid, "8496071c1b46c854b31185ea97743be6a8774479"); error = git_commit_lookup(&commit, repo, &oid); + check_error(error, "looking up commit"); const git_signature *author, *cmtter; const char *message; time_t ctime; unsigned int parents, p; - // Each of the properties of the commit object are accessible via methods, including commonly - // needed variations, such as `git_commit_time` which returns the author time and `_message` - // which gives you the commit message. + // Each of the properties of the commit object are accessible via methods, + // including commonly needed variations, such as `git_commit_time` which + // returns the author time and `git_commit_message` which gives you the + // commit message (as a NUL-terminated string). message = git_commit_message(commit); author = git_commit_author(commit); cmtter = git_commit_committer(commit); ctime = git_commit_time(commit); - // The author and committer methods return [git_signature] structures, which give you name, email - // and `when`, which is a `git_time` structure, giving you a timestamp and timezone offset. + // The author and committer methods return [git_signature] structures, + // which give you name, email and `when`, which is a `git_time` structure, + // giving you a timestamp and timezone offset. printf("Author: %s (%s)\n", author->name, author->email); - // Commits can have zero or more parents. The first (root) commit will have no parents, most commits - // will have one, which is the commit it was based on, and merge commits will have two or more. - // Commits can technically have any number, though it's pretty rare to have more than two. + // Commits can have zero or more parents. The first (root) commit will + // have no parents, most commits will have one (i.e. the commit it was + // based on) and merge commits will have two or more. Commits can + // technically have any number, though it's rare to have more than two. parents = git_commit_parentcount(commit); for (p = 0;p < parents;p++) { git_commit *parent; @@ -170,15 +202,17 @@ int main (int argc, char** argv) git_commit_free(parent); } - // Don't forget to close the object to prevent memory leaks. You will have to do this for - // all the objects you open and parse. + // Don't forget to close the object to prevent memory leaks. You will have + // to do this for all the objects you open and parse. git_commit_free(commit); // #### Writing Commits + + // libgit2 provides a couple of methods to create commit objects easily as + // well. There are four different create signatures, we'll just show one + // of them here. You can read about the other ones in the [commit API + // docs][cd]. // - // libgit2 provides a couple of methods to create commit objects easily as well. There are four - // different create signatures, we'll just show one of them here. You can read about the other - // ones in the [commit API docs][cd]. // [cd]: http://libgit2.github.com/libgit2/#HEAD/group/commit printf("\n*Commit Writing*\n"); @@ -186,24 +220,27 @@ int main (int argc, char** argv) git_tree *tree; git_commit *parent; - // Creating signatures for an authoring identity and time is pretty simple - you will need to have - // this to create a commit in order to specify who created it and when. Default values for the name - // and email should be found in the `user.name` and `user.email` configuration options. See the `config` - // section of this example file to see how to access config values. - git_signature_new((git_signature **)&author, "Scott Chacon", "schacon@gmail.com", - 123456789, 60); - git_signature_new((git_signature **)&cmtter, "Scott A Chacon", "scott@github.com", - 987654321, 90); - - // Commit objects need a tree to point to and optionally one or more parents. Here we're creating oid - // objects to create the commit with, but you can also use - git_oid_fromstr(&tree_id, "28873d96b4e8f4e33ea30f4c682fd325f7ba56ac"); + // Creating signatures for an authoring identity and time is simple. You + // will need to do this to specify who created a commit and when. Default + // values for the name and email should be found in the `user.name` and + // `user.email` configuration options. See the `config` section of this + // example file to see how to access config values. + git_signature_new((git_signature **)&author, + "Scott Chacon", "schacon@gmail.com", 123456789, 60); + git_signature_new((git_signature **)&cmtter, + "Scott A Chacon", "scott@github.com", 987654321, 90); + + // Commit objects need a tree to point to and optionally one or more + // parents. Here we're creating oid objects to create the commit with, + // but you can also use + git_oid_fromstr(&tree_id, "f60079018b664e4e79329a7ef9559c8d9e0378d1"); git_tree_lookup(&tree, repo, &tree_id); - git_oid_fromstr(&parent_id, "f0877d0b841d75172ec404fc9370173dfffc20d1"); + git_oid_fromstr(&parent_id, "5b5b025afb0b4c913b4c338a42934a3863bf3644"); git_commit_lookup(&parent, repo, &parent_id); - // Here we actually create the commit object with a single call with all the values we need to create - // the commit. The SHA key is written to the `commit_id` variable here. + // Here we actually create the commit object with a single call with all + // the values we need to create the commit. The SHA key is written to the + // `commit_id` variable here. git_commit_create_v( &commit_id, /* out id */ repo, @@ -220,35 +257,42 @@ int main (int argc, char** argv) printf("New Commit: %s\n", out); // #### Tag Parsing - // You can parse and create tags with the [tag management API][tm], which functions very similarly - // to the commit lookup, parsing and creation methods, since the objects themselves are very similar. + + // You can parse and create tags with the [tag management API][tm], which + // functions very similarly to the commit lookup, parsing and creation + // methods, since the objects themselves are very similar. + // // [tm]: http://libgit2.github.com/libgit2/#HEAD/group/tag printf("\n*Tag Parsing*\n"); git_tag *tag; const char *tmessage, *tname; git_otype ttype; - // We create an oid for the tag object if we know the SHA and look it up in the repository the same - // way that we would a commit (or any other) object. - git_oid_fromstr(&oid, "bc422d45275aca289c51d79830b45cecebff7c3a"); + // We create an oid for the tag object if we know the SHA and look it up + // the same way that we would a commit (or any other object). + git_oid_fromstr(&oid, "b25fa35b38051e4ae45d4222e795f9df2e43f1d1"); error = git_tag_lookup(&tag, repo, &oid); + check_error(error, "looking up tag"); - // Now that we have the tag object, we can extract the information it generally contains: the target - // (usually a commit object), the type of the target object (usually 'commit'), the name ('v1.0'), - // the tagger (a git_signature - name, email, timestamp), and the tag message. + // Now that we have the tag object, we can extract the information it + // generally contains: the target (usually a commit object), the type of + // the target object (usually 'commit'), the name ('v1.0'), the tagger (a + // git_signature - name, email, timestamp), and the tag message. git_tag_target((git_object **)&commit, tag); - tname = git_tag_name(tag); // "test" - ttype = git_tag_type(tag); // GIT_OBJ_COMMIT (otype enum) - tmessage = git_tag_message(tag); // "tag message\n" + tname = git_tag_name(tag); // "test" + ttype = git_tag_target_type(tag); // GIT_OBJ_COMMIT (otype enum) + tmessage = git_tag_message(tag); // "tag message\n" printf("Tag Message: %s\n", tmessage); git_commit_free(commit); // #### Tree Parsing - // [Tree parsing][tp] is a bit different than the other objects, in that we have a subtype which is the - // tree entry. This is not an actual object type in Git, but a useful structure for parsing and - // traversing tree entries. + + // [Tree parsing][tp] is a bit different than the other objects, in that + // we have a subtype which is the tree entry. This is not an actual + // object type in Git, but a useful structure for parsing and traversing + // tree entries. // // [tp]: http://libgit2.github.com/libgit2/#HEAD/group/tree printf("\n*Tree Parsing*\n"); @@ -260,54 +304,61 @@ int main (int argc, char** argv) git_oid_fromstr(&oid, "2a741c18ac5ff082a7caaec6e74db3075a1906b5"); git_tree_lookup(&tree, repo, &oid); - // Getting the count of entries in the tree so you can iterate over them if you want to. - int cnt = git_tree_entrycount(tree); // 3 - printf("tree entries: %d\n", cnt); + // Getting the count of entries in the tree so you can iterate over them + // if you want to. + size_t cnt = git_tree_entrycount(tree); // 3 + printf("tree entries: %d\n", (int)cnt); entry = git_tree_entry_byindex(tree, 0); printf("Entry name: %s\n", git_tree_entry_name(entry)); // "hello.c" - // You can also access tree entries by name if you know the name of the entry you're looking for. - entry = git_tree_entry_byname(tree, "hello.c"); + // You can also access tree entries by name if you know the name of the + // entry you're looking for. + entry = git_tree_entry_byname(tree, "README"); git_tree_entry_name(entry); // "hello.c" - // Once you have the entry object, you can access the content or subtree (or commit, in the case - // of submodules) that it points to. You can also get the mode if you want. + // Once you have the entry object, you can access the content or subtree + // (or commit, in the case of submodules) that it points to. You can also + // get the mode if you want. git_tree_entry_to_object(&objt, repo, entry); // blob // Remember to close the looked-up object once you are done using it git_object_free(objt); // #### Blob Parsing - // - // The last object type is the simplest and requires the least parsing help. Blobs are just file - // contents and can contain anything, there is no structure to it. The main advantage to using the - // [simple blob api][ba] is that when you're creating blobs you don't have to calculate the size - // of the content. There is also a helper for reading a file from disk and writing it to the db and - // getting the oid back so you don't have to do all those steps yourself. + + // The last object type is the simplest and requires the least parsing + // help. Blobs are just file contents and can contain anything, there is + // no structure to it. The main advantage to using the [simple blob + // api][ba] is that when you're creating blobs you don't have to calculate + // the size of the content. There is also a helper for reading a file + // from disk and writing it to the db and getting the oid back so you + // don't have to do all those steps yourself. // // [ba]: http://libgit2.github.com/libgit2/#HEAD/group/blob printf("\n*Blob Parsing*\n"); git_blob *blob; - git_oid_fromstr(&oid, "af7574ea73f7b166f869ef1a39be126d9a186ae0"); + git_oid_fromstr(&oid, "1385f264afb75a56a5bec74243be9b367ba4ca08"); git_blob_lookup(&blob, repo, &oid); // You can access a buffer with the raw contents of the blob directly. - // Note that this buffer may not be contain ASCII data for certain blobs (e.g. binary files): - // do not consider the buffer a NULL-terminated string, and use the `git_blob_rawsize` attribute to - // find out its exact size in bytes - printf("Blob Size: %ld\n", git_blob_rawsize(blob)); // 8 + // Note that this buffer may not be contain ASCII data for certain blobs + // (e.g. binary files): do not consider the buffer a NULL-terminated + // string, and use the `git_blob_rawsize` attribute to find out its exact + // size in bytes + printf("Blob Size: %ld\n", (long)git_blob_rawsize(blob)); // 8 git_blob_rawcontent(blob); // "content" // ### Revwalking - // - // The libgit2 [revision walking api][rw] provides methods to traverse the directed graph created - // by the parent pointers of the commit objects. Since all commits point back to the commit that - // came directly before them, you can walk this parentage as a graph and find all the commits that - // were ancestors of (reachable from) a given starting point. This can allow you to create `git log` - // type functionality. + + // The libgit2 [revision walking api][rw] provides methods to traverse the + // directed graph created by the parent pointers of the commit objects. + // Since all commits point back to the commit that came directly before + // them, you can walk this parentage as a graph and find all the commits + // that were ancestors of (reachable from) a given starting point. This + // can allow you to create `git log` type functionality. // // [rw]: http://libgit2.github.com/libgit2/#HEAD/group/revwalk @@ -315,13 +366,15 @@ int main (int argc, char** argv) git_revwalk *walk; git_commit *wcommit; - git_oid_fromstr(&oid, "f0877d0b841d75172ec404fc9370173dfffc20d1"); + git_oid_fromstr(&oid, "5b5b025afb0b4c913b4c338a42934a3863bf3644"); - // To use the revwalker, create a new walker, tell it how you want to sort the output and then push - // one or more starting points onto the walker. If you want to emulate the output of `git log` you - // would push the SHA of the commit that HEAD points to into the walker and then start traversing them. - // You can also 'hide' commits that you want to stop at or not see any of their ancestors. So if you - // want to emulate `git log branch1..branch2`, you would push the oid of `branch2` and hide the oid + // To use the revwalker, create a new walker, tell it how you want to sort + // the output and then push one or more starting points onto the walker. + // If you want to emulate the output of `git log` you would push the SHA + // of the commit that HEAD points to into the walker and then start + // traversing them. You can also 'hide' commits that you want to stop at + // or not see any of their ancestors. So if you want to emulate `git log + // branch1..branch2`, you would push the oid of `branch2` and hide the oid // of `branch1`. git_revwalk_new(&walk, repo); git_revwalk_sorting(walk, GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE); @@ -330,28 +383,32 @@ int main (int argc, char** argv) const git_signature *cauth; const char *cmsg; - // Now that we have the starting point pushed onto the walker, we can start asking for ancestors. It - // will return them in the sorting order we asked for as commit oids. - // We can then lookup and parse the commited pointed at by the returned OID; - // note that this operation is specially fast since the raw contents of the commit object will - // be cached in memory + // Now that we have the starting point pushed onto the walker, we start + // asking for ancestors. It will return them in the sorting order we asked + // for as commit oids. We can then lookup and parse the commited pointed + // at by the returned OID; note that this operation is specially fast + // since the raw contents of the commit object will be cached in memory while ((git_revwalk_next(&oid, walk)) == 0) { error = git_commit_lookup(&wcommit, repo, &oid); + check_error(error, "looking up commit during revwalk"); + cmsg = git_commit_message(wcommit); cauth = git_commit_author(wcommit); printf("%s (%s)\n", cmsg, cauth->email); + git_commit_free(wcommit); } - // Like the other objects, be sure to free the revwalker when you're done to prevent memory leaks. - // Also, make sure that the repository being walked it not deallocated while the walk is in - // progress, or it will result in undefined behavior + // Like the other objects, be sure to free the revwalker when you're done + // to prevent memory leaks. Also, make sure that the repository being + // walked it not deallocated while the walk is in progress, or it will + // result in undefined behavior git_revwalk_free(walk); // ### Index File Manipulation - // - // The [index file API][gi] allows you to read, traverse, update and write the Git index file - // (sometimes thought of as the staging area). + + // The [index file API][gi] allows you to read, traverse, update and write + // the Git index file (sometimes thought of as the staging area). // // [gi]: http://libgit2.github.com/libgit2/#HEAD/group/index @@ -360,18 +417,21 @@ int main (int argc, char** argv) git_index *index; unsigned int i, ecount; - // You can either open the index from the standard location in an open repository, as we're doing - // here, or you can open and manipulate any index file with `git_index_open_bare()`. The index - // for the repository will be located and loaded from disk. + // You can either open the index from the standard location in an open + // repository, as we're doing here, or you can open and manipulate any + // index file with `git_index_open_bare()`. The index for the repository + // will be located and loaded from disk. git_repository_index(&index, repo); - // For each entry in the index, you can get a bunch of information including the SHA (oid), path - // and mode which map to the tree objects that are written out. It also has filesystem properties - // to help determine what to inspect for changes (ctime, mtime, dev, ino, uid, gid, file_size and flags) - // All these properties are exported publicly in the `git_index_entry` struct + // For each entry in the index, you can get a bunch of information + // including the SHA (oid), path and mode which map to the tree objects + // that are written out. It also has filesystem properties to help + // determine what to inspect for changes (ctime, mtime, dev, ino, uid, + // gid, file_size and flags) All these properties are exported publicly in + // the `git_index_entry` struct ecount = git_index_entrycount(index); for (i = 0; i < ecount; ++i) { - git_index_entry *e = git_index_get(index, i); + const git_index_entry *e = git_index_get_byindex(index, i); printf("path: %s\n", e->path); printf("mtime: %d\n", (int)e->mtime.seconds); @@ -381,36 +441,37 @@ int main (int argc, char** argv) git_index_free(index); // ### References - // - // The [reference API][ref] allows you to list, resolve, create and update references such as - // branches, tags and remote references (everything in the .git/refs directory). + + // The [reference API][ref] allows you to list, resolve, create and update + // references such as branches, tags and remote references (everything in + // the .git/refs directory). // // [ref]: http://libgit2.github.com/libgit2/#HEAD/group/reference printf("\n*Reference Listing*\n"); - // Here we will implement something like `git for-each-ref` simply listing out all available - // references and the object SHA they resolve to. + // Here we will implement something like `git for-each-ref` simply listing + // out all available references and the object SHA they resolve to. git_strarray ref_list; - git_reference_listall(&ref_list, repo, GIT_REF_LISTALL); + git_reference_list(&ref_list, repo, GIT_REF_LISTALL); const char *refname; git_reference *ref; - // Now that we have the list of reference names, we can lookup each ref one at a time and - // resolve them to the SHA, then print both values out. + // Now that we have the list of reference names, we can lookup each ref + // one at a time and resolve them to the SHA, then print both values out. for (i = 0; i < ref_list.count; ++i) { refname = ref_list.strings[i]; git_reference_lookup(&ref, repo, refname); switch (git_reference_type(ref)) { case GIT_REF_OID: - git_oid_fmt(out, git_reference_oid(ref)); + git_oid_fmt(out, git_reference_target(ref)); printf("%s [%s]\n", refname, out); break; case GIT_REF_SYMBOLIC: - printf("%s => %s\n", refname, git_reference_target(ref)); + printf("%s => %s\n", refname, git_reference_symbolic_target(ref)); break; default: fprintf(stderr, "Unexpected reference type\n"); @@ -421,9 +482,9 @@ int main (int argc, char** argv) git_strarray_free(&ref_list); // ### Config Files - // - // The [config API][config] allows you to list and updatee config values in - // any of the accessible config file locations (system, global, local). + + // The [config API][config] allows you to list and updatee config values + // in any of the accessible config file locations (system, global, local). // // [config]: http://libgit2.github.com/libgit2/#HEAD/group/config @@ -435,12 +496,14 @@ int main (int argc, char** argv) git_config *cfg; // Open a config object so we can read global values from it. - git_config_open_ondisk(&cfg, "~/.gitconfig"); + char config_path[256]; + sprintf(config_path, "%s/config", repo_path); + check_error(git_config_open_ondisk(&cfg, config_path), "opening config"); - git_config_get_int32(cfg, "help.autocorrect", &j); + git_config_get_int32(&j, cfg, "help.autocorrect"); printf("Autocorrect: %d\n", j); - git_config_get_string(cfg, "user.email", &email); + git_config_get_string(&email, cfg, "user.email"); printf("Email: %s\n", email); // Finally, when you're done with the repository, you can free it as well. diff --git a/examples/network/Makefile b/examples/network/Makefile index c21869ac9..810eb705b 100644 --- a/examples/network/Makefile +++ b/examples/network/Makefile @@ -2,13 +2,20 @@ default: all CC = gcc CFLAGS += -g -CFLAGS += -I../../include -L../../ -lgit2 -lpthread +CFLAGS += -I../../include +LDFLAGS += -L../../build -L../.. +LIBRARIES += -lgit2 -lpthread OBJECTS = \ git2.o \ ls-remote.o \ fetch.o \ + clone.o \ index-pack.o all: $(OBJECTS) - $(CC) $(CFLAGS) -o git2 $(OBJECTS) + $(CC) $(CFLAGS) $(LDFLAGS) -o git2 $(OBJECTS) $(LIBRARIES) + +clean: + $(RM) $(OBJECTS) + $(RM) git2 diff --git a/examples/network/clone.c b/examples/network/clone.c new file mode 100644 index 000000000..00c25c1ae --- /dev/null +++ b/examples/network/clone.c @@ -0,0 +1,122 @@ +#include "common.h" +#include <git2.h> +#include <git2/clone.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifndef _WIN32 +# include <pthread.h> +# include <unistd.h> +#endif + +/* Shamelessly borrowed from http://stackoverflow.com/questions/3417837/ + * with permission of the original author, Martin Pool. + * http://sourcefrog.net/weblog/software/languages/C/unused.html + */ +#ifdef UNUSED +#elif defined(__GNUC__) +# define UNUSED(x) UNUSED_ ## x __attribute__((unused)) +#elif defined(__LCLINT__) +# define UNUSED(x) /*@unused@*/ x +#else +# define UNUSED(x) x +#endif + +typedef struct progress_data { + git_transfer_progress fetch_progress; + size_t completed_steps; + size_t total_steps; + const char *path; +} progress_data; + +static void print_progress(const progress_data *pd) +{ + int network_percent = (100*pd->fetch_progress.received_objects) / pd->fetch_progress.total_objects; + int index_percent = (100*pd->fetch_progress.indexed_objects) / pd->fetch_progress.total_objects; + int checkout_percent = pd->total_steps > 0 + ? (100 * pd->completed_steps) / pd->total_steps + : 0.f; + int kbytes = pd->fetch_progress.received_bytes / 1024; + + printf("net %3d%% (%4d kb, %5d/%5d) / idx %3d%% (%5d/%5d) / chk %3d%% (%4" PRIuZ "/%4" PRIuZ ") %s\n", + network_percent, kbytes, + pd->fetch_progress.received_objects, pd->fetch_progress.total_objects, + index_percent, pd->fetch_progress.indexed_objects, pd->fetch_progress.total_objects, + checkout_percent, + pd->completed_steps, pd->total_steps, + pd->path); +} + +static int fetch_progress(const git_transfer_progress *stats, void *payload) +{ + progress_data *pd = (progress_data*)payload; + pd->fetch_progress = *stats; + print_progress(pd); + return 0; +} +static void checkout_progress(const char *path, size_t cur, size_t tot, void *payload) +{ + progress_data *pd = (progress_data*)payload; + pd->completed_steps = cur; + pd->total_steps = tot; + pd->path = path; + print_progress(pd); +} + +static int cred_acquire(git_cred **out, + const char * UNUSED(url), + const char * UNUSED(username_from_url), + unsigned int UNUSED(allowed_types), + void * UNUSED(payload)) +{ + char username[128] = {0}; + char password[128] = {0}; + + printf("Username: "); + scanf("%s", username); + + /* Yup. Right there on your terminal. Careful where you copy/paste output. */ + printf("Password: "); + scanf("%s", password); + + return git_cred_userpass_plaintext_new(out, username, password); +} + +int do_clone(git_repository *repo, int argc, char **argv) +{ + progress_data pd = {{0}}; + git_repository *cloned_repo = NULL; + git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT; + git_checkout_opts checkout_opts = GIT_CHECKOUT_OPTS_INIT; + const char *url = argv[1]; + const char *path = argv[2]; + int error; + + (void)repo; // unused + + // Validate args + if (argc < 3) { + printf ("USAGE: %s <url> <path>\n", argv[0]); + return -1; + } + + // Set up options + checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; + checkout_opts.progress_cb = checkout_progress; + checkout_opts.progress_payload = &pd; + clone_opts.checkout_opts = checkout_opts; + clone_opts.fetch_progress_cb = &fetch_progress; + clone_opts.fetch_progress_payload = &pd; + clone_opts.cred_acquire_cb = cred_acquire; + + // Do the clone + error = git_clone(&cloned_repo, url, path, &clone_opts); + printf("\n"); + if (error != 0) { + const git_error *err = giterr_last(); + if (err) printf("ERROR %d: %s\n", err->klass, err->message); + else printf("ERROR %d: no detailed info\n", error); + } + else if (cloned_repo) git_repository_free(cloned_repo); + return error; +} diff --git a/examples/network/common.h b/examples/network/common.h index 29460bb36..a4cfa1a7e 100644 --- a/examples/network/common.h +++ b/examples/network/common.h @@ -10,5 +10,15 @@ int parse_pkt_line(git_repository *repo, int argc, char **argv); int show_remote(git_repository *repo, int argc, char **argv); int fetch(git_repository *repo, int argc, char **argv); int index_pack(git_repository *repo, int argc, char **argv); +int do_clone(git_repository *repo, int argc, char **argv); + +#ifndef PRIuZ +/* Define the printf format specifer to use for size_t output */ +#if defined(_MSC_VER) || defined(__MINGW32__) +# define PRIuZ "Iu" +#else +# define PRIuZ "zu" +#endif +#endif #endif /* __COMMON_H__ */ diff --git a/examples/network/fetch.c b/examples/network/fetch.c index f4a044984..6020ec6ec 100644 --- a/examples/network/fetch.c +++ b/examples/network/fetch.c @@ -3,23 +3,31 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <pthread.h> +#ifndef _WIN32 +# include <pthread.h> +# include <unistd.h> +#endif struct dl_data { git_remote *remote; - git_off_t *bytes; - git_indexer_stats *stats; int ret; int finished; }; +static void progress_cb(const char *str, int len, void *data) +{ + (void)data; + printf("remote: %.*s", len, str); + fflush(stdout); /* We don't have the \n to force the flush */ +} + static void *download(void *ptr) { struct dl_data *data = (struct dl_data *)ptr; // Connect to the remote end specifying that we want to fetch // information from it. - if (git_remote_connect(data->remote, GIT_DIR_FETCH) < 0) { + if (git_remote_connect(data->remote, GIT_DIRECTION_FETCH) < 0) { data->ret = -1; goto exit; } @@ -27,7 +35,7 @@ static void *download(void *ptr) // Download the packfile and index it. This function updates the // amount of received data and the indexer stats which lets you // inform the user about progress. - if (git_remote_download(data->remote, data->bytes, data->stats) < 0) { + if (git_remote_download(data->remote, NULL, NULL) < 0) { data->ret = -1; goto exit; } @@ -36,13 +44,13 @@ static void *download(void *ptr) exit: data->finished = 1; - pthread_exit(&data->ret); + return &data->ret; } -int update_cb(const char *refname, const git_oid *a, const git_oid *b) +static int update_cb(const char *refname, const git_oid *a, const git_oid *b, void *data) { - const char *action; char a_str[GIT_OID_HEXSZ+1], b_str[GIT_OID_HEXSZ+1]; + (void)data; git_oid_fmt(b_str, b); b_str[GIT_OID_HEXSZ] = '\0'; @@ -60,54 +68,80 @@ int update_cb(const char *refname, const git_oid *a, const git_oid *b) int fetch(git_repository *repo, int argc, char **argv) { - git_remote *remote = NULL; - git_off_t bytes = 0; - git_indexer_stats stats; - pthread_t worker; - struct dl_data data; - - // Figure out whether it's a named remote or a URL - printf("Fetching %s\n", argv[1]); - if (git_remote_load(&remote, repo, argv[1]) < 0) { - if (git_remote_new(&remote, repo, NULL, argv[1], NULL) < 0) - return -1; - } - - // Set up the information for the background worker thread - data.remote = remote; - data.bytes = &bytes; - data.stats = &stats; - data.ret = 0; - data.finished = 0; - memset(&stats, 0, sizeof(stats)); - - pthread_create(&worker, NULL, download, &data); - - // Loop while the worker thread is still running. Here we show processed - // and total objects in the pack and the amount of received - // data. Most frontends will probably want to show a percentage and - // the download rate. - do { - usleep(10000); - printf("\rReceived %d/%d objects in %d bytes", stats.processed, stats.total, bytes); - } while (!data.finished); - printf("\rReceived %d/%d objects in %d bytes\n", stats.processed, stats.total, bytes); - - // Disconnect the underlying connection to prevent from idling. - git_remote_disconnect(remote); - - // Update the references in the remote's namespace to point to the - // right commits. This may be needed even if there was no packfile - // to download, which can happen e.g. when the branches have been - // changed but all the neede objects are available locally. - if (git_remote_update_tips(remote, update_cb) < 0) - return -1; - - git_remote_free(remote); - - return 0; - -on_error: - git_remote_free(remote); - return -1; + git_remote *remote = NULL; + const git_transfer_progress *stats; + struct dl_data data; + git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; +#ifndef _WIN32 + pthread_t worker; +#endif + + if (argc < 2) { + fprintf(stderr, "usage: %s fetch <repo>\n", argv[-1]); + return EXIT_FAILURE; + } + + // Figure out whether it's a named remote or a URL + printf("Fetching %s for repo %p\n", argv[1], repo); + if (git_remote_load(&remote, repo, argv[1]) < 0) { + if (git_remote_create_inmemory(&remote, repo, NULL, argv[1]) < 0) + return -1; + } + + // Set up the callbacks (only update_tips for now) + callbacks.update_tips = &update_cb; + callbacks.progress = &progress_cb; + git_remote_set_callbacks(remote, &callbacks); + + // Set up the information for the background worker thread + data.remote = remote; + data.ret = 0; + data.finished = 0; + + stats = git_remote_stats(remote); + +#ifdef _WIN32 + download(&data); +#else + pthread_create(&worker, NULL, download, &data); + + // Loop while the worker thread is still running. Here we show processed + // and total objects in the pack and the amount of received + // data. Most frontends will probably want to show a percentage and + // the download rate. + do { + usleep(10000); + + if (stats->total_objects > 0) + printf("Received %d/%d objects (%d) in %" PRIuZ " bytes\r", + stats->received_objects, stats->total_objects, + stats->indexed_objects, stats->received_bytes); + } while (!data.finished); + + if (data.ret < 0) + goto on_error; + + pthread_join(worker, NULL); +#endif + + printf("\rReceived %d/%d objects in %zu bytes\n", + stats->indexed_objects, stats->total_objects, stats->received_bytes); + + // Disconnect the underlying connection to prevent from idling. + git_remote_disconnect(remote); + + // Update the references in the remote's namespace to point to the + // right commits. This may be needed even if there was no packfile + // to download, which can happen e.g. when the branches have been + // changed but all the neede objects are available locally. + if (git_remote_update_tips(remote) < 0) + return -1; + + git_remote_free(remote); + + return 0; + + on_error: + git_remote_free(remote); + return -1; } diff --git a/examples/network/git2.c b/examples/network/git2.c index 7c02305c4..ecb16630b 100644 --- a/examples/network/git2.c +++ b/examples/network/git2.c @@ -1,5 +1,6 @@ #include <stdlib.h> #include <stdio.h> +#include <string.h> #include "common.h" @@ -12,11 +13,12 @@ struct { } commands[] = { {"ls-remote", ls_remote}, {"fetch", fetch}, + {"clone", do_clone}, {"index-pack", index_pack}, { NULL, NULL} }; -int run_command(git_cb fn, int argc, char **argv) +static int run_command(git_cb fn, int argc, char **argv) { int error; git_repository *repo; @@ -45,7 +47,7 @@ int run_command(git_cb fn, int argc, char **argv) int main(int argc, char **argv) { - int i, error; + int i; if (argc < 2) { fprintf(stderr, "usage: %s <cmd> [repo]\n", argv[0]); diff --git a/examples/network/index-pack.c b/examples/network/index-pack.c index 03f3ae37e..889305da8 100644 --- a/examples/network/index-pack.c +++ b/examples/network/index-pack.c @@ -2,101 +2,87 @@ #include <stdlib.h> #include <stdio.h> #include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#ifdef _WIN32 +# include <io.h> +# include <Windows.h> + +# define open _open +# define read _read +# define close _close + +#define ssize_t unsigned int +#else +# include <unistd.h> +#endif #include "common.h" // This could be run in the main loop whilst the application waits for // the indexing to finish in a worker thread -int index_cb(const git_indexer_stats *stats, void *data) +static int index_cb(const git_transfer_progress *stats, void *data) { - printf("\rProcessing %d of %d", stats->processed, stats->total); -} + (void)data; + printf("\rProcessing %d of %d", stats->indexed_objects, stats->total_objects); -int index_pack(git_repository *repo, int argc, char **argv) -{ - git_indexer_stream *idx; - git_indexer_stats stats = {0, 0}; - int error, fd; - char hash[GIT_OID_HEXSZ + 1] = {0}; - ssize_t read_bytes; - char buf[512]; - - if (argc < 2) { - fprintf(stderr, "I need a packfile\n"); - return EXIT_FAILURE; - } - - if (git_indexer_stream_new(&idx, ".git") < 0) { - puts("bad idx"); - return -1; - } - - if ((fd = open(argv[1], 0)) < 0) { - perror("open"); - return -1; - } - - do { - read_bytes = read(fd, buf, sizeof(buf)); - if (read_bytes < 0) - break; - - if ((error = git_indexer_stream_add(idx, buf, read_bytes, &stats)) < 0) - goto cleanup; - - printf("\rIndexing %d of %d", stats.processed, stats.total); - } while (read_bytes > 0); - - if (read_bytes < 0) { - error = -1; - perror("failed reading"); - goto cleanup; - } - - if ((error = git_indexer_stream_finalize(idx, &stats)) < 0) - goto cleanup; - - printf("\rIndexing %d of %d\n", stats.processed, stats.total); - - git_oid_fmt(hash, git_indexer_stream_hash(idx)); - puts(hash); - -cleanup: - close(fd); - git_indexer_stream_free(idx); - return error; + return 0; } -int index_pack_old(git_repository *repo, int argc, char **argv) +int index_pack(git_repository *repo, int argc, char **argv) { - git_indexer *indexer; - git_indexer_stats stats; - int error; - char hash[GIT_OID_HEXSZ + 1] = {0}; - - if (argc < 2) { - fprintf(stderr, "I need a packfile\n"); - return EXIT_FAILURE; - } - - // Create a new indexer - error = git_indexer_new(&indexer, argv[1]); - if (error < 0) - return error; - - // Index the packfile. This function can take a very long time and - // should be run in a worker thread. - error = git_indexer_run(indexer, &stats); - if (error < 0) - return error; - - // Write the information out to an index file - error = git_indexer_write(indexer); - - // Get the packfile's hash (which should become it's filename) - git_oid_fmt(hash, git_indexer_hash(indexer)); - puts(hash); - - git_indexer_free(indexer); - - return 0; + git_indexer_stream *idx; + git_transfer_progress stats = {0, 0}; + int error; + char hash[GIT_OID_HEXSZ + 1] = {0}; + int fd; + ssize_t read_bytes; + char buf[512]; + + (void)repo; + + if (argc < 2) { + fprintf(stderr, "usage: %s index-pack <packfile>\n", argv[-1]); + return EXIT_FAILURE; + } + + if (git_indexer_stream_new(&idx, ".", NULL, NULL) < 0) { + puts("bad idx"); + return -1; + } + + if ((fd = open(argv[1], 0)) < 0) { + perror("open"); + return -1; + } + + do { + read_bytes = read(fd, buf, sizeof(buf)); + if (read_bytes < 0) + break; + + if ((error = git_indexer_stream_add(idx, buf, read_bytes, &stats)) < 0) + goto cleanup; + + index_cb(&stats, NULL); + } while (read_bytes > 0); + + if (read_bytes < 0) { + error = -1; + perror("failed reading"); + goto cleanup; + } + + if ((error = git_indexer_stream_finalize(idx, &stats)) < 0) + goto cleanup; + + printf("\rIndexing %d of %d\n", stats.indexed_objects, stats.total_objects); + + git_oid_fmt(hash, git_indexer_stream_hash(idx)); + puts(hash); + + cleanup: + close(fd); + git_indexer_stream_free(idx); + return error; } diff --git a/examples/network/ls-remote.c b/examples/network/ls-remote.c index 39cc64725..252011828 100644 --- a/examples/network/ls-remote.c +++ b/examples/network/ls-remote.c @@ -7,25 +7,27 @@ static int show_ref__cb(git_remote_head *head, void *payload) { char oid[GIT_OID_HEXSZ + 1] = {0}; + + (void)payload; git_oid_fmt(oid, &head->oid); printf("%s\t%s\n", oid, head->name); return 0; } -int use_unnamed(git_repository *repo, const char *url) +static int use_unnamed(git_repository *repo, const char *url) { git_remote *remote = NULL; int error; // Create an instance of a remote from the URL. The transport to use // is detected from the URL - error = git_remote_new(&remote, repo, NULL, url, NULL); + error = git_remote_create_inmemory(&remote, repo, NULL, url); if (error < 0) goto cleanup; // When connecting, the underlying code needs to know wether we // want to push or fetch - error = git_remote_connect(remote, GIT_DIR_FETCH); + error = git_remote_connect(remote, GIT_DIRECTION_FETCH); if (error < 0) goto cleanup; @@ -37,7 +39,7 @@ cleanup: return error; } -int use_remote(git_repository *repo, char *name) +static int use_remote(git_repository *repo, char *name) { git_remote *remote = NULL; int error; @@ -47,7 +49,7 @@ int use_remote(git_repository *repo, char *name) if (error < 0) goto cleanup; - error = git_remote_connect(remote, GIT_DIR_FETCH); + error = git_remote_connect(remote, GIT_DIRECTION_FETCH); if (error < 0) goto cleanup; @@ -63,7 +65,12 @@ cleanup: int ls_remote(git_repository *repo, int argc, char **argv) { - int error, i; + int error; + + if (argc < 2) { + fprintf(stderr, "usage: %s ls-remote <remote>\n", argv[-1]); + return EXIT_FAILURE; + } /* If there's a ':' in the name, assume it's an URL */ if (strchr(argv[1], ':') != NULL) { diff --git a/examples/rev-list.c b/examples/rev-list.c new file mode 100644 index 000000000..d9ec15f76 --- /dev/null +++ b/examples/rev-list.c @@ -0,0 +1,120 @@ +#include <stdio.h> +#include <string.h> + +#include <git2.h> + +static void check_error(int error_code, const char *action) +{ + if (!error_code) + return; + + const git_error *error = giterr_last(); + fprintf(stderr, "Error %d %s: %s\n", -error_code, action, + (error && error->message) ? error->message : "???"); + exit(1); +} + +static int push_commit(git_revwalk *walk, const git_oid *oid, int hide) +{ + if (hide) + return git_revwalk_hide(walk, oid); + else + return git_revwalk_push(walk, oid); +} + +static int push_spec(git_repository *repo, git_revwalk *walk, const char *spec, int hide) +{ + int error; + git_object *obj; + + if ((error = git_revparse_single(&obj, repo, spec)) < 0) + return error; + + error = push_commit(walk, git_object_id(obj), hide); + git_object_free(obj); + return error; +} + +static int push_range(git_repository *repo, git_revwalk *walk, const char *range, int hide) +{ + git_revspec revspec; + int error = 0; + + if ((error = git_revparse(&revspec, repo, range))) + return error; + + if (revspec.flags & GIT_REVPARSE_MERGE_BASE) { + /* TODO: support "<commit>...<commit>" */ + return GIT_EINVALIDSPEC; + } + + if ((error = push_commit(walk, git_object_id(revspec.from), !hide))) + goto out; + + error = push_commit(walk, git_object_id(revspec.to), hide); + +out: + git_object_free(revspec.from); + git_object_free(revspec.to); + return error; +} + +static int revwalk_parseopts(git_repository *repo, git_revwalk *walk, int nopts, char **opts) +{ + int hide, i, error; + unsigned int sorting = GIT_SORT_NONE; + + hide = 0; + for (i = 0; i < nopts; i++) { + if (!strcmp(opts[i], "--topo-order")) { + sorting = GIT_SORT_TOPOLOGICAL | (sorting & GIT_SORT_REVERSE); + git_revwalk_sorting(walk, sorting); + } else if (!strcmp(opts[i], "--date-order")) { + sorting = GIT_SORT_TIME | (sorting & GIT_SORT_REVERSE); + git_revwalk_sorting(walk, sorting); + } else if (!strcmp(opts[i], "--reverse")) { + sorting = (sorting & ~GIT_SORT_REVERSE) + | ((sorting & GIT_SORT_REVERSE) ? 0 : GIT_SORT_REVERSE); + git_revwalk_sorting(walk, sorting); + } else if (!strcmp(opts[i], "--not")) { + hide = !hide; + } else if (opts[i][0] == '^') { + if ((error = push_spec(repo, walk, opts[i] + 1, !hide))) + return error; + } else if (strstr(opts[i], "..")) { + if ((error = push_range(repo, walk, opts[i], hide))) + return error; + } else { + if ((error = push_spec(repo, walk, opts[i], hide))) + return error; + } + } + + return 0; +} + +int main (int argc, char **argv) +{ + int error; + git_repository *repo; + git_revwalk *walk; + git_oid oid; + char buf[41]; + + error = git_repository_open_ext(&repo, ".", 0, NULL); + check_error(error, "opening repository"); + + error = git_revwalk_new(&walk, repo); + check_error(error, "allocating revwalk"); + error = revwalk_parseopts(repo, walk, argc-1, argv+1); + check_error(error, "parsing options"); + + while (!git_revwalk_next(&oid, walk)) { + git_oid_fmt(buf, &oid); + buf[40] = '\0'; + printf("%s\n", buf); + } + + return 0; +} + diff --git a/examples/showindex.c b/examples/showindex.c index 7f2130b90..e92a9c8de 100644 --- a/examples/showindex.c +++ b/examples/showindex.c @@ -1,43 +1,67 @@ #include <git2.h> #include <stdio.h> +#include <string.h> int main (int argc, char** argv) { - git_repository *repo; - git_index *index; - unsigned int i, e, ecount; - git_index_entry **entries; - git_oid oid; - - char out[41]; - out[40] = '\0'; - - git_repository_open(&repo, "/opt/libgit2-test/.git"); - - git_repository_index(&index, repo); - git_index_read(index); - - ecount = git_index_entrycount(index); - for (i = 0; i < ecount; ++i) { - git_index_entry *e = git_index_get(index, i); - - oid = e->oid; - git_oid_fmt(out, &oid); - - printf("File Path: %s\n", e->path); - printf(" Blob SHA: %s\n", out); - printf("File Size: %d\n", (int)e->file_size); - printf(" Device: %d\n", (int)e->dev); - printf(" Inode: %d\n", (int)e->ino); - printf(" UID: %d\n", (int)e->uid); - printf(" GID: %d\n", (int)e->gid); - printf(" ctime: %d\n", (int)e->ctime.seconds); - printf(" mtime: %d\n", (int)e->mtime.seconds); - printf("\n"); - } - - git_index_free(index); - - git_repository_free(repo); + git_repository *repo = NULL; + git_index *index; + unsigned int i, ecount; + char *dir = "."; + size_t dirlen; + char out[41]; + out[40] = '\0'; + + if (argc > 1) + dir = argv[1]; + if (!dir || argc > 2) { + fprintf(stderr, "usage: showindex [<repo-dir>]\n"); + return 1; + } + + dirlen = strlen(dir); + if (dirlen > 5 && strcmp(dir + dirlen - 5, "index") == 0) { + if (git_index_open(&index, dir) < 0) { + fprintf(stderr, "could not open index: %s\n", dir); + return 1; + } + } else { + if (git_repository_open_ext(&repo, dir, 0, NULL) < 0) { + fprintf(stderr, "could not open repository: %s\n", dir); + return 1; + } + if (git_repository_index(&index, repo) < 0) { + fprintf(stderr, "could not open repository index\n"); + return 1; + } + } + + git_index_read(index); + + ecount = git_index_entrycount(index); + if (!ecount) + printf("Empty index\n"); + + for (i = 0; i < ecount; ++i) { + const git_index_entry *e = git_index_get_byindex(index, i); + + git_oid_fmt(out, &e->oid); + + printf("File Path: %s\n", e->path); + printf(" Stage: %d\n", git_index_entry_stage(e)); + printf(" Blob SHA: %s\n", out); + printf("File Mode: %07o\n", e->mode); + printf("File Size: %d bytes\n", (int)e->file_size); + printf("Dev/Inode: %d/%d\n", (int)e->dev, (int)e->ino); + printf(" UID/GID: %d/%d\n", (int)e->uid, (int)e->gid); + printf(" ctime: %d\n", (int)e->ctime.seconds); + printf(" mtime: %d\n", (int)e->mtime.seconds); + printf("\n"); + } + + git_index_free(index); + git_repository_free(repo); + + return 0; } diff --git a/examples/test/test-rev-list.sh b/examples/test/test-rev-list.sh new file mode 100755 index 000000000..bc0eea7cf --- /dev/null +++ b/examples/test/test-rev-list.sh @@ -0,0 +1,95 @@ +#!/bin/bash + +THIS_FILE="$(readlink -f "$0")" +ROOT="$(dirname "$(dirname "$(dirname "$THIS_FILE")")")" +PROGRAM="$ROOT"/examples/rev-list +LIBDIR="$ROOT"/build +REPO="$ROOT"/tests-clar/resources/testrepo.git + +cd "$REPO" + +run () { + LD_LIBRARY_PATH="$LIBDIR" "$PROGRAM" "$@" +} + +diff -u - <(run --date-order a4a7dce) <<EOF +a4a7dce85cf63874e984719f4fdd239f5145052f +c47800c7266a2be04c571c04d5a6614691ea99bd +9fd738e8f7967c078dceed8190330fc8648ee56a +4a202b346bb0fb0db7eff3cffeb3c70babbd2045 +5b5b025afb0b4c913b4c338a42934a3863bf3644 +8496071c1b46c854b31185ea97743be6a8774479 +EOF + +out="$(run --topo-order a4a7dce)" +diff -q - <(echo -n "$out") <<EOF >/dev/null || +a4a7dce85cf63874e984719f4fdd239f5145052f +c47800c7266a2be04c571c04d5a6614691ea99bd +9fd738e8f7967c078dceed8190330fc8648ee56a +4a202b346bb0fb0db7eff3cffeb3c70babbd2045 +5b5b025afb0b4c913b4c338a42934a3863bf3644 +8496071c1b46c854b31185ea97743be6a8774479 +EOF +diff -u - <(echo "$out") <<EOF +a4a7dce85cf63874e984719f4fdd239f5145052f +9fd738e8f7967c078dceed8190330fc8648ee56a +4a202b346bb0fb0db7eff3cffeb3c70babbd2045 +c47800c7266a2be04c571c04d5a6614691ea99bd +5b5b025afb0b4c913b4c338a42934a3863bf3644 +8496071c1b46c854b31185ea97743be6a8774479 +EOF + +diff -u - <(run --date-order --reverse a4a7dce) <<EOF +8496071c1b46c854b31185ea97743be6a8774479 +5b5b025afb0b4c913b4c338a42934a3863bf3644 +4a202b346bb0fb0db7eff3cffeb3c70babbd2045 +9fd738e8f7967c078dceed8190330fc8648ee56a +c47800c7266a2be04c571c04d5a6614691ea99bd +a4a7dce85cf63874e984719f4fdd239f5145052f +EOF + +out=$(run --topo-order --reverse a4a7dce) +diff -q - <(echo -n "$out") <<EOF >/dev/null || +8496071c1b46c854b31185ea97743be6a8774479 +5b5b025afb0b4c913b4c338a42934a3863bf3644 +4a202b346bb0fb0db7eff3cffeb3c70babbd2045 +9fd738e8f7967c078dceed8190330fc8648ee56a +c47800c7266a2be04c571c04d5a6614691ea99bd +a4a7dce85cf63874e984719f4fdd239f5145052f +EOF +diff -u - <(echo "$out") <<EOF +8496071c1b46c854b31185ea97743be6a8774479 +5b5b025afb0b4c913b4c338a42934a3863bf3644 +c47800c7266a2be04c571c04d5a6614691ea99bd +4a202b346bb0fb0db7eff3cffeb3c70babbd2045 +9fd738e8f7967c078dceed8190330fc8648ee56a +a4a7dce85cf63874e984719f4fdd239f5145052f +EOF + +out="$(run --date-order --topo-order --reverse --reverse a4a7dce)" +diff -q - <(echo -n "$out") <<EOF >/dev/null || +a4a7dce85cf63874e984719f4fdd239f5145052f +c47800c7266a2be04c571c04d5a6614691ea99bd +9fd738e8f7967c078dceed8190330fc8648ee56a +4a202b346bb0fb0db7eff3cffeb3c70babbd2045 +5b5b025afb0b4c913b4c338a42934a3863bf3644 +8496071c1b46c854b31185ea97743be6a8774479 +EOF +diff -u - <(echo "$out") <<EOF +a4a7dce85cf63874e984719f4fdd239f5145052f +9fd738e8f7967c078dceed8190330fc8648ee56a +4a202b346bb0fb0db7eff3cffeb3c70babbd2045 +c47800c7266a2be04c571c04d5a6614691ea99bd +5b5b025afb0b4c913b4c338a42934a3863bf3644 +8496071c1b46c854b31185ea97743be6a8774479 +EOF + +diff -u - <(run ^9fd738e~2 9fd738e) <<EOF +9fd738e8f7967c078dceed8190330fc8648ee56a +4a202b346bb0fb0db7eff3cffeb3c70babbd2045 +EOF + +diff -u - <(run --not 9fd738e..9fd738e~2) <<EOF +9fd738e8f7967c078dceed8190330fc8648ee56a +4a202b346bb0fb0db7eff3cffeb3c70babbd2045 +EOF |
