在常见的git开发流程中,我们至少都会有2个最基本的代码管理分支:
- 一个用于存放稳定版本代码的master主分支
- 一个用于开发,测试新需求,代码还可能存在bug的dev分支。
我们期望我们的master主分支的提交历史 能够做到干净,整洁,一目了然。以便维护和同他人协作,就像一条直线一样随着开发的进程不断向前推进。commit日志的规范以及提交的方式会有很大的影响。
以下通过模拟一个需求开发的过程来介绍下我们日常工作中比较常用的一些git命令 以及 个人认为比较 有效 简洁维护我们master提交历史记录的提交方式
开发步骤:
-
创建自己的开发分支
在接到一个新需求时,首先,我们通常会切到我们本地的master,然后拉取远程最新代码(假设我们本地已经有了 项目的master和dev分支)
git checkout master // checkout 切换已有分支 git pull
然后基于最新的master代码创建自己的开发分支, (在master分支下运行)
git checkout -b guanyu-v4.1.4 // guanyu-v4.1.4 自己的本地分支名,可以根据需求来起,也可以根据自己习惯起。本地多个分支,自己可以区分开就好
-
生成新需求的一条commit记录
创建开发分支之后,我们会在我们的新分支上 开发我们的新需求,这个过程中,我们可能产生多条commit 信息。比如今天写了某个功能模块,做了一次提交A,明天写了另外的模块,产生一个新的提交B.
当我们在我们的新开发分支 开发完成新需求时,我们需要把新需求合并到 dev分支,然后提测。 这个合并过程,我们常用的熟悉的命令有:
git merge // merge会产生新的一个commit记录,且代码提交记录不是一条清晰的线性历史 git rebase // rebase可以使得我们的提交记录是线性的,并且不会产生额外的commmit. 看起来就像是沿着一条时间线有序往下开发的(即使我们的开发过程可能是并行的)
但是当我们的开发分支有多条commit时(例如 commitA,commitB,commit),直接使用
git rebase
就有可能 产生多次反复处理冲突的过程和操作,(commitA 处理一次,commitB处理一次)。
通常的做法是,首先在我们的开发分支 上,把我们开发新需求产生的所有commit,通过git rebase -i
命令 合并成为 一条commitgit rebase -i commitA,commitB,commitC // 提交的commitId,通过git log 可以拿到, // 或者 git rebase -i HEAD~3 // 最新的3条commit
然后复制这条合并后的提交的commitId。
-
把这条commit记录追加到dev分支提测
现在,我们就拿到了开发新需求的产生的一条commitId。切换分支到dev.(此时记得拉取远程dev,因为dev可能已经有别人新加的测试代码) 把这条提交的commitId通过
git cherry-pick
命令添加到我们dev分支的提交记录后面。git checkout dev git pull git cherry-pick commitGuanyuId // cherry-pick 可以把更改 通过commitid追加到其他的分支 git push
-
修改代码bug
在新需求代码提交到远程dev部署测试后,我们必然会有修改代码bug的过程。这个过程,我们只需像上面一样,修改bug,产生修改bug的commit, 通过
git cherry-pick
把commit 放到 dev分支并且推送远程。 -
把没有bug的代码最终合并成一条commit提交到master主分支
在所有bug修改完成之后, 我们需要把代码合并master. 首先在我们的开发分支通过
git reabse -i
把开发和修改bug的产生的所有commit 合并成一条。也就是说 我们一个新需求最终 仅仅只会产生 一个commit ,如 [feature] guanyu-v4.1.4 ,最后我们将这条没有bug的新需求commit 追加到master并且推送远程。git checkout master git pull git cherry-pick xxx git push
到此,我们一个完整的新需求 就开发完成并且提交代码到了我们master分支。master只新增了我们新需求的一条commit记录
这样做的好处:我们master的提交日志 都是按需求来的,一个需求只有一个commit. 干净整洁,便于协同开发,不会有没有意义的提交。 而且便于我们review代码,打开我们的 某一个commit,这个需求对应的所有代码更改就会一目了然,清清楚楚。
小结: 1.
git rebase -i
可以用来合并多个commit, 去除没有意义的commit 2.git cherry-pick
可以通过commitid 将变更追加到某一个分支。
其他场景下的git命令
通常,我们真实开发不可能完全像上述一样,新需求开发期间会遇到其他问题,因此需要知道一些其他的常见git命令
- 有时,我们的新需求并不是基于 master开发的,可能是基于远程某一个分支来开发。此时,我们的需求是: 基于远程某一个分支创建本地新分支。此时用到的命令是:
git fetch --all // 同步远程所有更新,例如远程新加的分支,tag等 git branch -a // 查看本地以及远程所有分支名 git checkout -b xxx origin/xxx // 在本地创建一个基于远程xxx分支的新分支
- 当我们在本地创建的新分支做开发时,如果突然需要切换到其他分支处理一些事情。此时,我们可能不想做一次commit ,因为当前功能还没完成。就可以使用
git stash
来暂存。
暂存相关的命令git stash // 暂存被git代理的修改 git stash list // 查看暂存栈 git stash pop // 暂存栈中弹出并且恢复 最近一次缓存的工作目录 git stash apply // 应用某一次缓冲,此时 栈中还会存在这个记录 // stash 相关命令还有很多,例如删除,清空,显示像某个记录改动等
- 我们开发新需求时,新建的本地分支有时候需要推送到远程,防止本地电脑故障造成的代码丢失。 此时,就需要 在远程新建一个分支,并且把我们本地分支代码提交上去。
git push origin 本地分支名 : 远程分支名 // 此命令可以新建一个远程分支并且推送本地分支代码
- 版本回退。我们可以根据commitid 使代码回到相应的commit时候状态。 例如在线上代码出现重大问题时,做代码回退。 通常有以下两种方式:
git reset --hard commitId // 代码回到commitId,并且这条commitId之后的提交都会丢失
git revert -n commitId // 产生一个新的commitid, 追加在原来后面。 新产生的commit,是去除了 你指定的commitId产生的版本。
任何时候,我们都应该使用revert 而不是reset做版本回退。因为我们应该保留所有的代码改动。