テクノロジー · 実用ガイド
Linux でファイル履歴の最新バージョンだけをコピーする
Linux で読み取れる Windows のファイル履歴フォルダーから、保存時の UTC タイムスタンプを基に最新バージョンだけを別の場所にコピーします。別のファイルは区別したまま扱います。

Windows のファイル履歴(File History)フォルダーには、同じ文書の保存済みコピーが複数あり、それぞれの名前に UTC タイムスタンプが付いていることがあります。各ファイルの最新のコピーだけを取り出すには、元のディレクトリと完全なファイル名でバージョンをグループ分けし、保存時のタイムスタンプを比較します。Linux でファイルが表示される順番は、その代わりになる確実な基準ではありません。
以下のツールは、最新バージョンだけを集めたコピーを別の場所に作成します。元ファイルを書き換えたり削除したりせず、標準動作のプレビューでは出力ファイルも作成しません。選択内容を適用すると、選んだファイルを新しい出力ディレクトリにコピーします。バックアップから古いバージョンを削除したり、それらが使っている容量を空けたりする処理ではありません。
手元にあるデータを確認する
保存されたファイルそのものが入った、読み取り可能なフォルダーから始めます。対応する名前の例は Report (2024_05_03 09_30_00 UTC).docx です。同じディレクトリにある、より新しい Report (2024_05_08 16_15_00 UTC).docx は同じグループに属します。FileHistory という名前のフォルダーなら必ずこのような名前のファイルが見えるとは考えず、処理したいサブツリーをツールに指定してください。
元のバックアップは、そのまま保管してください。大きなディレクトリツリーを処理する前に、いくつかのファイルを確認し、開けることを確かめます。ディスクをマウントできない、ファイルを読み取れない、ファイル名が意味を読み取れない識別子になっている、といった場合、このツールは次に行う復旧手段にはなりません。ファイル履歴のカタログから元のパスを再構成したり、存在しないファイル内容を復旧したりすることはできません。
名前が似ているという理由だけで、別のユーザーやパソコンのバックアップを 1 つのフォルダーに混ぜないでください。目的の読み取り可能なサブツリーを選び、ディレクトリ構造を保ち、別のバックアップは別途処理します。異なるバックアップにある同名の文書が、同じ元ファイルだと確認できるわけではありません。
どのバージョンが選ばれるかを理解する
同じディレクトリ内で、元の完全なファイル名が同じバージョン同士を、名前から解析した UTC タイムスタンプで比較します。Report.docx と Report.pdf は別のグループです。Documents/Report.docx と Archive/Report.docx も別のグループです。拡張子とディレクトリは、ファイルを区別する要素に含まれます。
別のディスクにコピーすると、ファイルシステム上のタイムスタンプが変わることがあります。それらの日時は選択の基準にはなりません。ディレクトリの走査でたまたま最後に現れたバージョンを選ぶわけでもありません。Python のドキュメントでは、ディレクトリ走査で得られる一覧は必ずしも並べ替えられていないと説明されています。目指す結果は、対応する各グループに存在する保存時の UTC 値のうち、最も新しいものを選ぶことです。
ツールをダウンロードし、パスを決める
Linux でターミナルを開き、Python のバージョンを確認します。
python3 --version
Python 3.10 以降を使ってください。filehistory-latest-v1.py を作業フォルダーに保存し、そのフォルダーでターミナルを開きます。curl がインストールされていれば、次のコマンドでソースファイルをダウンロードできます。このコマンドはスクリプトを保存するだけで、実行はしません。
curl --fail --location "https://watchatease.com/guide-tools/filehistory-latest-v1.py" --output "filehistory-latest-v1.py"
以下の例では、FileHistory-copy/Documents が入力元のサブツリーで、latest-documents が FileHistory-copy と同じ階層に新しく作る出力フォルダーです。入力元は実際のパスに置き換えてください。空白を含むパスは引用符で囲みます。出力ディレクトリは、まだ存在していてはいけません。また、入力元と出力先の範囲は重複していてはいけません。出力先を入力元の中に置かないでください。
コピーする前に選択内容をプレビューする
この入力元のサブツリーに書き込むバックアップ処理を一時停止し、プレビューを始めてからコピーが終わるまで、入力元のファイルを変更しないでください。この処理は、すべてのファイルを同じ一瞬の状態で取得するものではありません。まず、--apply を付けずにコマンドを実行します。
python3 "filehistory-latest-v1.py" --source "./FileHistory-copy/Documents" --output "./latest-documents"
ツールは出力ディレクトリを作成せず、ターミナルに JSON 形式のプレビューを表示します。ファイル内容の読み取りやハッシュ計算は行わないため、プレビューが成功しても、後ですべてのファイルをコピーできるとは限りません。先に進む前に、パスと選択内容を確認してください。複数の保存済みバージョンがある見覚えのあるファイル、拡張子以外の名前が同じで拡張子が異なる 2 つのファイル、別のディレクトリにある同名のファイルを確認します。
対応するタイムスタンプのパターンに一致しない通常のファイルは、スキップしたものとして報告されます。最新バージョンだけを集めた出力にはコピーされません。スキップされたファイルも必要かもしれないため、その記録を確認し、必要に応じて別に保管またはコピーしてください。出力を、入力元のすべてをコピーしたものと考えてはいけません。
無効な UTC タイムスタンプ、シンボリックリンク、特殊ファイル、読み取り失敗があると、処理は進められません。最新のタイムスタンプが同じで一意に決められない場合も、確認が必要です。見慣れない日付を書き換えて検証を通したり、同順位のファイルを適当に選んだりせず、報告されたパスを調べてください。対応するファイルが 1 つもない場合も処理は進められません。タイムスタンプ付きファイルが入ったデータのサブツリーを選んでいるか確認してください。
最新バージョンを新しいディレクトリにコピーする
プレビューが集めたいファイルを示していることを確認したら、同じコマンドに --apply を付けて実行します。
python3 "filehistory-latest-v1.py" --source "./FileHistory-copy/Documents" --output "./latest-documents" --apply
出力には files ディレクトリ、manifest.csv、summary.json が含まれます。選択したファイルの相対ディレクトリ構造は保持します。既定では保存時のタイムスタンプ付きファイル名も保持するので、どのバージョンが選ばれたかを確認できます。元のバックアップには、古いバージョンが引き続き残ります。ツールがコピーするのはファイルの内容であり、元のアクセス権、ACL、ファイルシステムのタイムスタンプはコピーしません。
出力で元のファイル名を使いたい場合は、--restore-names を追加します。これは名前を扱いやすくするための機能で、どのタイムスタンプが選ばれるかは変わりません。適用する前に、別の未使用の出力パスを指定して、その設定をプレビューしてください。
python3 "filehistory-latest-v1.py" --source "./FileHistory-copy/Documents" --output "./latest-documents-original-names" --restore-names
python3 "filehistory-latest-v1.py" --source "./FileHistory-copy/Documents" --output "./latest-documents-original-names" --restore-names --apply
結果を利用する前に確認する
summary.json を読み、テキストエディターや表計算ビューアーで manifest.csv を確認します。コピーが完了していれば status は complete となり、_INCOMPLETE.txt マーカーはありません。マニフェストでは、source_relative_path と output_relative_path で各コピーの元と先をたどり、version_utc で選ばれた日付、sha256 で記録された内容のハッシュを確認できます。copied、older_version、unmatched の各記録を、合計件数や報告されたエラーと照らし合わせてください。
ツールは SHA-256 ハッシュで、コピーしたファイルの内容を確認します。ハッシュの一致は、コピーしたバイト列が選択した元ファイルと一致することを裏付けます。ただし、選択したファイルが完全で破損していない原本だったことや、期待したすべての文書がバックアップに含まれていたことまで証明するものではありません。
出力された文書や画像などをいくつか、通常使うアプリケーションで開いてください。重要なファイル、複数のバージョンがあるファイル、別々のディレクトリからのファイルを含めて確認します。内容を把握しているグループについて、選択されたタイムスタンプを、入力元にある各バージョンと比較してください。不足や想定外の点があれば、結果を完全なものとして扱う前に、プレビューと元ファイルに戻って確認します。
目的がディスク容量を空けることだった場合も、この処理では元のバックアップを整理・削除していない点を忘れないでください。まず最新バージョンだけのコピーを検証し、その後で、バックアップと古いバージョンを残すかどうかを別途判断します。古いバージョンが後で役立つこともあります。新しいフォルダーができたというだけで削除しないでください。
コマンドが完了しない場合
Python がスクリプトを見つけられない場合は、ターミナルの現在のフォルダーを確認するか、スクリプトのフルパスを指定してください。入力元が見つからない場合は、読み取り可能なバックアップが実際にマウントされている場所を指定します。Windows のドライブ文字が、そのまま Linux で使えるパスになるわけではありません。
出力先がすでに存在する場合は、別の未使用の出力ディレクトリを選びます。データが入った出力先を次の実行でも指定して、更新や結合が行われるとは考えないでください。適用時はプレビューと同じオプションを使い、--restore-names を選んでいた場合はそれも付けます。名前を元に戻すことでファイルとディレクトリのパスが衝突する場合は、その構成を見直すか、既定のタイムスタンプ付き出力名を使ってください。
プレビューや事前確認で失敗した場合は、出力ディレクトリを作成せずにエラーを返します。コピー中に失敗すると、一部だけの出力が _INCOMPLETE.txt と、failed または in_progress のサマリーとともに残ることがあります。レポートの書き込みも失敗した場合は、summary.json や manifest.csv が存在しないこともあります。ターミナルのエラー、未完了マーカー、完了を示す記録の欠落がある場合は、成功しなかった実行として扱ってください。報告された原因を修正し、もう一度プレビューしてから、別の新しい出力ディレクトリに適用します。
調査中は、未完了のディレクトリを別に保管してください。ツールが自動で削除することはありません。その中のファイルを後の実行結果と混ぜて、検証済みと扱わないでください。完了した 1 回の実行に対応するマニフェストとサマリーを使い、ファイルを開いて内容を確認してから、何を残すか判断します。
最新バージョンだけの集合では分からないこと
各グループから得られるのは、そこに残っている最新の保存済みバージョンであり、ファイルごとに日付が異なることがあります。そのため、この集合はパソコンのある特定の時点のスナップショットではありません。元のパソコンから削除されたファイルも、バックアップに保存済みコピーが残っていれば、出力に含まれることがあります。
存在しない古いバージョン、対応するパターンに一致しないファイル名、カタログにしかない情報、意味を読み取れない保存先パスは、この選択処理の対象外です。ツールは、削除履歴を推測したり、それらのパスを再構成したり、認識できないファイルを不要と判断したりはしません。
Windows を使える環境で、ファイル履歴の日付ごとの復元が必要な場合は、Microsoft が標準機能での手順を説明しています。別の既存の方法として、WindowsFileHistoryCleaner にも最新バージョンの選択とプレビューモードがあります。Mono は .NET の幅広い機能に対応していますが、このガイドでは、そのプロジェクトの Linux でのセットアップを検証済みとはしていません。ここで紹介する手順は、上記の、読み取り可能なタイムスタンプ付きファイルに対象を限定しています。
