赞
踩
git submodule、git subtree 介绍及差异见: submodule、subtree方案详解
子仓库分支与主仓库分支不会主动关联,不便管理,开发中需要较为小心
增加node脚本,支持主仓库子仓库同频分支操作,保证主、子分支的一致性
介绍
支持分支创建、切换、合并、拉取、创建Tags的同频操作操控面板
脚本目录结构
⚠️使用步骤
初始化子仓库
git submodule add [子仓库git地址]
增加packagen.json的script配置
- "scripts": {
- "branch": "node bin/branch.js"
- },
修改bin/branchMode/index.js 内的submoduleName变量
替换为子仓库的文件夹名称
运行脚本
npm run branch
bin/branch.js
- /**
- * 用于主仓库 & 子仓库 的分支同步
- */
-
- const {
- checkGitStatus,
- slcBranchType,
- BRANCH_CHECKOUT_TYPE
- } = require('./branchMode/index')
- const createBranch = require('./branchMode/createBranch.js')
- const toggleBranch = require('./branchMode/toggleBranch.js')
- const mergeBranch = require('./branchMode/mergeBranch.js')
- const pullBranch = require('./branchMode/pullBranch.js')
- const createBranchTags = require('./branchMode/createBranchTags.js')
-
- /**
- * 二、branch操作
- */
- const main = async() => {
- // 工作空间干净才允许执行操作
- await checkGitStatus()
- // git操作类型选择
- const featType = await slcBranchType()
- switch (featType) {
- // 创建
- case BRANCH_CHECKOUT_TYPE.create:
- createBranch()
- break
- // 切换
- case BRANCH_CHECKOUT_TYPE.toggle:
- toggleBranch()
- break
- // 合并
- case BRANCH_CHECKOUT_TYPE.merge:
- mergeBranch()
- break
- // 拉取
- case BRANCH_CHECKOUT_TYPE.pull:
- pullBranch()
- break
- // 创建Tags
- case BRANCH_CHECKOUT_TYPE.pushTags:
- createBranchTags()
- break
- default:
- break
- }
- }
-
- module.exports = main()
bin/util.js
- const inquirer = require('inquirer')
- const execa = require('execa')
-
- function log() {
- const str = [...arguments].join(', ')
- console.warn(
- `\n\u001b[33m ${str} \u001b[39m\n`
- )
- }
-
- function logGreen() {
- const str = [...arguments].join(', ')
- console.warn(
- `\n\u001b[32m ${str} \u001b[0m\n`
- )
- }
-
- function inputPrompt(msg, backup = null) {
- return inquirer.prompt([{
- name: 'detail',
- type: 'input',
- default: backup,
- message: msg
- }]).then(({ detail }) => detail)
- }
-
- function confirmPrompt(msg, backup = true) {
- return inquirer.prompt([{
- name: 'yes',
- type: 'confirm',
- default: backup,
- message: msg
- }]).then(({ yes }) => yes)
- }
-
- function selectPrompt(msg, list = [], backup = 0) {
- return inquirer.prompt([{
- name: 'select',
- type: 'list',
- choices: [
- new inquirer.Separator(' '),
- ...list,
- new inquirer.Separator(' ')
- ],
- default: backup,
- message: msg
- }]).then(({ select }) => select)
- }
-
- /**
- * 获取本地分支列表
- * @param {Boolean} isOrigin 查看远程分支
- * @param {String} cwd 执行路径
- * @returns branchList
- */
- const getBranchList = async(isOrigin = false, cwd = '') => {
- if (isOrigin) await execa.command('git fetch origin', { ...(cwd ? { cwd } : {}) })
- const currentBranch = execa.commandSync('git rev-parse --abbrev-ref HEAD', { ...(cwd ? { cwd } : {}) }).stdout
- let branchList = execa.commandSync(`git branch ${isOrigin ? '-r' : ''}`, { ...(cwd ? { cwd } : {}) }).stdout
- branchList = branchList.split('\n').map(item => {
- return item.split(' ').slice(-1)[0]
- })
- branchList = branchList.filter(item => item !== currentBranch)
- return branchList
- }
-
- const returnLog = (text = '') => {
- log(text)
- process.exit()
- }
-
- // 同步for运行Command
- const awaitForCommand = async(commandList = []) => {
- for (let i = 0; i < commandList.length; i++) {
- const item = commandList[i]
- await execa.command(item[0], { ...(item[1] ? { cwd: item[1] } : {}) })
- }
- }
-
- /**
- * 查看文件修改状态
- * @param {String} type 指定类型
- */
- const getStatusList = (type = '') => {
- let statusList = execa.commandSync('git status -s').stdout
- statusList = statusList.split('\n').map(item => {
- return item.split(' ').filter(item => item)
- })
- if (type) {
- statusList = statusList.filter(item => {
- return item?.[0]?.toUpperCase() === type.toUpperCase()
- })
- }
- return statusList
- }
-
- module.exports = {
- log,
- logGreen,
- inputPrompt,
- confirmPrompt,
- selectPrompt,
- getBranchList,
- returnLog,
- awaitForCommand,
- getStatusList
- }
bin/branchMode/index.js
- const execa = require('execa')
- const {
- selectPrompt,
- returnLog
- } = require('../util')
-
- // 子仓库目录
- const submoduleName = 'child-ui'
-
- // 当前分支
- const currentBranch = execa.commandSync('git rev-parse --abbrev-ref HEAD').stdout
-
- // 分支切换类型
- const BRANCH_CHECKOUT_TYPE = {
- create: 1,
- toggle: 2,
- merge: 3,
- pull: 4,
- pushTags: 5
- }
-
- // 枚举是否
- const ISOK_ENMU = {
- yes: 1,
- no: 2
- }
-
- // 选择是否
- const ISOK_OPTIONS = [
- { name: '是', value: ISOK_ENMU.yes },
- { name: '否', value: ISOK_ENMU.no }
- ]
-
- // (1)、检测当前工作空间是否有未提交的文件
- const checkGitStatus = async() => {
- const gitStatus = await execa.command('git status')
- if (!gitStatus.stdout.includes('nothing to commit')) {
- returnLog('检测到主仓库代码未提交, 请自行检查!')
- }
- const childStatus = await execa.command('git status', { cwd: `./${submoduleName}` })
- if (!childStatus.stdout.includes('nothing to commit')) {
- returnLog('检测到子仓库代码未提交, 请自行检查!')
- }
- }
-
- // (2)、选择分支操作类型
- const slcBranchType = async() => {
- const selectProjectName = await selectPrompt('branch操作类型选择',
- [
- { name: '创建', value: BRANCH_CHECKOUT_TYPE.create },
- { name: '切换', value: BRANCH_CHECKOUT_TYPE.toggle },
- { name: '合并', value: BRANCH_CHECKOUT_TYPE.merge },
- { name: '拉取', value: BRANCH_CHECKOUT_TYPE.pull },
- ...(
- currentBranch === 'master' ? [{ name: '创建Tags', value: BRANCH_CHECKOUT_TYPE.pushTags }] : []
- )
- ]
- )
- return selectProjectName
- }
-
- // (3)、子分支切换 & pull
- const toggleChildBranch = async(customBranch = currentBranch) => {
- const childBranch = execa.commandSync('git rev-parse --abbrev-ref HEAD', { cwd: `./${submoduleName}` }).stdout
- if (childBranch !== customBranch) {
- await execa.command(`git checkout ${customBranch}`, { cwd: `./${submoduleName}` })
- }
- await execa.command('git pull', { cwd: `./${submoduleName}` })
- }
-
- module.exports = {
- submoduleName,
- currentBranch,
- BRANCH_CHECKOUT_TYPE,
- ISOK_ENMU,
- ISOK_OPTIONS,
- checkGitStatus,
- slcBranchType,
- toggleChildBranch
- }
bin/branchMode/createBranch.js
- /**
- * 创建分支(主 + 子)
- */
- const {
- logGreen,
- inputPrompt,
- getBranchList,
- returnLog,
- awaitForCommand
- } = require('../util')
- const {
- submoduleName,
- checkGitStatus,
- toggleChildBranch
- } = require('./index')
-
- const createBranch = async() => {
- // (1) 子分支切换 & pull
- await toggleChildBranch()
- // (1.1) 主pull
- await awaitForCommand([['git pull']])
- // (2) 检测暂存区
- await checkGitStatus()
- // (3) 输入分支名
- const createName = await inputPrompt(`请输入分支名:`)
- if (!createName) return
- const localBranchList = await getBranchList()
- if (localBranchList.includes(createName)) {
- returnLog('分支已存在')
- }
- // (4) 创建主分支
- const parentOriginList = await getBranchList(true)
- // (4.1) 远程分支存在,创建本地 & 关联远程
- if (parentOriginList.includes(`origin/${createName}`)) {
- await awaitForCommand([[`git checkout -b ${createName} origin/${createName}`]])
- // (4.2) 创建分支 & 推至远程
- } else {
- await awaitForCommand([
- [`git checkout -b ${createName}`],
- [`git push --set-upstream origin ${createName}`]
- ])
- }
- // (5) 创建子分支
- const childLocalList = await getBranchList(false, `./${submoduleName}`)
- const childOriginList = await getBranchList(true, `./${submoduleName}`)
- // (5.1) 本地存在
- if (childLocalList.includes(createName)) {
- await awaitForCommand([[`git checkout ${createName}`, `./${submoduleName}`]])
- // (5.2) 远程存在
- } else if (childOriginList.includes(`origin/${createName}`)) {
- await awaitForCommand([[`git checkout -b ${createName} origin/${createName}`, `./${submoduleName}`]])
- // (5.3) 都不存在
- } else {
- await awaitForCommand([
- [`git checkout -b ${createName}`, `./${submoduleName}`],
- [`git push --set-upstream origin ${createName}`, `./${submoduleName}`]
- ])
- }
- logGreen('已成功创建')
- }
-
- module.exports = createBranch
bin/branchMode/mergeBranch.js
- 1/**
- * 合并分支(主 + 子)
- */
- const {
- logGreen,
- selectPrompt,
- getBranchList,
- awaitForCommand,
- getStatusList
- } = require('../util')
- const {
- submoduleName,
- currentBranch,
- checkGitStatus,
- toggleChildBranch
- } = require('./index')
-
- const mergeBranch = async() => {
- // (1) 主拉取
- await awaitForCommand([
- [`git pull`]
- ])
- // (2) 子分支切换 & pull
- await toggleChildBranch(currentBranch)
- // (3) 检测暂存区
- await checkGitStatus()
- // (4) 分支选择
- const localBranchList = await getBranchList()
- const branchKey = await selectPrompt('请选择合并分支', localBranchList)
- // (5) 主分支合并
- try {
- await awaitForCommand([[`git merge ${branchKey}`]])
- } catch (error) {
- // log(`主仓库merge异常: ${error}`)
- }
- // (6) 子分支合并
- await awaitForCommand([[`git merge ${branchKey}`, `./${submoduleName}`]])
- // (7) 自动提交commit同步文件
- const statusList = getStatusList()
- if (
- statusList.length === 1 &&
- // 暂存区 || 冲突
- ['M', 'UU'].includes(statusList?.[0]?.[0]?.toUpperCase()) &&
- statusList[0][1] === submoduleName
- ) {
- await awaitForCommand([
- [`git add ${submoduleName}`],
- [`git commit -m fix:子仓库commit同步`]
- ])
- }
- // (8) 提交远程
- await awaitForCommand([
- [`git push`, `./${submoduleName}`],
- [`git push`]
- ])
-
- logGreen('已合并')
- await checkGitStatus()
- }
-
- module.exports = mergeBranch
bin/branchMode/pullBranch.js
- /**
- * 拉取分支(主 + 子)
- */
- const { logGreen, awaitForCommand } = require('../util')
- const { currentBranch, checkGitStatus, toggleChildBranch } = require('./index')
-
- const pullBranch = async() => {
- await awaitForCommand([
- [`git pull`],
- [`git submodule update`]
- ])
- // 子分支切换 & pull
- await toggleChildBranch(currentBranch)
- logGreen(`git pull 成功`)
- await checkGitStatus()
- }
-
- module.exports = pullBranch
bin/branchMode/toggleBranch.js
- /**
- * 切换分支(主 + 子)
- */
- const { logGreen, selectPrompt, getBranchList, awaitForCommand } = require('../util')
- const { submoduleName, checkGitStatus, toggleChildBranch } = require('./index')
-
- const toggleBranch = async() => {
- const localBranchList = await getBranchList()
- const branchKey = await selectPrompt('请选择分支', localBranchList)
- // (1) 切换分支
- await awaitForCommand([
- [`git checkout ${branchKey}`],
- [`git checkout ${branchKey}`, `./${submoduleName}`]
- ])
- // (2) 检测暂存区
- await checkGitStatus()
- await awaitForCommand([
- [`git pull`],
- [`git submodule update`]
- ])
- // (3) 子分支切换 & pull
- await toggleChildBranch(branchKey)
-
- logGreen(`切换分支:${branchKey} 成功, 并执行git pull`)
- await checkGitStatus()
- }
-
- module.exports = toggleBranch
bin/branchMode/createBranchTags.js
- /**
- * 创建Tags(主 + 子)
- */
- const { logGreen, selectPrompt, inputPrompt, awaitForCommand } = require('../util')
- const { submoduleName, ISOK_ENMU, ISOK_OPTIONS } = require('./index')
-
- const createBranchTags = async() => {
- // (1) 主仓库Tags选择
- // const isTags = await selectPrompt('是否需要打Tags', ISOK_OPTIONS)
- // if (isTags !== ISOK_ENMU.yes) return
-
- // (2) 输入TagsName
- let tagsName = await inputPrompt(`请输入Tags名称:`)
- if (!tagsName) return
- tagsName = `tags/${tagsName}`
-
- // (3) 创建主仓库Tags
- await awaitForCommand([
- [`git tag ${tagsName}`],
- [`git push origin ${tagsName}`]
- ])
- logGreen(`主仓库Tags已推: ${tagsName}`)
-
- // (4) 创建子仓库Tags
- const isChildTags = await selectPrompt('子仓库是否需要打Tags', ISOK_OPTIONS)
- if (isChildTags !== ISOK_ENMU.yes) return
- await awaitForCommand([
- [`git tag ${tagsName}`, `./${submoduleName}`],
- [`git push origin ${tagsName}`, `./${submoduleName}`]
- ])
- logGreen(`子仓库Tags已推: ${tagsName}`)
- }
-
- module.exports = createBranchTags
到这里就大工告成了,有用的话麻烦关注收藏一下~
欢迎留言探讨
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。