大小写不一致时如何获取Git仓库中创建时的原始文件名?
Windows/PowerShell与Git大小写冲突的解决方法
问题背景
Windows和PowerShell默认不区分文件名大小写,但Git严格区分大小写。当Git仓库中提交了a_TEST_file.txt这类大小写混合的文件后,若用户在PowerShell脚本中传入小写的a_test_file.txt,调用Git命令(如git log)会返回“文件未找到”,且Resolve-Path、Convert-Path这类PowerShell命令无法返回Git中存储的原始大小写文件名。
解决方法
不需要强制用户输入严格大小写的文件名,以下两种可靠方法可以获取正确的原始文件名:
方法1:通过Git命令直接获取仓库中的真实文件名
利用Git的git ls-files命令忽略大小写匹配仓库内的文件,直接返回Git存储的原始大小写文件名:
# 假设用户输入的文件路径为 $inputPath $inputPath = ".\a_test_file.txt" # 拆分路径为目录和文件名 $targetDir = Split-Path -Path $inputPath -Parent $inputFileName = Split-Path -Path $inputPath -Leaf # 在目标目录下执行git命令,忽略大小写匹配文件 $correctFileName = git -C $targetDir ls-files -i --exclude-standard -- $inputFileName # 拼接得到正确的文件路径 $correctFullPath = Join-Path -Path $targetDir -ChildPath $correctFileName
说明:git ls-files -i参数会忽略大小写匹配仓库中已追踪的文件,返回的结果就是Git中存储的原始大小写文件名。
方法2:调用Windows API获取磁盘上的真实文件名
PowerShell内置Cmdlet返回的文件名会被自动转换大小写,直接调用Windows内核API可以获取磁盘上实际存储的文件名:
Add-Type -TypeDefinition @" using System; using System.Runtime.InteropServices; public class FileSystemHelper { [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] public static extern bool GetFullPathName(string lpFileName, uint nBufferLength, [Out] StringBuilder lpBuffer, IntPtr lpFilePart); } "@ function Get-RealFileName { param([string]$Path) $sb = New-Object System.Text.StringBuilder(260) [FileSystemHelper]::GetFullPathName($Path, $sb.Capacity, $sb, [IntPtr]::Zero) $fullPath = $sb.ToString() $dir = Split-Path -Path $fullPath -Parent $inputName = Split-Path -Path $fullPath -Leaf # 遍历目录匹配文件,返回真实大小写文件名 foreach ($file in [System.IO.Directory]::GetFiles($dir, $inputName, [System.IO.SearchOption]::TopDirectoryOnly)) { return Split-Path -Path $file -Leaf } return $inputName } # 使用示例 $inputPath = ".\a_test_file.txt" $realFileName = Get-RealFileName -Path $inputPath $correctFullPath = Join-Path -Path (Split-Path $inputPath -Parent) -ChildPath $realFileName
说明:通过GetFullPathName获取完整路径后,遍历目录匹配文件,得到磁盘上实际存储的大小写文件名。
不推荐的方法:让Git忽略大小写
可以通过Git配置让其在Windows环境下忽略大小写,但这会导致跨平台同步时出现冲突,仅适合单一Windows环境使用:
git config core.ignorecase true
测试代码
New-Item -ItemType Directory -Path '.\a_test' | Out-Null Push-Location -Path '.\a_test' git init 'a test' | Set-Content -Path .\a_TEST_file.txt git add . git commit -m '提交测试文件' git log .\a_TEST_file.txt git log .\a_test_file.txt $null -eq $(git log .\a_test_file.txt) Get-ChildItem '.\a_TEST_file.txt' Get-ChildItem '.\a_test_file.txt' Get-ChildItem '.\a_test_file.txt' | Select-Object -Property Name,FullName,ResolvedTarget,PSPath Resolve-Path -Path '.\a_test_file.txt' Convert-Path -Path '.\a_test_file.txt' Pop-Location Remove-Item -Recurse -Force -Path '.\a_test'
命令执行结果
PS C:\src> New-Item -ItemType Directory -Path '.\a_test' | Out-Null PS C:\src> Push-Location -Path '.\a_test' PS C:\src\a_test> git init 已初始化空的 Git 仓库于 C:/src/a_test/.git/ PS C:\src\a_test> 'a test' | Set-Content -Path .\a_TEST_file.txt PS C:\src\a_test> git add . PS C:\src\a_test> git commit -m '提交测试文件' [main (根提交) 7fd198b] 提交测试文件 1 file changed, 1 insertion(+) create mode 100644 a_TEST_file.txt PS C:\src\a_test> git log .\a_TEST_file.txt commit 7fd198b556dd1dabf8c9a47369e71d424cf995bd (HEAD -> main) Author: lit <lit@myco.com> Date: Sat Nov 18 04:40:29 2023 -0600 提交测试文件 PS C:\src\a_test> git log .\a_test_file.txt PS C:\src\a_test> $null -eq $(git log .\a_test_file.txt) True PS C:\src\a_test> PS C:\src\a_test> Get-ChildItem '.\a_TEST_file.txt' 目录: C:\src\a_test Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2023-11-18 04:40 8 a_TEST_file.txt PS C:\src\a_test> Get-ChildItem '.\a_test_file.txt' 目录: C:\src\a_test Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2023-11-18 04:40 8 a_test_file.txt PS C:\src\a_test> Get-ChildItem '.\a_test_file.txt' | Select-Object -Property Name,FullName,ResolvedTarget,PSPath Name FullName ResolvedTarget PSPath ---- -------- -------------- ------ a_test_file.txt C:\src\a_test\a_test_file.txt C:\src\a_test\a_test_file.txt Microsoft.PowerShell.Core\FileSystem::C:\src\a_test\a_test_file.txt PS C:\src\a_test> Resolve-Path -Path '.\a_test_file.txt' Path ---- C:\src\a_test\a_test_file.txt PS C:\src\a_test> Convert-Path -Path '.\a_test_file.txt' C:\src\a_test\a_test_file.txt PS C:\src\a_test> PS C:\src\a_test> Pop-Location PS C:\src> Remove-Item -Recurse -Force -Path '.\a_test' PS C:\src>
内容的提问来源于stack exchange,提问作者lit
相关产品推荐
相关产品推荐

