A console script that allows you to easily update multiple git repositories at once
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

139 lines
5.0 KiB

  1. # -*- coding: utf-8 -*-
  2. #
  3. # Copyright (C) 2011-2014 Ben Kurtovic <ben.kurtovic@gmail.com>
  4. # See the LICENSE file for details.
  5. import os
  6. import shlex
  7. import subprocess
  8. from .output import out, bold, red, green, blue
  9. __all__ = ["update_bookmarks", "update_directories"]
  10. def _exec_shell(command):
  11. """Execute a shell command and get the output."""
  12. command = shlex.split(command)
  13. result = subprocess.check_output(command, stderr=subprocess.STDOUT)
  14. if result:
  15. result = result[:-1] # Strip newline if command returned anything
  16. return result
  17. def _directory_is_git_repo(directory_path):
  18. """Check if a directory is a git repository."""
  19. if os.path.isdir(directory_path):
  20. git_subfolder = os.path.join(directory_path, ".git")
  21. if os.path.isdir(git_subfolder): # Check for path/to/repository/.git
  22. return True
  23. return False
  24. def _update_repository(repo_path, repo_name):
  25. """Update a single git repository by pulling from the remote."""
  26. out(1, bold(repo_name) + ":")
  27. # cd into our folder so git commands target the correct repo:
  28. os.chdir(repo_path)
  29. try:
  30. # Check if there is anything to pull, but don't do it yet:
  31. dry_fetch = _exec_shell("git fetch --dry-run")
  32. except subprocess.CalledProcessError:
  33. out(2, red("Error: ") + "cannot fetch; do you have a remote " \
  34. "repository configured correctly?")
  35. return
  36. try:
  37. last_commit = _exec_shell("git log -n 1 --pretty=\"%ar\"")
  38. except subprocess.CalledProcessError:
  39. last_commit = "never" # Couldn't get a log, so no commits
  40. if not dry_fetch: # No new changes to pull
  41. out(2, blue("No new changes.") +
  42. " Last commit was {0}.".format(last_commit))
  43. else: # Stuff has happened!
  44. out(2, "There are new changes upstream...")
  45. status = _exec_shell("git status")
  46. if status.endswith("nothing to commit, working directory clean"):
  47. out(2, green("Pulling new changes..."))
  48. result = _exec_shell("git pull")
  49. out(2, "The following changes have been made since {0}:".format(
  50. last_commit))
  51. print(result)
  52. else:
  53. out(2, red("Warning: ") +
  54. "you have uncommitted changes in this repository!")
  55. out(2, "Ignoring.")
  56. def _update_directory(dir_path, dir_name, is_bookmark=False):
  57. """Update a particular directory.
  58. First, make sure the specified object is actually a directory, then
  59. determine whether the directory is a git repo on its own or a directory
  60. of git repositories. If the former, update the single repository; if the
  61. latter, update all repositories contained within.
  62. """
  63. if is_bookmark:
  64. dir_type = "bookmark" # Where did we get this directory from?
  65. else:
  66. dir_type = "directory"
  67. dir_long_name = "{0} '{1}'".format(dir_type, bold(dir_path))
  68. try:
  69. os.listdir(dir_path) # Test if we can access this directory
  70. except OSError:
  71. out(0, red("Error: ") +
  72. "cannot enter {0}; does it exist?".format(dir_long_name))
  73. return
  74. if not os.path.isdir(dir_path):
  75. if os.path.exists(dir_path):
  76. out(0, red("Error: ") + dir_long_name + " is not a directory!")
  77. else:
  78. out(0, red("Error: ") + dir_long_name + " does not exist!")
  79. return
  80. if _directory_is_git_repo(dir_path):
  81. out(0, dir_long_name.capitalize() + " is a git repository:")
  82. _update_repository(dir_path, dir_name)
  83. else:
  84. repositories = []
  85. dir_contents = os.listdir(dir_path) # Get potential repos in directory
  86. for item in dir_contents:
  87. repo_path = os.path.join(dir_path, item)
  88. repo_name = os.path.join(dir_name, item)
  89. if _directory_is_git_repo(repo_path): # Filter out non-repositories
  90. repositories.append((repo_path, repo_name))
  91. num_of_repos = len(repositories)
  92. if num_of_repos == 1:
  93. out(0, dir_long_name.capitalize() + " contains 1 git repository:")
  94. else:
  95. out(0, dir_long_name.capitalize() +
  96. " contains {0} git repositories:".format(num_of_repos))
  97. repositories.sort() # Go alphabetically instead of randomly
  98. for repo_path, repo_name in repositories:
  99. _update_repository(repo_path, repo_name)
  100. def update_bookmarks(bookmarks):
  101. """Loop through and update all bookmarks."""
  102. if bookmarks:
  103. for bookmark_path, bookmark_name in bookmarks:
  104. _update_directory(bookmark_path, bookmark_name, is_bookmark=True)
  105. else:
  106. out(0, "You don't have any bookmarks configured! " \
  107. "Get help with 'gitup -h'.")
  108. def update_directories(paths):
  109. """Update a list of directories supplied by command arguments."""
  110. for path in paths:
  111. path = os.path.abspath(path) # Convert relative to absolute path
  112. path_name = os.path.split(path)[1] # Dir name ("x" in /path/to/x/)
  113. _update_directory(path, path_name, is_bookmark=False)