Last active
March 22, 2025 10:23
-
-
Save Raimo33/ad7e8af83d0dd38337b238ec24c67b63 to your computer and use it in GitHub Desktop.
Header pre-commit
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import os | |
import subprocess | |
import datetime | |
import re | |
CREATOR_NAME = "Claudio Raimondi" | |
CREATOR_EMAIL = "[email protected]" | |
HEADER_WIDTH = 80 | |
SEPARATOR = "=" * (HEADER_WIDTH) | |
SUPPORTED_EXTENSIONS = {'.c', '.cpp', '.tpp', '.inl', '.h', '.hpp', '.rb', '.java'} | |
def get_staged_files(): | |
"""Get a list of staged files in the Git repository.""" | |
result = subprocess.run(['git', 'diff', '--cached', '--name-only'], stdout=subprocess.PIPE) | |
return result.stdout.decode('utf-8').splitlines() | |
def get_files_staged_for_deletion(): | |
"""Get a list of files staged for deletion in the Git repository.""" | |
result = subprocess.run(['git', 'status', '--porcelain'], stdout=subprocess.PIPE) | |
lines = result.stdout.decode('utf-8').splitlines() | |
return [line.split()[1] for line in lines if line.startswith('D ')] | |
def create_header(file_name, ext): | |
"""Create a header string for the given file with the correct comment syntax.""" | |
now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') | |
header_lines = [ | |
f"File: {file_name}".ljust(HEADER_WIDTH), | |
f"Creator: {CREATOR_NAME}".ljust(HEADER_WIDTH), | |
f"Email: {CREATOR_EMAIL}".ljust(HEADER_WIDTH), | |
"", | |
f"created at: {now}".ljust(HEADER_WIDTH), | |
f"last edited: {now}".ljust(HEADER_WIDTH), | |
] | |
if ext in {'.c', '.cpp', '.tpp', '.h', '.java', '.js', '.ts', '.go'}: | |
header = [f"/*{SEPARATOR}"] + [""] + header_lines + [""] + [f"{SEPARATOR}*/"] | |
elif ext == '.py': | |
header = [f"#{SEPARATOR}"] + [f"# {line}" for line in header_lines] + [f"#{SEPARATOR}"] | |
elif ext == '.rb': | |
header = [f"#{SEPARATOR}"] + [f"# {line}" for line in header_lines] + [f"#{SEPARATOR}"] | |
else: | |
header = [f"/*{SEPARATOR}"] + [""] + header_lines + [""] + [f"{SEPARATOR}*/"] | |
return "\n".join(header) | |
def header_present(content, ext): | |
"""Check if the header is already present in the content.""" | |
if ext in {'.c', '.cpp', '.tpp', '.h', '.java', '.js', '.ts', '.go'}: | |
header_pattern = re.compile(r'/\*={80}\s+File: .+\s+Creator: .+\s+Email: .+\s+created at: .+\s+last edited: .+\s+={80}\*/') | |
elif ext in {'.py', '.rb'}: | |
header_pattern = re.compile(r'#={80}\s+# File: .+\s+# Creator: .+\s+# Email: .+\s+# created at: .+\s+# last edited: .+\s+#={80}') | |
else: | |
header_pattern = re.compile(r'/\*={80}\s+File: .+\s+Creator: .+\s+Email: .+\s+created at: .+\s+last edited: .+\s+={80}\*/') | |
return header_pattern.search(content) is not None | |
def update_header_timestamp(content, ext): | |
"""Update the 'last edited' timestamp in the header.""" | |
now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') | |
if ext in {'.c', '.cpp', '.tpp', '.h', '.java', '.js', '.ts', '.go'}: | |
updated_content = re.sub( | |
r'(last edited: )\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}', | |
f'\\g<1>{now}', | |
content | |
) | |
elif ext in {'.py', '.rb'}: | |
updated_content = re.sub( | |
r'(# last edited: )\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}', | |
f'\\g<1>{now}', | |
content | |
) | |
else: | |
updated_content = re.sub( | |
r'(last edited: )\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}', | |
f'\\g<1>{now}', | |
content | |
) | |
return updated_content | |
def add_header_to_file(file_path): | |
"""Add a header to the given file or update the timestamp if the header is already present.""" | |
with open(file_path, 'r+') as file: | |
content = file.read() | |
_, ext = os.path.splitext(file_path) | |
if header_present(content, ext): | |
updated_content = update_header_timestamp(content, ext) | |
file.seek(0) | |
file.write(updated_content) | |
file.truncate() | |
print(f"Updated header timestamp in {file_path}") | |
else: | |
file.seek(0, 0) | |
header = create_header(os.path.basename(file_path), ext) | |
file.write(f"{header}\n\n{content}") | |
print(f"Added header to {file_path}") | |
def main(): | |
staged_files = get_staged_files() | |
files_staged_for_deletion = get_files_staged_for_deletion() | |
for file_path in staged_files: | |
if file_path in files_staged_for_deletion: | |
print(f"Skipping {file_path} (staged for deletion)") | |
continue | |
_, ext = os.path.splitext(file_path) | |
if ext in SUPPORTED_EXTENSIONS: | |
add_header_to_file(file_path) | |
subprocess.run(['git', 'add', file_path]) | |
else: | |
print(f"Skipping {file_path} (unsupported extension: {ext})") | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment