Git 版本管理命令备忘录

Linux Kernel 是一个非常大规模的开源软件项目,在该项目 1991 ~ 2002 年维护的早期,所有对于代码的修改都是以补丁(Patches)和归档文件(Archived File)的形式进行传递。在 2002 年之后,Linux 内核项目开始使用一款称为 BitKeeper 的商业版本管理工具。但是到了 2005 年,Linux 社区与 BitKeeper 商业公司之间的合作关系破裂,促使 Linux 开源社区以及 Linus Torvalds 本人根据之前的版本管理经验,开发出了全新的分布式版本管理工具 Git

Git 不会以基于文件修改列表的方式来保存数据,而是以及引用快照的方式来保存文件的历史信息;即使没有 Git 版本管理服务器的支持,大部份操作也能够在本地正常进行;Git 当中的所有数据存储时都会经过 SHA-1 完整性校验,并且使用一个长度为 40 的散列字符串来存储和引用这些数据;Git 当中的所有操作都是在添加数据,因而很难执行不可撤消的任务,也很难让出现在删除数据之后无法找回的场景。

文件状态

Git 当中的文件会存在修改(Modified)、暂存(Staged)、提交(Committed)三种状态:

  • 修改(Modified):表示已经修改了文件,但是并没有将这些修改提交到数据库;
  • 暂存(Staged):表示已经将修改之后的文件标记为当前版本,以便进入下一个提交快照;
  • 提交(Committed):意味着数据已经被安全地存储在本地数据库中;

这样就引出了 Git 版本管理项目的工作树(Working Tree)、暂存区(Staging Area)、仓库(Git Directory)三个主要组成部分:

  • 工作树(Working Tree):工作树是项目一个版本的单个签出。这些文件从 Git 目录中的压缩数据库中取出,并放在磁盘上供您使用或修改。
  • 暂存区(Staging Area):暂存区是一个用于存储下一次提交信息的文件,通常包含在 Git 目录当中,很多时候也被称作索引(index);
  • 仓库(Git Directory):仓库是 Git 存储项目元数据和对象数据库的地方,通常位于 Git 仓库当中的 .git 目录;

接下来,展示一个 Git 项目的基本工作流程:

  1. 在工作树当中修改了一些文件;
  2. 有选择性的将需要的内容进行暂存,此时只会将这些更改添加至暂存区
  3. 执行提交,将暂存区的文件快照永久存储至 .git 目录当中;

命令概览

Git 主要命令 功能描述 Git 主要命令 功能描述
add 将文件内容添加到索引中; maintenance 运行一些任务来优化 Git 仓库数据;
am 从邮箱应用一系列的补丁; merge 合并两个或者更多的开发历史;
archive 从一个被命名树创建文件归档; mv 移动或者重命名一个文件、目录、符号链接;
bisect 使用二分查找法来查找引入错误的提交; notes 添加或者检查对象笔记;
branch 列出、创建、删除分支; pull 获取或者集成另一个仓库或者本地分支;
bundle 通过存档移动对象与引用; push 使用相关的对象更新远程引用;
checkout 切换分支或者恢复工作树的文件; range-diff 比较两个提交的范围,例如一个分支的两个版本;
cherry-pick 根据已经存在的提交应用更改; rebase 在另一个分支上的修改合并至当前分支;
citool git-commit 的图形化替代方案; reset 将当前 HEAD 重置为指定的状态;
clean 从当前工作树当中移除没有被跟踪的文件; restore 恢复工作树文件;
clone 克隆一个仓库到一个新的目录; revert 还原一些现有的提交;
commit 记录更改到仓库; rm 从工作树和索引当中移除文件;
describe 基于当前可用的引用,给予对象一个人类可读的名称; shortlog 简化 git log 的输出;
diff 显示提交以及提交之间的更改和工作树等; show 显示各种类型的对象;
fetch 从另一个仓库下载对象与引用; sparse-checkout 初始化和修改 sparse-checkout
format-patch 为电子邮件提交准备补丁; stash 将更改隐藏在一个脏的工作目录当中;
gc 清除不必要的文件并且优化本地存储库; status 展示当前的工作树状态;
gitk Git 仓库浏览器; submodule 初始化、更新或检查子模块
grep 打印符合正则表达式的行; switch 切换分支;
gui 一个可移植的 Git 图形界面 tag 创建、列出、删除、验证一个使用 GPG 签名的标记对象;
init 创建一个空的 Git 仓库,或者重新初始化一个已经存在的仓库; worktree 管理多个工作树;
log 显示提交日志; - -
Git 辅助命令 功能描述 Git 辅助命令 功能描述
config 获取和设置当前仓库或者全局的选项; prune 从对象数据库当中删除所有不可访问的对象;
fast-export Git 快速数据导出; reflog 管理引用日志信息;
fast-import Git 快速数据导入; remote 管理当前被追踪的仓库;
filter-branch 重新写入分支; repack 将未打包的对象打包至仓库之中;
mergetool 支行合并冲突解决工具来解决合合并冲突; replace 通过创建列出删除引用来替换对象;
pack-refs 打包标记高效的访问仓库; annotate 使用提交信息来注释文件的

用户配置

git config 命令用于设置 Git 外观和行为相关的配置变量,这些配置变量保存在 3 个不同的位置,其优先级从高到低如下所示:

  1. /etc/gitconfig 文件: 包含系统上每个用户及其仓库的通用配置,可以通过 --system 选项强制使用该配置文件;
  2. ~/.gitconfig~/.config/git/config 文件:只针对当前用户,可以使用 --global 选项强制使用该配置文件,可以对所有仓库都生效;Windows 系统则会查找用户目录 C:\Users\$USER 下的 .gitconfig 文件;
  3. 当前仓库 Git 目录中的 .git/config 文件:默认情况下用的,也可以通过 --local 选项强制使用该文件;

通过 git config --list --show-origin 命令可以查看所有的 Git 配置,以及这些配置所在的文件位置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
λ git config --list --show-origin

file:D:/Software/Tech/Git/etc/gitconfig core.symlinks=false
file:D:/Software/Tech/Git/etc/gitconfig core.autocrlf=true
file:D:/Software/Tech/Git/etc/gitconfig core.fscache=true
file:D:/Software/Tech/Git/etc/gitconfig color.diff=auto
file:D:/Software/Tech/Git/etc/gitconfig color.status=auto
file:D:/Software/Tech/Git/etc/gitconfig color.branch=auto
file:D:/Software/Tech/Git/etc/gitconfig color.interactive=true
file:D:/Software/Tech/Git/etc/gitconfig help.format=html
file:D:/Software/Tech/Git/etc/gitconfig diff.astextplain.textconv=astextplain
file:D:/Software/Tech/Git/etc/gitconfig rebase.autosquash=true
file:D:/Software/Tech/Git/etc/gitconfig filter.lfs.clean=git-lfs clean -- %f
file:D:/Software/Tech/Git/etc/gitconfig filter.lfs.smudge=git-lfs smudge -- %f
file:D:/Software/Tech/Git/etc/gitconfig filter.lfs.process=git-lfs filter-process
file:D:/Software/Tech/Git/etc/gitconfig filter.lfs.required=true
file:D:/Software/Tech/Git/etc/gitconfig credential.helper=
file:C:/Users/hank/.gitconfig user.name=hank
file:C:/Users/hank/.gitconfig user.email=uinika@outlook.com
file:C:/Users/hank/.gitconfig core.editor=vim

开始使用 Git 之前,需要配置用户名、邮件地址、编辑器,这些信息会写入到每一个提交当中,并且不可更改:

1
2
3
git config --global user.name "Hank"
git config --global user.email uinika@outlook.com
git config --global core.editor vim

如果需要检查当前的所有配置项,则可以通过 git config --list 命令将它们罗列出来:

1
2
3
4
5
6
7
8
9
10
11
λ git config --list

core.symlinks=false
core.autocrlf=true
core.fscache=true
user.name=Hank
user.email=uinika@outlook.com
core.editor=vim
core.autocrlf=false
core.quotepath=false
core.ignorecase=false

当然也可以通过输入 git config <key> 来检查某一项具体的配置:

1
2
3
λ git config user.name

Hank

获取帮助

直接执行 git help 命令,可以获得 Git 的帮助信息概览:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
λ git help
usage: git [--version] [--help] [-C <path>] [-c <name>=<value>]
[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
[-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]
[--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
[--super-prefix=<path>] [--config-env=<name>=<envvar>]
<command> [<args>]

These are common Git commands used in various situations:

start a working area (see also: git help tutorial)
clone Clone a repository into a new directory
init Create an empty Git repository or reinitialize an existing one

work on the current change (see also: git help everyday)
add Add file contents to the index
mv Move or rename a file, a directory, or a symlink
restore Restore working tree files
rm Remove files from the working tree and from the index
sparse-checkout Initialize and modify the sparse-checkout

examine the history and state (see also: git help revisions)
bisect Use binary search to find the commit that introduced a bug
diff Show changes between commits, commit and working tree, etc
grep Print lines matching a pattern
log Show commit logs
show Show various types of objects
status Show the working tree status

grow, mark and tweak your common history
branch List, create, or delete branches
commit Record changes to the repository
merge Join two or more development histories together
rebase Reapply commits on top of another base tip
reset Reset current HEAD to the specified state
switch Switch branches
tag Create, list, delete or verify a tag object signed with GPG

collaborate (see also: git help workflows)
fetch Download objects and refs from another repository
pull Fetch from and integrate with another repository or a local branch
push Update remote refs along with associated objects

'git help -a' and 'git help -g' list available subcommands and some
concept guides. See 'git help <command>' or 'git help <concept>'
to read about a specific subcommand or concept.
See 'git help git' for an overview of the system.

通过 git help <verb>git <verb> --helpman git-<verb> 三种命令格式,可以只查看到指定命令的帮助信息,执行之后会通过默认的 Web 浏览器打开 Git 安装目录 mingw64/share/doc/git-doc 下的 HTML 帮助页面。例如,要获得 git config 命令的帮助信息,只需要执行 git help config 命令即可。

如果无需阅读详细的 HTML 帮助手册,而只需在命令行简单的展示使用参考,则可以采用 -h 选项来获得较为简明的帮助信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
λ git config -h
usage: git config [<options>]

Config file location
--global use global config file
--system use system config file
--local use repository config file
--worktree use per-worktree config file
-f, --file <file> use given config file
--blob <blob-id> read config from given blob object

Action
--get get value: name [value-pattern]
--get-all get all values: key [value-pattern]
--get-regexp get values for regexp: name-regex [value-pattern]
--get-urlmatch get value specific for the URL: section[.var] URL
--replace-all replace all matching variables: name value [value-pattern]
--add add a new variable: name value
--unset remove a variable: name [value-pattern]
--unset-all remove all matches: name [value-pattern]
--rename-section rename section: old-name new-name
--remove-section remove a section: name
-l, --list list all
--fixed-value use string equality when comparing values to 'value-pattern'
-e, --edit open an editor
--get-color find the color configured: slot [default]
--get-colorbool find the color setting: slot [stdout-is-tty]

Type
-t, --type <> value is given this type
--bool value is "true" or "false"
--int value is decimal number
--bool-or-int value is --bool or --int
--bool-or-str value is --bool or string
--path value is a path (file or directory name)
--expiry-date value is an expiry date

Other
-z, --null terminate values with NUL byte
--name-only show variable names only
--includes respect include directives on lookup
--show-origin show origin of config (file, standard input, blob, command line)
--show-scope show scope of config (worktree, local, global, system, command)
--default <value> with --get, use default value when missing entry

获取 Git 仓库

初始化仓库

对于尚未纳入版本管理的工程目录,通过 git init 命令可以将其初始化为一个 Git 仓库,即可以进入 D:/Workspace/test 目录执行该命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
λ git init

hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint:
hint: git config --global init.defaultBranch <name>
hint:
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint:
hint: git branch -m <name>
Initialized empty Git repository in D:/Workspace/test/.git/

也只可以直接采用 git init 工程名称 格式的命令,初始化一个新的工程目录:

1
2
3
λ git init test

Initialized empty Git repository in D:/Workspace/test/.git/

由于 Git 提示没有设置默认初始化分支的名称,需要通过 git config --global init.defaultBranch <name> 进行设置,下面将其设置为 master

1
2
3
4
5
6
7
D:\Workspace\test
λ git config --global init.defaultBranch master

D:\Workspace\test
λ git init

Initialized empty Git repository in D:/Workspace/test/.git/

克隆仓库

如果需要获得一份已经存在的 Git 仓库拷贝,就需要使用到 git clone 命令,例如下面克隆笔者 Github 上的 rhino 工程:

1
2
3
4
5
6
7
λ git clone https://github.com/uinika/rhino.git

Cloning into 'rhino'...
remote: Enumerating objects: 758, done.
remote: Total 758 (delta 0), reused 0 (delta 0), pack-reused 758
Receiving objects: 100% (758/758), 1.03 MiB | 28.00 KiB/s, done.
Resolving deltas: 100% (356/356), done.

如果需要将工程克隆到本地另外一个名称的目录下面,则可以执行 git clone https://github.com/uinika/rhino.git example 命令,在这里 clone 命令后面的 example 就是重命名之后的本地工程名称:

1
2
3
4
5
6
7
λ git clone https://github.com/uinika/rhino.git example

Cloning into 'example'...
remote: Enumerating objects: 758, done.
Recremote: Total 758 (delta 0), reused 0 (delta 0), pack-reused 758eiving objects: 91% (690/758), 364.01 KiB | 350.0Receiving objects: 92% (698/758), 364.01 KiB | 350.00 KiB/s
Receiving objects: 100% (758/758), 1.03 MiB | 750.00 KiB/s, done.
Resolving deltas: 100% (356/356), done.

记录更新

已经纳入 Git 管理的工程目录下,每一个文件都具有已跟踪未跟踪两种状态。初次克隆某个仓库时,该目录当中的所有文件都处于已跟踪(Tracked)并且未修改(Unmodified)状态,当某个文件被编辑之后就会被标记为已修改(Modified)状态,此时可以选择将其放置到暂存区,使其处于暂存状态(Staged),如果此时再新加入一个文件,则该文件则会处于未跟踪(Untracked)状态,具体可以参考下面的示意图:

检查文件状态

使用 git status 命令查看哪些文件处于什么状态,克隆一个新仓库之后立即使用该命令,可以查看到类似如下输出:

1
2
3
4
5
6
λ git status

On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

由于 git status 命令的输出十分详细,可以采用 -s 或者 --short 选项简化状态信息的输出:

1
2
3
λ git status -s

λ git status --short

跟踪新文件

在工程下新建一个 README.md 文件,然后执行 git status 查看工程状态:

1
2
3
4
5
6
7
8
9
10
11
λ git status

On branch master

No commits yet

Untracked files:
(use "git add <file>..." to include in what will be committed)
README.md

nothing added to commit but untracked files present (use "git add" to track)

运行 git add 命令可以将 README.md 放置到暂存区,然后再运行 git status 命令查看工程状态:

1
2
3
4
5
6
7
8
9
10
λ git add README.md

λ git status
On branch master

No commits yet

Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: README.md

接下来,对 README.md 文件进行修改之后再保存,再一次运行 git status 查看工程状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
λ git status

On branch master

No commits yet

Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: README.md

Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README.md

观察上面的执行结果会发现 README.md 文件同时出现了在暂存区非暂存区,实际上 Git 仅仅暂存了执行 git add 命令时的版本,修改之后的 README.md 需要重新通过 git add 命令将其添加至暂存区

配置 .gitignore

.gitignore 文件用于配置无需纳入 Git 的文件,通常是一些日志文件,或者编译过程中创建的临时文件等,其配置的格式规范如下面列表所示:

  1. 所有空行或者以 # 开头的行都会被 Git 忽略;
  2. 可以使用标准的 Glob 模式匹配,它会递归的应用到整个 Git 工程;
  3. 匹配模式使用 / 开头可以防止递归,而以 / 结尾则表示当前指定的是目录;
  4. 要忽略指定模式以外的文件或目录,可以在模式前加上感叹号 ! 进行取反;

Glob 模式是一种 Shell 上使用的简化过的正则表达式:

  1. 星号 * 匹配零个或者多个任意字符;
  2. [abc] 匹配任意一个列在方括号中的字符;
  3. 问号 ? 只匹配一个任意字符;
  4. 如果在方括号当中使用短划线 - 分隔两个字符,表示所有在这两个字符范围内的都可以匹配;
  5. 使用两个星号 ** 表示匹配任意中间目录;
1
2
3
4
5
6
7
8
9
10
11
12
# 忽略全部 .exe 文件
*.exe
# 跟踪全部 main.exe,即使前面已经忽略了 .exe 文件
!main.exe
# 只忽略当前目录下的 TODO.md 文件,而不忽略子目录下的 TODO.md
/TODO.md
# 忽略所有目录下名为 build 的文件夹
build/
# 忽略 doc/note.txt,但不会忽略 doc/web/note.txt
doc/*.txt
# 忽略 src/ 及其子目录下的所有 .c 文件
src/**/*.c

查看暂存的修改

README.md 文件添加字符串 修改内容!,然后执行 git diff 命令查看尚未暂存的文件被更新了哪些部分,该命令比较的是工程当中目标文件与暂存区域快照之间的差异,也就是修改之后还没有暂存起来的变化内容:

1
2
3
4
5
6
7
8
9
λ git diff

diff --git a/README.md b/README.md
index e69de29..2432758 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1 @@
+修改内容!
\ No newline at end of file

查看提交历史

撤消操作

远程仓库的使用

打标签

Git 别名

分支管理

Git 分支简介

分支的新建与合并

分支管理

分支开发工作流

远程分支

变基

选择修订版本

交互式暂存

贮藏与清理

签署工作

搜索

重写历史

重置揭密

高级合并

Rerere

使用 Git 调试

子模块

打包

替换

凭证存储