]>
Commit | Line | Data |
---|---|---|
4df096a5 FBH |
1 | /* |
2 | * Copyright (c) 2006 Franck Bui-Huu | |
3 | * Copyright (c) 2006 Rene Scharfe | |
4 | */ | |
5 | #include <time.h> | |
6 | #include "cache.h" | |
7 | #include "builtin.h" | |
8 | #include "archive.h" | |
9 | #include "commit.h" | |
10 | #include "tree-walk.h" | |
11 | #include "exec_cmd.h" | |
12 | #include "pkt-line.h" | |
13 | ||
14 | static const char archive_usage[] = \ | |
15 | "git-archive --format=<fmt> [--prefix=<prefix>/] [<extra>] <tree-ish> [path...]"; | |
16 | ||
17 | struct archiver archivers[] = { | |
854c4168 RS |
18 | { |
19 | .name = "tar", | |
20 | .write_archive = write_tar_archive, | |
21 | }, | |
22 | { | |
23 | .name = "zip", | |
24 | .write_archive = write_zip_archive, | |
25 | .parse_extra = parse_extra_zip_args, | |
26 | }, | |
4df096a5 FBH |
27 | }; |
28 | ||
29 | static int run_remote_archiver(struct archiver *ar, int argc, | |
30 | const char **argv) | |
31 | { | |
32 | char *url, buf[1024]; | |
33 | int fd[2], i, len, rv; | |
34 | pid_t pid; | |
35 | ||
36 | sprintf(buf, "git-upload-archive"); | |
37 | ||
38 | url = xstrdup(ar->remote); | |
39 | pid = git_connect(fd, url, buf); | |
40 | if (pid < 0) | |
41 | return pid; | |
42 | ||
43 | for (i = 1; i < argc; i++) { | |
44 | if (!strncmp(argv[i], "--remote=", 9)) | |
45 | continue; | |
46 | packet_write(fd[1], "argument %s\n", argv[i]); | |
47 | } | |
48 | packet_flush(fd[1]); | |
49 | ||
50 | len = packet_read_line(fd[0], buf, sizeof(buf)); | |
51 | if (!len) | |
52 | die("git-archive: expected ACK/NAK, got EOF"); | |
53 | if (buf[len-1] == '\n') | |
54 | buf[--len] = 0; | |
55 | if (strcmp(buf, "ACK")) { | |
56 | if (len > 5 && !strncmp(buf, "NACK ", 5)) | |
57 | die("git-archive: NACK %s", buf + 5); | |
58 | die("git-archive: protocol error"); | |
59 | } | |
60 | ||
61 | len = packet_read_line(fd[0], buf, sizeof(buf)); | |
62 | if (len) | |
63 | die("git-archive: expected a flush"); | |
64 | ||
65 | /* Now, start reading from fd[0] and spit it out to stdout */ | |
66 | rv = copy_fd(fd[0], 1); | |
67 | ||
68 | close(fd[0]); | |
69 | rv |= finish_connect(pid); | |
70 | ||
71 | return !!rv; | |
72 | } | |
73 | ||
74 | static int init_archiver(const char *name, struct archiver *ar) | |
75 | { | |
76 | int rv = -1, i; | |
77 | ||
78 | for (i = 0; i < ARRAY_SIZE(archivers); i++) { | |
79 | if (!strcmp(name, archivers[i].name)) { | |
80 | memcpy(ar, &archivers[i], sizeof(struct archiver)); | |
81 | rv = 0; | |
82 | break; | |
83 | } | |
84 | } | |
85 | return rv; | |
86 | } | |
87 | ||
88 | void parse_pathspec_arg(const char **pathspec, struct archiver_args *ar_args) | |
89 | { | |
90 | ar_args->pathspec = get_pathspec(ar_args->base, pathspec); | |
91 | } | |
92 | ||
93 | void parse_treeish_arg(const char **argv, struct archiver_args *ar_args, | |
94 | const char *prefix) | |
95 | { | |
96 | const char *name = argv[0]; | |
97 | const unsigned char *commit_sha1; | |
98 | time_t archive_time; | |
99 | struct tree *tree; | |
100 | struct commit *commit; | |
101 | unsigned char sha1[20]; | |
102 | ||
103 | if (get_sha1(name, sha1)) | |
104 | die("Not a valid object name"); | |
105 | ||
106 | commit = lookup_commit_reference_gently(sha1, 1); | |
107 | if (commit) { | |
108 | commit_sha1 = commit->object.sha1; | |
109 | archive_time = commit->date; | |
110 | } else { | |
111 | commit_sha1 = NULL; | |
112 | archive_time = time(NULL); | |
113 | } | |
114 | ||
115 | tree = parse_tree_indirect(sha1); | |
116 | if (tree == NULL) | |
117 | die("not a tree object"); | |
118 | ||
119 | if (prefix) { | |
120 | unsigned char tree_sha1[20]; | |
121 | unsigned int mode; | |
122 | int err; | |
123 | ||
124 | err = get_tree_entry(tree->object.sha1, prefix, | |
125 | tree_sha1, &mode); | |
126 | if (err || !S_ISDIR(mode)) | |
127 | die("current working directory is untracked"); | |
128 | ||
129 | free(tree); | |
130 | tree = parse_tree_indirect(tree_sha1); | |
131 | } | |
132 | ar_args->tree = tree; | |
133 | ar_args->commit_sha1 = commit_sha1; | |
134 | ar_args->time = archive_time; | |
135 | } | |
136 | ||
137 | static const char *default_parse_extra(struct archiver *ar, | |
138 | const char **argv) | |
139 | { | |
140 | static char msg[64]; | |
141 | ||
142 | snprintf(msg, sizeof(msg) - 4, "'%s' format does not handle %s", | |
143 | ar->name, *argv); | |
144 | ||
145 | return strcat(msg, "..."); | |
146 | } | |
147 | ||
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" */ | |
153 | const char *remote = NULL; | |
154 | const char *base = ""; | |
155 | int list = 0; | |
156 | int i; | |
157 | ||
158 | for (i = 1; i < argc; i++) { | |
159 | const char *arg = argv[i]; | |
160 | ||
161 | if (!strcmp(arg, "--list") || !strcmp(arg, "-l")) { | |
162 | list = 1; | |
163 | continue; | |
164 | } | |
165 | if (!strncmp(arg, "--format=", 9)) { | |
166 | format = arg + 9; | |
167 | continue; | |
168 | } | |
169 | if (!strncmp(arg, "--prefix=", 9)) { | |
170 | base = arg + 9; | |
171 | continue; | |
172 | } | |
173 | if (!strncmp(arg, "--remote=", 9)) { | |
174 | remote = arg + 9; | |
175 | continue; | |
176 | } | |
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 | ||
190 | if (list) { | |
191 | if (!remote) { | |
192 | for (i = 0; i < ARRAY_SIZE(archivers); i++) | |
193 | printf("%s\n", archivers[i].name); | |
194 | exit(0); | |
195 | } | |
196 | die("--list and --remote are mutually exclusive"); | |
197 | } | |
198 | ||
199 | if (argc - i < 1) | |
200 | usage(archive_usage); | |
201 | if (!format) | |
202 | die("You must specify an archive format"); | |
203 | if (init_archiver(format, ar) < 0) | |
204 | die("Unknown archive format '%s'", format); | |
205 | ||
206 | if (extra_argc && !remote) { | |
207 | if (!ar->parse_extra) { | |
208 | die("%s", default_parse_extra(ar, extra_argv)); | |
209 | } | |
210 | ar->args.extra = ar->parse_extra(extra_argc, extra_argv); | |
211 | } | |
212 | ar->remote = remote; | |
213 | ar->args.base = base; | |
214 | ||
215 | return i; | |
216 | } | |
217 | ||
218 | int cmd_archive(int argc, const char **argv, const char *prefix) | |
219 | { | |
220 | struct archiver ar; | |
221 | int tree_idx; | |
222 | ||
223 | tree_idx = parse_archive_args(argc, argv, &ar); | |
224 | ||
225 | if (ar.remote) | |
226 | return run_remote_archiver(&ar, argc, argv); | |
227 | ||
228 | if (prefix == NULL) | |
229 | prefix = setup_git_directory(); | |
230 | ||
231 | argv += tree_idx; | |
232 | parse_treeish_arg(argv, &ar.args, prefix); | |
233 | parse_pathspec_arg(argv + 1, &ar.args); | |
234 | ||
235 | return ar.write_archive(&ar.args); | |
236 | } |