Git概述

官网:https://git-scm.com/

Git 是一个免费和开源的 分布式版本控制系统,旨在以速度和效率处理从小型到大型项目的所有内容。

Git易于学习, 占用空间小,性能快如闪电。它优于 SCM 工具,如 Subversion、CVS、Perforce 和 ClearCase,具有廉价的本地分支、方便的暂存区域和 多个工作流等功能。

其功能:

  • 记录代码的变更(记录开发过程的变更和进程)
  • 实现多人协作(多人的代码进行合并)

版本控制

版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统

可以将选定的文件回溯到之前的状态,甚至将整个项目都回退到过去某个时间点的状态,你可以比较文件的变化细节,查出最后是谁修改了哪个地方,从而找出导致怪异问题出现的原因,又是谁在何时报告了某个功能缺陷等等。 使用版本控制系统通常还意味着,就算你乱来一气把整个项目中的文件改的改删的删,你也照样可以轻松恢复到原先的样子。 但额外增加的工作量却微乎其微

从个人开发过渡到团队协作的

本地版本控制

复制整个项目目录的方式来保存不同的版本,并改名加上备份时间以示区别,有时候会混淆所在的工作目录,一不小心会写错文件或者覆盖意想外的文件。

为了解决这个问题,人们很久以前就开发了许多种本地版本控制系统,大多都是采用某种简单的数据库来记录文件的历次更新差异。

本地版本控制图解

集中式版本控制

如何让在不同系统上的开发者协同工作? 于是,集中化的版本控制系统(Centralized Version Control Systems,简称 CVCS)应运而生。 这类系统,诸如 CVS、Subversion 以及 Perforce 等,都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。 多年以来,这已成为版本控制系统的标准做法。

集中化的版本控制图解

这种做法带来了许多好处,特别是相较于老式的本地 VCS 来说。 现在,每个人都可以在一定程度上看到项目中的其他人正在做些什么。 而管理员也可以轻松掌控每个开发者的权限,并且管理一个 CVCS 要远比在各个客户端上维护本地数据库来得轻松容易。

事分两面,有好有坏。 这么做最显而易见的缺点是中央服务器的单点故障。 如果宕机一小时,那么在这一小时内,谁都无法提交更新,也就无法协同工作。 如果中心数据库所在的磁盘发生损坏,又没有做恰当备份,毫无疑问你将丢失所有数据——包括项目的整个变更历史,只剩下人们在各自机器上保留的单独快照。 本地版本控制系统也存在类似问题,只要整个项目的历史记录被保存在单一位置,就有丢失所有历史更新记录的风险。

分布式版本控制

客户端并不只提取最新版本的文件快照, 而是把代码仓库完整地镜像下来,包括完整的历史记录。 这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。 因为每一次的克隆操作,实际上都是一次对代码仓库的完整备份。

分布式版本控制图解

许多这类系统都可以指定和若干不同的远端代码仓库进行交互。籍此,你就可以在同一个项目中,分别和不同工作小组的人相互协作。 你可以根据需要设定不同的协作流程,比如层次模型式的工作流,而这在以前的集中式系统中是无法实现的。

  • 服务器断网的情况下也可以进行开发(版本控制是在本地进行的)
  • 每个客户端保存的也都说完整的项目(包含历史记录,更加安全)

基本命令

用户签名

签名的作用是区分不同操作者的身份。用户的签名信息在每一个版本的提交信息中能够看到,以此确认此次提交是谁做的。Git首次安装需要设置用户签名,否则无法提交代码(和代码托管中心的账号没有关系)

1
2
3
4
5
6
# 设置用户名
git config --global user.name 用户名
# 设置邮箱
git config --global user.email 邮箱
# 列出Git当前能找到的配置
git config --list

初始化

一个项目只需要初始化一次本地库即可,可通过项目目录查看是否有.git目录来辨别是否初始化过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ git init -h
用法:git init [-q | --quiet] [--bare] [--template=<template-directory>] [--shared[=<permissions>]] [<directory>]

--template <模板目录>
将使用模板的目录
--bare 创建一个裸仓库
--shared[=<权限>]
指定 git 存储库将在多个用户之间共享
-q, --quiet 安静
--separate-git-dir <gitdir>
将 git 目录与工作树分开
-b, --initial-branch <名称>
覆盖初始分支的名称
--object-format <哈希>
指定要使用的哈希算法
# 不指定即当前目录

查看状态

通过查看状态对文件进行检查是否提交,如果文件没有进行提交,则不允许进行下一步操作包括创建分支、拉取代码、切换分支等等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ git status -h
用法:git status [<options>] [--] <pathspec>...

-v, --verbose 详细
-s, --short 简明地显示状态
-b, --branch 显示分支信息
--show-stash 显示存储信息
--ahead-behind 计算完整的提前/落后值
--porcelain[=<版本>]
机器可读的输出
--long 以长格式显示状态(默认)
-z, --null 以 NUL 终止条目
-u, --untracked-files[=<模式>]
显示未跟踪的文件,可选模式:全部、正常、否。 (默认:全部)
--ignored[=<mode>] 显示被忽略的文件,可选模式:传统、匹配、否。 (默认:传统)
--ignore-submodules[=<何时>]
忽略对子模块的更改,可选时:全部、脏、未跟踪。 (默认:全部)
--column[=<style>] 按列列出未跟踪的文件
--no-renames 不检测重命名
-M, --find-renames[=<n>]
检测重命名,可选择设置相似度索引
--no-lock-index (已弃用:使用 `git --no-optional-locks status` 代替)不要锁定索引
1
2
3
4
5
6
7
Tao@DESKTOP-41ETPE3 MINGW64 /e/code (master)
$ git status
在主分支

还没有提交

无需提交(创建/复制文件并使用“git add”进行跟踪)

添加命令

将工作区变更的文件提交至暂存区

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ git add -h
用法: git add [<options>] [--] <pathspec>...

-n, --dry-run 试运行
-v, --verbose 详细

-i, --interactive 交互式拾取
-p, --patch 以交互方式选择帅哥
-e, --edit 编辑当前差异并应用
-f, --force 允许添加其他被忽略的文件
-u, --update 更新跟踪的文件
--renormalize 重新规范化跟踪文件的 EOL(隐含 -u)
-N, --intent-to-add 只记录后面要添加的路径
-A, --all 添加所有已跟踪和未跟踪文件的更改
--ignore-removal 忽略工作树中删除的路径(与 --no-all 相同)
--refresh 不添加,只刷新索引
--ignore-errors 只是跳过由于错误而无法添加的文件
--ignore-missing 检查是否 - 甚至丢失 - 文件在试运行中被忽略
--sparse 允许更新稀疏结帐锥之外的条目
--chmod (+|-)x 覆盖列出文件的可执行位
--pathspec-from-file <文件>
从文件中读取路径规范
--pathspec-file-nul 和 --pathspec-from-file,pathspec 元素用 NUL 字符分隔

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Tao@DESKTOP-41ETPE3 MINGW64 /e/code (master)
$ git add 1.txt
warning: in the working copy of '1.txt', LF will be replaced by CRLF the next time Git touches it
# 对换行符进行转换
Tao@DESKTOP-41ETPE3 MINGW64 /e/code (master)
$ git status
On branch master

No commits yet

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


Tao@DESKTOP-41ETPE3 MINGW64 /e/code (master)

删除命令

1
2
3
4
5
6
7
8
9
10
11
12
13
$ git rm -h
用法: git rm [<options>] [--] <file>...

-n, --dry-run 试运行
-q, --quiet 不列出删除的文件
--cached 仅从索引中删除
-f, --force 覆盖最新检查
-r 允许递归删除
--ignore-unmatch 即使没有匹配项也以零状态退出
--sparse 允许更新稀疏结帐锥之外的条目
--pathspec-from-file <文件>
从文件中读取路径规范
--pathspec-file-nul 和 --pathspec-from-file,pathspec 元素用 NUL 字符分隔

提交命令

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
48
49
50
51
$ git commit -h
用法:git commit [<options>] [--] <pathspec>...

-q, --quiet 成功提交后抑制摘要
-v, --verbose 在提交消息模板中显示差异

Commit message options
-F, --file <file> 从文件中读取消息
--author <author> 覆盖提交的作者
--date <date> 覆盖提交的日期
-m, --message <消息>
提交信息
-c, --reedit-message <提交>
重用和编辑来自指定提交的消息
-C, --reuse-message <提交>
重用来自指定提交的消息
--fixup [(修正|reword):]提交
使用 autosquash 格式的消息来修复或修改/改写指定的提交
--squash <commit> 使用自动压缩格式的消息来压缩指定的提交
--reset-author 提交现在由我创作(与 -C/-c/--amend 一起使用)
--trailer <trailer> 添加自定义预告片
-s, --signoff 添加一个 Signed-off-by 预告片
-t, --template <文件>
使用指定的模板文件
-e, --edit 强制编辑提交
--cleanup <mode> 如何去除消息中的空格和#comments
--status 在提交消息模板中包含状态
-S, --gpg-sign[=<key-id>]
GPG 签署提交

Commit contents options
-a, --all 提交所有更改的文件
-i, --include 将指定文件添加到索引以进行提交
--interactive 交互式添加文件
-p, --patch 以交互方式添加更改
-o, --only 只提交指定的文件
-n, --no-verify 绕过 pre-commit 和 commit-msg 钩子
--dry-run 显示将要提交的内容
--short 简明扼要地显示状态
--branch 显示分支信息
--ahead-behind 计算完整的提前/落后值
--porcelain 机器可读输出
--long 以长格式显示状态(默认)
-z, --null 以 NUL 终止条目
--amend 修改之前的提交
--no-post-rewrite 绕过 post-rewrite 钩子
-u, --untracked-files[=<模式>]
显示未跟踪的文件,可选模式:全部、正常、否。 (默认:全部)
--pathspec-from-file <文件>
从文件中读取路径规范
--pathspec-file-nul 和 --pathspec-from-file,pathspec 元素用 NUL 字符分隔

查看日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ git log -h
用法:git log [<options>] [<revision-range>] [[--] <path>...]
或: git show [<options>] <object>...

-q, --quiet 抑制差异输出
--source 显示源
--use-mailmap 使用邮件映射文件
--mailmap 别名 --use-mailmap
--decorate-refs <模式>
只装饰匹配 <pattern> 的 refs
--decorate-refs-exclude <模式>
不要装饰匹配 <pattern> 的 refs
--decorate[=...] 装饰选项
-L <range:file> 跟踪行范围 <start>,<end> 或 function :<funcname> in <file> 的演变

版本穿梭

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
# git reflog 查看版本信息
# git log 查看版本详细信息
$ git reset -h
用法:git reset [--mixed | --软 | --硬 | --合并 | --keep] [-q] [<提交>]
或: git reset [-q] [<tree-ish>] [--] <pathspec>...
或: git reset [-q] [--pathspec-from-file [--pathspec-file-nul]] [<tree-ish>]
或: git reset --patch [<tree-ish>] [--] [<pathspec>...]
或:已弃用:git reset [-q] [--stdin [-z]] [<tree-ish>]

-q, --quiet 安静,只报告错误
--no-refresh 重置后跳过刷新索引
--mixed 重置 HEAD 和索引
--soft reset only HEAD
--hard reset HEAD、索引和工作树
--merge 重置 HEAD、索引和工作树
--keep reset HEAD 但保留本地更改
--recurse-submodules[=<重置>]
控制子模块的递归更新
-p, --patch 以交互方式选择帅哥
-N, --intent-to-add 仅记录删除的路径将在以后添加的事实
--pathspec-from-file <文件>
从文件中读取路径规范
--pathspec-file-nul 和 --pathspec-from-file,pathspec 元素用 NUL 字符分隔
-z 已弃用(使用 --pathspec-file-nul 代替):路径用 NUL 字符分隔
--stdin 已弃用(使用 --pathspec-from-file=- 代替):从 <stdin> 读取路径
1
2
# 指定文件回到某个版本
git checkout 版本号 文件名

分支

分支概述

使用分支意味着你可以把你的工作从开发主线上分离开来,以免影响开发主线。最简单的可以理解为副本

使用分支可以并行推进多个功能的开发,提高开发效率;例如某分支在开发的过程中出现Bug,不会对其他分支有影响;失败的分支可以删除重新开始

查看分支

1
2
3
git branch -v

git branch

创建分支

1
git branch 分支名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Tao@DESKTOP-41ETPE3 MINGW64 /e/code (master)
$ git branch -v
* master 96bceab there modify

Tao@DESKTOP-41ETPE3 MINGW64 /e/code (master)
$ git branch
* master

Tao@DESKTOP-41ETPE3 MINGW64 /e/code (master)
$ git branch hot-fix

Tao@DESKTOP-41ETPE3 MINGW64 /e/code (master)
$ git branch
hot-fix
* master

Tao@DESKTOP-41ETPE3 MINGW64 /e/code (master)

切换分支

1
git checkout 分支名
1
2
3
4
5
6
7
8
9
10
11
12
13
Tao@DESKTOP-41ETPE3 MINGW64 /e/code (master)
$ git branch hot-fix

Tao@DESKTOP-41ETPE3 MINGW64 /e/code (master)
$ git branch
hot-fix
* master

Tao@DESKTOP-41ETPE3 MINGW64 /e/code (master)
$ git checkout hot-fix
Switched to branch 'hot-fix'

Tao@DESKTOP-41ETPE3 MINGW64 /e/code (hot-fix)

合并分支

1.正常合并

1
2
3
4
git merge 分支名

-----------------------------
# 注意如果要将分支合并到Master需切换到master在合并

2.冲突合并

在合并分支时,两个分支在同一个文件的同一个位置有两条完全不相同的修改。Git无法替我们决定使用哪一个。必须人为决定代码内容

1
2
3
4
在手动合并之后会报错
需要手动修改文件确认需要合并的代码
之后手动添加到暂存区提交本地库
之后合并不需要指定文件名提交

远程仓库

别名

1
2
3
4
# 别名用于代替远程代码托管中心的连接
git remote add xiaowangc https://github.com/qq780312916/xiaowangc.git

# 例如将我的github地址取了一个别名叫做xiaowangc

推送

1
git push 别名 分支
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ git push xiaowangc master
warning: ----------------- SECURITY WARNING ----------------
warning: | TLS certificate verification has been disabled! |
warning: ---------------------------------------------------
warning: HTTPS connections may not be secure. See https://aka.ms/gcm/tlsverify for more information.
warning: ----------------- SECURITY WARNING ----------------
warning: | TLS certificate verification has been disabled! |
warning: ---------------------------------------------------
warning: HTTPS connections may not be secure. See https://aka.ms/gcm/tlsverify for more information.
Enumerating objects: 13, done.
Counting objects: 100% (13/13), done.
Delta compression using up to 16 threads
Compressing objects: 100% (7/7), done.
Writing objects: 100% (13/13), 999 bytes | 999.00 KiB/s, done.
Total 13 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/qq780312916/xiaowangc.git
* [new branch] master -> master

拉取

1
2
3
4
5
$  git pull xiaowangc master

# 存在两种情况
# 将远程(新)代码同步至本地仓库
# 在空仓库拉取到本地仓库,需先初始化git
1
2
3
4
5
6
7
8
9
10
11
12
13
$  git pull xiaowangc master
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 689 bytes | 43.00 KiB/s, done.
From https://github.com/qq780312916/xiaowangc
* branch master -> FETCH_HEAD
9b571ca..0a29e5d master -> xiaowangc/master
Updating 9b571ca..0a29e5d
Fast-forward
2.txt | 1 +
1 file changed, 1 insertion(+)

克隆

1
2
git clone 链接
# clone会进行代码的拉取、初始化本地仓库、创建别名
1
2
3
4
5
6
7
8
$ git clone https://github.com/qq780312916/xiaowangc.git
Cloning into 'xiaowangc'...
remote: Enumerating objects: 16, done.
remote: Counting objects: 100% (16/16), done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 16 (delta 1), reused 12 (delta 0), pack-reused 0
Receiving objects: 100% (16/16), done.
Resolving deltas: 100% (1/1), done.