GIT简史
和Linus的Linux分不开,2002前,Linux居然是Linus本人亲自手工合并代码
2002-2005年,使用商业软件BitKeeper,授权Linux社区免费使用
2005年开发Linux Samba的Andrew 试图破解BitKeeper协议,被发现了,收回使用权
然后Linus花了两周用C写了一个分布式版本控制系统:git
2008年,github上线,git称为最流行的分布式版本控制系统
git词汇表(工作区,版本库,暂存区)
其他版本控制系统以文件变更列表的方式存储信息:存储的是差异积累,但是git不是
Git 更像是把数据看作是对小型文件系统的一组快照。 每次你提交更新,或在 Git 中保存项目状态时,它主要对当时的全部文件制作一个快照并保存这个快照的索引。 为了高效,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。 Git 对待数据更像是一个快照流。小型文件系统
git log中看到的hash是数据存储前作的校验和sha-1
branch:Git 的分支实质上仅是包含所指对象校验和(长度为 40 的 SHA-1 值字符串)的文件,所以它的创建和销毁都异常高效
git status -s:文件的状态
已提交(已经保存在本地版本库 git commit)---> 本地版本库 repository(.git directory
已暂存(git add)--->暂存区staging area
已修改(已经修改,但是还没有add)--->工作区working directory
未跟踪 untracked :之前的快照中不包含这些文件--->工作区working directory
被忽略的基本操作(diff, mv, rm)
git diff:
不加参数只显示尚未暂存的改动,而不是自上次提交以来所做的所有改动,一般是git status 和git add之间使用
--staged/--cached:查看已暂存的将要添加到下次提交里的内容
git commit:
-a(跳过暂存区)
-m(直接写message的subject信息)
git rm:
git rm xxx = rm xxx && git add
如果删除之前修改过并且已经放到暂存区域的话,则必须要用强制删除选项-f
让文件保留在磁盘,但不让 Git 继续跟踪,使用--cached选项(把本应忽略的文件提交的时候)
支持模式匹配
git mv:
git mv A B = mv A B & git rm A & git add B
自定义(别名, .gitignore)
alias:
git config --global alias.co checkout
git config --global alias.unstage 'reset HEAD --'
git config --global alias.last 'log -1 HEAD’
git config --global alias.visual '!gitk'
.gitignore:
无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表
所有空行或者以 # 开头的行都会被 Git 忽略。
可以使用标准的 glob 模式匹配(shell 所使用的简化了的正则表达式)
星号(*)匹配零个或多个任意字符;
[abc]匹配任何一个列在方括号中的字符
问号(?)只匹配一个任意字符;
方括号中使用短划线分隔两个字符(比如[0-9]表示匹配所有 0 到 9 的数字)。
使用两个星号(*) 表示匹配任意中间目录,比如`a/**/z` 可以匹配a/z,a/b/z或 `a/b/c/z`等
匹配模式可以以(/)开头防止递归。
匹配模式可以以(/)结尾指定目录。
要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反。
代码回退(reset, revert, checkout)
git commit —amend:
一着急提交信息写错了/漏提交文件了: 最后只有一次提交
git reset:
不小心 git add了和本次commit无关的东西: git reset HEAD file_name (--mixed是默认参数)
如果你在引用的尾部加上一个^, Git 会将其解析为该引用的上一个提交:HEAD^:“HEAD 的父提交”
merge 解决冲突时,搞花了,git reset --hard HEAD
3个参数:
--soft:撤销了上一次git commit命令
--mixed:撤销一上次提交,但还会取消暂存所有的东西。 于是,我们回滚到了所有git add和git commit的命令执行之前
--hard:你撤销了最后的提交、git add和git commit命令以及工作目录中的所有工作
git checkout (discard)
改花了,不想要了,还原成本地版本库的样子):git checkout — file_name
reset会移动 HEAD 分支的指向,而checkout则移动 HEAD 自身
git revert:
git revert -m 1parent
多人协作(fetch, pull, push, blame, 冲突)
pull:
=fetch+merge
commit message的一些约定:
如何编写commit message: https://chris.beams.io/posts/git-commit/
git emoji约定:https://gitmoji.carloscuesta.me/
git push:
不带参数会默认push 到 origin branch_name, 但是不管tag
冲突:
both modified
解决冲突,add,再commit
默认的commit信息中会告诉你冲突文件是什么
git log:
git log --left-right master…experiment:选择出被两个引用中的一个包含但又不被两者同时包含的提交
git stash:
切分支前,暂存还不想提交的代码
git stash
git stash list
git stash apply
cherry-pick:
假设我们有个稳定版本的分支,叫v2.0,另外还有个开发版本的分支v3.0,我们不能直接把两个分支合并,这样会导致稳定版本混乱,但是又想增加一个v3.0中的功能到v2.0中,这里就可以使用cherry-pick
操纵历史( rebase)
rebase:实质是丢弃一些现有的提交,然后相应地新建一些内容一样但实际上不同的提交
rebase的含义:
把你的commit一个接一个的重现在某个分支的顶端
保持你的commits的顺序
不要对在你的仓库外有副本的分支执行rebase
否则,人民群众会仇恨你,你的朋友和家人也会嘲笑你,唾弃你
一个基于rebase的workflow--atlassian
这个模型基于rebase的第二种含义
过程:
基于master拉分支
开发过程中,如果master有变化,重复:
git fetch origin
git rebase origin/master
如果多人合作,重复:
git fetch origin
git rebase origin/branch
上面两个过程可能有conflicts,rebase的冲突是一个一个出来的
git push
当pull request 被approved
git rebase -i origin/master
此时push,可能会需要-force,因为在改变public branch的历史
最后merge到master:
git checkout master
git pull
git merge --no-ff branch
这些配置,让上面的过程更顺畅:
git config --global branch.autosetuprebase always
git config --global pull.rebase preserve #(this is a very recent and useful addition that appeared in git 1.8.5)
git workflows
git flow第一批模型:
核心是master 分支和develop分支,这两个分支的生命周期是永远
master分支永远是可部署到生产环境的状态,develop是下一个准发布版本
当develop分支OK了,可以发布了,merge 回master,在master上打上release tag
支持性分支(这里所有的分支分类都只是约定的使用规范):
feature/topic分支(主要用于不确定发布时间或者不确定最后会不会被采纳的新特性)
一般从develop开分支,OK后,merge回develop分支
merge回develop使用—no-ff 参数
需要注意的是这里的feature branch不会出现在origin里,也就是不会push到origin/feature branch
release分支
用来支持下次发布的分支,也是从develop拉分支
merge回master,给master打tag
merge回develop(这两部也建议使用—no-ff参数)
删除release分支
hotfix分支
一般从master拉分支,解决生产环境的严重bug(必须立即修复的那种)
merge回master,给master打tag
merge回develop(这两部也建议使用—no-ff参数);如果此时有release分支,可以merge回release分支来代替
删除release分支
github flow:
轻量,非常简洁,适合有持续集成每天多次部署的团队
基于master开分支(branch)
在分支上完成开发
开pull request,并基于PR进行code review
pull request可以在开分支的初期就开,便于和其他人进行初期构想的讨论
在分支上的提交会自动更新在pull request上
部署分支到生产环境
验证分支是否OK
如果不OK,使用master重现部署生产环境
如果OK,merge master,删除分支
核心:master上的代码始终是可部署的,这里的可部署是指已经实际在生产环境验证过了
gitlab workflows:
production branch:app发布或者有发布window时,每次merge master 并不意味着现在master就能部署
production branch代表了生产环境代码,每次实际上线,将master merge到production branch,然后上线
env branches:
a staging environment:master代码
a pre-production environment:开master和pre-production branch 的merge request,
a production environment:从把pre-production merge到production branch
release branches:
从master拉release分支
'upstream first’policy:bug修复先merge到master,然后再cherry-pick到release branch
pull request vs. merge request:
pull request: github等,第一个动作是pull feature branch
merge request: gitlab, 最后一个动作是merge
都可以在branch开始后就开(WIP时可以不分配给任何人)
pull request/merge request merge的时候:
创建一个merge commit
默认使用--no-ff 参数
在descritption里写issue number可以关联并在merge后自动关闭对应的issue
扩展阅读
PRO GIT BOOK 2nd Edition:(有中文版)
https://git-scm.com/book/zh/v2
.gitignore:
https://github.com/github/gitignore
How to Write a Git Commit Message:
https://chris.beams.io/posts/git-commit/
https://gitmoji.carloscuesta.me/
workflows and branching models and strategies:
A Successful Git branching model:http://nvie.com/posts/a-successful-git-branching-model/
Github workflow:https://help.github.com/articles/what-is-a-good-git-workflow/
atlassian:https://www.atlassian.com/blog/archives/simple-git-workflow-simple
gitlab:https://docs.gitlab.com/ee/workflow/gitlab_flow.html