前言
每月迭代结束,都不会主动去删除自己创建的分支,而仓库分支几年都没有清理,导致branchs差不多2k条。
思路
一、用脚本实现批量删除,无需手动执行git命令批量操作
二、支持筛选条件,如:已合并的分支、最后一次commit在三个月前、包含release字符的分支
版本一
#!/bin/bash
repository_name="$1"
echo "您即将删除【"$repository_name"】下的分支,请先找其他人备份好,以免误删!!!!";
echo " "
# 过滤条件,将无需删除的分支加入到这个数组中
excluded_branches=("master" "develop")
# 指定要过滤的分支名称中需包含的字符(为空则不进行该过滤)
excluded_filter="release/1."
# 指定要删除的分支名称中需包含的字符(为空则不进行该过滤)
# include_filter=""
# 获取当前系统时间的秒数
current_time=$(date +%s)
# 计算三个月前的时间戳
three_months_ago=$((current_time - 3600 * 24 * 90))
# 计算两个月前的时间戳
two_months_ago=$((current_time - 3600 * 24 * 60))
# 计算一个月前的时间戳
one_months_ago=$((current_time - 3600 * 24 * 30))
# 获取所有已合并的远程分支,并进行循环处理
for branch in `git branch -r --merged origin/master | grep -v HEAD`; do
# 提取分支名称
# simple_name=$(echo "$branch" | grep '/' | cut -d'/' -f 2);
simple_name=$(echo "$branch" | sed 's#.*origin/##')
# 排除不需要删除的分支
excluded_flag="0";
for excluded_branch in "${excluded_branches[@]}"; do
if [[ "$simple_name" == "$excluded_branch" ]]; then
excluded_flag="1";
break
fi
done
# 判断是否满足删除条件
if [[ $excluded_flag == "0" ]]; then
if [[ -n $excluded_filter ]]; then
if [[ "$simple_name" == *"$excluded_filter"* ]]; then
continue
fi
fi
# if [[ -n $include_filter ]]; then
# if [[ "$simple_name" != *"$include_filter"* ]]; then
# continue
# fi
# fi
# 获取分支最后提交时间
branch_timestamp=$(git show --format="%at" "$branch" | head -n1)
# 根据实际需要选择 三个月前 $three_months_ago 两个月$two_months_ago 一个月前$one_months_ago 当前$current_time
if [[ $branch_timestamp -lt $three_months_ago ]]; then
echo "正在删除分支: $simple_name"
# 删除本地分支
# git branch -d "$simple_name"
# 删除远程分支
git push origin --delete "$simple_name"
echo "分支 $simple_name 删除完成"
echo ""
fi
fi
done
echo "清除结束"
问题一:删除分支过后,会出现本地与远端不一致的情况,导致去删除不存在的分支(通过pull无法解决)
原因:删除分支过后,会出现本地与远端不一致的情况,导致去删除不存在的分支(通过pull无法解决)
解决方法:增加更新远端与本地引用命令
版本二
增加更新远端与本地引用命令
#!/bin/bash
repository_name="$1"
echo "您即将删除【"$repository_name"】下的分支,请先找其他人备份好,以免误删!!!!";
echo " "
# 过滤条件,将无需删除的分支加入到这个数组中
excluded_branches=("master" "develop")
# 指定要过滤的分支名称中需包含的字符(为空则不进行该过滤)
excluded_filter="release/1."
# 指定要删除的分支名称中需包含的字符(为空则不进行该过滤)
include_filter=""
# 获取当前系统时间的秒数
current_time=$(date +%s)
# 计算三个月前的时间戳
three_months_ago=$((current_time - 3600 * 24 * 90))
# 计算两个月前的时间戳
two_months_ago=$((current_time - 3600 * 24 * 60))
# 计算一个月前的时间戳
one_months_ago=$((current_time - 3600 * 24 * 30))
echo "更新本地与远端的分支引用"
# 先更新分支引用,确保git branch -r拉取的分支信息,与远端仓库一致(有删除过分支会出现不一致的情况,导致不必要的遍历)
git remote update origin --prune
echo "更新完毕,开始批量删除目标分支"
# 获取所有已合并的远程分支,并进行循环处理
# for branch in `git branch -r --merged origin/master | grep -v HEAD`; do
# 获取所有的远程分支,并进行循环处理
for branch in `git branch -r | grep -v HEAD`; do
# 提取分支名称
# simple_name=$(echo "$branch" | grep '/' | cut -d'/' -f 2);
simple_name=$(echo "$branch" | sed 's#.*origin/##')
# 排除不需要删除的分支
excluded_flag="0";
for excluded_branch in "${excluded_branches[@]}"; do
if [[ "$simple_name" == "$excluded_branch" ]]; then
excluded_flag="1";
break
fi
done
# 判断是否满足删除条件
if [[ $excluded_flag == "0" ]]; then
if [[ -n $excluded_filter ]]; then
if [[ "$simple_name" == *"$excluded_filter"* ]]; then
continue
fi
fi
if [[ -n $include_filter ]]; then
if [[ "$simple_name" != *"$include_filter"* ]]; then
continue
fi
fi
# 获取分支最后提交时间
branch_timestamp=$(git show --format="%at" "$branch" | head -n1)
# 根据实际需要选择 三个月前 $three_months_ago 两个月$two_months_ago 一个月前$one_months_ago 当前$current_time
if [[ $branch_timestamp -lt $three_months_ago ]]; then
echo "正在删除分支: $simple_name"
# 删除本地分支
# git branch -d "$simple_name"
# 删除远程分支
git push origin --delete "$simple_name"
echo "分支 $simple_name 删除完成"
echo ""
fi
fi
done
echo "清除结束"
最终版
加上询问交互
- 安装inquirer.js
inquirer中文文档|inquirer js中文教程|解析 | npm中文文档
node执行脚本传参,支持传空的写法
用""包裹参数,不能只用空格分隔
node传入脚本,shell支持动态变量赋值
node用esec执行脚本,不能实时输出,会等脚本执行完毕一次性输出打印内容
nodejs 如何执行 shell 命令,并把结果实时回显输出?
Child process | Node.js v21.1.0 Documentation
解决方案:
用spawn替换exec
动态传入拉取分支规则
问题:这种写法会导致打印输出所有分支到控制台
解决:
代码如下:
const inquirer = require('inquirer');
const { spawn } = require('child_process');
const format = {
three_months_ago: '90天前',
two_months_ago: '60天前',
one_months_ago: '30天前',
current_time: '当前',
branch_all: '所有分支',
branch_merged: '已合并',
branch_no_merged: '未合并',
}
const label = {
name: '当前项目是:',
excluded_filter: '指定要过滤的分支名称中需包含的字符:',
include_filter: '指定要删除的分支名称中需包含的字符:',
date_range: '过滤日期范围:',
branch_rule: '获取远程分支范围:',
}
const question = [{
type: 'input',
message: '当前项目是:',
name: 'name',
default: 'ircloud-ydp-web'
},
{
type: 'input',
message: '指定要过滤的分支名称中需包含的字符(为空则不进行该过滤):',
name: 'excluded_filter',
default: 'release/1.'
},
{
type: 'input',
message: '指定要删除的分支名称中需包含的字符(为空则不进行该过滤):',
name: 'include_filter',
default: ''
},
{
type: 'list',
message: '过滤日期范围:',
name: 'date_range',
choices: ['three_months_ago', 'two_months_ago', 'one_months_ago', 'current_time'],
default: 'three_months_ago',
},
{
type: 'list',
message: '获取远程分支范围(全部、已合并、未合并):',
name: 'branch_rule',
choices: ['branch_all', 'branch_merged', 'branch_no_merged'],
default: 'branch_all',
}]
let answer = {}
function getParams () {
return inquirer.prompt(question).then(res => {
console.log(res)
answer = {...res}
})
}
function confirmAgain () {
const str = Object.keys(answer).reduce((prev, curr) => {
if(['date_range', 'branch_rule'].includes(curr)) {
prev += `${label[curr]} ${format[answer[curr]]}; `;
}else{
prev += `${label[curr]} ${answer[curr]}; `;
}
return prev;
}, '')
return inquirer.prompt([{
type: 'list',
message: `${str},确定吗?`,
name: 'answer',
choices: ['yes', 'no'],
default: 'yes'
}]).then(res => {
if (res.answer === 'yes') {
// let params = Object.values(res).reduce((prev, curr) => {
// prev += `"${curr}" `;
// return prev
// }, '')
// console.log(`bash scripts/delBranch.sh ${params}`);
// exec(`bash scripts/delBranch.sh ${params}`,(error, stdout, stderr) => {
// console.log(stdout);
// console.log(stderr);
// if (error !== null) {
// console.log(`exec error: ${error}`);
// }
// })
console.log('参数获取完毕,开始执行删除脚本');
let paramsArr = Object.values(answer);
const childProcess = spawn('sh', [`scripts/delBranch.sh`, ...paramsArr]);
childProcess.stdout.on('data', (data) => {
console.log(`脚本执行输出:${data}`);
});
childProcess.stderr.on('data', (data) => {
console.error(`脚本错误信息:${data}`)
})
} else {
console.log('终止执行');
}
})
}
async function main() {
await getParams();
await confirmAgain();
}
main();
#!/bin/bash
repository_name="$1"
echo "您即将删除【"$repository_name"】下的分支,请先找其他人备份好,以免误删!!!!";
echo " "
# 过滤条件,将无需删除的分支加入到这个数组中
excluded_branches=("master" "develop")
# 指定要过滤的分支名称中需包含的字符(为空则不进行该过滤)
# excluded_filter="release/1."
excluded_filter=$(echo ${2})
# 指定要删除的分支名称中需包含的字符(为空则不进行该过滤)
include_filter=$(echo ${3})
# 获取当前系统时间的秒数
current_time=$(date +%s)
# 计算三个月前的时间戳
three_months_ago=$((current_time - 3600 * 24 * 90))
# 计算两个月前的时间戳
two_months_ago=$((current_time - 3600 * 24 * 60))
# 计算一个月前的时间戳
one_months_ago=$((current_time - 3600 * 24 * 30))
time_range=$(eval echo '$'${4})
echo "更新本地与远端的分支引用"
# 先更新分支引用,确保git branch -r拉取的分支信息,与远端仓库一致(有删除过分支会出现不一致的情况,导致不必要的遍历)
git remote update origin --prune
echo "更新完毕,开始批量删除目标分支"
branch_all="";
branch_merged="--merged origin/master";
branch_no_merged="--no-merged origin/master";
get_branch_rule=$(eval echo '$'${5})
# 获取(所有/已合并/未合并)的远程分支,并进行循环处理
for branch in `git branch -r ${get_branch_rule} | grep -v HEAD` ; do
# 提取分支名称
# simple_name=$(echo "$branch" | grep '/' | cut -d'/' -f 2);
simple_name=$(echo "$branch" | sed 's#.*origin/##')
# 排除不需要删除的分支
excluded_flag="0";
for excluded_branch in "${excluded_branches[@]}"; do
if [[ "$simple_name" == "$excluded_branch" ]]; then
excluded_flag="1";
break
fi
done
# 判断是否满足删除条件
if [[ $excluded_flag == "0" ]]; then
if [[ -n $excluded_filter ]]; then
if [[ "$simple_name" == *"$excluded_filter"* ]]; then
continue
fi
fi
if [[ -n $include_filter ]]; then
if [[ "$simple_name" != *"$include_filter"* ]]; then
continue
fi
fi
# 获取分支最后提交时间
branch_timestamp=$(git show --format="%at" "$branch" | head -n1)
# 根据实际需要选择 三个月前 $three_months_ago 两个月$two_months_ago 一个月前$one_months_ago 当前$current_time
if [[ $branch_timestamp -lt $time_range ]]; then
echo "正在删除分支: $simple_name"
# 删除本地分支
# git branch -d "$simple_name"
# 删除远程分支
git push origin --delete "$simple_name"
echo "分支 $simple_name 删除完成"
echo ""
fi
fi
done
echo "清除结束"
package.json中加上执行命令
"delete-node": "node scripts/inquirerDelBranch.js"
结束
当时一键点击是最省时的