]>
Commit | Line | Data |
---|---|---|
4df096a5 FBH |
1 | /* |
2 | * Copyright (c) 2006 Franck Bui-Huu | |
3 | * Copyright (c) 2006 Rene Scharfe | |
4 | */ | |
4df096a5 FBH |
5 | #include "cache.h" |
6 | #include "builtin.h" | |
7 | #include "archive.h" | |
8 | #include "commit.h" | |
9 | #include "tree-walk.h" | |
10 | #include "exec_cmd.h" | |
11 | #include "pkt-line.h" | |
23d6d112 | 12 | #include "sideband.h" |
8460b2fc | 13 | #include "attr.h" |
4df096a5 FBH |
14 | |
15 | static const char archive_usage[] = \ | |
e0ffb248 | 16 | "git-archive --format=<fmt> [--prefix=<prefix>/] [--verbose] [<extra>] <tree-ish> [path...]"; |
4df096a5 | 17 | |
6c2f207b SP |
18 | static struct archiver_desc |
19 | { | |
20 | const char *name; | |
21 | write_archive_fn_t write_archive; | |
22 | parse_extra_args_fn_t parse_extra; | |
23 | } archivers[] = { | |
24 | { "tar", write_tar_archive, NULL }, | |
25 | { "zip", write_zip_archive, parse_extra_zip_args }, | |
4df096a5 FBH |
26 | }; |
27 | ||
37f94436 | 28 | static int run_remote_archiver(const char *remote, int argc, |
4df096a5 FBH |
29 | const char **argv) |
30 | { | |
23d6d112 | 31 | char *url, buf[LARGE_PACKET_MAX]; |
4df096a5 | 32 | int fd[2], i, len, rv; |
98158e9c | 33 | struct child_process *conn; |
fe5ab763 JH |
34 | const char *exec = "git-upload-archive"; |
35 | int exec_at = 0; | |
4df096a5 | 36 | |
fe5ab763 JH |
37 | for (i = 1; i < argc; i++) { |
38 | const char *arg = argv[i]; | |
599065a3 | 39 | if (!prefixcmp(arg, "--exec=")) { |
fe5ab763 JH |
40 | if (exec_at) |
41 | die("multiple --exec specified"); | |
42 | exec = arg + 7; | |
43 | exec_at = i; | |
44 | break; | |
45 | } | |
46 | } | |
4df096a5 | 47 | |
37f94436 | 48 | url = xstrdup(remote); |
98158e9c | 49 | conn = git_connect(fd, url, exec, 0); |
4df096a5 | 50 | |
fe5ab763 JH |
51 | for (i = 1; i < argc; i++) { |
52 | if (i == exec_at) | |
53 | continue; | |
4df096a5 | 54 | packet_write(fd[1], "argument %s\n", argv[i]); |
fe5ab763 | 55 | } |
4df096a5 FBH |
56 | packet_flush(fd[1]); |
57 | ||
58 | len = packet_read_line(fd[0], buf, sizeof(buf)); | |
59 | if (!len) | |
60 | die("git-archive: expected ACK/NAK, got EOF"); | |
61 | if (buf[len-1] == '\n') | |
62 | buf[--len] = 0; | |
63 | if (strcmp(buf, "ACK")) { | |
cc44c765 | 64 | if (len > 5 && !prefixcmp(buf, "NACK ")) |
4df096a5 FBH |
65 | die("git-archive: NACK %s", buf + 5); |
66 | die("git-archive: protocol error"); | |
67 | } | |
68 | ||
69 | len = packet_read_line(fd[0], buf, sizeof(buf)); | |
70 | if (len) | |
71 | die("git-archive: expected a flush"); | |
72 | ||
73 | /* Now, start reading from fd[0] and spit it out to stdout */ | |
9ac13ec9 | 74 | rv = recv_sideband("archive", fd[0], 1, 2); |
4df096a5 | 75 | close(fd[0]); |
ec587fde | 76 | close(fd[1]); |
98158e9c | 77 | rv |= finish_connect(conn); |
4df096a5 FBH |
78 | |
79 | return !!rv; | |
80 | } | |
81 | ||
5ecd293d PH |
82 | static void format_subst(const struct commit *commit, |
83 | const char *src, size_t len, | |
84 | struct strbuf *buf) | |
df4a394f | 85 | { |
5ecd293d | 86 | char *to_free = NULL; |
674d1727 PH |
87 | struct strbuf fmt; |
88 | ||
5ecd293d | 89 | if (src == buf->buf) |
b315c5c0 | 90 | to_free = strbuf_detach(buf, NULL); |
674d1727 | 91 | strbuf_init(&fmt, 0); |
df4a394f RS |
92 | for (;;) { |
93 | const char *b, *c; | |
df4a394f | 94 | |
5ecd293d PH |
95 | b = memmem(src, len, "$Format:", 8); |
96 | if (!b || src + len < b + 9) | |
df4a394f RS |
97 | break; |
98 | c = memchr(b + 8, '$', len - 8); | |
99 | if (!c) | |
100 | break; | |
101 | ||
674d1727 PH |
102 | strbuf_reset(&fmt); |
103 | strbuf_add(&fmt, b + 8, c - b - 8); | |
104 | ||
5ecd293d PH |
105 | strbuf_add(buf, src, b - src); |
106 | format_commit_message(commit, fmt.buf, buf); | |
107 | len -= c + 1 - src; | |
108 | src = c + 1; | |
df4a394f | 109 | } |
5ecd293d | 110 | strbuf_add(buf, src, len); |
674d1727 | 111 | strbuf_release(&fmt); |
5ecd293d | 112 | free(to_free); |
df4a394f RS |
113 | } |
114 | ||
5ecd293d PH |
115 | static int convert_to_archive(const char *path, |
116 | const void *src, size_t len, | |
117 | struct strbuf *buf, | |
118 | const struct commit *commit) | |
8460b2fc | 119 | { |
38c9c9b7 | 120 | static struct git_attr *attr_export_subst; |
8460b2fc | 121 | struct git_attr_check check[1]; |
8460b2fc RS |
122 | |
123 | if (!commit) | |
5ecd293d | 124 | return 0; |
8460b2fc | 125 | |
5ecd293d PH |
126 | if (!attr_export_subst) |
127 | attr_export_subst = git_attr("export-subst", 12); | |
8460b2fc | 128 | |
38c9c9b7 | 129 | check[0].attr = attr_export_subst; |
8460b2fc | 130 | if (git_checkattr(path, ARRAY_SIZE(check), check)) |
5ecd293d | 131 | return 0; |
8460b2fc | 132 | if (!ATTR_TRUE(check[0].value)) |
5ecd293d | 133 | return 0; |
8460b2fc | 134 | |
5ecd293d PH |
135 | format_subst(commit, src, len, buf); |
136 | return 1; | |
8460b2fc RS |
137 | } |
138 | ||
139 | void *sha1_file_to_archive(const char *path, const unsigned char *sha1, | |
140 | unsigned int mode, enum object_type *type, | |
5ecd293d | 141 | unsigned long *sizep, |
8460b2fc RS |
142 | const struct commit *commit) |
143 | { | |
5ecd293d | 144 | void *buffer; |
8460b2fc | 145 | |
5ecd293d | 146 | buffer = read_sha1_file(sha1, type, sizep); |
8460b2fc | 147 | if (buffer && S_ISREG(mode)) { |
5ecd293d | 148 | struct strbuf buf; |
c32f749f | 149 | size_t size = 0; |
5ecd293d PH |
150 | |
151 | strbuf_init(&buf, 0); | |
152 | strbuf_attach(&buf, buffer, *sizep, *sizep + 1); | |
153 | convert_to_working_tree(path, buf.buf, buf.len, &buf); | |
154 | convert_to_archive(path, buf.buf, buf.len, &buf, commit); | |
c32f749f RS |
155 | buffer = strbuf_detach(&buf, &size); |
156 | *sizep = size; | |
8460b2fc RS |
157 | } |
158 | ||
159 | return buffer; | |
160 | } | |
161 | ||
4df096a5 FBH |
162 | static int init_archiver(const char *name, struct archiver *ar) |
163 | { | |
164 | int rv = -1, i; | |
165 | ||
166 | for (i = 0; i < ARRAY_SIZE(archivers); i++) { | |
167 | if (!strcmp(name, archivers[i].name)) { | |
6c2f207b SP |
168 | memset(ar, 0, sizeof(*ar)); |
169 | ar->name = archivers[i].name; | |
170 | ar->write_archive = archivers[i].write_archive; | |
171 | ar->parse_extra = archivers[i].parse_extra; | |
4df096a5 FBH |
172 | rv = 0; |
173 | break; | |
174 | } | |
175 | } | |
176 | return rv; | |
177 | } | |
178 | ||
179 | void parse_pathspec_arg(const char **pathspec, struct archiver_args *ar_args) | |
180 | { | |
181 | ar_args->pathspec = get_pathspec(ar_args->base, pathspec); | |
182 | } | |
183 | ||
184 | void parse_treeish_arg(const char **argv, struct archiver_args *ar_args, | |
185 | const char *prefix) | |
186 | { | |
187 | const char *name = argv[0]; | |
188 | const unsigned char *commit_sha1; | |
189 | time_t archive_time; | |
190 | struct tree *tree; | |
8460b2fc | 191 | const struct commit *commit; |
4df096a5 FBH |
192 | unsigned char sha1[20]; |
193 | ||
194 | if (get_sha1(name, sha1)) | |
195 | die("Not a valid object name"); | |
196 | ||
197 | commit = lookup_commit_reference_gently(sha1, 1); | |
198 | if (commit) { | |
199 | commit_sha1 = commit->object.sha1; | |
200 | archive_time = commit->date; | |
201 | } else { | |
202 | commit_sha1 = NULL; | |
203 | archive_time = time(NULL); | |
204 | } | |
205 | ||
206 | tree = parse_tree_indirect(sha1); | |
207 | if (tree == NULL) | |
208 | die("not a tree object"); | |
209 | ||
210 | if (prefix) { | |
211 | unsigned char tree_sha1[20]; | |
212 | unsigned int mode; | |
213 | int err; | |
214 | ||
215 | err = get_tree_entry(tree->object.sha1, prefix, | |
216 | tree_sha1, &mode); | |
217 | if (err || !S_ISDIR(mode)) | |
218 | die("current working directory is untracked"); | |
219 | ||
4df096a5 FBH |
220 | tree = parse_tree_indirect(tree_sha1); |
221 | } | |
222 | ar_args->tree = tree; | |
223 | ar_args->commit_sha1 = commit_sha1; | |
8460b2fc | 224 | ar_args->commit = commit; |
4df096a5 FBH |
225 | ar_args->time = archive_time; |
226 | } | |
227 | ||
4df096a5 FBH |
228 | int parse_archive_args(int argc, const char **argv, struct archiver *ar) |
229 | { | |
230 | const char *extra_argv[MAX_EXTRA_ARGS]; | |
231 | int extra_argc = 0; | |
8ff21b1a | 232 | const char *format = "tar"; |
4df096a5 | 233 | const char *base = ""; |
e0ffb248 | 234 | int verbose = 0; |
4df096a5 FBH |
235 | int i; |
236 | ||
237 | for (i = 1; i < argc; i++) { | |
238 | const char *arg = argv[i]; | |
239 | ||
240 | if (!strcmp(arg, "--list") || !strcmp(arg, "-l")) { | |
37f94436 JH |
241 | for (i = 0; i < ARRAY_SIZE(archivers); i++) |
242 | printf("%s\n", archivers[i].name); | |
243 | exit(0); | |
4df096a5 | 244 | } |
e0ffb248 JH |
245 | if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) { |
246 | verbose = 1; | |
247 | continue; | |
248 | } | |
cc44c765 | 249 | if (!prefixcmp(arg, "--format=")) { |
4df096a5 FBH |
250 | format = arg + 9; |
251 | continue; | |
252 | } | |
cc44c765 | 253 | if (!prefixcmp(arg, "--prefix=")) { |
4df096a5 FBH |
254 | base = arg + 9; |
255 | continue; | |
256 | } | |
4df096a5 FBH |
257 | if (!strcmp(arg, "--")) { |
258 | i++; | |
259 | break; | |
260 | } | |
261 | if (arg[0] == '-') { | |
262 | if (extra_argc > MAX_EXTRA_ARGS - 1) | |
263 | die("Too many extra options"); | |
264 | extra_argv[extra_argc++] = arg; | |
265 | continue; | |
266 | } | |
267 | break; | |
268 | } | |
269 | ||
37f94436 JH |
270 | /* We need at least one parameter -- tree-ish */ |
271 | if (argc - 1 < i) | |
4df096a5 | 272 | usage(archive_usage); |
4df096a5 FBH |
273 | if (init_archiver(format, ar) < 0) |
274 | die("Unknown archive format '%s'", format); | |
275 | ||
37f94436 JH |
276 | if (extra_argc) { |
277 | if (!ar->parse_extra) | |
2232c0c6 RS |
278 | die("'%s' format does not handle %s", |
279 | ar->name, extra_argv[0]); | |
4df096a5 FBH |
280 | ar->args.extra = ar->parse_extra(extra_argc, extra_argv); |
281 | } | |
e0ffb248 | 282 | ar->args.verbose = verbose; |
4df096a5 FBH |
283 | ar->args.base = base; |
284 | ||
285 | return i; | |
286 | } | |
287 | ||
d751864c | 288 | static const char *extract_remote_arg(int *ac, const char **av) |
37f94436 JH |
289 | { |
290 | int ix, iy, cnt = *ac; | |
291 | int no_more_options = 0; | |
292 | const char *remote = NULL; | |
293 | ||
294 | for (ix = iy = 1; ix < cnt; ix++) { | |
295 | const char *arg = av[ix]; | |
296 | if (!strcmp(arg, "--")) | |
297 | no_more_options = 1; | |
298 | if (!no_more_options) { | |
cc44c765 | 299 | if (!prefixcmp(arg, "--remote=")) { |
37f94436 JH |
300 | if (remote) |
301 | die("Multiple --remote specified"); | |
302 | remote = arg + 9; | |
303 | continue; | |
304 | } | |
305 | if (arg[0] != '-') | |
306 | no_more_options = 1; | |
307 | } | |
308 | if (ix != iy) | |
309 | av[iy] = arg; | |
310 | iy++; | |
311 | } | |
312 | if (remote) { | |
313 | av[--cnt] = NULL; | |
314 | *ac = cnt; | |
315 | } | |
316 | return remote; | |
317 | } | |
318 | ||
4df096a5 FBH |
319 | int cmd_archive(int argc, const char **argv, const char *prefix) |
320 | { | |
321 | struct archiver ar; | |
322 | int tree_idx; | |
37f94436 | 323 | const char *remote = NULL; |
4df096a5 | 324 | |
d751864c | 325 | remote = extract_remote_arg(&argc, argv); |
37f94436 JH |
326 | if (remote) |
327 | return run_remote_archiver(remote, argc, argv); | |
4df096a5 | 328 | |
aa909861 | 329 | setvbuf(stderr, NULL, _IOLBF, BUFSIZ); |
8142f603 | 330 | |
37f94436 JH |
331 | memset(&ar, 0, sizeof(ar)); |
332 | tree_idx = parse_archive_args(argc, argv, &ar); | |
265d5280 RS |
333 | if (prefix == NULL) |
334 | prefix = setup_git_directory(); | |
4df096a5 FBH |
335 | |
336 | argv += tree_idx; | |
337 | parse_treeish_arg(argv, &ar.args, prefix); | |
338 | parse_pathspec_arg(argv + 1, &ar.args); | |
339 | ||
340 | return ar.write_archive(&ar.args); | |
341 | } |