版本控制简介
大纲
- 版本控制系统
- 分支模型
- Git
-
- Git介绍
-
- Git命令详情
版本控制系统
- 版本控制系统即VCS(Version Control System)是一种记录若干文件的修订记录的系统,帮助我们查阅或回到某个历史版本
-
- VCS 人肉式
-
- LVCS本地式版本控制系统
-
- CVCS集中式版本控制系统
-
- DVCS分布式版本控制系统
人肉VCS
问题
最大问题,污染了整个工作空间,没办法集中精力维护当前编辑的版本。
Local VCS - 本地式
- 举例
-
- RCS(Revision Control System)
优缺点
优点:具备个人版本管理
缺点:多人协同开发无法开展CVCS - 集中式
- 举例
-
- CVS(Concurrent Versions System)
-
- SVN(Subversion)
-
- Perforce
优缺点
优点:中央版本管理,数据内容可控
缺点:每次操作经过中央库,单点故障DVCS - 分布式
- 举例
-
- Git
-
- Mercurial
优缺点
优点:本地操作,查看历史版本,版本差异比较
分支模型
版本污染,开发提交被迫终止, 问题定位比较复杂:
如果采用如下方式进行开发:分支和分支模型
- 分支: 从目标仓库获得一份项目拷贝,每条拷贝都有和原仓库功能一样的开发线
- 分支模型(branching model)/工作流(workflow): 一个围绕项目**[开发/部署/测试]**等工作流程的分支操作(创建、合并等)规范集合
产品级的分支模型
常驻分支
常驻分支一但被创建就不会进行更改
- development分支
-
- 必须从master分支创建,master就是产品分支,必须是可
- production(master)
-
- 默认分支
活动分支
活动分支,跟着产品发布进行动态创建,有时候也进行删除,留下对应的版本号
- feature分支
-
- 从development创建
- hotfix分支
-
- 如hotfix-36,是从master创建
- release分支
-
- 如release-110,从development分支创建
分支模型-特性开发
分支模型-发布线
注意代码修复分支也应当合并到development开发分支,这样代码修复的分支也可以被追溯
涉及到的环境
- 开发环境 测试/开发环境具有测试数据库,缓存、相应配置等。开发环境需要提交到下一个release的特性分支,在本地测试分支
- 测试环境 测试数据库环境与缓存。release/development分支
- 预发布环境 release分支代码
- 生产环境 master分支代码
Git简介
Git是什么
- git是一个免费开源的分布式版本控制系统(DVCS)
- git是一个基于内容寻址的存储系统
历史
- git的出现离不开linux
-
- 1991-2002:几乎无版本控制(patch包)
-
- 2002-2005:BitKeeper
-
- 2005-至今:git
优势
- 速度快,不依赖网络,本地操作
- 完全的分布式
- 轻量级的分支操作
- Git已经成为现实意义上的标准
- 社区成熟活跃 git离不开github
安装
- Windows:msysgit (编者建议使用:)
- Mac
brew install git
- Ubuntu
apt-get install git
Git命令详解
Git常用命令
命令中输入git
,可以看到git推荐的常用命令:
zhanpeng@GE70:~$ gitusage: git [--version] [--help] [-C] [-c name=value] [--exec-path[= ]] [--html-path] [--man-path] [--info-path] [-p|--paginate|--no-pager] [--no-replace-objects] [--bare] [--git-dir= ] [--work-tree= ] [--namespace= ] [ ]The most commonly used git commands are: add Add file contents to the index bisect Find by binary search the change that introduced a bug branch List, create, or delete branches checkout Checkout a branch or paths to the working tree clone Clone a repository into a new directory commit Record changes to the repository diff Show changes between commits, commit and working tree, etc fetch Download objects and refs from another repository grep Print lines matching a pattern init Create an empty Git repository or reinitialize an existing one log Show commit logs merge Join two or more development histories together mv Move or rename a file, a directory, or a symlink pull Fetch from and integrate with another repository or a local branch push Update remote refs along with associated objects rebase Forward-port local commits to the updated upstream head reset Reset current HEAD to the specified state rm Remove files from the working tree and from the index show Show various types of objects status Show the working tree status tag Create, list, delete or verify a tag object signed with GPG'git help -a' and 'git help -g' lists available subcommands and someconcept guides. See 'git help ' or 'git help 'to read about a specific subcommand or concept.
查询命令帮助
举例
git help clone
git config - 配置
命令行配置git
- 用户配置
-
- git config --global user.name "ZhanPeng"
-
- git config --global user.email
- 配置级别
-
- --local 【默认、高优先级】:只影响本仓库
.git/config
- --local 【默认、高优先级】:只影响本仓库
-
- --global 【中优先级】:影响到所有当前用户的git仓库
~/.gitconfig
- --global 【中优先级】:影响到所有当前用户的git仓库
-
- --system 【低优先级】:影响到全系统的git仓库
/etc/gitconfig
- --system 【低优先级】:影响到全系统的git仓库
config文件
zhanpeng@GE70:~/Workspace/git/git_test$ cat .git/config [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true
git init - 初始化仓库
查看当前仓库的信息
git status
由于没有初始化仓库,则没有任何信息内容,提示我们不是git仓库
zhanpeng@GE70:~/Workspace/git$ git statusfatal: Not a git repository (or any of the parent directories): .git
初始化git仓库
之后我们使用git init
对仓库进行初始化
git init
显示如下信息
zhanpeng@GE70:~/Workspace/git/git_test$ git initInitialized empty Git repository in /home/zhanpeng/Workspace/git/git_test/.git/
查看目录结构
zhanpeng@GE70:~/Workspace/git/git_test$ tree -a.└── .git ├── branches ├── config ├── description ├── HEAD ├── hooks │ ├── applypatch-msg.sample │ ├── commit-msg.sample │ ├── post-update.sample │ ├── pre-applypatch.sample │ ├── pre-commit.sample │ ├── prepare-commit-msg.sample │ ├── pre-push.sample │ ├── pre-rebase.sample │ └── update.sample ├── info │ └── exclude ├── objects │ ├── info │ └── pack └── refs ├── heads └── tags
再次查看信息
zhanpeng@GE70:~/Workspace/git/git_test$ git statusOn branch masterInitial commitnothing to commit (create/copy files and use "git add" to track)
git status - 三对关系的状态变化
三种状态变化
- 未跟踪 <-> 跟踪
- 工作目录 <-> 暂存区
- 暂存区 <-> 最新提交
git-status:对状态的跟踪
-
内容状态
-
- 工作目录
-
- 暂存区
-
- 提交区
-
文件状态
-
- 未跟踪
-
- 已跟踪
这些状态是可以相互进行转化的
git status帮助我们了解这些状态创建文件查看状态
git touch命令创建文件,但是有没有add文件
zhanpeng@GE70:~/Workspace/git/git_test$ touch README.mdzhanpeng@GE70:~/Workspace/git/git_test$ git statusOn branch masterInitial commitUntracked files: (use "git add..." to include in what will be committed) README.mdnothing added to commit but untracked files present (use "git add" to track)
git add
添加单个文件
添加文件内容到缓存区(同时文件被追踪),如下举例添加文件
zhanpeng@GE70:~/Workspace/git/git_test$ git add README.md zhanpeng@GE70:~/Workspace/git/git_test$ git statusOn branch masterInitial commitChanges to be committed: (use "git rm --cached..." to unstage) new file: README.md
添加文件,代表着内容状态:从工作目标转到了缓存区;文件状态:从未跟踪转到了已跟踪。
批量添加
zhanpeng@GE70:~/Workspace/git/git_test$ touch package.jsonzhanpeng@GE70:~/Workspace/git/git_test$ git add .zhanpeng@GE70:~/Workspace/git/git_test$ git statusOn branch masterInitial commitChanges to be committed: (use "git rm --cached..." to unstage) new file: README.md new file: package.json
批量添加会添加在不想要添加的内容
忽略文件
- .gitignore
-
- 在添加时忽略匹配的文件
-
- 仅作用于未追踪的文件
常见gitignore
# Logslogs*. log# Runtime datapids*.pid*.seed# Directory for instrumented libs generated by jscoverage/JSCoverlib-cov# Editor config*.sublime-*# Compiled binary addons (http://nodejs.org/api/addons.html)build/Release# Dependency directorynode_modules
如果不知道如何书写gitignore,github上有官方仓库
只是帮助我们忽略文件,并不是删除git rm - 删除
git rm --cached
仅从暂存去删除git rm
从暂存区与工作目录删除git rm $(git ls-files --deleted)
删除所有被跟踪,但是在工作目录被删除的文件
工作目录与暂存区
工作目录与暂存区可以同时存在两个文件
zhanpeng@GE70:~/Workspace/git/git_test$ vim README.md zhanpeng@GE70:~/Workspace/git/git_test$ git statusOn branch masterInitial commitChanges to be committed: (use "git rm --cached..." to unstage) new file: README.md new file: package.jsonChanges not staged for commit: (use "git add ..." to update what will be committed) (use "git checkout -- ..." to discard changes in working directory) modified: README.md
暂存区
- 每个文件只能放置一次工作目录
-
- 工作目录和暂存区可以同时出现同一文件
-
- 工作目录的文件可以替换掉暂存区的文件
-
- 可以删除文件
-
- 提交暂存区,生成提交内容记录
git commit -
根据暂存区内容创建提交记录
zhanpeng@GE70:~/Workspace/git/git_test$ git commit -m '第一次提交'[master (root-commit) dfebdc2] 第一次提交 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 README.md create mode 100644 package.jsonzhanpeng@GE70:~/Workspace/git/git_test$ git statusOn branch masterChanges not staged for commit: (use "git add..." to update what will be committed) (use "git checkout -- ..." to discard changes in working directory) modified: README.mdno changes added to commit (use "git add" and/or "git commit -a")
直接提交
git commit -a -m 'update message in here'
git log
- commit 分割提交记录,SHA-1编码的HASH标识符
- git-config 配置的提交者的信息
zhanpeng@GE70:~/Workspace/git/git_test$ git logcommit dfebdc23263e5500782b4d6126949eda5dd0e582Author: 抢小孩糖吃Date: Sat Aug 27 16:57:11 2016 +0800 第一次提交
可以通过参数让log更美观
zhanpeng@GE70:~/Workspace/git/git_test$ git log --onelinedfebdc2 第一次提交
更复杂的美观配置
git log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
输出
zhanpeng@GE70:~/Workspace/git/git_test$ git log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit* dfebdc2 - (HEAD, master) 第一次提交 (31 hours ago) <抢小孩糖吃>抢小孩糖吃>
git中alias命令
git config alias.shortname <fullcommand>
超长命令修改成为别名
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
别名输出
zhanpeng@GE70:~/Workspace/git/git_test$ git lg* dfebdc2 - (HEAD, master) 第一次提交 (31 hours ago) <抢小孩糖吃>抢小孩糖吃>
别名会配置到config文件当中
zhanpeng@GE70:~$ cat ~/.gitconfig [username] email = zhan.peng@aliyun.com name = zhanpeng[user] email = zhan.peng@aliyun.com name = 抢小孩糖吃[core] autocrlf = input[alias] lg = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
git diff - 显示不同版本差异
- git diff:
-
- 工作目录与暂存区的差异
- git diff -cached [<reference>]
-
- 暂存区与某次提交差异,默认为HEAD
- git diff <reference>
-
- 工作目录与某次提交的差异
撤销本地修改
git status提示了如何撤销
zhanpeng@GE70:~/Workspace/git/git_test$ git statusOn branch masterChanges not staged for commit: (use "git add..." to update what will be committed) (use "git checkout -- ..." to discard changes in working directory) modified: README.mdno changes added to commit (use "git add" and/or "git commit -a")
git checkout -- <file>
将文件内容从暂存区复制到工作目录
##撤销暂存区内容 git reset HEAD <file>
将文件内容从上次提交复制到暂存区
撤销全部改动
git checkout HEAD -- <file>
将内容从上次提交复制到工作目录
课堂题目
总结
分支操作
git branch
git branch <branchName>
创建分支git branch -d <branchName>
删除分支git branch -v
显示所有分支信息
提交历史是不断向前的线,新提交会指向之前的分支,分支名称如master还有指针对象HEAD,在新提交时,都会指向新提交
git branch创建分支
创建next分支,有别于master分支
git branch next
查看分支信息
$ git branch -v* master 2986d7d hello world next 2986d7d hello world查看分支引用文件内容
yanfa@yanfa-PC MINGW32 /d/ZhanPeng/Workspace/git/git_test (master)$ cat .git/refs/heads/master2986d7d4706045d995943933f95389b8e51eedd0yanfa@yanfa-PC MINGW32 /d/ZhanPeng/Workspace/git/git_test (master)$ cat .git/refs/heads/next2986d7d4706045d995943933f95389b8e51eedd0
这时候我们git commit内容
git checkout
通过移动HEAD检出版本,可以用于分支切换
git checkout <branchName>
- 'git checkout -b <branchName>'
git checkout <reference>
切换分支
git checkout next
我们这里查看文件内容,确实被切换到了next分支,文件内容也跟随发生了变化
切换分支后提交
修改文件内容,使用git commit进行提交,通过branch -v查看
yanfa@yanfa-PC MINGW32 /d/ZhanPeng/Workspace/git/git_test (next)$ git branch -v master 93d0158 master* next cd72638 next branch
恢复到上一个分支
git checkout -
新建并切换到新分支
新建并切换到issue-26分支
git checkout -b issue-26查看分支
yanfa@yanfa-PC MINGW32 /d/ZhanPeng/Workspace/git/git_test (issue-26)$ git branch -v* issue-26 90e2ef3 issue 26 master 93d0158 master next cd72638 next branch
分离HEAD指针
通过git log查看所有提交的信息
yanfa@yanfa-PC MINGW32 /d/ZhanPeng/Workspace/git/git_test (issue-26)$ git logcommit 90e2ef3f203ed8b536bbf63af49bc4b56daa051aAuthor: zhanpengDate: Mon Aug 29 14:48:36 2016 +0800 issue 26commit cd72638964210491b5a594134b3f92c3893555b8Author: zhanpeng Date: Mon Aug 29 14:41:18 2016 +0800 next branchcommit 2986d7d4706045d995943933f95389b8e51eedd0Author: zhanpeng Date: Mon Aug 29 14:23:26 2016 +0800 hello worldcommit 379674d9469e1dd3ef38643259bf4dd96f02981bAuthor: zhanpeng Date: Mon Aug 29 14:22:02 2016 +0800 init and add readme:...skipping...commit 90e2ef3f203ed8b536bbf63af49bc4b56daa051aAuthor: zhanpeng Date: Mon Aug 29 14:48:36 2016 +0800 issue 26commit cd72638964210491b5a594134b3f92c3893555b8Author: zhanpeng Date: Mon Aug 29 14:41:18 2016 +0800 next branchcommit 2986d7d4706045d995943933f95389b8e51eedd0Author: zhanpeng Date: Mon Aug 29 14:23:26 2016 +0800 hello worldcommit 379674d9469e1dd3ef38643259bf4dd96f02981bAuthor: zhanpeng Date: Mon Aug 29 14:22:02 2016 +0800 init and add readme
HEAD跳转到固定提交点,输入值为提交点的前7位
git checkout 379674d
这时候HEAD和具体的分支进行了分离,这个状态我们称为detached head!
如果在HEAD与分支节点分离时,尽量减少提交内容,没有引用指向这个提交记录,会被git垃圾回收。
可以看到暂存区内工作文件内容发生变化,但实质的工作目录并没有变化。git reset
将当前分支回退到历史某个版本
git reset --mixed <commit>
(默认)git reset --soft <commit>
git reset --hard <commit>
分支跳转到固定以前的版本
git reset --mixed
除了切换master回到原来的某个commit,还会将当前的内容复制到暂存区
当我们使用git reset --hard <commit>
时,还会将内容复制到工作目录
git reset --soft <commit>
命令 由于之前的提交没有指针指向,则变成了没有索引的提交,将有可能进行垃圾回收 ###git reflog - 找回垃圾回收内容 yanfa@yanfa-PC MINGW32 /d/ZhanPeng/Workspace/git/git_test (master)$ git reflog93d0158 HEAD@{0}: checkout: moving from next to mastercd72638 HEAD@{1}: checkout: moving from master to next93d0158 HEAD@{2}: checkout: moving from 379674d9469e1dd3ef38643259bf4dd96f02981b to master379674d HEAD@{3}: checkout: moving from issue-26 to 379674d9469e1dd3ef38643259bf4dd96f02981b90e2ef3 HEAD@{4}: commit: issue 26cd72638 HEAD@{5}: checkout: moving from next to issue-26cd72638 HEAD@{6}: commit: next branch2986d7d HEAD@{7}: checkout: moving from master to next93d0158 HEAD@{8}: commit: master2986d7d HEAD@{9}: commit: hello world379674d HEAD@{10}: commit (initial): init and add readme
我们可以通过这个命令,找回commit。注意:这个reflog是不断向前的,也就是说有些信息会丢失。
如何避免hash值进行操作
使用便捷操作
A^
:A上的父提交A~n
:在A之前的第n次提交
##reset与checkout
git stash
保存stash
保存目前的工作目录和暂存区状态,并返回到干净的工作空间
git stash save 'push to stash area'
查看stash记录
git stash list
git stash apply
提取栈顶内容
git stash drop
丢弃栈顶内容
stash pop = stash apply + stash drop
提取栈顶内容,并丢弃
git merge - 合并开发
合并分支
git merge next
git cat-file -p HEAD
获取所有节点的父子关系
合并冲突
git merge next master
发生冲突,指两个分支都做了内容修改
merge fast-forward
不要使用fast forward方式合并出现新的commit
git merge next --no-ff
merge不足
人数增多,分支增多交叉非常多,使用命令帮助我们修剪分支
git rebase
修剪提交历史基线,俗称变基
git rebase master
重演复制过程
git rebase --onto
挑选commit的提交对象会被重演
rebase与merger
**注意:**不要在公有分支(master)使用rebase
git tag
对某个提交设置一个不变的别名
git tag v0.1 hash
远程操作
Git支持本地协议,所以我们可以初始化一个本地的远程服务器。
将当前仓库初始化为一个裸仓库git init ~/git-server --bare
注意:裸仓库是没有工作目录的,如果有工作目录,并且进行了提交,反而对整个git系统发生错乱,中央服务器被动接受,防止多人提交造成冲突。
git push
git push ~/git-server master
git remote
远程仓库相关配置操作
git remote add origin ~/git-servergit remote -v
我肯可以在.git/config文件中,查看到我们填写的远程仓库内容信息。
注意:默认的远程分支名称默认叫做origin冲突解决
在git commit提交后,希望把编写的信息提交到git服务器上,则需要如下命令:
git push origin master我们可以看到上面的人提交的分支,顺利推送到了远程origin上,但是下面的人提交的分支无法同步到远程。这时候需要获取远程仓库的提交历史,并解决冲突。
git fetch
获取远程仓库的提交历史
git fetch origin master
git fetch + merge解决冲突
git merge origin/master
最终会完成一个新的提交
git push origin master
git pull
git pull = git fetch + git mergo
完整获取远程仓库
git clone = git init + git remote + git pull
克隆一个远程仓库作为本地仓库