- 总结日常开发中的 Git 进阶使用
- 记录多
SSH
配置,git reflog
解决detached-head
代码丢失问题 - 总结团队协作下,如何保持 git 提交信息简洁
Changelog
- 2018/07/09,撰写
- 2019/03/10,添加多SSH配置
- 2019/04/26,添加
git cherry-pick
- 2019/10/07,添加如何删除git所有历史提交信息
- 2019/10/14,添加
git merge --no-ff
- 2019/12/14,添加
git reflog
使用 - 2020/02/01,添加 删除所有历史提交记录
- 2020/02/01,添加 团队协作下,如何保持 git 提交信息简洁
Ref
git reflog 解决提交代码丢失 detached-head
reflog
是 Git 操作的一道安全保障,它能够记录几乎所有本地仓库的改变。包括所有分支 commit 提交,已经删除(其实并未被实际删除)commit 都会被记录。总结而言,只要 HEAD 发生变化,就可以通过 reflog
查看到。
detached-head
代码丢失找回
- git提交到HEAD detached导致代码丢失
- StackOverflow - gitx How do I get my ‘Detached HEAD’ commits back into master
背景
日常开发中,切换分支误操作,造成本地代码修改丢失。
此时,可以借助 git reflog
找回丢失的代码修改。
丢失产生原因和步骤
首先在 master
分支上开发,此时线上出现 bug 且回到旧版本的 tag。这时 master
分支上有一部分代码修改但未提交。
在 master
分支上执行 git status
,有未提交的代码,如下图所示
在 master
分支上执行 git tag
查看标签信息,如下图所示
此时有未提交的代码,然后执行 git checkout v1.0
这个时候,提示当前分支为 detached HEAD
然后再执行 git add ./git commit
和 git checkout master
,切换回 master
分支。这个时候发现 detached HEAD
分支不见了,master
分支上未提交的代码也不见了。
代码找回
执行 git reflog
查看提交记录
查找对应提交的 commitId
为 247e11b
,然后执行下述命令行,找回丢失的代码
1 | git checkout 247e11b //检出对应的提交 |
删除所有历史提交记录
此处介绍如何删除所有历史提交记录,形成一个全新的仓库。
- 1 - Checkout
1 | git checkout --orphan new_branch |
- 2 - Add all the files
1 | git add -A |
git add
中使用参数-A
或--all
表示追踪所有操作,包含新增、修改和删除Git 2.0版开始,
-A
参数为默认参数,即git add .
等效于git add -A
或git add --all
- 3 - Commit the changes
1 | git commit -am "commit message" |
- 4 - Delete the branch
1 | git branch -D master //同时删除本地和远程分支 |
- 5 - Rename the current branch to master
1 | git branch -m master |
- 6 - force update your repository
1 | git push -f origin master |
下面对上述步骤进行说明
git checkout –orphan
如果你的某个分支上积累了无数次的无意义的提交,git log
信息满天飞,那么可以使用 git checkout --orphan <new_branch_name>
- 基于当前分支创建一个新的“孤儿(
orphan
)”的分支,没有任何提交历史,但包含当前分支所有内容 - 执行上述命令后,工作区(
Workspace
)中所有文件均被认为在该操作中新增(git statue
查看状态,所有文件状态均为new file
,如下图所示),此时执行git add .
会把所有文件添加到缓存区(Index
)
- 严格意义上说,执行
git checkout --orphan <new_branch_name>
后,创建的并不是一个分支,因为此时HEAD
指向的引用中没有commit
值。只有在进行一次提交后,它才算得上真正的分支。
orphan
译为“孤儿”,该参数表示创建一个孤立的分支,没有任何提交历史,且与当前分支不存在任何关系(查看提交信息,可发现其为一个孤立的点,如下图所示)孤儿(
orphan
)无父辈信息,同理,创建的分支也不包含任何历史提交信息
git commit -am
git branch -m
重命名
git push -f origin master
git项目协作——保证git信息简洁
同一分支 git pull 使用 rebase
默认情况下,git pull
使用的是 merge
行为。多人协作开发时,会产生不必要的 merge
提交记录,造成提交链混乱不堪。
推荐在同一个分支更新代码时,使用 git pull --rebase
。
1 | # 为某个分支单独设置,这里是设置 dev 分支 |
分支合并使用 merge –no-ff
Usage
Fast-Forward
:当前分支合并到另一分支时,如果没有冲突要解决,就会直接移动文件指针,并且不会产生合并提交记录。该过程中,存在git
文件指针快速移动, 因此该过程称为Fast-Forward
。--no-ff
(no fast foward
):每一次的合并,都会创建一个新的commit
记录。使用--no-ff
,可以保持原有分支提交链的完整性,并且当该分支被删除时,提交信息依旧存在。
结合上图分析,在 dev
(绿色) 分支上检出 feature-1
分支(蓝色),且 dev
分支不进行任何提交
- 直接
merge
,默认采用Fast-Forward
,两个分支的提交链会合并为一条直线,不利于后期代码审查和维护 - 使用
git merge --no-ff feature-1
合并代码,会产生一个新的提交,且两个分支的提交链不会重叠,利于后期代码审查和维护
merge 默认设置
git merge
默认使用 fast-forward
,可以通过如下方式,修改为默认使用 --no-ff
。
1 | git config --global merge.commit no |
此外,SourceTree 在设置中也可以设置 --no-ff
。
IDE中使用Git
VSCode中使用Git
- VSCode 中使用Git实践
- 推荐安装
Git Lens
和Git History
插件 - 克隆代码
Ctrl + Shift + P
打开命令面板,输入Git
,选择Git Clone
进行克隆代码
- 查看修改(VSCode会使用不同颜色进行标识)
- 红色箭头 - 标识删除行
- 蓝色竖线 - 该处有修改
- 绿色箭头 - 该处为新增
- 提交代码
Ctrl + Shift + G
打开代码管理器进行操作
git clone 设置缓存区
当工程较大时,使用 git clone
拉取代码,可能会出现 early EOF
的报错或者拉取代码失败。
这是因为 git clone
本质上是建立一个 HTTP 连接,工程较大时会超过默认设置的缓存大小。
使用 git config --list
查看 http.postbuffer
的大小,确认是否小于下载的工程大小。
使用 git config --global http.postbuffer 524288000 //500x1024x1024 设置为500M
可以对缓存区大小进行设置。
git clean
git clean -n
- 查看哪些文件将被git clean
清除,只是查看,并不会真正执行清除操作git clean -f
- 删除未跟踪的文件untracked files
git clean -fd
- 连同未跟踪的目录也一起删除git clean -fdx
- 删除未跟踪的文件和文件目录,并移除被忽略的文件。其中,-x
表示移除被忽略的文件并且.gitignore
文件中指定的文件和文件夹也会清除或者清除更改
git tag
git tag
: 显示所有标签git tag -l 'v1.0.*'
: 用通配符查看符合筛选条件的标签git show xxx
: 查看标签信息(提交者,邮箱等)git tag xxx
: 创建轻量标签git tag -a xxx
: 创建含有附注的标签git tag -a xxx -m 'xxxx'
: 创建含有附注的标签,并附加提交信息(默认标签打到当前Head提交状态)git tag -a xxx -m 'xxxx' \<commitID>
: 创建补丁标签,即对之前的提交添加标签git tag -d xxx
: 删除本地标签git push origin --delete tag <tagname>
: 删除远程标签
需要注意的是,
Git 使用的标签有 2 种类型:轻量级的(lightweight
)和含附注的(annotated
)。 —— git tag | Doc
- 轻量级标签就像是个不会变化的分支,实际上它就是个指向特定提交对象的引用。
- 含附注标签,实际上是存储在仓库中的一个独立对象,它有自身的校验和信息,包含着标签的名字,电子邮件地址和日期,以及标签说明,标签本身也允许使用
GNU Privacy Guard
(GPG
) 来签署或验证。
一般我们都建议使用含附注型的标签,以便保留相关信息;当然,如果只是临时性加注标签,或者不需要旁注额外信息,用轻量级标签也没问题。
cherry-pick
使用场景
cherry
译为樱桃,pick
译挑选。git cherry-pick
即选择某一个分支中的一个或几个提交,合并到其他分支中(选择的提交即所需的樱桃),主要使用场景为
- 情况1: 把弄错分支的提交移动到正确的分支上
- 情况2: 将其他分支的提交添加到当前分支
Demo
假设工程有个稳定版分支 v2.0
,还有个开发版分支 v3.0
。开发分支还未彻底完成,不能直接把两个分支合并,这样会导致稳定版本混乱,但是又想增加一个 v3.0
中的功能到 v2.0
中,这里就可以使用 cherry-pick
了。
1 | // 先在v3.0中查看要合并的commit的commit id |
语法
git cherry_pick commitID
:将其他分支的commitID
提交合并到当前分支git cherry_pick commitID
:将其他分支的commitID
提交合并到当前分支,-x
表示保留原提交的作者信息进行提交git cherry_pick <start-commit-id>…<end-commit-id>
: 该功能在Git 1.7.2 版本后才支持,将一个连续区间范围的提交,合并到到当前分支。提交范围区间左开右闭,即(start, end]
git cherry_pick <start-commit-id>^ … <end-commit-id>
: 同上,使用^
表示包含start-commit-id
,即[start, end]
JetBranins 系列IDE,内置了git cherry-pick 快捷键(樱桃图标)
git命令行代理设置
设置代理
1 | git config --global http.proxy 'socks5://127.0.0.1:1086' |
取消代理
1 | git config --global --unset http.proxy |
GitUp
Work quickly, safely, and without headaches.
The Git interface you’ve been missing
all your life has finally arrived.
GitG
gitg
是一个git图形化界面。- 安装
1 | brew install gitg //安装 |
- 使用
1 | gitg // 在目录终端下输入gitg即可 |
Git 代码回滚
场景描述
某项目,并行开发着n个需求。提测时,各需求的代码被合并到测试分支。不久之后,要求把部分需求代码从测试分支抽离出去。使用下图场景进行描述。并行开发3个需求,分别是feature1
,feature2
,feature3
。测试分支为master
。
feature2
与feature3
对同一文件进行修改,故意制造一个冲突。
提测时,各分支代码被合并到测试分支(master
)。首先,featuer1
分支被合并到测试分支。
之后,featuer2
分支也被合并到测试分支。
最后,合并feature3
至测试分支。合并时,产生了与feature2
代码的冲突。
解决冲突之后,继续将feature3
合并至测试分支。
在feature3
提测后,在测试分支上继续修复几个bug。
注意,此时feature2
虽已提测但并未进入测试,此时的bug修复均是针对 feature1
与feature3
。
几天之后,收到通知,feature2
的测试无法正常进行,需将代码从测试分支上抽出。
代码回滚操作
Step 1 切换分支
首先,切换到featuer2
分支。以防万一,创建feature2-copy
分支,对该分支进行备份。
1 | git checkout feature2 //切换到feature2分支 |
Step 2 确定要回滚的提交记录
使用git log
查看feature2-copy
分支的提交记录(输入q
退出git log
环境)。
1 | git log |
如图所示,需要回滚最新的3个提交。实际情况中,针对某需求的提交绝不止3个。若是将提交逐一revert
,工作量是非常大的。需要考虑将n
个commit
合并为一个commit
,最后一同revert
。
Step 3 git rebase 合并提交
使用git rebase -i
来合并commit
,传入需要拼接回滚至的提交的hashcode
。(此处,将所有回滚的提交合并到需要回滚的commit
集合中第一个提交)
1 | //hashcode为需要回滚的commit集合中第一个提交 |
将最近2次提交的command
从pick
改为s
。
在Vim中,
- 输入
i
,进入INSERT
模式。- 输入
ESC
,进入命令行模式。- 输入
:wq
,保存并退出VIM编辑器。
修改后,保存并退出,进入如下对话框。
此时,对最初一次的提交的commit message
进行修改。
修改后保存并退出,使用git log
再次查看 feature2-copy
分支的信息。
如上图所示,3次提交被成功合并。
Step 4 git revert 撤销提交
1 | git revert e544464c3de69adef5ca7556001abebaf40b218b |
保存并退出,再次查看feature2-copy
分支的提交记录。
Step 5 git cherry-pick
1 | git checkout master //切换到测试分支 |
切换到测试分支,并执行 git cherry-pick
命令。至此,成功的将feature2
分支从测试分支上抽离。
Git代码管理与团队协作
代码最终提交效果如上图所示。
- 主分支为
master
,创建一个develop
分支用于开发。 - 开发者
leo
和jack
创建自己的分支leo
和jack
进行开发。开发完成后,将其合并到develop
分支上。 - 项目进展到需要发布时,从
develop
分支创建release
分支,用于测试。测试通过后,将release
分支合并到develop
分支和master
分支上。
Demo目录和初始化
1 | //目录结构 |
git init --bare
用于创建一个“裸仓库”——只含有.git
目录,不含源文件。详情参考 Ref。
1 | mkdir c4 |
为了模拟团队协作,对两个开发者目录,分别设置不同的 user.name
信息。
git config --local
,使用--local
参数,只对本地参数进行配置。
1 | cd leo |
开发流程
master 分支初始化
1 | cd leo |
创建develop分支
1 | git checkout -b develop |
leo进行开发
1 | git checkout -b leo |
git merge --no-ff leo
,合并分支,并保留leo
分支的信息。关于参数--no-ff
的详情,参考 Ref。
最后,将开发分支推送到远程。
1 | git push origin develop |
jack进行开发
首先,从远程拉取最新代码。
1 | git pull origin |
之后,jack进行日常开发,流程同leo开发,此处不再赘述。
release分支测试
项目进展到需发布时,从develop
分支创建release
分支,用于测试。测试通过后,将release
分支合并到develop
分支和master
分支上。
远程多分支代码拉取
此处记录如何拉取远程所有分支,并建立本地分支追踪远程分支。
- Step 1:
clone
远程代码 - Step 2: 在sourcetree中可以看出,远程分支有
origin/master
,origin/dev
和origin/webtest
。此时本地分支为dev
,并建立了追踪关系。
- Step 3: 在对应的远程分支(
origin/master
)上点击,检出分支,设定本地分支名,并追踪远程分支即可。
- Step 4: 最终效果如下图,检出本地分支并追踪远程。
Tip
git branch -r
: 查看远程分支
git branch -a
: 查看所有分支Ref: Blog
多SSH配置
- 取消全局账户和邮箱设置
1 | git config --unset --global user.name |
- 新建SSH key:
1 | cd ~/.ssh |
- 新密钥添加到 SSH Agent
默认只读取 id_rsa
,为了让 SSH 识别新的私钥,需将其添加到 SSH Agent 中
1 | ssh-add ~/.ssh/id_rsa_jd |
若出现 Could not open a connection to your authentication agent
错误,执行以下命令
1 | ssh-agent bash |
- 修改
config
文件
1 | vim config # 若没有,可创建 touch config |
1 | # default user (lbs1203940926@163.com) |
- 拉取JD代码
1 |
|