271af345e45038a0ca59e27e966ca6a6c4be9f25
   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#include "remote.h"
  10
  11static int debug;
  12
  13struct helper_data
  14{
  15        const char *name;
  16        struct child_process *helper;
  17        FILE *out;
  18        unsigned fetch : 1,
  19                import : 1,
  20                option : 1,
  21                push : 1;
  22        /* These go from remote name (as in "list") to private name */
  23        struct refspec *refspecs;
  24        int refspec_nr;
  25};
  26
  27static void sendline(struct helper_data *helper, struct strbuf *buffer)
  28{
  29        if (debug)
  30                fprintf(stderr, "Debug: Remote helper: -> %s", buffer->buf);
  31        if (write_in_full(helper->helper->in, buffer->buf, buffer->len)
  32                != buffer->len)
  33                die_errno("Full write to remote helper failed");
  34}
  35
  36static int recvline(struct helper_data *helper, struct strbuf *buffer)
  37{
  38        strbuf_reset(buffer);
  39        if (debug)
  40                fprintf(stderr, "Debug: Remote helper: Waiting...\n");
  41        if (strbuf_getline(buffer, helper->out, '\n') == EOF) {
  42                if (debug)
  43                        fprintf(stderr, "Debug: Remote helper quit.\n");
  44                exit(128);
  45        }
  46
  47        if (debug)
  48                fprintf(stderr, "Debug: Remote helper: <- %s\n", buffer->buf);
  49        return 0;
  50}
  51
  52static void xchgline(struct helper_data *helper, struct strbuf *buffer)
  53{
  54        sendline(helper, buffer);
  55        recvline(helper, buffer);
  56}
  57
  58static void write_constant(int fd, const char *str)
  59{
  60        if (debug)
  61                fprintf(stderr, "Debug: Remote helper: -> %s", str);
  62        if (write_in_full(fd, str, strlen(str)) != strlen(str))
  63                die_errno("Full write to remote helper failed");
  64}
  65
  66const char *remove_ext_force(const char *url)
  67{
  68        if (url) {
  69                const char *colon = strchr(url, ':');
  70                if (colon && colon[1] == ':')
  71                        return colon + 2;
  72        }
  73        return url;
  74}
  75
  76static struct child_process *get_helper(struct transport *transport)
  77{
  78        struct helper_data *data = transport->data;
  79        struct strbuf buf = STRBUF_INIT;
  80        struct child_process *helper;
  81        const char **refspecs = NULL;
  82        int refspec_nr = 0;
  83        int refspec_alloc = 0;
  84
  85        if (data->helper)
  86                return data->helper;
  87
  88        helper = xcalloc(1, sizeof(*helper));
  89        helper->in = -1;
  90        helper->out = -1;
  91        helper->err = 0;
  92        helper->argv = xcalloc(4, sizeof(*helper->argv));
  93        strbuf_addf(&buf, "remote-%s", data->name);
  94        helper->argv[0] = strbuf_detach(&buf, NULL);
  95        helper->argv[1] = transport->remote->name;
  96        helper->argv[2] = remove_ext_force(transport->url);
  97        helper->git_cmd = 1;
  98        if (start_command(helper))
  99                die("Unable to run helper: git %s", helper->argv[0]);
 100        data->helper = helper;
 101
 102        write_constant(helper->in, "capabilities\n");
 103
 104        data->out = xfdopen(helper->out, "r");
 105        while (1) {
 106                const char *capname;
 107                int mandatory = 0;
 108                recvline(data, &buf);
 109
 110                if (!*buf.buf)
 111                        break;
 112
 113                if (*buf.buf == '*') {
 114                        capname = buf.buf + 1;
 115                        mandatory = 1;
 116                } else
 117                        capname = buf.buf;
 118
 119                if (debug)
 120                        fprintf(stderr, "Debug: Got cap %s\n", capname);
 121                if (!strcmp(capname, "fetch"))
 122                        data->fetch = 1;
 123                else if (!strcmp(capname, "option"))
 124                        data->option = 1;
 125                else if (!strcmp(capname, "push"))
 126                        data->push = 1;
 127                else if (!strcmp(capname, "import"))
 128                        data->import = 1;
 129                else if (!data->refspecs && !prefixcmp(capname, "refspec ")) {
 130                        ALLOC_GROW(refspecs,
 131                                   refspec_nr + 1,
 132                                   refspec_alloc);
 133                        refspecs[refspec_nr++] = strdup(buf.buf + strlen("refspec "));
 134                } else if (mandatory) {
 135                        die("Unknown madatory capability %s. This remote "
 136                            "helper probably needs newer version of Git.\n",
 137                            capname);
 138                }
 139        }
 140        if (refspecs) {
 141                int i;
 142                data->refspec_nr = refspec_nr;
 143                data->refspecs = parse_fetch_refspec(refspec_nr, refspecs);
 144                for (i = 0; i < refspec_nr; i++) {
 145                        free((char *)refspecs[i]);
 146                }
 147                free(refspecs);
 148        }
 149        strbuf_release(&buf);
 150        if (debug)
 151                fprintf(stderr, "Debug: Capabilities complete.\n");
 152        return data->helper;
 153}
 154
 155static int disconnect_helper(struct transport *transport)
 156{
 157        struct helper_data *data = transport->data;
 158        struct strbuf buf = STRBUF_INIT;
 159
 160        if (data->helper) {
 161                if (debug)
 162                        fprintf(stderr, "Debug: Disconnecting.\n");
 163                strbuf_addf(&buf, "\n");
 164                sendline(data, &buf);
 165                close(data->helper->in);
 166                fclose(data->out);
 167                finish_command(data->helper);
 168                free((char *)data->helper->argv[0]);
 169                free(data->helper->argv);
 170                free(data->helper);
 171                data->helper = NULL;
 172        }
 173        return 0;
 174}
 175
 176static const char *unsupported_options[] = {
 177        TRANS_OPT_UPLOADPACK,
 178        TRANS_OPT_RECEIVEPACK,
 179        TRANS_OPT_THIN,
 180        TRANS_OPT_KEEP
 181        };
 182static const char *boolean_options[] = {
 183        TRANS_OPT_THIN,
 184        TRANS_OPT_KEEP,
 185        TRANS_OPT_FOLLOWTAGS
 186        };
 187
 188static int set_helper_option(struct transport *transport,
 189                          const char *name, const char *value)
 190{
 191        struct helper_data *data = transport->data;
 192        struct strbuf buf = STRBUF_INIT;
 193        int i, ret, is_bool = 0;
 194
 195        get_helper(transport);
 196
 197        if (!data->option)
 198                return 1;
 199
 200        for (i = 0; i < ARRAY_SIZE(unsupported_options); i++) {
 201                if (!strcmp(name, unsupported_options[i]))
 202                        return 1;
 203        }
 204
 205        for (i = 0; i < ARRAY_SIZE(boolean_options); i++) {
 206                if (!strcmp(name, boolean_options[i])) {
 207                        is_bool = 1;
 208                        break;
 209                }
 210        }
 211
 212        strbuf_addf(&buf, "option %s ", name);
 213        if (is_bool)
 214                strbuf_addstr(&buf, value ? "true" : "false");
 215        else
 216                quote_c_style(value, &buf, NULL, 0);
 217        strbuf_addch(&buf, '\n');
 218
 219        xchgline(data, &buf);
 220
 221        if (!strcmp(buf.buf, "ok"))
 222                ret = 0;
 223        else if (!prefixcmp(buf.buf, "error")) {
 224                ret = -1;
 225        } else if (!strcmp(buf.buf, "unsupported"))
 226                ret = 1;
 227        else {
 228                warning("%s unexpectedly said: '%s'", data->name, buf.buf);
 229                ret = 1;
 230        }
 231        strbuf_release(&buf);
 232        return ret;
 233}
 234
 235static void standard_options(struct transport *t)
 236{
 237        char buf[16];
 238        int n;
 239        int v = t->verbose;
 240        int no_progress = v < 0 || (!t->progress && !isatty(1));
 241
 242        set_helper_option(t, "progress", !no_progress ? "true" : "false");
 243
 244        n = snprintf(buf, sizeof(buf), "%d", v + 1);
 245        if (n >= sizeof(buf))
 246                die("impossibly large verbosity value");
 247        set_helper_option(t, "verbosity", buf);
 248}
 249
 250static int release_helper(struct transport *transport)
 251{
 252        struct helper_data *data = transport->data;
 253        free_refspec(data->refspec_nr, data->refspecs);
 254        data->refspecs = NULL;
 255        disconnect_helper(transport);
 256        free(transport->data);
 257        return 0;
 258}
 259
 260static int fetch_with_fetch(struct transport *transport,
 261                            int nr_heads, struct ref **to_fetch)
 262{
 263        struct helper_data *data = transport->data;
 264        int i;
 265        struct strbuf buf = STRBUF_INIT;
 266
 267        standard_options(transport);
 268
 269        for (i = 0; i < nr_heads; i++) {
 270                const struct ref *posn = to_fetch[i];
 271                if (posn->status & REF_STATUS_UPTODATE)
 272                        continue;
 273
 274                strbuf_addf(&buf, "fetch %s %s\n",
 275                            sha1_to_hex(posn->old_sha1), posn->name);
 276        }
 277
 278        strbuf_addch(&buf, '\n');
 279        sendline(data, &buf);
 280
 281        while (1) {
 282                recvline(data, &buf);
 283
 284                if (!prefixcmp(buf.buf, "lock ")) {
 285                        const char *name = buf.buf + 5;
 286                        if (transport->pack_lockfile)
 287                                warning("%s also locked %s", data->name, name);
 288                        else
 289                                transport->pack_lockfile = xstrdup(name);
 290                }
 291                else if (!buf.len)
 292                        break;
 293                else
 294                        warning("%s unexpectedly said: '%s'", data->name, buf.buf);
 295        }
 296        strbuf_release(&buf);
 297        return 0;
 298}
 299
 300static int get_importer(struct transport *transport, struct child_process *fastimport)
 301{
 302        struct child_process *helper = get_helper(transport);
 303        memset(fastimport, 0, sizeof(*fastimport));
 304        fastimport->in = helper->out;
 305        fastimport->argv = xcalloc(5, sizeof(*fastimport->argv));
 306        fastimport->argv[0] = "fast-import";
 307        fastimport->argv[1] = "--quiet";
 308
 309        fastimport->git_cmd = 1;
 310        return start_command(fastimport);
 311}
 312
 313static int fetch_with_import(struct transport *transport,
 314                             int nr_heads, struct ref **to_fetch)
 315{
 316        struct child_process fastimport;
 317        struct helper_data *data = transport->data;
 318        int i;
 319        struct ref *posn;
 320        struct strbuf buf = STRBUF_INIT;
 321
 322        get_helper(transport);
 323
 324        if (get_importer(transport, &fastimport))
 325                die("Couldn't run fast-import");
 326
 327        for (i = 0; i < nr_heads; i++) {
 328                posn = to_fetch[i];
 329                if (posn->status & REF_STATUS_UPTODATE)
 330                        continue;
 331
 332                strbuf_addf(&buf, "import %s\n", posn->name);
 333                sendline(data, &buf);
 334                strbuf_reset(&buf);
 335        }
 336        disconnect_helper(transport);
 337        finish_command(&fastimport);
 338        free(fastimport.argv);
 339        fastimport.argv = NULL;
 340
 341        for (i = 0; i < nr_heads; i++) {
 342                char *private;
 343                posn = to_fetch[i];
 344                if (posn->status & REF_STATUS_UPTODATE)
 345                        continue;
 346                if (data->refspecs)
 347                        private = apply_refspecs(data->refspecs, data->refspec_nr, posn->name);
 348                else
 349                        private = strdup(posn->name);
 350                read_ref(private, posn->old_sha1);
 351                free(private);
 352        }
 353        strbuf_release(&buf);
 354        return 0;
 355}
 356
 357static int fetch(struct transport *transport,
 358                 int nr_heads, struct ref **to_fetch)
 359{
 360        struct helper_data *data = transport->data;
 361        int i, count;
 362
 363        count = 0;
 364        for (i = 0; i < nr_heads; i++)
 365                if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))
 366                        count++;
 367
 368        if (!count)
 369                return 0;
 370
 371        if (data->fetch)
 372                return fetch_with_fetch(transport, nr_heads, to_fetch);
 373
 374        if (data->import)
 375                return fetch_with_import(transport, nr_heads, to_fetch);
 376
 377        return -1;
 378}
 379
 380static int push_refs(struct transport *transport,
 381                struct ref *remote_refs, int flags)
 382{
 383        int force_all = flags & TRANSPORT_PUSH_FORCE;
 384        int mirror = flags & TRANSPORT_PUSH_MIRROR;
 385        struct helper_data *data = transport->data;
 386        struct strbuf buf = STRBUF_INIT;
 387        struct child_process *helper;
 388        struct ref *ref;
 389
 390        if (!remote_refs)
 391                return 0;
 392
 393        helper = get_helper(transport);
 394        if (!data->push)
 395                return 1;
 396
 397        for (ref = remote_refs; ref; ref = ref->next) {
 398                if (ref->peer_ref)
 399                        hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
 400                else if (!mirror)
 401                        continue;
 402
 403                ref->deletion = is_null_sha1(ref->new_sha1);
 404                if (!ref->deletion &&
 405                        !hashcmp(ref->old_sha1, ref->new_sha1)) {
 406                        ref->status = REF_STATUS_UPTODATE;
 407                        continue;
 408                }
 409
 410                if (force_all)
 411                        ref->force = 1;
 412
 413                strbuf_addstr(&buf, "push ");
 414                if (!ref->deletion) {
 415                        if (ref->force)
 416                                strbuf_addch(&buf, '+');
 417                        if (ref->peer_ref)
 418                                strbuf_addstr(&buf, ref->peer_ref->name);
 419                        else
 420                                strbuf_addstr(&buf, sha1_to_hex(ref->new_sha1));
 421                }
 422                strbuf_addch(&buf, ':');
 423                strbuf_addstr(&buf, ref->name);
 424                strbuf_addch(&buf, '\n');
 425        }
 426        if (buf.len == 0)
 427                return 0;
 428
 429        transport->verbose = flags & TRANSPORT_PUSH_VERBOSE ? 1 : 0;
 430        standard_options(transport);
 431
 432        if (flags & TRANSPORT_PUSH_DRY_RUN) {
 433                if (set_helper_option(transport, "dry-run", "true") != 0)
 434                        die("helper %s does not support dry-run", data->name);
 435        }
 436
 437        strbuf_addch(&buf, '\n');
 438        sendline(data, &buf);
 439
 440        ref = remote_refs;
 441        while (1) {
 442                char *refname, *msg;
 443                int status;
 444
 445                recvline(data, &buf);
 446                if (!buf.len)
 447                        break;
 448
 449                if (!prefixcmp(buf.buf, "ok ")) {
 450                        status = REF_STATUS_OK;
 451                        refname = buf.buf + 3;
 452                } else if (!prefixcmp(buf.buf, "error ")) {
 453                        status = REF_STATUS_REMOTE_REJECT;
 454                        refname = buf.buf + 6;
 455                } else
 456                        die("expected ok/error, helper said '%s'\n", buf.buf);
 457
 458                msg = strchr(refname, ' ');
 459                if (msg) {
 460                        struct strbuf msg_buf = STRBUF_INIT;
 461                        const char *end;
 462
 463                        *msg++ = '\0';
 464                        if (!unquote_c_style(&msg_buf, msg, &end))
 465                                msg = strbuf_detach(&msg_buf, NULL);
 466                        else
 467                                msg = xstrdup(msg);
 468                        strbuf_release(&msg_buf);
 469
 470                        if (!strcmp(msg, "no match")) {
 471                                status = REF_STATUS_NONE;
 472                                free(msg);
 473                                msg = NULL;
 474                        }
 475                        else if (!strcmp(msg, "up to date")) {
 476                                status = REF_STATUS_UPTODATE;
 477                                free(msg);
 478                                msg = NULL;
 479                        }
 480                        else if (!strcmp(msg, "non-fast forward")) {
 481                                status = REF_STATUS_REJECT_NONFASTFORWARD;
 482                                free(msg);
 483                                msg = NULL;
 484                        }
 485                }
 486
 487                if (ref)
 488                        ref = find_ref_by_name(ref, refname);
 489                if (!ref)
 490                        ref = find_ref_by_name(remote_refs, refname);
 491                if (!ref) {
 492                        warning("helper reported unexpected status of %s", refname);
 493                        continue;
 494                }
 495
 496                ref->status = status;
 497                ref->remote_status = msg;
 498        }
 499        strbuf_release(&buf);
 500        return 0;
 501}
 502
 503static int has_attribute(const char *attrs, const char *attr) {
 504        int len;
 505        if (!attrs)
 506                return 0;
 507
 508        len = strlen(attr);
 509        for (;;) {
 510                const char *space = strchrnul(attrs, ' ');
 511                if (len == space - attrs && !strncmp(attrs, attr, len))
 512                        return 1;
 513                if (!*space)
 514                        return 0;
 515                attrs = space + 1;
 516        }
 517}
 518
 519static struct ref *get_refs_list(struct transport *transport, int for_push)
 520{
 521        struct helper_data *data = transport->data;
 522        struct child_process *helper;
 523        struct ref *ret = NULL;
 524        struct ref **tail = &ret;
 525        struct ref *posn;
 526        struct strbuf buf = STRBUF_INIT;
 527
 528        helper = get_helper(transport);
 529
 530        if (data->push && for_push)
 531                write_str_in_full(helper->in, "list for-push\n");
 532        else
 533                write_str_in_full(helper->in, "list\n");
 534
 535        while (1) {
 536                char *eov, *eon;
 537                recvline(data, &buf);
 538
 539                if (!*buf.buf)
 540                        break;
 541
 542                eov = strchr(buf.buf, ' ');
 543                if (!eov)
 544                        die("Malformed response in ref list: %s", buf.buf);
 545                eon = strchr(eov + 1, ' ');
 546                *eov = '\0';
 547                if (eon)
 548                        *eon = '\0';
 549                *tail = alloc_ref(eov + 1);
 550                if (buf.buf[0] == '@')
 551                        (*tail)->symref = xstrdup(buf.buf + 1);
 552                else if (buf.buf[0] != '?')
 553                        get_sha1_hex(buf.buf, (*tail)->old_sha1);
 554                if (eon) {
 555                        if (has_attribute(eon + 1, "unchanged")) {
 556                                (*tail)->status |= REF_STATUS_UPTODATE;
 557                                read_ref((*tail)->name, (*tail)->old_sha1);
 558                        }
 559                }
 560                tail = &((*tail)->next);
 561        }
 562        if (debug)
 563                fprintf(stderr, "Debug: Read ref listing.\n");
 564        strbuf_release(&buf);
 565
 566        for (posn = ret; posn; posn = posn->next)
 567                resolve_remote_symref(posn, ret);
 568
 569        return ret;
 570}
 571
 572int transport_helper_init(struct transport *transport, const char *name)
 573{
 574        struct helper_data *data = xcalloc(sizeof(*data), 1);
 575        data->name = name;
 576
 577        if (getenv("GIT_TRANSPORT_HELPER_DEBUG"))
 578                debug = 1;
 579
 580        transport->data = data;
 581        transport->set_option = set_helper_option;
 582        transport->get_refs_list = get_refs_list;
 583        transport->fetch = fetch;
 584        transport->push_refs = push_refs;
 585        transport->disconnect = release_helper;
 586        return 0;
 587}