什么是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. node环境使用lowdb轻量数据库以及基本用法

    1.安装: npm install lowdb --save 2..引入lowdb,设置表 const low = require('lowdb');  const FileSync = requir ...

  2. NOIP初赛篇——03中央处理器CPU

    CPU ​ CPU(中央处理单元)是微机的核心部件,是决定微机性能的关键部件.20世纪70年代微型机的CPU问世,微型计算机的核心部件微处理器从Intel 4004,80286,80386,80486 ...

  3. 九个最容易出错的 Hive sql 详解及使用注意事项

    阅读本文小建议:本文适合细嚼慢咽,不要一目十行,不然会错过很多有价值的细节. 文章首发于公众号:五分钟学大数据 前言 在进行数仓搭建和数据分析时最常用的就是 sql,其语法简洁明了,易于理解,目前大数 ...

  4. 2020DevOps状态报告——变更管理

    如果你的公司还没有走向平台化,现在仍然可以是很大的飞跃.您仍然可以通过解决公司的变更管理流程来加快软件交付.在本章中,我们将研究我们在公司内部所学的变更管理模式.我们将向您展示什么是有效的,什么是无效 ...

  5. 【Java基础】基本语法-变量与运算符

    基本语法-变量与运算符 关键字和保留字 关键字定义:被 Java 语言赋予了特殊含义,用做专门用途的字符串(单词). 关键字特点:关键字中所有字母都为小写. 用于定义数据类型:class.interf ...

  6. Linux 入门教程:基础操作 01

    1.1 实验内容 实验楼环境介绍 常用 Shell 命令及快捷键 Linux 使用小技巧 1.2 实验知识点 Linux 基本命令 通配符的使用 查看帮助文档 终端的概念 通常我们在使用 Linux ...

  7. python中re模块的使用(正则表达式)

    一.什么是正则表达式? 正则表达式,又称规则表达式,通常被用来检索.替换那些符合某个模式(规则)的文本. 正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符.及这些特定字符的组合, ...

  8. 为了加快速度,Redis都做了哪些“变态”设计

    前言 列表对象是 Redis 中 5 种基础数据类型之一,在 Redis 3.2 版本之前,列表对象底层存储结构有两种:linkedlist(双端列表)和 ziplist(压缩列表),而在 Redis ...

  9. 【原创】X86_64汇编、寄存器、内嵌汇编

    整理的X86_64/X86汇编.寄存器.C内嵌汇编笔记,主要用于查阅使用. 目录 一.汇编语言 二.指令 数据传输指令 栈操作指令 push pop 运算指令 位操作 比较操作指令 标志寄存器 流控制 ...

  10. netstat -an|awk '/^tcp/ {++s[$NF]} END {for( a in s) {print a,s[a]}}'

    监控tcp连接情况 netstat  -an|awk '/^tcp/ {++s[$NF]} END {for( a in s) {print  a,s[a]}}'