On Thu, Jul 06, 2017 at 03:16:06AM -0400, Jeff King wrote:

> If we think it's buggy, we can fix it now. But I'm not convinced that
> sequential iteration is that bad. It's at least _simple_ and easy to
> explain. The only other thing that would make sense to me is sorting the
> entries by date (reflog date, not commit date) and showing them in that
> order. But that gives rise to some funny corner cases.
> 
> What do we do if there's clock skew within the reflog (e.g., somebody
> fixed their skewed clock such that HEAD@{5} is less recent than
> HEAD@{6}). Do we show them out of order then (even if only a single
> reflog is being shown?).
> 
> Or do we try some complicated sort where entries compare sequentially
> within a given reflog, but use date comparisons between different logs?
> I don't think that can work with a normal comparison sort, as it makes
> the comparison non-transitive.
> 
> But maybe we could do a pseudo-list-merge, where we treat the reflogs as
> queues and always pick from the queue with the most recent timestamp.
> 
> That would probably be pretty easy to retrofit on the iteration from the
> current series.

Something like the patch below. Looking over the output, the
interleaving makes it look confusing (especially if you don't use
--date, since then the interleaved numbers have no relation to each
other).

It would help if we actually had a use case where somebody really did
want to show two reflogs from a single command. Then we'd know what they
were expecting and which output would be most useful, at least for that
case. But I can't really think of why you'd want to do so (which is
probably why nobody noticed for years that the current behavior is so
broken).

---
diff --git a/reflog-walk.c b/reflog-walk.c
index a7644d944e..6cdf7bfb0f 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -90,7 +90,7 @@ struct commit_reflog {
 
 struct reflog_walk_info {
        struct commit_reflog **logs;
-       size_t nr, alloc, cur;
+       size_t nr, alloc;
        struct string_list complete_reflogs;
        struct commit_reflog *last_commit_reflog;
 };
@@ -284,26 +284,43 @@ int reflog_walk_empty(struct reflog_walk_info *info)
        return !info || !info->nr;
 }
 
-struct commit *next_entry_in_log(struct commit_reflog *log)
+static void skip_unusable_entries(struct commit_reflog *log)
 {
        while (log->recno >= 0) {
-               struct reflog_info *entry = &log->reflogs->items[log->recno--];
+               struct reflog_info *entry = &log->reflogs->items[log->recno];
                struct object *obj = parse_object(&entry->noid);
 
                if (obj && obj->type == OBJ_COMMIT)
-                       return (struct commit *)obj;
+                       return;
+               log->recno--;
        }
-       return NULL;
+}
+
+static timestamp_t next_timestamp(struct commit_reflog *log)
+{
+       return log->reflogs->items[log->recno].timestamp;
 }
 
 struct commit *next_reflog_entry(struct reflog_walk_info *walk)
 {
-       for (; walk->cur < walk->nr; walk->cur++) {
-               struct commit *ret = next_entry_in_log(walk->logs[walk->cur]);
-               if (ret) {
-                       walk->last_commit_reflog = walk->logs[walk->cur];
-                       return ret;
-               }
+       struct commit_reflog *best = NULL;
+       size_t i;
+
+       for (i = 0; i < walk->nr; i++) {
+               struct commit_reflog *log = walk->logs[i];
+
+               skip_unusable_entries(log);
+               if (log->recno < 0)
+                       continue;
+
+               if (!best || next_timestamp(log) > next_timestamp(best))
+                       best = log;
        }
+
+       if (best) {
+               walk->last_commit_reflog = best;
+               return lookup_commit(&best->reflogs->items[best->recno--].noid);
+       }
+
        return NULL;
 }

The implementation is fairly naive. It would be O(nr_reflogs *
nr_entries) in the worst case. I wouldn't expect nr_reflogs to get
large. But if we cared, we could fix it by keeping the reflogs in a
heap.

-Peff

Reply via email to