Skip to content

Commit 8d81408

Browse files
bonzinigitster
authored andcommitted
git-send-email: add --transfer-encoding option
The thread at http://thread.gmane.org/gmane.comp.version-control.git/257392 details problems when applying patches with "git am" in a repository with CRLF line endings. In the example in the thread, the repository originated from "git-svn" so it is not possible to use core.eol and friends on it. Right now, the best option is to use "git am --keep-cr". However, when a patch create new files, the patch application process will reject the new file because it finds a "/dev/null\r" string instead of "/dev/null". The problem is that SMTP transport is CRLF-unsafe. Sending a patch by email is the same as passing it through "dos2unix | unix2dos". The newly introduced CRLFs are normally transparent because git-am strips them. The keepcr=true setting preserves them, but it is mostly working by chance and it would be very problematic to have a "git am" workflow in a repository with mixed LF and CRLF line endings. The MIME solution to this is the quoted-printable transfer enconding. This is not something that we want to enable by default, since it makes received emails horrible to look at. However, it is a very good match for projects that store CRLF line endings in the repository. The only disadvantage of quoted-printable is that quoted-printable patches fail to apply if the maintainer uses "git am --keep-cr". This is because the decoded patch will have two carriage returns at the end of the line. Therefore, add support for base64 transfer encoding too, which makes received emails downright impossible to look at outside a MUA, but really just works. The patch covers all bases, including users that still live in the late 80s, by also providing a 7bit content transfer encoding that refuses to send emails with non-ASCII character in them. And finally, "8bit" will add a Content-Transfer-Encoding header but otherwise do nothing. Signed-off-by: Paolo Bonzini <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent bb29456 commit 8d81408

File tree

5 files changed

+208
-0
lines changed

5 files changed

+208
-0
lines changed

Documentation/config.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2303,6 +2303,7 @@ sendemail.smtpserverport::
23032303
sendemail.smtpserveroption::
23042304
sendemail.smtpuser::
23052305
sendemail.thread::
2306+
sendemail.transferencoding::
23062307
sendemail.validate::
23072308
See linkgit:git-send-email[1] for description.
23082309

Documentation/git-send-email.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,16 @@ Note that no attempts whatsoever are made to validate the encoding.
131131
Specify encoding of compose message. Default is the value of the
132132
'sendemail.composeencoding'; if that is unspecified, UTF-8 is assumed.
133133

134+
--transfer-encoding=(7bit|8bit|quoted-printable|base64)::
135+
Specify the transfer encoding to be used to send the message over SMTP.
136+
7bit will fail upon encountering a non-ASCII message. quoted-printable
137+
can be useful when the repository contains files that contain carriage
138+
returns, but makes the raw patch email file (as saved from a MUA) much
139+
harder to inspect manually. base64 is even more fool proof, but also
140+
even more opaque. Default is the value of the 'sendemail.transferEncoding'
141+
configuration value; if that is unspecified, git will use 8bit and not
142+
add a Content-Transfer-Encoding header.
143+
134144

135145
Sending
136146
~~~~~~~

contrib/completion/git-completion.bash

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1875,6 +1875,10 @@ _git_config ()
18751875
__gitcomp "$__git_send_email_suppresscc_options"
18761876
return
18771877
;;
1878+
sendemail.transferencoding)
1879+
__gitcomp "7bit 8bit quoted-printable base64"
1880+
return
1881+
;;
18781882
--get|--get-all|--unset|--unset-all)
18791883
__gitcomp_nl "$(__git_config_get_set_variables)"
18801884
return

git-send-email.perl

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ sub usage {
5858
--compose * Open an editor for introduction.
5959
--compose-encoding <str> * Encoding to assume for introduction.
6060
--8bit-encoding <str> * Encoding to assume 8bit mails if undeclared
61+
--transfer-encoding <str> * Transfer encoding to use (quoted-printable, 8bit, base64)
6162
6263
Sending:
6364
--envelope-sender <str> * Email envelope sender.
@@ -206,6 +207,7 @@ sub do_edit {
206207
my (@suppress_cc);
207208
my ($auto_8bit_encoding);
208209
my ($compose_encoding);
210+
my ($target_xfer_encoding);
209211

210212
my ($debug_net_smtp) = 0; # Net::SMTP, see send_message()
211213

@@ -242,6 +244,7 @@ sub do_edit {
242244
"from" => \$sender,
243245
"assume8bitencoding" => \$auto_8bit_encoding,
244246
"composeencoding" => \$compose_encoding,
247+
"transferencoding" => \$target_xfer_encoding,
245248
);
246249

247250
my %config_path_settings = (
@@ -314,6 +317,7 @@ sub signal_handler {
314317
"envelope-sender=s" => \$envelope_sender,
315318
"thread!" => \$thread,
316319
"validate!" => \$validate,
320+
"transfer-encoding=s" => \$target_xfer_encoding,
317321
"format-patch!" => \$format_patch,
318322
"8bit-encoding=s" => \$auto_8bit_encoding,
319323
"compose-encoding=s" => \$compose_encoding,
@@ -1482,6 +1486,12 @@ sub send_message {
14821486
}
14831487
}
14841488
}
1489+
if (defined $target_xfer_encoding) {
1490+
$xfer_encoding = '8bit' if not defined $xfer_encoding;
1491+
$message = apply_transfer_encoding(
1492+
$message, $xfer_encoding, $target_xfer_encoding);
1493+
$xfer_encoding = $target_xfer_encoding;
1494+
}
14851495
if (defined $xfer_encoding) {
14861496
push @xh, "Content-Transfer-Encoding: $xfer_encoding";
14871497
}
@@ -1556,6 +1566,32 @@ sub cleanup_compose_files {
15561566

15571567
$smtp->quit if $smtp;
15581568

1569+
sub apply_transfer_encoding {
1570+
my $message = shift;
1571+
my $from = shift;
1572+
my $to = shift;
1573+
1574+
return $message if ($from eq $to and $from ne '7bit');
1575+
1576+
require MIME::QuotedPrint;
1577+
require MIME::Base64;
1578+
1579+
$message = MIME::QuotedPrint::decode($message)
1580+
if ($from eq 'quoted-printable');
1581+
$message = MIME::Base64::decode($message)
1582+
if ($from eq 'base64');
1583+
1584+
die "cannot send message as 7bit"
1585+
if ($to eq '7bit' and $message =~ /[^[:ascii:]]/);
1586+
return $message
1587+
if ($to eq '7bit' or $to eq '8bit');
1588+
return MIME::QuotedPrint::encode($message, "\n", 0)
1589+
if ($to eq 'quoted-printable');
1590+
return MIME::Base64::encode($message, "\n")
1591+
if ($to eq 'base64');
1592+
die "invalid transfer encoding";
1593+
}
1594+
15591595
sub unique_email_list {
15601596
my %seen;
15611597
my @emails;

t/t9001-send-email.sh

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1298,6 +1298,163 @@ test_expect_success $PREREQ '--8bit-encoding also treats subject' '
12981298
test_cmp expected actual
12991299
'
13001300

1301+
test_expect_success $PREREQ 'setup expect' '
1302+
cat >email-using-8bit <<EOF
1303+
From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
1304+
Message-Id: <[email protected]>
1305+
From: A U Thor <[email protected]>
1306+
Date: Sat, 12 Jun 2010 15:53:58 +0200
1307+
Content-Type: text/plain; charset=UTF-8
1308+
Subject: Nothing to see here.
1309+
1310+
Dieser Betreff enthält auch einen Umlaut!
1311+
EOF
1312+
'
1313+
1314+
test_expect_success $PREREQ 'sendemail.transferencoding=7bit fails on 8bit data' '
1315+
clean_fake_sendmail &&
1316+
git config sendemail.transferEncoding 7bit &&
1317+
test_must_fail git send-email \
1318+
--transfer-encoding=7bit \
1319+
--smtp-server="$(pwd)/fake.sendmail" \
1320+
email-using-8bit \
1321+
2>errors >out &&
1322+
grep "cannot send message as 7bit" errors &&
1323+
test -z "$(ls msgtxt*)"
1324+
'
1325+
1326+
test_expect_success $PREREQ '--transfer-encoding overrides sendemail.transferEncoding' '
1327+
clean_fake_sendmail &&
1328+
git config sendemail.transferEncoding 8bit
1329+
test_must_fail git send-email \
1330+
--transfer-encoding=7bit \
1331+
--smtp-server="$(pwd)/fake.sendmail" \
1332+
email-using-8bit \
1333+
2>errors >out &&
1334+
grep "cannot send message as 7bit" errors &&
1335+
test -z "$(ls msgtxt*)"
1336+
'
1337+
1338+
test_expect_success $PREREQ 'sendemail.transferencoding=8bit' '
1339+
clean_fake_sendmail &&
1340+
git send-email \
1341+
--transfer-encoding=8bit \
1342+
--smtp-server="$(pwd)/fake.sendmail" \
1343+
email-using-8bit \
1344+
2>errors >out &&
1345+
sed '1,/^$/d' msgtxt1 >actual &&
1346+
sed '1,/^$/d' email-using-8bit >expected &&
1347+
test_cmp expected actual
1348+
'
1349+
1350+
test_expect_success $PREREQ 'setup expect' '
1351+
cat >expected <<EOF
1352+
Dieser Betreff enth=C3=A4lt auch einen Umlaut!
1353+
EOF
1354+
'
1355+
1356+
test_expect_success $PREREQ '8-bit and sendemail.transferencoding=quoted-printable' '
1357+
clean_fake_sendmail &&
1358+
git send-email \
1359+
--transfer-encoding=quoted-printable \
1360+
--smtp-server="$(pwd)/fake.sendmail" \
1361+
email-using-8bit \
1362+
2>errors >out &&
1363+
sed '1,/^$/d' msgtxt1 >actual &&
1364+
test_cmp expected actual
1365+
'
1366+
1367+
test_expect_success $PREREQ 'setup expect' '
1368+
cat >expected <<EOF
1369+
RGllc2VyIEJldHJlZmYgZW50aMOkbHQgYXVjaCBlaW5lbiBVbWxhdXQhCg==
1370+
EOF
1371+
'
1372+
1373+
test_expect_success $PREREQ '8-bit and sendemail.transferencoding=base64' '
1374+
clean_fake_sendmail &&
1375+
git send-email \
1376+
--transfer-encoding=base64 \
1377+
--smtp-server="$(pwd)/fake.sendmail" \
1378+
email-using-8bit \
1379+
2>errors >out &&
1380+
sed '1,/^$/d' msgtxt1 >actual &&
1381+
test_cmp expected actual
1382+
'
1383+
1384+
test_expect_success $PREREQ 'setup expect' '
1385+
cat >email-using-qp <<EOF
1386+
From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
1387+
Message-Id: <[email protected]>
1388+
From: A U Thor <[email protected]>
1389+
Date: Sat, 12 Jun 2010 15:53:58 +0200
1390+
MIME-Version: 1.0
1391+
Content-Transfer-Encoding: quoted-printable
1392+
Content-Type: text/plain; charset=UTF-8
1393+
Subject: Nothing to see here.
1394+
1395+
Dieser Betreff enth=C3=A4lt auch einen Umlaut!
1396+
EOF
1397+
'
1398+
1399+
test_expect_success $PREREQ 'convert from quoted-printable to base64' '
1400+
clean_fake_sendmail &&
1401+
git send-email \
1402+
--transfer-encoding=base64 \
1403+
--smtp-server="$(pwd)/fake.sendmail" \
1404+
email-using-qp \
1405+
2>errors >out &&
1406+
sed '1,/^$/d' msgtxt1 >actual &&
1407+
test_cmp expected actual
1408+
'
1409+
1410+
test_expect_success $PREREQ 'setup expect' "
1411+
tr -d '\\015' | tr '%' '\\015' > email-using-crlf <<EOF
1412+
From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
1413+
Message-Id: <[email protected]>
1414+
From: A U Thor <[email protected]>
1415+
Date: Sat, 12 Jun 2010 15:53:58 +0200
1416+
Content-Type: text/plain; charset=UTF-8
1417+
Subject: Nothing to see here.
1418+
1419+
Look, I have a CRLF and an = sign!%
1420+
EOF
1421+
"
1422+
1423+
test_expect_success $PREREQ 'setup expect' '
1424+
cat >expected <<EOF
1425+
Look, I have a CRLF and an =3D sign!=0D
1426+
EOF
1427+
'
1428+
1429+
test_expect_success $PREREQ 'CRLF and sendemail.transferencoding=quoted-printable' '
1430+
clean_fake_sendmail &&
1431+
git send-email \
1432+
--transfer-encoding=quoted-printable \
1433+
--smtp-server="$(pwd)/fake.sendmail" \
1434+
email-using-crlf \
1435+
2>errors >out &&
1436+
sed '1,/^$/d' msgtxt1 >actual &&
1437+
test_cmp expected actual
1438+
'
1439+
1440+
test_expect_success $PREREQ 'setup expect' '
1441+
cat >expected <<EOF
1442+
TG9vaywgSSBoYXZlIGEgQ1JMRiBhbmQgYW4gPSBzaWduIQ0K
1443+
EOF
1444+
'
1445+
1446+
test_expect_success $PREREQ 'CRLF and sendemail.transferencoding=base64' '
1447+
clean_fake_sendmail &&
1448+
git send-email \
1449+
--transfer-encoding=base64 \
1450+
--smtp-server="$(pwd)/fake.sendmail" \
1451+
email-using-crlf \
1452+
2>errors >out &&
1453+
sed '1,/^$/d' msgtxt1 >actual &&
1454+
test_cmp expected actual
1455+
'
1456+
1457+
13011458
# Note that the patches in this test are deliberately out of order; we
13021459
# want to make sure it works even if the cover-letter is not in the
13031460
# first mail.

0 commit comments

Comments
 (0)