Add a `--rebase` option to sync command

Previously repo would abort a sync if there were published changes not
merged upstream. The --rebase option allows the modification of
published commits.

This is a copy of http://go/grev/369694 with the merge conflicts
resolved.

Bug: 40014610
Change-Id: Idac8199400346327b530abea33f1ed794e5bb4c2
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/435838
Reviewed-by: Josip Sokcevic <sokcevic@google.com>
Tested-by: Jeroen Dhollander <jeroendh@google.com>
Commit-Queue: Jeroen Dhollander <jeroendh@google.com>
diff --git a/project.py b/project.py
index 28b1941..25a8cdc 100644
--- a/project.py
+++ b/project.py
@@ -1535,6 +1535,7 @@
         syncbuf,
         force_sync=False,
         force_checkout=False,
+        force_rebase=False,
         submodules=False,
         errors=None,
         verbose=False,
@@ -1680,14 +1681,15 @@
         if pub:
             not_merged = self._revlist(not_rev(revid), pub)
             if not_merged:
-                if upstream_gain:
+                if upstream_gain and not force_rebase:
                     # The user has published this branch and some of those
                     # commits are not yet merged upstream.  We do not want
                     # to rewrite the published commits so we punt.
                     fail(
                         LocalSyncFail(
                             "branch %s is published (but not merged) and is "
-                            "now %d commits behind"
+                            "now %d commits behind. Fix this manually or rerun "
+                            "with the --rebase option to force a rebase."
                             % (branch.name, len(upstream_gain)),
                             project=self.name,
                         )
diff --git a/subcmds/sync.py b/subcmds/sync.py
index 113e7a6..019ce3e 100644
--- a/subcmds/sync.py
+++ b/subcmds/sync.py
@@ -401,6 +401,13 @@
             "WARNING: this may cause loss of data",
         )
         p.add_option(
+            "--rebase",
+            dest="rebase",
+            action="store_true",
+            help="rebase local commits regardless of whether they are "
+            "published",
+        )
+        p.add_option(
             "-l",
             "--local-only",
             dest="local_only",
@@ -1009,7 +1016,13 @@
         return _FetchMainResult(all_projects)
 
     def _CheckoutOne(
-        self, detach_head, force_sync, force_checkout, verbose, project
+        self,
+        detach_head,
+        force_sync,
+        force_checkout,
+        force_rebase,
+        verbose,
+        project,
     ):
         """Checkout work tree for one project
 
@@ -1019,6 +1032,7 @@
             existing git directory that was previously linked to a different
             object directory).
             force_checkout: Force checking out of the repo content.
+            force_rebase: Force rebase.
             verbose: Whether to show verbose messages.
             project: Project object for the project to checkout.
 
@@ -1036,6 +1050,7 @@
                 syncbuf,
                 force_sync=force_sync,
                 force_checkout=force_checkout,
+                force_rebase=force_rebase,
                 errors=errors,
                 verbose=verbose,
             )
@@ -1109,6 +1124,7 @@
                     opt.detach_head,
                     opt.force_sync,
                     opt.force_checkout,
+                    opt.rebase,
                     opt.verbose,
                 ),
                 projects,