-
2017.9.6 更新至 分支管理-Bug分支
一年前用过git完成一些课程作业,一些基本知识早就忘记了。正好趁现在复习一遍,以后用处还很多。
主要参考:廖雪峰的Git教程,这确实是一份十分友好的入门教程。在整理笔记前,该教程已经有600多万的阅读量了。
下面进入整理后的正文部分。篇幅较长,可使用目录快速跳转。
目录
安装Git
创建版本库
时光机穿梭
版本回退/前进
工作区和暂存区
管理修改
撤销修改
删除文件
远程仓库
添加到远程仓库
从远程仓库克隆
分支管理
创建与合并分支
解决冲突
分支管理策略
Bug分支(未完待续)
一、安装Git
输入git
,以查看系统是否安装Git。
安装git:
sudo apt-get install git
安装完成后,设置名字和Email:
git config --global user.name "Your Name"
git config --global user.email "[email protected]"
--global
参数表示该计算机上所有Git仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和Email地址。
二、创建版本库
版本库也叫仓库,英文名repository,可以理解成一个目录。该目录里的所有文件都会被Git所管理。Git可以跟踪每个文件的修改、删除,以便追踪历史或者复原修改。
首先创建一个空目录:
mkdir learngit
cd learngit
第二步,通过git init
命令将此目录变成git可以管理的仓库,即初始化(Initialize)。
git init
注意:不一定只有空目录才能创建仓库,选择一个已有文件的目录也可以。
下面在该目录中(或者在其子目录中)添加一个readme.md
文件(.md为markdown文件后缀,有关markdown的学习笔记在这篇文章里有记载,使用纯文本文件.txt
不影响效果),内容如下:
Git is a version control system.
Git is free software.
目前该文件处在目录中而不是Git仓库中,我们还需要两步来把该文件放入仓库:
- 用命令
git add
将文件添加到暂存区(暂存区的概念在后文描述):
git add readme.md
- 用命令
git commit
告诉Git,把文件提交到仓库:
git commit -m "wrote a readme file"
-m
后面输入的是提交说明。
commit
命令一次可以提交多个文件。
例如:再创建三个文件,内容分别为file1
/file2
/file3
,文件名为file1.md
…,并将它们添加、提交。
三、时光机穿梭
首先学习git status
和git diff
这两个命令。
我们修改readme.md
中的内容,在第一行加入单词distributed:
Git is a distributed version control system.
Git is free software.
git status
命令可以查看仓库状态。运行后得到:
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: readme.md
no changes added to commit (use "git add" and/or "git commit -a")
结果表明readme.md
被修改,但没有提交。
git diff
命令可以查看修改前后的差别。运行后得到:
diff --git a/readme.md b/readme.md
index 8d7a2fb..a20531f 100644
--- a/readme.md
+++ b/readme.md
@@ -1,3 +1,3 @@
-Git is a version control system.
+Git is a distributed version control system.
Git is free software.
添加并提交(commit
)该文件,再次运行git status
,便得知:
On branch master
nothing to commit, working tree clean
版本回退/前进
为了产生多个版本,我们再次修改readme.md
文件,将内容改为:
Git is a distributed version control system.
Git is free software distributed under the GPL.
然后使用git add
和git commit
命令提交。
这样我们就有了三个版本的readme.md
文件,查看版本的命令为:git log
,结果如下:
commit dacffeaf5d817a376e7ba0d97d4d7644e3ebe817
Author: Hope <[email protected]>
Date: Tue Sep 5 17:29:04 2017 +0800
add GPL
commit c94ee6cb523d8ea55dfa6c571fc8a366a16e6607
Author: HusterHope <[email protected]>
Date: Tue Sep 5 15:38:42 2017 +0800
add distributed
commit 51b6aa32a9a84142ae9fa4df8b90e6b41e247c3d
Author: HusterHope <[email protected]>
Date: Tue Sep 5 15:36:47 2017 +0800
add 3 files.
commit c2e7c59e9a2454ec64d1c2394350a552a77504b5
Author: HusterHope <[email protected]>
Date: Tue Sep 5 15:34:09 2017 +0800
wrote a readme file
当然,这些信息看上去比较杂,可以用git log --pretty=oneline
命令简化输出,得到:
dacffeaf5d817a376e7ba0d97d4d7644e3ebe817 add GPL
c94ee6cb523d8ea55dfa6c571fc8a366a16e6607 add distributed
51b6aa32a9a84142ae9fa4df8b90e6b41e247c3d add 3 files.
c2e7c59e9a2454ec64d1c2394350a552a77504b5 wrote a readme file
历史记录中,每一行的开头就是版本号。(这些版本号在不同的电脑上是不一样的)
Git中,以HEAD
表示当前版本,HEAD^
代表上一个版本,HEAD^^
代表上上一个版本,HEAD~n
代表上n个版本(n为整数)。
现在我们希望退回上一版本(即“Add distributed”),输入回退命令:
git reset --hard HEAD^
如果需要撤销刚才的回退操作,我们就需要用到版本号(commit_id)了,首先使用命令:
git reflog
得到历史操作的版本号:
c94ee6c HEAD@{0}: reset: moving to HEAD^
dacffea HEAD@{1}: commit: add GPL
c94ee6c HEAD@{2}: commit: add distributed
51b6aa3 HEAD@{3}: commit: add 3 files.
c2e7c59 HEAD@{4}: commit (initial): wrote a readme file
可以看到“add GPL”的版本依然存在,于是使用命令:
git reset --hard dacffea
这样就又回到了最新版本。
工作区和暂存区
工作区(Working Directory)
电脑里能够看到的目录,比如LearnGit文件夹。
暂存区(Stage)
暂存区处于git的版本库(Repository)中,后者是工作区内的隐藏目录.git
,内部除了暂存区外,还有Git自动创建的第一个分支master
,和指向master的指针HEAD
。
在把文件添加到Git版本库中的时候,第一步就是把文件添加到暂存区;
第二步则是将暂存区的内容提交到当前分支。
管理修改
Git跟踪并管理的是修改,而非文件。
每次修改,如果不add
到暂存区,那就不会加入到commit
中。
为了和参考教程保持一致,我们将readme.md修改成:
Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage.
Git tracks changes of files.
并commit.
撤销修改
这一节原教程中有部分内容存在歧义,等以后有使用需求后再整理。
删除文件
比如我们使用指令rm file1.md
或者在文件管理器删除了file1.md,则工作区和版本库就出现了差异:
$ git status
On branch master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: file1.md
no changes added to commit (use "git add" and/or "git commit -a")
Git系统提示我们:
use “git add/rm
..." to update what will be committed. use “git checkout –
..." to discard changes in working directory.
即:可以使用git add/rm <file>...
指令将准备进行commit
的文件提交到版本库,这里用rm
而不用add
- 注意:Git在删除时也需要commit。
还可以用git checkout -- <file>...
命令撤销修改。
四、远程仓库
创建远程仓库的方法详见原文链接,此处不再赘述。
添加到远程库
在Github中添加Repository,选择默认设置。
然后,Github会给出一些操作提示,我们将本地仓库放置到刚创建的Github仓库中:
git remote add origin https://github.com/HusterHope/learngit.git
git push -u origin master
第一步:关联远程库。这里的origin
为远程库的名字,也可以修改成其他名称。HusterHope
为我的用户名,learngit
为Github仓库名。
第二步:把本地内容推送到远程库。这里终端会提示输入GitHub的用户名和密码。-u
参数的作用是让Git把本地的master
分支内容推送到远程新的master
分支,还会把本地的master
分支和远程的master
分支关联起来,在以后的推送或者拉取时就可以简化命令。
这样,只要本地作了提交,就可以通过命令:
git push origin master
把本地分支的修改推送至GitHub.
从远程库克隆
假设我们从零开发,即从远程库克隆到本地。
为了不和本地现有的learnGit
仓库冲突,我们在与它平行的目录下操作,终端输入:
cd ..
返回learnGit
仓库所在目录。
然后克隆远程库:
git clone https://github.com/HusterHope/GitSkills.git
进入GitSkills目录下,使用git status
命令查看状态:
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean
这样就获取了远程库,并完成同步。
- 注意:远程库在克隆到本地后会自动生成一个同仓库名相同的文件夹,这个文件夹就是Git仓库,我们就不用在本地手动
init
仓库了。
五、分支管理
分支就像《命运石之门》里的平行世界线。
如果整个工程只有一条主线,且开发一个新功能需要两周才能完成。第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了;如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。
这样就有了使用分支的需求,特别是用于版本迭代。
创建与合并分支
到现在为止,learnGit
仓库中都只有一条分支master
。master
分支是一条线,Git用master
指向最新的提交,再用HEAD
指向master
,就能确定当前分支,以及当前分支的提交点。
创建分支
首先,创建分支dev
:
git branch dev
可用命令git branch
查看分支。
切换分支
现在切换到刚才创建的分支上,输入指令:
git checkout dev
创建和切换到分支dev
的过程可以用以下一行指令代替:
git checkout -b dev
测试分支
然后就可以在dev
分支上正常提交了。比如加上一句话后提交:
Creating a new branch is quick.
再切换回master
分支:
git checkout master
神奇的事情发生了,刚才我们修改过的readme.md
,新的内容不见了!
原因是刚才的改动处于dev
分支上。
合并分支
现在,我们把dev
分支的工作成果合并到mater
分支上:
git merge dev
其中,git merge
命令用于合并指定分支到当前分支。
删除分支
合并完成后,可用以下命令删除dev
分支:
git branch -d dev
因为创建、合并和删除分支非常快,所以Git鼓励使用分支完成某个任务,合并后再删掉分支,这和直接在master
分支上工作效果是一样的,但过程更安全。
解决冲突
刚才创建和合并分支的过程没有出现两条分支一同前进的进度差异,因此合并的过程也十分顺畅。
当两个分支都对同一文件作出改动时,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突。
例如,创建并切换到分支feature1
,将readme.md
的最后一行改为:
Creating a new branch is quick AND simple.
提交修改后,切换到master
分支,并在此分支上将最后一行改为:(只有AND一词和另一分支有差别)
Creating a new branch is quick & simple.
再次提交修改。
这时,如果我们合并分支,就会产生冲突:
$ git merge feature1
Auto-merging readme.md
CONFLICT (content): Merge conflict in readme.md
Automatic merge failed; fix conflicts and then commit the result.
打开readme.md
,发现Git已经非常贴心地告诉了我们冲突的地方:
<<<<<<< HEAD
Creating a new branch is quick & simple.
Creating a new branch is quick AND simple.
>>>>>>> feature1
于是将master分支上的readme.md
文件最后一行改为:
Creating a new branch is quick AND simple.
并提交。
- 注意,此时Git已经自动帮我们把分支合并了,如果使用指令
git merge feature1
会发现提示:
Already up-to-date.
当然,如果没有两份readme.md
本身内容上没有冲突,合并依然可以进行,我们切换到分支feature1
,加入内容:
Merging branches may cause conflicts.
提交,合并分支:
$ git add readme.md
$ git commit -m "no conflict"
[feature1 98e19b0] no conflict
1 file changed, 3 insertions(+), 1 deletion(-)
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 4 commits.
(use "git push" to publish your local commits)
$ git merge feature1
Merge made by the 'recursive' strategy.
readme.md | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
这样,我们就可以删除分支feature1
,并push
到Github了。
PS.用指令git log --graph
可以查看分支合并图,使用指令git log --graph --pretty=oneline --abbrev-commit
可以查看更简洁的分支图。
分支管理策略
通常,合并分支时,如果可能,Git会用Fast forward
模式,但这种模式下,合并分支后,会丢掉分支信息。也就是在log
里看不出曾经分支的痕迹。
$ git log --graph --pretty=oneline --abbrev-commit
* 192c4f3 merge with no-ff
|\
| * 8e27cb7 add merge
|/
* e1116e0 backup to ff
* cb4a200 git use ff
如果要强制禁用Fast forward
模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。
可以使用--no-ff
变量禁用Fast forward
模式。
分支策略:在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,
master
分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;干活都在
dev
分支上,也就是说,dev
分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev
分支合并到master
上,在master
分支发布1.0版本;你和你的小伙伴们每个人都在
dev
分支上干活,每个人都有自己的分支,时不时地往dev
分支上合并就可以了。
效果如下图:
Bug分支
(未完待续)