前言
Git 基本介绍和学习
在正式学习 Git 之前,我们有必要的了解以下知识:
- Kernel
- Shell
- Git Bash
Kernel
将软件发出的数据转移成数据处理的指令,并提交给 CPU 及电脑中的其他电子组件处理,Kernel 是现代操作系统最基本的部分。
一种为众多应用程序提供安全访问计算机硬件的软件,这种访问是有限的,并且由 Kernel 决定一个程序在什么时候对某部分硬件操作多长时间。
严格地说:内核并不是计算机系统中必要的组成部分。有些程序可以直接地被调入计算机中执行;
这样的设计,说明了设计者不希望提供任何硬件抽象和操作系统的支持;它常见于早期计算机系统的设计中。
但随着电脑技术的发展,最终,一些辅助性程序,例如程序加载器和调试器,被设计到机器内核当中,或者写入在只读记忆体里。
这些变化发生时,操作系统内核的概念就渐渐明晰起来了!
更多参见:参见:Wiki
Shell-Wiki-Baidu
基本概念
Shell 俗称:“壳”,用来和 Kernel (内核)区分。
Shell 是指:为使用者提供操作界面的应用程序(软件)。
对于大多数情况来说,Shell 这个词是指:在 OS(Operation System)中,为访问 Kernel 所提供的一种服务程序,以后如果不特别注明,则 Shell 指的就是命令行式 Shell。
不过,Shell 也可用于泛指所有为用户提供操作界面的程序,也就是程序和用户交互的界面(GUI),与之相对的是:Kernel,Kernel 不提供和用户的交互功能。
因此,Shell 通常分为两类:
- 命令行与图形界面,命令行 Shell 提供一个命令行界面(CLI)
- 图形用户界面(GUI)
图释
命令行式 Shell
命令行式 Shell 有:bash / sh / ksh / csh / zsh(Unix/linux 系统)等。
最为人熟知的命令行 Shell,应该就是 Windows 的 CMD 界面。
传统意义上,Shell 指的是命令行式 Shell,而不是图形用户界面 Shell,以后如果不特别注明,则 Shell 指的就是命令行式 Shell。
Shell 是操作系统的最外层,它管理用户与操作系统之间的交互,为用户提供访问 OS Kernel 的方法。
用户可以通过交互式方法(键盘输入等)和 OS 之间进行通讯,也可以通过非交互式方法和 OS 之间进行通讯。
- 交互式 Shell:等待用户输入命令,再执行输入的命令。
非交互式 Shell:Shell 不与用户进行交互,而是读取存放在文件中的命令,再去执行这些命令,当文件中的命令被执行完比,则 Shell也就终止了。
非交互式使用的是 Shell Script 方法和 OS 之间进行通讯。
Shell Script:放在文件中的一串 Shell 和操作系统命令,它们可以被复用。本质上,Shell Script 是命令行的命令简单的组合到一个文件里面,通过执行该文件去访问 OS。
图形界面 Shell
图形用户界面 Shell:GUI Shell,如最广泛的:Window Explorer(MicroSoft Windows 系列操作系统),
还有:Linux Shell,其中 linux shell 包括 X window manager (BlackBox和FluxBox),以及功能更强大的 CDE、GNOME、KDE、 XFCE。
Git Bash/CMD/GUI
What's the Git Bash?
Git:一组命令行实用程序 或者说 一个开源的分布式版本控制系统(由命令行组成)。
Bash:Bourne-Again-Shell 的缩写,一个命令行界面,一个命令处理器,同时也是一个 Shell(大多数系统,如:Linux/Mac OS X 默认的 Shell),所以它能接收用户的输入且也能从文件(被称为 Script)从读取命令并执行。
Bash 是一个 Unix Shell 和 命令行语言,并且它是 Linux(Ubuntu 等)和 OS X 上的默认 Shell。
Git Bash: 顾名思义,一个 Git 的 Shell(用 Bash 作为 Shell 来用于 Git),适用于 MicroSoft Windows 环境的应用程序,它为 Git 命令行提供一个仿真层,用来作为用户和 OS Kernel 的交互方法;用外行术语来说:任何运行在 Linux 终端设备上的 Git 都被称之为 Git Bash。
如果要在 Windows 上使用 Bash(命令行),需要下载一个名为 Git Bash 的程序,而 Git Bash 则允许我们在 Windows 环境下轻松访问 Bash 以及一个叫做 Git 的工具。
即:Git Bash 能让开发者使用 Bash 去执行所有 Git 的功能,这和 CMD、GUI 是一样的,只不过的方法不同。
What's the Git CMD?
Git CMD 就像带有 git
命令的常规 Windows 命令提示符(CMD)一样,它使开发者可以通过命令行(CMD)使用所有 Git 功能。
Git CMD 相当于 Linux 中的 bad-ass 终端,当开发者在 Windows 上安装 Git 并习惯于使用命令行(CMD)时,就会使用 Git CMD 运行 Git 命令。
What's the Git GUI?
Git GUI 是能让开发使用使用图形用户界面(GUI)去执行 Git 的功能,本质上是针对不喜欢“黑屏”(又称 CMD)编码的用户。
如:开发者要提交一些更改从本地仓库到暂存队列,则使用 Git GUI 需要单击某个按钮提交更改,而使用 Git CMD 则需要使用命令:'git commit -m'
使用 Git Bash/CMD/GUI?
我建议新手应该学习并使用 Git Bash 去使用 Git 的所有功能,除非一些特殊情况的开发者应该使用 Git CMD。
我始终不建议开发者一上来就使用 Git GUI,它会让你无法弄清 Git 的优点以及其中的思想,让你变得呆滞。
What is the Git?
Git 类似于绝大多数的 VCS(Version Control System),但是 Git 在对信息的存储和认知方式上和绝大多数 VCS 有很大差异,理解这些差异将有助与避免使用Git 中的困惑。
直接记录快照,而非差异比较
Git 对待数据的(存储/读取)方法是:Git 像是把数据看作是对小型文件系统的一系列快照。
在 Git 中,每当你提交更新或保存项目状态时,它基本上就会对当时的全部文件创建一个快照并保存这个快照的索引。
为了效率,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。 Git 对待数据更像是一个 快照流。
而其他大部分 VCS 以文件变更列表的方式存储信息,这类系统(CVS、Subversion、Perforce、Bazaar 等等) 将它们存储的信息看作是一组基本文件和每个文件随时间逐步累积的差异 (它们通常称作 基于差异(delta-based) 的版本控制)。
(其他 VCS 的对待数据方式)
(Git 对待数据的方式)
Git 更像是一个小型的文件系统,提供了许多以此为基础构建的超强工具,而不只是一个简单的 VCS。 稍后我们在Git 分支讨论 Git 分支管理时,将探究这种方式对待数据所能获得的益处。
Git 近乎所有操作都是本地执行
Git 保证完整性(有关校验和)
Git 中所有的数据在存储前计算校验和,然后以校验和来引用。
这意味着不可能在 Git 不知情时更改任何文件内容或目录内容。 这个功能建构在 Git 底层,是构成 Git 哲学不可或缺的部分。 若你在传送过程中丢失信息或损坏文件,Git 就能发现。
Git 用以计算校验和的机制叫做 SHA-1 散列(hash,哈希)。 这是一个由 40 个十六进制字符(0-9 和 a-f)组成的字符串,基于 Git 中文件的内容或目录结构计算出来,所以每一个校验和可以认为是一个 Git Repository 的版本(一个 git commit 后保存的一个快照)或者说是一个 Repository 文件
SHA-1 哈希看起来是这样:
24b9da6552252987aa493b52f8696cd6d3b00373
Git 中使用这种哈希值的情况很多,你将经常看到这种哈希值。 实际上,Git 数据库中保存的信息都是以文件内容的哈希值来索引,而不是文件名。
所以你在 Git Bash 中输入 git log 查看 Git 的提交历史,通常只能查看到这串以 40 位二进制替代文件名的引用。
Git 一般只添加数据
Git 的已追踪文件只存在三种状态
Git 有三种状态,你的文件可能处于其中之一:
已提交(committed)
已提交表示数据已经安全地保存在本地数据库中。
已修改(modified)
已修改表示修改了文件,但还没保存到数据库中。
已暂存(staged)
已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
如果 Git 目录中保存着特定版本的文件,就属于 已提交 状态。
如果文件已修改并放入暂存区,就属于 已暂存 状态。
如果自上次检出后,文件作了修改但还没有放到暂存区域,就是 已修改 状态
在 Git 基础(或详见:Basic use to Git) 一章,你会进一步了解这些状态的细节, 并学会如何根据文件状态实施后续操作,以及怎样跳过暂存直接提交。
Git 项目的三个阶段
Git 项目的三个阶段,可以对应 Git 的已追踪文件存在的三种状态:
在工作目录中修改文件(已修改),在暂存区中放入文件(已暂存),在 Git 目录中存放已提交的项目版本快照(已提交)。
Git 目录
存放所有已提交的版本(每次提交都会为 Repository 生成快照)
暂存区
存放所有放入暂存区(git add)但未提交的文件。
工作区
当前所在的 Git Repository 中的某个项目版本。
Git 目录:Git 用来保存项目的元数据和对象数据库的地方。即: .git directory(Repository)
,用来存在项目的所有版本。这是 Git 中最重要的部分,从其它计算机克隆仓库时,复制的就是这里的数据。
暂存区:一个文件,保存了下次将要提交的文件列表信息,一般在 Git 仓库目录中。 按照 Git 的术语叫做“索引”,不过一般说法还是叫“暂存区”。
工作区:项目的某个版本独立提取出来的内容。 这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改。不妨想想,默认情况下,使用 Git Bash 时(或任何一个 Git Shell),开发者一直处于最新的提交中,也就是最新的版本中。
(Git 项目的三个阶段)
Git 常用术语
- 仓库(Repository)
受版本控制的所有文件修订历史的共享数据库 - 工作空间(Workspace)
本地硬盘或 Unix 用户帐户上编辑的文件副本 - 工作树/区(Working tree)
工作区中包含了仓库的工作文件。您可以修改的内容和提交更改作为新的提交到仓库。 - 暂存区(Staging area)
暂存区是工作区用来提交更改(commit)前可以暂存工作区的变化。 - 索引(Index)
索引是暂存区的另一种术语。 - 签入(Checkin)
将新版本复制回仓库 - 签出(Checkout)
从仓库中将文件的最新修订版本复制到工作空间 - 提交(Commit)
对各自文件的工作副本做了更改,并将这些更改提交到仓库 - 冲突(Conflict)
多人对同一文件的工作副本进行更改,并将这些更改提交到仓库 - 合并(Merge)
将某分支上的更改联接到此主干或同为主干的另一个分支 - 分支(Branch)
从主线上分离开的副本,默认分支叫master - 锁(Lock)
获得修改文件的专有权限。 - 头(HEAD)
头是一个象征性的参考,最常用以指向当前选择的分支。 - 修订(Revision)
表示代码的一个版本状态。Git通过用SHA1 hash算法表示的ID来标识不同的版本。 - 标记/标签(Tags)
标记指的是某个分支某个特定时间点的状态。通过标记,可以很方便的切换到标记时的状态。
Basic use to Git
一些重要点
Git 管理目录/文件夹意思
Git 可以管理 Repository 根目录下的子文件夹,但是请注意:Git 并不能管理子文件夹本身,而是管理子文件夹中的子文件,
这个意思为:如果子文件夹中不存在任何文件,则你无法使用 git add demo(文件夹名) 去将文件夹追踪/存入到暂存区,
因为 git add
glob 模式
获取 Git Repository
通常有两种获取 Git 项目仓库的方式:
- 将尚未进行版本控制的本地目录转换为 Git 仓库;
- 从其它服务器 克隆 一个已存在的 Git 仓库。
两种方式都会在你的本地机器上得到一个工作就绪的 Git 仓库。
Git 的初始化
Git 的初始化:使某个文件夹(目录)中的所有文件开始被 Git 管理。
git init
- 在任何一个地方创建一个空文件夹(或使用现有文件),其路径中必须不带中文,且建议不带空格。
- 使用 Git Bash(或其他编辑器),并用 cd 命令转到该文件夹的位置
- 在 Git Bash 中使用 git init 命令,将该文件夹(包括其里面的所有文件,若有)初始化成一个 Git 的 Repository。
Note:git init 命令初始化 Git Repository 完成之后,会在初始化的文件夹中存在一个 .git 子目录(文件夹),
这个子目录含有初始化的 Git Repository 中的所有必须文件,这些文件是 Git 的骨干,即:该子目录是 Git 用来跟踪管理版本库的。
没事千万别动该子目录中的文件,否则可能导致该 Git Repository 损坏,导致整个 Repository 不可用。
如果你想知道 .git 子目录中的文件的用处,参见:Git 内部原理 来了解更多关于到底 .git
文件夹中包含了哪些文件的信息
克隆现有的仓库
使用 git clone
命令可以克隆指定的 Git 仓库服务器上的几乎所有数据。
对于 git clone
命令来说,Git 克隆的是该 Git 仓库服务器上的几乎所有数据,而不是仅仅复制完成你的工作所需要文件,当你执行 git clone
命令的时候,默认配置下远程 Git 仓库中的每一个文件的每一个版本都将被拉取下来。
事实上,如果你的服务器的磁盘坏掉了,你通常可以使用任何一个克隆下来的用户端(开发者所使用的个人计算机)来重建服务器上的仓库 (虽然可能会丢失某些服务器端的钩子(hook)设置,但是所有版本的数据仍在,参见:在服务器上搭建 Git )。
一个简单例子:git clone https://github.com/libgit2/libgit2
以上命令会在当前使用该命令时,所在的路径(目录)下创建一个名为 “libgit2” (默认情况下,使用克隆的仓库的名字作为本地仓库名)的目录(以上路径有两个 libigt2,前一个指的是后一个 libgit2 所在的文件夹的路径,后一个指的是 Repository 的名字),
并在这个目录下初始化一个 .git
文件夹, 从远程仓库拉取下所有数据放入 .git
文件夹,然后从中读取最新版本的文件的拷贝。
如果你进入到这个新建的 libgit2 文件夹,你会发现所有的项目文件已经在里面了,准备就绪等待后续的开发和使用。
如果你想在克隆远程仓库的时候,自定义本地仓库的名字,你可以通过额外的参数指定新的目录名:
$ git clone https://github.com/libgit2/libgit2 mylibgit
这会执行与上一条命令相同的操作,但目标目录名变为了 mylibgit
。
注:Git 支持多种数据传输协议。 上面的例子使用的是 https://
协议,不过你也可以使用 git://
协议或者使用 SSH 传输协议,比如:
user@server:path/to/repo.git
, 在服务器上搭建 Git 将会介绍所有这些协议在服务器端如何配置使用,以及各种方式之间的利弊。
git clone -b
:从 remote 远程仓库中中克隆指定分支
Git 仓库中的文件的已跟踪和未跟踪
初始化 Git Repository 后,那么该 Repository 目录下的所有文件,都可以说存在以下两个状态之一的情况:
- 已跟踪(Tracked)
- 未跟踪(Untracked)
已跟踪的文件:是指那些被纳入了版本控制的文件,在上一次快照中有它们的记录,在工作一段时间后, 它们的状态可能是未修改,已修改或已放入暂存区。简而言之,已跟踪的文件就是 Git 已经知道的文件。
未跟踪的文件:工作目录中除已跟踪文件外的其它所有文件。未跟踪我呢见既不存在于上次快照的记录中,也没有被放入暂存区。
注:初次克隆某个仓库的时候,工作目录中的所有文件都属于已跟踪文件,并处于未修改状态,因为 Git 刚刚检出了它们, 而你尚未编辑过它们。
在开发者编辑过某些文件之后,由于自上次提交后(git commit)你对它们做了修改,Git 会将它们标记为已修改文件。
在工作时,你可以选择性地将这些修改过的文件放入暂存区,然后提交所有已暂存的修改,如此反复,从而在 Git 的 Repository 中更新修改的文件(详见:Git 的两个核心命令)。
查看仓库中文件的状态
Git 的两个核心命令
Git 存在两个核心功能:
- git add
- [git commit [-m "Remarks"(可选)]](https://git-scm.com/book/zh/v2/Git-%E5%9F%BA%E7%A1%80-%E8%AE%...)
总结流程
现在我们知道如何初始化一个 Git Repository(git init
)或 克隆一个仓库(git clone
),也知道如何精确的地将内容添加到(git add)下一次提交(git commit)中,现在让笔者来说一下初始化 Repository 的大概流程吧:
- 在计算机中任意一个位置新建一个文件夹。
- 使用 git init 命令初始化该文件夹目录
- 对该仓库(初始化的文件夹)进行一些配置(可选)
- 精确地将一个/多个/文件目录中的文件添加(git add)到下一次提交
- 修改已追踪文件/暂存区中的文件,对文件内容进行编辑(可选)
- 提交(git commit)暂存区中的已跟踪文件。
- 反复执行 4、5、6(执行过程中可能会有其他命令执行,但是已经超过以上命令的使用,所以这里不予讨论)
基本的工作流程:
- 在工作区(详见:What is the Git - Git 项目的三个阶段)修改文件
- 将你想要下次提交的更改选择性地暂存,这样只会将更改的部分添加到暂存区。
- 提交更新,找到暂存区的文件,将快照永久性存储到 Git 目录。
Reference
- Git Document
- Git Reference 很有用,可以作为 Git Document 的补充,有些注意事项存在于这里。