Author: stsp
Date: Wed Jun 27 18:53:25 2012
New Revision: 1354666

URL: http://svn.apache.org/viewvc?rev=1354666&view=rev
Log:
Add support for 'svn log --search', which can be used to restrict log output
to entries where the author, log message, or list of changed paths matches
a search pattern. The new --search option works in plain and XML output modes.

Currently, apr_fnmatch() is used for pattern matching, which supports glob
wildcards only. It would be nice to provide full regex support but I'm not
quite sure whether we can use apr_strmatch() without breaking compatibility
with older APR-util versions. So stick with glob patterns for now. Maybe
we can add full regex support later.

(Note: committing from slow laptop on train -- sorry if this breaks any tests)

* subversion/svn/cl.h
  (svn_cl__opt_state_t): Add search_pattern.

* subversion/svn/log-cmd.c
  (): Include apr_fnmatch.h.
  (log_receiver_baton): Add search_pattern.
  (match_search_pattern): New helper function that matches elements of a
   log entry to a search pattern.
  (log_entry_receiver, log_entry_receiver_xml): If a search pattern is provided
   only show log entries which match the pattern.
  (svn_cl__log): Pass search_pattern from opt_state to the log baton.

* subversion/svn/main.c
  (svn_cl__longopt_t): Add opt_search.
  (svn_cl__options): Add --search option.
  (svn_cl__cmd_table): Add --search option to the log subcommand and extend the
   corresponding help text.
  (main): Handle --search option.

* subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout: Adjust
   for changed output of 'svn help log'.

* subversion/tests/cmdline/log_tests.py
  (log_search, test_list): New test for log --search.

Modified:
    subversion/trunk/subversion/svn/cl.h
    subversion/trunk/subversion/svn/log-cmd.c
    subversion/trunk/subversion/svn/main.c
    
subversion/trunk/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout
    subversion/trunk/subversion/tests/cmdline/log_tests.py

Modified: subversion/trunk/subversion/svn/cl.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/cl.h?rev=1354666&r1=1354665&r2=1354666&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/cl.h (original)
+++ subversion/trunk/subversion/svn/cl.h Wed Jun 27 18:53:25 2012
@@ -237,6 +237,7 @@ typedef struct svn_cl__opt_state_t
   svn_boolean_t show_diff;        /* produce diff output (maps to --diff) */
   svn_boolean_t allow_mixed_rev; /* Allow operation on mixed-revision WC */
   svn_boolean_t include_externals; /* Recurses (in)to file & dir externals */
+  const char *search_pattern;     /* pattern argument for --search */
 } svn_cl__opt_state_t;
 
 

Modified: subversion/trunk/subversion/svn/log-cmd.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/log-cmd.c?rev=1354666&r1=1354665&r2=1354666&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/log-cmd.c (original)
+++ subversion/trunk/subversion/svn/log-cmd.c Wed Jun 27 18:53:25 2012
@@ -24,6 +24,7 @@
 #define APR_WANT_STRFUNC
 #define APR_WANT_STDIO
 #include <apr_want.h>
+#include <apr_fnmatch.h>
 
 #include "svn_client.h"
 #include "svn_compat.h"
@@ -70,6 +71,10 @@ struct log_receiver_baton
   /* Stack which keeps track of merge revision nesting, using svn_revnum_t's */
   apr_array_header_t *merge_stack;
 
+  /* Log message search pattern. Log entries will only be shown if the author,
+   * the log message, or a changed path matches this pattern. */
+  const char *search_pattern;
+
   /* Pool for persistent allocations. */
   apr_pool_t *pool;
 };
@@ -141,6 +146,47 @@ display_diff(const svn_log_entry_t *log_
 }
 
 
+/* Return TRUE if SEARCH_PATTERN matches the AUTHOR, LOG_MESSAGE, or a path
+ * in the set of keys of the CHANGED_PATHS hash. Else, return FALSE.
+ * LOG_MESSAGE and CHANGED_PATHS are allowed to be NULL. */
+static svn_boolean_t
+match_search_pattern(const char *search_pattern,
+                     const char *author,
+                     const char *log_message,
+                     apr_hash_t *changed_paths,
+                     apr_pool_t *pool)
+{
+  /* Match any substring containing the pattern, like UNIX 'grep' does. */
+  const char *pattern = apr_psprintf(pool, "*%s*", search_pattern);
+
+  /* Does the author match the search pattern? */
+  if (apr_fnmatch(pattern, author, 0) == APR_SUCCESS)
+    return TRUE;
+
+  /* Does the log message the search pattern? */
+  if (log_message && apr_fnmatch(pattern, log_message, 0) == APR_SUCCESS)
+    return TRUE;
+
+  if (changed_paths)
+    {
+      apr_hash_index_t *hi;
+
+      /* Does a changed path match the search pattern? */
+      for (hi = apr_hash_first(pool, changed_paths);
+           hi;
+           hi = apr_hash_next(hi))
+        {
+          const char *path = svn__apr_hash_index_key(hi);
+
+          if (apr_fnmatch(search_pattern, path, 0) == APR_SUCCESS)
+            return TRUE;
+        }
+    }
+
+  return FALSE;
+}
+
+
 /* Implement `svn_log_entry_receiver_t', printing the logs in
  * a human-readable and machine-parseable format.
  *
@@ -257,6 +303,13 @@ log_entry_receiver(void *baton,
   if (! lb->omit_log_message && message == NULL)
     message = "";
 
+  if (lb->search_pattern)
+    {
+      if (! match_search_pattern(lb->search_pattern, author, message,
+                                 log_entry->changed_paths2, pool))
+        return SVN_NO_ERROR;
+    }
+
   SVN_ERR(svn_cmdline_printf(pool,
                              SEP_STRING "r%ld | %s | %s",
                              log_entry->revision, author, date));
@@ -418,13 +471,6 @@ log_entry_receiver_xml(void *baton,
 
   svn_compat_log_revprops_out(&author, &date, &message, log_entry->revprops);
 
-  if (author)
-    author = svn_xml_fuzzy_escape(author, pool);
-  if (date)
-    date = svn_xml_fuzzy_escape(date, pool);
-  if (message)
-    message = svn_xml_fuzzy_escape(message, pool);
-
   if (log_entry->revision == 0 && message == NULL)
     return SVN_NO_ERROR;
 
@@ -437,6 +483,21 @@ log_entry_receiver_xml(void *baton,
       return SVN_NO_ERROR;
     }
 
+  if (lb->search_pattern)
+    {
+      /* Match search pattern before XML-escaping. */
+      if (! match_search_pattern(lb->search_pattern, author, message,
+                                 log_entry->changed_paths2, pool))
+        return SVN_NO_ERROR;
+    }
+
+  if (author)
+    author = svn_xml_fuzzy_escape(author, pool);
+  if (date)
+    date = svn_xml_fuzzy_escape(date, pool);
+  if (message)
+    message = svn_xml_fuzzy_escape(message, pool);
+
   revstr = apr_psprintf(pool, "%ld", log_entry->revision);
   /* <logentry revision="xxx"> */
   svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "logentry",
@@ -654,6 +715,7 @@ svn_cl__log(apr_getopt_t *os,
                                                    : opt_state->depth;
   lb.diff_extensions = opt_state->extensions;
   lb.merge_stack = apr_array_make(pool, 0, sizeof(svn_revnum_t));
+  lb.search_pattern = opt_state->search_pattern;
   lb.pool = pool;
 
   if (opt_state->xml)

Modified: subversion/trunk/subversion/svn/main.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/main.c?rev=1354666&r1=1354665&r2=1354666&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/main.c (original)
+++ subversion/trunk/subversion/svn/main.c Wed Jun 27 18:53:25 2012
@@ -130,6 +130,7 @@ typedef enum svn_cl__longopt_t {
   opt_allow_mixed_revisions,
   opt_include_externals,
   opt_symmetric,
+  opt_search,
 } svn_cl__longopt_t;
 
 
@@ -375,6 +376,8 @@ const apr_getopt_option_t svn_cl__option
                        "fixed revision. (See the svn:externals property)")},
   {"symmetric", opt_symmetric, 0,
                        N_("Symmetric merge")},
+  {"search", opt_search, 1,
+                       N_("use ARG as search pattern (glob syntax)")},
 
   /* Long-opt Aliases
    *
@@ -678,7 +681,17 @@ const svn_opt_subcommand_desc2_t svn_cl_
      "\n"
      "  The --depth option is only valid in combination with the --diff 
option\n"
      "  and limits the scope of the displayed diff to the specified depth.\n"
-
+     "\n"
+     "  If the --search option is used, log messages are displayed only if 
the\n"
+     "  provided search pattern matches the author, log message text, or, if\n"
+     "  the --verbose option is also provided, a changed path.\n"
+     "  The search pattern may include glob syntax wildcards:\n"
+     "      ?      matches any single character\n"
+     "      *      matches a sequence of arbitrary characters\n"
+     "      [...]  matches any of the characters listed inside the brackets\n"
+     "  If --limit is used in combination with --search, --limit restricts 
the\n"
+     "  number of log messages searched, rather than restricting the output\n"
+     "  to a particular number of matching log messages.\n"
      "\n"
      "  Examples:\n"
      "\n"
@@ -707,7 +720,7 @@ const svn_opt_subcommand_desc2_t svn_cl_
      "      svn log --stop-on-copy --limit 1 -r0:HEAD ^/branches/foo\n"),
     {'r', 'q', 'v', 'g', 'c', opt_targets, opt_stop_on_copy, opt_incremental,
      opt_xml, 'l', opt_with_all_revprops, opt_with_no_revprops, 
opt_with_revprop,
-     opt_depth, opt_diff, opt_diff_cmd, opt_internal_diff, 'x'},
+     opt_depth, opt_diff, opt_diff_cmd, opt_internal_diff, 'x', opt_search},
     {{opt_with_revprop, N_("retrieve revision property ARG")},
      {'c', N_("the change made in revision ARG")}} },
 
@@ -2153,6 +2166,8 @@ main(int argc, const char *argv[])
       case opt_properties_only:
         opt_state.diff.properties_only = TRUE;
         break;
+      case opt_search:
+        opt_state.search_pattern = opt_arg;
       default:
         /* Hmmm. Perhaps this would be a good place to squirrel away
            opts that commands like svn diff might need. Hmmm indeed. */

Modified: 
subversion/trunk/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout?rev=1354666&r1=1354665&r2=1354666&view=diff
==============================================================================
--- 
subversion/trunk/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout
 (original)
+++ 
subversion/trunk/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout
 Wed Jun 27 18:53:25 2012
@@ -30,6 +30,17 @@ usage: 1. log [PATH][@REV]
   The --depth option is only valid in combination with the --diff option
   and limits the scope of the displayed diff to the specified depth.
 
+  If the --search option is used, log messages are displayed only if the
+  provided search pattern matches the author, log message text, or, if
+  the --verbose option is also provided, a changed path.
+  The search pattern may include glob syntax wildcards:
+      ?      matches any single character
+      *      matches a sequence of arbitrary characters
+      [...]  matches any of the characters listed inside the brackets
+  If --limit is used in combination with --search, --limit restricts the
+  number of log messages searched, rather than restricting the output
+  to a particular number of matching log messages.
+
   Examples:
 
     Show the latest 5 log messages for the current working copy
@@ -99,6 +110,7 @@ Valid options:
                                    Ignore changes in EOL style.
                                 -p (--show-c-function):
                                    Show C function name in diff output.
+  --search ARG             : use ARG as search pattern (glob syntax)
 
 Global options:
   --username ARG           : specify a username ARG

Modified: subversion/trunk/subversion/tests/cmdline/log_tests.py
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/log_tests.py?rev=1354666&r1=1354665&r2=1354666&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/log_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/log_tests.py Wed Jun 27 18:53:25 
2012
@@ -2280,6 +2280,22 @@ def log_diff_moved(sbox):
   compare_diff_output(r2diff, log_chain[1]['diff_lines'])
   compare_diff_output(r1diff, log_chain[2]['diff_lines'])
 
+
+#----------------------------------------------------------------------
+def log_search(sbox):
+  "'svn log --search'"
+
+  guarantee_repos_and_wc(sbox)
+
+  os.chdir(sbox.wc_dir)
+
+  exit_code, output, err = svntest.actions.run_and_verify_svn(
+                             None, None, [], 'log', '--search',
+                             'for revision [367]')
+
+  log_chain = parse_log_output(output)
+  check_log_chain(log_chain, [7, 6, 3])
+
 ########################################################################
 # Run the tests
 
@@ -2323,6 +2339,7 @@ test_list = [ None,
               log_diff,
               log_xml_old,
               log_diff_moved,
+              log_search,
              ]
 
 if __name__ == '__main__':


Reply via email to