pw_presubmit: Fix git_repo.describe_files() output

- Use '@{upstream}' as the stand-in for the upstream tracking branch
  rather than an object.
- Make manually specifying @{u} or @{upstream} equivalent to the
  default behavior.
- Fix the git_repo.describe_files() output when comparing against
  @{upstream}.
- Move tracking branch handling into list_files() and describe_files()
  so it's consistent between pw format and pw presubmit.

Change-Id: I9f3474bc5106809d8957180e4af004d45ea6598c
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/77540
Pigweed-Auto-Submit: Wyatt Hepler <hepler@google.com>
Reviewed-by: Rob Mohr <mohrr@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
diff --git a/pw_presubmit/py/pw_presubmit/cli.py b/pw_presubmit/py/pw_presubmit/cli.py
index 528143a..94af44c 100644
--- a/pw_presubmit/py/pw_presubmit/cli.py
+++ b/pw_presubmit/py/pw_presubmit/cli.py
@@ -41,7 +41,7 @@
         '-b',
         '--base',
         metavar='commit',
-        default=git_repo.USE_TRACKING_BRANCH,
+        default=git_repo.TRACKING_BRANCH_ALIAS,
         help=('Git revision against which to diff for changed files. '
               'Default is the tracking branch of the current branch.'))
     base.add_argument(
diff --git a/pw_presubmit/py/pw_presubmit/git_repo.py b/pw_presubmit/py/pw_presubmit/git_repo.py
index f2fb22d..e1afff8 100644
--- a/pw_presubmit/py/pw_presubmit/git_repo.py
+++ b/pw_presubmit/py/pw_presubmit/git_repo.py
@@ -24,7 +24,8 @@
 _LOG = logging.getLogger(__name__)
 PathOrStr = Union[Path, str]
 
-USE_TRACKING_BRANCH = object()
+TRACKING_BRANCH_ALIAS = '@{upstream}'
+_TRACKING_BRANCH_ALIASES = TRACKING_BRANCH_ALIAS, '@{u}'
 
 
 def git_stdout(*args: PathOrStr,
@@ -57,42 +58,36 @@
         yield git_root / file
 
 
-def tracking_branch(repo_path: Path = None,
-                    suppress_exception: bool = True) -> Optional[str]:
+def tracking_branch(repo_path: Path = None) -> Optional[str]:
     """Returns the tracking branch of the current branch.
 
     Since most callers of this function can safely handle a return value of
-    None, default to suppressing exceptions and returning None when they
-    happen.
+    None, suppress exceptions and return None if there is no tracking branch.
 
     Args:
-        repo_path: repo path from which to run commands; defaults to Path.cwd()
-        suppress_exception: Whether to suppress CalledProcessError exceptions
+      repo_path: repo path from which to run commands; defaults to Path.cwd()
 
     Raises:
-        CalledProcessError: HEAD does not point to a branch or the branch is not
-            tracking another branch.
+      ValueError: if repo_path is not in a Git repository
+
+    Returns:
+      the remote tracking branch name or None if there is none
     """
     if repo_path is None:
         repo_path = Path.cwd()
 
-    # This command should raise an exception if repo_path is not a git
-    # repository. Always raise errors in that case.
-    _ = git_stdout('remote', '-v', repo=repo_path)
+    if not is_repo(repo_path or Path.cwd()):
+        raise ValueError(f'{repo_path} is not within a Git repository')
 
     # This command should only error out if there's no upstream branch set.
     try:
         return git_stdout('rev-parse',
                           '--abbrev-ref',
                           '--symbolic-full-name',
-                          '@{u}',
+                          TRACKING_BRANCH_ALIAS,
                           repo=repo_path)
 
     except subprocess.CalledProcessError:
-        if not suppress_exception:
-            raise
-        _LOG.warning('Error retrieving remote tracking branch of %s',
-                     repo_path)
         return None
 
 
@@ -112,7 +107,7 @@
     if repo_path is None:
         repo_path = Path.cwd()
 
-    if commit is USE_TRACKING_BRANCH:
+    if commit in _TRACKING_BRANCH_ALIASES:
         commit = tracking_branch(repo_path)
 
     if commit:
@@ -164,6 +159,13 @@
             f'under the {repo_path.resolve().relative_to(git_root.resolve())} '
             'subdirectory')
 
+    if commit in _TRACKING_BRANCH_ALIASES:
+        commit = tracking_branch(git_root)
+        if commit is None:
+            _LOG.warning(
+                'Attempted to list files changed since the remote tracking '
+                'branch, but the repo is not tracking a branch')
+
     if commit:
         yield f'that have changed since {commit}'
 
diff --git a/pw_presubmit/py/pw_presubmit/presubmit.py b/pw_presubmit/py/pw_presubmit/presubmit.py
index e64ba3c..599ef17 100644
--- a/pw_presubmit/py/pw_presubmit/presubmit.py
+++ b/pw_presubmit/py/pw_presubmit/presubmit.py
@@ -439,16 +439,12 @@
     files: List[Path] = []
 
     for repo, pathspecs in pathspecs_by_repo.items():
-        repo_base = base
-        if repo_base is git_repo.USE_TRACKING_BRANCH:
-            repo_base = git_repo.tracking_branch(repo)
-
         files += tools.exclude_paths(
-            exclude, git_repo.list_files(repo_base, pathspecs, repo), root)
+            exclude, git_repo.list_files(base, pathspecs, repo), root)
 
         _LOG.info(
             'Checking %s',
-            git_repo.describe_files(repo, repo, repo_base, pathspecs, exclude))
+            git_repo.describe_files(repo, repo, base, pathspecs, exclude))
 
     if output_directory is None:
         output_directory = root / '.presubmit'