Index: build.conf
===================================================================
--- build.conf	(revision 14265)
+++ build.conf	(working copy)
@@ -58,6 +58,7 @@
         subversion/tests/clients/cmdline/history_tests.py
         subversion/tests/clients/cmdline/lock_tests.py
         subversion/tests/clients/cmdline/cat_tests.py
+        subversion/tests/clients/cmdline/import_tests.py
 
 bdb-test-scripts =
 
Index: subversion/libsvn_client/commit.c
===================================================================
--- subversion/libsvn_client/commit.c	(revision 14265)
+++ subversion/libsvn_client/commit.c	(working copy)
@@ -46,6 +46,24 @@
 
 #include "svn_private_config.h"
 
+/* Baton to remember if changes were made to the repository */
+typedef struct 
+{
+  /* The import editor/baton. */
+  const svn_delta_editor_t *editor;
+
+  void *edit_baton;
+
+  /* Client context baton */
+  svn_client_ctx_t *ctx;
+
+  /* Paths (keys) excluded from the import (values ignored) */
+  apr_hash_t *excludes;
+
+  /* Whether any changes were made to the repository */
+  svn_boolean_t repos_changed; 
+} import_ctx_t;
+
 /* Apply PATH's contents (as a delta against the empty string) to
    FILE_BATON in EDITOR.  Use POOL for any temporary allocation.
    PROPERTIES is the set of node properties set on this file.
@@ -272,6 +290,7 @@
             const char *edit_path,
             svn_boolean_t nonrecursive,
             apr_hash_t *excludes,
+            void *import_ctx,
             svn_client_ctx_t *ctx,
             apr_pool_t *pool)
 {
@@ -279,6 +298,7 @@
   apr_hash_t *dirents;
   apr_hash_index_t *hi;
   apr_array_header_t *ignores;
+  import_ctx_t *imp_ctx = import_ctx;
 
   SVN_ERR (svn_path_check_valid (path, pool));
 
@@ -352,6 +372,8 @@
           SVN_ERR (editor->add_directory (this_edit_path, dir_baton, 
                                           NULL, SVN_INVALID_REVNUM, subpool,
                                           &this_dir_baton));
+          /* remember that the repository was modified */
+          imp_ctx->repos_changed = TRUE;
 
           /* By notifying before the recursive call below, we display
              a directory add before displaying adds underneath the
@@ -372,7 +394,7 @@
           /* Recurse. */
           SVN_ERR (import_dir (editor, this_dir_baton, 
                                this_path, this_edit_path, 
-                               FALSE, excludes, ctx, subpool));
+                               FALSE, excludes, imp_ctx, ctx, subpool));
 
           /* Finally, close the sub-directory. */
           SVN_ERR (editor->close_directory (this_dir_baton, subpool));
@@ -382,6 +404,8 @@
           /* Import a file. */
           SVN_ERR (import_file (editor, dir_baton, 
                                 this_path, this_edit_path, ctx, subpool));
+          /* remember that the repository was modified */
+          imp_ctx->repos_changed = TRUE;
         }
       /* We're silently ignoring things that aren't files or
          directories.  If we stop doing that, here is the place to
@@ -437,6 +461,7 @@
   apr_array_header_t *ignores;
   apr_array_header_t *batons = NULL;
   const char *edit_path = "";
+  import_ctx_t *imp_ctx = apr_pcalloc (pool, sizeof (*imp_ctx));
 
   /* Get a root dir baton.  We pass an invalid revnum to open_root
      to mean "base this on the youngest revision".  Should we have an
@@ -470,6 +495,8 @@
                                           root_baton,
                                           NULL, SVN_INVALID_REVNUM,
                                           pool, &root_baton));
+          /* remember that the repository was modified */
+          imp_ctx->repos_changed = TRUE;
         }
     }
   else if (kind == svn_node_file)
@@ -493,11 +520,13 @@
       if (! svn_cstring_match_glob_list (path, ignores))
         SVN_ERR (import_file (editor, root_baton, path, edit_path,
                               ctx, pool));
+      /* remember that the repository was modified */
+      imp_ctx->repos_changed = TRUE;
     }
   else if (kind == svn_node_dir)
     {
       SVN_ERR (import_dir (editor, root_baton, path, edit_path,
-                           nonrecursive, excludes, ctx, pool));
+                           nonrecursive, excludes, imp_ctx, ctx, pool));
 
     }
   else if (kind == svn_node_none)
@@ -518,7 +547,10 @@
         }
     }
 
-  SVN_ERR (editor->close_edit (edit_baton, pool));
+  if (imp_ctx->repos_changed)
+    SVN_ERR (editor->close_edit (edit_baton, pool));
+  else
+    SVN_ERR (editor->abort_edit (edit_baton, pool));
 
   return SVN_NO_ERROR;
 }
Index: subversion/tests/clients/cmdline/import_tests.py
===================================================================
--- subversion/tests/clients/cmdline/import_tests.py	(revision 0)
+++ subversion/tests/clients/cmdline/import_tests.py	(revision 0)
@@ -0,0 +1,235 @@
+#!/usr/bin/env python
+#
+#  import_tests.py:  import tests
+#
+#  Subversion is a tool for revision control. 
+#  See http://subversion.tigris.org for more information.
+#    
+# ====================================================================
+# Copyright (c) 2000-2004 CollabNet.  All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution.  The terms
+# are also available at http://subversion.tigris.org/license-1.html.
+# If newer versions of this license are posted there, you may use a
+# newer version instead, at your option.
+#
+######################################################################
+
+# General modules
+import string, sys, re, os.path
+
+# Our testing module
+import svntest
+from svntest import wc, SVNAnyOutput
+
+# (abbreviation)
+Skip = svntest.testcase.Skip
+XFail = svntest.testcase.XFail
+Item = wc.StateItem
+
+######################################################################
+# Tests
+#
+#   Each test must return on success or raise on failure.
+
+#----------------------------------------------------------------------
+# this test should be SKIPped on systems without the executable bit
+def import_executable(sbox):
+  " import of executable files"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  # create a new directory with files of various permissions
+  xt_path = os.path.join(wc_dir, "XT")
+  os.makedirs(xt_path)
+  all_path = os.path.join(wc_dir, "XT/all_exe")
+  none_path = os.path.join(wc_dir, "XT/none_exe")
+  user_path = os.path.join(wc_dir, "XT/user_exe")
+  group_path = os.path.join(wc_dir, "XT/group_exe")
+  other_path = os.path.join(wc_dir, "XT/other_exe")
+
+  for path in [all_path, none_path, user_path, group_path, other_path]:
+    svntest.main.file_append(path, "some text")
+
+  # set executable bits
+  os.chmod(all_path, 0777)
+  os.chmod(none_path, 0666)
+  os.chmod(user_path, 0766)
+  os.chmod(group_path, 0676)
+  os.chmod(other_path, 0667)
+
+  # import new files into repository
+  url = svntest.main.current_repo_url
+  output, errput =   svntest.actions.run_and_verify_svn(
+    None, None, [], 'import',
+    '--username', svntest.main.wc_author,
+    '--password', svntest.main.wc_passwd,
+    '-m', 'Log message for new import', xt_path, url)
+
+  lastline = string.strip(output.pop())
+  cm = re.compile ("(Committed|Imported) revision [0-9]+.")
+  match = cm.search (lastline)
+  if not match:
+    ### we should raise a less generic error here. which?
+    raise svntest.Failure
+
+  # remove (uncontrolled) local files
+  svntest.main.safe_rmtree(xt_path)
+
+  # Create expected disk tree for the update (disregarding props)
+  expected_disk = svntest.main.greek_state.copy()
+  expected_disk.add({
+    'all_exe' :   Item('some text', props={'svn:executable' : ''}),
+    'none_exe' :  Item('some text'),
+    'user_exe' :  Item('some text', props={'svn:executable' : ''}),
+    'group_exe' : Item('some text'),
+    'other_exe' : Item('some text'),
+    })
+
+  # Create expected status tree for the update (disregarding props).
+  # Newly imported file should be at revision 2.
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
+  expected_status.add({
+    'all_exe' : Item(status='  ', wc_rev=2),
+    'none_exe' : Item(status='  ', wc_rev=2),
+    'user_exe' : Item(status='  ', wc_rev=2),
+    'group_exe' : Item(status='  ', wc_rev=2),
+    'other_exe' : Item(status='  ', wc_rev=2),
+    })
+
+  # Create expected output tree for the update.
+  expected_output = svntest.wc.State(wc_dir, {
+    'all_exe' : Item(status='A '),
+    'none_exe' : Item(status='A '),
+    'user_exe' : Item(status='A '),
+    'group_exe' : Item(status='A '),
+    'other_exe' : Item(status='A '),
+  })
+  # do update and check three ways
+  svntest.actions.run_and_verify_update(wc_dir,
+                                        expected_output,
+                                        expected_disk,
+                                        expected_status,
+                                        None, None, None,
+                                        None, None, 1)
+
+#----------------------------------------------------------------------
+def import_ignores(sbox):
+  'do not import ignored files in imported dirs'
+
+  # The bug was that
+  #
+  #   $ svn import dir
+  #
+  # where dir contains some items that match the ignore list and some
+  # do not would add all items, ignored or not.
+  #
+  # This has been fixed by testing each item with the new
+  # svn_wc_is_ignored function.
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  dir_path = os.path.join(wc_dir, 'dir')
+  foo_c_path = os.path.join(dir_path, 'foo.c')
+  foo_o_path = os.path.join(dir_path, 'foo.o')
+
+  os.mkdir(dir_path, 0755)
+  open(foo_c_path, 'w')
+  open(foo_o_path, 'w')
+
+  # import new dir into repository
+  url = svntest.main.current_repo_url + '/dir'
+
+  output, errput = svntest.actions.run_and_verify_svn(
+    None, None, [], 'import',
+    '--username', svntest.main.wc_author,
+    '--password', svntest.main.wc_passwd,
+    '-m', 'Log message for new import',
+    dir_path, url)
+
+  lastline = string.strip(output.pop())
+  cm = re.compile ("(Committed|Imported) revision [0-9]+.")
+  match = cm.search (lastline)
+  if not match:
+    ### we should raise a less generic error here. which?
+    raise svntest.actions.SVNUnexpectedOutput
+
+  # remove (uncontrolled) local dir
+  svntest.main.safe_rmtree(dir_path)
+
+  # Create expected disk tree for the update (disregarding props)
+  expected_disk = svntest.main.greek_state.copy()
+  expected_disk.add({
+    'dir/foo.c' : Item(''),
+    })
+
+  # Create expected status tree for the update (disregarding props).
+  # Newly imported file should be at revision 2.
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
+  expected_status.add({
+    'dir' : Item(status='  ', wc_rev=2),
+    'dir/foo.c' : Item(status='  ', wc_rev=2),
+    })
+
+  # Create expected output tree for the update.
+  expected_output = svntest.wc.State(wc_dir, {
+    'dir' : Item(status='A '),
+    'dir/foo.c' : Item(status='A '),
+  })
+
+  # do update and check three ways
+  svntest.actions.run_and_verify_update(wc_dir,
+                                        expected_output,
+                                        expected_disk,
+                                        expected_status,
+                                        None, None, None,
+                                        None, None, 1)
+
+#----------------------------------------------------------------------
+def import_avoid_empty_revision(sbox):
+  "avoid creating revisions with no path changes"
+  
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  # create a new directory 
+  empty_dir = os.path.join(wc_dir, "empty_dir")
+  os.makedirs(empty_dir)
+
+  url = svntest.main.current_repo_url  
+  svntest.actions.run_and_verify_svn(None, None, None, 'import',
+                                     '--username', svntest.main.wc_author,
+                                     '--password', svntest.main.wc_passwd,
+                                     '-m', 'Log message for new import', 
+                                     empty_dir, url)
+
+  svntest.main.safe_rmtree(empty_dir) 
+
+  # Verify that an empty revision has not been created
+  svntest.actions.run_and_verify_svn(None, [ "At revision 1.\n"], 
+                                     None, "update", 
+                                     '--username', svntest.main.wc_author,
+                                     '--password', svntest.main.wc_passwd,
+                                     empty_dir) 
+
+#----------------------------------------------------------------------
+########################################################################
+# Run the tests
+
+
+# list all tests here, starting with None:
+test_list = [ None,
+              Skip(import_executable, (os.name != 'posix')),
+              import_ignores,
+              import_avoid_empty_revision,
+             ]
+
+if __name__ == '__main__':
+  svntest.main.run_tests(test_list)
+  # NOTREACHED
+
+
+### End of file.

Property changes on: subversion/tests/clients/cmdline/import_tests.py
___________________________________________________________________
Name: svn:executable
   + *

Index: subversion/tests/clients/cmdline/basic_tests.py
===================================================================
--- subversion/tests/clients/cmdline/basic_tests.py	(revision 14265)
+++ subversion/tests/clients/cmdline/basic_tests.py	(working copy)
@@ -1252,90 +1252,6 @@
 
 #----------------------------------------------------------------------
 
-# this test should be SKIPped on systems without the executable bit
-def basic_import_executable(sbox):
-  "basic import of executable files"
-
-  sbox.build()
-  wc_dir = sbox.wc_dir
-
-  # create a new directory with files of various permissions
-  xt_path = os.path.join(wc_dir, "XT")
-  os.makedirs(xt_path)
-  all_path = os.path.join(wc_dir, "XT/all_exe")
-  none_path = os.path.join(wc_dir, "XT/none_exe")
-  user_path = os.path.join(wc_dir, "XT/user_exe")
-  group_path = os.path.join(wc_dir, "XT/group_exe")
-  other_path = os.path.join(wc_dir, "XT/other_exe")
-
-  for path in [all_path, none_path, user_path, group_path, other_path]:
-    svntest.main.file_append(path, "some text")
-
-  # set executable bits
-  os.chmod(all_path, 0777)
-  os.chmod(none_path, 0666)
-  os.chmod(user_path, 0766)
-  os.chmod(group_path, 0676)
-  os.chmod(other_path, 0667)
-
-  # import new files into repository
-  url = svntest.main.current_repo_url
-  output, errput =   svntest.actions.run_and_verify_svn(
-    None, None, [], 'import',
-    '--username', svntest.main.wc_author,
-    '--password', svntest.main.wc_passwd,
-    '-m', 'Log message for new import', xt_path, url)
-
-  lastline = string.strip(output.pop())
-  cm = re.compile ("(Committed|Imported) revision [0-9]+.")
-  match = cm.search (lastline)
-  if not match:
-    ### we should raise a less generic error here. which?
-    raise svntest.Failure
-
-  # remove (uncontrolled) local files
-  svntest.main.safe_rmtree(xt_path)
-
-  # Create expected disk tree for the update (disregarding props)
-  expected_disk = svntest.main.greek_state.copy()
-  expected_disk.add({
-    'all_exe' :   Item('some text', props={'svn:executable' : ''}),
-    'none_exe' :  Item('some text'),
-    'user_exe' :  Item('some text', props={'svn:executable' : ''}),
-    'group_exe' : Item('some text'),
-    'other_exe' : Item('some text'),
-    })
-
-  # Create expected status tree for the update (disregarding props).
-  # Newly imported file should be at revision 2.
-  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
-  expected_status.add({
-    'all_exe' : Item(status='  ', wc_rev=2),
-    'none_exe' : Item(status='  ', wc_rev=2),
-    'user_exe' : Item(status='  ', wc_rev=2),
-    'group_exe' : Item(status='  ', wc_rev=2),
-    'other_exe' : Item(status='  ', wc_rev=2),
-    })
-
-  # Create expected output tree for the update.
-  expected_output = svntest.wc.State(wc_dir, {
-    'all_exe' : Item(status='A '),
-    'none_exe' : Item(status='A '),
-    'user_exe' : Item(status='A '),
-    'group_exe' : Item(status='A '),
-    'other_exe' : Item(status='A '),
-  })
-  # do update and check three ways
-  svntest.actions.run_and_verify_update(wc_dir,
-                                        expected_output,
-                                        expected_disk,
-                                        expected_status,
-                                        None, None, None,
-                                        None, None, 1)
-
-
-#----------------------------------------------------------------------
-
 def basic_cat(sbox):
   "basic cat of files"
 
@@ -1524,79 +1440,6 @@
 
 
 #----------------------------------------------------------------------
-def basic_import_ignores(sbox):
-  'do not import ignored files in imported dirs'
-
-  # The bug was that
-  #
-  #   $ svn import dir
-  #
-  # where dir contains some items that match the ignore list and some
-  # do not would add all items, ignored or not.
-  #
-  # This has been fixed by testing each item with the new
-  # svn_wc_is_ignored function.
-
-  sbox.build()
-  wc_dir = sbox.wc_dir
-
-  dir_path = os.path.join(wc_dir, 'dir')
-  foo_c_path = os.path.join(dir_path, 'foo.c')
-  foo_o_path = os.path.join(dir_path, 'foo.o')
-
-  os.mkdir(dir_path, 0755)
-  open(foo_c_path, 'w')
-  open(foo_o_path, 'w')
-
-  # import new dir into repository
-  url = svntest.main.current_repo_url + '/dir'
-
-  output, errput = svntest.actions.run_and_verify_svn(
-    None, None, [], 'import',
-    '--username', svntest.main.wc_author,
-    '--password', svntest.main.wc_passwd,
-    '-m', 'Log message for new import',
-    dir_path, url)
-
-  lastline = string.strip(output.pop())
-  cm = re.compile ("(Committed|Imported) revision [0-9]+.")
-  match = cm.search (lastline)
-  if not match:
-    ### we should raise a less generic error here. which?
-    raise svntest.actions.SVNUnexpectedOutput
-
-  # remove (uncontrolled) local dir
-  svntest.main.safe_rmtree(dir_path)
-
-  # Create expected disk tree for the update (disregarding props)
-  expected_disk = svntest.main.greek_state.copy()
-  expected_disk.add({
-    'dir/foo.c' : Item(''),
-    })
-
-  # Create expected status tree for the update (disregarding props).
-  # Newly imported file should be at revision 2.
-  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
-  expected_status.add({
-    'dir' : Item(status='  ', wc_rev=2),
-    'dir/foo.c' : Item(status='  ', wc_rev=2),
-    })
-
-  # Create expected output tree for the update.
-  expected_output = svntest.wc.State(wc_dir, {
-    'dir' : Item(status='A '),
-    'dir/foo.c' : Item(status='A '),
-  })
-
-  # do update and check three ways
-  svntest.actions.run_and_verify_update(wc_dir,
-                                        expected_output,
-                                        expected_disk,
-                                        expected_status,
-                                        None, None, None,
-                                        None, None, 1)
-
-#----------------------------------------------------------------------
 def uri_syntax(sbox):
   'make sure URI syntaxes are parsed correctly'
 
@@ -1632,7 +1475,7 @@
   else:
     raise svntest.Failure
 
-
+#----------------------------------------------------------------------
 ########################################################################
 # Run the tests
 
@@ -1656,11 +1499,9 @@
               basic_import,
               basic_cat,
               basic_ls,
-              Skip(basic_import_executable, (os.name != 'posix')),
               nonexistent_repository,
               basic_auth_cache,
               basic_add_ignores,
-              basic_import_ignores,
               uri_syntax,
               basic_checkout_file,
               ### todo: more tests needed:


