9
9
# License: GPL v2 or later
10
10
11
11
# Gateway between Git and MediaWiki.
12
- # Documentation & bugtracker: https://github.com/moy/ Git-Mediawiki/
12
+ # Documentation & bugtracker: https://github.com/Git-Mediawiki/Git-Mediawiki
13
13
14
14
use strict;
15
15
use MediaWiki::API;
56
56
57
57
# Accept both space-separated and multiple keys in config file.
58
58
# Spaces should be written as _ anyway because we'll use chomp.
59
- my @tracked_pages = split (/ [ \n ]/ , run_git( " config --get-all remote.${remotename} .pages" ));
59
+ my @tracked_pages = split (/ [ \n ]/ , run_git_quoted([ " config" , " --get-all" , " remote.${remotename} .pages" ] ));
60
60
chomp (@tracked_pages );
61
61
62
62
# Just like @tracked_pages, but for MediaWiki categories.
63
- my @tracked_categories = split (/ [ \n ]/ , run_git( " config --get-all remote.${remotename} .categories" ));
63
+ my @tracked_categories = split (/ [ \n ]/ , run_git_quoted([ " config" , " --get-all" , " remote.${remotename} .categories" ] ));
64
64
chomp (@tracked_categories );
65
65
66
66
# Just like @tracked_categories, but for MediaWiki namespaces.
67
- my @tracked_namespaces = split (/ [ \n ]/ , run_git( " config --get-all remote.${remotename} .namespaces" ));
67
+ my @tracked_namespaces = split (/ [ \n ]/ , run_git_quoted([ " config" , " --get-all" , " remote.${remotename} .namespaces" ] ));
68
68
for (@tracked_namespaces ) { s / _/ / g ; }
69
69
chomp (@tracked_namespaces );
70
70
71
71
# Import media files on pull
72
- my $import_media = run_git( " config --get --bool remote.${remotename} .mediaimport" );
72
+ my $import_media = run_git_quoted([ " config" , " --get" , " --bool" , " remote.${remotename} .mediaimport" ] );
73
73
chomp ($import_media );
74
74
$import_media = ($import_media eq ' true' );
75
75
76
76
# Export media files on push
77
- my $export_media = run_git( " config --get --bool remote.${remotename} .mediaexport" );
77
+ my $export_media = run_git_quoted([ " config" , " --get" , " --bool" , " remote.${remotename} .mediaexport" ] );
78
78
chomp ($export_media );
79
79
$export_media = !($export_media eq ' false' );
80
80
81
- my $wiki_login = run_git( " config --get remote.${remotename} .mwLogin" );
81
+ my $wiki_login = run_git_quoted([ " config" , " --get" , " remote.${remotename} .mwLogin" ] );
82
82
# Note: mwPassword is discouraged. Use the credential system instead.
83
- my $wiki_passwd = run_git( " config --get remote.${remotename} .mwPassword" );
84
- my $wiki_domain = run_git( " config --get remote.${remotename} .mwDomain" );
83
+ my $wiki_passwd = run_git_quoted([ " config" , " --get" , " remote.${remotename} .mwPassword" ] );
84
+ my $wiki_domain = run_git_quoted([ " config" , " --get" , " remote.${remotename} .mwDomain" ] );
85
85
chomp ($wiki_login );
86
86
chomp ($wiki_passwd );
87
87
chomp ($wiki_domain );
88
88
89
89
# Import only last revisions (both for clone and fetch)
90
- my $shallow_import = run_git( " config --get --bool remote.${remotename} .shallow" );
90
+ my $shallow_import = run_git_quoted([ " config" , " --get" , " --bool" , " remote.${remotename} .shallow" ] );
91
91
chomp ($shallow_import );
92
92
$shallow_import = ($shallow_import eq ' true' );
93
93
97
97
# Possible values:
98
98
# - by_rev: perform one query per new revision on the remote wiki
99
99
# - by_page: query each tracked page for new revision
100
- my $fetch_strategy = run_git( " config --get remote.${remotename} .fetchStrategy" );
100
+ my $fetch_strategy = run_git_quoted([ " config" , " --get" , " remote.${remotename} .fetchStrategy" ] );
101
101
if (!$fetch_strategy ) {
102
- $fetch_strategy = run_git( ' config --get mediawiki.fetchStrategy' );
102
+ $fetch_strategy = run_git_quoted([ " config" , " --get" , " mediawiki.fetchStrategy" ] );
103
103
}
104
104
chomp ($fetch_strategy );
105
105
if (!$fetch_strategy ) {
123
123
# will get the history with information lost). If the import is
124
124
# deterministic, this means everybody gets the same sha1 for each
125
125
# MediaWiki revision.
126
- my $dumb_push = run_git( " config --get --bool remote.${remotename} .dumbPush" );
126
+ my $dumb_push = run_git_quoted([ " config" , " --get" , " --bool" , " remote.${remotename} .dumbPush" ] );
127
127
if (!$dumb_push ) {
128
- $dumb_push = run_git( ' config --get --bool mediawiki.dumbPush' );
128
+ $dumb_push = run_git_quoted([ " config" , " --get" , " --bool" , " mediawiki.dumbPush" ] );
129
129
}
130
130
chomp ($dumb_push );
131
131
$dumb_push = ($dumb_push eq ' true' );
@@ -369,12 +369,14 @@ sub get_mw_pages {
369
369
return %pages ;
370
370
}
371
371
372
- # usage: $out = run_git("command args");
373
- # $out = run_git("command args", "raw"); # don't interpret output as UTF-8.
374
- sub run_git {
372
+ # usage: $out = run_git_quoted(["command", "args", ...]);
373
+ # $out = run_git_quoted(["command", "args", ...], "raw"); # don't interpret output as UTF-8.
374
+ # $out = run_git_quoted_nostderr(["command", "args", ...]); # discard stderr
375
+ # $out = run_git_quoted_nostderr(["command", "args", ...], "raw"); # ditto but raw instead of UTF-8 as above
376
+ sub _run_git {
375
377
my $args = shift ;
376
378
my $encoding = (shift || ' encoding(UTF-8)' );
377
- open (my $git , " -|:${encoding} " , " git ${ args} " )
379
+ open (my $git , " -|:${encoding} " , @$ args )
378
380
or die " Unable to fork: $! \n " ;
379
381
my $res = do {
380
382
local $/ = undef ;
@@ -385,6 +387,13 @@ sub run_git {
385
387
return $res ;
386
388
}
387
389
390
+ sub run_git_quoted {
391
+ _run_git([" git" , @{$_ [0]}], $_ [1]);
392
+ }
393
+
394
+ sub run_git_quoted_nostderr {
395
+ _run_git([' sh' , ' -c' , ' git "$@" 2>/dev/null' , ' --' , @{$_ [0]}], $_ [1]);
396
+ }
388
397
389
398
sub get_all_mediafiles {
390
399
my $pages = shift ;
@@ -511,8 +520,9 @@ sub download_mw_mediafile {
511
520
}
512
521
513
522
sub get_last_local_revision {
514
- # Get note regarding last mediawiki revision
515
- my $note = run_git(" notes --ref=${remotename} /mediawiki show refs/mediawiki/${remotename} /master 2>/dev/null" );
523
+ # Get note regarding last mediawiki revision.
524
+ my $note = run_git_quoted_nostderr([" notes" , " --ref=${remotename} /mediawiki" ,
525
+ " show" , " refs/mediawiki/${remotename} /master" ]);
516
526
my @note_info = split (/ / , $note );
517
527
518
528
my $lastrevision_number ;
@@ -807,7 +817,10 @@ sub get_more_refs {
807
817
sub mw_import {
808
818
# multiple import commands can follow each other.
809
819
my @refs = (shift , get_more_refs(' import' ));
820
+ my $processedRefs ;
810
821
foreach my $ref (@refs ) {
822
+ next if $processedRefs -> {$ref }; # skip duplicates: "import refs/heads/master" being issued twice; TODO: why?
823
+ $processedRefs -> {$ref } = 1;
811
824
mw_import_ref($ref );
812
825
}
813
826
print {*STDOUT } " done\n " ;
@@ -970,7 +983,7 @@ sub mw_import_revids {
970
983
}
971
984
972
985
sub error_non_fast_forward {
973
- my $advice = run_git( ' config --bool advice.pushNonFastForward' );
986
+ my $advice = run_git_quoted([ " config" , " --bool" , " advice.pushNonFastForward" ] );
974
987
chomp ($advice );
975
988
if ($advice ne ' false' ) {
976
989
# Native git-push would show this after the summary.
@@ -1014,7 +1027,7 @@ sub mw_upload_file {
1014
1027
}
1015
1028
} else {
1016
1029
# Don't let perl try to interpret file content as UTF-8 => use "raw"
1017
- my $content = run_git( " cat-file blob ${ new_sha1} " , ' raw' );
1030
+ my $content = run_git_quoted([ " cat-file" , " blob" , $ new_sha1] , ' raw' );
1018
1031
if ($content ne EMPTY) {
1019
1032
$mediawiki = connect_maybe($mediawiki , $remotename , $url );
1020
1033
$mediawiki -> {config }-> {upload_url } =
@@ -1084,7 +1097,7 @@ sub mw_push_file {
1084
1097
# with this content instead:
1085
1098
$file_content = DELETED_CONTENT;
1086
1099
} else {
1087
- $file_content = run_git( " cat-file blob ${ new_sha1} " );
1100
+ $file_content = run_git_quoted([ " cat-file" , " blob" , $ new_sha1] );
1088
1101
}
1089
1102
1090
1103
$mediawiki = connect_maybe($mediawiki , $remotename , $url );
@@ -1174,10 +1187,10 @@ sub mw_push_revision {
1174
1187
my $mw_revision = $last_remote_revid ;
1175
1188
1176
1189
# Get sha1 of commit pointed by local HEAD
1177
- my $HEAD_sha1 = run_git( " rev-parse ${ local} 2>/dev/null " );
1190
+ my $HEAD_sha1 = run_git_quoted_nostderr([ " rev-parse" , $ local] );
1178
1191
chomp ($HEAD_sha1 );
1179
1192
# Get sha1 of commit pointed by remotes/$remotename/master
1180
- my $remoteorigin_sha1 = run_git( " rev-parse refs/remotes/${remotename} /master 2>/dev/null " );
1193
+ my $remoteorigin_sha1 = run_git_quoted_nostderr([ " rev-parse" , " refs/remotes/${remotename} /master" ] );
1181
1194
chomp ($remoteorigin_sha1 );
1182
1195
1183
1196
if ($last_local_revid > 0 &&
@@ -1197,7 +1210,7 @@ sub mw_push_revision {
1197
1210
my $parsed_sha1 = $remoteorigin_sha1 ;
1198
1211
# Find a path from last MediaWiki commit to pushed commit
1199
1212
print {*STDERR } " Computing path from local to remote ...\n " ;
1200
- my @local_ancestry = split (/ \n / , run_git( " rev-list --boundary --parents ${ local} ^${parsed_sha1} " ));
1213
+ my @local_ancestry = split (/ \n / , run_git_quoted([ " rev-list" , " --boundary" , " --parents" , $ local, " ^${parsed_sha1} " ] ));
1201
1214
my %local_ancestry ;
1202
1215
foreach my $line (@local_ancestry ) {
1203
1216
if (my ($child , $parents ) = $line =~ / ^-?([a-f0-9]+) ([a-f0-9 ]+)/ ) {
@@ -1221,7 +1234,7 @@ sub mw_push_revision {
1221
1234
# No remote mediawiki revision. Export the whole
1222
1235
# history (linearized with --first-parent)
1223
1236
print {*STDERR } " Warning: no common ancestor, pushing complete history\n " ;
1224
- my $history = run_git( " rev-list --first-parent --children ${ local} " );
1237
+ my $history = run_git_quoted([ " rev-list" , " --first-parent" , " --children" , $ local] );
1225
1238
my @history = split (/ \n / , $history );
1226
1239
@history = @history [1..$#history ];
1227
1240
foreach my $line (reverse @history ) {
@@ -1233,12 +1246,12 @@ sub mw_push_revision {
1233
1246
foreach my $commit_info_split (@commit_pairs ) {
1234
1247
my $sha1_child = @{$commit_info_split }[0];
1235
1248
my $sha1_commit = @{$commit_info_split }[1];
1236
- my $diff_infos = run_git( " diff-tree -r --raw -z ${ sha1_child} ${ sha1_commit} " );
1249
+ my $diff_infos = run_git_quoted([ " diff-tree" , " -r " , " --raw" , " -z " , $ sha1_child, $ sha1_commit] );
1237
1250
# TODO: we could detect rename, and encode them with a #redirect on the wiki.
1238
1251
# TODO: for now, it's just a delete+add
1239
1252
my @diff_info_list = split (/ \0 / , $diff_infos );
1240
1253
# Keep the subject line of the commit message as mediawiki comment for the revision
1241
- my $commit_msg = run_git( qq( log --no-walk --format="%s " ${ sha1_commit} ) );
1254
+ my $commit_msg = run_git_quoted([ " log" , " --no-walk" , ' --format="%s"' , $ sha1_commit] );
1242
1255
chomp ($commit_msg );
1243
1256
# Push every blob
1244
1257
while (@diff_info_list ) {
@@ -1263,7 +1276,10 @@ sub mw_push_revision {
1263
1276
}
1264
1277
}
1265
1278
if (!$dumb_push ) {
1266
- run_git(qq( notes --ref=${remotename} /mediawiki add -f -m "mediawiki_revision: ${mw_revision} " ${sha1_commit} ) );
1279
+ run_git_quoted([" notes" , " --ref=${remotename} /mediawiki" ,
1280
+ " add" , " -f" , " -m" ,
1281
+ " mediawiki_revision: ${mw_revision} " ,
1282
+ $sha1_commit ]);
1267
1283
}
1268
1284
}
1269
1285
@@ -1304,7 +1320,7 @@ sub get_mw_namespace_id {
1304
1320
# already cached. Namespaces are stored in form:
1305
1321
# "Name_of_namespace:Id_namespace", ex.: "File:6".
1306
1322
my @temp = split (/ \n / ,
1307
- run_git( " config --get-all remote.${remotename} .namespaceCache" ));
1323
+ run_git_quoted([ " config" , " --get-all" , " remote.${remotename} .namespaceCache" ] ));
1308
1324
chomp (@temp );
1309
1325
foreach my $ns (@temp ) {
1310
1326
my ($n , $id ) = split (/ :/ , $ns );
@@ -1358,7 +1374,7 @@ sub get_mw_namespace_id {
1358
1374
1359
1375
# Store explicitly requested namespaces on disk
1360
1376
if (!exists $cached_mw_namespace_id {$name }) {
1361
- run_git( qq( config --add remote.${remotename} .namespaceCache "${name} :${store_id} ") );
1377
+ run_git_quoted([ " config" , " --add" , " remote.${remotename} .namespaceCache" , " ${name} :${store_id} " ] );
1362
1378
$cached_mw_namespace_id {$name } = 1;
1363
1379
}
1364
1380
return $id ;
0 commit comments