All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Harald Nordgren via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Kristoffer Haugsbakk <kristofferhaugsbakk@fastmail.com>,
	Johannes Sixt <j6t@kdbg.org>,
	Harald Nordgren <haraldnordgren@gmail.com>
Subject: [PATCH v6 0/5] branch: prune-merged
Date: Mon, 11 May 2026 09:44:46 +0000	[thread overview]
Message-ID: <pull.2285.v6.git.git.1778492691.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.2285.v5.git.git.1778482708.gitgitgadget@gmail.com>

 * --prune-merged now measures merged-ness against the remote's default
   branch instead of the candidate's upstream — so the decision no longer
   depends on which branch happens to be checked out locally.
 * delete_branches() / check_branch_commit() gained a per-candidate override
   that lets a caller substitute a different "what counts as merged"
   reference (or skip the check). branch -d callers pass NULL and keep their
   existing semantics.
 * prune_merged_branches() resolves each candidate's push-remote HEAD and
   threads it through, so --prune-merged --all-remotes measures each
   candidate against its own remote rather than a single global reference.

Harald Nordgren (5):
  branch: add --forked <remote>
  branch: let delete_branches warn instead of error on bulk refusal
  branch: add --prune-merged <remote>
  branch: add branch.<name>.pruneMerged opt-out
  branch: add --all-remotes flag

 Documentation/config/branch.adoc |   7 +
 Documentation/git-branch.adoc    |  32 +++
 builtin/branch.c                 | 344 +++++++++++++++++++++++++++++--
 t/t3200-branch.sh                | 278 +++++++++++++++++++++++++
 4 files changed, 647 insertions(+), 14 deletions(-)


base-commit: 7760f83b59750c27df653c5c46d0f80e44cfe02c
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2285%2FHaraldNordgren%2Ffetch-prune-local-branches-v6
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2285/HaraldNordgren/fetch-prune-local-branches-v6
Pull-Request: https://github.com/git/git/pull/2285

Range-diff vs v5:

 1:  77e67d4b8b = 1:  fb9817b220 branch: add --forked <remote>
 2:  807c9f981f = 2:  42a2f93d44 branch: let delete_branches warn instead of error on bulk refusal
 3:  77beb620d7 ! 3:  604ecb8965 branch: add --prune-merged <remote>
     @@ Commit message
          branch: add --prune-merged <remote>
      
          Delete the local branches that --forked <remote> would list,
     -    refusing any whose tip is not reachable from its upstream
     -    remote-tracking branch. With --force, delete unconditionally.
     -    The currently checked-out branch in any worktree is always
     -    preserved.
     +    refusing any whose tip is not reachable from the remote's default
     +    branch. With --force, delete unconditionally. The currently
     +    checked-out branch in any worktree is always preserved.
      
          Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
      
     @@ Documentation/git-branch.adoc: Each _<remote>_ may be either the name of a confi
      +	under some name on _<remote>_, and that name has since
      +	been pruned upstream.
      ++
     -+By default, the local tip must also be reachable from the
     -+upstream remote-tracking branch (see `--no-merged`); branches with
     -+unpushed commits are refused. With `--force` (or `-f`), delete
     -+them regardless. The currently checked-out branch in any worktree
     -+is always preserved.
     ++As a safety check, branches with commits not yet integrated into
     ++the remote's default branch are refused. With `--force` (or `-f`),
     ++delete them regardless. The currently checked-out branch in any
     ++worktree is always preserved.
      +
       `-v`::
       `-vv`::
     @@ builtin/branch.c
       #include "column.h"
       #include "utf8.h"
       #include "ref-filter.h"
     +@@ builtin/branch.c: static int branch_merged(int kind, const char *name,
     + 
     + static int check_branch_commit(const char *branchname, const char *refname,
     + 			       const struct object_id *oid, struct commit *head_rev,
     ++			       struct commit *head_rev_override,
     ++			       int use_head_rev_override,
     + 			       int kinds, int force, int warn_only,
     + 			       int *n_not_merged)
     + {
     + 	struct commit *rev = lookup_commit_reference(the_repository, oid);
     ++	int merged;
     ++
     + 	if (!force && !rev) {
     + 		error(_("couldn't look up commit object for '%s'"), refname);
     + 		return -1;
     + 	}
     +-	if (!force && !branch_merged(kinds, branchname, rev, head_rev)) {
     ++	if (use_head_rev_override) {
     ++		if (!head_rev_override)
     ++			return 0;
     ++		merged = repo_in_merge_bases(the_repository, rev,
     ++					     head_rev_override);
     ++		if (merged < 0)
     ++			exit(128);
     ++	} else {
     ++		merged = branch_merged(kinds, branchname, rev, head_rev);
     ++	}
     ++	if (!force && !merged) {
     + 		if (warn_only) {
     + 			warning(_("the branch '%s' is not fully merged"),
     + 				branchname);
     +@@ builtin/branch.c: static void delete_branch_config(const char *branchname)
     + 	strbuf_release(&buf);
     + }
     + 
     +-static int delete_branches(int argc, const char **argv, int force, int kinds,
     ++static int delete_branches(int argc, const char **argv,
     ++			   struct commit **head_rev_overrides,
     ++			   int force, int kinds,
     + 			   int quiet, int warn_only, int *n_not_merged)
     + {
     + 	struct commit *head_rev = NULL;
     +@@ builtin/branch.c: static int delete_branches(int argc, const char **argv, int force, int kinds,
     + 		}
     + 
     + 		if (!(flags & (REF_ISSYMREF|REF_ISBROKEN)) &&
     +-		    check_branch_commit(bname.buf, name, &oid, head_rev, kinds,
     +-					force, warn_only, n_not_merged)) {
     ++		    check_branch_commit(bname.buf, name, &oid, head_rev,
     ++					head_rev_overrides ? head_rev_overrides[i] : NULL,
     ++					!!head_rev_overrides,
     ++					kinds, force, warn_only, n_not_merged)) {
     + 			if (!warn_only)
     + 				ret = 1;
     + 			goto next;
      @@ builtin/branch.c: static int collect_forked_branch(const struct reference *ref, void *cb_data)
       	return 0;
       }
     @@ builtin/branch.c: static int collect_forked_branch(const struct reference *ref,
       	return 0;
       }
       
     ++static struct commit *resolve_remote_head(const char *remote_name)
     ++{
     ++	struct ref_store *refs = get_main_ref_store(the_repository);
     ++	struct strbuf head_ref = STRBUF_INIT;
     ++	struct object_id oid;
     ++	struct commit *commit = NULL;
     ++
     ++	strbuf_addf(&head_ref, "refs/remotes/%s/HEAD", remote_name);
     ++	if (refs_resolve_ref_unsafe(refs, head_ref.buf, RESOLVE_REF_READING,
     ++				    &oid, NULL))
     ++		commit = lookup_commit_reference(the_repository, &oid);
     ++	strbuf_release(&head_ref);
     ++	return commit;
     ++}
     ++
      +static int prune_merged_branches(int argc, const char **argv, int force,
      +				 int quiet)
      +{
      +	struct string_list candidates = STRING_LIST_INIT_DUP;
      +	struct string_list protected_default_refs = STRING_LIST_INIT_DUP;
      +	struct strvec deletable = STRVEC_INIT;
     ++	struct commit **head_rev_overrides = NULL;
     ++	size_t alloc = 0;
      +	struct string_list_item *item;
      +	int n_not_merged = 0;
      +	int ret = 0;
     @@ builtin/branch.c: static int collect_forked_branch(const struct reference *ref,
      +		struct branch *branch;
      +		const char *push_ref;
      +		const char *upstream;
     ++		const char *remote_name;
     ++		const char *slash;
      +
      +		strbuf_addf(&full, "refs/heads/%s", short_name);
      +		if (branch_checked_out(full.buf)) {
     @@ builtin/branch.c: static int collect_forked_branch(const struct reference *ref,
      +		if (string_list_has_string(&protected_default_refs, push_ref))
      +			continue;
      +
     ++		ALLOC_GROW(head_rev_overrides, deletable.nr + 1, alloc);
     ++		remote_name = push_ref + strlen("refs/remotes/");
     ++		slash = strchr(remote_name, '/');
     ++		if (slash) {
     ++			char *name = xstrndup(remote_name, slash - remote_name);
     ++			head_rev_overrides[deletable.nr] = resolve_remote_head(name);
     ++			free(name);
     ++		} else {
     ++			head_rev_overrides[deletable.nr] = NULL;
     ++		}
      +		strvec_push(&deletable, short_name);
      +	}
      +
      +	if (deletable.nr)
     -+		ret = delete_branches(deletable.nr, deletable.v, force,
     ++		ret = delete_branches(deletable.nr, deletable.v,
     ++				      head_rev_overrides, force,
      +				      FILTER_REFS_BRANCHES, quiet,
      +				      1, &n_not_merged);
      +
     @@ builtin/branch.c: static int collect_forked_branch(const struct reference *ref,
      +			n_not_merged);
      +
      +	strvec_clear(&deletable);
     ++	free(head_rev_overrides);
      +	string_list_clear(&candidates, 0);
      +	string_list_clear(&protected_default_refs, 0);
      +	return ret;
     @@ builtin/branch.c: int cmd_branch(int argc,
       		usage_with_options(builtin_branch_usage, options);
       
      @@ builtin/branch.c: int cmd_branch(int argc,
     + 	if (delete) {
     + 		if (!argc)
     + 			die(_("branch name required"));
     +-		ret = delete_branches(argc, argv, delete > 1, filter.kind,
     ++		ret = delete_branches(argc, argv, NULL, delete > 1, filter.kind,
     + 				      quiet, 0, NULL);
     + 		goto out;
       	} else if (forked) {
       		ret = list_forked_branches(argc, argv);
       		goto out;
     @@ t/t3200-branch.sh: test_expect_success '--forked requires at least one <remote>'
      +	test_must_fail git -C pm-force rev-parse --verify refs/heads/one
      +'
      +
     ++test_expect_success '--prune-merged measures merged-ness against <remote>/HEAD, not local HEAD' '
     ++	test_when_finished "rm -rf pm-head-indep" &&
     ++	git clone pm-upstream pm-head-indep &&
     ++	git -C pm-head-indep branch one --track origin/one &&
     ++	git -C pm-head-indep update-ref -d refs/remotes/origin/one &&
     ++	# Detach HEAD to an unrelated commit so the candidate is not
     ++	# reachable from local HEAD; it is still reachable from
     ++	# refs/remotes/origin/HEAD, which is what should matter.
     ++	git -C pm-head-indep commit --allow-empty -m unrelated &&
     ++	git -C pm-head-indep checkout --detach &&
     ++	git -C pm-head-indep reset --hard HEAD^ &&
     ++
     ++	git -C pm-head-indep branch --prune-merged origin &&
     ++
     ++	test_must_fail git -C pm-head-indep rev-parse --verify refs/heads/one
     ++'
     ++
     ++test_expect_success '--prune-merged skips merged-ness check when <remote>/HEAD is unset' '
     ++	test_when_finished "rm -rf pm-no-head" &&
     ++	git clone pm-upstream pm-no-head &&
     ++	git -C pm-no-head checkout -b one --track origin/one &&
     ++	test_commit -C pm-no-head unpushed &&
     ++	git -C pm-no-head checkout - &&
     ++
     ++	git -C pm-no-head update-ref -d refs/remotes/origin/HEAD &&
     ++	git -C pm-no-head update-ref -d refs/remotes/origin/one &&
     ++	git -C pm-no-head branch --prune-merged origin &&
     ++
     ++	test_must_fail git -C pm-no-head rev-parse --verify refs/heads/one
     ++'
     ++
      +test_expect_success '--prune-merged never deletes the checked-out branch' '
      +	test_when_finished "rm -rf pm-head" &&
      +	git clone pm-upstream pm-head &&
 4:  cf69fb5767 ! 4:  717fc6758e branch: add branch.<name>.pruneMerged opt-out
     @@ Documentation/git-branch.adoc: Each _<remote>_ may be either the name of a confi
      +	the branch was pushed under some name on _<remote>_, and
      +	that name has since been pruned upstream.
       +
     --By default, the local tip must also be reachable from the
     --upstream remote-tracking branch (see `--no-merged`); branches with
     --unpushed commits are refused. With `--force` (or `-f`), delete
     --them regardless. The currently checked-out branch in any worktree
     --is always preserved.
     -+The local tip must also be reachable from the upstream
     -+remote-tracking branch; branches with unpushed commits are refused.
     -+With `--force` (or `-f`), delete them regardless. The currently
     -+checked-out branch in any worktree is always preserved, as is
     -+any branch with `branch.<name>.pruneMerged` set to `false`.
     + As a safety check, branches with commits not yet integrated into
     + the remote's default branch are refused. With `--force` (or `-f`),
     + delete them regardless. The currently checked-out branch in any
     +-worktree is always preserved.
     ++worktree is always preserved, as is any branch with
     ++`branch.<name>.pruneMerged` set to `false`.
       
       `-v`::
       `-vv`::
     @@ builtin/branch.c: static int prune_merged_branches(int argc, const char **argv,
       		struct branch *branch;
       		const char *push_ref;
       		const char *upstream;
     + 		const char *remote_name;
     + 		const char *slash;
      +		int opt_out = 0;
       
       		strbuf_addf(&full, "refs/heads/%s", short_name);
     @@ builtin/branch.c: static int prune_merged_branches(int argc, const char **argv,
      +		}
      +		strbuf_release(&key);
       
     - 		strvec_push(&deletable, short_name);
     - 	}
     + 		ALLOC_GROW(head_rev_overrides, deletable.nr + 1, alloc);
     + 		remote_name = push_ref + strlen("refs/remotes/");
      
       ## t/t3200-branch.sh ##
      @@ t/t3200-branch.sh: test_expect_success '--prune-merged spares branches whose push ref is the defaul
 5:  f2cee8c79b ! 5:  be25572957 branch: add --all-remotes flag
     @@ Documentation/git-branch.adoc: git branch (-m|-M) [<old-branch>] <new-branch>
       
       DESCRIPTION
       -----------
     -@@ Documentation/git-branch.adoc: With `--force` (or `-f`), delete them regardless. The currently
     - checked-out branch in any worktree is always preserved, as is
     - any branch with `branch.<name>.pruneMerged` set to `false`.
     +@@ Documentation/git-branch.adoc: delete them regardless. The currently checked-out branch in any
     + worktree is always preserved, as is any branch with
     + `branch.<name>.pruneMerged` set to `false`.
       
      +`--all-remotes`::
      +	With `--forked` or `--prune-merged`, act on every
     @@ builtin/branch.c: static void collect_forked_set(int argc, const char **argv,
       	for_each_string_list_item(item, &out)
       		puts(item->string);
       
     -@@ builtin/branch.c: static int list_forked_branches(int argc, const char **argv)
     - 	return 0;
     +@@ builtin/branch.c: static struct commit *resolve_remote_head(const char *remote_name)
     + 	return commit;
       }
       
      -static int prune_merged_branches(int argc, const char **argv, int force,

-- 
gitgitgadget

  parent reply	other threads:[~2026-05-11  9:44 UTC|newest]

Thread overview: 70+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-01 21:35 [PATCH] fetch: add fetch.pruneLocalBranches config Harald Nordgren via GitGitGadget
2026-05-03 22:39 ` Junio C Hamano
2026-05-04 18:28   ` [PATCH] checkout: add --autostash option for branch switching Harald Nordgren
2026-05-10  1:01     ` Junio C Hamano
2026-05-05  7:14   ` [PATCH] fetch: add fetch.pruneLocalBranches config Johannes Sixt
2026-05-04 18:27 ` [PATCH v2 0/6] fetch: add fetch.pruneBranches config Harald Nordgren via GitGitGadget
2026-05-04 18:27   ` [PATCH v2 1/6] branch: add --forked <remote> Harald Nordgren via GitGitGadget
2026-05-04 23:25     ` Kristoffer Haugsbakk
2026-05-04 18:27   ` [PATCH v2 2/6] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-05-04 18:27   ` [PATCH v2 3/6] branch: add --prune-merged <remote> Harald Nordgren via GitGitGadget
2026-05-04 18:27   ` [PATCH v2 4/6] fetch: add --prune-merged Harald Nordgren via GitGitGadget
2026-05-04 18:27   ` [PATCH v2 5/6] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-04 18:27   ` [PATCH v2 6/6] branch: add --all-remotes flag Harald Nordgren via GitGitGadget
2026-05-05  7:22   ` [PATCH v3 0/6] fetch: add fetch.pruneBranches config Harald Nordgren via GitGitGadget
2026-05-05  7:22     ` [PATCH v3 1/6] branch: add --forked <remote> Harald Nordgren via GitGitGadget
2026-05-05  7:22     ` [PATCH v3 2/6] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-05-05  7:22     ` [PATCH v3 3/6] branch: add --prune-merged <remote> Harald Nordgren via GitGitGadget
2026-05-05  7:22     ` [PATCH v3 4/6] fetch: add --prune-merged Harald Nordgren via GitGitGadget
2026-05-05  7:22     ` [PATCH v3 5/6] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-05  7:22     ` [PATCH v3 6/6] branch: add --all-remotes flag Harald Nordgren via GitGitGadget
2026-05-05 19:23     ` [PATCH v4 0/6] fetch: add fetch.pruneBranches config Harald Nordgren via GitGitGadget
2026-05-05 19:23       ` [PATCH v4 1/6] branch: add --forked <remote> Harald Nordgren via GitGitGadget
2026-05-05 19:23       ` [PATCH v4 2/6] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-05-05 19:23       ` [PATCH v4 3/6] branch: add --prune-merged <remote> Harald Nordgren via GitGitGadget
2026-05-05 19:23       ` [PATCH v4 4/6] fetch: add --prune-merged Harald Nordgren via GitGitGadget
2026-05-05 20:48         ` Johannes Sixt
2026-05-05 22:07           ` [PATCH] fetch: add fetch.pruneLocalBranches config Harald Nordgren
2026-05-11  2:59             ` Junio C Hamano
2026-05-11  6:56               ` Harald Nordgren
2026-05-05 19:23       ` [PATCH v4 5/6] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-05 19:23       ` [PATCH v4 6/6] branch: add --all-remotes flag Harald Nordgren via GitGitGadget
2026-05-07 20:14       ` [PATCH v4 0/6] fetch: add fetch.pruneBranches config Harald Nordgren
2026-05-11  6:58       ` [PATCH v5 0/5] branch: prune-merged Harald Nordgren via GitGitGadget
2026-05-11  6:58         ` [PATCH v5 1/5] branch: add --forked <remote> Harald Nordgren via GitGitGadget
2026-05-11  6:58         ` [PATCH v5 2/5] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-05-11  8:18           ` Junio C Hamano
2026-05-11  8:44             ` [PATCH] fetch: add fetch.pruneLocalBranches config Harald Nordgren
2026-05-11  6:58         ` [PATCH v5 3/5] branch: add --prune-merged <remote> Harald Nordgren via GitGitGadget
2026-05-11  6:58         ` [PATCH v5 4/5] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-11  6:58         ` [PATCH v5 5/5] branch: add --all-remotes flag Harald Nordgren via GitGitGadget
2026-05-11  9:44         ` Harald Nordgren via GitGitGadget [this message]
2026-05-11  9:44           ` [PATCH v6 1/5] branch: add --forked <remote> Harald Nordgren via GitGitGadget
2026-05-11  9:44           ` [PATCH v6 2/5] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-05-11  9:44           ` [PATCH v6 3/5] branch: add --prune-merged <remote> Harald Nordgren via GitGitGadget
2026-05-11  9:44           ` [PATCH v6 4/5] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-11  9:44           ` [PATCH v6 5/5] branch: add --all-remotes flag Harald Nordgren via GitGitGadget
2026-05-11 23:20           ` [PATCH v6 0/5] branch: prune-merged Junio C Hamano
2026-05-12  7:35             ` [PATCH] fetch: add fetch.pruneLocalBranches config Harald Nordgren
2026-05-12  8:23           ` [PATCH v7 0/5] branch: prune-merged Harald Nordgren via GitGitGadget
2026-05-12  8:23             ` [PATCH v7 1/5] branch: add --forked <remote> Harald Nordgren via GitGitGadget
2026-05-12  8:23             ` [PATCH v7 2/5] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-05-12  8:23             ` [PATCH v7 3/5] branch: add --prune-merged <remote> Harald Nordgren via GitGitGadget
2026-05-12 13:53               ` Junio C Hamano
2026-05-12 17:00                 ` [PATCH] fetch: add fetch.pruneLocalBranches config Harald Nordgren
2026-05-12  8:23             ` [PATCH v7 4/5] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-12  8:23             ` [PATCH v7 5/5] branch: add --all-remotes flag Harald Nordgren via GitGitGadget
2026-05-12 17:07             ` [PATCH v8 0/5] branch: prune-merged Harald Nordgren via GitGitGadget
2026-05-12 17:07               ` [PATCH v8 1/5] branch: add --forked <remote> Harald Nordgren via GitGitGadget
2026-05-12 17:07               ` [PATCH v8 2/5] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-05-12 17:07               ` [PATCH v8 3/5] branch: add --prune-merged <remote> Harald Nordgren via GitGitGadget
2026-05-12 17:07               ` [PATCH v8 4/5] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-12 17:07               ` [PATCH v8 5/5] branch: add --all-remotes flag Harald Nordgren via GitGitGadget
2026-05-13 13:46               ` [PATCH v8 0/5] branch: prune-merged Junio C Hamano
2026-05-13 18:57                 ` [PATCH] fetch: add fetch.pruneLocalBranches config Harald Nordgren
2026-05-13 19:34               ` [PATCH v9 0/5] branch: prune-merged Harald Nordgren via GitGitGadget
2026-05-13 19:34                 ` [PATCH v9 1/5] branch: add --forked <remote> Harald Nordgren via GitGitGadget
2026-05-13 19:34                 ` [PATCH v9 2/5] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-05-13 19:34                 ` [PATCH v9 3/5] branch: add --prune-merged <remote> Harald Nordgren via GitGitGadget
2026-05-13 19:34                 ` [PATCH v9 4/5] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-13 19:34                 ` [PATCH v9 5/5] branch: add --all-remotes flag Harald Nordgren via GitGitGadget

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=pull.2285.v6.git.git.1778492691.gitgitgadget@gmail.com \
    --to=gitgitgadget@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=haraldnordgren@gmail.com \
    --cc=j6t@kdbg.org \
    --cc=kristofferhaugsbakk@fastmail.com \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.