From 3a8fcf143e50be734ea11f4f5ad32853a55e8d93 Mon Sep 17 00:00:00 2001 From: Tom CHEN Date: Wed, 1 Apr 2020 11:57:37 +0200 Subject: [PATCH] v3.1 diff-add-keep etc --- MMArchCompare.pas | 20 +++- MMArchPath.pas | 111 +++++++++++++----- README.md | 54 ++++----- mmarch.bdsproj | 6 +- mmarch.dpr | 50 ++++---- mmarch.res | Bin 23428 -> 23428 bytes test/compare/test_compare.bat | 12 +- test/compare/test_compare_3vermerger.bat | 4 +- .../compare_mmmerge_realworldtest.bat | 4 +- tutorial/README.md | 14 ++- 10 files changed, 168 insertions(+), 107 deletions(-) diff --git a/MMArchCompare.pas b/MMArchCompare.pas index 8008d1e..479d3b8 100644 --- a/MMArchCompare.pas +++ b/MMArchCompare.pas @@ -261,6 +261,7 @@ procedure generateScript(deletedFolderList, deletedNonResFileList, deletedResFil var scriptString, elTemp, strTemp: string; tslTemp: TStringList; + noRes: boolean; begin @@ -269,6 +270,8 @@ procedure generateScript(deletedFolderList, deletedNonResFileList, deletedResFil deletedResFileList.Sort; modifiedArchiveList.Sort; + noRes := (deletedResFileList.Count = 0) and (modifiedArchiveList.Count = 0); + if isNsis then // NSIS begin @@ -314,9 +317,12 @@ procedure generateScript(deletedFolderList, deletedNonResFileList, deletedResFil #13#10 + ';-----FILE COPYING (MODIFYING, DELETING) STARTS HERE-----'#13#10 + #13#10 + - ' SetOutPath $INSTDIR'#13#10 + - ' File mmarch.exe'#13#10 + - #13#10; + ' SetOutPath $INSTDIR'#13#10; + + if not noRes then + scriptString := scriptString + ' File mmarch.exe'#13#10; + + scriptString := scriptString + #13#10; if deletedFolderList.Count > 0 then begin @@ -327,7 +333,7 @@ procedure generateScript(deletedFolderList, deletedNonResFileList, deletedResFil scriptString := scriptString + #13#10; end; - scriptString := scriptString +' File /r /x *' + ToDeleteExt + ' ' + withTrailingSlash(beautifyPath(diffFileFolderName)) + '*.*'#13#10 + + scriptString := scriptString +' File /r /x *' + ToDeleteExt + ' /x *' + EmptyFolderKeep + ' ' + withTrailingSlash(beautifyPath(diffFileFolderName)) + '*.*'#13#10 + #13#10; if deletedNonResFileList.Count > 0 then @@ -364,8 +370,10 @@ procedure generateScript(deletedFolderList, deletedNonResFileList, deletedResFil scriptString := scriptString + #13#10; end; + if not noRes then + scriptString := scriptString + ' Delete "mmarch.exe"'#13#10; + scriptString := scriptString + - ' Delete "mmarch.exe"'#13#10 + #13#10 + ';-----FILE COPYING (MODIFYING, DELETING) ENDS HERE-----'#13#10 + #13#10 + @@ -385,7 +393,7 @@ procedure generateScript(deletedFolderList, deletedNonResFileList, deletedResFil scriptString := scriptString + #13#10; scriptString := scriptString + - 'echo ' + ToDeleteExt + '>excludelist.txt'#13#10 + + '(echo ' + ToDeleteExt + ' && echo ' + EmptyFolderKeep + ')>excludelist.txt'#13#10 + 'Xcopy files . /s /e /y /EXCLUDE:excludelist.txt'#13#10 + 'del excludelist.txt'#13#10 + #13#10; diff --git a/MMArchPath.pas b/MMArchPath.pas index 50cffd4..19bc90e 100644 --- a/MMArchPath.pas +++ b/MMArchPath.pas @@ -25,6 +25,7 @@ function getAllFilesInFolder(path: string; ext: string = '*'; isDir: boolean = f procedure addAllFilesToFileList(var fileList: TStringList; path: string; recursive: integer; isDir: boolean; usePathFilenamePair: boolean; extList: TStringList); overload; procedure addAllFilesToFileList(var fileList: TStringList; path: string; recursive: integer; isDir: boolean; usePathFilenamePair: boolean); overload; +procedure addKeepToAllEmptyFoldersRecur(folder: string); function beautifyPath(oldStr: String): string; function withTrailingSlash(path: string): string; @@ -33,6 +34,7 @@ procedure createDirRecur(dir: string); procedure copyFile0(oldFile, newFile: string); function createEmptyFile(filePath: string): boolean; procedure StrToFile(filePath, SourceString: string); +function isSubfolder(folder, potentialSubfolder: string): boolean; function moveDir(folderFrom, folderTo: string): Boolean; procedure delDir(folder: string); @@ -40,6 +42,8 @@ procedure delDir(folder: string); nameValSeparator: char = ':'; archResSeparator: char = ':'; + EmptyFolderKeep: string = '.mmarchkeep'; + supportedExts: array[0..7] of string = ('.lod', '.pac', '.snd', '.vid', '.lwd', '.mm7', '.dod', '.mm6'); {$IFDEF MSWINDOWS} @@ -219,18 +223,24 @@ function getAllFilesInFolder(path: string; ext: string = '*'; isDir: boolean = f if findfirst(fileMask, attr, searchResult) = 0 then begin repeat - if ( ( - (not isDir) and - FileExists(searchResult.Name) - ) or ( - isDir and - ( (searchResult.attr and faDirectory) = faDirectory ) and - (searchResult.Name <> '.') and - (searchResult.Name <> '..') - ) ) and ( + if ( + ( + (not isDir) and + FileExists(searchResult.Name) + ) or ( + isDir and + ( (searchResult.attr and faDirectory) = faDirectory ) and + (searchResult.Name <> '.') and + (searchResult.Name <> '..') + ) + ) + and + ( (fileMask <> '*.') or ( (fileMask = '*.') and (ExtractFileExt(searchResult.Name) = '') ) ) + and + (searchResult.Name <> EmptyFolderKeep) // special: ignore all .mmarchkeep file then Result.Add(searchResult.Name); until FindNext(searchResult) <> 0; @@ -247,10 +257,10 @@ procedure addAllFilesToFileListNonRecur(var fileList: TStringList; path: string; begin for ext in extList do for fileName in getAllFilesInFolder(path, ext, isDir) do - if usePathFilenamePair then - fileList.Add(fileName + nameValSeparator + beautifyPath(path)) - else - fileList.Add(withTrailingSlash(beautifyPath(path)) + fileName); + if usePathFilenamePair then + fileList.Add(fileName + nameValSeparator + beautifyPath(path)) + else + fileList.Add(withTrailingSlash(beautifyPath(path)) + fileName); end; @@ -261,7 +271,10 @@ procedure addAllFilesToFileListNonRecur(var fileList: TStringList; path: string; begin extList := TStringList.Create; extList.Add('*'); + addAllFilesToFileListNonRecur(fileList, path, isDir, usePathFilenamePair, extList); + + extList.Free; end; @@ -307,6 +320,29 @@ procedure addAllFilesToFileList(var fileList: TStringList; path: string; recursi extList.Add('*'); addAllFilesToFileList(fileList, path, recursive, isDir, usePathFilenamePair, extList); + + extList.Free; +end; + + +procedure addKeepToAllEmptyFoldersRecur(folder: string); +var + allFolders: TStringList; + elTemp: string; + +begin + folder := trimCharsRight(beautifyPath(folder), '\', '/'); + + allFolders := TStringList.Create; + allFolders.Add(folder); + addAllFilesToFileList(allFolders, folder, 1, true, false); + + for elTemp in allFolders do + if (getAllFilesInFolder(elTemp, '*', true).Count = 0) + and (getAllFilesInFolder(elTemp, '*', false).Count = 0) then // folder is empty (but maybe contain .mmarchkeep) + createEmptyFile(withTrailingSlash(elTemp) + EmptyFolderKeep); + + allFolders.Free; end; @@ -415,6 +451,20 @@ procedure StrToFile(filePath, SourceString: string); end; +function isSubfolder(folder, potentialSubfolder: string): boolean; +var + parentFolder, lastParentFolder: string; +begin + parentFolder := trimCharsRight(beautifyPath(potentialSubfolder), '\', '/'); + folder := trimCharsRight(beautifyPath(folder), '\', '/'); + repeat + lastParentFolder := parentFolder; + parentFolder := trimCharsRight(ExtractFilePath(parentFolder), '\', '/'); + Result := (parentFolder = folder); + until (parentFolder = lastParentFolder) or (parentFolder = '') or Result; +end; + + function moveDir(folderFrom, folderTo: string): Boolean; // folderFrom, folderTo cannot be current folder or ancestor folder var @@ -423,24 +473,29 @@ function moveDir(folderFrom, folderTo: string): Boolean; folderFrom := trimCharsRight(beautifyPath(folderFrom), '\', '/'); folderTo := trimCharsRight(beautifyPath(folderTo), '\', '/'); - if folderFrom <> folderTo then + if isSubfolder(folderFrom, folderTo) then begin - createDirRecur(ExtractFilePath(folderTo)); - ZeroMemory(@fos, SizeOf(fos)); - with fos do - begin - wFunc := FO_MOVE; - fFlags := FOF_FILESONLY; - pFrom := PChar(folderFrom + #0); - pTo := PChar(folderTo); - end; - Result := (0 = ShFileOperation(fos)); + Result := moveDir(folderFrom, folderFrom + '.mmarchtmp'); + Result := Result and moveDir(folderFrom + '.mmarchtmp', folderTo); end else - begin - Result := false; - end; - + if folderFrom <> folderTo then + begin + createDirRecur(ExtractFilePath(folderTo)); + ZeroMemory(@fos, SizeOf(fos)); + with fos do + begin + wFunc := FO_MOVE; + fFlags := FOF_FILESONLY; + pFrom := PChar(folderFrom + #0); + pTo := PChar(folderTo); + end; + Result := (0 = ShFileOperation(fos)); + end + else + begin + Result := false; + end; end; diff --git a/README.md b/README.md index 14b6b73..a281922 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ Command line tool to handle (extract, replace, compare resources and more) Heroes of Might and Magic 3 and Might and Magic 6, 7, 8 resource archive files (e.g. lod files) for Windows. -[Download mmarch v3.0](https://github.com/might-and-magic/mmarch/releases/download/v3.0/mmarch.zip) +[Download mmarch v3.1](https://github.com/might-and-magic/mmarch/releases/download/v3.1/mmarch.zip) -Based on [GrayFace's MMArchive](https://grayface.github.io/mm/#MMArchive) ([repo](https://github.com/GrayFace/Misc/)) (mmarch is actually kind of a wrapper of MMArchive). If you need a graphical user interface tool, use MMArchive. +Based on [GrayFace's MMArchive](https://grayface.github.io/mm/#MMArchive) ([repo](https://github.com/GrayFace/Misc/)). If you need a graphical user interface tool, use MMArchive. ## Summary & Table of Contents @@ -186,6 +186,8 @@ mmarch merge events.lod events2.lod Compare two archive files, or two folders containing archive files and/or files of any other type. +`mmarch compare` and all related commands and features (incl. NSIS/batch script generation) work totally even if your folders do not contain any MM archive files at all. Therefore, you can use **mmarch** as a general file comparison and diff generation tool. + (`k` is short for `compare`) The fourth parameter, `[OPTION]`, can be: @@ -232,20 +234,28 @@ Copy all diff files (i.e. non-resource file and extract in-archive resource file Note that if `DIFF_FOLDER` exsits, it will perform a merger of old diff files and new diff files by cleaning up old diff files. Therefore, you can do: `mmarch compare VERSION_1 VERSION_2 filesonly diff_folder` and then `mmarch compare VERSION_2 VERSION_3 filesonly diff_folder`. It's OK to do VER1 -> VER2 then VER2 -> VER3, or VER1 -> VER3 then VER2 -> VER3. But VER1 -> VER2 then VER1 -> VER3 will cause problem ([image demo](tutorial/img/multi_version.png)). This merger (cleanup) is only performed in `filesonly` command, and not in `nsis` or `batch`. -### `compare-files-to-nsis`/`-batch` +### `diff-files-to-nsis`/`-batch`, `diff-add-keep` -There are also two special commands: +There are also 3 special commands related to `compare`: ``` -mmarch compare-files-to-nsis -or -mmarch compare-files-to-batch +mmarch diff-files-to-nsis +OR +mmarch diff-files-to-batch ``` -(`cf2n` is short for `compare-files-to-nsis`; `cf2b` is short for `compare-files-to-batch`) +(`df2n` is short for `diff-files-to-nsis`; `df2b` is short for `diff-files-to-batch`) The former command generates a .nsi script file, while the later command generates a .bat (Window Batch) file `SCRIPT_FILE`, according to the files in `[OLD_DIFF_FOLDER]` that you get using `filesonly` option of `mmarch compare`. `[OLD_DIFF_FOLDER]` will then be moved to `SCRIPT_FILE`'s folder (becoming its subfolder) and renamed with `DIFF_FOLDER_NAME`. +``` +mmarch diff-add-keep +``` + +(`dak` is short for `diff-add-keep`) + +This is for developers using Git. It will add an empty file `.mmarchkeep` to every empty folder in `DIFF_FOLDER`, so that Git can keep the empty folders tracked. `diff-files-to-nsis`/`-batch` ignore `.mmarchkeep` files. + **`compare` mixed examples:** ``` mmarch compare game_folder_old game_folder_new nsis nsis_folder/script.nsi files @@ -255,7 +265,7 @@ mmarch compare game_folder_old game_folder_new nsis nsis_folder/script.nsi files ``` mmarch compare game_folder_old game_folder_new filesonly diff_folder_temp -mmarch compare-files-to-nsis diff_folder_temp nsis_folder/script.nsi files +mmarch diff-files-to-nsis diff_folder_temp nsis_folder/script.nsi files ``` ## `optimize` @@ -455,31 +465,6 @@ A user may: The batch file will not perform a self-deletion, users have to delete .bat, mmarch.exe and `DIFF_FOLDER` manually. -### Other Batch scripts - -Simple demostration of some non-straightforward, advanced usages of batch file: - -Save the resource list (one file name per line) in an archive as a txt file: - -``` -mmarch list events.lod> events_temp_list.txt -``` - -Save the resource list in an archive as a txt file, with `|` as leading, trailing character and separators. Then search to see if `D17.STR` file exists in the archive: - -``` -@echo|set /p="|"> events_temp_list.txt -mmarch list events.loD "|">> events_temp_list.txt -@echo|set /p="|">> events_temp_list.txt - -findstr "|D17.STR|" events_temp_list.txt -IF %errorlevel% == 0 ( - echo Found! -) ELSE ( - echo Not found! -) -``` - ## Compilation How to compile the source of **mmarch**: @@ -494,6 +479,7 @@ How to compile the source of **mmarch**: * [2020-03-11] v1.0: initial release * [2020-03-18] v2.0: support palette; support `*.EXT` and batch archive extraction; deal with in-archive & extracted file extension differences and the "cannot find the path specified" problem caused by it * [2020-03-31] v3.0: add `compare` method that can compare two dir and generate NSIS/Batch installer; tutorial +* [2020-04-02] v3.1: diff-files-to-* instead of compare-files-to-*; diff-add-keep; fix problem moving to subfolder ## License diff --git a/mmarch.bdsproj b/mmarch.bdsproj index d1f9631..0c364af 100644 --- a/mmarch.bdsproj +++ b/mmarch.bdsproj @@ -143,7 +143,7 @@ True False 3 - 0 + 1 0 0 False @@ -157,13 +157,13 @@ Tom CHEN Command line tool to handle Heroes 3 & Might and Magic 678 archive - 3.0.0.0 + 3.1.0.0 MIT License (c) 2020 Tom CHEN mmarch - 3.0.0.0 + 3.1.0.0 diff --git a/mmarch.dpr b/mmarch.dpr index 350151c..1c7e9e9 100644 --- a/mmarch.dpr +++ b/mmarch.dpr @@ -20,7 +20,7 @@ type MissingParamException = class(Exception); const - MMARCHVERSION: string = '3.0'; + MMARCHVERSION: string = '3.1'; MMARCHURL: string = 'https://github.com/might-and-magic/mmarch'; var @@ -39,7 +39,7 @@ resourcestring NeedArchiveFile = 'You must specify an archive file'; UnknownMethod = 'Unknown method: %s'; UnknownCompareOption = 'Unknown compare option: %s'; - OldDiffFolderEmpty = 'OLD_DIFF_FOLDER is empty'; + OldDiffFolderEmpty = 'Folder %s is empty'; HELPSTR_FirstLine = 'mmarch Version %s Usage:'; HELPSTR_ReqOpt = '(`%s`: required; `%s`: optional; `%s`: or):'; @@ -81,7 +81,7 @@ resourcestring HELPPRM_DIFF_FOLDER = 'DIFF_FOLDER'; HELPPRM_DIFF_FOLDER_NAME = 'DIFF_FOLDER_NAME'; HELPPRM_OLD_DIFF_FOLDER = 'OLD_DIFF_FOLDER'; - HELPPRM_SCRIPT_FOLDER = 'SCRIPT_FOLDER'; + HELPPRM_SCRIPT_FILE = 'SCRIPT_FILE'; HELPPRM_FILE_TO_XX_X = 'FILE_TO_XX_?'; @@ -97,9 +97,10 @@ begin WriteLn('mmarch create <' + HELPPRM_ARCHIVE_FILE + '> <' + HELPPRM_ARCHIVE_FILE_TYPE + '> <' + HELPPRM_FOLDER + '> [' + HELPPRM_FILE_TO_ADD_1 + '] [' + HELPPRM_FILE_TO_ADD_2 + '] [...]'); WriteLn('mmarch merge <' + HELPPRM_ARCHIVE_FILE + '> <' + HELPPRM_ARCHIVE_FILE_2 + '>'); WriteLn('mmarch compare <' + HELPPRM_ARCHIVE_FILE_OR_FOLDER + '> <' + HELPPRM_ARCHIVE_FILE_OR_FOLDER_2 + '>'); - WriteLn('mmarch compare <' + HELPPRM_ARCHIVE_FILE_OR_FOLDER + '> <' + HELPPRM_ARCHIVE_FILE_OR_FOLDER_2 + '> {nsis|batch} <' + HELPPRM_SCRIPT_FOLDER + '> <' + HELPPRM_DIFF_FOLDER_NAME + '>'); + WriteLn('mmarch compare <' + HELPPRM_ARCHIVE_FILE_OR_FOLDER + '> <' + HELPPRM_ARCHIVE_FILE_OR_FOLDER_2 + '> {nsis|batch} <' + HELPPRM_SCRIPT_FILE + '> <' + HELPPRM_DIFF_FOLDER_NAME + '>'); WriteLn('mmarch compare <' + HELPPRM_ARCHIVE_FILE_OR_FOLDER + '> <' + HELPPRM_ARCHIVE_FILE_OR_FOLDER_2 + '> filesonly <' + HELPPRM_DIFF_FOLDER + '>'); - WriteLn('mmarch compare-files-to-{nsis|batch} <' + HELPPRM_OLD_DIFF_FOLDER + '> <' + HELPPRM_SCRIPT_FOLDER + '> <' + HELPPRM_DIFF_FOLDER_NAME + '>'); + WriteLn('mmarch diff-files-to-{nsis|batch} <' + HELPPRM_OLD_DIFF_FOLDER + '> <' + HELPPRM_SCRIPT_FILE + '> <' + HELPPRM_DIFF_FOLDER_NAME + '>'); + WriteLn('mmarch diff-add-keep <' + HELPPRM_DIFF_FOLDER + '>'); WriteLn('mmarch optimize <' + HELPPRM_ARCHIVE_FILE + '>'); WriteLn('mmarch help'); @@ -378,10 +379,11 @@ begin end; -procedure compareFilesToAny(isNsis: boolean); +procedure diffFilesToAny(isNsis: boolean); var oldDiffFileFolder, scriptFilePath, diffFileFolderName, scriptFileFolder: string; deletedFolderList, deletedNonResFileList, deletedResFileList, modifiedArchiveList: TStringList; + moved: boolean; begin oldDiffFileFolder := ParamStr(2); @@ -396,7 +398,7 @@ begin (getAllFilesInFolder(oldDiffFileFolder, '*', false).Count = 0) and (getAllFilesInFolder(oldDiffFileFolder, '*', true).Count = 0) ) then - WriteLn(OldDiffFolderEmpty) + WriteLn(format(OldDiffFolderEmpty, [oldDiffFileFolder])) else begin @@ -410,9 +412,9 @@ begin getListFromDiffFiles(oldDiffFileFolder, deletedFolderList, deletedNonResFileList, deletedResFileList, modifiedArchiveList); - generateScript(deletedFolderList, deletedNonResFileList, deletedResFileList, modifiedArchiveList, scriptFilePath, diffFileFolderName, isNsis); - - moveDir(oldDiffFileFolder, withTrailingSlash(scriptFileFolder) + beautifyPath(diffFileFolderName)); + moved := moveDir(oldDiffFileFolder, withTrailingSlash(scriptFileFolder) + beautifyPath(diffFileFolderName)); + if moved then + generateScript(deletedFolderList, deletedNonResFileList, deletedResFileList, modifiedArchiveList, scriptFilePath, diffFileFolderName, isNsis); deletedFolderList.Free; deletedNonResFileList.Free; @@ -424,15 +426,21 @@ begin end; -procedure compareFilesToNsis; +procedure diffFilesToNsis; +begin + diffFilesToAny(true); +end; + + +procedure diffFilesToBatch; begin - compareFilesToAny(true); + diffFilesToAny(false); end; -procedure compareFilesToBatch; +procedure diffAddKeep; begin - compareFilesToAny(false); + addKeepToAllEmptyFoldersRecur(archiveFile); // archiveFile here is a folder path end; @@ -450,9 +458,7 @@ begin same := compareBase(oldArchiveOrFolder, newArchiveOrFolder, withTrailingSlash(ExtractFilePath(scriptFilePath)) + diffFileFolderName, deletedFolderList, deletedNonResFileList, deletedResFileList, modifiedArchiveList); if not same then - begin generateScript(deletedFolderList, deletedNonResFileList, deletedResFileList, modifiedArchiveList, scriptFilePath, diffFileFolderName, isNsis); - end; deletedFolderList.Free; deletedNonResFileList.Free; @@ -531,11 +537,12 @@ begin methodNumber := AnsiIndexStr(method, ['extract', 'e', 'list', 'l', 'add', 'a', 'delete', 'd', 'rename', 'r', 'create', 'c', 'merge', 'm', 'compare', 'k', 'optimize', 'o', - 'compare-files-to-nsis', 'cf2n', 'compare-files-to-batch', 'cf2b', + 'diff-files-to-nsis', 'df2n', 'diff-files-to-batch', 'df2b', 'diff-add-keep', 'dak', 'help', 'h', '']); archiveFile := ParamStr(2); - if (archiveFile = '') And (methodNumber < 22) And (methodNumber >= 0) then // < 18: method is not `help`, `compareFilesToNsis` or `compareFilesToBatch` + // < 24: method is not `help` + if (archiveFile = '') And (methodNumber < 24) And (methodNumber >= 0) then raise MissingParamException.Create(NeedArchiveFile); Case methodNumber of @@ -548,9 +555,10 @@ begin 12, 13: merge; 14, 15: compare; 16, 17: optimize; - 18, 19: compareFilesToNsis; - 20, 21: compareFilesToBatch; - 22, 23, 24: help; + 18, 19: diffFilesToNsis; + 20, 21: diffFilesToBatch; + 22, 23: diffAddKeep; + 24, 25, 26: help; else // -1: not found in the array raise MissingParamException.CreateFmt(UnknownMethod, [method]); end; diff --git a/mmarch.res b/mmarch.res index fc5846ce3566a00a2361f4e9c3f75d890193128a..9ee216d86f8bbd2da2d25095e0a4f6075f76017d 100644 GIT binary patch delta 37 pcmZqK&e*b@aYIG~CnEzh0|+o~E{sTLWHj7-GtwSNO_q$#1^~ZF3Dy7r delta 37 ocmZqK&e*b@aYIG~Cj$dB0~l;Bj7Vo>G}wGI(jG`nmW<8@0KW VER2 then VER2 -> VER3, or VER1 -> VER3 then VER2 -> VER3. But VER1 -> VER2 then VER1 -> VER3 will cause problem. +If `DIFF_FOLDER` contains previous diff files (such as `diff_folder_temp/` above), `mmarch compare OLD NEW filesonly DIFF_FOLDER` will perform a merger of old diff files and new diff files by cleaning up old diff files. It's OK to do VER1 -> VER2 then VER2 -> VER3, or VER1 -> VER3 then VER2 -> VER3. But VER1 -> VER2 then VER1 -> VER3 will cause problem. ![](https://raw.githubusercontent.com/might-and-magic/mmarch/master/tutorial/img/multi_version.png "") @@ -96,7 +96,7 @@ If `DIFF_FOLDER` contains previous diff files, `mmarch compare OLD NEW filesonly Use command ``` -mmarch compare-files-to-nsis diff_folder_temp nsis_folder/script.nsi files +mmarch diff-files-to-nsis diff_folder_temp nsis_folder/script.nsi files ``` to generate NSIS script `script.nsi` in `nsis_folder/` folder, from diff files in `diff_folder_temp/` folder. @@ -123,4 +123,8 @@ In order to make more complex installation file, you can modify the `.nsi` scrip You may also generate a Windows Batch file patch instead of an NSIS `.exe` installation file. +Developers using Git may need [`diff-add-keep`](https://github.com/might-and-magic/mmarch#diff-files-to-nsis-batch-diff-add-keep) command. + +`mmarch compare` and all related commands and features (incl. NSIS/batch script generation) work totally even if your folders do not contain any MM archive files at all. Therefore, you can use **mmarch** as a general file comparison and diff generation tool. + Visit [**mmarch**'s Home Page](https://github.com/might-and-magic/mmarch) for the full documentation.