use internal hash calculation and only use git to amend once proper hash found
authorFrédéric Péters <fpeters@0d.be>
Mon, 6 Sep 2021 12:07:49 +0000 (14:07 +0200)
committerFrédéric Péters <fpeters@0d.be>
Mon, 6 Sep 2021 12:07:54 +0000 (14:07 +0200)
git-acab

index b2f0af4..9645ac2 100755 (executable)
--- a/git-acab
+++ b/git-acab
@@ -3,12 +3,12 @@
 # amending the commit with a new date.
 
 import argparse
+import hashlib
 import os
+import subprocess
 import sys
 import time
 
-import git
-
 parser = argparse.ArgumentParser()
 parser.add_argument('--start', type=str, default='commit', help='now or commit')
 parser.add_argument('--prefix', action='store_true')
@@ -16,23 +16,24 @@ parser.add_argument('--suffix', action='store_true')
 parser.add_argument('-q', '--quiet', action='store_true')
 args = parser.parse_args()
 
-while not os.path.exists('.git'):
-    os.chdir('..')
-    if os.getcwd() == '/':
-        sys.stderr('failed to find a git repository')
-        sys.exit(1)
-
 
-def amend_commit(repo, initial_commit, start_timestamp):
+def amend_commit(start_timestamp):
     t0 = time.time()
     counter = 0
+    cat_file = subprocess.run(['git', 'cat-file', 'commit', 'HEAD'], capture_output=True).stdout
+    cat_file_lines = cat_file.splitlines()
+    cat_file_lines[2] = cat_file_lines[2].replace(cat_file_lines[2].rsplit(b' ', 2)[1], b'$AUTHOR__$')
+    cat_file_lines[3] = cat_file_lines[3].replace(cat_file_lines[3].rsplit(b' ', 2)[1], b'$COMMITER$')
+    hashed_bytes_template = b'commit %s\0%s\n' % (
+        str(len(b'\n'.join(cat_file_lines)) + 1).encode(),
+        b'\n'.join(cat_file_lines),
+    )
     authored_timestamp = start_timestamp
+    base_env = os.environ
     while True:
         authored_timestamp -= 1
         for committed_timestamp in range(start_timestamp, authored_timestamp, -1):
             counter += 1
-            if counter % 10000 == 0:
-                repo.git.prune()
             if not args.quiet:
                 print(
                     '%5d - %s - %s - [%s:%02d]'
@@ -46,32 +47,45 @@ def amend_commit(repo, initial_commit, start_timestamp):
                     end='\r',
                 )
 
-            commit = initial_commit.replace(
-                authored_date=authored_timestamp, committed_date=committed_timestamp
-            )
-            if args.prefix and not commit.hexsha.startswith('acab'):
+            hashed_bytes = hashed_bytes_template.replace(
+                b'$AUTHOR__$', str(authored_timestamp).encode()
+            ).replace(b'$COMMITER$', str(committed_timestamp).encode())
+            new_hash = hashlib.sha1(hashed_bytes).hexdigest()
+            if args.prefix and not new_hash.startswith('acab'):
                 continue
-            if args.suffix and not commit.hexsha.endswith('acab'):
+            if args.suffix and not new_hash.endswith('acab'):
                 continue
-            if 'acab' not in commit.hexsha:
+            if 'acab' not in new_hash:
                 continue
-            repo.active_branch.set_commit(commit)
-            repo.git.prune()
-            return commit
-
+            base_env['GIT_COMMITTER_DATE'] = time.strftime(
+                '%Y-%m-%d %H:%M:%S', time.localtime(committed_timestamp)
+            )
+            subprocess.run(
+                [
+                    'git',
+                    'commit',
+                    '--no-verify',
+                    '--amend',
+                    '--no-edit',
+                    '--date=%s' % time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(authored_timestamp)),
+                ],
+                capture_output=True,
+                env=base_env,
+            )
+            return
 
-repo = git.Repo()
-commit = repo.commit('HEAD')
 
 if args.start == 'commit':
-    start_timestamp = commit.authored_date
+    p = subprocess.run(['git', 'show', '--format=format:%at'], capture_output=True)
+    start_timestamp = int(p.stdout[: p.stdout.index(b'\n')])
 elif args.start == 'now':
     start_timestamp = int(time.time())
 else:
     sys.stderr('unknown value for --start')
     sys.exit(1)
 
-new_head = amend_commit(repo, commit, start_timestamp)
+amend_commit(start_timestamp)
 
 if not args.quiet:
-    print('\ngot %s 🔥🚓' % new_head.hexsha)
+    p = subprocess.run(['git', 'rev-parse', 'HEAD'], capture_output=True)
+    print('\ngot %s 🔥🚓' % p.stdout.decode().strip())