本篇文章主要为大家详解Git的使用方法,这些命令都是官方给出的git操作,但是在实际工作当中,许多公司会重新包装某些操作,以达到更加安全的方式使用git,因此在实际工作中,还需要详细查看公司的git文档,避免错误操作导致不可逆后果!
如果觉得看得不方便?点击链接访问我的个人文档,让你更加直观了解 git!
git 使用秘籍:早建分支!多用分支!
git commit — 创建新的提交记录
git commit
git commit -amend — 将暂存区里面的所有文件作为最近一次提交的修改
git branch — 创建分支
-
创建名为 newImage 的分支
git branch newImage
tip: 但是此时使用 git commit 可能不会在新的分支上提交记录,原因是我们提交的记录还是在原来的分支上,需要使用 git checkout newImage 切换分支
-
切换到名为 newImage 的分支
git checkout newImage
tip:在 Git 2.23 版本中,引入了一个名为 git switch 的新命令,最终会取代 git checkout,因为 checkout 作为单个命令有点超载(它承载了很多独立的功能)。 由于现在很多人还无法使用 switch,本次课程仍然使用 checkout 而不是 switch, 但是如果你想尝试一下新命令,我们的应用也是支持的!
-
创建一个新的分支同时切换到新创建的分支
git checkout -b newImage
-
创建一个新的分支同事指向某个节点
git checkout -b newImage C2
git merge / git rebase — 合并分支
-
git merge
-
把 bugFix 合并到 main 里
git merge bugFix
-
再把 main 分支合并到 bugFix
git checkout bugFix; git merge main
-
git rebase
Rebase 实际上就是取出一系列的提交记录,“复制”它们,然后在另外一个地方逐个的放下去。
Rebase 的优势就是可以创造更线性的提交历史。如果只允许使用 Rebase 的话,代码库的提交历史将会变得异常清晰。
-
把 bugFix 分支里的工作直接移到 main 分支上。移动以后会使得两个分支的功能看起来像是按顺序开发,但实际上它们是并行开发的。
git rebase main
tip:提交记录 原先的 bugFix 依然存在,而 bugFix’ 是我们 Rebase 到 main 分支上的 bugFix 的副本。
-
再将 main rebase 到 bugFix 上
git rebase bugFix
tip:由于 bugFig 继承自 main ,所以 Git 只是简单的把 main 分支的引用向前移动了一下而已。
-
其实还有更加简便的方法可以将 bugFix 分支移动到 main 下,并且分支指向 bugFix
git rebase main bugFix
tip:如果 main 和 bugFix 不在同一个分支,使用该命令后,会将 bugFix 有而 main 中没有的提交也一起合并到 main 下;假设 bugFix 和 main 在同一分支下,且 bugFix 在 main 之前,使用该命令后,会将 bugFix 移动到 main 位置,且分支被 bugFix 所指。
-
rebase -i
rebase -i 指的是交互式的 rebase,其中 -i 参数是 -interactive 的缩写。在命令后增加了这个选项, Git 会打开一个 UI 界面并列出将要被复制到目标分支的备选提交记录,它还会显示每个提交记录的哈希值和提交说明,提交说明有助于理解这个提交进行了哪些更改。在实际使用时,所谓的 UI 窗口一般会在文本编辑器 —— 如 Vim —— 中打开一个文件。
Tip:当 rebase UI界面打开时, 能做3件事:
-
调整提交记录的顺序(通过鼠标拖放来完成)
-
删除不想要的提交(通过切换 pick 的状态来完成,关闭就意味着不想要这个提交记录)
-
合并提交(略,待补充)
git rebase -i HEAD~4
HEAD — 当前检索符号
HEAD 是一个对当前检出记录的符号引用 —— 也就是指向你正在其基础上进行工作的提交记录
你可以通俗的理解为使用 git checkout c1 , 那么 HEAD 则指向 c1
如果想看 HEAD 指向,可以通过 cat .git/HEAD查看,如果 HEAD 指向的是一个引用,还可以用 git symbolic-ref HEAD查看它的指向。
git log — 查看提交记录的哈希值
git log
tip:由于 Git 的哈希值有 40 位,例如 fed2da64c0efc5293610bdd892f82a58e8cbc5d8 ,因此为了简化操作,只需要输入 fed2 即可对哈希进行处理。
^ / ~ / -f — 对节点的操作
由于通过哈希值来指定提交记录并不方便,因此, git 还引用了如下两个方法,方便我们对提交记录进行处理。使用 ^ 向上移动 1 个提交记录;使用 ~<num> 向上移动多个提交记录,如 ~3(不跟数字时与 ^相同,向上移动一次)
-
^
-
切换到 main 的父节点
git checkout main^
tip:可以将 HEAD作为相对引用的参照,例如使用 git checkout C3; git checkout HEAD^,此外,如果不加数字修改符直接检出 main^,会回到第一个父提交记录。

-
切换到 main 的另外一个父节点
git checkout main^2

-
~
-
后退当前四步
git checkout HEAD~4
Tip:~ 和 ^ 支持链式操作,例如 — git checkout HEAD~^2~2
-
创建一个 bugWork 节点,使其指向 main 的上一级的第二分支的上一级
git branch bugWork main~^2~
-
-f
-
将 main 分支强制指向 HEAD 的第 3 级父节点提交
git branch -f main HEAD~3
tip:-f 容许我们将分支强制移动到那个位置。
-
将 three 分支强制指向 C2 节点
git branch -f three C2
git reset / git revert — 撤销变更
和提交一样,撤销变更由底层部分(暂存区的独立文件或者片段)和上层部分(变更到底是通过哪种方式被撤销的)组成。
-
git reset
Git 把 main 分支移回到 main 的上一层
git reset HEAD~1
Tip:在reset后, C2所做的变更还在,但是处于未加入暂存区状态。
-
git revert
虽然在本地分支中使用 git reset 很方便,但是这种“改写历史”的方法对大家一起使用的远程分支是无效的。为了撤销更改并分享给别人,我们需要使用 git revert
git revent HEAD
Tip:在我们要撤销的提交记录后面其实也并没有删除该分支,反而增添了一个新的分支。这是因为新提交记录 C2′ 引入了更改 —— 这些更改刚好是用来撤销 C2 这个提交的。也就是说 C2′ 的状态与 C1 是相同的。revert 之后就可以把你的更改推送到远程仓库与其他人分享。
git cherry-pick — 将提交复制到当前位置
将 C2 和 C4 复制到当前 HEAD 下
git cherry-pick C2 C4


git tag — 永久地将某个特定的提交命名为里程碑
tag并不会随着新的提交而移动,也不能切换到某个标签上面进行修改提交,它就像是提交树上的一个锚点,标识了某个特定的位置。
git tag v1 C1


git describe — 查看离最近一次 tag 的距离
git describe 的语法是:
git describe <ref>
<ref> 可以是任何能被 Git 识别成提交记录的引用,如果没有指定的话,Git 会以你目前所检出的位置(HEAD)。
输出的结果:
<tag>_<numCommits>_g<hash>
tag 表示的是离 ref 最近的标签, numCommits 是表示这个 ref 与 tag 相差有多少个提交记录, hash 表示的是给定的 ref 所表示的提交记录哈希值的前几位。当 ref 提交记录上有某个标签时,则只输出标签名称

查看 main 的 git describe
git describe main
输出结果:v1_2_gC2
查看 side 的 git describe
git describe side
输出结果:v2_1_gC4
git clone — 在本地创建一个远程仓库的拷贝
git clone


Tip:在本地仓库多了一个名为 o/main 的分支,o 代表远程仓库的名称(开发中默认为 origin 而不是 o),这种类型的分支就叫做远程分支。远程分支反映了远程仓库(上次和它通信时)的状态,有助于理解本地的工作与公共工作的差别。远程分支有一个特别的属性,在检出时自动进入分离 HEAD 状态。Git 这么做是出于不能直接在这些分支上进行操作的原因。
再次使用 git checkout o/main; git commit


Tip:Git 变成了分离 HEAD 状态,当添加新的提交时 o/main也不会更新。这是因为 o/main只有在远程仓库中相应的分支更新了以后才会更新。
git fetch — 从远程仓库获取(下载)数据
git fetch


Tip:git fetch 完成了仅有的但是很重要的两步,一是从远程仓库下载本地仓库中缺失的提交记录,二是更新远程分支指针,git fetch通常通过互联网(使用 http://或 git://协议) 与远程仓库通信。
-
git 中常用到抓取后合并的动作
git fetch; git merge o/main

先用fetch下载了 C3, o/main 指向了 C3 然后通过 git merge o/main合并了这一提交记录。现在 main分支包含了远程仓库中的更新。
-
fetch 的高级操作
-
git fetch <remote> <place>
git fetch origin foo

含义:Git 会到远程仓库的 foo分支上,然后获取所有本地不存在的提交,放到本地的 o/foo上
Tip:使用 fetch 并不会更新本地 foo,原因是考虑到本地的 foo 有可能未修改完,git 不会帮我们篡改本地 foo,而是简单的下载远程 o/foo
-
git fetch <remote> <source>:<destination>
git fetch origin foo~1:bar

将 foo 的上一级分支更新到本地,并且与本地 bar 绑定
Tip:由于已经指定了目标分支,所以 foo 和 o/foo 都没有被更新
当本地分支不存在,则先在本地创建本地新的分支,然后再进行绑定,如下
git fetch origin foo~1:bar

当 <source> 为空时会创建本地分支,如下
git fetch origin :bar

git pull — 抓取并合并到本地分支
由于先抓取更新再合并到本地分支这个流程很常用,因此 Git 提供了一个专门的命令来完成这两个操作。以下用作对比
git pull


可以看出, git pull 就是 git fetch 和 git merge 的缩写
-
git pull 的高级用法
git pull origin main 等同于 git fetch origin main; git merge o/main

通过指定 main更新了 o/main。然后将 o/mainmerge 到的检出位置。
git pull origin main:foo 等同于 git fetch origin main:foo; git merge foo

先在本地创建了一个叫 foo的分支,从远程仓库中的 main 分支中下载提交记录,并合并到 foo,然后再 merge 到当前检出的分支 bar上。
git push — 上传且合并远程仓库记录
Tip: git push 不带任何参数时的行为与 Git 的一个名为 push.default 的配置有关。它的默认值取决于正使用的 Git 的版本,目前使用的是 upstream。 在项目进行推送之前,必须要检查该配置。
-
正常情况下合并
git push

远程仓库接收了 C2,远程仓库中的 main 分支也被更新到指向 C2,远程分支 (o/main) 也同样被更新。所有分支同步。
-
历史偏离时提交合并
所谓历史偏离即在你 push 前,你的同事已经 push 过,那么此时你的代码被 Git 判定为在旧版项目上进行修改的无效代码。
-
rebase
git fetch; git rebase o/main; git push

先是使用 git fetch 更新了 C2 ,再使用 git rebase o/main 创建 C3′ ,main 指向了 C3′,再次用 git push 更新了远程分支,并且更新了本地 o/main
-
merge
git fetch; git merge o/main; git push
先是使用 git fetch 更新了 C2 ,再使用 git merge o/main 创建 C4 ,main 指向了 C4,再次用 git push 更新了远程分支 C3 C4,并且更新了本地 o/main 也指向了 C4

-
git pull
git pull –rebase; git push
git pull –rebase 就是 git fetch 和 git rebase 的缩写
git pull; git push
git pull; git push 就是 git fetch 和 git merge 的缩写
-
push 的高级操作
-
git push <remote> <place>
git checkout C0; git push origin main

含义:切到本地仓库中的“main”分支,获取所有的提交,再到远程仓库“origin”中找到“main”分支,将远程仓库中没有的提交记录都添加上去
Tip:在这里使用 git checkout C0 将 HEAD 指向另外的节点的目的是清楚的告诉我们git push origin main 与当前指针无关。
-
git push <remote> <source>:<destination>
git push origin foo^:main

将 foo^ 与远程的 main 绑定,当提交 foo^ 时,会更新到远程仓库的 main 中
当远程分支不存在的时候,会在远程仓库创建新的分支,并且更新本地分支,如下
git push origin main:newBranch

当 <source> 为空时会删除远程仓库的分支,如下
gti push origin :foo

pull request — 远程服务器拒绝(remote rejected)
在一个大的合作团队中工作, 很可能是main被锁定了, 需要一些 pull request 流程来合并修改。如果直接提交(commit)到本地main, 然后试图推送(push)修改, 将会收到这样类似的信息
! [远程服务器拒绝] main -> main (TF402455: 不允许推送(push)这个分支; 你必须使用pull request来更新这个分支.)
正确的做法应该是新建一个分支, 推送(push)这个分支并申请pull request,但是忘记并直接提交给了main是无法推送更新的


远程跟踪分支
通过之前的观察,很容易让人以为 main 和 o/main 是有关联的,实则不然。在进行 pull 操作时, 提交记录会被先下载到 o/main 上,之后再合并到本地的 main 分支。在进行 push 操作时, 工作从 main 推到远程仓库中的 main 分支(同时会更新远程分支 o/main) 。因此,main 的地位可以被随时替代,已达到使用新的分支来跟踪远程分支的目的。
-
checkout -b
-
可以创建一个新的分支跟踪远程分支
git checkout -b foo o/main; git pull
Tip:使用远程分支来更新 foo 分支,以便于更好的跟踪远程分支。如下,执行 git checkout -b foo o/main 后会新建一个 foo 分支跟踪 o/main,当输入 git pull 时,会更新本地 o/main,然后分支 foo 跟随 o/main 进行移动,以达到更新 foo 分支的目的。

-
创建新分支绑定 o/main,通过 push 更新 o/main
git checkout -b foo o/main; git commit; git push

-
branch -u
可以创建一个新的分支跟踪远程分支
git branch -u o/main foo; git commit; git push

Tip:如果当前就在 foo 分支上, 还可以省略 foo:git branch -u o/main
