/*
* Tag object? Look what it points to..
*/
- while (object->type == TYPE_TAG) {
+ while (object->type == OBJ_TAG) {
struct tag *tag = (struct tag *) object;
if (revs->tag_objects && !(flags & UNINTERESTING))
add_pending_object(revs, object, tag->tag);
* Commit object? Just return it, we'll do all the complex
* reachability crud.
*/
- if (object->type == TYPE_COMMIT) {
+ if (object->type == OBJ_COMMIT) {
struct commit *commit = (struct commit *)object;
if (parse_commit(commit) < 0)
die("unable to parse commit %s", name);
* Tree object? Either mark it uniniteresting, or add it
* to the list of objects to look at later..
*/
- if (object->type == TYPE_TREE) {
+ if (object->type == OBJ_TREE) {
struct tree *tree = (struct tree *)object;
if (!revs->tree_objects)
return NULL;
/*
* Blob object? You know the drill by now..
*/
- if (object->type == TYPE_BLOB) {
+ if (object->type == OBJ_BLOB) {
struct blob *blob = (struct blob *)object;
if (!revs->blob_objects)
return NULL;
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;
parse_commit(p);
switch (rev_compare_tree(revs, p->tree, commit->tree)) {
case REV_TREE_SAME:
+ tree_same = 1;
if (!revs->simplify_history || (p->object.flags & UNINTERESTING)) {
/* Even if a merge with an uninteresting
* side branch brought the entire change
}
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;
}
return 0;
while (1) {
it = get_reference(revs, arg, sha1, 0);
- if (it->type != TYPE_TAG)
+ if (it->type != OBJ_TAG)
break;
memcpy(sha1, ((struct tag*)it)->tagged->sha1, 20);
}
- if (it->type != TYPE_COMMIT)
+ if (it->type != OBJ_COMMIT)
return 0;
commit = (struct commit *)it;
for (parents = commit->parents; parents; parents = parents->next) {
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->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;
}
}
+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.
*/
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;
break;
}
- flags = 0;
+ flags = show_merge = 0;
for (i = 1; i < argc; i++) {
struct object *object;
const char *arg = argv[i];
def = argv[i];
continue;
}
+ if (!strcmp(arg, "--merge")) {
+ show_merge = 1;
+ continue;
+ }
if (!strcmp(arg, "--topo-order")) {
revs->topo_order = 1;
continue;
}
if (symmetric) {
- exclude = get_merge_bases_clean(a, b);
+ 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;
object = get_reference(revs, arg, sha1, flags ^ local_flags);
add_pending_object(revs, object, arg);
}
+ if (show_merge)
+ prepare_show_merge(revs);
if (def && !revs->pending.nr) {
unsigned char sha1[20];
struct object *object;
}
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;
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)
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);
}