Git是什么呢?

Git是目前最先进最好用的分布式版本控制系统。
Git官网

分布式?

关于分布式,以及教程,我觉得廖老师写的很好,这是今天的链接:Git教程

Git的原理

Alt text

Git里面分为工作区和版本库,版本库里面的index就是暂存区stage,master是分支master所对应的master分支目录树。HEAD是当前仓库指针,当进行git add时,是将工作区修改添加到暂存区,通过commit操作将暂存区内容提交到master分支,reset HEAD其实是将master分支回滚到暂存区,所以工作区是被保留的,不会被回滚的。当本地修改后,没有做任何提交,可以通过checkout -- <file>丢弃修改。rm --cached是从暂存区删除文件,工作区的文件不会被删除,工作区里面的文件需要进行commit才能被删除掉,也就是提交到master和stage里面。checkout HEAD <file>就是用HEAD指向的分支里面的文件替换暂存区和工作区,这个动作比较危险。

记录一下常见的操作吧

创建一个仓库

1
2
3
$ mkdir git_test // 创建一个文件夹
$ cd git_test
$ git init // 初始化该仓库

这样我们可以看到文件夹下自动生成的隐藏的git文件

1
2
3
4
5
6
$ ll -a
total 8
drwxr-xr-x 4 lvxiaojiao staff 128B 11 24 15:16 .
drwxr-xr-x 63 lvxiaojiao staff 2.0K 11 24 14:39 ..
drwxr-xr-x 15 lvxiaojiao staff 480B 11 24 15:31 .git

本地提交一个修改

1
$ vim demo1.js

编辑并新建一个js文件,输入内容,比如某些开发内容

1
2
3
function sayHello() {
console.log('产品ABC');
}

ok,可以使用git status查看一下当前的git状态。

1
2
3
4
5
6
7
8
9
10
$ git status
// 红色文字提示的修改
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: demo1.js
no changes added to commit (use "git add" and/or "git commit -a")

那么怎么使用git来提交代码呢?第一步,将修改内容添加到暂存区

1
$ git add demo1.js

可以使用git status查看一下当前的git状态

1
2
3
4
5
6
7
$ git status
// 绿色文字提示的暂存区内容
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: demo1.js

第二步,提交暂存内容

1
$ git commit -m 'add demo1.js'

到了这一步,已经完成了将新建的demo1.js文件提交到本地master主干(自动生成的master分支)

本地重置一个修改

如果目前需要我们去修改代码,然而,还没等提交,PM又取消了刚刚的需求修改。比如,在工作区,我们对demo1.js文件进行如下修改

1
$ vim demo1.js

1
2
3
4
function sayHello() {
console.log('产品ABC');
console.log('需求1');
}
1
$ git add demo1.js

ok,如之前的步骤一样,我们将新的修改放入暂存区,这时候需求变更需要回滚刚刚的修改

1
2
3
$ git reset --hard 46ee765 // 版本号前几位
HEAD is now at 46ee765 add demo1.js

这个时候再次打开文件的时候,已经恢复到之前的干净版本

1
2
3
4
$ cat demo1.js
function sayHello() {
console.log('Hello!');
}

廖老师的教程里面说的非常详细,Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD指针,当你回退版本的时候,Git仅仅是把HEAD从指向回退的位置,然后顺便把工作区的文件更新了。所以你让HEAD指向哪个版本号,你就把当前版本定位在哪。那如果PM又说刚刚的修改还是保留吧,但是之前的commit id忘了怎么办?
在Git中,总是有后悔药可以吃的。当你用$ git reset --hard HEAD^回退到“产品ABC”版本时,再想恢复到“需求1”,就必须找到“需求1”的commit id。Git提供了一个命令git reflog用来记录你的每一次命令:

1
$ git reflog

总结一下:
HEAD指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令git reset --hard commit_id
穿梭前,用git log可以查看提交历史,以便确定要回退到哪个版本。
要重返未来,用git reflog查看命令历史,以便确定要回到未来的哪个版本

重置以后,PM提出正确的需求,我们进行修改和提交

1
2
3
4
function sayHello() {
console.log('产品ABC');
console.log('需求2');
}

1
2
$ git add demo1.js
$ git commit -m '需求2'

这样就又一次提交了代码的变更,然而,此时PM发现刚才提出的需求是没用的,需要直接干掉。
如果使用sourceTree,可以选中“产品ABC”的那次提交,选中将master提交到这次提交,这时发现“需求2”的修改已经回到未暂存区(此时文件的本地修改是存在的),选中该文件选择丢弃文件,然后“需求2”的修改被重置到之前的状态“产品ABC”
如果使用git命令,可以:

1
2
3
$ git reset 46ee7653486d8456d3b96a27618ce3517fce7da0 demo1.js // git reset HEAD demo1.js产品ABC的提交
Unstaged changes after reset:
M demo1.js

reset之后,此时本地的修改回退到工作区,不加–hard的reset是回滚代码,并保留之前的修改,使用hard是仓库和暂存区都进行回滚。如果想丢弃修改,可以这样

1
$ git checkout -- demo1.js // 如果git checkout .则丢弃所有修改

这样就回到干净的“产品ABC”提交了。此时PM提出这个需求全部作废,那么需要干掉demo1.js文件,可以

1
2
3
4
5
6
7
8
$ git rm demo1.js // 如果git checkout .则丢弃所有修改
rm 'demo1.js'
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
deleted: demo1.js

此时,工作区的文件已经清空了,但是暂存区里面还有demo1.js

1
2
3
4
$ git commit -m 'delete'
[master 2aa91ad] delete
1 file changed, 4 deletions(-)
delete mode 100644 demo1.js

此时,工作区和暂存区的文件都被清空了。