transport-helper.con commit Merge branch 'bg/apply-doc' (d2cd665)
   1#include "cache.h"
   2#include "transport.h"
   3#include "quote.h"
   4#include "run-command.h"
   5#include "commit.h"
   6#include "diff.h"
   7#include "revision.h"
   8#include "quote.h"
   9
  10struct helper_data
  11{
  12        const char *name;
  13        struct child_process *helper;
  14        FILE *out;
  15        unsigned fetch : 1,
  16                option : 1,
  17                push : 1;
  18};
  19
  20static struct child_process *get_helper(struct transport *transport)
  21{
  22        struct helper_data *data = transport->data;
  23        struct strbuf buf = STRBUF_INIT;
  24        struct child_process *helper;
  25
  26        if (data->helper)
  27                return data->helper;
  28
  29        helper = xcalloc(1, sizeof(*helper));
  30        helper->in = -1;
  31        helper->out = -1;
  32        helper->err = 0;
  33        helper->argv = xcalloc(4, sizeof(*helper->argv));
  34        strbuf_addf(&buf, "remote-%s", data->name);
  35        helper->argv[0] = strbuf_detach(&buf, NULL);
  36        helper->argv[1] = transport->remote->name;
  37        helper->argv[2] = transport->url;
  38        helper->git_cmd = 1;
  39        if (start_command(helper))
  40                die("Unable to run helper: git %s", helper->argv[0]);
  41        data->helper = helper;
  42
  43        write_str_in_full(helper->in, "capabilities\n");
  44
  45        data->out = xfdopen(helper->out, "r");
  46        while (1) {
  47                if (strbuf_getline(&buf, data->out, '\n') == EOF)
  48                        exit(128); /* child died, message supplied already */
  49
  50                if (!*buf.buf)
  51                        break;
  52                if (!strcmp(buf.buf, "fetch"))
  53                        data->fetch = 1;
  54                if (!strcmp(buf.buf, "option"))
  55                        data->option = 1;
  56                if (!strcmp(buf.buf, "push"))
  57                        data->push = 1;
  58        }
  59        return data->helper;
  60}
  61
  62static int disconnect_helper(struct transport *transport)
  63{
  64        struct helper_data *data = transport->data;
  65        if (data->helper) {
  66                write_str_in_full(data->helper->in, "\n");
  67                close(data->helper->in);
  68                fclose(data->out);
  69                finish_command(data->helper);
  70                free((char *)data->helper->argv[0]);
  71                free(data->helper->argv);
  72                free(data->helper);
  73                data->helper = NULL;
  74        }
  75        free(data);
  76        return 0;
  77}
  78
  79static const char *unsupported_options[] = {
  80        TRANS_OPT_UPLOADPACK,
  81        TRANS_OPT_RECEIVEPACK,
  82        TRANS_OPT_THIN,
  83        TRANS_OPT_KEEP
  84        };
  85static const char *boolean_options[] = {
  86        TRANS_OPT_THIN,
  87        TRANS_OPT_KEEP,
  88        TRANS_OPT_FOLLOWTAGS
  89        };
  90
  91static int set_helper_option(struct transport *transport,
  92                          const char *name, const char *value)
  93{
  94        struct helper_data *data = transport->data;
  95        struct child_process *helper = get_helper(transport);
  96        struct strbuf buf = STRBUF_INIT;
  97        int i, ret, is_bool = 0;
  98
  99        if (!data->option)
 100                return 1;
 101
 102        for (i = 0; i < ARRAY_SIZE(unsupported_options); i++) {
 103                if (!strcmp(name, unsupported_options[i]))
 104                        return 1;
 105        }
 106
 107        for (i = 0; i < ARRAY_SIZE(boolean_options); i++) {
 108                if (!strcmp(name, boolean_options[i])) {
 109                        is_bool = 1;
 110                        break;
 111                }
 112        }
 113
 114        strbuf_addf(&buf, "option %s ", name);
 115        if (is_bool)
 116                strbuf_addstr(&buf, value ? "true" : "false");
 117        else
 118                quote_c_style(value, &buf, NULL, 0);
 119        strbuf_addch(&buf, '\n');
 120
 121        if (write_in_full(helper->in, buf.buf, buf.len) != buf.len)
 122                die_errno("cannot send option to %s", data->name);
 123
 124        strbuf_reset(&buf);
 125        if (strbuf_getline(&buf, data->out, '\n') == EOF)
 126                exit(128); /* child died, message supplied already */
 127
 128        if (!strcmp(buf.buf, "ok"))
 129                ret = 0;
 130        else if (!prefixcmp(buf.buf, "error")) {
 131                ret = -1;
 132        } else if (!strcmp(buf.buf, "unsupported"))
 133                ret = 1;
 134        else {
 135                warning("%s unexpectedly said: '%s'", data->name, buf.buf);
 136                ret = 1;
 137        }
 138        strbuf_release(&buf);
 139        return ret;
 140}
 141
 142static void standard_options(struct transport *t)
 143{
 144        char buf[16];
 145        int n;
 146        int v = t->verbose;
 147        int no_progress = v < 0 || (!t->progress && !isatty(1));
 148
 149        set_helper_option(t, "progress", !no_progress ? "true" : "false");
 150
 151        n = snprintf(buf, sizeof(buf), "%d", v + 1);
 152        if (n >= sizeof(buf))
 153                die("impossibly large verbosity value");
 154        set_helper_option(t, "verbosity", buf);
 155}
 156
 157static int fetch_with_fetch(struct transport *transport,
 158                            int nr_heads, const struct ref **to_fetch)
 159{
 160        struct helper_data *data = transport->data;
 161        int i;
 162        struct strbuf buf = STRBUF_INIT;
 163
 164        standard_options(transport);
 165
 166        for (i = 0; i < nr_heads; i++) {
 167                const struct ref *posn = to_fetch[i];
 168                if (posn->status & REF_STATUS_UPTODATE)
 169                        continue;
 170
 171                strbuf_addf(&buf, "fetch %s %s\n",
 172                            sha1_to_hex(posn->old_sha1), posn->name);
 173        }
 174
 175        strbuf_addch(&buf, '\n');
 176        if (write_in_full(data->helper->in, buf.buf, buf.len) != buf.len)
 177                die_errno("cannot send fetch to %s", data->name);
 178
 179        while (1) {
 180                strbuf_reset(&buf);
 181                if (strbuf_getline(&buf, data->out, '\n') == EOF)
 182                        exit(128); /* child died, message supplied already */
 183
 184                if (!prefixcmp(buf.buf, "lock ")) {
 185                        const char *name = buf.buf + 5;
 186                        if (transport->pack_lockfile)
 187                                warning("%s also locked %s", data->name, name);
 188                        else
 189                                transport->pack_lockfile = xstrdup(name);
 190                }
 191                else if (!buf.len)
 192                        break;
 193                else
 194                        warning("%s unexpectedly said: '%s'", data->name, buf.buf);
 195        }
 196        strbuf_release(&buf);
 197        return 0;
 198}
 199
 200static int fetch(struct transport *transport,
 201                 int nr_heads, const struct ref **to_fetch)
 202{
 203        struct helper_data *data = transport->data;
 204        int i, count;
 205
 206        count = 0;
 207        for (i = 0; i < nr_heads; i++)
 208                if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))
 209                        count++;
 210
 211        if (!count)
 212                return 0;
 213
 214        if (data->fetch)
 215                return fetch_with_fetch(transport, nr_heads, to_fetch);
 216
 217        return -1;
 218}
 219
 220static int push_refs(struct transport *transport,
 221                struct ref *remote_refs, int flags)
 222{
 223        int force_all = flags & TRANSPORT_PUSH_FORCE;
 224        int mirror = flags & TRANSPORT_PUSH_MIRROR;
 225        struct helper_data *data = transport->data;
 226        struct strbuf buf = STRBUF_INIT;
 227        struct child_process *helper;
 228        struct ref *ref;
 229
 230        if (!remote_refs)
 231                return 0;
 232
 233        helper = get_helper(transport);
 234        if (!data->push)
 235                return 1;
 236
 237        for (ref = remote_refs; ref; ref = ref->next) {
 238                if (ref->peer_ref)
 239                        hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
 240                else if (!mirror)
 241                        continue;
 242
 243                ref->deletion = is_null_sha1(ref->new_sha1);
 244                if (!ref->deletion &&
 245                        !hashcmp(ref->old_sha1, ref->new_sha1)) {
 246                        ref->status = REF_STATUS_UPTODATE;
 247                        continue;
 248                }
 249
 250                if (force_all)
 251                        ref->force = 1;
 252
 253                strbuf_addstr(&buf, "push ");
 254                if (!ref->deletion) {
 255                        if (ref->force)
 256                                strbuf_addch(&buf, '+');
 257                        if (ref->peer_ref)
 258                                strbuf_addstr(&buf, ref->peer_ref->name);
 259                        else
 260                                strbuf_addstr(&buf, sha1_to_hex(ref->new_sha1));
 261                }
 262                strbuf_addch(&buf, ':');
 263                strbuf_addstr(&buf, ref->name);
 264                strbuf_addch(&buf, '\n');
 265        }
 266        if (buf.len == 0)
 267                return 0;
 268
 269        transport->verbose = flags & TRANSPORT_PUSH_VERBOSE ? 1 : 0;
 270        standard_options(transport);
 271
 272        if (flags & TRANSPORT_PUSH_DRY_RUN) {
 273                if (set_helper_option(transport, "dry-run", "true") != 0)
 274                        die("helper %s does not support dry-run", data->name);
 275        }
 276
 277        strbuf_addch(&buf, '\n');
 278        if (write_in_full(helper->in, buf.buf, buf.len) != buf.len)
 279                exit(128);
 280
 281        ref = remote_refs;
 282        while (1) {
 283                char *refname, *msg;
 284                int status;
 285
 286                strbuf_reset(&buf);
 287                if (strbuf_getline(&buf, data->out, '\n') == EOF)
 288                        exit(128); /* child died, message supplied already */
 289                if (!buf.len)
 290                        break;
 291
 292                if (!prefixcmp(buf.buf, "ok ")) {
 293                        status = REF_STATUS_OK;
 294                        refname = buf.buf + 3;
 295                } else if (!prefixcmp(buf.buf, "error ")) {
 296                        status = REF_STATUS_REMOTE_REJECT;
 297                        refname = buf.buf + 6;
 298                } else
 299                        die("expected ok/error, helper said '%s'\n", buf.buf);
 300
 301                msg = strchr(refname, ' ');
 302                if (msg) {
 303                        struct strbuf msg_buf = STRBUF_INIT;
 304                        const char *end;
 305
 306                        *msg++ = '\0';
 307                        if (!unquote_c_style(&msg_buf, msg, &end))
 308                                msg = strbuf_detach(&msg_buf, NULL);
 309                        else
 310                                msg = xstrdup(msg);
 311                        strbuf_release(&msg_buf);
 312
 313                        if (!strcmp(msg, "no match")) {
 314                                status = REF_STATUS_NONE;
 315                                free(msg);
 316                                msg = NULL;
 317                        }
 318                        else if (!strcmp(msg, "up to date")) {
 319                                status = REF_STATUS_UPTODATE;
 320                                free(msg);
 321                                msg = NULL;
 322                        }
 323                        else if (!strcmp(msg, "non-fast forward")) {
 324                                status = REF_STATUS_REJECT_NONFASTFORWARD;
 325                                free(msg);
 326                                msg = NULL;
 327                        }
 328                }
 329
 330                if (ref)
 331                        ref = find_ref_by_name(ref, refname);
 332                if (!ref)
 333                        ref = find_ref_by_name(remote_refs, refname);
 334                if (!ref) {
 335                        warning("helper reported unexpected status of %s", refname);
 336                        continue;
 337                }
 338
 339                ref->status = status;
 340                ref->remote_status = msg;
 341        }
 342        strbuf_release(&buf);
 343        return 0;
 344}
 345
 346static struct ref *get_refs_list(struct transport *transport, int for_push)
 347{
 348        struct helper_data *data = transport->data;
 349        struct child_process *helper;
 350        struct ref *ret = NULL;
 351        struct ref **tail = &ret;
 352        struct ref *posn;
 353        struct strbuf buf = STRBUF_INIT;
 354
 355        helper = get_helper(transport);
 356
 357        if (data->push && for_push)
 358                write_str_in_full(helper->in, "list for-push\n");
 359        else
 360                write_str_in_full(helper->in, "list\n");
 361
 362        while (1) {
 363                char *eov, *eon;
 364                if (strbuf_getline(&buf, data->out, '\n') == EOF)
 365                        exit(128); /* child died, message supplied already */
 366
 367                if (!*buf.buf)
 368                        break;
 369
 370                eov = strchr(buf.buf, ' ');
 371                if (!eov)
 372                        die("Malformed response in ref list: %s", buf.buf);
 373                eon = strchr(eov + 1, ' ');
 374                *eov = '\0';
 375                if (eon)
 376                        *eon = '\0';
 377                *tail = alloc_ref(eov + 1);
 378                if (buf.buf[0] == '@')
 379                        (*tail)->symref = xstrdup(buf.buf + 1);
 380                else if (buf.buf[0] != '?')
 381                        get_sha1_hex(buf.buf, (*tail)->old_sha1);
 382                tail = &((*tail)->next);
 383        }
 384        strbuf_release(&buf);
 385
 386        for (posn = ret; posn; posn = posn->next)
 387                resolve_remote_symref(posn, ret);
 388
 389        return ret;
 390}
 391
 392int transport_helper_init(struct transport *transport, const char *name)
 393{
 394        struct helper_data *data = xcalloc(sizeof(*data), 1);
 395        data->name = name;
 396
 397        transport->data = data;
 398        transport->set_option = set_helper_option;
 399        transport->get_refs_list = get_refs_list;
 400        transport->fetch = fetch;
 401        transport->push_refs = push_refs;
 402        transport->disconnect = disconnect_helper;
 403        return 0;
 404}