注意内容:
- 创建与合并分支
- 分支管理策略
- 分支暂存
- 补丁
- 多人协作开发
一、创建与合并分支
1.1 相关概念
master分支
默认情况下,master是一条线,git是利用master指向最新的提交(因为master默认指向最新的提交点),再用HEAD
指针指向master,就能确定当前分支以及当前分支的提交点。最终发布的就是master分支,但是对于其他的开发者不应该在master上进行修改或编写,应该建立子分支。子分支
刚开始建立子分支时,子分支和master分支是指向同一个位置(提交点)的,而此时HEAD
指针在子分支创建之后就会发生变化,不再指向master分支,而是指向子分支。也就是说HEAD
指针需要指向当前工作的分支。而我们在子分支上进行任何操作(修改,添加)是不会影响到master分支的,而只是子分支向前推进。合并
当子分支向前推进的时候,master所指向的位置是不变的,而我们所有的版本都保存在master上,于是这里我们需要将子分支和master分支进行合并。因为我们是在master分支上进行合并操作,那么HEAD
指针必须指向master分支(也就是要断开HEAD
与子分支)。于是就将master指向的提交点指向了子分支指向的提交点。合并之后我们如果确定不需要再使用子分支了,那么可以将其删除。
1.2 相关操作
- 创建一个分支brh:
git branch brh
- 查看
git branch
可以看到提示当前的工作区中有两个分支,同时使用*号提示指向master分支。
- 切换到brh分支
git checkout brh
可以看到我们再次查询的时候发现指向了brh分支。
简化操作(创建并切换):
git checkout -b brh
注意:这里我们在之前已经创建了此分支,需要先将之前的同名分支删掉再使用此命令。
- 删除分支
git branch -d brh
注意:在当前分支上是不能删除此分支的,需要切换到其他分支上再删除。
- 修改相关文件
Hello.java
(我们这里在仓库mypro
中操作)
public class Hello{
public static void main(String args[]){
System.out.println("Hello World");
System.out.println("Hello World--001");
System.out.println("Hello World--002");
System.out.println("Hello World--003");
System.out.println("modify --001");
System.out.println("modify --002");
}
}
此时的修改是在子分支上进行的,我们查询一下:
可以看到有明显的提示是在brh分支上。而主分支上的数据是不会改变的。我们可以先将子分支提交,而后在回到主分支上进行查看。
可以发现主分支上的文件确实没有改变。
- 将两个分支都推送到远程仓库中
git remote set-url origin https://github.com/yjaal/mypro.git
git push origin master
git push origin brh
但是最终发布的版本一定是master,于是这里需要进行合并。
- 合并(在主分支上操作)
git merge brh
首先我们分别改变主分支和brh中的Hello.java
文件:
master:
public class Hello{
public static void main(String args[]){
System.out.println("modify --003");
}
}
brh:
public class Hello{
public static void main(String args[]){
System.out.println("brh--modify --003");
}
}
然后我们分别提交后推送:
这里可以发现这里出现了合并冲突(
Automatic merge failed
),我们查看冲突的内容:
git status
提示信息是修改了两次
Hello.java
文件,查看此文件:
public class Hello{
public static void main(String args[]){
<<<<<<< HEAD
System.out.println("modify --003");
=======
System.out.println("brh--modify --003");
>>>>>>> brh
}
}
这表明HEAD
和brh
都有哪些信息,而此时我们是在主分支上,所以HEAD
指向master的内容,这里进行了标记。现在就必须手工修改冲突的文件。
- 手工修改冲突文件
Hello.java
master:
public class Hello{
public static void main(String args[]){
System.out.println("modify --003");
System.out.println("brh--modify --003");
}
}
这里我们希望主分支和子分支上修改的内容都进行保留,于是文件我们修改如上。然后我们再次进行提交。
可以看到冲突已经解决。然后进行推送。
- 在实际开发中存在很多分支合并的情况,那就需要查看分支合并的历史记录
git log --graph --pretty=oneline
这里“graph”表示使用绘图的方式进行显示。此是需要删除brh分支
二、分支管理策略
之前使用的合并方式都是使用的Fast forward
的合并方式,而此种方式只是改变了master的指针,可是有些时候也可以不使用此种快速合并方式,即在合并命令上增减上一个-no-ff
的参数,这样就表示在合并之后会自动的生成一个新的提交号,从而保证合并数据的完整性。可以这样理解,之前的合并方式是将子分支合并到master分支上,于是HEAD
指向的位置没有改变,而此种方式是让合并之后的文件再次进行提交,而HEAD
指向这个新的提交点,也就是主分支向前推进了一步。
- 创建新的分支:
git checkout -b brh
- 新建一个新的文件
Emp.java
public class Emp{
public static void main(String args[]){
System.out.println("the new file Emp.java");
}
}
- 提交修改
git add .
git commit -m "create a new file"
- 切换回master分支
git checkout master
- 使用非快速的方式合并分支
git merge --no-ff -m "no fast-forward merge" brh
此种方式会有一个新的提交,所以需要加上-m
的参数
- 查看一下提交的日志信息
git log --graph --pretty=oneline --abbrev-commit
这里我们在后面加上了一些参数,让显示的信息更加简洁。从提示信息中可以看到,出现了一个新的提交号,而之前的合并我们发现好像也有一个提交号,但是那是在合并发生冲突之后我们解决冲突重新提交产生的,并不是在合并的时候产生的。
于是这里我们的分支策略就是:
- master分支应该是非常稳定的,也就是仅用来发布新版本,不要在此分支上开发。
- 在各个子分支上进行开发工作,避免出现问题。
三、分支暂存
比如我们正在一个子分支上进行开发,但是突然有新的任务需要马上完成,同时此时我们不能将子分支上的代码提交,因为在实际开发中不能将有问题的或者没开发完的代码提交,于是此时我们就需要将分支暂存起来。
- 创建并切换到一个新的分支(先将没用的分支删除)
git checkout -b brh
- 在分支上编写
Emp.java
文件
public class Emp{
public static void main(String args[]){
System.out.println("the new file Emp.java");
System.out.println("modify --001");
}
}
- 将此文件保存在暂存区之中,此时代码还没有开发完成,不能提交
git add .
- 将此分支暂存
git stash
这样此工作场景就暂存起来了,查看:
我们在将分支暂存之后仓库的状态是干净的。
- 此时如果我们要修改的代码还在master分支上,于是我们又切换到master分支上:
git checkout master
- 再次创建一个新的分支进行开发
git checkout -b dev
- 修改
Hello.java
文件
public class Hello{
public static void main(String args[]){
System.out.println("modify --003");
System.out.println("brh--modify --003");
System.out.println("dve branch modify");
}
}
- 提交此分支
git commit -a -m "dev branch modify"
- 现在如果此分支完成了,那我们需要切换回brh分支上继续开发,但是注意:此时还处于dev分支上。切换回master分支并合并dev分支:
git checkout master
git merge --no-ff -m "merge dev branch" dev
git branch -d dev
- 此时我们需要切换到brh分支上继续开发,还需要恢复之前的开发状态
git checkout brh
git stash list
这里我们回到brh分支上查看了暂存区中的状态,表明brh分支上有一个暂存,同时对于brh上的暂存我们使用此命令在主分支上也是可以查到的。那现在我们需要将暂存区进行恢复。有两种形式,如下。
- 形式一:先恢复,而后再手工删除暂存
git stash apply
git stash drop
- 形式二:恢复的同时也将暂存内容删除
git stash pop
此时就可以进行开发了。
四、补丁(patch)
补丁就是针对局部代码的修改。在很多的代码维护中,如果按照最早克隆的方式克隆下来再修改,而我们又只是修改一段很小的部分,此时这种方式就太麻烦了,于是这里我们希望将代码的一些补丁信息发给开发者,而开发者需要知道哪些代码被修改了,这样修改的代价就比较低了。git提供了两种方案:一种是使用git diff
生成标准的补丁;另一种是使用git format-patch
生成git专用的补丁。
4.1 生成标准的补丁
补丁一定是针对文件的修改进行的,这里以Emp.java
为例:
//当前状态
public class Emp{
public static void main(String args[]){
System.out.println("the new file Emp.java");
}
}
- 建立一个新的分支并查看
git checkout -b cbrh
- 修改
Emp.java
文件:
public class Emp{
public static void main(String args[]){
System.out.println("the new file Emp.java");
System.out.println("modfigy --001");
System.out.println("modfigy --001");
}
}
- 比较代码
git diff Emp.java
- 在cbrh分支上进行提交
git commit -a -m "commit cbrh-001"
注意:此时我们并没有合并到主分支上。
- 生成补丁文件(
myPatch
)
git diff master > myPatch
此时在仓库中会生成一个补丁文件。这个文件是git可以读懂的信息文件。之后需要模拟另外一个开发者,他是专门进行补丁合并的开发者。
- 创建并切换到一个新的分支
git checkout master
git checkout -b patchbrh
注意:这里必须先切换到主分支,不然就是在brh上创建分支,那么新建的分支就和brh分支上的内容一样了,就不能进行模拟了。
- 应用补丁信息
git apply myPatch
此时我们查看patchbrh分支中的
Emp.java
文件就和cbrh分支上的此文件一样了,表示成功应用了此补丁。
- 提交合并分支patchbrh
git commit -a -m "patch apply"
git checkout master
git merge --no-ff -m "merge patchbrh" patchbrh
4.2生成git专用补丁
- 先进行一个清空:
git branch -D cbrh
git branch -D patchbrh
- 创建一个分支cbrh
git checkout -b cbrh
- 修改
Emp.java
文件
public class Emp{
public static void main(String args[]){
System.out.println("the new file Emp.java");
System.out.println("modfigy --001");
System.out.println("modfigy --001");
System.out.println("modfigy --002");
}
}
- 提交
git commit -a -m "modify --002"
- 下面与原始代码进行笔记,并且会自动生成补丁文件
git format-patch -M master
现在表示要与master分支笔记,其中-M
就是指定分支。
此时就已经生成了一个补丁文件
0001-modify-002.patch
,这个补丁文件严格来讲是一个email形式的数据,我们需要发送给另一个开发者。
- 创建并切换到分支patchbrh
git checkout master
git checkout -b patchbrh
- 应用补丁
git am 0001-modify-002.patch
- 提交补丁文件
git commit -a -m "one patch file"
可以看到这里我们直接提交的时候提示我们有一个新增的文件(就是补丁),这里我们先将补丁删除,然后再提交。之后就可以进行代码的更正了。
- 关于两种补丁方式的说明
生成标准补丁就是兼容性比较好,如果不是在git管理的仓库上,此类方式生成的补丁是非常容易被接受的;而如果是向公共的开发社区进行代码的补丁更正,那么建议使用git专用的补丁,这样不仅标准,而且也可以将更正人的id公布了。
五、多人协作开发
分支的处理实际上就是为了更好的多人开发做出的准备,那么下面我们利用两个命令行窗口的方式(模拟其他开发者)进行项目代码的编写。首先需要说明几点:
- 一般master分支是项目的核心分支,只要进行代码的克隆,那么此分支一定会被保存下来;
- 开发者一般会建立一系列分支,比如,这里建立了一个brh分支进行代码的编写;
- 如果要进行调试可以建立一个bug分支,如果要增加新功能,则可以建立feature分支。
开始试验:
- 准备:创建一个新的分支brh
git checkout -b brh
- 在新的分支上建立一个新的文件
Dept.java
public class Dept{
public static void main(String args[]){
System.out.println("the new file Dept.java");
}
}
- 建此代码进行提交
git commit -a -m "the new file Dept.java"
- 将两个分支提交到服务器上
git push origin master
git push origin brh
- 为了模拟另一个开发者,建立另一个命令行窗口(2号),并且将代码复制下来,先要在
E:\github\LocalSource
中建立一个目录myproclone
,然后将项目克隆下来。
git clone https://github.com/yjaal/mypro.git
- 2号窗口中查看分支信息
git branch -a
注意:在查看之前我们要进入到仓库的目录。提示信息显示只是将master分支拷贝下来了,但是
brh分支并没有存在。
- 建立并切换到brh分支上(2号)
git checkout -b brh
- 将远程服务器上的brh分支的内容拷贝到本地brh分支上(2号)
git merge origin/brh
此时我们就将远程brh分支拷贝到了本地brh分支,我们可以看到
Dept.java
文件已经存在了。
- 现在2号开发者增加了一个文件
Admin.java
public class Admin{
public static void main(String args[]){
System.out.println("the new file Admin.java on 2 command window");
}
}
- 2号开发者将新的代码进行提交
git add .
git commit -m "the commit of 2 command window"
- 2号开发者进行推送
git push origin brh
此时代码就已经推送到了服务器上了。
- 1号开发者目录下还只是上一次提交的内容,此时需要取得最新的数据,这里有两种方式可以取得:
git fetch//方式一:表示只是取得最新的分支数据,但是不会发生合并操作
git pull//方式二:取得最新数据并进行合并
这里出现了一些错误,指出当前的分支和服务器上的分支没有什么关系,如果要想解决则需要让这两个分支产生关系,而提示信息中也给我们指出了相关的命令。
- 关系匹配
git branch --set-upstream-to=origin/brh brh
git pull
这里我们先进行了关系匹配,而后进行了同步,在仓库中我们会发现新的文件
Admin.java
。这样就实现了多个开发者之间的关联。
- 2号开发者修改
Admin.java
文件
public class Admin{
public static void main(String args[]){
System.out.println("the new file Admin.java on 2 command window");
System.out.println("modify on 2 command window");
}
}
- 2号开发者进行提交并推送
git commit -a -m "modify on 2 command window"
git push origin brh
- 1号开发者也进行了开发者也对
Admin.java
文件进行了修改:
public class Admin{
public static void main(String args[]){
System.out.println("the new file Admin.java on 2 command window");
System.out.println("modify on 1 command window");
}
}
- 1号开发者进行提交
git commit -a -m "modify on 1 command window"
- 1号开发者进行同步
git pull
此时我们会发现出现了问题。因为两位开发者修改了同一个代码,产生了冲突,1号开发者的
Admin.java
文件如下:
public class Admin{
public static void main(String args[]){
System.out.println("the new file Admin.java on 2 command window");
<<<<<<< HEAD
System.out.println("the new file Admin.java on 1 command window");
=======
System.out.println("modify on 2 command window");
>>>>>>> 2f31c9c1cdaf9dae08da064828ac51d3ee18febe
}
}
- 1号开发者手工解决冲突,保留所有代码
Admin.java
public class Admin{
public static void main(String args[]){
System.out.println("the new file Admin.java on 2 command window");
System.out.println("the new file Admin.java on 1 command window");
System.out.println("modify on 2 command window");
}
}
- 再次提交并推送
git commit -a -m "solve confilict"
git push origin brh
其实这只是简单的冲突解决,和我们之前的冲突解决基本上是一样的。
最后:这就是分支操作的基本操作步骤。