Git实践

Git 简介

Git一个开放源码的分布式版本控制软件,传说第一个版本是 Linus Torvalds 在2005年为管理Linux 内核开发而在一周内完成的。
Github是一个从Ruby社区诞生出来的一个项目,是基于Git的公共代码仓库,Git火起来很大一部分原因还是Github。
Git每一个网络节点都是一个完整的VCS。但目前开发为了方便“交换”修改,还是用装有gitlab或类似工具的git主机作为中央仓库。

Git 优点/迁移收益

  • 分布式代码仓库,代码更安全,支持离线开发,本地也能提交文件查看版本记录创建分支回滚提交
  • 灵活的分支管理,利用指针切换版本,版本回退迅速快捷
  • 粒子化,元数据存储,最小提交单元是行,版本记录基于修改而不是文件
  • index区提供分批提交,分阶段提交,创建快照方便回滚
  • Git内容采用SHA-1哈希算法,能在遇到磁盘鼓掌和网络问题的时候降低对版本库的破坏,确保代码完整性

Git 模型

  • Git将本地Workspace 切成了两块区域working tree和index。
    | - Workspace
    | - index
    | - 本地Git库 -> 远程Git库
  • Gitflow 1
    alt
    feature分支处理单个需求,完成后提交到dev。 好处:需求区分明显。如果Develop出现阻断性Bug,易需求整体回滚,不影响dev上其他人开发。
    develop分支接入测试,修改bug。
    release分支发灰度包,修改灰度Bug。好处:develop开发不止。 开发不等封板再往trunk上提交代码。
    master只用来发布版本和打Tag。
  • Gitflow 2
    Master(Trunk) -> branch(Tag)

Git 名词

workspace: 工作区
staged/index: 暂存区,保存修改的内容
untracked file : 未跟踪过的文件
add: 把工作区文件提交到暂存区
commit: 把暂存区文件提交到本地git仓库
pull/push: 和远程git仓库同步代码。
revert: 类似SVN merge, 拉取某历史版本代码并生成一个新的commit。
reset: unstage一个文件,放回workspace & 将commit指向历史某个版本。
HEAD 当前版本,^上一个版本(^可累加,可用~number简写)。
reflog: 记录历史操作
checkout 恢复workspace文件 & 切换分支
ssh/https: git支持的两种协议,https传输慢,每次输入密码。
branch: 分支
merge :合并分支, 保留主干和分支所有提交
rebase : 合并分支,遗弃分支提交记录,下次gc会清除。 可用来合并多个commit。
tag: 只是一个快照,类似指向commit的指针,只是不能移动。
blame: 将文件中的每一行的作者、最新的变更提交和提交时间展示出来。
Cherry Pick: 不同的分支中捡出一个单独的commit,并把它和你当前的分支合并。

Git命令行与插件区别

  • 命令行清晰、简单、原始。各种插件杂乱、封装、可视化强。
  • 封装不易理解到Git的精华,信息不全,错误操作不易查找。
  • 命令行下git status 会提示你应该进行的操作。

一个插件的一个pull操作对应到命令行可能进行了三个操作:

git stash  
git pull  
git stash pop  

建议:熟练掌握Git之后再考虑是否使用第三方工具。

拉取代码

当版本库达到一定数量级,一次拉取完整版本库费时不讨好,有时也会因为拉取时间过长而中断,推荐通过depth参数拉取一定深度的代码:

git clone git://git.aikaiyuan.com/aikaiyuan.git. --depth 1  

拉取远程分支

git fetch origin release9.15.1  
git checkout -b release9.15.1 origin/release9.15.1  

如果出现错误:
error: pathspec 'origin/release9.18.1' did not match any file(s) known to git.
可能是因为拉取的时候无法fetch到远程分支,这时候需要配置

git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"  

然后通过--get查看fetch设置

git config --get remote.origin.fetch  

stackoverflow相关链接:http://stackoverflow.com/questions/11623862/git-fetch-doesnt-fetch-all-branches

提交代码

stash方式

我们需要尽量让本地代码和远程版本库同步,所以stash命令可以暂存本地所有加入到index区的修改,然后让本地commit处于远程commit的某一次提交中,然后就可以成功pull再push.

git stash  
git pull  
git stash pop  
git add file..  
git push  

rebase

即使在远程仓库的某一次提交上进行新的提交,造成本地和远程master出现不同的commit指向。git仍然可以通过rebase解决这个问题

git fetch  
git rebase origin/master  
git diff 解决冲突  
git add 冲突文件  
git rebase --continue  

代码回滚

workspace内回滚

git checkout filename  
git checkout filename1 filename2 ……  
git checkout . 当前目录/递归  

index内回滚

分为两步,首先移出index, 再checkout。

git reset HEAD file  
git checkout filename HEAD~2  

commit后回滚

  • 回滚某次提交
    类似svn的merge,会提交一次新的commit。默认会直接提交一次,用--no-commit参数就可以不提交,将revert的修改保存到workspace中。
    git revert HEAD^
    git revert --no-commit 
  • 回滚N次提交
    reset会将回滚的commit记录从commit链中删掉。也就是把HEAD指向reset的版本。
    git reset --soft  不修改 workspace和 index的内容
    git reset --mixed 默认项,不修改workspace, index销毁
    git reset --hard 毛都不留
  • 修改提交记录
    修改最后一次的提交(不重新提交),重新计算SHA1。commit链条中找不到老的提交记录,可以通过reflog找到。
    git add missFile.txt
    git commit --amend
  • submodule
git submodule add 仓库地址 路径  
    git submodule update --init --recursive

Case Study

  1. 如何把放弃本地修改把代码切到最新远程分支
    考虑到git的原理是基于修改的版本记录,所以本地的每次commit记录也都会同步push到远程库,所以直接pull的话并不会取消之前的commit。 我们需要遗弃本地的所有commit,使用reset把Commit的HEAD指针设置到最近pull成功的版本上,然后再pull一次取到最新代码。
  2. 撤销远程提交记录
    首先这样做非常危险,因为可能有一些小伙伴已经基于你提交的版本去开发了。
    如果一定要坑队友的可以本地reset到你想退的版本然后push --force
  3. 之前提交的一个功能出现Bug
    如果阻断其它人开发和正常的测试就立刻把那个功能回滚。 git revert 版本号 如果不影响其它人可以慢慢fix掉再commit --amend补充到那个功能提交记录里
  4. 在master分支灰度发版后发现c文件中有一Bug并修复,develop开发不止,已经有新版本的需求已提交,这时候需要把灰度Bug在develop分支中同步修改。
  5. 拉取远程代码merge后push到远程导致本地覆盖远程
    正常情况下pull的时候会把没有冲突的文件全都拉下来,所以不会出现没有改动的本地文件在push的时候反覆盖远程,考虑是否因为IDE自动格式化或git的自动LF to CRLF功能导致本地文件有改动然后未merge使用了本地版本,再push会导致覆盖掉其它同学的新代码。
  6. 在当前开发分支修改了一个空指针Bug,只想提交这一行代码
    git add bug.java -p

git官网 https://git-scm.com/about
gitflow模型 http://jeffkreeftmeijer.com/2010/why-arent-you-using-git-flow/
githug https://github.com/Gazler/githug

张鹏宇

继续阅读此作者的更多文章