Index: subversion/svnlook/main.c
===================================================================
--- subversion/svnlook/main.c	(revision 16222)
+++ subversion/svnlook/main.c	(working copy)
@@ -54,6 +54,7 @@
   subcommand_author,
   subcommand_cat,
   subcommand_changed,
+  subcommand_copied,
   subcommand_date,
   subcommand_diff,
   subcommand_dirschanged,
@@ -150,6 +151,11 @@
         "Print the paths that were changed.\n"),
      {'r', 't'} },
     
+    {"copied", subcommand_copied, {0},
+     N_("usage: svnlook copied REPOS_PATH\n\n"
+        "Print the paths that were added via \"copy\".\n"),
+     {'r', 't'} },
+    
     {"date", subcommand_date, {0},
      N_("usage: svnlook date REPOS_PATH\n\n"
         "Print the datestamp.\n"),
@@ -539,6 +545,55 @@
 }
 
 
+/* Recursively print all nodes in the tree that have been added via "copy"
+   (do not include directories affected only by "bubble-up"). */
+static svn_error_t *
+print_copied_tree (svn_repos_node_t *node,
+                   const char *path /* UTF-8! */,
+                   apr_pool_t *pool)
+{
+  const char *full_path;
+  apr_pool_t *subpool;
+
+  SVN_ERR (check_cancel (NULL));
+
+  if (! node)
+    return SVN_NO_ERROR;
+
+  /* Print the node if it's a copy */
+  if ((node->action == 'A') && (node->copyfrom_path))
+    {
+      SVN_ERR (svn_cmdline_printf (pool, "%s%s is a copy of %s%s (rev %d)\n",
+                                   path,
+                                   node->kind == svn_node_dir ? "/" : "",
+                                   node->copyfrom_path,
+                                   node->kind == svn_node_dir ? "/" : "",
+                                   node->copyfrom_rev)
+                                   );
+    }
+  
+  /* Return here if the node has no children. */
+  node = node->child;
+  if (! node)
+    return SVN_NO_ERROR;
+
+  /* Recursively handle the node's children. */
+  subpool = svn_pool_create (pool);  
+  full_path = svn_path_join (path, node->name, subpool);
+  SVN_ERR (print_copied_tree (node, full_path, subpool));
+  while (node->sibling)
+    {
+      svn_pool_clear (subpool);
+      node = node->sibling;
+      full_path = svn_path_join (path, node->name, subpool);
+      SVN_ERR (print_copied_tree (node, full_path, subpool));
+    }
+  svn_pool_destroy (subpool);
+
+  return SVN_NO_ERROR;
+}
+
+
 /* Set *FH to a file handle for a writable binary file at PATH.
    Create the file if it doesn't exist, truncate it if it does.
    Create ancestor directories if necessary.  Allocate *FH in POOL. */
@@ -1331,6 +1386,34 @@
   return SVN_NO_ERROR;
 }
 
+/* Print a list of all paths created via "copy" */
+static svn_error_t *
+do_copied (svnlook_ctxt_t *c, apr_pool_t *pool)
+{
+  svn_fs_root_t *root;
+  svn_revnum_t base_rev_id;
+  svn_repos_node_t *tree;
+
+  SVN_ERR (get_root (&root, c, pool));
+  if (c->is_revision)
+    base_rev_id = c->rev_id - 1;
+  else
+    base_rev_id = svn_fs_txn_base_revision (c->txn);
+
+  if (! SVN_IS_VALID_REVNUM (base_rev_id))
+    return svn_error_createf 
+      (SVN_ERR_FS_NO_SUCH_REVISION, NULL,
+       _("Transaction '%s' is not based on a revision; how odd"),
+       c->txn_name);
+  
+  SVN_ERR (generate_delta_tree (&tree, c->repos, root, base_rev_id, 
+                                TRUE, pool)); 
+  if (tree)
+    SVN_ERR (print_copied_tree (tree, "", pool));
+
+  return SVN_NO_ERROR;
+}
+
 /* Create a new temporary directory with an 'svnlook' prefix. */
 static svn_error_t *
 create_unique_tmpdir (const char **name, apr_pool_t *pool)
@@ -1713,6 +1796,18 @@
 
 /* This implements `svn_opt_subcommand_t'. */
 static svn_error_t *
+subcommand_copied (apr_getopt_t *os, void *baton, apr_pool_t *pool)
+{
+  struct svnlook_opt_state *opt_state = baton;
+  svnlook_ctxt_t *c;
+
+  SVN_ERR (get_ctxt_baton (&c, opt_state, pool));
+  SVN_ERR (do_copied (c, pool));
+  return SVN_NO_ERROR;
+}
+
+/* This implements `svn_opt_subcommand_t'. */
+static svn_error_t *
 subcommand_date (apr_getopt_t *os, void *baton, apr_pool_t *pool)
 {
   struct svnlook_opt_state *opt_state = baton;

