A few more options for git-cat-file
[gitweb.git] / epoch.c
diff --git a/epoch.c b/epoch.c
index 3a484b00c6057c6c6426b250dcccd0a8d7f2408f..db44f5ca9f73a75cc266d9b45eda0aca56dd1ede 100644 (file)
--- a/epoch.c
+++ b/epoch.c
@@ -8,9 +8,10 @@
  *
  */
 #include <stdlib.h>
-#include <openssl/bn.h>                // provides arbitrary precision integers
-                               // required to accurately represent fractional 
-                               //mass
+
+/* Provides arbitrary precision integers required to accurately represent
+ * fractional mass: */
+#include <openssl/bn.h>
 
 #include "cache.h"
 #include "commit.h"
@@ -27,7 +28,7 @@ static BN_CTX *context = NULL;
 static struct fraction *one = NULL;
 static struct fraction *zero = NULL;
 
-static BN_CTX *get_BN_CTX()
+static BN_CTX *get_BN_CTX(void)
 {
        if (!context) {
                context = BN_CTX_new();
@@ -35,7 +36,7 @@ static BN_CTX *get_BN_CTX()
        return context;
 }
 
-static struct fraction *new_zero()
+static struct fraction *new_zero(void)
 {
        struct fraction *result = xmalloc(sizeof(*result));
        BN_init(&result->numerator);
@@ -74,7 +75,7 @@ static struct fraction *init_fraction(struct fraction *fraction)
        return fraction;
 }
 
-static struct fraction *get_one()
+static struct fraction *get_one(void)
 {
        if (!one) {
                one = new_zero();
@@ -83,7 +84,7 @@ static struct fraction *get_one()
        return one;
 }
 
-static struct fraction *get_zero()
+static struct fraction *get_zero(void)
 {
        if (!zero) {
                zero = new_zero();
@@ -125,7 +126,6 @@ static struct fraction *add(struct fraction *result, struct fraction *left, stru
 static int compare(struct fraction *left, struct fraction *right)
 {
        BIGNUM a, b;
-
        int result;
 
        BN_init(&a);
@@ -159,7 +159,8 @@ static struct mass_counter *new_mass_counter(struct commit *commit, struct fract
        copy(&mass_counter->seen, get_zero());
 
        if (commit->object.util) {
-               die("multiple attempts to initialize mass counter for %s\n", sha1_to_hex(commit->object.sha1));
+               die("multiple attempts to initialize mass counter for %s",
+                   sha1_to_hex(commit->object.sha1));
        }
 
        commit->object.util = mass_counter;
@@ -174,149 +175,121 @@ static void free_mass_counter(struct mass_counter *counter)
        free(counter);
 }
 
-//
-// Finds the base commit of a list of commits.
-//
-// One property of the commit being searched for is that every commit reachable 
-// from the base commit is reachable from the commits in the starting list only 
-// via paths that include the base commit.
-//
-// This algorithm uses a conservation of mass approach to find the base commit.
-//
-// We start by injecting one unit of mass into the graph at each
-// of the commits in the starting list. Injecting mass into a commit
-// is achieved by adding to its pending mass counter and, if it is not already
-// enqueued, enqueuing the commit in a list of pending commits, in latest 
-// commit date first order.
-//
-// The algorithm then preceeds to visit each commit in the pending queue.
-// Upon each visit, the pending mass is added to the mass already seen for that 
-// commit and then divided into N equal portions, where N is the number of 
-// parents of the commit being visited. The divided portions are then injected 
-// into each of the parents.
-//
-// The algorithm continues until we discover a commit which has seen all the
-// mass originally injected or until we run out of things to do. 
-//
-// If we find a commit that has seen all the original mass, we have found
-// the common base of all the commits in the starting list. 
-//
-// The algorithm does _not_ depend on accurate timestamps for correct operation.
-// However, reasonably sane (e.g. non-random) timestamps are required in order 
-// to prevent an exponential performance characteristic. The occasional 
-// timestamp inaccuracy will not dramatically affect performance but may 
-// result in more nodes being processed than strictly necessary.
-//
-// This procedure sets *boundary to the address of the base commit. It returns 
-// non-zero if, and only if, there was a problem parsing one of the 
-// commits discovered during the traversal.
-//
+/*
+ * Finds the base commit of a list of commits.
+ *
+ * One property of the commit being searched for is that every commit reachable
+ * from the base commit is reachable from the commits in the starting list only
+ * via paths that include the base commit.
+ *
+ * This algorithm uses a conservation of mass approach to find the base commit.
+ *
+ * We start by injecting one unit of mass into the graph at each
+ * of the commits in the starting list. Injecting mass into a commit
+ * is achieved by adding to its pending mass counter and, if it is not already
+ * enqueued, enqueuing the commit in a list of pending commits, in latest
+ * commit date first order.
+ *
+ * The algorithm then preceeds to visit each commit in the pending queue.
+ * Upon each visit, the pending mass is added to the mass already seen for that
+ * commit and then divided into N equal portions, where N is the number of
+ * parents of the commit being visited. The divided portions are then injected
+ * into each of the parents.
+ *
+ * The algorithm continues until we discover a commit which has seen all the
+ * mass originally injected or until we run out of things to do.
+ *
+ * If we find a commit that has seen all the original mass, we have found
+ * the common base of all the commits in the starting list.
+ *
+ * The algorithm does _not_ depend on accurate timestamps for correct operation.
+ * However, reasonably sane (e.g. non-random) timestamps are required in order
+ * to prevent an exponential performance characteristic. The occasional
+ * timestamp inaccuracy will not dramatically affect performance but may
+ * result in more nodes being processed than strictly necessary.
+ *
+ * This procedure sets *boundary to the address of the base commit. It returns
+ * non-zero if, and only if, there was a problem parsing one of the
+ * commits discovered during the traversal.
+ */
 static int find_base_for_list(struct commit_list *list, struct commit **boundary)
 {
-
        int ret = 0;
-
        struct commit_list *cleaner = NULL;
        struct commit_list *pending = NULL;
-
-       *boundary = NULL;
-
        struct fraction injected;
-
        init_fraction(&injected);
+       *boundary = NULL;
 
        for (; list; list = list->next) {
-
                struct commit *item = list->item;
 
-               if (item->object.util) {
-                       die("%s:%d:%s: logic error: this should not have happened - commit %s\n",
-                           __FILE__, __LINE__, __FUNCTION__, sha1_to_hex(item->object.sha1));
-               }
-
-               new_mass_counter(list->item, get_one());
-               add(&injected, &injected, get_one());
+               if (!item->object.util) {
+                       new_mass_counter(list->item, get_one());
+                       add(&injected, &injected, get_one());
 
-               commit_list_insert(list->item, &cleaner);
-               commit_list_insert(list->item, &pending);
+                       commit_list_insert(list->item, &cleaner);
+                       commit_list_insert(list->item, &pending);
+               }
        }
 
        while (!*boundary && pending && !ret) {
-
                struct commit *latest = pop_commit(&pending);
-
                struct mass_counter *latest_node = (struct mass_counter *) latest->object.util;
+               int num_parents;
 
                if ((ret = parse_commit(latest)))
                        continue;
-
                add(&latest_node->seen, &latest_node->seen, &latest_node->pending);
 
-               int num_parents = count_parents(latest);
-
+               num_parents = count_parents(latest);
                if (num_parents) {
-
                        struct fraction distribution;
                        struct commit_list *parents;
 
                        divide(init_fraction(&distribution), &latest_node->pending, num_parents);
 
                        for (parents = latest->parents; parents; parents = parents->next) {
-
                                struct commit *parent = parents->item;
                                struct mass_counter *parent_node = (struct mass_counter *) parent->object.util;
 
                                if (!parent_node) {
-
                                        parent_node = new_mass_counter(parent, &distribution);
-
-                                       insert_by_date(&pending, parent);
+                                       insert_by_date(parent, &pending);
                                        commit_list_insert(parent, &cleaner);
-
                                } else {
-
-                                       if (!compare(&parent_node->pending, get_zero())) {
-                                               insert_by_date(&pending, parent);
-                                       }
+                                       if (!compare(&parent_node->pending, get_zero()))
+                                               insert_by_date(parent, &pending);
                                        add(&parent_node->pending, &parent_node->pending, &distribution);
-
                                }
                        }
 
                        clear_fraction(&distribution);
-
                }
 
-               if (!compare(&latest_node->seen, &injected)) {
+               if (!compare(&latest_node->seen, &injected))
                        *boundary = latest;
-               }
-
                copy(&latest_node->pending, get_zero());
-
        }
 
        while (cleaner) {
-
                struct commit *next = pop_commit(&cleaner);
                free_mass_counter((struct mass_counter *) next->object.util);
                next->object.util = NULL;
-
        }
 
        if (pending)
                free_commit_list(pending);
 
        clear_fraction(&injected);
-
        return ret;
-
 }
 
 
-//
-// Finds the base of an minimal, non-linear epoch, headed at head, by
-// applying the find_base_for_list to a list consisting of the parents
-//
+/*
+ * Finds the base of an minimal, non-linear epoch, headed at head, by
+ * applying the find_base_for_list to a list consisting of the parents
+ */
 static int find_base(struct commit *head, struct commit **boundary)
 {
        int ret = 0;
@@ -332,14 +305,14 @@ static int find_base(struct commit *head, struct commit **boundary)
        return ret;
 }
 
-//
-// This procedure traverses to the boundary of the first epoch in the epoch
-// sequence of the epoch headed at head_of_epoch. This is either the end of
-// the maximal linear epoch or the base of a minimal non-linear epoch.
-//
-// The queue of pending nodes is sorted in reverse date order and each node
-// is currently in the queue at most once.
-//
+/*
+ * This procedure traverses to the boundary of the first epoch in the epoch
+ * sequence of the epoch headed at head_of_epoch. This is either the end of
+ * the maximal linear epoch or the base of a minimal non-linear epoch.
+ *
+ * The queue of pending nodes is sorted in reverse date order and each node
+ * is currently in the queue at most once.
+ */
 static int find_next_epoch_boundary(struct commit *head_of_epoch, struct commit **boundary)
 {
        int ret;
@@ -350,10 +323,10 @@ static int find_next_epoch_boundary(struct commit *head_of_epoch, struct commit
                return ret;
 
        if (HAS_EXACTLY_ONE_PARENT(item)) {
-
-               // we are at the start of a maximimal linear epoch .. traverse to the end
-
-               // traverse to the end of a maximal linear epoch
+               /*
+                * We are at the start of a maximimal linear epoch.
+                * Traverse to the end.
+                */
                while (HAS_EXACTLY_ONE_PARENT(item) && !ret) {
                        item = item->parents->item;
                        ret = parse_commit(item);
@@ -361,35 +334,35 @@ static int find_next_epoch_boundary(struct commit *head_of_epoch, struct commit
                *boundary = item;
 
        } else {
-
-               // otherwise, we are at the start of a minimal, non-linear
-               // epoch - find the common base of all parents.
-
+               /*
+                * Otherwise, we are at the start of a minimal, non-linear
+                * epoch - find the common base of all parents.
+                */
                ret = find_base(item, boundary);
-
        }
 
        return ret;
 }
 
-//
-// Returns non-zero if parent is known to be a parent of child.
-//
+/*
+ * Returns non-zero if parent is known to be a parent of child.
+ */
 static int is_parent_of(struct commit *parent, struct commit *child)
 {
        struct commit_list *parents;
        for (parents = child->parents; parents; parents = parents->next) {
-               if (!memcmp(parent->object.sha1, parents->item->object.sha1, sizeof(parents->item->object.sha1)))
+               if (!memcmp(parent->object.sha1, parents->item->object.sha1,
+                           sizeof(parents->item->object.sha1)))
                        return 1;
        }
        return 0;
 }
 
-//
-// Pushes an item onto the merge order stack. If the top of the stack is
-// marked as being a possible "break", we check to see whether it actually
-// is a break.
-//
+/*
+ * Pushes an item onto the merge order stack. If the top of the stack is
+ * marked as being a possible "break", we check to see whether it actually
+ * is a break.
+ */
 static void push_onto_merge_order_stack(struct commit_list **stack, struct commit *item)
 {
        struct commit_list *top = *stack;
@@ -401,118 +374,104 @@ static void push_onto_merge_order_stack(struct commit_list **stack, struct commi
        commit_list_insert(item, stack);
 }
 
-//
-// Marks all interesting, visited commits reachable from this commit
-// as uninteresting. We stop recursing when we reach the epoch boundary,
-// an unvisited node or a node that has already been marking uninteresting.
-// This doesn't actually mark all ancestors between the start node and the
-// epoch boundary uninteresting, but does ensure that they will 
-// eventually be marked uninteresting when the main sort_first_epoch 
-// traversal eventually reaches them. 
-//
+/*
+ * Marks all interesting, visited commits reachable from this commit
+ * as uninteresting. We stop recursing when we reach the epoch boundary,
+ * an unvisited node or a node that has already been marking uninteresting.
+ *
+ * This doesn't actually mark all ancestors between the start node and the
+ * epoch boundary uninteresting, but does ensure that they will eventually
+ * be marked uninteresting when the main sort_first_epoch() traversal
+ * eventually reaches them.
+ */
 static void mark_ancestors_uninteresting(struct commit *commit)
 {
        unsigned int flags = commit->object.flags;
        int visited = flags & VISITED;
        int boundary = flags & BOUNDARY;
        int uninteresting = flags & UNINTERESTING;
+       struct commit_list *next;
 
        commit->object.flags |= UNINTERESTING;
-       if (uninteresting || boundary || !visited) {
-               return;
 
-               // we only need to recurse if
-               //      we are not on the boundary, and,
-               //      we have not already been marked uninteresting, and,
-               //      we have already been visited.
-
-               //
-               // the main sort_first_epoch traverse will 
-               // mark unreachable all uninteresting, unvisited parents 
-               // as they are visited so there is no need to duplicate
-               // that traversal here.
-               //
-               // similarly, if we are already marked uninteresting
-               // then either all ancestors have already been marked
-               // uninteresting or will be once the sort_first_epoch
-               // traverse reaches them.
-               //
-       }
-
-       struct commit_list *next;
+       /*
+        * We only need to recurse if
+        *      we are not on the boundary and
+        *      we have not already been marked uninteresting and
+        *      we have already been visited.
+        *
+        * The main sort_first_epoch traverse will mark unreachable
+        * all uninteresting, unvisited parents as they are visited
+        * so there is no need to duplicate that traversal here.
+        *
+        * Similarly, if we are already marked uninteresting
+        * then either all ancestors have already been marked
+        * uninteresting or will be once the sort_first_epoch
+        * traverse reaches them.
+        */
+
+       if (uninteresting || boundary || !visited)
+               return;
 
        for (next = commit->parents; next; next = next->next)
                mark_ancestors_uninteresting(next->item);
 }
 
-//
-// Sorts the nodes of the first epoch of the epoch sequence of the epoch headed at head
-// into merge order.
-//
+/*
+ * Sorts the nodes of the first epoch of the epoch sequence of the epoch headed at head
+ * into merge order.
+ */
 static void sort_first_epoch(struct commit *head, struct commit_list **stack)
 {
        struct commit_list *parents;
-       struct commit_list *reversed_parents = NULL;
 
        head->object.flags |= VISITED;
 
-       //
-       // parse_commit builds the parent list in reverse order with respect to the order of
-       // the git-commit-tree arguments.
-       //
-       // so we need to reverse this list to output the oldest (or most "local") commits last.
-       //
-
-       for (parents = head->parents; parents; parents = parents->next)
-               commit_list_insert(parents->item, &reversed_parents);
-
-       //
-       // todo: by sorting the parents in a different order, we can alter the 
-       // merge order to show contemporaneous changes in parallel branches
-       // occurring after "local" changes. This is useful for a developer
-       // when a developer wants to see all changes that were incorporated
-       // into the same merge as her own changes occur after her own
-       // changes.
-       //
+       /*
+        * TODO: By sorting the parents in a different order, we can alter the
+        * merge order to show contemporaneous changes in parallel branches
+        * occurring after "local" changes. This is useful for a developer
+        * when a developer wants to see all changes that were incorporated
+        * into the same merge as her own changes occur after her own
+        * changes.
+        */
 
-       while (reversed_parents) {
-
-               struct commit *parent = pop_commit(&reversed_parents);
+       for (parents = head->parents; parents; parents = parents->next) {
+               struct commit *parent = parents->item;
 
                if (head->object.flags & UNINTERESTING) {
-                       // propagates the uninteresting bit to
-                       // all parents. if we have already visited
-                       // this parent, then the uninteresting bit
-                       // will be propagated to each reachable 
-                       // commit that is still not marked uninteresting
-                       // and won't otherwise be reached.
+                       /*
+                        * Propagates the uninteresting bit to all parents.
+                        * if we have already visited this parent, then
+                        * the uninteresting bit will be propagated to each
+                        * reachable commit that is still not marked
+                        * uninteresting and won't otherwise be reached.
+                        */
                        mark_ancestors_uninteresting(parent);
                }
 
                if (!(parent->object.flags & VISITED)) {
                        if (parent->object.flags & BOUNDARY) {
-
                                if (*stack) {
-                                       die("something else is on the stack - %s\n", sha1_to_hex((*stack)->item->object.sha1));
+                                       die("something else is on the stack - %s",
+                                           sha1_to_hex((*stack)->item->object.sha1));
                                }
-
                                push_onto_merge_order_stack(stack, parent);
                                parent->object.flags |= VISITED;
 
                        } else {
-
                                sort_first_epoch(parent, stack);
-
-                               if (reversed_parents) {
-                                       //
-                                       // this indicates a possible discontinuity
-                                       // it may not be be actual discontinuity if
-                                       // the head of parent N happens to be the tail
-                                       // of parent N+1
-                                       //
-                                       // the next push onto the stack will resolve the 
-                                       // question
-                                       //
+                               if (parents) {
+                                       /*
+                                        * This indicates a possible
+                                        * discontinuity it may not be be
+                                        * actual discontinuity if the head
+                                        * of parent N happens to be the tail
+                                        * of parent N+1.
+                                        *
+                                        * The next push onto the stack will
+                                        * resolve the question.
+                                        */
                                        (*stack)->item->object.flags |= DISCONTINUITY;
                                }
                        }
@@ -522,26 +481,25 @@ static void sort_first_epoch(struct commit *head, struct commit_list **stack)
        push_onto_merge_order_stack(stack, head);
 }
 
-//
-// Emit the contents of the stack. 
-//
-// The stack is freed and replaced by NULL.
-//
-// Sets the return value to STOP if no further output should be generated.
-//
-static int emit_stack(struct commit_list **stack, emitter_func emitter)
+/*
+ * Emit the contents of the stack.
+ *
+ * The stack is freed and replaced by NULL.
+ *
+ * Sets the return value to STOP if no further output should be generated.
+ */
+static int emit_stack(struct commit_list **stack, emitter_func emitter, int include_last)
 {
        unsigned int seen = 0;
        int action = CONTINUE;
 
        while (*stack && (action != STOP)) {
-
                struct commit *next = pop_commit(stack);
-
                seen |= next->object.flags;
-
-               if (*stack) {
-                       action = (*emitter) (next);
+               if (*stack || include_last) {
+                       if (!*stack) 
+                               next->object.flags |= BOUNDARY;
+                       action = emitter(next);
                }
        }
 
@@ -553,13 +511,13 @@ static int emit_stack(struct commit_list **stack, emitter_func emitter)
        return (action == STOP || (seen & UNINTERESTING)) ? STOP : CONTINUE;
 }
 
-//
-// Sorts an arbitrary epoch into merge order by sorting each epoch
-// of its epoch sequence into order.
-//
-// Note: this algorithm currently leaves traces of its execution in the
-// object flags of nodes it discovers. This should probably be fixed.
-//
+/*
+ * Sorts an arbitrary epoch into merge order by sorting each epoch
+ * of its epoch sequence into order.
+ *
+ * Note: this algorithm currently leaves traces of its execution in the
+ * object flags of nodes it discovers. This should probably be fixed.
+ */
 static int sort_in_merge_order(struct commit *head_of_epoch, emitter_func emitter)
 {
        struct commit *next = head_of_epoch;
@@ -568,30 +526,27 @@ static int sort_in_merge_order(struct commit *head_of_epoch, emitter_func emitte
 
        ret = parse_commit(head_of_epoch);
 
-       while (next && next->parents && !ret && (action != STOP)) {
+       next->object.flags |= BOUNDARY;
 
+       while (next && next->parents && !ret && (action != STOP)) {
                struct commit *base = NULL;
 
-               if ((ret = find_next_epoch_boundary(next, &base)))
+               ret = find_next_epoch_boundary(next, &base);
+               if (ret)
                        return ret;
-
                next->object.flags |= BOUNDARY;
-               if (base) {
+               if (base)
                        base->object.flags |= BOUNDARY;
-               }
 
                if (HAS_EXACTLY_ONE_PARENT(next)) {
-
                        while (HAS_EXACTLY_ONE_PARENT(next)
                               && (action != STOP)
                               && !ret) {
-
                                if (next->object.flags & UNINTERESTING) {
                                        action = STOP;
                                } else {
-                                       action = (*emitter) (next);
+                                       action = emitter(next);
                                }
-
                                if (action != STOP) {
                                        next = next->parents->item;
                                        ret = parse_commit(next);
@@ -599,89 +554,81 @@ static int sort_in_merge_order(struct commit *head_of_epoch, emitter_func emitte
                        }
 
                } else {
-
                        struct commit_list *stack = NULL;
                        sort_first_epoch(next, &stack);
-                       action = emit_stack(&stack, emitter);
+                       action = emit_stack(&stack, emitter, (base == NULL));
                        next = base;
-
                }
-
        }
 
        if (next && (action != STOP) && !ret) {
-               (*emitter) (next);
+               emitter(next);
        }
 
        return ret;
 }
 
-//
-// Sorts the nodes reachable from a starting list in merge order, we 
-// first find the base for the starting list and then sort all nodes in this 
-// subgraph using the sort_first_epoch algorithm. Once we have reached the base
-// we can continue sorting using sort_in_merge_order.
-//
+/*
+ * Sorts the nodes reachable from a starting list in merge order, we
+ * first find the base for the starting list and then sort all nodes
+ * in this subgraph using the sort_first_epoch algorithm. Once we have
+ * reached the base we can continue sorting using sort_in_merge_order.
+ */
 int sort_list_in_merge_order(struct commit_list *list, emitter_func emitter)
 {
        struct commit_list *stack = NULL;
        struct commit *base;
-
        int ret = 0;
        int action = CONTINUE;
-
        struct commit_list *reversed = NULL;
 
-       for (; list; list = list->next) {
-
-               struct commit *next = list->item;
-
-               if (!(next->object.flags & UNINTERESTING)) {
-                       if (next->object.flags & DUPCHECK) {
-                               fprintf(stderr, "%s: duplicate commit %s ignored\n", __FUNCTION__, sha1_to_hex(next->object.sha1));
-                       } else {
-                               next->object.flags |= DUPCHECK;
-                               commit_list_insert(list->item, &reversed);
-                       }
-               }
-       }
-
-       if (!reversed->next) {
-
-               // if there is only one element in the list, we can sort it using 
-               // sort_in_merge_order.
+       for (; list; list = list->next)
+               commit_list_insert(list->item, &reversed);
 
+       if (!reversed)
+               return ret;
+       else if (!reversed->next) {
+               /*
+                * If there is only one element in the list, we can sort it
+                * using sort_in_merge_order.
+                */
                base = reversed->item;
-
        } else {
-
-               // otherwise, we search for the base of the list
-
-               if ((ret = find_base_for_list(reversed, &base)))
+               /*
+                * Otherwise, we search for the base of the list.
+                */
+               ret = find_base_for_list(reversed, &base);
+               if (ret)
                        return ret;
-
-               if (base) {
+               if (base)
                        base->object.flags |= BOUNDARY;
-               }
 
                while (reversed) {
-                       sort_first_epoch(pop_commit(&reversed), &stack);
-                       if (reversed) {
-                               //
-                               // if we have more commits to push, then the
-                               // first push for the next parent may (or may not)
-                               // represent a discontinuity with respect to the
-                               // parent currently on the top of the stack.
-                               //
-                               // mark it for checking here, and check it
-                               // with the next push...see sort_first_epoch for
-                               // more details.
-                               //
-                               stack->item->object.flags |= DISCONTINUITY;
+                       struct commit * next = pop_commit(&reversed);
+
+                       if (!(next->object.flags & VISITED) && next!=base) {
+                               sort_first_epoch(next, &stack);
+                               if (reversed) {
+                                       /*
+                                        * If we have more commits 
+                                        * to push, then the first
+                                        * push for the next parent may 
+                                        * (or may * not) represent a 
+                                        * discontinuity with respect
+                                        * to the parent currently on 
+                                        * the top of the stack.
+                                        *
+                                        * Mark it for checking here, 
+                                        * and check it with the next 
+                                        * push. See sort_first_epoch()
+                                        * for more details.
+                                        */
+                                       stack->item->object.flags |= DISCONTINUITY;
+                               }
                        }
                }
 
-               action = emit_stack(&stack, emitter);
+               action = emit_stack(&stack, emitter, (base==NULL));
        }
 
        if (base && (action != STOP)) {