Setting up a local git remote

Set up a bare repository and protect branches with hooks

Mar 20, 2026

When you think about remote Git repositories, it's usually something like GitHub or GitLab. But sometimes you just need a simple local remote, a private backup on your machine, a shared repo across computers on your local network, or a central copy that works completely offline.

Git already gives you everything you need with git init --bare. No extra services, no setup overhead.

What is a bare repository?

A bare repository is a Git repository that contains only the internal Git data, commits, branches, tags, and configuration, with no working directory. There are no project files to check out; it exists purely as a storage location for the repository history.

Creating a bare repository

Creating a bare repository is straightforward and only takes a single command:

git init --bare my-project.git

If you look inside the directory, you'll find the raw Git internals: HEAD, config, objects/, refs/, and a hooks/ directory, which we'll use later.

Tip

It's common practice to name bare repositories with a .git suffix: project.git. This makes it immediately clear that the folder is a repository storage location, not a working project.

Connecting a local project

There are two ways to connect to the bare repository depending on whether you have an existing project or are starting fresh.

If you already have a local project, add the bare repo as a remote and push:

git remote add origin ~/my-project.git
git push -u origin main

If you're starting fresh, clone it directly:

git clone ~/my-project.git

The path can be absolute (/home/user/repos/my-project.git) or use ~ for your home directory. On a shared machine or NAS, use the actual filesystem path to the bare repo.

Some other useful commands for working with remotes

git remote -v                   # verify the remote is configured
git remote show origin          # inspect the remote in detail
git fetch origin                # fetch latest without merging
git log origin/main..HEAD       # see commits not yet pushed

Protecting the main branch with hooks

One feature people often miss when using a plain Git server instead of platforms like GitHub or GitLab is branch protection. Luckily Git already supports this through hooks.

Since a bare repository acts as the central remote, we can install a pre-receive hook that runs whenever someone pushes to the repository. The hook can inspect the pushed references and reject the push if it targets a protected branch like main or master.

Inside the bare repository create the hook:

cd my-project.git/hooks
touch pre-receive
chmod +x pre-receive

Then add the following script to block direct pushes to protected branches:

#!/bin/sh

while read oldrev newrev refname
do
  if [ "$refname" = "refs/heads/main" ] || [ "$refname" = "refs/heads/master" ]; then
    echo "Direct pushes to main/master are not allowed."
    exit 1
  fi
done

exit 0

The pre-receive hook runs once for every push and receives the updated references through standard input. Each line contains the old commit hash, the new commit hash, and the reference name. By checking the reference name we can selectively reject pushes to protected branches while allowing everything.

Conclusion

With just a few lines of shell script you get a simple form of branch protection similar to what hosted Git platforms provide. This setup works well for personal backups, small teams on a shared server, or any situation where you want a central Git remote without the overhead of running a full platform. The hooks/ directory gives you room to grow, you can add post-receive hooks for notifications or automated deployments down the line.

Share

Comments (0)