Git的基本使用
基础(最常用的命令)
|
|
版本回退
前一个版本:
I’m a little tired!!!
当前版本:
I’m not tired!!!
这时使用 git add
和 git commit
提交了, 但是想回退过去的操作办法如下:
-
git中HEAD表示当前的提交版本, HEAD^表示前一个提交版本,所以想回退的操作为:
git reset --hard HEAD^
未回退命令时日志显示: 有两个版本: first 和 not tired
使用回退命令后日志显示: 只有 first 了
-
当知道commit ID时, 使用 commit ID直接跳到想要的版本.
比如现在想再返回 not tired, 操作命令为:
git reset --hard commit_id
这时再查看log结果为:
可以看到, 使用了这个命令后, 我们的not tired 提交又回来了.
-
当不知道commit ID时也是有办法的
git提供了
git reflog
命令, 这个命令的输出结果是记录你的每一次命令, 使用git reflog
命令查看历史命令:可以看到第一列记录了所有的commit ID, 最后一列记录了执行的操作, 根据最后一列信息找到对应的ID号即可
回退问题:
- 如何跳到某个指定commit_id的版本?
- 假如你刚提交了一个版本, 现在后悔了想返回到前一个版本, 应该使用什么命令?
- 假如你回退到前某个版本, 现在后悔了, 想返回到最近的某个版本, 但是 commit_id 不记得, 该怎么办?
回退总结:
-
HEAD 为当前版本, 使
git reset --hard commit_id
可以跳到对应的版本 -
git reflog
命令可以查看所有操作的记录, 可以用于查找所需要的版本号 -
tip: HEAD表示当前版本, HEAD^表示前一个提交版本, HEAD^^为前2个提交的版本, HEAD~100 为第前100个版本
撤销修改
前一节所说的版本回退有个缺点,
那就是, 回退到前一个版本, 所有相关文件的最新修改均会丢失.
并且版本回退是针对已提交的内容的.
现在有个新问题: 在git管理的版本下, 撤销对某个文件的修改.
这之前要了解一下git管理的基本逻辑.
git 基本逻辑
git仓库其实分为工作区(working tree), 暂存区(stage), 和 仓库()
工作区就是我们看到和使用的部分
暂存区是我们使用 git add filename
后文件所到达的部分
仓库是我们使用 git commit
后文件所在的部分
在使用 git commit
命令时, 只会将暂存区部分的内容保存到仓库
文件修改的撤销
- 在上一次提交后, 本次我们对某个文件进行了修改, 发现修改后程序运行出错了.
现在删除对文件的修改, 应该怎么做(并未使用 git add
命令)?
这时需要的命令为 git checkout -- filename
.
对文件修改后查看工作区状态:
可以看到有修改, 这时使用 git checkout filename
后结果为:
再次查看文件, 修改己经没有了
- 假设这次我们对文件进行了修改, 且已经使用了
git add filename
了该怎么办呢?
办法是使用 git reset HEAD filename
记其回到未 add 的情况.
我们将修改添加到暂存区, 并查看状态:
先看一下与前面的区别, 未add时, 字是红色的, 提示信息是"尚未暂存以备提交的变更"
使用 git add filename
后提示信息是"要提交的变更"
此时使用 git reset HEAD filename
后的状态为:
可以看到取消了暂存的变更, 这时查看状态则又到了修改未提交的状态了.
此时再使用 git checkout -- filename
命令即可以撤销修改了.
撤销问题
- 有修改, 但是未添加到暂存区, 如何撤销?
- 有修改, 已添加到暂存区, 如何撤销?
撤销总结
git checkout -- filename
撤销对工作区文件的修改git reset HEAD filename
撤销对文件的暂存操作
文件删除
git仓库的文件删除和正常文件夹的删除不大一样, 因为git将删除也视为了一个操作, 因此删除也需要提交. 正常删除文件后查看仓库状态:
同样, 使用 git checkout -- filename
也可以取消该操作.
使用 git rm filename
删除文件后查看状态:
可以看到, 直接删除后提示信息是红色的, 且提示信息是"修改尚未加入提交".
也就是说, 这时还需要使用 git add
命令才可以使用 git commit
将删除操作提交到仓库.
而 git rm filename
则可以直接将删除提交到仓库.
对于已添加,未添加的修改如何撤销可见上一节.
删除问题
- 若要删除仓库中的文件应如何操作?
- 直接删除与使用 git rm 命令删除有何不同?
- 删除后如何撤销?
删除总结
- 删除与修改一样, 都是要提交到仓库的
- 使用 git rm 操作相当于使用 直接删除并使用了 git add 命令
- 删除的撤销可根据是否添加到暂存区同修改一样操作
分支
创建与合并
-
分支的创建与删除:
git branch branch_name
创建新分支前:
创建新分支后:
git branch newBranch
(* 表示当前HEAD所在的分析, 即当前工作区的分支)切换分支:
git checkout newBranch
可以看到 * 从main到了newBranch分支删除:
git branch -d newBranch
(不能删除当前所在分支, 因此要先切回main分支才可执行该操作) -
分支的合并现有两个分支:main和bugFix 合并前:
将HEAD放在main分支上, 并执行
git merge bugFix
:可以看到, 合并会产生一个新的提交, 且当前节点有了两个父节点.
这里其实可以更深入理解下git checkout这个命令, 它的真实作用是切换当前工作区在git这个仓库树中的位置.即, HEAD可以指向main或者bugFix这种具体的分支名, 也可以指向某一个commit_id(每一个commit_id即是工作区的一个版本)
冲突的修正
假如我们现在有2个分支: master和newBranch, 在这两个分支中我们都对一个文件进行了修改. 这时, 将newBranch分支合并到master分支时就会出现冲突:
这里master分支中我们添加的内容是"Creating a new branch!!!“并提交了.
在newBranch分支中我们添加的内容是"Create a new branch!!!”.
所以在HEAD处于master分支, 并运行 git merge newBranch
时会出现冲突.
我们打开提示的冲突文件, 文件内容如下:
Git用<,=,>标记出不同分支的内容,我们修改如下后保存:
这时查看仓库状态为:
此时将其添加到仓库并提交
这时查看日志树可以看到合并过程:
分支问题
- 如何创建分支与删除分支?
- 如何在不同分支中进行切换?
- 如何合并不同的分支?
- 合并分支后有冲突了该怎么办?
分支总结
- 分支创建:
git branch branch_name
- 分支删除:
git branch -d branch_name
- 将其他分支合并到当前分支:
git merge other_branch
- 分支切换:
git checkout branch_name
- 冲突解决: 修正冲突的文件并添加到暂存区, 然后提交
高级篇:命令详解
分离HEAD
这是一个很小的概念, 单独说它, 就是因为它很重要.
其实git是由不同的提交组成, 每个提交都可以看成一个树中的节点.
所有的提交在一起组成了一个提交树.
而当前的工作区(也就是我们能看到并修改的那些文件), 就是HEAD所指向的.
其实HEAD并不是非要指向具体的分支, 它也可以直接指向某个提交.
比如当前的提交树为:
可以看到*在bugFix这个分支上, 这也表示HEAD指向bugFix, 我们可以直接让它指向C4 git checkout C4
(这里的C4是提交id):
同样, 也可以使用这个命令让它指向 main, 或者 C0:
其实checkout这个命令用来切换HEAD的指向并不准确, 新版的git使用switch命令
相对引用(^)
-
在不同的仓库进行切换时, 使用commit id十分麻烦, 因为id需要使用git log命令查找, git提供了相对引用, 即^表示上一个, ^^表示上2个, ~10 表示前10个.
如使用
git checkout main^
效果为:可以看到当HAED指向main时, HEAD^和main^都是指向C5的.
再次使用
git checkout HEAD~3
结果为:
撤销变更 (reset & revert)
当本地出错要回退时, 有2个命令可用.
- reset可将当前分支回退到指定提交. 可看执行结果:
原本提交树是这样, 使用 git reset HEAD^
后:
可以看到, 这个命令让分支放弃了当前节点而指向了父节点.
若对原本的提交树使用 git revert HEAD^
命令后的提交树为:
可以看到, 现在这人分支指向了C1’这个提交, 其实C1’与C1是相同的, 但是 git 将回退作为一个操作然后提交到仓库.
这样做的好处: 在本地其实是没有什么区别的, 但是如果推送到远程, 使用git reset后若要与远程同步, 需要将远程的最新提交删除, 这个操作难以实现, 因此将回退作为一个新的提交可以方便远程与本地的同步, 方便多人合作.
提交树的节点移动 (rebase & cherry-pick)
-
rebase: 它的主要作用是创建线性提交记录, 方便工程修改脉路整理. 比如对于当前提交树:
我们使用
git rebase main
后提交树变化为:现在分析一下这个结果. feature 和 main 分支的公共父节点是C1, 我在的HEAD指向feature, 此时运行以上命令后, git 将C2,C4 提交接到了main所指向的C3上, 也就是说: 这个命令会将当前分支的所有提交接到指定的分支上
因此 rebase 可以表象地理解为 剪枝 + 嫁接
如果使用merge命令的话提交树会变为:
这时提交记录则不够线性, 不方便查看对工程的修改历史
-
rebase 的另一个功能: 提交记录排序与删除.
git rebase -i HEAD~4
: 这个命令会弹出一个交互式的创口, 让你调整最新4个提交节点的顺序或者删除提交节点. 下面是一个例子:知道这个功能即可, 不具体说明了
-
cherry-pick: 这个命令允许我们随意地移动提交树中的节点, 将指定的提交节点移动到当前的HEAD所指定的节点下.
对以下提交树:
使用
git cherry-pick C2 C4 C6
后结果为:
可以看到, 选中的提交节点都添加到了当前分支 main 所指向的节点下.
这样的命令有什么作用呢?
场景一: 当前出了一些问题, 可此需要新建一些分支来解决, 最终解决了当前的问题:
但是此时, 我们只想要收录解决问题的提交到当前的main下, 此时就可以使用 rebase -i 和 cherry-pick 这两个命令来实现.
将HEAD调整到main分支上, 再使用 git cherry-pick C4
(左) 或者使用 git rebase -i HEAD~3
后调整提交 (右) 即可获得:
场景二: 更新历史提交节点中的信息
下面的提交树中, 我想更新newImage提交中的信息, 且是只更新它的信息, 可使用 rebase -i 和 cherry-pick 这两种方式来实现.
口述使用 rebase -i 命令过程: 先调整C2和C3顺序,将C2提交节点放到最前, 再使用 git commit --amend
来提交列新C2中的信息, 最后使用 rebase -i 命令将C3节点放到最前.
详细说明 cherry-pick 过程 (因为不用交互, 易于博客中展示). 首先调整HEAD到main: git checkout main
其次, 将C2 拿到最前: git cherry-pick C2
然后, 修改C2中的内容并提交: git commit --amend
最后, 将C3节点放到最前: git cherry-pick C3
tag
前文中提交节点要么是使用提交ID标识的, 要么是使用分支名子标识的.
分支名字会被移动, 且有新的提交时分支名字也会移动到新的提交节点中, 提交ID太长,难记.因此需要一个为某个提交节点进行永久命各的办法: 它就是tag. 对于以下的提交树:
为不同的提交节点添加tag, 如运行命令: git tag V1 C1, git tag V2 C2
, 这时的提交树为:
同时, 可以直接利用tag名称来移动HEAD: git checkout V1
可以看到这时HEAD指向了C1. 为什么不是V1* 呢, 这是因为无支对某个tag直接提交, 所以才会发生这种分离HEAD的现象.
describe
git describe <ref>
: <ref> 为提交树中的引用, 默认为HEAD.
该命令的输出结果为: <tag>_<num>_g<hash>, tag为与该引用最近的标签, num 为 该引用与最近标签的间隔提交数, <hash>为<ref>的提交ID.
对于以下的提交树, 使用 git describe, git describe main, git describe side
的结果分别为:
如果这篇文章帮到了你, 那就赞助我一瓶水吧, 这可以让我有动力去写更多的文章
Sponsor