]>
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" |
4df096a5 FBH |
13 | |
14 | static const char archive_usage[] = \ | |
e0ffb248 | 15 | "git-archive --format=<fmt> [--prefix=<prefix>/] [--verbose] [<extra>] <tree-ish> [path...]"; |
4df096a5 | 16 | |
6c2f207b SP |
17 | static struct archiver_desc |
18 | { | |
19 | const char *name; | |
20 | write_archive_fn_t write_archive; | |
21 | parse_extra_args_fn_t parse_extra; | |
22 | } archivers[] = { | |
23 | { "tar", write_tar_archive, NULL }, | |
24 | { "zip", write_zip_archive, parse_extra_zip_args }, | |
4df096a5 FBH |
25 | }; |
26 | ||
37f94436 | 27 | static int run_remote_archiver(const char *remote, int argc, |
4df096a5 FBH |
28 | const char **argv) |
29 | { | |
23d6d112 | 30 | char *url, buf[LARGE_PACKET_MAX]; |
4df096a5 FBH |
31 | int fd[2], i, len, rv; |
32 | pid_t pid; | |
fe5ab763 JH |
33 | const char *exec = "git-upload-archive"; |
34 | int exec_at = 0; | |
4df096a5 | 35 | |
fe5ab763 JH |
36 | for (i = 1; i < argc; i++) { |
37 | const char *arg = argv[i]; | |
38 | if (!strncmp("--exec=", arg, 7)) { | |
39 | if (exec_at) | |
40 | die("multiple --exec specified"); | |
41 | exec = arg + 7; | |
42 | exec_at = i; | |
43 | break; | |
44 | } | |
45 | } | |
4df096a5 | 46 | |
37f94436 | 47 | url = xstrdup(remote); |
fe5ab763 | 48 | pid = git_connect(fd, url, exec); |
4df096a5 FBH |
49 | if (pid < 0) |
50 | return pid; | |
51 | ||
fe5ab763 JH |
52 | for (i = 1; i < argc; i++) { |
53 | if (i == exec_at) | |
54 | continue; | |
4df096a5 | 55 | packet_write(fd[1], "argument %s\n", argv[i]); |
fe5ab763 | 56 | } |
4df096a5 FBH |
57 | packet_flush(fd[1]); |
58 | ||
59 | len = packet_read_line(fd[0], buf, sizeof(buf)); | |
60 | if (!len) | |
61 | die("git-archive: expected ACK/NAK, got EOF"); | |
62 | if (buf[len-1] == '\n') | |
63 | buf[--len] = 0; | |
64 | if (strcmp(buf, "ACK")) { | |
65 | if (len > 5 && !strncmp(buf, "NACK ", 5)) | |
66 | die("git-archive: NACK %s", buf + 5); | |
67 | die("git-archive: protocol error"); | |
68 | } | |
69 | ||
70 | len = packet_read_line(fd[0], buf, sizeof(buf)); | |
71 | if (len) | |
72 | die("git-archive: expected a flush"); | |
73 | ||
74 | /* Now, start reading from fd[0] and spit it out to stdout */ | |
9ac13ec9 | 75 | rv = recv_sideband("archive", fd[0], 1, 2); |
4df096a5 | 76 | close(fd[0]); |
ec587fde | 77 | close(fd[1]); |
4df096a5 FBH |
78 | rv |= finish_connect(pid); |
79 | ||
80 | return !!rv; | |
81 | } | |
82 | ||
83 | static int init_archiver(const char *name, struct archiver *ar) | |
84 | { | |
85 | int rv = -1, i; | |
86 | ||
87 | for (i = 0; i < ARRAY_SIZE(archivers); i++) { | |
88 | if (!strcmp(name, archivers[i].name)) { | |
6c2f207b SP |
89 | memset(ar, 0, sizeof(*ar)); |
90 | ar->name = archivers[i].name; | |
91 | ar->write_archive = archivers[i].write_archive; | |
92 | ar->parse_extra = archivers[i].parse_extra; | |
4df096a5 FBH |
93 | rv = 0; |
94 | break; | |
95 | } | |
96 | } | |
97 | return rv; | |
98 | } | |
99 | ||
100 | void parse_pathspec_arg(const char **pathspec, struct archiver_args *ar_args) | |
101 | { | |
102 | ar_args->pathspec = get_pathspec(ar_args->base, pathspec); | |
103 | } | |
104 | ||
105 | void parse_treeish_arg(const char **argv, struct archiver_args *ar_args, | |
106 | const char *prefix) | |
107 | { | |
108 | const char *name = argv[0]; | |
109 | const unsigned char *commit_sha1; | |
110 | time_t archive_time; | |
111 | struct tree *tree; | |
112 | struct commit *commit; | |
113 | unsigned char sha1[20]; | |
114 | ||
115 | if (get_sha1(name, sha1)) | |
116 | die("Not a valid object name"); | |
117 | ||
118 | commit = lookup_commit_reference_gently(sha1, 1); | |
119 | if (commit) { | |
120 | commit_sha1 = commit->object.sha1; | |
121 | archive_time = commit->date; | |
122 | } else { | |
123 | commit_sha1 = NULL; | |
124 | archive_time = time(NULL); | |
125 | } | |
126 | ||
127 | tree = parse_tree_indirect(sha1); | |
128 | if (tree == NULL) | |
129 | die("not a tree object"); | |
130 | ||
131 | if (prefix) { | |
132 | unsigned char tree_sha1[20]; | |
133 | unsigned int mode; | |
134 | int err; | |
135 | ||
136 | err = get_tree_entry(tree->object.sha1, prefix, | |
137 | tree_sha1, &mode); | |
138 | if (err || !S_ISDIR(mode)) | |
139 | die("current working directory is untracked"); | |
140 | ||
4df096a5 FBH |
141 | tree = parse_tree_indirect(tree_sha1); |
142 | } | |
143 | ar_args->tree = tree; | |
144 | ar_args->commit_sha1 = commit_sha1; | |
145 | ar_args->time = archive_time; | |
146 | } | |
147 | ||
4df096a5 FBH |
148 | int parse_archive_args(int argc, const char **argv, struct archiver *ar) |
149 | { | |
150 | const char *extra_argv[MAX_EXTRA_ARGS]; | |
151 | int extra_argc = 0; | |
152 | const char *format = NULL; /* might want to default to "tar" */ | |
4df096a5 | 153 | const char *base = ""; |
e0ffb248 | 154 | int verbose = 0; |
4df096a5 FBH |
155 | int i; |
156 | ||
157 | for (i = 1; i < argc; i++) { | |
158 | const char *arg = argv[i]; | |
159 | ||
160 | if (!strcmp(arg, "--list") || !strcmp(arg, "-l")) { | |
37f94436 JH |
161 | for (i = 0; i < ARRAY_SIZE(archivers); i++) |
162 | printf("%s\n", archivers[i].name); | |
163 | exit(0); | |
4df096a5 | 164 | } |
e0ffb248 JH |
165 | if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) { |
166 | verbose = 1; | |
167 | continue; | |
168 | } | |
4df096a5 FBH |
169 | if (!strncmp(arg, "--format=", 9)) { |
170 | format = arg + 9; | |
171 | continue; | |
172 | } | |
173 | if (!strncmp(arg, "--prefix=", 9)) { | |
174 | base = arg + 9; | |
175 | continue; | |
176 | } | |
4df096a5 FBH |
177 | if (!strcmp(arg, "--")) { |
178 | i++; | |
179 | break; | |
180 | } | |
181 | if (arg[0] == '-') { | |
182 | if (extra_argc > MAX_EXTRA_ARGS - 1) | |
183 | die("Too many extra options"); | |
184 | extra_argv[extra_argc++] = arg; | |
185 | continue; | |
186 | } | |
187 | break; | |
188 | } | |
189 | ||
37f94436 JH |
190 | /* We need at least one parameter -- tree-ish */ |
191 | if (argc - 1 < i) | |
4df096a5 FBH |
192 | usage(archive_usage); |
193 | if (!format) | |
194 | die("You must specify an archive format"); | |
195 | if (init_archiver(format, ar) < 0) | |
196 | die("Unknown archive format '%s'", format); | |
197 | ||
37f94436 JH |
198 | if (extra_argc) { |
199 | if (!ar->parse_extra) | |
2232c0c6 RS |
200 | die("'%s' format does not handle %s", |
201 | ar->name, extra_argv[0]); | |
4df096a5 FBH |
202 | ar->args.extra = ar->parse_extra(extra_argc, extra_argv); |
203 | } | |
e0ffb248 | 204 | ar->args.verbose = verbose; |
4df096a5 FBH |
205 | ar->args.base = base; |
206 | ||
207 | return i; | |
208 | } | |
209 | ||
d751864c | 210 | static const char *extract_remote_arg(int *ac, const char **av) |
37f94436 JH |
211 | { |
212 | int ix, iy, cnt = *ac; | |
213 | int no_more_options = 0; | |
214 | const char *remote = NULL; | |
215 | ||
216 | for (ix = iy = 1; ix < cnt; ix++) { | |
217 | const char *arg = av[ix]; | |
218 | if (!strcmp(arg, "--")) | |
219 | no_more_options = 1; | |
220 | if (!no_more_options) { | |
221 | if (!strncmp(arg, "--remote=", 9)) { | |
222 | if (remote) | |
223 | die("Multiple --remote specified"); | |
224 | remote = arg + 9; | |
225 | continue; | |
226 | } | |
227 | if (arg[0] != '-') | |
228 | no_more_options = 1; | |
229 | } | |
230 | if (ix != iy) | |
231 | av[iy] = arg; | |
232 | iy++; | |
233 | } | |
234 | if (remote) { | |
235 | av[--cnt] = NULL; | |
236 | *ac = cnt; | |
237 | } | |
238 | return remote; | |
239 | } | |
240 | ||
4df096a5 FBH |
241 | int cmd_archive(int argc, const char **argv, const char *prefix) |
242 | { | |
243 | struct archiver ar; | |
244 | int tree_idx; | |
37f94436 | 245 | const char *remote = NULL; |
4df096a5 | 246 | |
d751864c | 247 | remote = extract_remote_arg(&argc, argv); |
37f94436 JH |
248 | if (remote) |
249 | return run_remote_archiver(remote, argc, argv); | |
4df096a5 | 250 | |
aa909861 | 251 | setvbuf(stderr, NULL, _IOLBF, BUFSIZ); |
8142f603 | 252 | |
37f94436 JH |
253 | memset(&ar, 0, sizeof(ar)); |
254 | tree_idx = parse_archive_args(argc, argv, &ar); | |
4df096a5 FBH |
255 | if (prefix == NULL) |
256 | prefix = setup_git_directory(); | |
257 | ||
258 | argv += tree_idx; | |
259 | parse_treeish_arg(argv, &ar.args, prefix); | |
260 | parse_pathspec_arg(argv + 1, &ar.args); | |
261 | ||
262 | return ar.write_archive(&ar.args); | |
263 | } |