banner
The translated version of gitlet

gitlet文档翻译 & 人话版

Scroll down

gitlet的技术文档可能是很多小白第一次接触的长技术文档即使目前有很多市面上的翻译软件阅读起来也并非非常流畅,因此在这里做一个简单的翻译

前置信息

lab6 中提到的重要方法

文件操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
File f = new File("file.txt");
f.createNewFile() // 创建这个文件
f.exist() // 确认文件存在
Utils.writeContents(f, "Hello world"); // 字符串写入文件

// 文件读为字符串
static String readContentAsString(File file);

// 文件读入为字节数组
static byte[] readContents(File file)

//文件路径拼接
static File join(String first, String... others)

目录

1
2
File d = new File("dummy")
d.mkdir()

Java 中目录同样用 File 表示

序列化

我们想要储存一个对象而不单纯是一个字符串的话,可以使用序列化,将Model转换为 String,存入,需要读取时,再进行反序列化解析回 Model

1
2
3
4
import java.io.Serializable;
public class Model implements Serializable {
...
}
序列化
1
2
3
Model m;
File outfile = new File(saveFileName);
writeObject(outFile, m);
反序列化
1
2
3
Model m;
File inFile = new File(saveFileName);
m = readObject(inFile, Model.class);

gitlet介绍

Gitlet实际上就是一个mini版git,将文件不同版本历史记录呈现在一个提交树中

这就是gitlet的文件形态

注意

游离 HEAD 指针

你不会处于一个游离HEAD的状态,也就是这样的

在实际的git中,你可以使用 checkout 或者 reset来实现这个状态,比如

1
2
3
4
5
6
# 将指定file的修改放弃,恢复到当前分支最后一个commit时的状态
git checkout -- filename
# 将 HEAD 指针移动到某个历史提交,这就是 游离 HEAD状态
git checkout <commit_id>
# 移动 HEAD 并且清空暂存区,工作区也恢复到 <commit> 状态
git reset --hard <commit>

提交树

提交树是不可变的,一旦commit节点创建,就无法销毁

几个概念

  • blob: 已经保存的文件内容,实际上就是一个文件的多个版本,一个文件会对应一个或多个blob
  • trees: 一个将文件名对应到相应 blob 的目录结构
  • commit: 一个提交

相对于 git 的简化

  • 不处理子目录
  • 父级最多只有两个,也就是最多只可以由两个父级 merge 而来
  • 元数据仅包含时间戳和日志消息
    • 日志信息
    • 时间戳
    • 文件名到blob引用的映射
    • 两个父级引用(另一个用于merge)

blob的独特性

使用 SHA-1 哈希函数来生成160位的哈希值,所以基本上不可能出现完全一样的哈希值
我们用这个 SHA-1 哈希值来标识每个 blob
我们不需要进行计算,但是需要正确标记我们所有的对象

  • 哈希处理时包括所有的元数据
  • 正确区分 blob 和 commit 的哈希值
    • .gitlet中设计一个目录结构
    • 为每个对象都添加一个额外的哈希,让这个对象的blob和commit分别可以对应一个哈希

建议与规范

类的实现

有两个建议类CommitRepository,我们要实现的是gitlet.Main类,Main应该主要调用Repository中的辅助方法,而不是在Main本身中进行实现

runtime和memory要求

有些命令有runtime或者memory的要求,需要根据要求完成

失败情况

需要根据失败情况打印错误消息,并且不得更改其他内容
立即退出程序:System.exit(0)

一些与命令无关的的失败情况

  • 没有输入参数:打印Please enter a command.然后退出
  • 输入无效命令:打印消息No command with that name exists. 并退出。
  • 输入操作数量或格式错误:印消息 Incorrect operands. 并退出
  • 输入命令要求位于已初始化的Gitlet工作目录(包含.gitlet的目录)但是路径错误:打印消息 Not in an initialized Gitlet directory.

参数响应

init

创建一个初始commit,该commit不包含任何文件,message为init commit,属于一个初始分支master(也是当前分支),时间戳为 00:00:00 UTC, Thursday, 1 January 1970,所有仓库的这个初始 commit 都是一样的(同样的UID)

用法

java gitlet.Main init

失败情况

已存在gitlet的话,打印:A Gitlet version-control system already exists in the current directory.

add

将当前文件的副本添加到暂存区,用新内容覆盖暂存区中的旧内容(版本相同则不进行添加,若被更改后被添加,再次更改回原始版本,则直接从暂存区移除)
如果执行add时文件处于rm状态,则文件不再处于rm状态

用法

java gitlet.Main add [file name]

runtime

$ lgN $

失败情况

文件不存在,打印File does not exist.

注意

一次只能 add 一个文件(与git不同)

commit

保存一个当前commit 和 暂存区中文件的快照,commit只更新已经跟踪且已经被add的文件,当然这个commit中已经跟踪的文件也可能在新commit中被取消跟踪(rm指令删除)
提交后是这样的:

用法

java gitlet.Main commit [message]

注意

  1. commit之后,暂存区清空
  2. 不会对文件做任何改动,除了rm会在commit之后取消对文件的跟踪
  3. 只会commit暂存区的内容
  4. 添加到 commit tree中
  5. 移动HEAD指针指向我们的新commit
  6. 包含日期和时间
  7. 有一个关联的 log message 日志消息
  8. 每个commit 由 SHA-1 进行标识,包含blob引用,夫引用,log message 日志消息和提交时间

失败情况

没有文件被暂存,中止,打印No changes added to the commit.
如果没有 log message,打印Please enter a commit message.

rm

如果文件被暂存,还没有被添加,取消暂存,如果已经被追踪了,将其暂存(便于稍后删除),下次commit的时候这个暂存区中文件就消失了。已经被暂存或者追踪的文件都会直接在工作区中删除(物理上)

用法

java gitlet.Main rm [file name]

失败情况

文件没有被暂存或者追踪的话,打印No reason to remove the file.

log

从当前commit开始,沿着 commit tree 向后追溯,忽略 merge commit 中的第二个父提交

注意

  1. 每个commit前面有个===,后面有一个空行
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    ===
    commit a0da1ea5a15ab613bf9961fd86f010cf74c7ee48
    Date: Thu Nov 9 20:00:05 2017 -0800
    A commit message.

    ===
    commit 3e8bf1d794ca2e9ef8a4007275acf3751c7170ff
    Date: Thu Nov 9 17:01:33 2017 -0800
    Another commit message.

    ===
    commit e881c9575d180a215d1a636545b8fd9abfb1d2bb
    Date: Wed Dec 31 16:00:00 1969 -0800
    initial commit
  2. 每个 commit 条目显示提交对象的唯一 SHA-1 ID
  3. java.util.Datejava.util.Formatter对获取和格式化时间非常有用
  4. 对于 merge commit:在 SHA-1 下方添加一行类似Merge: 4975af1 2c1ead1,两个数字由第一个和第二个父提交 id 的前 7 位组成
    1
    2
    3
    4
    5
    ===
    commit 3e8bf1d794ca2e9ef8a4007275acf3751c7170ff
    Merge: 4975af1 2c1ead1
    Date: Sat Nov 11 12:30:00 2017 -0800
    Merged development into master.

runtime

与历史节点呈线性关系

global-log

类似log,显示所有信息,顺序无关紧要

用法

java gitlet.Main global-log

注意

  1. gitlet.Utils中有一个使用方法,帮助迭代目录中的文件

runtime

与历史提交文件次数呈线性关系

find

找到包含指定提交信息的 id,每行一个
多字信息需要放在引号中

runtime

与commit次数呈现线性关系

失败情况

不存在这样的 commit 的话,打印:Found no commit with that message.

status

显示当前存在的分支,用 * 标记当前分支,显示已暂存和添加的文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
=== Branches ===
*master
other-branch

=== Staged Files ===
wug.txt
wug2.txt

=== Removed Files ===
goodbye.txt

=== Modifications Not Staged For Commit ===
junk.txt (deleted)
wug3.txt (modified)

=== Untracked Files ===
random.stuff

用法

java gitlet.Main status

注意

这几种情况会被视为 “modified but not staged”:

  1. 已被跟踪,被更改,但未 add
  2. 已add,用于添加,但是内容与工作区不同
  3. 已add,用于添加,但是被删掉了
  4. 没有被暂存用于删除,但是已经被跟踪且已经删掉了

runtime

取决于工作目录中的数据量加上要添加或删除的文件数加上分支数

checkout

三种用法

  1. java gitlet.Main checkout -- [file name]
    将当前commit的文件版本覆盖工作区中的文件,且不会被暂存

  2. java gitlet.Main checkout [commit id] -- [file name]
    获取指定的 commit id 的文件版本,覆盖当前工作区的文件

  3. java gitlet.Main checkout [branch name]
    切换分支,将新分支的所有文件覆盖当前工作区,清空暂存区,所有旧分支的被跟踪的文件在新分支中不存在的话,会被清除

runtime

与被checkout的文件呈线性关系
与commit 快照中的文件呈线性关系

失败情况

  1. 文件在之前commit中不存在,打印File does not exist in that commit.
  2. 不存在指定的 commit id,打印
    No commit with that id exists.
  3. 不存在指定名字的branch, 打印No such branch exists.,如果checkout到当前分支,打印No need to checkout the current branch.,如果当前分支有未跟踪的文件,并且该文件被checkout后的文件覆盖,打印There is an untracked file in the way; delete it, or add and commit it first.并退出

branch

创建一个新分支,这个新分支只是指向当前commit的指针而已(SHA-1标识)

用法

java gitlet.Main branch [branch name]

注意

  1. 不会立刻切换到新分支
  2. 调用branch之前,应该在名为master的默认分支上运行

失败情况

给定名称已经存在,打印A branch with that name already exists.

rm-branch rm

删除指定名称的分支指针

用法

java gitlet.Main rm-branch [branch name]

注意

只是删除与该分支关联的指针,而并没有删除 commit tree 中的commit

runtime

常数时间

失败情况

指定分支名称不存在,打印A branch with that name does not exist.
删除当前分支,打印annot remove the current branch. 并 中止

reset

将 HEAD 移动到指定的 commit id,然后删除所有已被跟踪但是在指定commit中不存在的文件,暂存区清空,这里的[commit id]可以直接缩写为checkout

用法

java gitlet.Main reset [commit id]

失败情况

不存在指定的 id,打印No commit with that id exists.,存在未跟踪的文件,而且即将被覆盖,打印There is an untracked file in the way; delete it, or add and commit it first.

注意

很接近git中的git reset --hard [commit hash]

merge

  1. 引入一个概念叫分割点,就是开始出现两个分支的commit
    1. 如果给定的分支与分割点是同一个提交,啥都不做,打印Given branch is an ancestor of the current branch.
    2. 如果分割点是当前分支,打印Current branch fast-forwarded.
  2. 自分割点来,当前branch没修改过,在给定 branch 中修改的,直接覆盖,然后自动暂存
  3. 当前branch修改,给定branch为修改,不动
  4. 相同方式修改,不动(都删除了的话,也不动)
  5. 分割点不存在,存在当前分支的文件,不动
  6. 分割点不存在,存在给定branch的文件,check out 然后暂存
  7. 存在于分割点,当前branch未修改,给定branch不存在的,被删除
  8. 存在于分割点,给定branch未修改,当前branch不存在,也被删除
  9. 不同方式修改,将冲突文件内容替换为:
    1
    2
    3
    4
    5
    <<<<<<< HEAD
    contents of file in current branch
    =======
    contents of file in given branch
    >>>>>>>
    将 “contents of …” 替换为指定文件的内容
    已删除的文件视为空文件,直接连接,如果末尾没有换行符,可能类似于:
    1
    2
    3
    <<<<<<< HEAD
    contents of file in current branch=======
    contents of file in given branch>>>>>>>

用法

java gitlet.Main merge [branch name]

注意

  1. merge之后会自动commit并记录一个log messageMerged [given branch name] into [current branch name].
  2. 如果merge遇到冲入,在终端打印Encountered a merge conflict.

runtime

$O(NlgN + D)$,其中 $N$ 是两个分支的祖先提交的总数,$D$ 是这些提交下的所有文件的总数据量

失败情况

  1. 存在已暂存的添加或删除操作,打印You have uncommitted changes.并退出
  2. 不存在指定名称的分支,打印A branch with that name does not exist.
  3. 将分支与其自身合并,打印Cannot merge a branch with itself.
  4. 合并操作由于 commit 中没有更改而导致错误,则直接让正常的提交错误消息通过
  5. 如果覆盖或删除当前提交中未跟踪的文件,打印There is an untracked file in the way; delete it, or add and commit it first. 并退出
其他文章
cover
二叉树的数组表示推导
  • 25/09/28
  • 08:04
  • 数据结构
cover
Task of 25fall
  • 25/09/26
  • 07:55
  • 杂谈
30+
Posts
8+
Diary
85+
fans
目录导航 置顶
  1. 1. 前置信息
    1. 1.1. lab6 中提到的重要方法
  2. 2. gitlet介绍
    1. 2.1. 注意
    2. 2.2. 几个概念
    3. 2.3. 相对于 git 的简化
    4. 2.4. blob的独特性
  3. 3. 建议与规范
    1. 3.1. 类的实现
    2. 3.2. runtime和memory要求
    3. 3.3. 失败情况
  4. 4. 参数响应
    1. 4.1. init
    2. 4.2. add
    3. 4.3. commit
    4. 4.4. rm
    5. 4.5. log
    6. 4.6. global-log
    7. 4.7. find
    8. 4.8. status
    9. 4.9. checkout
    10. 4.10. branch
    11. 4.11. rm-branch rm
    12. 4.12. reset
    13. 4.13. merge