当前位置 : 主页 > 编程语言 > java >

git分支和冲突管理

来源:互联网 收集:自由互联 发布时间:2023-03-22
前言 分支就是科幻电影里面的平行宇宙,当你正在电脑前努力学习Git的时候,另一个你正在另一个平行宇宙里努力学习SVN。如果两个平行宇宙互不干扰,那对现在的你也没啥影响。不过

前言

分支就是科幻电影里面的平行宇宙,当你正在电脑前努力学习Git的时候,另一个你正在另一个平行宇宙里努力学习SVN。如果两个平行宇宙互不干扰,那对现在的你也没啥影响。不过,在某个时间点,两个平行宇宙合并了,结果,你既学会了Git又学会了SVN!

分支简介

为了真正理解Git处理分支的方式,我们需要回顾一下Git是如何保存数据的。 Git 保存的不是文件的变化或者差异,而是一系列不同时刻的文件快照。在进行提交操作时,Git会保存一个提交对象(commit object)。知道了Git保存数据的方式,我们可以很自然的想到——该提交对象会包含一个指向暂存内容快照的指针。 但不仅仅是这样,该提交对象还包含了作者的姓名和邮箱、提交时输入的信息以及指向它的父对象的指针。首次提交产生的提交对象没有父对象,普通提交操作产生的提交对象有一个父对象,而由多个分支合并产生的提交对象有多个父对象。 Git的分支,其实本质上仅仅是指向提交对象的可变指针。Git的默认分支名字是master。 在多次提交操作之后,你其实已经有一个指向最后那个提交对象的master分支。 它会在每次的提交操作中自动向前移动。 Git的 “master” 分支并不是一个特殊分支。它就跟其它分支完全没有区别。 之所以几乎每一个仓库> 都有 master 分支,是因为git init命令默认创建它,并且大多数人都懒得去改动它。 分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。 现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。

分支创建

Git是怎么创建新分支的呢? 很简单,它只是为你创建了一个可以移动的新的指针。 比如,创建一个testing分支, 你需要使用==git branch命令==:

git branch testing

image.png 这会在当前所在的提交对象上创建一个指针,两个指向相同提交历史的分支。 image.png 那么,Git又是怎么知道当前在哪一个分支上呢? 也很简单,它有一个名为HEAD的特殊指针。 请注意它和许多其它版本控制系统(如 Subversion 或 CVS)里的HEAD概念完全不同。 在Git中,它是一个指针,指向当前所在的本地分支(译注:将HEAD想象为当前分支的别名)。 在本例中,你仍然在master分支上。 因为git branch命令仅仅 创建 一个新分支,并不会自动切换到新分支中去。 你可以简单地使用==git log命令==查看各个分支当前所指的对象。 提供这一功能的参数是--decorate。

git log --oneline --decorate

image.png 正如你所见,当前 “master” 和 “testing” 分支均指向校验和以49f5f77开头的提交对象。

分支切换

要切换到一个已存在的分支,你需要使用==git checkout==命令。 我们现在切换到新创建的testing分支去:

git checkout testing

这样HEAD就指向testing分支了。 image.png

上面的创建分支和切换分支命令可以合起来用下面这个命令来替代。

git checkout -b testing

那么,这样的实现方式会给我们带来什么好处呢? 现在不妨再提交一次:

vim README.md git commit -a -m 'git测试:testing分支'

image.png HEAD 分支随着提交操作自动向前移动. image.png 如图所示,你的testing分支向前移动了,但是master分支却没有,它仍然指向运行git checkout时所指的对象。 这就有意思了,现在我们切换回master分支看看:

git checkout master

image.png 这条命令做了两件事。 一是使 HEAD 指回master分支,二是将工作目录恢复成master分支所指向的快照内容。 也就是说,你现在做修改的话,项目将始于一个较旧的版本。 本质上来讲,这就是忽略testing分支所做的修改,以便于向另一个方向进行开发。 可以使用==git branch命令==查看当前分支,注意前面带*的表示当前分支 image.png

注意: 分支切换会改变你工作目录中的文件。在切换分支时,一定要注意你工作目录里的文件会被改变。 如果是切换到一个较旧的分支,你的工作目录会恢复到该分支最后一次提交时的样子。 如果Git不能干净利落地完成这个任务,它将禁止切换分支。

合并分支(快速合并)

假如我们在testing上的工作完成了,就可以把testing合并到master上。Git怎么合并呢?最简单的方法,就是直接把master指向testing的当前提交,就完成了合并。首先切换到master分支,使用==git merge命令==

git merge testing Updating 64ba18a..760118b Fast-forward hello.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 hello.txt

git merge命令用于合并指定分支到当前分支。合并后,再查看内容,就可以看到,和testing分支的最新提交是完全一样的。

删除分支

合并完分支后,甚至可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后,我们就剩下了一条master分支,这里需要使用git branch -d命令来删除分支 image.png

git branch -d testing Deleted branch testing (was 760118b).

分支合并冲突—重点

准备新的dev分支,继续我们的新分支开发:

git checkout -b dev Switched to a new branch 'dev'

修改README.md内容,添加一样内容”dev分支git测试”,在dev分支上提交:

git commit -am "git测试:dev分支" [dev 6a6a08e] one commit 1 file changed, 1 insertion(+)

切换到master分支:

git checkout master Switched to branch 'master' Your branch is up-to-date with 'origin/master'.

在master分支上把README.md文件的最后改为”master分支git测试”,然后提交:

git commit -am "git测试:master分支" [master 75d6f25] two commit 1 file changed, 1 insertion(+)

现在,master分支和dev分支各自都分别有新的提交,变成了这样: image.png 这种情况下,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突,我们试试看:

git merge dev

image.png

果然冲突了!Git告诉我们, README.md文件存在冲突,必须手动解决冲突后再提交。==git status==也可以告诉我们冲突的文件:

git status

image.png 直接查看README.md的内容: image.png

再提交:

git cmmit -am 'merge commit' [master 9a4d00b] merge commit

现在,master分支和dev分支变成了下图所示: image.png 用带参数的git log也可以看到分支的合并情况:

git log --graph --pretty=oneline --abbrev-commit

最后,删除dev分支:

git branch -d dev Deleted branch dev (was 6a6a08e).

分支管理策略

实际公司开发的时候一般3个分支就可以了:

  • mster 主分支用来发布
  • dev 日常开发用的分支
  • bug 修改bug用的分支 首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活; 干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本,你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了;bug分支用来处理日常bug,搞定后合到dev分支即可; 假设远程公共仓库,有一个master和一个dev分支,进行多人协作开发时候(每个人的公钥必须加入到远程账号下,否则无法push), 每个人都应该clone一份到本地。 但是clone的只是master,如果远程的master和dev一样,没关系;如果不一致,则需要clone出dev分支git checkout -b dev origin/dev之后每个人在本地的dev分支上独自开发(最好不要在mast上开发), 开发完成之后push到远程dev,git push origin dev。 之后审核人再确定是否合并dev到master。
  • 团队多人开发协作

    当你从远程仓库克隆时,实际上Git自动把本地的master分支和远程的master分支对应起来了,并且,远程仓库的默认名称是origin。 要查看远程库的信息,用git remote:

    git remote origin

    或者,用==git remote -v==显示更详细的信息:

    git remote -v origin git@git.coding.net:tengj/gitLearn.git (fetch) origin git@git.coding.net:tengj/gitLearn.git (push)

    上面显示了可以抓取和推送的origin的地址。如果没有推送权限,就看不到push的地址。

    推送分支

    推送分支,就是把该分支上的所有本地提交推送到远程库。推送时,要指定本地分支,这样,Git就会把该分支推送到远程库对应的远程分支上:

    git push origin master

    如果要推送其他分支,比如dev,就改成:

    git push origin dev

    抓取分支

    多人协作时,大家都会往master和dev分支上推送各自的修改。 现在,模拟一个你的小伙伴,可以在另一台电脑(注意要把SSH Key添加到GitHub)或者同一台电脑的另一个目录下克隆:

    git clone git@git.coding.net:tengj/gitStudy.git Cloning into 'gitStudy'... remote: Counting objects: 3, done. remote: Total 3 (delta 0), reused 0 (delta 0) Receiving objects: 100% (3/3), done. Checking connectivity... done.

    当你的小伙伴从远程库clone时,默认情况下,你的小伙伴只能看到本地的master分支。不信可以用git branch命令看看:

    git branch

    • master

    现在,你的小伙伴要在dev分支上开发,就必须创建远程origin的dev分支到本地,于是他用这个命令创建本地dev分支(分支dev要先创建)。

    git checkout -b dev

    创建dev分之后,先同步远程服务器上的数据到本地

    git fetch origin From git.coding.net:tengj/gitStudy 段* [new branch] dev -> origin/dev

    现在,他就可以在dev上继续修改,然后,时不时地把dev分支push到远程:

    git commit -am 'test' [dev c120ad6] test 1 file changed, 1 insertion(+) $ git push origin dev Counting objects: 3, done. Delta compression using up to 4 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 262 bytes | 0 bytes/s, done. Total 3 (delta 0), reused 0 (delta 0) To git@git.coding.net:tengj/gitStudy.git 65c05aa..c120ad6 dev -> dev

    你的小伙伴已经向origin/dev分支推送了他的提交,而碰巧你也对同样的文件作了修改,并试图推送:

    git push origin dev To git@git.coding.net:tengj/gitStudy.git ! [rejected] dev -> dev (fetch first) error: failed to push some refs to 'git@git.coding.net:tengj/gitStudy.git' hint: Updates were rejected because the remote contains work that you do hint: not have locally. This is usually caused by another repository pushing hint: to the same ref. You may want to first integrate the remote changes hint: (e.g., 'git pull ...') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details.

    推送失败,因为你的小伙伴的最新提交和你试图推送的提交有冲突,解决办法也很简单,Git已经提示我们,先用git pull把最新的提交从origin/dev抓下来,然后,在本地合并,解决冲突,再推送:

    git pull origin dev remote: Counting objects: 3, done. remote: Compressing objects: 100% (2/2), done. remote: Total 3 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (3/3), done.

    因此,多人协作的工作模式通常是这样: 1.首先,可以试图用git push origin branch-name推送自己的修改; 2.如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并; 3.如果合并有冲突,则解决冲突,并在本地提交; 4.没有冲突或者解决掉冲突后,再用git push origin branch-name推送就能成功!

    如果git pull提示“no tracking information”,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to branch-name origin/branch-name。 这就是多人协作的工作模式,一旦熟悉了,就非常简单。

    总结

    到此,Git分支管理就学完了,整理一下所学的命令,大体如下: git branch 查看当前分支 git branch -v 查看每一个分支的最后一次提交 git branch -a 查看本地和远程分支的情况 git branch --merged 查看已经与当前分支合并的分支 git branch --no-merged 查看已经与当前分支未合并的分支 git branch -r 查看远程分支 git branch dev 创建分支 dev git checkout dev 切换到分支dev git checkout -b dev 创建并切换分支dev git merge dev 名称为dev的分支与当前分支合并 git branch -d dev 删除分支dev

    网友评论