草庐IT

git的分支管理

学学学无止境 2023-11-26 原文

        这篇文章主要想讲一下git下的分支管理,因为分支管理是你在日常工作中必不可少的一项操作,觉得自己之前了解的不是很多,这次便决定利用一些细碎的时间好好去再次学习巩固一下,初衷也是为了加深自己的理解,便写下此文,当然啦,如果对正在学习git的小伙伴起到一些帮助,那我会很开心。如果文章中有不足的地方,也欢迎留言指正哈。

一、情景

        在我们日常开发中,假设接到一个新的功能开发的需求给了你两周的时间进行你开发,这一周你完成了65%的代码,这时候便会面临着这么一个问题:如果我这时候提交,但是代码还没有写完也就是不完整的代码,你提交的话便会便使得代码库变得不完整,从而影响了其他同事的开发。这时你又想,那要不等我开发完毕再提交代码好了?但是问题又来了,你能保证你本地的电脑不会挂机吗(狗头.jpg),同时也有着丢失进度的风险。

现在有了git的分支管理,就大大解决了这个问题。也就是你开发的时候,你创建属于你的分支,别人是看不到的。正是因为这种独立性,各个分支之间的开发工作互不打扰,所以你在你的分支上干活,想提交就提交,知道开发完毕之后,再一次合并到原来的分支上,这样便解决了既安全又不会影响别人的工作的问题。

二、创建分支与合并分支

我们知道,一个项目只允许有一个主分支,通常用来合并其他开发完毕的分支的。因此我们日常的开发工作都是在其他分支下完成的。

首先,我们创建dev分支,然后切换到dev分支:

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

git checkout命令加上-b参数表示创建并切换,相当于一下两条命令:

$ git branch dev
$ git checkout dev
Switched to branch 'dev'

然后使用git branch命令查看当前分支:

$ git branch
* dev
  master

git branch命令会列出所有的分支,当前分支面前会标有一个*号

然后,我们就可以在dev分支上正常提交,比如对readme.txt做个修改,然后提交:

$ git add readme.txt 
$ git commit -m "branch test"
[dev b17d20e] branch test
 1 file changed, 1 insertion(+)

现在,dev分支上的工作完成,我们就可以切换回master分支:

$ git checkout master
Switched to branch 'master'

切换回master分支,你可以查看一下readme.txt文件,你会发现刚才你再dev分支下修改的内容,这master分支下是看不到的!原因是,(在合并分支之前)各个分支时独立开来的,master分支同样是,刚才的提交是在dev分支上,所以master分支上是看不到的。因此,如果想看到dev分支下的修改,把dev分支的工作成果合并到master分支上即可:(注意:当前分支是master)

$ git merge dev
Updating d46f35e..b17d20e
Fast-forward
 readme.txt | 1 +
 1 file changed, 1 insertion(+)

 git merge name命令用于合并指定分支到当前分支,合并后,再查看readme.txt的内容,就可以看到,和dev分支的最新提交是完全一样的,合并完成后,就可以放心的删除dev分支了(当然你也可以选择不删除)

$ git branch -d dev
Deleted branch dev (was b17d20e).

删除后,查看branch,就剩下master分支了

$ git branch
* master

注意:

 git merge 命令用于合并指定分支到当前分支这里我要提一下,这里所说的将指定分支合并到当前分支,这个当前分支不一定是master分支,可以是任意分支,只要使用checkout切换的当前所在分支都可以。git merge name中的name就是你想要合并的分支。

在工作中的通常操作是,某个大的模块你创建为分支feature-user-v1.0.0,然后这个大的模块下又有很多小模块,你想细化的话,你可以进行如下操作

首先:

1.创建并切换到feature-user-v1.0.0分支下    

git checkout -b dev

2.这时候分支指针指向的就是feature-user-v1.0.0分支,然后在当前分支创建一个新的分支(小模块)

git checkout -b feature-userMoudle

3.在这个新分支下开发,开发完毕之后提交

4.然后切换回feature-user-v1.0.0分支,合并这个小模块的分支git merge feature-userMoudle

5.之后删除这个小模块的分支

三、bug分支

        在我们开发工作中,每个bug都可以通过一个新的临时分支来修复,修复后。合并分支,然后将临时分支删除。

情景:你接到一个修复代号为101的bug的任务时,你很自然的想创建一个分支'hotfix-issue-101',来修复它,但是,你当前正在dev上进行的工作还没有提交,并不是你不想提交,而是工作只是进行了一般,还没发提交,预计完成还需要1天的时间,但是这个bug必须在2个小时内修复完毕,怎么办?

幸好的是,git还提供了stash功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作:

$ git stash
Saved working directory and index state WIP on dev: f52c633 add merge

执行了git stash之后,再执行git sttaus命令查看,此时的工作区是干净的(除非有没有被git管理的文件),因此可以放心的创建分支来修复bug

接着,我们就要确定在哪个分支上修复bug,假定需要在master分支上修复,就从master上创建临时分支(其他分支同理)

1.切换到要修改bug的分支,然后在该分支下创建一个新的分支并切换到这个新创建的分支上

$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 6 commits.
  (use "git push" to publish your local commits)

$ git checkout -b issue-101
Switched to a new branch 'issue-101'

2.修改工作进行完毕之后,进行提交

$ git add .
$ git commit -m "fix bug 101"
[issue-101 4c805e2] fix bug 101
 1 file changed, 1 insertion(+), 1 deletion(-)

3.然后切换到mster分支,进行合并

$ git switch master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 6 commits.
  (use "git push" to publish your local commits)

$ git merge issue-101
Merge made by the 'recursive' strategy.
 readme.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

4.然后删除这个临时创建的分支(可选,一般都会删除)

5.现在接着回到dev分支干活

$ git switch dev
Switched to branch 'dev'

$ git status
On branch dev
nothing to commit, working tree clean

git status查看分支状态的时候,分支是干净的,那么刚才的工作现场存到哪里去了呢?使用git stash list命令look look:

$ git stash list
stash@{0}: WIP on dev: f52c633 add merge

工作现场还在,git把stash内容存在某个地方了,所以需要恢复一下,有两个方法:

  • 一是git stash apply恢复,但是恢复后,stash内容并不会删除,你需要用git stash drop来删除
  • 另一种方式就是使用git stash pop,恢复的同时把stash内容也删了
$ git stash pop
On branch dev
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	new file:   hello.py

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.txt

Dropped refs/stash@{0} (5d677e2ee266f39ea296182fb2354265b91b3b2a)

再用git stash list查看,就不会看到任何stash的内容了

$ git stash list

接着就可以在dev分支上正常进行继续的开发工作了!

注意:在master分支上修复bug后,我们要想一想,dev分支是早期从master分支分出来的,所以这个bug其实在当前dev分支上也存在

那么在dev分支上修复同样的bug?重复操作一次,提交不在行了?有没有更简单的方法?

有!

同样的bug,要在dev上修复,我们只需要把4c805e2 fix bug 101这个提交所做的修改“复制”到dev分支,

 注意,我们只想复制4c805e2 fix bug 101这个提交所做的修改,并不是把整个master分支merge过来。

为了方便操作,git提供了一个cherry-pick命令,让我们能复制一个特定的提交到当前分支:

$ git branch
* dev
  master
$ git cherry-pick 4c805e2
[master 1d4b803] fix bug 101
 1 file changed, 1 insertion(+), 1 deletion(-)

        git自动给dev分支做了一次提交,注意这次提交的commit是1d4b803,它不同于master的4c805e2,因为这两个commit只是改动相同,但确实是两个不同的commit,用git-cherry-pick,我们就不需要哟啊在dev分支上手动再把bug的过程重复一遍。

        我当时是这样想的:既然可以在master分支上修复bug后,在dev分支上可以“重放”这个修复过程,那么直接在dev分支上修复bug,然后在master分支上"重放”行不行?当然可以,不过你扔需要git stash命令保存现场,才能从dev分支切换到master分支上。

小结:

修复bug时,我们会通过创建新的bug分支进行修复,然后合并,最后删除;

当手头工作没有完成时,先把工作现场git stash一下,然后修复bug,修复后,在git stash pop,回到工作现场;

master分支上修复的bug,想要合并到当前dev分支,可以用git cherry-pick <commit>命令,把bug提交的修改“复制”到当前分支,避免重复劳动

四、Feature分支

        在软件开发中,总会有无穷无尽的新功能要不断添加进来。所以,没添加一个新功能,最好新建一个feature分支,在上面开发,完成后,合并,最后,删除该feature分支。

        比如,你现在接到一个新的任务,开发代号为new,于是开始下面的开发工作:

1.首先新建一个feature分支,并切换到该分支上

$ git switch -c feature-new
Switched to a new branch 'feature-vulcan'

2.然后在该分支上进行开发,开发完毕后,提交相应的文件

$ git add new.js

$ git status
On branch feature-vulcan
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	new file:   new.js

$ git commit -m "add feature new"
[feature-new 287773e] add feature new
 1 file changed, 2 insertions(+)
 create mode 100644 vulcan.py

3.然后切换回dev,准备合并

这里举例的是,你所有的开发分支都是dev下开发的,所以每创建一个feature分支也是在dev分支下的,然后经过测试等debug流程之后,再把dev分支合并到master分支上到生产环境上,当然啦,如果你们没有分dev分支,那你可以直接在master分支上进行创建。

$ git switch dev

4.然后进行合并,然后删除该feature分支(可删可不删)

五、多人合作

        当你从远程仓库克隆时,实际上git自动把本地的master分支和远程的master分支对应起来了,并且,远程仓库的默认名称是origin

要查看远程仓库的信息,用git remote:

$ git remote
origin

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

$ git remote -v
origin  git@github.com:michaelliao/learngit.git (fetch)
origin  git@github.com:michaelliao/learngit.git (push)

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

 1.推送分支

        推送分支(git push origin name),就是把该分支上的所有本地提交的推送到远程仓库,推送时,要指定本地分支,这样,git就会把该推送到远程库对应的远程分支上:

$ git push origin master

但是,并不是一定要把本地分支往远程推送,那么,哪些分支需要推送,哪些不需要呢?

  • master分支是主分支,因此要时刻与远程分支同步
  • dev分支是开发分支,团队所有的成员都需要在上面工作,所以也需要跟远程同步
  • bug分支只是用于本地修复bug,就没有必要推到远程了,除非老板要看看你每周休了几个bug
  • feature分支是否推送到远程,取决于你是否和你的小伙伴合作在上面开发

总之,在git中,分支完全可以在本地自己藏着玩,是否推送,根据你自身情况而定!

2.抓取分支 

        多人协作时,大家都会往master和dev分支上推送各自的修改,现在模拟你的一个小伙伴,可以在另外一台电脑或者再同台电脑的另一个目录下戈隆

$ git clone git@github.com:michaelliao/learngit.git
Cloning into 'learngit'...
remote: Counting objects: 40, done.
remote: Compressing objects: 100% (21/21), done.
remote: Total 40 (delta 14), reused 40 (delta 14), pack-reused 0
Receiving objects: 100% (40/40), done.
Resolving deltas: 100% (14/14), done.

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

 

$ git branch
* master

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

$ git checkout -b dev origin/dev

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

$ git add env.txt

$ git commit -m "add env"
[dev 7a5e5dd] add env
 1 file changed, 1 insertion(+)
 create mode 100644 env.txt

$ 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), 308 bytes | 308.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To github.com:michaelliao/learngit.git
   f52c633..7a5e5dd  dev -> dev

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

$ cat env.txt
env

$ git add env.txt

$ git commit -m "add new env"
[dev 7bd91f1] add new env
 1 file changed, 1 insertion(+)
 create mode 100644 env.txt

$ git push origin dev
To github.com:michaelliao/learngit.git
 ! [rejected]        dev -> dev (non-fast-forward)
error: failed to push some refs to 'git@github.com:michaelliao/learngit.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: '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
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.

    git pull <remote> <branch>

If you wish to set tracking information for this branch you can do so with:

    git branch --set-upstream-to=origin/<branch> dev

git pull也失败了,原因是没有指定本地dev分支与远程origin/dev分支的链接,距提示,设置dev和origin/dev的链接:

$ git branch --set-upstream-to=origin/dev dev
Branch 'dev' set up to track remote branch 'dev' from 'origin'.

然后再进行pull

$ git pull
Auto-merging env.txt
CONFLICT (add/add): Merge conflict in env.txt
Automatic merge failed; fix conflicts and then commit the result

这回git pull成功,但是合并冲突,需要手动解决,解决的方法就是和分支管理中的解决冲突方法一样,解决后,提交,再push:

$ git commit -m "fix env conflict"
[dev 57c53ab] fix env conflict

$ git push origin dev
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 621 bytes | 621.00 KiB/s, done.
Total 6 (delta 0), reused 0 (delta 0)
To github.com:michaelliao/learngit.git
   7a5e5dd..57c53ab  dev -> dev

3.小结

多人协作模式通常是这样的:

1.首先可以试图用git push origin <branch-name>推送自己的修改;

2.如果推送失败,需要先用git pull试图合并

3.如果有冲突,则解决冲突,并在本地提交;

4.没有冲突或者解决掉冲突后,在用git push origin <branch-name> 推送就能成功!

tip:如果git pull提示no tracking information,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to <branch-name> origin/<branch-name>

这就是多人协作的工作模式,一旦熟悉了,就非常简单。

有关git的分支管理的更多相关文章

  1. ruby - i18n Assets 管理/翻译 UI - 2

    我正在使用i18n从头开始​​构建一个多语言网络应用程序,虽然我自己可以处理一大堆yml文件,但我说的语言(非常)有限,最终我想寻求外部帮助帮助。我想知道这里是否有人在使用UI插件/gem(与django上的django-rosetta不同)来处理多个翻译器,其中一些翻译器不愿意或无法处理存储库中的100多个文件,处理语言数据。谢谢&问候,安德拉斯(如果您已经在ruby​​onrails-talk上遇到了这个问题,我们深表歉意) 最佳答案 有一个rails3branchofthetolkgem在github上。您可以通过在Gemfi

  2. ruby-on-rails - 获取 inf-ruby 以使用 ruby​​ 版本管理器 (rvm) - 2

    我安装了ruby​​版本管理器,并将RVM安装的ruby​​实现设置为默认值,这样'哪个ruby'显示'~/.rvm/ruby-1.8.6-p383/bin/ruby'但是当我在emacs中打开inf-ruby缓冲区时,它使用安装在/usr/bin中的ruby​​。有没有办法让emacs像shell一样尊重ruby​​的路径?谢谢! 最佳答案 我创建了一个emacs扩展来将rvm集成到emacs中。如果您有兴趣,可以在这里获取:http://github.com/senny/rvm.el

  3. ruby-on-rails - 事件管理员日期过滤器日期格式自定义 - 2

    是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s

  4. git使用常见问题(提交代码,合并冲突) - 2

    文章目录git常用命令(简介,详细参数往下看)Git提交代码步骤gitpullgitstatusgitaddgitcommitgitpushgit代码冲突合并问题方法一:放弃本地代码方法二:合并代码常用命令以及详细参数gitadd将文件添加到仓库:gitdiff比较文件异同gitlog查看历史记录gitreset代码回滚版本库相关操作远程仓库相关操作分支相关操作创建分支查看分支:gitbranch合并分支:gitmerge删除分支:gitbranch-ddev查看分支合并图:gitlog–graph–pretty=oneline–abbrev-commit撤消某次提交git用户名密码相关配置g

  5. ruby - (Ruby || Python) 窗口管理器 - 2

    我想用这两种语言中的任何一种(最好是ruby​​)制作一个窗口管理器。老实说,除了我需要加载某种X模块外,我不知道从哪里开始。因此,如果有人有线索,如果您能指出正确的方向,那就太好了。谢谢 最佳答案 XCB,X的下一代API使用XML格式定义X协议(protocol),并使用脚本生成特定语言绑定(bind)。它在概念上与SWIG类似,只是它描述的不是CAPI,而是X协议(protocol)。目前,C和Python存在绑定(bind)。理论上,Ruby端口只是编写一个从XML协议(protocol)定义语言到Ruby的翻译器的问题。生

  6. ruby-on-rails - 事件管理员和自定义方法 - 2

    这是我在ActiveAdmin中的自定义页面ActiveAdmin.register_page"Settings"doaction_itemdolink_to('Importprojects','settings/importprojects')endcontentdopara"Text"endcontrollerdodefimportprojectssystem"rakedataspider:import_projects_ninja"para"OK"endendend我想做的是,当我单击“导入项目”按钮时,我想在Controller中执行rake任务。但是我无法访问该方法。可能是什

  7. ruby - Dropbox 类似 git 的服务——没有 rsync 和 inotify - 2

    关于如何使用git设置类似Dropbox的服务,您有什么建议吗?您认为git是解决此问题的合适工具吗?我在考虑使用git+rush解决方案,你觉得怎么样? 最佳答案 检查这个开源项目:https://github.com/hbons/SparkleShare来自项目的自述文件:Howdoesitwork?SparkleSharecreatesaspecialfolderonyourcomputer.Youcanaddremotelyhostedfolders(or"projects")tothisfolder.Theseprojec

  8. ruby - 混帐 & ruby : How can I unset the GIT_DIR variable from inside a ruby script? - 2

    我编写了一个非常简单的“部署”脚本,作为我的裸git存储库中的post-updateHook运行。变量如下livedomain=~/mydomain.comstagingdomain=~/stage.mydomain.comgitrepolocation=~/git.mydomain.com/thisrepo.git(bare)core=~/git.mydomain.com/thisrepo.gitcore==addedremoteintoeachlive&stagegitslive和stage都初始化了gitrepos(非裸),我已经将我的裸仓库作为远程添加到它们中的每一个(名为co

  9. ruby - 让 bundler 使用 http : instead of git:? - 2

    我正在安装gitlabhq,并且在Gemfile中有对某些资源的“git://...”的引用。但是,我在公司防火墙后面,所以我必须使用http://。我可以手动编辑Gemfile,但我想知道是否有另一种方法告诉bundler使用http://作为git存储库? 最佳答案 您可以通过运行gitconfig--globalurl."https://".insteadOfgit://或通过将以下内容添加到~/.gitconfig:[url"https://"]insteadOf=git://

  10. ruby-on-rails - (Ruby,Rails) 基于角色的身份验证和用户管理...? - 2

    我正在寻找用于Rails的优质管理插件。似乎大多数现有的插件/gem(例如“restful_authentication”、“acts_as_authenticated”)都围绕着self注册等展开。但是,我正在寻找一种功能齐全的基于管理/管理角色的解决方案——但不是简单地附加到另一个非基于角色的解决方案。如果我找不到,我想我会自己动手......只是不想重新发明轮子。 最佳答案 RyanBates最近做了两个关于授权的railscast(注意身份验证和授权之间的区别;身份验证检查用户是否如她所说的那样,授权检查用户是否有权访问资源

随机推荐