返回

TFS提交前获取变更文件列表并添加到注释 (tf.exe)

windows

如何在 TFS 提交前获取变更文件列表 (tf.exe)

有时我们需要用批处理脚本自动化提交 TFS (Team Foundation Server) 代码。 一个常见需求是在提交注释中包含已修改的文件列表。本文将探讨这个问题,提供一些解决思路。

问题

我有一个批处理文件,用于提交一个目录下的变更文件。 命令通常如下:

tf.exe checkin /comment:"Update list of common files" /noprompt /recursive ./common

我想把这些变更的文件列表添加到提交注释里——该怎么做呢?不管是添加、删除还是更新文件,我都需要一个变更的文件列表。

例如,当我添加两个文件 A.txt 和 B.txt 时,我希望看到注释:“Update: A.txt, B.txt”。

问题原因分析

tf.exe checkin 命令本身不直接提供将文件列表自动添加到注释中的功能。 我们需要借助其他 tf.exe 命令或者 PowerShell 来预先获取变更列表,然后构造包含此列表的注释。

解决方案

以下是几种可行的解决方案,从简单到复杂,各有优劣。

方案一:tf.exe status + 文本处理

原理:

tf.exe status 命令可以列出本地工作区中所有挂起的更改(pending changes)。我们可以通过解析这个命令的输出来提取文件名。

代码示例 (Batch):

@echo off
setlocal

set "CHANGED_FILES="
set "COMMENT=Update: "

:: 获取变更文件列表
for /f "tokens=2 delims=
@echo off
setlocal

set "CHANGED_FILES="
set "COMMENT=Update: "

:: 获取变更文件列表
for /f "tokens=2 delims=$" %%a in ('tf status /recursive /format:brief ./common ^| findstr /v /b /c:"$"') do (
    for /f "tokens=1,2 delims=: " %%b in ("%%a") do (
      set "CHANGED_FILES=!CHANGED_FILES!%%c, "
    )    
)

:: 移除最后的 ", "
if defined CHANGED_FILES set "CHANGED_FILES=%CHANGED_FILES:~0,-2%"

:: 如果有变更,则提交
if defined CHANGED_FILES (
    set "COMMENT=%COMMENT%%CHANGED_FILES%"
    tf checkin /comment:"%COMMENT%" /noprompt /recursive ./common
    echo Changes checked in with comment: "%COMMENT%"
) else (
    echo No changes detected.
)

endlocal
quot;
%%a in ('tf status /recursive /format:brief ./common ^| findstr /v /b /c:"
@echo off
setlocal

set "CHANGED_FILES="
set "COMMENT=Update: "

:: 获取变更文件列表
for /f "tokens=2 delims=$" %%a in ('tf status /recursive /format:brief ./common ^| findstr /v /b /c:"$"') do (
    for /f "tokens=1,2 delims=: " %%b in ("%%a") do (
      set "CHANGED_FILES=!CHANGED_FILES!%%c, "
    )    
)

:: 移除最后的 ", "
if defined CHANGED_FILES set "CHANGED_FILES=%CHANGED_FILES:~0,-2%"

:: 如果有变更,则提交
if defined CHANGED_FILES (
    set "COMMENT=%COMMENT%%CHANGED_FILES%"
    tf checkin /comment:"%COMMENT%" /noprompt /recursive ./common
    echo Changes checked in with comment: "%COMMENT%"
) else (
    echo No changes detected.
)

endlocal
quot;'
) do ( for /f "tokens=1,2 delims=: " %%b in ("%%a") do ( set "CHANGED_FILES=!CHANGED_FILES!%%c, " ) ) :: 移除最后的 ", " if defined CHANGED_FILES set "CHANGED_FILES=%CHANGED_FILES:~0,-2%" :: 如果有变更,则提交 if defined CHANGED_FILES ( set "COMMENT=%COMMENT%%CHANGED_FILES%" tf checkin /comment:"%COMMENT%" /noprompt /recursive ./common echo Changes checked in with comment: "%COMMENT%" ) else ( echo No changes detected. ) endlocal

代码解释:

  1. tf status /recursive /format:brief ./common: 获取./common目录下所有挂起的更改, 简洁格式.
  2. findstr /v /b /c:"$": 排除 tf status可能存在的以$开始的行,这是本地项的根目录行。
  3. for /f "tokens=2 delims=$" %%a in (...): 逐行处理 tf status 输出, 以 $ 为分隔符, 取第二部分(即包含文件更改类型和路径的部分)。
  4. for /f "tokens=1,2 delims=: " %%b in ("%%a"): 对得到的每一行, 使用: 进行分割, 第一列是更改类型, 第二列是文件相对路径,我们只需要相对路径.
  5. set "CHANGED_FILES=!CHANGED_FILES!%%c, " : 拼接文件路径到 CHANGED_FILES 变量。
  6. %CHANGED_FILES:~0,-2%: 去除末尾的 ", "。
  7. 构建 COMMENT 字符串, 然后执行 tf checkin.

进阶技巧:

可以添加更多错误处理,比如检查 tf status 命令是否成功执行。

安全性建议:

确保批处理文件只在可信的本地环境中运行。

方案二: 使用 PowerShell

原理:

PowerShell 提供了更强大的文本处理和对象操作能力。可以使用 tf.exe 的输出,转换为对象后,再进行处理。

代码示例 (PowerShell):

# 获取变更的文件
$changes = tf status /recursive /format:detailed ./common |  Where-Object {$_ -match "^\s*(add|delete|edit|rename|merge)\s*,.*"}

if ($changes) {
  # 提取文件名
  $fileNames = $changes | ForEach-Object {
        $filePath=$_.split(',')[1].trim() -replace '\
# 获取变更的文件
$changes = tf status /recursive /format:detailed ./common |  Where-Object {$_ -match "^\s*(add|delete|edit|rename|merge)\s*,.*"}

if ($changes) {
  # 提取文件名
  $fileNames = $changes | ForEach-Object {
        $filePath=$_.split(',')[1].trim() -replace '\$', ''
       if ($filePath)
       {
          $filePath
       }

    }
  $comment = "Update: " + ($fileNames -join ", ")

  # 执行提交
  tf checkin /comment:"$comment" /noprompt /recursive ./common
  Write-Host "Changes checked in with comment: $comment"
}
else {
  Write-Host "No changes detected."
}
#x27;
, '' if ($filePath) { $filePath } } $comment = "Update: " + ($fileNames -join ", ") # 执行提交 tf checkin /comment:"$comment" /noprompt /recursive ./common Write-Host "Changes checked in with comment: $comment" } else { Write-Host "No changes detected." }

代码解释:

  1. tf status /recursive /format:detailed ./common: 获取详细的变更信息。
  2. Where-Object {$_ -match "^\s*(add|delete|edit|rename|merge)\s*,.*"}:筛选出表示添加、删除、编辑、重命名或合并的行。
  3. $changes | ForEach-Object { ... }: 遍历每一个变更。
  4. $filePath=$_.split(',')[1].trim() -replace '\$', '': 字符串处理, 获取文件路径。先根据 ,分割,取第二部分(路径), 清除可能存在的路径开头和结尾空格,再把可能存在的$替换为空字符串.
  5. $fileNames -join ", ": 将文件名用 ", " 连接起来。
  6. 构建$comment字符串,然后执行 tf checkin

进阶技巧:

可以更精细地处理文件名,比如提取文件的绝对路径,或对路径进行规范化。

安全性建议:

与批处理脚本类似,确保 PowerShell 脚本也在可信的环境中运行。

方案三: 使用 tf diff (只适用于已修改的文件)

原理:

如果你的需求 针对已修改(edit)的文件,可以使用tf diff, 它的输出可以列出差异。但是, 它不会列出新增或删除的文件。

代码示例(Batch):

@echo off
setlocal

set "CHANGED_FILES="
set "COMMENT=Update: "
FOR /F "delims=" %%a in ('tf diff /recursive /format:brief ./common ^| findstr /V "^========="') do (

        SET "CHANGED_FILES=!CHANGED_FILES!%%a, "

)
:: 移除最后的 ", "
if defined CHANGED_FILES set "CHANGED_FILES=%CHANGED_FILES:~0,-2%"

:: 如果有变更,则提交
if defined CHANGED_FILES (
    set "COMMENT=%COMMENT%%CHANGED_FILES%"
    tf checkin /comment:"%COMMENT%" /noprompt /recursive ./common
    echo Changes checked in with comment: "%COMMENT%"
) else (
    echo No changes detected.
)
endlocal

代码解释:

  1. tf diff /recursive /format:brief ./common: 比较本地文件和服务器版本,简明输出.
  2. findstr /V "^=========": 排除tf diff 输出的标题行.
  3. FOR /F "delims=" %%a in (...): 逐行遍历输出, 并拼接.
  4. 构建注释, 然后提交.

限制: 这种方法只能用于已修改的文件。新增和删除的文件无法通过这个命令获得.

方案比较和选择

方案 优点 缺点 适用场景
tf status 可以获取所有类型的变更 (add, delete, edit) 需要处理文本输出 通用,大多数情况
PowerShell 更强大,更易于处理 需要 PowerShell 环境,脚本稍复杂 需要复杂逻辑或大量文本处理
tf diff 简单 仅适用于已修改的文件,无法获取新增/删除文件 只关心已修改的文件

建议优先使用 tf status 或 PowerShell 方案,因为它们更通用,能处理所有类型的变更。 如果只关心修改的文件,tf diff 可以作为一种更简单的方法。

根据自己项目的实际需求和环境选择合适的方案。 如果批处理脚本已足够用, 那就继续用批处理. 如果你需要更多的灵活性和扩展性, 那就上 PowerShell。