目录
- git branch
- git checkout
- git merge
- git rebase
- HEAD ,在提交树上移动
- 相对引用
- 强制修改分支位置
- 撤销变更
- 整理提交记录
- 提交技巧
- Git Tags
- Git Describe
git日常使用
参考网站:Git 实用技巧记录 和学习git 日常使用git的流程如下
当修改完代码,将本次修改的代码提交到远端流程:
# 工作区 -> 暂存区
git add <file/dir>
# 暂存区 -> 本地仓库 + 注释
git commit -m "memo"
# 本地仓库 -> 远程仓库
git push origin master # 本地 master 分支推送到远程 origin 仓库
拉取远端最新版本代码到本地流程:
# 本地仓库 <- 远程仓库
git clone <git_url> # 克隆远程仓库
git fetch upstream master # 拉取远程代码到本地但不应用在当前分支
git pull upstream master # 拉取远程代码到本地并且应用在当前分支
git pull --rebase upstream master # 如果平时使用 rebase 合并代码则加上
# 暂存区 <- 本地仓库
git reset HEAD <file> # 本地仓库文件内容覆盖暂存区文件内容
# 工作区 <- 暂存区
git checkout -- <file> # 暂存区文件内容覆盖工作区文件内容
将本地仓库代码覆盖到工作区的流程:
# 工作区 <- 本地仓库
git reset <commit> # 本地仓库覆盖到工作区(保存回退文件内容修改)
git reset --mixed <commit> # 同上
git reset --soft <commit> # 本地仓库覆盖到工作区(保留修改并加到暂存区)
git reset --hard <commit> # 本地仓库覆盖到工作区(不保留修改直接删除掉)
git 基础用法(本地)
git branch
创建一个新的分支
git branch your-branch-name
git checkout
创建一个新的分支的同时切换到新创建的分支:
git checkout -b your-branch-name
git merge
将两个分支合并在一起
下面这个是将bugFix分支合并到master上
git checkout master
git merge bugFix (把bugFix合并到当前分支上)
如果你出现这个情况:
可以使用命令:
git reflog expire --expire=now --all
删除所有隐藏的版本
git rebase
还有一种合并分支的方法是git rebase,实际上就是取出一系列的提交记录,复制它们,然后在另外一个地方逐个放下去。
我们想要把 bugFix 分支里的工作直接移到 master 分支上。移动以后会使得两个分支的功能看起来像是按顺序开发,但实际上它们是并行开发的。注意,提交记录 C3 依然存在(树上那个半透明的节点),而 C3’ 是我们 Rebase 到 master 分支上的 C3 的副本。
git rebase master
之后切换master然后把它rebase到bugFix分支上。
HEAD ,在提交树上移动
HEAD 是一个对当前检出记录的符号引用 —— 也就是指向你正在其基础上进行工作的提交记录。
HEAD 总是指向当前分支上最近一次提交记录。大多数修改提交树的 Git 命令都是从改变 HEAD 的指向开始的。
使用git checkout hashvalue
可以指向对应的版本
相对引用
通过指定提交记录哈希值的方式在 Git 中移动不太方便,实际使用时你就不得不用 git log 来查查看提交记录的哈希值。并且哈希值在真实的 Git 世界中也会更长。Git 对哈希的处理很智能。你只需要提供能够唯一标识提交记录的前几个字符即可。
使用相对引用的话,你就可以从一个易于记忆的地方(比如 bugFix 分支或 HEAD)开始计算。使用方法如下:
- 使用 ^ 向上移动 1 个提交记录
- 使用 ~ 向上移动多个提交记录,如 ~3
强制修改分支位置
相对引用使用的最多的就是移动分支,可以使用 -f 让分支指向另一个提交:
# 将master分支强制指向HEAD的第3级父提交
git branch -f master HEAD^3
撤销变更
主要有两种方法用来撤销变更:
一是 git reset
,还有就是 git revert
。
git reset 通过把分支记录回退几个提交记录来实现撤销改动。你可以将这想象成“改写历史”。git reset 向上移动分支,原来指向的提交记录就跟从来没有提交过一样。
在reset后, 之前 所做的变更还在,但是处于未加入暂存区状态。
虽然在你的本地分支中使用 git reset 很方便,但是这种“改写历史”的方法对大家一起使用的远程分支是无效的。
为了撤销更改并分享给别人,我们需要使用git revert HEAD
在我们要撤销的提交记录后面多了一个新提交。这是因为新提交记录 C2’ 引入了更改 —— 这些更改刚好是用来撤销 C2 这个提交的。也就是说 C2’ 的状态与 C1 是相同的。revert 之后就可以把你的更改推送到远程仓库与别人分享啦。
整理提交记录
开发人员有时会说“我想要把这个提交放到这里, 那个提交放到刚才那个提交的后面”, 而接下来就讲的就是它的实现方式。
第一个命令:
git cherry-pick <提交号>
如果你想将一些提交复制到当前所在的位置(HEAD)下面的话, Cherry-pick 是最直接的方式了。
将 side 分支上的工作复制到 master 分支:
git cherry-pick C2 C4
图1 | 图2 |
当我们不清楚提交记录的哈希值时,可以利用交互式rebase:
交互式 rebase 指的是使用带参数 --interactive
的 rebase 命令, 简写为 -i
如果你在命令后增加了这个选项, Git 会打开一个 UI 界面并列出将要被复制到目标分支的备选提交记录,它还会显示每个提交记录的哈希值和提交说明,提交说明有助于你理解这个提交进行了哪些更改。
在实际使用时,所谓的 UI 窗口一般会在文本编辑器 —— 如 Vim —— 中打开一个文件。
当 rebase UI界面打开时, 你能做3件事:
- 调整提交记录的顺序(通过鼠标拖放来完成)
- 删除你不想要的提交(通过切换 pick 的状态来完成,关闭就意味着你不想要这个提交记录)
- 合并提交。 它允许你把多个提交记录合并成一个。
git rebase -i HEAD~4
然后我们在gui中没有选中任何节点
图1 | 图2 |
提交技巧
情景一:
你之前在 newImage
分支上进行了一次提交,然后又基于它创建了 caption
分支,然后又提交了一次。
此时你想对的某个以前的提交记录进行一些小小的调整。
我们可以通过下面的方法来克服困难:
- 先用
git rebase -i
将提交重新排序,然后把我们想要修改的提交记录挪到最前 - 然后用
commit --amend
来进行一些小的修改 - 接着再用
git rebase -i
来将他们调回原来的顺序 - 最后把master以到修改的最前端就可以了
例如下面:
使用命令:
git rebase -i HEAD~2
# 然后调整顺序
git commit --amend
git rebase -i HEAD~2
# 然后调整顺序
git branch -f master caption
图1 | 图2 |
情景二
仍然是上面的要求,不过rebase的问题就是要进行两次排序,而这有可能造成由 rebase 而导致的冲突。下面我们使用git cherry-pick
:
cherry-pick
可以将提交树上任何地方的提交记录取过来追加到 HEAD 上(只要不是 HEAD 上游的提交就没问题)。
git checkout master
git cherry-pick C3 C2
git commit --amend
git checkout C1
git cherry-pick master C3'
git branch -f master HEAD
图1 | 图2 |
Git Tags
tag主要用于发布版本的管理,一个版本发布之后,我们可以为git打上 v.1.0.1 v.1.0.2 …这样的标签。
建立一个标签,指向提交记录C1,表示这是我们的1.0版本
git tag v1 C1
注意,如果你不指定提交记录,Git 会用 HEAD 所指向的位置。
图1 | 图2 |
Git Describe
标签在代码库中起着“锚点”的作用,Git 还为此专门设计了一个命令用来描述离你最近的锚点(也就是标签),它就是 git describe
语法如下:
git describe <ref>
可以是任何能被git识别成提交记录的引用,如果没有指定的话,会以目前所检出的位置(HEAD)
输出结果为:
<tag>_<numCommits>_g<hash>
tag
表示的是离ref
最近的标签,numCommits
表示的是这个ref
与tag
相差多少个提交记录,hash
表示的是你所给定的 ref
所表示的提交记录哈希值的前几位。
我们对如下分支进行describe:
git describe master
output: v1_2_gC2
git describe side
output: v2_1_gC4
git 基础用法(远程)
Git 远程仓库相当的操作实际可以归纳为两点:向远程仓库传输数据以及从远程仓库获取数据。既然我们能与远程仓库同步,那么就可以分享任何能被 Git 管理的更新.
git fetch
git fetch 做了些什么呢?
- 从远程仓库下载本地仓库中缺失的提交记录
- 更新远程分支指针
实际上将本地仓库中的远程分支更新成了远程仓库相应分支的最新状态。远程分支反映了远程仓库在你最后一次与它通信时的状态,git fetch 就是你与远程仓库通信的方式。
git fetch
并不会改变你本地仓库的状态。它不会更新你的 master
分支,也不会修改你磁盘上的文件。
git fetch 的理解为单纯的下载操作。
git pull
当远程分支中有新的提交时,你可以像合并本地分支那样来合并远程分支:
git cherry-pick origin/master
git rebase origin/master
git merge origin/master
实际上,由于先抓取更新再合并到本地分支这个流程很常用,因此 Git 提供了一个专门的命令来完成这两个操作。它就是 git pull
下图的左侧是远程仓库。
我们通过命令:
git fetch
git merge origin/master
先下载C3,然后通过merge合并了这一个提交记录,这样我们的master分支就包含了远程仓库中的更新。
git pull
其实就是git fetch
和git merge <just-fetched-branch>
的缩写
git push
git push 与 git pull 相反,负责将你的变更上传到指定的远程仓库,并在远程仓库上合并你的新提交记录。一旦 git push 完成, 你的朋友们就可以从这个远程仓库下载你分享的成果。
偏离的提交历史,十分重要!!!
假设你周一克隆了一个仓库,然后开始研发某个新功能。到周五时,你新功能开发测试完毕,可以发布了。但是 —— 天啊!你的同事这周写了一堆代码,还改了许多你的功能中使用的 API,这些变动会导致你新开发的功能变得不可用。但是他们已经将那些提交推送到远程仓库了,因此你的工作就变成了基于项目旧版的代码,与远程仓库最新的代码不匹配了。
此时git是不允许你push变更的,它会强制你先合并并远程最新的代码,然后才能分享你的工作。
最直接的方法就是通过 rebase 调整你的工作
如果我们在push之前做rebase的话
git fetch
git rebase o/master
git push
图1 | 图2 |
git fetch 更新了本地仓库中的远程分支,然后用rebase将我们的工作移动到最新的提交记录下面,最后在git push推送到远程仓库。
也可以精简指令为:
git pull --rebase
git push
我们还可以使用merge
尽管git merge
不会移动工作(它会创建新的合并提交),但是它会告诉git 你已经合并了远程仓库的所有变更。这是因为远程分支现在是你本地分支的祖先,也就是说你的提交已经包含了远程分支的所有变化。
git fetch
git merge o/master
git push
我们用 git fetch 更新了本地仓库中的远程分支,
然后合并了新变更到我们的本地分支(为了包含远程仓库的变更),
最后我们用 git push 把工作推送到远程仓库
图1 | 图2 |
但是这样命令就多敲了很多, `git pull` 就是 `fetch` 和 `merge` 的简写,类似的 `git pull --rebase` 就是 `fetch` 和 `rebase` 的简写!
配置参数选项
全局配置
# 用户信息
git config --global user.name "your_name"
git config --gloabl user.email "your_email"
# 文本编辑器
git config --global core.editor "nvim"
# 分页器 分页器作用详见git config --global core.pager "more"
# 别名
git config --gloabl alias.gs "git status"
# 纠错
git config --global help.autocorrect 1
个人配置
# 不加 --global 参数的话,则为个人配置
git config --list
git config user.name
git config user.name "your_name"
# 如果在项目中设置,则保存在 .git/config 文件里面
cat .git/config
[user]
name = "your_name"
......
merge 与 rebase的选择
只对尚未推送或分享给他人的本地修改执行rebase操作操作清理历史。
从不对已经推送到仓库的提交记录执行rebase操作。
更新仓库提交历史
合并多个commit提交记录
当为了完成一个功能提交多个commit记录,在提交PR之前,需要整理这些提交记录。包括合并、删除等操作
# 调整最近五次的提交记录
git rebase -i HEAD~5
git rebase -i xxxxx # 往前第六次的commit版本号
reword xxxx3 3rd commit
squash xxxx4 4th commit
pick xxxx5 5th commit
fixup xxxx6 6th commit
drop xxxx7 7th commit
# 查看提交历史记录
git log
* ce813eb - (HEAD -> master) 5th commit
* aa2f043 - 3rd commit -> modified
* 6c5418f - 2nd commit
* c8f7dea - 1st commit
对于各个参数的解释:
编号 | 选项列表 | 对应含义解释 |
1 | p/pick | 使用这个记录 |
2 | r/reword | 使用这个记录,并且修改提交信息 |
3 | e/edit | 使用这个记录,rebase时会暂停允许你修改这个commit |
4 | s/squash | 使用这个记录,将当前commit与上一个commit合并 |
5 | f/fixup | 与squash相同,但是不会保存当前commit的提交信息 |
6 | x/exec | 执行其他shell命令 |
7 | d/drop | 移除这个commit记录 |
删除意外调试的测试代码
有时提交之后,我们才发现提交的历史记录中存在这一些问题,但是此时又不希望新生成一个commit记录。此时可以修改之前的commit提交记录。
git add filename
# 更改当前最新一次提交记录
git commit --amend
# 改变且不改变提交信息
git commit --amend --no-edit
# 如果想改变提交最新一次提交记录并修改信息
git commit --amend -m "memo"
如何处理工作中断
现在我们正在一个分支上为项目添加一个小功能,此时,线上环境的有一个bug需要让我们修复,但是我们添加的小功能并没有完成。
如果此时,我们直接切换到主干分支,会将之前分支没有来得及提交的内容全部都带到了主干分支上,这是我们不想看到的情况。
此时我们需要保存上个分支的工作状态,在我们修改完成线上bug之后,再继续工作。
git中可以使用stash子命令帮助我们当前工作区、暂存区当中修改都保存到堆栈之中。等我们需要处理的时候,再弹出堆栈中的内容,我们再次开发
# 存储当前的修改但不提交
git stash
# 保存当前状态 包括 untracked的文件
git stash -u
# 展示所有 stashes 信息
git stash list
# 回到某个 stash 状态
git stash apply <stash@{n}>
# 删除储藏区
git stash drop <stash@{n}>
# 回到最后一个 stash 的状态并删除这个 stash 信息
git stash pop
# 删除所有的 stash 信息
git stash clear
# 从 stash 中拿出某个文件的修改
git checkout <stash@{n}> -- <file-path>
比较保险的做法是将当前所有修改进行push并保存到远程仓库中,这样不要害怕本地文件丢失的问题。等到我们需要继续开发的时候,拉下对应内容,再想办法进行补救。
# 将工作区和暂存区覆盖最近一次提交
git commit --amend
git commit --amend -m "memo"
# 会退到指定版本并记录修改内容(--mixed)
# 本地仓库覆盖到工作区(保存回退文件内容修改)
git reset xxxx(某个版本号)
git reset HEAD~ 退回到上一个版本
git reset HEAD~2 退回到上两个版本
# 本地仓库覆盖到工作区(不保留修改直接删除掉)
git reset --soft <commit/reference>
# 本地仓库覆盖到工作区(保留修改并加到暂存区)
git reset --hard <commit/reference>