git原理与常用命令及其使用

认识工作区、暂存区、版本库

⼯作区:是在电脑上你要写代码或⽂件的⽬录。
暂存区:英⽂叫 stage 或 index。⼀般存放在 .git ⽬录下的 index ⽂件(.git/index)中,我们
把暂存区有时也叫作索引(index)。
版本库:⼜名仓库,英⽂名 repository 。⼯作区有⼀个隐藏⽬录 .git ,它不算⼯作区,⽽
是 Git 的版本库。这个版本库⾥⾯的所有⽂件都可以被 Git 管理起来,每个⽂件的修改、删除,Git
都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以"还原"。


图中左侧为⼯作区,右侧为版本库。Git 的版本库⾥存了很多东西,其中最重要的就是暂存区。
在创建 Git 版本库时,Git 会为我们⾃动创建⼀个唯⼀的 master 分⽀,以及指向 master 的⼀个指
针叫 HEAD。
当对⼯作区修改(或新增)的⽂件执⾏ git add 命令时,暂存区⽬录树的⽂件索引会被更新。
当执⾏提交操作 git commit 时,master 分⽀会做相应的更新,可以简单理解为暂存区的⽬录
树才会被真正写到版本库中。
必须要通过使用 git add 和 git commit 命令才能将⽂件添加到仓库中 进行管理!

  1. index 就是我们的暂存区,add 后的内容都是添加到这⾥的。
  2. HEAD 就是我们的默认指向 master 分⽀的指针
  3. objects 为 Git 的对象库,⾥⾯包含了创建的各种版本库对象及内容。当执⾏ git add 命令
    时,暂存区的⽬录树被更新,同时⼯作区修改(或新增)的⽂件内容被写⼊到对象库中的⼀个新的
    对象中,就位于 ".git/objects" ⽬录下,让我们来看看这些对象有何⽤处:
    查找 object 时要将 commit id 分成2部分,其前2位是⽂件夹名称,后38位是⽂件名称。
    找到这个⽂件之后,⼀般不能直接看到⾥⾯是什么,该类⽂件是经过 (安全哈希算法)加密过的
    ⽂件,好在我们可以使⽤ git cat-file 命令来查看版本库对象的内容。
    总结⼀下,在本地的 git 仓库中,有⼏个⽂件或者⽬录很特殊
  • index: 暂存区, git add 后会更新该内容。
  • HEAD: 默认指向 master 分⽀的⼀个指针。
  • refs/heads/master: ⽂件⾥保存当前 master 分⽀的最新 commit id 。
  • objects: 包含了创建的各种版本库对象及内容,可以简单理解为放了 git 维护的所有修改。

修改文件

Git 比其他版本控制系统设计得优秀,因为 Git 跟踪并管理的是修改,而非文件。
**git diff [file]**命令用来显示暂存区和⼯作区⽂件的差异,显示的格式正是Unix通⽤的diff格
式。也可以使⽤ **git diff HEAD -- [file]**命令来查看版本库和⼯作区⽂件的区别。
知道了对 ReadMe 做了什么修改后,再把它提交到本地仓库就放心了。

版本回退

git reset命令语法格式为: git reset [--soft | --mixed | --hard] [HEAD]

--mixed 为默认选项,使⽤时可以不⽤带该参数。该参数将暂存区的内容退回为指定提交版本内容,⼯作区⽂件保持不变。
--soft参数对于⼯作区和暂存区的内容都不变,只是将版本库回退到某个指定版本。
--hard参数将暂存区与⼯作区都退回到指定版本。切记⼯作区有未提交的代码时不要⽤这个命令,因为⼯作区会回滚,你没有提交的代码就再也找不回了,所以使⽤该参数前⼀定要慎重。
HEAD 说明:
可直接写成 commit id,表示指定退回的版本
HEAD 表示当前版本
HEAD^ 上⼀个版本 或HEAD^1
HEAD^^ 上上⼀个版本 或HEAD^2
以此类推
version3 被用 git reset --hard 变成 version2
git log 并不能打印出 version 3 的 commit id ,运⽓好的话我们可以从终端
上去找找之前的记录,运⽓不好的话 commit id 已经被我们搞丢了
Git 还提供了⼀个 git reflog
Git 版本回退的时候,也可以使⽤部分 commit id 来代表⽬标版本
Git 的版本回退速度⾮常快,因为 Git 在内部有个指向当前分支(此处是master)的
HEAD 指针, refs/heads/master 文件里保存当前 master 分⽀的最新 commit id 。当我们
在回退版本的时候,Git 仅仅是给 refs/heads/master 中存储⼀个特定的version,可以简单理解
成如下示意图

撤销修改

如果我们在我们的⼯作区写了很⻓时间代码,需求变更之类的情况

情况⼀:对于⼯作区的代码,还没有 add

可以使用 git checkout -- [file] 命令让⼯作区的
⽂件回到最近⼀次 add 或 commit 时的状态。 要注意 **git checkout -- [file]**命令中的
-- 很重要,切记不要省略,⼀旦省略,该命令就变为其他意思了。

情况⼆:已经 add ,但没有 commit

add 后还是保存到了暂存区呢?怎么撤销呢?

git reset 回退命令,该命令如果使⽤ --mixed 参数,可以将暂存区
的内容退回为指定的版本内容,但⼯作区⽂件保持不变。那我们就可以只回退下暂存区的内容了

git status 查看⼀下,发现现在暂存区是干净的,工作区有修改。

这时候就回到情况一了。

情况三:已经 add ,并且也 commit 了

不要担⼼,我们可以 git reset --hard HEAD^ 回退到上⼀个版本!不过,这是有条件的,就是
你还没有把⾃⼰的本地版本库推送到远程。还记得Git是分布式版本控制系统吗?我们后⾯会讲到远程
版本库,⼀旦你推送到远程版本库,你就真的惨了......

删除文件

工作区和版本库就不⼀致了,要删⽂件,目前除了要删⼯作区的⽂件,还要清除版本库的文件

确实要从版本库中删除该⽂件
不小心删错了

分支管理

每次提交,Git都把代码串成⼀条时间线,这条时间线就可以理解为是⼀个分⽀。
再来理解⼀下HEAD,HEAD 严格来说不是指向提交,⽽是指向master,master才是指向提交的,所 以,HEAD 指向的就是当前分支。每次提交,master分⽀都会向前移动⼀步,这样,随着你不断提交,master分⽀的线也越来越长,而HEAD只要⼀直指向master分支即可指向当前分支

创建分支

Git ⽀持我们查看或创建其他分⽀,在这⾥我们来创建第⼀个⾃⼰的分⽀ dev ,对应的命令为
git branch dev
当我们创建新的分⽀后,Git 新建了⼀个指针叫 dev, * 表示当前 HEAD 指向的分⽀是 master 分
⽀。另外,可以通过⽬录结构发现,新的 dev 分⽀
发现⽬前 dev 和 master 指向同⼀个修改。并且也可以验证下 HEAD ⽬前是指向 master 的。

bash 复制代码
cat .git/HEAD

切换分支

git checkout dev
切换到分支dev提交代码后,当再切换到 master 分⽀之时,HEAD 就指向了 master,当然看不到提交了

合并分支

git merge
为了在 master 主分⽀上能看到新的提交,就需要合并分支

git merge dev # 合并 dev 分⽀
Updating 16623e1..3740dce
Fast-forward
ReadMe | 1 +
1 file changed, 1 insertion(+)
git merge 命令⽤于合并指定分⽀到当前分⽀。合并后,master 就能看到 dev 分⽀提交的内容
了。此时的状态如图如下所⽰。
git merge 命令⽤于合并指定分⽀到当前分⽀。合并后,master 就能看到 dev 分⽀提交的内容
了。此时的状态如图如下所显示。
Fast-forward 代表"快进模式",也就是直接把master指向dev的当前提交,所以合并速度⾮常快。
当然,也不是每次合并都能 Fast-forward,我们后⾯会讲其他方式的合并。

这种情况下,Git 只能试图把各⾃的修改合并起来,但这种合并就可能会有冲突

发现 ⽂件有冲突后,可以 cat ReadMe 直接查看⽂件内容,要说的是 Git 会⽤ <<<<<<<,=======,
>>>>>>> 来标记出不同分⽀的冲突内容
⼿动调整冲突代码,并需要再次提交修正后的结果
结果如下

记 dev1 分⽀使⽤完毕后就可以删除了

git log可以看分支状态
这样的好处是,从分⽀历史上就可以看出分⽀信息。例如我们现在已经删除了在合并冲突部分创建的 dev1 分⽀,但依旧能看到 master 其实是由其他分⽀合并
--no-ff 参数,表⽰禁⽤ Fast forward 模式。禁⽤ Fast forward 模式后合并会创建
⼀个新的 commit ,所以加上 -m 参数,把描述写进去。
git merge --no-ff -m "merge with no-ff" dev2
所以在合并分⽀时,加上 --no-ff 参数就可以⽤普通模式合并,合并后的历史有分⽀,能看出来曾
经做过合并,⽽ fast forward 合并就看不出来曾经做过合并。

删除分支

git branch -d dev
合并完成后, dev 分⽀对于我们来说就没用了, 那么dev分⽀就可以被删除掉,注意 如果当前正处于某分支下,就不能删除当前分⽀
因为创建、合并和删除分⽀非常快,所以Git⿎励你使⽤分⽀完成某个任务,合并后再删掉分⽀,这和直接在master分⽀上⼯作效果是⼀样的,但过程更安全

分支管理策略

在实际开发中,我们应该按照⼏个基本原则进⾏分⽀管理: ⾸先,master分⽀应该是⾮常稳定的,也就是仅⽤来发布新版本,平时不能在上⾯⼲活; 那在哪⼲活呢?⼲活都在dev分⽀上,也就是说,dev分⽀是不稳定的,到某个时候,⽐如1.0版本发布 时,再把dev分⽀合并到master上,在master分⽀发布1.0版本; 每个⼈都在dev分⽀上⼲活,每个⼈都有⾃⼰的分⽀,时不时地往dev分⽀上合并就 可以了。
所以,团队合作的分⽀看起来就像这样:

bug 分⽀

假如我们现在正在 dev2 分⽀上进⾏开发,开发到⼀半,突然发现 master 分⽀上⾯有 bug,需要
解决。在Git中,每个 bug 都可以通过⼀个新的临时分⽀来修复,修复后,合并分⽀,然后将临时分⽀删除。
Git 提供了 git stash 命令,可以将当前的⼯作区信息进⾏储藏,被储藏的内容可以在将来某个时
间恢复出来。
此时再用 git status 查看⼯作区,就是干净的(除⾮有没有被 Git 管理的⽂件),因此可以放⼼地创建分支来修复bug。 储藏 dev2 ⼯作区之后,由于我们要基于master分⽀修复 bug,所以需要切回 master 分⽀,再新建临时分⽀来修复 bug。
修复完成后,切换到 master 分⽀,并完成合并,最后删除 fix_bug 分支

⼯作区是⼲净的,刚才的⼯作现场存到哪去了?⽤ git stash list 命令看看:⼯作现场还在,Git 把 stash 内容存在某个地⽅了,但是需要恢复⼀下,如何恢复现场呢?我们可以使 ⽤ git stash pop 命令,恢复的同时会把 stash 也删了
恢复现场也可以采⽤ git stash apply 恢复,但是恢复后,stash内容并不删除,你需要
⽤ git stash drop 来删除; 你可以多次stash,恢复的时候,先⽤ git stash list 查看,然后恢复指定的stash,⽤命令 git stash apply stash@{0} ,这部分请同学们⾃⾏使⽤。 恢复完代码之后我们便可以继续完成开发,开发完成后便可以进⾏提交
我们的最终⽬的是要让 master 合并 dev2 分⽀的,那么正常情况下我们切回 master 分⽀直接合
并即可,但这样其实是有⼀定⻛险的。 是因为在合并分⽀时可能会有冲突,⽽代码冲突需要我们⼿动解决(在 master 上解决)。我们⽆法 保证对于冲突问题可以正确地⼀次性解决掉,因为在实际的项⽬中,代码冲突不只⼀两⾏那么简单, 有可能⼏⼗上百⾏,甚⾄更多,解决的过程中难免⼿误出错,导致错误的代码被合并到 master 上。

删除临时分支

软件开发中,总有⽆穷⽆尽的新的功能要不断添加进来。
添加⼀个新功能时,你肯定不希望因为⼀些实验性质的代码,把主分⽀搞乱了,所以,每添加⼀个新
功能,最好新建⼀个分⽀,我们可以将其称之为 feature 分⽀,在上⾯开发,完成后,合并,最
后,删除该 feature 分⽀。
可是,如果我们今天正在某个 feature 分⽀上开发了⼀半,被产品经理突然叫停,说是要停⽌新功
能的开发。虽然⽩⼲了,但是这个 feature 分⽀还是必须就地销毁,留着⽆⽤了。这时使⽤传统
的 git branch -d 命令删除分⽀的⽅法是不⾏的。演⽰如下:

git branch -D dev3

标签管理

相较于难以记住的 commit id , tag 很好的解决这个问题,因为 tag ⼀定要给⼀ 个让⼈容易记住,且有意义的名字。当我们需要回退到某个重要版本时,直接使⽤标签就能很快定位 到。
如何打标签?

  • 切换到需要打标签的分⽀上 然后,敲命令 git tag [name] 就可以打⼀个新标签
  • 那如何在指定的commit上打标签呢?⽅法是找到历史提交的commit id 然后git tag name branch
    Git 还提供可以创建带有说明的标签,⽤-a指定标签名,-m指定说明⽂字,格式为:
cpp 复制代码
git tag -a [name] -m "XXX" [commit_id]

Git 分支设计规范

master 分支

master 为主分⽀,该分⽀为只读且唯⼀分⽀。⽤于部署到正式发布环境,⼀般由合并
release 分⽀得到。
主分⽀作为稳定的唯⼀代码库,任何情况下不允许直接在 master 分⽀上修改代码。
产品的功能全部实现后,最终在master分⽀对外发布,另外所有在master分⽀的推送应该打标签
(tag)做记录,⽅便追溯。
master 分⽀不可删除。

release 分支

release 为预发布分⽀,基于本次上线所有的 feature 分⽀合并到 develop 分⽀之后,基
于 develop 分⽀创建。可以部署到测试或预发布集群。
命名以 release/ 开头,建议的命名规则: release/version_publishtime 。
release 分⽀主要⽤于提交给测试⼈员进⾏功能测试。发布提测阶段,会以 release 分⽀代码
为基准进⾏提测。
如果在 release 分⽀测试出问题,需要回归验证 develop 分⽀看否存在此问题。
release 分⽀属于临时分⽀,产品上线后可选删除。

develop 分支

develop 为开发分⽀,基于master分⽀创建的只读且唯⼀分⽀,始终保持最新完成以及 bug 修
复后的代码。可部署到开发环境对应集群。
可根据需求⼤⼩程度确定是由 feature 分⽀合并,还是直接在上⾯开发(⾮常不建议)。

feature 分支

feature 分⽀通常为新功能或新特性开发分支,以 develop 分⽀为基础创建 feature 分
命名⽀。以 feature/ 开头,建议的命名规则: feature/user_createtime_feature 。
新特性或新功能开发完成后,开发⼈员需合到 develop 分⽀。
⼀旦该需求发布上线,便将其删除。

hotfix 分支

hotfix 分支为线上 bug 修复分⽀或叫补丁分支,主要⽤于对线上的版本进⾏ bug 修复。当线上
出现紧急问题需要⻢上修复时,需要基于 master 分⽀创建 hotfix 分⽀。
命名以 hotfix/ 开头,建议的命名规则: hotfix/user_createtime_hotfix
当问题修复完成后,需要合并到 master 分⽀和 develop 分⽀并推送远程。⼀旦修复上线,便
将其删除。

总结

分支类型 命名规则 用途 创建来源 是否可删除
master 正式环境唯一代码库,所有发布版本的基准 初始化
release release/version_publishtime 预发布测试,版本发布前的最后验证 develop 分支 可选
develop 日常开发基准分支,集成最新完成的功能 master 分支
feature feature/user_createtime_feature 新功能开发,每个功能独立分支 develop 分支
hotfix hotfix/user_createtime_hotfix 线上紧急 BUG 修复 master 分支
相关推荐
海浪在开花21 分钟前
vscode查看文件历史git commit记录
git·vscode
睡觉待开机29 分钟前
git 基础操作
git
航重名了779489 小时前
《Git江湖录·分支篇》
git
挪威的深林10 小时前
【杂记二】git, github, vscode等
git·github
☆致夏☆12 小时前
Git基础
git
aseity13 小时前
OpenSSL 3.0.2 报 dh key too small 的问题
linux·git·svn·ssl
咖啡教室1 天前
用git rebase命令合并开发阶段中多条commit提交记录
git
一颗无畏豆儿1 天前
pycharm运行终端部署(Anaconda终端与Git运行终端)
git·python·pycharm·conda
AltMan1 天前
【程序员必备】Git从入门到掌握
git
ROC_bird..1 天前
linux_git使用
git