fast-import: introduce 'done' command

Add a 'done' command that causes fast-import to stop reading from the stream and exit. If the new --done command line flag was passed on the command line (or a "feature done" declaration included at the start of the stream), make the 'done' command mandatory. So "git fast-import --done"'s input format will be prefix-free, making errors easier to detect when they show up as early termination at some convenient time of the upstream of a pipe writing to fast-import. Another possible application of the 'done' command would to be allow a fast-import stream that is only a small part of a larger encapsulating stream to be easily parsed, leaving the file offset after the "done\n" so the other application can pick up from there. This patch does not teach fast-import to do that --- fast-import still uses buffered input (stdio). Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> Signed-off-by: Sverre Rabbelier <srabbelier@gmail.com> Acked-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Sverre Rabbelier committed Jul 16, 2011 at 15:03 UTC be56862f198d946dc75ac7092606e78a4f2ff1d3
3 files changed +75
Documentation/git-fast-import.txt
+25
@@ -101,6 +101,12 @@ OPTIONS
101 when the `cat-blob` command is encountered in the stream.
102 The default behaviour is to write to `stdout`.
103
104 +--done::
105 + Require a `done` command at the end of the stream.
106 + This option might be useful for detecting errors that
107 + cause the frontend to terminate before it has started to
108 + write a stream.
109 +
110 --export-pack-edges=<file>::
111 After creating a packfile, print a line of data to
112 <file> listing the filename of the packfile and the last
@@ -330,6 +336,11 @@ and control the current import process. More detailed discussion
336 standard output. This command is optional and is not needed
337 to perform an import.
338
339 +`done`::
340 + Marks the end of the stream. This command is optional
341 + unless the `done` feature was requested using the
342 + `--done` command line option or `feature done` command.
343 +
344 `cat-blob`::
345 Causes fast-import to print a blob in 'cat-file --batch'
346 format to the file descriptor set with `--cat-blob-fd` or
@@ -1015,6 +1026,11 @@ notes::
1026 Versions of fast-import not supporting notes will exit
1027 with a message indicating so.
1028
1029 +done::
1030 + Error out if the stream ends without a 'done' command.
1031 + Without this feature, errors causing the frontend to end
1032 + abruptly at a convenient point in the stream can go
1033 + undetected.
1034
1035 `option`
1036 ~~~~~~~~
@@ -1044,6 +1060,15 @@ not be passed as option:
1060 * cat-blob-fd
1061 * force
1062
1063 +`done`
1064 +~~~~~~
1065 +If the `done` feature is not in use, treated as if EOF was read.
1066 +This can be used to tell fast-import to finish early.
1067 +
1068 +If the `--done` command line option or `feature done` command is
1069 +in use, the `done` command is mandatory and marks the end of the
1070 +stream.
1071 +
1072 Crash Reports
1073 -------------
1074 If fast-import is supplied invalid input it will terminate with a
fast-import.c
+8
@@ -354,6 +354,7 @@ static unsigned int cmd_save = 100;
354 static uintmax_t next_mark;
355 static struct strbuf new_data = STRBUF_INIT;
356 static int seen_data_command;
357 +static int require_explicit_termination;
358
359 /* Signal handling */
360 static volatile sig_atomic_t checkpoint_requested;
@@ -3139,6 +3140,8 @@ static int parse_one_feature(const char *feature, int from_stream)
3140 relative_marks_paths = 1;
3141 } else if (!strcmp(feature, "no-relative-marks")) {
3142 relative_marks_paths = 0;
3143 + } else if (!strcmp(feature, "done")) {
3144 + require_explicit_termination = 1;
3145 } else if (!strcmp(feature, "force")) {
3146 force_update = 1;
3147 } else if (!strcmp(feature, "notes") || !strcmp(feature, "ls")) {
@@ -3288,6 +3291,8 @@ int main(int argc, const char **argv)
3291 parse_reset_branch();
3292 else if (!strcmp("checkpoint", command_buf.buf))
3293 parse_checkpoint();
3294 + else if (!strcmp("done", command_buf.buf))
3295 + break;
3296 else if (!prefixcmp(command_buf.buf, "progress "))
3297 parse_progress();
3298 else if (!prefixcmp(command_buf.buf, "feature "))
@@ -3307,6 +3312,9 @@ int main(int argc, const char **argv)
3312 if (!seen_data_command)
3313 parse_argv();
3314
3315 + if (require_explicit_termination && feof(stdin))
3316 + die("stream ends early");
3317 +
3318 end_packfile();
3319
3320 dump_branches();
t/t9300-fast-import.sh
+42
@@ -2197,6 +2197,48 @@ test_expect_success 'R: quiet option results in no stats being output' '
2197 test_cmp empty output
2198 '
2199
2200 +test_expect_success 'R: feature done means terminating "done" is mandatory' '
2201 + echo feature done | test_must_fail git fast-import &&
2202 + test_must_fail git fast-import --done </dev/null
2203 +'
2204 +
2205 +test_expect_success 'R: terminating "done" with trailing gibberish is ok' '
2206 + git fast-import <<-\EOF &&
2207 + feature done
2208 + done
2209 + trailing gibberish
2210 + EOF
2211 + git fast-import <<-\EOF
2212 + done
2213 + more trailing gibberish
2214 + EOF
2215 +'
2216 +
2217 +test_expect_success 'R: terminating "done" within commit' '
2218 + cat >expect <<-\EOF &&
2219 + OBJID
2220 + :000000 100644 OBJID OBJID A hello.c
2221 + :000000 100644 OBJID OBJID A hello2.c
2222 + EOF
2223 + git fast-import <<-EOF &&
2224 + commit refs/heads/done-ends
2225 + committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
2226 + data <<EOT
2227 + Commit terminated by "done" command
2228 + EOT
2229 + M 100644 inline hello.c
2230 + data <<EOT
2231 + Hello, world.
2232 + EOT
2233 + C hello.c hello2.c
2234 + done
2235 + EOF
2236 + git rev-list done-ends |
2237 + git diff-tree -r --stdin --root --always |
2238 + sed -e "s/$_x40/OBJID/g" >actual &&
2239 + test_cmp expect actual
2240 +'
2241 +
2242 cat >input <<EOF
2243 option git non-existing-option
2244 EOF