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