Sign in

    PaiGack/moon_shadow/gitx/gitz/repo does not have a README file

    Checkout

    pub(all) struct Checkout {
    locate : RepoLocate
    repo : RepoInfo?
    }

    A checkout, or the reason there is none.

    The reader does not build GitInfo: gitinfo imports this package for RepoInfo, so the other direction would be an import loop. What comes back is what it read; narrowing that to GitInfo, and trimming the line endings git leaves in ref files, stays in gitinfo.

    Checkout::to_repo_info

    fn Checkout::to_repo_info(self : Checkout) -> RepoInfo

    Commit

    pub(all) struct Commit {
    date : String
    author : String
    email : String
    first_parent : String
    }

    A commit object, inflated and parsed far enough to answer the fields GitInfo keeps.

    Objects are zlib streams, so reading one means inflating it. Doing it here rather than shelling out to git log -1 is what keeps the generator free of a process: same files, same answers, no shell to find.

    Repo

    pub(all) struct Repo {
    common_dir : String
    git_dir : String
    work_dir : String
    }

    Where .git is.

    Public because reading a checkout is this package's job: a caller that only wants a ref or a commit object gets the layout from discover and asks, instead of re-deriving the three directories itself.

    Repo::is_dirty

    fn Repo::is_dirty(self : Repo) -> Bool

    Whether tracked files differ from what HEAD recorded.

    git status --porcelain --untracked-files=no without the process. The index already carries the blob id git found when the file was last staged, so a tracked file is clean exactly when its content still hashes to that id — git diff-files asked by hand, and the only comparison that cannot be fooled by a copied timestamps. Untracked files are ignored on purpose: generated.mbt is committed, and writing it must not mark every build dirty.

    The cost is hashing every tracked file. A module is a few hundred files and the generator runs once per moon.mod change, so that is cheaper than the stat-based shortcut plus the mtime races it introduces.

    Repo::read_commit

    fn Repo::read_commit(self : Repo, id : String) -> Commit?

    commit as a Commit, or None when the object is not there or is not zlib this reader can inflate.

    A loose object is <common>/objects/<2 hex>/<38 hex>. A packed one is not read: packs are the one place where "read the files yourself" stops being worth the code, so a freshly git gced clone degrades to an empty commit date instead of a wrong one.

    Repo::read_ref

    fn Repo::read_ref(self : Repo, name : String, depth : Int) -> String

    One ref: the loose file first, then packed-refs.

    A ref that exists in both places is the loose one: git pack-refs deletes the loose file when it packs, so the two agree, and an edit leaves the newer value loose.

    RepoInfo

    pub(all) struct RepoInfo {
    available : Bool
    locate : RepoLocate
    branch : String
    tag : String
    describe : String
    commit : String
    short_commit : String
    commit_date : String
    author : String
    email : String
    dirty : Bool
    ahead : Int
    behind : Int
    } derive(Eq,
    Debug
    )

    A checkout read straight out of .git, before it is narrowed to a GitInfo.

    Everything here is a file's contents, produced by gen and understood by this package: the generator knows where a ref lives, this side knows what a ref means. locate matters because .git is a file in a worktree and in a submodule, and a reader that walks into one and gives up looks exactly like "no repository here".

    RepoInfo::empty

    fn RepoInfo::empty(locate? : RepoLocate) -> RepoInfo

    The "nothing here" value, with the reason it is empty.

    RepoInfo::equal

    #deprecated("`RepoInfo::equal` is deprecated, use `Eq::equal` instead.")
    fn RepoInfo::equal(RepoInfo, RepoInfo) -> Bool

    RepoInfo::not_equal

    #deprecated("`RepoInfo::not_equal` is deprecated, use `Eq::not_equal` instead.")
    fn RepoInfo::not_equal(x : RepoInfo, y : RepoInfo) -> Bool

    RepoInfo::to_repr

    #deprecated("`RepoInfo::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn RepoInfo::to_repr(RepoInfo) ->
    Repr

    RepoLocate

    pub(all) enum RepoLocate {
    Ok
    Missing
    Unreadable
    } derive(Eq,
    Debug
    )

    What the reader did with .git.

    RepoLocate::equal

    #deprecated("`RepoLocate::equal` is deprecated, use `Eq::equal` instead.")
    fn RepoLocate::equal(RepoLocate, RepoLocate) -> Bool

    RepoLocate::not_equal

    #deprecated("`RepoLocate::not_equal` is deprecated, use `Eq::not_equal` instead.")
    fn RepoLocate::not_equal(x : RepoLocate, y : RepoLocate) -> Bool

    RepoLocate::to_repr

    #deprecated("`RepoLocate::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn RepoLocate::to_repr(RepoLocate) ->
    Repr

    discover

    fn discover(cwd : String) -> Repo?

    Walk up from cwd to the first .git, the way git does.

    Two shapes to handle: <dir>/.git as a directory, and <dir>/.git as a file holding gitdir: <path> — a linked worktree (<common>/.git/worktrees/<name>, which has a commondir file pointing back) or a submodule (.git/modules/<name>, which has none).

    join_path

    fn join_path(left : String, right : String) -> String

    path as one joined path.

    Kept dumb on purpose: both sides come from git metadata (commondir: ../.. above all), and there is no path library on every toolchain this package is type-checked for. Trailing separators are dropped, .. is resolved halfway in normalize where the whole path is known.

    normalize

    fn normalize(path : String) -> String

    path with separators made / and . / .. resolved textually.

    Textually rather than by getcwd on purpose: commondir can name a directory git would refuse to enter, and following it with the filesystem is how a build tool ends up walking somewhere it should not have.

    read_checkout

    fn read_checkout(cwd : String) -> Checkout

    The checkout at cwd, read straight out of the files git would read.

    This is the whole point of the library: the machine that runs the software has no .git and maybe no git, and the build machine should not need a shell either. HEAD, the ref it names, packed-refs, refs/tags/*, the commit object and the index are all plain files — that is what git reads too.

    locate comes back beside the value because it is the one thing worth printing: "no repository" and "a .git that points nowhere" are different mistakes, and a version string is baked into the artifact, not logged.

    read_text

    fn read_text(path : String) -> String

    The contents of path with line noise stripped, or "" when it is not there.

    @fs rather than an fopen of our own because this package is also type-checked for wasm-gc, where a native-only FFI would not resolve.

    trim

    fn trim(text : StringView) -> String

    Whitespace off both ends, everything else kept.

    Takes a StringView so split results do not have to be copied first, and trims rather than String::trim because a ref name may hold any byte the filesystem allows — only the spaces git wrote are noise there.