Index: subversion/include/svn_subst.h
===================================================================
--- subversion/include/svn_subst.h	(revision 13318)
+++ subversion/include/svn_subst.h	(working copy)
@@ -84,6 +84,11 @@
   const svn_string_t *url;
   const svn_string_t *id;
 } svn_subst_keywords_t;
+/** NOTE : The static function svn_canonicalize_keywords() in
+ * props.c contains references to the SVN_KEYWORD_... macros.
+ * This function has to be updated too in case a new keyword
+ * is defined.
+ */ 
 
 
 /** Fill in an <tt>svn_subst_keywords_t *</tt> @a kw with the appropriate 
Index: subversion/libsvn_wc/props.c
===================================================================
--- subversion/libsvn_wc/props.c	(revision 13317)
+++ subversion/libsvn_wc/props.c	(working copy)
@@ -987,6 +987,82 @@
 }
 
 
+/** Return in an <tt>svn_stringbuf_t *</tt> with the 
+ * canonicalized equivalent of  @a value
+ * Repeated instances of the same  keyword are ignored.
+ * 
+ * All memory is allocated out of @a pool.
+ */
+static svn_stringbuf_t *
+svn_canonicalize_keywords (const svn_string_t *value,
+                                 apr_pool_t *pool)
+{
+  /* Structure to be used for canonicalization of svn:keywords */
+  typedef struct canon_table_s
+  {
+    const char *prop_value; /* the property value */
+    const int index; /* the index to the corresponding canonical form */
+    const int flag;  /* bit pattern to flag that this canonical form 
+                     ** is already set */
+  } canon_table_t;
+  static canon_table_t canon_kw_table[] = 
+    {
+       { SVN_KEYWORD_REVISION_LONG,   2, 0x0001 },
+       { SVN_KEYWORD_REVISION_SHORT,  2, 0x0001 },
+       { SVN_KEYWORD_REVISION_MEDIUM, 2, 0x0001 },
+       { SVN_KEYWORD_DATE_LONG,       4, 0x0002 },
+       { SVN_KEYWORD_DATE_SHORT,      4, 0x0002 },
+       { SVN_KEYWORD_AUTHOR_LONG,     6, 0x0004 },
+       { SVN_KEYWORD_AUTHOR_SHORT,    6, 0x0004 },
+       { SVN_KEYWORD_URL_SHORT,       7, 0x0008 },
+       { SVN_KEYWORD_URL_LONG,        7, 0x0008 },
+       { SVN_KEYWORD_ID,              9, 0x0010 }
+    };
+  const int sizeof_canon_kw_table = 
+                sizeof(canon_kw_table)/sizeof(canon_table_t) ;
+
+  apr_array_header_t *keyword_tokens;
+  svn_stringbuf_t *canonicalized_value = NULL ;
+  int flags = 0 ;
+  int i, j;
+
+  /* tokenize the input */
+  keyword_tokens = svn_cstring_split (value->data, " \t\v\n\b\r\f",
+                                      TRUE /* chop */, pool);
+
+  /* for all the tokens */
+  for (i = 0; i < keyword_tokens->nelts; ++i)
+    {
+      const char *keyword = APR_ARRAY_IDX (keyword_tokens, i, const char *);
+
+      for (j = 0; j < sizeof_canon_kw_table; j++)
+        {
+          /* see if a equivalent standard form exists */
+          if ((! strcasecmp (canon_kw_table[j].prop_value, keyword))
+              && (! (flags & canon_kw_table[j].flag )))
+            {
+              /* If so, canonicalize and prepare output */
+              if (!canonicalized_value)
+                canonicalized_value = 
+                  svn_stringbuf_create (
+                    canon_kw_table[canon_kw_table[j].index].prop_value, 
+                    pool);
+              else
+                {
+                    svn_stringbuf_appendcstr (canonicalized_value, " ");
+                    svn_stringbuf_appendcstr (
+                      canonicalized_value, 
+                      canon_kw_table[canon_kw_table[j].index].prop_value);
+                }
+              flags |=  canon_kw_table[j].flag ;
+              break; /* goto the next token from the input */
+            }
+        }
+    }
+
+  return canonicalized_value ;
+}
+
 svn_error_t *
 svn_wc_prop_set2 (const char *name,
                   const svn_string_t *value,
@@ -1061,7 +1137,7 @@
         }
       else if (strcmp (name, SVN_PROP_KEYWORDS) == 0)
         {
-          new_value = svn_stringbuf_create_from_string (value, pool);
+          new_value = svn_canonicalize_keywords (value, pool);
           svn_stringbuf_strip_whitespace (new_value);
         }
     }
Index: subversion/tests/clients/cmdline/prop_tests.py
===================================================================
--- subversion/tests/clients/cmdline/prop_tests.py	(revision 13318)
+++ subversion/tests/clients/cmdline/prop_tests.py	(working copy)
@@ -857,8 +857,8 @@
              ['foo http://foo.com/repos'+os.linesep])
 
   # Check svn:keywords
-  check_prop('svn:keywords', iota_path, ['Rev Date'])
-  check_prop('svn:keywords', mu_path, ['Rev  Date'])
+  check_prop('svn:keywords', iota_path, ['Revision Date'])
+  check_prop('svn:keywords', mu_path, ['Revision Date'])
 
   # Check svn:executable
   check_prop('svn:executable', iota_path, ['*'])
Index: subversion/tests/clients/cmdline/trans_tests.py
===================================================================
--- subversion/tests/clients/cmdline/trans_tests.py	(revision 13318)
+++ subversion/tests/clients/cmdline/trans_tests.py	(working copy)
@@ -714,7 +714,184 @@
   expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
   svntest.actions.run_and_verify_status(wc_dir, expected_status)
 
+ 
+#----------------------------------------------------------------------
+#      Testing for canonicalized input to svn:keywords
+#      Propset a case-different keyword and check if 
+#      expansion works
+def canonicalize_keywords_prop(sbox):
+  "test canonocalization of the svn:keywords input"
+
+  given_input =     [
+                     "lastchangedrevision",
+                     "lastcHANgedREviSIoN",
+                     "revision",
+                     "rev",
+                     "lastchangedby",
+                     "author",
+                     "aUtHoR",
+                     "headurl",
+                     "url",
+                     "lasTChangEDdate",
+                     "date",
+                     "DaTe",
+                     "id",
+                     "lastchangedrevision author LastChangedBy"
+                    ]
+  expected_output = [
+                     ["Revision\n"],
+                     ["Revision\n"],
+                     ["Revision\n"],
+                     ["Revision\n"],
+                     ["Author\n"],
+                     ["Author\n"],
+                     ["Author\n"],
+                     ["URL\n"],
+                     ["URL\n"],
+                     ["Date\n"],
+                     ["Date\n"],
+                     ["Date\n"],
+                     ["Id\n"],
+                     ["Revision Author\n"]
+                    ]
+			
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  iota_path = os.path.join(wc_dir, 'iota')
   
+  for ctr in range(len(expected_output)):
+    svntest.actions.run_and_verify_svn(None, None, [], 'propset',
+                                       "svn:keywords", 
+                                       given_input[ctr],
+                                       iota_path)
+    svntest.actions.run_and_verify_svn(None,  expected_output[ctr], [], 'propget',
+                                       "svn:keywords", 
+                                       iota_path)
+
+
+#----------------------------------------------------------------------
+#      Testing for expansion of keywords in the file
+#      in addition to canonicalizable input to svn:keywords
+#      Propset a case-different keyword and check if 
+#      expansion works
+def canonicalized_and_keywords_expanded(sbox):
+  "test keyword expansion after canonicalization"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  Z_path = os.path.join(wc_dir, 'Z')
+  svntest.actions.run_and_verify_svn (None, None, [], 'mkdir', Z_path)
+  
+  # Add the file that has the keyword to be expanded
+  url_path = os.path.join(Z_path, 'url')
+  svntest.main.file_append (url_path, """This is the file : url.
+
+LastChangedRevision:$LastChangedRevision$
+lastchangedrevision:$lastchangedrevision$
+Revision:$Revision$
+revision:$revision$
+Rev:$Rev$
+rev:$rev$
+
+LastChangedDate:$LastChangedDate$
+lastchangeddate:$lastchangeddate$
+Date:$Date$
+date:$date$
+
+LastChangedBy:$LastChangedBy$
+lastchangedby:$lastchangedby$
+Author:$Author$
+author:$author$
+
+HeadURL:$HeadURL$
+headurl:$headurl$
+URL:$URL$
+url:$url$
+""")
+  svntest.actions.run_and_verify_svn (None, None, [], 'add', url_path)
+  svntest.actions.run_and_verify_svn(None, None, [], 'propset',
+                                     "svn:keywords", 
+                                     "lastchangedrevision lastchangeddate lastchangedby headurl",
+                                     url_path)
+
+
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'ci', '-m', 'log msg', wc_dir)
+
+  # Check keyword got expanded (and thus the mkdir, add, ps, commit
+  # etc. worked)
+  fp = open(url_path, 'r')
+  lines = fp.readlines()
+  if (len(lines) == 23):
+    # LastChangedRevision
+    if(re.match("LastChangedRevision:\$LastChangedRevision\$", lines[2])):
+     print "LastChangedRevision expansion failed for", url_path
+     raise svntest.Failure
+    if not (re.match("lastchangedrevision:\$lastchangedrevision\$", lines[3])):
+     print "lastchangedrevision expansion failed for", url_path
+     raise svntest.Failure
+    #Revision
+    if(re.match("Revision:\$Revision\$", lines[4])):
+     print "Revision expansion failed for", url_path
+     raise svntest.Failure
+    if not (re.match("revision:\$revision\$", lines[5])):
+     print "revision expansion failed for", url_path
+     raise svntest.Failure
+    #Rev
+    if(re.match("Rev:\$Rev\$", lines[6])):
+     print "Rev expansion failed for", url_path
+     raise svntest.Failure
+    if not (re.match("rev:\$rev\$", lines[7])):
+     print "rev expansion failed for", url_path
+     raise svntest.Failure
+    #LastChangedDate
+    if(re.match("LastChangedDate:\$LastChangedDate\$", lines[9])):
+     print "LastChangedDate expansion failed for", url_path
+     raise svntest.Failure
+    if not (re.match("lastchangeddate:\$lastchangeddate\$", lines[10])):
+     print "lastchangeddate expansion failed for", url_path
+     raise svntest.Failure
+    #Date
+    if(re.match("Date:\$Date\$", lines[11])):
+     print "Date expansion failed for", url_path
+     raise svntest.Failure
+    if not (re.match("date:\$date\$", lines[12])):
+     print "date expansion failed for", url_path
+     raise svntest.Failure
+    #LastChangedBy
+    if(re.match("LastChangedBy:\$LastChangedBy\$", lines[14])):
+     print "LastChangedBy expansion failed for", url_path
+     raise svntest.Failure
+    if not (re.match("lastchangedby:\$lastchangedby\$", lines[15])):
+     print "lastchangedby expansion failed for", url_path
+     raise svntest.Failure
+    #Author
+    if(re.match("Author:\$Author\$", lines[16])):
+     print "Author expansion failed for", url_path
+     raise svntest.Failure
+    if not (re.match("author:\$author\$", lines[17])):
+     print "author expansion failed for", url_path
+     raise svntest.Failure
+    #HeadURL
+    if not (re.match("HeadURL:\$HeadURL: (http|file|svn|svn\\+ssh)://", lines[19])):
+     print "HeadURL expansion failed for", url_path
+     raise svntest.Failure
+    if not (re.match("headurl:\$headurl\$", lines[20])):
+     print "headurl expansion failed for", url_path
+     raise svntest.Failure
+    if not (re.match("URL:\$URL: (http|file|svn|svn\\+ssh)://", lines[21])):
+     print "URL expansion failed for", url_path
+     raise svntest.Failure
+    if not (re.match("url:\$url\$", lines[22])):
+     print "url expansion failed for", url_path
+     raise svntest.Failure
+  else:
+    print "File is in an inconsistent state"
+    raise svntest.Failure
+  fp.close()
+
 ########################################################################
 # Run the tests
 
@@ -732,6 +909,8 @@
               copy_propset_commit,
               propset_commit_checkout_nocrash,
               propset_revert_noerror, 
+              canonicalize_keywords_prop, 
+              canonicalized_and_keywords_expanded,
              ]
 
 if __name__ == '__main__':

