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