Skip to content

Commit d18f03d

Browse files
committed
fast-export: do automatic reencoding of commit messages only if requested
Automatic re-encoding of commit messages (and dropping of the encoding header) hurts attempts to do reversible history rewrites (e.g. sha1sum <-> sha256sum transitions, some subtree rewrites), and seems inconsistent with the general principle followed elsewhere in fast-export of requiring explicit user requests to modify the output (e.g. --signed-tags=strip, --tag-of-filtered-object=rewrite). Add a --reencode flag that the user can use to specify, and like other fast-export flags, default it to 'abort'. Signed-off-by: Elijah Newren <[email protected]>
1 parent 2cef40c commit d18f03d

File tree

3 files changed

+85
-6
lines changed

3 files changed

+85
-6
lines changed

Documentation/git-fast-export.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,13 @@ marks the same across runs.
129129
for intermediary filters (e.g. for rewriting commit messages
130130
which refer to older commits, or for stripping blobs by id).
131131

132+
--reencode=(yes|no|abort)::
133+
Specify how to handle `encoding` header in commit objects. When
134+
asking to 'abort' (which is the default), this program will die
135+
when encountering such a commit object. With 'yes', the commit
136+
message will be reencoded into UTF-8. With 'no', the original
137+
encoding will be preserved.
138+
132139
--refspec::
133140
Apply the specified refspec to each ref exported. Multiple of them can
134141
be specified.

builtin/fast-export.c

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ static const char *fast_export_usage[] = {
3333
static int progress;
3434
static enum { SIGNED_TAG_ABORT, VERBATIM, WARN, WARN_STRIP, STRIP } signed_tag_mode = SIGNED_TAG_ABORT;
3535
static enum { TAG_FILTERING_ABORT, DROP, REWRITE } tag_of_filtered_mode = TAG_FILTERING_ABORT;
36+
static enum { REENCODE_ABORT, REENCODE_YES, REENCODE_NO } reencode_mode = REENCODE_ABORT;
3637
static int fake_missing_tagger;
3738
static int use_done_feature;
3839
static int no_data;
@@ -77,6 +78,31 @@ static int parse_opt_tag_of_filtered_mode(const struct option *opt,
7778
return 0;
7879
}
7980

81+
static int parse_opt_reencode_mode(const struct option *opt,
82+
const char *arg, int unset)
83+
{
84+
if (unset) {
85+
reencode_mode = REENCODE_ABORT;
86+
return 0;
87+
}
88+
89+
switch (git_parse_maybe_bool(arg)) {
90+
case 0:
91+
reencode_mode = REENCODE_NO;
92+
break;
93+
case 1:
94+
reencode_mode = REENCODE_YES;
95+
break;
96+
default:
97+
if (!strcasecmp(arg, "abort"))
98+
reencode_mode = REENCODE_ABORT;
99+
else
100+
return error("Unknown reencoding mode: %s", arg);
101+
}
102+
103+
return 0;
104+
}
105+
80106
static struct decoration idnums;
81107
static uint32_t last_idnum;
82108

@@ -633,10 +659,21 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
633659
}
634660

635661
mark_next_object(&commit->object);
636-
if (anonymize)
662+
if (anonymize) {
637663
reencoded = anonymize_commit_message(message);
638-
else if (!is_encoding_utf8(encoding))
639-
reencoded = reencode_string(message, "UTF-8", encoding);
664+
} else if (encoding) {
665+
switch(reencode_mode) {
666+
case REENCODE_YES:
667+
reencoded = reencode_string(message, "UTF-8", encoding);
668+
break;
669+
case REENCODE_NO:
670+
break;
671+
case REENCODE_ABORT:
672+
die("Encountered commit-specific encoding %s in commit "
673+
"%s; use --reencode=[yes|no] to handle it",
674+
encoding, oid_to_hex(&commit->object.oid));
675+
}
676+
}
640677
if (!commit->parents)
641678
printf("reset %s\n", refname);
642679
printf("commit %s\nmark :%"PRIu32"\n", refname, last_idnum);
@@ -1091,6 +1128,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
10911128
OPT_CALLBACK(0, "tag-of-filtered-object", &tag_of_filtered_mode, N_("mode"),
10921129
N_("select handling of tags that tag filtered objects"),
10931130
parse_opt_tag_of_filtered_mode),
1131+
OPT_CALLBACK(0, "reencode", &reencode_mode, N_("mode"),
1132+
N_("select handling of commit messages in an alternate encoding"),
1133+
parse_opt_reencode_mode),
10941134
OPT_STRING(0, "export-marks", &export_filename, N_("file"),
10951135
N_("Dump marks to this file")),
10961136
OPT_STRING(0, "import-marks", &import_filename, N_("file"),

t/t9350-fast-export.sh

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,14 +94,14 @@ test_expect_success 'fast-export --show-original-ids | git fast-import' '
9494
test $MUSS = $(git rev-parse --verify refs/tags/muss)
9595
'
9696

97-
test_expect_success 'iso-8859-7' '
97+
test_expect_success 'reencoding iso-8859-7' '
9898
9999
test_when_finished "git reset --hard HEAD~1" &&
100100
test_config i18n.commitencoding iso-8859-7 &&
101101
test_tick &&
102102
echo rosten >file &&
103103
git commit -s -F "$TEST_DIRECTORY/t9350/simple-iso-8859-7-commit-message.txt" file &&
104-
git fast-export wer^..wer >iso-8859-7.fi &&
104+
git fast-export --reencode=yes wer^..wer >iso-8859-7.fi &&
105105
sed "s/wer/i18n/" iso-8859-7.fi |
106106
(cd new &&
107107
git fast-import &&
@@ -118,13 +118,45 @@ test_expect_success 'iso-8859-7' '
118118
! grep ^encoding actual)
119119
'
120120

121+
test_expect_success 'aborting on iso-8859-7' '
122+
123+
test_when_finished "git reset --hard HEAD~1" &&
124+
test_config i18n.commitencoding iso-8859-7 &&
125+
echo rosten >file &&
126+
git commit -s -F "$TEST_DIRECTORY/t9350/simple-iso-8859-7-commit-message.txt" file &&
127+
test_must_fail git fast-export --reencode=abort wer^..wer >iso-8859-7.fi
128+
'
129+
130+
test_expect_success 'preserving iso-8859-7' '
131+
132+
test_when_finished "git reset --hard HEAD~1" &&
133+
test_config i18n.commitencoding iso-8859-7 &&
134+
echo rosten >file &&
135+
git commit -s -F "$TEST_DIRECTORY/t9350/simple-iso-8859-7-commit-message.txt" file &&
136+
git fast-export --reencode=no wer^..wer >iso-8859-7.fi &&
137+
sed "s/wer/i18n-no-recoding/" iso-8859-7.fi |
138+
(cd new &&
139+
git fast-import &&
140+
# The commit object, if not re-encoded, is 240 bytes.
141+
# Removing the "encoding iso-8859-7\n" header would drops 20
142+
# bytes. Re-encoding the Pi character from \xF0 (\360) in
143+
# iso-8859-7 to \xCF\x80 (\317\200) in UTF-8 adds a byte.
144+
# Check for the expected size...
145+
test 240 -eq "$(git cat-file -s i18n-no-recoding)" &&
146+
# ...as well as the expected byte.
147+
git cat-file commit i18n-no-recoding >actual &&
148+
grep $(printf "\360") actual &&
149+
# Also make sure the commit has the "encoding" header
150+
grep ^encoding actual)
151+
'
152+
121153
test_expect_success 'encoding preserved if reencoding fails' '
122154
123155
test_when_finished "git reset --hard HEAD~1" &&
124156
test_config i18n.commitencoding iso-8859-7 &&
125157
echo rosten >file &&
126158
git commit -s -F "$TEST_DIRECTORY/t9350/broken-iso-8859-7-commit-message.txt" file &&
127-
git fast-export wer^..wer >iso-8859-7.fi &&
159+
git fast-export --reencode=yes wer^..wer >iso-8859-7.fi &&
128160
sed "s/wer/i18n-invalid/" iso-8859-7.fi |
129161
(cd new &&
130162
git fast-import &&

0 commit comments

Comments
 (0)