def report(self, bare=False, inspect_remotes=False): """ :param bool bare: Bare report only :param bool inspect_remotes: If True, report on which remote branches are cleanable :return GitRunReport: General report on current checkout state """ if not self.is_git_checkout: if self.remote_info: return GitRunReport(problem="not cloned yet") return GitRunReport.not_git() result = GitRunReport() if not self.config.remotes: result.add(problem="no remotes") if bare: return result if self.age is not None and self.age > FRESHNESS_THRESHOLD: result.add(note="last fetch %s ago" % represented_duration(self.age)) orphan_branches = self.orphan_branches if self.branches.current in orphan_branches: # Current is no more on its remote (should possibly checkout another branch and cleanup, or push) orphan_branches = orphan_branches[:] orphan_branches.remove(self.branches.current) result.add(note="current branch '%s' is orphaned" % self.branches.current) if len(orphan_branches) == 1: result.add(note="local branch '%s' can be pruned" % orphan_branches[0]) elif orphan_branches: result.add(note="%s can be pruned" % colored.plural(orphan_branches, "local branch")) result.add(self.branches.report) if inspect_remotes and self.remote_cleanable_branches: if len(self.remote_cleanable_branches) == 1: cleanable = "'%s'" % next(iter(self.remote_cleanable_branches)) else: cleanable = colored.plural(self.remote_cleanable_branches, "remote branch") result.add(note="%s can be cleaned" % cleanable) return result
def freshness(self): """Short freshness overview""" result = [] if self.report._problem: result.append(colored.problem(" ".join(self.report._problem))) if self.modified: result.append( colored.plural(self.modified, "diff", colored.problem)) if self.untracked: result.append(colored.warn("%s untracked" % len(self.untracked))) if self.report._note: result.append(colored.note(" ".join(self.report._note))) if not self.report._problem and not self.report._note and self._parent.age is not None: message = "up to date" if self._parent.age > FRESHNESS_THRESHOLD: message += "*" result.append(colored.pop(message)) return ", ".join(result)
def handle_ignore(action, what, target): """ Show/add/remove/clear ignores on current project :param str action: One of VALID_IGNORE_ACTIONS :param str what: What to add or remove :param ProjectDir target: Target dir """ if not isinstance(target, ProjectDir): abort( "--ignore applies to collections of checkouts, not particular checkouts" ) if not target.predominant: abort( "--ignore applies to internal bitbucket stash repos, this folder doesn't seem related to one" ) if target.predominant.type != 'stash': abort("--ignore applies to bitbucket stash only, not '%s'" % target.predominant.type) if not action: action = 'show' action = action.lower() if action not in VALID_IGNORE_ACTIONS: abort( "--ignore unknown action '%s', try for example: '--ignore show', or '--ignore add:hackday.*'" ) if action in ('show', 'clear') and what: abort("--ignore %s does not take arguments" % action) if action == 'clear': target.ignores.clear() elif action == 'add': added, invalid = target.ignores.add(what) if added: print("Added %s" % colored.plural(added, "pattern")) if invalid: print("%s: [%s], %s" % ( colored.note("Skipped"), '], ['.join(colored.problem(s) for s in invalid), colored.note("invalid regexes"), )) elif action == 'remove': removed, invalid = target.ignores.remove(what) if removed: print("Removed %s" % colored.plural(removed, "pattern")) if invalid: print("%s: [%s], %s" % ( colored.note("Skipped"), '], ['.join(colored.problem(s) for s in invalid), colored.note("not in ignore list"), )) values = target.ignores.values if not values: print(colored.note("No ignores defined")) else: print("%s:\n %s" % (colored.plural( values, "ignore", colored.NOTE), "\n ".join(values)))
def test_plural(): assert colored.plural(None, 'commit') == 'None commits' assert colored.plural([], 'commit') == '0 commits' assert colored.plural('', 'commit') == '0 commits' assert colored.plural(1, 'commit') == '1 commit' assert colored.plural('2', 'commit') == '2 commits' assert colored.plural(15, 'great dane') == '15 great danes' assert colored.plural(7, 'branch') == '7 branches' assert colored.plural(1, 'commit behind', on_first=True) == '1 commit behind' assert colored.plural('a b c'.split(), 'commit behind', on_first=True) == '3 commits behind' colored.activate_colors(True) assert colored.plural(1, 'commit', colored.PROBLEM) == '[31m1 commit[0m' assert colored.plural(1, 'commit', colored.problem) == '[31m1 commit[0m' colored.activate_colors(False)
def handle_single_clean(target, what): """ :param GitCheckout target: Single checkout to clean :param str what: Operation """ report = target.git.fetch() if report.has_problems: if what != "reset": what = "clean" print( target.header( GitRunReport(report).add(problem="<can't %s" % what))) abort() if what == "reset": return clean_reset(target) if what == "show": return clean_show(target) total_cleaned = 0 print(target.header()) if what in "remote all": if not target.git.remote_cleanable_branches: print(" No remote branches can be cleaned") else: total = len(target.git.remote_cleanable_branches) cleaned = 0 for branch in target.git.remote_cleanable_branches: remote, _, name = branch.partition("/") if not remote and name: raise Exception("Unknown branch spec '%s'" % branch) if run_git(target, False, "branch", "--delete", "--remotes", branch): cleaned += run_git(target, False, "push", "--delete", remote, name) total_cleaned += cleaned if cleaned == total: print("%s cleaned" % colored.plural(cleaned, "remote branch")) else: print("%s/%s remote branches cleaned" % (cleaned, total)) target.git.reset_cached_properties() if what == "all": # Fetch to update remote branches (and correctly detect new dangling local) target.git.fetch() if what in "local all": if not target.git.local_cleanable_branches: print(" No local branches can be cleaned") else: total = len(target.git.local_cleanable_branches) cleaned = 0 for branch in target.git.local_cleanable_branches: if branch == target.git.branches.current: fallback = target.git.fallback_branch() if not fallback: print( "Skipping branch '%s', can't determine fallback branch" % target.git.branches.current) continue run_git(target, True, "checkout", fallback) run_git(target, True, "pull") cleaned += run_git(target, False, "branch", "--delete", branch) total_cleaned += cleaned if cleaned == total: print( colored.highlight("%s cleaned" % colored.plural(cleaned, "local branch"))) else: print( colored.warn("%s/%s local branches cleaned" % (cleaned, total))) target.git.reset_cached_properties() if total_cleaned: print(target.header())