summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/revparse.c108
-rw-r--r--tests-clar/refs/revparse.c26
-rw-r--r--tests/resources/testrepo.git/logs/HEAD5
-rw-r--r--tests/resources/testrepo.git/logs/refs/heads/br22
-rw-r--r--tests/resources/testrepo.git/logs/refs/heads/master2
-rw-r--r--tests/resources/testrepo.git/logs/refs/remotes/origin/HEAD1
6 files changed, 127 insertions, 17 deletions
diff --git a/src/revparse.c b/src/revparse.c
index 7a07e0c38..61a9abc34 100644
--- a/src/revparse.c
+++ b/src/revparse.c
@@ -16,6 +16,8 @@
#include "git2/refs.h"
#include "git2/tag.h"
#include "git2/commit.h"
+#include "git2/reflog.h"
+#include "git2/refs.h"
GIT_BEGIN_DECL
@@ -111,20 +113,97 @@ static int revparse_lookup_object(git_object **out, git_repository *repo, const
}
-static int walk_ref_history(git_object **out, const char *refspec, const char *reflogspec)
+static int walk_ref_history(git_object **out, git_repository *repo, const char *refspec, const char *reflogspec)
{
- /* TODO */
+ git_reference *ref;
+ git_reflog *reflog = NULL;
+ int n, retcode = GIT_ERROR;
+ size_t i, refloglen;
+ const git_reflog_entry *entry;
+ git_buf buf = GIT_BUF_INIT;
+
+ if (git__prefixcmp(reflogspec, "@{") != 0 ||
+ git__suffixcmp(reflogspec, "}") != 0) {
+ giterr_set(GITERR_INVALID, "Bad reflogspec '%s'", reflogspec);
+ return GIT_ERROR;
+ }
- /* Empty refspec means current branch */
+ /* "@{-N}" form means walk back N checkouts. That means the HEAD log. */
+ if (strlen(refspec) == 0 && !git__prefixcmp(reflogspec, "@{-")) {
+ if (git__strtol32(&n, reflogspec+3, NULL, 0) < 0 ||
+ n < 1) {
+ giterr_set(GITERR_INVALID, "Invalid reflogspec %s", reflogspec);
+ return GIT_ERROR;
+ }
+ git_reference_lookup(&ref, repo, "HEAD");
+ git_reflog_read(&reflog, ref);
+ git_reference_free(ref);
- /* Possible syntaxes for reflogspec:
- * "8" -> 8th prior value for the ref
- * "-2" -> nth branch checked out before the current one (refspec must be empty)
- * "yesterday", "1 month 2 weeks 3 days 4 hours 5 seconds ago", "1979-02-26 18:30:00"
- * -> value of ref at given point in time
- * "upstream" or "u" -> branch the ref is set to build on top of
- * */
- return 0;
+ refloglen = git_reflog_entrycount(reflog);
+ for (i=0; i < refloglen; i++) {
+ const char *msg;
+ entry = git_reflog_entry_byindex(reflog, i);
+
+ msg = git_reflog_entry_msg(entry);
+ if (!git__prefixcmp(msg, "checkout: moving")) {
+ n--;
+ if (!n) {
+ char *branchname = strrchr(msg, ' ') + 1;
+ git_buf_printf(&buf, "refs/heads/%s", branchname);
+ retcode = revparse_lookup_fully_qualifed_ref(out, repo, git_buf_cstr(&buf));
+ break;
+ }
+ }
+ }
+ } else {
+ if (!strlen(refspec)) {
+ /* Empty refspec means current branch */
+ /* Get the target of HEAD */
+ git_reference_lookup(&ref, repo, "HEAD");
+ git_buf_puts(&buf, git_reference_target(ref));
+ git_reference_free(ref);
+
+ /* Get the reflog for that ref */
+ git_reference_lookup(&ref, repo, git_buf_cstr(&buf));
+ git_reflog_read(&reflog, ref);
+ git_reference_free(ref);
+ } else {
+ if (git__prefixcmp(refspec, "refs/heads/") != 0) {
+ git_buf_printf(&buf, "refs/heads/%s", refspec);
+ } else {
+ git_buf_puts(&buf, refspec);
+ }
+ }
+
+ /* @{u} or @{upstream} -> upstream branch, for a tracking branch. This is stored in the config. */
+ if (!strcmp(reflogspec, "@{u}") || !strcmp(reflogspec, "@{upstream}")) {
+ /* TODO */
+ }
+
+ /* @{N} -> Nth prior value for the ref (from reflog) */
+ else if (!git__strtol32(&n, reflogspec+2, NULL, 0)) {
+ if (n == 0) {
+ retcode = revparse_lookup_fully_qualifed_ref(out, repo, git_buf_cstr(&buf));
+ } else if (!git_reference_lookup(&ref, repo, git_buf_cstr(&buf))) {
+ if (!git_reflog_read(&reflog, ref)) {
+ const git_reflog_entry *entry = git_reflog_entry_byindex(reflog, n);
+ const git_oid *oid = git_reflog_entry_oidold(entry);
+ retcode = git_object_lookup(out, repo, oid, GIT_OBJ_ANY);
+ }
+ git_reference_free(ref);
+ }
+ }
+
+ /* @{Anything else} -> try to parse the expression into a date, and get the value of the ref as it
+ was then. */
+ else {
+ /* TODO */
+ }
+ }
+
+ if (reflog) git_reflog_free(reflog);
+ git_buf_free(&buf);
+ return retcode;
}
static git_object* dereference_object(git_object *obj)
@@ -322,7 +401,7 @@ int git_revparse_single(git_object **out, git_repository *repo, const char *spec
} else if (*spec_cur == '@') {
/* '@' syntax doesn't allow chaining */
git_buf_puts(&stepbuffer, spec_cur);
- retcode = walk_ref_history(out, git_buf_cstr(&specbuffer), git_buf_cstr(&stepbuffer));
+ retcode = walk_ref_history(out, repo, git_buf_cstr(&specbuffer), git_buf_cstr(&stepbuffer));
next_state = REVPARSE_STATE_DONE;
} else if (*spec_cur == '^') {
next_state = REVPARSE_STATE_CARET;
@@ -335,10 +414,7 @@ int git_revparse_single(git_object **out, git_repository *repo, const char *spec
if (current_state != next_state) {
/* Leaving INIT state, find the object specified, in case that state needs it */
- retcode = revparse_lookup_object(&next_obj, repo, git_buf_cstr(&specbuffer));
- if (retcode < 0) {
- next_state = REVPARSE_STATE_DONE;
- }
+ revparse_lookup_object(&next_obj, repo, git_buf_cstr(&specbuffer));
}
break;
diff --git a/tests-clar/refs/revparse.c b/tests-clar/refs/revparse.c
index 08cf406f6..d8013abbd 100644
--- a/tests-clar/refs/revparse.c
+++ b/tests-clar/refs/revparse.c
@@ -133,5 +133,29 @@ void test_refs_revparse__chaining(void)
void test_refs_revparse__reflog(void)
{
- /* TODO: how to create a fixture for this? git_reflog_write? */
+ cl_git_fail(git_revparse_single(&g_obj, g_repo, "@{-xyz}"));
+ cl_git_fail(git_revparse_single(&g_obj, g_repo, "@{-0}"));
+
+ cl_git_pass(git_revparse_single(&g_obj, g_repo, "@{-2}"));
+ oid_str_cmp(g_obj, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ cl_git_pass(git_revparse_single(&g_obj, g_repo, "@{-1}"));
+ oid_str_cmp(g_obj, "a4a7dce85cf63874e984719f4fdd239f5145052f");
+ cl_git_pass(git_revparse_single(&g_obj, g_repo, "master@{0}"));
+ oid_str_cmp(g_obj, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ cl_git_pass(git_revparse_single(&g_obj, g_repo, "master@{1}"));
+ oid_str_cmp(g_obj, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ cl_git_pass(git_revparse_single(&g_obj, g_repo, "@{0}"));
+ oid_str_cmp(g_obj, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ cl_git_pass(git_revparse_single(&g_obj, g_repo, "@{1}"));
+ oid_str_cmp(g_obj, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ /* Not ready yet
+ cl_git_pass(git_revparse_single(&g_obj, g_repo, "HEAD@{100 years ago}"));
+ oid_str_cmp(g_obj, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ cl_git_pass(git_revparse_single(&g_obj, g_repo, "master@{2012-4-30 10:23:20}"));
+ oid_str_cmp(g_obj, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ cl_git_pass(git_revparse_single(&g_obj, g_repo, "master@{upstream}"));
+ oid_str_cmp(g_obj, "???");
+ cl_git_pass(git_revparse_single(&g_obj, g_repo, "master@{u}"));
+ oid_str_cmp(g_obj, "???");
+ */
}
diff --git a/tests/resources/testrepo.git/logs/HEAD b/tests/resources/testrepo.git/logs/HEAD
new file mode 100644
index 000000000..5bdcb7ce7
--- /dev/null
+++ b/tests/resources/testrepo.git/logs/HEAD
@@ -0,0 +1,5 @@
+0000000000000000000000000000000000000000 be3563ae3f795b2b4353bcce3a527ad0a4f7f644 Ben Straub <bstraub@github.com> 1335806563 -0700 clone: from /Users/ben/src/libgit2/tests/resources/testrepo.git
+be3563ae3f795b2b4353bcce3a527ad0a4f7f644 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Ben Straub <bstraub@github.com> 1335806603 -0700 commit:
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750 c47800c7266a2be04c571c04d5a6614691ea99bd Ben Straub <bstraub@github.com> 1335806608 -0700 checkout: moving from master to br2
+c47800c7266a2be04c571c04d5a6614691ea99bd a4a7dce85cf63874e984719f4fdd239f5145052f Ben Straub <bstraub@github.com> 1335806617 -0700 commit: checking in
+a4a7dce85cf63874e984719f4fdd239f5145052f a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Ben Straub <bstraub@github.com> 1335806621 -0700 checkout: moving from br2 to master
diff --git a/tests/resources/testrepo.git/logs/refs/heads/br2 b/tests/resources/testrepo.git/logs/refs/heads/br2
new file mode 100644
index 000000000..4e27f6b8d
--- /dev/null
+++ b/tests/resources/testrepo.git/logs/refs/heads/br2
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 c47800c7266a2be04c571c04d5a6614691ea99bd Ben Straub <bstraub@github.com> 1335806608 -0700 branch: Created from refs/remotes/origin/br2
+a4a7dce85cf63874e984719f4fdd239f5145052f a4a7dce85cf63874e984719f4fdd239f5145052f Ben Straub <bstraub@github.com> 1335806617 -0700 commit: checking in
diff --git a/tests/resources/testrepo.git/logs/refs/heads/master b/tests/resources/testrepo.git/logs/refs/heads/master
new file mode 100644
index 000000000..c41e87a3f
--- /dev/null
+++ b/tests/resources/testrepo.git/logs/refs/heads/master
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 be3563ae3f795b2b4353bcce3a527ad0a4f7f644 Ben Straub <bstraub@github.com> 1335806563 -0700 clone: from /Users/ben/src/libgit2/tests/resources/testrepo.git
+be3563ae3f795b2b4353bcce3a527ad0a4f7f644 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Ben Straub <bstraub@github.com> 1335806603 -0700 commit: checking in
diff --git a/tests/resources/testrepo.git/logs/refs/remotes/origin/HEAD b/tests/resources/testrepo.git/logs/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..f1aac6d0f
--- /dev/null
+++ b/tests/resources/testrepo.git/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 be3563ae3f795b2b4353bcce3a527ad0a4f7f644 Ben Straub <bstraub@github.com> 1335806563 -0700 clone: from /Users/ben/src/libgit2/tests/resources/testrepo.git