Git cheatsheet (FreeBSD/Linux/MacOS+CLI)
Workflow via FreeBSD/Linux/MacOS and NFS
Unix-like operating systems, like FreeBSD, Linux or MacOS, include a git client and numerous related tools which can be used to fetch and share changes from the master copy held by ROOL.
The RISC OS sources currently need to be built using RISC OS. You can use emulator for this, in which case you need to run git
in a directory visible to the emulator, but for the rest of this cheatsheet, we assume you will be running git
on a Unix machine but building on a physical RISC OS machine, using the NFS protocol to share files back and forth between the two. The NFS server runs on the Unix machine, and the NFS client runs on RISC OS.
A key point to be aware of, to avoid “Insufficient access” errors, is that the RISC OS NFS client doesn’t support current user authentication protocols. This means that it always creates new files and directories with Unix user-ID (UID) 32767 and group-ID (GID) 9999, and its access to existing files and directories are only permitted to the extent that a user with that UID or GID would be. By contrast, the git
command will run with the IDs of your login shell – commonly this will be UIDs of 1001, 1000 or 501 for FreeBSD, Linux or MacOS respectively. It is crucial that files and directories created by git
are both readable and writable by the NFS client and vice versa, even though the former will be owned by UID 1001, 1000 or 501 and the latter will be owned by UID 32767.
Different methods are required to cater for files created on Unix versus RISC OS. For the former, we assign the special “set-GID” attribute on the shared directory so that its contents inherit the NFS client’s GID even though they will have the login shell’s UID, then we ensure the default permission mask is configured so that the files and directories created by git
are group-read-write. For the latter, we rely on the user (that’s you!) setting the access permissions to world read-write manually, either before or after copying them to the NFS mount. That can be achieved from the Filer menu File (or Dir.) > Access > Access details, selecting both Public read and Public write then clicking OK.
The following describes how to set things up using a number of popular Unix-like operating systems: Debian, FreeBSD, MacOS, Raspberry Pi OS and Ubuntu. It assumes your username is jbloggs
, your local network IP addresses are of the form 192.168.0.x, and you wish to share the directory ~/riscos
. Lines starting with #
are intended to be typed at a root shell, and those starting with $
are intended to be typed at a normal, unprivileged shell.
Initial setup
Debian:
# usermod -aG sudo jbloggs
# apt install -y git rpcbind nfs-kernel-server
FreeBSD:
# pkg install sudo git
# echo "jbloggs ALL=(ALL) ALL" > /usr/local/etc/sudoers.d/jbloggs
MacOS:
No actions needed.
Raspberry Pi OS:
$ sudo apt install -y nfs-kernel-server
Ubuntu:
$ sudo apt install -y git gawk rpcbind nfs-kernel-server
Starting from Ubuntu 20.10 (“Groovy”), it is also necessary to enable NFS over UDP, like this:
$ sudo gawk -i inplace '{ print } /^\[nfsd\]$/ { print "udp=y" }' /etc/nfs.conf
Create the mount point
The way you do this is common across all OSes:
$ mkdir ~/riscos
$ sudo chown 32767:9999 ~/riscos
$ sudo chmod 2777 ~/riscos
Configure the NFS server
Debian, Raspberry Pi OS, Ubuntu:
$ echo "/home/jbloggs/riscos 192.168.0.0/24(rw,sync,all_squash,anonuid=32767,anongid=9999,no_subtree_check)" | sudo tee -a /etc/exports
$ sudo exportfs -ra
$ sudo service portmap restart
$ sudo service nfs-kernel-server restart
FreeBSD:
$ cat - << EOF | sudo tee -a /etc/rc.conf
nfs_server_enable="YES"
nfs_server_flags="-u -t -n 4"
rpcbind_enable="YES"
mountd_flags="-r"
mountd_enable="YES"
EOF
$ echo "/usr/home/jbloggs/riscos -network 192.168.0.0 -mask 255.255.255.0" | sudo tee -a /etc/exports
$ sudo reboot
MacOS:
$ echo "/Users/jbloggs/riscos -network 192.168.0.0 -mask 255.255.255.0" | sudo tee -a /etc/exports
$ sudo nfsd enable
Configure default permissions mask
Debian, Raspberry Pi OS:
$ echo "umask 0002" >> ~/.bashrc
$ source ~/.bashrc
FreeBSD:
$ echo "umask 0002" >> ~/.shrc
$ . ~/.shrc
MacOS:
$ echo "umask 0002" >> ~/.profile
$ source ~/.profile
Ubuntu:
No actions needed.
Setting up the RISC OS machine
The NFS protocol is not enabled in OmniClient by default. To remedy this, open the file !Omni.Files.Startup
in an editor and change the relevant line to read:
SetEval Omni$NFS 1
Then:
- Start OmniClient (installed as !Omni in Apps)
- From the icon bar menu, choose Mounts > Protocols > NFS
- Fill in the following fields:
Name: a name by which the mount will be referred to on RISC OS
Server name: the name or IP address of the Unix machine
Directory path: matching the first word from /etc/exports
– for example /home/<username>/riscos
User name: nobody
- Click on Connect
- Optionally, from the icon bar menu, choose Mounts > Save mounts, so that the mount settings will be available under the Mounts submenu after any reboots
- There’s more information in the chapter OmniClient in the RISC OS 5 User Guide
Get sources to a single component
On the git machine, navigate to the directory where you want your source tree to be stored.
Next, choosing the Alarm application as an example, clone it:
git clone https://gitlab.riscosopen.org/RiscOS/Sources/Apps/Alarm.git
You can find the URL to use from the project’s GitLab page by clicking on the Clone button on the Project > Details screen. If you don’t have an account on the server, you must choose the HTTPS option.
Getting the latest sources to an entire product to build on RISC OS
On the git machine, navigate to the directory where you want your source tree to be stored.
Next, choosing the Risc PC IOMD ROM as an example, clone it:
git clone https://gitlab.riscosopen.org/Products/IOMDHAL.git
cd IOMDHAL
git submodule update --init --remote --no-fetch
If you have git version 2.23.0 or later, you can use this single command instead:
git clone --recurse-submodules --remote-submodules https://gitlab.riscosopen.org/Products/IOMDHAL.git
You may find the process completes significantly faster if you also pass a --jobs 4
parameter to git clone
.
Note that there is a different recommended procedure listed below for fetching the latest version if you have made source code changes locally since you did the clone, or if enough time has passed since you did the clone that someone else might have published changes.
Now, on the RISC OS machine, copy the IOMDHAL.RiscOS
directory from your NFS mount to a local filing system. The IOMDHAL./git
directory (IOMDHAL/.git
on the server) is very large and of no use from within RISC OS, so can be ignored. Make sure the Filer has ‘seen’ the SetPaths application inside your copy of the DDE, then run RiscOS.Apps.!Prepare
and follow the instructions. You only need to run !Prepare
once for each copy of the source tree: subsequently, you should run RiscOS.Apps.!Builder
instead.
Getting the source to a particular version of a build
To get a specific previous release, change into the IOMDHAL directory and do something like:
git checkout IOMDHAL-5_22
git submodule update
Viewing the history of my local source tree
Because git is a distributed version control system, you actually have a complete copy of the repository’s history, including any changes you’ve made locally but not yet shared. It is common to use graphical history viewers on your local machine both for studying the existing code in a faster way than is possible using web viewers, and for checking how your changes will look to others in advance of publishing them.
The grandfather of all git viewers, and still a popular option, is called gitk. It can be started from the following command line, for example:
gitk --all
You can also explore the history of individual files, for example
gitk --all -- Makefile
If you invoke gitk from within a component, you see the history of that component. If you invoke it from the higher up (for example the RiscOS directory) you see the to history of the build as a whole.
Many other git viewers are available, such as gitg, Fork, Ungit, SmartGit, GitKraken or SourceTree. A handy list can be found on git’s own website. Most IDEs, such as Visual Studio, X-code or Eclipse, come with git viewers built in, so you can do things like view multiple revisions of a file at the same time in different windows without having to explicitly check them out.
Local setup before you start to make changes
Before you can use git to record any changes locally – even if you never intend to share them – you need to configure git with a valid user name and email address. If you have not done this before, use something like:
git config --global user.name "John Smith"
git config --global user.email "jsmith@example.com"
Remember that if you publish your commits, the email address you specify here will be publicly visible. If you prefer, you have the option of using a unique private commit email address instead – any emails sent to that address won’t be delivered, but GitLab will still know that any commits that specify that address should be associated with your GitLab account.
Recording your changes locally
First, you need to copy your source tree from your build machine back to the machine you run git on (unless you were building in an emulator on that machine).
You should store the changes you wish to keep on a branch in each of your local git repositories. In many cases, this will make the process of merging your changes with other people’s much simpler. Use
git checkout -b <new_branch>
which also switches to the new branch so any future commits are applied there.
The branch name you choose should be descriptive of the line of development that you are performing, and typically would not need to include anything identifying you as a developer by name. We recommend the use of a CamelCase name for the branch because GitLab will automatically convert that to a string of space-separated words to form a default title for your contribution when you come to submit it to ROOL for review. Please do not use .
, -
or _
characters in your branch names, since these have special meanings for the tag names used in our repositories.
The next step is to stage your changes – that is, tell git which of your changed files (and even which subset of the changes to each file) you want to be recorded in your commit. This is your chance you check that you aren’t bothering your reviewers with irrelevant changes such as temporary debugging lines. To stage changes, use a command like
git add -i
Then to commit your staged changes
git commit
Enter a suitable change log when prompted. This should comprise a (brief) description of the changes on a single line, an empty line, then any further body text and details.
Updating your clone with the latest version
You might want to update your cloned repositories simply to obtain newer versions of the sources. However, it is also necessary to update them immediately before you publish a contribution, because it’s your responsibility to accommodate any changes that other people have made since you started your branch. The process is the same either way.
If you have changed any repositories, first make sure you have committed them before proceeding as described in the section above. To remind you which repositories you have changed, you can use the command line git status
command from the super-project:
git status | grep "modified content"
Once this is done, move back to the super-project, and do
git pull --rebase
git submodule update --init --remote --rebase
This will, for the super-project and then for each of the submodules in turn, fetch the changes from upstream, and then move (“rebase” in git terminology) any commits on your local branch so that they come after the upstream commits in the git history. The presence of --init
just ensures that if any new submodules have been added centrally you’ll get a copy of that too.
Occasionally, git won’t be able to do this rebasing automatically: in such cases it will stop and ask you to resolve the conflicts it couldn’t handle when moving a given commit. Once you have fixed the conflicts, mark them as such using
git add -i
then resume the rebase operation using
git rebase --continue
Once you’ve finished rebasing that component, you need to go back to the super-project and do
git submodule update --remote --rebase
again to update any remaining components.
If you have more than one local development branch, note that these operations will only automatically rebase the branch that each component was checked out on. It will be your responsibility to rebase any other branches explicitly using appropriate git rebase
commands.
Lastly, don’t forget to copy the sources back to your build machine at the end, if applicable.
Submit changes for review
Once you are ready to share your changes to a component, you do this by raising a GitLab Merge Request as follows.
First, if you don’t yet have a user account on the GitLab server, you need to request one by emailing code@riscosopen.org. Once you have an account you will need to go to your account’s Settings then click on SSH Keys and associate an SSH key with your account.
Then, if you have never submitted a merge request to a given component, you will need two things:
- You need to click on Request Access on the Details screen of the top-level RiscOS Group, and
- You need a personal fork of the component on the GitLab server: also on the Project Details screen, click on Fork. After you’ve done this, you can use the Clone button to grab a copy of the URL of the fork repository for use in the next step; in this case, you need the SSH version of the URL.
As a one-time operation for each cloned submodule that you have made edits to on your local git machine, you need to associate it with the fork like this:
git remote add <name> <url>
Where <name> identifies the remote repository and <url> is the URL copied in the previous step, for example:
git remote add myfork git@gitlab.riscosopen.org:myusername/Alarm.git
(You may notice that the repositories are listed under your username in a flat namespace. It is for this reason that we now require every component to have a unique name.)
The first time you wish to submit changes from a particular local clone, use
git push -u myfork mybranchname
(The -u
option makes git remember which repository to push changes to for the current branch in future.)
Git will helpfully print the URL you need to go to in order to initiate the merge request. Alternatively, navigate to the Repository Branches screen for your fork project on GitLab, and click on Merge request for the branch in question. On the merge request page:
- Since a merge request can consist of multiple commits, you are asked to enter a description of the merge request as a whole.
- Make sure you select ROOL as the assignee, or nobody will know to look at your merge request! Note that you will not be able to assign the merge request until you have been granted access to the project as explained in step 1 above.
- Don’t assign ROOL as a reviewer, leave that and the milestone unset
Although the GitLab interface allows it, please don’t click the upvote or approve buttons for your own work; they’re for the independent reviewers’ use.
If you are asked to make changes during the review process, and your merge request consists of only a single commit, you would typically make your edits and then do
git add -i
git commit --amend
git push --force
to update your fork project on the GitLab server.