@@ -0,0 +1,37 @@ | |||||
v0.3 (released June 7, 2015): | |||||
- Added support for Python 3. | |||||
- Fixed behavior on bare repositories. | |||||
- Made branch updating code safer in general: only fast-forwardable branches | |||||
tracking upstreams are updated. This deprecates `--merge` and `--rebase`. | |||||
- Added `--fetch-only` to disable branch updating entirely, if desired. | |||||
- Fixed trying to fetch remotes without configured refspecs. | |||||
- Miscellaneous fixes and tweaks. | |||||
v0.2.4 (released May 23, 2015): | |||||
- Follow the XDG Base Directory Specification for the config file. | |||||
- Added installation instructions for Homebrew. | |||||
v0.2.3 (released March 14, 2015): | |||||
- Added support for newer versions of GitPython. | |||||
v0.2.2 (released April 27, 2014): | |||||
- Fixed an error being raised when HEAD is detached. | |||||
v0.2.1 (released April 21, 2014): | |||||
- Fixed a bug when handling errors during a fetch. | |||||
v0.2 (released April 21, 2014): | |||||
- Rewrote backend to use GitPython instead of direct shell calls. Improved | |||||
stability and fixed various bugs. | |||||
- Use colorama for highlighting instead of ANSI escape codes. | |||||
- Added `--current-only`, `--merge`, and `--rebase` options. | |||||
v0.1 (released June 7, 2011): | |||||
- Initial release. |
@@ -1,4 +1,4 @@ | |||||
Copyright (C) 2011-2014 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
Copyright (C) 2011-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
of this software and associated documentation files (the "Software"), to deal | of this software and associated documentation files (the "Software"), to deal | ||||
@@ -6,13 +6,13 @@ directories, and more, hopefully providing a great way to get everything | |||||
up-to-date for short periods of internet access between long periods of none. | up-to-date for short periods of internet access between long periods of none. | ||||
gitup should work on OS X, Linux, and Windows. You should have the latest | gitup should work on OS X, Linux, and Windows. You should have the latest | ||||
version of git and at least Python 2.7 installed. | |||||
version of git and either Python 2.7 or Python 3 installed. | |||||
# Installation | # Installation | ||||
With [Homebrew](http://brew.sh/): | With [Homebrew](http://brew.sh/): | ||||
brew install pr0d1r2/contrib/gitup && brew link gitup | |||||
brew install gitup | |||||
## From source | ## From source | ||||
@@ -82,14 +82,14 @@ Update all git repositories in your current directory: | |||||
gitup . | gitup . | ||||
By default, gitup will fetch all remotes in a repository. Pass `--current-only` | By default, gitup will fetch all remotes in a repository. Pass `--current-only` | ||||
(or `-c`) to make it only fetch the remote tracked by the current branch. | |||||
(or `-c`) to make it fetch _only_ the remote tracked by the current branch. | |||||
gitup will _merge_ upstream branches by default unless `pull.rebase` or | |||||
`branch.<name>.rebase` is specified in git's config. Pass `--rebase` or `-r` to | |||||
make it always _rebase_ (like doing `git pull --rebase=preserve`). Pass | |||||
`--merge` or `-m` to make it always merge. | |||||
Also by default, gitup will try to fast-forward all branches that have | |||||
upstreams configured. It will always skip branches where this is not possible | |||||
(e.g. dirty working directory or a merge/rebase is required). Pass | |||||
`--fetch-only` (or `-f`) to only fetch remotes. | |||||
For a list of all command arguments and abbreviations: | |||||
For a full list of all command arguments and abbreviations: | |||||
gitup --help | gitup --help | ||||
@@ -1,14 +1,14 @@ | |||||
# -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
# | # | ||||
# Copyright (C) 2011-2014 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
# See the LICENSE file for details. | |||||
# Copyright (C) 2011-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
# Released under the terms of the MIT License. See LICENSE for details. | |||||
""" | """ | ||||
gitup: the git repository updater | gitup: the git repository updater | ||||
""" | """ | ||||
__author__ = "Ben Kurtovic" | __author__ = "Ben Kurtovic" | ||||
__copyright__ = "Copyright (C) 2011-2014 Ben Kurtovic" | |||||
__copyright__ = "Copyright (C) 2011-2015 Ben Kurtovic" | |||||
__license__ = "MIT License" | __license__ = "MIT License" | ||||
__version__ = "0.2.4" | |||||
__version__ = "0.3" | |||||
__email__ = "ben.kurtovic@gmail.com" | __email__ = "ben.kurtovic@gmail.com" |
@@ -1,13 +1,17 @@ | |||||
# -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
# | # | ||||
# Copyright (C) 2011-2014 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
# See the LICENSE file for details. | |||||
# Copyright (C) 2011-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
# Released under the terms of the MIT License. See LICENSE for details. | |||||
from __future__ import print_function | from __future__ import print_function | ||||
import ConfigParser as configparser | |||||
import os | import os | ||||
try: | |||||
import configparser | |||||
except ImportError: # Python 2 | |||||
import ConfigParser as configparser | |||||
from colorama import Fore, Style | from colorama import Fore, Style | ||||
__all__ = ["get_bookmarks", "add_bookmarks", "delete_bookmarks", | __all__ = ["get_bookmarks", "add_bookmarks", "delete_bookmarks", | ||||
@@ -58,7 +62,7 @@ def get_bookmarks(): | |||||
"""Get a list of all bookmarks, or an empty list if there are none.""" | """Get a list of all bookmarks, or an empty list if there are none.""" | ||||
config = _load_config_file() | config = _load_config_file() | ||||
try: | try: | ||||
return config.items("bookmarks") | |||||
return [path for path, _ in config.items("bookmarks")] | |||||
except configparser.NoSectionError: | except configparser.NoSectionError: | ||||
return [] | return [] | ||||
@@ -119,7 +123,7 @@ def list_bookmarks(): | |||||
bookmarks = get_bookmarks() | bookmarks = get_bookmarks() | ||||
if bookmarks: | if bookmarks: | ||||
print(YELLOW + "Current bookmarks:") | print(YELLOW + "Current bookmarks:") | ||||
for bookmark_path, _ in bookmarks: | |||||
for bookmark_path in bookmarks: | |||||
print(INDENT1, bookmark_path) | print(INDENT1, bookmark_path) | ||||
else: | else: | ||||
print("You have no bookmarks to display.") | print("You have no bookmarks to display.") |
@@ -1,15 +1,15 @@ | |||||
# -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
# | # | ||||
# Copyright (C) 2011-2014 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
# See the LICENSE file for details. | |||||
# Copyright (C) 2011-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
# Released under the terms of the MIT License. See LICENSE for details. | |||||
from __future__ import print_function | from __future__ import print_function | ||||
import argparse | import argparse | ||||
from colorama import init as color_init, Style | |||||
from colorama import init as color_init, Fore, Style | |||||
from . import __version__, __email__ | |||||
from . import __version__ | |||||
from .config import (get_bookmarks, add_bookmarks, delete_bookmarks, | from .config import (get_bookmarks, add_bookmarks, delete_bookmarks, | ||||
list_bookmarks) | list_bookmarks) | ||||
from .update import update_bookmarks, update_directories | from .update import update_bookmarks, update_directories | ||||
@@ -17,16 +17,16 @@ from .update import update_bookmarks, update_directories | |||||
def main(): | def main(): | ||||
"""Parse arguments and then call the appropriate function(s).""" | """Parse arguments and then call the appropriate function(s).""" | ||||
parser = argparse.ArgumentParser( | parser = argparse.ArgumentParser( | ||||
description="""Easily update multiple git repositories at once.""", | |||||
description="Easily update multiple git repositories at once.", | |||||
epilog=""" | epilog=""" | ||||
Both relative and absolute paths are accepted by all arguments. | Both relative and absolute paths are accepted by all arguments. | ||||
Questions? Comments? Email the author at {0}.""".format(__email__), | |||||
Direct bug reports and feature requests to: | |||||
https://github.com/earwig/git-repo-updater.""", | |||||
add_help=False) | add_help=False) | ||||
group_u = parser.add_argument_group("updating repositories") | group_u = parser.add_argument_group("updating repositories") | ||||
group_b = parser.add_argument_group("bookmarking") | group_b = parser.add_argument_group("bookmarking") | ||||
group_m = parser.add_argument_group("miscellaneous") | group_m = parser.add_argument_group("miscellaneous") | ||||
rebase_or_merge = group_u.add_mutually_exclusive_group() | |||||
group_u.add_argument( | group_u.add_argument( | ||||
'directories_to_update', nargs="*", metavar="path", | 'directories_to_update', nargs="*", metavar="path", | ||||
@@ -38,13 +38,9 @@ def main(): | |||||
group_u.add_argument( | group_u.add_argument( | ||||
'-c', '--current-only', action="store_true", help="""only fetch the | '-c', '--current-only', action="store_true", help="""only fetch the | ||||
remote tracked by the current branch instead of all remotes""") | remote tracked by the current branch instead of all remotes""") | ||||
rebase_or_merge.add_argument( | |||||
'-r', '--rebase', action="store_true", help="""always rebase upstream | |||||
branches instead of following `pull.rebase` and `branch.<name>.rebase` | |||||
in git config (like `git pull --rebase=preserve`)""") | |||||
rebase_or_merge.add_argument( | |||||
'-m', '--merge', action="store_true", help="""like --rebase, but merge | |||||
instead""") | |||||
group_u.add_argument( | |||||
'-f', '--fetch-only', action="store_true", | |||||
help="only fetch remotes, don't try to fast-forward any branches") | |||||
group_b.add_argument( | group_b.add_argument( | ||||
'-a', '--add', dest="bookmarks_to_add", nargs="+", metavar="path", | '-a', '--add', dest="bookmarks_to_add", nargs="+", metavar="path", | ||||
@@ -55,19 +51,33 @@ def main(): | |||||
group_b.add_argument( | group_b.add_argument( | ||||
'-l', '--list', dest="list_bookmarks", action="store_true", | '-l', '--list', dest="list_bookmarks", action="store_true", | ||||
help="list current bookmarks") | help="list current bookmarks") | ||||
group_m.add_argument( | group_m.add_argument( | ||||
'-h', '--help', action="help", help="show this help message and exit") | '-h', '--help', action="help", help="show this help message and exit") | ||||
group_m.add_argument( | group_m.add_argument( | ||||
'-v', '--version', action="version", | '-v', '--version', action="version", | ||||
version="gitup version " + __version__) | |||||
version="gitup " + __version__) | |||||
# 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) | |||||
color_init(autoreset=True) | color_init(autoreset=True) | ||||
args = parser.parse_args() | args = parser.parse_args() | ||||
update_args = args.current_only, args.rebase, args.merge | |||||
update_args = args.current_only, args.fetch_only | |||||
print(Style.BRIGHT + "gitup" + Style.RESET_ALL + ": the git-repo-updater") | print(Style.BRIGHT + "gitup" + Style.RESET_ALL + ": the git-repo-updater") | ||||
print() | 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") | |||||
acted = False | acted = False | ||||
if args.bookmarks_to_add: | if args.bookmarks_to_add: | ||||
add_bookmarks(args.bookmarks_to_add) | add_bookmarks(args.bookmarks_to_add) | ||||
@@ -1,7 +1,7 @@ | |||||
# -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
# | # | ||||
# Copyright (C) 2011-2014 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
# See the LICENSE file for details. | |||||
# Copyright (C) 2011-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
# Released under the terms of the MIT License. See LICENSE for details. | |||||
from __future__ import print_function | from __future__ import print_function | ||||
@@ -53,34 +53,6 @@ class _ProgressMonitor(RemoteProgress): | |||||
print(str(cur_count), end=end) | print(str(cur_count), end=end) | ||||
class _Stasher(object): | |||||
"""Manages the stash state of a given repository.""" | |||||
def __init__(self, repo): | |||||
self._repo = repo | |||||
self._clean = self._stashed = False | |||||
def clean(self): | |||||
"""Ensure the working directory is clean, so we can do checkouts.""" | |||||
if not self._clean: | |||||
res = self._repo.git.stash("--all") | |||||
self._clean = True | |||||
if res != "No local changes to save": | |||||
self._stashed = True | |||||
def restore(self): | |||||
"""Restore the pre-stash state.""" | |||||
if self._stashed: | |||||
self._repo.git.stash("pop", "--index") | |||||
def _read_config(repo, attr): | |||||
"""Read an attribute from git config.""" | |||||
try: | |||||
return repo.git.config("--get", attr) | |||||
except exc.GitCommandError: | |||||
return None | |||||
def _fetch_remotes(remotes): | def _fetch_remotes(remotes): | ||||
"""Fetch a list of remotes, displaying progress info along the way.""" | """Fetch a list of remotes, displaying progress info along the way.""" | ||||
def _get_name(ref): | def _get_name(ref): | ||||
@@ -94,6 +66,11 @@ def _fetch_remotes(remotes): | |||||
for remote in remotes: | for remote in remotes: | ||||
print(INDENT2, "Fetching", BOLD + remote.name, end="") | print(INDENT2, "Fetching", BOLD + remote.name, end="") | ||||
if not remote.config_reader.has_option("fetch"): | |||||
print(":", YELLOW + "skipped:", "no configured refspec.") | |||||
continue | |||||
try: | try: | ||||
results = remote.fetch(progress=_ProgressMonitor()) | results = remote.fetch(progress=_ProgressMonitor()) | ||||
except exc.GitCommandError as err: | except exc.GitCommandError as err: | ||||
@@ -117,56 +94,7 @@ def _fetch_remotes(remotes): | |||||
rlist.append("{0} ({1})".format(colored, ", ".join(names))) | rlist.append("{0} ({1})".format(colored, ", ".join(names))) | ||||
print(":", (", ".join(rlist) if rlist else up_to_date) + ".") | print(":", (", ".join(rlist) if rlist else up_to_date) + ".") | ||||
def _is_up_to_date(repo, branch, upstream): | |||||
"""Return whether *branch* is up-to-date with its *upstream*.""" | |||||
base = repo.git.merge_base(branch.commit, upstream.commit) | |||||
return repo.commit(base) == upstream.commit | |||||
def _rebase(repo, name): | |||||
"""Rebase the current HEAD of *repo* onto the branch *name*.""" | |||||
print(GREEN + "rebasing...", end="") | |||||
try: | |||||
res = repo.git.rebase(name, "--preserve-merges") | |||||
except exc.GitCommandError as err: | |||||
msg = err.stderr.replace("\n", " ").strip() | |||||
if not msg.endswith("."): | |||||
msg += "." | |||||
if "unstaged changes" in msg: | |||||
print(RED + " error:", "unstaged changes.") | |||||
elif "uncommitted changes" in msg: | |||||
print(RED + " error:", "uncommitted changes.") | |||||
else: | |||||
try: | |||||
repo.git.rebase("--abort") | |||||
except exc.GitCommandError: | |||||
pass | |||||
print(RED + " error:", msg if msg else "rebase conflict.", | |||||
"Aborted.") | |||||
else: | |||||
print("\b" * 6 + " " * 6 + "\b" * 6 + GREEN + "ed", end=".\n") | |||||
def _merge(repo, name): | |||||
"""Merge the branch *name* into the current HEAD of *repo*.""" | |||||
print(GREEN + "merging...", end="") | |||||
try: | |||||
repo.git.merge(name) | |||||
except exc.GitCommandError as err: | |||||
msg = err.stderr.replace("\n", " ").strip() | |||||
if not msg.endswith("."): | |||||
msg += "." | |||||
if "local changes" in msg and "would be overwritten" in msg: | |||||
print(RED + " error:", "uncommitted changes.") | |||||
else: | |||||
try: | |||||
repo.git.merge("--abort") | |||||
except exc.GitCommandError: | |||||
pass | |||||
print(RED + " error:", msg if msg else "merge conflict.", | |||||
"Aborted.") | |||||
else: | |||||
print("\b" * 6 + " " * 6 + "\b" * 6 + GREEN + "ed", end=".\n") | |||||
def _update_branch(repo, branch, merge, rebase, stasher=None): | |||||
def _update_branch(repo, branch, is_active=False): | |||||
"""Update a single branch.""" | """Update a single branch.""" | ||||
print(INDENT2, "Updating", BOLD + branch.name, end=": ") | print(INDENT2, "Updating", BOLD + branch.name, end=": ") | ||||
upstream = branch.tracking_branch() | upstream = branch.tracking_branch() | ||||
@@ -179,43 +107,39 @@ def _update_branch(repo, branch, merge, rebase, stasher=None): | |||||
except ValueError: | except ValueError: | ||||
print(YELLOW + "skipped:", "branch has no revisions.") | print(YELLOW + "skipped:", "branch has no revisions.") | ||||
return | return | ||||
if _is_up_to_date(repo, branch, upstream): | |||||
base = repo.git.merge_base(branch.commit, upstream.commit) | |||||
if repo.commit(base) == upstream.commit: | |||||
print(BLUE + "up to date", end=".\n") | print(BLUE + "up to date", end=".\n") | ||||
return | return | ||||
if stasher: | |||||
stasher.clean() | |||||
branch.checkout() | |||||
config_attr = "branch.{0}.rebase".format(branch.name) | |||||
if not merge and (rebase or _read_config(repo, config_attr)): | |||||
_rebase(repo, upstream.name) | |||||
else: | |||||
_merge(repo, upstream.name) | |||||
def _update_branches(repo, active, merge, rebase): | |||||
"""Update a list of branches.""" | |||||
if active: | |||||
_update_branch(repo, active, merge, rebase) | |||||
branches = set(repo.heads) - {active} | |||||
if branches: | |||||
stasher = _Stasher(repo) | |||||
if is_active: | |||||
try: | try: | ||||
for branch in sorted(branches, key=lambda b: b.name): | |||||
_update_branch(repo, branch, merge, rebase, stasher) | |||||
finally: | |||||
if active: | |||||
active.checkout() | |||||
stasher.restore() | |||||
repo.git.merge(upstream.name, ff_only=True) | |||||
print(GREEN + "done", end=".\n") | |||||
except exc.GitCommandError as err: | |||||
msg = err.stderr | |||||
if "local changes" in msg and "would be overwritten" in msg: | |||||
print(YELLOW + "skipped:", "uncommitted changes.") | |||||
else: | |||||
print(YELLOW + "skipped:", "not possible to fast-forward.") | |||||
else: | |||||
status = repo.git.merge_base( | |||||
branch.commit, upstream.commit, is_ancestor=True, | |||||
with_extended_output=True, with_exceptions=False)[0] | |||||
if status != 0: | |||||
print(YELLOW + "skipped:", "not possible to fast-forward.") | |||||
else: | |||||
repo.git.branch(branch.name, upstream.name, force=True) | |||||
print(GREEN + "done", end=".\n") | |||||
def _update_repository(repo, current_only=False, rebase=False, merge=False): | |||||
def _update_repository(repo, current_only=False, fetch_only=False): | |||||
"""Update a single git repository by fetching remotes and rebasing/merging. | """Update a single git repository by fetching remotes and rebasing/merging. | ||||
The specific actions depend on the arguments given. We will fetch all | The specific actions depend on the arguments given. We will fetch all | ||||
remotes if *current_only* is ``False``, or only the remote tracked by the | remotes if *current_only* is ``False``, or only the remote tracked by the | ||||
current branch if ``True``. By default, we will merge unless | |||||
``pull.rebase`` or ``branch.<name>.rebase`` is set in config; *rebase* will | |||||
cause us to always rebase with ``--preserve-merges``, and *merge* will | |||||
cause us to always merge. | |||||
current branch if ``True``. If *fetch_only* is ``False``, we will also | |||||
update all fast-forwardable branches that are tracking valid upstreams. | |||||
""" | """ | ||||
print(INDENT1, BOLD + os.path.split(repo.working_dir)[1] + ":") | print(INDENT1, BOLD + os.path.split(repo.working_dir)[1] + ":") | ||||
@@ -224,22 +148,28 @@ def _update_repository(repo, current_only=False, rebase=False, merge=False): | |||||
except TypeError: # Happens when HEAD is detached | except TypeError: # Happens when HEAD is detached | ||||
active = None | active = None | ||||
if current_only: | if current_only: | ||||
ref = active.tracking_branch() if active else None | |||||
if not active: | |||||
print(INDENT2, ERROR, | |||||
"--current-only doesn't make sense with a detached HEAD.") | |||||
return | |||||
ref = active.tracking_branch() | |||||
if not ref: | if not ref: | ||||
print(INDENT2, ERROR, "no remote tracked by current branch.") | print(INDENT2, ERROR, "no remote tracked by current branch.") | ||||
return | return | ||||
remotes = [repo.remotes[ref.remote_name]] | remotes = [repo.remotes[ref.remote_name]] | ||||
else: | else: | ||||
remotes = repo.remotes | remotes = repo.remotes | ||||
if not remotes: | if not remotes: | ||||
print(INDENT2, ERROR, "no remotes configured to pull from.") | |||||
print(INDENT2, ERROR, "no remotes configured to fetch.") | |||||
return | return | ||||
rebase = rebase or _read_config(repo, "pull.rebase") | |||||
_fetch_remotes(remotes) | _fetch_remotes(remotes) | ||||
_update_branches(repo, active, merge, rebase) | |||||
def _update_subdirectories(path, long_name, update_args): | |||||
if not fetch_only: | |||||
for branch in sorted(repo.heads, key=lambda b: b.name): | |||||
_update_branch(repo, branch, branch == active) | |||||
def _update_subdirectories(path, update_args): | |||||
"""Update all subdirectories that are git repos in a given directory.""" | """Update all subdirectories that are git repos in a given directory.""" | ||||
repos = [] | repos = [] | ||||
for item in os.listdir(path): | for item in os.listdir(path): | ||||
@@ -249,13 +179,12 @@ def _update_subdirectories(path, long_name, update_args): | |||||
continue | continue | ||||
repos.append(repo) | repos.append(repo) | ||||
suffix = "ies" if len(repos) != 1 else "y" | |||||
print(long_name[0].upper() + long_name[1:], | |||||
"contains {0} git repositor{1}:".format(len(repos), suffix)) | |||||
suffix = "" if len(repos) == 1 else "s" | |||||
print(BOLD + path, "({0} repo{1}):".format(len(repos), suffix)) | |||||
for repo in sorted(repos, key=lambda r: os.path.split(r.working_dir)[1]): | for repo in sorted(repos, key=lambda r: os.path.split(r.working_dir)[1]): | ||||
_update_repository(repo, *update_args) | _update_repository(repo, *update_args) | ||||
def _update_directory(path, update_args, is_bookmark=False): | |||||
def _update_directory(path, update_args): | |||||
"""Update a particular directory. | """Update a particular directory. | ||||
Determine whether the directory is a git repo on its own, a directory of | Determine whether the directory is a git repo on its own, a directory of | ||||
@@ -263,29 +192,24 @@ def _update_directory(path, update_args, is_bookmark=False): | |||||
repository; if the second, update all repositories contained within; if the | repository; if the second, update all repositories contained within; if the | ||||
third, print an error. | third, print an error. | ||||
""" | """ | ||||
dir_type = "bookmark" if is_bookmark else "directory" | |||||
long_name = dir_type + ' "' + BOLD + path + RESET + '"' | |||||
try: | try: | ||||
repo = Repo(path) | repo = Repo(path) | ||||
except exc.NoSuchPathError: | except exc.NoSuchPathError: | ||||
print(ERROR, long_name, "doesn't exist!") | |||||
print(ERROR, BOLD + path, "doesn't exist!") | |||||
except exc.InvalidGitRepositoryError: | except exc.InvalidGitRepositoryError: | ||||
if os.path.isdir(path): | if os.path.isdir(path): | ||||
_update_subdirectories(path, long_name, update_args) | |||||
_update_subdirectories(path, update_args) | |||||
else: | else: | ||||
print(ERROR, long_name, "isn't a repository!") | |||||
print(ERROR, BOLD + path, "isn't a repository!") | |||||
else: | else: | ||||
long_name = (dir_type.capitalize() + ' "' + BOLD + repo.working_dir + | |||||
RESET + '"') | |||||
print(long_name, "is a git repository:") | |||||
print(BOLD + repo.working_dir, "(1 repo):") | |||||
_update_repository(repo, *update_args) | _update_repository(repo, *update_args) | ||||
def update_bookmarks(bookmarks, update_args): | def update_bookmarks(bookmarks, update_args): | ||||
"""Loop through and update all bookmarks.""" | """Loop through and update all bookmarks.""" | ||||
if bookmarks: | if bookmarks: | ||||
for path, name in bookmarks: | |||||
_update_directory(path, update_args, is_bookmark=True) | |||||
for path in bookmarks: | |||||
_update_directory(path, update_args) | |||||
else: | else: | ||||
print("You don't have any bookmarks configured! Get help with 'gitup -h'.") | print("You don't have any bookmarks configured! Get help with 'gitup -h'.") | ||||
@@ -293,4 +217,4 @@ def update_directories(paths, update_args): | |||||
"""Update a list of directories supplied by command arguments.""" | """Update a list of directories supplied by command arguments.""" | ||||
for path in paths: | for path in paths: | ||||
full_path = os.path.abspath(path) | full_path = os.path.abspath(path) | ||||
_update_directory(full_path, update_args, is_bookmark=False) | |||||
_update_directory(full_path, update_args) |
@@ -1,7 +1,7 @@ | |||||
# -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
# | # | ||||
# Copyright (C) 2011-2014 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
# See the LICENSE file for details. | |||||
# Copyright (C) 2011-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
# Released under the terms of the MIT License. See LICENSE for details. | |||||
import sys | import sys | ||||
@@ -23,7 +23,7 @@ setup( | |||||
version = __version__, | version = __version__, | ||||
author = "Ben Kurtovic", | author = "Ben Kurtovic", | ||||
author_email = "ben.kurtovic@gmail.com", | author_email = "ben.kurtovic@gmail.com", | ||||
description = "Easily pull to multiple git repositories at once.", | |||||
description = "Easily pull to multiple git repositories at once", | |||||
long_description = long_desc, | long_description = long_desc, | ||||
license = "MIT License", | license = "MIT License", | ||||
keywords = "git repository pull update", | keywords = "git repository pull update", | ||||
@@ -35,8 +35,14 @@ setup( | |||||
"Natural Language :: English", | "Natural Language :: English", | ||||
"Operating System :: MacOS :: MacOS X", | "Operating System :: MacOS :: MacOS X", | ||||
"Operating System :: POSIX :: Linux", | "Operating System :: POSIX :: Linux", | ||||
"Operating System :: Microsoft :: Windows", | |||||
"Programming Language :: Python", | "Programming Language :: Python", | ||||
"Programming Language :: Python :: 2.7", | "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", | |||||
"Topic :: Software Development :: Version Control" | "Topic :: Software Development :: Version Control" | ||||
] | ] | ||||
) | ) |