1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

WinMergeで比較レポートを自動出力する

Last updated at Posted at 2025-05-22

概要

コード変更を行った際のエビデンスとしてWinMergeの比較レポートを使用して出力しているが、数十ファイルの変更があった際に、レポート出力するには手間と時間がかかるため自動出力したい。

実現方法の検討

WinMergeはコマンドラインを使って比較レポートを出力することができる。
比較元と比較先のフォルダを指定することで、フォルダ内のファイルを対象に比較レポートを作るツールを作る。

課題

変更ないものは比較レポートが不要。
比較方法はとりあえず fcコマンドで確認して不一致のものを対象にしてみる。
このためWinMergeで利用できる比較条件のオプションは指定できない。
※今のところ困っていないのでスルー

作成した比較レポートをExcelにまとめて出力できるようにもしたい。

サンプルコード

事前準備として、下記コードと同一フォルダに「empty.txt」名で空ファイルを用意する(新規ファイルとの比較用)。

import argparse
import os
import glob
import copy
# import openpyxl
import subprocess
from enum import Enum

# 定数定義
WINMERGE_PATH = r'"C:\Program Files\WinMerge\WinMergeU.exe"'
FILECOMPARE_PATH = f'fc'
EMPTY_FILE_PATH = r'.\empty.txt'


class ResultCode(Enum):
    """ 戻り値 """
    OK = 0
    NG = -1


class CommandParam():
    """ コマンドパラメータクラス """
    path_before: str
    path_after: str
    path_output: str


def main():
    """
    自動WinMerge比較レポート出力メイン処理
    """
    # コマンドラインチェック
    command_param = CommandParam()
    ret = check_commandline(command_param)

    # ファイル一覧を取得
    if ret == ResultCode.OK:
        # 変更後のファイル一覧を作成する
        # (削除されたファイルの比較レポートは不要、新規作成したファイルの比較レポートは必要)
        file_list = []
        ret = get_file_list(command_param.path_after, file_list)

    # WinMergeを使用して比較レポートを生成する
    if ret == ResultCode.OK:
        ret = run_winmerge(command_param.path_before,
                           command_param.path_after, command_param.path_output, file_list)


def check_commandline(param: CommandParam) -> ResultCode:
    """ 
    コマンドラインチェック

    Args:
        param (class)       : (out)コマンドパラメータ

    returns:
        ResultCode.OK       : 正常
        ResultCode.NG       : 引数エラー
    """
    result = ResultCode.OK

    # コマンドライン パラメータ
    parser = argparse.ArgumentParser(
        description='Aout WinMerge Tool', add_help=True)
    parser.add_argument('arg1', help='変更前のパス')
    parser.add_argument('arg2', help='変更後のパス')
    parser.add_argument('arg3', help='出力先のパス')
    args = parser.parse_args()

    # パスチェック
    # for path in args:
    #    if os.path.exists(path) == False:
    #        print(f'{path} が見つかりません')
    #        result = ResultCode.NG

    # パスチェック
    if os.path.exists(args.arg1) == False:
        print(f'{args.arg1} が見つかりません')
        result = ResultCode.NG

    # パスチェック
    if os.path.exists(args.arg2) == False:
        print(f'{args.arg2} が見つかりません')
        result = ResultCode.NG

    # パスチェック
    if os.path.exists(args.arg3) == False:
        print(f'{args.arg3} が見つかりません')
        result = ResultCode.NG

    if result == ResultCode.OK:
        param.path_before = args.arg1
        param.path_after = args.arg2
        param.path_output = args.arg3

    return result


def get_file_list(path_after: str,
                  file_list: list) -> ResultCode:
    """
    ファイル一覧を取得

    Args:
        path_after (str)    : (in) 変更後のパス
        file_list (list)    : (out)ファイル一覧

    returns:
        ResultCode.OK       : 正常
        ResultCode.NG       : 引数エラー
    """
    result = ResultCode.OK

    # ファイル一覧を作成する
    path_filter = os.path.join(path_after, "*.*")
    for path_and_filename in glob.glob(path_filter):
        # ファイル名のみリストに登録する
        file_list.append(os.path.split(path_and_filename)[1])

    return result


def run_winmerge(path_before: str,
                 path_after: str,
                 path_output: str,
                 file_list: list) -> ResultCode:
    """
    WinMergeを実行する

    Args:
        path_before (str)   : (in) 変更前のパス
        path_after (str)    : (in) 変更後のパス
        path_output (str)   : (in) 出力先のパス
        file_list (list)    : (out)ファイル一覧

    returns:
        ResultCode.OK       : 正常
        ResultCode.NG       : 引数エラー
    """
    result: ResultCode = ResultCode.OK

    for filename in file_list:
        # 比較元・比較先のファイルパス作成
        filepath_before = os.path.join(path_before, filename)
        filepath_after = os.path.join(path_after, filename)
        filepath_output = os.path.join(path_output, filename + '.html')

        # 比較元に比較先と同名のファイルがあるかチェック
        if os.path.isfile(filepath_before) == False:
            # ファイルがない(= 新規作成のファイル)場合、空ファイルを指定する
            filepath_before = EMPTY_FILE_PATH

        # ファイル比較して不一致なら比較レポートを生成する
        ret = file_compare(filepath_before, filepath_after)
        if ret == False:
            # WinMergeを起動して、比較レポートを生成する
            command = f"{WINMERGE_PATH} {filepath_before} {filepath_after} " \
                f"/noninteractive /u /or {filepath_output}"
            ret = subprocess.run(command, shell=True)

    return result


def file_compare(path1: str,
                 path2: str) -> bool:
    """
    ファイル比較

    Args:
        path1 (str)         : (in) 比較するファイルパス(1)
        path2 (str)         : (in) 比較するファイルパス(2)

    returns:
        True                : 一致
        False               : 不一致
    """
    result = True

    # ファイル比較
    command = f"{FILECOMPARE_PATH} {path1} {path2}"
    ret = subprocess.run(command, capture_output=True, text=True, shell=True)
    lines = ret.stdout.splitlines()
    if lines[1] != 'FC: 相違点は検出されませんでした':
        result = False

    return result


if __name__ == "__main__":
    main()
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?