From patchwork Sun Oct 21 14:02:27 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Duy Nguyen X-Patchwork-Id: 10650839 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 49931109C for ; Sun, 21 Oct 2018 14:02:40 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3843C28782 for ; Sun, 21 Oct 2018 14:02:40 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 2C1CF2878E; Sun, 21 Oct 2018 14:02:40 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 87EFE28782 for ; Sun, 21 Oct 2018 14:02:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727771AbeJUWRE (ORCPT ); Sun, 21 Oct 2018 18:17:04 -0400 Received: from mail-lf1-f68.google.com ([209.85.167.68]:38759 "EHLO mail-lf1-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727477AbeJUWRE (ORCPT ); Sun, 21 Oct 2018 18:17:04 -0400 Received: by mail-lf1-f68.google.com with SMTP id x24-v6so12647338lfe.5 for ; Sun, 21 Oct 2018 07:02:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=/tUp/JjRAuES4rzyFhaRn4qBT3ZQ09IMMP520wAn9Ws=; b=foSgqueQA+PxpZnaELgfVAkpQIGLgHoC0uXQV8qciRK6Pt8pt0n4j/KMxuyUaiMctj QFZJFBudq0kdg09405BU9VxgmUAqUcFPK6mwID0VOvMskG0PCEGZVJhE2uTFQ04saNBi InuCKhpS/CNNY+peVDNbEH6hnzWUSIdUXIonnrfsjSPmj/7+PR+7K4Eqlcw1AEHYDEan XDr24Jg9uAWcKctyJsmxjws75W8Nl8ohDAUeVn0WWvcMl2aGkv3ox/bD9zzIkC4Y+qDI EGtFDHFY8WMMoZ+uk4yfhNh3cvKGUH/NNxrCT2GI6jgMfmybU2Kbbtpt0VZzRkaz11sq wffA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=/tUp/JjRAuES4rzyFhaRn4qBT3ZQ09IMMP520wAn9Ws=; b=VFn+Z6gGaKpRQq2leGgM4SVnzGsEwSZ7t4BE48Bi6oo240lLjDhM6pn3Wrr2jmc6vb UeukqXbjXVOEbREXZDxswnUsIJukvmP/+BY3qPd/cxHXOxw8ouplljbl1HMM2FIJRLJ0 //OrajmBQe39BQpUHUFd+U/31jhEF5kHMUys3KUFqFdWU5Qpxb0E+7pbhGx4qbMjnSO0 4UE2VGZynsXUwx9WY5cJ6uclhnFe7odkG2zNHmMrwwWEEpbzFlmmIBw6nFJN3KidMMmI wMQMb9Qt+y00V+2zPvLTvQSNRVVLdu0oVilJwXC+j2ylz2oLvPHBk3b72PeBCy/JbnPe Eifg== X-Gm-Message-State: ABuFfoiwm3ETHdNpjLy+RKfOLX8aRTWkB5Q4cIVAef9+Sz0d7OEuLMoK bv/9gwlYmBmaMaI2oV6JqKgzz4BR X-Google-Smtp-Source: ACcGV62VK5frwgEwNhhY0BvNPaW6ruCmr2p+MRjIis27kupkQi3nsLywID9G7o0TEG/xxi1QKroEHQ== X-Received: by 2002:ac2:434d:: with SMTP id o13-v6mr7442316lfl.129.1540130553916; Sun, 21 Oct 2018 07:02:33 -0700 (PDT) Received: from localhost.localdomain (c80-216-12-205.bredband.comhem.se. [80.216.12.205]) by smtp.gmail.com with ESMTPSA id x6-v6sm6235563lji.36.2018.10.21.07.02.32 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 21 Oct 2018 07:02:33 -0700 (PDT) From: =?utf-8?b?Tmd1eeG7hW4gVGjDoWkgTmfhu41jIER1eQ==?= To: pclouds@gmail.com Cc: avarab@gmail.com, git@vger.kernel.org, gitster@pobox.com, me@ttaylorr.com, sunshine@sunshineco.com, szeder.dev@gmail.com Subject: [PATCH v4 1/2] t1300: extract and use test_cmp_config() Date: Sun, 21 Oct 2018 16:02:27 +0200 Message-Id: <20181021140228.24941-2-pclouds@gmail.com> X-Mailer: git-send-email 2.19.1.647.g708186aaf9 In-Reply-To: <20181021140228.24941-1-pclouds@gmail.com> References: <20181002160658.15891-1-pclouds@gmail.com> <20181021140228.24941-1-pclouds@gmail.com> MIME-Version: 1.0 Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP In many config-related tests it's common to check if a config variable has expected value and we want to print the differences when the test fails. Doing it the normal way is three lines of shell code. Let's add a function do to all this (and a little more). This function has uses outside t1300 as well but I'm not going to convert them all. And it will be used in the next commit where per-worktree config feature is introduced. Signed-off-by: Nguyễn Thái Ngọc Duy --- t/t1300-config.sh | 79 ++++++++++------------------------------- t/test-lib-functions.sh | 23 ++++++++++++ 2 files changed, 42 insertions(+), 60 deletions(-) diff --git a/t/t1300-config.sh b/t/t1300-config.sh index e2cd50ecfc..9652b241c7 100755 --- a/t/t1300-config.sh +++ b/t/t1300-config.sh @@ -76,15 +76,11 @@ EOF test_expect_success 'non-match result' 'test_cmp expect .git/config' test_expect_success 'find mixed-case key by canonical name' ' - echo Second >expect && - git config cores.whatever >actual && - test_cmp expect actual + test_cmp_config Second cores.whatever ' test_expect_success 'find mixed-case key by non-canonical name' ' - echo Second >expect && - git config CoReS.WhAtEvEr >actual && - test_cmp expect actual + test_cmp_config Second CoReS.WhAtEvEr ' test_expect_success 'subsections are not canonicalized by git-config' ' @@ -94,12 +90,8 @@ test_expect_success 'subsections are not canonicalized by git-config' ' [section "SubSection"] key = two EOF - echo one >expect && - git config section.subsection.key >actual && - test_cmp expect actual && - echo two >expect && - git config section.SubSection.key >actual && - test_cmp expect actual + test_cmp_config one section.subsection.key && + test_cmp_config two section.SubSection.key ' cat > .git/config <<\EOF @@ -212,9 +204,7 @@ test_expect_success 'really really mean test' ' ' test_expect_success 'get value' ' - echo alpha >expect && - git config beta.haha >actual && - test_cmp expect actual + test_cmp_config alpha beta.haha ' cat > expect << EOF @@ -251,15 +241,11 @@ test_expect_success 'non-match' ' ' test_expect_success 'non-match value' ' - echo wow >expect && - git config --get nextsection.nonewline !for >actual && - test_cmp expect actual + test_cmp_config wow --get nextsection.nonewline !for ' test_expect_success 'multi-valued get returns final one' ' - echo "wow2 for me" >expect && - git config --get nextsection.nonewline >actual && - test_cmp expect actual + test_cmp_config "wow2 for me" --get nextsection.nonewline ' test_expect_success 'multi-valued get-all returns all' ' @@ -520,21 +506,11 @@ test_expect_success 'editing stdin is an error' ' test_expect_success 'refer config from subdirectory' ' mkdir x && - ( - cd x && - echo strasse >expect && - git config --get --file ../other-config ein.bahn >actual && - test_cmp expect actual - ) - + test_cmp_config -C x strasse --get --file ../other-config ein.bahn ' test_expect_success 'refer config from subdirectory via --file' ' - ( - cd x && - git config --file=../other-config --get ein.bahn >actual && - test_cmp expect actual - ) + test_cmp_config -C x strasse --file=../other-config --get ein.bahn ' cat > expect << EOF @@ -688,16 +664,13 @@ test_expect_success numbers ' test_expect_success '--int is at least 64 bits' ' git config giga.watts 121g && - echo 129922760704 >expect && - git config --int --get giga.watts >actual && - test_cmp expect actual + echo >expect && + test_cmp_config 129922760704 --int --get giga.watts ' test_expect_success 'invalid unit' ' git config aninvalid.unit "1auto" && - echo 1auto >expect && - git config aninvalid.unit >actual && - test_cmp expect actual && + test_cmp_config 1auto aninvalid.unit && test_must_fail git config --int --get aninvalid.unit 2>actual && test_i18ngrep "bad numeric config value .1auto. for .aninvalid.unit. in file .git/config: invalid unit" actual ' @@ -1039,9 +1012,7 @@ test_expect_success '--null --get-regexp' ' test_expect_success 'inner whitespace kept verbatim' ' git config section.val "foo bar" && - echo "foo bar" >expect && - git config section.val >actual && - test_cmp expect actual + test_cmp_config "foo bar" section.val ' test_expect_success SYMLINKS 'symlinked configuration' ' @@ -1809,21 +1780,15 @@ big = 1M EOF test_expect_success 'identical modern --type specifiers are allowed' ' - git config --type=int --type=int core.big >actual && - echo 1048576 >expect && - test_cmp expect actual + test_cmp_config 1048576 --type=int --type=int core.big ' test_expect_success 'identical legacy --type specifiers are allowed' ' - git config --int --int core.big >actual && - echo 1048576 >expect && - test_cmp expect actual + test_cmp_config 1048576 --int --int core.big ' test_expect_success 'identical mixed --type specifiers are allowed' ' - git config --int --type=int core.big >actual && - echo 1048576 >expect && - test_cmp expect actual + test_cmp_config 1048576 --int --type=int core.big ' test_expect_success 'non-identical modern --type specifiers are not allowed' ' @@ -1842,21 +1807,15 @@ test_expect_success 'non-identical mixed --type specifiers are not allowed' ' ' test_expect_success '--type allows valid type specifiers' ' - echo "true" >expect && - git config --type=bool core.foo >actual && - test_cmp expect actual + test_cmp_config true --type=bool core.foo ' test_expect_success '--no-type unsets type specifiers' ' - echo "10" >expect && - git config --type=bool --no-type core.number >actual && - test_cmp expect actual + test_cmp_config 10 --type=bool --no-type core.number ' test_expect_success 'unset type specifiers may be reset to conflicting ones' ' - echo 1048576 >expect && - git config --type=bool --no-type --type=int core.big >actual && - test_cmp expect actual + test_cmp_config 1048576 --type=bool --no-type --type=int core.big ' test_expect_success '--type rejects unknown specifiers' ' diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh index 78d8c3783b..d158c8d0bf 100644 --- a/t/test-lib-functions.sh +++ b/t/test-lib-functions.sh @@ -747,6 +747,29 @@ test_cmp() { $GIT_TEST_CMP "$@" } +# Check that the given config key has the expected value. +# +# test_cmp_config [-C ] +# [...] +# +# for example to check that the value of core.bar is foo +# +# test_cmp_config foo core.bar +# +test_cmp_config() { + local GD && + if test "$1" = "-C" + then + shift && + GD="-C $1" && + shift + fi && + printf "%s\n" "$1" >expect.config && + shift && + git $GD config "$@" >actual.config && + test_cmp expect.config actual.config +} + # test_cmp_bin - helper to compare binary files test_cmp_bin() { From patchwork Sun Oct 21 14:02:28 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Duy Nguyen X-Patchwork-Id: 10650841 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D0D57109C for ; Sun, 21 Oct 2018 14:02:42 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BDB2E28782 for ; Sun, 21 Oct 2018 14:02:42 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B12412878E; Sun, 21 Oct 2018 14:02:42 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2F69128782 for ; Sun, 21 Oct 2018 14:02:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727776AbeJUWRG (ORCPT ); Sun, 21 Oct 2018 18:17:06 -0400 Received: from mail-lf1-f65.google.com ([209.85.167.65]:33771 "EHLO mail-lf1-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727266AbeJUWRG (ORCPT ); Sun, 21 Oct 2018 18:17:06 -0400 Received: by mail-lf1-f65.google.com with SMTP id o21-v6so28410872lfe.0 for ; Sun, 21 Oct 2018 07:02:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=vTDY9S0lIXn31g7XyV3B2oFKbPtwa4cNxDhkpCXnalc=; b=IF4BMmhnjT4VRiLB9To2Z7NRQSMNY2jDgmHtJlZ73EGZNv9/fGqa4LS0JvBeraiJis du5Y3mQbbinth/Dv9cHjH1eIxj5hvieNZ6w95572LitJFSp7LDLxeTtWRSC7XFU4gyyD Ujkr3yQd2eHGwOxCCKZ+4DS/z6kjOYo0PHaN0kvTksUrOFSfN7AaArNSVnGGaJXAGN1O nAlw+rM+UxWBGQvfa+LDv27WzZwKkpQRnhTslLasSEpYxUoZwhaNiKDnAD5Ar9SGqpTQ rZvGM9TNbu1Bryd3P+a/lwjz669KkACi1N9v2/saJ+I4TwzLxf3V/z/4HosNS+MRRy6k Jq2Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=vTDY9S0lIXn31g7XyV3B2oFKbPtwa4cNxDhkpCXnalc=; b=n4RNwrfbTTPFtRtKwmv5bAAK2TFPP6mZxkXHj/+NbXI5E8RZpU2yKI272gJh6++bIh Hv+QWc+wRyiU64LHnjAYWHEpvVuzBl4h6esgO1ATTS4srbapMsqvw0L45UfD/wIFb9rG ljaJrkuZou4vg/yDlF24Edqcl8DSuqY2hyW2RT+7vVm1DqYVWKjzC1EpKeZzMuL9gQ1z tlodvqhyBCV9y/uc2h6tnjtMUW7rcAnZ/R6vygfm6EnVRNdyaICYK6Maqt8GS/ReMh+f NdXEkWMLclRDo8j/Df+gyMaQTpuTMiOWhWtmCtsctUj53rN8zKsxKzj903cHX30AR/Gv hVnQ== X-Gm-Message-State: ABuFfohAn5k2Ayx/zWvXyuzEV2dQZehwic7ydxA9fJnCN1TN92OYX0o7 mGKrHSJz1yUoPQUm2G3fDYQ8Ns/4 X-Google-Smtp-Source: ACcGV61irf9O+Y1tFERUTmWaDaGUBoIYBeLD4JU77RRZB3Gxz4waN17e5hzm4Ul9N8PRm7a/CQ62qA== X-Received: by 2002:a19:d155:: with SMTP id i82-v6mr6981589lfg.127.1540130555118; Sun, 21 Oct 2018 07:02:35 -0700 (PDT) Received: from localhost.localdomain (c80-216-12-205.bredband.comhem.se. [80.216.12.205]) by smtp.gmail.com with ESMTPSA id x6-v6sm6235563lji.36.2018.10.21.07.02.33 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 21 Oct 2018 07:02:34 -0700 (PDT) From: =?utf-8?b?Tmd1eeG7hW4gVGjDoWkgTmfhu41jIER1eQ==?= To: pclouds@gmail.com Cc: avarab@gmail.com, git@vger.kernel.org, gitster@pobox.com, me@ttaylorr.com, sunshine@sunshineco.com, szeder.dev@gmail.com Subject: [PATCH v4 2/2] worktree: add per-worktree config files Date: Sun, 21 Oct 2018 16:02:28 +0200 Message-Id: <20181021140228.24941-3-pclouds@gmail.com> X-Mailer: git-send-email 2.19.1.647.g708186aaf9 In-Reply-To: <20181021140228.24941-1-pclouds@gmail.com> References: <20181002160658.15891-1-pclouds@gmail.com> <20181021140228.24941-1-pclouds@gmail.com> MIME-Version: 1.0 Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP A new repo extension is added, worktreeConfig. When it is present: - Repository config reading by default includes $GIT_DIR/config _and_ $GIT_DIR/config.worktree. "config" file remains shared in multiple worktree setup. - The special treatment for core.bare and core.worktree, to stay effective only in main worktree, is gone. These config settings are supposed to be in config.worktree. This extension is most useful in multiple worktree setup because you now have an option to store per-worktree config (which is either .git/config.worktree for main worktree, or .git/worktrees/xx/config.worktree for linked ones). This extension can be used in single worktree mode, even though it's pretty much useless (but this can happen after you remove all linked worktrees and move back to single worktree). "git config" reads from both "config" and "config.worktree" by default (i.e. without either --user, --file...) when this extension is present. Default writes still go to "config", not "config.worktree". A new option --worktree is added for that (*). Since a new repo extension is introduced, existing git binaries should refuse to access to the repo (both from main and linked worktrees). So they will not misread the config file (i.e. skip the config.worktree part). They may still accidentally write to the config file anyway if they use with "git config --file ". This design places a bet on the assumption that the majority of config variables are shared so it is the default mode. A safer move would be default writes go to per-worktree file, so that accidental changes are isolated. (*) "git config --worktree" points back to "config" file when this extension is not present and there is only one worktree so that it works in any both single and multiple worktree setups. Signed-off-by: Nguyễn Thái Ngọc Duy --- Documentation/config.txt | 12 +++- Documentation/git-config.txt | 26 ++++++--- Documentation/git-worktree.txt | 33 +++++++++++ Documentation/gitrepository-layout.txt | 8 +++ builtin/config.c | 19 ++++++- cache.h | 2 + config.c | 11 ++++ environment.c | 1 + setup.c | 40 ++++++++++--- t/t2029-worktree-config.sh | 79 ++++++++++++++++++++++++++ 10 files changed, 213 insertions(+), 18 deletions(-) create mode 100755 t/t2029-worktree-config.sh diff --git a/Documentation/config.txt b/Documentation/config.txt index 552827935a..244560a35e 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -2,8 +2,9 @@ CONFIGURATION FILE ------------------ The Git configuration file contains a number of variables that affect -the Git commands' behavior. The `.git/config` file in each repository -is used to store the configuration for that repository, and +the Git commands' behavior. The files `.git/config` and optionally +`config.worktree` (see `extensions.worktreeConfig` below) in each +repository are used to store the configuration for that repository, and `$HOME/.gitconfig` is used to store a per-user configuration as fallback values for the `.git/config` file. The file `/etc/gitconfig` can be used to store a system-wide default configuration. @@ -371,6 +372,13 @@ advice.*:: editor input from the user. -- +extensions.worktreeConfig:: + If set, by default "git config" reads from both "config" and + "config.worktree" file from GIT_DIR in that order. In + multiple working directory mode, "config" file is shared while + "config.worktree" is per-working directory (i.e., it's in + GIT_COMMON_DIR/worktrees//config.worktree) + core.fileMode:: Tells Git if the executable bit of files in the working tree is to be honored. diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index 5e87d82933..1bfe9f56a7 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -45,13 +45,15 @@ unset an existing `--type` specifier with `--no-type`. When reading, the values are read from the system, global and repository local configuration files by default, and options -`--system`, `--global`, `--local` and `--file ` can be -used to tell the command to read from only that location (see <>). +`--system`, `--global`, `--local`, `--worktree` and +`--file ` can be used to tell the command to read from only +that location (see <>). When writing, the new value is written to the repository local configuration file by default, and options `--system`, `--global`, -`--file ` can be used to tell the command to write to -that location (you can say `--local` but that is the default). +`--worktree`, `--file ` can be used to tell the command to +write to that location (you can say `--local` but that is the +default). This command will fail with non-zero status upon error. Some exit codes are: @@ -131,6 +133,11 @@ from all available files. + See also <>. +--worktree:: + Similar to `--local` except that `.git/config.worktree` is + read from or written to if `extensions.worktreeConfig` is + present. If not it's the same as `--local`. + -f config-file:: --file config-file:: Use the given config file instead of the one specified by GIT_CONFIG. @@ -281,6 +288,10 @@ $XDG_CONFIG_HOME/git/config:: $GIT_DIR/config:: Repository specific configuration file. +$GIT_DIR/config.worktree:: + This is optional and is only searched when + `extensions.worktreeConfig` is present in $GIT_DIR/config. + If no further options are given, all reading options will read all of these files that are available. If the global or the system-wide configuration file are not available they will be ignored. If the repository configuration @@ -299,9 +310,10 @@ configuration file. Note that this also affects options like `--replace-all` and `--unset`. *'git config' will only ever change one file at a time*. You can override these rules either by command-line options or by environment -variables. The `--global` and the `--system` options will limit the file used -to the global or system-wide file respectively. The `GIT_CONFIG` environment -variable has a similar effect, but you can specify any filename you want. +variables. The `--global`, `--system` and `--worktree` options will limit +the file used to the global, system-wide or per-worktree file respectively. +The `GIT_CONFIG` environment variable has a similar effect, but you +can specify any filename you want. ENVIRONMENT diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt index e2ee9fc21b..408c87c9ef 100644 --- a/Documentation/git-worktree.txt +++ b/Documentation/git-worktree.txt @@ -204,6 +204,36 @@ working trees, it can be used to identify worktrees. For example if you only have two working trees, at "/abc/def/ghi" and "/abc/def/ggg", then "ghi" or "def/ghi" is enough to point to the former working tree. +CONFIGURATION FILE +------------------ +By default, the repository "config" file is shared across all working +trees. If the config variables `core.bare` or `core.worktree` are +already present in the config file, they will be applied to the main +working trees only. + +In order to have configuration specific to working trees, you can turn +on "worktreeConfig" extension, e.g.: + +------------ +$ git config extensions.worktreeConfig true +------------ + +In this mode, specific configuration stays in the path pointed by `git +rev-parse --git-path config.worktree`. You can add or update +configuration in this file with `git config --worktree`. Older Git +versions will refuse to access repositories with this extension. + +Note that in this file, the exception for `core.bare` and `core.worktree` +is gone. If you have them in $GIT_DIR/config before, you must move +them to the `config.worktree` of the main working tree. You may also +take this opportunity to review and move other configuration that you +do not want to share to all working trees: + + - `core.worktree` and `core.bare` should never be shared + + - `core.sparseCheckout` is recommended per working tree, unless you + are sure you always use sparse checkout for all working trees. + DETAILS ------- Each linked working tree has a private sub-directory in the repository's @@ -253,6 +283,9 @@ to `/path/main/.git/worktrees/test-next` then a file named `test-next` entry from being pruned. See linkgit:gitrepository-layout[5] for details. +When extensions.worktreeConfig is enabled, the config file +`.git/worktrees//config.worktree` is read after `.git/config` is. + LIST OUTPUT FORMAT ------------------ The worktree list command has two output formats. The default format shows the diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt index e85148f05e..36fcca8087 100644 --- a/Documentation/gitrepository-layout.txt +++ b/Documentation/gitrepository-layout.txt @@ -143,6 +143,11 @@ config:: if $GIT_COMMON_DIR is set and "$GIT_COMMON_DIR/config" will be used instead. +config.worktree:: + Working directory specific configuration file for the main + working directory in multiple working directory setup (see + linkgit:git-worktree[1]). + branches:: A slightly deprecated way to store shorthands to be used to specify a URL to 'git fetch', 'git pull' and 'git push'. @@ -275,6 +280,9 @@ worktrees//locked:: or manually by `git worktree prune`. The file may contain a string explaining why the repository is locked. +worktrees//config.worktree:: + Working directory specific configuration file. + SEE ALSO -------- linkgit:git-init[1], diff --git a/builtin/config.c b/builtin/config.c index 97b58c4aea..84385ef165 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -5,6 +5,7 @@ #include "parse-options.h" #include "urlmatch.h" #include "quote.h" +#include "worktree.h" static const char *const builtin_config_usage[] = { N_("git config []"), @@ -24,6 +25,7 @@ static char key_delim = ' '; static char term = '\n'; static int use_global_config, use_system_config, use_local_config; +static int use_worktree_config; static struct git_config_source given_config_source; static int actions, type; static char *default_value; @@ -123,6 +125,7 @@ static struct option builtin_config_options[] = { OPT_BOOL(0, "global", &use_global_config, N_("use global config file")), OPT_BOOL(0, "system", &use_system_config, N_("use system config file")), OPT_BOOL(0, "local", &use_local_config, N_("use repository config file")), + OPT_BOOL(0, "worktree", &use_worktree_config, N_("use per-worktree config file")), OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")), OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object")), OPT_GROUP(N_("Action")), @@ -602,6 +605,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) PARSE_OPT_STOP_AT_NON_OPTION); if (use_global_config + use_system_config + use_local_config + + use_worktree_config + !!given_config_source.file + !!given_config_source.blob > 1) { error(_("only one config file at a time")); usage_builtin_config(); @@ -645,7 +649,20 @@ int cmd_config(int argc, const char **argv, const char *prefix) given_config_source.file = git_etc_gitconfig(); else if (use_local_config) given_config_source.file = git_pathdup("config"); - else if (given_config_source.file) { + else if (use_worktree_config) { + struct worktree **worktrees = get_worktrees(0); + if (repository_format_worktree_config) + given_config_source.file = git_pathdup("config.worktree"); + else if (worktrees[0] && worktrees[1]) + die(_("--worktree cannot be used with multiple " + "working trees unless the config\n" + "extension worktreeConfig is enabled. " + "Please read \"CONFIGURATION FILE\"\n" + "section in \"git help worktree\" for details")); + else + given_config_source.file = git_pathdup("config"); + free_worktrees(worktrees); + } else if (given_config_source.file) { if (!is_absolute_path(given_config_source.file) && prefix) given_config_source.file = prefix_filename(prefix, given_config_source.file); diff --git a/cache.h b/cache.h index 59c8a93046..8fd1bb2a57 100644 --- a/cache.h +++ b/cache.h @@ -960,11 +960,13 @@ extern int grafts_replace_parents; extern int repository_format_precious_objects; extern char *repository_format_partial_clone; extern const char *core_partial_clone_filter_default; +extern int repository_format_worktree_config; struct repository_format { int version; int precious_objects; char *partial_clone; /* value of extensions.partialclone */ + int worktree_config; int is_bare; int hash_algo; char *work_tree; diff --git a/config.c b/config.c index 4051e38823..aa0ed854f6 100644 --- a/config.c +++ b/config.c @@ -1695,6 +1695,17 @@ static int do_git_config_sequence(const struct config_options *opts, if (repo_config && !access_or_die(repo_config, R_OK, 0)) ret += git_config_from_file(fn, repo_config, data); + /* + * Note: this should have a new scope, CONFIG_SCOPE_WORKTREE. + * But let's not complicate things before it's actually needed. + */ + if (repository_format_worktree_config) { + char *path = git_pathdup("config.worktree"); + if (!access_or_die(path, R_OK, 0)) + ret += git_config_from_file(fn, path, data); + free(path); + } + current_parsing_scope = CONFIG_SCOPE_CMDLINE; if (git_config_from_parameters(fn, data) < 0) die(_("unable to parse command-line config")); diff --git a/environment.c b/environment.c index 3f3c8746c2..268310b3dc 100644 --- a/environment.c +++ b/environment.c @@ -33,6 +33,7 @@ int ref_paranoia = -1; int repository_format_precious_objects; char *repository_format_partial_clone; const char *core_partial_clone_filter_default; +int repository_format_worktree_config; const char *git_commit_encoding; const char *git_log_output_encoding; const char *apply_default_whitespace; diff --git a/setup.c b/setup.c index b24c811c1c..1be5037f12 100644 --- a/setup.c +++ b/setup.c @@ -402,6 +402,20 @@ void setup_work_tree(void) initialized = 1; } +static int read_worktree_config(const char *var, const char *value, void *vdata) +{ + struct repository_format *data = vdata; + + if (strcmp(var, "core.bare") == 0) { + data->is_bare = git_config_bool(var, value); + } else if (strcmp(var, "core.worktree") == 0) { + if (!value) + return config_error_nonbool(var); + data->work_tree = xstrdup(value); + } + return 0; +} + static int check_repo_format(const char *var, const char *value, void *vdata) { struct repository_format *data = vdata; @@ -423,16 +437,13 @@ static int check_repo_format(const char *var, const char *value, void *vdata) if (!value) return config_error_nonbool(var); data->partial_clone = xstrdup(value); - } else + } else if (!strcmp(ext, "worktreeconfig")) + data->worktree_config = git_config_bool(var, value); + else string_list_append(&data->unknown_extensions, ext); - } else if (strcmp(var, "core.bare") == 0) { - data->is_bare = git_config_bool(var, value); - } else if (strcmp(var, "core.worktree") == 0) { - if (!value) - return config_error_nonbool(var); - data->work_tree = xstrdup(value); } - return 0; + + return read_worktree_config(var, value, vdata); } static int check_repository_format_gently(const char *gitdir, struct repository_format *candidate, int *nongit_ok) @@ -466,7 +477,20 @@ static int check_repository_format_gently(const char *gitdir, struct repository_ repository_format_precious_objects = candidate->precious_objects; repository_format_partial_clone = candidate->partial_clone; + repository_format_worktree_config = candidate->worktree_config; string_list_clear(&candidate->unknown_extensions, 0); + + if (repository_format_worktree_config) { + /* + * pick up core.bare and core.worktree from per-worktree + * config if present + */ + strbuf_addf(&sb, "%s/config.worktree", gitdir); + git_config_from_file(read_worktree_config, sb.buf, candidate); + strbuf_release(&sb); + has_common = 0; + } + if (!has_common) { if (candidate->is_bare != -1) { is_bare_repository_cfg = candidate->is_bare; diff --git a/t/t2029-worktree-config.sh b/t/t2029-worktree-config.sh new file mode 100755 index 0000000000..286121d8de --- /dev/null +++ b/t/t2029-worktree-config.sh @@ -0,0 +1,79 @@ +#!/bin/sh + +test_description="config file in multi worktree" + +. ./test-lib.sh + +test_expect_success 'setup' ' + test_commit start +' + +test_expect_success 'config --worktree in single worktree' ' + git config --worktree foo.bar true && + test_cmp_config true foo.bar +' + +test_expect_success 'add worktrees' ' + git worktree add wt1 && + git worktree add wt2 +' + +test_expect_success 'config --worktree without extension' ' + test_must_fail git config --worktree foo.bar false +' + +test_expect_success 'enable worktreeConfig extension' ' + git config extensions.worktreeConfig true && + test_cmp_config true extensions.worktreeConfig +' + +test_expect_success 'config is shared as before' ' + git config this.is shared && + test_cmp_config shared this.is && + test_cmp_config -C wt1 shared this.is && + test_cmp_config -C wt2 shared this.is +' + +test_expect_success 'config is shared (set from another worktree)' ' + git -C wt1 config that.is also-shared && + test_cmp_config also-shared that.is && + test_cmp_config -C wt1 also-shared that.is && + test_cmp_config -C wt2 also-shared that.is +' + +test_expect_success 'config private to main worktree' ' + git config --worktree this.is for-main && + test_cmp_config for-main this.is && + test_cmp_config -C wt1 shared this.is && + test_cmp_config -C wt2 shared this.is +' + +test_expect_success 'config private to linked worktree' ' + git -C wt1 config --worktree this.is for-wt1 && + test_cmp_config for-main this.is && + test_cmp_config -C wt1 for-wt1 this.is && + test_cmp_config -C wt2 shared this.is +' + +test_expect_success 'core.bare no longer for main only' ' + test_config core.bare true && + test "$(git rev-parse --is-bare-repository)" = true && + test "$(git -C wt1 rev-parse --is-bare-repository)" = true && + test "$(git -C wt2 rev-parse --is-bare-repository)" = true +' + +test_expect_success 'per-worktree core.bare is picked up' ' + git -C wt1 config --worktree core.bare true && + test "$(git rev-parse --is-bare-repository)" = false && + test "$(git -C wt1 rev-parse --is-bare-repository)" = true && + test "$(git -C wt2 rev-parse --is-bare-repository)" = false +' + +test_expect_success 'config.worktree no longer read without extension' ' + git config --unset extensions.worktreeConfig && + test_cmp_config shared this.is && + test_cmp_config -C wt1 shared this.is && + test_cmp_config -C wt2 shared this.is +' + +test_done