Let's set up a git server on a Raspberry Pi. This server will be a private server for tracking code and files generated on your local network - at least that's the scope of my git server. Yours may be different.
Nothing special need be done to designate an RPi as a Git-Server. As long as it's got git
installed, virtually any RPi is fit for this purpose. Simply choose one that's accessible to other hosts (RPi git-clients) on your network as the clients will need to make SSH connections to the Git-Server. Assuming you want to use public-key authentication, you'll need to copy public keys from git-clients to the git-server using ssh-copy-id
.
Let's assume we have an RPi w/ hostname rpigitserver
as the Git-Server, and that we have another RPi w/ hostname rpigitclient
as one of the clients. Let's also assume that the repository we wish to maintain source control over is called etc-update-motd-d
; this is simply a descriptive name, and need not be the same name as git folder holding the same code on the client(s).
Rather than scattering the repositories about, we'll put all of our git repositories under a common folder that we'll name ~/git-srv
; sub-folders will designate the names of the individual repositories.
$ hostname # to get our bearings straight
rpigitserver
$ mkdir ~/git-srv
Next, create a sub-directory under ~/git-srv
to contain our first repository (etc-update-motd-d
), and initialize this sub-directory as a git
repository :
$ hostname # to get our bearings straight
rpigitserver
$ mkdir ~/git-srv/etc-update-motd-d.git # NOTE that we added the file extension `.git`
$ cd ~/git-srv/etc-update-motd-d.git
$ git init --bare # NOTE '--bare' option
And that's it... our Git-Server is alive!
Now move to (one of) the git-clients: rpigitclient
. Assume rpigitclient
is the host on which we've been coding a set of scripts which comprise the project we'll call motd-d
. Remember, the folder names on client and server need not match, although they certainly can match - if that is desired.
$ hostname # to get our bearings straight
rpigitclient
$ ssh-copy-id pi@rpigitserver # copy SSH pub key to `rpigitserver` ==> IF NECESSARY
$ cd ~/scripts/motd-d # where the code for project motd-d is kept
$ git init # "initialize" the client repo; NOTE we did not use the `--bare` option!
Initialized empty Git repository in ...
$ git add <filenames> -OR- <.> # add the files which are to be tracked; use '.' for all files
# This is a good time to run 'git status' - to see where we stand...
$ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: 10-intro
new file: 12-ckmount
...
new file: 75-imgutil
new file: 99-source
# ^ 'git status' reports we have a number of files "staged", but nothing has yet been committed
$ git commit -m 'initial commit' # commit the files with a suitable/meaningful message
[master (root-commit) 3503154] initial commit
13 files changed, 55 insertions(+)
create mode 100755 10-intro
create mode 100755 12-ckmount
...
create mode 100755 75-imgutil
create mode 100755 99-source
# ^ 'git commit...' verifies the list of files committed
$ git remote add origin pi@rpigitserver:/home/pi/git-srv/etc-update-motd-d.git
# ^ declares the designated folder on `rpigitserver` as the "remote origin"
$ git push -u origin master
Enumerating objects: 15, done.
Counting objects: 100% (15/15), done.
...
* [new branch] master -> master
branch 'master' set up to track 'origin/master'.
# ^ "pushes" previously `add`ed & `commit`ted files to the Git-Server (or 'origin/rpigitserver`) repo 'master' branch
# Let's run 'git status' again:
$ git status
On branch master
Your branch is up to date with 'origin/master'.
nothing to commit, working tree clean
# ^ 'git status' confirms we've successfully updated the repo on 'rpigitserver'
# once the 'remote origin' has been established, future 'git push' operations *to* rpigitserver are simplified:
$ git push
# or, to pull/fetch updates *from* the server
$ git pull
# NOTE: If you need to make changes to the 'remote origin', it may be simpler to make those
# changes by manually editing the `./git/config` folder in your client repo.
At this point, we have uploaded the code stored on the client machine in ~/scripts/motd-d
to the repo called etc-update-motd-d.git
on the Git-Server. Things are moving right along :)
You may have noticed (by running ls -l
) that the working files in your repo have modification 'date-times' that don't reflect their true modification time. Weirdly (to me anyway), this is "by design"; maybe I've been looking at GitHub for too long? If you're so inclined, you can override this odd behavior. You'll need to install an "extra" app for it: git-restore-mtime
. Here's how this works:
$ hostname # to get our bearings straight
rpigitclient
$ sudo apt update && sudo apt install git-restore-mtime # NOTE 'git-restore-mtime'
$ cd ~/scripts/motd-d && git restore-mtime
12 files to be processed in work dir
Statistics:
0.04 seconds
36 log lines processed
7 commits evaluated
1 directories updated
12 files updated
total 44
Let's move to another git-client, and "clone" (copy) the repo from Git-Server (rpigitserver
). Let's assume this client is also located on the host rpigitserver
; this might be in a different user's home directory (or not). We do this to show how a "local" clone is accomplished.
$ hostname # to get our bearings straight
rpigitserver
$ pwd
/home/developer # 'developer' is a userid; i.e. not user pi
$ git clone pi@rpigitserver:/home/pi/git-srv/etc-update-motd-d.git
Cloning into 'etc-update-motd-d'...
...
Receiving objects: 100% ... , done.
$
# i.e. executing 'git clone' from '/home/developer' creates/clones the repo in '/home/developer/etc-update-motd-d.git'
ALTERNATIVELY, let's create that clone under a different folder name; i.e. something other than etc-update-motd-d.git
$ hostname # to get our bearings straight
rpigitclient2
$ pwd
/home/developer
$ git clone pi@rpigitserver:/home/pi/git-srv/etc-update-motd-d.git motd
Cloning into 'motd'...
...
Receiving objects: 100% (3/3), done.
# We've cloned the repo into a folder named '/home/developer/motd'!
Now let's assume that development has been on-going on rpigitclient
, and the developer is ready to commit
the code, and upload that code to the Git-Server. This developer has been x-busy, and has added two files: foo
and bar
.
Here's how that commit is done, and pushed to the Git-Server:
$ hostname # to get our bearings straight
rpigitclient
$ cd ~/scripts/motd-d
$ git add foo bar # git will add all *changed* files to the 'staging area'
$ git commit -m 'added files foo & bar, x-important!'
[master d27a3ae] added files foo & bar, x-important!
2 files changed, 2 insertions(+)
create mode 100644 bar
create mode 100644 foo
$ git remote -v show # verify the correct remote server URL is being used
origin pi@rpigitserver/home/pi/git-srv/etc-update-motd-d.git (fetch)
origin pi@rpigitserver/home/pi/git-srv/etc-update-motd-d.git (push)
$ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (4/4), 331 bytes | 331.00 KiB/s, done.
Total 4 (delta 1), reused 0 (delta 0), pack-reused 0
To ssh://raspberrypi5/home/git/git-srv/etc-update-motd-d.git
55cd03b..d27a3ae master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.
# ^ done, mission accomplished
Our revised code has now been commit
ed and push
ed to the Git-Server.
Unfortunately, you learn that the developer making these changes was a moron (it happens more frequently these days). You discovered your mistake when you pulled from the repo motd.git
, and found two suspect files named foo
and bar
. You investigate, and use git rm
from the rpigitclient
host to correct the problem:
$ hostname # to get our bearings straight
rpigitclient
$ cd ~/scripts/motd-d
$ git rm foo bar # you remove files from the repo
$ rm 'bar' 'foo' # you remove files from the filesystem
$ git commit -m "remove files foo & bar added by Moronski"
[master 95dea5d] remove files foo & bar added by Moronski
2 files changed, 2 deletions(-)
delete mode 100644 bar
delete mode 100644 foo
$ git push
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 248 bytes | 248.00 KiB/s, done.
Total 2 (delta 1), reused 0 (delta 0), pack-reused 0
To pi@rpigitserver:/home/pi/git-srv/etc-update-motd-d.git
d27a3ae..95dea5d master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.
$
During the course of your work, you will occasionally need to update the repo on one or more of your Git-Clients. This is done as follows:
$ hostname # to get our bearings straight
rpigitclient
$ cd ~/scripts/motd-d
$ git branch --show-current
master # or whatever the current branch is
$ git pull
From pi@rpigitserver:/home/pi/git-srv/etc-update-motd-d
* branch master -> FETCH_HEAD
...
$
Recall that when we initialized our repository in ~/git-srv/etc-update-motd-d.git
on rpigitserver
, we used the --bare
option. Take a look now inside that folder; ls -l
inside the repository. Next, go to a clone
d repo, and take a look inside that folder using ls -l
. You will notice that there are some differences, the most obvious being that the files in the client repo are not visible inside the server repo ~/git-srv/etc-update-motd-d.git
folder! You can search for them if you like, but you won't find them (unless you've read this). This is a result of using the --bare
option in initialization - the files and their contents are in the repository, but the repository contains no "working tree".
But now suppose we want to create a working tree inside ~/git-srv/etc-update-motd-d.git
... how do we add that? As usual in git
, there's a command for that:
$ hostname # to get our bearings straight
rpigitserver
$ cd ~/git-srv/etc-update-motd-d.git
$ pwd
~/git-srv/etc-update-motd-d.git
# add the working tree under tha name 'motd-worktree'
# we can access/add/delete/update/commit files from this tree
$ git worktree add motd-worktree
$ ls -l ./motd-worktree
total 44
-rw-r--r-- 1 git git 65 Jan 3 02:19 10-intro
-rw-r--r-- 1 git git 41 Jan 3 02:19 20-uptime
...
-rw-r--r-- 1 git git 264 Jan 3 02:19 75-imgutil
-rw-r--r-- 1 git git 157 Jan 3 02:19 99-source
# as there is no compelling reason to have a worktree in our 'bare' server repository,
# we'll just remove the working tree as follows:
$ git worktree remove motd-worktree
# And now we're back to a 'bare' repo; a Git-Server
Client Objective: | git client command |
---|---|
Create (clone ) a repo on a client: |
git clone <ssh://user@gitserver:/path/to/repo> |
A L T E R N A T I V E to clone |
1. mkdir mygitrepo && cd mygitrepo |
2. git init |
|
3. git remote add origin <user@gitserver:/path/to/repo> |
|
4. git pull origin master |
|
Create an empty client repo - two steps: | ↓↓↓↓↓ |
1. create a folder & cd into it |
mkdir mygitrepo && cd mygitrepo |
2. initialize the new git repo | git init |
Designate 'remote' ssh srv for client repo | git remote add origin <user@gitserver:/path/to/repo> |
--- ALTERNATIVELY : | manually edit </path/to/repo/.git/config> |
Designate GitHub as the 'remote' | see this recipe |
Show the 'remote' in use for client repo | git remote -v [show] |
Check the 'status' of the client repo | git status see note |
Commit changes to a git client - two steps: | ↓↓↓↓↓ |
1. add new & changed files | git add <file1 file2 etc> -OR- <.> for all files |
2. commit changes with a commit message | git commit -m 'a commit message' |
Get (show) the remote (server) used in a repo | git remote -v |
INITIAL Push repo changes to "gitserver" | git push -u origin master |
SUBSEQUENT Push repo changes to "gitserver" | git push |
INITIAL Pull changes from "gitserver" | git pull origin master |
SUBSEQUENT Pull changes from "gitserver" | git pull |
Restore true mod. time to repo files - two steps | ↓↓↓↓↓ |
1. install | sudo apt install git-restore-mtime |
2. run from inside repo | git restore-mtime |
Server Objective: | git server command |
---|---|
Create an empty server repo - two steps: | ↓↓↓↓↓ |
1. create a folder & cd into it |
mkdir srvgitrepo && cd srvgitrepo |
2. initialize the new git repo | git init --bare |
Create a "working tree" in git server repo | git worktree add <srv-worktree> |
Designate GitHub as the 'remote' for worktree | See this recipe; perform from inside the worktree |
Remove a "working tree" from git server repo | git worktree remove <srv-worktree> |
You will note that using git status
(e.g. in a client repo) will give you the "wrong answer" in many situations. For example:
One of my collaborators has informed me that he has made some
commits
to a repo that we use. I then descend into that repo (i.e.cd blockead.git
) on my host workstation, and rungit status
. That command will yield the following as status:
~blockhead.git $ git status
On branch master
Your branch is up to date with 'origin/master'.
nothing to commit, working tree clean
WTFO?? I wonder what happened to those
commits
he said he made?
Now for the "explanation": Technically speaking the output of git status
may not be the "wrong answer", but it is most certainly an ambiguous and confusing answer. There are various explanations available online, but they "boil down" to this:
"up-to-date" means "up-to-date" with the branch that my branch tracks... which in this case means "up-to-date" with the local
origin/master
branch
Inevitably, the authors of all (most) of these explanations deny that this wording is a problem. I can only guess that they are afraid of offending someone... Well, I'm sorry, but I can't excuse such stupidity and piss-poor use of the English language - even if it is git
. But hey - this is called open-source software for a reason, and so I'll offer the following "open-source" solution to the dysfunctional output of git status
:
$ cd ~/blockhead.git
$ git pull --dry-run # alternatively: 'git fetch --dry-run'
From rpigitserver:/home/pi/git-srv/blockhead.git
2acea0b..b797bb0 master -> origin/master # <=== THIS MEANS A CHANGE HAS BEEN MADE
Is this stupid?... Yeah - I think so. The fact that one needs to resort to such arcane measures to discover whether there is an update on a remote origin tells me that the authors of git
are completely out-of-touch with "normal" users (i.e. "normal" means: 'not an idiotic git-nerd').
- Chapter 2 - Git Basics; highly recommended !
- A search
- The Git working tree, index and commit history explained by example
- git-worktree - Manage multiple working trees
- Q&A: fatal: This operation must be run in a work tree
- Q&A: What would I use git-worktree for?
- Experiment on your code freely with Git worktree
- Using Git Worktree to Master Git Workflow
- Git Worktrees in Use
- Q&A: List files in local Git repo? - good answer
- How can I make git show a list of the files that are being tracked? 1 good answer & 1 incorrect answer
- Difference between a working tree and a repository in Git? is interesting...
-
Install via
sudo apt install git-restore-mtime
; run command viagit restore-mtime