
HTTPS://BI6.US/GI/B/
The Problem
After encountering "issues" with GitHub, I wanted to move my projects somewhere else. I could have certainly used GitLab or SourceHut, and may still use them for other projects. But I remembered reading somewhere about accessing GIT repos via HTTPS instead of SSH. Since I already had a web presence, this seemed ideal.
The most authoritative reference I could find seemed to be on the "Git on the Server — The Protocols" page from the git-scm.com site. [1]
It suggested this process to create a bare directory containing the GIT state. You could then upload this directory to a web server and direct people to that URL to clone the repo:
$ cd /var/www/htdocs/ $ git clone --bare /path/to/git_project gitproject.git $ cd gitproject.git $ mv hooks/post-update.sample hooks/post-update $ chmod a+x hooks/post-update
But there are two issues:
-
You have to have an existing repo to clone from.
-
When you try to clone via HTTP(S), you get the error:
fatal: repository '<url>' not found
where
<url>
is the URL you hosted it at.
Clearly, this won't work for me.
The Investigation
After staring blankly at the state of my repo and the documentation for several minutes, it occurred to me we had to execute the contents of the post-update
hook.
If you look at the sample post-update file, its documentation says:
# An example hook script to prepare a packed repository for # use over dumb transports.
which was the clue we should just execute the command:
$ git update-server-info
The Solution
So my process for creating an empty, read-only repo to be hosted on my web server follows. It assumes you have a development machine with git installed that's not the same machine as your web server. But there's nothing stopping you from using your web server as a development machine if it has git installed and you're not freaked out by testing in production. Just make sure you create the repo in a different directory than where it's hosted.
In this example, we're assuming the name of the repo is test and we're eventually hosting it on git.example.com.
-
Create the bare, empty repo on your development machine.
The process given in Pro Git says to clone an existing repo, but we can just create a new one from scratch. I'm building the bare repo in the
/tmp
directory of my machine. You can probably create this directory anywhere, but in a later step we're going to test our work by checking out its contents in a different directory.$ cd /tmp $ git init --bare test
-
Enable the post-update script.
The version of git I use creates a prototype script in the
hooks
directory calledpost-update.sample
. If we rename it frompost-update.sample
topost-update
, git will understand it needs to run this script after each update. On my system, this file already had the execute bit set, but we include achmod
command to verify this is the case:$ cd test/hooks $ mv post-update.sample post-update $ chmod a+x post-update
-
Run update-server-info from within the bare repo.
This command assumes you're still in the
test/hooks
directory you cd'd into in the last step. It modifies the repo so its contents can be carried over a dumb transport. See the Pro Git book for details. [1]$ git update-server-info
-
(optional) Create an empty
index.html
file in the bare repo.It seems somehow wrong to let random visitors see into the inner workings of my git repos, so I create an empty
index.html
file to hide it's contents. This step assumes we're in the/tmp/test/hooks
directory, so the first thing we do is cd up one directory level:$ cd .. $ echo -e '<!DOCTYPE html>\n<!-- This space intentionally left blank -->\n<html/>' > index.html
Later, I'll probably create a script that creates a more informative
index.html
file, maybe listing all the files and branches in the repo. -
Upload the bare repo to your web server.
I'm using
scp
for this step. I'm assuming you know how to use it. Remember, thetest
directory which contains a bare GIT repo is in the/tmp
directory and that we're currently in the/tmp/test
directory. We're assuming the root of the web server's file-space is/var/html
on the machinegit.example.com
. You will certainly have to change these values to match your own situation.$ cd .. $ scp -r test git.example.com:/var/html/git
Once you've uploaded the files, verify they're there by pointing a browser at
https://git.example.com/git/test/
(or whatever URL you uploaded your bare repo to.) You should see a blank page. If you do a ctrl-u to view the source, you should see the "this space intentionally left blank" comment. -
CD to your home directory and clone the repo.
To test that everything works, let's clone the repo. We didn't add any contents to this repo, so we'll probably get a message saying we cloned an empty repo. That's okay. You want to replace the
https://git.example.com/git/test/
bit below with whatever URL you uploaded the repo to:$ cd ~ $ git clone https://git.example.com/git/test/
If you didn't get any errors, then call it a success and carry on with your day.
-
(optional) Delete the original bare repo directory.
You don't have to do this, but since the canonical instance of your repo lives on the web server, it may be less confusing if you deleted the working copy of the bare repo.
$ rm -rf /tmp/test
If you're just looking for a single script you can cut and paste, here's one that's just the collection of previous steps.
TMPDIR=/tmp REPONAME=test SSHHOST=git.example.com SSHPATH=/var/html/git URLROOT=https://git.example.com/git cd "$TMPDIR" git init --bare "$REPONAME" cd "$REPONAME/hooks" mv post-update.sample post-update chmod a+x post-update git update-server-info cd .. echo -e '<!DOCTYPE html>\n<!-- This space intentionally left blank -->\n<html/>' > index.html cd .. scp -r "$REPONAME" "${SSHHOST}:${SSHPATH}" cd ~ git clone "${URLROOT}/${REPONAME}" # Uncomment the next line to automagically delete the bare repo # rm -rf "${TMPDIR}/${REPONAME}"
The Caveats
The main caveat with this technique is it produces a read-only repository. "How then," you may ask, "do you update the contents or accept pull requests?" The simplest answer is "Not without some difficulty."
I maintain a few read-only repos on the Bit-Roastery site. This text is tracked in the repo at https://BI6.US/GI/B/. If anyone wants to suggest a change, I ask them to produce a patch and email it to me at [email protected]. I don't claim this is the "best" way to manage a GIT repo, but it works for me.
How You Can Produce a Patch
Suppose you wanted to change something in the repo at https://BI6.US/GI/B/. To check out the repo, make a change, commit the change and then produce a patch, you would do something like this:
-
Check out the repo and note the current commit id:
The
clone
command shouldn't be foreign to frequent git users. But we may need to know what the most recent commit id on the main branch is later on. That's why I added therev-parse
command.git clone https://BI6.US/GI/B/ cd B git rev-parse HEAD
-
Make a change of some sort.
In the real world you would make a real change. Maybe I made a spelling error or you have an idea for how to improve the
bin/build-bare-repo
script. But as an example, we're just going to create a new file with the current date in it.date >> datefile.txt
-
Commit the change.
git add datefile.txt git commit -m "add date file"
-
Create the patch.
Here's where you have to pay attention. This works only if you've made a single local commit. The
git log
command will show you a list of commits. Verify that there's only one new commit and then run theformat-patch
command:git log git format-patch HEAD~1
If you've made multiple commits, it's okay. We just have to change the number after the
HEAD
. If you've made 2 commits, you would use this command:git format-patch HEAD~2
If you made 3 commits, use this command:
git format-patch HEAD~3
The
format-patch
command will create one patch file for each commit. If you were following along the example and only made the one commit in the previous step, you should be left with a patch file named something like0001-add-date-file.patch
. -
(optional) Email me the patch
Send me the patch via email at [email protected]. If it's just the date file example, I'll probably reject the patch (sorry). But if I made a real mistake, I'll likely apply the patch and send you a thank-you note.
Applying Patches to Bare Repositories
But applying the patch is a little weird.
The process documented here creates a "bare" repo.
The git apply
command assumes you're running it from within a working directory.
I don't know of any way to apply patches directly to bare repos, but since I have ssh access to my web server, it's easy enough for me to clone the repo, apply the patch and then push the result.
If you want to do something similar, here's the process I use:
-
Check Out the Repo Using ssh.
Using the example parameters from a previous section, the command would look like this:
cd ~ git clone `whoami`@git.example.com:/var/html/git/test/
This assumes you have an account on
git.example.com
and read/write access to/var/html/git/test
directory. Note this directory is the filesystem path to the repo and not the web url path to the repo. -
Apply the patches.
I'm assuming you only have one patch (named
0001-add-date-file.patch
) in your home directory.cd test git apply ~/0001-add-date-file.patch
If you have more than one patch file, repeat the
apply
command for each. -
Add, Commit and Push changes.
This is pretty standard. I usually don't use the
-A
option togit add
since it's a good practice to be explicit about changes you're making. But in this case it's probably fine.git add -A git commit -m "Add Date File" git push
-
Verify the Changes.
We should test our work before declaring victory. I'm going to check the newly updated repo out into the
/tmp
directory and verify its contents have changed.git clone https://git.example.com/git/test/ /tmp/test.test cat /tmp/test.test/datefile.txt
Assuming the repo didn't have
datefile.txt
in it originally, if the file exists, then we can probably declare victory.
Conclusion
You may never need to host a GIT repository on a web server. But if you do, this is a simple way to set it up.
References
- Git on the Server — The Protocols [Pro Git]