IntelliJ IDEA下基于Scala实现的Git检查工具

2023-08-21 11:39:38

图片

本文使用Scala实现自定义的Git检查工具,读者可以基于本文的示例进行扩展与实现,也可以进行其他应用方向的尝试。

01、Git检查工具

在实现Git检查工具之前需要知道程序究竟要做什么。我们知道,在管理Git分支时可以进行代码合并操作,这样可以将其他开发者提交的内容同步到当前分支中,当用户对自己的分支进行提交时就不会与现有版本产生冲突。

反向合并也可以理解为一种回合,在用户使用GitLab等版本管理软件时经常会出现这种现象,但是反向合并带来了十分严重的问题: 代码污染。

可以这样理解,用户分支是介于生产分支与测试分支中间的媒介,它必须保证与两种分支的匹配性问题,即文件差异性问题。通常用户分支是基于生产拉取出来的全新分支,而很多开发者都试图使用这个分支进行修改并提交到测试分支进行测试发布。

在理想情况下项目的测试分支与生产分支应该是一致的,因此反向合并容易被修改或纠正,但是在测试分支与生产分支差异较大的时候,反向合并会将测试分支中的内容合并到用户分支中,如果用户分支被提交到生产分支上,则将会产生不可恢复的灾难。

基于上述原因,我们使用Scala设计一款简单的检查工具,它可以检查指定分支或分支组中所有的提交信息,并从这些信息中过滤出带有回合操作的历史。

如果发生过反向合并的操作,则在Git提交历史记录中通常会带有Mergeremotetrackingbranch...的字样信息,但是带有这种信息的提交并不一定都产生了合并问题。

当通过Git检查工具过滤出符合上述特征的分支后,可以通过判断与生产分支的差异数量并设定一个判断阈值的方式再次深度过滤或直接人工观察用户分支的差异化等多种方式来确保上线分支的准确性。

02、编写配置

在Git版本控制管理章节里提到过,反向合并会对开发者的项目分支带来污染,因此可以实现一个用于Git分支检查的工具,这样在每次例行版本维护时可以帮助我们快速定位反向合并的问题。

工具不一定能解决所有的问题,因为每个问题的出现都有其随机性,但是工具却能从某些方面提升我们的效率。读者在学习完本章后,可以根据需要自行扩展并定制更多的功能。

首先在resources资源目录下,创建一个名为config.conf的文件,它用于Git检查工具的基础配置。config.conf配置文件中定义了本地Git项目的根目录及待检查的分支,代码如下:

{
  group1 = {
    workDir = "Git项目目录"
  }
  group2 = {
    workDir = "Git项目目录"
    base = master
    branches = [
      user_local_branch
    ]
  }
}

 

在上述配置中对待检查目标进行了分组,运行时用户可以将需要对比的项目及分支预先定义好,这样可以在项目启动后通过接收参数的方式来动态调整使用哪一组配置进行目标分支的检查与分析。

在每一组配置里,workDir指定本地Git项目的根目录。base用于指定项目的主分支(master)。branches是一个分支列表,它代表了待检查的分支,这些分支既可以是本地分支,也可以是远程分支。如果是远程分支,则通常要在其前面添加origin/前缀。

接下来定义一个用于控制日志输出的配置文件,代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
    <properties>
        <property name="APP_HOME">$${env:APP_HOME}</property>
        <property name="LOG_HOME">${APP_HOME}/logs</property>
        <property name="mainFilename">${LOG_HOME}/vh.log</property>
    </properties>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT" follow="true">
            <PatternLayout pattern="%date{yyyy-MM-dd HH:mm:ss.SSS} %level - %msg%n" />
        </Console>
        <RollingFile name="FileMain" fileName="${mainFilename}"
                     filePattern="${LOG_HOME}/vh%date{yyyyMMdd}_%i.log.gz">
            <PatternLayout>
                <pattern>%date{yyyy-MM-dd HH:mm:ss.SSS} %level - %msg%n</pattern>
            </PatternLayout>
            <Policies>
                <CronTriggeringPolicy schedule="0 0 0 * * ?" evaluateOnStartup="true"/>
                <SizeBasedTriggeringPolicy size="20 MB" />
            </Policies>
        </RollingFile>
    </Appenders>
    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console" />
            <AppenderRef ref="FileMain" />
        </Root>
    </Loggers>
</Configuration>

03、编写启动程序

接下来编写项目的启动程序,启动程序可以接收外界传入的参数以实现不同配置的切换使用,代码如下:

package com.scala.git
import org.slf4j.LoggerFactory

object MainCheck {
  private val log = LoggerFactory.getLogger(getClass)
  def main(args: Array[String]): Unit = {
    log.info(s"接收外界传递的切换配置: ${args.group}")
    var group = "group2"
    if(args.length > 0){
      group = args(0)
    }
    log.info(s"当前配置为$group")
    group match {
      case "group2" => CheckTask.main(args)
      case _ => log.error(s"not found $group")
    }
  }
}

 

因为Scala程序可以与Java语言混合编写,因此Java开发人员在阅读Scala程序时相对容易理解一些。

在MainCheck对象的主方法中接收了外界传递进来的group参数,它可以在程序启动时动态传递到主方法中并替代默认配置组group2。

接下来通过match操作对group变量所代表的分组配置进行匹配,如果匹配成功,则执行对应用的功能调用。如果匹配不上,则输出日志提示。

04、编写校验逻辑

在MainCheck.scala应用程序中,当外界变量group匹配成功后会调用具体的执行逻辑,此逻辑封装在CheckTask对象方法中。

在编写CheckTask对象之前先来编写GitUtil.scala程序文件,其作用为调用并执行CMD命令以便获取指定分支的所有提交信息,这些提交信息将以数组的形式返回,代码如下:

package com.scala.util
import java.io.File
import org.slf4j.LoggerFactory

import scala.sys.process.{Process, ProcessLogger}

object GitUtil {
  private val isWin = System.getProperty("os.name").toLowerCase.contains("Windows")
  private val log = LoggerFactory.getLogger(getClass)

  def getCommits(from: String, to: String, workDir: String): String = {
    val cols = Array("%H", "%s", "%an", "%ae", "%ci")
    val tem = from + ".." + to + " --pretty=format:\"" + cols.mkString("/") + "\"";
    val value = cmdCommits(s"git log " + tem, new File(workDir))
    value
  }

  def cmdCommits(cmd: String, workDir: File): String = {
    var commits:Array[String] = null;
    if(!isWin){
      commits = cmd.split("\\s")
    }else{
      commits = Array("cmd", "/c") ++ cmd.split("\\s")
    }
    Process(commits, workDir).!!(ProcessLogger(s => log.error(s"err => $s")))
  }
}

 接下来实现CheckTask.scala程序文件,代码如下:

package com.scala.git

import com.scala.util.GitUtil
import com.typesafe.config.ConfigFactory
import scala.collection.JavaConverters._

object CheckTask {

  private val config = ConfigFactory.load("config.conf").getConfig("group2")
  private val orderWorkDir = config.getString("workDir");
  private val base = config.getString("base");
  private val branchs = config.getStringList("branchs");

  def main(args: Array[String]): Unit = {
    println(s"参照对比分支[$base]")
    println(s"待检查分支集合$branchs")
    checkBraches(base, asScalaBuffer(branchs).toArray).foreach(b => println(s"发现可疑分支 $b"))
  }
  
  def checkBraches(base: String, brans: Array[String]): Array[String] = {
    brans.filter(b => checkMergeError(base, b))
  }

  private def checkMergeError(base: String, target: String): Boolean = {
        println(s"对比分支:$base,检查分支:$target")
        //取得所有提交信息
        val commits = getDiffCommits(base, target)
        //从历史提交记录过滤出回合过的分支
        val targets = commits.filter(isMergeReverse)
        targets.foreach(c => {println(c.mkString("\t"))})
        println(s"分支[$target]中可疑提交次数: ${targets.length}")
        targets.length != 0
  }

  private def isMergeReverse(messages: Array[String]): Boolean = {
    val msg = messages(1)
    if(msg.startsWith("Merge branch 'int_") || msg.startsWith("Merge remote-tracking branch ")){
      val splits = msg.split("\\s")
      val end = splits(splits.length-1)
      val flag = end.startsWith("int_") || end.startsWith("local_int_")
      return !flag
    }
    false
  }

  private def getDiffCommits(from: String, to: String): Array[Array[String]] = {
    GitUtil.getCommits(from, to, orderWorkDir).lines.map(_.split("/")).toArray
  }
}

现在尝试运行工具,随便选取系统中的某个Git项目并修改config.conf配置文件以使其与Git项目中的分支对应,然后运行MainCheck.scala程序文件,运行效果如图1所示。

图片

■ 图1 运行Git检查工具

 

 

 

 

 

 

更多推荐

基于SSM的旅游网站系统

基于SSM的旅游网站系统【附源码文档】、前后端分离开发语言:Java数据库:MySQL技术:Spring+SpringMVC+MyBatis+Vue工具:IDEA/Ecilpse、Navicat、Maven【主要功能】角色:管理员、用户管理员:用户管理、景点信息管理、购票信息管理、酒店信息管理、客房类型管理、客房信息管

终端数据防泄漏

需求背景随着各行各业业务数据信息化发展,各类产品研发及设计等行业,都有关乎自身发展的核心数据,包括业务数据、代码数据、机密文档、用户数据等敏感信息,这些信息数据有以下共性:属于核心机密资料,万一泄密会对企业造成恶劣影响,包括市场占有率下降、丧失核心竞争力、损失客户信心等各种显性与隐性影响;核心数据类型多,还有业务系统数

Java基于SpringBoot的高校招生管理系统,附源码,教程

博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W+,Csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌文章目录简介系统设计思路1数据库设计2系统整体设计2.1系统设计思想2.2系统流程图系统详细设计1系统功能模块2.管理员功能模块3学生功能模块六源码咨询

钉钉旧版服务端SDK支持异步方法的升级改造

最近项目中需要对接钉钉,有些钉钉API的访问需要使用旧版服务端SDK才能搞定,但是这个SDK使用的还是.NETFramework2.0框架,不能跨平台部署,也不支持async\await的异步操作方法,Nuget上也有其它用户改造的.NETCore版本,但是都不支持异步方法,于是就想自己改造一下,经过若干小时的改造,最

Stable Diffusion 系统教程 | 强大的ControlNet 控制网

2023年的2月13日,一款名叫ControlNet的插件横空出世,AI绘画变得更加可控ControlNet直译过来很简单,就叫做控制网,开发者是一名华裔,毕业于苏州大学,目前在斯坦福做读博士一年级,大佬大佬!在controlNet之前,基于扩散模型的绘画是极为难控制的,平时自嗨画画其实没有一点问题,随机就随机一点,但

Java 中的四种引用方式

文章目录Java中的四种引用方式1、强引用(StrongReference)(1)弱化方式1(2)弱化方式22、软引用(SoftReference)3、弱引用(WeakReference)4、虚引用(PhantomReference)Java中的四种引用方式1、强引用(StrongReference)强引用是最普遍的引

【Python】Python 模式匹配与正则表达式

Python模式匹配与正则表达式1.模式匹配与正则表达式你可能熟悉文本查找,即按下Ctrl-F,输入你要查找的词。“正则表达式”更进一步,它们让你指定要查找的“模式”。你也许不知道一家公司的准确电话号码,但如果你住在美国或加拿大,你就知道它有3位数字,然后是一个短横线,然后是4位数字(有时候以3位区号开始)。因此作为一

Android SurfaceFlinger导读(02)MessageQueue

该系列文章总纲链接:AndroidGUI系统之SurfaceFlinger系列文章目录说明:关于导读:导读部分主要是方便初学者理解SurfaceFlinger代码中的机制,为后面分析代码打下一个更好的基础,这样就可以把更多的精力放在surfaceFlinger的业务逻辑分析上。关于代码分支:以下代码分析均在androi

单元测试框架-pytest

单元测试框架-pytest官网常用插件pytest-html:生成html报告pytest-xdist:实现并发测试pytest-ordering:实现测试用例顺序设置pytest-rerunfailures:测试用例失败重试allure-pytest:生成测试报告引入依赖在项目根目录下创建:requirements.

RK3568开发笔记(七):在宿主机ubuntu上搭建Qt交叉编译开发环境,编译一个Demo,目标板运行Demo测试

若该文为原创文章,转载请注明原文出处本文章博客地址:https://hpzwl.blog.csdn.net/article/details/132733901红胖子网络科技博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中…瑞芯微开

Docker和debezium是什么关系,如何部署?

目录一、什么是Docker二、什么是debezium一、什么是DockerDocker是一种开源的容器化平台,用于构建、部署和运行应用程序。它通过将应用程序及其依赖项打包到一个称为容器的独立单元中,使应用程序能够在不同的环境中以一致的方式运行。以下是Docker的一些核心概念和特性:容器:Docker使用容器来封装应用

热文推荐