From 58206c9ae79af8ef675e30087e5430065b078bbb Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 16 May 2013 10:38:27 -0700 Subject: [PATCH 1/2] Add cat-file example and increase const use in API This adds an example implementation that emulates git cat-file. It is a convenient and relatively simple example of getting data out of a repository. Implementing this also revealed that there are a number of APIs that are still not using const pointers to objects that really ought to be. The main cause of this is that `git_vector_bsearch` may need to call `git_vector_sort` before doing the search, so a const pointer to the vector is not allowed. However, for tree objects, with a little care, we can ensure that the vector of tree entries is always sorted and allow lookups to take a const pointer. Also, the missing const in commit objects just looks like an oversight. --- examples/Makefile | 2 +- examples/cat-file.c | 229 ++++++++++++++++++++++++++++++++++++++++++ include/git2/commit.h | 9 +- include/git2/tree.h | 6 +- src/commit.c | 6 +- src/tree.c | 23 +++-- src/tree.h | 2 +- 7 files changed, 260 insertions(+), 17 deletions(-) create mode 100644 examples/cat-file.c diff --git a/examples/Makefile b/examples/Makefile index 2c18731fd..c5d555566 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -3,7 +3,7 @@ CC = gcc CFLAGS = -g -I../include -I../src -Wall -Wextra -Wmissing-prototypes -Wno-missing-field-initializers LFLAGS = -L../build -lgit2 -lz -APPS = general showindex diff rev-list +APPS = general showindex diff rev-list cat-file all: $(APPS) diff --git a/examples/cat-file.c b/examples/cat-file.c new file mode 100644 index 000000000..ebb6cb0ca --- /dev/null +++ b/examples/cat-file.c @@ -0,0 +1,229 @@ +#include +#include +#include +#include + +static git_repository *g_repo; + +static void check(int error, const char *message) +{ + if (error) { + fprintf(stderr, "%s (%d)\n", message, error); + exit(1); + } +} + +static void usage(const char *message, const char *arg) +{ + if (message && arg) + fprintf(stderr, "%s: %s\n", message, arg); + else if (message) + fprintf(stderr, "%s\n", message); + fprintf(stderr, "usage: cat-file (-t | -s | -e | -p) [] \n"); + exit(1); +} + +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 = (const char *)(arg + len); + return 1; +} + +static void print_signature(const char *header, const git_signature *sig) +{ + char sign; + int offset, hours, minutes; + + if (!sig) + return; + + offset = sig->when.offset; + if (offset < 0) { + sign = '-'; + offset = -offset; + } else { + sign = '+'; + } + + hours = offset / 60; + minutes = offset % 60; + + printf("%s %s <%s> %ld %c%02d%02d\n", + header, sig->name, sig->email, (long)sig->when.time, + sign, hours, minutes); +} + +static void show_blob(const git_blob *blob) +{ + /* ? Does this need crlf filtering? */ + fwrite(git_blob_rawcontent(blob), git_blob_rawsize(blob), 1, stdout); +} + +static void show_tree(const git_tree *tree) +{ + size_t i, max_i = (int)git_tree_entrycount(tree); + char oidstr[GIT_OID_HEXSZ + 1]; + const git_tree_entry *te; + + for (i = 0; i < max_i; ++i) { + te = git_tree_entry_byindex(tree, i); + + git_oid_tostr(oidstr, sizeof(oidstr), git_tree_entry_id(te)); + + printf("%06o %s %s\t%s\n", + git_tree_entry_filemode(te), + git_object_type2string(git_tree_entry_type(te)), + oidstr, git_tree_entry_name(te)); + } +} + +static void show_commit(const git_commit *commit) +{ + unsigned int i, max_i; + char oidstr[GIT_OID_HEXSZ + 1]; + + git_oid_tostr(oidstr, sizeof(oidstr), git_commit_tree_id(commit)); + printf("tree %s\n", oidstr); + + max_i = (unsigned int)git_commit_parentcount(commit); + for (i = 0; i < max_i; ++i) { + git_oid_tostr(oidstr, sizeof(oidstr), git_commit_parent_id(commit, i)); + printf("parent %s\n", oidstr); + } + + print_signature("author", git_commit_author(commit)); + print_signature("committer", git_commit_committer(commit)); + + if (git_commit_message(commit)) + printf("\n%s\n", git_commit_message(commit)); +} + +static void show_tag(const git_tag *tag) +{ + char oidstr[GIT_OID_HEXSZ + 1]; + + git_oid_tostr(oidstr, sizeof(oidstr), git_tag_target_id(tag));; + printf("object %s\n", oidstr); + printf("type %s\n", git_object_type2string(git_tag_target_type(tag))); + printf("tag %s\n", git_tag_name(tag)); + print_signature("tagger", git_tag_tagger(tag)); + + if (git_tag_message(tag)) + printf("\n%s\n", git_tag_message(tag)); +} + +enum { + SHOW_TYPE = 1, + SHOW_SIZE = 2, + SHOW_NONE = 3, + SHOW_PRETTY = 4 +}; + +int main(int argc, char *argv[]) +{ + const char *dir = ".", *rev = NULL; + int i, action = 0, verbose = 0; + git_object *obj = NULL; + char oidstr[GIT_OID_HEXSZ + 1]; + + git_threads_init(); + + for (i = 1; i < argc; ++i) { + char *a = argv[i]; + + if (a[0] != '-') { + if (rev != NULL) + usage("Only one rev should be provided", NULL); + else + rev = a; + } + else if (!strcmp(a, "-t")) + action = SHOW_TYPE; + else if (!strcmp(a, "-s")) + action = SHOW_SIZE; + else if (!strcmp(a, "-e")) + action = SHOW_NONE; + else if (!strcmp(a, "-p")) + action = SHOW_PRETTY; + else if (!strcmp(a, "-q")) + verbose = 0; + else if (!strcmp(a, "-v")) + verbose = 1; + else if (!strcmp(a, "--help") || !strcmp(a, "-h")) + usage(NULL, NULL); + else if (!check_str_param(a, "--git-dir=", &dir)) + usage("Unknown option", a); + } + + if (!action || !rev) + usage(NULL, NULL); + + check(git_repository_open_ext(&g_repo, dir, 0, NULL), + "Could not open repository"); + + if (git_revparse_single(&obj, g_repo, rev) < 0) { + fprintf(stderr, "Could not resolve '%s'\n", rev); + exit(1); + } + if (verbose) { + char oidstr[GIT_OID_HEXSZ + 1]; + git_oid_tostr(oidstr, sizeof(oidstr), git_object_id(obj)); + + printf("%s %s\n--\n", + git_object_type2string(git_object_type(obj)), oidstr); + } + + switch (action) { + case SHOW_TYPE: + printf("%s\n", git_object_type2string(git_object_type(obj))); + break; + case SHOW_SIZE: { + git_odb *odb; + git_odb_object *odbobj; + + check(git_repository_odb(&odb, g_repo), "Could not open ODB"); + check(git_odb_read(&odbobj, odb, git_object_id(obj)), + "Could not find obj"); + + printf("%ld\n", (long)git_odb_object_size(odbobj)); + + git_odb_object_free(odbobj); + git_odb_free(odb); + } + break; + case SHOW_NONE: + /* just want return result */ + break; + case SHOW_PRETTY: + + switch (git_object_type(obj)) { + case GIT_OBJ_BLOB: + show_blob((const git_blob *)obj); + break; + case GIT_OBJ_COMMIT: + show_commit((const git_commit *)obj); + break; + case GIT_OBJ_TREE: + show_tree((const git_tree *)obj); + break; + case GIT_OBJ_TAG: + show_tag((const git_tag *)obj); + break; + default: + printf("unknown %s\n", oidstr); + break; + } + break; + } + + git_object_free(obj); + git_repository_free(g_repo); + + git_threads_shutdown(); + + return 0; +} diff --git a/include/git2/commit.h b/include/git2/commit.h index a420ba635..20b345f84 100644 --- a/include/git2/commit.h +++ b/include/git2/commit.h @@ -164,7 +164,10 @@ GIT_EXTERN(unsigned int) git_commit_parentcount(const git_commit *commit); * @param n the position of the parent (from 0 to `parentcount`) * @return 0 or an error code */ -GIT_EXTERN(int) git_commit_parent(git_commit **out, git_commit *commit, unsigned int n); +GIT_EXTERN(int) git_commit_parent( + git_commit **out, + const git_commit *commit, + unsigned int n); /** * Get the oid of a specified parent for a commit. This is different from @@ -175,7 +178,9 @@ GIT_EXTERN(int) git_commit_parent(git_commit **out, git_commit *commit, unsigned * @param n the position of the parent (from 0 to `parentcount`) * @return the id of the parent, NULL on error. */ -GIT_EXTERN(const git_oid *) git_commit_parent_id(git_commit *commit, unsigned int n); +GIT_EXTERN(const git_oid *) git_commit_parent_id( + const git_commit *commit, + unsigned int n); /** * Get the commit object that is the th generation ancestor diff --git a/include/git2/tree.h b/include/git2/tree.h index 6ad722048..d673f50c4 100644 --- a/include/git2/tree.h +++ b/include/git2/tree.h @@ -97,7 +97,7 @@ GIT_EXTERN(size_t) git_tree_entrycount(const git_tree *tree); * @return the tree entry; NULL if not found */ GIT_EXTERN(const git_tree_entry *) git_tree_entry_byname( - git_tree *tree, const char *filename); + const git_tree *tree, const char *filename); /** * Lookup a tree entry by its position in the tree @@ -110,7 +110,7 @@ GIT_EXTERN(const git_tree_entry *) git_tree_entry_byname( * @return the tree entry; NULL if not found */ GIT_EXTERN(const git_tree_entry *) git_tree_entry_byindex( - git_tree *tree, size_t idx); + const git_tree *tree, size_t idx); /** * Lookup a tree entry by SHA value. @@ -141,7 +141,7 @@ GIT_EXTERN(const git_tree_entry *) git_tree_entry_byoid( */ GIT_EXTERN(int) git_tree_entry_bypath( git_tree_entry **out, - git_tree *root, + const git_tree *root, const char *path); /** diff --git a/src/commit.c b/src/commit.c index be6e32c76..1ab9b34f7 100644 --- a/src/commit.c +++ b/src/commit.c @@ -266,14 +266,16 @@ int git_commit_tree(git_tree **tree_out, const git_commit *commit) return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_id); } -const git_oid *git_commit_parent_id(git_commit *commit, unsigned int n) +const git_oid *git_commit_parent_id( + const git_commit *commit, unsigned int n) { assert(commit); return git_vector_get(&commit->parent_ids, n); } -int git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n) +int git_commit_parent( + git_commit **parent, const git_commit *commit, unsigned int n) { const git_oid *parent_id; assert(commit); diff --git a/src/tree.c b/src/tree.c index a48b322d4..10d131438 100644 --- a/src/tree.c +++ b/src/tree.c @@ -271,25 +271,27 @@ int git_tree_entry_to_object( } static const git_tree_entry *entry_fromname( - git_tree *tree, const char *name, size_t name_len) + const git_tree *tree, const char *name, size_t name_len) { size_t idx; - if (tree_key_search(&idx, &tree->entries, name, name_len) < 0) + assert(tree->entries.sorted); /* be safe when we cast away constness */ + + if (tree_key_search(&idx, (git_vector *)&tree->entries, name, name_len) < 0) return NULL; return git_vector_get(&tree->entries, idx); } const git_tree_entry *git_tree_entry_byname( - git_tree *tree, const char *filename) + const git_tree *tree, const char *filename) { assert(tree && filename); return entry_fromname(tree, filename, strlen(filename)); } const git_tree_entry *git_tree_entry_byindex( - git_tree *tree, size_t idx) + const git_tree *tree, size_t idx) { assert(tree); return git_vector_get(&tree->entries, idx); @@ -311,9 +313,9 @@ const git_tree_entry *git_tree_entry_byoid( return NULL; } -int git_tree__prefix_position(git_tree *tree, const char *path) +int git_tree__prefix_position(const git_tree *tree, const char *path) { - git_vector *entries = &tree->entries; + const git_vector *entries = &tree->entries; struct tree_key_search ksearch; size_t at_pos; @@ -323,8 +325,11 @@ int git_tree__prefix_position(git_tree *tree, const char *path) ksearch.filename = path; ksearch.filename_len = strlen(path); + assert(tree->entries.sorted); /* be safe when we cast away constness */ + /* Find tree entry with appropriate prefix */ - git_vector_bsearch2(&at_pos, entries, &homing_search_cmp, &ksearch); + git_vector_bsearch2( + &at_pos, (git_vector *)entries, &homing_search_cmp, &ksearch); for (; at_pos < entries->length; ++at_pos) { const git_tree_entry *entry = entries->contents[at_pos]; @@ -408,6 +413,8 @@ int git_tree__parse(void *_tree, git_odb_object *odb_obj) buffer += GIT_OID_RAWSZ; } + git_vector_sort(&tree->entries); + return 0; } @@ -796,7 +803,7 @@ static size_t subpath_len(const char *path) int git_tree_entry_bypath( git_tree_entry **entry_out, - git_tree *root, + const git_tree *root, const char *path) { int error = 0; diff --git a/src/tree.h b/src/tree.h index 7cb2dd36c..f07039a07 100644 --- a/src/tree.h +++ b/src/tree.h @@ -47,7 +47,7 @@ int git_tree__parse(void *tree, git_odb_object *obj); * @param prefix the beginning of a path to find in the tree. * @return index of the first item at or after the given prefix. */ -int git_tree__prefix_position(git_tree *tree, const char *prefix); +int git_tree__prefix_position(const git_tree *tree, const char *prefix); /** From 8d78400142bc001e18e0b0687290d6b446e05130 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 16 May 2013 10:43:10 -0700 Subject: [PATCH 2/2] Make examples/diff.c compile vs threadsafe library --- examples/diff.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/examples/diff.c b/examples/diff.c index 2ef405665..bb4f0ec21 100644 --- a/examples/diff.c +++ b/examples/diff.c @@ -117,7 +117,10 @@ int main(int argc, char *argv[]) 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; + char *a, *treeish1 = NULL, *treeish2 = NULL; + const char *dir = "."; + + git_threads_init(); /* parse arguments as copied from git-diff */ @@ -162,7 +165,8 @@ int main(int argc, char *argv[]) !check_uint16_param(a, "--inter-hunk-context=", &opts.interhunk_lines) && !check_str_param(a, "--src-prefix=", &opts.old_prefix) && - !check_str_param(a, "--dst-prefix=", &opts.new_prefix)) + !check_str_param(a, "--dst-prefix=", &opts.new_prefix) && + !check_str_param(a, "--git-dir=", &dir)) usage("Unknown arg", a); } @@ -216,6 +220,8 @@ int main(int argc, char *argv[]) git_tree_free(t2); git_repository_free(repo); + git_threads_shutdown(); + return 0; }