summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVicent Marti <tanoku@gmail.com>2013-04-07 07:22:38 +0200
committerVicent Marti <tanoku@gmail.com>2013-04-07 07:22:38 +0200
commitd9ecaf8c6f2d2d538319174e42e5c98585c902e5 (patch)
treeae3d0bee5061af1bf35fbda34f2afc2757365ee2
parent2274483727b62ae155d7206668713d5346756de6 (diff)
parent2e2332857d26c7dbed3e4b940bb571da348bb5c7 (diff)
downloadlibgit2-d9ecaf8c6f2d2d538319174e42e5c98585c902e5.tar.gz
Merge remote-tracking branch 'gnprice/revwalk' into development
-rw-r--r--examples/.gitignore1
-rw-r--r--examples/rev-list.c116
-rwxr-xr-xexamples/test/test-rev-list.sh95
-rw-r--r--include/git2/revparse.h13
-rw-r--r--include/git2/revwalk.h17
-rw-r--r--src/revparse.c25
-rw-r--r--src/revwalk.c25
-rw-r--r--tests-clar/refs/revparse.c43
-rw-r--r--tests-clar/revwalk/basic.c43
9 files changed, 363 insertions, 15 deletions
diff --git a/examples/.gitignore b/examples/.gitignore
index e40bfc29f..e8e0820a5 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 000000000..b7e466f9e
--- /dev/null
+++ b/examples/rev-list.c
@@ -0,0 +1,116 @@
+#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, 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 "<commit>...<commit>" */
+ 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;
+}
+
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
diff --git a/include/git2/revparse.h b/include/git2/revparse.h
index 6edb7767c..edd8b3cce 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/include/git2/revwalk.h b/include/git2/revwalk.h
index ad57b622e..8bfe0b502 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.
@@ -217,6 +217,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
+ * <commit>..<commit>
+ * where each <commit> 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.
*
* @param walk traversal handle to close. If NULL nothing occurs.
diff --git a/src/revparse.c b/src/revparse.c
index 1518a7c3c..2ba29383e 100644
--- a/src/revparse.c
+++ b/src/revparse.c
@@ -867,3 +867,28 @@ cleanup:
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/src/revwalk.c b/src/revwalk.c
index 02834ab36..c1071843b 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 <regex.h>
@@ -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 "<commit>...<commit>" */
+ 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/refs/revparse.c b/tests-clar/refs/revparse.c
index e0bccf4a9..705014c2a 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")));
@@ -596,3 +623,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);
+}
diff --git a/tests-clar/revwalk/basic.c b/tests-clar/revwalk/basic.c
index 438ec0162..e82776260 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";
@@ -39,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;
@@ -57,22 +60,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);
/*{
@@ -91,6 +89,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;
@@ -189,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));
+}