Git --- 分布式版本控制系统

一、 Git 简介1. Git 诞生
  • Linus在1991年创建了开源的Linux,此后Linux系统不断发展,已经成为最大的服务器系统软件了

  • 很长一段时间内,Linux的代码管理:由世界各地的志愿者把源代码文件通过diff的方式发给Linus,然后由Linus本人通过手工方式合并代码(虽然一些商用的版本控制系统比 CVS、SVN 等好用,但Linux没有使用,这与Linux的开源精神不符)

  • 随着 Linux系统 的发展,其代码管理由人工手动合并难以维持;这时,BitKeeper的东家BitMover公司出于人道主义精神,授权Linux社区免费使用这个版本控制系统

  • 2005年,双方合作局面被打破;原因是:Linux社区牛人聚集,试图开发Samba的Andrew试图破解BitKeeper的协议,BitMover公司怒了,要收回Linux社区的免费使用权。

  • Linus花了两周时间自己用C写了一个分布式版本控制系统,这就是Git!一个月之内,Linux系统的源码已经由Git管理了

  • Git迅速成为最流行的分布式版本控制系统,尤其是2008年,GitHub网站上线了,它为开源项目免费提供Git存储,无数开源项目开始迁移至GitHub,包括jQuery,PHP,Ruby等等

  • 2. Git 是一个 分布式版本控制系统3. 集中式 和 分布式版本控制系统的 区别
  • 集中式 版本控制系统:
    • 版本库是集中存放在中央服务器的,工作时,先从中央服务器取得最新的版本;工作后,再把代码推送给中央服务器

    • 弊病:传输速度相对较慢;需要联网才能工作(不联网不能提交等操作)

    • 常见的集中式版本控制系统:CVS、 SVN

  • 分布式 版本控制系统:
    • 分布式 版本控制系统 没有中央服务器,每个人的电脑上都是一个完整的版本库;不联网即可完成代码提交等操作

    • 分布式版本控制系统,通常也有一台充当 “中央服务器” 的电脑;协同办公时,将各自代码推送到远程,其他开发人员可将远程最新代码拉取到本地仓库

    • 常见的分布式版本控制系统:Git

  • 4. 常用的版本控制对比
  • VSS
    • 微软开发的版本控制系统,全程 Visual Source Safe ;作为 Microsoft Visual Studio 的一名成员,它主要任务就是负责项目文件的管理;

    • 早期扛起了版本管理系统方面的大旗;能帮助解决一部分版本控制方面的问题,也在一定程度上帮助解决代码共享方面的难题

    • 不足:没有分支功能;同一文档在同一时间内只允许一人修改;文件存储,服务器必须共享文件夹,对文件的安全性没有足够保障

  • SVN
    • 全称:Subversion;集中式版本控制系统;是一个开放源代码的版本控制系统,相较于RCS、CVS,它采用了分支管理系统,它的设计目标就是取代CVS

    • 革新:提供了分支的功能,从而解决了VSS文件独占的问题,提供开发人员的工作效率

    • 不足:必须联网,如果不能连接到服务器上,基本上不可以工作(不能提交,还原,对比等等)

  • Git

    • 分布式版本控制系统,适合分布式开发,每一个个体都可以作为服务器(即一个完整的版本库);每一次Clone就是从远程服务器上pull到了所有的内容,包括版本信息

    • 革新:操作速度快;可离线工作;远程仓库维持这一个原始版本库(杀手锏之一),每个开发人员本地有自己的版本库,开发人员在不联网的情况下,即可工作,在本地版本库进行提交、还原、对比等操作

    • 版本的分支(branch)和合并(merge)十分方便

  • 二、 Git 相关概念1. 版本库、版本、版本号
  • 版本库:又称仓库(repository)
    • 可以把其简单的看成一个目录(文件夹);这个目录下的文件都由Git管理,当我们对文件进行修改、删除等操作,Git都会对其进行跟踪

    • Git的版本库里存了很多东西:其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支 master,以及指向master的一个指针叫 HEAD

  • 版本:对版本库中的文件进行修改,并已提交日志的方式(相关Git提交指令)让Git记录下此次操作,就是一个版本

  • 版本号:每一个版本的唯一标识;每次提交日志,Git都会生成一个版本号;和SVN不一样,Git的commit id不是1,2,3……递增的数字;而是一个SHA1计算出来的一个非常大的数字,用十六进制表示

  • 当前版本:Git中,用 HEAD 表示当前版本; HEAD 是指向 master 分支的指针

  • 2. 工作区、暂存区
  • 工作区:简单理解为,我们工作时所在的目录(文件夹)

  • 暂存区:
    • Git版本库中,暂时存放修改的文件的区域

    • Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念

  • 工作区 、暂存区 、版本库的对应关系:

  • 三、获取 Git 仓库方式一:在现有文件夹中 Git 初始化 git init
  • 现有文件夹中,执行 git init 后,版本库中会创建一个文件 .git ;这个文件是Git来跟踪管理版本库的;千万不要动
  • 方式二:克隆远程 Git 仓库 git clone 远程仓库地址
  • 命令: 默认本地仓库名字 git clone https://github.com/ZXvictory/myTest.git

  • 命令: 自定义本地仓库名字;git clone https://github.com/ZXvictory/myTest.git 自定义仓库名

  • 四、Git 在本地仓库 记录每次更新第一步:查看 仓库中被改动文件的 状态:git status
  • 状态1:未放到暂存区的文件(下图:红色部分)

  • 状态2:放到暂存区 且 未被提交的文件(下图:绿色部分)

  • (1) 查看文件变化:尚未暂存的文件 git diff(2) 查看文件变化:已暂存 将要添加到下次提交的文件 git diff --cahced(3) 一键还原的命令 git checkout -- 文件:三种情况,如下
  • 情况1:工作区的文件被修改,但没推到暂存区,执行 git checkout -- 文件,会撤销修改,回到和版本库一模一样的状态

  • 情况2:文件已添加到暂存区,但又被修改,执行 git checkout -- 文件,会撤销修改,回到添加到暂存区后的状态

  • 情况3:将暂存区 或 仓库上的文件被删除,执行 git checkout -- 文件,可还原删除的文件;若文件没有被暂存,不能还原

  • (4) 未暂存的的文件最好 及时 暂存/提交;否则一旦删除或修改,将无法撤回
  • 工作区 和 版本库的文件 最好保持一致;举例说明:版本库中 处于暂存区 或 master分支上的一个文件,在本地(工作区)手动删除,即造成:工作区中没有此文件;版本库中有此文件;此时,处理方案有两种:
    • 方案一:在版本库中,删除该文件 git rm 文件

    • 方案二:执行 git checkout --文件 还原工作区中删除的文件

  • 第二步:将有变动的文件 放到 缓存区:git add *git add -A
  • 添加单个文件到暂存区:git add 单个文件

  • 添加所有文件到暂存区:git add *git add -A

  • 执行上面的命令,没有任何显示,这就对了,Unix的哲学是 “没有消息就是好消息”,说明添加成功;再查看文件状态如下

  • 第三步:将缓存区文件 提交到 本地仓库:git commit -m "日志"
  • 执行完以上第一、二、三步后,意味着 将版本库中文件的修改,提交到了本地仓库的当前分支上了

  • 图示步骤如下:
    • 将有改动的文件添加到暂存区

    • 将暂存区文件提交到本地仓库

  • 五、 Git 日志 与 版本回退1. 查看详细日志 git log2. 查看简要日志(常用) git log --oneline3. 查看详细版本号日志 git log --pretty=oneline4. 查看所有日志(含回退前的日志) git reflog5. 版本回退 git reset --hard 版本号前几位git reset --hard HEAD^
  • 当前版本:用 HEAD 表示

  • 方式一:
    • git reset --hard HEAD^ 表示退回上一个版本;
    • 实际上有几个^ 就退回几个版本;退回到前100个版本的命令:git reset --hard HEAD~100
  • 方式二:
    • git reet --hard 版本号 其中版本号前几位即可,Git会自动去找
  • 六、 Git 分支操作1. Git 分支开发工作流
  • 什么是分支:
    • 每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支
    • HEAD 总是指向当前分支
  • 2. Git 分支 常见操作(1) 查看所有分支(当前分支前面有 * 表示) git branch(2) 查看本地分支 和 所有远程分支 git branch -a(3) 创建分支 git branch 分支名(4) 删除分支 git branch -d 分支名
  • 新分支上:文件修改提交后,没有被合并;使用git branch -d 分支名是不能将该分支删除的,会报错

  • 此时,使用强制删除指令,可删除没有被合并的新分支 git branch -D 分支名

  • (5) 切换分支 git checkout 分支名(6) 创建 并 切换分支 git checkout -b 分支名(7) 合并指定分支到当前分支 git merge 分支名git merge --no-ff 分支名(建议使用)
  • 执行 git merge 分支名 命令:Git进行"快进式合并"(fast-farward merge);如果是将dev合并到master上,执行上述命令,Git 会直接将Master分支指向Develop分支,不会有合并日志了;如下图:
  • 执行 git merge --no-ff 分支名 命令:会执行正常合并,并在Master分支上生成一个新节点,作为合并时的操作节点,执行上述命令,将以默认日志进行提交;若想自定义日志内容可执行 git merge --no-ff 分支名
  • 3. Git 分支的变基七、远程仓库 与 远程分支1. 介绍 远程仓库
  • 远程仓库
    • 作为Git的杀手级功能之一

    • 远程仓库存储着一个代码的原始版本库;开发人员通过 Git远程仓库 与 Git本地仓库 之间的通信,来多人协同办公,更新代码

    • 找一台电脑充当服务器的角色,每天24小时开机,其他每个人都从这个“服务器”仓库克隆一份到自己的电脑上,并且各自把各自的提交推送到服务器仓库里,也从服务器仓库中拉取别人的提交;这里说的“服务器仓库”,就是Git远程仓库

  • GitHub
    • GitHub 提供Git仓库托管服务的;只要注册一个GitHub账号,就可以免费获得Git远程仓库
  • 2. 本地项目 关联到远程仓库第一步:GitHup 上 添加远程仓库
  • GitHub 上创建远程仓库

    • 登录GitHub后,点击 New repository 创建远程仓库
    • 远程仓库的默认名字是 origin
  • 第二步:GitHub 上 设置 SSH 秘钥第三步:本地添加远程仓库 (关联) git remote add [shortname] [url]
  • 运行 git remote add [shortname] [url] 添加一个新的远程 Git 仓库,同时指定一个你可以轻松引用的简写,即 [shortname];之后涉及到该远程仓库的 url,均可用 [shortname] 代替

  • 查看所有远程仓库,命令 git remoe

  • 查看所有远程仓库详细信息,命令:git remote -v

  • 第四步:推送到远程仓库 git push [remote-name] [branch-name]
  • [remote-name] 也就是第三步中的 [shortname]
  • 第四步:(simple方式)推送到远程仓库 并 指定默认远程仓库 git push -u [remote-name] [branch-name] ;此后 推送 或 抓取可简化命令
  • 上面命令将本地的 [branch-name] 分支推送到 [remote-name] 主机,同时指定 [remote-name] 为默认主机,后面就可以不加任何参数使用 git push

  • 简化后的指令 git push;默认只推送当前分支 到默认主机

  • 第五步:从远程仓库 抓取 更新 git fetch [remote-name] [branch-name]
  • 若第四步用了 simple 方式,此指令可以简化为 git fetch

  • 特殊:抓取远程更新,但并不会自动合并

  • 第五步:从远程仓库 拉取 更新 git plull [remote-name] [branch-name]
  • 若第四步用了 simple 方式,此指令可以简化为 git pill

  • 特殊:拉取远程更新,会自动合并

  • 3. 查看 所有的远程仓库 git remote
  • 一个本地仓库,可对应多个远程仓库; 查看 所有的远程仓库的 命令 git remote

  • 查看所有远程仓库详细信息,命令:git remote -v

  • 4. 查看 某远程仓库的更多信息 git remote show [remote-name]5. 删除 远程仓库 git remote rm [remote-name]八、 Git 分支管理策略 及 工作场景1. Git 分支管理策略(1) 主分支 master
  • 代码库应该有一个、且仅有一个主分支。所有提供给用户使用的正式版本,都在这个主分支上发布, 即 用来分布重大版本

  • Git主分支的名字,默认叫做Master。它是自动建立的,版本库初始化以后,默认就是在主分支在进行开发

  • (2) 开发分支 dev
  • 这个分支可以用来生成代码的最新隔夜版本(nightly)。如果想正式对外发布,就在Master分支上,对Develop分支进行"合并"(merge),下图为master分支多次合并dev的图示
  • (3) 功能分支 feature(临时)
  • 为了开发某种特定功能,从Develop分支上新建 feature 功能分支;开发完成后,再将 feature 分支合并到 develop 分支上

  • 命名:可以采用 feature-* 的形式

  • 测试稳定后,删除 feature分支,将 develop 分支合并到 master 分支上

  • git checkout -b feature-x develop git checkout develop git merge --no-ff feature-x # 开发完成后,将功能分支合并到develop分支 git branch -d feature-x # 删除feature分支(4) 预发布分支 release(临时)
  • 发布正式版本之前(即合并到Master分支之前),我们可能需要有一个预发布的版本进行测试

  • 预发布分支是从Develop分支上面分出来的,预发布结束以后,必须合并进Develop和Master分支

  • 命名:可以采用 release-* 的形式

  • git checkout -b release-1.2 develop git checkout master git merge --no-ff release-1.2 # 确认没有问题后,合并到master分支 git tag -a 1.2 # 对合并生成的新节点,做一个标签 git checkout develop git merge --no-ff release-1.2 # 再合并到develop分支 git branch -d release-1.2 # 删除预发布分支(5) bug分支(临时)
  • 软件正式发布以后,难免会出现bug。这时就需要创建一个分支,进行bug修补

  • 修补bug分支是从Master分支上面分出来的;修补结束以后,再合并进Master和Develop分支

  • 命名:可以采用 fixbug-* 的形式

  • git checkout -b fixbug-0.1 master git checkout master git merge --no-ff fixbug-0.1 # 修补结束后,合并到master分支 git tag -a 0.1.1 git checkout develop git merge --no-ff fixbug-0.1 # 合并到develop分支 git branch -d fixbug-0.1 # 删除"修补bug分支"2. 解决:分支冲突
  • 多人分别在自己的分支上,开发同一个文件中的代码,在合并时可能会造成分支冲突

  • 合并分支,若有冲突,会出现如下提示:

  • Git用<<<<<<<=======>>>>>>> 标记出不同分支的内容,如下图;需要手动解决冲突

  • 3. 解决:紧急bug
  • 场景:在自己的开发分支上,代码写到一半,需要要解决紧急bug;但有不想提交现在的代码,因为还没开发完

  • 解决:此时,可以将开发分支上的代码储藏起来 git stash;现在工作区是干净的,不会影响解决bug;可以看看储藏区git stash list,刚才的代码在 储藏区里了;解决完bug后,需要恢复储藏的代码,有两个命令:

    • 命令一:git stash apply;此时代码已恢复,但储藏区的内容并没有清除,需要git stash drop 手动清除

    • 命令二:git stash pop;恢复代码的同时,把储藏区的内容也删除了

  • 4. 开发:不确定的功能
  • 场景:master上引出一个分支,来开发新功能;开发完了但还没合并到其他分支上;此时需求不要了,需要删除新分支,执行git branch -d 分支名;会报错error: The branch 'feature' is not fully merged.

  • 解决:此时可以使用 git branch -D 分支名;来强行删除新分支

  • 九、Git 自定义

    相关内容推荐