| 1 | #define USE_THE_REPOSITORY_VARIABLE |
| 2 | #include "builtin.h" |
| 3 | #include "abspath.h" |
| 4 | #include "gettext.h" |
| 5 | #include "setup.h" |
| 6 | #include "strvec.h" |
| 7 | #include "parse-options.h" |
| 8 | #include "pkt-line.h" |
| 9 | #include "bundle.h" |
| 10 | |
| 11 | /* |
| 12 | * Basic handler for bundle files to connect repositories via sneakernet. |
| 13 | * Invocation must include action. |
| 14 | * This function can create a bundle or provide information on an existing |
| 15 | * bundle supporting "fetch", "pull", and "ls-remote". |
| 16 | */ |
| 17 | |
| 18 | #define BUILTIN_BUNDLE_CREATE_USAGE \ |
| 19 | N_("git bundle create [-q | --quiet | --progress]\n" \ |
| 20 | " [--version=<version>] <file> <git-rev-list-args>") |
| 21 | #define BUILTIN_BUNDLE_VERIFY_USAGE \ |
| 22 | N_("git bundle verify [-q | --quiet] <file>") |
| 23 | #define BUILTIN_BUNDLE_LIST_HEADS_USAGE \ |
| 24 | N_("git bundle list-heads <file> [<refname>...]") |
| 25 | #define BUILTIN_BUNDLE_UNBUNDLE_USAGE \ |
| 26 | N_("git bundle unbundle [--progress] <file> [<refname>...]") |
| 27 | |
| 28 | static char const * const builtin_bundle_usage[] = { |
| 29 | BUILTIN_BUNDLE_CREATE_USAGE, |
| 30 | BUILTIN_BUNDLE_VERIFY_USAGE, |
| 31 | BUILTIN_BUNDLE_LIST_HEADS_USAGE, |
| 32 | BUILTIN_BUNDLE_UNBUNDLE_USAGE, |
| 33 | NULL, |
| 34 | }; |
| 35 | |
| 36 | static const char * const builtin_bundle_create_usage[] = { |
| 37 | BUILTIN_BUNDLE_CREATE_USAGE, |
| 38 | NULL |
| 39 | }; |
| 40 | |
| 41 | static const char * const builtin_bundle_verify_usage[] = { |
| 42 | BUILTIN_BUNDLE_VERIFY_USAGE, |
| 43 | NULL |
| 44 | }; |
| 45 | |
| 46 | static const char * const builtin_bundle_list_heads_usage[] = { |
| 47 | BUILTIN_BUNDLE_LIST_HEADS_USAGE, |
| 48 | NULL |
| 49 | }; |
| 50 | |
| 51 | static const char * const builtin_bundle_unbundle_usage[] = { |
| 52 | BUILTIN_BUNDLE_UNBUNDLE_USAGE, |
| 53 | NULL |
| 54 | }; |
| 55 | |
| 56 | static int parse_options_cmd_bundle(int argc, |
| 57 | const char **argv, |
| 58 | const char* prefix, |
| 59 | const char * const usagestr[], |
| 60 | const struct option options[], |
| 61 | char **bundle_file) { |
| 62 | argc = parse_options(argc, argv, NULL, options, usagestr, |
| 63 | PARSE_OPT_STOP_AT_NON_OPTION); |
| 64 | if (!argc) |
| 65 | usage_msg_opt(_("need a <file> argument"), usagestr, options); |
| 66 | *bundle_file = prefix_filename_except_for_dash(prefix, argv[0]); |
| 67 | return argc; |
| 68 | } |
| 69 | |
| 70 | static int cmd_bundle_create(int argc, const char **argv, const char *prefix, |
| 71 | struct repository *repo UNUSED) { |
| 72 | struct strvec pack_opts = STRVEC_INIT; |
| 73 | int version = -1; |
| 74 | int ret; |
| 75 | struct option options[] = { |
| 76 | OPT_PASSTHRU_ARGV('q', "quiet", &pack_opts, NULL, |
| 77 | N_("do not show progress meter"), |
| 78 | PARSE_OPT_NOARG), |
| 79 | OPT_PASSTHRU_ARGV(0, "progress", &pack_opts, NULL, |
| 80 | N_("show progress meter"), |
| 81 | PARSE_OPT_NOARG), |
| 82 | OPT_PASSTHRU_ARGV(0, "all-progress", &pack_opts, NULL, |
| 83 | N_("historical; same as --progress"), |
| 84 | PARSE_OPT_NOARG | PARSE_OPT_HIDDEN), |
| 85 | OPT_PASSTHRU_ARGV(0, "all-progress-implied", &pack_opts, NULL, |
| 86 | N_("historical; does nothing"), |
| 87 | PARSE_OPT_NOARG | PARSE_OPT_HIDDEN), |
| 88 | OPT_INTEGER(0, "version", &version, |
| 89 | N_("specify bundle format version")), |
| 90 | OPT_END() |
| 91 | }; |
| 92 | char *bundle_file; |
| 93 | |
| 94 | if (isatty(STDERR_FILENO)) |
| 95 | strvec_push(&pack_opts, "--progress"); |
| 96 | strvec_push(&pack_opts, "--all-progress-implied"); |
| 97 | |
| 98 | argc = parse_options_cmd_bundle(argc, argv, prefix, |
| 99 | builtin_bundle_create_usage, options, &bundle_file); |
| 100 | /* bundle internals use argv[1] as further parameters */ |
| 101 | |
| 102 | if (!startup_info->have_repository) |
| 103 | die(_("Need a repository to create a bundle.")); |
| 104 | ret = !!create_bundle(the_repository, bundle_file, argc, argv, &pack_opts, version); |
| 105 | strvec_clear(&pack_opts); |
| 106 | free(bundle_file); |
| 107 | return ret; |
| 108 | } |
| 109 | |
| 110 | /* |
| 111 | * Similar to read_bundle_header(), but handle "-" as stdin. |
| 112 | */ |
| 113 | static int open_bundle(const char *path, struct bundle_header *header, |
| 114 | const char **name) |
| 115 | { |
| 116 | if (!strcmp(path, "-")) { |
| 117 | if (name) |
| 118 | *name = "<stdin>"; |
| 119 | return read_bundle_header_fd(0, header, "<stdin>"); |
| 120 | } |
| 121 | |
| 122 | if (name) |
| 123 | *name = path; |
| 124 | return read_bundle_header(path, header); |
| 125 | } |
| 126 | |
| 127 | static int cmd_bundle_verify(int argc, const char **argv, const char *prefix, |
| 128 | struct repository *repo UNUSED) { |
| 129 | struct bundle_header header = BUNDLE_HEADER_INIT; |
| 130 | int bundle_fd = -1; |
| 131 | int quiet = 0; |
| 132 | int ret; |
| 133 | struct option options[] = { |
| 134 | OPT_BOOL('q', "quiet", &quiet, |
| 135 | N_("do not show bundle details")), |
| 136 | OPT_END() |
| 137 | }; |
| 138 | char *bundle_file; |
| 139 | const char *name; |
| 140 | |
| 141 | argc = parse_options_cmd_bundle(argc, argv, prefix, |
| 142 | builtin_bundle_verify_usage, options, &bundle_file); |
| 143 | /* bundle internals use argv[1] as further parameters */ |
| 144 | |
| 145 | if (!startup_info->have_repository) { |
| 146 | ret = error(_("need a repository to verify a bundle")); |
| 147 | goto cleanup; |
| 148 | } |
| 149 | |
| 150 | if ((bundle_fd = open_bundle(bundle_file, &header, &name)) < 0) { |
| 151 | ret = 1; |
| 152 | goto cleanup; |
| 153 | } |
| 154 | close(bundle_fd); |
| 155 | if (verify_bundle(the_repository, &header, |
| 156 | quiet ? VERIFY_BUNDLE_QUIET : VERIFY_BUNDLE_VERBOSE)) { |
| 157 | ret = 1; |
| 158 | goto cleanup; |
| 159 | } |
| 160 | |
| 161 | fprintf(stderr, _("%s is okay\n"), name); |
| 162 | ret = 0; |
| 163 | cleanup: |
| 164 | free(bundle_file); |
| 165 | bundle_header_release(&header); |
| 166 | return ret; |
| 167 | } |
| 168 | |
| 169 | static int cmd_bundle_list_heads(int argc, const char **argv, const char *prefix, |
| 170 | struct repository *repo UNUSED) { |
| 171 | struct bundle_header header = BUNDLE_HEADER_INIT; |
| 172 | int bundle_fd = -1; |
| 173 | int ret; |
| 174 | struct option options[] = { |
| 175 | OPT_END() |
| 176 | }; |
| 177 | char *bundle_file; |
| 178 | |
| 179 | argc = parse_options_cmd_bundle(argc, argv, prefix, |
| 180 | builtin_bundle_list_heads_usage, options, &bundle_file); |
| 181 | /* bundle internals use argv[1] as further parameters */ |
| 182 | |
| 183 | if ((bundle_fd = open_bundle(bundle_file, &header, NULL)) < 0) { |
| 184 | ret = 1; |
| 185 | goto cleanup; |
| 186 | } |
| 187 | close(bundle_fd); |
| 188 | ret = !!list_bundle_refs(&header, argc, argv); |
| 189 | cleanup: |
| 190 | free(bundle_file); |
| 191 | bundle_header_release(&header); |
| 192 | return ret; |
| 193 | } |
| 194 | |
| 195 | static int cmd_bundle_unbundle(int argc, const char **argv, const char *prefix, |
| 196 | struct repository *repo UNUSED) { |
| 197 | struct bundle_header header = BUNDLE_HEADER_INIT; |
| 198 | int bundle_fd = -1; |
| 199 | int ret; |
| 200 | int progress = isatty(2); |
| 201 | |
| 202 | struct option options[] = { |
| 203 | OPT_BOOL(0, "progress", &progress, |
| 204 | N_("show progress meter")), |
| 205 | OPT_END() |
| 206 | }; |
| 207 | char *bundle_file; |
| 208 | struct strvec extra_index_pack_args = STRVEC_INIT; |
| 209 | |
| 210 | argc = parse_options_cmd_bundle(argc, argv, prefix, |
| 211 | builtin_bundle_unbundle_usage, options, &bundle_file); |
| 212 | /* bundle internals use argv[1] as further parameters */ |
| 213 | |
| 214 | if (!startup_info->have_repository) |
| 215 | die(_("Need a repository to unbundle.")); |
| 216 | |
| 217 | if ((bundle_fd = open_bundle(bundle_file, &header, NULL)) < 0) { |
| 218 | ret = 1; |
| 219 | goto cleanup; |
| 220 | } |
| 221 | if (progress) |
| 222 | strvec_pushl(&extra_index_pack_args, "-v", "--progress-title", |
| 223 | _("Unbundling objects"), NULL); |
| 224 | ret = !!unbundle(the_repository, &header, bundle_fd, |
| 225 | &extra_index_pack_args, NULL) || |
| 226 | list_bundle_refs(&header, argc, argv); |
| 227 | bundle_header_release(&header); |
| 228 | |
| 229 | cleanup: |
| 230 | strvec_clear(&extra_index_pack_args); |
| 231 | free(bundle_file); |
| 232 | return ret; |
| 233 | } |
| 234 | |
| 235 | int cmd_bundle(int argc, |
| 236 | const char **argv, |
| 237 | const char *prefix, |
| 238 | struct repository *repo) |
| 239 | { |
| 240 | parse_opt_subcommand_fn *fn = NULL; |
| 241 | struct option options[] = { |
| 242 | OPT_SUBCOMMAND("create", &fn, cmd_bundle_create), |
| 243 | OPT_SUBCOMMAND("verify", &fn, cmd_bundle_verify), |
| 244 | OPT_SUBCOMMAND("list-heads", &fn, cmd_bundle_list_heads), |
| 245 | OPT_SUBCOMMAND("unbundle", &fn, cmd_bundle_unbundle), |
| 246 | OPT_END() |
| 247 | }; |
| 248 | |
| 249 | argc = parse_options(argc, argv, prefix, options, builtin_bundle_usage, |
| 250 | 0); |
| 251 | |
| 252 | packet_trace_identity("bundle"); |
| 253 | |
| 254 | return !!fn(argc, argv, prefix, repo); |
| 255 | } |