Samir Parikh / Blog


Originally published on 07 October 2021

Last updated on 07 October 2021

Contents

Almost two years ago, I wrote about how to install cgit, a simple web front-end to Git repositories, on FreeBSD. Truth be told, I never got around to setting up a production instance of my own to use. This is mainly because I still haven’t incorporated Git into my workflows. But the more I wrangle text files and short programs, the more I want to start using Git for things like revision control and to serve as another repository for some of my files. My end goal is to have a separate Git server that also has a web front-end like cgit or stagit from which users can browse what I’ve shared, but without having all of the modern collaboration features that something like GitHub or GitLab offer.

In order to learn more about Git, I’ve been slowly going through the Pro Git book to not only learn how to use Git, but also to use it on the server. In this post, I’ll share how to setup a very simple Git server running FreeBSD to host your remote repositories to which you can push and fetch updates using SSH. Many of the initial first steps mirror those when installing cgit but I’ll go ahead and repeat them here for ease of reference. I’m also assuming that remoteuser has sudo privileges on the remote FreeBSD machine.

Create git User

The first thing to do is to create the git user on the remote FreeBSD server which will own all of the remote repository directories and files:

[remoteuser@freebsd ~]$ sudo adduser
Username: git
Full name: git remote user
Uid (Leave empty for default): 
Login group [git]: 
Login group is git. Invite git into other groups? []: 
Login class [default]: 
Shell (sh csh tcsh bash rbash git-shell nologin) [sh]: git-shell
Home directory [/home/git]: 
Home directory permissions (Leave empty for default): 
Use password-based authentication? [yes]: 
Use an empty password? (yes/no) [no]: 
Use a random password? (yes/no) [no]: 
Enter password: 
Enter password again: 
Lock out the account after creation? [no]: 
Username   : git
Password   : 
Full Name  : git remote user
Uid        : 1004
Class      : 
Groups     : git 
Home       : /home/git
Home Mode  : 
Shell      : /usr/local/libexec/git-core/git-shell
Locked     : no
OK? (yes/no): yes
adduser: INFO: Successfully added (git) to the user database.
Add another user? (yes/no): no
Goodbye!

Note that I assigned git-shell as the user’s shell instead of something like tcsh(1). This will allow us SSH access to run Git commands but in a restricted manner for enhanced security.

Configure Remote Repositories

Next, create the directories to house the remote repositories and configure the appropriate permissions:

[remoteuser@freebsd ~]$ sudo mkdir -p /home/git/repos/repository01

[remoteuser@freebsd ~]$ sudo git init --bare /home/git/repos/repository01/
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint: 
hint:   git config --global init.defaultBranch 
hint: 
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint: 
hint:   git branch -m 
Initialized empty Git repository in /usr/home/git/repos/repository01/

[remoteuser@freebsd ~]$ sudo chown -R git:git /usr/home/git/repos

If you want, you can also go ahead and update the repo’s description in the /usr/home/git/repos/repository01/description file.

Enable SSH Access

On the remote machine, we need to perform the following steps:

[remoteuser@freebsd ~]$ sudo mkdir -p /home/git/.ssh

[remoteuser@freebsd ~]$ sudo cp /home/remoteuser/.ssh/authorized_keys /home/git/.ssh/

[remoteuser@freebsd ~]$ sudo chown -R git:git /home/git/.ssh

[remoteuser@freebsd ~]$ sudo chmod -R 700 /home/git/.ssh

On the local machine, I updated my $HOME/.ssh/config file to include an entry for my FreeBSD host to make it easier to push to and fetch from the remote repository:

Host gitserver
    Hostname                vps.ip.address
    IdentityFile            $HOME/.ssh/public_key.pem
    IdentitiesOnly          yes

Initialize Local Repository and Push Changes

Let’s assume we have an existing directory called localrepo on our local machine that we would like to put under version control. As we progress through these steps, you don’t need to execute git status every time. I just did it here to demonstrate how Git tracks, stages and commits your changes.

localuser1@ubuntu1:~$ cd /path/to/localrepo

localuser1@ubuntu1:localrepo$ git init
Initialized empty Git repository in /path/to/localrepo/.git/
localuser1@ubuntu1:localrepo$ git status
On branch master 

No commits yet                

Untracked files:
  (use "git add ..." to include in what will be committed)
        file01.txt            
        file02.txt            
        file03.txt            
        file04.txt            
        file05.txt                 
        file06.txt
        file07.txt                   
        file08.txt 
        file09.txt                                                                 
        file10.txt                                                                 

nothing added to commit but untracked files present (use "git add" to track)       

localuser1@ubuntu1:localrepo$ git add -A

localuser1@ubuntu1:localrepo$ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached ..." to unstage)
        new file:   file01.txt                                                     
        new file:   file02.txt                                                     
        new file:   file03.txt                                                     
        new file:   file04.txt                                                     
        new file:   file05.txt                                                     
        new file:   file06.txt                                                     
        new file:   file07.txt                                                     
        new file:   file08.txt
        new file:   file09.txt
        new file:   file10.txt

localuser1@ubuntu1:localrepo$ git commit -am "add new repo"
[master (root-commit) 00a8e02] add new repo                    
 10 files changed, 10 insertions(+)
 create mode 100644 file01.txt
 create mode 100644 file02.txt           
 create mode 100644 file03.txt
 create mode 100644 file04.txt           
 create mode 100644 file05.txt
 create mode 100644 file06.txt                                                     
 create mode 100644 file07.txt
 create mode 100644 file08.txt
 create mode 100644 file09.txt
 create mode 100644 file10.txt

localuser1@ubuntu1:localrepo$ git status
On branch master  
nothing to commit, working tree clean

Now we can add the remote repository we created earlier. Note that the following commands are still being executed on the local machine:

localuser1@ubuntu1:localrepo$ git remote add origin git@gitserver:/home/git/repos/repository01

localuser1@ubuntu1:localrepo$ git remote -v
origin  git@gitserver:/home/git/repos/repository01 (fetch)
origin  git@gitserver:/home/git/repos/repository01 (push)

localuser1@ubuntu1:localrepo$ git push -u origin master
Enumerating objects: 12, done.
Counting objects: 100% (12/12), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (12/12), 567 bytes | 189.00 KiB/s, done.
Total 12 (delta 0), reused 0 (delta 0)
To gitserver:/home/git/repos/repository01
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.

localuser1@ubuntu1:localrepo$ git status
On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

Working with the Remote Repository

Now let’s say that I am on another local machine that also has SSH access to my remote FreeBSD Git server. I can now clone the remote repository on my local machine if I want to continue working on those files:

localuser2@ubuntu2:~$ cd projectFiles

localuser2@ubuntu2:~/projectFiles$ git clone git@gitserver:/home/git/repos/repository01
Cloning into 'repository01'...
remote: Enumerating objects: 12, done.
remote: Counting objects: 100% (12/12), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 12 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (12/12), done.

localuser2@ubuntu2:~/projectFiles$ ls -all
total 32
drwxrwxr-x  3 localuser2 localuser2  4096 Oct  6 15:31 .
drwx------ 91 localuser2 localuser2 20480 Oct  6 15:30 ..
drwxrwxr-x  3 localuser2 localuser2  4096 Oct  6 15:31 repository01

localuser2@ubuntu2:~/projectFiles$ cd repository01

localuser2@ubuntu2:~/projectFiles/repository01$ ls -all
total 132
drwxrwxr-x 3 localuser2 localuser2 4096 Oct  6 15:31 .
drwxrwxr-x 3 localuser2 localuser2 4096 Oct  6 15:31 ..
-rw-rw-r-- 1 localuser2 localuser2    3 Oct  6 15:31 file01.txt
-rw-rw-r-- 1 localuser2 localuser2    3 Oct  6 15:31 file02.txt
-rw-rw-r-- 1 localuser2 localuser2    3 Oct  6 15:31 file03.txt
-rw-rw-r-- 1 localuser2 localuser2    3 Oct  6 15:31 file04.txt
-rw-rw-r-- 1 localuser2 localuser2    3 Oct  6 15:31 file05.txt
-rw-rw-r-- 1 localuser2 localuser2    3 Oct  6 15:31 file06.txt
-rw-rw-r-- 1 localuser2 localuser2    3 Oct  6 15:31 file07.txt
-rw-rw-r-- 1 localuser2 localuser2    3 Oct  6 15:31 file08.txt
-rw-rw-r-- 1 localuser2 localuser2    3 Oct  6 15:31 file09.txt
-rw-rw-r-- 1 localuser2 localuser2    3 Oct  6 15:31 file10.txt
drwxrwxr-x 8 localuser2 localuser2 4096 Oct  6 15:31 .git

I can go back to my original local machine, make changes, and push them back up to the remote repository:

localuser1@ubuntu1:localrepo$ touch newfile.txt

localuser1@ubuntu1:localrepo$ git status
On branch master

Your branch is up to date with 'origin/master'.

Untracked files:                         
  (use "git add ..." to include in what will be committed)
        newfile.txt                      

nothing added to commit but untracked files present (use "git add" to track)

localuser1@ubuntu1:localrepo$ git add newfile.txt

localuser1@ubuntu1:localrepo$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git restore --staged ..." to unstage)
        new file:   newfile.txt

localuser1@ubuntu1:localrepo$ git commit -am "add newfile.txt"
[master 886c44e] add newfile.txt
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 newfile.txt

localuser1@ubuntu1:localrepo$ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean

localuser1@ubuntu1:localrepo$ git push
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 281 bytes | 281.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0)
To gitserver:/home/git/repos/repository01
   00a8e02..886c44e  master -> master

localuser1@ubuntu1:localrepo$ git status
On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

And finally, if I got back to my second local machine, I can pull down those changes as well:

localuser2@ubuntu2:~/projectFiles/repository01$ git pull
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 261 bytes | 261.00 KiB/s, done.
From gitserver:/home/git/repos/repository01
   00a8e02..886c44e  master     -> origin/master
Updating 00a8e02..886c44e
Fast-forward
 newfile.txt | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 newfile.txt

localuser2@ubuntu2:~/projectFiles/repository01$ ls -all
total 140
drwxrwxr-x 3 localuser2 localuser2 4096 Oct  6 15:38 .
drwxrwxr-x 3 localuser2 localuser2 4096 Oct  6 15:31 ..
-rw-rw-r-- 1 localuser2 localuser2    3 Oct  6 15:31 file01.txt
-rw-rw-r-- 1 localuser2 localuser2    3 Oct  6 15:31 file02.txt
-rw-rw-r-- 1 localuser2 localuser2    3 Oct  6 15:31 file03.txt
-rw-rw-r-- 1 localuser2 localuser2    3 Oct  6 15:31 file04.txt
-rw-rw-r-- 1 localuser2 localuser2    3 Oct  6 15:31 file05.txt
-rw-rw-r-- 1 localuser2 localuser2    3 Oct  6 15:31 file06.txt
-rw-rw-r-- 1 localuser2 localuser2    3 Oct  6 15:31 file07.txt
-rw-rw-r-- 1 localuser2 localuser2    3 Oct  6 15:31 file08.txt
-rw-rw-r-- 1 localuser2 localuser2    3 Oct  6 15:31 file09.txt
-rw-rw-r-- 1 localuser2 localuser2    3 Oct  6 15:31 file10.txt
drwxrwxr-x 8 localuser2 localuser2 4096 Oct  6 15:38 .git
-rw-rw-r-- 1 localuser2 localuser2    0 Oct  6 15:38 newfile.txt

There are numerous other features of Git, including viewing your commit history, reverting back to prior versions, and working with branches but hopefully the steps I outlined above are enough to get you started with working with remote repositories as you begin your Git journey like me!

UPDATE from 07 October 2021

Reader bjb from Mastodon, who has a keen eye, had a good question regarding one of the messages returned from Git after running the sudo git init --bare /home/git/repos/repository01/ command. They noticed that Git initialized the repository in the directory /usr/home/git/repos/repository01/. This is because in FreeBSD, the directory /home is actually symlinked to /usr/home:

$ ls -all /home
lrwxr-xr-x  1 root  wheel  8 Oct  6 15:18 /home -> usr/home

I thought this was a great question which warranted updating this post.