Search code examples
gitgithubbitbucket

How to Prevent PRs from Removing Commits


I noticed that PRs on Stash don't show if old commits have been removed.

e.g:

  1. make a new branch
  2. $git reset HEAD~100 --hard && git clean -f && touch mango to remove 100 commits and add a file making the PR look like it has some changes
  3. $git commit -m "Added mango" && git push -f
  4. setup a PR from that branch The above on Stash would just show that a file "Mango" has been added and completely ignore the 100 missing commits

Are there good ways to avoid this issue besides hoping none of the developers are malicious? and do PRs on GitHub have the same issue?


Solution

  • Sometimes, you have to try to think like the computer - forget what you know you did, and look at what the end result is.

    You had a history like this:

    a99 - a100 - a101 - ... - a199 - a200 <- branch A
    

    You created a new branch:

    a99 - a100 - a101 - ... - a199 - a200 <- branch A
                                       ^- branch B
    

    Then you "removed" 100 commits from branch B, by resetting it back to an earlier point in history - but you didn't touch branch A:

    a99 - a100 - a101 - ... - a199 - a200 <- branch A
            ^- branch B
    

    And you added a commit:

    a99 - a100 - a101 - ... - a199 - a200 <- branchA
              \
               b1
                ^- branchB
    

    Looking at that diagram, it's hopefully clear that there's no record of "removing" anything. You would have exactly the same diagram if you'd got as far as commit a100, created branch B, and then left it lying around while you created another 100 commits on branch A.

    That's exactly what git is designed to assume - that the branches diverged not because someone wants to remove something, but because two different people (or the same person working on two different features) wants to combine two sets of changes. So when you merge them together - whether via a Pull Request / Merge Request UI, or just directly in a local clone - it will simply try to apply the changes between a100 and b1 on top of a200, and create this:

    a99 - a100 - a101 - ... - a199 - a200 - a201<- branchA
              \                              /
               b1 ---------------------------
                ^- branchB
    

    You actually have to go a long way out of your way to remove part of the history in git, because the commits themselves are immutable. All you can really do is point a branch at an alternative line of commits, and shared UIs like GitHub, Gitlab, and BitBucket have settings for who is allowed to do that to which branch.