* Git trims the last character of content from remotes @ 2026-05-04 17:01 Hugo Osvaldo Barrera 2026-05-05 0:34 ` Chris Torek 2026-05-05 9:38 ` Git trims the last character of content from remotes Mikael Magnusson 0 siblings, 2 replies; 9+ messages in thread From: Hugo Osvaldo Barrera @ 2026-05-04 17:01 UTC (permalink / raw) To: git Hi all, When I push content to GitLab, the remote server sends back some text which git then prints to stderr: remote: remote: To create a merge request for zk, visit: remote: https://gitlab.alpinelinux.org/WhyNotHugo/aports/-/merge_requests/new?merge_request%5Bsource_branch%5D=zk remote: When the width of a whole line is the same as my terminal width, the last digit gets trimmed off. E.g.: if I resize my terminal for the above to fix exactly, and re-run the same command, git prints: remote: https://gitlab.alpinelinux.org/WhyNotHugo/aports/-/merge_requests/new?merge_request%5Bsource_branch%5D=z From what I can tell, sideband.c prints ANSI_SUFFIX = "\033[K", this escape sequence being "clear the line from the current position until the end of the line", and this is the root cause of the issue. When piping to cat or to a file, this sequence is not printed, so the output is fine. Is this a bug? Thanks, PS: please CC me, as I am not subscribed to the list. -- Hugo ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Git trims the last character of content from remotes 2026-05-04 17:01 Git trims the last character of content from remotes Hugo Osvaldo Barrera @ 2026-05-05 0:34 ` Chris Torek 2026-05-05 19:41 ` René Scharfe 2026-05-10 12:42 ` [PATCH] sideband: clear full line when printing remote messages René Scharfe 2026-05-05 9:38 ` Git trims the last character of content from remotes Mikael Magnusson 1 sibling, 2 replies; 9+ messages in thread From: Chris Torek @ 2026-05-05 0:34 UTC (permalink / raw) To: Hugo Osvaldo Barrera; +Cc: git On Mon, May 4, 2026 at 10:02 AM Hugo Osvaldo Barrera <hugo@whynothugo.nl> wrote: [snippage] > When the width of a whole line is the same as my terminal width ... [snippage] > ... sideband.c prints ANSI_SUFFIX = "\033[K", this escape > sequence being "clear the line from the current position until the end of the > line", and this is the root cause of the issue. Interesting. In Ye Olden Dayes of (n)curses, there was (and still is) a terminal capacity boolean flag, "xn" or (in terminfo which is more verbose) "xenl", the "terminal eats newline glitch". Consider your bog-standard 80x24 "glass tty" from the late 1970s / early 1980s. Printing a line of exactly 80 characters caused the cursor to march from column 1, to 2, to 3, ..., to 80, to ... column 81? There is no column 81. So what is this "glass tty" to do? Some acted like a print head, leaving the cursor stuck in column 80, so that printing *more* characters just made that big black blob of ink on the paper er I mean erased each previous character with the new one printed on top. So then a final "new line" sequence left the cursor on column 1 of the next line, which is where we want it. Some thought this was annoying and/or stupid so they immediately wrapped to column 1 of the next line, as if the computer had sent a newline sequence. But if the line was in fact exactly 80 characters, this meant the subsequent newline sequence moved to column 1 of the *next* row, leaving a blank line (or scrolling the screen twice or whatever). This is Obviously Bad Behavior, but the "overprint" answer is equally Obviously Bad. There were two ways of dealing with the problem intelligently: put the cursor to an internal "column 81" that, if there's a newline, sends the cursor to column 1 of the next row; or simply set a flag and eat the next character if it's a newline. (This is a little trickier than it sounds since the newline sequence is actually CR+LF, or LF+CR, depending on certain computer-maker choices, but it works either way.) The xn / xenl flag describes terminals that behave this way. The screen-oriented programs (ex/vi, now vim and emacs and nano and so on, plus things like "more"/"less"/other pagers, etc) would know to send an extra newline here if the xn/xenl flag is true, and not if not since the cursor was already on column 1 of the next line automatically. (Though actually this depends on another boolean, "am", auto-right-margin. Lacking "am", the cursor simply hammers on the final column, the overprint Bad Behavior Mode.) Alas, this does not describe what happens if one sends the "clear to end of line" sequence. If the cursor is in the phantom "column 81", perhaps that sequence does nothing. If it's lingering in column 80, perhaps that clears the character under the cursor. All that xn tells you is "send a newline anyway". As for what to do, well, that could be tricky. Git *could* check for "am" and "xn" / "xenl", but that requires parsing termcap/terminfo, which is kind of a nightmare. It also requires counting cursor column movements, which is something of a mug's game.[1] If you're willing to play that game though, you could just count and, if at the last column as determined by "tty column width" inquiry, omit the ESC [ K entirely: there's nothing to clear. If you have a non-empty prefix string before this "clear to end of line" suffix, the solution is more obvious: print the ESC [ K as a *prefix* rather than a suffix, but that fails with the empty prefix. One last easy possibility is to print an extra space before the ESC [ K. It's imperfect, as it causes a blank line for these exact-width lines, but avoids data loss. Chris [1]: https://www.merriam-webster.com/dictionary/mug%27s%20game ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Git trims the last character of content from remotes 2026-05-05 0:34 ` Chris Torek @ 2026-05-05 19:41 ` René Scharfe 2026-05-06 9:37 ` Mikael Magnusson 2026-05-10 12:42 ` [PATCH] sideband: clear full line when printing remote messages René Scharfe 1 sibling, 1 reply; 9+ messages in thread From: René Scharfe @ 2026-05-05 19:41 UTC (permalink / raw) To: Chris Torek, Hugo Osvaldo Barrera; +Cc: git On 5/5/26 2:34 AM, Chris Torek wrote: > On Mon, May 4, 2026 at 10:02 AM Hugo Osvaldo Barrera <hugo@whynothugo.nl> wrote: > [snippage] >> When the width of a whole line is the same as my terminal width ... > [snippage] >> ... sideband.c prints ANSI_SUFFIX = "\033[K", this escape >> sequence being "clear the line from the current position until the end of the >> line", and this is the root cause of the issue. > If you have a non-empty prefix > string before this "clear to end of line" suffix, the solution is more > obvious: print the ESC [ K as a *prefix* rather than a suffix, but > that fails with the empty prefix. We do have a non-empty prefix, but why would it be necessary? What's wrong with clearing the full line starting from column 1? Anyway, do you mean something like this? diff --git a/sideband.c b/sideband.c index ea7c25211e..5bfdd1d372 100644 --- a/sideband.c +++ b/sideband.c @@ -120,7 +120,7 @@ static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n) #define DISPLAY_PREFIX "remote: " -#define ANSI_SUFFIX "\033[K" +#define ANSI_PREFIX "\033[K" #define DUMB_SUFFIX " " int demultiplex_sideband(const char *me, int status, @@ -129,15 +129,19 @@ int demultiplex_sideband(const char *me, int status, struct strbuf *scratch, enum sideband_type *sideband_type) { + static const char *prefix; static const char *suffix; const char *b, *brk; int band; if (!suffix) { - if (isatty(2) && !is_terminal_dumb()) - suffix = ANSI_SUFFIX; - else + if (isatty(2) && !is_terminal_dumb()) { + prefix = DISPLAY_PREFIX ANSI_PREFIX; + suffix = ""; + } else { + prefix = DISPLAY_PREFIX; suffix = DUMB_SUFFIX; + } } if (status == PACKET_READ_EOF) { @@ -172,7 +176,7 @@ int demultiplex_sideband(const char *me, int status, if (die_on_error) die(_("remote error: %s"), buf + 1); strbuf_addf(scratch, "%s%s", scratch->len ? "\n" : "", - DISPLAY_PREFIX); + prefix); maybe_colorize_sideband(scratch, buf + 1, len); *sideband_type = SIDEBAND_REMOTE_ERROR; @@ -203,7 +207,7 @@ int demultiplex_sideband(const char *me, int status, strbuf_addstr(scratch, suffix); if (!scratch->len) - strbuf_addstr(scratch, DISPLAY_PREFIX); + strbuf_addstr(scratch, prefix); /* * A use case that we should not add clear-to-eol suffix @@ -230,7 +234,7 @@ int demultiplex_sideband(const char *me, int status, if (*b) { strbuf_addstr(scratch, scratch->len ? - "" : DISPLAY_PREFIX); + "" : prefix); maybe_colorize_sideband(scratch, b, strlen(b)); } return 0; ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: Git trims the last character of content from remotes 2026-05-05 19:41 ` René Scharfe @ 2026-05-06 9:37 ` Mikael Magnusson 2026-05-06 9:40 ` Mikael Magnusson 0 siblings, 1 reply; 9+ messages in thread From: Mikael Magnusson @ 2026-05-06 9:37 UTC (permalink / raw) To: René Scharfe; +Cc: Chris Torek, Hugo Osvaldo Barrera, git On Tue, May 5, 2026 at 9:46 PM René Scharfe <l.s.r@web.de> wrote: > > On 5/5/26 2:34 AM, Chris Torek wrote: > > On Mon, May 4, 2026 at 10:02 AM Hugo Osvaldo Barrera <hugo@whynothugo.nl> wrote: > > [snippage] > >> When the width of a whole line is the same as my terminal width ... > > [snippage] > >> ... sideband.c prints ANSI_SUFFIX = "\033[K", this escape > >> sequence being "clear the line from the current position until the end of the > >> line", and this is the root cause of the issue. > > > If you have a non-empty prefix > > string before this "clear to end of line" suffix, the solution is more > > obvious: print the ESC [ K as a *prefix* rather than a suffix, but > > that fails with the empty prefix. > We do have a non-empty prefix, but why would it be necessary? What's > wrong with clearing the full line starting from column 1? > > Anyway, do you mean something like this? If the purpose of the clear is to reset the background color on wrapped lines, this will not have any effect, since you clear before the new line is wrapped in. (This is a bit of an obscure edge case, if you set the background color, and wrap the line, the entire new line will be scrolled in with the active background color, then you write perhaps 10 more characters and send the sequence to reset the background color, but the entire rest of the line is still brown, or whatever it was set to when you wrapped). Example command to reproduce locally, % echo -e '\e[43maaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\e[0mhihi' (Add more aaaaaaa if necessary so that the line breaks before they end). -- Mikael Magnusson ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Git trims the last character of content from remotes 2026-05-06 9:37 ` Mikael Magnusson @ 2026-05-06 9:40 ` Mikael Magnusson 2026-05-06 16:00 ` René Scharfe 0 siblings, 1 reply; 9+ messages in thread From: Mikael Magnusson @ 2026-05-06 9:40 UTC (permalink / raw) To: René Scharfe; +Cc: Chris Torek, Hugo Osvaldo Barrera, git On Wed, May 6, 2026 at 11:37 AM Mikael Magnusson <mikachu@gmail.com> wrote: > > On Tue, May 5, 2026 at 9:46 PM René Scharfe <l.s.r@web.de> wrote: > > > > On 5/5/26 2:34 AM, Chris Torek wrote: > > > On Mon, May 4, 2026 at 10:02 AM Hugo Osvaldo Barrera <hugo@whynothugo.nl> wrote: > > > [snippage] > > >> When the width of a whole line is the same as my terminal width ... > > > [snippage] > > >> ... sideband.c prints ANSI_SUFFIX = "\033[K", this escape > > >> sequence being "clear the line from the current position until the end of the > > >> line", and this is the root cause of the issue. > > > > > If you have a non-empty prefix > > > string before this "clear to end of line" suffix, the solution is more > > > obvious: print the ESC [ K as a *prefix* rather than a suffix, but > > > that fails with the empty prefix. > > We do have a non-empty prefix, but why would it be necessary? What's > > wrong with clearing the full line starting from column 1? > > > > Anyway, do you mean something like this? > > If the purpose of the clear is to reset the background color on > wrapped lines, this will not have any effect, since you clear before > the new line is wrapped in. (This is a bit of an obscure edge case, if > you set the background color, and wrap the line, the entire new line > will be scrolled in with the active background color, then you write > perhaps 10 more characters and send the sequence to reset the > background color, but the entire rest of the line is still brown, or > whatever it was set to when you wrapped). > > Example command to reproduce locally, > % echo -e '\e[43maaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\e[0mhihi' > (Add more aaaaaaa if necessary so that the line breaks before they end). Sorry for the double post, but I forgot an important thing, this only happens if you *actually* scroll in a new line, ie if you open a new terminal and run this, you won't see any problems until you get to the bottom of the screen. -- Mikael Magnusson ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Git trims the last character of content from remotes 2026-05-06 9:40 ` Mikael Magnusson @ 2026-05-06 16:00 ` René Scharfe 0 siblings, 0 replies; 9+ messages in thread From: René Scharfe @ 2026-05-06 16:00 UTC (permalink / raw) To: Mikael Magnusson; +Cc: Chris Torek, Hugo Osvaldo Barrera, git On 5/6/26 11:40 AM, Mikael Magnusson wrote: > On Wed, May 6, 2026 at 11:37 AM Mikael Magnusson <mikachu@gmail.com> wrote: >> >> On Tue, May 5, 2026 at 9:46 PM René Scharfe <l.s.r@web.de> wrote: >>> >>> On 5/5/26 2:34 AM, Chris Torek wrote: >>>> On Mon, May 4, 2026 at 10:02 AM Hugo Osvaldo Barrera <hugo@whynothugo.nl> wrote: >>>> [snippage] >>>>> When the width of a whole line is the same as my terminal width ... >>>> [snippage] >>>>> ... sideband.c prints ANSI_SUFFIX = "\033[K", this escape >>>>> sequence being "clear the line from the current position until the end of the >>>>> line", and this is the root cause of the issue. >>> >>>> If you have a non-empty prefix >>>> string before this "clear to end of line" suffix, the solution is more >>>> obvious: print the ESC [ K as a *prefix* rather than a suffix, but >>>> that fails with the empty prefix. >>> We do have a non-empty prefix, but why would it be necessary? What's >>> wrong with clearing the full line starting from column 1? >>> >>> Anyway, do you mean something like this? >> >> If the purpose of the clear is to reset the background color on >> wrapped lines, this will not have any effect, since you clear before >> the new line is wrapped in. (This is a bit of an obscure edge case, if >> you set the background color, and wrap the line, the entire new line >> will be scrolled in with the active background color, then you write >> perhaps 10 more characters and send the sequence to reset the >> background color, but the entire rest of the line is still brown, or >> whatever it was set to when you wrapped). >> >> Example command to reproduce locally, >> % echo -e '\e[43maaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\e[0mhihi' >> (Add more aaaaaaa if necessary so that the line breaks before they end). > > Sorry for the double post, but I forgot an important thing, this only > happens if you *actually* scroll in a new line, ie if you open a new > terminal and run this, you won't see any problems until you get to the > bottom of the screen. The purpose of clearing here is to avoid leaving local progress line remnants after the remote line. Original discussion: https://lore.kernel.org/git/alpine.LFD.0.9999.0711032328490.21255@xanadu.home/ You're right that erasing before filling the whole line and then some is unnecessary. But it wouldn't hurt, either, no? René ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH] sideband: clear full line when printing remote messages 2026-05-05 0:34 ` Chris Torek 2026-05-05 19:41 ` René Scharfe @ 2026-05-10 12:42 ` René Scharfe 2026-05-10 23:30 ` Junio C Hamano 1 sibling, 1 reply; 9+ messages in thread From: René Scharfe @ 2026-05-10 12:42 UTC (permalink / raw) To: Chris Torek, Hugo Osvaldo Barrera; +Cc: git demultiplex_sideband() can write its remote output over active local progress lines. That's why it has been using ANSI code Erase in Line on smart terminals to clear the remainder of lines it writes since ebe8fa738d (fix display overlap between remote and local progress, 2007-11-04). This erases the last character of remote lines that span the full width of the terminal, though, as the cursor is stuck at the rightmost column for them. It's the same effect as in the following command, which clears the 1 and shows just the leading zeros: $ EL="\033[K" $ printf "%0${COLUMNS}d${EL}\n" 1 If we move the ANSI code to the start we get to see the 1 as well: $ printf "${EL}%0${COLUMNS}d\n" 1 So do the same in demultiplex_sideband() and emit the ANSI code as a prefix instead of a suffix to show messages in full even if they happen to fill the whole width of a smart terminal. Reported-by: Hugo Osvaldo Barrera <hugo@whynothugo.nl> Suggested-by: Chris Torek <chris.torek@gmail.com> Signed-off-by: René Scharfe <l.s.r@web.de> --- sideband.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/sideband.c b/sideband.c index ea7c25211e..48ed4c8099 100644 --- a/sideband.c +++ b/sideband.c @@ -120,7 +120,7 @@ static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n) #define DISPLAY_PREFIX "remote: " -#define ANSI_SUFFIX "\033[K" +#define ANSI_PREFIX "\033[K" #define DUMB_SUFFIX " " int demultiplex_sideband(const char *me, int status, @@ -129,15 +129,18 @@ int demultiplex_sideband(const char *me, int status, struct strbuf *scratch, enum sideband_type *sideband_type) { - static const char *suffix; + static const char *prefix, *suffix; const char *b, *brk; int band; if (!suffix) { - if (isatty(2) && !is_terminal_dumb()) - suffix = ANSI_SUFFIX; - else + if (isatty(2) && !is_terminal_dumb()) { + prefix = ANSI_PREFIX DISPLAY_PREFIX; + suffix = ""; + } else { + prefix = DISPLAY_PREFIX; suffix = DUMB_SUFFIX; + } } if (status == PACKET_READ_EOF) { @@ -171,8 +174,7 @@ int demultiplex_sideband(const char *me, int status, case 3: if (die_on_error) die(_("remote error: %s"), buf + 1); - strbuf_addf(scratch, "%s%s", scratch->len ? "\n" : "", - DISPLAY_PREFIX); + strbuf_addf(scratch, "%s%s", scratch->len ? "\n" : "", prefix); maybe_colorize_sideband(scratch, buf + 1, len); *sideband_type = SIDEBAND_REMOTE_ERROR; @@ -203,7 +205,7 @@ int demultiplex_sideband(const char *me, int status, strbuf_addstr(scratch, suffix); if (!scratch->len) - strbuf_addstr(scratch, DISPLAY_PREFIX); + strbuf_addstr(scratch, prefix); /* * A use case that we should not add clear-to-eol suffix @@ -229,8 +231,8 @@ int demultiplex_sideband(const char *me, int status, } if (*b) { - strbuf_addstr(scratch, scratch->len ? - "" : DISPLAY_PREFIX); + if (!scratch->len) + strbuf_addstr(scratch, prefix); maybe_colorize_sideband(scratch, b, strlen(b)); } return 0; -- 2.54.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH] sideband: clear full line when printing remote messages 2026-05-10 12:42 ` [PATCH] sideband: clear full line when printing remote messages René Scharfe @ 2026-05-10 23:30 ` Junio C Hamano 0 siblings, 0 replies; 9+ messages in thread From: Junio C Hamano @ 2026-05-10 23:30 UTC (permalink / raw) To: René Scharfe; +Cc: Chris Torek, Hugo Osvaldo Barrera, git René Scharfe <l.s.r@web.de> writes: > demultiplex_sideband() can write its remote output over active local > progress lines. That's why it has been using ANSI code Erase in Line on > smart terminals to clear the remainder of lines it writes since > ebe8fa738d (fix display overlap between remote and local progress, > 2007-11-04). > > This erases the last character of remote lines that span the full width > of the terminal, though, as the cursor is stuck at the rightmost column > for them. It's the same effect as in the following command, which > clears the 1 and shows just the leading zeros: > > $ EL="\033[K" > $ printf "%0${COLUMNS}d${EL}\n" 1 > > If we move the ANSI code to the start we get to see the 1 as well: > > $ printf "${EL}%0${COLUMNS}d\n" 1 > > So do the same in demultiplex_sideband() and emit the ANSI code as a > prefix instead of a suffix to show messages in full even if they happen > to fill the whole width of a smart terminal. Makes sense. The final objective is to make sure that leftover letters near the end of line printed by previous "print" would not remain after the material we are printing, so it does not matter if we print and then erase the remainder or we erase the whole line and print. And the latter is an obvious way to make it easier to reason about in the presense of funkiness in the ways terminals behave around the end of line. ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Git trims the last character of content from remotes 2026-05-04 17:01 Git trims the last character of content from remotes Hugo Osvaldo Barrera 2026-05-05 0:34 ` Chris Torek @ 2026-05-05 9:38 ` Mikael Magnusson 1 sibling, 0 replies; 9+ messages in thread From: Mikael Magnusson @ 2026-05-05 9:38 UTC (permalink / raw) To: Hugo Osvaldo Barrera; +Cc: git On Mon, May 4, 2026 at 7:02 PM Hugo Osvaldo Barrera <hugo@whynothugo.nl> wrote: > > Hi all, > > When I push content to GitLab, the remote server sends back some text which git > then prints to stderr: > > remote: > remote: To create a merge request for zk, visit: > remote: https://gitlab.alpinelinux.org/WhyNotHugo/aports/-/merge_requests/new?merge_request%5Bsource_branch%5D=zk > remote: > > When the width of a whole line is the same as my terminal width, the last digit > gets trimmed off. E.g.: if I resize my terminal for the above to fix exactly, > and re-run the same command, git prints: > > remote: https://gitlab.alpinelinux.org/WhyNotHugo/aports/-/merge_requests/new?merge_request%5Bsource_branch%5D=z > > From what I can tell, sideband.c prints ANSI_SUFFIX = "\033[K", this escape > sequence being "clear the line from the current position until the end of the > line", and this is the root cause of the issue. > > When piping to cat or to a file, this sequence is not printed, so the output is > fine. > > Is this a bug? grep has the same bug with --color, if you have a line of text the same width as your terminal, for the same reason. urxvt supports an extension of \e[3K to clear to end but not erase the character under the cursor if it's wrapped (but not yet moved to the next line). xterm hasn't picked it up though, so it's not a general solution, unfortunately. It's a little unclear to me why git prints this sequence here at all, are we expecting that there is already other text printed on the line? Or is it to clear cells that may have had another background color when the current line was scrolled in? Maybe that case is more unusual and less harmful than actually eating the final character, that it's not worth clearing? -- Mikael Magnusson ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-05-10 23:30 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-05-04 17:01 Git trims the last character of content from remotes Hugo Osvaldo Barrera 2026-05-05 0:34 ` Chris Torek 2026-05-05 19:41 ` René Scharfe 2026-05-06 9:37 ` Mikael Magnusson 2026-05-06 9:40 ` Mikael Magnusson 2026-05-06 16:00 ` René Scharfe 2026-05-10 12:42 ` [PATCH] sideband: clear full line when printing remote messages René Scharfe 2026-05-10 23:30 ` Junio C Hamano 2026-05-05 9:38 ` Git trims the last character of content from remotes Mikael Magnusson
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox