list: add a --relative-to option

The current list output only shows project paths relative to the
root of the repo client checkout.  It can be helpful to also get
a listing of paths based on other paths (e.g. the current working
directory), so add an option to repo list to support that.  We'll
leverage this in bash completion to support completing projects by
their local paths and not just remote names.

Bug: https://crbug.com/gerrit/14797
Change-Id: Ia2b35d18c890217768448118b003874a1016efd4
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/312904
Reviewed-by: Xin Li <delphij@google.com>
Tested-by: Mike Frysinger <vapier@google.com>
diff --git a/completion.bash b/completion.bash
index 04347ce..6a5bfe1 100644
--- a/completion.bash
+++ b/completion.bash
@@ -37,6 +37,7 @@
 __complete_repo_list_projects() {
   local repo=${COMP_WORDS[0]}
   "${repo}" list -n 2>/dev/null
+  "${repo}" list -p --relative-to=. 2>/dev/null
 }
 
 # Complete the repo <command> argument.
diff --git a/man/repo-list.1 b/man/repo-list.1
index a86315a..7f85e61 100644
--- a/man/repo-list.1
+++ b/man/repo-list.1
@@ -27,15 +27,19 @@
 \fB\-a\fR, \fB\-\-all\fR
 show projects regardless of checkout state
 .TP
-\fB\-f\fR, \fB\-\-fullpath\fR
-display the full work tree path instead of the
-relative path
-.TP
 \fB\-n\fR, \fB\-\-name\-only\fR
 display only the name of the repository
 .TP
 \fB\-p\fR, \fB\-\-path\-only\fR
 display only the path of the repository
+.TP
+\fB\-f\fR, \fB\-\-fullpath\fR
+display the full work tree path instead of the
+relative path
+.TP
+\fB\-\-relative\-to\fR=\fI\,PATH\/\fR
+display paths relative to this one (default: top of
+repo client checkout)
 .SS Logging options:
 .TP
 \fB\-v\fR, \fB\-\-verbose\fR
diff --git a/subcmds/list.py b/subcmds/list.py
index 8d0c564..6adf85b 100644
--- a/subcmds/list.py
+++ b/subcmds/list.py
@@ -12,6 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import os
+
 from command import Command, MirrorSafeCommand
 
 
@@ -43,20 +45,26 @@
     p.add_option('-a', '--all',
                  action='store_true',
                  help='show projects regardless of checkout state')
-    p.add_option('-f', '--fullpath',
-                 dest='fullpath', action='store_true',
-                 help='display the full work tree path instead of the relative path')
     p.add_option('-n', '--name-only',
                  dest='name_only', action='store_true',
                  help='display only the name of the repository')
     p.add_option('-p', '--path-only',
                  dest='path_only', action='store_true',
                  help='display only the path of the repository')
+    p.add_option('-f', '--fullpath',
+                 dest='fullpath', action='store_true',
+                 help='display the full work tree path instead of the relative path')
+    p.add_option('--relative-to', metavar='PATH',
+                 help='display paths relative to this one (default: top of repo client checkout)')
 
   def ValidateOptions(self, opt, args):
     if opt.fullpath and opt.name_only:
       self.OptionParser.error('cannot combine -f and -n')
 
+    # Resolve any symlinks so the output is stable.
+    if opt.relative_to:
+      opt.relative_to = os.path.realpath(opt.relative_to)
+
   def Execute(self, opt, args):
     """List all projects and the associated directories.
 
@@ -76,6 +84,8 @@
     def _getpath(x):
       if opt.fullpath:
         return x.worktree
+      if opt.relative_to:
+        return os.path.relpath(x.worktree, opt.relative_to)
       return x.relpath
 
     lines = []