Created
May 14, 2013 18:33
-
-
Save skopp/5578308 to your computer and use it in GitHub Desktop.
git bare vs non-bare repos
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| !!! | |
| %html{:lang => "en"} | |
| %head | |
| %title Git bare vs. non-bare repositories | |
| %meta{:charset => "utf-8"}/ | |
| %meta{:content => "git, mercurial, push, pull, revision control, repository, repositories, cvs, remote repository", :name => "keywords"}/ | |
| %meta{:content => "The Git revision control system has something called a bare and a non-bare repository. This article deals with the issue and also compares the Git design to the design of Mercurial and Bazaar.", :name => "description"}/ | |
| %link{:href => "/themes/bitflop/stylesheet.css", :media => "screen", :rel => "stylesheet", :type => "text/css"}/ | |
| %link{:href => "/rss.php", :rel => "alternate", :type => "application/rss+xml"}/ | |
| %body{:onload => "sh_highlightDocument();"} | |
| .maindiv | |
| .top | |
| .menucontainer | |
| .menu | |
| .menuitems | |
| %a.menuitem_home{:href => "http://www.bitflop.com/document/16"} Home | |
| | | |
| %a.menuitem{:href => "http://www.bitflop.com/category/articles"} Articles | |
| | | |
| %a.menuitem{:href => "http://www.bitflop.com/category/tutorials"} Tutorials | |
| | | |
| %a.menuitem{:href => "http://www.bitflop.com/document/30"} About me | |
| | | |
| %a.menuitem{:href => "http://www.bitflop.com/document/32"} Links | |
| %div{:style => "width:728px;margin:20px auto 50px auto;"} | |
| .page | |
| %h1 Git bare vs. non-bare repositories | |
| .posted_by Posted by: Kim N. Lesmer on 09.10.2010 | |
| %div | |
| #abstract | |
| The Git revision control system has something called a "bare" and a "non-bare" repository. This article deals with the issue and also compares the Git design to the design of Mercurial and Bazaar. | |
| %br/ | |
| = succeed "NB!" do | |
| %br/ | |
| The article isn't relevant to Git prior to version 1.7.0. | |
| %p The other day I was working with Git and I got the following error after having tried to push some changes back to a remote repository that I had created. | |
| %pre | |
| :preserve | |
| remote: error: refusing to update checked out branch: refs/heads/master | |
| 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 variable to 'refuse'. | |
| %p I didn't understand this error and being used to how Mercurial works, this didn't make any sense, so I did some diggin. | |
| %p In the ideal world of distributed revision control there is no central repository. People just pull from whomever they want changes from and no pushes exist. That is actually how the Linux Kernel is being developed. | |
| %p In the real world a central repository with push access is sometimes necessary and all the different distributed revision control systems allows this, but the way they deal with a push are very different. | |
| %p In a distributed revision control system you work with a local repository that contains both the working tree and the revision history. | |
| %p In Git you can create such a repository with the following command: | |
| %pre | |
| :preserve | |
| $ mkdir my_repo | |
| $ cd my_repo | |
| $ git init | |
| %p In Bazaar it is done the same way: | |
| %pre | |
| :preserve | |
| $ mkdir my_repo | |
| $ cd my_repo | |
| $ bzr init | |
| %p In Mercurial a repository is created in almost the same way the only difference is that the init command creates the directory if it doesn't exist: | |
| %pre | |
| :preserve | |
| $ hg init my_repo | |
| %p In all three example the working tree resides in the directory itself and the revision history and system files resides in a hidden sub-directory. People then normally pull changes from eachother. | |
| %p | |
| In Git the hidden sub-directory is called | |
| = succeed "," do | |
| %i .git | |
| in Bazaar it is called | |
| = succeed "," do | |
| %i .bzr | |
| and in Mercurial it is called | |
| = succeed "." do | |
| %i .hg | |
| %p In Mercurial and Bazaar when you initialize a repository it can serve as a remote repository by default, and anyone with write access to the repository can push changes into it. | |
| %p In Git that's not possible unless the repository is initialized as a "bare" repository. | |
| %p | |
| A "bare" repository in Git just contains the version control information and no working files (no tree) and it doesn't contain the special | |
| %i .git | |
| sub-directory. Instead, it contains all the contents of the | |
| %i .git | |
| sub-directory directly in the main directory itself. | |
| %p A "non-bare" repository in Git is the same as the normal repository in Mercurial and Bazaar. It has a bunch of working files (the tree), and a hidden directory containing the version control information. | |
| %p In Git (from version 1.7.0 and above) the repository has to be "bare" (no working files) in order to accept a push. | |
| %p From a technical point of view you can (in theory) push and pull between repositories whether they are "bare" or not. Git has an index, which basically tells it what the head of the current branch looks like. If you push to a "non-bare" repository, Git will look at the working files, compare them to the index, and see that they differ - so it will think that the working files have changed. | |
| %p "bare" repositories exist in Git as a way of having a central (mainly remote) repository that a number of people can push to. If you want to transfer changes from a "non-bare" repository to another, the correct way is to pull from the destination rather than push from the target. | |
| %p In Git you should only use a "bare" repository to clone and pull from, and push to. It doesn't have a checked out tree, so it just does what the "server" notionally does in a centralized VCS - records commits, branches, etc when you push to it, and gives you the latest versions when you clone or pull from it. | |
| %p In Mercurial any repository can serve as a remote repository as mentioned, but push changes only affects the version control and not the working tree unless someone physically access the remote repository (making it local from his or hers perspective) and updates the tree manually. | |
| %p So in Git a push isn't possible unless the repository is "bare" (no working files) and in Mercurial a push is only affecting the version control (not any working files). | |
| %p In Bazaar on the other hand any repository can serve as a remote repository, and any push changes also affects the working tree. | |
| %p From a distributed technical point of view IMHO the Bazaar system is poorly designed and the way both Git and Mercurial addresses the issue is much safer. | |
| %p In Git and Mercurial a repository with a working tree is expected to contain files that someone is working on. It isn't looked upon as a remote repository, but as a local distributed repository no matter where it resides physically. And this makes perfect sense from a distributed point of view. | |
| %p In Mercurial if an empty repository is cloned and files are added and then pushed back into the remote repository then the remote repository will contain no working files (no tree) until and unless someone updates the repository manually. | |
| %p If you want a Git repository to function like a remote backup, you have to create the repository as a "bare" repository. Meaning: Don't insist on having a working tree on your remote backup copy - only use it as a backup. It is not meant to contain any working files and it will not contain any. | |
| %p A quick rule of thumb is to never push into a repository that has a work tree attached to it, until you REALLY know what you are doing no matter what distributed revision control system you are using. | |
| %p As long as you don't need to physically work on the remote repository then having a working tree remotely doesn't make sense anyway and you don't need it. And if you do need to work on the remote repository then pull changes in rather than accept pushes. | |
| %p Any repository that someone is working on is not something that should receive changes without their approval since such changes might create problems. | |
| %p If you want to have Git working as a remote backup repository, you have to create the remote repository as a bare repository: | |
| %pre | |
| :preserve | |
| $ mkdir my_remote_backup_repo | |
| $ cd my_remote_backup_repo | |
| $ git --bare init | |
| %p Now you can clone that remote repository and push files back into it, but you wont have a working tree in the remote location. | |
| %p Question: How do I turn an existing "non-bare" repository into a "bare" repository in Git (taken from the Git wiki)? | |
| %p Answer: A safe method is to let Git handle all the internal settings for you by doing something like this: | |
| %pre | |
| :preserve | |
| $ git clone --bare -l non_bare_repo new_bare_repo | |
| %p Question: How do I do the opposite? (Turn an existing "bare" repository into a "non-bare" repository in Git)? | |
| %p Answer: You just clone it and delete the original. | |
| %p | |
| See | |
| %a{:href => "http://sitaramc.github.com/concepts/bare.html"} http://sitaramc.github.com/concepts/bare.html | |
| for futher information on Git bare and non-bare repositories. | |
| %p | |
| Linus Thorvalds gives a great talk about | |
| = succeed "." do | |
| %a{:href => "http://www.youtube.com/watch?v=4XpnKHJAok8"} Git and distributed revision control on Google Talk | |
| %p | |
| %i If you have any comments or corrections feel free to email them to me. | |
| %br/ | |
| %br/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| %head | |
| %title | |
| all about "bare" repos -- what, why, and how to fix a non-bare push | |
| :css | |
| body { background: #fff; margin-left: 40px; font-size: 0.9em; font-family: sans-serif; max-width: 800px; } | |
| h1 { background: #ffb; margin-left: -30px; border-top: 5px solid #ccc; } | |
| h2 { background: #ffb; margin-left: -20px; border-top: 3px solid #ddd; } | |
| h3 { background: #ffb; margin-left: -10px; } | |
| h4 { background: #ffb; } | |
| code { font-size: 1.1em; background: #ddf; } | |
| pre { margin-left: 2em; background: #ddf; } | |
| pre code { font-size: 1.1em; background: #ddf; } | |
| %base{:href => "http://sitaramc.github.com/"}/ | |
| %p{:style => "text-align:center"} | |
| %a{:href => "master-toc.html"} master TOC | |
| | | |
| %a{:href => "master-toc.html#concepts/bare"} chapter TOC | |
| | | |
| %a{:href => "license.html"} license | |
| %p | |
| %a{:name => "concepts_bare_all_about_bare_repos_what_why_and_how_to_fix_a_non_bare_push_"} | |
| %h1 all about "bare" repos -- what, why, and how to fix a non-bare push | |
| %p | |
| %center | |
| %strong | |
| %em Update for git 1.7.0 and above | |
| %blockquote | |
| %p | |
| As of git 1.7.0, the default value for | |
| %code 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. However, this | |
| document is still useful as a detailed explanation of what precisely this | |
| problem is. Just pretend, as you read along, that you're on a pre-1.7.0 | |
| system or that someone set | |
| %code receive.denyCurrentBranch | |
| to "warn", "ignore" | |
| or 'false'. | |
| %p | |
| %a{:name => "concepts_bare_what_is_a_bare_repo__"} | |
| %h2 what is a bare repo? | |
| %p | |
| A bare repository is a concept that is sort of unique to a Distributed VCS | |
| like git (and, I presume, other such DVCSs like Hg/Bzr/etc also). | |
| %p A normal git repository is a directory that contains | |
| %ul | |
| %li project directories and files (the "working tree" mentioned above) | |
| %li | |
| a single directory called | |
| %code .git | |
| containing all of git's | |
| administrative and control files; we'll call it the | |
| %strong magic | |
| directory | |
| because git can't do any magic without it :-) | |
| %p | |
| When you do a | |
| %code git status | |
| inside such a directory, git looks inside the | |
| "magic" directory, compares your current working tree with the "current | |
| branch" as recorded in the magic directory, and tells you what files have | |
| changed, etc etc. | |
| %p | |
| A "bare" repo, as the git | |
| %a{:href => "http://www.kernel.org/pub/software/scm/git/docs/gitglossary.html"} glossary | |
| says, is a repository that does not contain a "working tree" at all. It | |
| doesn't contain the special | |
| %code .git | |
| sub-directory either; instead, it | |
| contains all the contents of the | |
| %code .git | |
| subdirectory right in the main | |
| directory itself. | |
| %p | |
| %a{:name => "concepts_bare_yeah_yeah_but_why_do_I_need_a_bare_repo__"} | |
| %h2 | |
| yeah yeah, but | |
| %strong why | |
| do I need a bare repo? | |
| %p ok; demo time... | |
| %p | |
| Let's try creating a small repo, adding a file, committing, and checking | |
| %code> git status | |
| \: | |
| %pre | |
| %code | |
| :preserve | |
| mkdir a; cd a; git init | |
| echo hi > a;git add a; git commit -m a | |
| git status | |
| %p This should respond | |
| %pre | |
| %code | |
| :preserve | |
| # On branch master | |
| # nothing to commit (working directory clean) | |
| %p | |
| So far so good. Now someone clones our repository, adds a new file, commits, | |
| and pushes his changes back to our repository: | |
| %pre | |
| %code | |
| :preserve | |
| cd ..;git clone a b | |
| cd b; echo there >> b; git add b; git commit -m b | |
| git push | |
| %p | |
| The | |
| %code git push | |
| above sends your new commits to the "origin" repository. More | |
| specifically, it updates the "magic" directory on repo "a" with this new | |
| commit. | |
| %p | |
| Now you go back to the main repo and check | |
| %code git status | |
| %pre | |
| %code | |
| :preserve | |
| cd ../a | |
| git status | |
| %p which responds | |
| %pre | |
| %code | |
| :preserve | |
| # On branch master | |
| # Changes to be committed: | |
| # (use "git reset HEAD <file>..." to unstage) | |
| # | |
| # deleted: b | |
| %p | |
| Whoa! What happened here? We | |
| %strong added | |
| a file called | |
| %code b | |
| in the cloned | |
| repository and pushed to the "origin". But your origin now claims you | |
| %strong deleted | |
| that file...? | |
| %p | |
| To understand this, you need to realise that | |
| = succeed ";" do | |
| %strong | |
| the "magic" directory is always | |
| assumed to be correct | |
| it is the "standard" against which your working tree | |
| is compared to determine what changes you made in your working tree. | |
| %p | |
| So when you asked for a status, git first looked inside the magic directory. | |
| The magic directory said you should have two files, "a" and "b", but your work | |
| tree has only file "a". So | |
| %code git status | |
| concludes that you have deleted | |
| the file "b"! | |
| %p | |
| In other words, when someone changes the "magic" directory | |
| = succeed "," do | |
| %strong | |
| behind your | |
| back | |
| your locally checked out copy (your working tree) appears to have the | |
| %strong opposite | |
| changes made by you. | |
| %p | |
| All this confusion can (and | |
| = succeed ")" do | |
| %em should | |
| be avoided by using a "bare" | |
| repository to clone and pull from, and push to. It doesn't have a checked out | |
| tree, so it just does what the "server" notionally does in a centralised VCS | |
| \-- records commits, branches, etc when you push to it, and gives you the | |
| latest versions when you clone or pull from it. | |
| %p | |
| %a{:name => "concepts_bare_how_do_I_fix_such_a_non_bare_push__"} | |
| %h2 how do I fix such a non-bare push? | |
| %p | |
| If you did push into a non-bare repo, the first thing to do is make sure that | |
| no one is working on that checked out work tree, and no local changes have | |
| been made since the last checkout. | |
| %p | |
| Once you've made sure of that, you should log onto that machine, cd to that | |
| repo, and do this: | |
| %pre | |
| %code | |
| :preserve | |
| git reset --hard HEAD | |
| %p | |
| If you had local changes on that work tree, or you are not sure if you had | |
| any, then you have a bit of a challenge. There are ways to deal with it quite | |
| nicely in git, but I haven't tried them yet. Ideas: | |
| %ol | |
| %li | |
| %p | |
| see if any of the HEADs in reflog match your current work tree exactly, in | |
| which case you really do NOT have any local changes, and you can safely do | |
| that reset. Use | |
| %code git reflog show your-branch-name | |
| to find the topmost | |
| commit after the most recent push or set of pushes (in case there were | |
| more than one) and compare your work tree against that. | |
| %p Let's say your reflog output looks like this: | |
| %pre | |
| %code | |
| :preserve | |
| $ git reflog show master | |
| fcbb5a7 master@{0}: push | |
| e41b4ce master@{1}: push | |
| 6ef3360 master@{2}: commit: blah blah more blah | |
| a48b324 master@{3}: commit: blah blah blah | |
| 48e4b98 master@{4}: commit (initial): first commit | |
| %p | |
| As you can see, there were two "behind your back" pushes, and it is very | |
| likely that master@{2} is the "base" for your current work tree. So you | |
| compare against that: | |
| %pre | |
| %code | |
| :preserve | |
| $ git diff --stat --exit-code master@{2} | |
| %p | |
| If there are no differences, you're probably OK to do that | |
| = succeed "." do | |
| %code | |
| git reset | |
| \--hard | |
| If you're really paranoid, you'll check to make sure that | |
| master@{2} is an ancestor of HEAD: | |
| %pre | |
| %code | |
| :preserve | |
| git rev-list HEAD..master@{2} # should be empty | |
| %li | |
| %p | |
| anyway, if the diff above showed differences, you have a merge of some | |
| sort coming up. Here's what you do: | |
| %ul | |
| %li | |
| %p make a temporary branch off that same commit | |
| %pre | |
| %code | |
| :preserve | |
| git checkout -b temp master@{2} | |
| %li | |
| %p now you have two alternatives: | |
| %ul | |
| %li | |
| %p | |
| The safe way: commit those changes. If the work is not complete, | |
| complete it -- you have all the time in the world now that you have | |
| created a temp branch, and any number of pushes can now happen on | |
| 'master' without affecting you, since it isn't the checked out branch. | |
| %li | |
| %p | |
| The quicker way: stash those changes using | |
| = succeed "." do | |
| %code git stash | |
| I am 99% sure | |
| stashing will work as well here, but would appreciate hearing from any | |
| git.experts reading this (email me at [email protected]) | |
| %li | |
| %p | |
| now checkout master and either merge temp or | |
| %code git stash pop | |
| depending | |
| on which option you chose above; it's just a normal branch now! | |
| %p | |
| %a{:name => "concepts_bare_how_do_I_prevent_the_problem_from_happening_again__"} | |
| %h2 how do I prevent the problem from happening again? | |
| %p The best option is upgrade to git 1.7.0 or later! | |
| %p | |
| If you cannot upgrade, and if you or others will continue to push into this | |
| repo, you have to make it a bare repo. | |
| %p | |
| To make it a "real" bare repo, just delete all the files except | |
| = succeed "," do | |
| %code .git | |
| then | |
| %code> mv .git/* .; rmdir .git | |
| \. Finally, edit the file called | |
| %code config | |
| and change | |
| %code bare = false | |
| to | |
| = succeed "." do | |
| %code bare = true | |
| %p | |
| If you're not comfortable playing with git at that level, there is another | |
| alternative. You may have guessed by now that you can make a non-bare repo | |
| look like a bare repo for push purposes by creating and checking out a branch | |
| that your users will not push to. The entire repo does not have to be bare, | |
| really... | |
| %p | |
| So just run | |
| %code git checkout -b DUMMY_BRANCH_DONT_USE | |
| (or some such name) and | |
| keep this branch checked out forever in this repo. This is a cheap way to | |
| make a non-bare repo into a bare repo for all practical purposes. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment