summaryrefslogtreecommitdiff
AgeCommit message (Collapse)AuthorFilesLines
2024-03-03t-ctype: align output of iRené Scharfe1-1/+1
The unit test reports misclassified characters like this: # check "isdigit(i) == !!memchr("123456789", i, len)" failed at t/unit-tests/t-ctype.c:36 # left: 1 # right: 0 # i: 0x30 Reduce the indent of i to put its colon directly below the ones in the preceding lines for consistency. Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-03-03t-ctype: simplify EOF checkRené Scharfe1-2/+1
EOF is not a member of any character class. If a classifier function returns a non-zero result for it, presumably by mistake, then the unit test check reports: # check "!iseof(EOF)" failed at t/unit-tests/t-ctype.c:53 # i: 0xffffffff (EOF) The numeric value of EOF is not particularly interesting in this context. Stop printing the second line. Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-03-03t-ctype: allow NUL anywhere in the specification stringRené Scharfe1-14/+4
Replace the custom function is_in() for looking up a character in the specification string with memchr(3) and sizeof. This is shorter, simpler and allows NUL anywhere in the string, which may come in handy if we ever want to support more character classes that contain it. Getting the string size using sizeof only works in a macro and with a string constant. Use ARRAY_SIZE and compile-time checks to make sure we are not passed a string pointer. Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-03-02repack: check error writing to pack-objects subprocessJunio C Hamano1-2/+3
When "git repack" repacks promisor objects, it starts a pack-objects subprocess and uses xwrite() to send object names over the pipe to it, but without any error checking. An I/O error or short write (even though a short write is unlikely for such a small amount of data) can result in a packfile that lacks certain objects we wanted to put in there, leading to a silent repository corruption. Use write_in_full(), instead of xwrite(), to mitigate short write risks, check errors from it, and abort if we see a failure. Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-03-02sideband: avoid short write(2)Junio C Hamano1-2/+2
The sideband demultiplexor writes the data it receives on sideband with xwrite(). We can lose data if the underlying write(2) results in a short write. If they are limited to unimportant bytes like eye-candy progress meter, it may be OK to lose them, but lets be careful and ensure that we use write_in_full() instead. Note that the original does not check for errors, and this rewrite does not check for one. At least not yet. Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-03-02unpack: replace xwrite() loop with write_in_full()Junio C Hamano2-21/+4
We have two packfile stream consumers, index-pack and unpack-objects, that allow excess payload after the packfile stream data. Their code to relay excess data hasn't changed significantly since their original implementation that appeared in 67e5a5ec (git-unpack-objects: re-write to read from stdin, 2005-06-28) and 9bee2478 (mimic unpack-objects when --stdin is used with index-pack, 2006-10-25). These code blocks contain hand-rolled loops using xwrite(), written before our write_in_full() helper existed. This helper now provides the same functionality. Replace these loops with write_in_full() for shorter, clearer code. Update related variables accordingly. Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-03-02test_i18ngrep: hard deprecate and forbid its useJunio C Hamano1-2/+1
Since v2.44.0-rc0~109 (Merge branch 'sp/test-i18ngrep', 2023-12-27) none of the tests we have, either in 'master' or in flight and collected in 'seen', use test_i18ngrep. Perhaps it is good time to update test_i18ngrep to BUG to avoid people adding new calls to it. Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-03-01The third batchJunio C Hamano1-0/+15
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-03-01Merge branch 'tb/multi-pack-verbatim-reuse' into HEADJunio C Hamano1-5/+4
Docfix. * tb/multi-pack-verbatim-reuse: Documentation/config/pack.txt: fix broken AsciiDoc mark-up
2024-03-01Merge branch 'hs/rebase-not-in-progress' into HEADJunio C Hamano1-1/+1
Error message update. * hs/rebase-not-in-progress: rebase: make warning less passive aggressive
2024-03-01Merge branch 'jw/remote-doc-typofix' into HEADJunio C Hamano1-1/+1
Docfix. * jw/remote-doc-typofix: git-remote.txt: fix typo
2024-03-01Merge branch 'jc/doc-add-placeholder-fix' into HEADJunio C Hamano1-10/+10
Practice the new mark-up rule for <placeholders> with "git add" documentation page. * jc/doc-add-placeholder-fix: doc: apply the new placeholder rules to git-add documentation
2024-03-01Merge branch 'ja/doc-placeholders-markup-rules' into HEADJunio C Hamano1-0/+7
The way placeholders are to be marked-up in documentation have been specified; use "_<placeholder>_" to typeset the word inside a pair of <angle-brakets> emphasized. * ja/doc-placeholders-markup-rules: doc: clarify the format of placeholders
2024-03-01Merge branch 'ps/reflog-list' into HEADJunio C Hamano20-205/+380
"git reflog" learned a "list" subcommand that enumerates known reflogs. * ps/reflog-list: builtin/reflog: introduce subcommand to list reflogs refs: stop resolving ref corresponding to reflogs refs: drop unused params from the reflog iterator callback refs: always treat iterators as ordered refs/files: sort merged worktree and common reflogs refs/files: sort reflogs returned by the reflog iterator dir-iterator: support iteration in sorted order dir-iterator: pass name to `prepare_next_entry_data()` directly
2024-03-01Merge branch 'ds/doc-send-email-capitalization' into HEADJunio C Hamano2-15/+15
Doc update. * ds/doc-send-email-capitalization: documentation: send-email: use camel case consistently
2024-03-01Merge branch 'ja/docfixes' into HEADJunio C Hamano4-16/+16
Doc update. * ja/docfixes: doc: end sentences with full-stop doc: close unclosed angle-bracket of a placeholder in git-clone doc doc: git-rev-parse: enforce command-line description syntax
2024-03-01Merge branch 'cp/t9146-use-test-path-helpers' into HEADJunio C Hamano1-44/+12
Test script clean-up. * cp/t9146-use-test-path-helpers: t9146: replace test -d/-e/-f with appropriate test_path_is_* function
2024-03-01Merge branch 'ps/difftool-dir-diff-exit-code' into HEADJunio C Hamano3-46/+67
"git difftool --dir-diff" learned to honor the "--trust-exit-code" option; it used to always exit with 0 and signalled success. * ps/difftool-dir-diff-exit-code: git-difftool--helper: honor `--trust-exit-code` with `--dir-diff`
2024-03-01gitcli: drop mention of “non-dashed form”Kristoffer Haugsbakk1-3/+0
Git builtins used to be called like e.g. `git-commit`, not `git commit` (*dashed form* and *non-dashed form*, respectively). The dashed form was deprecated in version 1.5.4 (2006). Now only a few commands have an alternative dashed form when `SKIP_DASHED_BUILT_INS` is active.[1] The mention here is from 2f7ee089dff (parse-options: Add a gitcli(5) man page., 2007-12-13), back when the deprecation was relatively recent. These days though it seems like an irrelevant point to make to budding CLI scripters—you don’t have to warn against a style that probably doesn’t even work on their git(1) installation. † 1: 179227d6e21 (Optionally skip linking/copying the built-ins, 2020-09-21) Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-03-01format_trailers_from_commit(): indirectly call trailer_info_get()Linus Arver1-1/+4
This is another preparatory refactor to unify the trailer formatters. For background, note that the "trailers" string array is the `char **trailers` member in `struct trailer_info` and that the trailer_item objects are the elements of the `struct list_head *head` linked list. Currently trailer_info_get() only populates `char **trailers`. And parse_trailers() first calls trailer_info_get() so that it can use the `char **trailers` to populate a list of `struct trailer_item` objects Instead of calling trailer_info_get() directly from format_trailers_from_commit(), make it call parse_trailers() instead because parse_trailers() already calls trailer_info_get(). This change is a NOP because format_trailer_info() (which format_trailers_from_commit() wraps around) only looks at the "trailers" string array, not the trailer_item objects which parse_trailers() populates. For now we do need to create a dummy LIST_HEAD(trailer_objects); because parse_trailers() expects it in its signature. In a future patch, we'll change format_trailer_info() to use the parsed trailer_item objects (trailer_objects) instead of the `char **trailers` array. Signed-off-by: Linus Arver <linusa@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-03-01format_trailer_info(): move "fast path" to callerLinus Arver1-11/+9
This is another preparatory refactor to unify the trailer formatters. This allows us to drop the "msg" parameter from format_trailer_info(), so that it take 3 parameters, similar to format_trailers() which also takes 3 parameters: void format_trailers(const struct process_trailer_options *opts, struct list_head *trailers, struct strbuf *out) The short-term goal is to make format_trailer_info() be smart enough to deprecate format_trailers(). And then ultimately we will rename format_trailer_info() to format_trailers(). Signed-off-by: Linus Arver <linusa@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-03-01format_trailers(): use strbuf instead of FILELinus Arver3-8/+14
This is another preparatory refactor to unify the trailer formatters. Make format_trailers() also write to a strbuf, to align with format_trailers_from_commit() which also does the same. Doing this makes format_trailers() behave similar to format_trailer_info() (which will soon help us replace one with the other). Signed-off-by: Linus Arver <linusa@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-03-01trailer_info_get(): reorder parametersLinus Arver3-8/+10
This is another preparatory refactor to unify the trailer formatters. Take const struct process_trailer_options *opts as the first parameter, because these options are required for parsing trailers (e.g., whether to treat "---" as the end of the log message). And take struct trailer_info *info last, because it's an "out parameter" (something that the caller wants to use as the output of this function). Signed-off-by: Linus Arver <linusa@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-03-01trailer: move interpret_trailers() to interpret-trailers.cLinus Arver3-109/+123
The interpret-trailers.c builtin is the only place we need to call interpret_trailers(), so move its definition there (together with a few helper functions called only by it) and remove its external declaration from <trailer.h>. Several helper functions that are called by interpret_trailers() remain in trailer.c because other callers in the same file still call them. Declare them in <trailer.h> so that interpret_trailers() (now in builtin/interpret-trailers.c) can continue calling them as a trailer API user. This enriches <trailer.h> with a more granular API, which can then be unit-tested in the future (because interpret_trailers() by itself does too many things to be able to be easily unit-tested). Take this opportunity to demote some file-handling functions out of the trailer API implementation, as these have nothing to do with trailers. Helped-by: Junio C Hamano <gitster@pobox.com> Signed-off-by: Linus Arver <linusa@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-03-01trailer: reorder format_trailers_from_commit() parametersLinus Arver4-9/+11
Currently there are two functions for formatting trailers in <trailer.h>: void format_trailers(const struct process_trailer_options *, struct list_head *trailers, FILE *outfile); void format_trailers_from_commit(struct strbuf *out, const char *msg, const struct process_trailer_options *opts); and although they are similar enough (even taking the same process_trailer_options struct pointer) they are used quite differently. One might intuitively think that format_trailers_from_commit() builds on top of format_trailers(), but this is not the case. Instead format_trailers_from_commit() calls format_trailer_info() and format_trailers() is never called in that codepath. This is a preparatory refactor to help us deprecate format_trailers() in favor of format_trailer_info() (at which point we can rename the latter to the former). When the deprecation is complete, both format_trailers_from_commit(), and the interpret-trailers builtin will be able to call into the same helper function (instead of format_trailers() and format_trailer_info(), respectively). Unifying the formatters is desirable because it simplifies the API. Reorder parameters for format_trailers_from_commit() to prefer const struct process_trailer_options *opts as the first parameter, because these options are intimately tied to formatting trailers. And take struct strbuf *out last, because it's an "out parameter" (something that the caller wants to use as the output of this function). Similarly, reorder parameters for format_trailer_info(), because later on we will unify the two together. Signed-off-by: Linus Arver <linusa@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-03-01trailer: rename functions to use 'trailer'Linus Arver3-18/+18
Rename process_trailers() to interpret_trailers(), because it matches the name for the builtin command of the same name (git-interpret-trailers), which is the sole user of process_trailers(). In a following commit, we will move "interpret_trailers" from trailer.c to builtin/interpret-trailers.c. That move will necessitate the growth of the trailer.h API, forcing us to expose some additional functions in trailer.h. Rename relevant functions so that they include the term "trailer" in their name, so that clients of the API will be able to easily identify them by their "trailer" moniker, just like all the other functions already exposed by trailer.h. Rename `struct list_head *head` to `struct list_head *trailers` because "head" conveys no additional information beyond the "list_head" type. Reorder parameters for format_trailers_from_commit() to prefer const struct process_trailer_options *opts as the first parameter, because these options are intimately tied to formatting trailers. Parameters like `FILE *outfile` should be last because they are a kind of 'out' parameter, so put such parameters at the end. This will be the pattern going forward in this series. Helped-by: Junio C Hamano <gitster@pobox.com> Helped-by: Christian Couder <chriscool@tuxfamily.org> Signed-off-by: Linus Arver <linusa@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-03-01shortlog: add test for de-duplicating folded trailersLinus Arver2-0/+33
The shortlog builtin was taught to use the trailer iterator interface in 47beb37bc6 (shortlog: match commit trailers with --group, 2020-09-27). The iterator always unfolds values and this has always been the case since the time the iterator was first introduced in f0939a0eb1 (trailer: add interface for iterating over commit trailers, 2020-09-27). Add a comment line to remind readers of this behavior. The fact that the iterator always unfolds values is important (at least for shortlog) because unfolding allows it to recognize both folded and unfolded versions of the same trailer for de-duplication. Capture the existing behavior in a new test case to guard against regressions in this area. This test case is based off of the existing "shortlog de-duplicates trailers in a single commit" just above it. Now if we were to remove the call to unfold_value(&iter->val); inside the iterator, this new test case will break. Signed-off-by: Linus Arver <linusa@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-03-01trailer: free trailer_info _after_ all related usageLinus Arver1-2/+1
In de7c27a186 (trailer: use offsets for trailer_start/trailer_end, 2023-10-20), we started using trailer block offsets in trailer_info. In particular, we dropped the use of a separate stack variable "size_t trailer_end", in favor of accessing the new "trailer_block_end" member of trailer_info (as "info.trailer_block_end"). At that time, we forgot to also move the trailer_info_release(&info); line to be _after_ this new use of the trailer_info struct. Move it now. Note that even without this patch, we didn't have leaks or any other problems because trailer_info_release() only frees memory allocated on the heap. The "trailer_block_end" member was allocated on the stack back then (as it is now) so it was still safe to use for all this time. Reported-by: Junio C Hamano <gitster@pobox.com> Signed-off-by: Linus Arver <linusa@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-02-29docs: sort configuration variable groupings alphabeticallyEric Sunshine1-14/+14
By and large, variable groupings in Documentation/config.txt are sorted alphabetically, though a few are not. Those outliers make it more difficult to find a specific grouping when quickly running an eye over the list to locate a variable of interest. Address this shortcoming by sorting the groupings alphabetically. NOTE: This change only sorts the top-level groupings (i.e. "core.*" comes after "completion.*"); it does not touch the ordering of variables within each group since variables within individual groups might intentionally be ordered in some other fashion (such as most-common-first or most-important-first). Reported-by: Bruno Haible <bruno@clisp.org> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-02-29add: use unsigned type for collection of bitsEugenio Gigante1-1/+1
The 'refresh' function in 'builtin/add.c' declares 'flags' as signed, and passes it as an argument to the 'refresh_index' function, which though expects an unsigned value. Since in this case 'flags' represents a bag of bits, whose MSB is not used in special ways, change the type of 'flags' to unsigned. Signed-off-by: Eugenio Gigante <giganteeugenio2@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-02-29upload-pack: only accept packfile-uris if we advertised itJeff King2-9/+26
Clients are only supposed to request particular capabilities or features if the server advertised them. For the "packfile-uris" feature, we only advertise it if uploadpack.blobpacfileuri is set, but we always accept a request from the client regardless. In practice this doesn't really hurt anything, as we'd pass the client's protocol list on to pack-objects, which ends up ignoring it. But we should try to follow the protocol spec, and tightening this up may catch buggy or misbehaving clients more easily. Thanks to recent refactoring, we can hoist the config check from upload_pack_advertise() into upload_pack_config(). Note the subtle handling of a value-less bool (which does not count for triggering an advertisement). Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-02-29commit-reach(repo_get_merge_bases_many_dirty): pass on errorsJohannes Schindelin3-16/+16
(Actually, this commit is only about passing on "missing commits" errors, but adding that to the commit's title would have made it too long.) The `merge_bases_many()` function was just taught to indicate parsing errors, and now the `repo_get_merge_bases_many_dirty()` function is aware of that, too. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-02-29commit-reach(repo_get_merge_bases_many): pass on "missing commits" errorsJohannes Schindelin6-28/+31
The `merge_bases_many()` function was just taught to indicate parsing errors, and now the `repo_get_merge_bases_many()` function is aware of that, too. Naturally, there are a lot of callers that need to be adjusted now, too. Next stop: `repo_get_merge_bases_dirty()`. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-02-29commit-reach(get_octopus_merge_bases): pass on "missing commits" errorsJohannes Schindelin5-15/+26
The `merge_bases_many()` function was just taught to indicate parsing errors, and now the `repo_get_merge_bases()` function (which is also surfaced via the `get_merge_bases()` macro) is aware of that, too. Naturally, the callers need to be adjusted now, too. Next step: adjust `repo_get_merge_bases_many()`. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-02-29commit-reach(repo_get_merge_bases): pass on "missing commits" errorsJohannes Schindelin17-51/+96
The `merge_bases_many()` function was just taught to indicate parsing errors, and now the `repo_get_merge_bases()` function (which is also surfaced via the `repo_get_merge_bases()` macro) is aware of that, too. Naturally, there are a lot of callers that need to be adjusted now, too. Next step: adjust the callers of `get_octopus_merge_bases()`. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-02-29commit-reach(get_merge_bases_many_0): pass on "missing commits" errorsJohannes Schindelin1-21/+36
The `merge_bases_many()` function was just taught to indicate parsing errors, and now the `get_merge_bases_many_0()` function is aware of that, too. Next step: adjust the callers of `get_merge_bases_many_0()`. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-02-29commit-reach(merge_bases_many): pass on "missing commits" errorsJohannes Schindelin1-13/+22
The `paint_down_to_common()` function was just taught to indicate parsing errors, and now the `merge_bases_many()` function is aware of that, too. One tricky aspect is that `merge_bases_many()` parses commits of its own, but wants to gracefully handle the scenario where NULL is passed as a merge head, returning the empty list of merge bases. The way this was handled involved calling `repo_parse_commit(NULL)` and relying on it to return an error. This has to be done differently now so that we can handle missing commits correctly by producing a fatal error. Next step: adjust the caller of `merge_bases_many()`: `get_merge_bases_many_0()`. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-02-29commit-reach(paint_down_to_common): start reporting errorsJohannes Schindelin1-21/+45
If a commit cannot be parsed, it is currently ignored when looking for merge bases. That's undesirable as the operation can pretend success in a corrupt repository, even though the command should fail with an error message. Let's start at the bottom of the stack by teaching the `paint_down_to_common()` function to return an `int`: if negative, it indicates fatal error, if 0 success. This requires a couple of callers to be adjusted accordingly. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-02-29commit-reach(paint_down_to_common): prepare for handling shallow commitsJohannes Schindelin1-4/+12
When `git fetch --update-shallow` needs to test for commit ancestry, it can naturally run into a missing object (e.g. if it is a parent of a shallow commit). For the purpose of `--update-shallow`, this needs to be treated as if the child commit did not even have that parent, i.e. the commit history needs to be clamped. For all other scenarios, clamping the commit history is actually a bug, as it would hide repository corruption (for an analysis regarding shallow and partial clones, see the analysis further down). Add a flag to optionally ask the function to ignore missing commits, as `--update-shallow` needs it to, while detecting missing objects as a repository corruption error by default. This flag is needed, and cannot be replaced by `is_repository_shallow()` to indicate that situation, because that function would return 0 in the `--update-shallow` scenario: There is not actually a `shallow` file in that scenario, as demonstrated e.g. by t5537.10 ("add new shallow root with receive.updateshallow on") and t5538.4 ("add new shallow root with receive.updateshallow on"). Note: shallow commits' parents are set to `NULL` internally already, therefore there is no need to special-case shallow repositories here, as the merge-base logic will not try to access parent commits of shallow commits. Likewise, partial clones aren't an issue either: If a commit is missing during the revision walk in the merge-base logic, it is fetched via `promisor_remote_get_direct()`. And not only the single missing commit object: Due to the way the "promised" objects are fetched (in `fetch_objects()` in `promisor-remote.c`, using `fetch --filter=blob:none`), there is no actual way to fetch a single commit object, as the remote side will pass that commit OID to `pack-objects --revs [...]` which in turn passes it to `rev-list` which interprets this as a commit _range_ instead of a single object. Therefore, in partial clones (unless they are shallow in addition), all commits reachable from a commit that is in the local object database are also present in that local database. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-02-28upload-pack: use existing config mechanism for advertisementJeff King1-16/+10
When serving a v2 capabilities request, we call upload_pack_advertise() to tell us the set of features we can advertise to the client. That involves looking at various config options, all of which need to be kept in sync with the rules we use in upload_pack_config to set flags like allow_filter, allow_sideband_all, and so on. If these two pieces of code get out of sync then we may refuse to respect a capability we advertised, or vice versa accept one that we should not. Instead, let's call the same config helper that we'll use for processing the actual client request, and then just pick the values out of the resulting struct. This is only a little bit shorter than the current code, but we don't repeat any policy logic (e.g., we don't have to worry about the magic sideband-all environment variable here anymore). And this reveals a gap in the existing code: there is no struct flag for the packfile-uris capability (we accept it even if it is not advertised, which we should not). We'll leave the advertisement code for now and deal with it in the next patch. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-02-28upload-pack: centralize setup of sideband-all configJeff King1-2/+3
We read uploadpack.allowsidebandall to set a matching flag in our upload_pack_data struct. But for our tests, we also respect GIT_TEST_SIDEBAND_ALL from the environment, and anybody looking at the flag in the struct needs to remember to check both. There's only one such piece of code now, but we're about to add another. So let's have the config step actually fold the environment value into the struct, letting the rest of the code use the flag in the obvious way. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-02-28upload-pack: use repository struct to get configJeff King1-5/+6
Our upload_pack_v2() function gets a repository struct, but we ignore it totally. In practice this doesn't cause any problems, as it will never differ from the_repository. But in the spirit of taking a small step towards getting rid of the_repository, let's at least starting using it to grab config. There are probably other spots that could benefit, but it's a start. Note that we don't need to pass the repo for protected_config(); the whole point there is that we are not looking at repo config, so there is no repo-specific version of the function. For the v0 version of the protocol, we're not passed a repository struct, so we'll continue to use the_repository there. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-02-28upload-pack: free tree buffers after parsingJeff King4-4/+23
When a client sends us a "want" or "have" line, we call parse_object() to get an object struct. If the object is a tree, then the parsed state means that tree->buffer points to the uncompressed contents of the tree. But we don't really care about it. We only really need to parse commits and tags; for trees and blobs, the important output is just a "struct object" with the correct type. But much worse, we do not ever free that tree buffer. It's not leaked in the traditional sense, in that we still have a pointer to it from the global object hash. But if the client requests many trees, we'll hold all of their contents in memory at the same time. Nobody really noticed because it's rare for clients to directly request a tree. It might happen for a lightweight tag pointing straight at a tree, or it might happen for a "tree:depth" partial clone filling in missing trees. But it's also possible for a malicious client to request a lot of trees, causing upload-pack's memory to balloon. For example, without this patch, requesting every tree in git.git like: pktline() { local msg="$*" printf "%04x%s\n" $((1+4+${#msg})) "$msg" } want_trees() { pktline command=fetch printf 0001 git cat-file --batch-all-objects --batch-check='%(objectname) %(objecttype)' | while read oid type; do test "$type" = "tree" || continue pktline want $oid done pktline done printf 0000 } want_trees | GIT_PROTOCOL=version=2 valgrind --tool=massif ./git upload-pack . >/dev/null shows a peak heap usage of ~3.7GB. Which is just about the sum of the sizes of all of the uncompressed trees. For linux.git, it's closer to 17GB. So the obvious thing to do is to call free_tree_buffer() after we realize that we've parsed a tree. We know that upload-pack won't need it later. But let's push the logic into parse_object_with_flags(), telling it to discard the tree buffer immediately. There are two reasons for this. One, all of the relevant call-sites already call the with_options variant to pass the SKIP_HASH flag. So it actually ends up as less code than manually free-ing in each spot. And two, it enables an extra optimization that I'll discuss below. I've touched all of the sites that currently use SKIP_HASH in upload-pack. That drops the peak heap of the upload-pack invocation above from 3.7GB to ~24MB. I've also modified the caller in get_reference(); a partial clone benefits from its use in pack-objects for the reasons given in 0bc2557951 (upload-pack: skip parse-object re-hashing of "want" objects, 2022-09-06), where we were measuring blob requests. But note that the results of get_reference() are used for traversing, as well; so we really would _eventually_ use the tree contents. That makes this at first glance a space/time tradeoff: we won't hold all of the trees in memory at once, but we'll have to reload them each when it comes time to traverse. And here's where our extra optimization comes in. If the caller is not going to immediately look at the tree contents, and it doesn't care about checking the hash, then parse_object() can simply skip loading the tree entirely, just like we do for blobs! And now it's not a space/time tradeoff in get_reference() anymore. It's just a lazy-load: we're delaying reading the tree contents until it's time to actually traverse them one by one. And of course for upload-pack, this optimization means we never load the trees at all, saving lots of CPU time. Timing the "every tree from git.git" request above shows upload-pack dropping from 32 seconds of CPU to 19 (the remainder is mostly due to pack-objects actually sending the pack; timing just the upload-pack portion shows we go from 13s to ~0.28s). These are all highly gamed numbers, of course. For real-world partial-clone requests we're saving only a small bit of time in practice. But it does help harden upload-pack against malicious denial-of-service attacks. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-02-28upload-pack: use PARSE_OBJECT_SKIP_HASH_CHECK in more placesJeff King1-2/+4
In commit 0bc2557951 (upload-pack: skip parse-object re-hashing of "want" objects, 2022-09-06), we optimized the parse_object() calls for v2 "want" lines from the client so that they avoided parsing blobs, and so that they used the commit-graph rather than parsing commit objects from scratch. We should extend that to two other spots: 1. We parse "have" objects in the got_oid() function. These won't generally be non-commits (unlike "want" lines from a partial clone). But we still benefit from the use of the commit-graph. 2. For v0, the "want" lines are parsed in receive_needs(). These are also less likely to be non-commits because by default they have to be ref tips. There are config options you might set to allow non-tip objects, but you'd mostly do so to support partial clones, and clients recent enough to support partial clone will generally speak v2 anyway. So I don't expect this change to improve performance much for day-to-day operations. But both are possible denial-of-service vectors, where an attacker can waste our time by sending over a large number of objects to parse (of course we may waste even more time serving a pack to them, but we try as much as possible to optimize that in pack-objects; we should do what we can here in upload-pack, too). With this patch, running p5600 with GIT_TEST_PROTOCOL_VERSION=0 shows similar results to what we saw in 0bc2557951 (which ran with the v2 protocol by default). Here are the numbers for linux.git: Test HEAD^ HEAD ----------------------------------------------------------------------------- 5600.3: checkout of result 50.91(87.95+2.93) 41.75(79.00+3.18) -18.0% Or for a more extreme (and malicious) case, we can claim to "have" every blob in git.git over the v0 protocol: $ { echo "0032want $(git rev-parse HEAD)" printf 0000 git cat-file --batch-all-objects --batch-check='%(objectname) %(objecttype)' | perl -alne 'print "0032have $F[0]" if $F[1] eq "blob"' } >input $ time ./git.old upload-pack . <input >/dev/null real 0m52.951s user 0m51.633s sys 0m1.304s $ time ./git.new upload-pack . <input >/dev/null real 0m0.261s user 0m0.156s sys 0m0.105s (Note that these don't actually compute a pack because of the hacky protocol usage, so those numbers are representing the raw blob-parsing effort done by upload-pack). Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-02-28upload-pack: always turn off save_commit_bufferJeff King2-2/+2
When the client sends us "want $oid" lines, we call parse_object($oid) to get an object struct. It's important to parse the commits because we need to traverse them in the negotiation phase. But of course we don't need to hold on to the commit messages for each one. We've turned off the save_commit_buffer flag in get_common_commits() for a long time, since f0243f26f6 (git-upload-pack: More efficient usage of the has_sha1 array, 2005-10-28). That helps with the commits we see while actually traversing. But: 1. That function is only used by the v0 protocol. I think the v2 protocol's code path leaves the flag on (and thus pays the extra memory penalty), though I didn't measure it specifically. 2. If the client sends us a bunch of "want" lines, that happens before the negotiation phase. So we'll hold on to all of those commit messages. Generally the number of "want" lines scales with the refs, not with the number of objects in the repo. But a malicious client could send a lot in order to waste memory. As an example of (2), if I generate a request to fetch all commits in git.git like this: pktline() { local msg="$*" printf "%04x%s\n" $((1+4+${#msg})) "$msg" } want_commits() { pktline command=fetch printf 0001 git cat-file --batch-all-objects --batch-check='%(objectname) %(objecttype)' | while read oid type; do test "$type" = "commit" || continue pktline want $oid done pktline done printf 0000 } want_commits | GIT_PROTOCOL=version=2 valgrind --tool=massif git-upload-pack . >/dev/null before this patch upload-pack peaks at ~125MB, and after at ~35MB. The difference is not coincidentally about the same as the sum of all commit object sizes as computed by: git cat-file --batch-all-objects --batch-check='%(objecttype) %(objectsize)' | perl -alne '$v += $F[1] if $F[0] eq "commit"; END { print $v }' In a larger repository like linux.git, that number is ~1GB. In a repository with a full commit-graph file this will have no impact (and the commit graph would save us from parsing at all, so is a much better solution!). But it's easy to do, might help a little in real-world cases (where even if you have a commit graph it might not be fully up to date), and helps a lot for a worst-case malicious request. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-02-28upload-pack: disallow object-info capability by defaultTaylor Blau4-3/+40
We added an "object-info" capability to the v2 upload-pack protocol in a2ba162cda (object-info: support for retrieving object info, 2021-04-20). In the almost 3 years since, we have not added any client-side support, and it does not appear to exist in other implementations either (JGit understands the verb on the server side, but not on the client side). Since this largely unused code is accessible over the network by default, it increases the attack surface of upload-pack. I don't know of any particularly severe problem, but one issue is that because of the request/response nature of the v2 protocol, it will happily read an unbounded number of packets, adding each one to a string list (without regard to whether they are objects we know about, duplicates, etc). This may be something we want to improve in the long run, but in the short term it makes sense to disable the feature entirely. We'll add a config option as an escape hatch for anybody who wants to develop the feature further. A more gentle option would be to add the config option to let people disable it manually, but leave it enabled by default. But given that there's no client side support, that seems like the wrong balance with security. Disabling by default will slow adoption a bit once client-side support does become available (there were some patches[1] in 2022, but nothing got merged and there's been nothing since). But clients have to deal with older servers that do not understand the option anyway (and the capability system handles that), so it will just be a matter of servers flipping their config at that point (and hopefully once any unbounded allocations have been addressed). [jk: this is a patch that GitHub has been running for several years, but rebased forward and with a new commit message for upstream] [1] https://lore.kernel.org/git/20220208231911.725273-1-calvinwan@google.com/ Signed-off-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-02-28upload-pack: accept only a single packfile-uri lineJeff King2-1/+5
When we see a packfile-uri line from the client, we use string_list_split() to split it on commas and store the result in a string_list. A single packfile-uri line is therefore limited to storing ~64kb, the size of a pkt-line. But we'll happily accept multiple such lines, and each line appends to the string list, growing without bound. In theory this could be useful, making: 0017packfile-uris http 0018packfile-uris https equivalent to: 001dpackfile-uris http,https But the protocol documentation doesn't indicate that this should work (and indeed, refers to this in the singular as "the following argument can be included in the client's request"). And the client-side implementation in fetch-pack has always sent a single line (JGit appears to understand the line on the server side but has no client-side implementation, and libgit2 understands neither). If we were worried about compatibility, we could instead just put a limit on the maximum number of values we'd accept. The current client implementation limits itself to only two values: "http" and "https", so something like "256" would be more than enough. But accepting only a single line seems more in line with the protocol documentation, and matches other parts of the protocol (e.g., we will not accept a second "filter" line). We'll also make this more explicit in the protocol documentation; as above, I think this was always the intent, but there's no harm in making it clear. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-02-28upload-pack: use a strmap for want-ref linesJeff King2-14/+19
When the "ref-in-want" capability is advertised (which it is not by default), then upload-pack processes a "want-ref" line from the client by checking that the name is a valid ref and recording it in a string-list. In theory this list should grow no larger than the number of refs in the server-side repository. But since we don't do any de-duplication, a client which sends "want-ref refs/heads/foo" over and over will cause the array to grow without bound. We can fix this by switching to strmap, which efficiently detects duplicates. There are two client-visible changes here: 1. The "wanted-refs" response will now be in an apparently-random order (based on iterating the hashmap) rather than the order given by the client. The protocol documentation is quiet on ordering here. The current fetch-pack implementation is happy with any order, as it looks up each returned ref using a binary search in its local sorted list. JGit seems to implement want-ref on the server side, but has no client-side support. libgit2 doesn't support either side. It would obviously be possible to record the original order or to use the strmap as an auxiliary data structure. But if the client doesn't care, we may as well do the simplest thing. 2. We'll now reject duplicates explicitly as a protocol error. The client should never send them (and our current implementation, even when asked to "git fetch master:one master:two" will de-dup on the client side). If we wanted to be more forgiving, we could perhaps just throw away the duplicates. But then our "wanted-refs" response back to the client would omit the duplicates, and it's hard to say what a client that accidentally sent a duplicate would do with that. So I think we're better off to complain loudly before anybody accidentally writes such a client. Let's also add a note to the protocol documentation clarifying that duplicates are forbidden. As discussed above, this was already the intent, but it's not very explicit. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-02-28upload-pack: use oidset for deepen_not listJeff King1-10/+11
We record the oid of every deepen-not line the client sends to us. For a well-behaved client, the resulting array should be bounded by the number of unique refs we have. But because there's no de-duplication, a malicious client can cause the array to grow unbounded by just sending the same "refs/heads/foo" over and over (assuming such a ref exists). Since the deepen-not list is just being fed to a "rev-list --not" traversal, the order of items doesn't matter. So we can replace the oid_array with an oidset which notices and skips duplicates. That bounds the memory in malicious cases to be linear in the number of unique refs. And even in non-malicious cases, there may be a slight improvement in memory usage if multiple refs point to the same oid (though in practice this list is probably pretty tiny anyway, as it comes from the user specifying "--shallow-exclude" on the client fetch). Note that in the trace2 output we'll now output the number of de-duplicated objects, rather than the total number of "deepen-not" lines we received. This is arguably a more useful value for tracing / debugging anyway. Reported-by: Benjamin Flesch <benjaminflesch@icloud.com> Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-02-28upload-pack: switch deepen-not list to an oid_arrayJeff King1-7/+7
When we see a "deepen-not" line from the client, we verify that the given name can be resolved as a ref, and then add it to a string list to be passed later to an internal "rev-list --not" traversal. We record the actual refname in the string list (so the traversal resolves it again later), but we'd be better off recording the resolved oid: 1. There's a tiny bit of wasted work in resolving it twice. 2. There's a small race condition with simultaneous updates; the later traversal may resolve to a different value (or not at all). This shouldn't cause any bad behavior (we do not care about the value in this first resolution, so whatever value rev-list gets is OK) but it could mean a confusing error message (if upload-pack fails to resolve the ref it produces a useful message, but a failing traversal later results in just "revision walk setup failed"). 3. It makes it simpler to de-duplicate the results. We don't de-dup at all right now, but we will in the next patch. >From the client's perspective the behavior should be the same. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>