Git是一个免费的开源分布式版本控制系统,旨在快速高效地处理从小型到大型项目的所有内容。
首先,在正式了解各种指令的具体含义前,推荐一下learngitbranching.js.org这个网站,运用可视化和通关的方式,简明易懂,非常适合入门。同时,Git官方也提供了非常权威的使用指南,以此作为参考。
版本
Git的版本不同,对应的操作可能有所不同,在此先贴出本文所使用的Windows和Git版本信息。
1 | Windows: 1909 |
Git的下载与安装
在Linux(Ubuntu或Debian)
首先,你可以尝试在终端中输入git
,有可能你的电脑自带了Git。
但是如果十分不巧的是,你的终端上打出如下信息:
1 | $ git --version |
此处所使用的Ubuntu版本为Ubuntu 18.04.3 LTS,可能与您的版本有所偏差。
但是,只要是输出的不为git version X.XX.X.XXXX
,后面省略的部分为版本号,均说明被没有安装Git。
要安装的话也非常简单,只需输入sudo apt install git
就可以完成Git的安装。
如果您使用的是老版本的Ubuntu或Debian,要把命令改成sudo apt-get install git-core
,因为以前有个软件也叫GIT(GNU Interactive Tools),结果Git就只能叫git-core
了。由于Git名气实在太大,后来就把GNU Interactive Tools改成gnuit
,git-core
正式改为git
。
在Windows上安装Git
在Windows上安装Git,直接到官网下载安装程序,不过有些时候下载会非常非常慢,也可以选择在淘宝镜像下载,按照默认选项安装即可。
安装完成后,在开始菜单找到Git->Git Bash
,点开能够跳出一个类似于命令行的东西,就说明Git安装成功了。
Git的配置
安装完之后,需要对Git进行进一步的设置,在这个命令行中输入:
1 | $ git config --global user.name "Your Name" |
此处的Your name
为你的Git的用户名,email@example.com
为你Git的邮箱,这个适用于进行Git的认证的。
在此处git config
后面跟随的--global
参数表明,使用了这个参数,这台机器上的所有Git仓库都会使用这个配置。当然,如果你想,也可以对不同的仓库使用不同的用户名和Email地址。
Git的使用
创建版本库
版本库,可以简称为库,英文名为repository,就是一个目录,这个目录里的所有东西都被Git管理起来,你要是对其中的文件进行增删改查等操作,Git就能够追踪到它,以便之后可以还原或是进行什么操作。
创建版本库的操作,就是先指定一个你想要建库的文件夹,当然你想新建一个这个库的文件夹也可以直接使用mkdir <folderName>
,其中folderName
为你想要的文件夹名。之后cd <folderName>
进入到这个文件夹,为了使用这个文件夹作为Git的仓库,需要使用git init
将其初始化。
1 | $ mkdir <folderName> # 创建文件夹 |
操作文件
如果你想新建一个文件并添加到Git库中,那么你只需要先在这个文件夹下新建一个文件,然后使用git add <fileName>
,当你执行完这个指令没有任何提示,那么就说明你做对了,成功地添加了一个文件到缓存中。当然只是添加到缓存还不够,还需要将其提交到仓库,这个时候就需要使用git commit -m <message>
,这里面的-m <message>
是本次提交的说明,可以在里面写任何内容,不过需要用双引号把他们包起来,建议是写一些有意义的话语,这样以便之后就可以方便地找到改动记录。
当然,有了添加文件也需要有删除文件,git rm <fileName>
提供了这样的功能,需要注意的是,这里的删除操作,是从缓存区中删除,而非从库中删除这个文件。如果你想彻底删除一个文件,那么首先你需要在文件目录下将其删除,然后通过git rm <fileName>
删除对这个文件的操作,这样的话,在下次的版本管理中就彻底没有这个文件了。如果要删除之前修改过或已经放到暂存区的文件,则必须使用强制删除选项 -f
(译注:即 force 的首字母)。这是一种安全特性,用于防止误删尚未添加到快照的数据,这样的数据不能被 Git 恢复。
还有一种情况就是,我们想把一个文件从Git仓库中删除,但是我们还想把这个文件放在这个目录中,就是说我们仍然想把这个文件放到这个文件夹,即磁盘当中,但是我们不想让Git对这个文件继续进行追踪,你就可以使用git rm --cached <name>
,当然这里不止可以是文件,还可以是文件夹,因而我们使用<name>
来表示它。
如果你在Git库中移动了某个文件或者对一个文件重命名,你应该自然而然的想到,机器可能不会这么聪明地想到你是移动了或者重命名,你应该会想到机器会记录为:你删除了一个文件,然后又新增了一个文件。当然在此之前的VCS(version control system,即版本控制系统)确实会这么认为,但是Git很聪明,它可以猜测到发生了什么,但是,Git仍然给了我们一个指令git mv <oldFileName> <newFileName>
来重命名一个文件。
1 | $ git add <fileName> # 添加文件到缓存 |
所以,你应该可以大概了解Git的结构,包含一组元文件,以及在上面的若干次提交记录,每一个提交记录中又包含了对若干个文件的修改。
查看状态
有时候,已经提交了一些东西或是修改了一些东西,想看一下自己对那些东西有哪些改动该怎么办呢?Git也提供了相关的操作,分别是git status
和git diff
。
我们先来看如何查看当前变动的操作吧,有两个git指令是相关的,分别是git status
和git diff
。
git status
命令用于显示工作目录和暂存区的状态。使用此命令能看到那些修改被暂存到了, 哪些没有, 哪些文件没有被Git tracked到。git status
不显示已经commit
到项目历史中去的信息。
1 | $ git status |
就本次提交而言,git status
分为了三个部分,Changes not staged for commit
、Untracked files
以及Changes to be committed
。所以通过英文的意思和前面的解释可以大致了解:Changes to be committed
是已经加到缓存中但是还尚未被commit的文件,Changes not staged for commit
是已经在Git库中,但是我们对其进行了一些修改还未提交更改到Git库中的文件,Untracked files
就是没有添加到缓存中,但是它位于Git库所在的文件夹中的文件。如果你不想看到这些没有添加到Git库中的文件,那么你使用git status --uno
就可以忽略这些了。同时,有时候这些更改的名字会比较长,你也可以使用git status -s
查看到这些更改的简化版本。
1 | $ git status -s |
后面显而易见是带路径的文件名。前面的表示其状态,可能有四种状态,A
、M
、AM
和???
。A
表示加进了缓存区中;M
表示文件在Git库中但进行了修改;AM
表示这个文件在我们将它添加到缓存之后又有改动;???
表示这个文件并没有加到Git库中。
如果你没有可视化界面,类似于VS Code的可视化界面,但是仍想看看具体修改了那些内容,那么你就可以使用git diff
,显示已写入缓存与已修改但尚未写入缓存的改动的区别,可以查看git status
的详细内容,具体到修改了那个字节。不过如果你的终端不支持UTF-8
,那么显示中文就会出现乱码。git diff
也有对应的后缀拓展的功能:
- 尚未缓存的改动:
git diff
- 查看已缓存的改动:
git diff --cached
- 查看已缓存的与未缓存的所有改动:
git diff HEAD
- 显示摘要而非整个 diff:
git diff --stat
查看提交历史
当然,在你提交了数个版本之后,有时候想回顾一下过去提交的版本,那么你可以使用git log
命令查看。
1 | $ git log |
由于这个提交记录比较多,这里就截取一段,我们可以看到包括提交的哈希码、作者、日期,最下面一行为git commit -m <message>
中的信息。如果你想要一个更简洁的版本,那么你可以加上--oneline
选项来查看历史记录的简洁的版本。
1 | 033c067 (HEAD -> hexo, origin/hexo, origin/HEAD) Add Aliyun ECS Address |
不喜欢正着看?那么你可以使用--reverse
选项来逆向查看工作日志,这里的逆向指的是,从Git库的第一个提交开始到最后一个。
1 | 92822fa First commit |
显示太多了你不喜欢看?那么你可以使用-<num>
选项来控制查看近几条记录,比如-2
就只看近两条记录。
你甚至可以用--author=<author>
来指定看某个作者提交的记录,也可以用--since=<time>
、--before=time
、--until=time
、--after=time
,具体的时间之前还是时间之后就看各位的英语功底了,懂得都懂好吧😉。需要细说的是后面时间格式的书写,这个time,你可以用双引号包起来,也可以不包起来。关于时间的格式,你可以直接指定说--after=yesterday
,找到昨天以后的所有提交记录,也可以指定某一个日期,比如--after=2020.3.1
,这里面年月日的分隔符可以是-
也可以是.
,要是你喜欢混搭,Git也是可以认出来的。当然,又不是每个人都记得住具体什么时间(比如说我),你也可以指定说--after=1.minute.ago
(前一分钟)、--after=1.hour.ago
(前一小时)、--after=1.day.ago
(前一天)、--after=1.week.ago
(前一星期)、--after=1.month.age
(前一个月),都是可以的。
你还可以使用--grep=<Description>
选项,通过你在git commit -m <message>
中的消息来找到对应的提交记录(所以切记要写好描述,最好不要用意义不明的数字字母组合);你还可以使用-- <fileName>
选项,要注意这个--
的后面有空格,来通过文件名来查找修改了这个文件的记录。你甚至还可以通过修改的内容来找到对应的提交记录,这里要使用-S"<String>"
选项,这里的S
后面没有=
,如果你会一些正则表达式的知识,那么你可以使用-G"<Regex>"
,你还可以通过分支、标签、是否有merge来查找对应的提交记录,但是毕竟教程还没到那边,所以在这边就先提这么一下。
通过以上描述,你应该能发现,Git给予了使用者相当高的自由度,你不仅仅可以单独使用以上这个过滤器,你还可以将它们组合起来使用。如果使用得当,可以迅速且精准的定位到某一提交记录上。
撤销操作
有时候,你在刚使用git commit
完一次后,突然想起来“噢!我刚才少提交了几个文件!”,那又怎么办呢?所幸Git也提供了这样的操作,例如:
1 | $ git add fileA # 提交了一个文件 |
这边的这个git commit --amend
就是修正上一个提交,往上一个提交记录中在加上本次的修改内容,所以在git log
的提示中,并不会出现两次提交记录,这两次提交记录会合为一个。这样的好处就是你的Git提交记录里不会充斥着啊!上个记录忘加了XXX文件
或是小修改!
这种毫无意义的提交记录,弄乱你的仓库。
又有时候,你刚使用git add <file>
将文件提交到缓存中,突然一拍脑袋,“噢!我刚才多提交了几个文件!”,那又该怎么办呢?其实细心的你也许发现了,在上文中提及的git status
中Changes not staged for commit
之下有这么一条语句:
1 | Changes to be committed: |
这句话就提示你,如果一不小心多加了几个文件到缓存中,可以使用git restore --staged <file>
来取消误加进缓存里的文件。
亦或者,你改了半天,发现自己改错了,想要回退至一开始的状态怎么办?(实际上这种情况时有发生)需注意的是,这里的改错了的前提是在尚未进行git commit
操作的时候。
上文中git status
的输出中也给了你一个解决的方法:
1 | Changes not staged for commit: |
你可以使用git restore <file>
来进行撤销之前所做的修改。
需要注意的是,由于Git版本的不同,撤销操作的具体指令也会略有不同,先前版本Git的是git checkout -- <file>
,所以还是以自己版本的Git为准,可以使用git status
下面的提示来确定具体使用哪一条语句。
远程操作
为了能在任意 Git 项目上协作,你需要知道如何管理自己的远程仓库。 远程仓库是指托管在因特网或其他网络中 的你的项目的版本库。 你可以有好几个远程仓库,通常有些仓库对你只读,有些则可以读写。 与他人协作涉及 管理远程仓库以及根据需要推送或拉取数据。 管理远程仓库包括了解如何添加远程仓库、移除无效的远程仓 库、管理不同的远程分支并定义它们是否被跟踪等等。
假如你使用了Github、Gitee等git的代码托管平台,并在上面创建了一个代码库,你要下载这个代码库到本地的话,你可以使用git clone <url>
将他们克隆到本地,这里的<url>
是git的地址,可以在Github中找到
如果你想查看你已经配置的远程仓库服务器,可以运行git remote
命令。 它会列出你指定的每一个远程服务器 的简写。如果你已经克隆了自己的仓库,那么至少应该能看到origin
——这是 Git 给你克隆的仓库服务器的默认名字:
1 | $ git clone https://github.com/schacon/ticgit |
你也可以指定选项-v
,会显示需要读写远程仓库使用的 Git 保存的简写与其对应的 URL。
1 | $ git remote -v |
当然,之前这些都是基于你在Github上有这么一个仓库的前提下,那要是我想往Github上创建一个库呢?运行 git remote add
添加一个新的远程 Git 仓库,同时你还可以指定一个方便使用的简写:
1 | $ git remote |
在这里面,我们指定了pb
作为我们的简写,来代替整个URL。
下一个问题,我们的Github库可能有好多个协作者,有一个协作者修改了其中的内容并发到了远程仓库上(具体是怎么发送的,后文会继续介绍),但是如你所知,Git可不能自动地修改,那么你该怎样拉取你还没有的数据呢?你可以执行git fetch <remote>
这个语句,这个命令会访问远程仓库,从中拉取所有你还没有的数据。 执行完成后,你将会拥有那个远程仓库中所有分支的引用,可以随时合并或查看。如果你使用 clone
命令克隆了一个仓库,命令会自动将其添加为远程仓库并默认以origin
为简写。 所以,git fetch origin
会抓取克隆(或上一次抓取)后新推送的所有工作。 必须注意 git fetch
命令只会将数据下载到你的本地仓库——它并不会自动合并或修改你当前的工作。 就是说,它还停留在原来你本地上的那个版本,虽说它已经把远程仓库的版本下来下来。
git fetch
听上去好像有点不太方便,你还要继续合并修改的内容,Git也考虑到了这种情况,git pull
就可以自动设置本地 master 分支跟踪克隆的远程仓库的 master
分支(或其它名字的默认分支)。 运行 git pull
通常会从最初克隆的服务器上抓取数据并自动尝试合并到当前所在的分支。
说完了怎么从远程仓库拉取数据,现在要了解如何将数据推送到远程仓库中,这个命令很简单:git push
。 当你想要将 master
分支推送到 origin
服务器时(再次说明,克隆时通常会自动帮你设置好那两个名字), 那么运行这个命令就可以将你所做的备份到服务器:
1 | $ git push origin master |
在前面,我们给新建的仓库起了个别名pb
,如果你过了几天不喜欢这个名字了(喜新厌旧),你可以运行 git remote rename
来修改一个远程仓库的简写名。 例如,想要将 pb
重命名为 paul
,可以用 git remote rename
这样做:
1 | $ git remote rename pb paul |
值得注意的是这同样也会修改你所有远程跟踪的分支名字。 那些过去引用 pb/master
的现在会引用 paul/master
。
如果因为一些原因想要移除一个远程仓库——你已经从服务器上搬走了或不再想使用某一个特定的镜像了, 又或者某一个贡献者不再贡献了——可以使用 git remote remove
或 git remote rm
:
1 | $ git remote remove paul |