From b25f76a20a87afd1127da0c64825a27afbfda57a Mon Sep 17 00:00:00 2001 From: Tony Duckles Date: Mon, 20 Aug 2012 23:05:51 -0500 Subject: [PATCH] Support before-commit hook option (--pre-commit) Allow the caller to optionally run a shell script before each replayed commit. This could be useful if you wanted to modify the replayed file/directory contents, e.g. strip-out a certain file at a certain source revision, etc. * svn2svn/run/svnreplay.py (commit_from_svn_log_entry): Support beforecommit hook. * tests/t0103*: Add unit-tests for new --pre-commit option. * svn2svn/run/svnreplay.py (main): Minor tweaks to command-line argument help-text. * svn2svn/shell.py (_run_raw_command): Fix typo --- README.mkd | 75 ++++++++++++++++-------------- svn2svn/run/svnreplay.py | 56 ++++++++++++---------- svn2svn/shell.py | 2 +- tests/t0103-replay-beforecommit.sh | 35 ++++++++++++++ tests/t0103/before-commit.sh | 10 ++++ 5 files changed, 118 insertions(+), 60 deletions(-) create mode 100755 tests/t0103-replay-beforecommit.sh create mode 100755 tests/t0103/before-commit.sh diff --git a/README.mkd b/README.mkd index 67b39ac..a81fb2c 100644 --- a/README.mkd +++ b/README.mkd @@ -90,41 +90,46 @@ See `svnreplay.py --help`: logical ancestry where possible. Options: - --version show program's version number and exit - -h, --help show this help message and exit - -v, --verbose enable additional output (use -vv or -vvv for more) - -a, --archive archive/mirror mode; same as -UDP (see REQUIRE's below) - maintain same commit author, same commit time, and - file/dir properties - -U, --keep-author maintain same commit authors (svn:author) as source - (REQUIRES 'pre-revprop-change' hook script to allow - 'svn:author' changes) - -D, --keep-date maintain same commit time (svn:date) as source - (REQUIRES 'pre-revprop-change' hook script to allow - 'svn:date' changes) - -P, --keep-prop maintain same file/dir SVN properties as source - -R, --keep-revnum maintain same rev #'s as source. creates placeholder - target revisions (by modifying a 'svn2svn:keep-revnum' - property at the root of the target repo) - -c, --continue continue from last source commit to target (based on - svn2svn:* revprops) - -f, --force allow replaying into a non-empty target folder - -r, --revision=ARG revision range to replay from source_url - A revision argument can be one of: - START start rev # (end will be 'HEAD') - START:END start and ending rev #'s - Any revision # formats which SVN understands are - supported, e.g. 'HEAD', '{2010-01-31}', etc. - -u, --log-author append source commit author to replayed commit mesages - -d, --log-date append source commit time to replayed commit messages - -l, --limit=NUM maximum number of source revisions to process - -n, --dry-run process next source revision but don't commit changes to - target working-copy (forces --limit=1) - -x, --verify verify ancestry and content for changed paths in commit - after every target commit or last target commit - -X, --verify-all verify ancestry and content for entire target_url tree - after every target commit or last target commit - --debug enable debugging output (same as -vvv) + --version show program's version number and exit + -h, --help show this help message and exit + -v, --verbose Enable additional output (use -vv or -vvv for more). + -a, --archive Archive/mirror mode; same as -UDP (see REQUIRES + below). + Maintain same commit author, same commit time, and + file/dir properties. + -U, --keep-author Maintain same commit authors (svn:author) as source. + (REQUIRES 'pre-revprop-change' hook script to allow + 'svn:author' changes.) + -D, --keep-date Maintain same commit time (svn:date) as source. + (REQUIRES 'pre-revprop-change' hook script to allow + 'svn:date' changes.) + -P, --keep-prop Maintain same file/dir SVN properties as source. + -R, --keep-revnum Maintain same rev #'s as source. creates placeholder + target revisions (by modifying a 'svn2svn:keep-revnum' + property at the root of the target repo). + -c, --continue Continue from last source commit to target (based on + svn2svn:* revprops). + -f, --force Allow replaying into a non-empty target folder. + -r, --revision=ARG Revision range to replay from source_url. + A revision argument can be one of: + START Start rev # (end will be 'HEAD') + START:END Start and ending rev #'s + Any revision # formats which SVN understands are + supported, e.g. 'HEAD', '{2010-01-31}', etc. + -u, --log-author Append source commit author to replayed commit + mesages. + -d, --log-date Append source commit time to replayed commit messages. + -l, --limit=NUM Maximum number of source revisions to process. + -n, --dry-run Process next source revision but don't commit changes + to target working-copy (forces --limit=1). + -x, --verify Verify ancestry and content for changed paths in + commit after every target commit or last target + commit. + -X, --verify-all Verify ancestry and content for entire target_url tree + after every target commit or last target commit. + --pre-commit=CMD Run the given shell script before each replayed + commit, e.g. to modify file-content during replay. + --debug Enable debugging output (same as -vvv). Side Effects ------------ diff --git a/svn2svn/run/svnreplay.py b/svn2svn/run/svnreplay.py index fd69c87..37383dd 100644 --- a/svn2svn/run/svnreplay.py +++ b/svn2svn/run/svnreplay.py @@ -49,7 +49,12 @@ def commit_from_svn_log_entry(log_entry, commit_paths=None, target_revprops=None """ Given an SVN log entry and an optional list of changed paths, do an svn commit. """ - # TODO: Run optional external shell hook here, for doing pre-commit filtering + if options.beforecommit: + # Run optional external shell hook here, for doing pre-commit filtering + # $1 = Path to working copy + # $2 = Source revision # + args = [os.getcwd(), log_entry['revision']] + run_shell_command(options.beforecommit, args=args) # Display the _wc_target "svn status" info if running in -vv (or higher) mode if ui.get_level() >= ui.EXTRA: ui.status(">> commit_from_svn_log_entry: Pre-commit _wc_target status:", level=ui.EXTRA, color='CYAN') @@ -1025,47 +1030,50 @@ Examples: parser = optparse.OptionParser(usage, description=description, formatter=HelpFormatter(), version="%prog "+str(full_version)) parser.add_option("-v", "--verbose", dest="verbosity", action="count", default=1, - help="enable additional output (use -vv or -vvv for more)") + help="Enable additional output (use -vv or -vvv for more).") parser.add_option("-a", "--archive", action="store_true", dest="archive", default=False, - help="archive/mirror mode; same as -UDP (see REQUIRE's below)\n" - "maintain same commit author, same commit time, and file/dir properties") + help="Archive/mirror mode; same as -UDP (see REQUIRES below).\n" + "Maintain same commit author, same commit time, and file/dir properties.") parser.add_option("-U", "--keep-author", action="store_true", dest="keep_author", default=False, - help="maintain same commit authors (svn:author) as source\n" - "(REQUIRES 'pre-revprop-change' hook script to allow 'svn:author' changes)") + help="Maintain same commit authors (svn:author) as source.\n" + "(REQUIRES 'pre-revprop-change' hook script to allow 'svn:author' changes.)") parser.add_option("-D", "--keep-date", action="store_true", dest="keep_date", default=False, - help="maintain same commit time (svn:date) as source\n" - "(REQUIRES 'pre-revprop-change' hook script to allow 'svn:date' changes)") + help="Maintain same commit time (svn:date) as source.\n" + "(REQUIRES 'pre-revprop-change' hook script to allow 'svn:date' changes.)") parser.add_option("-P", "--keep-prop", action="store_true", dest="keep_prop", default=False, - help="maintain same file/dir SVN properties as source") + help="Maintain same file/dir SVN properties as source.") parser.add_option("-R", "--keep-revnum", action="store_true", dest="keep_revnum", default=False, - help="maintain same rev #'s as source. creates placeholder target " - "revisions (by modifying a 'svn2svn:keep-revnum' property at the root of the target repo)") + help="Maintain same rev #'s as source. creates placeholder target " + "revisions (by modifying a 'svn2svn:keep-revnum' property at the root of the target repo).") parser.add_option("-c", "--continue", action="store_true", dest="cont_from_break", - help="continue from last source commit to target (based on svn2svn:* revprops)") + help="Continue from last source commit to target (based on svn2svn:* revprops).") parser.add_option("-f", "--force", action="store_true", dest="force_nocont", - help="allow replaying into a non-empty target folder") + help="Allow replaying into a non-empty target folder.") parser.add_option("-r", "--revision", type="string", dest="revision", metavar="ARG", - help="revision range to replay from source_url\n" + help="Revision range to replay from source_url.\n" "A revision argument can be one of:\n" - " START start rev # (end will be 'HEAD')\n" - " START:END start and ending rev #'s\n" + " START Start rev # (end will be 'HEAD')\n" + " START:END Start and ending rev #'s\n" "Any revision # formats which SVN understands are " "supported, e.g. 'HEAD', '{2010-01-31}', etc.") parser.add_option("-u", "--log-author", action="store_true", dest="log_author", default=False, - help="append source commit author to replayed commit mesages") + help="Append source commit author to replayed commit mesages.") parser.add_option("-d", "--log-date", action="store_true", dest="log_date", default=False, - help="append source commit time to replayed commit messages") + help="Append source commit time to replayed commit messages.") parser.add_option("-l", "--limit", type="int", dest="entries_proc_limit", metavar="NUM", - help="maximum number of source revisions to process") + help="Maximum number of source revisions to process.") parser.add_option("-n", "--dry-run", action="store_true", dest="dry_run", default=False, - help="process next source revision but don't commit changes to " - "target working-copy (forces --limit=1)") + help="Process next source revision but don't commit changes to " + "target working-copy (forces --limit=1).") parser.add_option("-x", "--verify", action="store_const", const=1, dest="verify", - help="verify ancestry and content for changed paths in commit after every target commit or last target commit") + help="Verify ancestry and content for changed paths in commit after every target commit or last target commit.") parser.add_option("-X", "--verify-all", action="store_const", const=2, dest="verify", - help="verify ancestry and content for entire target_url tree after every target commit or last target commit") + help="Verify ancestry and content for entire target_url tree after every target commit or last target commit.") + parser.add_option("--pre-commit", type="string", dest="beforecommit", metavar="CMD", + help="Run the given shell script before each replayed commit, e.g. " + "to modify file-content during replay.") parser.add_option("--debug", dest="verbosity", const=ui.DEBUG, action="store_const", - help="enable debugging output (same as -vvv)") + help="Enable debugging output (same as -vvv).") global options options, args = parser.parse_args() if len(args) != 2: diff --git a/svn2svn/shell.py b/svn2svn/shell.py index c7ca776..d4fad19 100644 --- a/svn2svn/shell.py +++ b/svn2svn/shell.py @@ -113,7 +113,7 @@ def _run_raw_command(cmd, args, fail_if_stderr=False, no_fail=False): def _run_raw_shell_command(cmd, no_fail=False): ui.status("* %s", cmd, level=ui.EXTRA, color='BLUE') st, out = commands.getstatusoutput(cmd) - if st != 0 and not nofail: + if st != 0 and not no_fail: raise ExternalCommandFailed( "External program failed with non-zero return code (%d): %s\n%s" % (st, cmd, out)) diff --git a/tests/t0103-replay-beforecommit.sh b/tests/t0103-replay-beforecommit.sh new file mode 100755 index 0000000..d5c6ce6 --- /dev/null +++ b/tests/t0103-replay-beforecommit.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +test_description='Use svnreplay along with --pre-commit to create a modified filtered repo with only /trunk/Module2/ProjectB history +' +. ./test-lib.sh +. ./replay-lib.sh + +SVNREPLAY="../svnreplay.py" +PWD=${TEST_DIRECTORY:-.} +PWDURL=$(echo "file://$PWD" | sed 's/\ /%20/g') +REPO="$PWD/_repo_replay" +REPOURL=$(echo "file://$REPO" | sed 's/\ /%20/g') + +init_replay_repo "$REPO" +rm -rf _wc_target + +################################################################ +OFFSET="/trunk/Module2/ProjectB" +svn mkdir -q -m "Add /trunk" $REPOURL/trunk +svn mkdir -q --parents -m "Add $OFFSET" $REPOURL$OFFSET + +test_expect_success \ + "svnreplay --pre-commit _repo_ref$OFFSET _repo_replay$OFFSET" \ + "$SVNREPLAY -av --pre-commit=\"$PWD/t0103/before-commit.sh\" \"$PWDURL/_repo_ref$OFFSET\" \"$PWDURL/_repo_replay$OFFSET\"" + +test_expect_failure \ + "svnreplay --pre-commit _repo_ref$OFFSET _repo_replay$OFFSET (verify-all)" \ + "$SVNREPLAY -avcX --pre-commit=\"$PWD/t0103/before-commit.sh\" \"$PWDURL/_repo_ref$OFFSET\" \"$PWDURL/_repo_replay$OFFSET\"" + +test_expect_failure \ + "diff-repo _repo_ref$OFFSET _repo_replay$OFFSET" \ + "./diff-repo.sh \"$PWDURL/_repo_ref$OFFSET\" \"$PWDURL/_repo_replay$OFFSET\"" + +#rm -rf "$REPO" _wc_target +test_done diff --git a/tests/t0103/before-commit.sh b/tests/t0103/before-commit.sh new file mode 100755 index 0000000..257de09 --- /dev/null +++ b/tests/t0103/before-commit.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +WC=$1 +REV=$2 + +svn mkdir -q --parents "$WC/t0103" +echo "$REV" >> "$WC/t0103/t0103.txt" +svn add -q "$WC/t0103/t0103.txt" + +exit 0 -- 2.45.2