Initial commit.
This commit is contained in:
commit
6166b42b2e
12
.flake8
Normal file
12
.flake8
Normal file
@ -0,0 +1,12 @@
|
||||
[flake8]
|
||||
max-line-length = 120
|
||||
exclude = .venv
|
||||
|
||||
# specify which checks to enable
|
||||
select =
|
||||
C, # complexity checks
|
||||
E, # pep8 errors
|
||||
F, # pyflakes fatals
|
||||
W, # pep8 warnings
|
||||
B, # bugbear checks for design issues (requires flake8-bugbear)
|
||||
N # pep8 naming (requires pep8-naming)
|
50
.pre-commit-config.yaml
Normal file
50
.pre-commit-config.yaml
Normal file
@ -0,0 +1,50 @@
|
||||
default_language_version:
|
||||
python: python3.10
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.3.0
|
||||
hooks:
|
||||
- id: check-merge-conflict
|
||||
- id: check-shebang-scripts-are-executable
|
||||
- id: check-executables-have-shebangs
|
||||
- id: end-of-file-fixer
|
||||
- id: mixed-line-ending
|
||||
args: [--fix=lf]
|
||||
- id: no-commit-to-branch # without arguments, master/main will be protected.
|
||||
- id: trailing-whitespace
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 22.10.0
|
||||
hooks:
|
||||
- id: black
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: 5.10.1
|
||||
hooks:
|
||||
- id: isort
|
||||
name: isort
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 5.0.4
|
||||
hooks:
|
||||
- id: flake8
|
||||
additional_dependencies: [
|
||||
'flake8-bugbear==22.6.22',
|
||||
'pep8-naming==0.13.0'
|
||||
]
|
||||
- repo: https://github.com/pycqa/pydocstyle
|
||||
rev: 6.1.1
|
||||
hooks:
|
||||
- id: pydocstyle
|
||||
exclude: 'setup.py|scratch/' # Because complaining about docstrings here is annoying.
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: 'v0.991'
|
||||
hooks:
|
||||
- id: mypy
|
||||
# Passing filenames to mypy can do odd things. See
|
||||
# https://github.com/pre-commit/mirrors-mypy/issues/33.
|
||||
# mypy.ini determines the set of files that will actually be checked.
|
||||
pass_filenames: false
|
||||
# The pre-commit hook passes some options, but we set options in mypy.ini.
|
||||
args: []
|
||||
# The pre-commit hook only has python, not pyi.
|
||||
types: []
|
||||
types_or: [python, pyi]
|
11
LICENCE
Normal file
11
LICENCE
Normal file
@ -0,0 +1,11 @@
|
||||
This program is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <https://www.gnu.org/licenses/>.
|
29
README.md
Normal file
29
README.md
Normal file
@ -0,0 +1,29 @@
|
||||
# git-tidy
|
||||
|
||||
Have you ever come back to a git repo after a while, started developing, and
|
||||
then realised you weren't on the main branch, or your branch was missing the
|
||||
latest changes, so you ended up with merge conflicts?
|
||||
|
||||
Or have you ever finished working on a feature, completed a pull-request online,
|
||||
and wanted an easy way to clean up your local repo from all the unused
|
||||
references that have accumulated themselves?
|
||||
|
||||
Then `git-tidy` is for you!
|
||||
|
||||
This is a simple Python script which you can install by copying (or symlinking)
|
||||
anywhere on your path (I recommend ~/bin/, but something like /usr/local/bin/
|
||||
would also work).
|
||||
|
||||
Entering
|
||||
```bash
|
||||
git tidy
|
||||
```
|
||||
at the terminal will:
|
||||
- Check out the default branch, usually `main`
|
||||
- if this is not configured, it will try to figure out what it should be,
|
||||
- Pull to make sure that you're up-to-date with the default branch,
|
||||
- Delete all branches that have already been merged into the default, and,
|
||||
- Delete all references which have been removed from the remote as well,
|
||||
|
||||
leaving you with a pristine, tidy repository in order to continue your
|
||||
development.
|
104
git-tidy
Executable file
104
git-tidy
Executable file
@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# git-tidy is free software: you can redistribute it and/or modify it under the terms of the GNU
|
||||
# General Public License as published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
|
||||
# git-tidy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License along with git-tidy. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
common_default_branches = [
|
||||
"main",
|
||||
"master",
|
||||
"devel",
|
||||
"dev",
|
||||
]
|
||||
|
||||
# Check whether we're actually in a git repo before doing anything else.
|
||||
# TODO: Rename this function to something more obvious.
|
||||
def get_status():
|
||||
git_status = subprocess.run(
|
||||
["git", "status"],
|
||||
capture_output=True, # we don't want to show the user everything
|
||||
)
|
||||
if git_status.returncode == 0:
|
||||
return True
|
||||
|
||||
print(git_status.stderr.decode())
|
||||
return False
|
||||
|
||||
|
||||
def get_default_branch() -> str:
|
||||
# Try to see if there's a configuration already.
|
||||
sp = subprocess.run(["git", "config", "--get", "tidy.defaultbranch"], capture_output=True)
|
||||
if sp.returncode != 0:
|
||||
print("No default branch for git-tidy configured, will try to figure out which to use...")
|
||||
candidates = []
|
||||
branch_list = [
|
||||
branch.strip()
|
||||
for branch in subprocess.run(["git", "branch", "--list"], capture_output=True).stdout.decode().split("\n")
|
||||
if len(branch)
|
||||
]
|
||||
# Check whether any branches match our list of common defaults.
|
||||
for branch in branch_list:
|
||||
# Remove the asterisk in front of the active branch name.
|
||||
if branch[0] == "*":
|
||||
branch = branch[2:]
|
||||
|
||||
if branch in common_default_branches:
|
||||
candidates.append(branch)
|
||||
|
||||
if len(candidates) == 1:
|
||||
# Only one branch name matches, so we'll go with that one.
|
||||
subprocess.run(["git", "config", "--add", "tidy.defaultbranch", candidates[0]])
|
||||
return candidates[0]
|
||||
|
||||
# TODO: Handle the zero case.
|
||||
|
||||
# If we don't have one or zero, which do we have?
|
||||
selection = -1
|
||||
while (selection < 0) or (selection >= len(candidates)):
|
||||
print(
|
||||
f"{'Several' if len(candidates) > 1 else 'No'} potential default branch candidates found. Please select one:"
|
||||
)
|
||||
for n, candidate in enumerate(candidates):
|
||||
print(f"{n}: {candidate}")
|
||||
|
||||
# TODO: This breaks if the user is stupid and doesn't input an int.
|
||||
selection = int(input("Which to choose?"))
|
||||
|
||||
return candidates[selection]
|
||||
|
||||
# TODO: Might be nice to have a sanity-check that the configured branch is in fact actually on the list. It may just be easiest to do this by trying to switch to it.
|
||||
return sp.stdout.decode()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if not get_status():
|
||||
exit()
|
||||
|
||||
default_branch = get_default_branch()
|
||||
print(f"Switching to {default_branch}")
|
||||
# Checkout the default branch.
|
||||
subprocess.run(["git", "switch", default_branch])
|
||||
|
||||
subprocess.run(["git", "pull"]) # Make sure it's the most up-to-date version.
|
||||
|
||||
# Check which branches are `--merged` and delete them.
|
||||
merged_branch_output = subprocess.run(["git", "branch", "--merged"], capture_output=True)
|
||||
merged_branches = [
|
||||
branch for branch in merged_branch_output.stdout.decode().split("\n") if len(branch)
|
||||
] # Remove zero-length elements.
|
||||
for branch in merged_branches:
|
||||
if branch[0] != "*":
|
||||
subprocess.run(["git", "branch", "-d", branch.strip()])
|
||||
|
||||
# Clear up dangling references to remote branches which are no longer.
|
||||
subprocess.run(["git", "fetch", "--all", "-p"])
|
13
pyproject.toml
Normal file
13
pyproject.toml
Normal file
@ -0,0 +1,13 @@
|
||||
[tool.black]
|
||||
line-length = 120
|
||||
include = '\.pyi?$'
|
||||
exclude ='''
|
||||
/(
|
||||
| \.git
|
||||
| \.mypy_cache
|
||||
)/
|
||||
'''
|
||||
|
||||
[tool.isort]
|
||||
profile = "black"
|
||||
line_length = 120
|
Loading…
Reference in New Issue
Block a user