版本控制(Version control)在软件开发中,可以帮助程序员进行代码的追踪、维护、控制等等一系列的操作
通过版本控制工具,也可以帮助我们在软件开发的过程中,确保由不同人所编辑的同一程序文件都得到同步
简而言之,日常开发中,我们可能需要记录我们什么时候,对哪一个文件进行了那些修改以便于我们进行维护以及版本管理
同时开发中需要多人进行协作开发,那么我们就需要将多人编写的代码在开发中进行合并
为此,我们需要一个工具,可以自动化的实现上述功能,这个工具就是版本控制工具
分类
集中式版本控制
CVS和SVN都是是属于集中式版本控制系统(Centralized Version Control Systems,简称 CVCS)
- 它们的主要特点是单一的集中管理的服务器,保存所有文件的修订版本
- 协同开发人员通过客户端连接到这台服务器,取出最新的文件或者提交更新
但是集中式版本控制也有一个核心的问题:中央服务 器不能出现故障:
-
如果宕机一小时,那么在这一小时内,谁都无法提 交更新,也就无法协同工作
-
如果中心数据库所在的磁盘发生损坏,又没有做恰当备份,毫无疑问你将丢失所有数据
-
即使本地存在对应的代码,因为所有的提交记录和版本迭代信息都是保存在服务器的,本地并不存在
所以也只能恢复对应的代码,所有的提交记录和分支管理将无法恢复
分布式版本控制
Git是属于分布式版本控制系统(Distributed Version Control System,简称 DVCS)
-
客户端并不只提取最新版本的文件快照, 而是把代码仓库完整地镜像下来,包括完整的历史记录
-
这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复
-
因为每一次的克隆操作,实际上都是一次对代码仓库的完整备份
安装
# git和svn的一个比较大的区别是
# svn对于文件的修改存放的是文件的增量更新
# git对于文件的修改存放的是快照
brew install git
三大状态
Git文件(已被版本库管理的文件)的三大状态
| 分类 | 标识 | 说明 |
|---|---|---|
| 已修改 | modified | 在工作目录修改Git文件 修改的部分并没有被纳入到git版本库中 所以新建的文件,也就是从来没有被纳入到暂存区的文件并不是git文件,没有被git所管理 |
| 已暂存 | staged/indexed | 对已修改的文件执行Git暂存操作,将文件存入暂存区 只有纳入暂存区的文件才可以被提交到本地版本库 |
| 巳提交 | committed | 将已暂存的文件执行Git提交操作,将文件存入本地版本库 |
基本命令
# 初始化一个空的≠本地版本库
# 在项目的根目录下生成.git文件夹
# 所以的修改,分支,版本等信息都会被存放在.git文件夹中
# 我们使用git命令来操作git,本质上是在操作.git文件夹
git init
# 新建一个名为text.txt的空文件
touch text.txt
# 使用vim对text.txt进行编辑
# 在命令模式下 输入set number 可以显示每一行的行号
# 输入 数字 --- 跳转到某一个具体行
# dd --- 直接删除一整行
vim text.txt
# 查看text.txt的文件内容
cat text.txt
# 使用默认程序打开某一个文件, 应用程序,网页等
open text.txt
# 打开某个应用程序时,需要加上参数-a
open -a wechat
# 打开某个网页的时候, URL 必须完成,也就是需要带上对应的协议
open http://www.google.com
# 查看git中文件状态
git status
# file 从工作区到暂存区
# file可以是具体的文件名 也可以是 通配符. --- 表示所有的文件
git add <file>
# 从暂存区到本地仓库
# 如果不加上 -m 提交信息 会自动打开vi,要求输入提交信息
# 如果不输入提交信息 那么提交自动失败
# 如果提交成功 会通过sha-1摘要算法生成一个唯一的hash值 被称之为commit id
git commit -m "提交信息"
# 可以使用参数-am, 直接将文件从工作区 提交到 本地版本库
# 但是-am只能将已经纳入git版本库管理的文件直接进行提交
# 如果一个文件是新建的文件,也就是说这个文件并没有被纳入git版本库
# 那么git并没有办法直接使用-am命令将文件直接从工作区提交到本地版本库
git commit -am '提交信息'
# 查看git提交日志
# 日志默认是按照提交顺序 倒序输出
git log
基本配置
git主要有以下的三个git配置文件
| 目录 | 说明 | 优先级 | 配置指令 |
|---|---|---|---|
/etc/gitconfig | 系统级别的配置 针对于整个系统中所有的用户 很少配置 | 最低 | git config --system |
~/.gitconfig | 针对用户的配置 | 较中 | git config --global |
.git/config | 针对某一个具体项目的单独配置 不指定参数时候的默认值 | 最高 | git config --local |
# 直接输入git config 可以查看git config所有的参数信息
git config
# 查看某一个具体属性的配置值 --get 是可以省略的
git config --get user.email
# 查看当前所在的工作目录
pwd
# 创建目录并进入目录
mkdir folder && cd folder
# 查看系统环境变量
# 多个环境变量之间使用空格分割 --- 非标准 约定俗成的 环境变量
echo $path
# 多个环境变量之间使用 : 分割 --- 类Unix系统 环境变量
echo $PATH
# 将Hello Git中的内容输入到text.txt中,并覆盖text.txt中原本的所有内容
# echo默认是将输入的字符串直接输出到终端
echo 'Hello Git' > text.txt
# 跳转到上一个文件夹中
cd -
# 查看git可执行程序所在的路径
which git
# 必须存在一条提交记录后才可以执行git restore 命令
# 从工作区中丢弃文件 --- 早期版本命令为 git checkout -- <file>
# restore相对于暂存区中最后一次暂存的内容,也就是拿暂存区中的文件内容覆盖掉工作区中的文件内容
git restore <file>
# 从暂存区中丢弃文件回工作区,git依旧会对这部分文件进行跟踪
# 早期版本命令为 git reset HEAD <file>
git restore --staged <file>
# 从暂存区中移除整个文件到工作区,也就是本地磁盘
# 在本地库中没有记录的时候(也就是首次提交)
# 可以使用这种方式将提交内容从暂存区恢复到工作区
git rm --cached <file>
# 使用git rm 来删除版本库中的文件 使用git rm -r 删除版本库中的文件夹
# 如果直接从版本库中删除文件,不需要-f 如果从暂存区中直接删除文件 需要加上-f
# git rm <file> <=> rm <file> + git add .
git rm -f <file>
# mv <old-file> <new-file> + git add .
git mv <old-file> <new-file>
# 修改上一次的提交记录
git commit --amend -m 'new commit info'
# 日志查看
git log
# 查看最近n条日志
git log -n
# 以一行显示日志
git log --pretty=oneline
# 以图形化方式显示日志
git log --graph
# 以简写方式输出git日志
# abbrev 是 abbreviation
git log --abbrev-commit
# 查看git帮助信息
git help <command>
man git-<command>
git <command> --help
.gitignore
.gitignore 文件是一个文本文件,它告诉 Git 要忽略项目中的哪些文件或文件夹
本地 .gitignore 文件通常被放置在项目的根目录中
# 忽略任意目录下所有的以a为后缀名的文件
*.a
# 排除foo.a
!foo.a
# 忽略根目录下名为demo的文件
/demo
# 忽略build下的所有文件
build/
# 忽略doc下的所有md文件,但不忽略doc下的文件夹下的所有md文件
# 例如忽略 doc/README.md 不忽略 doc/style/README.md
doc/*.md
# 忽略doc下任意层级的md文件
# 忽略 doc/README.md 也忽略 doc/style/README.md
doc/**/*.md
版本回退
执行git log的结果如下(有四条提交记录)
commit 79e5723d9b9a3e2c519889318716540012d593e1 (HEAD -> master)
Author: klaus <klauswang.2018@gmail.com>
Date: Sun Jan 29 17:31:28 2023 +0800
add hello react
commit 531433024655718bb3da155dda2e1682be5268aa
Author: klaus <klauswang.2018@gmail.com>
Date: Sun Jan 29 17:31:18 2023 +0800
add hello vue
commit 4856b7c3af54c7f307ee717eeff3d08831ef5a80
Author: klaus <klauswang.2018@gmail.com>
Date: Sun Jan 29 17:31:09 2023 +0800
add hello git
commit 17247592469847854751192eead6e36882b74e09
Author: klaus <klauswang.2018@gmail.com>
Date: Sun Jan 29 17:30:52 2023 +0800
add hello world
# 回退到上一个版本
git reset HEAD^
# 回退到上两个版本
git reset HEAD^^
git reset HEAD~2
# 回退到某一个具体的版本
git reset <commit_id>
在版本回退到旧版本的时候,查看提交日志直接使用git log即可
但是git log 输出的事版本的提交记录,也就是说git log只保存旧的提交记录
当前版本往后的新的提交记录是不存在的,此时就可以使用git reflog
# git log --> 查看提交日志
# git reflog --> 查看操作日志
1724759 (HEAD -> master) HEAD@{0}: reset: moving to 1724759246
1724759 (HEAD -> master) HEAD@{1}: reset: moving to HEAD~2
5314330 HEAD@{2}: reset: moving to head^ # rest: xxxx --> 版本回退
79e5723 HEAD@{3}: commit: add hello react # 所以可以从操作日志中看出,最新一次的commit id 是 79e5723
5314330 HEAD@{4}: commit: add hello vue # commit: xxxx --> 新的提交记录
4856b7c HEAD@{5}: commit: add hello git
1724759 (HEAD -> master) HEAD@{6}: commit (initial): add hello world
除了可以使用reset进行版本回退外,也可以使用checkout进行版本的回退
# 切换到某一个具体的提交节点
# 此时仅仅只是head指向了该commit_id
# master永远指向的是最新的一个提交节点
# 所以此时HEAD处于游离状态,也就是HEAD直接指向了提交节点
git checkout <commit_id>
# 此时可以通过新建一个分支 来改变游离状态
# 此时 HEAD就会指向<new_branch_name>,而<new_branch_name>会指向提交节点
git switch -c <new_branch_name>
# 基于<commit_id> 创建一个新分支,新分支名为<new_branch_name>
git branch <new_branch_name> <commit_id>
# 修改当前分支的名称
git branch <old_branch_name> <new_branch_name>
reset vs checkout
工作现场
如果在当前分支的工作区或暂存区中存在未修改的内容时,是不可以切换到另一个分支的
因为切换后git会丢弃工作区和暂存区中文件的修改
只有一种特殊情况,那就是两个分支指针同时指向同一个提交节点的时候,
此时即使工作区或暂存区中存在未提交的修改,也可以在不同分支之间进行切换
所以如果我们在工作区或暂存区中有未提交的修改,此时就可以使用git stash命令来保存现场
在切换分支完成其余操作后,在恢复现场
# 将工作区和暂存区中的内容 缓存起来
git stash
# 在缓存修改的同时,对暂存的内容添加注释(描述)
git stash save '对本次暂存的描述信息'
# 查看所有保存的现场
git stash list
# 恢复最近保存的那次现场,并将其从保存列表中将该现场删除
git stash pop
# 恢复最近保存的现场,但不从保存列表中删除现场
git stash apply
# 恢复某一个具体的现场
# index为索引值, 从0开始,步长为1
git stash apply stash@{<index>}
# 通过git stash apply恢复的现场,并不会再现场列表中将对应的现场给移除
# 所以需要手动进行删除
# 如果没有stash@{<index>} 这个参数就说明需要移除的是现场列表中最新的一次保存的现场保存记录
git stash drop stash@{<index>}