Author: stefan2
Date: Fri Jul 24 13:37:33 2015
New Revision: 1692514

URL: http://svn.apache.org/r1692514
Log:
On the svn-mergeinfo-normalizer branch:
Add the copy graph to our local log info.  Expose it through a set of
internal history API functions.

This is a mere preparation for the detection of "natural branch history".

* tools/client-side/svn-mergeinfo-normalizer/mergeinfo-normalizer.h
  (svn_min__get_history,
   svn_min__intersect_history,
   svn_min__history_range): Declare new internal API functions.

* tools/client-side/svn-mergeinfo-normalizer/log.c
  (copy_t): New struct describing a node in the copy graph.
  (svn_min__log_t): Add copy graph as a collection of nodes.
  (copy_order): New function defining the order of these nodes.
  (log_entry_receiver,
   svn_min__log): Construct the copy graph as we go.
  (segment_t): New struct defining a history segment.
  (next_copy,
   svn_min__get_history,
   compare_history_revision,
   svn_min__intersect_history,
   svn_min__history_ranges): Implement the new internal API.

Modified:
    
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/log.c
    
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/mergeinfo-normalizer.h

Modified: 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/log.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/log.c?rev=1692514&r1=1692513&r2=1692514&view=diff
==============================================================================
--- 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/log.c
 (original)
+++ 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/log.c
 Fri Jul 24 13:37:33 2015
@@ -31,6 +31,7 @@
 #include "svn_pools.h"
 #include "svn_hash.h"
 
+#include "private/svn_fspath.h"
 #include "private/svn_subr_private.h"
 #include "private/svn_sorts_private.h"
 
@@ -49,6 +50,15 @@ typedef struct log_entry_t
   apr_array_header_t *deletions;
 } log_entry_t;
 
+typedef struct copy_t
+{
+  const char *path;
+  svn_revnum_t revision;
+
+  const char *copyfrom_path;
+  svn_revnum_t copyfrom_revision;
+} copy_t;
+
 struct svn_min__log_t
 {
   apr_hash_t *unique_paths;
@@ -58,9 +68,28 @@ struct svn_min__log_t
   svn_revnum_t head_rev;
   apr_array_header_t *entries;
 
+  apr_array_header_t *copies;
+
   svn_boolean_t quiet;
 };
 
+static int
+copy_order(const void *lhs,
+           const void *rhs)
+{
+  const copy_t *lhs_copy = *(const copy_t * const *)lhs;
+  const copy_t *rhs_copy = *(const copy_t * const *)rhs;
+
+  int diff = strcmp(lhs_copy->path, rhs_copy->path);
+  if (diff)
+    return diff;
+
+  if (lhs_copy->revision < rhs_copy->revision)
+    return -1;
+
+  return lhs_copy->revision == rhs_copy->revision ? 0 : 1;
+}
+
 static const char *
 internalize(apr_hash_t *unique_paths,
             const char *path,
@@ -118,6 +147,20 @@ log_entry_receiver(void *baton,
 
           APR_ARRAY_PUSH(entry->deletions, const char *) = path;
         }
+
+      if (SVN_IS_VALID_REVNUM(change->copyfrom_rev))
+        {
+          copy_t *copy = apr_pcalloc(log->pool, sizeof(*copy));
+          copy->path = path;
+          copy->revision = log_entry->revision;
+          copy->copyfrom_path = internalize(log->unique_paths,
+                                            change->copyfrom_path,
+                                            strlen(change->copyfrom_path),
+                                            log->pool);
+          copy->copyfrom_revision = change->copyfrom_rev;
+
+          APR_ARRAY_PUSH(log->copies, copy_t *) = copy;
+        }
     }
 
   count = entry->paths->nelts;
@@ -185,6 +228,7 @@ svn_min__log(svn_min__log_t **log,
   result->first_rev = SVN_INVALID_REVNUM;
   result->head_rev = SVN_INVALID_REVNUM;
   result->entries = apr_array_make(result_pool, 1024, sizeof(log_entry_t *));
+  result->copies = apr_array_make(result_pool, 1024, sizeof(copy_t *));
   result->quiet = baton->opt_state->quiet;
 
   if (!baton->opt_state->quiet)
@@ -209,6 +253,7 @@ svn_min__log(svn_min__log_t **log,
                           scratch_pool));
 
   svn_sort__array_reverse(result->entries, scratch_pool);
+  svn_sort__array(result->copies, copy_order);
 
   if (!baton->opt_state->quiet)
     {
@@ -420,6 +465,196 @@ svn_min__find_deletion(svn_min__log_t *l
   return SVN_INVALID_REVNUM;
 }
 
+typedef struct segment_t
+{
+  const char *path;
+  svn_revnum_t start;
+  svn_revnum_t end;
+} segment_t;
+
+static const copy_t *
+next_copy(svn_min__log_t *log,
+          const char *path,
+          svn_revnum_t revision,
+          apr_pool_t *scratch_pool)
+{
+  const copy_t *copy = NULL;
+  int idx;
+
+  copy_t *to_find = apr_pcalloc(scratch_pool, sizeof(*to_find));
+  to_find->path = path;
+  to_find->revision = revision;
+ 
+  idx = svn_sort__bsearch_lower_bound(log->copies, &to_find, copy_order);
+  if (idx < log->copies->nelts)
+    {
+      /* Found an exact match? */
+      copy = APR_ARRAY_IDX(log->copies, idx, const copy_t *);
+      if (copy->revision != revision || strcmp(copy->path, path))
+        copy = NULL;
+    }
+
+  if (!copy && idx > 0)
+    {
+      /* No exact match. The predecessor may be the closest copy. */
+      copy = APR_ARRAY_IDX(log->copies, idx - 1, const copy_t *);
+      if (strcmp(copy->path, path))
+        copy = NULL;
+    }
+
+  /* Mabye, the parent folder got copied later, i.e. is the closest copy.
+     We implicitly recurse up the tree. */
+  if (!svn_fspath__is_root(to_find->path, strlen(to_find->path)))
+    {
+      const copy_t *parent_copy;
+      to_find->path = svn_fspath__dirname(to_find->path, scratch_pool);
+
+      parent_copy = next_copy(log, to_find->path, revision, scratch_pool);
+      if (!copy)
+        copy = parent_copy;
+      else if (parent_copy && parent_copy->revision > copy->revision)
+        copy = parent_copy;
+    }
+
+  return copy;
+}
+
+apr_array_header_t *
+svn_min__get_history(svn_min__log_t *log,
+                     const char *path,
+                     svn_revnum_t start_rev,
+                     svn_revnum_t end_rev,
+                     apr_pool_t *result_pool,
+                     apr_pool_t *scratch_pool)
+{
+  segment_t *segment;
+  const copy_t *copy;
+  apr_array_header_t *result = apr_array_make(result_pool, 16,
+                                              sizeof(segment_t *));
+
+  if (!SVN_IS_VALID_REVNUM(start_rev))
+    start_rev = log->head_rev;
+
+  for (copy = next_copy(log, path, start_rev, scratch_pool);
+       copy && start_rev >= end_rev;
+       copy = next_copy(log, path, start_rev, scratch_pool))
+    {
+      segment = apr_pcalloc(result_pool, sizeof(*segment));
+      segment->start = MAX(end_rev, copy->revision);
+      segment->end = start_rev;
+      segment->path = apr_pstrdup(result_pool, path);
+
+      APR_ARRAY_PUSH(result, segment_t *) = segment;
+
+      start_rev = copy->copyfrom_revision;
+      path = svn_fspath__join(copy->copyfrom_path,
+                              svn_fspath__skip_ancestor(copy->path, path),
+                              scratch_pool);
+    }
+
+  if (start_rev >= end_rev)
+    {
+      segment = apr_pcalloc(result_pool, sizeof(*segment));
+      segment->start = end_rev;
+      segment->end = start_rev;
+      segment->path = apr_pstrdup(result_pool, path);
+
+      APR_ARRAY_PUSH(result, segment_t *) = segment;
+    }
+
+  return result;
+}
+
+static int
+compare_history_revision(const void *lhs,
+                         const void *rhs)
+{
+  const segment_t *segment = *(const segment_t * const *)lhs;
+  svn_revnum_t revision = *(const svn_revnum_t *)rhs;
+
+  if (segment->start < revision)
+    return -1;
+
+  return segment->start == revision ? 0 : 1;
+}
+
+apr_array_header_t *
+svn_min__intersect_history(apr_array_header_t *lhs,
+                           apr_array_header_t *rhs,
+                           apr_pool_t *result_pool)
+{
+  apr_array_header_t *result = apr_array_make(result_pool, 16,
+                                              sizeof(segment_t *));
+
+  int lhs_idx = 0;
+  int rhs_idx = 0;
+
+  /* Careful: the segments are ordered latest to oldest. */
+  while (lhs_idx < lhs->nelts && rhs_idx < rhs->nelts)
+    {
+      segment_t *lhs_segment = APR_ARRAY_IDX(lhs, lhs_idx, segment_t *);
+      segment_t *rhs_segment = APR_ARRAY_IDX(rhs, rhs_idx, segment_t *);
+
+      /* Skip non-overlapping revision segments */
+      if (lhs_segment->start > rhs_segment->end)
+        {
+          ++lhs_idx;
+          continue;
+        }
+      else if (lhs_segment->end < rhs_segment->start)
+        {
+          ++rhs_idx;
+          continue;
+        }
+
+      /* Revision ranges overlap. Also the same path? */
+      if (!strcmp(lhs_segment->path, rhs_segment->path))
+        {
+          segment_t *segment = apr_pcalloc(result_pool, sizeof(*segment));
+          segment->start = MAX(lhs_segment->start, rhs_segment->start);
+          segment->end = MIN(lhs_segment->end, rhs_segment->end);
+          segment->path = apr_pstrdup(result_pool, lhs_segment->path);
+
+          APR_ARRAY_PUSH(result, segment_t *) = segment;
+        }
+
+      /* The segment that starts earlier may overlap with another one.
+         If they should start at the same rev, the next iteration will
+         skip the respective other segment. */
+      if (lhs_segment->start > rhs_segment->start)
+        ++lhs_idx;
+      else
+        ++rhs_idx;
+    }
+
+   return result;
+}
+
+svn_rangelist_t *
+svn_min__history_ranges(apr_array_header_t *history,
+                        apr_pool_t *result_pool)
+{
+  svn_rangelist_t *result = apr_array_make(result_pool, history->nelts,
+                                           sizeof(svn_merge_range_t *));
+
+  int i;
+  for (i = 0; i < history->nelts; ++i)
+    {
+      const segment_t *segment = APR_ARRAY_IDX(history, i, segment_t *);
+
+      /* Convert to merge ranges.  Note that start+1 is the first rev
+         actually in that range. */
+      svn_merge_range_t *range = apr_pcalloc(result_pool, sizeof(*range));
+      range->start = MAX(0, segment->start - 1);
+      range->end = segment->end;
+      range->inheritable = TRUE;
+
+      APR_ARRAY_PUSH(result, svn_merge_range_t *) = range;
+    }
+
+  return result;
+}
+
 svn_error_t *
 svn_min__print_log_stats(svn_min__log_t *log,
                          apr_pool_t *scratch_pool)

Modified: 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/mergeinfo-normalizer.h
URL: 
http://svn.apache.org/viewvc/subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/mergeinfo-normalizer.h?rev=1692514&r1=1692513&r2=1692514&view=diff
==============================================================================
--- 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/mergeinfo-normalizer.h
 (original)
+++ 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/mergeinfo-normalizer.h
 Fri Jul 24 13:37:33 2015
@@ -190,6 +190,23 @@ svn_revnum_t
 svn_min__find_deletion(svn_min__log_t *log,
                        const char *path);
 
+apr_array_header_t *
+svn_min__get_history(svn_min__log_t *log,
+                     const char *path,
+                     svn_revnum_t start_rev,
+                     svn_revnum_t end_rev,
+                     apr_pool_t *result_pool,
+                     apr_pool_t *scratch_pool);
+
+apr_array_header_t *
+svn_min__intersect_history(apr_array_header_t *lhs,
+                           apr_array_header_t *rhs,
+                           apr_pool_t *result_pool);
+
+svn_rangelist_t *
+svn_min__history_ranges(apr_array_header_t *history,
+                        apr_pool_t *result_pool);
+
 svn_error_t *
 svn_min__print_log_stats(svn_min__log_t *log,
                          apr_pool_t *scratch_pool);


Reply via email to