# 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:])