Skip to content
Open
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
37 changes: 19 additions & 18 deletions gitcontroltool/Blob.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
/* ����һ�����⣺��Ҫ�����blob����
@1. ����value����洢�����Ӷ�Ӧ��key-value
step1 �ڹ������½�һ��file���󣬲�д���ļ����ݣ�����̨���롢������д��һ�����е��ļ���
step2 Ϊ���file����������Ϊ���ļ���hashֵ��������
/* 任务一的理解:主要是针对blob对象
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

其实Tree对象也是一样,也是通过key-value接口进行读写的,Tree和Blob本质上没什么不同,都是有自己的value值,然后value的哈希值作为key,只是Blob和Tree的value的具体含义不一样

@1. 给定value,向存储中添加对应的key-value
step1 在工作区新建一个file对象,并写入文件内容(控制台输入、或者是写入一个已有的文件)
step2 为这个file对象重命名为《文件的hash值》并保存

@2. ����key�����ҵõ���Ӧ��valueֵ
�����ȡ���ļ���hashֵ���Ϳ��Եõ��ļ����ݣ���ʱ���ļ���hashֵ������key���ļ����ݾ���value��
@2. 给定key,查找得到对应的value值
如果读取《文件的hash值》就可以得到文件内容,此时《文件的hash值》就是key,文件内容就是value。
*/
package gitcontroltool;
import java.io.*;
// ��װBlob�࣬�������԰���blob��������֣�һ��������������������ȡ������ļ����ݣ�
// 封装Blob类,类中属性包含blob对象的名字,一个输入流变量(用来读取具体的文件内容)
public class Blob {
private String name; // blob���������
FileInputStream input; // ����������
public Blob(String filename) throws Exception { // blob����Ĺ��췽����ԭ���ļ����ݲ��䣬����һ���ļ���Ϊhash��origin's value����blob����
Hash s = new Hash(filename,true); // step1.����һ�����еľ����ļ��������ɶ�Ӧ��hashֵ
this.name = s.getSha(); // step2.Ϊ��ǰblob������hashֵ������
this.input = new FileInputStream(filename); // step3.���������������ɵ�ǰ����ľ����ļ����ɵ�file����ʵ������һ��ֻ���ڹ��췽��������ʵ��
private String name; // blob对象的名字
FileInputStream input; // 输入流变量
public Blob(String filename) throws Exception { // blob对象的构造方法,原有文件内容不变,生成一个文件名为hash(origin's value)的blob对象
Hash s = new Hash(filename,true); // step1.传入一个已有的具体文件,先生成对应的hash值
this.name = s.getSha(); // step2.为当前blob对象赋予hash值做名字
this.input = new FileInputStream(filename); // step3.输入流变量引用由当前传入的具体文件生成的file对象实例,这一步只能在构造方法函数内实现
}

public void createBlob() throws IOException { // д�봫���ļ������ݣ�д�����blob��������ֱ��档
FileOutputStream output = new FileOutputStream(this.name); // �ļ������ڻ��Զ�����
byte[] buffer = new byte[1024]; // �����ַ���������������������д�ٶȡ�
public void createBlob() throws IOException { // 写入传入文件的内容,写入后用blob对象的名字保存。
FileOutputStream output = new FileOutputStream(this.name); // 文件不存在会自动创建
byte[] buffer = new byte[1024]; // 利用字符数组做缓冲器,提升读写速度。
int numRead = 0;
do {
numRead = input.read(buffer);
Expand All @@ -32,13 +32,14 @@ public class Blob {
output.close();
}

public String getkey() { // д�봫���ļ������ݣ�д�����blob��������ֱ��档
public String getkey() { // 写入传入文件的内容,写入后用blob对象的名字保存。
String key = this.name;
return key;
}


@Override
public String toString() { // ��дobject��tostring������
public String toString() { // 重写object的tostring方法。
return "blob " + name;
}
}
37 changes: 19 additions & 18 deletions gitcontroltool/Hash.java
Original file line number Diff line number Diff line change
@@ -1,53 +1,54 @@
package gitcontroltool;
import java.io.*;
import java.security.MessageDigest; // ������ϢժҪ���ṩ��ϢժҪSHA1�㷨
import java.security.MessageDigest; // 导入消息摘要,提供信息摘要SHA1算法


public class Hash {
// ��shaֵ�ļ����װΪһ���࣬�������г�Ա����sha1����ʼshaֵ����outFile������תΪ16���Ƶ��ַ�����
// �ṩ�����ļ����ݣ�blob�����ļ������ַ��������tree����shaֵ����Ͷ�ȡ�������ṩ�ַ�����ת�ַ����ķ���
// 将sha值的计算封装为一个类,该类中有成员属性sha1(初始sha值)和outFile(最终转为16进制的字符串)
// 提供基于文件内容(blob)和文件名或字符串(针对tree)的sha值计算和读取方法、提供字符数组转字符串的方法

private byte[] sha1;
public Hash(String inFile,boolean isFile) throws Exception{ // �������Ķ���ʱ��������Ƿ����ļ�����ʼ����Ӧ�Ķ���˽�г�Ա����
public Hash(String inFile,boolean isFile) throws Exception{ // 构造该类的对象时,会根据是否是文件来初始化相应的对象私有成员属性
if(isFile){
FileInputStream input = new FileInputStream(inFile);
this.sha1 = SHA1content(input); // �����ļ����ݼ���hashֵ
this.sha1 = SHA1content(input); // 利用文件内容计算hash值
}
else{
this.sha1 = SHA1name(inFile); // ���ļ�������hashֵ
this.sha1 = SHA1name(inFile); // 用文件名计算hash值
}
}

public static byte[] SHA1content(InputStream is) throws Exception { // @1.��һ�������ļ���ͨ���ļ����ݼ���shaֵ�ķ�������blob�����keyֵ
byte[] buffer = new byte[1024]; // ����һ���ֽ�������������
MessageDigest complete = MessageDigest.getInstance("SHA-1"); // ָ��SHA1�㷨
public static byte[] SHA1content(InputStream is) throws Exception { // @1.对一个具体文件,通过文件内容计算sha值的方法,即blob对象的key值
byte[] buffer = new byte[1024]; // 采用一个字节数组做缓冲器
MessageDigest complete = MessageDigest.getInstance("SHA-1"); // 指定SHA1算法
int numRead = 0;
do {
numRead = is.read(buffer); // ���������ж�buffer.length���ֽڸ�buffer�����ҷ���ʵ�ʶ�ȡ���ֽ��������ͱ���numRead
numRead = is.read(buffer); // 从输入流中读buffer.length个字节给buffer,并且返回实际读取的字节数给整型变量numRead
if (numRead > 0) {
complete.update(buffer, 0, numRead);
}
} while (numRead != -1); // numRead = -1�����ļ���ȡ��ϡ���Ҫ�ر������������ҷ�����ϢժҪ�ֽ����顣
} while (numRead != -1); // numRead = -1代表文件读取完毕。需要关闭输入流,并且返回消息摘要字节数组。
is.close();
return complete.digest();
}

public static byte[] SHA1name(String s) throws Exception{ // @2.��һ���ļ����ļ���������shaֵ�ķ���
public static byte[] SHA1name(String s) throws Exception{ // @2.对一个文件或文件夹名计算sha值的方法
MessageDigest complete = MessageDigest.getInstance("SHA-1");
int numRead = 0;
complete.update(s.getBytes());
return complete.digest();
}

public String convertToHexString(byte data[]) { // @3.����MessageDigest�����digest()�������ص����ַ����飬Ҫ�õ�ʮ�����Ƶ�shaֵ����ҪתΪ�ַ���
//��tree��value�������ļ������ַ����������ļ��������ַ����������ļ��е�treekey���ַ����������ļ���blobkey���ַ�����
StringBuffer strBuffer = new StringBuffer(); // ��StringBufferȥ�����ɱ��ַ�������
public String convertToHexString(byte data[]) { // @3.由于MessageDigest对象的digest()方法返回的是字符数组,要得到十六进制的sha值还需要转为字符串
//(tree的value包括了文件名(字符串)、子文件夹名(字符串)、子文件夹的treekey(字符串)、子文件的blobkey(字符串)
StringBuffer strBuffer = new StringBuffer(); // 用StringBuffer去建立可变字符串对象
for (int i = 0; i < data.length; i++) {
strBuffer.append(Integer.toHexString(0xff & data[i])); // ��ʮ��������oxff��ij���ֽ�ֵ����λ�����㣬
// ֻ������32λ�����8λ����֤����ת����ʮ�����Ʋ������
strBuffer.append(Integer.toHexString(0xff & data[i])); // 用十六进制数oxff与某个字节值做按位与运算,
// 只保留了32位的最后8位,保证负数转换成十六进制不会出错
}
return strBuffer.toString();
}
public String getSha() { //
return convertToHexString(sha1);
}
}
}
15 changes: 8 additions & 7 deletions gitcontroltool/Test.java
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
package gitcontroltool;
import java.io.*;
// ��Ԫ�������������Ч��
// ������һ���ļ����ݣ���洢�����Ӷ�Ӧ��key-value������key�����ҵõ���Ӧ��valueֵ
// 单元测试类测试最终效果
// 即给定一个文件内容,向存储中添加对应的key-value;给定key,查找得到对应的value值


public class Test {

public static void testValid(String filename){ // testvalid���������Ƿ��������һ����hash��value����keyֵΪ�ļ��������ļ�
// ����һ�������origin�ļ����ƣ�����һ��hash��origin��s value�������ֵ����ļ����ļ�����ͬorigin��
public static void testValid(String filename){ // testvalid函数测试是否可以生成一个以hash(value)即key值为文件名的新文件
// 传入一个具体的origin文件名称,生成一个hash(origins value)做名字的新文件,文件内容同origin。
File file = new File(filename);
try{
Blob blob = new Blob(file.getAbsolutePath());
blob.createBlob();
System.out.println(blob);
}
catch (Exception ex){
ex.printStackTrace(); // �������д�ӡ�쳣��Ϣ�ڳ����г�����λ�ü�ԭ��
ex.printStackTrace(); // 在命令行打印异常信息在程序中出错的位置及原因
}
}

public static void getValue(String path, String encoding) throws IOException {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getValue方法是不是应该整合到功能代码里?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

代码逻辑的拆分有改进空间,最好是拆分出一个类,专门做key-value的get、set接口,文件的读写代码全部封装在这个类里

明白学长的意思!今晚会再做修改。

// ����testValid()�������ɵ�keyֵΪ�ļ������ļ���������getValue����ȥ�õ���Ӧ��value
// 根据testValid()方法生成的key值为文件名的文件,现在用getValue方法去得到对应的value
String content = "";
File file = new File(path);
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), encoding));
Expand All @@ -37,4 +38,4 @@ public static void main(String[] args) throws IOException {
String testfile = "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed";
getValue(testfile,"utf-8");
}
}
}
49 changes: 49 additions & 0 deletions gitcontroltool/任务一详细文档
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
课程项目:
参考git实现原理,使用Java实现blob、tree、commit核心存储结构
功能:
1.Commit:提交commit,可以使用”git log”查看commit历史
2.回滚 使用”git reset” 回滚到指定分支
3.分支 创建分支、实现分支间切换

第一周任务:
(1)文件的key-value:
实现key-value存储(封装为class):
Key:文件名
Value:文件中内容(key根据value计算得出)
功能:
1.给定value,向存储中添加对应的key-value
2.给定key,查找得到对应的value

(2)文件夹的key-value存储:
遍历文件夹目录, 如果遇到子文件则转化为blob并保存
如果遇到子文件夹则递归调用内部的文件夹或文件,转化为blob与tree
并保存

(3)单元测试(unittest)
通过代码自动化的检测key-value存储实现是否正确,确保大量文件进行存储时的效率与正确率。


存储目标分析:
(1)blob文件的value:文件内容
Key:value的hash值
(2)tree文件夹的value:内部blob文件的key、每个子文件夹tree的key、子文件以及子
文件夹的名称
Key:tree文件夹value的hash值

实现设计:
(1)hash类:计算文件blob、文件夹tree的hash值。
-计算字符串(string型)参数的hash方法
-计算file的hash方法
-返回得到的hash值
(2)<优化后>KeyValueObject: blob、tree的父类,子类blob与tree继承自object类
计算key值
创建类型为blob类型的文件
创建类型为tree类型的文件

(3)blob类:
计算该blob文件的key
创建以key命名的blob文件

(4)tree类:
计算该tree文件的key值
以key命名的tree文件