【Git 系列】基础知识全集
阅读原文时间:2021年11月20日阅读:4

Git 是一种分布式版本控制系统,它可以不受网络连接的限制,加上其它众多优点,目前已经成为程序开发人员做项目版本管理时的首选,非开发人员也可以用 Git 来做自己的文档版本管理工具。

1.1 Git 与 SVN 区别

Git 不仅仅是个版本控制系统,它也是个内容管理系统(CMS),工作管理系统等。

如果你是一个具有使用 SVN 背景的人,你需要做一定的思想转换,来适应 Git 提供的一些概念和特征。

GitSVN 区别点:

  1. Git 是分布式的,SVN 不是:这是 Git 和其它非分布式的版本控制系统,例如 SVNCVS 等,最核心的区别。
  2. Git 把内容按元数据方式存储,而 SVN 是按文件:所有的资源控制系统都是把文件的元信息隐藏在一个类似 .svn.cvs 等的文件夹里。
  3. Git 分支和 SVN 的分支不同:分支在 SVN 中一点都不特别,其实它就是版本库中的另外一个目录。
  4. Git 没有一个全局的版本号,而 SVN 有:目前为止这是跟 SVN 相比 Git 缺少的最大的一个特征。
  5. Git 的内容完整性要优于 SVNGit 的内容存储使用的是 SHA-1 哈希算法。这能确保代码内容的完整性,确保在遇到磁盘故障和网络问题时降低对版本库的破坏。

1.2 版本控制

版本控制(Revision control)是一种在开发的过程中用于管理我们对文件、目录或工程等内容的修改历史,方便查看更改历史记录,备份以便恢复以前的版本的软件工程技术。一句话就是用于管理多人协同开发项目的技术。

  • 优点
  1. 实现跨区域多人协同开发;
  2. 追踪和记载一个或者多个文件的历史记录;
  3. 组织和保护你的源代码和文档;
  4. 统计工作量;
  5. 并行开发、提高开发效率;
  6. 跟踪记录整个软件的开发过程;
  7. 减轻开发人员的负担,节省时间,同时降低人为错误。

1.3 Git 工作区、暂存区和版本库

  1. 工作区:就是你在电脑里能看到的目录。

  2. 版本库:工作区有一个隐藏目录 .git,这个不算工作区,而是 Git 的版本库。

  3. 暂存区:本地版本库里存了很多东西,其中最重要的就是称为 stage(或者叫index)的暂存区。

下面这个图展示了工作区、版本库中的暂存区和版本库之间的关系:

1.4 分支

分支是为了将修改记录的整个流程分开存储,让分开的分支不受其它分支的影响,所以在同一个数据库里可以同时进行多个不同的修改。

  • 主分支(Master)

是 Git 为我们自动创建的第一个分支,也叫主分支,其它分支开发完成后都要合并到 master

  • 分支合并(Merge)

将某分支上的更改联接到此主干或同为主干的另一个分支。

  • 合并冲突(Conflict)

多人对同一文件的工作副本进行更改,并将这些更改提交到仓库。

1.5 标签

标签是用于标记特定的点或提交的历史,通常会用来标记发布版本的名称或版本号(如:publish/0.0.1),虽然标签看起来有点像分支,但打上标签的提交是固定的,不能随意的改动。

1.6 头(HEAD)

头是一个象征性的参考,最常用以指向当前选择的分支。

2.1 Workspace:工作区

  1. 程序员进行开发改动的地方,是你当前看到的,也是最新的;
  2. 平常我们开发就是拷贝远程仓库中的一个分支,基于该分支进行开发。在开发过程中就是对工作区的操作。

2.2 Index / Stage:暂存区

  1. .git 目录下的 index 文件, 暂存区会记录 git add 添加文件的相关信息(文件名、大小、timestamp…),不保存文件实体, 通过 id 指向每个文件实体。可以使用 git status 查看暂存区的状态。暂存区标记了你当前工作区中,哪些内容是被 Git 管理的;
  2. 当你完成某个需求或功能后需要提交到远程仓库,那么第一步就是通过git add 先提交到暂存区,被 Git 管理。

2.3 Repository:本地仓库

  1. 保存了对象被提交 过的各个版本,比起工作区和暂存区的内容,它要更旧一些;
  2. git commit 后同步 index 的目录树到本地仓库,方便从下一步通过 git push 同步本地仓库与远程仓库的同步。

2.4 Remote:远程仓库

远程仓库的内容可能被分布在多个地点的处于协作关系的本地仓库修改,因此它可能与本地仓库同步,也可能不同步,但是它的内容是最旧的。

2.5 四个区域的关系

  1. 任何对象都是在工作区中诞生和被修改;
  2. 任何修改都是从进入 index 区才开始被版本控制;
  3. 只有把修改提交到本地仓库,该修改才能在仓库中留下痕迹;
  4. 与协作者分享本地的修改,可以把它们 push 到远程仓库来共享。

版本控制就是对文件的版本控制:首先要知道文件当前在什么状态,然后才能对文件进行修改、提交等操作,不然可能会提交了现在还不想提交的文件。

3.1 文件的四种状态

  1. Untracked: 未跟踪, 此文件在文件夹中, 但并没有加入到 Git 库, 不参与版本控制.通过 git add 状态变为 Staged;
  2. Unmodify: 文件已经入库, 未修改, 即版本库中的文件快照内容与文件夹中完全一致. 这种类型的文件有两种去处, 如果它被修改, 而变为 Modified. 如果使用 git rm 移出版本库, 则成为 Untracked 文件;
  3. Modified: 文件已修改, 仅仅是修改, 并没有进行其他的操作. 这个文件也有两个去处,通过 git add 可进入暂存 staged 状态, 使用 git checkout则丢弃修改过, 返回到 unmodify 状态, 这个 git checkout 即从库中取出文件, 覆盖当前修改;
  4. Staged: 暂存状态.执行 git commit 则将修改同步到库中, 这时库中的文件和本地文件又变为一致, 文件为 Unmodify 状态. 执行 git reset HEAD filename 取消暂存, 文件状态为 Modified

3.2 查看文件状态

git status # 添加指定文件到暂存区;
git add (dir) # 添加当前目录的所有文件到暂存区。

3.3 移除文件与目录(撤销 add)

git rm --cached # 直接从暂存区删除文件,工作区不做出改变
git checkout . # 用赞寻去全部或指定文件替换工作区的文件,这个操作很危险,会清楚工作区中未添加到暂存区的改动。
git clean # 一般会加上参数 -df,-d 表示包含目录,-f 表示强制清除;

3.4 查看文件修改后的差异

git status # 只能查看对哪些文件做了改动,如果要看改动了什么,可以用;
git diff (files) # 查看文件修改后的差异

此外,还有以下两个常用命令:

git diff --cached # 比较暂存区的文件与之前已经提交过的文件
git diff HEAD~n # 比较 repo 与工作空间中的文件差异

3.5 签出

  • 使用情景

该文件已经存在仓库中,工作区已经对其进行修改过了,如果想撤销修改,可以使用 checkout

检出命令git checkout 是 git 最常用的命令之一,同时也是一个很危险的命令,因为折腾命令会重写工作区。

3.6 提交

通过 add 只是将文件或者目录添加到 index 即暂存区,使用 commit 可以实现将暂存区的文件提交到本地仓库。

git commit -m [message] # 提交暂存区到仓库区
git commit [file1] [file2] ... -m [message] # 提交暂存区的指定文件到仓库区
git commit -a # 提交工作区自上次 commit 之后的变化,直接到仓库区,跳过了 add,对新文件无效
git commit -v # 提交时显示所有 diff 信息

3.7 撤销更新

  • 撤销暂存区更新

    git reset HEAD [filename] # 将暂存区指定文件移出到工作区

  • 撤销本地仓库更新

放弃工作区和暂存区的改动,同时 HEAD 指针指向前一个 commit 对象:

git log  #查看提交日志,
git reset --hard HELD~1  #撤销了上一次的提交,这里的上一次提交日志就不存在了。
cat [filename]  #查看文件内内容
git revert #把指定的提交修改回滚,并同时生成一个新的提交

3.8 日志与历史

git log #查看所有提交日志;
git log [filename] #查看某文件提交日志;
history #查看在 bash 下输入过的指令;
git reflog #查看仓库中所有分支的所有更新记录,包括已经撤销的更新。

4.1 分类介绍

Git 配置文件有三个级别,分别是系统级别、全局级别以及仓库级别。下面的表格展示了各个级别的配置的具体信息:

配置级别

文件位置

配置命令

优先级别

系统

Git 安装目录\etc\gitconfig

git config --system

全局

用户文件夹.gitconfig

git config --global

仓库

仓库文件夹.git\config

git config --local

  • 对全局的 Git 用户名和邮箱进行配置

    git config --global user.name "xxxx" :设置所有仓库提交的用户名
    git config --global user.email "xxxx@example.com":设置所有仓库提交的邮箱

  • 查看所有配置或某一项配置:

    git config --global --list;
    git config --global --get user.name;
    git config --global --get user.email;

系统和当前项目的命令同上,仅需将global改为system/local即可。

创建本地仓库的方法有两种:一种是创建全新的仓库,另一种是克隆远程仓库。

5.1 创建全新仓库

  • 使用当前目录作为 Git 仓库

只需使它初始化:git init ;执行后可以看到,仅仅在项目目录多出了一个.git 目录;

  • 使用指定目录作为 Git 仓库

使用如下命令,可以把创建目录与仓库一起完成:

git init [project-name] # 新建一个目录,将其初始化为 Git 代码库

5.2 克隆远程仓库

  1. 将远程服务器上的仓库完全镜像一份至本地,而不是取某一个特定版本,所以用 clone 而不是 checkout,语法格式为:

    git clone (url)

例如:我们要从克隆的远程仓库托管在 Github 上,首先,我们先前往 Github 上拷贝地址如:

https://github.com/vanDusty/Java-Note,然后执行:

git clone https://github.com/vanDusty/Java-Note```

克隆远程仓库到当前目录。

### 5.3 Git 仓库目录结构

- 初始化仓库使用 `git init` 命令,初始目录(`.git`)结构:

```xml
.git
├── branches
├── config
├── description
├── HEAD
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── prepare-commit-msg.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   └── update.sample
├── info
│   └── exclude
├── objects
│   ├── info
│   └── pack
└── refs
    ├── heads
    └── tags

9 directories, 13 files
  • 这个初始化后的仓库,当创建完分支 dev-branch,并 push 后的目录结构:

    .git/
    ├── branches
    ├── COMMIT_EDITMSG
    ├── config
    ├── description
    ├── HEAD
    ├── hooks
    │ ├── applypatch-msg.sample
    │ ├── commit-msg.sample
    │ ├── post-update.sample
    │ ├── pre-applypatch.sample
    │ ├── pre-commit.sample
    │ ├── prepare-commit-msg.sample
    │ ├── pre-push.sample
    │ ├── pre-rebase.sample
    │ └── update.sample
    ├── index
    ├── info
    │ └── exclude
    ├── logs
    │ ├── HEAD
    │ └── refs
    │ ├── heads
    │ │ ├── dev-branch
    │ │ └── master
    │ └── remotes
    │ └── origin
    │ └── master
    ├── objects
    │ ├── 2d
    │ │ └── d2c71b69c837a7459f4bd9c9f7db6520731d06
    │ ├── 5c
    │ │ └── 7b8eda18a75e13d27c31e65a54b0abd7948510
    │ ├── 77
    │ │ └── cad3aecf7c2754231095598119979d62a1e1da
    │ ├── info
    │ └── pack
    └── refs
    ├── heads
    │ ├── dev-branch
    │ └── master
    ├── remotes
    │ └── origin
    │ └── master
    └── tags

    19 directories, 25 files

5.4 目录结构说明

  • branches 目录

一种不常用的存储速记方式,用于指定 git fetchgit pullgit pushURL ,目前已基本不用。

  • COMMIT_EDITMSG 文件

commit 编辑信息,仅记录最近一次提交的 commit 编辑信息。

  • config 文件

存储当前仓库的配置信息

  • description 文件

用于在 GitWeb 中展示项目的描述信息。

  • HEAD 文件

存储 HEAD 指针,指向当前分支,即:记录当前活动分支。

  • hooks 目录

目录下存储了许多钩子文件(一些脚本),这些文件是各种 Git 命令使用的自定义脚本,可以在 Git 特定阶段自动执行。

  • index 文件

暂存区(stage),二进制文件。

  • info 目录

存储库的其他信息将记录在此目录中。

  • logs 目录

保存所有更新的引用记录。

  • objects 目录

简单理解就是:objects 目录是 Git 的数据库,存储所有数据内容。

  • refs 目录

(1)heads 目录

目录有以各个本地分支名命名的文件,保存对应分支最新提交的 ID,是通过 SHA1 算法计算的一个字符串。

(2)remotes 目录

目录有以各个远程分支名命名的文件,保存对应分支最新提交的 ID,和 heads 目录一个原理。

(3)tags 目录

存储在开发过程中打的标签,里面的文件以标签名命令,文件内容为对应的 ID

Git 是分布式版本控制系统,同一个 Git 仓库,可以分布到不同的机器上,即发布到不同的远程仓库上。

6.1 常见的托管平台

  • GitHub

对,就是那个“全世界最大同性交友网站”。https://github.com

绝大多数好的开源项目都来自 GitHub,但是 GitHub 只能新建公开的 Git 仓库,私有仓库要收费,有时候访问比较卡,如果你做的是一个开源项目,可以首选 GitHub。

  • Gitlab

提到 GitHub 就会自然的想到 Gitlab,Gitlab 支持无限的公有项目和私有项目。https://about.gitlab.com

  • Bitbucket

Bitbucket免费支持 5 个开发成员的团队创建无限私有代码托管库。https://bitbucket.org

  • Gitee 码云

前面说的都是国外的,下面来说几个国内的。码云,个人开发者可免费创建 1000 个项目(不限公有、私有),提供最多 5G 的免费代码存储空间。http://git.oschina.net

  • Coding.net

谈到 Coding.net,首先必须提的是速度快,同样一个账号最多可以创建 1000 个项目(5 个私有),也支持任务的创建等。https://coding.net

6.2 远程仓库操作

申请到了 Git 远程仓库的帐号并创建了一个空的远程仓库现在我们就可以结合本地的仓库与远程仓库一起协同工作了,多人协同开发,这也是我们工作时的情形,这里我们全部使用命令完成(实际开发中很多开发工具例如:IDEA 等,无需命令操作)。

  • 克隆项目

远程操作的第一步,通常是从远程主机克隆一个版本库,这时就要用到 git clone 命令,例如:

git clone https://github.com/vanDusty/Java-Note

该命令会在本地主机生成一个目录,与远程主机的版本库同名。如果要指定不同的目录名,可以将目录名作为 git clone 命令的第二个参数。

git clone <版本库的网址> <本地目录名>

git clone 支持多种协议

除了 HTTP(s)以外,还支持 SSHGit、本地文件协议等。通常来说,Git 协议下载速度最快,SSH 协议用于需要用户认证的场合。各种协议优劣的详细讨论请参考官方文档。

  • git remote

为了便于管理,Git 要求每个远程主机都必须指定一个主机名。git remote 命令就用于管理主机名。

不带选项的时候, git remote 命令列出所有远程主机名。

git remote -v  #参看远程主机的网址。

结果表示,当前只有一台远程主机,叫做 origin,以及它的网址。

origin  git@github.com:jquery/jquery.git (fetch)
origin  git@github.com:jquery/jquery.git (push)

克隆版本库的时候,所使用的远程主机自动被 Git 命名为 origin 。如果想用其他的主机名,需要用 git clone 命令的 -o 选项指定:

git clone -o dusty https://github.com/vanDusty/Java-Note

上面命令表示,克隆的时候,指定远程主机叫做 dusty

更多命令如下:

$ git remote show <主机名>  #查看该主机的详细信息
$ git remote add <主机名> <网址> #添加远程主机
$ git remote rm <主机名> #删除远程主机
$ git remoterename<原主机名> <新主机名> #远程主机的改名
  • git fetch

一旦远程主机的版本库有了更新,需要将这些更新取回本地,这时就要用到该命令。

git fetch  #将某个远程主机的更新,全部取回本地。
git fetch <远程主机名> <分支名> #取回指定主机名的指定分支

所取回的更新,在本地主机上要用"远程主机名/分支名"的形式读取。比如 origin 主机的 master,就要用 origin/master 读取。

git checkout -b newBrach origin/master  #在 origin/master 的基础上,创建一个新分支。

也可以使用 git merge 命令或者 git rebase 命令,在本地分支上合并远程分支。

git merge origin/master #或者$ git rebase origin/master
  • git pull

git pull 命令的作用是,取回远程主机某个分支的更新,再与本地的指定分支合并。它的完整格式稍稍有点复杂。

git pull <远程主机名> <远程分支名>:<本地分支名>

示例:取回 origin 主机的 master 分支,与本地的 develop 分支合并:

git pull origin master:develop

如果远程分支( master)要与当前分支合并,则冒号后面的部分可以省略。可以简写为:

git pull origin master

上面命令表示,取回 origin/master 分支,再与当前分支合并。实质上,这等同于先做 git fetch ,再做 git merge

git fetch origin
git merge master/develop
  • git fetchgit pull 的区别

    git fetch:相当于是从远程获取最新版本到本地,不会自动合并。
    git pull:相当于是从远程获取最新版本并merge到本地

偷个懒,发现一个很全的【Git常用命令参考手册