Teach revision walking machinery to walk multiple times sequencially
authorHeiko Voigt <hvoigt@hvoigt.net>
Thu, 29 Mar 2012 07:21:21 +0000 (09:21 +0200)
committerJunio C Hamano <gitster@pobox.com>
Fri, 30 Mar 2012 15:57:49 +0000 (08:57 -0700)
Previously it was not possible to iterate revisions twice using the
revision walking api. We add a reset_revision_walk() which clears the
used flags. This allows us to do multiple sequencial revision walks.

We add the appropriate calls to the existing submodule machinery doing
revision walks. This is done to avoid surprises if future code wants to
call these functions more than once during the processes lifetime.

Signed-off-by: Heiko Voigt <hvoigt@hvoigt.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
.gitignore
Documentation/technical/api-revision-walking.txt
Makefile
object.c
object.h
revision.c
revision.h
submodule.c
t/t0062-revision-walking.sh [new file with mode: 0755]
test-revision-walking.c [new file with mode: 0644]

index 87fcc5f6ff2e180280ff767fd291247739c7d0fa..382481cd4df04e43efca6c5533550c3239981898 100644 (file)
 /test-mktemp
 /test-parse-options
 /test-path-utils
+/test-revision-walking
 /test-run-command
 /test-sha1
 /test-sigchain
index 996da0503acfa3e3a0ed0f57a951d0fbc1500fb8..b7d0d9a8a7b45f4988c0ee8170fec25c415cc918 100644 (file)
@@ -56,6 +56,11 @@ function.
        returning a `struct commit *` each time you call it. The end of the
        revision list is indicated by returning a NULL pointer.
 
+`reset_revision_walk`::
+
+       Reset the flags used by the revision walking api. You can use
+       this to do multiple sequencial revision walks.
+
 Data structures
 ---------------
 
index 2bce23e5eb783580a1287b09a84ab36391d5f6ea..681134a75c3bf5081882f2b5ddd456b17852bf2f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -479,6 +479,7 @@ TEST_PROGRAMS_NEED_X += test-match-trees
 TEST_PROGRAMS_NEED_X += test-mktemp
 TEST_PROGRAMS_NEED_X += test-parse-options
 TEST_PROGRAMS_NEED_X += test-path-utils
+TEST_PROGRAMS_NEED_X += test-revision-walking
 TEST_PROGRAMS_NEED_X += test-run-command
 TEST_PROGRAMS_NEED_X += test-sha1
 TEST_PROGRAMS_NEED_X += test-sigchain
index 6b06297a5f06cc35cb266d6dd36c92df75a82de7..f84e80a955bd83a4a3de6971d106a8ac430806bc 100644 (file)
--- a/object.c
+++ b/object.c
@@ -275,3 +275,14 @@ void object_array_remove_duplicates(struct object_array *array)
                array->nr = dst;
        }
 }
+
+void clear_object_flags(unsigned flags)
+{
+       int i;
+
+       for (i=0; i < obj_hash_size; i++) {
+               struct object *obj = obj_hash[i];
+               if (obj)
+                       obj->flags &= ~flags;
+       }
+}
index b6618d92bf04d549350d83b6237770c48734686f..6a97b6ba1a43e1d38eb07515ad298e0067628127 100644 (file)
--- a/object.h
+++ b/object.h
@@ -76,4 +76,6 @@ void add_object_array(struct object *obj, const char *name, struct object_array
 void add_object_array_with_mode(struct object *obj, const char *name, struct object_array *array, unsigned mode);
 void object_array_remove_duplicates(struct object_array *);
 
+void clear_object_flags(unsigned flags);
+
 #endif /* OBJECT_H */
index c97d83448426c317d82c08faf6150b8fe3bd4326..4fbc14b6b4f181ce88675ff406cec5c5af10607a 100644 (file)
@@ -2061,6 +2061,11 @@ static void set_children(struct rev_info *revs)
        }
 }
 
+void reset_revision_walk(void)
+{
+       clear_object_flags(SEEN | ADDED | SHOWN);
+}
+
 int prepare_revision_walk(struct rev_info *revs)
 {
        int nr = revs->pending.nr;
index b8e9223954a5d66e01bd1eb342a936495aa67ad1..eb87fd1e2f5d16032cb92d96c4bae9e2397f2978 100644 (file)
@@ -192,6 +192,7 @@ extern void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ct
                                 const char * const usagestr[]);
 extern int handle_revision_arg(const char *arg, struct rev_info *revs,int flags,int cant_be_filename);
 
+extern void reset_revision_walk(void);
 extern int prepare_revision_walk(struct rev_info *revs);
 extern struct commit *get_revision(struct rev_info *revs);
 extern char *get_revision_mark(const struct rev_info *revs, const struct commit *commit);
index 9a2806067954c55a27c068706b5bfe67a1189fd5..645ff5db0fcd36dd986fe28be9fd50d989b712b7 100644 (file)
@@ -404,6 +404,7 @@ int check_submodule_needs_pushing(unsigned char new_sha1[20], const char *remote
        while ((commit = get_revision(&rev)) && !needs_pushing)
                commit_need_pushing(commit, &needs_pushing);
 
+       reset_revision_walk();
        free(sha1_copy);
        strbuf_release(&remotes_arg);
 
@@ -741,6 +742,7 @@ static int find_first_merges(struct object_array *result, const char *path,
                if (in_merge_bases(b, &commit, 1))
                        add_object_array(o, NULL, &merges);
        }
+       reset_revision_walk();
 
        /* Now we've got all merges that contain a and b. Prune all
         * merges that contain another found merge and save them in
diff --git a/t/t0062-revision-walking.sh b/t/t0062-revision-walking.sh
new file mode 100755 (executable)
index 0000000..3d98eb8
--- /dev/null
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Heiko Voigt
+#
+
+test_description='Test revision walking api'
+
+. ./test-lib.sh
+
+cat >run_twice_expected <<-EOF
+1st
+ > add b
+ > add a
+2nd
+ > add b
+ > add a
+EOF
+
+test_expect_success 'setup' '
+       echo a > a &&
+       git add a &&
+       git commit -m "add a" &&
+       echo b > b &&
+       git add b &&
+       git commit -m "add b"
+'
+
+test_expect_success 'revision walking can be done twice' '
+       test-revision-walking run-twice > run_twice_actual
+       test_cmp run_twice_expected run_twice_actual
+'
+
+test_done
diff --git a/test-revision-walking.c b/test-revision-walking.c
new file mode 100644 (file)
index 0000000..3ade02c
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * test-revision-walking.c: test revision walking API.
+ *
+ * (C) 2012 Heiko Voigt <hvoigt@hvoigt.net>
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "cache.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+
+static void print_commit(struct commit *commit)
+{
+       struct strbuf sb = STRBUF_INIT;
+       struct pretty_print_context ctx = {0};
+       ctx.date_mode = DATE_NORMAL;
+       format_commit_message(commit, " %m %s", &sb, &ctx);
+       printf("%s\n", sb.buf);
+       strbuf_release(&sb);
+}
+
+static int run_revision_walk(void)
+{
+       struct rev_info rev;
+       struct commit *commit;
+       const char *argv[] = {NULL, "--all", NULL};
+       int argc = ARRAY_SIZE(argv) - 1;
+       int got_revision = 0;
+
+       init_revisions(&rev, NULL);
+       setup_revisions(argc, argv, &rev, NULL);
+       if (prepare_revision_walk(&rev))
+               die("revision walk setup failed");
+
+       while ((commit = get_revision(&rev)) != NULL) {
+               print_commit(commit);
+               got_revision = 1;
+       }
+
+       reset_revision_walk();
+       return got_revision;
+}
+
+int main(int argc, char **argv)
+{
+       if (argc < 2)
+               return 1;
+
+       if (!strcmp(argv[1], "run-twice")) {
+               printf("1st\n");
+               if (!run_revision_walk())
+                       return 1;
+               printf("2nd\n");
+               if (!run_revision_walk())
+                       return 1;
+
+               return 0;
+       }
+
+       fprintf(stderr, "check usage\n");
+       return 1;
+}