吃🥮也不忘学git

2,847 阅读3分钟

手写一下git的一些命令

git作为企业开发中比不可以少的一部分,可以说和我们以后的代码生活息息相关。

    git add fileRoute    // 添加指定文件到暂存区
    git commit -m "又要提交什么东东"    //添加指定目录到暂存区,包括子目录
    git push origin master       // 上传至远程云端
    
    git log --oneline       // 查看提交记录
    git checkout -b "branchname"    // 创建分支和切换分支。
    git merge "branchname"      // 合并分支

前三行命令几乎每天都需要敲上几次,可以说都是朋友了,但这个朋友具体是怎么做的呢,今天就来研究一下,有点简陋,敬请谅解。

QQ图片20210818163048.jpg

开搞准备

需要下载git软件,这样才可以使用命令。(使用命令的目的是和手写的做对比)

需要下载node.js软件,这样才可以在命令行运行js文件。

开始咯

  1. 先初始化git, 使用git init --这样在目录下回出现.git文件夹(这是我们本地仓库哦)
  2. 创建一个js文件,所有代码都在这里面,使用原型链的方式实现

Git类

创建一个Git函数,代表一个仓库类,我们可以通过new关键字来创建仓库。

类中有仓库名、版本id(使用变量自增保证不重复)、分支数组、主分支实例...

function Git(name) {
    // 仓库名
    this.name = name;
    // 版本id
    this.lastCommitId = -1;
    // 分支数组
    this.branches = [];
    // 默认是master(主分支)
    this.master = new Branch('master', null);
    this.branches.push(this.master);
    // 头指针
    this.HEAD = this.master;
}
//创建test仓库,将其赋值给repo变量
var repo = new Git("test");

Branch类

分支类,可以创建多个分支,因为master为主分支,不可以随意修改,所以我们需要创建分支来修改和测试。

function Branch(name, commit) {
    // 分支名
    this.name = name;
    this.commit = commit
}

Commit类

image.png

创建Commit类,类中有当前当前版本id、前一个版本parent(版本id)、提交说明

function Commit(id, parent, message) {
    this.id = id;
    this.parent = parent;
    this.message = message;
}

使用原型的方式,使得每一个仓库都可以拥有commit提交功能。

Git.prototype.commit = function (message) {
    var commit = new Commit(++this.lastCommitId, this.HEAD.commit, message)
    // 头指针指向当前新创建的commit类节点
    this.HEAD.commit = commit;
    return commit;
}

repo.commit("hello1")// 需要打印的话使用console.log(repo.commit("hello1"))

这样提交功能就实现了咯

image.png

git log --oneline

同样是原型的用法,使得每一个仓库都可以拥有log查看历史提交记录功能。

这里采用了类似链表的操作,一步一步查找上一个版本

Git.prototype.log = function () {
    var commit = this.HEAD.commit,
        history = [];
    while (commit) {
        history.push(commit);
        commit = commit.parent
    }
    return history
}
// 多添加几个记录
repo.commit("hello2")
repo.commit("hello3")
console.log(repo.log());

image.png

上图返回的是每个提交记录的所有内容,我们正常使用git log --oneline不需要

image.png

所以我们可以创建一个函数来使得他只返回版本id

repo.commit("change1")
repo.commit("change2")
repo.commit("change3")
function historyToIdMapper(history) {
    var ids = history.map(commit => commit.id)
    return ids.join("-")
}

console.log(historyToIdMapper(repo.log()));

image.png

创建分支

同样是原型的用法,使得每一个仓库都可以拥有其他分支,确保master分支不会改变。

我们可以使用git checkout -b "branchname" 命令创建一个新的分支,在checkout函数中,我们手写的过程需要判断一下分支名称是否存在,存在就将头指针指向该分支。使用git checkout "branchname" 可以创建一个新的分支,并且将头指针 移动到当前分支的头部。

Git.prototype.checkout = function (branchName) {
    for (var i = this.branches.length; i--;) {
        if (this.branches[i].name == branchName) {
            console.log('Switched to existing branch: ' + branchName);
            this.HEAD = this.branches[i];
            return this;
        }
    }
    var newBranch = new Branch(branchName, this.HEAD.commit);
    this.branches.push(newBranch);
    this.HEAD = newBranch;
    console.log(`Switched to new branch: ` + branchName);
    // 返回当前分支
    return this;
}


console.log(historyToIdMapper(repo.log()));
repo.checkout("testing")
repo.commit("change4")
console.log(historyToIdMapper(repo.log()));
repo.checkout("master")
console.log(historyToIdMapper(repo.log()));

下图是:在创建分支后提交一次,分别查看记录,然后选择master分支,查看master分支是否受新分支的影响

image.png

合并分支

只写了合并master和一个分支

Git.prototype.Merge = function (branchName){
    for (var i = this.branches.length; i--;) {
        if (this.branches[i].name == branchName) {
            var merge = [], commit = this.branches[i].commit,temp
            while (commit != this.master.commit) {
                merge.unshift(commit)
                commit = commit.parent
            }   
            merge.forEach(val =>{
                temp = this.master.commit
                this.master.commit = val
                this.master.commit.parent =temp
            })
            return this
        }
    }
    console.log(branchName +" branch is not find");
}


//测试
console.log(historyToIdMapper(repo.log()));
repo.checkout("testing")
repo.commit("change4")
console.log(historyToIdMapper(repo.log()));
repo.checkout("master")
console.log(historyToIdMapper(repo.log()));
repo.Merge("testing")
console.log(historyToIdMapper(repo.log()));

测试是在创建了新的分支testing后,然后转到master分支,再调用Merge合并函数,在进行打印结果

image.png

结语

手写不易,请大家给文章点一个,谢谢。笔者在这提前祝大家中秋快乐。

111.jpg