threads / rfc / 6092

[RFC] git reflog show

Subject: [RFC] git reflog show

## tl;dr

14 messages between Dec 23, 2006 and Dec 26, 2006.

replies: 13people: 3as markdown or json

Shawn Pearce· Dec 23, 2006, 10:19 UTC · lore

I'm toying with what a 'git reflog show' might look like. I attached the prototype code below. Here's the output from the recent part of my mmap pack window topic:

  $ ./git-reflog show refs/heads/sp/mapwin
  ...clipped for space...
  
  ref sp/mapwin@{10}
  commit f6ec09f.. -> 1960ea7..
  User: Shawn O. Pearce <spearce@spearce.org>
  Date: Sat Dec 23 01:46:07 2006 -0500
  
      commit (amend): Support unmapping windows on 'temporary' packfiles.
  
  ref sp/mapwin@{9}
  commit 1960ea7.. -> e70352c..
  User: Shawn O. Pearce <spearce@spearce.org>
  Date: Sat Dec 23 01:49:10 2006 -0500
  
      commit: Create pack_report() as a debugging aid.
  
  ref sp/mapwin@{8}
  commit e70352c.. -> 161cd2d..
  User: Shawn O. Pearce <spearce@spearce.org>
  Date: Sat Dec 23 01:53:48 2006 -0500
  
      commit (amend): Create pack_report() as a debugging aid.
  
  ref sp/mapwin@{7}
  commit 161cd2d.. -> 81e4d26..
  User: Shawn O. Pearce <spearce@spearce.org>
  Date: Sat Dec 23 01:58:03 2006 -0500
  
      reset --hard 
  
  ref sp/mapwin@{6}
  commit 81e4d26.. -> e69e59b..
  User: Shawn O. Pearce <spearce@spearce.org>
  Date: Sat Dec 23 01:58:44 2006 -0500
  
      commit (amend): Fully activate the sliding window pack access.
  
  ref sp/mapwin@{5}
  commit e69e59b.. -> 11bedbb..
  User: Shawn O. Pearce <spearce@spearce.org>
  Date: Sat Dec 23 01:58:51 2006 -0500
  
      am: Load core configuration in git-verify-pack.
  
  ref sp/mapwin@{4}
  commit 11bedbb.. -> 88035fd..
  User: Shawn O. Pearce <spearce@spearce.org>
  Date: Sat Dec 23 01:58:52 2006 -0500
  
      am: Ensure core.packedGitWindowSize cannot be less than 2 pages.
  
  ref sp/mapwin@{3}
  commit 88035fd.. -> b3b706e..
  User: Shawn O. Pearce <spearce@spearce.org>
  Date: Sat Dec 23 01:58:52 2006 -0500
  
      am: Improve error message when packfile mmap fails.
  
  ref sp/mapwin@{2}
  commit b3b706e.. -> 62c8beb..
  User: Shawn O. Pearce <spearce@spearce.org>
  Date: Sat Dec 23 01:58:52 2006 -0500
  
      am: Support unmapping windows on 'temporary' packfiles.
  
  ref sp/mapwin@{1}
  commit 62c8beb.. -> 6b43fd0..
  User: Shawn O. Pearce <spearce@spearce.org>
  Date: Sat Dec 23 01:58:52 2006 -0500
  
      am: Create pack_report() as a debugging aid.
  
  ref sp/mapwin@{0}
  commit 6b43fd0.. -> bbdc8fa..
  User: Shawn O. Pearce <spearce@spearce.org>
  Date: Sat Dec 23 02:06:23 2006 -0500
  
      commit: Test suite for sliding window mmap implementation.
  
diff --git a/builtin-reflog.c b/builtin-reflog.c
index d4f7353..7156a2c 100644
--- a/builtin-reflog.c
+++ b/builtin-reflog.c
@@ -161,8 +161,173 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 	return status;
 }
 
+struct cmd_reflog_list_ref
+{
+	const char *name;
+	unsigned int ent_cnt;
+};
+
+struct cmd_reflog_list_ent
+{
+	struct cmd_reflog_list_ent* next;
+	struct cmd_reflog_list_ref *ref;
+	const char *cmd;
+	char *user;
+	unsigned long timestamp;
+	int tz;
+	unsigned int pos;
+	unsigned char old_sha1[20];
+	unsigned char new_sha1[20];
+};
+
+struct cmd_reflog_list_cb
+{
+	struct cmd_reflog_list_ent* entries;
+	struct cmd_reflog_list_ref* cur_ref;
+	unsigned int ref_cnt;
+	unsigned int ent_cnt;
+};
+
+static int list_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+		char *data, void *cb_data)
+{
+	struct cmd_reflog_list_cb *cb = cb_data;
+	struct cmd_reflog_list_ent *e;
+	unsigned long timestamp;
+	int tz;
+	char *user_end, *cp, *ep;
+
+	cp = strchr(data, '>');
+	if (!cp || *++cp != ' ')
+		return 0; /* corrupt line? */
+	user_end = cp;
+	timestamp = strtoul(cp, &ep, 10);
+	if (*ep++ != ' ')
+		return 0; /* corrupt line? */
+	tz = strtol(ep, &cp, 10);
+	if (*cp++ != '\t')
+		return 0; /* corrupt line? */
+
+	e = xmalloc(sizeof(*e));
+	e->ref = cb->cur_ref;
+	e->cmd = xstrdup(cp);
+	e->user = xmalloc(user_end - data + 1);
+	strncpy(e->user, data, user_end - data);
+	e->user[user_end - data] = 0;
+	e->timestamp = timestamp;
+	e->tz = tz;
+	e->pos = cb->cur_ref->ent_cnt++;
+	hashcpy(e->old_sha1, osha1);
+	hashcpy(e->new_sha1, nsha1);
+	e->next = cb->entries;
+	cb->entries = e;
+	cb->ent_cnt++;
+	return 0;
+}
+
+static int list_reflog(const char *ref, const unsigned char *sha1,
+		int unused, void *cb_data)
+{
+	struct cmd_reflog_list_cb *cb = cb_data;
+
+	cb->ref_cnt++;
+	cb->cur_ref = xmalloc(sizeof(*cb->cur_ref));
+	cb->cur_ref->name = xstrdup(ref);
+	cb->cur_ref->ent_cnt = 0;
+	for_each_reflog_ent(ref, list_reflog_ent, cb);
+	return 0;
+}
+
+static int list_cmp(const void *_a, const void *_b)
+{
+	const struct cmd_reflog_list_ent *a = *((void**)_a);
+	const struct cmd_reflog_list_ent *b = *((void**)_b);
+	if (a->ref == b->ref)
+		return a->pos < b->pos ? -1 : 1;
+	if (a->timestamp < b->timestamp)
+		return -1;
+	if (a->timestamp == b->timestamp)
+		return 0;
+	return 1;
+}
+
+static void reflog_show(struct cmd_reflog_list_ent *e)
+{
+	const char *ref = e->ref->name;
+	if (!strncmp("refs/heads/", ref, 11))
+		ref += 11;
+	printf("ref %s@{%u}\n", ref, e->ref->ent_cnt - e->pos - 1);
+	printf("commit ");
+	if (is_null_sha1(e->old_sha1))
+		printf("deleted");
+	else
+		printf("%s..", find_unique_abbrev(e->old_sha1, DEFAULT_ABBREV));
+	printf(" -> ");
+	if (is_null_sha1(e->new_sha1))
+		printf("deleted");
+	else
+		printf("%s..", find_unique_abbrev(e->new_sha1, DEFAULT_ABBREV));
+	printf("\n");
+	printf("User: %s\n", e->user);
+	printf("Date: %s\n", show_date(e->timestamp, e->tz, 0));
+	printf("\n");
+	printf("    %s\n", e->cmd);
+}
+
+static const char reflog_show_usage[] =
+"git-reflog show [--all|<refs>...]";
+
+static int cmd_reflog_show(int argc, const char **argv, const char *prefix)
+{
+	struct cmd_reflog_list_cb cb;
+	unsigned char sha1[20];
+	int i, do_all = 0, status = 0;
+	struct cmd_reflog_list_ent **ent_arr;
+	struct cmd_reflog_list_ent *e;
+
+	cb.ref_cnt = 0;
+	cb.ent_cnt = 0;
+	cb.entries = NULL;
+
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+		if (!strcmp(arg, "--all"))
+			do_all = 1;
+		else if (arg[0] == '-')
+			usage(reflog_show_usage);
+		else
+			break;
+	}
+	if (do_all)
+		status |= for_each_ref(list_reflog, &cb);
+	while (i < argc) {
+		const char *ref = argv[i++];
+		if (!resolve_ref(ref, sha1, 1, NULL))
+			status |= error("no such ref: %s", ref);
+		else
+			status |= list_reflog(ref, sha1, 0, &cb);
+	}
+	if (!cb.ref_cnt) {
+		const char *ref = "HEAD";
+		if (!resolve_ref(ref, sha1, 1, NULL))
+			status |= error("no such ref: %s", ref);
+		else
+			status |= list_reflog(ref, sha1, 0, &cb);
+	}
+
+	ent_arr = xmalloc(cb.ent_cnt * sizeof(*ent_arr));
+	for (i = 0, e = cb.entries; e; i++, e = e->next)
+		ent_arr[i] = e;
+	qsort(ent_arr, cb.ent_cnt, sizeof(*ent_arr), list_cmp);
+
+	for (i = 0; i < cb.ent_cnt; i++)
+		reflog_show(ent_arr[i]);
+
+	return status;
+}
+
 static const char reflog_usage[] =
-"git-reflog (expire | ...)";
+"git-reflog (expire | show | ...)";
 
 int cmd_reflog(int argc, const char **argv, const char *prefix)
 {
@@ -170,6 +335,8 @@ int cmd_reflog(int argc, const char **argv, const char *prefix)
 		usage(reflog_usage);
 	else if (!strcmp(argv[1], "expire"))
 		return cmd_reflog_expire(argc - 1, argv + 1, prefix);
+	else if (!strcmp(argv[1], "show"))
+		return cmd_reflog_show(argc - 1, argv + 1, prefix);
 	else
 		usage(reflog_usage);
 }
Junio C Hamano· Dec 23, 2006, 10:42 UTC · re: Shawn Pearce · lore

Re: [RFC] git reflog show

Shawn Pearce <spearce@spearce.org> writes:
Show 7 quoted lines
> I'm toying with what a 'git reflog show' might look like.  I attached
> the prototype code below.  Here's the output from the recent part of
> my mmap pack window topic:
>
>   $ ./git-reflog show refs/heads/sp/mapwin
>   ...clipped for space...
>   

The output should look almost the same as "git log" for the part that represents a series of normal branch activity where commits are built up one-by-one. I think it would be useful if you can somehow make parts that are _not_ linear stand out and perhaps allow omitting a stretch of ordinary single strand of pearls.

Johannes Schindelin· Dec 23, 2006, 14:53 UTC · re: Junio C Hamano · lore

Re: [RFC] git reflog show

Hi,
On Sat, 23 Dec 2006, Junio C Hamano wrote:
Show 15 quoted lines
> Shawn Pearce <spearce@spearce.org> writes:
> 
> > I'm toying with what a 'git reflog show' might look like.  I attached
> > the prototype code below.  Here's the output from the recent part of
> > my mmap pack window topic:
> >
> >   $ ./git-reflog show refs/heads/sp/mapwin
> >   ...clipped for space...
> >   
> 
> The output should look almost the same as "git log" for the part
> that represents a series of normal branch activity where commits
> are built up one-by-one.  I think it would be useful if you can
> somehow make parts that are _not_ linear stand out and perhaps
> allow omitting a stretch of ordinary single strand of pearls.

I wonder if it would make sense to teach the revision walking machinery about reflogs. A commit could be marked as coming from a reflog entry, and in that case the parents could be determined by the reflog rather than the commit itself.

Is this a stupid idea?

Ciao, Dscho

Shawn Pearce· Dec 24, 2006, 06:11 UTC · re: Johannes Schindelin · lore

Re: [RFC] git reflog show

Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:
> I wonder if it would make sense to teach the revision walking machinery 
> about reflogs. A commit could be marked as coming from a reflog entry, and 
> in that case the parents could be determined by the reflog rather than the 
> commit itself.

The revision machinery already knows about reflogs with --reflog, used by git-pack-objects via git-repack. But here its really only useful to seed the list of commits to be walked as part of a pack generation, to make sure the things referenced by the reflog stay around after a repacking. And it implies --all.

Rewriting the commits in memory to appear to have parents based on their order of appearence in the reflog would nicely generate a single strand of perls, but it makes it difficult to then access the same commit's real parents, doesn't it? So that may make the revision machinary somewhat limited in some applications.

Besides we want the reflog message entry and not the commit message when we perform pretty output, etc. So really we are then talking about generating synthetic commit objects for the reflog data.

-- 
Shawn.
Johannes Schindelin· Dec 24, 2006, 12:49 UTC · re: Shawn Pearce · lore

Re: [RFC] git reflog show

Hi,
On Sun, 24 Dec 2006, Shawn Pearce wrote:
Show 11 quoted lines
> Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:
> > I wonder if it would make sense to teach the revision walking machinery 
> > about reflogs. A commit could be marked as coming from a reflog entry, and 
> > in that case the parents could be determined by the reflog rather than the 
> > commit itself.
> 
> The revision machinery already knows about reflogs with --reflog,
> used by git-pack-objects via git-repack.  But here its really only
> useful to seed the list of commits to be walked as part of a pack
> generation, to make sure the things referenced by the reflog stay
> around after a repacking.  And it implies --all.
Ah. I think it is a bit misnamed.

Besides, --reflog options of pack-objects, show-branch and general revision-walk based programs are independent.

I propose to change the behaviour of "--reflog" in revision.c (which should not have a big impact, since it is not even documented yet):

- if --all-reflog is passed, include all reflogs (part of the current 
  behaviour of --reflog), and
- if --reflog is passed, write the reflog messages in addition to the 
  commit lines, and rewrite the parent(s).
Show 5 quoted lines
> Rewriting the commits in memory to appear to have parents based
> on their order of appearence in the reflog would nicely generate
> a single strand of perls, but it makes it difficult to then access
> the same commit's real parents, doesn't it?  So that may make the
> revision machinary somewhat limited in some applications.
Uhm, we rewrite parents all the time. Just think about "git log Makefile".
> Besides we want the reflog message entry and not the commit message
> when we perform pretty output, etc.  So really we are then talking
> about generating synthetic commit objects for the reflog data.

Yes, but if we have to read the reflog anyway to determine the logical (local) parent, we can just as well read the message, and display it, too.

What it buys us is that we do not duplicate efforts here, and we can easily visualize the reflog in gitk, too.

Ciao, Dscho

Junio C Hamano· Dec 24, 2006, 21:47 UTC · re: Johannes Schindelin · lore

Re: [RFC] git reflog show

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
Show 5 quoted lines
> Yes, but if we have to read the reflog anyway to determine the logical 
> (local) parent, we can just as well read the message, and display it, too.
>
> What it buys us is that we do not duplicate efforts here, and we can 
> easily visualize the reflog in gitk, too.
I do not think you can sanely rewrite parenthood.
Think about this sequence
	$ git init-db; edit foo; git add foo
	$ git commit -m 'first'
	$ edit foo; git add foo
        $ git commit -m 'second'
        $ git reset --hard HEAD@{1}
At this point, the commits on the reflog are:
        $ git show-branch --reflog=3 HEAD
        ! [HEAD@{0}] first
         ! [HEAD@{1}] second
          ! [HEAD@{2}] first
        ---
         +  [HEAD@{1}] second
        +++ [HEAD@{0}] first

We are currently at the first commit, and its "rewritten" parent should point at the second commit (i.e. pretend HEAD@{1} is the parent of HEAD@{0}). The "rewritten" parent of the second commit should be HEAD@{2}, but that is actually the same first commit which is HEAD@{0}.

Also I highly doubt visualization based on parents information rewritten to match the reflog order is of _any_ use. By rewriting the parenthood, you are losing the topology and your visualization is no better than what "tac .git/logs/$ref" would give.

I do agree that reusing the message formattng and display code from the usual log machinery is desirable. I think you can hook that information somewhere in object->util (or a callback mechanism) and teach log display machinery to use it as a generic mechanism. That might make the existing code to insert "Subject: " for --pretty=format simpler, but I haven't looked.

Johannes Schindelin· Dec 25, 2006, 14:01 UTC · re: Junio C Hamano · lore

Re: [RFC] git reflog show

Hi,
On Sun, 24 Dec 2006, Junio C Hamano wrote:
Show 10 quoted lines
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > Yes, but if we have to read the reflog anyway to determine the logical 
> > (local) parent, we can just as well read the message, and display it, 
> > too.
> >
> > What it buys us is that we do not duplicate efforts here, and we can 
> > easily visualize the reflog in gitk, too.
> 
> I do not think you can sanely rewrite parenthood.

Well, I did not mean to change the parents in the commit object. Rather, let the revision walker determine the parent differently for reflogs.

> Also I highly doubt visualization based on parents information rewritten 
> to match the reflog order is of _any_ use.  By rewriting the parenthood, 
> you are losing the topology and your visualization is no better than 
> what "tac .git/logs/$ref" would give.

Okay, but if they are _not_ rewritten, we can reuse the log machinery to show the revisions in "upstream..master@{2.hours.ago}", but in the order they came into the local repository.

Ciao, Dscho

Junio C Hamano· Dec 25, 2006, 18:07 UTC · re: Johannes Schindelin · lore

Re: [RFC] git reflog show

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
Show 8 quoted lines
>> Also I highly doubt visualization based on parents information rewritten 
>> to match the reflog order is of _any_ use.  By rewriting the parenthood, 
>> you are losing the topology and your visualization is no better than 
>> what "tac .git/logs/$ref" would give.
>
> Okay, but if they are _not_ rewritten, we can reuse the log machinery to 
> show the revisions in "upstream..master@{2.hours.ago}", but in the order 
> they came into the local repository.

Shawn's code was about showing where the tip of the branch was, and I think you are talking about something entirely different, which I would address later.

A sensible way to reuse existing code to implement Shawn's one is to add the revisions to rev.pending, and iterate over that array like "git show" does. This does not need to touch the existing revision walking code at all. The most valuable parts of the revision walking code are about ancestry traversal and history simplification with pathspec, neither of which makes much sense to use when "walking" reflog. Reflog walking might want to use the filtering by commit_match() but then it is only the matter of renaming the function to a bit more specific name and exporting it.

You can largely reuse the display side of the code that way, and I think you should be able to hook into the code without making it too specific to the reflog (perhaps using object->util and/or a callback) if you need to give extra information (e.g. comments and commit information from the log).

But it _also_ makes sense to use reflog when the primary thing we are interested in seeing is not how the tip jumped around, but seeing how the branch acquired commits, which I think is what you are suggesting. What we would want to have is a sort order different from the existing topo or date, which is "reflog order". While I think it makes sense, a naive implementation would be somewhat expensive. Instead of sorting topologically at the end of prepare_revision_walk (you would need to limit the list if you do this), for each commit, you binary search the reflog entries to find the earliest one that is the ancestor of the commit, and use the reflog entry's timestamp as the age of the commit (it's the first time the branch saw that commit), and sort commits using that.

Shawn Pearce· Dec 26, 2006, 05:48 UTC · re: Junio C Hamano · lore

Re: [RFC] git reflog show

Junio C Hamano <junkio@cox.net> wrote:
Show 16 quoted lines
> A sensible way to reuse existing code to implement Shawn's one
> is to add the revisions to rev.pending, and iterate over that
> array like "git show" does.  This does not need to touch the
> existing revision walking code at all.  The most valuable parts
> of the revision walking code are about ancestry traversal and
> history simplification with pathspec, neither of which makes
> much sense to use when "walking" reflog.  Reflog walking might
> want to use the filtering by commit_match() but then it is only
> the matter of renaming the function to a bit more specific name
> and exporting it.
> 
> You can largely reuse the display side of the code that way, and
> I think you should be able to hook into the code without making
> it too specific to the reflog (perhaps using object->util and/or
> a callback) if you need to give extra information (e.g. comments
> and commit information from the log).
So what you are proposing is to make the reflog visible in 'git log'
by a new option?  Or to just try to reuse all of its display code
but keep the reflog under 'git reflog show' ?
 
-- 
Shawn.
Junio C Hamano· Dec 26, 2006, 06:14 UTC · re: Shawn Pearce · lore

Re: [RFC] git reflog show

Shawn Pearce <spearce@spearce.org> writes:
> So what you are proposing is to make the reflog visible in 'git log'
> by a new option?  Or to just try to reuse all of its display code
> but keep the reflog under 'git reflog show' ?

I am not specifically proposing anything, but the part you quoted is about the latter.

What Johannes brought up would involve hooking into the usual log walking machinery to show the commits in "merge order" (more precisely, "local appearance order"). I do not think it is about "showing the reflog data", but about using reflog data to give a different presentation order to the usual log output. Your "reflog show" is more about showing what are recorded in the reflog.

It is not like implementing the sorting of the logs in local appearance order would make your "reflog show" redundant or useless; both are independently useful, I think.

Johannes Schindelin· Dec 26, 2006, 16:14 UTC · re: Junio C Hamano · lore

Re: [RFC] git reflog show

Hi,
On Mon, 25 Dec 2006, Junio C Hamano wrote:
Show 14 quoted lines
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> >> Also I highly doubt visualization based on parents information rewritten 
> >> to match the reflog order is of _any_ use.  By rewriting the parenthood, 
> >> you are losing the topology and your visualization is no better than 
> >> what "tac .git/logs/$ref" would give.
> >
> > Okay, but if they are _not_ rewritten, we can reuse the log machinery to 
> > show the revisions in "upstream..master@{2.hours.ago}", but in the order 
> > they came into the local repository.
> 
> Shawn's code was about showing where the tip of the branch was,
> and I think you are talking about something entirely different,
> which I would address later.
If I read Shawn's mail correctly, it is not only about the tip.
> The most valuable parts of the revision walking code are about ancestry 
> traversal and history simplification with pathspec, neither of which 
> makes much sense to use when "walking" reflog.

Sorry to be a PITA here, but I think that it _would_ make sense. Quite often I ask myself "That feature in this file used to work. When and how was it changed?" Right now, I use git-log with path simplification, but with reflog walking, I could ask in a more specific way!

Show 5 quoted lines
> But it _also_ makes sense to use reflog when the primary thing we are 
> interested in seeing is not how the tip jumped around, but seeing how 
> the branch acquired commits, which I think is what you are suggesting.  
> What we would want to have is a sort order different from the existing 
> topo or date, which is "reflog order".

I think it should not be a different sort order. As you pointed out yourself, a "git reset --hard HEAD^" removes the assumption of the commit list being a DAG.

Rather, I think about introducing a "get_parents(revs, commit)" function in revision.c, which takes reflog information rather than commit->parents if "--walk-reflogs" is passed to setup_revisions().

I hope to have a proof of concept later today.

Ciao, Dscho

Junio C Hamano· Dec 26, 2006, 19:03 UTC · re: Johannes Schindelin · lore

Re: [RFC] git reflog show

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
Show 5 quoted lines
>> Shawn's code was about showing where the tip of the branch was,
>> and I think you are talking about something entirely different,
>> which I would address later.
>
> If I read Shawn's mail correctly, it is not only about the tip.

What I meant was that I did not see anything that mentions "$ref@{ago}~47" and such; what was shown was only about the transition between the tip.

Show 8 quoted lines
>> The most valuable parts of the revision walking code are about ancestry 
>> traversal and history simplification with pathspec, neither of which 
>> makes much sense to use when "walking" reflog.
>
> Sorry to be a PITA here, but I think that it _would_ make sense. Quite 
> often I ask myself "That feature in this file used to work. When and how 
> was it changed?" Right now, I use git-log with path simplification, but 
> with reflog walking, I could ask in a more specific way!

When you put it that way, it sort of makes sense. Right now, get_revision() walks the chain of commits using the parent links and the history simplification (try_to_simplify_commit) looks at each commit and compares the tree with its parents' trees to decide if the commit is worth considering.

If we _had_ an object type "reflog entry" which has one "parent reflog entry" field and an associated "tip commit" object, just like a commit object has zero or more "parent commit" field and an associated "tree" object, the same algorithm could be taught to follow the "parent reflog entry" chain while walking and look at the tree associated with "tip commit" for the history simplification purposes.

I am not proposing to introduce a new object type here, but conceptually we could. Maybe a quick hack would be to represent each reflog entry as a phony "struct commit" in core,

 1. whose tree is taken from the tree of the tip commit,
 2. whose buffer is handcrafted by the true commit log message
    with data from reflog, and
 3. whose parent list points at another phony "commit" that
    represents ref@{N+1} reflog entry.

Then we would give a new populate_parent() member to "struct revinfo" which is called instead of parse_object() to fill the commit's parent list before traversal. With 1. we do not have to touch any code for history simplification, with 2. we do not have to touch pretty-print-commit (the result would include whatever reflog wants to add in the message automatically), and with 3. plus populate_parent callback would give us the traversal.

Show 9 quoted lines
>> But it _also_ makes sense to use reflog when the primary thing we are 
>> interested in seeing is not how the tip jumped around, but seeing how 
>> the branch acquired commits, which I think is what you are suggesting.  
>> What we would want to have is a sort order different from the existing 
>> topo or date, which is "reflog order".
>
> I think it should not be a different sort order. As you pointed out 
> yourself, a "git reset --hard HEAD^" removes the assumption of the commit 
> list being a DAG.

I think I misunderstood your proposal and the misunderstanding resulted in an unrelated useful enhancement ;-). If HEAD@{0} and HEAD@{2} points at the same commit, then obviously commits reachable from that commit did not come into the branch no later than HEAD@{2} time (what HEAD@{0} says becomes irrelevant), so you can define an order that lists commits in the order the earliest time they appeared on a branch.

Johannes Schindelin· Dec 26, 2006, 23:20 UTC · re: Junio C Hamano · lore

Re: [RFC] git reflog show

Hi,
On Tue, 26 Dec 2006, Junio C Hamano wrote:
Show 31 quoted lines
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> >> The most valuable parts of the revision walking code are about 
> >> ancestry traversal and history simplification with pathspec, neither 
> >> of which makes much sense to use when "walking" reflog.
> >
> > Sorry to be a PITA here, but I think that it _would_ make sense. Quite 
> > often I ask myself "That feature in this file used to work. When and 
> > how was it changed?" Right now, I use git-log with path 
> > simplification, but with reflog walking, I could ask in a more 
> > specific way!
> 
> When you put it that way, it sort of makes sense.  Right now, 
> get_revision() walks the chain of commits using the parent links and the 
> history simplification (try_to_simplify_commit) looks at each commit and 
> compares the tree with its parents' trees to decide if the commit is 
> worth considering.
> 
> [...]
> 
> I am not proposing to introduce a new object type here, but
> conceptually we could.  Maybe a quick hack would be to represent
> each reflog entry as a phony "struct commit" in core,
> 
>  1. whose tree is taken from the tree of the tip commit,
> 
>  2. whose buffer is handcrafted by the true commit log message
>     with data from reflog, and
> 
>  3. whose parent list points at another phony "commit" that
>     represents ref@{N+1} reflog entry.

I see only one problem with this approach: "next..master@{0}" would stop to work as expected.

Show 16 quoted lines
> >> But it _also_ makes sense to use reflog when the primary thing we are 
> >> interested in seeing is not how the tip jumped around, but seeing how 
> >> the branch acquired commits, which I think is what you are 
> >> suggesting.  What we would want to have is a sort order different 
> >> from the existing topo or date, which is "reflog order".
> >
> > I think it should not be a different sort order. As you pointed out 
> > yourself, a "git reset --hard HEAD^" removes the assumption of the 
> > commit list being a DAG.
> 
> I think I misunderstood your proposal and the misunderstanding resulted 
> in an unrelated useful enhancement ;-).  If HEAD@{0} and HEAD@{2} points 
> at the same commit, then obviously commits reachable from that commit 
> did not come into the branch no later than HEAD@{2} time (what HEAD@{0} 
> says becomes irrelevant), so you can define an order that lists commits 
> in the order the earliest time they appeared on a branch.

I fear that this traversal is not as easy as it seems. We cannot just blindly walk the reflogs, since the simplification relies on being able to actually rewrite the commit_list. Also, if a commit was seen, it must be possible to show it _again_. And if you show the commit diff, you rely on commit->parents actually containing the rewritten parents. And the same commit could be in revs->commits twice, and the correct reflog has still to be shown.

So I tend to think that rewriting the parents, buffer _and_ flags is the way to go.

Ciao, Dscho

Junio C Hamano· Dec 26, 2006, 23:50 UTC · re: Johannes Schindelin · lore

Re: [RFC] git reflog show

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
Show 14 quoted lines
>> I am not proposing to introduce a new object type here, but
>> conceptually we could.  Maybe a quick hack would be to represent
>> each reflog entry as a phony "struct commit" in core,
>> 
>>  1. whose tree is taken from the tree of the tip commit,
>> 
>>  2. whose buffer is handcrafted by the true commit log message
>>     with data from reflog, and
>> 
>>  3. whose parent list points at another phony "commit" that
>>     represents ref@{N+1} reflog entry.
>
> I see only one problem with this approach: "next..master@{0}" would stop 
> to work as expected.

I was not talking about doing this as part of the usual "rev-list". This is a special hack to support reflog, and that is Ok, because reflog is a very special hack itself. I was trying to rephrase what you said about reusing revision traversal machinery for walking reflog. The actual syntax would more likely to be:

	reflog show master@{20}..master@{4} -- paths...

where you do NOT even allow handle_revision_arg() to interpret double-dots, nor sha1_hex() to interpret master@{20}. We do not want the real commit objects when walking reflog entries.

You are going to synthesize an imaginary commit ancestry chain that corresponds to the reflog entries of 'master' branch (so you would error out when your replacement handle_revision_arg() finds either is not a reflog entry and/or talk about different refs).

In other words, the usual "next..master@{0}" WILL not do this phony wrapping with "imaginary reflog objects". We know it cannot work.

Show 14 quoted lines
>> I think I misunderstood your proposal and the misunderstanding resulted 
>> in an unrelated useful enhancement ;-).  If HEAD@{0} and HEAD@{2} points 
>> at the same commit, then obviously commits reachable from that commit 
>> did not come into the branch no later than HEAD@{2} time (what HEAD@{0} 
>> says becomes irrelevant), so you can define an order that lists commits 
>> in the order the earliest time they appeared on a branch.
>
> I fear that this traversal is not as easy as it seems. We cannot just 
> blindly walk the reflogs, since the simplification relies on being able to 
> actually rewrite the commit_list. Also, if a commit was seen, it must be 
> possible to show it _again_. And if you show the commit diff, you rely on 
> commit->parents actually containing the rewritten parents. And the same 
> commit could be in revs->commits twice, and the correct reflog has still 
> to be shown.

I think you are misunderstanding me completely. This part is about a separate topic of using reflog when walking the real history. We do not wrap or do anything funky like that. We just walk the true history. We do not walk reflog at all.

Then, before you output anything, at the point you would call topological sort, instead you would sort the commits with the timestamp of the earliest appearance in the repository (or on a particular branch), instead of the usual commit date. And reflog data is how you would determine when each commit appeared in this repository (or on this particular branch).

← back to recent threads