Author: stefan2
Date: Thu Nov 27 22:16:52 2014
New Revision: 1642242

URL: http://svn.apache.org/r1642242
Log:
On the svn-mergeinfo-normalizer branch: Commit initial code.

* tools/svn-mergeinfo-normalizer: New folder.
 
* tools/svn-mergeinfo-normalizer/mergeinfo-normalizer.h,
  tools/svn-mergeinfo-normalizer/svn-mergeinfo-normalizer.c: New tool main 
files.
 
* tools/svn-mergeinfo-normalizer/analyze-cmd.c,
  tools/svn-mergeinfo-normalizer/clear-obsolete-cmd.c,
  tools/svn-mergeinfo-normalizer/combine-ranges-cmd.c,
  tools/svn-mergeinfo-normalizer/help-cmd.c,
  tools/svn-mergeinfo-normalizer/normalize-cmd.c: New sub-commands.

* tools/svn-mergeinfo-normalizer/log.c,
  tools/svn-mergeinfo-normalizer/util.c,
  tools/svn-mergeinfo-normalizer/wc_mergeinfo.c: New queries, utils and data 
models.

Added:
    
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/
    
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/analyze-cmd.c
   (with props)
    
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/clear-obsolete-cmd.c
   (with props)
    
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/combine-ranges-cmd.c
   (with props)
    
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/help-cmd.c
   (with props)
    
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/log.c
   (with props)
    
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/mergeinfo-normalizer.h
   (with props)
    
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/normalize-cmd.c
   (with props)
    
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/svn-mergeinfo-normalizer.c
   (with props)
    
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/util.c
   (with props)
    
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/wc_mergeinfo.c
   (with props)

Added: 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/analyze-cmd.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/analyze-cmd.c?rev=1642242&view=auto
==============================================================================
--- 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/analyze-cmd.c
 (added)
+++ 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/analyze-cmd.c
 Thu Nov 27 22:16:52 2014
@@ -0,0 +1,410 @@
+/*
+ * analyze-cmd.c -- Print which MI can be elided, which one can not and why
+ *
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+/* ==================================================================== */
+
+
+
+/*** Includes. ***/
+
+#include "svn_cmdline.h"
+#include "svn_dirent_uri.h"
+#include "svn_hash.h"
+#include "svn_path.h"
+#include "svn_pools.h"
+#include "svn_sorts.h"
+#include "private/svn_fspath.h"
+#include "private/svn_sorts_private.h"
+
+#include "mergeinfo-normalizer.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+static svn_error_t *
+remove_obsolete_lines(svn_ra_session_t *session,
+                      svn_mergeinfo_t mergeinfo,
+                      apr_pool_t *scratch_pool)
+{
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+  apr_array_header_t *to_remove
+    = apr_array_make(scratch_pool, 16, sizeof(const char *));
+
+  int i;
+  apr_hash_index_t *hi;
+  for (hi = apr_hash_first(scratch_pool, mergeinfo);
+       hi;
+       hi = apr_hash_next(hi))
+    {
+      const char *path = apr_hash_this_key(hi);
+      svn_node_kind_t kind;
+
+      SVN_ERR_ASSERT(*path == '/');
+      SVN_ERR(svn_ra_check_path(session, path + 1, SVN_INVALID_REVNUM, &kind,
+                                scratch_pool));
+      if (kind == svn_node_none)
+        APR_ARRAY_PUSH(to_remove, const char *) = path;
+    }
+
+  svn_sort__array(to_remove, svn_sort_compare_paths);
+  if (to_remove->nelts)
+    {
+      SVN_ERR(svn_cmdline_printf(iterpool,
+                            _("    %d branches don't exist in HEAD:\n"),
+                            to_remove->nelts));
+
+      for (i = 0; i < to_remove->nelts; ++i)
+        {
+          const char *path;
+          svn_pool_clear(iterpool);
+
+          path = APR_ARRAY_IDX(to_remove, i, const char *);
+          svn_hash_sets(mergeinfo, path, NULL);
+
+          SVN_ERR(svn_cmdline_printf(iterpool, _("    %s\n"), path));
+        }
+    }
+  else
+    {
+      SVN_ERR(svn_cmdline_printf(iterpool,
+                             _("    All branches still exist in HEAD.\n")));
+    }
+
+  SVN_ERR(svn_cmdline_printf(iterpool, _("\n")));
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
+static svn_rangelist_t *
+find_reverse_ranges(svn_rangelist_t *ranges,
+                    apr_pool_t *result_pool)
+{
+  svn_rangelist_t *result = apr_array_make(result_pool, 0, ranges->elt_size);
+
+  int i;
+  for (i = 0; i < ranges->nelts; ++i)
+    {
+      const svn_merge_range_t *range
+        = APR_ARRAY_IDX(ranges, i, const svn_merge_range_t *);
+
+      if (range->start >= range->end)
+        APR_ARRAY_PUSH(result, const svn_merge_range_t *) = range;
+    }
+
+  return result;
+}
+
+static svn_error_t *
+print_ranges(svn_rangelist_t *ranges,
+             const char *title,
+             apr_pool_t *scratch_pool)
+{
+  svn_string_t *string;
+
+  SVN_ERR(svn_rangelist_to_string(&string, ranges, scratch_pool));
+  SVN_ERR(svn_cmdline_printf(scratch_pool, _("        %s%s\n"),
+                             title, string->data));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+remove_lines(svn_min__log_t *log,
+             const char *relpath,
+             svn_mergeinfo_t parent_mergeinfo,
+             svn_mergeinfo_t subtree_mergeinfo,
+             apr_pool_t *scratch_pool)
+{
+  apr_pool_t *iterpool;
+  apr_hash_index_t *hi;
+  apr_hash_t *processed;
+  svn_boolean_t needs_header = TRUE;
+
+  if (apr_hash_count(subtree_mergeinfo) == 0)
+    return SVN_NO_ERROR;
+
+  iterpool = svn_pool_create(scratch_pool);
+  processed = apr_hash_make(scratch_pool);
+
+  SVN_ERR(svn_cmdline_printf(iterpool,
+                             _("    Try to elide remaining branches:\n")));
+
+  for (hi = apr_hash_first(scratch_pool, parent_mergeinfo);
+       hi;
+       hi = apr_hash_next(hi))
+    {
+      const char *parent_path, *subtree_path;
+      svn_rangelist_t *parent_ranges, *subtree_ranges, *reverse_ranges;
+      svn_rangelist_t *subtree_only, *parent_only;
+      svn_rangelist_t *operative_outside_subtree, *operative_in_subtree;
+
+      svn_pool_clear(iterpool);
+
+      parent_path = apr_hash_this_key(hi);
+      subtree_path = svn_fspath__join(parent_path, relpath, scratch_pool);
+      parent_ranges = apr_hash_this_val(hi);
+      subtree_ranges = svn_hash_gets(subtree_mergeinfo, subtree_path);
+
+      if (!subtree_ranges)
+        continue;
+
+      svn_hash_sets(processed, subtree_path, subtree_path);
+
+      reverse_ranges = find_reverse_ranges(subtree_ranges, iterpool);
+      if (reverse_ranges->nelts)
+        {
+          SVN_ERR(svn_cmdline_printf(iterpool,
+                                  _("    Reverse range(s) found for %s:\n"),
+                                  subtree_path));
+          SVN_ERR(print_ranges(reverse_ranges, "", iterpool));
+          continue;
+        }
+
+      SVN_ERR(svn_rangelist_diff(&parent_only, &subtree_only,
+                                 parent_ranges, subtree_ranges, TRUE,
+                                 iterpool));
+      subtree_only
+        = svn_min__operative(log, subtree_path, subtree_only, iterpool);
+
+      if (!subtree_only->nelts && !parent_only->nelts)
+        {
+          SVN_ERR(svn_cmdline_printf(iterpool,
+                                  _("    elide redundant branch %s\n"),
+                                  subtree_path));
+          svn_hash_sets(subtree_mergeinfo, subtree_path, NULL);
+          continue;
+        }
+
+      operative_outside_subtree
+        = svn_min__operative_outside_subtree(log, parent_path, subtree_path,
+                                             subtree_only, iterpool);
+      operative_in_subtree
+        = svn_min__operative(log, subtree_path, parent_only, iterpool);
+
+      if (operative_outside_subtree->nelts || operative_in_subtree->nelts)
+        {
+          SVN_ERR(svn_cmdline_printf(iterpool,
+                                     _("    CANNOT elide branch %s\n"),
+                                     subtree_path));
+          if (operative_outside_subtree->nelts)
+            SVN_ERR(print_ranges(operative_outside_subtree,
+                                 _("revisions not movable to parent: "),
+                                 iterpool));
+          if (operative_in_subtree->nelts)
+            SVN_ERR(print_ranges(operative_in_subtree,
+                                 _("revisions missing in sub-node: "),
+                                 iterpool));
+        }
+      else
+        {
+          SVN_ERR(svn_cmdline_printf(iterpool,
+                                     _("    elide branch %s\n"),
+                                     subtree_path));
+          if (subtree_only->nelts)
+            SVN_ERR(print_ranges(subtree_only,
+                                 _("revisions moved to parent: "),
+                                 iterpool));
+          if (parent_only->nelts)
+            SVN_ERR(print_ranges(parent_only,
+                                 _("revisions inoperative in sub-node: "),
+                                 iterpool));
+
+          SVN_ERR(svn_rangelist_merge2(parent_ranges, subtree_only,
+                                       parent_ranges->pool, iterpool));
+          svn_hash_sets(subtree_mergeinfo, subtree_path, NULL);
+        }
+    }
+
+
+  for (hi = apr_hash_first(scratch_pool, subtree_mergeinfo);
+       hi;
+       hi = apr_hash_next(hi))
+    {
+      const char *path = apr_hash_this_key(hi);
+      svn_pool_clear(iterpool);
+
+      if (!svn_hash_gets(processed, path))
+        {
+          if (needs_header)
+            {
+              SVN_ERR(svn_cmdline_printf(scratch_pool,
+                           _("\n    Branches not mentioned in parent:\n")));
+              needs_header = FALSE;
+            }
+
+          SVN_ERR(svn_cmdline_printf(iterpool, ("    %s\n"), path));
+        }
+    }
+
+  SVN_ERR(svn_cmdline_printf(iterpool, "\n"));
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+analyze(svn_ra_session_t *session,
+        apr_array_header_t *wc_mergeinfo,
+        svn_min__log_t *log,
+        apr_pool_t *scratch_pool)
+{
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+
+  int i;
+  for (i = wc_mergeinfo->nelts - 1; i >= 0; --i)
+    {
+      const char *parent_path;
+      const char *relpath;
+      svn_mergeinfo_t parent_mergeinfo;
+      svn_mergeinfo_t subtree_mergeinfo;
+
+      if (svn_min__get_mergeinfo_pair(&parent_path, &relpath,
+                                      &parent_mergeinfo, &subtree_mergeinfo,
+                                      wc_mergeinfo, i))
+        {
+          SVN_ERR(svn_cmdline_printf(iterpool,
+                                     _("Trying to elide mergeinfo from path\n"
+                                       "    %s\n"
+                                       "    into mergeinfo at path\n"
+                                       "    %s\n\n"),
+                                     svn_dirent_join(parent_path, relpath,
+                                                     iterpool),
+                                     parent_path));
+        }
+      else
+        {
+          parent_mergeinfo = NULL;
+          subtree_mergeinfo = svn_min__get_mergeinfo(wc_mergeinfo, i);
+
+          SVN_ERR(svn_cmdline_printf(iterpool,
+                                     _("Trying to elide mergeinfo at path\n"
+                                       "    %s\n\n"),
+                                     svn_min__get_mergeinfo_path(wc_mergeinfo,
+                                                                 i)));
+        }
+
+      svn_pool_clear(iterpool);
+
+      subtree_mergeinfo = svn_mergeinfo_dup(subtree_mergeinfo, iterpool);
+      SVN_ERR(remove_obsolete_lines(session, subtree_mergeinfo, iterpool));
+
+      if (parent_mergeinfo)
+        {
+          parent_mergeinfo = svn_mergeinfo_dup(parent_mergeinfo, iterpool);
+          SVN_ERR(remove_lines(log, relpath, parent_mergeinfo,
+                                subtree_mergeinfo, iterpool));
+        }
+
+      if (apr_hash_count(subtree_mergeinfo))
+        {
+          apr_hash_index_t *hi;
+
+          if (parent_mergeinfo)
+            SVN_ERR(svn_cmdline_printf(iterpool,
+                      _("    Sub-tree merge info cannot be elided due to "
+                        "the following branches:\n")));
+          else
+            SVN_ERR(svn_cmdline_printf(iterpool,
+                      _("    Merge info kept for the following branches:\n")));
+
+          for (hi = apr_hash_first(scratch_pool, subtree_mergeinfo);
+                hi;
+                hi = apr_hash_next(hi))
+            {
+              const char *branch = apr_hash_this_key(hi);
+              SVN_ERR(svn_cmdline_printf(iterpool, _("    %s\n"), branch));
+            }
+
+          SVN_ERR(svn_cmdline_printf(iterpool, _("\n")));
+        }
+      else
+        {
+          SVN_ERR(svn_cmdline_printf(iterpool,
+                    _("    All sub-tree mergeinfo can be elided.\n\n")));
+        }
+    }
+
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_min__analyze(apr_getopt_t *os,
+                 void *baton,
+                 apr_pool_t *pool)
+{
+  svn_min__cmd_baton_t *cmd_baton = baton;
+  apr_pool_t *iterpool = svn_pool_create(pool);
+  apr_pool_t *subpool = svn_pool_create(pool);
+
+  int i;
+  for (i = 0; i < cmd_baton->opt_state->targets->nelts; i++)
+    {
+      svn_ra_session_t *session;
+      apr_array_header_t *wc_mergeinfo;
+      svn_min__log_t *log;
+      const char *url;
+      const char *common_path;
+
+      svn_pool_clear(iterpool);
+      SVN_ERR(svn_min__add_wc_info(baton, i, iterpool, subpool));
+      SVN_ERR(svn_client_open_ra_session2(&session, cmd_baton->repo_root,
+                                          NULL, cmd_baton->ctx, iterpool,
+                                          subpool));
+
+      /* scan working copy */
+      svn_pool_clear(subpool);
+      SVN_ERR(svn_cmdline_printf(subpool, _("Scanning working copy %s ...\n"),
+                                 cmd_baton->local_abspath));
+      SVN_ERR(svn_min__read_mergeinfo(&wc_mergeinfo, cmd_baton, iterpool,
+                                      subpool));
+      SVN_ERR(svn_min__print_mergeinfo_stats(wc_mergeinfo, subpool));
+
+      /* fetch log */
+      svn_pool_clear(subpool);
+      common_path = svn_min__common_parent(wc_mergeinfo, subpool, subpool);
+      SVN_ERR_ASSERT(*common_path == '/');
+
+      url = svn_path_url_add_component2(cmd_baton->repo_root,
+                                        common_path + 1,
+                                        subpool);
+      SVN_ERR(svn_cmdline_printf(subpool, _("Fetching log for %s ...\n"),
+                                 url));
+      SVN_ERR(svn_min__log(&log, url, cmd_baton, iterpool, subpool));
+      SVN_ERR(svn_min__print_log_stats(log, subpool));
+
+      /* actual analysis */
+      svn_pool_clear(subpool);
+      SVN_ERR(analyze(session, wc_mergeinfo, log, subpool));
+    }
+
+  svn_pool_destroy(subpool);
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}

Propchange: 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/analyze-cmd.c
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/clear-obsolete-cmd.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/clear-obsolete-cmd.c?rev=1642242&view=auto
==============================================================================
--- 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/clear-obsolete-cmd.c
 (added)
+++ 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/clear-obsolete-cmd.c
 Thu Nov 27 22:16:52 2014
@@ -0,0 +1,140 @@
+/*
+ * clear-obsolete-cmd.c -- Remove branches from MI that don't exist in HEAD.
+ *
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+/* ==================================================================== */
+
+
+
+/*** Includes. ***/
+
+#include "svn_dirent_uri.h"
+#include "svn_hash.h"
+#include "svn_path.h"
+#include "svn_pools.h"
+#include "private/svn_fspath.h"
+
+#include "mergeinfo-normalizer.h"
+
+#include "svn_private_config.h"
+#include <apr_poll.h>
+
+
+/*** Code. ***/
+
+static svn_error_t *
+remove_obsolete_lines(svn_ra_session_t *session,
+                      svn_mergeinfo_t mergeinfo,
+                      apr_pool_t *scratch_pool)
+{
+  apr_array_header_t *to_remove
+    = apr_array_make(scratch_pool, 16, sizeof(const char *));
+
+  int i;
+  apr_hash_index_t *hi;
+  for (hi = apr_hash_first(scratch_pool, mergeinfo);
+       hi;
+       hi = apr_hash_next(hi))
+    {
+      const char *path = apr_hash_this_key(hi);
+      svn_node_kind_t kind;
+
+      SVN_ERR_ASSERT(*path == '/');
+      SVN_ERR(svn_ra_check_path(session, path + 1, SVN_INVALID_REVNUM, &kind,
+                                scratch_pool));
+      if (kind == svn_node_none)
+        APR_ARRAY_PUSH(to_remove, const char *) = path;
+    }
+
+  for (i = 0; i < to_remove->nelts; ++i)
+    {
+      const char *path = APR_ARRAY_IDX(to_remove, i, const char *);
+      svn_hash_sets(mergeinfo, path, NULL);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+remove_obsoletes(apr_array_header_t *wc_mergeinfo,
+                 svn_ra_session_t *session,
+                 apr_pool_t *scratch_pool)
+{
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+
+  int i;
+  for (i = 0; i < wc_mergeinfo->nelts; ++i)
+    {
+      svn_mergeinfo_t mergeinfo = svn_min__get_mergeinfo(wc_mergeinfo, i);
+      svn_pool_clear(iterpool);
+
+      /* Combine mergeinfo ranges */
+      SVN_ERR(remove_obsolete_lines(session, mergeinfo, iterpool));
+    }
+
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_min__clear_obsolete(apr_getopt_t *os,
+                        void *baton,
+                        apr_pool_t *pool)
+{
+  svn_min__cmd_baton_t *cmd_baton = baton;
+  apr_pool_t *iterpool = svn_pool_create(pool);
+  apr_pool_t *subpool = svn_pool_create(pool);
+
+  int i;
+  for (i = 0; i < cmd_baton->opt_state->targets->nelts; i++)
+    {
+      svn_ra_session_t *session;
+      apr_array_header_t *wc_mergeinfo;
+
+      svn_pool_clear(iterpool);
+      SVN_ERR(svn_min__add_wc_info(baton, i, iterpool, subpool));
+      SVN_ERR(svn_client_open_ra_session2(&session, cmd_baton->repo_root,
+                                          NULL, cmd_baton->ctx, iterpool,
+                                          subpool));
+
+      /* scan working copy */
+      svn_pool_clear(subpool);
+      SVN_ERR(svn_min__read_mergeinfo(&wc_mergeinfo, cmd_baton, iterpool,
+                                      subpool));
+
+      /* actual normalization */
+      svn_pool_clear(subpool);
+      SVN_ERR(remove_obsoletes(wc_mergeinfo, session, subpool));
+
+      /* write results to disk */
+      svn_pool_clear(subpool);
+      if (!cmd_baton->opt_state->dry_run)
+        SVN_ERR(svn_min__write_mergeinfo(cmd_baton, wc_mergeinfo, subpool));
+    }
+
+  svn_pool_destroy(subpool);
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}

Propchange: 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/clear-obsolete-cmd.c
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/combine-ranges-cmd.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/combine-ranges-cmd.c?rev=1642242&view=auto
==============================================================================
--- 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/combine-ranges-cmd.c
 (added)
+++ 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/combine-ranges-cmd.c
 Thu Nov 27 22:16:52 2014
@@ -0,0 +1,186 @@
+/*
+ * combine-ranges-cmd.c -- Combine revision ranges in MI if the gap between
+ *                         them is inoperative for the respective path.
+ *
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+/* ==================================================================== */
+
+
+
+/*** Includes. ***/
+
+#include "svn_dirent_uri.h"
+#include "svn_hash.h"
+#include "svn_path.h"
+#include "svn_pools.h"
+#include "private/svn_fspath.h"
+
+#include "mergeinfo-normalizer.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+static svn_boolean_t
+all_positive_ranges(svn_rangelist_t *ranges)
+{
+  int i;
+  for (i = 0; i < ranges->nelts; ++i)
+    {
+      const svn_merge_range_t *range
+        = APR_ARRAY_IDX(ranges, i, const svn_merge_range_t *);
+
+      if (range->start > range->end)
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static svn_boolean_t
+inoperative(svn_min__log_t *log,
+            const char *path,
+            svn_revnum_t start,
+            svn_revnum_t end,
+            apr_pool_t *scratch_pool)
+{
+  svn_merge_range_t range = { 0 };
+  apr_array_header_t *ranges = apr_array_make(scratch_pool, 1, sizeof(&range));
+
+  range.start = start - 1;
+  range.end = end;
+  APR_ARRAY_PUSH(ranges, svn_merge_range_t *) = &range;
+
+  return svn_min__operative(log, path, ranges, scratch_pool)->nelts == 0;
+}
+
+static svn_error_t *
+shorten_lines(apr_array_header_t *wc_mergeinfo,
+              svn_min__log_t *log,
+              apr_pool_t *scratch_pool)
+{
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+  apr_pool_t *iterpool2 = svn_pool_create(scratch_pool);
+
+  int i;
+  for (i = 0; i < wc_mergeinfo->nelts; ++i)
+    {
+      apr_hash_index_t *hi;
+      svn_mergeinfo_t mergeinfo = svn_min__get_mergeinfo(wc_mergeinfo, i);
+
+      svn_pool_clear(iterpool);
+
+      for (hi = apr_hash_first(iterpool, mergeinfo);
+           hi;
+           hi = apr_hash_next(hi))
+        {
+          int source, dest;
+          const char *path = apr_hash_this_key(hi);
+          svn_rangelist_t *ranges = apr_hash_this_val(hi);
+
+          if (ranges->nelts < 2 || !all_positive_ranges(ranges))
+            continue;
+
+          for (source = 1, dest = 0; source < ranges->nelts; ++source)
+            {
+              svn_merge_range_t *source_range
+                = APR_ARRAY_IDX(ranges, source, svn_merge_range_t *);
+              svn_merge_range_t *dest_range
+                = APR_ARRAY_IDX(ranges, dest, svn_merge_range_t *);
+
+              svn_pool_clear(iterpool2);
+
+              if (   (source_range->inheritable == dest_range->inheritable)
+                  && inoperative(log, path, dest_range->end + 1,
+                                 source_range->start, iterpool2))
+                {
+                  dest_range->end = source_range->end;
+                }
+              else
+                {
+                  ++dest;
+                  APR_ARRAY_IDX(ranges, dest, svn_merge_range_t *)
+                    = source_range;
+                }
+            }
+
+          ranges->nelts = dest + 1;
+        }
+    }
+
+  svn_pool_destroy(iterpool2);
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_min__combine_ranges(apr_getopt_t *os,
+                        void *baton,
+                        apr_pool_t *pool)
+{
+  svn_min__cmd_baton_t *cmd_baton = baton;
+  apr_pool_t *iterpool = svn_pool_create(pool);
+  apr_pool_t *subpool = svn_pool_create(pool);
+
+  int i;
+  for (i = 0; i < cmd_baton->opt_state->targets->nelts; i++)
+    {
+      apr_array_header_t *wc_mergeinfo;
+      svn_min__log_t *log;
+      const char *url;
+      const char *common_path;
+
+      svn_pool_clear(iterpool);
+      SVN_ERR(svn_min__add_wc_info(baton, i, iterpool, subpool));
+
+      /* scan working copy */
+      svn_pool_clear(subpool);
+      SVN_ERR(svn_min__read_mergeinfo(&wc_mergeinfo, cmd_baton, iterpool,
+                                      subpool));
+
+      /* fetch log */
+      svn_pool_clear(subpool);
+      common_path = svn_min__common_parent(wc_mergeinfo, subpool, subpool);
+      SVN_ERR_ASSERT(*common_path == '/');
+      url = svn_path_url_add_component2(cmd_baton->repo_root,
+                                        common_path + 1,
+                                        subpool);
+      SVN_ERR(svn_min__log(&log, url, cmd_baton, iterpool, subpool));
+
+      /* actual normalization */
+      svn_pool_clear(subpool);
+      SVN_ERR(shorten_lines(wc_mergeinfo, log, subpool));
+
+      /* write results to disk */
+      svn_pool_clear(subpool);
+      if (!cmd_baton->opt_state->dry_run)
+        SVN_ERR(svn_min__write_mergeinfo(cmd_baton, wc_mergeinfo, subpool));
+    }
+
+  svn_pool_destroy(subpool);
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}

Propchange: 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/combine-ranges-cmd.c
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/help-cmd.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/help-cmd.c?rev=1642242&view=auto
==============================================================================
--- 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/help-cmd.c
 (added)
+++ 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/help-cmd.c
 Thu Nov 27 22:16:52 2014
@@ -0,0 +1,190 @@
+/*
+ * help-cmd.c -- Provide help
+ *
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+/* ==================================================================== */
+
+
+
+/*** Includes. ***/
+
+#include "svn_hash.h"
+#include "svn_string.h"
+#include "svn_config.h"
+#include "svn_dirent_uri.h"
+#include "svn_error.h"
+#include "mergeinfo-normalizer.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_min__help(apr_getopt_t *os,
+              void *baton,
+              apr_pool_t *pool)
+{
+  svn_min__opt_state_t *opt_state = NULL;
+  svn_stringbuf_t *version_footer = NULL;
+  const char *config_path;
+
+  char help_header[] =
+  N_("usage: svn <subcommand> [options] [args]\n"
+     "Subversion command-line client.\n"
+     "Type 'svn help <subcommand>' for help on a specific subcommand.\n"
+     "Type 'svn --version' to see the program version and RA modules\n"
+     "  or 'svn --version --quiet' to see just the version number.\n"
+     "\n"
+     "Most subcommands take file and/or directory arguments, recursing\n"
+     "on the directories.  If no arguments are supplied to such a\n"
+     "command, it recurses on the current directory (inclusive) by default.\n"
+     "\n"
+     "Available subcommands:\n");
+
+  char help_footer[] =
+  N_("Subversion is a tool for version control.\n"
+     "For additional information, see http://subversion.apache.org/\n";);
+
+  const char *ra_desc_start
+    = _("The following repository access (RA) modules are available:\n\n");
+
+  if (baton)
+    {
+      svn_min__cmd_baton_t *const cmd_baton = baton;
+#ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE
+      /* Windows never actually stores plaintext passwords, it
+         encrypts the contents using CryptoAPI. ...
+
+         ... If CryptoAPI is available ... but it should be on all
+         versions of Windows that are even remotely interesting two
+         days before the scheduled end of the world, when this comment
+         is being written. */
+#  ifndef WIN32
+      svn_boolean_t store_auth_creds =
+        SVN_CONFIG_DEFAULT_OPTION_STORE_AUTH_CREDS;
+      svn_boolean_t store_passwords =
+        SVN_CONFIG_DEFAULT_OPTION_STORE_PASSWORDS;
+      svn_boolean_t store_plaintext_passwords = FALSE;
+      svn_config_t *cfg;
+
+      if (cmd_baton->ctx->config)
+        {
+          cfg = svn_hash_gets(cmd_baton->ctx->config,
+                              SVN_CONFIG_CATEGORY_CONFIG);
+          if (cfg)
+            {
+              SVN_ERR(svn_config_get_bool(cfg, &store_auth_creds,
+                                          SVN_CONFIG_SECTION_AUTH,
+                                          SVN_CONFIG_OPTION_STORE_AUTH_CREDS,
+                                          store_auth_creds));
+              SVN_ERR(svn_config_get_bool(cfg, &store_passwords,
+                                          SVN_CONFIG_SECTION_AUTH,
+                                          SVN_CONFIG_OPTION_STORE_PASSWORDS,
+                                          store_passwords));
+            }
+          cfg = svn_hash_gets(cmd_baton->ctx->config,
+                              SVN_CONFIG_CATEGORY_SERVERS);
+          if (cfg)
+            {
+              const char *value;
+              SVN_ERR(svn_config_get_yes_no_ask
+                      (cfg, &value,
+                       SVN_CONFIG_SECTION_GLOBAL,
+                       SVN_CONFIG_OPTION_STORE_PLAINTEXT_PASSWORDS,
+                       SVN_CONFIG_DEFAULT_OPTION_STORE_PLAINTEXT_PASSWORDS));
+              if (0 == svn_cstring_casecmp(value, SVN_CONFIG_TRUE))
+                store_plaintext_passwords = TRUE;
+            }
+        }
+
+      if (store_plaintext_passwords && store_auth_creds && store_passwords)
+        {
+          version_footer = svn_stringbuf_create(
+              _("WARNING: Plaintext password storage is enabled!\n\n"),
+              pool);
+          svn_stringbuf_appendcstr(version_footer, ra_desc_start);
+        }
+#  endif /* !WIN32 */
+#endif /* !SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE */
+
+      opt_state = cmd_baton->opt_state;
+    }
+
+  if (!version_footer)
+    version_footer = svn_stringbuf_create(ra_desc_start, pool);
+  SVN_ERR(svn_ra_print_modules(version_footer, pool));
+
+  /*
+   * Show auth creds storage providers.
+   */
+  SVN_ERR(svn_config_get_user_config_path(&config_path,
+                                          opt_state ? opt_state->config_dir
+                                                    : NULL,
+                                          NULL,
+                                          pool));
+  svn_stringbuf_appendcstr(version_footer,
+                           _("\nThe following authentication credential caches 
are available:\n\n"));
+
+  /*### There is no API to query available providers at run time. */
+#if (defined(WIN32) && !defined(__MINGW32__))
+  version_footer =
+    svn_stringbuf_create(apr_psprintf(pool, _("%s* Wincrypt cache in %s\n"),
+                                      version_footer->data,
+                                      svn_dirent_local_style(config_path,
+                                                             pool)),
+                         pool);
+#elif !defined(SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE)
+  version_footer =
+    svn_stringbuf_create(apr_psprintf(pool, _("%s* Plaintext cache in %s\n"),
+                                      version_footer->data,
+                                      svn_dirent_local_style(config_path,
+                                                             pool)),
+                         pool);
+#endif
+#ifdef SVN_HAVE_GNOME_KEYRING
+  svn_stringbuf_appendcstr(version_footer, "* Gnome Keyring\n");
+#endif
+#ifdef SVN_HAVE_GPG_AGENT
+  svn_stringbuf_appendcstr(version_footer, "* GPG-Agent\n");
+#endif
+#ifdef SVN_HAVE_KEYCHAIN_SERVICES
+  svn_stringbuf_appendcstr(version_footer, "* Mac OS X Keychain\n");
+#endif
+#ifdef SVN_HAVE_KWALLET
+  svn_stringbuf_appendcstr(version_footer, "* KWallet (KDE)\n");
+#endif
+
+  return svn_opt_print_help4(os,
+                             "svn-mergeinfo-normalizer",
+                             opt_state ? opt_state->version : FALSE,
+                             opt_state ? opt_state->quiet : FALSE,
+                             TRUE,
+                             version_footer->data,
+                             help_header,   /* already gettext()'d */
+                             svn_min__cmd_table,
+                             svn_min__options,
+                             svn_min__global_options,
+                             _(help_footer),
+                             pool);
+}

Propchange: 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/help-cmd.c
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
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=1642242&view=auto
==============================================================================
--- 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/log.c
 (added)
+++ 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/log.c
 Thu Nov 27 22:16:52 2014
@@ -0,0 +1,371 @@
+/*
+ * log.c -- Fetch log data and implement the log queries
+ *
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+#include "svn_cmdline.h"
+#include "svn_client.h"
+#include "svn_dirent_uri.h"
+#include "svn_string.h"
+#include "svn_path.h"
+#include "svn_error.h"
+#include "svn_sorts.h"
+#include "svn_pools.h"
+#include "svn_hash.h"
+
+#include "private/svn_subr_private.h"
+#include "private/svn_sorts_private.h"
+
+#include "mergeinfo-normalizer.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+typedef struct log_entry_t
+{
+  svn_revnum_t revision;
+  const char *common_base;
+  apr_array_header_t *paths;
+} log_entry_t;
+
+struct svn_min__log_t
+{
+  apr_hash_t *unique_paths;
+
+  svn_revnum_t first_rev;
+  svn_revnum_t head_rev;
+  apr_array_header_t *entries;
+};
+
+static const char *
+internalize(apr_hash_t *unique_paths,
+            const char *path,
+            apr_ssize_t path_len)
+{
+  const char *result = apr_hash_get(unique_paths, path, path_len);
+  if (result == NULL)
+    {
+      result = apr_pstrmemdup(apr_hash_pool_get(unique_paths), path, path_len);
+      apr_hash_set(unique_paths, result, path_len, result);
+    }
+
+  return result;
+}
+
+static svn_error_t *
+log_entry_receiver(void *baton,
+                   svn_log_entry_t *log_entry,
+                   apr_pool_t *scratch_pool)
+{
+  svn_min__log_t *log = baton;
+  apr_pool_t *result_pool = log->entries->pool;
+  log_entry_t *entry;
+  apr_hash_index_t *hi;
+  const char *common_base;
+  int count;
+
+  if (!log_entry->changed_paths || !apr_hash_count(log_entry->changed_paths))
+    return SVN_NO_ERROR;
+
+  entry = apr_pcalloc(result_pool, sizeof(*entry));
+  entry->revision = log_entry->revision;
+  entry->paths = apr_array_make(result_pool,
+                                apr_hash_count(log_entry->changed_paths),
+                                sizeof(const char *));
+
+  for (hi = apr_hash_first(scratch_pool, log_entry->changed_paths);
+       hi;
+       hi = apr_hash_next(hi))
+    {
+      const char *path = apr_hash_this_key(hi);
+      path = internalize(log->unique_paths, path, apr_hash_this_key_len(hi));
+      APR_ARRAY_PUSH(entry->paths, const char *) = path;
+    }
+
+  count = entry->paths->nelts;
+  if (count == 1)
+    {
+      entry->common_base = APR_ARRAY_IDX(entry->paths, 0, const char *);
+    }
+  else
+    {
+      svn_sort__array(entry->paths, svn_sort_compare_paths);
+
+      common_base = svn_dirent_get_longest_ancestor(
+                      APR_ARRAY_IDX(entry->paths, 0, const char *),
+                      APR_ARRAY_IDX(entry->paths, count - 1, const char *),
+                      scratch_pool);
+      entry->common_base = internalize(log->unique_paths, common_base,
+                                       strlen(common_base));
+    }
+
+  APR_ARRAY_PUSH(log->entries, log_entry_t *) = entry;
+
+  log->first_rev = log_entry->revision;
+  if (log->head_rev == SVN_INVALID_REVNUM)
+    log->head_rev = log_entry->revision;
+
+  return SVN_NO_ERROR;
+}
+
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_min__log(svn_min__log_t **log,
+             const char *url,
+             svn_min__cmd_baton_t *baton,
+             apr_pool_t *result_pool,
+             apr_pool_t *scratch_pool)
+{
+  svn_client_ctx_t *ctx = baton->ctx;
+  svn_min__log_t *result;
+
+  apr_array_header_t *targets;
+  apr_array_header_t *revisions;
+  apr_array_header_t *revprops;
+  svn_opt_revision_t peg_revision = { svn_opt_revision_head };
+  svn_opt_revision_range_t range = { { svn_opt_revision_unspecified },
+                                     { svn_opt_revision_unspecified } };
+
+  targets = apr_array_make(scratch_pool, 1, sizeof(const char *));
+  APR_ARRAY_PUSH(targets, const char *) = url;
+
+  revisions = apr_array_make(scratch_pool, 1, sizeof(&range));
+  APR_ARRAY_PUSH(revisions, svn_opt_revision_range_t *) = &range;
+
+  revprops = apr_array_make(scratch_pool, 0, sizeof(const char *));
+
+  result = apr_pcalloc(result_pool, sizeof(*result));
+  result->unique_paths = svn_hash__make(result_pool);
+  result->first_rev = SVN_INVALID_REVNUM;
+  result->head_rev = SVN_INVALID_REVNUM;
+  result->entries = apr_array_make(result_pool, 1024, sizeof(log_entry_t *));
+
+  SVN_ERR(svn_client_log5(targets,
+                          &peg_revision,
+                          revisions,
+                          0, /* no limit */
+                          TRUE, /* verbose */
+                          TRUE, /* stop-on-copy */
+                          FALSE, /* merge history */
+                          revprops,
+                          log_entry_receiver,
+                          result,
+                          ctx,
+                          scratch_pool));
+
+  svn_sort__array_reverse(result->entries, scratch_pool);
+  *log = result;
+
+  return SVN_NO_ERROR;
+}
+
+static void
+append_rev_to_ranges(svn_rangelist_t *ranges,
+                     svn_revnum_t revision,
+                     svn_boolean_t inheritable,
+                     apr_pool_t *result_pool)
+{
+  svn_merge_range_t *range;
+  if (ranges->nelts)
+    {
+      range = APR_ARRAY_IDX(ranges, ranges->nelts - 1, svn_merge_range_t *);
+      if (range->end + 1 == revision && range->inheritable == inheritable)
+        {
+          range->end = revision;
+          return;
+        }
+    }
+
+  range = apr_pcalloc(result_pool, sizeof(*range));
+  range->start = revision - 1;
+  range->end = revision;
+  range->inheritable = inheritable;
+
+  APR_ARRAY_PUSH(ranges, svn_merge_range_t *) = range;
+}
+
+static int
+compare_rev_log_entry(const void *lhs,
+                      const void *rhs)
+{
+  const log_entry_t *entry = *(const log_entry_t * const *)lhs;
+  svn_revnum_t revision = *(const svn_revnum_t *)rhs;
+
+  if (entry->revision < revision)
+    return -1;
+
+  return entry->revision == revision ? 0 : 1;
+}
+
+static void
+restrict_range(svn_min__log_t *log,
+               svn_merge_range_t *range,
+               svn_rangelist_t *ranges,
+               apr_pool_t *result_pool)
+{
+  if (range->start + 1 < log->first_rev)
+    {
+      svn_merge_range_t *new_range
+        = apr_pmemdup(result_pool, range, sizeof(*range));
+      new_range->end = MIN(new_range->end, log->first_rev - 1);
+
+      APR_ARRAY_PUSH(ranges, svn_merge_range_t *) = new_range;
+      range->start = new_range->end;
+    }
+
+  if (range->end > log->head_rev)
+    {
+      svn_merge_range_t *new_range
+        = apr_pmemdup(result_pool, range, sizeof(*range));
+      new_range->start = log->head_rev;
+
+      APR_ARRAY_PUSH(ranges, svn_merge_range_t *) = new_range;
+      range->end = new_range->start;
+    }
+}
+
+static svn_boolean_t
+is_relevant(const char *changed_path,
+            const char *path,
+            const void *baton)
+{
+  return  svn_dirent_is_ancestor(changed_path, path)
+       || svn_dirent_is_ancestor(path, changed_path);
+}
+
+static svn_boolean_t
+below_path_outside_subtree(const char *changed_path,
+                           const char *path,
+                           const void *baton)
+{
+  const char *subtree = baton;
+
+  /* Is this a change _below_ PATH but not within SUBTREE? */
+  return   !svn_dirent_is_ancestor(subtree, changed_path)
+        && svn_dirent_is_ancestor(path, changed_path)
+        && strcmp(path, changed_path);
+}
+
+static svn_rangelist_t *
+filter_ranges(svn_min__log_t *log,
+              const char *path,
+              svn_rangelist_t *ranges,
+              svn_boolean_t (*path_relavent)(const char*, const char *,
+                                             const void *),
+              const void *baton,
+              apr_pool_t *result_pool)
+{
+  svn_rangelist_t *result;
+  int i, k, l;
+
+  if (!SVN_IS_VALID_REVNUM(log->first_rev))
+    return svn_rangelist_dup(ranges, result_pool);
+
+  result = apr_array_make(result_pool, 0, ranges->elt_size);
+  for (i = 0; i < ranges->nelts; ++i)
+    {
+      svn_merge_range_t range
+        = *APR_ARRAY_IDX(ranges, i, const svn_merge_range_t *);
+      restrict_range(log, &range, result, result_pool);
+
+      ++range.start;
+      for (k = svn_sort__bsearch_lower_bound(log->entries, &range.start,
+                                             compare_rev_log_entry);
+           k < log->entries->nelts;
+           ++k)
+        {
+          const log_entry_t *entry = APR_ARRAY_IDX(log->entries, k,
+                                                  const log_entry_t *);
+          if (entry->revision > range.end)
+            break;
+
+          if (!is_relevant(entry->common_base, path, NULL))
+            continue;
+
+          for (l = 0; l < entry->paths->nelts; ++l)
+            {
+              const char *changed_path
+                = APR_ARRAY_IDX(entry->paths, l, const char *);
+
+              /* Is this a change _below_ PATH but not within SUBTREE? */
+              if (path_relavent(changed_path, path, baton))
+                {
+                  append_rev_to_ranges(result, entry->revision,
+                                       range.inheritable, result_pool);
+                  break;
+                }
+            }
+        }
+    }
+
+  return result;
+}
+
+svn_rangelist_t *
+svn_min__operative(svn_min__log_t *log,
+                   const char *path,
+                   svn_rangelist_t *ranges,
+                   apr_pool_t *result_pool)
+{
+  return filter_ranges(log, path, ranges, is_relevant, NULL, result_pool);
+}
+
+svn_rangelist_t *
+svn_min__operative_outside_subtree(svn_min__log_t *log,
+                                   const char *path,
+                                   const char *subtree,
+                                   svn_rangelist_t *ranges,
+                                   apr_pool_t *result_pool)
+{
+  return filter_ranges(log, path, ranges, below_path_outside_subtree,
+                       subtree, result_pool);
+}
+
+svn_error_t *
+svn_min__print_log_stats(svn_min__log_t *log,
+                         apr_pool_t *scratch_pool)
+{
+  int change_count = 0;
+
+  int i;
+  for (i = 0; i < log->entries->nelts; ++i)
+    {
+      const log_entry_t *entry = APR_ARRAY_IDX(log->entries, i,
+                                               const log_entry_t *);
+      change_count += entry->paths->nelts;
+    }
+
+  SVN_ERR(svn_cmdline_printf(scratch_pool,
+                             _("    Received %d revisions from %ld to %ld.\n"),
+                             log->entries->nelts, log->first_rev,
+                             log->head_rev));
+  SVN_ERR(svn_cmdline_printf(scratch_pool,
+                             _("    Received %d path changes.\n"),
+                             change_count));
+  SVN_ERR(svn_cmdline_printf(scratch_pool,
+                             _("    Pool has %u different paths.\n\n"),
+                             apr_hash_count(log->unique_paths)));
+
+  return SVN_NO_ERROR;
+}

Propchange: 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/log.c
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
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=1642242&view=auto
==============================================================================
--- 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/mergeinfo-normalizer.h
 (added)
+++ 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/mergeinfo-normalizer.h
 Thu Nov 27 22:16:52 2014
@@ -0,0 +1,183 @@
+/*
+ * mergeinfo-normalizer.h:  tool-global functions and structures.
+ *
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+/* ==================================================================== */
+
+
+
+#ifndef SVN_MERGEINFO_NORMALIZER_H
+#define SVN_MERGEINFO_NORMALIZER_H
+
+/*** Includes. ***/
+#include <apr_tables.h>
+#include <apr_getopt.h>
+
+#include "svn_client.h"
+#include "svn_opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/*** Command dispatch. ***/
+
+/* Hold results of option processing that are shared by multiple
+   commands. */
+typedef struct svn_min__opt_state_t
+{
+  /* After option processing is done, reflects the switch actually
+     given on the command line, or svn_depth_unknown if none. */
+  svn_depth_t depth;
+
+  svn_boolean_t quiet;           /* sssh...avoid unnecessary output */
+  svn_boolean_t version;         /* print version information */
+  svn_boolean_t help;            /* print usage message */
+  const char *auth_username;     /* auth username */
+  const char *auth_password;     /* auth password */
+  apr_array_header_t *targets;
+  svn_boolean_t no_auth_cache;   /* do not cache authentication information */
+  svn_boolean_t dry_run;         /* try operation but make no changes */
+  const char *config_dir;        /* over-riding configuration directory */
+  apr_array_header_t *config_options; /* over-riding configuration options */
+
+  /* trust server SSL certs that would otherwise be rejected as "untrusted" */
+  svn_boolean_t trust_server_cert_unknown_ca;
+  svn_boolean_t trust_server_cert_cn_mismatch;
+  svn_boolean_t trust_server_cert_expired;
+  svn_boolean_t trust_server_cert_not_yet_valid;
+  svn_boolean_t trust_server_cert_other_failure;
+  svn_boolean_t allow_mixed_rev;   /* Allow operation on mixed-revision WC */
+  svn_boolean_t non_interactive;
+} svn_min__opt_state_t;
+
+
+typedef struct svn_min__cmd_baton_t
+{
+  svn_min__opt_state_t *opt_state;
+  svn_client_ctx_t *ctx;
+
+  const char *local_abspath;
+  const char *wc_root;
+  const char *repo_root;
+} svn_min__cmd_baton_t;
+
+
+/* Declare all the command procedures */
+svn_opt_subcommand_t
+  svn_min__help,
+  svn_min__normalize,
+  svn_min__clear_obsolete,
+  svn_min__combine_ranges,
+  svn_min__analyze;
+
+/* See definition in svn.c for documentation. */
+extern const svn_opt_subcommand_desc2_t svn_min__cmd_table[];
+
+/* See definition in svn.c for documentation. */
+extern const int svn_min__global_options[];
+
+/* See definition in svn.c for documentation. */
+extern const apr_getopt_option_t svn_min__options[];
+
+
+/* Our cancellation callback. */
+svn_error_t *
+svn_min__check_cancel(void *baton);
+
+
+/*** Command-line output functions -- printing to the user. ***/
+
+svn_error_t *
+svn_min__add_wc_info(svn_min__cmd_baton_t* baton,
+                     int idx,
+                     apr_pool_t* result_pool,
+                     apr_pool_t* scratch_pool);
+
+svn_error_t *
+svn_min__read_mergeinfo(apr_array_header_t **result,
+                        svn_min__cmd_baton_t *baton,
+                        apr_pool_t *result_pool,
+                        apr_pool_t *scratch_pool);
+
+const char *
+svn_min__common_parent(apr_array_header_t *mergeinfo,
+                       apr_pool_t *result_pool,
+                       apr_pool_t *scratch_pool);
+
+svn_mergeinfo_t
+svn_min__get_mergeinfo(apr_array_header_t *mergeinfo,
+                       int idx);
+
+const char *
+svn_min__get_mergeinfo_path(apr_array_header_t *mergeinfo,
+                            int idx);
+
+svn_boolean_t
+svn_min__get_mergeinfo_pair(const char **parent_path,
+                            const char **subtree_relpath,
+                            svn_mergeinfo_t *parent_mergeinfo,
+                            svn_mergeinfo_t *subtree_mergeinfo,
+                            apr_array_header_t *mergeinfo,
+                            int idx);
+
+svn_error_t *
+svn_min__write_mergeinfo(svn_min__cmd_baton_t *baton,
+                         apr_array_header_t *mergeinfo,
+                         apr_pool_t *scratch_pool);
+
+svn_error_t *
+svn_min__print_mergeinfo_stats(apr_array_header_t *wc_mergeinfo,
+                               apr_pool_t *scratch_pool);
+
+typedef struct svn_min__log_t svn_min__log_t;
+
+svn_error_t *
+svn_min__log(svn_min__log_t **log,
+             const char *url,
+             svn_min__cmd_baton_t *baton,
+             apr_pool_t *result_pool,
+             apr_pool_t *scratch_pool);
+
+svn_rangelist_t *
+svn_min__operative(svn_min__log_t *log,
+                   const char *path,
+                   svn_rangelist_t *ranges,
+                   apr_pool_t *result_pool);
+
+svn_rangelist_t *
+svn_min__operative_outside_subtree(svn_min__log_t *log,
+                                   const char *path,
+                                   const char *subtree,
+                                   svn_rangelist_t *ranges,
+                                   apr_pool_t *result_pool);
+
+svn_error_t *
+svn_min__print_log_stats(svn_min__log_t *log,
+                         apr_pool_t *scratch_pool);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* SVN_MERGEINFO_NORMALIZER_H */

Propchange: 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/mergeinfo-normalizer.h
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/normalize-cmd.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/normalize-cmd.c?rev=1642242&view=auto
==============================================================================
--- 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/normalize-cmd.c
 (added)
+++ 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/normalize-cmd.c
 Thu Nov 27 22:16:52 2014
@@ -0,0 +1,216 @@
+/*
+ * normalize-cmd.c -- Elide mergeinfo from sub-nodes
+ *
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+/* ==================================================================== */
+
+
+
+/*** Includes. ***/
+
+#include "svn_dirent_uri.h"
+#include "svn_hash.h"
+#include "svn_path.h"
+#include "svn_pools.h"
+#include "private/svn_fspath.h"
+
+#include "mergeinfo-normalizer.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+static svn_boolean_t
+all_positive_ranges(svn_rangelist_t *ranges)
+{
+  int i;
+  for (i = 0; i < ranges->nelts; ++i)
+    {
+      const svn_merge_range_t *range
+        = APR_ARRAY_IDX(ranges, i, const svn_merge_range_t *);
+
+      if (range->start > range->end)
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static svn_error_t *
+remove_lines(svn_min__log_t *log,
+             const char *relpath,
+             svn_mergeinfo_t parent_mergeinfo,
+             svn_mergeinfo_t subtree_mergeinfo,
+             apr_pool_t *scratch_pool)
+{
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+
+  apr_hash_index_t *hi;
+  for (hi = apr_hash_first(scratch_pool, parent_mergeinfo);
+       hi;
+       hi = apr_hash_next(hi))
+    {
+      const char *parent_path, *subtree_path;
+      svn_rangelist_t *parent_ranges, *subtree_ranges;
+      svn_rangelist_t *operative_outside_subtree, *operative_in_subtree;
+
+      svn_pool_clear(iterpool);
+
+      parent_path = apr_hash_this_key(hi);
+      subtree_path = svn_fspath__join(parent_path, relpath, iterpool);
+      parent_ranges = apr_hash_this_val(hi);
+      subtree_ranges = svn_hash_gets(subtree_mergeinfo, subtree_path);
+
+      if (subtree_ranges && all_positive_ranges(subtree_ranges))
+        {
+          svn_rangelist_t *subtree_only;
+          svn_rangelist_t *parent_only;
+
+          SVN_ERR(svn_rangelist_diff(&parent_only, &subtree_only,
+                                     parent_ranges, subtree_ranges, FALSE,
+                                     iterpool));
+          subtree_only
+            = svn_min__operative(log, subtree_path, parent_only, iterpool);
+
+          operative_outside_subtree
+            = svn_min__operative_outside_subtree(log, parent_path, 
subtree_path,
+                                                 subtree_only, iterpool);
+          operative_in_subtree
+            = svn_min__operative(log, subtree_path, parent_only, iterpool);
+
+          /* This will also work when subtree_only is empty. */
+          if (   !operative_outside_subtree->nelts
+              && !operative_in_subtree->nelts)
+            {
+              SVN_ERR(svn_rangelist_merge2(parent_ranges, subtree_only,
+                                           parent_ranges->pool, iterpool));
+              svn_hash_sets(subtree_mergeinfo, subtree_path, NULL);
+            }
+        }
+    }
+
+  /* TODO: Move subtree ranges to parent even if the parent has no entry
+   * for the respective branches, yet. */
+
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+normalize(apr_array_header_t *wc_mergeinfo,
+          svn_min__log_t *log,
+          apr_pool_t *scratch_pool)
+{
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+
+  int i;
+  for (i = wc_mergeinfo->nelts - 1; i >= 0; --i)
+    {
+      const char *parent_path;
+      const char *relpath;
+      svn_mergeinfo_t parent_mergeinfo;
+      svn_mergeinfo_t subtree_mergeinfo;
+
+      if (svn_min__get_mergeinfo_pair(&parent_path, &relpath,
+                                      &parent_mergeinfo, &subtree_mergeinfo,
+                                      wc_mergeinfo, i))
+        {
+          svn_mergeinfo_t parent_mergeinfo_copy;
+          svn_mergeinfo_t subtree_mergeinfo_copy;
+
+          svn_pool_clear(iterpool);
+          parent_mergeinfo_copy = svn_mergeinfo_dup(parent_mergeinfo,
+                                                    iterpool);
+          subtree_mergeinfo_copy = svn_mergeinfo_dup(subtree_mergeinfo,
+                                                     iterpool);
+
+          SVN_ERR(remove_lines(log, relpath, parent_mergeinfo_copy,
+                               subtree_mergeinfo_copy, iterpool));
+
+          if (apr_hash_count(subtree_mergeinfo_copy) == 0)
+            {
+              SVN_ERR(svn_mergeinfo_merge2(parent_mergeinfo,
+                                           parent_mergeinfo_copy,
+                                           apr_hash_pool_get(parent_mergeinfo),
+                                           iterpool));
+              apr_hash_clear(subtree_mergeinfo);
+            }
+        }
+    }
+
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_min__normalize(apr_getopt_t *os,
+                   void *baton,
+                   apr_pool_t *pool)
+{
+  svn_min__cmd_baton_t *cmd_baton = baton;
+  apr_pool_t *iterpool = svn_pool_create(pool);
+  apr_pool_t *subpool = svn_pool_create(pool);
+
+  int i;
+  for (i = 0; i < cmd_baton->opt_state->targets->nelts; i++)
+    {
+      apr_array_header_t *wc_mergeinfo;
+      svn_min__log_t *log;
+      const char *url;
+      const char *common_path;
+
+      svn_pool_clear(iterpool);
+      SVN_ERR(svn_min__add_wc_info(baton, i, iterpool, subpool));
+
+      /* scan working copy */
+      svn_pool_clear(subpool);
+      SVN_ERR(svn_min__read_mergeinfo(&wc_mergeinfo, cmd_baton, iterpool,
+                                      subpool));
+
+      /* fetch log */
+      svn_pool_clear(subpool);
+      common_path = svn_min__common_parent(wc_mergeinfo, subpool, subpool);
+      SVN_ERR_ASSERT(*common_path == '/');
+      url = svn_path_url_add_component2(cmd_baton->repo_root,
+                                        common_path + 1,
+                                        subpool);
+      SVN_ERR(svn_min__log(&log, url, cmd_baton, iterpool, subpool));
+
+      /* actual normalization */
+      svn_pool_clear(subpool);
+      SVN_ERR(normalize(wc_mergeinfo, log, subpool));
+
+      /* write results to disk */
+      svn_pool_clear(subpool);
+      if (!cmd_baton->opt_state->dry_run)
+        SVN_ERR(svn_min__write_mergeinfo(cmd_baton, wc_mergeinfo, subpool));
+    }
+
+  svn_pool_destroy(subpool);
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}

Propchange: 
subversion/branches/svn-mergeinfo-normalizer/tools/client-side/svn-mergeinfo-normalizer/normalize-cmd.c
------------------------------------------------------------------------------
    svn:eol-style = native


Reply via email to