软件演化与配置管理

一. 软件演化

1. Lifecycle of a software

Software Development Life Cycle (SDLC): From 0 to 1

image-20211221203106899

Multiple versions in the life of a software: From 1 to n

image-20211221203333937

软件演化Lehman定律

  • 持续变化
    • 环境变化产生软件修改,软件修改促进环境变化
  • 复杂度逐渐增大
    • 系统变化,结构和功能变复杂 难以维护并失去控制,无法继续演化 需要大量额外的资源和维护工作来保持系统的正常运行
    • 软件修改引入新的错误,造成故障率升高

2. 软件演化的处理策略

  • 软件维护
    • 修改软件缺陷或增加新功能而对软件进行的变更
    • 软件变更通常发生在局部,不会改变整个结构
  • 软件再工程
    • 避免软件退化而对软件的一部分进行重新设计、编码和测试,提高软件的可维护性和可靠性等

二. 软件维护

软件维护的概念:软件产品发行和被投入运行使用之后对其的修改,以改正错误,改善性能或其他属性,从而使产品适应新的环境或新的需求

软件维护的类型

  • 纠错性维护:开发时的测试不彻底,错误隐藏,特定环境暴露 诊断和改正错误(改正性维护)
  • 适应性维护:外部环境(硬软件配置)和数据环境(数据库、数据格式、输入输出方式、数据存储介质)变化 适应变化(适应性维护)
  • 完善性维护:用户提出的功能与性能要求 完善性维护
  • 预防性维护:提高软件的可维护性、可靠性等,为以后进一步改进软件打下良好基础

几种维护活动中,完善性维护所占的比重最大,即大部分维护工作是改变和加强软件,而不是纠错

image-20211222161751794

软件维护的内容

  • 程序维护
  • 数据维护
  • 硬件维护

软件维护的成本——极其昂贵

  • 业务应用系统:维护成本与开发成本大体相同
  • 嵌入式实时系统:维护成本是开发成本的四倍以上

软件维护的典型困难

  • 软件维护中的大部分问题都可以归咎于软件规划和开发方法的缺陷:原开发人员程序难以读懂;人员流动,难以沟通;文档不足;软件设计时,欠考虑软件的可修改性;软件升级频繁,难以追踪演化

遗留系统:已经运行了很长时间的、对用户来说很重要的、但是目前已无法完全满足要求却不知道如何处理的软件系统(维护人员没有参与开发、不具备现有的开发规范、文档不完整) 更换有风险

三. 软件配置管理(SCM)

1. 背景

image-20211222162934146

2. 软件配置的概念

软件配置:由在软件工程过程中产生的所有信息项构成,可以看作该软件的具体形态(软件配置项)在某一时刻的瞬间映像

SCI:软件配置项

SCM:软件配置管理(Software Configuration Management)——处理变更、保持软件系统的完整性,跟踪变更,保存系统在不同时间的状态;使混乱减到最小,对正在开发的软件进行标识、组织和控制

image-20211222165140722

软件配置管理的目标:标识变更、控制变更、确保变更的正确实现、向开发组织内各角色报告变更

当变更发生时,能够提高适应变更的容易程度,并且能够减少所花费的工作量

3. SCM的基本元素

配置项(Configuration Item, CI),基线(Baseline),配置管理数据库(CMDB),最终硬件库(Definitive Hardware Store, DHS),最终软件库(Definitive Software Library, DSL)

3.1 配置项 CI

软件过程的输出信息主要包括三个主要类别:计算机程序(源代码和可执行程序)、描述计算机程序的文档(针对技术开发者和用户)、数据(包含在程序内部或外部)。这些项包含了所有在软件过程中产生的信息,总称为软件配置项(SCI)。SCI是软件全生命周期内受管理和控制的基本单位,大到整个系统,小到某个硬件设备或软件模块。

SCI具有唯一的名称标识和多个属性:名称、描述、类型(模型元素、程序、数据、文档等)、项目标识符、关联关系、变更和版本信息

配置项之间的依赖关系

image-20211222183927178
  • 整体-部分关系
    • Data flow diagram (DFD) analysis model
    • Analysis model requirement specification
  • 关联关系
    • Data model data flow diagram (DFD)
    • Data model test case class m
  • 组成关系、组合关系

配置项的演变图

在对象成为基线以前可能要做多次变更,在成为基线之后也可能需要频繁的变更;对于每一配置项都要建立一个演变图,以记录对象的变更历史。

image-20211222184547493

3.2 基线 Baseline

基线的概念:已经通过 正式评审和批准 的软件规格说明或代码,可以作为进一步开发的基础,并且只有通过正式的变更规程才能修改基线,基线可以看作是软件开发过程中的“里程碑”。(软件配置项成为基线前,可以迅速而随意的进行变更)

Version Release(内发布,相对正确的基线) Baseline Milestone Checkpoint

image-20211222185513337

基线是在某个时间点上对产品属性的一致描述,它是定义变化的基础???

image-20211222185651825

3.3 配置管理数据库

配置管理数据库(CMDB,也称“SCM中心存储库”),用于保存与软件相关的所有配置项的信息以及配置项之间关系的数据库——配置项及其版本号、变更可能影响到的配置项、配置项变更路线及轨迹、与配置项有关的变更内容、计划升级替换或弃用的配置项、不同配置项之间的关系(关联矩阵)

配置管理数据库 CMDB的功能:存储配置项及其之间的关系、版本控制、相关性跟踪和变更管理、需求跟踪、配置管理、审核跟踪

版本控制

image-20211222190241021

  • 测试:开发环境下,由开发人员参与的测试
  • 测试:开发环境,外部用户参与测试

SCM 软件配置管理常用工具:VSS (Microsoft Visual SourceSafe)、CVS (Concurrent Version System)、IBM Rational ClearCase、SVN (Subversion)、Git(GitHub、GitLab、GitEE)

四. 持续集成

  • 持续集成:敏捷开发的一项重要实践
    • 每个成员每天至少集成一次(可能多次)
    • 每次集成通过自动化的构建(包括编译、发布、自动化测试)来验证,从而尽快发现集成错误,大大减少集成的问题,让团队能够更快的开发内聚的软件
  • 价值
    • 减少风险:不是等到最后再做集成测试,而是每天都做测试
    • 减少重复过程:通过自动化来实现
    • 任何时间、任何地点生成可部署的软件
    • 增强项目的可见性
    • 建立团队对开发产品的信心

image-20211222191322283

Git与GitHub

一. 本地 VS 集中式 VS 分布式版本控制系统

  • 本地版本控制系统(Local VCS):采用简单的 数据库或文件系统 来记录本地文件的历次更新差异
  • 集中化的版本控制系统(Centralized VCS):包括CVS、Subversion和Perforce等,有一个单一的集中管理服务器,保存所有文件的修订版本,而协同工作的开发者通过客户端连到这台服务器,取出最新文件或者提交更新(不涉及版本信息)(有单点故障和可靠性问题)。
  • 分布式版本控制系统(Distributed VCS):客户端并不只提取最新版本的文件快照,而是把原始的代码仓库完整地镜像下来;协同工作的某一服务器发生故障,都可以用任何一个镜像出来的本地仓库恢复。
image-20211222192840621 image-20211222192844979 image-20211222192850082

二. Git的基本思想

分布式版本控制工具——最开始是用于管理Linux内核的开发(绝大多数的 Linux 内核维护工作都花在了提交补丁和保存归档的繁琐事务上)

基本思想:其他大多数SCM系统则关心文件内容的具体差异(记录更新文件,以及更新哪些行的内容),而Git关心文件数据整体是否发生了变化(不保存这些前后变化的差异)

  • 变化的文件快照,记录在微型的文件系统中
  • 提交更新,纵览全部文件的指纹信息并对文件作一快照,然后保存一个指向这次快照的索引
  • 若文件没有变化,Git不会再次保存,只对上次保存的快照作一链接

image-20211222200003014

Git中文件的三种状态: 已提交(committed):文件已经被安全地保存在本地数据库中;已修改(modified):文件被修改,还没提交保存;已暂存(staged):把已修改的文件放在下次提交时要保存的清单中

Git管理项目的三个工作区: Git目录(仓库,Git Directory)、工作目录(working directory)、暂存目录(staging area,本质上是一个文件,保存下次将要提交的文件列表信息)

image-20211222200609090

工作流程:在工作目录中修改某些文件 修改后的文件进行快照,保存到暂存区域 提交更新,将保存到暂存区域的文件快照永久转储到Git目录中

三. 基本Git指令

  • 初始化新仓库执行git init命令

  • git add 命令告诉Git开始对这些文件进行跟踪(运行了git add之后又对相应文件做了修改,要重新git add,否则提交时只会提交add之前所做的修改)

  • 提交 git commit

    • git commit 加上-a 选项,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过git add步骤
    • git commit --amend撤销上一次提交,形成新的提交(本质:合并暂存区的修改和最近的一次commit的修改内容,用生成的新的commit替换掉原来的commit,而不会形成新的分支)
    • git reset HEAD^:工作区不变,暂存区回退到上一次commit之前,上一次commit取消
  • git clone [URL]从远程服务器克隆特定的git仓库至本地

  • 文件当前处于什么状态,用git status命令

    • # On branch master nothing to commit (working directory clean) 当前没有任何跟踪着的文件,也没有任何文件在上次提交后更改过
    • # On branch master # Untracked files: … 有未跟踪的文件,使用git add开始跟踪一个新文件
    • # On branch master # Changes to be committed: 有处于已暂存状态的文件
  • 查看具体修改了什么地方,可以用git diff命令

    • git diff ,比较工作目录中当前文件和暂存区域快照之间的差异,(修改之后但未加入暂存)
    • git diff --cached,比较暂存区域内的文件的更改(修改之后加入缓存但尚未提交,与上次提交时的快照差异)
    • git diff HEAD,已缓存和未缓存的所有差异
    • image-20211222203634211
  • git reset [files] 重置文件的暂存状态,把文件从暂存区移除,使得暂存区版本和仓库版本一致、

  • git checkout把文件从暂存区复制到工作目录,丢弃自上一次git add以来的所有本地修改

  • git checkout HEAD命令将最后一次提交的结果复制到工作目录和暂存区,丢弃了本地修改

  • git rm命令从暂存区域中移除,并从工作目录中删除指定的文件(不从工作目录中删除用 git rm --cached)

  • git log查看git项目的提交历史,进行统计分析

    • 不加任何参数:会按提交时间列出所有的commit,最近的排在最上面,包含SHA-1校验和、作者的名字和电子邮件地址、提交时间以及提交说明
    • -p参数:显示每次提交的内容差异(针对每个变化了的文件,增加了哪些行、删除了哪些行)
    • --stat参数:查看每次commit的简要统计信息,列出所有被修改过的文件及总数量、每个被修改过的文件有多少行发生变化等
    • --pretty=format:… 按特定格式展示commit的结果(格式化输出)
    • --graph:以图形方式展示commit的历史

image-20211222211322457

四. Git远程仓库指令

远程仓库:托管在网络上的Git项目仓库

  • 添加远程仓库
    • git clone,指令在克隆远程仓库内容到本地之后,自动生成了一个远程仓库配置(origin)
    • git remote add ,添加一个新的远程Git仓库,同时指定一个缩写
  • 查看已配置的远程仓库
    • git remote:获取当前配置的所有远程仓库
    • git remote –v: 显示当前配置的远程仓库及其读写操作权限(fetch, push)和URL地址
    • git remote show [remote-name]:查看某个远程仓库的详细信息
  • git remote rm pb:从本地移除远程仓库 (不再关注其更新、不再对其有贡献)
  • git remote rename pb pb1:将远程仓库重命名
  • git fetch:从远程仓库抓取数据到本地,获取本地仓库尚未拥有的全部更新
    • 如果本地仓库有了不同的修改,则需要手工将本地修改与远程仓库的修改合并起来(分支的合并git merge)
  • git pull == git fetch + git merge:获取远程仓库的更新并与本地当前分支合并
  • git push [remote-name] [branch-name]:将本地仓库中的数据推送到远程仓库

五. Git分支指令

在 git 中提交时,会保存一个提交(commit)对象,该对象包含一个指向暂存内容快照的指针,包含本次提交的作者等相关附属信息,包含零个或多个指向该提交对象的父对象指针

Git中的分支本质上仅仅是个指向 commit 对象的可变指针

新提交节点中的所有文件都会被复制到暂存区域和工作目录中;只存在于老的提交节点中的文件会被删除

  • git branch列出当前所有分支
  • git branch (name) 在当前commit对象上新建一个分支指针
  • git使用一个叫做HEAD的特别指针来获知你当前在哪个分支上工作
  • 要切换到其他分支,可以执行git checkout命令
  • 创建一个新的分支并立即切换过去:git branch –b
  • 删除一个分支:git branch –d
  • 使用git log –decorate查看当前各个分支所指的commit对象
image-20211222221210782
image-20211222221226032

如果在不同的分支中都修改了同一个文件的同一部分,Git无法干净地把两者合到一起,使用git status查看任何包含未解决冲突的文件,在有冲突的文件里加入标准的冲突解决标记,可以通过它们来手工定位并解决这些冲突

分支合并的本质

  • 被合并的分支是当前commit对象的祖父节点,那么合并命令将什么也不做
  • 当前commit是被合并分支的祖父节点,就导致fast-forward合并;指向只是简单的移动,并生成一个新的提交
  • 默认把当前commit对象和被合并的分支,以及他们的共同祖父commit节点进行一次三方合并
image-20211222222429399 image-20211222222434284

分支的衍合/变基(rebase)

merge:把两个父分支合并进行一次提交,提交历史不是线性的

rebase:在当前分支上重演另一个分支的历史,提交历史是线性的——保持提交历史的整洁

image-20211222222741305

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

  • 长期分支:稳定master;develop或next后续开发或稳定性测试 进入稳定状态合并至master(拥有不同层次的稳定性:当这些分支进入到更稳定的水平时,再把它们合并到更高层分支中去 )
  • 特性分支(Topic):短期的、用来实现单一特性或与其相关工作的分支(开发工作分散在不同的流水线里,每个分支里的改变都和它的目标特性相关)

六. 远程分支

使用“(远程仓库名)/(分支名)”表示

只能看,不能修改

git push origin --delete serverfix 从origin远程仓库上删除serverfix的远程分支

七. 使用Git进行协同开发的实例

八. GitHub