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

9

98

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?

Erlandson answered 17/5, 2012 at 14:12 Comment(5)
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.Cajun
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-fileSteelwork
Thanks for the hint, useful. I need a more file manager like experience though (edited the question).Erlandson
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.Durno
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?Zelda
G
142

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.

Gaze answered 17/5, 2012 at 14:39 Comment(2)
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)?Patriliny
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.Tomsk
H
21

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.

Headwards answered 16/5, 2013 at 8:50 Comment(21)
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.Cilice
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".Zelda
@Zelda 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/…Headwards
"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.Zelda
@Zelda 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.Headwards
@Headwards 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.Zelda
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?Zelda
@Zelda 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.Headwards
@Zelda "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.Headwards
@Zelda 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.Headwards
"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.Zelda
@Zelda 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.Headwards
@Zelda "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.Headwards
I guess that explains everything. :) This is basically a workaround for not having root access and not being to install anything.Zelda
@Zelda Yes, but the answer above is a way to restore a missing .git if it ever get removed or is missing for any reason.Headwards
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.Zelda
@Zelda If you were to delete your local .git by mistake, this answer could also work.Headwards
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.Zelda
@Zelda "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.Headwards
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.Zelda
@Zelda 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.Headwards
K
21

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
Klingel answered 26/1, 2017 at 20:45 Comment(2)
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.Fibro
@SridharSarnobat, then consider doing the tasks described in the accepted answer: https://mcmap.net/q/55418/-how-do-i-convert-a-bare-git-repository-into-a-normal-one-in-place -- I put this one-line answer here so people with enough free space can do things the easy way.Klingel
V
12

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.

Vaas answered 25/4, 2016 at 15:6 Comment(0)
E
8

cd into bare repo and do

  1. Either:
git config core.bare false
git reset --hard
  1. Or
git clone X.git X

(will give you regular git repo that named X)

Esdras answered 31/1, 2018 at 3:41 Comment(2)
This is the simplest solution and I can confirm in 2019 that it works (the first approach).Fibro
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.Fibro
S
6

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.

Steelwork answered 17/5, 2012 at 14:49 Comment(2)
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.Erlandson
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.Restricted
V
4

If you don't mind working on different worktree , then

git worktree add ../repo2
cd ..
git status # now works fine

Please note, this is not a clone.

Viipuri answered 28/8, 2017 at 12:30 Comment(1)
This is great, not sure why this isn't higher up on the listRourke
R
0

Push-to-Deploy

Rather than convert the bare remote into a standard repository, you can use the post-receive script in the hooks directory to expand the repository into a deployment directory.

Here is a good example of setting up Push-to-Deploy

For ease of reference, this is the script contents example from the above link. It will deploy only pushes from "master" branch to a directory named "deploy" that is on the same level as the parent directory of the repository:

#!/usr/bin/env ruby
# post-receive

# 1. Read STDIN (Format: "from_commit to_commit branch_name")
from, to, branch = ARGF.read.split " "

# 2. Only deploy if master branch was pushed
if (branch =~ /master$/) == nil
    puts "Received branch #{branch}, not deploying."
    exit
end

# 3. Copy files to deploy directory
deploy_to_dir = File.expand_path('../deploy')
`GIT_WORK_TREE="#{deploy_to_dir}" git checkout -f master`
puts "DEPLOY: master(#{to}) copied to '#{deploy_to_dir}'"
Ramsgate answered 31/1, 2019 at 18:6 Comment(0)
C
0

Another case windows 10

In my case I wouldn't even init any new repository with a working tree in another path for later clone the (BARE:main) remository because it wasn't a repository and also I couldn't find the .git folder because I couldn't create it after run

git init

So my problem was in System environment variables where there was a different path associate to my git. I just only need to delete that variable and then I was able to run git init

I gonna attach picture where you can access to System environment variables
  1. Go start windows < windows-key >
  2. Write system
  3. Press on System control panel enter image description here
  4. Inside About look for Advanced system settings under related settings
  5. Then click in on Environment Variables... enter image description here
  6. Delete something you see realted to git.
Cater answered 1/6, 2023 at 6:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.