Skip to content

[libra] 实现 git reflog 命令 #1354

Description

@mon3stera

1.1 任务描述

reflog 是 git 的一个核心的“后悔药”命令。它记录了本地仓库中引用(如 HEAD 和分支)的历史变更。当用户执行了可能丢失提交的操作(如 reset 或错误的 rebase),reflog 可以帮助用户轻松找回这些提交。它是 git 用户广泛依赖的功能。

1.2 实现范围

  • 范围内:
    1. 实现 libra reflog 主命令,展示 HEAD 的变更日志。
    2. 在所有会修改 HEAD 或分支引用的命令中,集成日志记录逻辑。
    3. 输出格式与标准 git reflog 默认格式保持相似。
  • 可能的范围外:
    1. reflog 的高级子命令和选项,如 delete, expire 等。
    2. 复杂的输出格式化选项(如 --date)等。
    3. reflog 记录的自动过期清理机制。

2.1 核心命令

  • 功能:按时间倒序打印出 HEAD 引用的所有历史变更记录。
  • 输出格式:<new_old_short> HEAD@{<index>}: <action>: <message>
  • <new_oid_short>:变更后引用所指向的 Commit OID (SHA-1) 的短哈希。
  • HEAD@{<index>}reflog 选择器,{0} 代表最新的一次变更,{1} 是上一次,以此类推。
  • <action>:描述本次变更是由什么类型的操作触发的。
  • <message>:对本次变更的详细描述。
a1b2c3d HEAD@{0}: commit: Add user authentication feature
f8e7a6d HEAD@{1}: reset: moving to HEAD~1
c9d0e1f HEAD@{2}: switch: moving from main to feature-branch
...

2.2 日志记录的触发命令和消息格式

  • commit -> commit message 的第一行。
  • switch -> moving from <old_branch> to <new_branch>
  • reset -> moving to <target_ref>
  • merge -> merge <branch_name>:<merge_strategy>
  • rebase -> rebase finished: returning to <branch_name>
  • pull -> pull: <merge/rebase_details>
  • clone -> clone: from <repository_url>

3. 实现方案

libra 使用 sqlite 来存储一些元数据,可以新建一张表存储所有引用变更日志:

CREATE TABLE reflogs (
    id              INTEGER PRIMARY KEY AUTOINCREMENT,
    ref_name        TEXT NOT NULL,        -- 变更的引用名,如 'HEAD', 'refs/heads/main'
    old_oid         TEXT NOT NULL,        -- 变更前的 OID (SHA-1)
    new_oid         TEXT NOT NULL,        -- 变更后的 OID (SHA-1)
    committer_name  TEXT NOT NULL,        -- 操作者的名字 (从 config 读取)
    committer_email TEXT NOT NULL,        -- 操作者的邮箱 (从 config 读取)
    timestamp       INTEGER NOT NULL,     -- 操作发生时的 Unix 时间戳
    action          TEXT NOT NULL,        -- 操作类型,如 'commit', 'reset'
    message         TEXT NOT NULL         -- 详细描述信息
);

可以在 ref_nametimestamp 上面创建索引。

然后,需要一个统一的,可以复用的函数来处理日志写入,用一个枚举来表示 reflog

pub enum ActionDetail<'a> {
    Commit { message: &'a str },
    Reset { target: &'a str },
    Switch { from: &'a str, to: &'a str },
    Merge { branch: &'a str },
    Pull, // 待定
    Clone { from: &'a str },
}

pub enum ReflogContext<'a> {
	pub old_oid: &'a str,
	pub new_oid: &'a str,
	pub detail: ActionDetail<'a>,
}

更新变更日志有两种方案:

  1. 实现一个全新的记录修改日志的函数:
pub fn log_ref_change(
	db: &DbConn,
	ref_name: &str, 
	context: &ReflogContext<'_>,
)
  1. 可以考虑集成到 update_branch 函数里面:
pub async fn update_branch(
	branch_name: &str, 
	commit_hash: &str, 
	remote: Option<&str>, 
	context: &ReflogContext<'_>,
) 

因为这些操作涉及到多张表的同时变更,所以还需要开启一个事务来保证 ACID。

最后,实现 reflog 的命令行参数。

4. 测试

因为 reflog 涉及到很多其他指令的行为变更,所以还需要为受到影响的指令也进行测试。

Metadata

Metadata

Assignees

Labels

Type

Fields

No fields configured for Task.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions