This commit is contained in:
2025-07-21 13:20:24 +02:00
parent 55794a208c
commit 02c4229116
2 changed files with 116 additions and 5 deletions

View File

@@ -0,0 +1,114 @@
# GitTools Module Architecture
## 1. Purpose
GitTools is a lightweight Gitoriented service layer written in VLang.
It sits **between** higherlevel application code (CLI tools, DevOps scripts, GUIs) and the Git executable, offering:
* **Repository discovery & lifecycle** under a single *coderoot* directory
* **Highlevel 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, SSHkey setup) to smooth local development workflows.
---
## 2. HighLevel 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 inmemory map of `&GitRepo` and persists metadata in Redis (`git:<coderoothash>` keys).
* **GitRepo** wraps a single workingtree and exposes both **mutating** commands (`commit`, `push`, `pull`, …) and **informational** queries (`need_commit`, `get_changes_*`).
* **GitLocation** is a purevalue 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 inmemory map **and** Redis; cloning is performed ondemand.
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 CLIlike arguments and delegates to each selected repo.
---
## 3. FilebyFile 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, SSHkey handling. |
| **repository\_load.v** | Pulls reality into memory (`git fetch`, branch/tag maps) and maintains the `last_load` timestamp. |
| **repository\_info.v** | Highlevel 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 threadsafe.
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` envvar shortcircuits remote fetches, to make sure we are not stuck e.g. in plane

View File

@@ -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 ""
}