gitgithubdvcsgit-baregit-non-bare-repository

Why can't I push to a checked out branch of a non-bare repository?


I am confused regarding a scenario that I created. I created a repository on Github (Lets call it A) and pushed code to it. After that I cloned that repository to my local (Lets call it B) such that origin of my local was remote repo A.

Now I cloned from my local B to create another local instance C. Now I had remote origin of C as repo B and upstream of C was A.

A → B → C

This is similar to forking but here I created clone on client side instead of server side.

Now if I tried to use push from C to its origin B:

git push origin 

then I received an error stating that I cannot push to non-bare repositories. I understand that pushing to non-bare repositories can result in loss of commits in remote not present in local.

However is this case not similar to the one where i push my code from B to A ?

I am confused if B to A is possible then why not C to B.

For merging to A we can push to upstream as:

git push upstream

Solution

  • Some basic stuff

    Understanding the situation

    Let's consider the following:

    That being said, let's say you modify some files in B. You do git commit as many times you want and even a git push at the end. Or you don't do anything at all. But you are on master branch.

    Later on, you modify files in C. And you do commit and tries to push to A. Remember: you are on master branch of C. The git push works!

    Then, you try pushing C to B as well. It doesn't work.

    Result: GIT will (not) literally scream, warning about the fact you are trying to defile (update) the master branch of the non-bare repository B which its HEAD points to another commit! If he lets you do the push, you will mess the history tracked by GIT on repository B. It won't know what happened to B anymore! You might even overwrite modification on that branch with the same name residing on B! So no, you can't push to B from C if both are non-bare repositories!

    What now?! Will my world end like this?! What could the great I have done?! How possibly could GIT ignore his master's wishes?! This is pure heresy!

    Solution

    1 - Have two branches on B - the master and a temporary one. And make the head points to the temporary branch. Example:

    cd B                  # change to B's working directory
    git branch temp       # create 'temp' branch
    git checkout temp     # change from master branch to branch temp
    

    2 - Now, move to C working directory (wd for short) and pull with the contents of B. Note that I'm considering that B is a remote of C (as you have mentioned in your case):

    cd ../C               # change to C's working directory
    git pull B master     # pulls B's modifications to C
    

    3 - Modify your files in C. Note that you are on C's master branch. Then, after committing modifications of C, push it to B's master:

    git push B master     # pushes C's mods to B's master branch
    

    4 - Now go back to B wd and make the HEAD point back to the master branch:

    cd ../B               # change to B's working directory
    git checkout master   # change from temp branch to branch master
    

    5 - You can delete the temporary branch if you won't be using it anymore:

    git branch -d temp    # delete branch temp
    

    6 - If you are doing new modifications in C, you don't need to do both steps 4 and 5. If you do, any time you wish to do modifications in C, you will need to do both steps 1 and 2 beforehand.

    And this solves your issue! Probably...

    Clarifications & Reinforcements