使用PowerShell开发脚本程序进行批量SVN提交
使用PowerShell开发脚本程序进行批量SVN提交
随着软件开发的不断进步,版本控制系统如Subversion (SVN) 成为了团队协作和代码管理的重要工具。当需要一次性提交大量文件时,手动操作效率低下且容易出错。为此,可以使用PowerShell编写一个脚本来简化这一过程,并通过PS2EXE将脚本转换为.exe文件,以方便非技术用户执行。升级到PowerShell 7解决了输出中文字符时的乱码问题。
主脚本
主程序逻辑包括询问用户SVN工作目录、验证目录有效性、扫描目录并调用批量提交函数。在结束时,确保返回到原始位置:
# 在脚本开头添加
$scriptPath = $PSScriptRoot
if ([string]::IsNullOrEmpty($scriptPath)) {
$scriptPath = Split-Path -Parent ([System.Diagnostics.Process]::GetCurrentProcess().MainModule.FileName)
}
# 修改默认SVN路径逻辑
$configPath = Join-Path $scriptPath "config.json"
$defaultSvnPath = "E:\safeware\Apache-Subversion-1.14.2\bin\svn.exe"
# 尝试从配置文件读取SVN路径
if (Test-Path $configPath) {
try {
$config = Get-Content $configPath -Raw | ConvertFrom-Json
$defaultSvnPath = $config.svnPath
} catch {
Write-Host "读取配置文件失败,使用默认SVN路径" -ForegroundColor Yellow
}
}
do {
Write-Host "请输入SVN可执行文件路径 (直接回车使用默认路径: $defaultSvnPath):"
$inputPath = Read-Host
if ([string]::IsNullOrWhiteSpace($inputPath)) {
$svnExecutable = $defaultSvnPath
} else {
$svnExecutable = $inputPath
}
if (-not (Test-Path $svnExecutable)) {
Write-Host "SVN可执行文件路径无效或不存在,请重新输入。" -ForegroundColor Red
continue
}
# 测试SVN命令是否可用
try {
$svnVersion = & $svnExecutable --version 2>&1
if ($LASTEXITCODE -eq 0) {
Write-Host "已成功找到SVN程序。" -ForegroundColor Green
break
} else {
Write-Host "SVN程序测试失败,请检查路径是否正确。" -ForegroundColor Red
}
} catch {
Write-Host "无法执行SVN程序,请检查路径是否正确。" -ForegroundColor Red
}
} while ($true)
# 在脚本开头添加
Add-Type -AssemblyName System.Web
# 函数:执行SVN命令并处理错误
function Invoke-SvnCommand {
param (
[string[]]$Arguments,
[switch]$IgnoreError
)
try {
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = $svnExecutable
$pinfo.Arguments = $Arguments -join ' '
$pinfo.RedirectStandardOutput = $true
$pinfo.RedirectStandardError = $true
$pinfo.UseShellExecute = $false
$pinfo.StandardOutputEncoding = [System.Text.Encoding]::Default
$pinfo.StandardErrorEncoding = [System.Text.Encoding]::Default
$pinfo.CreateNoWindow = $true
Write-Host "执行命令: svn $($pinfo.Arguments)" -ForegroundColor Gray
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $pinfo
$process.Start() | Out-Null
$output = $process.StandardOutput.ReadToEnd()
$errorOutput = $process.StandardError.ReadToEnd()
$process.WaitForExit()
if ($process.ExitCode -ne 0 -and -not $IgnoreError) {
Write-Host "SVN命令执行失败: $errorOutput" -ForegroundColor Red
return $false
}
if (-not [string]::IsNullOrEmpty($output)) {
Write-Host $output
}
return $true
}
catch {
Write-Host "执行SVN命令时出错: $_" -ForegroundColor Red
return $false
}
}
# 函数:检查目录是否在SVN工作副本中
function Test-SvnWorkingCopy {
param (
[string]$Path
)
$currentPath = $Path
while ($currentPath) {
try {
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = $svnExecutable
$pinfo.Arguments = "info `"$currentPath`""
$pinfo.RedirectStandardOutput = $true
$pinfo.RedirectStandardError = $true
$pinfo.UseShellExecute = $false
$pinfo.StandardOutputEncoding = [System.Text.Encoding]::Default
$pinfo.StandardErrorEncoding = [System.Text.Encoding]::Default
$pinfo.CreateNoWindow = $true
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $pinfo
$process.Start() | Out-Null
$output = $process.StandardOutput.ReadToEnd()
$errorOutput = $process.StandardError.ReadToEnd()
$process.WaitForExit()
if ($process.ExitCode -eq 0) {
# 找到了SVN工作副本的根目录
return $true
}
}
catch {
Write-Host "检查SVN工作副本时出错: $_" -ForegroundColor Red
}
# 向上查找父目录
$currentPath = Split-Path -Parent $currentPath
}
return $false
}
# 函数:检查路径是否在SVN版本控制中
function Test-SvnPath {
param (
[string]$Path
)
try {
# 使用 Start-Process 来执行 SVN 命令
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = $svnExecutable
# 直接使用路径,不进行编码转换
$pinfo.Arguments = "status --non-recursive `"$Path`""
$pinfo.RedirectStandardOutput = $true
$pinfo.RedirectStandardError = $true
$pinfo.UseShellExecute = $false
# 设置为默认的系统编码(通常是GBK)
$pinfo.StandardOutputEncoding = [System.Text.Encoding]::Default
$pinfo.StandardErrorEncoding = [System.Text.Encoding]::Default
$pinfo.CreateNoWindow = $true
Write-Host "准备执行命令: $($pinfo.FileName) $($pinfo.Arguments)" -ForegroundColor Gray
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $pinfo
$process.Start() | Out-Null
# 同时读取标准输出和错误输出
$output = $process.StandardOutput.ReadToEnd()
$errorOutput = $process.StandardError.ReadToEnd()
$process.WaitForExit()
Write-Host "命令输出:"
if ([string]::IsNullOrEmpty($output)) {
Write-Host " 标准输出为空"
} else {
Write-Host $output
}
if (-not [string]::IsNullOrEmpty($errorOutput)) {
Write-Host "错误输出:" -ForegroundColor Yellow
Write-Host $errorOutput -ForegroundColor Yellow
}
Write-Host "退出代码: $($process.ExitCode)"
Write-Host "检查路径: $Path"
Write-Host "--------------------------------"
if ($process.ExitCode -eq 0) {
if ([string]::IsNullOrEmpty($output)) {
return $true
}
if ($output -match '^\?') {
return $false
}
return $true
}
return $false
}
catch {
Write-Host "检查路径状态时出错: $_" -ForegroundColor Red
Write-Host "错误详情: $($_.Exception.Message)" -ForegroundColor Red
Write-Host "堆栈跟踪: $($_.ScriptStackTrace)" -ForegroundColor Red
return $false
}
}
# 函数:批量提交文件
function Submit-BatchFiles {
param (
[string[]]$Files,
[int]$BatchNumber,
[int]$ProcessedFiles,
[int]$TotalFiles
)
if ($Files.Count -eq 0) { return }
Write-Host "[批次 $BatchNumber] [总进度: $ProcessedFiles/$TotalFiles] 正在提交 $($Files.Count) 个文件..."
Write-Host "提交的文件列表:"
$Files | ForEach-Object { Write-Host " $_" }
$commitMessage = "Batch commit #$BatchNumber ($ProcessedFiles/$TotalFiles)"
try {
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = $svnExecutable
# 获取文件的绝对路径并构建命令参数
$absolutePaths = $Files | ForEach-Object {
# 从当前工作目录获取绝对路径
$absolutePath = Join-Path (Get-Location).Path $_
# 规范化路径(处理 .. 和 . 等)
$absolutePath = [System.IO.Path]::GetFullPath($absolutePath)
"`"$absolutePath`""
}
$fileArgs = [string]::Join(' ', $absolutePaths)
$pinfo.Arguments = "commit $fileArgs -m `"$commitMessage`""
$pinfo.RedirectStandardOutput = $true
$pinfo.RedirectStandardError = $true
$pinfo.UseShellExecute = $false
$pinfo.StandardOutputEncoding = [System.Text.Encoding]::Default
$pinfo.StandardErrorEncoding = [System.Text.Encoding]::Default
$pinfo.CreateNoWindow = $true
Write-Host "执行命令: svn $($pinfo.Arguments)" -ForegroundColor Gray
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $pinfo
$process.Start() | Out-Null
$output = $process.StandardOutput.ReadToEnd()
$errorOutput = $process.StandardError.ReadToEnd()
$process.WaitForExit()
Write-Host "命令输出:"
if ([string]::IsNullOrEmpty($output)) {
Write-Host " 标准输出为空"
} else {
Write-Host $output
}
if (-not [string]::IsNullOrEmpty($errorOutput)) {
Write-Host "错误输出:" -ForegroundColor Yellow
Write-Host $errorOutput -ForegroundColor Yellow
}
Write-Host "退出代码: $($process.ExitCode)"
Write-Host "--------------------------------"
if ($process.ExitCode -ne 0) {
Write-Host "SVN提交失败: $errorOutput" -ForegroundColor Red
return $false
}
return $true
}
catch {
Write-Host "执行SVN提交时出错: $_" -ForegroundColor Red
Write-Host "错误详情: $($_.Exception.Message)" -ForegroundColor Red
Write-Host "堆栈跟踪: $($_.ScriptStackTrace)" -ForegroundColor Red
return $false
}
}
# 主程序开始
try {
# 询问用户SVN工作目录
do {
$svnDir = Read-Host "请输入SVN工作目录路径"
if (-not (Test-Path $svnDir)) {
Write-Host "提供的SVN工作目录路径无效或不存在,请检查后重新输入。" -ForegroundColor Red
continue
}
# 检查是否在SVN工作副本中
if (-not (Test-SvnWorkingCopy $svnDir)) {
Write-Host "提供的目录不在任何SVN工作副本中,请检查后重新输入。" -ForegroundColor Red
continue
}
break # 如果验证通过,跳出循环
} while ($true)
# 切换到SVN工作目录
Push-Location $svnDir
# 获取所有文件夹和文件
Write-Host "`n正在扫描工作目录..."
Write-Host "正在获取所有文件夹..."
# 修改:按照目录深度排序文件夹
$allDirs = Get-ChildItem -Path $svnDir -Directory -Recurse |
Sort-Object @{Expression={($_.FullName -split '\\').Count}; Ascending=$true}
Write-Host "找到 $($allDirs.Count) 个文件夹"
Write-Host "正在获取所有文件..."
$allFiles = Get-ChildItem -Path $svnDir -File -Recurse
Write-Host "找到 $($allFiles.Count) 个文件"
# 第一阶段:处理所有文件夹
Write-Host "`n第一阶段:处理所有文件夹..."
$unversionedDirs = @()
foreach ($dir in $allDirs) {
Write-Host "检查文件夹: $($dir.FullName)" -ForegroundColor Gray
if (-not (Test-SvnPath $dir.FullName)) {
Write-Host " 未版本控制: $($dir.FullName)" -ForegroundColor Yellow
$unversionedDirs += $dir.FullName
}
}
# 提交所有未版本控制的文件夹
if ($unversionedDirs.Count -gt 0) {
Write-Host "`n正在添加未版本控制的文件夹..."
foreach ($dir in $unversionedDirs) {
Write-Host "正在添加文件夹: $dir"
if (Invoke-SvnCommand @("add", "--depth=empty", "--non-recursive", "`"$dir`"") -IgnoreError) {
# 立即提交每个文件夹
$commitMessage = "Add directory: $([System.IO.Path]::GetFileName($dir))"
# 修改:路径和消息都使用双引号
$dirPath = "`"$dir`""
$quotedMessage = "`"$commitMessage`""
Invoke-SvnCommand @("commit", $dirPath, "-m", $quotedMessage)
}
}
}
# 第二阶段:处理所有文件
Write-Host "`n第二阶段:处理所有文件..."
$unversionedFiles = @()
foreach ($file in $allFiles) {
Write-Host "检查文件: $($file.FullName)" -ForegroundColor Gray
if (-not (Test-SvnPath $file.FullName)) {
Write-Host " 未版本控制: $($file.FullName)" -ForegroundColor Yellow
$unversionedFiles += $file.FullName
}
}
# 添加所有未版本控制的文件
if ($unversionedFiles.Count -gt 0) {
Write-Host "`n正在添加未版本控制的文件..."
foreach ($file in $unversionedFiles) {
Write-Host "正在添加文件: $file"
Invoke-SvnCommand @("add", $file) -IgnoreError
}
}
# 获取所有需要提交的文件并批量提交
Write-Host "`n第三阶段:批量提交文件更改..."
$filesToCommit = @()
Write-Host "正在获取修改状态..."
$svnStatus = & $svnExecutable status
foreach ($line in $svnStatus) {
if ($line -match '^[AM]\s+(.+)$') {
$filesToCommit += $matches[1]
Write-Host " 待提交: $($matches[1])"
}
}
Write-Host "`n共有 $($filesToCommit.Count) 个文件需要提交"
# 批量提交文件
$batchSize = 50
$batchNumber = 1
$processedFiles = 0
for ($i = 0; $i -lt $filesToCommit.Count; $i += $batchSize) {
$batch = $filesToCommit[$i..([Math]::Min($i + $batchSize - 1, $filesToCommit.Count - 1))]
$processedFiles += $batch.Count
Submit-BatchFiles -Files $batch -BatchNumber $batchNumber -ProcessedFiles $processedFiles -TotalFiles $filesToCommit.Count
$batchNumber++
}
Write-Host "`n所有操作完成。共处理 $processedFiles 个文件。" -ForegroundColor Green
}
catch {
Write-Host "发生错误: $_" -ForegroundColor Red
Write-Host $_.ScriptStackTrace
}
finally {
# 恢复原始目录
Pop-Location
Read-Host "按回车键退出"
}
将PS脚本转换为EXE
为了让脚本更易于分发给其他同事或客户,选择了PS2EXE模块。安装方法如下:
Install-Module -Name PS2EXE
之后,使用ps2exe
cmdlet将.ps1
脚本转换为独立的.exe
文件:
- 构建脚本
# 设置版本和路径
$version = "1.0.0"
# 使用绝对路径
$scriptPath = $PSScriptRoot
$outputPath = Join-Path $scriptPath "dist"
$iconPath = Join-Path $scriptPath "icon.ico"
# 创建输出目录
New-Item -ItemType Directory -Force -Path $outputPath
# 转换为exe
$exeName = Join-Path $outputPath "SVN批量提交工具.exe"
Invoke-ps2exe `
-InputFile (Join-Path $scriptPath "batchSvnCommit.ps1") `
-OutputFile $exeName `
-NoConsole:$false `
-RequireAdmin:$false `
-Version $version `
-Title "SVN批量提交工具" `
-Company "Your Company" `
-Product "SVN Tools" `
-Copyright "Copyright 2024" `
-Description "SVN文件批量提交工具" `
-IconFile $iconPath
- 直接构建
ps2exe .\build.ps1
生成的可执行文件可以在没有安装PowerShell的环境中运行,提升了使用的便利性。
解决乱码问题
升级到PowerShell 7解决了输出中文字符时的乱码问题。PowerShell 7基于.NET Core构建,优化了对UTF-8编码的支持,确保无论是输出还是输入中文字符,都能正确显示。
使用PowerShell开发脚本程序进行批量SVN提交的更多相关文章
- 利用IntelliJ IDEA与Maven开发scala程序,并打包提交到spark集群
https://zhuanlan.zhihu.com/p/23141509 https://blog.csdn.net/u011470552/article/details/54564636 http ...
- Atitit.使用引擎加脚本架构的设计 使用php,js来开发桌面程序。。
Atitit.使用引擎加脚本架构的设计 使用php,js来开发桌面程序.. 1. 引擎加脚本架构 跨平台,桌面与web的优势1 2. 架构桌面引擎(java,c#)2 3. php桌面引擎要点2 3. ...
- 用RegularJS开发小程序 — mpregular解析
本文来自网易云社区. Mpregular 是基于 RegularJS(简称 Regular) 的小程序开发框架.开发者可以将直接用 RegularJS 开发小程序,或者将现有的 RegularJS 应 ...
- iOS开发人员程序许可协议
请细致阅读以下的许可协议条款和条件之前下载或使用苹果软件. 这些条款和条件构成你和苹果之间的法律协议. iOS开发人员程序许可协议 目的 你想使用苹果软件(例如以下定义)来开发一个或多个应 ...
- Genesis2000使用c#开发脚本
这是我自学程序以来在博客园的第一篇博客,如有不好的地方请大家指正,谢谢! 这边文章的目的是给予那些在PCB使用Genesis2000程序脚本开发的人员提供.net平台下的开发方法. 目前genesis ...
- 基于MINA框架快速开发网络应用程序
1.MINA框架简介 MINA(Multipurpose Infrastructure for Network Applications)是用于开发高性能和高可用性的网络应用程序的基础框架.通过使用M ...
- cygwin,在win中开发linux程序
cygwin,在win中开发linux程序 http://www.cygwin.cn/site/info/show.php?IID=1001 很多用windows的朋友不习惯于用linux的开发环境 ...
- 【前端工具】Chrome 扩展程序的开发与发布 -- 手把手教你开发扩展程序
关于 chrome 扩展的文章,很久之前也写过一篇.清除页面广告?身为前端,自己做一款简易的chrome扩展吧. 本篇文章重在分享一些制作扩展的过程中比较重要的知识及难点. 什么是 chrome 扩展 ...
- 【游戏开发】Excel表格批量转换成lua的转表工具
一.简介 在上篇博客<[游戏开发]Excel表格批量转换成CSV的小工具> 中,我们介绍了如何将策划提供的Excel表格转换为轻便的CSV文件供开发人员使用.实际在Unity开发中,很多游 ...
- 使用mpvue开发小程序教程(二)
在上篇文章中,我们介绍了使用mpvue开发小程序所需要的一些开发环境的搭建,并创建了第一个mpvue小程序代码骨架并将其运行起来.在本文中,我们来研究熟悉一下mpvue项目的主要目录和文件结构. 在V ...
随机推荐
- Linux iostat 命令详解
Linux iostat 命令详解 在Linux系统管理中,监控磁盘I/O性能是一项至关重要的任务.iostat是sysstat包中的一个实用工具,用于监控和显示系统输入输出设备和CPU的使用情况.它 ...
- SQL Server统计信息更新会被阻塞或引起会话阻塞吗?
在SQL Server数据库中,统计信息更新(UPDATE STATISTICS)会被其它会话阻塞吗?统计信息更新(UPDATE STATISTICS)会引起其它会话阻塞吗?在回答这两个问题前,我们必 ...
- 深入剖析Vue框架:从基础到未来趋势
深入剖析Vue框架:从基础到未来趋势 Vue 框架简介 Vue.js 是一款用于构建用户界面的 JavaScript 框架 ,它基于标准 HTML.CSS 和 JavaScript 构建,并提供了一套 ...
- 硅基流动最新邀请码:9MqV8tO4
硅基流动最新邀请码:9MqV8tO4 硅基流动最新邀请码:9MqV8tO4
- vue路由$router.push()的三种传参方式
- vue+elementUI当渲染文本超出一定字数时显示省略号
如图,当渲染的文字超出30字后显示省略号 1.设置过滤器 filters: { ellipsis(value) { if (!value) return ""; if (value ...
- DeepSeek-R1的“思考”艺术,你真的了解吗?
大家好~,这里是AI粉嫩特攻队!今天咱们来聊聊一个有趣的话题--DeepSeek-R1到底什么时候会"思考",什么时候又会选择"偷懒"? 最近有朋友问我:&qu ...
- Hive - [08] 数据仓库物理模型设计
分区 分区是将表的数据按照某个列的值进行划分和存储的一种方式.通过分区,可以将数据按照特定的维度进行组织,提高查询效率和数据管理的灵活性. 一.分区的优势 提高查询性能:通过分区,可以将数据按照特定的 ...
- Trae和Cursor小斗法
前情 自从AI IDE面世以来,网络上到处流传程序员要失业了,小白也能轻松完成程序开发了,某某0基础靠AI上架了苹果应用,平时工作也有偶尔用用AI工具的我,都觉得这些都是标题党文章不予理会的,直到看到 ...
- 洛谷P10112 [GESP202312 八级] 奖品分配 题解
题目传送门. 看了题解才发现我有多蠢. 我的做法真是唐完了. 在此之前请学习扩展欧几里得定理和扩展欧几里得定理求逆元. 发现奖品要么 \(N\) 个,要么 \(N+1\) 个,于是分类讨论,当奖品只有 ...