Chromium Code Reviews| Index: git_cl.py |
| diff --git a/git_cl.py b/git_cl.py |
| index 9218ebd332b072c00ae48c66eca5f4419312f2fb..dc317879021613d39be9449c8329eabc7fff7e05 100755 |
| --- a/git_cl.py |
| +++ b/git_cl.py |
| @@ -2900,20 +2900,75 @@ def color_for_status(status): |
| 'error': Fore.WHITE, |
| }.get(status, Fore.WHITE) |
| +def split_diff_files(diff): |
| + """Splits a diff for the individual files and outputs a dict |
| + {file name: diff}. |
| + """ |
| + files = {} |
| + current_lines = [] |
| + current_file = '' |
| + def append(): |
| + if current_file: |
| + files[current_file] = '\n'.join(current_lines+['']) |
| + for line in diff.splitlines(): |
| + if line.startswith('Index: '): |
| + continue |
| + if line.startswith('diff '): |
| + append() |
| + current_lines = [] |
| + full_file_name = line.split()[-1] |
| + current_file = '/'.join(full_file_name.split('/')[1:]) |
| + line = re.sub(' [ab]/'+re.escape(current_file), ' '+current_file, line) |
| + if line.startswith('+++ ') or line.startswith('--- '): |
| + line = re.sub(' [ab]/'+re.escape(current_file), ' '+current_file, line) |
| + current_lines.append(line) |
| + append() |
| + return files |
| + |
| +def diff_compare(left, right): |
| + """Compares two diffs without regarding actual file names, line number |
| + information or commit hashes. |
|
tandrii(chromium)
2016/04/15 15:15:13
nit: add empty line.
|
| + Returns True if they are equal, False otherwise. |
| + """ |
| + file_pattern = re.compile('^(diff |\+\+\+ |--- ).*$', re.MULTILINE) |
| + line_pattern = re.compile('^@@[ +\-0-9,]+@@', re.MULTILINE) |
| + index_pattern = re.compile('^index [0-9a-f]+\.\.[0-9a-f]+', re.MULTILINE) |
| + left = re.sub(line_pattern, '@@', re.sub(index_pattern, 'index', |
| + re.sub(file_pattern, '\\1', left))) |
| + right = re.sub(line_pattern, '@@', re.sub(index_pattern, 'index', |
| + re.sub(file_pattern, '\\1', right))) |
| + return left == right |
| + |
| def fetch_cl_status(cl, auth_config=None): |
| - """Fetches information for an issue and returns (branch, issue, status).""" |
| + """Fetches information for an issue and returns |
| + (branch, issue, status, outdated). |
| + """ |
| url = cl.GetIssueURL() |
| status = cl.GetStatus() |
| + outdated = False |
| + if cl.GetIssue(): |
| + latest_patchset = cl.GetMostRecentPatchset() |
| + uploaded_diff = cl.GetPatchSetDiff(cl.GetIssue(), latest_patchset) |
| + uploaded_file_diffs = split_diff_files(uploaded_diff) |
| + change = cl.GetChange(cl.GetCommonAncestorWithUpstream(), None) |
| + local_file_diffs = {f.LocalPath(): f.GenerateScmDiff() |
| + for f in change.AffectedFiles()} |
| + files = sorted(uploaded_file_diffs.keys()) |
| + if files != sorted(local_file_diffs.keys()) or \ |
| + any(not diff_compare(uploaded_file_diffs[f], local_file_diffs[f]) |
| + for f in uploaded_file_diffs.keys()): |
| + outdated = True |
| if url and (not status or status == 'error'): |
| # The issue probably doesn't exist anymore. |
| url += ' (broken)' |
| - return (cl, url, status) |
| + return (cl, url, status, outdated) |
| def get_cl_statuses( |
| changes, fine_grained, max_processes=None, auth_config=None): |
| - """Returns a blocking iterable of (branch, issue, color) for given branches. |
| + """Returns a blocking iterable of (branch, issue, color, outdated) for given |
| + branches. |
| If fine_grained is true, this will fetch CL statuses from the server. |
| Otherwise, simply indicate if there's a matching url for the given branches. |
| @@ -2944,7 +2999,7 @@ def get_cl_statuses( |
| for c in changes: |
| cl = Changelist(branchref=c.GetBranch(), auth_config=auth_config) |
| url = cl.GetIssueURL() |
| - yield (c, url, 'waiting' if url else 'error') |
| + yield (c, url, 'waiting' if url else 'error', False) |
| def upload_branch_deps(cl, args): |
| @@ -3111,17 +3166,17 @@ def CMDstatus(parser, args): |
| for cl in sorted(changes, key = lambda c : c.GetBranch()): |
| branch = cl.GetBranch() |
| while branch not in branch_statuses: |
| - c, i, status = output.next() |
| - branch_statuses[c.GetBranch()] = (i, status) |
| - issue_url, status = branch_statuses.pop(branch) |
| - color = color_for_status(status) |
| - reset = Fore.RESET |
| - if not setup_color.IS_TTY: |
| - color = '' |
| - reset = '' |
| + c, i, status, outdated = output.next() |
| + branch_statuses[c.GetBranch()] = (i, status, outdated) |
| + issue_url, status, outdated = branch_statuses.pop(branch) |
| + make_color = lambda c : c if setup_color.IS_TTY else '' |
| + color = make_color(color_for_status(status)) |
| + reset = make_color(Fore.RESET) |
| + color_outdated = make_color(Fore.RED) |
| status_str = '(%s)' % status if status else '' |
| - print ' %*s : %s%s %s%s' % ( |
| - alignment, ShortBranchName(branch), color, issue_url, |
| + outdated_str = '%soutdated%s ' % (color_outdated, color) if outdated else '' |
| + print ' %*s : %s%s %s%s%s' % ( |
| + alignment, ShortBranchName(branch), color, issue_url, outdated_str, |
| status_str, reset) |
| cl = Changelist(auth_config=auth_config) |