interpret_nth_last_branch(): avoid traversing the reflog twice

You can have quite a many reflog entries, but you typically won't recall
which branch you were on after switching branches for more than several
times.

Instead of reading the reflog twice, this reads the branch switching event
and keeps as many entries as the user asked from the latest such entries,
which is the minimum required to be able to switch back to the branch we
were recently on.

[jc: improvements from Dscho squashed in]

Signed-off-by: Junio C Hamano <gitster@pobox.com>
diff --git a/sha1_name.c b/sha1_name.c
index 9e1538e..d6972f2 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -685,8 +685,7 @@
 }
 
 struct grab_nth_branch_switch_cbdata {
-	int counting;
-	int nth;
+	long cnt, alloc;
 	struct strbuf *buf;
 };
 
@@ -697,6 +696,7 @@
 	struct grab_nth_branch_switch_cbdata *cb = cb_data;
 	const char *match = NULL, *target = NULL;
 	size_t len;
+	int nth;
 
 	if (!prefixcmp(message, "checkout: moving from ")) {
 		match = message + strlen("checkout: moving from ");
@@ -711,16 +711,9 @@
 	if (target[len] == '\n' && !strncmp(match, target, len))
 		return 0;
 
-	if (cb->counting) {
-		cb->nth++;
-		return 0;
-	}
-
-	if (cb->nth-- <= 0) {
-		strbuf_reset(cb->buf);
-		strbuf_add(cb->buf, match, len);
-		return 1;
-	}
+	nth = cb->cnt++ % cb->alloc;
+	strbuf_reset(&cb->buf[nth]);
+	strbuf_add(&cb->buf[nth], match, len);
 	return 0;
 }
 
@@ -737,7 +730,8 @@
  */
 int interpret_nth_last_branch(const char *name, struct strbuf *buf)
 {
-	int nth;
+	long nth;
+	int i;
 	struct grab_nth_branch_switch_cbdata cb;
 	const char *brace;
 	char *num_end;
@@ -750,19 +744,22 @@
 	nth = strtol(name+3, &num_end, 10);
 	if (num_end != brace)
 		return -1;
-
-	cb.counting = 1;
-	cb.nth = 0;
-	cb.buf = buf;
+	if (nth <= 0)
+		return -1;
+	cb.alloc = nth;
+	cb.buf = xmalloc(nth * sizeof(struct strbuf));
+	for (i = 0; i < nth; i++)
+		strbuf_init(&cb.buf[i], 20);
+	cb.cnt = 0;
 	for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb);
-
-	if (cb.nth < nth)
+	if (cb.cnt < nth)
 		return 0;
-
-	cb.counting = 0;
-	cb.nth -= nth;
-	cb.buf = buf;
-	for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb);
+	i = cb.cnt % nth;
+	strbuf_reset(buf);
+	strbuf_add(buf, cb.buf[i].buf, cb.buf[i].len);
+	for (i = 0; i < nth; i++)
+		strbuf_release(&cb.buf[i]);
+	free(cb.buf);
 
 	return brace-name+1;
 }