Skip to content

Commit 56baa61

Browse files
peffgitster
authored andcommitted
archive: move file extension format-guessing lower
The process for guessing an archive output format based on the filename is something like this: a. parse --output in cmd_archive; check the filename against a static set of mapping heuristics (right now it just matches ".zip" for zip files). b. if found, stick a fake "--format=zip" at the beginning of the arguments list (if the user did specify a --format manually, the later option will override our fake one) c. if it's a remote call, ship the arguments to the remote (including the fake), which will call write_archive on their end d. if it's local, ship the arguments to write_archive locally There are two problems: 1. The set of mappings is static and at too high a level. The write_archive level is going to check config for user-defined formats, some of which will specify extensions. We need to delay lookup until those are parsed, so we can match against them. 2. For a remote archive call, our set of mappings (or formats) may not match the remote side's. This is OK in practice right now, because all versions of git understand "zip" and "tar". But as new formats are added, there is going to be a mismatch between what the client can do and what the remote server can do. To fix (1), this patch refactors the location guessing to happen at the write_archive level, instead of the cmd_archive level. So instead of sticking a fake --format field in the argv list, we actually pass a "name hint" down the callchain; this hint is used at the appropriate time to guess the format (if one hasn't been given already). This patch leaves (2) unfixed. The name_hint is converted to a "--format" option as before, and passed to the remote. This means the local side's idea of how extensions map to formats will take precedence. Another option would be to pass the name hint to the remote side and let the remote choose. This isn't a good idea for two reasons: 1. There's no room in the protocol for passing that information. We can pass a new argument, but older versions of git on the server will choke on it. 2. Letting the remote side decide creates a silent inconsistency in user experience. Consider the case that the locally installed git knows about the "tar.gz" format, but a remote server doesn't. Running "git archive -o foo.tar.gz" will use the tar.gz format. If we use --remote, and the local side chooses the format, then we send "--format=tar.gz" to the remote, which will complain about the unknown format. But if we let the remote side choose the format, then it will realize that it doesn't know about "tar.gz" and output uncompressed tar without even issuing a warning. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 4d7c989 commit 56baa61

File tree

4 files changed

+41
-41
lines changed

4 files changed

+41
-41
lines changed

archive.c

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -298,9 +298,10 @@ static void parse_treeish_arg(const char **argv,
298298
PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_HIDDEN, NULL, (p) }
299299

300300
static int parse_archive_args(int argc, const char **argv,
301-
const struct archiver **ar, struct archiver_args *args)
301+
const struct archiver **ar, struct archiver_args *args,
302+
const char *name_hint)
302303
{
303-
const char *format = "tar";
304+
const char *format = NULL;
304305
const char *base = NULL;
305306
const char *remote = NULL;
306307
const char *exec = NULL;
@@ -359,6 +360,11 @@ static int parse_archive_args(int argc, const char **argv,
359360
exit(0);
360361
}
361362

363+
if (!format && name_hint)
364+
format = archive_format_from_filename(name_hint);
365+
if (!format)
366+
format = "tar";
367+
362368
/* We need at least one parameter -- tree-ish */
363369
if (argc < 1)
364370
usage_with_options(archive_usage, opts);
@@ -384,7 +390,7 @@ static int parse_archive_args(int argc, const char **argv,
384390
}
385391

386392
int write_archive(int argc, const char **argv, const char *prefix,
387-
int setup_prefix)
393+
int setup_prefix, const char *name_hint)
388394
{
389395
int nongit = 0;
390396
const struct archiver *ar = NULL;
@@ -397,7 +403,7 @@ int write_archive(int argc, const char **argv, const char *prefix,
397403
init_tar_archiver();
398404
init_zip_archiver();
399405

400-
argc = parse_archive_args(argc, argv, &ar, &args);
406+
argc = parse_archive_args(argc, argv, &ar, &args, name_hint);
401407
if (nongit) {
402408
/*
403409
* We know this will die() with an error, so we could just
@@ -412,3 +418,14 @@ int write_archive(int argc, const char **argv, const char *prefix,
412418

413419
return ar->write_archive(ar, &args);
414420
}
421+
422+
const char *archive_format_from_filename(const char *filename)
423+
{
424+
const char *ext = strrchr(filename, '.');
425+
if (!ext)
426+
return NULL;
427+
ext++;
428+
if (!strcasecmp(ext, "zip"))
429+
return "zip";
430+
return NULL;
431+
}

archive.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ extern void init_zip_archiver(void);
2929
typedef int (*write_archive_entry_fn_t)(struct archiver_args *args, const unsigned char *sha1, const char *path, size_t pathlen, unsigned int mode, void *buffer, unsigned long size);
3030

3131
extern int write_archive_entries(struct archiver_args *args, write_archive_entry_fn_t write_entry);
32-
extern int write_archive(int argc, const char **argv, const char *prefix, int setup_prefix);
32+
extern int write_archive(int argc, const char **argv, const char *prefix, int setup_prefix, const char *name_hint);
33+
34+
const char *archive_format_from_filename(const char *filename);
3335

3436
#endif /* ARCHIVE_H */

builtin/archive.c

Lines changed: 16 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ static void create_output_file(const char *output_file)
2424
}
2525

2626
static int run_remote_archiver(int argc, const char **argv,
27-
const char *remote, const char *exec)
27+
const char *remote, const char *exec,
28+
const char *name_hint)
2829
{
2930
char buf[LARGE_PACKET_MAX];
3031
int fd[2], i, len, rv;
@@ -37,6 +38,17 @@ static int run_remote_archiver(int argc, const char **argv,
3738
transport = transport_get(_remote, _remote->url[0]);
3839
transport_connect(transport, "git-upload-archive", exec, fd);
3940

41+
/*
42+
* Inject a fake --format field at the beginning of the
43+
* arguments, with the format inferred from our output
44+
* filename. This way explicit --format options can override
45+
* it.
46+
*/
47+
if (name_hint) {
48+
const char *format = archive_format_from_filename(name_hint);
49+
if (format)
50+
packet_write(fd[1], "argument --format=%s\n", format);
51+
}
4052
for (i = 1; i < argc; i++)
4153
packet_write(fd[1], "argument %s\n", argv[i]);
4254
packet_flush(fd[1]);
@@ -63,17 +75,6 @@ static int run_remote_archiver(int argc, const char **argv,
6375
return !!rv;
6476
}
6577

66-
static const char *format_from_name(const char *filename)
67-
{
68-
const char *ext = strrchr(filename, '.');
69-
if (!ext)
70-
return NULL;
71-
ext++;
72-
if (!strcasecmp(ext, "zip"))
73-
return "--format=zip";
74-
return NULL;
75-
}
76-
7778
#define PARSE_OPT_KEEP_ALL ( PARSE_OPT_KEEP_DASHDASH | \
7879
PARSE_OPT_KEEP_ARGV0 | \
7980
PARSE_OPT_KEEP_UNKNOWN | \
@@ -84,7 +85,6 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
8485
const char *exec = "git-upload-archive";
8586
const char *output = NULL;
8687
const char *remote = NULL;
87-
const char *format_option = NULL;
8888
struct option local_opts[] = {
8989
OPT_STRING('o', "output", &output, "file",
9090
"write the archive to this file"),
@@ -98,32 +98,13 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
9898
argc = parse_options(argc, argv, prefix, local_opts, NULL,
9999
PARSE_OPT_KEEP_ALL);
100100

101-
if (output) {
101+
if (output)
102102
create_output_file(output);
103-
format_option = format_from_name(output);
104-
}
105-
106-
/*
107-
* We have enough room in argv[] to muck it in place, because
108-
* --output must have been given on the original command line
109-
* if we get to this point, and parse_options() must have eaten
110-
* it, i.e. we can add back one element to the array.
111-
*
112-
* We add a fake --format option at the beginning, with the
113-
* format inferred from our output filename. This way explicit
114-
* --format options can override it, and the fake option is
115-
* inserted before any "--" that might have been given.
116-
*/
117-
if (format_option) {
118-
memmove(argv + 2, argv + 1, sizeof(*argv) * argc);
119-
argv[1] = format_option;
120-
argv[++argc] = NULL;
121-
}
122103

123104
if (remote)
124-
return run_remote_archiver(argc, argv, remote, exec);
105+
return run_remote_archiver(argc, argv, remote, exec, output);
125106

126107
setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
127108

128-
return write_archive(argc, argv, prefix, 1);
109+
return write_archive(argc, argv, prefix, 1, output);
129110
}

builtin/upload-archive.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ static int run_upload_archive(int argc, const char **argv, const char *prefix)
6464
sent_argv[sent_argc] = NULL;
6565

6666
/* parse all options sent by the client */
67-
return write_archive(sent_argc, sent_argv, prefix, 0);
67+
return write_archive(sent_argc, sent_argv, prefix, 0, NULL);
6868
}
6969

7070
__attribute__((format (printf, 1, 2)))

0 commit comments

Comments
 (0)