Git development
 help / color / mirror / Atom feed
* [PATCH 3/3] daemon: guard NULL REMOTE_PORT in execute() logging
From: Sebastien Tardif via GitGitGadget @ 2026-05-14 15:46 UTC (permalink / raw)
  To: git; +Cc: Sebastien Tardif, Sebastien Tardif
In-Reply-To: <pull.2300.git.git.1778773592.gitgitgadget@gmail.com>

From: Sebastien Tardif <sebtardif@ncf.ca>

The REMOTE_PORT environment variable is used in a format string
without a NULL check, while REMOTE_ADDR is checked. If REMOTE_PORT
is unset, NULL is passed to printf's %s, which is undefined behavior.

Add a fallback string for the NULL case.

Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
---
 daemon.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/daemon.c b/daemon.c
index 103c08d868..78cca8673f 100644
--- a/daemon.c
+++ b/daemon.c
@@ -753,7 +753,7 @@ static int execute(void)
 	struct strvec env = STRVEC_INIT;
 
 	if (addr)
-		loginfo("Connection from %s:%s", addr, port);
+		loginfo("Connection from %s:%s", addr, port ? port : "?");
 
 	set_keep_alive(0);
 	alarm(init_timeout ? init_timeout : timeout);
-- 
gitgitgadget

^ permalink raw reply related

* [PATCH 2/3] daemon: fix IPv6 address truncation in ip2str()
From: Sebastien Tardif via GitGitGadget @ 2026-05-14 15:46 UTC (permalink / raw)
  To: git; +Cc: Sebastien Tardif, Sebastien Tardif
In-Reply-To: <pull.2300.git.git.1778773592.gitgitgadget@gmail.com>

From: Sebastien Tardif <sebtardif@ncf.ca>

The sockaddr struct size (ai_addrlen) is passed as the output buffer
size to inet_ntop(). For IPv6, sizeof(sockaddr_in6) is 28 bytes but
INET6_ADDRSTRLEN is 46, so long IPv6 addresses are silently truncated.

Fix this by passing sizeof(ip) instead, which is the actual size of
the destination buffer. Drop the now-unused len parameter from
ip2str() and update all callers.

Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
---
 daemon.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/daemon.c b/daemon.c
index 80fa0226d8..103c08d868 100644
--- a/daemon.c
+++ b/daemon.c
@@ -947,7 +947,7 @@ struct socketlist {
 	size_t alloc;
 };
 
-static const char *ip2str(int family, struct sockaddr *sin, socklen_t len)
+static const char *ip2str(int family, struct sockaddr *sin)
 {
 #ifdef NO_IPV6
 	static char ip[INET_ADDRSTRLEN];
@@ -958,11 +958,11 @@ static const char *ip2str(int family, struct sockaddr *sin, socklen_t len)
 	switch (family) {
 #ifndef NO_IPV6
 	case AF_INET6:
-		inet_ntop(family, &((struct sockaddr_in6*)sin)->sin6_addr, ip, len);
+		inet_ntop(family, &((struct sockaddr_in6*)sin)->sin6_addr, ip, sizeof(ip));
 		break;
 #endif
 	case AF_INET:
-		inet_ntop(family, &((struct sockaddr_in*)sin)->sin_addr, ip, len);
+		inet_ntop(family, &((struct sockaddr_in*)sin)->sin_addr, ip, sizeof(ip));
 		break;
 	default:
 		xsnprintf(ip, sizeof(ip), "<unknown>");
@@ -1019,14 +1019,14 @@ static int setup_named_sock(char *listen_addr, int listen_port, struct socketlis
 
 		if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
 			logerror("Could not bind to %s: %s",
-				 ip2str(ai->ai_family, ai->ai_addr, ai->ai_addrlen),
+				 ip2str(ai->ai_family, ai->ai_addr),
 				 strerror(errno));
 			close(sockfd);
 			continue;	/* not fatal */
 		}
 		if (listen(sockfd, 5) < 0) {
 			logerror("Could not listen to %s: %s",
-				 ip2str(ai->ai_family, ai->ai_addr, ai->ai_addrlen),
+				 ip2str(ai->ai_family, ai->ai_addr),
 				 strerror(errno));
 			close(sockfd);
 			continue;	/* not fatal */
@@ -1080,7 +1080,7 @@ static int setup_named_sock(char *listen_addr, int listen_port, struct socketlis
 
 	if ( bind(sockfd, (struct sockaddr *)&sin, sizeof sin) < 0 ) {
 		logerror("Could not bind to %s: %s",
-			 ip2str(AF_INET, (struct sockaddr *)&sin, sizeof(sin)),
+			 ip2str(AF_INET, (struct sockaddr *)&sin),
 			 strerror(errno));
 		close(sockfd);
 		return 0;
@@ -1088,7 +1088,7 @@ static int setup_named_sock(char *listen_addr, int listen_port, struct socketlis
 
 	if (listen(sockfd, 5) < 0) {
 		logerror("Could not listen to %s: %s",
-			 ip2str(AF_INET, (struct sockaddr *)&sin, sizeof(sin)),
+			 ip2str(AF_INET, (struct sockaddr *)&sin),
 			 strerror(errno));
 		close(sockfd);
 		return 0;
-- 
gitgitgadget


^ permalink raw reply related

* [PATCH 1/3] daemon: fix IPv6 address corruption in lookup_hostname()
From: Sebastien Tardif via GitGitGadget @ 2026-05-14 15:46 UTC (permalink / raw)
  To: git; +Cc: Sebastien Tardif, Sebastien Tardif
In-Reply-To: <pull.2300.git.git.1778773592.gitgitgadget@gmail.com>

From: Sebastien Tardif <sebtardif@ncf.ca>

getaddrinfo() is called with AF_UNSPEC hints, so it may return IPv6
results. However, the code unconditionally casts ai_addr to
sockaddr_in and passes AF_INET to inet_ntop(). On IPv6-only hosts,
this reads from the wrong struct offset, producing garbage IP
addresses.

Fix this by checking ai_family and extracting the address pointer
into a local variable before calling inet_ntop() once with the
correct family. Die on unexpected address families.

Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
---
 daemon.c | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/daemon.c b/daemon.c
index 0a7b1aae44..80fa0226d8 100644
--- a/daemon.c
+++ b/daemon.c
@@ -674,9 +674,20 @@ static void lookup_hostname(struct hostinfo *hi)
 
 		gai = getaddrinfo(hi->hostname.buf, NULL, &hints, &ai);
 		if (!gai) {
-			struct sockaddr_in *sin_addr = (void *)ai->ai_addr;
+			void *addr;
+
+			if (ai->ai_family == AF_INET) {
+				struct sockaddr_in *sa = (void *)ai->ai_addr;
+				addr = &sa->sin_addr;
+			} else if (ai->ai_family == AF_INET6) {
+				struct sockaddr_in6 *sa6 = (void *)ai->ai_addr;
+				addr = &sa6->sin6_addr;
+			} else {
+				die("unexpected address family: %d",
+				    ai->ai_family);
+			}
 
-			inet_ntop(AF_INET, &sin_addr->sin_addr,
+			inet_ntop(ai->ai_family, addr,
 				  addrbuf, sizeof(addrbuf));
 			strbuf_addstr(&hi->ip_address, addrbuf);
 
-- 
gitgitgadget


^ permalink raw reply related

* [PATCH 0/3] daemon: fix network address handling bugs
From: Sebastien Tardif via GitGitGadget @ 2026-05-14 15:46 UTC (permalink / raw)
  To: git; +Cc: Sebastien Tardif

Fix three related issues in daemon.c's network address handling:

IPv6 address corruption in lookup_hostname(): getaddrinfo() is called with
AF_UNSPEC hints, so it may return IPv6 results. However, the code
unconditionally casts ai_addr to sockaddr_in and passes AF_INET to
inet_ntop(). On IPv6-only hosts, this reads from the wrong struct offset,
producing garbage IP addresses. Fixed by checking ai_family and handling
both AF_INET and AF_INET6.

IPv6 address truncation in ip2str(): The sockaddr struct size (ai_addrlen)
is passed as the output buffer size to inet_ntop(). For IPv6,
sizeof(sockaddr_in6) is 28 bytes but INET6_ADDRSTRLEN is 46, so long IPv6
addresses are silently truncated. Fixed by passing sizeof(ip) instead, and
dropping the now-unused len parameter.

NULL pointer in execute() logging: REMOTE_PORT environment variable is used
in a format string without a NULL check (only REMOTE_ADDR was checked). If
REMOTE_PORT is unset, NULL is passed to printf's %s, which is undefined
behavior. Fixed by using a fallback string.

Sebastien Tardif (3):
  daemon: fix IPv6 address corruption in lookup_hostname()
  daemon: fix IPv6 address truncation in ip2str()
  daemon: guard NULL REMOTE_PORT in execute() logging

 daemon.c | 31 +++++++++++++++++++++----------
 1 file changed, 21 insertions(+), 10 deletions(-)


base-commit: 59ff4886a579f4bc91e976fe18590b9ae02c7a08
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2300%2FSebTardif%2Ffix%2Fdaemon-ipv6-and-null-port-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2300/SebTardif/fix/daemon-ipv6-and-null-port-v1
Pull-Request: https://github.com/git/git/pull/2300
-- 
gitgitgadget

^ permalink raw reply

* Re: [PATCH] git-jump: pick a mode automatically when invoked without arguments
From: Erik Cervin Edin @ 2026-05-14 15:40 UTC (permalink / raw)
  To: git
In-Reply-To: <20260508175240.GA737125@coredump.intra.peff.net>

On 26/05/08 04:30PM, Greg Hurrell wrote:
> Hopefully, they at least read the README before installing it from contrib/
> (although Homebrew recently starting installing it for folks automatically,
> so may not remain true for much longer on macOS...)

Oh! I didn't know that but looking closer on my homebrew installed Git,
looks like you're right.

I would've assumed that if users had jumped through the hoops of
installing git-jump from contrib/ it stands to reason they would have
some sense of what it does. But if this is landing as a part of regular
brew install git, I'd wager there's a few unsuspecting people running
git-jump that don't know what it does.

> Would having "git jump auto" work for you?

Imo, this sounds ideal -- there's something odd about `git jump` picking
the subcommand heuristically. At least when I invoke git-jump I always
do so with a specific intent of _where_ I want to jump. Then again, I
never do a jump merge or a jump ws.

> Homebrew installing `git-jump` by default a few months ago[^2] broke
> this, because aliases can't shadow builtin commands.

But it looks like this doesn't work in this case. Even if you have
git-jump installed stand-alone (mine is in ~/bin/git-jump, in PATH,
before /opt/homebrew/bin.)

FWIW I alias jump to j, jump diff to jd and jump grep to jg. E.g.

    git jd # git jump diff

On 26/05/08 09:07AM, Greg Hurrell via GitGitGadget wrote:
> -usage: git jump [--stdout] <mode> [<args>]
> +usage: git jump [--stdout] [<mode>] [<args>]

The usage message makes <mode> optional but doesn't explain what
happens when you omit it. Seems worth documenting the auto-detect behavior
there too.

> 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 when they run `git jump` without arguments:
>
> 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`.

I can think of a third situation -- when there are staged changes flagged by
git diff --cached --check.

If we're going to teach git-jump how to be more clever about where to jump,
does it also make sense to bake `git jump ws` into this?

Also, if this is going to grow into a proper auto-detect heuristic, it
might be cleaner as a first-class mode rather than logic spliced into the
argument parser. Something like:

    mode_auto() {
        if test -n "$(git ls-files -u)"; then
            mode_merge "$@"
        elif ! git diff --quiet; then
            mode_diff "$@"
        elif ! git diff --cached --check >/dev/null 2>&1; then
            mode_ws --cached "$@"
        else
            return 0
        fi
    }

That way `git jump auto` works explicitly, bare `git jump` defaults
to it (just `set -- auto` when $# -lt 1), and the usage text can
document the heuristic. It also keeps the detection and dispatch in
one place in case someone wants to tweak the priority later.

All in all, I think an auto jumping mode could be genuinely useful.

^ permalink raw reply

* Re: [PATCH v3 2/4] approxidate: alias "today" to "now"
From: Junio C Hamano @ 2026-05-14 15:36 UTC (permalink / raw)
  To: Tuomas Ahola; +Cc: git, Jeff King
In-Reply-To: <20260514115520.6660-3-taahol@utu.fi>

Tuomas Ahola <taahol@utu.fi> writes:

>     Sorry, I don't know if I understood.  Does the patch change the behavior of
>     that command somehow?  Is there some kind of edge case I missed?

No, I did not think it was a good idea to carve the behaviour in
stone that "git log --since=today" behaves as if it were given "git
log --since=now".  My reaction would have been very different if we
were deliberatly and explicitly saying "today is synonym for now",
but the thing is, it is not a designed behaviour but what
approxidate does for anything it does not understand, e.g.

    git log --since=decay
    git log --since=bogus

all behave as if it were given --since=now.

^ permalink raw reply

* Re: [GSoC RFC PATCH 0/1] graph: add indentation for commits preceded by a root
From: Phillip Wood @ 2026-05-14 15:15 UTC (permalink / raw)
  To: Pablo Sabater, git
  Cc: gitster, christian.couder, karthik.188, jltobler, ayu.chandekar,
	siddharthasthana31, chandrapratap3519
In-Reply-To: <20260402211717.3604688-1-pabloosabaterr@gmail.com>

Hi Pablo

On 02/04/2026 22:17, Pablo Sabater wrote:
> When having a history with multiple root commits and drawing the history
> near the roots, the graphing engine renders the commit one below the other,
> seeming that they are related, which makes the graph confusing.
> 
> This issue was reported by Junio at:
>    https://lore.kernel.org/git/xmqqikaawrpx.fsf@gitster.g/
> 
> e.g.:
> 
>    * root-B
>    * child-A2
>    * child-A1
>    * root-A
> 
> [...]
 >
>    * root-B
>      * child-A2
>     /
>    * child-A1
>    * root-A

I'm rather late to the party here, but personally I find the indentation 
a bit confusing, it would be clearer to me if we had a blank line after 
a root commit

     * root-B

     * child-A2
     * child-A1
     * root-A

It takes the same amount of vertical space but keeps the children of 
root-A together.

Thanks

Phillip

> This is done by adding a is_placeholder flag to the columns, the root commit
> is actually there but marked as a placeholder
> 
> e.g.:
> 
>     * root-B
>    (B) * child-A2
>      /
>     * child-A1
>     * root-A
> 
> (B) would be root-B column with the placeholder flag active.
> 
> Then teaching the rendering function to print a padding ' ' when meeting a
> placeholder column outputs the second example.
> 
> There could also be the case where there are multiple roots
> 
> without the patch:
> 
>    * A root
>    * B root
>    * C root
>    * D1 child
>    * D root
> 
> with the patch, the indentation cascades:
> 
>    * A root
>      * B root
>        * C root
>          * D1 child
>       _ /
>      /
>     /
>    * D root
> 
> the _ / might look weird but that's how the collapsing rendering does it
> for big gaps, this case being from the 4th column to the 0th column.
> Another patch could change the collapsing rendering for placeholders ?
> I haven't done it to keep it minimal, but a follow up could make it
> to be straight '/'. This would make it bigger but easier for the eye to follow.
> IMO is not worth it, but opinions are welcome.
> 
> The patch also adds tests for different cases like a root preceding multiple
> parents merges and the examples above.
> 
> There could be some edge cases still so any testing is very welcome.
> 
> Pablo Sabater (1):
>    graph: add indentation for commits preceded by a root
> 
>   graph.c                      |  68 ++++++++++++++++--
>   t/t4215-log-skewed-merges.sh | 136 +++++++++++++++++++++++++++++++++++
>   2 files changed, 198 insertions(+), 6 deletions(-)
> 
> 
> base-commit: 256554692df0685b45e60778b08802b720880c50


^ permalink raw reply

* [PATCH 2/2] use __builtin_add_overflow() in st_add() with Clang
From: René Scharfe @ 2026-05-14 15:13 UTC (permalink / raw)
  To: Git List; +Cc: Jeff King
In-Reply-To: <c6e9b337-c4fc-4cbd-ac32-e8d3814749b0@web.de>

Clang and GCC optimize away comparisons of overflow checks by checking
the carry flag on x64.  GCC does the same on ARM64, but Clang currently
(version 22.1) doesn't.

Provide a variant of st_add() that wraps __builtin_add_overflow() to
help Clang optimize it.  Use it on all platforms for simplicity.

On an Apple M1 I get a nice speedup for a command that builds lots of
strings using a strbuf, which exercises the st_add3() in strbuf_grow()
for every line of output:

Benchmark 1: ./git_main cat-file --batch-all-objects --batch-check='%(objectname)'
  Time (mean ± σ):     119.8 ms ±   0.2 ms    [User: 113.0 ms, System: 5.8 ms]
  Range (min … max):   119.6 ms … 120.4 ms    24 runs

Benchmark 2: ./git cat-file --batch-all-objects --batch-check='%(objectname)'
  Time (mean ± σ):     114.6 ms ±   0.1 ms    [User: 107.6 ms, System: 6.0 ms]
  Range (min … max):   114.4 ms … 114.9 ms    25 runs

Summary
  ./git cat-file --batch-all-objects --batch-check='%(objectname)' ran
    1.05 ± 0.00 times faster than ./git_main cat-file --batch-all-objects --batch-check='%(objectname)'

Suggested-by: Jeff King <peff@peff.net>
Signed-off-by: René Scharfe <l.s.r@web.de>
---
 git-compat-util.h | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/git-compat-util.h b/git-compat-util.h
index ae1bdc90a4..aa088d04bb 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -614,6 +614,17 @@ static inline bool strip_suffix(const char *str, const char *suffix,
 int git_open_cloexec(const char *name, int flags);
 #define git_open(name) git_open_cloexec(name, O_RDONLY)
 
+/* Help Clang; GCC generates the same code for both variants. */
+#if defined(__clang__)
+static inline size_t st_add(size_t a, size_t b)
+{
+	size_t sum;
+	if (__builtin_add_overflow(a, b, &sum))
+		die("size_t overflow: %"PRIuMAX" + %"PRIuMAX,
+		    (uintmax_t)a, (uintmax_t)b);
+	return sum;
+}
+#else
 static inline size_t st_add(size_t a, size_t b)
 {
 	if (unsigned_add_overflows(a, b))
@@ -621,6 +632,7 @@ static inline size_t st_add(size_t a, size_t b)
 		    (uintmax_t)a, (uintmax_t)b);
 	return a + b;
 }
+#endif
 #define st_add3(a,b,c)   st_add(st_add((a),(b)),(c))
 #define st_add4(a,b,c,d) st_add(st_add3((a),(b),(c)),(d))
 
-- 
2.54.0

^ permalink raw reply related

* [PATCH 1/2] strbuf: use st_add3() in strbuf_grow()
From: René Scharfe @ 2026-05-14 15:11 UTC (permalink / raw)
  To: Git List

Simplify the code by calling st_add3() to do overflow checks instead of
open-coding it.  This changes the error message to include the offending
summands, which can be helpful when tracking down the cause.

Signed-off-by: René Scharfe <l.s.r@web.de>
---
 strbuf.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/strbuf.c b/strbuf.c
index 3e04addc22..bb04d3910e 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -106,12 +106,9 @@ void strbuf_attach(struct strbuf *sb, void *buf, size_t len, size_t alloc)
 void strbuf_grow(struct strbuf *sb, size_t extra)
 {
 	int new_buf = !sb->alloc;
-	if (unsigned_add_overflows(extra, 1) ||
-	    unsigned_add_overflows(sb->len, extra + 1))
-		die("you want to use way too much memory");
 	if (new_buf)
 		sb->buf = NULL;
-	ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc);
+	ALLOC_GROW(sb->buf, st_add3(sb->len, extra, 1), sb->alloc);
 	if (new_buf)
 		sb->buf[0] = '\0';
 }
-- 
2.54.0

^ permalink raw reply related

* [PATCH v1 11/11] git-gui: add gui and pick as explicit subcommands
From: Mark Levedahl @ 2026-05-14 14:33 UTC (permalink / raw)
  To: git; +Cc: egg_mushroomcow, j6t, bootaina702, Mark Levedahl
In-Reply-To: <20260514143322.865587-1-mlevedahl@gmail.com>

git-gui accepts subcommands blame | browser | citool, and assumes the
subcommand is 'gui' if none is actually given, But, git gui also has a
repository picker (choose_repository::pick) that can create a new
repository + worktree, or choose an existing one, switch to that, and
the run the gui. The user has no direct control over invoking the
picker, instead the picker is triggered by failure in the repository /
worktree discover process: this includes being started in a directory
not controlled by git, which is probably the intended use case.

The picker can appear when the user has no intention of creating a new
worktree, and the user cannot use the picker to create a new worktree
inside another.

So, add two new explicit subcommands:
    gui  - Run the gui if repository/worktree discovery succeeds, or die
           with an error message, but never run the picker.
    pick - First run the picker, regardless, then start the gui in
           the chosen worktree.

Nothing in this changes the prior behavior, the alternates above must be
explicitly selected to see any change.

Signed-off-by: Mark Levedahl <mlevedahl@gmail.com>
---
 git-gui.sh | 22 ++++++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/git-gui.sh b/git-gui.sh
index 3a83dd5..c56aeef 100755
--- a/git-gui.sh
+++ b/git-gui.sh
@@ -1021,6 +1021,7 @@ proc load_config {include_global} {
 ##
 ## feature option selection
 
+set run_picker_on_error 1
 if {[regexp {^git-(.+)$} [file tail $argv0] _junk subcommand]} {
 	unset _junk
 } else {
@@ -1030,6 +1031,7 @@ if {$subcommand eq {gui.sh}} {
 	set subcommand gui
 }
 if {$subcommand eq {gui} && [llength $argv] > 0} {
+	set run_picker_on_error 0
 	set subcommand [lindex $argv 0]
 	set argv [lrange $argv 1 end]
 }
@@ -1047,6 +1049,7 @@ blame {
 	disable_option multicommit
 	disable_option branch
 	disable_option transport
+	set run_picker_on_error 0
 }
 citool {
 	enable_option singlecommit
@@ -1055,6 +1058,7 @@ citool {
 	disable_option multicommit
 	disable_option branch
 	disable_option transport
+	set run_picker_on_error 0
 
 	while {[llength $argv] > 0} {
 		set a [lindex $argv 0]
@@ -1162,14 +1166,28 @@ proc pick_repo {} {
 	set picked 1
 }
 
+# run repository picker if explicitly requested
+switch -- $subcommand {
+	pick {
+		pick_repo
+		set subcommand gui
+		set run_picker_on_error 0
+	}
+}
+
 # find repository.
 if {[catch {
 	set _gitdir [git rev-parse --absolute-git-dir]
 } err]} {
 	if {[is_gitvars_error $err]} {
 		exit 1
-	} else {
+	}
+	if {$run_picker_on_error} {
 		pick_repo
+	} else {
+		catch {wm withdraw .}
+		error_popup [strcat [mc "Git directory not found:"] "\n\n$err"]
+		exit 1
 	}
 }
 
@@ -3051,7 +3069,7 @@ gui {
 	# fall through to setup UI for commits
 }
 default {
-	set err "[mc usage:] $argv0 \[{blame|browser|citool}\]"
+	set err "[mc usage:] $argv0 \[{blame|browser|citool|gui|pick}\]"
 	if {[tk windowingsystem] eq "win32"} {
 		wm withdraw .
 		tk_messageBox -icon error -message $err \
-- 
2.54.0.99.14


^ permalink raw reply related

* [PATCH v1 10/11] git-gui: improve worktree discovery
From: Mark Levedahl @ 2026-05-14 14:33 UTC (permalink / raw)
  To: git; +Cc: egg_mushroomcow, j6t, bootaina702, Mark Levedahl
In-Reply-To: <20260514143322.865587-1-mlevedahl@gmail.com>

git gui's worktree discovery needs update based upon prior work in this
series. In the normal case, all information we need comes directly from
git rev-parse (--show-toplevel, and --show-prefix). Should this work, we
have a valid worktree and all git gui commands can run.

If not, we need to consider:
- if GIT_DIR or GIT_WORK_TREE are in the environment, just stop as we
  the input configuration was wrong, the user must fix that.
- if we have a browser or blame subcommand, no worktree is needed so
  git-gui can run without.
- using the git repository's parent is a valid worktree (if possible),
  restoring prior behavior.

The current directory should be either the root of the worktree, if one
is found, or the top-level of the git repository.

Make it so. Also, make worktree discover directly follow repository
discovery, reducing the locations that might need error trapping to
catch configuration issues.

Signed-off-by: Mark Levedahl <mlevedahl@gmail.com>
---
 git-gui.sh | 56 ++++++++++++++++++++++--------------------------------
 1 file changed, 23 insertions(+), 33 deletions(-)

diff --git a/git-gui.sh b/git-gui.sh
index e326401..3a83dd5 100755
--- a/git-gui.sh
+++ b/git-gui.sh
@@ -1173,6 +1173,28 @@ if {[catch {
 	}
 }
 
+# find worktree, continue without if not required
+if {[catch {
+	set _gitworktree [git rev-parse --show-toplevel]
+	set _prefix [git rev-parse --show-prefix]
+	cd $_gitworktree
+} err]} {
+	if {[is_gitvars_error $err]} {
+		exit 1
+	}
+	set _gitworktree {}
+	set _prefix {}
+	if {[is_enabled bare]} {
+		cd $_gitdir
+	} elseif {![is_parent_worktree]} {
+		catch {wm withdraw .}
+		error_popup [strcat [mc "Cannot use bare repository:"] "\n\n" $_gitdir]
+		exit 1
+	}
+}
+
+# repository and worktree config are complete, export them
+set_gitdir_vars
 
 # Use object format as hash algorithm (either "sha1" or "sha256")
 set hashalgorithm [git rev-parse --show-object-format]
@@ -1189,37 +1211,8 @@ if {$hashalgorithm eq "sha1"} {
 load_config 0
 apply_config
 
-set _gitworktree [git rev-parse --show-toplevel]
 
-if {$_prefix ne {}} {
-	if {$_gitworktree eq {}} {
-		regsub -all {[^/]+/} $_prefix ../ cdup
-	} else {
-		set cdup $_gitworktree
-	}
-	if {[catch {cd $cdup} err]} {
-		catch {wm withdraw .}
-		error_popup [strcat [mc "Cannot move to top of working directory:"] "\n\n$err"]
-		exit 1
-	}
-	set _gitworktree [pwd]
-	unset cdup
-} elseif {![is_enabled bare]} {
-	if {[is_bare]} {
-		catch {wm withdraw .}
-		error_popup [strcat [mc "Cannot use bare repository:"] "\n\n$_gitdir"]
-		exit 1
-	}
-	if {$_gitworktree eq {}} {
-		set _gitworktree [file dirname $_gitdir]
-	}
-	if {[catch {cd $_gitworktree} err]} {
-		catch {wm withdraw .}
-		error_popup [strcat [mc "No working directory"] " $_gitworktree:\n\n$err"]
-		exit 1
-	}
-	set _gitworktree [pwd]
-}
+# Derive a human-readable repository name
 set _reponame [file split [file normalize $_gitdir]]
 if {[lindex $_reponame end] eq {.git}} {
 	set _reponame [lindex $_reponame end-1]
@@ -1227,9 +1220,6 @@ if {[lindex $_reponame end] eq {.git}} {
 	set _reponame [lindex $_reponame end]
 }
 
-# Export the final paths
-set_gitdir_vars
-
 ######################################################################
 ##
 ## global init
-- 
2.54.0.99.14


^ permalink raw reply related

* [PATCH v1 09/11] git-gui: support using repository parent dir as a worktree
From: Mark Levedahl @ 2026-05-14 14:33 UTC (permalink / raw)
  To: git; +Cc: egg_mushroomcow, j6t, bootaina702, Mark Levedahl
In-Reply-To: <20260514143322.865587-1-mlevedahl@gmail.com>

git-gui, since 87cd09f43e ("git-gui: work from the .git dir",
2010-01-23), has had the intent to allow starting from inside a
repository, then switching to the parent directory if that is a valid
worktree.

This certainly hasn't worked since 2d92ab32fd ("rev-parse: make
--show-toplevel without a worktree an error", 2019-11-19) in git, but
breaking this git-gui feature was unintentional.

Add a proc to test if the parent of the git repository is a valid
worktree, and set that directory as the worktree if so. Use invocations
of git rev-parse to assure all validity and safety checks included in
git-core are executed.
---
 git-gui.sh | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/git-gui.sh b/git-gui.sh
index a03eaa7..e326401 100755
--- a/git-gui.sh
+++ b/git-gui.sh
@@ -1100,6 +1100,23 @@ unset argv0dir
 ##
 ## repository setup
 
+proc is_parent_worktree {} {
+	# Directory 'parent' of a repository named 'parent/.git' might be the worktree
+	set ok 0
+	if {[file tail $::_gitdir] eq {.git}} {
+		set gitdir_parent [file join $::_gitdir {..}]
+		set expected_worktree [file normalize $gitdir_parent]
+		catch {set git_worktree [git -C $gitdir_parent rev-parse --show-toplevel]}
+		if {[string compare $expected_worktree $git_worktree] == 0} {
+			set ::_prefix {}
+			set ::_gitworktree $git_worktree
+			cd $git_worktree
+			set ok 1
+		}
+	}
+	return $ok
+}
+
 proc is_gitvars_error {err} {
 	set havevars 0
 	set GIT_DIR {}
-- 
2.54.0.99.14


^ permalink raw reply related

* [PATCH v1 08/11] git-gui: simplify [is_bare] to report if a worktree is known
From: Mark Levedahl @ 2026-05-14 14:33 UTC (permalink / raw)
  To: git; +Cc: egg_mushroomcow, j6t, bootaina702, Mark Levedahl
In-Reply-To: <20260514143322.865587-1-mlevedahl@gmail.com>

git-gui includes proc is_bare, used in several places to make decisions
on whether a worktree exists, but also in discovery to tell if a
worktree can be supported.

But, is_bare is out of date with regard to multiple worktrees, safe
repository guards, and possibly other relevant features known to git
rev-parse. Also, is_bare caches its result on the first call, so is not
useful if a later step in the discovery process finds a worktree.

So, simplify is_bare to report whether git-gui has a worktree or is
working only from a repository.

Signed-off-by: Mark Levedahl <mlevedahl@gmail.com>
---
 git-gui.sh | 25 +------------------------
 1 file changed, 1 insertion(+), 24 deletions(-)

diff --git a/git-gui.sh b/git-gui.sh
index 81789dd..a03eaa7 100755
--- a/git-gui.sh
+++ b/git-gui.sh
@@ -372,7 +372,6 @@ if {[tk windowingsystem] eq "aqua"} {
 set _appname {Git Gui}
 set _gitdir {}
 set _gitworktree {}
-set _isbare {}
 set _githtmldir {}
 set _prefix {}
 set _reponame {}
@@ -524,29 +523,7 @@ proc get_config {name} {
 }
 
 proc is_bare {} {
-	global _isbare
-	global _gitdir
-	global _gitworktree
-
-	if {$_isbare eq {}} {
-		if {[catch {
-			set _bare [git rev-parse --is-bare-repository]
-			switch  -- $_bare {
-			true { set _isbare 1 }
-			false { set _isbare 0}
-			default { throw }
-			}
-		}]} {
-			if {[is_config_true core.bare]
-				|| ($_gitworktree eq {}
-					&& [lindex [file split $_gitdir] end] ne {.git})} {
-				set _isbare 1
-			} else {
-				set _isbare 0
-			}
-		}
-	}
-	return $_isbare
+	return [expr {$::_gitworktree eq {}}]
 }
 
 ######################################################################
-- 
2.54.0.99.14


^ permalink raw reply related

* [PATCH v1 07/11] git-gui: use rev-parse exclusively to find a repository
From: Mark Levedahl @ 2026-05-14 14:33 UTC (permalink / raw)
  To: git; +Cc: egg_mushroomcow, j6t, bootaina702, Mark Levedahl
In-Reply-To: <20260514143322.865587-1-mlevedahl@gmail.com>

git-gui attempts to use env(GIT_DIR) directly as the git repository,
accepting GIT_DIR if it is a directory. Only if that fails is git
rev-parse used to discover the repository.  But, this avoids all of
git-core's validity checking on a repository, thus possibly deferring an
error to a later step, possibly unexpected. Repository validation should
be part of initial setup so that later processing does not need error
trapping for configuration errors.

Let's just invoke rev-parse so all error checking is done. Stop here if
the user set GIT_DIR or GIT_WORK_TREE. Otherwise, continue the existing
behavior and show the repository picker.

Also, remove a later check on whether _gitdir is a directory: that code
cannot be reached without rev-parse having validating the repository.

Signed-off-by: Mark Levedahl <mlevedahl@gmail.com>
---
 git-gui.sh | 24 +++++++++---------------
 1 file changed, 9 insertions(+), 15 deletions(-)

diff --git a/git-gui.sh b/git-gui.sh
index 2e2ddc0..81789dd 100755
--- a/git-gui.sh
+++ b/git-gui.sh
@@ -374,6 +374,7 @@ set _gitdir {}
 set _gitworktree {}
 set _isbare {}
 set _githtmldir {}
+set _prefix {}
 set _reponame {}
 set _shellpath {@@SHELL_PATH@@}
 
@@ -1167,19 +1168,18 @@ proc pick_repo {} {
 	set picked 1
 }
 
+# find repository.
 if {[catch {
-		set _gitdir $env(GIT_DIR)
-		set _prefix {}
-		}]
-	&& [catch {
-		# beware that from the .git dir this sets _gitdir to .
-		# and _prefix to the empty string
-		set _gitdir [git rev-parse --absolute-git-dir]
-		set _prefix [git rev-parse --show-prefix]
-	} err]} {
+	set _gitdir [git rev-parse --absolute-git-dir]
+} err]} {
+	if {[is_gitvars_error $err]} {
+		exit 1
+	} else {
 		pick_repo
+	}
 }
 
+
 # Use object format as hash algorithm (either "sha1" or "sha256")
 set hashalgorithm [git rev-parse --show-object-format]
 if {$hashalgorithm eq "sha1"} {
@@ -1191,12 +1191,6 @@ if {$hashalgorithm eq "sha1"} {
 	exit 1
 }
 
-if {![file isdirectory $_gitdir]} {
-	catch {wm withdraw .}
-	error_popup [strcat [mc "Git directory not found:"] "\n\n$_gitdir"]
-	exit 1
-}
-
 # _gitdir exists, so try loading the config
 load_config 0
 apply_config
-- 
2.54.0.99.14


^ permalink raw reply related

* [PATCH v1 06/11] git gui: GIT_DIR / GIT_WORK_TREE make any discovery error fatal
From: Mark Levedahl @ 2026-05-14 14:33 UTC (permalink / raw)
  To: git; +Cc: egg_mushroomcow, j6t, bootaina702, Mark Levedahl
In-Reply-To: <20260514143322.865587-1-mlevedahl@gmail.com>

git accepts any combination of GIT_DIR and GIT_WORK_TREE to override the
normal repository and worktree discovery process.  git-gui should accept
any such valid configuration, but overriding the discovery process means
the user has assured that the combination of current directory, GIT_DIR,
and GIT_WORK_TREE will lead to the correct repository and worktree. As
such, an error found during discovery where either or both of GIT_DIR
and GIT_WORK_TREE are set is a fatal error, no further exploration
should be tried.

Provide a common proc to support displaying an error message and exiting
if GIT_DIR or GIT_WORK_TREE are in the environment.

Signed-off-by: Mark Levedahl <mlevedahl@gmail.com>
---
 git-gui.sh | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/git-gui.sh b/git-gui.sh
index c2cf5f1..2e2ddc0 100755
--- a/git-gui.sh
+++ b/git-gui.sh
@@ -1122,6 +1122,24 @@ unset argv0dir
 ##
 ## repository setup
 
+proc is_gitvars_error {err} {
+	set havevars 0
+	set GIT_DIR {}
+	set GIT_WORK_TREE {}
+	catch {set GIT_DIR $::env(GIT_DIR); set havevars 1}
+	catch {set GIT_WORK_TREE $::env(GIT_WORK_TREE) ; set havevars 1}
+
+	if {$havevars} {
+		catch {wm withdraw .}
+		error_popup [strcat [mc "Invalid configuration:"] \
+		   "\n" "GIT_DIR: " $GIT_DIR \
+		   "\n" "GIT_WORK_TREE: " $GIT_WORK_TREE \
+			"\n\n$err"]
+		return 1
+	}
+	return 0
+}
+
 proc set_gitdir_vars {} {
 	global _gitdir _gitworktree env
 	if {$_gitdir ne {}} {
-- 
2.54.0.99.14


^ permalink raw reply related

* [PATCH v1 03/11] git-gui: guard set/unset of GIT_DIR and GIT_WORK_TREE
From: Mark Levedahl @ 2026-05-14 14:33 UTC (permalink / raw)
  To: git; +Cc: egg_mushroomcow, j6t, bootaina702, Mark Levedahl
In-Reply-To: <20260514143322.865587-1-mlevedahl@gmail.com>

git-gui unconditionally exports GIT_DIR and GIT_WORK_TREE to the
environment, and furthmore unconditionally unsets these in many places.
But, GIT_WORK_TREE should be set only if it is not {} as the empty
value, really meaning no work-tree is found, causes git to throw fatal
errors (git-gui gets the error from branch --show-current).  Fixing this
is required to allow blame and browser to operate from a repository
without a worktree.

Establish a pair of functions to remove GIT_DIR and GIT_WORK_TREE from
the environment, avoiding any error if they do not exist. Also, add a
function to export these, but export GIT_WORK_TREE only if not empty.

Signed-off-by: Mark Levedahl <mlevedahl@gmail.com>
---
 git-gui.sh | 32 ++++++++++++++++++++++----------
 1 file changed, 22 insertions(+), 10 deletions(-)

diff --git a/git-gui.sh b/git-gui.sh
index a951fcd..387cad6 100755
--- a/git-gui.sh
+++ b/git-gui.sh
@@ -1122,6 +1122,22 @@ unset argv0dir
 ##
 ## repository setup
 
+proc set_gitdir_vars {} {
+	global _gitdir _gitworktree env
+	if {$_gitdir ne {}} {
+		set env(GIT_DIR) $_gitdir
+	}
+	if {$_gitworktree ne {}} {
+		set env(GIT_WORK_TREE) $_gitworktree
+	}
+}
+
+proc unset_gitdir_vars {} {
+	global env
+	catch {unset env(GIT_DIR)}
+	catch {unset env(GIT_WORK_TREE)}
+}
+
 set picked 0
 if {[catch {
 		set _gitdir $env(GIT_DIR)
@@ -1207,8 +1223,8 @@ if {[lindex $_reponame end] eq {.git}} {
 	set _reponame [lindex $_reponame end]
 }
 
-set env(GIT_DIR) $_gitdir
-set env(GIT_WORK_TREE) $_gitworktree
+# Export the final paths
+set_gitdir_vars
 
 ######################################################################
 ##
@@ -2050,13 +2066,11 @@ proc do_gitk {revs {is_submodule false}} {
 			# TODO we could make life easier (start up faster?) for gitk
 			# by setting these to the appropriate values to allow gitk
 			# to skip the heuristics to find their proper value
-			unset env(GIT_DIR)
-			unset env(GIT_WORK_TREE)
+			unset_gitdir_vars
 		}
 		safe_exec_bg [concat $cmd $revs "--" "--"]
 
-		set env(GIT_DIR) $_gitdir
-		set env(GIT_WORK_TREE) $_gitworktree
+		set_gitdir_vars
 		cd $pwd
 
 		if {[info exists main_status]} {
@@ -2084,16 +2098,14 @@ proc do_git_gui {} {
 
 		# see note in do_gitk about unsetting these vars when
 		# running tools in a submodule
-		unset env(GIT_DIR)
-		unset env(GIT_WORK_TREE)
+		unset_gitdir_vars
 
 		set pwd [pwd]
 		cd $current_diff_path
 
 		safe_exec_bg [concat $exe gui]
 
-		set env(GIT_DIR) $_gitdir
-		set env(GIT_WORK_TREE) $_gitworktree
+		set_gitdir_vars
 		cd $pwd
 
 		set status_operation [$::main_status \
-- 
2.54.0.99.14


^ permalink raw reply related

* [PATCH v1 05/11] git-gui: use --absolute-git-dir
From: Mark Levedahl @ 2026-05-14 14:33 UTC (permalink / raw)
  To: git; +Cc: egg_mushroomcow, j6t, bootaina702, Mark Levedahl
In-Reply-To: <20260514143322.865587-1-mlevedahl@gmail.com>

git-gui uses git rev-parse --git-dir to get the pathname of the
discovered git repository. The returned value can be relative, and is
'.' if the current directory is the top of the repository directory
itself.  git-gui has code to change '.' to [pwd] in this case so that
subsequent logic runs.

But, git rev-parse supports --absolute-git-dir from fac60b8925
("rev-parse: add option for absolute or relative path formatting",
2020-12-13), and included in git 2.31. git-gui requires git >= 2.36, so
this more useful form is always available. Use --absolute-git-dir to
always get an absolute path, avoiding the need for other checks.
---
 git-gui.sh | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/git-gui.sh b/git-gui.sh
index 0b73c35..c2cf5f1 100755
--- a/git-gui.sh
+++ b/git-gui.sh
@@ -1156,7 +1156,7 @@ if {[catch {
 	&& [catch {
 		# beware that from the .git dir this sets _gitdir to .
 		# and _prefix to the empty string
-		set _gitdir [git rev-parse --git-dir]
+		set _gitdir [git rev-parse --absolute-git-dir]
 		set _prefix [git rev-parse --show-prefix]
 	} err]} {
 		pick_repo
@@ -1173,18 +1173,12 @@ if {$hashalgorithm eq "sha1"} {
 	exit 1
 }
 
-# we expand the _gitdir when it's just a single dot (i.e. when we're being
-# run from the .git dir itself) lest the routines to find the worktree
-# get confused
-if {$_gitdir eq "."} {
-	set _gitdir [pwd]
-}
-
 if {![file isdirectory $_gitdir]} {
 	catch {wm withdraw .}
 	error_popup [strcat [mc "Git directory not found:"] "\n\n$_gitdir"]
 	exit 1
 }
+
 # _gitdir exists, so try loading the config
 load_config 0
 apply_config
-- 
2.54.0.99.14


^ permalink raw reply related

* [PATCH v1 04/11] git-gui: put choose_repository::pick in a proc
From: Mark Levedahl @ 2026-05-14 14:33 UTC (permalink / raw)
  To: git; +Cc: egg_mushroomcow, j6t, bootaina702, Mark Levedahl
In-Reply-To: <20260514143322.865587-1-mlevedahl@gmail.com>

git-gui includes a 'repository picker', which allows creating a new
repository + worktree, or selecting a worktree from a recent list.
git-gui runs the picker when a valid git repository is not found. All of
the code for this is embedded in the discovery process block, making the
latter more difficult to read, and also making things more difficult if
we want to have an explicit 'pick' subcommand to force this to run.

Let's move this invocation and supporting code to a separate proc,
aiding in subsequent refactoring. Assure GIT_DIR and GIT_WORK_TREE are
unset, configuration is loaded, ant that _gitdir is correctly set
afterwards. As this is invoked before worktree discovery, later code
will set that anyway so need not be included here.

Signed-off-by: Mark Levedahl <mlevedahl@gmail.com>
---
 git-gui.sh | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/git-gui.sh b/git-gui.sh
index 387cad6..0b73c35 100755
--- a/git-gui.sh
+++ b/git-gui.sh
@@ -1139,6 +1139,16 @@ proc unset_gitdir_vars {} {
 }
 
 set picked 0
+proc pick_repo {} {
+	unset_gitdir_vars
+	load_config 1
+	apply_config
+	choose_repository::pick
+	set _gitdir [git rev-parse --absolute-git-dir]
+	set _prefix {}
+	set picked 1
+}
+
 if {[catch {
 		set _gitdir $env(GIT_DIR)
 		set _prefix {}
@@ -1149,13 +1159,7 @@ if {[catch {
 		set _gitdir [git rev-parse --git-dir]
 		set _prefix [git rev-parse --show-prefix]
 	} err]} {
-	load_config 1
-	apply_config
-	choose_repository::pick
-	if {![file isdirectory $_gitdir]} {
-		exit 1
-	}
-	set picked 1
+		pick_repo
 }
 
 # Use object format as hash algorithm (either "sha1" or "sha256")
-- 
2.54.0.99.14


^ permalink raw reply related

* [PATCH v1 02/11] git-gui: refactor browser / blame argument parsing
From: Mark Levedahl @ 2026-05-14 14:33 UTC (permalink / raw)
  To: git; +Cc: egg_mushroomcow, j6t, bootaina702, Mark Levedahl
In-Reply-To: <20260514143322.865587-1-mlevedahl@gmail.com>

git-gui has subcommands blame and browser, both of which accept a
pathname, possibly preceded by a commit-ish item to specify a revision.
Also, blame can take a first argument that gives a line number to focus.

The command line parser for the above is more complex than needed, and
cannot work without a worktree as the pathname objects are checked
against the current worktree for existence. This also precludes naming a
directory or file that does not exist on the currently checked out
branch.

So, replace this with a simpler parser that looks at argument number and
number of arguments to know what value to expect. The blame and browser
backends already have error checking with diagnostic information, so
defer most error checking to those. Also, allow a line-number selection
to be given and silently ignored for the browser, further simplifying
this code.

Signed-off-by: Mark Levedahl <mlevedahl@gmail.com>
---
 git-gui.sh | 66 +++++++++++++-----------------------------------------
 1 file changed, 16 insertions(+), 50 deletions(-)

diff --git a/git-gui.sh b/git-gui.sh
index 6048f92..a951fcd 100755
--- a/git-gui.sh
+++ b/git-gui.sh
@@ -2986,51 +2986,34 @@ blame {
 	set head {}
 	set path {}
 	set jump_spec {}
-	set is_path 0
+	set nargs [llength $argv]
+	if {$nargs < 1} {
+		usage
+	}
+	set argn 0
 	foreach a $argv {
-		set p [file join $_prefix $a]
+		set argn [expr {$argn + 1}]
 
-		if {$is_path || [file exists $p]} {
-			if {$path ne {}} usage
-			set path [normalize_relpath $p]
-			break
-		} elseif {$a eq {--}} {
-			if {$path ne {}} {
-				if {$head ne {}} usage
-				set head $path
-				set path {}
+		if {$argn < $nargs} {
+			# revision or line number
+			if {[regexp {^--line=(\d+)$} $a a lnum]} {
+				set jump_spec [list $lnum]
+			} else {
+				set head $a
 			}
-			set is_path 1
-		} elseif {[regexp {^--line=(\d+)$} $a a lnum]} {
-			if {$jump_spec ne {} || $head ne {}} usage
-			set jump_spec [list $lnum]
-		} elseif {$head eq {}} {
-			if {$head ne {}} usage
-			set head $a
-			set is_path 1
-		} else {
-			usage
-		}
-	}
-	unset is_path
-
-	if {$head ne {} && $path eq {}} {
-		if {[string index $head 0] eq {/}} {
-			set path [normalize_relpath $head]
-			set head {}
 		} else {
-			set path [normalize_relpath $_prefix$head]
-			set head {}
+			set path [normalize_relpath $a]
 		}
 	}
 
 	if {$head eq {}} {
 		load_current_branch
+		set head $current_branch
 	} else {
 		if {[regexp [string map "@@ [expr $hashlength - 1]" {^[0-9a-f]{1,@@}$}] $head]} {
 			if {[catch {
-					set head [git rev-parse --verify $head]
-				} err]} {
+				set head [git rev-parse --verify $head]
+			} err]} {
 				if {[tk windowingsystem] eq "win32"} {
 					tk_messageBox -icon error -title [mc Error] -message $err
 				} else {
@@ -3046,26 +3029,9 @@ blame {
 	switch -- $subcommand {
 	browser {
 		if {$jump_spec ne {}} usage
-		if {$head eq {}} {
-			if {$path ne {} && [file isdirectory $path]} {
-				set head $current_branch
-			} else {
-				set head $path
-				set path {}
-			}
-		}
 		browser::new $head $path
 	}
 	blame   {
-		if {$head eq {} && ![file exists $path]} {
-			catch {wm withdraw .}
-			tk_messageBox \
-				-icon error \
-				-type ok \
-				-title [mc "git-gui: fatal error"] \
-				-message [mc "fatal: cannot stat path %s: No such file or directory" $path]
-			exit 1
-		}
 		blame::new $head $path $jump_spec
 	}
 	}
-- 
2.54.0.99.14


^ permalink raw reply related

* [PATCH v1 01/11] git-gui: allow specifying path '.' to the browser
From: Mark Levedahl @ 2026-05-14 14:33 UTC (permalink / raw)
  To: git; +Cc: egg_mushroomcow, j6t, bootaina702, Mark Levedahl
In-Reply-To: <20260514143322.865587-1-mlevedahl@gmail.com>

Invoking "git-gui browser rev ." should show the file browser for the
commitish rev, starting at the root directory. This errors out in
normalize_relpath because the '.' is removed, yielding an empty list as
argument to [file join ...]. Fix this.

Signed-off-by: Mark Levedahl <mlevedahl@gmail.com>
---
 git-gui.sh | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/git-gui.sh b/git-gui.sh
index 23fe76e..6048f92 100755
--- a/git-gui.sh
+++ b/git-gui.sh
@@ -2965,7 +2965,11 @@ proc normalize_relpath {path} {
 		}
 		lappend elements $item
 	}
-	return [eval file join $elements]
+	if {$elements ne {}} {
+		return [eval file join $elements]
+	} else {
+		return {}
+	}
 }
 
 # -- Not a normal commit type invocation?  Do that instead!
-- 
2.54.0.99.14


^ permalink raw reply related

* [PATCH v1 00/11] Improve git gui operation without a worktree
From: Mark Levedahl @ 2026-05-14 14:33 UTC (permalink / raw)
  To: git; +Cc: egg_mushroomcow, j6t, bootaina702, Mark Levedahl
In-Reply-To: <50df7f28-c63c-4762-b542-b888ea3604c0@gmail.com>

git gui has a number of inter-related problems that result in problems
during startup from anything but a checked out worktree pointing at a
valid git repository. Some of the symptoms are:
- blame / browser subcommands, and launching gitk, are intended to be
  useful without a worktree, but fail to work.
- unlike git, git-gui is supposed to use the parent directory as a
  worktree if started from the .git subdirectory in the very common
  single worktree + embedded git repository format. This does not
  work.
- git-gui includes a repository picker allowing a user select a worktree
  from a list and/or start a new repo+worktree: this dialog appears at
  unexpected times, masking useful error feedback on configuration
  problems.

This patch series addresses the above issues, substantially rewriting
the blame / browser command line process, the initial repository and
worktree discovery processes, and using git rev-parse when possible to
handle repository / worktree discovery including any specification of
GIT_DIR or GIT_WORK_TREE to reduce the future likelihood of conflict
with command line git. This also allows explicit user control to avoid
the repository picker masking a configuration error.

Note: I question why git-gui ever exports GIT_WORK_TREE. If it is not
empty, that is the current directory when startup is complete and any
git command will use the current directory as the worktree. If empty,
there is no worktree and the current directory should be (and after this
series, is) at the toplevel of the gitdir: again, there is nothing to
communicate to another process. If a process being launched needs a
different worktree, that should be the startup directory given to the
process without changing git-gui's current directory.

Mark Levedahl (11):
  git-gui: allow specifying path '.' to the browser
  git-gui: refactor browser / blame argument parsing
  git-gui: guard set/unset of GIT_DIR and GIT_WORK_TREE
  git-gui: put choose_repository::pick in a proc
  git-gui: use --absolute-git-dir
  git gui: GIT_DIR / GIT_WORK_TREE make any discovery error fatal
  git-gui: use rev-parse exclusively to find a repository
  git-gui: simplify [is_bare] to report if a worktree is known
  git-gui: support using repository parent dir as a worktree
  git-gui: improve worktree discovery
  git-gui: add gui and pick as explicit subcommands

 git-gui.sh | 276 ++++++++++++++++++++++++++---------------------------
 1 file changed, 135 insertions(+), 141 deletions(-)

-- 
2.54.0.99.14


^ permalink raw reply

* Re: [PATCH v7 0/3] git-gui: robustify startup and fix environment handling
From: Mark Levedahl @ 2026-05-14 14:28 UTC (permalink / raw)
  To: Shroom Moo, git; +Cc: Johannes Sixt, Aina Boot
In-Reply-To: <tencent_66A1C2CDB9D5B764A5B4468D3F11845A2A09@qq.com>

On 5/9/26 9:37 AM, Shroom Moo wrote:
> Shroom Moo (3):
>   git-gui: restructure repository startup
>   git-gui: disable gitk visualization when no worktree available
>   git-gui: handle GIT_DIR and GIT_WORK_TREE early
>
>
After careful consideration, I find starting off by fixing what is broken in git-gui about
using a bare-repository, and letting git core handle GIT_DIR and GIT_WORK_TREE, leads to a
much more complete and different solution. A patch series (attempting to) do so will follow.

Mark

^ permalink raw reply

* Re: I object the social engineering from Master to Main
From: Chandra @ 2026-05-14 13:42 UTC (permalink / raw)
  To: A D, git@vger.kernel.org
In-Reply-To: <CANULcizX5J5zE+QgY9TOvqpjrcJM3uFOOAWRJzOW2rBweg2WNA@mail.gmail.com>

I am strongly in favor of changing master to main. As a person currently in a country where 14 million of my people are enslaved, who also had experiences living in a factory as a child, with many friends who are descendents or have their own experiences being trafficked, this is one of those things that causes unnecessary cognitive ergonomic friction. It's a bandwidth tax for anyone not privileged enough to know only freedom.

As an engineering tool, creating pointless cognitive bandwidth sapping is counterproductive.

This is not just about history, this is about the present, where even in Florida there are 700k people who were trafficked in the last year. This is not just about the sensitivities of the privileged, but also about preserving the cognitive bandwidth of those without it.

An engineering tool should not cause emotional recoil just to use. Accessible and ergonomic design standards ought to be upheld to support seamless usage for all who use a tool.

Thank you,


Chandra Kethi-Reddy
@archonphronesis:matrix.org

Sent from Proton Mail for iOS.

-------- Original Message --------
On Thursday, 05/14/26 at 19:00 A D <diop.alpha@gmail.com> wrote:
It is not your role, as a tool, to try to engineer society through
warnings or changes of names. Your role is to deliver solid, reliable
versioning, that's it! Leave the rest to spheres where it can actually
be done without weaponizing your position or your audience.

I happen to be black African myself, and I never had any problem with
the term "Master", and its history for literal decades. The term has
been used in Computer Engineering longer than some of you have been
alive, and I see no reason to change, or to nudge people toward
changing it, just because it offends the sensibilities of some of you,
who I very much doubt are even black in the first place.

"Master" is a term with a history, and it's cool, if some of you are
offended by it, change your repos, but leave ours alone and keep your
warnings to yourself.

Cordially,
Saliou Alpha Diop



^ permalink raw reply

* I object the social engineering from Master to Main
From: A D @ 2026-05-14 13:30 UTC (permalink / raw)
  To: git

It is not your role, as a tool, to try to engineer society through
warnings or changes of names. Your role is to deliver solid, reliable
versioning, that's it! Leave the rest to spheres where it can actually
be done without weaponizing your position or your audience.

I happen to be black African myself, and I never had any problem with
the term "Master", and its history for literal decades. The term has
been used in Computer Engineering longer than some of you have been
alive, and I see no reason to change, or to nudge people toward
changing it, just because it offends the sensibilities of some of you,
who I very much doubt are even black in the first place.

"Master" is a term with a history, and it's cool, if some of you are
offended by it, change your repos, but leave ours alone and keep your
warnings to yourself.

Cordially,
Saliou Alpha Diop

^ permalink raw reply

* [PATCH v4 8/8] send-pack: pass negotiation config in push
From: Derrick Stolee via GitGitGadget @ 2026-05-14 12:41 UTC (permalink / raw)
  To: git; +Cc: gitster, ps, Matthew John Cheetham, Derrick Stolee,
	Derrick Stolee
In-Reply-To: <pull.2085.v4.git.1778762495.gitgitgadget@gmail.com>

From: Derrick Stolee <stolee@gmail.com>

When push.negotiate is enabled, 'git push' spawns a child 'git fetch
--negotiate-only' process to find common commits.  Pass
--negotiation-include and --negotiation-restrict options from the
'remote.<name>.negotiationInclude' and
'remote.<name>.negotiationRestrict' config keys to this child process.

When negotiationRestrict is configured, it replaces the default
behavior of using all remote refs as negotiation tips. This allows
the user to control which local refs are used for push negotiation.

When negotiationInclude is configured, the specified ref patterns
are passed as --negotiation-include to ensure their tips are always
sent as 'have' lines during push negotiation.

This change also updates the use of --negotiation-tip into
--negotiation-restrict now that the new synonym exists.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
---
 Documentation/config/remote.adoc |  6 ++++++
 send-pack.c                      | 37 ++++++++++++++++++++++++++------
 send-pack.h                      |  2 ++
 t/t5516-fetch-push.sh            | 30 ++++++++++++++++++++++++++
 transport.c                      |  2 ++
 5 files changed, 70 insertions(+), 7 deletions(-)

diff --git a/Documentation/config/remote.adoc b/Documentation/config/remote.adoc
index 9ae20e4379..460b4e7952 100644
--- a/Documentation/config/remote.adoc
+++ b/Documentation/config/remote.adoc
@@ -122,6 +122,9 @@ command-line option.  If `--negotiation-restrict` (or its synonym
 `--negotiation-tip`) is specified on the command line, then the config
 values are not used.
 +
+These values also influence negotiation during `git push` if
+`push.negotiate` is enabled.
++
 Blank values signal to ignore all previous values, allowing a reset of
 the list from broader config scenarios.
 
@@ -149,6 +152,9 @@ unconditionally on top of those heuristically selected commits.  This
 option is also used during push negotiation when `push.negotiate` is
 enabled.
 +
+These values also influence negotiation during `git push` if
+`push.negotiate` is enabled.
++
 Blank values signal to ignore all previous values, allowing a reset of
 the list from broader config scenarios.
 
diff --git a/send-pack.c b/send-pack.c
index 3d5d36ba3b..d18e030ce8 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -433,28 +433,48 @@ static void reject_invalid_nonce(const char *nonce, int len)
 
 static void get_commons_through_negotiation(struct repository *r,
 					    const char *url,
+					    const struct string_list *negotiation_include,
+					    const struct string_list *negotiation_restrict,
 					    const struct ref *remote_refs,
 					    struct oid_array *commons)
 {
 	struct child_process child = CHILD_PROCESS_INIT;
 	const struct ref *ref;
 	int len = r->hash_algo->hexsz + 1; /* hash + NL */
-	int nr_negotiation_tip = 0;
+	int nr_negotiation = 0;
 
 	child.git_cmd = 1;
 	child.no_stdin = 1;
 	child.out = -1;
 	strvec_pushl(&child.args, "fetch", "--negotiate-only", NULL);
-	for (ref = remote_refs; ref; ref = ref->next) {
-		if (!is_null_oid(&ref->new_oid)) {
+
+	if (negotiation_restrict && negotiation_restrict->nr) {
+		struct string_list_item *item;
+		for_each_string_list_item(item, negotiation_restrict)
 			strvec_pushf(&child.args, "--negotiation-restrict=%s",
-				     oid_to_hex(&ref->new_oid));
-			nr_negotiation_tip++;
+				     item->string);
+		nr_negotiation = negotiation_restrict->nr;
+	} else {
+		for (ref = remote_refs; ref; ref = ref->next) {
+			if (!is_null_oid(&ref->new_oid)) {
+				strvec_pushf(&child.args, "--negotiation-restrict=%s",
+					     oid_to_hex(&ref->new_oid));
+				nr_negotiation++;
+			}
 		}
 	}
+
+	if (negotiation_include && negotiation_include->nr) {
+		struct string_list_item *item;
+		for_each_string_list_item(item, negotiation_include)
+			strvec_pushf(&child.args, "--negotiation-include=%s",
+				     item->string);
+		nr_negotiation += negotiation_include->nr;
+	}
+
 	strvec_push(&child.args, url);
 
-	if (!nr_negotiation_tip) {
+	if (!nr_negotiation) {
 		child_process_clear(&child);
 		return;
 	}
@@ -528,7 +548,10 @@ int send_pack(struct repository *r,
 	repo_config_get_bool(r, "push.negotiate", &push_negotiate);
 	if (push_negotiate) {
 		trace2_region_enter("send_pack", "push_negotiate", r);
-		get_commons_through_negotiation(r, args->url, remote_refs, &commons);
+		get_commons_through_negotiation(r, args->url,
+					       args->negotiation_include,
+					       args->negotiation_restrict,
+					       remote_refs, &commons);
 		trace2_region_leave("send_pack", "push_negotiate", r);
 	}
 
diff --git a/send-pack.h b/send-pack.h
index c5ded2d200..13850c98bb 100644
--- a/send-pack.h
+++ b/send-pack.h
@@ -18,6 +18,8 @@ struct repository;
 
 struct send_pack_args {
 	const char *url;
+	const struct string_list *negotiation_include;
+	const struct string_list *negotiation_restrict;
 	unsigned verbose:1,
 		quiet:1,
 		porcelain:1,
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index ac8447f21e..177cbc6c75 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -254,6 +254,36 @@ test_expect_success 'push with negotiation does not attempt to fetch submodules'
 	! grep "Fetching submodule" err
 '
 
+test_expect_success 'push with negotiation and remote.<name>.negotiationInclude' '
+	test_when_finished rm -rf negotiation_include &&
+	mk_empty negotiation_include &&
+	git push negotiation_include $the_first_commit:refs/remotes/origin/first_commit &&
+	test_commit -C negotiation_include unrelated_commit &&
+	git -C negotiation_include config receive.hideRefs refs/remotes/origin/first_commit &&
+	test_when_finished "rm event" &&
+	GIT_TRACE2_EVENT="$(pwd)/event" \
+		git -c protocol.version=2 -c push.negotiate=1 \
+		-c remote.negotiation_include.negotiationInclude=refs/heads/main \
+		push negotiation_include refs/heads/main:refs/remotes/origin/main &&
+	test_grep \"key\":\"total_rounds\" event &&
+	grep_wrote 2 event # 1 commit, 1 tree
+'
+
+test_expect_success 'push with negotiation and remote.<name>.negotiationRestrict' '
+	test_when_finished rm -rf negotiation_restrict &&
+	mk_empty negotiation_restrict &&
+	git push negotiation_restrict $the_first_commit:refs/remotes/origin/first_commit &&
+	test_commit -C negotiation_restrict unrelated_commit &&
+	git -C negotiation_restrict config receive.hideRefs refs/remotes/origin/first_commit &&
+	test_when_finished "rm event" &&
+	GIT_TRACE2_EVENT="$(pwd)/event" \
+		git -c protocol.version=2 -c push.negotiate=1 \
+		-c remote.negotiation_restrict.negotiationRestrict=refs/heads/main \
+		push negotiation_restrict refs/heads/main:refs/remotes/origin/main &&
+	test_grep \"key\":\"total_rounds\" event &&
+	grep_wrote 2 event # 1 commit, 1 tree
+'
+
 test_expect_success 'push without wildcard' '
 	mk_empty testrepo &&
 
diff --git a/transport.c b/transport.c
index fa54928966..a2d8958cb8 100644
--- a/transport.c
+++ b/transport.c
@@ -921,6 +921,8 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
 	args.atomic = !!(flags & TRANSPORT_PUSH_ATOMIC);
 	args.push_options = transport->push_options;
 	args.url = transport->url;
+	args.negotiation_include = &transport->remote->negotiation_include;
+	args.negotiation_restrict = &transport->remote->negotiation_restrict;
 
 	if (flags & TRANSPORT_PUSH_CERT_ALWAYS)
 		args.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;
-- 
gitgitgadget

^ permalink raw reply related


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