[TOC]
背景
以前协同修改文件的方法:
-
通过复制文件来备份不同版本,按照日期等命名规则来区分。
-
文件共享,大家都能编辑,容易被别人的修改覆盖,所以需要文件名加上编辑者的名字。
Git解决的问题
- 以前方式比较麻烦、容易出错。
Git
是一个分布式版本管理系统,是为了更好地管理Linux内核
开发而创立的。
Git
可以在任何时间点,把文档的状态作为更新记录保存起来。因此可以把编辑过的文档复原到以前的状态,也可以显示编辑前后的内容差异。
而且,编辑旧文件后,试图覆盖较新的文件的时候(即上传文件到服务器时),系统会发出警告,因此可以避免在无意中覆盖了他人的编辑内容。
管理历史记录的数据库(Repository)
数据库是记录文件或目录状态
的地方,存储着内容修改的历史记录
。在数据库的管理下,把文件和目录修改的历史记录放在对应的目录下。
本地数据库
为了方便用户个人使用,在自己的机器上配置的数据库。
远程数据库(共享数据库)
配有专用的服务器,为了多人共享而建立的数据库。
如果想要公开在本地数据库中修改的内容,把内容上传到远程数据库就可以了。另外,通过远程数据库还可以取得其他人修改的内容。
创建数据库的方法
有两种方法:
- 创建全新的数据库。
- 复制远程数据库。
修改记录的提交
若要把文件或目录的添加和变更
保存到数据库,就需要进行提交。
执行提交后,数据库中会生成上次提交的状态与当前状态的差异记录
(也被称为revision
)。
系统会根据修改的内容和目录结构
使用SHA1
哈希函数计算出没有重复的40位16进制的英文及数字
来给提交命名。指定这个命名,就可以在数据库中找到对应的提交。
工作树和索引
在Git管理下,我们实际操作的目录就是工作树
。
在数据库和工作树之间有索引
,索引是为了向数据库提交作准备的区域。
https://ws1.sinaimg.cn/large/006tKfTcgy1ftn4p78kw4j30fv07a0tg.jpg
基础配置
三个配置文件
Git
工作环境变量三个存放位置:
-
/etc/gitconfig
:对系统所有用户都普遍适用的配置。 -
~/.gitconfig
:只适用当前用户的配置。 -
your-project/.git/config
:只适用当前项目的配置。
重要配置1:用户信息
$ git config --global user.name "dszkng"
$ git config --global user.email dszkng@outlook.com
这两项代表着是哪个用户提交的。
使用--global
选项时说明配置的是当前用户主目录下这个配置文件。
重要配置2:文本编辑器
Git
需要你输入一些额外消息的时候,会自动调用一个外部文本编辑器给你用。默认会使用操作系统指定的默认编辑器,一般可能会是Vi
或者Vim
。如果你有其他偏好,可以重新设置:
$ git config --global core.editor emacs
重要配置3:差异分析工具
在解决合并冲突时使用哪种差异分析工具?
比如要改用vimdiff
的话:
$ git config --global merge.tool vimdiff
Git
可以理解kdiff3
,tkdiff
,meld
,xxdiff
,emerge
,vimdiff
,gvimdiff
,ecmerge
,和 opendiff
等合并工具的输出信息。当然,你也可以指定使用自己开发的工具。
查看配置信息
$ git config --list
credential.helper=osxkeychain
user.name=dszkng
user.email=dszkng@outlook.com
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
core.ignorecase=true
core.precomposeunicode=true
remote.origin.url=ssh://gogs@git.genecard.cn:2222/zhichun/zc-cms.git
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
branch.master.remote=origin
branch.master.merge=refs/heads/master
$ git config user.email
dszkng@outlook.com
有时候会看到重复的变量名,那就说明它们来自不同的配置文件,不过最终Git
实际采用的是最后一个。
基本使用
Git思想及基本工作原理
只有掌握了基本的原理,用起来才会知其所以然,游刃有余。
分支(branch)操作
使用场景
在开发软件时,可能有多人同时为同一个软件(并行)开发功能
或修复Bug
,可能存在多个Release
版本,并且需要对各个版本进行维护。
所幸,Git的分支功能可以支持同时进行多个功能的开发和版本管理。
什么是分支?
分支是为了将修改记录的整体流程分叉保存
。分叉后的分支不受其他分支的影响,所以在同一个数据库里可以同时进行多个修改。
为了不受其他开发人员的影响,一般是在主分支上建立自己专用的分支。完成工作后,将自己分支上的修改合并到主分支。因为每一次提交的历史记录都会被保存,所以当发生问题时,定位和修改造成问题的提交就容易多了。
创建分支
$ git branch branch_name
- 切换并创建分支
$ git checkout -b branch_name
- 查看创建过的分支
$ git branch
切换分支
$ git checkout branch_name
在切换工作分支时需要进行checkout
操作。Git
会从工作树
还原向目标分支提交的修改内容。checkout
之后的提交记录将被追加
到目标分支。
- HEAD
HEAD
指向的是现在使用中的分支的最后一次更新
。通常默认指向master
分支的最后一次更新。通过移动HEAD
,就可以变更使用的分支。
ps
:提交时使用~(tilde)
和^(caret)
就可以指定某个提交的相对位置
。
比如对于:HEAD3 -> HEAD2 -> HEAD1 -> HEAD(master)。
HEAD1
用HEAD~1
或HEAD~^
表示。
HEAD2
用HEAD~2
或HEAD~1^1
表示。
HEAD3
用HEAD~3
或HEAD~1^2
或HEAD~2^1
表示。
- stash(暂存区)
stash
是临时保存文件修改内容的区域。stash
可以暂时保存工作树
和索引里还没提交的修改内容
,等事后再取出暂存的修改,应用到原先的分支或其他的分支上。
ps
:如果在还未提交的修改内容
以及新添加的文件
,留在索引区域
或工作树
的情况下切换到其他的分支时,修改内容会从原来的分支移动
到目标分支。
但是如果在checkout
的目标分支中相同的文件
也有修改,checkout
会失败的。这时要么先提交修改内容
,要么用stash暂时保存修改内容
后再checkout
。
合并分支
$ git merge branch_name
将完成作业的Topic
分支合并到Merge
分支有两种方法,合并后分支的历史记录
会有很大的差别。
比如bugfix
分支是从master
分支分叉出来的,bugfix
合并到master
时:
![](https://img.haomeiwen.com/i5069668/d7fee4d5844f61ba.jpg)
- merge
$ git merge branch_name
- 如果
master
分支状态没有被更改过,bugfix
分支的历史记录包含master
分支所有的历史记录,这时候合并是最简单的,这就是快进(fast-forward)合并
。
image
- 如果
master
分支在分叉之后又有了更新,这种情况下,要把master
分支的修改内容和bugfix
分支的修改内容汇合
起来。因此,合并
两个修改会生成一个提交。这时,master
分支的HEAD
会移动到该提交上。
image
ps
:执行合并时,如果设定了non fast-forward
选项,即使在能够fast-forward
合并的情况下也会生成新的提交并合并
。
![](https://img.haomeiwen.com/i5069668/cda5e3d4a92f5214.jpg)
- rebase
$ git init
$ vim a.txt # 新增a.txt,内容:create file
$ git commit -a -m 'first commit'
$ git branch issue # 新建issue分支
$ vim a.txt # 修改文件制造冲突条件,增加内容:first edit
$ git commit -a -m 'update a.txt in master branch'
$ git checkout issue # 切换分支
$ vim a.txt # 修改文件,增加内容:second edit
$ git commit -a -m 'update a.txt in issue branch'
$ git rebase master # 合并master和issue,但是会冲突
$ vim a.txt # 修改冲突内容
$ git add . # 不需要commit
$ git rebase --continue # 继续修改冲突后的提交
$ git rebase --abort # 如果要取消rebase的话
$ git checkout master
$ git merge issue # 这时候执行合并操作,实际进行的是fast-forward合并
bugfix
分支的历史记录
会添加在master
分支的后面。历史记录
成一条线,相当整洁。
rebase
操作可能会有冲突,需要修改各自产生冲突的部分。rebase
之后,master
的HEAD
位置不变。因此,要合并master
分支和bugfix
分支,即是将master
的HEAD
移动到bugfix
的HEAD
这里。
![](https://img.haomeiwen.com/i5069668/00a6219d6b25a648.jpg)
![](https://img.haomeiwen.com/i5069668/483fb5ff10c56c33.jpg)
ps
:merge
和rebase
都是合并历史记录
,但是各自的特征不同。
- merge
保持修改内容的历史记录,但是历史记录
会很复杂。 - rebase
历史记录简单,是在原有提交的基础上将差异内容反映进去
。
因此,可能导致原本的提交内容无法正常运行。
删除分支
$ git branch -d branch_name
分支实践
两种分支
分支可以被任意创建,但是,要先确定运用规则
才可以有效地利用分支。
-
Merge
分支
Merge
分支是为了可以随时发布release
而创建的分支,它还能作为Topic
分支的源分支
使用。保持分支稳定
的状态是很重要的。如果要进行更改,通常先创建Topic
分支,而针对该分支,可以使用Jenkins之类的CI工具进行自动化编译以及测试。
通常,大家会将master
分支当作Merge
分支使用。
ps
:在数据库初次提交后,Git
会默认创建一个master分支,不修改分支情况下,每次修改提交都是在master
分支上。
-
Topic
分支
Topic
分支是为了开发新功能
或修复Bug
等任务而建立的分支。若要同时进行多个的任务,请创建多个的Topic
分支。
Topic
分支是从稳定的Merge
分支创建的。完成作业后,要把Topic
分支合并回Merge
分支。
常用分支
- 主分支
- master:只负责
管理发布的状态
,在提交时使用标签
记录发布版本号
。
- master:只负责
- develop:日常开发分支。
- release分支
- release-2018.9:从
develop
分叉出,为release
作准备的分支,做最后的调整,再合并到develop
分支。
- release-2018.9:从
- release-2017.8
- 特性分支(topic分支)
- feature:日常新功能开发。
- bugfix:从
release
分支分叉出,解决完bug
问题,最后合并到release
分支。
- bugfix:从
- hotfix分支
- hotfix-20180805:从
master
分支分叉出,解决完紧急bug
问题,最后合并到develop
分支。因为直接从develop
分支创建可以发布的版本要花许多的时间,所以最好选择从master
分支创建。
- hotfix-20180805:从
- hotfix-20180802
远程分支操作
fetch
- 四种用法
$ git fetch # 更新git remote中所有的远程repo所有branch的最新commit_id,将其记录到.git/FETCH_HEAD文件中。
$ git fetch remote_repo # 只更新名称为remote_repo的远程repo上的所有branch的最新commit_id,将其记录。
$ git fetch remote_repo remote_branch_name # 只更新名称为remote_repo的远程repo上的remote_branch_name分支。
$ git fetch remote_repo remote_branch_name:local_branch_name # 同上,并在本地创建local_branch_name分支来保存远端分支的所有数据。
ps
:FETCH_HEAD
是一个版本链接,记录在本地的.git/FETCH_HEAD
文件中,指向目前已经从远程仓库取下来的分支的末端版本。
pull
- 运行过程
首先,基于本地的FETCH_HEAD
记录,比对本地的FETCH_HEAD
记录与远程仓库
的版本号,然后git fetch
获得当前指向的远程分支的后续版本的数据,然后再利用git merge
将其与本地的当前分支
合并。
push
标签(tag)操作
使用场景
通常在发版本时会给版本库打个tag
,这个tag
就是这次版本的快照,指向某个commit
的指针,类似于branch
,但是tag
不能像branch
一样可以移动。
好处是查找以前某个版本时不用记0348411ee60d4951aa53afb50bc7d6b1c6d5cdfc
这种长串的commit_id
,通过tag
来查找更清晰明了。
两种标签
-
轻标签
:只添加名称。 -
注解标签
:添加名称、注解和签名。
打标签
$ git tag tag_name # 不带注解
$ git tag -a tag_name # 调用编辑器来写注解
$ git tag -am "注解" tag_name # 简单字符串注解
- 为
commit
的历史记录打上标签:
$ git tag v0.1 0348411
查看标签
$ git tag
$ git tag -n # 查看标签和注解信息
- 列出的
tag
是按名称排序的,也可以查看某个tag
的详细信息:
$ git show tag_name
删除标签
- 本地删除:
$ git tag -d tag_name
- 删除远程标签:
$ git tag -d v1.0 # 先本地删除
$ git push origin :refs/tags/v1.0 # 远程删除
推送标签
创建的标签都是只存储在本地的,不会自动推送到远程。所以打错的标签可以在本地安全删除。
- 推送某个标签到远程:
$ git push origin tag_name
- 一次性推送全部尚未推送到远程的本地标签
$ git push origin --tags
已提交(committed)操作
修改最后一次提交
使用选项:git commit --amend
。
$ git log
$ vim xxx # wq
$ git add xxx
$ git commit --amend # 本次修改并入最后一次提交,进入编辑,修改提交说明
撤销/反悔(revert)某次提交
$ git revert HEAD # 撤销前一次commit
$ git revert HEAD^ # 撤销前前一次commit
$ git revert commit_id
ps
:第三种方式撤销,在没有和后面提交相冲突的情况下是直接成功的。
比如说:那次提交是新增了一个文件,后面的每次提交都没有修改这个文件,这时候撤销那次提交,直接做相反的操作把新增的文件删了,比较顺利。
回退/重置(reset)到某次提交
$ git reset --hard # 最后一次commit
$ git reset --hard HEAD~~ # 回退到前前一次commit
$ git reset --hard ORIG_HEAD # 回退到某次commit
ps
:git revert
和git reset
的区别:
-
git revert
是用一次新的commit
来回滚之前的commit
,而git reset
是直接删除指定commit
之后的所有commit
。 -
在回滚这一操作上看,效果差不多。但是在日后继续
merge
以前的老版本时有区别。因为git revert
是用一次逆向的commit
来中和
之前的提交,因此日后合并老的branch
时,导致这部分改变不会再次出现,但是git reset
是之间把某些commit
在某个branch
上删除,因而和老的branch
再次merge
时,这些被回滚的commit
应该还会被引入。 -
git reset
是把HEAD
向后移动了一下,而git revert
是HEAD
继续前进,只是新的commit
的内容和要revert
的内容正好相反,能够抵消
要被revert
的内容。
导入提交
-
使用场景:
在branch1
开发时进行了多次提交,这时切换到branch2
,想把之前branch1
分支提交的commit
都copy
过来,怎么办?
做法
:首先切换到branch1
分支,然后查看提交历史记录,也可以用sourceTree
查看,也可以用命令git log
。 -
用法:
$ git checkout branch1
$ git log # 找到想要操作的commit
$ git checkout branch2 # 将commit导入branch2
$ git cherry-pick commit_id # 单个commit
$ git cherry-pick commit_id1..commit_id2 # commit_id1和commit_id2之间的commit
汇合(合并)提交
- 用法:
$ git rebase -i HEAD~~
自动打开编辑器,HEAD
- HEAD~~
的commit
的都被列出来了,将最后一个pick
改成squash
,保存,进入注释说明编辑,再保存退出,完成合并(两个提交合并成了一个)。
修改提交
参考资料
Git Community Book 中文版
廖雪峰的官方网站 - Git教程
猴子都能懂的GIT入门
Git Book
网友评论