QR Code

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:

  1. You have to have an existing repo to clone from.

  2. 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.

  1. 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
  2. Enable the post-update script.

    The version of git I use creates a prototype script in the hooks directory called post-update.sample. If we rename it from post-update.sample to post-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 a chmod command to verify this is the case:

    $ cd test/hooks
    $ mv post-update.sample post-update
    $ chmod a+x post-update
  3. 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
  4. (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.

  5. 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, the test 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 machine git.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.

  6. 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.

  7. (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:

  1. 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 the rev-parse command.

    git clone https://BI6.US/GI/B/
    cd B
    git rev-parse HEAD
  2. 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
  3. Commit the change.

    git add datefile.txt
    git commit -m "add date file"
  4. 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 the format-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 like 0001-add-date-file.patch.

  5. (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:

  1. 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.

  2. 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.

  3. Add, Commit and Push changes.

    This is pretty standard. I usually don't use the -A option to git 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
  4. 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

  1. Git on the Server — The Protocols [Pro Git]