def send_upstream(self, downstream, upstream):
        """
        Sends the change indicated by the comment upstream.

        @param downstream - gerrit.Remote downstream object
        @param upstream - gerrit.Remote upstream object

        """

        # Check if upstream project before doing anything.
        if not self.is_upstream_project():
            return

        ssh = downstream.SSH()

        # Check to see if comment indicates a change is upstream ready
        if not self.is_upstream_indicated():
            logger.debug("Change %s: Upstream not indicated" % self.change_id)
            return

        # Grab all of the approvals
        approvals = self.get_approvals(downstream.SSH())

        # Check to see if comment has necessary approvals.
        if self.is_forced():
            if not self.is_release_approved(approvals):
                msg = "Could not send to upstream: Release not approved."
                logger.debug("Change %s: %s" % (self.change_id, msg))
                ssh.exec_once('gerrit review -m %s %s'
                              % (pipes.quote(msg), self.revision))
                return
            msg = ("***WARNING***\n%s (%s) has requested a forced upstream "
                   "push. Bypassing all votes except for Release...\n\n"
                   "Current votes are:\n\n%s"
                   % (self._data['author']['name'],
                      self._data['author']['email'],
                      self.stringify_approvals(approvals)))
            logger.debug("Change %s: %s" % (self.change_id, msg))
            ssh.exec_once('gerrit review -m %s %s'
                          % (pipes.quote(msg), self.revision))
        else:
            if not self.is_upstream_approved(approvals):
                msg = ("Could not send to upstream: One or more labels"
                       " not approved.")
                logger.debug("Change %s: %s" % (self.change_id, msg))
                ssh.exec_once('gerrit review -m %s %s'
                              % (pipes.quote(msg), self.revision))
                return

        # Do some git stuffs to push upstream
        logger.debug("Change %s: Sending to upstream" % self.change_id)

        repo_dir = '~/tmp'
        repo_dir = os.path.expanduser(repo_dir)
        repo_dir = os.path.abspath(repo_dir)

        # Add uuid, want a unique directory here
        uuid_dir = str(uuid4())
        repo_dir = os.path.join(repo_dir, uuid_dir)

        # Make Empty directory - We want this to stop and fail on OSError
        if not os.path.isdir(repo_dir):
            os.makedirs(repo_dir)
            logger.debug(
                "Change %s: Created directory %s" % (self.change_id, repo_dir)
            )

        # Save the current working directory
        old_cwd = os.getcwd()

        try:
            # Change to newly created directory.
            os.chdir(repo_dir)

            # Init the cwd
            git.init()

            # Add the remotes for upstream and downstream
            remote_url = "ssh://%s@%s:%s/%s"
            git.add_remote('downstream', remote_url % (downstream.username,
                                                       downstream.host,
                                                       downstream.port,
                                                       self.project))

            # Figure out what user we will pose as
            # This every upstream user sharing the same key is kinda shady.
            # Default back to the configured user if username doesnt exist.
            # should fail in this case
            username = self.patchset_uploader_username
            name = self.patchSet_uploader_name
            email = self.patchset_uploader_email
            if not username:
                logger.debug("Change %s: Unable to use author credentials."
                             " Defaulting to configured credentials."
                             % self.change_id)
                username = upstream.username
                name = self._conf['git-config']['name']
                email = self._conf['git-config']['email']

            git.add_remote('upstream', remote_url % (username,
                                                     upstream.host,
                                                     upstream.port,
                                                     self.project))
            logger.debug('Change %s: Sending upstream as '
                         'username %s, email %s, name %s'
                         % (self.change_id, username, email, name))
            try:
                env = get_review_env()

                # Set committer info
                git.set_config('user.email', email)
                git.set_config('user.name', name)

                # Download  specific change to local
                args = ['git-review', '-r', 'downstream', '-d',
                        '%s,%s' % (self.change_id, self.patchset_id)]
                logger.debug('Change %s: running: %s'
                             % (self.change_id, ' '.join(args)))
                out = subprocess.check_output(args,
                                              stderr=subprocess.STDOUT,
                                              env=env)
                logger.debug("Change %s: %s" % (self.change_id, out))

                # Send downloaded change to upstream
                args = ['git-review', '-R', '-y', '-r', 'upstream',
                        self.branch, '-t', self.topic]
                logger.debug('Change %s: running: %s'
                             % (self.change_id, ' '.join(args)))
                out = subprocess.check_output(args,
                                              stderr=subprocess.STDOUT,
                                              env=env)
                logger.debug("Change %s: %s" % (self.change_id, out))

                upstream_url = self.get_upstream_url(upstream)

                msg = 'Sent to upstream: %s' % (upstream_url)
                # Send comment to downstream gerrit with link to change in
                # upstream gerrit
                ssh.exec_once('gerrit review -m %s %s'
                              % (pipes.quote(msg), self.revision))

            except subprocess.CalledProcessError as e:
                msg = "Could not send to upstream:\n%s" % e.output
                ssh.exec_once('gerrit review -m %s %s'
                              % (pipes.quote(msg), self.revision))
                logger.error("Change %s: Unable to send to upstream"
                             % self.change_id)
                logger.error("Change %s: %s" % (self.change_id, out))

            except Exception:
                msg = 'Could not send to upstream: Error running git-review'
                ssh.exec_once('gerrit review -m %s %s'
                              % (pipes.quote(msg), self.revision))
                logger.exception("Change %s: Unable to send to upstream"
                                 % self.change_id)

        finally:
            # Change to old current working directory
            os.chdir(old_cwd)

            # Attempt to clean up created directory
            shutil.rmtree(repo_dir)
    def _sync(self, remote):
        """
        Pushes all normal branches from a source repo to gerrit.

        @param remote - gerrit.Remote object

        """
        # Only sync if source repo is provided.
        if not self.source:
            return

        # Only sync if heads and/or tags are specified
        if not self.heads and not self.tags:
            return

        msg = "Project %s: syncing with repo %s." % (self.name, self.source)
        logger.info(msg)
        print msg

        repo_dir = '~/tmp'
        repo_dir = os.path.expanduser(repo_dir)
        repo_dir = os.path.abspath(repo_dir)

        # Make Empty directory - We want this to stop and fail on OSError
        if not os.path.isdir(repo_dir):
            os.makedirs(repo_dir)
            logger.debug(
                "Project %s: Created directory %s" % (self.name, repo_dir)
            )

        # Save the current working directory
        old_cwd = os.getcwd()

        try:
            # Change cwd to that repo
            os.chdir(repo_dir)

            uuid_dir = str(uuid4())
            repo_dir = os.path.join(repo_dir, uuid_dir)

            # Do a git clone --bare <source_repo>
            git.clone(self.source, name=uuid_dir, bare=True)

            # Change to bare cloned directory
            os.chdir(uuid_dir)

            # Add remote named gerrit
            ssh_url = 'ssh://%s@%s:%s/%s' % (
                remote.username,
                remote.host,
                remote.port,
                self.name
            )
            git.add_remote('gerrit', ssh_url)

            # Push heads
            if self.heads:
                kwargs = {'all_': True}
                if self.force:
                    kwargs['force'] = True
                git.push('gerrit', **kwargs)

            # Push tags
            if self.tags:
                kwargs = {'tags': True}
                if self.force:
                    kwargs['force'] = True
                git.push('gerrit', **kwargs)

            ref_kwargs = self.ref_kwargs()

            # Grab origin refs
            origin_refset = git.remote_refs('origin', **ref_kwargs)

            # Grab gerrit refs
            gerrit_refset = git.remote_refs('gerrit', **ref_kwargs)

            # Find refs that should be removed.
            prune_refset = gerrit_refset - origin_refset
            if self.preserve_prefix == PRESERVE_ALL_BRANCHES:
                msg = "Project %s: Preserving all refs" % self.name
                logger.debug(msg)
                print msg
                prune_refset = set([])
            elif not self.preserve_prefix is None:
                msg = "Project %s: Preserving refs with prefixes of %s" \
                      % (self.name, self.preserve_prefix)
                logger.debug(msg)
                print msg
                heads_prefix = "refs/heads/%s" % self.preserve_prefix
                tags_prefix = "refs/tags/%s" % self.preserve_prefix
                keep = lambda ref: not ref.startswith(heads_prefix) and \
                    not ref.startswith(tags_prefix)
                prune_refset = filter(keep, prune_refset)

            # Prefix each ref in refset with ':' to delete
            colonize = lambda ref: ':%s' % ref
            prune_refset = map(colonize, prune_refset)

            # Remove branches no longer needed
            if prune_refset:
                git.push('gerrit', refspecs=prune_refset)

        finally:
            # Change to old current working directory
            os.chdir(old_cwd)

            # Attempt to clean up created directory
            shutil.rmtree(repo_dir)
    def _config(self, remote, conf, groups):
        """
        Builds the groups file and project.config file for a project.

        @param remote - gerrit.Remote object
        @param conf - Dict containing git config information
        @param groups - List of groups

        """
        if not self.config:
            return

        msg = "Project %s: Configuring." % self.name
        logger.info(msg)
        print msg

        repo_dir = '~/tmp'
        repo_dir = os.path.expanduser(repo_dir)
        repo_dir = os.path.abspath(repo_dir)

        uuid_dir = str(uuid4())
        repo_dir = os.path.join(repo_dir, uuid_dir)

        # Make Empty directory - We want this to stop and fail on OSError
        logger.debug(
            "Project %s: Creating directory %s" % (self.name, repo_dir)
        )
        os.makedirs(repo_dir)

        # Save the current working directory
        old_cwd = os.getcwd()

        origin = 'origin'

        try:
            # Change cwd to that repo
            os.chdir(repo_dir)

            # Git init empty directory
            git.init()

            # Add remote origin
            ssh_url = 'ssh://%s@%s:%s/%s' % (
                remote.username,
                remote.host,
                remote.port,
                self.name
            )

            git.add_remote(origin, ssh_url)

            # Fetch refs/meta/config for project
            refspec = 'refs/meta/config:refs/remotes/origin/meta/config'
            git.fetch(origin, refspec)

            # Checkout refs/meta/config
            git.checkout_branch('meta/config')

            # Get md5 of existing config
            _file = os.path.join(repo_dir, 'project.config')
            contents = ''
            try:
                with open(_file, 'r') as f:
                    contents = f.read()
            except IOError:
                pass
            existing_md5 = hashlib.md5(contents).hexdigest()

            # Get md5 of new config
            with open(self.config, 'r') as f:
                contents = f.read()
            new_md5 = hashlib.md5(contents).hexdigest()

            msg = "Project %s: Md5 comparision\n%s\n%s"
            msg = msg % (self.name, existing_md5, new_md5)
            logger.debug(msg)
            print msg

            # Only alter if checksums do not match
            if existing_md5 != new_md5:

                logger.debug(
                    "Project %s: config md5's are different." % self.name
                )

                # Update project.config file
                _file = os.path.join(repo_dir, 'project.config')
                with open(_file, 'w') as f:
                    f.write(contents)

                # Update groups file
                group_contents = groups_file_contents(groups)
                _file = os.path.join(repo_dir, 'groups')
                with open(_file, 'w') as f:
                    f.write(group_contents)

                # Git config user.email
                git.set_config('user.email', conf['git-config']['email'])

                # Git config user.name
                git.set_config('user.name', conf['git-config']['name'])

                # Add groups and project.config
                git.add(['groups', 'project.config'])

                # Git commit
                git.commit(message='Setting up %s' % self.name)

                # Git push
                git.push(origin, refspecs='meta/config:refs/meta/config')
                logger.info("Project %s: pushed configuration." % self.name)

            else:
                msg = "Project %s: config unchanged." % self.name
                logger.info(msg)
                print msg

        finally:
            # Change to old current working directory
            os.chdir(old_cwd)

            # Attempt to clean up created directory
            shutil.rmtree(repo_dir)
    def send_upstream(self, downstream, upstream):
        """
        Sends the change indicated by the comment upstream.

        @param downstream - gerrit.Remote downstream object
        @param upstream - gerrit.Remote upstream object

        """
        # Check if upstream project before doing anything.
        if not self.is_upstream_project():
            return

        ssh = downstream.SSH()

        # Check to see if comment indicates a change is upstream ready
        if not self.is_upstream_indicated():
            logger.debug("Change %s: Upstream not indicated" % self.change_id)
            return

        # Grab all of the approvals
        approvals = self.get_approvals(downstream.SSH())

        # Check to see if comment has necessary approvals.
        if not self.is_upstream_approved(approvals):
            msg = ("Could not send to upstream: One or more labels"
                   " not approved.")
            logger.debug("Change %s: %s" % (self.change_id, msg))
            ssh.exec_once('gerrit review -m %s %s' %
                          (pipes.quote(msg), self.revision))
            return

        # Do some git stuffs to push upstream
        logger.debug("Change %s: Sending to upstream" % self.change_id)

        repo_dir = '~/tmp'
        repo_dir = os.path.expanduser(repo_dir)
        repo_dir = os.path.abspath(repo_dir)

        # Add uuid, want a unique directory here
        uuid_dir = str(uuid4())
        repo_dir = os.path.join(repo_dir, uuid_dir)

        # Make Empty directory - We want this to stop and fail on OSError
        if not os.path.isdir(repo_dir):
            os.makedirs(repo_dir)
            logger.debug("Change %s: Created directory %s" %
                         (self.change_id, repo_dir))

        # Save the current working directory
        old_cwd = os.getcwd()

        try:
            # Change to newly created directory.
            os.chdir(repo_dir)

            # Init the cwd
            git.init()

            # Add the remotes for upstream and downstream
            remote_url = "ssh://%s@%s:%s/%s"
            git.add_remote(
                'downstream',
                remote_url % (downstream.username, downstream.host,
                              downstream.port, self.project))

            # Figure out what user we will pose as
            # This every upstream user sharing the same key is kinda shady.
            # Default back to the configured user if username doesnt exist.
            # should fail in this case
            username = self.change_owner_username
            name = self.change_owner_name
            email = self.change_owner_email
            if not username:
                logger.debug("Change %s: Unable to use author credentials."
                             " Defaulting to configured credentials." %
                             self.change_id)
                username = upstream.username
                name = self._conf['git-config']['name']
                email = self._conf['git-config']['email']

            git.add_remote(
                'upstream', remote_url %
                (username, upstream.host, upstream.port, self.project))
            logger.debug('Change %s: Sending upstream as '
                         'username %s, email %s, name %s' %
                         (self.change_id, username, email, name))
            try:
                env = get_review_env()

                # Set committer info
                git.set_config('user.email', email)
                git.set_config('user.name', name)

                # Download  specific change to local
                args = [
                    'git-review', '-r', 'downstream', '-d',
                    '%s,%s' % (self.change_id, self.patchset_id)
                ]
                logger.debug('Change %s: running: %s' %
                             (self.change_id, ' '.join(args)))
                out = subprocess.check_output(args,
                                              stderr=subprocess.STDOUT,
                                              env=env)
                logger.debug("Change %s: %s" % (self.change_id, out))

                # Send downloaded change to upstream
                args = [
                    'git-review', '-y', '-r', 'upstream', self.branch, '-t',
                    self.topic
                ]
                logger.debug('Change %s: running: %s' %
                             (self.change_id, ' '.join(args)))
                out = subprocess.check_output(args,
                                              stderr=subprocess.STDOUT,
                                              env=env)
                logger.debug("Change %s: %s" % (self.change_id, out))

                upstream_url = self.get_upstream_url(upstream)

                msg = 'Sent to upstream: %s' % (upstream_url)
                # Send comment to downstream gerrit with link to change in
                # upstream gerrit
                ssh.exec_once('gerrit review -m %s %s' %
                              (pipes.quote(msg), self.revision))

            except subprocess.CalledProcessError as e:
                msg = "Could not send to upstream:\n%s" % e.output
                ssh.exec_once('gerrit review -m %s %s' %
                              (pipes.quote(msg), self.revision))
                logger.error("Change %s: Unable to send to upstream" %
                             self.change_id)
                logger.error("Change %s: %s" % (self.change_id, out))

            except Exception:
                msg = 'Could not send to upstream: Error running git-review'
                ssh.exec_once('gerrit review -m %s %s' %
                              (pipes.quote(msg), self.revision))
                logger.exception("Change %s: Unable to send to upstream" %
                                 self.change_id)

        finally:
            # Change to old current working directory
            os.chdir(old_cwd)

            # Attempt to clean up created directory
            shutil.rmtree(repo_dir)
    def _sync(self, remote):
        """
        Pushes all normal branches from a source repo to gerrit.

        @param remote - gerrit.Remote object

        """
        # Only sync if source repo is provided.
        if not self.source:
            return

        # Only sync if heads and/or tags are specified
        if not self.heads and not self.tags:
            return

        msg = "Project %s: syncing with repo %s." % (self.name, self.source)
        logger.info(msg)
        print msg

        repo_dir = '~/tmp'
        repo_dir = os.path.expanduser(repo_dir)
        repo_dir = os.path.abspath(repo_dir)

        # Make Empty directory - We want this to stop and fail on OSError
        if not os.path.isdir(repo_dir):
            os.makedirs(repo_dir)
            logger.debug("Project %s: Created directory %s" %
                         (self.name, repo_dir))

        # Save the current working directory
        old_cwd = os.getcwd()

        try:
            # Change cwd to that repo
            os.chdir(repo_dir)

            uuid_dir = str(uuid4())
            repo_dir = os.path.join(repo_dir, uuid_dir)

            # Do a git clone --bare <source_repo>
            git.clone(self.source, name=uuid_dir, bare=True)

            # Change to bare cloned directory
            os.chdir(uuid_dir)

            # Add remote named gerrit
            ssh_url = 'ssh://%s@%s:%s/%s' % (remote.username, remote.host,
                                             remote.port, self.name)
            git.add_remote('gerrit', ssh_url)

            # Push heads
            if self.heads:
                kwargs = {'all_': True}
                if self.force:
                    kwargs['force'] = True
                git.push('gerrit', **kwargs)

            # Push tags
            if self.tags:
                kwargs = {'tags': True}
                if self.force:
                    kwargs['force'] = True
                git.push('gerrit', **kwargs)

            ref_kwargs = self.ref_kwargs()

            # Grab origin refs
            origin_refset = git.remote_refs('origin', **ref_kwargs)

            # Grab gerrit refs
            gerrit_refset = git.remote_refs('gerrit', **ref_kwargs)

            # Find refs that should be removed.
            prune_refset = gerrit_refset - origin_refset
            if self.preserve_prefix:
                msg = "Project %s: Preserving refs with prefixes of %s" \
                      % (self.name, self.preserve_prefix)
                logger.debug(msg)
                print msg
                heads_prefix = "refs/heads/%s" % self.preserve_prefix
                tags_prefix = "refs/tags/%s" % self.preserve_prefix
                keep = lambda ref: not ref.startswith(heads_prefix) and \
                    not ref.startswith(tags_prefix)
                prune_refset = filter(keep, prune_refset)

            # Prefix each ref in refset with ':' to delete
            colonize = lambda ref: ':%s' % ref
            prune_refset = map(colonize, prune_refset)

            # Remove branches no longer needed
            if prune_refset:
                git.push('gerrit', refspecs=prune_refset)

        finally:
            # Change to old current working directory
            os.chdir(old_cwd)

            # Attempt to clean up created directory
            shutil.rmtree(repo_dir)
    def _config(self, remote, conf, groups):
        """
        Builds the groups file and project.config file for a project.

        @param remote - gerrit.Remote object
        @param conf - Dict containing git config information
        @param groups - List of groups

        """
        if not self.config:
            return

        msg = "Project %s: Configuring." % self.name
        logger.info(msg)
        print msg

        repo_dir = '~/tmp'
        repo_dir = os.path.expanduser(repo_dir)
        repo_dir = os.path.abspath(repo_dir)

        uuid_dir = str(uuid4())
        repo_dir = os.path.join(repo_dir, uuid_dir)

        # Make Empty directory - We want this to stop and fail on OSError
        logger.debug("Project %s: Creating directory %s" %
                     (self.name, repo_dir))
        os.makedirs(repo_dir)

        # Save the current working directory
        old_cwd = os.getcwd()

        origin = 'origin'

        try:
            # Change cwd to that repo
            os.chdir(repo_dir)

            # Git init empty directory
            git.init()

            # Add remote origin
            ssh_url = 'ssh://%s@%s:%s/%s' % (remote.username, remote.host,
                                             remote.port, self.name)

            git.add_remote(origin, ssh_url)

            # Fetch refs/meta/config for project
            refspec = 'refs/meta/config:refs/remotes/origin/meta/config'
            git.fetch(origin, refspec)

            # Checkout refs/meta/config
            git.checkout_branch('meta/config')

            # Get md5 of existing config
            _file = os.path.join(repo_dir, 'project.config')
            contents = ''
            try:
                with open(_file, 'r') as f:
                    contents = f.read()
            except IOError:
                pass
            existing_md5 = hashlib.md5(contents).hexdigest()

            # Get md5 of new config
            with open(self.config, 'r') as f:
                contents = f.read()
            new_md5 = hashlib.md5(contents).hexdigest()

            msg = "Project %s: Md5 comparision\n%s\n%s"
            msg = msg % (self.name, existing_md5, new_md5)
            logger.debug(msg)
            print msg

            # Only alter if checksums do not match
            if existing_md5 != new_md5:

                logger.debug("Project %s: config md5's are different." %
                             self.name)

                # Update project.config file
                _file = os.path.join(repo_dir, 'project.config')
                with open(_file, 'w') as f:
                    f.write(contents)

                # Update groups file
                group_contents = groups_file_contents(groups)
                _file = os.path.join(repo_dir, 'groups')
                with open(_file, 'w') as f:
                    f.write(group_contents)

                # Git config user.email
                git.set_config('user.email', conf['git-config']['email'])

                # Git config user.name
                git.set_config('user.name', conf['git-config']['name'])

                # Add groups and project.config
                git.add(['groups', 'project.config'])

                # Git commit
                git.commit(message='Setting up %s' % self.name)

                # Git push
                git.push(origin, refspecs='meta/config:refs/meta/config')
                logger.info("Project %s: pushed configuration." % self.name)

            else:
                msg = "Project %s: config unchanged." % self.name
                logger.info(msg)
                print msg

        finally:
            # Change to old current working directory
            os.chdir(old_cwd)

            # Attempt to clean up created directory
            shutil.rmtree(repo_dir)