# HG changeset patch # User Raphaël Gomès <rgomes@octobus.net> # Date 1583850324 -3600 # Tue Mar 10 15:25:24 2020 +0100 # Node ID 1380b2ba70147d0ccf5a1693e67e3398ab22eb07 # Parent 4003ee32d0b6b387f63e4d35325257ac4f402b2d Add script to generate unknown files diff --git a/repo-scripts/generate_unknown_files b/repo-scripts/generate_unknown_files new file mode 100755 --- /dev/null +++ b/repo-scripts/generate_unknown_files @@ -0,0 +1,113 @@ +#!/usr/bin/env python +""" +Generates empty files that have a very high chance of being treated as "unknown" +by Mercurial by shuffling filenames of existing tracked files. +""" +from __future__ import unicode_literals + +import argparse +import os +import random +import re +import sys +import hglib +from hglib.util import cmdbuilder, b + + +def generate_unknown_paths(tracked_paths): + """ + Takes in an iterable of paths (str), shuffles the filenames and returns + the new list. It keeps the "true" file extensions intact and removes the "." + at the start of the filename. + + Ex: + ["path/to/repo/file.txt", "path/to/repo/otherfile.tar.gz"] + => ["path/to/repo/efil.txt", "path/to/repo/r.oehelfttrai.gz"] + ["path/to/repo/.hgignore"] + => ["path/to/repo/hoggrnie"] + """ + cleaner_regex = re.compile('[\W_]+', flags=re.UNICODE) + new_paths = [] + + # For easier testing + seed = os.environ.get("SCMPERF_RANDOM_SEED") + + if seed is not None: + random.seed(int(seed)) + + for path in tracked_paths: + basename = os.path.basename(path) + filename, ext = os.path.splitext(basename) + + # That way we don't easily create problematic filenames (on Linux...) + filename = cleaner_regex.sub("", filename) + + if len(filename) < 3: + # skip files that are too small to prevent easy conflict + continue + + l = list(filename) + + random.shuffle(l) + new_name = ''.join(l) + ext + new_paths.append(path[0:-len(basename)] + new_name) + + return new_paths + + +def create_files(paths): + for path in paths: + with open(path, mode="a"): + os.utime(path, None) + + +class Repo(object): + def __init__(self, repo_path): + self.repo_path = repo_path + self.client = hglib.open(path=self.repo_path) + + def get_tracked_files(self): + args = cmdbuilder(b('files')) + out = self.client.rawcommand(args) + return out.splitlines() + + +def main(args): + parser = argparse.ArgumentParser() + parser.add_argument("-R", "--repository", help="path to the repository", + action="store", default=".", type=str) + parser.add_argument("--max", help="maximum of files to create", + action="store", default=0, type=int) + parser.add_argument( + "--dry-run", + help="don't actually generate the files, print them to stdout", + action="store_true", + default=False, + ) + args = parser.parse_args(args) + max = args.max + repo_path = args.repository + + repo = Repo(repo_path) + tracked_paths = repo.get_tracked_files() + + if not len(tracked_paths): + raise ValueError("No tracked files in repo %s" % repo_path) + + if max: + tracked_paths = tracked_paths[:max] + + unknown_paths = generate_unknown_paths(tracked_paths=tracked_paths) + + if not unknown_paths: + raise ValueError("No unknown paths were generated") + + if not args.dry_run: + create_files(unknown_paths) + else: + for pair in zip(tracked_paths, unknown_paths): + print("%s => %s" % pair) + + +if __name__ == '__main__': + main(sys.argv[1:])