tweak git and ssh
This commit is contained in:
parent
3941a8d2ac
commit
dd7d702010
@ -47,3 +47,5 @@
|
||||
[credential "https://gist.github.com"]
|
||||
helper =
|
||||
helper = !/usr/bin/gh auth git-credential
|
||||
[push]
|
||||
autoSetupRemote = true
|
||||
|
@ -52,3 +52,7 @@ export SDKMAN_DIR="$HOME/.sdkman"
|
||||
|
||||
# Flutter
|
||||
[[ -s "$HOME/pkg/flutter/bin" ]] && export PATH="$HOME/pkg/flutter/bin:$PATH"
|
||||
|
||||
# Rye
|
||||
[[ -s "$HOME/.rye/env" ]] && source "$HOME/.rye/env"
|
||||
. "$HOME/.cargo/env"
|
||||
|
@ -5,6 +5,7 @@ Host * !*.github.com !github.com
|
||||
IdentityFile %d/.ssh/id_ed25519
|
||||
IdentityFile %d/.ssh/id_rsa
|
||||
# VisualHostKey yes
|
||||
VerifyHostKeyDNS ask
|
||||
|
||||
# Github SSH adapter for restricted networks
|
||||
Host github.com gist.github.com
|
||||
@ -91,6 +92,8 @@ Host td-prod td-prod2 td-prod3 td-prod4
|
||||
User guru
|
||||
Host w6421
|
||||
User "guru-domain\\epratten"
|
||||
#RequestTTY yes
|
||||
# RemoteCommand "C:\Program Files\Git\bin\bash.exe" --login
|
||||
|
||||
# Personal Infra
|
||||
Host oci-arm
|
||||
|
6
scripts/git-detect-fake-authors
Executable file
6
scripts/git-detect-fake-authors
Executable file
@ -0,0 +1,6 @@
|
||||
#! /bin/bash
|
||||
# This script finds every time someone makes a commit under someone else's name
|
||||
# NOTE: This includes co-authoring
|
||||
set -e
|
||||
|
||||
git-log-sqlite -q 'SELECT committer as Real, author as Fake, count(*) as Count FROM commits WHERE author NOT LIKE committer GROUP BY author, committer ORDER BY count DESC;'
|
182
scripts/git-log-sqlite
Executable file
182
scripts/git-log-sqlite
Executable file
@ -0,0 +1,182 @@
|
||||
#! /usr/bin/env python3
|
||||
import subprocess
|
||||
import sqlite3
|
||||
import argparse
|
||||
import sys
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
FIELDS = {
|
||||
"ct": "timestamp",
|
||||
"aN": "author",
|
||||
"aE": "email",
|
||||
"cN": "committer",
|
||||
"cE": "committer_email",
|
||||
"s": "subject",
|
||||
"b": "body",
|
||||
"N": "notes",
|
||||
}
|
||||
|
||||
|
||||
def read_properties() -> Dict[str, Dict[str, str]]:
|
||||
output = {}
|
||||
for field in FIELDS:
|
||||
# Construct the log request
|
||||
format_str = f"%H %{field}%x00"
|
||||
|
||||
# Get the results
|
||||
repo_results = subprocess.run(
|
||||
["git", "log", f"--format=format:{format_str}"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
).stdout
|
||||
submodule_results = subprocess.run(
|
||||
[
|
||||
"git",
|
||||
"submodule",
|
||||
"foreach",
|
||||
"git",
|
||||
"log",
|
||||
f"--format=format:{format_str}",
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
).stdout
|
||||
|
||||
# Parse the results
|
||||
all_results = repo_results + submodule_results
|
||||
all_results = all_results.split("\x00")
|
||||
for result in all_results:
|
||||
if " " not in result or result == "":
|
||||
continue
|
||||
commit_hash, value = result.split(" ", 1)
|
||||
if commit_hash.startswith("Entering"):
|
||||
continue
|
||||
if commit_hash.startswith("\n"):
|
||||
commit_hash = commit_hash[1:]
|
||||
if commit_hash not in output:
|
||||
output[commit_hash] = {}
|
||||
output[commit_hash][field] = value
|
||||
|
||||
return output
|
||||
|
||||
|
||||
def create_table(cursor: sqlite3.Cursor) -> None:
|
||||
sql = "CREATE TABLE IF NOT EXISTS commits (hash TEXT PRIMARY KEY, "
|
||||
for field in FIELDS.values():
|
||||
ty = "TEXT"
|
||||
if field == "timestamp":
|
||||
ty = "INTEGER"
|
||||
if field == "hash":
|
||||
ty = "TEXT PRIMARY KEY"
|
||||
|
||||
sql += f"{field} {ty}, "
|
||||
sql = sql[:-2] + ")"
|
||||
logger.debug(f"Creating table with SQL: {sql}")
|
||||
cursor.execute(sql)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
# Handle program arguments
|
||||
ap = argparse.ArgumentParser(
|
||||
prog="git-log-sqlite", description="Interact with the git log using SQL"
|
||||
)
|
||||
ap.add_argument(
|
||||
"--dump",
|
||||
help="Path to a sqlite3 database file to dump contents to. DELETES EXISTING FILE",
|
||||
type=Path,
|
||||
)
|
||||
ap.add_argument(
|
||||
"--interactive",
|
||||
"-i",
|
||||
help="Start an interactive SQL session",
|
||||
action="store_true",
|
||||
)
|
||||
ap.add_argument("--query", "-q", help="Run a query and print the results")
|
||||
ap.add_argument("--no-header", help="Do not print the header", action="store_true")
|
||||
ap.add_argument("--mode", help="Set the mode for the sqlite3 command", default="table")
|
||||
ap.add_argument(
|
||||
"-v", "--verbose", help="Enable verbose logging", action="store_true"
|
||||
)
|
||||
args = ap.parse_args()
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG if args.verbose else logging.INFO,
|
||||
format="%(levelname)s: %(message)s",
|
||||
)
|
||||
|
||||
# Interactive mode and query mode are mutually exclusive
|
||||
if args.interactive and args.query:
|
||||
logger.error("Interactive mode and query mode are mutually exclusive")
|
||||
return 1
|
||||
|
||||
# If the user didn't specify anything, print the help message
|
||||
if not (args.interactive or args.query):
|
||||
ap.print_help()
|
||||
return 1
|
||||
|
||||
# Read the properties
|
||||
commits = read_properties()
|
||||
logger.debug(f"Read {len(commits)} commits")
|
||||
|
||||
# Open a connection to the database
|
||||
if args.dump:
|
||||
args.dump.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.dump.unlink(missing_ok=True)
|
||||
conn = sqlite3.connect(args.dump if args.dump else ":memory:")
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Create a table to store the data
|
||||
create_table(cursor)
|
||||
|
||||
# Insert the data into the table
|
||||
rows = list(commits.items())
|
||||
rows.sort(key=lambda x: x[1]["ct"])
|
||||
for commit_hash, data in rows:
|
||||
sql = "INSERT INTO commits VALUES (" + ",".join(["?"] * (len(FIELDS) + 1)) + ")"
|
||||
values = [commit_hash] + [data.get(field, None) for field in FIELDS.keys()]
|
||||
cursor.execute(sql, values)
|
||||
|
||||
# Commit the changes
|
||||
conn.commit()
|
||||
|
||||
# If just dumping, we are done
|
||||
if args.dump:
|
||||
conn.close()
|
||||
return 0
|
||||
|
||||
# Dump to a temp file
|
||||
import tempfile
|
||||
|
||||
temp_file = Path(tempfile.mkstemp()[1])
|
||||
temp_conn = sqlite3.connect(temp_file)
|
||||
temp_conn.executescript("\n".join(conn.iterdump()))
|
||||
temp_conn.commit()
|
||||
conn.close()
|
||||
|
||||
# Build the base sqlite command
|
||||
sqlite_cmd = ["sqlite3", "--cmd", f".mode {args.mode}"]
|
||||
if not args.no_header:
|
||||
sqlite_cmd.append("--cmd")
|
||||
sqlite_cmd.append(".headers on")
|
||||
|
||||
# If running a query, do so
|
||||
if args.query:
|
||||
subprocess.run(sqlite_cmd + [temp_file, args.query])
|
||||
|
||||
# If running interactively, do so
|
||||
if args.interactive:
|
||||
subprocess.run(sqlite_cmd + ["--interactive", temp_file])
|
||||
|
||||
# Delete the temp file
|
||||
temp_file.unlink()
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
Loading…
x
Reference in New Issue
Block a user