{"thread":{"id":"38136","subject":"[PATCH] git-svn: Support for git-svn propset","startedAt":"2014-12-07T10:47:23Z","lastAt":"2014-12-08T23:43:00Z","messageCount":3,"participants":["Alfred Perlstein","Eric Wong"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"253278","messageId":"20141207104723.GB54199@elvis.mu.org","threadId":"38136","inReplyTo":null,"subject":"[PATCH] git-svn: Support for git-svn propset","fromName":"Alfred Perlstein","fromEmail":"alfred@freebsd.org","sentAt":"2014-12-07T10:47:23Z","receivedAt":"2014-12-07T10:47:23Z","isPatch":true,"sender":{"key":"alfred@freebsd.org","avatar":"https://avatars.githubusercontent.com/u/629072?v=4"},"body":"This change allows git-svn to support setting subversion properties.\n\nVery useful for manually setting properties when committing to a\nsubversion repo that *requires* properties to be set without requiring\nmoving your changeset to separate subversion checkout in order to\nset props.\n\nThis change is initially from David Fraser\nAppearing here:\n  http://marc.info/?l=git&m=125259772625008&w=2\n\nThey are now forward ported to most recent git along with fixes to\ndeal with files in subdirectories.\n\nStyle and functional changes from Eric Wong have been taken\nin their entirety from:\n  http://mid.gmane.org/20141201094911.GA13931@dcvr.yhbt.net\n\nThere is a nit that I want to point out.  The code does not support\nadding props unless there are also content changes to the files as\nwell.  You can see this in the testcase.\n\nReviewed-by: Eric Wong <normalperson@yhbt.net>\nSigned-off-by: Alfred Perlstein <alfred@freebsd.org>\nSigned-off-by: David Fraser <davidf@sjsoft.com>\n---\n git-svn.perl               | 49 ++++++++++++++++++++++-\n perl/Git/SVN/Editor.pm     | 42 ++++++++++++++++++++\n t/t9148-git-svn-propset.sh | 97 ++++++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 187 insertions(+), 1 deletion(-)\n create mode 100755 t/t9148-git-svn-propset.sh\n\ndiff --git a/git-svn.perl b/git-svn.perl\nindex b6e2186..60f8814 100755\n--- a/git-svn.perl\n+++ b/git-svn.perl\n@@ -115,7 +115,7 @@ my ($_stdin, $_help, $_edit,\n \t$_before, $_after,\n \t$_merge, $_strategy, $_preserve_merges, $_dry_run, $_parents, $_local,\n \t$_prefix, $_no_checkout, $_url, $_verbose,\n-\t$_commit_url, $_tag, $_merge_info, $_interactive);\n+\t$_commit_url, $_tag, $_merge_info, $_interactive, $_set_svn_props);\n \n # This is a refactoring artifact so Git::SVN can get at this git-svn switch.\n sub opt_prefix { return $_prefix || '' }\n@@ -193,6 +193,7 @@ my %cmd = (\n \t\t\t  'dry-run|n' => \\$_dry_run,\n \t\t\t  'fetch-all|all' => \\$_fetch_all,\n \t\t\t  'commit-url=s' => \\$_commit_url,\n+\t\t\t  'set-svn-props=s' => \\$_set_svn_props,\n \t\t\t  'revision|r=i' => \\$_revision,\n \t\t\t  'no-rebase' => \\$_no_rebase,\n \t\t\t  'mergeinfo=s' => \\$_merge_info,\n@@ -228,6 +229,9 @@ my %cmd = (\n         'propget' => [ \\&cmd_propget,\n \t\t       'Print the value of a property on a file or directory',\n \t\t       { 'revision|r=i' => \\$_revision } ],\n+        'propset' => [ \\&cmd_propset,\n+\t\t       'Set the value of a property on a file or directory - will be set on commit',\n+\t\t       {} ],\n         'proplist' => [ \\&cmd_proplist,\n \t\t       'List all properties of a file or directory',\n \t\t       { 'revision|r=i' => \\$_revision } ],\n@@ -1376,6 +1380,49 @@ sub cmd_propget {\n \tprint $props->{$prop} . \"\\n\";\n }\n \n+# cmd_propset (PROPNAME, PROPVAL, PATH)\n+# ------------------------\n+# Adjust the SVN property PROPNAME to PROPVAL for PATH.\n+sub cmd_propset {\n+\tmy ($propname, $propval, $path) = @_;\n+\t$path = '.' if not defined $path;\n+\t$path = $cmd_dir_prefix . $path;\n+\tusage(1) if not defined $propname;\n+\tusage(1) if not defined $propval;\n+\tmy $file = basename($path);\n+\tmy $dn = dirname($path);\n+\tmy $cur_props = Git::SVN::Editor::check_attr( \"svn-properties\", $path );\n+\tmy @new_props;\n+\tif (!$cur_props || $cur_props eq \"unset\" || $cur_props eq \"\" || $cur_props eq \"set\") {\n+\t\tpush @new_props, \"$propname=$propval\";\n+\t} else {\n+\t\t# TODO: handle combining properties better\n+\t\tmy @props = split(/;/, $cur_props);\n+\t\tmy $replaced_prop;\n+\t\tforeach my $prop (@props) {\n+\t\t\t# Parse 'name=value' syntax and set the property.\n+\t\t\tif ($prop =~ /([^=]+)=(.*)/) {\n+\t\t\t\tmy ($n,$v) = ($1,$2);\n+\t\t\t\tif ($n eq $propname) {\n+\t\t\t\t\t$v = $propval;\n+\t\t\t\t\t$replaced_prop = 1;\n+\t\t\t\t}\n+\t\t\t\tpush @new_props, \"$n=$v\";\n+\t\t\t}\n+\t\t}\n+\t\tif (!$replaced_prop) {\n+\t\t\tpush @new_props, \"$propname=$propval\";\n+\t\t}\n+\t}\n+\tmy $attrfile = \"$dn/.gitattributes\";\n+\topen my $attrfh, '>>', $attrfile or die \"Can't open $attrfile: $!\\n\";\n+\t# TODO: don't simply append here if $file already has svn-properties\n+\tmy $new_props = join(';', @new_props);\n+\tprint $attrfh \"$file svn-properties=$new_props\\n\" or\n+\t\tdie \"write to $attrfile: $!\\n\";\n+\tclose $attrfh or die \"close $attrfile: $!\\n\";\n+}\n+\n # cmd_proplist (PATH)\n # -------------------\n # Print the list of SVN properties for PATH.\ndiff --git a/perl/Git/SVN/Editor.pm b/perl/Git/SVN/Editor.pm\nindex 34e8af9..b84ce13 100644\n--- a/perl/Git/SVN/Editor.pm\n+++ b/perl/Git/SVN/Editor.pm\n@@ -288,6 +288,44 @@ sub apply_autoprops {\n \t}\n }\n \n+sub check_attr {\n+\tmy ($attr,$path) = @_;\n+\tmy $fh = command_output_pipe(\"check-attr\", $attr, \"--\", $path);\n+\treturn undef if (!$fh);\n+\n+\tmy $val = <$fh>;\n+\tclose $fh;\n+\tif ($val) { $val =~ s/^[^:]*:\\s*[^:]*:\\s*(.*)\\s*$/$1/; }\n+\treturn $val;\n+}\n+\n+sub apply_manualprops {\n+\tmy ($self, $file, $fbat) = @_;\n+\tmy $pending_properties = check_attr( \"svn-properties\", $file );\n+\tif ($pending_properties eq \"\") { return; }\n+\t# Parse the list of properties to set.\n+\tmy @props = split(/;/, $pending_properties);\n+\t# TODO: get existing properties to compare to\n+\t# - this fails for add so currently not done\n+\t# my $existing_props = ::get_svnprops($file);\n+\tmy $existing_props = {};\n+\t# TODO: caching svn properties or storing them in .gitattributes\n+\t# would make that faster\n+\tforeach my $prop (@props) {\n+\t\t# Parse 'name=value' syntax and set the property.\n+\t\tif ($prop =~ /([^=]+)=(.*)/) {\n+\t\t\tmy ($n,$v) = ($1,$2);\n+\t\t\tfor ($n, $v) {\n+\t\t\t\ts/^\\s+//; s/\\s+$//;\n+\t\t\t}\n+\t\t\tmy $existing = $existing_props->{$n};\n+\t\t\tif (!defined($existing) || $existing ne $v) {\n+\t\t\t    $self->change_file_prop($fbat, $n, $v);\n+\t\t\t}\n+\t\t}\n+\t}\n+}\n+\n sub A {\n \tmy ($self, $m, $deletions) = @_;\n \tmy ($dir, $file) = split_path($m->{file_b});\n@@ -296,6 +334,7 @@ sub A {\n \t\t\t\t\tundef, -1);\n \tprint \"\\tA\\t$m->{file_b}\\n\" unless $::_q;\n \t$self->apply_autoprops($file, $fbat);\n+\t$self->apply_manualprops($m->{file_b}, $fbat);\n \t$self->chg_file($fbat, $m);\n \t$self->close_file($fbat,undef,$self->{pool});\n }\n@@ -311,6 +350,7 @@ sub C {\n \tmy $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,\n \t\t\t\t$upa, $self->{r});\n \tprint \"\\tC\\t$m->{file_a} => $m->{file_b}\\n\" unless $::_q;\n+\t$self->apply_manualprops($m->{file_b}, $fbat);\n \t$self->chg_file($fbat, $m);\n \t$self->close_file($fbat,undef,$self->{pool});\n }\n@@ -333,6 +373,7 @@ sub R {\n \t\t\t\t$upa, $self->{r});\n \tprint \"\\tR\\t$m->{file_a} => $m->{file_b}\\n\" unless $::_q;\n \t$self->apply_autoprops($file, $fbat);\n+\t$self->apply_manualprops($m->{file_b}, $fbat);\n \t$self->chg_file($fbat, $m);\n \t$self->close_file($fbat,undef,$self->{pool});\n \n@@ -348,6 +389,7 @@ sub M {\n \tmy $fbat = $self->open_file($self->repo_path($m->{file_b}),\n \t\t\t\t$pbat,$self->{r},$self->{pool});\n \tprint \"\\t$m->{chg}\\t$m->{file_b}\\n\" unless $::_q;\n+\t$self->apply_manualprops($m->{file_b}, $fbat);\n \t$self->chg_file($fbat, $m);\n \t$self->close_file($fbat,undef,$self->{pool});\n }\ndiff --git a/t/t9148-git-svn-propset.sh b/t/t9148-git-svn-propset.sh\nnew file mode 100755\nindex 0000000..584f1c5\n--- /dev/null\n+++ b/t/t9148-git-svn-propset.sh\n@@ -0,0 +1,97 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2014 Alfred Perlstein\n+#\n+\n+test_description='git svn propset tests'\n+\n+. ./lib-git-svn.sh\n+\n+foo_subdir2=\"subdir/subdir2/foo_subdir2\"\n+\n+set -e\n+mkdir import &&\n+(set -e ; cd import\n+\tmkdir subdir\n+\tmkdir subdir/subdir2\n+\ttouch foo \t\t# for 'add props top level'\n+\ttouch subdir/foo_subdir # for 'add props relative'\n+\ttouch \"$foo_subdir2\"\t# for 'add props subdir'\n+\tsvn_cmd import -m 'import for git svn' . \"$svnrepo\" >/dev/null\n+)\n+rm -rf import\n+\n+test_expect_success 'initialize git svn' '\n+\tgit svn init \"$svnrepo\"\n+\t'\n+\n+test_expect_success 'fetch revisions from svn' '\n+\tgit svn fetch\n+\t'\n+\n+set_props()\n+{\n+\tlocal subdir=\"$1\"\n+\tlocal file=\"$2\"\n+\tshift;shift;\n+\t(cd \"$subdir\" &&\n+\t\twhile [ $# -gt 0 ] ; do\n+\t\t\tgit svn propset \"$1\" \"$2\" \"$file\" || exit 1\n+\t\t\tshift;shift;\n+\t\tdone &&\n+\t\techo hello >> \"$file\" &&\n+\t\tgit commit -m \"testing propset\" \"$file\")\n+}\n+\n+confirm_props()\n+{\n+\tlocal subdir=\"$1\"\n+\tlocal file=\"$2\"\n+\tshift;shift;\n+\t(set -e ; cd \"svn_project/$subdir\" &&\n+\t\twhile [ $# -gt 0 ] ; do\n+\t\t\ttest \"$(svn_cmd propget \"$1\" \"$file\")\" = \"$2\" || exit 1\n+\t\t\tshift;shift;\n+\t\tdone)\n+}\n+\n+\n+#The current implementation has a restriction:\n+#svn propset will be taken as a delta for svn dcommit only\n+#if the file content is also modified\n+test_expect_success 'add props top level' '\n+\tset_props \".\" \"foo\" \"svn:keywords\" \"FreeBSD=%H\" &&\n+\tgit svn dcommit &&\n+\tsvn_cmd co \"$svnrepo\" svn_project &&\n+\tconfirm_props \".\" \"foo\" \"svn:keywords\" \"FreeBSD=%H\" &&\n+\trm -rf svn_project\n+\t'\n+\n+test_expect_success 'add multiple props' '\n+\tset_props \".\" \"foo\" \\\n+\t\t\"svn:keywords\" \"FreeBSD=%H\" fbsd:nokeywords yes &&\n+\tgit svn dcommit &&\n+\tsvn_cmd co \"$svnrepo\" svn_project &&\n+\tconfirm_props \".\" \"foo\" \\\n+\t\t\"svn:keywords\" \"FreeBSD=%H\" fbsd:nokeywords yes &&\n+\trm -rf svn_project\n+\t'\n+\n+test_expect_success 'add props subdir' '\n+\tset_props \".\" \"$foo_subdir2\" svn:keywords \"FreeBSD=%H\" &&\n+\tgit svn dcommit &&\n+\tsvn_cmd co \"$svnrepo\" svn_project &&\n+\tconfirm_props \".\" \"$foo_subdir2\" \"svn:keywords\" \"FreeBSD=%H\" &&\n+\trm -rf svn_project\n+\t'\n+\n+test_expect_success 'add props relative' '\n+\tset_props \"subdir/subdir2\" \"../foo_subdir\" \\\n+\t\tsvn:keywords \"FreeBSD=%H\" &&\n+\tgit svn dcommit &&\n+\tsvn_cmd co \"$svnrepo\" svn_project &&\n+\tconfirm_props \"subdir/subdir2\" \"../foo_subdir\" \\\n+\t\tsvn:keywords \"FreeBSD=%H\" &&\n+\trm -rf svn_project\n+\t'\n+test_done\n-- \n2.1.2\n"},{"id":"253374","messageId":"20141208213636.GA3743@dcvr.yhbt.net","threadId":"38136","inReplyTo":"20141207104723.GB54199@elvis.mu.org","subject":"Re: [PATCH] git-svn: Support for git-svn propset","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2014-12-08T21:36:36Z","receivedAt":"2014-12-08T21:36:36Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Alfred Perlstein <alfred@freebsd.org> wrote:\n> Appearing here:\n>   http://marc.info/?l=git&m=125259772625008&w=2\n\nProbably better to use a mid URL here, too\n\nhttp://mid.gmane.org/1927112650.1281253084529659.JavaMail.root@klofta.sjsoft.com\n\nsuch a long URL, though...\n\n> --- a/perl/Git/SVN/Editor.pm\n> +++ b/perl/Git/SVN/Editor.pm\n> @@ -288,6 +288,44 @@ sub apply_autoprops {\n>  \t}\n>  }\n>  \n> +sub check_attr {\n> +\tmy ($attr,$path) = @_;\n> +\tmy $fh = command_output_pipe(\"check-attr\", $attr, \"--\", $path);\n> +\treturn undef if (!$fh);\n> +\n> +\tmy $val = <$fh>;\n> +\tclose $fh;\n> +\tif ($val) { $val =~ s/^[^:]*:\\s*[^:]*:\\s*(.*)\\s*$/$1/; }\n> +\treturn $val;\n> +}\n\nI just noticed command_output_pipe didn't use a corresponding\ncommand_close_pipe to check for errors, but command_oneline is even\nbetter.  I'll squash the following:\n\n--- a/perl/Git/SVN/Editor.pm\n+++ b/perl/Git/SVN/Editor.pm\n@@ -290,11 +290,7 @@ sub apply_autoprops {\n \n sub check_attr {\n \tmy ($attr,$path) = @_;\n-\tmy $fh = command_output_pipe(\"check-attr\", $attr, \"--\", $path);\n-\treturn undef if (!$fh);\n-\n-\tmy $val = <$fh>;\n-\tclose $fh;\n+\tmy $val = command_oneline(\"check-attr\", $attr, \"--\", $path);\n \tif ($val) { $val =~ s/^[^:]*:\\s*[^:]*:\\s*(.*)\\s*$/$1/; }\n \treturn $val;\n }\n\nIn your test, \"local\" isn't portable, unfortunately, but tests seem to\nwork fine without local so I've removed them:\n\n--- a/t/t9148-git-svn-propset.sh\n+++ b/t/t9148-git-svn-propset.sh\n@@ -29,10 +29,9 @@ test_expect_success 'fetch revisions from svn' '\n \tgit svn fetch\n \t'\n \n-set_props()\n-{\n-\tlocal subdir=\"$1\"\n-\tlocal file=\"$2\"\n+set_props () {\n+\tsubdir=\"$1\"\n+\tfile=\"$2\"\n \tshift;shift;\n \t(cd \"$subdir\" &&\n \t\twhile [ $# -gt 0 ] ; do\n@@ -43,10 +42,9 @@ set_props()\n \t\tgit commit -m \"testing propset\" \"$file\")\n }\n \n-confirm_props()\n-{\n-\tlocal subdir=\"$1\"\n-\tlocal file=\"$2\"\n+confirm_props () {\n+\tsubdir=\"$1\"\n+\tfile=\"$2\"\n \tshift;shift;\n \t(set -e ; cd \"svn_project/$subdir\" &&\n \t\twhile [ $# -gt 0 ] ; do\n\nUnless there's other improvements we missed, I'll push out your v3 with\nmy changes squashed in for Junio to pull in a day or two.  Thank you\nagain for working on this!\n"},{"id":"253390","messageId":"04BE232D-0198-4DEA-90ED-8C8E36456CC5@mu.org","threadId":"38136","inReplyTo":"20141208213636.GA3743@dcvr.yhbt.net","subject":"Re: [PATCH] git-svn: Support for git-svn propset","fromName":"Alfred Perlstein","fromEmail":"bright@mu.org","sentAt":"2014-12-08T23:43:00Z","receivedAt":"2014-12-08T23:43:00Z","isPatch":true,"sender":{"key":"bright@mu.org","avatar":null},"body":"\n\n> On Dec 8, 2014, at 1:36 PM, Eric Wong <normalperson@yhbt.net> wrote:\n> \n> Alfred Perlstein <alfred@freebsd.org> wrote:\n>> Appearing here:\n>>  http://marc.info/?l=git&m=125259772625008&w=2\n> \n> Probably better to use a mid URL here, too\n> \n> http://mid.gmane.org/1927112650.1281253084529659.JavaMail.root@klofta.sjsoft.com\n> \n> such a long URL, though...\n> \n>> --- a/perl/Git/SVN/Editor.pm\n>> +++ b/perl/Git/SVN/Editor.pm\n>> @@ -288,6 +288,44 @@ sub apply_autoprops {\n>>    }\n>> }\n>> \n>> +sub check_attr {\n>> +    my ($attr,$path) = @_;\n>> +    my $fh = command_output_pipe(\"check-attr\", $attr, \"--\", $path);\n>> +    return undef if (!$fh);\n>> +\n>> +    my $val = <$fh>;\n>> +    close $fh;\n>> +    if ($val) { $val =~ s/^[^:]*:\\s*[^:]*:\\s*(.*)\\s*$/$1/; }\n>> +    return $val;\n>> +}\n> \n> I just noticed command_output_pipe didn't use a corresponding\n> command_close_pipe to check for errors, but command_oneline is even\n> better.  I'll squash the following:\n> \n> --- a/perl/Git/SVN/Editor.pm\n> +++ b/perl/Git/SVN/Editor.pm\n> @@ -290,11 +290,7 @@ sub apply_autoprops {\n> \n> sub check_attr {\n>    my ($attr,$path) = @_;\n> -    my $fh = command_output_pipe(\"check-attr\", $attr, \"--\", $path);\n> -    return undef if (!$fh);\n> -\n> -    my $val = <$fh>;\n> -    close $fh;\n> +    my $val = command_oneline(\"check-attr\", $attr, \"--\", $path);\n>    if ($val) { $val =~ s/^[^:]*:\\s*[^:]*:\\s*(.*)\\s*$/$1/; }\n>    return $val;\n> }\n> \n> In your test, \"local\" isn't portable, unfortunately, but tests seem to\n> work fine without local so I've removed them:\n> \n> --- a/t/t9148-git-svn-propset.sh\n> +++ b/t/t9148-git-svn-propset.sh\n> @@ -29,10 +29,9 @@ test_expect_success 'fetch revisions from svn' '\n>    git svn fetch\n>    '\n> \n> -set_props()\n> -{\n> -    local subdir=\"$1\"\n> -    local file=\"$2\"\n> +set_props () {\n> +    subdir=\"$1\"\n> +    file=\"$2\"\n>    shift;shift;\n>    (cd \"$subdir\" &&\n>        while [ $# -gt 0 ] ; do\n> @@ -43,10 +42,9 @@ set_props()\n>        git commit -m \"testing propset\" \"$file\")\n> }\n> \n> -confirm_props()\n> -{\n> -    local subdir=\"$1\"\n> -    local file=\"$2\"\n> +confirm_props () {\n> +    subdir=\"$1\"\n> +    file=\"$2\"\n>    shift;shift;\n>    (set -e ; cd \"svn_project/$subdir\" &&\n>        while [ $# -gt 0 ] ; do\n> \n> Unless there's other improvements we missed, I'll push out your v3 with\n> my changes squashed in for Junio to pull in a day or two.  Thank you\n> again for working on this!\n> \n\nEric,\n\nAll looks good to me. \n\nThank you all very much for the feedback and help.  It's made this a very rewarding endeavor. \n\n-Alfred. "}]}