I made git-try also find diffs in one extra gip sub-repository
and append them to the patch sent to the try bots. This is 
very useful for chromium+webkit tries.

Review URL: http://codereview.chromium.org/212034

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@26817 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/git-try b/git-try
index 8d8babd..4a702e7 100755
--- a/git-try
+++ b/git-try
@@ -15,9 +15,11 @@
 import trychange
 
 
-def Backquote(cmd):
+def Backquote(cmd, cwd=None):
   """Like running `cmd` in a shell script."""
-  return subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0].strip()
+  return subprocess.Popen(cmd,
+                          cwd=cwd,
+                          stdout=subprocess.PIPE).communicate()[0].strip()
 
 
 def GetTryServerConfig():
@@ -37,18 +39,18 @@
   return locals
 
 
-def GetBranchName():
+def GetBranchName(working_dir=None):
   """Return name of current git branch."""
-  branch = Backquote(['git', 'symbolic-ref', 'HEAD'])
+  branch = Backquote(['git', 'symbolic-ref', 'HEAD'], working_dir)
   if not branch.startswith('refs/heads/'):
     raise "Couldn't figure out branch name"
   branch = branch[len('refs/heads/'):]
   return branch
 
 
-def GetPatchName():
+def GetPatchName(working_dir=None):
   """Construct a name for this patch."""
-  short_sha = Backquote(['git', 'rev-parse', '--short=4', 'HEAD'])
+  short_sha = Backquote(['git', 'rev-parse', '--short=4', 'HEAD'], working_dir)
   return GetBranchName() + '-' + short_sha
 
 
@@ -61,26 +63,59 @@
   return Backquote(['git', 'config',
                     'branch.%s.rietveldpatchset' % GetBranchName()])
 
+def GetSubRepWorkingDir(sub_rep_path):
+  """Computes the path to the sub repository"""
+  if sub_rep_path:
+    root_dir = os.path.abspath(Backquote(['git', 'rev-parse', '--show-cdup']))
+    return os.path.join(root_dir, sub_rep_path)
+  return None
 
-def GetMungedDiff(branch, prefix='src/'):
-  """Get the diff we'll send to the try server.  We munge paths to match svn."""
+def GetMungedDiff(branch, prefix, sub_rep_path):
+  """Get the diff we'll send to the try server.  We munge paths to match svn.
+     We add the prefix that the try bot is expecting. If sub_rep_path is 
+     provided, diff will be calculated in the sub repository."""
   # Make the following changes:
   # - Prepend "src/" (or some other prefix) to paths as svn is expecting
   # - In the case of added files, replace /dev/null with the path to the file
   #   being added.
+
+  cwd = GetSubRepWorkingDir(sub_rep_path)
+
   output = []
   if not branch:
     # Try to guess the upstream branch.
-    branch = Backquote(['git', 'cl', 'upstream'])
-  diff = subprocess.Popen(['git', 'diff-tree', '-p', '--no-prefix',
-                           branch, 'HEAD'],
-                          stdout=subprocess.PIPE).stdout.readlines()
+    branch = Backquote(['git', 'cl', 'upstream'], cwd)
+  command = ['git', 'diff-tree', '-p']
+
+  new_cwd = None
+  if not sub_rep_path:
+    command.extend(['--no-prefix'])
+  else:
+    # Append /
+    sub_rep_path = os.path.join(sub_rep_path, '')
+    # Add the right prefix
+    command.extend(['--src-prefix=' + sub_rep_path])
+    command.extend(['--dst-prefix=' + sub_rep_path])
+    
+  command.extend([branch, 'HEAD'])
+
+  # Run diff tree
+  diff = subprocess.Popen(command,
+                          stdout=subprocess.PIPE,
+                          cwd=cwd).stdout.readlines()
+  # Replace --- /dev/null with --- <new file name>
   for i in range(len(diff)):
     line = diff[i]
     if line.startswith('--- /dev/null'):
-      line = '--- %s' % prefix + diff[i+1][4:]
-    elif line.startswith('--- ') or line.startswith('+++ '):
-      line = line[0:4] + prefix + line[4:]
+      line = '--- %s' % diff[i+1][4:]
+    output.append(line)
+  diff = output
+
+  # Add root prefix
+  output = []
+  for line in diff:
+    if line.startswith('--- ') or line.startswith('+++ '):
+      line = line[0:4] + os.path.join(prefix,line[4:])
     output.append(line)
 
   munged_diff = ''.join(output)
@@ -89,6 +124,20 @@
 
   return munged_diff
 
+def OneRepositoryDiff(diff_file, patch_names, branch, prefix, sub_rep_path):
+  """Computes a diff for one git repository at a given path against a given
+     branch. Writes the diff into diff_file and appends a name to the
+     patch_names list."""
+  
+  diff = GetMungedDiff(branch, prefix, sub_rep_path)
+
+  # Write the diff out
+  diff_file.write(diff)
+
+  # Add patch name to list of patches
+  patch_name = GetPatchName(GetSubRepWorkingDir(sub_rep_path))
+  patch_names.extend([patch_name])
+
 
 def ValidEmail(email):
   return re.match(r"^[a-zA-Z0-9._%-+]+@[a-zA-Z0-9._%-]+.[a-zA-Z]{2,6}$", email)
@@ -119,34 +168,63 @@
                     help="Make the try run use be a clobber build")
   parser.add_option("-r", "--revision",
                     help="Specify the SVN base revision to use")
+  parser.add_option("--root", default="src", metavar="PATH",
+                    help="Specify the root prefix that is appended to paths "
+                         "in the patch")
+  parser.add_option("--dry_run", action="store_true",
+                    help="Print the diff but don't send it to the try bots")
+  parser.add_option("--sub_rep", nargs=2, action="append", default=[], 
+                    metavar="PATH BRANCH", 
+                    help="Specify a path to a git sub-repository and a branch "
+                         "to diff with in order to simultanously try changes "
+                         "in multiple git repositories. Option may be "
+                         "specified multiple times.")
+  parser.add_option("--webkit", metavar="BRANCH",
+                    help="Specify webkit branch. Syntactic sugar for "
+                         "--sub_rep third_party/WebKit/ <branch>")
+                         
   (options, args) = parser.parse_args(sys.argv)
 
+  if options.webkit:
+    options.sub_rep.extend([('third_party/WebKit/', options.webkit)])
+
   branch = None
   if len(args) > 1:
     branch = args[1]
+  patch_names = []
 
-  patch_name = GetPatchName()
-  diff = GetMungedDiff(branch)
-
-  # Write the diff out to a temporary file
+  # Dump all diffs into one diff file.
   diff_file = tempfile.NamedTemporaryFile()
-  diff_file.write(diff)
+  
+  # Calculate diff for main git repository.
+  OneRepositoryDiff(diff_file, patch_names, branch, options.root, None)
+  
+  # Calculate diff for each extra git repository.
+  for path_and_branch in options.sub_rep:
+    OneRepositoryDiff(diff_file,
+                      patch_names,
+                      path_and_branch[1],
+                      options.root,
+                      path_and_branch[0]) 
+  # Make diff file ready for reading.
   diff_file.flush()
 
+  # Concatenate patch names
+  # Prepare args for TryChange
   email = GetEmail()
   user = email.partition('@')[0]
   args = [
       '-u', user,
       '-e', email,
-      '-n', patch_name,
-      '--diff', diff_file.name,
+      '-n', '-'.join(patch_names),
+      '--diff', diff_file.name, 
   ]
 
   # Send to try server via HTTP if we can parse the config, otherwise
   # upload via SVN.
   config = GetTryServerConfig()
   if config is not None:
-    sendmsg = "Sending %s using HTTP..." % patch_name
+    sendmsg = "Sending %s using HTTP..." % '-'.join(patch_names)
     args.extend(['--use_http'])
     if config['try_server_http_host'] is not None:
       args.extend(['--host', config['try_server_http_host']])
@@ -174,5 +252,9 @@
         '--patchset', GetRietveldPatchsetNumber(),
     ])
 
+  if options.dry_run:
+    print open(diff_file.name, 'r').read()
+    exit(0)
+
   print sendmsg
   TryChange(args=args)