def main(argv):
    assert len(argv) == 1, "No arguments expected"
    upfn = upstream
    cur = current_branch()
    if cur == 'HEAD':

        def _upfn(b):
            parent = upstream(b)
            if parent:
                return hash_one(parent)

        upfn = _upfn
        cur = hash_one(cur)
    downstreams = [b for b in branches() if upfn(b) == cur]
    if not downstreams:
        return "No downstream branches"
    elif len(downstreams) == 1:
        run('checkout', downstreams[0])
    else:
        high = len(downstreams) - 1
        print
        while True:
            print "Please select a downstream branch"
            for i, b in enumerate(downstreams):
                print "  %d. %s" % (i, b)
            r = raw_input("Selection (0-%d)[0]: " % high).strip() or '0'
            if not r.isdigit() or (0 > int(r) > high):
                print "Invalid choice."
            else:
                run('checkout', downstreams[int(r)])
                break
Example #2
0
    def start(self):
        self.__branches_info = get_branches_info(
            include_tracking_status=self.verbosity >= 1)
        roots = set()

        # A map of parents to a list of their children.
        for branch, branch_info in self.__branches_info.iteritems():
            if not branch_info:
                continue

            parent = branch_info.upstream
            if not self.__branches_info[parent]:
                branch_upstream = upstream(branch)
                # If git can't find the upstream, mark the upstream as gone.
                if branch_upstream:
                    parent = branch_upstream
                else:
                    self.__gone_branches.add(parent)
                # A parent that isn't in the branches info is a root.
                roots.add(parent)

            self.__parent_map[parent].append(branch)

        self.__current_branch = current_branch()
        self.__current_hash = hash_one('HEAD', short=True)
        self.__tag_set = tags()

        if roots:
            for root in sorted(roots):
                self.__append_branch(root)
        else:
            no_branches = OutputLine()
            no_branches.append('No User Branches')
            self.output.append(no_branches)
def main(argv):
  assert len(argv) == 1, "No arguments expected"
  upfn = upstream
  cur = current_branch()
  if cur == 'HEAD':
    def _upfn(b):
      parent = upstream(b)
      if parent:
        return hash_one(parent)
    upfn = _upfn
    cur = hash_one(cur)
  downstreams = [b for b in branches() if upfn(b) == cur]
  if not downstreams:
    return "No downstream branches"
  elif len(downstreams) == 1:
    run('checkout', downstreams[0])
  else:
    high = len(downstreams) - 1
    print
    while True:
      print "Please select a downstream branch"
      for i, b in enumerate(downstreams):
        print "  %d. %s" % (i, b)
      r = raw_input("Selection (0-%d)[0]: " % high).strip() or '0'
      if not r.isdigit() or (0 > int(r) > high):
        print "Invalid choice."
      else:
        run('checkout', downstreams[int(r)])
        break
Example #4
0
def main(args):
  current = current_branch()
  if current == 'HEAD':
    current = None
  old_name_help = 'The old branch to rename.'
  if current:
    old_name_help += ' (default %(default)r)'

  parser = argparse.ArgumentParser()
  parser.add_argument('old_name', nargs=('?' if current else 1),
                      help=old_name_help, default=current)
  parser.add_argument('new_name', help='The new branch name.')

  opts = parser.parse_args(args)

  # when nargs=1, we get a list :(
  if isinstance(opts.old_name, list):
    opts.old_name = opts.old_name[0]

  try:
    run('branch', '-m', opts.old_name, opts.new_name)

    # update the downstreams
    for branch, merge in branch_config_map('merge').items():
      if merge == 'refs/heads/' + opts.old_name:
        # Only care about local branches
        if branch_config(branch, 'remote') == '.':
          set_branch_config(branch, 'merge', 'refs/heads/' + opts.new_name)
  except subprocess2.CalledProcessError as cpe:
    sys.stderr.write(cpe.stderr)
    return 1
  return 0
Example #5
0
def main(args):
  default_args = git.config_list('depot-tools.upstream-diff.default-args')
  args = default_args + args

  parser = argparse.ArgumentParser()
  parser.add_argument('--wordwise', action='store_true', default=False,
                      help=(
                        'Print a colorized wordwise diff '
                        'instead of line-wise diff'))
  opts, extra_args = parser.parse_known_args(args)

  cur = git.current_branch()
  if not cur or cur == 'HEAD':
    print 'fatal: Cannot perform git-upstream-diff while not on a branch'
    return 1

  par = git.upstream(cur)
  if not par:
    print 'fatal: No upstream configured for branch \'%s\'' % cur
    return 1

  cmd = [git.GIT_EXE, 'diff', '--patience', '-C', '-C']
  if opts.wordwise:
    cmd += ['--word-diff=color', r'--word-diff-regex=(\w+|[^[:space:]])']
  cmd += [git.get_or_create_merge_base(cur, par)]

  cmd += extra_args

  return subprocess2.check_call(cmd)
def main(args):
  current = current_branch()
  if current == 'HEAD':
    current = None
  old_name_help = 'The old branch to rename.'
  if current:
    old_name_help += ' (default %(default)r)'

  parser = argparse.ArgumentParser()
  parser.add_argument('old_name', nargs=('?' if current else 1),
                      help=old_name_help, default=current)
  parser.add_argument('new_name', help='The new branch name.')

  opts = parser.parse_args(args)

  # when nargs=1, we get a list :(
  if isinstance(opts.old_name, list):
    opts.old_name = opts.old_name[0]

  try:
    run('branch', '-m', opts.old_name, opts.new_name)

    # update the downstreams
    for branch, merge in branch_config_map('merge').iteritems():
      if merge == 'refs/heads/' + opts.old_name:
        # Only care about local branches
        if branch_config(branch, 'remote') == '.':
          set_branch_config(branch, 'merge', 'refs/heads/' + opts.new_name)
  except subprocess2.CalledProcessError as cpe:
    sys.stderr.write(cpe.stderr)
    return 1
Example #7
0
def main(argv):
    colorama.init()
    assert len(argv) == 1, "No arguments expected"
    branch_map = {}
    par_map = collections.defaultdict(list)
    for branch in branches():
        par = upstream(branch) or NO_UPSTREAM
        branch_map[branch] = par
        par_map[par].append(branch)

    current = current_branch()
    hashes = hash_multi(current, *branch_map.keys())
    current_hash = hashes[0]
    par_hashes = {
        k: hashes[i + 1]
        for i, k in enumerate(branch_map.iterkeys())
    }
    par_hashes[NO_UPSTREAM] = 0
    tag_set = tags()
    while par_map:
        for parent in par_map:
            if parent not in branch_map:
                if parent not in par_hashes:
                    par_hashes[parent] = hash_one(parent)
                print_branch(current, current_hash, parent, par_hashes,
                             par_map, branch_map, tag_set)
                break
Example #8
0
def main(args):
    default_args = git.config_list('depot-tools.upstream-diff.default-args')
    args = default_args + args

    parser = argparse.ArgumentParser()
    parser.add_argument('--wordwise',
                        action='store_true',
                        default=False,
                        help=('Print a colorized wordwise diff '
                              'instead of line-wise diff'))
    opts, extra_args = parser.parse_known_args(args)

    cur = git.current_branch()
    if not cur or cur == 'HEAD':
        print 'fatal: Cannot perform git-upstream-diff while not on a branch'
        return 1

    par = git.upstream(cur)
    if not par:
        print 'fatal: No upstream configured for branch \'%s\'' % cur
        return 1

    cmd = [git.GIT_EXE, 'diff', '--patience', '-C', '-C']
    if opts.wordwise:
        cmd += ['--word-diff=color', r'--word-diff-regex=(\w+|[^[:space:]])']
    cmd += [git.get_or_create_merge_base(cur, par)]

    cmd += extra_args

    subprocess2.check_call(cmd)
  def start(self):
    self.__branches_info = get_branches_info(
        include_tracking_status=self.verbosity >= 1)
    roots = set()

    # A map of parents to a list of their children.
    for branch, branch_info in self.__branches_info.iteritems():
      if not branch_info:
        continue

      parent = branch_info.upstream
      if parent and not self.__branches_info[parent]:
        branch_upstream = upstream(branch)
        # If git can't find the upstream, mark the upstream as gone.
        if branch_upstream:
          parent = branch_upstream
        else:
          self.__gone_branches.add(parent)
        # A parent that isn't in the branches info is a root.
        roots.add(parent)

      self.__parent_map[parent].append(branch)

    self.__current_branch = current_branch()
    self.__current_hash = hash_one('HEAD', short=True)
    self.__tag_set = tags()

    if roots:
      for root in sorted(roots):
        self.__append_branch(root)
    else:
      no_branches = OutputLine()
      no_branches.append('No User Branches')
      self.output.append(no_branches)
Example #10
0
def main(args):
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        description=__doc__,
    )
    parser.add_argument('branch_name')
    g = parser.add_mutually_exclusive_group()
    g.add_argument('--upstream-current',
                   '--upstream_current',
                   action='store_true',
                   help='set upstream branch to current branch.')
    g.add_argument('--upstream',
                   metavar='REF',
                   default=root(),
                   help='upstream branch (or tag) to track.')
    g.add_argument('--inject-current',
                   '--inject_current',
                   action='store_true',
                   help='new branch adopts current branch\'s upstream,' +
                   ' and new branch becomes current branch\'s upstream.')
    g.add_argument('--lkgr',
                   action='store_const',
                   const='lkgr',
                   dest='upstream',
                   help='set basis ref for new branch to lkgr.')

    opts = parser.parse_args(args)

    try:
        if opts.inject_current:
            below = current_branch()
            if below is None:
                raise Exception('no current branch')
            above = upstream(below)
            if above is None:
                raise Exception('branch %s has no upstream' % (below))
            run('checkout', '--track', above, '-b', opts.branch_name)
            run('branch', '--set-upstream-to', opts.branch_name, below)
        elif opts.upstream_current:
            run('checkout', '--track', '-b', opts.branch_name)
        else:
            if opts.upstream in tags():
                # TODO(iannucci): ensure that basis_ref is an ancestor of HEAD?
                run('checkout', '--no-track', '-b', opts.branch_name,
                    hash_one(opts.upstream))
                set_config('branch.%s.remote' % opts.branch_name, '.')
                set_config('branch.%s.merge' % opts.branch_name, opts.upstream)
            else:
                # TODO(iannucci): Detect unclean workdir then stash+pop if we need to
                # teleport to a conflicting portion of history?
                run('checkout', '--track', opts.upstream, '-b',
                    opts.branch_name)
        get_or_create_merge_base(opts.branch_name)
    except subprocess2.CalledProcessError as cpe:
        sys.stdout.write(cpe.stdout)
        sys.stderr.write(cpe.stderr)
        return 1
    sys.stderr.write('Switched to branch %s.\n' % opts.branch_name)
    return 0
Example #11
0
def SplitCl(description_file, comment_file, changelist, cmd_upload):
    """"Splits a branch into smaller branches and uploads CLs.

  Args:
    description_file: File containing the description of uploaded CLs.
    comment_file: File containing the comment of uploaded CLs.
    changelist: The Changelist class.
    cmd_upload: The function associated with the git cl upload command.

  Returns:
    0 in case of success. 1 in case of error.
  """
    description = AddUploadedByGitClSplitToDescription(
        ReadFile(description_file))
    comment = ReadFile(comment_file) if comment_file else None

    try:
        EnsureInGitRepository()

        cl = changelist()
        change = cl.GetChange(cl.GetCommonAncestorWithUpstream(), None)
        files = change.AffectedFiles()

        if not files:
            print 'Cannot split an empty CL.'
            return 1

        author = git.run('config', 'user.email').strip() or None
        refactor_branch = git.current_branch()
        assert refactor_branch, "Can't run from detached branch."
        refactor_branch_upstream = git.upstream(refactor_branch)
        assert refactor_branch_upstream, \
            "Branch %s must have an upstream." % refactor_branch

        owners_database = owners.Database(change.RepositoryRoot(), file,
                                          os.path)
        owners_database.load_data_needed_for([f.LocalPath() for f in files])

        files_split_by_owners = GetFilesSplitByOwners(owners_database, files)

        print('Will split current branch (' + refactor_branch + ') in ' +
              str(len(files_split_by_owners)) + ' CLs.\n')

        for directory, files in files_split_by_owners.iteritems():
            # Use '/' as a path separator in the branch name and the CL description
            # and comment.
            directory = directory.replace(os.path.sep, '/')
            # Upload the CL.
            UploadCl(refactor_branch, refactor_branch_upstream, directory,
                     files, author, description, comment, owners_database,
                     changelist, cmd_upload)

        # Go back to the original branch.
        git.run('checkout', refactor_branch)

    except subprocess2.CalledProcessError as cpe:
        sys.stderr.write(cpe.stderr)
        return 1
    return 0
def main(args):
    root_ref = root()

    parser = argparse.ArgumentParser()
    g = parser.add_mutually_exclusive_group()
    g.add_argument('new_parent',
                   nargs='?',
                   help='New parent branch (or tag) to reparent to.')
    g.add_argument('--root',
                   action='store_true',
                   help='Reparent to the configured root branch (%s).' %
                   root_ref)
    g.add_argument('--lkgr',
                   action='store_true',
                   help='Reparent to the lkgr tag.')
    opts = parser.parse_args(args)

    # TODO(iannucci): Allow specification of the branch-to-reparent

    branch = current_branch()
    if opts.root:
        new_parent = root_ref
    elif opts.lkgr:
        new_parent = 'lkgr'
    else:
        new_parent = opts.new_parent
    cur_parent = upstream(branch)

    if branch == 'HEAD' or not branch:
        parser.error('Must be on the branch you want to reparent')
    if new_parent == cur_parent:
        parser.error('Cannot reparent a branch to its existing parent')

    mbase = get_or_create_merge_base(branch, cur_parent)

    all_tags = tags()
    if cur_parent in all_tags:
        cur_parent += ' [tag]'

    try:
        run('show-ref', new_parent)
    except subprocess2.CalledProcessError:
        print >> sys.stderr, 'fatal: invalid reference: %s' % new_parent
        return 1

    if new_parent in all_tags:
        print("Reparenting %s to track %s [tag] (was %s)" %
              (branch, new_parent, cur_parent))
        set_branch_config(branch, 'remote', '.')
        set_branch_config(branch, 'merge', new_parent)
    else:
        print("Reparenting %s to track %s (was %s)" %
              (branch, new_parent, cur_parent))
        run('branch', '--set-upstream-to', new_parent, branch)

    manual_merge_base(branch, mbase, new_parent)

    # TODO(iannucci): ONLY rebase-update the branch which moved (and dependants)
    return git_rebase_update.main(['--no-fetch'])
Example #13
0
def main(argv, outbuf):
    if '-h' in argv or '--help' in argv:
        _print_help(outbuf)
        return 0

    map_extra = git_common.get_config_list('depot_tools.map_extra')
    cmd = [
        git_common.GIT_EXE, 'log',
        git_common.root(), '--graph', '--branches', '--tags', '--color=always',
        '--date=short', '--pretty=format:%H%x00%D%x00%cd%x00%s'
    ] + map_extra + argv

    log_proc = subprocess2.Popen(cmd, stdout=subprocess2.PIPE, shell=False)

    current = git_common.current_branch()
    all_tags = set(git_common.tags())
    all_branches = set(git_common.branches())
    if current in all_branches:
        all_branches.remove(current)

    merge_base_map = {}
    for branch in all_branches:
        merge_base = git_common.get_or_create_merge_base(branch)
        if merge_base:
            merge_base_map.setdefault(merge_base, set()).add(branch)

    for merge_base, branches in merge_base_map.items():
        merge_base_map[merge_base] = ', '.join(branches)

    try:
        for line in log_proc.stdout:
            if b'\x00' not in line:
                outbuf.write(line)
                continue

            graph, commit, branch_list, commit_date, subject = _parse_log_line(
                line)

            if 'HEAD' in branch_list:
                graph = graph.replace('*', BLUE_BACK + '*')

            line = '{graph}{commit}\t{branches}{date} ~ {subject}'.format(
                graph=graph,
                commit=BRIGHT_RED + commit[:10] + RESET,
                branches=_color_branch_list(branch_list, all_branches,
                                            all_tags, current),
                date=YELLOW + commit_date + RESET,
                subject=subject)

            if commit in merge_base_map:
                line += '    <({})'.format(WHITE + merge_base_map[commit] +
                                           RESET)

            line += os.linesep
            outbuf.write(line.encode('utf-8', 'replace'))
    except (BrokenPipeError, KeyboardInterrupt):
        pass
    return 0
Example #14
0
def main(args):
  root_ref = root()

  parser = argparse.ArgumentParser()
  g = parser.add_mutually_exclusive_group()
  g.add_argument('new_parent', nargs='?',
                 help='New parent branch (or tag) to reparent to.')
  g.add_argument('--root', action='store_true',
                 help='Reparent to the configured root branch (%s).' % root_ref)
  g.add_argument('--lkgr', action='store_true',
                 help='Reparent to the lkgr tag.')
  opts = parser.parse_args(args)

  # TODO(iannucci): Allow specification of the branch-to-reparent

  branch = current_branch()
  if opts.root:
    new_parent = root_ref
  elif opts.lkgr:
    new_parent = 'lkgr'
  else:
    if not opts.new_parent:
      parser.error('Must specify new parent somehow')
    new_parent = opts.new_parent
  cur_parent = upstream(branch)

  if branch == 'HEAD' or not branch:
    parser.error('Must be on the branch you want to reparent')
  if new_parent == cur_parent:
    parser.error('Cannot reparent a branch to its existing parent')

  mbase = get_or_create_merge_base(branch, cur_parent)

  all_tags = tags()
  if cur_parent in all_tags:
    cur_parent += ' [tag]'

  try:
    run('show-ref', new_parent)
  except subprocess2.CalledProcessError:
    print >> sys.stderr, 'fatal: invalid reference: %s' % new_parent
    return 1

  if new_parent in all_tags:
    print ("Reparenting %s to track %s [tag] (was %s)"
           % (branch, new_parent, cur_parent))
    set_branch_config(branch, 'remote', '.')
    set_branch_config(branch, 'merge', new_parent)
  else:
    print ("Reparenting %s to track %s (was %s)"
           % (branch, new_parent, cur_parent))
    run('branch', '--set-upstream-to', new_parent, branch)

  manual_merge_base(branch, mbase, new_parent)

  # TODO(iannucci): ONLY rebase-update the branch which moved (and dependants)
  return git_rebase_update.main(['--no-fetch'])
    def start(self):
        self.__branches_info = get_branches_info(
            include_tracking_status=self.verbosity >= 1)
        if (self.verbosity >= 2):
            # Avoid heavy import unless necessary.
            from git_cl import get_cl_statuses, color_for_status, Changelist

            change_cls = [
                Changelist(branchref='refs/heads/' + b)
                for b in self.__branches_info.keys() if b
            ]
            status_info = get_cl_statuses(change_cls,
                                          fine_grained=self.verbosity > 2,
                                          max_processes=self.maxjobs)

            # This is a blocking get which waits for the remote CL status to be
            # retrieved.
            for cl, status in status_info:
                self.__status_info[cl.GetBranch()] = (cl.GetIssueURL(),
                                                      color_for_status(status),
                                                      status)

        roots = set()

        # A map of parents to a list of their children.
        for branch, branch_info in self.__branches_info.items():
            if not branch_info:
                continue

            parent = branch_info.upstream
            if self.__check_cycle(branch):
                continue
            if not self.__branches_info[parent]:
                branch_upstream = upstream(branch)
                # If git can't find the upstream, mark the upstream as gone.
                if branch_upstream:
                    parent = branch_upstream
                else:
                    self.__gone_branches.add(parent)
                # A parent that isn't in the branches info is a root.
                roots.add(parent)

            self.__parent_map[parent].append(branch)

        self.__current_branch = current_branch()
        self.__current_hash = hash_one('HEAD', short=True)
        self.__tag_set = tags()

        if roots:
            for root in sorted(roots):
                self.__append_branch(root)
        else:
            no_branches = OutputLine()
            no_branches.append('No User Branches')
            self.output.append(no_branches)
Example #16
0
def find_return_branch():
  """Finds the branch which we should return to after rebase-update completes.

  This value may persist across multiple invocations of rebase-update, if
  rebase-update runs into a conflict mid-way.
  """
  return_branch = git.config(STARTING_BRANCH_KEY)
  if not return_branch:
    return_branch = git.current_branch()
    if return_branch != 'HEAD':
      git.set_config(STARTING_BRANCH_KEY, return_branch)

  return return_branch
def find_return_branch():
    """Finds the branch which we should return to after rebase-update completes.

  This value may persist across multiple invocations of rebase-update, if
  rebase-update runs into a conflict mid-way.
  """
    return_branch = git.config(STARTING_BRANCH_KEY)
    if not return_branch:
        return_branch = git.current_branch()
        if return_branch != 'HEAD':
            git.set_config(STARTING_BRANCH_KEY, return_branch)

    return return_branch
def main(args):
  parser = argparse.ArgumentParser(
    formatter_class=argparse.ArgumentDefaultsHelpFormatter,
    description=__doc__,
  )
  parser.add_argument('branch_name')
  g = parser.add_mutually_exclusive_group()
  g.add_argument('--upstream-current', '--upstream_current',
                 action='store_true',
                 help='set upstream branch to current branch.')
  g.add_argument('--upstream', metavar='REF', default=root(),
                 help='upstream branch (or tag) to track.')
  g.add_argument('--inject-current', '--inject_current',
                 action='store_true',
                 help='new branch adopts current branch\'s upstream,' +
                 ' and new branch becomes current branch\'s upstream.')
  g.add_argument('--lkgr', action='store_const', const='lkgr', dest='upstream',
                 help='set basis ref for new branch to lkgr.')

  opts = parser.parse_args(args)

  try:
    if opts.inject_current:
      below = current_branch()
      if below is None:
        raise Exception('no current branch')
      above = upstream(below)
      if above is None:
        raise Exception('branch %s has no upstream' % (below))
      run('checkout', '--track', above, '-b', opts.branch_name)
      run('branch', '--set-upstream-to', opts.branch_name, below)
    elif opts.upstream_current:
      run('checkout', '--track', '-b', opts.branch_name)
    else:
      if opts.upstream in tags():
        # TODO(iannucci): ensure that basis_ref is an ancestor of HEAD?
        run('checkout', '--no-track', '-b', opts.branch_name,
            hash_one(opts.upstream))
        set_config('branch.%s.remote' % opts.branch_name, '.')
        set_config('branch.%s.merge' % opts.branch_name, opts.upstream)
      else:
        # TODO(iannucci): Detect unclean workdir then stash+pop if we need to
        # teleport to a conflicting portion of history?
        run('checkout', '--track', opts.upstream, '-b', opts.branch_name)
    get_or_create_merge_base(opts.branch_name)
  except subprocess2.CalledProcessError as cpe:
    sys.stdout.write(cpe.stdout)
    sys.stderr.write(cpe.stderr)
    return 1
  sys.stderr.write('Switched to branch %s.\n' % opts.branch_name)
  return 0
  def start(self):
    self.__branches_info = get_branches_info(
        include_tracking_status=self.verbosity >= 1)
    if (self.verbosity >= 2):
      # Avoid heavy import unless necessary.
      from git_cl import get_cl_statuses, color_for_status, Changelist

      change_cls = [Changelist(branchref='refs/heads/'+b)
                    for b in self.__branches_info.keys() if b]
      status_info = get_cl_statuses(change_cls,
                                    fine_grained=self.verbosity > 2,
                                    max_processes=self.maxjobs)

      # This is a blocking get which waits for the remote CL status to be
      # retrieved.
      for cl, status in status_info:
        self.__status_info[cl.GetBranch()] = (cl.GetIssueURL(),
                                              color_for_status(status),
                                              status)

    roots = set()

    # A map of parents to a list of their children.
    for branch, branch_info in self.__branches_info.iteritems():
      if not branch_info:
        continue

      parent = branch_info.upstream
      if not self.__branches_info[parent]:
        branch_upstream = upstream(branch)
        # If git can't find the upstream, mark the upstream as gone.
        if branch_upstream:
          parent = branch_upstream
        else:
          self.__gone_branches.add(parent)
        # A parent that isn't in the branches info is a root.
        roots.add(parent)

      self.__parent_map[parent].append(branch)

    self.__current_branch = current_branch()
    self.__current_hash = hash_one('HEAD', short=True)
    self.__tag_set = tags()

    if roots:
      for root in sorted(roots):
        self.__append_branch(root)
    else:
      no_branches = OutputLine()
      no_branches.append('No User Branches')
      self.output.append(no_branches)
Example #20
0
    def start(self):
        self.__branches_info = get_branches_info(
            include_tracking_status=self.verbosity >= 1)
        if (self.verbosity >= 2):
            # Avoid heavy import unless necessary.
            from git_cl import get_cl_statuses

            status_info = get_cl_statuses(self.__branches_info.keys(),
                                          fine_grained=self.verbosity > 2,
                                          max_processes=self.maxjobs)

            for _ in xrange(len(self.__branches_info)):
                # This is a blocking get which waits for the remote CL status to be
                # retrieved.
                (branch, url, color) = status_info.next()
                self.__status_info[branch] = (url, color)

        roots = set()

        # A map of parents to a list of their children.
        for branch, branch_info in self.__branches_info.iteritems():
            if not branch_info:
                continue

            parent = branch_info.upstream
            if not self.__branches_info[parent]:
                branch_upstream = upstream(branch)
                # If git can't find the upstream, mark the upstream as gone.
                if branch_upstream:
                    parent = branch_upstream
                else:
                    self.__gone_branches.add(parent)
                # A parent that isn't in the branches info is a root.
                roots.add(parent)

            self.__parent_map[parent].append(branch)

        self.__current_branch = current_branch()
        self.__current_hash = hash_one('HEAD', short=True)
        self.__tag_set = tags()

        if roots:
            for root in sorted(roots):
                self.__append_branch(root)
        else:
            no_branches = OutputLine()
            no_branches.append('No User Branches')
            self.output.append(no_branches)
Example #21
0
def main(args):
    default_args = git.get_config_list(
        'depot-tools.upstream-diff.default-args')
    args = default_args + args

    current_branch = git.current_branch()

    parser = argparse.ArgumentParser()
    parser.add_argument('--wordwise',
                        action='store_true',
                        default=False,
                        help=('Print a colorized wordwise diff '
                              'instead of line-wise diff'))
    parser.add_argument('--branch',
                        default=current_branch,
                        help='Show changes from a different branch. Passing '
                        '"HEAD" is the same as omitting this option (it '
                        'diffs against the current branch)')
    opts, extra_args = parser.parse_known_args(args)

    if opts.branch == 'HEAD':
        opts.branch = current_branch

    if not opts.branch or opts.branch == 'HEAD':
        print('fatal: Cannot perform git-upstream-diff while not on a branch')
        return 1

    par = git.upstream(opts.branch)
    if not par:
        print('fatal: No upstream configured for branch \'%s\'' % opts.branch)
        return 1

    cmd = [
        git.GIT_EXE, '-c', 'core.quotePath=false', 'diff', '--patience', '-C',
        '-C'
    ]
    if opts.wordwise:
        cmd += ['--word-diff=color', r'--word-diff-regex=(\w+|[^[:space:]])']
    cmd += [git.get_or_create_merge_base(opts.branch, par)]
    # Only specify the end commit if it is not the current branch, this lets the
    # diff include uncommitted changes when diffing the current branch.
    if opts.branch != current_branch:
        cmd += [opts.branch]

    cmd += extra_args

    return subprocess2.check_call(cmd)
def find_return_branch_workdir():
  """Finds the branch and working directory which we should return to after
  rebase-update completes.

  These values may persist across multiple invocations of rebase-update, if
  rebase-update runs into a conflict mid-way.
  """
  return_branch = git.config(STARTING_BRANCH_KEY)
  workdir = git.config(STARTING_WORKDIR_KEY)
  if not return_branch:
    workdir = os.getcwd()
    git.set_config(STARTING_WORKDIR_KEY, workdir)
    return_branch = git.current_branch()
    if return_branch != 'HEAD':
      git.set_config(STARTING_BRANCH_KEY, return_branch)

  return return_branch, workdir
Example #23
0
def main(args):
    parser = argparse.ArgumentParser()
    parser.add_argument('--pick',
                        help=('The number to pick if this command would '
                              'prompt'))
    opts = parser.parse_args(args)

    upfn = upstream
    cur = current_branch()
    if cur == 'HEAD':

        def _upfn(b):
            parent = upstream(b)
            if parent:
                return hash_one(parent)

        upfn = _upfn
        cur = hash_one(cur)
    downstreams = [b for b in branches() if upfn(b) == cur]
    if not downstreams:
        return "No downstream branches"
    elif len(downstreams) == 1:
        run_stream('checkout',
                   downstreams[0],
                   stdout=sys.stdout,
                   stderr=sys.stderr)
    else:
        high = len(downstreams) - 1
        while True:
            print "Please select a downstream branch"
            for i, b in enumerate(downstreams):
                print "  %d. %s" % (i, b)
            prompt = "Selection (0-%d)[0]: " % high
            r = opts.pick
            if r:
                print prompt + r
            else:
                r = raw_input(prompt).strip() or '0'
            if not r.isdigit() or (0 > int(r) > high):
                print "Invalid choice."
            else:
                run_stream('checkout',
                           downstreams[int(r)],
                           stdout=sys.stdout,
                           stderr=sys.stderr)
                break
def find_return_branch_workdir():
    """Finds the branch and working directory which we should return to after
  rebase-update completes.

  These values may persist across multiple invocations of rebase-update, if
  rebase-update runs into a conflict mid-way.
  """
    return_branch = git.config(STARTING_BRANCH_KEY)
    workdir = git.config(STARTING_WORKDIR_KEY)
    if not return_branch:
        workdir = os.getcwd()
        git.set_config(STARTING_WORKDIR_KEY, workdir)
        return_branch = git.current_branch()
        if return_branch != 'HEAD':
            git.set_config(STARTING_BRANCH_KEY, return_branch)

    return return_branch, workdir
Example #25
0
def main(argv):
    parser = argparse.ArgumentParser(
        description=__doc__.strip().splitlines()[0],
        epilog=' '.join(__doc__.strip().splitlines()[1:]))
    g = parser.add_mutually_exclusive_group()
    g.add_argument(
        'merge_base',
        nargs='?',
        help='The new hash to use as the merge base for the current branch')
    g.add_argument('--delete',
                   '-d',
                   action='store_true',
                   help='Remove the set mark.')
    opts = parser.parse_args(argv)

    cur = current_branch()

    if opts.delete:
        try:
            remove_merge_base(cur)
        except CalledProcessError:
            print "No merge base currently exists for %s." % cur
        return 0

    if opts.merge_base:
        try:
            opts.merge_base = hash_one(opts.merge_base)
        except CalledProcessError:
            print >> sys.stderr, ('fatal: could not resolve %s as a commit' %
                                  (opts.merge_base))
            return 1

        manual_merge_base(cur, opts.merge_base, upstream(cur))

    ret = 0
    actual = get_or_create_merge_base(cur)
    if opts.merge_base and opts.merge_base != actual:
        ret = 1
        print "Invalid merge_base %s" % opts.merge_base

    print "merge_base(%s): %s" % (cur, actual)

    return ret
def main(argv):
  parser = argparse.ArgumentParser(
    description=__doc__.strip().splitlines()[0],
    epilog=' '.join(__doc__.strip().splitlines()[1:]))
  g = parser.add_mutually_exclusive_group()
  g.add_argument(
    'merge_base', nargs='?',
    help='The new hash to use as the merge base for the current branch'
  )
  g.add_argument('--delete', '-d', action='store_true',
                 help='Remove the set mark.')
  opts = parser.parse_args(argv)

  cur = current_branch()

  if opts.delete:
    try:
      remove_merge_base(cur)
    except CalledProcessError:
      print "No merge base currently exists for %s." % cur
    return 0

  if opts.merge_base:
    try:
      opts.merge_base = hash_one(opts.merge_base)
    except CalledProcessError:
      print >> sys.stderr, (
          'fatal: could not resolve %s as a commit' % (opts.merge_base)
      )
      return 1

    manual_merge_base(cur, opts.merge_base)

  ret = 0
  actual = get_or_create_merge_base(cur)
  if opts.merge_base and opts.merge_base != actual:
    ret = 1
    print "Invalid merge_base %s" % opts.merge_base

  print "merge_base(%s): %s" % (cur, actual)

  return ret
def main(args):
  default_args = git.get_config_list('depot-tools.upstream-diff.default-args')
  args = default_args + args

  current_branch = git.current_branch()

  parser = argparse.ArgumentParser()
  parser.add_argument('--wordwise', action='store_true', default=False,
                      help=(
                        'Print a colorized wordwise diff '
                        'instead of line-wise diff'))
  parser.add_argument('--branch', default=current_branch,
                      help='Show changes from a different branch. Passing '
                           '"HEAD" is the same as omitting this option (it '
                           'diffs against the current branch)')
  opts, extra_args = parser.parse_known_args(args)

  if opts.branch == 'HEAD':
    opts.branch = current_branch

  if not opts.branch or opts.branch == 'HEAD':
    print 'fatal: Cannot perform git-upstream-diff while not on a branch'
    return 1

  par = git.upstream(opts.branch)
  if not par:
    print 'fatal: No upstream configured for branch \'%s\'' % opts.branch
    return 1

  cmd = [git.GIT_EXE, '-c', 'core.quotePath=false',
         'diff', '--patience', '-C', '-C']
  if opts.wordwise:
    cmd += ['--word-diff=color', r'--word-diff-regex=(\w+|[^[:space:]])']
  cmd += [git.get_or_create_merge_base(opts.branch, par)]
  # Only specify the end commit if it is not the current branch, this lets the
  # diff include uncommitted changes when diffing the current branch.
  if opts.branch != current_branch:
    cmd += [opts.branch]

  cmd += extra_args

  return subprocess2.check_call(cmd)
def main(args):
  parser = argparse.ArgumentParser()
  parser.add_argument('--pick',
                      help=(
                          'The number to pick if this command would '
                          'prompt'))
  opts = parser.parse_args(args)

  upfn = upstream
  cur = current_branch()
  if cur == 'HEAD':
    def _upfn(b):
      parent = upstream(b)
      if parent:
        return hash_one(parent)
    upfn = _upfn
    cur = hash_one(cur)
  downstreams = [b for b in branches() if upfn(b) == cur]
  if not downstreams:
    print "No downstream branches"
    return 1
  elif len(downstreams) == 1:
    run('checkout', downstreams[0], stdout=sys.stdout, stderr=sys.stderr)
  else:
    high = len(downstreams) - 1
    while True:
      print "Please select a downstream branch"
      for i, b in enumerate(downstreams):
        print "  %d. %s" % (i, b)
      prompt = "Selection (0-%d)[0]: " % high
      r = opts.pick
      if r:
        print prompt + r
      else:
        r = raw_input(prompt).strip() or '0'
      if not r.isdigit() or (0 > int(r) > high):
        print "Invalid choice."
      else:
        run('checkout', downstreams[int(r)], stdout=sys.stdout,
            stderr=sys.stderr)
        break
  return 0
Example #29
0
def main(argv):
  # No command line flags. Just use the parser to prevent people from trying
  # to pass flags that don't do anything, and to provide 'usage'.
  parser = argparse.ArgumentParser(
      description='Automatically set up git-svn for a repo mirrored from svn.')
  parser.parse_args(argv)

  upstreams = []
  # Always configure the upstream trunk.
  upstreams.append(root())
  # Optionally configure whatever upstream branch might be currently checked
  # out. This is needed for work on svn-based branches, otherwise git-svn gets
  # very confused and tries to relate branch commits back to trunk, making a big
  # mess of the codereview patches, and generating all kinds of spurious errors
  # about the repo being in some sort of bad state.
  curr_upstream = get_upstream(current_branch())
  # There will be no upstream if the checkout is in detached HEAD.
  if curr_upstream:
    upstreams.append(curr_upstream)
  for upstream in upstreams:
    config_svn(upstream)
  return 0
Example #30
0
def main(argv):
    # No command line flags. Just use the parser to prevent people from trying
    # to pass flags that don't do anything, and to provide 'usage'.
    parser = argparse.ArgumentParser(
        description='Automatically set up git-svn for a repo mirrored from svn.'
    )
    parser.parse_args(argv)

    upstreams = []
    # Always configure the upstream trunk.
    upstreams.append(root())
    # Optionally configure whatever upstream branch might be currently checked
    # out. This is needed for work on svn-based branches, otherwise git-svn gets
    # very confused and tries to relate branch commits back to trunk, making a big
    # mess of the codereview patches, and generating all kinds of spurious errors
    # about the repo being in some sort of bad state.
    curr_upstream = get_upstream(current_branch())
    # There will be no upstream if the checkout is in detached HEAD.
    if curr_upstream:
        upstreams.append(curr_upstream)
    for upstream in upstreams:
        config_svn(upstream)
    return 0
Example #31
0
def create_new_branch(branch_name,
                      upstream_current=False,
                      upstream=None,
                      inject_current=False):
    upstream = upstream or git_common.root()
    try:
        if inject_current:
            below = git_common.current_branch()
            if below is None:
                raise Exception('no current branch')
            above = git_common.upstream(below)
            if above is None:
                raise Exception('branch %s has no upstream' % (below))
            git_common.run('checkout', '--track', above, '-b', branch_name)
            git_common.run('branch', '--set-upstream-to', branch_name, below)
        elif upstream_current:
            git_common.run('checkout', '--track', '-b', branch_name)
        else:
            if upstream in git_common.tags():
                # TODO(iannucci): ensure that basis_ref is an ancestor of HEAD?
                git_common.run('checkout', '--no-track', '-b', branch_name,
                               git_common.hash_one(upstream))
                git_common.set_config('branch.%s.remote' % branch_name, '.')
                git_common.set_config('branch.%s.merge' % branch_name,
                                      upstream)
            else:
                # TODO(iannucci): Detect unclean workdir then stash+pop if we need to
                # teleport to a conflicting portion of history?
                git_common.run('checkout', '--track', upstream, '-b',
                               branch_name)
        git_common.get_or_create_merge_base(branch_name)
    except subprocess2.CalledProcessError as cpe:
        sys.stdout.write(cpe.stdout.decode('utf-8', 'replace'))
        sys.stderr.write(cpe.stderr.decode('utf-8', 'replace'))
        return 1
    sys.stderr.write('Switched to branch %s.\n' % branch_name)
    return 0
def main(argv):
  colorama.init()
  assert len(argv) == 1, "No arguments expected"
  branch_map = {}
  par_map = collections.defaultdict(list)
  for branch in branches():
    par = upstream(branch) or NO_UPSTREAM
    branch_map[branch] = par
    par_map[par].append(branch)

  current = current_branch()
  hashes = hash_multi(current, *branch_map.keys())
  current_hash = hashes[0]
  par_hashes = {k: hashes[i+1] for i, k in enumerate(branch_map.iterkeys())}
  par_hashes[NO_UPSTREAM] = 0
  tag_set = tags()
  while par_map:
    for parent in par_map:
      if parent not in branch_map:
        if parent not in par_hashes:
          par_hashes[parent] = hash_one(parent)
        print_branch(current, current_hash, parent, par_hashes, par_map,
                     branch_map, tag_set)
        break
Example #33
0
def main(argv):
    if '-h' in argv:
        print_help()
        return 0

    map_extra = get_config_list('depot_tools.map_extra')
    fmt = '%C(red bold)%h%x09%Creset%C(green)%d%Creset %C(yellow)%cd%Creset ~ %s'
    log_proc = subprocess2.Popen([
        GIT_EXE, 'log', '--graph', '--branches', '--tags',
        root(), '--color=always', '--date=short', ('--pretty=format:' + fmt)
    ] + map_extra + argv,
                                 stdout=subprocess2.PIPE,
                                 shell=False)

    current = current_branch()
    all_branches = set(branches())
    merge_base_map = {b: get_or_create_merge_base(b) for b in all_branches}
    merge_base_map = {b: v for b, v in merge_base_map.items() if v}
    if current in all_branches:
        all_branches.remove(current)
    all_tags = set(tags())
    try:
        for line in log_proc.stdout.xreadlines():
            if merge_base_map:
                commit = line[line.find(BRIGHT_RED) +
                              len(BRIGHT_RED):line.find('\t')]
                base_for_branches = set()
                for branch, sha in merge_base_map.items():
                    if sha.startswith(commit):
                        base_for_branches.add(branch)
                if base_for_branches:
                    newline = '\r\n' if line.endswith('\r\n') else '\n'
                    line = line.rstrip(newline)
                    line += ''.join(
                        (BRIGHT, WHITE,
                         '    <(%s)' % (', '.join(base_for_branches)), RESET,
                         newline))
                    for b in base_for_branches:
                        del merge_base_map[b]

            start = line.find(GREEN + ' (')
            end = line.find(')', start)
            if start != -1 and end != -1:
                start += len(GREEN) + 2
                branch_list = line[start:end].split(', ')
                branches_str = ''
                if branch_list:
                    colored_branches = []
                    head_marker = ''
                    for b in branch_list:
                        if b == "HEAD":
                            head_marker = BLUEBAK + BRIGHT + '*'
                            continue
                        if b == current:
                            colored_branches.append(CYAN + BRIGHT + b + RESET)
                            current = None
                        elif b in all_branches:
                            colored_branches.append(GREEN + BRIGHT + b + RESET)
                            all_branches.remove(b)
                        elif b in all_tags:
                            colored_branches.append(MAGENTA + BRIGHT + b +
                                                    RESET)
                        elif b.startswith('tag: '):
                            colored_branches.append(MAGENTA + BRIGHT + b[5:] +
                                                    RESET)
                        else:
                            colored_branches.append(RED + b)
                        branches_str = '(%s) ' % (
                            (GREEN + ", ").join(colored_branches) + GREEN)
                    line = "%s%s%s" % (line[:start - 1], branches_str,
                                       line[end + 5:])
                    if head_marker:
                        line = line.replace('*', head_marker, 1)
            sys.stdout.write(line)
    except (IOError, KeyboardInterrupt):
        pass
    finally:
        sys.stderr.close()
        sys.stdout.close()
    return 0
def main(args=None):
  parser = argparse.ArgumentParser()
  parser.add_argument('--verbose', '-v', action='store_true')
  parser.add_argument('--keep-going', '-k', action='store_true',
                      help='Keep processing past failed rebases.')
  parser.add_argument('--no_fetch', '--no-fetch', '-n',
                      action='store_true',
                      help='Skip fetching remotes.')
  opts = parser.parse_args(args)

  if opts.verbose:  # pragma: no cover
    logging.getLogger().setLevel(logging.DEBUG)

  # TODO(iannucci): snapshot all branches somehow, so we can implement
  #                 `git rebase-update --undo`.
  #   * Perhaps just copy packed-refs + refs/ + logs/ to the side?
  #     * commit them to a secret ref?
  #       * Then we could view a summary of each run as a
  #         `diff --stat` on that secret ref.

  if git.in_rebase():
    # TODO(iannucci): Be able to resume rebase with flags like --continue,
    # etc.
    print (
      'Rebase in progress. Please complete the rebase before running '
      '`git rebase-update`.'
    )
    return 1

  return_branch, return_workdir = find_return_branch_workdir()
  os.chdir(git.run('rev-parse', '--show-toplevel'))

  if git.current_branch() == 'HEAD':
    if git.run('status', '--porcelain'):
      print 'Cannot rebase-update with detached head + uncommitted changes.'
      return 1
  else:
    git.freeze()  # just in case there are any local changes.

  skipped, branch_tree = git.get_branch_tree()
  for branch in skipped:
    print 'Skipping %s: No upstream specified' % branch

  if not opts.no_fetch:
    fetch_remotes(branch_tree)

  merge_base = {}
  for branch, parent in branch_tree.iteritems():
    merge_base[branch] = git.get_or_create_merge_base(branch, parent)

  logging.debug('branch_tree: %s' % pformat(branch_tree))
  logging.debug('merge_base: %s' % pformat(merge_base))

  retcode = 0
  unrebased_branches = []
  # Rebase each branch starting with the root-most branches and working
  # towards the leaves.
  for branch, parent in git.topo_iter(branch_tree):
    if git.is_dormant(branch):
      print 'Skipping dormant branch', branch
    else:
      ret = rebase_branch(branch, parent, merge_base[branch])
      if not ret:
        retcode = 1

        if opts.keep_going:
          print '--keep-going set, continuing with next branch.'
          unrebased_branches.append(branch)
          if git.in_rebase():
            git.run_with_retcode('rebase', '--abort')
          if git.in_rebase():  # pragma: no cover
            print 'Failed to abort rebase. Something is really wrong.'
            break
        else:
          break

  if unrebased_branches:
    print
    print 'The following branches could not be cleanly rebased:'
    for branch in unrebased_branches:
      print '  %s' % branch

  if not retcode:
    remove_empty_branches(branch_tree)

    # return_branch may not be there any more.
    if return_branch in git.branches():
      git.run('checkout', return_branch)
      git.thaw()
    else:
      root_branch = git.root()
      if return_branch != 'HEAD':
        print (
          "%r was merged with its parent, checking out %r instead."
          % (return_branch, root_branch)
        )
      git.run('checkout', root_branch)
    if return_workdir:
      os.chdir(return_workdir)
    git.set_config(STARTING_BRANCH_KEY, '')
    git.set_config(STARTING_WORKDIR_KEY, '')

  return retcode
Example #35
0
def UploadCl(refactor_branch, refactor_branch_upstream, directory, files,
             description, comment, reviewers, changelist, cmd_upload,
             cq_dry_run, enable_auto_submit, repository_root):
    """Uploads a CL with all changes to |files| in |refactor_branch|.

  Args:
    refactor_branch: Name of the branch that contains the changes to upload.
    refactor_branch_upstream: Name of the upstream of |refactor_branch|.
    directory: Path to the directory that contains the OWNERS file for which
        to upload a CL.
    files: List of AffectedFile instances to include in the uploaded CL.
    description: Description of the uploaded CL.
    comment: Comment to post on the uploaded CL.
    reviewers: A set of reviewers for the CL.
    changelist: The Changelist class.
    cmd_upload: The function associated with the git cl upload command.
    cq_dry_run: If CL uploads should also do a cq dry run.
    enable_auto_submit: If CL uploads should also enable auto submit.
  """
    # Create a branch.
    if not CreateBranchForDirectory(refactor_branch, directory,
                                    refactor_branch_upstream):
        print('Skipping ' + directory + ' for which a branch already exists.')
        return

    # Checkout all changes to files in |files|.
    deleted_files = []
    modified_files = []
    for action, f in files:
        abspath = os.path.abspath(os.path.join(repository_root, f))
        if action == 'D':
            deleted_files.append(abspath)
        else:
            modified_files.append(abspath)

    if deleted_files:
        git.run(*['rm'] + deleted_files)
    if modified_files:
        git.run(*['checkout', refactor_branch, '--'] + modified_files)

    # Commit changes. The temporary file is created with delete=False so that it
    # can be deleted manually after git has read it rather than automatically
    # when it is closed.
    with gclient_utils.temporary_file() as tmp_file:
        gclient_utils.FileWrite(
            tmp_file, FormatDescriptionOrComment(description, directory))
        git.run('commit', '-F', tmp_file)

    # Upload a CL.
    upload_args = ['-f']
    if reviewers:
        upload_args.extend(['-r', ','.join(reviewers)])
    if cq_dry_run:
        upload_args.append('--cq-dry-run')
    if not comment:
        upload_args.append('--send-mail')
    if enable_auto_submit:
        upload_args.append('--enable-auto-submit')
    print('Uploading CL for ' + directory + '...')

    ret = cmd_upload(upload_args)
    if ret != 0:
        print('Uploading failed for ' + directory + '.')
        print('Note: git cl split has built-in resume capabilities.')
        print('Delete ' + git.current_branch() +
              ' then run git cl split again to resume uploading.')

    if comment:
        changelist().AddComment(FormatDescriptionOrComment(comment, directory),
                                publish=True)
Example #36
0
def main(argv):
  if '-h' in argv:
    print_help()
    return 0

  map_extra = get_config_list('depot_tools.map_extra')
  fmt = '%C(red bold)%h%x09%Creset%C(green)%d%Creset %C(yellow)%cd%Creset ~ %s'
  log_proc = subprocess2.Popen(
    [GIT_EXE, 'log', '--graph', '--branches', '--tags', root(),
     '--color=always', '--date=short', ('--pretty=format:' + fmt)
    ] + map_extra + argv,
    stdout=subprocess2.PIPE,
    shell=False)

  current = current_branch()
  all_branches = set(branches())
  merge_base_map = {b: get_or_create_merge_base(b) for b in all_branches}
  merge_base_map = {b: v for b, v in merge_base_map.iteritems() if v}
  if current in all_branches:
    all_branches.remove(current)
  all_tags = set(tags())
  try:
    for line in log_proc.stdout.xreadlines():
      if merge_base_map:
        commit = line[line.find(BRIGHT_RED)+len(BRIGHT_RED):line.find('\t')]
        base_for_branches = set()
        for branch, sha in merge_base_map.iteritems():
          if sha.startswith(commit):
            base_for_branches.add(branch)
        if base_for_branches:
          newline = '\r\n' if line.endswith('\r\n') else '\n'
          line = line.rstrip(newline)
          line += ''.join(
              (BRIGHT, WHITE, '    <(%s)' % (', '.join(base_for_branches)),
               RESET, newline))
          for b in base_for_branches:
            del merge_base_map[b]

      start = line.find(GREEN+' (')
      end   = line.find(')', start)
      if start != -1 and end != -1:
        start += len(GREEN) + 2
        branch_list = line[start:end].split(', ')
        branches_str = ''
        if branch_list:
          colored_branches = []
          head_marker = ''
          for b in branch_list:
            if b == "HEAD":
              head_marker = BLUEBAK+BRIGHT+'*'
              continue
            if b == current:
              colored_branches.append(CYAN+BRIGHT+b+RESET)
              current = None
            elif b in all_branches:
              colored_branches.append(GREEN+BRIGHT+b+RESET)
              all_branches.remove(b)
            elif b in all_tags:
              colored_branches.append(MAGENTA+BRIGHT+b+RESET)
            elif b.startswith('tag: '):
              colored_branches.append(MAGENTA+BRIGHT+b[5:]+RESET)
            else:
              colored_branches.append(RED+b)
            branches_str = '(%s) ' % ((GREEN+", ").join(colored_branches)+GREEN)
          line = "%s%s%s" % (line[:start-1], branches_str, line[end+5:])
          if head_marker:
            line = line.replace('*', head_marker, 1)
      sys.stdout.write(line)
  except (IOError, KeyboardInterrupt):
    pass
  finally:
    sys.stderr.close()
    sys.stdout.close()
  return 0
    def __init__(self,
                 files,
                 local_root,
                 author,
                 reviewers,
                 fopen,
                 os_path,
                 email_postfix='@chromium.org',
                 disable_color=False,
                 override_files=None,
                 ignore_author=False):
        self.email_postfix = email_postfix

        if os.name == 'nt' or disable_color:
            self.COLOR_LINK = ''
            self.COLOR_BOLD = ''
            self.COLOR_GREY = ''
            self.COLOR_RESET = ''

        self.os_path = os_path

        self.author = author

        filtered_files = files

        reviewers = list(reviewers)
        if author and not ignore_author:
            reviewers.append(author)

        # Eliminate files that existing reviewers can review.
        self.client = owners_client.DepotToolsClient(
            root=local_root,
            branch=git_common.current_branch(),
            fopen=fopen,
            os_path=os_path)

        approval_status = self.client.GetFilesApprovalStatus(
            filtered_files, reviewers, [])
        filtered_files = [
            f for f in filtered_files
            if approval_status[f] != owners_client.OwnersClient.APPROVED
        ]

        # If some files are eliminated.
        if len(filtered_files) != len(files):
            files = filtered_files

        self.files_to_owners = self.client.BatchListOwners(files)

        self.owners_to_files = {}
        self._map_owners_to_files()

        self.original_files_to_owners = copy.deepcopy(self.files_to_owners)

        # This is the queue that will be shown in the interactive questions.
        # It is initially sorted by the score in descending order. In the
        # interactive questions a user can choose to "defer" its decision, then the
        # owner will be put to the end of the queue and shown later.
        self.owners_queue = []

        self.unreviewed_files = set()
        self.reviewed_by = {}
        self.selected_owners = set()
        self.deselected_owners = set()
        self.reset()
Example #38
0
def SplitCl(description_file, comment_file, changelist, cmd_upload, dry_run,
            cq_dry_run):
  """"Splits a branch into smaller branches and uploads CLs.

  Args:
    description_file: File containing the description of uploaded CLs.
    comment_file: File containing the comment of uploaded CLs.
    changelist: The Changelist class.
    cmd_upload: The function associated with the git cl upload command.
    dry_run: Whether this is a dry run (no branches or CLs created).
    cq_dry_run: If CL uploads should also do a cq dry run.

  Returns:
    0 in case of success. 1 in case of error.
  """
  description = AddUploadedByGitClSplitToDescription(ReadFile(description_file))
  comment = ReadFile(comment_file) if comment_file else None

  try:
    EnsureInGitRepository()

    cl = changelist()
    change = cl.GetChange(cl.GetCommonAncestorWithUpstream(), None)
    files = change.AffectedFiles()

    if not files:
      print 'Cannot split an empty CL.'
      return 1

    author = git.run('config', 'user.email').strip() or None
    refactor_branch = git.current_branch()
    assert refactor_branch, "Can't run from detached branch."
    refactor_branch_upstream = git.upstream(refactor_branch)
    assert refactor_branch_upstream, \
        "Branch %s must have an upstream." % refactor_branch

    owners_database = owners.Database(change.RepositoryRoot(), file, os.path)
    owners_database.load_data_needed_for([f.LocalPath() for f in files])

    files_split_by_owners = GetFilesSplitByOwners(owners_database, files)

    num_cls = len(files_split_by_owners)
    print('Will split current branch (' + refactor_branch + ') into ' +
          str(num_cls) + ' CLs.\n')
    if cq_dry_run and num_cls > CL_SPLIT_FORCE_LIMIT:
      print (
        'This will generate "%r" CLs. This many CLs can potentially generate'
        ' too much load on the build infrastructure. Please email'
        ' [email protected] to ensure that this won\'t  break anything.'
        ' The infra team reserves the right to cancel your jobs if they are'
        ' overloading the CQ.') % num_cls
      answer = raw_input('Proceed? (y/n):')
      if answer.lower() != 'y':
        return 0

    for cl_index, (directory, files) in \
        enumerate(files_split_by_owners.iteritems(), 1):
      # Use '/' as a path separator in the branch name and the CL description
      # and comment.
      directory = directory.replace(os.path.sep, '/')
      file_paths = [f.LocalPath() for f in files]
      reviewers = owners_database.reviewers_for(file_paths, author)

      if dry_run:
        PrintClInfo(cl_index, num_cls, directory, file_paths, description,
                    reviewers)
      else:
        UploadCl(refactor_branch, refactor_branch_upstream, directory, files,
                 description, comment, reviewers, changelist, cmd_upload,
                 cq_dry_run)

    # Go back to the original branch.
    git.run('checkout', refactor_branch)

  except subprocess2.CalledProcessError as cpe:
    sys.stderr.write(cpe.stderr)
    return 1
  return 0
Example #39
0
def main(args=None):
  parser = argparse.ArgumentParser()
  parser.add_argument('--verbose', '-v', action='store_true')
  parser.add_argument('--keep-going', '-k', action='store_true',
                      help='Keep processing past failed rebases.')
  parser.add_argument('--no_fetch', '--no-fetch', '-n',
                      action='store_true',
                      help='Skip fetching remotes.')
  parser.add_argument(
      '--current', action='store_true', help='Only rebase the current branch.')
  parser.add_argument('branches', nargs='*',
                      help='Branches to be rebased. All branches are assumed '
                           'if none specified.')
  opts = parser.parse_args(args)

  if opts.verbose:  # pragma: no cover
    logging.getLogger().setLevel(logging.DEBUG)

  # TODO(iannucci): snapshot all branches somehow, so we can implement
  #                 `git rebase-update --undo`.
  #   * Perhaps just copy packed-refs + refs/ + logs/ to the side?
  #     * commit them to a secret ref?
  #       * Then we could view a summary of each run as a
  #         `diff --stat` on that secret ref.

  if git.in_rebase():
    # TODO(iannucci): Be able to resume rebase with flags like --continue,
    # etc.
    print('Rebase in progress. Please complete the rebase before running '
          '`git rebase-update`.')
    return 1

  return_branch, return_workdir = find_return_branch_workdir()
  os.chdir(git.run('rev-parse', '--show-toplevel'))

  if git.current_branch() == 'HEAD':
    if git.run('status', '--porcelain'):
      print('Cannot rebase-update with detached head + uncommitted changes.')
      return 1
  else:
    git.freeze()  # just in case there are any local changes.

  branches_to_rebase = set(opts.branches)
  if opts.current:
    branches_to_rebase.add(git.current_branch())

  skipped, branch_tree = git.get_branch_tree()
  if branches_to_rebase:
    skipped = set(skipped).intersection(branches_to_rebase)
  for branch in skipped:
    print('Skipping %s: No upstream specified' % branch)

  if not opts.no_fetch:
    fetch_remotes(branch_tree)

  merge_base = {}
  for branch, parent in branch_tree.items():
    merge_base[branch] = git.get_or_create_merge_base(branch, parent)

  logging.debug('branch_tree: %s' % pformat(branch_tree))
  logging.debug('merge_base: %s' % pformat(merge_base))

  retcode = 0
  unrebased_branches = []
  # Rebase each branch starting with the root-most branches and working
  # towards the leaves.
  for branch, parent in git.topo_iter(branch_tree):
    # Only rebase specified branches, unless none specified.
    if branches_to_rebase and branch not in branches_to_rebase:
      continue
    if git.is_dormant(branch):
      print('Skipping dormant branch', branch)
    else:
      ret = rebase_branch(branch, parent, merge_base[branch])
      if not ret:
        retcode = 1

        if opts.keep_going:
          print('--keep-going set, continuing with next branch.')
          unrebased_branches.append(branch)
          if git.in_rebase():
            git.run_with_retcode('rebase', '--abort')
          if git.in_rebase():  # pragma: no cover
            print('Failed to abort rebase. Something is really wrong.')
            break
        else:
          break

  if unrebased_branches:
    print()
    print('The following branches could not be cleanly rebased:')
    for branch in unrebased_branches:
      print('  %s' % branch)

  if not retcode:
    remove_empty_branches(branch_tree)

    # return_branch may not be there any more.
    if return_branch in git.branches():
      git.run('checkout', return_branch)
      git.thaw()
    else:
      root_branch = git.root()
      if return_branch != 'HEAD':
        print("%s was merged with its parent, checking out %s instead." %
              (git.unicode_repr(return_branch), git.unicode_repr(root_branch)))
      git.run('checkout', root_branch)

    # return_workdir may also not be there any more.
    if return_workdir:
      try:
        os.chdir(return_workdir)
      except OSError as e:
        print(
            "Unable to return to original workdir %r: %s" % (return_workdir, e))
    git.set_config(STARTING_BRANCH_KEY, '')
    git.set_config(STARTING_WORKDIR_KEY, '')

  return retcode
Example #40
0
def SplitCl(description_file, comment_file, changelist, cmd_upload, dry_run,
            cq_dry_run, enable_auto_submit, repository_root):
    """"Splits a branch into smaller branches and uploads CLs.

  Args:
    description_file: File containing the description of uploaded CLs.
    comment_file: File containing the comment of uploaded CLs.
    changelist: The Changelist class.
    cmd_upload: The function associated with the git cl upload command.
    dry_run: Whether this is a dry run (no branches or CLs created).
    cq_dry_run: If CL uploads should also do a cq dry run.
    enable_auto_submit: If CL uploads should also enable auto submit.

  Returns:
    0 in case of success. 1 in case of error.
  """
    description = AddUploadedByGitClSplitToDescription(
        ReadFile(description_file))
    comment = ReadFile(comment_file) if comment_file else None

    try:
        EnsureInGitRepository()

        cl = changelist()
        upstream = cl.GetCommonAncestorWithUpstream()
        files = [
            (action.strip(), f)
            for action, f in scm.GIT.CaptureStatus(repository_root, upstream)
        ]

        if not files:
            print('Cannot split an empty CL.')
            return 1

        author = git.run('config', 'user.email').strip() or None
        refactor_branch = git.current_branch()
        assert refactor_branch, "Can't run from detached branch."
        refactor_branch_upstream = git.upstream(refactor_branch)
        assert refactor_branch_upstream, \
            "Branch %s must have an upstream." % refactor_branch

        owners_database = owners.Database(repository_root, open, os.path)
        owners_database.load_data_needed_for([f for _, f in files])

        files_split_by_owners = GetFilesSplitByOwners(owners_database, files)

        num_cls = len(files_split_by_owners)
        print('Will split current branch (' + refactor_branch + ') into ' +
              str(num_cls) + ' CLs.\n')
        if cq_dry_run and num_cls > CL_SPLIT_FORCE_LIMIT:
            print(
                'This will generate "%r" CLs. This many CLs can potentially generate'
                ' too much load on the build infrastructure. Please email'
                ' [email protected] to ensure that this won\'t  break anything.'
                ' The infra team reserves the right to cancel your jobs if they are'
                ' overloading the CQ.' % num_cls)
            answer = raw_input('Proceed? (y/n):')
            if answer.lower() != 'y':
                return 0

        for cl_index, (directory, files) in \
            enumerate(files_split_by_owners.items(), 1):
            # Use '/' as a path separator in the branch name and the CL description
            # and comment.
            directory = directory.replace(os.path.sep, '/')
            file_paths = [f for _, f in files]
            reviewers = owners_database.reviewers_for(file_paths, author)

            if dry_run:
                PrintClInfo(cl_index, num_cls, directory, file_paths,
                            description, reviewers)
            else:
                UploadCl(refactor_branch, refactor_branch_upstream, directory,
                         files, description, comment, reviewers, changelist,
                         cmd_upload, cq_dry_run, enable_auto_submit,
                         repository_root)

        # Go back to the original branch.
        git.run('checkout', refactor_branch)

    except subprocess2.CalledProcessError as cpe:
        sys.stderr.write(cpe.stderr)
        return 1
    return 0
def main(args):
  root_ref = root()

  parser = argparse.ArgumentParser()
  g = parser.add_mutually_exclusive_group()
  g.add_argument('new_parent', nargs='?',
                 help='New parent branch (or tag) to reparent to.')
  g.add_argument('--root', action='store_true',
                 help='Reparent to the configured root branch (%s).' % root_ref)
  g.add_argument('--lkgr', action='store_true',
                 help='Reparent to the lkgr tag.')
  opts = parser.parse_args(args)

  # TODO(iannucci): Allow specification of the branch-to-reparent

  branch = current_branch()

  if opts.root:
    new_parent = root_ref
  elif opts.lkgr:
    new_parent = 'lkgr'
  else:
    if not opts.new_parent:
      parser.error('Must specify new parent somehow')
    new_parent = opts.new_parent
  cur_parent = upstream(branch)

  if branch == 'HEAD' or not branch:
    parser.error('Must be on the branch you want to reparent')
  if new_parent == cur_parent:
    parser.error('Cannot reparent a branch to its existing parent')

  if not cur_parent:
    msg = (
      "Unable to determine %s@{upstream}.\n\nThis can happen if you didn't use "
      "`git new-branch` to create the branch and haven't used "
      "`git branch --set-upstream-to` to assign it one.\n\nPlease assign an "
      "upstream branch and then run this command again."
    )
    print(msg % branch, file=sys.stderr)
    return 1

  mbase = get_or_create_merge_base(branch, cur_parent)

  all_tags = tags()
  if cur_parent in all_tags:
    cur_parent += ' [tag]'

  try:
    run('show-ref', new_parent)
  except subprocess2.CalledProcessError:
    print('fatal: invalid reference: %s' % new_parent, file=sys.stderr)
    return 1

  if new_parent in all_tags:
    print("Reparenting %s to track %s [tag] (was %s)" % (branch, new_parent,
                                                         cur_parent))
    set_branch_config(branch, 'remote', '.')
    set_branch_config(branch, 'merge', new_parent)
  else:
    print("Reparenting %s to track %s (was %s)" % (branch, new_parent,
                                                   cur_parent))
    run('branch', '--set-upstream-to', new_parent, branch)

  manual_merge_base(branch, mbase, new_parent)

  # ONLY rebase-update the branch which moved (and dependants)
  _, branch_tree = get_branch_tree()
  branches = [branch]
  for branch, parent in topo_iter(branch_tree):
    if parent in branches:
      branches.append(branch)
  return git_rebase_update.main(['--no-fetch'] + branches)
def main(args=None):
    parser = argparse.ArgumentParser()
    parser.add_argument('--verbose', '-v', action='store_true')
    parser.add_argument('--no_fetch',
                        '--no-fetch',
                        '-n',
                        action='store_true',
                        help='Skip fetching remotes.')
    opts = parser.parse_args(args)

    if opts.verbose:  # pragma: no cover
        logging.getLogger().setLevel(logging.DEBUG)

    # TODO(iannucci): snapshot all branches somehow, so we can implement
    #                 `git rebase-update --undo`.
    #   * Perhaps just copy packed-refs + refs/ + logs/ to the side?
    #     * commit them to a secret ref?
    #       * Then we could view a summary of each run as a
    #         `diff --stat` on that secret ref.

    if git.in_rebase():
        # TODO(iannucci): Be able to resume rebase with flags like --continue,
        # etc.
        print(
            'Rebase in progress. Please complete the rebase before running '
            '`git rebase-update`.')
        return 1

    return_branch, return_workdir = find_return_branch_workdir()
    os.chdir(git.run('rev-parse', '--show-toplevel'))

    if git.current_branch() == 'HEAD':
        if git.run('status', '--porcelain'):
            print 'Cannot rebase-update with detached head + uncommitted changes.'
            return 1
    else:
        git.freeze()  # just in case there are any local changes.

    skipped, branch_tree = git.get_branch_tree()
    for branch in skipped:
        print 'Skipping %s: No upstream specified' % branch

    if not opts.no_fetch:
        fetch_remotes(branch_tree)

    merge_base = {}
    for branch, parent in branch_tree.iteritems():
        merge_base[branch] = git.get_or_create_merge_base(branch, parent)

    logging.debug('branch_tree: %s' % pformat(branch_tree))
    logging.debug('merge_base: %s' % pformat(merge_base))

    retcode = 0
    # Rebase each branch starting with the root-most branches and working
    # towards the leaves.
    for branch, parent in git.topo_iter(branch_tree):
        if git.is_dormant(branch):
            print 'Skipping dormant branch', branch
        else:
            ret = rebase_branch(branch, parent, merge_base[branch])
            if not ret:
                retcode = 1
                break

    if not retcode:
        remove_empty_branches(branch_tree)

        # return_branch may not be there any more.
        if return_branch in git.branches():
            git.run('checkout', return_branch)
            git.thaw()
        else:
            root_branch = git.root()
            if return_branch != 'HEAD':
                print(
                    "%r was merged with its parent, checking out %r instead." %
                    (return_branch, root_branch))
            git.run('checkout', root_branch)
        if return_workdir:
            os.chdir(return_workdir)
        git.set_config(STARTING_BRANCH_KEY, '')
        git.set_config(STARTING_WORKDIR_KEY, '')

    return retcode