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.
git
UserThe 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.
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.defaultBranchhint: 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.
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
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
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.