#0 系列目录#git
Git 相比 Subversion,不管概念上仍是使用上,复杂度实际上是高出一个等级的。为何这么说?分别看下 git help -a 和 svn help 命令清单的对比,单按这个来看,就若是要掌握全部命令的用法,Git 的学习曲线绝对是比 Subversion 高的。尽管如此,但仍是有愈来愈多项目开始用 Git 来作源码管理了。程序员
实际中,咱们用到的的 Git 命令仍是颇有限的,可能也就 git help 中那些而已。github
#1 “新人报道”# 你刚入职一家公司,或新加入某个团队,立马参与到一个项目中,那么就得获取项目代码,开始你的项目生涯。这个时候通常你须要克隆一份项目代码
,下面都以 GitHub 上的项目地址为例:sql
$ git clone git@github.com:akun/pm.git
以后就进入项目目录,运行项目中的构建脚本,而后就能够熟悉代码,展开具体工做了。编程
固然,有的时候,有一个新项目是由你发起的,你要将初始化的项目工程放到 Git 版本仓库中
:vim
$ mkdir pm $ cd pm $ git init $ touch README.md $ git add README.md $ git commit
Git是分布式的版本控制系统
,因此刚才的操做,算是已经在你本地版本控制起来了,为了推送本地仓库到远程仓库
,就还得执行:分布式
$ git remote add origin git@github.com:akun/pm.git $ git push -u origin master
通常这个时候都会设置下 ~/.gitconfig 或 .git/config 中的配置,最基本的就是用户名和邮箱。svn
确认当前的 Git 配置信息:工具
$ git config --list
设置用户名和邮箱:布局
$ git config user.name akun $ git config user.email admin@example.com
刚才的命令只是对 .git/config 生效,若是想全局生效,也就是 ~/.gitconfig,就得加上 --global 参数,好比:
$ git config --global user.name akun $ git config --global user.email admin@example.com
#2 平常工做# 当你已经逐渐融入了一个项目,可能一天的工做场景或完成某个任务的工做周期是这样的:
##2.1 更新## 不管是清早或下午或晚上,开始了你的一天工做,你首先会更新你的工做目录:
$ cd ~/projects/pm $ git checkout develop # 我想在 develop 分支上开始一天的工做
更新方式一:
$ git fetch --all # 从远程仓库获取全部分支的代码变动 $ git merge
更新方式二:
$ git fetch --all $ git rebase # 默认就衍合 develop 分支的代码了
更新方式三,能够认为是 fetch 和 merge 的合集:
$ git pull # 懒得理解 fetch 和 merge 就直接 pull 吧
这样你就能够在最新的项目代码基础上工做了。
注解:
git pull --rebase 至关因而前面的方式二的合集
这里说的三种方式,可能每一个人或团队都有本身的习惯吧
想了解 Git 中的“衍合”,能够实践下这个文档:Git-分支-分支的衍合
##2.2 修改## 可能你写了一个新的模块,须要归入项目的版本控制:
$ git add tools.py
可能你发现某个模块已经陈旧了,再也不使用了:
$ git rm utils.py
可能你发现一个模块的命名不太合理,须要更名:
$ git mv model.py models.py
可能你要建立一个新的较大的模块,须要归档为目录的方式:
$ mkdir groups $ touch groups/__init__.py $ git add groups/__init__.py
注解:
Git 不支持空文件加加入版本控制,非得必要咋办,后续的其它场景会简单说明下。
可能你发现要写的模块代码布局相似于旧的模块,直接复制个代码模版:
$ cp users/tests.py groups/tests.py $ git add groups/tests.py
注解:
Git 没有自带的所谓 cp 命令
##2.3 检查## 忙碌的一天过去了,或者一个任务完成了,这个时候通常会将你的工做成果,也就是代码更新到版本仓库(分为本地版本仓库和远程版本仓库)。
习惯上会先检查下修改状态:
$ git status
看到一些 Git 状态信息,确认是修改了哪些文件,以后通常会本身 code review 一下代码的改动,可能有的人会习惯直接用 Git 方式来查看:
$ git diff
这里的 diff 只是查看其中“工做目录”和“暂存区域”的区别。要查看“暂存区域”和“本地仓库”的区别
,能够用:
$ git diff --staged # 或 git diff --cached
注解:
最好理解下三个区的概念,以代码角度来理解:
工做目录:git clone 后得到的一份本地的代码,也包括新编辑的,还没有加入版本控制的代码
暂存区域:git add 后暂存起来,还没有 git commit 的代码
本地仓库:git commit 后正式被版本控制记录起来的代码
能够看下图,能更好的理解这三个区:
而后本地运行下相关的单元测试,确认是否有问题。通常来讲这个时候,没有什么特殊状况,就直接进入“提交”甚至是“推送”阶段了,而后结束一个工做日或工做周期,但不免会有些特殊状况出现。
##2.4 取消修改## 当你 code review 完后,发现有些改动不满意;或者运行完单元测试,发现有些测试用例没经过,你可能会进行取消这些修改的操做。
若是还没 add
,那么能够:
$ git checkout -- main.py
为了不恰好跟分支名重合,因此加了两个斜杠(虽然几率很低),若是已经 add 了,但还没 commit
,那么能够:
$ git reset HEAD main.py
万一刚提交完毕,也就是已经 commit 了,才发现代码有问题
,好比:忘记把某个文件提交了,这个时候咋办?Git 好处是能够覆盖上一次提交,那么能够:
$ git add tests.py $ git commit --amend
上面还只是简单的撤销操做,Git 还能支持更高级的重写历史功能,想掌握高级技能的能够实践下这个文档:Git-工具-重写历史
##2.5 解决冲突## 有时候同别人合做写一个模块的代码,会把对方代码合并或衍合过来,好比:对方修复了某个缺陷,你恰好也须要这个修复;再好比:对方完成了某个特性,你也恰好须要用下这 个特性等等各类状况。
大多数状况,代码的合并或衍合不会冲突,但也有冲突的状况,分两种状况说明,第一种是合并操做时候有冲突
:
$ git fetch --all $ git merge bugfix/remove_error # 这个时候就提示你代码冲突了,处理完冲突的代码后 $ git diff # code review 下代码 $ git add remove.py $ git commit # 日志中就多了一条合并操做的日志了
另外一种是衍合操做时有冲突
:
$ git fetch --all $ git rebase bugfix/remove_error # 这个时候就提示你代码冲突了,处理完冲突的代码后 $ git diff # code review 下代码 $ git rebase --continue # 有时候会 git rebase --skip # 直到不用再 rebase 为止
##2.6 提交到本地版本仓库## 最后,一切确认没问题了:code review 完毕,本身以为代码满意了;有可能也合并完别人的修改而且没有冲突了;运行单元测试也经过了。那么就提交代码吧:
$ git commit
##2.7 推送到远程版本仓库## Git 中的 commit 只是提交到本身本地的版本控制仓库,若是想分享你的代码提交,还须要推送到远程的版本控制仓库:
$ git push
#3 在分支工做# ##3.1 建立新的本地分支## 肯定要新开个分支来写代码
,这里以贡献新特性为例子:
$ git checkout -b features/batch_remove $ git branch -a # 确认已经在新分支中工做了 $ git log # 能够确认是基于刚才的分支新分出来的
这里已经隐含了自动切换到新分支的动做了。
##3.2 在新的本地分支工做## 相似,“平常工做”中的工做周期操做,这个时候,你就能够在新分支中进行大刀阔斧的工做了,直到分支中代码符合要求。
##3.3 推送成为做为远程分支## 若是想把分支分享给别人,能够推送到远程版本库
,这样别人能够根据须要来把你的分支代码更新到他本身的本地仓库,例如:
$ git push origin features/batch_remove
##3.4 合并或衍合远程分支## 在分支中工做一段时间后,确认相关的功能代码、测试代码、文档等都提交完毕了,单元测试经过,你们 code review 一致认为没问题,审核经过,最后该分支的持续集成(CI)完整 build 经过。这个时候,就能够进行合并的操做了。
其实前面也提过相似操做,这里再相似重复一遍,若是用合并:
$ git fetch --all $ git merge features/batch_remove # 若是没提示冲突,那就合并成功 # 若是这个时候就提示你代码冲突了,处理完冲突的代码后 $ git diff # code review 下代码 $ git add batch.py $ git commit # 日志中就多了一条合并操做的日志了
若是用衍合:
$ git fetch --all $ git rebase features/batch_remove # 若是没提示冲突,那就衍合成功 # 若是这个时候就提示你代码冲突了,处理完冲突的代码后 $ git diff # code review 下代码 $ git rebase --continue # 有时候会 git rebase --skip # 直到不用再 rebase 为止
这里也提下直接合并本地分支,有时候你建立的分支只是本身用用,没有共享给别人,由于本地已经有了这份分支代码了,那么就省去 git fetch 操做,相似上述方式合并或衍合代码就行。
对比 Subversion 的分支合并操做,实在是简化很多。
##3.5 删除分支## 若是确认工做完毕的分支再也不须要了,那就记得及时清理掉,删除远程分支
:
$ git push origin :features/batch_remove
删除本地分支:
$ git branch -d features/batch_remove
顺便说下,一段时间后,必定有一堆别人的分支,而后你 git fetch 下来了,这样就出如今本地的分支清单中,但远程版本库中已经删除了,若是想本地分支清单干净些,能够在 git fetch 时候这样执行:
$ git fetch --all -p
#4 发布分支# 可能在平时的研发分支工做一段时间后,而且测试完毕,你们以为符合发布条件了。终于能够进入到版本发布阶段的工做了。
##4.1 建立发布分支## 通常来讲这个时候已经将在某个发布分支上工做了,好比:
$ git checkout -b release-1.2 develop # develop 就是平时的研发分支 $ release.sh 1.2 # 好比有个执行发布脚本 $ git commit
##4.2 打标签## 肯定能够发布了,就开始打标签吧,好比:
$ git checkout master $ git merge --no-ff release-1.2 $ git tag -a v1.2 $ git tag # 确认下打上了标签了 $ git push origin v1.2 # 推送标签到远程版本库
#5 其它场景# code review 查看代码,要知道对应代码是由谁写的,好询问了解具体代码的思路:
$ git blame
跟踪问题时候,会查看日志,更方便历史代码定位:
$ git log
以为完整的 Git 命令太长,想用相似 Subversion 的缩写命令,能够用 alias,好比配置文件中能够写上:
[alias] br = branch ci = commit co = checkout diffs = diff --staged st = status lg = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n''%C(white)%s%C(reset) %C(dim white)- %an%C(reset)' --all
有时候合并或衍合代码,但本地有修改了一半的代码没有提交,能够先暂存起来:
$ git stash # 合并或衍合完毕代码后 $ git stash pop # 恢复刚才修改了一半的代码
原来的一个项目想拆分多个项目,又想保留版本仓库记录,能够用下 git subtree split,例如:
$ git subtree split --prefix=plugins/sqli
Git 不支持空文件夹加入版本控制,变通方式:
$ mkdir downloads $ vim downloads/.gitignore # 增长 * 和 !.gitignore 这两条规则
对于习惯命令行下编程的程序员来讲,多看帮助老是好的,直接执行:
$ git help
能够看到 Git 的经常使用命令,若是想看到更全的 Git 命令,能够执行:
$ git help -a
单独查看某个命令的帮助,能够执行:
$ git help add # 好比 add 命令
这里顺带说下几个使用 Git 的好习惯,但有的其实跟 Git 联系也不算大,只是顺带提下:
**保持工做目录干净。**或者说工做目录中的代码变动就为了完成一个任务,即一次只作一件事。完成任务后,就直接 git commit 提交到本地版本仓库的某个分支中,而不用担忧其它任务做出的代码变动无提交。而且,对于分支切换更方便,而不用担忧代码被覆盖或冲突的问题。
**Git 的日志信息足够有效。**足够有效的意思,是说此次提交做出的变动摘要,只要别人阅读了日志就能知道大概,若是为了深刻了解变动细节才会去查看具体代码变动。
**git commit 前 code review。**code review 自己就是个好习惯,提交前确认是一种更为严谨的方式,若是以为本身 code review 发现不了什么问题,那么随便从身边抓个会代码的,跟别人讲解下代码变动的内容,说不定会发现你没考虑到的问题。
**git commit 前跑单元测试。**写单元测试自己也是个不错的习惯,若是项目自己已经有了完备的单元测试覆盖了,那么你对代码的修改,应该能经过单元测试,因此提交前执行一遍是否经过。若是没经过,就得看下是功能代码写的有问题,仍是测试代码有问题,好比:功能需求或接口设计有变化,而测试代码没有同步更新。
**有代码变动及时提交。**有 Git 这种版本控制工具,自己就是为了记录研发过程,避免意外致使代码丢失,若是为了完成某个任务须要很长时间,代码也好久没有提交,风险过高。这个时候,通常会本身开个分支,而将代码提交到分支中,既解决代码要及时提交的问题,又解决代码提交频繁,可能形成代码不稳定影响别人的问题,由于那个分支只有你本身在工做。而这一点,Git 分支的功能更为强大,更加鼓励多开分支。