#define TREECHANGE (1u << 4)
static const char rev_list_usage[] =
- "git-rev-list [OPTION] commit-id <commit-id>\n"
- " --max-count=nr\n"
- " --max-age=epoch\n"
- " --min-age=epoch\n"
- " --parents\n"
- " --bisect\n"
- " --objects\n"
- " --unpacked\n"
- " --header\n"
- " --pretty\n"
- " --no-merges\n"
- " --merge-order [ --show-breaks ]\n"
- " --topo-order";
-
-static int dense = 0;
+"git-rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
+" limiting output:\n"
+" --max-count=nr\n"
+" --max-age=epoch\n"
+" --min-age=epoch\n"
+" --sparse\n"
+" --no-merges\n"
+" --all\n"
+" ordering output:\n"
+" --merge-order [ --show-breaks ]\n"
+" --topo-order\n"
+" formatting output:\n"
+" --parents\n"
+" --objects\n"
+" --unpacked\n"
+" --header | --pretty\n"
+" special purpose:\n"
+" --bisect"
+;
+
+static int dense = 1;
static int unpacked = 0;
static int bisect_list = 0;
static int tag_objects = 0;
fflush(stdout);
}
-static void rewrite_one(struct commit **pp)
+static int rewrite_one(struct commit **pp)
{
for (;;) {
struct commit *p = *pp;
if (p->object.flags & (TREECHANGE | UNINTERESTING))
- return;
- /* Only single-parent commits don't have TREECHANGE */
+ return 0;
+ if (!p->parents)
+ return -1;
*pp = p->parents->item;
}
}
static void rewrite_parents(struct commit *commit)
{
- struct commit_list *parent = commit->parents;
- while (parent) {
- rewrite_one(&parent->item);
- parent = parent->next;
+ struct commit_list **pp = &commit->parents;
+ while (*pp) {
+ struct commit_list *parent = *pp;
+ if (rewrite_one(&parent->item) < 0) {
+ *pp = parent->next;
+ continue;
+ }
+ pp = &parent->next;
}
}
stop_traversal=1;
return CONTINUE;
}
- if (max_count != -1 && !max_count--)
- return STOP;
if (no_merges && (commit->parents && commit->parents->next))
return CONTINUE;
if (paths && dense) {
return CONTINUE;
}
+ if (max_count != -1 && !max_count--)
+ return STOP;
+
show_commit(commit);
return CONTINUE;
if (commit->object.flags & (UNINTERESTING | COUNTED))
break;
- nr++;
+ if (!paths || (commit->object.flags & TREECHANGE))
+ nr++;
commit->object.flags |= COUNTED;
p = commit->parents;
entry = p;
}
}
}
+
return nr;
}
nr = 0;
p = list;
while (p) {
- nr++;
+ if (!paths || (p->item->object.flags & TREECHANGE))
+ nr++;
p = p->next;
}
closest = 0;
best = list;
- p = list;
- while (p) {
- int distance = count_distance(p);
+ for (p = list; p; p = p->next) {
+ int distance;
+
+ if (paths && !(p->item->object.flags & TREECHANGE))
+ continue;
+
+ distance = count_distance(p);
clear_distance(list);
if (nr - distance < distance)
distance = nr - distance;
best = p;
closest = distance;
}
- p = p->next;
}
if (best)
best->next = NULL;
return !is_different;
}
+static int same_tree_as_empty(struct tree *t1)
+{
+ int retval;
+ void *tree;
+ struct tree_desc empty, real;
+
+ if (!t1)
+ return 0;
+
+ tree = read_object_with_reference(t1->object.sha1, "tree", &real.size, NULL);
+ if (!tree)
+ return 0;
+ real.buf = tree;
+
+ empty.buf = "";
+ empty.size = 0;
+
+ is_different = 0;
+ retval = diff_tree(&empty, &real, "", &diff_opt);
+ free(tree);
+
+ return retval >= 0 && !is_different;
+}
+
static struct commit *try_to_simplify_merge(struct commit *commit, struct commit_list *parent)
{
if (!commit->tree)
struct commit_list *parent = commit->parents;
list = list->next;
+ if (!parent) {
+ if (!same_tree_as_empty(commit->tree))
+ commit->object.flags |= TREECHANGE;
+ continue;
+ }
+
/*
* Exactly one parent? Check if it leaves the tree
* unchanged
*/
- if (parent && !parent->next) {
+ if (!parent->next) {
struct tree *t1 = commit->tree;
struct tree *t2 = parent->item->tree;
if (!t1 || !t2 || same_tree(t1, t2))
add_object(obj, &pending_objects, name);
}
-static struct commit *get_commit_reference(const char *name, unsigned int flags)
+static struct commit *get_commit_reference(const char *name, const unsigned char *sha1, unsigned int flags)
{
- unsigned char sha1[20];
struct object *object;
- if (get_sha1(name, sha1))
- usage(rev_list_usage);
object = parse_object(sha1);
if (!object)
die("bad object %s", name);
static int include_one_commit(const char *path, const unsigned char *sha1)
{
- struct commit *com = get_commit_reference(path, 0);
+ struct commit *com = get_commit_reference(path, sha1, 0);
handle_one_commit(com, global_lst);
return 0;
}
const char *arg = argv[i];
char *dotdot;
struct commit *commit;
+ unsigned char sha1[20];
if (!strncmp(arg, "--max-count=", 12)) {
max_count = atoi(arg + 12);
dense = 1;
continue;
}
+ if (!strcmp(arg, "--sparse")) {
+ dense = 0;
+ continue;
+ }
if (!strcmp(arg, "--")) {
- paths = get_pathspec(prefix, argv + i + 1);
- if (paths) {
- limited = 1;
- diff_tree_setup_paths(paths);
- }
+ i++;
break;
}
flags = 0;
dotdot = strstr(arg, "..");
if (dotdot) {
+ unsigned char from_sha1[20];
char *next = dotdot + 2;
- struct commit *exclude = NULL;
- struct commit *include = NULL;
*dotdot = 0;
if (!*next)
next = "HEAD";
- exclude = get_commit_reference(arg, UNINTERESTING);
- include = get_commit_reference(next, 0);
- if (exclude && include) {
+ if (!get_sha1(arg, from_sha1) && !get_sha1(next, sha1)) {
+ struct commit *exclude;
+ struct commit *include;
+
+ exclude = get_commit_reference(arg, from_sha1, UNINTERESTING);
+ include = get_commit_reference(next, sha1, 0);
+ if (!exclude || !include)
+ die("Invalid revision range %s..%s", arg, next);
limited = 1;
handle_one_commit(exclude, &list);
handle_one_commit(include, &list);
arg++;
limited = 1;
}
- commit = get_commit_reference(arg, flags);
+ if (get_sha1(arg, sha1) < 0) {
+ struct stat st;
+ if (lstat(arg, &st) < 0)
+ die("'%s': %s", arg, strerror(errno));
+ break;
+ }
+ commit = get_commit_reference(arg, sha1, flags);
handle_one_commit(commit, &list);
}
+ if (!list &&
+ (!(tag_objects||tree_objects||blob_objects) && !pending_objects))
+ usage(rev_list_usage);
+
+ paths = get_pathspec(prefix, argv + i);
+ if (paths) {
+ limited = 1;
+ diff_tree_setup_paths(paths);
+ }
+
save_commit_buffer = verbose_header;
track_object_refs = 0;