depot_tools: Run git_common_test in Python 3

Bug: 1009809
Change-Id: Idfcbd26de3420798f092c7fa55a6126d7c389a8c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/1834317
Reviewed-by: Anthony Polito <apolito@google.com>
Commit-Queue: Edward Lesmes <ehmaldonado@chromium.org>
diff --git a/git_common.py b/git_common.py
index 8e81357..e022ff3 100644
--- a/git_common.py
+++ b/git_common.py
@@ -6,12 +6,13 @@
 # Derived from https://gist.github.com/aljungberg/626518
 
 from __future__ import print_function
+from __future__ import unicode_literals
 
 import multiprocessing.pool
 from multiprocessing.pool import IMapIterator
 def wrapper(func):
   def wrap(self, timeout=None):
-    return func(self, timeout=timeout or 1e100)
+    return func(self, timeout=timeout or 1 << 31)
   return wrap
 IMapIterator.next = wrapper(IMapIterator.next)
 IMapIterator.__next__ = IMapIterator.next
@@ -303,11 +304,17 @@
 def once(function):
   """@Decorates |function| so that it only performs its action once, no matter
   how many times the decorated |function| is called."""
-  def _inner_gen():
-    yield function()
-    while True:
-      yield
-  return _inner_gen().next
+  has_run = [False]
+  def _wrapper(*args, **kwargs):
+    if not has_run[0]:
+      has_run[0] = True
+      function(*args, **kwargs)
+  return _wrapper
+
+
+def unicode_repr(s):
+  result = repr(s)
+  return result[1:] if result.startswith('u') else result
 
 
 ## Git functions
@@ -597,8 +604,8 @@
     treedict - { name: (mode, type, ref) }
   """
   with tempfile.TemporaryFile() as f:
-    for name, (mode, typ, ref) in treedict.iteritems():
-      f.write('%s %s %s\t%s\0' % (mode, typ, ref, name))
+    for name, (mode, typ, ref) in treedict.items():
+      f.write(('%s %s %s\t%s\0' % (mode, typ, ref, name)).encode())
     f.seek(0)
     return run('mktree', '-z', stdin=f)
 
@@ -612,7 +619,7 @@
     * 'cool_branch~2'
   """
   try:
-    return map(binascii.unhexlify, hash_multi(*commitrefs))
+    return [binascii.unhexlify(h) for h in hash_multi(*commitrefs)]
   except subprocess2.CalledProcessError:
     raise BadCommitRefException(commitrefs)
 
@@ -751,6 +758,7 @@
   kwargs.setdefault('shell', False)
   autostrip = kwargs.pop('autostrip', True)
   indata = kwargs.pop('indata', None)
+  decode = kwargs.pop('decode', True)
 
   cmd = (GIT_EXE, '-c', 'color.ui=never') + cmd
   proc = subprocess2.Popen(cmd, **kwargs)
@@ -760,8 +768,12 @@
     raise subprocess2.CalledProcessError(retcode, cmd, os.getcwd(), ret, err)
 
   if autostrip:
-    ret = (ret or '').strip()
-    err = (err or '').strip()
+    ret = (ret or b'').strip()
+    err = (err or b'').strip()
+
+  if decode:
+    ret = ret.decode('utf-8', 'replace')
+    err = err.decode('utf-8', 'replace')
 
   return ret, err
 
@@ -810,9 +822,9 @@
   def tokenizer(stream):
     acc = BytesIO()
     c = None
-    while c != '':
+    while c != b'':
       c = stream.read(1)
-      if c in (None, '', '\0'):
+      if c in (None, b'', b'\0'):
         if len(acc.getvalue()):
           yield acc.getvalue()
           acc = BytesIO()
@@ -821,12 +833,14 @@
 
   def parser(tokens):
     while True:
-      # Raises StopIteration if it runs out of tokens.
-      status_dest = next(tokens)
+      try:
+        status_dest = next(tokens).decode('utf-8')
+      except StopIteration:
+        return
       stat, dest = status_dest[:2], status_dest[3:]
       lstat, rstat = stat
       if lstat == 'R':
-        src = next(tokens)
+        src = next(tokens).decode('utf-8')
       else:
         src = dest
       yield (dest, stat_entry(lstat, rstat, src))
@@ -848,7 +862,7 @@
     # nothing to commit at this point.
     print('Nothing to commit; squashed branch is empty')
     return False
-  run('commit', '--no-verify', '-a', '-F', '-', indata=log_msg)
+  run('commit', '--no-verify', '-a', '-F', '-', indata=log_msg.encode())
   return True
 
 
@@ -858,7 +872,8 @@
 
 def thaw():
   took_action = False
-  for sha in (s.strip() for s in run_stream('rev-list', 'HEAD').xreadlines()):
+  for sha in run_stream('rev-list', 'HEAD').readlines():
+    sha = sha.strip().decode('utf-8')
     msg = run('show', '--format=%f%b', '-s', 'HEAD')
     match = FREEZE_MATCHER.match(msg)
     if not match:
@@ -899,7 +914,7 @@
   # TODO(iannucci): There is probably a more efficient way to do these.
   if top_down:
     while branch_tree:
-      this_pass = [(b, p) for b, p in branch_tree.iteritems()
+      this_pass = [(b, p) for b, p in branch_tree.items()
                    if p not in branch_tree]
       assert this_pass, "Branch tree has cycles: %r" % branch_tree
       for branch, parent in sorted(this_pass):
@@ -907,11 +922,11 @@
         del branch_tree[branch]
   else:
     parent_to_branches = collections.defaultdict(set)
-    for branch, parent in branch_tree.iteritems():
+    for branch, parent in branch_tree.items():
       parent_to_branches[parent].add(branch)
 
     while branch_tree:
-      this_pass = [(b, p) for b, p in branch_tree.iteritems()
+      this_pass = [(b, p) for b, p in branch_tree.items()
                    if not parent_to_branches[b]]
       assert this_pass, "Branch tree has cycles: %r" % branch_tree
       for branch, parent in sorted(this_pass):
@@ -1014,7 +1029,9 @@
     if info.upstream not in info_map and info.upstream not in missing_upstreams:
       missing_upstreams[info.upstream] = None
 
-  return dict(info_map.items() + missing_upstreams.items())
+  result = info_map.copy()
+  result.update(missing_upstreams)
+  return result
 
 
 def make_workdir_common(repository, new_workdir, files_to_symlink,