Game of Trees
Game of Trees (Got) is a simple, easy to use version control system that aims to be compatible with git repositories (see git-repository(5)).
It's still under development, but can be used today as a substitute for git. It is freely reusable under a BSD license.
Utilities
Game of Trees (Got) is made up of several components:
got(1) only provides commands for version control. Repository maintenance is handled by gotadmin, the git server is handled by gotd, the web interface is handled by gotwebd, and the terminal viewer is handled by tog(1).
Install
# pkg_add got
Configuring got
First, we need to tell got about the author of the repo:
$ export GOT_AUTHOR="username <username@example.com>"
Replace username
and username@example.com
. It's recommended to add this as part of your ~/.profile?:
$ echo 'export GOT_AUTHOR="username <username@example.com>"' >> ~/.profile
Let's also enable tab-completion of got commands:
$ echo 'set -A complete_got_1 -- $(got -h 2>&1 | sed -n s/commands://p)' >> ~/.profile
By default, got uses the text editor vi(1) if $EDITOR or $VISUAL are not set.
Clone a repository
To clone a repo:
$ got clone git://example.com/repo.git
Replace git://example.com/repo.git
with the repo's actual URL.
got currently supports four protocols: git, ssh, http, and https. ssh and https are preferred protocols because they provide encryption.
For this guide, we will use the ngircd git repo as an example:
$ cd ~ $ got clone https://github.com/ngircd/ngircd
To clone a repo using ssh:
$ got clone ssh://example.com/repo.git
NOTE: If you are attempting to clone a repo over ssh, make sure that the
fingerprints in ~/.ssh/known_hosts
have not changed. If they have, you will
be unable to clone.
For example, to clone OpenBSD's github repo:
$ got clone ssh://git@github.com/openbsd/src.git
Create a mirror
To create a cloned mirror of a repository:
$ got clone -m ssh://example.com/example.git
WARNING: With a cloned repository, new fetches will cause any custom changes to get discarded.
Checkout code
Once you have a repository, you must first checkout the code into a work tree before you can use it:
$ got checkout /path/to/repository
Replace /path/to/respository
.
Let's checkout the ngircd code:
$ cd ~ $ got checkout ~/ngircd.git
You can check out code from a specific branch or
commit with -b
and -c
:
$ got checkout -b branch -c commit /path/to/repository
got creates an ID SHA1 hash for every commit, so replace commit
with that hash.
Before we can checkout another work tree from ngircd, we must delete the old one:
$ rm -r ~/ngircd $ got checkout -b HEAD -c f4ade537d550b872b7e86e6ffce940dfbad4c60c ~/ngircd.git
This will first delete the old work tree, then check out the f4ade537d550b872b7e86e6ffce940dfbad4c60c commit from HEAD of the ngircd.git repo.
got checkout
will show these status codes:
Status | Meaning |
---|---|
A | new file added |
E | file already exists |
For the next step, we will go ahead and delete this work tree, then check out a work tree based on the most recent commit:
$ rm -r ~/ngircd $ got checkout ~/ngircd.git
Create a new repository
To create an empty repository:
$ got init /path/to/repository
Replace /path/to/repository
. For example:
$ got init ~/example.git
This will create a got repo called example.git.
Afterwards, we need to import the code for the repository:
$ got import -m "Import sources" -r /path/to/repository /path/to/code
-m
provides the log message for the import.-r
provides the repository path.
Replace /path/to/repository
and /path/to/code
. For example:
$ got import -m "Import sources" -r ~/example.git ~/ngircd
This will import the code from ngircd
into example.git
.
Fetch new changes
To fetch changes from a remote repo:
$ got fetch ssh://example.com/repo.git
If no repository is specified, "origin" will be used.
Add file
To add an unversioned file for the next commit:
$ got add filename
Replace filename
.
Here's an example. We first checkout a working directory from example.git:
$ got checkout ~/example.git/ A /home/username/example/.clang_complete A /home/username/example/.dockerignore ... A /home/username/example/src/tool/tool.h Checked out refs/heads/main: 06aa157fd328d1678f916c9edb2799815b37140e Now shut up and hack
Then we create a new file and add it:
$ cd ~/example/ $ echo 'print "hello, world"' > hello.sh $ got add hello.sh
If adding a directory:
$ got add -R pathname
-R
allows recursion. Replace pathname
.
Let's add a directory with a file inside ngircd
, then begin tracking the directory with got:
$ mkdir -p newcode/ $ echo 'print "IRCNow and Forever"' > newcode/ircnow.sh $ got add -R newcode
Commit
To commit changes in a work tree:
$ got commit -m "Commit message"
Replace Commit message
with a commit message.
If changes have been staged, only staged changes will be committed.
Status | Meaning |
---|---|
M | modified file |
D | file deleted |
A | new file added |
NOTE: If changes are not based on the most recent
commit, you may first be required to run got update
before commits can be made.
NOTE: Before running got commit
, make sure to set
up the GOT_AUTHOR environment variable (see above).
Let's commit our new files:
$ got commit -m "Hello, world!" A hello.sh A newcode/ircnow.sh Created commit 23282eae1c4eea70cf25001425820ccf2c47e3e5
Remove file
To remove a versioned file for deletion from the repo in the next commit:
$ got remove filename
Replace filename
.
Let's remove hello.sh:
$ got remove ~/example/hello.sh
If removing a directory:
$ got remove -R pathname
-R
allows recursion. Replace pathname
.
Let's remove the newcode
directory:
$ got remove -R ~/example/newcode/
To commit these changes:
$ got commit -m "Goodbye, world!" D hello.sh D newcode/ircnow.sh Created commit 9ecebae09293d5c48d5da458bc755df74d332c4f
View changes
To view changes in a work tree:
$ got diff
If you provide two objects, got will show the diff between just those two:
$ got diff object1 object2
Replace object1
and object
with the ID SHA1 hash. For example, to view
the diff between the two commits we just made:
$ got diff 23282eae1c4eea70cf25001425820ccf2c47e3e5 9ecebae09293d5c48d5da4 diff 23282eae1c4eea70cf25001425820ccf2c47e3e5 9ecebae09293d5c48d5da458bc755df74d332c4f commit - 23282eae1c4eea70cf25001425820ccf2c47e3e5 commit + 9ecebae09293d5c48d5da458bc755df74d332c4f blob - 39b43f133bfac5074a751bbae6da72fd25007124 (mode 644) blob + /dev/null --- hello.sh +++ /dev/null @@ -1 +0,0 @@ -print "hello, world" blob - 463ce51f15b0cb33fb2150b524e20ae5c39e2d8d (mode 644) blob + /dev/null --- newcode/ircnow.sh +++ /dev/null @@ -1 +0,0 @@ -print "IRCNow and Forever"
This will give you the diff of two files with those ID hashes.
Blame
For a line-by-line history of a file:
$ got blame /path/to/file
Use -c commit
and replace commit
with the ID SHA1 hash to start history from that specific commit. For example:
$ cd ~/ngircd $ got blame -c 71ae2b7d ~/ngircd/NEWS
Update
To update the work tree to the most recent commit on the work tree's branch:
$ got update
This will require manual merging of files if there are conflicts.
Status | Meaning |
---|---|
U | file updated, no local changes |
G | file updated, local changes merged |
C | file updated, conflicts occurred during merge |
D | file deleted |
A | new file added |
~ | versioned file blocked by non-regular file |
! | missing versioned file restored |
# | file not updated due to merge conflicts |
? | changes for an unversioned file not merged |
NOTE: If there are staged changes, you must first commit or unstage them before using got update
.
Suppose we check out a specific commit:
$ cd ~ $ rm -r ngircd $ got checkout -b HEAD -c f4ade537d550b872b7e86e6ffce940dfbad4c60c ~/ngircd.git
We can then update a specific commit:
$ cd ~/ngircd/ $ got update -c c8b12af1d2d155ec79dc2044a4ff177cf07de4fe
View status
To view the status of files in a work tree:
$ got status
Status | Meaning |
---|---|
M | modified |
A | added in next commit |
D | deleted in next commit |
C | modified or added but contains merge conflicts |
! | versioned file expected but missing |
~ | versioned file blocked by non-regular file |
? | unversioned item, not tracked |
If changes are staged, the second column uses these codes:
Status | Meaning |
---|---|
M | modification staged |
A | addition staged |
D | deletion staged |
Show History
To show commit history:
$ got log
This will produce a log of all commits from the current branch:
... ----------------------------------------------- commit ab0eb099e9c0ed60d25fb50dd78d2a638d3b49b8 from: Alexander Barton <alex@barton.de> date: Tue Dec 11 22:04:21 2001 UTC - Test auf stdint.h (HAVE_STDINT_H) hinzugefuegt. ----------------------------------------------- commit f4ade537d550b872b7e86e6ffce940dfbad4c60c from: Alexander Barton <alex@barton.de> date: Tue Dec 11 21:53:04 2001 UTC Initial revision
To display commits from other branches -b
, starting at a
specific commit -c
, limited by the number of commits
-l
:
$ got log -b -c commit -l N
Replace commit
with the ID SHA1 hash and N
with the
number of commits.
For example:
$ got log -b -c 71ae2b7d7f9ae7bc02ed072c07100de0027373d6 -l 10 ----------------------------------------------- commit 71ae2b7d7f9ae7bc02ed072c07100de0027373d6 (master, origin/master, tags/rel-26.1) from: Alexander Barton <alex@barton.de> date: Sat Jan 2 13:32:48 2021 UTC ngIRCd Release 26.1 ...
It's also possible to show commits that match a regular expressions:
$ got log -S regex
Replace regex
with a regular expression:
$ got log -S '[Oo]pen[Ss]+[Ll]' ----------------------------------------------- commit daa88b765111b14047c97256bd2a9a2daabe123b from: Christoph Biedl <ngircd.anoy@manchmal.in-ulm.de> date: Mon Dec 5 22:51:07 2016 UTC via: Alexander Barton <alex@barton.de> Fix building ngIRCd with OpenSSL 1.1 ...
Branching
To print the name of the current branch of the work tree:
$ cd /path/to/worktree $ got branch
For example, in the ngircd working directory, we are in the master branch by default:
$ cd ~/ngircd $ got branch master
To list all the branches:
$ got branch -l * master: 512af135d06e7dad93f51eae51b3979e1d4005cc origin/HEAD: refs/remotes/origin/master origin/master: 512af135d06e7dad93f51eae51b3979e1d4005cc
Inside a work tree, you can create a branch and switch to it:
$ got branch branchname
Replace branchname
.
For example:
$ got branch testing Switching work tree from refs/heads/master to refs/heads/testing $ got branch -l master: 512af135d06e7dad93f51eae51b3979e1d4005cc * testing: 512af135d06e7dad93f51eae51b3979e1d4005cc origin/HEAD: refs/remotes/origin/master origin/master: 512af135d06e7dad93f51eae51b3979e1d4005cc
You can also delete a branch:
$ got branch -d branchename
Only the branch reference branchname
is deleted; commit, tree, and blob
objects remain in the repo.
Let's delete the testing
branch:
$ got branch -d testing
To branch at a specific commit:
$ got branch -c commit branchname
Replace commit
with the ID SHA1 hash and replace branchname
. For example:
$ got branch -c 02850008f4a4e8fff5799157d21ee7924345b3e1 gnutls
This creates the gnutls branch of ngircd.
Tags
To list all existing tags:
$ got tag -l
To create a tag pointing to the most recent commit:
$ got tag -m "Message" tagname
Replace tagname
and Message
.
To create a tag pointing to a specific commit:
$ got tag -c commit -m "Message" tagname
Replace commit
with the ID SHA1 hash and replace Message
and tagname
. For example:
$ got tag -c 3c627dd70d032fa2c5087617da27586cf85e899a -m "Debian OpenSSL Build" debopenssl Created tag aa8df5f73de7d59f4c385a5a33090e75734cc235
This will create the debopenssl
tag to point to commit 3c627dd70d032fa2c5087617da27586cf85e899a
.
Revert changes
If you make any changes to files or folders in a work tree, you can revert those changes:
WARNING: There is no way to undo reverted changes!
$ got revert /path/to/file
Replace /path/to/file
. Files added with got add
become unversioned, and files deleted with got remove
are restored.
To be safe, use -p
so that got asks before reverting.
You can also add -R
for recursion:
$ got revert -R -p /path/to/folder
For example:
$ cd ~/ngircd $ mkdir JUNK $ echo "Here is some new junk" > JUNK/INFO $ got add -R JUNK/ $ got revert -R -p JUNK/
Send Changes
To send changes to a remote repo:
$ got send -r ssh://example.com/repo.git
Replace ssh://example.com/repo.git
with the protocol and URL.
By default, if no remote repo is provided, the work tree's origin (its default repo) will be used.
Changes are only sent if they are based on up-to-date branches in the remote repo. If not, new changes must first be fetched with got fetch
and local branches must be rebased with got rebase
. -f
ignores this requirement.
WARNING: Try to avoid -f
because it may result
in inconsistent tags in different repos.
To send all branches:
$ got send -r ssh://example.com/repo.git -a
Replace example.com/repo.git
. To specify a specific branch:
$ got send -r ssh://example.com/repo.git -b branchname
Replace branchname
. To delete a branch (only references
are deleted):
$ got send -r ssh://example.com/repo.git -d branchname
Rebase
When you have multiple branches, you may want to rebase the commits of one branch onto your current branch:
$ got rebase branchname
Replace branchname
. Note that branchname
must have a
common ancestor with the current branch.
Commits from branchname
are made to the work tree's current branch.
To show a list of past rebases:
$ got rebase -l
Staging
To stage for the next commit:
$ got stage
To stage just one file:
$ got stage /path/to/file
stage [-l] [-p] [-F response-script] [-S] [path ...]
Status | Meaning |
---|---|
A | addition staged |
M | modification staged |
D | deletion staged |
If there are staged changes, got commit will not commit any paths that do not have staged changes.
To list staged changes:
$ got stage -l
If a file is out of date compared with the work tree's current branch, you must run got update
before staging.
To unstage all changes:
$ got unstage
To unstage a single file:
$ got unstage /path/to/file
Info
To show metadata about a work tree:
$ got info
To show additional data about a file:
$ got info /path/to/file
Documentation
The game of trees work tree format is described in got-worktree(5).
The Git repository format is described in git-repository(5).