基础
git
(读音为/gɪt/)是一个开源的分布式版本控制软件
可以有效、高速地进行从很小到非常大的项目版本管理。 也是Linux之父Linus Torvalds为了帮助管理Linux内核开发而开发的一个开放源码的版本控制软件
具体来说,就是用来管理代码项目的软件
作为一名软件开发者,肯定会有很多代码项目,这些项目就可以使用
git
来提交至基于git
的代码存储平台如github
、gitee
、gitlab
等等,且可以管理它们的不同版本,还可以去克隆别的开发者的开源项目,拉取他们的代码学习,而且git
命令适用于所有基于git
的代码存储平台综上,
git
应是一名软件开发者的必备技能
安装
windows下安装非常方便,只需官网下载安装程序,按指引安装即可
安装完成后,打开cmd终端输入
git --version
# 上方简写
git -v
git --version
# 上方简写
git -v
出现相应git版本,说明安装成功
可以使用线上的练习网站,gitlearn, 这需要已经对git命令有了基础的了解,进行实战练习
基本概念
Git 工作区、暂存区和版本库概念:
- **工作区:**就是在电脑里能看到的目录
- **暂存区:**英文叫 stage 或 index。一般存放在 .git 目录下的 index 文件(.git/index)中,暂存区有时也叫作索引(index)
- **版本库:**工作区有一个隐藏目录 .git,这个不算工作区,而是 Git 的版本库
工作区、暂存区和代码库
在 Git 中,有三个主要的区域用于存储代码更改:工作区、暂存区和代码库。
- 工作区:工作区是您在计算机上实际编写代码的地方。它是您在编辑器或 IDE 中打开的目录或文件夹。
- 暂存区:暂存区是一个存储将要提交到代码库的更改的区域。它实际上是一个文件,称为索引(index),它记录了所有将要提交的更改的元数据和内容快照。
- 代码库:代码库是 Git 用于存储历史记录的地方。它包含了所有已提交的更改和提交记录,以及分支和标签等其他元数据。
当您在工作区中修改了文件并准备提交更改时,您需要使用 git add
命令将更改添加到暂存区。在 git add
后,更改的内容将被复制到暂存区,等待进一步提交。
使用 git commit
命令提交暂存区中的更改将把它们保存到代码库中,这样它们就成为 Git 历史记录的一部分了。
因此,可以将暂存区视为一个缓冲区,用于准备提交到代码库的更改。工作区是实际编辑代码的地方,而代码库是保存历史记录的地方。
查看状态
显示文件修改情况,是否添加到本地仓库了
git status
git status
.gitignore
为 npm 包添加.gitignore,文件内容加入,代码提交至仓库时就会跳过.gitignore 中的内容,如以下代码就会跳过 node_modules
node_modules
node_modules
如果不想提交.gitignore,可以在.git/info/exclude 中加入以下代码排除它自身
.gitignore
.gitignore
合并分支/git merge
git merge有四种参数
- --ff 平常什么都不加的时候,就使用默认的 --ff , 即 fast-forward 方式
- -no-ff
- -squash
- --no-squash
非常常见的使用场景:主分支master的代码不动,开发在dev分支开发,完成需求并测试没有bug后再合并dev分支的代码到主分支
一、开发分支(dev)上的代码达到上线的标准后,要合并到 master 分支
git checkout dev
git add .
git commit -m "dev"
git pull
# 如果冲突,解决冲突
git add .
git commit -m "fix"
git checkout master
git merge dev
git push -u origin master
git checkout dev
git add .
git commit -m "dev"
git pull
# 如果冲突,解决冲突
git add .
git commit -m "fix"
git checkout master
git merge dev
git push -u origin master
二、当master代码改动了,需要更新开发分支(dev)上的代码
git checkout master
git add .
git commit -m "master"
git pull
# 如果冲突,解决冲突
git add .
git commit -m "fix"
git checkout dev
git merge master
git push -u origin dev
git checkout master
git add .
git commit -m "master"
git pull
# 如果冲突,解决冲突
git add .
git commit -m "fix"
git checkout dev
git merge master
git push -u origin dev
--ff/fast-forward
Git 合并两个分支时,如果顺着一个分支走下去可以到达另一个分支的话,那么 Git 在合并两者时,只会简单地把指针右移,叫做“快进”(fast-forward)不过这种情况如果删除分支,则会丢失merge分支信息
git merge --no-ff的作用
在许多介绍 Git 工作流的文章里,都会推荐在合并分支时,加上 --no-ff
参数:
git checkout dev
git merge --no-ff feature
git checkout dev
git merge --no-ff feature
--no-ff
在这的作用是禁止快进式合并
Git 合并两个分支时,如果顺着一个分支走下去可以到达另一个分支的话,那么 Git 在合并两者时,只会简单地把指针右移,叫做“快进”(fast-forward),比如下图:
A---B---C feature
/
D---E---F master
A---B---C feature
/
D---E---F master
要把 feature 合并到 master 中,执行以下命令
git checkout master
git merge feature
git checkout master
git merge feature
结果就会变成
A---B---C feature
/ master
D---E---F
A---B---C feature
/ master
D---E---F
因为 feature 就在 master 的下游,所以直接移动了 master 的指针,master 和 feature 都指向了 C。而如果执行了 git merge --no-ff feature
的话,是下面的结果:
A---B---C feature
/ \
D---E---F-----------G master
A---B---C feature
/ \
D---E---F-----------G master
由于 --no-ff
禁止了快进,所以会生成一个新的提交,master 指向 G
从合并后的代码来看,结果其实是一样的,区别就在于 --no-ff
会让 Git 生成一个新的提交对象。为什么要这样?
通常我们把 master 作为主分支,上面存放的都是比较稳定的代码,提交频率也很低,而 feature 是用来开发特性的,上面会存在许多零碎的提交,快进式合并会把 feature 的提交历史混入到 master 中,搅乱 master 的提交历史。所以如果你根本不在意提交历史,也不爱管 master 干不干净,那么 --no-ff
其实没什么用。不过,如果某一次 master 出现了问题,你需要回退到上个版本的时候,比如上例,你就会发现退一个版本到了 B,而不是想要的 F,因为 feature 的历史合并进了 master 里
合并分支-git rebase
git merge的缺点:当合并两个没有上下游关系的分支时,git会自动生成一个merge commit,记录此次的merge,该操作本身没有问题,问题是如果遇到特殊情况需要反复merge的时候,就会导致commit的提交记录非常混乱
git rebase和git merge区别
- merge 是一个合并操作,会将两个分支的修改合并在一起
- merge 的提交历史忠实地记录了实际发生过什么,关注点在真实的提交历史上面
- rebase 并没有进行合并操作,只是提取了当前分支的修改,将其复制在了目标分支的最新提交后面
简单来说,就是merge会保留分支记录,rebase则不会
具体命令如下
git checkout dev
git rebase master
git checkout master
git merge dev
git checkout dev
git rebase master
git checkout master
git merge dev
git stash
暂存
切换分支一般有以下四种情况:
当前分支工作区和缓存区是干净的
也就是在commit之后再没做任何更改,往别的分支切换时没有影响当前分支有更改,与其他分支更改了不同的地方,未commit
分支切换的时候,会自动合并当前分支有更改,与其他分支更改了相同的地方,未commit
切换分支操作会被拒绝,提示当前分支的更改内容没有提交至远程仓库当前分支有更改,已经commit
往别的分支切换时没有影响
第二种情况会导致分支代码错乱,第三种情况会导致不能切换分支
针对第二种和第三种情况,使用git stash
命令解决
示例:开发一个功能,还没开发完,这时来了一个更紧急的需求,需要先去实现这个需求
自然而然想要切换一个分支去实现,但切换分支操作会被拒绝,但当前分支的更改内容没有提交至远程仓库,因为还没开发完,不太适合提交,所以这时就可以使用git stash了
git stash将当前分支的所有改动全部存起来,然后当前分支的项目就会直接恢复到最后一次commit之前的代码了,这时就可以切换分支,去完成那个紧急的需求了
# 存储改动
git stash
# 查看存储的改动列表
git stash list
# 恢复改动
git stash apply
# 删除存储的改动列表
git stash drop
# 或者使用,等价于apply和drop,直接恢复和删除
git stash pop
# 存储改动
git stash
# 查看存储的改动列表
git stash list
# 恢复改动
git stash apply
# 删除存储的改动列表
git stash drop
# 或者使用,等价于apply和drop,直接恢复和删除
git stash pop
管理远程仓库地址
# 查看远程仓库地址
git remote -v
# 添加远程仓库地址到本地仓库中,如已存在,会提示exist
git remote add origin https://git-repo-address.git
# 修改远程仓库地址
git remote set-url origin https://git-repo-address.git
# 删除远程仓库地址
git remote remove origin
# 查看远程仓库地址
git remote -v
# 添加远程仓库地址到本地仓库中,如已存在,会提示exist
git remote add origin https://git-repo-address.git
# 修改远程仓库地址
git remote set-url origin https://git-repo-address.git
# 删除远程仓库地址
git remote remove origin
commit
git log查看commit的哈希,然后回退到相应commi
git reset --hard f36801544670e00b2f59a28e19017d2786c4085e
git reset --hard f36801544670e00b2f59a28e19017d2786c4085e
修改才提交的commit
git commit --amend
git commit --amend
Fast-forward
默认情况下直接使用 git merge
命令,没有附加任何选项命令的话,那么应该是交给 git 来判断使用哪种 merge 模式,实际上 git 默认执行的指令是 git merge -ff
指令(默认值)
什么是 Fast-forward
git merge会自动执行了 Fast-forward
操作 Fast-forward
是指 Master 合并 Feature 时候发现 Master 当前节点一直和 Feature 的根节点相同,没有发生改变,那么 Master 快速移动头指针到 Feature 的位置,所以 Fast-forward 并不会发生真正的合并,只是通过移动指针造成合并的假象,这也体现 git 设计的巧妙之处。
先简单介绍一下 git merge
的三个合并参数模式:
- -ff 自动合并模式:当合并的分支为当前分支的后代的,那么会自动执行
--ff (Fast-forward)
模式,如果不匹配则执行--no-ff(non-Fast-forward)
合并模式 - --no-ff 非 Fast-forward 模式:在任何情况下都会创建新的 commit 进行多方合并(及时被合并的分支为自己的直接后代)
- --ff-onlu Fast-forward 模式:只会按照
Fast-forward
模式进行合并,如果不符合条件(并非当前分支的直接后代),则会拒绝合并请求并且推出
选择
三种 merge 模式没有好坏和优劣之分,只有根据团队的需求和实际情况选择合适的合并模式才是最优解,那么应该怎么选择呢? 给出以下推荐:
- 如果你是小型团队,并且追求干净线性 git 历史记录,那么我推荐使用
git merge --ff-only
方式保持主线模式开发是一种不错的选择 - 如果你团队不大不小,并且也不追求线性的 git 历史记录,要体现相对真实的 merge 记录,那么默认的
git --ff
比较合适 - 如果你是大型团队,并且要严格监控每个功能分支的合并情况,那么使用
--no-ff
禁用Fast-forward
是一个不错的选择