reflog-walk: don't segfault on non-commit sha1's in the reflog
authorDennis Kaarsemaker <dennis@kaarsemaker.net>
Tue, 5 Jan 2016 21:12:10 +0000 (22:12 +0100)
committerJunio C Hamano <gitster@pobox.com>
Tue, 5 Jan 2016 21:41:06 +0000 (13:41 -0800)
git reflog (ab)uses the log machinery to display its list of log
entries. To do so it must fake commit parent information for the log
walker.

For refs in refs/heads this is no problem, as they should only ever
point to commits. Tags and other refs however can point to anything,
thus their reflog may contain non-commit objects.

To avoid segfaulting, we check whether reflog entries are commits before
feeding them to the log walker and skip any non-commits. This means that
git reflog output will be incomplete for such refs, but that's one step
up from segfaulting. A more complete solution would be to decouple git
reflog from the log walker machinery.

Signed-off-by: Dennis Kaarsemaker <dennis@kaarsemaker.net>
Helped-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
reflog-walk.c
t/t1410-reflog.sh

index f8e743a..d3cc524 100644 (file)
@@ -222,6 +222,7 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit)
        struct commit_info *commit_info =
                get_commit_info(commit, &info->reflogs, 0);
        struct commit_reflog *commit_reflog;
+       struct object *logobj;
        struct reflog_info *reflog;
 
        info->last_commit_reflog = NULL;
@@ -233,15 +234,20 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit)
                commit->parents = NULL;
                return;
        }
-
-       reflog = &commit_reflog->reflogs->items[commit_reflog->recno];
        info->last_commit_reflog = commit_reflog;
-       commit_reflog->recno--;
-       commit_info->commit = (struct commit *)parse_object(reflog->osha1);
-       if (!commit_info->commit) {
+
+       do {
+               reflog = &commit_reflog->reflogs->items[commit_reflog->recno];
+               commit_reflog->recno--;
+               logobj = parse_object(reflog->osha1);
+       } while (commit_reflog->recno && (logobj && logobj->type != OBJ_COMMIT));
+
+       if (!logobj || logobj->type != OBJ_COMMIT) {
+               commit_info->commit = NULL;
                commit->parents = NULL;
                return;
        }
+       commit_info->commit = (struct commit *)logobj;
 
        commit->parents = xcalloc(1, sizeof(struct commit_list));
        commit->parents->item = commit_info->commit;
index b79049f..17a194b 100755 (executable)
@@ -325,4 +325,17 @@ test_expect_success 'parsing reverse reflogs at BUFSIZ boundaries' '
        test_cmp expect actual
 '
 
+test_expect_success 'no segfaults for reflog containing non-commit sha1s' '
+       git update-ref --create-reflog -m "Creating ref" \
+               refs/tests/tree-in-reflog HEAD &&
+       git update-ref -m "Forcing tree" refs/tests/tree-in-reflog HEAD^{tree} &&
+       git update-ref -m "Restoring to commit" refs/tests/tree-in-reflog HEAD &&
+       git reflog refs/tests/tree-in-reflog
+'
+
+test_expect_failure 'reflog with non-commit entries displays all entries' '
+       git reflog refs/tests/tree-in-reflog >actual &&
+       test_line_count = 3 actual
+'
+
 test_done