Week 6

Day 5 - "I'm putting my foot down"

Pushing back!

We now know a lot about remote locations right? We are ready to start collaborating with someone. Let us first see how Tamagoyaki are getting on with things.
In the trenches...
"But I know I have the remote right Simon, it's giving some weird error about refusing to update it." Rob was getting more than a little cross now.

"Maybe you got the command wrong?" asked Simon.

Rob slammed his hands down on the keyboard, "Well then you come over here and type it, but I don't understand how hard it is to write git push." He shook his head, "I even checked using the git remote tool and the push is apparently all set up."

"Are you trying to push to a non-bare repository?" Chirped Klaus. He had been listening to the discussion escalate from mild annoyance to key shattering intrusion. "You can't push to a non-bare repository, else everything gets out of sync."

"What do you mean bare?" asked Rob.

Klaus smiled and his head appeared over the cubicle. "Come over here genius and I'll show you," he said with the slightest amount of gloat at being one of the only people to know something about Git that Rob didn't.

Bare? Non-bare? At first glance, this may seem confusing. It isn't exactly an intuitive word to describe a repository but what Klaus mentioned was absolutely true. In Git, if you want to push changes back to a repository, instead of pulling them, the repository that you push to should be what is called a bare repository. What this means is that the repository has no working copy.

Note - Pushing to a non-bare repository

As mentioned, pushing to a non-bare repository is not a good idea. When you push to a repository, you update the objects in the objects database. This will affect commits, blobs and trees. We should make a distinction here. We are really only worried about pushing to a branch which is currently checked out. Pushing to a non-bare repository will mean that the index will get changed, as it should reflect what the branch looks like at HEAD.

Now this causes a problem when we are trying to see what has been modified in the working directory when compared to the HEAD. As our working copy will contain older versions of the files than are in the index, it will appear as if many more changes have occurred and we risk undoing those changes. For these reasons, it is much simpler just to push only to bare repositories.

At first glance this may seem like a rather odd thing to want. Why would we want a repository that doesn't have a working tree? The simple answer is that a bare repository allows us to be able to push changes into it. Let us recreate the error message that Rob was talking about.
john@satsuki:~/coderepo-cl$ echo "This is another update to newfile3" >> newfile3
john@satsuki:~/coderepo-cl$ git commit -a -m 'Added more to newfile3'
[wonderful dbf1e9a] Added more to newfile3
1 files changed, 1 insertions(+), 0 deletions(-)

We have made a modification to the wonderful branch. So let us now try to push that back to the origin remote repository with the git push command.
john@satsuki:~/coderepo-cl$ git push
Counting objects: 5, done.
Compressing objects: 100Writing objects: 100Total 3 (delta 1), reused 0 (delta 0)
Unpacking objects: 100remote: error: refusing to update checked out branch: refs/heads/wonderful
remote: error: By default, updating the current branch in a non-bare repository
remote: error: is denied, because it will make the index and work tree inconsistent
remote: error: with what you pushed, and will require 'git reset --hard' to match
remote: error: the work tree to HEAD.
remote: error:
remote: error: You can set 'receive.denyCurrentBranch' configuration variable to
remote: error: 'ignore' or 'warn' in the remote repository to allow pushing into
remote: error: its current branch; however, this is not recommended unless you
remote: error: arranged to update its work tree to match what you pushed in some
remote: error: other way.
remote: error:
remote: error: To squelch this message and still keep the default behaviour, set
remote: error: 'receive.denyCurrentBranch' configuration
remote: error: variable to 'refuse'.
To /home/john/coderepo
! [remote rejected] wonderful -> wonderful (branch is currently checked out)
error: failed to push some refs to '/home/john/coderepo'
john@satsuki:~/coderepo-cl$

Bingo! There is the error we were looking for. In order to get round this, we are going to create another clone of the repository in coderepo. This time however, we are going to pass an option to it when we create it.
john@satsuki:~/coderepo-cl$ cd ..
john@satsuki:~$ git clone coderepo coderepo-bk --bare
Initialized empty Git repository in /home/john/coderepo-bk/
john@satsuki:~$ cd coderepo-bk/
john@satsuki:~/coderepo-bk$ ls
branches config description HEAD hooks info objects
packed-refs refs
john@satsuki:~/coderepo-bk$ cd ..
john@satsuki:~$ cd coderepo-cl/
john@satsuki:~/coderepo-cl$

As you can see, creating the clone with the --bare option has had a definite effect on the structure of the clone itself. In fact, the contents of the coderepo-bk folder look remarkably similar to the .git folder found in our normal working tree. They are in fact, one and the same. We have removed the need for a separate .git folder because we do not require a working tree. Therefore, the contents of the .git folder and placed in the root coderepo-bk folder.

We are getting closer to making our push. We have created a bare repository, which we should now be able to push to. The problem is, we have not yet defined our new repository as a remote location in the coderepo-cl repository. This is something we will do now and to do this, we will use our git remote tool once more, this time employing the add parameter.
john@satsuki:~/coderepo-cl$ git remote add backup
/home/john/coderepo-bk
john@satsuki:~/coderepo-cl$ git remote show backup
* remote backup
Fetch URL: /home/john/coderepo-bk
Push URL: /home/john/coderepo-bk
HEAD branch: wonderful
Remote branches:
master new (next fetch will store in remotes/backup)
wonderful new (next fetch will store in remotes/backup)
zaney new (next fetch will store in remotes/backup)
Local refs configured for 'git push':
master pushes to master (up to date)
wonderful pushes to wonderful (fast-forwardable)
john@satsuki:~/coderepo-cl$

We have created a remote called backup. As you can see, some interrogation of the remote repository has taken place. It is already aware of the branches present in our remote location. Let us do a git fetch to update the local references.
john@satsuki:~/coderepo-cl$ git fetch backup
From /home/john/coderepo-bk
* [new branch] master -> backup/master
* [new branch] wonderful -> backup/wonderful
* [new branch] zaney -> backup/zaney
john@satsuki:~/coderepo-cl$ git remote show backup
* remote backup
Fetch URL: /home/john/coderepo-bk
Push URL: /home/john/coderepo-bk
HEAD branch: wonderful
Remote branches:
master tracked
wonderful tracked
zaney tracked
Local refs configured for 'git push':
master pushes to master (up to date)
wonderful pushes to wonderful (fast-forwardable)
john@satsuki:~/coderepo-cl$

Now we can see that all of the remote references for our backup remote have been updated. If we run a diff against our local wonderful branch and the remote branch backup/wonderful, we can see the differences.

Note - Pushing your tags

By default tags are not pushed when using the git push command. If we want our tags to be push to the remote repository as well, we need to append the --tags parameter to the git push command.

In contrast, using git pull will automatically pull down any tags which refer to any objects that have been pulled. This behaviour can be overridden by using the --no-tags parameter with git pull.

john@satsuki:~/coderepo-cl$ git diff wonderful backup/wonderful
diff --git a/newfile3 b/newfile3
index 7268b97..638113c 100644
--- a/newfile3
+++ b/newfile3
@@ -1,2 +1 @@
These changes are in the origin
-This is another update to newfile3
john@satsuki:~/coderepo-cl$ git branch -v

There we go! Those are the changes we just committed to the local repository. The last thing we need to do is to initiate a git push. We are going to have to define which remote we wish to push to now for two reasons. The first is that now we have two remotes, and so Git would not know if we meant origin or backup. The second reason is that by default, the wonderful branch is set up to push to the origin remote.
john@satsuki:~/coderepo-cl$ git push backup
Counting objects: 5, done.
Compressing objects: 100Writing objects: 100Total 3 (delta 1), reused 0 (delta 0)
Unpacking objects: 100To /home/john/coderepo-bk
1c3206a..dbf1e9a wonderful -> wonderful
john@satsuki:~/coderepo-cl$

There we go, we have now managed to push our changes to a remote location. In this case, we have pushed an update from the wonderful branch to the backup/wonderful branch. If we wanted to, we could have pushed our repository to a new branch name, or indeed any of the existing branch names.
john@satsuki:~/coderepo-cl$ git push backup wonderful:newbranch
Total 0 (delta 0), reused 0 (delta 0)
To /home/john/coderepo-bk
* [new branch] wonderful -> newbranch
john@satsuki:~/coderepo-cl$ git remote show backup
* remote backup
Fetch URL: /home/john/coderepo-bk
Push URL: /home/john/coderepo-bk
HEAD branch (remote HEAD is ambiguous, may be one of the following):
newbranch
wonderful
Remote branches:
master tracked
newbranch tracked
wonderful tracked
zaney tracked
Local refs configured for 'git push':
master pushes to master (up to date)
wonderful pushes to wonderful (up to date)
john@satsuki:~/coderepo-cl$

By specifying the local and remote branch using the local:remote syntax, we have told Git to push our local wonderful branch to the remote branch called newbranch. As the newbranch did not exist in the remote backup repository, it was created, as can be seen above.

Note - Killing a remote branch

Now that we have made branches on the remote end, it is a good idea to know how to delete them also. After all, if all you ever did was push new branches, pretty soon you may end up with a lot of rubbish in your repository. To delete a remote branch, all that you need to do is to use the following syntax.
git push <remote> :<branchname>

Notice that we are using the same syntax as before, but not specifying a local branch name, just the remote. Git interprets this as a call to delete the remote branch.

If you have a clone of a remote repository and a remote branch is removed, you may see a message like the following if you run git remote show <remote>;
refs/remotes/<remote>/<branchname> stale (use 'git remote prune' to remove)

So we can run git remote prune to remove local references to remote branches that no longer exist. If you have created a tracking branch of this remote branch, this will remain unaffected.

So this ends the Week. We have learnt a lot more about how to play with branches and remotes. In the After Hours section, we will take a deeper look at remote branches and how they are configured and represented in Git.

Previous Day

Next Day

 
   
home | download | read now | source | feedback | legal stuff