CS61B学习笔记——proj2 gitlet的实现
记录一下做gitlet的思路。方便后期对项目进行重构和优化~Gitlet顶层设计 -.gitlet存储一切 -objects 存储commit和blob对象(使用hashcode作为文件名) -commits 存储每个commit对象 -blobs 存储每个blob对象 -refs -heads 存储分支末端 -remotes 远端分支(unfinish
记录一下做gitlet的思路。方便后期对项目进行重构和优化~
Gitlet顶层设计
-.gitlet 存储一切
-objects 存储commit和blob对象(使用hashcode作为文件名)
-commits 存储每个commit对象
-blobs 存储每个blob对象
-refs
-heads 存储分支末端(文件名为分支名,内容为对应commit的hashCode)
-remotes 远端分支(unfinished)
-HEAD 存储当前branch的name,默认为master
-staging 存储缓存区内容(以Stage class的形式存储blob)
-removedStage 存储被rm的文件(以Stage class的形式存储)
Class定义
Commit
实例变量
String message;
Date curTime;
List<String> parentHashCodes; // 第一个code存储的是当前分支的parent
List<String> blobCodes; // 使用hashCode数组表示对应的blob
method
public String getMessage();
public Date getCurTime();
public List<String> getParentsHashCode();
public List<String> getBlobCodes();
功能
记录用户每次commit的msg,时间信息,并对相关文件进行跟踪。每个commit实例都是commit tree上的一个节点。
Blob
实例变量
String refs; // 存储的是相对路径
byte[] content;
method
public String getRefs();
public byte[] getContent();
功能
将文件的ref和content以blob的形式存起来,方便加入staging area和被commit跟踪。
Stage
实例变量
List<Blob> blobs; // 存储stage for add or removal的blob
method
public List<Blob> getBlobs();
public void add(Blob blob);
public void add(List<Blob> addBlobs);
功能
将stage for add or removal的文件以stage的形式存储,并写入相应的文件中(这个可以优化,实际上只要一个文件就够了)。
Command
init 思路
init
创建必要的文件夹;
初始化initial commit,并将其存入commits文件夹。
失败的情况:
如果有.gitlet文件存在,输出错误信息:
A Gitlet version-control system already exists in the current directory.
commit的格式如下:
它应该包含hashcode,date和message(merge commit比较特别)
add 思路
add [file name]
将需要stage for add的文件以blob的形式存入staging中;
如果file和当前commit中跟踪的文件相同(blob的hashCode相同),则不将其添加到staging中;
失败的情况:
如果该文件不存在,输出错误信息:
File does not exist.
commit 思路
commit [message]
实例化一个新的commit对象,blobCodes复制headCommit中的内容;
更新msg和parentCodes;
根据staging中的blobs更新/增加commit跟踪的对象;
根据removedStage中的blobs删除commit跟踪的对象;
将commit存入commits文件夹中;
让当前branch指向新的commit;
清空缓存区(删除staging和removedStage文件);
失败的情况:
如果缓存区中没有内容(staging和removedStage不存在),输出错误信息:
No changes added to the commit.
如果message为空,输出错误信息:
Please enter a commit message.
rm 思路
rm [file name]
如果文件在stage for add区域,则将其中缓存区删除;
如果文件被当前commit跟踪,则将其存入stage for removal区域。如果该文件存在于工作目录中,就将其删除;
失败的情况:
如果该文件既不在stage for add区域,也不被当前commit跟踪,输出错误信息:
No reason to remove the file.
log 思路
log
从当前commit开始,依次回溯打印commit信息,直到initial commit;
如果有多条支路(比如merge commit),选择first parent commit进行打印,忽略另外一条支路。体现在commit的parentHashCodes中,第一个存储的便是first parent;
merge commit比较特别,需要多打印一行;
失败的情况:
无
global-log 思路
global-log
使用gitlet.Utils中的method获取commits文件夹中所有的commit,打印即可,不关心顺序;
失败的情况:
无
find 思路
find [commit message]
打印所有符合message的commit id,如果有多个结果,一个一行;
遍历commit即可,和global-log的思路类似,不过要筛选一下;
失败的情况:
如果没有符合条件的commit存在,输出错误信息:
Found no commit with that message.
status 思路
status
打印现存的分支名称,读取heads中文件的名称即可;
在当前分支的前面加上*号;
打印缓存区的文件;
打印被删除的文件名称;
最后两项是extra credit的内容,现在不用管,暑假有空再看看吧;
失败的情况:
无
checkout 思路
check out命令有三种使用场景:
1.checkout – [file name]
如果文件被当前commit所跟踪,则其放入工作目录中(如果工作目录中有同名文件,则替代它);
失败的情况:
文件并不被当前commit跟踪,输出错误信息:
File does not exist in that commit.
2.checkout [commit id] – [file name]
和1很类似,不过换成对应id的commit
失败的情况:
commit不存在,输出错误信息:
No commit with that id exists.
文件不被对应commit跟踪,输出错误信息:
File does not exist in that commit.
3.checkout [branch name]
checked branch和当前commit所跟踪的文件可以分为三类:
仅被当前commit跟踪(删除文件)
被两个commit共同跟踪(用checked branch中的blobs覆写这些文件)
仅被checked branch跟踪;
仅被checked branch跟踪的文件又可以分为两类:
不存在于当前工作目录(覆写)
已经存在于当前工作目录的文件(打印错误信息)
清空缓存区(删除对应的文件即可)
失败的情况:
如果checked branch不存在,输出错误信息:
No such branch exists.
如果checked branch就是当前分支,输出错误信息:
No need to checkout the current branch.
如果工作目录中存在仅被checked branch跟踪,且将要被覆写的文件,输出错误信息:
There is an untracked file in the way; delete it, or add and commit it first.
branch 思路
branch [branch name]
在heads文件夹中创建新的branch,内容为当前commit的hashCode;
失败的情况:
无
rm-branch 思路
rm-branch [branch name]
删除heads文件夹中对应name的文件即可;
失败的情况:
如果给定的branch不存在,输出错误信息:
A branch with that name does not exist.
如果尝试删除的branch为当前branch,输出错误信息:
Cannot remove the current branch.
reset 思路
reset [commit id]
相当于check out到对应的commit;
复用check out中的代码;
将当前分支存储的内容改为对应commit的id;
清空缓存区(删除对应的文件);
失败的情况:
如果没有对应的commit存在,输出错误信息:
No commit with that id exists.
如果工作目录存在仅被reset commit跟踪,且将被覆写的文件,输出错误信息:
There is an untracked file in the way; delete it, or add and commit it first.
merge 思路
merge [branch name]
这个是整个项目中最复杂的命令了,推荐观看intro视频梳理思路。
失败的情况:
如果缓存区还有blob(文件存在),输出错误信息:
You have uncommitted changes.
如果给定的branch不存在,输出错误信息:
A branch with that name does not exist.
如果给定的branch和当前branch相同,输出错误信息:
Cannot merge a branch with itself.
如果工作目录存在仅被merge commit跟踪,且将被覆写的文件,输出错误信息:
There is an untracked file in the way; delete it, or add and commit it first.
总结
前后花了大概一周左右的时间,兜兜转转总算做完了基础的部分。
收获
复习了cs61b前期所学的大部分知识,coding能力有了提升;
第一个可以被写在简历上的项目(后期可能得优化一下~);
开始的时候一片空白,无从下手,充满恐惧;完成之后则是满满的成就感。
不足
设计和理解存在不足——在动手coding之前没有充分的准备,对项目的规划不足,有很多地方都不理解就开始动手了,导致后期经常更改前面的代码,浪费了很多时间;
项目中使用的数据结构没有事先规划好,大量无脑使用List,浪费很多时间;
merge寻找splitPoint的算法还存在可以改进的点,现在使用的时间复杂度太高了(可以使用BFS来做);
在定义函数前仔细考虑,检查是否有同样功能的函数已经被定义了,不要重复造轮子;
使用check,is,get,find等关键字细分函数的功能;
Java泛型和数组(有空看一下)。
严重依赖autograder,20分钟的等待时间太漫长啦~
未来
继续刷完cs61b吧,暑假有空可以再回顾一下gitlet,挺好的。
更多推荐
所有评论(0)