Browse Source

Support shell globs and tilde expansion (fixes #29).

tags/v0.4
Ben Kurtovic 7 years ago
parent
commit
a702056178
3 changed files with 40 additions and 22 deletions
  1. +1
    -0
      CHANGELOG
  2. +13
    -4
      gitup/config.py
  3. +26
    -18
      gitup/update.py

+ 1
- 0
CHANGELOG View File

@@ -5,6 +5,7 @@ v0.4 (unreleased):
- Added a `--bookmark-file` flag to support multiple bookmark config files.
- Added a `--cleanup` flag to remove old bookmarks that don't exist.
- Added an `--exec` flag to run a shell command on all of your repos.
- Added support for shell glob patterns and tilde expansion in bookmark files.
- Cleaned up the bookmark file format, fixing a related Windows bug. The script
will automatically migrate to the new one.
- Fixed a bug related to Python 3 compatibility.


+ 13
- 4
gitup/config.py View File

@@ -5,6 +5,7 @@

from __future__ import print_function

from glob import glob
import os

from colorama import Fore, Style
@@ -48,6 +49,12 @@ def _save_config_file(bookmarks, config_path=None):
with open(cfg_path, "wb") as config_file:
config_file.write(dump)

def _normalize_path(path):
"""Normalize the given path."""
if path.startswith("~"):
return os.path.normcase(os.path.normpath(path))
return os.path.normcase(os.path.abspath(path))

def get_default_config_path():
"""Return the default path to the configuration file."""
xdg_cfg = os.environ.get("XDG_CONFIG_HOME") or os.path.join("~", ".config")
@@ -60,9 +67,10 @@ def get_bookmarks(config_path=None):
def add_bookmarks(paths, config_path=None):
"""Add a list of paths as bookmarks to the config file."""
config = _load_config_file(config_path)
paths = [_normalize_path(path) for path in paths]

added, exists = [], []
for path in paths:
path = os.path.normcase(os.path.abspath(path))
if path in config:
exists.append(path)
else:
@@ -82,11 +90,11 @@ def add_bookmarks(paths, config_path=None):
def delete_bookmarks(paths, config_path=None):
"""Remove a list of paths from the bookmark config file."""
config = _load_config_file(config_path)
paths = [_normalize_path(path) for path in paths]

deleted, notmarked = [], []
if config:
for path in paths:
path = os.path.normcase(os.path.abspath(path))
if path in config:
config.remove(path)
deleted.append(path)
@@ -94,7 +102,7 @@ def delete_bookmarks(paths, config_path=None):
notmarked.append(path)
_save_config_file(config, config_path)
else:
notmarked = [os.path.abspath(path) for path in paths]
notmarked = paths

if deleted:
print(YELLOW + "Deleted bookmarks:")
@@ -122,7 +130,8 @@ def clean_bookmarks(config_path=None):
print("You have no bookmarks to clean up.")
return

delete = [path for path in bookmarks if not os.path.isdir(path)]
delete = [path for path in bookmarks
if not (os.path.isdir(path) or glob(os.path.expanduser(path)))]
if not delete:
print("All of your bookmarks are valid.")
return


+ 26
- 18
gitup/update.py View File

@@ -5,6 +5,7 @@

from __future__ import print_function

from glob import glob
import os
import shlex

@@ -192,18 +193,20 @@ def _run_command(repo, command):
for line in out[1].splitlines() + out[2].splitlines():
print(INDENT2, line)

def _dispatch_to_subdirs(path, callback, *args):
"""Apply the callback to all git repo subdirectories in the directory."""
def _dispatch_multi(base, paths, callback, *args):
"""Apply the callback to all git repos in the list of paths."""
repos = []
for item in os.listdir(path):
for path in paths:
try:
repo = Repo(os.path.join(path, item))
repo = Repo(path)
except (exc.InvalidGitRepositoryError, exc.NoSuchPathError):
continue
repos.append(repo)

base = os.path.abspath(base)
suffix = "" if len(repos) == 1 else "s"
print(BOLD + path, "({0} repo{1}):".format(len(repos), suffix))
print(BOLD + base, "({0} repo{1}):".format(len(repos), suffix))

for repo in sorted(repos, key=lambda r: os.path.split(r.working_dir)[1]):
callback(repo, *args)

@@ -211,19 +214,25 @@ def _dispatch(path, callback, *args):
"""Apply a callback function on each valid repo in the given path.

Determine whether the directory is a git repo on its own, a directory of
git repositories, or something invalid. If the first, apply the callback on
it; if the second, apply the callback on all repositories contained within;
if the third, print an error.
git repositories, a shell glob pattern, or something invalid. If the first,
apply the callback on it; if the second or third, apply the callback on all
repositories contained within; if the last, print an error.

The given args are passed directly to the callback function after the repo.
"""
path = os.path.expanduser(path)
try:
repo = Repo(path)
except exc.NoSuchPathError:
print(ERROR, BOLD + path, "doesn't exist!")
paths = glob(path)
if paths:
_dispatch_multi(path, paths, callback, *args)
else:
print(ERROR, BOLD + path, "doesn't exist!")
except exc.InvalidGitRepositoryError:
if os.path.isdir(path):
_dispatch_to_subdirs(path, callback, *args)
paths = [os.path.join(path, item) for item in os.listdir(path)]
_dispatch_multi(path, paths, callback, *args)
else:
print(ERROR, BOLD + path, "isn't a repository!")
else:
@@ -232,20 +241,19 @@ def _dispatch(path, callback, *args):

def update_bookmarks(bookmarks, update_args):
"""Loop through and update all bookmarks."""
if bookmarks:
for path in bookmarks:
_dispatch(path, _update_repository, *update_args)
else:
if not bookmarks:
print("You don't have any bookmarks configured! Get help with 'gitup -h'.")
return

for path in bookmarks:
_dispatch(path, _update_repository, *update_args)

def update_directories(paths, update_args):
"""Update a list of directories supplied by command arguments."""
for path in paths:
full_path = os.path.abspath(path)
_dispatch(full_path, _update_repository, *update_args)
_dispatch(path, _update_repository, *update_args)

def run_command(paths, command):
"""Run an arbitrary shell command on all repos."""
for path in paths:
full_path = os.path.abspath(path)
_dispatch(full_path, _run_command, command)
_dispatch(path, _run_command, command)

Loading…
Cancel
Save