git 中head、工作树、和索引分别是什么,有什么关系和区别
HEAD
在git中,可以存在很多分支,每一个分支本质上是一个指向commit对象的可变指针,
HEAD可以理解为一个特别的指针,是一个指向开发者正在工作中的本地分支的指针。简单来讲,开发者目前在哪儿,HEAD 就指向哪儿。
例如当前我们处于master分支,所以HEAD这个指针指向了master分支指针
![](https://file.jishuzhan.net/article/1788507490648854530/de4d937efbb96839d375a4eb74588b55.webp)
当我们切换分支的时候,HEAD指针通常指向我们所在的分支,当我们在某个分支上创建新的提交时,分支指针总是会指向当前分支的最新提交
所以,HEAD指针 --------> 分支指针 --------> 最新提交
工作树和索引
工作树:开发者实际操作的目录,其实就是工作区域。
索引:为了向本地仓库提交做准备的区域,也就是暂存区
区别
从所在的位置来看:
- HEAD 指针通常指向我们所在的分支,当我们在某个分支上创建新的提交时,分支指针总是会指向当前分支的最新提交
- 工作树是查看和编辑的(源)文件的实际内容
- 索引(暂存区)是放置你想要提交给 git仓库文件的地方,如工作树的代码通过 git add 则添加到 git 索引中,通过git commit 则将索引区域的文件提交到 git 仓库中
协同开发中如何保证自己本地代码最新
在协同开发项目时,为了确保本地仓库代码是最新的,可以遵循以下步骤:
1.获取远程仓库更新,首先需要从远程仓库获取最新的代码更新。可以使用以下命令:
sql
git fetch <remote_name>
<remote_name>
通常是 origin(默认的远程仓库别名)或你指定的其他远程仓库名称。这个命令会从远程仓库获取所有分支的最新提交,但不会自动合并到你当前的分支。
2.切换到需要更新的分支:通常你需要在主开发分支(如 master 或 main)上更新代码。使用以下命令切换分支:
xml
git checkout <branch_name>
3.合并远程分支更新:将远程分支的更新合并到你当前的本地分支:
xml
git merge <remote_name>/<branch_name>
4.处理合并冲突(如果有) 。如果发生合并冲突,Git 会标记出有冲突的文件。你需要手动编辑这些文件,解决冲突,然后使用以下命令将已解决的文件添加到暂存区:
xml
git add <file1> <file2> ...
5.最后,提交合并更改:
sql
git commit -m "Merge remote branch into local branch"
需要注意的是:git pull
: 这个命令相当于执行 git fetch
后再执行 git merge
,即拉取远程更新并自动合并到你的本地分支。
通常情况下,你可以使用 git pull 命令来快速获取最新的代码并合并到你的本地分支,以保持代码的最新状态。
如何添加远端仓库的新分支到本地库
如果远程仓库新增了一些你本地仓库中没有的分支,想要保证本地代码是最新的,可以按照以下步骤操作:
1.获取远程仓库更新
首先从远程仓库获取所有分支的最新更新:
sql
git fetch <remote_name>
<remote_name> 通常是 origin 或你指定的远程仓库别名。
2.查看远程分支列表
使用以下命令查看所有远程分支:
git branch -r
这将列出所有远程分支,新增的分支会在列表中显示。
3.创建本地分支跟踪远程分支
对于你想要跟踪的每个新远程分支,创建一个本地分支来跟踪它:
xml
git checkout -b <new_local_branch> <remote_name>/<remote_branch>
将 <new_local_branch>
替换为你要创建的本地分支名称,<remote_name>/<remote_branch>
替换为你想要跟踪的远程分支的完整名称。
执行这个命令将自动创建一个新的本地分支,并将其与指定的远程分支关联起来。
4.切换到新建的本地分支
使用以下命令切换到新建的本地分支:
xml
git checkout <new_local_branch>
5.更新本地分支代码
现在你已经在新建的本地分支上了,使用以下命令从远程分支获取最新代码并合并到本地分支
git pull
这将从关联的远程分支获取最新代码并合并到你当前的本地分支。
重复步骤 3-5
,为每个你想要跟踪的新远程分支创建本地分支并获取最新代码。
通过这些步骤,你可以确保本地代码包含远程仓库中所有分支的最新更改,并且可以在本地创建新分支来跟踪远程分支的进展。定期从远程仓库获取更新,并在本地分支上合并这些更新,是保持本地代码最新的好方法。
常见的Git命令
git的操作可以通过命令的形式如执行,日常使用就如下图6个命令即可
![](https://file.jishuzhan.net/article/1788507490648854530/371ea858eb80d115ee46eb65d41e0584.webp)
实际上,如果想要熟练使用,超过60多个命令需要了解,下面则介绍下常见的的git命令
配置
Git自带一个 git config 的工具来帮助设置控制 Git外观和行为的配置变量,在我们安装完git之后,第一件事就是设置你的用户名和邮件地址
后续每一个提交都会使用这些信息,它们会写入到你的每一次提交中,不可更改
设置提交代码时的用户信息命令如下:
- git config [--global] user.name "[name]"
- git config [--global] user.email "[email address]"
启动
一个git项目的初始有两个途径,分别是:
- git init [project-name]:创建或在当前目录初始化一个git代码库
- git clone url:下载一个项目和它的整个代码历史
日常基本操作
在日常工作中,代码常用的基本操作如下:
- git init 初始化仓库,默认为 master 分支
- git add . 提交全部文件修改到缓存区
- git add <具体某个文件路径+全名> 提交某些文件到缓存区
- git diff 查看当前代码 add后,会 add 哪些内容
- git diff --staged查看现在 commit 提交后,会提交哪些内容
- git status 查看当前分支状态
- git pull <远程仓库名> <远程分支名> 拉取远程仓库的分支与本地当前分支合并
- git pull <远程仓库名> <远程分支名>:<本地分支名> 拉取远程仓库的分支与本地某个分支合并
- git commit -m "<注释>" 提交代码到本地仓库,并写提交注释
- git commit -v 提交时显示所有diff信息
- git commit --amend [file1] [file2] 重做上一次commit,并包括指定文件的新变化
关于提交信息的格式,可以遵循以下的规则:
- feat: 新特性,添加功能
- fix: 修改 bug
- refactor: 代码重构
- docs: 文档修改
- style: 代码格式修改, 注意不是 css 修改
- test: 测试用例修改
- chore: 其他修改, 比如构建流程, 依赖管理
分支操作
- git branch 查看本地所有分支
- git branch -r 查看远程所有分支
- git branch -a 查看本地和远程所有分支
- git merge <分支名> 合并分支
- git merge --abort 合并分支出现冲突时,取消合并,一切回到合并前的状态
- git branch <新分支名> 基于当前分支,新建一个分支
- git checkout --orphan <新分支名> 新建一个空分支(会保留之前分支的所有文件)
- git branch -D <分支名> 删除本地某个分支
- git push <远程库名> :<分支名> 删除远程某个分支
- git branch <新分支名称> <提交ID> 从提交历史恢复某个删掉的某个分支
- git branch -m <原分支名> <新分支名> 分支更名
- git checkout <分支名> 切换到本地某个分支
- git checkout <远程库名>/<分支名> 切换到线上某个分支
- git checkout -b <新分支名> 把基于当前分支新建分支,并切换为这个分支
远程同步
远程操作常见的命令:
- git fetch [remote] 下载远程仓库的所有变动
- git remote -v 显示所有远程仓库
- git pull [remote] [branch] 拉取远程仓库的分支与本地当前分支合并
- git fetch 获取线上最新版信息记录,不合并
- git push [remote] [branch] 上传本地指定分支到远程仓库
- git push [remote] --force 强行推送当前分支到远程仓库,即使有冲突
- git push [remote] --all 推送所有分支到远程仓库
撤销
- git checkout [file] 恢复暂存区的指定文件到工作区
- git checkout [commit] [file] 恢复某个commit的指定文件到暂存区和工作区
- git checkout . 恢复暂存区的所有文件到工作区
- git reset [commit] 重置当前分支的指针为指定commit,同时重置暂存区,但工作区不变
- git reset --hard 重置暂存区与工作区,与上一次commit保持一致
- git reset [file] 重置暂存区的指定文件,与上一次commit保持一致,但工作区不变
- git revert [commit] 后者的所有变化都将被前者抵消,并且应用到当前分支
reset:真实硬性回滚,目标版本后面的提交记录全部丢失了
revert:同样回滚,这个回滚操作相当于一个提价,目标版本后面的提交记录也全部都有
存储操作
你正在进行项目中某一部分的工作,里面的东西处于一个比较杂乱的状态,而你想转到其他分支上进行一些工作,但又不想提交这些杂乱的代码,这时候可以将代码进行存储
- git stash 暂时将未提交的变化移除
- git stash pop 取出储藏中最后存入的工作状态进行恢复,会删除储藏
- git stash list 查看所有储藏中的工作
- git stash apply <储藏的名称> 取出储藏中对应的工作状态进行恢复,不会删除储藏
- git stash clear 清空所有储藏中的工作
- git stash drop <储藏的名称> 删除对应的某个储藏
git diff命令
git diff 命令用于比较文件内容或者不同提交之间的差异。它可以显示文件之间的修改、暂存区和工作区之间的差异,以及提交之间的差异。
下面是一些常用的 git diff 用法:
比较工作区和暂存区的差异:
git diff
这条命令会显示当前工作区中与暂存区之间的差异。它会列出所有已修改但未暂存的文件的详细差异,包括添加、删除和修改的行。
比较暂存区和最新提交之间的差异:
git diff --staged
这条命令会显示暂存区中与最新提交之间的差异。它会列出所有已暂存但未提交的更改的详细差异。
比较两个提交之间的差异:
git diff <commit1> <commit2>
这条命令会显示两个提交之间的差异。你可以使用提交的哈希值、分支名或者标签名来指定提交。它会列出这两个提交之间所有文件的详细差异。
比较工作区中某个文件与最新提交之间的差异:
git diff <file>
这条命令会显示工作区中某个文件与最新提交之间的差异。它会列出文件的详细差异。
git diff
命令还支持许多选项和参数,例如限制显示差异的行数、忽略空白、只显示某些文件等。通过使用这些选项,你可以根据需要对比文件或提交之间的差异,并了解到修改的细节。
![](https://file.jishuzhan.net/article/1788507490648854530/7f67a72072e45256a358ffa4aa083210.webp)
![](https://file.jishuzhan.net/article/1788507490648854530/78f9b50087d0fbf87628ad70603006a2.webp)
![](https://file.jishuzhan.net/article/1788507490648854530/e82a986619ecae597bf52b42a22f1d97.webp)
git reset命令
git reset
用于回退版本,可以遗弃不再使用的提交,它可以移动 HEAD 指针以及修改工作区和暂存区的状态。
执行遗弃时,需要根据影响的范围而指定不同的参数,可以指定是否复原暂存区或工作区内容
- --hard 将 HEAD 指针移动到指定的提交,并且强制重置暂存区和工作区的内容为指定提交的内容。这意味着之前的更改完全被删除,工作区被重置为指定提交的状态。
- --mixed(默认) 默认的行为是将 HEAD 指针移动到指定的提交,并且将暂存区的内容重置为指定提交的内容,但是工作区的内容不受影响。
- --soft 。git reset 仅仅移动了 HEAD 指针,不会更改暂存区和工作区的状态。这意味着,之前提交的更改被"撤销",但是这些更改会被保留在暂存区和工作区中,等待重新提交。
arduino
// 没有指定ID, 暂存区的内容会被当前ID版本号的内容覆盖,工作区不变
git reset
// 指定ID,暂存区的内容会被指定ID版本号的内容覆盖,工作区不变
git reset <ID>
日志ID可以通过查询,可以git log进行查询
假设现在项目新增文件test.js
,并已经commit提交到本地仓库当中。但是目前发现test.js中存在一定错误,可以考虑使用reset
命令进行回退。
![](https://file.jishuzhan.net/article/1788507490648854530/caa4ad53c955d280f6a9eadefaf0b0d1.webp)
如果采用默认reset ,git reset HEAD~1,则为mixed模式,本地库的指针移动的同时,重置暂存区,但是工作区不动。因此提示 untracker files
![](https://file.jishuzhan.net/article/1788507490648854530/2affe046cd9b1021069cddbe968e3af6.webp)
如果采用soft reset,test.js 文件处于 "Changes to be committed" 的状态。这表示该文件已经被添加到 Git 的暂存区(Staging Area),但尚未被提交(commit)
![](https://file.jishuzhan.net/article/1788507490648854530/7813a832e407484dc264c7c98269ef41.webp)
如果采用hard reset,会将本地仓库、暂存区、工作目录(硬盘)的改动全部撤销,所以test.js文件的改动在本地也会全部丢失。
![](https://file.jishuzhan.net/article/1788507490648854530/1d491fd2e7f64675b109757a9b151256.webp)
![](https://file.jishuzhan.net/article/1788507490648854530/ac73d2b889381ebf71f4487d3e3f40c8.webp)
如何找回reset hard不小心丢失的改动
或者说如何找回本地库删除的文件
根据上一节内容,我们不小心使用 resethard
模式将 本地仓库、暂存区、工作目录(硬盘)的改动全部撤销,所以test.js文件的改动在本地也会全部丢失。
但是,如果你之前曾经提交过这些文件的更改,你仍然可以通过 Git 的历史记录找回这些更改。
git reflog
命令。这个命令会显示你所进行的所有操作,包括提交、分支切换和重置等。你可以使用 git reflog 命令找到之前的提交记录,然后使用 git checkout 或者 git reset --hard 命令将代码恢复到你需要的状态。
例如如下示例,我不小心将test.js改动通过hard模式撤销,那我通过 reflog
命令查看历史操作,
然后使用 reset --hard <id>
来恢复到所需要的状态。
![](https://file.jishuzhan.net/article/1788507490648854530/9eb4b961028f9d4532d03bfe1eaa9f4a.webp)
如何找回暂存区删除的文件
删除了工作区的文件,并通过add命令将删除操作同步到了暂存区,这时该如何找回?同样使用git reset --hard命令,索引为git log中最新的一条,也可以使用 git reset --hard HEAD
![](https://file.jishuzhan.net/article/1788507490648854530/0ff34646ebd8e32ac349e89a08259d7f.webp)
![](https://file.jishuzhan.net/article/1788507490648854530/53df42aa8a7ab6091fa8b1ca9bc7aa5b.webp)
git reset 和 git revert区别
git reset
git reset
用于回退版本,可以遗弃不再使用的提交
执行遗弃时,需要根据影响的范围而指定不同的参数,可以指定是否复原索引或工作树内容
- --hard 本地库的指针移动的同时,重置暂存区,重置工作区
- --mixed(默认) 本地库的指针移动的同时,重置暂存区,但是工作区不动
- --soft 本地库的指针移动的时候,暂存区,工作区都不动
arduino
// 没有指定ID, 暂存区的内容会被当前ID版本号的内容覆盖,工作区不变
git reset
// 指定ID,暂存区的内容会被指定ID版本号的内容覆盖,工作区不变
git reset <ID>
日志ID可以通过查询,可以git log进行查询,如下:
git revert
git revert
在当前提交后面,新增一次提交,抵消掉上一次提交导致的所有变化,不会改变过去的历史,主要是用于安全地取消过去发布的提交
跟git reset用法基本一致,git revert 撤销某次操作,此次操作之前和之后的 commit和history都会保留,并且把这次撤销,作为一次最新的提交,如下:
xml
git revert <commit_id>
区别
撤销(revert
)被设计为撤销公开的提交(比如已经push)的安全方式,git reset被设计为重设本地更改
因为两个命令的目的不同,它们的实现也不一样:重设完全地移除了一堆更改,而撤销保留了原来的更改,用一个新的提交来实现撤销
两者主要区别如下:
- git revert是用一次新的commit来回滚之前的commit,git reset是直接删除指定的commit
- git reset 是把HEAD向后移动了一下 ,而git revert是HEAD继续前进,只是新的commit的内容和要revert的内容正好相反,能够抵消要被revert的内容
- 在回滚这一操作上看,效果差不多。但是在日后继续 merge 以前的老版本时有区别
git revert是用一次逆向的commit"中和"之前的提交,因此日后合并老的branch时,之前提交合并的代码仍然存在,导致不能够重新合并
但是git reset是之间把某些commit在某个branch上删除,因而和老的branch再次merge时,这些被回滚的commit应该还会被引入
- 如果回退分支的代码以后还需要的情况则使用git revert, 如果分支是提错了没用的并且不想让别人发现这些错误代码,则使用git reset
常用git撤销操作
![](https://file.jishuzhan.net/article/1788507490648854530/d192edc9161c7a90a0e47f11f21dadfc.webp)
如果是在工作区对文件进行修改,并想要进行撤销时,可以使用git checkout 或 git restore命令。
![](https://file.jishuzhan.net/article/1788507490648854530/b9149ffcd2516b73337a73b43d8efc8d.webp)
如果某文件已经提交到暂存区,我们想要将其从暂存区撤销到工作目录,并且不变动该文件存在的更改(不对硬盘本地的代码文件进行更改),则使用git reset命令或 git restore --staged
![](https://file.jishuzhan.net/article/1788507490648854530/86199dd3ff543190cdc07e587ee28b43.webp)
如果某文件已经提交到暂存区,想要将对应修改撤销,并且不保留对该文件存在的更改(不保留硬盘本地的代码文件的更改),使用git checkout HEAD xxx
![](https://file.jishuzhan.net/article/1788507490648854530/3df8273faa1e4edde54ac7da3c2f6c66.webp)
![](https://file.jishuzhan.net/article/1788507490648854530/e6bebd4bd9ffbb4342032c5e2e3b1d5b.webp)
![](https://file.jishuzhan.net/article/1788507490648854530/496b7ab17205acafcd8cefaf9a8bc34d.webp)
![](https://file.jishuzhan.net/article/1788507490648854530/43d16820bb01a36630a02282a3730693.webp)
分支管理
在版本控制过程中,我们可以通过多个分支来同时推进多个任务,这就是分支的意义。不同任务可以并行开发,互相不影响,提高开发效率。并且以一个真实的业务场景举例:
假如线上出现紧急BUG,需要修复并上线。假设我们线上生产环境用的是 master 分支, 平时开发使用
develop 分支开发, 遇到线上问题的时候, 可以基于 master 分支上复制出一个新分支取名为 temp_bug的临时分支,
![](https://file.jishuzhan.net/article/1788507490648854530/2a42ac9e71b7515ba051392c8e49930c.webp)
有一个分支单独用来修复 BUG, 确认修复无误后, 再将这个分支提交的代码合并到 master 分支中, 然后将
master 分支上线, 就不会影响我们正在开发的新功能了
查看分支
分支分为两种, 一种是本地已经存在 的, 还有一种是服务器上有,但本地不存在的分支, 查看的方式稍有区别。
Git 查看分支的命令为 git branch
, 在不带参数的情况下, 默认查看到的是本地分支列表,
![](https://file.jishuzhan.net/article/1788507490648854530/ff827c256c0366dd2c6d3190c42c0aef.webp)
在图中只看到一个 develop 并且为绿色, 说明当前本地的仓库中只有一个分支, 绿色则代表当前所处的分支。
如果为 git branch
加上-a
参数,可以理解为 all 的意思, 就是查看本地的分支和远程的所有分支 , 参考命令如下:在图中可以看到列出了很多个分支名, 其中包含了绿色的和红色的。 红色的有个特点: 都是以 remotes/ 开头, 这说明这个分支并不在本地
![](https://file.jishuzhan.net/article/1788507490648854530/b8bddb707c10836d1c6cc45bd3209ff8.webp)
新建分支
新建分支有两种情况, 第一种是远程已经有的分支, 需要把它在本地也同样创建一份 ; 还有一种是新建一个远端和本地都不存在的分支
新建本地分支法1
新建一个远程和本地都不存在的分支方式, 和切换分支区别不大, 只需要在 git checkout 分支名
中间加入参数 -b
即可。 通过这种方式, 创建的分支结构和内容会与当前所在的分支一模一样, 也就是说, 它其实就是复制了当前的分支
bash
git checkout -b test
![](https://file.jishuzhan.net/article/1788507490648854530/cde7ab3810f7d02ee23bc7450e6080b2.webp)
可以看到会告诉你切换分支成功, 但没有提示你对应远程的分支名 , 因为远程现在还不存在这个分
支。 所以你此时如果使用 git push 是不能直接推送的, 需要在第一次推送的时候指定远程分支名, 参考命令如下图所示:
arduino
git push --set-upstream origin test
![](https://file.jishuzhan.net/article/1788507490648854530/4312a2d5943f5a121eb93f1c9c704c6f.webp)
新建本地分支法2
使用 git branch 和 git checkout 命令
xml
git branch <branch-name>
这个命令会创建一个名为 的新分支,但不会切换到这个分支上。然后,使用 git checkout 命令切换到新创建的分支上:
xml
git checkout <branch-name>
或者,如果你使用的是 Git 版本 >= 2.23,也可以使用 git switch 命令:
导入远程分支法1:
远程分支:远程已经有的分支, 需要把它在本地也同样创建一份
使用 git checkout <branch-name>
命令时,如果本地不存在这个分支,Git 会尝试从远程仓库获取同名的分支,并在本地创建这个分支。在执行前, 需要确保这个分支在远程或者本地已经存在
即使 git checkout 命令的主要目的是切换分支,但它也可以用来创建本地分支
![](https://file.jishuzhan.net/article/1788507490648854530/a0484da357bc7d6fcc7988b97e6aca97.webp)
导入远程分支法2:
还可以使用 git fetch 命令来导入远程分支。git fetch origin <branch-name>:
这条命令会从远程仓库 origin
下载指定分支 <branch-name>
到本地,但不会自动创建本地分支,只是将远程分支的内容下载到本地仓库中。你可以在之后手动创建一个本地分支来跟踪这个远程分支:
xml
git checkout -b <local-branch-name> origin/<branch-name>
这样就可以将远程分支导入到本地,并创建一个本地分支来跟踪远程分支的变化。
合并分支
假设我们现在有三个分支, 分别是: master 生产环境、 develop 开发环境、 test bug 修复分支。 现在我在 test
分支中修复了 BUG, 需要发布到线上生产服务器上, 那么我们就可以通过合并分支的方式将 test 分支的代码合并到master 分支中去
合并的时候, Git 会对比两个分支代码的差异, 当 test 分支处于比较新的状态时候, Git 会将 test 分支的代码和提交记录复制到 master 分支中来, 合并代码的参考命令如下:
bash
git merge test
在没有代码冲突的情况下, 执行合并命令的返回结果信息如下图所示
![](https://file.jishuzhan.net/article/1788507490648854530/a5bf899048887b358bb3da57cc3da487.webp)
删除分支
一般用来修复 BUG 的分支, 我们可以把它当做是一个临时分支, 当合并到 master 之后, 就可以删除掉它了, 这样可以减少 Git 仓库的体积。 删除分支有两种方式, 普通删除 和强制删除
Git 中删除分支的命令为 git branch -d
分支名 , 其中参数 -d 代表的是普通删除。 一般情况下, 删除使用普通删除即可。 普通删除相对来说比较安全, 避免造成数据丢失的情况, 参考命令如下
bash
git branch -d test
在极少数情况下你可能会遇到普通删除无法将分支删除的情况, 这个时候你可以将 -d 替换成 -D , 这样就可以进行强制删除, 参考命令如下
bash
git branch -D test
如何解决代码冲突问题
代码冲突产生的原因就是在于一个文件在不同的地方(分支或暂存区) 被修改, 然后又需要在一个地方进行合并, 就会让 Git 无法确定使用哪一个版本, 从而产生代码冲突, 让开发者自己来判断, 解决代码冲突。
一般团队协作中遇到的三种冲突解决思路和流程, 分别是: 拉取远程仓库时候的冲突、 rebase 复制其他分支版本冲突、暂存区冲突等,
解决代码冲突通常需要去编辑冲突的文件, 将需要的代码进行保留, 不需要的代码给删除, 清理完不需要的代码之
后需要通知 Git 让它继续执行任务, 不同场景的冲突, 通知 Git 的方式并不一样, 分为以下几种:
-
git merge 和 git pull 命令导致的冲突, 处理完冲突后使用 git commit -a;
-
git rebase 命令导致的冲突, 处理完冲突之后使用 git rebase --continue 或 git rebase --skip ;
-
git stash apply 命令导致的冲突, 处理完冲突之后使用 git add . 即可。
拉取远程仓库时候的冲突
平时在提交代码之前通常会拉取一下代码, 所以拉取代码命令用的比较多, 冲突也比较多常见
例如有一个文件bb.txt,一个人在本地对它进行了修改,而远程仓库也对bb.txt进行了修改。这时使用 git pull 命令拉取代码便会产生冲突, 因为远程仓库也修改了 bb.txt 文件, 而本地仓库也修改了此文件, Git 不知道以哪一个版本为准
![](https://file.jishuzhan.net/article/1788507490648854530/f151392d643afbf1e861b679eb8a185c.webp)
这个时候需要我们手动去编辑文件, 将需要的版本保留, 不需要的代码删除 , 这里我使用 vim
命令编辑冲突文件
![](https://file.jishuzhan.net/article/1788507490648854530/334af5dd5dd33432576e045b33d2a642.webp)
当 Git 无法执行自动合并时,因为更改在同一区域,它会用特殊字符来表示冲突的区域。这些字符序列是这样的:
- <<<<<<<
- =======
<<<<<<<和=======之间的所有内容都是你的本地修改。 这些修改还没有在远程版本库中。=======和>>>>>>>之间的所有行都是来自远程版本库或另一个分支的修改。现在你需要研究这两个部分并做出决定。
可以看到冲突的两个版本内容, 远程的版本在下方, 而本地的版本在上方, 我们只保留 22222 , 然后保
存并退出, 接着执行 git commit -a
命令就可以完成解决冲突
![](https://file.jishuzhan.net/article/1788507490648854530/175731c8e1b46983ba7cd455305b3d09.webp)
可以看到 Git 需要输入备注信息, 这里可以填写代码冲突解决的一些备注信息, 也可以使用默认信息, 然
后 :x 保存并退出即可, 此时就已经成功解决完冲突
rebase冲突
使用 git rebase 命令复制另外一个分支的版本记录到当前分支时候也有可能产生代码冲突问题。我们同样使
用一个实例进行演示;
在两个分支中, 同时去修改同一个文件并且同一行代码, 这样 Git 就不能自动选择版本, 从而制造一个冲突出
来。
首先切换到 test 分支中,接着我们随意修改一个代码文件, 这里以 aa.txt 文件为例,执行命令如下所示
![](https://file.jishuzhan.net/article/1788507490648854530/d2f28b88a0d60276c6c898639aed24dc.webp)
文件 aa.txt 已经被修改, 我们将修改的内容提交到一个新版本中去
![](https://file.jishuzhan.net/article/1788507490648854530/7ed895a158d1f40df9fd26c9cb5150c6.webp)
接下来切换到develop分支,修改 aa.txt 文件并提交一个版本, 同样我们将工作区的改动提交到新版本当中
![](https://file.jishuzhan.net/article/1788507490648854530/8e153802973c5a8c45e73a0b786dfeb9.webp)
目前两个分支都对同一个文件进行了修改。
现在我们切换到 test
分支中去, 然后使用 git rebase
命令将刚才在 develop
分支的提交复制到 test
中去
![](https://file.jishuzhan.net/article/1788507490648854530/78a6a93ed133c83860ed0f8e290342f6.webp)
接下来手动解决冲突,使用 vim 命令, 只保留正确的代码, 将不正确的代码删除,
![](https://file.jishuzhan.net/article/1788507490648854530/22a6e06a3b848b14032adb2b450b79c8.webp)
最终修改完成之后,让 git rebase
的任务继续执行, 这个时候我们需要使用下面的命令来触发:
![](https://file.jishuzhan.net/article/1788507490648854530/fa99927b862525ae88d719a595dae3ea.webp)
暂存区冲突
将工作区的改动暂时存储起来的时候一样, 也会出现代码冲突问题
以一个小例子为例, 首先切换到 develop 分支,随意修改一个代码文件aa.txt,并将修改使用git stash
临时存储起来。
![](https://file.jishuzhan.net/article/1788507490648854530/37c642b5c1739fe82b77e1c7d559fd47.webp)
接着我再一次去编辑 aa.txt 文件, 然后将内容改为与之前不一样的内容,
![](https://file.jishuzhan.net/article/1788507490648854530/f1f2b9a32603517af835742e1f3997dc.webp)
![](https://file.jishuzhan.net/article/1788507490648854530/485ce383afc72bdeaf177083bec5462a.webp)
可以看到 aa.txt 文件处于修改状态了, 现在我们使用 git commit 命令将这个修改提交到新版本中。在这个基础上我再来使用 git stash apply
命令从暂存区恢复修改
![](https://file.jishuzhan.net/article/1788507490648854530/b73a868cd4af061443340208bf2e7e1b.webp)
如何处理stash冲突呢?先使用 git status
查看文件状态,可以看到 aa.txt 文件为 双方修改 , 说明这个文件存在冲突问题,
![](https://file.jishuzhan.net/article/1788507490648854530/5c587301c74a9bb3f2dc6c8eac5288a1.webp)
我们同样使用 vim 命令将里面不需要的代码删掉, 留下需要的部分即可,然后使用 git add 命令将工作区的修改提交, 再查看文件状态,看到 aa.txt 文件已经不再处于 双方修改 的状态了, 此时我们按照正常流程开发即可。
![](https://file.jishuzhan.net/article/1788507490648854530/1f72c3d70de73af4ff839d858e15f845.webp)
对git stash 的理解?应用场景?
stash,译为存放,在 git 中,可以理解为保存当前工作进度,会把暂存区和工作区的改动进行保存,这些修改会保存在一个栈上
后续你可以在任何时候任何分支重新将某次的修改推出来,重新应用这些更改的代码
默认情况下,git stash
会缓存下列状态的文件:
- 添加到暂存区的修改(staged changes)
- Git跟踪的但并未添加到暂存区的修改(unstaged changes)
但以下状态的文件不会缓存:
- 在工作目录中新的文件(untracked files)
- 被忽略的文件(ignored files)
如果想要上述的文件都被缓存,可以使用-u或者--include-untracked可以工作目录新的文件,使用-a或者--all命令可以当前目录下的所有修改
关于git stash常见的命令如下:
- git stash
- git stash save
- git stash list
- git stash pop
- git stash apply
- git stash show
- git stash drop
- git stash clear
应用场景
有时候线上出现紧急 bug, 需要把已经提交的版本发布到线上,这个时候还没有开发完整的改动就比较尴尬。如果需要去切换分支去合并代码, Git 会提示有未提交的改动, 而不允许切换分支;遇到这种问题时候, 可以使用这一节中的 git stash 命令, 将工作区改动临时存储起来, 然后就可以切换分支了。等处理完一切事物后, 再回到开发分支将暂存区域恢复, 继续进行开发。
当你在项目的一部分上进行修改但是还没有开发完毕, 而这时你想要切换到另一个分支或者拉下远端的代码去做一点别的事情
但是不想对一次未完成的代码的commit提交,这时候就可以使用 git stash
例如以下场景:
当你的开发进行到一半,但是代码还不想进行提交 ,然后需要同步去关联远端代码时.如果你本地的代码和远端代码没有冲突时,可以直接通过git pull解决
但是如果可能发生冲突怎么办.直接git pull会拒绝覆盖当前的修改,这时候就可以依次使用下述的命令:
- git stash
- git pull
- git stash pop
或者当你开发到一半,现在要修改别的分支问题的时候,你也可以使用git stash缓存当前区域的代码
- git stash:保存开发到一半的代码
- git commit -m '修改问题'
- git stash pop:将代码追加到最新的提交之后
git stash
保存当前工作进度,会把暂存区和工作区的改动保存起来
git stash save
git stash save可以用于存储修改.并且将git的工作状态切回到HEAD也就是上一次合法提交上
如果给定具体的文件路径,git stash只会处理路径下的文件.其他的文件不会被存储,其存在一些参数:
- --keep-index 或者 -k 只会存储为加入 git 管理的文件
- --include-untracked 为追踪的文件也会被缓存,当前的工作空间会被恢复为完全清空的状态
- -a 或者 --all 命令可以当前目录下的所有修改,包括被 git 忽略的文件
git stash list
显示保存进度的列表。也就意味着,git stash命令可以多次执行,当多次使用git stash命令后,栈里会充满未提交的代码,如下:
![](https://file.jishuzhan.net/article/1788507490648854530/f858cffa1abb5fe45a1188b47aac0ab3.webp)
其中,stash@{0}、stash@{1}就是当前stash的名称
git stash pop
git stash pop 从栈中读取最近一次保存的内容,也就是栈顶的stash会恢复到工作区
也可以通过 git stash pop + stash名字执行恢复哪个stash恢复到当前目录
如果从stash中恢复的内容和当前目录中的内容发生了冲突,则需要手动修复冲突或者创建新的分支来解决冲突
git stash apply
将堆栈中的内容应用到当前目录,不同于git stash pop,该命令不会将内容从堆栈中删除
也就说该命令能够将堆栈的内容多次应用到工作目录中,适应于多个分支的情况
同样,可以通过git stash apply + stash名字执行恢复哪个stash恢复到当前目录
git stash show
查看堆栈中最新保存的stash和当前目录的差异
通过使用git stash show -p查看详细的不同
通过使用git stash show stash@{1}查看指定的stash和当前目录差异
![](https://file.jishuzhan.net/article/1788507490648854530/1147e51757207e3e90221e30704c247c.webp)
git stash drop
git stash drop + stash名称表示从堆栈中移除某个指定的stash
git stash clear
删除所有存储的进度
git rebase的理解
git merge命令和git rebase命令,这两个命令都是用来合并代码的。在基准分支上合并目标分支的代码,会将目标分支的提交记录全部前置到基准分支的最新提交记录之前。在 master 分支上使用了rebase命令之后,Git 将 feature 分支上面的所有 commit 记录都前置到了 master 分支的最新 commit 记录之前。
![](https://file.jishuzhan.net/article/1788507490648854530/4542f03ac86447e9fb701837e5434e68.webp)
rebase是以 commit 为维度的,按 commit 提交的顺序依次进行合并操作;如果在合并的过程中,某个 commit 遇到了冲突,则需要我们先解决该冲突,然后才能继续进行合并操作。特别地,在我们解决冲突之后,需要使用git add + 冲突文件命令将当前冲突标记为已解决,然后使用git rebase --continue命令继续合并操作。
通过上面的描述,我们能发现merge和rebase有一个很明显的差异,那就是当遇到冲突的时候,使用merge命令,我们只需要解决一次冲突即可;使用rebase命令,我们则需要依次解决每一个冲突。
对于 Git 的rebase
命令,其除了能进行代码合并之外,还有一个常用的功能,那就是将多个 commit 合并为一个,仍然以上面的 feature 分支为例,我们将其从 master 分支拉取之后,进行了三次提交,现在我们将这三个提交结论合并为一个,其命令一般形式为:
git rebase -i HEAD~N
其中,N为我们需要合并的 commit 记录的数量,因为示例中是三次提交记录,所以在此场景下,将N替换为3即可。
git log 命令
log记录过多时可以改变展示方式
![](https://file.jishuzhan.net/article/1788507490648854530/7bc1eec08268b82e05f3fd521c799b04.webp)
![](https://file.jishuzhan.net/article/1788507490648854530/85518acbd705d772b05a290ce98236ab.webp)
git reset
![](https://file.jishuzhan.net/article/1788507490648854530/dde94bc20847f2c061d382766ffc8613.webp)
![](https://file.jishuzhan.net/article/1788507490648854530/91d92ad7e7bfd867e968779514a538d5.webp)
git reset 三种模式的区别
![](https://file.jishuzhan.net/article/1788507490648854530/ef3d5dcdb6c13ac23831a5551b1741e1.webp)
![](https://file.jishuzhan.net/article/1788507490648854530/b760db9f56767f437722a6eed80bdad4.webp)
![](https://file.jishuzhan.net/article/1788507490648854530/bff51276f8b7dc08f51713eac27a95d6.webp)