} else if (!strcmp(arg, "--topo-order")) {
revs->lifo = 1;
revs->topo_order = 1;
+ } else if (!strcmp(arg, "--simplify-merges")) {
+ revs->simplify_merges = 1;
+ revs->rewrite_parents = 1;
+ revs->simplify_history = 0;
+ revs->limited = 1;
} else if (!strcmp(arg, "--date-order")) {
revs->lifo = 0;
revs->topo_order = 1;
l->next = add_decoration(&revs->children, &parent->object, l);
}
+static int remove_duplicate_parents(struct commit *commit)
+{
+ struct commit_list **pp, *p;
+ int surviving_parents;
+
+ /* Examine existing parents while marking ones we have seen... */
+ pp = &commit->parents;
+ while ((p = *pp) != NULL) {
+ struct commit *parent = p->item;
+ if (parent->object.flags & TMP_MARK) {
+ *pp = p->next;
+ continue;
+ }
+ parent->object.flags |= TMP_MARK;
+ pp = &p->next;
+ }
+ /* count them while clearing the temporary mark */
+ surviving_parents = 0;
+ for (p = commit->parents; p; p = p->next) {
+ p->item->object.flags &= ~TMP_MARK;
+ surviving_parents++;
+ }
+ return surviving_parents;
+}
+
+static struct commit_list **simplify_one(struct commit *commit, struct commit_list **tail)
+{
+ struct commit_list *p;
+ int cnt;
+
+ /*
+ * We store which commit each one simplifies to in its util field.
+ * Have we handled this one?
+ */
+ if (commit->util)
+ return tail;
+
+ /*
+ * An UNINTERESTING commit simplifies to itself, so does a
+ * root commit. We do not rewrite parents of such commit
+ * anyway.
+ */
+ if ((commit->object.flags & UNINTERESTING) || !commit->parents) {
+ commit->util = commit;
+ return tail;
+ }
+
+ /*
+ * Do we know what commit all of our parents should be rewritten to?
+ * Otherwise we are not ready to rewrite this one yet.
+ */
+ for (cnt = 0, p = commit->parents; p; p = p->next) {
+ if (!p->item->util) {
+ tail = &commit_list_insert(p->item, tail)->next;
+ cnt++;
+ }
+ }
+ if (cnt)
+ return tail;
+
+ /*
+ * Rewrite our list of parents.
+ */
+ for (p = commit->parents; p; p = p->next)
+ p->item = p->item->util;
+ cnt = remove_duplicate_parents(commit);
+
+ /*
+ * It is possible that we are a merge and one side branch
+ * does not have any commit that touches the given paths;
+ * in such a case, the immediate parents will be rewritten
+ * to different commits.
+ *
+ * o----X X: the commit we are looking at;
+ * / / o: a commit that touches the paths;
+ * ---o----'
+ *
+ * Further reduce the parents by removing redundant parents.
+ */
+ if (1 < cnt) {
+ struct commit_list *h = reduce_heads(commit->parents);
+ cnt = commit_list_count(h);
+ free_commit_list(commit->parents);
+ commit->parents = h;
+ }
+
+ /*
+ * A commit simplifies to itself if it is a root, if it is
+ * UNINTERESTING, if it touches the given paths, or if it is a
+ * merge and its parents simplifies to more than one commits
+ * (the first two cases are already handled at the beginning of
+ * this function).
+ *
+ * Otherwise, it simplifies to what its sole parent simplifies to.
+ */
+ if (!cnt ||
+ (commit->object.flags & UNINTERESTING) ||
+ !(commit->object.flags & TREESAME) ||
+ (1 < cnt))
+ commit->util = commit;
+ else
+ commit->util = commit->parents->item->util;
+ return tail;
+}
+
+static void simplify_merges(struct rev_info *revs)
+{
+ struct commit_list *list;
+ struct commit_list *yet_to_do, **tail;
+
+ /* feed the list reversed */
+ yet_to_do = NULL;
+ for (list = revs->commits; list; list = list->next)
+ commit_list_insert(list->item, &yet_to_do);
+ while (yet_to_do) {
+ list = yet_to_do;
+ yet_to_do = NULL;
+ tail = &yet_to_do;
+ while (list) {
+ struct commit *commit = list->item;
+ struct commit_list *next = list->next;
+ free(list);
+ list = next;
+ tail = simplify_one(commit, tail);
+ }
+ }
+
+ /* clean up the result, removing the simplified ones */
+ list = revs->commits;
+ revs->commits = NULL;
+ tail = &revs->commits;
+ while (list) {
+ struct commit *commit = list->item;
+ struct commit_list *next = list->next;
+ free(list);
+ list = next;
+ if (commit->util == commit)
+ tail = &commit_list_insert(commit, tail)->next;
+ }
+
+ /* sort topologically at the end */
+ sort_in_topological_order(&revs->commits, revs->lifo);
+}
+
static void set_children(struct rev_info *revs)
{
struct commit_list *l;
return -1;
if (revs->topo_order)
sort_in_topological_order(&revs->commits, revs->lifo);
+ if (revs->simplify_merges)
+ simplify_merges(revs);
if (revs->children.name)
set_children(revs);
return 0;
}
}
-static void remove_duplicate_parents(struct commit *commit)
-{
- struct commit_list **pp, *p;
-
- /* Examine existing parents while marking ones we have seen... */
- pp = &commit->parents;
- while ((p = *pp) != NULL) {
- struct commit *parent = p->item;
- if (parent->object.flags & TMP_MARK) {
- *pp = p->next;
- continue;
- }
- parent->object.flags |= TMP_MARK;
- pp = &p->next;
- }
- /* ... and clear the temporary mark */
- for (p = commit->parents; p; p = p->next)
- p->item->object.flags &= ~TMP_MARK;
-}
-
static int rewrite_parents(struct rev_info *revs, struct commit *commit)
{
struct commit_list **pp = &commit->parents;