|
|
@@ -20,46 +20,74 @@ RESET = Style.RESET_ALL |
|
|
|
|
|
|
|
INDENT1 = " " * 3 |
|
|
|
INDENT2 = " " * 7 |
|
|
|
ERROR = RED + "Error:" + RESET |
|
|
|
|
|
|
|
def _update_repository(repo, rebase=True): |
|
|
|
def _read_config(repo, attr): |
|
|
|
"""Read an attribute from git config.""" |
|
|
|
try: |
|
|
|
return repo.git.config("--get", attr) |
|
|
|
except exc.GitCommandError: |
|
|
|
return None |
|
|
|
|
|
|
|
def _update_repository(repo, current_only=False, rebase=False, merge=False, |
|
|
|
verbose=False): |
|
|
|
"""Update a single git repository by fetching remotes and rebasing/merging. |
|
|
|
|
|
|
|
The specific actions depends on ... |
|
|
|
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 |
|
|
|
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. If *verbose* is set, additional information is |
|
|
|
printed out for the user. |
|
|
|
""" |
|
|
|
def _update_branch(branch): |
|
|
|
"""Update a single branch.""" |
|
|
|
print(INDENT2, "Updating branch:", branch, end=" ") |
|
|
|
upstream = branch.tracking_branch() |
|
|
|
if not upstream: |
|
|
|
print("Branch is not tracking any remote.") |
|
|
|
continue |
|
|
|
c_attr = "branch.{0}.rebase".format(branch.name) |
|
|
|
if not merge and (rebase or repo_rebase or _read_config(repo, c_attr)): |
|
|
|
### TODO: rebase |
|
|
|
else: |
|
|
|
### TODO: merge |
|
|
|
|
|
|
|
print(INDENT1, BOLD + os.path.split(repo.working_dir)[1] + ":") |
|
|
|
|
|
|
|
ref = repo.head.ref.tracking_branch() |
|
|
|
if ref: |
|
|
|
remote = repo.remotes[ref.remote_name] |
|
|
|
# else: |
|
|
|
### |
|
|
|
active = repo.active_branch |
|
|
|
if current_only: |
|
|
|
ref = active.tracking_branch() |
|
|
|
if not ref: |
|
|
|
print(INDENT2, ERROR, "no remote tracked by current branch.") |
|
|
|
return |
|
|
|
remotes = [repo.remotes[ref.remote_name]] |
|
|
|
else: |
|
|
|
remotes = repo.remotes |
|
|
|
|
|
|
|
remote.fetch() |
|
|
|
for remote in remotes: |
|
|
|
print(INDENT2, "Fetching remote:", remote.name) |
|
|
|
remote.fetch() # TODO: show progress |
|
|
|
|
|
|
|
if not repo.remotes: |
|
|
|
print(INDENT2, RED + "Error:" + RESET, "no remotes configured.") |
|
|
|
return |
|
|
|
try: |
|
|
|
repo = repo.remotes.origin |
|
|
|
except AttributeError: |
|
|
|
if len(repo.remotes) == 1: |
|
|
|
repo = repo.remotes[0] |
|
|
|
else: |
|
|
|
print(INDENT2, RED + "Error:" + RESET, "ambiguous remotes:", |
|
|
|
", ".join(remote.name for remote in repo.remotes)) |
|
|
|
repo_rebase = _read_config(repo, "pull.rebase") |
|
|
|
|
|
|
|
_update_branch(active) |
|
|
|
branches = set(repo.heads) - {active} |
|
|
|
if branches: |
|
|
|
stashed = repo.git.stash("--all") != "No local changes to save" |
|
|
|
try: |
|
|
|
for branch in sorted(branches, key=lambda b: b.name): |
|
|
|
branch.checkout() |
|
|
|
_update_branch(branch) |
|
|
|
finally: |
|
|
|
active.checkout() |
|
|
|
if stashed: |
|
|
|
repo.git.stash("pop") |
|
|
|
|
|
|
|
##################################### |
|
|
|
|
|
|
|
try: |
|
|
|
# Check if there is anything to pull, but don't do it yet: |
|
|
|
dry_fetch = _exec_shell("git fetch --dry-run") |
|
|
|
except subprocess.CalledProcessError: |
|
|
|
print(INDENT2, RED + "Error:" + RESET, "cannot fetch;", |
|
|
|
"do you have a remote repository configured correctly?") |
|
|
|
return |
|
|
|
|
|
|
|
try: |
|
|
|
last_commit = _exec_shell("git log -n 1 --pretty=\"%ar\"") |
|
|
|
except subprocess.CalledProcessError: |
|
|
|
last_commit = "never" # Couldn't get a log, so no commits |
|
|
@@ -87,7 +115,7 @@ def _update_repository(repo, rebase=True): |
|
|
|
"you have uncommitted changes in this repository!") |
|
|
|
print(INDENT2, "Ignoring.") |
|
|
|
|
|
|
|
def _update_subdirectories(path, long_name): |
|
|
|
def _update_subdirectories(path, long_name, update_args): |
|
|
|
"""Update all subdirectories that are git repos in a given directory.""" |
|
|
|
repos = [] |
|
|
|
for item in os.listdir(path): |
|
|
@@ -101,9 +129,9 @@ def _update_subdirectories(path, long_name): |
|
|
|
print(long_name[0].upper() + long_name[1:], |
|
|
|
"contains {0} git repositor{1}:".format(len(repos), suffix)) |
|
|
|
for repo in sorted(repos, key=lambda r: os.path.split(r.working_dir)[1]): |
|
|
|
_update_repository(repo) |
|
|
|
_update_repository(repo, *update_args) |
|
|
|
|
|
|
|
def _update_directory(path, is_bookmark=False): |
|
|
|
def _update_directory(path, update_args, is_bookmark=False): |
|
|
|
"""Update a particular directory. |
|
|
|
|
|
|
|
Determine whether the directory is a git repo on its own, a directory of |
|
|
@@ -117,29 +145,28 @@ def _update_directory(path, is_bookmark=False): |
|
|
|
try: |
|
|
|
repo = Repo(path) |
|
|
|
except exc.NoSuchPathError: |
|
|
|
print(RED + "Error:" + RESET, long_name, "doesn't exist!") |
|
|
|
print(ERROR, long_name, "doesn't exist!") |
|
|
|
except exc.InvalidGitRepositoryError: |
|
|
|
if os.path.isdir(path): |
|
|
|
_update_subdirectories(path, long_name) |
|
|
|
_update_subdirectories(path, long_name, update_args) |
|
|
|
else: |
|
|
|
print(RED + "Error:" + RESET, long_name, "isn't a repository!") |
|
|
|
print(ERROR, long_name, "isn't a repository!") |
|
|
|
else: |
|
|
|
long_name = (dir_type.capitalize() + ' "' + BOLD + repo.working_dir + |
|
|
|
RESET + '"') |
|
|
|
print(long_name, "is a git repository:") |
|
|
|
_update_repository(repo) |
|
|
|
_update_repository(repo, *update_args) |
|
|
|
|
|
|
|
def update_bookmarks(bookmarks, current_only=False, rebase=False, merge=False, |
|
|
|
verbose=False): |
|
|
|
def update_bookmarks(bookmarks, update_args): |
|
|
|
"""Loop through and update all bookmarks.""" |
|
|
|
if bookmarks: |
|
|
|
for path, name in bookmarks: |
|
|
|
_update_directory(path, is_bookmark=True) |
|
|
|
_update_directory(path, update_args, is_bookmark=True) |
|
|
|
else: |
|
|
|
print("You don't have any bookmarks configured! Get help with 'gitup -h'.") |
|
|
|
|
|
|
|
def update_directories(paths, current_only=False, rebase=False, merge=False, |
|
|
|
verbose=False): |
|
|
|
def update_directories(paths, update_args): |
|
|
|
"""Update a list of directories supplied by command arguments.""" |
|
|
|
for path in paths: |
|
|
|
_update_directory(os.path.abspath(path), is_bookmark=False) |
|
|
|
full_path = os.path.abspath(path) |
|
|
|
_update_directory(full_path, update_args, is_bookmark=False) |