Documentation / technical / directory-rename-detection.txton commit Merge branch 'sg/index-pack-progress' (da924b5)
   1Directory rename detection
   2==========================
   3
   4Rename detection logic in diffcore-rename that checks for renames of
   5individual files is aggregated and analyzed in merge-recursive for cases
   6where combinations of renames indicate that a full directory has been
   7renamed.
   8
   9Scope of abilities
  10------------------
  11
  12It is perhaps easiest to start with an example:
  13
  14  * When all of x/a, x/b and x/c have moved to z/a, z/b and z/c, it is
  15    likely that x/d added in the meantime would also want to move to z/d by
  16    taking the hint that the entire directory 'x' moved to 'z'.
  17
  18More interesting possibilities exist, though, such as:
  19
  20  * one side of history renames x -> z, and the other renames some file to
  21    x/e, causing the need for the merge to do a transitive rename.
  22
  23  * one side of history renames x -> z, but also renames all files within x.
  24    For example, x/a -> z/alpha, x/b -> z/bravo, etc.
  25
  26  * both 'x' and 'y' being merged into a single directory 'z', with a
  27    directory rename being detected for both x->z and y->z.
  28
  29  * not all files in a directory being renamed to the same location;
  30    i.e. perhaps most the files in 'x' are now found under 'z', but a few
  31    are found under 'w'.
  32
  33  * a directory being renamed, which also contained a subdirectory that was
  34    renamed to some entirely different location.  (And perhaps the inner
  35    directory itself contained inner directories that were renamed to yet
  36    other locations).
  37
  38  * combinations of the above; see t/t6043-merge-rename-directories.sh for
  39    various interesting cases.
  40
  41Limitations -- applicability of directory renames
  42-------------------------------------------------
  43
  44In order to prevent edge and corner cases resulting in either conflicts
  45that cannot be represented in the index or which might be too complex for
  46users to try to understand and resolve, a couple basic rules limit when
  47directory rename detection applies:
  48
  49  1) If a given directory still exists on both sides of a merge, we do
  50     not consider it to have been renamed.
  51
  52  2) If a subset of to-be-renamed files have a file or directory in the
  53     way (or would be in the way of each other), "turn off" the directory
  54     rename for those specific sub-paths and report the conflict to the
  55     user.
  56
  57  3) If the other side of history did a directory rename to a path that
  58     your side of history renamed away, then ignore that particular
  59     rename from the other side of history for any implicit directory
  60     renames (but warn the user).
  61
  62Limitations -- detailed rules and testcases
  63-------------------------------------------
  64
  65t/t6043-merge-rename-directories.sh contains extensive tests and commentary
  66which generate and explore the rules listed above.  It also lists a few
  67additional rules:
  68
  69  a) If renames split a directory into two or more others, the directory
  70     with the most renames, "wins".
  71
  72  b) Avoid directory-rename-detection for a path, if that path is the
  73     source of a rename on either side of a merge.
  74
  75  c) Only apply implicit directory renames to directories if the other side
  76     of history is the one doing the renaming.
  77
  78Limitations -- support in different commands
  79--------------------------------------------
  80
  81Directory rename detection is supported by 'merge' and 'cherry-pick'.
  82Other git commands which users might be surprised to see limited or no
  83directory rename detection support in:
  84
  85  * diff
  86
  87    Folks have requested in the past that `git diff` detect directory
  88    renames and somehow simplify its output.  It is not clear whether this
  89    would be desirable or how the output should be simplified, so this was
  90    simply not implemented.  Further, to implement this, directory rename
  91    detection logic would need to move from merge-recursive to
  92    diffcore-rename.
  93
  94  * am
  95
  96    git-am tries to avoid a full three way merge, instead calling
  97    git-apply.  That prevents us from detecting renames at all, which may
  98    defeat the directory rename detection.  There is a fallback, though; if
  99    the initial git-apply fails and the user has specified the -3 option,
 100    git-am will fall back to a three way merge.  However, git-am lacks the
 101    necessary information to do a "real" three way merge.  Instead, it has
 102    to use build_fake_ancestor() to get a merge base that is missing files
 103    whose rename may have been important to detect for directory rename
 104    detection to function.
 105
 106  * rebase
 107
 108    Since am-based rebases work by first generating a bunch of patches
 109    (which no longer record what the original commits were and thus don't
 110    have the necessary info from which we can find a real merge-base), and
 111    then calling git-am, this implies that am-based rebases will not always
 112    successfully detect directory renames either (see the 'am' section
 113    above).  merged-based rebases (rebase -m) and cherry-pick-based rebases
 114    (rebase -i) are not affected by this shortcoming, and fully support
 115    directory rename detection.