Files
managed-git/Git/Repository.fs
2022-09-03 09:26:43 +01:00

84 lines
2.7 KiB
Forth

namespace Git
open System.IO.Abstractions
/// If you have a Repository in scope, you know it existed at the time it was created.
type Repository =
private
{
Directory : IDirectoryInfo
IsCaseSensitive : bool
}
member this.Fs = this.Directory.FileSystem
type InitFailure =
| DirectoryDoesNotExist
| AlreadyGit
[<RequireQualifiedAccess>]
module Repository =
let gitDir (r : Repository) : IDirectoryInfo =
r.Fs.Path.Combine (r.Directory.FullName, ".git")
|> r.Fs.DirectoryInfo.FromDirectoryName
let internal objectDir (r : Repository) : IDirectoryInfo =
r.Fs.Path.Combine ((gitDir r).FullName, "objects")
|> r.Fs.DirectoryInfo.FromDirectoryName
let internal refDir (r : Repository) : IDirectoryInfo =
r.Fs.Path.Combine ((gitDir r).FullName, "refs")
|> r.Fs.DirectoryInfo.FromDirectoryName
let internal createSubDir (r : IDirectoryInfo) (name : string) : IDirectoryInfo =
let output =
r.FileSystem.Path.Combine (r.FullName, name)
|> r.FileSystem.DirectoryInfo.FromDirectoryName
output.Create ()
output
/// TODO this function diverges from `git` itself, which requires the existence of `.git/HEAD`.
let make (dir : IDirectoryInfo) : Repository option =
let fs = dir.FileSystem
let gitPath = fs.Path.Combine (dir.FullName, ".git")
if dir.Exists && fs.Directory.Exists gitPath then
{
Directory = dir
IsCaseSensitive =
// Yes, if someone's made both `.git` and `.GIT` then we may think
// the filesystem is case insensitive.
not (fs.Directory.Exists (gitPath.ToUpperInvariant ()))
}
|> Some
else
None
let init (defaultBranchName : BranchName) (dir : IDirectoryInfo) : Result<Repository, InitFailure> =
match make dir with
| Some _ -> Error AlreadyGit
| None ->
if not dir.Exists then
Error DirectoryDoesNotExist
else
// TODO do this atomically
let gitDir = createSubDir dir ".git"
let objectDir = createSubDir gitDir "objects"
let _packDir = createSubDir objectDir "pack"
let _infoDir = createSubDir objectDir "info"
let refsDir = createSubDir gitDir "refs"
let _headsDir = createSubDir refsDir "heads"
let _tagsDir = createSubDir refsDir "tags"
let fs = dir.FileSystem
fs.File.WriteAllText (
fs.Path.Combine (dir.FullName, ".git", "HEAD"),
sprintf "ref: refs/heads/%s" (string<BranchName> defaultBranchName)
)
make dir |> Option.get |> Ok