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

RFC: rebase inconsistency in 2.18 -- course of action?

From
Elijah Newren <newren@gmail.com>
Date
Jun 7, 2018, 04:58 UTC
Message-ID
<CABPp-BGxaroePB6aKWAkZeADLB7VE3y1CPy2RyNwpn=+C01g3A@mail.gmail.com>
Hi everyone,

We've got a small rebase problem; I'm curious for thoughts about the best course of action. I'll provide several options below.

In short, while interactive-based and merge-based rebases handle directory rename detection fine, am-based rebases do not. This inconsistency may frustrate or surprise users.

=== Demonstration Example ===

Let's say we have a repo with three commits -- an original commit ('O'), and two branches based off of it, A and B with an extra commit each:

  O: has files l, x/a, x/b, x/c
  A: renames l -> lt, x/ -> y/
  B: modifies l, adds x/d
If I checkout B and run
  git rebase --interactive A

and don't bother editing the list of commits at all but just save, then I end up with lt, y/{a, b, c, d} as expected. However, if I run

  git rebase A
then I end up with lt, y/{a, b, c} and x/d; d is in the wrong place.
=== Problem explanation ===

am-based rebases suffer from a reduced ability to detect directory renames upstream, which is fundamental to the fact that it throws away information about the history: in particular, it dispenses with the original commits involved by turning them into patches, and without the knowledge of the original commits we cannot determine a proper merge base.

The am-based rebase will proceed after generating patches by commit-wise first trying git-apply, and if that fails it will attempt a 3-way merge. Since am has no knowledge of original commits, it instead reconstructs a provisional merge base using build_fake_ancestor() which will only contain the original versions of the files modified in the patch. Without the full list of files in the real merge base, renames of any missing files cannot be detected. Directory rename detection works by looking at individual file renames and deducing when a full directory has been renamed.

Trying to modify build_fake_ancestor() to instead create a merge base that includes common file information by looking for a commit that contained all the same blobs as the pre-image of the patch would require a very expensive search through history, and may choose a wrong commit from among the several it finds. (Also, regular am outside of rebase may not find any commit that matches, forcing it to somehow choose a near-fit). Further, this would only work when the fallback to a 3-way merge is triggered (i.e. when git-apply fails), which may not happen for some patches. (For example, if the l->lt change were not in the example, then git-apply would succeed and incorrectly place d in x/, so we'd never even hit the 3-way fallback.)

In short, the am machinery simply doesn't have the necessary information to properly detect directory renames, except when other files involved in the renames upstream were also modified on the side with the new file additions.

=== Possible solutions ===
1. Just accept it as a known shortcoming.
2. Revert directory rename detection from master...again.
   (Please, please, let's not pick this one.)
3. Claim it's not a bug at all.  In particular, the git-rebase(1) manpage
   states for the `--merge` option:
       Use merging strategies to rebase.  When the recursive (default)
       merge strategy is used, this allows rebase to be aware of
       renames on the upstream side.
   While "renames" is precisely why rebase --merge was added in commit
   58634dbff822 back on 2006-06-21, am-based rebases gained the
   ability to handle normal (non-directory) renames back in commit
   18cdf802ca6e from 2008-11-10.  So folks might be used to and expect
   their simple rebases to handle renames the same as --merge or
   --interactive rebases do.
4. Edit the 2.18 release notes.  Point out that directory renames can
   be detected by cherry-pick, merge, and rebases with either the -i
   or -m options (or any rebase option that implies either -i or -m);
   but not always by other rebases.  Folks may want to set pull.rebase
   to 'interactive' and just get used to immediately saving the
   popped-up list of commits and exiting to avoid this problem.
   Also, there's no good way to avoid this problem when using git-am
   directly.  At best, we can tell users to carefully select which
   older commit base to apply the patches on top of and then
   immediately run rebase --merge afterward.
5. Add an --am option to git-rebase, and make it not be the default
   (though make it be triggered by am-specific options like
   --whitespace or -C).  For the default, use either:
     5a. do_merge=t
     5b. interactive_rebase=implied
   As a slight aside, since there are only a few small changes
   necessary to make git-rebase--interactive handle ALL functionality
   from git-rebase--merge, we could take 5b one step further and
   delete git-rebase--merge and have one less rebase type.
   Either 5a or 5b may have a minor performance hit here, though
   personally I don't see this as a major factor.  But I'm happy to
   discuss at a high level if folks are curious (exact figures would
   depend heavily on the repo, size of commits, etc.)
6. Since there's not much time left before 2.18, do (1) and (4) for
   now, and (5) for 2.19.  Continue doing (1) or (3) for git-am itself.
7. Other solutions I haven't thought of?
=== Final Notes ===

I have patches implementing 5b, including the bonus removal of git-rebase--merge. However:

  * while my patches apply cleanly on master or next, there are lots
    of minor conflicts for the final two patches with ag/rebase-p in
    pu (in fact, even master conflicts with ag/rebase-p).  So I'm
    curious if I should wait off on sending these patches until that
    topic advances.
  * My first 9 patches are 7 independent fixes and cleanups, so I'll
    send those out shortly.

Thanks for reading this far, Elijah

Next: Elijah Newren
Message 1 of 130 in “RFC: rebase inconsistency in 2.18 -- course of action?”
  1. Elijah NewrenJun 7, 2018
  2. t3401: add directory rename testcases for rebase and amElijah Newren, Jun 7, 2018
  3. Elijah NewrenJun 25, 2018
  4. apply: fix grammar error in commentElijah Newren, Jun 7, 2018
  5. t5407: fix test to cover intended argumentsElijah Newren, Jun 7, 2018
  6. git-rebase--merge: modernize "git-$cmd" to "git $cmd"Elijah Newren, Jun 7, 2018
  7. Elijah NewrenJun 7, 2018
  8. git-rebase--merge: modernize "git-$cmd" to "git $cmd"Elijah Newren, Jun 27, 2018
  9. Johannes SchindelinJul 12, 2018
  10. Elijah NewrenJul 13, 2018
  11. Johannes SchindelinJul 14, 2018
  12. 1/2 t3422: new testcases for checking when incompatible options passedElijah Newren, Jun 7, 2018
  13. 2/2 git-rebase: error out when incompatible options passedElijah Newren, Jun 7, 2018
  14. Phillip WoodJun 10, 2018
  15. Elijah NewrenJun 11, 2018
  16. Phillip WoodJun 11, 2018
  17. Elijah NewrenJun 11, 2018
  18. Phillip WoodJun 12, 2018
  19. 0/7 Document/fix/warn about rebase incompatibilities and inconsistencesElijah Newren, Jun 17, 2018
  20. 1/7 git-rebase.txt: document incompatible optionsElijah Newren, Jun 17, 2018
  21. Phillip WoodJun 17, 2018
  22. Elijah NewrenJun 20, 2018
  23. Eric SunshineJun 17, 2018
  24. Elijah NewrenJun 20, 2018
  25. 2/7 git-rebase.sh: update help messages a bitElijah Newren, Jun 17, 2018
  26. 3/7 t3422: new testcases for checking when incompatible options passedElijah Newren, Jun 17, 2018
  27. 4/7 git-rebase: error out when incompatible options passedElijah Newren, Jun 17, 2018
  28. 7/7 git-rebase: make --allow-empty-message the defaultElijah Newren, Jun 17, 2018
  29. Phillip WoodJun 17, 2018
  30. Elijah NewrenJun 20, 2018
  31. 6/7 git-rebase.txt: address confusion between --no-ff vs --force-rebaseElijah Newren, Jun 17, 2018
  32. 5/7 git-rebase.txt: document behavioral inconsistencies between modesElijah Newren, Jun 17, 2018
  33. Phillip WoodJun 17, 2018
  34. 0/7 Document/fix/warn about rebase incompatibilities and inconsistenciesElijah Newren, Jun 21, 2018
  35. 7/7 git-rebase: make --allow-empty-message the defaultElijah Newren, Jun 21, 2018
  36. 3/7 t3422: new testcases for checking when incompatible options passedElijah Newren, Jun 21, 2018
  37. Junio C HamanoJun 21, 2018
  38. Elijah NewrenJun 21, 2018
  39. Eric SunshineJun 21, 2018
  40. 2/7 git-rebase.sh: update help messages a bitElijah Newren, Jun 21, 2018
  41. Junio C HamanoJun 21, 2018
  42. Elijah NewrenJun 21, 2018
  43. 5/7 git-rebase.txt: document behavioral inconsistencies between modesElijah Newren, Jun 21, 2018
  44. Junio C HamanoJun 21, 2018
  45. Elijah NewrenJun 21, 2018
  46. 1/7 git-rebase.txt: document incompatible optionsElijah Newren, Jun 21, 2018
  47. Junio C HamanoJun 21, 2018
  48. Elijah NewrenJun 21, 2018
  49. SZEDER GáborJun 22, 2018
  50. 4/7 git-rebase: error out when incompatible options passedElijah Newren, Jun 21, 2018
  51. Junio C HamanoJun 21, 2018
  52. Elijah NewrenJun 21, 2018
  53. 6/7 git-rebase.txt: address confusion between --no-ff vs --force-rebaseElijah Newren, Jun 21, 2018
  54. Junio C HamanoJun 21, 2018
  55. Elijah NewrenJun 21, 2018
  56. 0/9 Document/fix/warn about rebase incompatibilities and inconsistenciesElijah Newren, Jun 25, 2018
  57. 3/9 t3422: new testcases for checking when incompatible options passedElijah Newren, Jun 25, 2018
  58. Junio C HamanoJun 26, 2018
  59. Elijah NewrenJun 27, 2018
  60. 2/9 git-rebase.sh: update help messages a bitElijah Newren, Jun 25, 2018
  61. 5/9 git-rebase.txt: address confusion between --no-ff vs --force-rebaseElijah Newren, Jun 25, 2018
  62. 9/9 git-rebase: make --allow-empty-message the defaultElijah Newren, Jun 25, 2018
  63. 8/9 t3401: add directory rename testcases for rebase and amElijah Newren, Jun 25, 2018
  64. 7/9 git-rebase.txt: document behavioral differences between modesElijah Newren, Jun 25, 2018
  65. 1/9 git-rebase.txt: document incompatible optionsElijah Newren, Jun 25, 2018
  66. 6/9 directory-rename-detection.txt: technical docs on abilities and limitationsElijah Newren, Jun 25, 2018
  67. 4/9 git-rebase: error out when incompatible options passedElijah Newren, Jun 25, 2018
  68. 0/9 Document/fix/warn about rebase incompatibilities and inconsistenciesElijah Newren, Jun 27, 2018
  69. 2/9 git-rebase.sh: update help messages a bitElijah Newren, Jun 27, 2018
  70. 1/9 git-rebase.txt: document incompatible optionsElijah Newren, Jun 27, 2018
  71. 7/9 git-rebase.txt: document behavioral differences between modesElijah Newren, Jun 27, 2018
  72. 9/9 git-rebase: make --allow-empty-message the defaultElijah Newren, Jun 27, 2018
  73. 2.19.0 regression: leaving editor with empty commit message doesn't stop rebase [was: Re: [RFC PATCH v5 9/9] git-rebase: make --allow-empty-message the default]SZEDER Gábor, Sep 12, 2018
  74. Elijah NewrenSep 12, 2018
  75. sequencer: fix --allow-empty-message behavior, make it smarterElijah Newren, Sep 12, 2018
  76. Junio C HamanoSep 13, 2018
  77. 6/9 directory-rename-detection.txt: technical docs on abilities and limitationsElijah Newren, Jun 27, 2018
  78. 8/9 t3401: add directory rename testcases for rebase and amElijah Newren, Jun 27, 2018
  79. 4/9 git-rebase: error out when incompatible options passedElijah Newren, Jun 27, 2018
  80. 5/9 git-rebase.txt: address confusion between --no-ff vs --force-rebaseElijah Newren, Jun 27, 2018
  81. 3/9 t3422: new testcases for checking when incompatible options passedElijah Newren, Jun 27, 2018
  82. Phillip WoodJun 29, 2018
  83. git-rebase.sh: handle keep-empty like all other optionsElijah Newren, Jun 7, 2018
  84. Phillip WoodJun 10, 2018
  85. Elijah NewrenJun 11, 2018
  86. Phillip WoodJun 11, 2018
  87. Elijah NewrenJun 11, 2018
  88. Phillip WoodJun 12, 2018
  89. 1/2 t3418: add testcase showing problems with rebase -i and strategy optionsElijah Newren, Jun 7, 2018
  90. 2/2 Fix use of strategy options with interactive rebasesElijah Newren, Jun 7, 2018
  91. 0/2 Fix use of strategy options with interactive rebasesElijah Newren, Jun 27, 2018
  92. 1/2 t3418: add testcase showing problems with rebase -i and strategy optionsElijah Newren, Jun 27, 2018
  93. Eric SunshineJun 27, 2018
  94. Elijah NewrenJun 27, 2018
  95. Junio C HamanoJun 27, 2018
  96. Elijah NewrenJun 27, 2018
  97. Johannes SixtJun 27, 2018
  98. Eric SunshineJun 27, 2018
  99. SZEDER GáborJun 27, 2018
  100. Junio C HamanoJun 27, 2018
  101. Junio C HamanoJun 27, 2018
  102. 2/2 Fix use of strategy options with interactive rebasesElijah Newren, Jun 27, 2018
  103. 0/2 Fix use of strategy options with interactive rebasesElijah Newren, Jun 27, 2018
  104. 2/2 Fix use of strategy options with interactive rebasesElijah Newren, Jun 27, 2018
  105. Johannes SchindelinJul 12, 2018
  106. Elijah NewrenJul 13, 2018
  107. 1/2 t3418: add testcase showing problems with rebase -i and strategy optionsElijah Newren, Jun 27, 2018
  108. SZEDER GáborJun 27, 2018
  109. SZEDER GáborJul 11, 2018
  110. Elijah NewrenJul 11, 2018
  111. 0/3 Send more rebases through interactive machineryElijah Newren, Jun 7, 2018
  112. 1/3 git-rebase, sequencer: add a --quiet option for the interactive machineryElijah Newren, Jun 7, 2018
  113. Johannes SchindelinJun 9, 2018
  114. Elijah NewrenJun 9, 2018
  115. 3/3 git-rebase.sh: make git-rebase--interactive the defaultElijah Newren, Jun 7, 2018
  116. Johannes SchindelinJun 9, 2018
  117. Elijah NewrenJun 10, 2018
  118. Johannes SchindelinJun 17, 2018
  119. Elijah NewrenJun 20, 2018
  120. Johannes SchindelinJun 21, 2018
  121. Elijah NewrenJun 21, 2018
  122. Johannes SchindelinJan 21, 2019
  123. Elijah NewrenJan 21, 2019
  124. Johannes SchindelinJan 22, 2019
  125. 2/3 rebase: Implement --merge via git-rebase--interactiveElijah Newren, Jun 7, 2018
  126. Johannes SchindelinJun 9, 2018
  127. Elijah NewrenJun 10, 2018
  128. Phillip WoodJun 11, 2018
  129. Junio C HamanoJun 11, 2018
  130. Elijah NewrenJun 11, 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.