From 804c5f562736b164148e648d475d95298d6d49a8 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Sun, 3 Mar 2013 20:22:51 -0800 Subject: [PATCH 1/7] Fix puzzling doc comment Signed-off-by: Greg Price --- include/git2/revwalk.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/git2/revwalk.h b/include/git2/revwalk.h index ad57b622ee1..0af80625e31 100644 --- a/include/git2/revwalk.h +++ b/include/git2/revwalk.h @@ -92,7 +92,7 @@ GIT_EXTERN(void) git_revwalk_reset(git_revwalk *walker); * * The given commit will be used as one of the roots * when starting the revision walk. At least one commit - * must be pushed the repository before a walk can + * must be pushed onto the walker before a walk can * be started. * * @param walk the walker being used for the traversal. From 06e6eab0e208966c1152fb13b54eec884e63f2aa Mon Sep 17 00:00:00 2001 From: Greg Price Date: Tue, 19 Mar 2013 12:02:19 -0700 Subject: [PATCH 2/7] revwalk tests: better diagram of example repo The purported command output was already inaccurate, as the refs aren't where it shows. In any event, the labels a reader of this file really needs are the indices used in commit_sorting_*, to make it possible to understand them by referring directly from those arrays to the diagram rather than from the index arrays, to commit_ids, to the diagram. Add those. Signed-off-by: Greg Price --- tests-clar/revwalk/basic.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests-clar/revwalk/basic.c b/tests-clar/revwalk/basic.c index 438ec01627d..de529a9e200 100644 --- a/tests-clar/revwalk/basic.c +++ b/tests-clar/revwalk/basic.c @@ -1,15 +1,14 @@ #include "clar_libgit2.h" /* - $ git log --oneline --graph --decorate - * a4a7dce (HEAD, br2) Merge branch 'master' into br2 + * a4a7dce [0] Merge branch 'master' into br2 |\ - | * 9fd738e (master) a fourth commit - | * 4a202b3 a third commit - * | c47800c branch commit one + | * 9fd738e [1] a fourth commit + | * 4a202b3 [2] a third commit + * | c47800c [3] branch commit one |/ - * 5b5b025 another commit - * 8496071 testing + * 5b5b025 [5] another commit + * 8496071 [4] testing */ static const char *commit_head = "a4a7dce85cf63874e984719f4fdd239f5145052f"; From 2932c8826a1d948565124aa6c9a32df68a15895b Mon Sep 17 00:00:00 2001 From: Greg Price Date: Mon, 4 Mar 2013 02:17:04 -0800 Subject: [PATCH 3/7] revwalk: refactor tests a bit Signed-off-by: Greg Price --- tests-clar/revwalk/basic.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/tests-clar/revwalk/basic.c b/tests-clar/revwalk/basic.c index de529a9e200..2f1f817c93b 100644 --- a/tests-clar/revwalk/basic.c +++ b/tests-clar/revwalk/basic.c @@ -56,22 +56,17 @@ static int get_commit_index(git_oid *raw_oid) return -1; } -static int test_walk(git_revwalk *walk, const git_oid *root, - int flags, const int possible_results[][6], int results_count) +static int test_walk_only(git_revwalk *walk, + const int possible_results[][commit_count], int results_count) { git_oid oid; - int i; int result_array[commit_count]; - git_revwalk_sorting(walk, flags); - git_revwalk_push(walk, root); - for (i = 0; i < commit_count; ++i) result_array[i] = -1; i = 0; - while (git_revwalk_next(&oid, walk) == 0) { result_array[i++] = get_commit_index(&oid); /*{ @@ -90,6 +85,15 @@ static int test_walk(git_revwalk *walk, const git_oid *root, return GIT_ERROR; } +static int test_walk(git_revwalk *walk, const git_oid *root, + int flags, const int possible_results[][6], int results_count) +{ + git_revwalk_sorting(walk, flags); + git_revwalk_push(walk, root); + + return test_walk_only(walk, possible_results, results_count); +} + static git_repository *_repo; static git_revwalk *_walk; From b208d9002289dcd8170750cb94c84678afdd6e0c Mon Sep 17 00:00:00 2001 From: Greg Price Date: Wed, 20 Mar 2013 10:01:58 -0700 Subject: [PATCH 4/7] revparse: Parse range-like syntax Signed-off-by: Greg Price --- include/git2/revparse.h | 13 ++++++++++++ src/revparse.c | 25 ++++++++++++++++++++++ tests-clar/refs/revparse.c | 43 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) diff --git a/include/git2/revparse.h b/include/git2/revparse.h index 6edb7767c9a..edd8b3cce82 100644 --- a/include/git2/revparse.h +++ b/include/git2/revparse.h @@ -32,6 +32,19 @@ GIT_BEGIN_DECL */ GIT_EXTERN(int) git_revparse_single(git_object **out, git_repository *repo, const char *spec); +/** + * Parse a string with the form of a revision range, as accepted by + * `git rev-list`, `git diff`, and others. + * + * @param left (output) the left-hand commit + * @param right (output) the right-hand commit + * @param threedots (output) 0 if the endpoints are separated by two dots, 1 if by three + * @param repo the repository to find the commits in + * @param rangelike the rangelike string to be parsed + * @return 0 on success, or any error `git_revparse_single` can return + */ +GIT_EXTERN(int) git_revparse_rangelike(git_object **left, git_object **right, int *threedots, git_repository *repo, const char *rangelike); + /** @} */ GIT_END_DECL #endif diff --git a/src/revparse.c b/src/revparse.c index 8848799751f..7f149713063 100644 --- a/src/revparse.c +++ b/src/revparse.c @@ -866,3 +866,28 @@ int git_revparse_single(git_object **out, git_repository *repo, const char *spec git_buf_free(&buf); return error; } + +int git_revparse_rangelike(git_object **left, git_object **right, int *threedots, git_repository *repo, const char *rangelike) +{ + int error = 0; + const char *p, *q; + char *revspec; + + p = strstr(rangelike, ".."); + if (!p) { + giterr_set(GITERR_INVALID, "Malformed range (or rangelike syntax): %s", rangelike); + return GIT_EINVALIDSPEC; + } else if (p[2] == '.') { + *threedots = 1; + q = p + 3; + } else { + *threedots = 0; + q = p + 2; + } + + revspec = git__substrdup(rangelike, p - rangelike); + error = (git_revparse_single(left, repo, revspec) + || git_revparse_single(right, repo, q)); + git__free(revspec); + return error; +} diff --git a/tests-clar/refs/revparse.c b/tests-clar/refs/revparse.c index be92c19564c..b8d7d5edc1b 100644 --- a/tests-clar/refs/revparse.c +++ b/tests-clar/refs/revparse.c @@ -32,6 +32,33 @@ static void test_object(const char *spec, const char *expected_oid) test_object_inrepo(spec, expected_oid, g_repo); } +static void test_rangelike(const char *rangelike, + const char *expected_left, + const char *expected_right, + int expected_threedots) +{ + char objstr[64] = {0}; + git_object *left, *right; + int threedots; + int error; + + error = git_revparse_rangelike(&left, &right, &threedots, g_repo, rangelike); + + if (expected_left != NULL) { + cl_assert_equal_i(0, error); + cl_assert_equal_i(threedots, expected_threedots); + git_oid_fmt(objstr, git_object_id(left)); + cl_assert_equal_s(objstr, expected_left); + git_oid_fmt(objstr, git_object_id(right)); + cl_assert_equal_s(objstr, expected_right); + } else + cl_assert(error != 0); + + git_object_free(left); + git_object_free(right); +} + + void test_refs_revparse__initialize(void) { cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git"))); @@ -595,3 +622,19 @@ void test_refs_revparse__try_to_retrieve_branch_before_abbrev_sha(void) git_object_free(target); cl_git_sandbox_cleanup(); } + + +void test_refs_revparse__range(void) +{ + test_rangelike("be3563a^1..be3563a", + "9fd738e8f7967c078dceed8190330fc8648ee56a", + "be3563ae3f795b2b4353bcce3a527ad0a4f7f644", + 0); + + test_rangelike("be3563a^1...be3563a", + "9fd738e8f7967c078dceed8190330fc8648ee56a", + "be3563ae3f795b2b4353bcce3a527ad0a4f7f644", + 1); + + test_rangelike("be3563a^1.be3563a", NULL, NULL, 0); +} From af079d8bf69a4bd92d6a4eff3c3d1e4d73190a78 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Sun, 3 Mar 2013 20:54:23 -0800 Subject: [PATCH 5/7] revwalk: Parse revision ranges All the hard work is already in revparse. Signed-off-by: Greg Price --- include/git2/revwalk.h | 15 +++++++++++++++ src/revwalk.c | 25 +++++++++++++++++++++++++ tests-clar/revwalk/basic.c | 12 ++++++++++++ 3 files changed, 52 insertions(+) diff --git a/include/git2/revwalk.h b/include/git2/revwalk.h index 0af80625e31..8bfe0b502da 100644 --- a/include/git2/revwalk.h +++ b/include/git2/revwalk.h @@ -216,6 +216,21 @@ GIT_EXTERN(int) git_revwalk_next(git_oid *out, git_revwalk *walk); */ GIT_EXTERN(void) git_revwalk_sorting(git_revwalk *walk, unsigned int sort_mode); +/** + * Push and hide the respective endpoints of the given range. + * + * The range should be of the form + * .. + * where each is in the form accepted by 'git_revparse_single'. + * The left-hand commit will be hidden and the right-hand commit pushed. + * + * @param walk the walker being used for the traversal + * @param range the range + * @return 0 or an error code + * + */ +GIT_EXTERN(int) git_revwalk_push_range(git_revwalk *walk, const char *range); + /** * Free a revision walker previously allocated. * diff --git a/src/revwalk.c b/src/revwalk.c index 02834ab3659..c1071843b2f 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -11,6 +11,7 @@ #include "pool.h" #include "revwalk.h" +#include "git2/revparse.h" #include "merge.h" #include @@ -228,6 +229,30 @@ int git_revwalk_push_ref(git_revwalk *walk, const char *refname) return push_ref(walk, refname, 0); } +int git_revwalk_push_range(git_revwalk *walk, const char *range) +{ + git_object *left, *right; + int threedots; + int error = 0; + + if ((error = git_revparse_rangelike(&left, &right, &threedots, walk->repo, range))) + return error; + if (threedots) { + /* TODO: support "..." */ + giterr_set(GITERR_INVALID, "Symmetric differences not implemented in revwalk"); + return GIT_EINVALIDSPEC; + } + + if ((error = push_commit(walk, git_object_id(left), 1))) + goto out; + error = push_commit(walk, git_object_id(right), 0); + + out: + git_object_free(left); + git_object_free(right); + return error; +} + int git_revwalk_hide_ref(git_revwalk *walk, const char *refname) { assert(walk && refname); diff --git a/tests-clar/revwalk/basic.c b/tests-clar/revwalk/basic.c index 2f1f817c93b..e8277626059 100644 --- a/tests-clar/revwalk/basic.c +++ b/tests-clar/revwalk/basic.c @@ -38,6 +38,10 @@ static const int commit_sorting_time_reverse[][6] = { {4, 5, 2, 1, 3, 0} }; +static const int commit_sorting_segment[][6] = { + {1, 2, -1, -1, -1, -1} +}; + #define commit_count 6 static const int result_bytes = 24; @@ -192,3 +196,11 @@ void test_revwalk_basic__disallow_non_commit(void) cl_git_pass(git_oid_fromstr(&oid, "521d87c1ec3aef9824daf6d96cc0ae3710766d91")); cl_git_fail(git_revwalk_push(_walk, &oid)); } + +void test_revwalk_basic__push_range(void) +{ + git_revwalk_reset(_walk); + git_revwalk_sorting(_walk, 0); + cl_git_pass(git_revwalk_push_range(_walk, "9fd738e~2..9fd738e")); + cl_git_pass(test_walk_only(_walk, commit_sorting_segment, 1)); +} From 8f7f5e55436eb6b20c12c435eab03e9f37be2e39 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Sun, 31 Mar 2013 14:56:32 -0700 Subject: [PATCH 6/7] examples: rev-list This demonstrates parts of the interface for specifying revisions that Git users are familiar with from 'git rev-list', 'git log', and other Git commands. A similar query interface is used in out-of-core command-line programs that browse a Git repo (like 'tig'), and may be useful for an 'advanced search' interface in GUI or web applications. In this version, we parse all the query modifiers we can support with the existing logic in revwalk: basic include/exclude commits, and the ordering flags. More logic will be required to support '--grep', '--author', the pickaxe '-S', etc. Signed-off-by: Greg Price --- examples/.gitignore | 1 + examples/rev-list.c | 116 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 examples/rev-list.c diff --git a/examples/.gitignore b/examples/.gitignore index e40bfc29f28..e8e0820a5bb 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -1,4 +1,5 @@ general showindex diff +rev-list *.dSYM diff --git a/examples/rev-list.c b/examples/rev-list.c new file mode 100644 index 00000000000..b7e466f9eaf --- /dev/null +++ b/examples/rev-list.c @@ -0,0 +1,116 @@ +#include +#include + +#include + +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, git_object *obj, int hide) +{ + if (hide) + return git_revwalk_hide(walk, git_object_id(obj)); + else + return git_revwalk_push(walk, git_object_id(obj)); +} + +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))) + return error; + return push_commit(walk, obj, hide); +} + +static int push_range(git_repository *repo, git_revwalk *walk, const char *range, int hide) +{ + git_object *left, *right; + int threedots; + int error = 0; + + if ((error = git_revparse_rangelike(&left, &right, &threedots, repo, range))) + return error; + if (threedots) { + /* TODO: support "..." */ + return GIT_EINVALIDSPEC; + } + + if ((error = push_commit(walk, left, !hide))) + goto out; + error = push_commit(walk, right, hide); + + out: + git_object_free(left); + git_object_free(right); + return error; +} + +static int revwalk_parseopts(git_repository *repo, git_revwalk *walk, int nopts, const char *const *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; +} + From 2e2332857d26c7dbed3e4b940bb571da348bb5c7 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Wed, 20 Mar 2013 09:39:20 -0700 Subject: [PATCH 7/7] examples: a test, for rev-list This test file could probably be improved by a framework like the one in git.git:t/, or by using a language like Python instead of shell. The other examples would benefit from tests too. Probably best to settle on a framework to write them in, then add more tests. Signed-off-by: Greg Price --- examples/test/test-rev-list.sh | 95 ++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100755 examples/test/test-rev-list.sh diff --git a/examples/test/test-rev-list.sh b/examples/test/test-rev-list.sh new file mode 100755 index 00000000000..bc0eea7cf46 --- /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) </dev/null || +a4a7dce85cf63874e984719f4fdd239f5145052f +c47800c7266a2be04c571c04d5a6614691ea99bd +9fd738e8f7967c078dceed8190330fc8648ee56a +4a202b346bb0fb0db7eff3cffeb3c70babbd2045 +5b5b025afb0b4c913b4c338a42934a3863bf3644 +8496071c1b46c854b31185ea97743be6a8774479 +EOF +diff -u - <(echo "$out") </dev/null || +8496071c1b46c854b31185ea97743be6a8774479 +5b5b025afb0b4c913b4c338a42934a3863bf3644 +4a202b346bb0fb0db7eff3cffeb3c70babbd2045 +9fd738e8f7967c078dceed8190330fc8648ee56a +c47800c7266a2be04c571c04d5a6614691ea99bd +a4a7dce85cf63874e984719f4fdd239f5145052f +EOF +diff -u - <(echo "$out") </dev/null || +a4a7dce85cf63874e984719f4fdd239f5145052f +c47800c7266a2be04c571c04d5a6614691ea99bd +9fd738e8f7967c078dceed8190330fc8648ee56a +4a202b346bb0fb0db7eff3cffeb3c70babbd2045 +5b5b025afb0b4c913b4c338a42934a3863bf3644 +8496071c1b46c854b31185ea97743be6a8774479 +EOF +diff -u - <(echo "$out") <