———–
我先开一会儿吐槽大会,Git 这货色我用了两年,基本尼玛用不明确。
我搞不明确的一个重要起因就是,命令的性能太杂,有时候一个需要能够用好几种命令解决,而且有的命令还 tm 有别名。这导致什么问题呢,我在网上找到的答案形形色色,居然都能达成目标,难以找到法则,毫无套路可言。对于我这种不喜爱动脑子,只喜爱玩套路的人来说,几乎不能承受。
以前我用 Git,就晓得 add .
,而后 commit -m
,最初 push origin master
一套带走,或者就是把 Git 作为下载器,去 clone
他人的我的项目。然而在工作中呢,和他人一起开发代码,就须要解决一些简单状况,比方解决抵触,比方手残复原,等等等实用场景,这些我在后文都会列举。
对于工具的学习,我认为应该多做减法,只捡最有用的学,那些奇技淫巧不学也罢,应该把工夫投入更有价值的事件中。
所以本文不是一个大而全 Git 命令的使用手册,而是依据理论工作中最常见问题,提供小而美的解决方案,仅仅波及四个命令:add
,commit
,reset
,checkout
。
一、前言
首先,在进入 Git 的各种神仙操作之前,肯定要明确 git 的三个「分区」是什么,否则的话你肯定没方法真正了解 Git 的原理。
Git 的三个分区别离是:working directory
,stage/index area
,commit history
。
working directory
是「工作目录」,也就是咱们肉眼可能看到的文件,后文咱们称其为 work dir
区。
当咱们在 work dir
中 执行 git add
相干命令后,就会把 work dir
中的批改增加到「暂存区」stage area
(或者叫 index area
)中去 ,后文咱们称暂存区为 stage
区。
当 stage
中存在批改时,咱们 应用 git commit
相干命令之后,就会把 stage
中的批改保留到「提交历史」commit history
中,也就是 HEAD
指针指向的地位。后文咱们称「提交历史」为 history
区。
对于 commit history
咱们多说几句,任何批改只有进入 commit history
,根本能够认为永远不会失落了。每个 commit
都有一个惟一的 Hash 值,咱们常常说的 HEAD
或者 master
分支,都能够了解为一个指向某个 commit
的指针。
work dir
和 stage
区域的状态,能够通过命令 git status
来查看,history
区域的提交历史能够通过 git log
命令来查看。
好的,如果下面的内容你都可能了解,那么本文就齐全围绕这三个概念开展,上面就是一个「状态转移图」:
二、本地 Git 极简教程
需要一,如何把 work dir
中的批改退出 stage
。
这个是最简略,应用 git add
相干的命令就行了。顺便一提,add
有个别名叫做 stage
,也就是说你可能见到 git stage
相干的命令,这个命令和 git add
命令是齐全一样的。
危险等级:无风险。
理由:不会扭转任或撤销任何已作出的批改,而且还会将 work dir
中未追踪的批改(Untracked file)增加到暂存区 stage
中进行追踪。
需要二,如何把 stage
中的批改还原到 work dir
中。
这个需要很常见,也很重要,比方我先将以后 work dir
中的批改增加到 stage
中,而后又对 work dir
中的文件进行了批改,然而又悔恨了,如何把 work dir
中的全副或局部文件还原成 stage
中的样子呢?
来个理论场景,我先新建两个文件,而后把他们都加到 stage
:
$ touch a.txt b.txt
$ git add .
$ git status
On branch master
Changes to be committed:
new file: a.txt
new file: b.txt
而后我又批改了 a.txt
文件:
$ echo hello world >> a.txt
$ git status
On branch master
Changes to be committed:
new file: a.txt
new file: b.txt
Changes not staged for commit:
modified: a.txt
当初,我悔恨了,我认为不应该批改 a.txt
,我想把它还原成 stage
中的空文件,怎么办?
答案是,应用 checkout
命令:
$ git checkout a.txt
Updated 1 path from the index
$ git status
On branch master
Changes to be committed:
new file: a.txt
new file: b.txt
看到了么,输入显示从 index
区(也就是 stage
区)更新了一个文件,也就是把 work dir
中 a.txt
文件还原成了 stage
中的状态(一个空文件)。
当然,如果 work dir
中被批改的文件很多,能够应用通配符全副复原成 stage
:
$ git checkout .
有一点须要指出的是,checkout
命令只会把被「批改」的文件复原成 stage
的状态,如果 work dir
中新增了新文件,你应用 git checkout .
是不会删除新文件的。
危险等级:中危险。
理由:在 work dir
做出的「批改」会被 stage
笼罩,无奈复原。所以应用该命令你应该确定 work dir
中的批改能够摈弃。
需要三,将 stage
区的文件增加到 history
区。
很简略,就是 git commit
相干的命令,个别咱们就是这样用的:
$ git commit -m '一些形容'
再简略提一些常见场景,比如说 commit
完之后,忽然发现一些错别字须要批改,又不想为改几个错别字而新开一个 commit
到 history
区,那么就能够应用上面这个命令:
$ git commit --amend
这样就是把错别字的批改和之前的那个 commit
中的批改合并,作为一个 commit
提交到 history
区。
危险等级:无风险。
理由:不会扭转任或撤销任何已作出的批改,而且还会将 stage
区的批改退出 history
区并调配一个 Hash 值。只有不乱动本地的 .git
文件夹,进入 history
的批改就永远不会失落。
需要四,将 history
区的文件还原到 stage
区。
这个需要很常见,比如说我用了一个 git add .
一股脑把所有批改退出 stage
,然而忽然想起来文件 a.txt
中的代码我还没写完,不应该把它 commit
到 history
区,所以我得把它从 stage
中撤销,等前面我写完了再提交。
$ echo aaa >> a.txt; echo bbb >> b.txt;
$ git add .
$ git status
On branch master
Changes to be committed:
modified: a.txt
modified: b.txt
如何把 a.txt
从 stage
区还原进去呢?能够应用 git reset
命令:
$ git reset a.txt
$ git status
On branch master
Changes to be committed:
modified: b.txt
Changes not staged for commit:
modified: a.txt
你看,这样就能够把 a.txt
文件从 stage
区移出,这时候进行 git commit
相干的操作就不会把这个文件一起提交到 history
区了。
下面的这个命令是一个简写,实际上 reset
命令的残缺写法如下:
$ git reset --mixed HEAD a.txt
其中,mixed
是一个模式(mode)参数,如果 reset
省略这个选项的话默认是 mixed
模式;HEAD
指定了一个历史提交的 hash 值;a.txt
指定了一个或者多个文件。
该命令的自然语言形容是:不扭转 work dir
中的任何数据,将 stage
区域中的 a.txt
文件还原成 HEAD
指向的 commit history
中的样子。就相当于把对 a.txt
的批改从 stage
区撤销,但仍然保留在 work dir
中,变为 unstage
的状态。
危险等级:低危险。
理由:不会扭转 work dir
中的数据,会扭转 stage
区的数据,所以应确保 stage
中被改变数据是能够摈弃的。
需要五,将 work dir
的批改提交到 history
区。
这个需要很简略啦,先 git add
而后 git commit
就行了,或者一个快捷办法是应用命令 git commit -a
。
危险等级:无风险。
理由:不言而喻。
需要六,将 history
区的历史提交还原到 work dir
中。
这个场景,我说一个极其一点的例子:比方我从 GitHub 上 clone
了一个我的项目,而后乱改了一通代码,后果发现我写的代码基本跑不通,于是悔恨了,罗唆不改了,我想复原成最后的模样,怎么办?
仍然是应用 checkout
命令,然而和之前的应用形式有一些不同:
$ git checkout HEAD .
Updated 12 paths from d480c4f
这样,work dir
和 stage
中所有的「批改」都会被撤销,复原成 HEAD
指向的那个 history commit
。
留神,相似之前通过 stage
复原 work dir
的 checkout
命令,这里撤销的也只是批改,新增的文件不会被撤销。
当然,只有找到任意一个 commit
的 HASH 值,checkout
命令可就以将文件复原成任一个 history commit
中的样子:
$ git checkout 2bdf04a some_test.go
Updated 1 path from 2bdf04a
# 前文的用法显示 update from index
比方,我改了某个测试文件,后果发现测试跑不过了,所以就把该文件复原到了它能跑过的那个历史版本……
危险等级:高风险。
理由:这个操作会将指定文件在 work dir
的数据恢复成指定 commit
的样子,且会删除该文件在 stage
中的数据,都无奈复原,所以应该谨慎应用。
三、其余技巧
需要一,合并多个 commit
。
比如说我本地从 17bd20c
到 HEAD
有多个 commit
,但我心愿把他们合并成一个 commit
推到近程仓库,这时候就能够应用 reset
命令:
$ git reset 17bd20c
$ git add .
$ git commit -m 'balabala'
回顾一下方才说的 reset
命令的作用,相当于把 HEAD 移到了 17bd20c
这个 commit
,而且不会批改 work dir
中的数据,所以只有 add
再 commit
,就相当于把两头的多个 commit
合并到一个了。
需要二,因为 HEAD
指针的回退,导致有的 commit
在 git log
命令中无奈看到,怎么失去它们的 Hash 值呢?
再反复一遍,只有你不乱动本地的 .git
文件夹,任何批改只有提交到 commit history
中,都永远不会失落,看不到某些 commit
只是因为它们不是咱们以后 HEAD
地位的「历史」提交,咱们能够应用如下命令查看操作记录:
$ git reflog
比方 reset
,checkout
等等要害操作都会在这里留下记录,所有 commit
的 Hash 值都能在这里找到,所以如果你发现有哪个 commit
忽然找不到了,肯定都能够在这里找到。
需要三,怎么解决抵触?
记住,Git 尽管高大上,但也不要迷恋,肯定要懂得借助先进的工具。
比拟风行的代码编辑器或者 IDE 都会集成不便的可视化 Git 工具,至于解决抵触,可视化的体现形式不是比你在命令行里 git diff
看半天要清晰明了得多?只须要点点点就行了。
所以说,只有明确本文讲的这些基本操作,够你用的了,平时能用图形化工具就多用图形化工具,毕竟工具都是为人服务的。
前文 Git/SQL/ 正则表达式的在线练习平台 介绍了一个优良的 Git 在线练习平台,值得一刷。