Git 学习笔记(一) Git 的基本用法

作为一名渣渣,在进入实验室接触到前端开发之前,我虽然在浏览网页或者查阅资料的时候看到过 GitHub,但那个时候对于 Git 真是一点不了解,一直到现在,对 Git 的了解也只是在一个很浅的层面,所以这篇文章的题目是 Git 的基本用法,除了介绍一些基本操作,例如将项目文件纳入到 Git 的版本控制,使用过程中的一些小细节等,还会写一些我这么多天来使用 Git 遇到的一些基本问题,作为学习笔记记录下来。

先说一下,由于我一直是在 Windows 环境下工作,所以这篇文章讲述的都是在 Windows 下 Git 的基本操作,其他平台下的操作可能与之类似但不完全相同。


对于不喜欢使用命令行的同学,我在这里推荐一款图形化 Git 管理软件 Tower ,这款软件原本在 Mac 下非常受欢迎,我本人也一直在使用。在今年7月份时,Tower 公司在其官网中宣布,Mac 平台的 Git 客户端 Tower 即将登陆 Windows 平台,就在上个月该公司终于面向 Windows 平台发布了 Tower,大家可以到其官方网站上下载该软件,唯一有点令人沮丧的是该软件是一款收费软件,价格还不低,人民币将近六百元,我等无产阶级望之兴叹,不得不做些另辟蹊径之事了……当然,除了 Tower,另一款图形化 Git 管理软件 sourceTree 也是够用的,虽然比之 Tower 可能略逊一点,但该软件是一款免费软件,任何人都可以使用,想体验一下图形化的 Git 管理的同学可以试用一下。


Git 的账户配置

1
2
$ git config --global user.name "Kannnnng"
$ git config --global user.email "575664647@qq.com"

以上两句代码是全局设置名字与邮箱,这些信息将作为提交时的签名,方便别人知道是谁做出的提交。注意,配置过程中用到了 --global 参数,这是 Git 的全局配置参数,会影响到你在本地建立的所有项目。
如果在某个项目中你想设置与全局不一样的设置,你可以在该项目中使用不带有 --global 参数的 git config 命令。

Git 的仓库建立

Git 仓库有两种方法建立,一种是对已有的 Git 仓库克隆,从而在本地获取该仓库的副本;二是本地新建一个仓库,从而把未纳入版本控制的文件纳入到版本控制。

克隆仓库

获得一个仓库的拷贝副本,可以通过多种协议获取,如 ssh://、http(s)://,使用 http(s):// 时命令为

1
$ git clone https://github.com/Kannnnng/Kannnnng.github.io.git

如果你在 GitHub 上已经登陆,并且在此之前你已经为自己的账户配置过了 git ssh key(没有配置过的同学请看这里),那么就可以使用更为有效和迅速的 ssh://协议,命令为

1
$ git clone git@github.com:Kannnnng/Kannnnng.github.io.git

初始化仓库

如果你有一个文件夹 Test,现在你想把这个文件夹下的文件纳入到 Git 的版本控制之中,那么你可以在这个文件夹下面初始化一个仓库,命令为

1
$ git init

如果这时你的文件夹下面新出现了一个名为 .git 的隐藏文件夹,那么说明你的仓库就已经初始化完成了。但是此时仓库仅仅存在于你本地目录下,在远程是没有一个仓库与之对应的,所以你需要自己动手,建立远程仓库并将其与你本地仓库建立连接(同步)。这个过程一般分为以下几步:
1.登陆你的 GitHub,新建一个名字与本地仓库名字相同的远程仓库;
2.如果你已经为自己的账户配置过了 git ssh key,那么你需要在仓库的 Setting 里面找到标签页 Deploy keys,之后在其中添加你的公钥,同时勾选页面最下面的那个选项(是否接收推送)并保存,如果没有配置过 git ssh key 则跳过此步骤;
3.在本地仓库目录下运行 git remote add [name] [url],其中,name 是远程版本库名称,默认为 origin(详细信息点击这里),url 则是你在 GitHub 上刚刚新建的仓库的 url,直接复制过来即可。完整命令如下

1
$ git remote add origin git@github.com:Kannnnng/Test.git

Git 的其他命令

首先你应该了解使用 Git 工作的步骤,一般来说常用的就三个命令:git status(查看文件改动状态)、git add(将文件添加到索引中)、git commit(提交当前索引中的文件并添加说明),这里探讨一点细节。

git status 命令显示的文件的三种状态

使用 git status 命令以后,Git 会列出当前项目中文件的三种状态。
1.Changes to be committed,是指下列文件已经被更改过。同时已经通过 git add 命令添加到了索引中,下一步就是使用 git commit 命令来提交更改。
2.Changes not staged for commit,是指下列文件已经被更改过,但还没有通过 git add 命令添加到索引中。
3.Untracked files,是指下列文件都是一些新文件,之前没有被 Git 追踪,现在需要通过 git add 命令添加到索引中。

通过以上三种状态,我们就可以方便地区分哪些文件需要提交,哪些文件是还没有被添加到索引,哪些文件是新文件,需要添加到索引以便纳入到版本控制之中。

git add 的其他用法

git add 最常用的就是后面跟 .,表示将所有改动过的或新建的文件添加到索引,不过,通过帮助信息可以得知,git add 还有一些在特殊情况下很有用的命令形式。

1
2
3
4
$ git add .                           #将所有改动过的或新建的文件添加到索引
$ git add xxx1.js xxx2.js xxx3.js #将特定的文件添加到索引
$ git add -f xxx4.js #将通过 .gitignore 忽略的文件添加到索引
$ git add -u #将除新建文件以外的其他改动过的文件添加到索引

git reset 的详细讨论

有时你改动了本地工作区一些代码,之后因为某些原因需要将改动撤销,使工作区恢复到未改动之前的样子,这时可以使用 git reset 命令完成。

1
$ git reset --hard

其实,git reset 命令不仅仅只有这一个功能,通过 git reset -h,可以看到完整命令是

1
$ git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]

注意到该命令存在五种不同的模式,最常用的是 --mixed 与 --hard,其中 --mixed 更是 git reset 的默认模式,两者都会把当前索引区重置,也就是说,通过 git add 命令将改动文件添加到索引区的操作可以使用 git reset --mixed 或 git reset --hard 来撤销,两者的不同在于对工作区的操作,--mixed 模式不会重置工作区,也就是说工作区的代码还是改动过的代码,而--hard 模式则会将工作区一并重置,改动过的代码将恢复到未改动之前,所以使用--hard 模式一定注意。
除了以上两种比较常用的模式之外,--soft 模式也是比较有用的,它对本地仓库的改动相较于前两种模式来说更小,它不会更改索引区与工作区任何内容,而是仅仅将 HEAD (指向当前分支的最后一次提交记录)指向你所指定的提交记录,同时在指定提交记录之后的所有改动过的文件都会处于等待提交状态,也就是上文所说的 Changes to be committed,所以--soft 模式一般用于更改提交记录(至少我是这么做的)。
--merge 模式与 --keep 模式没用过,对它们的用法也不太了解,如果你想了解更多,可以查看这篇博文
最后要说的是,所有模式均可以在最后指定某一历史提交记录,这样你就可以控制本地代码具体回退到哪个历史版本,用法示例如下,最后的参数即你想回退到的版本的 SHA1 值。

1
$ git reset 模式 0f14df……092904

git log 查看提交日志

使用 git log 命令可以让你很好地掌握项目代码的提交记录,对项目进度有一个清晰地了解。一般地,查看代码提交日志可以分为查看本地的日志与远端仓库的提交日志。查看本地项目代码提交日志比较简单,直接使用 git log 命令即可。查看远端仓库的提交日志与此稍有不同,你需要先将远端的最新代码 fetch 一份到本地,之后才能使用 git log 命令查看远端的提交日志,也就是下面这样

1
2
$ git fetch
$ git log origin/branch

这里我们用到了 git fetch 命令,这个命令所做的工作是将远端仓库的最新代码复制一份到本地,但你不用担心复制下来的代码会把你工作区的代码覆盖掉,实际上使用 git fetch 命令取回的代码不会放在工作区,它对工作区代码没有任何影响,它的作用之一就是方便你随时查看别人提交上去的代码与提交日志;你还可以用取回的远端仓库最新代码与你本地代码做比较,查看两者之间的异同;除此之外,你可以通过 git merge 命令将取回的远端仓库最新代码与你本地工作区的代码合并,从而更新你工作区的代码,不过,你有没有觉得这条命令的作用与 git pull 命令的作用相同,实际上,git pull = git fetch + git merge。
通过 git log 命令的帮助信息,我们可以了解到这条命令有很多附加的参数,能够更好地显示项目的日志信息,我最常用的是下面这个,意思是只输出当前项目最新的 number 条日志记录,例如取 number 等于3,则只会显示最新的3条日志记录,这样方便查看项目代码的最新提交记录,减少输出一些我们不关心的更早信息。

1
$ git log -n number

另外,--graph 也是一个比较有意思的参数,它指示 Git 以一种图形化的方式显示项目代码更新记录,让你看的时候更加清晰明了,不过我到现在为止一直都是一个人做项目,基本上没有同其他人一起协同提交代码,而且项目的规模都比较小,分支很少,所以看提交日志的时候基本没有压力,也就没怎么用过这个参数,对此有兴趣的同学可以去多多尝试一下。

git diff 比较代码差异

Git 一个很重要的功能就是方便开发者了解代码的具体变动情况,明白前一次提交与后一次提交之间的差异部分在哪里,git diff 命令能够很好地胜任这个任务,使用 git diff 命令可以查看当前项目相对于最近一次提交的更改情况,例如你之前提交过一次本地代码,一段时间以后你对本地代码有了多处修改,这时你想看看现在的代码与之前的代码之间的具体差别,就可以使用下面的命令

1
$ git diff

这是最简形式,默认比较本地工作区代码与最近一次提交代码之间的差别,它会将两个代码版本之间的差别全部显示出来,如果没有完全显示,你可以按下回车键或 DOWN 方向键,显示出下面未显示的部分。
当然,该命令仍然有比较多的可附加参数,具体同学们可以自行学习,在这里我只写几个我经常用到的。
首先是比较本地工作区代码与远端仓库代码之前的具体差异,因为我有时仅凭借日志文件并不能完全记起我对代码到底做出了哪些具体的修改,所以我更喜欢结合 git log 与 git diff 命令一起查看项目代码的更新,即查看远端代码提交日志的同时,附带着查看一下远端代码与我本地工作区代码之间的差别。查看远端代码与本地代码差异的命令如下,其中前面的 branch 代表本地工作区分支,后面的 branch 代表远端分支,origin 为远端仓库名称。

1
2
$ git fetch
$ git diff branch origin/branch

这里同样用到了 git fetch 命令,原因很简单,既然要比较本地代码与远端代码之间的差异,那肯定要先将远端代码复制到本地,然后才能够与本地代码比较。
令人扫兴的是,如果你对本地项目代码做出了大量修改,那么上面的命令就会将所有的差异全部打印出来,若你只关心某一个或哪些文件发生了更改,那么想要在 Git 打印出来的所有差异中找到你想要的就会大费周章,幸好 git diff 命令有一个可选参数 --stat,可以控制 git diff 命令仅仅显示是哪些文件内的代码存在差异,而不是一股脑儿将所有文件的差异都打印出来,如此你就可以快速了解你现在的项目与远程仓库内的项目之间有哪些文件发生了变化,产生了差异,然后你再通过 git diff 命令对你感兴趣的特定文件做差异比较,从而提高工作效率。具体代码如下

1
2
$ git fetch
$ git diff branch origin/branch --stat

执行以上命令以后,Git 会打印出一个文件列表,列出哪些文件之间存在差异,之后你可以通过下面的命令来具体查看某一文件,下面的命令作用是查看本地工作区的 xxx.html 文件与远端仓库内的 xxx.html 文件之前存在哪些具体差异。

1
$ git diff branch origin/branch \-\- xxx.html

git diff 命令甚至还可以比较两次历史版本之间的差异,只需要在命令的最后添加两次历史版本的 SHA1 值作为参数就可以了。例如

1
$ git diff c211d1……49e973fc 0c1580……b702a31b

不过这是本地历史版本比较,想要比较远程仓库历史版本,应该也是需要使用 git fetch 命令,不过我没有使用过,所以就不乱说话了。

还有一些命令形式不太常规,在这里就不列出了,有兴趣的同学可以自己去了解。

Git 的常见问题

在我使用 Git 的过程中遇到过很多很多的问题,这些问题或大或小,有些解决起来比较麻烦,有些只要知道了一些小技巧解决起来易如反掌,下面我就把我自己所遇到过的同时自己还有印象(比较折磨人的问题一般都令人印象深刻)的问题写在下面,在作为自己的 Git 笔记的同时,也希望能够帮助到更多的同学。

版本不同情况下强制推送

Git 要求你在推送代码之前本地版本与远程版本相同,这样做很容易理解,因为如果修改本地代码之前没有从远程更新最新版本的代码,那本地版本肯定会缺少一些最新的修改,直接推送的话会把最新的修改覆盖掉,但如果有些时候我们确实想要(不顾一切地)用本地代码完全替代远程代码,这个时候我们可以使用以下命令

1
$ git push -f

不过还是需要提醒一下,这个命令很蛋疼,一旦将本地代码强制推送到远程仓库,则没有同步的修改代码就很不好找回来了,所以除非你非常确定现在的操作是正确的,否则不要轻易使用这条命令。

当前修改尚未推送但需获取最新代码

Git 要求你在获取代码(git pull)之前对本地工作区的修改需要全部提交,这样做是保证本地修改的代码不被远程代码覆盖,但有时你可能不想现在就提交修改(例如某个功能你只完成了一半),那么你可以先将修改的代码存入 Git 栈中,这样本地工作区与未修改之前的便相同,之后获取远程最新代码,最后再将修改的代码从 Git 栈读取出来,完成以上操作执行以下命令即可。

1
2
3
$ git stash                           #将修改过的代码保存到 Git 栈中,使本地代码与未修改之前的代码相同
$ git pull #获取远端仓库的最新代码
$ git stash pop #从 Git 栈中读取最近一次保存的代码,恢复到工作区中

除此之外你可能还希望知道下列命令,它们在操作 Git 栈方面有很大帮助。

1
2
3
$ git stash pop stash@{[number]}      #从 Git 栈中读取指定的代码,恢复到工作区中,number 可以通过 git stash list 查看
$ git stash list #显示 Git 栈内的所有备份,可以利用该命令确定从哪个地方开始恢复
$ git clear #清空 Git 栈