Subversionで、あるリビジョン以降の差分をファイルで抽出
Subversionのexportコマンドは作業コピー内の全ファイルを吐き出してしまいます。ぜんぶですよ、ぜんぶ!! Web仕事の場合、この大量のファイルをすべてFTPで再アップロードしなくちゃいけない、となるとたいへんです。できれば、内容が変わってないファイルはそのままにしたいですね。
できます。
svnコマンドのdiffには、パッチファイルのような行単位の差分を省略して、ファイル名のみ出力する機能があります。しかも、A/M/Dのマーク付きで。
svn diff -r100:HEAD --summarize .
で、こんな感じの出力が得られます。
D admin/htdocs/articles/_default D admin/htdocs/articles/_list.html M admin/htdocs/articles/_edit.html A admin/htdocs/articles/css/view.css M admin/htdocs/articles/index.html M admin/htdocs/articles/_view.html M admin/htdocs/css/common.css M admin/htdocs/index.html
これをPythonでばばっと処理してみました。
#!/usr/bin/env python #!coding: utf-8 import sys, os, re, shutil from optparse import OptionParser def list_changed_path(ws, rev): if not re.search(":", rev): rev += ":HEAD" pwd = os.getcwd() try: os.chdir(ws) svnfd = os.popen('svn diff -r%s --summarize .' % rev, 'r') overwrite = [] remove = [] for line in svnfd: mo = re.match(r'^\s*([AMD])\s*(.*)$', line.strip()) if mo: print line.strip() if mo.group(1) in ['A', 'M']: overwrite.append(mo.group(2).strip(" \n")) elif mo.group(1) in ['D']: remove.append(mo.group(2).strip(" \n")) svnfd.close() finally: os.chdir(pwd) overwrite.sort() remove.sort() return (overwrite, remove) if __name__ == '__main__': parser = OptionParser(usage="%prog <workspace> <rev>[:<rev>] <target>") (options, args) = parser.parse_args() if len(args) != 3: parser.error("Parameters not matched!") ws,rev,target = args # clean target dir if os.path.exists(target): for root, dirs, files in os.walk(target, topdown=False): for name in files: os.remove(os.path.join(root, name)) for name in dirs: os.rmdir(os.path.join(root, name)) else: os.makedirs(target) # parse svn diff summary overwrite, remove = list_changed_path(ws, rev) # copy added/modified files for p in overwrite: s = os.path.join(ws, p) d = os.path.join(target, p) if os.path.isfile(s): if not os.path.exists(os.path.dirname(d)): os.makedirs(os.path.dirname(d)) shutil.copy2(s, d) elif os.path.isdir(s): if not os.path.exists(d): os.makedirs(d) # report if removed files found if remove: fd = file(os.path.join(target, "__TO_BE_REMOVED__.txt"), "wt") for p in remove: fd.write("D %s\n" % p) fd.close()
上書きファイルのセットが吐き出せるコマンドのできあがりです。
$ svn-export-changed myworkspace 100 upload_files
で、myworkspaceのリビジョン100以降に変更があったファイルを、upload_filesにコピーします。ワークスペースは最新で、手元にコミット保留中のファイルがないのが前提です。
$ svn update -r200 myworkspace $ svn-export-changed myworkspace 100:200 upload_files
とかすれば、100から200の変更のみ取り出せます。(たぶんね。テストしてないからだめかも)
もし削除すべきファイルがあれば、出力先のフォルダの__TO_BE_REMOVED__.txt ファイルにレポートされます。
こりゃ便利。