# 什么是 git?
- Git 是一个免费开源的分布式版本控制系统,它使用一个特殊的名为仓库的数据库来记录文件的变化
- 仓库中的每个文件都有一个完整的版本历史记录,可以看到谁在什么时间修改了哪些文件的哪些内容,在需要时也可以将文件恢复到之前的某个版本
- 有了版本控制系统,我们就可以跟踪每个文件的变化,让项目成员之间的协作更加高效
- 目前世界上最流行的版本控制系统有两种
- 集中式版本控制系统,如 SVN、CVS 等
- 分布式版本控制系统,如 Git、Mercurial 等
# 集中式版本控制系统
- 工作流程
- 所有文件都保存在中央服务器上,每个人的电脑上只保存了一个副本
- 当需要修改文件时,要先从中央服务器上下载最新的版本,然后再添加想要的修改内容,修改完成后再上传回中央服务器

优点
- 使用简单,只需要从中央服务器上下载最新的版本,修改完成后再上传到中央服务器上即可,不需要考虑其他问题
缺点
- 中央服务器的单点故障问题:如果中央服务器出现故障或者网络连接出现问题,那么所有人都无法工作了,只能等待中央服务器或者网络恢复正常
# 分布式版本控制系统
- 工作流程
- 每个人的电脑上都有一个完整的版本库,因此可以在本地进行修改,不需要考虑网络问题,即使中央服务器出现故障,也可以继续工作
- 当需要将修改内容分享给其他人时,只需要将仓库互相同步一下即可

# Git 的初始化配置
git config --global user.name "Your Name" 配置用户名 | |
git config --global user.email "mail@example.com" 配置邮箱 | |
git config --global credential.helper store 存储配置 | |
git config --global --list 查看配置信息 |
- 注:
- 省略(Local):本地配置,只对本地仓库有效
--global
:全局配置,所以仓库生效--system
:系统配置,对所有用户生效
# 新建版本库(仓库)
版本库又叫仓库(Repository,简称 Repo),可以将仓库理解成一个目录,这个目录里面所有的文件都可以被 Git 管理起来,每个文件的修改、删除、添加等操作,Git 都能跟踪到,以便任何时候都可以追踪历史或者还原到之前的某一个版本
创建一个仓库非常简单,只需要将一个目录变成 Git 可以管理的仓库即可
一般来说,可以通过两种方式创建仓库
git init <project-name>
:在自己电脑本地直接创建一个仓库(省略 project-name 则在当前目录创建)git clone <url>
:从远程服务器上克隆一个已经存在的仓库
创建本地仓库



.git
这个目录存放了 Git 仓库的所有数据.git
中的文件和目录都是 Git 仓库的重要组成部分,不能随意删除或者修改这些文件的内容,否则会破坏 Git 仓库克隆远程仓库
git clone https://github.com/geekhall-laoyang/remote-repo.git |

# Git 的工作区域和文件状态
# Git 的工作区域
Git 的本地数据管理分为三个区域,分别是工作区、暂存区、本地仓库
工作区(Working Directory):电脑里能实际看到的目录
暂存区(Staging Area):也叫索引,用来临时存放未提交的内容,一般在 .git 目录下的 index 中
本地仓库(Local Repository):Git 在本地的版本库,它包含了完整的项目历史和元数据,是 Git 存储代码和版本信息的主要位置,仓库信息存储在.git 这个隐藏目录中
远程仓库(Remote Repository):托管在远程服务器上的仓库。常用的有 GitHub、GitLab、Gitee
简单来说
- 工作区是实际操作的目录,暂存区是中间区域,用于临时存放即将提交的修改内容,本地仓库是 Git 存储代码和版本信息的主要位置
流程
- 当修改完工作区的文件后,需要将它们添加到暂存区,然后再将暂存区的修改提交到本地仓库中
该过程可以使用 Git 提供的命令来查看、比较或者撤销修改内容,来保证版本控制的准确性和完整性

# 文件状态
文件状态 | 说明 |
---|---|
未跟踪(Untrack) | 新创建的还没有被 Git 管理起来的文件 |
未修改(Unmodified) | 已经被 Git 管理起来,但是文件的内容没有发生变化,还没有被修改过 |
已修改(Modified) | 已经修改了的文件,但是还没有添加到暂存区里面 |
已暂存(Staged) | 修改后并且已经添加到了暂存区域内的文件 |
已提交(Committed) | 把暂存区的文件提交到本地仓库后的状态 |

# 添加和提交文件
命令 | 说明 |
---|---|
git status | 查看仓库的状态,列出还未提交的新的或修改的文件 |
git log git log --oneline | 查看提交记录 使用 --oneline 参数查看简洁的提交记录 |
git add | 将文件添加到暂存区,等待后续的提交操作 可以使用通配符,如: git add *.txt 可以使用目录,如: git add . |
git commit -m "提交的信息" | 将文件提交到仓库中(只会提交暂存区中的文件) 如果不指定 - m,则会进入交互模式(默认使用 vim 进行编辑) |
- 创建文件,查看文件状态

- 将文件添加到暂存区
git rm --cache <file>
:可以取消暂存

- 将暂存区中的文件提交到仓库中

git add
还可以使用通配符来添加多个文件- 示例:
git add *.txt
- 示例:

git add
还可以接受文件夹作为参数- 比如想要添加所有文件的话,使用
git add .
(这里的点表示当前目录)
- 比如想要添加所有文件的话,使用

git log
:查看提交记录

git log --oneline
:查看简洁的提交记录

# 回退版本
reset 命令用于回退版本,可以退回到之前的某一个提交的状态
- 语法格式
git reset --mixed <commit-id> |
- 三种模式
git reset --soft 表示回退到某一个版本,并且保留工作区和暂存区的所有修改内容 | |
git reset --hard 表示回退到某一个版本,并且丢弃工作区和暂存区的所有修改内容 | |
git reset --mixed 表示回退到某一个版本,并且只保留工作区的修改内容,丢弃暂存区的修改内容 |
mixed 是 reset 命令的默认参数
示例
- 创建三个文件,并依次提交

--soft

- 查看工作区的内容:file3.txt 文件还在

- 查看暂存区的内容

git status

--hard

--mixed

一般来说,当连续提交了多个版本,但又觉得这些提交没有太大意义,可以合并成一个版本时,便可以通过
--soft
和--mixed
这两个参数来进行回退后重新提交这两个参数的主要区别是:在重新提交之前,
--mixed
需要执行一下git add
操作来将变动的内容重新添加到暂存区,而--soft
则不需要,因为暂存区没有被清空--hard
的使用场景:真的要放弃目前本地的所有修改内容(谨慎使用)如果出现了失误操作也没关系,Git 中的所有操作都是可以回溯的,可以使用
git reflog
命令来查看操作的历史记录,然后找到误操作之前的版本号,再使用git reset
命令来回退到该版本即可

# 查看差异

- 语法格式
git diff
- 默认比较的是工作区和暂存区之间的差异内容(因为修改后还没提交到暂存区),会显示发生更改的文件以及更改的详细信息

git diff HEAD
- 比较工作区和版本库之间的差异(添加的内容还没有被提交到仓库中)

git diff --cached
- 比较暂存区和版本库之间的差异

git diff <commit-id> <commit-id>
- 查看两个提交之间的差异

git diff 39ab5d2 HEAD
HEAD
:指向分支的最新提交节点HEAD~ / HEAD^
:表示上一个版本HEAD~数字
:表示提交之前的第 n 个版本
git diff HEAD~3 HEAD file3.txt
- 表示只查看文件 file3.txt 的差异

git diff 分支1 分支2
- 查看两个分支之间的差异,直接加上两个分支名即可
# 删除文件
命令 | 说明 |
---|---|
rm file git add file | 先从工作区删除文件,然后再暂存删除内容 |
git rm <file> | 把文件从工作区和暂存区同时删除 |
git rm --cached <file> | 把文件从暂存区删除,但保留在当前工作区中 |
git rm -r * | 递归删除某个目录下的所有子目录和文件 |
- 删除后记得提交!
# .gitignore
忽略文件
这个文件的作用就是可以忽略掉一些不应该被加入到版本库中的文件,这样可以让仓库体积更小、更加干净
应该忽略哪些文件?
- 系统或软件自动生成的文件,比如一些工具或软件产生的临时文件
- 编译生成的中间文件、可执行文件等
- 运行过程中生成的日志文件、缓存文件、临时文件
- 设计身份、密码、口令、秘钥等敏感信息文件
可以在这个文件中列出需要忽略的文件的模式,这样这些文件就不会被添加到版本库

- 创建两个日志文件

- 将 access.log 添加到 .gitignore 中

- 提交文件后,access.log 并没有被提交到版本库中

.gitignore
文件生效的前提是:文件不能是已经被添加到版本库中的文件*.log
写入文件中,忽略所有日志文件PS:空文件夹是不会被 git 管理起来的
添加要忽略的文件夹:
temp/

- 查看仓库状态:
git status -s
- M 表示文件被修改过

- 提交文件
git commit -am "test ignore folder"
- temp 文件没有被提交到仓库中
-am
指添加到暂存和提交这两步操作合为一步

.gitignore
文件的匹配规则- 从上到下逐行匹配,每一行表示一个忽略模式
- 空行或者以
#
开头的行会被 Git 忽略。一般空行用于可读性的分隔,#
一般用作注释 - 使用标准的 Blob 模式匹配(指 shell 所使用的简化了的正则表达式)
*
通配任意个字符?
匹配单个字符[]
表示匹配列表中的单个字符,比如:[abc] 表示 a/b/c
**
表示匹配任意的中间目录- 中括号可以使用短中线连接,比如
[0-9]
表示任意一位数字,[a-z]
表示任意一位小写字母
!
表示取反- 要忽略指定模式以外的文件或者目录,取反即可
示例
# 忽略所有的.a文件
*.a
# 但跟踪所有的lib.a,即便你在前面忽略了.a文件
!lib.a
# 只忽略当前目录下的TODO文件,而不忽略子目录下的/TODO
/TODO
# 忽略任何目录下名为build的文件夹
build/
# 忽略doc/notes.txt,但不忽略doc/server/arch.txt(doc子目录下面的txt文件)
doc/*.txt
# 忽略doc/目录及其所有子目录下的.pdf文件
doc/**/*.pdf
- GitHub 上提供了各种常用语言的忽略文件的模板,在新建仓库时可以直接使用,也可以根据自己需要进行修改
# 远程仓库
# GitHub / Gitee
# SSH 配置
- 生成 SSH 秘钥
- 输入密钥名称(如果是第一次生成,则直接回车,如果是已经配置过 SSH 密钥了,则需要指定新的名称,如 test)
ssh-keygen -t rsa -b 4096 |

- 复制公钥信息到 GitHub 上

- 如果是指定了新的文件名,需要增加一步新的配置,将以下内容写入 config 文件
tail -5 config
:查看 config 文件最后 5 行内容- 这个配置文件的意思是:当访问 GitHub 时,指定使用 SSH 下的 test 这个秘钥
# github
Host github.com
HostName github.com
PreferredAuthentications publickey
IdentityFile ~/.ssh/test
# 克隆远程仓库
- 生成好 SSH 秘钥之后,回到本地仓库执行
git clone
命令 - Tips:如果创建秘钥的时候没有输入密码,则可以直接回车

注意
- 本地仓库和远程仓库是两个独立的仓库,在本地仓库做的任何修改都不会影响到远程仓库,在远程仓库中做修改也不会影响到本地仓库
- 因此需要一种机制来同步本地仓库和远程仓库的修改内容,让它们的状态保持一致
git push
:把本地仓库的修改推送给远程仓库


git pull <远程仓库名> <远程分支名>:<本地分支名>
:把远程仓库的修改拉取到本地仓库

- 执行完
git pull
,Git 会自动执行一次合并操作,如果远程仓库中的修改内容和本地仓库中的修改内容没有冲突的话,那么合并操作就会成功,否则合并操作就会因为冲突而失败,此时需要手动解决冲突 - 从远程仓库获取内容还可以使用
fetch
命令 - 两者的区别
fetch
只是获取远程仓库的修改,并不会自动合并到本地仓库中,需要手动合并
# 关联本地仓库和远程仓库
git remote add <远程仓库别名> <远程仓库地址> | |
git push -u <远程仓库名> <分支名> |
- 示例
git remote add origin git@github.com:eu8188/first-repo.git | |
git branch -M master # 指定分支的名称为 master,可省略 | |
git push -u origin master:master # 把本地的 master 分支和远程的 origin 仓库的 master 分支关联起来 | |
ps:后面的:master可以省略,因为本地分支的名称和远程分支的名称相同 |
- 执行完再使用
git remote -v
查看- 可以查看当前仓库所对应的远程仓库的别名和地址


- 注:Gitee 的操作和 GitHub 基本一致
# GitLab
- 感觉暂时不需要懂,先空着
# GUI 工具
- GitHub Desktop
- SourceTree
- GitKraken
- VSCode 扩展 GitLens
# 在 VSCode 中使用 Git
ctrl + shift + p:打开命令面板
设置 gitbash 为默认终端
- 打开设置搜索:
terminal.integrated.profiles.windows
- 添加
"git.path": "D:\Develop\Git\bin\git.exe"
"terminal.integrated.profiles.windows": {
"Git Bash": {
"source": "Git Bash",
"path": "D:\\Develop\\Git\\bin\\git.exe",
"args": []
}
},
"terminal.integrated.defaultProfile.windows": "Git Bash"
- 打开设置搜索:

- 文件状态
符号 | 状态 |
---|---|
??(Untracked) | 未跟踪 |
M(Modified) | 已修改 |
A(Added) | 已添加暂存 |
D(Deleted) | 已删除 |
R(Renamed) | 重命名 |
U(Updated) | 已更新未合并 |

# 分支
- 可以把分支看作是代码库中的不同版本,可以独立存在,并且有自己的提交记录
- 分支适合团队协作和开发管理,比如多个开发人员可以在自己的分支上进行开发工作,最后再合并到主线代码库中
- 也可以在一个分支上进行新功能的开发,或者建立一个问题修复的分支来处理一些 bug 和缺陷,这样就可以让主线代码仓库处于一个随时可用的比较稳定的状态,而不会影响到其他功能的开发和测试,保证了项目的正常运行和高效协作
- 分支的优点就是能够提高团队协作的效率,减少冲突和错误的影响,让团队中的每个人都能够独立开发和测试

命令 | 说明 |
---|---|
git branch | 查看分支列表 |
git branch branch-name | 创建分支 |
git checkout branch-name git switch branch-name | 切换分支 |
git merge branch-name | 合并分支 |
git branch -d branch-name 【已合并】git branch -D branch-name 【未合并】 | 删除分支 |
git log --graph --oneline --decorate --all | 查看分支图 |

# 解决合并冲突
如果两个分支修改了同一个文件的同一行代码,Git 就不知道该保留哪个分支的修改内容,也就产生了冲突
- 手动解决一下冲突,看一下要保留哪个内容
# rebase
- 在不同分支上执行
rebase
命令

使用
alias
对命令进行重命名- 如:
alias graph="git log --oneline --graph --decorate --all"
- 如:
示例
- 在 dev 分支上
- 在 main 分支上
Merge
- 优点:不会破坏原分支的提交历史,方便回溯和查看
- 缺点:会产生额外的提交节点,分支图比较复杂
Rebase
- 优点:不会新增额外的提交记录,形成线性历史,比较直观和干净
- 缺点:会改变提交历史,改变了当前分支 branch out 的节点。需要避免在共享分支使用
一般来说,如果只是想把两个分支合并起来,不关心提交历史,使用
git merge
;如果确定只有自己在一个分支上开发,并且希望提交历史更加清晰明了,则使用rebase
# 分支管理和工作流模型
GitFlow
GitHub Flow
分支命名
- 推荐使用带有意义的描述性名称来命名分支
- 版本发布分支 / Tag 示例:v1.0.0
- 功能分支示例:feature-login-page
- 修复分支示例:hotfix-#issueid-desc
分支管理
- 定期合并已经成功验证的分支,及时删除已经合并的分支
- 保存合适的分支数量
- 为分支设置合适的管理权限