Search code examples
gitgithubversion-controlversioning

Are squashed commits in a rebase visible to others?


I'm wondering if squashed commits in a rebase can be visible+accessible to other people. I'll illustrate a quick example. Assume this is all happening on my local machine in a local git repo.

  • I make a commit 'X' with env secrets (oh no!)
  • I make a commit 'Y' that removes the committed env secrets along with some other unrelated code changes
  • I then rebase by squashing the 2 commits (e.g. git rebase -i HEAD~2) so that my commit with the env secrets has been squashed

Question: If I then push my commit to a public repo, would another person (assume a bad actor) be able to somehow pull the changes and search for the squashed commit which contains the env secrets?

Note: I understand from here that reflog commits are still accessible locally, but I'm asking not from a local repo perspective, but rather from the perspective of someone on another machine pulling my new commits. Additionally, I don't mind if squashed commit messages (emphasis on message) are still visible such as in the case someone forgets to comment them out during the rebase, the question I have is concerned with the squashed commits themselves.

Thank you!!


Solution

  • During the interactive rebase, X and Y get replaced by a completely new commit (Z) containing the resulting changes. If the original commits are not linked to another commit, branch, tag or other reference, they become dangling/unreachable. As you already know, they remain accessible in your local repository and will eventually get garbage-collected.

    When you push your branch to a remote, only the referenced commits are pushed. In this case Z and earlier commits not yet on the remote (if any). X and Y do not get pushed unless they are part of another reference (e.g. branch) that you push later.


    $ git clone <remote-repo> .
    $ echo "SECRET" > file.txt
    $ git commit -am "X"
    $ echo "******" > file.txt
    $ git commit -am "Y"
    
    $ git log --oneline --graph --all
    * 2e61b2a (HEAD -> master) Y
    * fbeb59a X
    * 3068e71 (origin/master, origin/HEAD) Initial commit
    
    $ git rebase -i HEAD~2               # pick X, squash Y, new message: 'Z'
    
    $ gittest % git log --oneline --graph --all
    * bc58ac7 (HEAD -> master) Z
    * 3068e71 (origin/master, origin/HEAD) Initial commit
    

    The new commit bc58ac7 replaced the two other commits. The diff does not show the secret value (seen in commit X) but only the resulting changes of both commits:

    $ git diff 3068e71 bc58ac7
    diff --git a/file.txt b/file.txt
    index e69de29..0b13ec0 100644
    --- a/file.txt
    +++ b/file.txt
    @@ -0,0 +1 @@
    +******
    

    After a push, we see that origin/master references the new commit:

    $ git push
    $ git log --oneline --graph --all
    * bc58ac7 (HEAD -> master, origin/master, origin/HEAD) Z
    * 3068e71 Initial commit
    

    When we print all objects on the remote repository, we see that the two commits are not there. (This command needs to be executed in the remote repository, not in the local clone.)

    $ git cat-file --batch-check --batch-all-objects
    0b13ec01f786f69139940c06f3bc7f9645550cfa blob 7
    3068e71e3ca27d864af7a1c5eb7dc90aee31de14 commit 229
    76fd94cb5a10f70fe2fadc41af74e9eeeb8e35b5 tree 36
    bc58ac7510f3e08d693029674a7ac545404e48cd commit 264
    bdd68b0120ca91384c1606468b4ca81b8f67c728 tree 36
    e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 blob 0