From f7b15b549561947ee7bb6859ff1ff2832d8b5074 Mon Sep 17 00:00:00 2001 From: Ben Kurtovic Date: Tue, 28 Aug 2018 00:51:10 -0400 Subject: [PATCH 1/6] Version bump; PyPI tweaks. --- CHANGELOG | 4 ++++ README.md | 4 ++++ gitup/__init__.py | 2 +- setup.py | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index f9a9800..c247772 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +v0.6 (unreleased): + +- ... + v0.5 (released August 28, 2018): - Added a `--depth` flag to control recursion depth when searching for diff --git a/README.md b/README.md index eefa4e2..d470a48 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,10 @@ version of git and either Python 2.7 or Python 3 installed. # Installation +With [pip](https://github.com/pypa/pip/): + + pip install gitup + With [Homebrew](http://brew.sh/): brew install gitup diff --git a/gitup/__init__.py b/gitup/__init__.py index b973f47..0467a3c 100644 --- a/gitup/__init__.py +++ b/gitup/__init__.py @@ -10,5 +10,5 @@ gitup: the git repository updater __author__ = "Ben Kurtovic" __copyright__ = "Copyright (C) 2011-2018 Ben Kurtovic" __license__ = "MIT License" -__version__ = "0.5" +__version__ = "0.6.dev0" __email__ = "ben.kurtovic@gmail.com" diff --git a/setup.py b/setup.py index 7b8ce11..cbfc306 100644 --- a/setup.py +++ b/setup.py @@ -25,6 +25,7 @@ setup( author_email = "ben.kurtovic@gmail.com", description = "Easily update multiple git repositories at once", long_description = long_desc, + long_description_content_type = "text/markdown", license = "MIT License", keywords = "git repository pull update", url = "https://github.com/earwig/git-repo-updater", From fea24e0a74169f519e765199918ef12ecb16d1ec Mon Sep 17 00:00:00 2001 From: Ben Kurtovic Date: Sun, 2 Sep 2018 17:43:04 -0400 Subject: [PATCH 2/6] Start working on integrated test suite, refactor --- .gitignore | 8 +++++--- CHANGELOG | 4 +++- Pipfile | 23 +++++++++++++++++++++++ README.md | 2 +- gitup/__main__.py | 2 +- gitup/{script.py => cli.py} | 40 ++++++++++++++++++++++------------------ gitup/config.py | 4 ++-- gitup/migrate.py | 2 +- gitup/test/__init__.py | 10 ++++++++++ gitup/test/test_bookmarks.py | 14 ++++++++++++++ gitup/test/test_cli.py | 24 ++++++++++++++++++++++++ setup.py | 4 +--- 12 files changed, 107 insertions(+), 30 deletions(-) create mode 100644 Pipfile rename gitup/{script.py => cli.py} (84%) create mode 100644 gitup/test/__init__.py create mode 100644 gitup/test/test_bookmarks.py create mode 100644 gitup/test/test_cli.py diff --git a/.gitignore b/.gitignore index cc17441..b0aa89d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ *.egg *.egg-info .DS_Store -__pycache__ -build -dist +Pipfile.lock +__pycache__/ +.pytest_cache/ +build/ +dist/ diff --git a/CHANGELOG b/CHANGELOG index c247772..32fc751 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,8 @@ v0.6 (unreleased): -- ... +- Add an integrated pytest testing suite, runnable with `--selftest`. +- Refactor internals, remove deprecated options, and drop support for + end-of-life Python versions. v0.5 (released August 28, 2018): diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..31e69e1 --- /dev/null +++ b/Pipfile @@ -0,0 +1,23 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[dev-packages] +pylint = "*" +pytest = "*" +twine = "*" + +[packages] +"e1839a8" = {path = ".", editable = true} +GitPython = ">= 2.1.8" +colorama = ">= 0.3.9" + +[requires] +python_version = "3.7" + +[scripts] +test = "pytest gitup -v -rxw" +lint = "pylint --disable=missing-docstring --output-format=colorized gitup" +cloc = "cloc --vcs=git" +build = "python setup.py sdist bdist_wheel --universal" diff --git a/README.md b/README.md index d470a48..3bc5aaf 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ enough to handle several remotes, dirty working directories, diverged local branches, detached HEADs, and more. It was originally created to manage a large collection of projects and deal with sporadic internet access. -gitup should work on OS X, Linux, and Windows. You should have the latest +gitup should work on macOS, Linux, and Windows. You should have the latest version of git and either Python 2.7 or Python 3 installed. # Installation diff --git a/gitup/__main__.py b/gitup/__main__.py index 86df533..af84f8d 100644 --- a/gitup/__main__.py +++ b/gitup/__main__.py @@ -3,7 +3,7 @@ # Copyright (C) 2011-2018 Ben Kurtovic # Released under the terms of the MIT License. See LICENSE for details. -from .script import run +from gitup.cli import run if __name__ == "__main__": run() diff --git a/gitup/script.py b/gitup/cli.py similarity index 84% rename from gitup/script.py rename to gitup/cli.py index 38d7e3e..54c7aca 100644 --- a/gitup/script.py +++ b/gitup/cli.py @@ -10,12 +10,12 @@ import os import platform import sys -from colorama import init as color_init, Fore, Style +from colorama import init as color_init, Style -from . import __version__ -from .config import (get_default_config_path, get_bookmarks, add_bookmarks, - delete_bookmarks, list_bookmarks, clean_bookmarks) -from .update import update_bookmarks, update_directories, run_command +from gitup import __version__ +from gitup.config import (get_default_config_path, get_bookmarks, add_bookmarks, + delete_bookmarks, list_bookmarks, clean_bookmarks) +from gitup.update import update_bookmarks, update_directories, run_command def _decode(path): """Decode the given string using the system's filesystem encoding.""" @@ -23,8 +23,8 @@ def _decode(path): return path return path.decode(sys.getfilesystemencoding()) -def main(): - """Parse arguments and then call the appropriate function(s).""" +def _build_parser(): + """Build and return the argument parser.""" parser = argparse.ArgumentParser( description="Easily update multiple git repositories at once.", epilog=""" @@ -87,25 +87,29 @@ def main(): '-v', '--version', action="version", version="gitup {0} (Python {1})".format( __version__, platform.python_version())) + group_m.add_argument( + '--selftest', action="store_true", + help="run integrated test suite and exit (pytest must be available)") - # TODO: deprecated arguments, for removal in v1.0: - parser.add_argument( - '-m', '--merge', action="store_true", help=argparse.SUPPRESS) - parser.add_argument( - '-r', '--rebase', action="store_true", help=argparse.SUPPRESS) + return parser +def _selftest(): + """Run the integrated test suite with pytest.""" + from .test import run_tests + run_tests() + +def main(): + """Parse arguments and then call the appropriate function(s).""" + parser = _build_parser() color_init(autoreset=True) args = parser.parse_args() print(Style.BRIGHT + "gitup" + Style.RESET_ALL + ": the git-repo-updater") print() - # TODO: remove in v1.0 - if args.merge or args.rebase: - print(Style.BRIGHT + Fore.YELLOW + "Warning:", "--merge and --rebase " - "are deprecated. Branches are only updated if they\ntrack an " - "upstream branch and can be safely fast-forwarded. Use " - "--fetch-only to\navoid updating any branches.\n") + if args.selftest: + _selftest() + return if args.bookmark_file: args.bookmark_file = os.path.expanduser(args.bookmark_file) diff --git a/gitup/config.py b/gitup/config.py index 7eeea7b..0e1e069 100644 --- a/gitup/config.py +++ b/gitup/config.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2011-2016 Ben Kurtovic +# Copyright (C) 2011-2018 Ben Kurtovic # Released under the terms of the MIT License. See LICENSE for details. from __future__ import print_function @@ -10,7 +10,7 @@ import os from colorama import Fore, Style -from .migrate import run_migrations +from gitup.migrate import run_migrations __all__ = ["get_default_config_path", "get_bookmarks", "add_bookmarks", "delete_bookmarks", "list_bookmarks", "clean_bookmarks"] diff --git a/gitup/migrate.py b/gitup/migrate.py index 2de21f8..acea23c 100644 --- a/gitup/migrate.py +++ b/gitup/migrate.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2011-2016 Ben Kurtovic +# Copyright (C) 2011-2018 Ben Kurtovic # Released under the terms of the MIT License. See LICENSE for details. import os diff --git a/gitup/test/__init__.py b/gitup/test/__init__.py new file mode 100644 index 0000000..d632e70 --- /dev/null +++ b/gitup/test/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2011-2018 Ben Kurtovic +# Released under the terms of the MIT License. See LICENSE for details. + +def run_tests(args=None): + import pytest + if args is None: + args = ["-v", "-rxw"] + return pytest.main(args) diff --git a/gitup/test/test_bookmarks.py b/gitup/test/test_bookmarks.py new file mode 100644 index 0000000..3a917ae --- /dev/null +++ b/gitup/test/test_bookmarks.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2011-2018 Ben Kurtovic +# Released under the terms of the MIT License. See LICENSE for details. + +from __future__ import print_function, unicode_literals + +from gitup import config + +def test_empty_list(tmpdir, capsys): + config_path = tmpdir / "config" + config.list_bookmarks(config_path) + captured = capsys.readouterr() + assert captured.out == "You have no bookmarks to display.\n" diff --git a/gitup/test/test_cli.py b/gitup/test/test_cli.py new file mode 100644 index 0000000..c3e5300 --- /dev/null +++ b/gitup/test/test_cli.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2011-2018 Ben Kurtovic +# Released under the terms of the MIT License. See LICENSE for details. + +from __future__ import print_function, unicode_literals + +import platform +import subprocess +import sys + +from gitup import __version__ + +def run_cli(*args): + cmd = [sys.executable, "-m", "gitup"] + list(args) + output = subprocess.check_output(cmd) + return output.strip().decode("utf8") + +def test_cli_version(): + """make sure we're using the right version of gitup""" + output = run_cli("-v") + expected = "gitup {} (Python {})".format( + __version__, platform.python_version()) + assert output == expected diff --git a/setup.py b/setup.py index cbfc306..5cfd4f4 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ with open('README.md') as fp: setup( name = "gitup", packages = find_packages(), - entry_points = {"console_scripts": ["gitup = gitup.script:run"]}, + entry_points = {"console_scripts": ["gitup = gitup.cli:run"]}, install_requires = ["GitPython >= 2.1.8", "colorama >= 0.3.9"], version = __version__, author = "Ben Kurtovic", @@ -41,8 +41,6 @@ setup( "Programming Language :: Python", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.2", - "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", From 5ca6f877204d66b8e2d40fdbb2d217840766aca2 Mon Sep 17 00:00:00 2001 From: smittytone Date: Tue, 26 Feb 2019 08:50:04 +0000 Subject: [PATCH 3/6] Add comment-line remover Accessed by _load_config_file() --- gitup/config.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/gitup/config.py b/gitup/config.py index 0e1e069..51cb4f0 100644 --- a/gitup/config.py +++ b/gitup/config.py @@ -37,8 +37,27 @@ def _load_config_file(config_path=None): except IOError: return [] paths = [path.decode("utf8").strip() for path in paths] + paths = _strip_comment_lines(paths) return [path for path in paths if path] +def _strip_comment_lines(paths): + """Remove any lines starting with #.""" + i = 0 + while i < len(paths): + path = paths[i] + removed_comment = False + for j in range(0, len(path)): + path_char = path[j] + if path_char == " ": + continue + if path_char == "#": + removed_comment = True + paths.pop(i) + break + if removed_comment is False: + i = i + 1 + return paths + def _save_config_file(bookmarks, config_path=None): """Save the bookmarks list to the given config file.""" run_migrations() From da701bc09b1c3911cd7c3707276af3cc52357541 Mon Sep 17 00:00:00 2001 From: smittytone Date: Tue, 26 Feb 2019 12:17:11 +0000 Subject: [PATCH 4/6] Remove comment finder Put it in a better place... --- gitup/config.py | 19 ------------------- gitup/update.py | 36 ++++++++++++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/gitup/config.py b/gitup/config.py index 51cb4f0..0e1e069 100644 --- a/gitup/config.py +++ b/gitup/config.py @@ -37,27 +37,8 @@ def _load_config_file(config_path=None): except IOError: return [] paths = [path.decode("utf8").strip() for path in paths] - paths = _strip_comment_lines(paths) return [path for path in paths if path] -def _strip_comment_lines(paths): - """Remove any lines starting with #.""" - i = 0 - while i < len(paths): - path = paths[i] - removed_comment = False - for j in range(0, len(path)): - path_char = path[j] - if path_char == " ": - continue - if path_char == "#": - removed_comment = True - paths.pop(i) - break - if removed_comment is False: - i = i + 1 - return paths - def _save_config_file(bookmarks, config_path=None): """Save the bookmarks list to the given config file.""" run_migrations() diff --git a/gitup/update.py b/gitup/update.py index 051ef00..123fff7 100644 --- a/gitup/update.py +++ b/gitup/update.py @@ -21,6 +21,7 @@ BOLD = Style.BRIGHT BLUE = Fore.BLUE + BOLD GREEN = Fore.GREEN + BOLD RED = Fore.RED + BOLD +CYAN = Fore.CYAN + BOLD YELLOW = Fore.YELLOW + BOLD RESET = Style.RESET_ALL @@ -256,11 +257,17 @@ def _dispatch(base_path, callback, args): Repo(base) valid = [base] except exc.NoSuchPathError: - paths = glob(base) - if not paths: - print(ERROR, BOLD + base, "doesn't exist!") + if is_comment(base): + comment = get_comment(base) + if len(comment) > 0: + print(CYAN + BOLD + comment) return - valid = _collect(paths, max_depth) + else: + paths = glob(base) + if not paths: + print(ERROR, BOLD + base, "doesn't exist!") + return + valid = _collect(paths, max_depth) except exc.InvalidGitRepositoryError: if not os.path.isdir(base) or args.max_depth == 0: print(ERROR, BOLD + base, "isn't a repository!") @@ -276,6 +283,27 @@ def _dispatch(base_path, callback, args): for name, path in sorted(paths): callback(Repo(path), name, args) +def is_comment(path): + """Does the line start with a # symbol?""" + cs = path.lstrip() + if cs[0] == "#": + return True + return False + +def get_comment(path): + """Return the string minus the comment symbol.""" + rs = "" + cs = path.lstrip() + if len(cs) > 0: + for j in range(0, len(cs)): + c = cs[j] + if c == "#" and len(rs) == 0: + continue + rs = rs + c + if len(rs) > 0: + rs = rs.lstrip() + return rs + def update_bookmarks(bookmarks, args): """Loop through and update all bookmarks.""" if not bookmarks: From ce522521fc6c4cd0000b7e418bff98ee0f8d0da4 Mon Sep 17 00:00:00 2001 From: Ben Kurtovic Date: Sat, 2 Mar 2019 18:19:40 -0500 Subject: [PATCH 5/6] Clean up bookmark comments implementation and document --- CHANGELOG | 1 + LICENSE | 2 +- gitup/update.py | 30 ++++++++---------------------- 3 files changed, 10 insertions(+), 23 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 32fc751..b6d8ce9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,6 @@ v0.6 (unreleased): +- Support simple comments in the bookmarks file. (#51) - Add an integrated pytest testing suite, runnable with `--selftest`. - Refactor internals, remove deprecated options, and drop support for end-of-life Python versions. diff --git a/LICENSE b/LICENSE index fa06940..1ce52db 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (C) 2011-2018 Ben Kurtovic +Copyright (C) 2011-2019 Ben Kurtovic Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/gitup/update.py b/gitup/update.py index 123fff7..eab5344 100644 --- a/gitup/update.py +++ b/gitup/update.py @@ -259,15 +259,14 @@ def _dispatch(base_path, callback, args): except exc.NoSuchPathError: if is_comment(base): comment = get_comment(base) - if len(comment) > 0: + if comment: print(CYAN + BOLD + comment) return - else: - paths = glob(base) - if not paths: - print(ERROR, BOLD + base, "doesn't exist!") - return - valid = _collect(paths, max_depth) + paths = glob(base) + if not paths: + print(ERROR, BOLD + base, "doesn't exist!") + return + valid = _collect(paths, max_depth) except exc.InvalidGitRepositoryError: if not os.path.isdir(base) or args.max_depth == 0: print(ERROR, BOLD + base, "isn't a repository!") @@ -285,24 +284,11 @@ def _dispatch(base_path, callback, args): def is_comment(path): """Does the line start with a # symbol?""" - cs = path.lstrip() - if cs[0] == "#": - return True - return False + return path.lstrip().startswith("#") def get_comment(path): """Return the string minus the comment symbol.""" - rs = "" - cs = path.lstrip() - if len(cs) > 0: - for j in range(0, len(cs)): - c = cs[j] - if c == "#" and len(rs) == 0: - continue - rs = rs + c - if len(rs) > 0: - rs = rs.lstrip() - return rs + return path.lstrip().lstrip("#").strip() def update_bookmarks(bookmarks, args): """Loop through and update all bookmarks.""" From b502b2eaa46a6a10d9db228209f984bb235444a7 Mon Sep 17 00:00:00 2001 From: Ben Kurtovic Date: Fri, 20 Sep 2019 00:26:03 -0400 Subject: [PATCH 6/6] release/0.5.1 --- CHANGELOG | 2 +- gitup/__init__.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index b6d8ce9..24dabc0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,4 @@ -v0.6 (unreleased): +v0.5.1 (released September 20, 2019): - Support simple comments in the bookmarks file. (#51) - Add an integrated pytest testing suite, runnable with `--selftest`. diff --git a/gitup/__init__.py b/gitup/__init__.py index 0467a3c..c54e5a1 100644 --- a/gitup/__init__.py +++ b/gitup/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2011-2018 Ben Kurtovic +# Copyright (C) 2011-2019 Ben Kurtovic # Released under the terms of the MIT License. See LICENSE for details. """ @@ -8,7 +8,7 @@ gitup: the git repository updater """ __author__ = "Ben Kurtovic" -__copyright__ = "Copyright (C) 2011-2018 Ben Kurtovic" +__copyright__ = "Copyright (C) 2011-2019 Ben Kurtovic" __license__ = "MIT License" -__version__ = "0.6.dev0" +__version__ = "0.5.1" __email__ = "ben.kurtovic@gmail.com"