def _FixUpManifests(self, repo_manifest):
        """Points the checkouts at the new branch in the manifests.

    Within the branch, make sure all manifests with projects that are
    "branchable" are checked out to "refs/heads/<new_branch>".  Do this
    by updating all manifests in the known manifest projects.
    """
        assert not self._run.options.delete_branch, 'Cannot fix a deleted branch.'

        # Use local branch ref.
        branch_ref = git.NormalizeRef(self.branch_name)

        logging.debug('Fixing manifest projects for new branch.')
        for project in site_config.params.MANIFEST_PROJECTS:
            manifest_checkout = repo_manifest.FindCheckout(project)
            manifest_dir = manifest_checkout['local_path']
            push_remote = manifest_checkout['push_remote']

            # Checkout revision can be either a sha1 or a branch ref.
            src_ref = manifest_checkout['revision']
            if not git.IsSHA1(src_ref):
                src_ref = git.NormalizeRemoteRef(push_remote, src_ref)

            git.CreateBranch(manifest_dir, manifest_version.PUSH_BRANCH,
                             src_ref)

            # We want to process default.xml and official.xml + their imports.
            pending_manifests = [
                constants.DEFAULT_MANIFEST, constants.OFFICIAL_MANIFEST
            ]
            processed_manifests = []

            while pending_manifests:
                # Canonicalize the manifest name (resolve dir and symlinks).
                manifest_path = os.path.join(manifest_dir,
                                             pending_manifests.pop())
                manifest_path = os.path.realpath(manifest_path)

                # Don't process a manifest more than once.
                if manifest_path in processed_manifests:
                    continue

                processed_manifests.append(manifest_path)

                if not os.path.exists(manifest_path):
                    logging.info('Manifest not found: %s', manifest_path)
                    continue

                logging.debug('Fixing manifest at %s.', manifest_path)
                included_manifests = self._UpdateManifest(manifest_path)
                pending_manifests += included_manifests

            git.RunGit(manifest_dir, ['add', '-A'], print_cmd=True)
            message = 'Fix up manifest after branching %s.' % branch_ref
            git.RunGit(manifest_dir, ['commit', '-m', message], print_cmd=True)
            push_to = git.RemoteRef(push_remote, branch_ref)
            git.GitPush(manifest_dir,
                        manifest_version.PUSH_BRANCH,
                        push_to,
                        skip=self.skip_remote_push)
    def _VerifyPush(self, new_branch, rename_from=None, delete=False):
        """Verify that |new_branch| has been created.

    Args:
      new_branch: The new remote branch to create (or delete).
      rename_from: If set, |rename_from| is being renamed to |new_branch|.
      delete: If set, |new_branch| is being deleted.
    """
        # Pushes all operate on remote branch refs.
        new_branch = git.NormalizeRef(new_branch)

        # Calculate source and destination revisions.
        suffixes = ['', '-new-special-branch', '-old-special-branch']
        if delete:
            src_revs = [''] * len(suffixes)
        elif rename_from is not None:
            rename_from = git.NormalizeRef(rename_from)
            rename_from_tracking = git.NormalizeRemoteRef('cros', rename_from)
            src_revs = [
                '%s%s' % (rename_from_tracking, suffix) for suffix in suffixes
            ]
        else:
            src_revs = [
                CHROMITE_REVISION, SPECIAL_REVISION1, SPECIAL_REVISION2
            ]
        dest_revs = ['%s%s' % (new_branch, suffix) for suffix in suffixes]

        # Verify pushes happened correctly.
        for src_rev, dest_rev in zip(src_revs, dest_revs):
            cmd = ['push', '%s:%s' % (src_rev, dest_rev)]
            self.rc_mock.assertCommandContains(cmd)
            if rename_from is not None:
                cmd = ['push', ':%s' % (rename_from, )]
                self.rc_mock.assertCommandContains(cmd)
    def _ProcessCheckout(self, src_manifest, src_checkout):
        """Performs per-checkout push operations.

    Args:
      src_manifest: The ManifestCheckout object for the current manifest.
      src_checkout: The ProjectCheckout object to process.
    """
        if not src_checkout.IsBranchableProject():
            # We don't have the ability to push branches to this repository. Just
            # use TOT instead.
            return

        checkout_name = src_checkout['name']
        remote = src_checkout['push_remote']
        src_ref = src_checkout['revision']
        suffix = self._GetBranchSuffix(src_manifest, src_checkout)

        # The source/destination branches depend on options.
        if self.rename_to:
            # Rename flow.  Both src and dst branches exist.
            src_branch = '%s%s' % (self.branch_name, suffix)
            dst_branch = '%s%s' % (self.rename_to, suffix)
        elif self._run.options.delete_branch:
            # Delete flow.  Only dst branch exists.
            src_branch = None
            dst_branch = '%s%s' % (self.branch_name, suffix)
        else:
            # Create flow (default).  Only dst branch exists.  Source
            # for the branch will just be src_ref.
            src_branch = None
            dst_branch = '%s%s' % (self.branch_name, suffix)

        # Normalize branch refs to remote.  We only process remote branches.
        src_branch = git.NormalizeRemoteRef(remote, src_branch)
        dst_branch = git.NormalizeRemoteRef(remote, dst_branch)

        # Determine whether src/dst branches exist now, by getting their sha1s.
        if src_branch:
            src_sha1 = self._GetSHA1(src_checkout, src_branch)
        elif git.IsSHA1(src_ref):
            src_sha1 = src_ref
        dst_sha1 = self._GetSHA1(src_checkout, dst_branch)

        # Complain if the branch already exists, unless that is expected.
        force = self._run.options.force_create or self._run.options.delete_branch
        if dst_sha1 and not force:
            # We are either creating a branch or renaming a branch, and the
            # destination branch unexpectedly exists.  Accept this only if the
            # destination branch is already at the revision we want.
            if src_sha1 != dst_sha1:
                raise BranchError(
                    'Checkout %s already contains branch %s.  Run with '
                    '--force-create to overwrite.' %
                    (checkout_name, dst_branch))

            logging.info(
                'Checkout %s already contains branch %s and it already'
                ' points to revision %s', checkout_name, dst_branch, dst_sha1)

        elif self._run.options.delete_branch:
            # Delete the dst_branch, if it exists.
            if dst_sha1:
                self._DeleteBranch(src_checkout, dst_branch)
            else:
                raise BranchError(
                    'Checkout %s does not contain branch %s to delete.' %
                    (checkout_name, dst_branch))

        elif self.rename_to:
            # Copy src_branch to dst_branch, if it exists, then delete src_branch.
            if src_sha1:
                self._CopyBranch(src_checkout, src_branch, dst_branch)
                self._DeleteBranch(src_checkout, src_branch)
            else:
                raise BranchError(
                    'Checkout %s does not contain branch %s to rename.' %
                    (checkout_name, src_branch))

        else:
            # Copy src_ref to dst_branch.
            self._CopyBranch(src_checkout,
                             src_ref,
                             dst_branch,
                             force=self._run.options.force_create)