From: Phillip Wood <phillip.wood123@gmail.com>
To: Derrick Stolee via GitGitGadget <gitgitgadget@gmail.com>,
git@vger.kernel.org
Cc: gitster@pobox.com, johannes.schindelin@gmx.de, me@ttaylorr.com,
"Jeff Hostetler" <git@jeffhostetler.com>,
"Elijah Newren" <newren@gmail.com>,
"Derrick Stolee" <derrickstolee@github.com>,
"Ævar Arnfjörð Bjarmason" <avarab@gmail.com>
Subject: Re: [PATCH 1/4] branch: add branch_checked_out() helper
Date: Tue, 14 Jun 2022 11:09:15 +0100 [thread overview]
Message-ID: <42ada1a3-adc2-2c43-9aa1-7854858865ae@gmail.com> (raw)
In-Reply-To: <dbb7eae390c90d4b710f48d8875bd7db0409aea3.1654718942.git.gitgitgadget@gmail.com>
Hi Stolee
On 08/06/2022 21:08, Derrick Stolee via GitGitGadget wrote:
> From: Derrick Stolee <derrickstolee@github.com>
>
> The validate_new_branchname() method contains a check to see if a branch
> is checked out in any non-bare worktree. This is intended to prevent a
> force push that will mess up an existing checkout. This helper is not
> suitable to performing just that check, because the method will die()
> when the branch is checked out instead of returning an error code.
>
> Create a new branch_checked_out() helper that performs the most basic
> form of this check. To ensure we can call branch_checked_out() in a loop
> with good performance, do a single preparation step that iterates over
> all worktrees and stores their current HEAD branches in a strmap. The
> branch_checked_out() helper can then discover these branches using a
> hash lookup.
>
> This helper is currently missing some key functionality. Namely: it
> doesn't look for active rebases or bisects which mean that the branch is
> "checked out" even though HEAD doesn't point to that ref. This
> functionality will be added in a coming change.
>
> We could use branch_checked_out() in validate_new_branchname(), but this
> missing functionality would be a regression. However, we have no tests
> that cover this case!
>
> Add a new test script that will be expanded with these cross-worktree
> ref updates. The current tests would still pass if we refactored
> validate_new_branchname() to use this version of branch_checked_out().
> The next change will fix that functionality and add the proper test
> coverage.
>
> Signed-off-by: Derrick Stolee <derrickstolee@github.com>
> ---
> branch.c | 42 +++++++++++++++++++++++++++++++++++++++
> branch.h | 8 ++++++++
> t/t2407-worktree-heads.sh | 24 ++++++++++++++++++++++
> 3 files changed, 74 insertions(+)
> create mode 100755 t/t2407-worktree-heads.sh
>
> diff --git a/branch.c b/branch.c
> index 2d6569b0c62..061a11f3415 100644
> --- a/branch.c
> +++ b/branch.c
> @@ -10,6 +10,7 @@
> #include "worktree.h"
> #include "submodule-config.h"
> #include "run-command.h"
> +#include "strmap.h"
>
> struct tracking {
> struct refspec_item spec;
> @@ -369,6 +370,47 @@ int validate_branchname(const char *name, struct strbuf *ref)
> return ref_exists(ref->buf);
> }
>
> +static int initialized_checked_out_branches;
> +static struct strmap current_checked_out_branches = STRMAP_INIT;
I looks like this map is never freed which I think makes sense but makes
me wonder about the relevance of patch 5. I think it would probably be
worth marking the map with UNLEAK() in prepare_checked_out_branches().
> +static void prepare_checked_out_branches(void)
> +{
> + int i = 0;
> + struct worktree **worktrees;
> +
> + if (initialized_checked_out_branches)
> + return;
> + initialized_checked_out_branches = 1;
> +
> + worktrees = get_worktrees();
> +
> + while (worktrees[i]) {
> + struct worktree *wt = worktrees[i++];
> +
> + if (wt->is_bare)
> + continue;
> +
> + if (wt->head_ref)
> + strmap_put(¤t_checked_out_branches,
> + wt->head_ref,
> + xstrdup(wt->path));
STRMAP_INIT sets .strdup_strings = 1, so the xstrdup() is unnecessary.
> + }
> +
> + free_worktrees(worktrees);
> +}
> +
> +int branch_checked_out(const char *refname, char **path)
> +{
> + const char *path_in_set;
> + prepare_checked_out_branches();
> +
> + path_in_set = strmap_get(¤t_checked_out_branches, refname);
> + if (path_in_set && path)
> + *path = xstrdup(path_in_set);
> +
> + return !!path_in_set;
> +}
I like the idea of having a specific function to see if a branch is
checkout out rather than Ævar's suggestion of forcing all callers to do
strmap_get(get_worktree_refs_strmap(), ref)
which will quickly get tiresome. I do wonder though if we'd be better
off with a thin wrapper around strmap_get() such as
const char* branch_checked_out(const char *refname)
{
prepare_checked_out_branches();
return strmap_get(¤t_checked_out_branches, refname);
}
so that the user can choose whether to copy the path or not.
Best Wishes
Phillip
> /*
> * Check if a branch 'name' can be created as a new branch; die otherwise.
> * 'force' can be used when it is OK for the named branch already exists.
> diff --git a/branch.h b/branch.h
> index 560b6b96a8f..5ea93d217b1 100644
> --- a/branch.h
> +++ b/branch.h
> @@ -101,6 +101,14 @@ void create_branches_recursively(struct repository *r, const char *name,
> const char *tracking_name, int force,
> int reflog, int quiet, enum branch_track track,
> int dry_run);
> +
> +/*
> + * Returns true if the branch at 'refname' is checked out at any
> + * non-bare worktree. The path of the worktree is stored in the
> + * given 'path', if provided.
> + */
> +int branch_checked_out(const char *refname, char **path);
> +
> /*
> * Check if 'name' can be a valid name for a branch; die otherwise.
> * Return 1 if the named branch already exists; return 0 otherwise.
> diff --git a/t/t2407-worktree-heads.sh b/t/t2407-worktree-heads.sh
> new file mode 100755
> index 00000000000..dd905dc1a5c
> --- /dev/null
> +++ b/t/t2407-worktree-heads.sh
> @@ -0,0 +1,24 @@
> +#!/bin/sh
> +
> +test_description='test operations trying to overwrite refs at worktree HEAD'
> +
> +. ./test-lib.sh
> +
> +test_expect_success 'setup' '
> + for i in 1 2 3 4
> + do
> + test_commit $i &&
> + git branch wt-$i &&
> + git worktree add wt-$i wt-$i || return 1
> + done
> +'
> +
> +test_expect_success 'refuse to overwrite: checked out in worktree' '
> + for i in 1 2 3 4
> + do
> + test_must_fail git branch -f wt-$i HEAD 2>err
> + grep "cannot force update the branch" err || return 1
> + done
> +'
> +
> +test_done
next prev parent reply other threads:[~2022-06-14 10:10 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-06-08 20:08 [PATCH 0/4] Create branch_checked_out() helper Derrick Stolee via GitGitGadget
2022-06-08 20:08 ` [PATCH 1/4] branch: add " Derrick Stolee via GitGitGadget
2022-06-09 23:47 ` Junio C Hamano
2022-06-13 23:59 ` Ævar Arnfjörð Bjarmason
2022-06-14 13:32 ` Derrick Stolee
2022-06-14 15:24 ` Ævar Arnfjörð Bjarmason
2022-06-14 10:09 ` Phillip Wood [this message]
2022-06-14 11:22 ` Phillip Wood
2022-06-14 13:48 ` Derrick Stolee
2022-06-08 20:09 ` [PATCH 2/4] branch: check for bisects and rebases Derrick Stolee via GitGitGadget
2022-06-08 22:03 ` Junio C Hamano
2022-06-08 20:09 ` [PATCH 3/4] fetch: use new branch_checked_out() and add tests Derrick Stolee via GitGitGadget
2022-06-14 0:05 ` Ævar Arnfjörð Bjarmason
2022-06-14 10:10 ` Phillip Wood
2022-06-14 14:06 ` Derrick Stolee
2022-06-08 20:09 ` [PATCH 4/4] branch: use branch_checked_out() when deleting refs Derrick Stolee via GitGitGadget
2022-06-13 14:59 ` [PATCH 5/5] branch: fix branch_checked_out() leaks Derrick Stolee
2022-06-13 23:03 ` Junio C Hamano
2022-06-14 0:33 ` Ævar Arnfjörð Bjarmason
2022-06-14 15:37 ` Derrick Stolee
2022-06-14 16:43 ` Ævar Arnfjörð Bjarmason
2022-06-14 19:27 ` [PATCH v2 0/5] Create branch_checked_out() helper Derrick Stolee via GitGitGadget
2022-06-14 19:27 ` [PATCH v2 1/5] branch: add " Derrick Stolee via GitGitGadget
2022-06-14 19:27 ` [PATCH v2 2/5] branch: check for bisects and rebases Derrick Stolee via GitGitGadget
2022-06-14 19:27 ` [PATCH v2 3/5] fetch: use new branch_checked_out() and add tests Derrick Stolee via GitGitGadget
2022-06-14 19:27 ` [PATCH v2 4/5] branch: use branch_checked_out() when deleting refs Derrick Stolee via GitGitGadget
2022-06-14 19:27 ` [PATCH v2 5/5] branch: fix branch_checked_out() leaks Derrick Stolee via GitGitGadget
2022-06-19 13:58 ` [PATCH v2 0/5] Create branch_checked_out() helper Phillip Wood
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=42ada1a3-adc2-2c43-9aa1-7854858865ae@gmail.com \
--to=phillip.wood123@gmail.com \
--cc=avarab@gmail.com \
--cc=derrickstolee@github.com \
--cc=git@jeffhostetler.com \
--cc=git@vger.kernel.org \
--cc=gitgitgadget@gmail.com \
--cc=gitster@pobox.com \
--cc=johannes.schindelin@gmx.de \
--cc=me@ttaylorr.com \
--cc=newren@gmail.com \
--cc=phillip.wood@dunelm.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).