Refine repo_pull.py

Use `repo manifest` command to retrieve manifest.
Change working directory to $ANDROID_BUILD_TOP before executing
commands.

Bug: 159346230
Test: ./repo_pull.py bash '<some commit query>' \
  -g https://android-review.googlesource.com
Change-Id: Ie5d29a4ae7b4b0e00c0900c8445e318dac807c77
diff --git a/tools/repo_pull/repo_pull.py b/tools/repo_pull/repo_pull.py
index 2042614..d423611 100755
--- a/tools/repo_pull/repo_pull.py
+++ b/tools/repo_pull/repo_pull.py
@@ -154,34 +154,28 @@
         return len(self.parents) > 1
 
 
-def find_manifest_xml(dir_path):
-    """Find the path to manifest.xml for this Android source tree."""
-    dir_path_prev = None
-    while dir_path != dir_path_prev:
-        path = os.path.join(dir_path, '.repo', 'manifest.xml')
-        if os.path.exists(path):
-            return path
-        dir_path_prev = dir_path
-        dir_path = os.path.dirname(dir_path)
+def find_repo_top(curdir):
+    """Find the top directory for this git-repo source tree."""
+    olddir = None
+    while curdir != olddir:
+        if os.path.exists(os.path.join(curdir, '.repo')):
+            return curdir
+        olddir = curdir
+        curdir = os.path.dirname(curdir)
     raise ValueError('.repo dir not found')
 
 
-def build_project_name_dir_dict(manifest_path):
+def build_project_name_dir_dict(manifest_name):
     """Build the mapping from Gerrit project name to source tree project
     directory path."""
+    manifest_cmd = ['repo', 'manifest']
+    if manifest_name:
+        manifest_cmd.extend(['-m', manifest_name])
+    raw_manifest_xml = run(manifest_cmd, stdout=PIPE, check=True).stdout
+
+    manifest_xml = xml.dom.minidom.parseString(raw_manifest_xml)
     project_dirs = {}
-    parsed_xml = xml.dom.minidom.parse(manifest_path)
-
-    includes = parsed_xml.getElementsByTagName('include')
-    for include in includes:
-        include_path = include.getAttribute('name')
-        if not os.path.isabs(include_path):
-            manifest_dir = os.path.dirname(os.path.realpath(manifest_path))
-            include_path = os.path.join(manifest_dir, include_path)
-        project_dirs.update(build_project_name_dir_dict(include_path))
-
-    projects = parsed_xml.getElementsByTagName('project')
-    for project in projects:
+    for project in manifest_xml.getElementsByTagName('project'):
         name = project.getAttribute('name')
         path = project.getAttribute('path')
         if path:
@@ -301,15 +295,14 @@
 
 def _main_bash(args):
     """Print the bash command to pull the change lists."""
-
+    repo_top = find_repo_top(os.getcwd())
+    project_dirs = build_project_name_dir_dict(args.manifest)
     branch_name = _get_local_branch_name_from_args(args)
 
-    manifest_path = _get_manifest_xml_from_args(args)
-    project_dirs = build_project_name_dir_dict(manifest_path)
-
     change_lists = _get_change_lists_from_args(args)
     change_list_groups = group_and_sort_change_lists(change_lists)
 
+    print(_sh_quote_command(['pushd', repo_top]))
     for changes in change_list_groups:
         for change in changes:
             project_dir = project_dirs.get(change.project, change.project)
@@ -319,6 +312,7 @@
                 change, branch_name, args.merge, args.pick))
             cmds.append(['popd'])
             print(_sh_quote_commands(cmds))
+    print(_sh_quote_command(['popd']))
 
 
 def _do_pull_change_lists_for_project(task):
@@ -329,6 +323,7 @@
     merge_opt = task_opts['merge_opt']
     pick_opt = task_opts['pick_opt']
     project_dirs = task_opts['project_dirs']
+    repo_top = task_opts['repo_top']
 
     for i, change in enumerate(changes):
         try:
@@ -341,7 +336,7 @@
         print(change.commit_sha1[0:10], i + 1, cwd)
         cmds = build_pull_commands(change, branch_name, merge_opt, pick_opt)
         for cmd in cmds:
-            proc = run(cmd, cwd=cwd, stderr=PIPE)
+            proc = run(cmd, cwd=os.path.join(repo_top, cwd), stderr=PIPE)
             if proc.returncode != 0:
                 return (change, changes[i + 1:], cmd, proc.stderr)
     return None
@@ -368,12 +363,10 @@
 
 def _main_pull(args):
     """Pull the change lists."""
-
+    repo_top = find_repo_top(os.getcwd())
+    project_dirs = build_project_name_dir_dict(args.manifest)
     branch_name = _get_local_branch_name_from_args(args)
 
-    manifest_path = _get_manifest_xml_from_args(args)
-    project_dirs = build_project_name_dir_dict(manifest_path)
-
     # Collect change lists
     change_lists = _get_change_lists_from_args(args)
     change_list_groups = group_and_sort_change_lists(change_lists)
@@ -384,6 +377,7 @@
         'merge_opt': args.merge,
         'pick_opt': args.pick,
         'project_dirs': project_dirs,
+        'repo_top': repo_top,
     }
 
     # Run the commands to pull the change lists
@@ -439,14 +433,6 @@
     return parser.parse_args()
 
 
-def _get_manifest_xml_from_args(args):
-    """Get the path to manifest.xml from args."""
-    manifest_path = args.manifest
-    if not args.manifest:
-        manifest_path = find_manifest_xml(os.getcwd())
-    return manifest_path
-
-
 def _get_change_lists_from_args(args):
     """Query the change lists by args."""
     url_opener = create_url_opener_from_args(args)