什么是Git

版本控制系统

首先我们要明白,什么是版本控制系统(version control systems)?

版本控制系统,顾名思义,就是能控制文件处于哪个版本的系统。

e.g. 你在博客园里编辑的文章,你可以控制文章是处于某个时间点的版本。

而Git就是一款版本控制系统,而且是分布式的。

与分布式相对应的是集中式版本控制系统,它的版本库是存储在中央服务器的。工作的时候你需要先从中央服务器取得最新的版本,等工作完了,再把自己的工作推送回中央服务器。但这有个问题:万一哪天中央服务器崩溃了甚至数据丢失了,那所有人不就都无法工作了?

于是分布式版本控制系统(Distributed Version Control System,简称 DVCS)就应运而生了。它没有“中央服务器”,每个人的电脑上都有一份完整的版本库,如果某个人的电脑坏了导致版本库丢失了,直接从别人那复制一个版本库过来即可。

Git的基本工作原理

有一类版本控制系统是基于差异(delta-based)来进行版本控制的。

这类版本控制系统存储的是新文件和旧文件间的区别(delta)。这里的delta和高中物理里的delta一样,\(Δv = v_2 - v_1\),都指的是变化量。

而Git和上述这类版本控制系统的工作原理不同:每当你对提交新项目或者对项目进行修改,它会把项目的所有文件都压缩保存起来,这个压缩文件在Git中被称为快照(snapshot)。每次有更新就会每次把所有文件都压缩起来存储一遍,形成一系列快照。

当我们需要处于某个时间点版本的文件时,直接根据索引,找到那个时间点的快照即可。

操作指令

在开始介绍Git相关的指令前,我们先创建三个文件。

创建版本库

所谓“版本库(repository)”就是一个用来存储文件历史版本的仓库。所以要想对文件进行版本控制,我们需要先初始化一个版本库来存储文件的各个历史版本。

$ git init 命令能把当前所在目录变为由Git管理的仓库(或是对该位置已有仓库进行初始化),使得该目录下的文件可被Git进行版本控制。

当执行完该命令后,可以看到该目录下生成了一个 .git 的文件夹。

把文件添加到版本库

创建完版本库后,我们还需要把文件放进去才能对其进行版本控制。

而把文件添加到版本库需要两个步骤:

第一步,用 $ git add 命令把需要跟踪版本的文件添加到暂存区。

第二步,用 $ git commit 命令告诉仓库:我确定要对暂存区里的文件进行版本控制,请把暂存区里的文件正式放入仓库管理。

-m "xxx" 是对本次提交的备注说明;2 files changed 指有两个文件变动:即我们新添加的 README.mddemo1.txt1 insertion 指插入1行内容:即我们在 README.md 文件中插入的“Git is a version control system.”。

在每次提交变动(commit)后,都会产生对应的快照。快照,我们在Git的工作原理那章提到过,它就是被放入版本库中的所有文件的压缩版本。我们可以把快照理解成是游戏的存档。在玩游戏时,我们每通过一关就会保存游戏。如果进行到某一关死了,那就可以通过读取存档来回到前面的某一关。Git也一样,每次commit就是在给文件存档,要是哪一天文件误删了或出错了,那我们就可以读取存档,让文件恢复到存档时的状态。

通过 $ git log 命令,可以查看当前共有几个存档。

同步修改到版本库

当我们在工作区(工作区就是当你打开“我的电脑”看到的目录就叫工作区)里对文件进行修改后,如何把更新同步到版本库中,让版本库也拥有文件最新的版本呢?

我们先添加一行话到 README.md 文件中,使其内容变为如下:

Git is a version control system.
Git is distributed.

现在,运行 $ git status 命令查看版本库当前状态:

从状态中我们可以得到2个信息:

  1. README.md 文件进行了修改,并且这次修改还没有被添加到暂存区。
  2. learn_git 目录下,demo2.txt 文件没被添加到仓库中以进行版本追踪。

如果我们想要进一步得知对 README.md 文件进行了哪些修改,可以用 $ git diff 命令(这步并不是必须的)。

可以看到,新增加的改动是“Git is distributed”这行话。

确定了作出哪些修改后,再把它提交到仓库自然就放心多了。提交修改和之前提交新文件需要同样的两个步骤:$ git add$ git commit

我们先运行 $ git add,然后看看版本库状态。

可以看到,当我们用 $ git add 命令把文件添加到暂存区后,版本库状态就不会再提示我们“尚未暂存已备提交的变更”,而是提示“要提交的变更”,即提示我们要用 $ git commit 命令提交这次变更。

那我们就再运行一下 $ git commit 命令,再来看看版本库状态。

再用git log查看一下当前共有几个存档(快照)。

从以上两张截图可以看出现在已经没有需要提交的修改了,只剩下未跟踪的文件 demo2.txt 了,刚才的那次变动已经被版本库存档了。

把文件从版本库中删除

删除文件也算是对文件的一种修改。同样也需要两步:

  1. $ git rm <file> 把该文件从工作区删除。
  2. commit这次变动,把删除同步到版本库。

注:在执行 $ git rm <file> 指令时,Git会默认执行 $ git add 命令来把这次修改提交到暂存区。

回退到某个版本

我们已知目前共有2个存档:第一个存档的ID是b1a6863...,存档备注是“added README and demo1”;第二个存档的ID是245fcc1...,存档备注是“modified README”。

在Git中,HEAD 是一个指针,它会指向当前所在分支的最新存档,分支的概念我们后边在介绍,但反正目前我们没有创建别的分支。你让 HEAD 指向哪个版本号,Git就把当前版本定位在哪。所以此时 HEAD 代表的就是ID为245fcc...的存档,那么在它前一个的存档就是 HEAD^,前N个的存档就是 HEAD~N

有一天你可能会觉得第二个版本很糟糕,不如第一个版本,于是想坐时光机回到过去,让一切复原到第一个版本时的样子。这时我们可以用 $ git reset 命令来帮助我们穿越时空。

我们再来看一下 README.md 里的内容,后面新添加的那行就不见了,内容回到了最初的时候:

Git is a version control system.

再运行一下 $ git log

一切真的都回到了过去!连存档都变成和当时一样,只有b1a6863...这一个了!

为什么回退的速度这么快呢?

因为我们每个版本的文件都被保存在了快照里,Git仅仅只需要把 HEAD 指针从一个快照指向到另一个快照,再把目录里的文件替换成那个快照里的文件即可。

所以建议:无论是大的变动还是小的变动,都存一下档(即commit)。只有把这个版本存档了,保存进快照里了,日后才能通过读档来返回到该版本。

可万一你又后悔了,觉得一切还是像第二个版本时的那样好,怎么办?通过 $ git log 命令已经找不到第二个版本的存档了,是不是回不去了?

并不是的。在Git中,你的每次操作就会留下记录,我们可以用 $ git reflog 来查看所有的操作。

从中我们可以看到,备注为“modified README”的那次commit的ID是245fcc1。有了commit ID后,我们就又能通过 $ git reset 命令回到未来了!

再运行一下 $ git log 和打开 README.md 文件,可以确认真的回到了未来!

撤销修改

撤销修改有三种情况:

  1. 修改还没添加到暂存区。
  2. 修改已经添加到暂存区,但还没commit。
  3. 修改已经commit。

修改还没添加到暂存区

我们先谈第一种情况。

假设你往 README.md 文件里添加了一行“My boss is stupid. ”,而这最新修改还没有被你添加到暂存区。所以当你运行$ git status 命令时,你看到Git提示你有尚未暂存已备提交的修改。

你觉得这句话可能会让你被炒鱿鱼,尽管你说的是事实。于是你最终还是决定把这句话删掉。

有两种删除方式:

  1. 手动打开 README.md 文件,然后把这句话删掉,让文件回到添加这行话前的样子。
  2. 按照Git提示的,用 $ git restore <file> 命令,来直接撤销改文件在工作区的变动。

你觉得第二种方式看起来显得逼格更高点,于是你输入如下命令。

$ git restore README.md

然后当你再打开README.md文件,发现那句话果然消失了;再运行 $ git status 命令,发现Git也没有任何提示了。

修改已经添加到暂存区但还没commit

再来谈谈第二种情况:你不仅写了那句话,还提交到了暂存区!所以当你运行 $ git status 命令时,你看到Git提示你有要提交的变更。

有了上次撤销的经验,你就放心地按照Git的提示输入如下命令:

$ git restore --staged README.md

再运行 $ git status,你发现现在它提示的和第一种情况一样了,而打开工作区里 README.md 文件,那句话还在。原来这个命令只是帮你撤销一小步,仅仅是从暂存区移除,而不是帮你一夜回到解放前。

当然了,如果你很不幸的已经commit这次提交了,那就只能用 $ git reset 来版本回退了。

总结

Git中文件的所有状态以及状态间的转换可以被概括为下面这张图。

最后,总结一下今天出现过的几个Git命令:

# 创建版本库
$ git init # 把文件添加到暂存区
$ git add <file> # 把暂存区里的变动提交到版本库
$ git commit # 查看当前所有快照
$ git log # 查看版本库当前状态
$ git status # 查看某文件的变动
$ git diff # 删除文件,并把这次变动提交到暂存区
$ git rm <file> # 版本间的前进和回退
$ git reset # 撤销修改
$ git restore

参考

  1. https://sp18.datastructur.es/materials/guides/using-git.html
  2. https://www.liaoxuefeng.com/wiki/896043488029600
  3. http://git-scm.com/book/en/v2/Getting-Started-What-is-Git%3F
  4. https://stackoverflow.com/questions/4964099/what-is-a-git-snapshot

有问题欢迎大家在评论区留言,转载请注明出处。

Git轻松入门1:本地仓库篇的更多相关文章

  1. Git轻松入门2:分支篇

    什么是分支 在玩剧情类游戏时,不同的选择会触发不同的剧情路线,每条剧情路线都会独立发展,最终走向不同的结局. Git中所谓的"分支(branch)"就如同游戏中的剧情路线,用户可以 ...

  2. Git轻松入门3:远程仓库篇

    在第一讲中,我们有介绍过:Git是分布式版本控制系统.每个人的电脑上都有一份完整的版本库.当对项目作出了修改后,只要把修改推送给对方即可.但很有可能的情况是:两台电脑不在一个局域网内,无法互相访问:或 ...

  3. Git【入门】这一篇就够了

    前言 欢迎关注公众号,白嫖原创PDF,也可以催更,微信搜:JavaPub,回复:[666] Git 在生产工作中是使用频率很高的工具,但我发现很多文章只是对它做了简单的提交命令说明,真正遇到 版本冲突 ...

  4. git学习笔记04-将本地仓库添加到GitHub远程仓库-git比svn先进的地方

    第1步:创建SSH Key.在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsa和id_rsa.pub这两个文件,如果已经有了,可直接跳到下一步. 如果没有,打开Shel ...

  5. git 快速使用(本地仓库同步到远程仓库)

    学git一段时间,可惜公司用的是svn,平时少用,又忘了,总结一下,免得下次又得重新学习.得多多用才是正道! 一.  将本地的提交到网上git仓库 1.在git创建仓库                ...

  6. Git总结笔记3-把本地仓库推送到github

    说明:此笔记在centos 7 上完成 1.配置公钥 [root@kangvcar ~]# ssh-keygen -t rsa -C "kangvcar@126.com" [roo ...

  7. 如何在git上创建的本地仓库

    一.安装git(在git) 二. 三.输入个人信息(代码提交者) git config --global user.name "xxxx" git config --global ...

  8. Android Studio使用GIt提交代码到本地仓库后没有Push,如何回退保存

    当在AS中使用Git来提交代码时,有时候我们不注意的情况下会把不想提交的文件或文件夹(比如\build下的)提交到本地仓库,如果此时并没有Push到远程仓库的话.如果让本地仓库的提交回退并且保存之前的 ...

  9. git笔记十:本地仓库同步到gitlab

    本地仓库同步到gitlab 帮助文档 git remote --help 操作场景: 本地创建git仓库(含有readme.md文件), commit了三次 gitlab网站创建了一个项目 添加了re ...

  10. git 使用案例(本地仓库无缝迁移远程仓库)

    之前都是直接从gitlab上clone代码,然后把本地代码copy过去,然后push.有点麻烦,查询了一下如何无缝从本地仓库迁移到远程仓库.记录一波... 下面的例子采用github来做例子. 1. ...

随机推荐

  1. Linux性能优化:CPU性能分析工具--vmstat

    Blog:博客园 个人 目录 参数说明 输出信息说明 procs memory swap io system cpu 示例 vmstat是Virtual Meomory Statistics(虚拟内存 ...

  2. R绘图(1): 在散点图边缘加上直方图/密度图/箱型图

    当我们在绘制散点图的时候,可能会遇到点特别多的情况,这时点与点之间过度重合,影响我们对图的认知.为了更好地反映特征,我们可以加上点的密度信息,比如在原来散点所在的位置将密度用热图的形式呈现出来,再比如 ...

  3. File对象目录列表器

    /****File对象即能代表一个文件又能代表一组目录下的特定文件集:如果它代表一组文件那可以用list方法返回一组字符数组,数组内的对象是该目录下的符合条件的每个文件. 为什么说符合条件下这个前提呢 ...

  4. ethernet

    OSI参考模型 简化相关网络操作:提供不同厂商之间的互联 应用层 为应用程序提供服务 HTTP Telnet 表示层 数据格式化,加密.解密 ASCII EBCDIC JPEG 会话层 建立.维护.管 ...

  5. 知识图谱和neo4j的基本操作

    一.知识图谱的简介 1.知识图谱是什么 知识图谱本质上是语义网络(Semantic Network)的知识库 可以理解为一个关系图网络. 2.什么是图 图(Graph)是由节点(Vertex)和边(E ...

  6. Python Kafka Client 性能测试

    一.前言 由于工作原因使用到了 Kafka,而现有的代码并不能满足性能需求,所以需要开发高效读写 Kafka 的工具,本文是一个 Python Kafka Client 的性能测试记录,通过本次测试, ...

  7. 【Vue】Vue开发环境搭建

    Vue前置学习环境 文章目录 Vue前置学习环境 IDE Node.js 调试环境 工程环境 小结 IDE WebStorm 官网下载:https://www.jetbrains.com/websto ...

  8. Upload - Labs (上)

    Pass - 01: 1.尝试上传一个php文件:aaa.php,发现只允许上传某些图片类型,用bp抓包,发现http请求都没通过burp就弹出了不允许上传的提示框,这表明验证点在前端,而不在服务端 ...

  9. idea 启动热部署Devtolls

    1.在子工程pom.xml中添加devtools jar包到需要启动的项目中 1 <dependency> 2 <groupId>org.springframework.boo ...

  10. python7、8章

    目录 第七章 用户输入和while循环 7.1 函数input()的工作原理 7.1.1 编写清晰的程序 7.1.2 使用int()来获取数值输入 分析: 结果: 7.1.3 求模运算符 7.1.4 ...