How do I convert a bare git repository into a normal one (in-place)?

43,277

Solution 1

Note: I tested this on a very simple 1-commit repository. Double-check this, read the man pages, and always be happy you've backed up before following advice you found on StackOverflow. (You do back up, right?)

To convert a --bare repository to a non-bare:

  1. Make a .git folder in the top-level of your repository.
  2. Move the repository management things (HEAD branches config description hooks info objects refs etc.) into the .git you just created.
  3. Run git config --local --bool core.bare false to convert the local git-repository to non-bare.
  4. (via comment by Tamás Pap) After step #3 you will see that you are on branch master (or whichever your main branch is) and all your files are deleted and the deletion is staged. That's normal. Just manually checkout master, or do a git reset --hard, and you are done.
  5. (to resolve issue reported by Royi) Edit .git/config file adding line fetch = +refs/heads/*:refs/remotes/origin/* after url = <...> in [remote "origin"] section. Otherwise git fetch will not see origin/master and other origin's branches.

These steps are in the opposite direction of this question, "git-convert normal to bare repository" - in particular note this answer, which states that the above steps (in, I presume, either direction) is different from doing a git-clone. Not sure if that's relevant to you, though, but you mentioned git clone in the question.

Solution 2

I had a slightly different scenario:

Solution:

  • clone a bare repo in that content, in a .git dir:
    git clone --bare https://github.com/user/project .git
  • Mark it as a non-bare repo:
    git config --local --bool core.bare false
  • reset the index (otherwise, it believes everything has been deleted, since a .git bare repo doesn't include a file 'index'.)
    git reset HEAD -- .
    That restores the .git/index.

I have effectively transformed a bare repo into a non-bare one, while preserving the content I had previously got.
The full script I have been using for years involves the steps:

cd /path/to/current/worktree

# That creates a .git directly at the right place
git clone --bare /url/of/repo .git

# restore the link between the local repo and its upstream remote repo
git config --local --bool core.bare false
git config --local remote.origin.fetch +refs/heads/*:refs/remotes/origin/*
git fetch origin
git branch -u origin/master master

# reset the index (not the working tree)
git reset HEAD -- .

But I do recon the accepted solution (with the helpful git reset step added by ADTC) is simpler.

Solution 3

The original poster's question is about not having enough space to do things the simple way. For those that do have enough space, the answer is far simpler:

git clone foo.git foo

Solution 4

To simplify and combine the information in the answers:

There are three differences that make a bare repo different from a normal .git folder:

  • core.bare is set to true in config file
  • index file and working tree do not in exist
  • a default refspec for the "origin" remote is not generated

So, you can simply move your bare repo to be the .git subfolder of a new folder,

mkdir clone
mv bare.git clone/.git

Change core.bare:

cd clone
git config --local --bool core.bare false

Add a default origin refspec to make git fetch and git push pick the same defaults as usual:

git config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'

And generate the index file and working tree:

git checkout master

I recommend git checkout rather than git reset to generate the files, in case it is accidentally typed into the wrong place.

Solution 5

If you are low on diskspace, expanding the working tree by converting to a normal repository will be an issue, but you can browse the contents of a bare repo without converting it. Use git cat-file -p <commit-sha> on any commit to see the tree to which it refers. Use git cat-file -p <blob-sha> to see the contents of the file referenced by the blob. Use git show <sha>:path where sha is either a commit or a tree to see the contents of the blob at path.

Share:
43,277

Related videos on Youtube

nyi
Author by

nyi

Updated on July 29, 2021

Comments

  • nyi
    nyi almost 3 years

    I have a bare git repository, but need to access and browse its contents over ssh (in a file manager like user experience).

    I assume I could clone it:

    git clone -l <path_to_bare_repo> <new_normal_repo>
    

    However, my repository is about 20GB in size and I don't have the space to duplicate it. Is there a way to convert the bare repository in-place to end up with a working copy in it?

    • Noufal Ibrahim
      Noufal Ibrahim about 12 years
      Untested but if you move put the contents of the bare repository into a .git directory and set the bare parameter in the config to false, it should behave like a regular repository where you can just git checkout to get your files.
    • William Pursell
      William Pursell about 12 years
      Depending on what you mean by "browse its contents", you can probably do everything you want in a bare repo using git show and git cat-file
    • nyi
      nyi about 12 years
      Thanks for the hint, useful. I need a more file manager like experience though (edited the question).
    • Neil Mayhew
      Neil Mayhew about 10 years
      If your filesystem supports hard links, and you clone into the same filesystem, clone -l doesn't take any more disk space because it hard-links all of the objects. You will, however, need space for the checkout, as others have noted.
    • ADTC
      ADTC over 9 years
      If your bare repository takes up 20GB of disk space, how much more would the working tree need? Do you really have that much space?
  • nyi
    nyi about 12 years
    You are right, but I need to browse it more conveniently (in a file manager over ssh). I'll thus need to live with the increased disk space.
  • Translunar
    Translunar over 8 years
    It's also worth noting that if the bare repository was created on a machine with different line endings, you may still see modified files even after performing these steps. I believe that's normal; git is trying to fix the linefeeds. Not sure why.
  • Royi
    Royi over 8 years
    I did all written, yet still when I push files they don't show up. What could it be (I also switched denyCurrentBranch to ignore)?
  • ADTC
    ADTC about 8 years
    So you basically cloned a GitHub repo as bare, switched to non-bare, manually put all the file from your "non-repo" and reset index? How is it different from just cloning the repo as non-bare in the first place? The non-bare clone process will check out all the files anyway. And if you really want, you can just replace the checked out files with those from your "non-repo".
  • VonC
    VonC about 8 years
    @ADTC the goal is to get a .git subfolder in a working tree (that you know is your repo) initially made out of an archive (non-git). I could not have checked out a non-bare repo, as the folder in which I was doing the checkout is not empty. Doing the non-bare git clone --no-checkout in a subfolder would have forced me to move the .git one level up. Doing a bare clone allowed me to directly create the .git subfolder where I wanted it. You can see the script here: github.com/VonC/compileEverything/blob/…
  • ADTC
    ADTC about 8 years
    "I could not have checked out a non-bare repo, as the folder in which I was doing the checkout is not empty." You mean you have a non-git working tree that contains changes which are not committed yet, and you intend to commit after you have converted it to a Git-enabled working tree? Yeah I guess, it works for such an use case. Personally, I would clone to an empty folder and compare the two first (using an external tool). But that's just me.
  • VonC
    VonC about 8 years
    @ADTC No: I do not have "non-git working tree that contains changes which are not committed yet". I do not want to commit anything. What I do have is an exact working tree of a repo: all is missing is its .git. I do get the .git through a bare clone, I transform that .git folder into a non-bare one, and do a git reset to make git realize that the working tree is already there. That is exactly what github.com/VonC/compileEverything/blob/… does.
  • ADTC
    ADTC about 8 years
    @VonC ok good that you explained what you are doing. I understand what you mean. But that brings me back to square one. Why? When you can simply check out the working tree, why do you need to plug the .git into an already existing working tree? And what do you do if there are differences between this working tree and the current repo state (the tree you would see if you checked out fresh)? Do you commit or reset --hard? If you're just going to reset --hard, you might as well just delete the working tree and checkout/clone again. The end result is the same, with less brain damage.
  • ADTC
    ADTC about 8 years
    Although I can think of one particular case, where your method could be useful. If there are untracked and ignored files you want to preserve, then yeah, plugging the .git into the working tree would make sense. Is that the reason why you are doing this?
  • VonC
    VonC about 8 years
    @ADTC I understand your question, but that particular process is done in a project (github.com/VonC/compileEverything) meant to install a git server. Meaning I do not have any tool (including git itself) when I start recompiling all the dependencies I need for git to compile. I start the all process by unzipping an archive of my compileEverything repo (so good working tree, but no .git, and no local git). Then, once I install git, I do the process described in the answer.
  • VonC
    VonC about 8 years
    @ADTC "When you can simply check out the working tree, why do you need to plug the .git into an already existing working tree?": because that working tree has no .git subfolder.
  • VonC
    VonC about 8 years
    @ADTC And what do you do if there are differences between this working tree and the current repo state: that is what "git reset" does: it reset the index but does not throw the local differences it would detect (like a reset --hard would). In my case, there is no differences.
  • ADTC
    ADTC about 8 years
    "unzipping an archive of my compileEverything repo" You do realize this archive can already contain .git folder as well, right? You don't need to have Git installed to have a .git folder somewhere. "In my case, there is no differences" Then your process is over-engineered. You could basically just find a way to install Git and clone the repos from scratch, skipping the whole archive extraction step.
  • VonC
    VonC about 8 years
    @ADTC No that archive do not contain any .git. Try it yourself and click on the "Download Zip" button you see on the github.com/VonC/compileEverything page: the archive you will get will not include any .git subfolder.
  • VonC
    VonC about 8 years
    @ADTC "You could basically just find a way to install Git and clone the repos from scratch": that is what compileEverything does (by... compiling everything). This has served me on multiple missions for clients where I do not have root access on the server, and I cannot install whatever I need (because of products depends on older versions of the dependencies needed by git). Look for the section "April 2013 – September 2014" in stackoverflow.com/cv/vonc for more details and links.
  • ADTC
    ADTC about 8 years
    I guess that explains everything. :) This is basically a workaround for not having root access and not being to install anything.
  • VonC
    VonC about 8 years
    @ADTC Yes, but the answer above is a way to restore a missing .git if it ever get removed or is missing for any reason.
  • ADTC
    ADTC about 8 years
    Yes, what I'm saying is you have this scenario because of restrictions. For someone without restrictions, they can have a simple script that installs Git if it's not installed, and then just clones the repo the normal way. So basically what you're doing is a workaround when system restrictions prevent you from doing the straightforward way.
  • VonC
    VonC about 8 years
    @ADTC If you were to delete your local .git by mistake, this answer could also work.
  • ADTC
    ADTC about 8 years
    For the case of (permanently) deleting the local .git by mistake, you can just do git clone --no-checkout <url>. Then either move the files into the new folder or move the new .git into the folder with the files. Finally just do git reset. That's quicker and easier, and you are guaranteed to get a clean non-bare clone of the repo (i.e. you don't need to convert a bare repo using other commands). Tested this with a repo I have. Works great. I think you can improve your script with the same technique, pretty much skipping lines 16-21.
  • VonC
    VonC about 8 years
    @ADTC "Then either move the files into the new folder or move the new .git into the folder with the files": That is precisely what I do not want to do, because I actually can restore the .git folder directly in its right location, without moving any file around. I think I will keep line 16-21 in their current pristine condition then ;) I understand this is not intuitive and therefore would not be the solution that you would recommend. But it does work.
  • ADTC
    ADTC about 8 years
    How hard is it to move a folder? mv folder1 folder2/ ... Or is that also a restriction? I don't understand why you would choose a more complex method over a much simpler one. Is that something to do with pride and ego? If yes, suit yourself. I'm done here.
  • VonC
    VonC about 8 years
    @ADTC it was a restriction at the time I had to come up with that solution, but I wanted to illustrate an alternative way I found interesting at the time. I have edited the answer to make it clearer the accepted solution is simpler.
  • jpaugh
    jpaugh over 7 years
    Actually, this is a real issue (+1). Working trees often consume as much as half of the disk space; partly because git histories are aggressively compressed.
  • Nickolai
    Nickolai almost 6 years
    This is great, not sure why this isn't higher up on the list
  • Sridhar Sarnobat
    Sridhar Sarnobat about 5 years
    The reason I want to avoid this is when the repo gets large, git clone sometimes dies. I want to do an rsync of the bare repo then convert it.
  • Sridhar Sarnobat
    Sridhar Sarnobat about 5 years
    This is the simplest solution and I can confirm in 2019 that it works (the first approach).
  • sarnold
    sarnold almost 5 years
    @SridharSarnobat, then consider doing the tasks described in the accepted answer: stackoverflow.com/a/10637882/377270 -- I put this one-line answer here so people with enough free space can do things the easy way.
  • Sridhar Sarnobat
    Sridhar Sarnobat almost 5 years
    Just one little note I'd forgotten (applying to all answers): you'll need to edit the config file when you first push to set the remote url.
  • davidbak
    davidbak almost 5 years
    Thank you! I came into work one day and found my main repo - from which I had several worktrees elsewhere - was reporting itself to be "bare". All the other worktrees were fine. Had no idea how to get unwedged but found this answer and it worked great.