Git development
 help / color / mirror / Atom feed
* [PATCH 1/4] doc: link to config for git-replay(1)
From: kristofferhaugsbakk @ 2026-05-21 18:01 UTC (permalink / raw)
  To: git; +Cc: Kristoffer Haugsbakk, Siddharth Asthana
In-Reply-To: <CV_doc_replay_config.709@msgid.xyz>

From: Kristoffer Haugsbakk <code@khaugsbakk.name>

This config doc was added in 336ac90c (replay: add replay.refAction
config option, 2025-11-06) but never included anywhere. Include it in
git-replay(1) and git-config(1).

Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
---
 Documentation/config.adoc     | 2 ++
 Documentation/git-replay.adoc | 4 ++++
 2 files changed, 6 insertions(+)

diff --git a/Documentation/config.adoc b/Documentation/config.adoc
index 62eebe7c545..51fabecb9b0 100644
--- a/Documentation/config.adoc
+++ b/Documentation/config.adoc
@@ -511,6 +511,8 @@ include::config/remotes.adoc[]
 
 include::config/repack.adoc[]
 
+include::config/replay.adoc[]
+
 include::config/rerere.adoc[]
 
 include::config/revert.adoc[]
diff --git a/Documentation/git-replay.adoc b/Documentation/git-replay.adoc
index a32f72aead3..f9ca2db2833 100644
--- a/Documentation/git-replay.adoc
+++ b/Documentation/git-replay.adoc
@@ -209,6 +209,10 @@ This replays the range `aabbcc..ddeeff` onto commit `112233` and updates
 `refs/heads/mybranch` to point at the result. This can be useful when you want
 to use bare commit IDs instead of branch names.
 
+CONFIGURATION
+-------------
+include::config/replay.adoc[]
+
 GIT
 ---
 Part of the linkgit:git[1] suite
-- 
2.54.0.13.g9c7419e39f8


^ permalink raw reply related

* [PATCH 0/4] doc: replay: fix config link
From: kristofferhaugsbakk @ 2026-05-21 18:01 UTC (permalink / raw)
  To: git; +Cc: Kristoffer Haugsbakk, Siddharth Asthana

From: Kristoffer Haugsbakk <code@khaugsbakk.name>

Topic name: kh/doc-replay-config

Topic summary: link to the config for git-replay(1) (one variable) in
git-replay(1) and git-config(1). Also improve the doc for that config
variable and `--ref-action`.

[1/4] doc: link to config for git-replay(1)
[2/4] doc: replay: simplify replay.refAction description
[3/4] doc: replay: use a nested definition list
[4/4] doc: replay: move “default” to the right-hand-side

 Documentation/config.adoc        |  2 ++
 Documentation/config/replay.adoc | 17 +++++++----------
 Documentation/git-replay.adoc    | 13 +++++++++----
 3 files changed, 18 insertions(+), 14 deletions(-)


base-commit: a89346e34a937f001e5d397ee62224e3e9852040
-- 
2.54.0.13.g9c7419e39f8


^ permalink raw reply

* Re: [PATCH 8/8] setup: construct object database in `apply_repository_format()`
From: Junio C Hamano @ 2026-05-21 17:59 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git
In-Reply-To: <20260521-b4-pks-setup-centralize-odb-creation-v1-8-f130d2a7e8ae@pks.im>

Patrick Steinhardt <ps@pks.im> writes:

> With the preceding changes we now always construct the repository's
> object database before applying the repository format. Remove this
> duplication by constructing it in `apply_repository_format()` instead.
>
> Note that we create the object database _after_ having set up the
> repository's hash algorithm, but _before_ setting the compat hash
> algorithm. This is intentional:
>
>   - Constructing the object database may require knowledge of its
>     intended object format.
>
>   - Setting up the compatibility hash requires the object database to be
>     initialized already, because we immediately read the loose object
>     map.
>
> The first point is sensible, the second maybe a little less so. Ideally,
> it should be the responsibility of the object database itself to
> initialize any data structures required for the compatibility hash. But
> this would require further changes, so this is kept as-is for now.

Yeah, I guess it is a good place to stop, instead of solving the
chicken-and-egg problem in one go.

> Further note that this requires us to move handling of the environment
> variables GIT_OBJECT_DIRECTORY and GIT_ALTERNATE_OBJECT_DIRECTORIES into
> the repository format, as well. This allows the caller more flexibility
> around whether or not those environment variables are being honored, as
> we do do want to respect them in "setup.c", but not in "repository.c".

It seems that we really really really want to do so ;-).  "do do
want to" -> "do want to" or even "want to", perhaps.

> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
>  repository.c |  4 +---
>  setup.c      | 45 +++++++++++++++++++++------------------------
>  setup.h      | 10 ++++++++++
>  3 files changed, 32 insertions(+), 27 deletions(-)
>
> diff --git a/repository.c b/repository.c
> index 61dfbb8be6..187dd471c4 100644
> --- a/repository.c
> +++ b/repository.c
> @@ -291,13 +291,11 @@ int repo_init(struct repository *repo,
>  	if (read_repository_format_from_commondir(&format, repo->commondir))
>  		goto error;
>  
> -	if (apply_repository_format(repo, &format, &err) < 0) {
> +	if (apply_repository_format(repo, &format, 0, &err) < 0) {
>  		warning("%s", err.buf);
>  		goto error;
>  	}
>  
> -	repo->objects = odb_new(repo, NULL, NULL);
> -
>  	if (worktree)
>  		repo_set_worktree(repo, worktree);
>  
> diff --git a/setup.c b/setup.c
> index 4a8d6230b1..513fc88749 100644
> --- a/setup.c
> +++ b/setup.c
> @@ -1752,12 +1752,22 @@ enum discovery_result discover_git_directory_reason(struct strbuf *commondir,
>  
>  int apply_repository_format(struct repository *repo,
>  			    const struct repository_format *format,
> +			    enum apply_repository_format_flags flags,
>  			    struct strbuf *err)
>  {
> +	char *object_directory = NULL, *alternate_object_directories = NULL;
> +
>  	if (verify_repository_format(format, err) < 0)
>  		return -1;
>  
> +	if (flags & APPLY_REPOSITORY_FORMAT_HONOR_ENV) {
> +		object_directory = xstrdup_or_null(getenv(DB_ENVIRONMENT));
> +		alternate_object_directories = xstrdup_or_null(getenv(ALTERNATE_DB_ENVIRONMENT));
> +	}
> +
>  	repo_set_hash_algo(repo, format->hash_algo);
> +	repo->objects = odb_new(repo, object_directory,
> +				alternate_object_directories);
>  	repo_set_compat_hash_algo(repo, format->compat_hash_algo);
>  	repo_set_ref_storage_format(repo,
>  				    format->ref_storage_format,
> @@ -1773,6 +1783,8 @@ int apply_repository_format(struct repository *repo,
>  	repo->repository_format_precious_objects =
>  		format->precious_objects;
>  
> +	free(alternate_object_directories);
> +	free(object_directory);
>  	return 0;
>  }
>  
> @@ -1785,7 +1797,8 @@ int apply_repository_format(struct repository *repo,
>   * If successful and fmt is not NULL, fill fmt with data.
>   */
>  static void check_and_apply_repository_format(struct repository *repo,
> -					      struct repository_format *fmt)
> +					      struct repository_format *fmt,
> +					      enum apply_repository_format_flags flags)
>  {
>  	struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
>  	struct strbuf err = STRBUF_INIT;
> @@ -1794,7 +1807,7 @@ static void check_and_apply_repository_format(struct repository *repo,
>  		fmt = &repo_fmt;
>  
>  	check_repository_format_gently(repo_get_git_dir(repo), fmt, NULL);
> -	if (apply_repository_format(repo, fmt, &err) < 0)
> +	if (apply_repository_format(repo, fmt, flags, &err) < 0)
>  		die("%s", err.buf);
>  	startup_info->have_repository = 1;
>  
> @@ -1874,15 +1887,9 @@ const char *enter_repo(struct repository *repo, const char *path, unsigned flags
>  	}
>  
>  	if (is_git_directory(".")) {
> -		struct strvec to_free = STRVEC_INIT;
> -
>  		set_git_dir(repo, ".", 0);
> -		repo->objects = odb_new(repo,
> -					getenv_safe(&to_free, DB_ENVIRONMENT),
> -					getenv_safe(&to_free, ALTERNATE_DB_ENVIRONMENT));
> -		check_and_apply_repository_format(repo, NULL);
> -
> -		strvec_clear(&to_free);
> +		check_and_apply_repository_format(repo, NULL,
> +						  APPLY_REPOSITORY_FORMAT_HONOR_ENV);
>  		return path;
>  	}
>  
> @@ -2034,8 +2041,6 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
>  	    startup_info->have_repository ||
>  	    /* GIT_DIR_EXPLICIT */
>  	    getenv(GIT_DIR_ENVIRONMENT)) {
> -		struct strvec to_free = STRVEC_INIT;
> -
>  		if (!repo->gitdir) {
>  			const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
>  			if (!gitdir)
> @@ -2046,17 +2051,13 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
>  		if (startup_info->have_repository) {
>  			struct strbuf err = STRBUF_INIT;
>  
> -			repo->objects = odb_new(repo,
> -						getenv_safe(&to_free, DB_ENVIRONMENT),
> -						getenv_safe(&to_free, ALTERNATE_DB_ENVIRONMENT));
> -			if (apply_repository_format(repo, &repo_fmt, &err) < 0)
> +			if (apply_repository_format(repo, &repo_fmt,
> +						    APPLY_REPOSITORY_FORMAT_HONOR_ENV, &err) < 0)
>  				die("%s", err.buf);
>  
>  			clear_repository_format(&repo_fmt);
>  			strbuf_release(&err);
>  		}
> -
> -		strvec_clear(&to_free);
>  	}
>  	/*
>  	 * Since precompose_string_if_needed() needs to look at
> @@ -2805,7 +2806,6 @@ int init_db(struct repository *repo,
>  	int exist_ok = flags & INIT_DB_EXIST_OK;
>  	char *original_git_dir = real_pathdup(git_dir, 1);
>  	struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
> -	struct strvec to_free = STRVEC_INIT;
>  
>  	if (real_git_dir) {
>  		struct stat st;
> @@ -2826,16 +2826,14 @@ int init_db(struct repository *repo,
>  	}
>  	startup_info->have_repository = 1;
>  
> -	repo->objects = odb_new(repo, getenv_safe(&to_free, DB_ENVIRONMENT),
> -				getenv_safe(&to_free, ALTERNATE_DB_ENVIRONMENT));
> -
>  	/*
>  	 * Check to see if the repository version is right.
>  	 * Note that a newly created repository does not have
>  	 * config file, so this will not fail.  What we are catching
>  	 * is an attempt to reinitialize new repository with an old tool.
>  	 */
> -	check_and_apply_repository_format(repo, &repo_fmt);
> +	check_and_apply_repository_format(repo, &repo_fmt,
> +					  APPLY_REPOSITORY_FORMAT_HONOR_ENV);
>  
>  	repository_format_configure(repo, &repo_fmt, hash, ref_storage_format);
>  
> @@ -2892,7 +2890,6 @@ int init_db(struct repository *repo,
>  	}
>  
>  	clear_repository_format(&repo_fmt);
> -	strvec_clear(&to_free);
>  	free(original_git_dir);
>  	return 0;
>  }
> diff --git a/setup.h b/setup.h
> index 5ed92f53fa..821b55aca0 100644
> --- a/setup.h
> +++ b/setup.h
> @@ -221,6 +221,15 @@ void clear_repository_format(struct repository_format *format);
>  int verify_repository_format(const struct repository_format *format,
>  			     struct strbuf *err);
>  
> +enum apply_repository_format_flags {
> +	/*
> +	 * Honor environment variables when applying the repository format to
> +	 * the repository. For now, this only covers environment variables that
> +	 * relate to the object database.
> +	 */
> +	APPLY_REPOSITORY_FORMAT_HONOR_ENV = (1 << 0),
> +};
> +
>  /*
>   * Apply the given repository format to the repo. This initializes extensions
>   * and basic data structures required for normal operation. Returns 0 on
> @@ -228,6 +237,7 @@ int verify_repository_format(const struct repository_format *format,
>   */
>  int apply_repository_format(struct repository *repo,
>  			    const struct repository_format *format,
> +			    enum apply_repository_format_flags flags,
>  			    struct strbuf *err);
>  
>  const char *get_template_dir(const char *option_template);

^ permalink raw reply

* Re: [PATCH 1/8] t0001: plug test gaps for git-init(1) with GIT_OBJECT_DIRECTORY
From: Kristoffer Haugsbakk @ 2026-05-21 17:51 UTC (permalink / raw)
  To: Patrick Steinhardt, git
In-Reply-To: <20260521-b4-pks-setup-centralize-odb-creation-v1-1-f130d2a7e8ae@pks.im>

On Thu, May 21, 2026, at 09:42, Patrick Steinhardt wrote:
> In subsequent commits we'll rework how we set up the repository. This is
> a somewhat intricate and thus fragile sequence, there's many things that

Should this be s/, there/; there/ ? Depends on if this is a list of
three items or if “This is” is a subclause that is supposed to point at
“there's many”.

> can go subtly wrong, and there are lots of interesting interactions that
> one can discover.
>
> One such discovered edge case was the interaction between git-init(1)
> and the "GIT_OBJECT_DIRECTORY" enviroment variable. When set, the
> behaviour is that the object directory should be created at the path
> that the variable points to. This behaviour is documented as such in
> its man page:
>
>   If the object storage directory is specified via the
>   GIT_OBJECT_DIRECTORY environment variable then the sha1 directories
>   are created underneath; otherwise, the default $GIT_DIR/objects
>   directory is used.
>
> Curiously enough though we don't seem to have any tests that exercise
> this directly, and thus a subsequent commit inadvertently broke this
> expectation.

Isn’t it more that “the upcoming changes *would have* broken” them if
not for this change? This seems to refer to a an alternative commit
history where this change does not exist?

>
> Plug this test gap.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
>[snip]

^ permalink raw reply

* Re: [PATCH 16/18] odb/source-loose: wire up `write_object_stream()` callback
From: Junio C Hamano @ 2026-05-21 17:49 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git
In-Reply-To: <20260521-b4-pks-odb-source-loose-v1-16-6553b399be2d@pks.im>

Patrick Steinhardt <ps@pks.im> writes:

> -int odb_source_loose_write_stream(struct odb_source_loose *loose,
> +/*
> + * Write the given stream into the loose object source. The only difference to
> + * the generic implementation of this function is that we don't perform an

"difference to" -> "difference from"???

^ permalink raw reply

* Re: [PATCH v2 07/11] git-gui: try harder to find worktree from gitdir
From: Mark Levedahl @ 2026-05-21 17:45 UTC (permalink / raw)
  To: Shroom Moo; +Cc: git, Johannes Sixt, Aina Boot
In-Reply-To: <tencent_E13EB585242AD7C263B8B3B732A428465D09@qq.com>



On 5/21/26 12:55 AM, Shroom Moo wrote:
> On 5/21/26 4:24 AM, Mark Levedahl wrote:
>> +	} elseif [file exists {gitdir}] {
>> +		if {[catch {
>> +			set fd_gitdir [open {gitdir} {r}]
>> +			set gitlink_parent [file dirname [read $fd_gitdir]]
>> +			catch {close $fd_gitdir}
>> +			set worktree [git -C $gitlink_parent rev-parse --show-toplevel]
>> +			set parent_gitdir [git -C $worktree rev-parse --absolute-git-dir]
>> +			if {$::_gitdir ne $parent_gitdir} {
>> +				set worktree {}
>> +			}
>> +		}]} {
>> +			catch {close $fd_gitdir}
>> +			set worktree {}
>> +		}
>> +	}
> There is also an unaddressed issue: 
> In [file exists {gitdir}] and [open {gitdir} r], {gitdir} is a 
> literal string referring to a file named gitdir in the current 
> working directory. However, in the context of a linked worktree 
> (created via git worktree add), the actual file path is 
> $_gitdir/gitdir (e.g., .git/worktrees/<name>/gitdir). While the 
> current working directory could be anywhere (even inside the .git 
> directory), $_gitdir is an absolute path pointing to that worktree's 
> gitdir (e.g., /path/to/main/.git/worktrees/branch). The gitdir file 
> resides within the $_gitdir directory and contains a relative path 
> like ../../.git/worktrees/branch. The current code logic will never 
> locate this file. 
You have to be in the particular worktree's gitdir for this to work. I there exists
    worktrees/foo
    worktrees/frotz
    worktrees/bar
Which would we expore? The code above must be in foo, frotz, bar

The main worktree is found not from worktrees/*, but from the root of the gitdir.
>
> Additionally, [file exists {gitdir}] checks for the gitdir file in 
> the current working directory. Since the function has not yet 
> switched to $_gitdir when this check runs, it is almost impossible 
> to find the file. Consequently, this logic never triggers, preventing 
> linked worktrees from being recognized. 
>
> Maybe the identification of linked worktree should not directly look 
> for the gitdir file, but should check whether there is a.git file and 
> its content points to... /.git/worktrees/... ? Anyways, using the 
> literal {gitdir} to search in the current directory lead to risks. 
>
> Shroom
>
We cannot get to this code if not inside the gitdir, and if the user set GIT_DIR and/or
GIT_WORK_TREE to do something clever, that either worked or the code already threw an
error. git, without GIT_WORK_TREE set, uses the current directory as the worktree, or the
parent directory containing .git. So, we must be inside the gitdir if this code path gets hit.

Mark

Mark

^ permalink raw reply

* Re: [PATCH v2 10/11] git-gui: adapt blame/browser parsing for bare operation
From: Mark Levedahl @ 2026-05-21 17:35 UTC (permalink / raw)
  To: Shroom Moo; +Cc: git, Johannes Sixt, Aina Boot
In-Reply-To: <tencent_407FE60B6954528497709B6CAD49018D120A@qq.com>



On 5/21/26 1:02 AM, Shroom Moo wrote:
> On 5/21/26 4:24 AM, Mark Levedahl wrote: 
>> +proc find_path_type {head path} {
>> +	if {$path eq {./}} {
>> +		# the root-tree exists in every rev, ls-tree gives data on the contents,
>> +		# not the type of tree itself. So, if the rev exists, return {tree}
>> +		if {[catch {set objtype [git ls-tree $head]}]} {
>> +			set objtype {}
>> +		} else {
>> +			set objtype {tree}
>> +		}
>> +	} else {
>> +		# test that the path exists in head, ls-tree gives info on the path only
>> +		if {[catch {set objtype [git ls-tree {--format=%(objecttype)} $head $path]}]} {
>> +			set objtype {}
>> +		}
>> +	}
>> +	return $objtype
>> +}
> In v1, argument parsing relied on file exists within the worktree to 
> determine if a path existed, without using ls-tree. In v2, the use of 
> git ls-tree seems to actually be intended to list directory contents, 
> rather than querying the type of the path itself. 
>
> If $path is a directory (a tree object), git ls-tree outputs the 
> object type for every entry within that directory, one per line. 
>
> The variable objtype is assigned a multi-line string. When compared 
> against "tree", the match fails, causing the function to return an 
> empty string, which subsequently leads to an error. We can change to 
> "git cat-file -t" or similiar approaches. 
>
> Shroom
>
git ls-tree $rev $path --format='%(objecttype)' gives the type of the object at $path in
$rev, or an error. The types returned are "tree" for a directory, "blob" for a file. So
this gives definitive information if the object desired exists in the given rev, and is of
the right type. (We don't care about commits and tags, those cannot be blamed or browsed).

git-lstree $rev $dirname/  lists all of the objects in the directory, while git-lstree
$rev $dirname (no trailing /) gives info on the directory itself. There is no name for the
root directory itself, all of its contents are listed. That is why the root './' is
special case.

Asking the worktree that is on version 33 about whether frotz is a directory in version 2
is just asking for trouble, at best the worktree is authoritative for the checked out
version, but even then there can be uncommitted changed. In the root of git-gui, I get

/git-gui.sh browser gitgui-0.9.0 Makefile
'Makefile' is not a directory in rev 'gitgui-0.9.0'

so the types of objects are being checked.

Mark


^ permalink raw reply

* Re: [PATCH 1/8] t0001: plug test gaps for git-init(1) with GIT_OBJECT_DIRECTORY
From: Junio C Hamano @ 2026-05-21 16:49 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git
In-Reply-To: <20260521-b4-pks-setup-centralize-odb-creation-v1-1-f130d2a7e8ae@pks.im>

Patrick Steinhardt <ps@pks.im> writes:

> In subsequent commits we'll rework how we set up the repository. This is
> a somewhat intricate and thus fragile sequence, there's many things that
> can go subtly wrong, and there are lots of interesting interactions that
> one can discover.
>
> One such discovered edge case was the interaction between git-init(1)
> and the "GIT_OBJECT_DIRECTORY" enviroment variable. When set, the

"environment"???

> behaviour is that the object directory should be created at the path
> that the variable points to. This behaviour is documented as such in
> its man page:
>
>   If the object storage directory is specified via the
>   GIT_OBJECT_DIRECTORY environment variable then the sha1 directories
>   are created underneath; otherwise, the default $GIT_DIR/objects
>   directory is used.
>
> Curiously enough though we don't seem to have any tests that exercise
> this directly, and thus a subsequent commit inadvertently broke this
> expectation.
>
> Plug this test gap.

Nice.

> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
>  t/t0001-init.sh | 10 ++++++++++
>  1 file changed, 10 insertions(+)
>
> diff --git a/t/t0001-init.sh b/t/t0001-init.sh
> index e4d32bb4d2..e89feca544 100755
> --- a/t/t0001-init.sh
> +++ b/t/t0001-init.sh
> @@ -980,4 +980,14 @@ test_expect_success 're-init reads matching includeIf.onbranch' '
>  	test_cmp expect err
>  '
>  
> +test_expect_success 'init honors GIT_OBJECT_DIRECTORY' '
> +	test_when_finished "rm -rf init-objdir custom-odb" &&
> +	mkdir custom-odb &&
> +	env GIT_OBJECT_DIRECTORY="$(pwd)/custom-odb" \
> +		git init init-objdir &&
> +	test_path_is_missing init-objdir/.git/objects/pack &&
> +	test_path_is_dir custom-odb/pack &&
> +	test_path_is_dir custom-odb/info
> +'
> +
>  test_done

^ permalink raw reply

* Re: [PATCH v3 1/8] environment: move "trust_ctime" into `struct repo_config_values`
From: Tian Yuchen @ 2026-05-21 16:37 UTC (permalink / raw)
  To: Olamide Caleb Bello, git
  Cc: phillip.wood123, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me
In-Reply-To: <20260423165432.143598-2-belkid98@gmail.com>

Hi Bello!

On 4/24/26 00:54, Olamide Caleb Bello wrote:

The code itself looks great to me, but I have some reservations about 
the description here (in terms of why trust_ctime is eagerly parsed):

 > `core.trustctime` is parsed eagerly
 > because it is used in low‑level stat‑matching functions
 > (`match_stat_data()`), where a lazy parse could cause unexpected
 > fatal errors and complicate libification efforts.

It's true that if we use repo_config_get_bool() to parse trust_ctime, 
following the call stack downwards, there is a die() call. The terminate 
condition is that the configuration does not exist or contains invalid 
characters.

But I think there is another factor: match_stat_data() is called on a 
hot path. The following code is implemented in read-cache.c, 
refresh_index() function:

	for (i = 0; i < istate->cache_nr; i++) {
		...
		new_entry = refresh_cache_ent(istate, ce, options,
					      &cache_errno, &changed,
					      &t2_did_lstat, &t2_did_scan);
		t2_sum_lstat += t2_did_lstat;
		t2_sum_scan += t2_did_scan;
		if (new_entry == ce)
		...

The call chain: refresh_index() -> refresh_cache_ent() -> 
ie_match_stat() -> ce_match_stat_basic() -> *match_stat_data()*

Therefore, if the variable is lazily parsed, this means there will be a 
performance regression whenever the index status needs to be checked, 
e.g. 'git status'.

So, I guess it would be better to extend a bit:

'...where a lazy parse could cause unexpected fatal, and result in a 
performance regression...'

Thanks, yuchen

^ permalink raw reply

* [PATCH 4/4] doc: hook: don’t self-link via config include
From: kristofferhaugsbakk @ 2026-05-21 16:25 UTC (permalink / raw)
  To: git; +Cc: Kristoffer Haugsbakk, jn.avila, adrian.ratiu
In-Reply-To: <CV_doc_hook.6f0@msgid.xyz>

From: Kristoffer Haugsbakk <code@khaugsbakk.name>

Do not link to git-hook(1) from the config options when we already are
in that doc.

This implementation is similar to the updates to git-init(1) and
git-commit(1), implemented in [1] and [2], respectively.

† 1: e7b3a768 (doc: git-init: rework config item init.templateDir,
     2024-03-10)
† 2: 819fdd6e (doc: convert git commit config to new format, 2025-01-15)

Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
---
 Documentation/config/hook.adoc | 19 +++++++++++++------
 Documentation/git-hook.adoc    |  1 +
 2 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/Documentation/config/hook.adoc b/Documentation/config/hook.adoc
index a9dc0063c12..083dc60a132 100644
--- a/Documentation/config/hook.adoc
+++ b/Documentation/config/hook.adoc
@@ -1,10 +1,17 @@
+ifdef::git-hook[]
+:see-git-hook:
+endif::git-hook[]
+ifndef::git-hook[]
+:see-git-hook: See linkgit:git-hook[1].
+endif::git-hook[]
+
 hook.<friendly-name>.command::
 	The command to execute for `hook.<friendly-name>`. `<friendly-name>`
 	is a unique name that identifies this hook. The hook events that
 	trigger the command are configured with `hook.<friendly-name>.event`.
 	The value can be an executable path or a shell oneliner. If more than
 	one value is specified for the same `<friendly-name>`, only the last
-	value parsed is used. See linkgit:git-hook[1].
+	value parsed is used. {see-git-hook}
 
 hook.<friendly-name>.event::
 	The hook events that trigger `hook.<friendly-name>`. The value is the
@@ -14,7 +21,7 @@ hook.<friendly-name>.event::
 	This is a multi-valued key. To run `hook.<friendly-name>` on multiple
 	events, specify the key more than once. An empty value resets
 	the list of events, clearing any previously defined events for
-	`hook.<friendly-name>`. See linkgit:git-hook[1].
+	`hook.<friendly-name>`. {see-git-hook}
 +
 The `<friendly-name>` must not be the same as a known hook event name
 (e.g. do not use `hook.pre-commit.event`). Using a known event name as
@@ -27,7 +34,7 @@ hook.<friendly-name>.enabled::
 	Set to `false` to disable the hook without removing its
 	configuration. This is particularly useful when a hook is defined
 	in a system or global config file and needs to be disabled for a
-	specific repository. See linkgit:git-hook[1].
+	specific repository. {see-git-hook}
 
 hook.<friendly-name>.parallel::
 	Whether the hook `hook.<friendly-name>` may run in parallel with other hooks
@@ -37,13 +44,13 @@ hook.<friendly-name>.parallel::
 	all hooks for that event run sequentially regardless of `hook.jobs`.
 	Only configured (named) hooks need to declare this. Traditional hooks
 	found in the hooks directory do not need to, and run in parallel when
-	the effective job count is greater than 1. See linkgit:git-hook[1].
+	the effective job count is greater than 1. {see-git-hook}
 
 hook.<event>.enabled::
 	Switch to enable or disable all hooks for the `<event>` hook event.
 	When set to `false`, no hooks fire for that event, regardless of any
 	per-hook `hook.<friendly-name>.enabled` settings. Defaults to `true`.
-	See linkgit:git-hook[1].
+	{see-git-hook}
 +
 Note on naming: `<event>` must be the event name (e.g. `pre-commit`),
 not a hook friendly-name. Since using a known event name as a
@@ -60,7 +67,7 @@ hook.<event>.jobs::
 	setting has no effect unless all configured hooks for the event have
 	`hook.<friendly-name>.parallel` set to `true`. Set to `-1` to use the
 	number of available CPU cores. Must be a positive integer or `-1`;
-	zero is rejected with a warning. See linkgit:git-hook[1].
+	zero is rejected with a warning. {see-git-hook}
 +
 Note on naming: although this key resembles `hook.<friendly-name>.*`
 (a per-hook setting), `<event>` must be the event name, not a hook
diff --git a/Documentation/git-hook.adoc b/Documentation/git-hook.adoc
index 750df58e58e..4868852aa0b 100644
--- a/Documentation/git-hook.adoc
+++ b/Documentation/git-hook.adoc
@@ -204,6 +204,7 @@ unintended and unsupported ways.
 
 CONFIGURATION
 -------------
+:git-hook: 1
 include::config/hook.adoc[]
 
 SEE ALSO
-- 
2.54.0.13.g9c7419e39f8


^ permalink raw reply related

* [PATCH 3/4] doc: config: include existing git-hook(1) section
From: kristofferhaugsbakk @ 2026-05-21 16:25 UTC (permalink / raw)
  To: git; +Cc: Kristoffer Haugsbakk, jn.avila, adrian.ratiu
In-Reply-To: <CV_doc_hook.6f0@msgid.xyz>

From: Kristoffer Haugsbakk <code@khaugsbakk.name>

It is already included in git-hook(1) but missing from git-config(1).

Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
---
 Documentation/config.adoc | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Documentation/config.adoc b/Documentation/config.adoc
index dcea3c0c15e..a80e7db46d9 100644
--- a/Documentation/config.adoc
+++ b/Documentation/config.adoc
@@ -451,6 +451,8 @@ include::config/guitool.adoc[]
 
 include::config/help.adoc[]
 
+include::config/hook.adoc[]
+
 include::config/http.adoc[]
 
 include::config/i18n.adoc[]
-- 
2.54.0.13.g9c7419e39f8


^ permalink raw reply related

* [PATCH 2/4] doc: hook: consistently capitalize Git
From: kristofferhaugsbakk @ 2026-05-21 16:25 UTC (permalink / raw)
  To: git; +Cc: Kristoffer Haugsbakk, jn.avila, adrian.ratiu
In-Reply-To: <CV_doc_hook.6f0@msgid.xyz>

From: Kristoffer Haugsbakk <code@khaugsbakk.name>

Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
---
 Documentation/git-hook.adoc | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Documentation/git-hook.adoc b/Documentation/git-hook.adoc
index 96c5c9c3c23..750df58e58e 100644
--- a/Documentation/git-hook.adoc
+++ b/Documentation/git-hook.adoc
@@ -3,7 +3,7 @@ git-hook(1)
 
 NAME
 ----
-git-hook - Run git hooks
+git-hook - Run Git hooks
 
 SYNOPSIS
 --------
@@ -15,8 +15,8 @@ SYNOPSIS
 DESCRIPTION
 -----------
 
-A command interface for running git hooks (see linkgit:githooks[5]),
-for use by other scripted git commands.
+A command interface for running Git hooks (see linkgit:githooks[5]),
+for use by other scripted Git commands.
 
 This command parses the default configuration files for sets of configs like
 so:
@@ -161,7 +161,7 @@ setting, allowing all hooks for the event to run concurrently, even if they
 are not individually marked as parallel.
 +
 Some hooks always run sequentially regardless of this flag or the
-`hook.jobs` config, because git knows they cannot safely run in parallel:
+`hook.jobs` config, because Git knows they cannot safely run in parallel:
 `applypatch-msg`, `pre-commit`, `prepare-commit-msg`, `commit-msg`,
 `post-commit`, `post-checkout`, and `push-to-checkout`.
 
-- 
2.54.0.13.g9c7419e39f8


^ permalink raw reply related

* [PATCH 1/4] doc: hook: remove stray backtick
From: kristofferhaugsbakk @ 2026-05-21 16:25 UTC (permalink / raw)
  To: git; +Cc: Kristoffer Haugsbakk, jn.avila, adrian.ratiu
In-Reply-To: <CV_doc_hook.6f0@msgid.xyz>

From: Kristoffer Haugsbakk <code@khaugsbakk.name>

Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
---
 Documentation/git-hook.adoc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/git-hook.adoc b/Documentation/git-hook.adoc
index 46ea52db55f..96c5c9c3c23 100644
--- a/Documentation/git-hook.adoc
+++ b/Documentation/git-hook.adoc
@@ -41,7 +41,7 @@ spell-checker for your commit messages, you would write a configuration like so:
 
 With this config, when you run 'git commit', first `~/bin/linter --cpp20` will
 have a chance to check your files to be committed (during the `pre-commit` hook
-event`), and then `~/bin/spellchecker` will have a chance to check your commit
+event), and then `~/bin/spellchecker` will have a chance to check your commit
 message (during the `commit-msg` hook event).
 
 Commands are run in the order Git encounters their associated
-- 
2.54.0.13.g9c7419e39f8


^ permalink raw reply related

* [PATCH 0/4] doc: hook: small improvements
From: kristofferhaugsbakk @ 2026-05-21 16:25 UTC (permalink / raw)
  To: git; +Cc: Kristoffer Haugsbakk, jn.avila, adrian.ratiu

From: Kristoffer Haugsbakk <code@khaugsbakk.name>

Topic name: kh/doc-hook

Topic summary: Small improvements to git-hook(1) and the associated config.

[1/4] doc: hook: remove stray backtick
[2/4] doc: hook: consistently capitalize Git
[3/4] doc: config: include existing git-hook(1) section
[4/4] doc: hook: don’t self-link via config include

 Documentation/config.adoc      |  2 ++
 Documentation/config/hook.adoc | 19 +++++++++++++------
 Documentation/git-hook.adoc    | 11 ++++++-----
 3 files changed, 21 insertions(+), 11 deletions(-)


base-commit: aec3f587505a472db67e9462d0702e7d463a449d
-- 
2.54.0.13.g9c7419e39f8


^ permalink raw reply

* Re: [PATCH 03/18] odb/source-loose: start converting to a proper `struct odb_source`
From: Junio C Hamano @ 2026-05-21 15:49 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git
In-Reply-To: <20260521-b4-pks-odb-source-loose-v1-3-6553b399be2d@pks.im>

Patrick Steinhardt <ps@pks.im> writes:

> Start converting `struct odb_source_loose` into a proper pluggable
> `struct odb_source` by embedding the base struct and assigning it the
> new `ODB_SOURCE_LOOSE` type. Furthermore, wire up lifecycle management
> of this source by implementing the `free` callback and taking ownership
> of the chdir notifications.
>
> Note that the loose source is not yet functional as a standalone `struct
> odb_source`, as it's missing all of the callback implementations. These
> will be wired up in subsequent commits.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
>  object-file.c      | 17 -----------------
>  object-file.h      |  2 --
>  odb/source-files.c |  2 +-
>  odb/source-loose.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
>  odb/source-loose.h | 14 ++++++++++++++
>  odb/source.h       |  3 +++
>  6 files changed, 63 insertions(+), 20 deletions(-)
>
> diff --git a/object-file.c b/object-file.c
> index 7a1908bfc0..977d959d33 100644
> --- a/object-file.c
> +++ b/object-file.c
> @@ -2041,14 +2041,6 @@ static struct oidtree *odb_source_loose_cache(struct odb_source *source,
>  	return files->loose->cache;
>  }
>  
> -static void odb_source_loose_clear_cache(struct odb_source_loose *loose)
> -{
> -	oidtree_clear(loose->cache);
> -	FREE_AND_NULL(loose->cache);
> -	memset(&loose->subdir_seen, 0,
> -	       sizeof(loose->subdir_seen));
> -}
> -
>  void odb_source_loose_reprepare(struct odb_source *source)
>  {
>  	struct odb_source_files *files = odb_source_files_downcast(source);
> @@ -2205,15 +2197,6 @@ struct odb_transaction *odb_transaction_files_begin(struct odb_source *source)
>  	return &transaction->base;
>  }
>  
> -void odb_source_loose_free(struct odb_source_loose *loose)
> -{
> -	if (!loose)
> -		return;
> -	odb_source_loose_clear_cache(loose);
> -	loose_object_map_clear(&loose->map);
> -	free(loose);
> -}
> -
>  struct odb_loose_read_stream {
>  	struct odb_read_stream base;
>  	git_zstream z;
> diff --git a/object-file.h b/object-file.h
> index 1d8312cf7f..02c9680980 100644
> --- a/object-file.h
> +++ b/object-file.h
> @@ -21,8 +21,6 @@ struct object_info;
>  struct odb_read_stream;
>  struct odb_source;
>  
> -void odb_source_loose_free(struct odb_source_loose *loose);
> -
>  /* Reprepare the loose source by emptying the loose object cache. */
>  void odb_source_loose_reprepare(struct odb_source *source);
>  
> diff --git a/odb/source-files.c b/odb/source-files.c
> index 185cc6903e..ccc637311b 100644
> --- a/odb/source-files.c
> +++ b/odb/source-files.c
> @@ -27,7 +27,7 @@ static void odb_source_files_free(struct odb_source *source)
>  {
>  	struct odb_source_files *files = odb_source_files_downcast(source);
>  	chdir_notify_unregister(NULL, odb_source_files_reparent, files);
> -	odb_source_loose_free(files->loose);
> +	odb_source_free(&files->loose->base);
>  	packfile_store_free(files->packed);
>  	odb_source_release(&files->base);
>  	free(files);
> diff --git a/odb/source-loose.c b/odb/source-loose.c
> index c9e7414814..92e18f5adb 100644
> --- a/odb/source-loose.c
> +++ b/odb/source-loose.c
> @@ -1,10 +1,55 @@
>  #include "git-compat-util.h"
> +#include "abspath.h"
> +#include "chdir-notify.h"
> +#include "loose.h"
> +#include "odb.h"
> +#include "odb/source-files.h"
>  #include "odb/source-loose.h"
> +#include "oidtree.h"
> +
> +void odb_source_loose_clear_cache(struct odb_source_loose *loose)
> +{
> +	oidtree_clear(loose->cache);
> +	FREE_AND_NULL(loose->cache);
> +	memset(&loose->subdir_seen, 0,
> +	       sizeof(loose->subdir_seen));
> +}
> +
> +static void odb_source_loose_reparent(const char *name UNUSED,
> +				      const char *old_cwd,
> +				      const char *new_cwd,
> +				      void *cb_data)
> +{
> +	struct odb_source_loose *loose = cb_data;
> +	char *path = reparent_relative_path(old_cwd, new_cwd,
> +					    loose->base.path);
> +	free(loose->base.path);
> +	loose->base.path = path;
> +}
> +
> +static void odb_source_loose_free(struct odb_source *source)
> +{
> +	struct odb_source_loose *loose = odb_source_loose_downcast(source);
> +	odb_source_loose_clear_cache(loose);
> +	loose_object_map_clear(&loose->map);
> +	chdir_notify_unregister(NULL, odb_source_loose_reparent, loose);
> +	odb_source_release(&loose->base);
> +	free(loose);
> +}
>  
>  struct odb_source_loose *odb_source_loose_new(struct odb_source_files *files)
>  {
>  	struct odb_source_loose *loose;
> +
>  	CALLOC_ARRAY(loose, 1);
> +	odb_source_init(&loose->base, files->base.odb, ODB_SOURCE_LOOSE,
> +			files->base.path, files->base.local);
>  	loose->files = files;
> +
> +	loose->base.free = odb_source_loose_free;
> +
> +	if (!is_absolute_path(loose->base.path))
> +		chdir_notify_register(NULL, odb_source_loose_reparent, loose);
> +
>  	return loose;
>  }
> diff --git a/odb/source-loose.h b/odb/source-loose.h
> index bf61e767c8..441da9e418 100644
> --- a/odb/source-loose.h
> +++ b/odb/source-loose.h
> @@ -12,6 +12,7 @@ struct oidtree;
>   * file per object. This source is part of the files source.
>   */
>  struct odb_source_loose {
> +	struct odb_source base;
>  	struct odb_source_files *files;
>  
>  	/*
> @@ -32,4 +33,17 @@ struct odb_source_loose {
>  
>  struct odb_source_loose *odb_source_loose_new(struct odb_source_files *files);
>  
> +/*
> + * Cast the given object database source to the loose backend. This will cause
> + * a BUG in case the source uses doesn't use this backend.

"uses doesn't use"???

> + */
> +static inline struct odb_source_loose *odb_source_loose_downcast(struct odb_source *source)
> +{
> +	if (source->type != ODB_SOURCE_LOOSE)
> +		BUG("trying to downcast source of type '%d' to loose", source->type);
> +	return container_of(source, struct odb_source_loose, base);
> +}
> +
> +void odb_source_loose_clear_cache(struct odb_source_loose *loose);
> +
>  #endif
> diff --git a/odb/source.h b/odb/source.h
> index 0a440884e4..8bcb67787e 100644
> --- a/odb/source.h
> +++ b/odb/source.h
> @@ -14,6 +14,9 @@ enum odb_source_type {
>  	/* The "files" backend that uses loose objects and packfiles. */
>  	ODB_SOURCE_FILES,
>  
> +	/* The "loose" backend that uses loose objects, only. */
> +	ODB_SOURCE_LOOSE,
> +
>  	/* The "in-memory" backend that stores objects in memory. */
>  	ODB_SOURCE_INMEMORY,
>  };

^ permalink raw reply

* Re: [PATCH 4/9] run-command: add support for timeout in command finisher
From: Johannes Sixt @ 2026-05-21 14:36 UTC (permalink / raw)
  To: Siddh Raman Pant
  Cc: git@vger.kernel.org, gitster@pobox.com, newren@gmail.com,
	ps@pks.im, oswald.buddenhagen@gmx.de, code@khaugsbakk.name
In-Reply-To: <2f7eea03273ffaacc50a9ae186673da88fc3345f.camel@oracle.com>

Am 21.05.26 um 11:59 schrieb Siddh Raman Pant:
> The timeout is for the failure path, where the external helper has
> already stopped following that protocol or is blocked on something
> outside git's control. Since git starts the helper and puts it on the
> log/grep path, git also needs a bounded way to recover when that helper
> does not make progress. Otherwise an optional note source can prevent
> the main git command from completing.

That Git communicates with a process that looks like it stopped is the
normal case, for example:

- Output is sent to the pager. The user can take their time to study the
output. All the while, git waits patiently for the user to advance the
pager.

- Git fetch transfers large amounts of data across the network. Most of
the time it waits for data to arrive and does nothing. The peer process
looks like it hangs. Git does not decide to kill the connection at any
time. It is the user's decision to do so.

If the notes provider hangs, then it is not on Git to decide when it has
waited long enough.

-- Hannes


^ permalink raw reply

* Re: [PATCH v5 0/4] approxidate: tweak special date formats
From: Tuomas Ahola @ 2026-05-21 14:14 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Jeff King
In-Reply-To: <xmqqik8g28gw.fsf@gitster.g>

Junio C Hamano <gitster@pobox.com> wrote:

> Tuomas Ahola <taahol@utu.fi> writes:
> 
> > -static void date_today(struct tm *tm, struct tm *now, int *num UNUSED)
> > +static void date_today(struct tm *tm, struct tm *now, int *num)
> >  {
> >  	if (tm->tm_hour == now->tm_hour &&
> >  	    tm->tm_min == now->tm_min &&
> >  	    tm->tm_sec == now->tm_sec)
> >  		date_time(tm, 0);
> > +	*num = 0;
> >  	tm->tm_mday = -1;
> >  	update_tm(tm, now, 0);
> >  }
> 
> Hmph, what is this change about?  Does the lack of this clearing
> break some test?
> 

As you can see, many other date_*() functions have that same assignment, too.
It looked a bit off, so I first left it out, but this revision does add a
corner case test[*] that would fail without it.

> In any case, will queue.  It seems that we are getting to the point
> of diminishing returns and better off declaring victory soonish?
> 

Yes, I agree.  The patch series is effectively finished as I don't have any
further itches or ideas.  And I think the series gets its work done quite well
actually.

> > +check_approxidate 'January 5th today pm' '2009-01-30 12:00:00'

[*] Namely, this one.

^ permalink raw reply

* Re: [PATCH v11] checkout: extend --track with a "fetch" mode to refresh start-point
From: Phillip Wood @ 2026-05-21 14:06 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Harald Nordgren via GitGitGadget, git, Ramsay Jones,
	D. Ben Knoble, Kristoffer Haugsbakk, Marc Branchaud,
	Harald Nordgren
In-Reply-To: <xmqqtss02a2o.fsf@gitster.g>

On 21/05/2026 13:58, Junio C Hamano wrote:
> Phillip Wood <phillip.wood123@gmail.com> writes:
> 
>>> One.  Have you considered the case where the remote-tracking refs
>>> are overlapping, e.g., where "origin" and "upstream" point at
>>> different URLs but they both store in "refs/remotes/upstream/*"?
>>> Perhaps their URLs may textually be different but are pointing
>>> logically at the same place (e.g., one ssh:// the other https:// for
>>> example).
>>>
>>> What should happen?  What does happen after you apply this patch?
>>
>> It would be worth looking at what "git checkout --track" does in that
>> case and seeing if we can share the code.
> 
> It always is a good idea to think how we can share code for
> different purposes to solve a new problem, but in this particular
> one, I am not sure if "git checkout -t -b topic upstream/main"
> codepath has much to offer to solve what the new "before the
> checkout, update from the remote" feature wants to do.  To the
> former, it does not matter how refs/remotes/upstream/* are updated
> and by fetching which remote at all.

Don't we want to avoid creating a branch with an ambiguous upstream so 
that a subsequent "git pull" works though? Looking at 
branch.c:setup_tracking() it seems to reject upstream branches that 
match more than one remote.

Thanks

Phillip

>  The only thing it cares about
> is to leave the record that this new "topic" branch works with
> refs/remotes/upstrea/main.  But the latter needs to be able to
> compute which remote it should fetch from.  It is a problem that
> existing code had no need to solve.
> 


^ permalink raw reply

* [PATCH v4] remote: qualify "git pull" advice for non-upstream compareBranches
From: Harald Nordgren via GitGitGadget @ 2026-05-21 14:06 UTC (permalink / raw)
  To: git; +Cc: Harald Nordgren, Harald Nordgren
In-Reply-To: <pull.2301.v3.git.git.1779282625696.gitgitgadget@gmail.com>

From: Harald Nordgren <haraldnordgren@gmail.com>

Enable ENABLE_ADVICE_PULL for push-branch comparisons too, not just
the upstream entry, so the "use git pull" hint prints when the local
branch is behind its push branch.

Spell out "git pull <remote> <branch>" so running the suggested
command actually pulls the ref the user was told about; plain
"git pull" would fetch the upstream instead.

Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
---
    remote: qualify "git pull" advice for non-upstream branches
    
     * Don't suggest git pull when we have no good command to suggest.
     * New test for this. Asserts the behind line shows with no follow-up
       advice.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2301%2FHaraldNordgren%2Fstatus-pull-advice-qualified-v4
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2301/HaraldNordgren/status-pull-advice-qualified-v4
Pull-Request: https://github.com/git/git/pull/2301

Range-diff vs v3:

 1:  3703be9aac ! 1:  ef54dacb07 remote: qualify "git pull" advice for non-upstream compareBranches
     @@ remote.c: int format_tracking_info(struct branch *branch, struct strbuf *sb,
       		full_ref = resolve_compare_branch(branch,
       						  branches.items[i].string);
      @@ remote.c: int format_tracking_info(struct branch *branch, struct strbuf *sb,
     - 		if (reported)
     - 			strbuf_addstr(sb, "\n");
       
     --		if (is_upstream)
     -+		if (is_upstream || is_push)
     + 		if (is_upstream)
       			flags |= ENABLE_ADVICE_PULL;
      -		if (is_push)
      -			flags |= ENABLE_ADVICE_PUSH;
     @@ remote.c: int format_tracking_info(struct branch *branch, struct strbuf *sb,
      +				if (push_remote_name &&
      +				    skip_prefix(full_ref, "refs/remotes/", &push_branch_name) &&
      +				    skip_prefix(push_branch_name, push_remote_name, &push_branch_name) &&
     -+				    *push_branch_name == '/')
     ++				    *push_branch_name == '/') {
      +					push_branch_name++;
     -+				else
     ++					flags |= ENABLE_ADVICE_PULL;
     ++				} else {
      +					push_remote_name = NULL;
     ++				}
     ++			} else {
     ++				flags |= ENABLE_ADVICE_PULL;
      +			}
      +		}
       		format_branch_comparison(sb, !cmp, ours, theirs, short_ref,
     @@ t/t6040-tracking-info.sh: test_expect_success 'status.compareBranches with remap
      +	EOF
      +	test_cmp expect actual
      +'
     ++
     ++test_expect_success 'status.compareBranches suppresses advice when push tracking ref is unconventional' '
     ++	test_config -C test push.default current &&
     ++	test_config -C test remote.imported.url ../. &&
     ++	test_config -C test remote.imported.fetch "+refs/heads/*:refs/imported/imported/*" &&
     ++	test_config -C test branch.feature17.pushRemote imported &&
     ++	test_config -C test status.compareBranches "@{push}" &&
     ++	git -C test fetch imported &&
     ++	git -C test checkout --no-track -b feature17 refs/imported/imported/main &&
     ++	(cd test && advance work17) &&
     ++	git -C test push imported HEAD:feature17 &&
     ++	git -C test fetch imported &&
     ++	git -C test reset --hard HEAD^ &&
     ++	git -C test status >actual &&
     ++	cat >expect <<-EOF &&
     ++	On branch feature17
     ++	Your branch is behind ${SQ}imported/imported/feature17${SQ} by 1 commit, and can be fast-forwarded.
     ++
     ++	nothing to commit, working tree clean
     ++	EOF
     ++	test_cmp expect actual
     ++'
      +
       test_done


 remote.c                 |  48 +++++++++++++++----
 t/t6040-tracking-info.sh | 100 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 140 insertions(+), 8 deletions(-)

diff --git a/remote.c b/remote.c
index 24a8118d25..193e1dd1f1 100644
--- a/remote.c
+++ b/remote.c
@@ -2268,6 +2268,8 @@ static void format_branch_comparison(struct strbuf *sb,
 				     bool up_to_date,
 				     int ours, int theirs,
 				     const char *branch_name,
+				     const char *push_remote_name,
+				     const char *push_branch_name,
 				     enum ahead_behind_flags abf,
 				     unsigned flags)
 {
@@ -2303,9 +2305,15 @@ static void format_branch_comparison(struct strbuf *sb,
 			       "and can be fast-forwarded.\n",
 			   theirs),
 			branch_name, theirs);
-		if (use_pull_advice && advice_enabled(ADVICE_STATUS_HINTS))
-			strbuf_addstr(sb,
-				_("  (use \"git pull\" to update your local branch)\n"));
+		if (use_pull_advice && advice_enabled(ADVICE_STATUS_HINTS)) {
+			if (push_remote_name && push_branch_name)
+				strbuf_addf(sb,
+					_("  (use \"git pull %s %s\" to update your local branch)\n"),
+					push_remote_name, push_branch_name);
+			else
+				strbuf_addstr(sb,
+					_("  (use \"git pull\" to update your local branch)\n"));
+		}
 	} else {
 		strbuf_addf(sb,
 			Q_("Your branch and '%s' have diverged,\n"
@@ -2316,9 +2324,15 @@ static void format_branch_comparison(struct strbuf *sb,
 			       "respectively.\n",
 			   ours + theirs),
 			branch_name, ours, theirs);
-		if (use_divergence_advice && advice_enabled(ADVICE_STATUS_HINTS))
-			strbuf_addstr(sb,
-				_("  (use \"git pull\" if you want to integrate the remote branch with yours)\n"));
+		if (use_divergence_advice && advice_enabled(ADVICE_STATUS_HINTS)) {
+			if (push_remote_name && push_branch_name)
+				strbuf_addf(sb,
+					_("  (use \"git pull %s %s\" if you want to integrate the remote branch with yours)\n"),
+					push_remote_name, push_branch_name);
+			else
+				strbuf_addstr(sb,
+					_("  (use \"git pull\" if you want to integrate the remote branch with yours)\n"));
+		}
 	}
 }
 
@@ -2356,6 +2370,8 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
 		int ours, theirs, cmp;
 		int is_upstream, is_push;
 		unsigned flags = 0;
+		const char *push_remote_name = NULL;
+		const char *push_branch_name = NULL;
 
 		full_ref = resolve_compare_branch(branch,
 						  branches.items[i].string);
@@ -2399,11 +2415,27 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
 
 		if (is_upstream)
 			flags |= ENABLE_ADVICE_PULL;
-		if (is_push)
-			flags |= ENABLE_ADVICE_PUSH;
 		if (show_divergence_advice && is_upstream)
 			flags |= ENABLE_ADVICE_DIVERGENCE;
+		if (is_push) {
+			flags |= ENABLE_ADVICE_PUSH;
+			if (!upstream_ref || strcmp(upstream_ref, full_ref)) {
+				push_remote_name = pushremote_for_branch(branch, NULL);
+				if (push_remote_name &&
+				    skip_prefix(full_ref, "refs/remotes/", &push_branch_name) &&
+				    skip_prefix(push_branch_name, push_remote_name, &push_branch_name) &&
+				    *push_branch_name == '/') {
+					push_branch_name++;
+					flags |= ENABLE_ADVICE_PULL;
+				} else {
+					push_remote_name = NULL;
+				}
+			} else {
+				flags |= ENABLE_ADVICE_PULL;
+			}
+		}
 		format_branch_comparison(sb, !cmp, ours, theirs, short_ref,
+					 push_remote_name, push_branch_name,
 					 abf, flags);
 		reported = 1;
 
diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh
index 0242b5bf7a..91cbb8775d 100755
--- a/t/t6040-tracking-info.sh
+++ b/t/t6040-tracking-info.sh
@@ -646,4 +646,104 @@ test_expect_success 'status.compareBranches with remapped push and upstream remo
 	test_cmp expect actual
 '
 
+test_expect_success 'status.compareBranches behind both upstream and push' '
+	test_config -C test push.default current &&
+	test_config -C test remote.pushDefault origin &&
+	test_config -C test status.compareBranches "@{upstream} @{push}" &&
+	git -C test checkout -b feature13 upstream/main &&
+	(cd test && advance work13) &&
+	git -C test push origin &&
+	git -C test branch --set-upstream-to upstream/ahead &&
+	git -C test reset --hard HEAD^ &&
+	git -C test status >actual &&
+	cat >expect <<-EOF &&
+	On branch feature13
+	Your branch is behind ${SQ}upstream/ahead${SQ} by 1 commit, and can be fast-forwarded.
+	  (use "git pull" to update your local branch)
+
+	Your branch is behind ${SQ}origin/feature13${SQ} by 1 commit, and can be fast-forwarded.
+	  (use "git pull origin feature13" to update your local branch)
+
+	nothing to commit, working tree clean
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'status.compareBranches with remapped push and behind push branch' '
+	test_config -C test remote.pushDefault origin &&
+	test_config -C test remote.origin.push refs/heads/feature14:refs/heads/remapped14 &&
+	test_config -C test status.compareBranches "@{push}" &&
+	git -C test checkout -b feature14 upstream/main &&
+	(cd test && advance work14) &&
+	git -C test push &&
+	git -C test reset --hard HEAD^ &&
+	git -C test status >actual &&
+	cat >expect <<-EOF &&
+	On branch feature14
+	Your branch is behind ${SQ}origin/remapped14${SQ} by 1 commit, and can be fast-forwarded.
+	  (use "git pull origin remapped14" to update your local branch)
+
+	nothing to commit, working tree clean
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'status.compareBranches with behind push branch and no upstream' '
+	test_config -C test push.default current &&
+	test_config -C test remote.pushDefault origin &&
+	test_config -C test status.compareBranches "@{push}" &&
+	git -C test checkout --no-track -b feature15 upstream/main &&
+	(cd test && advance work15) &&
+	git -C test push origin &&
+	git -C test reset --hard HEAD^ &&
+	git -C test status >actual &&
+	cat >expect <<-EOF &&
+	On branch feature15
+	Your branch is behind ${SQ}origin/feature15${SQ} by 1 commit, and can be fast-forwarded.
+	  (use "git pull origin feature15" to update your local branch)
+
+	nothing to commit, working tree clean
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'status.compareBranches behind upstream-equals-push suggests plain pull' '
+	test_config -C test status.compareBranches "@{upstream} @{push}" &&
+	git -C test checkout -b feature16 origin/main &&
+	(cd test && advance work16) &&
+	git -C test push origin HEAD:main &&
+	git -C test reset --hard HEAD^ &&
+	git -C test status >actual &&
+	cat >expect <<-EOF &&
+	On branch feature16
+	Your branch is behind ${SQ}origin/main${SQ} by 1 commit, and can be fast-forwarded.
+	  (use "git pull" to update your local branch)
+
+	nothing to commit, working tree clean
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'status.compareBranches suppresses advice when push tracking ref is unconventional' '
+	test_config -C test push.default current &&
+	test_config -C test remote.imported.url ../. &&
+	test_config -C test remote.imported.fetch "+refs/heads/*:refs/imported/imported/*" &&
+	test_config -C test branch.feature17.pushRemote imported &&
+	test_config -C test status.compareBranches "@{push}" &&
+	git -C test fetch imported &&
+	git -C test checkout --no-track -b feature17 refs/imported/imported/main &&
+	(cd test && advance work17) &&
+	git -C test push imported HEAD:feature17 &&
+	git -C test fetch imported &&
+	git -C test reset --hard HEAD^ &&
+	git -C test status >actual &&
+	cat >expect <<-EOF &&
+	On branch feature17
+	Your branch is behind ${SQ}imported/imported/feature17${SQ} by 1 commit, and can be fast-forwarded.
+
+	nothing to commit, working tree clean
+	EOF
+	test_cmp expect actual
+'
+
 test_done

base-commit: aec3f587505a472db67e9462d0702e7d463a449d
-- 
gitgitgadget

^ permalink raw reply related

* Re: [PATCH v3] git-jump: pick a mode automatically when invoked without arguments
From: Junio C Hamano @ 2026-05-21 14:00 UTC (permalink / raw)
  To: Greg Hurrell via GitGitGadget
  Cc: git, Jeff King, Greg Hurrell, Erik Cervin Edin, Greg Hurrell
In-Reply-To: <pull.2108.v3.git.1779371110195.gitgitgadget@gmail.com>

"Greg Hurrell via GitGitGadget" <gitgitgadget@gmail.com> writes:

>      * Removed stray # from README.
>      * Don't both teaching "auto" to select "ws" mode, because it is always
>        subsumed by "diff".
>      * Update usage string to make clear that git jump --stdout foo is not a
>        synonym for git jump --stdout auto foo, because distinguishing
>        between foo as <mode> and foo as <arg> is fraught with ambiguity.
>     
>     In answer to Junio's question:
>     
>     > If more than one interesting cases apply, what happens, and what
>     > should happen?
>     
>     it's an ordered choice (merge > diff).

After 'diff --quiet "$@"' says "nothing interesting between the
index and the working tree", I actually think it may be worth using
either 'git diff --check HEAD "$@"' or 'git diff --cached --check "$@"'
to see if ws fix is needed.

But I am not a target audience of this feature, so I'll let others
figure out what to do here.

^ permalink raw reply

* [PATCH v3] git-jump: pick a mode automatically when invoked without arguments
From: Greg Hurrell via GitGitGadget @ 2026-05-21 13:45 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Greg Hurrell, Erik Cervin Edin, Junio C Hamano,
	Greg Hurrell, Greg Hurrell
In-Reply-To: <pull.2108.v2.git.1779280307112.gitgitgadget@gmail.com>

From: Greg Hurrell <greg.hurrell@datadoghq.com>

When `git jump` is invoked with no positional arguments (and no
arguments after `--stdout`) it currently prints usage and exits with
status 1.

But there are two situations where we can usefully infer the most
valuable and likely mode that a user would want to use, and select it
automatically:

1. When there are unmerged paths in the index, the user likely
   wants `git jump merge`.

2. When the working tree has unstaged changes, the user likely
   wants `git jump diff`.

In this commit we teach `git jump` a new "auto" mode which detects these
cases and dispatches to the corresponding mode automatically. The user
can either explicitly spell out `git jump auto`, or just leave it at
`git jump` (because "auto" is the default).

If none of the interesting cases listed above applies, then auto mode
falls back to the existing usage-and-exit behavior.

Signed-off-by: Greg Hurrell <greg.hurrell@datadoghq.com>
---
    git-jump: pick a mode automatically when invoked without arguments
    
    Changes since v2; all of these in response to feedback from Junio:
    
     * Removed stray # from README.
     * Don't both teaching "auto" to select "ws" mode, because it is always
       subsumed by "diff".
     * Update usage string to make clear that git jump --stdout foo is not a
       synonym for git jump --stdout auto foo, because distinguishing
       between foo as <mode> and foo as <arg> is fraught with ambiguity.
    
    In answer to Junio's question:
    
    > If more than one interesting cases apply, what happens, and what
    > should happen?
    
    it's an ordered choice (merge > diff).

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2108%2Fwincent%2Fauto-jump-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2108/wincent/auto-jump-v3
Pull-Request: https://github.com/gitgitgadget/git/pull/2108

Range-diff vs v2:

 1:  5fbc8480ef ! 1:  af758dcfd2 git-jump: pick a mode automatically when invoked without arguments
     @@ Commit message
          arguments after `--stdout`) it currently prints usage and exits with
          status 1.
      
     -    But there are several situations where we can usefully infer the most
     +    But there are two situations where we can usefully infer the most
          valuable and likely mode that a user would want to use, and select it
          automatically:
      
     @@ Commit message
          2. When the working tree has unstaged changes, the user likely
             wants `git jump diff`.
      
     -    3. In the presence of conflict markers or whitespace errors (as reported
     -       by `git diff --check`), the user likely wants `git jump ws`.
     -
          In this commit we teach `git jump` a new "auto" mode which detects these
          cases and dispatches to the corresponding mode automatically. The user
          can either explicitly spell out `git jump auto`, or just leave it at
     @@ contrib/git-jump/README: git jump grep foo_bar
       git jump grep -i foo_bar
       
      +# jump to places with conflict markers or whitespace errors
     -+# (as reported by # `git diff --check`)
     ++# (as reported by `git diff --check`)
      +git jump ws
      +
       # use the silver searcher for git jump grep
     @@ contrib/git-jump/README: git jump grep foo_bar
      +# whitespace problems; otherwise show usage
      +git jump auto
      +
     -+# with no explicit mode, same as "auto"
     ++# with no explicit mode and no args, same as "auto"
      +git jump
       --------------------------------------------------
       
     @@ contrib/git-jump/README: git jump grep foo_bar
      
       ## contrib/git-jump/git-jump ##
      @@
     - 
       usage() {
       	cat <<\EOF
     --usage: git jump [--stdout] <mode> [<args>]
     -+usage: git jump [--stdout] [<mode>] [<args>]
     + usage: git jump [--stdout] <mode> [<args>]
     ++   or: git jump [--stdout]
       
       Jump to interesting elements in an editor.
      -The <mode> parameter is one of:
     -+The <mode> parameter is one of the following,
     -+defaulting to "auto" if omitted:
     ++The <mode> parameter is one of the following.
     ++With no <mode> and no <args>, it defaults to "auto".
       
       diff: elements are diff hunks. Arguments are given to diff.
       
     @@ contrib/git-jump/git-jump: mode_ws() {
      +		mode_merge "$@"
      +	elif ! git diff --quiet "$@"; then
      +		mode_diff "$@"
     -+	elif ! git diff --check >/dev/null 2>&1; then
     -+		mode_ws "$@"
      +	else
      +		usage >&2
      +		exit 1


 contrib/git-jump/README   | 12 ++++++++++++
 contrib/git-jump/git-jump | 26 +++++++++++++++++++++++---
 2 files changed, 35 insertions(+), 3 deletions(-)

diff --git a/contrib/git-jump/README b/contrib/git-jump/README
index 3211841305..aabec4a756 100644
--- a/contrib/git-jump/README
+++ b/contrib/git-jump/README
@@ -75,8 +75,20 @@ git jump grep foo_bar
 # arbitrary grep options
 git jump grep -i foo_bar
 
+# jump to places with conflict markers or whitespace errors
+# (as reported by `git diff --check`)
+git jump ws
+
 # use the silver searcher for git jump grep
 git config jump.grepCmd "ag --column"
+
+# pick a mode automatically: "merge" if there are unmerged paths,
+# "diff" if the worktree has unstaged changes, "ws" if there are
+# whitespace problems; otherwise show usage
+git jump auto
+
+# with no explicit mode and no args, same as "auto"
+git jump
 --------------------------------------------------
 
 You can use the optional argument '--stdout' to print the listing to
diff --git a/contrib/git-jump/git-jump b/contrib/git-jump/git-jump
index 8d1d5d79a6..79286d8112 100755
--- a/contrib/git-jump/git-jump
+++ b/contrib/git-jump/git-jump
@@ -3,9 +3,11 @@
 usage() {
 	cat <<\EOF
 usage: git jump [--stdout] <mode> [<args>]
+   or: git jump [--stdout]
 
 Jump to interesting elements in an editor.
-The <mode> parameter is one of:
+The <mode> parameter is one of the following.
+With no <mode> and no <args>, it defaults to "auto".
 
 diff: elements are diff hunks. Arguments are given to diff.
 
@@ -16,6 +18,10 @@ grep: elements are grep hits. Arguments are given to git grep or, if
 
 ws: elements are whitespace errors. Arguments are given to diff --check.
 
+auto: select one of the other modes based on worktree state;
+      "merge" if there are unmerged paths, "diff" if there are
+      unstaged changes, "ws" if there are whitespace errors.
+
 If the optional argument `--stdout` is given, print the quickfix
 lines to standard output instead of feeding it to the editor.
 EOF
@@ -82,6 +88,21 @@ mode_ws() {
 	git diff --check "$@"
 }
 
+mode_auto() {
+	if test "$(git rev-parse --is-inside-work-tree 2>/dev/null)" != "true"; then
+		usage >&2
+		exit 1
+	fi
+	if test -n "$(git ls-files -u "$@")"; then
+		mode_merge "$@"
+	elif ! git diff --quiet "$@"; then
+		mode_diff "$@"
+	else
+		usage >&2
+		exit 1
+	fi
+}
+
 use_stdout=
 while test $# -gt 0; do
 	case "$1" in
@@ -99,8 +120,7 @@ while test $# -gt 0; do
 	shift
 done
 if test $# -lt 1; then
-	usage >&2
-	exit 1
+	set -- auto
 fi
 mode=$1; shift
 type "mode_$mode" >/dev/null 2>&1 || { usage >&2; exit 1; }

base-commit: aec3f587505a472db67e9462d0702e7d463a449d
-- 
gitgitgadget

^ permalink raw reply related

* Re: [PATCH v5 0/4] approxidate: tweak special date formats
From: Junio C Hamano @ 2026-05-21 13:33 UTC (permalink / raw)
  To: Tuomas Ahola; +Cc: git, Jeff King
In-Reply-To: <20260521105408.8222-1-taahol@utu.fi>

Tuomas Ahola <taahol@utu.fi> writes:

> -static void date_today(struct tm *tm, struct tm *now, int *num UNUSED)
> +static void date_today(struct tm *tm, struct tm *now, int *num)
>  {
>  	if (tm->tm_hour == now->tm_hour &&
>  	    tm->tm_min == now->tm_min &&
>  	    tm->tm_sec == now->tm_sec)
>  		date_time(tm, 0);
> +	*num = 0;
>  	tm->tm_mday = -1;
>  	update_tm(tm, now, 0);
>  }

Hmph, what is this change about?  Does the lack of this clearing
break some test?

In any case, will queue.  It seems that we are getting to the point
of diminishing returns and better off declaring victory soonish?

> diff --git a/t/t0006-date.sh b/t/t0006-date.sh
> index b187b1bfc4..9a76b84ed9 100755
> --- a/t/t0006-date.sh
> +++ b/t/t0006-date.sh
> @@ -212,13 +212,14 @@ check_approxidate 'noon today' '2009-08-30 12:00:00'
>  check_approxidate 'today at noon' '2009-08-30 12:00:00' '-12 hours'
>  check_approxidate 'noon today' '2009-09-01 12:00:00' '+36 hours'
>  check_approxidate 'noon yesterday' '2009-08-29 12:00:00'
> +check_approxidate 'noon yesterday' '2009-08-29 12:00:00' '-12 hours'
>  check_approxidate 'last Friday at noon' '2009-08-28 12:00:00'
>  check_approxidate 'last Friday at noon' '2009-08-28 12:00:00' '-12 hours'
> -check_approxidate 'noon yesterday' '2009-08-29 12:00:00' '-12 hours'
>  check_approxidate 'tea last saturday' '2009-08-29 17:00:00'
>  check_approxidate 'tea last saturday' '2009-08-29 17:00:00' '-12 hours'
>  check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'
>  check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00' '-12 hours'
> +check_approxidate 'January 5th today pm' '2009-01-30 12:00:00'
>  check_approxidate '10am noon' '2009-08-29 12:00:00'
>  check_approxidate 'January 5th yesterday' '2009-01-29 19:20:00'
>  check_approxidate 'January 5th yesterday' '2008-12-31 19:20:00' '+2 days'
>
> base-commit: 94f057755b7941b321fd11fec1b2e3ca5313a4e0

^ permalink raw reply

* Re: [PATCH v9 3/5] branch: add --prune-merged <remote>
From: Junio C Hamano @ 2026-05-21 13:29 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Harald Nordgren via GitGitGadget, git, Kristoffer Haugsbakk,
	Johannes Sixt, Harald Nordgren
In-Reply-To: <6501a3d5-a5ec-421b-8526-ee7d4ae5ea98@gmail.com>

Phillip Wood <phillip.wood123@gmail.com> writes:

> I think being able to prune branches that have been merged into their 
> upstream is a good idea. However, I find the focus on remotes rather 
> than upstream branches in the UI a bit confusing. While the upstream of 
> a branch is often a remote tracking branch it doesn't have to be. For my 
> personal projects I often do
>
> 	git checkout -b topic master
>
> and it would be nice to be able to run
>
> 	git branch --prune-merged master
>
> to clean up those topics that have been merged.

Excellent suggestion.

The task at hand is to make a list of things that "track" (in the
"checkout -t" sense) something, see which one in that list are
descendant of that something, and removing those that have already
been merged into that something.  There is nothing that requires
that something to be a remote-tracking branch at all.

If

    git branch --forked master

(i.e., "who tracks 'master' in the 'checkout -t' sense") were
available, then

    git branch --merged master | sort >1
    git branch --forked master | sort >2
    comm -12 1 2

would give us the list of branches that forked from 'master' and
have already been merged, i.e., candidate to be removed via the
"prune-merged" mechanism.  With the built-in "git branch -d"
protection to require capital "-D" to force removal of a new work
that is not merged to tracked, we actually need only 

    git branch --forked master |
    xargs git branch -d

to do so.  We may want to give --dry-run to "git branch -d" so that
you can say

    git branch --forked master |
    xargs git branch -d --dry-run

to see what would be removed.

And again, there is no strong reason why "--forked" has to work only
with remote-tracking branches.

> Similarly I think it is 
> confusing that
>
> 	git checkout -b topic origin
>
> starts a branch from the default branch on origin, but if I run
>
> 	git branch --prune-merged origin

Sorry, this was my bad.  I agree that it is not a useful thing for
"origin" to mean "origin/*" here when it is established that
"origin" to mean "origin/HEAD".

^ permalink raw reply

* Re: [PATCH v11] checkout: extend --track with a "fetch" mode to refresh start-point
From: Junio C Hamano @ 2026-05-21 12:58 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Harald Nordgren via GitGitGadget, git, Ramsay Jones,
	D. Ben Knoble, Kristoffer Haugsbakk, Marc Branchaud,
	Harald Nordgren
In-Reply-To: <b8932b27-8006-4b43-b7e5-1fac0fbf42c7@gmail.com>

Phillip Wood <phillip.wood123@gmail.com> writes:

>> One.  Have you considered the case where the remote-tracking refs
>> are overlapping, e.g., where "origin" and "upstream" point at
>> different URLs but they both store in "refs/remotes/upstream/*"?
>> Perhaps their URLs may textually be different but are pointing
>> logically at the same place (e.g., one ssh:// the other https:// for
>> example).
>> 
>> What should happen?  What does happen after you apply this patch?
>
> It would be worth looking at what "git checkout --track" does in that 
> case and seeing if we can share the code.

It always is a good idea to think how we can share code for
different purposes to solve a new problem, but in this particular
one, I am not sure if "git checkout -t -b topic upstream/main"
codepath has much to offer to solve what the new "before the
checkout, update from the remote" feature wants to do.  To the
former, it does not matter how refs/remotes/upstream/* are updated
and by fetching which remote at all.  The only thing it cares about
is to leave the record that this new "topic" branch works with
refs/remotes/upstrea/main.  But the latter needs to be able to
compute which remote it should fetch from.  It is a problem that
existing code had no need to solve.


^ permalink raw reply

* Re: [PATCH v9 3/5] branch: add --prune-merged <remote>
From: Harald Nordgren @ 2026-05-21 12:37 UTC (permalink / raw)
  To: phillip.wood
  Cc: Harald Nordgren via GitGitGadget, git, Kristoffer Haugsbakk,
	Johannes Sixt
In-Reply-To: <6501a3d5-a5ec-421b-8526-ee7d4ae5ea98@gmail.com>

> I think being able to prune branches that have been merged into their
> upstream is a good idea. However, I find the focus on remotes rather
> than upstream branches in the UI a bit confusing. While the upstream of
> a branch is often a remote tracking branch it doesn't have to be. For my
> personal projects I often do
>
>         git checkout -b topic master
>
> and it would be nice to be able to run
>
>         git branch --prune-merged master
>
> to clean up those topics that have been merged. Similarly I think it is
> confusing that
>
>         git checkout -b topic origin
>
> starts a branch from the default branch on origin, but if I run
>
>         git branch --prune-merged origin
>
> to clean it up, it will clean up all the branches with an upstream on
> origin, not just those whose upstream matches origin/HEAD.
>
> So I like the idea, but would prefer the arguments to --prune-merged to
> be upstream branches, not remotes. We could support globs so that
>
>         git branch --prune-merges 'origin/*'
>
> would clean up all the branches whose upstream is on origin if that is
> useful.
>
> Thanks
>
> Phillip

Hi Phillip!

This seems like a big change. It almost becomes a different feature.
Would be interesting to hear what others have to say as well.


Harald

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox