X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=revision.c;h=75c648c13c6c50335ffc3e79f5d6303bea20104e;hb=3dfb9278dff6d81fcc062e9a56edab9ece38ea7d;hp=6a6952cd559af89d3c8dc2c477ddd8c25b470d66;hpb=b9d301bca767c9c26b8514fb326dd237cb269fbb;p=git.git diff --git a/revision.c b/revision.c index 6a6952cd5..b588f7487 100644 --- a/revision.c +++ b/revision.c @@ -31,17 +31,12 @@ static char *path_name(struct name_path *path, const char *name) return n; } -struct object_list **add_object(struct object *obj, - struct object_list **p, - struct name_path *path, - const char *name) +void add_object(struct object *obj, + struct object_array *p, + struct name_path *path, + const char *name) { - struct object_list *entry = xmalloc(sizeof(*entry)); - entry->item = obj; - entry->next = *p; - entry->name = path_name(path, name); - *p = entry; - return &entry->next; + add_object_array(obj, path_name(path, name), p); } static void mark_blob_uninteresting(struct blob *blob) @@ -117,9 +112,9 @@ void mark_parents_uninteresting(struct commit *commit) } } -static void add_pending_object(struct rev_info *revs, struct object *obj, const char *name) +void add_pending_object(struct rev_info *revs, struct object *obj, const char *name) { - add_object(obj, &revs->pending_objects, NULL, name); + add_object_array(obj, name, &revs->pending); } static struct object *get_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags) @@ -140,7 +135,7 @@ static struct commit *handle_commit(struct rev_info *revs, struct object *object /* * Tag object? Look what it points to.. */ - while (object->type == tag_type) { + while (object->type == OBJ_TAG) { struct tag *tag = (struct tag *) object; if (revs->tag_objects && !(flags & UNINTERESTING)) add_pending_object(revs, object, tag->tag); @@ -153,7 +148,7 @@ static struct commit *handle_commit(struct rev_info *revs, struct object *object * Commit object? Just return it, we'll do all the complex * reachability crud. */ - if (object->type == commit_type) { + if (object->type == OBJ_COMMIT) { struct commit *commit = (struct commit *)object; if (parse_commit(commit) < 0) die("unable to parse commit %s", name); @@ -169,7 +164,7 @@ static struct commit *handle_commit(struct rev_info *revs, struct object *object * Tree object? Either mark it uniniteresting, or add it * to the list of objects to look at later.. */ - if (object->type == tree_type) { + if (object->type == OBJ_TREE) { struct tree *tree = (struct tree *)object; if (!revs->tree_objects) return NULL; @@ -184,7 +179,7 @@ static struct commit *handle_commit(struct rev_info *revs, struct object *object /* * Blob object? You know the drill by now.. */ - if (object->type == blob_type) { + if (object->type == OBJ_BLOB) { struct blob *blob = (struct blob *)object; if (!revs->blob_objects) return NULL; @@ -285,7 +280,7 @@ int rev_same_tree_as_empty(struct rev_info *revs, struct tree *t1) static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit) { struct commit_list **pp, *parent; - int tree_changed = 0; + int tree_changed = 0, tree_same = 0; if (!commit->tree) return; @@ -303,7 +298,8 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit) parse_commit(p); switch (rev_compare_tree(revs, p->tree, commit->tree)) { case REV_TREE_SAME: - if (p->object.flags & UNINTERESTING) { + tree_same = 1; + if (!revs->simplify_history || (p->object.flags & UNINTERESTING)) { /* Even if a merge with an uninteresting * side branch brought the entire change * we are interested in, we do not want @@ -339,7 +335,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit) } die("bad tree compare for commit %s", sha1_to_hex(commit->object.sha1)); } - if (tree_changed) + if (tree_changed && !tree_same) commit->object.flags |= TREECHANGE; } @@ -498,11 +494,11 @@ static int add_parents_only(struct rev_info *revs, const char *arg, int flags) return 0; while (1) { it = get_reference(revs, arg, sha1, 0); - if (strcmp(it->type, tag_type)) + if (it->type != OBJ_TAG) break; - memcpy(sha1, ((struct tag*)it)->tagged->sha1, 20); + hashcpy(sha1, ((struct tag*)it)->tagged->sha1); } - if (strcmp(it->type, commit_type)) + if (it->type != OBJ_COMMIT) return 0; commit = (struct commit *)it; for (parents = commit->parents; parents; parents = parents->next) { @@ -513,18 +509,19 @@ static int add_parents_only(struct rev_info *revs, const char *arg, int flags) return 1; } -void init_revisions(struct rev_info *revs) +void init_revisions(struct rev_info *revs, const char *prefix) { memset(revs, 0, sizeof(*revs)); revs->abbrev = DEFAULT_ABBREV; revs->ignore_merges = 1; + revs->simplify_history = 1; revs->pruning.recursive = 1; revs->pruning.add_remove = file_add_remove; revs->pruning.change = file_change; revs->lifo = 1; revs->dense = 1; - revs->prefix = setup_git_directory(); + revs->prefix = prefix; revs->max_age = -1; revs->min_age = -1; revs->max_count = -1; @@ -540,6 +537,61 @@ void init_revisions(struct rev_info *revs) diff_setup(&revs->diffopt); } +static void add_pending_commit_list(struct rev_info *revs, + struct commit_list *commit_list, + unsigned int flags) +{ + while (commit_list) { + struct object *object = &commit_list->item->object; + object->flags |= flags; + add_pending_object(revs, object, sha1_to_hex(object->sha1)); + commit_list = commit_list->next; + } +} + +static void prepare_show_merge(struct rev_info *revs) +{ + struct commit_list *bases; + struct commit *head, *other; + unsigned char sha1[20]; + const char **prune = NULL; + int i, prune_num = 1; /* counting terminating NULL */ + + if (get_sha1("HEAD", sha1) || !(head = lookup_commit(sha1))) + die("--merge without HEAD?"); + if (get_sha1("MERGE_HEAD", sha1) || !(other = lookup_commit(sha1))) + die("--merge without MERGE_HEAD?"); + add_pending_object(revs, &head->object, "HEAD"); + add_pending_object(revs, &other->object, "MERGE_HEAD"); + bases = get_merge_bases(head, other, 1); + while (bases) { + struct commit *it = bases->item; + struct commit_list *n = bases->next; + free(bases); + bases = n; + it->object.flags |= UNINTERESTING; + add_pending_object(revs, &it->object, "(merge-base)"); + } + + if (!active_nr) + read_cache(); + for (i = 0; i < active_nr; i++) { + struct cache_entry *ce = active_cache[i]; + if (!ce_stage(ce)) + continue; + if (ce_path_match(ce, revs->prune_data)) { + prune_num++; + prune = xrealloc(prune, sizeof(*prune) * prune_num); + prune[prune_num-2] = ce->name; + prune[prune_num-1] = NULL; + } + while ((i+1 < active_nr) && + ce_same_name(ce, active_cache[i+1])) + i++; + } + revs->prune_data = prune; +} + /* * Parse revision information, filling in the "rev_info" structure, * and removing the used arguments from the argument list. @@ -549,7 +601,7 @@ void init_revisions(struct rev_info *revs) */ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def) { - int i, flags, seen_dashdash; + int i, flags, seen_dashdash, show_merge; const char **unrecognized = argv + 1; int left = 1; @@ -566,7 +618,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch break; } - flags = 0; + flags = show_merge = 0; for (i = 1; i < argc; i++) { struct object *object; const char *arg = argv[i]; @@ -633,6 +685,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch def = argv[i]; continue; } + if (!strcmp(arg, "--merge")) { + show_merge = 1; + continue; + } if (!strcmp(arg, "--topo-order")) { revs->topo_order = 1; continue; @@ -756,6 +812,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch revs->full_diff = 1; continue; } + if (!strcmp(arg, "--full-history")) { + revs->simplify_history = 0; + continue; + } + if (!strcmp(arg, "--relative-date")) { + revs->relative_date = 1; + continue; + } opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i); if (opts > 0) { revs->diff = 1; @@ -771,27 +835,46 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch unsigned char from_sha1[20]; const char *next = dotdot + 2; const char *this = arg; + int symmetric = *next == '.'; + unsigned int flags_exclude = flags ^ UNINTERESTING; + *dotdot = 0; + next += symmetric; + if (!*next) next = "HEAD"; if (dotdot == arg) this = "HEAD"; if (!get_sha1(this, from_sha1) && !get_sha1(next, sha1)) { - struct object *exclude; - struct object *include; - - exclude = get_reference(revs, this, from_sha1, flags ^ UNINTERESTING); - include = get_reference(revs, next, sha1, flags); - if (!exclude || !include) - die("Invalid revision range %s..%s", arg, next); + struct commit *a, *b; + struct commit_list *exclude; + + a = lookup_commit_reference(from_sha1); + b = lookup_commit_reference(sha1); + if (!a || !b) { + die(symmetric ? + "Invalid symmetric difference expression %s...%s" : + "Invalid revision range %s..%s", + arg, next); + } if (!seen_dashdash) { *dotdot = '.'; verify_non_filename(revs->prefix, arg); } - add_pending_object(revs, exclude, this); - add_pending_object(revs, include, next); + + if (symmetric) { + exclude = get_merge_bases(a, b, 1); + add_pending_commit_list(revs, exclude, + flags_exclude); + free_commit_list(exclude); + a->object.flags |= flags; + } else + a->object.flags |= flags_exclude; + b->object.flags |= flags; + add_pending_object(revs, &a->object, this); + add_pending_object(revs, &b->object, next); continue; } *dotdot = '.'; @@ -831,7 +914,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch object = get_reference(revs, arg, sha1, flags ^ local_flags); add_pending_object(revs, object, arg); } - if (def && !revs->pending_objects) { + if (show_merge) + prepare_show_merge(revs); + if (def && !revs->pending.nr) { unsigned char sha1[20]; struct object *object; if (get_sha1(def, sha1)) @@ -851,23 +936,25 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch } if (revs->combine_merges) { revs->ignore_merges = 0; - if (revs->dense_combined_merges && - (revs->diffopt.output_format != DIFF_FORMAT_DIFFSTAT)) + if (revs->dense_combined_merges && !revs->diffopt.output_format) revs->diffopt.output_format = DIFF_FORMAT_PATCH; } revs->diffopt.abbrev = revs->abbrev; - diff_setup_done(&revs->diffopt); + if (diff_setup_done(&revs->diffopt) < 0) + die("diff_setup_done failed"); return left; } void prepare_revision_walk(struct rev_info *revs) { - struct object_list *list; + int nr = revs->pending.nr; + struct object_array_entry *list = revs->pending.objects; - list = revs->pending_objects; - revs->pending_objects = NULL; - while (list) { + revs->pending.nr = 0; + revs->pending.alloc = 0; + revs->pending.objects = NULL; + while (--nr >= 0) { struct commit *commit = handle_commit(revs, list->item, list->name); if (commit) { if (!(commit->object.flags & SEEN)) { @@ -875,7 +962,7 @@ void prepare_revision_walk(struct rev_info *revs) insert_by_date(commit, &revs->commits); } } - list = list->next; + list++; } if (revs->no_walk) @@ -894,6 +981,8 @@ static int rewrite_one(struct rev_info *revs, struct commit **pp) struct commit *p = *pp; if (!revs->limited) add_parents_to_list(revs, p, &revs->commits); + if (p->parents && p->parents->next) + return 0; if (p->object.flags & (TREECHANGE | UNINTERESTING)) return 0; if (!p->parents) @@ -944,9 +1033,11 @@ struct commit *get_revision(struct rev_info *revs) } do { - struct commit *commit = revs->commits->item; + struct commit_list *entry = revs->commits; + struct commit *commit = entry->item; - revs->commits = revs->commits->next; + revs->commits = entry->next; + free(entry); /* * If we haven't done the list limiting, we need to look at @@ -984,8 +1075,15 @@ struct commit *get_revision(struct rev_info *revs) commit->parents && commit->parents->next) continue; if (revs->prune_fn && revs->dense) { - if (!(commit->object.flags & TREECHANGE)) - continue; + /* Commit without changes? */ + if (!(commit->object.flags & TREECHANGE)) { + /* drop merges unless we want parenthood */ + if (!revs->parents) + continue; + /* non-merge - always ignore it */ + if (!commit->parents || !commit->parents->next) + continue; + } if (revs->parents) rewrite_parents(revs, commit); }