git-rev-list: add option to list all objects (not just commits)
authorLinus Torvalds <torvalds@ppc970.osdl.org>
Sat, 25 Jun 2005 05:56:58 +0000 (22:56 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Sat, 25 Jun 2005 05:56:58 +0000 (22:56 -0700)
When you do

git-rev-list --objects $(git-rev-parse HEAD^..HEAD)

it now lists not only the "commit difference" between the parent of HEAD
and HEAD itself (which is normally just the parent, but in the case of a
merge will be all the newly merged commits), but also all the new tree
and blob objects that weren't in the original.

NOTE! It doesn't walk all the way to the root, so it doesn't do a full
object search in the full old history.  Instead, it will only look as
far back in the history as it needs to resolve the commits.  Thus, if
the commit reverts a blob (or tree) back to a state much further back in
history, we may end up listing some blobs (or trees) as "new" even
though they exist further back.

Regardless, the list of objects will be a superset (usually exact) list
of objects needed to go from the beginning commit to ending commit.

As a particularly obvious special case,

git-rev-list --objects HEAD

will end up listing every single object that is reachable from the HEAD
commit.

Side note: the objects are sorted by "recency", with commits first.

rev-list.c

index 16920ef69e844697628ca3775403f86cc1c13945..6b35b61ec25136c0c7249d217e2f47f663da3199 100644 (file)
@@ -1,5 +1,7 @@
 #include "cache.h"
 #include "commit.h"
+#include "tree.h"
+#include "blob.h"
 #include "epoch.h"
 
 #define SEEN           (1u << 0)
@@ -17,6 +19,8 @@ static const char rev_list_usage[] =
                      "  --merge-order [ --show-breaks ]";
 
 static int bisect_list = 0;
+static int tree_objects = 0;
+static int blob_objects = 0;
 static int verbose_header = 0;
 static int show_parents = 0;
 static int hdr_termination = 0;
@@ -94,20 +98,102 @@ static int process_commit(struct commit * commit)
        return CONTINUE;
 }
 
+static struct object_list **add_object(struct object *obj, struct object_list **p)
+{
+       struct object_list *entry = xmalloc(sizeof(*entry));
+       entry->item = obj;
+       entry->next = NULL;
+       *p = entry;
+       return &entry->next;
+}
+
+static struct object_list **process_blob(struct blob *blob, struct object_list **p)
+{
+       struct object *obj = &blob->object;
+
+       if (!blob_objects)
+               return p;
+       if (obj->flags & (UNINTERESTING | SEEN))
+               return p;
+       obj->flags |= SEEN;
+       return add_object(obj, p);
+}
+
+static struct object_list **process_tree(struct tree *tree, struct object_list **p)
+{
+       struct object *obj = &tree->object;
+       struct tree_entry_list *entry;
+
+       if (!tree_objects)
+               return p;
+       if (obj->flags & (UNINTERESTING | SEEN))
+               return p;
+       if (parse_tree(tree) < 0)
+               die("bad tree object %s", sha1_to_hex(obj->sha1));
+       obj->flags |= SEEN;
+       p = add_object(obj, p);
+       for (entry = tree->entries ; entry ; entry = entry->next) {
+               if (entry->directory)
+                       p = process_tree(entry->item.tree, p);
+               else
+                       p = process_blob(entry->item.blob, p);
+       }
+       return p;
+}
+
 static void show_commit_list(struct commit_list *list)
 {
+       struct object_list *objects = NULL, **p = &objects;
        while (list) {
                struct commit *commit = pop_most_recent_commit(&list, SEEN);
 
+               p = process_tree(commit->tree, p);
                if (process_commit(commit) == STOP)
                        break;
        }
+       while (objects) {
+               puts(sha1_to_hex(objects->item->sha1));
+               objects = objects->next;
+       }
+}
+
+static void mark_blob_uninteresting(struct blob *blob)
+{
+       if (!blob_objects)
+               return;
+       if (blob->object.flags & UNINTERESTING)
+               return;
+       blob->object.flags |= UNINTERESTING;
+}
+
+static void mark_tree_uninteresting(struct tree *tree)
+{
+       struct object *obj = &tree->object;
+       struct tree_entry_list *entry;
+
+       if (!tree_objects)
+               return;
+       if (obj->flags & UNINTERESTING)
+               return;
+       obj->flags |= UNINTERESTING;
+       if (parse_tree(tree) < 0)
+               die("bad tree %s", sha1_to_hex(obj->sha1));
+       entry = tree->entries;
+       while (entry) {
+               if (entry->directory)
+                       mark_tree_uninteresting(entry->item.tree);
+               else
+                       mark_blob_uninteresting(entry->item.blob);
+               entry = entry->next;
+       }
 }
 
 static void mark_parents_uninteresting(struct commit *commit)
 {
        struct commit_list *parents = commit->parents;
 
+       if (tree_objects)
+               mark_tree_uninteresting(commit->tree);
        while (parents) {
                struct commit *commit = parents->item;
                commit->object.flags |= UNINTERESTING;
@@ -276,6 +362,11 @@ int main(int argc, char **argv)
                        bisect_list = 1;
                        continue;
                }
+               if (!strcmp(arg, "--objects")) {
+                       tree_objects = 1;
+                       blob_objects = 1;
+                       continue;
+               }
                if (!strncmp(arg, "--merge-order", 13)) {
                        merge_order = 1;
                        continue;