• Dmitriy Zaporozhets's avatar
    Merge branch 'disable-git-follow' into 'master' · 795a29ae
    Dmitriy Zaporozhets authored
    Disable --follow in `git log` to avoid loading duplicate commit data in infinite scroll
    
    `git` doesn't work properly when `--follow` and `--skip` are specified together. We could even be **omitting commits in the Web log** as a result.
    
    Here are the gory details. Let's say you ran:
    
    ```
    git log -n=5 --skip=2 README
    ```
    
    This is the working case since it omits `--follow`. This is what happens:
    
    1. `git` starts at `HEAD` and traverses down the tree until it finds the top-most commit relevant to README.
    2. Once this is found, this commit is returned via `get_revision_1()`.
    3. If the `skip_count` is positive, decrement and repeat step 2. Otherwise go onto step 4.
    4. `show_log()` gets called with that commit.
    5. Repeat step 1 until we have all five entries.
    
    That's exactly what we want. What happens when you use `--follow`? You have to understand how step 1 is performed:
    
    * When you specify a pathspec on the command-line (e.g. README), a flag `prune` [gets set here](https://github.com/git/git/blob/master/revision.c#L2351).
    * If the `prune` flag is active, `get_commit_action()` determines whether the commit should be [scanned for matching paths](https://github.com/git/git/blob/master/revision.c#L2989).
    * In the case of `--follow`, however, `prune` is [disabled here](https://github.com/git/git/blob/master/revision.c#L2350).
    * As a result, a commit is never scanned for matching paths and therefore never pruned. `HEAD` will always get returned as the first commit, even if it's not relevant to the README.
    * Making matters worse, the `--skip` in the example above would actually skip a every other entry after `HEAD` N times. If README were changed in these skipped commits, we would actually miss information!
    
    Since git uses a matching algorithm to determine whether a file was renamed, I
    believe `git` needs to generate a diff of each commit to do this and traverse
    each commit one-by-one to do this. I think that's the rationale for disabling
    the `prune` functionality since you can't just do a simple string comparison.
    
    Closes #4181, #4229, #3574, #2410
    
    See merge request !2210
    795a29ae
repository.rb 14.8 KB