Skip to content

Instantly share code, notes, and snippets.

@Raimo33
Last active March 22, 2025 10:23
Show Gist options
  • Save Raimo33/ad7e8af83d0dd38337b238ec24c67b63 to your computer and use it in GitHub Desktop.
Save Raimo33/ad7e8af83d0dd38337b238ec24c67b63 to your computer and use it in GitHub Desktop.
Header pre-commit
#!/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