当前位置:   article > 正文

Git速成_起先我们在 master 工作到 c1,然后开始一个新分支 iss91 尝试修复 91 号缺陷,提交

起先我们在 master 工作到 c1,然后开始一个新分支 iss91 尝试修复 91 号缺陷,提交

什么是Git

 Git是Linux发明者Linus开发的一款新时代的版本控制系统,版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统,对于软件开发领域来说版本控制是最重要的一环,而Git毫无疑问是当下最流行、最好用的版本控制系统
 Git是一个分布式的版本控制系统。根本上来讲 Git 是一套内容寻址 (content-addressable) 文件系统。这种说法的意思是,从内部来看,Git 是简单的 key-value 数据存储。它允许插入任意类型的内容,并会返回一个键值,通过该键值可以在任何时候再取出该内容。Git中不存在主库这样的概念,每一份复制出的库都可以独立使用,任何俩个库之间的不一致之处都可以进行合并。

Git与其他版本控制系统的主要差异

记录文件的差异
其他版本控制系统
关心文件内容的具体差异,每次记录有哪些文件更新,以及都更新了哪些行的什么内容
image

git:把幻化的文件作快照,记录在一个微型文件系统。每次提交更新时,它会纵览一遍所有文件的指纹信息并对文件作一快照,然后保存一个指向这个快照的索引。如果文件没有变化,git不会再次保存快照,而是对上次保存的快照作一链接。
image

操作的差异

其他系统:

 需要联网,比如subversion和cvs,可以编译文件,但是无法提交更新。

Git
 绝大多数操作只需要访问本地文件和资源,不用联网
举个例子,如果要浏览项目的历史更新摘要,Git 不用跑到外面的服务器上去取数据回来,而直接从本地数据库读取后展示给你看。所以任何时候你都可以马上翻阅,无需等待。如果想要看当前版本的文件和一个月 前的版本之间有何差异,Git 会取出一个月前的快照和当前文件作一次差异运算,而不用请求远程服务器来做这件事。

Git文件的三种状态

  • 已修改(modified)
    修改了某个文件,但是还没有提交保存
  • 已暂存(satged)
    已修改的文件放在下次提交时要保存的清单
  • 已提交(committed)
    表示该文件已经被安全地保存到本地数据库中

所以git管理项目时,文件流转的三个工作区域:git工作目录,暂存区域,以及本地仓库。
image

 每个项目都有一个git目录(如.git),它是git用来保存元数据和对象数据库的地方。该目录非常重要,每次克隆镜像仓库的时候,实际拷贝的就是这个目录里面的数据。

  • Working Directory(工作目录:就是在自己电脑上看到的目录)
     从项目中取出某个版本的所有文件和目录,用以开始后续工作的叫做工作目录。这些文件实际上都是从 Git 目录中的压缩对象数据库中提取出来的,接下来就可以在工作目录中对这些文件进行编辑.
  • 暂存区
     所谓的暂存区域只不过是个简单的文件,一般都放在 Git 目录中。有时候人们会把这个文件叫做索引文件,不过标准说法还是叫暂存区域。
  • 本地仓库

基本的Git工作流程:

  • 在工作目录中修改某些文件。(写代码)
  • 对修改后的文件进行快照,然后保存到暂存区域.==(git add)==
  • 提交更新,将保存在暂存区域的文件快照永久转储到 Git 目录中 ==(git commit -m)==
     所以,我们可以从文件所处的位置来判断状态:如果是 Git 目录中保存着的特定版本文件,就属于已提交状态;如果作了修改并已放入暂存区域,就属于已暂存状态;如果自上次取出后,作了修改但还没有放到暂存区域,就是已修改状态。

git的文件目录结构(版本库)

  • hooks:这个目录存放一些shell脚本,可以设置特定的git命令后触发相应的脚本;在搭建gitweb系统或其他git托管系统会经常用到hook script
  • info:包含仓库的一些信息
  • logs:保存所有更新的引用记录
  • objects:所有的Git对象都会存放在这个目录中,对象的SHA1哈希值的前两位是文件夹名称,后38位作为对象文件名
  • refs:这个目录一般包括三个子文件夹,heads、remotes和tags,heads中的文件标识了项目中的各个分支指向的当前commit
  • COMMIT_EDITMSG:保存最新的commit message,Git系统不会用到这个文件,只是给用户一个参考
  • config:这个是GIt仓库的配置文件
  • description:仓库的描述信息,主要给gitweb等git托管系统使用
  • index:这个文件就是我们前面提到的暂存区(stage),是一个二进制文件
  • HEAD:这个文件包含了一个档期分支(branch)的引用,通过这个文件Git可以得到下一次commit的parent
  • ORIG_HEAD:HEAD指针的前一个状态

image
当我们git初始化仓库之后,git会为我们自动创建唯一一个master分支,所以刚开始commit
默认王master分支上提交

GIT基本操作

设置用户名和邮箱地址

$ git config --global user.name "username"
$ git config --global user.email "email"

该设置在github仓库主页下显示谁提交了该文件
  • 1
  • 2
  • 3
  • 4

初始化Git仓库

  • 创建文件夹

  • 在文件内初始化git(创建git仓库)

git init #初始化仓库(生成.git目录)
# git status
  • 1
  • 2

对Git仓库的CURD

  • 将文件添加到==暂存区==
git add demo.txt #添加到暂存区
# git status
  • 1
  • 2
  • 将文件从暂存区提交到==仓库==
git commit -m '第一次提交'
# git status
  • 1
  • 2
  • ==修改==(更新)仓库文件(Linux命令)
vim demo.txt #修改文件
# git status
git add demo.txt #添加到暂存区这时会提醒你文件被替换了
# git status
git commit -m'提交修改'
# git status
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 删除仓库文件
# 1.从工作区删除文件
    rm -rf demo.txt
# 2.从Git中删除文件  
    git rm -rf demo.txt
# 3.提交
    git commit -m '删除描述'

如果你删错了文件因为版本库中还存在(最起码你add了的话)你可以很轻松的回复到最新版本

$ git checkout --demo.txt


命令git rm⽤用于删除一个文件。如果一个⽂件已经被提交到版本库,那么你永远不用担⼼误
删,但是要⼩心,你只能恢复文件到最新版本,你会丢失最近⼀次提交后你修改的内容。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

==删除文件详解==

git rm 文件名
    - 只是删除工作目录跟暂存区域的文件,也就是取消跟踪,
      在下次提交时不纳入版本管理(快照里的不会删除)
      //这样快照中就没有了      
      git reset --soft HEAD~
    - 当工作目录和暂存区的同一个文件存在不同内容市执行git rm -rf
      文件名可以同时删除两个
    - 如果只删除暂存区的文件(保留工作目录)可以执行
      git rm -- cached 文件名  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 修改文件名
git mv 旧文件名 新文件名
  • 1

版本前进跟后退(git reset)

原理是基于HEAD指针的移动

1、从暂存区回退到工作区
回退文件,将文件从暂存区回退到工作区
git reset HEAD filename  
//也可以使用 
git reset filename
  • 1
  • 2
  • 3
  • 4
2、版本回退(3种方式)
git reset HEAD^
//一个^表示一个版本,可以多个,另外也可以使用 git reset HEAD~n这种形式
  • 1
  • 2

image

commit后面的那一大串字符串是版本号(Hash计算出来的),因为Git是一个分布式的版本控制系统,如果版本是用123这种的就跟svn一样会有版本冲突了(后面会研究多人在同一个版本库里工作)

==reset的命令选项(重点)==

git reset  HEAD~
// 移动HEAD的指向,将其指向上一个快照
// 将HEAD移动后指向的快照回滚到暂存区(重要以前不知道这个)

git reset --soft HEAD~
// 移动HEAD的指向,将其指向上一个快照
// 并没有修改暂存区中的内容
// 相当于撤销一次错误的提交 回滚到提交之前

git reset --hard HEAD~
// 一定HEAD的指向,将其指向上一个快照
// 将HEAD移动后指向的快照回滚到暂存区
// 将暂存区中的文件还原到工作目录
// 此操作有风险


git reset 所有的命令 都符合上述的规则
切记切记!!!!!!!!!!!!!!!!!!!
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

==所以版本回退还有:==

git reset --hard ca1935
//版本号没必要写全
  • 1
  • 2

==总结==

//HEAD代表当前版本
//切换版本
git reset --hard commit_id

//回退前可以使用 git log 查看历史,以便确定回退到哪个版本
git log

//重返未来,用 git reflog 查看命令历史 确定回到未来的哪个版本
git reflog
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

image

git diff (比较信息)

比较暂存区域与工作目录
git diff
  • 1
比较2个历史快照
git diff id1 id2
// 友情提醒 id不用写全 一般输5个就行
  • 1
  • 2
比较当前工作目录和Git仓库中的快照
git diff 快照ID
  • 1
比较暂存区域和Git仓库快照
git diff -- cached [快照ID]
  • 1

Git管理远程仓库

 Git是分布式版本控制系统,同⼀一个Git仓库,可以分布到不同的机器上。怎么分布呢?
好在这个世界上有个叫GitHub的神奇的网站,从名字就可以看出,这个网站就是提
供Git仓库托管服务的,所以,只要注册⼀一个GitHub账号,就可以免费获得Git远程仓库

SSH的配置

 由于你的本地Git仓库和GitHub仓库之间的传输是通过SSH加密的,所以,需要⼀点设置:

$ ssh-keygen -t rsa -C "youremail@example.com"

  一路回车使用默认就行,由于这个key也不是军事目的无需设置密码

如果一切顺利的话,可以在用户主目录里找到.ssh目录,里面有id_rsa和id_rsa.pub两个文
件,这两个就是SSH Key的秘钥对,id_rsa是私钥,不能泄露出去,id_rsa.pub是公钥,可
以放心地告诉任何人。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

 登陆GitHub,打开“Account settings”,“SSH Keys”页面:然后,点“Add SSH Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub⽂文件的内容

 为什么GitHub需要SSH Key呢?因为GitHub需要识别出你推送的提交确实是你推送的,而
不是别⼈人冒充的,而Git支持SSH协议,所以,GitHub只要知道了你的公钥,就可以确认只
有你自己才能推送。

添加远程仓库

 现在的情景是,你已经在本地创建了一个Git仓库后,又想在GitHub创建⼀一个Git仓库,并且让这两个仓库进行远程同步,这样,GitHub上的仓库既可以作为备份,又可以让其他人
通过该仓库来协作,真是一举多得

 在github上创建一个仓库后GitHub告诉我们,可以从这个仓库克隆
出新的仓库,也可以把一个已有的本地仓库与之关联,然后,把本地仓库的内容推送到
GitHub仓库。

关联远程仓库:

  • 正规的
git remote add origin HTTPS/SSH
// origin是远程仓库的名字,这是git的默认叫法,也可以叫别的但是
//origin一看就知道是远程仓库
  • 1
  • 2
  • 3
  • 不知道从哪看的
$ git remote add origin HTTPS/SSH

$ git remote add [指定仓库名] [HTTPS/SSH]
   仓库名一般是 origin

可以用字符创pb织带对应的仓库地址 例:

$ git remote add pb SSH
$ git remote -v # 查看
这样我们就可以用 pb抓取想应 库中的信息了
$ git fetch pb #抓取本地仓库没有的信息
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
$ git fetch [remote-name]
  • 1

推送数据到远程仓库

注意:如果不克隆直接往github上传的话github上的仓库必须是空
的才行那样才能push成功

<!--把本地master分支的最新修改版推送至github-->
$ git push -u origin master

<!--
    第一次推送master分支的所有内容由于远程库是空的我们第一次
    推送master分支时,加上-u参数Git不但会把本地的master分支内容
    推送到远程新的master分支还会把本地的master分支跟远程的分支
    关联起来,在以后的推送或者拉去时就可以简化命令
-->

<!--以后你就可以这样写-->
git push origin master

$ git push [remote-name] [branch-name]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

从远程库克隆

 上次我们讲了先有本地库,后有远程库的时候,如何关联远程库。
现在,假设我们从零开发,那么最好的⽅方式是先创建远程库,然后,从远程库克隆。

 注意:在github上创建仓库的时候最好初始化一个README.md文件

远程仓库已经准备好了,接下来就是克隆一个本地库

git clone SSH/HTTPS
  • 1

其他

查看当前远程仓库
$ git remote #显示简短的远程库的名称默认是 origin

$ git remote -v #显示对应的克隆地址
  • 1
  • 2
  • 3
查看远程仓库信息
$ git remote show [remote-name]

比如想查看克隆的 origin仓库:

$ git remote show origin
  • 1
  • 2
  • 3
  • 4
  • 5
git更改远程仓库
修改远程仓库地址(方式一)
git remote -v //查看远程仓库的链接
//修改url的链接
git remote set-url origin URL //URL为新地址
  • 1
  • 2
  • 3
先删除远程仓库地址在添加(方式二)
git remote rm origin //删除现有的远程连接
git remote add origin url //添加新连接
  • 1
  • 2

分支管理

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

image
 分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你
写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活
了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨⼤大风险。
现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来
的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性
合并到原来的分支上,这样,既安全,又不影响别人工作。
其他版本控制系统如SVN等都有分支管理,但是用过之后你会发现,这些版本控制系统创建
和切换分支比蜗牛还慢,简直让人无法忍受,结果分支功能成了摆设,大家都不去用。
但Git的分支是与众不同的,无论创建、切换和删除分支,Git在1秒钟之内就能完成!无论
你的版本库是1个文件还是1万个文件

创建与合并分支(理论)

 在版本回退里,你已经知道,每次提交,Git都把它们串成一条时间线,这条时间线就是一
个分⽀。截止到目前,只有一条时间线,在Git里,这个分支叫主分支,即master分支。
HEAD严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指
向的就是当前分支。
 一开始的时候,master分支是一条线,Git用master指向最新的提交,再用HEAD指向
master,就能确定当前分支,以及当前分支的提交点:

image

 每次提交,master分支都会向前移动一步,这样,随着你不断提交,master分支的线也越
来越长。
当我们创建新的分支,例如dev时,Git新建了一个指针叫dev,指向master相同的提交,
再把HEAD指向dev,就表示当前分支在dev上:
image

 你看,Git创建一个分支很快,因为除了增加一个dev指针,改改HEAD的指向,工作区的文
件都没有任何变化!
不过,从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev
指针往前移动一步,而master指针不变:

image
 假如我们在dev上的工作完成了,就可以把dev合并到master上。Git怎么合并呢?最简单
的方法,就是直接把master指向dev的当前提交,就完成了合并
image
 所以Git合并分支也很快!就改改指针,工作区内容也不变!
合并完分支后,甚至可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后,我
们就剩下了一条master分支
image

创建合并分支实战

创建分支
git branch 分支名
  • 1
切换分支
git checkout 分支名
  • 1
git checkout -b 分支名
//这个命令加上-b参数表示创建并切换相当于以上2个命令
  • 1
  • 2
查看分支
git branch 分支名
//会列出所有的分支当前分支前面会有个*号

<!--小甲鱼的-->
git log --decorate --oneline

// 图形化
git log --decorate --oneline --graph --all
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
合并分支
git merge 分支名
  • 1
实战
1.创建dev分支,然后切换到dev分支:

$ git checkout -b dev

   git checkout -b +参数: 表示创建并切换相当于一下两种命令:
       $ git branch dev 
       $ git checkout dev

2.查看当前分支:
$ git branch #列出所有分支,并在当前分支前面加上一个 *号
3.然后我们就能在dev分支上正常提交,比如crud
$ git add a.txt
$ git commit -m'提交'


3.现在dev分支完成工作,我们就可以切换回master分支了

$ git checkout master
但是切换回来之后,刚才添加的内容不见了,因为那个是提交在了dev分支上,而master分支的提交点并没有
改变
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

image

现在我们把dev分支的工作成果合并到master分支上

$ git merge dev #用于合并指定分支到当前分支

这种情况下命令行会有 Fast-forward 的信息表示这是"快进模式"即直接把master指向dev,所以非常快速但是并不是所有的合并方式都是这样的,下面会介绍

合并完成之后,就可以放心的删除dev分支了

$ git branch -d dev

因为创建合并和删除分支的速度非常快,所以git鼓励你使用分支完成某个任务,合并后在
删除分支这样更加安全
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
小结:
 Git鼓励大量使用分支
 查看分支:
   $ git branch
 创建分支:
   $ git branch name
 切换分支:
   $ git checkout name
 创建+切换分支:
   $ git checkout -b name
 合并某分支到当前分支:
   $ git merge name
  删除分支:
   $ git branch -d name
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

解决冲突

 有时候合并操作并不会如此顺利。如果在不同的分支中都修改了同一个文件的同一部分,Git 就无法干净地把两者合到一起(译注:逻辑上说,这种问题只能由人来裁决。)

当两个分支都各自分别有新的提交变成下图所示:
image

$ git merge iss53
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
  • 1
  • 2
  • 3
  • 4

 Git 作了合并,但没有提交,它会停下来等你解决冲突。要看看哪些文件在合并时发生冲突,可以用 git status 查阅:

$ git status
On branch master
You have unmerged paths.
  (fix conflicts and run "git commit")

Unmerged paths:
  (use "git add <file>..." to mark resolution)

        both modified:      index.html

no changes added to commit (use "git add" and/or "git commit -a")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

 任何包含未解决冲突的文件都会以未合并(unmerged)的状态列出。Git 会在有冲突的文件里加入标准的冲突解决标记,可以通过它们来手工定位并解决这些冲突。可以看到此文件包含类似下面这样的部分:

<<<<<<< HEAD
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
  please contact us at support@github.com
</div>
>>>>>>> iss53
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

 可以看到 ======= 隔开的上半部分,是 HEAD(即 master 分支,在运行 merge 命令时所切换到的分支)中的内容,下半部分是在 iss53 分支中的内容。解决冲突的办法无非是二者选其一或者由你亲自整合到一起。比如你可以通过把这段内容替换为下面这样来解决:

<div id="footer">
please contact us at email.support@github.com
</div>
  • 1
  • 2
  • 3

 这个解决方案各采纳了两个分支中的一部分内容,而且我还删除了 <<<<<<<,======= 和 >>>>>>> 这些行。在解决了所有文件里的所有冲突后,运行 git add 将把它们标记为已解决状态(译注:实际上就是来一次快照保存到暂存区域。)。因为一旦暂存,就表示冲突已经解决。如果你想用一个有图形界面的工具来解决这些问题,不妨运行 git mergetool,它会调用一个可视化的合并工具并引导你解决所有冲突:

//手动解决完冲突之后
git add 文件
git commit -m""

//删除分支
  • 1
  • 2
  • 3
  • 4
  • 5
$ git mergetool

This message is displayed because 'merge.tool' is not configured.
See 'git mergetool --tool-help' or 'git help config' for more details.
'git mergetool' will now attempt to use one of the following tools:
opendiff kdiff3 tkdiff xxdiff meld tortoisemerge gvimdiff diffuse diffmerge ecmerge p4merge araxis bc3 codecompare vimdiff emerge
Merging:
index.html

Normal merge conflict for 'index.html':
  {local}: modified file
  {remote}: modified file
Hit return to start merge resolution tool (opendiff):
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

 退出合并工具以后,Git 会询问你合并是否成功。如果回答是,它会为你把相关文件暂存起来,以表明状态为已解决。

再运行一次 git status 来确认所有冲突都已解决:

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

        modified:   index.html
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

 如果觉得满意了,并且确认所有冲突都已解决,也就是进入了暂存区,就可以用 git commit 来完成这次合并提交。提交的记录差不多是这样:

Merge branch 'iss53'

Conflicts:
  index.html
#
# It looks like you may be committing a merge.
# If this is not correct, please remove the file
#       .git/MERGE_HEAD
# and try again.
#
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

 如果想给将来看这次合并的人一些方便,可以修改该信息,提供更多合并细节。比如你都作了哪些改动,以及这么做的原因。有时候裁决冲突的理由并不直接或明显,有必要略加注解。

小结:
    当Git⽆无法⾃自动合并分⽀支时,就必须⾸首先解决冲突。解决冲突后,再提交,合并完成
    ⽤用git log --graph命令可以看到分⽀支合并图
  • 1
  • 2
  • 3
  • 4
分支管理(策略)

到目前为止,你已经学会了如何创建、合并和删除分支。除此之外,我们还需要学习如何管理分支,在日后的常规工作中会经常用到下面介绍的管理命令。

git branch 命令不仅仅能创建和删除分支,如果不加任何参数,它会给出当前所有分支的清单:

$ git branch
  iss53
* master
  testing
  • 1
  • 2
  • 3
  • 4

注意看 master 分支前的 * 字符:它表示当前所在的分支。也就是说,如果现在提交更新,master 分支将随着开发进度前移。若要查看各个分支最后一个提交对象的信息,运行

git branch -v
  • 1
$ git branch -v
  iss53   93b412c fix javascript issue
* master  7a98805 Merge branch 'iss53'
  testing 782fd34 add scott to the author list in the readmes
  • 1
  • 2
  • 3
  • 4

要从该清单中筛选出你已经(或尚未)与当前分支合并的分支,可以用 ==–merged== 和 ==–no-merged== 选项(Git 1.5.6 以上版本)。比如用 ==git branch –merged== 查看哪些分支已被并入当前分支(译注:也就是说哪些分支是当前分支的直接上游。):

$ git branch --merged
  iss53
* master
  • 1
  • 2
  • 3

之前我们已经合并了 iss53,所以在这里会看到它。一般来说,列表中没有 * 的分支通常都可以用 ==git branch -d== 来删掉。原因很简单,既然已经把它们所包含的工作整合到了其他分支,删掉也不会损失什么。

另外可以用 git branch –no-merged 查看尚未合并的工作:

$ git branch --no-merged
  testing
  • 1
  • 2

它会显示还未合并进来的分支。由于这些分支中还包含着尚未合并进来的工作成果,所以简单地用 git branch -d 删除该分支会提示错误,因为那样做会丢失数据:

$ git branch -d testing
error: The branch 'testing' is not fully merged.
If you are sure you want to delete it, run 'git branch -D testing'.
  • 1
  • 2
  • 3

不过,如果你确实想要删除该分支上的改动,可以用大写的删除选项 -D 强制执行,就像上面提示信息中给出的那样。

利用分支进行开发的工作流程

 现在我们已经学会了新建分支和合并分支,可以(或应该)用它来做点什么呢?在本节,我们会介绍一些利用分支进行开发的工作流程。而正是由于分支管理的便捷,才衍生出了这类典型的工作模式,你可以根据项目的实际情况选择一种用用看。

长期分支

 由于 Git使用简单的三方合并,所以就算在较长一段时间内,反复多次把某个分支合并到另一分支,也不是什么难事。也就是说,你可以同时拥有多个开放的分支,每个分支用于完成特定的任务,随着开发的推进,你可以随时把某个特性分支的成果并到其他分支中。

 许多使用 Git 的开发者都喜欢用这种方式来开展工作,比如仅在 master 分支中保留完全稳定的代码,即已经发布或即将发布的代码。与此同时,他们还有一个名为 develop 或 next 的平行分支,专门用于后续的开发,或仅用于稳定性测试 — 当然并不是说一定要绝对稳定,不过一旦进入某种稳定状态,便可以把它合并到 master 里。这样,在确保这些已完成的特性分支(短期分支,比如之前的 iss53 分支)能够通过所有测试,并且不会引入更多错误之后,就可以并到主干分支中,等待下一次的发布。

 本质上我们刚才谈论的,是随着提交对象不断右移的指针。稳定分支的指针总是在提交历史中落后一大截,而前沿分支总是比较靠前
image

 或者把它们想象成工作流水线,或许更好理解一些,经过测试的提交对象集合被遴选到更稳定的流水线
image
 你可以用这招维护不同层次的稳定性。某些大项目还会有个 proposed(建议)或 pu(proposed updates,建议更新)分支,它包含着那些可能还没有成熟到进入 next 或 master 的内容。这么做的目的是拥有不同层次的稳定性:当这些分支进入到更稳定的水平时,再把它们合并到更高层分支中去。再次说明下,使用多个长期分支的做法并非必需,不过一般来说,对于特大型项目或特复杂的项目,这么做确实更容易管理。

特性分支

 在任何规模的项目中都可以使用特性(Topic)分支。一个特性分支是指一个短期的,用来实现单一特性或与其相关工作的分支。可能你在以前的版本控制系统里从未做过类似这样的事情,因为通常创建与合并分支消耗太大。然而在 Git 中,一天之内建立、使用、合并再删除多个分支是常见的事。

 我们在上节的例子里已经见过这种用法了。我们创建了 iss53 和 hotfix 这两个特性分支,在提交了若干更新后,把它们合并到主干分支,然后删除。该技术允许你迅速且完全的进行语境切换 — 因为你的工作分散在不同的流水线里,每个分支里的改变都和它的目标特性相关,浏览代码之类的事情因而变得更简单了。你可以把作出的改变保持在特性分支中几分钟,几天甚至几个月,等它们成熟以后再合并,而不用在乎它们建立的顺序或者进度。
 由下往上,起先我们在 master 工作到 C1,然后开始一个新分支 iss91 尝试修复 91 号缺陷,提交到 C6 的时候,又冒出一个解决该问题的新办法,于是从之前 C4 的地方又分出一个分支 iss91v2,干到 C8 的时候,又回到主干 master 中提交了 C9 和 C10,再回到 iss91v2 继续工作,提交 C11,接着,又冒出个不太确定的想法,从 master 的最新提交 C10 处开了个新的分支 dumbidea 做些试验。
image

 现在,假定两件事情:我们最终决定使用第二个解决方案,即 iss91v2 中的办法;另外,我们把 dumbidea 分支拿给同事们看了以后,发现它竟然是个天才之作。所以接下来,我们准备抛弃原来的 iss91 分支(实际上会丢弃 C5 和 C6),直接在主干中并入另外两个分支。最终的提交历史将变成
image

注意:
请务必牢记这些分支全部都是本地分支,这一点很重要。 
当你在使用分支及合并的时候,一切都是在你自己的 Git 仓库中进行的 
完全不涉及与服务器的交互。
  • 1
  • 2
  • 3
  • 4
远程分支

 远程分支(remote branch)是对远程仓库中的分支的索引。它们是一些无法移动的本地分支;只有在 Git 进行网络交互时才会更新。远程分支就像是书签,提醒着你上次连接远程仓库时上面各分支的位置

 我们用 (远程仓库名)/(分支名) 这样的形式表示远程分支。比如我们想看看上次同 origin 仓库通讯时 master 分支的样子,就应该查看 origin/master 分支。如果你和同伴一起修复某个问题,但他们先推送了一个 iss53 分支到远程仓库,虽然你可能也有一个本地的 iss53 分支,但指向服务器上最新更新的却应该是 origin/iss53 分支

 可能有点乱,我们不妨举例说明。假设你们团队有个地址为 git.ourcompany.com 的 Git 服务器。如果你从这里克隆,Git 会自动为你将此远程仓库命名为 origin,并下载其中所有的数据,建立一个指向它的 master 分支的指针,在本地命名为 origin/master,但你无法在本地更改其数据。接着,Git 建立一个属于你自己的本地 master 分支,始于 origin 上 master 分支相同的位置,你可以就此开始工作

 ==一次 Git 克隆会建立你自己的本地分支 master 和远程分支 origin/master,并且将它们都指向 origin 上的 master 分支==。

image


 如果你在本地 master 分支做了些改动,与此同时,其他人向 git.ourcompany.com 推送了他们的更新,那么服务器上的 master 分支就会向前推进,而与此同时,你在本地的提交历史正朝向不同方向发展。不过只要你不和服务器通讯,你的 origin/master 指针仍然保持原位不会移动

 ==在本地工作的同时有人向远程仓库推送内容会让提交历史开始分流==。
image


 可以运行 git fetch origin 来同步远程服务器上的数据到本地。该命令首先找到 origin 是哪个服务器(本例为 git.ourcompany.com),从上面获取你尚未拥有的数据,更新你本地的数据库,然后把 origin/master 的指针移到它最新的位置上

  ==git fetch 命令会更新 remote 索引==

image


 为了演示拥有多个远程分支(在不同的远程服务器上)的项目是如何工作的,我们假设你还有另一个仅供你的敏捷开发小组使用的内部服务器 git.team1.ourcompany.com。可以用第二章中提到的 git remote add 命令把它加为当前项目的远程分支之一。我们把它命名为 teamone,以便代替完整的 Git URL 以方便使用

==把另一个服务器加为远程仓库==

image

推送本地分支

 要想和其他人分享某个本地分支,你需要把它推送到一个你拥有写权限的远程仓库。你创建的本地分支不会因为你的写入操作而被自动同步到你引入的远程服务器上,你需要明确地执行推送分支的操作。换句话说,对于无意分享的分支,你尽管保留为私人分支好了,而只推送那些协同工作要用到的特性分支。

 如果你有个叫 serverfix 的分支需要和他人一起开发,可以运行 git push (远程仓库名) (分支名):

$ git push origin serverfix

Counting objects: 20, done.
Compressing objects: 100% (14/14), done.
Writing objects: 100% (15/15), 1.74 KiB, done.
Total 15 (delta 5), reused 0 (delta 0)
To git@github.com:schacon/simplegit.git
 * [new branch]      serverfix -> serverfix
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

 这里其实走了一点捷径。Git 自动把 serverfix 分支名扩展为 refs/heads/serverfix:refs/heads/serverfix,意为“取出我在本地的 serverfix 分支,推送到远程仓库的 serverfix 分支中去”。我们将在第九章进一步介绍 refs/heads/ 部分的细节,不过一般使用的时候都可以省略它。也可以运行 git push origin serverfix:serverfix 来实现相同的效果,它的意思是“上传我本地的 serverfix 分支到远程仓库中去,仍旧称它为 serverfix 分支”。通过此语法,你可以把本地分支推送到某个命名不同的远程分支:若想把远程分支叫作 awesomebranch,可以用 git push origin serverfix:awesomebranch 来推送数据。

接下来,当你的协作者再次从服务器上获取数据时,他们将得到一个新的远程分支 origin/serverfix,并指向服务器上 serverfix 所指向的版本:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/秋刀鱼在做梦/article/detail/761961
推荐阅读
相关标签
  

闽ICP备14008679号