| @@ -20,14 +20,28 @@ __email__ = "ben.kurtovic@verizon.net" | |||||
| config_filename = os.path.join(os.path.expanduser("~"), ".gitup") | config_filename = os.path.join(os.path.expanduser("~"), ".gitup") | ||||
| ansi = { # ANSI escape codes to make terminal output colorful | |||||
| "reset": "\x1b[0m", | |||||
| "bold": "\x1b[1m", | |||||
| "red": "\x1b[1m\x1b[31m", | |||||
| "green": "\x1b[1m\x1b[32m", | |||||
| "yellow": "\x1b[1m\x1b[33m", | |||||
| "blue": "\x1b[1m\x1b[34m", | |||||
| } | |||||
| # Text formatting functions | |||||
| bold = lambda t: style_text(t, "bold") | |||||
| red = lambda t: style_text(t, "red") | |||||
| green = lambda t: style_text(t, "green") | |||||
| yellow = lambda t: style_text(t, "yellow") | |||||
| blue = lambda t: style_text(t, "blue") | |||||
| def style_text(text, effect): | |||||
| """Give a text string a certain effect, such as boldness, or a color.""" | |||||
| ansi = { # ANSI escape codes to make terminal output fancy | |||||
| "reset": "\x1b[0m", | |||||
| "bold": "\x1b[1m", | |||||
| "red": "\x1b[1m\x1b[31m", | |||||
| "green": "\x1b[1m\x1b[32m", | |||||
| "yellow": "\x1b[1m\x1b[33m", | |||||
| "blue": "\x1b[1m\x1b[34m", | |||||
| } | |||||
| try: # pad text with effect, unless effect does not exist | |||||
| return "{}{}{}".format(ansi[effect], text, ansi['reset']) | |||||
| except KeyError: | |||||
| return text | |||||
| def out(indent, msg): | def out(indent, msg): | ||||
| """Print a message at a given indentation level.""" | """Print a message at a given indentation level.""" | ||||
| @@ -57,7 +71,7 @@ def directory_is_git_repo(directory_path): | |||||
| def update_repository(repo_path, repo_name): | def update_repository(repo_path, repo_name): | ||||
| """Update a single git repository by pulling from the remote.""" | """Update a single git repository by pulling from the remote.""" | ||||
| out(1, "{}{}{}:".format(ansi['bold'], repo_name, ansi['reset'])) | |||||
| out(1, bold(repo_name) + ":") | |||||
| os.chdir(repo_path) # cd into our folder so git commands target the correct | os.chdir(repo_path) # cd into our folder so git commands target the correct | ||||
| # repo | # repo | ||||
| @@ -67,34 +81,33 @@ def update_repository(repo_path, repo_name): | |||||
| # anything to pull, but | # anything to pull, but | ||||
| # don't do it yet | # don't do it yet | ||||
| except subprocess.CalledProcessError: | except subprocess.CalledProcessError: | ||||
| out(2, """{}Error:{} cannot fetch; do you have a remote repository | |||||
| configured correctly?""".format(ansi['red'], ansi['reset'])) | |||||
| out(2, red("Error: ") + "cannot fetch; do you have a remote " + | |||||
| "repository configured correctly?") | |||||
| return | return | ||||
| try: | try: | ||||
| last = exec_shell("git log -n 1 --pretty=\"%ar\"") # last commit time | |||||
| last_commit = exec_shell("git log -n 1 --pretty=\"%ar\"") | |||||
| except subprocess.CalledProcessError: | except subprocess.CalledProcessError: | ||||
| last = "never" # couldn't get a log, so no commits | |||||
| last_commit = "never" # couldn't get a log, so no commits | |||||
| if not dry_fetch: # no new changes to pull | if not dry_fetch: # no new changes to pull | ||||
| out(2, "{}No new changes.{} Last commit was {}.".format(ansi['blue'], | |||||
| ansi['reset'], last)) | |||||
| out(2, blue("No new changes.") + " Last commit was {}.".format( | |||||
| last_commit)) | |||||
| else: # stuff has happened! | else: # stuff has happened! | ||||
| out(2, "There are new changes upstream...") | out(2, "There are new changes upstream...") | ||||
| status = exec_shell("git status") | status = exec_shell("git status") | ||||
| if status.endswith("nothing to commit (working directory clean)"): | if status.endswith("nothing to commit (working directory clean)"): | ||||
| out(2, "{}Pulling new changes...{}".format(ansi['green'], | |||||
| ansi['reset'])) | |||||
| out(2, green("Pulling new changes...")) | |||||
| result = exec_shell("git pull") | result = exec_shell("git pull") | ||||
| out(2, "The following changes have been made since {}:".format( | out(2, "The following changes have been made since {}:".format( | ||||
| last)) | |||||
| last_commit)) | |||||
| print result | print result | ||||
| else: | else: | ||||
| out(2, """{}Warning:{} You have uncommitted changes in this | |||||
| repository!""".format(ansi['red'], ansi['reset'])) | |||||
| out(2, red("Warning: ") + "you have uncommitted changes in this " + | |||||
| "repository!") | |||||
| out(2, "Ignoring.") | out(2, "Ignoring.") | ||||
| def update_directory(dir_path, dir_name, is_bookmark=False): | def update_directory(dir_path, dir_name, is_bookmark=False): | ||||
| @@ -103,32 +116,28 @@ def update_directory(dir_path, dir_name, is_bookmark=False): | |||||
| of git repositories. If the former, update the single repository; if the | of git repositories. If the former, update the single repository; if the | ||||
| latter, update all repositories contained within.""" | latter, update all repositories contained within.""" | ||||
| if is_bookmark: | if is_bookmark: | ||||
| dir_source = "Bookmark" # where did we get this directory from? | |||||
| dir_type = "bookmark" # where did we get this directory from? | |||||
| else: | else: | ||||
| dir_source = "Directory" | |||||
| dir_type = "directory" | |||||
| dir_long_name = "{} '{}'".format(dir_type, bold(dir_path)) | |||||
| try: | try: | ||||
| os.listdir(dir_path) # test if we can access this directory | os.listdir(dir_path) # test if we can access this directory | ||||
| except OSError: | except OSError: | ||||
| out(0, "{}Error:{} cannot enter {} '{}{}{}'; does it exist?".format( | |||||
| ansi['red'], ansi['reset'], dir_source.lower(), ansi['bold'], dir_path, | |||||
| ansi['reset'])) | |||||
| out(0, red("Error: ") + "cannot enter {}; does it exist?".format( | |||||
| dir_long_name)) | |||||
| return | return | ||||
| if not os.path.isdir(dir_path): | if not os.path.isdir(dir_path): | ||||
| if os.path.exists(dir_path): | if os.path.exists(dir_path): | ||||
| error_message = "is not a directory" | |||||
| out(0, red("Error: ") + dir_long_name + " is not a directory!") | |||||
| else: | else: | ||||
| error_message = "does not exist" | |||||
| out(0, "{}Error{}: {} '{}{}{}' {}!".format(ansi['red'], ansi['reset'], | |||||
| dir_source, ansi['bold'], dir_path, ansi['reset'], | |||||
| error_message)) | |||||
| out(0, red("Error: ") + dir_long_name + " does not exist!") | |||||
| return | return | ||||
| if directory_is_git_repo(dir_path): | if directory_is_git_repo(dir_path): | ||||
| out(0, "{} '{}{}{}' is a git repository:".format(dir_source, | |||||
| ansi['bold'], dir_path, ansi['reset'])) | |||||
| out(0, dir_long_name.capitalize() + " is a git repository:") | |||||
| update_repository(dir_path, dir_name) | update_repository(dir_path, dir_name) | ||||
| else: | else: | ||||
| @@ -141,15 +150,13 @@ def update_directory(dir_path, dir_name, is_bookmark=False): | |||||
| if directory_is_git_repo(repo_path): # filter out non-repositories | if directory_is_git_repo(repo_path): # filter out non-repositories | ||||
| repositories.append((repo_path, repo_name)) | repositories.append((repo_path, repo_name)) | ||||
| repo_count = len(repositories) | |||||
| if repo_count == 1: | |||||
| pluralize = "repository" | |||||
| num_of_repos = len(repositories) | |||||
| if num_of_repos == 1: | |||||
| out(0, dir_long_name.capitalize() + " contains 1 git repository:") | |||||
| else: | else: | ||||
| pluralize = "repositories" | |||||
| out(0, "{} '{}{}{}' contains {} git {}:".format(dir_source, | |||||
| ansi['bold'], dir_path, ansi['reset'], repo_count, pluralize)) | |||||
| out(0, dir_long_name.capitalize() + | |||||
| " contains {} git repositories:".format(num_of_repos)) | |||||
| repositories.sort() # go alphabetically instead of randomly | repositories.sort() # go alphabetically instead of randomly | ||||
| for repo_path, repo_name in repositories: | for repo_path, repo_name in repositories: | ||||
| update_repository(repo_path, repo_name) | update_repository(repo_path, repo_name) | ||||
| @@ -172,8 +179,8 @@ def update_bookmarks(): | |||||
| for bookmark_path, bookmark_name in bookmarks: | for bookmark_path, bookmark_name in bookmarks: | ||||
| update_directory(bookmark_path, bookmark_name, is_bookmark=True) | update_directory(bookmark_path, bookmark_name, is_bookmark=True) | ||||
| else: | else: | ||||
| out(0, """You don't have any bookmarks configured! Get help with | |||||
| 'gitup -h'.""") | |||||
| out(0, "You don't have any bookmarks configured! Get help with " + | |||||
| "'gitup -h'.") | |||||
| def load_config_file(): | def load_config_file(): | ||||
| """Read the file storing our config options from config_filename and return | """Read the file storing our config options from config_filename and return | ||||
| @@ -196,7 +203,8 @@ def add_bookmarks(paths): | |||||
| if not config.has_section("bookmarks"): | if not config.has_section("bookmarks"): | ||||
| config.add_section("bookmarks") | config.add_section("bookmarks") | ||||
| out(0, "{}Added bookmarks:{}".format(ansi['yellow'], ansi['reset'])) | |||||
| out(0, yellow("Added bookmarks:")) | |||||
| for path in paths: | for path in paths: | ||||
| path = os.path.abspath(path) # convert relative to absolute path | path = os.path.abspath(path) # convert relative to absolute path | ||||
| if config.has_option("bookmarks", path): | if config.has_option("bookmarks", path): | ||||
| @@ -204,7 +212,7 @@ def add_bookmarks(paths): | |||||
| else: | else: | ||||
| path_name = os.path.split(path)[1] | path_name = os.path.split(path)[1] | ||||
| config.set("bookmarks", path, path_name) | config.set("bookmarks", path, path_name) | ||||
| out(1, "{}{}{}".format(ansi['bold'], path, ansi['reset'])) | |||||
| out(1, bold(path)) | |||||
| save_config_file(config) | save_config_file(config) | ||||
| @@ -213,12 +221,12 @@ def delete_bookmarks(paths): | |||||
| config = load_config_file() | config = load_config_file() | ||||
| if config.has_section("bookmarks"): | if config.has_section("bookmarks"): | ||||
| out(0, "{}Deleted bookmarks:{}".format(ansi['yellow'], ansi['reset'])) | |||||
| out(0, yellow("Deleted bookmarks:")) | |||||
| for path in paths: | for path in paths: | ||||
| path = os.path.abspath(path) # convert relative to absolute path | path = os.path.abspath(path) # convert relative to absolute path | ||||
| config_was_changed = config.remove_option("bookmarks", path) | config_was_changed = config.remove_option("bookmarks", path) | ||||
| if config_was_changed: | if config_was_changed: | ||||
| out(1, "{}{}{}".format(ansi['bold'], path, ansi['reset'])) | |||||
| out(1, bold(path)) | |||||
| else: | else: | ||||
| out(1, "'{}' is not bookmarked.".format(path)) | out(1, "'{}' is not bookmarked.".format(path)) | ||||
| save_config_file(config) | save_config_file(config) | ||||
| @@ -235,7 +243,7 @@ def list_bookmarks(): | |||||
| bookmarks = [] | bookmarks = [] | ||||
| if bookmarks: | if bookmarks: | ||||
| out(0, "{}Current bookmarks:{}".format(ansi['yellow'], ansi['reset'])) | |||||
| out(0, yellow("Current bookmarks:")) | |||||
| for bookmark_path, bookmark_name in bookmarks: | for bookmark_path, bookmark_name in bookmarks: | ||||
| out(1, bookmark_path) | out(1, bookmark_path) | ||||
| else: | else: | ||||
| @@ -277,7 +285,7 @@ def main(): | |||||
| args = parser.parse_args() | args = parser.parse_args() | ||||
| print "{}gitup{}: the git-repo-updater".format(ansi['bold'], ansi['reset']) | |||||
| print bold("gitup") + ": the git-repo-updater" | |||||
| if args.bookmarks_to_add: | if args.bookmarks_to_add: | ||||
| add_bookmarks(args.bookmarks_to_add) | add_bookmarks(args.bookmarks_to_add) | ||||