From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: AS31976 209.132.180.0/23 X-Spam-Status: No, score=-5.6 required=3.0 tests=AWL,BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD shortcircuit=no autolearn=ham autolearn_force=no version=3.4.0 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by dcvr.yhbt.net (Postfix) with ESMTP id 7E7C1203C1 for ; Tue, 13 Dec 2016 01:41:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932595AbcLMBlO (ORCPT ); Mon, 12 Dec 2016 20:41:14 -0500 Received: from mail-pf0-f169.google.com ([209.85.192.169]:33790 "EHLO mail-pf0-f169.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932525AbcLMBlH (ORCPT ); Mon, 12 Dec 2016 20:41:07 -0500 Received: by mail-pf0-f169.google.com with SMTP id d2so15453675pfd.0 for ; Mon, 12 Dec 2016 17:41:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=wQiL2bsd38DPDEK5CF+IlLFBdd1YOqrzRdnRnw9osjo=; b=BRHMK3PIRStyCqQUHoKJdBeVBvXLTlWUF3XjQ8d9qy7S20Jua+sxoI9rFuTMIZDrmk 15UdjQ55IEuLjgZbD5+/XsGjL0JSxTCkJ1eTK9S+RmN3ThnOw2E4raoM1l0s2OH+j6eP CDzHwOs2+Y71jXaxVW+Fgwia5ondZTvTFXDcdWh7CJg8zXeU3CGPQPQeBV9mS7wzPIi4 qcjn54WK5uPNOiY9Qu2E2t7/5ZDhdMotkbza48yXhc3aLbh/yjDtopPjFGE4xfwJRFbG 3p08EOzcyyNv/40jeadbqyGWwrQQ9JHaVGwanuXyA72GlkCPmXcrEv+5bA49XLHUPyhi NNjw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=wQiL2bsd38DPDEK5CF+IlLFBdd1YOqrzRdnRnw9osjo=; b=WqfiCNCniblSlxZdcWtnwZxy4xz0Bw/ttKY/eYWlYrXYs6DPxNngp0NcoWl2cJdvey cEyU8DQHDAPXZ6TPU0U1E3+NbFQdVi1RfQtvH6NAedr0AXqV6zK5kQzBYo680pGDiow5 oBN3mr7mFiGonILeHXy//nkPgtkk3c9jbnj8l4+YFqWVRFT2MupfdOrukmntUWBb2vID YRDpkFCY4oiEK6eliEWPoMtmBNjJHKrB5Y8urruE1ctD9TyyjRaJiTtge7gTamz1a9R1 PTCc0BZNIzpjWiTV18pMI+OWuf2ro6ef9QQ60Q/5BJxbMjBZB/q/8guhibRai0+hj/Rt 8esg== X-Gm-Message-State: AKaTC00Mj8tFOudVWV0mDMDXEmunYwX7U3J7CWrpwig58zgjLZb7G488NoQTzEpoJKT7QNZU X-Received: by 10.98.141.153 with SMTP id p25mr98220203pfk.148.1481593265917; Mon, 12 Dec 2016 17:41:05 -0800 (PST) Received: from localhost ([2620:0:1000:5b10:642c:37a4:3709:a2b3]) by smtp.gmail.com with ESMTPSA id 140sm78707145pgg.0.2016.12.12.17.41.05 (version=TLS1_2 cipher=AES128-SHA bits=128/128); Mon, 12 Dec 2016 17:41:05 -0800 (PST) From: Stefan Beller To: gitster@pobox.com Cc: git@vger.kernel.org, David.Turner@twosigma.com, bmwill@google.com, Stefan Beller Subject: [PATCH 6/6] rm: add absorb a submodules git dir before deletion Date: Mon, 12 Dec 2016 17:40:55 -0800 Message-Id: <20161213014055.14268-7-sbeller@google.com> X-Mailer: git-send-email 2.11.0.rc2.35.g26e18c9 In-Reply-To: <20161213014055.14268-1-sbeller@google.com> References: <20161213014055.14268-1-sbeller@google.com> Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org When deleting a submodule we need to keep the actual git directory around, such that we do not lose local changes in there and at a later checkout of the submodule we don't need to clone it again. Implement `depopulate_submodule`, that migrates the git directory before deletion of a submodule and afterwards the equivalent of "rm -rf", which is already found in entry.c, so expose that and for clarity add a suffix "_or_dir" to it. Signed-off-by: Stefan Beller --- builtin/rm.c | 18 ++++++------------ cache.h | 2 ++ entry.c | 5 +++++ submodule.c | 31 +++++++++++++++++++++++++++++++ submodule.h | 6 ++++++ t/t3600-rm.sh | 41 ++++++++++++++++------------------------- 6 files changed, 66 insertions(+), 37 deletions(-) diff --git a/builtin/rm.c b/builtin/rm.c index fdd7183f61..f8c5e9b6c6 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -400,18 +400,12 @@ int cmd_rm(int argc, const char **argv, const char *prefix) continue; } } else { - strbuf_reset(&buf); - strbuf_addstr(&buf, path); - if (!remove_dir_recursively(&buf, 0)) { - removed = 1; - if (!remove_path_from_gitmodules(path)) - gitmodules_modified = 1; - strbuf_release(&buf); - continue; - } else if (!file_exists(path)) - /* Submodule was removed by user */ - if (!remove_path_from_gitmodules(path)) - gitmodules_modified = 1; + if (file_exists(path)) + depopulate_submodule(path); + removed = 1; + if (!remove_path_from_gitmodules(path)) + gitmodules_modified = 1; + continue; /* Fallthrough and let remove_path() fail. */ } } diff --git a/cache.h b/cache.h index a50a61a197..b645ca2f9a 100644 --- a/cache.h +++ b/cache.h @@ -2018,4 +2018,6 @@ void sleep_millisec(int millisec); */ void safe_create_dir(const char *dir, int share); +extern void remove_directory_or_die(struct strbuf *path); + #endif /* CACHE_H */ diff --git a/entry.c b/entry.c index c6eea240b6..02c4ac9f22 100644 --- a/entry.c +++ b/entry.c @@ -73,6 +73,11 @@ static void remove_subtree(struct strbuf *path) die_errno("cannot rmdir '%s'", path->buf); } +void remove_directory_or_die(struct strbuf *path) +{ + remove_subtree(path); +} + static int create_file(const char *path, unsigned int mode) { mode = (mode & 0100) ? 0777 : 0666; diff --git a/submodule.c b/submodule.c index e42efa2337..3770ecb7b9 100644 --- a/submodule.c +++ b/submodule.c @@ -308,6 +308,37 @@ static void print_submodule_summary(struct rev_info *rev, FILE *f, strbuf_release(&sb); } +void depopulate_submodule(const char *path) +{ + struct strbuf pathbuf = STRBUF_INIT; + char *dot_git = xstrfmt("%s/.git", path); + + /* Is it populated? */ + if (!resolve_gitdir(dot_git)) + goto out; + + /* Does it have a .git directory? */ + if (!submodule_uses_gitfile(path)) { + absorb_git_dir_into_superproject("", path, + ABSORB_GITDIR_RECURSE_SUBMODULES); + + if (!submodule_uses_gitfile(path)) { + /* + * We should be using a gitfile by now. Let's double + * check as losing the git dir would be fatal. + */ + die("BUG: could not absorb git directory for '%s'", path); + } + } + + strbuf_addstr(&pathbuf, path); + remove_directory_or_die(&pathbuf); + +out: + strbuf_release(&pathbuf); + free(dot_git); +} + /* Helper function to display the submodule header line prior to the full * summary output. If it can locate the submodule objects directory it will * attempt to lookup both the left and right commits and put them into the diff --git a/submodule.h b/submodule.h index 3ed3aa479a..516e377a12 100644 --- a/submodule.h +++ b/submodule.h @@ -53,6 +53,12 @@ extern void show_submodule_inline_diff(FILE *f, const char *path, const char *del, const char *add, const char *reset, const struct diff_options *opt); extern void set_config_fetch_recurse_submodules(int value); + +/* + * Removes a submodule from a given path. When the submodule contains its + * git directory instead of a gitlink, migrate that first into the superproject. + */ +extern void depopulate_submodule(const char *path); extern void check_for_new_submodule_commits(unsigned char new_sha1[20]); extern int fetch_populated_submodules(const struct argv_array *options, const char *prefix, int command_line_option, diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh index 5e5a16c863..5aa6db584c 100755 --- a/t/t3600-rm.sh +++ b/t/t3600-rm.sh @@ -569,26 +569,22 @@ test_expect_success 'rm of a conflicted unpopulated submodule succeeds' ' test_cmp expect actual ' -test_expect_success 'rm of a populated submodule with a .git directory fails even when forced' ' +test_expect_success 'rm of a populated submodule with a .git directory migrates git dir' ' git checkout -f master && git reset --hard && git submodule update && (cd submod && rm .git && cp -R ../.git/modules/sub .git && - GIT_WORK_TREE=. git config --unset core.worktree + GIT_WORK_TREE=. git config --unset core.worktree && + rm -r ../.git/modules/sub ) && - test_must_fail git rm submod && - test -d submod && - test -d submod/.git && - git status -s -uno --ignore-submodules=none >actual && - ! test -s actual && - test_must_fail git rm -f submod && - test -d submod && - test -d submod/.git && + git rm submod 2>output.err && + ! test -d submod && + ! test -d submod/.git && git status -s -uno --ignore-submodules=none >actual && - ! test -s actual && - rm -rf submod + test -s actual && + test_i18ngrep Migrating output.err ' cat >expect.deepmodified <actual && - ! test -s actual && - test_must_fail git rm -f submod && - test -d submod && - test -d submod/subsubmod/.git && + git rm submod 2>output.err && + ! test -d submod && + ! test -d submod/subsubmod/.git && git status -s -uno --ignore-submodules=none >actual && - ! test -s actual && - rm -rf submod + test -s actual && + test_i18ngrep Migrating output.err ' test_expect_success 'checking out a commit after submodule removal needs manual updates' ' - git commit -m "submodule removal" submod && + git commit -m "submodule removal" submod .gitmodules && git checkout HEAD^ && git submodule update && - git checkout -q HEAD^ 2>actual && + git checkout -q HEAD^ && git checkout -q master 2>actual && test_i18ngrep "^warning: unable to rmdir submod:" actual && git status -s submod >actual && -- 2.11.0.rc2.35.g7af3268