[備忘録] iCloud上のディレクトリをgit管理すると痛い目にあう話


絶対にiCloud上でコードを管理するな!!!

iCloud上のディレクトリをgit管理していると,以下のようにバージョンファイルが勝手に作成されてしまうことがある.これは,iCloud上で作業することで発生するので,iCloud上では作業しないようにしよう.

この問題の解決方法

もうすでに,バージョンファイルが作成されてしまっていることに気づかずにコミットしてしまった場合は,バージョンファイルをgitの管理対象から外す必要がある.そして,ついでにバージョンファイルも消す.

手順

  1. iCloudからローカル環境に作業ディレクトリを移す.
  2. ローカルに移動した作業ディレクトリでバージョンファイルを管理対象から消し,存在自体も消す.
  3. コミットしてプッシュ

バージョンファイルの削除について

インデックスにキャッシュされている(管理対象となっている)ファイル一覧は以下で取得できる.この中にバージョンファイルが入っていることを確認する.

git ls-files

上記のコマンドで出力されたファイルを一旦ファイル出力する.例えば以下のように.

git ls-files > ls-files.txt

出力したファイルの中から,バージョンファイルを抜き出し,バージョンファイルとオリジナルファイルの内容が同じであればバージョンファイルは心置きなく消していいことになる.内容が異なっている場合のみ,中身を確認して消すべきかそうでないかを確認する必要がある.
そのため,消しても良いファイル一覧を取得するプログラムを作成する.例えば以下のように.
(バージョンファイルは間にスペースがあるため少し面倒.ファイル出力するときに,一つのファイルとして表現するためにクォーティングする必要がある.)

grouping.py

import re
from typing import Optional, Tuple
import filecmp

with open("ls-files.txt") as f:
    files = f.readlines()

pattern = r"(^.+)(\s[0-9]+)(.*$)"
pattern = re.compile(pattern)

def split_file_name(filename: str) -> Optional[Tuple[str, str, str]]:
    m = pattern.match(filename)
    if m is None:
        return None
    prefix, version, suffix = m.groups()
    return prefix, version, suffix

splited = [split_file_name(file) for file in files]

versioned = list(filter(lambda s: s is not None, splited))

versioned_dict = {}
for file in versioned:
    key = (file[0],file[2])
    if key not in versioned_dict:
        versioned_dict[key] = []
    versioned_dict[key].append(file)

# rm_files(消しても良いファイル達), save_dict(中身を確認するファイル達)の作成
rm_files = []
save_dict = {}

keys = versioned_dict.keys()
for key in keys:
    ori_file = "".join(key)
    for sp in versioned_dict[key]:
        ver_file = "".join(sp)

        # ファイルの比較
        if filecmp.cmp(ori_file, ver_file):
            rm_files.append(ver_file)
        else:
            if key not in save_dict:
                save_dict[key] = []
            save_dict[key].append(ver_file)

# 消しても良いファイル一覧をテキストファイルとして出力
with open("rm.txt", "w") as f:
    for file in rm_files:
        f.write('\"{}\"'.format(file) + " ")

上記のようなプログラムで消しても良いファイル一覧が取得できたら,それらのファイルをgit管理対象から外し,存在自体も削除する.以下のように.

eval git rm $(cat rm.txt)

shellの解釈の順番の都合によりevalが必要.