From patchwork Sat Sep 18 23:15:18 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Elijah Newren X-Patchwork-Id: 12504037 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 98B52C433FE for ; Sat, 18 Sep 2021 23:15:33 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6DEFE610A6 for ; Sat, 18 Sep 2021 23:15:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240673AbhIRXQx (ORCPT ); Sat, 18 Sep 2021 19:16:53 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45244 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239641AbhIRXQv (ORCPT ); Sat, 18 Sep 2021 19:16:51 -0400 Received: from mail-wr1-x433.google.com (mail-wr1-x433.google.com [IPv6:2a00:1450:4864:20::433]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5816FC061757 for ; Sat, 18 Sep 2021 16:15:27 -0700 (PDT) Received: by mail-wr1-x433.google.com with SMTP id t8so21528462wrq.4 for ; Sat, 18 Sep 2021 16:15:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=KkNxmtteEZDd/DPFVopftVZzyLOL6If+eKZ7WucQR0I=; b=dDtW0uqOZRTdLQbACPn80d+6QQPmPWLUTVu04iwbsc/20lfiFgyKTsQIUnIMTJ9eVW Hiu8W7PrXUarx7ZqqgNyjEymImamumaLYvy7qi4wdDF0Z+a36con11K2v7eaUztIjXxX K50RQH9UtSU7VWp7FnctNrZoYXOIGa3DasQX8zvVbKrMkBsAEDXONJDw3YcBREwZYmp+ jjsSWRs2aTxqN0Nydoide89YirXh7vuDBeIMCUhqKas/lxBsZSLQvkV4ri76rxR1BOG7 GdYVexOvTI/p6xG5mqo4+aFETsh3IG03TGWmkFrF6P9skHJUEkveIBTrlW2B4q4wL2DK w0kA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=KkNxmtteEZDd/DPFVopftVZzyLOL6If+eKZ7WucQR0I=; b=ioz8wTi9TOy1/iyz6dzMWurfQse0W2hkgL6zZLgfPCtnTWa19AsCgakBPCl68+rHbj hacpNeBfDkUgVsSfKSypM3A8a3ck9ppDYlhC8YI5Sm5R8WSq/EbVvHQ6ndFXN/n8poTU NAUkLm+LU6hdRFoqCSd/ejxHMyLFz03TyinInYcaemktyFo5dUnzMqrytOv6Caa+7Rrs b0gSZ75CXuk8JVpJKtSAGgtf/Mw0X9B6cX17n2JQa+Jco5dwY74QKgr8ZbF7JLaVXJGr 1NhUvjeIr0lU2WlIWVk8Y1Gn8nDCYMT1Idqqtdw7hFHUAVPGE+lW3VRnFBHAlDykGC30 abmw== X-Gm-Message-State: AOAM532TsMsmaOp+FYQDohQhI/8Xzk2bQvKNU8Gx/tr5PdF3bgxLB/xG iI+J93XOxZus9c4H9MAwCLQsI04YG9s= X-Google-Smtp-Source: ABdhPJwWICy0xHW/OxmX4kH873v1WawMaP/6LjJhu93sQpIdPExbo44o3L789PF8FwoTBrMOcNrheQ== X-Received: by 2002:a5d:4c4c:: with SMTP id n12mr19700583wrt.19.1632006925853; Sat, 18 Sep 2021 16:15:25 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id n17sm11044847wrp.17.2021.09.18.16.15.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 18 Sep 2021 16:15:25 -0700 (PDT) Message-Id: In-Reply-To: References: Date: Sat, 18 Sep 2021 23:15:18 +0000 Subject: [PATCH 1/6] t2500: add various tests for nuking untracked files Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: =?utf-8?b?w4Z2YXIgQXJuZmrDtnLDsA==?= Bjarmason , Fedor Biryukov , Elijah Newren , Elijah Newren Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Elijah Newren From: Elijah Newren Noting that unpack_trees treats reset=1 & update=1 as license to nuke untracked files, I looked for code paths that use this combination and tried to generate testcases which demonstrated unintentional loss of untracked files and directories. I found several. I also include testcases for `git reset --{hard,merge,keep}`. A hard reset is perhaps the most direct test of unpack_tree's reset=1 behavior, but we cannot make `git reset --hard` preserve untracked files without some migration work. Also, the two commands `checkout --force` (because of the --force) and `read-tree --reset` (because it's plumbing and we need to keep it backward compatible) were left out as we expect those to continue removing untracked files and directories. Signed-off-by: Elijah Newren --- t/t2500-untracked-overwriting.sh | 244 +++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100755 t/t2500-untracked-overwriting.sh diff --git a/t/t2500-untracked-overwriting.sh b/t/t2500-untracked-overwriting.sh new file mode 100755 index 00000000000..a1a6dfa671e --- /dev/null +++ b/t/t2500-untracked-overwriting.sh @@ -0,0 +1,244 @@ +#!/bin/sh + +test_description='Test handling of overwriting untracked files' + +. ./test-lib.sh + +test_setup_reset () { + test_create_repo reset_$1 && + ( + cd reset_$1 && + test_commit init && + + git branch stable && + git branch work && + + git checkout work && + test_commit foo && + + git checkout stable + ) +} + +test_expect_success 'reset --hard will nuke untracked files/dirs' ' + test_setup_reset hard && + ( + cd reset_hard && + git ls-tree -r stable && + git log --all --name-status --oneline && + git ls-tree -r work && + + mkdir foo.t && + echo precious >foo.t/file && + echo foo >expect && + + git reset --hard work && + + # check that untracked directory foo.t/ was nuked + test_path_is_file foo.t && + test_cmp expect foo.t + ) +' + +test_expect_success 'reset --merge will preserve untracked files/dirs' ' + test_setup_reset merge && + ( + cd reset_merge && + + mkdir foo.t && + echo precious >foo.t/file && + cp foo.t/file expect && + + test_must_fail git reset --merge work 2>error && + test_cmp expect foo.t/file && + grep "Updating.*foo.t.*would lose untracked files" error + ) +' + +test_expect_success 'reset --keep will preserve untracked files/dirs' ' + test_setup_reset keep && + ( + cd reset_keep && + + mkdir foo.t && + echo precious >foo.t/file && + cp foo.t/file expect && + + test_must_fail git reset --merge work 2>error && + test_cmp expect foo.t/file && + grep "Updating.*foo.t.*would lose untracked files" error + ) +' + +test_setup_checkout_m () { + test_create_repo checkout && + ( + cd checkout && + test_commit init && + + test_write_lines file has some >filler && + git add filler && + git commit -m filler && + + git branch stable && + + git switch -c work && + echo stuff >notes.txt && + test_write_lines file has some words >filler && + git add notes.txt filler && + git commit -m filler && + + git checkout stable + ) +} + +test_expect_failure 'checkout -m does not nuke untracked file' ' + test_setup_checkout_m && + ( + cd checkout && + + # Tweak filler + test_write_lines this file has some >filler && + # Make an untracked file, save its contents in "expect" + echo precious >notes.txt && + cp notes.txt expect && + + test_must_fail git checkout -m work && + test_cmp expect notes.txt + ) +' + +test_setup_sequencing () { + test_create_repo sequencing_$1 && + ( + cd sequencing_$1 && + test_commit init && + + test_write_lines this file has some words >filler && + git add filler && + git commit -m filler && + + mkdir -p foo/bar && + test_commit foo/bar/baz && + + git branch simple && + git branch fooey && + + git checkout fooey && + git rm foo/bar/baz.t && + echo stuff >>filler && + git add -u && + git commit -m "changes" && + + git checkout simple && + echo items >>filler && + echo newstuff >>newfile && + git add filler newfile && + git commit -m another + ) +} + +test_expect_failure 'git rebase --abort and untracked files' ' + test_setup_sequencing rebase_abort_and_untracked && + ( + cd sequencing_rebase_abort_and_untracked && + git checkout fooey && + test_must_fail git rebase simple && + + cat init.t && + git rm init.t && + echo precious >init.t && + cp init.t expect && + git status --porcelain && + test_must_fail git rebase --abort && + test_cmp expect init.t + ) +' + +test_expect_failure 'git rebase fast forwarding and untracked files' ' + test_setup_sequencing rebase_fast_forward_and_untracked && + ( + cd sequencing_rebase_fast_forward_and_untracked && + git checkout init && + echo precious >filler && + cp filler expect && + test_must_fail git rebase init simple && + test_cmp expect filler + ) +' + +test_expect_failure 'git rebase --autostash and untracked files' ' + test_setup_sequencing rebase_autostash_and_untracked && + ( + cd sequencing_rebase_autostash_and_untracked && + git checkout simple && + git rm filler && + mkdir filler && + echo precious >filler/file && + cp filler/file expect && + git rebase --autostash init && + test_path_is_file filler/file + ) +' + +test_expect_failure 'git stash and untracked files' ' + test_setup_sequencing stash_and_untracked_files && + ( + cd sequencing_stash_and_untracked_files && + git checkout simple && + git rm filler && + mkdir filler && + echo precious >filler/file && + cp filler/file expect && + git status --porcelain && + git stash push && + git status --porcelain && + test_path_is_file filler/file + ) +' + +test_expect_failure 'git am --abort and untracked dir vs. unmerged file' ' + test_setup_sequencing am_abort_and_untracked && + ( + cd sequencing_am_abort_and_untracked && + git format-patch -1 --stdout fooey >changes.mbox && + test_must_fail git am --3way changes.mbox && + + # Delete the conflicted file; we will stage and commit it later + rm filler && + + # Put an unrelated untracked directory there + mkdir filler && + echo foo >filler/file1 && + echo bar >filler/file2 && + + test_must_fail git am --abort 2>errors && + test_path_is_dir filler && + grep "Updating .filler. would lose untracked files in it" errors + ) +' + +test_expect_failure 'git am --skip and untracked dir vs deleted file' ' + test_setup_sequencing am_skip_and_untracked && + ( + cd sequencing_am_skip_and_untracked && + git checkout fooey && + git format-patch -1 --stdout simple >changes.mbox && + test_must_fail git am --3way changes.mbox && + + # Delete newfile + rm newfile && + + # Put an unrelated untracked directory there + mkdir newfile && + echo foo >newfile/file1 && + echo bar >newfile/file2 && + + # Change our mind about resolutions, just skip this patch + test_must_fail git am --skip 2>errors && + test_path_is_dir newfile && + grep "Updating .newfile. would lose untracked files in it" errors + ) +' + +test_done From patchwork Sat Sep 18 23:15:19 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Elijah Newren X-Patchwork-Id: 12504045 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BB673C43217 for ; Sat, 18 Sep 2021 23:15:33 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9DF64610A6 for ; Sat, 18 Sep 2021 23:15:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240711AbhIRXQ4 (ORCPT ); Sat, 18 Sep 2021 19:16:56 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45250 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240663AbhIRXQw (ORCPT ); Sat, 18 Sep 2021 19:16:52 -0400 Received: from mail-wm1-x331.google.com (mail-wm1-x331.google.com [IPv6:2a00:1450:4864:20::331]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 193B6C061574 for ; Sat, 18 Sep 2021 16:15:28 -0700 (PDT) Received: by mail-wm1-x331.google.com with SMTP id 70so7229284wme.5 for ; Sat, 18 Sep 2021 16:15:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=rWTixTNpeXmUaMnWhtjF/TFEXVOP6u+T23+R/wTJFWQ=; b=QktkJ8TxBkX2Zh6NCnBZPUJZ7iY+4LKq6DC2LrGHDKXM4xvNfYLdwr5k7TBV+Nsisg 2eYOzvHwILiNDQfy/mXwbKYyiC9mD5IgeNbRD3VP5eYpIS7YPVTo48QNLRaUJeUahKAv 2bzRokHOv57RioRBYFcV5TG3ZO6RiSjl/h2eW6qyzPADfb/dFQrcWjViN6+OxBSWsuxC Ymgd+EzVZy9QV2yatJP1gRx0kj1PhKaCHFgnSotNx/eITndJOxwRLQwrU7RzQ8iuYkfp 19Os/fvby2V0XRW9AXk8xW45nrJOFRUfIi1jPzi+nZL/YdzCyXH5EcMniIXyoM1Okl+z pXgw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=rWTixTNpeXmUaMnWhtjF/TFEXVOP6u+T23+R/wTJFWQ=; b=sbxjU94fqunhTHRLEKgmh/Pm+LcjJHH4Q5VOqrg3nqfUS1mEeYDgKBZWaJIas8dCPP 3DZZADej7wMEwMtS/mOoY/79Lxwi/sttglq40tUqrCM/44tQY9jkbJ56jPQcgoNz0WuV ufuZHHx9br31xco/oSovlN9/CSWO6V3NkrugaOpBHaSumtAYyXOZ1TKPSUZR8iWr32yF Du6NfJxpr3fiXT+OBLeNEZJPYZUiWJgG+u1GNEZGy6DrpEMIEhamUDdkUHXsKf79TDZ5 0yIrLHtENTV9S/8wnXCv+IHg6YdGb/1U8gP++58QHOhWWzymYtx/vjd1oaRjx5C/Sv8+ NGjg== X-Gm-Message-State: AOAM533fig8vv0pFCnPNEjvEEPLYXhVaMknp64iDhLDK/jl+/T6OPLJM Z0sUX5hKRqiQXlqhrHM8n9mdX/X+g84= X-Google-Smtp-Source: ABdhPJzn1a4N9vK/KBky6Q4D/QvKile3AMRHI+/O7tNb34pqmJHGdym/Vt3LcGU5pSueRNFtnIQj1A== X-Received: by 2002:a05:600c:1c07:: with SMTP id j7mr9746947wms.171.1632006926614; Sat, 18 Sep 2021 16:15:26 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id u25sm14764046wmj.10.2021.09.18.16.15.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 18 Sep 2021 16:15:26 -0700 (PDT) Message-Id: <45bd05a945f034d03555f04a1ba85835482dc591.1632006923.git.gitgitgadget@gmail.com> In-Reply-To: References: Date: Sat, 18 Sep 2021 23:15:19 +0000 Subject: [PATCH 2/6] Split unpack_trees 'reset' flag into two for untracked handling Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: =?utf-8?b?w4Z2YXIgQXJuZmrDtnLDsA==?= Bjarmason , Fedor Biryukov , Elijah Newren , Elijah Newren Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Elijah Newren From: Elijah Newren Traditionally, unpack_trees_options->reset was used to signal that it was okay to delete any untracked files in the way. This was used by `git read-tree --reset`, but then started appearing in other places as well. However, many of the other uses should not be deleting untracked files in the way. Split this into two separate fields: reset_nuke_untracked reset_keep_untracked and, since many code paths in unpack_trees need to be followed for both of these flags, introduce a third one for convenience: reset_either which is simply an or-ing of the other two. Modify existing callers so that read-tree --reset reset --hard checkout --force continue using reset_nuke_untracked, but so that other callers, including am checkout without --force stash (though currently dead code; reset always had a value of 0) numerous callers from rebase/sequencer to reset_head() will use the new reset_keep_untracked field. Signed-off-by: Elijah Newren --- builtin/am.c | 6 +++++- builtin/checkout.c | 10 +++++++++- builtin/read-tree.c | 11 ++++++++--- builtin/reset.c | 15 +++++++++++++-- builtin/stash.c | 2 +- reset.c | 9 +++++++-- t/t1013-read-tree-submodule.sh | 4 ++-- t/t2500-untracked-overwriting.sh | 6 +++--- unpack-trees.c | 17 ++++++++++++----- unpack-trees.h | 4 +++- 10 files changed, 63 insertions(+), 21 deletions(-) diff --git a/builtin/am.c b/builtin/am.c index c79e0167e98..dbe6cbe6a33 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -1918,8 +1918,12 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset) opts.dst_index = &the_index; opts.update = 1; opts.merge = 1; - opts.reset = reset; + opts.reset_keep_untracked = reset; opts.fn = twoway_merge; + /* Setup opts.dir so that ignored files in the way get overwritten */ + opts.dir = xcalloc(1, sizeof(*opts.dir)); + opts.dir->flags |= DIR_SHOW_IGNORED; + setup_standard_excludes(opts.dir); init_tree_desc(&t[0], head->buffer, head->size); init_tree_desc(&t[1], remote->buffer, remote->size); diff --git a/builtin/checkout.c b/builtin/checkout.c index b5d477919a7..ab0bb4d94f0 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -646,12 +646,20 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o, opts.head_idx = -1; opts.update = worktree; opts.skip_unmerged = !worktree; - opts.reset = 1; + if (o->force) + opts.reset_nuke_untracked = 1; + else + opts.reset_keep_untracked = 1; opts.merge = 1; opts.fn = oneway_merge; opts.verbose_update = o->show_progress; opts.src_index = &the_index; opts.dst_index = &the_index; + if (o->overwrite_ignore) { + opts.dir = xcalloc(1, sizeof(*opts.dir)); + opts.dir->flags |= DIR_SHOW_IGNORED; + setup_standard_excludes(opts.dir); + } init_checkout_metadata(&opts.meta, info->refname, info->commit ? &info->commit->object.oid : null_oid(), NULL); diff --git a/builtin/read-tree.c b/builtin/read-tree.c index 485e7b04794..8b94e1aa261 100644 --- a/builtin/read-tree.c +++ b/builtin/read-tree.c @@ -133,7 +133,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix) N_("3-way merge if no file level merging required")), OPT_BOOL(0, "aggressive", &opts.aggressive, N_("3-way merge in presence of adds and removes")), - OPT_BOOL(0, "reset", &opts.reset, + OPT_BOOL(0, "reset", &opts.reset_keep_untracked, N_("same as -m, but discard unmerged entries")), { OPTION_STRING, 0, "prefix", &opts.prefix, N_("/"), N_("read the tree into the index under /"), @@ -162,6 +162,11 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix) opts.head_idx = -1; opts.src_index = &the_index; opts.dst_index = &the_index; + if (opts.reset_keep_untracked) { + opts.dir = xcalloc(1, sizeof(*opts.dir)); + opts.dir->flags |= DIR_SHOW_IGNORED; + setup_standard_excludes(opts.dir); + } git_config(git_read_tree_config, NULL); @@ -171,7 +176,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix) hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); prefix_set = opts.prefix ? 1 : 0; - if (1 < opts.merge + opts.reset + prefix_set) + if (1 < opts.merge + opts.reset_keep_untracked + prefix_set) die("Which one? -m, --reset, or --prefix?"); /* @@ -183,7 +188,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix) * mode. */ - if (opts.reset || opts.merge || opts.prefix) { + if (opts.reset_keep_untracked || opts.merge || opts.prefix) { if (read_cache_unmerged() && (opts.prefix || opts.merge)) die(_("You need to resolve your current index first")); stage = opts.merge = 1; diff --git a/builtin/reset.c b/builtin/reset.c index 43e855cb887..ba39c4882a6 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -10,6 +10,7 @@ #define USE_THE_INDEX_COMPATIBILITY_MACROS #include "builtin.h" #include "config.h" +#include "dir.h" #include "lockfile.h" #include "tag.h" #include "object.h" @@ -70,9 +71,19 @@ static int reset_index(const char *ref, const struct object_id *oid, int reset_t break; case HARD: opts.update = 1; - /* fallthrough */ + opts.reset_nuke_untracked = 1; + break; + case MIXED: + opts.reset_keep_untracked = 1; /* but opts.update=0, so untracked left alone */ + break; default: - opts.reset = 1; + BUG("invalid reset_type passed to reset_index"); + } + if (opts.reset_keep_untracked) { + /* Setup opts.dir so we can overwrite ignored files */ + opts.dir = xcalloc(1, sizeof(*opts.dir)); + opts.dir->flags |= DIR_SHOW_IGNORED; + setup_standard_excludes(opts.dir); } read_cache_unmerged(); diff --git a/builtin/stash.c b/builtin/stash.c index 8f42360ca91..4ceb3581b47 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -256,7 +256,7 @@ static int reset_tree(struct object_id *i_tree, int update, int reset) opts.src_index = &the_index; opts.dst_index = &the_index; opts.merge = 1; - opts.reset = reset; + opts.reset_keep_untracked = reset; opts.update = update; opts.fn = oneway_merge; diff --git a/reset.c b/reset.c index 79310ae071b..0880c76aef9 100644 --- a/reset.c +++ b/reset.c @@ -1,5 +1,6 @@ #include "git-compat-util.h" #include "cache-tree.h" +#include "dir.h" #include "lockfile.h" #include "refs.h" #include "reset.h" @@ -57,8 +58,12 @@ int reset_head(struct repository *r, struct object_id *oid, const char *action, unpack_tree_opts.update = 1; unpack_tree_opts.merge = 1; init_checkout_metadata(&unpack_tree_opts.meta, switch_to_branch, oid, NULL); - if (!detach_head) - unpack_tree_opts.reset = 1; + if (!detach_head) { + unpack_tree_opts.reset_keep_untracked = 1; + unpack_tree_opts.dir = xcalloc(1, sizeof(*unpack_tree_opts.dir)); + unpack_tree_opts.dir->flags |= DIR_SHOW_IGNORED; + setup_standard_excludes(unpack_tree_opts.dir); + } if (repo_read_index_unmerged(r) < 0) { ret = error(_("could not read index")); diff --git a/t/t1013-read-tree-submodule.sh b/t/t1013-read-tree-submodule.sh index b6df7444c05..4e485c223ad 100755 --- a/t/t1013-read-tree-submodule.sh +++ b/t/t1013-read-tree-submodule.sh @@ -10,10 +10,10 @@ KNOWN_FAILURE_SUBMODULE_OVERWRITE_IGNORED_UNTRACKED=1 test_submodule_switch_recursing_with_args "read-tree -u -m" -test_submodule_forced_switch_recursing_with_args "read-tree -u --reset" +test_submodule_switch_recursing_with_args "read-tree -u --reset" test_submodule_switch "read-tree -u -m" -test_submodule_forced_switch "read-tree -u --reset" +test_submodule_switch "read-tree -u --reset" test_done diff --git a/t/t2500-untracked-overwriting.sh b/t/t2500-untracked-overwriting.sh index a1a6dfa671e..786ec33d63a 100755 --- a/t/t2500-untracked-overwriting.sh +++ b/t/t2500-untracked-overwriting.sh @@ -92,7 +92,7 @@ test_setup_checkout_m () { ) } -test_expect_failure 'checkout -m does not nuke untracked file' ' +test_expect_success 'checkout -m does not nuke untracked file' ' test_setup_checkout_m && ( cd checkout && @@ -138,7 +138,7 @@ test_setup_sequencing () { ) } -test_expect_failure 'git rebase --abort and untracked files' ' +test_expect_success 'git rebase --abort and untracked files' ' test_setup_sequencing rebase_abort_and_untracked && ( cd sequencing_rebase_abort_and_untracked && @@ -155,7 +155,7 @@ test_expect_failure 'git rebase --abort and untracked files' ' ) ' -test_expect_failure 'git rebase fast forwarding and untracked files' ' +test_expect_success 'git rebase fast forwarding and untracked files' ' test_setup_sequencing rebase_fast_forward_and_untracked && ( cd sequencing_rebase_fast_forward_and_untracked && diff --git a/unpack-trees.c b/unpack-trees.c index 5786645f315..d952eebe96a 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -301,7 +301,7 @@ static int check_submodule_move_head(const struct cache_entry *ce, if (!sub) return 0; - if (o->reset) + if (o->reset_nuke_untracked) flags |= SUBMODULE_MOVE_HEAD_FORCE; if (submodule_move_head(ce->name, old_id, new_id, flags)) @@ -1696,6 +1696,13 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options if (len > MAX_UNPACK_TREES) die("unpack_trees takes at most %d trees", MAX_UNPACK_TREES); + if (o->reset_nuke_untracked && o->reset_keep_untracked) + BUG("reset_nuke_untracked and reset_keep_untracked are incompatible"); + + o->reset_either = 0; + if (o->reset_nuke_untracked || o->reset_keep_untracked) + o->reset_either = 1; + trace_performance_enter(); trace2_region_enter("unpack_trees", "unpack_trees", the_repository); @@ -1989,7 +1996,7 @@ static int verify_uptodate_1(const struct cache_entry *ce, */ if ((ce->ce_flags & CE_VALID) || ce_skip_worktree(ce)) ; /* keep checking */ - else if (o->reset || ce_uptodate(ce)) + else if (o->reset_either || ce_uptodate(ce)) return 0; if (!lstat(ce->name, &st)) { @@ -2218,7 +2225,7 @@ static int verify_absent_1(const struct cache_entry *ce, int len; struct stat st; - if (o->index_only || o->reset || !o->update) + if (o->index_only || o->reset_nuke_untracked || !o->update) return 0; len = check_leading_path(ce->name, ce_namelen(ce), 0); @@ -2585,7 +2592,7 @@ int twoway_merge(const struct cache_entry * const *src, if (current) { if (current->ce_flags & CE_CONFLICTED) { - if (same(oldtree, newtree) || o->reset) { + if (same(oldtree, newtree) || o->reset_either) { if (!newtree) return deleted_entry(current, current, o); else @@ -2683,7 +2690,7 @@ int oneway_merge(const struct cache_entry * const *src, if (old && same(old, a)) { int update = 0; - if (o->reset && o->update && !ce_uptodate(old) && !ce_skip_worktree(old) && + if (o->reset_either && o->update && !ce_uptodate(old) && !ce_skip_worktree(old) && !(old->ce_flags & CE_FSMONITOR_VALID)) { struct stat st; if (lstat(old->name, &st) || diff --git a/unpack-trees.h b/unpack-trees.h index 2d88b19dca7..c419bf8b1f9 100644 --- a/unpack-trees.h +++ b/unpack-trees.h @@ -46,7 +46,9 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts, void clear_unpack_trees_porcelain(struct unpack_trees_options *opts); struct unpack_trees_options { - unsigned int reset, + unsigned int reset_nuke_untracked, + reset_keep_untracked, + reset_either, /* internal use only */ merge, update, clone, From patchwork Sat Sep 18 23:15:20 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Elijah Newren X-Patchwork-Id: 12504043 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B56F3C4332F for ; Sat, 18 Sep 2021 23:15:33 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 91A57610A5 for ; Sat, 18 Sep 2021 23:15:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240701AbhIRXQz (ORCPT ); Sat, 18 Sep 2021 19:16:55 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45252 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240667AbhIRXQx (ORCPT ); Sat, 18 Sep 2021 19:16:53 -0400 Received: from mail-wr1-x430.google.com (mail-wr1-x430.google.com [IPv6:2a00:1450:4864:20::430]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A65A8C061757 for ; Sat, 18 Sep 2021 16:15:28 -0700 (PDT) Received: by mail-wr1-x430.google.com with SMTP id g16so21525358wrb.3 for ; Sat, 18 Sep 2021 16:15:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=SGGB3no4HJnRqYS/bkhEDKZRFKhVocDYyLxbxcSmqFM=; b=QysD44TbubAe9CG6IME3cdk5aNjTNfJYWeTEu+CdDjtC6xiMF47KgLDNTUOK+Wuyji W+mLtjBngYycEs9tYJeFf98UrDUHplvCZb8/jKql9dkWvlGQew/FOQAp255nISP8LlFz M5TbW6//0nz68kAtvneffMZRg3sFlIHDlG6Sgp+nfJljRovBKYkwEsdGBlmYDYBIfsuM RSh8UUGEYAd0oTzVpHSGyjve6YifrTIyqVCVrRfqquD6Fvv5OCIOpepIKvOnNA/glzy0 WZChPdAxD7duVOkkIBWa6tx0KY5MbIV4uszoXVsK18JPCBq7YaJWeUuOz4x4oT99gK1g QmyA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=SGGB3no4HJnRqYS/bkhEDKZRFKhVocDYyLxbxcSmqFM=; b=xTI2iUrjRfmLDt5SuM7//OINhbBfa6ilUImbjoVyF++gzbcN2477fcI3AMsaV7+21Z SZYNCnSPhCTmyQ9Fk3i9ZJZWQLKhhov6c6orcSKYfCSNTha41I0ZoJZl/qPUXj2koapG 3KrCILZ4DS8Nfz/JzJ+JR+hYR1iWTle0zNdUqQRkZPkxICbjD6Ij2xbTutEY9IXMhjBG BNpI4XEvwbjCxS2EoxmoAr3p59cTcbRqNMZGTR+PR1de+bytN4r/Pbo3bD9l4F94CO4P XsOlLkaqBR2YWXHB5K9PsUV7Qh+AynEd815tR2HYTDNlVVYctktRslIBMkKrEmgwLsg4 Pdfg== X-Gm-Message-State: AOAM533XZBXr/MlrhLd0c7mR+CRLZXuOVKKHvLJ0jXoeIX5/1Nua0G0r nLWiDiUh18/7TV2LY3SbcFVARVQuY8o= X-Google-Smtp-Source: ABdhPJweRoSrS6NNTWXspowa09LP8dpPnpmrmCzzhFk5TTEAiTxk8Gfbk2OUWxz8jnpc8VIEZL7o9Q== X-Received: by 2002:a5d:64e6:: with SMTP id g6mr4791388wri.151.1632006927354; Sat, 18 Sep 2021 16:15:27 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id k17sm16241142wmj.0.2021.09.18.16.15.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 18 Sep 2021 16:15:26 -0700 (PDT) Message-Id: In-Reply-To: References: Date: Sat, 18 Sep 2021 23:15:20 +0000 Subject: [PATCH 3/6] unpack-trees: avoid nuking untracked dir in way of unmerged file Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: =?utf-8?b?w4Z2YXIgQXJuZmrDtnLDsA==?= Bjarmason , Fedor Biryukov , Elijah Newren , Elijah Newren Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Elijah Newren From: Elijah Newren Signed-off-by: Elijah Newren --- t/t2500-untracked-overwriting.sh | 2 +- unpack-trees.c | 35 ++++++++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/t/t2500-untracked-overwriting.sh b/t/t2500-untracked-overwriting.sh index 786ec33d63a..017946a494f 100755 --- a/t/t2500-untracked-overwriting.sh +++ b/t/t2500-untracked-overwriting.sh @@ -197,7 +197,7 @@ test_expect_failure 'git stash and untracked files' ' ) ' -test_expect_failure 'git am --abort and untracked dir vs. unmerged file' ' +test_expect_success 'git am --abort and untracked dir vs. unmerged file' ' test_setup_sequencing am_abort_and_untracked && ( cd sequencing_am_abort_and_untracked && diff --git a/unpack-trees.c b/unpack-trees.c index d952eebe96a..3b3d1c0ff40 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -2163,9 +2163,15 @@ static int icase_exists(struct unpack_trees_options *o, const char *name, int le return src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE); } +enum absent_checking_type { + COMPLETELY_ABSENT, + ABSENT_ANY_DIRECTORY +}; + static int check_ok_to_remove(const char *name, int len, int dtype, const struct cache_entry *ce, struct stat *st, enum unpack_trees_error_types error_type, + enum absent_checking_type absent_type, struct unpack_trees_options *o) { const struct cache_entry *result; @@ -2200,6 +2206,10 @@ static int check_ok_to_remove(const char *name, int len, int dtype, return 0; } + /* If we only care about directories, then we can remove */ + if (absent_type == ABSENT_ANY_DIRECTORY) + return 0; + /* * The previous round may already have decided to * delete this path, which is in a subdirectory that @@ -2220,6 +2230,7 @@ static int check_ok_to_remove(const char *name, int len, int dtype, */ static int verify_absent_1(const struct cache_entry *ce, enum unpack_trees_error_types error_type, + enum absent_checking_type absent_type, struct unpack_trees_options *o) { int len; @@ -2245,7 +2256,8 @@ static int verify_absent_1(const struct cache_entry *ce, NULL, o); else ret = check_ok_to_remove(path, len, DT_UNKNOWN, NULL, - &st, error_type, o); + &st, error_type, + absent_type, o); } free(path); return ret; @@ -2260,7 +2272,7 @@ static int verify_absent_1(const struct cache_entry *ce, return check_ok_to_remove(ce->name, ce_namelen(ce), ce_to_dtype(ce), ce, &st, - error_type, o); + error_type, absent_type, o); } } @@ -2270,14 +2282,23 @@ static int verify_absent(const struct cache_entry *ce, { if (!o->skip_sparse_checkout && (ce->ce_flags & CE_NEW_SKIP_WORKTREE)) return 0; - return verify_absent_1(ce, error_type, o); + return verify_absent_1(ce, error_type, COMPLETELY_ABSENT, o); +} + +static int verify_absent_if_directory(const struct cache_entry *ce, + enum unpack_trees_error_types error_type, + struct unpack_trees_options *o) +{ + if (!o->skip_sparse_checkout && (ce->ce_flags & CE_NEW_SKIP_WORKTREE)) + return 0; + return verify_absent_1(ce, error_type, ABSENT_ANY_DIRECTORY, o); } static int verify_absent_sparse(const struct cache_entry *ce, enum unpack_trees_error_types error_type, struct unpack_trees_options *o) { - return verify_absent_1(ce, error_type, o); + return verify_absent_1(ce, error_type, COMPLETELY_ABSENT, o); } static int merged_entry(const struct cache_entry *ce, @@ -2351,6 +2372,12 @@ static int merged_entry(const struct cache_entry *ce, * Previously unmerged entry left as an existence * marker by read_index_unmerged(); */ + if (verify_absent_if_directory(merge, + ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o)) { + discard_cache_entry(merge); + return -1; + } + invalidate_ce_path(old, o); } From patchwork Sat Sep 18 23:15:21 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Elijah Newren X-Patchwork-Id: 12504041 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id AB0DFC433F5 for ; Sat, 18 Sep 2021 23:15:33 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 840BA60F9D for ; Sat, 18 Sep 2021 23:15:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240689AbhIRXQy (ORCPT ); Sat, 18 Sep 2021 19:16:54 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45258 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240681AbhIRXQx (ORCPT ); Sat, 18 Sep 2021 19:16:53 -0400 Received: from mail-wr1-x431.google.com (mail-wr1-x431.google.com [IPv6:2a00:1450:4864:20::431]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 30457C06175F for ; Sat, 18 Sep 2021 16:15:29 -0700 (PDT) Received: by mail-wr1-x431.google.com with SMTP id g16so21525372wrb.3 for ; Sat, 18 Sep 2021 16:15:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=SYquEVrbmwckyhynTdj/XoNgNvOdK5s/HwKoK92MltQ=; b=h/C3GX9fppZkVoCkDqysPZJHDsid3HYpMpzcNMHd6Whoj3s30IwZJEjXjeuGvsq00a iZYRUwswpsL4ODkvEYmT3Dsy8N63vcxgCfsNWjmUCrsOk1aFeMgMsv87W2R5B3NUmQXW 5M7locvKLQphlZHnTTB/epntQTWogPXETto6da+pKCMJAPMayrqKWo+ndW1HD+gCIEdf unXtPJy5imQDDs6lauO96OQX+VnaWX2dA09OxfNxwg5gRFzyWQ9w5ce+YbSKAYSHdod9 SxxIsREaSjYbPRrcM8Y3wpZeVumael7N5fGMfmFEMeyqMNqTAGQeJjGNf3x5BD65L84W ccIQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=SYquEVrbmwckyhynTdj/XoNgNvOdK5s/HwKoK92MltQ=; b=LRpYdv1jq+EbJ4aHo68m3jmr4q3x/57fsj6HlxtPltZ+u/K4H0wQyRbKkzf3XwawYI JbDoanZGKxyz7dRupO4XA8nvv7i2dRywIF5ddKSkyaTKo2Oqi1qASMqu2ZaACCfoqt7S QWMM0AvNrxLasMyKAPEtXq3pHIfRiXS7XePw0HOHdySaujL1JlgAMSREXkAfc3tkLYwx HkImnacX1L9fNYziPOxaYR6syiLAUTLJmr/HJ35eTGumgJnYFvFH2JtBoBA/Rj0Aagnr YmtJdGGf28uH+oYppr3WoMaLCzE2mfGGgNd7m3in8A6KXblbs82WHloiLniJJf4vp7h4 exwQ== X-Gm-Message-State: AOAM530b+1z0s3wcHZ16+2L1igKjSXz/IX16F1alQoMQMXq2e6YX3rTD HmA5hq479JhHKOnKn2QK4dQFbEHPirQ= X-Google-Smtp-Source: ABdhPJzE2+EeR230PvepC6h72jI+CgORSgyPofeMZqbX0ZibeoOHQeajGPs+Lm87nmvgyMDUfJkgJg== X-Received: by 2002:a5d:5351:: with SMTP id t17mr20291945wrv.405.1632006927886; Sat, 18 Sep 2021 16:15:27 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id z12sm10877623wro.75.2021.09.18.16.15.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 18 Sep 2021 16:15:27 -0700 (PDT) Message-Id: <01bf850bb0f0796564e9363d7faeb792a594f684.1632006924.git.gitgitgadget@gmail.com> In-Reply-To: References: Date: Sat, 18 Sep 2021 23:15:21 +0000 Subject: [PATCH 4/6] unpack-trees: avoid nuking untracked dir in way of locally deleted file Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: =?utf-8?b?w4Z2YXIgQXJuZmrDtnLDsA==?= Bjarmason , Fedor Biryukov , Elijah Newren , Elijah Newren Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Elijah Newren From: Elijah Newren Signed-off-by: Elijah Newren --- t/t2500-untracked-overwriting.sh | 2 +- unpack-trees.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/t/t2500-untracked-overwriting.sh b/t/t2500-untracked-overwriting.sh index 017946a494f..d4d9dc928aa 100755 --- a/t/t2500-untracked-overwriting.sh +++ b/t/t2500-untracked-overwriting.sh @@ -218,7 +218,7 @@ test_expect_success 'git am --abort and untracked dir vs. unmerged file' ' ) ' -test_expect_failure 'git am --skip and untracked dir vs deleted file' ' +test_expect_success 'git am --skip and untracked dir vs deleted file' ' test_setup_sequencing am_skip_and_untracked && ( cd sequencing_am_skip_and_untracked && diff --git a/unpack-trees.c b/unpack-trees.c index 3b3d1c0ff40..858595a13f1 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -2395,7 +2395,11 @@ static int deleted_entry(const struct cache_entry *ce, if (verify_absent(ce, ERROR_WOULD_LOSE_UNTRACKED_REMOVED, o)) return -1; return 0; + } else { + if (verify_absent_if_directory(ce, ERROR_WOULD_LOSE_UNTRACKED_REMOVED, o)) + return -1; } + if (!(old->ce_flags & CE_CONFLICTED) && verify_uptodate(old, o)) return -1; add_entry(o, ce, CE_REMOVE, 0); From patchwork Sat Sep 18 23:15:22 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Elijah Newren X-Patchwork-Id: 12504049 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1980CC433FE for ; Sat, 18 Sep 2021 23:15:37 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E82AB60F9D for ; Sat, 18 Sep 2021 23:15:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240715AbhIRXQ5 (ORCPT ); Sat, 18 Sep 2021 19:16:57 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45260 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240684AbhIRXQy (ORCPT ); Sat, 18 Sep 2021 19:16:54 -0400 Received: from mail-wm1-x331.google.com (mail-wm1-x331.google.com [IPv6:2a00:1450:4864:20::331]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id EDF4DC061574 for ; Sat, 18 Sep 2021 16:15:29 -0700 (PDT) Received: by mail-wm1-x331.google.com with SMTP id z184-20020a1c7ec1000000b003065f0bc631so12777875wmc.0 for ; Sat, 18 Sep 2021 16:15:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=F0pVzr0KpoBWpnOg7Y2znbxx72Wd3oBvoI/ST7c8rEw=; b=WdX2sTEbjgaZxOns72cV+gv1f97WyWaqjL92BOs0t5FNsPfIGD2hTTs8IUslfnJnZ9 2a1Q9R6Btn6RpgHhl0y3aWt4wkbraPhjGNnIvxO+9Dniy+nVAgVMaE027+LDgyNZ3PZp Tfy6eedIY0XaBedQ0qrIiLOLSqwhLnagmn4pxnNEcFZlJ4UdJg3h1JTS6+p48AjyOp4q vmmOz4r5wr1kDVhIK2Oxk2aITWwngOWXC4Vb/2MEeWe8vsryWOQZ0O/HXTZYPppNgtYZ DVnISK/ybMKIAjOt7vLQcwnZeIiFNgL2wPx3DQAqmJc3ZWhz5PSi4enj/Wl2NPL3I66p /oyQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=F0pVzr0KpoBWpnOg7Y2znbxx72Wd3oBvoI/ST7c8rEw=; b=VNwPkeVrYooxrQToXnZWAnfPj3lYlrsyTeRdWCaXvZUcy4c98o+cOr19c5NoLLNgi5 VgsUdhdmL8ptQpRU7I5AAf/RzGp8EF8KgA2lTbd4kuk7tb/3/dUJilk0bnqksZe3zGTg hyr6tC+zRQ5m/cPP3tGL+uA7RoXUQ0+p+oQusswnofoSAl7zv1XYZ6D/r6bUeG1xwEW9 jOxoJ6BA0MeiF2VHX78c2M65iYuzPPH9uG3NRmJn+7JTl/Nd1VOnxETb7HZVWcw/RbqD Re8s0AGo4DhGir9M00miXgbZCbVZJddRIqT9F58R31sSnvPLckH+Om1egdcWoQVf4rI7 Ik6w== X-Gm-Message-State: AOAM5320Cf3ZDQ3X55uHH7RNiD/5MInW0wixrIu/Vv84011c6HrsJJ9Z MKwO5kurgPOAwM7ZIjQb8tu4jT6bpMs= X-Google-Smtp-Source: ABdhPJwjNUqPnYp3CTvY7ndC293mGVBlVixU70JOGay+qfGISZlYJmkD3oaXKzQbsXSiWYwzXHESGg== X-Received: by 2002:a05:600c:35d2:: with SMTP id r18mr1462843wmq.97.1632006928579; Sat, 18 Sep 2021 16:15:28 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id w9sm10266606wmc.19.2021.09.18.16.15.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 18 Sep 2021 16:15:28 -0700 (PDT) Message-Id: <60c5d6b4615a6ac4179ec6c10e17cca480bc147a.1632006924.git.gitgitgadget@gmail.com> In-Reply-To: References: Date: Sat, 18 Sep 2021 23:15:22 +0000 Subject: [PATCH 5/6] Comment important codepaths regarding nuking untracked files/dirs Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: =?utf-8?b?w4Z2YXIgQXJuZmrDtnLDsA==?= Bjarmason , Fedor Biryukov , Elijah Newren , Elijah Newren Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Elijah Newren From: Elijah Newren In the last few commits we focused on code in unpack-trees.c that mistakenly removed untracked files or directories. There may be more of those, but in this commit we change our focus: callers of toplevel commands that are expected to remove untracked files or directories. As noted previously, we have toplevel commands that are expected to delete untracked files such as 'read-tree --reset', 'reset --hard', and 'checkout --force'. However, that does not mean that other highlevel commands that happen to call these other commands thought about or conveyed to users the possibility that untracked files could be removed. Audit the code for such callsites, and add comments near existing callsites to mention whether these are safe or not. My auditing is somewhat incomplete, though; it skipped several cases: * git-rebase--preserve-merges.sh: is in the process of being deprecated/removed, so I won't leave a note that there are likely more bugs in that script. * contrib/git-new-workdir: why is the -f flag being used in a new empty directory?? It shouldn't hurt, but it seems useless. * git-p4.py: Don't see why -f is needed for a new dir (maybe it's not and is just superfluous), but I'm not at all familiar with the p4 stuff * git-archimport.perl: Don't care; arch is long since dead * git-cvs*.perl: Don't care; cvs is long since dead Signed-off-by: Elijah Newren --- builtin/stash.c | 1 + builtin/submodule--helper.c | 4 ++++ builtin/worktree.c | 5 +++++ contrib/rerere-train.sh | 2 +- submodule.c | 1 + 5 files changed, 12 insertions(+), 1 deletion(-) diff --git a/builtin/stash.c b/builtin/stash.c index 4ceb3581b47..ce5e0364c68 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -1519,6 +1519,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q } else { struct child_process cp = CHILD_PROCESS_INIT; cp.git_cmd = 1; + /* BUG: this nukes untracked files in the way */ strvec_pushl(&cp.args, "reset", "--hard", "-q", "--no-recurse-submodules", NULL); if (run_command(&cp)) { diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index ef2776a9e45..a49242d15ae 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -2864,6 +2864,10 @@ static int add_submodule(const struct add_data *add_data) prepare_submodule_repo_env(&cp.env_array); cp.git_cmd = 1; cp.dir = add_data->sm_path; + /* + * NOTE: we only get here if add_data->force is true, so + * passing --force to checkout is reasonable. + */ strvec_pushl(&cp.args, "checkout", "-f", "-q", NULL); if (add_data->branch) { diff --git a/builtin/worktree.c b/builtin/worktree.c index 0d0a80da61f..383947ff54f 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -356,6 +356,11 @@ static int add_worktree(const char *path, const char *refname, if (opts->checkout) { cp.argv = NULL; strvec_clear(&cp.args); + /* + * NOTE: reset --hard is okay here, because 'worktree add' + * refuses to work in an extant non-empty directory, so there + * is no risk of deleting untracked files. + */ strvec_pushl(&cp.args, "reset", "--hard", "--no-recurse-submodules", NULL); if (opts->quiet) strvec_push(&cp.args, "--quiet"); diff --git a/contrib/rerere-train.sh b/contrib/rerere-train.sh index eeee45dd341..75125d6ae00 100755 --- a/contrib/rerere-train.sh +++ b/contrib/rerere-train.sh @@ -91,7 +91,7 @@ do git checkout -q $commit -- . git rerere fi - git reset -q --hard + git reset -q --hard # Might nuke untracked files... done if test -z "$branch" diff --git a/submodule.c b/submodule.c index 8e611fe1dbf..a9b71d585cf 100644 --- a/submodule.c +++ b/submodule.c @@ -1866,6 +1866,7 @@ static void submodule_reset_index(const char *path) strvec_pushf(&cp.args, "--super-prefix=%s%s/", get_super_prefix_or_empty(), path); + /* TODO: determine if this might overwright untracked files */ strvec_pushl(&cp.args, "read-tree", "-u", "--reset", NULL); strvec_push(&cp.args, empty_tree_oid_hex()); From patchwork Sat Sep 18 23:15:23 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Elijah Newren X-Patchwork-Id: 12504047 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 175ADC433F5 for ; Sat, 18 Sep 2021 23:15:37 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 01A39610A5 for ; Sat, 18 Sep 2021 23:15:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240717AbhIRXQ6 (ORCPT ); Sat, 18 Sep 2021 19:16:58 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45266 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240697AbhIRXQy (ORCPT ); Sat, 18 Sep 2021 19:16:54 -0400 Received: from mail-wr1-x435.google.com (mail-wr1-x435.google.com [IPv6:2a00:1450:4864:20::435]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9BB94C061574 for ; Sat, 18 Sep 2021 16:15:30 -0700 (PDT) Received: by mail-wr1-x435.google.com with SMTP id t18so21601814wrb.0 for ; Sat, 18 Sep 2021 16:15:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=/3m+7QqjHSrfqnwKbprHOY8NTr4Stirdf/5VwuV4OCM=; b=jNsX3EKQDvJiWo9duKajnqAwEeTxC/BYQrY5TOJicctG11tfGJOP0gnMfuemd/oX7J jaeAaPz5uJYNqmNR2FuleboLEmU5COccKiWvb18eL1LMwBa8NLVdTPyvXT+WlaFCEOc/ kBQ1J3yhLTPtIR94Kho8cK51332GK2RZXHEBctp7+9CnJ/SQtqu34ESfdYD2tRyzAJeJ US6IECX+1+FC/OGxTAHZk9Et+rngMv2iRFzicI2GlLhgGtULZ1rObw0xOhsKliTW47wv CQSdYX+qRHK4P1/YSoAFUuf0baiH2A5J5q03D9dUiJDrZBt7TV2NU78LBcb2zCyOKDFD mv/Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=/3m+7QqjHSrfqnwKbprHOY8NTr4Stirdf/5VwuV4OCM=; b=NwKP92m1F0qG/MNmMvKCjCvs6RK2hiG6RbH+mB3/AbWW9hWaldyY6zH9EZ4dmPpkWY CAOZ+E1kLbhfIs9bHY1GXWPZQnAER2MkoKVm9m6QkOIxI3oyzJzObTKO42QU7vibfhfC h2jvdvxl4KE0vx+H9fVhC5GFpmmSpaFn19Kb9BxKPSgNjBhwMjqG7vLM6SkcY2lsnhjQ uI7jNp6VCFNwzfZIH01S1c6qBWVH0W7T57Vi6VnCgYmRUT8HSA2bOMoa4GHyz2gusV/W MDuNKaWPRRJU73JiuOye+b5DuCKfFerwRutPrxCqMo2tRyilRI1eB1hieTewF0WixfUz wkUA== X-Gm-Message-State: AOAM531uYfoPeaDVUtw7w2mOX+Qlu72/jwknEho8jprtTcijZcPJyd/S FeinQZdO2mNwpw6qiV5wxiZdNRv7boE= X-Google-Smtp-Source: ABdhPJx0EsfDDWvIKq06fE6loS/nYUxZG0mswXRuOL0KFZla7eJfeaZSKlFmnA7WTYs8CrnQx/W3vg== X-Received: by 2002:adf:f011:: with SMTP id j17mr19854082wro.320.1632006929247; Sat, 18 Sep 2021 16:15:29 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id u13sm11179301wrt.41.2021.09.18.16.15.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 18 Sep 2021 16:15:28 -0700 (PDT) Message-Id: <6ea23d165cf1f9433831cb4a01abf6f483a8bc29.1632006924.git.gitgitgadget@gmail.com> In-Reply-To: References: Date: Sat, 18 Sep 2021 23:15:23 +0000 Subject: [PATCH 6/6] Documentation: call out commands that nuke untracked files/directories Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: =?utf-8?b?w4Z2YXIgQXJuZmrDtnLDsA==?= Bjarmason , Fedor Biryukov , Elijah Newren , Elijah Newren Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Elijah Newren From: Elijah Newren Some commands have traditionally also removed untracked files (or directories) that were in the way of a tracked file we needed. Document these cases. Signed-off-by: Elijah Newren --- Documentation/git-checkout.txt | 5 +++-- Documentation/git-read-tree.txt | 5 +++-- Documentation/git-reset.txt | 3 ++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index b1a6fe44997..d473c9bf387 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -118,8 +118,9 @@ OPTIONS -f:: --force:: When switching branches, proceed even if the index or the - working tree differs from `HEAD`. This is used to throw away - local changes. + working tree differs from `HEAD`, and even if there are untracked + files in the way. This is used to throw away local changes and + any untracked files or directories that are in the way. + When checking out paths from the index, do not fail upon unmerged entries; instead, unmerged entries are ignored. diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt index 5fa8bab64c2..4731ec3283f 100644 --- a/Documentation/git-read-tree.txt +++ b/Documentation/git-read-tree.txt @@ -39,8 +39,9 @@ OPTIONS --reset:: Same as -m, except that unmerged entries are discarded instead - of failing. When used with `-u`, updates leading to loss of - working tree changes will not abort the operation. + of failing. When used with `-u`, updates leading to loss of + working tree changes or untracked files or directories will not + abort the operation. -u:: After a successful merge, update the files in the work diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index 252e2d4e47d..6f7685f53d5 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -69,7 +69,8 @@ linkgit:git-add[1]). --hard:: Resets the index and working tree. Any changes to tracked files in the - working tree since `` are discarded. + working tree since `` are discarded. Any untracked files or + directories in the way of writing any tracked files are simply deleted. --merge:: Resets the index and updates the files in the working tree that are