Golang实现gitlab下主干开发模式的CR流程

背景

GitLab下不具备gerrit那样的commit级别的CR的机制,多人在开发同一个功能的时候,往往是都大家拉出各自的分支,然后往开发分支上进行合并,如果开发过程中依赖对方实现的逻辑,那么这个合并过程就非常繁琐。要么就是开发过程中不做CR,大家都在一个分支上开发,最后在集中进行CR,那么这个时候功能开发得差不多了,CR的粒度就非常大,可能就会涉及到大量的代码逻辑调整。基于这种现状,这里尝试探索一种新的CR方式:边开发边CR,简化CR协同流程

实现思路

这里以master分支为例,来说下具体的流程:

  1. master分支设置为保护模式:不允许任何人提交,需要通过MR的方式合入代码.
  2. 大家都在本地都在master分支上写代码,不需要拉出feature分支.
  3. 写完代码后,直接git push推送是不成功的,这个时候需要使用git cr命令来替代git push.
  4. git cr命令执行后会先同步远端master代码带本地,若有冲突在本地解决,无冲突则将本地代码推送到远端临时分支,同时发起一个MR合并请求.
  5. 在MR里做CR,CR通过后即可合入master.
    借助GitLab下的Push Options思路,来实现这种机制,Push Options详细介绍:https://wdocs.gitlab.com/ee/user/project/push_options.html

实现代码

<pre class="prism-highlight prism-language-go">package main

import (
    "fmt"
    "os/exec"
    "strings"

    "github.com/pkg/errors"
)

func gitBranch() (branch string, err error) {
    cmd := exec.Command("/bin/bash", "-c", "git rev-parse --abbrev-ref HEAD")
    out, err := cmd.CombinedOutput()
    if err != nil {
        err = errors.Wrap(err, "请在git仓库目录下运行!!!")
        return
    }
    branch = strings.TrimSpace(string(out))
    return
}

func gitUser() (username string, err error) {
    cmd := exec.Command("/bin/bash", "-c", "git config user.name")
    out, err := cmd.CombinedOutput()
    if err != nil {
        err = errors.Wrap(err, "请在git仓库目录下运行!!!")
        return
    }
    username = strings.TrimSpace(string(out))
    return
}

func gitPull(branch string) error {
    fmt.Printf("开始同步分支 %s 远端代码到本地...\n", branch)
    shell := `
#!/bin/bash

set -e

export TARGET_BRANCH="%s"
RES=$(git ls-remote --heads origin refs/heads/${TARGET_BRANCH})
if [[ "RES" == "" ]]; then
  echo "远端仓库里不存在分支:${TARGET_BRANCH},请先创建好该分支"
  exit 1
fi

git pull origin ${TARGET_BRANCH}
    `
    args := []string{"-c", fmt.Sprintf(shell, branch)}
    cmd := exec.Command("/bin/sh", args...)
    out, err := cmd.CombinedOutput()
    if err != nil {
        err = errors.Wrapf(err, "执行失败,错误信息如下:\n%s", string(out))
        return err
    }
    fmt.Println("同步远端代码到本地成功.")
    return nil
}

func gitPush(username, branch string) error {
    fmt.Println("推送代码到远端仓库并发起评审任务...")
    shell := `
#!/bin/bash

set -e

export USER_NAME="%s"
export TARGET_BRANCH="%s"
export SOURCE_BRANCH="cr/${USER_NAME}/${TARGET_BRANCH}"

git push origin HEAD:${SOURCE_BRANCH} \
-o merge_request.create \
-o merge_request.title="%s" \
-o merge_request.target=${TARGET_BRANCH} \
-o merge_request.source=${SOURCE_BRANCH} \
-o merge_request.remove_source_branch=true
    `
    args := []string{"-c", fmt.Sprintf(shell, username, branch, fmt.Sprintf("%s merge code into %s", username, branch))}
    cmd := exec.Command("/bin/sh", args...)
    out, err := cmd.CombinedOutput()
    if err != nil {
        err = errors.Wrapf(err, "执行失败,错误信息如下:\n%s", string(out))
        return err
    }
    fmt.Println("推送代码并创建评审任务成功,详情如下:")
    fmt.Println(string(out))
    return nil
}

func main() {
    branch, err := gitBranch()
    if err != nil {
        fmt.Println(err)
        return
    }
    username, err := gitUser()
    if err != nil {
        fmt.Println(err)
        return
    }
    if err = gitPull(branch); err != nil {
        fmt.Println(err)
        return
    }
    if err = gitPush(username, branch); err != nil {
        fmt.Println(err)
        return
    }
}

代码已提交到github,链接地址为:https://github.com/5bug/git-cr

使用方法

image.png

分享:

扫一扫在手机阅读、分享本文