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
Whenever you git clone
, from a developer point of view, you'll want to work on the code that you just cloned. So GIT gives you a "working tree" to work upon. It's called a tree because it resembles one when you consider all the commits and branches you made and put on a graph.
The cloned repository is called non-bare repository. To create a non-bare repository, you do a simple git init
- which is what the original programmer did to start tracking the cloned code with GIT. You could also clone it into a bare repository but the details and usefulness of it should be an answer of its own in a proper question regarding it.
A bare repository does not contain a working tree. It is only meant to store your code - a server for code managed by GIT, if you prefer. To create a bare repository, you do a simple git init --bare name_of_repository.git
. It will create a directory named name_of_repository.git containing all the needed files by GIT. The git extension is just a convention used; it isn't required and could be anything or nothing at all.
There is something like a pointer in GIT that is called HEAD. It points to the latest commit that is active in the branch you work on, be it a bare or a non-bare repository.
Branches are like 'different copies' of the code you just pulled from a remote repository (that might have different modifications or not). It has whatever names the developer thought to be proper. They are useful because you can work on different functions or fix different problems without worrying about the current code being developed by you or others. Later, you can always merge everything into the main branch - usually master - and then delete those merged branches that are not necessary anymore.
GIT tries its best to avoid problems between versions of files of different locations or branches. So he won't allow you to git push
in some cases it determines to be chaotic to say the least. GIT is never wrong because it asks you to check, change or force what you are doing. So any error won't be GIT's fault but yours only.
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!
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...
git branch name_of_the_branch
creates a new branch;git checkout name_of_the_branch
makes the HEAD points to this new branch;git checkout -b name_of_the_branch
creates a branch and makes the HEAD point to it in one single command. I used the longer method because you should know the longer method as well;