subcmds: Use repo logger

Bug: b/292704435
Change-Id: Ia3a45d87fc0bf0d4a1ba53050d9c3cd2dba20e55
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/386236
Reviewed-by: Jason Chang <jasonnc@google.com>
Commit-Queue: Aravind Vasudevan <aravindvasudev@google.com>
Tested-by: Aravind Vasudevan <aravindvasudev@google.com>
diff --git a/subcmds/abandon.py b/subcmds/abandon.py
index 996c3d2..f6c0c66 100644
--- a/subcmds/abandon.py
+++ b/subcmds/abandon.py
@@ -15,7 +15,6 @@
 import collections
 import functools
 import itertools
-import sys
 
 from command import Command
 from command import DEFAULT_LOCAL_JOBS
@@ -23,6 +22,10 @@
 from error import RepoExitError
 from git_command import git
 from progress import Progress
+from repo_logging import RepoLogger
+
+
+logger = RepoLogger(__file__)
 
 
 class AbandonError(RepoExitError):
@@ -126,18 +129,12 @@
         if err:
             for br in err.keys():
                 err_msg = "error: cannot abandon %s" % br
-                print(err_msg, file=sys.stderr)
+                logger.error(err_msg)
                 for proj in err[br]:
-                    print(
-                        " " * len(err_msg) + " | %s" % _RelPath(proj),
-                        file=sys.stderr,
-                    )
+                    logger.error(" " * len(err_msg) + " | %s", _RelPath(proj))
             raise AbandonError(aggregate_errors=aggregate_errors)
         elif not success:
-            print(
-                "error: no project has local branch(es) : %s" % nb,
-                file=sys.stderr,
-            )
+            logger.error("error: no project has local branch(es) : %s", nb)
             raise AbandonError(aggregate_errors=aggregate_errors)
         else:
             # Everything below here is displaying status.
diff --git a/subcmds/checkout.py b/subcmds/checkout.py
index 67f1838..ea48263 100644
--- a/subcmds/checkout.py
+++ b/subcmds/checkout.py
@@ -13,7 +13,6 @@
 # limitations under the License.
 
 import functools
-import sys
 from typing import NamedTuple
 
 from command import Command
@@ -22,6 +21,10 @@
 from error import RepoExitError
 from progress import Progress
 from project import Project
+from repo_logging import RepoLogger
+
+
+logger = RepoLogger(__file__)
 
 
 class CheckoutBranchResult(NamedTuple):
@@ -99,12 +102,9 @@
 
         if err_projects:
             for p in err_projects:
-                print(
-                    "error: %s/: cannot checkout %s" % (p.relpath, nb),
-                    file=sys.stderr,
-                )
+                logger.error("error: %s/: cannot checkout %s", p.relpath, nb)
             raise CheckoutCommandError(aggregate_errors=err)
         elif not success:
             msg = f"error: no project has branch {nb}"
-            print(msg, file=sys.stderr)
+            logger.error(msg)
             raise MissingBranchError(msg)
diff --git a/subcmds/cherry_pick.py b/subcmds/cherry_pick.py
index 980720e..f9ae3e3 100644
--- a/subcmds/cherry_pick.py
+++ b/subcmds/cherry_pick.py
@@ -18,9 +18,11 @@
 from command import Command
 from error import GitError
 from git_command import GitCommand
+from repo_logging import RepoLogger
 
 
 CHANGE_ID_RE = re.compile(r"^\s*Change-Id: I([0-9a-f]{40})\s*$")
+logger = RepoLogger(__file__)
 
 
 class CherryPick(Command):
@@ -52,7 +54,7 @@
         try:
             p.Wait()
         except GitError:
-            print(p.stderr, file=sys.stderr)
+            logger.error(p.stderr)
             raise
 
         sha1 = p.stdout.strip()
@@ -67,9 +69,7 @@
         try:
             p.Wait()
         except GitError:
-            print(
-                "error: Failed to retrieve old commit message", file=sys.stderr
-            )
+            logger.error("error: Failed to retrieve old commit message")
             raise
 
         old_msg = self._StripHeader(p.stdout)
@@ -85,14 +85,13 @@
         try:
             p.Wait()
         except GitError as e:
-            print(str(e))
-            print(
+            logger.error(e)
+            logger.warn(
                 "NOTE: When committing (please see above) and editing the "
                 "commit message, please remove the old Change-Id-line and "
-                "add:"
+                "add:\n%s",
+                self._GetReference(sha1),
             )
-            print(self._GetReference(sha1), file=sys.stderr)
-            print(file=sys.stderr)
             raise
 
         if p.stdout:
@@ -115,10 +114,7 @@
         try:
             p.Wait()
         except GitError:
-            print(
-                "error: Failed to update commit message",
-                file=sys.stderr,
-            )
+            logger.error("error: Failed to update commit message")
             raise
 
     def _IsChangeId(self, line):
diff --git a/subcmds/download.py b/subcmds/download.py
index e33698e..4396c9e 100644
--- a/subcmds/download.py
+++ b/subcmds/download.py
@@ -19,9 +19,11 @@
 from error import GitError
 from error import NoSuchProjectError
 from error import RepoExitError
+from repo_logging import RepoLogger
 
 
 CHANGE_RE = re.compile(r"^([1-9][0-9]*)(?:[/\.-]([1-9][0-9]*))?$")
+logger = RepoLogger(__file__)
 
 
 class DownloadCommandError(RepoExitError):
@@ -109,21 +111,16 @@
                     except NoSuchProjectError:
                         project = None
                     if project not in projects:
-                        print(
+                        logger.error(
                             "error: %s matches too many projects; please "
-                            "re-run inside the project checkout." % (a,),
-                            file=sys.stderr,
+                            "re-run inside the project checkout.",
+                            a,
                         )
                         for project in projects:
-                            print(
-                                "  %s/ @ %s"
-                                % (
-                                    project.RelPath(
-                                        local=opt.this_manifest_only
-                                    ),
-                                    project.revisionExpr,
-                                ),
-                                file=sys.stderr,
+                            logger.error(
+                                "  %s/ @ %s",
+                                project.RelPath(local=opt.this_manifest_only),
+                                project.revisionExpr,
                             )
                         raise NoSuchProjectError()
                 else:
@@ -156,18 +153,21 @@
             dl = project.DownloadPatchSet(change_id, ps_id)
 
             if not opt.revert and not dl.commits:
-                print(
-                    "[%s] change %d/%d has already been merged"
-                    % (project.name, change_id, ps_id),
-                    file=sys.stderr,
+                logger.error(
+                    "[%s] change %d/%d has already been merged",
+                    project.name,
+                    change_id,
+                    ps_id,
                 )
                 continue
 
             if len(dl.commits) > 1:
-                print(
-                    "[%s] %d/%d depends on %d unmerged changes:"
-                    % (project.name, change_id, ps_id, len(dl.commits)),
-                    file=sys.stderr,
+                logger.error(
+                    "[%s] %d/%d depends on %d unmerged changes:",
+                    project.name,
+                    change_id,
+                    ps_id,
+                    len(dl.commits),
                 )
                 for c in dl.commits:
                     print("  %s" % (c), file=sys.stderr)
@@ -204,9 +204,10 @@
                         project._Checkout(dl.commit)
 
             except GitError:
-                print(
-                    "[%s] Could not complete the %s of %s"
-                    % (project.name, mode, dl.commit),
-                    file=sys.stderr,
+                logger.error(
+                    "[%s] Could not complete the %s of %s",
+                    project.name,
+                    mode,
+                    dl.commit,
                 )
                 raise
diff --git a/subcmds/forall.py b/subcmds/forall.py
index 9a02c49..287f2e0 100644
--- a/subcmds/forall.py
+++ b/subcmds/forall.py
@@ -28,8 +28,10 @@
 from command import MirrorSafeCommand
 from command import WORKER_BATCH_SIZE
 from error import ManifestInvalidRevisionError
+from repo_logging import RepoLogger
 
 
+logger = RepoLogger(__file__)
 _CAN_COLOR = [
     "branch",
     "diff",
@@ -293,10 +295,10 @@
             rc = rc or errno.EINTR
         except Exception as e:
             # Catch any other exceptions raised
-            print(
-                "forall: unhandled error, terminating the pool: %s: %s"
-                % (type(e).__name__, e),
-                file=sys.stderr,
+            logger.error(
+                "forall: unhandled error, terminating the pool: %s: %s",
+                type(e).__name__,
+                e,
             )
             rc = rc or getattr(e, "errno", 1)
         if rc != 0:
diff --git a/subcmds/grep.py b/subcmds/grep.py
index 19c06d4..b677b6b 100644
--- a/subcmds/grep.py
+++ b/subcmds/grep.py
@@ -24,6 +24,10 @@
 from error import SilentRepoExitError
 from git_command import GitCommand
 from project import Project
+from repo_logging import RepoLogger
+
+
+logger = RepoLogger(__file__)
 
 
 class GrepColoring(Coloring):
@@ -371,7 +375,7 @@
         if opt.revision:
             if "--cached" in cmd_argv:
                 msg = "fatal: cannot combine --cached and --revision"
-                print(msg, file=sys.stderr)
+                logger.error(msg)
                 raise InvalidArgumentsError(msg)
             have_rev = True
             cmd_argv.extend(opt.revision)
@@ -396,5 +400,5 @@
             sys.exit(0)
         elif have_rev and bad_rev:
             for r in opt.revision:
-                print("error: can't search revision %s" % r, file=sys.stderr)
+                logger.error("error: can't search revision %s", r)
         raise GrepCommandError(aggregate_errors=errors)
diff --git a/subcmds/init.py b/subcmds/init.py
index 529b212..9ac42d8 100644
--- a/subcmds/init.py
+++ b/subcmds/init.py
@@ -23,9 +23,12 @@
 from git_command import git_require
 from git_command import MIN_GIT_VERSION_HARD
 from git_command import MIN_GIT_VERSION_SOFT
+from repo_logging import RepoLogger
 from wrapper import Wrapper
 
 
+logger = RepoLogger(__file__)
+
 _REPO_ALLOW_SHALLOW = os.environ.get("REPO_ALLOW_SHALLOW")
 
 
@@ -330,11 +333,11 @@
     def Execute(self, opt, args):
         git_require(MIN_GIT_VERSION_HARD, fail=True)
         if not git_require(MIN_GIT_VERSION_SOFT):
-            print(
-                "repo: warning: git-%s+ will soon be required; please upgrade "
-                "your version of git to maintain support."
-                % (".".join(str(x) for x in MIN_GIT_VERSION_SOFT),),
-                file=sys.stderr,
+            logger.warning(
+                "repo: warning: git-%s+ will soon be required; "
+                "please upgrade your version of git to maintain "
+                "support.",
+                ".".join(str(x) for x in MIN_GIT_VERSION_SOFT),
             )
 
         rp = self.manifest.repoProject
@@ -357,10 +360,7 @@
                 )
             except wrapper.CloneFailure as e:
                 err_msg = "fatal: double check your --repo-rev setting."
-                print(
-                    err_msg,
-                    file=sys.stderr,
-                )
+                logger.error(err_msg)
                 self.git_event_log.ErrorEvent(err_msg)
                 raise RepoUnhandledExceptionError(e)
 
diff --git a/subcmds/manifest.py b/subcmds/manifest.py
index f72df34..101240d 100644
--- a/subcmds/manifest.py
+++ b/subcmds/manifest.py
@@ -17,6 +17,10 @@
 import sys
 
 from command import PagedCommand
+from repo_logging import RepoLogger
+
+
+logger = RepoLogger(__file__)
 
 
 class Manifest(PagedCommand):
@@ -132,7 +136,7 @@
             manifest.SetUseLocalManifests(not opt.ignore_local_manifests)
 
             if opt.json:
-                print("warning: --json is experimental!", file=sys.stderr)
+                logger.warn("warning: --json is experimental!")
                 doc = manifest.ToDict(
                     peg_rev=opt.peg_rev,
                     peg_rev_upstream=opt.peg_rev_upstream,
@@ -159,13 +163,13 @@
             if output_file != "-":
                 fd.close()
                 if manifest.path_prefix:
-                    print(
-                        f"Saved {manifest.path_prefix} submanifest to "
-                        f"{output_file}",
-                        file=sys.stderr,
+                    logger.warn(
+                        "Saved %s submanifest to %s",
+                        manifest.path_prefix,
+                        output_file,
                     )
                 else:
-                    print(f"Saved manifest to {output_file}", file=sys.stderr)
+                    logger.warn("Saved manifest to %s", output_file)
 
     def ValidateOptions(self, opt, args):
         if args:
diff --git a/subcmds/rebase.py b/subcmds/rebase.py
index c0e83ad..439557c 100644
--- a/subcmds/rebase.py
+++ b/subcmds/rebase.py
@@ -17,6 +17,10 @@
 from color import Coloring
 from command import Command
 from git_command import GitCommand
+from repo_logging import RepoLogger
+
+
+logger = RepoLogger(__file__)
 
 
 class RebaseColoring(Coloring):
@@ -104,17 +108,15 @@
         one_project = len(all_projects) == 1
 
         if opt.interactive and not one_project:
-            print(
-                "error: interactive rebase not supported with multiple "
-                "projects",
-                file=sys.stderr,
+            logger.error(
+                "error: interactive rebase not supported with multiple projects"
             )
+
             if len(args) == 1:
-                print(
-                    "note: project %s is mapped to more than one path"
-                    % (args[0],),
-                    file=sys.stderr,
+                logger.warn(
+                    "note: project %s is mapped to more than one path", args[0]
                 )
+
             return 1
 
         # Setup the common git rebase args that we use for all projects.
@@ -145,10 +147,9 @@
             cb = project.CurrentBranch
             if not cb:
                 if one_project:
-                    print(
-                        "error: project %s has a detached HEAD"
-                        % _RelPath(project),
-                        file=sys.stderr,
+                    logger.error(
+                        "error: project %s has a detached HEAD",
+                        _RelPath(project),
                     )
                     return 1
                 # Ignore branches with detached HEADs.
@@ -157,10 +158,9 @@
             upbranch = project.GetBranch(cb)
             if not upbranch.LocalMerge:
                 if one_project:
-                    print(
-                        "error: project %s does not track any remote branches"
-                        % _RelPath(project),
-                        file=sys.stderr,
+                    logger.error(
+                        "error: project %s does not track any remote branches",
+                        _RelPath(project),
                     )
                     return 1
                 # Ignore branches without remotes.
diff --git a/subcmds/selfupdate.py b/subcmds/selfupdate.py
index 51d963e..7268309 100644
--- a/subcmds/selfupdate.py
+++ b/subcmds/selfupdate.py
@@ -13,15 +13,18 @@
 # limitations under the License.
 
 import optparse
-import sys
 
 from command import Command
 from command import MirrorSafeCommand
 from error import RepoExitError
+from repo_logging import RepoLogger
 from subcmds.sync import _PostRepoFetch
 from subcmds.sync import _PostRepoUpgrade
 
 
+logger = RepoLogger(__file__)
+
+
 class SelfupdateError(RepoExitError):
     """Exit error for failed selfupdate command."""
 
@@ -66,7 +69,7 @@
         else:
             result = rp.Sync_NetworkHalf()
             if result.error:
-                print("error: can't update repo", file=sys.stderr)
+                logger.error("error: can't update repo")
                 raise SelfupdateError(aggregate_errors=[result.error])
 
             rp.bare_git.gc("--auto")
diff --git a/subcmds/stage.py b/subcmds/stage.py
index 4d54eb1..92a00ea 100644
--- a/subcmds/stage.py
+++ b/subcmds/stage.py
@@ -17,6 +17,10 @@
 from color import Coloring
 from command import InteractiveCommand
 from git_command import GitCommand
+from repo_logging import RepoLogger
+
+
+logger = RepoLogger(__file__)
 
 
 class _ProjectList(Coloring):
@@ -62,7 +66,7 @@
             if p.IsDirty()
         ]
         if not all_projects:
-            print("no projects have uncommitted modifications", file=sys.stderr)
+            logger.error("no projects have uncommitted modifications")
             return
 
         out = _ProjectList(self.manifest.manifestProject.config)
diff --git a/subcmds/upload.py b/subcmds/upload.py
index ec89ad4..618a10e 100644
--- a/subcmds/upload.py
+++ b/subcmds/upload.py
@@ -29,10 +29,12 @@
 from git_refs import R_HEADS
 from hooks import RepoHook
 from project import ReviewableBranch
+from repo_logging import RepoLogger
 from subcmds.sync import LocalSyncState
 
 
 _DEFAULT_UNUSUAL_COMMIT_THRESHOLD = 5
+logger = RepoLogger(__file__)
 
 
 class UploadExitError(SilentRepoExitError):
@@ -70,16 +72,16 @@
     # If any branch has many commits, prompt the user.
     if many_commits:
         if len(branches) > 1:
-            print(
+            logger.warn(
                 "ATTENTION: One or more branches has an unusually high number "
                 "of commits."
             )
         else:
-            print(
+            logger.warn(
                 "ATTENTION: You are uploading an unusually high number of "
                 "commits."
             )
-        print(
+        logger.warn(
             "YOU PROBABLY DO NOT MEAN TO DO THIS. (Did you rebase across "
             "branches?)"
         )
@@ -93,7 +95,7 @@
 
 def _die(fmt, *args):
     msg = fmt % args
-    print("error: %s" % msg, file=sys.stderr)
+    logger.error("error: %s", msg)
     raise UploadExitError(msg)
 
 
@@ -748,16 +750,13 @@
             for result in results:
                 project, avail = result
                 if avail is None:
-                    print(
+                    logger.error(
                         'repo: error: %s: Unable to upload branch "%s". '
                         "You might be able to fix the branch by running:\n"
-                        "  git branch --set-upstream-to m/%s"
-                        % (
-                            project.RelPath(local=opt.this_manifest_only),
-                            project.CurrentBranch,
-                            project.manifest.branch,
-                        ),
-                        file=sys.stderr,
+                        "  git branch --set-upstream-to m/%s",
+                        project.RelPath(local=opt.this_manifest_only),
+                        project.CurrentBranch,
+                        project.manifest.branch,
                     )
                 elif avail:
                     pending.append(result)
@@ -772,14 +771,11 @@
 
         if not pending:
             if opt.branch is None:
-                print(
-                    "repo: error: no branches ready for upload", file=sys.stderr
-                )
+                logger.error("repo: error: no branches ready for upload")
             else:
-                print(
-                    'repo: error: no branches named "%s" ready for upload'
-                    % (opt.branch,),
-                    file=sys.stderr,
+                logger.error(
+                    'repo: error: no branches named "%s" ready for upload',
+                    opt.branch,
                 )
             return 1
 
@@ -809,10 +805,9 @@
                 project_list=pending_proj_names, worktree_list=pending_worktrees
             ):
                 if LocalSyncState(manifest).IsPartiallySynced():
-                    print(
+                    logger.error(
                         "Partially synced tree detected. Syncing all projects "
-                        "may resolve issues you're seeing.",
-                        file=sys.stderr,
+                        "may resolve issues you're seeing."
                     )
                 ret = 1
         if ret: