Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Parse git rev-list-style options #1393

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Apr 7, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/.gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
general
showindex
diff
rev-list
*.dSYM
116 changes: 116 additions & 0 deletions examples/rev-list.c
Original file line number Diff line number Diff line change
@@ -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;
}

95 changes: 95 additions & 0 deletions examples/test/test-rev-list.sh
Original file line number Diff line number Diff line change
@@ -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
13 changes: 13 additions & 0 deletions include/git2/revparse.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
17 changes: 16 additions & 1 deletion include/git2/revwalk.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
* <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.
*
Expand Down
25 changes: 25 additions & 0 deletions src/revparse.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
25 changes: 25 additions & 0 deletions src/revwalk.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "pool.h"

#include "revwalk.h"
#include "git2/revparse.h"
#include "merge.h"

#include <regex.h>
Expand Down Expand Up @@ -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;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would it take to support this? Is it just a matter of pushing both sides of the range spec, and hiding the parent of their merge base?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You hide the merge base, not its parents, but yes. The revwalk has internal functions to calculate what the merge base of the pushed commits is, but maybe we should expose a function to let you hide the merge base from the public API.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, just pushing both sides and hiding the merge bases -- all of them if there's more than one. From the rev-list docs:

Another special notation is "'<commit1>'...'<commit2>'" which is useful
for merges.  The resulting set of commits is the symmetric difference
between the two operands.  The following two commands are equivalent:

-----------------------------------------------------------------------
        $ git rev-list A B --not $(git merge-base --all A B)
        $ git rev-list A...B
-----------------------------------------------------------------------


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);
Expand Down
43 changes: 43 additions & 0 deletions tests-clar/refs/revparse.c
Original file line number Diff line number Diff line change
Expand Up @@ -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")));
Expand Down Expand Up @@ -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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😍

}
Loading