git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: Thomas Rast <trast@inf.ethz.ch>
To: Jeff King <peff@peff.net>
Cc: "Junio C Hamano" <gitster@pobox.com>,
	git@vger.kernel.org,
	"Uwe Kleine-König" <u.kleine-koenig@pengutronix.de>,
	"Ramsay Jones" <ramsay@ramsay1.demon.co.uk>
Subject: [PATCH] log: use true parents for diff when walking reflogs
Date: Sat, 3 Aug 2013 12:36:15 +0200	[thread overview]
Message-ID: <d6dadc4ab54d81490ca46bcfbd44a61be24f6eb7.1375524982.git.trast@inf.ethz.ch> (raw)

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 <peff@peff.net>
Signed-off-by: Thomas Rast <trast@inf.ethz.ch>
---

Jeff King <peff@peff.net> wrote:
> 
> Your description (and solution) make a lot of sense to me. Another code
> path that has a similar problem is the "-g" reflog walker. It rewrites
> the parents based on the reflog, and the diffs it produces are mostly
> useless (e.g., try "git stash list -p").
> 
> Should we be applying the same technique there?

Good point.  This is how.  It applies on top of the other patch.

It doesn't really help for 'git stash list -p', though, because
stashes are merge commits.  Now they just don't show anything.  You
could try 'git stash list -p -m', though.


 revision.c             | 28 +++++++++++++++++++++++++---
 t/t1411-reflog-show.sh | 22 ++++++++++++++++++++++
 2 files changed, 47 insertions(+), 3 deletions(-)

diff --git a/revision.c b/revision.c
index e3ca936..ac20d1a 100644
--- a/revision.c
+++ b/revision.c
@@ -2848,6 +2848,7 @@ static struct commit *get_revision_1(struct rev_info *revs)
 		free(entry);
 
 		if (revs->reflog_info) {
+			save_parents(revs, commit);
 			fake_reflog_parent(revs->reflog_info, commit);
 			commit->object.flags &= ~(ADDED | SEEN | SHOWN);
 		}
@@ -3083,6 +3084,8 @@ void put_revision_mark(const struct rev_info *revs, const struct commit *commit)
 
 define_commit_slab(saved_parents, struct commit_list *);
 
+#define EMPTY_PARENT_LIST ((struct commit_list *)-1)
+
 void save_parents(struct rev_info *revs, struct commit *commit)
 {
 	struct commit_list **pp;
@@ -3093,16 +3096,35 @@ void save_parents(struct rev_info *revs, struct commit *commit)
 	}
 
 	pp = saved_parents_at(revs->saved_parents_slab, commit);
-	assert(*pp == NULL);
-	*pp = copy_commit_list(commit->parents);
+
+	/*
+	 * When walking with reflogs, we may visit the same commit
+	 * several times: once for each appearance in the reflog.
+	 *
+	 * In this case, save_parents() will be called multiple times.
+	 * We want to keep only the first set of parents.  We need to
+	 * store a sentinel value for an empty (i.e., NULL) parent
+	 * list to distinguish it from a not-yet-saved list, however.
+	 */
+	if (*pp)
+		return;
+	if (commit->parents)
+		*pp = copy_commit_list(commit->parents);
+	else
+		*pp = EMPTY_PARENT_LIST;
 }
 
 struct commit_list *get_saved_parents(struct rev_info *revs, const struct commit *commit)
 {
+	struct commit_list *parents;
+
 	if (!revs->saved_parents_slab)
 		return commit->parents;
 
-	return *saved_parents_at(revs->saved_parents_slab, commit);
+	parents = *saved_parents_at(revs->saved_parents_slab, commit);
+	if (parents == EMPTY_PARENT_LIST)
+		return NULL;
+	return parents;
 }
 
 void free_saved_parents(struct rev_info *revs)
diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh
index 9a105fe..6f47c0d 100755
--- a/t/t1411-reflog-show.sh
+++ b/t/t1411-reflog-show.sh
@@ -144,4 +144,26 @@ test_expect_success 'empty reflog file' '
 	test_cmp expect actual
 '
 
+# This guards against the alternative of showing the diffs vs. the
+# reflog ancestor.  The reflog used is designed to list the commits
+# more than once, so as to exercise the corresponding logic.
+test_expect_success 'git log -g -p shows diffs vs. parents' '
+	test_commit two &&
+	git branch flipflop &&
+	git update-ref refs/heads/flipflop -m flip1 HEAD^ &&
+	git update-ref refs/heads/flipflop -m flop1 HEAD &&
+	git update-ref refs/heads/flipflop -m flip2 HEAD^ &&
+	git log -g -p flipflop >reflog &&
+	grep -v ^Reflog reflog >actual &&
+	git log -1 -p HEAD^ >log.one &&
+	git log -1 -p HEAD >log.two &&
+	(
+		cat log.one; echo
+		cat log.two; echo
+		cat log.one; echo
+		cat log.two
+	) >expect &&
+	test_cmp expect actual
+'
+
 test_done
-- 
1.8.4.rc1.414.g34bc5b2

             reply	other threads:[~2013-08-03 10:36 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-08-03 10:36 Thomas Rast [this message]
2013-08-03 10:40 ` [PATCH] log: use true parents for diff when walking reflogs Thomas Rast
2013-08-05 19:16 ` Junio C Hamano

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=d6dadc4ab54d81490ca46bcfbd44a61be24f6eb7.1375524982.git.trast@inf.ethz.ch \
    --to=trast@inf.ethz.ch \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=peff@peff.net \
    --cc=ramsay@ramsay1.demon.co.uk \
    --cc=u.kleine-koenig@pengutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).