git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH v3 7/7] revision.c: refactor basic topo-order logic

From
Derrick Stolee <stolee@gmail.com>
Date
Oct 11, 2018, 16:21 UTC
Message-ID
<1c0eb7d3-7f31-9377-d42f-4ac2f36ac26a@gmail.com>
In-Reply-To
<20181011153510.GF27312@sigill.intra.peff.net>
On 10/11/2018 11:35 AM, Jeff King wrote:
Show 28 quoted lines
> On Fri, Sep 21, 2018 at 10:39:36AM -0700, Derrick Stolee via GitGitGadget wrote:
>
>> From: Derrick Stolee <dstolee@microsoft.com>
>>
>> When running a command like 'git rev-list --topo-order HEAD',
>> Git performed the following steps:
>> [...]
>> In the new algorithm, these three steps correspond to three
>> different commit walks. We run these walks simultaneously,
> A minor nit, but this commit message doesn't mention the most basic
> thing up front: that its main purpose is to introduce a new algorithm
> for topo-order. ;)
>
> It's obvious in the context of reviewing the series, but somebody
> reading "git log" later may want a bit more. Perhaps:
>
>    revision.c: implement generation-based topo-order algorithm
>
> as a subject, and/or an introductory paragraph like:
>
>    The current --topo-order algorithm requires walking all commits we
>    are going to output up front, topo-sorting them, all before
>    outputting the first value. This patch introduces a new algorithm
>    which uses stored generation numbers to incrementally walk in
>    topo-order, outputting commits as we go.
>
> Other than that, I find this to be a wonderfully explanatory commit
> message. :)
Good idea. I'll make that change.
Show 20 quoted lines
>
>> The walks are as follows:
>>
>> 1. EXPLORE: using the explore_queue priority queue (ordered by
>>     maximizing the generation number), parse each reachable
>>     commit until all commits in the queue have generation
>>     number strictly lower than needed. During this walk, update
>>     the UNINTERESTING flags as necessary.
> OK, this makes sense. If we know that everybody else in our queue is at
> generation X, then it is safe to output a commit at generation greater
> than X.
>
> I think this by itself would allow us to implement "show no parents
> before all of its children are shown", right? But --topo-order promises
> a bit more: "avoid showing commits no multiple lines of history
> intermixed".
>
> I guess also INFINITY generation numbers need more. For a real
> generation number, we know that "gen(A) == gen(B)" implies that there is
> no ancestry relationship between the two. But not so for INFINITY.

Yeah, to deal with INFINITY (and ZERO, but that won't happen if generation_numbers_enabled() returns true), we treat gen(A) == gen(B) as a "no information" state. So, to output a commit at generation X, we need to have our maximum generation number in the unexplored area to be at most X - 1. You'll see strict inequality when checking generations.

Show 17 quoted lines
>> 2. INDEGREE: using the indegree_queue priority queue (ordered
>>     by maximizing the generation number), add one to the in-
>>     degree of each parent for each commit that is walked. Since
>>     we walk in order of decreasing generation number, we know
>>     that discovering an in-degree value of 0 means the value for
>>     that commit was not initialized, so should be initialized to
>>     two. (Recall that in-degree value "1" is what we use to say a
>>     commit is ready for output.) As we iterate the parents of a
>>     commit during this walk, ensure the EXPLORE walk has walked
>>     beyond their generation numbers.
> I wondered how this would work for INFINITY. We can't know the order of
> a bunch of INFINITY nodes at all, so we never know when their in-degree
> values are "done". But if I understand the EXPLORE walk, we'd basically
> walk all of INFINITY down to something with a real generation number. Is
> that right?
>
> But after that, I'm not totally clear on why we need this INDEGREE walk.

The INDEGREE walk is an important element for Kahn's algorithm. The final output order is dictated by peeling commits of "indegree zero" to ensure all children are output before their parents. (Note: since we use literal 0 to mean "uninitialized", we peel commits when the indegree slab has value 1.)

This walk replaces the indegree logic from sort_in_topological_order(). That method performs one walk that fills the indegree slab, then another walk that peels the commits with indegree 0 and inserts them into a list.

Show 22 quoted lines
>> 3. TOPO: using the topo_queue priority queue (ordered based on
>>     the sort_order given, which could be commit-date, author-
>>     date, or typical topo-order which treats the queue as a LIFO
>>     stack), remove a commit from the queue and decrement the
>>     in-degree of each parent. If a parent has an in-degree of
>>     one, then we add it to the topo_queue. Before we decrement
>>     the in-degree, however, ensure the INDEGREE walk has walked
>>     beyond that generation number.
> OK, this makes sense to make --author-date-order, etc, work. Potentially
> those numbers might have no relationship at all with the graph
> structure, but we promise "no parent before its children are shown", so
> this is really just a tie-breaker after the topo-sort anyway. As long as
> steps 1 and 2 are correct and produce a complete set of commits for one
> "layer", this should be OK.
>
> I guess I'm not 100% convinced that we don't have a case where we
> haven't yet parsed or considered some commit that we know cannot have an
> ancestry relationship with commits we are outputting. But it may have an
> author-date-order relationship.
>
> (I'm not at all convinced that this _is_ a problem, and I suspect it
> isn't; I'm only suggesting I haven't fully grokked the proof).

The INDEGREE walk should not stop until it has explored at least to the point that all indegree 0 commits are exposed (relative to the current state of the walk).

At initialization, we walk from all starting positions until the maximum 
generation number in our queue is less than the minimum generation of a 
starting commit. The starting positions that have indegree 0 are then 
added to the topo_queue, and the sort order dictates which is the best. 
 From this point on, we can only create a new "indegree 0" commit by 
removing a commit from the topo_queue and decrementing the indegree of 
its parents. Those parents with indegree 0 are inserted into topo_queue 
and compared to all other indegree 0 commits. Thus, we will always 
explore enough to make the right choice relative our sort order.
Show 11 quoted lines
>
>> ---
>>   object.h   |   4 +-
>>   revision.c | 196 +++++++++++++++++++++++++++++++++++++++++++++++++++--
>>   revision.h |   2 +
>>   3 files changed, 194 insertions(+), 8 deletions(-)
> I'll pause here on evaluating the actual code. It looks sane from a
> cursory read, but there's no point in digging further until I'm sure I
> fully understand the algorithm. I think that needs a little more brain
> power from me, and hopefully discussion around my comments above will
> help trigger that.

Thanks for reading! I understand that reading the code is useless without understanding the high-level concepts. I'm happy to iterate on this. If I can find a better way to explain the algorithm in the commit message to avoid the "huh?" moments above, then I will.

Thanks, -Stolee

Previous: Jeff KingNext: Jeff King
Message 45 of 87 in “Use generation numbers for --topo-order”
  1. 0/6 Use generation numbers for --topo-orderDerrick Stolee via GitGitGadget, Aug 27, 2018
  2. 1/6 prio-queue: add 'peek' operationDerrick Stolee via GitGitGadget, Aug 27, 2018
  3. 2/6 test-reach: add run_three_modes methodDerrick Stolee via GitGitGadget, Aug 27, 2018
  4. 3/6 test-reach: add rev-list testsDerrick Stolee via GitGitGadget, Aug 27, 2018
  5. 4/6 revision.c: begin refactoring --topo-order logicDerrick Stolee via GitGitGadget, Aug 27, 2018
  6. 5/6 commit/revisions: bookkeeping before refactoringDerrick Stolee via GitGitGadget, Aug 27, 2018
  7. 6/6 revision.c: refactor basic topo-order logicDerrick Stolee via GitGitGadget, Aug 27, 2018
  8. Junio C HamanoAug 27, 2018
  9. 0/6 Use generation numbers for --topo-orderDerrick Stolee via GitGitGadget, Sep 18, 2018
  10. 1/6 prio-queue: add 'peek' operationDerrick Stolee via GitGitGadget, Sep 18, 2018
  11. 2/6 test-reach: add run_three_modes methodDerrick Stolee via GitGitGadget, Sep 18, 2018
  12. SZEDER GáborSep 18, 2018
  13. Junio C HamanoSep 19, 2018
  14. Junio C HamanoSep 19, 2018
  15. Junio C HamanoSep 20, 2018
  16. 3/6 test-reach: add rev-list testsDerrick Stolee via GitGitGadget, Sep 18, 2018
  17. 4/6 revision.c: begin refactoring --topo-order logicDerrick Stolee via GitGitGadget, Sep 18, 2018
  18. 5/6 commit/revisions: bookkeeping before refactoringDerrick Stolee via GitGitGadget, Sep 18, 2018
  19. 6/6 revision.c: refactor basic topo-order logicDerrick Stolee via GitGitGadget, Sep 18, 2018
  20. Ævar Arnfjörð BjarmasonSep 18, 2018
  21. Ævar Arnfjörð BjarmasonSep 18, 2018
  22. Derrick StoleeSep 21, 2018
  23. 0/7 Use generation numbers for --topo-orderDerrick Stolee via GitGitGadget, Sep 21, 2018
  24. 1/7 prio-queue: add 'peek' operationDerrick Stolee via GitGitGadget, Sep 21, 2018
  25. Derrick StoleeSep 26, 2018
  26. Jeff KingOct 11, 2018
  27. 2/7 test-reach: add run_three_modes methodDerrick Stolee via GitGitGadget, Sep 21, 2018
  28. Jeff KingOct 11, 2018
  29. 3/7 test-reach: add rev-list testsDerrick Stolee via GitGitGadget, Sep 21, 2018
  30. Jeff KingOct 11, 2018
  31. Junio C HamanoOct 12, 2018
  32. 4/7 revision.c: begin refactoring --topo-order logicDerrick Stolee via GitGitGadget, Sep 21, 2018
  33. Jeff KingOct 11, 2018
  34. Junio C HamanoOct 12, 2018
  35. Derrick StoleeOct 12, 2018
  36. Johannes SixtOct 12, 2018
  37. Junio C HamanoOct 13, 2018
  38. 5/7 commit/revisions: bookkeeping before refactoringDerrick Stolee via GitGitGadget, Sep 21, 2018
  39. Jeff KingOct 11, 2018
  40. 6/7 revision.h: add whitespace in flag definitionsDerrick Stolee via GitGitGadget, Sep 21, 2018
  41. 7/7 revision.c: refactor basic topo-order logicDerrick Stolee via GitGitGadget, Sep 21, 2018
  42. Derrick StoleeSep 27, 2018
  43. Jakub NarebskiOct 6, 2018
  44. Jeff KingOct 11, 2018
  45. Derrick StoleeOct 11, 2018
  46. Jeff KingOct 25, 2018
  47. Derrick StoleeOct 25, 2018
  48. Stefan BellerOct 11, 2018
  49. Junio C HamanoSep 21, 2018
  50. 0/7 Use generation numbers for --topo-orderDerrick Stolee via GitGitGadget, Oct 16, 2018
  51. 1/7 prio-queue: add 'peek' operationDerrick Stolee via GitGitGadget, Oct 16, 2018
  52. 3/7 test-reach: add rev-list testsDerrick Stolee via GitGitGadget, Oct 16, 2018
  53. Jakub NarebskiOct 21, 2018
  54. Derrick StoleeOct 21, 2018
  55. 4/7 revision.c: begin refactoring --topo-order logicDerrick Stolee via GitGitGadget, Oct 16, 2018
  56. Jakub NarebskiOct 21, 2018
  57. Junio C HamanoOct 22, 2018
  58. Derrick StoleeOct 22, 2018
  59. revision.c: use new algorithm in A..B caseDerrick Stolee, Oct 22, 2018
  60. Junio C HamanoOct 25, 2018
  61. Jakub NarebskiOct 26, 2018
  62. 2/7 test-reach: add run_three_modes methodDerrick Stolee via GitGitGadget, Oct 16, 2018
  63. 5/7 commit/revisions: bookkeeping before refactoringDerrick Stolee via GitGitGadget, Oct 16, 2018
  64. Jakub NarebskiOct 21, 2018
  65. 6/7 revision.c: generation-based topo-order algorithmDerrick Stolee via GitGitGadget, Oct 16, 2018
  66. Jakub NarebskiOct 22, 2018
  67. Derrick StoleeOct 23, 2018
  68. Jakub NarebskiOct 26, 2018
  69. 7/7 t6012: make rev-list tests more interestingDerrick Stolee via GitGitGadget, Oct 16, 2018
  70. Jakub NarebskiOct 23, 2018
  71. Jakub NarebskiOct 21, 2018
  72. Junio C HamanoNov 1, 2018
  73. Derrick StoleeNov 1, 2018
  74. Junio C HamanoNov 1, 2018
  75. 0/7 Use generation numbers for --topo-orderDerrick Stolee, Nov 1, 2018
  76. 1/7 prio-queue: add 'peek' operationDerrick Stolee, Nov 1, 2018
  77. 2/7 test-reach: add run_three_modes methodDerrick Stolee, Nov 1, 2018
  78. 3/7 test-reach: add rev-list testsDerrick Stolee, Nov 1, 2018
  79. 4/7 revision.c: begin refactoring --topo-order logicDerrick Stolee, Nov 1, 2018
  80. 5/7 commit/revisions: bookkeeping before refactoringDerrick Stolee, Nov 1, 2018
  81. 6/7 revision.c: generation-based topo-order algorithmDerrick Stolee, Nov 1, 2018
  82. SZEDER GáborNov 1, 2018
  83. Derrick StoleeNov 1, 2018
  84. Mike HommeyNov 8, 2019
  85. Derrick StoleeNov 11, 2019
  86. SZEDER GáborNov 18, 2019
  87. 7/7 t6012: make rev-list tests more interestingDerrick Stolee, Nov 1, 2018

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.