Skip to content

Commit

Permalink
Import git9 from mercurial repository
Browse files Browse the repository at this point in the history
  • Loading branch information
oridb committed Jun 29, 2019
0 parents commit ec28e68
Show file tree
Hide file tree
Showing 32 changed files with 5,727 additions and 0 deletions.
19 changes: 19 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Copyright (c) 2019 Ori Bernstein

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
151 changes: 151 additions & 0 deletions README
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
Git for Plan 9: git/fs
======================

Plan 9 is a non-posix system that currently has no git port. Git
itself feels distinctly un-plan9ish.

Git/fs implements a git client for plan 9. The intent is to support
working with git repositories, without cloning the git interface
directly.

Git/fs is alpha software. It more or less works for me, but has many
bugs, and many more missing tools, but it's at the point where the
hard parts are done, and "the rest is just scripting", using standard
command line tools (cp, echo, diff, patch, and diff3).

Structure
---------

The git/fs program provides a file system mounted on /mnt/git. It
provides a read-only view into the repository contents to allow
scripts to inspect the data. Surrounding scripts and binaries will
manipulate the repository contents directly. These changes will be
immediately mirrored in the file system.

Scripts will generally mount git/fs as needed to do
their work, but if you want to browse the repository
manually, run it yourself. You'll get `/mnt/git` mounted,
with the following contents:

/mnt/git/object: The objects in the repo.
/mnt/git/branch: The branches in the repo.
/mnt/git/ctl: A file showing the status of the repo.
Currently, it only shows the current branch.
/mnt/git/HEAD An alias for the currently checked out
commit directory.

Visible Differences
-------------------

The most obvious difference is that Git's index is a bit boneheaded, so I'm
ignoring it. The index doesn't affect the wire protocol, so this
isn't an interoperability issue, unless you share the same physical
repository on both Plan 9 and Unix. If you do, expect them to disagree
about the files that have been modified in the working copy.

In fact, the entire concept of the staging area has been dropped, as
it's both confusing and clunky. There are now only three states that
files can be in: 'untracked', 'dirty', and 'committed'. Tracking is
done with empty files under .git/index9/{removed,tracked}/path/to/file.

It's implemented in Plan 9 flavor C, and provides tools for writing
repository contents, and a file system for read-only access, which
will mirror the current state of the repository.

Installation
------------

Install with `mk install`.

Examples
--------

Some usage examples:

git/clone git://git.eigenstate.org/ori/mc.git
git/log
cd subdir/name
git/add foo.c
diff bar.c /mnt/git/HEAD/
git/commit
git/push

Commits are presented as directories with the following
contents:

author: A file containing the author name
hash: A file containing the commit hash
parent: A file containing the commit parents, one per line.
msg: A file containing the log message for that commit
tree: A directory containing a view of the repository.

So, for example:

% ls /mnt/git/branch/heads/master
/mnt/git/branch/heads/master/author
/mnt/git/branch/heads/master/hash
/mnt/git/branch/heads/master/msg
/mnt/git/branch/heads/master/parent
/mnt/git/branch/heads/master/tree
% cat /mnt/git/branch/heads/master/hash
7d539a7c08aba3f31b3913e0efef11c43ea9

# This is the same commit, with the same contents.
% ls /mnt/git/object/7d539a7c08aba3f31b3913e0efef11c43ea9f9ef
/mnt/git/object/7d539a7c08aba3f31b3913e0efef11c43ea9f9ef/author
/mnt/git/object/7d539a7c08aba3f31b3913e0efef11c43ea9f9ef/hash
/mnt/git/object/7d539a7c08aba3f31b3913e0efef11c43ea9f9ef/msg
/mnt/git/object/7d539a7c08aba3f31b3913e0efef11c43ea9f9ef/parent
/mnt/git/object/7d539a7c08aba3f31b3913e0efef11c43ea9f9ef/tree

# what git/diff will hopefully do more concisely soon, filtering
# out the non-git files.
ape/diff -ur /mnt/git/branch/heads/master/tree .
Only in .: .git
Only in .: debug
diff -ur /mnt/git/branch/heads/master/tree/fold.myr ./fold.myr
--- /mnt/git/branch/heads/master/tree/fold.myr Wed Dec 31 16:00:00 1969
+++ ./fold.myr Mon Apr 1 21:39:06 2019
@@ -6,6 +6,8 @@
const foldexpr : (e : expr# -> std.option(constval))
;;

+/* Look, diffing files just works, and I don't need any fancy glue! */
+
const foldexpr = {e
match e
| &(`Eident &[.sc=`Sclassenum, .name=name, .ty=`Tyenum &(`Body enum)]):
Only in .: refs


The following utilities and binaries are provided:

fs: The git filesystem.
fetch: The protocol bits for getting data from a git server.
send: The protocol bits for sending data to a git server.
save: The gnarly bits for storing the files for a commit.
conf: A program to extract information from a config file.
clone: Clones a repository.
commit: Commits a snapshot of the working directory.
log: Prints the contents of a commmit log.
add: Tells the repository to add a file to the next commit.
walk: `du`, but for git status.


Supported protocols: git:// and git+ssh://. If someone
implements others, I'll gladly accept patches.

TODOs
-----

Documentation has not yet been written. You'll need to read the
source. Notably missing functionality includes:

git/mkpatch: Generate a 'git am' compatible patch.
git/apply: Apply a diff.
git/diff: Wrapper wrapper around git/walk that
diffs the changed files.
git/merge: Yup, what it says on the label. Should
also be a script around git/fs.
git/log: Need to figure out how to make it filter
by files.
46 changes: 46 additions & 0 deletions add
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/bin/rc -e

rfork ne

fn usage {
echo 'usage: '$argv0' [-r] files..' >[1=2]
echo ' -r: remove instead of adding' >[1=2]
}

add='tracked'
del='removed'
while(~ $1 -*){
switch($1){
case -r
add='removed'
del='tracked'
case --
break
case *; usage
}
shift
}

dir=`{pwd}
base=`{git/conf -r}
if(! ~ $status ''){
echo 'not in git repository' `{pwd} >[1=2]
exit notrepo
}
cd $base
rel=`{sed 's@^'$base'/*@@' <{echo $dir}}
if(~ $#rel 0)
rel=''
for(f in $*){
if(! test -f $base/$rel/$f){
echo 'could not add '$base/$rel/$f': does not exist' >[1=2]
exit 'nofile'
}
addpath=.git/index9/$add/$rel/$f
delpath=.git/index9/$del/$rel/$f
mkdir -p `{basename -d $addpath}
mkdir -p `{basename -d $delpath}
touch $addpath
rm -f $delpath
}
81 changes: 81 additions & 0 deletions branch
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#!/bin/rc -e

rfork en

fn usage{
echo usage: $argv0 [-b base] [-o origin] new >[2=1]
echo ' '-b base: use "base" for branch (default: current branch) >[2=1]
echo ' '-o origin: use "origin" for remote branch >[2=1]
echo ' 'new: name of new branch
exit usage
}

if(! cd `{git/conf -r}){
exit 'not in git repository'
exit notgit
}
git/fs
nl='
'
stay=''
create=''
cur=`{awk '$1=="branch"{print $2}' < /mnt/git/ctl}
while(~ $1 -* && ! ~ $1 --){
switch($1){
case -c; create=true
case -s; stay=true
case -o; origin=$1
case *; usage
}
shift
}
if(~ $1 --) shift

if(~ $#* 0){
echo $cur
exit
}
if(! ~ $#* 1)
usage
new=$1

if(~ $create ''){
if(! test -e .git/refs/heads/$new){
echo branch $new: does not exist >[1=2]
exit exists
}
}
if not{
if(test -e .git/refs/heads/$new){
echo could not create $new: already exists >[1=2]
exit exists
}
branched=''
candidates=(.git/refs/$cur .git/refs/heads/$cur .git/refs/remotes/$cur .git/refs/remotes/*/$cur)
for(br in $candidates){
if(test -f $br){
echo 'creating new branch '$new
cp $br .git/refs/heads/$new
branched="ok"
}
}
if(~ $branched ''){
echo 'could not find branch '$cur >[1=2]
exit notfound
}
}

if(~ $stay ''){
rm -f `$nl{git/walk -cfT}
echo 'ref: refs/heads/'$new > .git/HEAD
tree=/mnt/git/HEAD/tree
@{builtin cd $tree && tar cif /fd/1 .} | @{tar xf /fd/0}
for(f in `$nl{walk -f $tree | sed 's@^'$tree'/*@@'}){
if(! ~ $#f 0){
idx=.git/index9/tracked/$f
mkdir -p `{basename -d $idx}
walk -eq $f > $idx
}
}
}
98 changes: 98 additions & 0 deletions clone
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#!/bin/rc

rfork en
nl='
'

if(~ $#* 1){
remote=$1
local=`{basename $1 .git}
}
if not if(~ $#* 2){
remote=$1
local=$2
}
if not{
echo usage: git/clone remote [local] >[1=2]
exit usage
}
if(test -e $local){
echo $local already exists
exit exists
}
fn clone{
mkdir -p $local/.git
mkdir -p $local/.git/objects/pack/
mkdir -p $local/.git/refs/heads/
cd $local
dircp /sys/lib/git/template .git
echo '[remote "origin"]' >> .git/config
echo ' url='$remote >> .git/config
echo ' fetch=+refs/heads/*:refs/remotes/origin/*' >> .git/config
{git/fetch $remote >[2=3] | awk '
/^remote/{
if($2=="HEAD"){
headhash=$3
headref=""
}else{
gsub("^refs/heads", "refs/remotes/origin", $2)
if($2 == "refs/remotes/origin/master" || $3 == headhash)
headref=$2
outfile = ".git/" $2
system("mkdir -p `{basename -d "outfile"}")
print $3 > outfile
close(outfile)
}
}
END{
if(headref != ""){
remote = headref
gsub("^refs/remotes/origin", "refs/heads", headref)
system("mkdir -p `{basename -d .git/" headref"}");
system("cp .git/" remote " .git/" headref)
print "ref: " headref > ".git/HEAD"
}else{
print "warning: detached head "headhash > "/fd/2"
print headhash > ".git/HEAD"
}
}
'} |[3] tr '\x0d' '\x0a'
if(! ~ $status '|')
exit 'clone:' $status
tree=/mnt/git/branch/heads/master/tree
echo checking out repository...
if(test -f .git/refs/remotes/origin/master){
cp .git/refs/remotes/origin/master .git/refs/heads/master
git/fs
@ {builtin cd $tree && tar cif /fd/1 .} | @ {tar xf /fd/0}
if(! ~ $status '')
exit 'checkout:' $status
for(f in `$nl{walk -f $tree | sed 's@^'$tree'/*@@'}){
if(! ~ $#f 0){
idx=.git/index9/tracked/$f
mkdir -p `{basename -d $idx}
walk -eq $f > $idx
}
}
}
if not{
echo no master branch >[1=2]
echo check out your code with git/branch >[1=2]
}
}
@{clone}
st=$status
if(~ $st ''){
echo done.
}
if not{
echo clone failed: $st >[2=1]
echo cleaning up $local >[2=1]
rm -rf $local
}
Loading

0 comments on commit ec28e68

Please sign in to comment.