3 # ====================================================================
7 # This script allows for server-side tweaks of the svn:author property
8 # in two modes: single-revision and search-and-replace. Both modes
9 # bypass the repository hook subsystem.
11 # ====================================================================
12 # Copyright (c) 2007 CollabNet. All rights reserved.
14 # This software is licensed as described in the file COPYING, which
15 # you should have received as part of this distribution. The terms
16 # are also available at http://subversion.tigris.org/license-1.html.
17 # If newer versions of this license are posted there, you may use a
18 # newer version instead, at your option.
20 # This software consists of voluntary contributions made by many
21 # individuals. For exact contribution history, see the revision
22 # history and logs, available at http://subversion.tigris.org/.
23 # ====================================================================
27 from svn
import repos
, fs
, core
29 def error_and_exit(errmsg
):
30 """Print ERRMSG as an error, and exit with a non-zero error code."""
31 sys
.stderr
.write("\nERROR: %s\n" % (errmsg
))
34 def usage_and_exit(errmsg
=None):
35 """Print the usage message, to stderr if ERRMSG is provided, to
36 stdout otherwise. If ERRMSG is provided, print it as an error,
38 cmd
= os
.path
.basename(sys
.argv
[0])
39 stream
= errmsg
and sys
.stderr
or sys
.stdout
40 stream
.write("""Usage: 1. %s REPOS-PATH replace OLDAUTHOR [NEWAUTHOR]
41 2. %s REPOS-PATH revision REV [NEWAUTHOR]
43 Change the svn:author property for one or more revisions of the
44 repository located at REPOS-PATH. If in "replace" mode, any instance
45 of an author named OLDAUTHOR is changed to NEWAUTHOR. If in "revision"
46 mode, simply change the author of the single revision REV to NEWAUTHOR.
47 In either mode, if NEWAUTHOR is not provided, the existing author will
50 WARNING: Changing revision properties is not a versioned event, and
51 this script will bypass the repository's hook subsystem (so
52 you won't see notification emails and such from any changes
53 made with this script).
56 error_and_exit(errmsg
)
60 def fetch_rev_author(fs_obj
, revision
):
61 """Return the value of the svn:author property for REVISION in
62 repository filesystem FS_OBJ."""
63 return fs
.svn_fs_revision_prop(fs_obj
, revision
,
64 core
.SVN_PROP_REVISION_AUTHOR
)
66 def tweak_rev_author(fs_obj
, revision
, author
):
67 """Change the value of the svn:author property for REVISION in
68 repository filesystem FS_OBJ in AUTHOR."""
70 print "Deleting author for revision %d..." % (revision
),
72 print "Tweaking author for revision %d..." % (revision
),
74 fs
.svn_fs_change_rev_prop(fs_obj
, revision
,
75 core
.SVN_PROP_REVISION_AUTHOR
, author
)
81 def get_fs_obj(repos_path
):
82 """Return a repository filesystem object for the repository
83 located at REPOS_PATH (which must obey the Subversion path
84 canonicalization rules)."""
85 return repos
.svn_repos_fs(repos
.svn_repos_open(repos_path
))
89 if argc
< 4 or argc
> 5:
90 usage_and_exit("Not enough arguments provided.")
92 repos_path
= core
.svn_path_canonicalize(sys
.argv
[1])
93 except AttributeError:
94 repos_path
= os
.path
.normpath(sys
.argv
[1])
95 if repos_path
[-1] == '/' and len(repos_path
) > 1:
96 repos_path
= repos_path
[:-1]
103 if mode
== "replace":
104 old_author
= sys
.argv
[3]
105 fs_obj
= get_fs_obj(repos_path
)
106 for revision
in range(fs
.svn_fs_youngest_rev(fs_obj
) + 1):
107 if fetch_rev_author(fs_obj
, revision
) == old_author
:
108 tweak_rev_author(fs_obj
, revision
, author
)
109 elif mode
== "revision":
111 revision
= int(sys
.argv
[3])
113 usage_and_exit("Invalid revision number (%s) provided."
115 tweak_rev_author(get_fs_obj(repos_path
), revision
, author
)
117 usage_and_exit("Invalid mode (%s) provided." % (mode
))
121 error_and_exit(str(e
))
123 if __name__
== "__main__":