2

I'm new to git and Github. I have learned many of the terms (push, pull, commit, branch, etc), but I'm going to mostly use common parlance to explain my initial expectations.

I assumed the process was:

1.) Create a git repo out of my existing files.
2.) Copy those to my Github account to create a central "hub".
3.) Anyone who works on the project will check it out from Github, do work on their computers, then upload the changes to the hub.
4.) Anyone else working on the same branch can easily upload their changes as well, and will be warned if there are any conflicts. They can also easily download changes since their last download to update their local copy.

So I did step 1 and 2. And I created a few branches. I've made a few commits and pushed the changes to Github. I also cloned the master branch on another machine, and then branched off of that. I pushed the new branch to Github. Everything seems to be working fine. But now, as I prepare to add other developers to the project, I've come across repeated admonitions that the "central" repository should be "bare."

Here, here ("it's not recommended to push into a non-bare repository"), and atlassian says "Central repositories should always be bare repositories", and several other places. Apparently, if it's not bare and someone else pushes a new file, then my local version will consider itself to have deleted that file when I push. I'll end up undoing the work of other developers, the very problem I was trying to fix with git!

That has me worried, since I've already set up my Github project with a bunch of files. I was about to set to work figuring out who to convert my hub to a bare repo, when I came across some information that suggests that with newer versions of Git, you don't need a bare repository at the hub. I think it has to do with version 2.3 having updated behavior for git push. Is that true and why?

And what do I do if my local Windows machine has version 2.7, but another CentOS machine is using 1.7.1?

Community
  • 1
  • 1
Buttle Butkus
  • 9,206
  • 13
  • 79
  • 120

3 Answers3

6

The problem with non bare repositories is that they have a checked-out branch, and if someone pushes to that branch, then things go weird: The working copy and the index will be behind the branch. That should not happen in a normal workflow: imagine what will happen if just after receiving a pushed branch in your non-bare main repo, you do a one-line commit? Your commit would actually roll back all the received changes, without even adding any file to the index.

GitHub repos are bare. Although you can see the files and the branches, there is nothing actually checked out (you cannot commit in a github branch directly!).

rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • Hi @rodrigo. I think perhaps I'm starting to understand what you mean by "you cannot commit in a github branch directly". I download (pull, and checkout) a branch from my GitHub repo. I commit changes locally. Once they are committed, I can upload (push) back to the Github repo, where that branch will be updated (the index will be updated?). So only my local, non-bare repo can be "out of date", right? And once I commit and push, GitHub and I are once again in synch. – Buttle Butkus Nov 13 '15 at 00:17
  • @ButtleButkus: All correct, except that bare repositories, such as GitHub, do not have an index. The index is where files go when you do `git add`, and bare repos don't do that. – rodrigo Nov 13 '15 at 00:31
3

The GitHub repositories are always bare, so you have nothing to worry about.

The advice still holds. The change in version 2.3 has to do with which branches are pushed by default.

Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
  • Oh that's good to know. So far I have not tried any conflicting pushes. What about this: "As of git 1.7.0, the default value for receive.denyCurrentBranch has changed from "warn" to "refuse". As a result, on 1.7.0 and above, by default, you will not get into this sort of trouble." Referring to changes in another checked out version being undone when you push your version? http://web.archive.org/web/20130820233326/http://gitolite.com/concepts/bare.html – Buttle Butkus Nov 12 '15 at 22:46
  • Again, if you are pushing to GitHub, you won't ever notice, because the GitHub repo is bare. The `receive.denyCurrentBranch` only affects what happens when you push to a non-bare repository, which you aren't doing. – Dietrich Epp Nov 12 '15 at 22:48
3

I see that rodrigo has already given the right answer and the reason for it, but given the question ("is this obsolete") I want to add something. It's is not obsolete advice (and probably will never be), but git has added a new ability "out of the box" as it were:

receive.denyCurrentBranch
[snip]
Another option is "updateInstead" which will update the working tree if pushing into the current branch. This option is intended for synchronizing working directories when one side is not easily accessible via interactive ssh (e.g. a live web site, hence the requirement that the working directory be clean). This mode also comes in handy when developing inside a VM to test and fix code on different Operating Systems.

In other words, if you have a non-bare repository that you want to use both as a repository (not just a raw deployment) and as a deployed (but not edited) tree, you can now do that.1 This has been in place since git version 2.3, with a minor fix2 in version 2.4.

The way to use it is simple—simpler than the fancy deployment mechanisms people put into post-receive hooks, so perhaps worth the extra space-cost of keeping a complete repository. You simply clone the repository where you want the tree to wind up, check out the branch you want deployed here (or add -b to the clone command), then use git config to set receive.denyCurrentBranch to updateInstead:

$ git clone -b staging <url>
$ cd <repo>
$ git config receive.denyCurrentBranch updateInstead

and now anyone who pushes to staging in this new clone will update the checked-out staging branch, provided no one touches it to cause it to become "dirty".


1Technically it has always been possible, by setting receive.denyCurrentBranch to false and writing a post-receive or update hook that "did the right thing", but it was kind of error-prone.

2The fix is that you can push to an unborn branch as well now. That is, if you did a git clone then did a git checkout -b to create a new branch, but have made no commits on it yet, you can now cause that branch to become a "real branch"3 with a git push, as long as the remaining conditions (clean index and work-tree) are met.

3A new but "unborn" branch exists only as a symbolic reference in the HEAD file, not as a refs/heads/<branch> file or entry in the packed-refs file. This means that if you do another git checkout to some other branch, the unborn branch vanishes, with no sign it ever existed. In that sense one might call it a "virtual" branch, or at least "not quite real".

Community
  • 1
  • 1
torek
  • 448,244
  • 59
  • 642
  • 775
  • So this new functionality would allow me to keep a production server up-to-date by pushing changes directly to it, which it will accept as long as it is clean itself. The working files would be the production files. But the more I read the more it seems preferable to just to log in to the production server and pull down from GitHub. – Buttle Butkus Nov 13 '15 at 00:25
  • Yes, that's the intent of the new feature. i have not used it at all myself though. – torek Nov 13 '15 at 00:40