f2962245779419753c247e5beaa179db53f7e7f0
   1#include "builtin.h"
   2#include "config.h"
   3#include "parse-options.h"
   4#include "refs.h"
   5#include "lockfile.h"
   6#include "cache-tree.h"
   7#include "unpack-trees.h"
   8#include "merge-recursive.h"
   9#include "argv-array.h"
  10#include "run-command.h"
  11#include "dir.h"
  12#include "rerere.h"
  13
  14static const char * const git_stash_helper_usage[] = {
  15        N_("git stash--helper list [<options>]"),
  16        N_("git stash--helper drop [-q|--quiet] [<stash>]"),
  17        N_("git stash--helper ( pop | apply ) [--index] [-q|--quiet] [<stash>]"),
  18        N_("git stash--helper branch <branchname> [<stash>]"),
  19        N_("git stash--helper clear"),
  20        NULL
  21};
  22
  23static const char * const git_stash_helper_list_usage[] = {
  24        N_("git stash--helper list [<options>]"),
  25        NULL
  26};
  27
  28static const char * const git_stash_helper_drop_usage[] = {
  29        N_("git stash--helper drop [-q|--quiet] [<stash>]"),
  30        NULL
  31};
  32
  33static const char * const git_stash_helper_pop_usage[] = {
  34        N_("git stash--helper pop [--index] [-q|--quiet] [<stash>]"),
  35        NULL
  36};
  37
  38static const char * const git_stash_helper_apply_usage[] = {
  39        N_("git stash--helper apply [--index] [-q|--quiet] [<stash>]"),
  40        NULL
  41};
  42
  43static const char * const git_stash_helper_branch_usage[] = {
  44        N_("git stash--helper branch <branchname> [<stash>]"),
  45        NULL
  46};
  47
  48static const char * const git_stash_helper_clear_usage[] = {
  49        N_("git stash--helper clear"),
  50        NULL
  51};
  52
  53static const char *ref_stash = "refs/stash";
  54static struct strbuf stash_index_path = STRBUF_INIT;
  55
  56/*
  57 * w_commit is set to the commit containing the working tree
  58 * b_commit is set to the base commit
  59 * i_commit is set to the commit containing the index tree
  60 * u_commit is set to the commit containing the untracked files tree
  61 * w_tree is set to the working tree
  62 * b_tree is set to the base tree
  63 * i_tree is set to the index tree
  64 * u_tree is set to the untracked files tree
  65 */
  66struct stash_info {
  67        struct object_id w_commit;
  68        struct object_id b_commit;
  69        struct object_id i_commit;
  70        struct object_id u_commit;
  71        struct object_id w_tree;
  72        struct object_id b_tree;
  73        struct object_id i_tree;
  74        struct object_id u_tree;
  75        struct strbuf revision;
  76        int is_stash_ref;
  77        int has_u;
  78};
  79
  80static void free_stash_info(struct stash_info *info)
  81{
  82        strbuf_release(&info->revision);
  83}
  84
  85static void assert_stash_like(struct stash_info *info, const char *revision)
  86{
  87        if (get_oidf(&info->b_commit, "%s^1", revision) ||
  88            get_oidf(&info->w_tree, "%s:", revision) ||
  89            get_oidf(&info->b_tree, "%s^1:", revision) ||
  90            get_oidf(&info->i_tree, "%s^2:", revision))
  91                die(_("'%s' is not a stash-like commit"), revision);
  92}
  93
  94static int get_stash_info(struct stash_info *info, int argc, const char **argv)
  95{
  96        int ret;
  97        char *end_of_rev;
  98        char *expanded_ref;
  99        const char *revision;
 100        const char *commit = NULL;
 101        struct object_id dummy;
 102        struct strbuf symbolic = STRBUF_INIT;
 103
 104        if (argc > 1) {
 105                int i;
 106                struct strbuf refs_msg = STRBUF_INIT;
 107
 108                for (i = 0; i < argc; i++)
 109                        strbuf_addf(&refs_msg, " '%s'", argv[i]);
 110
 111                fprintf_ln(stderr, _("Too many revisions specified:%s"),
 112                           refs_msg.buf);
 113                strbuf_release(&refs_msg);
 114
 115                return -1;
 116        }
 117
 118        if (argc == 1)
 119                commit = argv[0];
 120
 121        strbuf_init(&info->revision, 0);
 122        if (!commit) {
 123                if (!ref_exists(ref_stash)) {
 124                        free_stash_info(info);
 125                        fprintf_ln(stderr, _("No stash entries found."));
 126                        return -1;
 127                }
 128
 129                strbuf_addf(&info->revision, "%s@{0}", ref_stash);
 130        } else if (strspn(commit, "0123456789") == strlen(commit)) {
 131                strbuf_addf(&info->revision, "%s@{%s}", ref_stash, commit);
 132        } else {
 133                strbuf_addstr(&info->revision, commit);
 134        }
 135
 136        revision = info->revision.buf;
 137
 138        if (get_oid(revision, &info->w_commit)) {
 139                error(_("%s is not a valid reference"), revision);
 140                free_stash_info(info);
 141                return -1;
 142        }
 143
 144        assert_stash_like(info, revision);
 145
 146        info->has_u = !get_oidf(&info->u_tree, "%s^3:", revision);
 147
 148        end_of_rev = strchrnul(revision, '@');
 149        strbuf_add(&symbolic, revision, end_of_rev - revision);
 150
 151        ret = dwim_ref(symbolic.buf, symbolic.len, &dummy, &expanded_ref);
 152        strbuf_release(&symbolic);
 153        switch (ret) {
 154        case 0: /* Not found, but valid ref */
 155                info->is_stash_ref = 0;
 156                break;
 157        case 1:
 158                info->is_stash_ref = !strcmp(expanded_ref, ref_stash);
 159                break;
 160        default: /* Invalid or ambiguous */
 161                free_stash_info(info);
 162        }
 163
 164        free(expanded_ref);
 165        return !(ret == 0 || ret == 1);
 166}
 167
 168static int do_clear_stash(void)
 169{
 170        struct object_id obj;
 171        if (get_oid(ref_stash, &obj))
 172                return 0;
 173
 174        return delete_ref(NULL, ref_stash, &obj, 0);
 175}
 176
 177static int clear_stash(int argc, const char **argv, const char *prefix)
 178{
 179        struct option options[] = {
 180                OPT_END()
 181        };
 182
 183        argc = parse_options(argc, argv, prefix, options,
 184                             git_stash_helper_clear_usage,
 185                             PARSE_OPT_STOP_AT_NON_OPTION);
 186
 187        if (argc)
 188                return error(_("git stash clear with parameters is "
 189                               "unimplemented"));
 190
 191        return do_clear_stash();
 192}
 193
 194static int reset_tree(struct object_id *i_tree, int update, int reset)
 195{
 196        int nr_trees = 1;
 197        struct unpack_trees_options opts;
 198        struct tree_desc t[MAX_UNPACK_TREES];
 199        struct tree *tree;
 200        struct lock_file lock_file = LOCK_INIT;
 201
 202        read_cache_preload(NULL);
 203        if (refresh_cache(REFRESH_QUIET))
 204                return -1;
 205
 206        hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
 207
 208        memset(&opts, 0, sizeof(opts));
 209
 210        tree = parse_tree_indirect(i_tree);
 211        if (parse_tree(tree))
 212                return -1;
 213
 214        init_tree_desc(t, tree->buffer, tree->size);
 215
 216        opts.head_idx = 1;
 217        opts.src_index = &the_index;
 218        opts.dst_index = &the_index;
 219        opts.merge = 1;
 220        opts.reset = reset;
 221        opts.update = update;
 222        opts.fn = oneway_merge;
 223
 224        if (unpack_trees(nr_trees, t, &opts))
 225                return -1;
 226
 227        if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
 228                return error(_("unable to write new index file"));
 229
 230        return 0;
 231}
 232
 233static int diff_tree_binary(struct strbuf *out, struct object_id *w_commit)
 234{
 235        struct child_process cp = CHILD_PROCESS_INIT;
 236        const char *w_commit_hex = oid_to_hex(w_commit);
 237
 238        /*
 239         * Diff-tree would not be very hard to replace with a native function,
 240         * however it should be done together with apply_cached.
 241         */
 242        cp.git_cmd = 1;
 243        argv_array_pushl(&cp.args, "diff-tree", "--binary", NULL);
 244        argv_array_pushf(&cp.args, "%s^2^..%s^2", w_commit_hex, w_commit_hex);
 245
 246        return pipe_command(&cp, NULL, 0, out, 0, NULL, 0);
 247}
 248
 249static int apply_cached(struct strbuf *out)
 250{
 251        struct child_process cp = CHILD_PROCESS_INIT;
 252
 253        /*
 254         * Apply currently only reads either from stdin or a file, thus
 255         * apply_all_patches would have to be updated to optionally take a
 256         * buffer.
 257         */
 258        cp.git_cmd = 1;
 259        argv_array_pushl(&cp.args, "apply", "--cached", NULL);
 260        return pipe_command(&cp, out->buf, out->len, NULL, 0, NULL, 0);
 261}
 262
 263static int reset_head(void)
 264{
 265        struct child_process cp = CHILD_PROCESS_INIT;
 266
 267        /*
 268         * Reset is overall quite simple, however there is no current public
 269         * API for resetting.
 270         */
 271        cp.git_cmd = 1;
 272        argv_array_push(&cp.args, "reset");
 273
 274        return run_command(&cp);
 275}
 276
 277static int get_newly_staged(struct strbuf *out, struct object_id *c_tree)
 278{
 279        struct child_process cp = CHILD_PROCESS_INIT;
 280        const char *c_tree_hex = oid_to_hex(c_tree);
 281
 282        /*
 283         * diff-index is very similar to diff-tree above, and should be
 284         * converted together with update_index.
 285         */
 286        cp.git_cmd = 1;
 287        argv_array_pushl(&cp.args, "diff-index", "--cached", "--name-only",
 288                         "--diff-filter=A", NULL);
 289        argv_array_push(&cp.args, c_tree_hex);
 290        return pipe_command(&cp, NULL, 0, out, 0, NULL, 0);
 291}
 292
 293static int update_index(struct strbuf *out)
 294{
 295        struct child_process cp = CHILD_PROCESS_INIT;
 296
 297        /*
 298         * Update-index is very complicated and may need to have a public
 299         * function exposed in order to remove this forking.
 300         */
 301        cp.git_cmd = 1;
 302        argv_array_pushl(&cp.args, "update-index", "--add", "--stdin", NULL);
 303        return pipe_command(&cp, out->buf, out->len, NULL, 0, NULL, 0);
 304}
 305
 306static int restore_untracked(struct object_id *u_tree)
 307{
 308        int res;
 309        struct child_process cp = CHILD_PROCESS_INIT;
 310
 311        /*
 312         * We need to run restore files from a given index, but without
 313         * affecting the current index, so we use GIT_INDEX_FILE with
 314         * run_command to fork processes that will not interfere.
 315         */
 316        cp.git_cmd = 1;
 317        argv_array_push(&cp.args, "read-tree");
 318        argv_array_push(&cp.args, oid_to_hex(u_tree));
 319        argv_array_pushf(&cp.env_array, "GIT_INDEX_FILE=%s",
 320                         stash_index_path.buf);
 321        if (run_command(&cp)) {
 322                remove_path(stash_index_path.buf);
 323                return -1;
 324        }
 325
 326        child_process_init(&cp);
 327        cp.git_cmd = 1;
 328        argv_array_pushl(&cp.args, "checkout-index", "--all", NULL);
 329        argv_array_pushf(&cp.env_array, "GIT_INDEX_FILE=%s",
 330                         stash_index_path.buf);
 331
 332        res = run_command(&cp);
 333        remove_path(stash_index_path.buf);
 334        return res;
 335}
 336
 337static int do_apply_stash(const char *prefix, struct stash_info *info,
 338                          int index, int quiet)
 339{
 340        int ret;
 341        int has_index = index;
 342        struct merge_options o;
 343        struct object_id c_tree;
 344        struct object_id index_tree;
 345        struct commit *result;
 346        const struct object_id *bases[1];
 347
 348        read_cache_preload(NULL);
 349        if (refresh_cache(REFRESH_QUIET))
 350                return -1;
 351
 352        if (write_cache_as_tree(&c_tree, 0, NULL))
 353                return error(_("cannot apply a stash in the middle of a merge"));
 354
 355        if (index) {
 356                if (oideq(&info->b_tree, &info->i_tree) ||
 357                    oideq(&c_tree, &info->i_tree)) {
 358                        has_index = 0;
 359                } else {
 360                        struct strbuf out = STRBUF_INIT;
 361
 362                        if (diff_tree_binary(&out, &info->w_commit)) {
 363                                strbuf_release(&out);
 364                                return error(_("could not generate diff %s^!."),
 365                                             oid_to_hex(&info->w_commit));
 366                        }
 367
 368                        ret = apply_cached(&out);
 369                        strbuf_release(&out);
 370                        if (ret)
 371                                return error(_("conflicts in index."
 372                                               "Try without --index."));
 373
 374                        discard_cache();
 375                        read_cache();
 376                        if (write_cache_as_tree(&index_tree, 0, NULL))
 377                                return error(_("could not save index tree"));
 378
 379                        reset_head();
 380                }
 381        }
 382
 383        if (info->has_u && restore_untracked(&info->u_tree))
 384                return error(_("could not restore untracked files from stash"));
 385
 386        init_merge_options(&o);
 387
 388        o.branch1 = "Updated upstream";
 389        o.branch2 = "Stashed changes";
 390
 391        if (oideq(&info->b_tree, &c_tree))
 392                o.branch1 = "Version stash was based on";
 393
 394        if (quiet)
 395                o.verbosity = 0;
 396
 397        if (o.verbosity >= 3)
 398                printf_ln(_("Merging %s with %s"), o.branch1, o.branch2);
 399
 400        bases[0] = &info->b_tree;
 401
 402        ret = merge_recursive_generic(&o, &c_tree, &info->w_tree, 1, bases,
 403                                      &result);
 404        if (ret) {
 405                rerere(0);
 406
 407                if (index)
 408                        fprintf_ln(stderr, _("Index was not unstashed."));
 409
 410                return ret;
 411        }
 412
 413        if (has_index) {
 414                if (reset_tree(&index_tree, 0, 0))
 415                        return -1;
 416        } else {
 417                struct strbuf out = STRBUF_INIT;
 418
 419                if (get_newly_staged(&out, &c_tree)) {
 420                        strbuf_release(&out);
 421                        return -1;
 422                }
 423
 424                if (reset_tree(&c_tree, 0, 1)) {
 425                        strbuf_release(&out);
 426                        return -1;
 427                }
 428
 429                ret = update_index(&out);
 430                strbuf_release(&out);
 431                if (ret)
 432                        return -1;
 433
 434                discard_cache();
 435        }
 436
 437        if (quiet) {
 438                if (refresh_cache(REFRESH_QUIET))
 439                        warning("could not refresh index");
 440        } else {
 441                struct child_process cp = CHILD_PROCESS_INIT;
 442
 443                /*
 444                 * Status is quite simple and could be replaced with calls to
 445                 * wt_status in the future, but it adds complexities which may
 446                 * require more tests.
 447                 */
 448                cp.git_cmd = 1;
 449                cp.dir = prefix;
 450                argv_array_push(&cp.args, "status");
 451                run_command(&cp);
 452        }
 453
 454        return 0;
 455}
 456
 457static int apply_stash(int argc, const char **argv, const char *prefix)
 458{
 459        int ret;
 460        int quiet = 0;
 461        int index = 0;
 462        struct stash_info info;
 463        struct option options[] = {
 464                OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 465                OPT_BOOL(0, "index", &index,
 466                         N_("attempt to recreate the index")),
 467                OPT_END()
 468        };
 469
 470        argc = parse_options(argc, argv, prefix, options,
 471                             git_stash_helper_apply_usage, 0);
 472
 473        if (get_stash_info(&info, argc, argv))
 474                return -1;
 475
 476        ret = do_apply_stash(prefix, &info, index, quiet);
 477        free_stash_info(&info);
 478        return ret;
 479}
 480
 481static int do_drop_stash(const char *prefix, struct stash_info *info, int quiet)
 482{
 483        int ret;
 484        struct child_process cp_reflog = CHILD_PROCESS_INIT;
 485        struct child_process cp = CHILD_PROCESS_INIT;
 486
 487        /*
 488         * reflog does not provide a simple function for deleting refs. One will
 489         * need to be added to avoid implementing too much reflog code here
 490         */
 491
 492        cp_reflog.git_cmd = 1;
 493        argv_array_pushl(&cp_reflog.args, "reflog", "delete", "--updateref",
 494                         "--rewrite", NULL);
 495        argv_array_push(&cp_reflog.args, info->revision.buf);
 496        ret = run_command(&cp_reflog);
 497        if (!ret) {
 498                if (!quiet)
 499                        printf_ln(_("Dropped %s (%s)"), info->revision.buf,
 500                                  oid_to_hex(&info->w_commit));
 501        } else {
 502                return error(_("%s: Could not drop stash entry"),
 503                             info->revision.buf);
 504        }
 505
 506        /*
 507         * This could easily be replaced by get_oid, but currently it will throw
 508         * a fatal error when a reflog is empty, which we can not recover from.
 509         */
 510        cp.git_cmd = 1;
 511        /* Even though --quiet is specified, rev-parse still outputs the hash */
 512        cp.no_stdout = 1;
 513        argv_array_pushl(&cp.args, "rev-parse", "--verify", "--quiet", NULL);
 514        argv_array_pushf(&cp.args, "%s@{0}", ref_stash);
 515        ret = run_command(&cp);
 516
 517        /* do_clear_stash if we just dropped the last stash entry */
 518        if (ret)
 519                do_clear_stash();
 520
 521        return 0;
 522}
 523
 524static void assert_stash_ref(struct stash_info *info)
 525{
 526        if (!info->is_stash_ref) {
 527                error(_("'%s' is not a stash reference"), info->revision.buf);
 528                free_stash_info(info);
 529                exit(1);
 530        }
 531}
 532
 533static int drop_stash(int argc, const char **argv, const char *prefix)
 534{
 535        int ret;
 536        int quiet = 0;
 537        struct stash_info info;
 538        struct option options[] = {
 539                OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 540                OPT_END()
 541        };
 542
 543        argc = parse_options(argc, argv, prefix, options,
 544                             git_stash_helper_drop_usage, 0);
 545
 546        if (get_stash_info(&info, argc, argv))
 547                return -1;
 548
 549        assert_stash_ref(&info);
 550
 551        ret = do_drop_stash(prefix, &info, quiet);
 552        free_stash_info(&info);
 553        return ret;
 554}
 555
 556static int pop_stash(int argc, const char **argv, const char *prefix)
 557{
 558        int ret;
 559        int index = 0;
 560        int quiet = 0;
 561        struct stash_info info;
 562        struct option options[] = {
 563                OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 564                OPT_BOOL(0, "index", &index,
 565                         N_("attempt to recreate the index")),
 566                OPT_END()
 567        };
 568
 569        argc = parse_options(argc, argv, prefix, options,
 570                             git_stash_helper_pop_usage, 0);
 571
 572        if (get_stash_info(&info, argc, argv))
 573                return -1;
 574
 575        assert_stash_ref(&info);
 576        if ((ret = do_apply_stash(prefix, &info, index, quiet)))
 577                printf_ln(_("The stash entry is kept in case "
 578                            "you need it again."));
 579        else
 580                ret = do_drop_stash(prefix, &info, quiet);
 581
 582        free_stash_info(&info);
 583        return ret;
 584}
 585
 586static int branch_stash(int argc, const char **argv, const char *prefix)
 587{
 588        int ret;
 589        const char *branch = NULL;
 590        struct stash_info info;
 591        struct child_process cp = CHILD_PROCESS_INIT;
 592        struct option options[] = {
 593                OPT_END()
 594        };
 595
 596        argc = parse_options(argc, argv, prefix, options,
 597                             git_stash_helper_branch_usage, 0);
 598
 599        if (!argc) {
 600                fprintf_ln(stderr, _("No branch name specified"));
 601                return -1;
 602        }
 603
 604        branch = argv[0];
 605
 606        if (get_stash_info(&info, argc - 1, argv + 1))
 607                return -1;
 608
 609        cp.git_cmd = 1;
 610        argv_array_pushl(&cp.args, "checkout", "-b", NULL);
 611        argv_array_push(&cp.args, branch);
 612        argv_array_push(&cp.args, oid_to_hex(&info.b_commit));
 613        ret = run_command(&cp);
 614        if (!ret)
 615                ret = do_apply_stash(prefix, &info, 1, 0);
 616        if (!ret && info.is_stash_ref)
 617                ret = do_drop_stash(prefix, &info, 0);
 618
 619        free_stash_info(&info);
 620
 621        return ret;
 622}
 623
 624static int list_stash(int argc, const char **argv, const char *prefix)
 625{
 626        struct child_process cp = CHILD_PROCESS_INIT;
 627        struct option options[] = {
 628                OPT_END()
 629        };
 630
 631        argc = parse_options(argc, argv, prefix, options,
 632                             git_stash_helper_list_usage,
 633                             PARSE_OPT_KEEP_UNKNOWN);
 634
 635        if (!ref_exists(ref_stash))
 636                return 0;
 637
 638        cp.git_cmd = 1;
 639        argv_array_pushl(&cp.args, "log", "--format=%gd: %gs", "-g",
 640                         "--first-parent", "-m", NULL);
 641        argv_array_pushv(&cp.args, argv);
 642        argv_array_push(&cp.args, ref_stash);
 643        argv_array_push(&cp.args, "--");
 644        return run_command(&cp);
 645}
 646
 647int cmd_stash__helper(int argc, const char **argv, const char *prefix)
 648{
 649        pid_t pid = getpid();
 650        const char *index_file;
 651
 652        struct option options[] = {
 653                OPT_END()
 654        };
 655
 656        git_config(git_default_config, NULL);
 657
 658        argc = parse_options(argc, argv, prefix, options, git_stash_helper_usage,
 659                             PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH);
 660
 661        index_file = get_index_file();
 662        strbuf_addf(&stash_index_path, "%s.stash.%" PRIuMAX, index_file,
 663                    (uintmax_t)pid);
 664
 665        if (argc < 1)
 666                usage_with_options(git_stash_helper_usage, options);
 667        if (!strcmp(argv[0], "apply"))
 668                return !!apply_stash(argc, argv, prefix);
 669        else if (!strcmp(argv[0], "clear"))
 670                return !!clear_stash(argc, argv, prefix);
 671        else if (!strcmp(argv[0], "drop"))
 672                return !!drop_stash(argc, argv, prefix);
 673        else if (!strcmp(argv[0], "pop"))
 674                return !!pop_stash(argc, argv, prefix);
 675        else if (!strcmp(argv[0], "branch"))
 676                return !!branch_stash(argc, argv, prefix);
 677        else if (!strcmp(argv[0], "list"))
 678                return !!list_stash(argc, argv, prefix);
 679
 680        usage_msg_opt(xstrfmt(_("unknown subcommand: %s"), argv[0]),
 681                      git_stash_helper_usage, options);
 682}