Skip to content

Commit 838f9a1

Browse files
Thomas Rastgitster
authored andcommitted
log: use true parents for diff when walking reflogs
The reflog walking logic (git log -g) replaces the true parent list with the preceding commit in the reflog. This results in bogus commit diffs when combined with options such as -p; the diff is against the reflog predecessor, not the parent of the commit. Save the true parents on the side, extending the functions from the previous commit. The diff logic picks them up and uses them to show the correct diffs. We do have to be somewhat careful about repeated calling of save_parents(), since the reflog may list a commit more than once. We now store (commit_list*)-1 to distinguish the "not saved yet" and "root commit" cases. This lets us preserve an empty parent list even if save_parents() is repeatedly called. Suggested-by: Jeff King <[email protected]> Signed-off-by: Thomas Rast <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 53d00b3 commit 838f9a1

File tree

2 files changed

+47
-3
lines changed

2 files changed

+47
-3
lines changed

revision.c

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2848,6 +2848,7 @@ static struct commit *get_revision_1(struct rev_info *revs)
28482848
free(entry);
28492849

28502850
if (revs->reflog_info) {
2851+
save_parents(revs, commit);
28512852
fake_reflog_parent(revs->reflog_info, commit);
28522853
commit->object.flags &= ~(ADDED | SEEN | SHOWN);
28532854
}
@@ -3083,6 +3084,8 @@ void put_revision_mark(const struct rev_info *revs, const struct commit *commit)
30833084

30843085
define_commit_slab(saved_parents, struct commit_list *);
30853086

3087+
#define EMPTY_PARENT_LIST ((struct commit_list *)-1)
3088+
30863089
void save_parents(struct rev_info *revs, struct commit *commit)
30873090
{
30883091
struct commit_list **pp;
@@ -3093,16 +3096,35 @@ void save_parents(struct rev_info *revs, struct commit *commit)
30933096
}
30943097

30953098
pp = saved_parents_at(revs->saved_parents_slab, commit);
3096-
assert(*pp == NULL);
3097-
*pp = copy_commit_list(commit->parents);
3099+
3100+
/*
3101+
* When walking with reflogs, we may visit the same commit
3102+
* several times: once for each appearance in the reflog.
3103+
*
3104+
* In this case, save_parents() will be called multiple times.
3105+
* We want to keep only the first set of parents. We need to
3106+
* store a sentinel value for an empty (i.e., NULL) parent
3107+
* list to distinguish it from a not-yet-saved list, however.
3108+
*/
3109+
if (*pp)
3110+
return;
3111+
if (commit->parents)
3112+
*pp = copy_commit_list(commit->parents);
3113+
else
3114+
*pp = EMPTY_PARENT_LIST;
30983115
}
30993116

31003117
struct commit_list *get_saved_parents(struct rev_info *revs, const struct commit *commit)
31013118
{
3119+
struct commit_list *parents;
3120+
31023121
if (!revs->saved_parents_slab)
31033122
return commit->parents;
31043123

3105-
return *saved_parents_at(revs->saved_parents_slab, commit);
3124+
parents = *saved_parents_at(revs->saved_parents_slab, commit);
3125+
if (parents == EMPTY_PARENT_LIST)
3126+
return NULL;
3127+
return parents;
31063128
}
31073129

31083130
void free_saved_parents(struct rev_info *revs)

t/t1411-reflog-show.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,26 @@ test_expect_success 'empty reflog file' '
144144
test_cmp expect actual
145145
'
146146

147+
# This guards against the alternative of showing the diffs vs. the
148+
# reflog ancestor. The reflog used is designed to list the commits
149+
# more than once, so as to exercise the corresponding logic.
150+
test_expect_success 'git log -g -p shows diffs vs. parents' '
151+
test_commit two &&
152+
git branch flipflop &&
153+
git update-ref refs/heads/flipflop -m flip1 HEAD^ &&
154+
git update-ref refs/heads/flipflop -m flop1 HEAD &&
155+
git update-ref refs/heads/flipflop -m flip2 HEAD^ &&
156+
git log -g -p flipflop >reflog &&
157+
grep -v ^Reflog reflog >actual &&
158+
git log -1 -p HEAD^ >log.one &&
159+
git log -1 -p HEAD >log.two &&
160+
(
161+
cat log.one; echo
162+
cat log.two; echo
163+
cat log.one; echo
164+
cat log.two
165+
) >expect &&
166+
test_cmp expect actual
167+
'
168+
147169
test_done

0 commit comments

Comments
 (0)