Example #1
0
  def GenerateDiff(filenames, cwd, full_move, revision):
    """Returns a string containing the diff for the given file list.

    The files in the list should either be absolute paths or relative to the
    given root. If no root directory is provided, the repository root will be
    used.
    The diff will always use relative paths.
    """
    assert isinstance(filenames, (list, tuple))
    # If the user specified a custom diff command in their svn config file,
    # then it'll be used when we do svn diff, which we don't want to happen
    # since we want the unified diff.
    if SVN.AssertVersion("1.7")[0]:
      # On svn >= 1.7, the "--internal-diff" flag will solve this.
      return SVN._GenerateDiffInternal(filenames, cwd, full_move, revision,
                                       ["diff", "--internal-diff"],
                                       ["diff", "--internal-diff"])
    else:
      # On svn < 1.7, the "--internal-diff" flag doesn't exist.  Using
      # --diff-cmd=diff doesn't always work, since e.g. Windows cmd users may
      # not have a "diff" executable in their path at all.  So we use an empty
      # temporary directory as the config directory, which bypasses any user
      # settings for the diff-cmd.  However, we don't pass this for the
      # remote_safe_diff_command parameter, since when a new config-dir is
      # specified for an svn diff against a remote URL, it triggers
      # authentication prompts.  In this case there isn't really a good
      # alternative to svn 1.7's --internal-diff flag.
      bogus_dir = tempfile.mkdtemp()
      try:
        return SVN._GenerateDiffInternal(filenames, cwd, full_move, revision,
                                         ["diff", "--config-dir", bogus_dir],
                                         ["diff"])
      finally:
        depends_utils.rmtree(bogus_dir)
Example #2
0
    def GenerateDiff(filenames, cwd, full_move, revision):
        """Returns a string containing the diff for the given file list.

    The files in the list should either be absolute paths or relative to the
    given root. If no root directory is provided, the repository root will be
    used.
    The diff will always use relative paths.
    """
        assert isinstance(filenames, (list, tuple))
        # If the user specified a custom diff command in their svn config file,
        # then it'll be used when we do svn diff, which we don't want to happen
        # since we want the unified diff.
        if SVN.AssertVersion("1.7")[0]:
            # On svn >= 1.7, the "--internal-diff" flag will solve this.
            return SVN._GenerateDiffInternal(filenames, cwd, full_move,
                                             revision,
                                             ["diff", "--internal-diff"],
                                             ["diff", "--internal-diff"])
        else:
            # On svn < 1.7, the "--internal-diff" flag doesn't exist.  Using
            # --diff-cmd=diff doesn't always work, since e.g. Windows cmd users may
            # not have a "diff" executable in their path at all.  So we use an empty
            # temporary directory as the config directory, which bypasses any user
            # settings for the diff-cmd.  However, we don't pass this for the
            # remote_safe_diff_command parameter, since when a new config-dir is
            # specified for an svn diff against a remote URL, it triggers
            # authentication prompts.  In this case there isn't really a good
            # alternative to svn 1.7's --internal-diff flag.
            bogus_dir = tempfile.mkdtemp()
            try:
                return SVN._GenerateDiffInternal(
                    filenames, cwd, full_move, revision,
                    ["diff", "--config-dir", bogus_dir], ["diff"])
            finally:
                depends_utils.rmtree(bogus_dir)
Example #3
0
  def Revert(cwd, callback=None, ignore_externals=False, no_ignore=False):
    """Reverts all svn modifications in cwd, including properties.

    Deletes any modified files or directory.

    A "svn update --revision BASE" call is required after to revive deleted
    files.
    """
    for file_status in SVN.CaptureStatus(None, cwd, no_ignore=no_ignore):
      file_path = os.path.join(cwd, file_status[1])
      if (ignore_externals and
          file_status[0][0] == 'X' and
          file_status[0][1:].isspace()):
        # Ignore externals.
        logging.info('Ignoring external %s' % file_status[1])
        continue

      # This is the case where '! L    .' is returned by 'svn status'. Just
      # strip off the '/.'.
      if file_path.endswith(os.path.sep + '.'):
        file_path = file_path[:-2]

      if callback:
        callback(file_status)

      if os.path.exists(file_path):
        # svn revert is really stupid. It fails on inconsistent line-endings,
        # on switched directories, etc. So take no chance and delete everything!
        # In theory, it wouldn't be necessary for property-only change but then
        # it'd have to look for switched directories, etc so it's not worth
        # optimizing this use case.
        if os.path.isfile(file_path) or os.path.islink(file_path):
          logging.info('os.remove(%s)' % file_path)
          os.remove(file_path)
        elif os.path.isdir(file_path):
          logging.info('rmtree(%s)' % file_path)
          depends_utils.rmtree(file_path)
        else:
          logging.critical(
            ('No idea what is %s.\nYou just found a bug in depends'
              ', please ping [email protected] ASAP!') % file_path)

      if (file_status[0][0] in ('D', 'A', '!') or
          not file_status[0][1:].isspace()):
        # Added, deleted file requires manual intervention and require calling
        # revert, like for properties.
        if not os.path.isdir(cwd):
          # '.' was deleted. It's not worth continuing.
          return
        try:
          SVN.Capture(['revert', file_status[1]], cwd=cwd)
        except subprocess2.CalledProcessError:
          if not os.path.exists(file_path):
            continue
          raise
Example #4
0
    def Revert(cwd, callback=None, ignore_externals=False, no_ignore=False):
        """Reverts all svn modifications in cwd, including properties.

    Deletes any modified files or directory.

    A "svn update --revision BASE" call is required after to revive deleted
    files.
    """
        for file_status in SVN.CaptureStatus(None, cwd, no_ignore=no_ignore):
            file_path = os.path.join(cwd, file_status[1])
            if (ignore_externals and file_status[0][0] == 'X'
                    and file_status[0][1:].isspace()):
                # Ignore externals.
                logging.info('Ignoring external %s' % file_status[1])
                continue

            # This is the case where '! L    .' is returned by 'svn status'. Just
            # strip off the '/.'.
            if file_path.endswith(os.path.sep + '.'):
                file_path = file_path[:-2]

            if callback:
                callback(file_status)

            if os.path.exists(file_path):
                # svn revert is really stupid. It fails on inconsistent line-endings,
                # on switched directories, etc. So take no chance and delete everything!
                # In theory, it wouldn't be necessary for property-only change but then
                # it'd have to look for switched directories, etc so it's not worth
                # optimizing this use case.
                if os.path.isfile(file_path) or os.path.islink(file_path):
                    logging.info('os.remove(%s)' % file_path)
                    os.remove(file_path)
                elif os.path.isdir(file_path):
                    logging.info('rmtree(%s)' % file_path)
                    depends_utils.rmtree(file_path)
                else:
                    logging.critical(
                        ('No idea what is %s.\nYou just found a bug in depends'
                         ', please ping [email protected] ASAP!') %
                        file_path)

            if (file_status[0][0] in ('D', 'A', '!')
                    or not file_status[0][1:].isspace()):
                # Added, deleted file requires manual intervention and require calling
                # revert, like for properties.
                if not os.path.isdir(cwd):
                    # '.' was deleted. It's not worth continuing.
                    return
                try:
                    SVN.Capture(['revert', file_status[1]], cwd=cwd)
                except subprocess2.CalledProcessError:
                    if not os.path.exists(file_path):
                        continue
                    raise
Example #5
0
  def RunAndGetFileList(verbose, args, cwd, file_list, stdout=None):
    """Runs svn checkout, update, or status, output to stdout.

    The first item in args must be either "checkout", "update", or "status".

    svn's stdout is parsed to collect a list of files checked out or updated.
    These files are appended to file_list.  svn's stdout is also printed to
    sys.stdout as in Run.

    Args:
      verbose: If True, uses verbose output
      args: A sequence of command line parameters to be passed to svn.
      cwd: The directory where svn is to be run.

    Raises:
      Error: An error occurred while running the svn command.
    """
    stdout = stdout or sys.stdout
    if file_list is None:
      # Even if our caller doesn't care about file_list, we use it internally.
      file_list = []

    # svn update and svn checkout use the same pattern: the first three columns
    # are for file status, property status, and lock status.  This is followed
    # by two spaces, and then the path to the file.
    update_pattern = '^...  (.*)$'

    # The first three columns of svn status are the same as for svn update and
    # svn checkout.  The next three columns indicate addition-with-history,
    # switch, and remote lock status.  This is followed by one space, and then
    # the path to the file.
    status_pattern = '^...... (.*)$'

    # args[0] must be a supported command.  This will blow up if it's something
    # else, which is good.  Note that the patterns are only effective when
    # these commands are used in their ordinary forms, the patterns are invalid
    # for "svn status --show-updates", for example.
    pattern = {
          'checkout': update_pattern,
          'status':   status_pattern,
          'update':   update_pattern,
        }[args[0]]
    compiled_pattern = re.compile(pattern)
    # Place an upper limit.
    backoff_time = 5
    retries = 0
    while True:
      retries += 1
      previous_list_len = len(file_list)
      failure = []

      def CaptureMatchingLines(line):
        match = compiled_pattern.search(line)
        if match:
          file_list.append(match.group(1))
        if line.startswith('svn: '):
          failure.append(line)

      try:
        depends_utils.CheckCallAndFilterAndHeader(
            ['svn'] + args,
            cwd=cwd,
            always=verbose,
            filter_fn=CaptureMatchingLines,
            stdout=stdout)
      except subprocess2.CalledProcessError:
        def IsKnownFailure():
          for x in failure:
            if (x.startswith('svn: OPTIONS of') or
                x.startswith('svn: PROPFIND of') or
                x.startswith('svn: REPORT of') or
                x.startswith('svn: Unknown hostname') or
                x.startswith('svn: Server sent unexpected return value') or
                x.startswith('svn: Can\'t connect to host')):
              return True
          return False

        # Subversion client is really misbehaving with Google Code.
        if args[0] == 'checkout':
          # Ensure at least one file was checked out, otherwise *delete* the
          # directory.
          if len(file_list) == previous_list_len:
            if not IsKnownFailure():
              # No known svn error was found, bail out.
              raise
            # No file were checked out, so make sure the directory is
            # deleted in case it's messed up and try again.
            # Warning: It's bad, it assumes args[2] is the directory
            # argument.
            if os.path.isdir(args[2]):
              depends_utils.rmtree(args[2])
          else:
            # Progress was made, convert to update since an aborted checkout
            # is now an update.
            args = ['update'] + args[1:]
        else:
          # It was an update or export.
          # We enforce that some progress has been made or a known failure.
          if len(file_list) == previous_list_len and not IsKnownFailure():
            # No known svn error was found and no progress, bail out.
            raise
        if retries == 10:
          raise
        print "Sleeping %.1f seconds and retrying...." % backoff_time
        time.sleep(backoff_time)
        backoff_time *= 1.3
        continue
      break
Example #6
0
    def RunAndGetFileList(verbose, args, cwd, file_list, stdout=None):
        """Runs svn checkout, update, or status, output to stdout.

    The first item in args must be either "checkout", "update", or "status".

    svn's stdout is parsed to collect a list of files checked out or updated.
    These files are appended to file_list.  svn's stdout is also printed to
    sys.stdout as in Run.

    Args:
      verbose: If True, uses verbose output
      args: A sequence of command line parameters to be passed to svn.
      cwd: The directory where svn is to be run.

    Raises:
      Error: An error occurred while running the svn command.
    """
        stdout = stdout or sys.stdout
        if file_list is None:
            # Even if our caller doesn't care about file_list, we use it internally.
            file_list = []

        # svn update and svn checkout use the same pattern: the first three columns
        # are for file status, property status, and lock status.  This is followed
        # by two spaces, and then the path to the file.
        update_pattern = '^...  (.*)$'

        # The first three columns of svn status are the same as for svn update and
        # svn checkout.  The next three columns indicate addition-with-history,
        # switch, and remote lock status.  This is followed by one space, and then
        # the path to the file.
        status_pattern = '^...... (.*)$'

        # args[0] must be a supported command.  This will blow up if it's something
        # else, which is good.  Note that the patterns are only effective when
        # these commands are used in their ordinary forms, the patterns are invalid
        # for "svn status --show-updates", for example.
        pattern = {
            'checkout': update_pattern,
            'status': status_pattern,
            'update': update_pattern,
        }[args[0]]
        compiled_pattern = re.compile(pattern)
        # Place an upper limit.
        backoff_time = 5
        retries = 0
        while True:
            retries += 1
            previous_list_len = len(file_list)
            failure = []

            def CaptureMatchingLines(line):
                match = compiled_pattern.search(line)
                if match:
                    file_list.append(match.group(1))
                if line.startswith('svn: '):
                    failure.append(line)

            try:
                depends_utils.CheckCallAndFilterAndHeader(
                    ['svn'] + args,
                    cwd=cwd,
                    always=verbose,
                    filter_fn=CaptureMatchingLines,
                    stdout=stdout)
            except subprocess2.CalledProcessError:

                def IsKnownFailure():
                    for x in failure:
                        if (x.startswith('svn: OPTIONS of')
                                or x.startswith('svn: PROPFIND of')
                                or x.startswith('svn: REPORT of')
                                or x.startswith('svn: Unknown hostname')
                                or x.startswith(
                                    'svn: Server sent unexpected return value')
                                or
                                x.startswith('svn: Can\'t connect to host')):
                            return True
                    return False

                # Subversion client is really misbehaving with Google Code.
                if args[0] == 'checkout':
                    # Ensure at least one file was checked out, otherwise *delete* the
                    # directory.
                    if len(file_list) == previous_list_len:
                        if not IsKnownFailure():
                            # No known svn error was found, bail out.
                            raise
                        # No file were checked out, so make sure the directory is
                        # deleted in case it's messed up and try again.
                        # Warning: It's bad, it assumes args[2] is the directory
                        # argument.
                        if os.path.isdir(args[2]):
                            depends_utils.rmtree(args[2])
                    else:
                        # Progress was made, convert to update since an aborted checkout
                        # is now an update.
                        args = ['update'] + args[1:]
                else:
                    # It was an update or export.
                    # We enforce that some progress has been made or a known failure.
                    if len(file_list
                           ) == previous_list_len and not IsKnownFailure():
                        # No known svn error was found and no progress, bail out.
                        raise
                if retries == 10:
                    raise
                print "Sleeping %.1f seconds and retrying...." % backoff_time
                time.sleep(backoff_time)
                backoff_time *= 1.3
                continue
            break