Useful GitHub Tricks

1. Introduction

This short note collects three GitHub habits that keep repos clean and builds reliable: .gitignore, pre-commit, and a minimal GitHub Actions workflow. The goal is fewer accidental commits and more consistent checks.

2. .gitignore

.gitignore only ignores files that are not yet tracked by Git. If a file is already in history, remove it from tracking with git rm --cached <file> before the ignore rules take effect. Start from a template and then trim it down for your project.

Below is a common starter template (Linux + Python). You can generate a custom one with the toptal gitignore generator:

1
### Linux ###
2
*~
3
4
# temporary files which can be created if a process still has a handle open of a deleted file
5
.fuse_hidden*
6
7
# KDE directory preferences
8
.directory
9
10
# Linux trash folder which might appear on any partition or disk
11
.Trash-*
12
13
# .nfs files are created when an open file is removed but is still being accessed
14
.nfs*
15
16
### Python ###
17
# Byte-compiled / optimized / DLL files
18
__pycache__/
19
*.py[cod]
20
*$py.class
21
22
# C extensions
23
*.so
24
25
# Distribution / packaging
26
.Python
27
build/
28
develop-eggs/
29
dist/
30
downloads/
31
eggs/
32
.eggs/
33
lib/
34
lib64/
35
parts/
36
sdist/
37
var/
38
wheels/
39
share/python-wheels/
40
*.egg-info/
41
.installed.cfg
42
*.egg
43
MANIFEST
44
45
# PyInstaller
46
# Usually these files are written by a python script from a template
47
# before PyInstaller builds the exe, so as to inject date/other infos into it.
48
*.manifest
49
*.spec
50
51
# Installer logs
52
pip-log.txt
53
pip-delete-this-directory.txt
54
55
# Unit test / coverage reports
56
htmlcov/
57
.tox/
58
.nox/
59
.coverage
60
.coverage.*
61
.cache
62
nosetests.xml
63
coverage.xml
64
*.cover
65
*.py,cover
66
.hypothesis/
67
.pytest_cache/
68
cover/
69
70
# Translations
71
*.mo
72
*.pot
73
74
# Django stuff:
75
*.log
76
local_settings.py
77
db.sqlite3
78
db.sqlite3-journal
79
80
# Flask stuff:
81
instance/
82
.webassets-cache
83
84
# Scrapy stuff:
85
.scrapy
86
87
# Sphinx documentation
88
docs/_build/
89
90
# PyBuilder
91
.pybuilder/
92
target/
93
94
# Jupyter Notebook
95
.ipynb_checkpoints
96
97
# IPython
98
profile_default/
99
ipython_config.py
100
101
# pyenv
102
# For a library or package, you might want to ignore these files since the code is
103
# intended to run in multiple environments; otherwise, check them in:
104
# .python-version
105
106
# pipenv
107
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
108
# However, in case of collaboration, if having platform-specific dependencies or dependencies
109
# having no cross-platform support, pipenv may install dependencies that don't work, or not
110
# install all needed dependencies.
111
#Pipfile.lock
112
113
# poetry
114
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
115
# This is especially recommended for binary packages to ensure reproducibility, and is more
116
# commonly ignored for libraries.
117
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
118
#poetry.lock
119
120
# pdm
121
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
122
#pdm.lock
123
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
124
# in version control.
125
# https://pdm.fming.dev/#use-with-ide
126
.pdm.toml
127
128
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
129
__pypackages__/
130
131
# Celery stuff
132
celerybeat-schedule
133
celerybeat.pid
134
135
# SageMath parsed files
136
*.sage.py
137
138
# Environments
139
.env
140
.venv
141
env/
142
venv/
143
ENV/
144
env.bak/
145
venv.bak/
146
147
# Spyder project settings
148
.spyderproject
149
.spyproject
150
151
# Rope project settings
152
.ropeproject
153
154
# mkdocs documentation
155
/site
156
157
# mypy
158
.mypy_cache/
159
.dmypy.json
160
dmypy.json
161
162
# Pyre type checker
163
.pyre/
164
165
# pytype static type analyzer
166
.pytype/
167
168
# Cython debug symbols
169
cython_debug/
170
171
# PyCharm
172
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
173
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
174
# and can be added to the global gitignore or merged into this file. For a more nuclear
175
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
176
#.idea/
177
178
### Python Patch ###
179
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
180
poetry.toml
181
182
# ruff
183
.ruff_cache/
184
185
# LSP config files
186
pyrightconfig.json

3. Pre-commit

Use pre-commit to run checks automatically before each commit. Here I use prek, a Go-based pre-commit runner.

Install prek (pick one method):

1
# Using uv (recommended)
2
uv tool install prek
3
4
# Using uvx (install and run in one command)
5
uvx prek
6
7
# Adding prek to the project dev-dependencies
8
uv add --dev prek
9
10
# Using pip
11
pip install prek
12
13
# Using pipx
14
pipx install prek

Basic usage:

1
# Run all hooks against all files
2
prek run -a
3
4
# Update hook versions
5
prek auto-update
6
7
# Run hooks on every commit
8
prek install
9
10
# Uninstall the git hook
11
prek uninstall
12
13
# Clean up unused repos, hook envs and tool versions from prek cache
14
prek cache gc

Create a .pre-commit-config.yaml in the repo root:

.pre-commit-config.yaml

1
# Let all hooks run so you see the full set of issues in one pass.
2
fail_fast: false
3
4
exclude: "^.github/workflows/"
5
6
repos:
7
# 1. Basic file cleanup and checks (Standard hooks)
8
- repo: https://github.com/pre-commit/pre-commit-hooks
9
rev: v6.0.0
10
hooks:
11
- id: trailing-whitespace # Removes trailing whitespace
12
- id: check-added-large-files # Prevents committing giant files
13
- id: check-case-conflict # Checks for case conflicts (important for Windows/Mac)
14
- id: end-of-file-fixer # Ensures files end with a newline
15
- id: fix-byte-order-marker # Removes UTF-8 BOM
16
- id: check-json # Validates JSON syntax
17
- id: check-toml # Validates TOML syntax
18
- id: check-yaml # Validates YAML syntax (checks only, no formatting)
19
- id: check-xml # Validates XML syntax
20
- id: mixed-line-ending # Enforces consistent line endings (LF/CRLF)
21
- id: check-symlinks # Checks for broken symlinks
22
- id: check-merge-conflict # Checks for leftover merge conflict markers
23
- id: detect-private-key # Checks for private keys
24
# - id: no-commit-to-branch # (Commented out) Prevents committing to main branch
25
- id: check-executables-have-shebangs # Ensures executables have a valid shebang
26
- id: pretty-format-json # Formats JSON files
27
args: [--autofix, --no-sort-keys] # Autofix enabled, preserves key order
28
29
# 2. Spell checker (Catches typos in variable names)
30
- repo: https://github.com/crate-ci/typos
31
rev: v1.42.1
32
hooks:
33
- id: typos
34
# Fast dictionary-based spell check; configurable via `typos.toml`.
35
36
# 3. Python linting and formatting (Ruff - Extremely fast)
37
- repo: https://github.com/astral-sh/ruff-pre-commit
38
rev: v0.14.13
39
hooks:
40
- id: ruff-format # Formats Python code (like Black)
41
- id: ruff # Lints and fixes Python logic (like Flake8/Isort)
42
args: [--fix] # Auto-fix lint; CI remains green after fixes
43
44
# 4. Security scan (Secrets detection)
45
- repo: https://github.com/gitleaks/gitleaks
46
rev: v8.30.0
47
hooks:
48
- id: gitleaks
49
# Scans for hard-coded secrets; honors `.gitleaks.toml` if present.
50
51
# 5. Markdown formatting
52
- repo: https://github.com/hukkin/mdformat
53
rev: 1.0.0
54
hooks:
55
- id: mdformat
56
language: python
57
additional_dependencies:
58
- mdformat-gfm # GitHub Flavored Markdown support (tables, etc.)
59
- mdformat-black # Formats Python code blocks inside Markdown
60
- mdformat-mkdocs
61
- mdformat-footnote
62
# Keep Markdown consistent across docs and READMEs.
63
64
- repo: https://github.com/abravalheri/validate-pyproject
65
rev: v0.24.1
66
hooks:
67
- id: validate-pyproject # Validate pyproject.toml against PEP 621/517
68
69
- repo: https://github.com/igorshubovych/markdownlint-cli
70
rev: v0.47.0
71
hooks:
72
- id: markdownlint # Auto-fix Markdown style issues where possible
73
args: [--fix, --disable=MD013, --disable=MD041]
74
75
- repo: https://github.com/rbubley/mirrors-prettier
76
rev: v3.8.1
77
hooks:
78
- id: prettier
79
types_or: [yaml, toml]

A better way is to create 'prek.toml' and configure hooks there.

1
fail_fast = false
2
3
[[repos]]
4
repo = "https://github.com/pre-commit/pre-commit-hooks"
5
rev = "v6.0.0"
6
hooks = [
7
{ id = "trailing-whitespace" },
8
{ id = "check-added-large-files" },
9
{ id = "check-case-conflict" },
10
{ id = "end-of-file-fixer" },
11
{ id = "fix-byte-order-marker" },
12
{ id = "check-json" },
13
{ id = "check-toml" },
14
{ id = "check-yaml" },
15
{ id = "check-xml" },
16
{ id = "mixed-line-ending" },
17
{ id = "check-symlinks" },
18
{ id = "check-merge-conflict" },
19
{ id = "detect-private-key" },
20
{ id = "check-executables-have-shebangs" },
21
{
22
id = "pretty-format-json",
23
args = [
24
"--autofix",
25
"--no-sort-keys"
26
]
27
}
28
]
29
30
[[repos]]
31
repo = "https://github.com/crate-ci/typos"
32
rev = "v1.43.3"
33
hooks = [
34
{ id = "typos" }
35
]
36
37
[[repos]]
38
repo = "https://github.com/astral-sh/ruff-pre-commit"
39
rev = "v0.15.0"
40
hooks = [
41
{ id = "ruff-format" },
42
{
43
id = "ruff",
44
args = ["--fix"]
45
}
46
]
47
48
[[repos]]
49
repo = "https://github.com/gitleaks/gitleaks"
50
rev = "v8.30.0"
51
hooks = [
52
{ id = "gitleaks" }
53
]
54
55
[[repos]]
56
repo = "https://github.com/hukkin/mdformat"
57
rev = "1.0.0"
58
hooks = [
59
{
60
id = "mdformat",
61
language = "python",
62
additional_dependencies = [
63
"mdformat-gfm",
64
"mdformat-black",
65
"mdformat-mkdocs",
66
"mdformat-footnote"
67
]
68
}
69
]
70
71
[[repos]]
72
repo = "https://github.com/abravalheri/validate-pyproject"
73
rev = "v0.25"
74
hooks = [
75
{ id = "validate-pyproject" }
76
]
77
78
[[repos]]
79
repo = "https://github.com/igorshubovych/markdownlint-cli"
80
rev = "v0.47.0"
81
hooks = [
82
{ id = "markdownlint-fix" }
83
]
84
85
[[repos]]
86
repo = "https://github.com/rbubley/mirrors-prettier"
87
rev = "v3.8.1"
88
hooks = [
89
{
90
id = "prettier",
91
types_or = [
92
"yaml",
93
"toml"
94
]
95
}
96
]
97

After you write the config, always run prek auto-update to fetch the latest hook versions.

We can ignore some folder or rules in toml file

1
[tool.ruff]
2
exclude = [
3
"..."
4
]
5
6
[tool.ruff.lint]
7
ignore = ["..."]
8
9
[tool.typos.files]
10
extend-exclude = ["..."]

4. GitHub Actions

To keep local and CI checks consistent, add a simple workflow that runs the same pre-commit hooks on every push and pull request. The action below does two passes: first it runs hooks and allows fixes, then it runs again to verify everything is clean. If fixes were made, it can auto-commit them back to the branch.

.github/workflows/pre-commit.yml

1
name: pre-commit
2
3
on:
4
pull_request:
5
push:
6
branches: [main]
7
8
jobs:
9
prek:
10
runs-on: ubuntu-latest
11
permissions:
12
contents: write
13
steps:
14
- uses: actions/checkout@v6
15
with:
16
ref: ${{ github.head_ref || github.ref_name }}
17
- uses: actions/setup-go@v6
18
with:
19
go-version: "stable"
20
- name: pre-commit run (fix first pass)
21
id: prek_fix
22
uses: j178/prek-action@v1
23
continue-on-error: true
24
- name: pre-commit run (verify second pass)
25
uses: j178/prek-action@v1
26
- name: Detect changes
27
id: diff
28
run: |
29
if git diff --quiet; then
30
echo "changed=false" >> $GITHUB_OUTPUT
31
else
32
echo "changed=true" >> $GITHUB_OUTPUT
33
fi
34
- name: Auto-commit fixes
35
if: steps.diff.outputs.changed == 'true'
36
uses: stefanzweifel/git-auto-commit-action@v7
37
with:
38
commit_message: "Apply pre-commit fixes"

References

  1. https://www.toptal.com/developers/gitignore
  2. https://pre-commit.com
  3. https://github.com/j178/prek
  4. https://github.com/stefanzweifel/git-auto-commit-action