Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions COMPATIBILITY.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "libra"
version = "0.23.55"
version = "0.23.56"
edition = "2024"
license = "MIT"
description = "AI agent-native version control system with Git on-disk compatibility, SQLite-backed metadata, and tiered cloud storage"
Expand Down
5 changes: 3 additions & 2 deletions docs/commands/bundle.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ git clone repository.bundle restored
| List heads | `libra bundle list-heads <f>` | `git bundle list-heads <f>` |
| Import objects | `libra bundle unbundle <f>` | `git bundle unbundle <f>` |

Deferred surfaces are prerequisite/thin/incremental bundle creation and cloning
from a bundle through `libra clone`. `verify` checks checksum integrity but does
Deferred surfaces are prerequisite/thin/incremental bundle creation.
`libra clone <bundle>` reads a Git v2 bundle (directory first, then
`<path>.bundle`, then `<path>`). `verify` checks checksum integrity but does
not build a temporary index to exhaustively decode every pack entry.
10 changes: 9 additions & 1 deletion docs/commands/clone.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ copied to matching `.libraignore` files so Libra ignore rules work immediately.
For bare clones, no working tree checkout is performed and the repository directory itself
becomes the object store. Bare clones do not create `.libraignore`.

A local Git v2 bundle is a valid source: after a repository directory is ruled
out, `clone` tries `<path>.bundle` and then `<path>`. The default destination
name drops a `.bundle` suffix. `HEAD` is taken from the bundle's `HEAD` line;
if that line is missing, the default branch is checked out when the bundle
contains it; otherwise no local branch is created. `--depth` on a plain-path
bundle is ignored with Git's local-clone warning. `remote.origin.url` records
the bundle's absolute path.

## Global Config Schema Guard

Configuration schema compatibility is role-scoped. Before `libra clone` trusts
Expand Down Expand Up @@ -73,7 +81,7 @@ libra clone /path/to/local/repo
### `[LOCAL_PATH]`

Optional destination directory. When omitted, Libra infers the directory name from the
repository URL (e.g., `repo` from `repo.git`). If inference fails, an error is returned
repository URL (e.g., `repo` from `repo.git` or `repo.bundle`). If inference fails, an error is returned
asking the user to specify the path explicitly.

```bash
Expand Down
2 changes: 1 addition & 1 deletion docs/commands/zh-CN/bundle.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,4 @@ git clone repository.bundle restored
| 列出 heads | `libra bundle list-heads <f>` | `git bundle list-heads <f>` |
| 导入对象 | `libra bundle unbundle <f>` | `git bundle unbundle <f>` |

仍延后的 surface:prerequisite/thin/增量 bundle 创建,以及通过 `libra clone` 从 bundle 克隆。`verify` 会校验 checksum,但不会构建临时 index 来穷尽解码每个 pack entry。
仍延后的 surface:prerequisite/thin/增量 bundle 创建`libra clone <bundle>` 已支持读取 Git v2 bundle(先仓库目录,再 `<path>.bundle`,再 `<path>`)。`verify` 会校验 checksum,但不会构建临时 index 来穷尽解码每个 pack entry。
4 changes: 3 additions & 1 deletion docs/commands/zh-CN/clone.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ libra clone [OPTIONS] <REMOTE_REPO> [LOCAL_PATH]

对于裸克隆,不会执行工作树检出,仓库目录本身会直接成为对象存储。裸克隆不会创建 `.libraignore`。

本地 Git v2 bundle 可作为源:排除仓库目录后依次尝试 `<path>.bundle` 与 `<path>`。默认目标目录名去掉 `.bundle` 后缀。`HEAD` 取自 bundle 的 `HEAD` 行;没有该行时,若 bundle 含默认分支则检出它,否则不创建本地分支。普通路径上的 `--depth` 会按本地克隆警告忽略。`remote.origin.url` 记录 bundle 的绝对路径。

## 全局配置 Schema 保护

配置 schema 兼容性按角色判定。`libra clone` 在信任配置前,以只读方式检查 GlobalConfig 与 SystemConfig 元数据。真正的配置 future schema,或未注册/名称不匹配的迁移 receipt,在命令需要该作用域时以 `LBR-CONFIG-001` fail-closed。当前 manifest 已知的 Repository-only receipt(包括 `2026090801`)不会使配置库被误判为 future,受支持的配置值仍可读取。本 build 能识别 configuration-owned legacy-reader barrier;详见[配置兼容性](config.md#配置-schema-兼容性)。
Expand All @@ -42,7 +44,7 @@ libra clone /path/to/local/repo

### `[LOCAL_PATH]`

可选目标目录。省略时,Libra 会从仓库 URL 推断目录名(例如从 `repo.git` 推断 `repo`)。如果无法推断,会返回错误,要求用户显式指定路径。
可选目标目录。省略时,Libra 会从仓库 URL 推断目录名(例如从 `repo.git` 或 `repo.bundle` 推断 `repo`)。如果无法推断,会返回错误,要求用户显式指定路径。

```bash
libra clone git@github.com:user/repo.git my-dir
Expand Down
4 changes: 2 additions & 2 deletions docs/development/commands/bundle.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
- create:explicit revisions + `--all`/`--branches`/`--tags`,完整 non-thin bundle。
- verify/list-heads:v2 header、prerequisite、PACK v2、完整 trailer checksum(list-heads 只读 header)。
- unbundle:验证并安装 pack/index,打印 heads,不更新 refs,与 Git unbundle 的消费边界一致。
- 延后:prerequisite/thin/incremental range create、`libra clone <bundle>`、verify 全 entry decode
- 延后:prerequisite/thin/incremental range create、verify 全 entry decode。 `libra clone <bundle>` 已实现(issues/474 CL-09)

## 设计方案

Expand Down Expand Up @@ -38,7 +38,7 @@
| 类别 | 未完成项 | 当前处理 |
|---|---|---|
| create | prerequisite/thin/`A..B` incremental | 延后;只写 full bundle。 |
| clone | Libra clone-from-bundle | 先 unbundle + explicit update-ref,或 system Git clone。 |
| clone | (已实现)`libra clone <bundle>` | 先判定仓库目录,再 `<path>.bundle` / `<path>`;HEAD 选择见 ADR-CL-06。 |
| verify | exhaustive pack entry decode | checksum + version/prerequisite;unbundle 建 index 时执行更深验证。 |
| scale | >1 GiB bundle | 明确拒绝。 |

Expand Down
24 changes: 12 additions & 12 deletions docs/development/plan/issues/474.md
Original file line number Diff line number Diff line change
Expand Up @@ -1206,7 +1206,7 @@ ER-CL-02 接入 / 豁免清单(2026-09-23):

**Task type:** `implementation`

**Lifecycle / Acceptance:** `pending` /
**Lifecycle / Acceptance:** `in-progress` / `locally-accepted`

**Description:** 按 ADR-CL-06 第 1、2 条,新增 bundle 源识别(仓库目录优先,再尝试 `<path>.bundle` 与 `<path>`)与 bundle 读取客户端,复用 `bundle.rs` 的校验与 pack 安装,按 bundle heads 建立跟踪 ref、tag 与检出分支。唯一行为轴是「从 bundle 文件克隆」。

Expand All @@ -1223,20 +1223,20 @@ ER-CL-02 接入 / 豁免清单(2026-09-23):

**Acceptance criteria:**

- [ ] 判据矩阵 M-BUNDLE U1–U2(识别与不存在)。
- [ ] `HEAD` 选择通过 M-BUNDLE U3–U4。
- [ ] `--bare` 与 `--depth` 组合通过 M-BUNDLE U5(期望已写回 ADR-CL-01)。
- [ ] 前置提交缺失通过 M-BUNDLE U6。
- [ ] 损坏输入通过 M-BUNDLE U7,失败零残留(GC-CL-04)。
- [ ] 远程配置与状态通过 M-BUNDLE U8。
- [ ] ER-06a 同步:`docs/commands/clone.md`、`docs/commands/bundle.md` 与 zh-CN、`docs/development/commands/bundle.md:41` 改为已实现、`COMPATIBILITY.md:165`、`:207` 原行更新、网站 `clone.en.md`、`bundle.en.md`。
- [x] 判据矩阵 M-BUNDLE U1–U2(识别与不存在)。
- [x] `HEAD` 选择通过 M-BUNDLE U3–U4。
- [x] `--bare` 与 `--depth` 组合通过 M-BUNDLE U5(期望已写回 ADR-CL-01)。
- [x] 前置提交缺失通过 M-BUNDLE U6。
- [x] 损坏输入通过 M-BUNDLE U7,失败零残留(GC-CL-04)。
- [x] 远程配置与状态通过 M-BUNDLE U8。
- [x] ER-06a 同步:`docs/commands/clone.md`、`docs/commands/bundle.md` 与 zh-CN、`docs/development/commands/bundle.md:41` 改为已实现、`COMPATIBILITY.md:165`、`:207` 原行更新、网站 `clone.en.md`、`bundle.en.md`。

**Verification:**

- [ ] `source .env.test && source .env.live-test && cargo nextest run --lib command::fetch`(new:bundle 源识别单测)
- [ ] `source .env.test && source .env.live-test && cargo nextest run --test command_test clone_test`(new:`test_clone_from_bundle_matrix`,含 `t5605:75-107` 移植)
- [ ] `source .env.test && source .env.live-test && cargo nextest run --test command_test bundle_test`(回归)
- [ ] `source .env.test && source .env.live-test && cargo nextest run --test compat_ledger_schema`
- [x] `source .env.test && source .env.live-test && cargo nextest run --lib command::fetch`(new:bundle 源识别单测)
- [x] `source .env.test && source .env.live-test && cargo nextest run --test command_test clone_test`(new:`test_clone_from_bundle_matrix`,含 `t5605:75-107` 移植)
- [x] `source .env.test && source .env.live-test && cargo nextest run --test command_test bundle_test`(回归)
- [x] `source .env.test && source .env.live-test && cargo nextest run --test compat_ledger_schema`

**Full-suite trigger:** `T-1: 在共享的 remote 源识别(fetch::discover_remote)中新增源类型`

Expand Down
7 changes: 4 additions & 3 deletions docs/development/plan/plan-status.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
|---|---|---|---|
| [`issues/470.md`](issues/470.md) | 工作树物化丢失可执行位与 mode 变化检测 | 未启动 | FM-01/02/05(3 卡) |
| [`issues/473.md`](issues/473.md) | `init` 与 Git 对齐 | 未启动 | IN-01..IN-12(12 卡) |
| [`issues/474.md`](issues/474.md) | clone 浅克隆完整性、bundle 源、bare 与 mirror 对齐 | 实施中 | CL-01 `done`/`complete`(`v0.23.47`);CL-02 `done`/`complete`(`v0.23.48`);CL-03 C 组 `v0.23.49`(`compat-network-remotes` 重跑排队中);CL-04..CL-08 `locally-accepted`(未 bump) |
| [`issues/474.md`](issues/474.md) | clone 浅克隆完整性、bundle 源、bare 与 mirror 对齐 | 实施中 | CL-01 `done`/`complete`(`v0.23.47`);CL-02 `done`/`complete`(`v0.23.48`);CL-03 C 组 `v0.23.49`(`compat-network-remotes` 重跑排队中);CL-04..CL-09 `locally-accepted`(未 bump) |
| [`issues/475.md`](issues/475.md) | `config` Git 兼容参数层对齐 | 未启动 | CF-01..CF-15(15 卡) |
| [`issues/476.md`](issues/476.md) | 工作树命令族与 Git 对齐 | **实施中** | WT-02 `v0.23.29` / WT-04 `v0.23.30` / WT-08 `v0.23.31` / WT-09 `v0.23.32` / WT-10 `v0.23.33` / WT-11 `v0.23.34` / WT-01 `v0.23.35`(`done`/`remote-pending`);WT-03 受 DEP-WT-08 阻塞;intent-to-add 已迁至 plan-20260918 |
| [`issues/477.md`](issues/477.md) | 历史改写命令族与 Git 对齐 | **已收口** | HF-01..HF-31(31 卡)全 `done/complete`,聚合发布 v0.22.49;子 issue #495 |
Expand Down Expand Up @@ -183,7 +183,8 @@
| **CL-06 普通路径忽略浅化参数** | **`in-progress`/`locally-accepted`** | **未 bump(等 CL-05 发布窗口)** |
| **CL-07 shallow Git 源克隆** | **`in-progress`/`locally-accepted`** | **未 bump(等 CL-06 发布窗口)** |
| **CL-08 bundle create 写入 HEAD** | **`in-progress`/`locally-accepted`** | **未 bump(等 CL-07 发布窗口)** |
| CL-09..CL-15 | `pending` | 未 bump |
| **CL-09 clone 接受 bundle 源** | **`in-progress`/`locally-accepted`** | **未 bump(等 CL-08 发布窗口)** |
| CL-10..CL-15 | `pending` | 未 bump |

### 3.6 issues/476(工作树命令族)

Expand Down Expand Up @@ -225,7 +226,7 @@ SBX-01..05 `done/locally-accepted`;**发布步按 DEFER-SBX-06 正式延后**

- **当前正在执行:** `plan-20260907` B3-00(发布者:本会话执行该卡的 Agent)。`plan-20260925` SCAP-01 已 `remote-pending`(PR #519 / `v0.23.55`,D 组异步)。
- **SCAP-01 与 B3-00:** SCAP-01 的 C 组已落地,`runtime.rs` 写集互斥放开;B3-00 可开工。
- **下一步(SCAP D 组落地后):** 把 SCAP-01 / SCAP-02 标成 `done` / `complete`。`issues/474` 的 `CL-04`..`CL-08` 仍按窗口排队
- **下一步(SCAP D 组落地后):** 把 SCAP-01 / SCAP-02 标成 `done` / `complete`。`issues/474` 的 `CL-09`..`CL-15` 正按窗口发布(不等待 D 组 CDN)
- **并行窗口(不在本执行指针):** `issues/476` WT-03 仍等 DEP-WT-08;`plan-20260918` 其余 add 卡、`plan-20260819` M2 仍登记为实施中,但不抢本卡的 `fsck.rs` 写集。

---
Expand Down
2 changes: 1 addition & 1 deletion install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ $ErrorActionPreference = "Stop"
# One of the release version surfaces. `compat_version_surface_sync` pins it
# to Cargo.toml: this value is substituted verbatim into the download URL, so
# a stale value silently installs an old binary when -Version is not given.
$DefaultVersion = "v0.23.55"
$DefaultVersion = "v0.23.56"
# Public-only trust anchor for stable-manifest verification. It deliberately
# has no environment override: the install-smoke harness rewrites these
# clearly-marked constants in a temporary COPY of this script.
Expand Down
2 changes: 1 addition & 1 deletion install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ INSTALL_DIR="${LIBRA_INSTALL_DIR:-$LIBRA_HOME/bin}"
# user opts in with LIBRA_ALLOW_FALLBACK=1. Default behaviour is fail-fast so
# offline installs cannot silently regress to a stale version. Bump this on
# every release so the opt-in fallback remains useful.
DEFAULT_VERSION="v0.23.55"
DEFAULT_VERSION="v0.23.56"
# Public-only trust anchor for stable-manifest verification. It deliberately
# has no environment override: the install-smoke harness rewrites these
# clearly-marked constants in a temporary COPY of this script, never through
Expand Down
17 changes: 9 additions & 8 deletions src/command/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -557,11 +557,12 @@ async fn encode_pack(entries: Vec<Entry>) -> CliResult<Vec<u8>> {
// ----------------------------------------------------------------------------

/// The parsed text header of a bundle.
struct BundleHeader {
prerequisites: Vec<(String, String)>,
heads: Vec<(String, String)>,
#[derive(Debug, Clone)]
pub(crate) struct BundleHeader {
pub(crate) prerequisites: Vec<(String, String)>,
pub(crate) heads: Vec<(String, String)>,
/// Byte offset where the pack begins (just after the blank line).
pack_offset: usize,
pub(crate) pack_offset: usize,
}

fn verify(file: &Path) -> CliResult<()> {
Expand Down Expand Up @@ -747,7 +748,7 @@ fn files_equal(left: &Path, right: &Path) -> std::io::Result<bool> {
}
}

fn read_bundle_bounded(file: &Path, exit_code: i32) -> CliResult<Vec<u8>> {
pub(crate) fn read_bundle_bounded(file: &Path, exit_code: i32) -> CliResult<Vec<u8>> {
let input = fs::File::open(file).map_err(|error| read_err(error, exit_code))?;
let size = input
.metadata()
Expand Down Expand Up @@ -779,7 +780,7 @@ fn read_bundle_bounded(file: &Path, exit_code: i32) -> CliResult<Vec<u8>> {
Ok(bytes)
}

fn verify_prerequisites(header: &BundleHeader, exit_code: i32) -> CliResult<()> {
pub(crate) fn verify_prerequisites(header: &BundleHeader, exit_code: i32) -> CliResult<()> {
let storage = util::objects_storage();
let mut missing = Vec::new();
for (oid, _) in &header.prerequisites {
Expand All @@ -799,7 +800,7 @@ fn verify_prerequisites(header: &BundleHeader, exit_code: i32) -> CliResult<()>
.with_stable_code(StableErrorCode::CliInvalidTarget))
}

fn validate_bundle_pack(pack: &[u8], exit_code: i32) -> CliResult<ObjectHash> {
pub(crate) fn validate_bundle_pack(pack: &[u8], exit_code: i32) -> CliResult<ObjectHash> {
let hash_len = get_hash_kind().size();
if pack.len() < 12 + hash_len || &pack[0..4] != b"PACK" || pack[4..8] != [0, 0, 0, 2] {
return Err(
Expand Down Expand Up @@ -828,7 +829,7 @@ fn validate_bundle_pack(pack: &[u8], exit_code: i32) -> CliResult<ObjectHash> {
}

/// Parse the text header up to the blank line that precedes the pack.
fn parse_header(bytes: &[u8], exit_code: i32) -> CliResult<BundleHeader> {
pub(crate) fn parse_header(bytes: &[u8], exit_code: i32) -> CliResult<BundleHeader> {
// A malformed bundle is a verification failure (exit 1), matching
// `git bundle verify` — exit 128 is reserved for usage errors.
let invalid = |message: &str| {
Expand Down
Loading
Loading