⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 blame.c

📁 linux subdivision ying gai ke yi le ba
💻 C
📖 第 1 页 / 共 2 页
字号:
  svn_stream_t *last_stream;
  svn_stream_t *cur_stream;
  const char *temp_dir;
  struct delta_baton *delta_baton;

  /* Clear the current pool. */
  svn_pool_clear (frb->currpool);

  /* If this file has a non-textual mime-type, bail out. */
  SVN_ERR (check_mimetype (prop_diffs, frb->target, frb->currpool));

  if (frb->ctx->notify_func)
    frb->ctx->notify_func (frb->ctx->notify_baton,
                           path,
                           svn_wc_notify_blame_revision,
                           svn_node_none,
                           NULL,
                           svn_wc_notify_state_inapplicable,
                           svn_wc_notify_state_inapplicable,
                           revnum);

  if (frb->ctx->cancel_func)
    SVN_ERR (frb->ctx->cancel_func (frb->ctx->cancel_baton));

  /* If there were no content changes, we couldn't care less about this
     revision now.  Note that we checked the mime type above, so things
     work if the user just changes the mime type in a commit.
     Also note that we don't switch the pools in this case.  This is important,
     since the tempfile will be removed by the pool and we need the tempfile
     from the last revision with content changes. */
  if (!content_delta_handler)
    return SVN_NO_ERROR;

  /* Create delta baton. */
  delta_baton = apr_palloc (frb->currpool, sizeof (*delta_baton));

  /* Prepare the text delta window handler. */
  if (frb->last_filename)
    SVN_ERR (svn_io_file_open (&delta_baton->source_file, frb->last_filename,
                               APR_READ, APR_OS_DEFAULT, frb->currpool));
  else
    /* Means empty stream below. */
    delta_baton->source_file = NULL;
  last_stream = svn_stream_from_aprfile (delta_baton->source_file, pool);

  SVN_ERR (svn_io_temp_dir (&temp_dir, frb->currpool));
  SVN_ERR (svn_io_open_unique_file (&delta_baton->file, &delta_baton->filename,
                                    svn_path_join (temp_dir, "tmp",
                                                   frb->currpool),
                                    ".tmp", FALSE, frb->currpool));
  apr_pool_cleanup_register (frb->currpool, delta_baton->file,
                             cleanup_tempfile, apr_pool_cleanup_null);
  cur_stream = svn_stream_from_aprfile (delta_baton->file, frb->currpool);

  /* Get window handler for applying delta. */
  svn_txdelta_apply (last_stream, cur_stream, NULL, NULL,
                     frb->currpool,
                     &delta_baton->wrapped_handler,
                     &delta_baton->wrapped_baton);

  /* Wrap the window handler with our own. */
  delta_baton->file_rev_baton = frb;
  *content_delta_handler = window_handler;
  *content_delta_baton = delta_baton;

  /* Create the rev structure. */
  frb->rev = apr_palloc (frb->mainpool, sizeof (struct rev));

  if (revnum < frb->start_rev)
    {
      /* We shouldn't get more than one revision before start. */
      assert (frb->last_filename == NULL);

      /* The file existed before start_rev; generate no blame info for
         lines from this revision (or before). */
      frb->rev->revision = SVN_INVALID_REVNUM;
      frb->rev->author = NULL;
      frb->rev->date = NULL;
    }
  else
    {
      svn_string_t *str;
      assert (revnum <= frb->end_rev);

      /* Set values from revision props. */
      frb->rev->revision = revnum;

      if ((str = apr_hash_get (rev_props, SVN_PROP_REVISION_AUTHOR,
                               sizeof (SVN_PROP_REVISION_AUTHOR) - 1)))
        frb->rev->author = apr_pstrdup (frb->mainpool, str->data);
      else
        frb->rev->author = NULL;

      if ((str = apr_hash_get (rev_props, SVN_PROP_REVISION_DATE,
                               sizeof (SVN_PROP_REVISION_DATE) - 1)))
        frb->rev->date = apr_pstrdup (frb->mainpool, str->data);
      else
        frb->rev->date = NULL;
    }

  return SVN_NO_ERROR;
}

static svn_error_t *
old_blame (const char *target, const char *url,
           svn_ra_plugin_t *ra_lib,
           void *session,
           struct file_rev_baton *frb);

svn_error_t *
svn_client_blame (const char *target,
                  const svn_opt_revision_t *start,
                  const svn_opt_revision_t *end,
                  svn_client_blame_receiver_t receiver,
                  void *receiver_baton,
                  svn_client_ctx_t *ctx,
                  apr_pool_t *pool)
{
  struct file_rev_baton frb;
  svn_ra_plugin_t *ra_lib; 
  void *session;
  const char *url;
  svn_revnum_t start_revnum, end_revnum;
  struct blame *walk;
  apr_file_t *file;
  apr_pool_t *iterpool;
  svn_stream_t *stream;
  svn_error_t *err;

  if (start->kind == svn_opt_revision_unspecified
      || end->kind == svn_opt_revision_unspecified)
    return svn_error_create
      (SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);

  /* Get an RA plugin for this filesystem object. */
  SVN_ERR (svn_client__ra_lib_from_path (&ra_lib, &session, &end_revnum,
                                         &url, target, end,
                                         ctx, pool));

  SVN_ERR (svn_client__get_revision_number (&start_revnum, ra_lib, session,
                                            start, target, pool));

  if (end_revnum < start_revnum)
    return svn_error_create
      (SVN_ERR_CLIENT_BAD_REVISION, NULL,
       _("Start revision must precede end revision"));

  frb.start_rev = start_revnum;
  frb.end_rev = end_revnum;
  frb.target = target;
  frb.ctx = ctx;
  frb.last_filename = NULL;
  frb.blame = NULL;
  frb.avail = NULL;
  frb.mainpool = pool;
  /* The callback will flip the following two pools, because it needs
     information from the previous call.  Obviously, it can't rely on
     the lifetime of the pool provided by get_file_revs. */
  frb.lastpool = svn_pool_create (pool);
  frb.currpool = svn_pool_create (pool);

  /* Collect all blame information.
     We need to ensure that we get one revision before the start_rev,
     if available so that we can know what was actually changed in the start
     revision. */
  err = ra_lib->get_file_revs (session, "",
                               start_revnum - (start_revnum > 0 ? 1 : 0),
                               end_revnum,
                               file_rev_handler, &frb, pool);

  /* Fall back if it wasn't supported by the server.  Servers earlier
     than 1.1 need this. */
  if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
    {
      svn_error_clear (err);
      err = old_blame (target, url, ra_lib, session, &frb);
    }

  SVN_ERR (err);

  /* Report the blame to the caller. */

  /* The callback has to have been called at least once. */
  assert (frb.last_filename != NULL);

  /* Create a pool for the iteration below. */
  iterpool = svn_pool_create (pool);

  /* Open the last file and get a stream. */
  SVN_ERR (svn_io_file_open (&file, frb.last_filename, APR_READ,
                             APR_OS_DEFAULT, pool));
  stream = svn_stream_from_aprfile (file, pool);

  /* Process each blame item. */
  for (walk = frb.blame; walk; walk = walk->next)
    {
      apr_off_t line_no;
      for (line_no = walk->start;
           !walk->next || line_no < walk->next->start;
           ++line_no)
        {
          svn_boolean_t eof;
          svn_stringbuf_t *sb;
          apr_pool_clear (iterpool);
          SVN_ERR (svn_stream_readline (stream, &sb, "\n", &eof, iterpool));
          if (ctx->cancel_func)
            SVN_ERR (ctx->cancel_func (ctx->cancel_baton));
          if (!eof || sb->len)
            SVN_ERR (receiver (receiver_baton, line_no, walk->rev->revision,
                               walk->rev->author, walk->rev->date,
                               sb->data, iterpool));
          if (eof) break;
        }
    }

  SVN_ERR (svn_stream_close (stream));

  /* We don't need the temp file any more. */
  SVN_ERR (svn_io_file_close (file, pool));

  svn_pool_destroy (frb.lastpool);
  svn_pool_destroy (frb.currpool);
  svn_pool_destroy (iterpool);

  return SVN_NO_ERROR;
}


/* This is used when there is no get_file_revs available. */
static svn_error_t *
old_blame (const char *target, const char *url,
           svn_ra_plugin_t *ra_lib,
           void *session,
           struct file_rev_baton *frb)
{
  const char *reposURL;
  struct log_message_baton lmb;
  apr_array_header_t *condensed_targets;
  apr_file_t *file;
  svn_stream_t *stream;
  struct rev *rev;
  svn_node_kind_t kind;
  apr_pool_t *pool = frb->mainpool;

  SVN_ERR (ra_lib->check_path (session, "", frb->end_rev, &kind, pool));

  if (kind == svn_node_dir)
    return svn_error_createf (SVN_ERR_CLIENT_IS_DIRECTORY, NULL,
                              _("URL '%s' refers to a directory"), url);

  condensed_targets = apr_array_make (pool, 1, sizeof (const char *));
  (*((const char **)apr_array_push (condensed_targets))) = "";

  SVN_ERR (ra_lib->get_repos_root (session, &reposURL, pool));

  /* URI decode the path before placing it in the baton, since changed_paths
     passed into log_message_receiver will not be URI encoded. */
  lmb.path = svn_path_uri_decode (url + strlen (reposURL), pool);

  lmb.cancel_func = frb->ctx->cancel_func;
  lmb.cancel_baton = frb->ctx->cancel_baton;
  lmb.eldest = NULL;
  lmb.pool = pool;

  /* Accumulate revision metadata by walking the revisions
     backwards; this allows us to follow moves/copies
     correctly. */
  SVN_ERR (ra_lib->get_log (session,
                            condensed_targets,
                            frb->end_rev,
                            frb->start_rev,
                            TRUE,
                            FALSE,
                            log_message_receiver,
                            &lmb,
                            pool));

  SVN_ERR (svn_client__open_ra_session (&session, ra_lib, reposURL, NULL,
                                        NULL, NULL, FALSE, FALSE,
                                        frb->ctx, pool));

  /* Inspect the first revision's change metadata; if there are any
     prior revisions, compute a new starting revision/path.  If no
     revisions were selected, no blame is assigned.  A modified
     item certainly has a prior revision.  It is reasonable for an
     added item to have none, but anything else is unexpected.  */
  if (!lmb.eldest)
    {
      lmb.eldest = apr_palloc (pool, sizeof (*rev));
      lmb.eldest->revision = frb->end_rev;
      lmb.eldest->path = lmb.path;
      lmb.eldest->next = NULL;
      rev = apr_palloc (pool, sizeof (*rev));
      rev->revision = SVN_INVALID_REVNUM;
      rev->author = NULL;
      rev->date = NULL;
      frb->blame = blame_create (frb, rev, 0);
    }
  else if (lmb.action == 'M' || SVN_IS_VALID_REVNUM (lmb.copyrev))
    {
      rev = apr_palloc (pool, sizeof (*rev));
      if (SVN_IS_VALID_REVNUM (lmb.copyrev))
        rev->revision = lmb.copyrev;
      else
        rev->revision = lmb.eldest->revision - 1;
      rev->path = lmb.path;
      rev->next = lmb.eldest;
      lmb.eldest = rev;
      rev = apr_palloc (pool, sizeof (*rev));
      rev->revision = SVN_INVALID_REVNUM;
      rev->author = NULL;
      rev->date = NULL;
      frb->blame = blame_create (frb, rev, 0);
    }
  else if (lmb.action == 'A')
    {
      frb->blame = blame_create (frb, lmb.eldest, 0);
    }
  else
    return svn_error_createf (APR_EGENERAL, NULL,
                              _("Revision action '%c' for "
                                "revision %ld of '%s' "
                                "lacks a prior revision"),
                              lmb.action, lmb.eldest->revision,
                              lmb.eldest->path);

  /* Walk the revision list in chronological order, downloading
     each fulltext, diffing it with its predecessor, and accumulating
     the blame information into db.blame.  Use two iteration pools
     rather than one, because the diff routines need to look at a
     sliding window of revisions.  Two pools gives us a ring buffer
     of sorts. */
  for (rev = lmb.eldest; rev; rev = rev->next)
    {
      const char *tmp;
      const char *temp_dir;
      apr_hash_t *props;
      svn_string_t *mimetype;
      
      apr_pool_clear (frb->currpool);
      SVN_ERR (svn_io_temp_dir (&temp_dir, frb->currpool));
      SVN_ERR (svn_io_open_unique_file (&file, &tmp,
                 svn_path_join (temp_dir, "tmp", frb->currpool), ".tmp",
                                        FALSE, frb->currpool));

      apr_pool_cleanup_register (frb->currpool, file, cleanup_tempfile,
                                 apr_pool_cleanup_null);

      stream = svn_stream_from_aprfile (file, frb->currpool);
      SVN_ERR (ra_lib->get_file (session, rev->path + 1, rev->revision,
                                 stream, NULL, &props, frb->currpool));
      SVN_ERR (svn_stream_close (stream));
      SVN_ERR (svn_io_file_close (file, frb->currpool));

      /* If this file has a non-textual mime-type, bail out. */
      if (props && 
          ((mimetype = apr_hash_get (props, SVN_PROP_MIME_TYPE, 
                                     sizeof (SVN_PROP_MIME_TYPE) - 1))))
        {
          if (svn_mime_type_is_binary (mimetype->data))
            return svn_error_createf 
              (SVN_ERR_CLIENT_IS_BINARY_FILE, 0,
               _("Cannot calculate blame information for binary file '%s'"),
               target);
        }

      if (frb->ctx->notify_func)
        frb->ctx->notify_func (frb->ctx->notify_baton,
                               rev->path,
                               svn_wc_notify_blame_revision,
                               svn_node_none,
                               NULL,
                               svn_wc_notify_state_inapplicable,
                               svn_wc_notify_state_inapplicable,
                               rev->revision);

      if (frb->ctx->cancel_func)
        SVN_ERR (frb->ctx->cancel_func (frb->ctx->cancel_baton));

      if (frb->last_filename)
        {
          frb->rev = rev;
          SVN_ERR (add_file_blame (frb->last_filename, tmp, frb));
        }

      frb->last_filename = tmp;
      {
        apr_pool_t *tmppool = frb->currpool;
        frb->currpool = frb->lastpool;
        frb->lastpool = tmppool;
      }
    }

  return SVN_NO_ERROR;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -