git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH v3] packed-refs: use `fwrite()` when passing refs verbatim

From
Karthik Nayak <karthik.188@gmail.com>
Date
Oct 6, 2026, 21:11 UTC
Message-ID
<CAOLa=ZSbT6AHfU178khN7n9rHAmNE5HEM1acr96HXqXpRcSzmg@mail.gmail.com>
In-Reply-To
<asTqPcCl3RdS8YN4@pks.im>
Patrick Steinhardt <ps@pks.im> writes:
Show 16 quoted lines
> On Tue, Oct 06, 2026 at 11:18:40AM +0200, Karthik Nayak wrote:
>> The `write_with_updates()` function uses a `struct ref_iterator` to
>> iterate over all refs to write to the temporary packed-refs file. It
>> receives the iterator from `packed_ref_iterator_begin()` which takes a
>> snapshot of the 'packed-refs' file.
>>
>> While writing to the new packed-refs file, writes are routed via
>> `write_packed_entry()` which uses `fprintf()`. Even for references which
>> haven't changed, we use the same mechanism. Instead, let's track the
>> position of unchanged references in the snapshot iterator and directly
>> use `fwrite()`.
>
> Nit, not worth a reroll on its own: you state the status quo and then
> jump to the solution right away without stating what the problem is with
> the status quo.
>
Fair, let me amend.
Show 20 quoted lines
>> diff --git a/refs/packed-backend.c b/refs/packed-backend.c
>> index a73fc6aca7..43ad674cf4 100644
>> --- a/refs/packed-backend.c
>> +++ b/refs/packed-backend.c
>> @@ -879,6 +879,12 @@ struct packed_ref_iterator {
>>  	/* The current position in the snapshot's buffer: */
>>  	const char *pos;
>>
>> +	/*
>> +	 * Start of the current record, set when advancing `pos`. Used to
>> +	 * pass records verbatim to `fwrite()`.
>> +	 */
>> +	const char *record_start;
>
> The way this is written makes you think that `pos == record_start`, and
> thus one wonders why we even need this separate variable in the first
> place. So I assume that we modify `pos` in some cases without modifying
> the new variable at the same point in time. But if so, the above comment
> is not true anymore.
>

Hmm. I only state that this is 'set _when_ advancing `pos`', Why do you think that this would mean `pos == record_start`?

Show 19 quoted lines
>> @@ -1233,6 +1240,19 @@ static int write_packed_entry(FILE *fh, const char *refname,
>>  	return 0;
>>  }
>>
>> +/*
>> + * Write an entry to the packed-refs file skip any formatting and directly
>> + * write to  the file using `fwrite()`. e.g. when deleting references and
>> + * remaining refs need to be written verbatim.
>> + */
>> +static int write_packed_entry_raw(FILE *fh, const char *entry, size_t len)
>> +{
>> +	if (fwrite(entry, len, 1, fh) != 1)
>> +		return -1;
>> +
>> +	return 0;
>> +}
>
> Nit: this function is somewhat ponitless as it's a trivial wrapper
> around fwrite(3).
Yeah fair, we can simply inline this.
Show 28 quoted lines
>> @@ -1530,9 +1550,13 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
>>  		}
>>
>>  		if (cmp < 0) {
>> -			/* Pass the old reference through. */
>> -			if (write_packed_entry(out, iter->ref.name,
>> -					       iter->ref.oid, iter->ref.peeled_oid))
>> +			const struct packed_ref_iterator *packed_iter =
>> +				(const struct packed_ref_iterator *)iter;
>> +			size_t len = packed_iter->pos - packed_iter->record_start;
>
> Alright, so `pos` and `record_start` do get advanced independent from
> one another. So the comment that you have for `record_start` is
> inaccurate indeed.
>
>> +			if (write_packed_entry_raw(out,
>> +						   packed_iter->record_start,
>> +						   len))
>>  				goto write_error;
>
> Other than those nits though I'm quite happy about this change. A 20%
> win is nothing to scoff at, doubly so because reference deletions are
> extremely expensive once your repository reaches a certain number of
> refs.
>
> Thanks!
>
> Patrick
Yeah I agree, I'm quite happy to see the 20% drop too :)

Thanks for the review. Will send in a new version once we sort out the comment on the field.

Previous: Patrick SteinhardtNext: Patrick Steinhardt
Message 9 of 16 in “packed-refs: use `fwrite()` when passing refs verbatim”
  1. packed-refs: use `fwrite()` when passing refs verbatimKarthik Nayak, Sep 30, 2026
  2. Toon ClaesOct 1, 2026
  3. Karthik NayakOct 2, 2026
  4. packed-refs: use `fwrite()` when passing refs verbatimKarthik Nayak, Oct 2, 2026
  5. Toon ClaesOct 5, 2026
  6. Karthik NayakOct 6, 2026
  7. packed-refs: use `fwrite()` when passing refs verbatimKarthik Nayak, Oct 6, 2026
  8. Patrick SteinhardtOct 6, 2026
  9. Karthik NayakOct 6, 2026
  10. Patrick SteinhardtOct 7, 2026
  11. Karthik NayakOct 7, 2026
  12. Junio C HamanoOct 6, 2026
  13. Karthik NayakOct 6, 2026
  14. packed-refs: use `fwrite()` when passing refs verbatimKarthik Nayak, Oct 7, 2026
  15. Patrick SteinhardtOct 7, 2026
  16. Toon ClaesOct 8, 2026

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.