文章目录
- 引子
- [1. 创建 提交 删除](#1. 创建 提交 删除)
-
- [init - - 创建一个初始化的本地仓库](#init - - 创建一个初始化的本地仓库)
- [config - - 对本地仓库的配置](#config - - 对本地仓库的配置)
- [add - - 新增](#add - - 新增)
- [commit - - 提交](#commit - - 提交)
- [rm - - 删除](#rm - - 删除)
- [2. 状态查看 和 版本回退](#2. 状态查看 和 版本回退)
- [3. 撤销修改](#3. 撤销修改)
-
- [checkout - - 让 工作区 回到最近一次 添加 / 提交](#checkout - - 让 工作区 回到最近一次 添加 / 提交)
- [reset - - 版本回退](#reset - - 版本回退)
- [4. 分支管理](#4. 分支管理)
-
- [branch - - 新建、查看、删除本地分支](#branch - - 新建、查看、删除本地分支)
- [checkout - - 切换分支](#checkout - - 切换分支)
- [merge - - 合并](#merge - - 合并)
- [stash - - 保存工作区内容、取出](#stash - - 保存工作区内容、取出)
引子
git 用作版本管理,是开发人员必须掌握的工具之一。笔者想借由一些基础语法和操作入手,尝试将 git 的工作结构融入其中,以便那些和我一样想要开始学习 git 的朋友能更好的认识这个工具的本质。
🎯写在前面:
- git 涉及的管理区域需要仔细体会。
- git 管理的是修改(添加、修改、删除),而不是文件,简单来说对一连串时间主导的 commit obj 的索引的管理,就是 git 的管理。
- HEAD 指针指向分支的最后一次 commit!!回滚版本实际就是 HEAD 指针前移!!
- 本篇内容将从本地仓库讲起,冲突问题本地仓库和远端的处理模式基本一致,最后在介绍远端的相关命令,有助于对git模型的整体理解。
1. 创建 提交 删除
init - - 创建一个初始化的本地仓库
- 命令:
git init
在新建仓库里能看到 .git 文件,是自动生成的仓库区,不要手动修改!我们在此外的区域进行管理即可。
config - - 对本地仓库的配置
- 命令:
git config [option]
,在建好的仓库目录下可以对该仓库进行信息配置。-l
:查看仓库配置信息;user.name "用户名"
:设置仓库所属用户名;user.email "用户邮箱"
:设置仓库所属用户邮箱;--unset user.name
:删除仓库所属用户名;--unset user.email
:删除仓库所属用户邮箱;--global
:一台服务器下创建多个本地仓库时,加上这个选项,会在该服务器下的所有本地仓库中生效。不过,使用该选项创建后,要想删除内容,需要同样用该选项,删除才会生效。
add - - 新增
- 命令:
git add 路径1 [路径2 ...]
,将本地文件 / 文件夹提交到暂存区。
介绍一下版本库里的区域:
- objects 对象库 :
- add 新增工作区的修改的时候,会将修改的内容写入一个 git 对象中,放入对象库中保存。这其实就做到了文件的版本管理。
- add 新增工作区的修改的时候,会将修改的内容写入一个 git 对象中,放入对象库中保存。这其实就做到了文件的版本管理。
- stage 暂存区:
- 存储的是 git 对象中的索引,是一个轻量化的区域。
- 存储的是 git 对象中的索引,是一个轻量化的区域。
commit - - 提交
-
命令:
git commit -m "描述"
,将暂存区的内容写到 [master(图示)] 分支下。 -
master 分支树:
- 存储的同样是 git 对象中的索引;
- 由一个指针指向分支结构,我们只要找到这个指针就可以找到一个分支中的所有内容。
rm - - 删除
- 命令:
git rm [filename]
,像比与在终端先 rm filename 再进行 add 操作,git 提供的 rm 将这两步合成了一步。使用完就可以直接 commit 提交了。
2. 状态查看 和 版本回退
log - - 日志
- 命令:
git log
,可以查看到 HEAD 指针及其之前的版本信息 。
(如果版本发生过回退操作,则可能会出现,HEAD 指针之后仍存在历史提交版本的情况,这些情况 log 就看不到了,需要用到 reflog )--pretty=oneline
:规划成一行,以易懂的形式展示。--graph
:将分支规划成图显示在前面。--abbrev-commit
:将 id 进行缩写显示。
- 结合 "
.git
" 文件的内容一起看一下:- HEAD 指向的内容是最后一次 commit 生成的 git 对象,这也是分支 master 的头头;
- 所有的 git 对象都存在 object 下,记录着每一个 commit 的详细信息;
- 暂存区的内容都在 index 里。
status - - 查看
- 命令:
git status "描述"
,查看本地、暂存库中的文件状态。
diff - - 比较
- 命令:
git diff [file]
,显示 已写入暂存区 和 已经被修改但尚未写入暂存区文件 的区别。- 尚未提交到暂存区的改动:git diff [file];
- 这里的结果绿框是改动前,-1 的含义是改动前的文件的第一行被修改了;
- 橙框是改动后,+1,2 的含义是从第一行起的连续两行被改动了;
- 查看 版本库 与 工作区 的区别:git diff HEAD [-- file]。
- 尚未提交到暂存区的改动:git diff [file];
reset - - 版本回退
reset 可以将版本库中的操作进行回退
也可以回退到当前版本(工作区、暂存区有修改,可以使用 git reset 让工作区、暂存区和版本库一致,相当于撤回工作区、暂存区的修改)。
a. 回退到指定版本
- 命令:
git reset [option] [HEAD] [filename]
,这里的HEAD指的是指针,即 git 对象的 ID。--soft
:只回退版本库;--mixed
:回退暂存库、版本库,是 默认选项;--hard
:回退工作区、暂存区、版本库。慎用,工作区的新增内容会全部消失。
选项 | 工作区 | 暂存区 | 版本库 |
---|---|---|---|
--soft |
vers1,vers2 | vers1,vers2 | vers1 |
--mixed (默认项) |
vers1,vers2 | vers1 | vers1 |
--hard |
vers1 | vers1 | vers1 |
b. 回退到 当前/上一版本
- 命令:
git reset [option] HEAD[^^] [filename]
,直接跟 HEAD 是回退到当前版本,在 HEAD 后加^
就是回退到上一个版本,加 ^^ 就是回退到上上个版本。- 选项的控制范围也是一样的,如果不加,就是默认 --mixed,让暂存区和版本库一致。
场景模拟1_1
在一个仓库中我做了三次提交:
第一次创建提交:file1(里面的内容是version 1)
第二次创建提交:file2、file3
第三次修改提交:file1(里面的内容是version 1\nversion2)
这里要做的是将版本回退到第一次提交,然后版本再次返回,回退到第三次提交
如上操作,在回退到第一版时,输入 git log 是只有第一次提交的记录的,我们能顺利回退完全是因为在最开始打印了第三次的提交 id。
在已经 git log 不出原来信息的情况,又要怎么回退到第三次提交呢?
reflog - - 参考日志
- 命令:
git reflog
,可以叫做显示可引用的历史版本记录,会记录所有出现过的提交信息。
场景模拟1_2
接着上面的场景模拟1_1,在 git reflog 中可以找到所有的记录!!
ps:如果版本提交次数太多,想要恢复的那个 id 找不到了,就恢复不了咯~~~
3. 撤销修改
撤销修改这一节,适用于如下情况:我们想把本地的内容撤销掉,恢复上一个版本的内容。撤销的目的主要是,不要让本地代码影响远程仓库!
这里引入一个远程仓库的概念,我们能接触到的几个分区大概就是这样:
工作区 --> 暂存区 --> 版本库 --> 远端仓库
add commit push
而撤销修改的前提就是,需要撤销的内容还没有 push,没有推送至远端,否则这些撤销修改都是没有意义的。
本地 git 仓库存在以下这三种分区,对于要撤销的修改就会有三种状况:
情况 | 工作区 | 暂存区 | 版本库 | 处理方式 |
---|---|---|---|---|
只有修改,还没 add | xxx | git checkout -- [filename] | ||
没有 commit | xxx | xxx | git reset HEAD [filename] | |
add、commit 都做了 | xxx | xxx | xxx | git reset --hard HEAD^ [filename] |
checkout - - 让 工作区 回到最近一次 添加 / 提交
- 命令:
git checkout -- [filename]
,这里必须要加 - - 否则这个命令就不是这个意思咯
reset - - 版本回退
...具体见上一个板块 ~~
4. 分支管理
这一节牵扯到多个分支的问题,之前因为都是以一个分支举例,所以需要进一步说明一个细节:被 HEAD 指向的分支,是当前工作分支。使用下面这个命令可以查看当前本地分支情况。
branch - - 新建、查看、删除本地分支
- 命令:
git branch
,查看当前本地分支情况 ,其中带*
号的就是当前工作分支。 - 命令:
git branch 分支名
,新建分支 。- 注意:分支在当前的版本建立,也就是说创建分支的时候,master 和 dev 里放的都是最后一次操作的索引。
- 命令:
git branch -d 分支名
,删除本地分支。在有提交的情况下,是不能删除的。 - 命令:
git branch -D 分支名
,强制删除本地分支 。- 不能删除当前所在分支,想要删除一个分支,必须先切换出这个分支才行。
- 不能删除当前所在分支,想要删除一个分支,必须先切换出这个分支才行。
checkout - - 切换分支
- 命令:
git checkout [option] 分支名
,切换到指定分支。-b
:切换到指定分支,如果没有该分支则创建再切换
场景模拟2_1
merge - - 合并
- 命令:
git merge [option] 分支名
,可以将指定分支的内容合并进当前分支。--no-ff
:不使用 fast-forward 模式,可以在 log 中看出该次提交是来自 merge 的,同时带上-m "描述"
选项,进行一次提交。
场景模拟2_2
接着上个场景2_1
注意,这里 merge 的时候给出的信息中有一个 fast-forward
,这个 快速模式 意思就是通过改变了 master 的指向,快速合并。如果是 fast-forward 模式,会分辨不出来是 merge 进来的还是正常提交的。
场景模拟3(合并冲突)
如果在 merge 的时候,两个分支都对相同内容有改动,就会产生冲突,需要我们去手动解决。<<<<HEAD 和 === 之间的是当前分支的改动,=== 和 >>>dev 之间的是分支dev的改动。
非 fast-forward (no-ff) 模式的合并,去看他的分支,能清楚的看出是 merge 进来的内容。
如果想要普通的 merge 也可以这样看出过程,需要在 merge 后加上 --no-ff 选项
stash - - 保存工作区内容、取出
stash 又是一个新区域咯~~
- 推入
- 命令:
git stash / git stash push
,可以将所在分支的 工作区中的 、已经被 git 追踪管理的内容 保存在 stash 中。这份暂存的内容会有一个默认的 msg,内容是当前分支的最近一次提交的 msg,想要自己定义更详细的内容,建议使用下面的命令; - 命令:
git stash save "msg content"
,储存时自定义 msg。
- 命令:
- 查看
- 命令:
git stash list
,查看 stash 中存了哪些内容,行开头是 stash 的名称和序号,idx 为 0 是栈顶,一切操作不带 idx 的都默认为对栈顶进行操作。
- 命令:
- 应用 / 弹出
- 命令:
git stash pop [idx]
,将 stash 中存储的内容取出至工作区,不带 idx 默认从栈顶弹出。 - 命令:
git stash apply [idx]
,将 stash 中存储的内容应用至工作区,stash 中不删除,即可以多次应用,不带 idx 默认应用栈顶内容。
- 命令:
- 删除
- 命令:
git drop [idx]
,将 idx 标记的 stash 从 list 中丢弃,不带 idx 默认丢弃栈顶内容。
- 命令:
首先声明这样一个场景:我们在日常开发的时候为了保证主分支(线上分支)的稳定,新功能和 BUG 修改工作都应该基于主分支,在自己本地的新建分支上完成,测试通过后 merge 进主分支!git 的分支管理提供给我们如上便利,同时还能实现多人共同协作...
假设我们正在分支 dev 进行新功能开发,本地工作区有内容未提交,但是此时有一个新 bug 需要马上处理。如果这时候我们直接 git checkout master 转回主分支并进行 git checkout -b dev_fix_bug,进入 dev_fix_bug 会发现 dev 编辑的未提交内容都会显示出来,这是我们不希望看到的。此时就需要用到 stash 命令,在 dev 开发分支上将未提交的代码保存!
stash 的暂存只在该分支上有效,当该分支上有 stash 的情况下,pull 了远端最新的代码,或者工作区有改动,导致行内冲突,这样的冲突也是需要我们手动去解决的。