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