...
This commit is contained in:
114
lib/develop/gittools/architecture.md
Normal file
114
lib/develop/gittools/architecture.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# GitTools Module Architecture
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
GitTools is a lightweight Git‑oriented service layer written in V Lang.
|
||||
It sits **between** higher‑level application code (CLI tools, DevOps scripts, GUIs) and the Git executable, offering:
|
||||
|
||||
* **Repository discovery & life‑cycle** under a single *code‑root* directory
|
||||
* **High‑level operations** (clone, commit, push, pull, delete, …) that can be executed in batch across many repos
|
||||
* **Status inspection & caching** through Redis, so expensive `git` calls are avoided between runs
|
||||
* **Utility helpers** (path mapping, VS Code / SourceTree launchers, SSH‑key setup) to smooth local development workflows.
|
||||
|
||||
---
|
||||
|
||||
## 2. High‑Level Design
|
||||
|
||||
```
|
||||
┌────────────────────┐ 1️⃣ factory.new()
|
||||
│ GitStructure │<─────────────┐
|
||||
│ (singleton/cache) │ │
|
||||
└────────────────────┘ │
|
||||
▲ owns many │
|
||||
│ │
|
||||
│ 2️⃣ get_repo()/path() │
|
||||
│ ▼
|
||||
┌────────────────────┐ exec() / status_update()
|
||||
│ GitRepo │──────────────────────────────┐
|
||||
│ (one repository) │ │
|
||||
└────────────────────┘◄──────────────────────────────┘
|
||||
│
|
||||
▼ uses
|
||||
┌────────────────────┐
|
||||
│ GitLocation │ (URL ↔ path ↔ metadata conversions)
|
||||
└────────────────────┘
|
||||
```
|
||||
|
||||
* **GitStructure** (singleton per *coderoot*) is the entry point; it maintains an in‑memory map of `&GitRepo` and persists metadata in Redis (`git:<coderoot‑hash>` keys).
|
||||
* **GitRepo** wraps a single working‑tree and exposes both **mutating** commands (`commit`, `push`, `pull`, …) and **informational** queries (`need_commit`, `get_changes_*`).
|
||||
* **GitLocation** is a pure‑value helper that parses/creates Git URLs or paths without touching the filesystem.
|
||||
|
||||
Key flows:
|
||||
|
||||
1. `gittools.new()` (→ `factory.v`) constructs or fetches a `GitStructure` for a *coderoot*.
|
||||
2. Repository acquisition via `get_repo()` | `get_repos()` | `path()` – these consult the in‑memory map **and** Redis; cloning is performed on‑demand.
|
||||
3. Expensive remote state (`git fetch`, branch/tag lists) is refreshed by `GitRepo.load()` and memoised until `remote_check_period` expires.
|
||||
4. Batch operations are orchestrated by `GitStructure.do()` (→ `gittools_do.v`) which parses CLI‑like arguments and delegates to each selected repo.
|
||||
|
||||
---
|
||||
|
||||
## 3. File‑by‑File Breakdown
|
||||
|
||||
| File | Core Responsibility |
|
||||
| --------------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
||||
| **factory.v** | *Public API*. Creates/gets `GitStructure`, initialises Redis config, and exposes `gittools.path()` helper. |
|
||||
| **gitstructure.v** | Implements the `GitStructure` aggregate: caching, recursive discovery, config persistence. |
|
||||
| **gitlocation.v** | Pure parsing utilities to derive a `GitLocation` from URLs or FS paths. |
|
||||
| **repository.v** | Primary `GitRepo` implementation: mutations (commit/push/etc.), checkout logic, SSH‑key handling. |
|
||||
| **repository\_load.v** | Pulls reality into memory (`git fetch`, branch/tag maps) and maintains the `last_load` timestamp. |
|
||||
| **repository\_info.v** | High‑level queries (`need_pull`, `need_push`,…) and diff helpers. |
|
||||
| **repository\_utils.v** | Convenience UX helpers (VS Code, SourceTree, human paths, URL builders). |
|
||||
| **repository\_cache.v** | Thin Redis (de)serialisation for `GitRepo`. |
|
||||
| **gittools\_do.v** | Batch command dispatcher used by topline scripts/CLI. |
|
||||
| **repos\_get.v / repos\_print.v** | Collection filtering, status table printer. |
|
||||
| **tests** | Pure V unit tests for URL parsing & path logic. |
|
||||
|
||||
---
|
||||
|
||||
## 4. Data Structures & Storage
|
||||
|
||||
### 4.1 GitStructure
|
||||
|
||||
```v
|
||||
pub struct GitStructure {
|
||||
key string // md5(coderoot)
|
||||
coderoot pathlib.Path // ~/code by default
|
||||
repos map[string]&GitRepo // key = provider:account:name
|
||||
config_ ?GitStructureConfig // persisted in Redis
|
||||
}
|
||||
```
|
||||
|
||||
*Redis schema*
|
||||
|
||||
```
|
||||
site:key → config JSON
|
||||
site:key:repos:<provider:acct:name> → GitRepo JSON
|
||||
```
|
||||
|
||||
### 4.2 GitRepo (excerpt)
|
||||
|
||||
```v
|
||||
pub struct GitRepo {
|
||||
provider string // e.g. github
|
||||
account string // org/user
|
||||
name string // repo name
|
||||
status_remote GitRepoStatusRemote
|
||||
status_local GitRepoStatusLocal
|
||||
status_wanted GitRepoStatusWanted
|
||||
last_load int // epoch
|
||||
has_changes bool
|
||||
}
|
||||
```
|
||||
|
||||
Status structs separate **remote**, **local** and **desired** state, enabling `need_*` predicates to remain trivial.
|
||||
|
||||
---
|
||||
|
||||
## 5. Execution & Behavioural Notes
|
||||
|
||||
1. **Shallow clones** – configurable via `GitStructureConfig.light`; uses `--depth 1` to accelerate onboarding.
|
||||
2. **SSH vs HTTPS selection** – `GitRepo.get_repo_url_for_clone()` interrogates `ssh-agent` presence; falls back to HTTPS when no agent.
|
||||
3. **Global instance cache** – `__global ( gsinstances map[string]&GitStructure )` guarantees a single object per process.
|
||||
*Caveat:* not thread‑safe.
|
||||
4. **Command execution** – all Git interaction flows through `GitRepo.exec()`, a thin `os.execute` wrapper that embeds `cd` into the command.
|
||||
5. **Offline mode** – an `OFFLINE` env‑var short‑circuits remote fetches, to make sure we are not stuck e.g. in plane
|
||||
@@ -54,9 +54,7 @@ pub fn (mut repo GitRepo) need_push() !bool {
|
||||
last_remote_commit := repo.get_last_remote_commit() or {
|
||||
return error('Failed to get last remote commit: ${repo.path()}\n${err}')
|
||||
}
|
||||
last_local_commit := repo.get_last_local_commit() or {
|
||||
return error('Failed to get last local commit: ${repo.path()}\n${err}')
|
||||
}
|
||||
last_local_commit := repo.get_last_local_commit()!
|
||||
// If remote commit is empty, it means the branch doesn't exist remotely yet
|
||||
if last_remote_commit.len == 0 {
|
||||
return true
|
||||
@@ -138,6 +136,5 @@ pub fn (self GitRepo) get_last_local_commit() !string {
|
||||
if self.status_local.branch in self.status_local.branches {
|
||||
return self.status_local.branches[self.status_local.branch]
|
||||
}
|
||||
|
||||
return error("can't find branch: ${self.status_local.branch} in local branches:\n${self.status_local.branches}")
|
||||
return ""
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user