{"thread":{"id":"56409","subject":"[PATCH 01/15] scalar: create a rudimentary executable","startedAt":"2021-08-30T21:34:54Z","lastAt":"2021-12-14T13:18:05Z","messageCount":303,"participants":["Johannes Schindelin via GitGitGadget","Derrick Stolee via GitGitGadget","Matthew John Cheetham via GitGitGadget","Derrick Stolee","Eric Sunshine","Ævar Arnfjörð Bjarmason","Elijah Newren","Junio C Hamano","Johannes Schindelin","Bagas Sanjaya","Theodore Ts'o","Matt Rogers","Jeff King"],"isPatch":true,"patchVersion":1,"patchTotal":15},"messages":[{"id":"434183","messageId":"pull.1005.git.1630359290.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":null,"subject":"[PATCH 00/15] [RFC] Upstreaming the Scalar command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-08-30T21:34:35Z","receivedAt":"2021-08-30T21:34:54Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"tl;dr: This series contributes the Scalar command to the Git project. This\ncommand provides an opinionated way to create and configure repositories\nwith a focus on very large repositories.\n\n\nBackground\n==========\n\nYears ago, Microsoft wanted to move the source code of the Windows operating\nsystem to Git. The challenge there was to prove that Git could scale to\nmassive monorepos. The VFS for Git (formerly GVFS) project was born to take\nup that challenge.\n\nThe final solution included a virtual filesystem (with both user-mode and\nkernel components) and a customized fork of Git for Windows. This solution\ncontained several key concepts, such as only populating a portion of the\nworking directory, demand-fetching blobs, and performing periodic repo\nmaintenance in the background. However, the required kernel drivers made it\ndifficult to port the solution to other platforms.\n\nBut it was realized that many of these key concepts were independent of the\nactual VFS and its projection of the working directory. The Scalar project\nwas created to make that separation, refine the key concepts, and then\nextract those features into the new Scalar command.\n\n\nThe present\n===========\n\nThe Scalar project provides a completely functional non-virtual experience\nfor monorepos. But why stop there. The Scalar project was designed to be a\nself-destructing vehicle to allow those key concepts to be moved into core\nGit itself for the benefit of all. For example, partial clone,\nsparse-checkout, and background maintenance have already been upstreamed and\nremoved from Scalar proper. This patch series provides a C-based\nimplementation of the final remaining portions of the Scalar command. This\nwill make it easier for users to experiment with the Scalar command. It will\nalso make it substantially easier to experiment with moving functionality\nfrom Scalar into core Git, while maintaining backwards-compatibility for\nexisting Scalar users.\n\nThe C-based Scalar has been shipped to Scalar users, and can be tested by\nany interested reader:\nhttps://github.com/microsoft/git/releases/tag/v2.33.0.vfs.0.0 (it offers a\nGit for Windows installer, a macOS package and an Ubuntu package).\n\n\nOpportunities\n=============\n\nApart from providing the Scalar command, this contribution is intended to\nserve as a basis for further mailing list discussions on moving (some of)\nthese key concepts into the main Git commands.\n\nFor example, we previously discussed the idea of a \"git big-clone\" that does\nmuch of what \"scalar clone\" is doing. This patch series is a step to make\nsuch functionality exist in the Git code base while we simmer on what such a\n\"git big-clone\" command-line interface would look like.\n\nThis is one of many possible ways to do this. Creating a 'git big-clone'\ncould lock Git into backwards compatibility concerns so it is necessary to\napproach such an endeavor with caution. As a discussion starter, the scalar\nclone <url> command does roughly this:\n\n 1. git clone --sparse --filter=blob:none /src\n 2. git -C /src sparse-checkout init --cone\n 3. git -C /src config (many times)\n 4. git -C /src maintenance start\n\nIt is my hope inspire discussions about what parts of Scalar could go into\ncore Git, and where, and in which form. While we wish to maintain\nbackwards-compatibility of Scalar's command-line interface (because it is\nalready in use), by having the Scalar code in the same code base as Git's,\nit will be much easier to move functionality without having to maintain\nloose version coupling between independently-versioned Scalar and Git. The\ntight version-coupling, along with having access to libgit.a also allows the\nC-based implementation of Scalar to be much smaller than the original .NET\nversion.\n\nFor example, we might choose in the future to implement, say, git clone\n--scale=partial,cone to initialize a partial clone with a cone-sparse\ncheckout, that would not only be totally doable, and not only would we\nalready have precedent and data to prove that this actually makes engineers\nhappy who have to work on ginormous repositories, but we could then also\nimplement it by moving parts of contrib/scalar/ to builtin/ (where\ncontrib/scalar/ would then call the built-ins accordingly rather than\nhard-coding the defaults itself).\n\nWe now also have the opportunity to discuss the merits of Scalar's clone\ncaching, which is not actually part of this patch series because it is a bit\ncoupled with the GVFS parts of microsoft/git for the moment, where clones\nautomatically get registered with a populated alternate repository that is\nidentified by the URL, meaning: subsequent clones of the same repository are\nvastly faster than the first one because they do not actually download the\nalready-received objects again, they access the cache instead.\n\nAnother thing that I could imagine to be discussed at length is the\ndistinction between enlistment and worktree (where the latter is the actual\nGit worktree and usually lives in the src/ subdirectory of the former). This\nencourages untracked and ignored files to be placed outside the worktree,\nmaking Git's job much easier. This idea, too, might find its way in one way\nor another into Git proper.\n\nThese are just a few concepts in Scalar that do not yet have equivalents in\nGit. By putting this initial implementation into contrib/, we create a\nfoundation for future discussions of these concepts.\n\nWe plan on updating the recommended config settings in scalar register as\nnew Git features are available (such as builtin FSMonitor and sparse-index,\nwhen ready). To facilitate upgrading existing Scalar enlistments, their\npaths are automatically added to the [scalar] section of the global Git\nconfig, and the scalar reconfigure --all command will process all of them.\n\n\nEpilogue\n========\n\nNow, to address some questions that I imagine every reader has who made it\nthis far:\n\n * Why not put the Scalar functionality directly into a built-in? Creating a\n   Git builtin requires scrutiny over every aspect of the feature, which is\n   difficult to do while also maintaining the command-line interface\n   contract and expected behavior of the Scalar command (there are existing\n   users, after all). By having the Scalar command in contrib/, we present a\n   simple option for users to have these features in the short term while\n   the Git contributor community decides which bits to absorb into Git\n   built-ins.\n * Why implement the Scalar command in the Git codebase? We ported Scalar to\n   the microsoft/git fork for several reasons. First, we realized it was\n   possible now that the core features exist inside Git itself. Second,\n   compiling Scalar directly within a version of Git allows us to remove a\n   version compatibility check from each config option that might or might\n   not apply based on the installed Git version. Finally, this new location\n   has greatly simplified our release process and the installation process\n   for users. We now have ways to install Scalar with microsoft/git via\n   winget, brew, and apt-get. This has been the case since we shipped\n   v2.32.0 to our users, read: this setup has served us well already.\n * Why contribute Scalar to the Git project? We are biased, of course, yet\n   we do have evidence that the Scalar command is a helpful tool that offers\n   an simple way to handle huge repositories with ease. By contributing it\n   to the core Git project, we are able to share it with more users,\n   especially some users who do not want to install the microsoft/git fork.\n   We intend to include Scalar as a component in git-for-windows/git, but\n   are contributing it here first. Further, we think there is benefit to the\n   Git developer community as this presents an example of how to set certain\n   defaults that work for large repositories.\n * Does this integrate with the built-in FSMonitor yet? No, not yet. I do\n   have a couple of add-on patch series lined up, one of them being the\n   integration with the built-in FSMonitor, which obviously has to wait\n   until the FSMonitor patch series advances further.\n\nDerrick Stolee (4):\n  scalar: 'register' sets recommended config and starts maintenance\n  scalar: 'unregister' stops background maintenance\n  scalar: implement 'scalar list'\n  scalar: implement the `run` command\n\nJohannes Schindelin (10):\n  scalar: create a rudimentary executable\n  scalar: start documenting the command\n  scalar: create test infrastructure\n  scalar: let 'unregister' handle a deleted enlistment directory\n    gracefully\n  scalar: implement the `clone` subcommand\n  scalar: teach 'clone' to support the --single-branch option\n  scalar: allow reconfiguring an existing enlistment\n  scalar: teach 'reconfigure' to optionally handle all registered\n    enlistments\n  scalar: implement the `version` command\n  scalar: accept -C and -c options before the subcommand\n\nMatthew John Cheetham (1):\n  scalar: implement the `delete` command\n\n Makefile                         |   8 +\n contrib/scalar/.gitignore        |   5 +\n contrib/scalar/Makefile          |  57 +++\n contrib/scalar/scalar.c          | 844 +++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        | 152 ++++++\n contrib/scalar/t/Makefile        |  78 +++\n contrib/scalar/t/t9099-scalar.sh |  88 ++++\n 7 files changed, 1232 insertions(+)\n create mode 100644 contrib/scalar/.gitignore\n create mode 100644 contrib/scalar/Makefile\n create mode 100644 contrib/scalar/scalar.c\n create mode 100644 contrib/scalar/scalar.txt\n create mode 100644 contrib/scalar/t/Makefile\n create mode 100755 contrib/scalar/t/t9099-scalar.sh\n\n\nbase-commit: ebf3c04b262aa27fbb97f8a0156c2347fecafafb\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1005%2Fdscho%2Fscalar-the-beginning-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1005/dscho/scalar-the-beginning-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/1005\n-- \ngitgitgadget\n"},{"id":"434181","messageId":"b8c7d3f84508ae0fb300f47c726764f4cbf46be9.1630359290.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.git.1630359290.gitgitgadget@gmail.com","subject":"[PATCH 01/15] scalar: create a rudimentary executable","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-08-30T21:34:36Z","receivedAt":"2021-08-30T21:34:55Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe idea of Scalar (https://github.com/microsoft/scalar), and before\nthat, of VFS for Git, has always been to prove that Git _can_ scale, and\nto upstream whatever strategies have been demonstrated to help.\n\nWith this patch, we start the journey from that C# project to move what\nis left to Git's own `contrib/` directory, reimplementing it in pure C,\nwith the intention to facilitate integrating the functionality into core\nGit all while maintaining backwards-compatibility for existing Scalar\nusers (which will be much easier when both live in the same worktree).\nIt was always to plan to contribute all of the proven strategies back to\ncore Git.\n\nFor example, while the virtual filesystem provided by VFS for Git helped\nthe team developing the Windows operating system to move onto Git, while\ntrying to upstream it we realized that it cannot be done: getting the\nvirtual filesystem to work (which we only managed to implement fully on\nWindows, but not on, say, macOS or Linux), and the required server-side\nsupport for the GVFS protocol, made this not quite feasible.\n\nThe Scalar project learned from that and tackled the problem with\ndifferent tactics: instead of pretending to Git that the working\ndirectory is fully populated, it _specifically_ teaches Git about\npartial clone (which is based on VFS for Git's cache server), about\nsparse checkout (which VFS for Git tried to do transparently, in the\nfile system layer), and regularly runs maintenance tasks to keep the\nrepository in a healthy state.\n\nWith partial clone, sparse checkout and `git maintenance` having been\nupstreamed, there is little left that `scalar.exe` does that which\n`git.exe` cannot do. One such thing is that `scalar clone <url>` will\nautomatically set up a partial, sparse clone, and configure\nknown-helpful settings from the start.\n\nSo let's bring this convenience into Git's tree.\n\nThe idea here is that you can (optionally) build Scalar via\n\n\tmake -C contrib/scalar/Makefile\n\nThis will build the `scalar` executable and put it into the\ncontrib/scalar/ subdirectory.\n\nThe slightly awkward addition of the `contrib/scalar/*` bits to the\ntop-level `Makefile` are actually really required: we want to link to\n`libgit.a`, which means that we will need to use the very same `CFLAGS`\nand `LDFLAGS` as the rest of Git.\n\nAn early development version of this patch tried to replicate all the\nconditional code in `contrib/scalar/Makefile` (e.g. `NO_POLL`) just like\n`contrib/svn-fe/Makefile` used to do before it was retired. It turned\nout to be quite the whack-a-mole game: the SHA-1-related flags, the\nflags enabling/disabling `compat/poll/`, `compat/regex/`,\n`compat/win32mmap.c` & friends depending on the current platform... To\nput it mildly: it was a major mess.\n\nInstead, this patch makes minimal changes to the top-level `Makefile` so\nthat the bits in `contrib/scalar/` can be compiled and linked, and\nadds a `contrib/scalar/Makefile` that uses the top-level `Makefile` in a\nmost minimal way to do the actual compiling.\n\nNote: With this commit, we only establish the infrastructure, no\nScalar functionality is implemented yet; We will do that incrementally\nover the next few commits.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Makefile                  |  8 ++++++++\n contrib/scalar/.gitignore |  2 ++\n contrib/scalar/Makefile   | 34 ++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.c   | 36 ++++++++++++++++++++++++++++++++++++\n 4 files changed, 80 insertions(+)\n create mode 100644 contrib/scalar/.gitignore\n create mode 100644 contrib/scalar/Makefile\n create mode 100644 contrib/scalar/scalar.c\n\ndiff --git a/Makefile b/Makefile\nindex c3565fc0f8f..2d5c822f7a8 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2447,6 +2447,10 @@ endif\n .PHONY: objects\n objects: $(OBJECTS)\n \n+SCALAR_SOURCES := contrib/scalar/scalar.c\n+SCALAR_OBJECTS := $(SCALAR_SOURCES:c=o)\n+OBJECTS += $(SCALAR_OBJECTS)\n+\n dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d)\n dep_dirs := $(addsuffix .depend,$(sort $(dir $(OBJECTS))))\n \n@@ -2586,6 +2590,10 @@ $(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n \t\t$(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)\n \n+contrib/scalar/scalar$X: $(SCALAR_OBJECTS) GIT-LDFLAGS $(GITLIBS)\n+\t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \\\n+\t\t$(filter %.o,$^) $(LIBS)\n+\n $(LIB_FILE): $(LIB_OBJS)\n \t$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^\n \ndiff --git a/contrib/scalar/.gitignore b/contrib/scalar/.gitignore\nnew file mode 100644\nindex 00000000000..ff3d47e84d0\n--- /dev/null\n+++ b/contrib/scalar/.gitignore\n@@ -0,0 +1,2 @@\n+/*.exe\n+/scalar\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nnew file mode 100644\nindex 00000000000..40c03ad10e1\n--- /dev/null\n+++ b/contrib/scalar/Makefile\n@@ -0,0 +1,34 @@\n+QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir\n+QUIET_SUBDIR1  =\n+\n+ifneq ($(findstring s,$(MAKEFLAGS)),s)\n+ifndef V\n+\tQUIET_SUBDIR0  = +@subdir=\n+\tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n+\t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n+else\n+\texport V\n+endif\n+endif\n+\n+all:\n+\n+include ../../config.mak.uname\n+-include ../../config.mak.autogen\n+-include ../../config.mak\n+\n+TARGETS = scalar$(X) scalar.o\n+GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n+\n+all: scalar$X\n+\n+$(GITLIBS):\n+\t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)\n+\n+$(TARGETS): $(GITLIBS) scalar.c\n+\t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)\n+\n+clean:\n+\t$(RM) $(TARGETS)\n+\n+.PHONY: all clean FORCE\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nnew file mode 100644\nindex 00000000000..7cff29e0fcd\n--- /dev/null\n+++ b/contrib/scalar/scalar.c\n@@ -0,0 +1,36 @@\n+/*\n+ * The Scalar command-line interface.\n+ */\n+\n+#include \"cache.h\"\n+#include \"gettext.h\"\n+#include \"parse-options.h\"\n+\n+static struct {\n+\tconst char *name;\n+\tint (*fn)(int, const char **);\n+} builtins[] = {\n+\t{ NULL, NULL},\n+};\n+\n+int cmd_main(int argc, const char **argv)\n+{\n+\tstruct strbuf scalar_usage = STRBUF_INIT;\n+\tint i;\n+\n+\tif (argc > 1) {\n+\t\targv++;\n+\t\targc--;\n+\n+\t\tfor (i = 0; builtins[i].name; i++)\n+\t\t\tif (!strcmp(builtins[i].name, argv[0]))\n+\t\t\t\treturn !!builtins[i].fn(argc, argv);\n+\t}\n+\n+\tstrbuf_addstr(&scalar_usage,\n+\t\t      N_(\"scalar <command> [<options>]\\n\\nCommands:\\n\"));\n+\tfor (i = 0; builtins[i].name; i++)\n+\t\tstrbuf_addf(&scalar_usage, \"\\t%s\\n\", builtins[i].name);\n+\n+\tusage(scalar_usage.buf);\n+}\n-- \ngitgitgadget\n\n"},{"id":"434182","messageId":"4f886575dcfb97d5bbd04fe7551e03935ac0af69.1630359290.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.git.1630359290.gitgitgadget@gmail.com","subject":"[PATCH 02/15] scalar: start documenting the command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-08-30T21:34:37Z","receivedAt":"2021-08-30T21:34:56Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis commit establishes the infrastructure to build the manual page for\nthe `scalar` command.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/.gitignore |  3 +++\n contrib/scalar/Makefile   | 14 +++++++++++++-\n contrib/scalar/scalar.txt | 38 ++++++++++++++++++++++++++++++++++++++\n 3 files changed, 54 insertions(+), 1 deletion(-)\n create mode 100644 contrib/scalar/scalar.txt\n\ndiff --git a/contrib/scalar/.gitignore b/contrib/scalar/.gitignore\nindex ff3d47e84d0..00441073f59 100644\n--- a/contrib/scalar/.gitignore\n+++ b/contrib/scalar/.gitignore\n@@ -1,2 +1,5 @@\n+/*.xml\n+/*.1\n+/*.html\n /*.exe\n /scalar\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nindex 40c03ad10e1..85c186634e9 100644\n--- a/contrib/scalar/Makefile\n+++ b/contrib/scalar/Makefile\n@@ -6,6 +6,7 @@ ifndef V\n \tQUIET_SUBDIR0  = +@subdir=\n \tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n \t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n+\tQUIET          = @\n else\n \texport V\n endif\n@@ -30,5 +31,16 @@ $(TARGETS): $(GITLIBS) scalar.c\n \n clean:\n \t$(RM) $(TARGETS)\n+\t$(RM) scalar.1 scalar.html scalar.xml\n \n-.PHONY: all clean FORCE\n+docs: scalar.html scalar.1\n+\n+scalar.html: | scalar.1 # prevent them from trying to build `doc.dep` in parallel\n+\n+scalar.html scalar.1: scalar.txt\n+\t$(QUIET_SUBDIR0)../../Documentation$(QUIET_SUBDIR1) \\\n+\t\tMAN_TXT=../contrib/scalar/scalar.txt \\\n+\t\t../contrib/scalar/$@\n+\t$(QUIET)test scalar.1 != \"$@\" || mv ../../Documentation/$@ .\n+\n+.PHONY: all clean docs FORCE\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nnew file mode 100644\nindex 00000000000..5f7131861a5\n--- /dev/null\n+++ b/contrib/scalar/scalar.txt\n@@ -0,0 +1,38 @@\n+scalar(1)\n+=========\n+\n+NAME\n+----\n+scalar - an opinionated repository management tool\n+\n+SYNOPSIS\n+--------\n+[verse]\n+scalar <command> [<options>]\n+\n+DESCRIPTION\n+-----------\n+\n+Scalar is an opinionated repository management tool. By creating new\n+repositories or registering existing repositories with Scalar, your Git\n+experience will speed up. Scalar sets advanced Git config settings,\n+maintains your repositories in the background, and helps reduce data sent\n+across the network.\n+\n+An important Scalar concept is the enlistment: this is the top-level directory\n+of the project. It usually contains the subdirectory `src/` which is a Git\n+worktree. This encourages the separation between tracked files (inside `src/`)\n+and untracked files, such as build artifacts (outside `src/`). When registering\n+an existing Git worktree with Scalar whose name is not `src`, the enlistment\n+will be identical to the worktree.\n+\n+The `scalar` command implements various subcommands, and different options\n+depending on the subcommand.\n+\n+SEE ALSO\n+--------\n+linkgit:git-maintenance[1].\n+\n+Scalar\n+---\n+Associated with the linkgit:git[1] suite\n-- \ngitgitgadget\n\n"},{"id":"434186","messageId":"bcfde9bc7651266deaa0e5036cebd058dffc12a7.1630359290.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.git.1630359290.gitgitgadget@gmail.com","subject":"[PATCH 03/15] scalar: create test infrastructure","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-08-30T21:34:38Z","receivedAt":"2021-08-30T21:34:59Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nTo test the Scalar command, create a test script in contrib/scalar/t\nthat is executed as `make -C contrib/scalar test`. Since Scalar has no\nmeaningful capabilities yet, the only test is rather simple. We will add\nmore tests in subsequent commits that introduce corresponding, new\nfunctionality.\n\nNote: this test script is intended to test `scalar` only lightly, even\nafter all of the functionality is implemented.\n\nA more comprehensive functional (or: integration) test suite can be\nfound at https://github.com/microsoft/scalar; It is used in the workflow\nhttps://github.com/microsoft/git/blob/HEAD/.github/workflows/scalar-functional-tests.yml\nin Microsoft's Git fork. This test suite performs end-to-end tests with\na real remote repository, and is run as part of the regular CI builds.\nSince those tests require some functionality supported only by\nMicrosoft's Git fork (\"GVFS protocol\"), there is no intention to port\nthat fuller test suite to `contrib/scalar/`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/Makefile          | 17 +++++--\n contrib/scalar/t/Makefile        | 78 ++++++++++++++++++++++++++++++++\n contrib/scalar/t/t9099-scalar.sh | 17 +++++++\n 3 files changed, 109 insertions(+), 3 deletions(-)\n create mode 100644 contrib/scalar/t/Makefile\n create mode 100755 contrib/scalar/t/t9099-scalar.sh\n\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nindex 85c186634e9..8620042f281 100644\n--- a/contrib/scalar/Makefile\n+++ b/contrib/scalar/Makefile\n@@ -3,6 +3,7 @@ QUIET_SUBDIR1  =\n \n ifneq ($(findstring s,$(MAKEFLAGS)),s)\n ifndef V\n+\tQUIET_GEN      = @echo '   ' GEN $@;\n \tQUIET_SUBDIR0  = +@subdir=\n \tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n \t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n@@ -21,7 +22,7 @@ include ../../config.mak.uname\n TARGETS = scalar$(X) scalar.o\n GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n \n-all: scalar$X\n+all: scalar$X ../../bin-wrappers/scalar\n \n $(GITLIBS):\n \t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)\n@@ -30,9 +31,19 @@ $(TARGETS): $(GITLIBS) scalar.c\n \t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)\n \n clean:\n-\t$(RM) $(TARGETS)\n+\t$(RM) $(TARGETS) ../../bin-wrappers/scalar\n \t$(RM) scalar.1 scalar.html scalar.xml\n \n+../../bin-wrappers/scalar: ../../wrap-for-bin.sh Makefile\n+\t@mkdir -p ../../bin-wrappers\n+\t$(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \\\n+\t     -e 's|@@BUILD_DIR@@|$(shell cd ../.. && pwd)|' \\\n+\t     -e 's|@@PROG@@|contrib/scalar/scalar$(X)|' < $< > $@ && \\\n+\tchmod +x $@\n+\n+test: all\n+\t$(MAKE) -C t\n+\n docs: scalar.html scalar.1\n \n scalar.html: | scalar.1 # prevent them from trying to build `doc.dep` in parallel\n@@ -43,4 +54,4 @@ scalar.html scalar.1: scalar.txt\n \t\t../contrib/scalar/$@\n \t$(QUIET)test scalar.1 != \"$@\" || mv ../../Documentation/$@ .\n \n-.PHONY: all clean docs FORCE\n+.PHONY: all clean docs test FORCE\ndiff --git a/contrib/scalar/t/Makefile b/contrib/scalar/t/Makefile\nnew file mode 100644\nindex 00000000000..6170672bb37\n--- /dev/null\n+++ b/contrib/scalar/t/Makefile\n@@ -0,0 +1,78 @@\n+# Run scalar tests\n+#\n+# Copyright (c) 2005,2021 Junio C Hamano, Johannes Schindelin\n+#\n+\n+-include ../../../config.mak.autogen\n+-include ../../../config.mak\n+\n+SHELL_PATH ?= $(SHELL)\n+PERL_PATH ?= /usr/bin/perl\n+RM ?= rm -f\n+PROVE ?= prove\n+DEFAULT_TEST_TARGET ?= test\n+TEST_LINT ?= test-lint\n+\n+ifdef TEST_OUTPUT_DIRECTORY\n+TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results\n+else\n+TEST_RESULTS_DIRECTORY = ../../../t/test-results\n+endif\n+\n+# Shell quote;\n+SHELL_PATH_SQ = $(subst ','\\'',$(SHELL_PATH))\n+PERL_PATH_SQ = $(subst ','\\'',$(PERL_PATH))\n+TEST_RESULTS_DIRECTORY_SQ = $(subst ','\\'',$(TEST_RESULTS_DIRECTORY))\n+\n+T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))\n+\n+all: $(DEFAULT_TEST_TARGET)\n+\n+test: $(TEST_LINT)\n+\t$(MAKE) aggregate-results-and-cleanup\n+\n+prove: $(TEST_LINT)\n+\t@echo \"*** prove ***\"; GIT_CONFIG=.git/config $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)\n+\t$(MAKE) clean-except-prove-cache\n+\n+$(T):\n+\t@echo \"*** $@ ***\"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)\n+\n+clean-except-prove-cache:\n+\t$(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)'\n+\t$(RM) -r valgrind/bin\n+\n+clean: clean-except-prove-cache\n+\t$(RM) .prove\n+\n+test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax\n+\n+test-lint-duplicates:\n+\t@dups=`echo $(T) | tr ' ' '\\n' | sed 's/-.*//' | sort | uniq -d` && \\\n+\t\ttest -z \"$$dups\" || { \\\n+\t\techo >&2 \"duplicate test numbers:\" $$dups; exit 1; }\n+\n+test-lint-executable:\n+\t@bad=`for i in $(T); do test -x \"$$i\" || echo $$i; done` && \\\n+\t\ttest -z \"$$bad\" || { \\\n+\t\techo >&2 \"non-executable tests:\" $$bad; exit 1; }\n+\n+test-lint-shell-syntax:\n+\t@'$(PERL_PATH_SQ)' ../../../t/check-non-portable-shell.pl $(T)\n+\n+aggregate-results-and-cleanup: $(T)\n+\t$(MAKE) aggregate-results\n+\t$(MAKE) clean\n+\n+aggregate-results:\n+\tfor f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \\\n+\t\techo \"$$f\"; \\\n+\tdone | '$(SHELL_PATH_SQ)' ../../../t/aggregate-results.sh\n+\n+valgrind:\n+\t$(MAKE) GIT_TEST_OPTS=\"$(GIT_TEST_OPTS) --valgrind\"\n+\n+test-results:\n+\tmkdir -p test-results\n+\n+.PHONY: $(T) aggregate-results clean valgrind\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nnew file mode 100755\nindex 00000000000..16f2b72b126\n--- /dev/null\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -0,0 +1,17 @@\n+#!/bin/sh\n+\n+test_description='test the `scalar` command'\n+\n+TEST_DIRECTORY=$PWD/../../../t\n+export TEST_DIRECTORY\n+\n+# Make it work with --no-bin-wrappers\n+PATH=$PWD/..:$PATH\n+\n+. ../../../t/test-lib.sh\n+\n+test_expect_success 'scalar shows a usage' '\n+\ttest_expect_code 129 scalar -h\n+'\n+\n+test_done\n-- \ngitgitgadget\n\n"},{"id":"434184","messageId":"3786f4c597fffc13f638efd26875dcb257d54ab4.1630359290.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.git.1630359290.gitgitgadget@gmail.com","subject":"[PATCH 04/15] scalar: 'register' sets recommended config and starts maintenance","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-08-30T21:34:39Z","receivedAt":"2021-08-30T21:35:00Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nLet's start implementing the `register` command. With this commit,\nrecommended settings are configured upon `scalar register`, and Git's\nbackground maintenance is started.\n\nThe recommended config settings may very well change in the future. For\nexample, once the built-in FSMonitor is available, we will want to\nenable it upon `scalar register`. For that reason, we explicitly support\nrunning `scalar register` in an already-registered enlistment.\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 256 ++++++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt |  14 ++-\n 2 files changed, 269 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 7cff29e0fcd..7660327c27b 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -5,11 +5,267 @@\n #include \"cache.h\"\n #include \"gettext.h\"\n #include \"parse-options.h\"\n+#include \"config.h\"\n+#include \"run-command.h\"\n+\n+/*\n+ * Remove the deepest subdirectory in the provided path string. Path must not\n+ * include a trailing path separator. Returns 1 if parent directory found,\n+ * otherwise 0.\n+ */\n+static int strbuf_parent_directory(struct strbuf *buf)\n+{\n+\tsize_t len = buf->len;\n+\tsize_t offset = offset_1st_component(buf->buf);\n+\tchar *path_sep = find_last_dir_sep(buf->buf + offset);\n+\tstrbuf_setlen(buf, path_sep ? path_sep - buf->buf : offset);\n+\n+\treturn buf->len < len;\n+}\n+\n+static void setup_enlistment_directory(int argc, const char **argv,\n+\t\t\t\t       const char * const *usagestr,\n+\t\t\t\t       const struct option *options,\n+\t\t\t\t       struct strbuf *enlistment_root)\n+{\n+\tstruct strbuf path = STRBUF_INIT;\n+\tchar *root;\n+\tint enlistment_found = 0;\n+\n+\tif (startup_info->have_repository)\n+\t\tBUG(\"gitdir already set up?!?\");\n+\n+\tif (argc > 1)\n+\t\tusage_with_options(usagestr, options);\n+\n+\t/* find the worktree, determine its corresponding root */\n+\tif (argc == 1)\n+\t\tstrbuf_add_absolute_path(&path, argv[0]);\n+\telse if (strbuf_getcwd(&path) < 0)\n+\t\tdie(_(\"need a working directory\"));\n+\n+\tstrbuf_trim_trailing_dir_sep(&path);\n+\tdo {\n+\t\tconst size_t len = path.len;\n+\n+\t\t/* check if currently in enlistment root with src/ workdir */\n+\t\tstrbuf_addstr(&path, \"/src/.git\");\n+\t\tif (is_git_directory(path.buf)) {\n+\t\t\tstrbuf_strip_suffix(&path, \"/.git\");\n+\n+\t\t\tif (enlistment_root)\n+\t\t\t\tstrbuf_add(enlistment_root, path.buf, len);\n+\n+\t\t\tenlistment_found = 1;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\t/* reset to original path */\n+\t\tstrbuf_setlen(&path, len);\n+\n+\t\t/* check if currently in workdir */\n+\t\tstrbuf_addstr(&path, \"/.git\");\n+\t\tif (is_git_directory(path.buf)) {\n+\t\t\tstrbuf_setlen(&path, len);\n+\n+\t\t\tif (enlistment_root) {\n+\t\t\t\t/*\n+\t\t\t\t * If the worktree's directory's name is `src`, the enlistment is the\n+\t\t\t\t * parent directory, otherwise it is identical to the worktree.\n+\t\t\t\t */\n+\t\t\t\troot = strip_path_suffix(path.buf, \"src\");\n+\t\t\t\tstrbuf_addstr(enlistment_root, root ? root : path.buf);\n+\t\t\t\tfree(root);\n+\t\t\t}\n+\n+\t\t\tenlistment_found = 1;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tstrbuf_setlen(&path, len);\n+\t} while (strbuf_parent_directory(&path));\n+\n+\tif (!enlistment_found)\n+\t\tdie(_(\"could not find enlistment root\"));\n+\n+\tif (chdir(path.buf) < 0)\n+\t\tdie_errno(_(\"could not switch to '%s'\"), path.buf);\n+\n+\tstrbuf_release(&path);\n+\tsetup_git_directory();\n+}\n+\n+static int run_git(const char *arg, ...)\n+{\n+\tstruct strvec argv = STRVEC_INIT;\n+\tva_list args;\n+\tconst char *p;\n+\tint res;\n+\n+\tva_start(args, arg);\n+\tstrvec_push(&argv, arg);\n+\twhile ((p = va_arg(args, const char *)))\n+\t\tstrvec_push(&argv, p);\n+\tva_end(args);\n+\n+\tres = run_command_v_opt(argv.v, RUN_GIT_CMD);\n+\n+\tstrvec_clear(&argv);\n+\treturn res;\n+}\n+\n+static int set_recommended_config(void)\n+{\n+\tstruct {\n+\t\tconst char *key;\n+\t\tconst char *value;\n+\t} config[] = {\n+\t\t{ \"am.keepCR\", \"true\" },\n+\t\t{ \"core.FSCache\", \"true\" },\n+\t\t{ \"core.multiPackIndex\", \"true\" },\n+\t\t{ \"core.preloadIndex\", \"true\" },\n+#ifndef WIN32\n+\t\t{ \"core.untrackedCache\", \"true\" },\n+#else\n+\t\t/*\n+\t\t * Unfortunately, Scalar's Functional Tests demonstrated\n+\t\t * that the untracked cache feature is unreliable on Windows\n+\t\t * (which is a bummer because that platform would benefit the\n+\t\t * most from it). For some reason, freshly created files seem\n+\t\t * not to update the directory's `lastModified` time\n+\t\t * immediately, but the untracked cache would need to rely on\n+\t\t * that.\n+\t\t *\n+\t\t * Therefore, with a sad heart, we disable this very useful\n+\t\t * feature on Windows.\n+\t\t */\n+\t\t{ \"core.untrackedCache\", \"false\" },\n+#endif\n+\t\t{ \"core.bare\", \"false\" },\n+\t\t{ \"core.logAllRefUpdates\", \"true\" },\n+\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n+\t\t{ \"credential.validate\", \"false\" }, /* GCM4W-only */\n+\t\t{ \"gc.auto\", \"0\" },\n+\t\t{ \"gui.GCWarning\", \"false\" },\n+\t\t{ \"index.threads\", \"true\" },\n+\t\t{ \"index.version\", \"4\" },\n+\t\t{ \"merge.stat\", \"false\" },\n+\t\t{ \"merge.renames\", \"false\" },\n+\t\t{ \"pack.useBitmaps\", \"false\" },\n+\t\t{ \"pack.useSparse\", \"true\" },\n+\t\t{ \"receive.autoGC\", \"false\" },\n+\t\t{ \"reset.quiet\", \"true\" },\n+\t\t{ \"feature.manyFiles\", \"false\" },\n+\t\t{ \"feature.experimental\", \"false\" },\n+\t\t{ \"fetch.unpackLimit\", \"1\" },\n+\t\t{ \"fetch.writeCommitGraph\", \"false\" },\n+#ifdef WIN32\n+\t\t{ \"http.sslBackend\", \"schannel\" },\n+#endif\n+\t\t{ \"status.aheadBehind\", \"false\" },\n+\t\t{ \"commitGraph.generationVersion\", \"1\" },\n+\t\t{ \"core.autoCRLF\", \"false\" },\n+\t\t{ \"core.safeCRLF\", \"false\" },\n+\t\t{ NULL, NULL },\n+\t};\n+\tint i;\n+\tchar *value;\n+\n+\tfor (i = 0; config[i].key; i++) {\n+\t\tif (git_config_get_string(config[i].key, &value)) {\n+\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"created\");\n+\t\t\tif (git_config_set_gently(config[i].key,\n+\t\t\t\t\t\t  config[i].value) < 0)\n+\t\t\t\treturn error(_(\"could not configure %s=%s\"),\n+\t\t\t\t\t     config[i].key, config[i].value);\n+\t\t} else {\n+\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"exists\");\n+\t\t\tfree(value);\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * The `log.excludeDecoration` setting is special because it allows\n+\t * for multiple values.\n+\t */\n+\tif (git_config_get_string(\"log.excludeDecoration\", &value)) {\n+\t\ttrace2_data_string(\"scalar\", the_repository,\n+\t\t\t\t   \"log.excludeDecoration\", \"created\");\n+\t\tif (git_config_set_multivar_gently(\"log.excludeDecoration\",\n+\t\t\t\t\t\t   \"refs/prefetch/*\",\n+\t\t\t\t\t\t   CONFIG_REGEX_NONE, 0))\n+\t\t\treturn error(_(\"could not configure \"\n+\t\t\t\t       \"log.excludeDecoration\"));\n+\t} else {\n+\t\ttrace2_data_string(\"scalar\", the_repository,\n+\t\t\t\t   \"log.excludeDecoration\", \"exists\");\n+\t\tfree(value);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int start_maintenance(void)\n+{\n+\treturn run_git(\"maintenance\", \"start\", NULL);\n+}\n+\n+static int add_enlistment(void)\n+{\n+\tint res;\n+\n+\tif (!the_repository->worktree)\n+\t\tdie(_(\"Scalar enlistments require a worktree\"));\n+\n+\tres = run_git(\"config\", \"--global\", \"--get\", \"--fixed-value\",\n+\t\t      \"scalar.repo\", the_repository->worktree, NULL);\n+\n+\t/*\n+\t * If the setting is already there, then do nothing.\n+\t */\n+\tif (!res)\n+\t\treturn 0;\n+\n+\treturn run_git(\"config\", \"--global\", \"--add\",\n+\t\t       \"scalar.repo\", the_repository->worktree, NULL);\n+}\n+\n+static int register_dir(void)\n+{\n+\tint res = add_enlistment();\n+\n+\tif (!res)\n+\t\tres = set_recommended_config();\n+\n+\tif (!res)\n+\t\tres = start_maintenance();\n+\n+\treturn res;\n+}\n+\n+static int cmd_register(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar register [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn register_dir();\n+}\n \n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"register\", cmd_register },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 5f7131861a5..41429db7990 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,7 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n-scalar <command> [<options>]\n+scalar register [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -29,6 +29,18 @@ will be identical to the worktree.\n The `scalar` command implements various subcommands, and different options\n depending on the subcommand.\n \n+COMMANDS\n+--------\n+\n+Register\n+~~~~~~~~\n+\n+register [<enlistment>]::\n+    Adds the enlistment's repository to the list of registered repositories\n+    and starts background maintenance. If `<enlistment>` is not provided,\n+    then the enlistment associated with the current working directory is\n+    registered.\n+\n SEE ALSO\n --------\n linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"434185","messageId":"2a6ac170e6b3e55108354281ce1f1482af727570.1630359290.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.git.1630359290.gitgitgadget@gmail.com","subject":"[PATCH 05/15] scalar: 'unregister' stops background maintenance","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-08-30T21:34:40Z","receivedAt":"2021-08-30T21:35:01Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nJust like `scalar register` starts the scheduled background maintenance,\n`scalar unregister` stops it. Note that we use `git maintenance start`\nin `scalar register`, but we do not use `git maintenance stop` in\n`scalar unregister`: this would stop maintenance for _all_ repositories,\nnot just for the one we want to unregister.\n\nThe `unregister` command also removes the corresponding entry from the\n`[scalar]` section in the global Git config.\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 50 ++++++++++++++++++++++++++++++++-------\n contrib/scalar/scalar.txt |  8 +++++++\n 2 files changed, 50 insertions(+), 8 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 7660327c27b..ef91a1af38b 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -205,12 +205,12 @@ static int set_recommended_config(void)\n \treturn 0;\n }\n \n-static int start_maintenance(void)\n+static int toggle_maintenance(int enable)\n {\n-\treturn run_git(\"maintenance\", \"start\", NULL);\n+\treturn run_git(\"maintenance\", enable ? \"start\" : \"unregister\", NULL);\n }\n \n-static int add_enlistment(void)\n+static int add_or_remove_enlistment(int add)\n {\n \tint res;\n \n@@ -221,24 +221,39 @@ static int add_enlistment(void)\n \t\t      \"scalar.repo\", the_repository->worktree, NULL);\n \n \t/*\n-\t * If the setting is already there, then do nothing.\n+\t * If we want to add and the setting is already there, then do nothing.\n+\t * If we want to remove and the setting is not there, then do nothing.\n \t */\n-\tif (!res)\n+\tif ((add && !res) || (!add && res))\n \t\treturn 0;\n \n-\treturn run_git(\"config\", \"--global\", \"--add\",\n+\treturn run_git(\"config\", \"--global\", add ? \"--add\" : \"--unset\",\n+\t\t       add ? \"--no-fixed-value\" : \"--fixed-value\",\n \t\t       \"scalar.repo\", the_repository->worktree, NULL);\n }\n \n static int register_dir(void)\n {\n-\tint res = add_enlistment();\n+\tint res = add_or_remove_enlistment(1);\n \n \tif (!res)\n \t\tres = set_recommended_config();\n \n \tif (!res)\n-\t\tres = start_maintenance();\n+\t\tres = toggle_maintenance(1);\n+\n+\treturn res;\n+}\n+\n+static int unregister_dir(void)\n+{\n+\tint res = 0;\n+\n+\tif (toggle_maintenance(0) < 0)\n+\t\tres = -1;\n+\n+\tif (add_or_remove_enlistment(0) < 0)\n+\t\tres = -1;\n \n \treturn res;\n }\n@@ -261,11 +276,30 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_unregister(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar unregister [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn unregister_dir();\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n \t{ \"register\", cmd_register },\n+\t{ \"unregister\", cmd_unregister },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 41429db7990..cddaa969403 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -9,6 +9,7 @@ SYNOPSIS\n --------\n [verse]\n scalar register [<enlistment>]\n+scalar unregister [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -41,6 +42,13 @@ register [<enlistment>]::\n     then the enlistment associated with the current working directory is\n     registered.\n \n+Unregister\n+~~~~~~~~~~\n+\n+unregister [<enlistment>]::\n+    Remove the specified repository from the list of repositories\n+    registered with Scalar and stop the scheduled background maintenance.\n+\n SEE ALSO\n --------\n linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"434187","messageId":"c272ff4069d5906ad8922f621015578ff7a93813.1630359290.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.git.1630359290.gitgitgadget@gmail.com","subject":"[PATCH 07/15] scalar: implement 'scalar list'","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-08-30T21:34:42Z","receivedAt":"2021-08-30T21:35:03Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe produced list simply consists of those repositories registered under\nthe multi-valued `scalar.repo` config setting in the user's Git config.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 11 +++++++++++\n contrib/scalar/scalar.txt | 12 +++++++++++-\n 2 files changed, 22 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 14e688f44a9..91ceb97e552 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -258,6 +258,16 @@ static int unregister_dir(void)\n \treturn res;\n }\n \n+static int cmd_list(int argc, const char **argv)\n+{\n+\tif (argc != 1)\n+\t\tdie(_(\"`scalar list` does not take arguments\"));\n+\n+\tif (run_git(\"config\", \"--global\", \"--get-all\", \"scalar.repo\", NULL) < 0)\n+\t\treturn -1;\n+\treturn 0;\n+}\n+\n static int cmd_register(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -344,6 +354,7 @@ static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n \t{ NULL, NULL},\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex cddaa969403..e1f629fddad 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,6 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n+scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n \n@@ -28,11 +29,20 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand.\n+depending on the subcommand. With the exception of `list`, all subcommands\n+expect to be run in an enlistment.\n \n COMMANDS\n --------\n \n+List\n+~~~~\n+\n+list::\n+    To see which repositories are currently registered by the service, run\n+    `scalar list`. This subcommand does not need to be run inside a Scalar\n+    enlistment.\n+\n Register\n ~~~~~~~~\n \n-- \ngitgitgadget\n\n"},{"id":"434188","messageId":"087fc9be19491c10002eb0fc316bc72ff386d44c.1630359290.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.git.1630359290.gitgitgadget@gmail.com","subject":"[PATCH 06/15] scalar: let 'unregister' handle a deleted enlistment directory gracefully","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-08-30T21:34:41Z","receivedAt":"2021-08-30T21:35:04Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nWhen a user deleted an enlistment manually, let's be generous and\n_still_ unregister it.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 46 ++++++++++++++++++++++++++++++++\n contrib/scalar/t/t9099-scalar.sh | 15 +++++++++++\n 2 files changed, 61 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex ef91a1af38b..14e688f44a9 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -276,6 +276,24 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int remove_deleted_enlistment(struct strbuf *path)\n+{\n+\tint res = 0;\n+\tstrbuf_realpath_forgiving(path, path->buf, 1);\n+\n+\tif (run_git(\"config\", \"--global\",\n+\t\t    \"--unset\", \"--fixed-value\",\n+\t\t    \"scalar.repo\", path->buf, NULL) < 0)\n+\t\tres = -1;\n+\n+\tif (run_git(\"config\", \"--global\",\n+\t\t    \"--unset\", \"--fixed-value\",\n+\t\t    \"maintenance.repo\", path->buf, NULL) < 0)\n+\t\tres = -1;\n+\n+\treturn res;\n+}\n+\n static int cmd_unregister(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -289,6 +307,34 @@ static int cmd_unregister(int argc, const char **argv)\n \targc = parse_options(argc, argv, NULL, options,\n \t\t\t     usage, 0);\n \n+\t/*\n+\t * Be forgiving when the enlistment or worktree does not even exist any\n+\t * longer; This can be the case if a user deleted the worktree by\n+\t * mistake and _still_ wants to unregister the thing.\n+\t */\n+\tif (argc == 1) {\n+\t\tstruct strbuf src_path = STRBUF_INIT, workdir_path = STRBUF_INIT;\n+\n+\t\tstrbuf_addf(&src_path, \"%s/src/.git\", argv[0]);\n+\t\tstrbuf_addf(&workdir_path, \"%s/.git\", argv[0]);\n+\t\tif (!is_directory(src_path.buf) && !is_directory(workdir_path.buf)) {\n+\t\t\t/* remove possible matching registrations */\n+\t\t\tint res = -1;\n+\n+\t\t\tstrbuf_strip_suffix(&src_path, \"/.git\");\n+\t\t\tres = remove_deleted_enlistment(&src_path) && res;\n+\n+\t\t\tstrbuf_strip_suffix(&workdir_path, \"/.git\");\n+\t\t\tres = remove_deleted_enlistment(&workdir_path) && res;\n+\n+\t\t\tstrbuf_release(&src_path);\n+\t\t\tstrbuf_release(&workdir_path);\n+\t\t\treturn res;\n+\t\t}\n+\t\tstrbuf_release(&src_path);\n+\t\tstrbuf_release(&workdir_path);\n+\t}\n+\n \tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n \n \treturn unregister_dir();\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 16f2b72b126..ef0e8d680d5 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -14,4 +14,19 @@ test_expect_success 'scalar shows a usage' '\n \ttest_expect_code 129 scalar -h\n '\n \n+test_expect_success 'scalar unregister' '\n+\tgit init vanish/src &&\n+\tscalar register vanish/src &&\n+\tgit config --get --global --fixed-value \\\n+\t\tmaintenance.repo \"$(pwd)/vanish/src\" &&\n+\tscalar list >scalar.repos &&\n+\tgrep -F \"$(pwd)/vanish/src\" scalar.repos &&\n+\trm -rf vanish/src/.git &&\n+\tscalar unregister vanish &&\n+\ttest_must_fail git config --get --global --fixed-value \\\n+\t\tmaintenance.repo \"$(pwd)/vanish/src\" &&\n+\tscalar list >scalar.repos &&\n+\t! grep -F \"$(pwd)/vanish/src\" scalar.repos\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"434189","messageId":"2cbf0b611133df5fa7eed1bf38460f9d119d2a6e.1630359290.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.git.1630359290.gitgitgadget@gmail.com","subject":"[PATCH 08/15] scalar: implement the `clone` subcommand","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-08-30T21:34:43Z","receivedAt":"2021-08-30T21:35:06Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis implements Scalar's opinionated `clone` command: it tries to use a\npartial clone and sets up a sparse checkout by default. In contrast to\n`git clone`, `scalar clone` sets up the worktree in the `src/`\nsubdirectory, to encourage a separation between the source files and the\nbuild output (which helps Git tremendously because it avoids untracked\nfiles that have to be specifically ignored when refreshing the index).\n\nAlso, it registers the repository for regular, scheduled maintenance,\nand configures a flurry of configuration settings based on the\nexperience and experiments of the Microsoft Windows and the Microsoft\nOffice development teams.\n\nNote: We intentionally use a slightly wasteful `set_config()` function\n(which does not reuse a single `strbuf`, for example, though performance\n_really_ does not matter here) for convenience and readability.\n\nAlso note: since the `scalar clone` command is by far the most commonly\ncalled `scalar` subcommand, we document it at the top of the manual\npage.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 200 +++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        |  35 +++++-\n contrib/scalar/t/t9099-scalar.sh |  32 +++++\n 3 files changed, 262 insertions(+), 5 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 91ceb97e552..13cdfa94d16 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -7,6 +7,7 @@\n #include \"parse-options.h\"\n #include \"config.h\"\n #include \"run-command.h\"\n+#include \"refs.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -258,6 +259,204 @@ static int unregister_dir(void)\n \treturn res;\n }\n \n+/* printf-style interface, expects `<key>=<value>` argument */\n+static int set_config(const char *fmt, ...)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tchar *value;\n+\tint res;\n+\tva_list args;\n+\n+\tva_start(args, fmt);\n+\tstrbuf_vaddf(&buf, fmt, args);\n+\tva_end(args);\n+\n+\tvalue = strchr(buf.buf, '=');\n+\tif (value)\n+\t\t*(value++) = '\\0';\n+\tres = git_config_set_gently(buf.buf, value);\n+\tstrbuf_release(&buf);\n+\n+\treturn res;\n+}\n+\n+static char *remote_default_branch(const char *url)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tstruct strbuf out = STRBUF_INIT;\n+\n+\tcp.git_cmd = 1;\n+\tstrvec_pushl(&cp.args, \"ls-remote\", \"--symref\", url, \"HEAD\", NULL);\n+\tstrbuf_addstr(&out, \"-\\n\");\n+\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n+\t\tchar *ref = out.buf;\n+\n+\t\twhile ((ref = strstr(ref + 1, \"\\nref: \"))) {\n+\t\t\tconst char *p;\n+\t\t\tchar *head, *branch;\n+\n+\t\t\tref += strlen(\"\\nref: \");\n+\t\t\thead = strstr(ref, \"\\tHEAD\");\n+\n+\t\t\tif (!head || memchr(ref, '\\n', head - ref))\n+\t\t\t\tcontinue;\n+\n+\t\t\tif (skip_prefix(ref, \"refs/heads/\", &p)) {\n+\t\t\t\tbranch = xstrndup(p, head - p);\n+\t\t\t\tstrbuf_release(&out);\n+\t\t\t\treturn branch;\n+\t\t\t}\n+\n+\t\t\terror(_(\"remote HEAD is not a branch: '%.*s'\"),\n+\t\t\t      (int)(head - ref), ref);\n+\t\t\tstrbuf_release(&out);\n+\t\t\treturn NULL;\n+\t\t}\n+\t}\n+\twarning(_(\"failed to get default branch name from remote; \"\n+\t\t  \"using local default\"));\n+\tstrbuf_reset(&out);\n+\n+\tchild_process_init(&cp);\n+\tcp.git_cmd = 1;\n+\tstrvec_pushl(&cp.args, \"symbolic-ref\", \"--short\", \"HEAD\", NULL);\n+\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n+\t\tstrbuf_trim(&out);\n+\t\treturn strbuf_detach(&out, NULL);\n+\t}\n+\n+\tstrbuf_release(&out);\n+\terror(_(\"failed to get default branch name\"));\n+\treturn NULL;\n+}\n+\n+static int cmd_clone(int argc, const char **argv)\n+{\n+\tconst char *branch = NULL;\n+\tint full_clone = 0;\n+\tstruct option clone_options[] = {\n+\t\tOPT_STRING('b', \"branch\", &branch, N_(\"<branch>\"),\n+\t\t\t   N_(\"branch to checkout after clone\")),\n+\t\tOPT_BOOL(0, \"full-clone\", &full_clone,\n+\t\t\t N_(\"when cloning, create full working directory\")),\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const clone_usage[] = {\n+\t\tN_(\"scalar clone [<options>] [--] <repo> [<dir>]\"),\n+\t\tNULL\n+\t};\n+\tconst char *url;\n+\tchar *enlistment = NULL, *dir = NULL;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint res;\n+\n+\targc = parse_options(argc, argv, NULL, clone_options, clone_usage, 0);\n+\n+\tif (argc == 2) {\n+\t\turl = argv[0];\n+\t\tenlistment = xstrdup(argv[1]);\n+\t} else if (argc == 1) {\n+\t\turl = argv[0];\n+\n+\t\tstrbuf_addstr(&buf, url);\n+\t\t/* Strip trailing slashes, if any */\n+\t\twhile (buf.len > 0 && is_dir_sep(buf.buf[buf.len - 1]))\n+\t\t\tstrbuf_setlen(&buf, buf.len - 1);\n+\t\t/* Strip suffix `.git`, if any */\n+\t\tstrbuf_strip_suffix(&buf, \".git\");\n+\n+\t\tenlistment = find_last_dir_sep(buf.buf);\n+\t\tif (!enlistment) {\n+\t\t\tdie(_(\"cannot deduce worktree name from '%s'\"), url);\n+\t\t}\n+\t\tenlistment = xstrdup(enlistment + 1);\n+\t} else {\n+\t\tusage_msg_opt(_(\"You must specify a repository to clone.\"),\n+\t\t\t      clone_usage, clone_options);\n+\t}\n+\n+\tif (is_directory(enlistment))\n+\t\tdie(_(\"directory '%s' exists already\"), enlistment);\n+\n+\tdir = xstrfmt(\"%s/src\", enlistment);\n+\n+\tstrbuf_reset(&buf);\n+\tif (branch)\n+\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", branch);\n+\telse {\n+\t\tchar *b = repo_default_branch_name(the_repository, 1);\n+\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", b);\n+\t\tfree(b);\n+\t}\n+\n+\tif ((res = run_git(\"-c\", buf.buf, \"init\", \"--\", dir, NULL)))\n+\t\tgoto cleanup;\n+\n+\tif (chdir(dir) < 0) {\n+\t\tres = error_errno(_(\"could not switch to '%s'\"), dir);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tsetup_git_directory();\n+\n+\t/* common-main already logs `argv` */\n+\ttrace2_def_repo(the_repository);\n+\n+\tif (!branch && !(branch = remote_default_branch(url))) {\n+\t\tres = error(_(\"failed to get default branch for '%s'\"), url);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tif (set_config(\"remote.origin.url=%s\", url) ||\n+\t    set_config(\"remote.origin.fetch=\"\n+\t\t       \"+refs/heads/*:refs/remotes/origin/*\") ||\n+\t    set_config(\"remote.origin.promisor=true\") ||\n+\t    set_config(\"remote.origin.partialCloneFilter=blob:none\")) {\n+\t\tres = error(_(\"could not configure remote in '%s'\"), dir);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tif (!full_clone &&\n+\t    (res = run_git(\"sparse-checkout\", \"init\", \"--cone\", NULL)))\n+\t\tgoto cleanup;\n+\n+\tif (set_recommended_config())\n+\t\treturn error(_(\"could not configure '%s'\"), dir);\n+\n+\tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL))) {\n+\t\twarning(_(\"partial clone failed; attempting full clone\"));\n+\n+\t\tif (set_config(\"remote.origin.promisor\") ||\n+\t\t    set_config(\"remote.origin.partialCloneFilter\")) {\n+\t\t\tres = error(_(\"could not configure for full clone\"));\n+\t\t\tgoto cleanup;\n+\t\t}\n+\n+\t\tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL)))\n+\t\t\tgoto cleanup;\n+\t}\n+\n+\tif ((res = set_config(\"branch.%s.remote=origin\", branch)))\n+\t\tgoto cleanup;\n+\tif ((res = set_config(\"branch.%s.merge=refs/heads/%s\",\n+\t\t\t      branch, branch)))\n+\t\tgoto cleanup;\n+\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addf(&buf, \"origin/%s\", branch);\n+\tres = run_git(\"checkout\", \"-f\", \"-t\", buf.buf, NULL);\n+\tif (res)\n+\t\tgoto cleanup;\n+\n+\tres = register_dir();\n+\n+cleanup:\n+\tfree(enlistment);\n+\tfree(dir);\n+\tstrbuf_release(&buf);\n+\treturn res;\n+}\n+\n static int cmd_list(int argc, const char **argv)\n {\n \tif (argc != 1)\n@@ -354,6 +553,7 @@ static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"clone\", cmd_clone },\n \t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex e1f629fddad..90d59f1d79f 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,6 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n+scalar clone [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n@@ -29,19 +30,43 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand. With the exception of `list`, all subcommands\n-expect to be run in an enlistment.\n+depending on the subcommand. With the exception of `clone` and `list`, all\n+subcommands expect to be run in an enlistment.\n \n COMMANDS\n --------\n \n+Clone\n+~~~~~\n+\n+clone [<options>] <url> [<enlistment>]::\n+    Clones the specified repository, similar to linkgit:git-clone[1]. By\n+    default, only commit and tree objects are cloned. Once finished, the\n+    worktree is located at `<enlistment>/src`.\n++\n+The sparse-checkout feature is enabled (except when run with `--full-clone`)\n+and the only files present are those in the top-level directory. Use\n+`git sparse-checkout set` to expand the set of directories you want to see,\n+or `git sparse-checkout disable` to expand to all files (see\n+linkgit:git-sparse-checkout[1] for more details). You can explore the\n+subdirectories outside your sparse-checkout by using `git ls-tree HEAD`.\n+\n+-b <name>::\n+--branch <name>::\n+    Instead of checking out the branch pointed to by the cloned repository's\n+    HEAD, check out the `<name>` branch instead.\n+\n+--[no-]full-clone::\n+    A sparse-checkout is initialized by default. This behavior can be turned\n+    off via `--full-clone`.\n+\n List\n ~~~~\n \n list::\n     To see which repositories are currently registered by the service, run\n-    `scalar list`. This subcommand does not need to be run inside a Scalar\n-    enlistment.\n+    `scalar list`. This subcommand, like `clone`, does not need to be run\n+    inside a Scalar enlistment.\n \n Register\n ~~~~~~~~\n@@ -61,7 +86,7 @@ unregister [<enlistment>]::\n \n SEE ALSO\n --------\n-linkgit:git-maintenance[1].\n+linkgit:git-clone[1], linkgit:git-maintenance[1].\n \n Scalar\n ---\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex ef0e8d680d5..295398f62cc 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -10,6 +10,9 @@ PATH=$PWD/..:$PATH\n \n . ../../../t/test-lib.sh\n \n+GIT_TEST_MAINT_SCHEDULER=\"crontab:test-tool crontab ../cron.txt\"\n+export GIT_TEST_MAINT_SCHEDULER\n+\n test_expect_success 'scalar shows a usage' '\n \ttest_expect_code 129 scalar -h\n '\n@@ -29,4 +32,33 @@ test_expect_success 'scalar unregister' '\n \t! grep -F \"$(pwd)/vanish/src\" scalar.repos\n '\n \n+test_expect_success 'set up repository to clone' '\n+\ttest_commit first &&\n+\ttest_commit second &&\n+\ttest_commit third &&\n+\tgit switch -c parallel first &&\n+\tmkdir -p 1/2 &&\n+\ttest_commit 1/2/3 &&\n+\tgit config uploadPack.allowFilter true &&\n+\tgit config uploadPack.allowAnySHA1InWant true\n+'\n+\n+test_expect_success 'scalar clone' '\n+\tsecond=$(git rev-parse --verify second:second.t) &&\n+\tscalar clone \"file://$(pwd)\" cloned &&\n+\t(\n+\t\tcd cloned/src &&\n+\n+\t\tgit config --get --global --fixed-value maintenance.repo \\\n+\t\t\t\"$(pwd)\" &&\n+\n+\t\ttest_path_is_missing 1/2 &&\n+\t\ttest_must_fail git rev-list --missing=print $second &&\n+\t\tgit rev-list $second &&\n+\t\tgit cat-file blob $second >actual &&\n+\t\techo \"second\" >expect &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"434190","messageId":"9af1c37c2ea8b51cb2941b0b62460925eb76dfe4.1630359290.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.git.1630359290.gitgitgadget@gmail.com","subject":"[PATCH 09/15] scalar: teach 'clone' to support the --single-branch option","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-08-30T21:34:44Z","receivedAt":"2021-08-30T21:35:07Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nJust like `git clone`, the `scalar clone` command now also offers to\nrestrict the clone to a single branch.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          |  9 +++++++--\n contrib/scalar/scalar.txt        | 12 +++++++++++-\n contrib/scalar/t/t9099-scalar.sh |  6 +++++-\n 3 files changed, 23 insertions(+), 4 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 13cdfa94d16..908eaa84df1 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -333,12 +333,15 @@ static char *remote_default_branch(const char *url)\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n-\tint full_clone = 0;\n+\tint full_clone = 0, single_branch = 0;\n \tstruct option clone_options[] = {\n \t\tOPT_STRING('b', \"branch\", &branch, N_(\"<branch>\"),\n \t\t\t   N_(\"branch to checkout after clone\")),\n \t\tOPT_BOOL(0, \"full-clone\", &full_clone,\n \t\t\t N_(\"when cloning, create full working directory\")),\n+\t\tOPT_BOOL(0, \"single-branch\", &single_branch,\n+\t\t\t N_(\"only download metadata for the branch that will \"\n+\t\t\t    \"be checked out\")),\n \t\tOPT_END(),\n \t};\n \tconst char * const clone_usage[] = {\n@@ -409,7 +412,9 @@ static int cmd_clone(int argc, const char **argv)\n \n \tif (set_config(\"remote.origin.url=%s\", url) ||\n \t    set_config(\"remote.origin.fetch=\"\n-\t\t       \"+refs/heads/*:refs/remotes/origin/*\") ||\n+\t\t       \"+refs/heads/%s:refs/remotes/origin/%s\",\n+\t\t       single_branch ? branch : \"*\",\n+\t\t       single_branch ? branch : \"*\") ||\n \t    set_config(\"remote.origin.promisor=true\") ||\n \t    set_config(\"remote.origin.partialCloneFilter=blob:none\")) {\n \t\tres = error(_(\"could not configure remote in '%s'\"), dir);\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 90d59f1d79f..bb9411b38cb 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,7 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n-scalar clone [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n+scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n@@ -56,6 +56,16 @@ subdirectories outside your sparse-checkout by using `git ls-tree HEAD`.\n     Instead of checking out the branch pointed to by the cloned repository's\n     HEAD, check out the `<name>` branch instead.\n \n+--[no-]single-branch::\n+    Clone only the history leading to the tip of a single branch,\n+    either specified by the `--branch` option or the primary\n+    branch remote's `HEAD` points at.\n++\n+Further fetches into the resulting repository will only update the\n+remote-tracking branch for the branch this option was used for the initial\n+cloning. If the HEAD at the remote did not point at any branch when\n+`--single-branch` clone was made, no remote-tracking branch is created.\n+\n --[no-]full-clone::\n     A sparse-checkout is initialized by default. This behavior can be turned\n     off via `--full-clone`.\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 295398f62cc..9a35ab4fde6 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -45,13 +45,17 @@ test_expect_success 'set up repository to clone' '\n \n test_expect_success 'scalar clone' '\n \tsecond=$(git rev-parse --verify second:second.t) &&\n-\tscalar clone \"file://$(pwd)\" cloned &&\n+\tscalar clone \"file://$(pwd)\" cloned --single-branch &&\n \t(\n \t\tcd cloned/src &&\n \n \t\tgit config --get --global --fixed-value maintenance.repo \\\n \t\t\t\"$(pwd)\" &&\n \n+\t\tgit for-each-ref --format=\"%(refname)\" refs/remotes/origin/ >actual &&\n+\t\techo \"refs/remotes/origin/parallel\" >expect &&\n+\t\ttest_cmp expect actual &&\n+\n \t\ttest_path_is_missing 1/2 &&\n \t\ttest_must_fail git rev-list --missing=print $second &&\n \t\tgit rev-list $second &&\n-- \ngitgitgadget\n\n"},{"id":"434191","messageId":"c3f16bccd023601bb1d041c36cf5f49011abcb76.1630359290.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.git.1630359290.gitgitgadget@gmail.com","subject":"[PATCH 10/15] scalar: implement the `run` command","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-08-30T21:34:45Z","receivedAt":"2021-08-30T21:35:09Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nNote: this subcommand is provided primarily for backwards-compatibility,\nfor existing Scalar uses. It is mostly just a shim for `git\nmaintenance`, mapping task names from the way Scalar called them to the\nway Git calls them.\n\nThe reason why those names differ? The background maintenance was first\nimplemented in Scalar, and when it was contributed as a patch series\nimplementing the `git maintenance` command, reviewers suggested better\nnames, those suggestions were accepted before the patches were\nintegrated into core Git.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 64 +++++++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt | 19 ++++++++++++\n 2 files changed, 83 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 908eaa84df1..d5d38a1afeb 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -490,6 +490,69 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_run(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tstruct {\n+\t\tconst char *arg, *task;\n+\t} tasks[] = {\n+\t\t{ \"config\", NULL },\n+\t\t{ \"commit-graph\", \"commit-graph\" },\n+\t\t{ \"fetch\", \"prefetch\" },\n+\t\t{ \"loose-objects\", \"loose-objects\" },\n+\t\t{ \"pack-files\", \"incremental-repack\" },\n+\t\t{ NULL, NULL }\n+\t};\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tconst char *usagestr[] = { NULL, NULL };\n+\tint i;\n+\n+\tstrbuf_addstr(&buf, N_(\"scalar run <task> [<enlistment>]\\nTasks:\\n\"));\n+\tfor (i = 0; tasks[i].arg; i++)\n+\t\tstrbuf_addf(&buf, \"\\t%s\\n\", tasks[i].arg);\n+\tusagestr[0] = buf.buf;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usagestr, 0);\n+\n+\tif (argc == 0)\n+\t\tusage_with_options(usagestr, options);\n+\n+\tif (!strcmp(\"all\", argv[0]))\n+\t\ti = -1;\n+\telse {\n+\t\tfor (i = 0; tasks[i].arg && strcmp(tasks[i].arg, argv[0]); i++)\n+\t\t\t; /* keep looking for the task */\n+\n+\t\tif (i > 0 && !tasks[i].arg) {\n+\t\t\terror(_(\"no such task: '%s'\"), argv[0]);\n+\t\t\tusage_with_options(usagestr, options);\n+\t\t}\n+\t}\n+\n+\targc--;\n+\targv++;\n+\tsetup_enlistment_directory(argc, argv, usagestr, options, NULL);\n+\tstrbuf_release(&buf);\n+\n+\tif (i == 0)\n+\t\treturn register_dir();\n+\n+\tif (i > 0)\n+\t\treturn run_git(\"maintenance\", \"run\",\n+\t\t\t       \"--task\", tasks[i].task, NULL);\n+\n+\tif (register_dir())\n+\t\treturn -1;\n+\tfor (i = 1; tasks[i].arg; i++)\n+\t\tif (run_git(\"maintenance\", \"run\",\n+\t\t\t    \"--task\", tasks[i].task, NULL))\n+\t\t\treturn -1;\n+\treturn 0;\n+}\n+\n static int remove_deleted_enlistment(struct strbuf *path)\n {\n \tint res = 0;\n@@ -562,6 +625,7 @@ static struct {\n \t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n+\t{ \"run\", cmd_run },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex bb9411b38cb..9aadaf6323f 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -12,6 +12,7 @@ scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<e\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n+scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -94,6 +95,24 @@ unregister [<enlistment>]::\n     Remove the specified repository from the list of repositories\n     registered with Scalar and stop the scheduled background maintenance.\n \n+Run\n+~~~\n+\n+scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]::\n+    Run the given maintenance task (or all tasks, if `all` was specified).\n+    Except for `all` and `config`, this subcommand simply hands off to\n+    linkgit:git-maintenance[1] (mapping `fetch` to `prefetch` and\n+    `pack-files` to `incremental-repack`).\n++\n+These tasks are run automatically as part of the scheduled maintenance,\n+as soon as the repository is registered with Scalar. It should therefore\n+not be necessary to run this subcommand manually.\n++\n+The `config` task is specific to Scalar and configures all those\n+opinionated default settings that make Git work more efficiently with\n+large repositories. As this task is run as part of `scalar clone`\n+automatically, explicit invocations of this task are rarely needed.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"434192","messageId":"13056f02018542f8143e4933fbe180a0a9f77004.1630359290.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.git.1630359290.gitgitgadget@gmail.com","subject":"[PATCH 11/15] scalar: allow reconfiguring an existing enlistment","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-08-30T21:34:46Z","receivedAt":"2021-08-30T21:35:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis comes in handy during Scalar upgrades, or when config settings were\nmessed up by mistake.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 81 ++++++++++++++++++++------------\n contrib/scalar/scalar.txt        |  8 ++++\n contrib/scalar/t/t9099-scalar.sh |  8 ++++\n 3 files changed, 68 insertions(+), 29 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex d5d38a1afeb..4eff3464a13 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -115,18 +115,20 @@ static int run_git(const char *arg, ...)\n \treturn res;\n }\n \n-static int set_recommended_config(void)\n+static int set_recommended_config(int reconfigure)\n {\n \tstruct {\n \t\tconst char *key;\n \t\tconst char *value;\n+\t\tint overwrite_on_reconfigure;\n \t} config[] = {\n-\t\t{ \"am.keepCR\", \"true\" },\n-\t\t{ \"core.FSCache\", \"true\" },\n-\t\t{ \"core.multiPackIndex\", \"true\" },\n-\t\t{ \"core.preloadIndex\", \"true\" },\n+\t\t/* Required */\n+\t\t{ \"am.keepCR\", \"true\", 1 },\n+\t\t{ \"core.FSCache\", \"true\", 1 },\n+\t\t{ \"core.multiPackIndex\", \"true\", 1 },\n+\t\t{ \"core.preloadIndex\", \"true\", 1 },\n #ifndef WIN32\n-\t\t{ \"core.untrackedCache\", \"true\" },\n+\t\t{ \"core.untrackedCache\", \"true\", 1 },\n #else\n \t\t/*\n \t\t * Unfortunately, Scalar's Functional Tests demonstrated\n@@ -140,29 +142,30 @@ static int set_recommended_config(void)\n \t\t * Therefore, with a sad heart, we disable this very useful\n \t\t * feature on Windows.\n \t\t */\n-\t\t{ \"core.untrackedCache\", \"false\" },\n+\t\t{ \"core.untrackedCache\", \"false\", 1 },\n #endif\n-\t\t{ \"core.bare\", \"false\" },\n-\t\t{ \"core.logAllRefUpdates\", \"true\" },\n-\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n-\t\t{ \"credential.validate\", \"false\" }, /* GCM4W-only */\n-\t\t{ \"gc.auto\", \"0\" },\n-\t\t{ \"gui.GCWarning\", \"false\" },\n-\t\t{ \"index.threads\", \"true\" },\n-\t\t{ \"index.version\", \"4\" },\n-\t\t{ \"merge.stat\", \"false\" },\n-\t\t{ \"merge.renames\", \"false\" },\n-\t\t{ \"pack.useBitmaps\", \"false\" },\n-\t\t{ \"pack.useSparse\", \"true\" },\n-\t\t{ \"receive.autoGC\", \"false\" },\n-\t\t{ \"reset.quiet\", \"true\" },\n-\t\t{ \"feature.manyFiles\", \"false\" },\n-\t\t{ \"feature.experimental\", \"false\" },\n-\t\t{ \"fetch.unpackLimit\", \"1\" },\n-\t\t{ \"fetch.writeCommitGraph\", \"false\" },\n+\t\t{ \"core.bare\", \"false\", 1 },\n+\t\t{ \"core.logAllRefUpdates\", \"true\", 1 },\n+\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\", 1 },\n+\t\t{ \"credential.validate\", \"false\", 1 }, /* GCM4W-only */\n+\t\t{ \"gc.auto\", \"0\", 1 },\n+\t\t{ \"gui.GCWarning\", \"false\", 1 },\n+\t\t{ \"index.threads\", \"true\", 1 },\n+\t\t{ \"index.version\", \"4\", 1 },\n+\t\t{ \"merge.stat\", \"false\", 1 },\n+\t\t{ \"merge.renames\", \"false\", 1 },\n+\t\t{ \"pack.useBitmaps\", \"false\", 1 },\n+\t\t{ \"pack.useSparse\", \"true\", 1 },\n+\t\t{ \"receive.autoGC\", \"false\", 1 },\n+\t\t{ \"reset.quiet\", \"true\", 1 },\n+\t\t{ \"feature.manyFiles\", \"false\", 1 },\n+\t\t{ \"feature.experimental\", \"false\", 1 },\n+\t\t{ \"fetch.unpackLimit\", \"1\", 1 },\n+\t\t{ \"fetch.writeCommitGraph\", \"false\", 1 },\n #ifdef WIN32\n-\t\t{ \"http.sslBackend\", \"schannel\" },\n+\t\t{ \"http.sslBackend\", \"schannel\", 1 },\n #endif\n+\t\t/* Optional */\n \t\t{ \"status.aheadBehind\", \"false\" },\n \t\t{ \"commitGraph.generationVersion\", \"1\" },\n \t\t{ \"core.autoCRLF\", \"false\" },\n@@ -173,7 +176,8 @@ static int set_recommended_config(void)\n \tchar *value;\n \n \tfor (i = 0; config[i].key; i++) {\n-\t\tif (git_config_get_string(config[i].key, &value)) {\n+\t\tif ((reconfigure && config[i].overwrite_on_reconfigure) ||\n+\t\t    git_config_get_string(config[i].key, &value)) {\n \t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"created\");\n \t\t\tif (git_config_set_gently(config[i].key,\n \t\t\t\t\t\t  config[i].value) < 0)\n@@ -238,7 +242,7 @@ static int register_dir(void)\n \tint res = add_or_remove_enlistment(1);\n \n \tif (!res)\n-\t\tres = set_recommended_config();\n+\t\tres = set_recommended_config(0);\n \n \tif (!res)\n \t\tres = toggle_maintenance(1);\n@@ -425,7 +429,7 @@ static int cmd_clone(int argc, const char **argv)\n \t    (res = run_git(\"sparse-checkout\", \"init\", \"--cone\", NULL)))\n \t\tgoto cleanup;\n \n-\tif (set_recommended_config())\n+\tif (set_recommended_config(0))\n \t\treturn error(_(\"could not configure '%s'\"), dir);\n \n \tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL))) {\n@@ -490,6 +494,24 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_reconfigure(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar reconfigure [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn set_recommended_config(1);\n+}\n+\n static int cmd_run(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -626,6 +648,7 @@ static struct {\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n \t{ \"run\", cmd_run },\n+\t{ \"reconfigure\", cmd_reconfigure },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 9aadaf6323f..227e3542a07 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -13,6 +13,7 @@ scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n+scalar reconfigure <enlistment>\n \n DESCRIPTION\n -----------\n@@ -113,6 +114,13 @@ opinionated default settings that make Git work more efficiently with\n large repositories. As this task is run as part of `scalar clone`\n automatically, explicit invocations of this task are rarely needed.\n \n+Reconfigure\n+~~~~~~~~~~~\n+\n+After a Scalar upgrade, or when the configuration of a Scalar enlistment\n+was somehow corrupted or changed by mistake, this subcommand allows to\n+reconfigure the enlistment.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 9a35ab4fde6..e6d74a06ca0 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -65,4 +65,12 @@ test_expect_success 'scalar clone' '\n \t)\n '\n \n+test_expect_success 'scalar reconfigure' '\n+\tgit init one/src &&\n+\tscalar register one &&\n+\tgit -C one/src config core.preloadIndex false &&\n+\tscalar reconfigure one &&\n+\ttest true = \"$(git -C one/src config core.preloadIndex)\"\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"434193","messageId":"732a28c22fcecafa308cd9444efe9158800b94e5.1630359290.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.git.1630359290.gitgitgadget@gmail.com","subject":"[PATCH 12/15] scalar: teach 'reconfigure' to optionally handle all registered enlistments","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-08-30T21:34:47Z","receivedAt":"2021-08-30T21:35:12Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nAfter a Scalar upgrade, it can come in really handy if there is an easy\nway to reconfigure all Scalar enlistments. This new option offers this\nfunctionality.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 61 ++++++++++++++++++++++++++++++--\n contrib/scalar/scalar.txt        | 10 ++++--\n contrib/scalar/t/t9099-scalar.sh |  3 ++\n 3 files changed, 68 insertions(+), 6 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 4eff3464a13..1f8778cbb39 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -494,22 +494,77 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int get_scalar_repos(const char *key, const char *value, void *data)\n+{\n+\tstruct string_list *list = data;\n+\n+\tif (!strcmp(key, \"scalar.repo\"))\n+\t\tstring_list_append(list, value);\n+\n+\treturn 0;\n+}\n+\n static int cmd_reconfigure(int argc, const char **argv)\n {\n+\tint all = 0;\n \tstruct option options[] = {\n+\t\tOPT_BOOL('a', \"all\", &all,\n+\t\t\t N_(\"reconfigure all registered enlistments\")),\n \t\tOPT_END(),\n \t};\n \tconst char * const usage[] = {\n-\t\tN_(\"scalar reconfigure [<enlistment>]\"),\n+\t\tN_(\"scalar reconfigure [--all | <enlistment>]\"),\n \t\tNULL\n \t};\n+\tstruct string_list scalar_repos = STRING_LIST_INIT_DUP;\n+\tint i, res = 0;\n+\tstruct repository r = { NULL };\n+\tstruct strbuf commondir = STRBUF_INIT, gitdir = STRBUF_INIT;\n \n \targc = parse_options(argc, argv, NULL, options,\n \t\t\t     usage, 0);\n \n-\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\tif (!all) {\n+\t\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\t\treturn set_recommended_config(1);\n+\t}\n+\n+\tif (argc > 0)\n+\t\tusage_msg_opt(_(\"--all or <enlistment>, but not both\"),\n+\t\t\t      usage, options);\n+\n+\tgit_config(get_scalar_repos, &scalar_repos);\n \n-\treturn set_recommended_config(1);\n+\tfor (i = 0; i < scalar_repos.nr; i++) {\n+\t\tconst char *dir = scalar_repos.items[i].string;\n+\n+\t\tstrbuf_reset(&commondir);\n+\t\tstrbuf_reset(&gitdir);\n+\n+\t\tif (chdir(dir) < 0) {\n+\t\t\twarning_errno(_(\"could not switch to '%s'\"), dir);\n+\t\t\tres = -1;\n+\t\t} else if (discover_git_directory(&commondir, &gitdir) < 0) {\n+\t\t\twarning_errno(_(\"git repository gone in '%s'\"), dir);\n+\t\t\tres = -1;\n+\t\t} else {\n+\t\t\tgit_config_clear();\n+\n+\t\t\tthe_repository = &r;\n+\t\t\tr.commondir = commondir.buf;\n+\t\t\tr.gitdir = gitdir.buf;\n+\n+\t\t\tif (set_recommended_config(1) < 0)\n+\t\t\t\tres = -1;\n+\t\t}\n+\t}\n+\n+\tstring_list_clear(&scalar_repos, 1);\n+\tstrbuf_release(&commondir);\n+\tstrbuf_release(&gitdir);\n+\n+\treturn res;\n }\n \n static int cmd_run(int argc, const char **argv)\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 227e3542a07..2a1a0695b4d 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -13,7 +13,7 @@ scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n-scalar reconfigure <enlistment>\n+scalar reconfigure [ --all | <enlistment> ]\n \n DESCRIPTION\n -----------\n@@ -32,8 +32,8 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand. With the exception of `clone` and `list`, all\n-subcommands expect to be run in an enlistment.\n+depending on the subcommand. With the exception of `clone`, `list` and\n+`reconfigure --all`, all subcommands expect to be run in an enlistment.\n \n COMMANDS\n --------\n@@ -121,6 +121,10 @@ After a Scalar upgrade, or when the configuration of a Scalar enlistment\n was somehow corrupted or changed by mistake, this subcommand allows to\n reconfigure the enlistment.\n \n+With the `--all` option, all enlistments currently registered with Scalar\n+will be reconfigured. This option is meant to to be run every time Scalar\n+was upgraded.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex e6d74a06ca0..5fe7fabd0e5 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -70,6 +70,9 @@ test_expect_success 'scalar reconfigure' '\n \tscalar register one &&\n \tgit -C one/src config core.preloadIndex false &&\n \tscalar reconfigure one &&\n+\ttest true = \"$(git -C one/src config core.preloadIndex)\" &&\n+\tgit -C one/src config core.preloadIndex false &&\n+\tscalar reconfigure -a &&\n \ttest true = \"$(git -C one/src config core.preloadIndex)\"\n '\n \n-- \ngitgitgadget\n\n"},{"id":"434194","messageId":"13afbd68812b92b5f51af4377c9b95f1725dd3f0.1630359290.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.git.1630359290.gitgitgadget@gmail.com","subject":"[PATCH 13/15] scalar: implement the `delete` command","fromName":"Matthew John Cheetham via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-08-30T21:34:48Z","receivedAt":"2021-08-30T21:35:13Z","isPatch":true,"sender":{"key":"mjcheetham@outlook.com","avatar":"https://avatars.githubusercontent.com/u/5658207?v=4"},"body":"From: Matthew John Cheetham <mjcheetham@outlook.com>\n\nDelete an enlistment by first unregistering the repository and then\ndeleting the enlistment directory (usually the directory containing the\nworktree `src/` directory).\n\nOn Windows, if the current directory is inside the enlistment's\ndirectory, change to the parent of the enlistment directory, to allow us\nto delete the enlistment (directories used by processes e.g. as current\nworking directories cannot be deleted on Windows).\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Matthew John Cheetham <mjcheetham@outlook.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 55 ++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        |  8 +++++\n contrib/scalar/t/t9099-scalar.sh |  9 ++++++\n 3 files changed, 72 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 1f8778cbb39..c616e91231a 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -8,6 +8,7 @@\n #include \"config.h\"\n #include \"run-command.h\"\n #include \"refs.h\"\n+#include \"dir.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -334,6 +335,33 @@ static char *remote_default_branch(const char *url)\n \treturn NULL;\n }\n \n+static int delete_enlistment(struct strbuf *enlistment)\n+{\n+#ifdef WIN32\n+\tstruct strbuf parent = STRBUF_INIT;\n+#endif\n+\n+\tif (unregister_dir())\n+\t\tdie(_(\"failed to unregister repository\"));\n+\n+#ifdef WIN32\n+\t/*\n+\t * Change the current directory to one outside of the enlistment so\n+\t * that we may delete everything underneath it.\n+\t */\n+\tstrbuf_addbuf(&parent, enlistment);\n+\tstrbuf_parent_directory(&parent);\n+\tif (chdir(parent.buf) < 0)\n+\t\tdie_errno(_(\"could not switch to '%s'\"), parent.buf);\n+\tstrbuf_release(&parent);\n+#endif\n+\n+\tif (remove_dir_recursively(enlistment, 0))\n+\t\tdie(_(\"failed to delete enlistment directory\"));\n+\n+\treturn 0;\n+}\n+\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n@@ -694,6 +722,32 @@ static int cmd_unregister(int argc, const char **argv)\n \treturn unregister_dir();\n }\n \n+static int cmd_delete(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar delete <enlistment>\"),\n+\t\tNULL\n+\t};\n+\tstruct strbuf enlistment = STRBUF_INIT;\n+\tint res = 0;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tif (argc != 1)\n+\t\tusage_with_options(usage, options);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, &enlistment);\n+\n+\tres = delete_enlistment(&enlistment);\n+\tstrbuf_release(&enlistment);\n+\n+\treturn res;\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n@@ -704,6 +758,7 @@ static struct {\n \t{ \"unregister\", cmd_unregister },\n \t{ \"run\", cmd_run },\n \t{ \"reconfigure\", cmd_reconfigure },\n+\t{ \"delete\", cmd_delete },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 2a1a0695b4d..00923023243 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -14,6 +14,7 @@ scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n scalar reconfigure [ --all | <enlistment> ]\n+scalar delete <enlistment>\n \n DESCRIPTION\n -----------\n@@ -125,6 +126,13 @@ With the `--all` option, all enlistments currently registered with Scalar\n will be reconfigured. This option is meant to to be run every time Scalar\n was upgraded.\n \n+Delete\n+~~~~~~\n+\n+delete <enlistment>::\n+    This subcommand lets you delete an existing Scalar enlistment from your\n+    local file system, unregistering the repository.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 5fe7fabd0e5..7e8771d0eff 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -76,4 +76,13 @@ test_expect_success 'scalar reconfigure' '\n \ttest true = \"$(git -C one/src config core.preloadIndex)\"\n '\n \n+test_expect_success 'scalar delete without enlistment shows a usage' '\n+\ttest_expect_code 129 scalar delete\n+'\n+\n+test_expect_success 'scalar delete with enlistment' '\n+\tscalar delete cloned &&\n+\ttest_path_is_missing cloned\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"434195","messageId":"6455b18f1b623032b9066c1730dee045fbe7a3f3.1630359290.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.git.1630359290.gitgitgadget@gmail.com","subject":"[PATCH 15/15] scalar: accept -C and -c options before the subcommand","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-08-30T21:34:50Z","receivedAt":"2021-08-30T21:35:16Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe `git` executable has these two very useful options:\n\n-C <directory>:\n\tswitch to the specified directory before performing any actions\n\n-c <key>=<value>:\n\ttemporarily configure this setting for the duration of the\n\tspecified scalar subcommand\n\nWith this commit, we teach the `scalar` executable the same trick.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 22 +++++++++++++++++++++-\n contrib/scalar/scalar.txt | 10 ++++++++++\n 2 files changed, 31 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex be0a49b0d75..41ba4b2f3b1 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -806,6 +806,25 @@ int cmd_main(int argc, const char **argv)\n \tstruct strbuf scalar_usage = STRBUF_INIT;\n \tint i;\n \n+\twhile (argc > 1 && *argv[1] == '-') {\n+\t\tif (!strcmp(argv[1], \"-C\")) {\n+\t\t\tif (argc < 3)\n+\t\t\t\tdie(_(\"-C requires a <directory>\"));\n+\t\t\tif (chdir(argv[2]) < 0)\n+\t\t\t\tdie_errno(_(\"could not change to '%s'\"),\n+\t\t\t\t\t  argv[2]);\n+\t\t\targc -= 2;\n+\t\t\targv += 2;\n+\t\t} else if (!strcmp(argv[1], \"-c\")) {\n+\t\t\tif (argc < 3)\n+\t\t\t\tdie(_(\"-c requires a <key>=<value> argument\"));\n+\t\t\tgit_config_push_parameter(argv[2]);\n+\t\t\targc -= 2;\n+\t\t\targv += 2;\n+\t\t} else\n+\t\t\tbreak;\n+\t}\n+\n \tif (argc > 1) {\n \t\targv++;\n \t\targc--;\n@@ -816,7 +835,8 @@ int cmd_main(int argc, const char **argv)\n \t}\n \n \tstrbuf_addstr(&scalar_usage,\n-\t\t      N_(\"scalar <command> [<options>]\\n\\nCommands:\\n\"));\n+\t\t      N_(\"scalar [-C <directory>] [-c <key>=<value>] \"\n+\t\t\t \"<command> [<options>]\\n\\nCommands:\\n\"));\n \tfor (i = 0; builtins[i].name; i++)\n \t\tstrbuf_addf(&scalar_usage, \"\\t%s\\n\", builtins[i].name);\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 00923023243..6d85640ef42 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -36,6 +36,16 @@ The `scalar` command implements various subcommands, and different options\n depending on the subcommand. With the exception of `clone`, `list` and\n `reconfigure --all`, all subcommands expect to be run in an enlistment.\n \n+The following options can be specified _before_ the subcommand:\n+\n+-C <directory>::\n+    Before running the subcommand, change the working directory. This\n+    option imitates the same option of linkgit:git[1].\n+\n+-c <key>=<value>::\n+    For the duration of running the specified subcommand, configure this\n+    setting. This option imitates the same option of linkgit:git[1].\n+\n COMMANDS\n --------\n \n-- \ngitgitgadget\n"},{"id":"434196","messageId":"73d08c0c8948340f6c08c1689b40900062db5b4a.1630359290.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.git.1630359290.gitgitgadget@gmail.com","subject":"[PATCH 14/15] scalar: implement the `version` command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-08-30T21:34:49Z","receivedAt":"2021-08-30T21:35:17Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe .NET version of Scalar has a `version` command. This was necessary\nbecause it was versioned independently of Git.\n\nSince Scalar is now tightly coupled with Git, it does not make sense for\nthem to show different versions. Therefore, it shows the same output as\n`git versions`. For backwards-compatibility with the .NET version,\n`scalar version` prints to `stderr`, though (`git version` prints to\n`stdout` instead).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c | 39 +++++++++++++++++++++++++++++++++++++++\n 1 file changed, 39 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex c616e91231a..be0a49b0d75 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -9,6 +9,7 @@\n #include \"run-command.h\"\n #include \"refs.h\"\n #include \"dir.h\"\n+#include \"help.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -362,6 +363,15 @@ static int delete_enlistment(struct strbuf *enlistment)\n \treturn 0;\n }\n \n+/*\n+ * Dummy implementation; Using `get_version_info()` would cause a link error\n+ * without this.\n+ */\n+void load_builtin_commands(const char *prefix, struct cmdnames *cmds)\n+{\n+\tdie(\"not implemented\");\n+}\n+\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n@@ -748,6 +758,34 @@ static int cmd_delete(int argc, const char **argv)\n \treturn res;\n }\n \n+static int cmd_version(int argc, const char **argv)\n+{\n+\tint verbose = 0, build_options = 0;\n+\tstruct option options[] = {\n+\t\tOPT__VERBOSE(&verbose, N_(\"include Git version\")),\n+\t\tOPT_BOOL(0, \"build-options\", &build_options,\n+\t\t\t N_(\"include Git's build options\")),\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar verbose [-v | --verbose] [--build-options]\"),\n+\t\tNULL\n+\t};\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tif (argc != 0)\n+\t\tusage_with_options(usage, options);\n+\n+\tget_version_info(&buf, build_options);\n+\tfprintf(stderr, \"%s\\n\", buf.buf);\n+\tstrbuf_release(&buf);\n+\n+\treturn 0;\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n@@ -759,6 +797,7 @@ static struct {\n \t{ \"run\", cmd_run },\n \t{ \"reconfigure\", cmd_reconfigure },\n \t{ \"delete\", cmd_delete },\n+\t{ \"version\", cmd_version },\n \t{ NULL, NULL},\n };\n \n-- \ngitgitgadget\n\n"},{"id":"434212","messageId":"0d1ce3c0-2a19-e97a-bf0f-5f8893c806ea@gmail.com","threadId":"56409","inReplyTo":"pull.1005.git.1630359290.gitgitgadget@gmail.com","subject":"Re: [PATCH 00/15] [RFC] Upstreaming the Scalar command","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2021-08-31T00:51:32Z","receivedAt":"2021-08-31T00:51:37Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 8/30/21 5:34 PM, Johannes Schindelin via GitGitGadget wrote:\n> tl;dr: This series contributes the Scalar command to the Git project. This\n> command provides an opinionated way to create and configure repositories\n> with a focus on very large repositories.\n\nI want to give Johannes a big thanks for organizing this RFC. As you\ncan see from the authorship of the patches, this was an amazingly\ncollaborative effort, but Johannes led the way by creating a base that\nthe rest of us could work with, then finally he brought in all of the\ngritty details to finish the effort.\n\n> Background\n> ==========\n\n...\n\n> The Scalar project\n> was created to make that separation, refine the key concepts, and then\n> extract those features into the new Scalar command.\n\nWhen people have asked me how Scalar fits with the core Git client, I\npoint them to our \"Philosophy of Scalar\" document [1]. The most concise\nsummary of our goals since starting Scalar has been that Scalar aligns\nwith features already within Git that enable scale. I've said several\ntimes that we are constantly making Scalar do less by making Git do more.\n\n[1] https://github.com/microsoft/git/blob/HEAD/contrib/scalar/docs/philosophy.md\n\nHere is an example: when our large, internal customer told us that they\nrequired Linux support for Scalar, we looked at what it would take. We\ncould have done the necessary platform-specific things to convince .NET\nCore to create a long-running process that launched Git maintenance tasks\nat different intervals, creating a similar mechanism to the Windows and\nmacOS services that did those operations. But we also knew that the\nexisting system was stuck with architectural decisions from VFS for Git\nthat were not actually in service of how Scalar worked. Instead, we\ndecided to build background maintenance into Git itself and had our Linux\nport of Scalar run \"git maintenance start\".\n\nOnce the Linux port was proven out with Git's background maintenance, we\nrealized that the window where a user actually interacts with Scalar instead\nof Git is extremely narrow: users run \"scalar clone\" or \"scalar register\"\nand otherwise only run Git commands. The Scalar process does not need to\nexist outside of that. (There are some other helpers that can be used in\na pinch to diagnose and fix problems, but they are rarely used. These\ncommands, such as 'scalar diagnose' can be contributed separately.)\n\nIt became clear that for our own needs it would be easier to ship one\ninstaller that included the microsoft/git fork and the Scalar CLI, and\nit would be simple to rewrite the Scalar CLI with all of the Git helper\nAPIs. We organized the code in a way that we thought would be amenable\nto an upstream contribution (by placing in contrib/ and using Git code\nstyle).\n\nThe thing about these commands is that they are _opinionated_. We rely\non these opinions for important internal users, but we realize that they\nare not necessarily optimal for all users. Hence, we did not think it\nwise to push those opinions onto the 'git' executable. Having 'scalar'\ncontinue to live as a separate executable made sense to us.\n\nI believe that by contributing Scalar to the full community, that we\ncreate opportunities for Git in the future. For one, users and Git\ndistributors can opt into compiling Scalar so it is more available\nto users who are interested. Another hopeful idea is that maybe this\nreinvigorates ideas of how to streamline Git clones for large repos\nwithout users needing to learn each and every knob to twist to get\nthings working. Since the Scalar CLI is contributed in the full\nlicense of the Git project, pieces of it can be adapted into Git\nproper as needed.\n\nI look forward to hearing your thoughts.\n\nThanks,\n-Stolee\n"},{"id":"434237","messageId":"CAPig+cTG2+A0auFYdu-PHBVa1w2A5neS+6JQ5m+ADaOBq8vzUA@mail.gmail.com","threadId":"56409","inReplyTo":"732a28c22fcecafa308cd9444efe9158800b94e5.1630359290.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 12/15] scalar: teach 'reconfigure' to optionally handle all registered enlistments","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-08-31T06:19:02Z","receivedAt":"2021-08-31T06:19:16Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Aug 30, 2021 at 5:35 PM Johannes Schindelin via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> After a Scalar upgrade, it can come in really handy if there is an easy\n> way to reconfigure all Scalar enlistments. This new option offers this\n> functionality.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n> diff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\n> @@ -121,6 +121,10 @@ After a Scalar upgrade, or when the configuration of a Scalar enlistment\n> +With the `--all` option, all enlistments currently registered with Scalar\n> +will be reconfigured. This option is meant to to be run every time Scalar\n> +was upgraded.\n\ns/was/is/\n"},{"id":"434240","messageId":"CAPig+cQAGxRrLUcPZtZqGEJ9RggRsDt0WJK+dgWmXtfCnZihOQ@mail.gmail.com","threadId":"56409","inReplyTo":"73d08c0c8948340f6c08c1689b40900062db5b4a.1630359290.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 14/15] scalar: implement the `version` command","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-08-31T06:24:37Z","receivedAt":"2021-08-31T06:24:50Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Aug 30, 2021 at 5:35 PM Johannes Schindelin via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> The .NET version of Scalar has a `version` command. This was necessary\n> because it was versioned independently of Git.\n>\n> Since Scalar is now tightly coupled with Git, it does not make sense for\n> them to show different versions. Therefore, it shows the same output as\n> `git versions`. For backwards-compatibility with the .NET version,\n\ns/versions/version/\n\n> `scalar version` prints to `stderr`, though (`git version` prints to\n> `stdout` instead).\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n"},{"id":"434245","messageId":"87fsuqxc6l.fsf@evledraar.gmail.com","threadId":"56409","inReplyTo":"3786f4c597fffc13f638efd26875dcb257d54ab4.1630359290.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 04/15] scalar: 'register' sets recommended config and starts maintenance","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-08-31T08:11:31Z","receivedAt":"2021-08-31T08:15:23Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Aug 30 2021, Derrick Stolee via GitGitGadget wrote:\n\n> [...]\n> +#ifndef WIN32\n> +\t\t{ \"core.untrackedCache\", \"true\" },\n> +#else\n> +\t\t/*\n> +\t\t * Unfortunately, Scalar's Functional Tests demonstrated\n> +\t\t * that the untracked cache feature is unreliable on Windows\n> +\t\t * (which is a bummer because that platform would benefit the\n> +\t\t * most from it). For some reason, freshly created files seem\n> +\t\t * not to update the directory's `lastModified` time\n> +\t\t * immediately, but the untracked cache would need to rely on\n> +\t\t * that.\n> +\t\t *\n> +\t\t * Therefore, with a sad heart, we disable this very useful\n> +\t\t * feature on Windows.\n> +\t\t */\n> +\t\t{ \"core.untrackedCache\", \"false\" },\n> +#endif\n> [...]\n\nOk, but why the need to set it to \"false\" explicitly? Does it need to be\nso opinionated as to overwrite existing user-set config in these cases?\n\n> +\t\t{ \"core.bare\", \"false\" },\n\nShouldn't this be set by \"git init\" already?\n\n> [...]\n> +\t\t{ \"core.logAllRefUpdates\", \"true\" },\n\nAn opinionated thing unrelated to performance?\n\n> [...]\n> +\t\t{ \"feature.manyFiles\", \"false\" },\n> +\t\t{ \"feature.experimental\", \"false\" },\n\nDitto the question about the need to set this, these are false by\ndefault, right?\n\n> [...]\n> +\t\tif (git_config_get_string(config[i].key, &value)) {\n> +\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"created\");\n> +\t\t\tif (git_config_set_gently(config[i].key,\n> +\t\t\t\t\t\t  config[i].value) < 0)\n> +\t\t\t\treturn error(_(\"could not configure %s=%s\"),\n> +\t\t\t\t\t     config[i].key, config[i].value);\n> +\t\t} else {\n> +\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"exists\");\n> +\t\t\tfree(value);\n> +\t\t}\n\nThe commit message doesn't discuss these trace2 additions, these in\nparticular seem like they might be useful, but better done as as some\nmore general trace2 intergration in config.c, i.e. if the functions\nbeing called here did the same logging on config set/get.\n"},{"id":"434246","messageId":"87czpuxbwp.fsf@evledraar.gmail.com","threadId":"56409","inReplyTo":"bcfde9bc7651266deaa0e5036cebd058dffc12a7.1630359290.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 03/15] scalar: create test infrastructure","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-08-31T08:15:36Z","receivedAt":"2021-08-31T08:21:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Aug 30 2021, Johannes Schindelin via GitGitGadget wrote:\n\n> To test the Scalar command, create a test script in contrib/scalar/t\n> that is executed as `make -C contrib/scalar test`. Since Scalar has no\n> meaningful capabilities yet, the only test is rather simple. We will add\n> more tests in subsequent commits that introduce corresponding, new\n> functionality.\n\nAs a comment on 01..03/15: I'd really prefer if we stop using this\npattern of sub-Makefile, the dependencies are a pain to manage, and we\nend up copy/pasting large sets of functionality.\n\nThat would mean just adding the build of this command to the top-level\nMakefile behind some \"CONTRIB_SCALAR\" flag or whatever, but I find that\nmuch cleaner than....\n\n> @@ -21,7 +22,7 @@ include ../../config.mak.uname\n>  TARGETS = scalar$(X) scalar.o\n>  GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n>  \n> -all: scalar$X\n> +all: scalar$X ../../bin-wrappers/scalar\n>  \n> [...]\n> +../../bin-wrappers/scalar: ../../wrap-for-bin.sh Makefile\n> [...]\n>  scalar.html: | scalar.1 # prevent them from trying to build `doc.dep` in parallel\n\n...things like this, which refer to assets built by other Makefiles, and\nneed to plaster over the dependency issues...\n\n> +++ b/contrib/scalar/t/Makefile\n> @@ -0,0 +1,78 @@\n> +# Run scalar tests\n> +#\n> +# Copyright (c) 2005,2021 Junio C Hamano, Johannes Schindelin\n> +#\n> +\n> +-include ../../../config.mak.autogen\n> +-include ../../../config.mak\n> +\n> +SHELL_PATH ?= $(SHELL)\n> +PERL_PATH ?= /usr/bin/perl\n> +RM ?= rm -f\n> +PROVE ?= prove\n> +DEFAULT_TEST_TARGET ?= test\n> +TEST_LINT ?= test-lint\n> +\n> +ifdef TEST_OUTPUT_DIRECTORY\n> +TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results\n> +else\n> +TEST_RESULTS_DIRECTORY = ../../../t/test-results\n> +endif\n> +\n> +# Shell quote;\n> +SHELL_PATH_SQ = $(subst ','\\'',$(SHELL_PATH))\n> +PERL_PATH_SQ = $(subst ','\\'',$(PERL_PATH))\n> +TEST_RESULTS_DIRECTORY_SQ = $(subst ','\\'',$(TEST_RESULTS_DIRECTORY))\n> +\n> +T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))\n> +\n> +all: $(DEFAULT_TEST_TARGET)\n> +\n> +test: $(TEST_LINT)\n> +\t$(MAKE) aggregate-results-and-cleanup\n> +\n> +prove: $(TEST_LINT)\n> +\t@echo \"*** prove ***\"; GIT_CONFIG=.git/config $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)\n> +\t$(MAKE) clean-except-prove-cache\n> +\n> +$(T):\n> +\t@echo \"*** $@ ***\"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)\n> +\n> +clean-except-prove-cache:\n> +\t$(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)'\n> +\t$(RM) -r valgrind/bin\n> +\n> +clean: clean-except-prove-cache\n> +\t$(RM) .prove\n> +\n> +test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax\n> +\n> +test-lint-duplicates:\n> +\t@dups=`echo $(T) | tr ' ' '\\n' | sed 's/-.*//' | sort | uniq -d` && \\\n> +\t\ttest -z \"$$dups\" || { \\\n> +\t\techo >&2 \"duplicate test numbers:\" $$dups; exit 1; }\n> +\n> +test-lint-executable:\n> +\t@bad=`for i in $(T); do test -x \"$$i\" || echo $$i; done` && \\\n> +\t\ttest -z \"$$bad\" || { \\\n> +\t\techo >&2 \"non-executable tests:\" $$bad; exit 1; }\n> +\n> +test-lint-shell-syntax:\n> +\t@'$(PERL_PATH_SQ)' ../../../t/check-non-portable-shell.pl $(T)\n> +\n> +aggregate-results-and-cleanup: $(T)\n> +\t$(MAKE) aggregate-results\n> +\t$(MAKE) clean\n> +\n> +aggregate-results:\n> +\tfor f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \\\n> +\t\techo \"$$f\"; \\\n> +\tdone | '$(SHELL_PATH_SQ)' ../../../t/aggregate-results.sh\n> +\n> +valgrind:\n> +\t$(MAKE) GIT_TEST_OPTS=\"$(GIT_TEST_OPTS) --valgrind\"\n> +\n> +test-results:\n> +\tmkdir -p test-results\n> +\n> +.PHONY: $(T) aggregate-results clean valgrind\n\n...and this entire copy/pasting & adjusting of t/Makefile.\n"},{"id":"434247","messageId":"87a6kyxbo1.fsf@evledraar.gmail.com","threadId":"56409","inReplyTo":"2cbf0b611133df5fa7eed1bf38460f9d119d2a6e.1630359290.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 08/15] scalar: implement the `clone` subcommand","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-08-31T08:23:55Z","receivedAt":"2021-08-31T08:26:32Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Aug 30 2021, Johannes Schindelin via GitGitGadget wrote:\n\n> This implements Scalar's opinionated `clone` command: it tries to use a\n> partial clone and sets up a sparse checkout by default. In contrast to\n> `git clone`, `scalar clone` sets up the worktree in the `src/`\n> subdirectory, to encourage a separation between the source files and the\n> build output (which helps Git tremendously because it avoids untracked\n> files that have to be specifically ignored when refreshing the index).\n\nPerhaps nobody else wondered this while reading this, but I thought this\nmight be some sparse/worktree magic where cloning into \"foo\" would have\n\"foo/.git\", but the worktree was somehow magically mapped at foo/src/\".\n\nBut no, it just takes your \"scalar clone <url> foo\" and translates it to\n\"foo/src\", so you'll get a directory at \"foo\".\n\n> Note: We intentionally use a slightly wasteful `set_config()` function\n> (which does not reuse a single `strbuf`, for example, though performance\n> _really_ does not matter here) for convenience and readability.\n\nFWIW I think the commit message could do without this, that part of the\ncode is obviously not performance sensitive at all. But maybe an\nexplicit note helps anyway...\n"},{"id":"434248","messageId":"877dg2xbjp.fsf@evledraar.gmail.com","threadId":"56409","inReplyTo":"c3f16bccd023601bb1d041c36cf5f49011abcb76.1630359290.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 10/15] scalar: implement the `run` command","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-08-31T08:27:30Z","receivedAt":"2021-08-31T08:29:03Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Aug 30 2021, Derrick Stolee via GitGitGadget wrote:\n\n> +\tconst char *usagestr[] = { NULL, NULL };\n\nMissing usage strings?\n\n> +\tif (argc == 0)\n\nStyle nit (per style guide): s/argc == 0/!argc/g.\n\n> +\tif (!strcmp(\"all\", argv[0]))\n> +\t\ti = -1;\n\nStyle nit (per style guide): missing braces here.\n\n(Just noting the style nits once, but more in this patch, and presumably\nthe rest of the series...)\n"},{"id":"434249","messageId":"874kb6xbf9.fsf@evledraar.gmail.com","threadId":"56409","inReplyTo":"13056f02018542f8143e4933fbe180a0a9f77004.1630359290.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 11/15] scalar: allow reconfiguring an existing enlistment","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-08-31T08:29:44Z","receivedAt":"2021-08-31T08:31:42Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Aug 30 2021, Johannes Schindelin via GitGitGadget wrote:\n\n> This comes in handy during Scalar upgrades, or when config settings were\n> messed up by mistake.\n\n> [...]\n>  \t\tconst char *key;\n>  \t\tconst char *value;\n> +\t\tint overwrite_on_reconfigure;\n\nIf you make this a \"keep_on_reconfigure\", then ...\n\n>  \t} config[] = {\n> -\t\t{ \"am.keepCR\", \"true\" },\n> -\t\t{ \"core.FSCache\", \"true\" },\n> -\t\t{ \"core.multiPackIndex\", \"true\" },\n> -\t\t{ \"core.preloadIndex\", \"true\" },\n> +\t\t/* Required */\n> +\t\t{ \"am.keepCR\", \"true\", 1 },\n> +\t\t{ \"core.FSCache\", \"true\", 1 },\n> +\t\t{ \"core.multiPackIndex\", \"true\", 1 },\n> +\t\t{ \"core.preloadIndex\", \"true\", 1 },\n\nYou won't need the churn/boilerplate of adding \"1\" to everything here,\nbut can just change the initial patch to use designated initializers.\n\nThat along with a throwaway macro like:\n\n#define SCALAR_CFG_TRUE(k) (.key = k, .value = \"true\")\n#define SCALAR_CFG_FALSE(k) (.key = k, .value = \"false\")\n\nMight (or might not) make this even easier to eyeball...\n"},{"id":"434250","messageId":"871r6axban.fsf@evledraar.gmail.com","threadId":"56409","inReplyTo":"6455b18f1b623032b9066c1730dee045fbe7a3f3.1630359290.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 15/15] scalar: accept -C and -c options before the subcommand","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-08-31T08:32:48Z","receivedAt":"2021-08-31T08:34:29Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Aug 30 2021, Johannes Schindelin via GitGitGadget wrote:\n\n> The `git` executable has these two very useful options:\n>\n> -C <directory>:\n> \tswitch to the specified directory before performing any actions\n>\n> -c <key>=<value>:\n> \ttemporarily configure this setting for the duration of the\n> \tspecified scalar subcommand\n>\n> With this commit, we teach the `scalar` executable the same trick.\n> [...]\n> +\twhile (argc > 1 && *argv[1] == '-') {\n> +\t\tif (!strcmp(argv[1], \"-C\")) {\n> +\t\t\tif (argc < 3)\n> +\t\t\t\tdie(_(\"-C requires a <directory>\"));\n> +\t\t\tif (chdir(argv[2]) < 0)\n> +\t\t\t\tdie_errno(_(\"could not change to '%s'\"),\n> +\t\t\t\t\t  argv[2]);\n> +\t\t\targc -= 2;\n> +\t\t\targv += 2;\n> +\t\t} else if (!strcmp(argv[1], \"-c\")) {\n> +\t\t\tif (argc < 3)\n> +\t\t\t\tdie(_(\"-c requires a <key>=<value> argument\"));\n> +\t\t\tgit_config_push_parameter(argv[2]);\n> +\t\t\targc -= 2;\n> +\t\t\targv += 2;\n> +\t\t} else\n> +\t\t\tbreak;\n> +\t}\n\nThis along with my earlier comment about the Makefile copy/pasting makes\nme wonder if an easier way to integrate this wouldn't be to refactor\ngit.c a bit to have it understand either \"git\" or \"scalar\", then instead\nof \"ls-tree\" etc. as \"git\" the subcommands would become \"built-ins\".\n\nWhich would give us both \"[git|scalar] [-c ...] <cmd>\" for free, and\nelimante the need for the inevetable future divergence of wanting -p,\n-P, --exec-path etc. in both.\n"},{"id":"434284","messageId":"3e443075-2d69-9ab9-3d22-915f4f19d22b@gmail.com","threadId":"56409","inReplyTo":"87fsuqxc6l.fsf@evledraar.gmail.com","subject":"Re: [PATCH 04/15] scalar: 'register' sets recommended config and starts maintenance","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2021-08-31T14:22:54Z","receivedAt":"2021-08-31T14:22:58Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 8/31/2021 4:11 AM, Ævar Arnfjörð Bjarmason wrote:\n> \n> On Mon, Aug 30 2021, Derrick Stolee via GitGitGadget wrote:\n> \n>> [...]\n>> +#ifndef WIN32\n>> +\t\t{ \"core.untrackedCache\", \"true\" },\n>> +#else\n>> +\t\t/*\n>> +\t\t * Unfortunately, Scalar's Functional Tests demonstrated\n>> +\t\t * that the untracked cache feature is unreliable on Windows\n>> +\t\t * (which is a bummer because that platform would benefit the\n>> +\t\t * most from it). For some reason, freshly created files seem\n>> +\t\t * not to update the directory's `lastModified` time\n>> +\t\t * immediately, but the untracked cache would need to rely on\n>> +\t\t * that.\n>> +\t\t *\n>> +\t\t * Therefore, with a sad heart, we disable this very useful\n>> +\t\t * feature on Windows.\n>> +\t\t */\n>> +\t\t{ \"core.untrackedCache\", \"false\" },\n>> +#endif\n>> [...]\n> \n> Ok, but why the need to set it to \"false\" explicitly? Does it need to be\n> so opinionated as to overwrite existing user-set config in these cases?\n\nUsers can overwrite this local config value, but this is placed to avoid\na global config value from applying specifically within Scalar-created\nrepos.\n \n>> +\t\t{ \"core.bare\", \"false\" },\n> \n> Shouldn't this be set by \"git init\" already?\n\nThis one is probably a bit _too_ defensive. It can be removed.\n\n>> [...]\n>> +\t\t{ \"core.logAllRefUpdates\", \"true\" },\n> \n> An opinionated thing unrelated to performance?\n\nIt's an opinionated thing related to supporting monorepo users. It helps\nus diagnose issues they have by recreating a sequence of events.\n\n>> [...]\n>> +\t\t{ \"feature.manyFiles\", \"false\" },\n>> +\t\t{ \"feature.experimental\", \"false\" },\n> \n> Ditto the question about the need to set this, these are false by\n> default, right?\n\nBut if a user has them on globally, then we don't want them to apply\nlocally (in favor of the settings that we set explicitly).\n\n>> [...]\n>> +\t\tif (git_config_get_string(config[i].key, &value)) {\n>> +\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"created\");\n>> +\t\t\tif (git_config_set_gently(config[i].key,\n>> +\t\t\t\t\t\t  config[i].value) < 0)\n>> +\t\t\t\treturn error(_(\"could not configure %s=%s\"),\n>> +\t\t\t\t\t     config[i].key, config[i].value);\n>> +\t\t} else {\n>> +\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"exists\");\n>> +\t\t\tfree(value);\n>> +\t\t}\n> \n> The commit message doesn't discuss these trace2 additions, these in\n> particular seem like they might be useful, but better done as as some\n> more general trace2 intergration in config.c, i.e. if the functions\n> being called here did the same logging on config set/get.\n\nIf we want to do such a tracing change within git_config_set*(), then\nthat would be an appropriate replacement. The biggest reason to include\nthem here is to trace that an existing value already exists, for the\ncase of running 'scalar reconfigure' during an upgrade. That part\ndoesn't make much sense to put into config.c.\n\nThanks,\n-Stolee\n"},{"id":"434286","messageId":"8a96c097-2261-7cc3-839c-b388b595f2d1@gmail.com","threadId":"56409","inReplyTo":"871r6axban.fsf@evledraar.gmail.com","subject":"Re: [PATCH 15/15] scalar: accept -C and -c options before the subcommand","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2021-08-31T14:30:07Z","receivedAt":"2021-08-31T14:30:18Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 8/31/2021 4:32 AM, Ævar Arnfjörð Bjarmason wrote:\n> \n> On Mon, Aug 30 2021, Johannes Schindelin via GitGitGadget wrote:\n> \n>> The `git` executable has these two very useful options:\n>>\n>> -C <directory>:\n>> \tswitch to the specified directory before performing any actions\n>>\n>> -c <key>=<value>:\n>> \ttemporarily configure this setting for the duration of the\n>> \tspecified scalar subcommand\n>>\n>> With this commit, we teach the `scalar` executable the same trick.\n>> [...]\n>> +\twhile (argc > 1 && *argv[1] == '-') {\n>> +\t\tif (!strcmp(argv[1], \"-C\")) {\n>> +\t\t\tif (argc < 3)\n>> +\t\t\t\tdie(_(\"-C requires a <directory>\"));\n>> +\t\t\tif (chdir(argv[2]) < 0)\n>> +\t\t\t\tdie_errno(_(\"could not change to '%s'\"),\n>> +\t\t\t\t\t  argv[2]);\n>> +\t\t\targc -= 2;\n>> +\t\t\targv += 2;\n>> +\t\t} else if (!strcmp(argv[1], \"-c\")) {\n>> +\t\t\tif (argc < 3)\n>> +\t\t\t\tdie(_(\"-c requires a <key>=<value> argument\"));\n>> +\t\t\tgit_config_push_parameter(argv[2]);\n>> +\t\t\targc -= 2;\n>> +\t\t\targv += 2;\n>> +\t\t} else\n>> +\t\t\tbreak;\n>> +\t}\n> \n> This along with my earlier comment about the Makefile copy/pasting makes\n> me wonder if an easier way to integrate this wouldn't be to refactor\n> git.c a bit to have it understand either \"git\" or \"scalar\", then instead\n> of \"ls-tree\" etc. as \"git\" the subcommands would become \"built-ins\".\n> \n> Which would give us both \"[git|scalar] [-c ...] <cmd>\" for free, and\n> elimante the need for the inevetable future divergence of wanting -p,\n> -P, --exec-path etc. in both.\n \nSuch a change would likely eliminate the ability to not include Scalar\nwhen building the Git codebase, which we tried to avoid by keeping it\nwithin contrib and have it be compiled via an opt-in flag.\n\nIf we want to talk about integrating Scalar into Git in a deeper way,\nthen that is an interesting discussion to have, but it lives at a much\nhigher level than Makefile details.\n\nThe questions we are really looking to answer in this RFC are:\n\n1. Will the Git project accept Scalar into its codebase?\n\n2. What is the best place for Scalar to live in the Git codebase?\n\nWe erred on the side of keeping Scalar as optional as possible. If\nthe community is more interested in a deeper integration, then that\ncould be an interesting direction.\n\nIn my opinion, I think the current tactic is safest. We could always\ndecide on a deeper integration later by moving the code around. It\nseems harder to do the reverse.\n\nThanks,\n-Stolee\n"},{"id":"434290","messageId":"87mtoxwt63.fsf@evledraar.gmail.com","threadId":"56409","inReplyTo":"8a96c097-2261-7cc3-839c-b388b595f2d1@gmail.com","subject":"Re: [PATCH 15/15] scalar: accept -C and -c options before the subcommand","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-08-31T14:52:39Z","receivedAt":"2021-08-31T15:06:02Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Aug 31 2021, Derrick Stolee wrote:\n\n> On 8/31/2021 4:32 AM, Ævar Arnfjörð Bjarmason wrote:\n>> \n>> On Mon, Aug 30 2021, Johannes Schindelin via GitGitGadget wrote:\n>> \n>>> The `git` executable has these two very useful options:\n>>>\n>>> -C <directory>:\n>>> \tswitch to the specified directory before performing any actions\n>>>\n>>> -c <key>=<value>:\n>>> \ttemporarily configure this setting for the duration of the\n>>> \tspecified scalar subcommand\n>>>\n>>> With this commit, we teach the `scalar` executable the same trick.\n>>> [...]\n>>> +\twhile (argc > 1 && *argv[1] == '-') {\n>>> +\t\tif (!strcmp(argv[1], \"-C\")) {\n>>> +\t\t\tif (argc < 3)\n>>> +\t\t\t\tdie(_(\"-C requires a <directory>\"));\n>>> +\t\t\tif (chdir(argv[2]) < 0)\n>>> +\t\t\t\tdie_errno(_(\"could not change to '%s'\"),\n>>> +\t\t\t\t\t  argv[2]);\n>>> +\t\t\targc -= 2;\n>>> +\t\t\targv += 2;\n>>> +\t\t} else if (!strcmp(argv[1], \"-c\")) {\n>>> +\t\t\tif (argc < 3)\n>>> +\t\t\t\tdie(_(\"-c requires a <key>=<value> argument\"));\n>>> +\t\t\tgit_config_push_parameter(argv[2]);\n>>> +\t\t\targc -= 2;\n>>> +\t\t\targv += 2;\n>>> +\t\t} else\n>>> +\t\t\tbreak;\n>>> +\t}\n>> \n>> This along with my earlier comment about the Makefile copy/pasting makes\n>> me wonder if an easier way to integrate this wouldn't be to refactor\n>> git.c a bit to have it understand either \"git\" or \"scalar\", then instead\n>> of \"ls-tree\" etc. as \"git\" the subcommands would become \"built-ins\".\n>> \n>> Which would give us both \"[git|scalar] [-c ...] <cmd>\" for free, and\n>> elimante the need for the inevetable future divergence of wanting -p,\n>> -P, --exec-path etc. in both.\n>  \n> Such a change would likely eliminate the ability to not include Scalar\n> when building the Git codebase, which we tried to avoid by keeping it\n> within contrib and have it be compiled via an opt-in flag.\n\nI mean to still have it behind a flag, but to handle it similar to how\nwe handle NO_CURL, EXCLUDED_PROGRAMS and the like, i.e. not requiring\nparallel maintenance of copy/pasted Makefile logic in contrib/.\n\n> If we want to talk about integrating Scalar into Git in a deeper way,\n> then that is an interesting discussion to have, but it lives at a much\n> higher level than Makefile details.\n\nTo be clear I'm proposing no change at all in term of what happens when\nyou run \"make install\", just commenting on the implementation details of\nhow we arrange for things to be built and configured before that step.\n\nI realize that this is following some prior art of\ne.g. contrib/subtree/Makefile, but IMNSHO that approach is a historical\nmistake we should be backing out of. There was some recent discussion of\nthis here:\nhttps://lore.kernel.org/git/87pmz4ig4o.fsf@evledraar.gmail.com/\n\nE.g. now we have some painful management of the depencency graph between\n/Makefile and Documentation/Makefile requiring fixes like 56550ea7180\n(Makefile: add missing dependencies of 'config-list.h', 2021-04-08),\nadding yet another Makefile into the mix which (to take one example)\ndepends on doc.dep, which in turn depends on ...; It's all a bunch of\nneedless complexity we can avoid.\n\n> The questions we are really looking to answer in this RFC are:\n>\n> 1. Will the Git project accept Scalar into its codebase?\n>\n> 2. What is the best place for Scalar to live in the Git codebase?\n>\n> We erred on the side of keeping Scalar as optional as possible. If\n> the community is more interested in a deeper integration, then that\n> could be an interesting direction.\n\nIndeed, to be clear I realize I'm entirely punting on the real questions\nyou're interested in. I just gave this an initial cursory skimming for\nnow, I have not formed an informed opinion on your #1, but just a little\nbit of #2.\n\nMy initial reaction to #1 without having looked into it deeply is some\ncombination of \"sure, why not?\", and that the people/group contributing\nmajor scalability work to git.git should be given the benefit of the\ndoubt. Maybe we won't keep \"scalar\" long-term, or change its UI etc.,\nall of that can be handled in some carefully worded documentation\nsomewhere.\n\nOf course all these suggestions I'm making about Makefile arrangement\nare rather pointless if there isn't consensus to get past the hurdle of\nyour #1.\n\n> In my opinion, I think the current tactic is safest. We could always\n> decide on a deeper integration later by moving the code around. It\n> seems harder to do the reverse.\n\nI think \"deeper integration\" is the reverse of what you think it is.\n\nI.e. if I'm patching or maintaining part of the Makefile logic to it's\ndeeper (or perhaps \"gnarlier\" is the righ word?) integration to need to\nduplicate that work in two places, or always take into account that some\nnot-built-by-default-but-quite-common command's *.txt docs and *.sh\ntests live in some unusual place for the purposes of CI, lint, tooling\netc.\n\nIn other words, it's a question of how much net complexity is being\nadded to the (build) system. That complexity doesn't automatically\nreduce just because some files live in another directory, sometimes\nthat's an increase in complexity.\n\nWhereas just conditionally adding it to some list in the top-level\nMakefile (or Documentation/Makefile) is relatively maintenance-free, and\nto our users / packagers the result should be the same or near enough.\nIt won't matter to them if building the optional thing is another \"make\"\ncommand or just a flag to the existing \"make\" command.\n"},{"id":"434304","messageId":"CAPig+cSy=mps-b3HWZSC7WcMaYV_H71BVRK5b0cSy2fA0ogJ7w@mail.gmail.com","threadId":"56409","inReplyTo":"87a6kyxbo1.fsf@evledraar.gmail.com","subject":"Re: [PATCH 08/15] scalar: implement the `clone` subcommand","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-08-31T16:47:07Z","receivedAt":"2021-08-31T16:47:21Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, Aug 31, 2021 at 8:04 AM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> On Mon, Aug 30 2021, Johannes Schindelin via GitGitGadget wrote:\n> > Note: We intentionally use a slightly wasteful `set_config()` function\n> > (which does not reuse a single `strbuf`, for example, though performance\n> > _really_ does not matter here) for convenience and readability.\n>\n> FWIW I think the commit message could do without this, that part of the\n> code is obviously not performance sensitive at all. But maybe an\n> explicit note helps anyway...\n\nFWIW, I also found this distracting; it takes the reader's attention\naway from more important aspects of the patch. (But it alone is not\nworth a re-roll; it was just a minor hiccup.)\n"},{"id":"434451","messageId":"CABPp-BGEDdpb9QFBQux2-_3BJZAcBo30svRF+hfV-KjOcQPojA@mail.gmail.com","threadId":"56409","inReplyTo":"0d1ce3c0-2a19-e97a-bf0f-5f8893c806ea@gmail.com","subject":"Re: [PATCH 00/15] [RFC] Upstreaming the Scalar command","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-09-01T15:00:08Z","receivedAt":"2021-09-01T15:00:23Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Aug 30, 2021 at 5:52 PM Derrick Stolee <stolee@gmail.com> wrote:\n>\n> On 8/30/21 5:34 PM, Johannes Schindelin via GitGitGadget wrote:\n> > tl;dr: This series contributes the Scalar command to the Git project. This\n> > command provides an opinionated way to create and configure repositories\n> > with a focus on very large repositories.\n>\n> I want to give Johannes a big thanks for organizing this RFC. As you\n> can see from the authorship of the patches, this was an amazingly\n> collaborative effort, but Johannes led the way by creating a base that\n> the rest of us could work with, then finally he brought in all of the\n> gritty details to finish the effort.\n>\n> > Background\n> > ==========\n>\n> ...\n>\n> > The Scalar project\n> > was created to make that separation, refine the key concepts, and then\n> > extract those features into the new Scalar command.\n>\n> When people have asked me how Scalar fits with the core Git client, I\n> point them to our \"Philosophy of Scalar\" document [1]. The most concise\n> summary of our goals since starting Scalar has been that Scalar aligns\n> with features already within Git that enable scale. I've said several\n> times that we are constantly making Scalar do less by making Git do more.\n>\n> [1] https://github.com/microsoft/git/blob/HEAD/contrib/scalar/docs/philosophy.md\n>\n> Here is an example: when our large, internal customer told us that they\n> required Linux support for Scalar, we looked at what it would take. We\n> could have done the necessary platform-specific things to convince .NET\n> Core to create a long-running process that launched Git maintenance tasks\n> at different intervals, creating a similar mechanism to the Windows and\n> macOS services that did those operations. But we also knew that the\n> existing system was stuck with architectural decisions from VFS for Git\n> that were not actually in service of how Scalar worked. Instead, we\n> decided to build background maintenance into Git itself and had our Linux\n> port of Scalar run \"git maintenance start\".\n>\n> Once the Linux port was proven out with Git's background maintenance, we\n> realized that the window where a user actually interacts with Scalar instead\n> of Git is extremely narrow: users run \"scalar clone\" or \"scalar register\"\n> and otherwise only run Git commands. The Scalar process does not need to\n> exist outside of that. (There are some other helpers that can be used in\n> a pinch to diagnose and fix problems, but they are rarely used. These\n> commands, such as 'scalar diagnose' can be contributed separately.)\n>\n> It became clear that for our own needs it would be easier to ship one\n> installer that included the microsoft/git fork and the Scalar CLI, and\n> it would be simple to rewrite the Scalar CLI with all of the Git helper\n> APIs. We organized the code in a way that we thought would be amenable\n> to an upstream contribution (by placing in contrib/ and using Git code\n> style).\n>\n> The thing about these commands is that they are _opinionated_. We rely\n> on these opinions for important internal users, but we realize that they\n> are not necessarily optimal for all users. Hence, we did not think it\n> wise to push those opinions onto the 'git' executable. Having 'scalar'\n> continue to live as a separate executable made sense to us.\n>\n> I believe that by contributing Scalar to the full community, that we\n> create opportunities for Git in the future. For one, users and Git\n> distributors can opt into compiling Scalar so it is more available\n> to users who are interested. Another hopeful idea is that maybe this\n> reinvigorates ideas of how to streamline Git clones for large repos\n> without users needing to learn each and every knob to twist to get\n> things working. Since the Scalar CLI is contributed in the full\n> license of the Git project, pieces of it can be adapted into Git\n> proper as needed.\n>\n> I look forward to hearing your thoughts.\n>\n> Thanks,\n> -Stolee\n\nLooks like exciting stuff, you two.  I'm behind on review as it is; I\nstill need to get back to Stolee's sparse-index add/rm/mv series, but\nI'll try to circle back and take a look.\n"},{"id":"434452","messageId":"xmqq5yvks22s.fsf@gitster.g","threadId":"56409","inReplyTo":"3786f4c597fffc13f638efd26875dcb257d54ab4.1630359290.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 04/15] scalar: 'register' sets recommended config and starts maintenance","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-09-01T16:16:59Z","receivedAt":"2021-09-01T16:17:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> +static void setup_enlistment_directory(int argc, const char **argv,\n> +\t\t\t\t       const char * const *usagestr,\n> +\t\t\t\t       const struct option *options,\n> +\t\t\t\t       struct strbuf *enlistment_root)\n> +{\n> +\tstruct strbuf path = STRBUF_INIT;\n> +\tchar *root;\n> +\tint enlistment_found = 0;\n> +\n> +\tif (startup_info->have_repository)\n> +\t\tBUG(\"gitdir already set up?!?\");\n> +\n> +\tif (argc > 1)\n> +\t\tusage_with_options(usagestr, options);\n> +\n> +\t/* find the worktree, determine its corresponding root */\n> +\tif (argc == 1)\n> +\t\tstrbuf_add_absolute_path(&path, argv[0]);\n> +\telse if (strbuf_getcwd(&path) < 0)\n> +\t\tdie(_(\"need a working directory\"));\n> +\n> +\tstrbuf_trim_trailing_dir_sep(&path);\n> +\tdo {\n> +\t\tconst size_t len = path.len;\n> +\n> +\t\t/* check if currently in enlistment root with src/ workdir */\n> +\t\tstrbuf_addstr(&path, \"/src/.git\");\n> +\t\tif (is_git_directory(path.buf)) {\n> +\t\t\tstrbuf_strip_suffix(&path, \"/.git\");\n> +\n> +\t\t\tif (enlistment_root)\n> +\t\t\t\tstrbuf_add(enlistment_root, path.buf, len);\n> +\n> +\t\t\tenlistment_found = 1;\n> +\t\t\tbreak;\n> +\t\t}\n\nThis special casing of \"normally the top of the working tree is\nenlisted, but if the repository is called src/, then we enslist\none level up\" is a bit of eyesore because\n\n (1) it is unclear why such a directory with 'src/' subdirectory is\n     so special, and\n\n (2) it fails to serve those who has the same need but named their\n     source subdirectory differently (like 'source/').\n\n\"The design decisions we made are all part of being opinionated\" can\nall explain it away, but at least we should let the users know where\nthe opinionated choices scalar makes want to lead them to, and this\n\"src/\" stuff needs a bit of clarification.  Perhaps a documentation\nwill be added in later steps?\n\n> +\tfor (i = 0; config[i].key; i++) {\n> +\t\tif (git_config_get_string(config[i].key, &value)) {\n> +\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"created\");\n> +\t\t\tif (git_config_set_gently(config[i].key,\n> +\t\t\t\t\t\t  config[i].value) < 0)\n> +\t\t\t\treturn error(_(\"could not configure %s=%s\"),\n> +\t\t\t\t\t     config[i].key, config[i].value);\n> +\t\t} else {\n> +\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"exists\");\n> +\t\t\tfree(value);\n> +\t\t}\n\nI wonder if we should have a table of configuration variables and\ntheir default values.  The above code implements a skewed \"we only\navoid overriding what is explicitly configured\".  A variable that\nthe user left unconfigured because the user found its default\nsatisfactory will be overridden, and if the value scalar wants to\nuse happens to be the default value, we leave an explicit\nconfiguration to that default value in the resulting configuration\nfile.\n\nBut I think the above is the best we can do without such a central\nregistry of configuration variables.\n"},{"id":"434453","messageId":"xmqqsfyoqm6e.fsf@gitster.g","threadId":"56409","inReplyTo":"2cbf0b611133df5fa7eed1bf38460f9d119d2a6e.1630359290.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 08/15] scalar: implement the `clone` subcommand","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-09-01T16:45:45Z","receivedAt":"2021-09-01T16:45:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Johannes Schindelin via GitGitGadget\" <gitgitgadget@gmail.com>\nwrites:\n\n> +static char *remote_default_branch(const char *url)\n> +{\n> +\tstruct child_process cp = CHILD_PROCESS_INIT;\n> +\tstruct strbuf out = STRBUF_INIT;\n> +\n> +\tcp.git_cmd = 1;\n> +\tstrvec_pushl(&cp.args, \"ls-remote\", \"--symref\", url, \"HEAD\", NULL);\n> +\tstrbuf_addstr(&out, \"-\\n\");\n\nIs this a workaround for the problem that the first \"ref:\" line\nwon't be found by looking for \"\\nref: \" in the loop?  Cute, but the\nextra \"-\" is a bit misleading.\n\n> +\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n> +\t\tchar *ref = out.buf;\n> +\n> +\t\twhile ((ref = strstr(ref + 1, \"\\nref: \"))) {\n> +\t\t\tconst char *p;\n> +\t\t\tchar *head, *branch;\n> +\n> +\t\t\tref += strlen(\"\\nref: \");\n> +\t\t\thead = strstr(ref, \"\\tHEAD\");\n> +\n> +\t\t\tif (!head || memchr(ref, '\\n', head - ref))\n> +\t\t\t\tcontinue;\n\nOK.  We expect \"ref: \" <refname> \"\\t\" <head> \"\\n\" where <head> is\n\"HEAD\" for their .git/HEAD and refs/remotes/<nick>/HEAD for their\nremote-tracking branch for the remote they call <nick>, on a single\nline.  We reject a line that is not of that shape, and we reject a\nline that is about remote-tracking branch by only looking for\n\"\\tHEAD\". Makes sense.\n\nThe strstr() goes from \"ref + 1\", which feels sloppy.  When we\nreject the line we found that begins with \"ref :\", I would have\nexpected that the next scan would start at the beginning of the next\nline, not from the middle of this line at the first letter 'e' in\n'refs/heads/' on the current line \"ref: refs/heads/.....\".  As long\nas the current line is long enough, strstr() would not miss the\nbeginning of the next line, so it might be OK.\n\n> +\t\t\tif (skip_prefix(ref, \"refs/heads/\", &p)) {\n> +\t\t\t\tbranch = xstrndup(p, head - p);\n> +\t\t\t\tstrbuf_release(&out);\n> +\t\t\t\treturn branch;\n> +\t\t\t}\n> +\n> +\t\t\terror(_(\"remote HEAD is not a branch: '%.*s'\"),\n> +\t\t\t      (int)(head - ref), ref);\n> +\t\t\tstrbuf_release(&out);\n> +\t\t\treturn NULL;\n\nOK.  Any symref whose basename is HEAD in their remote-tracking\nhierarchy would have been rejected earlier in the loop.\n\nIs there a particular reason why we return early here, instead of\nbreaking out of hte loop and let the generic \"failed to get\" code\npath below to handle this case?\n\n> +\t\t}\n> +\t}\n> +\twarning(_(\"failed to get default branch name from remote; \"\n> +\t\t  \"using local default\"));\n> +\tstrbuf_reset(&out);\n> +\n> +\tchild_process_init(&cp);\n> +\tcp.git_cmd = 1;\n> +\tstrvec_pushl(&cp.args, \"symbolic-ref\", \"--short\", \"HEAD\", NULL);\n> +\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n> +\t\tstrbuf_trim(&out);\n> +\t\treturn strbuf_detach(&out, NULL);\n> +\t}\n> +\n> +\tstrbuf_release(&out);\n> +\terror(_(\"failed to get default branch name\"));\n> +\treturn NULL;\n> +}\n\n> +static int cmd_clone(int argc, const char **argv)\n> +{\n> +\tconst char *branch = NULL;\n> +\tint full_clone = 0;\n> +\tstruct option clone_options[] = {\n> +\t\tOPT_STRING('b', \"branch\", &branch, N_(\"<branch>\"),\n> +\t\t\t   N_(\"branch to checkout after clone\")),\n> +\t\tOPT_BOOL(0, \"full-clone\", &full_clone,\n> +\t\t\t N_(\"when cloning, create full working directory\")),\n> +\t\tOPT_END(),\n> +\t};\n> +\tconst char * const clone_usage[] = {\n> +\t\tN_(\"scalar clone [<options>] [--] <repo> [<dir>]\"),\n> +\t\tNULL\n> +\t};\n> +\tconst char *url;\n> +\tchar *enlistment = NULL, *dir = NULL;\n> +\tstruct strbuf buf = STRBUF_INIT;\n> +\tint res;\n> +\n> +\targc = parse_options(argc, argv, NULL, clone_options, clone_usage, 0);\n> +\n> +\tif (argc == 2) {\n> +\t\turl = argv[0];\n> +\t\tenlistment = xstrdup(argv[1]);\n> +\t} else if (argc == 1) {\n> +\t\turl = argv[0];\n> +\n> +\t\tstrbuf_addstr(&buf, url);\n> +\t\t/* Strip trailing slashes, if any */\n> +\t\twhile (buf.len > 0 && is_dir_sep(buf.buf[buf.len - 1]))\n> +\t\t\tstrbuf_setlen(&buf, buf.len - 1);\n> +\t\t/* Strip suffix `.git`, if any */\n> +\t\tstrbuf_strip_suffix(&buf, \".git\");\n> +\n> +\t\tenlistment = find_last_dir_sep(buf.buf);\n> +\t\tif (!enlistment) {\n> +\t\t\tdie(_(\"cannot deduce worktree name from '%s'\"), url);\n> +\t\t}\n> +\t\tenlistment = xstrdup(enlistment + 1);\n> +\t} else {\n> +\t\tusage_msg_opt(_(\"You must specify a repository to clone.\"),\n> +\t\t\t      clone_usage, clone_options);\n> +\t}\n> +\n> +\tif (is_directory(enlistment))\n> +\t\tdie(_(\"directory '%s' exists already\"), enlistment);\n> +\n> +\tdir = xstrfmt(\"%s/src\", enlistment);\n> +\n> +\tstrbuf_reset(&buf);\n> +\tif (branch)\n> +\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", branch);\n> +\telse {\n> +\t\tchar *b = repo_default_branch_name(the_repository, 1);\n> +\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", b);\n> +\t\tfree(b);\n\nDoesn't \"git clone\" already use their HEAD without having to make an\nextra \"git ls-remote\" roundtrip?\n\nAhh, you do not do \"git clone\"; you do \"git init\", set things up,\nand then \"git fetch\" and checkout, all manually.\n\nWhich is kind of shame.\n\nI wonder if it is a cleaner implementation to give a new option to\n\"git clone\" that gives a command sequence (not necessarily have to\nbe implemented as a shell script) that specifies necessary\npre-configuration steps to be done before the command starts the\ntransfer step.\n"},{"id":"434632","messageId":"a7c9870a-9af3-bac8-1964-358ecdebe344@gmail.com","threadId":"56409","inReplyTo":"xmqqsfyoqm6e.fsf@gitster.g","subject":"Re: [PATCH 08/15] scalar: implement the `clone` subcommand","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2021-09-03T12:30:49Z","receivedAt":"2021-09-03T12:30:56Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 9/1/2021 12:45 PM, Junio C Hamano wrote:\n> \"Johannes Schindelin via GitGitGadget\" <gitgitgadget@gmail.com>\n> writes:\n> \n...\n>> +\tdir = xstrfmt(\"%s/src\", enlistment);\n>> +\n>> +\tstrbuf_reset(&buf);\n>> +\tif (branch)\n>> +\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", branch);\n>> +\telse {\n>> +\t\tchar *b = repo_default_branch_name(the_repository, 1);\n>> +\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", b);\n>> +\t\tfree(b);\n> \n> Doesn't \"git clone\" already use their HEAD without having to make an\n> extra \"git ls-remote\" roundtrip?\n> \n> Ahh, you do not do \"git clone\"; you do \"git init\", set things up,\n> and then \"git fetch\" and checkout, all manually.\n> \n> Which is kind of shame.\n> \n> I wonder if it is a cleaner implementation to give a new option to\n> \"git clone\" that gives a command sequence (not necessarily have to\n> be implemented as a shell script) that specifies necessary\n> pre-configuration steps to be done before the command starts the\n> transfer step.\n\nI agree that 'git clone' plus maybe some more improvements like\n'--sparse=cone' to set up cone-mode sparse-checkout would be good.\nAnd also the implementation being contributed here is cleaner if\nwe can use 'git clone'.\n\nWe are trying to balance a clean upstream implementation with some\ncustom things that we still need in our microsoft/git fork to\nhandle the integration with the GVFS Protocol (i.e. partial clone\non Azure Repos). That customization is cleaner to keep here in the\nscalar code instead of adding an option to 'git clone'. It is\ndifficult to justify code patterns here due to choices we have made\nin our fork, so I _could_ see a way to replace those custom bits\nwith new, custom flags to 'git clone'. It just requires additional\ninvestment during our integration when we incorporate these upstream\nchanges. Naturally, I'm motivated to avoid that extra work.\n\nIf your opinion to switch to 'git clone' is a strong one, then I\ncould see us doing that change. I just want you to be aware of the\nhidden reasons for choices like these.\n\nThanks,\n-Stolee\n"},{"id":"434633","messageId":"nycvar.QRO.7.76.6.2109031709190.55@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"xmqqsfyoqm6e.fsf@gitster.g","subject":"Re: [PATCH 08/15] scalar: implement the `clone` subcommand","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-09-03T15:20:35Z","receivedAt":"2021-09-03T15:20:35Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 1 Sep 2021, Junio C Hamano wrote:\n\n> \"Johannes Schindelin via GitGitGadget\" <gitgitgadget@gmail.com>\n> writes:\n>\n> > +static char *remote_default_branch(const char *url)\n> > +{\n> > +\tstruct child_process cp = CHILD_PROCESS_INIT;\n> > +\tstruct strbuf out = STRBUF_INIT;\n> > +\n> > +\tcp.git_cmd = 1;\n> > +\tstrvec_pushl(&cp.args, \"ls-remote\", \"--symref\", url, \"HEAD\", NULL);\n> > +\tstrbuf_addstr(&out, \"-\\n\");\n>\n> Is this a workaround for the problem that the first \"ref:\" line\n> won't be found by looking for \"\\nref: \" in the loop?  Cute, but the\n> extra \"-\" is a bit misleading.\n\nThe `-` is actually needed because of the `ref + 1` below, over which you\nstumbled.\n\n>\n> > +\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n> > +\t\tchar *ref = out.buf;\n> > +\n> > +\t\twhile ((ref = strstr(ref + 1, \"\\nref: \"))) {\n> > +\t\t\tconst char *p;\n> > +\t\t\tchar *head, *branch;\n> > +\n> > +\t\t\tref += strlen(\"\\nref: \");\n> > +\t\t\thead = strstr(ref, \"\\tHEAD\");\n> > +\n> > +\t\t\tif (!head || memchr(ref, '\\n', head - ref))\n> > +\t\t\t\tcontinue;\n>\n> OK.  We expect \"ref: \" <refname> \"\\t\" <head> \"\\n\" where <head> is\n> \"HEAD\" for their .git/HEAD and refs/remotes/<nick>/HEAD for their\n> remote-tracking branch for the remote they call <nick>, on a single\n> line.  We reject a line that is not of that shape, and we reject a\n> line that is about remote-tracking branch by only looking for\n> \"\\tHEAD\". Makes sense.\n>\n> The strstr() goes from \"ref + 1\", which feels sloppy.\n\nI would use a different adjective, one that is less judgemental in nature,\nbut then, you were talking about your feelings.\n\n> When we reject the line we found that begins with \"ref :\", I would have\n> expected that the next scan would start at the beginning of the next\n> line, not from the middle of this line at the first letter 'e' in\n> 'refs/heads/' on the current line \"ref: refs/heads/.....\".  As long as\n> the current line is long enough, strstr() would not miss the beginning\n> of the next line, so it might be OK.\n\nIt would even work if the current line is shorter, but as you point out:\nit is wasteful. And it could be improved to be more readable. I reworked\nit, and it now looks like this:\n\n\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n\t\tconst char *line = out.buf;\n\n\t\twhile (*line) {\n\t\t\tconst char *eol = strchrnul(line, '\\n'), *p;\n\t\t\tsize_t len = eol - line;\n\t\t\tchar *branch;\n\n\t\t\tif (!skip_prefix(line, \"ref: \", &p) ||\n\t\t\t    !strip_suffix_mem(line, &len, \"\\tHEAD\")) {\n\t\t\t\tline = eol + (*eol == '\\n');\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\teol = line + len;\n\t\t\tif (skip_prefix(p, \"refs/heads/\", &p)) {\n\t\t\t\tbranch = xstrndup(p, eol - p);\n\t\t\t\tstrbuf_release(&out);\n\t\t\t\treturn branch;\n\t\t\t}\n\n\t\t\terror(_(\"remote HEAD is not a branch: '%.*s'\"),\n\t\t\t      (int)(eol - p), p);\n\t\t\tstrbuf_release(&out);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\nIt now parses the output line by line, looking for the expected prefix and\nsuffix, then verifies the ref name format, and either returns the short\nbranch name or errors out with the message that this is not a branch.\n\n>\n> > +\t\t\tif (skip_prefix(ref, \"refs/heads/\", &p)) {\n> > +\t\t\t\tbranch = xstrndup(p, head - p);\n> > +\t\t\t\tstrbuf_release(&out);\n> > +\t\t\t\treturn branch;\n> > +\t\t\t}\n> > +\n> > +\t\t\terror(_(\"remote HEAD is not a branch: '%.*s'\"),\n> > +\t\t\t      (int)(head - ref), ref);\n> > +\t\t\tstrbuf_release(&out);\n> > +\t\t\treturn NULL;\n>\n> OK.  Any symref whose basename is HEAD in their remote-tracking\n> hierarchy would have been rejected earlier in the loop.\n>\n> Is there a particular reason why we return early here, instead of\n> breaking out of hte loop and let the generic \"failed to get\" code\n> path below to handle this case?\n\nYes, the reason is that I wanted to err on the side of caution. If the\nremote repository reports a default branch that is not a default branch at\nall, I do not want to pretend that things are fine and then run into\ntrouble later when we set up a non-branch as remote-tracking target or\nsomething like that.\n\n>\n> > +\t\t}\n> > +\t}\n> > +\twarning(_(\"failed to get default branch name from remote; \"\n> > +\t\t  \"using local default\"));\n> > +\tstrbuf_reset(&out);\n> > +\n> > +\tchild_process_init(&cp);\n> > +\tcp.git_cmd = 1;\n> > +\tstrvec_pushl(&cp.args, \"symbolic-ref\", \"--short\", \"HEAD\", NULL);\n> > +\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n> > +\t\tstrbuf_trim(&out);\n> > +\t\treturn strbuf_detach(&out, NULL);\n> > +\t}\n> > +\n> > +\tstrbuf_release(&out);\n> > +\terror(_(\"failed to get default branch name\"));\n> > +\treturn NULL;\n> > +}\n>\n> > +static int cmd_clone(int argc, const char **argv)\n> > +{\n> > +\tconst char *branch = NULL;\n> > +\tint full_clone = 0;\n> > +\tstruct option clone_options[] = {\n> > +\t\tOPT_STRING('b', \"branch\", &branch, N_(\"<branch>\"),\n> > +\t\t\t   N_(\"branch to checkout after clone\")),\n> > +\t\tOPT_BOOL(0, \"full-clone\", &full_clone,\n> > +\t\t\t N_(\"when cloning, create full working directory\")),\n> > +\t\tOPT_END(),\n> > +\t};\n> > +\tconst char * const clone_usage[] = {\n> > +\t\tN_(\"scalar clone [<options>] [--] <repo> [<dir>]\"),\n> > +\t\tNULL\n> > +\t};\n> > +\tconst char *url;\n> > +\tchar *enlistment = NULL, *dir = NULL;\n> > +\tstruct strbuf buf = STRBUF_INIT;\n> > +\tint res;\n> > +\n> > +\targc = parse_options(argc, argv, NULL, clone_options, clone_usage, 0);\n> > +\n> > +\tif (argc == 2) {\n> > +\t\turl = argv[0];\n> > +\t\tenlistment = xstrdup(argv[1]);\n> > +\t} else if (argc == 1) {\n> > +\t\turl = argv[0];\n> > +\n> > +\t\tstrbuf_addstr(&buf, url);\n> > +\t\t/* Strip trailing slashes, if any */\n> > +\t\twhile (buf.len > 0 && is_dir_sep(buf.buf[buf.len - 1]))\n> > +\t\t\tstrbuf_setlen(&buf, buf.len - 1);\n> > +\t\t/* Strip suffix `.git`, if any */\n> > +\t\tstrbuf_strip_suffix(&buf, \".git\");\n> > +\n> > +\t\tenlistment = find_last_dir_sep(buf.buf);\n> > +\t\tif (!enlistment) {\n> > +\t\t\tdie(_(\"cannot deduce worktree name from '%s'\"), url);\n> > +\t\t}\n> > +\t\tenlistment = xstrdup(enlistment + 1);\n> > +\t} else {\n> > +\t\tusage_msg_opt(_(\"You must specify a repository to clone.\"),\n> > +\t\t\t      clone_usage, clone_options);\n> > +\t}\n> > +\n> > +\tif (is_directory(enlistment))\n> > +\t\tdie(_(\"directory '%s' exists already\"), enlistment);\n> > +\n> > +\tdir = xstrfmt(\"%s/src\", enlistment);\n> > +\n> > +\tstrbuf_reset(&buf);\n> > +\tif (branch)\n> > +\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", branch);\n> > +\telse {\n> > +\t\tchar *b = repo_default_branch_name(the_repository, 1);\n> > +\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", b);\n> > +\t\tfree(b);\n>\n> Doesn't \"git clone\" already use their HEAD without having to make an\n> extra \"git ls-remote\" roundtrip?\n>\n> Ahh, you do not do \"git clone\"; you do \"git init\", set things up,\n> and then \"git fetch\" and checkout, all manually.\n>\n> Which is kind of shame.\n>\n> I wonder if it is a cleaner implementation to give a new option to\n> \"git clone\" that gives a command sequence (not necessarily have to\n> be implemented as a shell script) that specifies necessary\n> pre-configuration steps to be done before the command starts the\n> transfer step.\n\nRight. It is a shame, I agree. And it is one of the things I want to work\non, after the Scalar patch series made it into Git.\n\nThe reason why I don't want to work on this now is that I expect this\neffort to result in new options for `git clone`, new options that need to\nbe designed well, and where I fully expect a long discussion until we\nreach a consensus how these options should look like, especially since we\nwill need to maintain backwards-compatibility of Scalar's functionality.\n\nTherefore, in the interest to keep the patch series relatively easy to\nreview, I left this in the \"for later\" pile, for now.\n\nCiao,\nDscho\n"},{"id":"434634","messageId":"nycvar.QRO.7.76.6.2109031720420.55@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"CAPig+cSy=mps-b3HWZSC7WcMaYV_H71BVRK5b0cSy2fA0ogJ7w@mail.gmail.com","subject":"Re: [PATCH 08/15] scalar: implement the `clone` subcommand","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-09-03T15:21:12Z","receivedAt":"2021-09-03T15:21:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Eric,\n\nOn Tue, 31 Aug 2021, Eric Sunshine wrote:\n\n> On Tue, Aug 31, 2021 at 8:04 AM Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com> wrote:\n> > On Mon, Aug 30 2021, Johannes Schindelin via GitGitGadget wrote:\n> > > Note: We intentionally use a slightly wasteful `set_config()` function\n> > > (which does not reuse a single `strbuf`, for example, though performance\n> > > _really_ does not matter here) for convenience and readability.\n> >\n> > FWIW I think the commit message could do without this, that part of the\n> > code is obviously not performance sensitive at all. But maybe an\n> > explicit note helps anyway...\n>\n> FWIW, I also found this distracting; it takes the reader's attention\n> away from more important aspects of the patch. (But it alone is not\n> worth a re-roll; it was just a minor hiccup.)\n\nSince I reworked the remote default branch parsing anyway, I removed this\nparagraph from the commit message.\n\nCiao,\nDscho\n"},{"id":"434635","messageId":"nycvar.QRO.7.76.6.2109031722460.55@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"CAPig+cTG2+A0auFYdu-PHBVa1w2A5neS+6JQ5m+ADaOBq8vzUA@mail.gmail.com","subject":"Re: [PATCH 12/15] scalar: teach 'reconfigure' to optionally handle all registered enlistments","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-09-03T15:23:19Z","receivedAt":"2021-09-03T15:23:22Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Eric,\n\nOn Tue, 31 Aug 2021, Eric Sunshine wrote:\n\n> On Mon, Aug 30, 2021 at 5:35 PM Johannes Schindelin via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n> > After a Scalar upgrade, it can come in really handy if there is an easy\n> > way to reconfigure all Scalar enlistments. This new option offers this\n> > functionality.\n> >\n> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > ---\n> > diff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\n> > @@ -121,6 +121,10 @@ After a Scalar upgrade, or when the configuration of a Scalar enlistment\n> > +With the `--all` option, all enlistments currently registered with Scalar\n> > +will be reconfigured. This option is meant to to be run every time Scalar\n> > +was upgraded.\n>\n> s/was/is/\n\nI wanted to convey a temporal order, so I changed it to \"every time after\nScalar is upgraded\". Okay?\n\nCiao,\nDscho\n"},{"id":"434636","messageId":"nycvar.QRO.7.76.6.2109031724160.55@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"CAPig+cQAGxRrLUcPZtZqGEJ9RggRsDt0WJK+dgWmXtfCnZihOQ@mail.gmail.com","subject":"Re: [PATCH 14/15] scalar: implement the `version` command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-09-03T15:24:27Z","receivedAt":"2021-09-03T15:24:28Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Eric,\n\nOn Tue, 31 Aug 2021, Eric Sunshine wrote:\n\n> On Mon, Aug 30, 2021 at 5:35 PM Johannes Schindelin via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n> > The .NET version of Scalar has a `version` command. This was necessary\n> > because it was versioned independently of Git.\n> >\n> > Since Scalar is now tightly coupled with Git, it does not make sense for\n> > them to show different versions. Therefore, it shows the same output as\n> > `git versions`. For backwards-compatibility with the .NET version,\n>\n> s/versions/version/\n\nThank you!\nDscho\n\n>\n> > `scalar version` prints to `stderr`, though (`git version` prints to\n> > `stdout` instead).\n> >\n> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n>\n"},{"id":"434637","messageId":"nycvar.QRO.7.76.6.2109031724560.55@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"xmqq5yvks22s.fsf@gitster.g","subject":"Re: [PATCH 04/15] scalar: 'register' sets recommended config and starts maintenance","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-09-03T15:41:37Z","receivedAt":"2021-09-03T15:41:41Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 1 Sep 2021, Junio C Hamano wrote:\n\n> \"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n> > +static void setup_enlistment_directory(int argc, const char **argv,\n> > +\t\t\t\t       const char * const *usagestr,\n> > +\t\t\t\t       const struct option *options,\n> > +\t\t\t\t       struct strbuf *enlistment_root)\n> > +{\n> > +\tstruct strbuf path = STRBUF_INIT;\n> > +\tchar *root;\n> > +\tint enlistment_found = 0;\n> > +\n> > +\tif (startup_info->have_repository)\n> > +\t\tBUG(\"gitdir already set up?!?\");\n> > +\n> > +\tif (argc > 1)\n> > +\t\tusage_with_options(usagestr, options);\n> > +\n> > +\t/* find the worktree, determine its corresponding root */\n> > +\tif (argc == 1)\n> > +\t\tstrbuf_add_absolute_path(&path, argv[0]);\n> > +\telse if (strbuf_getcwd(&path) < 0)\n> > +\t\tdie(_(\"need a working directory\"));\n> > +\n> > +\tstrbuf_trim_trailing_dir_sep(&path);\n> > +\tdo {\n> > +\t\tconst size_t len = path.len;\n> > +\n> > +\t\t/* check if currently in enlistment root with src/ workdir */\n> > +\t\tstrbuf_addstr(&path, \"/src/.git\");\n> > +\t\tif (is_git_directory(path.buf)) {\n> > +\t\t\tstrbuf_strip_suffix(&path, \"/.git\");\n> > +\n> > +\t\t\tif (enlistment_root)\n> > +\t\t\t\tstrbuf_add(enlistment_root, path.buf, len);\n> > +\n> > +\t\t\tenlistment_found = 1;\n> > +\t\t\tbreak;\n> > +\t\t}\n>\n> This special casing of \"normally the top of the working tree is\n> enlisted, but if the repository is called src/, then we enslist\n> one level up\" is a bit of eyesore because\n>\n>  (1) it is unclear why such a directory with 'src/' subdirectory is\n>      so special, and\n>\n>  (2) it fails to serve those who has the same need but named their\n>      source subdirectory differently (like 'source/').\n\nAll true. I wish we had come up with a better way, or with a way to\noverride this via an option.\n\nUnfortunately, we are now bound by the fact that there are already users\nout there...\n\n> \"The design decisions we made are all part of being opinionated\" can\n> all explain it away, but at least we should let the users know where\n> the opinionated choices scalar makes want to lead them to, and this\n> \"src/\" stuff needs a bit of clarification.  Perhaps a documentation\n> will be added in later steps?\n\nI had hoped that the initial blurb of the manual page was sufficient, but\nyou're right, the `register` subcommand is particular in that it allows to\nforce Scalar to consider the worktree to be identical to the Scalar\nenlistment. I added this:\n\n\tdiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\n\tindex 1593da45eae..568987064b2 100644\n\t--- a/contrib/scalar/scalar.txt\n\t+++ b/contrib/scalar/scalar.txt\n\t@@ -40,6 +40,10 @@ register [<enlistment>]::\n\t\tand starts background maintenance. If `<enlistment>` is not provided,\n\t\tthen the enlistment associated with the current working directory is\n\t\tregistered.\n\t++\n\t+Note: when this subcommand is called in a worktree that is called `src/`, its\n\t+parent directory is considered to be the Scalar enlistment. If the worktree is\n\t+_not_ called `src/`, it itself will be considered to be the Scalar enlistment.\n\n> > +\tfor (i = 0; config[i].key; i++) {\n> > +\t\tif (git_config_get_string(config[i].key, &value)) {\n> > +\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"created\");\n> > +\t\t\tif (git_config_set_gently(config[i].key,\n> > +\t\t\t\t\t\t  config[i].value) < 0)\n> > +\t\t\t\treturn error(_(\"could not configure %s=%s\"),\n> > +\t\t\t\t\t     config[i].key, config[i].value);\n> > +\t\t} else {\n> > +\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"exists\");\n> > +\t\t\tfree(value);\n> > +\t\t}\n>\n> I wonder if we should have a table of configuration variables and\n> their default values.  The above code implements a skewed \"we only\n> avoid overriding what is explicitly configured\".  A variable that\n> the user left unconfigured because the user found its default\n> satisfactory will be overridden, and if the value scalar wants to\n> use happens to be the default value, we leave an explicit\n> configuration to that default value in the resulting configuration\n> file.\n>\n> But I think the above is the best we can do without such a central\n> registry of configuration variables.\n\nEven with such a central registry, there would still be the question\nwhether the user, by staying with the default, wanted Git (or in this\ninstance, Scalar) to keep using the old default. The intention is\nunfortunately not clear just from setting the variable.\n\nSo I think this is the best we can do.\n\nCiao,\nDscho\n"},{"id":"434638","messageId":"nycvar.QRO.7.76.6.2109031747290.55@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"877dg2xbjp.fsf@evledraar.gmail.com","subject":"Re: [PATCH 10/15] scalar: implement the `run` command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-09-03T15:50:16Z","receivedAt":"2021-09-03T15:50:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Ævar,\n\nOn Tue, 31 Aug 2021, Ævar Arnfjörð Bjarmason wrote:\n\n> On Mon, Aug 30 2021, Derrick Stolee via GitGitGadget wrote:\n>\n> > +\tconst char *usagestr[] = { NULL, NULL };\n>\n> Missing usage strings?\n\nThis command will show a generated usage, i.e. a non-static string. It\ntherefore cannot be specified here already. See the `strbuf_*()` calls\npopulating `buf` and the `usagestr[0] = buf.buf;` assignment.\n\n> > +\tif (argc == 0)\n>\n> Style nit (per style guide): s/argc == 0/!argc/g.\n\nIt is true that we often do this, but in this instance it would be\nmisleading: `argc` is a counter, not a Boolean.\n\n> > +\tif (!strcmp(\"all\", argv[0]))\n> > +\t\ti = -1;\n>\n> Style nit (per style guide): missing braces here.\n\nThe style guide specifically allows my preference to leave single-line\nblocks without curlies.\n\nCiao,\nJohannes\n"},{"id":"434639","messageId":"nycvar.QRO.7.76.6.2109031750291.55@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"874kb6xbf9.fsf@evledraar.gmail.com","subject":"Re: [PATCH 11/15] scalar: allow reconfiguring an existing enlistment","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-09-03T15:53:45Z","receivedAt":"2021-09-03T15:53:40Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Ævar,\n\nOn Tue, 31 Aug 2021, Ævar Arnfjörð Bjarmason wrote:\n\n>\n> On Mon, Aug 30 2021, Johannes Schindelin via GitGitGadget wrote:\n>\n> > This comes in handy during Scalar upgrades, or when config settings were\n> > messed up by mistake.\n>\n> > [...]\n> >  \t\tconst char *key;\n> >  \t\tconst char *value;\n> > +\t\tint overwrite_on_reconfigure;\n>\n> If you make this a \"keep_on_reconfigure\", then ...\n\nI do not think that this would be a better name, or that renaming this\nfield would do anything except cause more work for me.\n\n>\n> >  \t} config[] = {\n> > -\t\t{ \"am.keepCR\", \"true\" },\n> > -\t\t{ \"core.FSCache\", \"true\" },\n> > -\t\t{ \"core.multiPackIndex\", \"true\" },\n> > -\t\t{ \"core.preloadIndex\", \"true\" },\n> > +\t\t/* Required */\n> > +\t\t{ \"am.keepCR\", \"true\", 1 },\n> > +\t\t{ \"core.FSCache\", \"true\", 1 },\n> > +\t\t{ \"core.multiPackIndex\", \"true\", 1 },\n> > +\t\t{ \"core.preloadIndex\", \"true\", 1 },\n>\n> You won't need the churn/boilerplate of adding \"1\" to everything here,\n> but can just change the initial patch to use designated initializers.\n>\n> That along with a throwaway macro like:\n>\n> #define SCALAR_CFG_TRUE(k) (.key = k, .value = \"true\")\n> #define SCALAR_CFG_FALSE(k) (.key = k, .value = \"false\")\n>\n> Might (or might not) make this even easier to eyeball...\n\nTo me, it makes things less readable. There is an entire section with the\nheader `/* Optional */` below, and I want this list to stay as readable as\nit is now.\n\nCiao,\nDscho\n"},{"id":"434640","messageId":"CAPig+cQKKPq0cUn4iFpqaYOrwWnBTg0RhgHRztOGxB2Mm-GVUA@mail.gmail.com","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2109031722460.55@tvgsbejvaqbjf.bet","subject":"Re: [PATCH 12/15] scalar: teach 'reconfigure' to optionally handle all registered enlistments","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-09-03T17:02:49Z","receivedAt":"2021-09-03T17:03:03Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Sep 3, 2021 at 11:23 AM Johannes Schindelin\n<Johannes.Schindelin@gmx.de> wrote:\n> On Tue, 31 Aug 2021, Eric Sunshine wrote:\n> > On Mon, Aug 30, 2021 at 5:35 PM Johannes Schindelin via GitGitGadget\n> > > +With the `--all` option, all enlistments currently registered with Scalar\n> > > +will be reconfigured. This option is meant to to be run every time Scalar\n> > > +was upgraded.\n> >\n> > s/was/is/\n>\n> I wanted to convey a temporal order, so I changed it to \"every time after\n> Scalar is upgraded\". Okay?\n\nI think I understood the intent of the original, but it causes a\ngrammatical hiccup. Your revised version can work, although I might\nwrite it this way:\n\n    This option is meant to be run each time Scalar is upgraded.\n\nHowever, perhaps that is too ambiguous and some users may think that\nthe process of upgrading Scalar will automatically run this command,\nand you'd like to make it clear that it is the user's responsibility.\nSo, perhaps:\n\n    Use this option after each Scalar upgrade.\n\nor something.\n"},{"id":"434645","messageId":"xmqq35qlh91x.fsf@gitster.g","threadId":"56409","inReplyTo":"a7c9870a-9af3-bac8-1964-358ecdebe344@gmail.com","subject":"Re: [PATCH 08/15] scalar: implement the `clone` subcommand","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-09-03T17:18:34Z","receivedAt":"2021-09-03T17:18:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Derrick Stolee <stolee@gmail.com> writes:\n\n>> Ahh, you do not do \"git clone\"; you do \"git init\", set things up,\n>> and then \"git fetch\" and checkout, all manually.\n>> \n>> Which is kind of shame.\n>> \n>> I wonder if it is a cleaner implementation to give a new option to\n>> \"git clone\" that gives a command sequence (not necessarily have to\n>> be implemented as a shell script) that specifies necessary\n>> pre-configuration steps to be done before the command starts the\n>> transfer step.\n>\n> I agree that 'git clone' plus maybe some more improvements like\n> '--sparse=cone' to set up cone-mode sparse-checkout would be good.\n> And also the implementation being contributed here is cleaner if\n> we can use 'git clone'.\n>\n> We are trying to balance a clean upstream implementation with some\n> custom things that we still need in our microsoft/git fork to\n> handle the integration with the GVFS Protocol (i.e. partial clone\n> on Azure Repos). That customization is cleaner to keep here in the\n> scalar code instead of adding an option to 'git clone'.\n\nOh, there is no disagreement on that point, at least in the short\nterm.  I was wondering why \"clone\" subcommand needs a duplicated\nlogic that should be unnecessary, before realizing that this was\nnot implemented as a wrapper to (possibly updated) \"clone\", and\nI agree that starting with a looser coupling like this step does\nis easier to everybody.\n\n\"Kind of shame\" is just that I wished we had already prepared \"git\nclone\" side to accept customization more easily before its various\ndistinct phases (new repository creation, where a custom logic may\nwant to affect the name and location of it and how \"git init\" is\ndriven, initial \"fetch\", where a custom logic may want to affect the\nfetch refspec and its parameters like depths and cones, and initial\n\"checkout\") do their things.  If we allowed such plug-in of logic to\naffect how \"git clone\" worked already, it would have been possible\nto do \"scalar clone\" with much less code.  It also would allow us to\nreorganize the \"clone --local\" hack in a way that is easier to\nreason about (I think even in today's code, the way I hooked it up\ncan be seen which is quite messy).  It may even help folks who want\nto extend \"git clone\" to clone a repository recursively its\nsubmodules with project-specific customizations (like which ones to\nclone by default, etc.).\n\nI suspect that learning from the way \"scalar clone\" is done on top\nof \"init\" + \"fetch\" + \"checkout\" in this initial series may help us\nextend \"git clone\" later to fill such needs.\n\n> If your opinion to switch to 'git clone' is a strong one, then I\n> could see us doing that change. I just want you to be aware of the\n> hidden reasons for choices like these.\n\nNot at all at this moment.\n\nIt is mostly that the way \"init\" + \"fetch\" + \"checkout\" was done in\nthis step reminded me of a much longer-term wish I have had for a\nwhile.\n"},{"id":"434646","messageId":"xmqqwnnxftzn.fsf@gitster.g","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2109031709190.55@tvgsbejvaqbjf.bet","subject":"Re: [PATCH 08/15] scalar: implement the `clone` subcommand","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-09-03T17:29:16Z","receivedAt":"2021-09-03T17:29:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> It would even work if the current line is shorter, but as you point out:\n> it is wasteful. And it could be improved to be more readable. I reworked\n> it, and it now looks like this:\n>\n> \tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n> \t\tconst char *line = out.buf;\n>\n> \t\twhile (*line) {\n> \t\t\tconst char *eol = strchrnul(line, '\\n'), *p;\n> \t\t\tsize_t len = eol - line;\n> \t\t\tchar *branch;\n>\n> \t\t\tif (!skip_prefix(line, \"ref: \", &p) ||\n> \t\t\t    !strip_suffix_mem(line, &len, \"\\tHEAD\")) {\n> \t\t\t\tline = eol + (*eol == '\\n');\n> \t\t\t\tcontinue;\n> \t\t\t}\n>\n> \t\t\teol = line + len;\n> \t\t\tif (skip_prefix(p, \"refs/heads/\", &p)) {\n> \t\t\t\tbranch = xstrndup(p, eol - p);\n> \t\t\t\tstrbuf_release(&out);\n> \t\t\t\treturn branch;\n> \t\t\t}\n>\n> \t\t\terror(_(\"remote HEAD is not a branch: '%.*s'\"),\n> \t\t\t      (int)(eol - p), p);\n> \t\t\tstrbuf_release(&out);\n> \t\t\treturn NULL;\n> \t\t}\n> \t}\n>\n> It now parses the output line by line, looking for the expected prefix and\n> suffix, then verifies the ref name format, and either returns the short\n> branch name or errors out with the message that this is not a branch.\n\nIt is much easier to read and understand how the loop works with\nabove.\n\n>> > +\t\t\terror(_(\"remote HEAD is not a branch: '%.*s'\"),\n>> > +\t\t\t      (int)(head - ref), ref);\n>> > +\t\t\tstrbuf_release(&out);\n>> > +\t\t\treturn NULL;\n>>\n>> OK.  Any symref whose basename is HEAD in their remote-tracking\n>> hierarchy would have been rejected earlier in the loop.\n>>\n>> Is there a particular reason why we return early here, instead of\n>> breaking out of hte loop and let the generic \"failed to get\" code\n>> path below to handle this case?\n>\n> Yes, the reason is that I wanted to err on the side of caution. If the\n> remote repository reports a default branch that is not a default branch at\n> all, I do not want to pretend that things are fine and then run into\n> trouble later when we set up a non-branch as remote-tracking target or\n> something like that.\n\nWouldn't we have the same problem when the remote end does not\nadvertise HEAD and we fall back to \"local default\", though?  We'd\nrun into trouble later as we use \"local default\" that may correspond\nto a non-branch there as remote-tracking target, or something like\nthat.\n\nNot that I care too deeply in the error case, though.  I just felt\nthat this early return was an uneven way to follow the principle to\nerr on the side of caution, as we continue with the local default\nwhen the other side fails to tell us what their HEAD points at.\n\nThanks.\n"},{"id":"434647","messageId":"xmqqsfylftom.fsf@gitster.g","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2109031724560.55@tvgsbejvaqbjf.bet","subject":"Re: [PATCH 04/15] scalar: 'register' sets recommended config and starts maintenance","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-09-03T17:35:53Z","receivedAt":"2021-09-03T17:35:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n>> \"The design decisions we made are all part of being opinionated\" can\n>> all explain it away, but at least we should let the users know where\n>> the opinionated choices scalar makes want to lead them to, and this\n>> \"src/\" stuff needs a bit of clarification.  Perhaps a documentation\n>> will be added in later steps?\n>\n> I had hoped that the initial blurb of the manual page was sufficient, but\n> you're right, the `register` subcommand is particular in that it allows to\n> force Scalar to consider the worktree to be identical to the Scalar\n> enlistment. I added this:\n\nSorry, if it weren't clear that I was commenting on each step as I\nread along without peeking later steps.  I think I saw it was\nwritten somewhere that this was to encourage use of read-only\ndirectory that keeps the sources with build artifacts and crufts\ncreated outside it (so forests of projects will not have the source\ndirectories, each of which has its own .git/, next to each other---\ninstead we would have shell directories, each with its own src/ and\nsrc/.git, next to each other).  The additional documentation below\nis a good thing to have handy when readers learn how to use\n\"register\" (or more generally, what an \"enlistment\" is).  As long as\nthe motivation behind that design is given somewhere (not necessarily\nhere) for readers to discover, I am OK with the design.\n\n> \tdiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\n> \tindex 1593da45eae..568987064b2 100644\n> \t--- a/contrib/scalar/scalar.txt\n> \t+++ b/contrib/scalar/scalar.txt\n> \t@@ -40,6 +40,10 @@ register [<enlistment>]::\n> \t\tand starts background maintenance. If `<enlistment>` is not provided,\n> \t\tthen the enlistment associated with the current working directory is\n> \t\tregistered.\n> \t++\n> \t+Note: when this subcommand is called in a worktree that is called `src/`, its\n> \t+parent directory is considered to be the Scalar enlistment. If the worktree is\n> \t+_not_ called `src/`, it itself will be considered to be the Scalar enlistment.\n\nThanks.\n\n"},{"id":"434648","messageId":"xmqqk0jxft1p.fsf@gitster.g","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2109031747290.55@tvgsbejvaqbjf.bet","subject":"Re: [PATCH 10/15] scalar: implement the `run` command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-09-03T17:49:38Z","receivedAt":"2021-09-03T17:49:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> Hi Ævar,\n>\n> On Tue, 31 Aug 2021, Ævar Arnfjörð Bjarmason wrote:\n>\n>> On Mon, Aug 30 2021, Derrick Stolee via GitGitGadget wrote:\n>>\n>> > +\tconst char *usagestr[] = { NULL, NULL };\n>>\n>> Missing usage strings?\n>\n> This command will show a generated usage, i.e. a non-static string. It\n> therefore cannot be specified here already. See the `strbuf_*()` calls\n> populating `buf` and the `usagestr[0] = buf.buf;` assignment.\n>\n>> > +\tif (argc == 0)\n>>\n>> Style nit (per style guide): s/argc == 0/!argc/g.\n>\n> It is true that we often do this, but in this instance it would be\n> misleading: `argc` is a counter, not a Boolean.\n\nThat argument could be a plausible excuse to deviate from the style\nif it were\n\n\tif (argc == 0)\n\t\tdo no args case;\n\telse if (argc == 1)\n\t\tdo one arg case;\n\telse if (argc == 2)\n\t\tdo two args case;\n\t...\n\nReplacing the first one with \"if (!argc)\" may make it less readable.\n\nBut I do not think the reasoning applies here\n\n\tif (argc == 0)\n\t\tdo a thing that applies only to no args case;\n\nwithout \"else\".  This is talking about \"do we have any argument? Yes\nor no?\" Boolean here.\n\n>> > +\tif (!strcmp(\"all\", argv[0]))\n>> > +\t\ti = -1;\n>>\n>> Style nit (per style guide): missing braces here.\n>\n> The style guide specifically allows my preference to leave single-line\n> blocks without curlies.\n\nActually, the exception goes the other way, no?\n\nWe generally want to avoid such an unnecessary braces around a\nsingle statement block.  But when we have an else clause that has a\nblock with multiple statements (hence braces are required), as an\nexception, the guide asks you to write braces around the body of the\nif side for consistency.\n\nWhen you only have just a couple of lines on the \"else {}\" side, I\ndo not think it matters too much either way for readability, though.\nI cannot see the \"else\" side in the above clause, but IIRC it wasn't\njust a few lines, was it?\n\nThanks.\n"},{"id":"434649","messageId":"pull.1005.v2.git.1630691688.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.git.1630359290.gitgitgadget@gmail.com","subject":"[PATCH v2 00/15] [RFC] Upstreaming the Scalar command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-03T17:54:32Z","receivedAt":"2021-09-03T17:54:54Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"tl;dr: This series contributes the Scalar command to the Git project. This\ncommand provides an opinionated way to create and configure repositories\nwith a focus on very large repositories.\n\n\nBackground\n==========\n\nYears ago, Microsoft wanted to move the source code of the Windows operating\nsystem to Git. The challenge there was to prove that Git could scale to\nmassive monorepos. The VFS for Git (formerly GVFS) project was born to take\nup that challenge.\n\nThe final solution included a virtual filesystem (with both user-mode and\nkernel components) and a customized fork of Git for Windows. This solution\ncontained several key concepts, such as only populating a portion of the\nworking directory, demand-fetching blobs, and performing periodic repo\nmaintenance in the background. However, the required kernel drivers made it\ndifficult to port the solution to other platforms.\n\nBut it was realized that many of these key concepts were independent of the\nactual VFS and its projection of the working directory. The Scalar project\nwas created to make that separation, refine the key concepts, and then\nextract those features into the new Scalar command.\n\n\nThe present\n===========\n\nThe Scalar project provides a completely functional non-virtual experience\nfor monorepos. But why stop there. The Scalar project was designed to be a\nself-destructing vehicle to allow those key concepts to be moved into core\nGit itself for the benefit of all. For example, partial clone,\nsparse-checkout, and background maintenance have already been upstreamed and\nremoved from Scalar proper. This patch series provides a C-based\nimplementation of the final remaining portions of the Scalar command. This\nwill make it easier for users to experiment with the Scalar command. It will\nalso make it substantially easier to experiment with moving functionality\nfrom Scalar into core Git, while maintaining backwards-compatibility for\nexisting Scalar users.\n\nThe C-based Scalar has been shipped to Scalar users, and can be tested by\nany interested reader:\nhttps://github.com/microsoft/git/releases/tag/v2.33.0.vfs.0.0 (it offers a\nGit for Windows installer, a macOS package and an Ubuntu package).\n\n\nOpportunities\n=============\n\nApart from providing the Scalar command, this contribution is intended to\nserve as a basis for further mailing list discussions on moving (some of)\nthese key concepts into the main Git commands.\n\nFor example, we previously discussed the idea of a \"git big-clone\" that does\nmuch of what \"scalar clone\" is doing. This patch series is a step to make\nsuch functionality exist in the Git code base while we simmer on what such a\n\"git big-clone\" command-line interface would look like.\n\nThis is one of many possible ways to do this. Creating a 'git big-clone'\ncould lock Git into backwards compatibility concerns so it is necessary to\napproach such an endeavor with caution. As a discussion starter, the scalar\nclone <url> command does roughly this:\n\n 1. git clone --sparse --filter=blob:none /src\n 2. git -C /src sparse-checkout init --cone\n 3. git -C /src config (many times)\n 4. git -C /src maintenance start\n\nIt is my hope inspire discussions about what parts of Scalar could go into\ncore Git, and where, and in which form. While we wish to maintain\nbackwards-compatibility of Scalar's command-line interface (because it is\nalready in use), by having the Scalar code in the same code base as Git's,\nit will be much easier to move functionality without having to maintain\nloose version coupling between independently-versioned Scalar and Git. The\ntight version-coupling, along with having access to libgit.a also allows the\nC-based implementation of Scalar to be much smaller than the original .NET\nversion.\n\nFor example, we might choose in the future to implement, say, git clone\n--scale=partial,cone to initialize a partial clone with a cone-sparse\ncheckout, that would not only be totally doable, and not only would we\nalready have precedent and data to prove that this actually makes engineers\nhappy who have to work on ginormous repositories, but we could then also\nimplement it by moving parts of contrib/scalar/ to builtin/ (where\ncontrib/scalar/ would then call the built-ins accordingly rather than\nhard-coding the defaults itself).\n\nWe now also have the opportunity to discuss the merits of Scalar's clone\ncaching, which is not actually part of this patch series because it is a bit\ncoupled with the GVFS parts of microsoft/git for the moment, where clones\nautomatically get registered with a populated alternate repository that is\nidentified by the URL, meaning: subsequent clones of the same repository are\nvastly faster than the first one because they do not actually download the\nalready-received objects again, they access the cache instead.\n\nAnother thing that I could imagine to be discussed at length is the\ndistinction between enlistment and worktree (where the latter is the actual\nGit worktree and usually lives in the src/ subdirectory of the former). This\nencourages untracked and ignored files to be placed outside the worktree,\nmaking Git's job much easier. This idea, too, might find its way in one way\nor another into Git proper.\n\nThese are just a few concepts in Scalar that do not yet have equivalents in\nGit. By putting this initial implementation into contrib/, we create a\nfoundation for future discussions of these concepts.\n\nWe plan on updating the recommended config settings in scalar register as\nnew Git features are available (such as builtin FSMonitor and sparse-index,\nwhen ready). To facilitate upgrading existing Scalar enlistments, their\npaths are automatically added to the [scalar] section of the global Git\nconfig, and the scalar reconfigure --all command will process all of them.\n\n\nEpilogue\n========\n\nNow, to address some questions that I imagine every reader has who made it\nthis far:\n\n * Why not put the Scalar functionality directly into a built-in? Creating a\n   Git builtin requires scrutiny over every aspect of the feature, which is\n   difficult to do while also maintaining the command-line interface\n   contract and expected behavior of the Scalar command (there are existing\n   users, after all). By having the Scalar command in contrib/, we present a\n   simple option for users to have these features in the short term while\n   the Git contributor community decides which bits to absorb into Git\n   built-ins.\n * Why implement the Scalar command in the Git codebase? We ported Scalar to\n   the microsoft/git fork for several reasons. First, we realized it was\n   possible now that the core features exist inside Git itself. Second,\n   compiling Scalar directly within a version of Git allows us to remove a\n   version compatibility check from each config option that might or might\n   not apply based on the installed Git version. Finally, this new location\n   has greatly simplified our release process and the installation process\n   for users. We now have ways to install Scalar with microsoft/git via\n   winget, brew, and apt-get. This has been the case since we shipped\n   v2.32.0 to our users, read: this setup has served us well already.\n * Why contribute Scalar to the Git project? We are biased, of course, yet\n   we do have evidence that the Scalar command is a helpful tool that offers\n   an simple way to handle huge repositories with ease. By contributing it\n   to the core Git project, we are able to share it with more users,\n   especially some users who do not want to install the microsoft/git fork.\n   We intend to include Scalar as a component in git-for-windows/git, but\n   are contributing it here first. Further, we think there is benefit to the\n   Git developer community as this presents an example of how to set certain\n   defaults that work for large repositories.\n * Does this integrate with the built-in FSMonitor yet? No, not yet. I do\n   have a couple of add-on patch series lined up, one of them being the\n   integration with the built-in FSMonitor, which obviously has to wait\n   until the FSMonitor patch series advances further.\n\nChanges since v1:\n\n * A couple typos were fixed\n * The code parsing the output of ls-remote was made more readable\n * The indentation used in scalar.txt now consistently uses tabs\n * We no longer hard-code core.bare = false when registering with Scalar\n\nDerrick Stolee (4):\n  scalar: 'register' sets recommended config and starts maintenance\n  scalar: 'unregister' stops background maintenance\n  scalar: implement 'scalar list'\n  scalar: implement the `run` command\n\nJohannes Schindelin (10):\n  scalar: create a rudimentary executable\n  scalar: start documenting the command\n  scalar: create test infrastructure\n  scalar: let 'unregister' handle a deleted enlistment directory\n    gracefully\n  scalar: implement the `clone` subcommand\n  scalar: teach 'clone' to support the --single-branch option\n  scalar: allow reconfiguring an existing enlistment\n  scalar: teach 'reconfigure' to optionally handle all registered\n    enlistments\n  scalar: implement the `version` command\n  scalar: accept -C and -c options before the subcommand\n\nMatthew John Cheetham (1):\n  scalar: implement the `delete` command\n\n Makefile                         |   8 +\n contrib/scalar/.gitignore        |   5 +\n contrib/scalar/Makefile          |  57 +++\n contrib/scalar/scalar.c          | 844 +++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        | 156 ++++++\n contrib/scalar/t/Makefile        |  78 +++\n contrib/scalar/t/t9099-scalar.sh |  88 ++++\n 7 files changed, 1236 insertions(+)\n create mode 100644 contrib/scalar/.gitignore\n create mode 100644 contrib/scalar/Makefile\n create mode 100644 contrib/scalar/scalar.c\n create mode 100644 contrib/scalar/scalar.txt\n create mode 100644 contrib/scalar/t/Makefile\n create mode 100755 contrib/scalar/t/t9099-scalar.sh\n\n\nbase-commit: ebf3c04b262aa27fbb97f8a0156c2347fecafafb\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1005%2Fdscho%2Fscalar-the-beginning-v2\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1005/dscho/scalar-the-beginning-v2\nPull-Request: https://github.com/gitgitgadget/git/pull/1005\n\nRange-diff vs v1:\n\n  1:  b8c7d3f8450 =  1:  b8c7d3f8450 scalar: create a rudimentary executable\n  2:  4f886575dcf =  2:  4f886575dcf scalar: start documenting the command\n  3:  bcfde9bc765 =  3:  bcfde9bc765 scalar: create test infrastructure\n  4:  3786f4c597f !  4:  ee3e26a0c4e scalar: 'register' sets recommended config and starts maintenance\n     @@ contrib/scalar/scalar.c\n      +\t\t */\n      +\t\t{ \"core.untrackedCache\", \"false\" },\n      +#endif\n     -+\t\t{ \"core.bare\", \"false\" },\n      +\t\t{ \"core.logAllRefUpdates\", \"true\" },\n      +\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n      +\t\t{ \"credential.validate\", \"false\" }, /* GCM4W-only */\n     @@ contrib/scalar/scalar.txt: will be identical to the worktree.\n      +~~~~~~~~\n      +\n      +register [<enlistment>]::\n     -+    Adds the enlistment's repository to the list of registered repositories\n     -+    and starts background maintenance. If `<enlistment>` is not provided,\n     -+    then the enlistment associated with the current working directory is\n     -+    registered.\n     ++\tAdds the enlistment's repository to the list of registered repositories\n     ++\tand starts background maintenance. If `<enlistment>` is not provided,\n     ++\tthen the enlistment associated with the current working directory is\n     ++\tregistered.\n     +++\n     ++Note: when this subcommand is called in a worktree that is called `src/`, its\n     ++parent directory is considered to be the Scalar enlistment. If the worktree is\n     ++_not_ called `src/`, it itself will be considered to be the Scalar enlistment.\n      +\n       SEE ALSO\n       --------\n  5:  2a6ac170e6b !  5:  6142f75875b scalar: 'unregister' stops background maintenance\n     @@ contrib/scalar/scalar.txt: SYNOPSIS\n       \n       DESCRIPTION\n       -----------\n     -@@ contrib/scalar/scalar.txt: register [<enlistment>]::\n     -     then the enlistment associated with the current working directory is\n     -     registered.\n     +@@ contrib/scalar/scalar.txt: Note: when this subcommand is called in a worktree that is called `src/`, its\n     + parent directory is considered to be the Scalar enlistment. If the worktree is\n     + _not_ called `src/`, it itself will be considered to be the Scalar enlistment.\n       \n      +Unregister\n      +~~~~~~~~~~\n      +\n      +unregister [<enlistment>]::\n     -+    Remove the specified repository from the list of repositories\n     -+    registered with Scalar and stop the scheduled background maintenance.\n     ++\tRemove the specified repository from the list of repositories\n     ++\tregistered with Scalar and stop the scheduled background maintenance.\n      +\n       SEE ALSO\n       --------\n  6:  087fc9be194 =  6:  82dd253154f scalar: let 'unregister' handle a deleted enlistment directory gracefully\n  7:  c272ff4069d !  7:  fb7c931ddb3 scalar: implement 'scalar list'\n     @@ contrib/scalar/scalar.txt: an existing Git worktree with Scalar whose name is no\n      +~~~~\n      +\n      +list::\n     -+    To see which repositories are currently registered by the service, run\n     -+    `scalar list`. This subcommand does not need to be run inside a Scalar\n     -+    enlistment.\n     ++\tTo see which repositories are currently registered by the service, run\n     ++\t`scalar list`. This subcommand does not need to be run inside a Scalar\n     ++\tenlistment.\n      +\n       Register\n       ~~~~~~~~\n  8:  2cbf0b61113 !  8:  f3223c10788 scalar: implement the `clone` subcommand\n     @@ Commit message\n          experience and experiments of the Microsoft Windows and the Microsoft\n          Office development teams.\n      \n     -    Note: We intentionally use a slightly wasteful `set_config()` function\n     -    (which does not reuse a single `strbuf`, for example, though performance\n     -    _really_ does not matter here) for convenience and readability.\n     -\n     -    Also note: since the `scalar clone` command is by far the most commonly\n     +    Note: since the `scalar clone` command is by far the most commonly\n          called `scalar` subcommand, we document it at the top of the manual\n          page.\n      \n     @@ contrib/scalar/scalar.c: static int unregister_dir(void)\n      +\n      +\tcp.git_cmd = 1;\n      +\tstrvec_pushl(&cp.args, \"ls-remote\", \"--symref\", url, \"HEAD\", NULL);\n     -+\tstrbuf_addstr(&out, \"-\\n\");\n      +\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n     -+\t\tchar *ref = out.buf;\n     -+\n     -+\t\twhile ((ref = strstr(ref + 1, \"\\nref: \"))) {\n     -+\t\t\tconst char *p;\n     -+\t\t\tchar *head, *branch;\n     ++\t\tconst char *line = out.buf;\n      +\n     -+\t\t\tref += strlen(\"\\nref: \");\n     -+\t\t\thead = strstr(ref, \"\\tHEAD\");\n     ++\t\twhile (*line) {\n     ++\t\t\tconst char *eol = strchrnul(line, '\\n'), *p;\n     ++\t\t\tsize_t len = eol - line;\n     ++\t\t\tchar *branch;\n      +\n     -+\t\t\tif (!head || memchr(ref, '\\n', head - ref))\n     ++\t\t\tif (!skip_prefix(line, \"ref: \", &p) ||\n     ++\t\t\t    !strip_suffix_mem(line, &len, \"\\tHEAD\")) {\n     ++\t\t\t\tline = eol + (*eol == '\\n');\n      +\t\t\t\tcontinue;\n     ++\t\t\t}\n      +\n     -+\t\t\tif (skip_prefix(ref, \"refs/heads/\", &p)) {\n     -+\t\t\t\tbranch = xstrndup(p, head - p);\n     ++\t\t\teol = line + len;\n     ++\t\t\tif (skip_prefix(p, \"refs/heads/\", &p)) {\n     ++\t\t\t\tbranch = xstrndup(p, eol - p);\n      +\t\t\t\tstrbuf_release(&out);\n      +\t\t\t\treturn branch;\n      +\t\t\t}\n      +\n      +\t\t\terror(_(\"remote HEAD is not a branch: '%.*s'\"),\n     -+\t\t\t      (int)(head - ref), ref);\n     ++\t\t\t      (int)(eol - p), p);\n      +\t\t\tstrbuf_release(&out);\n      +\t\t\treturn NULL;\n      +\t\t}\n     @@ contrib/scalar/scalar.txt: an existing Git worktree with Scalar whose name is no\n      +~~~~~\n      +\n      +clone [<options>] <url> [<enlistment>]::\n     -+    Clones the specified repository, similar to linkgit:git-clone[1]. By\n     -+    default, only commit and tree objects are cloned. Once finished, the\n     -+    worktree is located at `<enlistment>/src`.\n     ++\tClones the specified repository, similar to linkgit:git-clone[1]. By\n     ++\tdefault, only commit and tree objects are cloned. Once finished, the\n     ++\tworktree is located at `<enlistment>/src`.\n      ++\n      +The sparse-checkout feature is enabled (except when run with `--full-clone`)\n      +and the only files present are those in the top-level directory. Use\n     @@ contrib/scalar/scalar.txt: an existing Git worktree with Scalar whose name is no\n      +\n      +-b <name>::\n      +--branch <name>::\n     -+    Instead of checking out the branch pointed to by the cloned repository's\n     -+    HEAD, check out the `<name>` branch instead.\n     ++\tInstead of checking out the branch pointed to by the cloned\n     ++\trepository's HEAD, check out the `<name>` branch instead.\n      +\n      +--[no-]full-clone::\n     -+    A sparse-checkout is initialized by default. This behavior can be turned\n     -+    off via `--full-clone`.\n     ++\tA sparse-checkout is initialized by default. This behavior can be\n     ++\tturned off via `--full-clone`.\n      +\n       List\n       ~~~~\n       \n       list::\n     -     To see which repositories are currently registered by the service, run\n     --    `scalar list`. This subcommand does not need to be run inside a Scalar\n     --    enlistment.\n     -+    `scalar list`. This subcommand, like `clone`, does not need to be run\n     -+    inside a Scalar enlistment.\n     + \tTo see which repositories are currently registered by the service, run\n     +-\t`scalar list`. This subcommand does not need to be run inside a Scalar\n     +-\tenlistment.\n     ++\t`scalar list`. This subcommand, like `clone`, does not need to be run\n     ++\tinside a Scalar enlistment.\n       \n       Register\n       ~~~~~~~~\n  9:  9af1c37c2ea !  9:  b3c4b3dccc6 scalar: teach 'clone' to support the --single-branch option\n     @@ contrib/scalar/scalar.txt: scalar - an opinionated repository management tool\n       scalar register [<enlistment>]\n       scalar unregister [<enlistment>]\n      @@ contrib/scalar/scalar.txt: subdirectories outside your sparse-checkout by using `git ls-tree HEAD`.\n     -     Instead of checking out the branch pointed to by the cloned repository's\n     -     HEAD, check out the `<name>` branch instead.\n     + \tInstead of checking out the branch pointed to by the cloned\n     + \trepository's HEAD, check out the `<name>` branch instead.\n       \n      +--[no-]single-branch::\n     -+    Clone only the history leading to the tip of a single branch,\n     -+    either specified by the `--branch` option or the primary\n     -+    branch remote's `HEAD` points at.\n     ++\tClone only the history leading to the tip of a single branch, either\n     ++\tspecified by the `--branch` option or the primary branch remote's\n     ++\t`HEAD` points at.\n      ++\n      +Further fetches into the resulting repository will only update the\n      +remote-tracking branch for the branch this option was used for the initial\n     @@ contrib/scalar/scalar.txt: subdirectories outside your sparse-checkout by using\n      +`--single-branch` clone was made, no remote-tracking branch is created.\n      +\n       --[no-]full-clone::\n     -     A sparse-checkout is initialized by default. This behavior can be turned\n     -     off via `--full-clone`.\n     + \tA sparse-checkout is initialized by default. This behavior can be\n     + \tturned off via `--full-clone`.\n      \n       ## contrib/scalar/t/t9099-scalar.sh ##\n      @@ contrib/scalar/t/t9099-scalar.sh: test_expect_success 'set up repository to clone' '\n 10:  c3f16bccd02 ! 10:  b7fc2dc29c8 scalar: implement the `run` command\n     @@ contrib/scalar/scalar.txt: scalar clone [--single-branch] [--branch <main-branch\n       DESCRIPTION\n       -----------\n      @@ contrib/scalar/scalar.txt: unregister [<enlistment>]::\n     -     Remove the specified repository from the list of repositories\n     -     registered with Scalar and stop the scheduled background maintenance.\n     + \tRemove the specified repository from the list of repositories\n     + \tregistered with Scalar and stop the scheduled background maintenance.\n       \n      +Run\n      +~~~\n      +\n      +scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]::\n     -+    Run the given maintenance task (or all tasks, if `all` was specified).\n     -+    Except for `all` and `config`, this subcommand simply hands off to\n     -+    linkgit:git-maintenance[1] (mapping `fetch` to `prefetch` and\n     -+    `pack-files` to `incremental-repack`).\n     ++\tRun the given maintenance task (or all tasks, if `all` was specified).\n     ++\tExcept for `all` and `config`, this subcommand simply hands off to\n     ++\tlinkgit:git-maintenance[1] (mapping `fetch` to `prefetch` and\n     ++\t`pack-files` to `incremental-repack`).\n      ++\n      +These tasks are run automatically as part of the scheduled maintenance,\n      +as soon as the repository is registered with Scalar. It should therefore\n 11:  13056f02018 ! 11:  9a834c23d08 scalar: allow reconfiguring an existing enlistment\n     @@ contrib/scalar/scalar.c: static int set_recommended_config(void)\n      -\t\t{ \"core.untrackedCache\", \"false\" },\n      +\t\t{ \"core.untrackedCache\", \"false\", 1 },\n       #endif\n     --\t\t{ \"core.bare\", \"false\" },\n      -\t\t{ \"core.logAllRefUpdates\", \"true\" },\n      -\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n      -\t\t{ \"credential.validate\", \"false\" }, /* GCM4W-only */\n     @@ contrib/scalar/scalar.c: static int set_recommended_config(void)\n      -\t\t{ \"feature.experimental\", \"false\" },\n      -\t\t{ \"fetch.unpackLimit\", \"1\" },\n      -\t\t{ \"fetch.writeCommitGraph\", \"false\" },\n     -+\t\t{ \"core.bare\", \"false\", 1 },\n      +\t\t{ \"core.logAllRefUpdates\", \"true\", 1 },\n      +\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\", 1 },\n      +\t\t{ \"credential.validate\", \"false\", 1 }, /* GCM4W-only */\n 12:  732a28c22fc ! 12:  79e9f5d203a scalar: teach 'reconfigure' to optionally handle all registered enlistments\n     @@ contrib/scalar/scalar.txt: After a Scalar upgrade, or when the configuration of\n       reconfigure the enlistment.\n       \n      +With the `--all` option, all enlistments currently registered with Scalar\n     -+will be reconfigured. This option is meant to to be run every time Scalar\n     -+was upgraded.\n     ++will be reconfigured. This option is meant to to be run every time after\n     ++Scalar is upgraded.\n      +\n       SEE ALSO\n       --------\n 13:  13afbd68812 ! 13:  94a21982652 scalar: implement the `delete` command\n     @@ contrib/scalar/scalar.txt: scalar register [<enlistment>]\n       DESCRIPTION\n       -----------\n      @@ contrib/scalar/scalar.txt: With the `--all` option, all enlistments currently registered with Scalar\n     - will be reconfigured. This option is meant to to be run every time Scalar\n     - was upgraded.\n     + will be reconfigured. This option is meant to to be run every time after\n     + Scalar is upgraded.\n       \n      +Delete\n      +~~~~~~\n      +\n      +delete <enlistment>::\n     -+    This subcommand lets you delete an existing Scalar enlistment from your\n     -+    local file system, unregistering the repository.\n     ++\tThis subcommand lets you delete an existing Scalar enlistment from your\n     ++\tlocal file system, unregistering the repository.\n      +\n       SEE ALSO\n       --------\n 14:  73d08c0c894 ! 14:  707d8e19683 scalar: implement the `version` command\n     @@ Commit message\n      \n          Since Scalar is now tightly coupled with Git, it does not make sense for\n          them to show different versions. Therefore, it shows the same output as\n     -    `git versions`. For backwards-compatibility with the .NET version,\n     +    `git version`. For backwards-compatibility with the .NET version,\n          `scalar version` prints to `stderr`, though (`git version` prints to\n          `stdout` instead).\n      \n 15:  6455b18f1b6 ! 15:  26e23b5c5e5 scalar: accept -C and -c options before the subcommand\n     @@ contrib/scalar/scalar.txt: The `scalar` command implements various subcommands,\n      +The following options can be specified _before_ the subcommand:\n      +\n      +-C <directory>::\n     -+    Before running the subcommand, change the working directory. This\n     -+    option imitates the same option of linkgit:git[1].\n     ++\tBefore running the subcommand, change the working directory. This\n     ++\toption imitates the same option of linkgit:git[1].\n      +\n      +-c <key>=<value>::\n     -+    For the duration of running the specified subcommand, configure this\n     -+    setting. This option imitates the same option of linkgit:git[1].\n     ++\tFor the duration of running the specified subcommand, configure this\n     ++\tsetting. This option imitates the same option of linkgit:git[1].\n      +\n       COMMANDS\n       --------\n\n-- \ngitgitgadget\n"},{"id":"434650","messageId":"b8c7d3f84508ae0fb300f47c726764f4cbf46be9.1630691688.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v2.git.1630691688.gitgitgadget@gmail.com","subject":"[PATCH v2 01/15] scalar: create a rudimentary executable","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-03T17:54:33Z","receivedAt":"2021-09-03T17:54:56Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe idea of Scalar (https://github.com/microsoft/scalar), and before\nthat, of VFS for Git, has always been to prove that Git _can_ scale, and\nto upstream whatever strategies have been demonstrated to help.\n\nWith this patch, we start the journey from that C# project to move what\nis left to Git's own `contrib/` directory, reimplementing it in pure C,\nwith the intention to facilitate integrating the functionality into core\nGit all while maintaining backwards-compatibility for existing Scalar\nusers (which will be much easier when both live in the same worktree).\nIt was always to plan to contribute all of the proven strategies back to\ncore Git.\n\nFor example, while the virtual filesystem provided by VFS for Git helped\nthe team developing the Windows operating system to move onto Git, while\ntrying to upstream it we realized that it cannot be done: getting the\nvirtual filesystem to work (which we only managed to implement fully on\nWindows, but not on, say, macOS or Linux), and the required server-side\nsupport for the GVFS protocol, made this not quite feasible.\n\nThe Scalar project learned from that and tackled the problem with\ndifferent tactics: instead of pretending to Git that the working\ndirectory is fully populated, it _specifically_ teaches Git about\npartial clone (which is based on VFS for Git's cache server), about\nsparse checkout (which VFS for Git tried to do transparently, in the\nfile system layer), and regularly runs maintenance tasks to keep the\nrepository in a healthy state.\n\nWith partial clone, sparse checkout and `git maintenance` having been\nupstreamed, there is little left that `scalar.exe` does that which\n`git.exe` cannot do. One such thing is that `scalar clone <url>` will\nautomatically set up a partial, sparse clone, and configure\nknown-helpful settings from the start.\n\nSo let's bring this convenience into Git's tree.\n\nThe idea here is that you can (optionally) build Scalar via\n\n\tmake -C contrib/scalar/Makefile\n\nThis will build the `scalar` executable and put it into the\ncontrib/scalar/ subdirectory.\n\nThe slightly awkward addition of the `contrib/scalar/*` bits to the\ntop-level `Makefile` are actually really required: we want to link to\n`libgit.a`, which means that we will need to use the very same `CFLAGS`\nand `LDFLAGS` as the rest of Git.\n\nAn early development version of this patch tried to replicate all the\nconditional code in `contrib/scalar/Makefile` (e.g. `NO_POLL`) just like\n`contrib/svn-fe/Makefile` used to do before it was retired. It turned\nout to be quite the whack-a-mole game: the SHA-1-related flags, the\nflags enabling/disabling `compat/poll/`, `compat/regex/`,\n`compat/win32mmap.c` & friends depending on the current platform... To\nput it mildly: it was a major mess.\n\nInstead, this patch makes minimal changes to the top-level `Makefile` so\nthat the bits in `contrib/scalar/` can be compiled and linked, and\nadds a `contrib/scalar/Makefile` that uses the top-level `Makefile` in a\nmost minimal way to do the actual compiling.\n\nNote: With this commit, we only establish the infrastructure, no\nScalar functionality is implemented yet; We will do that incrementally\nover the next few commits.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Makefile                  |  8 ++++++++\n contrib/scalar/.gitignore |  2 ++\n contrib/scalar/Makefile   | 34 ++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.c   | 36 ++++++++++++++++++++++++++++++++++++\n 4 files changed, 80 insertions(+)\n create mode 100644 contrib/scalar/.gitignore\n create mode 100644 contrib/scalar/Makefile\n create mode 100644 contrib/scalar/scalar.c\n\ndiff --git a/Makefile b/Makefile\nindex c3565fc0f8f..2d5c822f7a8 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2447,6 +2447,10 @@ endif\n .PHONY: objects\n objects: $(OBJECTS)\n \n+SCALAR_SOURCES := contrib/scalar/scalar.c\n+SCALAR_OBJECTS := $(SCALAR_SOURCES:c=o)\n+OBJECTS += $(SCALAR_OBJECTS)\n+\n dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d)\n dep_dirs := $(addsuffix .depend,$(sort $(dir $(OBJECTS))))\n \n@@ -2586,6 +2590,10 @@ $(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n \t\t$(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)\n \n+contrib/scalar/scalar$X: $(SCALAR_OBJECTS) GIT-LDFLAGS $(GITLIBS)\n+\t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \\\n+\t\t$(filter %.o,$^) $(LIBS)\n+\n $(LIB_FILE): $(LIB_OBJS)\n \t$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^\n \ndiff --git a/contrib/scalar/.gitignore b/contrib/scalar/.gitignore\nnew file mode 100644\nindex 00000000000..ff3d47e84d0\n--- /dev/null\n+++ b/contrib/scalar/.gitignore\n@@ -0,0 +1,2 @@\n+/*.exe\n+/scalar\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nnew file mode 100644\nindex 00000000000..40c03ad10e1\n--- /dev/null\n+++ b/contrib/scalar/Makefile\n@@ -0,0 +1,34 @@\n+QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir\n+QUIET_SUBDIR1  =\n+\n+ifneq ($(findstring s,$(MAKEFLAGS)),s)\n+ifndef V\n+\tQUIET_SUBDIR0  = +@subdir=\n+\tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n+\t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n+else\n+\texport V\n+endif\n+endif\n+\n+all:\n+\n+include ../../config.mak.uname\n+-include ../../config.mak.autogen\n+-include ../../config.mak\n+\n+TARGETS = scalar$(X) scalar.o\n+GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n+\n+all: scalar$X\n+\n+$(GITLIBS):\n+\t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)\n+\n+$(TARGETS): $(GITLIBS) scalar.c\n+\t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)\n+\n+clean:\n+\t$(RM) $(TARGETS)\n+\n+.PHONY: all clean FORCE\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nnew file mode 100644\nindex 00000000000..7cff29e0fcd\n--- /dev/null\n+++ b/contrib/scalar/scalar.c\n@@ -0,0 +1,36 @@\n+/*\n+ * The Scalar command-line interface.\n+ */\n+\n+#include \"cache.h\"\n+#include \"gettext.h\"\n+#include \"parse-options.h\"\n+\n+static struct {\n+\tconst char *name;\n+\tint (*fn)(int, const char **);\n+} builtins[] = {\n+\t{ NULL, NULL},\n+};\n+\n+int cmd_main(int argc, const char **argv)\n+{\n+\tstruct strbuf scalar_usage = STRBUF_INIT;\n+\tint i;\n+\n+\tif (argc > 1) {\n+\t\targv++;\n+\t\targc--;\n+\n+\t\tfor (i = 0; builtins[i].name; i++)\n+\t\t\tif (!strcmp(builtins[i].name, argv[0]))\n+\t\t\t\treturn !!builtins[i].fn(argc, argv);\n+\t}\n+\n+\tstrbuf_addstr(&scalar_usage,\n+\t\t      N_(\"scalar <command> [<options>]\\n\\nCommands:\\n\"));\n+\tfor (i = 0; builtins[i].name; i++)\n+\t\tstrbuf_addf(&scalar_usage, \"\\t%s\\n\", builtins[i].name);\n+\n+\tusage(scalar_usage.buf);\n+}\n-- \ngitgitgadget\n\n"},{"id":"434651","messageId":"bcfde9bc7651266deaa0e5036cebd058dffc12a7.1630691688.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v2.git.1630691688.gitgitgadget@gmail.com","subject":"[PATCH v2 03/15] scalar: create test infrastructure","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-03T17:54:35Z","receivedAt":"2021-09-03T17:54:57Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nTo test the Scalar command, create a test script in contrib/scalar/t\nthat is executed as `make -C contrib/scalar test`. Since Scalar has no\nmeaningful capabilities yet, the only test is rather simple. We will add\nmore tests in subsequent commits that introduce corresponding, new\nfunctionality.\n\nNote: this test script is intended to test `scalar` only lightly, even\nafter all of the functionality is implemented.\n\nA more comprehensive functional (or: integration) test suite can be\nfound at https://github.com/microsoft/scalar; It is used in the workflow\nhttps://github.com/microsoft/git/blob/HEAD/.github/workflows/scalar-functional-tests.yml\nin Microsoft's Git fork. This test suite performs end-to-end tests with\na real remote repository, and is run as part of the regular CI builds.\nSince those tests require some functionality supported only by\nMicrosoft's Git fork (\"GVFS protocol\"), there is no intention to port\nthat fuller test suite to `contrib/scalar/`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/Makefile          | 17 +++++--\n contrib/scalar/t/Makefile        | 78 ++++++++++++++++++++++++++++++++\n contrib/scalar/t/t9099-scalar.sh | 17 +++++++\n 3 files changed, 109 insertions(+), 3 deletions(-)\n create mode 100644 contrib/scalar/t/Makefile\n create mode 100755 contrib/scalar/t/t9099-scalar.sh\n\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nindex 85c186634e9..8620042f281 100644\n--- a/contrib/scalar/Makefile\n+++ b/contrib/scalar/Makefile\n@@ -3,6 +3,7 @@ QUIET_SUBDIR1  =\n \n ifneq ($(findstring s,$(MAKEFLAGS)),s)\n ifndef V\n+\tQUIET_GEN      = @echo '   ' GEN $@;\n \tQUIET_SUBDIR0  = +@subdir=\n \tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n \t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n@@ -21,7 +22,7 @@ include ../../config.mak.uname\n TARGETS = scalar$(X) scalar.o\n GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n \n-all: scalar$X\n+all: scalar$X ../../bin-wrappers/scalar\n \n $(GITLIBS):\n \t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)\n@@ -30,9 +31,19 @@ $(TARGETS): $(GITLIBS) scalar.c\n \t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)\n \n clean:\n-\t$(RM) $(TARGETS)\n+\t$(RM) $(TARGETS) ../../bin-wrappers/scalar\n \t$(RM) scalar.1 scalar.html scalar.xml\n \n+../../bin-wrappers/scalar: ../../wrap-for-bin.sh Makefile\n+\t@mkdir -p ../../bin-wrappers\n+\t$(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \\\n+\t     -e 's|@@BUILD_DIR@@|$(shell cd ../.. && pwd)|' \\\n+\t     -e 's|@@PROG@@|contrib/scalar/scalar$(X)|' < $< > $@ && \\\n+\tchmod +x $@\n+\n+test: all\n+\t$(MAKE) -C t\n+\n docs: scalar.html scalar.1\n \n scalar.html: | scalar.1 # prevent them from trying to build `doc.dep` in parallel\n@@ -43,4 +54,4 @@ scalar.html scalar.1: scalar.txt\n \t\t../contrib/scalar/$@\n \t$(QUIET)test scalar.1 != \"$@\" || mv ../../Documentation/$@ .\n \n-.PHONY: all clean docs FORCE\n+.PHONY: all clean docs test FORCE\ndiff --git a/contrib/scalar/t/Makefile b/contrib/scalar/t/Makefile\nnew file mode 100644\nindex 00000000000..6170672bb37\n--- /dev/null\n+++ b/contrib/scalar/t/Makefile\n@@ -0,0 +1,78 @@\n+# Run scalar tests\n+#\n+# Copyright (c) 2005,2021 Junio C Hamano, Johannes Schindelin\n+#\n+\n+-include ../../../config.mak.autogen\n+-include ../../../config.mak\n+\n+SHELL_PATH ?= $(SHELL)\n+PERL_PATH ?= /usr/bin/perl\n+RM ?= rm -f\n+PROVE ?= prove\n+DEFAULT_TEST_TARGET ?= test\n+TEST_LINT ?= test-lint\n+\n+ifdef TEST_OUTPUT_DIRECTORY\n+TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results\n+else\n+TEST_RESULTS_DIRECTORY = ../../../t/test-results\n+endif\n+\n+# Shell quote;\n+SHELL_PATH_SQ = $(subst ','\\'',$(SHELL_PATH))\n+PERL_PATH_SQ = $(subst ','\\'',$(PERL_PATH))\n+TEST_RESULTS_DIRECTORY_SQ = $(subst ','\\'',$(TEST_RESULTS_DIRECTORY))\n+\n+T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))\n+\n+all: $(DEFAULT_TEST_TARGET)\n+\n+test: $(TEST_LINT)\n+\t$(MAKE) aggregate-results-and-cleanup\n+\n+prove: $(TEST_LINT)\n+\t@echo \"*** prove ***\"; GIT_CONFIG=.git/config $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)\n+\t$(MAKE) clean-except-prove-cache\n+\n+$(T):\n+\t@echo \"*** $@ ***\"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)\n+\n+clean-except-prove-cache:\n+\t$(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)'\n+\t$(RM) -r valgrind/bin\n+\n+clean: clean-except-prove-cache\n+\t$(RM) .prove\n+\n+test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax\n+\n+test-lint-duplicates:\n+\t@dups=`echo $(T) | tr ' ' '\\n' | sed 's/-.*//' | sort | uniq -d` && \\\n+\t\ttest -z \"$$dups\" || { \\\n+\t\techo >&2 \"duplicate test numbers:\" $$dups; exit 1; }\n+\n+test-lint-executable:\n+\t@bad=`for i in $(T); do test -x \"$$i\" || echo $$i; done` && \\\n+\t\ttest -z \"$$bad\" || { \\\n+\t\techo >&2 \"non-executable tests:\" $$bad; exit 1; }\n+\n+test-lint-shell-syntax:\n+\t@'$(PERL_PATH_SQ)' ../../../t/check-non-portable-shell.pl $(T)\n+\n+aggregate-results-and-cleanup: $(T)\n+\t$(MAKE) aggregate-results\n+\t$(MAKE) clean\n+\n+aggregate-results:\n+\tfor f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \\\n+\t\techo \"$$f\"; \\\n+\tdone | '$(SHELL_PATH_SQ)' ../../../t/aggregate-results.sh\n+\n+valgrind:\n+\t$(MAKE) GIT_TEST_OPTS=\"$(GIT_TEST_OPTS) --valgrind\"\n+\n+test-results:\n+\tmkdir -p test-results\n+\n+.PHONY: $(T) aggregate-results clean valgrind\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nnew file mode 100755\nindex 00000000000..16f2b72b126\n--- /dev/null\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -0,0 +1,17 @@\n+#!/bin/sh\n+\n+test_description='test the `scalar` command'\n+\n+TEST_DIRECTORY=$PWD/../../../t\n+export TEST_DIRECTORY\n+\n+# Make it work with --no-bin-wrappers\n+PATH=$PWD/..:$PATH\n+\n+. ../../../t/test-lib.sh\n+\n+test_expect_success 'scalar shows a usage' '\n+\ttest_expect_code 129 scalar -h\n+'\n+\n+test_done\n-- \ngitgitgadget\n\n"},{"id":"434652","messageId":"4f886575dcfb97d5bbd04fe7551e03935ac0af69.1630691688.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v2.git.1630691688.gitgitgadget@gmail.com","subject":"[PATCH v2 02/15] scalar: start documenting the command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-03T17:54:34Z","receivedAt":"2021-09-03T17:54:58Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis commit establishes the infrastructure to build the manual page for\nthe `scalar` command.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/.gitignore |  3 +++\n contrib/scalar/Makefile   | 14 +++++++++++++-\n contrib/scalar/scalar.txt | 38 ++++++++++++++++++++++++++++++++++++++\n 3 files changed, 54 insertions(+), 1 deletion(-)\n create mode 100644 contrib/scalar/scalar.txt\n\ndiff --git a/contrib/scalar/.gitignore b/contrib/scalar/.gitignore\nindex ff3d47e84d0..00441073f59 100644\n--- a/contrib/scalar/.gitignore\n+++ b/contrib/scalar/.gitignore\n@@ -1,2 +1,5 @@\n+/*.xml\n+/*.1\n+/*.html\n /*.exe\n /scalar\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nindex 40c03ad10e1..85c186634e9 100644\n--- a/contrib/scalar/Makefile\n+++ b/contrib/scalar/Makefile\n@@ -6,6 +6,7 @@ ifndef V\n \tQUIET_SUBDIR0  = +@subdir=\n \tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n \t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n+\tQUIET          = @\n else\n \texport V\n endif\n@@ -30,5 +31,16 @@ $(TARGETS): $(GITLIBS) scalar.c\n \n clean:\n \t$(RM) $(TARGETS)\n+\t$(RM) scalar.1 scalar.html scalar.xml\n \n-.PHONY: all clean FORCE\n+docs: scalar.html scalar.1\n+\n+scalar.html: | scalar.1 # prevent them from trying to build `doc.dep` in parallel\n+\n+scalar.html scalar.1: scalar.txt\n+\t$(QUIET_SUBDIR0)../../Documentation$(QUIET_SUBDIR1) \\\n+\t\tMAN_TXT=../contrib/scalar/scalar.txt \\\n+\t\t../contrib/scalar/$@\n+\t$(QUIET)test scalar.1 != \"$@\" || mv ../../Documentation/$@ .\n+\n+.PHONY: all clean docs FORCE\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nnew file mode 100644\nindex 00000000000..5f7131861a5\n--- /dev/null\n+++ b/contrib/scalar/scalar.txt\n@@ -0,0 +1,38 @@\n+scalar(1)\n+=========\n+\n+NAME\n+----\n+scalar - an opinionated repository management tool\n+\n+SYNOPSIS\n+--------\n+[verse]\n+scalar <command> [<options>]\n+\n+DESCRIPTION\n+-----------\n+\n+Scalar is an opinionated repository management tool. By creating new\n+repositories or registering existing repositories with Scalar, your Git\n+experience will speed up. Scalar sets advanced Git config settings,\n+maintains your repositories in the background, and helps reduce data sent\n+across the network.\n+\n+An important Scalar concept is the enlistment: this is the top-level directory\n+of the project. It usually contains the subdirectory `src/` which is a Git\n+worktree. This encourages the separation between tracked files (inside `src/`)\n+and untracked files, such as build artifacts (outside `src/`). When registering\n+an existing Git worktree with Scalar whose name is not `src`, the enlistment\n+will be identical to the worktree.\n+\n+The `scalar` command implements various subcommands, and different options\n+depending on the subcommand.\n+\n+SEE ALSO\n+--------\n+linkgit:git-maintenance[1].\n+\n+Scalar\n+---\n+Associated with the linkgit:git[1] suite\n-- \ngitgitgadget\n\n"},{"id":"434653","messageId":"ee3e26a0c4e8fd9ecad1ff71f86423c2685adec4.1630691688.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v2.git.1630691688.gitgitgadget@gmail.com","subject":"[PATCH v2 04/15] scalar: 'register' sets recommended config and starts maintenance","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-03T17:54:36Z","receivedAt":"2021-09-03T17:54:59Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nLet's start implementing the `register` command. With this commit,\nrecommended settings are configured upon `scalar register`, and Git's\nbackground maintenance is started.\n\nThe recommended config settings may very well change in the future. For\nexample, once the built-in FSMonitor is available, we will want to\nenable it upon `scalar register`. For that reason, we explicitly support\nrunning `scalar register` in an already-registered enlistment.\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 255 ++++++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt |  18 ++-\n 2 files changed, 272 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 7cff29e0fcd..0e627bb100e 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -5,11 +5,266 @@\n #include \"cache.h\"\n #include \"gettext.h\"\n #include \"parse-options.h\"\n+#include \"config.h\"\n+#include \"run-command.h\"\n+\n+/*\n+ * Remove the deepest subdirectory in the provided path string. Path must not\n+ * include a trailing path separator. Returns 1 if parent directory found,\n+ * otherwise 0.\n+ */\n+static int strbuf_parent_directory(struct strbuf *buf)\n+{\n+\tsize_t len = buf->len;\n+\tsize_t offset = offset_1st_component(buf->buf);\n+\tchar *path_sep = find_last_dir_sep(buf->buf + offset);\n+\tstrbuf_setlen(buf, path_sep ? path_sep - buf->buf : offset);\n+\n+\treturn buf->len < len;\n+}\n+\n+static void setup_enlistment_directory(int argc, const char **argv,\n+\t\t\t\t       const char * const *usagestr,\n+\t\t\t\t       const struct option *options,\n+\t\t\t\t       struct strbuf *enlistment_root)\n+{\n+\tstruct strbuf path = STRBUF_INIT;\n+\tchar *root;\n+\tint enlistment_found = 0;\n+\n+\tif (startup_info->have_repository)\n+\t\tBUG(\"gitdir already set up?!?\");\n+\n+\tif (argc > 1)\n+\t\tusage_with_options(usagestr, options);\n+\n+\t/* find the worktree, determine its corresponding root */\n+\tif (argc == 1)\n+\t\tstrbuf_add_absolute_path(&path, argv[0]);\n+\telse if (strbuf_getcwd(&path) < 0)\n+\t\tdie(_(\"need a working directory\"));\n+\n+\tstrbuf_trim_trailing_dir_sep(&path);\n+\tdo {\n+\t\tconst size_t len = path.len;\n+\n+\t\t/* check if currently in enlistment root with src/ workdir */\n+\t\tstrbuf_addstr(&path, \"/src/.git\");\n+\t\tif (is_git_directory(path.buf)) {\n+\t\t\tstrbuf_strip_suffix(&path, \"/.git\");\n+\n+\t\t\tif (enlistment_root)\n+\t\t\t\tstrbuf_add(enlistment_root, path.buf, len);\n+\n+\t\t\tenlistment_found = 1;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\t/* reset to original path */\n+\t\tstrbuf_setlen(&path, len);\n+\n+\t\t/* check if currently in workdir */\n+\t\tstrbuf_addstr(&path, \"/.git\");\n+\t\tif (is_git_directory(path.buf)) {\n+\t\t\tstrbuf_setlen(&path, len);\n+\n+\t\t\tif (enlistment_root) {\n+\t\t\t\t/*\n+\t\t\t\t * If the worktree's directory's name is `src`, the enlistment is the\n+\t\t\t\t * parent directory, otherwise it is identical to the worktree.\n+\t\t\t\t */\n+\t\t\t\troot = strip_path_suffix(path.buf, \"src\");\n+\t\t\t\tstrbuf_addstr(enlistment_root, root ? root : path.buf);\n+\t\t\t\tfree(root);\n+\t\t\t}\n+\n+\t\t\tenlistment_found = 1;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tstrbuf_setlen(&path, len);\n+\t} while (strbuf_parent_directory(&path));\n+\n+\tif (!enlistment_found)\n+\t\tdie(_(\"could not find enlistment root\"));\n+\n+\tif (chdir(path.buf) < 0)\n+\t\tdie_errno(_(\"could not switch to '%s'\"), path.buf);\n+\n+\tstrbuf_release(&path);\n+\tsetup_git_directory();\n+}\n+\n+static int run_git(const char *arg, ...)\n+{\n+\tstruct strvec argv = STRVEC_INIT;\n+\tva_list args;\n+\tconst char *p;\n+\tint res;\n+\n+\tva_start(args, arg);\n+\tstrvec_push(&argv, arg);\n+\twhile ((p = va_arg(args, const char *)))\n+\t\tstrvec_push(&argv, p);\n+\tva_end(args);\n+\n+\tres = run_command_v_opt(argv.v, RUN_GIT_CMD);\n+\n+\tstrvec_clear(&argv);\n+\treturn res;\n+}\n+\n+static int set_recommended_config(void)\n+{\n+\tstruct {\n+\t\tconst char *key;\n+\t\tconst char *value;\n+\t} config[] = {\n+\t\t{ \"am.keepCR\", \"true\" },\n+\t\t{ \"core.FSCache\", \"true\" },\n+\t\t{ \"core.multiPackIndex\", \"true\" },\n+\t\t{ \"core.preloadIndex\", \"true\" },\n+#ifndef WIN32\n+\t\t{ \"core.untrackedCache\", \"true\" },\n+#else\n+\t\t/*\n+\t\t * Unfortunately, Scalar's Functional Tests demonstrated\n+\t\t * that the untracked cache feature is unreliable on Windows\n+\t\t * (which is a bummer because that platform would benefit the\n+\t\t * most from it). For some reason, freshly created files seem\n+\t\t * not to update the directory's `lastModified` time\n+\t\t * immediately, but the untracked cache would need to rely on\n+\t\t * that.\n+\t\t *\n+\t\t * Therefore, with a sad heart, we disable this very useful\n+\t\t * feature on Windows.\n+\t\t */\n+\t\t{ \"core.untrackedCache\", \"false\" },\n+#endif\n+\t\t{ \"core.logAllRefUpdates\", \"true\" },\n+\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n+\t\t{ \"credential.validate\", \"false\" }, /* GCM4W-only */\n+\t\t{ \"gc.auto\", \"0\" },\n+\t\t{ \"gui.GCWarning\", \"false\" },\n+\t\t{ \"index.threads\", \"true\" },\n+\t\t{ \"index.version\", \"4\" },\n+\t\t{ \"merge.stat\", \"false\" },\n+\t\t{ \"merge.renames\", \"false\" },\n+\t\t{ \"pack.useBitmaps\", \"false\" },\n+\t\t{ \"pack.useSparse\", \"true\" },\n+\t\t{ \"receive.autoGC\", \"false\" },\n+\t\t{ \"reset.quiet\", \"true\" },\n+\t\t{ \"feature.manyFiles\", \"false\" },\n+\t\t{ \"feature.experimental\", \"false\" },\n+\t\t{ \"fetch.unpackLimit\", \"1\" },\n+\t\t{ \"fetch.writeCommitGraph\", \"false\" },\n+#ifdef WIN32\n+\t\t{ \"http.sslBackend\", \"schannel\" },\n+#endif\n+\t\t{ \"status.aheadBehind\", \"false\" },\n+\t\t{ \"commitGraph.generationVersion\", \"1\" },\n+\t\t{ \"core.autoCRLF\", \"false\" },\n+\t\t{ \"core.safeCRLF\", \"false\" },\n+\t\t{ NULL, NULL },\n+\t};\n+\tint i;\n+\tchar *value;\n+\n+\tfor (i = 0; config[i].key; i++) {\n+\t\tif (git_config_get_string(config[i].key, &value)) {\n+\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"created\");\n+\t\t\tif (git_config_set_gently(config[i].key,\n+\t\t\t\t\t\t  config[i].value) < 0)\n+\t\t\t\treturn error(_(\"could not configure %s=%s\"),\n+\t\t\t\t\t     config[i].key, config[i].value);\n+\t\t} else {\n+\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"exists\");\n+\t\t\tfree(value);\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * The `log.excludeDecoration` setting is special because it allows\n+\t * for multiple values.\n+\t */\n+\tif (git_config_get_string(\"log.excludeDecoration\", &value)) {\n+\t\ttrace2_data_string(\"scalar\", the_repository,\n+\t\t\t\t   \"log.excludeDecoration\", \"created\");\n+\t\tif (git_config_set_multivar_gently(\"log.excludeDecoration\",\n+\t\t\t\t\t\t   \"refs/prefetch/*\",\n+\t\t\t\t\t\t   CONFIG_REGEX_NONE, 0))\n+\t\t\treturn error(_(\"could not configure \"\n+\t\t\t\t       \"log.excludeDecoration\"));\n+\t} else {\n+\t\ttrace2_data_string(\"scalar\", the_repository,\n+\t\t\t\t   \"log.excludeDecoration\", \"exists\");\n+\t\tfree(value);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int start_maintenance(void)\n+{\n+\treturn run_git(\"maintenance\", \"start\", NULL);\n+}\n+\n+static int add_enlistment(void)\n+{\n+\tint res;\n+\n+\tif (!the_repository->worktree)\n+\t\tdie(_(\"Scalar enlistments require a worktree\"));\n+\n+\tres = run_git(\"config\", \"--global\", \"--get\", \"--fixed-value\",\n+\t\t      \"scalar.repo\", the_repository->worktree, NULL);\n+\n+\t/*\n+\t * If the setting is already there, then do nothing.\n+\t */\n+\tif (!res)\n+\t\treturn 0;\n+\n+\treturn run_git(\"config\", \"--global\", \"--add\",\n+\t\t       \"scalar.repo\", the_repository->worktree, NULL);\n+}\n+\n+static int register_dir(void)\n+{\n+\tint res = add_enlistment();\n+\n+\tif (!res)\n+\t\tres = set_recommended_config();\n+\n+\tif (!res)\n+\t\tres = start_maintenance();\n+\n+\treturn res;\n+}\n+\n+static int cmd_register(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar register [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn register_dir();\n+}\n \n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"register\", cmd_register },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 5f7131861a5..568987064b2 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,7 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n-scalar <command> [<options>]\n+scalar register [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -29,6 +29,22 @@ will be identical to the worktree.\n The `scalar` command implements various subcommands, and different options\n depending on the subcommand.\n \n+COMMANDS\n+--------\n+\n+Register\n+~~~~~~~~\n+\n+register [<enlistment>]::\n+\tAdds the enlistment's repository to the list of registered repositories\n+\tand starts background maintenance. If `<enlistment>` is not provided,\n+\tthen the enlistment associated with the current working directory is\n+\tregistered.\n++\n+Note: when this subcommand is called in a worktree that is called `src/`, its\n+parent directory is considered to be the Scalar enlistment. If the worktree is\n+_not_ called `src/`, it itself will be considered to be the Scalar enlistment.\n+\n SEE ALSO\n --------\n linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"434654","messageId":"6142f75875b8245c93230e141be3664253e2137b.1630691688.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v2.git.1630691688.gitgitgadget@gmail.com","subject":"[PATCH v2 05/15] scalar: 'unregister' stops background maintenance","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-03T17:54:37Z","receivedAt":"2021-09-03T17:55:01Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nJust like `scalar register` starts the scheduled background maintenance,\n`scalar unregister` stops it. Note that we use `git maintenance start`\nin `scalar register`, but we do not use `git maintenance stop` in\n`scalar unregister`: this would stop maintenance for _all_ repositories,\nnot just for the one we want to unregister.\n\nThe `unregister` command also removes the corresponding entry from the\n`[scalar]` section in the global Git config.\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 50 ++++++++++++++++++++++++++++++++-------\n contrib/scalar/scalar.txt |  8 +++++++\n 2 files changed, 50 insertions(+), 8 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 0e627bb100e..2b5c52a25f5 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -204,12 +204,12 @@ static int set_recommended_config(void)\n \treturn 0;\n }\n \n-static int start_maintenance(void)\n+static int toggle_maintenance(int enable)\n {\n-\treturn run_git(\"maintenance\", \"start\", NULL);\n+\treturn run_git(\"maintenance\", enable ? \"start\" : \"unregister\", NULL);\n }\n \n-static int add_enlistment(void)\n+static int add_or_remove_enlistment(int add)\n {\n \tint res;\n \n@@ -220,24 +220,39 @@ static int add_enlistment(void)\n \t\t      \"scalar.repo\", the_repository->worktree, NULL);\n \n \t/*\n-\t * If the setting is already there, then do nothing.\n+\t * If we want to add and the setting is already there, then do nothing.\n+\t * If we want to remove and the setting is not there, then do nothing.\n \t */\n-\tif (!res)\n+\tif ((add && !res) || (!add && res))\n \t\treturn 0;\n \n-\treturn run_git(\"config\", \"--global\", \"--add\",\n+\treturn run_git(\"config\", \"--global\", add ? \"--add\" : \"--unset\",\n+\t\t       add ? \"--no-fixed-value\" : \"--fixed-value\",\n \t\t       \"scalar.repo\", the_repository->worktree, NULL);\n }\n \n static int register_dir(void)\n {\n-\tint res = add_enlistment();\n+\tint res = add_or_remove_enlistment(1);\n \n \tif (!res)\n \t\tres = set_recommended_config();\n \n \tif (!res)\n-\t\tres = start_maintenance();\n+\t\tres = toggle_maintenance(1);\n+\n+\treturn res;\n+}\n+\n+static int unregister_dir(void)\n+{\n+\tint res = 0;\n+\n+\tif (toggle_maintenance(0) < 0)\n+\t\tres = -1;\n+\n+\tif (add_or_remove_enlistment(0) < 0)\n+\t\tres = -1;\n \n \treturn res;\n }\n@@ -260,11 +275,30 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_unregister(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar unregister [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn unregister_dir();\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n \t{ \"register\", cmd_register },\n+\t{ \"unregister\", cmd_unregister },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 568987064b2..d9a79984492 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -9,6 +9,7 @@ SYNOPSIS\n --------\n [verse]\n scalar register [<enlistment>]\n+scalar unregister [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -45,6 +46,13 @@ Note: when this subcommand is called in a worktree that is called `src/`, its\n parent directory is considered to be the Scalar enlistment. If the worktree is\n _not_ called `src/`, it itself will be considered to be the Scalar enlistment.\n \n+Unregister\n+~~~~~~~~~~\n+\n+unregister [<enlistment>]::\n+\tRemove the specified repository from the list of repositories\n+\tregistered with Scalar and stop the scheduled background maintenance.\n+\n SEE ALSO\n --------\n linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"434655","messageId":"82dd253154fb8dabd6f344df2196d8e6261d09a5.1630691688.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v2.git.1630691688.gitgitgadget@gmail.com","subject":"[PATCH v2 06/15] scalar: let 'unregister' handle a deleted enlistment directory gracefully","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-03T17:54:38Z","receivedAt":"2021-09-03T17:55:02Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nWhen a user deleted an enlistment manually, let's be generous and\n_still_ unregister it.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 46 ++++++++++++++++++++++++++++++++\n contrib/scalar/t/t9099-scalar.sh | 15 +++++++++++\n 2 files changed, 61 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 2b5c52a25f5..d114c038b64 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -275,6 +275,24 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int remove_deleted_enlistment(struct strbuf *path)\n+{\n+\tint res = 0;\n+\tstrbuf_realpath_forgiving(path, path->buf, 1);\n+\n+\tif (run_git(\"config\", \"--global\",\n+\t\t    \"--unset\", \"--fixed-value\",\n+\t\t    \"scalar.repo\", path->buf, NULL) < 0)\n+\t\tres = -1;\n+\n+\tif (run_git(\"config\", \"--global\",\n+\t\t    \"--unset\", \"--fixed-value\",\n+\t\t    \"maintenance.repo\", path->buf, NULL) < 0)\n+\t\tres = -1;\n+\n+\treturn res;\n+}\n+\n static int cmd_unregister(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -288,6 +306,34 @@ static int cmd_unregister(int argc, const char **argv)\n \targc = parse_options(argc, argv, NULL, options,\n \t\t\t     usage, 0);\n \n+\t/*\n+\t * Be forgiving when the enlistment or worktree does not even exist any\n+\t * longer; This can be the case if a user deleted the worktree by\n+\t * mistake and _still_ wants to unregister the thing.\n+\t */\n+\tif (argc == 1) {\n+\t\tstruct strbuf src_path = STRBUF_INIT, workdir_path = STRBUF_INIT;\n+\n+\t\tstrbuf_addf(&src_path, \"%s/src/.git\", argv[0]);\n+\t\tstrbuf_addf(&workdir_path, \"%s/.git\", argv[0]);\n+\t\tif (!is_directory(src_path.buf) && !is_directory(workdir_path.buf)) {\n+\t\t\t/* remove possible matching registrations */\n+\t\t\tint res = -1;\n+\n+\t\t\tstrbuf_strip_suffix(&src_path, \"/.git\");\n+\t\t\tres = remove_deleted_enlistment(&src_path) && res;\n+\n+\t\t\tstrbuf_strip_suffix(&workdir_path, \"/.git\");\n+\t\t\tres = remove_deleted_enlistment(&workdir_path) && res;\n+\n+\t\t\tstrbuf_release(&src_path);\n+\t\t\tstrbuf_release(&workdir_path);\n+\t\t\treturn res;\n+\t\t}\n+\t\tstrbuf_release(&src_path);\n+\t\tstrbuf_release(&workdir_path);\n+\t}\n+\n \tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n \n \treturn unregister_dir();\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 16f2b72b126..ef0e8d680d5 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -14,4 +14,19 @@ test_expect_success 'scalar shows a usage' '\n \ttest_expect_code 129 scalar -h\n '\n \n+test_expect_success 'scalar unregister' '\n+\tgit init vanish/src &&\n+\tscalar register vanish/src &&\n+\tgit config --get --global --fixed-value \\\n+\t\tmaintenance.repo \"$(pwd)/vanish/src\" &&\n+\tscalar list >scalar.repos &&\n+\tgrep -F \"$(pwd)/vanish/src\" scalar.repos &&\n+\trm -rf vanish/src/.git &&\n+\tscalar unregister vanish &&\n+\ttest_must_fail git config --get --global --fixed-value \\\n+\t\tmaintenance.repo \"$(pwd)/vanish/src\" &&\n+\tscalar list >scalar.repos &&\n+\t! grep -F \"$(pwd)/vanish/src\" scalar.repos\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"434656","messageId":"fb7c931ddb3a248542bc6a03d09189e937315d0c.1630691688.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v2.git.1630691688.gitgitgadget@gmail.com","subject":"[PATCH v2 07/15] scalar: implement 'scalar list'","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-03T17:54:39Z","receivedAt":"2021-09-03T17:55:07Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe produced list simply consists of those repositories registered under\nthe multi-valued `scalar.repo` config setting in the user's Git config.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 11 +++++++++++\n contrib/scalar/scalar.txt | 12 +++++++++++-\n 2 files changed, 22 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex d114c038b64..7f5436399da 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -257,6 +257,16 @@ static int unregister_dir(void)\n \treturn res;\n }\n \n+static int cmd_list(int argc, const char **argv)\n+{\n+\tif (argc != 1)\n+\t\tdie(_(\"`scalar list` does not take arguments\"));\n+\n+\tif (run_git(\"config\", \"--global\", \"--get-all\", \"scalar.repo\", NULL) < 0)\n+\t\treturn -1;\n+\treturn 0;\n+}\n+\n static int cmd_register(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -343,6 +353,7 @@ static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n \t{ NULL, NULL},\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex d9a79984492..f2528557a0c 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,6 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n+scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n \n@@ -28,11 +29,20 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand.\n+depending on the subcommand. With the exception of `list`, all subcommands\n+expect to be run in an enlistment.\n \n COMMANDS\n --------\n \n+List\n+~~~~\n+\n+list::\n+\tTo see which repositories are currently registered by the service, run\n+\t`scalar list`. This subcommand does not need to be run inside a Scalar\n+\tenlistment.\n+\n Register\n ~~~~~~~~\n \n-- \ngitgitgadget\n\n"},{"id":"434657","messageId":"f3223c10788fe836d0322006dc2dcbafe08f6b5b.1630691688.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v2.git.1630691688.gitgitgadget@gmail.com","subject":"[PATCH v2 08/15] scalar: implement the `clone` subcommand","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-03T17:54:40Z","receivedAt":"2021-09-03T17:55:08Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis implements Scalar's opinionated `clone` command: it tries to use a\npartial clone and sets up a sparse checkout by default. In contrast to\n`git clone`, `scalar clone` sets up the worktree in the `src/`\nsubdirectory, to encourage a separation between the source files and the\nbuild output (which helps Git tremendously because it avoids untracked\nfiles that have to be specifically ignored when refreshing the index).\n\nAlso, it registers the repository for regular, scheduled maintenance,\nand configures a flurry of configuration settings based on the\nexperience and experiments of the Microsoft Windows and the Microsoft\nOffice development teams.\n\nNote: since the `scalar clone` command is by far the most commonly\ncalled `scalar` subcommand, we document it at the top of the manual\npage.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 201 +++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        |  35 +++++-\n contrib/scalar/t/t9099-scalar.sh |  32 +++++\n 3 files changed, 263 insertions(+), 5 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 7f5436399da..bf18003b297 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -7,6 +7,7 @@\n #include \"parse-options.h\"\n #include \"config.h\"\n #include \"run-command.h\"\n+#include \"refs.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -257,6 +258,205 @@ static int unregister_dir(void)\n \treturn res;\n }\n \n+/* printf-style interface, expects `<key>=<value>` argument */\n+static int set_config(const char *fmt, ...)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tchar *value;\n+\tint res;\n+\tva_list args;\n+\n+\tva_start(args, fmt);\n+\tstrbuf_vaddf(&buf, fmt, args);\n+\tva_end(args);\n+\n+\tvalue = strchr(buf.buf, '=');\n+\tif (value)\n+\t\t*(value++) = '\\0';\n+\tres = git_config_set_gently(buf.buf, value);\n+\tstrbuf_release(&buf);\n+\n+\treturn res;\n+}\n+\n+static char *remote_default_branch(const char *url)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tstruct strbuf out = STRBUF_INIT;\n+\n+\tcp.git_cmd = 1;\n+\tstrvec_pushl(&cp.args, \"ls-remote\", \"--symref\", url, \"HEAD\", NULL);\n+\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n+\t\tconst char *line = out.buf;\n+\n+\t\twhile (*line) {\n+\t\t\tconst char *eol = strchrnul(line, '\\n'), *p;\n+\t\t\tsize_t len = eol - line;\n+\t\t\tchar *branch;\n+\n+\t\t\tif (!skip_prefix(line, \"ref: \", &p) ||\n+\t\t\t    !strip_suffix_mem(line, &len, \"\\tHEAD\")) {\n+\t\t\t\tline = eol + (*eol == '\\n');\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\teol = line + len;\n+\t\t\tif (skip_prefix(p, \"refs/heads/\", &p)) {\n+\t\t\t\tbranch = xstrndup(p, eol - p);\n+\t\t\t\tstrbuf_release(&out);\n+\t\t\t\treturn branch;\n+\t\t\t}\n+\n+\t\t\terror(_(\"remote HEAD is not a branch: '%.*s'\"),\n+\t\t\t      (int)(eol - p), p);\n+\t\t\tstrbuf_release(&out);\n+\t\t\treturn NULL;\n+\t\t}\n+\t}\n+\twarning(_(\"failed to get default branch name from remote; \"\n+\t\t  \"using local default\"));\n+\tstrbuf_reset(&out);\n+\n+\tchild_process_init(&cp);\n+\tcp.git_cmd = 1;\n+\tstrvec_pushl(&cp.args, \"symbolic-ref\", \"--short\", \"HEAD\", NULL);\n+\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n+\t\tstrbuf_trim(&out);\n+\t\treturn strbuf_detach(&out, NULL);\n+\t}\n+\n+\tstrbuf_release(&out);\n+\terror(_(\"failed to get default branch name\"));\n+\treturn NULL;\n+}\n+\n+static int cmd_clone(int argc, const char **argv)\n+{\n+\tconst char *branch = NULL;\n+\tint full_clone = 0;\n+\tstruct option clone_options[] = {\n+\t\tOPT_STRING('b', \"branch\", &branch, N_(\"<branch>\"),\n+\t\t\t   N_(\"branch to checkout after clone\")),\n+\t\tOPT_BOOL(0, \"full-clone\", &full_clone,\n+\t\t\t N_(\"when cloning, create full working directory\")),\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const clone_usage[] = {\n+\t\tN_(\"scalar clone [<options>] [--] <repo> [<dir>]\"),\n+\t\tNULL\n+\t};\n+\tconst char *url;\n+\tchar *enlistment = NULL, *dir = NULL;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint res;\n+\n+\targc = parse_options(argc, argv, NULL, clone_options, clone_usage, 0);\n+\n+\tif (argc == 2) {\n+\t\turl = argv[0];\n+\t\tenlistment = xstrdup(argv[1]);\n+\t} else if (argc == 1) {\n+\t\turl = argv[0];\n+\n+\t\tstrbuf_addstr(&buf, url);\n+\t\t/* Strip trailing slashes, if any */\n+\t\twhile (buf.len > 0 && is_dir_sep(buf.buf[buf.len - 1]))\n+\t\t\tstrbuf_setlen(&buf, buf.len - 1);\n+\t\t/* Strip suffix `.git`, if any */\n+\t\tstrbuf_strip_suffix(&buf, \".git\");\n+\n+\t\tenlistment = find_last_dir_sep(buf.buf);\n+\t\tif (!enlistment) {\n+\t\t\tdie(_(\"cannot deduce worktree name from '%s'\"), url);\n+\t\t}\n+\t\tenlistment = xstrdup(enlistment + 1);\n+\t} else {\n+\t\tusage_msg_opt(_(\"You must specify a repository to clone.\"),\n+\t\t\t      clone_usage, clone_options);\n+\t}\n+\n+\tif (is_directory(enlistment))\n+\t\tdie(_(\"directory '%s' exists already\"), enlistment);\n+\n+\tdir = xstrfmt(\"%s/src\", enlistment);\n+\n+\tstrbuf_reset(&buf);\n+\tif (branch)\n+\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", branch);\n+\telse {\n+\t\tchar *b = repo_default_branch_name(the_repository, 1);\n+\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", b);\n+\t\tfree(b);\n+\t}\n+\n+\tif ((res = run_git(\"-c\", buf.buf, \"init\", \"--\", dir, NULL)))\n+\t\tgoto cleanup;\n+\n+\tif (chdir(dir) < 0) {\n+\t\tres = error_errno(_(\"could not switch to '%s'\"), dir);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tsetup_git_directory();\n+\n+\t/* common-main already logs `argv` */\n+\ttrace2_def_repo(the_repository);\n+\n+\tif (!branch && !(branch = remote_default_branch(url))) {\n+\t\tres = error(_(\"failed to get default branch for '%s'\"), url);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tif (set_config(\"remote.origin.url=%s\", url) ||\n+\t    set_config(\"remote.origin.fetch=\"\n+\t\t       \"+refs/heads/*:refs/remotes/origin/*\") ||\n+\t    set_config(\"remote.origin.promisor=true\") ||\n+\t    set_config(\"remote.origin.partialCloneFilter=blob:none\")) {\n+\t\tres = error(_(\"could not configure remote in '%s'\"), dir);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tif (!full_clone &&\n+\t    (res = run_git(\"sparse-checkout\", \"init\", \"--cone\", NULL)))\n+\t\tgoto cleanup;\n+\n+\tif (set_recommended_config())\n+\t\treturn error(_(\"could not configure '%s'\"), dir);\n+\n+\tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL))) {\n+\t\twarning(_(\"partial clone failed; attempting full clone\"));\n+\n+\t\tif (set_config(\"remote.origin.promisor\") ||\n+\t\t    set_config(\"remote.origin.partialCloneFilter\")) {\n+\t\t\tres = error(_(\"could not configure for full clone\"));\n+\t\t\tgoto cleanup;\n+\t\t}\n+\n+\t\tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL)))\n+\t\t\tgoto cleanup;\n+\t}\n+\n+\tif ((res = set_config(\"branch.%s.remote=origin\", branch)))\n+\t\tgoto cleanup;\n+\tif ((res = set_config(\"branch.%s.merge=refs/heads/%s\",\n+\t\t\t      branch, branch)))\n+\t\tgoto cleanup;\n+\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addf(&buf, \"origin/%s\", branch);\n+\tres = run_git(\"checkout\", \"-f\", \"-t\", buf.buf, NULL);\n+\tif (res)\n+\t\tgoto cleanup;\n+\n+\tres = register_dir();\n+\n+cleanup:\n+\tfree(enlistment);\n+\tfree(dir);\n+\tstrbuf_release(&buf);\n+\treturn res;\n+}\n+\n static int cmd_list(int argc, const char **argv)\n {\n \tif (argc != 1)\n@@ -353,6 +553,7 @@ static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"clone\", cmd_clone },\n \t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex f2528557a0c..d4e3cb73fda 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,6 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n+scalar clone [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n@@ -29,19 +30,43 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand. With the exception of `list`, all subcommands\n-expect to be run in an enlistment.\n+depending on the subcommand. With the exception of `clone` and `list`, all\n+subcommands expect to be run in an enlistment.\n \n COMMANDS\n --------\n \n+Clone\n+~~~~~\n+\n+clone [<options>] <url> [<enlistment>]::\n+\tClones the specified repository, similar to linkgit:git-clone[1]. By\n+\tdefault, only commit and tree objects are cloned. Once finished, the\n+\tworktree is located at `<enlistment>/src`.\n++\n+The sparse-checkout feature is enabled (except when run with `--full-clone`)\n+and the only files present are those in the top-level directory. Use\n+`git sparse-checkout set` to expand the set of directories you want to see,\n+or `git sparse-checkout disable` to expand to all files (see\n+linkgit:git-sparse-checkout[1] for more details). You can explore the\n+subdirectories outside your sparse-checkout by using `git ls-tree HEAD`.\n+\n+-b <name>::\n+--branch <name>::\n+\tInstead of checking out the branch pointed to by the cloned\n+\trepository's HEAD, check out the `<name>` branch instead.\n+\n+--[no-]full-clone::\n+\tA sparse-checkout is initialized by default. This behavior can be\n+\tturned off via `--full-clone`.\n+\n List\n ~~~~\n \n list::\n \tTo see which repositories are currently registered by the service, run\n-\t`scalar list`. This subcommand does not need to be run inside a Scalar\n-\tenlistment.\n+\t`scalar list`. This subcommand, like `clone`, does not need to be run\n+\tinside a Scalar enlistment.\n \n Register\n ~~~~~~~~\n@@ -65,7 +90,7 @@ unregister [<enlistment>]::\n \n SEE ALSO\n --------\n-linkgit:git-maintenance[1].\n+linkgit:git-clone[1], linkgit:git-maintenance[1].\n \n Scalar\n ---\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex ef0e8d680d5..295398f62cc 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -10,6 +10,9 @@ PATH=$PWD/..:$PATH\n \n . ../../../t/test-lib.sh\n \n+GIT_TEST_MAINT_SCHEDULER=\"crontab:test-tool crontab ../cron.txt\"\n+export GIT_TEST_MAINT_SCHEDULER\n+\n test_expect_success 'scalar shows a usage' '\n \ttest_expect_code 129 scalar -h\n '\n@@ -29,4 +32,33 @@ test_expect_success 'scalar unregister' '\n \t! grep -F \"$(pwd)/vanish/src\" scalar.repos\n '\n \n+test_expect_success 'set up repository to clone' '\n+\ttest_commit first &&\n+\ttest_commit second &&\n+\ttest_commit third &&\n+\tgit switch -c parallel first &&\n+\tmkdir -p 1/2 &&\n+\ttest_commit 1/2/3 &&\n+\tgit config uploadPack.allowFilter true &&\n+\tgit config uploadPack.allowAnySHA1InWant true\n+'\n+\n+test_expect_success 'scalar clone' '\n+\tsecond=$(git rev-parse --verify second:second.t) &&\n+\tscalar clone \"file://$(pwd)\" cloned &&\n+\t(\n+\t\tcd cloned/src &&\n+\n+\t\tgit config --get --global --fixed-value maintenance.repo \\\n+\t\t\t\"$(pwd)\" &&\n+\n+\t\ttest_path_is_missing 1/2 &&\n+\t\ttest_must_fail git rev-list --missing=print $second &&\n+\t\tgit rev-list $second &&\n+\t\tgit cat-file blob $second >actual &&\n+\t\techo \"second\" >expect &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"434658","messageId":"b3c4b3dccc6d943990d5256944284ac3fbc39fec.1630691688.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v2.git.1630691688.gitgitgadget@gmail.com","subject":"[PATCH v2 09/15] scalar: teach 'clone' to support the --single-branch option","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-03T17:54:41Z","receivedAt":"2021-09-03T17:55:09Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nJust like `git clone`, the `scalar clone` command now also offers to\nrestrict the clone to a single branch.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          |  9 +++++++--\n contrib/scalar/scalar.txt        | 12 +++++++++++-\n contrib/scalar/t/t9099-scalar.sh |  6 +++++-\n 3 files changed, 23 insertions(+), 4 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex bf18003b297..7dd1f28948f 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -333,12 +333,15 @@ static char *remote_default_branch(const char *url)\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n-\tint full_clone = 0;\n+\tint full_clone = 0, single_branch = 0;\n \tstruct option clone_options[] = {\n \t\tOPT_STRING('b', \"branch\", &branch, N_(\"<branch>\"),\n \t\t\t   N_(\"branch to checkout after clone\")),\n \t\tOPT_BOOL(0, \"full-clone\", &full_clone,\n \t\t\t N_(\"when cloning, create full working directory\")),\n+\t\tOPT_BOOL(0, \"single-branch\", &single_branch,\n+\t\t\t N_(\"only download metadata for the branch that will \"\n+\t\t\t    \"be checked out\")),\n \t\tOPT_END(),\n \t};\n \tconst char * const clone_usage[] = {\n@@ -409,7 +412,9 @@ static int cmd_clone(int argc, const char **argv)\n \n \tif (set_config(\"remote.origin.url=%s\", url) ||\n \t    set_config(\"remote.origin.fetch=\"\n-\t\t       \"+refs/heads/*:refs/remotes/origin/*\") ||\n+\t\t       \"+refs/heads/%s:refs/remotes/origin/%s\",\n+\t\t       single_branch ? branch : \"*\",\n+\t\t       single_branch ? branch : \"*\") ||\n \t    set_config(\"remote.origin.promisor=true\") ||\n \t    set_config(\"remote.origin.partialCloneFilter=blob:none\")) {\n \t\tres = error(_(\"could not configure remote in '%s'\"), dir);\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex d4e3cb73fda..6ceb528f347 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,7 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n-scalar clone [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n+scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n@@ -56,6 +56,16 @@ subdirectories outside your sparse-checkout by using `git ls-tree HEAD`.\n \tInstead of checking out the branch pointed to by the cloned\n \trepository's HEAD, check out the `<name>` branch instead.\n \n+--[no-]single-branch::\n+\tClone only the history leading to the tip of a single branch, either\n+\tspecified by the `--branch` option or the primary branch remote's\n+\t`HEAD` points at.\n++\n+Further fetches into the resulting repository will only update the\n+remote-tracking branch for the branch this option was used for the initial\n+cloning. If the HEAD at the remote did not point at any branch when\n+`--single-branch` clone was made, no remote-tracking branch is created.\n+\n --[no-]full-clone::\n \tA sparse-checkout is initialized by default. This behavior can be\n \tturned off via `--full-clone`.\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 295398f62cc..9a35ab4fde6 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -45,13 +45,17 @@ test_expect_success 'set up repository to clone' '\n \n test_expect_success 'scalar clone' '\n \tsecond=$(git rev-parse --verify second:second.t) &&\n-\tscalar clone \"file://$(pwd)\" cloned &&\n+\tscalar clone \"file://$(pwd)\" cloned --single-branch &&\n \t(\n \t\tcd cloned/src &&\n \n \t\tgit config --get --global --fixed-value maintenance.repo \\\n \t\t\t\"$(pwd)\" &&\n \n+\t\tgit for-each-ref --format=\"%(refname)\" refs/remotes/origin/ >actual &&\n+\t\techo \"refs/remotes/origin/parallel\" >expect &&\n+\t\ttest_cmp expect actual &&\n+\n \t\ttest_path_is_missing 1/2 &&\n \t\ttest_must_fail git rev-list --missing=print $second &&\n \t\tgit rev-list $second &&\n-- \ngitgitgadget\n\n"},{"id":"434659","messageId":"b7fc2dc29c81603ec6df3385abd4e972916caf97.1630691688.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v2.git.1630691688.gitgitgadget@gmail.com","subject":"[PATCH v2 10/15] scalar: implement the `run` command","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-03T17:54:42Z","receivedAt":"2021-09-03T17:55:11Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nNote: this subcommand is provided primarily for backwards-compatibility,\nfor existing Scalar uses. It is mostly just a shim for `git\nmaintenance`, mapping task names from the way Scalar called them to the\nway Git calls them.\n\nThe reason why those names differ? The background maintenance was first\nimplemented in Scalar, and when it was contributed as a patch series\nimplementing the `git maintenance` command, reviewers suggested better\nnames, those suggestions were accepted before the patches were\nintegrated into core Git.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 64 +++++++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt | 19 ++++++++++++\n 2 files changed, 83 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 7dd1f28948f..0452dfce915 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -490,6 +490,69 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_run(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tstruct {\n+\t\tconst char *arg, *task;\n+\t} tasks[] = {\n+\t\t{ \"config\", NULL },\n+\t\t{ \"commit-graph\", \"commit-graph\" },\n+\t\t{ \"fetch\", \"prefetch\" },\n+\t\t{ \"loose-objects\", \"loose-objects\" },\n+\t\t{ \"pack-files\", \"incremental-repack\" },\n+\t\t{ NULL, NULL }\n+\t};\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tconst char *usagestr[] = { NULL, NULL };\n+\tint i;\n+\n+\tstrbuf_addstr(&buf, N_(\"scalar run <task> [<enlistment>]\\nTasks:\\n\"));\n+\tfor (i = 0; tasks[i].arg; i++)\n+\t\tstrbuf_addf(&buf, \"\\t%s\\n\", tasks[i].arg);\n+\tusagestr[0] = buf.buf;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usagestr, 0);\n+\n+\tif (argc == 0)\n+\t\tusage_with_options(usagestr, options);\n+\n+\tif (!strcmp(\"all\", argv[0]))\n+\t\ti = -1;\n+\telse {\n+\t\tfor (i = 0; tasks[i].arg && strcmp(tasks[i].arg, argv[0]); i++)\n+\t\t\t; /* keep looking for the task */\n+\n+\t\tif (i > 0 && !tasks[i].arg) {\n+\t\t\terror(_(\"no such task: '%s'\"), argv[0]);\n+\t\t\tusage_with_options(usagestr, options);\n+\t\t}\n+\t}\n+\n+\targc--;\n+\targv++;\n+\tsetup_enlistment_directory(argc, argv, usagestr, options, NULL);\n+\tstrbuf_release(&buf);\n+\n+\tif (i == 0)\n+\t\treturn register_dir();\n+\n+\tif (i > 0)\n+\t\treturn run_git(\"maintenance\", \"run\",\n+\t\t\t       \"--task\", tasks[i].task, NULL);\n+\n+\tif (register_dir())\n+\t\treturn -1;\n+\tfor (i = 1; tasks[i].arg; i++)\n+\t\tif (run_git(\"maintenance\", \"run\",\n+\t\t\t    \"--task\", tasks[i].task, NULL))\n+\t\t\treturn -1;\n+\treturn 0;\n+}\n+\n static int remove_deleted_enlistment(struct strbuf *path)\n {\n \tint res = 0;\n@@ -562,6 +625,7 @@ static struct {\n \t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n+\t{ \"run\", cmd_run },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 6ceb528f347..ff8792e5a64 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -12,6 +12,7 @@ scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<e\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n+scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -98,6 +99,24 @@ unregister [<enlistment>]::\n \tRemove the specified repository from the list of repositories\n \tregistered with Scalar and stop the scheduled background maintenance.\n \n+Run\n+~~~\n+\n+scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]::\n+\tRun the given maintenance task (or all tasks, if `all` was specified).\n+\tExcept for `all` and `config`, this subcommand simply hands off to\n+\tlinkgit:git-maintenance[1] (mapping `fetch` to `prefetch` and\n+\t`pack-files` to `incremental-repack`).\n++\n+These tasks are run automatically as part of the scheduled maintenance,\n+as soon as the repository is registered with Scalar. It should therefore\n+not be necessary to run this subcommand manually.\n++\n+The `config` task is specific to Scalar and configures all those\n+opinionated default settings that make Git work more efficiently with\n+large repositories. As this task is run as part of `scalar clone`\n+automatically, explicit invocations of this task are rarely needed.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"434660","messageId":"79e9f5d203a85f9a4cf6321d10c5d04b0dbdf8f8.1630691688.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v2.git.1630691688.gitgitgadget@gmail.com","subject":"[PATCH v2 12/15] scalar: teach 'reconfigure' to optionally handle all registered enlistments","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-03T17:54:44Z","receivedAt":"2021-09-03T17:55:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nAfter a Scalar upgrade, it can come in really handy if there is an easy\nway to reconfigure all Scalar enlistments. This new option offers this\nfunctionality.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 61 ++++++++++++++++++++++++++++++--\n contrib/scalar/scalar.txt        | 10 ++++--\n contrib/scalar/t/t9099-scalar.sh |  3 ++\n 3 files changed, 68 insertions(+), 6 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex bfbf58f7a91..7e98a1d6b06 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -494,22 +494,77 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int get_scalar_repos(const char *key, const char *value, void *data)\n+{\n+\tstruct string_list *list = data;\n+\n+\tif (!strcmp(key, \"scalar.repo\"))\n+\t\tstring_list_append(list, value);\n+\n+\treturn 0;\n+}\n+\n static int cmd_reconfigure(int argc, const char **argv)\n {\n+\tint all = 0;\n \tstruct option options[] = {\n+\t\tOPT_BOOL('a', \"all\", &all,\n+\t\t\t N_(\"reconfigure all registered enlistments\")),\n \t\tOPT_END(),\n \t};\n \tconst char * const usage[] = {\n-\t\tN_(\"scalar reconfigure [<enlistment>]\"),\n+\t\tN_(\"scalar reconfigure [--all | <enlistment>]\"),\n \t\tNULL\n \t};\n+\tstruct string_list scalar_repos = STRING_LIST_INIT_DUP;\n+\tint i, res = 0;\n+\tstruct repository r = { NULL };\n+\tstruct strbuf commondir = STRBUF_INIT, gitdir = STRBUF_INIT;\n \n \targc = parse_options(argc, argv, NULL, options,\n \t\t\t     usage, 0);\n \n-\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\tif (!all) {\n+\t\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\t\treturn set_recommended_config(1);\n+\t}\n+\n+\tif (argc > 0)\n+\t\tusage_msg_opt(_(\"--all or <enlistment>, but not both\"),\n+\t\t\t      usage, options);\n+\n+\tgit_config(get_scalar_repos, &scalar_repos);\n \n-\treturn set_recommended_config(1);\n+\tfor (i = 0; i < scalar_repos.nr; i++) {\n+\t\tconst char *dir = scalar_repos.items[i].string;\n+\n+\t\tstrbuf_reset(&commondir);\n+\t\tstrbuf_reset(&gitdir);\n+\n+\t\tif (chdir(dir) < 0) {\n+\t\t\twarning_errno(_(\"could not switch to '%s'\"), dir);\n+\t\t\tres = -1;\n+\t\t} else if (discover_git_directory(&commondir, &gitdir) < 0) {\n+\t\t\twarning_errno(_(\"git repository gone in '%s'\"), dir);\n+\t\t\tres = -1;\n+\t\t} else {\n+\t\t\tgit_config_clear();\n+\n+\t\t\tthe_repository = &r;\n+\t\t\tr.commondir = commondir.buf;\n+\t\t\tr.gitdir = gitdir.buf;\n+\n+\t\t\tif (set_recommended_config(1) < 0)\n+\t\t\t\tres = -1;\n+\t\t}\n+\t}\n+\n+\tstring_list_clear(&scalar_repos, 1);\n+\tstrbuf_release(&commondir);\n+\tstrbuf_release(&gitdir);\n+\n+\treturn res;\n }\n \n static int cmd_run(int argc, const char **argv)\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex df6e961ca37..8637e207860 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -13,7 +13,7 @@ scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n-scalar reconfigure <enlistment>\n+scalar reconfigure [ --all | <enlistment> ]\n \n DESCRIPTION\n -----------\n@@ -32,8 +32,8 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand. With the exception of `clone` and `list`, all\n-subcommands expect to be run in an enlistment.\n+depending on the subcommand. With the exception of `clone`, `list` and\n+`reconfigure --all`, all subcommands expect to be run in an enlistment.\n \n COMMANDS\n --------\n@@ -125,6 +125,10 @@ After a Scalar upgrade, or when the configuration of a Scalar enlistment\n was somehow corrupted or changed by mistake, this subcommand allows to\n reconfigure the enlistment.\n \n+With the `--all` option, all enlistments currently registered with Scalar\n+will be reconfigured. This option is meant to to be run every time after\n+Scalar is upgraded.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex e6d74a06ca0..5fe7fabd0e5 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -70,6 +70,9 @@ test_expect_success 'scalar reconfigure' '\n \tscalar register one &&\n \tgit -C one/src config core.preloadIndex false &&\n \tscalar reconfigure one &&\n+\ttest true = \"$(git -C one/src config core.preloadIndex)\" &&\n+\tgit -C one/src config core.preloadIndex false &&\n+\tscalar reconfigure -a &&\n \ttest true = \"$(git -C one/src config core.preloadIndex)\"\n '\n \n-- \ngitgitgadget\n\n"},{"id":"434661","messageId":"94a21982652a1e0dea3fed08d56f17159066e965.1630691688.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v2.git.1630691688.gitgitgadget@gmail.com","subject":"[PATCH v2 13/15] scalar: implement the `delete` command","fromName":"Matthew John Cheetham via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-03T17:54:45Z","receivedAt":"2021-09-03T17:55:13Z","isPatch":true,"sender":{"key":"mjcheetham@outlook.com","avatar":"https://avatars.githubusercontent.com/u/5658207?v=4"},"body":"From: Matthew John Cheetham <mjcheetham@outlook.com>\n\nDelete an enlistment by first unregistering the repository and then\ndeleting the enlistment directory (usually the directory containing the\nworktree `src/` directory).\n\nOn Windows, if the current directory is inside the enlistment's\ndirectory, change to the parent of the enlistment directory, to allow us\nto delete the enlistment (directories used by processes e.g. as current\nworking directories cannot be deleted on Windows).\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Matthew John Cheetham <mjcheetham@outlook.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 55 ++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        |  8 +++++\n contrib/scalar/t/t9099-scalar.sh |  9 ++++++\n 3 files changed, 72 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 7e98a1d6b06..822d7c39903 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -8,6 +8,7 @@\n #include \"config.h\"\n #include \"run-command.h\"\n #include \"refs.h\"\n+#include \"dir.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -334,6 +335,33 @@ static char *remote_default_branch(const char *url)\n \treturn NULL;\n }\n \n+static int delete_enlistment(struct strbuf *enlistment)\n+{\n+#ifdef WIN32\n+\tstruct strbuf parent = STRBUF_INIT;\n+#endif\n+\n+\tif (unregister_dir())\n+\t\tdie(_(\"failed to unregister repository\"));\n+\n+#ifdef WIN32\n+\t/*\n+\t * Change the current directory to one outside of the enlistment so\n+\t * that we may delete everything underneath it.\n+\t */\n+\tstrbuf_addbuf(&parent, enlistment);\n+\tstrbuf_parent_directory(&parent);\n+\tif (chdir(parent.buf) < 0)\n+\t\tdie_errno(_(\"could not switch to '%s'\"), parent.buf);\n+\tstrbuf_release(&parent);\n+#endif\n+\n+\tif (remove_dir_recursively(enlistment, 0))\n+\t\tdie(_(\"failed to delete enlistment directory\"));\n+\n+\treturn 0;\n+}\n+\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n@@ -694,6 +722,32 @@ static int cmd_unregister(int argc, const char **argv)\n \treturn unregister_dir();\n }\n \n+static int cmd_delete(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar delete <enlistment>\"),\n+\t\tNULL\n+\t};\n+\tstruct strbuf enlistment = STRBUF_INIT;\n+\tint res = 0;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tif (argc != 1)\n+\t\tusage_with_options(usage, options);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, &enlistment);\n+\n+\tres = delete_enlistment(&enlistment);\n+\tstrbuf_release(&enlistment);\n+\n+\treturn res;\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n@@ -704,6 +758,7 @@ static struct {\n \t{ \"unregister\", cmd_unregister },\n \t{ \"run\", cmd_run },\n \t{ \"reconfigure\", cmd_reconfigure },\n+\t{ \"delete\", cmd_delete },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 8637e207860..b7ace8b9f1f 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -14,6 +14,7 @@ scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n scalar reconfigure [ --all | <enlistment> ]\n+scalar delete <enlistment>\n \n DESCRIPTION\n -----------\n@@ -129,6 +130,13 @@ With the `--all` option, all enlistments currently registered with Scalar\n will be reconfigured. This option is meant to to be run every time after\n Scalar is upgraded.\n \n+Delete\n+~~~~~~\n+\n+delete <enlistment>::\n+\tThis subcommand lets you delete an existing Scalar enlistment from your\n+\tlocal file system, unregistering the repository.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 5fe7fabd0e5..7e8771d0eff 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -76,4 +76,13 @@ test_expect_success 'scalar reconfigure' '\n \ttest true = \"$(git -C one/src config core.preloadIndex)\"\n '\n \n+test_expect_success 'scalar delete without enlistment shows a usage' '\n+\ttest_expect_code 129 scalar delete\n+'\n+\n+test_expect_success 'scalar delete with enlistment' '\n+\tscalar delete cloned &&\n+\ttest_path_is_missing cloned\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"434662","messageId":"9a834c23d08972e6b30742414415a798138ca32d.1630691688.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v2.git.1630691688.gitgitgadget@gmail.com","subject":"[PATCH v2 11/15] scalar: allow reconfiguring an existing enlistment","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-03T17:54:43Z","receivedAt":"2021-09-03T17:55:14Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis comes in handy during Scalar upgrades, or when config settings were\nmessed up by mistake.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 79 +++++++++++++++++++++-----------\n contrib/scalar/scalar.txt        |  8 ++++\n contrib/scalar/t/t9099-scalar.sh |  8 ++++\n 3 files changed, 67 insertions(+), 28 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 0452dfce915..bfbf58f7a91 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -115,18 +115,20 @@ static int run_git(const char *arg, ...)\n \treturn res;\n }\n \n-static int set_recommended_config(void)\n+static int set_recommended_config(int reconfigure)\n {\n \tstruct {\n \t\tconst char *key;\n \t\tconst char *value;\n+\t\tint overwrite_on_reconfigure;\n \t} config[] = {\n-\t\t{ \"am.keepCR\", \"true\" },\n-\t\t{ \"core.FSCache\", \"true\" },\n-\t\t{ \"core.multiPackIndex\", \"true\" },\n-\t\t{ \"core.preloadIndex\", \"true\" },\n+\t\t/* Required */\n+\t\t{ \"am.keepCR\", \"true\", 1 },\n+\t\t{ \"core.FSCache\", \"true\", 1 },\n+\t\t{ \"core.multiPackIndex\", \"true\", 1 },\n+\t\t{ \"core.preloadIndex\", \"true\", 1 },\n #ifndef WIN32\n-\t\t{ \"core.untrackedCache\", \"true\" },\n+\t\t{ \"core.untrackedCache\", \"true\", 1 },\n #else\n \t\t/*\n \t\t * Unfortunately, Scalar's Functional Tests demonstrated\n@@ -140,28 +142,29 @@ static int set_recommended_config(void)\n \t\t * Therefore, with a sad heart, we disable this very useful\n \t\t * feature on Windows.\n \t\t */\n-\t\t{ \"core.untrackedCache\", \"false\" },\n+\t\t{ \"core.untrackedCache\", \"false\", 1 },\n #endif\n-\t\t{ \"core.logAllRefUpdates\", \"true\" },\n-\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n-\t\t{ \"credential.validate\", \"false\" }, /* GCM4W-only */\n-\t\t{ \"gc.auto\", \"0\" },\n-\t\t{ \"gui.GCWarning\", \"false\" },\n-\t\t{ \"index.threads\", \"true\" },\n-\t\t{ \"index.version\", \"4\" },\n-\t\t{ \"merge.stat\", \"false\" },\n-\t\t{ \"merge.renames\", \"false\" },\n-\t\t{ \"pack.useBitmaps\", \"false\" },\n-\t\t{ \"pack.useSparse\", \"true\" },\n-\t\t{ \"receive.autoGC\", \"false\" },\n-\t\t{ \"reset.quiet\", \"true\" },\n-\t\t{ \"feature.manyFiles\", \"false\" },\n-\t\t{ \"feature.experimental\", \"false\" },\n-\t\t{ \"fetch.unpackLimit\", \"1\" },\n-\t\t{ \"fetch.writeCommitGraph\", \"false\" },\n+\t\t{ \"core.logAllRefUpdates\", \"true\", 1 },\n+\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\", 1 },\n+\t\t{ \"credential.validate\", \"false\", 1 }, /* GCM4W-only */\n+\t\t{ \"gc.auto\", \"0\", 1 },\n+\t\t{ \"gui.GCWarning\", \"false\", 1 },\n+\t\t{ \"index.threads\", \"true\", 1 },\n+\t\t{ \"index.version\", \"4\", 1 },\n+\t\t{ \"merge.stat\", \"false\", 1 },\n+\t\t{ \"merge.renames\", \"false\", 1 },\n+\t\t{ \"pack.useBitmaps\", \"false\", 1 },\n+\t\t{ \"pack.useSparse\", \"true\", 1 },\n+\t\t{ \"receive.autoGC\", \"false\", 1 },\n+\t\t{ \"reset.quiet\", \"true\", 1 },\n+\t\t{ \"feature.manyFiles\", \"false\", 1 },\n+\t\t{ \"feature.experimental\", \"false\", 1 },\n+\t\t{ \"fetch.unpackLimit\", \"1\", 1 },\n+\t\t{ \"fetch.writeCommitGraph\", \"false\", 1 },\n #ifdef WIN32\n-\t\t{ \"http.sslBackend\", \"schannel\" },\n+\t\t{ \"http.sslBackend\", \"schannel\", 1 },\n #endif\n+\t\t/* Optional */\n \t\t{ \"status.aheadBehind\", \"false\" },\n \t\t{ \"commitGraph.generationVersion\", \"1\" },\n \t\t{ \"core.autoCRLF\", \"false\" },\n@@ -172,7 +175,8 @@ static int set_recommended_config(void)\n \tchar *value;\n \n \tfor (i = 0; config[i].key; i++) {\n-\t\tif (git_config_get_string(config[i].key, &value)) {\n+\t\tif ((reconfigure && config[i].overwrite_on_reconfigure) ||\n+\t\t    git_config_get_string(config[i].key, &value)) {\n \t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"created\");\n \t\t\tif (git_config_set_gently(config[i].key,\n \t\t\t\t\t\t  config[i].value) < 0)\n@@ -237,7 +241,7 @@ static int register_dir(void)\n \tint res = add_or_remove_enlistment(1);\n \n \tif (!res)\n-\t\tres = set_recommended_config();\n+\t\tres = set_recommended_config(0);\n \n \tif (!res)\n \t\tres = toggle_maintenance(1);\n@@ -425,7 +429,7 @@ static int cmd_clone(int argc, const char **argv)\n \t    (res = run_git(\"sparse-checkout\", \"init\", \"--cone\", NULL)))\n \t\tgoto cleanup;\n \n-\tif (set_recommended_config())\n+\tif (set_recommended_config(0))\n \t\treturn error(_(\"could not configure '%s'\"), dir);\n \n \tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL))) {\n@@ -490,6 +494,24 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_reconfigure(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar reconfigure [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn set_recommended_config(1);\n+}\n+\n static int cmd_run(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -626,6 +648,7 @@ static struct {\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n \t{ \"run\", cmd_run },\n+\t{ \"reconfigure\", cmd_reconfigure },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex ff8792e5a64..df6e961ca37 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -13,6 +13,7 @@ scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n+scalar reconfigure <enlistment>\n \n DESCRIPTION\n -----------\n@@ -117,6 +118,13 @@ opinionated default settings that make Git work more efficiently with\n large repositories. As this task is run as part of `scalar clone`\n automatically, explicit invocations of this task are rarely needed.\n \n+Reconfigure\n+~~~~~~~~~~~\n+\n+After a Scalar upgrade, or when the configuration of a Scalar enlistment\n+was somehow corrupted or changed by mistake, this subcommand allows to\n+reconfigure the enlistment.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 9a35ab4fde6..e6d74a06ca0 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -65,4 +65,12 @@ test_expect_success 'scalar clone' '\n \t)\n '\n \n+test_expect_success 'scalar reconfigure' '\n+\tgit init one/src &&\n+\tscalar register one &&\n+\tgit -C one/src config core.preloadIndex false &&\n+\tscalar reconfigure one &&\n+\ttest true = \"$(git -C one/src config core.preloadIndex)\"\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"434663","messageId":"707d8e19683c5381c989a3f5dc32148bba3715bc.1630691688.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v2.git.1630691688.gitgitgadget@gmail.com","subject":"[PATCH v2 14/15] scalar: implement the `version` command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-03T17:54:46Z","receivedAt":"2021-09-03T17:55:17Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe .NET version of Scalar has a `version` command. This was necessary\nbecause it was versioned independently of Git.\n\nSince Scalar is now tightly coupled with Git, it does not make sense for\nthem to show different versions. Therefore, it shows the same output as\n`git version`. For backwards-compatibility with the .NET version,\n`scalar version` prints to `stderr`, though (`git version` prints to\n`stdout` instead).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c | 39 +++++++++++++++++++++++++++++++++++++++\n 1 file changed, 39 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 822d7c39903..e3349dce47b 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -9,6 +9,7 @@\n #include \"run-command.h\"\n #include \"refs.h\"\n #include \"dir.h\"\n+#include \"help.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -362,6 +363,15 @@ static int delete_enlistment(struct strbuf *enlistment)\n \treturn 0;\n }\n \n+/*\n+ * Dummy implementation; Using `get_version_info()` would cause a link error\n+ * without this.\n+ */\n+void load_builtin_commands(const char *prefix, struct cmdnames *cmds)\n+{\n+\tdie(\"not implemented\");\n+}\n+\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n@@ -748,6 +758,34 @@ static int cmd_delete(int argc, const char **argv)\n \treturn res;\n }\n \n+static int cmd_version(int argc, const char **argv)\n+{\n+\tint verbose = 0, build_options = 0;\n+\tstruct option options[] = {\n+\t\tOPT__VERBOSE(&verbose, N_(\"include Git version\")),\n+\t\tOPT_BOOL(0, \"build-options\", &build_options,\n+\t\t\t N_(\"include Git's build options\")),\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar verbose [-v | --verbose] [--build-options]\"),\n+\t\tNULL\n+\t};\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tif (argc != 0)\n+\t\tusage_with_options(usage, options);\n+\n+\tget_version_info(&buf, build_options);\n+\tfprintf(stderr, \"%s\\n\", buf.buf);\n+\tstrbuf_release(&buf);\n+\n+\treturn 0;\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n@@ -759,6 +797,7 @@ static struct {\n \t{ \"run\", cmd_run },\n \t{ \"reconfigure\", cmd_reconfigure },\n \t{ \"delete\", cmd_delete },\n+\t{ \"version\", cmd_version },\n \t{ NULL, NULL},\n };\n \n-- \ngitgitgadget\n\n"},{"id":"434664","messageId":"26e23b5c5e5ab8727465fe4a78e6d9bb2306d5ca.1630691688.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v2.git.1630691688.gitgitgadget@gmail.com","subject":"[PATCH v2 15/15] scalar: accept -C and -c options before the subcommand","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-03T17:54:47Z","receivedAt":"2021-09-03T17:55:17Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe `git` executable has these two very useful options:\n\n-C <directory>:\n\tswitch to the specified directory before performing any actions\n\n-c <key>=<value>:\n\ttemporarily configure this setting for the duration of the\n\tspecified scalar subcommand\n\nWith this commit, we teach the `scalar` executable the same trick.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 22 +++++++++++++++++++++-\n contrib/scalar/scalar.txt | 10 ++++++++++\n 2 files changed, 31 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex e3349dce47b..670a259a12c 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -806,6 +806,25 @@ int cmd_main(int argc, const char **argv)\n \tstruct strbuf scalar_usage = STRBUF_INIT;\n \tint i;\n \n+\twhile (argc > 1 && *argv[1] == '-') {\n+\t\tif (!strcmp(argv[1], \"-C\")) {\n+\t\t\tif (argc < 3)\n+\t\t\t\tdie(_(\"-C requires a <directory>\"));\n+\t\t\tif (chdir(argv[2]) < 0)\n+\t\t\t\tdie_errno(_(\"could not change to '%s'\"),\n+\t\t\t\t\t  argv[2]);\n+\t\t\targc -= 2;\n+\t\t\targv += 2;\n+\t\t} else if (!strcmp(argv[1], \"-c\")) {\n+\t\t\tif (argc < 3)\n+\t\t\t\tdie(_(\"-c requires a <key>=<value> argument\"));\n+\t\t\tgit_config_push_parameter(argv[2]);\n+\t\t\targc -= 2;\n+\t\t\targv += 2;\n+\t\t} else\n+\t\t\tbreak;\n+\t}\n+\n \tif (argc > 1) {\n \t\targv++;\n \t\targc--;\n@@ -816,7 +835,8 @@ int cmd_main(int argc, const char **argv)\n \t}\n \n \tstrbuf_addstr(&scalar_usage,\n-\t\t      N_(\"scalar <command> [<options>]\\n\\nCommands:\\n\"));\n+\t\t      N_(\"scalar [-C <directory>] [-c <key>=<value>] \"\n+\t\t\t \"<command> [<options>]\\n\\nCommands:\\n\"));\n \tfor (i = 0; builtins[i].name; i++)\n \t\tstrbuf_addf(&scalar_usage, \"\\t%s\\n\", builtins[i].name);\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex b7ace8b9f1f..5804baf7d70 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -36,6 +36,16 @@ The `scalar` command implements various subcommands, and different options\n depending on the subcommand. With the exception of `clone`, `list` and\n `reconfigure --all`, all subcommands expect to be run in an enlistment.\n \n+The following options can be specified _before_ the subcommand:\n+\n+-C <directory>::\n+\tBefore running the subcommand, change the working directory. This\n+\toption imitates the same option of linkgit:git[1].\n+\n+-c <key>=<value>::\n+\tFor the duration of running the specified subcommand, configure this\n+\tsetting. This option imitates the same option of linkgit:git[1].\n+\n COMMANDS\n --------\n \n-- \ngitgitgadget\n"},{"id":"434682","messageId":"7d68dc36-81b3-1c8e-61d7-c656aee8c1fb@gmail.com","threadId":"56409","inReplyTo":"fb7c931ddb3a248542bc6a03d09189e937315d0c.1630691688.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 07/15] scalar: implement 'scalar list'","fromName":"Bagas Sanjaya","fromEmail":"bagasdotme@gmail.com","sentAt":"2021-09-04T08:58:27Z","receivedAt":"2021-09-04T08:58:36Z","isPatch":true,"sender":{"key":"bagasdotme@gmail.com","avatar":"https://avatars.githubusercontent.com/u/40219486?v=4"},"body":"On 04/09/21 00.54, Derrick Stolee via GitGitGadget wrote:\n> +List\n> +~~~~\n> +\n> +list::\n> +\tTo see which repositories are currently registered by the service, run\n> +\t`scalar list`. This subcommand does not need to be run inside a Scalar\n> +\tenlistment.\n> +\n\nI think the man-page-style wording should be:\n\n> list::\n> \tList enlistments that are currently registered by Scalar. This\n> \tsubcommand does not need to be run inside an enlistment.\n\n-- \nAn old man doll... just what I always wanted! - Clara\n"},{"id":"434749","messageId":"87bl56plbi.fsf@evledraar.gmail.com","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2109031750291.55@tvgsbejvaqbjf.bet","subject":"Re: [PATCH 11/15] scalar: allow reconfiguring an existing enlistment","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-09-06T01:01:03Z","receivedAt":"2021-09-06T01:03:25Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Sep 03 2021, Johannes Schindelin wrote:\n\n> Hi Ævar,\n>\n> On Tue, 31 Aug 2021, Ævar Arnfjörð Bjarmason wrote:\n>\n>>\n>> On Mon, Aug 30 2021, Johannes Schindelin via GitGitGadget wrote:\n>>\n>> > This comes in handy during Scalar upgrades, or when config settings were\n>> > messed up by mistake.\n>>\n>> > [...]\n>> >  \t\tconst char *key;\n>> >  \t\tconst char *value;\n>> > +\t\tint overwrite_on_reconfigure;\n>>\n>> If you make this a \"keep_on_reconfigure\", then ...\n>\n> I do not think that this would be a better name, or that renaming this\n> field would do anything except cause more work for me.\n\nIt would also result in more readable code, i.e. why add boilerplate \",\n1\" to a boolean field in this case if every single setting is set to\n\"1\"? Doesn't it make more sense to invert the variable name & save on\nthe verbosity?\n\n>>\n>> >  \t} config[] = {\n>> > -\t\t{ \"am.keepCR\", \"true\" },\n>> > -\t\t{ \"core.FSCache\", \"true\" },\n>> > -\t\t{ \"core.multiPackIndex\", \"true\" },\n>> > -\t\t{ \"core.preloadIndex\", \"true\" },\n>> > +\t\t/* Required */\n>> > +\t\t{ \"am.keepCR\", \"true\", 1 },\n>> > +\t\t{ \"core.FSCache\", \"true\", 1 },\n>> > +\t\t{ \"core.multiPackIndex\", \"true\", 1 },\n>> > +\t\t{ \"core.preloadIndex\", \"true\", 1 },\n>>\n>> You won't need the churn/boilerplate of adding \"1\" to everything here,\n>> but can just change the initial patch to use designated initializers.\n>>\n>> That along with a throwaway macro like:\n>>\n>> #define SCALAR_CFG_TRUE(k) (.key = k, .value = \"true\")\n>> #define SCALAR_CFG_FALSE(k) (.key = k, .value = \"false\")\n>>\n>> Might (or might not) make this even easier to eyeball...\n>\n> To me, it makes things less readable. There is an entire section with the\n> header `/* Optional */` below, and I want this list to stay as readable as\n> it is now.\n\nYeah, I think those macros are probably less readable too. I should have\nphrased that as a \"one could even...\", but just the smaller change of\navoiding the \", 1\" everywhere seems worthwhile.\n"},{"id":"434750","messageId":"877dfupl7o.fsf@evledraar.gmail.com","threadId":"56409","inReplyTo":"pull.1005.v2.git.1630691688.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 00/15] [RFC] Upstreaming the Scalar command","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-09-06T00:59:03Z","receivedAt":"2021-09-06T01:05:35Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Sep 03 2021, Johannes Schindelin via GitGitGadget wrote:\n\n> Changes since v1:\n>\n>  * A couple typos were fixed\n>  * The code parsing the output of ls-remote was made more readable\n>  * The indentation used in scalar.txt now consistently uses tabs\n>  * We no longer hard-code core.bare = false when registering with Scalar\n\nA summary of outstanding but unaddressed things would be useful during\nre-rolls. In this case at least:\n\n - My point that the build system part of this is more complex than it\n   probably needs to be:\n   https://lore.kernel.org/git/87mtoxwt63.fsf@evledraar.gmail.com/\n\n - My point & Junio concurring with some style suggestions:\n   https://lore.kernel.org/git/xmqqk0jxft1p.fsf@gitster.g\n\n - My minor style comment about the \", 1\". Just saying it's\n   \"outstanding\" because I think what you had & I just replied to in\n   https://lore.kernel.org/git/87bl56plbi.fsf@evledraar.gmail.com/ might\n   have been a dismissal of the macro suggestion (which I don't like\n   either), not just the simpler \"get rid of init verbosity to all 1\".\n"},{"id":"434751","messageId":"8735qipkqd.fsf@evledraar.gmail.com","threadId":"56409","inReplyTo":"f3223c10788fe836d0322006dc2dcbafe08f6b5b.1630691688.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 08/15] scalar: implement the `clone` subcommand","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-09-06T01:12:11Z","receivedAt":"2021-09-06T01:16:02Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Sep 03 2021, Johannes Schindelin via GitGitGadget wrote:\n\n> From: Johannes Schindelin <johannes.schindelin@gmx.de>\n>\n> This implements Scalar's opinionated `clone` command: it tries to use a\n> partial clone and sets up a sparse checkout by default. In contrast to\n> `git clone`, `scalar clone` sets up the worktree in the `src/`\n> subdirectory, to encourage a separation between the source files and the\n> build output (which helps Git tremendously because it avoids untracked\n> files that have to be specifically ignored when refreshing the index).\n\nPerhaps it's simpler to just say about that /src/ injection:\n\n    `scalar clone` adds an implicit \"/src\" subdirectory to whatever\n    directory the user provides, with the added stricture on top of\n    doing that with \"git clone\" that the \"src\" cannot exist already.\n\n...\n\n> +\tif (is_directory(enlistment))\n> +\t\tdie(_(\"directory '%s' exists already\"), enlistment);\n> +\n> +\tdir = xstrfmt(\"%s/src\", enlistment);\n\nWhich also seems to suggest a bug here. I.e. if I \"git clone <repo>\n/tmp/xyz/abc\" and Ctrl+C it we'll remove \"abc\", but leave \"xyz\"\nbehind. Since we're creating that \"xyz\" (or \"src\") implicitly here an\nabort/ctrl+C followed by a retry is going to run into this error, isn't\nit?\n\nI.e. it seems what's missing in this state machine is checking if the\ndirectory was there already, and if it isn't add it to the existing\natexit() removals.\n\nWhich may be tricky seeing as this is shelling out to \"init\" then\n\"fetch\" etc, i.e. who removes it? But maybe not.\n"},{"id":"435135","messageId":"nycvar.QRO.7.76.6.2109082021030.55@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"CAPig+cQKKPq0cUn4iFpqaYOrwWnBTg0RhgHRztOGxB2Mm-GVUA@mail.gmail.com","subject":"Re: [PATCH 12/15] scalar: teach 'reconfigure' to optionally handle all registered enlistments","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-09-08T18:21:26Z","receivedAt":"2021-09-08T18:21:21Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Eric,\n\nOn Fri, 3 Sep 2021, Eric Sunshine wrote:\n\n> On Fri, Sep 3, 2021 at 11:23 AM Johannes Schindelin\n> <Johannes.Schindelin@gmx.de> wrote:\n> > On Tue, 31 Aug 2021, Eric Sunshine wrote:\n> > > On Mon, Aug 30, 2021 at 5:35 PM Johannes Schindelin via GitGitGadget\n> > > > +With the `--all` option, all enlistments currently registered with Scalar\n> > > > +will be reconfigured. This option is meant to to be run every time Scalar\n> > > > +was upgraded.\n> > >\n> > > s/was/is/\n> >\n> > I wanted to convey a temporal order, so I changed it to \"every time after\n> > Scalar is upgraded\". Okay?\n>\n> I think I understood the intent of the original, but it causes a\n> grammatical hiccup. Your revised version can work, although I might\n> write it this way:\n>\n>     This option is meant to be run each time Scalar is upgraded.\n>\n> However, perhaps that is too ambiguous and some users may think that\n> the process of upgrading Scalar will automatically run this command,\n> and you'd like to make it clear that it is the user's responsibility.\n> So, perhaps:\n>\n>     Use this option after each Scalar upgrade.\n>\n> or something.\n\nI like the last one best, too.\n\nThank you,\nDscho\n"},{"id":"435139","messageId":"nycvar.QRO.7.76.6.2109082049290.55@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"xmqqwnnxftzn.fsf@gitster.g","subject":"Re: [PATCH 08/15] scalar: implement the `clone` subcommand","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-09-08T18:59:13Z","receivedAt":"2021-09-08T18:59:05Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Fri, 3 Sep 2021, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>\n> > It would even work if the current line is shorter, but as you point out:\n> > it is wasteful. And it could be improved to be more readable. I reworked\n> > it, and it now looks like this:\n> >\n> > \tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n> > \t\tconst char *line = out.buf;\n> >\n> > \t\twhile (*line) {\n> > \t\t\tconst char *eol = strchrnul(line, '\\n'), *p;\n> > \t\t\tsize_t len = eol - line;\n> > \t\t\tchar *branch;\n> >\n> > \t\t\tif (!skip_prefix(line, \"ref: \", &p) ||\n> > \t\t\t    !strip_suffix_mem(line, &len, \"\\tHEAD\")) {\n> > \t\t\t\tline = eol + (*eol == '\\n');\n> > \t\t\t\tcontinue;\n> > \t\t\t}\n> >\n> > \t\t\teol = line + len;\n> > \t\t\tif (skip_prefix(p, \"refs/heads/\", &p)) {\n> > \t\t\t\tbranch = xstrndup(p, eol - p);\n> > \t\t\t\tstrbuf_release(&out);\n> > \t\t\t\treturn branch;\n> > \t\t\t}\n> >\n> > \t\t\terror(_(\"remote HEAD is not a branch: '%.*s'\"),\n> > \t\t\t      (int)(eol - p), p);\n> > \t\t\tstrbuf_release(&out);\n> > \t\t\treturn NULL;\n> > \t\t}\n> > \t}\n> >\n> > It now parses the output line by line, looking for the expected prefix and\n> > suffix, then verifies the ref name format, and either returns the short\n> > branch name or errors out with the message that this is not a branch.\n>\n> It is much easier to read and understand how the loop works with\n> above.\n\nExcellent.\n\n> >> > +\t\t\terror(_(\"remote HEAD is not a branch: '%.*s'\"),\n> >> > +\t\t\t      (int)(head - ref), ref);\n> >> > +\t\t\tstrbuf_release(&out);\n> >> > +\t\t\treturn NULL;\n> >>\n> >> OK.  Any symref whose basename is HEAD in their remote-tracking\n> >> hierarchy would have been rejected earlier in the loop.\n> >>\n> >> Is there a particular reason why we return early here, instead of\n> >> breaking out of hte loop and let the generic \"failed to get\" code\n> >> path below to handle this case?\n> >\n> > Yes, the reason is that I wanted to err on the side of caution. If the\n> > remote repository reports a default branch that is not a default branch at\n> > all, I do not want to pretend that things are fine and then run into\n> > trouble later when we set up a non-branch as remote-tracking target or\n> > something like that.\n>\n> Wouldn't we have the same problem when the remote end does not\n> advertise HEAD and we fall back to \"local default\", though?  We'd\n> run into trouble later as we use \"local default\" that may correspond\n> to a non-branch there as remote-tracking target, or something like\n> that.\n\nAll true, there will be trouble at some stage.\n\nThe difference between the two cases, in my mind, is that cloning an empty\nrepository would run into the latter code path and might still have a\nchance to work as intended by using the local default branch name.\n\nIn any case, as I indicated earlier, I am _very_ interested in moving as\nmuch functionality as possible from Scalar to Git proper. In this\ninstance, I hope to move most of the code from `scalar.c` to `clone.c`\n(guarded by one or more new options). And as soon as that happens, the\ndiscussion we're having right now will be moot ;-)\n\nWhich means that I want to weigh how much effort to put into polishing an\nunlikely code path on one side, and on the other side how much effort to\nput into moving the functionality away from `contrib/` and deleting that\nunlikely code path.\n\nIn the same vein, while this patch series contains (mostly) code in\n`contrib/` (and therefore technically does not need to adhere strictly to\nGit's code style), it is probably wise to pay closer attention to the code\nstyle particularly in those parts that are prone to be moved verbatim (or\nclose to verbatim) to Git proper.\n\nThanks,\nDscho\n\n> Not that I care too deeply in the error case, though.  I just felt\n> that this early return was an uneven way to follow the principle to\n> err on the side of caution, as we continue with the local default\n> when the other side fails to tell us what their HEAD points at.\n>\n> Thanks.\n>\n"},{"id":"435142","messageId":"nycvar.QRO.7.76.6.2109082100180.55@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"xmqqk0jxft1p.fsf@gitster.g","subject":"Re: [PATCH 10/15] scalar: implement the `run` command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-09-08T19:11:23Z","receivedAt":"2021-09-08T19:11:16Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Fri, 3 Sep 2021, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>\n> > Hi Ævar,\n> >\n> > On Tue, 31 Aug 2021, Ævar Arnfjörð Bjarmason wrote:\n> >\n> >> On Mon, Aug 30 2021, Derrick Stolee via GitGitGadget wrote:\n> >>\n> >> > +\tconst char *usagestr[] = { NULL, NULL };\n> >>\n> >> Missing usage strings?\n> >\n> > This command will show a generated usage, i.e. a non-static string. It\n> > therefore cannot be specified here already. See the `strbuf_*()` calls\n> > populating `buf` and the `usagestr[0] = buf.buf;` assignment.\n> >\n> >> > +\tif (argc == 0)\n> >>\n> >> Style nit (per style guide): s/argc == 0/!argc/g.\n> >\n> > It is true that we often do this, but in this instance it would be\n> > misleading: `argc` is a counter, not a Boolean.\n>\n> That argument could be a plausible excuse to deviate from the style\n> if it were\n>\n> \tif (argc == 0)\n> \t\tdo no args case;\n> \telse if (argc == 1)\n> \t\tdo one arg case;\n> \telse if (argc == 2)\n> \t\tdo two args case;\n> \t...\n>\n> Replacing the first one with \"if (!argc)\" may make it less readable.\n>\n> But I do not think the reasoning applies here\n>\n> \tif (argc == 0)\n> \t\tdo a thing that applies only to no args case;\n>\n> without \"else\".  This is talking about \"do we have any argument? Yes\n> or no?\" Boolean here.\n\nWell, I offer a differing opinion. But you're right, we are at least\nconsistent in Git's source code in using `!i` where other projects would\nuse `i == 0`, and consistency is definitely something I'd like to see more\nin Git, not less.\n\nSo I changed it as you suggested.\n\n>\n> >> > +\tif (!strcmp(\"all\", argv[0]))\n> >> > +\t\ti = -1;\n> >>\n> >> Style nit (per style guide): missing braces here.\n> >\n> > The style guide specifically allows my preference to leave single-line\n> > blocks without curlies.\n>\n> Actually, the exception goes the other way, no?\n>\n> We generally want to avoid such an unnecessary braces around a\n> single statement block.  But when we have an else clause that has a\n> block with multiple statements (hence braces are required), as an\n> exception, the guide asks you to write braces around the body of the\n> if side for consistency.\n\nYou're right. I am somehow still using the previous style where we\n_required_ single-line blocks _not_ to have curly brackets (see e.g.\naa1c48df817 ([PATCH] ls-tree enhancements, 2005-04-15), the `else` part of\nthe added `if (! eltbuf)` block).\n\n>\n> When you only have just a couple of lines on the \"else {}\" side, I\n> do not think it matters too much either way for readability, though.\n> I cannot see the \"else\" side in the above clause, but IIRC it wasn't\n> just a few lines, was it?\n\nIt depends what you count as \"just a few lines\". There are seven lines\nenclosed within the curly brackets of the `else` block.\n\nBut as much as I enjoy thorough reviews of the Scalar code, I am failing\nat getting excited about code style discussions, therefore I simply went\nwith your suggestion to enclose even the single-line block in curly\nbrackets.\n\nThanks,\nDscho\n"},{"id":"435143","messageId":"nycvar.QRO.7.76.6.2109082111360.55@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"7d68dc36-81b3-1c8e-61d7-c656aee8c1fb@gmail.com","subject":"Re: [PATCH v2 07/15] scalar: implement 'scalar list'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-09-08T19:11:53Z","receivedAt":"2021-09-08T19:11:55Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Bagas,\n\nOn Sat, 4 Sep 2021, Bagas Sanjaya wrote:\n\n> On 04/09/21 00.54, Derrick Stolee via GitGitGadget wrote:\n> > +List\n> > +~~~~\n> > +\n> > +list::\n> > +\tTo see which repositories are currently registered by the service, run\n> > +\t`scalar list`. This subcommand does not need to be run inside a Scalar\n> > +\tenlistment.\n> > +\n>\n> I think the man-page-style wording should be:\n>\n> > list::\n> >  List enlistments that are currently registered by Scalar. This\n> >  subcommand does not need to be run inside an enlistment.\n\nThank you, I adopted that wording.\n\nCiao,\nDscho\n"},{"id":"435149","messageId":"nycvar.QRO.7.76.6.2109082112270.55@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"8735qipkqd.fsf@evledraar.gmail.com","subject":"Re: [PATCH v2 08/15] scalar: implement the `clone` subcommand","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-09-08T19:23:29Z","receivedAt":"2021-09-08T19:23:29Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Ævar,\n\nOn Mon, 6 Sep 2021, Ævar Arnfjörð Bjarmason wrote:\n\n>\n> On Fri, Sep 03 2021, Johannes Schindelin via GitGitGadget wrote:\n>\n> > From: Johannes Schindelin <johannes.schindelin@gmx.de>\n> >\n> > This implements Scalar's opinionated `clone` command: it tries to use a\n> > partial clone and sets up a sparse checkout by default. In contrast to\n> > `git clone`, `scalar clone` sets up the worktree in the `src/`\n> > subdirectory, to encourage a separation between the source files and the\n> > build output (which helps Git tremendously because it avoids untracked\n> > files that have to be specifically ignored when refreshing the index).\n>\n> Perhaps it's simpler to just say about that /src/ injection:\n>\n>     `scalar clone` adds an implicit \"/src\" subdirectory to whatever\n>     directory the user provides, with the added stricture on top of\n>     doing that with \"git clone\" that the \"src\" cannot exist already.\n\nIt would not only be simpler, it would also skip an important point I\ntried to make, namely how this _differs_ from `git clone`. Rather crucial,\nreally.\n\n> ...\n>\n> > +\tif (is_directory(enlistment))\n> > +\t\tdie(_(\"directory '%s' exists already\"), enlistment);\n> > +\n> > +\tdir = xstrfmt(\"%s/src\", enlistment);\n>\n> Which also seems to suggest a bug here. I.e. if I \"git clone <repo>\n> /tmp/xyz/abc\" and Ctrl+C it we'll remove \"abc\", but leave \"xyz\"\n> behind. Since we're creating that \"xyz\" (or \"src\") implicitly here an\n> abort/ctrl+C followed by a retry is going to run into this error, isn't\n> it?\n\nSure, it's just like calling `git clone <url> a/b/c` and upon failure\nseeing only the `c` directory removed, while `a/b` is left behind.\n\n> I.e. it seems what's missing in this state machine is checking if the\n> directory was there already, and if it isn't add it to the existing\n> atexit() removals.\n>\n> Which may be tricky seeing as this is shelling out to \"init\" then\n> \"fetch\" etc, i.e. who removes it? But maybe not.\n\nI would rather spend time (after this patch series landed, of course) on\nteaching `git clone` to handle what Scalar needs, and upon Ctrl+C to\noptionally remove _all_ the directories it created, not just the innermost\none.\n\nThen we get that functionality \"for free\", without spending a lot of time\non code that will be obsolete soon enough anyway.\n\nCiao,\nJohannes\n"},{"id":"435151","messageId":"pull.1005.v3.git.1631129086.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v2.git.1630691688.gitgitgadget@gmail.com","subject":"[PATCH v3 00/15] [RFC] Upstreaming the Scalar command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-08T19:24:30Z","receivedAt":"2021-09-08T19:24:51Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"tl;dr: This series contributes the Scalar command to the Git project. This\ncommand provides an opinionated way to create and configure repositories\nwith a focus on very large repositories.\n\n\nBackground\n==========\n\nYears ago, Microsoft wanted to move the source code of the Windows operating\nsystem to Git. The challenge there was to prove that Git could scale to\nmassive monorepos. The VFS for Git (formerly GVFS) project was born to take\nup that challenge.\n\nThe final solution included a virtual filesystem (with both user-mode and\nkernel components) and a customized fork of Git for Windows. This solution\ncontained several key concepts, such as only populating a portion of the\nworking directory, demand-fetching blobs, and performing periodic repo\nmaintenance in the background. However, the required kernel drivers made it\ndifficult to port the solution to other platforms.\n\nBut it was realized that many of these key concepts were independent of the\nactual VFS and its projection of the working directory. The Scalar project\nwas created to make that separation, refine the key concepts, and then\nextract those features into the new Scalar command.\n\n\nThe present\n===========\n\nThe Scalar project provides a completely functional non-virtual experience\nfor monorepos. But why stop there. The Scalar project was designed to be a\nself-destructing vehicle to allow those key concepts to be moved into core\nGit itself for the benefit of all. For example, partial clone,\nsparse-checkout, and background maintenance have already been upstreamed and\nremoved from Scalar proper. This patch series provides a C-based\nimplementation of the final remaining portions of the Scalar command. This\nwill make it easier for users to experiment with the Scalar command. It will\nalso make it substantially easier to experiment with moving functionality\nfrom Scalar into core Git, while maintaining backwards-compatibility for\nexisting Scalar users.\n\nThe C-based Scalar has been shipped to Scalar users, and can be tested by\nany interested reader:\nhttps://github.com/microsoft/git/releases/tag/v2.33.0.vfs.0.0 (it offers a\nGit for Windows installer, a macOS package and an Ubuntu package).\n\n\nOpportunities\n=============\n\nApart from providing the Scalar command, this contribution is intended to\nserve as a basis for further mailing list discussions on moving (some of)\nthese key concepts into the main Git commands.\n\nFor example, we previously discussed the idea of a \"git big-clone\" that does\nmuch of what \"scalar clone\" is doing. This patch series is a step to make\nsuch functionality exist in the Git code base while we simmer on what such a\n\"git big-clone\" command-line interface would look like.\n\nThis is one of many possible ways to do this. Creating a 'git big-clone'\ncould lock Git into backwards compatibility concerns so it is necessary to\napproach such an endeavor with caution. As a discussion starter, the scalar\nclone <url> command does roughly this:\n\n 1. git clone --sparse --filter=blob:none /src\n 2. git -C /src sparse-checkout init --cone\n 3. git -C /src config (many times)\n 4. git -C /src maintenance start\n\nIt is my hope inspire discussions about what parts of Scalar could go into\ncore Git, and where, and in which form. While we wish to maintain\nbackwards-compatibility of Scalar's command-line interface (because it is\nalready in use), by having the Scalar code in the same code base as Git's,\nit will be much easier to move functionality without having to maintain\nloose version coupling between independently-versioned Scalar and Git. The\ntight version-coupling, along with having access to libgit.a also allows the\nC-based implementation of Scalar to be much smaller than the original .NET\nversion.\n\nFor example, we might choose in the future to implement, say, git clone\n--scale=partial,cone to initialize a partial clone with a cone-sparse\ncheckout, that would not only be totally doable, and not only would we\nalready have precedent and data to prove that this actually makes engineers\nhappy who have to work on ginormous repositories, but we could then also\nimplement it by moving parts of contrib/scalar/ to builtin/ (where\ncontrib/scalar/ would then call the built-ins accordingly rather than\nhard-coding the defaults itself).\n\nWe now also have the opportunity to discuss the merits of Scalar's clone\ncaching, which is not actually part of this patch series because it is a bit\ncoupled with the GVFS parts of microsoft/git for the moment, where clones\nautomatically get registered with a populated alternate repository that is\nidentified by the URL, meaning: subsequent clones of the same repository are\nvastly faster than the first one because they do not actually download the\nalready-received objects again, they access the cache instead.\n\nAnother thing that I could imagine to be discussed at length is the\ndistinction between enlistment and worktree (where the latter is the actual\nGit worktree and usually lives in the src/ subdirectory of the former). This\nencourages untracked and ignored files to be placed outside the worktree,\nmaking Git's job much easier. This idea, too, might find its way in one way\nor another into Git proper.\n\nThese are just a few concepts in Scalar that do not yet have equivalents in\nGit. By putting this initial implementation into contrib/, we create a\nfoundation for future discussions of these concepts.\n\nWe plan on updating the recommended config settings in scalar register as\nnew Git features are available (such as builtin FSMonitor and sparse-index,\nwhen ready). To facilitate upgrading existing Scalar enlistments, their\npaths are automatically added to the [scalar] section of the global Git\nconfig, and the scalar reconfigure --all command will process all of them.\n\n\nEpilogue\n========\n\nNow, to address some questions that I imagine every reader has who made it\nthis far:\n\n * Why not put the Scalar functionality directly into a built-in? Creating a\n   Git builtin requires scrutiny over every aspect of the feature, which is\n   difficult to do while also maintaining the command-line interface\n   contract and expected behavior of the Scalar command (there are existing\n   users, after all). By having the Scalar command in contrib/, we present a\n   simple option for users to have these features in the short term while\n   the Git contributor community decides which bits to absorb into Git\n   built-ins.\n * Why implement the Scalar command in the Git codebase? We ported Scalar to\n   the microsoft/git fork for several reasons. First, we realized it was\n   possible now that the core features exist inside Git itself. Second,\n   compiling Scalar directly within a version of Git allows us to remove a\n   version compatibility check from each config option that might or might\n   not apply based on the installed Git version. Finally, this new location\n   has greatly simplified our release process and the installation process\n   for users. We now have ways to install Scalar with microsoft/git via\n   winget, brew, and apt-get. This has been the case since we shipped\n   v2.32.0 to our users, read: this setup has served us well already.\n * Why contribute Scalar to the Git project? We are biased, of course, yet\n   we do have evidence that the Scalar command is a helpful tool that offers\n   an simple way to handle huge repositories with ease. By contributing it\n   to the core Git project, we are able to share it with more users,\n   especially some users who do not want to install the microsoft/git fork.\n   We intend to include Scalar as a component in git-for-windows/git, but\n   are contributing it here first. Further, we think there is benefit to the\n   Git developer community as this presents an example of how to set certain\n   defaults that work for large repositories.\n * Does this integrate with the built-in FSMonitor yet? No, not yet. I do\n   have a couple of add-on patch series lined up, one of them being the\n   integration with the built-in FSMonitor, which obviously has to wait\n   until the FSMonitor patch series advances further.\n\nChanges since v2:\n\n * Adjusted the description of the list command in the manual page , as\n   suggested by Bagas.\n * Addressed two style nits in cmd_run().\n * The documentation of git reconfigure -a was improved.\n\nChanges since v1:\n\n * A couple typos were fixed\n * The code parsing the output of ls-remote was made more readable\n * The indentation used in scalar.txt now consistently uses tabs\n * We no longer hard-code core.bare = false when registering with Scalar\n\nDerrick Stolee (4):\n  scalar: 'register' sets recommended config and starts maintenance\n  scalar: 'unregister' stops background maintenance\n  scalar: implement 'scalar list'\n  scalar: implement the `run` command\n\nJohannes Schindelin (10):\n  scalar: create a rudimentary executable\n  scalar: start documenting the command\n  scalar: create test infrastructure\n  scalar: let 'unregister' handle a deleted enlistment directory\n    gracefully\n  scalar: implement the `clone` subcommand\n  scalar: teach 'clone' to support the --single-branch option\n  scalar: allow reconfiguring an existing enlistment\n  scalar: teach 'reconfigure' to optionally handle all registered\n    enlistments\n  scalar: implement the `version` command\n  scalar: accept -C and -c options before the subcommand\n\nMatthew John Cheetham (1):\n  scalar: implement the `delete` command\n\n Makefile                         |   8 +\n contrib/scalar/.gitignore        |   5 +\n contrib/scalar/Makefile          |  57 +++\n contrib/scalar/scalar.c          | 844 +++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        | 154 ++++++\n contrib/scalar/t/Makefile        |  78 +++\n contrib/scalar/t/t9099-scalar.sh |  88 ++++\n 7 files changed, 1234 insertions(+)\n create mode 100644 contrib/scalar/.gitignore\n create mode 100644 contrib/scalar/Makefile\n create mode 100644 contrib/scalar/scalar.c\n create mode 100644 contrib/scalar/scalar.txt\n create mode 100644 contrib/scalar/t/Makefile\n create mode 100755 contrib/scalar/t/t9099-scalar.sh\n\n\nbase-commit: ebf3c04b262aa27fbb97f8a0156c2347fecafafb\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1005%2Fdscho%2Fscalar-the-beginning-v3\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1005/dscho/scalar-the-beginning-v3\nPull-Request: https://github.com/gitgitgadget/git/pull/1005\n\nRange-diff vs v2:\n\n  1:  b8c7d3f8450 =  1:  b8c7d3f8450 scalar: create a rudimentary executable\n  2:  4f886575dcf =  2:  4f886575dcf scalar: start documenting the command\n  3:  bcfde9bc765 =  3:  bcfde9bc765 scalar: create test infrastructure\n  4:  ee3e26a0c4e =  4:  ee3e26a0c4e scalar: 'register' sets recommended config and starts maintenance\n  5:  6142f75875b =  5:  6142f75875b scalar: 'unregister' stops background maintenance\n  6:  82dd253154f =  6:  82dd253154f scalar: let 'unregister' handle a deleted enlistment directory gracefully\n  7:  fb7c931ddb3 !  7:  d291d3723a6 scalar: implement 'scalar list'\n     @@ contrib/scalar/scalar.txt: an existing Git worktree with Scalar whose name is no\n      +~~~~\n      +\n      +list::\n     -+\tTo see which repositories are currently registered by the service, run\n     -+\t`scalar list`. This subcommand does not need to be run inside a Scalar\n     -+\tenlistment.\n     ++\tList enlistments that are currently registered by Scalar. This\n     ++\tsubcommand does not need to be run inside an enlistment.\n      +\n       Register\n       ~~~~~~~~\n  8:  f3223c10788 !  8:  40dbf61771e scalar: implement the `clone` subcommand\n     @@ contrib/scalar/scalar.txt: an existing Git worktree with Scalar whose name is no\n       List\n       ~~~~\n       \n     - list::\n     - \tTo see which repositories are currently registered by the service, run\n     --\t`scalar list`. This subcommand does not need to be run inside a Scalar\n     --\tenlistment.\n     -+\t`scalar list`. This subcommand, like `clone`, does not need to be run\n     -+\tinside a Scalar enlistment.\n     - \n     - Register\n     - ~~~~~~~~\n      @@ contrib/scalar/scalar.txt: unregister [<enlistment>]::\n       \n       SEE ALSO\n  9:  b3c4b3dccc6 =  9:  414dbe7d859 scalar: teach 'clone' to support the --single-branch option\n 10:  b7fc2dc29c8 ! 10:  76de416a643 scalar: implement the `run` command\n     @@ contrib/scalar/scalar.c: static int cmd_register(int argc, const char **argv)\n      +\targc = parse_options(argc, argv, NULL, options,\n      +\t\t\t     usagestr, 0);\n      +\n     -+\tif (argc == 0)\n     ++\tif (!argc)\n      +\t\tusage_with_options(usagestr, options);\n      +\n     -+\tif (!strcmp(\"all\", argv[0]))\n     ++\tif (!strcmp(\"all\", argv[0])) {\n      +\t\ti = -1;\n     -+\telse {\n     ++\t} else {\n      +\t\tfor (i = 0; tasks[i].arg && strcmp(tasks[i].arg, argv[0]); i++)\n      +\t\t\t; /* keep looking for the task */\n      +\n 11:  9a834c23d08 = 11:  655a902b9df scalar: allow reconfiguring an existing enlistment\n 12:  79e9f5d203a ! 12:  2d1987bfcda scalar: teach 'reconfigure' to optionally handle all registered enlistments\n     @@ contrib/scalar/scalar.txt: After a Scalar upgrade, or when the configuration of\n       reconfigure the enlistment.\n       \n      +With the `--all` option, all enlistments currently registered with Scalar\n     -+will be reconfigured. This option is meant to to be run every time after\n     -+Scalar is upgraded.\n     ++will be reconfigured. Use this option after each Scalar upgrade.\n      +\n       SEE ALSO\n       --------\n 13:  94a21982652 ! 13:  c67938299ee scalar: implement the `delete` command\n     @@ contrib/scalar/scalar.txt: scalar register [<enlistment>]\n       \n       DESCRIPTION\n       -----------\n     -@@ contrib/scalar/scalar.txt: With the `--all` option, all enlistments currently registered with Scalar\n     - will be reconfigured. This option is meant to to be run every time after\n     - Scalar is upgraded.\n     +@@ contrib/scalar/scalar.txt: reconfigure the enlistment.\n     + With the `--all` option, all enlistments currently registered with Scalar\n     + will be reconfigured. Use this option after each Scalar upgrade.\n       \n      +Delete\n      +~~~~~~\n 14:  707d8e19683 = 14:  d2cd2b7094b scalar: implement the `version` command\n 15:  26e23b5c5e5 = 15:  7ccc4f8b9b0 scalar: accept -C and -c options before the subcommand\n\n-- \ngitgitgadget\n"},{"id":"435152","messageId":"b8c7d3f84508ae0fb300f47c726764f4cbf46be9.1631129086.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v3.git.1631129086.gitgitgadget@gmail.com","subject":"[PATCH v3 01/15] scalar: create a rudimentary executable","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-08T19:24:31Z","receivedAt":"2021-09-08T19:24:51Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe idea of Scalar (https://github.com/microsoft/scalar), and before\nthat, of VFS for Git, has always been to prove that Git _can_ scale, and\nto upstream whatever strategies have been demonstrated to help.\n\nWith this patch, we start the journey from that C# project to move what\nis left to Git's own `contrib/` directory, reimplementing it in pure C,\nwith the intention to facilitate integrating the functionality into core\nGit all while maintaining backwards-compatibility for existing Scalar\nusers (which will be much easier when both live in the same worktree).\nIt was always to plan to contribute all of the proven strategies back to\ncore Git.\n\nFor example, while the virtual filesystem provided by VFS for Git helped\nthe team developing the Windows operating system to move onto Git, while\ntrying to upstream it we realized that it cannot be done: getting the\nvirtual filesystem to work (which we only managed to implement fully on\nWindows, but not on, say, macOS or Linux), and the required server-side\nsupport for the GVFS protocol, made this not quite feasible.\n\nThe Scalar project learned from that and tackled the problem with\ndifferent tactics: instead of pretending to Git that the working\ndirectory is fully populated, it _specifically_ teaches Git about\npartial clone (which is based on VFS for Git's cache server), about\nsparse checkout (which VFS for Git tried to do transparently, in the\nfile system layer), and regularly runs maintenance tasks to keep the\nrepository in a healthy state.\n\nWith partial clone, sparse checkout and `git maintenance` having been\nupstreamed, there is little left that `scalar.exe` does that which\n`git.exe` cannot do. One such thing is that `scalar clone <url>` will\nautomatically set up a partial, sparse clone, and configure\nknown-helpful settings from the start.\n\nSo let's bring this convenience into Git's tree.\n\nThe idea here is that you can (optionally) build Scalar via\n\n\tmake -C contrib/scalar/Makefile\n\nThis will build the `scalar` executable and put it into the\ncontrib/scalar/ subdirectory.\n\nThe slightly awkward addition of the `contrib/scalar/*` bits to the\ntop-level `Makefile` are actually really required: we want to link to\n`libgit.a`, which means that we will need to use the very same `CFLAGS`\nand `LDFLAGS` as the rest of Git.\n\nAn early development version of this patch tried to replicate all the\nconditional code in `contrib/scalar/Makefile` (e.g. `NO_POLL`) just like\n`contrib/svn-fe/Makefile` used to do before it was retired. It turned\nout to be quite the whack-a-mole game: the SHA-1-related flags, the\nflags enabling/disabling `compat/poll/`, `compat/regex/`,\n`compat/win32mmap.c` & friends depending on the current platform... To\nput it mildly: it was a major mess.\n\nInstead, this patch makes minimal changes to the top-level `Makefile` so\nthat the bits in `contrib/scalar/` can be compiled and linked, and\nadds a `contrib/scalar/Makefile` that uses the top-level `Makefile` in a\nmost minimal way to do the actual compiling.\n\nNote: With this commit, we only establish the infrastructure, no\nScalar functionality is implemented yet; We will do that incrementally\nover the next few commits.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Makefile                  |  8 ++++++++\n contrib/scalar/.gitignore |  2 ++\n contrib/scalar/Makefile   | 34 ++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.c   | 36 ++++++++++++++++++++++++++++++++++++\n 4 files changed, 80 insertions(+)\n create mode 100644 contrib/scalar/.gitignore\n create mode 100644 contrib/scalar/Makefile\n create mode 100644 contrib/scalar/scalar.c\n\ndiff --git a/Makefile b/Makefile\nindex c3565fc0f8f..2d5c822f7a8 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2447,6 +2447,10 @@ endif\n .PHONY: objects\n objects: $(OBJECTS)\n \n+SCALAR_SOURCES := contrib/scalar/scalar.c\n+SCALAR_OBJECTS := $(SCALAR_SOURCES:c=o)\n+OBJECTS += $(SCALAR_OBJECTS)\n+\n dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d)\n dep_dirs := $(addsuffix .depend,$(sort $(dir $(OBJECTS))))\n \n@@ -2586,6 +2590,10 @@ $(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n \t\t$(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)\n \n+contrib/scalar/scalar$X: $(SCALAR_OBJECTS) GIT-LDFLAGS $(GITLIBS)\n+\t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \\\n+\t\t$(filter %.o,$^) $(LIBS)\n+\n $(LIB_FILE): $(LIB_OBJS)\n \t$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^\n \ndiff --git a/contrib/scalar/.gitignore b/contrib/scalar/.gitignore\nnew file mode 100644\nindex 00000000000..ff3d47e84d0\n--- /dev/null\n+++ b/contrib/scalar/.gitignore\n@@ -0,0 +1,2 @@\n+/*.exe\n+/scalar\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nnew file mode 100644\nindex 00000000000..40c03ad10e1\n--- /dev/null\n+++ b/contrib/scalar/Makefile\n@@ -0,0 +1,34 @@\n+QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir\n+QUIET_SUBDIR1  =\n+\n+ifneq ($(findstring s,$(MAKEFLAGS)),s)\n+ifndef V\n+\tQUIET_SUBDIR0  = +@subdir=\n+\tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n+\t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n+else\n+\texport V\n+endif\n+endif\n+\n+all:\n+\n+include ../../config.mak.uname\n+-include ../../config.mak.autogen\n+-include ../../config.mak\n+\n+TARGETS = scalar$(X) scalar.o\n+GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n+\n+all: scalar$X\n+\n+$(GITLIBS):\n+\t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)\n+\n+$(TARGETS): $(GITLIBS) scalar.c\n+\t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)\n+\n+clean:\n+\t$(RM) $(TARGETS)\n+\n+.PHONY: all clean FORCE\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nnew file mode 100644\nindex 00000000000..7cff29e0fcd\n--- /dev/null\n+++ b/contrib/scalar/scalar.c\n@@ -0,0 +1,36 @@\n+/*\n+ * The Scalar command-line interface.\n+ */\n+\n+#include \"cache.h\"\n+#include \"gettext.h\"\n+#include \"parse-options.h\"\n+\n+static struct {\n+\tconst char *name;\n+\tint (*fn)(int, const char **);\n+} builtins[] = {\n+\t{ NULL, NULL},\n+};\n+\n+int cmd_main(int argc, const char **argv)\n+{\n+\tstruct strbuf scalar_usage = STRBUF_INIT;\n+\tint i;\n+\n+\tif (argc > 1) {\n+\t\targv++;\n+\t\targc--;\n+\n+\t\tfor (i = 0; builtins[i].name; i++)\n+\t\t\tif (!strcmp(builtins[i].name, argv[0]))\n+\t\t\t\treturn !!builtins[i].fn(argc, argv);\n+\t}\n+\n+\tstrbuf_addstr(&scalar_usage,\n+\t\t      N_(\"scalar <command> [<options>]\\n\\nCommands:\\n\"));\n+\tfor (i = 0; builtins[i].name; i++)\n+\t\tstrbuf_addf(&scalar_usage, \"\\t%s\\n\", builtins[i].name);\n+\n+\tusage(scalar_usage.buf);\n+}\n-- \ngitgitgadget\n\n"},{"id":"435153","messageId":"4f886575dcfb97d5bbd04fe7551e03935ac0af69.1631129086.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v3.git.1631129086.gitgitgadget@gmail.com","subject":"[PATCH v3 02/15] scalar: start documenting the command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-08T19:24:32Z","receivedAt":"2021-09-08T19:24:53Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis commit establishes the infrastructure to build the manual page for\nthe `scalar` command.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/.gitignore |  3 +++\n contrib/scalar/Makefile   | 14 +++++++++++++-\n contrib/scalar/scalar.txt | 38 ++++++++++++++++++++++++++++++++++++++\n 3 files changed, 54 insertions(+), 1 deletion(-)\n create mode 100644 contrib/scalar/scalar.txt\n\ndiff --git a/contrib/scalar/.gitignore b/contrib/scalar/.gitignore\nindex ff3d47e84d0..00441073f59 100644\n--- a/contrib/scalar/.gitignore\n+++ b/contrib/scalar/.gitignore\n@@ -1,2 +1,5 @@\n+/*.xml\n+/*.1\n+/*.html\n /*.exe\n /scalar\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nindex 40c03ad10e1..85c186634e9 100644\n--- a/contrib/scalar/Makefile\n+++ b/contrib/scalar/Makefile\n@@ -6,6 +6,7 @@ ifndef V\n \tQUIET_SUBDIR0  = +@subdir=\n \tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n \t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n+\tQUIET          = @\n else\n \texport V\n endif\n@@ -30,5 +31,16 @@ $(TARGETS): $(GITLIBS) scalar.c\n \n clean:\n \t$(RM) $(TARGETS)\n+\t$(RM) scalar.1 scalar.html scalar.xml\n \n-.PHONY: all clean FORCE\n+docs: scalar.html scalar.1\n+\n+scalar.html: | scalar.1 # prevent them from trying to build `doc.dep` in parallel\n+\n+scalar.html scalar.1: scalar.txt\n+\t$(QUIET_SUBDIR0)../../Documentation$(QUIET_SUBDIR1) \\\n+\t\tMAN_TXT=../contrib/scalar/scalar.txt \\\n+\t\t../contrib/scalar/$@\n+\t$(QUIET)test scalar.1 != \"$@\" || mv ../../Documentation/$@ .\n+\n+.PHONY: all clean docs FORCE\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nnew file mode 100644\nindex 00000000000..5f7131861a5\n--- /dev/null\n+++ b/contrib/scalar/scalar.txt\n@@ -0,0 +1,38 @@\n+scalar(1)\n+=========\n+\n+NAME\n+----\n+scalar - an opinionated repository management tool\n+\n+SYNOPSIS\n+--------\n+[verse]\n+scalar <command> [<options>]\n+\n+DESCRIPTION\n+-----------\n+\n+Scalar is an opinionated repository management tool. By creating new\n+repositories or registering existing repositories with Scalar, your Git\n+experience will speed up. Scalar sets advanced Git config settings,\n+maintains your repositories in the background, and helps reduce data sent\n+across the network.\n+\n+An important Scalar concept is the enlistment: this is the top-level directory\n+of the project. It usually contains the subdirectory `src/` which is a Git\n+worktree. This encourages the separation between tracked files (inside `src/`)\n+and untracked files, such as build artifacts (outside `src/`). When registering\n+an existing Git worktree with Scalar whose name is not `src`, the enlistment\n+will be identical to the worktree.\n+\n+The `scalar` command implements various subcommands, and different options\n+depending on the subcommand.\n+\n+SEE ALSO\n+--------\n+linkgit:git-maintenance[1].\n+\n+Scalar\n+---\n+Associated with the linkgit:git[1] suite\n-- \ngitgitgadget\n\n"},{"id":"435154","messageId":"bcfde9bc7651266deaa0e5036cebd058dffc12a7.1631129086.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v3.git.1631129086.gitgitgadget@gmail.com","subject":"[PATCH v3 03/15] scalar: create test infrastructure","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-08T19:24:33Z","receivedAt":"2021-09-08T19:24:57Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nTo test the Scalar command, create a test script in contrib/scalar/t\nthat is executed as `make -C contrib/scalar test`. Since Scalar has no\nmeaningful capabilities yet, the only test is rather simple. We will add\nmore tests in subsequent commits that introduce corresponding, new\nfunctionality.\n\nNote: this test script is intended to test `scalar` only lightly, even\nafter all of the functionality is implemented.\n\nA more comprehensive functional (or: integration) test suite can be\nfound at https://github.com/microsoft/scalar; It is used in the workflow\nhttps://github.com/microsoft/git/blob/HEAD/.github/workflows/scalar-functional-tests.yml\nin Microsoft's Git fork. This test suite performs end-to-end tests with\na real remote repository, and is run as part of the regular CI builds.\nSince those tests require some functionality supported only by\nMicrosoft's Git fork (\"GVFS protocol\"), there is no intention to port\nthat fuller test suite to `contrib/scalar/`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/Makefile          | 17 +++++--\n contrib/scalar/t/Makefile        | 78 ++++++++++++++++++++++++++++++++\n contrib/scalar/t/t9099-scalar.sh | 17 +++++++\n 3 files changed, 109 insertions(+), 3 deletions(-)\n create mode 100644 contrib/scalar/t/Makefile\n create mode 100755 contrib/scalar/t/t9099-scalar.sh\n\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nindex 85c186634e9..8620042f281 100644\n--- a/contrib/scalar/Makefile\n+++ b/contrib/scalar/Makefile\n@@ -3,6 +3,7 @@ QUIET_SUBDIR1  =\n \n ifneq ($(findstring s,$(MAKEFLAGS)),s)\n ifndef V\n+\tQUIET_GEN      = @echo '   ' GEN $@;\n \tQUIET_SUBDIR0  = +@subdir=\n \tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n \t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n@@ -21,7 +22,7 @@ include ../../config.mak.uname\n TARGETS = scalar$(X) scalar.o\n GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n \n-all: scalar$X\n+all: scalar$X ../../bin-wrappers/scalar\n \n $(GITLIBS):\n \t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)\n@@ -30,9 +31,19 @@ $(TARGETS): $(GITLIBS) scalar.c\n \t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)\n \n clean:\n-\t$(RM) $(TARGETS)\n+\t$(RM) $(TARGETS) ../../bin-wrappers/scalar\n \t$(RM) scalar.1 scalar.html scalar.xml\n \n+../../bin-wrappers/scalar: ../../wrap-for-bin.sh Makefile\n+\t@mkdir -p ../../bin-wrappers\n+\t$(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \\\n+\t     -e 's|@@BUILD_DIR@@|$(shell cd ../.. && pwd)|' \\\n+\t     -e 's|@@PROG@@|contrib/scalar/scalar$(X)|' < $< > $@ && \\\n+\tchmod +x $@\n+\n+test: all\n+\t$(MAKE) -C t\n+\n docs: scalar.html scalar.1\n \n scalar.html: | scalar.1 # prevent them from trying to build `doc.dep` in parallel\n@@ -43,4 +54,4 @@ scalar.html scalar.1: scalar.txt\n \t\t../contrib/scalar/$@\n \t$(QUIET)test scalar.1 != \"$@\" || mv ../../Documentation/$@ .\n \n-.PHONY: all clean docs FORCE\n+.PHONY: all clean docs test FORCE\ndiff --git a/contrib/scalar/t/Makefile b/contrib/scalar/t/Makefile\nnew file mode 100644\nindex 00000000000..6170672bb37\n--- /dev/null\n+++ b/contrib/scalar/t/Makefile\n@@ -0,0 +1,78 @@\n+# Run scalar tests\n+#\n+# Copyright (c) 2005,2021 Junio C Hamano, Johannes Schindelin\n+#\n+\n+-include ../../../config.mak.autogen\n+-include ../../../config.mak\n+\n+SHELL_PATH ?= $(SHELL)\n+PERL_PATH ?= /usr/bin/perl\n+RM ?= rm -f\n+PROVE ?= prove\n+DEFAULT_TEST_TARGET ?= test\n+TEST_LINT ?= test-lint\n+\n+ifdef TEST_OUTPUT_DIRECTORY\n+TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results\n+else\n+TEST_RESULTS_DIRECTORY = ../../../t/test-results\n+endif\n+\n+# Shell quote;\n+SHELL_PATH_SQ = $(subst ','\\'',$(SHELL_PATH))\n+PERL_PATH_SQ = $(subst ','\\'',$(PERL_PATH))\n+TEST_RESULTS_DIRECTORY_SQ = $(subst ','\\'',$(TEST_RESULTS_DIRECTORY))\n+\n+T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))\n+\n+all: $(DEFAULT_TEST_TARGET)\n+\n+test: $(TEST_LINT)\n+\t$(MAKE) aggregate-results-and-cleanup\n+\n+prove: $(TEST_LINT)\n+\t@echo \"*** prove ***\"; GIT_CONFIG=.git/config $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)\n+\t$(MAKE) clean-except-prove-cache\n+\n+$(T):\n+\t@echo \"*** $@ ***\"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)\n+\n+clean-except-prove-cache:\n+\t$(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)'\n+\t$(RM) -r valgrind/bin\n+\n+clean: clean-except-prove-cache\n+\t$(RM) .prove\n+\n+test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax\n+\n+test-lint-duplicates:\n+\t@dups=`echo $(T) | tr ' ' '\\n' | sed 's/-.*//' | sort | uniq -d` && \\\n+\t\ttest -z \"$$dups\" || { \\\n+\t\techo >&2 \"duplicate test numbers:\" $$dups; exit 1; }\n+\n+test-lint-executable:\n+\t@bad=`for i in $(T); do test -x \"$$i\" || echo $$i; done` && \\\n+\t\ttest -z \"$$bad\" || { \\\n+\t\techo >&2 \"non-executable tests:\" $$bad; exit 1; }\n+\n+test-lint-shell-syntax:\n+\t@'$(PERL_PATH_SQ)' ../../../t/check-non-portable-shell.pl $(T)\n+\n+aggregate-results-and-cleanup: $(T)\n+\t$(MAKE) aggregate-results\n+\t$(MAKE) clean\n+\n+aggregate-results:\n+\tfor f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \\\n+\t\techo \"$$f\"; \\\n+\tdone | '$(SHELL_PATH_SQ)' ../../../t/aggregate-results.sh\n+\n+valgrind:\n+\t$(MAKE) GIT_TEST_OPTS=\"$(GIT_TEST_OPTS) --valgrind\"\n+\n+test-results:\n+\tmkdir -p test-results\n+\n+.PHONY: $(T) aggregate-results clean valgrind\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nnew file mode 100755\nindex 00000000000..16f2b72b126\n--- /dev/null\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -0,0 +1,17 @@\n+#!/bin/sh\n+\n+test_description='test the `scalar` command'\n+\n+TEST_DIRECTORY=$PWD/../../../t\n+export TEST_DIRECTORY\n+\n+# Make it work with --no-bin-wrappers\n+PATH=$PWD/..:$PATH\n+\n+. ../../../t/test-lib.sh\n+\n+test_expect_success 'scalar shows a usage' '\n+\ttest_expect_code 129 scalar -h\n+'\n+\n+test_done\n-- \ngitgitgadget\n\n"},{"id":"435155","messageId":"ee3e26a0c4e8fd9ecad1ff71f86423c2685adec4.1631129086.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v3.git.1631129086.gitgitgadget@gmail.com","subject":"[PATCH v3 04/15] scalar: 'register' sets recommended config and starts maintenance","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-08T19:24:34Z","receivedAt":"2021-09-08T19:24:57Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nLet's start implementing the `register` command. With this commit,\nrecommended settings are configured upon `scalar register`, and Git's\nbackground maintenance is started.\n\nThe recommended config settings may very well change in the future. For\nexample, once the built-in FSMonitor is available, we will want to\nenable it upon `scalar register`. For that reason, we explicitly support\nrunning `scalar register` in an already-registered enlistment.\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 255 ++++++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt |  18 ++-\n 2 files changed, 272 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 7cff29e0fcd..0e627bb100e 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -5,11 +5,266 @@\n #include \"cache.h\"\n #include \"gettext.h\"\n #include \"parse-options.h\"\n+#include \"config.h\"\n+#include \"run-command.h\"\n+\n+/*\n+ * Remove the deepest subdirectory in the provided path string. Path must not\n+ * include a trailing path separator. Returns 1 if parent directory found,\n+ * otherwise 0.\n+ */\n+static int strbuf_parent_directory(struct strbuf *buf)\n+{\n+\tsize_t len = buf->len;\n+\tsize_t offset = offset_1st_component(buf->buf);\n+\tchar *path_sep = find_last_dir_sep(buf->buf + offset);\n+\tstrbuf_setlen(buf, path_sep ? path_sep - buf->buf : offset);\n+\n+\treturn buf->len < len;\n+}\n+\n+static void setup_enlistment_directory(int argc, const char **argv,\n+\t\t\t\t       const char * const *usagestr,\n+\t\t\t\t       const struct option *options,\n+\t\t\t\t       struct strbuf *enlistment_root)\n+{\n+\tstruct strbuf path = STRBUF_INIT;\n+\tchar *root;\n+\tint enlistment_found = 0;\n+\n+\tif (startup_info->have_repository)\n+\t\tBUG(\"gitdir already set up?!?\");\n+\n+\tif (argc > 1)\n+\t\tusage_with_options(usagestr, options);\n+\n+\t/* find the worktree, determine its corresponding root */\n+\tif (argc == 1)\n+\t\tstrbuf_add_absolute_path(&path, argv[0]);\n+\telse if (strbuf_getcwd(&path) < 0)\n+\t\tdie(_(\"need a working directory\"));\n+\n+\tstrbuf_trim_trailing_dir_sep(&path);\n+\tdo {\n+\t\tconst size_t len = path.len;\n+\n+\t\t/* check if currently in enlistment root with src/ workdir */\n+\t\tstrbuf_addstr(&path, \"/src/.git\");\n+\t\tif (is_git_directory(path.buf)) {\n+\t\t\tstrbuf_strip_suffix(&path, \"/.git\");\n+\n+\t\t\tif (enlistment_root)\n+\t\t\t\tstrbuf_add(enlistment_root, path.buf, len);\n+\n+\t\t\tenlistment_found = 1;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\t/* reset to original path */\n+\t\tstrbuf_setlen(&path, len);\n+\n+\t\t/* check if currently in workdir */\n+\t\tstrbuf_addstr(&path, \"/.git\");\n+\t\tif (is_git_directory(path.buf)) {\n+\t\t\tstrbuf_setlen(&path, len);\n+\n+\t\t\tif (enlistment_root) {\n+\t\t\t\t/*\n+\t\t\t\t * If the worktree's directory's name is `src`, the enlistment is the\n+\t\t\t\t * parent directory, otherwise it is identical to the worktree.\n+\t\t\t\t */\n+\t\t\t\troot = strip_path_suffix(path.buf, \"src\");\n+\t\t\t\tstrbuf_addstr(enlistment_root, root ? root : path.buf);\n+\t\t\t\tfree(root);\n+\t\t\t}\n+\n+\t\t\tenlistment_found = 1;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tstrbuf_setlen(&path, len);\n+\t} while (strbuf_parent_directory(&path));\n+\n+\tif (!enlistment_found)\n+\t\tdie(_(\"could not find enlistment root\"));\n+\n+\tif (chdir(path.buf) < 0)\n+\t\tdie_errno(_(\"could not switch to '%s'\"), path.buf);\n+\n+\tstrbuf_release(&path);\n+\tsetup_git_directory();\n+}\n+\n+static int run_git(const char *arg, ...)\n+{\n+\tstruct strvec argv = STRVEC_INIT;\n+\tva_list args;\n+\tconst char *p;\n+\tint res;\n+\n+\tva_start(args, arg);\n+\tstrvec_push(&argv, arg);\n+\twhile ((p = va_arg(args, const char *)))\n+\t\tstrvec_push(&argv, p);\n+\tva_end(args);\n+\n+\tres = run_command_v_opt(argv.v, RUN_GIT_CMD);\n+\n+\tstrvec_clear(&argv);\n+\treturn res;\n+}\n+\n+static int set_recommended_config(void)\n+{\n+\tstruct {\n+\t\tconst char *key;\n+\t\tconst char *value;\n+\t} config[] = {\n+\t\t{ \"am.keepCR\", \"true\" },\n+\t\t{ \"core.FSCache\", \"true\" },\n+\t\t{ \"core.multiPackIndex\", \"true\" },\n+\t\t{ \"core.preloadIndex\", \"true\" },\n+#ifndef WIN32\n+\t\t{ \"core.untrackedCache\", \"true\" },\n+#else\n+\t\t/*\n+\t\t * Unfortunately, Scalar's Functional Tests demonstrated\n+\t\t * that the untracked cache feature is unreliable on Windows\n+\t\t * (which is a bummer because that platform would benefit the\n+\t\t * most from it). For some reason, freshly created files seem\n+\t\t * not to update the directory's `lastModified` time\n+\t\t * immediately, but the untracked cache would need to rely on\n+\t\t * that.\n+\t\t *\n+\t\t * Therefore, with a sad heart, we disable this very useful\n+\t\t * feature on Windows.\n+\t\t */\n+\t\t{ \"core.untrackedCache\", \"false\" },\n+#endif\n+\t\t{ \"core.logAllRefUpdates\", \"true\" },\n+\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n+\t\t{ \"credential.validate\", \"false\" }, /* GCM4W-only */\n+\t\t{ \"gc.auto\", \"0\" },\n+\t\t{ \"gui.GCWarning\", \"false\" },\n+\t\t{ \"index.threads\", \"true\" },\n+\t\t{ \"index.version\", \"4\" },\n+\t\t{ \"merge.stat\", \"false\" },\n+\t\t{ \"merge.renames\", \"false\" },\n+\t\t{ \"pack.useBitmaps\", \"false\" },\n+\t\t{ \"pack.useSparse\", \"true\" },\n+\t\t{ \"receive.autoGC\", \"false\" },\n+\t\t{ \"reset.quiet\", \"true\" },\n+\t\t{ \"feature.manyFiles\", \"false\" },\n+\t\t{ \"feature.experimental\", \"false\" },\n+\t\t{ \"fetch.unpackLimit\", \"1\" },\n+\t\t{ \"fetch.writeCommitGraph\", \"false\" },\n+#ifdef WIN32\n+\t\t{ \"http.sslBackend\", \"schannel\" },\n+#endif\n+\t\t{ \"status.aheadBehind\", \"false\" },\n+\t\t{ \"commitGraph.generationVersion\", \"1\" },\n+\t\t{ \"core.autoCRLF\", \"false\" },\n+\t\t{ \"core.safeCRLF\", \"false\" },\n+\t\t{ NULL, NULL },\n+\t};\n+\tint i;\n+\tchar *value;\n+\n+\tfor (i = 0; config[i].key; i++) {\n+\t\tif (git_config_get_string(config[i].key, &value)) {\n+\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"created\");\n+\t\t\tif (git_config_set_gently(config[i].key,\n+\t\t\t\t\t\t  config[i].value) < 0)\n+\t\t\t\treturn error(_(\"could not configure %s=%s\"),\n+\t\t\t\t\t     config[i].key, config[i].value);\n+\t\t} else {\n+\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"exists\");\n+\t\t\tfree(value);\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * The `log.excludeDecoration` setting is special because it allows\n+\t * for multiple values.\n+\t */\n+\tif (git_config_get_string(\"log.excludeDecoration\", &value)) {\n+\t\ttrace2_data_string(\"scalar\", the_repository,\n+\t\t\t\t   \"log.excludeDecoration\", \"created\");\n+\t\tif (git_config_set_multivar_gently(\"log.excludeDecoration\",\n+\t\t\t\t\t\t   \"refs/prefetch/*\",\n+\t\t\t\t\t\t   CONFIG_REGEX_NONE, 0))\n+\t\t\treturn error(_(\"could not configure \"\n+\t\t\t\t       \"log.excludeDecoration\"));\n+\t} else {\n+\t\ttrace2_data_string(\"scalar\", the_repository,\n+\t\t\t\t   \"log.excludeDecoration\", \"exists\");\n+\t\tfree(value);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int start_maintenance(void)\n+{\n+\treturn run_git(\"maintenance\", \"start\", NULL);\n+}\n+\n+static int add_enlistment(void)\n+{\n+\tint res;\n+\n+\tif (!the_repository->worktree)\n+\t\tdie(_(\"Scalar enlistments require a worktree\"));\n+\n+\tres = run_git(\"config\", \"--global\", \"--get\", \"--fixed-value\",\n+\t\t      \"scalar.repo\", the_repository->worktree, NULL);\n+\n+\t/*\n+\t * If the setting is already there, then do nothing.\n+\t */\n+\tif (!res)\n+\t\treturn 0;\n+\n+\treturn run_git(\"config\", \"--global\", \"--add\",\n+\t\t       \"scalar.repo\", the_repository->worktree, NULL);\n+}\n+\n+static int register_dir(void)\n+{\n+\tint res = add_enlistment();\n+\n+\tif (!res)\n+\t\tres = set_recommended_config();\n+\n+\tif (!res)\n+\t\tres = start_maintenance();\n+\n+\treturn res;\n+}\n+\n+static int cmd_register(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar register [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn register_dir();\n+}\n \n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"register\", cmd_register },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 5f7131861a5..568987064b2 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,7 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n-scalar <command> [<options>]\n+scalar register [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -29,6 +29,22 @@ will be identical to the worktree.\n The `scalar` command implements various subcommands, and different options\n depending on the subcommand.\n \n+COMMANDS\n+--------\n+\n+Register\n+~~~~~~~~\n+\n+register [<enlistment>]::\n+\tAdds the enlistment's repository to the list of registered repositories\n+\tand starts background maintenance. If `<enlistment>` is not provided,\n+\tthen the enlistment associated with the current working directory is\n+\tregistered.\n++\n+Note: when this subcommand is called in a worktree that is called `src/`, its\n+parent directory is considered to be the Scalar enlistment. If the worktree is\n+_not_ called `src/`, it itself will be considered to be the Scalar enlistment.\n+\n SEE ALSO\n --------\n linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"435156","messageId":"6142f75875b8245c93230e141be3664253e2137b.1631129086.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v3.git.1631129086.gitgitgadget@gmail.com","subject":"[PATCH v3 05/15] scalar: 'unregister' stops background maintenance","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-08T19:24:35Z","receivedAt":"2021-09-08T19:25:00Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nJust like `scalar register` starts the scheduled background maintenance,\n`scalar unregister` stops it. Note that we use `git maintenance start`\nin `scalar register`, but we do not use `git maintenance stop` in\n`scalar unregister`: this would stop maintenance for _all_ repositories,\nnot just for the one we want to unregister.\n\nThe `unregister` command also removes the corresponding entry from the\n`[scalar]` section in the global Git config.\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 50 ++++++++++++++++++++++++++++++++-------\n contrib/scalar/scalar.txt |  8 +++++++\n 2 files changed, 50 insertions(+), 8 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 0e627bb100e..2b5c52a25f5 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -204,12 +204,12 @@ static int set_recommended_config(void)\n \treturn 0;\n }\n \n-static int start_maintenance(void)\n+static int toggle_maintenance(int enable)\n {\n-\treturn run_git(\"maintenance\", \"start\", NULL);\n+\treturn run_git(\"maintenance\", enable ? \"start\" : \"unregister\", NULL);\n }\n \n-static int add_enlistment(void)\n+static int add_or_remove_enlistment(int add)\n {\n \tint res;\n \n@@ -220,24 +220,39 @@ static int add_enlistment(void)\n \t\t      \"scalar.repo\", the_repository->worktree, NULL);\n \n \t/*\n-\t * If the setting is already there, then do nothing.\n+\t * If we want to add and the setting is already there, then do nothing.\n+\t * If we want to remove and the setting is not there, then do nothing.\n \t */\n-\tif (!res)\n+\tif ((add && !res) || (!add && res))\n \t\treturn 0;\n \n-\treturn run_git(\"config\", \"--global\", \"--add\",\n+\treturn run_git(\"config\", \"--global\", add ? \"--add\" : \"--unset\",\n+\t\t       add ? \"--no-fixed-value\" : \"--fixed-value\",\n \t\t       \"scalar.repo\", the_repository->worktree, NULL);\n }\n \n static int register_dir(void)\n {\n-\tint res = add_enlistment();\n+\tint res = add_or_remove_enlistment(1);\n \n \tif (!res)\n \t\tres = set_recommended_config();\n \n \tif (!res)\n-\t\tres = start_maintenance();\n+\t\tres = toggle_maintenance(1);\n+\n+\treturn res;\n+}\n+\n+static int unregister_dir(void)\n+{\n+\tint res = 0;\n+\n+\tif (toggle_maintenance(0) < 0)\n+\t\tres = -1;\n+\n+\tif (add_or_remove_enlistment(0) < 0)\n+\t\tres = -1;\n \n \treturn res;\n }\n@@ -260,11 +275,30 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_unregister(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar unregister [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn unregister_dir();\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n \t{ \"register\", cmd_register },\n+\t{ \"unregister\", cmd_unregister },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 568987064b2..d9a79984492 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -9,6 +9,7 @@ SYNOPSIS\n --------\n [verse]\n scalar register [<enlistment>]\n+scalar unregister [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -45,6 +46,13 @@ Note: when this subcommand is called in a worktree that is called `src/`, its\n parent directory is considered to be the Scalar enlistment. If the worktree is\n _not_ called `src/`, it itself will be considered to be the Scalar enlistment.\n \n+Unregister\n+~~~~~~~~~~\n+\n+unregister [<enlistment>]::\n+\tRemove the specified repository from the list of repositories\n+\tregistered with Scalar and stop the scheduled background maintenance.\n+\n SEE ALSO\n --------\n linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"435157","messageId":"82dd253154fb8dabd6f344df2196d8e6261d09a5.1631129086.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v3.git.1631129086.gitgitgadget@gmail.com","subject":"[PATCH v3 06/15] scalar: let 'unregister' handle a deleted enlistment directory gracefully","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-08T19:24:36Z","receivedAt":"2021-09-08T19:25:04Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nWhen a user deleted an enlistment manually, let's be generous and\n_still_ unregister it.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 46 ++++++++++++++++++++++++++++++++\n contrib/scalar/t/t9099-scalar.sh | 15 +++++++++++\n 2 files changed, 61 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 2b5c52a25f5..d114c038b64 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -275,6 +275,24 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int remove_deleted_enlistment(struct strbuf *path)\n+{\n+\tint res = 0;\n+\tstrbuf_realpath_forgiving(path, path->buf, 1);\n+\n+\tif (run_git(\"config\", \"--global\",\n+\t\t    \"--unset\", \"--fixed-value\",\n+\t\t    \"scalar.repo\", path->buf, NULL) < 0)\n+\t\tres = -1;\n+\n+\tif (run_git(\"config\", \"--global\",\n+\t\t    \"--unset\", \"--fixed-value\",\n+\t\t    \"maintenance.repo\", path->buf, NULL) < 0)\n+\t\tres = -1;\n+\n+\treturn res;\n+}\n+\n static int cmd_unregister(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -288,6 +306,34 @@ static int cmd_unregister(int argc, const char **argv)\n \targc = parse_options(argc, argv, NULL, options,\n \t\t\t     usage, 0);\n \n+\t/*\n+\t * Be forgiving when the enlistment or worktree does not even exist any\n+\t * longer; This can be the case if a user deleted the worktree by\n+\t * mistake and _still_ wants to unregister the thing.\n+\t */\n+\tif (argc == 1) {\n+\t\tstruct strbuf src_path = STRBUF_INIT, workdir_path = STRBUF_INIT;\n+\n+\t\tstrbuf_addf(&src_path, \"%s/src/.git\", argv[0]);\n+\t\tstrbuf_addf(&workdir_path, \"%s/.git\", argv[0]);\n+\t\tif (!is_directory(src_path.buf) && !is_directory(workdir_path.buf)) {\n+\t\t\t/* remove possible matching registrations */\n+\t\t\tint res = -1;\n+\n+\t\t\tstrbuf_strip_suffix(&src_path, \"/.git\");\n+\t\t\tres = remove_deleted_enlistment(&src_path) && res;\n+\n+\t\t\tstrbuf_strip_suffix(&workdir_path, \"/.git\");\n+\t\t\tres = remove_deleted_enlistment(&workdir_path) && res;\n+\n+\t\t\tstrbuf_release(&src_path);\n+\t\t\tstrbuf_release(&workdir_path);\n+\t\t\treturn res;\n+\t\t}\n+\t\tstrbuf_release(&src_path);\n+\t\tstrbuf_release(&workdir_path);\n+\t}\n+\n \tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n \n \treturn unregister_dir();\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 16f2b72b126..ef0e8d680d5 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -14,4 +14,19 @@ test_expect_success 'scalar shows a usage' '\n \ttest_expect_code 129 scalar -h\n '\n \n+test_expect_success 'scalar unregister' '\n+\tgit init vanish/src &&\n+\tscalar register vanish/src &&\n+\tgit config --get --global --fixed-value \\\n+\t\tmaintenance.repo \"$(pwd)/vanish/src\" &&\n+\tscalar list >scalar.repos &&\n+\tgrep -F \"$(pwd)/vanish/src\" scalar.repos &&\n+\trm -rf vanish/src/.git &&\n+\tscalar unregister vanish &&\n+\ttest_must_fail git config --get --global --fixed-value \\\n+\t\tmaintenance.repo \"$(pwd)/vanish/src\" &&\n+\tscalar list >scalar.repos &&\n+\t! grep -F \"$(pwd)/vanish/src\" scalar.repos\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"435158","messageId":"d291d3723a6bb8e1d4f871b5279063c3e0a3fd79.1631129086.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v3.git.1631129086.gitgitgadget@gmail.com","subject":"[PATCH v3 07/15] scalar: implement 'scalar list'","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-08T19:24:37Z","receivedAt":"2021-09-08T19:25:05Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe produced list simply consists of those repositories registered under\nthe multi-valued `scalar.repo` config setting in the user's Git config.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 11 +++++++++++\n contrib/scalar/scalar.txt | 11 ++++++++++-\n 2 files changed, 21 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex d114c038b64..7f5436399da 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -257,6 +257,16 @@ static int unregister_dir(void)\n \treturn res;\n }\n \n+static int cmd_list(int argc, const char **argv)\n+{\n+\tif (argc != 1)\n+\t\tdie(_(\"`scalar list` does not take arguments\"));\n+\n+\tif (run_git(\"config\", \"--global\", \"--get-all\", \"scalar.repo\", NULL) < 0)\n+\t\treturn -1;\n+\treturn 0;\n+}\n+\n static int cmd_register(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -343,6 +353,7 @@ static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n \t{ NULL, NULL},\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex d9a79984492..f93e3d00efd 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,6 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n+scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n \n@@ -28,11 +29,19 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand.\n+depending on the subcommand. With the exception of `list`, all subcommands\n+expect to be run in an enlistment.\n \n COMMANDS\n --------\n \n+List\n+~~~~\n+\n+list::\n+\tList enlistments that are currently registered by Scalar. This\n+\tsubcommand does not need to be run inside an enlistment.\n+\n Register\n ~~~~~~~~\n \n-- \ngitgitgadget\n\n"},{"id":"435159","messageId":"40dbf61771ece7b798c16560aeef7fa0fcee4ad2.1631129086.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v3.git.1631129086.gitgitgadget@gmail.com","subject":"[PATCH v3 08/15] scalar: implement the `clone` subcommand","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-08T19:24:38Z","receivedAt":"2021-09-08T19:25:08Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis implements Scalar's opinionated `clone` command: it tries to use a\npartial clone and sets up a sparse checkout by default. In contrast to\n`git clone`, `scalar clone` sets up the worktree in the `src/`\nsubdirectory, to encourage a separation between the source files and the\nbuild output (which helps Git tremendously because it avoids untracked\nfiles that have to be specifically ignored when refreshing the index).\n\nAlso, it registers the repository for regular, scheduled maintenance,\nand configures a flurry of configuration settings based on the\nexperience and experiments of the Microsoft Windows and the Microsoft\nOffice development teams.\n\nNote: since the `scalar clone` command is by far the most commonly\ncalled `scalar` subcommand, we document it at the top of the manual\npage.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 201 +++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        |  31 ++++-\n contrib/scalar/t/t9099-scalar.sh |  32 +++++\n 3 files changed, 261 insertions(+), 3 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 7f5436399da..bf18003b297 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -7,6 +7,7 @@\n #include \"parse-options.h\"\n #include \"config.h\"\n #include \"run-command.h\"\n+#include \"refs.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -257,6 +258,205 @@ static int unregister_dir(void)\n \treturn res;\n }\n \n+/* printf-style interface, expects `<key>=<value>` argument */\n+static int set_config(const char *fmt, ...)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tchar *value;\n+\tint res;\n+\tva_list args;\n+\n+\tva_start(args, fmt);\n+\tstrbuf_vaddf(&buf, fmt, args);\n+\tva_end(args);\n+\n+\tvalue = strchr(buf.buf, '=');\n+\tif (value)\n+\t\t*(value++) = '\\0';\n+\tres = git_config_set_gently(buf.buf, value);\n+\tstrbuf_release(&buf);\n+\n+\treturn res;\n+}\n+\n+static char *remote_default_branch(const char *url)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tstruct strbuf out = STRBUF_INIT;\n+\n+\tcp.git_cmd = 1;\n+\tstrvec_pushl(&cp.args, \"ls-remote\", \"--symref\", url, \"HEAD\", NULL);\n+\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n+\t\tconst char *line = out.buf;\n+\n+\t\twhile (*line) {\n+\t\t\tconst char *eol = strchrnul(line, '\\n'), *p;\n+\t\t\tsize_t len = eol - line;\n+\t\t\tchar *branch;\n+\n+\t\t\tif (!skip_prefix(line, \"ref: \", &p) ||\n+\t\t\t    !strip_suffix_mem(line, &len, \"\\tHEAD\")) {\n+\t\t\t\tline = eol + (*eol == '\\n');\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\teol = line + len;\n+\t\t\tif (skip_prefix(p, \"refs/heads/\", &p)) {\n+\t\t\t\tbranch = xstrndup(p, eol - p);\n+\t\t\t\tstrbuf_release(&out);\n+\t\t\t\treturn branch;\n+\t\t\t}\n+\n+\t\t\terror(_(\"remote HEAD is not a branch: '%.*s'\"),\n+\t\t\t      (int)(eol - p), p);\n+\t\t\tstrbuf_release(&out);\n+\t\t\treturn NULL;\n+\t\t}\n+\t}\n+\twarning(_(\"failed to get default branch name from remote; \"\n+\t\t  \"using local default\"));\n+\tstrbuf_reset(&out);\n+\n+\tchild_process_init(&cp);\n+\tcp.git_cmd = 1;\n+\tstrvec_pushl(&cp.args, \"symbolic-ref\", \"--short\", \"HEAD\", NULL);\n+\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n+\t\tstrbuf_trim(&out);\n+\t\treturn strbuf_detach(&out, NULL);\n+\t}\n+\n+\tstrbuf_release(&out);\n+\terror(_(\"failed to get default branch name\"));\n+\treturn NULL;\n+}\n+\n+static int cmd_clone(int argc, const char **argv)\n+{\n+\tconst char *branch = NULL;\n+\tint full_clone = 0;\n+\tstruct option clone_options[] = {\n+\t\tOPT_STRING('b', \"branch\", &branch, N_(\"<branch>\"),\n+\t\t\t   N_(\"branch to checkout after clone\")),\n+\t\tOPT_BOOL(0, \"full-clone\", &full_clone,\n+\t\t\t N_(\"when cloning, create full working directory\")),\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const clone_usage[] = {\n+\t\tN_(\"scalar clone [<options>] [--] <repo> [<dir>]\"),\n+\t\tNULL\n+\t};\n+\tconst char *url;\n+\tchar *enlistment = NULL, *dir = NULL;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint res;\n+\n+\targc = parse_options(argc, argv, NULL, clone_options, clone_usage, 0);\n+\n+\tif (argc == 2) {\n+\t\turl = argv[0];\n+\t\tenlistment = xstrdup(argv[1]);\n+\t} else if (argc == 1) {\n+\t\turl = argv[0];\n+\n+\t\tstrbuf_addstr(&buf, url);\n+\t\t/* Strip trailing slashes, if any */\n+\t\twhile (buf.len > 0 && is_dir_sep(buf.buf[buf.len - 1]))\n+\t\t\tstrbuf_setlen(&buf, buf.len - 1);\n+\t\t/* Strip suffix `.git`, if any */\n+\t\tstrbuf_strip_suffix(&buf, \".git\");\n+\n+\t\tenlistment = find_last_dir_sep(buf.buf);\n+\t\tif (!enlistment) {\n+\t\t\tdie(_(\"cannot deduce worktree name from '%s'\"), url);\n+\t\t}\n+\t\tenlistment = xstrdup(enlistment + 1);\n+\t} else {\n+\t\tusage_msg_opt(_(\"You must specify a repository to clone.\"),\n+\t\t\t      clone_usage, clone_options);\n+\t}\n+\n+\tif (is_directory(enlistment))\n+\t\tdie(_(\"directory '%s' exists already\"), enlistment);\n+\n+\tdir = xstrfmt(\"%s/src\", enlistment);\n+\n+\tstrbuf_reset(&buf);\n+\tif (branch)\n+\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", branch);\n+\telse {\n+\t\tchar *b = repo_default_branch_name(the_repository, 1);\n+\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", b);\n+\t\tfree(b);\n+\t}\n+\n+\tif ((res = run_git(\"-c\", buf.buf, \"init\", \"--\", dir, NULL)))\n+\t\tgoto cleanup;\n+\n+\tif (chdir(dir) < 0) {\n+\t\tres = error_errno(_(\"could not switch to '%s'\"), dir);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tsetup_git_directory();\n+\n+\t/* common-main already logs `argv` */\n+\ttrace2_def_repo(the_repository);\n+\n+\tif (!branch && !(branch = remote_default_branch(url))) {\n+\t\tres = error(_(\"failed to get default branch for '%s'\"), url);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tif (set_config(\"remote.origin.url=%s\", url) ||\n+\t    set_config(\"remote.origin.fetch=\"\n+\t\t       \"+refs/heads/*:refs/remotes/origin/*\") ||\n+\t    set_config(\"remote.origin.promisor=true\") ||\n+\t    set_config(\"remote.origin.partialCloneFilter=blob:none\")) {\n+\t\tres = error(_(\"could not configure remote in '%s'\"), dir);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tif (!full_clone &&\n+\t    (res = run_git(\"sparse-checkout\", \"init\", \"--cone\", NULL)))\n+\t\tgoto cleanup;\n+\n+\tif (set_recommended_config())\n+\t\treturn error(_(\"could not configure '%s'\"), dir);\n+\n+\tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL))) {\n+\t\twarning(_(\"partial clone failed; attempting full clone\"));\n+\n+\t\tif (set_config(\"remote.origin.promisor\") ||\n+\t\t    set_config(\"remote.origin.partialCloneFilter\")) {\n+\t\t\tres = error(_(\"could not configure for full clone\"));\n+\t\t\tgoto cleanup;\n+\t\t}\n+\n+\t\tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL)))\n+\t\t\tgoto cleanup;\n+\t}\n+\n+\tif ((res = set_config(\"branch.%s.remote=origin\", branch)))\n+\t\tgoto cleanup;\n+\tif ((res = set_config(\"branch.%s.merge=refs/heads/%s\",\n+\t\t\t      branch, branch)))\n+\t\tgoto cleanup;\n+\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addf(&buf, \"origin/%s\", branch);\n+\tres = run_git(\"checkout\", \"-f\", \"-t\", buf.buf, NULL);\n+\tif (res)\n+\t\tgoto cleanup;\n+\n+\tres = register_dir();\n+\n+cleanup:\n+\tfree(enlistment);\n+\tfree(dir);\n+\tstrbuf_release(&buf);\n+\treturn res;\n+}\n+\n static int cmd_list(int argc, const char **argv)\n {\n \tif (argc != 1)\n@@ -353,6 +553,7 @@ static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"clone\", cmd_clone },\n \t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex f93e3d00efd..d65fb5f1491 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,6 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n+scalar clone [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n@@ -29,12 +30,36 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand. With the exception of `list`, all subcommands\n-expect to be run in an enlistment.\n+depending on the subcommand. With the exception of `clone` and `list`, all\n+subcommands expect to be run in an enlistment.\n \n COMMANDS\n --------\n \n+Clone\n+~~~~~\n+\n+clone [<options>] <url> [<enlistment>]::\n+\tClones the specified repository, similar to linkgit:git-clone[1]. By\n+\tdefault, only commit and tree objects are cloned. Once finished, the\n+\tworktree is located at `<enlistment>/src`.\n++\n+The sparse-checkout feature is enabled (except when run with `--full-clone`)\n+and the only files present are those in the top-level directory. Use\n+`git sparse-checkout set` to expand the set of directories you want to see,\n+or `git sparse-checkout disable` to expand to all files (see\n+linkgit:git-sparse-checkout[1] for more details). You can explore the\n+subdirectories outside your sparse-checkout by using `git ls-tree HEAD`.\n+\n+-b <name>::\n+--branch <name>::\n+\tInstead of checking out the branch pointed to by the cloned\n+\trepository's HEAD, check out the `<name>` branch instead.\n+\n+--[no-]full-clone::\n+\tA sparse-checkout is initialized by default. This behavior can be\n+\tturned off via `--full-clone`.\n+\n List\n ~~~~\n \n@@ -64,7 +89,7 @@ unregister [<enlistment>]::\n \n SEE ALSO\n --------\n-linkgit:git-maintenance[1].\n+linkgit:git-clone[1], linkgit:git-maintenance[1].\n \n Scalar\n ---\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex ef0e8d680d5..295398f62cc 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -10,6 +10,9 @@ PATH=$PWD/..:$PATH\n \n . ../../../t/test-lib.sh\n \n+GIT_TEST_MAINT_SCHEDULER=\"crontab:test-tool crontab ../cron.txt\"\n+export GIT_TEST_MAINT_SCHEDULER\n+\n test_expect_success 'scalar shows a usage' '\n \ttest_expect_code 129 scalar -h\n '\n@@ -29,4 +32,33 @@ test_expect_success 'scalar unregister' '\n \t! grep -F \"$(pwd)/vanish/src\" scalar.repos\n '\n \n+test_expect_success 'set up repository to clone' '\n+\ttest_commit first &&\n+\ttest_commit second &&\n+\ttest_commit third &&\n+\tgit switch -c parallel first &&\n+\tmkdir -p 1/2 &&\n+\ttest_commit 1/2/3 &&\n+\tgit config uploadPack.allowFilter true &&\n+\tgit config uploadPack.allowAnySHA1InWant true\n+'\n+\n+test_expect_success 'scalar clone' '\n+\tsecond=$(git rev-parse --verify second:second.t) &&\n+\tscalar clone \"file://$(pwd)\" cloned &&\n+\t(\n+\t\tcd cloned/src &&\n+\n+\t\tgit config --get --global --fixed-value maintenance.repo \\\n+\t\t\t\"$(pwd)\" &&\n+\n+\t\ttest_path_is_missing 1/2 &&\n+\t\ttest_must_fail git rev-list --missing=print $second &&\n+\t\tgit rev-list $second &&\n+\t\tgit cat-file blob $second >actual &&\n+\t\techo \"second\" >expect &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"435160","messageId":"414dbe7d859a39c14063b69d672837e7c99c410e.1631129086.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v3.git.1631129086.gitgitgadget@gmail.com","subject":"[PATCH v3 09/15] scalar: teach 'clone' to support the --single-branch option","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-08T19:24:39Z","receivedAt":"2021-09-08T19:25:09Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nJust like `git clone`, the `scalar clone` command now also offers to\nrestrict the clone to a single branch.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          |  9 +++++++--\n contrib/scalar/scalar.txt        | 12 +++++++++++-\n contrib/scalar/t/t9099-scalar.sh |  6 +++++-\n 3 files changed, 23 insertions(+), 4 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex bf18003b297..7dd1f28948f 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -333,12 +333,15 @@ static char *remote_default_branch(const char *url)\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n-\tint full_clone = 0;\n+\tint full_clone = 0, single_branch = 0;\n \tstruct option clone_options[] = {\n \t\tOPT_STRING('b', \"branch\", &branch, N_(\"<branch>\"),\n \t\t\t   N_(\"branch to checkout after clone\")),\n \t\tOPT_BOOL(0, \"full-clone\", &full_clone,\n \t\t\t N_(\"when cloning, create full working directory\")),\n+\t\tOPT_BOOL(0, \"single-branch\", &single_branch,\n+\t\t\t N_(\"only download metadata for the branch that will \"\n+\t\t\t    \"be checked out\")),\n \t\tOPT_END(),\n \t};\n \tconst char * const clone_usage[] = {\n@@ -409,7 +412,9 @@ static int cmd_clone(int argc, const char **argv)\n \n \tif (set_config(\"remote.origin.url=%s\", url) ||\n \t    set_config(\"remote.origin.fetch=\"\n-\t\t       \"+refs/heads/*:refs/remotes/origin/*\") ||\n+\t\t       \"+refs/heads/%s:refs/remotes/origin/%s\",\n+\t\t       single_branch ? branch : \"*\",\n+\t\t       single_branch ? branch : \"*\") ||\n \t    set_config(\"remote.origin.promisor=true\") ||\n \t    set_config(\"remote.origin.partialCloneFilter=blob:none\")) {\n \t\tres = error(_(\"could not configure remote in '%s'\"), dir);\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex d65fb5f1491..46999cf7c84 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,7 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n-scalar clone [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n+scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n@@ -56,6 +56,16 @@ subdirectories outside your sparse-checkout by using `git ls-tree HEAD`.\n \tInstead of checking out the branch pointed to by the cloned\n \trepository's HEAD, check out the `<name>` branch instead.\n \n+--[no-]single-branch::\n+\tClone only the history leading to the tip of a single branch, either\n+\tspecified by the `--branch` option or the primary branch remote's\n+\t`HEAD` points at.\n++\n+Further fetches into the resulting repository will only update the\n+remote-tracking branch for the branch this option was used for the initial\n+cloning. If the HEAD at the remote did not point at any branch when\n+`--single-branch` clone was made, no remote-tracking branch is created.\n+\n --[no-]full-clone::\n \tA sparse-checkout is initialized by default. This behavior can be\n \tturned off via `--full-clone`.\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 295398f62cc..9a35ab4fde6 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -45,13 +45,17 @@ test_expect_success 'set up repository to clone' '\n \n test_expect_success 'scalar clone' '\n \tsecond=$(git rev-parse --verify second:second.t) &&\n-\tscalar clone \"file://$(pwd)\" cloned &&\n+\tscalar clone \"file://$(pwd)\" cloned --single-branch &&\n \t(\n \t\tcd cloned/src &&\n \n \t\tgit config --get --global --fixed-value maintenance.repo \\\n \t\t\t\"$(pwd)\" &&\n \n+\t\tgit for-each-ref --format=\"%(refname)\" refs/remotes/origin/ >actual &&\n+\t\techo \"refs/remotes/origin/parallel\" >expect &&\n+\t\ttest_cmp expect actual &&\n+\n \t\ttest_path_is_missing 1/2 &&\n \t\ttest_must_fail git rev-list --missing=print $second &&\n \t\tgit rev-list $second &&\n-- \ngitgitgadget\n\n"},{"id":"435161","messageId":"76de416a64352fc565893d344b41fe7440c59142.1631129086.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v3.git.1631129086.gitgitgadget@gmail.com","subject":"[PATCH v3 10/15] scalar: implement the `run` command","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-08T19:24:40Z","receivedAt":"2021-09-08T19:25:10Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nNote: this subcommand is provided primarily for backwards-compatibility,\nfor existing Scalar uses. It is mostly just a shim for `git\nmaintenance`, mapping task names from the way Scalar called them to the\nway Git calls them.\n\nThe reason why those names differ? The background maintenance was first\nimplemented in Scalar, and when it was contributed as a patch series\nimplementing the `git maintenance` command, reviewers suggested better\nnames, those suggestions were accepted before the patches were\nintegrated into core Git.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 64 +++++++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt | 19 ++++++++++++\n 2 files changed, 83 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 7dd1f28948f..8a11f390251 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -490,6 +490,69 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_run(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tstruct {\n+\t\tconst char *arg, *task;\n+\t} tasks[] = {\n+\t\t{ \"config\", NULL },\n+\t\t{ \"commit-graph\", \"commit-graph\" },\n+\t\t{ \"fetch\", \"prefetch\" },\n+\t\t{ \"loose-objects\", \"loose-objects\" },\n+\t\t{ \"pack-files\", \"incremental-repack\" },\n+\t\t{ NULL, NULL }\n+\t};\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tconst char *usagestr[] = { NULL, NULL };\n+\tint i;\n+\n+\tstrbuf_addstr(&buf, N_(\"scalar run <task> [<enlistment>]\\nTasks:\\n\"));\n+\tfor (i = 0; tasks[i].arg; i++)\n+\t\tstrbuf_addf(&buf, \"\\t%s\\n\", tasks[i].arg);\n+\tusagestr[0] = buf.buf;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usagestr, 0);\n+\n+\tif (!argc)\n+\t\tusage_with_options(usagestr, options);\n+\n+\tif (!strcmp(\"all\", argv[0])) {\n+\t\ti = -1;\n+\t} else {\n+\t\tfor (i = 0; tasks[i].arg && strcmp(tasks[i].arg, argv[0]); i++)\n+\t\t\t; /* keep looking for the task */\n+\n+\t\tif (i > 0 && !tasks[i].arg) {\n+\t\t\terror(_(\"no such task: '%s'\"), argv[0]);\n+\t\t\tusage_with_options(usagestr, options);\n+\t\t}\n+\t}\n+\n+\targc--;\n+\targv++;\n+\tsetup_enlistment_directory(argc, argv, usagestr, options, NULL);\n+\tstrbuf_release(&buf);\n+\n+\tif (i == 0)\n+\t\treturn register_dir();\n+\n+\tif (i > 0)\n+\t\treturn run_git(\"maintenance\", \"run\",\n+\t\t\t       \"--task\", tasks[i].task, NULL);\n+\n+\tif (register_dir())\n+\t\treturn -1;\n+\tfor (i = 1; tasks[i].arg; i++)\n+\t\tif (run_git(\"maintenance\", \"run\",\n+\t\t\t    \"--task\", tasks[i].task, NULL))\n+\t\t\treturn -1;\n+\treturn 0;\n+}\n+\n static int remove_deleted_enlistment(struct strbuf *path)\n {\n \tint res = 0;\n@@ -562,6 +625,7 @@ static struct {\n \t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n+\t{ \"run\", cmd_run },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 46999cf7c84..f139a14445d 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -12,6 +12,7 @@ scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<e\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n+scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -97,6 +98,24 @@ unregister [<enlistment>]::\n \tRemove the specified repository from the list of repositories\n \tregistered with Scalar and stop the scheduled background maintenance.\n \n+Run\n+~~~\n+\n+scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]::\n+\tRun the given maintenance task (or all tasks, if `all` was specified).\n+\tExcept for `all` and `config`, this subcommand simply hands off to\n+\tlinkgit:git-maintenance[1] (mapping `fetch` to `prefetch` and\n+\t`pack-files` to `incremental-repack`).\n++\n+These tasks are run automatically as part of the scheduled maintenance,\n+as soon as the repository is registered with Scalar. It should therefore\n+not be necessary to run this subcommand manually.\n++\n+The `config` task is specific to Scalar and configures all those\n+opinionated default settings that make Git work more efficiently with\n+large repositories. As this task is run as part of `scalar clone`\n+automatically, explicit invocations of this task are rarely needed.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"435162","messageId":"655a902b9dfc66017bbd6a686158108a56926904.1631129086.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v3.git.1631129086.gitgitgadget@gmail.com","subject":"[PATCH v3 11/15] scalar: allow reconfiguring an existing enlistment","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-08T19:24:41Z","receivedAt":"2021-09-08T19:25:13Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis comes in handy during Scalar upgrades, or when config settings were\nmessed up by mistake.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 79 +++++++++++++++++++++-----------\n contrib/scalar/scalar.txt        |  8 ++++\n contrib/scalar/t/t9099-scalar.sh |  8 ++++\n 3 files changed, 67 insertions(+), 28 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 8a11f390251..1fff7eb7c12 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -115,18 +115,20 @@ static int run_git(const char *arg, ...)\n \treturn res;\n }\n \n-static int set_recommended_config(void)\n+static int set_recommended_config(int reconfigure)\n {\n \tstruct {\n \t\tconst char *key;\n \t\tconst char *value;\n+\t\tint overwrite_on_reconfigure;\n \t} config[] = {\n-\t\t{ \"am.keepCR\", \"true\" },\n-\t\t{ \"core.FSCache\", \"true\" },\n-\t\t{ \"core.multiPackIndex\", \"true\" },\n-\t\t{ \"core.preloadIndex\", \"true\" },\n+\t\t/* Required */\n+\t\t{ \"am.keepCR\", \"true\", 1 },\n+\t\t{ \"core.FSCache\", \"true\", 1 },\n+\t\t{ \"core.multiPackIndex\", \"true\", 1 },\n+\t\t{ \"core.preloadIndex\", \"true\", 1 },\n #ifndef WIN32\n-\t\t{ \"core.untrackedCache\", \"true\" },\n+\t\t{ \"core.untrackedCache\", \"true\", 1 },\n #else\n \t\t/*\n \t\t * Unfortunately, Scalar's Functional Tests demonstrated\n@@ -140,28 +142,29 @@ static int set_recommended_config(void)\n \t\t * Therefore, with a sad heart, we disable this very useful\n \t\t * feature on Windows.\n \t\t */\n-\t\t{ \"core.untrackedCache\", \"false\" },\n+\t\t{ \"core.untrackedCache\", \"false\", 1 },\n #endif\n-\t\t{ \"core.logAllRefUpdates\", \"true\" },\n-\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n-\t\t{ \"credential.validate\", \"false\" }, /* GCM4W-only */\n-\t\t{ \"gc.auto\", \"0\" },\n-\t\t{ \"gui.GCWarning\", \"false\" },\n-\t\t{ \"index.threads\", \"true\" },\n-\t\t{ \"index.version\", \"4\" },\n-\t\t{ \"merge.stat\", \"false\" },\n-\t\t{ \"merge.renames\", \"false\" },\n-\t\t{ \"pack.useBitmaps\", \"false\" },\n-\t\t{ \"pack.useSparse\", \"true\" },\n-\t\t{ \"receive.autoGC\", \"false\" },\n-\t\t{ \"reset.quiet\", \"true\" },\n-\t\t{ \"feature.manyFiles\", \"false\" },\n-\t\t{ \"feature.experimental\", \"false\" },\n-\t\t{ \"fetch.unpackLimit\", \"1\" },\n-\t\t{ \"fetch.writeCommitGraph\", \"false\" },\n+\t\t{ \"core.logAllRefUpdates\", \"true\", 1 },\n+\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\", 1 },\n+\t\t{ \"credential.validate\", \"false\", 1 }, /* GCM4W-only */\n+\t\t{ \"gc.auto\", \"0\", 1 },\n+\t\t{ \"gui.GCWarning\", \"false\", 1 },\n+\t\t{ \"index.threads\", \"true\", 1 },\n+\t\t{ \"index.version\", \"4\", 1 },\n+\t\t{ \"merge.stat\", \"false\", 1 },\n+\t\t{ \"merge.renames\", \"false\", 1 },\n+\t\t{ \"pack.useBitmaps\", \"false\", 1 },\n+\t\t{ \"pack.useSparse\", \"true\", 1 },\n+\t\t{ \"receive.autoGC\", \"false\", 1 },\n+\t\t{ \"reset.quiet\", \"true\", 1 },\n+\t\t{ \"feature.manyFiles\", \"false\", 1 },\n+\t\t{ \"feature.experimental\", \"false\", 1 },\n+\t\t{ \"fetch.unpackLimit\", \"1\", 1 },\n+\t\t{ \"fetch.writeCommitGraph\", \"false\", 1 },\n #ifdef WIN32\n-\t\t{ \"http.sslBackend\", \"schannel\" },\n+\t\t{ \"http.sslBackend\", \"schannel\", 1 },\n #endif\n+\t\t/* Optional */\n \t\t{ \"status.aheadBehind\", \"false\" },\n \t\t{ \"commitGraph.generationVersion\", \"1\" },\n \t\t{ \"core.autoCRLF\", \"false\" },\n@@ -172,7 +175,8 @@ static int set_recommended_config(void)\n \tchar *value;\n \n \tfor (i = 0; config[i].key; i++) {\n-\t\tif (git_config_get_string(config[i].key, &value)) {\n+\t\tif ((reconfigure && config[i].overwrite_on_reconfigure) ||\n+\t\t    git_config_get_string(config[i].key, &value)) {\n \t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"created\");\n \t\t\tif (git_config_set_gently(config[i].key,\n \t\t\t\t\t\t  config[i].value) < 0)\n@@ -237,7 +241,7 @@ static int register_dir(void)\n \tint res = add_or_remove_enlistment(1);\n \n \tif (!res)\n-\t\tres = set_recommended_config();\n+\t\tres = set_recommended_config(0);\n \n \tif (!res)\n \t\tres = toggle_maintenance(1);\n@@ -425,7 +429,7 @@ static int cmd_clone(int argc, const char **argv)\n \t    (res = run_git(\"sparse-checkout\", \"init\", \"--cone\", NULL)))\n \t\tgoto cleanup;\n \n-\tif (set_recommended_config())\n+\tif (set_recommended_config(0))\n \t\treturn error(_(\"could not configure '%s'\"), dir);\n \n \tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL))) {\n@@ -490,6 +494,24 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_reconfigure(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar reconfigure [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn set_recommended_config(1);\n+}\n+\n static int cmd_run(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -626,6 +648,7 @@ static struct {\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n \t{ \"run\", cmd_run },\n+\t{ \"reconfigure\", cmd_reconfigure },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex f139a14445d..f4e4686e8c8 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -13,6 +13,7 @@ scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n+scalar reconfigure <enlistment>\n \n DESCRIPTION\n -----------\n@@ -116,6 +117,13 @@ opinionated default settings that make Git work more efficiently with\n large repositories. As this task is run as part of `scalar clone`\n automatically, explicit invocations of this task are rarely needed.\n \n+Reconfigure\n+~~~~~~~~~~~\n+\n+After a Scalar upgrade, or when the configuration of a Scalar enlistment\n+was somehow corrupted or changed by mistake, this subcommand allows to\n+reconfigure the enlistment.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 9a35ab4fde6..e6d74a06ca0 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -65,4 +65,12 @@ test_expect_success 'scalar clone' '\n \t)\n '\n \n+test_expect_success 'scalar reconfigure' '\n+\tgit init one/src &&\n+\tscalar register one &&\n+\tgit -C one/src config core.preloadIndex false &&\n+\tscalar reconfigure one &&\n+\ttest true = \"$(git -C one/src config core.preloadIndex)\"\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"435163","messageId":"2d1987bfcda3977bb7822c8158170cd803ebe5f9.1631129086.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v3.git.1631129086.gitgitgadget@gmail.com","subject":"[PATCH v3 12/15] scalar: teach 'reconfigure' to optionally handle all registered enlistments","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-08T19:24:42Z","receivedAt":"2021-09-08T19:25:14Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nAfter a Scalar upgrade, it can come in really handy if there is an easy\nway to reconfigure all Scalar enlistments. This new option offers this\nfunctionality.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 61 ++++++++++++++++++++++++++++++--\n contrib/scalar/scalar.txt        |  9 +++--\n contrib/scalar/t/t9099-scalar.sh |  3 ++\n 3 files changed, 67 insertions(+), 6 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 1fff7eb7c12..67fa5305225 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -494,22 +494,77 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int get_scalar_repos(const char *key, const char *value, void *data)\n+{\n+\tstruct string_list *list = data;\n+\n+\tif (!strcmp(key, \"scalar.repo\"))\n+\t\tstring_list_append(list, value);\n+\n+\treturn 0;\n+}\n+\n static int cmd_reconfigure(int argc, const char **argv)\n {\n+\tint all = 0;\n \tstruct option options[] = {\n+\t\tOPT_BOOL('a', \"all\", &all,\n+\t\t\t N_(\"reconfigure all registered enlistments\")),\n \t\tOPT_END(),\n \t};\n \tconst char * const usage[] = {\n-\t\tN_(\"scalar reconfigure [<enlistment>]\"),\n+\t\tN_(\"scalar reconfigure [--all | <enlistment>]\"),\n \t\tNULL\n \t};\n+\tstruct string_list scalar_repos = STRING_LIST_INIT_DUP;\n+\tint i, res = 0;\n+\tstruct repository r = { NULL };\n+\tstruct strbuf commondir = STRBUF_INIT, gitdir = STRBUF_INIT;\n \n \targc = parse_options(argc, argv, NULL, options,\n \t\t\t     usage, 0);\n \n-\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\tif (!all) {\n+\t\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\t\treturn set_recommended_config(1);\n+\t}\n+\n+\tif (argc > 0)\n+\t\tusage_msg_opt(_(\"--all or <enlistment>, but not both\"),\n+\t\t\t      usage, options);\n+\n+\tgit_config(get_scalar_repos, &scalar_repos);\n \n-\treturn set_recommended_config(1);\n+\tfor (i = 0; i < scalar_repos.nr; i++) {\n+\t\tconst char *dir = scalar_repos.items[i].string;\n+\n+\t\tstrbuf_reset(&commondir);\n+\t\tstrbuf_reset(&gitdir);\n+\n+\t\tif (chdir(dir) < 0) {\n+\t\t\twarning_errno(_(\"could not switch to '%s'\"), dir);\n+\t\t\tres = -1;\n+\t\t} else if (discover_git_directory(&commondir, &gitdir) < 0) {\n+\t\t\twarning_errno(_(\"git repository gone in '%s'\"), dir);\n+\t\t\tres = -1;\n+\t\t} else {\n+\t\t\tgit_config_clear();\n+\n+\t\t\tthe_repository = &r;\n+\t\t\tr.commondir = commondir.buf;\n+\t\t\tr.gitdir = gitdir.buf;\n+\n+\t\t\tif (set_recommended_config(1) < 0)\n+\t\t\t\tres = -1;\n+\t\t}\n+\t}\n+\n+\tstring_list_clear(&scalar_repos, 1);\n+\tstrbuf_release(&commondir);\n+\tstrbuf_release(&gitdir);\n+\n+\treturn res;\n }\n \n static int cmd_run(int argc, const char **argv)\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex f4e4686e8c8..2fa96fcabc6 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -13,7 +13,7 @@ scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n-scalar reconfigure <enlistment>\n+scalar reconfigure [ --all | <enlistment> ]\n \n DESCRIPTION\n -----------\n@@ -32,8 +32,8 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand. With the exception of `clone` and `list`, all\n-subcommands expect to be run in an enlistment.\n+depending on the subcommand. With the exception of `clone`, `list` and\n+`reconfigure --all`, all subcommands expect to be run in an enlistment.\n \n COMMANDS\n --------\n@@ -124,6 +124,9 @@ After a Scalar upgrade, or when the configuration of a Scalar enlistment\n was somehow corrupted or changed by mistake, this subcommand allows to\n reconfigure the enlistment.\n \n+With the `--all` option, all enlistments currently registered with Scalar\n+will be reconfigured. Use this option after each Scalar upgrade.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex e6d74a06ca0..5fe7fabd0e5 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -70,6 +70,9 @@ test_expect_success 'scalar reconfigure' '\n \tscalar register one &&\n \tgit -C one/src config core.preloadIndex false &&\n \tscalar reconfigure one &&\n+\ttest true = \"$(git -C one/src config core.preloadIndex)\" &&\n+\tgit -C one/src config core.preloadIndex false &&\n+\tscalar reconfigure -a &&\n \ttest true = \"$(git -C one/src config core.preloadIndex)\"\n '\n \n-- \ngitgitgadget\n\n"},{"id":"435164","messageId":"c67938299ee48655b198effe0a0cca73d82d4d00.1631129086.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v3.git.1631129086.gitgitgadget@gmail.com","subject":"[PATCH v3 13/15] scalar: implement the `delete` command","fromName":"Matthew John Cheetham via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-08T19:24:43Z","receivedAt":"2021-09-08T19:25:18Z","isPatch":true,"sender":{"key":"mjcheetham@outlook.com","avatar":"https://avatars.githubusercontent.com/u/5658207?v=4"},"body":"From: Matthew John Cheetham <mjcheetham@outlook.com>\n\nDelete an enlistment by first unregistering the repository and then\ndeleting the enlistment directory (usually the directory containing the\nworktree `src/` directory).\n\nOn Windows, if the current directory is inside the enlistment's\ndirectory, change to the parent of the enlistment directory, to allow us\nto delete the enlistment (directories used by processes e.g. as current\nworking directories cannot be deleted on Windows).\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Matthew John Cheetham <mjcheetham@outlook.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 55 ++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        |  8 +++++\n contrib/scalar/t/t9099-scalar.sh |  9 ++++++\n 3 files changed, 72 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 67fa5305225..00bedb0bf66 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -8,6 +8,7 @@\n #include \"config.h\"\n #include \"run-command.h\"\n #include \"refs.h\"\n+#include \"dir.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -334,6 +335,33 @@ static char *remote_default_branch(const char *url)\n \treturn NULL;\n }\n \n+static int delete_enlistment(struct strbuf *enlistment)\n+{\n+#ifdef WIN32\n+\tstruct strbuf parent = STRBUF_INIT;\n+#endif\n+\n+\tif (unregister_dir())\n+\t\tdie(_(\"failed to unregister repository\"));\n+\n+#ifdef WIN32\n+\t/*\n+\t * Change the current directory to one outside of the enlistment so\n+\t * that we may delete everything underneath it.\n+\t */\n+\tstrbuf_addbuf(&parent, enlistment);\n+\tstrbuf_parent_directory(&parent);\n+\tif (chdir(parent.buf) < 0)\n+\t\tdie_errno(_(\"could not switch to '%s'\"), parent.buf);\n+\tstrbuf_release(&parent);\n+#endif\n+\n+\tif (remove_dir_recursively(enlistment, 0))\n+\t\tdie(_(\"failed to delete enlistment directory\"));\n+\n+\treturn 0;\n+}\n+\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n@@ -694,6 +722,32 @@ static int cmd_unregister(int argc, const char **argv)\n \treturn unregister_dir();\n }\n \n+static int cmd_delete(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar delete <enlistment>\"),\n+\t\tNULL\n+\t};\n+\tstruct strbuf enlistment = STRBUF_INIT;\n+\tint res = 0;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tif (argc != 1)\n+\t\tusage_with_options(usage, options);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, &enlistment);\n+\n+\tres = delete_enlistment(&enlistment);\n+\tstrbuf_release(&enlistment);\n+\n+\treturn res;\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n@@ -704,6 +758,7 @@ static struct {\n \t{ \"unregister\", cmd_unregister },\n \t{ \"run\", cmd_run },\n \t{ \"reconfigure\", cmd_reconfigure },\n+\t{ \"delete\", cmd_delete },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 2fa96fcabc6..6fc57707718 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -14,6 +14,7 @@ scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n scalar reconfigure [ --all | <enlistment> ]\n+scalar delete <enlistment>\n \n DESCRIPTION\n -----------\n@@ -127,6 +128,13 @@ reconfigure the enlistment.\n With the `--all` option, all enlistments currently registered with Scalar\n will be reconfigured. Use this option after each Scalar upgrade.\n \n+Delete\n+~~~~~~\n+\n+delete <enlistment>::\n+\tThis subcommand lets you delete an existing Scalar enlistment from your\n+\tlocal file system, unregistering the repository.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 5fe7fabd0e5..7e8771d0eff 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -76,4 +76,13 @@ test_expect_success 'scalar reconfigure' '\n \ttest true = \"$(git -C one/src config core.preloadIndex)\"\n '\n \n+test_expect_success 'scalar delete without enlistment shows a usage' '\n+\ttest_expect_code 129 scalar delete\n+'\n+\n+test_expect_success 'scalar delete with enlistment' '\n+\tscalar delete cloned &&\n+\ttest_path_is_missing cloned\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"435165","messageId":"d2cd2b7094b5a727a0d57f94b51caf7c6d8a44fc.1631129086.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v3.git.1631129086.gitgitgadget@gmail.com","subject":"[PATCH v3 14/15] scalar: implement the `version` command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-08T19:24:44Z","receivedAt":"2021-09-08T19:25:19Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe .NET version of Scalar has a `version` command. This was necessary\nbecause it was versioned independently of Git.\n\nSince Scalar is now tightly coupled with Git, it does not make sense for\nthem to show different versions. Therefore, it shows the same output as\n`git version`. For backwards-compatibility with the .NET version,\n`scalar version` prints to `stderr`, though (`git version` prints to\n`stdout` instead).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c | 39 +++++++++++++++++++++++++++++++++++++++\n 1 file changed, 39 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 00bedb0bf66..728166aa97a 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -9,6 +9,7 @@\n #include \"run-command.h\"\n #include \"refs.h\"\n #include \"dir.h\"\n+#include \"help.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -362,6 +363,15 @@ static int delete_enlistment(struct strbuf *enlistment)\n \treturn 0;\n }\n \n+/*\n+ * Dummy implementation; Using `get_version_info()` would cause a link error\n+ * without this.\n+ */\n+void load_builtin_commands(const char *prefix, struct cmdnames *cmds)\n+{\n+\tdie(\"not implemented\");\n+}\n+\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n@@ -748,6 +758,34 @@ static int cmd_delete(int argc, const char **argv)\n \treturn res;\n }\n \n+static int cmd_version(int argc, const char **argv)\n+{\n+\tint verbose = 0, build_options = 0;\n+\tstruct option options[] = {\n+\t\tOPT__VERBOSE(&verbose, N_(\"include Git version\")),\n+\t\tOPT_BOOL(0, \"build-options\", &build_options,\n+\t\t\t N_(\"include Git's build options\")),\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar verbose [-v | --verbose] [--build-options]\"),\n+\t\tNULL\n+\t};\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tif (argc != 0)\n+\t\tusage_with_options(usage, options);\n+\n+\tget_version_info(&buf, build_options);\n+\tfprintf(stderr, \"%s\\n\", buf.buf);\n+\tstrbuf_release(&buf);\n+\n+\treturn 0;\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n@@ -759,6 +797,7 @@ static struct {\n \t{ \"run\", cmd_run },\n \t{ \"reconfigure\", cmd_reconfigure },\n \t{ \"delete\", cmd_delete },\n+\t{ \"version\", cmd_version },\n \t{ NULL, NULL},\n };\n \n-- \ngitgitgadget\n\n"},{"id":"435166","messageId":"7ccc4f8b9b0f5b33aafeac460bcc646e7236aa4a.1631129086.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v3.git.1631129086.gitgitgadget@gmail.com","subject":"[PATCH v3 15/15] scalar: accept -C and -c options before the subcommand","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-08T19:24:45Z","receivedAt":"2021-09-08T19:25:20Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe `git` executable has these two very useful options:\n\n-C <directory>:\n\tswitch to the specified directory before performing any actions\n\n-c <key>=<value>:\n\ttemporarily configure this setting for the duration of the\n\tspecified scalar subcommand\n\nWith this commit, we teach the `scalar` executable the same trick.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 22 +++++++++++++++++++++-\n contrib/scalar/scalar.txt | 10 ++++++++++\n 2 files changed, 31 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 728166aa97a..76a77ca1ed0 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -806,6 +806,25 @@ int cmd_main(int argc, const char **argv)\n \tstruct strbuf scalar_usage = STRBUF_INIT;\n \tint i;\n \n+\twhile (argc > 1 && *argv[1] == '-') {\n+\t\tif (!strcmp(argv[1], \"-C\")) {\n+\t\t\tif (argc < 3)\n+\t\t\t\tdie(_(\"-C requires a <directory>\"));\n+\t\t\tif (chdir(argv[2]) < 0)\n+\t\t\t\tdie_errno(_(\"could not change to '%s'\"),\n+\t\t\t\t\t  argv[2]);\n+\t\t\targc -= 2;\n+\t\t\targv += 2;\n+\t\t} else if (!strcmp(argv[1], \"-c\")) {\n+\t\t\tif (argc < 3)\n+\t\t\t\tdie(_(\"-c requires a <key>=<value> argument\"));\n+\t\t\tgit_config_push_parameter(argv[2]);\n+\t\t\targc -= 2;\n+\t\t\targv += 2;\n+\t\t} else\n+\t\t\tbreak;\n+\t}\n+\n \tif (argc > 1) {\n \t\targv++;\n \t\targc--;\n@@ -816,7 +835,8 @@ int cmd_main(int argc, const char **argv)\n \t}\n \n \tstrbuf_addstr(&scalar_usage,\n-\t\t      N_(\"scalar <command> [<options>]\\n\\nCommands:\\n\"));\n+\t\t      N_(\"scalar [-C <directory>] [-c <key>=<value>] \"\n+\t\t\t \"<command> [<options>]\\n\\nCommands:\\n\"));\n \tfor (i = 0; builtins[i].name; i++)\n \t\tstrbuf_addf(&scalar_usage, \"\\t%s\\n\", builtins[i].name);\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 6fc57707718..3a80f829edc 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -36,6 +36,16 @@ The `scalar` command implements various subcommands, and different options\n depending on the subcommand. With the exception of `clone`, `list` and\n `reconfigure --all`, all subcommands expect to be run in an enlistment.\n \n+The following options can be specified _before_ the subcommand:\n+\n+-C <directory>::\n+\tBefore running the subcommand, change the working directory. This\n+\toption imitates the same option of linkgit:git[1].\n+\n+-c <key>=<value>::\n+\tFor the duration of running the specified subcommand, configure this\n+\tsetting. This option imitates the same option of linkgit:git[1].\n+\n COMMANDS\n --------\n \n-- \ngitgitgadget\n"},{"id":"435214","messageId":"bf098e5d-3223-5d72-d0a9-b19c5b958132@gmail.com","threadId":"56409","inReplyTo":"d291d3723a6bb8e1d4f871b5279063c3e0a3fd79.1631129086.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 07/15] scalar: implement 'scalar list'","fromName":"Bagas Sanjaya","fromEmail":"bagasdotme@gmail.com","sentAt":"2021-09-09T06:11:36Z","receivedAt":"2021-09-09T06:11:44Z","isPatch":true,"sender":{"key":"bagasdotme@gmail.com","avatar":"https://avatars.githubusercontent.com/u/40219486?v=4"},"body":"On 09/09/21 02.24, Derrick Stolee via GitGitGadget wrote:\n> +List\n> +~~~~\n> +\n> +list::\n> +\tList enlistments that are currently registered by Scalar. This\n> +\tsubcommand does not need to be run inside an enlistment.\n> +\n\nLooks OK.\n\n-- \nAn old man doll... just what I always wanted! - Clara\n"},{"id":"435232","messageId":"87r1dydp4m.fsf@evledraar.gmail.com","threadId":"56409","inReplyTo":"pull.1005.v3.git.1631129086.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 00/15] [RFC] Upstreaming the Scalar command","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-09-09T10:14:57Z","receivedAt":"2021-09-09T10:23:13Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Sep 08 2021, Johannes Schindelin via GitGitGadget wrote:\n\n> Changes since v2:\n>\n>  * Adjusted the description of the list command in the manual page , as\n>    suggested by Bagas.\n>  * Addressed two style nits in cmd_run().\n>  * The documentation of git reconfigure -a was improved.\n>\n> Changes since v1:\n>\n>  * A couple typos were fixed\n>  * The code parsing the output of ls-remote was made more readable\n>  * The indentation used in scalar.txt now consistently uses tabs\n>  * We no longer hard-code core.bare = false when registering with Scalar\n\nIn the summary I had on v1->v2 points 1-3 are for v2->v3, respectively,\noutstanding, addressed, outstanding:\n\n    https://lore.kernel.org/git/877dfupl7o.fsf@evledraar.gmail.com/\n\nIn addition the discussion ending here:\nhttps://lore.kernel.org/git/nycvar.QRO.7.76.6.2109082112270.55@tvgsbejvaqbjf.bet/\n\nFor that point: I think it's fair enough not to properly handle the\ncleanup case in \"scalar clone\", but perhaps add a note in the commit\nmessage that unlike \"git clone\" this is known not to clean after itself\nproperly on ctrl+c?\n"},{"id":"435235","messageId":"87mtomdoiv.fsf@evledraar.gmail.com","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2109082049290.55@tvgsbejvaqbjf.bet","subject":"Re: [PATCH 08/15] scalar: implement the `clone` subcommand","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-09-09T10:29:44Z","receivedAt":"2021-09-09T10:36:12Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Sep 08 2021, Johannes Schindelin wrote:\n\n> [...]\n> Which means that I want to weigh how much effort to put into polishing an\n> unlikely code path on one side, and on the other side how much effort to\n> put into moving the functionality away from `contrib/` and deleting that\n> unlikely code path.\n>\n> In the same vein, while this patch series contains (mostly) code in\n> `contrib/` (and therefore technically does not need to adhere strictly to\n> Git's code style), it is probably wise to pay closer attention to the code\n> style particularly in those parts that are prone to be moved verbatim (or\n> close to verbatim) to Git proper.\n\nI don't think we have any such exception to our usual style & preferred\ncode patterns in contrib/* or compat/* in general. In the latter case we\nhave e.g. compat/regex/ and other externally-imported codebases, which\nwe've tried to stylistically modify as little as possible to make\nsubsequent imports easier, ditto sha1dc/ etc.\n\nI think the general (but unwritten) rule has been to draw the\ndistinction on whether or not code is still externally maintained and\nexpected to be imported, or if it's expected to be maintained in git.git\ngoing forward.\n\nI think that this proposed series falls thoroughly in the latter\ncategory, but maybe I've misunderstood it..\n\nAlso re my [1] I had some (still relevant, but unaddressed) points on v1\nabout how placing this in contrib/* made certain aspects of integrating\nit into our build system harder. I was imagining that distinction as\npurely an internal implementation detail to git.git (make install\netc. would behave the same), but per the above it seems to come with\ndeeper connotations than that at least in your mind.\n\n1. https://lore.kernel.org/git/87r1dydp4m.fsf@evledraar.gmail.com\n\n"},{"id":"435276","messageId":"CABPp-BGxYVP6X6v5eu4grZUgDb6hmHYVDEwT1Cu4u592KeQ1fA@mail.gmail.com","threadId":"56409","inReplyTo":"b8c7d3f84508ae0fb300f47c726764f4cbf46be9.1631129086.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 01/15] scalar: create a rudimentary executable","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-09-09T15:36:45Z","receivedAt":"2021-09-09T15:37:00Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Wed, Sep 8, 2021 at 12:24 PM Johannes Schindelin via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> From: Johannes Schindelin <johannes.schindelin@gmx.de>\n>\n> The idea of Scalar (https://github.com/microsoft/scalar), and before\n> that, of VFS for Git, has always been to prove that Git _can_ scale, and\n> to upstream whatever strategies have been demonstrated to help.\n>\n> With this patch, we start the journey from that C# project to move what\n> is left to Git's own `contrib/` directory, reimplementing it in pure C,\n> with the intention to facilitate integrating the functionality into core\n> Git all while maintaining backwards-compatibility for existing Scalar\n> users (which will be much easier when both live in the same worktree).\n> It was always to plan to contribute all of the proven strategies back to\n> core Git.\n\ns/always to plan/always the plan/\n\n> For example, while the virtual filesystem provided by VFS for Git helped\n> the team developing the Windows operating system to move onto Git, while\n> trying to upstream it we realized that it cannot be done: getting the\n> virtual filesystem to work (which we only managed to implement fully on\n> Windows, but not on, say, macOS or Linux), and the required server-side\n> support for the GVFS protocol, made this not quite feasible.\n>\n> The Scalar project learned from that and tackled the problem with\n> different tactics: instead of pretending to Git that the working\n> directory is fully populated, it _specifically_ teaches Git about\n> partial clone (which is based on VFS for Git's cache server), about\n> sparse checkout (which VFS for Git tried to do transparently, in the\n> file system layer), and regularly runs maintenance tasks to keep the\n> repository in a healthy state.\n>\n> With partial clone, sparse checkout and `git maintenance` having been\n> upstreamed, there is little left that `scalar.exe` does that which\n> `git.exe` cannot do. One such thing is that `scalar clone <url>` will\n> automatically set up a partial, sparse clone, and configure\n> known-helpful settings from the start.\n\ns/does that which/does which/\n\n> So let's bring this convenience into Git's tree.\n>\n> The idea here is that you can (optionally) build Scalar via\n>\n>         make -C contrib/scalar/Makefile\n>\n> This will build the `scalar` executable and put it into the\n> contrib/scalar/ subdirectory.\n>\n> The slightly awkward addition of the `contrib/scalar/*` bits to the\n> top-level `Makefile` are actually really required: we want to link to\n> `libgit.a`, which means that we will need to use the very same `CFLAGS`\n> and `LDFLAGS` as the rest of Git.\n>\n> An early development version of this patch tried to replicate all the\n> conditional code in `contrib/scalar/Makefile` (e.g. `NO_POLL`) just like\n> `contrib/svn-fe/Makefile` used to do before it was retired. It turned\n> out to be quite the whack-a-mole game: the SHA-1-related flags, the\n> flags enabling/disabling `compat/poll/`, `compat/regex/`,\n> `compat/win32mmap.c` & friends depending on the current platform... To\n> put it mildly: it was a major mess.\n>\n> Instead, this patch makes minimal changes to the top-level `Makefile` so\n> that the bits in `contrib/scalar/` can be compiled and linked, and\n> adds a `contrib/scalar/Makefile` that uses the top-level `Makefile` in a\n> most minimal way to do the actual compiling.\n>\n> Note: With this commit, we only establish the infrastructure, no\n> Scalar functionality is implemented yet; We will do that incrementally\n> over the next few commits.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  Makefile                  |  8 ++++++++\n>  contrib/scalar/.gitignore |  2 ++\n>  contrib/scalar/Makefile   | 34 ++++++++++++++++++++++++++++++++++\n>  contrib/scalar/scalar.c   | 36 ++++++++++++++++++++++++++++++++++++\n>  4 files changed, 80 insertions(+)\n>  create mode 100644 contrib/scalar/.gitignore\n>  create mode 100644 contrib/scalar/Makefile\n>  create mode 100644 contrib/scalar/scalar.c\n>\n> diff --git a/Makefile b/Makefile\n> index c3565fc0f8f..2d5c822f7a8 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -2447,6 +2447,10 @@ endif\n>  .PHONY: objects\n>  objects: $(OBJECTS)\n>\n> +SCALAR_SOURCES := contrib/scalar/scalar.c\n> +SCALAR_OBJECTS := $(SCALAR_SOURCES:c=o)\n> +OBJECTS += $(SCALAR_OBJECTS)\n> +\n>  dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d)\n>  dep_dirs := $(addsuffix .depend,$(sort $(dir $(OBJECTS))))\n>\n> @@ -2586,6 +2590,10 @@ $(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS\n>         $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n>                 $(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)\n>\n> +contrib/scalar/scalar$X: $(SCALAR_OBJECTS) GIT-LDFLAGS $(GITLIBS)\n> +       $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \\\n> +               $(filter %.o,$^) $(LIBS)\n> +\n>  $(LIB_FILE): $(LIB_OBJS)\n>         $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^\n>\n> diff --git a/contrib/scalar/.gitignore b/contrib/scalar/.gitignore\n> new file mode 100644\n> index 00000000000..ff3d47e84d0\n> --- /dev/null\n> +++ b/contrib/scalar/.gitignore\n> @@ -0,0 +1,2 @@\n> +/*.exe\n> +/scalar\n> diff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\n> new file mode 100644\n> index 00000000000..40c03ad10e1\n> --- /dev/null\n> +++ b/contrib/scalar/Makefile\n> @@ -0,0 +1,34 @@\n> +QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir\n> +QUIET_SUBDIR1  =\n> +\n> +ifneq ($(findstring s,$(MAKEFLAGS)),s)\n> +ifndef V\n> +       QUIET_SUBDIR0  = +@subdir=\n> +       QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n> +                        $(MAKE) $(PRINT_DIR) -C $$subdir\n> +else\n> +       export V\n> +endif\n> +endif\n> +\n> +all:\n> +\n> +include ../../config.mak.uname\n> +-include ../../config.mak.autogen\n> +-include ../../config.mak\n> +\n> +TARGETS = scalar$(X) scalar.o\n> +GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n> +\n> +all: scalar$X\n> +\n> +$(GITLIBS):\n> +       $(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)\n> +\n> +$(TARGETS): $(GITLIBS) scalar.c\n> +       $(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)\n> +\n> +clean:\n> +       $(RM) $(TARGETS)\n> +\n> +.PHONY: all clean FORCE\n> diff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\n> new file mode 100644\n> index 00000000000..7cff29e0fcd\n> --- /dev/null\n> +++ b/contrib/scalar/scalar.c\n> @@ -0,0 +1,36 @@\n> +/*\n> + * The Scalar command-line interface.\n> + */\n> +\n> +#include \"cache.h\"\n> +#include \"gettext.h\"\n> +#include \"parse-options.h\"\n> +\n> +static struct {\n> +       const char *name;\n> +       int (*fn)(int, const char **);\n> +} builtins[] = {\n> +       { NULL, NULL},\n> +};\n> +\n> +int cmd_main(int argc, const char **argv)\n> +{\n> +       struct strbuf scalar_usage = STRBUF_INIT;\n> +       int i;\n> +\n> +       if (argc > 1) {\n> +               argv++;\n> +               argc--;\n> +\n> +               for (i = 0; builtins[i].name; i++)\n> +                       if (!strcmp(builtins[i].name, argv[0]))\n> +                               return !!builtins[i].fn(argc, argv);\n> +       }\n> +\n> +       strbuf_addstr(&scalar_usage,\n> +                     N_(\"scalar <command> [<options>]\\n\\nCommands:\\n\"));\n> +       for (i = 0; builtins[i].name; i++)\n> +               strbuf_addf(&scalar_usage, \"\\t%s\\n\", builtins[i].name);\n> +\n> +       usage(scalar_usage.buf);\n> +}\n> --\n> gitgitgadget\n>\n"},{"id":"435718","messageId":"nycvar.QRO.7.76.6.2109131531210.55@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"CABPp-BGxYVP6X6v5eu4grZUgDb6hmHYVDEwT1Cu4u592KeQ1fA@mail.gmail.com","subject":"Re: [PATCH v3 01/15] scalar: create a rudimentary executable","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-09-13T13:32:50Z","receivedAt":"2021-09-13T13:34:55Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Elijah,\n\nOn Thu, 9 Sep 2021, Elijah Newren wrote:\n\n> On Wed, Sep 8, 2021 at 12:24 PM Johannes Schindelin via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n> >\n> > From: Johannes Schindelin <johannes.schindelin@gmx.de>\n> >\n> > The idea of Scalar (https://github.com/microsoft/scalar), and before\n> > that, of VFS for Git, has always been to prove that Git _can_ scale, and\n> > to upstream whatever strategies have been demonstrated to help.\n> >\n> > With this patch, we start the journey from that C# project to move what\n> > is left to Git's own `contrib/` directory, reimplementing it in pure C,\n> > with the intention to facilitate integrating the functionality into core\n> > Git all while maintaining backwards-compatibility for existing Scalar\n> > users (which will be much easier when both live in the same worktree).\n> > It was always to plan to contribute all of the proven strategies back to\n> > core Git.\n>\n> s/always to plan/always the plan/\n>\n> > For example, while the virtual filesystem provided by VFS for Git helped\n> > the team developing the Windows operating system to move onto Git, while\n> > trying to upstream it we realized that it cannot be done: getting the\n> > virtual filesystem to work (which we only managed to implement fully on\n> > Windows, but not on, say, macOS or Linux), and the required server-side\n> > support for the GVFS protocol, made this not quite feasible.\n> >\n> > The Scalar project learned from that and tackled the problem with\n> > different tactics: instead of pretending to Git that the working\n> > directory is fully populated, it _specifically_ teaches Git about\n> > partial clone (which is based on VFS for Git's cache server), about\n> > sparse checkout (which VFS for Git tried to do transparently, in the\n> > file system layer), and regularly runs maintenance tasks to keep the\n> > repository in a healthy state.\n> >\n> > With partial clone, sparse checkout and `git maintenance` having been\n> > upstreamed, there is little left that `scalar.exe` does that which\n> > `git.exe` cannot do. One such thing is that `scalar clone <url>` will\n> > automatically set up a partial, sparse clone, and configure\n> > known-helpful settings from the start.\n>\n> s/does that which/does which/\n\nThank you!\n\nI am holding off from sending a new iteration (with the suggested fixes)\nuntil tomorrow, waiting for more suggestions to trickle in.\n\nThanks,\nDscho\n\n>\n> > So let's bring this convenience into Git's tree.\n> >\n> > The idea here is that you can (optionally) build Scalar via\n> >\n> >         make -C contrib/scalar/Makefile\n> >\n> > This will build the `scalar` executable and put it into the\n> > contrib/scalar/ subdirectory.\n> >\n> > The slightly awkward addition of the `contrib/scalar/*` bits to the\n> > top-level `Makefile` are actually really required: we want to link to\n> > `libgit.a`, which means that we will need to use the very same `CFLAGS`\n> > and `LDFLAGS` as the rest of Git.\n> >\n> > An early development version of this patch tried to replicate all the\n> > conditional code in `contrib/scalar/Makefile` (e.g. `NO_POLL`) just like\n> > `contrib/svn-fe/Makefile` used to do before it was retired. It turned\n> > out to be quite the whack-a-mole game: the SHA-1-related flags, the\n> > flags enabling/disabling `compat/poll/`, `compat/regex/`,\n> > `compat/win32mmap.c` & friends depending on the current platform... To\n> > put it mildly: it was a major mess.\n> >\n> > Instead, this patch makes minimal changes to the top-level `Makefile` so\n> > that the bits in `contrib/scalar/` can be compiled and linked, and\n> > adds a `contrib/scalar/Makefile` that uses the top-level `Makefile` in a\n> > most minimal way to do the actual compiling.\n> >\n> > Note: With this commit, we only establish the infrastructure, no\n> > Scalar functionality is implemented yet; We will do that incrementally\n> > over the next few commits.\n> >\n> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > ---\n> >  Makefile                  |  8 ++++++++\n> >  contrib/scalar/.gitignore |  2 ++\n> >  contrib/scalar/Makefile   | 34 ++++++++++++++++++++++++++++++++++\n> >  contrib/scalar/scalar.c   | 36 ++++++++++++++++++++++++++++++++++++\n> >  4 files changed, 80 insertions(+)\n> >  create mode 100644 contrib/scalar/.gitignore\n> >  create mode 100644 contrib/scalar/Makefile\n> >  create mode 100644 contrib/scalar/scalar.c\n> >\n> > diff --git a/Makefile b/Makefile\n> > index c3565fc0f8f..2d5c822f7a8 100644\n> > --- a/Makefile\n> > +++ b/Makefile\n> > @@ -2447,6 +2447,10 @@ endif\n> >  .PHONY: objects\n> >  objects: $(OBJECTS)\n> >\n> > +SCALAR_SOURCES := contrib/scalar/scalar.c\n> > +SCALAR_OBJECTS := $(SCALAR_SOURCES:c=o)\n> > +OBJECTS += $(SCALAR_OBJECTS)\n> > +\n> >  dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d)\n> >  dep_dirs := $(addsuffix .depend,$(sort $(dir $(OBJECTS))))\n> >\n> > @@ -2586,6 +2590,10 @@ $(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS\n> >         $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n> >                 $(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)\n> >\n> > +contrib/scalar/scalar$X: $(SCALAR_OBJECTS) GIT-LDFLAGS $(GITLIBS)\n> > +       $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \\\n> > +               $(filter %.o,$^) $(LIBS)\n> > +\n> >  $(LIB_FILE): $(LIB_OBJS)\n> >         $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^\n> >\n> > diff --git a/contrib/scalar/.gitignore b/contrib/scalar/.gitignore\n> > new file mode 100644\n> > index 00000000000..ff3d47e84d0\n> > --- /dev/null\n> > +++ b/contrib/scalar/.gitignore\n> > @@ -0,0 +1,2 @@\n> > +/*.exe\n> > +/scalar\n> > diff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\n> > new file mode 100644\n> > index 00000000000..40c03ad10e1\n> > --- /dev/null\n> > +++ b/contrib/scalar/Makefile\n> > @@ -0,0 +1,34 @@\n> > +QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir\n> > +QUIET_SUBDIR1  =\n> > +\n> > +ifneq ($(findstring s,$(MAKEFLAGS)),s)\n> > +ifndef V\n> > +       QUIET_SUBDIR0  = +@subdir=\n> > +       QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n> > +                        $(MAKE) $(PRINT_DIR) -C $$subdir\n> > +else\n> > +       export V\n> > +endif\n> > +endif\n> > +\n> > +all:\n> > +\n> > +include ../../config.mak.uname\n> > +-include ../../config.mak.autogen\n> > +-include ../../config.mak\n> > +\n> > +TARGETS = scalar$(X) scalar.o\n> > +GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n> > +\n> > +all: scalar$X\n> > +\n> > +$(GITLIBS):\n> > +       $(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)\n> > +\n> > +$(TARGETS): $(GITLIBS) scalar.c\n> > +       $(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)\n> > +\n> > +clean:\n> > +       $(RM) $(TARGETS)\n> > +\n> > +.PHONY: all clean FORCE\n> > diff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\n> > new file mode 100644\n> > index 00000000000..7cff29e0fcd\n> > --- /dev/null\n> > +++ b/contrib/scalar/scalar.c\n> > @@ -0,0 +1,36 @@\n> > +/*\n> > + * The Scalar command-line interface.\n> > + */\n> > +\n> > +#include \"cache.h\"\n> > +#include \"gettext.h\"\n> > +#include \"parse-options.h\"\n> > +\n> > +static struct {\n> > +       const char *name;\n> > +       int (*fn)(int, const char **);\n> > +} builtins[] = {\n> > +       { NULL, NULL},\n> > +};\n> > +\n> > +int cmd_main(int argc, const char **argv)\n> > +{\n> > +       struct strbuf scalar_usage = STRBUF_INIT;\n> > +       int i;\n> > +\n> > +       if (argc > 1) {\n> > +               argv++;\n> > +               argc--;\n> > +\n> > +               for (i = 0; builtins[i].name; i++)\n> > +                       if (!strcmp(builtins[i].name, argv[0]))\n> > +                               return !!builtins[i].fn(argc, argv);\n> > +       }\n> > +\n> > +       strbuf_addstr(&scalar_usage,\n> > +                     N_(\"scalar <command> [<options>]\\n\\nCommands:\\n\"));\n> > +       for (i = 0; builtins[i].name; i++)\n> > +               strbuf_addf(&scalar_usage, \"\\t%s\\n\", builtins[i].name);\n> > +\n> > +       usage(scalar_usage.buf);\n> > +}\n> > --\n> > gitgitgadget\n> >\n>\n"},{"id":"435719","messageId":"87ilz44kdk.fsf@evledraar.gmail.com","threadId":"56409","inReplyTo":"87r1dydp4m.fsf@evledraar.gmail.com","subject":"Re: [PATCH v3 00/15] [RFC] Upstreaming the Scalar command","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-09-13T14:20:40Z","receivedAt":"2021-09-13T15:28:24Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Sep 09 2021, Ævar Arnfjörð Bjarmason wrote:\n\n> On Wed, Sep 08 2021, Johannes Schindelin via GitGitGadget wrote:\n>\n>> Changes since v2:\n>>\n>>  * Adjusted the description of the list command in the manual page , as\n>>    suggested by Bagas.\n>>  * Addressed two style nits in cmd_run().\n>>  * The documentation of git reconfigure -a was improved.\n>>\n>> Changes since v1:\n>>\n>>  * A couple typos were fixed\n>>  * The code parsing the output of ls-remote was made more readable\n>>  * The indentation used in scalar.txt now consistently uses tabs\n>>  * We no longer hard-code core.bare = false when registering with Scalar\n>\n> In the summary I had on v1->v2 points 1-3 are for v2->v3, respectively,\n> outstanding, addressed, outstanding:\n>\n>     https://lore.kernel.org/git/877dfupl7o.fsf@evledraar.gmail.com/\n>\n> In addition the discussion ending here:\n> https://lore.kernel.org/git/nycvar.QRO.7.76.6.2109082112270.55@tvgsbejvaqbjf.bet/\n>\n> For that point: I think it's fair enough not to properly handle the\n> cleanup case in \"scalar clone\", but perhaps add a note in the commit\n> message that unlike \"git clone\" this is known not to clean after itself\n> properly on ctrl+c?\n\nSeeing [1] about the planned re-roll I have the above a shot a few days\nago, see the original discussion at [2] (indirectly linked above).\n\nThe dependency graph isn't quite there yet, but I basically had it\nworking (some twiddling around adding a new top-level command name\nneeded). Result:\n    \n     .gitignore             |  1 +\n     Documentation/Makefile |  4 ++++\n     Makefile               | 27 +++++++++++++++++++++++++++\n     3 files changed, 32 insertions(+)\n\nAnd by comparison, your v3:\n    \n     Makefile                  |  8 +++++\n     contrib/scalar/.gitignore |  5 +++\n     contrib/scalar/Makefile   | 57 ++++++++++++++++++++++++++++++++++\n     contrib/scalar/t/Makefile | 78 +++++++++++++++++++++++++++++++++++++++++++++++\n     4 files changed, 148 insertions(+)\n\nSo that's a very pleasing reduction in complexity.\n\nThe WIP change for that is below, some oddities like the\ns/scalarscalar/scalar/ (couldn't find the bit that generated that\nbuilt-in blurb at the time).\n\nBut for one the automatic lint integration in Documentation/ found one\nnit, and \"make test\" etc. all work with this automatically.\n\nAll guarded behind a CONTRIB_SCALAR flag, so the end result isn't in any\nway different.\n\nAgain, as pointed out in [2] the proposal isn't in any way to change\nwhat an end user sees, just to make our build system less\ncomplex. Stretching dependency graphs across Makefiles is a pain.\n\nSo just as an example I was improving the \"sparse\" and \"hdr-check\"\ntargets today, with this dropped into the main Makefile under a flag\nstuff like that will Just Work, if it's under its own Makefile\ninfrastructure it'll need to be treated specially for every such change,\nditto \"make TAGS\" etc.\n\n1. http://lore.kernel.org/git/nycvar.QRO.7.76.6.2109131531210.55@tvgsbejvaqbjf.bet;\n2. https://lore.kernel.org/git/87mtoxwt63.fsf@evledraar.gmail.com/\n\ndiff --git a/.gitignore b/.gitignore\nindex 311841f9bed..491cb2177af 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -216,6 +216,7 @@\n /configure\n /.vscode/\n /tags\n+/scalar\n /TAGS\n /cscope*\n /compile_commands.json\ndiff --git a/Documentation/Makefile b/Documentation/Makefile\nindex f5605b7767f..f0a03faf40f 100644\n--- a/Documentation/Makefile\n+++ b/Documentation/Makefile\n@@ -19,6 +19,10 @@ MAN1_TXT += git.txt\n MAN1_TXT += gitk.txt\n MAN1_TXT += gitweb.txt\n \n+ifdef CONTRIB_SCALAR\n+MAN1_TXT += scalar.txt\n+endif\n+\n # man5 / man7 guides (note: new guides should also be added to command-list.txt)\n MAN5_TXT += gitattributes.txt\n MAN5_TXT += githooks.txt\ndiff --git a/Makefile b/Makefile\nindex 429c276058d..7407df45b2a 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -584,6 +584,7 @@ FUZZ_OBJS =\n FUZZ_PROGRAMS =\n GIT_OBJS =\n LIB_OBJS =\n+NONGIT_PROGRAM_OBJS =\n OBJECTS =\n PROGRAM_OBJS =\n PROGRAMS =\n@@ -691,10 +692,17 @@ PROGRAM_OBJS += shell.o\n .PHONY: program-objs\n program-objs: $(PROGRAM_OBJS)\n \n+ifdef CONTRIB_SCALAR\n+NONGIT_PROGRAM_OBJS += scalar.o\n+endif\n+.PHONY: nongit-program-objs\n+nongit-program-objs: $(NONGIT_PROGRAM_OBJS)\n+\n # Binary suffix, set to .exe for Windows builds\n X =\n \n PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS))\n+PROGRAMS += $(patsubst %.o,%$X,$(NONGIT_PROGRAM_OBJS))\n \n TEST_BUILTINS_OBJS += test-advise.o\n TEST_BUILTINS_OBJS += test-bitmap.o\n@@ -800,6 +808,9 @@ BINDIR_PROGRAMS_NEED_X += git-receive-pack\n BINDIR_PROGRAMS_NEED_X += git-shell\n BINDIR_PROGRAMS_NEED_X += git-upload-archive\n BINDIR_PROGRAMS_NEED_X += git-upload-pack\n+ifdef CONTRIB_SCALAR\n+BINDIR_PROGRAMS_NEED_X += scalar\n+endif\n \n BINDIR_PROGRAMS_NO_X += git-cvsserver\n \n@@ -1247,6 +1258,10 @@ else\n ALL_COMMANDS_TO_INSTALL += git-receive-pack$(X)\n ALL_COMMANDS_TO_INSTALL += git-upload-archive$(X)\n ALL_COMMANDS_TO_INSTALL += git-upload-pack$(X)\n+\n+ifdef CONTRIB_SCALAR\n+ALL_COMMANDS_TO_INSTALL += scalar$(X)\n+endif\n endif\n \n ALL_CFLAGS = $(DEVELOPER_CFLAGS) $(CPPFLAGS) $(CFLAGS)\n@@ -2459,6 +2474,7 @@ git-objs: $(GIT_OBJS)\n \n OBJECTS += $(GIT_OBJS)\n OBJECTS += $(PROGRAM_OBJS)\n+OBJECTS += $(NONGIT_PROGRAM_OBJS)\n OBJECTS += $(TEST_OBJS)\n OBJECTS += $(XDIFF_OBJS)\n OBJECTS += $(FUZZ_OBJS)\n@@ -2582,6 +2598,9 @@ compat/nedmalloc/nedmalloc.sp compat/nedmalloc/nedmalloc.o: EXTRA_CPPFLAGS = \\\n compat/nedmalloc/nedmalloc.sp: SP_EXTRA_FLAGS += -Wno-non-pointer-null\n endif\n \n+ifdef CONTRIB_SCALAR\n+# TODO: Implicit rule for git-scalar here\n+endif\n git-%$X: %.o GIT-LDFLAGS $(GITLIBS)\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)\n \n@@ -2596,6 +2615,11 @@ git-http-push$X: http.o http-push.o GIT-LDFLAGS $(GITLIBS)\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n \t\t$(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)\n \n+ifdef CONTRIB_SCALAR\n+scalar: scalar.o $(GITLIBS)\n+\t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)\n+endif\n+\n $(REMOTE_CURL_ALIASES): $(REMOTE_CURL_PRIMARY)\n \t$(QUIET_LNCP)$(RM) $@ && \\\n \tln $< $@ 2>/dev/null || \\\n@@ -2800,6 +2824,7 @@ GIT-BUILD-OPTIONS: FORCE\n \t@echo SHELL_PATH=\\''$(subst ','\\'',$(SHELL_PATH_SQ))'\\' >$@+\n \t@echo TEST_SHELL_PATH=\\''$(subst ','\\'',$(TEST_SHELL_PATH_SQ))'\\' >>$@+\n \t@echo PERL_PATH=\\''$(subst ','\\'',$(PERL_PATH_SQ))'\\' >>$@+\n+\t@echo CONTRIB_SCALAR=\\''$(subst ','\\'',$(subst ','\\'',$(CONTRIB_SCALAR)))'\\' >>$@+\n \t@echo DIFF=\\''$(subst ','\\'',$(subst ','\\'',$(DIFF)))'\\' >>$@+\n \t@echo PYTHON_PATH=\\''$(subst ','\\'',$(PYTHON_PATH_SQ))'\\' >>$@+\n \t@echo TAR=\\''$(subst ','\\'',$(subst ','\\'',$(TAR)))'\\' >>$@+\n@@ -2881,6 +2906,8 @@ bin-wrappers/%: wrap-for-bin.sh\n \t$(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \\\n \t     -e 's|@@BUILD_DIR@@|$(shell pwd)|' \\\n \t     -e 's|@@PROG@@|$(patsubst test-%,t/helper/test-%$(X),$(@F))$(patsubst git%,$(X),$(filter $(@F),$(BINDIR_PROGRAMS_NEED_X)))|' < $< > $@ && \\\n+\tsed -e 's|scalarscalar|scalar|' <$@ >$@+ && \\\n+\tmv $@+ $@ && \\\n \tchmod +x $@\n"},{"id":"435790","messageId":"nycvar.QRO.7.76.6.2109131914080.55@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"87ilz44kdk.fsf@evledraar.gmail.com","subject":"Re: [PATCH v3 00/15] [RFC] Upstreaming the Scalar command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-09-13T20:53:05Z","receivedAt":"2021-09-13T20:53:33Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Ævar,\n\nOn Mon, 13 Sep 2021, Ævar Arnfjörð Bjarmason wrote:\n\n>\n> On Thu, Sep 09 2021, Ævar Arnfjörð Bjarmason wrote:\n>\n> > On Wed, Sep 08 2021, Johannes Schindelin via GitGitGadget wrote:\n> >\n> >> Changes since v2:\n> >>\n> >>  * Adjusted the description of the list command in the manual page , as\n> >>    suggested by Bagas.\n> >>  * Addressed two style nits in cmd_run().\n> >>  * The documentation of git reconfigure -a was improved.\n> >>\n> >> Changes since v1:\n> >>\n> >>  * A couple typos were fixed\n> >>  * The code parsing the output of ls-remote was made more readable\n> >>  * The indentation used in scalar.txt now consistently uses tabs\n> >>  * We no longer hard-code core.bare = false when registering with Scalar\n> >\n> > In the summary I had on v1->v2 points 1-3 are for v2->v3, respectively,\n> > outstanding, addressed, outstanding:\n> >\n> >     https://lore.kernel.org/git/877dfupl7o.fsf@evledraar.gmail.com/\n> >\n> > In addition the discussion ending here:\n> > https://lore.kernel.org/git/nycvar.QRO.7.76.6.2109082112270.55@tvgsbejvaqbjf.bet/\n> >\n> > For that point: I think it's fair enough not to properly handle the\n> > cleanup case in \"scalar clone\", but perhaps add a note in the commit\n> > message that unlike \"git clone\" this is known not to clean after itself\n> > properly on ctrl+c?\n>\n> Seeing [1] about the planned re-roll I have the above a shot a few days\n> ago, see the original discussion at [2] (indirectly linked above).\n\nThere is a good reason why I did not engage in that tangent about\ndeviating from the established `contrib/*/Makefile` paradigm: I find it\nparticularly unrelated to what this here patch series is trying to\naccomplish, and I cannot bring myself to be interested in the proposed\nbuild system changes, either, because I do not see any benefit in the\nchanges, only downsides.\n\nI find the distraction unnecessary.\n\nBesides, the way I designed it, the code in `contrib/scalar/` intrudes as\nlittle as possible on the core Git build system. The impact on the\ntop-level `Makefile` is quite minimal, which is just the way I firmly\nbelieve it should be.\n\nIn short: I do not want those intrusive changes to the top-level\n`Makefile`, not in this patch series, and not as a follow-up, either.\n\nWe have much bigger fries to fry: namely, how to migrate the improvements\nfor large-scale operations from Scalar to core Git, so that all Git users\ncan benefit. Granted, it will take a lot effort, and it would be easier to\nmove around `Makefile` rules instead. But ultimately, the benefit of\nallowing users to handle larger repositories with ease will be worth that\neffort.\n\nCiao,\nJohannes\n"},{"id":"435841","messageId":"87r1dro2kf.fsf@evledraar.gmail.com","threadId":"56409","inReplyTo":"b8c7d3f84508ae0fb300f47c726764f4cbf46be9.1630691688.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 01/15] scalar: create a rudimentary executable","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-09-14T10:47:04Z","receivedAt":"2021-09-14T10:48:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Sep 03 2021, Johannes Schindelin via GitGitGadget wrote:\n\n> diff --git a/Makefile b/Makefile\n> index c3565fc0f8f..2d5c822f7a8 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -2447,6 +2447,10 @@ endif\n>  .PHONY: objects\n>  objects: $(OBJECTS)\n>  \n> +SCALAR_SOURCES := contrib/scalar/scalar.c\n> +SCALAR_OBJECTS := $(SCALAR_SOURCES:c=o)\n> +OBJECTS += $(SCALAR_OBJECTS)\n> +\n>  dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d)\n>  dep_dirs := $(addsuffix .depend,$(sort $(dir $(OBJECTS))))\n>  \n\nAside from anything else we may disagree with about the build system\nintegration, this breaks the \"objects\" target. You're adding things to\nit, but it won't be reflected by those changes.\n\nI have this fixup for it on top locally, the reference to 852ec00310 is\nto a commit in your repo:\n\n-- >8 --\nMakefile: fix scalar \"make objects\" regression\n\nIn 852ec00310 (scalar: create a rudimentary executable, 2021-04-10)\nthe contrib/scalar/scalar.o file was added to OBJECTS, but was added\nbelow the \"objects\" target.\n\nThat target was added in 029bac01a8 (Makefile: add\n{program,xdiff,test,git,fuzz}-objs & objects targets, 2021-02-23)\nalong with others (git-objs, test-objs) to serve as ad-hoc targets for\ne.g. compiling during interactive the targets need to come after we\nfully declare the variable.\n\nBefore this change we'd still do /some/ scalar things on \"make\nobjects\", namely create the contrib/scalar/.depend/ directory (see\ndep_dirs in the context), we just wouldn't create the object itself.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Makefile | 5 ++---\n 1 file changed, 2 insertions(+), 3 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 6e14f626e3..6ace4be141 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2464,12 +2464,11 @@ OBJECTS += $(FUZZ_OBJS)\n ifndef NO_CURL\n \tOBJECTS += http.o http-walker.o remote-curl.o\n endif\n-.PHONY: objects\n-objects: $(OBJECTS)\n-\n SCALAR_SOURCES := contrib/scalar/scalar.c\n SCALAR_OBJECTS := $(SCALAR_SOURCES:c=o)\n OBJECTS += $(SCALAR_OBJECTS)\n+.PHONY: objects\n+objects: $(OBJECTS)\n \n dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d)\n dep_dirs := $(addsuffix .depend,$(sort $(dir $(OBJECTS))))\n-- \n2.33.0.1013.ge8323766266\n\n"},{"id":"435843","messageId":"87mtofnzv1.fsf@evledraar.gmail.com","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2109131914080.55@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v3 00/15] [RFC] Upstreaming the Scalar command","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-09-14T10:59:56Z","receivedAt":"2021-09-14T11:47:05Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Sep 13 2021, Johannes Schindelin wrote:\n\n> Hi Ævar,\n>\n> On Mon, 13 Sep 2021, Ævar Arnfjörð Bjarmason wrote:\n>\n>>\n>> On Thu, Sep 09 2021, Ævar Arnfjörð Bjarmason wrote:\n>>\n>> > On Wed, Sep 08 2021, Johannes Schindelin via GitGitGadget wrote:\n>> >\n>> >> Changes since v2:\n>> >>\n>> >>  * Adjusted the description of the list command in the manual page , as\n>> >>    suggested by Bagas.\n>> >>  * Addressed two style nits in cmd_run().\n>> >>  * The documentation of git reconfigure -a was improved.\n>> >>\n>> >> Changes since v1:\n>> >>\n>> >>  * A couple typos were fixed\n>> >>  * The code parsing the output of ls-remote was made more readable\n>> >>  * The indentation used in scalar.txt now consistently uses tabs\n>> >>  * We no longer hard-code core.bare = false when registering with Scalar\n>> >\n>> > In the summary I had on v1->v2 points 1-3 are for v2->v3, respectively,\n>> > outstanding, addressed, outstanding:\n>> >\n>> >     https://lore.kernel.org/git/877dfupl7o.fsf@evledraar.gmail.com/\n>> >\n>> > In addition the discussion ending here:\n>> > https://lore.kernel.org/git/nycvar.QRO.7.76.6.2109082112270.55@tvgsbejvaqbjf.bet/\n>> >\n>> > For that point: I think it's fair enough not to properly handle the\n>> > cleanup case in \"scalar clone\", but perhaps add a note in the commit\n>> > message that unlike \"git clone\" this is known not to clean after itself\n>> > properly on ctrl+c?\n>>\n>> Seeing [1] about the planned re-roll I have the above a shot a few days\n>> ago, see the original discussion at [2] (indirectly linked above).\n>\n> There is a good reason why I did not engage in that tangent about\n> deviating from the established `contrib/*/Makefile` paradigm: I find it\n> particularly unrelated to what this here patch series is trying to\n> accomplish, and I cannot bring myself to be interested in the proposed\n> build system changes, either, because I do not see any benefit in the\n> changes, only downsides.\n>\n> I find the distraction unnecessary.\n\nPerhaps I'm reading too much between the lines here, so forgive any\nundue knee-jerk reaction.\n\nBut aside from any technical disagreement we may have I find this way of\nhandling reviews quite disrespectful of other people's time.\n\nSure, maybe I'm wrong, and maybe you either don't see any value in the\nproposed changes or maybe they're just bad suggestions.\n\nI still took the time to review and comment on a series you're\nsubmitting. I think the least you can do is to include some comment in a\nre-roll like:\n\n    Skimmed Ævar's proposal about an alternate build system\n    implementation, sorry, I just don't think it's worth it, going to\n    not change anything there.\n\nWhich would be fair enough, and would leave the ball in my court in\nterms of either dropping it, submitting any patch on top etc.\n\nAs opposed to just ignoring that whole thread, leaving both me wondering\nif it's even been seen (and sending a few reminders like the linked\nupthread), as well as others trying to track the state of the series.\n\n> Besides, the way I designed it, the code in `contrib/scalar/` intrudes as\n> little as possible on the core Git build system. The impact on the\n> top-level `Makefile` is quite minimal, which is just the way I firmly\n> believe it should be.\n>\n> In short: I do not want those intrusive changes to the top-level\n> `Makefile`, not in this patch series, and not as a follow-up, either.\n\nCommunication grievances aside:\n\n * Saying that we have an \"established `contrib/*/Makefile` paradigm\"\n   doesn't really follow in this case. Most of that isn't C code, and\n   the bits that are C code are not using libgit.a.\n\n   And as I've argued elsewhere I think that whole pattern was a mistake\n   in the first place, it makes inter-Makefile dependencies a pain to\n   manage, has resulted in bitrot of things like git-subtree.sh and\n   mw-to-git, all because we conflate whether we want to build/test\n   things with what we'd like in a default installation.\n\n * Because of that any number of targets / workflows in the Makefile\n   aren't going to work by default, e.g. try checking it out and doing\n   \"make TAGS\".\n\n   That specific one happens to because we exclude contrib explicitly,\n   that could be fixed, but there's going to be any number of things\n   like that, but current and future ones.\n\n * One target that seems missing (maybe I've somehow missed it) is any\n   support for installing the build command, its docs etc.\n\n * I hacked a bit more on this today and came up with a not-quite-ready\n   change that both for the Makefile and Documentation/Makefile will\n   build scalar.c like any other top-level command, and we'll always get\n   a bin-wrapper/scalar, we'll only do something different at \"install\"\n   time.\n\n   This means that just like the \"all:: $(FUZZ_OBJS)\" we'll always build\n   a scalar.o, so that'll prevent others from e.g. breaking a library\n   function you rely on (which'll only be annoying caught in CI, or\n   integration or whatever).\n\n * I think it's fair to say that whatever one thinks of my argument,\n   your [1] leaves the reader hanging about *why* you went for this\n   \"make -C contrib/scalar/Makefile\".\n\n   As summarized there you tried to have it completely separate, but\n   failed.\n\n   So now there's a bit of integration in the top-level Makefile\n   already. But why try in the first place? Just slavish conformance to\n   existing \"contrib/\" convention?\n\n   The dependency back on assets in subdirs there is deep, so surely\n   it's not to build this independently somehow.\n\n> We have much bigger fries to fry: namely, how to migrate the improvements\n> for large-scale operations from Scalar to core Git, so that all Git users\n> can benefit. Granted, it will take a lot effort, and it would be easier to\n> move around `Makefile` rules instead. But ultimately, the benefit of\n> allowing users to handle larger repositories with ease will be worth that\n> effort.\n\nIt's your implementation that requires mostly moving around / copying\nMakefile rules, if you piggy-back on the existing Makefile rules you'll\nget most of the behavior you've duplicated for free without any moving\nor copying.\n\nBut in any case, re the last bullet point above and your \"[...]and not\nas a follow-up, either\" comments it's not clear whether you're saying\nthat you don't have time to work on this, or that you wouldn't want it\nat all in any shape or form, even if someone else did it.\n\nWhich is not to say that I'm promising to do so even if that's the case,\nI do think the onus is on the person proposing the change, and to take\nproductive feedback about things that are introducing unnecessary\ncomplexity, and won't saddle others with undue technical debt going\nforward.\n\n1. https://lore.kernel.org/git/b8c7d3f84508ae0fb300f47c726764f4cbf46be9.1631129086.git.gitgitgadget@gmail.com/\n"},{"id":"435851","messageId":"nycvar.QRO.7.76.6.2109141432520.59@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"87mtofnzv1.fsf@evledraar.gmail.com","subject":"Train station analogy, was Re: [PATCH v3 00/15] [RFC] Upstreaming the Scalar command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-09-14T14:24:53Z","receivedAt":"2021-09-14T14:25:06Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Ævar,\n\nOn Tue, 14 Sep 2021, Ævar Arnfjörð Bjarmason wrote:\n\n> On Mon, Sep 13 2021, Johannes Schindelin wrote:\n>\n> > On Mon, 13 Sep 2021, Ævar Arnfjörð Bjarmason wrote:\n> >\n> >> On Thu, Sep 09 2021, Ævar Arnfjörð Bjarmason wrote:\n> >>\n> >> > In the summary I had on v1->v2 points 1-3 are for v2->v3,\n> >> > respectively, outstanding, addressed, outstanding:\n> >> >\n> >> >     https://lore.kernel.org/git/877dfupl7o.fsf@evledraar.gmail.com/\n> >> >\n> >> > In addition the discussion ending here:\n> >> > https://lore.kernel.org/git/nycvar.QRO.7.76.6.2109082112270.55@tvgsbejvaqbjf.bet/\n> >> >\n> >> > For that point: I think it's fair enough not to properly handle the\n> >> > cleanup case in \"scalar clone\", but perhaps add a note in the\n> >> > commit message that unlike \"git clone\" this is known not to clean\n> >> > after itself properly on ctrl+c?\n> >>\n> >> Seeing [1] about the planned re-roll I have the above a shot a few\n> >> days ago, see the original discussion at [2] (indirectly linked\n> >> above).\n> >\n> > There is a good reason why I did not engage in that tangent about\n> > deviating from the established `contrib/*/Makefile` paradigm: I find\n> > it particularly unrelated to what this here patch series is trying to\n> > accomplish, and I cannot bring myself to be interested in the proposed\n> > build system changes, either, because I do not see any benefit in the\n> > changes, only downsides.\n> >\n> > I find the distraction unnecessary.\n>\n> Perhaps I'm reading too much between the lines here, so forgive any\n> undue knee-jerk reaction.\n\nOkay, let's try an analogy.\n\nImagine that a person is asking for directions to the train station. And\nthe other person is replying by asking \"did you know that this train\nstation was built in 1878? It is actually quite interesting a story...\n[and then goes on to describe the history and what excites them about\nit]\". Now, the first person tries again to ask for directions, again does\nnot get an answer to that question, and is slowly starting to look at\ntheir watch. The second person, being completely oblivious to all of this,\ngoes on with their wonderful story about the train station and its\ncultural heritage. So the first person walks a bit further to ask a third\nperson, but the second person is not done yet and says \"but you haven't\nheard me out! That's disrespectful!\".\n\nJust imagine for a minute how you would feel if you were the first person.\n\nAnd that is how I feel asking for reviews about the Scalar patch series\nand then being forcefully dragged into that tangent about the build\nprocess.\n\nI find the well-established paradigm to keep contrib/'s build procedures\nas confined to their own directory as possible the most reasonable way to\nhandle the build by virtue of _not_ polluting the top-level Makefile\nunnecessarily. All of your objections strike me simply as personal\nviewpoints, not as technical arguments, and they fail to address this\n\"pollution of the top-level Makefile\" problem. I therefore strongly\ndisagree with your suggestion that the build system should be changed, I\nwould even argue that your suggestion should been dismissed on purely\ntechnical grounds, and I wish you hadn't forced me to say this as\nforcefully.\n\nAnd even if I looked more favorably on your suggestion to change the build\nprocedure, I find this distraction about the build as little constructive\nas the explanations about the train station's history above. Those\nsuggestions do succeed in derailing the conversation about how Git could\nscale better, how Scalar _does_ teach Git how to scale better, and about\nhow to teach Git itself more and more of Scalar's tricks.\n\nIf you have ideas how to teach, say, `git clone` to perform a couple of\nScalar's tricks, by all means, let's hear them, or even better, let's see\nthose patches. If you want to change the build system, still, I cannot\nstop you from sending patches to that end to the Git mailing list, but\nplease expect me to be uninterested in them in any way, and to prefer to\nspend my efforts to improve Git elsewhere. If you have other ideas how to\nimprove on Scalar in a user-perceptible way, however, I am all ears again.\n\nI hope this clarifies it, without the need to read between the lines,\nJohannes\n"},{"id":"435852","messageId":"852ec003109b8244e2f9360ec64749779989c4a2.1631630356.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v4.git.1631630356.gitgitgadget@gmail.com","subject":"[PATCH v4 01/15] scalar: create a rudimentary executable","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-14T14:39:02Z","receivedAt":"2021-09-14T14:43:30Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe idea of Scalar (https://github.com/microsoft/scalar), and before\nthat, of VFS for Git, has always been to prove that Git _can_ scale, and\nto upstream whatever strategies have been demonstrated to help.\n\nWith this patch, we start the journey from that C# project to move what\nis left to Git's own `contrib/` directory, reimplementing it in pure C,\nwith the intention to facilitate integrating the functionality into core\nGit all while maintaining backwards-compatibility for existing Scalar\nusers (which will be much easier when both live in the same worktree).\nIt has always been the plan to contribute all of the proven strategies\nback to core Git.\n\nFor example, while the virtual filesystem provided by VFS for Git helped\nthe team developing the Windows operating system to move onto Git, while\ntrying to upstream it we realized that it cannot be done: getting the\nvirtual filesystem to work (which we only managed to implement fully on\nWindows, but not on, say, macOS or Linux), and the required server-side\nsupport for the GVFS protocol, made this not quite feasible.\n\nThe Scalar project learned from that and tackled the problem with\ndifferent tactics: instead of pretending to Git that the working\ndirectory is fully populated, it _specifically_ teaches Git about\npartial clone (which is based on VFS for Git's cache server), about\nsparse checkout (which VFS for Git tried to do transparently, in the\nfile system layer), and regularly runs maintenance tasks to keep the\nrepository in a healthy state.\n\nWith partial clone, sparse checkout and `git maintenance` having been\nupstreamed, there is little left that `scalar.exe` does which `git.exe`\ncannot do. One such thing is that `scalar clone <url>` will\nautomatically set up a partial, sparse clone, and configure\nknown-helpful settings from the start.\n\nSo let's bring this convenience into Git's tree.\n\nThe idea here is that you can (optionally) build Scalar via\n\n\tmake -C contrib/scalar/Makefile\n\nThis will build the `scalar` executable and put it into the\ncontrib/scalar/ subdirectory.\n\nThe slightly awkward addition of the `contrib/scalar/*` bits to the\ntop-level `Makefile` are actually really required: we want to link to\n`libgit.a`, which means that we will need to use the very same `CFLAGS`\nand `LDFLAGS` as the rest of Git.\n\nAn early development version of this patch tried to replicate all the\nconditional code in `contrib/scalar/Makefile` (e.g. `NO_POLL`) just like\n`contrib/svn-fe/Makefile` used to do before it was retired. It turned\nout to be quite the whack-a-mole game: the SHA-1-related flags, the\nflags enabling/disabling `compat/poll/`, `compat/regex/`,\n`compat/win32mmap.c` & friends depending on the current platform... To\nput it mildly: it was a major mess.\n\nInstead, this patch makes minimal changes to the top-level `Makefile` so\nthat the bits in `contrib/scalar/` can be compiled and linked, and\nadds a `contrib/scalar/Makefile` that uses the top-level `Makefile` in a\nmost minimal way to do the actual compiling.\n\nNote: With this commit, we only establish the infrastructure, no\nScalar functionality is implemented yet; We will do that incrementally\nover the next few commits.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Makefile                  |  8 ++++++++\n contrib/scalar/.gitignore |  2 ++\n contrib/scalar/Makefile   | 34 ++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.c   | 36 ++++++++++++++++++++++++++++++++++++\n 4 files changed, 80 insertions(+)\n create mode 100644 contrib/scalar/.gitignore\n create mode 100644 contrib/scalar/Makefile\n create mode 100644 contrib/scalar/scalar.c\n\ndiff --git a/Makefile b/Makefile\nindex c3565fc0f8f..2d5c822f7a8 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2447,6 +2447,10 @@ endif\n .PHONY: objects\n objects: $(OBJECTS)\n \n+SCALAR_SOURCES := contrib/scalar/scalar.c\n+SCALAR_OBJECTS := $(SCALAR_SOURCES:c=o)\n+OBJECTS += $(SCALAR_OBJECTS)\n+\n dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d)\n dep_dirs := $(addsuffix .depend,$(sort $(dir $(OBJECTS))))\n \n@@ -2586,6 +2590,10 @@ $(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n \t\t$(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)\n \n+contrib/scalar/scalar$X: $(SCALAR_OBJECTS) GIT-LDFLAGS $(GITLIBS)\n+\t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \\\n+\t\t$(filter %.o,$^) $(LIBS)\n+\n $(LIB_FILE): $(LIB_OBJS)\n \t$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^\n \ndiff --git a/contrib/scalar/.gitignore b/contrib/scalar/.gitignore\nnew file mode 100644\nindex 00000000000..ff3d47e84d0\n--- /dev/null\n+++ b/contrib/scalar/.gitignore\n@@ -0,0 +1,2 @@\n+/*.exe\n+/scalar\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nnew file mode 100644\nindex 00000000000..40c03ad10e1\n--- /dev/null\n+++ b/contrib/scalar/Makefile\n@@ -0,0 +1,34 @@\n+QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir\n+QUIET_SUBDIR1  =\n+\n+ifneq ($(findstring s,$(MAKEFLAGS)),s)\n+ifndef V\n+\tQUIET_SUBDIR0  = +@subdir=\n+\tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n+\t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n+else\n+\texport V\n+endif\n+endif\n+\n+all:\n+\n+include ../../config.mak.uname\n+-include ../../config.mak.autogen\n+-include ../../config.mak\n+\n+TARGETS = scalar$(X) scalar.o\n+GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n+\n+all: scalar$X\n+\n+$(GITLIBS):\n+\t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)\n+\n+$(TARGETS): $(GITLIBS) scalar.c\n+\t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)\n+\n+clean:\n+\t$(RM) $(TARGETS)\n+\n+.PHONY: all clean FORCE\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nnew file mode 100644\nindex 00000000000..7cff29e0fcd\n--- /dev/null\n+++ b/contrib/scalar/scalar.c\n@@ -0,0 +1,36 @@\n+/*\n+ * The Scalar command-line interface.\n+ */\n+\n+#include \"cache.h\"\n+#include \"gettext.h\"\n+#include \"parse-options.h\"\n+\n+static struct {\n+\tconst char *name;\n+\tint (*fn)(int, const char **);\n+} builtins[] = {\n+\t{ NULL, NULL},\n+};\n+\n+int cmd_main(int argc, const char **argv)\n+{\n+\tstruct strbuf scalar_usage = STRBUF_INIT;\n+\tint i;\n+\n+\tif (argc > 1) {\n+\t\targv++;\n+\t\targc--;\n+\n+\t\tfor (i = 0; builtins[i].name; i++)\n+\t\t\tif (!strcmp(builtins[i].name, argv[0]))\n+\t\t\t\treturn !!builtins[i].fn(argc, argv);\n+\t}\n+\n+\tstrbuf_addstr(&scalar_usage,\n+\t\t      N_(\"scalar <command> [<options>]\\n\\nCommands:\\n\"));\n+\tfor (i = 0; builtins[i].name; i++)\n+\t\tstrbuf_addf(&scalar_usage, \"\\t%s\\n\", builtins[i].name);\n+\n+\tusage(scalar_usage.buf);\n+}\n-- \ngitgitgadget\n\n"},{"id":"435853","messageId":"6ab9c7195da4e5c9bab2460296bdd6dcb744a9fb.1631630356.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v4.git.1631630356.gitgitgadget@gmail.com","subject":"[PATCH v4 02/15] scalar: start documenting the command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-14T14:39:03Z","receivedAt":"2021-09-14T14:43:31Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis commit establishes the infrastructure to build the manual page for\nthe `scalar` command.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/.gitignore |  3 +++\n contrib/scalar/Makefile   | 14 +++++++++++++-\n contrib/scalar/scalar.txt | 38 ++++++++++++++++++++++++++++++++++++++\n 3 files changed, 54 insertions(+), 1 deletion(-)\n create mode 100644 contrib/scalar/scalar.txt\n\ndiff --git a/contrib/scalar/.gitignore b/contrib/scalar/.gitignore\nindex ff3d47e84d0..00441073f59 100644\n--- a/contrib/scalar/.gitignore\n+++ b/contrib/scalar/.gitignore\n@@ -1,2 +1,5 @@\n+/*.xml\n+/*.1\n+/*.html\n /*.exe\n /scalar\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nindex 40c03ad10e1..85c186634e9 100644\n--- a/contrib/scalar/Makefile\n+++ b/contrib/scalar/Makefile\n@@ -6,6 +6,7 @@ ifndef V\n \tQUIET_SUBDIR0  = +@subdir=\n \tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n \t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n+\tQUIET          = @\n else\n \texport V\n endif\n@@ -30,5 +31,16 @@ $(TARGETS): $(GITLIBS) scalar.c\n \n clean:\n \t$(RM) $(TARGETS)\n+\t$(RM) scalar.1 scalar.html scalar.xml\n \n-.PHONY: all clean FORCE\n+docs: scalar.html scalar.1\n+\n+scalar.html: | scalar.1 # prevent them from trying to build `doc.dep` in parallel\n+\n+scalar.html scalar.1: scalar.txt\n+\t$(QUIET_SUBDIR0)../../Documentation$(QUIET_SUBDIR1) \\\n+\t\tMAN_TXT=../contrib/scalar/scalar.txt \\\n+\t\t../contrib/scalar/$@\n+\t$(QUIET)test scalar.1 != \"$@\" || mv ../../Documentation/$@ .\n+\n+.PHONY: all clean docs FORCE\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nnew file mode 100644\nindex 00000000000..5f7131861a5\n--- /dev/null\n+++ b/contrib/scalar/scalar.txt\n@@ -0,0 +1,38 @@\n+scalar(1)\n+=========\n+\n+NAME\n+----\n+scalar - an opinionated repository management tool\n+\n+SYNOPSIS\n+--------\n+[verse]\n+scalar <command> [<options>]\n+\n+DESCRIPTION\n+-----------\n+\n+Scalar is an opinionated repository management tool. By creating new\n+repositories or registering existing repositories with Scalar, your Git\n+experience will speed up. Scalar sets advanced Git config settings,\n+maintains your repositories in the background, and helps reduce data sent\n+across the network.\n+\n+An important Scalar concept is the enlistment: this is the top-level directory\n+of the project. It usually contains the subdirectory `src/` which is a Git\n+worktree. This encourages the separation between tracked files (inside `src/`)\n+and untracked files, such as build artifacts (outside `src/`). When registering\n+an existing Git worktree with Scalar whose name is not `src`, the enlistment\n+will be identical to the worktree.\n+\n+The `scalar` command implements various subcommands, and different options\n+depending on the subcommand.\n+\n+SEE ALSO\n+--------\n+linkgit:git-maintenance[1].\n+\n+Scalar\n+---\n+Associated with the linkgit:git[1] suite\n-- \ngitgitgadget\n\n"},{"id":"435854","messageId":"14992033d7cf50b3d536f276221be06bbdabc2fe.1631630356.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v4.git.1631630356.gitgitgadget@gmail.com","subject":"[PATCH v4 03/15] scalar: create test infrastructure","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-14T14:39:04Z","receivedAt":"2021-09-14T14:43:33Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nTo test the Scalar command, create a test script in contrib/scalar/t\nthat is executed as `make -C contrib/scalar test`. Since Scalar has no\nmeaningful capabilities yet, the only test is rather simple. We will add\nmore tests in subsequent commits that introduce corresponding, new\nfunctionality.\n\nNote: this test script is intended to test `scalar` only lightly, even\nafter all of the functionality is implemented.\n\nA more comprehensive functional (or: integration) test suite can be\nfound at https://github.com/microsoft/scalar; It is used in the workflow\nhttps://github.com/microsoft/git/blob/HEAD/.github/workflows/scalar-functional-tests.yml\nin Microsoft's Git fork. This test suite performs end-to-end tests with\na real remote repository, and is run as part of the regular CI builds.\nSince those tests require some functionality supported only by\nMicrosoft's Git fork (\"GVFS protocol\"), there is no intention to port\nthat fuller test suite to `contrib/scalar/`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/Makefile          | 17 +++++--\n contrib/scalar/t/Makefile        | 78 ++++++++++++++++++++++++++++++++\n contrib/scalar/t/t9099-scalar.sh | 17 +++++++\n 3 files changed, 109 insertions(+), 3 deletions(-)\n create mode 100644 contrib/scalar/t/Makefile\n create mode 100755 contrib/scalar/t/t9099-scalar.sh\n\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nindex 85c186634e9..8620042f281 100644\n--- a/contrib/scalar/Makefile\n+++ b/contrib/scalar/Makefile\n@@ -3,6 +3,7 @@ QUIET_SUBDIR1  =\n \n ifneq ($(findstring s,$(MAKEFLAGS)),s)\n ifndef V\n+\tQUIET_GEN      = @echo '   ' GEN $@;\n \tQUIET_SUBDIR0  = +@subdir=\n \tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n \t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n@@ -21,7 +22,7 @@ include ../../config.mak.uname\n TARGETS = scalar$(X) scalar.o\n GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n \n-all: scalar$X\n+all: scalar$X ../../bin-wrappers/scalar\n \n $(GITLIBS):\n \t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)\n@@ -30,9 +31,19 @@ $(TARGETS): $(GITLIBS) scalar.c\n \t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)\n \n clean:\n-\t$(RM) $(TARGETS)\n+\t$(RM) $(TARGETS) ../../bin-wrappers/scalar\n \t$(RM) scalar.1 scalar.html scalar.xml\n \n+../../bin-wrappers/scalar: ../../wrap-for-bin.sh Makefile\n+\t@mkdir -p ../../bin-wrappers\n+\t$(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \\\n+\t     -e 's|@@BUILD_DIR@@|$(shell cd ../.. && pwd)|' \\\n+\t     -e 's|@@PROG@@|contrib/scalar/scalar$(X)|' < $< > $@ && \\\n+\tchmod +x $@\n+\n+test: all\n+\t$(MAKE) -C t\n+\n docs: scalar.html scalar.1\n \n scalar.html: | scalar.1 # prevent them from trying to build `doc.dep` in parallel\n@@ -43,4 +54,4 @@ scalar.html scalar.1: scalar.txt\n \t\t../contrib/scalar/$@\n \t$(QUIET)test scalar.1 != \"$@\" || mv ../../Documentation/$@ .\n \n-.PHONY: all clean docs FORCE\n+.PHONY: all clean docs test FORCE\ndiff --git a/contrib/scalar/t/Makefile b/contrib/scalar/t/Makefile\nnew file mode 100644\nindex 00000000000..6170672bb37\n--- /dev/null\n+++ b/contrib/scalar/t/Makefile\n@@ -0,0 +1,78 @@\n+# Run scalar tests\n+#\n+# Copyright (c) 2005,2021 Junio C Hamano, Johannes Schindelin\n+#\n+\n+-include ../../../config.mak.autogen\n+-include ../../../config.mak\n+\n+SHELL_PATH ?= $(SHELL)\n+PERL_PATH ?= /usr/bin/perl\n+RM ?= rm -f\n+PROVE ?= prove\n+DEFAULT_TEST_TARGET ?= test\n+TEST_LINT ?= test-lint\n+\n+ifdef TEST_OUTPUT_DIRECTORY\n+TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results\n+else\n+TEST_RESULTS_DIRECTORY = ../../../t/test-results\n+endif\n+\n+# Shell quote;\n+SHELL_PATH_SQ = $(subst ','\\'',$(SHELL_PATH))\n+PERL_PATH_SQ = $(subst ','\\'',$(PERL_PATH))\n+TEST_RESULTS_DIRECTORY_SQ = $(subst ','\\'',$(TEST_RESULTS_DIRECTORY))\n+\n+T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))\n+\n+all: $(DEFAULT_TEST_TARGET)\n+\n+test: $(TEST_LINT)\n+\t$(MAKE) aggregate-results-and-cleanup\n+\n+prove: $(TEST_LINT)\n+\t@echo \"*** prove ***\"; GIT_CONFIG=.git/config $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)\n+\t$(MAKE) clean-except-prove-cache\n+\n+$(T):\n+\t@echo \"*** $@ ***\"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)\n+\n+clean-except-prove-cache:\n+\t$(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)'\n+\t$(RM) -r valgrind/bin\n+\n+clean: clean-except-prove-cache\n+\t$(RM) .prove\n+\n+test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax\n+\n+test-lint-duplicates:\n+\t@dups=`echo $(T) | tr ' ' '\\n' | sed 's/-.*//' | sort | uniq -d` && \\\n+\t\ttest -z \"$$dups\" || { \\\n+\t\techo >&2 \"duplicate test numbers:\" $$dups; exit 1; }\n+\n+test-lint-executable:\n+\t@bad=`for i in $(T); do test -x \"$$i\" || echo $$i; done` && \\\n+\t\ttest -z \"$$bad\" || { \\\n+\t\techo >&2 \"non-executable tests:\" $$bad; exit 1; }\n+\n+test-lint-shell-syntax:\n+\t@'$(PERL_PATH_SQ)' ../../../t/check-non-portable-shell.pl $(T)\n+\n+aggregate-results-and-cleanup: $(T)\n+\t$(MAKE) aggregate-results\n+\t$(MAKE) clean\n+\n+aggregate-results:\n+\tfor f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \\\n+\t\techo \"$$f\"; \\\n+\tdone | '$(SHELL_PATH_SQ)' ../../../t/aggregate-results.sh\n+\n+valgrind:\n+\t$(MAKE) GIT_TEST_OPTS=\"$(GIT_TEST_OPTS) --valgrind\"\n+\n+test-results:\n+\tmkdir -p test-results\n+\n+.PHONY: $(T) aggregate-results clean valgrind\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nnew file mode 100755\nindex 00000000000..16f2b72b126\n--- /dev/null\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -0,0 +1,17 @@\n+#!/bin/sh\n+\n+test_description='test the `scalar` command'\n+\n+TEST_DIRECTORY=$PWD/../../../t\n+export TEST_DIRECTORY\n+\n+# Make it work with --no-bin-wrappers\n+PATH=$PWD/..:$PATH\n+\n+. ../../../t/test-lib.sh\n+\n+test_expect_success 'scalar shows a usage' '\n+\ttest_expect_code 129 scalar -h\n+'\n+\n+test_done\n-- \ngitgitgadget\n\n"},{"id":"435855","messageId":"bbbc4c3339043bcd718dd2defcbaaaac2092227a.1631630356.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v4.git.1631630356.gitgitgadget@gmail.com","subject":"[PATCH v4 04/15] scalar: 'register' sets recommended config and starts maintenance","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-14T14:39:05Z","receivedAt":"2021-09-14T14:43:37Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nLet's start implementing the `register` command. With this commit,\nrecommended settings are configured upon `scalar register`, and Git's\nbackground maintenance is started.\n\nThe recommended config settings may very well change in the future. For\nexample, once the built-in FSMonitor is available, we will want to\nenable it upon `scalar register`. For that reason, we explicitly support\nrunning `scalar register` in an already-registered enlistment.\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 255 ++++++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt |  18 ++-\n 2 files changed, 272 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 7cff29e0fcd..0e627bb100e 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -5,11 +5,266 @@\n #include \"cache.h\"\n #include \"gettext.h\"\n #include \"parse-options.h\"\n+#include \"config.h\"\n+#include \"run-command.h\"\n+\n+/*\n+ * Remove the deepest subdirectory in the provided path string. Path must not\n+ * include a trailing path separator. Returns 1 if parent directory found,\n+ * otherwise 0.\n+ */\n+static int strbuf_parent_directory(struct strbuf *buf)\n+{\n+\tsize_t len = buf->len;\n+\tsize_t offset = offset_1st_component(buf->buf);\n+\tchar *path_sep = find_last_dir_sep(buf->buf + offset);\n+\tstrbuf_setlen(buf, path_sep ? path_sep - buf->buf : offset);\n+\n+\treturn buf->len < len;\n+}\n+\n+static void setup_enlistment_directory(int argc, const char **argv,\n+\t\t\t\t       const char * const *usagestr,\n+\t\t\t\t       const struct option *options,\n+\t\t\t\t       struct strbuf *enlistment_root)\n+{\n+\tstruct strbuf path = STRBUF_INIT;\n+\tchar *root;\n+\tint enlistment_found = 0;\n+\n+\tif (startup_info->have_repository)\n+\t\tBUG(\"gitdir already set up?!?\");\n+\n+\tif (argc > 1)\n+\t\tusage_with_options(usagestr, options);\n+\n+\t/* find the worktree, determine its corresponding root */\n+\tif (argc == 1)\n+\t\tstrbuf_add_absolute_path(&path, argv[0]);\n+\telse if (strbuf_getcwd(&path) < 0)\n+\t\tdie(_(\"need a working directory\"));\n+\n+\tstrbuf_trim_trailing_dir_sep(&path);\n+\tdo {\n+\t\tconst size_t len = path.len;\n+\n+\t\t/* check if currently in enlistment root with src/ workdir */\n+\t\tstrbuf_addstr(&path, \"/src/.git\");\n+\t\tif (is_git_directory(path.buf)) {\n+\t\t\tstrbuf_strip_suffix(&path, \"/.git\");\n+\n+\t\t\tif (enlistment_root)\n+\t\t\t\tstrbuf_add(enlistment_root, path.buf, len);\n+\n+\t\t\tenlistment_found = 1;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\t/* reset to original path */\n+\t\tstrbuf_setlen(&path, len);\n+\n+\t\t/* check if currently in workdir */\n+\t\tstrbuf_addstr(&path, \"/.git\");\n+\t\tif (is_git_directory(path.buf)) {\n+\t\t\tstrbuf_setlen(&path, len);\n+\n+\t\t\tif (enlistment_root) {\n+\t\t\t\t/*\n+\t\t\t\t * If the worktree's directory's name is `src`, the enlistment is the\n+\t\t\t\t * parent directory, otherwise it is identical to the worktree.\n+\t\t\t\t */\n+\t\t\t\troot = strip_path_suffix(path.buf, \"src\");\n+\t\t\t\tstrbuf_addstr(enlistment_root, root ? root : path.buf);\n+\t\t\t\tfree(root);\n+\t\t\t}\n+\n+\t\t\tenlistment_found = 1;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tstrbuf_setlen(&path, len);\n+\t} while (strbuf_parent_directory(&path));\n+\n+\tif (!enlistment_found)\n+\t\tdie(_(\"could not find enlistment root\"));\n+\n+\tif (chdir(path.buf) < 0)\n+\t\tdie_errno(_(\"could not switch to '%s'\"), path.buf);\n+\n+\tstrbuf_release(&path);\n+\tsetup_git_directory();\n+}\n+\n+static int run_git(const char *arg, ...)\n+{\n+\tstruct strvec argv = STRVEC_INIT;\n+\tva_list args;\n+\tconst char *p;\n+\tint res;\n+\n+\tva_start(args, arg);\n+\tstrvec_push(&argv, arg);\n+\twhile ((p = va_arg(args, const char *)))\n+\t\tstrvec_push(&argv, p);\n+\tva_end(args);\n+\n+\tres = run_command_v_opt(argv.v, RUN_GIT_CMD);\n+\n+\tstrvec_clear(&argv);\n+\treturn res;\n+}\n+\n+static int set_recommended_config(void)\n+{\n+\tstruct {\n+\t\tconst char *key;\n+\t\tconst char *value;\n+\t} config[] = {\n+\t\t{ \"am.keepCR\", \"true\" },\n+\t\t{ \"core.FSCache\", \"true\" },\n+\t\t{ \"core.multiPackIndex\", \"true\" },\n+\t\t{ \"core.preloadIndex\", \"true\" },\n+#ifndef WIN32\n+\t\t{ \"core.untrackedCache\", \"true\" },\n+#else\n+\t\t/*\n+\t\t * Unfortunately, Scalar's Functional Tests demonstrated\n+\t\t * that the untracked cache feature is unreliable on Windows\n+\t\t * (which is a bummer because that platform would benefit the\n+\t\t * most from it). For some reason, freshly created files seem\n+\t\t * not to update the directory's `lastModified` time\n+\t\t * immediately, but the untracked cache would need to rely on\n+\t\t * that.\n+\t\t *\n+\t\t * Therefore, with a sad heart, we disable this very useful\n+\t\t * feature on Windows.\n+\t\t */\n+\t\t{ \"core.untrackedCache\", \"false\" },\n+#endif\n+\t\t{ \"core.logAllRefUpdates\", \"true\" },\n+\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n+\t\t{ \"credential.validate\", \"false\" }, /* GCM4W-only */\n+\t\t{ \"gc.auto\", \"0\" },\n+\t\t{ \"gui.GCWarning\", \"false\" },\n+\t\t{ \"index.threads\", \"true\" },\n+\t\t{ \"index.version\", \"4\" },\n+\t\t{ \"merge.stat\", \"false\" },\n+\t\t{ \"merge.renames\", \"false\" },\n+\t\t{ \"pack.useBitmaps\", \"false\" },\n+\t\t{ \"pack.useSparse\", \"true\" },\n+\t\t{ \"receive.autoGC\", \"false\" },\n+\t\t{ \"reset.quiet\", \"true\" },\n+\t\t{ \"feature.manyFiles\", \"false\" },\n+\t\t{ \"feature.experimental\", \"false\" },\n+\t\t{ \"fetch.unpackLimit\", \"1\" },\n+\t\t{ \"fetch.writeCommitGraph\", \"false\" },\n+#ifdef WIN32\n+\t\t{ \"http.sslBackend\", \"schannel\" },\n+#endif\n+\t\t{ \"status.aheadBehind\", \"false\" },\n+\t\t{ \"commitGraph.generationVersion\", \"1\" },\n+\t\t{ \"core.autoCRLF\", \"false\" },\n+\t\t{ \"core.safeCRLF\", \"false\" },\n+\t\t{ NULL, NULL },\n+\t};\n+\tint i;\n+\tchar *value;\n+\n+\tfor (i = 0; config[i].key; i++) {\n+\t\tif (git_config_get_string(config[i].key, &value)) {\n+\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"created\");\n+\t\t\tif (git_config_set_gently(config[i].key,\n+\t\t\t\t\t\t  config[i].value) < 0)\n+\t\t\t\treturn error(_(\"could not configure %s=%s\"),\n+\t\t\t\t\t     config[i].key, config[i].value);\n+\t\t} else {\n+\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"exists\");\n+\t\t\tfree(value);\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * The `log.excludeDecoration` setting is special because it allows\n+\t * for multiple values.\n+\t */\n+\tif (git_config_get_string(\"log.excludeDecoration\", &value)) {\n+\t\ttrace2_data_string(\"scalar\", the_repository,\n+\t\t\t\t   \"log.excludeDecoration\", \"created\");\n+\t\tif (git_config_set_multivar_gently(\"log.excludeDecoration\",\n+\t\t\t\t\t\t   \"refs/prefetch/*\",\n+\t\t\t\t\t\t   CONFIG_REGEX_NONE, 0))\n+\t\t\treturn error(_(\"could not configure \"\n+\t\t\t\t       \"log.excludeDecoration\"));\n+\t} else {\n+\t\ttrace2_data_string(\"scalar\", the_repository,\n+\t\t\t\t   \"log.excludeDecoration\", \"exists\");\n+\t\tfree(value);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int start_maintenance(void)\n+{\n+\treturn run_git(\"maintenance\", \"start\", NULL);\n+}\n+\n+static int add_enlistment(void)\n+{\n+\tint res;\n+\n+\tif (!the_repository->worktree)\n+\t\tdie(_(\"Scalar enlistments require a worktree\"));\n+\n+\tres = run_git(\"config\", \"--global\", \"--get\", \"--fixed-value\",\n+\t\t      \"scalar.repo\", the_repository->worktree, NULL);\n+\n+\t/*\n+\t * If the setting is already there, then do nothing.\n+\t */\n+\tif (!res)\n+\t\treturn 0;\n+\n+\treturn run_git(\"config\", \"--global\", \"--add\",\n+\t\t       \"scalar.repo\", the_repository->worktree, NULL);\n+}\n+\n+static int register_dir(void)\n+{\n+\tint res = add_enlistment();\n+\n+\tif (!res)\n+\t\tres = set_recommended_config();\n+\n+\tif (!res)\n+\t\tres = start_maintenance();\n+\n+\treturn res;\n+}\n+\n+static int cmd_register(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar register [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn register_dir();\n+}\n \n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"register\", cmd_register },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 5f7131861a5..568987064b2 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,7 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n-scalar <command> [<options>]\n+scalar register [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -29,6 +29,22 @@ will be identical to the worktree.\n The `scalar` command implements various subcommands, and different options\n depending on the subcommand.\n \n+COMMANDS\n+--------\n+\n+Register\n+~~~~~~~~\n+\n+register [<enlistment>]::\n+\tAdds the enlistment's repository to the list of registered repositories\n+\tand starts background maintenance. If `<enlistment>` is not provided,\n+\tthen the enlistment associated with the current working directory is\n+\tregistered.\n++\n+Note: when this subcommand is called in a worktree that is called `src/`, its\n+parent directory is considered to be the Scalar enlistment. If the worktree is\n+_not_ called `src/`, it itself will be considered to be the Scalar enlistment.\n+\n SEE ALSO\n --------\n linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"435856","messageId":"eadcddb2a9bb6ad07299fa74f32847afbc67bf71.1631630356.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v4.git.1631630356.gitgitgadget@gmail.com","subject":"[PATCH v4 05/15] scalar: 'unregister' stops background maintenance","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-14T14:39:06Z","receivedAt":"2021-09-14T14:44:05Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nJust like `scalar register` starts the scheduled background maintenance,\n`scalar unregister` stops it. Note that we use `git maintenance start`\nin `scalar register`, but we do not use `git maintenance stop` in\n`scalar unregister`: this would stop maintenance for _all_ repositories,\nnot just for the one we want to unregister.\n\nThe `unregister` command also removes the corresponding entry from the\n`[scalar]` section in the global Git config.\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 50 ++++++++++++++++++++++++++++++++-------\n contrib/scalar/scalar.txt |  8 +++++++\n 2 files changed, 50 insertions(+), 8 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 0e627bb100e..2b5c52a25f5 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -204,12 +204,12 @@ static int set_recommended_config(void)\n \treturn 0;\n }\n \n-static int start_maintenance(void)\n+static int toggle_maintenance(int enable)\n {\n-\treturn run_git(\"maintenance\", \"start\", NULL);\n+\treturn run_git(\"maintenance\", enable ? \"start\" : \"unregister\", NULL);\n }\n \n-static int add_enlistment(void)\n+static int add_or_remove_enlistment(int add)\n {\n \tint res;\n \n@@ -220,24 +220,39 @@ static int add_enlistment(void)\n \t\t      \"scalar.repo\", the_repository->worktree, NULL);\n \n \t/*\n-\t * If the setting is already there, then do nothing.\n+\t * If we want to add and the setting is already there, then do nothing.\n+\t * If we want to remove and the setting is not there, then do nothing.\n \t */\n-\tif (!res)\n+\tif ((add && !res) || (!add && res))\n \t\treturn 0;\n \n-\treturn run_git(\"config\", \"--global\", \"--add\",\n+\treturn run_git(\"config\", \"--global\", add ? \"--add\" : \"--unset\",\n+\t\t       add ? \"--no-fixed-value\" : \"--fixed-value\",\n \t\t       \"scalar.repo\", the_repository->worktree, NULL);\n }\n \n static int register_dir(void)\n {\n-\tint res = add_enlistment();\n+\tint res = add_or_remove_enlistment(1);\n \n \tif (!res)\n \t\tres = set_recommended_config();\n \n \tif (!res)\n-\t\tres = start_maintenance();\n+\t\tres = toggle_maintenance(1);\n+\n+\treturn res;\n+}\n+\n+static int unregister_dir(void)\n+{\n+\tint res = 0;\n+\n+\tif (toggle_maintenance(0) < 0)\n+\t\tres = -1;\n+\n+\tif (add_or_remove_enlistment(0) < 0)\n+\t\tres = -1;\n \n \treturn res;\n }\n@@ -260,11 +275,30 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_unregister(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar unregister [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn unregister_dir();\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n \t{ \"register\", cmd_register },\n+\t{ \"unregister\", cmd_unregister },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 568987064b2..d9a79984492 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -9,6 +9,7 @@ SYNOPSIS\n --------\n [verse]\n scalar register [<enlistment>]\n+scalar unregister [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -45,6 +46,13 @@ Note: when this subcommand is called in a worktree that is called `src/`, its\n parent directory is considered to be the Scalar enlistment. If the worktree is\n _not_ called `src/`, it itself will be considered to be the Scalar enlistment.\n \n+Unregister\n+~~~~~~~~~~\n+\n+unregister [<enlistment>]::\n+\tRemove the specified repository from the list of repositories\n+\tregistered with Scalar and stop the scheduled background maintenance.\n+\n SEE ALSO\n --------\n linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"435857","messageId":"c3c2f3a497127efaeacb5563ba9ef2c3a2a0b6b1.1631630356.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v4.git.1631630356.gitgitgadget@gmail.com","subject":"[PATCH v4 06/15] scalar: let 'unregister' handle a deleted enlistment directory gracefully","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-14T14:39:07Z","receivedAt":"2021-09-14T14:44:06Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nWhen a user deleted an enlistment manually, let's be generous and\n_still_ unregister it.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 46 ++++++++++++++++++++++++++++++++\n contrib/scalar/t/t9099-scalar.sh | 15 +++++++++++\n 2 files changed, 61 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 2b5c52a25f5..d114c038b64 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -275,6 +275,24 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int remove_deleted_enlistment(struct strbuf *path)\n+{\n+\tint res = 0;\n+\tstrbuf_realpath_forgiving(path, path->buf, 1);\n+\n+\tif (run_git(\"config\", \"--global\",\n+\t\t    \"--unset\", \"--fixed-value\",\n+\t\t    \"scalar.repo\", path->buf, NULL) < 0)\n+\t\tres = -1;\n+\n+\tif (run_git(\"config\", \"--global\",\n+\t\t    \"--unset\", \"--fixed-value\",\n+\t\t    \"maintenance.repo\", path->buf, NULL) < 0)\n+\t\tres = -1;\n+\n+\treturn res;\n+}\n+\n static int cmd_unregister(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -288,6 +306,34 @@ static int cmd_unregister(int argc, const char **argv)\n \targc = parse_options(argc, argv, NULL, options,\n \t\t\t     usage, 0);\n \n+\t/*\n+\t * Be forgiving when the enlistment or worktree does not even exist any\n+\t * longer; This can be the case if a user deleted the worktree by\n+\t * mistake and _still_ wants to unregister the thing.\n+\t */\n+\tif (argc == 1) {\n+\t\tstruct strbuf src_path = STRBUF_INIT, workdir_path = STRBUF_INIT;\n+\n+\t\tstrbuf_addf(&src_path, \"%s/src/.git\", argv[0]);\n+\t\tstrbuf_addf(&workdir_path, \"%s/.git\", argv[0]);\n+\t\tif (!is_directory(src_path.buf) && !is_directory(workdir_path.buf)) {\n+\t\t\t/* remove possible matching registrations */\n+\t\t\tint res = -1;\n+\n+\t\t\tstrbuf_strip_suffix(&src_path, \"/.git\");\n+\t\t\tres = remove_deleted_enlistment(&src_path) && res;\n+\n+\t\t\tstrbuf_strip_suffix(&workdir_path, \"/.git\");\n+\t\t\tres = remove_deleted_enlistment(&workdir_path) && res;\n+\n+\t\t\tstrbuf_release(&src_path);\n+\t\t\tstrbuf_release(&workdir_path);\n+\t\t\treturn res;\n+\t\t}\n+\t\tstrbuf_release(&src_path);\n+\t\tstrbuf_release(&workdir_path);\n+\t}\n+\n \tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n \n \treturn unregister_dir();\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 16f2b72b126..ef0e8d680d5 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -14,4 +14,19 @@ test_expect_success 'scalar shows a usage' '\n \ttest_expect_code 129 scalar -h\n '\n \n+test_expect_success 'scalar unregister' '\n+\tgit init vanish/src &&\n+\tscalar register vanish/src &&\n+\tgit config --get --global --fixed-value \\\n+\t\tmaintenance.repo \"$(pwd)/vanish/src\" &&\n+\tscalar list >scalar.repos &&\n+\tgrep -F \"$(pwd)/vanish/src\" scalar.repos &&\n+\trm -rf vanish/src/.git &&\n+\tscalar unregister vanish &&\n+\ttest_must_fail git config --get --global --fixed-value \\\n+\t\tmaintenance.repo \"$(pwd)/vanish/src\" &&\n+\tscalar list >scalar.repos &&\n+\t! grep -F \"$(pwd)/vanish/src\" scalar.repos\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"435858","messageId":"90ef9b826b297e54a2bc56b99a9073387b0c6b1c.1631630356.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v4.git.1631630356.gitgitgadget@gmail.com","subject":"[PATCH v4 07/15] scalar: implement 'scalar list'","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-14T14:39:08Z","receivedAt":"2021-09-14T14:44:16Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe produced list simply consists of those repositories registered under\nthe multi-valued `scalar.repo` config setting in the user's Git config.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 11 +++++++++++\n contrib/scalar/scalar.txt | 11 ++++++++++-\n 2 files changed, 21 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex d114c038b64..7f5436399da 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -257,6 +257,16 @@ static int unregister_dir(void)\n \treturn res;\n }\n \n+static int cmd_list(int argc, const char **argv)\n+{\n+\tif (argc != 1)\n+\t\tdie(_(\"`scalar list` does not take arguments\"));\n+\n+\tif (run_git(\"config\", \"--global\", \"--get-all\", \"scalar.repo\", NULL) < 0)\n+\t\treturn -1;\n+\treturn 0;\n+}\n+\n static int cmd_register(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -343,6 +353,7 @@ static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n \t{ NULL, NULL},\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex d9a79984492..f93e3d00efd 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,6 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n+scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n \n@@ -28,11 +29,19 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand.\n+depending on the subcommand. With the exception of `list`, all subcommands\n+expect to be run in an enlistment.\n \n COMMANDS\n --------\n \n+List\n+~~~~\n+\n+list::\n+\tList enlistments that are currently registered by Scalar. This\n+\tsubcommand does not need to be run inside an enlistment.\n+\n Register\n ~~~~~~~~\n \n-- \ngitgitgadget\n\n"},{"id":"435859","messageId":"79cde4417d8894253b53d0232f50ee55ca5a8a16.1631630356.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v4.git.1631630356.gitgitgadget@gmail.com","subject":"[PATCH v4 08/15] scalar: implement the `clone` subcommand","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-14T14:39:09Z","receivedAt":"2021-09-14T14:44:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis implements Scalar's opinionated `clone` command: it tries to use a\npartial clone and sets up a sparse checkout by default. In contrast to\n`git clone`, `scalar clone` sets up the worktree in the `src/`\nsubdirectory, to encourage a separation between the source files and the\nbuild output (which helps Git tremendously because it avoids untracked\nfiles that have to be specifically ignored when refreshing the index).\n\nAlso, it registers the repository for regular, scheduled maintenance,\nand configures a flurry of configuration settings based on the\nexperience and experiments of the Microsoft Windows and the Microsoft\nOffice development teams.\n\nNote: since the `scalar clone` command is by far the most commonly\ncalled `scalar` subcommand, we document it at the top of the manual\npage.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 201 +++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        |  31 ++++-\n contrib/scalar/t/t9099-scalar.sh |  32 +++++\n 3 files changed, 261 insertions(+), 3 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 7f5436399da..bf18003b297 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -7,6 +7,7 @@\n #include \"parse-options.h\"\n #include \"config.h\"\n #include \"run-command.h\"\n+#include \"refs.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -257,6 +258,205 @@ static int unregister_dir(void)\n \treturn res;\n }\n \n+/* printf-style interface, expects `<key>=<value>` argument */\n+static int set_config(const char *fmt, ...)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tchar *value;\n+\tint res;\n+\tva_list args;\n+\n+\tva_start(args, fmt);\n+\tstrbuf_vaddf(&buf, fmt, args);\n+\tva_end(args);\n+\n+\tvalue = strchr(buf.buf, '=');\n+\tif (value)\n+\t\t*(value++) = '\\0';\n+\tres = git_config_set_gently(buf.buf, value);\n+\tstrbuf_release(&buf);\n+\n+\treturn res;\n+}\n+\n+static char *remote_default_branch(const char *url)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tstruct strbuf out = STRBUF_INIT;\n+\n+\tcp.git_cmd = 1;\n+\tstrvec_pushl(&cp.args, \"ls-remote\", \"--symref\", url, \"HEAD\", NULL);\n+\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n+\t\tconst char *line = out.buf;\n+\n+\t\twhile (*line) {\n+\t\t\tconst char *eol = strchrnul(line, '\\n'), *p;\n+\t\t\tsize_t len = eol - line;\n+\t\t\tchar *branch;\n+\n+\t\t\tif (!skip_prefix(line, \"ref: \", &p) ||\n+\t\t\t    !strip_suffix_mem(line, &len, \"\\tHEAD\")) {\n+\t\t\t\tline = eol + (*eol == '\\n');\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\teol = line + len;\n+\t\t\tif (skip_prefix(p, \"refs/heads/\", &p)) {\n+\t\t\t\tbranch = xstrndup(p, eol - p);\n+\t\t\t\tstrbuf_release(&out);\n+\t\t\t\treturn branch;\n+\t\t\t}\n+\n+\t\t\terror(_(\"remote HEAD is not a branch: '%.*s'\"),\n+\t\t\t      (int)(eol - p), p);\n+\t\t\tstrbuf_release(&out);\n+\t\t\treturn NULL;\n+\t\t}\n+\t}\n+\twarning(_(\"failed to get default branch name from remote; \"\n+\t\t  \"using local default\"));\n+\tstrbuf_reset(&out);\n+\n+\tchild_process_init(&cp);\n+\tcp.git_cmd = 1;\n+\tstrvec_pushl(&cp.args, \"symbolic-ref\", \"--short\", \"HEAD\", NULL);\n+\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n+\t\tstrbuf_trim(&out);\n+\t\treturn strbuf_detach(&out, NULL);\n+\t}\n+\n+\tstrbuf_release(&out);\n+\terror(_(\"failed to get default branch name\"));\n+\treturn NULL;\n+}\n+\n+static int cmd_clone(int argc, const char **argv)\n+{\n+\tconst char *branch = NULL;\n+\tint full_clone = 0;\n+\tstruct option clone_options[] = {\n+\t\tOPT_STRING('b', \"branch\", &branch, N_(\"<branch>\"),\n+\t\t\t   N_(\"branch to checkout after clone\")),\n+\t\tOPT_BOOL(0, \"full-clone\", &full_clone,\n+\t\t\t N_(\"when cloning, create full working directory\")),\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const clone_usage[] = {\n+\t\tN_(\"scalar clone [<options>] [--] <repo> [<dir>]\"),\n+\t\tNULL\n+\t};\n+\tconst char *url;\n+\tchar *enlistment = NULL, *dir = NULL;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint res;\n+\n+\targc = parse_options(argc, argv, NULL, clone_options, clone_usage, 0);\n+\n+\tif (argc == 2) {\n+\t\turl = argv[0];\n+\t\tenlistment = xstrdup(argv[1]);\n+\t} else if (argc == 1) {\n+\t\turl = argv[0];\n+\n+\t\tstrbuf_addstr(&buf, url);\n+\t\t/* Strip trailing slashes, if any */\n+\t\twhile (buf.len > 0 && is_dir_sep(buf.buf[buf.len - 1]))\n+\t\t\tstrbuf_setlen(&buf, buf.len - 1);\n+\t\t/* Strip suffix `.git`, if any */\n+\t\tstrbuf_strip_suffix(&buf, \".git\");\n+\n+\t\tenlistment = find_last_dir_sep(buf.buf);\n+\t\tif (!enlistment) {\n+\t\t\tdie(_(\"cannot deduce worktree name from '%s'\"), url);\n+\t\t}\n+\t\tenlistment = xstrdup(enlistment + 1);\n+\t} else {\n+\t\tusage_msg_opt(_(\"You must specify a repository to clone.\"),\n+\t\t\t      clone_usage, clone_options);\n+\t}\n+\n+\tif (is_directory(enlistment))\n+\t\tdie(_(\"directory '%s' exists already\"), enlistment);\n+\n+\tdir = xstrfmt(\"%s/src\", enlistment);\n+\n+\tstrbuf_reset(&buf);\n+\tif (branch)\n+\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", branch);\n+\telse {\n+\t\tchar *b = repo_default_branch_name(the_repository, 1);\n+\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", b);\n+\t\tfree(b);\n+\t}\n+\n+\tif ((res = run_git(\"-c\", buf.buf, \"init\", \"--\", dir, NULL)))\n+\t\tgoto cleanup;\n+\n+\tif (chdir(dir) < 0) {\n+\t\tres = error_errno(_(\"could not switch to '%s'\"), dir);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tsetup_git_directory();\n+\n+\t/* common-main already logs `argv` */\n+\ttrace2_def_repo(the_repository);\n+\n+\tif (!branch && !(branch = remote_default_branch(url))) {\n+\t\tres = error(_(\"failed to get default branch for '%s'\"), url);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tif (set_config(\"remote.origin.url=%s\", url) ||\n+\t    set_config(\"remote.origin.fetch=\"\n+\t\t       \"+refs/heads/*:refs/remotes/origin/*\") ||\n+\t    set_config(\"remote.origin.promisor=true\") ||\n+\t    set_config(\"remote.origin.partialCloneFilter=blob:none\")) {\n+\t\tres = error(_(\"could not configure remote in '%s'\"), dir);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tif (!full_clone &&\n+\t    (res = run_git(\"sparse-checkout\", \"init\", \"--cone\", NULL)))\n+\t\tgoto cleanup;\n+\n+\tif (set_recommended_config())\n+\t\treturn error(_(\"could not configure '%s'\"), dir);\n+\n+\tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL))) {\n+\t\twarning(_(\"partial clone failed; attempting full clone\"));\n+\n+\t\tif (set_config(\"remote.origin.promisor\") ||\n+\t\t    set_config(\"remote.origin.partialCloneFilter\")) {\n+\t\t\tres = error(_(\"could not configure for full clone\"));\n+\t\t\tgoto cleanup;\n+\t\t}\n+\n+\t\tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL)))\n+\t\t\tgoto cleanup;\n+\t}\n+\n+\tif ((res = set_config(\"branch.%s.remote=origin\", branch)))\n+\t\tgoto cleanup;\n+\tif ((res = set_config(\"branch.%s.merge=refs/heads/%s\",\n+\t\t\t      branch, branch)))\n+\t\tgoto cleanup;\n+\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addf(&buf, \"origin/%s\", branch);\n+\tres = run_git(\"checkout\", \"-f\", \"-t\", buf.buf, NULL);\n+\tif (res)\n+\t\tgoto cleanup;\n+\n+\tres = register_dir();\n+\n+cleanup:\n+\tfree(enlistment);\n+\tfree(dir);\n+\tstrbuf_release(&buf);\n+\treturn res;\n+}\n+\n static int cmd_list(int argc, const char **argv)\n {\n \tif (argc != 1)\n@@ -353,6 +553,7 @@ static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"clone\", cmd_clone },\n \t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex f93e3d00efd..d65fb5f1491 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,6 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n+scalar clone [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n@@ -29,12 +30,36 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand. With the exception of `list`, all subcommands\n-expect to be run in an enlistment.\n+depending on the subcommand. With the exception of `clone` and `list`, all\n+subcommands expect to be run in an enlistment.\n \n COMMANDS\n --------\n \n+Clone\n+~~~~~\n+\n+clone [<options>] <url> [<enlistment>]::\n+\tClones the specified repository, similar to linkgit:git-clone[1]. By\n+\tdefault, only commit and tree objects are cloned. Once finished, the\n+\tworktree is located at `<enlistment>/src`.\n++\n+The sparse-checkout feature is enabled (except when run with `--full-clone`)\n+and the only files present are those in the top-level directory. Use\n+`git sparse-checkout set` to expand the set of directories you want to see,\n+or `git sparse-checkout disable` to expand to all files (see\n+linkgit:git-sparse-checkout[1] for more details). You can explore the\n+subdirectories outside your sparse-checkout by using `git ls-tree HEAD`.\n+\n+-b <name>::\n+--branch <name>::\n+\tInstead of checking out the branch pointed to by the cloned\n+\trepository's HEAD, check out the `<name>` branch instead.\n+\n+--[no-]full-clone::\n+\tA sparse-checkout is initialized by default. This behavior can be\n+\tturned off via `--full-clone`.\n+\n List\n ~~~~\n \n@@ -64,7 +89,7 @@ unregister [<enlistment>]::\n \n SEE ALSO\n --------\n-linkgit:git-maintenance[1].\n+linkgit:git-clone[1], linkgit:git-maintenance[1].\n \n Scalar\n ---\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex ef0e8d680d5..295398f62cc 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -10,6 +10,9 @@ PATH=$PWD/..:$PATH\n \n . ../../../t/test-lib.sh\n \n+GIT_TEST_MAINT_SCHEDULER=\"crontab:test-tool crontab ../cron.txt\"\n+export GIT_TEST_MAINT_SCHEDULER\n+\n test_expect_success 'scalar shows a usage' '\n \ttest_expect_code 129 scalar -h\n '\n@@ -29,4 +32,33 @@ test_expect_success 'scalar unregister' '\n \t! grep -F \"$(pwd)/vanish/src\" scalar.repos\n '\n \n+test_expect_success 'set up repository to clone' '\n+\ttest_commit first &&\n+\ttest_commit second &&\n+\ttest_commit third &&\n+\tgit switch -c parallel first &&\n+\tmkdir -p 1/2 &&\n+\ttest_commit 1/2/3 &&\n+\tgit config uploadPack.allowFilter true &&\n+\tgit config uploadPack.allowAnySHA1InWant true\n+'\n+\n+test_expect_success 'scalar clone' '\n+\tsecond=$(git rev-parse --verify second:second.t) &&\n+\tscalar clone \"file://$(pwd)\" cloned &&\n+\t(\n+\t\tcd cloned/src &&\n+\n+\t\tgit config --get --global --fixed-value maintenance.repo \\\n+\t\t\t\"$(pwd)\" &&\n+\n+\t\ttest_path_is_missing 1/2 &&\n+\t\ttest_must_fail git rev-list --missing=print $second &&\n+\t\tgit rev-list $second &&\n+\t\tgit cat-file blob $second >actual &&\n+\t\techo \"second\" >expect &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"435860","messageId":"0acdaeb739669620d6e8ee59037ecd5ae52cf007.1631630356.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v4.git.1631630356.gitgitgadget@gmail.com","subject":"[PATCH v4 09/15] scalar: teach 'clone' to support the --single-branch option","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-14T14:39:10Z","receivedAt":"2021-09-14T14:44:19Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nJust like `git clone`, the `scalar clone` command now also offers to\nrestrict the clone to a single branch.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          |  9 +++++++--\n contrib/scalar/scalar.txt        | 12 +++++++++++-\n contrib/scalar/t/t9099-scalar.sh |  6 +++++-\n 3 files changed, 23 insertions(+), 4 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex bf18003b297..7dd1f28948f 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -333,12 +333,15 @@ static char *remote_default_branch(const char *url)\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n-\tint full_clone = 0;\n+\tint full_clone = 0, single_branch = 0;\n \tstruct option clone_options[] = {\n \t\tOPT_STRING('b', \"branch\", &branch, N_(\"<branch>\"),\n \t\t\t   N_(\"branch to checkout after clone\")),\n \t\tOPT_BOOL(0, \"full-clone\", &full_clone,\n \t\t\t N_(\"when cloning, create full working directory\")),\n+\t\tOPT_BOOL(0, \"single-branch\", &single_branch,\n+\t\t\t N_(\"only download metadata for the branch that will \"\n+\t\t\t    \"be checked out\")),\n \t\tOPT_END(),\n \t};\n \tconst char * const clone_usage[] = {\n@@ -409,7 +412,9 @@ static int cmd_clone(int argc, const char **argv)\n \n \tif (set_config(\"remote.origin.url=%s\", url) ||\n \t    set_config(\"remote.origin.fetch=\"\n-\t\t       \"+refs/heads/*:refs/remotes/origin/*\") ||\n+\t\t       \"+refs/heads/%s:refs/remotes/origin/%s\",\n+\t\t       single_branch ? branch : \"*\",\n+\t\t       single_branch ? branch : \"*\") ||\n \t    set_config(\"remote.origin.promisor=true\") ||\n \t    set_config(\"remote.origin.partialCloneFilter=blob:none\")) {\n \t\tres = error(_(\"could not configure remote in '%s'\"), dir);\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex d65fb5f1491..46999cf7c84 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,7 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n-scalar clone [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n+scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n@@ -56,6 +56,16 @@ subdirectories outside your sparse-checkout by using `git ls-tree HEAD`.\n \tInstead of checking out the branch pointed to by the cloned\n \trepository's HEAD, check out the `<name>` branch instead.\n \n+--[no-]single-branch::\n+\tClone only the history leading to the tip of a single branch, either\n+\tspecified by the `--branch` option or the primary branch remote's\n+\t`HEAD` points at.\n++\n+Further fetches into the resulting repository will only update the\n+remote-tracking branch for the branch this option was used for the initial\n+cloning. If the HEAD at the remote did not point at any branch when\n+`--single-branch` clone was made, no remote-tracking branch is created.\n+\n --[no-]full-clone::\n \tA sparse-checkout is initialized by default. This behavior can be\n \tturned off via `--full-clone`.\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 295398f62cc..9a35ab4fde6 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -45,13 +45,17 @@ test_expect_success 'set up repository to clone' '\n \n test_expect_success 'scalar clone' '\n \tsecond=$(git rev-parse --verify second:second.t) &&\n-\tscalar clone \"file://$(pwd)\" cloned &&\n+\tscalar clone \"file://$(pwd)\" cloned --single-branch &&\n \t(\n \t\tcd cloned/src &&\n \n \t\tgit config --get --global --fixed-value maintenance.repo \\\n \t\t\t\"$(pwd)\" &&\n \n+\t\tgit for-each-ref --format=\"%(refname)\" refs/remotes/origin/ >actual &&\n+\t\techo \"refs/remotes/origin/parallel\" >expect &&\n+\t\ttest_cmp expect actual &&\n+\n \t\ttest_path_is_missing 1/2 &&\n \t\ttest_must_fail git rev-list --missing=print $second &&\n \t\tgit rev-list $second &&\n-- \ngitgitgadget\n\n"},{"id":"435861","messageId":"ada242c7c8c1f530231f97e76c97fff18e368ff5.1631630356.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v4.git.1631630356.gitgitgadget@gmail.com","subject":"[PATCH v4 11/15] scalar: allow reconfiguring an existing enlistment","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-14T14:39:12Z","receivedAt":"2021-09-14T14:44:27Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis comes in handy during Scalar upgrades, or when config settings were\nmessed up by mistake.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 79 +++++++++++++++++++++-----------\n contrib/scalar/scalar.txt        |  8 ++++\n contrib/scalar/t/t9099-scalar.sh |  8 ++++\n 3 files changed, 67 insertions(+), 28 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 8a11f390251..1fff7eb7c12 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -115,18 +115,20 @@ static int run_git(const char *arg, ...)\n \treturn res;\n }\n \n-static int set_recommended_config(void)\n+static int set_recommended_config(int reconfigure)\n {\n \tstruct {\n \t\tconst char *key;\n \t\tconst char *value;\n+\t\tint overwrite_on_reconfigure;\n \t} config[] = {\n-\t\t{ \"am.keepCR\", \"true\" },\n-\t\t{ \"core.FSCache\", \"true\" },\n-\t\t{ \"core.multiPackIndex\", \"true\" },\n-\t\t{ \"core.preloadIndex\", \"true\" },\n+\t\t/* Required */\n+\t\t{ \"am.keepCR\", \"true\", 1 },\n+\t\t{ \"core.FSCache\", \"true\", 1 },\n+\t\t{ \"core.multiPackIndex\", \"true\", 1 },\n+\t\t{ \"core.preloadIndex\", \"true\", 1 },\n #ifndef WIN32\n-\t\t{ \"core.untrackedCache\", \"true\" },\n+\t\t{ \"core.untrackedCache\", \"true\", 1 },\n #else\n \t\t/*\n \t\t * Unfortunately, Scalar's Functional Tests demonstrated\n@@ -140,28 +142,29 @@ static int set_recommended_config(void)\n \t\t * Therefore, with a sad heart, we disable this very useful\n \t\t * feature on Windows.\n \t\t */\n-\t\t{ \"core.untrackedCache\", \"false\" },\n+\t\t{ \"core.untrackedCache\", \"false\", 1 },\n #endif\n-\t\t{ \"core.logAllRefUpdates\", \"true\" },\n-\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n-\t\t{ \"credential.validate\", \"false\" }, /* GCM4W-only */\n-\t\t{ \"gc.auto\", \"0\" },\n-\t\t{ \"gui.GCWarning\", \"false\" },\n-\t\t{ \"index.threads\", \"true\" },\n-\t\t{ \"index.version\", \"4\" },\n-\t\t{ \"merge.stat\", \"false\" },\n-\t\t{ \"merge.renames\", \"false\" },\n-\t\t{ \"pack.useBitmaps\", \"false\" },\n-\t\t{ \"pack.useSparse\", \"true\" },\n-\t\t{ \"receive.autoGC\", \"false\" },\n-\t\t{ \"reset.quiet\", \"true\" },\n-\t\t{ \"feature.manyFiles\", \"false\" },\n-\t\t{ \"feature.experimental\", \"false\" },\n-\t\t{ \"fetch.unpackLimit\", \"1\" },\n-\t\t{ \"fetch.writeCommitGraph\", \"false\" },\n+\t\t{ \"core.logAllRefUpdates\", \"true\", 1 },\n+\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\", 1 },\n+\t\t{ \"credential.validate\", \"false\", 1 }, /* GCM4W-only */\n+\t\t{ \"gc.auto\", \"0\", 1 },\n+\t\t{ \"gui.GCWarning\", \"false\", 1 },\n+\t\t{ \"index.threads\", \"true\", 1 },\n+\t\t{ \"index.version\", \"4\", 1 },\n+\t\t{ \"merge.stat\", \"false\", 1 },\n+\t\t{ \"merge.renames\", \"false\", 1 },\n+\t\t{ \"pack.useBitmaps\", \"false\", 1 },\n+\t\t{ \"pack.useSparse\", \"true\", 1 },\n+\t\t{ \"receive.autoGC\", \"false\", 1 },\n+\t\t{ \"reset.quiet\", \"true\", 1 },\n+\t\t{ \"feature.manyFiles\", \"false\", 1 },\n+\t\t{ \"feature.experimental\", \"false\", 1 },\n+\t\t{ \"fetch.unpackLimit\", \"1\", 1 },\n+\t\t{ \"fetch.writeCommitGraph\", \"false\", 1 },\n #ifdef WIN32\n-\t\t{ \"http.sslBackend\", \"schannel\" },\n+\t\t{ \"http.sslBackend\", \"schannel\", 1 },\n #endif\n+\t\t/* Optional */\n \t\t{ \"status.aheadBehind\", \"false\" },\n \t\t{ \"commitGraph.generationVersion\", \"1\" },\n \t\t{ \"core.autoCRLF\", \"false\" },\n@@ -172,7 +175,8 @@ static int set_recommended_config(void)\n \tchar *value;\n \n \tfor (i = 0; config[i].key; i++) {\n-\t\tif (git_config_get_string(config[i].key, &value)) {\n+\t\tif ((reconfigure && config[i].overwrite_on_reconfigure) ||\n+\t\t    git_config_get_string(config[i].key, &value)) {\n \t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"created\");\n \t\t\tif (git_config_set_gently(config[i].key,\n \t\t\t\t\t\t  config[i].value) < 0)\n@@ -237,7 +241,7 @@ static int register_dir(void)\n \tint res = add_or_remove_enlistment(1);\n \n \tif (!res)\n-\t\tres = set_recommended_config();\n+\t\tres = set_recommended_config(0);\n \n \tif (!res)\n \t\tres = toggle_maintenance(1);\n@@ -425,7 +429,7 @@ static int cmd_clone(int argc, const char **argv)\n \t    (res = run_git(\"sparse-checkout\", \"init\", \"--cone\", NULL)))\n \t\tgoto cleanup;\n \n-\tif (set_recommended_config())\n+\tif (set_recommended_config(0))\n \t\treturn error(_(\"could not configure '%s'\"), dir);\n \n \tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL))) {\n@@ -490,6 +494,24 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_reconfigure(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar reconfigure [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn set_recommended_config(1);\n+}\n+\n static int cmd_run(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -626,6 +648,7 @@ static struct {\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n \t{ \"run\", cmd_run },\n+\t{ \"reconfigure\", cmd_reconfigure },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex f139a14445d..f4e4686e8c8 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -13,6 +13,7 @@ scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n+scalar reconfigure <enlistment>\n \n DESCRIPTION\n -----------\n@@ -116,6 +117,13 @@ opinionated default settings that make Git work more efficiently with\n large repositories. As this task is run as part of `scalar clone`\n automatically, explicit invocations of this task are rarely needed.\n \n+Reconfigure\n+~~~~~~~~~~~\n+\n+After a Scalar upgrade, or when the configuration of a Scalar enlistment\n+was somehow corrupted or changed by mistake, this subcommand allows to\n+reconfigure the enlistment.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 9a35ab4fde6..e6d74a06ca0 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -65,4 +65,12 @@ test_expect_success 'scalar clone' '\n \t)\n '\n \n+test_expect_success 'scalar reconfigure' '\n+\tgit init one/src &&\n+\tscalar register one &&\n+\tgit -C one/src config core.preloadIndex false &&\n+\tscalar reconfigure one &&\n+\ttest true = \"$(git -C one/src config core.preloadIndex)\"\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"435862","messageId":"64e3403ac123f609a85c2d385b9d3d1547453779.1631630356.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v4.git.1631630356.gitgitgadget@gmail.com","subject":"[PATCH v4 10/15] scalar: implement the `run` command","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-14T14:39:11Z","receivedAt":"2021-09-14T14:44:28Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nNote: this subcommand is provided primarily for backwards-compatibility,\nfor existing Scalar uses. It is mostly just a shim for `git\nmaintenance`, mapping task names from the way Scalar called them to the\nway Git calls them.\n\nThe reason why those names differ? The background maintenance was first\nimplemented in Scalar, and when it was contributed as a patch series\nimplementing the `git maintenance` command, reviewers suggested better\nnames, those suggestions were accepted before the patches were\nintegrated into core Git.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 64 +++++++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt | 19 ++++++++++++\n 2 files changed, 83 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 7dd1f28948f..8a11f390251 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -490,6 +490,69 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_run(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tstruct {\n+\t\tconst char *arg, *task;\n+\t} tasks[] = {\n+\t\t{ \"config\", NULL },\n+\t\t{ \"commit-graph\", \"commit-graph\" },\n+\t\t{ \"fetch\", \"prefetch\" },\n+\t\t{ \"loose-objects\", \"loose-objects\" },\n+\t\t{ \"pack-files\", \"incremental-repack\" },\n+\t\t{ NULL, NULL }\n+\t};\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tconst char *usagestr[] = { NULL, NULL };\n+\tint i;\n+\n+\tstrbuf_addstr(&buf, N_(\"scalar run <task> [<enlistment>]\\nTasks:\\n\"));\n+\tfor (i = 0; tasks[i].arg; i++)\n+\t\tstrbuf_addf(&buf, \"\\t%s\\n\", tasks[i].arg);\n+\tusagestr[0] = buf.buf;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usagestr, 0);\n+\n+\tif (!argc)\n+\t\tusage_with_options(usagestr, options);\n+\n+\tif (!strcmp(\"all\", argv[0])) {\n+\t\ti = -1;\n+\t} else {\n+\t\tfor (i = 0; tasks[i].arg && strcmp(tasks[i].arg, argv[0]); i++)\n+\t\t\t; /* keep looking for the task */\n+\n+\t\tif (i > 0 && !tasks[i].arg) {\n+\t\t\terror(_(\"no such task: '%s'\"), argv[0]);\n+\t\t\tusage_with_options(usagestr, options);\n+\t\t}\n+\t}\n+\n+\targc--;\n+\targv++;\n+\tsetup_enlistment_directory(argc, argv, usagestr, options, NULL);\n+\tstrbuf_release(&buf);\n+\n+\tif (i == 0)\n+\t\treturn register_dir();\n+\n+\tif (i > 0)\n+\t\treturn run_git(\"maintenance\", \"run\",\n+\t\t\t       \"--task\", tasks[i].task, NULL);\n+\n+\tif (register_dir())\n+\t\treturn -1;\n+\tfor (i = 1; tasks[i].arg; i++)\n+\t\tif (run_git(\"maintenance\", \"run\",\n+\t\t\t    \"--task\", tasks[i].task, NULL))\n+\t\t\treturn -1;\n+\treturn 0;\n+}\n+\n static int remove_deleted_enlistment(struct strbuf *path)\n {\n \tint res = 0;\n@@ -562,6 +625,7 @@ static struct {\n \t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n+\t{ \"run\", cmd_run },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 46999cf7c84..f139a14445d 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -12,6 +12,7 @@ scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<e\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n+scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -97,6 +98,24 @@ unregister [<enlistment>]::\n \tRemove the specified repository from the list of repositories\n \tregistered with Scalar and stop the scheduled background maintenance.\n \n+Run\n+~~~\n+\n+scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]::\n+\tRun the given maintenance task (or all tasks, if `all` was specified).\n+\tExcept for `all` and `config`, this subcommand simply hands off to\n+\tlinkgit:git-maintenance[1] (mapping `fetch` to `prefetch` and\n+\t`pack-files` to `incremental-repack`).\n++\n+These tasks are run automatically as part of the scheduled maintenance,\n+as soon as the repository is registered with Scalar. It should therefore\n+not be necessary to run this subcommand manually.\n++\n+The `config` task is specific to Scalar and configures all those\n+opinionated default settings that make Git work more efficiently with\n+large repositories. As this task is run as part of `scalar clone`\n+automatically, explicit invocations of this task are rarely needed.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"435863","messageId":"5c11117da51a6ab16689e1f4eac62da538f30779.1631630356.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v4.git.1631630356.gitgitgadget@gmail.com","subject":"[PATCH v4 12/15] scalar: teach 'reconfigure' to optionally handle all registered enlistments","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-14T14:39:13Z","receivedAt":"2021-09-14T14:44:44Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nAfter a Scalar upgrade, it can come in really handy if there is an easy\nway to reconfigure all Scalar enlistments. This new option offers this\nfunctionality.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 61 ++++++++++++++++++++++++++++++--\n contrib/scalar/scalar.txt        |  9 +++--\n contrib/scalar/t/t9099-scalar.sh |  3 ++\n 3 files changed, 67 insertions(+), 6 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 1fff7eb7c12..67fa5305225 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -494,22 +494,77 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int get_scalar_repos(const char *key, const char *value, void *data)\n+{\n+\tstruct string_list *list = data;\n+\n+\tif (!strcmp(key, \"scalar.repo\"))\n+\t\tstring_list_append(list, value);\n+\n+\treturn 0;\n+}\n+\n static int cmd_reconfigure(int argc, const char **argv)\n {\n+\tint all = 0;\n \tstruct option options[] = {\n+\t\tOPT_BOOL('a', \"all\", &all,\n+\t\t\t N_(\"reconfigure all registered enlistments\")),\n \t\tOPT_END(),\n \t};\n \tconst char * const usage[] = {\n-\t\tN_(\"scalar reconfigure [<enlistment>]\"),\n+\t\tN_(\"scalar reconfigure [--all | <enlistment>]\"),\n \t\tNULL\n \t};\n+\tstruct string_list scalar_repos = STRING_LIST_INIT_DUP;\n+\tint i, res = 0;\n+\tstruct repository r = { NULL };\n+\tstruct strbuf commondir = STRBUF_INIT, gitdir = STRBUF_INIT;\n \n \targc = parse_options(argc, argv, NULL, options,\n \t\t\t     usage, 0);\n \n-\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\tif (!all) {\n+\t\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\t\treturn set_recommended_config(1);\n+\t}\n+\n+\tif (argc > 0)\n+\t\tusage_msg_opt(_(\"--all or <enlistment>, but not both\"),\n+\t\t\t      usage, options);\n+\n+\tgit_config(get_scalar_repos, &scalar_repos);\n \n-\treturn set_recommended_config(1);\n+\tfor (i = 0; i < scalar_repos.nr; i++) {\n+\t\tconst char *dir = scalar_repos.items[i].string;\n+\n+\t\tstrbuf_reset(&commondir);\n+\t\tstrbuf_reset(&gitdir);\n+\n+\t\tif (chdir(dir) < 0) {\n+\t\t\twarning_errno(_(\"could not switch to '%s'\"), dir);\n+\t\t\tres = -1;\n+\t\t} else if (discover_git_directory(&commondir, &gitdir) < 0) {\n+\t\t\twarning_errno(_(\"git repository gone in '%s'\"), dir);\n+\t\t\tres = -1;\n+\t\t} else {\n+\t\t\tgit_config_clear();\n+\n+\t\t\tthe_repository = &r;\n+\t\t\tr.commondir = commondir.buf;\n+\t\t\tr.gitdir = gitdir.buf;\n+\n+\t\t\tif (set_recommended_config(1) < 0)\n+\t\t\t\tres = -1;\n+\t\t}\n+\t}\n+\n+\tstring_list_clear(&scalar_repos, 1);\n+\tstrbuf_release(&commondir);\n+\tstrbuf_release(&gitdir);\n+\n+\treturn res;\n }\n \n static int cmd_run(int argc, const char **argv)\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex f4e4686e8c8..2fa96fcabc6 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -13,7 +13,7 @@ scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n-scalar reconfigure <enlistment>\n+scalar reconfigure [ --all | <enlistment> ]\n \n DESCRIPTION\n -----------\n@@ -32,8 +32,8 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand. With the exception of `clone` and `list`, all\n-subcommands expect to be run in an enlistment.\n+depending on the subcommand. With the exception of `clone`, `list` and\n+`reconfigure --all`, all subcommands expect to be run in an enlistment.\n \n COMMANDS\n --------\n@@ -124,6 +124,9 @@ After a Scalar upgrade, or when the configuration of a Scalar enlistment\n was somehow corrupted or changed by mistake, this subcommand allows to\n reconfigure the enlistment.\n \n+With the `--all` option, all enlistments currently registered with Scalar\n+will be reconfigured. Use this option after each Scalar upgrade.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex e6d74a06ca0..5fe7fabd0e5 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -70,6 +70,9 @@ test_expect_success 'scalar reconfigure' '\n \tscalar register one &&\n \tgit -C one/src config core.preloadIndex false &&\n \tscalar reconfigure one &&\n+\ttest true = \"$(git -C one/src config core.preloadIndex)\" &&\n+\tgit -C one/src config core.preloadIndex false &&\n+\tscalar reconfigure -a &&\n \ttest true = \"$(git -C one/src config core.preloadIndex)\"\n '\n \n-- \ngitgitgadget\n\n"},{"id":"435864","messageId":"914c16c7fcd948374fb2c0582c435d0d6ac775a0.1631630356.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v4.git.1631630356.gitgitgadget@gmail.com","subject":"[PATCH v4 13/15] scalar: implement the `delete` command","fromName":"Matthew John Cheetham via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-14T14:39:14Z","receivedAt":"2021-09-14T14:44:45Z","isPatch":true,"sender":{"key":"mjcheetham@outlook.com","avatar":"https://avatars.githubusercontent.com/u/5658207?v=4"},"body":"From: Matthew John Cheetham <mjcheetham@outlook.com>\n\nDelete an enlistment by first unregistering the repository and then\ndeleting the enlistment directory (usually the directory containing the\nworktree `src/` directory).\n\nOn Windows, if the current directory is inside the enlistment's\ndirectory, change to the parent of the enlistment directory, to allow us\nto delete the enlistment (directories used by processes e.g. as current\nworking directories cannot be deleted on Windows).\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Matthew John Cheetham <mjcheetham@outlook.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 55 ++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        |  8 +++++\n contrib/scalar/t/t9099-scalar.sh |  9 ++++++\n 3 files changed, 72 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 67fa5305225..00bedb0bf66 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -8,6 +8,7 @@\n #include \"config.h\"\n #include \"run-command.h\"\n #include \"refs.h\"\n+#include \"dir.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -334,6 +335,33 @@ static char *remote_default_branch(const char *url)\n \treturn NULL;\n }\n \n+static int delete_enlistment(struct strbuf *enlistment)\n+{\n+#ifdef WIN32\n+\tstruct strbuf parent = STRBUF_INIT;\n+#endif\n+\n+\tif (unregister_dir())\n+\t\tdie(_(\"failed to unregister repository\"));\n+\n+#ifdef WIN32\n+\t/*\n+\t * Change the current directory to one outside of the enlistment so\n+\t * that we may delete everything underneath it.\n+\t */\n+\tstrbuf_addbuf(&parent, enlistment);\n+\tstrbuf_parent_directory(&parent);\n+\tif (chdir(parent.buf) < 0)\n+\t\tdie_errno(_(\"could not switch to '%s'\"), parent.buf);\n+\tstrbuf_release(&parent);\n+#endif\n+\n+\tif (remove_dir_recursively(enlistment, 0))\n+\t\tdie(_(\"failed to delete enlistment directory\"));\n+\n+\treturn 0;\n+}\n+\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n@@ -694,6 +722,32 @@ static int cmd_unregister(int argc, const char **argv)\n \treturn unregister_dir();\n }\n \n+static int cmd_delete(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar delete <enlistment>\"),\n+\t\tNULL\n+\t};\n+\tstruct strbuf enlistment = STRBUF_INIT;\n+\tint res = 0;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tif (argc != 1)\n+\t\tusage_with_options(usage, options);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, &enlistment);\n+\n+\tres = delete_enlistment(&enlistment);\n+\tstrbuf_release(&enlistment);\n+\n+\treturn res;\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n@@ -704,6 +758,7 @@ static struct {\n \t{ \"unregister\", cmd_unregister },\n \t{ \"run\", cmd_run },\n \t{ \"reconfigure\", cmd_reconfigure },\n+\t{ \"delete\", cmd_delete },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 2fa96fcabc6..6fc57707718 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -14,6 +14,7 @@ scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n scalar reconfigure [ --all | <enlistment> ]\n+scalar delete <enlistment>\n \n DESCRIPTION\n -----------\n@@ -127,6 +128,13 @@ reconfigure the enlistment.\n With the `--all` option, all enlistments currently registered with Scalar\n will be reconfigured. Use this option after each Scalar upgrade.\n \n+Delete\n+~~~~~~\n+\n+delete <enlistment>::\n+\tThis subcommand lets you delete an existing Scalar enlistment from your\n+\tlocal file system, unregistering the repository.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 5fe7fabd0e5..7e8771d0eff 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -76,4 +76,13 @@ test_expect_success 'scalar reconfigure' '\n \ttest true = \"$(git -C one/src config core.preloadIndex)\"\n '\n \n+test_expect_success 'scalar delete without enlistment shows a usage' '\n+\ttest_expect_code 129 scalar delete\n+'\n+\n+test_expect_success 'scalar delete with enlistment' '\n+\tscalar delete cloned &&\n+\ttest_path_is_missing cloned\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"435865","messageId":"069957701204f311077c43af1fd4a42519ef758a.1631630356.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v4.git.1631630356.gitgitgadget@gmail.com","subject":"[PATCH v4 14/15] scalar: implement the `version` command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-14T14:39:15Z","receivedAt":"2021-09-14T14:44:46Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe .NET version of Scalar has a `version` command. This was necessary\nbecause it was versioned independently of Git.\n\nSince Scalar is now tightly coupled with Git, it does not make sense for\nthem to show different versions. Therefore, it shows the same output as\n`git version`. For backwards-compatibility with the .NET version,\n`scalar version` prints to `stderr`, though (`git version` prints to\n`stdout` instead).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c | 39 +++++++++++++++++++++++++++++++++++++++\n 1 file changed, 39 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 00bedb0bf66..728166aa97a 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -9,6 +9,7 @@\n #include \"run-command.h\"\n #include \"refs.h\"\n #include \"dir.h\"\n+#include \"help.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -362,6 +363,15 @@ static int delete_enlistment(struct strbuf *enlistment)\n \treturn 0;\n }\n \n+/*\n+ * Dummy implementation; Using `get_version_info()` would cause a link error\n+ * without this.\n+ */\n+void load_builtin_commands(const char *prefix, struct cmdnames *cmds)\n+{\n+\tdie(\"not implemented\");\n+}\n+\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n@@ -748,6 +758,34 @@ static int cmd_delete(int argc, const char **argv)\n \treturn res;\n }\n \n+static int cmd_version(int argc, const char **argv)\n+{\n+\tint verbose = 0, build_options = 0;\n+\tstruct option options[] = {\n+\t\tOPT__VERBOSE(&verbose, N_(\"include Git version\")),\n+\t\tOPT_BOOL(0, \"build-options\", &build_options,\n+\t\t\t N_(\"include Git's build options\")),\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar verbose [-v | --verbose] [--build-options]\"),\n+\t\tNULL\n+\t};\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tif (argc != 0)\n+\t\tusage_with_options(usage, options);\n+\n+\tget_version_info(&buf, build_options);\n+\tfprintf(stderr, \"%s\\n\", buf.buf);\n+\tstrbuf_release(&buf);\n+\n+\treturn 0;\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n@@ -759,6 +797,7 @@ static struct {\n \t{ \"run\", cmd_run },\n \t{ \"reconfigure\", cmd_reconfigure },\n \t{ \"delete\", cmd_delete },\n+\t{ \"version\", cmd_version },\n \t{ NULL, NULL},\n };\n \n-- \ngitgitgadget\n\n"},{"id":"435866","messageId":"7539725bb4f1cf560b604d2b32a3682b4cdde46d.1631630356.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v4.git.1631630356.gitgitgadget@gmail.com","subject":"[PATCH v4 15/15] scalar: accept -C and -c options before the subcommand","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-14T14:39:16Z","receivedAt":"2021-09-14T14:44:47Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe `git` executable has these two very useful options:\n\n-C <directory>:\n\tswitch to the specified directory before performing any actions\n\n-c <key>=<value>:\n\ttemporarily configure this setting for the duration of the\n\tspecified scalar subcommand\n\nWith this commit, we teach the `scalar` executable the same trick.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 22 +++++++++++++++++++++-\n contrib/scalar/scalar.txt | 10 ++++++++++\n 2 files changed, 31 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 728166aa97a..76a77ca1ed0 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -806,6 +806,25 @@ int cmd_main(int argc, const char **argv)\n \tstruct strbuf scalar_usage = STRBUF_INIT;\n \tint i;\n \n+\twhile (argc > 1 && *argv[1] == '-') {\n+\t\tif (!strcmp(argv[1], \"-C\")) {\n+\t\t\tif (argc < 3)\n+\t\t\t\tdie(_(\"-C requires a <directory>\"));\n+\t\t\tif (chdir(argv[2]) < 0)\n+\t\t\t\tdie_errno(_(\"could not change to '%s'\"),\n+\t\t\t\t\t  argv[2]);\n+\t\t\targc -= 2;\n+\t\t\targv += 2;\n+\t\t} else if (!strcmp(argv[1], \"-c\")) {\n+\t\t\tif (argc < 3)\n+\t\t\t\tdie(_(\"-c requires a <key>=<value> argument\"));\n+\t\t\tgit_config_push_parameter(argv[2]);\n+\t\t\targc -= 2;\n+\t\t\targv += 2;\n+\t\t} else\n+\t\t\tbreak;\n+\t}\n+\n \tif (argc > 1) {\n \t\targv++;\n \t\targc--;\n@@ -816,7 +835,8 @@ int cmd_main(int argc, const char **argv)\n \t}\n \n \tstrbuf_addstr(&scalar_usage,\n-\t\t      N_(\"scalar <command> [<options>]\\n\\nCommands:\\n\"));\n+\t\t      N_(\"scalar [-C <directory>] [-c <key>=<value>] \"\n+\t\t\t \"<command> [<options>]\\n\\nCommands:\\n\"));\n \tfor (i = 0; builtins[i].name; i++)\n \t\tstrbuf_addf(&scalar_usage, \"\\t%s\\n\", builtins[i].name);\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 6fc57707718..3a80f829edc 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -36,6 +36,16 @@ The `scalar` command implements various subcommands, and different options\n depending on the subcommand. With the exception of `clone`, `list` and\n `reconfigure --all`, all subcommands expect to be run in an enlistment.\n \n+The following options can be specified _before_ the subcommand:\n+\n+-C <directory>::\n+\tBefore running the subcommand, change the working directory. This\n+\toption imitates the same option of linkgit:git[1].\n+\n+-c <key>=<value>::\n+\tFor the duration of running the specified subcommand, configure this\n+\tsetting. This option imitates the same option of linkgit:git[1].\n+\n COMMANDS\n --------\n \n-- \ngitgitgadget\n"},{"id":"435867","messageId":"pull.1005.v4.git.1631630356.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v3.git.1631129086.gitgitgadget@gmail.com","subject":"[PATCH v4 00/15] Upstreaming the Scalar command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-14T14:39:01Z","receivedAt":"2021-09-14T14:44:55Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"tl;dr: This series contributes the Scalar command to the Git project. This\ncommand provides an opinionated way to create and configure repositories\nwith a focus on very large repositories.\n\nChanges since v3:\n\n * Moved the \"Changes since\" section to the top, to make it easier to see\n   what changed.\n * Reworded the commit message of the first patch.\n * Removed the [RFC] prefix because I did not hear any objections against\n   putting this into contrib/.\n\nChanges since v2:\n\n * Adjusted the description of the list command in the manual page , as\n   suggested by Bagas.\n * Addressed two style nits in cmd_run().\n * The documentation of git reconfigure -a was improved.\n\nChanges since v1:\n\n * A couple typos were fixed\n * The code parsing the output of ls-remote was made more readable\n * The indentation used in scalar.txt now consistently uses tabs\n * We no longer hard-code core.bare = false when registering with Scalar\n\n\nBackground\n==========\n\nYears ago, Microsoft wanted to move the source code of the Windows operating\nsystem to Git. The challenge there was to prove that Git could scale to\nmassive monorepos. The VFS for Git (formerly GVFS) project was born to take\nup that challenge.\n\nThe final solution included a virtual filesystem (with both user-mode and\nkernel components) and a customized fork of Git for Windows. This solution\ncontained several key concepts, such as only populating a portion of the\nworking directory, demand-fetching blobs, and performing periodic repo\nmaintenance in the background. However, the required kernel drivers made it\ndifficult to port the solution to other platforms.\n\nBut it was realized that many of these key concepts were independent of the\nactual VFS and its projection of the working directory. The Scalar project\nwas created to make that separation, refine the key concepts, and then\nextract those features into the new Scalar command.\n\n\nThe present\n===========\n\nThe Scalar project provides a completely functional non-virtual experience\nfor monorepos. But why stop there. The Scalar project was designed to be a\nself-destructing vehicle to allow those key concepts to be moved into core\nGit itself for the benefit of all. For example, partial clone,\nsparse-checkout, and background maintenance have already been upstreamed and\nremoved from Scalar proper. This patch series provides a C-based\nimplementation of the final remaining portions of the Scalar command. This\nwill make it easier for users to experiment with the Scalar command. It will\nalso make it substantially easier to experiment with moving functionality\nfrom Scalar into core Git, while maintaining backwards-compatibility for\nexisting Scalar users.\n\nThe C-based Scalar has been shipped to Scalar users, and can be tested by\nany interested reader:\nhttps://github.com/microsoft/git/releases/tag/v2.33.0.vfs.0.0 (it offers a\nGit for Windows installer, a macOS package and an Ubuntu package).\n\n\nOpportunities\n=============\n\nApart from providing the Scalar command, this contribution is intended to\nserve as a basis for further mailing list discussions on moving (some of)\nthese key concepts into the main Git commands.\n\nFor example, we previously discussed the idea of a \"git big-clone\" that does\nmuch of what \"scalar clone\" is doing. This patch series is a step to make\nsuch functionality exist in the Git code base while we simmer on what such a\n\"git big-clone\" command-line interface would look like.\n\nThis is one of many possible ways to do this. Creating a 'git big-clone'\ncould lock Git into backwards compatibility concerns so it is necessary to\napproach such an endeavor with caution. As a discussion starter, the scalar\nclone <url> command does roughly this:\n\n 1. git clone --sparse --filter=blob:none /src\n 2. git -C /src sparse-checkout init --cone\n 3. git -C /src config (many times)\n 4. git -C /src maintenance start\n\nIt is my hope inspire discussions about what parts of Scalar could go into\ncore Git, and where, and in which form. While we wish to maintain\nbackwards-compatibility of Scalar's command-line interface (because it is\nalready in use), by having the Scalar code in the same code base as Git's,\nit will be much easier to move functionality without having to maintain\nloose version coupling between independently-versioned Scalar and Git. The\ntight version-coupling, along with having access to libgit.a also allows the\nC-based implementation of Scalar to be much smaller than the original .NET\nversion.\n\nFor example, we might choose in the future to implement, say, git clone\n--scale=partial,cone to initialize a partial clone with a cone-sparse\ncheckout, that would not only be totally doable, and not only would we\nalready have precedent and data to prove that this actually makes engineers\nhappy who have to work on ginormous repositories, but we could then also\nimplement it by moving parts of contrib/scalar/ to builtin/ (where\ncontrib/scalar/ would then call the built-ins accordingly rather than\nhard-coding the defaults itself).\n\nWe now also have the opportunity to discuss the merits of Scalar's clone\ncaching, which is not actually part of this patch series because it is a bit\ncoupled with the GVFS parts of microsoft/git for the moment, where clones\nautomatically get registered with a populated alternate repository that is\nidentified by the URL, meaning: subsequent clones of the same repository are\nvastly faster than the first one because they do not actually download the\nalready-received objects again, they access the cache instead.\n\nAnother thing that I could imagine to be discussed at length is the\ndistinction between enlistment and worktree (where the latter is the actual\nGit worktree and usually lives in the src/ subdirectory of the former). This\nencourages untracked and ignored files to be placed outside the worktree,\nmaking Git's job much easier. This idea, too, might find its way in one way\nor another into Git proper.\n\nThese are just a few concepts in Scalar that do not yet have equivalents in\nGit. By putting this initial implementation into contrib/, we create a\nfoundation for future discussions of these concepts.\n\nWe plan on updating the recommended config settings in scalar register as\nnew Git features are available (such as builtin FSMonitor and sparse-index,\nwhen ready). To facilitate upgrading existing Scalar enlistments, their\npaths are automatically added to the [scalar] section of the global Git\nconfig, and the scalar reconfigure --all command will process all of them.\n\n\nEpilogue\n========\n\nNow, to address some questions that I imagine every reader has who made it\nthis far:\n\n * Why not put the Scalar functionality directly into a built-in? Creating a\n   Git builtin requires scrutiny over every aspect of the feature, which is\n   difficult to do while also maintaining the command-line interface\n   contract and expected behavior of the Scalar command (there are existing\n   users, after all). By having the Scalar command in contrib/, we present a\n   simple option for users to have these features in the short term while\n   the Git contributor community decides which bits to absorb into Git\n   built-ins.\n * Why implement the Scalar command in the Git codebase? We ported Scalar to\n   the microsoft/git fork for several reasons. First, we realized it was\n   possible now that the core features exist inside Git itself. Second,\n   compiling Scalar directly within a version of Git allows us to remove a\n   version compatibility check from each config option that might or might\n   not apply based on the installed Git version. Finally, this new location\n   has greatly simplified our release process and the installation process\n   for users. We now have ways to install Scalar with microsoft/git via\n   winget, brew, and apt-get. This has been the case since we shipped\n   v2.32.0 to our users, read: this setup has served us well already.\n * Why contribute Scalar to the Git project? We are biased, of course, yet\n   we do have evidence that the Scalar command is a helpful tool that offers\n   an simple way to handle huge repositories with ease. By contributing it\n   to the core Git project, we are able to share it with more users,\n   especially some users who do not want to install the microsoft/git fork.\n   We intend to include Scalar as a component in git-for-windows/git, but\n   are contributing it here first. Further, we think there is benefit to the\n   Git developer community as this presents an example of how to set certain\n   defaults that work for large repositories.\n * Does this integrate with the built-in FSMonitor yet? No, not yet. I do\n   have a couple of add-on patch series lined up, one of them being the\n   integration with the built-in FSMonitor, which obviously has to wait\n   until the FSMonitor patch series advances further.\n\nDerrick Stolee (4):\n  scalar: 'register' sets recommended config and starts maintenance\n  scalar: 'unregister' stops background maintenance\n  scalar: implement 'scalar list'\n  scalar: implement the `run` command\n\nJohannes Schindelin (10):\n  scalar: create a rudimentary executable\n  scalar: start documenting the command\n  scalar: create test infrastructure\n  scalar: let 'unregister' handle a deleted enlistment directory\n    gracefully\n  scalar: implement the `clone` subcommand\n  scalar: teach 'clone' to support the --single-branch option\n  scalar: allow reconfiguring an existing enlistment\n  scalar: teach 'reconfigure' to optionally handle all registered\n    enlistments\n  scalar: implement the `version` command\n  scalar: accept -C and -c options before the subcommand\n\nMatthew John Cheetham (1):\n  scalar: implement the `delete` command\n\n Makefile                         |   8 +\n contrib/scalar/.gitignore        |   5 +\n contrib/scalar/Makefile          |  57 +++\n contrib/scalar/scalar.c          | 844 +++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        | 154 ++++++\n contrib/scalar/t/Makefile        |  78 +++\n contrib/scalar/t/t9099-scalar.sh |  88 ++++\n 7 files changed, 1234 insertions(+)\n create mode 100644 contrib/scalar/.gitignore\n create mode 100644 contrib/scalar/Makefile\n create mode 100644 contrib/scalar/scalar.c\n create mode 100644 contrib/scalar/scalar.txt\n create mode 100644 contrib/scalar/t/Makefile\n create mode 100755 contrib/scalar/t/t9099-scalar.sh\n\n\nbase-commit: ebf3c04b262aa27fbb97f8a0156c2347fecafafb\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1005%2Fdscho%2Fscalar-the-beginning-v4\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1005/dscho/scalar-the-beginning-v4\nPull-Request: https://github.com/gitgitgadget/git/pull/1005\n\nRange-diff vs v3:\n\n  1:  b8c7d3f8450 !  1:  852ec003109 scalar: create a rudimentary executable\n     @@ Commit message\n          with the intention to facilitate integrating the functionality into core\n          Git all while maintaining backwards-compatibility for existing Scalar\n          users (which will be much easier when both live in the same worktree).\n     -    It was always to plan to contribute all of the proven strategies back to\n     -    core Git.\n     +    It has always been the plan to contribute all of the proven strategies\n     +    back to core Git.\n      \n          For example, while the virtual filesystem provided by VFS for Git helped\n          the team developing the Windows operating system to move onto Git, while\n     @@ Commit message\n          repository in a healthy state.\n      \n          With partial clone, sparse checkout and `git maintenance` having been\n     -    upstreamed, there is little left that `scalar.exe` does that which\n     -    `git.exe` cannot do. One such thing is that `scalar clone <url>` will\n     +    upstreamed, there is little left that `scalar.exe` does which `git.exe`\n     +    cannot do. One such thing is that `scalar clone <url>` will\n          automatically set up a partial, sparse clone, and configure\n          known-helpful settings from the start.\n      \n  2:  4f886575dcf =  2:  6ab9c7195da scalar: start documenting the command\n  3:  bcfde9bc765 =  3:  14992033d7c scalar: create test infrastructure\n  4:  ee3e26a0c4e =  4:  bbbc4c33390 scalar: 'register' sets recommended config and starts maintenance\n  5:  6142f75875b =  5:  eadcddb2a9b scalar: 'unregister' stops background maintenance\n  6:  82dd253154f =  6:  c3c2f3a4971 scalar: let 'unregister' handle a deleted enlistment directory gracefully\n  7:  d291d3723a6 =  7:  90ef9b826b2 scalar: implement 'scalar list'\n  8:  40dbf61771e =  8:  79cde4417d8 scalar: implement the `clone` subcommand\n  9:  414dbe7d859 =  9:  0acdaeb7396 scalar: teach 'clone' to support the --single-branch option\n 10:  76de416a643 = 10:  64e3403ac12 scalar: implement the `run` command\n 11:  655a902b9df = 11:  ada242c7c8c scalar: allow reconfiguring an existing enlistment\n 12:  2d1987bfcda = 12:  5c11117da51 scalar: teach 'reconfigure' to optionally handle all registered enlistments\n 13:  c67938299ee = 13:  914c16c7fcd scalar: implement the `delete` command\n 14:  d2cd2b7094b = 14:  06995770120 scalar: implement the `version` command\n 15:  7ccc4f8b9b0 = 15:  7539725bb4f scalar: accept -C and -c options before the subcommand\n\n-- \ngitgitgadget\n"},{"id":"435868","messageId":"nycvar.QRO.7.76.6.2109141709330.59@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"pull.1005.v4.git.1631630356.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 00/15] Upstreaming the Scalar command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-09-14T15:10:16Z","receivedAt":"2021-09-14T15:10:30Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Tue, 14 Sep 2021, Johannes Schindelin via GitGitGadget wrote:\n\n> tl;dr: This series contributes the Scalar command to the Git project. This\n> command provides an opinionated way to create and configure repositories\n> with a focus on very large repositories.\n>\n> Changes since v3:\n>\n>  * Moved the \"Changes since\" section to the top, to make it easier to see\n>    what changed.\n>  * Reworded the commit message of the first patch.\n>  * Removed the [RFC] prefix because I did not hear any objections against\n>    putting this into contrib/.\n\nForgot to say:\n\n  * Sent this out to reflect my latest state before taking off for two\n    weeks.\n\nCiao,\nDscho\n"},{"id":"435892","messageId":"xmqqilz32hhr.fsf@gitster.g","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2109141432520.59@tvgsbejvaqbjf.bet","subject":"Re: Train station analogy, was Re: [PATCH v3 00/15] [RFC] Upstreaming the Scalar command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-09-14T17:29:20Z","receivedAt":"2021-09-14T17:29:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> Okay, let's try an analogy.\n>\n> Imagine that a person is asking for directions to the train station. And\n> the other person is replying by asking \"did you know that this train\n> station was built in 1878? It is actually quite interesting a story...\n> [and then goes on to describe the history and what excites them about\n> it]\". Now, the first person tries again to ask for directions, again does\n> not get an answer to that question, and is slowly starting to look at\n> their watch. The second person, being completely oblivious to all of this,\n> goes on with their wonderful story about the train station and its\n> cultural heritage. So the first person walks a bit further to ask a third\n> person, but the second person is not done yet and says \"but you haven't\n> heard me out! That's disrespectful!\".\n>\n> Just imagine for a minute how you would feel if you were the first person.\n>\n> And that is how I feel asking for reviews about the Scalar patch series\n> and then being forcefully dragged into that tangent about the build\n> process.\n\nAt least to me, how this Makefile for Scalar should interact with\nthe overall build process does not mesh well with the story about\nhwo direction to and history of the station are unrelated.  If we\nplan to start from contrib/ and eventually want to make it a part\nof the core Git (i.e. \"git scalar <subcmd> ...\" becomes just like\n\"git bisect <subcmd> ...\"), we would eventually need to see the\nrecipe needed for including \"bisect\" and \"scalar\" work the same\nway, no?\n\nI am getting the impression that such a unified build process is\nÆvar wants to see at the end, I am not even sure if you do from\nthe above \"analogy\".  Cool down a bit, perhaps?\n\nThe following assumes that you share the goal of making \"git\nscalar\" just like \"git bisect\"---another first class citizen of\nGit toolbox, the user can choose to use it or the user may not\nhave a need to interact with it, but it exists there by default\nand is not an opt-in add-on component.\n\nI would understand it if your plan is to convert to a unified\nbuild procedure at the very end of the upstreaming process, and\nnot while you populate contrib/ with more and more scalar stuff,\nbecause the Makefile bits for the entire scalar, while not yet\nupstreamed, has already been written as a separate procedure and\nhaving to convert the whole thing upfront before you can start\ntrickle parts would mean you need to (re)start the process.  And\nI would even be sympathetic if you felt it like a distraction.\n\nBut at least I view it as a step that needs to happen sometime\nbetween now and at the end.  I do not yet have an opinion on\nwhich one is more pleasant, between (1) having to deal with a\nsingle Makefile that needs to be aware of two different locations\n*.[ch] lives in, and (2) having to deal with two Makefiles that\nduplicates definitions and risks them needlessly diverging.\n\nI also would understand it if the reason why you want to keep the\ntop-level Makefile as intact as possible because you sense a high\nprobability that scalar will stay in contrib/ and even turn out\nto be a failure.  Keeping the build procedure separated certainly\nwill keep it easier to yank it out later.  But I do not think\nsuch a case is quite likely.\n\nThanks.\n"},{"id":"435897","messageId":"xmqq35q72ggj.fsf@gitster.g","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2109141709330.59@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v4 00/15] Upstreaming the Scalar command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-09-14T17:51:40Z","receivedAt":"2021-09-14T17:51:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> Hi,\n>\n> On Tue, 14 Sep 2021, Johannes Schindelin via GitGitGadget wrote:\n>\n>> tl;dr: This series contributes the Scalar command to the Git project. This\n>> command provides an opinionated way to create and configure repositories\n>> with a focus on very large repositories.\n>>\n>> Changes since v3:\n>>\n>>  * Moved the \"Changes since\" section to the top, to make it easier to see\n>>    what changed.\n>>  * Reworded the commit message of the first patch.\n>>  * Removed the [RFC] prefix because I did not hear any objections against\n>>    putting this into contrib/.\n>\n> Forgot to say:\n>\n>   * Sent this out to reflect my latest state before taking off for two\n>     weeks.\n\nThanks, will queue.\n\nHave fun.\n"},{"id":"435900","messageId":"87ilz3nhfg.fsf@evledraar.gmail.com","threadId":"56409","inReplyTo":"xmqqilz32hhr.fsf@gitster.g","subject":"Re: Train station analogy, was Re: [PATCH v3 00/15] [RFC] Upstreaming the Scalar command","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-09-14T18:09:21Z","receivedAt":"2021-09-14T18:25:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Sep 14 2021, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>\n>> Okay, let's try an analogy.\n>>\n>> Imagine that a person is asking for directions to the train station. And\n>> the other person is replying by asking \"did you know that this train\n>> station was built in 1878? It is actually quite interesting a story...\n>> [and then goes on to describe the history and what excites them about\n>> it]\". Now, the first person tries again to ask for directions, again does\n>> not get an answer to that question, and is slowly starting to look at\n>> their watch. The second person, being completely oblivious to all of this,\n>> goes on with their wonderful story about the train station and its\n>> cultural heritage. So the first person walks a bit further to ask a third\n>> person, but the second person is not done yet and says \"but you haven't\n>> heard me out! That's disrespectful!\".\n>>\n>> Just imagine for a minute how you would feel if you were the first person.\n>>\n>> And that is how I feel asking for reviews about the Scalar patch series\n>> and then being forcefully dragged into that tangent about the build\n>> process.\n>\n> At least to me, how this Makefile for Scalar should interact with\n> the overall build process does not mesh well with the story about\n> hwo direction to and history of the station are unrelated.  If we\n> plan to start from contrib/ and eventually want to make it a part\n> of the core Git (i.e. \"git scalar <subcmd> ...\" becomes just like\n> \"git bisect <subcmd> ...\"), we would eventually need to see the\n> recipe needed for including \"bisect\" and \"scalar\" work the same\n> way, no?\n>\n> I am getting the impression that such a unified build process is\n> Ævar wants to see at the end, I am not even sure if you do from\n> the above \"analogy\".  Cool down a bit, perhaps?\n>\n> The following assumes that you share the goal of making \"git\n> scalar\" just like \"git bisect\"---another first class citizen of\n> Git toolbox, the user can choose to use it or the user may not\n> have a need to interact with it, but it exists there by default\n> and is not an opt-in add-on component.\n>\n> I would understand it if your plan is to convert to a unified\n> build procedure at the very end of the upstreaming process, and\n> not while you populate contrib/ with more and more scalar stuff,\n> because the Makefile bits for the entire scalar, while not yet\n> upstreamed, has already been written as a separate procedure and\n> having to convert the whole thing upfront before you can start\n> trickle parts would mean you need to (re)start the process.  And\n> I would even be sympathetic if you felt it like a distraction.\n>\n> But at least I view it as a step that needs to happen sometime\n> between now and at the end.  I do not yet have an opinion on\n> which one is more pleasant, between (1) having to deal with a\n> single Makefile that needs to be aware of two different locations\n> *.[ch] lives in, and (2) having to deal with two Makefiles that\n> duplicates definitions and risks them needlessly diverging.\n\nFor what it's worth what I had on top of this is not (1) or (2), but a\n(0): I.e. there isn't a contrib/scalar anymore, I moved:\n\n    contrib/scalar/scalar.c -> scalar\n    contrib/scalar/scalar.txt -> Documentation/scalar.txt\n    contrib/scalar/t9099-scalar.sh -> t/t9099-scalar.sh\n\nWe build, test, and otherwise check (e.g. \"make check-docs\") it by\ndefault, what we don't do is install it unless you ask. You need to run:\n\n    # Or any other install* target\n    make install install-doc INSTALL_SCALAR=YesPlease\n\nIt could be be kept in contrib/scalar/ even with that sort of approach,\nand it would still be simpler than the two-Makefile approach.\n\nBut just moving the code, tests and documentation where everything else\nlives cuts down an all sorts of special cases, file globs in various\nplaces (e.g. doc lints) will just work and won't need adjustment.\n\n> I also would understand it if the reason why you want to keep the\n> top-level Makefile as intact as possible because you sense a high\n> probability that scalar will stay in contrib/ and even turn out\n> to be a failure.  Keeping the build procedure separated certainly\n> will keep it easier to yank it out later.  But I do not think\n> such a case is quite likely.\n\nFor what it's worth the WIP patch(es) I have on top of it will probably\nmake such a thing even easier, not that removing it from the tree would\nbe much of a problem in either case. It's mostly a few lines added to\nlists in various places in the Makfile.\n\nIf I were to clean this up properly most of the changes would be\nteaching the Makefile that it can build N number of named top-level\n\"special\" commands that get dropped into bin/, not just the \"git\" we\nhardcode now.\n\nIf you're interested I could try to clean that up and send something on\ntop, but given the tense-ness of the discussion & unrelated but relevant\npatch queue I've got outstanding wouldn't do so otherwise...\n"},{"id":"435910","messageId":"87ee9rnfb1.fsf@evledraar.gmail.com","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2109141432520.59@tvgsbejvaqbjf.bet","subject":"Re: Train station analogy, was Re: [PATCH v3 00/15] [RFC] Upstreaming the Scalar command","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-09-14T18:25:10Z","receivedAt":"2021-09-14T19:11:06Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Sep 14 2021, Johannes Schindelin wrote:\n\n> Hi Ævar,\n>\n> On Tue, 14 Sep 2021, Ævar Arnfjörð Bjarmason wrote:\n>\n>> On Mon, Sep 13 2021, Johannes Schindelin wrote:\n>>\n>> > On Mon, 13 Sep 2021, Ævar Arnfjörð Bjarmason wrote:\n>> >\n>> >> On Thu, Sep 09 2021, Ævar Arnfjörð Bjarmason wrote:\n>> >>\n>> >> > In the summary I had on v1->v2 points 1-3 are for v2->v3,\n>> >> > respectively, outstanding, addressed, outstanding:\n>> >> >\n>> >> >     https://lore.kernel.org/git/877dfupl7o.fsf@evledraar.gmail.com/\n>> >> >\n>> >> > In addition the discussion ending here:\n>> >> > https://lore.kernel.org/git/nycvar.QRO.7.76.6.2109082112270.55@tvgsbejvaqbjf.bet/\n>> >> >\n>> >> > For that point: I think it's fair enough not to properly handle the\n>> >> > cleanup case in \"scalar clone\", but perhaps add a note in the\n>> >> > commit message that unlike \"git clone\" this is known not to clean\n>> >> > after itself properly on ctrl+c?\n>> >>\n>> >> Seeing [1] about the planned re-roll I have the above a shot a few\n>> >> days ago, see the original discussion at [2] (indirectly linked\n>> >> above).\n>> >\n>> > There is a good reason why I did not engage in that tangent about\n>> > deviating from the established `contrib/*/Makefile` paradigm: I find\n>> > it particularly unrelated to what this here patch series is trying to\n>> > accomplish, and I cannot bring myself to be interested in the proposed\n>> > build system changes, either, because I do not see any benefit in the\n>> > changes, only downsides.\n>> >\n>> > I find the distraction unnecessary.\n>>\n>> Perhaps I'm reading too much between the lines here, so forgive any\n>> undue knee-jerk reaction.\n>\n> Okay, let's try an analogy.\n>\n> Imagine that a person is asking for directions to the train station. And\n> the other person is replying by asking \"did you know that this train\n> station was built in 1878? It is actually quite interesting a story...\n> [and then goes on to describe the history and what excites them about\n> it]\". Now, the first person tries again to ask for directions, again does\n> not get an answer to that question, and is slowly starting to look at\n> their watch. The second person, being completely oblivious to all of this,\n> goes on with their wonderful story about the train station and its\n> cultural heritage. So the first person walks a bit further to ask a third\n> person, but the second person is not done yet and says \"but you haven't\n> heard me out! That's disrespectful!\".\n\nTo be clear that's not what I said or meant. I wasn't saying you had to\nexhaustively hear out some argument or participate in a discussion\nyou're not interested in.\n\nI am saying that if you're soliciting feedback and you get some, and the\nperson giving you the feedback sends you pings back, as I did here:\n\n    https://lore.kernel.org/git/871r6axban.fsf@evledraar.gmail.com/\n    https://lore.kernel.org/git/87mtoxwt63.fsf@evledraar.gmail.com/\n    https://lore.kernel.org/git/877dfupl7o.fsf@evledraar.gmail.com/\n    https://lore.kernel.org/git/87r1dydp4m.fsf@evledraar.gmail.com/\n\nThat it would be nice to at least reply with some brief comment to the\neffect that you're not personally interested in improving this area.\n\nNow, shortly after you send this you re-rolled the v3\n(https://lore.kernel.org/git/pull.1005.v4.git.1631630356.gitgitgadget@gmail.com/)\nwith a note of:\n\n * Removed the [RFC] prefix because I did not hear any objections against\n   putting this into contrib/.\n\nI don't know if you wrote that after this reply, or really didn't see\nany of above, but that's a really inaccurate/misleading comment\nconsidering that context.\n\n> Just imagine for a minute how you would feel if you were the first person.\n>\n> And that is how I feel asking for reviews about the Scalar patch series\n> and then being forcefully dragged into that tangent about the build\n> process.\n>\n> I find the well-established paradigm to keep contrib/'s build procedures\n> as confined to their own directory as possible the most reasonable way to\n> handle the build by virtue of _not_ polluting the top-level Makefile\n> unnecessarily. All of your objections strike me simply as personal\n> viewpoints, not as technical arguments, and they fail to address this\n> \"pollution of the top-level Makefile\" problem. I therefore strongly\n> disagree with your suggestion that the build system should be changed, I\n> would even argue that your suggestion should been dismissed on purely\n> technical grounds, and I wish you hadn't forced me to say this as\n> forcefully.\n>\n> And even if I looked more favorably on your suggestion to change the build\n> procedure, I find this distraction about the build as little constructive\n> as the explanations about the train station's history above.\n\nIf we're indulging each other, here's my version of that train analogy:\n\nWe're in the business of selling a sugary drink that comes in a red can.\n\nWe've got a big factory with an attached train station, and all the\ntrains that move our product are also red.\n\nNow, some of our customers want the new purple colored sugary drink\nwe've cooked up. A new purple factory's all set up for making them.\n\nBut for some reason the part of the business and distribution plans call\nfor building a new purple train station parallel to our existing one.\n\nAll purple sugary drinks are expected to be moved on purple trains, all\nour conductors will need some slight re-training and switchover time to\nflip-flop between red and purple trains. Issues with purple production\nfloors and purple workflows will need to be ironed out.\n\nWe did a survey of our customers and most of them weren't even aware\nthat there was such a thing as train. Or perhaps they've seen other\ntrains, but most haven't seen our trains.\n\nTrains occasionally need field servicing, luckily our fleet of red\ntrains is set up to carry its own spare parts. Purple trains can only be\nserviced with the assistance of a red train.\n\nA product survey asking customers whether their enjoyment of the red or\npurple sugary drinks might be impacted by the color of the train they\nwere shipped on only resulted in puzzled blank looks from the\nparticipants.\n\nThe current state of the purple train station and its fleet of purple\ntrains is that it somewhat works with some hiccups. The currently built\npurple train station omitted any sort of train track for leaving the\nstation though. It should be easy to add one, but...\n\nThe manager of the purple train project has been asked whether we can't\njust have our red machines in our red factory make the purple sugary\ndrink, which we can then load on our fleet of red trains. Why do we need\na new parallel rail system when we really care about customers drinking\nour delicious sugary drinks?\n\nIn case it's not obvious:\n\n   {red,purple} sugary drink  = /usr/bin/git & /usr/bin/scalar\n   {red,purple} train station = ./Makefile & ./contrib/scalar/Makefile\n   train track for leaving the station = make install (AFAICT your\n                                         current patches have no\n                                         installation mechanism)\n\n> Those\n> suggestions do succeed in derailing the conversation about how Git could\n> scale better, how Scalar _does_ teach Git how to scale better, and about\n> how to teach Git itself more and more of Scalar's tricks.\n>\n> If you have ideas how to teach, say, `git clone` to perform a couple of\n> Scalar's tricks, by all means, let's hear them, or even better, let's see\n> those patches. If you want to change the build system, still, I cannot\n> stop you from sending patches to that end to the Git mailing list, but\n> please expect me to be uninterested in them in any way, and to prefer to\n> spend my efforts to improve Git elsewhere. If you have other ideas how to\n> improve on Scalar in a user-perceptible way, however, I am all ears again.\n>\n> I hope this clarifies it, without the need to read between the lines,\n> Johannes\n\nWhatever the end goal of the patches you're sending part of them is\nproposing a given approach to go from A to B.\n\nI find it odd to be claiming that the end-state is so important that we\ncan't spare time to discuss some of the implementation\ndetails.\n\nParticularly in this case, where I've sent a mostly working patch-on-top\nwhich I think should be clear from context I'd be willing to finish up,\nso it's not like it's just pointless nitpicking with no end-goal of a\ncode improvement in sight.\n"},{"id":"435920","messageId":"5ae0c3fe-52a4-49b2-32c2-17feab2dd2d5@gmail.com","threadId":"56409","inReplyTo":"87ilz3nhfg.fsf@evledraar.gmail.com","subject":"Re: Train station analogy, was Re: [PATCH v3 00/15] [RFC] Upstreaming the Scalar command","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2021-09-14T20:35:42Z","receivedAt":"2021-09-14T20:35:46Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 9/14/2021 2:09 PM, Ævar Arnfjörð Bjarmason wrote:\n> \n> On Tue, Sep 14 2021, Junio C Hamano wrote:\n> \n>> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>>\n>> At least to me, how this Makefile for Scalar should interact with\n>> the overall build process does not mesh well with the story about\n>> hwo direction to and history of the station are unrelated.  If we\n>> plan to start from contrib/ and eventually want to make it a part\n>> of the core Git (i.e. \"git scalar <subcmd> ...\" becomes just like\n>> \"git bisect <subcmd> ...\"), we would eventually need to see the\n>> recipe needed for including \"bisect\" and \"scalar\" work the same\n>> way, no?\n\nWe should definitely work to find a better way to describe our\nvision for how _the ideas in Scalar_ can be adopted into Git proper.\n\nBefore this series, we were adding functionality to Git that allowed\nScalar to simplify to just a CLI that configures Git features. This\nsubmission allows that CLI to be available via an opt-in compile flag.\nThis should allow more users to try out the ideas and perhaps we find\nthe things that really work for people (and almost more importantly,\nthe ideas that are _too_ opinionated).\n\nBut the way I see Scalar being fully incorporated into Git is not as\na \"git scalar <foo>\" command or even having \"scalar\" be included by\ndefault. Instead, perhaps a new builtin would need to be created and\nits CLI would need to be presented and reviewed with significant\nattention to long-term support in the Git project. Having Scalar as\na testing ground for these ideas seems like a positive way forward.\n\nThis is a big reason why we think that contrib/ is a good place for\nit to exist.\n\n>> I am getting the impression that such a unified build process is\n>> Ævar wants to see at the end, I am not even sure if you do from\n>> the above \"analogy\".  Cool down a bit, perhaps?\n\nI agree that the temperature of this thread has gotten a bit\nheated. I think there is something valuable to be gained from\neach perspective, but not in a way that either has presented it.\n\n>> The following assumes that you share the goal of making \"git\n>> scalar\" just like \"git bisect\"---another first class citizen of\n>> Git toolbox, the user can choose to use it or the user may not\n>> have a need to interact with it, but it exists there by default\n>> and is not an opt-in add-on component.\n>>\n>> I would understand it if your plan is to convert to a unified\n>> build procedure at the very end of the upstreaming process, and\n>> not while you populate contrib/ with more and more scalar stuff,\n>> because the Makefile bits for the entire scalar, while not yet\n>> upstreamed, has already been written as a separate procedure and\n>> having to convert the whole thing upfront before you can start\n>> trickle parts would mean you need to (re)start the process.  And\n>> I would even be sympathetic if you felt it like a distraction.\n>>\n>> But at least I view it as a step that needs to happen sometime\n>> between now and at the end.  I do not yet have an opinion on\n>> which one is more pleasant, between (1) having to deal with a\n>> single Makefile that needs to be aware of two different locations\n>> *.[ch] lives in, and (2) having to deal with two Makefiles that\n>> duplicates definitions and risks them needlessly diverging.\n\nSince we already need to modify the root Makefile, I think having\nthe root Makefile add the files from contrib/scalar from an\noptional flag is a great way to reduce duplication across multiple\nMakefiles while also maintaining the Scalar is compiled optionally.\n\nOne big goal is to minimize how often we need to update Scalar. I\ncan see things like adjusting the recommended config once per\nrelease cycle based on which new features are available. I don't\nreally want to be spending time updating the Makefile to match a\ncontribution that was already carefully reviewed and tested. I\nalso don't want to put the burden of updating contrib/scalar upon\nthose contributors.\n\n> For what it's worth what I had on top of this is not (1) or (2), but a\n> (0): I.e. there isn't a contrib/scalar anymore, I moved:\n> \n>     contrib/scalar/scalar.c -> scalar>     contrib/scalar/scalar.txt -> Documentation/scalar.txt\n>     contrib/scalar/t9099-scalar.sh -> t/t9099-scalar.sh\n> \n> We build, test, and otherwise check (e.g. \"make check-docs\") it by\n> default, what we don't do is install it unless you ask. You need to run:\n> \n>     # Or any other install* target\n>     make install install-doc INSTALL_SCALAR=YesPlease\n> \n> It could be be kept in contrib/scalar/ even with that sort of approach,\n> and it would still be simpler than the two-Makefile approach.\n\nI think keeping it in contrib/scalar is best for now. But I do\nagree that a single Makefile has benefits.\n\nOne early suggestion from a while back was to modify git.c to\nhandle the \"scalar\" executable as well as the \"git\" executable,\nspecifically to reduce duplication handling options such as\n\n  -c config.key=value\n  -C worktree\n  --exec-path\n\nand similar commands. While our duplication of the \"-c\" option\ndoes add similar code in a second place, these other options\nare less critical for Scalar, especially in its current version.\nI think refactoring the code in git.c to cater to the \"scalar\"\nexecutable is at least premature. If we want to pursue these\nother options in the future, then that refactoring could happen\nas a separate discussion after the rest of the build system and\nCLI have been figured out.\n\n_Perhaps_ Johannes still had that level of integration in his\nhead when responding to the single-Makefile recommendations.\n\n> But just moving the code, tests and documentation where everything else\n> lives cuts down an all sorts of special cases, file globs in various\n> places (e.g. doc lints) will just work and won't need adjustment.\n> \n>> I also would understand it if the reason why you want to keep the\n>> top-level Makefile as intact as possible because you sense a high\n>> probability that scalar will stay in contrib/ and even turn out\n>> to be a failure.  Keeping the build procedure separated certainly\n>> will keep it easier to yank it out later.  But I do not think\n>> such a case is quite likely.\n> \n> For what it's worth the WIP patch(es) I have on top of it will probably\n> make such a thing even easier, not that removing it from the tree would\n> be much of a problem in either case. It's mostly a few lines added to\n> lists in various places in the Makfile.\n\nDo you have a version of these patches available for adaptation\ninto this series? I'd like to take a look and see what it would\nlook like to squash them into this series. Forgive me if I just\nmissed the link. (I see the diff you posted earlier in this thread.)\n\n> If I were to clean this up properly most of the changes would be\n> teaching the Makefile that it can build N number of named top-level\n> \"special\" commands that get dropped into bin/, not just the \"git\" we\n> hardcode now.\n\nThis is an interesting idea for revamping how adjacent tools are\ncompiled and shipped with Git from contrib/ (or possibly elsewhere\nif we decided to start including more things as \"blessed helpers\".\n\nAs a complete aside: I'm interested in using the sparse-checkout\nfeature as I work on the Git codebase, just to make sure I hit\npain points before any other user.\n\nThis is the best that I could do for my purposes:\n\n$ git sparse-checkout list\n.github\nDocumentation\nbuiltin\ncompat\ncontrib/scalar\newah\ngit-gui\ngitk-git\ngitk-gui\ngitweb\nmergetools\nnegotiator\nperl\npo\nrefs\nsha1dc\nsha256\nt\ntemplates\ntrace2\nxdiff\n\nAnd 'git status' reports that this includes 97% of the tracked\nfiles. Perhaps there are ways to make this be smaller by having\nmake skip building things like git-gui if the directory doesn't\nexist. Another idea would be to skip any logic around translating\nmessages if the 'po' directory is missing.\n\nThe reason I bring this up is that I'm interested in finding\nways to make our build system be streamlined a bit using the\npresence of directories as a way to opt in/out of certain build\noutputs. Since Scalar is being added as a new component, this is\na good opportunity to establish a pattern that works for this\neffort, too.\n\nThanks,\n-Stolee\n\n"},{"id":"435924","messageId":"xmqq1r5qzv35.fsf@gitster.g","threadId":"56409","inReplyTo":"xmqqilz32hhr.fsf@gitster.g","subject":"Re: Train station analogy, was Re: [PATCH v3 00/15] [RFC] Upstreaming the Scalar command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-09-14T21:49:18Z","receivedAt":"2021-09-14T21:49:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> But at least I view it as a step that needs to happen sometime\n> between now and at the end.  I do not yet have an opinion on\n> which one is more pleasant, between (1) having to deal with a\n> single Makefile that needs to be aware of two different locations\n> *.[ch] lives in, and (2) having to deal with two Makefiles that\n> duplicates definitions and risks them needlessly diverging.\n\nFWIW, I am leaning towards the latter, with the assumption that this\nmay take more than a cycle to cook in contrib/.\n\nAdding the Makefile bits to the top-level and keeping the topic in\n'next' means all the topics that pass the pipeline will need to be\nwritten in such a way that its Makefile change works well with and\nwithout the unified Makefile bits from this topic, an additional\nburden on other topics this topic would impose.\n\nSo it is understandable to want to keep the changes to the top-level\nMakefile to the minimum, even if it may mean that it requires more\neffort in the end to clean things up when the topic graduates.\n\nAn alternative would be to bypass the contrib/ phase and start as a\nnew subcommand that is first-class citizen from day one and let it\nspend as much time as it needs to mature.  It would burden the\ntopics that pass the pipeline while this is cooking the same way as\nhaving a unified build procedure in the top-level Makefile, of\ncourse, though.\n"},{"id":"435937","messageId":"YUEuuFTSZkxB86cC@mit.edu","threadId":"56409","inReplyTo":"5ae0c3fe-52a4-49b2-32c2-17feab2dd2d5@gmail.com","subject":"Re: Train station analogy, was Re: [PATCH v3 00/15] [RFC] Upstreaming the Scalar command","fromName":"Theodore Ts'o","fromEmail":"tytso@mit.edu","sentAt":"2021-09-14T23:22:32Z","receivedAt":"2021-09-14T23:22:51Z","isPatch":true,"sender":{"key":"tytso@mit.edu","avatar":"https://avatars.githubusercontent.com/u/51416?v=4"},"body":"On Tue, Sep 14, 2021 at 04:35:42PM -0400, Derrick Stolee wrote:\n> \n> We should definitely work to find a better way to describe our\n> vision for how _the ideas in Scalar_ can be adopted into Git proper.\n> ..\n> \n> But the way I see Scalar being fully incorporated into Git is not as\n> a \"git scalar <foo>\" command or even having \"scalar\" be included by\n> default.\n\nI agree 1000%.  It may be convenient for existing scalar users to have\na way they can stick with the CLI that they are used to, but before we\nadd this functionality to git proper, let's please make sure git users\nget a CLI which makes sense and not dictated by history.\n\nWe already have enough people who complain that the git interface\ndesign is hard to understand but which can't be changed because we\nbias our UI design in favor of existing users as opposed new users.\nGiven that I'm an existing user, I'm actually not complaining, but I\ndo recognize the validity of the complaints that for example, git\nreset does three different, mostly unrelated things.\n\nGiven that the existing scalar users are used to \"scalar <foo>\" and\nnot \"git scalar <foo\">, I'd gently suggest that it's better that there\nbe an existing compatibility program which translates \"scalar <foo>\"\nto whatever git command(s) makes sense, as opposed to optimizing for\nthe simplicity of said program so that all forms of \"scalar <foo>\"\nshould get translated to \"git scalar <foo>\".\n\nSo for example, if we are inside a mono repro, it would seem to me\nthat \"git commit\" should automatically do the right thing, as opposed\nto imposing cognitive load on the user to know when they are supposed\nto type \"git commit ...\" versus \"git scalar commit ...\" versus \"scalar\ncommit\".\n\nPlease, let's design the UI for scalar integration into git with deep\nsympathy for the users, and *not* the convenience of the compatibility\nscript for the existing scalar CLI users.\n\n\t\t\t\t\t\t- Ted\n"},{"id":"436035","messageId":"87k0jhn0p9.fsf@evledraar.gmail.com","threadId":"56409","inReplyTo":"5ae0c3fe-52a4-49b2-32c2-17feab2dd2d5@gmail.com","subject":"Re: Train station analogy, was Re: [PATCH v3 00/15] [RFC] Upstreaming the Scalar command","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-09-15T17:51:04Z","receivedAt":"2021-09-15T18:38:47Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Sep 14 2021, Derrick Stolee wrote:\n\n> On 9/14/2021 2:09 PM, Ævar Arnfjörð Bjarmason wrote:\n>> \n>> On Tue, Sep 14 2021, Junio C Hamano wrote:\n>> \n>>> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>>>\n>>> At least to me, how this Makefile for Scalar should interact with\n>>> the overall build process does not mesh well with the story about\n>>> hwo direction to and history of the station are unrelated.  If we\n>>> plan to start from contrib/ and eventually want to make it a part\n>>> of the core Git (i.e. \"git scalar <subcmd> ...\" becomes just like\n>>> \"git bisect <subcmd> ...\"), we would eventually need to see the\n>>> recipe needed for including \"bisect\" and \"scalar\" work the same\n>>> way, no?\n>\n> We should definitely work to find a better way to describe our\n> vision for how _the ideas in Scalar_ can be adopted into Git proper.\n>\n> Before this series, we were adding functionality to Git that allowed\n> Scalar to simplify to just a CLI that configures Git features. This\n> submission allows that CLI to be available via an opt-in compile flag.\n> This should allow more users to try out the ideas and perhaps we find\n> the things that really work for people (and almost more importantly,\n> the ideas that are _too_ opinionated).\n\nYeah that makes sense. I think it looks like a useful command & it's\nalready useful to users.\n\nI haven't been suggesting any changes to what gets installed here, FWIW\nI think we could be even more aggressive on that front, e.g. shipping it\nunconditionally in libexec, maybe with an optional switch for\n/usr/bin/scalar, or to ship \"scalar\" symlinked to \"git\" and have it\nroute to the top-level scalar command depending on argv.\n\nI dabbled in that a bit locally, FWIW it seems if anything even easier\nto do than the approaches we've discussed so far, but I wanted to focus\non providing the same behavior in terms of build system maintenance.\n\n> But the way I see Scalar being fully incorporated into Git is not as\n> a \"git scalar <foo>\" command or even having \"scalar\" be included by\n> default. Instead, perhaps a new builtin would need to be created and\n> its CLI would need to be presented and reviewed with significant\n> attention to long-term support in the Git project. Having Scalar as\n> a testing ground for these ideas seems like a positive way forward.\n\n*Nod*\n\n> This is a big reason why we think that contrib/ is a good place for\n> it to exist.\n\nHere's where you and Johannes lose me. There's some rationale in your\nminds for why sticking it in contrib is the obvious way to go. So far\nyou've been describing how it'll look to users etc, how it's arranged in\nour source tree only matters to git.git developers.\n\nI think the actual reason is to carve in advance some subjective\nownership/apartness or whatever for this thing, if that's the case I\nthink documentation/commit messages would also work.\n\nI really don't care much if something lives in contrib or not in the\nabstract, but various integration around builds in Makefile makes that\nmuch easier in practice, and if \"make install\" looks the same...\n\n>>> I am getting the impression that such a unified build process is\n>>> Ævar wants to see at the end, I am not even sure if you do from\n>>> the above \"analogy\".  Cool down a bit, perhaps?\n>\n> I agree that the temperature of this thread has gotten a bit\n> heated. I think there is something valuable to be gained from\n> each perspective, but not in a way that either has presented it.\n\nThanks, hopefully we can keep it jovial going forward. If you've got any\n(either on-list or off-list) feedback about how I can improve my side of\nthat it would be most welcome.\n\n>>> The following assumes that you share the goal of making \"git\n>>> scalar\" just like \"git bisect\"---another first class citizen of\n>>> Git toolbox, the user can choose to use it or the user may not\n>>> have a need to interact with it, but it exists there by default\n>>> and is not an opt-in add-on component.\n>>>\n>>> I would understand it if your plan is to convert to a unified\n>>> build procedure at the very end of the upstreaming process, and\n>>> not while you populate contrib/ with more and more scalar stuff,\n>>> because the Makefile bits for the entire scalar, while not yet\n>>> upstreamed, has already been written as a separate procedure and\n>>> having to convert the whole thing upfront before you can start\n>>> trickle parts would mean you need to (re)start the process.  And\n>>> I would even be sympathetic if you felt it like a distraction.\n>>>\n>>> But at least I view it as a step that needs to happen sometime\n>>> between now and at the end.  I do not yet have an opinion on\n>>> which one is more pleasant, between (1) having to deal with a\n>>> single Makefile that needs to be aware of two different locations\n>>> *.[ch] lives in, and (2) having to deal with two Makefiles that\n>>> duplicates definitions and risks them needlessly diverging.\n>\n> Since we already need to modify the root Makefile, I think having\n> the root Makefile add the files from contrib/scalar from an\n> optional flag is a great way to reduce duplication across multiple\n> Makefiles while also maintaining the Scalar is compiled optionally.\n>\n> One big goal is to minimize how often we need to update Scalar. I\n> can see things like adjusting the recommended config once per\n> release cycle based on which new features are available. I don't\n> really want to be spending time updating the Makefile to match a\n> contribution that was already carefully reviewed and tested. I\n> also don't want to put the burden of updating contrib/scalar upon\n> those contributors.\n\nI'd think not having large parts of t/Makefile & Makefile should ease\nthat maintenance burden for you & others.\n\n>> For what it's worth what I had on top of this is not (1) or (2), but a\n>> (0): I.e. there isn't a contrib/scalar anymore, I moved:\n>> \n>>     contrib/scalar/scalar.c -> scalar>     contrib/scalar/scalar.txt -> Documentation/scalar.txt\n>>     contrib/scalar/t9099-scalar.sh -> t/t9099-scalar.sh\n>> \n>> We build, test, and otherwise check (e.g. \"make check-docs\") it by\n>> default, what we don't do is install it unless you ask. You need to run:\n>> \n>>     # Or any other install* target\n>>     make install install-doc INSTALL_SCALAR=YesPlease\n>> \n>> It could be be kept in contrib/scalar/ even with that sort of approach,\n>> and it would still be simpler than the two-Makefile approach.\n>\n> I think keeping it in contrib/scalar is best for now. But I do\n> agree that a single Makefile has benefits.\n\nI noted the \"why contrib\" above.\n\n> One early suggestion from a while back was to modify git.c to\n> handle the \"scalar\" executable as well as the \"git\" executable,\n> specifically to reduce duplication handling options such as\n>\n>   -c config.key=value\n>   -C worktree\n>   --exec-path\n>\n> and similar commands. While our duplication of the \"-c\" option\n> does add similar code in a second place, these other options\n> are less critical for Scalar, especially in its current version.\n> I think refactoring the code in git.c to cater to the \"scalar\"\n> executable is at least premature. If we want to pursue these\n> other options in the future, then that refactoring could happen\n> as a separate discussion after the rest of the build system and\n> CLI have been figured out.\n\nAs noted above that seems like a sensible way forward, I hadn't noticed\nhow much of git.c's setup was copied into scalar.c.\n\nIt seems to me that it wouldn't be that hard, on the order of the\nexisting setup code or less. I.e. just make \"git.c\" learn that it may be\nrunning some arbitrary command name, and do some options parsing, but\nand finally dispatch to a cmd_scalar(). IOW mostly like a built-in.\n\n> _Perhaps_ Johannes still had that level of integration in his\n> head when responding to the single-Makefile recommendations.\n>\n>> But just moving the code, tests and documentation where everything else\n>> lives cuts down an all sorts of special cases, file globs in various\n>> places (e.g. doc lints) will just work and won't need adjustment.\n>> \n>>> I also would understand it if the reason why you want to keep the\n>>> top-level Makefile as intact as possible because you sense a high\n>>> probability that scalar will stay in contrib/ and even turn out\n>>> to be a failure.  Keeping the build procedure separated certainly\n>>> will keep it easier to yank it out later.  But I do not think\n>>> such a case is quite likely.\n>> \n>> For what it's worth the WIP patch(es) I have on top of it will probably\n>> make such a thing even easier, not that removing it from the tree would\n>> be much of a problem in either case. It's mostly a few lines added to\n>> lists in various places in the Makfile.\n>\n> Do you have a version of these patches available for adaptation\n> into this series? I'd like to take a look and see what it would\n> look like to squash them into this series. Forgive me if I just\n> missed the link. (I see the diff you posted earlier in this thread.)\n\nI've got it at\nhttps://github.com/avar/git/tree/avar-dscho/scalar-the-beginning-normalize-Makefile\n\nNot very ML-ready, and soft-depends on some other Makefile cleanups I\nthought I'd do & still haven't untangled and submitted. Soft-depends as\nin this can easily be done on master, but some of the variable names\netc. are quite confusing there.\n\nBut you should be able to check it out, it'll build, test and install if\nyou run \"install\" with \"INSTALL_SCALAR=Y\".\n\nYou may run into on everly eager new (but unrelated to this, I just\nmerged it on top) Makefile assertion I'm experimenting with, just\ncomment out the relevant line in the Makefile if that happens,\ni.e. something like this error:\n\n    Makefile:3608: *** \"please sort and de-duplicate BUILT_INS_EXTRA!\".  Stop.\n\n>> If I were to clean this up properly most of the changes would be\n>> teaching the Makefile that it can build N number of named top-level\n>> \"special\" commands that get dropped into bin/, not just the \"git\" we\n>> hardcode now.\n>\n> This is an interesting idea for revamping how adjacent tools are\n> compiled and shipped with Git from contrib/ (or possibly elsewhere\n> if we decided to start including more things as \"blessed helpers\".\n>\n> As a complete aside: I'm interested in using the sparse-checkout\n> feature as I work on the Git codebase, just to make sure I hit\n> pain points before any other user.\n>\n> This is the best that I could do for my purposes:\n>\n> $ git sparse-checkout list\n> .github\n> Documentation\n> builtin\n> compat\n> contrib/scalar\n> ewah\n> git-gui\n> gitk-git\n> gitk-gui\n> gitweb\n> mergetools\n> negotiator\n> perl\n> po\n> refs\n> sha1dc\n> sha256\n> t\n> templates\n> trace2\n> xdiff\n>\n> And 'git status' reports that this includes 97% of the tracked\n> files. Perhaps there are ways to make this be smaller by having\n> make skip building things like git-gui if the directory doesn't\n> exist. Another idea would be to skip any logic around translating\n> messages if the 'po' directory is missing.\n\nFor git-gui in particular NO_TCLTK=Y should do it.\n\n> The reason I bring this up is that I'm interested in finding\n> ways to make our build system be streamlined a bit using the\n> presence of directories as a way to opt in/out of certain build\n> outputs. Since Scalar is being added as a new component, this is\n> a good opportunity to establish a pattern that works for this\n> effort, too.\n\nSure, the hard part isn't that you can't grep out nonexisting files or\ndirectories when building where we now use a glob.\n\nIt's that everything downstream of that, i.e. tests, installation\netc. is going to have to work properly in the face of arbitrary parts of\nwhat the developer who tested the code expected going missing.\n\nWhich is why we've generally carved out very specific things, usually\nalong the boundaries of installed dependencies.\n"},{"id":"436946","messageId":"87sfxu2kyc.fsf@evledraar.gmail.com","threadId":"56409","inReplyTo":"852ec003109b8244e2f9360ec64749779989c4a2.1631630356.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 01/15] scalar: create a rudimentary executable","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-09-24T12:52:55Z","receivedAt":"2021-09-24T13:23:23Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Sep 14 2021, Johannes Schindelin via GitGitGadget wrote:\n\n\n> +int cmd_main(int argc, const char **argv)\n> +{\n> +\tstruct strbuf scalar_usage = STRBUF_INIT;\n> +\tint i;\n> +\n> +\tif (argc > 1) {\n> +\t\targv++;\n> +\t\targc--;\n> +\n> +\t\tfor (i = 0; builtins[i].name; i++)\n> +\t\t\tif (!strcmp(builtins[i].name, argv[0]))\n> +\t\t\t\treturn !!builtins[i].fn(argc, argv);\n> +\t}\n> +\n> +\tstrbuf_addstr(&scalar_usage,\n> +\t\t      N_(\"scalar <command> [<options>]\\n\\nCommands:\\n\"));\n> +\tfor (i = 0; builtins[i].name; i++)\n> +\t\tstrbuf_addf(&scalar_usage, \"\\t%s\\n\", builtins[i].name);\n> +\n> +\tusage(scalar_usage.buf);\n> +}\n\nIn 04/15 you continue and use the parse-options.c API, but here it's the\nusage.c API, which is generally being phased out. Any reason for the\ndifference? It's preferrable not to add new usage() users if we can help\nit, I think we'll eventually want to remove it.\n"},{"id":"436988","messageId":"xmqqy27lvoyb.fsf@gitster.g","threadId":"56409","inReplyTo":"87sfxu2kyc.fsf@evledraar.gmail.com","subject":"Re: [PATCH v4 01/15] scalar: create a rudimentary executable","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-09-24T17:54:52Z","receivedAt":"2021-09-24T17:55:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> ... the\n> usage.c API, which is generally being phased out.\n\nThat is news to me.  Any reason why you think so?\n"},{"id":"437096","messageId":"8735pr2lcq.fsf@evledraar.gmail.com","threadId":"56409","inReplyTo":"xmqqy27lvoyb.fsf@gitster.g","subject":"Re: [PATCH v4 01/15] scalar: create a rudimentary executable","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-09-26T19:15:37Z","receivedAt":"2021-09-26T19:22:19Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Sep 24 2021, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>> ... the\n>> usage.c API, which is generally being phased out.\n>\n> That is news to me.  Any reason why you think so?\n\nPerhaps better phrased as \"generally going unused where we're using\nparse-options.c\", although some quick historical trends I ran as an\nad-hoc show usage() standing still since v1.6.0 in number of builtin*.c\nfiles[1], v.s. a 3x growth in usage_with_options() since then[2].\n\nBut in this case we're using an ad-hoc parser in cmd_main(), seemingly\nbecause it ends up copy/pasting a very small part of git.ci\nfunctionality over there, which is something that's been discussed as\nsomething we should move over to parse_options() sooner than later.\n\nIt looks like a better approach to just use parse_options() consistently\nin scalar.c, as e.g. commit-graph.c, stash.c, multi-pack-index.c\netc. that all implement a similar cmd/subcommand pattern do.\n\nThe end-state of duplicating the \"-C\" and \"-c\" options from git.c can\nthen easily be handled by parse-options.c, IIRC the stumbling point in\nmigrating over git.c was some of the statefulness of other parts\npotentially needing incremenatl parsing (i.e. via parse_options_step()\nand friends).\n\n1. $ parallel \"printf \"%s: \" {} && git grep -l '\\busage\\(' {} -- 'builtin*.c' | wc -l\" ::: v1.{1..9}.0 v2.{0..32}.0\nv1.1.0:0\nv1.2.0:0\nv1.3.0:0\nv1.4.0:20\nv1.5.0:51\nv1.7.0:35\nv1.6.0:40\nv1.8.0:34\nv2.1.0:32\nv1.9.0:33\nv2.0.0:33\nv2.2.0:32\nv2.3.0:32\nv2.4.0:32\nv2.5.0:32\nv2.6.0:31\nv2.7.0:31\nv2.9.0:29\nv2.8.0:30\nv2.11.0:29\nv2.10.0:29\nv2.12.0:29\nv2.13.0:29\nv2.14.0:31\nv2.15.0:31\nv2.16.0:31\nv2.17.0:31\nv2.19.0:32\nv2.18.0:31\nv2.22.0:31\nv2.21.0:32\nv2.24.0:31\nv2.23.0:31\nv2.20.0:32\nv2.25.0:30\nv2.26.0:30\nv2.27.0:30\nv2.32.0:29\nv2.31.0:30\nv2.29.0:31\nv2.28.0:29\nv2.30.0:31\n\n2. $ parallel \"printf \"%s: \" {} && git grep -l '\\busage_with_options\\(' {} -- 'builtin*.c' | wc -l\" ::: v1.{1..9}.0 v2.{0..32}.0\nv1.1.0:0\nv1.2.0:0\nv1.3.0:0\nv1.4.0:0\nv1.5.0:0\nv1.6.0:19\nv1.7.0:33\nv1.8.0:42\nv2.0.0:42\nv1.9.0:42\nv2.1.0:43\nv2.2.0:43\nv2.3.0:43\nv2.4.0:43\nv2.5.0:44\nv2.6.0:45\nv2.8.0:44\nv2.10.0:45\nv2.7.0:44\nv2.9.0:45\nv2.11.0:45\nv2.12.0:45\nv2.13.0:46\nv2.14.0:47\nv2.15.0:47\nv2.16.0:47\nv2.17.0:47\nv2.19.0:50\nv2.18.0:49\nv2.20.0:52\nv2.21.0:52\nv2.22.0:53\nv2.24.0:54\nv2.23.0:54\nv2.25.0:56\nv2.26.0:55\nv2.27.0:55\nv2.28.0:55\nv2.30.0:58\nv2.32.0:60\nv2.29.0:58\nv2.31.0:58\n"},{"id":"437186","messageId":"xmqqbl4dpxnc.fsf@gitster.g","threadId":"56409","inReplyTo":"8735pr2lcq.fsf@evledraar.gmail.com","subject":"Re: [PATCH v4 01/15] scalar: create a rudimentary executable","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-09-27T20:32:39Z","receivedAt":"2021-09-27T20:32:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> On Fri, Sep 24 2021, Junio C Hamano wrote:\n>\n>> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>>\n>>> ... the\n>>> usage.c API, which is generally being phased out.\n>>\n>> That is news to me.  Any reason why you think so?\n>\n> Perhaps better phrased as \"generally going unused where we're using\n> parse-options.c\", ...\n\nAh, if it was merely an observation of the general trend, then I\nagree.  My reaction was primarily \"Oh, why is somebody all of a\nsudden setting a project decision unilaterally, especially when even\nI do not do so very often myself?  Did I miss recent discussion that\nresulted in an update to Documentation/ meant for developers?\"\n\nThanks.\n"},{"id":"437246","messageId":"CABPp-BG_wupp1o5bBSYOJSvF3eJjf=TbX0RBHqqKuD+3F8s6hw@mail.gmail.com","threadId":"56409","inReplyTo":"bbbc4c3339043bcd718dd2defcbaaaac2092227a.1631630356.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 04/15] scalar: 'register' sets recommended config and starts maintenance","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-09-28T05:01:30Z","receivedAt":"2021-09-28T05:01:45Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Sep 14, 2021 at 7:39 AM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n...\n> +static int set_recommended_config(void)\n> +{\n> +       struct {\n> +               const char *key;\n> +               const char *value;\n> +       } config[] = {\n> +               { \"am.keepCR\", \"true\" },\n> +               { \"core.FSCache\", \"true\" },\n> +               { \"core.multiPackIndex\", \"true\" },\n> +               { \"core.preloadIndex\", \"true\" },\n> +#ifndef WIN32\n> +               { \"core.untrackedCache\", \"true\" },\n> +#else\n> +               /*\n> +                * Unfortunately, Scalar's Functional Tests demonstrated\n> +                * that the untracked cache feature is unreliable on Windows\n> +                * (which is a bummer because that platform would benefit the\n> +                * most from it). For some reason, freshly created files seem\n> +                * not to update the directory's `lastModified` time\n> +                * immediately, but the untracked cache would need to rely on\n> +                * that.\n> +                *\n> +                * Therefore, with a sad heart, we disable this very useful\n> +                * feature on Windows.\n> +                */\n> +               { \"core.untrackedCache\", \"false\" },\n> +#endif\n\nInteresting.  (I'm somewhat leery of the untrackedCache just knowing\nthat it used to operate despite an exponential number of visits to\nfiles (exponential in depth of directories) and getting different\nanswers with different visits, making me feel like it was black magic\nthat it ever worked and wondering what kind of corner case issues\nstill lurk with it.  See e.g.\nhttps://lore.kernel.org/git/CABPp-BFiwzzUgiTj_zu+vF5x20L0=1cf25cHwk7KZQj2YkVzXw@mail.gmail.com/)\n\n> +               { \"core.logAllRefUpdates\", \"true\" },\n> +               { \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n\nNot only opinionated, but special configuration for certain sites?\nI'm not complaining, just slightly surprised.\n\n> +               { \"credential.validate\", \"false\" }, /* GCM4W-only */\n> +               { \"gc.auto\", \"0\" },\n> +               { \"gui.GCWarning\", \"false\" },\n> +               { \"index.threads\", \"true\" },\n> +               { \"index.version\", \"4\" },\n\nI take it your users don't make use of jgit?  (Users aren't using jgit\ndirectly here, at least not to my knowledge, but multiple gradle\nplugins do.)  I tried turning this on a while back, and quickly got\nmultiple reports of problems because jgit didn't understand the index.\nI had to turn it off and send out various PSAs on how to recover.\n\n> +               { \"merge.stat\", \"false\" },\n> +               { \"merge.renames\", \"false\" },\n\nIs this just historical and not needed anymore, is it here just for a\nlittle longer and you are planning on transitioning away from this, or\nare you still set on this setting?\n\n> +               { \"pack.useBitmaps\", \"false\" },\n\nI don't understand anything bitmap related, but I thought they were\nperformance related, so I'm surprised by this one.  Is there a reason\nfor this one?  (Is it handled by maintenance instead?)\n\n> +               { \"pack.useSparse\", \"true\" },\n> +               { \"receive.autoGC\", \"false\" },\n> +               { \"reset.quiet\", \"true\" },\n> +               { \"feature.manyFiles\", \"false\" },\n\nIf you simply set core.untrackedCache to false _after_ setting\nfeature.manyFiles to true, would it make sense to switch this?  (Or\ndoes it matter, since you've already individually set all the config\nsettings that this one would set?)\n\n> +               { \"feature.experimental\", \"false\" },\n> +               { \"fetch.unpackLimit\", \"1\" },\n> +               { \"fetch.writeCommitGraph\", \"false\" },\n> +#ifdef WIN32\n> +               { \"http.sslBackend\", \"schannel\" },\n> +#endif\n> +               { \"status.aheadBehind\", \"false\" },\n> +               { \"commitGraph.generationVersion\", \"1\" },\n> +               { \"core.autoCRLF\", \"false\" },\n> +               { \"core.safeCRLF\", \"false\" },\n> +               { NULL, NULL },\n> +       };\n\nAre there easy-ish ways for other groups of users to adopt scalar but\nchange the list of config settings (e.g. index.version and\nmerge.renames) in some common way for all those users?\n"},{"id":"437247","messageId":"CABPp-BGLcLKk1QLHtb6KpCOpn=M1-wwK6KD91rLCYbh6n5gTYA@mail.gmail.com","threadId":"56409","inReplyTo":"bbbc4c3339043bcd718dd2defcbaaaac2092227a.1631630356.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 04/15] scalar: 'register' sets recommended config and starts maintenance","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-09-28T05:05:18Z","receivedAt":"2021-09-28T05:05:32Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Sorry, one more thing...\n\nOn Tue, Sep 14, 2021 at 7:39 AM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n...\n> +               /* check if currently in enlistment root with src/ workdir */\n> +               strbuf_addstr(&path, \"/src/.git\");\n> +               if (is_git_directory(path.buf)) {\n\n...and...\n\n> +               /* check if currently in workdir */\n> +               strbuf_addstr(&path, \"/.git\");\n> +               if (is_git_directory(path.buf)) {\n\nDo these two checks suggest that only a primary worktree can be\nenlisted with scalar?  (Is git-worktree generally incompatible?)\n"},{"id":"437248","messageId":"CABPp-BH0U9+KEHTuMdfu-jhL+y3eRzjyfuRzWy9-jGxka8PH+Q@mail.gmail.com","threadId":"56409","inReplyTo":"2cbf0b611133df5fa7eed1bf38460f9d119d2a6e.1630359290.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 08/15] scalar: implement the `clone` subcommand","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-09-28T05:19:28Z","receivedAt":"2021-09-28T05:19:42Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Aug 30, 2021 at 2:36 PM Johannes Schindelin via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n...\n>  COMMANDS\n>  --------\n>\n> +Clone\n> +~~~~~\n> +\n> +clone [<options>] <url> [<enlistment>]::\n> +    Clones the specified repository, similar to linkgit:git-clone[1]. By\n> +    default, only commit and tree objects are cloned. Once finished, the\n> +    worktree is located at `<enlistment>/src`.\n> ++\n> +The sparse-checkout feature is enabled (except when run with `--full-clone`)\n> +and the only files present are those in the top-level directory. Use\n> +`git sparse-checkout set` to expand the set of directories you want to see,\n> +or `git sparse-checkout disable` to expand to all files (see\n> +linkgit:git-sparse-checkout[1] for more details). You can explore the\n> +subdirectories outside your sparse-checkout by using `git ls-tree HEAD`.\n\nShould this be `git ls-tree [-r] HEAD`?  Do you expect people to just\nadd directories that are found immediately under the toplevel, rather\nthan some that are a bit deeper?\n"},{"id":"437249","messageId":"CABPp-BG3FwBVO67ZVPCLokA3xh5=fLiXYe-K2GiRa7ELFvQkEA@mail.gmail.com","threadId":"56409","inReplyTo":"ada242c7c8c1f530231f97e76c97fff18e368ff5.1631630356.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 11/15] scalar: allow reconfiguring an existing enlistment","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-09-28T05:24:47Z","receivedAt":"2021-09-28T05:25:02Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Sep 14, 2021 at 7:39 AM Johannes Schindelin via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> From: Johannes Schindelin <johannes.schindelin@gmx.de>\n>\n> This comes in handy during Scalar upgrades, or when config settings were\n> messed up by mistake.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  contrib/scalar/scalar.c          | 79 +++++++++++++++++++++-----------\n>  contrib/scalar/scalar.txt        |  8 ++++\n>  contrib/scalar/t/t9099-scalar.sh |  8 ++++\n>  3 files changed, 67 insertions(+), 28 deletions(-)\n>\n> diff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\n> index 8a11f390251..1fff7eb7c12 100644\n> --- a/contrib/scalar/scalar.c\n> +++ b/contrib/scalar/scalar.c\n> @@ -115,18 +115,20 @@ static int run_git(const char *arg, ...)\n>         return res;\n>  }\n>\n> -static int set_recommended_config(void)\n> +static int set_recommended_config(int reconfigure)\n>  {\n>         struct {\n>                 const char *key;\n>                 const char *value;\n> +               int overwrite_on_reconfigure;\n>         } config[] = {\n> -               { \"am.keepCR\", \"true\" },\n> -               { \"core.FSCache\", \"true\" },\n> -               { \"core.multiPackIndex\", \"true\" },\n> -               { \"core.preloadIndex\", \"true\" },\n> +               /* Required */\n> +               { \"am.keepCR\", \"true\", 1 },\n> +               { \"core.FSCache\", \"true\", 1 },\n> +               { \"core.multiPackIndex\", \"true\", 1 },\n> +               { \"core.preloadIndex\", \"true\", 1 },\n>  #ifndef WIN32\n> -               { \"core.untrackedCache\", \"true\" },\n> +               { \"core.untrackedCache\", \"true\", 1 },\n>  #else\n>                 /*\n>                  * Unfortunately, Scalar's Functional Tests demonstrated\n> @@ -140,28 +142,29 @@ static int set_recommended_config(void)\n>                  * Therefore, with a sad heart, we disable this very useful\n>                  * feature on Windows.\n>                  */\n> -               { \"core.untrackedCache\", \"false\" },\n> +               { \"core.untrackedCache\", \"false\", 1 },\n>  #endif\n> -               { \"core.logAllRefUpdates\", \"true\" },\n> -               { \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n> -               { \"credential.validate\", \"false\" }, /* GCM4W-only */\n> -               { \"gc.auto\", \"0\" },\n> -               { \"gui.GCWarning\", \"false\" },\n> -               { \"index.threads\", \"true\" },\n> -               { \"index.version\", \"4\" },\n> -               { \"merge.stat\", \"false\" },\n> -               { \"merge.renames\", \"false\" },\n> -               { \"pack.useBitmaps\", \"false\" },\n> -               { \"pack.useSparse\", \"true\" },\n> -               { \"receive.autoGC\", \"false\" },\n> -               { \"reset.quiet\", \"true\" },\n> -               { \"feature.manyFiles\", \"false\" },\n> -               { \"feature.experimental\", \"false\" },\n> -               { \"fetch.unpackLimit\", \"1\" },\n> -               { \"fetch.writeCommitGraph\", \"false\" },\n> +               { \"core.logAllRefUpdates\", \"true\", 1 },\n> +               { \"credential.https://dev.azure.com.useHttpPath\", \"true\", 1 },\n> +               { \"credential.validate\", \"false\", 1 }, /* GCM4W-only */\n> +               { \"gc.auto\", \"0\", 1 },\n> +               { \"gui.GCWarning\", \"false\", 1 },\n> +               { \"index.threads\", \"true\", 1 },\n> +               { \"index.version\", \"4\", 1 },\n> +               { \"merge.stat\", \"false\", 1 },\n> +               { \"merge.renames\", \"false\", 1 },\n> +               { \"pack.useBitmaps\", \"false\", 1 },\n> +               { \"pack.useSparse\", \"true\", 1 },\n> +               { \"receive.autoGC\", \"false\", 1 },\n> +               { \"reset.quiet\", \"true\", 1 },\n> +               { \"feature.manyFiles\", \"false\", 1 },\n> +               { \"feature.experimental\", \"false\", 1 },\n> +               { \"fetch.unpackLimit\", \"1\", 1 },\n> +               { \"fetch.writeCommitGraph\", \"false\", 1 },\n>  #ifdef WIN32\n> -               { \"http.sslBackend\", \"schannel\" },\n> +               { \"http.sslBackend\", \"schannel\", 1 },\n>  #endif\n> +               /* Optional */\n>                 { \"status.aheadBehind\", \"false\" },\n>                 { \"commitGraph.generationVersion\", \"1\" },\n>                 { \"core.autoCRLF\", \"false\" },\n\nNow you have optional settings...but index.version and merge.renames\naren't among them??  Why are those required?  (...and to go a step\nfurther; should merge.renames even be off in a merge-ort world?)\n"},{"id":"437253","messageId":"CABPp-BGzw4XUhEnUSnp5C1q-7qHcLP-8owbpqTfnbrCuwVkMQw@mail.gmail.com","threadId":"56409","inReplyTo":"914c16c7fcd948374fb2c0582c435d0d6ac775a0.1631630356.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 13/15] scalar: implement the `delete` command","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-09-28T06:24:05Z","receivedAt":"2021-09-28T06:24:37Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Sep 14, 2021 at 7:39 AM Matthew John Cheetham via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> From: Matthew John Cheetham <mjcheetham@outlook.com>\n>\n> Delete an enlistment by first unregistering the repository and then\n> deleting the enlistment directory (usually the directory containing the\n> worktree `src/` directory).\n>\n> On Windows, if the current directory is inside the enlistment's\n> directory, change to the parent of the enlistment directory, to allow us\n> to delete the enlistment (directories used by processes e.g. as current\n> working directories cannot be deleted on Windows).\n\nBut if the current directory is inside the enlistment's directory,\ndidn't that happen because the parent process' current directory was\ninside the enlistment directory?  Or was there some kind of directory\nswitching that scalar itself was doing causing it to be inside the\nenlistment directory?\n\nIf the the current directory was inside the enlistment's directory\nbecause it inherited a parent process' current directory, wouldn't\nthat also prevent deleting it?  If so, should there be a special check\nfor that case and pre-emptively returning an error rather than\nattempting the recursive directory deletion and just spitting out an\nerror when it fails?\n\n(Also seems slightly related to\nhttps://github.com/gitgitgadget/git/pull/1037, which I'll submit as\nsoon as en/removing_untracked_fixes hits next.)\n\n>\n> Co-authored-by: Victoria Dye <vdye@github.com>\n> Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  contrib/scalar/scalar.c          | 55 ++++++++++++++++++++++++++++++++\n>  contrib/scalar/scalar.txt        |  8 +++++\n>  contrib/scalar/t/t9099-scalar.sh |  9 ++++++\n>  3 files changed, 72 insertions(+)\n>\n> diff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\n> index 67fa5305225..00bedb0bf66 100644\n> --- a/contrib/scalar/scalar.c\n> +++ b/contrib/scalar/scalar.c\n> @@ -8,6 +8,7 @@\n>  #include \"config.h\"\n>  #include \"run-command.h\"\n>  #include \"refs.h\"\n> +#include \"dir.h\"\n>\n>  /*\n>   * Remove the deepest subdirectory in the provided path string. Path must not\n> @@ -334,6 +335,33 @@ static char *remote_default_branch(const char *url)\n>         return NULL;\n>  }\n>\n> +static int delete_enlistment(struct strbuf *enlistment)\n> +{\n> +#ifdef WIN32\n> +       struct strbuf parent = STRBUF_INIT;\n> +#endif\n> +\n> +       if (unregister_dir())\n> +               die(_(\"failed to unregister repository\"));\n> +\n> +#ifdef WIN32\n> +       /*\n> +        * Change the current directory to one outside of the enlistment so\n> +        * that we may delete everything underneath it.\n> +        */\n> +       strbuf_addbuf(&parent, enlistment);\n> +       strbuf_parent_directory(&parent);\n> +       if (chdir(parent.buf) < 0)\n> +               die_errno(_(\"could not switch to '%s'\"), parent.buf);\n> +       strbuf_release(&parent);\n> +#endif\n> +\n> +       if (remove_dir_recursively(enlistment, 0))\n> +               die(_(\"failed to delete enlistment directory\"));\n> +\n> +       return 0;\n> +}\n> +\n>  static int cmd_clone(int argc, const char **argv)\n>  {\n>         const char *branch = NULL;\n> @@ -694,6 +722,32 @@ static int cmd_unregister(int argc, const char **argv)\n>         return unregister_dir();\n>  }\n>\n> +static int cmd_delete(int argc, const char **argv)\n> +{\n> +       struct option options[] = {\n> +               OPT_END(),\n> +       };\n> +       const char * const usage[] = {\n> +               N_(\"scalar delete <enlistment>\"),\n> +               NULL\n> +       };\n> +       struct strbuf enlistment = STRBUF_INIT;\n> +       int res = 0;\n> +\n> +       argc = parse_options(argc, argv, NULL, options,\n> +                            usage, 0);\n> +\n> +       if (argc != 1)\n> +               usage_with_options(usage, options);\n> +\n> +       setup_enlistment_directory(argc, argv, usage, options, &enlistment);\n> +\n> +       res = delete_enlistment(&enlistment);\n> +       strbuf_release(&enlistment);\n> +\n> +       return res;\n> +}\n> +\n>  static struct {\n>         const char *name;\n>         int (*fn)(int, const char **);\n> @@ -704,6 +758,7 @@ static struct {\n>         { \"unregister\", cmd_unregister },\n>         { \"run\", cmd_run },\n>         { \"reconfigure\", cmd_reconfigure },\n> +       { \"delete\", cmd_delete },\n>         { NULL, NULL},\n>  };\n>\n> diff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\n> index 2fa96fcabc6..6fc57707718 100644\n> --- a/contrib/scalar/scalar.txt\n> +++ b/contrib/scalar/scalar.txt\n> @@ -14,6 +14,7 @@ scalar register [<enlistment>]\n>  scalar unregister [<enlistment>]\n>  scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n>  scalar reconfigure [ --all | <enlistment> ]\n> +scalar delete <enlistment>\n>\n>  DESCRIPTION\n>  -----------\n> @@ -127,6 +128,13 @@ reconfigure the enlistment.\n>  With the `--all` option, all enlistments currently registered with Scalar\n>  will be reconfigured. Use this option after each Scalar upgrade.\n>\n> +Delete\n> +~~~~~~\n> +\n> +delete <enlistment>::\n> +       This subcommand lets you delete an existing Scalar enlistment from your\n> +       local file system, unregistering the repository.\n> +\n>  SEE ALSO\n>  --------\n>  linkgit:git-clone[1], linkgit:git-maintenance[1].\n> diff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\n> index 5fe7fabd0e5..7e8771d0eff 100755\n> --- a/contrib/scalar/t/t9099-scalar.sh\n> +++ b/contrib/scalar/t/t9099-scalar.sh\n> @@ -76,4 +76,13 @@ test_expect_success 'scalar reconfigure' '\n>         test true = \"$(git -C one/src config core.preloadIndex)\"\n>  '\n>\n> +test_expect_success 'scalar delete without enlistment shows a usage' '\n> +       test_expect_code 129 scalar delete\n> +'\n> +\n> +test_expect_success 'scalar delete with enlistment' '\n> +       scalar delete cloned &&\n> +       test_path_is_missing cloned\n> +'\n> +\n>  test_done\n> --\n> gitgitgadget\n"},{"id":"437256","messageId":"87zgrxxiol.fsf@evledraar.gmail.com","threadId":"56409","inReplyTo":"CABPp-BG_wupp1o5bBSYOJSvF3eJjf=TbX0RBHqqKuD+3F8s6hw@mail.gmail.com","subject":"Re: [PATCH v4 04/15] scalar: 'register' sets recommended config and starts maintenance","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-09-28T07:27:02Z","receivedAt":"2021-09-28T07:28:47Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Sep 27 2021, Elijah Newren wrote:\n\n> On Tue, Sep 14, 2021 at 7:39 AM Derrick Stolee via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n> ...\n>> +               { \"pack.useBitmaps\", \"false\" },\n>\n> I don't understand anything bitmap related, but I thought they were\n> performance related, so I'm surprised by this one.  Is there a reason\n> for this one?  (Is it handled by maintenance instead?)\n\nI don't know why Derrick did this, but there's still AFAIK cases where\nbitmaps are worse than not in the context of a client (the scalar\nuse-case), see the rabbit hole starting at:\nhttps://lore.kernel.org/git/878s6nfq54.fsf@evledraar.gmail.com/\n"},{"id":"438093","messageId":"nycvar.QRO.7.76.6.2110062139040.395@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"xmqq1r5qzv35.fsf@gitster.g","subject":"Re: Train station analogy, was Re: [PATCH v3 00/15] [RFC] Upstreaming the Scalar command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-10-06T20:09:47Z","receivedAt":"2021-10-06T20:10:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 14 Sep 2021, Junio C Hamano wrote:\n\n> An alternative would be to bypass the contrib/ phase and start as a\n> new subcommand that is first-class citizen from day one and let it\n> spend as much time as it needs to mature.\n\nI don't think that there is a lot of sense in that. The main benefits of\n`scalar` are in the `register` and the `clone` part, and the most natural\nend game would hence be for `git init` and `git clone` to sprout new\noptions to support Scalar's features, in a Git-native way.\n\nAs I have explained earlier, the `scalar` command has existing users, and\ntherefore its command-line interface is not up for discussion (for\nexample, turning `scalar` into `git scalar` would be a usability\ndisaster). Scalar's _functionality_, however, should make it into Git\nproper. Into existing built-ins, that is.\n\nSo I don't think that the contrib/ phase can be by-passed. It would not\nmake sense to port Scalar to a new builtin. To the contrary,\ncontrib/scalar/ should be the final destination for the `scalar` command.\nAnd you can't bypass a final destination. That simply makes no sense.\n\nSo why bother with contrib/ at all? you may ask. The reason is that it\nmakes it substantially easier for me to move the features into core Git,\nas I can incrementally implement those new options for Git's built-ins,\nuse them in `contrib/scalar/` instead of duplicating the functionality,\nand then make use of Scalar's Functional Test suite for a much more\ncomprehensive testing (which has served us already really well in the\npast). It also doesn't hurt that this way, my day job will be very happy\nbecause Scalar users directly benefit from that work.\n\nOf course, these suggestions to integrate Scalar more into the core part\nof Git (missing the point that the final destination for the functionality\nis not a new built-in, but rather new options for existing built-ins) made\neverything much more cumbersome for me instead, for no gain that would be\napparent to me, impeding on aforementioned ease to move the features into\ncore Git (which has not happened yet, as a consequence), but hopefully\nthis will soon be a thing of the past.\n\nSo I would like to request that we close the discussion about the question\nwhether to integrate Scalar more into the top-level Makefile or into\ngit.c, and instead go ahead with keeping the `scalar` command in\ncontrib/scalar/. The freed-up time can then be used to focus on the much\nmore rewarding project of upstreaming Scalar's functionality such as\nteaching `git clone` a short-and-sweet option that Just Makes Sense for\nlarge monorepos (i.e. that imitates at least a large part of what `scalar\nclone` does right now).\n\nCiao,\nDscho\n"},{"id":"438094","messageId":"xmqqr1cxrjdl.fsf@gitster.g","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2110062139040.395@tvgsbejvaqbjf.bet","subject":"Re: Train station analogy, was Re: [PATCH v3 00/15] [RFC] Upstreaming the Scalar command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-10-06T20:25:10Z","receivedAt":"2021-10-06T20:25:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> I don't think that there is a lot of sense in that. The main benefits of\n> `scalar` are in the `register` and the `clone` part, and the most natural\n> end game would hence be for `git init` and `git clone` to sprout new\n> options to support Scalar's features, in a Git-native way.\n\nYes, that is even better.  An endgame where everybody benefits\nnatively would be highly desirable.\n\nNow you are back, do you think we can have the \"no more preserve\nmerges backend\" topic graduate to 'master', or do you prefer to cook\nit over the cycle (or even two)?\n\nThanks.\n"},{"id":"438095","messageId":"nycvar.QRO.7.76.6.2110062211500.395@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"CABPp-BG_wupp1o5bBSYOJSvF3eJjf=TbX0RBHqqKuD+3F8s6hw@mail.gmail.com","subject":"Re: [PATCH v4 04/15] scalar: 'register' sets recommended config and starts maintenance","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-10-06T20:32:35Z","receivedAt":"2021-10-06T20:32:59Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Elijah,\n\nOn Mon, 27 Sep 2021, Elijah Newren wrote:\n\n> On Tue, Sep 14, 2021 at 7:39 AM Derrick Stolee via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n> ...\n> > +static int set_recommended_config(void)\n> > +{\n> > +       struct {\n> > +               const char *key;\n> > +               const char *value;\n> > +       } config[] = {\n> > +               { \"am.keepCR\", \"true\" },\n> > +               { \"core.FSCache\", \"true\" },\n> > +               { \"core.multiPackIndex\", \"true\" },\n> > +               { \"core.preloadIndex\", \"true\" },\n> > +#ifndef WIN32\n> > +               { \"core.untrackedCache\", \"true\" },\n> > +#else\n> > +               /*\n> > +                * Unfortunately, Scalar's Functional Tests demonstrated\n> > +                * that the untracked cache feature is unreliable on Windows\n> > +                * (which is a bummer because that platform would benefit the\n> > +                * most from it). For some reason, freshly created files seem\n> > +                * not to update the directory's `lastModified` time\n> > +                * immediately, but the untracked cache would need to rely on\n> > +                * that.\n> > +                *\n> > +                * Therefore, with a sad heart, we disable this very useful\n> > +                * feature on Windows.\n> > +                */\n> > +               { \"core.untrackedCache\", \"false\" },\n> > +#endif\n>\n> Interesting.  (I'm somewhat leery of the untrackedCache just knowing\n> that it used to operate despite an exponential number of visits to\n> files (exponential in depth of directories) and getting different\n> answers with different visits, making me feel like it was black magic\n> that it ever worked and wondering what kind of corner case issues\n> still lurk with it.  See e.g.\n> https://lore.kernel.org/git/CABPp-BFiwzzUgiTj_zu+vF5x20L0=1cf25cHwk7KZQj2YkVzXw@mail.gmail.com/)\n\nThe implementation of the untracked cache certainly is quite a challenge\nto wrap one's head around, for sure. However, it does manage to speed up\noperations substantially (when it works).\n\nThe real fun starts when you turn on the FSMonitor, though. Then it is\nreliable, all of a sudden! The reason seems to be some sort of delayed\nlastModified (AKA mtime) evaluation which is somehow triggered by\nFSMonitor ;-)\n\nSo in microsoft/git, where we include FSMonitor and turn it on as part of\n`scalar clone`, we also enable the untracked cache, for noticeably happier\nusers.\n\n> > +               { \"core.logAllRefUpdates\", \"true\" },\n> > +               { \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n>\n> Not only opinionated, but special configuration for certain sites?\n> I'm not complaining, just slightly surprised.\n\nYes. I am not aware of other sites where you would want to use different\ncredentials depending on the URL path, but Azure DevOps definitely is such\na site, and therefore needs `useHttpPath`. Rather than requiring users to\nknow this, we set it for them.\n\n> > +               { \"credential.validate\", \"false\" }, /* GCM4W-only */\n> > +               { \"gc.auto\", \"0\" },\n> > +               { \"gui.GCWarning\", \"false\" },\n> > +               { \"index.threads\", \"true\" },\n> > +               { \"index.version\", \"4\" },\n>\n> I take it your users don't make use of jgit?\n\nNope ;-) I doubt that the features we use to make Git scalable are\nimplemented in JGit.\n\n> (Users aren't using jgit directly here, at least not to my knowledge,\n> but multiple gradle plugins do.)  I tried turning this on a while back,\n> and quickly got multiple reports of problems because jgit didn't\n> understand the index. I had to turn it off and send out various PSAs on\n> how to recover.\n\nTBH it gives me shivers of dread thinking about large\nrepositories/worktrees being handled within a Java VM. The amount of,\nlet's call it \"non-canonical\" code, required by JGit to make it somewhat\nperformant, is staggering. Just think about the way you have to emulate\nmmap()ing part of a packfile and interpreting it as a packed C struct. I\nforgot the details, of course, and I am quite glad that I did.\n\n> > +               { \"merge.stat\", \"false\" },\n> > +               { \"merge.renames\", \"false\" },\n>\n> Is this just historical and not needed anymore, is it here just for a\n> little longer and you are planning on transitioning away from this, or\n> are you still set on this setting?\n\nIt is here mostly for historical reasons.\n\n> > +               { \"pack.useBitmaps\", \"false\" },\n>\n> I don't understand anything bitmap related, but I thought they were\n> performance related, so I'm surprised by this one.  Is there a reason\n> for this one?  (Is it handled by maintenance instead?)\n\nAgain, this is here for historical reasons. Scalar sets this, and my goal\nwith this patch series is to port it from .NET to C. So I did not question\nthe reasoning.\n\nMy _guess_ however is that bitmaps really only work well when everything\nis in one single pack. Which is rather not the case with Scalar\nenlistments: they are way too large to be repacked all the time.\n\n> > +               { \"pack.useSparse\", \"true\" },\n> > +               { \"receive.autoGC\", \"false\" },\n> > +               { \"reset.quiet\", \"true\" },\n> > +               { \"feature.manyFiles\", \"false\" },\n>\n> If you simply set core.untrackedCache to false _after_ setting\n> feature.manyFiles to true, would it make sense to switch this?  (Or\n> does it matter, since you've already individually set all the config\n> settings that this one would set?)\n\nFrankly, I was a bit puzzled why `feature.manyFiles` was set to `false`.\nThe rationale is explained in\nhttps://github.com/microsoft/scalar/commit/2fc84dba9c95:\n\n\tThe feature.* config settings change the defaults for some other\n\tconfig settings. We already monitor config settings pretty carefully,\n\tso let's disable these.\n\nAs to switching this, it shouldn't matter. The idea of `feature.*` is to\nset defaults, but not override any explicitly configured settings.\n\n> > +               { \"feature.experimental\", \"false\" },\n> > +               { \"fetch.unpackLimit\", \"1\" },\n> > +               { \"fetch.writeCommitGraph\", \"false\" },\n> > +#ifdef WIN32\n> > +               { \"http.sslBackend\", \"schannel\" },\n> > +#endif\n> > +               { \"status.aheadBehind\", \"false\" },\n> > +               { \"commitGraph.generationVersion\", \"1\" },\n> > +               { \"core.autoCRLF\", \"false\" },\n> > +               { \"core.safeCRLF\", \"false\" },\n> > +               { NULL, NULL },\n> > +       };\n>\n> Are there easy-ish ways for other groups of users to adopt scalar but\n> change the list of config settings (e.g. index.version and\n> merge.renames) in some common way for all those users?\n\nNot in Scalar.\n\nI would hope, however, that we could figure out ways to make this more\nconfigurable when re-implementing this functionality in core Git. I have a\ncouple ideas, but nothing fleshed out, and besides, I do not want to think\ntoo far ahead, I already made that mistake and then got bogged down in\ndiscussions about minimal vs non-minimal changes in the top-level Makefile\n;-)\n\nSo yeah, good point, but it's probably not a good time yet to discuss this\ntangent.\n\nThank you for reviewing,\nDscho\n"},{"id":"438097","messageId":"nycvar.QRO.7.76.6.2110062233180.395@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"CABPp-BGLcLKk1QLHtb6KpCOpn=M1-wwK6KD91rLCYbh6n5gTYA@mail.gmail.com","subject":"Re: [PATCH v4 04/15] scalar: 'register' sets recommended config and starts maintenance","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-10-06T20:38:08Z","receivedAt":"2021-10-06T20:38:24Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Elijah,\n\nOn Mon, 27 Sep 2021, Elijah Newren wrote:\n\n> Sorry, one more thing...\n>\n> On Tue, Sep 14, 2021 at 7:39 AM Derrick Stolee via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n> ...\n> > +               /* check if currently in enlistment root with src/ workdir */\n> > +               strbuf_addstr(&path, \"/src/.git\");\n> > +               if (is_git_directory(path.buf)) {\n>\n> ...and...\n>\n> > +               /* check if currently in workdir */\n> > +               strbuf_addstr(&path, \"/.git\");\n> > +               if (is_git_directory(path.buf)) {\n>\n> Do these two checks suggest that only a primary worktree can be\n> enlisted with scalar?  (Is git-worktree generally incompatible?)\n\nGood point! I think we'll need to use `is_nonbare_repository_dir()`\ninstead.\n\nThis also has the additional benefit of doing away with quite a bit of\n`/.git` appending and undoing it. I.e. it simplifies the code\ndramatically.\n\nCiao,\nDscho\n"},{"id":"438099","messageId":"nycvar.QRO.7.76.6.2110062238540.395@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"CABPp-BH0U9+KEHTuMdfu-jhL+y3eRzjyfuRzWy9-jGxka8PH+Q@mail.gmail.com","subject":"Re: [PATCH 08/15] scalar: implement the `clone` subcommand","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-10-06T20:40:06Z","receivedAt":"2021-10-06T20:40:24Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Elijah,\n\nOn Mon, 27 Sep 2021, Elijah Newren wrote:\n\n> On Mon, Aug 30, 2021 at 2:36 PM Johannes Schindelin via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n> >\n> ...\n> >  COMMANDS\n> >  --------\n> >\n> > +Clone\n> > +~~~~~\n> > +\n> > +clone [<options>] <url> [<enlistment>]::\n> > +    Clones the specified repository, similar to linkgit:git-clone[1]. By\n> > +    default, only commit and tree objects are cloned. Once finished, the\n> > +    worktree is located at `<enlistment>/src`.\n> > ++\n> > +The sparse-checkout feature is enabled (except when run with `--full-clone`)\n> > +and the only files present are those in the top-level directory. Use\n> > +`git sparse-checkout set` to expand the set of directories you want to see,\n> > +or `git sparse-checkout disable` to expand to all files (see\n> > +linkgit:git-sparse-checkout[1] for more details). You can explore the\n> > +subdirectories outside your sparse-checkout by using `git ls-tree HEAD`.\n>\n> Should this be `git ls-tree [-r] HEAD`?  Do you expect people to just\n> add directories that are found immediately under the toplevel, rather\n> than some that are a bit deeper?\n\nI fear that `git ls-tree -r HEAD` in any monorepo might be a bit too\noverwhelming for any reader.\n\nBut I agree that just looking at HEAD is probably not enough. Maybe we\nshould use `git ls-tree HEAD[:<dir>]`?\n\nCiao,\nDscho\n"},{"id":"438102","messageId":"nycvar.QRO.7.76.6.2110062241150.395@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"CABPp-BG3FwBVO67ZVPCLokA3xh5=fLiXYe-K2GiRa7ELFvQkEA@mail.gmail.com","subject":"Re: [PATCH v4 11/15] scalar: allow reconfiguring an existing enlistment","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-10-06T20:43:18Z","receivedAt":"2021-10-06T20:43:34Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Elijah,\n\nOn Mon, 27 Sep 2021, Elijah Newren wrote:\n\n> On Tue, Sep 14, 2021 at 7:39 AM Johannes Schindelin via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n> >\n> > From: Johannes Schindelin <johannes.schindelin@gmx.de>\n> >\n> > This comes in handy during Scalar upgrades, or when config settings were\n> > messed up by mistake.\n> >\n> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > ---\n> >  contrib/scalar/scalar.c          | 79 +++++++++++++++++++++-----------\n> >  contrib/scalar/scalar.txt        |  8 ++++\n> >  contrib/scalar/t/t9099-scalar.sh |  8 ++++\n> >  3 files changed, 67 insertions(+), 28 deletions(-)\n> >\n> > diff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\n> > index 8a11f390251..1fff7eb7c12 100644\n> > --- a/contrib/scalar/scalar.c\n> > +++ b/contrib/scalar/scalar.c\n> > @@ -115,18 +115,20 @@ static int run_git(const char *arg, ...)\n> >         return res;\n> >  }\n> >\n> > -static int set_recommended_config(void)\n> > +static int set_recommended_config(int reconfigure)\n> >  {\n> >         struct {\n> >                 const char *key;\n> >                 const char *value;\n> > +               int overwrite_on_reconfigure;\n> >         } config[] = {\n> > -               { \"am.keepCR\", \"true\" },\n> > -               { \"core.FSCache\", \"true\" },\n> > -               { \"core.multiPackIndex\", \"true\" },\n> > -               { \"core.preloadIndex\", \"true\" },\n> > +               /* Required */\n> > +               { \"am.keepCR\", \"true\", 1 },\n> > +               { \"core.FSCache\", \"true\", 1 },\n> > +               { \"core.multiPackIndex\", \"true\", 1 },\n> > +               { \"core.preloadIndex\", \"true\", 1 },\n> >  #ifndef WIN32\n> > -               { \"core.untrackedCache\", \"true\" },\n> > +               { \"core.untrackedCache\", \"true\", 1 },\n> >  #else\n> >                 /*\n> >                  * Unfortunately, Scalar's Functional Tests demonstrated\n> > @@ -140,28 +142,29 @@ static int set_recommended_config(void)\n> >                  * Therefore, with a sad heart, we disable this very useful\n> >                  * feature on Windows.\n> >                  */\n> > -               { \"core.untrackedCache\", \"false\" },\n> > +               { \"core.untrackedCache\", \"false\", 1 },\n> >  #endif\n> > -               { \"core.logAllRefUpdates\", \"true\" },\n> > -               { \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n> > -               { \"credential.validate\", \"false\" }, /* GCM4W-only */\n> > -               { \"gc.auto\", \"0\" },\n> > -               { \"gui.GCWarning\", \"false\" },\n> > -               { \"index.threads\", \"true\" },\n> > -               { \"index.version\", \"4\" },\n> > -               { \"merge.stat\", \"false\" },\n> > -               { \"merge.renames\", \"false\" },\n> > -               { \"pack.useBitmaps\", \"false\" },\n> > -               { \"pack.useSparse\", \"true\" },\n> > -               { \"receive.autoGC\", \"false\" },\n> > -               { \"reset.quiet\", \"true\" },\n> > -               { \"feature.manyFiles\", \"false\" },\n> > -               { \"feature.experimental\", \"false\" },\n> > -               { \"fetch.unpackLimit\", \"1\" },\n> > -               { \"fetch.writeCommitGraph\", \"false\" },\n> > +               { \"core.logAllRefUpdates\", \"true\", 1 },\n> > +               { \"credential.https://dev.azure.com.useHttpPath\", \"true\", 1 },\n> > +               { \"credential.validate\", \"false\", 1 }, /* GCM4W-only */\n> > +               { \"gc.auto\", \"0\", 1 },\n> > +               { \"gui.GCWarning\", \"false\", 1 },\n> > +               { \"index.threads\", \"true\", 1 },\n> > +               { \"index.version\", \"4\", 1 },\n> > +               { \"merge.stat\", \"false\", 1 },\n> > +               { \"merge.renames\", \"false\", 1 },\n> > +               { \"pack.useBitmaps\", \"false\", 1 },\n> > +               { \"pack.useSparse\", \"true\", 1 },\n> > +               { \"receive.autoGC\", \"false\", 1 },\n> > +               { \"reset.quiet\", \"true\", 1 },\n> > +               { \"feature.manyFiles\", \"false\", 1 },\n> > +               { \"feature.experimental\", \"false\", 1 },\n> > +               { \"fetch.unpackLimit\", \"1\", 1 },\n> > +               { \"fetch.writeCommitGraph\", \"false\", 1 },\n> >  #ifdef WIN32\n> > -               { \"http.sslBackend\", \"schannel\" },\n> > +               { \"http.sslBackend\", \"schannel\", 1 },\n> >  #endif\n> > +               /* Optional */\n> >                 { \"status.aheadBehind\", \"false\" },\n> >                 { \"commitGraph.generationVersion\", \"1\" },\n> >                 { \"core.autoCRLF\", \"false\" },\n>\n> Now you have optional settings...but index.version and merge.renames\n> aren't among them??  Why are those required?  (...and to go a step\n> further; should merge.renames even be off in a merge-ort world?)\n\nI think the idea here is that they are required so that a `scalar\nreconfigure` will set them, even if the current enlistment had been\ncreated by a previous Scalar version that had _not_ set those.\n\nAnd yes, in a merge-ort world, `merge.renames` should probably be forced\nto `true`, again because it is in the \"Required\" section.\n\nCiao,\nDscho\n"},{"id":"438104","messageId":"nycvar.QRO.7.76.6.2110062243420.395@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"468CE4B8-D2C9-4FBC-B801-739F86C88ACB@outlook.com","subject":"Re: [PATCH v4 13/15] scalar: implement the `delete` command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-10-06T20:48:30Z","receivedAt":"2021-10-06T20:48:45Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Matthew and Elijah,\n\nOn Mon, 4 Oct 2021, Matthew Cheetham wrote:\n\n> On 28 Sep 2021, at 7:24 am, Elijah Newren <newren@gmail.com> wrote:\n>\n> > But if the current directory is inside the enlistment's directory,\n> > didn't that happen because the parent process' current directory was\n> > inside the enlistment directory?  Or was there some kind of directory\n> > switching that scalar itself was doing causing it to be inside the\n> > enlistment directory?\n>\n> Yes to the latter. `setup_enlistment_directory` changes the current\n> directory much like `setup_git_directory`.\n>\n> > If the the current directory was inside the enlistment's directory\n> > because it inherited a parent process' current directory, wouldn't\n> > that also prevent deleting it?  If so, should there be a special check\n> > for that case and pre-emptively returning an error rather than\n> > attempting the recursive directory deletion and just spitting out an\n> > error when it fails?\n>\n> You are correct. Speaking to Johannes about this I believe he is looking\n> to add a check/error in a new patch series revision.\n\nIndeed, I did notice GGG#1037, and I changed the code so that it detects\nwhether `scalar delete` was called from within the enlistment, and refuses\nto run in that case. Users will have to call `scalar delete <path>` from\noutside the enlistment.\n\nCiao,\nDscho\n"},{"id":"438108","messageId":"875yu9iolf.fsf@evledraar.gmail.com","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2110062139040.395@tvgsbejvaqbjf.bet","subject":"Re: Train station analogy, was Re: [PATCH v3 00/15] [RFC] Upstreaming the Scalar command","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-10-07T01:03:40Z","receivedAt":"2021-10-07T01:57:21Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Oct 06 2021, Johannes Schindelin wrote:\n\n> On Tue, 14 Sep 2021, Junio C Hamano wrote:\n>\n>> An alternative would be to bypass the contrib/ phase and start as a\n>> new subcommand that is first-class citizen from day one and let it\n>> spend as much time as it needs to mature.\n>\n> I don't think that there is a lot of sense in that. The main benefits of\n> `scalar` are in the `register` and the `clone` part, and the most natural\n> end game would hence be for `git init` and `git clone` to sprout new\n> options to support Scalar's features, in a Git-native way.\n>\n> As I have explained earlier, the `scalar` command has existing users,\n> and therefore its command-line interface is not up for discussion (for\n> example, turning `scalar` into `git scalar` would be a usability\n> disaster). Scalar's _functionality_, however, should make it into Git\n> proper. Into existing built-ins, that is.\n\nGiven the sub-thread this seems like it's trying to be an indirect reply\nto me, but I haven't been advocating changing the UX of \"scalar\" in any\nway, or that we should have a \"git scalar\".\n\nI have in fact been advocating exactly what you're advocating here. So\nwe're in violent agreement. Yes the CLI UI shouldn't change, that's the\nwhole point of having a \"scalar\" in git.git, not a \"git scalar\" or\nwhatever.\n\nI've been suggesting we can simplify the *build system* git.git uses,\nsomething no user will ever see.\n\n> So I don't think that the contrib/ phase can be by-passed. It would not\n> make sense to port Scalar to a new builtin. To the contrary,\n> contrib/scalar/ should be the final destination for the `scalar` command.\n> And you can't bypass a final destination. That simply makes no sense.\n\nI'm right now using a \"scalar\" installed on my system based on the diff\nat the end of this E-Mail that changes the *build system* without any\nuser-facing changes (see\nhttps://lore.kernel.org/git/87k0jhn0p9.fsf@evledraar.gmail.com/) for a\nprevious reference.\n    \n    $ which scalar\n    /home/avar/local/bin/scalar\n    $ scalar -h 2>&1 | head -n 1\n    usage: scalar [-C <directory>] [-c <key>=<value>] <command> [<options>]\n    $ man scalar\n    [...]\n    NAME\n           scalar - an opinionated repository management tool\n    $ git help scalar\n    No manual entry for gitscalar\n\nExcept that I think your patches as they stand (correct me if I'm wrong)\ndon't have any way to install it or its documentation, just to build it\nin-place.\n\n> So why bother with contrib/ at all? you may ask. The reason is that it\n> makes it substantially easier for me to move the features into core Git,\n> as I can incrementally implement those new options for Git's built-ins,\n> use them in `contrib/scalar/` instead of duplicating the functionality,\n> and then make use of Scalar's Functional Test suite for a much more\n> comprehensive testing (which has served us already really well in the\n> past). It also doesn't hurt that this way, my day job will be very happy\n> because Scalar users directly benefit from that work.\n\nI think all of that summarizes \"why have this live in git.git\", which I\n100% agree with. It's not a counter-argument to a working solution for\nsimplifying your proposed build system integration.\n\n> Of course, these suggestions to integrate Scalar more into the core part\n> of Git (missing the point that the final destination for the functionality\n> is not a new built-in, but rather new options for existing built-ins) made\n> everything much more cumbersome for me instead, for no gain that would be\n> apparent to me, impeding on aforementioned ease to move the features into\n> core Git (which has not happened yet, as a consequence), but hopefully\n> this will soon be a thing of the past.\n\nThe diff below doesn't make scalar a built-in.\n\n> So I would like to request that we close the discussion about the question\n> whether to integrate Scalar more into the top-level Makefile or into\n> git.c, and instead go ahead with keeping the `scalar` command in\n> contrib/scalar/. The freed-up time can then be used to focus on the much\n> more rewarding project of upstreaming Scalar's functionality such as\n> teaching `git clone` a short-and-sweet option that Just Makes Sense for\n> large monorepos (i.e. that imitates at least a large part of what `scalar\n> clone` does right now).\n\nThe reason I care about this is because duplicating this as a one-off\nmay be easier for you now, but it creates a lot of maintenance burden\nfor others down the line.\n\nFor example, I've been fixing memory leaks recently and have a\nlinux-leaks CI job that's now running on GitHub. Scalar would benefit\nfrom having a t/t9099-scalar.sh run as part of that.\n\nYes you can special-case it somehow and teach ci/, or even \"make test\"\nto do the same thing. But there's a lot of little things both current\nand future that are going to be like that. Here's another one:\n\n    $ make -C contrib/scalar scalar\n    $ echo bad >cache.h\n    $ make -C contrib/scalar scalar\n    make: 'scalar' is up to date.\n\nI.e. because it's using its own Makefile it's not getting the very\nbasics of the dependency graph right. My version?\n\n    $ make scalar\n    $ touch cache.h\n    $ make scalar\n    [... Works exactly as well as doing the same with \"git\"...]\n\nYes we could fix that and other things etc, but why not just get all\nthat for free in around 1/4 lines of Makefile boilerplate (and less than\nthat in complexity)?\n\n .gitignore                                   |  1 +\n Documentation/Makefile                       |  3 ++\n {contrib/scalar => Documentation}/scalar.txt |  4 +-\n Makefile                                     | 44 +++++++++++-----\n contrib/scalar/.gitignore                    |  5 --\n contrib/scalar/Makefile                      | 57 --------------------\n contrib/scalar/t/Makefile                    | 78 ----------------------------\n contrib/scalar/scalar.c => scalar.c          |  0\n {contrib/scalar/t => t}/t9099-scalar.sh      |  8 +--\n 9 files changed, 37 insertions(+), 163 deletions(-)\n\n[Note that this probably doesn't fully apply to your series/master, not\nbecause of any great textual/semantic conflict, but just because I find\nit convenient to stack my WIP/unsubmitted serieses in related areas on\ntop of one another. This is on top of other Makefile changes I've got\nunsubmitted, but it would be easy/trivial to re-apply on master+your\nseries].\n\ndiff --git a/.gitignore b/.gitignore\nindex 68ecb7f7e9c..ec9771e3532 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -217,6 +217,7 @@\n /configure\n /.vscode/\n /tags\n+/scalar\n /TAGS\n /cscope*\n /compile_commands.json\ndiff --git a/Documentation/Makefile b/Documentation/Makefile\nindex f5605b7767f..57b72e1999a 100644\n--- a/Documentation/Makefile\n+++ b/Documentation/Makefile\n@@ -18,6 +18,9 @@ MAN1_TXT += $(filter-out \\\n MAN1_TXT += git.txt\n MAN1_TXT += gitk.txt\n MAN1_TXT += gitweb.txt\n+ifndef NO_INSTALL_SCALAR_DOC\n+MAN1_TXT += scalar.txt\n+endif\n \n # man5 / man7 guides (note: new guides should also be added to command-list.txt)\n MAN5_TXT += gitattributes.txt\ndiff --git a/contrib/scalar/scalar.txt b/Documentation/scalar.txt\nsimilarity index 99%\nrename from contrib/scalar/scalar.txt\nrename to Documentation/scalar.txt\nindex 3a80f829edc..f287ab18c31 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/Documentation/scalar.txt\n@@ -149,6 +149,6 @@ SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\n \n-Scalar\n+GIT\n ---\n-Associated with the linkgit:git[1] suite\n+Part of the linkgit:git[1] suite\ndiff --git a/Makefile b/Makefile\nindex fd617f8c775..536028922e1 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -602,6 +602,7 @@ LIB_OBJS =\n LIB_OBJS_NO_COMPAT_OBJS =\n OBJECTS =\n PROGRAM_OBJS =\n+SCALAR_OBJS =\n SCRIPT_LIB =\n SCRIPT_PERL =\n SCRIPT_PROGRAMS =\n@@ -807,6 +808,7 @@ BUILT_INS += $(BUILT_INS_EXTRA)\n \n # what 'all' will build but not install in gitexecdir\n OTHER_PROGRAMS = git$X\n+OTHER_PROGRAMS += scalar$X\n ARTIFACTS_TAR += $(OTHER_PROGRAMS)\n \n # what test wrappers are needed and 'install' will install, in bindir\n@@ -818,12 +820,18 @@ BINDIR_PROGRAMS_NEED_X += git-upload-pack\n \n BINDIR_PROGRAMS_NO_X += git-cvsserver\n \n+ifdef INSTALL_SCALAR\n+BINDIR_PROGRAMS_NEED_X += scalar\n+endif\n INSTALL_BINDIR_XPROGRAMS += $(patsubst %,%$X,$(BINDIR_PROGRAMS_NEED_X))\n INSTALL_BINDIR_PROGRAMS += $(INSTALL_BINDIR_XPROGRAMS) $(BINDIR_PROGRAMS_NO_X)\n \n # We have bin-wrappers for programs that we don't install\n TEST_BINDIR_PROGRAMS_NEED_X += $(BINDIR_PROGRAMS_NEED_X)\n TEST_BINDIR_PROGRAMS_NEED_X += $(TEST_PROGRAMS_NEED_X)\n+ifndef INSTALL_SCALAR\n+TEST_BINDIR_PROGRAMS_NEED_X += scalar$X\n+endif\n \n TEST_BINDIR_PROGRAMS += $(TEST_BINDIR_PROGRAMS_NEED_X)\n TEST_BINDIR_PROGRAMS += $(BINDIR_PROGRAMS_NO_X)\n@@ -2230,7 +2238,7 @@ please_set_SHELL_PATH_to_a_more_modern_shell:\n \n shell_compatibility_test: please_set_SHELL_PATH_to_a_more_modern_shell\n \n-strip: $(BIN_PROGRAMS) git$X\n+strip: $(BIN_PROGRAMS) git$X scalar$X\n \t$(STRIP) $(STRIP_OPTS) $^\n \n ### Flags affecting all rules\n@@ -2286,6 +2294,10 @@ git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS)\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \\\n \t\t$(filter %.o,$^) $(LIBS)\n \n+scalar$X: scalar.o GIT-LDFLAGS $(GITLIBS)\n+\t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \\\n+\t\t$(filter %.o,$^) $(LIBS)\n+\n help.sp help.s help.o: command-list.h\n \n builtin/help.sp builtin/help.s builtin/help.o: config-list.h GIT-PREFIX\n@@ -2557,7 +2569,12 @@ GIT_OBJS += git.o\n .PHONY: git-objs\n git-objs: $(GIT_OBJS)\n \n+SCALAR_OBJS += scalar.o\n+.PHONY: scalar-objs\n+scalar-objs: $(SCALAR_OBJS)\n+\n OBJECTS += $(GIT_OBJS)\n+OBJECTS += $(SCALAR_OBJS)\n OBJECTS += $(PROGRAM_OBJS)\n OBJECTS += $(TEST_OBJS)\n OBJECTS += $(XDIFF_OBJS)\n@@ -2565,13 +2582,10 @@ OBJECTS += $(FUZZ_OBJS)\n ifndef NO_CURL\n \tOBJECTS += http.o http-walker.o remote-curl.o\n endif\n+OBJECTS += $(SCALAR_OBJECTS)\n .PHONY: objects\n objects: $(OBJECTS)\n \n-SCALAR_SOURCES := contrib/scalar/scalar.c\n-SCALAR_OBJECTS := $(SCALAR_SOURCES:c=o)\n-OBJECTS += $(SCALAR_OBJECTS)\n-\n dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d)\n dep_dirs := $(addsuffix .depend,$(sort $(dir $(OBJECTS))))\n \n@@ -2710,10 +2724,6 @@ $(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n \t\t$(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)\n \n-contrib/scalar/scalar$X: $(SCALAR_OBJECTS) GIT-LDFLAGS $(GITLIBS)\n-\t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \\\n-\t\t$(filter %.o,$^) $(LIBS)\n-\n $(LIB_FILE): $(LIB_OBJS) $(LIB_COMPAT_OBJS)\n \t$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^\n \n@@ -3260,11 +3270,17 @@ endif\n install-gitweb:\n \t$(MAKE) -C gitweb install\n \n+ifdef INSTALL_SCALAR\n+NO_INSTALL_SCALAR_DOC =\n+else\n+NO_INSTALL_SCALAR_DOC = NoScalarPlease\n+endif\n+\n install-doc: install-man-perl\n-\t$(MAKE) -C Documentation install\n+\t$(MAKE) -C Documentation install NO_INSTALL_SCALAR_DOC=$(NO_INSTALL_SCALAR_DOC)\n \n install-man: install-man-perl\n-\t$(MAKE) -C Documentation install-man\n+\t$(MAKE) -C Documentation install-man NO_INSTALL_SCALAR_DOC=$(NO_INSTALL_SCALAR_DOC)\n \n install-man-perl: man-perl\n \t$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(mandir_SQ)/man3'\n@@ -3272,13 +3288,13 @@ install-man-perl: man-perl\n \t(cd '$(DESTDIR_SQ)$(mandir_SQ)/man3' && umask 022 && $(TAR) xof -)\n \n install-html:\n-\t$(MAKE) -C Documentation install-html\n+\t$(MAKE) -C Documentation install-html NO_INSTALL_SCALAR_DOC=$(NO_INSTALL_SCALAR_DOC)\n \n install-info:\n-\t$(MAKE) -C Documentation install-info\n+\t$(MAKE) -C Documentation install-info NO_INSTALL_SCALAR_DOC=$(NO_INSTALL_SCALAR_DOC)\n \n install-pdf:\n-\t$(MAKE) -C Documentation install-pdf\n+\t$(MAKE) -C Documentation install-pdf NO_INSTALL_SCALAR_DOC=$(NO_INSTALL_SCALAR_DOC)\n \n quick-install-doc:\n \t$(MAKE) -C Documentation quick-install\ndiff --git a/contrib/scalar/.gitignore b/contrib/scalar/.gitignore\ndeleted file mode 100644\nindex 00441073f59..00000000000\n--- a/contrib/scalar/.gitignore\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-/*.xml\n-/*.1\n-/*.html\n-/*.exe\n-/scalar\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\ndeleted file mode 100644\nindex 8620042f281..00000000000\n--- a/contrib/scalar/Makefile\n+++ /dev/null\n@@ -1,57 +0,0 @@\n-QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir\n-QUIET_SUBDIR1  =\n-\n-ifneq ($(findstring s,$(MAKEFLAGS)),s)\n-ifndef V\n-\tQUIET_GEN      = @echo '   ' GEN $@;\n-\tQUIET_SUBDIR0  = +@subdir=\n-\tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n-\t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n-\tQUIET          = @\n-else\n-\texport V\n-endif\n-endif\n-\n-all:\n-\n-include ../../config.mak.uname\n--include ../../config.mak.autogen\n--include ../../config.mak\n-\n-TARGETS = scalar$(X) scalar.o\n-GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n-\n-all: scalar$X ../../bin-wrappers/scalar\n-\n-$(GITLIBS):\n-\t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)\n-\n-$(TARGETS): $(GITLIBS) scalar.c\n-\t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)\n-\n-clean:\n-\t$(RM) $(TARGETS) ../../bin-wrappers/scalar\n-\t$(RM) scalar.1 scalar.html scalar.xml\n-\n-../../bin-wrappers/scalar: ../../wrap-for-bin.sh Makefile\n-\t@mkdir -p ../../bin-wrappers\n-\t$(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \\\n-\t     -e 's|@@BUILD_DIR@@|$(shell cd ../.. && pwd)|' \\\n-\t     -e 's|@@PROG@@|contrib/scalar/scalar$(X)|' < $< > $@ && \\\n-\tchmod +x $@\n-\n-test: all\n-\t$(MAKE) -C t\n-\n-docs: scalar.html scalar.1\n-\n-scalar.html: | scalar.1 # prevent them from trying to build `doc.dep` in parallel\n-\n-scalar.html scalar.1: scalar.txt\n-\t$(QUIET_SUBDIR0)../../Documentation$(QUIET_SUBDIR1) \\\n-\t\tMAN_TXT=../contrib/scalar/scalar.txt \\\n-\t\t../contrib/scalar/$@\n-\t$(QUIET)test scalar.1 != \"$@\" || mv ../../Documentation/$@ .\n-\n-.PHONY: all clean docs test FORCE\ndiff --git a/contrib/scalar/t/Makefile b/contrib/scalar/t/Makefile\ndeleted file mode 100644\nindex 6170672bb37..00000000000\n--- a/contrib/scalar/t/Makefile\n+++ /dev/null\n@@ -1,78 +0,0 @@\n-# Run scalar tests\n-#\n-# Copyright (c) 2005,2021 Junio C Hamano, Johannes Schindelin\n-#\n-\n--include ../../../config.mak.autogen\n--include ../../../config.mak\n-\n-SHELL_PATH ?= $(SHELL)\n-PERL_PATH ?= /usr/bin/perl\n-RM ?= rm -f\n-PROVE ?= prove\n-DEFAULT_TEST_TARGET ?= test\n-TEST_LINT ?= test-lint\n-\n-ifdef TEST_OUTPUT_DIRECTORY\n-TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results\n-else\n-TEST_RESULTS_DIRECTORY = ../../../t/test-results\n-endif\n-\n-# Shell quote;\n-SHELL_PATH_SQ = $(subst ','\\'',$(SHELL_PATH))\n-PERL_PATH_SQ = $(subst ','\\'',$(PERL_PATH))\n-TEST_RESULTS_DIRECTORY_SQ = $(subst ','\\'',$(TEST_RESULTS_DIRECTORY))\n-\n-T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))\n-\n-all: $(DEFAULT_TEST_TARGET)\n-\n-test: $(TEST_LINT)\n-\t$(MAKE) aggregate-results-and-cleanup\n-\n-prove: $(TEST_LINT)\n-\t@echo \"*** prove ***\"; GIT_CONFIG=.git/config $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)\n-\t$(MAKE) clean-except-prove-cache\n-\n-$(T):\n-\t@echo \"*** $@ ***\"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)\n-\n-clean-except-prove-cache:\n-\t$(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)'\n-\t$(RM) -r valgrind/bin\n-\n-clean: clean-except-prove-cache\n-\t$(RM) .prove\n-\n-test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax\n-\n-test-lint-duplicates:\n-\t@dups=`echo $(T) | tr ' ' '\\n' | sed 's/-.*//' | sort | uniq -d` && \\\n-\t\ttest -z \"$$dups\" || { \\\n-\t\techo >&2 \"duplicate test numbers:\" $$dups; exit 1; }\n-\n-test-lint-executable:\n-\t@bad=`for i in $(T); do test -x \"$$i\" || echo $$i; done` && \\\n-\t\ttest -z \"$$bad\" || { \\\n-\t\techo >&2 \"non-executable tests:\" $$bad; exit 1; }\n-\n-test-lint-shell-syntax:\n-\t@'$(PERL_PATH_SQ)' ../../../t/check-non-portable-shell.pl $(T)\n-\n-aggregate-results-and-cleanup: $(T)\n-\t$(MAKE) aggregate-results\n-\t$(MAKE) clean\n-\n-aggregate-results:\n-\tfor f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \\\n-\t\techo \"$$f\"; \\\n-\tdone | '$(SHELL_PATH_SQ)' ../../../t/aggregate-results.sh\n-\n-valgrind:\n-\t$(MAKE) GIT_TEST_OPTS=\"$(GIT_TEST_OPTS) --valgrind\"\n-\n-test-results:\n-\tmkdir -p test-results\n-\n-.PHONY: $(T) aggregate-results clean valgrind\ndiff --git a/contrib/scalar/scalar.c b/scalar.c\nsimilarity index 100%\nrename from contrib/scalar/scalar.c\nrename to scalar.c\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/t/t9099-scalar.sh\nsimilarity index 94%\nrename from contrib/scalar/t/t9099-scalar.sh\nrename to t/t9099-scalar.sh\nindex 7e8771d0eff..f686ba5d84c 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/t/t9099-scalar.sh\n@@ -2,13 +2,7 @@\n \n test_description='test the `scalar` command'\n \n-TEST_DIRECTORY=$PWD/../../../t\n-export TEST_DIRECTORY\n-\n-# Make it work with --no-bin-wrappers\n-PATH=$PWD/..:$PATH\n-\n-. ../../../t/test-lib.sh\n+. ./test-lib.sh\n \n GIT_TEST_MAINT_SCHEDULER=\"crontab:test-tool crontab ../cron.txt\"\n export GIT_TEST_MAINT_SCHEDULER\n"},{"id":"438143","messageId":"nycvar.QRO.7.76.6.2110071256110.395@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"xmqqr1cxrjdl.fsf@gitster.g","subject":"Re: Train station analogy, was Re: [PATCH v3 00/15] [RFC] Upstreaming the Scalar command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-10-07T10:58:00Z","receivedAt":"2021-10-07T10:58:31Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 6 Oct 2021, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>\n> > I don't think that there is a lot of sense in that. The main benefits of\n> > `scalar` are in the `register` and the `clone` part, and the most natural\n> > end game would hence be for `git init` and `git clone` to sprout new\n> > options to support Scalar's features, in a Git-native way.\n>\n> Yes, that is even better.  An endgame where everybody benefits\n> natively would be highly desirable.\n\nI am glad that this question is now resolved.\n\n> Now you are back, do you think we can have the \"no more preserve\n> merges backend\" topic graduate to 'master', or do you prefer to cook\n> it over the cycle (or even two)?\n\nI did not see _any_ problems with it, so I'd be in favor of promoting it\nto `master`. But if you want to play the game more carefully, I would be\nfine with that, too.\n\nCiao,\nDscho\n"},{"id":"438144","messageId":"pull.1005.v5.git.1633604349.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v4.git.1631630356.gitgitgadget@gmail.com","subject":"[PATCH v5 00/15] Upstreaming the Scalar command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-07T10:58:54Z","receivedAt":"2021-10-07T10:59:14Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"tl;dr: This series contributes the Scalar command to the Git project. This\ncommand provides an opinionated way to create and configure repositories\nwith a focus on very large repositories.\n\nChanges since v4:\n\n * scalar delete now refuses to delete anything if it was started from\n   within the enlistment.\n * scalar delete releases any handles to the object store before deleting\n   the enlistment.\n * The OBJECTS list in the Makefile will now include Scalar.\n * scalar register now supports secondary worktrees, in addition to the\n   primary worktree.\n\nChanges since v3:\n\n * Moved the \"Changes since\" section to the top, to make it easier to see\n   what changed.\n * Reworded the commit message of the first patch.\n * Removed the [RFC] prefix because I did not hear any objections against\n   putting this into contrib/.\n\nChanges since v2:\n\n * Adjusted the description of the list command in the manual page , as\n   suggested by Bagas.\n * Addressed two style nits in cmd_run().\n * The documentation of git reconfigure -a was improved.\n\nChanges since v1:\n\n * A couple typos were fixed\n * The code parsing the output of ls-remote was made more readable\n * The indentation used in scalar.txt now consistently uses tabs\n * We no longer hard-code core.bare = false when registering with Scalar\n\n\nBackground\n==========\n\nYears ago, Microsoft wanted to move the source code of the Windows operating\nsystem to Git. The challenge there was to prove that Git could scale to\nmassive monorepos. The VFS for Git (formerly GVFS) project was born to take\nup that challenge.\n\nThe final solution included a virtual filesystem (with both user-mode and\nkernel components) and a customized fork of Git for Windows. This solution\ncontained several key concepts, such as only populating a portion of the\nworking directory, demand-fetching blobs, and performing periodic repo\nmaintenance in the background. However, the required kernel drivers made it\ndifficult to port the solution to other platforms.\n\nBut it was realized that many of these key concepts were independent of the\nactual VFS and its projection of the working directory. The Scalar project\nwas created to make that separation, refine the key concepts, and then\nextract those features into the new Scalar command.\n\n\nThe present\n===========\n\nThe Scalar project provides a completely functional non-virtual experience\nfor monorepos. But why stop there. The Scalar project was designed to be a\nself-destructing vehicle to allow those key concepts to be moved into core\nGit itself for the benefit of all. For example, partial clone,\nsparse-checkout, and background maintenance have already been upstreamed and\nremoved from Scalar proper. This patch series provides a C-based\nimplementation of the final remaining portions of the Scalar command. This\nwill make it easier for users to experiment with the Scalar command. It will\nalso make it substantially easier to experiment with moving functionality\nfrom Scalar into core Git, while maintaining backwards-compatibility for\nexisting Scalar users.\n\nThe C-based Scalar has been shipped to Scalar users, and can be tested by\nany interested reader:\nhttps://github.com/microsoft/git/releases/tag/v2.33.0.vfs.0.0 (it offers a\nGit for Windows installer, a macOS package and an Ubuntu package).\n\n\nOpportunities\n=============\n\nApart from providing the Scalar command, this contribution is intended to\nserve as a basis for further mailing list discussions on moving (some of)\nthese key concepts into the main Git commands.\n\nFor example, we previously discussed the idea of a \"git big-clone\" that does\nmuch of what \"scalar clone\" is doing. This patch series is a step to make\nsuch functionality exist in the Git code base while we simmer on what such a\n\"git big-clone\" command-line interface would look like.\n\nThis is one of many possible ways to do this. Creating a 'git big-clone'\ncould lock Git into backwards compatibility concerns so it is necessary to\napproach such an endeavor with caution. As a discussion starter, the scalar\nclone <url> command does roughly this:\n\n 1. git clone --sparse --filter=blob:none /src\n 2. git -C /src sparse-checkout init --cone\n 3. git -C /src config (many times)\n 4. git -C /src maintenance start\n\nIt is my hope inspire discussions about what parts of Scalar could go into\ncore Git, and where, and in which form. While we wish to maintain\nbackwards-compatibility of Scalar's command-line interface (because it is\nalready in use), by having the Scalar code in the same code base as Git's,\nit will be much easier to move functionality without having to maintain\nloose version coupling between independently-versioned Scalar and Git. The\ntight version-coupling, along with having access to libgit.a also allows the\nC-based implementation of Scalar to be much smaller than the original .NET\nversion.\n\nFor example, we might choose in the future to implement, say, git clone\n--scale=partial,cone to initialize a partial clone with a cone-sparse\ncheckout, that would not only be totally doable, and not only would we\nalready have precedent and data to prove that this actually makes engineers\nhappy who have to work on ginormous repositories, but we could then also\nimplement it by moving parts of contrib/scalar/ to builtin/ (where\ncontrib/scalar/ would then call the built-ins accordingly rather than\nhard-coding the defaults itself).\n\nWe now also have the opportunity to discuss the merits of Scalar's clone\ncaching, which is not actually part of this patch series because it is a bit\ncoupled with the GVFS parts of microsoft/git for the moment, where clones\nautomatically get registered with a populated alternate repository that is\nidentified by the URL, meaning: subsequent clones of the same repository are\nvastly faster than the first one because they do not actually download the\nalready-received objects again, they access the cache instead.\n\nAnother thing that I could imagine to be discussed at length is the\ndistinction between enlistment and worktree (where the latter is the actual\nGit worktree and usually lives in the src/ subdirectory of the former). This\nencourages untracked and ignored files to be placed outside the worktree,\nmaking Git's job much easier. This idea, too, might find its way in one way\nor another into Git proper.\n\nThese are just a few concepts in Scalar that do not yet have equivalents in\nGit. By putting this initial implementation into contrib/, we create a\nfoundation for future discussions of these concepts.\n\nWe plan on updating the recommended config settings in scalar register as\nnew Git features are available (such as builtin FSMonitor and sparse-index,\nwhen ready). To facilitate upgrading existing Scalar enlistments, their\npaths are automatically added to the [scalar] section of the global Git\nconfig, and the scalar reconfigure --all command will process all of them.\n\n\nEpilogue\n========\n\nNow, to address some questions that I imagine every reader has who made it\nthis far:\n\n * Why not put the Scalar functionality directly into a built-in? Creating a\n   Git builtin requires scrutiny over every aspect of the feature, which is\n   difficult to do while also maintaining the command-line interface\n   contract and expected behavior of the Scalar command (there are existing\n   users, after all). By having the Scalar command in contrib/, we present a\n   simple option for users to have these features in the short term while\n   the Git contributor community decides which bits to absorb into Git\n   built-ins.\n * Why implement the Scalar command in the Git codebase? We ported Scalar to\n   the microsoft/git fork for several reasons. First, we realized it was\n   possible now that the core features exist inside Git itself. Second,\n   compiling Scalar directly within a version of Git allows us to remove a\n   version compatibility check from each config option that might or might\n   not apply based on the installed Git version. Finally, this new location\n   has greatly simplified our release process and the installation process\n   for users. We now have ways to install Scalar with microsoft/git via\n   winget, brew, and apt-get. This has been the case since we shipped\n   v2.32.0 to our users, read: this setup has served us well already.\n * Why contribute Scalar to the Git project? We are biased, of course, yet\n   we do have evidence that the Scalar command is a helpful tool that offers\n   an simple way to handle huge repositories with ease. By contributing it\n   to the core Git project, we are able to share it with more users,\n   especially some users who do not want to install the microsoft/git fork.\n   We intend to include Scalar as a component in git-for-windows/git, but\n   are contributing it here first. Further, we think there is benefit to the\n   Git developer community as this presents an example of how to set certain\n   defaults that work for large repositories.\n * Does this integrate with the built-in FSMonitor yet? No, not yet. I do\n   have a couple of add-on patch series lined up, one of them being the\n   integration with the built-in FSMonitor, which obviously has to wait\n   until the FSMonitor patch series advances further.\n\nDerrick Stolee (4):\n  scalar: 'register' sets recommended config and starts maintenance\n  scalar: 'unregister' stops background maintenance\n  scalar: implement 'scalar list'\n  scalar: implement the `run` command\n\nJohannes Schindelin (10):\n  scalar: create a rudimentary executable\n  scalar: start documenting the command\n  scalar: create test infrastructure\n  scalar: let 'unregister' handle a deleted enlistment directory\n    gracefully\n  scalar: implement the `clone` subcommand\n  scalar: teach 'clone' to support the --single-branch option\n  scalar: allow reconfiguring an existing enlistment\n  scalar: teach 'reconfigure' to optionally handle all registered\n    enlistments\n  scalar: implement the `version` command\n  scalar: accept -C and -c options before the subcommand\n\nMatthew John Cheetham (1):\n  scalar: implement the `delete` command\n\n Makefile                         |   9 +\n contrib/scalar/.gitignore        |   5 +\n contrib/scalar/Makefile          |  57 +++\n contrib/scalar/scalar.c          | 845 +++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        | 154 ++++++\n contrib/scalar/t/Makefile        |  78 +++\n contrib/scalar/t/t9099-scalar.sh |  88 ++++\n 7 files changed, 1236 insertions(+)\n create mode 100644 contrib/scalar/.gitignore\n create mode 100644 contrib/scalar/Makefile\n create mode 100644 contrib/scalar/scalar.c\n create mode 100644 contrib/scalar/scalar.txt\n create mode 100644 contrib/scalar/t/Makefile\n create mode 100755 contrib/scalar/t/t9099-scalar.sh\n\n\nbase-commit: ebf3c04b262aa27fbb97f8a0156c2347fecafafb\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1005%2Fdscho%2Fscalar-the-beginning-v5\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1005/dscho/scalar-the-beginning-v5\nPull-Request: https://github.com/gitgitgadget/git/pull/1005\n\nRange-diff vs v4:\n\n  1:  852ec003109 !  1:  7119a8efc21 scalar: create a rudimentary executable\n     @@ Commit message\n          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n      \n       ## Makefile ##\n     -@@ Makefile: endif\n     - .PHONY: objects\n     - objects: $(OBJECTS)\n     - \n     +@@ Makefile: OBJECTS += $(FUZZ_OBJS)\n     + ifndef NO_CURL\n     + \tOBJECTS += http.o http-walker.o remote-curl.o\n     + endif\n     ++\n      +SCALAR_SOURCES := contrib/scalar/scalar.c\n      +SCALAR_OBJECTS := $(SCALAR_SOURCES:c=o)\n      +OBJECTS += $(SCALAR_OBJECTS)\n      +\n     - dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d)\n     - dep_dirs := $(addsuffix .depend,$(sort $(dir $(OBJECTS))))\n     + .PHONY: objects\n     + objects: $(OBJECTS)\n       \n      @@ Makefile: $(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS\n       \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n  2:  6ab9c7195da =  2:  edc1e5f73bd scalar: start documenting the command\n  3:  14992033d7c =  3:  9eeb66d5b5d scalar: create test infrastructure\n  4:  bbbc4c33390 !  4:  8ebfa51ae15 scalar: 'register' sets recommended config and starts maintenance\n     @@ contrib/scalar/scalar.c\n      +\t\tconst size_t len = path.len;\n      +\n      +\t\t/* check if currently in enlistment root with src/ workdir */\n     -+\t\tstrbuf_addstr(&path, \"/src/.git\");\n     -+\t\tif (is_git_directory(path.buf)) {\n     -+\t\t\tstrbuf_strip_suffix(&path, \"/.git\");\n     -+\n     ++\t\tstrbuf_addstr(&path, \"/src\");\n     ++\t\tif (is_nonbare_repository_dir(&path)) {\n      +\t\t\tif (enlistment_root)\n      +\t\t\t\tstrbuf_add(enlistment_root, path.buf, len);\n      +\n     @@ contrib/scalar/scalar.c\n      +\t\tstrbuf_setlen(&path, len);\n      +\n      +\t\t/* check if currently in workdir */\n     -+\t\tstrbuf_addstr(&path, \"/.git\");\n     -+\t\tif (is_git_directory(path.buf)) {\n     -+\t\t\tstrbuf_setlen(&path, len);\n     -+\n     ++\t\tif (is_nonbare_repository_dir(&path)) {\n      +\t\t\tif (enlistment_root) {\n      +\t\t\t\t/*\n      +\t\t\t\t * If the worktree's directory's name is `src`, the enlistment is the\n     @@ contrib/scalar/scalar.c\n      +\t\t\tenlistment_found = 1;\n      +\t\t\tbreak;\n      +\t\t}\n     -+\n     -+\t\tstrbuf_setlen(&path, len);\n      +\t} while (strbuf_parent_directory(&path));\n      +\n      +\tif (!enlistment_found)\n  5:  eadcddb2a9b =  5:  51b5fc577c9 scalar: 'unregister' stops background maintenance\n  6:  c3c2f3a4971 =  6:  0041477374e scalar: let 'unregister' handle a deleted enlistment directory gracefully\n  7:  90ef9b826b2 =  7:  12efa86e4cb scalar: implement 'scalar list'\n  8:  79cde4417d8 =  8:  670891d14cc scalar: implement the `clone` subcommand\n  9:  0acdaeb7396 =  9:  03b8385ebb5 scalar: teach 'clone' to support the --single-branch option\n 10:  64e3403ac12 = 10:  5a0b3843f98 scalar: implement the `run` command\n 11:  ada242c7c8c = 11:  6d9aaeb05fa scalar: allow reconfiguring an existing enlistment\n 12:  5c11117da51 = 12:  2967d7f1425 scalar: teach 'reconfigure' to optionally handle all registered enlistments\n 13:  914c16c7fcd ! 13:  8069cc536fe scalar: implement the `delete` command\n     @@ contrib/scalar/scalar.c\n       #include \"run-command.h\"\n       #include \"refs.h\"\n      +#include \"dir.h\"\n     ++#include \"packfile.h\"\n       \n       /*\n        * Remove the deepest subdirectory in the provided path string. Path must not\n     @@ contrib/scalar/scalar.c: static int cmd_unregister(int argc, const char **argv)\n       \n      +static int cmd_delete(int argc, const char **argv)\n      +{\n     ++\tchar *cwd = xgetcwd();\n      +\tstruct option options[] = {\n      +\t\tOPT_END(),\n      +\t};\n     @@ contrib/scalar/scalar.c: static int cmd_unregister(int argc, const char **argv)\n      +\n      +\tsetup_enlistment_directory(argc, argv, usage, options, &enlistment);\n      +\n     -+\tres = delete_enlistment(&enlistment);\n     ++\tif (dir_inside_of(cwd, enlistment.buf) >= 0)\n     ++\t\tres = error(_(\"refusing to delete current working directory\"));\n     ++\telse {\n     ++\t\tclose_object_store(the_repository->objects);\n     ++\t\tres = delete_enlistment(&enlistment);\n     ++\t}\n      +\tstrbuf_release(&enlistment);\n     ++\tfree(cwd);\n      +\n      +\treturn res;\n      +}\n 14:  06995770120 ! 14:  2ecfaa5d0fe scalar: implement the `version` command\n     @@ Commit message\n      \n       ## contrib/scalar/scalar.c ##\n      @@\n     - #include \"run-command.h\"\n       #include \"refs.h\"\n       #include \"dir.h\"\n     + #include \"packfile.h\"\n      +#include \"help.h\"\n       \n       /*\n 15:  7539725bb4f = 15:  f81e8b3bcf1 scalar: accept -C and -c options before the subcommand\n\n-- \ngitgitgadget\n"},{"id":"438145","messageId":"7119a8efc214ada2c372693731193043ea97dabc.1633604349.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v5.git.1633604349.gitgitgadget@gmail.com","subject":"[PATCH v5 01/15] scalar: create a rudimentary executable","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-07T10:58:55Z","receivedAt":"2021-10-07T10:59:15Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe idea of Scalar (https://github.com/microsoft/scalar), and before\nthat, of VFS for Git, has always been to prove that Git _can_ scale, and\nto upstream whatever strategies have been demonstrated to help.\n\nWith this patch, we start the journey from that C# project to move what\nis left to Git's own `contrib/` directory, reimplementing it in pure C,\nwith the intention to facilitate integrating the functionality into core\nGit all while maintaining backwards-compatibility for existing Scalar\nusers (which will be much easier when both live in the same worktree).\nIt has always been the plan to contribute all of the proven strategies\nback to core Git.\n\nFor example, while the virtual filesystem provided by VFS for Git helped\nthe team developing the Windows operating system to move onto Git, while\ntrying to upstream it we realized that it cannot be done: getting the\nvirtual filesystem to work (which we only managed to implement fully on\nWindows, but not on, say, macOS or Linux), and the required server-side\nsupport for the GVFS protocol, made this not quite feasible.\n\nThe Scalar project learned from that and tackled the problem with\ndifferent tactics: instead of pretending to Git that the working\ndirectory is fully populated, it _specifically_ teaches Git about\npartial clone (which is based on VFS for Git's cache server), about\nsparse checkout (which VFS for Git tried to do transparently, in the\nfile system layer), and regularly runs maintenance tasks to keep the\nrepository in a healthy state.\n\nWith partial clone, sparse checkout and `git maintenance` having been\nupstreamed, there is little left that `scalar.exe` does which `git.exe`\ncannot do. One such thing is that `scalar clone <url>` will\nautomatically set up a partial, sparse clone, and configure\nknown-helpful settings from the start.\n\nSo let's bring this convenience into Git's tree.\n\nThe idea here is that you can (optionally) build Scalar via\n\n\tmake -C contrib/scalar/Makefile\n\nThis will build the `scalar` executable and put it into the\ncontrib/scalar/ subdirectory.\n\nThe slightly awkward addition of the `contrib/scalar/*` bits to the\ntop-level `Makefile` are actually really required: we want to link to\n`libgit.a`, which means that we will need to use the very same `CFLAGS`\nand `LDFLAGS` as the rest of Git.\n\nAn early development version of this patch tried to replicate all the\nconditional code in `contrib/scalar/Makefile` (e.g. `NO_POLL`) just like\n`contrib/svn-fe/Makefile` used to do before it was retired. It turned\nout to be quite the whack-a-mole game: the SHA-1-related flags, the\nflags enabling/disabling `compat/poll/`, `compat/regex/`,\n`compat/win32mmap.c` & friends depending on the current platform... To\nput it mildly: it was a major mess.\n\nInstead, this patch makes minimal changes to the top-level `Makefile` so\nthat the bits in `contrib/scalar/` can be compiled and linked, and\nadds a `contrib/scalar/Makefile` that uses the top-level `Makefile` in a\nmost minimal way to do the actual compiling.\n\nNote: With this commit, we only establish the infrastructure, no\nScalar functionality is implemented yet; We will do that incrementally\nover the next few commits.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Makefile                  |  9 +++++++++\n contrib/scalar/.gitignore |  2 ++\n contrib/scalar/Makefile   | 34 ++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.c   | 36 ++++++++++++++++++++++++++++++++++++\n 4 files changed, 81 insertions(+)\n create mode 100644 contrib/scalar/.gitignore\n create mode 100644 contrib/scalar/Makefile\n create mode 100644 contrib/scalar/scalar.c\n\ndiff --git a/Makefile b/Makefile\nindex c3565fc0f8f..a12cac1b68b 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2444,6 +2444,11 @@ OBJECTS += $(FUZZ_OBJS)\n ifndef NO_CURL\n \tOBJECTS += http.o http-walker.o remote-curl.o\n endif\n+\n+SCALAR_SOURCES := contrib/scalar/scalar.c\n+SCALAR_OBJECTS := $(SCALAR_SOURCES:c=o)\n+OBJECTS += $(SCALAR_OBJECTS)\n+\n .PHONY: objects\n objects: $(OBJECTS)\n \n@@ -2586,6 +2591,10 @@ $(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n \t\t$(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)\n \n+contrib/scalar/scalar$X: $(SCALAR_OBJECTS) GIT-LDFLAGS $(GITLIBS)\n+\t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \\\n+\t\t$(filter %.o,$^) $(LIBS)\n+\n $(LIB_FILE): $(LIB_OBJS)\n \t$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^\n \ndiff --git a/contrib/scalar/.gitignore b/contrib/scalar/.gitignore\nnew file mode 100644\nindex 00000000000..ff3d47e84d0\n--- /dev/null\n+++ b/contrib/scalar/.gitignore\n@@ -0,0 +1,2 @@\n+/*.exe\n+/scalar\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nnew file mode 100644\nindex 00000000000..40c03ad10e1\n--- /dev/null\n+++ b/contrib/scalar/Makefile\n@@ -0,0 +1,34 @@\n+QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir\n+QUIET_SUBDIR1  =\n+\n+ifneq ($(findstring s,$(MAKEFLAGS)),s)\n+ifndef V\n+\tQUIET_SUBDIR0  = +@subdir=\n+\tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n+\t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n+else\n+\texport V\n+endif\n+endif\n+\n+all:\n+\n+include ../../config.mak.uname\n+-include ../../config.mak.autogen\n+-include ../../config.mak\n+\n+TARGETS = scalar$(X) scalar.o\n+GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n+\n+all: scalar$X\n+\n+$(GITLIBS):\n+\t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)\n+\n+$(TARGETS): $(GITLIBS) scalar.c\n+\t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)\n+\n+clean:\n+\t$(RM) $(TARGETS)\n+\n+.PHONY: all clean FORCE\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nnew file mode 100644\nindex 00000000000..7cff29e0fcd\n--- /dev/null\n+++ b/contrib/scalar/scalar.c\n@@ -0,0 +1,36 @@\n+/*\n+ * The Scalar command-line interface.\n+ */\n+\n+#include \"cache.h\"\n+#include \"gettext.h\"\n+#include \"parse-options.h\"\n+\n+static struct {\n+\tconst char *name;\n+\tint (*fn)(int, const char **);\n+} builtins[] = {\n+\t{ NULL, NULL},\n+};\n+\n+int cmd_main(int argc, const char **argv)\n+{\n+\tstruct strbuf scalar_usage = STRBUF_INIT;\n+\tint i;\n+\n+\tif (argc > 1) {\n+\t\targv++;\n+\t\targc--;\n+\n+\t\tfor (i = 0; builtins[i].name; i++)\n+\t\t\tif (!strcmp(builtins[i].name, argv[0]))\n+\t\t\t\treturn !!builtins[i].fn(argc, argv);\n+\t}\n+\n+\tstrbuf_addstr(&scalar_usage,\n+\t\t      N_(\"scalar <command> [<options>]\\n\\nCommands:\\n\"));\n+\tfor (i = 0; builtins[i].name; i++)\n+\t\tstrbuf_addf(&scalar_usage, \"\\t%s\\n\", builtins[i].name);\n+\n+\tusage(scalar_usage.buf);\n+}\n-- \ngitgitgadget\n\n"},{"id":"438146","messageId":"edc1e5f73bd852478f82873c3039388dee652683.1633604349.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v5.git.1633604349.gitgitgadget@gmail.com","subject":"[PATCH v5 02/15] scalar: start documenting the command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-07T10:58:56Z","receivedAt":"2021-10-07T10:59:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis commit establishes the infrastructure to build the manual page for\nthe `scalar` command.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/.gitignore |  3 +++\n contrib/scalar/Makefile   | 14 +++++++++++++-\n contrib/scalar/scalar.txt | 38 ++++++++++++++++++++++++++++++++++++++\n 3 files changed, 54 insertions(+), 1 deletion(-)\n create mode 100644 contrib/scalar/scalar.txt\n\ndiff --git a/contrib/scalar/.gitignore b/contrib/scalar/.gitignore\nindex ff3d47e84d0..00441073f59 100644\n--- a/contrib/scalar/.gitignore\n+++ b/contrib/scalar/.gitignore\n@@ -1,2 +1,5 @@\n+/*.xml\n+/*.1\n+/*.html\n /*.exe\n /scalar\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nindex 40c03ad10e1..85c186634e9 100644\n--- a/contrib/scalar/Makefile\n+++ b/contrib/scalar/Makefile\n@@ -6,6 +6,7 @@ ifndef V\n \tQUIET_SUBDIR0  = +@subdir=\n \tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n \t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n+\tQUIET          = @\n else\n \texport V\n endif\n@@ -30,5 +31,16 @@ $(TARGETS): $(GITLIBS) scalar.c\n \n clean:\n \t$(RM) $(TARGETS)\n+\t$(RM) scalar.1 scalar.html scalar.xml\n \n-.PHONY: all clean FORCE\n+docs: scalar.html scalar.1\n+\n+scalar.html: | scalar.1 # prevent them from trying to build `doc.dep` in parallel\n+\n+scalar.html scalar.1: scalar.txt\n+\t$(QUIET_SUBDIR0)../../Documentation$(QUIET_SUBDIR1) \\\n+\t\tMAN_TXT=../contrib/scalar/scalar.txt \\\n+\t\t../contrib/scalar/$@\n+\t$(QUIET)test scalar.1 != \"$@\" || mv ../../Documentation/$@ .\n+\n+.PHONY: all clean docs FORCE\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nnew file mode 100644\nindex 00000000000..5f7131861a5\n--- /dev/null\n+++ b/contrib/scalar/scalar.txt\n@@ -0,0 +1,38 @@\n+scalar(1)\n+=========\n+\n+NAME\n+----\n+scalar - an opinionated repository management tool\n+\n+SYNOPSIS\n+--------\n+[verse]\n+scalar <command> [<options>]\n+\n+DESCRIPTION\n+-----------\n+\n+Scalar is an opinionated repository management tool. By creating new\n+repositories or registering existing repositories with Scalar, your Git\n+experience will speed up. Scalar sets advanced Git config settings,\n+maintains your repositories in the background, and helps reduce data sent\n+across the network.\n+\n+An important Scalar concept is the enlistment: this is the top-level directory\n+of the project. It usually contains the subdirectory `src/` which is a Git\n+worktree. This encourages the separation between tracked files (inside `src/`)\n+and untracked files, such as build artifacts (outside `src/`). When registering\n+an existing Git worktree with Scalar whose name is not `src`, the enlistment\n+will be identical to the worktree.\n+\n+The `scalar` command implements various subcommands, and different options\n+depending on the subcommand.\n+\n+SEE ALSO\n+--------\n+linkgit:git-maintenance[1].\n+\n+Scalar\n+---\n+Associated with the linkgit:git[1] suite\n-- \ngitgitgadget\n\n"},{"id":"438147","messageId":"9eeb66d5b5d47a6498518657c7ba608e2bc78114.1633604349.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v5.git.1633604349.gitgitgadget@gmail.com","subject":"[PATCH v5 03/15] scalar: create test infrastructure","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-07T10:58:57Z","receivedAt":"2021-10-07T10:59:23Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nTo test the Scalar command, create a test script in contrib/scalar/t\nthat is executed as `make -C contrib/scalar test`. Since Scalar has no\nmeaningful capabilities yet, the only test is rather simple. We will add\nmore tests in subsequent commits that introduce corresponding, new\nfunctionality.\n\nNote: this test script is intended to test `scalar` only lightly, even\nafter all of the functionality is implemented.\n\nA more comprehensive functional (or: integration) test suite can be\nfound at https://github.com/microsoft/scalar; It is used in the workflow\nhttps://github.com/microsoft/git/blob/HEAD/.github/workflows/scalar-functional-tests.yml\nin Microsoft's Git fork. This test suite performs end-to-end tests with\na real remote repository, and is run as part of the regular CI builds.\nSince those tests require some functionality supported only by\nMicrosoft's Git fork (\"GVFS protocol\"), there is no intention to port\nthat fuller test suite to `contrib/scalar/`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/Makefile          | 17 +++++--\n contrib/scalar/t/Makefile        | 78 ++++++++++++++++++++++++++++++++\n contrib/scalar/t/t9099-scalar.sh | 17 +++++++\n 3 files changed, 109 insertions(+), 3 deletions(-)\n create mode 100644 contrib/scalar/t/Makefile\n create mode 100755 contrib/scalar/t/t9099-scalar.sh\n\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nindex 85c186634e9..8620042f281 100644\n--- a/contrib/scalar/Makefile\n+++ b/contrib/scalar/Makefile\n@@ -3,6 +3,7 @@ QUIET_SUBDIR1  =\n \n ifneq ($(findstring s,$(MAKEFLAGS)),s)\n ifndef V\n+\tQUIET_GEN      = @echo '   ' GEN $@;\n \tQUIET_SUBDIR0  = +@subdir=\n \tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n \t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n@@ -21,7 +22,7 @@ include ../../config.mak.uname\n TARGETS = scalar$(X) scalar.o\n GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n \n-all: scalar$X\n+all: scalar$X ../../bin-wrappers/scalar\n \n $(GITLIBS):\n \t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)\n@@ -30,9 +31,19 @@ $(TARGETS): $(GITLIBS) scalar.c\n \t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)\n \n clean:\n-\t$(RM) $(TARGETS)\n+\t$(RM) $(TARGETS) ../../bin-wrappers/scalar\n \t$(RM) scalar.1 scalar.html scalar.xml\n \n+../../bin-wrappers/scalar: ../../wrap-for-bin.sh Makefile\n+\t@mkdir -p ../../bin-wrappers\n+\t$(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \\\n+\t     -e 's|@@BUILD_DIR@@|$(shell cd ../.. && pwd)|' \\\n+\t     -e 's|@@PROG@@|contrib/scalar/scalar$(X)|' < $< > $@ && \\\n+\tchmod +x $@\n+\n+test: all\n+\t$(MAKE) -C t\n+\n docs: scalar.html scalar.1\n \n scalar.html: | scalar.1 # prevent them from trying to build `doc.dep` in parallel\n@@ -43,4 +54,4 @@ scalar.html scalar.1: scalar.txt\n \t\t../contrib/scalar/$@\n \t$(QUIET)test scalar.1 != \"$@\" || mv ../../Documentation/$@ .\n \n-.PHONY: all clean docs FORCE\n+.PHONY: all clean docs test FORCE\ndiff --git a/contrib/scalar/t/Makefile b/contrib/scalar/t/Makefile\nnew file mode 100644\nindex 00000000000..6170672bb37\n--- /dev/null\n+++ b/contrib/scalar/t/Makefile\n@@ -0,0 +1,78 @@\n+# Run scalar tests\n+#\n+# Copyright (c) 2005,2021 Junio C Hamano, Johannes Schindelin\n+#\n+\n+-include ../../../config.mak.autogen\n+-include ../../../config.mak\n+\n+SHELL_PATH ?= $(SHELL)\n+PERL_PATH ?= /usr/bin/perl\n+RM ?= rm -f\n+PROVE ?= prove\n+DEFAULT_TEST_TARGET ?= test\n+TEST_LINT ?= test-lint\n+\n+ifdef TEST_OUTPUT_DIRECTORY\n+TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results\n+else\n+TEST_RESULTS_DIRECTORY = ../../../t/test-results\n+endif\n+\n+# Shell quote;\n+SHELL_PATH_SQ = $(subst ','\\'',$(SHELL_PATH))\n+PERL_PATH_SQ = $(subst ','\\'',$(PERL_PATH))\n+TEST_RESULTS_DIRECTORY_SQ = $(subst ','\\'',$(TEST_RESULTS_DIRECTORY))\n+\n+T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))\n+\n+all: $(DEFAULT_TEST_TARGET)\n+\n+test: $(TEST_LINT)\n+\t$(MAKE) aggregate-results-and-cleanup\n+\n+prove: $(TEST_LINT)\n+\t@echo \"*** prove ***\"; GIT_CONFIG=.git/config $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)\n+\t$(MAKE) clean-except-prove-cache\n+\n+$(T):\n+\t@echo \"*** $@ ***\"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)\n+\n+clean-except-prove-cache:\n+\t$(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)'\n+\t$(RM) -r valgrind/bin\n+\n+clean: clean-except-prove-cache\n+\t$(RM) .prove\n+\n+test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax\n+\n+test-lint-duplicates:\n+\t@dups=`echo $(T) | tr ' ' '\\n' | sed 's/-.*//' | sort | uniq -d` && \\\n+\t\ttest -z \"$$dups\" || { \\\n+\t\techo >&2 \"duplicate test numbers:\" $$dups; exit 1; }\n+\n+test-lint-executable:\n+\t@bad=`for i in $(T); do test -x \"$$i\" || echo $$i; done` && \\\n+\t\ttest -z \"$$bad\" || { \\\n+\t\techo >&2 \"non-executable tests:\" $$bad; exit 1; }\n+\n+test-lint-shell-syntax:\n+\t@'$(PERL_PATH_SQ)' ../../../t/check-non-portable-shell.pl $(T)\n+\n+aggregate-results-and-cleanup: $(T)\n+\t$(MAKE) aggregate-results\n+\t$(MAKE) clean\n+\n+aggregate-results:\n+\tfor f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \\\n+\t\techo \"$$f\"; \\\n+\tdone | '$(SHELL_PATH_SQ)' ../../../t/aggregate-results.sh\n+\n+valgrind:\n+\t$(MAKE) GIT_TEST_OPTS=\"$(GIT_TEST_OPTS) --valgrind\"\n+\n+test-results:\n+\tmkdir -p test-results\n+\n+.PHONY: $(T) aggregate-results clean valgrind\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nnew file mode 100755\nindex 00000000000..16f2b72b126\n--- /dev/null\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -0,0 +1,17 @@\n+#!/bin/sh\n+\n+test_description='test the `scalar` command'\n+\n+TEST_DIRECTORY=$PWD/../../../t\n+export TEST_DIRECTORY\n+\n+# Make it work with --no-bin-wrappers\n+PATH=$PWD/..:$PATH\n+\n+. ../../../t/test-lib.sh\n+\n+test_expect_success 'scalar shows a usage' '\n+\ttest_expect_code 129 scalar -h\n+'\n+\n+test_done\n-- \ngitgitgadget\n\n"},{"id":"438148","messageId":"8ebfa51ae15ef98e2488138cd3be748d35100a3d.1633604349.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v5.git.1633604349.gitgitgadget@gmail.com","subject":"[PATCH v5 04/15] scalar: 'register' sets recommended config and starts maintenance","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-07T10:58:58Z","receivedAt":"2021-10-07T10:59:32Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nLet's start implementing the `register` command. With this commit,\nrecommended settings are configured upon `scalar register`, and Git's\nbackground maintenance is started.\n\nThe recommended config settings may very well change in the future. For\nexample, once the built-in FSMonitor is available, we will want to\nenable it upon `scalar register`. For that reason, we explicitly support\nrunning `scalar register` in an already-registered enlistment.\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 248 ++++++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt |  18 ++-\n 2 files changed, 265 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 7cff29e0fcd..38721d671ba 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -5,11 +5,259 @@\n #include \"cache.h\"\n #include \"gettext.h\"\n #include \"parse-options.h\"\n+#include \"config.h\"\n+#include \"run-command.h\"\n+\n+/*\n+ * Remove the deepest subdirectory in the provided path string. Path must not\n+ * include a trailing path separator. Returns 1 if parent directory found,\n+ * otherwise 0.\n+ */\n+static int strbuf_parent_directory(struct strbuf *buf)\n+{\n+\tsize_t len = buf->len;\n+\tsize_t offset = offset_1st_component(buf->buf);\n+\tchar *path_sep = find_last_dir_sep(buf->buf + offset);\n+\tstrbuf_setlen(buf, path_sep ? path_sep - buf->buf : offset);\n+\n+\treturn buf->len < len;\n+}\n+\n+static void setup_enlistment_directory(int argc, const char **argv,\n+\t\t\t\t       const char * const *usagestr,\n+\t\t\t\t       const struct option *options,\n+\t\t\t\t       struct strbuf *enlistment_root)\n+{\n+\tstruct strbuf path = STRBUF_INIT;\n+\tchar *root;\n+\tint enlistment_found = 0;\n+\n+\tif (startup_info->have_repository)\n+\t\tBUG(\"gitdir already set up?!?\");\n+\n+\tif (argc > 1)\n+\t\tusage_with_options(usagestr, options);\n+\n+\t/* find the worktree, determine its corresponding root */\n+\tif (argc == 1)\n+\t\tstrbuf_add_absolute_path(&path, argv[0]);\n+\telse if (strbuf_getcwd(&path) < 0)\n+\t\tdie(_(\"need a working directory\"));\n+\n+\tstrbuf_trim_trailing_dir_sep(&path);\n+\tdo {\n+\t\tconst size_t len = path.len;\n+\n+\t\t/* check if currently in enlistment root with src/ workdir */\n+\t\tstrbuf_addstr(&path, \"/src\");\n+\t\tif (is_nonbare_repository_dir(&path)) {\n+\t\t\tif (enlistment_root)\n+\t\t\t\tstrbuf_add(enlistment_root, path.buf, len);\n+\n+\t\t\tenlistment_found = 1;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\t/* reset to original path */\n+\t\tstrbuf_setlen(&path, len);\n+\n+\t\t/* check if currently in workdir */\n+\t\tif (is_nonbare_repository_dir(&path)) {\n+\t\t\tif (enlistment_root) {\n+\t\t\t\t/*\n+\t\t\t\t * If the worktree's directory's name is `src`, the enlistment is the\n+\t\t\t\t * parent directory, otherwise it is identical to the worktree.\n+\t\t\t\t */\n+\t\t\t\troot = strip_path_suffix(path.buf, \"src\");\n+\t\t\t\tstrbuf_addstr(enlistment_root, root ? root : path.buf);\n+\t\t\t\tfree(root);\n+\t\t\t}\n+\n+\t\t\tenlistment_found = 1;\n+\t\t\tbreak;\n+\t\t}\n+\t} while (strbuf_parent_directory(&path));\n+\n+\tif (!enlistment_found)\n+\t\tdie(_(\"could not find enlistment root\"));\n+\n+\tif (chdir(path.buf) < 0)\n+\t\tdie_errno(_(\"could not switch to '%s'\"), path.buf);\n+\n+\tstrbuf_release(&path);\n+\tsetup_git_directory();\n+}\n+\n+static int run_git(const char *arg, ...)\n+{\n+\tstruct strvec argv = STRVEC_INIT;\n+\tva_list args;\n+\tconst char *p;\n+\tint res;\n+\n+\tva_start(args, arg);\n+\tstrvec_push(&argv, arg);\n+\twhile ((p = va_arg(args, const char *)))\n+\t\tstrvec_push(&argv, p);\n+\tva_end(args);\n+\n+\tres = run_command_v_opt(argv.v, RUN_GIT_CMD);\n+\n+\tstrvec_clear(&argv);\n+\treturn res;\n+}\n+\n+static int set_recommended_config(void)\n+{\n+\tstruct {\n+\t\tconst char *key;\n+\t\tconst char *value;\n+\t} config[] = {\n+\t\t{ \"am.keepCR\", \"true\" },\n+\t\t{ \"core.FSCache\", \"true\" },\n+\t\t{ \"core.multiPackIndex\", \"true\" },\n+\t\t{ \"core.preloadIndex\", \"true\" },\n+#ifndef WIN32\n+\t\t{ \"core.untrackedCache\", \"true\" },\n+#else\n+\t\t/*\n+\t\t * Unfortunately, Scalar's Functional Tests demonstrated\n+\t\t * that the untracked cache feature is unreliable on Windows\n+\t\t * (which is a bummer because that platform would benefit the\n+\t\t * most from it). For some reason, freshly created files seem\n+\t\t * not to update the directory's `lastModified` time\n+\t\t * immediately, but the untracked cache would need to rely on\n+\t\t * that.\n+\t\t *\n+\t\t * Therefore, with a sad heart, we disable this very useful\n+\t\t * feature on Windows.\n+\t\t */\n+\t\t{ \"core.untrackedCache\", \"false\" },\n+#endif\n+\t\t{ \"core.logAllRefUpdates\", \"true\" },\n+\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n+\t\t{ \"credential.validate\", \"false\" }, /* GCM4W-only */\n+\t\t{ \"gc.auto\", \"0\" },\n+\t\t{ \"gui.GCWarning\", \"false\" },\n+\t\t{ \"index.threads\", \"true\" },\n+\t\t{ \"index.version\", \"4\" },\n+\t\t{ \"merge.stat\", \"false\" },\n+\t\t{ \"merge.renames\", \"false\" },\n+\t\t{ \"pack.useBitmaps\", \"false\" },\n+\t\t{ \"pack.useSparse\", \"true\" },\n+\t\t{ \"receive.autoGC\", \"false\" },\n+\t\t{ \"reset.quiet\", \"true\" },\n+\t\t{ \"feature.manyFiles\", \"false\" },\n+\t\t{ \"feature.experimental\", \"false\" },\n+\t\t{ \"fetch.unpackLimit\", \"1\" },\n+\t\t{ \"fetch.writeCommitGraph\", \"false\" },\n+#ifdef WIN32\n+\t\t{ \"http.sslBackend\", \"schannel\" },\n+#endif\n+\t\t{ \"status.aheadBehind\", \"false\" },\n+\t\t{ \"commitGraph.generationVersion\", \"1\" },\n+\t\t{ \"core.autoCRLF\", \"false\" },\n+\t\t{ \"core.safeCRLF\", \"false\" },\n+\t\t{ NULL, NULL },\n+\t};\n+\tint i;\n+\tchar *value;\n+\n+\tfor (i = 0; config[i].key; i++) {\n+\t\tif (git_config_get_string(config[i].key, &value)) {\n+\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"created\");\n+\t\t\tif (git_config_set_gently(config[i].key,\n+\t\t\t\t\t\t  config[i].value) < 0)\n+\t\t\t\treturn error(_(\"could not configure %s=%s\"),\n+\t\t\t\t\t     config[i].key, config[i].value);\n+\t\t} else {\n+\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"exists\");\n+\t\t\tfree(value);\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * The `log.excludeDecoration` setting is special because it allows\n+\t * for multiple values.\n+\t */\n+\tif (git_config_get_string(\"log.excludeDecoration\", &value)) {\n+\t\ttrace2_data_string(\"scalar\", the_repository,\n+\t\t\t\t   \"log.excludeDecoration\", \"created\");\n+\t\tif (git_config_set_multivar_gently(\"log.excludeDecoration\",\n+\t\t\t\t\t\t   \"refs/prefetch/*\",\n+\t\t\t\t\t\t   CONFIG_REGEX_NONE, 0))\n+\t\t\treturn error(_(\"could not configure \"\n+\t\t\t\t       \"log.excludeDecoration\"));\n+\t} else {\n+\t\ttrace2_data_string(\"scalar\", the_repository,\n+\t\t\t\t   \"log.excludeDecoration\", \"exists\");\n+\t\tfree(value);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int start_maintenance(void)\n+{\n+\treturn run_git(\"maintenance\", \"start\", NULL);\n+}\n+\n+static int add_enlistment(void)\n+{\n+\tint res;\n+\n+\tif (!the_repository->worktree)\n+\t\tdie(_(\"Scalar enlistments require a worktree\"));\n+\n+\tres = run_git(\"config\", \"--global\", \"--get\", \"--fixed-value\",\n+\t\t      \"scalar.repo\", the_repository->worktree, NULL);\n+\n+\t/*\n+\t * If the setting is already there, then do nothing.\n+\t */\n+\tif (!res)\n+\t\treturn 0;\n+\n+\treturn run_git(\"config\", \"--global\", \"--add\",\n+\t\t       \"scalar.repo\", the_repository->worktree, NULL);\n+}\n+\n+static int register_dir(void)\n+{\n+\tint res = add_enlistment();\n+\n+\tif (!res)\n+\t\tres = set_recommended_config();\n+\n+\tif (!res)\n+\t\tres = start_maintenance();\n+\n+\treturn res;\n+}\n+\n+static int cmd_register(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar register [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn register_dir();\n+}\n \n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"register\", cmd_register },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 5f7131861a5..568987064b2 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,7 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n-scalar <command> [<options>]\n+scalar register [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -29,6 +29,22 @@ will be identical to the worktree.\n The `scalar` command implements various subcommands, and different options\n depending on the subcommand.\n \n+COMMANDS\n+--------\n+\n+Register\n+~~~~~~~~\n+\n+register [<enlistment>]::\n+\tAdds the enlistment's repository to the list of registered repositories\n+\tand starts background maintenance. If `<enlistment>` is not provided,\n+\tthen the enlistment associated with the current working directory is\n+\tregistered.\n++\n+Note: when this subcommand is called in a worktree that is called `src/`, its\n+parent directory is considered to be the Scalar enlistment. If the worktree is\n+_not_ called `src/`, it itself will be considered to be the Scalar enlistment.\n+\n SEE ALSO\n --------\n linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"438149","messageId":"51b5fc577c9ea7d8f5f7f7c676e9b2c25b65f71c.1633604349.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v5.git.1633604349.gitgitgadget@gmail.com","subject":"[PATCH v5 05/15] scalar: 'unregister' stops background maintenance","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-07T10:58:59Z","receivedAt":"2021-10-07T10:59:35Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nJust like `scalar register` starts the scheduled background maintenance,\n`scalar unregister` stops it. Note that we use `git maintenance start`\nin `scalar register`, but we do not use `git maintenance stop` in\n`scalar unregister`: this would stop maintenance for _all_ repositories,\nnot just for the one we want to unregister.\n\nThe `unregister` command also removes the corresponding entry from the\n`[scalar]` section in the global Git config.\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 50 ++++++++++++++++++++++++++++++++-------\n contrib/scalar/scalar.txt |  8 +++++++\n 2 files changed, 50 insertions(+), 8 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 38721d671ba..fc55404230d 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -197,12 +197,12 @@ static int set_recommended_config(void)\n \treturn 0;\n }\n \n-static int start_maintenance(void)\n+static int toggle_maintenance(int enable)\n {\n-\treturn run_git(\"maintenance\", \"start\", NULL);\n+\treturn run_git(\"maintenance\", enable ? \"start\" : \"unregister\", NULL);\n }\n \n-static int add_enlistment(void)\n+static int add_or_remove_enlistment(int add)\n {\n \tint res;\n \n@@ -213,24 +213,39 @@ static int add_enlistment(void)\n \t\t      \"scalar.repo\", the_repository->worktree, NULL);\n \n \t/*\n-\t * If the setting is already there, then do nothing.\n+\t * If we want to add and the setting is already there, then do nothing.\n+\t * If we want to remove and the setting is not there, then do nothing.\n \t */\n-\tif (!res)\n+\tif ((add && !res) || (!add && res))\n \t\treturn 0;\n \n-\treturn run_git(\"config\", \"--global\", \"--add\",\n+\treturn run_git(\"config\", \"--global\", add ? \"--add\" : \"--unset\",\n+\t\t       add ? \"--no-fixed-value\" : \"--fixed-value\",\n \t\t       \"scalar.repo\", the_repository->worktree, NULL);\n }\n \n static int register_dir(void)\n {\n-\tint res = add_enlistment();\n+\tint res = add_or_remove_enlistment(1);\n \n \tif (!res)\n \t\tres = set_recommended_config();\n \n \tif (!res)\n-\t\tres = start_maintenance();\n+\t\tres = toggle_maintenance(1);\n+\n+\treturn res;\n+}\n+\n+static int unregister_dir(void)\n+{\n+\tint res = 0;\n+\n+\tif (toggle_maintenance(0) < 0)\n+\t\tres = -1;\n+\n+\tif (add_or_remove_enlistment(0) < 0)\n+\t\tres = -1;\n \n \treturn res;\n }\n@@ -253,11 +268,30 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_unregister(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar unregister [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn unregister_dir();\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n \t{ \"register\", cmd_register },\n+\t{ \"unregister\", cmd_unregister },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 568987064b2..d9a79984492 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -9,6 +9,7 @@ SYNOPSIS\n --------\n [verse]\n scalar register [<enlistment>]\n+scalar unregister [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -45,6 +46,13 @@ Note: when this subcommand is called in a worktree that is called `src/`, its\n parent directory is considered to be the Scalar enlistment. If the worktree is\n _not_ called `src/`, it itself will be considered to be the Scalar enlistment.\n \n+Unregister\n+~~~~~~~~~~\n+\n+unregister [<enlistment>]::\n+\tRemove the specified repository from the list of repositories\n+\tregistered with Scalar and stop the scheduled background maintenance.\n+\n SEE ALSO\n --------\n linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"438150","messageId":"0041477374edb0a6f32d15cab18d99075e5ac60c.1633604349.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v5.git.1633604349.gitgitgadget@gmail.com","subject":"[PATCH v5 06/15] scalar: let 'unregister' handle a deleted enlistment directory gracefully","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-07T10:59:00Z","receivedAt":"2021-10-07T10:59:41Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nWhen a user deleted an enlistment manually, let's be generous and\n_still_ unregister it.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 46 ++++++++++++++++++++++++++++++++\n contrib/scalar/t/t9099-scalar.sh | 15 +++++++++++\n 2 files changed, 61 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex fc55404230d..dc84ce0d5b2 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -268,6 +268,24 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int remove_deleted_enlistment(struct strbuf *path)\n+{\n+\tint res = 0;\n+\tstrbuf_realpath_forgiving(path, path->buf, 1);\n+\n+\tif (run_git(\"config\", \"--global\",\n+\t\t    \"--unset\", \"--fixed-value\",\n+\t\t    \"scalar.repo\", path->buf, NULL) < 0)\n+\t\tres = -1;\n+\n+\tif (run_git(\"config\", \"--global\",\n+\t\t    \"--unset\", \"--fixed-value\",\n+\t\t    \"maintenance.repo\", path->buf, NULL) < 0)\n+\t\tres = -1;\n+\n+\treturn res;\n+}\n+\n static int cmd_unregister(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -281,6 +299,34 @@ static int cmd_unregister(int argc, const char **argv)\n \targc = parse_options(argc, argv, NULL, options,\n \t\t\t     usage, 0);\n \n+\t/*\n+\t * Be forgiving when the enlistment or worktree does not even exist any\n+\t * longer; This can be the case if a user deleted the worktree by\n+\t * mistake and _still_ wants to unregister the thing.\n+\t */\n+\tif (argc == 1) {\n+\t\tstruct strbuf src_path = STRBUF_INIT, workdir_path = STRBUF_INIT;\n+\n+\t\tstrbuf_addf(&src_path, \"%s/src/.git\", argv[0]);\n+\t\tstrbuf_addf(&workdir_path, \"%s/.git\", argv[0]);\n+\t\tif (!is_directory(src_path.buf) && !is_directory(workdir_path.buf)) {\n+\t\t\t/* remove possible matching registrations */\n+\t\t\tint res = -1;\n+\n+\t\t\tstrbuf_strip_suffix(&src_path, \"/.git\");\n+\t\t\tres = remove_deleted_enlistment(&src_path) && res;\n+\n+\t\t\tstrbuf_strip_suffix(&workdir_path, \"/.git\");\n+\t\t\tres = remove_deleted_enlistment(&workdir_path) && res;\n+\n+\t\t\tstrbuf_release(&src_path);\n+\t\t\tstrbuf_release(&workdir_path);\n+\t\t\treturn res;\n+\t\t}\n+\t\tstrbuf_release(&src_path);\n+\t\tstrbuf_release(&workdir_path);\n+\t}\n+\n \tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n \n \treturn unregister_dir();\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 16f2b72b126..ef0e8d680d5 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -14,4 +14,19 @@ test_expect_success 'scalar shows a usage' '\n \ttest_expect_code 129 scalar -h\n '\n \n+test_expect_success 'scalar unregister' '\n+\tgit init vanish/src &&\n+\tscalar register vanish/src &&\n+\tgit config --get --global --fixed-value \\\n+\t\tmaintenance.repo \"$(pwd)/vanish/src\" &&\n+\tscalar list >scalar.repos &&\n+\tgrep -F \"$(pwd)/vanish/src\" scalar.repos &&\n+\trm -rf vanish/src/.git &&\n+\tscalar unregister vanish &&\n+\ttest_must_fail git config --get --global --fixed-value \\\n+\t\tmaintenance.repo \"$(pwd)/vanish/src\" &&\n+\tscalar list >scalar.repos &&\n+\t! grep -F \"$(pwd)/vanish/src\" scalar.repos\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"438151","messageId":"12efa86e4cb3e09b18e002b8a0cfaf0c27f939b0.1633604349.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v5.git.1633604349.gitgitgadget@gmail.com","subject":"[PATCH v5 07/15] scalar: implement 'scalar list'","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-07T10:59:01Z","receivedAt":"2021-10-07T10:59:42Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe produced list simply consists of those repositories registered under\nthe multi-valued `scalar.repo` config setting in the user's Git config.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 11 +++++++++++\n contrib/scalar/scalar.txt | 11 ++++++++++-\n 2 files changed, 21 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex dc84ce0d5b2..d13eb951c3d 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -250,6 +250,16 @@ static int unregister_dir(void)\n \treturn res;\n }\n \n+static int cmd_list(int argc, const char **argv)\n+{\n+\tif (argc != 1)\n+\t\tdie(_(\"`scalar list` does not take arguments\"));\n+\n+\tif (run_git(\"config\", \"--global\", \"--get-all\", \"scalar.repo\", NULL) < 0)\n+\t\treturn -1;\n+\treturn 0;\n+}\n+\n static int cmd_register(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -336,6 +346,7 @@ static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n \t{ NULL, NULL},\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex d9a79984492..f93e3d00efd 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,6 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n+scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n \n@@ -28,11 +29,19 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand.\n+depending on the subcommand. With the exception of `list`, all subcommands\n+expect to be run in an enlistment.\n \n COMMANDS\n --------\n \n+List\n+~~~~\n+\n+list::\n+\tList enlistments that are currently registered by Scalar. This\n+\tsubcommand does not need to be run inside an enlistment.\n+\n Register\n ~~~~~~~~\n \n-- \ngitgitgadget\n\n"},{"id":"438152","messageId":"670891d14cca3cd3ec01a61a1cb4726cfdcdc0a0.1633604349.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v5.git.1633604349.gitgitgadget@gmail.com","subject":"[PATCH v5 08/15] scalar: implement the `clone` subcommand","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-07T10:59:02Z","receivedAt":"2021-10-07T10:59:43Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis implements Scalar's opinionated `clone` command: it tries to use a\npartial clone and sets up a sparse checkout by default. In contrast to\n`git clone`, `scalar clone` sets up the worktree in the `src/`\nsubdirectory, to encourage a separation between the source files and the\nbuild output (which helps Git tremendously because it avoids untracked\nfiles that have to be specifically ignored when refreshing the index).\n\nAlso, it registers the repository for regular, scheduled maintenance,\nand configures a flurry of configuration settings based on the\nexperience and experiments of the Microsoft Windows and the Microsoft\nOffice development teams.\n\nNote: since the `scalar clone` command is by far the most commonly\ncalled `scalar` subcommand, we document it at the top of the manual\npage.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 201 +++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        |  31 ++++-\n contrib/scalar/t/t9099-scalar.sh |  32 +++++\n 3 files changed, 261 insertions(+), 3 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex d13eb951c3d..0401462b9b3 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -7,6 +7,7 @@\n #include \"parse-options.h\"\n #include \"config.h\"\n #include \"run-command.h\"\n+#include \"refs.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -250,6 +251,205 @@ static int unregister_dir(void)\n \treturn res;\n }\n \n+/* printf-style interface, expects `<key>=<value>` argument */\n+static int set_config(const char *fmt, ...)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tchar *value;\n+\tint res;\n+\tva_list args;\n+\n+\tva_start(args, fmt);\n+\tstrbuf_vaddf(&buf, fmt, args);\n+\tva_end(args);\n+\n+\tvalue = strchr(buf.buf, '=');\n+\tif (value)\n+\t\t*(value++) = '\\0';\n+\tres = git_config_set_gently(buf.buf, value);\n+\tstrbuf_release(&buf);\n+\n+\treturn res;\n+}\n+\n+static char *remote_default_branch(const char *url)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tstruct strbuf out = STRBUF_INIT;\n+\n+\tcp.git_cmd = 1;\n+\tstrvec_pushl(&cp.args, \"ls-remote\", \"--symref\", url, \"HEAD\", NULL);\n+\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n+\t\tconst char *line = out.buf;\n+\n+\t\twhile (*line) {\n+\t\t\tconst char *eol = strchrnul(line, '\\n'), *p;\n+\t\t\tsize_t len = eol - line;\n+\t\t\tchar *branch;\n+\n+\t\t\tif (!skip_prefix(line, \"ref: \", &p) ||\n+\t\t\t    !strip_suffix_mem(line, &len, \"\\tHEAD\")) {\n+\t\t\t\tline = eol + (*eol == '\\n');\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\teol = line + len;\n+\t\t\tif (skip_prefix(p, \"refs/heads/\", &p)) {\n+\t\t\t\tbranch = xstrndup(p, eol - p);\n+\t\t\t\tstrbuf_release(&out);\n+\t\t\t\treturn branch;\n+\t\t\t}\n+\n+\t\t\terror(_(\"remote HEAD is not a branch: '%.*s'\"),\n+\t\t\t      (int)(eol - p), p);\n+\t\t\tstrbuf_release(&out);\n+\t\t\treturn NULL;\n+\t\t}\n+\t}\n+\twarning(_(\"failed to get default branch name from remote; \"\n+\t\t  \"using local default\"));\n+\tstrbuf_reset(&out);\n+\n+\tchild_process_init(&cp);\n+\tcp.git_cmd = 1;\n+\tstrvec_pushl(&cp.args, \"symbolic-ref\", \"--short\", \"HEAD\", NULL);\n+\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n+\t\tstrbuf_trim(&out);\n+\t\treturn strbuf_detach(&out, NULL);\n+\t}\n+\n+\tstrbuf_release(&out);\n+\terror(_(\"failed to get default branch name\"));\n+\treturn NULL;\n+}\n+\n+static int cmd_clone(int argc, const char **argv)\n+{\n+\tconst char *branch = NULL;\n+\tint full_clone = 0;\n+\tstruct option clone_options[] = {\n+\t\tOPT_STRING('b', \"branch\", &branch, N_(\"<branch>\"),\n+\t\t\t   N_(\"branch to checkout after clone\")),\n+\t\tOPT_BOOL(0, \"full-clone\", &full_clone,\n+\t\t\t N_(\"when cloning, create full working directory\")),\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const clone_usage[] = {\n+\t\tN_(\"scalar clone [<options>] [--] <repo> [<dir>]\"),\n+\t\tNULL\n+\t};\n+\tconst char *url;\n+\tchar *enlistment = NULL, *dir = NULL;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint res;\n+\n+\targc = parse_options(argc, argv, NULL, clone_options, clone_usage, 0);\n+\n+\tif (argc == 2) {\n+\t\turl = argv[0];\n+\t\tenlistment = xstrdup(argv[1]);\n+\t} else if (argc == 1) {\n+\t\turl = argv[0];\n+\n+\t\tstrbuf_addstr(&buf, url);\n+\t\t/* Strip trailing slashes, if any */\n+\t\twhile (buf.len > 0 && is_dir_sep(buf.buf[buf.len - 1]))\n+\t\t\tstrbuf_setlen(&buf, buf.len - 1);\n+\t\t/* Strip suffix `.git`, if any */\n+\t\tstrbuf_strip_suffix(&buf, \".git\");\n+\n+\t\tenlistment = find_last_dir_sep(buf.buf);\n+\t\tif (!enlistment) {\n+\t\t\tdie(_(\"cannot deduce worktree name from '%s'\"), url);\n+\t\t}\n+\t\tenlistment = xstrdup(enlistment + 1);\n+\t} else {\n+\t\tusage_msg_opt(_(\"You must specify a repository to clone.\"),\n+\t\t\t      clone_usage, clone_options);\n+\t}\n+\n+\tif (is_directory(enlistment))\n+\t\tdie(_(\"directory '%s' exists already\"), enlistment);\n+\n+\tdir = xstrfmt(\"%s/src\", enlistment);\n+\n+\tstrbuf_reset(&buf);\n+\tif (branch)\n+\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", branch);\n+\telse {\n+\t\tchar *b = repo_default_branch_name(the_repository, 1);\n+\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", b);\n+\t\tfree(b);\n+\t}\n+\n+\tif ((res = run_git(\"-c\", buf.buf, \"init\", \"--\", dir, NULL)))\n+\t\tgoto cleanup;\n+\n+\tif (chdir(dir) < 0) {\n+\t\tres = error_errno(_(\"could not switch to '%s'\"), dir);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tsetup_git_directory();\n+\n+\t/* common-main already logs `argv` */\n+\ttrace2_def_repo(the_repository);\n+\n+\tif (!branch && !(branch = remote_default_branch(url))) {\n+\t\tres = error(_(\"failed to get default branch for '%s'\"), url);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tif (set_config(\"remote.origin.url=%s\", url) ||\n+\t    set_config(\"remote.origin.fetch=\"\n+\t\t       \"+refs/heads/*:refs/remotes/origin/*\") ||\n+\t    set_config(\"remote.origin.promisor=true\") ||\n+\t    set_config(\"remote.origin.partialCloneFilter=blob:none\")) {\n+\t\tres = error(_(\"could not configure remote in '%s'\"), dir);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tif (!full_clone &&\n+\t    (res = run_git(\"sparse-checkout\", \"init\", \"--cone\", NULL)))\n+\t\tgoto cleanup;\n+\n+\tif (set_recommended_config())\n+\t\treturn error(_(\"could not configure '%s'\"), dir);\n+\n+\tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL))) {\n+\t\twarning(_(\"partial clone failed; attempting full clone\"));\n+\n+\t\tif (set_config(\"remote.origin.promisor\") ||\n+\t\t    set_config(\"remote.origin.partialCloneFilter\")) {\n+\t\t\tres = error(_(\"could not configure for full clone\"));\n+\t\t\tgoto cleanup;\n+\t\t}\n+\n+\t\tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL)))\n+\t\t\tgoto cleanup;\n+\t}\n+\n+\tif ((res = set_config(\"branch.%s.remote=origin\", branch)))\n+\t\tgoto cleanup;\n+\tif ((res = set_config(\"branch.%s.merge=refs/heads/%s\",\n+\t\t\t      branch, branch)))\n+\t\tgoto cleanup;\n+\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addf(&buf, \"origin/%s\", branch);\n+\tres = run_git(\"checkout\", \"-f\", \"-t\", buf.buf, NULL);\n+\tif (res)\n+\t\tgoto cleanup;\n+\n+\tres = register_dir();\n+\n+cleanup:\n+\tfree(enlistment);\n+\tfree(dir);\n+\tstrbuf_release(&buf);\n+\treturn res;\n+}\n+\n static int cmd_list(int argc, const char **argv)\n {\n \tif (argc != 1)\n@@ -346,6 +546,7 @@ static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"clone\", cmd_clone },\n \t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex f93e3d00efd..d65fb5f1491 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,6 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n+scalar clone [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n@@ -29,12 +30,36 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand. With the exception of `list`, all subcommands\n-expect to be run in an enlistment.\n+depending on the subcommand. With the exception of `clone` and `list`, all\n+subcommands expect to be run in an enlistment.\n \n COMMANDS\n --------\n \n+Clone\n+~~~~~\n+\n+clone [<options>] <url> [<enlistment>]::\n+\tClones the specified repository, similar to linkgit:git-clone[1]. By\n+\tdefault, only commit and tree objects are cloned. Once finished, the\n+\tworktree is located at `<enlistment>/src`.\n++\n+The sparse-checkout feature is enabled (except when run with `--full-clone`)\n+and the only files present are those in the top-level directory. Use\n+`git sparse-checkout set` to expand the set of directories you want to see,\n+or `git sparse-checkout disable` to expand to all files (see\n+linkgit:git-sparse-checkout[1] for more details). You can explore the\n+subdirectories outside your sparse-checkout by using `git ls-tree HEAD`.\n+\n+-b <name>::\n+--branch <name>::\n+\tInstead of checking out the branch pointed to by the cloned\n+\trepository's HEAD, check out the `<name>` branch instead.\n+\n+--[no-]full-clone::\n+\tA sparse-checkout is initialized by default. This behavior can be\n+\tturned off via `--full-clone`.\n+\n List\n ~~~~\n \n@@ -64,7 +89,7 @@ unregister [<enlistment>]::\n \n SEE ALSO\n --------\n-linkgit:git-maintenance[1].\n+linkgit:git-clone[1], linkgit:git-maintenance[1].\n \n Scalar\n ---\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex ef0e8d680d5..295398f62cc 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -10,6 +10,9 @@ PATH=$PWD/..:$PATH\n \n . ../../../t/test-lib.sh\n \n+GIT_TEST_MAINT_SCHEDULER=\"crontab:test-tool crontab ../cron.txt\"\n+export GIT_TEST_MAINT_SCHEDULER\n+\n test_expect_success 'scalar shows a usage' '\n \ttest_expect_code 129 scalar -h\n '\n@@ -29,4 +32,33 @@ test_expect_success 'scalar unregister' '\n \t! grep -F \"$(pwd)/vanish/src\" scalar.repos\n '\n \n+test_expect_success 'set up repository to clone' '\n+\ttest_commit first &&\n+\ttest_commit second &&\n+\ttest_commit third &&\n+\tgit switch -c parallel first &&\n+\tmkdir -p 1/2 &&\n+\ttest_commit 1/2/3 &&\n+\tgit config uploadPack.allowFilter true &&\n+\tgit config uploadPack.allowAnySHA1InWant true\n+'\n+\n+test_expect_success 'scalar clone' '\n+\tsecond=$(git rev-parse --verify second:second.t) &&\n+\tscalar clone \"file://$(pwd)\" cloned &&\n+\t(\n+\t\tcd cloned/src &&\n+\n+\t\tgit config --get --global --fixed-value maintenance.repo \\\n+\t\t\t\"$(pwd)\" &&\n+\n+\t\ttest_path_is_missing 1/2 &&\n+\t\ttest_must_fail git rev-list --missing=print $second &&\n+\t\tgit rev-list $second &&\n+\t\tgit cat-file blob $second >actual &&\n+\t\techo \"second\" >expect &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"438153","messageId":"5a0b3843f986246d33f5ea3c63e5d67619348816.1633604349.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v5.git.1633604349.gitgitgadget@gmail.com","subject":"[PATCH v5 10/15] scalar: implement the `run` command","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-07T10:59:04Z","receivedAt":"2021-10-07T10:59:44Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nNote: this subcommand is provided primarily for backwards-compatibility,\nfor existing Scalar uses. It is mostly just a shim for `git\nmaintenance`, mapping task names from the way Scalar called them to the\nway Git calls them.\n\nThe reason why those names differ? The background maintenance was first\nimplemented in Scalar, and when it was contributed as a patch series\nimplementing the `git maintenance` command, reviewers suggested better\nnames, those suggestions were accepted before the patches were\nintegrated into core Git.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 64 +++++++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt | 19 ++++++++++++\n 2 files changed, 83 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 754e19d781d..d9631287e12 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -483,6 +483,69 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_run(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tstruct {\n+\t\tconst char *arg, *task;\n+\t} tasks[] = {\n+\t\t{ \"config\", NULL },\n+\t\t{ \"commit-graph\", \"commit-graph\" },\n+\t\t{ \"fetch\", \"prefetch\" },\n+\t\t{ \"loose-objects\", \"loose-objects\" },\n+\t\t{ \"pack-files\", \"incremental-repack\" },\n+\t\t{ NULL, NULL }\n+\t};\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tconst char *usagestr[] = { NULL, NULL };\n+\tint i;\n+\n+\tstrbuf_addstr(&buf, N_(\"scalar run <task> [<enlistment>]\\nTasks:\\n\"));\n+\tfor (i = 0; tasks[i].arg; i++)\n+\t\tstrbuf_addf(&buf, \"\\t%s\\n\", tasks[i].arg);\n+\tusagestr[0] = buf.buf;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usagestr, 0);\n+\n+\tif (!argc)\n+\t\tusage_with_options(usagestr, options);\n+\n+\tif (!strcmp(\"all\", argv[0])) {\n+\t\ti = -1;\n+\t} else {\n+\t\tfor (i = 0; tasks[i].arg && strcmp(tasks[i].arg, argv[0]); i++)\n+\t\t\t; /* keep looking for the task */\n+\n+\t\tif (i > 0 && !tasks[i].arg) {\n+\t\t\terror(_(\"no such task: '%s'\"), argv[0]);\n+\t\t\tusage_with_options(usagestr, options);\n+\t\t}\n+\t}\n+\n+\targc--;\n+\targv++;\n+\tsetup_enlistment_directory(argc, argv, usagestr, options, NULL);\n+\tstrbuf_release(&buf);\n+\n+\tif (i == 0)\n+\t\treturn register_dir();\n+\n+\tif (i > 0)\n+\t\treturn run_git(\"maintenance\", \"run\",\n+\t\t\t       \"--task\", tasks[i].task, NULL);\n+\n+\tif (register_dir())\n+\t\treturn -1;\n+\tfor (i = 1; tasks[i].arg; i++)\n+\t\tif (run_git(\"maintenance\", \"run\",\n+\t\t\t    \"--task\", tasks[i].task, NULL))\n+\t\t\treturn -1;\n+\treturn 0;\n+}\n+\n static int remove_deleted_enlistment(struct strbuf *path)\n {\n \tint res = 0;\n@@ -555,6 +618,7 @@ static struct {\n \t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n+\t{ \"run\", cmd_run },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 46999cf7c84..f139a14445d 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -12,6 +12,7 @@ scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<e\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n+scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -97,6 +98,24 @@ unregister [<enlistment>]::\n \tRemove the specified repository from the list of repositories\n \tregistered with Scalar and stop the scheduled background maintenance.\n \n+Run\n+~~~\n+\n+scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]::\n+\tRun the given maintenance task (or all tasks, if `all` was specified).\n+\tExcept for `all` and `config`, this subcommand simply hands off to\n+\tlinkgit:git-maintenance[1] (mapping `fetch` to `prefetch` and\n+\t`pack-files` to `incremental-repack`).\n++\n+These tasks are run automatically as part of the scheduled maintenance,\n+as soon as the repository is registered with Scalar. It should therefore\n+not be necessary to run this subcommand manually.\n++\n+The `config` task is specific to Scalar and configures all those\n+opinionated default settings that make Git work more efficiently with\n+large repositories. As this task is run as part of `scalar clone`\n+automatically, explicit invocations of this task are rarely needed.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"438154","messageId":"03b8385ebb5ceae12eaff138606e877216a6f833.1633604349.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v5.git.1633604349.gitgitgadget@gmail.com","subject":"[PATCH v5 09/15] scalar: teach 'clone' to support the --single-branch option","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-07T10:59:03Z","receivedAt":"2021-10-07T10:59:45Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nJust like `git clone`, the `scalar clone` command now also offers to\nrestrict the clone to a single branch.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          |  9 +++++++--\n contrib/scalar/scalar.txt        | 12 +++++++++++-\n contrib/scalar/t/t9099-scalar.sh |  6 +++++-\n 3 files changed, 23 insertions(+), 4 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 0401462b9b3..754e19d781d 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -326,12 +326,15 @@ static char *remote_default_branch(const char *url)\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n-\tint full_clone = 0;\n+\tint full_clone = 0, single_branch = 0;\n \tstruct option clone_options[] = {\n \t\tOPT_STRING('b', \"branch\", &branch, N_(\"<branch>\"),\n \t\t\t   N_(\"branch to checkout after clone\")),\n \t\tOPT_BOOL(0, \"full-clone\", &full_clone,\n \t\t\t N_(\"when cloning, create full working directory\")),\n+\t\tOPT_BOOL(0, \"single-branch\", &single_branch,\n+\t\t\t N_(\"only download metadata for the branch that will \"\n+\t\t\t    \"be checked out\")),\n \t\tOPT_END(),\n \t};\n \tconst char * const clone_usage[] = {\n@@ -402,7 +405,9 @@ static int cmd_clone(int argc, const char **argv)\n \n \tif (set_config(\"remote.origin.url=%s\", url) ||\n \t    set_config(\"remote.origin.fetch=\"\n-\t\t       \"+refs/heads/*:refs/remotes/origin/*\") ||\n+\t\t       \"+refs/heads/%s:refs/remotes/origin/%s\",\n+\t\t       single_branch ? branch : \"*\",\n+\t\t       single_branch ? branch : \"*\") ||\n \t    set_config(\"remote.origin.promisor=true\") ||\n \t    set_config(\"remote.origin.partialCloneFilter=blob:none\")) {\n \t\tres = error(_(\"could not configure remote in '%s'\"), dir);\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex d65fb5f1491..46999cf7c84 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,7 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n-scalar clone [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n+scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n@@ -56,6 +56,16 @@ subdirectories outside your sparse-checkout by using `git ls-tree HEAD`.\n \tInstead of checking out the branch pointed to by the cloned\n \trepository's HEAD, check out the `<name>` branch instead.\n \n+--[no-]single-branch::\n+\tClone only the history leading to the tip of a single branch, either\n+\tspecified by the `--branch` option or the primary branch remote's\n+\t`HEAD` points at.\n++\n+Further fetches into the resulting repository will only update the\n+remote-tracking branch for the branch this option was used for the initial\n+cloning. If the HEAD at the remote did not point at any branch when\n+`--single-branch` clone was made, no remote-tracking branch is created.\n+\n --[no-]full-clone::\n \tA sparse-checkout is initialized by default. This behavior can be\n \tturned off via `--full-clone`.\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 295398f62cc..9a35ab4fde6 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -45,13 +45,17 @@ test_expect_success 'set up repository to clone' '\n \n test_expect_success 'scalar clone' '\n \tsecond=$(git rev-parse --verify second:second.t) &&\n-\tscalar clone \"file://$(pwd)\" cloned &&\n+\tscalar clone \"file://$(pwd)\" cloned --single-branch &&\n \t(\n \t\tcd cloned/src &&\n \n \t\tgit config --get --global --fixed-value maintenance.repo \\\n \t\t\t\"$(pwd)\" &&\n \n+\t\tgit for-each-ref --format=\"%(refname)\" refs/remotes/origin/ >actual &&\n+\t\techo \"refs/remotes/origin/parallel\" >expect &&\n+\t\ttest_cmp expect actual &&\n+\n \t\ttest_path_is_missing 1/2 &&\n \t\ttest_must_fail git rev-list --missing=print $second &&\n \t\tgit rev-list $second &&\n-- \ngitgitgadget\n\n"},{"id":"438155","messageId":"6d9aaeb05fae09f577ce9d7cb1e008b2625c67ed.1633604349.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v5.git.1633604349.gitgitgadget@gmail.com","subject":"[PATCH v5 11/15] scalar: allow reconfiguring an existing enlistment","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-07T10:59:05Z","receivedAt":"2021-10-07T10:59:48Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis comes in handy during Scalar upgrades, or when config settings were\nmessed up by mistake.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 79 +++++++++++++++++++++-----------\n contrib/scalar/scalar.txt        |  8 ++++\n contrib/scalar/t/t9099-scalar.sh |  8 ++++\n 3 files changed, 67 insertions(+), 28 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex d9631287e12..6bebba0b51f 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -108,18 +108,20 @@ static int run_git(const char *arg, ...)\n \treturn res;\n }\n \n-static int set_recommended_config(void)\n+static int set_recommended_config(int reconfigure)\n {\n \tstruct {\n \t\tconst char *key;\n \t\tconst char *value;\n+\t\tint overwrite_on_reconfigure;\n \t} config[] = {\n-\t\t{ \"am.keepCR\", \"true\" },\n-\t\t{ \"core.FSCache\", \"true\" },\n-\t\t{ \"core.multiPackIndex\", \"true\" },\n-\t\t{ \"core.preloadIndex\", \"true\" },\n+\t\t/* Required */\n+\t\t{ \"am.keepCR\", \"true\", 1 },\n+\t\t{ \"core.FSCache\", \"true\", 1 },\n+\t\t{ \"core.multiPackIndex\", \"true\", 1 },\n+\t\t{ \"core.preloadIndex\", \"true\", 1 },\n #ifndef WIN32\n-\t\t{ \"core.untrackedCache\", \"true\" },\n+\t\t{ \"core.untrackedCache\", \"true\", 1 },\n #else\n \t\t/*\n \t\t * Unfortunately, Scalar's Functional Tests demonstrated\n@@ -133,28 +135,29 @@ static int set_recommended_config(void)\n \t\t * Therefore, with a sad heart, we disable this very useful\n \t\t * feature on Windows.\n \t\t */\n-\t\t{ \"core.untrackedCache\", \"false\" },\n+\t\t{ \"core.untrackedCache\", \"false\", 1 },\n #endif\n-\t\t{ \"core.logAllRefUpdates\", \"true\" },\n-\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n-\t\t{ \"credential.validate\", \"false\" }, /* GCM4W-only */\n-\t\t{ \"gc.auto\", \"0\" },\n-\t\t{ \"gui.GCWarning\", \"false\" },\n-\t\t{ \"index.threads\", \"true\" },\n-\t\t{ \"index.version\", \"4\" },\n-\t\t{ \"merge.stat\", \"false\" },\n-\t\t{ \"merge.renames\", \"false\" },\n-\t\t{ \"pack.useBitmaps\", \"false\" },\n-\t\t{ \"pack.useSparse\", \"true\" },\n-\t\t{ \"receive.autoGC\", \"false\" },\n-\t\t{ \"reset.quiet\", \"true\" },\n-\t\t{ \"feature.manyFiles\", \"false\" },\n-\t\t{ \"feature.experimental\", \"false\" },\n-\t\t{ \"fetch.unpackLimit\", \"1\" },\n-\t\t{ \"fetch.writeCommitGraph\", \"false\" },\n+\t\t{ \"core.logAllRefUpdates\", \"true\", 1 },\n+\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\", 1 },\n+\t\t{ \"credential.validate\", \"false\", 1 }, /* GCM4W-only */\n+\t\t{ \"gc.auto\", \"0\", 1 },\n+\t\t{ \"gui.GCWarning\", \"false\", 1 },\n+\t\t{ \"index.threads\", \"true\", 1 },\n+\t\t{ \"index.version\", \"4\", 1 },\n+\t\t{ \"merge.stat\", \"false\", 1 },\n+\t\t{ \"merge.renames\", \"false\", 1 },\n+\t\t{ \"pack.useBitmaps\", \"false\", 1 },\n+\t\t{ \"pack.useSparse\", \"true\", 1 },\n+\t\t{ \"receive.autoGC\", \"false\", 1 },\n+\t\t{ \"reset.quiet\", \"true\", 1 },\n+\t\t{ \"feature.manyFiles\", \"false\", 1 },\n+\t\t{ \"feature.experimental\", \"false\", 1 },\n+\t\t{ \"fetch.unpackLimit\", \"1\", 1 },\n+\t\t{ \"fetch.writeCommitGraph\", \"false\", 1 },\n #ifdef WIN32\n-\t\t{ \"http.sslBackend\", \"schannel\" },\n+\t\t{ \"http.sslBackend\", \"schannel\", 1 },\n #endif\n+\t\t/* Optional */\n \t\t{ \"status.aheadBehind\", \"false\" },\n \t\t{ \"commitGraph.generationVersion\", \"1\" },\n \t\t{ \"core.autoCRLF\", \"false\" },\n@@ -165,7 +168,8 @@ static int set_recommended_config(void)\n \tchar *value;\n \n \tfor (i = 0; config[i].key; i++) {\n-\t\tif (git_config_get_string(config[i].key, &value)) {\n+\t\tif ((reconfigure && config[i].overwrite_on_reconfigure) ||\n+\t\t    git_config_get_string(config[i].key, &value)) {\n \t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"created\");\n \t\t\tif (git_config_set_gently(config[i].key,\n \t\t\t\t\t\t  config[i].value) < 0)\n@@ -230,7 +234,7 @@ static int register_dir(void)\n \tint res = add_or_remove_enlistment(1);\n \n \tif (!res)\n-\t\tres = set_recommended_config();\n+\t\tres = set_recommended_config(0);\n \n \tif (!res)\n \t\tres = toggle_maintenance(1);\n@@ -418,7 +422,7 @@ static int cmd_clone(int argc, const char **argv)\n \t    (res = run_git(\"sparse-checkout\", \"init\", \"--cone\", NULL)))\n \t\tgoto cleanup;\n \n-\tif (set_recommended_config())\n+\tif (set_recommended_config(0))\n \t\treturn error(_(\"could not configure '%s'\"), dir);\n \n \tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL))) {\n@@ -483,6 +487,24 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_reconfigure(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar reconfigure [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn set_recommended_config(1);\n+}\n+\n static int cmd_run(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -619,6 +641,7 @@ static struct {\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n \t{ \"run\", cmd_run },\n+\t{ \"reconfigure\", cmd_reconfigure },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex f139a14445d..f4e4686e8c8 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -13,6 +13,7 @@ scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n+scalar reconfigure <enlistment>\n \n DESCRIPTION\n -----------\n@@ -116,6 +117,13 @@ opinionated default settings that make Git work more efficiently with\n large repositories. As this task is run as part of `scalar clone`\n automatically, explicit invocations of this task are rarely needed.\n \n+Reconfigure\n+~~~~~~~~~~~\n+\n+After a Scalar upgrade, or when the configuration of a Scalar enlistment\n+was somehow corrupted or changed by mistake, this subcommand allows to\n+reconfigure the enlistment.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 9a35ab4fde6..e6d74a06ca0 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -65,4 +65,12 @@ test_expect_success 'scalar clone' '\n \t)\n '\n \n+test_expect_success 'scalar reconfigure' '\n+\tgit init one/src &&\n+\tscalar register one &&\n+\tgit -C one/src config core.preloadIndex false &&\n+\tscalar reconfigure one &&\n+\ttest true = \"$(git -C one/src config core.preloadIndex)\"\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"438156","messageId":"2ecfaa5d0fe7fecde35e409900b2dfb78b02a96b.1633604349.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v5.git.1633604349.gitgitgadget@gmail.com","subject":"[PATCH v5 14/15] scalar: implement the `version` command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-07T10:59:08Z","receivedAt":"2021-10-07T10:59:54Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe .NET version of Scalar has a `version` command. This was necessary\nbecause it was versioned independently of Git.\n\nSince Scalar is now tightly coupled with Git, it does not make sense for\nthem to show different versions. Therefore, it shows the same output as\n`git version`. For backwards-compatibility with the .NET version,\n`scalar version` prints to `stderr`, though (`git version` prints to\n`stdout` instead).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c | 39 +++++++++++++++++++++++++++++++++++++++\n 1 file changed, 39 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 8aaeca7cc64..b2e92cf63b5 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -10,6 +10,7 @@\n #include \"refs.h\"\n #include \"dir.h\"\n #include \"packfile.h\"\n+#include \"help.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -356,6 +357,15 @@ static int delete_enlistment(struct strbuf *enlistment)\n \treturn 0;\n }\n \n+/*\n+ * Dummy implementation; Using `get_version_info()` would cause a link error\n+ * without this.\n+ */\n+void load_builtin_commands(const char *prefix, struct cmdnames *cmds)\n+{\n+\tdie(\"not implemented\");\n+}\n+\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n@@ -749,6 +759,34 @@ static int cmd_delete(int argc, const char **argv)\n \treturn res;\n }\n \n+static int cmd_version(int argc, const char **argv)\n+{\n+\tint verbose = 0, build_options = 0;\n+\tstruct option options[] = {\n+\t\tOPT__VERBOSE(&verbose, N_(\"include Git version\")),\n+\t\tOPT_BOOL(0, \"build-options\", &build_options,\n+\t\t\t N_(\"include Git's build options\")),\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar verbose [-v | --verbose] [--build-options]\"),\n+\t\tNULL\n+\t};\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tif (argc != 0)\n+\t\tusage_with_options(usage, options);\n+\n+\tget_version_info(&buf, build_options);\n+\tfprintf(stderr, \"%s\\n\", buf.buf);\n+\tstrbuf_release(&buf);\n+\n+\treturn 0;\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n@@ -760,6 +798,7 @@ static struct {\n \t{ \"run\", cmd_run },\n \t{ \"reconfigure\", cmd_reconfigure },\n \t{ \"delete\", cmd_delete },\n+\t{ \"version\", cmd_version },\n \t{ NULL, NULL},\n };\n \n-- \ngitgitgadget\n\n"},{"id":"438157","messageId":"2967d7f1425e06f15e3f696fa873b952b128df51.1633604349.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v5.git.1633604349.gitgitgadget@gmail.com","subject":"[PATCH v5 12/15] scalar: teach 'reconfigure' to optionally handle all registered enlistments","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-07T10:59:06Z","receivedAt":"2021-10-07T10:59:59Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nAfter a Scalar upgrade, it can come in really handy if there is an easy\nway to reconfigure all Scalar enlistments. This new option offers this\nfunctionality.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 61 ++++++++++++++++++++++++++++++--\n contrib/scalar/scalar.txt        |  9 +++--\n contrib/scalar/t/t9099-scalar.sh |  3 ++\n 3 files changed, 67 insertions(+), 6 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 6bebba0b51f..234a7dce479 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -487,22 +487,77 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int get_scalar_repos(const char *key, const char *value, void *data)\n+{\n+\tstruct string_list *list = data;\n+\n+\tif (!strcmp(key, \"scalar.repo\"))\n+\t\tstring_list_append(list, value);\n+\n+\treturn 0;\n+}\n+\n static int cmd_reconfigure(int argc, const char **argv)\n {\n+\tint all = 0;\n \tstruct option options[] = {\n+\t\tOPT_BOOL('a', \"all\", &all,\n+\t\t\t N_(\"reconfigure all registered enlistments\")),\n \t\tOPT_END(),\n \t};\n \tconst char * const usage[] = {\n-\t\tN_(\"scalar reconfigure [<enlistment>]\"),\n+\t\tN_(\"scalar reconfigure [--all | <enlistment>]\"),\n \t\tNULL\n \t};\n+\tstruct string_list scalar_repos = STRING_LIST_INIT_DUP;\n+\tint i, res = 0;\n+\tstruct repository r = { NULL };\n+\tstruct strbuf commondir = STRBUF_INIT, gitdir = STRBUF_INIT;\n \n \targc = parse_options(argc, argv, NULL, options,\n \t\t\t     usage, 0);\n \n-\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\tif (!all) {\n+\t\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\t\treturn set_recommended_config(1);\n+\t}\n+\n+\tif (argc > 0)\n+\t\tusage_msg_opt(_(\"--all or <enlistment>, but not both\"),\n+\t\t\t      usage, options);\n+\n+\tgit_config(get_scalar_repos, &scalar_repos);\n \n-\treturn set_recommended_config(1);\n+\tfor (i = 0; i < scalar_repos.nr; i++) {\n+\t\tconst char *dir = scalar_repos.items[i].string;\n+\n+\t\tstrbuf_reset(&commondir);\n+\t\tstrbuf_reset(&gitdir);\n+\n+\t\tif (chdir(dir) < 0) {\n+\t\t\twarning_errno(_(\"could not switch to '%s'\"), dir);\n+\t\t\tres = -1;\n+\t\t} else if (discover_git_directory(&commondir, &gitdir) < 0) {\n+\t\t\twarning_errno(_(\"git repository gone in '%s'\"), dir);\n+\t\t\tres = -1;\n+\t\t} else {\n+\t\t\tgit_config_clear();\n+\n+\t\t\tthe_repository = &r;\n+\t\t\tr.commondir = commondir.buf;\n+\t\t\tr.gitdir = gitdir.buf;\n+\n+\t\t\tif (set_recommended_config(1) < 0)\n+\t\t\t\tres = -1;\n+\t\t}\n+\t}\n+\n+\tstring_list_clear(&scalar_repos, 1);\n+\tstrbuf_release(&commondir);\n+\tstrbuf_release(&gitdir);\n+\n+\treturn res;\n }\n \n static int cmd_run(int argc, const char **argv)\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex f4e4686e8c8..2fa96fcabc6 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -13,7 +13,7 @@ scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n-scalar reconfigure <enlistment>\n+scalar reconfigure [ --all | <enlistment> ]\n \n DESCRIPTION\n -----------\n@@ -32,8 +32,8 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand. With the exception of `clone` and `list`, all\n-subcommands expect to be run in an enlistment.\n+depending on the subcommand. With the exception of `clone`, `list` and\n+`reconfigure --all`, all subcommands expect to be run in an enlistment.\n \n COMMANDS\n --------\n@@ -124,6 +124,9 @@ After a Scalar upgrade, or when the configuration of a Scalar enlistment\n was somehow corrupted or changed by mistake, this subcommand allows to\n reconfigure the enlistment.\n \n+With the `--all` option, all enlistments currently registered with Scalar\n+will be reconfigured. Use this option after each Scalar upgrade.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex e6d74a06ca0..5fe7fabd0e5 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -70,6 +70,9 @@ test_expect_success 'scalar reconfigure' '\n \tscalar register one &&\n \tgit -C one/src config core.preloadIndex false &&\n \tscalar reconfigure one &&\n+\ttest true = \"$(git -C one/src config core.preloadIndex)\" &&\n+\tgit -C one/src config core.preloadIndex false &&\n+\tscalar reconfigure -a &&\n \ttest true = \"$(git -C one/src config core.preloadIndex)\"\n '\n \n-- \ngitgitgadget\n\n"},{"id":"438158","messageId":"8069cc536fedce2c4707efa87832bc06b443060f.1633604349.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v5.git.1633604349.gitgitgadget@gmail.com","subject":"[PATCH v5 13/15] scalar: implement the `delete` command","fromName":"Matthew John Cheetham via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-07T10:59:07Z","receivedAt":"2021-10-07T11:00:01Z","isPatch":true,"sender":{"key":"mjcheetham@outlook.com","avatar":"https://avatars.githubusercontent.com/u/5658207?v=4"},"body":"From: Matthew John Cheetham <mjcheetham@outlook.com>\n\nDelete an enlistment by first unregistering the repository and then\ndeleting the enlistment directory (usually the directory containing the\nworktree `src/` directory).\n\nOn Windows, if the current directory is inside the enlistment's\ndirectory, change to the parent of the enlistment directory, to allow us\nto delete the enlistment (directories used by processes e.g. as current\nworking directories cannot be deleted on Windows).\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Matthew John Cheetham <mjcheetham@outlook.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 63 ++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        |  8 ++++\n contrib/scalar/t/t9099-scalar.sh |  9 +++++\n 3 files changed, 80 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 234a7dce479..8aaeca7cc64 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -8,6 +8,8 @@\n #include \"config.h\"\n #include \"run-command.h\"\n #include \"refs.h\"\n+#include \"dir.h\"\n+#include \"packfile.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -327,6 +329,33 @@ static char *remote_default_branch(const char *url)\n \treturn NULL;\n }\n \n+static int delete_enlistment(struct strbuf *enlistment)\n+{\n+#ifdef WIN32\n+\tstruct strbuf parent = STRBUF_INIT;\n+#endif\n+\n+\tif (unregister_dir())\n+\t\tdie(_(\"failed to unregister repository\"));\n+\n+#ifdef WIN32\n+\t/*\n+\t * Change the current directory to one outside of the enlistment so\n+\t * that we may delete everything underneath it.\n+\t */\n+\tstrbuf_addbuf(&parent, enlistment);\n+\tstrbuf_parent_directory(&parent);\n+\tif (chdir(parent.buf) < 0)\n+\t\tdie_errno(_(\"could not switch to '%s'\"), parent.buf);\n+\tstrbuf_release(&parent);\n+#endif\n+\n+\tif (remove_dir_recursively(enlistment, 0))\n+\t\tdie(_(\"failed to delete enlistment directory\"));\n+\n+\treturn 0;\n+}\n+\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n@@ -687,6 +716,39 @@ static int cmd_unregister(int argc, const char **argv)\n \treturn unregister_dir();\n }\n \n+static int cmd_delete(int argc, const char **argv)\n+{\n+\tchar *cwd = xgetcwd();\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar delete <enlistment>\"),\n+\t\tNULL\n+\t};\n+\tstruct strbuf enlistment = STRBUF_INIT;\n+\tint res = 0;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tif (argc != 1)\n+\t\tusage_with_options(usage, options);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, &enlistment);\n+\n+\tif (dir_inside_of(cwd, enlistment.buf) >= 0)\n+\t\tres = error(_(\"refusing to delete current working directory\"));\n+\telse {\n+\t\tclose_object_store(the_repository->objects);\n+\t\tres = delete_enlistment(&enlistment);\n+\t}\n+\tstrbuf_release(&enlistment);\n+\tfree(cwd);\n+\n+\treturn res;\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n@@ -697,6 +759,7 @@ static struct {\n \t{ \"unregister\", cmd_unregister },\n \t{ \"run\", cmd_run },\n \t{ \"reconfigure\", cmd_reconfigure },\n+\t{ \"delete\", cmd_delete },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 2fa96fcabc6..6fc57707718 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -14,6 +14,7 @@ scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n scalar reconfigure [ --all | <enlistment> ]\n+scalar delete <enlistment>\n \n DESCRIPTION\n -----------\n@@ -127,6 +128,13 @@ reconfigure the enlistment.\n With the `--all` option, all enlistments currently registered with Scalar\n will be reconfigured. Use this option after each Scalar upgrade.\n \n+Delete\n+~~~~~~\n+\n+delete <enlistment>::\n+\tThis subcommand lets you delete an existing Scalar enlistment from your\n+\tlocal file system, unregistering the repository.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 5fe7fabd0e5..7e8771d0eff 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -76,4 +76,13 @@ test_expect_success 'scalar reconfigure' '\n \ttest true = \"$(git -C one/src config core.preloadIndex)\"\n '\n \n+test_expect_success 'scalar delete without enlistment shows a usage' '\n+\ttest_expect_code 129 scalar delete\n+'\n+\n+test_expect_success 'scalar delete with enlistment' '\n+\tscalar delete cloned &&\n+\ttest_path_is_missing cloned\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"438159","messageId":"f81e8b3bcf1b8972bc5214b31fdb092bb975b457.1633604349.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v5.git.1633604349.gitgitgadget@gmail.com","subject":"[PATCH v5 15/15] scalar: accept -C and -c options before the subcommand","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-07T10:59:09Z","receivedAt":"2021-10-07T11:00:01Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe `git` executable has these two very useful options:\n\n-C <directory>:\n\tswitch to the specified directory before performing any actions\n\n-c <key>=<value>:\n\ttemporarily configure this setting for the duration of the\n\tspecified scalar subcommand\n\nWith this commit, we teach the `scalar` executable the same trick.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 22 +++++++++++++++++++++-\n contrib/scalar/scalar.txt | 10 ++++++++++\n 2 files changed, 31 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex b2e92cf63b5..6c496318bd4 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -807,6 +807,25 @@ int cmd_main(int argc, const char **argv)\n \tstruct strbuf scalar_usage = STRBUF_INIT;\n \tint i;\n \n+\twhile (argc > 1 && *argv[1] == '-') {\n+\t\tif (!strcmp(argv[1], \"-C\")) {\n+\t\t\tif (argc < 3)\n+\t\t\t\tdie(_(\"-C requires a <directory>\"));\n+\t\t\tif (chdir(argv[2]) < 0)\n+\t\t\t\tdie_errno(_(\"could not change to '%s'\"),\n+\t\t\t\t\t  argv[2]);\n+\t\t\targc -= 2;\n+\t\t\targv += 2;\n+\t\t} else if (!strcmp(argv[1], \"-c\")) {\n+\t\t\tif (argc < 3)\n+\t\t\t\tdie(_(\"-c requires a <key>=<value> argument\"));\n+\t\t\tgit_config_push_parameter(argv[2]);\n+\t\t\targc -= 2;\n+\t\t\targv += 2;\n+\t\t} else\n+\t\t\tbreak;\n+\t}\n+\n \tif (argc > 1) {\n \t\targv++;\n \t\targc--;\n@@ -817,7 +836,8 @@ int cmd_main(int argc, const char **argv)\n \t}\n \n \tstrbuf_addstr(&scalar_usage,\n-\t\t      N_(\"scalar <command> [<options>]\\n\\nCommands:\\n\"));\n+\t\t      N_(\"scalar [-C <directory>] [-c <key>=<value>] \"\n+\t\t\t \"<command> [<options>]\\n\\nCommands:\\n\"));\n \tfor (i = 0; builtins[i].name; i++)\n \t\tstrbuf_addf(&scalar_usage, \"\\t%s\\n\", builtins[i].name);\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 6fc57707718..3a80f829edc 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -36,6 +36,16 @@ The `scalar` command implements various subcommands, and different options\n depending on the subcommand. With the exception of `clone`, `list` and\n `reconfigure --all`, all subcommands expect to be run in an enlistment.\n \n+The following options can be specified _before_ the subcommand:\n+\n+-C <directory>::\n+\tBefore running the subcommand, change the working directory. This\n+\toption imitates the same option of linkgit:git[1].\n+\n+-c <key>=<value>::\n+\tFor the duration of running the specified subcommand, configure this\n+\tsetting. This option imitates the same option of linkgit:git[1].\n+\n COMMANDS\n --------\n \n-- \ngitgitgadget\n"},{"id":"438161","messageId":"87fstdgh8m.fsf@evledraar.gmail.com","threadId":"56409","inReplyTo":"pull.1005.v5.git.1633604349.gitgitgadget@gmail.com","subject":"Re: [PATCH v5 00/15] Upstreaming the Scalar command","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-10-07T11:28:07Z","receivedAt":"2021-10-07T12:19:11Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Oct 07 2021, Johannes Schindelin via GitGitGadget wrote:\n\n>  * The OBJECTS list in the Makefile will now include Scalar.\n\nSo that looks like a partial fix for what I brought up in [1] [...]\n\n> Range-diff vs v4:\n>\n>   1:  852ec003109 !  1:  7119a8efc21 scalar: create a rudimentary executable\n>      @@ Commit message\n>           Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n>       \n>        ## Makefile ##\n>      -@@ Makefile: endif\n>      - .PHONY: objects\n>      - objects: $(OBJECTS)\n>      - \n>      +@@ Makefile: OBJECTS += $(FUZZ_OBJS)\n>      + ifndef NO_CURL\n>      + \tOBJECTS += http.o http-walker.o remote-curl.o\n>      + endif\n>      ++\n>       +SCALAR_SOURCES := contrib/scalar/scalar.c\n>       +SCALAR_OBJECTS := $(SCALAR_SOURCES:c=o)\n>       +OBJECTS += $(SCALAR_OBJECTS)\n>       +\n>      - dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d)\n>      - dep_dirs := $(addsuffix .depend,$(sort $(dir $(OBJECTS))))\n>      + .PHONY: objects\n>      + objects: $(OBJECTS)\n\nExcept that this & contrib/scalar/Makefile is still broken in multiple\nways. We now have two Makefiles that can build contrib/scalar/scalar:\n\n    touch advice.h; make -j8 contrib/scalar/scalar\n\nBut try:\n\n    $ touch advice.h; (cd contrib/scalar 2>/dev/null && make scalar)\n    make: 'scalar' is up to date.\n\nI.e. (I'm presuming in response to what I brought up in [1]) the\ndepenency graph in the top-level Makefile is correct in this specific\narea. But it understands the \".depends\" files (depending on\nCOMPUTE_HEADER_DEPENDENCIES), your sub-Makefile doesn't.\n\nThere's similar whack-a-mole issues in other areas, e.g.:\n\n    make -C contrib/scalar/ test\n\nWill break or not depending on whether you've built the top-level\ngit.\n\nI noticed at least one other subtle breakage (first thing I checked\nafter those two).\n\nI'm happy to send you a working patch to integrate that fixes all these\nissues, it also integrates with \"make install\", this series leaves us\nwith a \"scalar\" binary, but no way to install it, if we just piggy-back\non the existing installation procedure.\n\nThe side-thread on the v3[3] that you most recently replied to is\nconflating some suggestion of shipping this as a built-in, with the\npurely build-system implementation details I'm suggesting here.\n\nI did mention using it as a built-in in [4], but for the semi-related\nissue of scalar.c copy/pasting less code from git.c. But that was in the\ncontext of such a thing being purely a non-visible implementation\ndetail. I.e. it would still be \"scalar\", not \"git scalar\".\n\n*That* suggestion is just a side-musing about whether it would be easier\nto teach git.c to inspects its argv and have a special-case for\ndispatching to cmd_scalar(), a user would never know the difference. I\nthink that might also be worthwhile, but I care *way* less about that\nthan making maintaining the Makefile a hassle, and it's an entirely\northogonal suggestion.\n\n1. https://lore.kernel.org/git/875yu9iolf.fsf@evledraar.gmail.com/\n2. https://lore.kernel.org/git/87mtofnzv1.fsf@evledraar.gmail.com/\n3. https://lore.kernel.org/git/xmqq1r5qzv35.fsf@gitster.g\n4. https://lore.kernel.org/git/87k0jhn0p9.fsf@evledraar.gmail.com/\n"},{"id":"438170","messageId":"CABPp-BFmyuQYbUbCVjJfNhyZYwDrG_anZDKdFjy78oeK4zn+Ew@mail.gmail.com","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2110062238540.395@tvgsbejvaqbjf.bet","subject":"Re: [PATCH 08/15] scalar: implement the `clone` subcommand","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-10-07T14:09:53Z","receivedAt":"2021-10-07T14:10:25Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Wed, Oct 6, 2021 at 1:40 PM Johannes Schindelin\n<Johannes.Schindelin@gmx.de> wrote:\n>\n> Hi Elijah,\n>\n> On Mon, 27 Sep 2021, Elijah Newren wrote:\n>\n> > On Mon, Aug 30, 2021 at 2:36 PM Johannes Schindelin via GitGitGadget\n> > <gitgitgadget@gmail.com> wrote:\n> > >\n> > ...\n> > >  COMMANDS\n> > >  --------\n> > >\n> > > +Clone\n> > > +~~~~~\n> > > +\n> > > +clone [<options>] <url> [<enlistment>]::\n> > > +    Clones the specified repository, similar to linkgit:git-clone[1]. By\n> > > +    default, only commit and tree objects are cloned. Once finished, the\n> > > +    worktree is located at `<enlistment>/src`.\n> > > ++\n> > > +The sparse-checkout feature is enabled (except when run with `--full-clone`)\n> > > +and the only files present are those in the top-level directory. Use\n> > > +`git sparse-checkout set` to expand the set of directories you want to see,\n> > > +or `git sparse-checkout disable` to expand to all files (see\n> > > +linkgit:git-sparse-checkout[1] for more details). You can explore the\n> > > +subdirectories outside your sparse-checkout by using `git ls-tree HEAD`.\n> >\n> > Should this be `git ls-tree [-r] HEAD`?  Do you expect people to just\n> > add directories that are found immediately under the toplevel, rather\n> > than some that are a bit deeper?\n>\n> I fear that `git ls-tree -r HEAD` in any monorepo might be a bit too\n> overwhelming for any reader.\n\nOh, right...\n\n> But I agree that just looking at HEAD is probably not enough. Maybe we\n> should use `git ls-tree HEAD[:<dir>]`?\n\nSounds good.\n"},{"id":"439728","messageId":"pull.1005.v6.git.1635323239.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v5.git.1633604349.gitgitgadget@gmail.com","subject":"[PATCH v6 00/15] Upstreaming the Scalar command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-27T08:27:04Z","receivedAt":"2021-10-27T08:27:24Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"tl;dr: This series contributes the core part of the Scalar command to the\nGit project. This command provides an opinionated way to create and\nconfigure Git repositories with a focus on very large repositories.\n\nChanges since v5:\n\n * Fixed the commit message talking about make -C contrib/scalar/Makefile.\n * Fixed the git ls-tree invocation suggested in the manual for scalar\n   clone.\n * Invoking make -C contrib/scalar, then changing a source file of libgit.a\n   and then immediately invoking make -C contrib/scalar again will now\n   implicitly rebuild libgit.a.\n\nChanges since v4:\n\n * scalar delete now refuses to delete anything if it was started from\n   within the enlistment.\n * scalar delete releases any handles to the object store before deleting\n   the enlistment.\n * The OBJECTS list in the Makefile will now include Scalar.\n * scalar register now supports secondary worktrees, in addition to the\n   primary worktree.\n\nChanges since v3:\n\n * Moved the \"Changes since\" section to the top, to make it easier to see\n   what changed.\n * Reworded the commit message of the first patch.\n * Removed the [RFC] prefix because I did not hear any objections against\n   putting this into contrib/.\n\nChanges since v2:\n\n * Adjusted the description of the list command in the manual page , as\n   suggested by Bagas.\n * Addressed two style nits in cmd_run().\n * The documentation of git reconfigure -a was improved.\n\nChanges since v1:\n\n * A couple typos were fixed\n * The code parsing the output of ls-remote was made more readable\n * The indentation used in scalar.txt now consistently uses tabs\n * We no longer hard-code core.bare = false when registering with Scalar\n\n\nBackground\n==========\n\nYears ago, Microsoft wanted to move the source code of the Windows operating\nsystem to Git. The challenge there was to prove that Git could scale to\nmassive monorepos. The VFS for Git (formerly GVFS) project was born to take\nup that challenge.\n\nThe final solution included a virtual filesystem (with both user-mode and\nkernel components) and a customized fork of Git for Windows. This solution\ncontained several key concepts, such as only populating a portion of the\nworking directory, demand-fetching blobs, and performing periodic repo\nmaintenance in the background. However, the required kernel drivers made it\ndifficult to port the solution to other platforms.\n\nBut it was realized that many of these key concepts were independent of the\nactual VFS and its projection of the working directory. The Scalar project\nwas created to make that separation, refine the key concepts, and then\nextract those features into the new Scalar command.\n\n\nThe present\n===========\n\nThe Scalar project provides a completely functional non-virtual experience\nfor monorepos. But why stop there. The Scalar project was designed to be a\nself-destructing vehicle to allow those key concepts to be moved into core\nGit itself for the benefit of all. For example, partial clone,\nsparse-checkout, and background maintenance have already been upstreamed and\nremoved from Scalar proper. This patch series provides a C-based\nimplementation of the final remaining portions of the Scalar command. This\nwill make it easier for users to experiment with the Scalar command. It will\nalso make it substantially easier to experiment with moving functionality\nfrom Scalar into core Git, while maintaining backwards-compatibility for\nexisting Scalar users.\n\nThe C-based Scalar has been shipped to Scalar users, and can be tested by\nany interested reader: https://github.com/microsoft/git/releases/ (it offers\na Git for Windows installer, a macOS package and an Ubuntu package, Scalar\nhas been included since v2.33.0.vfs.0.0).\n\n\nNext steps\n==========\n\nI have lined up a few more patch series on top of this one:\n\n 1. Implement a scalar diagnose command.\n 2. Use the built-in FSMonitor (that patch series obviously needs to wait\n    for FSMonitor to be integrated).\n 3. Modify the config machinery to be more generous about concurrent writes,\n    say, to the user-wide config.\n 4. A few patches to optionally build and install scalar as part of a\n    regular Git install (also teaching git help scalar to find the Scalar\n    documentation\n\nThese are included in my vfs-with-scalar branch thicket\n[https://github.com/dscho/git/commits/vfs-with-scalar]. On top of that, this\nbranch thicket also includes patches I do not plan on upstreaming, mainly\nbecause they are too specific either to VFS for Git, or they support Azure\nRepos (which does not offer partial clones but speaks the GVFS protocol,\nwhich can be used to emulate partial clones).\n\nOne other thing is very interesting about that vfs-with-scalar branch\nthicket: it contains a GitHub workflow which will run Scalar's quite\nextensive Functional Tests suite. This test suite is quite comprehensive and\ncaught us a lot of bugs in the past, not only in the Scalar code, but also\ncore Git.\n\n\nEpilogue\n========\n\nNow, to address some questions that I imagine every reader has who made it\nthis far:\n\n * Why not put the Scalar functionality directly into core Git, even a\n   built-in? Creating a Git builtin requires scrutiny over every aspect of\n   the feature, which is difficult to do while also maintaining the\n   command-line interface contract and expected behavior of the Scalar\n   command (there are existing users, after all). By having the Scalar\n   command in contrib/, we present a simple option for users to have these\n   features in the short term while the Git contributor community decides\n   which bits to absorb into Git built-ins.\n * Why implement the Scalar command in the Git codebase? We ported Scalar to\n   the microsoft/git fork for several reasons. First, we realized it was\n   possible in the first place, now that the core features exist inside Git\n   itself. Second, compiling Scalar directly within a version of Git allows\n   us to remove a version compatibility check from each config option that\n   might or might not apply based on the installed Git version. Finally,\n   this new location has greatly simplified our release process and the\n   installation process for users. We now have ways to install Scalar with\n   microsoft/git via winget, brew, and apt-get. This has been the case since\n   we shipped v2.32.0 to our users, read: this setup has served us well\n   already.\n * Why contribute Scalar to the Git project? We are biased, of course, yet\n   we do have evidence that the Scalar command is a helpful tool that offers\n   an simple way to handle huge repositories with ease. By contributing it\n   to the core Git project, we are able to share it with more users,\n   especially some users who do not want to install the microsoft/git fork.\n   We intend to include Scalar as a component in git-for-windows/git, but\n   are contributing it here first. Further, we think there is benefit to the\n   Git developer community as this presents an example of how to set certain\n   defaults that work for large repositories.\n\nDerrick Stolee (4):\n  scalar: 'register' sets recommended config and starts maintenance\n  scalar: 'unregister' stops background maintenance\n  scalar: implement 'scalar list'\n  scalar: implement the `run` command\n\nJohannes Schindelin (10):\n  scalar: create a rudimentary executable\n  scalar: start documenting the command\n  scalar: create test infrastructure\n  scalar: let 'unregister' handle a deleted enlistment directory\n    gracefully\n  scalar: implement the `clone` subcommand\n  scalar: teach 'clone' to support the --single-branch option\n  scalar: allow reconfiguring an existing enlistment\n  scalar: teach 'reconfigure' to optionally handle all registered\n    enlistments\n  scalar: implement the `version` command\n  scalar: accept -C and -c options before the subcommand\n\nMatthew John Cheetham (1):\n  scalar: implement the `delete` command\n\n Makefile                         |   9 +\n contrib/scalar/.gitignore        |   5 +\n contrib/scalar/Makefile          |  57 +++\n contrib/scalar/scalar.c          | 845 +++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        | 155 ++++++\n contrib/scalar/t/Makefile        |  78 +++\n contrib/scalar/t/t9099-scalar.sh |  88 ++++\n 7 files changed, 1237 insertions(+)\n create mode 100644 contrib/scalar/.gitignore\n create mode 100644 contrib/scalar/Makefile\n create mode 100644 contrib/scalar/scalar.c\n create mode 100644 contrib/scalar/scalar.txt\n create mode 100644 contrib/scalar/t/Makefile\n create mode 100755 contrib/scalar/t/t9099-scalar.sh\n\n\nbase-commit: ebf3c04b262aa27fbb97f8a0156c2347fecafafb\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1005%2Fdscho%2Fscalar-the-beginning-v6\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1005/dscho/scalar-the-beginning-v6\nPull-Request: https://github.com/gitgitgadget/git/pull/1005\n\nRange-diff vs v5:\n\n  1:  7119a8efc21 !  1:  9b0b00438ec scalar: create a rudimentary executable\n     @@ Commit message\n      \n          The idea here is that you can (optionally) build Scalar via\n      \n     -            make -C contrib/scalar/Makefile\n     +            make -C contrib/scalar/\n      \n          This will build the `scalar` executable and put it into the\n          contrib/scalar/ subdirectory.\n     @@ contrib/scalar/Makefile (new)\n      +TARGETS = scalar$(X) scalar.o\n      +GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n      +\n     -+all: scalar$X\n     ++all: scalar$(X)\n      +\n      +$(GITLIBS):\n      +\t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)\n     @@ contrib/scalar/Makefile (new)\n      +clean:\n      +\t$(RM) $(TARGETS)\n      +\n     -+.PHONY: all clean FORCE\n     ++.PHONY: $(GITLIBS) all clean FORCE\n      \n       ## contrib/scalar/scalar.c (new) ##\n      @@\n  2:  edc1e5f73bd !  2:  40fee75968e scalar: start documenting the command\n     @@ contrib/scalar/Makefile: $(TARGETS): $(GITLIBS) scalar.c\n       \t$(RM) $(TARGETS)\n      +\t$(RM) scalar.1 scalar.html scalar.xml\n       \n     --.PHONY: all clean FORCE\n     +-.PHONY: $(GITLIBS) all clean FORCE\n      +docs: scalar.html scalar.1\n      +\n      +scalar.html: | scalar.1 # prevent them from trying to build `doc.dep` in parallel\n     @@ contrib/scalar/Makefile: $(TARGETS): $(GITLIBS) scalar.c\n      +\t\t../contrib/scalar/$@\n      +\t$(QUIET)test scalar.1 != \"$@\" || mv ../../Documentation/$@ .\n      +\n     -+.PHONY: all clean docs FORCE\n     ++.PHONY: $(GITLIBS) all clean docs FORCE\n      \n       ## contrib/scalar/scalar.txt (new) ##\n      @@\n  3:  9eeb66d5b5d !  3:  e3507c2d5f7 scalar: create test infrastructure\n     @@ contrib/scalar/Makefile: include ../../config.mak.uname\n       TARGETS = scalar$(X) scalar.o\n       GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n       \n     --all: scalar$X\n     -+all: scalar$X ../../bin-wrappers/scalar\n     +-all: scalar$(X)\n     ++all: scalar$(X) ../../bin-wrappers/scalar\n       \n       $(GITLIBS):\n       \t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)\n     @@ contrib/scalar/Makefile: scalar.html scalar.1: scalar.txt\n       \t\t../contrib/scalar/$@\n       \t$(QUIET)test scalar.1 != \"$@\" || mv ../../Documentation/$@ .\n       \n     --.PHONY: all clean docs FORCE\n     -+.PHONY: all clean docs test FORCE\n     +-.PHONY: $(GITLIBS) all clean docs FORCE\n     ++.PHONY: $(GITLIBS) all clean docs test FORCE\n      \n       ## contrib/scalar/t/Makefile (new) ##\n      @@\n  4:  8ebfa51ae15 =  4:  53224e506ea scalar: 'register' sets recommended config and starts maintenance\n  5:  51b5fc577c9 =  5:  3591e53700b scalar: 'unregister' stops background maintenance\n  6:  0041477374e =  6:  fe04ae7ec66 scalar: let 'unregister' handle a deleted enlistment directory gracefully\n  7:  12efa86e4cb =  7:  136aec439fd scalar: implement 'scalar list'\n  8:  670891d14cc !  8:  ed24a5e9b4c scalar: implement the `clone` subcommand\n     @@ contrib/scalar/scalar.txt: an existing Git worktree with Scalar whose name is no\n      +`git sparse-checkout set` to expand the set of directories you want to see,\n      +or `git sparse-checkout disable` to expand to all files (see\n      +linkgit:git-sparse-checkout[1] for more details). You can explore the\n     -+subdirectories outside your sparse-checkout by using `git ls-tree HEAD`.\n     ++subdirectories outside your sparse-checkout by using `git ls-tree\n     ++HEAD[:<directory>]`.\n      +\n      +-b <name>::\n      +--branch <name>::\n  9:  03b8385ebb5 !  9:  5ee54a9e25f scalar: teach 'clone' to support the --single-branch option\n     @@ contrib/scalar/scalar.txt: scalar - an opinionated repository management tool\n       scalar list\n       scalar register [<enlistment>]\n       scalar unregister [<enlistment>]\n     -@@ contrib/scalar/scalar.txt: subdirectories outside your sparse-checkout by using `git ls-tree HEAD`.\n     +@@ contrib/scalar/scalar.txt: HEAD[:<directory>]`.\n       \tInstead of checking out the branch pointed to by the cloned\n       \trepository's HEAD, check out the `<name>` branch instead.\n       \n 10:  5a0b3843f98 = 10:  3361eb8f091 scalar: implement the `run` command\n 11:  6d9aaeb05fa = 11:  c4778c1dc5f scalar: allow reconfiguring an existing enlistment\n 12:  2967d7f1425 = 12:  cd824e9e483 scalar: teach 'reconfigure' to optionally handle all registered enlistments\n 13:  8069cc536fe = 13:  843026d5481 scalar: implement the `delete` command\n 14:  2ecfaa5d0fe = 14:  5ca169b3f3a scalar: implement the `version` command\n 15:  f81e8b3bcf1 = 15:  e3a6eea0534 scalar: accept -C and -c options before the subcommand\n\n-- \ngitgitgadget\n"},{"id":"439729","messageId":"9b0b00438ecd53fb39d3b378c0c655c8b317959b.1635323239.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v6.git.1635323239.gitgitgadget@gmail.com","subject":"[PATCH v6 01/15] scalar: create a rudimentary executable","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-27T08:27:05Z","receivedAt":"2021-10-27T08:27:25Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe idea of Scalar (https://github.com/microsoft/scalar), and before\nthat, of VFS for Git, has always been to prove that Git _can_ scale, and\nto upstream whatever strategies have been demonstrated to help.\n\nWith this patch, we start the journey from that C# project to move what\nis left to Git's own `contrib/` directory, reimplementing it in pure C,\nwith the intention to facilitate integrating the functionality into core\nGit all while maintaining backwards-compatibility for existing Scalar\nusers (which will be much easier when both live in the same worktree).\nIt has always been the plan to contribute all of the proven strategies\nback to core Git.\n\nFor example, while the virtual filesystem provided by VFS for Git helped\nthe team developing the Windows operating system to move onto Git, while\ntrying to upstream it we realized that it cannot be done: getting the\nvirtual filesystem to work (which we only managed to implement fully on\nWindows, but not on, say, macOS or Linux), and the required server-side\nsupport for the GVFS protocol, made this not quite feasible.\n\nThe Scalar project learned from that and tackled the problem with\ndifferent tactics: instead of pretending to Git that the working\ndirectory is fully populated, it _specifically_ teaches Git about\npartial clone (which is based on VFS for Git's cache server), about\nsparse checkout (which VFS for Git tried to do transparently, in the\nfile system layer), and regularly runs maintenance tasks to keep the\nrepository in a healthy state.\n\nWith partial clone, sparse checkout and `git maintenance` having been\nupstreamed, there is little left that `scalar.exe` does which `git.exe`\ncannot do. One such thing is that `scalar clone <url>` will\nautomatically set up a partial, sparse clone, and configure\nknown-helpful settings from the start.\n\nSo let's bring this convenience into Git's tree.\n\nThe idea here is that you can (optionally) build Scalar via\n\n\tmake -C contrib/scalar/\n\nThis will build the `scalar` executable and put it into the\ncontrib/scalar/ subdirectory.\n\nThe slightly awkward addition of the `contrib/scalar/*` bits to the\ntop-level `Makefile` are actually really required: we want to link to\n`libgit.a`, which means that we will need to use the very same `CFLAGS`\nand `LDFLAGS` as the rest of Git.\n\nAn early development version of this patch tried to replicate all the\nconditional code in `contrib/scalar/Makefile` (e.g. `NO_POLL`) just like\n`contrib/svn-fe/Makefile` used to do before it was retired. It turned\nout to be quite the whack-a-mole game: the SHA-1-related flags, the\nflags enabling/disabling `compat/poll/`, `compat/regex/`,\n`compat/win32mmap.c` & friends depending on the current platform... To\nput it mildly: it was a major mess.\n\nInstead, this patch makes minimal changes to the top-level `Makefile` so\nthat the bits in `contrib/scalar/` can be compiled and linked, and\nadds a `contrib/scalar/Makefile` that uses the top-level `Makefile` in a\nmost minimal way to do the actual compiling.\n\nNote: With this commit, we only establish the infrastructure, no\nScalar functionality is implemented yet; We will do that incrementally\nover the next few commits.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Makefile                  |  9 +++++++++\n contrib/scalar/.gitignore |  2 ++\n contrib/scalar/Makefile   | 34 ++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.c   | 36 ++++++++++++++++++++++++++++++++++++\n 4 files changed, 81 insertions(+)\n create mode 100644 contrib/scalar/.gitignore\n create mode 100644 contrib/scalar/Makefile\n create mode 100644 contrib/scalar/scalar.c\n\ndiff --git a/Makefile b/Makefile\nindex c3565fc0f8f..a12cac1b68b 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2444,6 +2444,11 @@ OBJECTS += $(FUZZ_OBJS)\n ifndef NO_CURL\n \tOBJECTS += http.o http-walker.o remote-curl.o\n endif\n+\n+SCALAR_SOURCES := contrib/scalar/scalar.c\n+SCALAR_OBJECTS := $(SCALAR_SOURCES:c=o)\n+OBJECTS += $(SCALAR_OBJECTS)\n+\n .PHONY: objects\n objects: $(OBJECTS)\n \n@@ -2586,6 +2591,10 @@ $(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n \t\t$(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)\n \n+contrib/scalar/scalar$X: $(SCALAR_OBJECTS) GIT-LDFLAGS $(GITLIBS)\n+\t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \\\n+\t\t$(filter %.o,$^) $(LIBS)\n+\n $(LIB_FILE): $(LIB_OBJS)\n \t$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^\n \ndiff --git a/contrib/scalar/.gitignore b/contrib/scalar/.gitignore\nnew file mode 100644\nindex 00000000000..ff3d47e84d0\n--- /dev/null\n+++ b/contrib/scalar/.gitignore\n@@ -0,0 +1,2 @@\n+/*.exe\n+/scalar\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nnew file mode 100644\nindex 00000000000..f6f0036f0fa\n--- /dev/null\n+++ b/contrib/scalar/Makefile\n@@ -0,0 +1,34 @@\n+QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir\n+QUIET_SUBDIR1  =\n+\n+ifneq ($(findstring s,$(MAKEFLAGS)),s)\n+ifndef V\n+\tQUIET_SUBDIR0  = +@subdir=\n+\tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n+\t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n+else\n+\texport V\n+endif\n+endif\n+\n+all:\n+\n+include ../../config.mak.uname\n+-include ../../config.mak.autogen\n+-include ../../config.mak\n+\n+TARGETS = scalar$(X) scalar.o\n+GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n+\n+all: scalar$(X)\n+\n+$(GITLIBS):\n+\t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)\n+\n+$(TARGETS): $(GITLIBS) scalar.c\n+\t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)\n+\n+clean:\n+\t$(RM) $(TARGETS)\n+\n+.PHONY: $(GITLIBS) all clean FORCE\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nnew file mode 100644\nindex 00000000000..7cff29e0fcd\n--- /dev/null\n+++ b/contrib/scalar/scalar.c\n@@ -0,0 +1,36 @@\n+/*\n+ * The Scalar command-line interface.\n+ */\n+\n+#include \"cache.h\"\n+#include \"gettext.h\"\n+#include \"parse-options.h\"\n+\n+static struct {\n+\tconst char *name;\n+\tint (*fn)(int, const char **);\n+} builtins[] = {\n+\t{ NULL, NULL},\n+};\n+\n+int cmd_main(int argc, const char **argv)\n+{\n+\tstruct strbuf scalar_usage = STRBUF_INIT;\n+\tint i;\n+\n+\tif (argc > 1) {\n+\t\targv++;\n+\t\targc--;\n+\n+\t\tfor (i = 0; builtins[i].name; i++)\n+\t\t\tif (!strcmp(builtins[i].name, argv[0]))\n+\t\t\t\treturn !!builtins[i].fn(argc, argv);\n+\t}\n+\n+\tstrbuf_addstr(&scalar_usage,\n+\t\t      N_(\"scalar <command> [<options>]\\n\\nCommands:\\n\"));\n+\tfor (i = 0; builtins[i].name; i++)\n+\t\tstrbuf_addf(&scalar_usage, \"\\t%s\\n\", builtins[i].name);\n+\n+\tusage(scalar_usage.buf);\n+}\n-- \ngitgitgadget\n\n"},{"id":"439730","messageId":"40fee75968e6bb0940bfa040b917f305fbb6671f.1635323239.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v6.git.1635323239.gitgitgadget@gmail.com","subject":"[PATCH v6 02/15] scalar: start documenting the command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-27T08:27:06Z","receivedAt":"2021-10-27T08:27:27Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis commit establishes the infrastructure to build the manual page for\nthe `scalar` command.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/.gitignore |  3 +++\n contrib/scalar/Makefile   | 14 +++++++++++++-\n contrib/scalar/scalar.txt | 38 ++++++++++++++++++++++++++++++++++++++\n 3 files changed, 54 insertions(+), 1 deletion(-)\n create mode 100644 contrib/scalar/scalar.txt\n\ndiff --git a/contrib/scalar/.gitignore b/contrib/scalar/.gitignore\nindex ff3d47e84d0..00441073f59 100644\n--- a/contrib/scalar/.gitignore\n+++ b/contrib/scalar/.gitignore\n@@ -1,2 +1,5 @@\n+/*.xml\n+/*.1\n+/*.html\n /*.exe\n /scalar\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nindex f6f0036f0fa..e862d3ad8fa 100644\n--- a/contrib/scalar/Makefile\n+++ b/contrib/scalar/Makefile\n@@ -6,6 +6,7 @@ ifndef V\n \tQUIET_SUBDIR0  = +@subdir=\n \tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n \t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n+\tQUIET          = @\n else\n \texport V\n endif\n@@ -30,5 +31,16 @@ $(TARGETS): $(GITLIBS) scalar.c\n \n clean:\n \t$(RM) $(TARGETS)\n+\t$(RM) scalar.1 scalar.html scalar.xml\n \n-.PHONY: $(GITLIBS) all clean FORCE\n+docs: scalar.html scalar.1\n+\n+scalar.html: | scalar.1 # prevent them from trying to build `doc.dep` in parallel\n+\n+scalar.html scalar.1: scalar.txt\n+\t$(QUIET_SUBDIR0)../../Documentation$(QUIET_SUBDIR1) \\\n+\t\tMAN_TXT=../contrib/scalar/scalar.txt \\\n+\t\t../contrib/scalar/$@\n+\t$(QUIET)test scalar.1 != \"$@\" || mv ../../Documentation/$@ .\n+\n+.PHONY: $(GITLIBS) all clean docs FORCE\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nnew file mode 100644\nindex 00000000000..5f7131861a5\n--- /dev/null\n+++ b/contrib/scalar/scalar.txt\n@@ -0,0 +1,38 @@\n+scalar(1)\n+=========\n+\n+NAME\n+----\n+scalar - an opinionated repository management tool\n+\n+SYNOPSIS\n+--------\n+[verse]\n+scalar <command> [<options>]\n+\n+DESCRIPTION\n+-----------\n+\n+Scalar is an opinionated repository management tool. By creating new\n+repositories or registering existing repositories with Scalar, your Git\n+experience will speed up. Scalar sets advanced Git config settings,\n+maintains your repositories in the background, and helps reduce data sent\n+across the network.\n+\n+An important Scalar concept is the enlistment: this is the top-level directory\n+of the project. It usually contains the subdirectory `src/` which is a Git\n+worktree. This encourages the separation between tracked files (inside `src/`)\n+and untracked files, such as build artifacts (outside `src/`). When registering\n+an existing Git worktree with Scalar whose name is not `src`, the enlistment\n+will be identical to the worktree.\n+\n+The `scalar` command implements various subcommands, and different options\n+depending on the subcommand.\n+\n+SEE ALSO\n+--------\n+linkgit:git-maintenance[1].\n+\n+Scalar\n+---\n+Associated with the linkgit:git[1] suite\n-- \ngitgitgadget\n\n"},{"id":"439731","messageId":"e3507c2d5f74b93bfa62b5dd561eb8564f5fed34.1635323239.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v6.git.1635323239.gitgitgadget@gmail.com","subject":"[PATCH v6 03/15] scalar: create test infrastructure","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-27T08:27:07Z","receivedAt":"2021-10-27T08:27:37Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nTo test the Scalar command, create a test script in contrib/scalar/t\nthat is executed as `make -C contrib/scalar test`. Since Scalar has no\nmeaningful capabilities yet, the only test is rather simple. We will add\nmore tests in subsequent commits that introduce corresponding, new\nfunctionality.\n\nNote: this test script is intended to test `scalar` only lightly, even\nafter all of the functionality is implemented.\n\nA more comprehensive functional (or: integration) test suite can be\nfound at https://github.com/microsoft/scalar; It is used in the workflow\nhttps://github.com/microsoft/git/blob/HEAD/.github/workflows/scalar-functional-tests.yml\nin Microsoft's Git fork. This test suite performs end-to-end tests with\na real remote repository, and is run as part of the regular CI builds.\nSince those tests require some functionality supported only by\nMicrosoft's Git fork (\"GVFS protocol\"), there is no intention to port\nthat fuller test suite to `contrib/scalar/`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/Makefile          | 17 +++++--\n contrib/scalar/t/Makefile        | 78 ++++++++++++++++++++++++++++++++\n contrib/scalar/t/t9099-scalar.sh | 17 +++++++\n 3 files changed, 109 insertions(+), 3 deletions(-)\n create mode 100644 contrib/scalar/t/Makefile\n create mode 100755 contrib/scalar/t/t9099-scalar.sh\n\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nindex e862d3ad8fa..44796572ef4 100644\n--- a/contrib/scalar/Makefile\n+++ b/contrib/scalar/Makefile\n@@ -3,6 +3,7 @@ QUIET_SUBDIR1  =\n \n ifneq ($(findstring s,$(MAKEFLAGS)),s)\n ifndef V\n+\tQUIET_GEN      = @echo '   ' GEN $@;\n \tQUIET_SUBDIR0  = +@subdir=\n \tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n \t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n@@ -21,7 +22,7 @@ include ../../config.mak.uname\n TARGETS = scalar$(X) scalar.o\n GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n \n-all: scalar$(X)\n+all: scalar$(X) ../../bin-wrappers/scalar\n \n $(GITLIBS):\n \t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)\n@@ -30,9 +31,19 @@ $(TARGETS): $(GITLIBS) scalar.c\n \t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)\n \n clean:\n-\t$(RM) $(TARGETS)\n+\t$(RM) $(TARGETS) ../../bin-wrappers/scalar\n \t$(RM) scalar.1 scalar.html scalar.xml\n \n+../../bin-wrappers/scalar: ../../wrap-for-bin.sh Makefile\n+\t@mkdir -p ../../bin-wrappers\n+\t$(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \\\n+\t     -e 's|@@BUILD_DIR@@|$(shell cd ../.. && pwd)|' \\\n+\t     -e 's|@@PROG@@|contrib/scalar/scalar$(X)|' < $< > $@ && \\\n+\tchmod +x $@\n+\n+test: all\n+\t$(MAKE) -C t\n+\n docs: scalar.html scalar.1\n \n scalar.html: | scalar.1 # prevent them from trying to build `doc.dep` in parallel\n@@ -43,4 +54,4 @@ scalar.html scalar.1: scalar.txt\n \t\t../contrib/scalar/$@\n \t$(QUIET)test scalar.1 != \"$@\" || mv ../../Documentation/$@ .\n \n-.PHONY: $(GITLIBS) all clean docs FORCE\n+.PHONY: $(GITLIBS) all clean docs test FORCE\ndiff --git a/contrib/scalar/t/Makefile b/contrib/scalar/t/Makefile\nnew file mode 100644\nindex 00000000000..6170672bb37\n--- /dev/null\n+++ b/contrib/scalar/t/Makefile\n@@ -0,0 +1,78 @@\n+# Run scalar tests\n+#\n+# Copyright (c) 2005,2021 Junio C Hamano, Johannes Schindelin\n+#\n+\n+-include ../../../config.mak.autogen\n+-include ../../../config.mak\n+\n+SHELL_PATH ?= $(SHELL)\n+PERL_PATH ?= /usr/bin/perl\n+RM ?= rm -f\n+PROVE ?= prove\n+DEFAULT_TEST_TARGET ?= test\n+TEST_LINT ?= test-lint\n+\n+ifdef TEST_OUTPUT_DIRECTORY\n+TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results\n+else\n+TEST_RESULTS_DIRECTORY = ../../../t/test-results\n+endif\n+\n+# Shell quote;\n+SHELL_PATH_SQ = $(subst ','\\'',$(SHELL_PATH))\n+PERL_PATH_SQ = $(subst ','\\'',$(PERL_PATH))\n+TEST_RESULTS_DIRECTORY_SQ = $(subst ','\\'',$(TEST_RESULTS_DIRECTORY))\n+\n+T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))\n+\n+all: $(DEFAULT_TEST_TARGET)\n+\n+test: $(TEST_LINT)\n+\t$(MAKE) aggregate-results-and-cleanup\n+\n+prove: $(TEST_LINT)\n+\t@echo \"*** prove ***\"; GIT_CONFIG=.git/config $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)\n+\t$(MAKE) clean-except-prove-cache\n+\n+$(T):\n+\t@echo \"*** $@ ***\"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)\n+\n+clean-except-prove-cache:\n+\t$(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)'\n+\t$(RM) -r valgrind/bin\n+\n+clean: clean-except-prove-cache\n+\t$(RM) .prove\n+\n+test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax\n+\n+test-lint-duplicates:\n+\t@dups=`echo $(T) | tr ' ' '\\n' | sed 's/-.*//' | sort | uniq -d` && \\\n+\t\ttest -z \"$$dups\" || { \\\n+\t\techo >&2 \"duplicate test numbers:\" $$dups; exit 1; }\n+\n+test-lint-executable:\n+\t@bad=`for i in $(T); do test -x \"$$i\" || echo $$i; done` && \\\n+\t\ttest -z \"$$bad\" || { \\\n+\t\techo >&2 \"non-executable tests:\" $$bad; exit 1; }\n+\n+test-lint-shell-syntax:\n+\t@'$(PERL_PATH_SQ)' ../../../t/check-non-portable-shell.pl $(T)\n+\n+aggregate-results-and-cleanup: $(T)\n+\t$(MAKE) aggregate-results\n+\t$(MAKE) clean\n+\n+aggregate-results:\n+\tfor f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \\\n+\t\techo \"$$f\"; \\\n+\tdone | '$(SHELL_PATH_SQ)' ../../../t/aggregate-results.sh\n+\n+valgrind:\n+\t$(MAKE) GIT_TEST_OPTS=\"$(GIT_TEST_OPTS) --valgrind\"\n+\n+test-results:\n+\tmkdir -p test-results\n+\n+.PHONY: $(T) aggregate-results clean valgrind\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nnew file mode 100755\nindex 00000000000..16f2b72b126\n--- /dev/null\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -0,0 +1,17 @@\n+#!/bin/sh\n+\n+test_description='test the `scalar` command'\n+\n+TEST_DIRECTORY=$PWD/../../../t\n+export TEST_DIRECTORY\n+\n+# Make it work with --no-bin-wrappers\n+PATH=$PWD/..:$PATH\n+\n+. ../../../t/test-lib.sh\n+\n+test_expect_success 'scalar shows a usage' '\n+\ttest_expect_code 129 scalar -h\n+'\n+\n+test_done\n-- \ngitgitgadget\n\n"},{"id":"439732","messageId":"53224e506eaaf45d3385e32f0687f6d1cce5d27e.1635323239.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v6.git.1635323239.gitgitgadget@gmail.com","subject":"[PATCH v6 04/15] scalar: 'register' sets recommended config and starts maintenance","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-27T08:27:08Z","receivedAt":"2021-10-27T08:27:37Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nLet's start implementing the `register` command. With this commit,\nrecommended settings are configured upon `scalar register`, and Git's\nbackground maintenance is started.\n\nThe recommended config settings may very well change in the future. For\nexample, once the built-in FSMonitor is available, we will want to\nenable it upon `scalar register`. For that reason, we explicitly support\nrunning `scalar register` in an already-registered enlistment.\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 248 ++++++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt |  18 ++-\n 2 files changed, 265 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 7cff29e0fcd..38721d671ba 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -5,11 +5,259 @@\n #include \"cache.h\"\n #include \"gettext.h\"\n #include \"parse-options.h\"\n+#include \"config.h\"\n+#include \"run-command.h\"\n+\n+/*\n+ * Remove the deepest subdirectory in the provided path string. Path must not\n+ * include a trailing path separator. Returns 1 if parent directory found,\n+ * otherwise 0.\n+ */\n+static int strbuf_parent_directory(struct strbuf *buf)\n+{\n+\tsize_t len = buf->len;\n+\tsize_t offset = offset_1st_component(buf->buf);\n+\tchar *path_sep = find_last_dir_sep(buf->buf + offset);\n+\tstrbuf_setlen(buf, path_sep ? path_sep - buf->buf : offset);\n+\n+\treturn buf->len < len;\n+}\n+\n+static void setup_enlistment_directory(int argc, const char **argv,\n+\t\t\t\t       const char * const *usagestr,\n+\t\t\t\t       const struct option *options,\n+\t\t\t\t       struct strbuf *enlistment_root)\n+{\n+\tstruct strbuf path = STRBUF_INIT;\n+\tchar *root;\n+\tint enlistment_found = 0;\n+\n+\tif (startup_info->have_repository)\n+\t\tBUG(\"gitdir already set up?!?\");\n+\n+\tif (argc > 1)\n+\t\tusage_with_options(usagestr, options);\n+\n+\t/* find the worktree, determine its corresponding root */\n+\tif (argc == 1)\n+\t\tstrbuf_add_absolute_path(&path, argv[0]);\n+\telse if (strbuf_getcwd(&path) < 0)\n+\t\tdie(_(\"need a working directory\"));\n+\n+\tstrbuf_trim_trailing_dir_sep(&path);\n+\tdo {\n+\t\tconst size_t len = path.len;\n+\n+\t\t/* check if currently in enlistment root with src/ workdir */\n+\t\tstrbuf_addstr(&path, \"/src\");\n+\t\tif (is_nonbare_repository_dir(&path)) {\n+\t\t\tif (enlistment_root)\n+\t\t\t\tstrbuf_add(enlistment_root, path.buf, len);\n+\n+\t\t\tenlistment_found = 1;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\t/* reset to original path */\n+\t\tstrbuf_setlen(&path, len);\n+\n+\t\t/* check if currently in workdir */\n+\t\tif (is_nonbare_repository_dir(&path)) {\n+\t\t\tif (enlistment_root) {\n+\t\t\t\t/*\n+\t\t\t\t * If the worktree's directory's name is `src`, the enlistment is the\n+\t\t\t\t * parent directory, otherwise it is identical to the worktree.\n+\t\t\t\t */\n+\t\t\t\troot = strip_path_suffix(path.buf, \"src\");\n+\t\t\t\tstrbuf_addstr(enlistment_root, root ? root : path.buf);\n+\t\t\t\tfree(root);\n+\t\t\t}\n+\n+\t\t\tenlistment_found = 1;\n+\t\t\tbreak;\n+\t\t}\n+\t} while (strbuf_parent_directory(&path));\n+\n+\tif (!enlistment_found)\n+\t\tdie(_(\"could not find enlistment root\"));\n+\n+\tif (chdir(path.buf) < 0)\n+\t\tdie_errno(_(\"could not switch to '%s'\"), path.buf);\n+\n+\tstrbuf_release(&path);\n+\tsetup_git_directory();\n+}\n+\n+static int run_git(const char *arg, ...)\n+{\n+\tstruct strvec argv = STRVEC_INIT;\n+\tva_list args;\n+\tconst char *p;\n+\tint res;\n+\n+\tva_start(args, arg);\n+\tstrvec_push(&argv, arg);\n+\twhile ((p = va_arg(args, const char *)))\n+\t\tstrvec_push(&argv, p);\n+\tva_end(args);\n+\n+\tres = run_command_v_opt(argv.v, RUN_GIT_CMD);\n+\n+\tstrvec_clear(&argv);\n+\treturn res;\n+}\n+\n+static int set_recommended_config(void)\n+{\n+\tstruct {\n+\t\tconst char *key;\n+\t\tconst char *value;\n+\t} config[] = {\n+\t\t{ \"am.keepCR\", \"true\" },\n+\t\t{ \"core.FSCache\", \"true\" },\n+\t\t{ \"core.multiPackIndex\", \"true\" },\n+\t\t{ \"core.preloadIndex\", \"true\" },\n+#ifndef WIN32\n+\t\t{ \"core.untrackedCache\", \"true\" },\n+#else\n+\t\t/*\n+\t\t * Unfortunately, Scalar's Functional Tests demonstrated\n+\t\t * that the untracked cache feature is unreliable on Windows\n+\t\t * (which is a bummer because that platform would benefit the\n+\t\t * most from it). For some reason, freshly created files seem\n+\t\t * not to update the directory's `lastModified` time\n+\t\t * immediately, but the untracked cache would need to rely on\n+\t\t * that.\n+\t\t *\n+\t\t * Therefore, with a sad heart, we disable this very useful\n+\t\t * feature on Windows.\n+\t\t */\n+\t\t{ \"core.untrackedCache\", \"false\" },\n+#endif\n+\t\t{ \"core.logAllRefUpdates\", \"true\" },\n+\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n+\t\t{ \"credential.validate\", \"false\" }, /* GCM4W-only */\n+\t\t{ \"gc.auto\", \"0\" },\n+\t\t{ \"gui.GCWarning\", \"false\" },\n+\t\t{ \"index.threads\", \"true\" },\n+\t\t{ \"index.version\", \"4\" },\n+\t\t{ \"merge.stat\", \"false\" },\n+\t\t{ \"merge.renames\", \"false\" },\n+\t\t{ \"pack.useBitmaps\", \"false\" },\n+\t\t{ \"pack.useSparse\", \"true\" },\n+\t\t{ \"receive.autoGC\", \"false\" },\n+\t\t{ \"reset.quiet\", \"true\" },\n+\t\t{ \"feature.manyFiles\", \"false\" },\n+\t\t{ \"feature.experimental\", \"false\" },\n+\t\t{ \"fetch.unpackLimit\", \"1\" },\n+\t\t{ \"fetch.writeCommitGraph\", \"false\" },\n+#ifdef WIN32\n+\t\t{ \"http.sslBackend\", \"schannel\" },\n+#endif\n+\t\t{ \"status.aheadBehind\", \"false\" },\n+\t\t{ \"commitGraph.generationVersion\", \"1\" },\n+\t\t{ \"core.autoCRLF\", \"false\" },\n+\t\t{ \"core.safeCRLF\", \"false\" },\n+\t\t{ NULL, NULL },\n+\t};\n+\tint i;\n+\tchar *value;\n+\n+\tfor (i = 0; config[i].key; i++) {\n+\t\tif (git_config_get_string(config[i].key, &value)) {\n+\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"created\");\n+\t\t\tif (git_config_set_gently(config[i].key,\n+\t\t\t\t\t\t  config[i].value) < 0)\n+\t\t\t\treturn error(_(\"could not configure %s=%s\"),\n+\t\t\t\t\t     config[i].key, config[i].value);\n+\t\t} else {\n+\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"exists\");\n+\t\t\tfree(value);\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * The `log.excludeDecoration` setting is special because it allows\n+\t * for multiple values.\n+\t */\n+\tif (git_config_get_string(\"log.excludeDecoration\", &value)) {\n+\t\ttrace2_data_string(\"scalar\", the_repository,\n+\t\t\t\t   \"log.excludeDecoration\", \"created\");\n+\t\tif (git_config_set_multivar_gently(\"log.excludeDecoration\",\n+\t\t\t\t\t\t   \"refs/prefetch/*\",\n+\t\t\t\t\t\t   CONFIG_REGEX_NONE, 0))\n+\t\t\treturn error(_(\"could not configure \"\n+\t\t\t\t       \"log.excludeDecoration\"));\n+\t} else {\n+\t\ttrace2_data_string(\"scalar\", the_repository,\n+\t\t\t\t   \"log.excludeDecoration\", \"exists\");\n+\t\tfree(value);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int start_maintenance(void)\n+{\n+\treturn run_git(\"maintenance\", \"start\", NULL);\n+}\n+\n+static int add_enlistment(void)\n+{\n+\tint res;\n+\n+\tif (!the_repository->worktree)\n+\t\tdie(_(\"Scalar enlistments require a worktree\"));\n+\n+\tres = run_git(\"config\", \"--global\", \"--get\", \"--fixed-value\",\n+\t\t      \"scalar.repo\", the_repository->worktree, NULL);\n+\n+\t/*\n+\t * If the setting is already there, then do nothing.\n+\t */\n+\tif (!res)\n+\t\treturn 0;\n+\n+\treturn run_git(\"config\", \"--global\", \"--add\",\n+\t\t       \"scalar.repo\", the_repository->worktree, NULL);\n+}\n+\n+static int register_dir(void)\n+{\n+\tint res = add_enlistment();\n+\n+\tif (!res)\n+\t\tres = set_recommended_config();\n+\n+\tif (!res)\n+\t\tres = start_maintenance();\n+\n+\treturn res;\n+}\n+\n+static int cmd_register(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar register [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn register_dir();\n+}\n \n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"register\", cmd_register },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 5f7131861a5..568987064b2 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,7 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n-scalar <command> [<options>]\n+scalar register [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -29,6 +29,22 @@ will be identical to the worktree.\n The `scalar` command implements various subcommands, and different options\n depending on the subcommand.\n \n+COMMANDS\n+--------\n+\n+Register\n+~~~~~~~~\n+\n+register [<enlistment>]::\n+\tAdds the enlistment's repository to the list of registered repositories\n+\tand starts background maintenance. If `<enlistment>` is not provided,\n+\tthen the enlistment associated with the current working directory is\n+\tregistered.\n++\n+Note: when this subcommand is called in a worktree that is called `src/`, its\n+parent directory is considered to be the Scalar enlistment. If the worktree is\n+_not_ called `src/`, it itself will be considered to be the Scalar enlistment.\n+\n SEE ALSO\n --------\n linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"439733","messageId":"3591e53700bbf9e193af4ad8c14793fcc377292e.1635323239.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v6.git.1635323239.gitgitgadget@gmail.com","subject":"[PATCH v6 05/15] scalar: 'unregister' stops background maintenance","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-27T08:27:09Z","receivedAt":"2021-10-27T08:27:39Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nJust like `scalar register` starts the scheduled background maintenance,\n`scalar unregister` stops it. Note that we use `git maintenance start`\nin `scalar register`, but we do not use `git maintenance stop` in\n`scalar unregister`: this would stop maintenance for _all_ repositories,\nnot just for the one we want to unregister.\n\nThe `unregister` command also removes the corresponding entry from the\n`[scalar]` section in the global Git config.\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 50 ++++++++++++++++++++++++++++++++-------\n contrib/scalar/scalar.txt |  8 +++++++\n 2 files changed, 50 insertions(+), 8 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 38721d671ba..fc55404230d 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -197,12 +197,12 @@ static int set_recommended_config(void)\n \treturn 0;\n }\n \n-static int start_maintenance(void)\n+static int toggle_maintenance(int enable)\n {\n-\treturn run_git(\"maintenance\", \"start\", NULL);\n+\treturn run_git(\"maintenance\", enable ? \"start\" : \"unregister\", NULL);\n }\n \n-static int add_enlistment(void)\n+static int add_or_remove_enlistment(int add)\n {\n \tint res;\n \n@@ -213,24 +213,39 @@ static int add_enlistment(void)\n \t\t      \"scalar.repo\", the_repository->worktree, NULL);\n \n \t/*\n-\t * If the setting is already there, then do nothing.\n+\t * If we want to add and the setting is already there, then do nothing.\n+\t * If we want to remove and the setting is not there, then do nothing.\n \t */\n-\tif (!res)\n+\tif ((add && !res) || (!add && res))\n \t\treturn 0;\n \n-\treturn run_git(\"config\", \"--global\", \"--add\",\n+\treturn run_git(\"config\", \"--global\", add ? \"--add\" : \"--unset\",\n+\t\t       add ? \"--no-fixed-value\" : \"--fixed-value\",\n \t\t       \"scalar.repo\", the_repository->worktree, NULL);\n }\n \n static int register_dir(void)\n {\n-\tint res = add_enlistment();\n+\tint res = add_or_remove_enlistment(1);\n \n \tif (!res)\n \t\tres = set_recommended_config();\n \n \tif (!res)\n-\t\tres = start_maintenance();\n+\t\tres = toggle_maintenance(1);\n+\n+\treturn res;\n+}\n+\n+static int unregister_dir(void)\n+{\n+\tint res = 0;\n+\n+\tif (toggle_maintenance(0) < 0)\n+\t\tres = -1;\n+\n+\tif (add_or_remove_enlistment(0) < 0)\n+\t\tres = -1;\n \n \treturn res;\n }\n@@ -253,11 +268,30 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_unregister(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar unregister [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn unregister_dir();\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n \t{ \"register\", cmd_register },\n+\t{ \"unregister\", cmd_unregister },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 568987064b2..d9a79984492 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -9,6 +9,7 @@ SYNOPSIS\n --------\n [verse]\n scalar register [<enlistment>]\n+scalar unregister [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -45,6 +46,13 @@ Note: when this subcommand is called in a worktree that is called `src/`, its\n parent directory is considered to be the Scalar enlistment. If the worktree is\n _not_ called `src/`, it itself will be considered to be the Scalar enlistment.\n \n+Unregister\n+~~~~~~~~~~\n+\n+unregister [<enlistment>]::\n+\tRemove the specified repository from the list of repositories\n+\tregistered with Scalar and stop the scheduled background maintenance.\n+\n SEE ALSO\n --------\n linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"439734","messageId":"fe04ae7ec66e66c50f63e7e4ab836f9856c76683.1635323239.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v6.git.1635323239.gitgitgadget@gmail.com","subject":"[PATCH v6 06/15] scalar: let 'unregister' handle a deleted enlistment directory gracefully","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-27T08:27:10Z","receivedAt":"2021-10-27T08:27:39Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nWhen a user deleted an enlistment manually, let's be generous and\n_still_ unregister it.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 46 ++++++++++++++++++++++++++++++++\n contrib/scalar/t/t9099-scalar.sh | 15 +++++++++++\n 2 files changed, 61 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex fc55404230d..dc84ce0d5b2 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -268,6 +268,24 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int remove_deleted_enlistment(struct strbuf *path)\n+{\n+\tint res = 0;\n+\tstrbuf_realpath_forgiving(path, path->buf, 1);\n+\n+\tif (run_git(\"config\", \"--global\",\n+\t\t    \"--unset\", \"--fixed-value\",\n+\t\t    \"scalar.repo\", path->buf, NULL) < 0)\n+\t\tres = -1;\n+\n+\tif (run_git(\"config\", \"--global\",\n+\t\t    \"--unset\", \"--fixed-value\",\n+\t\t    \"maintenance.repo\", path->buf, NULL) < 0)\n+\t\tres = -1;\n+\n+\treturn res;\n+}\n+\n static int cmd_unregister(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -281,6 +299,34 @@ static int cmd_unregister(int argc, const char **argv)\n \targc = parse_options(argc, argv, NULL, options,\n \t\t\t     usage, 0);\n \n+\t/*\n+\t * Be forgiving when the enlistment or worktree does not even exist any\n+\t * longer; This can be the case if a user deleted the worktree by\n+\t * mistake and _still_ wants to unregister the thing.\n+\t */\n+\tif (argc == 1) {\n+\t\tstruct strbuf src_path = STRBUF_INIT, workdir_path = STRBUF_INIT;\n+\n+\t\tstrbuf_addf(&src_path, \"%s/src/.git\", argv[0]);\n+\t\tstrbuf_addf(&workdir_path, \"%s/.git\", argv[0]);\n+\t\tif (!is_directory(src_path.buf) && !is_directory(workdir_path.buf)) {\n+\t\t\t/* remove possible matching registrations */\n+\t\t\tint res = -1;\n+\n+\t\t\tstrbuf_strip_suffix(&src_path, \"/.git\");\n+\t\t\tres = remove_deleted_enlistment(&src_path) && res;\n+\n+\t\t\tstrbuf_strip_suffix(&workdir_path, \"/.git\");\n+\t\t\tres = remove_deleted_enlistment(&workdir_path) && res;\n+\n+\t\t\tstrbuf_release(&src_path);\n+\t\t\tstrbuf_release(&workdir_path);\n+\t\t\treturn res;\n+\t\t}\n+\t\tstrbuf_release(&src_path);\n+\t\tstrbuf_release(&workdir_path);\n+\t}\n+\n \tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n \n \treturn unregister_dir();\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 16f2b72b126..ef0e8d680d5 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -14,4 +14,19 @@ test_expect_success 'scalar shows a usage' '\n \ttest_expect_code 129 scalar -h\n '\n \n+test_expect_success 'scalar unregister' '\n+\tgit init vanish/src &&\n+\tscalar register vanish/src &&\n+\tgit config --get --global --fixed-value \\\n+\t\tmaintenance.repo \"$(pwd)/vanish/src\" &&\n+\tscalar list >scalar.repos &&\n+\tgrep -F \"$(pwd)/vanish/src\" scalar.repos &&\n+\trm -rf vanish/src/.git &&\n+\tscalar unregister vanish &&\n+\ttest_must_fail git config --get --global --fixed-value \\\n+\t\tmaintenance.repo \"$(pwd)/vanish/src\" &&\n+\tscalar list >scalar.repos &&\n+\t! grep -F \"$(pwd)/vanish/src\" scalar.repos\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"439735","messageId":"136aec439fd7e1c807088b57101df54bf6beb34f.1635323239.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v6.git.1635323239.gitgitgadget@gmail.com","subject":"[PATCH v6 07/15] scalar: implement 'scalar list'","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-27T08:27:11Z","receivedAt":"2021-10-27T08:27:40Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe produced list simply consists of those repositories registered under\nthe multi-valued `scalar.repo` config setting in the user's Git config.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 11 +++++++++++\n contrib/scalar/scalar.txt | 11 ++++++++++-\n 2 files changed, 21 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex dc84ce0d5b2..d13eb951c3d 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -250,6 +250,16 @@ static int unregister_dir(void)\n \treturn res;\n }\n \n+static int cmd_list(int argc, const char **argv)\n+{\n+\tif (argc != 1)\n+\t\tdie(_(\"`scalar list` does not take arguments\"));\n+\n+\tif (run_git(\"config\", \"--global\", \"--get-all\", \"scalar.repo\", NULL) < 0)\n+\t\treturn -1;\n+\treturn 0;\n+}\n+\n static int cmd_register(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -336,6 +346,7 @@ static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n \t{ NULL, NULL},\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex d9a79984492..f93e3d00efd 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,6 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n+scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n \n@@ -28,11 +29,19 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand.\n+depending on the subcommand. With the exception of `list`, all subcommands\n+expect to be run in an enlistment.\n \n COMMANDS\n --------\n \n+List\n+~~~~\n+\n+list::\n+\tList enlistments that are currently registered by Scalar. This\n+\tsubcommand does not need to be run inside an enlistment.\n+\n Register\n ~~~~~~~~\n \n-- \ngitgitgadget\n\n"},{"id":"439737","messageId":"ed24a5e9b4c10ccdf6553c9698e2586a8abcc365.1635323239.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v6.git.1635323239.gitgitgadget@gmail.com","subject":"[PATCH v6 08/15] scalar: implement the `clone` subcommand","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-27T08:27:12Z","receivedAt":"2021-10-27T08:27:45Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis implements Scalar's opinionated `clone` command: it tries to use a\npartial clone and sets up a sparse checkout by default. In contrast to\n`git clone`, `scalar clone` sets up the worktree in the `src/`\nsubdirectory, to encourage a separation between the source files and the\nbuild output (which helps Git tremendously because it avoids untracked\nfiles that have to be specifically ignored when refreshing the index).\n\nAlso, it registers the repository for regular, scheduled maintenance,\nand configures a flurry of configuration settings based on the\nexperience and experiments of the Microsoft Windows and the Microsoft\nOffice development teams.\n\nNote: since the `scalar clone` command is by far the most commonly\ncalled `scalar` subcommand, we document it at the top of the manual\npage.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 201 +++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        |  32 ++++-\n contrib/scalar/t/t9099-scalar.sh |  32 +++++\n 3 files changed, 262 insertions(+), 3 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex d13eb951c3d..0401462b9b3 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -7,6 +7,7 @@\n #include \"parse-options.h\"\n #include \"config.h\"\n #include \"run-command.h\"\n+#include \"refs.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -250,6 +251,205 @@ static int unregister_dir(void)\n \treturn res;\n }\n \n+/* printf-style interface, expects `<key>=<value>` argument */\n+static int set_config(const char *fmt, ...)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tchar *value;\n+\tint res;\n+\tva_list args;\n+\n+\tva_start(args, fmt);\n+\tstrbuf_vaddf(&buf, fmt, args);\n+\tva_end(args);\n+\n+\tvalue = strchr(buf.buf, '=');\n+\tif (value)\n+\t\t*(value++) = '\\0';\n+\tres = git_config_set_gently(buf.buf, value);\n+\tstrbuf_release(&buf);\n+\n+\treturn res;\n+}\n+\n+static char *remote_default_branch(const char *url)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tstruct strbuf out = STRBUF_INIT;\n+\n+\tcp.git_cmd = 1;\n+\tstrvec_pushl(&cp.args, \"ls-remote\", \"--symref\", url, \"HEAD\", NULL);\n+\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n+\t\tconst char *line = out.buf;\n+\n+\t\twhile (*line) {\n+\t\t\tconst char *eol = strchrnul(line, '\\n'), *p;\n+\t\t\tsize_t len = eol - line;\n+\t\t\tchar *branch;\n+\n+\t\t\tif (!skip_prefix(line, \"ref: \", &p) ||\n+\t\t\t    !strip_suffix_mem(line, &len, \"\\tHEAD\")) {\n+\t\t\t\tline = eol + (*eol == '\\n');\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\teol = line + len;\n+\t\t\tif (skip_prefix(p, \"refs/heads/\", &p)) {\n+\t\t\t\tbranch = xstrndup(p, eol - p);\n+\t\t\t\tstrbuf_release(&out);\n+\t\t\t\treturn branch;\n+\t\t\t}\n+\n+\t\t\terror(_(\"remote HEAD is not a branch: '%.*s'\"),\n+\t\t\t      (int)(eol - p), p);\n+\t\t\tstrbuf_release(&out);\n+\t\t\treturn NULL;\n+\t\t}\n+\t}\n+\twarning(_(\"failed to get default branch name from remote; \"\n+\t\t  \"using local default\"));\n+\tstrbuf_reset(&out);\n+\n+\tchild_process_init(&cp);\n+\tcp.git_cmd = 1;\n+\tstrvec_pushl(&cp.args, \"symbolic-ref\", \"--short\", \"HEAD\", NULL);\n+\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n+\t\tstrbuf_trim(&out);\n+\t\treturn strbuf_detach(&out, NULL);\n+\t}\n+\n+\tstrbuf_release(&out);\n+\terror(_(\"failed to get default branch name\"));\n+\treturn NULL;\n+}\n+\n+static int cmd_clone(int argc, const char **argv)\n+{\n+\tconst char *branch = NULL;\n+\tint full_clone = 0;\n+\tstruct option clone_options[] = {\n+\t\tOPT_STRING('b', \"branch\", &branch, N_(\"<branch>\"),\n+\t\t\t   N_(\"branch to checkout after clone\")),\n+\t\tOPT_BOOL(0, \"full-clone\", &full_clone,\n+\t\t\t N_(\"when cloning, create full working directory\")),\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const clone_usage[] = {\n+\t\tN_(\"scalar clone [<options>] [--] <repo> [<dir>]\"),\n+\t\tNULL\n+\t};\n+\tconst char *url;\n+\tchar *enlistment = NULL, *dir = NULL;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint res;\n+\n+\targc = parse_options(argc, argv, NULL, clone_options, clone_usage, 0);\n+\n+\tif (argc == 2) {\n+\t\turl = argv[0];\n+\t\tenlistment = xstrdup(argv[1]);\n+\t} else if (argc == 1) {\n+\t\turl = argv[0];\n+\n+\t\tstrbuf_addstr(&buf, url);\n+\t\t/* Strip trailing slashes, if any */\n+\t\twhile (buf.len > 0 && is_dir_sep(buf.buf[buf.len - 1]))\n+\t\t\tstrbuf_setlen(&buf, buf.len - 1);\n+\t\t/* Strip suffix `.git`, if any */\n+\t\tstrbuf_strip_suffix(&buf, \".git\");\n+\n+\t\tenlistment = find_last_dir_sep(buf.buf);\n+\t\tif (!enlistment) {\n+\t\t\tdie(_(\"cannot deduce worktree name from '%s'\"), url);\n+\t\t}\n+\t\tenlistment = xstrdup(enlistment + 1);\n+\t} else {\n+\t\tusage_msg_opt(_(\"You must specify a repository to clone.\"),\n+\t\t\t      clone_usage, clone_options);\n+\t}\n+\n+\tif (is_directory(enlistment))\n+\t\tdie(_(\"directory '%s' exists already\"), enlistment);\n+\n+\tdir = xstrfmt(\"%s/src\", enlistment);\n+\n+\tstrbuf_reset(&buf);\n+\tif (branch)\n+\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", branch);\n+\telse {\n+\t\tchar *b = repo_default_branch_name(the_repository, 1);\n+\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", b);\n+\t\tfree(b);\n+\t}\n+\n+\tif ((res = run_git(\"-c\", buf.buf, \"init\", \"--\", dir, NULL)))\n+\t\tgoto cleanup;\n+\n+\tif (chdir(dir) < 0) {\n+\t\tres = error_errno(_(\"could not switch to '%s'\"), dir);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tsetup_git_directory();\n+\n+\t/* common-main already logs `argv` */\n+\ttrace2_def_repo(the_repository);\n+\n+\tif (!branch && !(branch = remote_default_branch(url))) {\n+\t\tres = error(_(\"failed to get default branch for '%s'\"), url);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tif (set_config(\"remote.origin.url=%s\", url) ||\n+\t    set_config(\"remote.origin.fetch=\"\n+\t\t       \"+refs/heads/*:refs/remotes/origin/*\") ||\n+\t    set_config(\"remote.origin.promisor=true\") ||\n+\t    set_config(\"remote.origin.partialCloneFilter=blob:none\")) {\n+\t\tres = error(_(\"could not configure remote in '%s'\"), dir);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tif (!full_clone &&\n+\t    (res = run_git(\"sparse-checkout\", \"init\", \"--cone\", NULL)))\n+\t\tgoto cleanup;\n+\n+\tif (set_recommended_config())\n+\t\treturn error(_(\"could not configure '%s'\"), dir);\n+\n+\tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL))) {\n+\t\twarning(_(\"partial clone failed; attempting full clone\"));\n+\n+\t\tif (set_config(\"remote.origin.promisor\") ||\n+\t\t    set_config(\"remote.origin.partialCloneFilter\")) {\n+\t\t\tres = error(_(\"could not configure for full clone\"));\n+\t\t\tgoto cleanup;\n+\t\t}\n+\n+\t\tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL)))\n+\t\t\tgoto cleanup;\n+\t}\n+\n+\tif ((res = set_config(\"branch.%s.remote=origin\", branch)))\n+\t\tgoto cleanup;\n+\tif ((res = set_config(\"branch.%s.merge=refs/heads/%s\",\n+\t\t\t      branch, branch)))\n+\t\tgoto cleanup;\n+\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addf(&buf, \"origin/%s\", branch);\n+\tres = run_git(\"checkout\", \"-f\", \"-t\", buf.buf, NULL);\n+\tif (res)\n+\t\tgoto cleanup;\n+\n+\tres = register_dir();\n+\n+cleanup:\n+\tfree(enlistment);\n+\tfree(dir);\n+\tstrbuf_release(&buf);\n+\treturn res;\n+}\n+\n static int cmd_list(int argc, const char **argv)\n {\n \tif (argc != 1)\n@@ -346,6 +546,7 @@ static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"clone\", cmd_clone },\n \t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex f93e3d00efd..e8730967f16 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,6 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n+scalar clone [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n@@ -29,12 +30,37 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand. With the exception of `list`, all subcommands\n-expect to be run in an enlistment.\n+depending on the subcommand. With the exception of `clone` and `list`, all\n+subcommands expect to be run in an enlistment.\n \n COMMANDS\n --------\n \n+Clone\n+~~~~~\n+\n+clone [<options>] <url> [<enlistment>]::\n+\tClones the specified repository, similar to linkgit:git-clone[1]. By\n+\tdefault, only commit and tree objects are cloned. Once finished, the\n+\tworktree is located at `<enlistment>/src`.\n++\n+The sparse-checkout feature is enabled (except when run with `--full-clone`)\n+and the only files present are those in the top-level directory. Use\n+`git sparse-checkout set` to expand the set of directories you want to see,\n+or `git sparse-checkout disable` to expand to all files (see\n+linkgit:git-sparse-checkout[1] for more details). You can explore the\n+subdirectories outside your sparse-checkout by using `git ls-tree\n+HEAD[:<directory>]`.\n+\n+-b <name>::\n+--branch <name>::\n+\tInstead of checking out the branch pointed to by the cloned\n+\trepository's HEAD, check out the `<name>` branch instead.\n+\n+--[no-]full-clone::\n+\tA sparse-checkout is initialized by default. This behavior can be\n+\tturned off via `--full-clone`.\n+\n List\n ~~~~\n \n@@ -64,7 +90,7 @@ unregister [<enlistment>]::\n \n SEE ALSO\n --------\n-linkgit:git-maintenance[1].\n+linkgit:git-clone[1], linkgit:git-maintenance[1].\n \n Scalar\n ---\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex ef0e8d680d5..295398f62cc 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -10,6 +10,9 @@ PATH=$PWD/..:$PATH\n \n . ../../../t/test-lib.sh\n \n+GIT_TEST_MAINT_SCHEDULER=\"crontab:test-tool crontab ../cron.txt\"\n+export GIT_TEST_MAINT_SCHEDULER\n+\n test_expect_success 'scalar shows a usage' '\n \ttest_expect_code 129 scalar -h\n '\n@@ -29,4 +32,33 @@ test_expect_success 'scalar unregister' '\n \t! grep -F \"$(pwd)/vanish/src\" scalar.repos\n '\n \n+test_expect_success 'set up repository to clone' '\n+\ttest_commit first &&\n+\ttest_commit second &&\n+\ttest_commit third &&\n+\tgit switch -c parallel first &&\n+\tmkdir -p 1/2 &&\n+\ttest_commit 1/2/3 &&\n+\tgit config uploadPack.allowFilter true &&\n+\tgit config uploadPack.allowAnySHA1InWant true\n+'\n+\n+test_expect_success 'scalar clone' '\n+\tsecond=$(git rev-parse --verify second:second.t) &&\n+\tscalar clone \"file://$(pwd)\" cloned &&\n+\t(\n+\t\tcd cloned/src &&\n+\n+\t\tgit config --get --global --fixed-value maintenance.repo \\\n+\t\t\t\"$(pwd)\" &&\n+\n+\t\ttest_path_is_missing 1/2 &&\n+\t\ttest_must_fail git rev-list --missing=print $second &&\n+\t\tgit rev-list $second &&\n+\t\tgit cat-file blob $second >actual &&\n+\t\techo \"second\" >expect &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"439738","messageId":"5ee54a9e25f00fab14511f81ebdd3ac8e89475a3.1635323239.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v6.git.1635323239.gitgitgadget@gmail.com","subject":"[PATCH v6 09/15] scalar: teach 'clone' to support the --single-branch option","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-27T08:27:13Z","receivedAt":"2021-10-27T08:27:46Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nJust like `git clone`, the `scalar clone` command now also offers to\nrestrict the clone to a single branch.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          |  9 +++++++--\n contrib/scalar/scalar.txt        | 12 +++++++++++-\n contrib/scalar/t/t9099-scalar.sh |  6 +++++-\n 3 files changed, 23 insertions(+), 4 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 0401462b9b3..754e19d781d 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -326,12 +326,15 @@ static char *remote_default_branch(const char *url)\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n-\tint full_clone = 0;\n+\tint full_clone = 0, single_branch = 0;\n \tstruct option clone_options[] = {\n \t\tOPT_STRING('b', \"branch\", &branch, N_(\"<branch>\"),\n \t\t\t   N_(\"branch to checkout after clone\")),\n \t\tOPT_BOOL(0, \"full-clone\", &full_clone,\n \t\t\t N_(\"when cloning, create full working directory\")),\n+\t\tOPT_BOOL(0, \"single-branch\", &single_branch,\n+\t\t\t N_(\"only download metadata for the branch that will \"\n+\t\t\t    \"be checked out\")),\n \t\tOPT_END(),\n \t};\n \tconst char * const clone_usage[] = {\n@@ -402,7 +405,9 @@ static int cmd_clone(int argc, const char **argv)\n \n \tif (set_config(\"remote.origin.url=%s\", url) ||\n \t    set_config(\"remote.origin.fetch=\"\n-\t\t       \"+refs/heads/*:refs/remotes/origin/*\") ||\n+\t\t       \"+refs/heads/%s:refs/remotes/origin/%s\",\n+\t\t       single_branch ? branch : \"*\",\n+\t\t       single_branch ? branch : \"*\") ||\n \t    set_config(\"remote.origin.promisor=true\") ||\n \t    set_config(\"remote.origin.partialCloneFilter=blob:none\")) {\n \t\tres = error(_(\"could not configure remote in '%s'\"), dir);\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex e8730967f16..56f744a4aa9 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,7 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n-scalar clone [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n+scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n@@ -57,6 +57,16 @@ HEAD[:<directory>]`.\n \tInstead of checking out the branch pointed to by the cloned\n \trepository's HEAD, check out the `<name>` branch instead.\n \n+--[no-]single-branch::\n+\tClone only the history leading to the tip of a single branch, either\n+\tspecified by the `--branch` option or the primary branch remote's\n+\t`HEAD` points at.\n++\n+Further fetches into the resulting repository will only update the\n+remote-tracking branch for the branch this option was used for the initial\n+cloning. If the HEAD at the remote did not point at any branch when\n+`--single-branch` clone was made, no remote-tracking branch is created.\n+\n --[no-]full-clone::\n \tA sparse-checkout is initialized by default. This behavior can be\n \tturned off via `--full-clone`.\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 295398f62cc..9a35ab4fde6 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -45,13 +45,17 @@ test_expect_success 'set up repository to clone' '\n \n test_expect_success 'scalar clone' '\n \tsecond=$(git rev-parse --verify second:second.t) &&\n-\tscalar clone \"file://$(pwd)\" cloned &&\n+\tscalar clone \"file://$(pwd)\" cloned --single-branch &&\n \t(\n \t\tcd cloned/src &&\n \n \t\tgit config --get --global --fixed-value maintenance.repo \\\n \t\t\t\"$(pwd)\" &&\n \n+\t\tgit for-each-ref --format=\"%(refname)\" refs/remotes/origin/ >actual &&\n+\t\techo \"refs/remotes/origin/parallel\" >expect &&\n+\t\ttest_cmp expect actual &&\n+\n \t\ttest_path_is_missing 1/2 &&\n \t\ttest_must_fail git rev-list --missing=print $second &&\n \t\tgit rev-list $second &&\n-- \ngitgitgadget\n\n"},{"id":"439739","messageId":"3361eb8f09177f86849cb1c7737780668a313f5a.1635323239.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v6.git.1635323239.gitgitgadget@gmail.com","subject":"[PATCH v6 10/15] scalar: implement the `run` command","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-27T08:27:14Z","receivedAt":"2021-10-27T08:27:50Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nNote: this subcommand is provided primarily for backwards-compatibility,\nfor existing Scalar uses. It is mostly just a shim for `git\nmaintenance`, mapping task names from the way Scalar called them to the\nway Git calls them.\n\nThe reason why those names differ? The background maintenance was first\nimplemented in Scalar, and when it was contributed as a patch series\nimplementing the `git maintenance` command, reviewers suggested better\nnames, those suggestions were accepted before the patches were\nintegrated into core Git.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 64 +++++++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt | 19 ++++++++++++\n 2 files changed, 83 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 754e19d781d..d9631287e12 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -483,6 +483,69 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_run(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tstruct {\n+\t\tconst char *arg, *task;\n+\t} tasks[] = {\n+\t\t{ \"config\", NULL },\n+\t\t{ \"commit-graph\", \"commit-graph\" },\n+\t\t{ \"fetch\", \"prefetch\" },\n+\t\t{ \"loose-objects\", \"loose-objects\" },\n+\t\t{ \"pack-files\", \"incremental-repack\" },\n+\t\t{ NULL, NULL }\n+\t};\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tconst char *usagestr[] = { NULL, NULL };\n+\tint i;\n+\n+\tstrbuf_addstr(&buf, N_(\"scalar run <task> [<enlistment>]\\nTasks:\\n\"));\n+\tfor (i = 0; tasks[i].arg; i++)\n+\t\tstrbuf_addf(&buf, \"\\t%s\\n\", tasks[i].arg);\n+\tusagestr[0] = buf.buf;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usagestr, 0);\n+\n+\tif (!argc)\n+\t\tusage_with_options(usagestr, options);\n+\n+\tif (!strcmp(\"all\", argv[0])) {\n+\t\ti = -1;\n+\t} else {\n+\t\tfor (i = 0; tasks[i].arg && strcmp(tasks[i].arg, argv[0]); i++)\n+\t\t\t; /* keep looking for the task */\n+\n+\t\tif (i > 0 && !tasks[i].arg) {\n+\t\t\terror(_(\"no such task: '%s'\"), argv[0]);\n+\t\t\tusage_with_options(usagestr, options);\n+\t\t}\n+\t}\n+\n+\targc--;\n+\targv++;\n+\tsetup_enlistment_directory(argc, argv, usagestr, options, NULL);\n+\tstrbuf_release(&buf);\n+\n+\tif (i == 0)\n+\t\treturn register_dir();\n+\n+\tif (i > 0)\n+\t\treturn run_git(\"maintenance\", \"run\",\n+\t\t\t       \"--task\", tasks[i].task, NULL);\n+\n+\tif (register_dir())\n+\t\treturn -1;\n+\tfor (i = 1; tasks[i].arg; i++)\n+\t\tif (run_git(\"maintenance\", \"run\",\n+\t\t\t    \"--task\", tasks[i].task, NULL))\n+\t\t\treturn -1;\n+\treturn 0;\n+}\n+\n static int remove_deleted_enlistment(struct strbuf *path)\n {\n \tint res = 0;\n@@ -555,6 +618,7 @@ static struct {\n \t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n+\t{ \"run\", cmd_run },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 56f744a4aa9..39143b08324 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -12,6 +12,7 @@ scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<e\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n+scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -98,6 +99,24 @@ unregister [<enlistment>]::\n \tRemove the specified repository from the list of repositories\n \tregistered with Scalar and stop the scheduled background maintenance.\n \n+Run\n+~~~\n+\n+scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]::\n+\tRun the given maintenance task (or all tasks, if `all` was specified).\n+\tExcept for `all` and `config`, this subcommand simply hands off to\n+\tlinkgit:git-maintenance[1] (mapping `fetch` to `prefetch` and\n+\t`pack-files` to `incremental-repack`).\n++\n+These tasks are run automatically as part of the scheduled maintenance,\n+as soon as the repository is registered with Scalar. It should therefore\n+not be necessary to run this subcommand manually.\n++\n+The `config` task is specific to Scalar and configures all those\n+opinionated default settings that make Git work more efficiently with\n+large repositories. As this task is run as part of `scalar clone`\n+automatically, explicit invocations of this task are rarely needed.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"439740","messageId":"c4778c1dc5fc20d06918504132be21f5d4b6a50a.1635323239.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v6.git.1635323239.gitgitgadget@gmail.com","subject":"[PATCH v6 11/15] scalar: allow reconfiguring an existing enlistment","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-27T08:27:15Z","receivedAt":"2021-10-27T08:28:04Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis comes in handy during Scalar upgrades, or when config settings were\nmessed up by mistake.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 79 +++++++++++++++++++++-----------\n contrib/scalar/scalar.txt        |  8 ++++\n contrib/scalar/t/t9099-scalar.sh |  8 ++++\n 3 files changed, 67 insertions(+), 28 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex d9631287e12..6bebba0b51f 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -108,18 +108,20 @@ static int run_git(const char *arg, ...)\n \treturn res;\n }\n \n-static int set_recommended_config(void)\n+static int set_recommended_config(int reconfigure)\n {\n \tstruct {\n \t\tconst char *key;\n \t\tconst char *value;\n+\t\tint overwrite_on_reconfigure;\n \t} config[] = {\n-\t\t{ \"am.keepCR\", \"true\" },\n-\t\t{ \"core.FSCache\", \"true\" },\n-\t\t{ \"core.multiPackIndex\", \"true\" },\n-\t\t{ \"core.preloadIndex\", \"true\" },\n+\t\t/* Required */\n+\t\t{ \"am.keepCR\", \"true\", 1 },\n+\t\t{ \"core.FSCache\", \"true\", 1 },\n+\t\t{ \"core.multiPackIndex\", \"true\", 1 },\n+\t\t{ \"core.preloadIndex\", \"true\", 1 },\n #ifndef WIN32\n-\t\t{ \"core.untrackedCache\", \"true\" },\n+\t\t{ \"core.untrackedCache\", \"true\", 1 },\n #else\n \t\t/*\n \t\t * Unfortunately, Scalar's Functional Tests demonstrated\n@@ -133,28 +135,29 @@ static int set_recommended_config(void)\n \t\t * Therefore, with a sad heart, we disable this very useful\n \t\t * feature on Windows.\n \t\t */\n-\t\t{ \"core.untrackedCache\", \"false\" },\n+\t\t{ \"core.untrackedCache\", \"false\", 1 },\n #endif\n-\t\t{ \"core.logAllRefUpdates\", \"true\" },\n-\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n-\t\t{ \"credential.validate\", \"false\" }, /* GCM4W-only */\n-\t\t{ \"gc.auto\", \"0\" },\n-\t\t{ \"gui.GCWarning\", \"false\" },\n-\t\t{ \"index.threads\", \"true\" },\n-\t\t{ \"index.version\", \"4\" },\n-\t\t{ \"merge.stat\", \"false\" },\n-\t\t{ \"merge.renames\", \"false\" },\n-\t\t{ \"pack.useBitmaps\", \"false\" },\n-\t\t{ \"pack.useSparse\", \"true\" },\n-\t\t{ \"receive.autoGC\", \"false\" },\n-\t\t{ \"reset.quiet\", \"true\" },\n-\t\t{ \"feature.manyFiles\", \"false\" },\n-\t\t{ \"feature.experimental\", \"false\" },\n-\t\t{ \"fetch.unpackLimit\", \"1\" },\n-\t\t{ \"fetch.writeCommitGraph\", \"false\" },\n+\t\t{ \"core.logAllRefUpdates\", \"true\", 1 },\n+\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\", 1 },\n+\t\t{ \"credential.validate\", \"false\", 1 }, /* GCM4W-only */\n+\t\t{ \"gc.auto\", \"0\", 1 },\n+\t\t{ \"gui.GCWarning\", \"false\", 1 },\n+\t\t{ \"index.threads\", \"true\", 1 },\n+\t\t{ \"index.version\", \"4\", 1 },\n+\t\t{ \"merge.stat\", \"false\", 1 },\n+\t\t{ \"merge.renames\", \"false\", 1 },\n+\t\t{ \"pack.useBitmaps\", \"false\", 1 },\n+\t\t{ \"pack.useSparse\", \"true\", 1 },\n+\t\t{ \"receive.autoGC\", \"false\", 1 },\n+\t\t{ \"reset.quiet\", \"true\", 1 },\n+\t\t{ \"feature.manyFiles\", \"false\", 1 },\n+\t\t{ \"feature.experimental\", \"false\", 1 },\n+\t\t{ \"fetch.unpackLimit\", \"1\", 1 },\n+\t\t{ \"fetch.writeCommitGraph\", \"false\", 1 },\n #ifdef WIN32\n-\t\t{ \"http.sslBackend\", \"schannel\" },\n+\t\t{ \"http.sslBackend\", \"schannel\", 1 },\n #endif\n+\t\t/* Optional */\n \t\t{ \"status.aheadBehind\", \"false\" },\n \t\t{ \"commitGraph.generationVersion\", \"1\" },\n \t\t{ \"core.autoCRLF\", \"false\" },\n@@ -165,7 +168,8 @@ static int set_recommended_config(void)\n \tchar *value;\n \n \tfor (i = 0; config[i].key; i++) {\n-\t\tif (git_config_get_string(config[i].key, &value)) {\n+\t\tif ((reconfigure && config[i].overwrite_on_reconfigure) ||\n+\t\t    git_config_get_string(config[i].key, &value)) {\n \t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"created\");\n \t\t\tif (git_config_set_gently(config[i].key,\n \t\t\t\t\t\t  config[i].value) < 0)\n@@ -230,7 +234,7 @@ static int register_dir(void)\n \tint res = add_or_remove_enlistment(1);\n \n \tif (!res)\n-\t\tres = set_recommended_config();\n+\t\tres = set_recommended_config(0);\n \n \tif (!res)\n \t\tres = toggle_maintenance(1);\n@@ -418,7 +422,7 @@ static int cmd_clone(int argc, const char **argv)\n \t    (res = run_git(\"sparse-checkout\", \"init\", \"--cone\", NULL)))\n \t\tgoto cleanup;\n \n-\tif (set_recommended_config())\n+\tif (set_recommended_config(0))\n \t\treturn error(_(\"could not configure '%s'\"), dir);\n \n \tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL))) {\n@@ -483,6 +487,24 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_reconfigure(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar reconfigure [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn set_recommended_config(1);\n+}\n+\n static int cmd_run(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -619,6 +641,7 @@ static struct {\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n \t{ \"run\", cmd_run },\n+\t{ \"reconfigure\", cmd_reconfigure },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 39143b08324..89fd7901585 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -13,6 +13,7 @@ scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n+scalar reconfigure <enlistment>\n \n DESCRIPTION\n -----------\n@@ -117,6 +118,13 @@ opinionated default settings that make Git work more efficiently with\n large repositories. As this task is run as part of `scalar clone`\n automatically, explicit invocations of this task are rarely needed.\n \n+Reconfigure\n+~~~~~~~~~~~\n+\n+After a Scalar upgrade, or when the configuration of a Scalar enlistment\n+was somehow corrupted or changed by mistake, this subcommand allows to\n+reconfigure the enlistment.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 9a35ab4fde6..e6d74a06ca0 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -65,4 +65,12 @@ test_expect_success 'scalar clone' '\n \t)\n '\n \n+test_expect_success 'scalar reconfigure' '\n+\tgit init one/src &&\n+\tscalar register one &&\n+\tgit -C one/src config core.preloadIndex false &&\n+\tscalar reconfigure one &&\n+\ttest true = \"$(git -C one/src config core.preloadIndex)\"\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"439741","messageId":"cd824e9e483d53b7cc10c8a2f5777ef0da636027.1635323239.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v6.git.1635323239.gitgitgadget@gmail.com","subject":"[PATCH v6 12/15] scalar: teach 'reconfigure' to optionally handle all registered enlistments","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-27T08:27:16Z","receivedAt":"2021-10-27T08:28:05Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nAfter a Scalar upgrade, it can come in really handy if there is an easy\nway to reconfigure all Scalar enlistments. This new option offers this\nfunctionality.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 61 ++++++++++++++++++++++++++++++--\n contrib/scalar/scalar.txt        |  9 +++--\n contrib/scalar/t/t9099-scalar.sh |  3 ++\n 3 files changed, 67 insertions(+), 6 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 6bebba0b51f..234a7dce479 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -487,22 +487,77 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int get_scalar_repos(const char *key, const char *value, void *data)\n+{\n+\tstruct string_list *list = data;\n+\n+\tif (!strcmp(key, \"scalar.repo\"))\n+\t\tstring_list_append(list, value);\n+\n+\treturn 0;\n+}\n+\n static int cmd_reconfigure(int argc, const char **argv)\n {\n+\tint all = 0;\n \tstruct option options[] = {\n+\t\tOPT_BOOL('a', \"all\", &all,\n+\t\t\t N_(\"reconfigure all registered enlistments\")),\n \t\tOPT_END(),\n \t};\n \tconst char * const usage[] = {\n-\t\tN_(\"scalar reconfigure [<enlistment>]\"),\n+\t\tN_(\"scalar reconfigure [--all | <enlistment>]\"),\n \t\tNULL\n \t};\n+\tstruct string_list scalar_repos = STRING_LIST_INIT_DUP;\n+\tint i, res = 0;\n+\tstruct repository r = { NULL };\n+\tstruct strbuf commondir = STRBUF_INIT, gitdir = STRBUF_INIT;\n \n \targc = parse_options(argc, argv, NULL, options,\n \t\t\t     usage, 0);\n \n-\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\tif (!all) {\n+\t\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\t\treturn set_recommended_config(1);\n+\t}\n+\n+\tif (argc > 0)\n+\t\tusage_msg_opt(_(\"--all or <enlistment>, but not both\"),\n+\t\t\t      usage, options);\n+\n+\tgit_config(get_scalar_repos, &scalar_repos);\n \n-\treturn set_recommended_config(1);\n+\tfor (i = 0; i < scalar_repos.nr; i++) {\n+\t\tconst char *dir = scalar_repos.items[i].string;\n+\n+\t\tstrbuf_reset(&commondir);\n+\t\tstrbuf_reset(&gitdir);\n+\n+\t\tif (chdir(dir) < 0) {\n+\t\t\twarning_errno(_(\"could not switch to '%s'\"), dir);\n+\t\t\tres = -1;\n+\t\t} else if (discover_git_directory(&commondir, &gitdir) < 0) {\n+\t\t\twarning_errno(_(\"git repository gone in '%s'\"), dir);\n+\t\t\tres = -1;\n+\t\t} else {\n+\t\t\tgit_config_clear();\n+\n+\t\t\tthe_repository = &r;\n+\t\t\tr.commondir = commondir.buf;\n+\t\t\tr.gitdir = gitdir.buf;\n+\n+\t\t\tif (set_recommended_config(1) < 0)\n+\t\t\t\tres = -1;\n+\t\t}\n+\t}\n+\n+\tstring_list_clear(&scalar_repos, 1);\n+\tstrbuf_release(&commondir);\n+\tstrbuf_release(&gitdir);\n+\n+\treturn res;\n }\n \n static int cmd_run(int argc, const char **argv)\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 89fd7901585..737cf563c1a 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -13,7 +13,7 @@ scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n-scalar reconfigure <enlistment>\n+scalar reconfigure [ --all | <enlistment> ]\n \n DESCRIPTION\n -----------\n@@ -32,8 +32,8 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand. With the exception of `clone` and `list`, all\n-subcommands expect to be run in an enlistment.\n+depending on the subcommand. With the exception of `clone`, `list` and\n+`reconfigure --all`, all subcommands expect to be run in an enlistment.\n \n COMMANDS\n --------\n@@ -125,6 +125,9 @@ After a Scalar upgrade, or when the configuration of a Scalar enlistment\n was somehow corrupted or changed by mistake, this subcommand allows to\n reconfigure the enlistment.\n \n+With the `--all` option, all enlistments currently registered with Scalar\n+will be reconfigured. Use this option after each Scalar upgrade.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex e6d74a06ca0..5fe7fabd0e5 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -70,6 +70,9 @@ test_expect_success 'scalar reconfigure' '\n \tscalar register one &&\n \tgit -C one/src config core.preloadIndex false &&\n \tscalar reconfigure one &&\n+\ttest true = \"$(git -C one/src config core.preloadIndex)\" &&\n+\tgit -C one/src config core.preloadIndex false &&\n+\tscalar reconfigure -a &&\n \ttest true = \"$(git -C one/src config core.preloadIndex)\"\n '\n \n-- \ngitgitgadget\n\n"},{"id":"439742","messageId":"843026d54812dd5408270b4ddc2ba9873819e631.1635323239.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v6.git.1635323239.gitgitgadget@gmail.com","subject":"[PATCH v6 13/15] scalar: implement the `delete` command","fromName":"Matthew John Cheetham via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-27T08:27:17Z","receivedAt":"2021-10-27T08:28:08Z","isPatch":true,"sender":{"key":"mjcheetham@outlook.com","avatar":"https://avatars.githubusercontent.com/u/5658207?v=4"},"body":"From: Matthew John Cheetham <mjcheetham@outlook.com>\n\nDelete an enlistment by first unregistering the repository and then\ndeleting the enlistment directory (usually the directory containing the\nworktree `src/` directory).\n\nOn Windows, if the current directory is inside the enlistment's\ndirectory, change to the parent of the enlistment directory, to allow us\nto delete the enlistment (directories used by processes e.g. as current\nworking directories cannot be deleted on Windows).\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Matthew John Cheetham <mjcheetham@outlook.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 63 ++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        |  8 ++++\n contrib/scalar/t/t9099-scalar.sh |  9 +++++\n 3 files changed, 80 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 234a7dce479..8aaeca7cc64 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -8,6 +8,8 @@\n #include \"config.h\"\n #include \"run-command.h\"\n #include \"refs.h\"\n+#include \"dir.h\"\n+#include \"packfile.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -327,6 +329,33 @@ static char *remote_default_branch(const char *url)\n \treturn NULL;\n }\n \n+static int delete_enlistment(struct strbuf *enlistment)\n+{\n+#ifdef WIN32\n+\tstruct strbuf parent = STRBUF_INIT;\n+#endif\n+\n+\tif (unregister_dir())\n+\t\tdie(_(\"failed to unregister repository\"));\n+\n+#ifdef WIN32\n+\t/*\n+\t * Change the current directory to one outside of the enlistment so\n+\t * that we may delete everything underneath it.\n+\t */\n+\tstrbuf_addbuf(&parent, enlistment);\n+\tstrbuf_parent_directory(&parent);\n+\tif (chdir(parent.buf) < 0)\n+\t\tdie_errno(_(\"could not switch to '%s'\"), parent.buf);\n+\tstrbuf_release(&parent);\n+#endif\n+\n+\tif (remove_dir_recursively(enlistment, 0))\n+\t\tdie(_(\"failed to delete enlistment directory\"));\n+\n+\treturn 0;\n+}\n+\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n@@ -687,6 +716,39 @@ static int cmd_unregister(int argc, const char **argv)\n \treturn unregister_dir();\n }\n \n+static int cmd_delete(int argc, const char **argv)\n+{\n+\tchar *cwd = xgetcwd();\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar delete <enlistment>\"),\n+\t\tNULL\n+\t};\n+\tstruct strbuf enlistment = STRBUF_INIT;\n+\tint res = 0;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tif (argc != 1)\n+\t\tusage_with_options(usage, options);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, &enlistment);\n+\n+\tif (dir_inside_of(cwd, enlistment.buf) >= 0)\n+\t\tres = error(_(\"refusing to delete current working directory\"));\n+\telse {\n+\t\tclose_object_store(the_repository->objects);\n+\t\tres = delete_enlistment(&enlistment);\n+\t}\n+\tstrbuf_release(&enlistment);\n+\tfree(cwd);\n+\n+\treturn res;\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n@@ -697,6 +759,7 @@ static struct {\n \t{ \"unregister\", cmd_unregister },\n \t{ \"run\", cmd_run },\n \t{ \"reconfigure\", cmd_reconfigure },\n+\t{ \"delete\", cmd_delete },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 737cf563c1a..f416d637289 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -14,6 +14,7 @@ scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n scalar reconfigure [ --all | <enlistment> ]\n+scalar delete <enlistment>\n \n DESCRIPTION\n -----------\n@@ -128,6 +129,13 @@ reconfigure the enlistment.\n With the `--all` option, all enlistments currently registered with Scalar\n will be reconfigured. Use this option after each Scalar upgrade.\n \n+Delete\n+~~~~~~\n+\n+delete <enlistment>::\n+\tThis subcommand lets you delete an existing Scalar enlistment from your\n+\tlocal file system, unregistering the repository.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 5fe7fabd0e5..7e8771d0eff 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -76,4 +76,13 @@ test_expect_success 'scalar reconfigure' '\n \ttest true = \"$(git -C one/src config core.preloadIndex)\"\n '\n \n+test_expect_success 'scalar delete without enlistment shows a usage' '\n+\ttest_expect_code 129 scalar delete\n+'\n+\n+test_expect_success 'scalar delete with enlistment' '\n+\tscalar delete cloned &&\n+\ttest_path_is_missing cloned\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"439743","messageId":"5ca169b3f3a0aead986ba2e7bb31ae5678bdc37a.1635323239.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v6.git.1635323239.gitgitgadget@gmail.com","subject":"[PATCH v6 14/15] scalar: implement the `version` command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-27T08:27:18Z","receivedAt":"2021-10-27T08:28:09Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe .NET version of Scalar has a `version` command. This was necessary\nbecause it was versioned independently of Git.\n\nSince Scalar is now tightly coupled with Git, it does not make sense for\nthem to show different versions. Therefore, it shows the same output as\n`git version`. For backwards-compatibility with the .NET version,\n`scalar version` prints to `stderr`, though (`git version` prints to\n`stdout` instead).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c | 39 +++++++++++++++++++++++++++++++++++++++\n 1 file changed, 39 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 8aaeca7cc64..b2e92cf63b5 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -10,6 +10,7 @@\n #include \"refs.h\"\n #include \"dir.h\"\n #include \"packfile.h\"\n+#include \"help.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -356,6 +357,15 @@ static int delete_enlistment(struct strbuf *enlistment)\n \treturn 0;\n }\n \n+/*\n+ * Dummy implementation; Using `get_version_info()` would cause a link error\n+ * without this.\n+ */\n+void load_builtin_commands(const char *prefix, struct cmdnames *cmds)\n+{\n+\tdie(\"not implemented\");\n+}\n+\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n@@ -749,6 +759,34 @@ static int cmd_delete(int argc, const char **argv)\n \treturn res;\n }\n \n+static int cmd_version(int argc, const char **argv)\n+{\n+\tint verbose = 0, build_options = 0;\n+\tstruct option options[] = {\n+\t\tOPT__VERBOSE(&verbose, N_(\"include Git version\")),\n+\t\tOPT_BOOL(0, \"build-options\", &build_options,\n+\t\t\t N_(\"include Git's build options\")),\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar verbose [-v | --verbose] [--build-options]\"),\n+\t\tNULL\n+\t};\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tif (argc != 0)\n+\t\tusage_with_options(usage, options);\n+\n+\tget_version_info(&buf, build_options);\n+\tfprintf(stderr, \"%s\\n\", buf.buf);\n+\tstrbuf_release(&buf);\n+\n+\treturn 0;\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n@@ -760,6 +798,7 @@ static struct {\n \t{ \"run\", cmd_run },\n \t{ \"reconfigure\", cmd_reconfigure },\n \t{ \"delete\", cmd_delete },\n+\t{ \"version\", cmd_version },\n \t{ NULL, NULL},\n };\n \n-- \ngitgitgadget\n\n"},{"id":"439744","messageId":"e3a6eea0534d4e0ae5e9597b1dc3b86bfd9bffe2.1635323239.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v6.git.1635323239.gitgitgadget@gmail.com","subject":"[PATCH v6 15/15] scalar: accept -C and -c options before the subcommand","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-27T08:27:19Z","receivedAt":"2021-10-27T08:28:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe `git` executable has these two very useful options:\n\n-C <directory>:\n\tswitch to the specified directory before performing any actions\n\n-c <key>=<value>:\n\ttemporarily configure this setting for the duration of the\n\tspecified scalar subcommand\n\nWith this commit, we teach the `scalar` executable the same trick.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 22 +++++++++++++++++++++-\n contrib/scalar/scalar.txt | 10 ++++++++++\n 2 files changed, 31 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex b2e92cf63b5..6c496318bd4 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -807,6 +807,25 @@ int cmd_main(int argc, const char **argv)\n \tstruct strbuf scalar_usage = STRBUF_INIT;\n \tint i;\n \n+\twhile (argc > 1 && *argv[1] == '-') {\n+\t\tif (!strcmp(argv[1], \"-C\")) {\n+\t\t\tif (argc < 3)\n+\t\t\t\tdie(_(\"-C requires a <directory>\"));\n+\t\t\tif (chdir(argv[2]) < 0)\n+\t\t\t\tdie_errno(_(\"could not change to '%s'\"),\n+\t\t\t\t\t  argv[2]);\n+\t\t\targc -= 2;\n+\t\t\targv += 2;\n+\t\t} else if (!strcmp(argv[1], \"-c\")) {\n+\t\t\tif (argc < 3)\n+\t\t\t\tdie(_(\"-c requires a <key>=<value> argument\"));\n+\t\t\tgit_config_push_parameter(argv[2]);\n+\t\t\targc -= 2;\n+\t\t\targv += 2;\n+\t\t} else\n+\t\t\tbreak;\n+\t}\n+\n \tif (argc > 1) {\n \t\targv++;\n \t\targc--;\n@@ -817,7 +836,8 @@ int cmd_main(int argc, const char **argv)\n \t}\n \n \tstrbuf_addstr(&scalar_usage,\n-\t\t      N_(\"scalar <command> [<options>]\\n\\nCommands:\\n\"));\n+\t\t      N_(\"scalar [-C <directory>] [-c <key>=<value>] \"\n+\t\t\t \"<command> [<options>]\\n\\nCommands:\\n\"));\n \tfor (i = 0; builtins[i].name; i++)\n \t\tstrbuf_addf(&scalar_usage, \"\\t%s\\n\", builtins[i].name);\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex f416d637289..cf4e5b889cc 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -36,6 +36,16 @@ The `scalar` command implements various subcommands, and different options\n depending on the subcommand. With the exception of `clone`, `list` and\n `reconfigure --all`, all subcommands expect to be run in an enlistment.\n \n+The following options can be specified _before_ the subcommand:\n+\n+-C <directory>::\n+\tBefore running the subcommand, change the working directory. This\n+\toption imitates the same option of linkgit:git[1].\n+\n+-c <key>=<value>::\n+\tFor the duration of running the specified subcommand, configure this\n+\tsetting. This option imitates the same option of linkgit:git[1].\n+\n COMMANDS\n --------\n \n-- \ngitgitgadget\n"},{"id":"439840","messageId":"3b7f5ab0-abe5-836d-9e36-0d8cd128401c@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v6.git.1635323239.gitgitgadget@gmail.com","subject":"Re: [PATCH v6 00/15] Upstreaming the Scalar command","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2021-10-27T21:57:03Z","receivedAt":"2021-10-27T21:57:10Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 10/27/2021 4:27 AM, Johannes Schindelin via GitGitGadget wrote:\n> tl;dr: This series contributes the core part of the Scalar command to the\n> Git project. This command provides an opinionated way to create and\n> configure Git repositories with a focus on very large repositories.\n\nI started a discussion [1] to help answer some big questions about this\nseries, but to separate those thoughts from the concrete patches. If\nyou are interested, please contribute feedback there.\n\n[1] https://lore.kernel.org/git/b67bbef4-e4c3-b6a7-1c7f-7d405902ef8b@gmail.com/\n\nThanks,\n-Stolee\n"},{"id":"441481","messageId":"pull.1005.v7.git.1637158762.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v6.git.1635323239.gitgitgadget@gmail.com","subject":"[PATCH v7 00/17] Upstreaming the Scalar command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-17T14:19:05Z","receivedAt":"2021-11-17T14:19:29Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"tl;dr: This series contributes the core part of the Scalar command to the\nGit project. This command provides an opinionated way to create and\nconfigure Git repositories with a focus on very large repositories.\n\nChanges since v6:\n\n * Rebased on top of v2.34.0.\n * Inserted a commit that adds contrib/scalar/README.md, containing the\n   roadmap of what I have planned for Scalar.\n * The Scalar test's definition of GIT_TEST_MAINT_SCHEDULER has been\n   adjusted to accommodate for a change in v2.32.0..v2.34.0.\n * The config setting defaults now include fetch.showForcedUpdates=false,\n   which has been identified as helping with a performance issue in large\n   repositories.\n * To avoid mistaking the current patch series for being feature-complete\n   enough to unleash onto end users, I moved the Makefile rules to build\n   HTML/manual pages to a later patch series.\n * The patch that adds support for -c <key>=<value> and -C <directory> was\n   moved to its own add-on patch series: While it is obvious that those\n   options are valuable to have, an open question is whether there are other\n   \"pre-command\" options in git that would be useful, too, and I would like\n   to postpone that discussion to that date.\n * I added two patches that I had planned on keeping in an add-on patch\n   series for later, to build and test Scalar as part of the CI. I am still\n   not 100% certain that it is a good idea to do so already now, but let's\n   see what the reviewers have to say.\n\nChanges since v5:\n\n * Fixed the commit message talking about make -C contrib/scalar/Makefile.\n * Fixed the git ls-tree invocation suggested in the manual for scalar\n   clone.\n * Invoking make -C contrib/scalar, then changing a source file of libgit.a\n   and then immediately invoking make -C contrib/scalar again will now\n   implicitly rebuild libgit.a.\n\nChanges since v4:\n\n * scalar delete now refuses to delete anything if it was started from\n   within the enlistment.\n * scalar delete releases any handles to the object store before deleting\n   the enlistment.\n * The OBJECTS list in the Makefile will now include Scalar.\n * scalar register now supports secondary worktrees, in addition to the\n   primary worktree.\n\nChanges since v3:\n\n * Moved the \"Changes since\" section to the top, to make it easier to see\n   what changed.\n * Reworded the commit message of the first patch.\n * Removed the [RFC] prefix because I did not hear any objections against\n   putting this into contrib/.\n\nChanges since v2:\n\n * Adjusted the description of the list command in the manual page , as\n   suggested by Bagas.\n * Addressed two style nits in cmd_run().\n * The documentation of git reconfigure -a was improved.\n\nChanges since v1:\n\n * A couple typos were fixed\n * The code parsing the output of ls-remote was made more readable\n * The indentation used in scalar.txt now consistently uses tabs\n * We no longer hard-code core.bare = false when registering with Scalar\n\n\nBackground\n==========\n\nMicrosoft invested a lot of effort into scaling Git to the needs of the\nWindows operating system source code. Based on the experience of the first\napproach, VFS for Git, the Scalar project was started. Scalar specifically\nhas as its core goal to funnel all improvements into core Git.\n\n\nThe present\n===========\n\nThe Scalar project provides a completely functional non-virtual experience\nfor monorepos. But why stop there. The Scalar project was designed to be a\nself-destructing vehicle to allow those key concepts to be moved into core\nGit itself for the benefit of all. For example, partial clone,\nsparse-checkout, and scheduled background maintenance have already been\nupstreamed and removed from Scalar proper. This patch series provides a\nC-based implementation of the final remaining portions of the Scalar\ncommand. This will make it easier for users to experiment with the Scalar\ncommand. It will also make it substantially easier to experiment with moving\nfunctionality from Scalar into core Git, while maintaining\nbackwards-compatibility for existing Scalar users.\n\nThe C-based Scalar has been shipped to Scalar users, and can be tested by\nany interested reader: https://github.com/microsoft/git/releases/ (it offers\na Git for Windows installer, a macOS package and an Ubuntu package, Scalar\nhas been included since v2.33.0.vfs.0.0).\n\n\nNext steps\n==========\n\nSince there are existing Scalar users, I want to ensure\nbackwards-compatibility with its existing command-line interface. Keeping\nthat in mind, everything in this series is up for discussion.\n\nI obviously believe that Scalar brings a huge benefit, and think that it\nwould be ideal for all of Scalar's learnings to end up in git clone/git\ninit/git maintenance eventually. It is also conceivable, however, that the\nscalar command could graduate to be a core part of Git at some stage in the\nfuture (such a decision would probably depend highly on users' feedback).\nSee also the discussion about the architecture of Scalar\n[https://lore.kernel.org/git/b67bbef4-e4c3-b6a7-1c7f-7d405902ef8b@gmail.com/],\nkicked off by Stolee.\n\nOn top of this patch series, I have lined up a few more:\n\n 1. Implement a scalar diagnose command.\n 2. Use the built-in FSMonitor (that patch series obviously needs to wait\n    for FSMonitor to be integrated).\n 3. Modify the config machinery to be more generous about concurrent writes,\n    say, to the user-wide config.\n 4. A few patches to optionally build and install scalar as part of a\n    regular Git install (also teaching git help scalar to find the Scalar\n    documentation\n\nThese are included in my vfs-with-scalar branch thicket\n[https://github.com/dscho/git/commits/vfs-with-scalar]. On top of that, this\nbranch thicket also includes patches I do not plan on upstreaming, mainly\nbecause they are too specific either to VFS for Git, or they support Azure\nRepos (which does not offer partial clones but speaks the GVFS protocol,\nwhich can be used to emulate partial clones).\n\nOne other thing is very interesting about that vfs-with-scalar branch\nthicket: it contains a GitHub workflow which will run Scalar's quite\nextensive Functional Tests suite. This test suite is quite comprehensive and\ncaught us a lot of bugs in the past, not only in the Scalar code, but also\ncore Git.\n\n\nEpilogue\n========\n\nNow, to address some questions that I imagine every reader has who made it\nthis far:\n\n * Why not put the Scalar functionality directly into core Git, even a\n   built-in? I wanted to provide an easy way for Git contributors to \"play\n   with\" Scalar, without forcing a new top-level command into Git.\n * Why implement the Scalar command in the Git code base? Apart from\n   simplifying Scalar maintenance in the Microsoft port of Git, the tight\n   version coupling between Git and Scalar reduces the maintenance burden\n   even further. Besides, I believe that it will make it much easier to\n   shift functionality from Scalar into core Git, once we took the hurdle of\n   accepting the Scalar code into the code base.\n * Why contribute Scalar to the Git project? We are biased, of course, yet\n   our data-driven approach provides evidence that Scalar helps handling\n   huge repositories with ease. By contributing it to the core Git project,\n   we are able to share it with more users, especially some users who do not\n   want to install Microsoft's fork of Git. We also hope that a lot of\n   Scalar (maybe all of it) will end up in core Git, to benefit even more\n   users.\n\nDerrick Stolee (4):\n  scalar: 'register' sets recommended config and starts maintenance\n  scalar: 'unregister' stops background maintenance\n  scalar: implement 'scalar list'\n  scalar: implement the `run` command\n\nJohannes Schindelin (12):\n  scalar: add a README with a roadmap\n  scalar: create a rudimentary executable\n  scalar: start documenting the command\n  scalar: create test infrastructure\n  cmake: optionally build `scalar`, too\n  ci: also run the `scalar` tests\n  scalar: let 'unregister' handle a deleted enlistment directory\n    gracefully\n  scalar: implement the `clone` subcommand\n  scalar: teach 'clone' to support the --single-branch option\n  scalar: allow reconfiguring an existing enlistment\n  scalar: teach 'reconfigure' to optionally handle all registered\n    enlistments\n  scalar: implement the `version` command\n\nMatthew John Cheetham (1):\n  scalar: implement the `delete` command\n\n .github/workflows/main.yml          |  15 +\n Makefile                            |   9 +\n ci/run-build-and-tests.sh           |   1 +\n ci/run-test-slice.sh                |   5 +\n contrib/buildsystems/CMakeLists.txt |  14 +\n contrib/scalar/.gitignore           |   2 +\n contrib/scalar/Makefile             |  45 ++\n contrib/scalar/README.md            |  71 +++\n contrib/scalar/scalar.c             | 826 ++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt           | 145 +++++\n contrib/scalar/t/Makefile           |  78 +++\n contrib/scalar/t/t9099-scalar.sh    |  88 +++\n 12 files changed, 1299 insertions(+)\n create mode 100644 contrib/scalar/.gitignore\n create mode 100644 contrib/scalar/Makefile\n create mode 100644 contrib/scalar/README.md\n create mode 100644 contrib/scalar/scalar.c\n create mode 100644 contrib/scalar/scalar.txt\n create mode 100644 contrib/scalar/t/Makefile\n create mode 100755 contrib/scalar/t/t9099-scalar.sh\n\n\nbase-commit: cd3e606211bb1cf8bc57f7d76bab98cc17a150bc\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1005%2Fdscho%2Fscalar-the-beginning-v7\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1005/dscho/scalar-the-beginning-v7\nPull-Request: https://github.com/gitgitgadget/git/pull/1005\n\nRange-diff vs v6:\n\n  -:  ----------- >  1:  3aa095dc824 scalar: add a README with a roadmap\n  1:  9b0b00438ec =  2:  e0693cc713c scalar: create a rudimentary executable\n  2:  40fee75968e !  3:  d80627615f8 scalar: start documenting the command\n     @@ Metadata\n       ## Commit message ##\n          scalar: start documenting the command\n      \n     -    This commit establishes the infrastructure to build the manual page for\n     -    the `scalar` command.\n     +    Let's build up the documentation for the Scalar command along with the\n     +    patches that implement its functionality.\n      \n     -    Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n     -\n     - ## contrib/scalar/.gitignore ##\n     -@@\n     -+/*.xml\n     -+/*.1\n     -+/*.html\n     - /*.exe\n     - /scalar\n     +    Note: To discourage the feature-incomplete documentation from being\n     +    mistaken for the complete thing, we do not yet provide any way to build\n     +    HTML or manual pages from the text file.\n      \n     - ## contrib/scalar/Makefile ##\n     -@@ contrib/scalar/Makefile: ifndef V\n     - \tQUIET_SUBDIR0  = +@subdir=\n     - \tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n     - \t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n     -+\tQUIET          = @\n     - else\n     - \texport V\n     - endif\n     -@@ contrib/scalar/Makefile: $(TARGETS): $(GITLIBS) scalar.c\n     - \n     - clean:\n     - \t$(RM) $(TARGETS)\n     -+\t$(RM) scalar.1 scalar.html scalar.xml\n     - \n     --.PHONY: $(GITLIBS) all clean FORCE\n     -+docs: scalar.html scalar.1\n     -+\n     -+scalar.html: | scalar.1 # prevent them from trying to build `doc.dep` in parallel\n     -+\n     -+scalar.html scalar.1: scalar.txt\n     -+\t$(QUIET_SUBDIR0)../../Documentation$(QUIET_SUBDIR1) \\\n     -+\t\tMAN_TXT=../contrib/scalar/scalar.txt \\\n     -+\t\t../contrib/scalar/$@\n     -+\t$(QUIET)test scalar.1 != \"$@\" || mv ../../Documentation/$@ .\n     -+\n     -+.PHONY: $(GITLIBS) all clean docs FORCE\n     +    Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n      \n       ## contrib/scalar/scalar.txt (new) ##\n      @@\n  3:  e3507c2d5f7 !  4:  9da1616849e scalar: create test infrastructure\n     @@ Commit message\n          more tests in subsequent commits that introduce corresponding, new\n          functionality.\n      \n     -    Note: this test script is intended to test `scalar` only lightly, even\n     +    Note: This test script is intended to test `scalar` only lightly, even\n          after all of the functionality is implemented.\n      \n          A more comprehensive functional (or: integration) test suite can be\n          found at https://github.com/microsoft/scalar; It is used in the workflow\n          https://github.com/microsoft/git/blob/HEAD/.github/workflows/scalar-functional-tests.yml\n          in Microsoft's Git fork. This test suite performs end-to-end tests with\n     -    a real remote repository, and is run as part of the regular CI builds.\n     +    a real remote repository, and is run as part of the regular CI and PR\n     +    builds in that fork.\n     +\n          Since those tests require some functionality supported only by\n          Microsoft's Git fork (\"GVFS protocol\"), there is no intention to port\n          that fuller test suite to `contrib/scalar/`.\n     @@ contrib/scalar/Makefile: $(TARGETS): $(GITLIBS) scalar.c\n       clean:\n      -\t$(RM) $(TARGETS)\n      +\t$(RM) $(TARGETS) ../../bin-wrappers/scalar\n     - \t$(RM) scalar.1 scalar.html scalar.xml\n       \n     +-.PHONY: $(GITLIBS) all clean FORCE\n      +../../bin-wrappers/scalar: ../../wrap-for-bin.sh Makefile\n      +\t@mkdir -p ../../bin-wrappers\n      +\t$(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \\\n     @@ contrib/scalar/Makefile: $(TARGETS): $(GITLIBS) scalar.c\n      +test: all\n      +\t$(MAKE) -C t\n      +\n     - docs: scalar.html scalar.1\n     - \n     - scalar.html: | scalar.1 # prevent them from trying to build `doc.dep` in parallel\n     -@@ contrib/scalar/Makefile: scalar.html scalar.1: scalar.txt\n     - \t\t../contrib/scalar/$@\n     - \t$(QUIET)test scalar.1 != \"$@\" || mv ../../Documentation/$@ .\n     - \n     --.PHONY: $(GITLIBS) all clean docs FORCE\n     -+.PHONY: $(GITLIBS) all clean docs test FORCE\n     ++.PHONY: $(GITLIBS) all clean test FORCE\n      \n       ## contrib/scalar/t/Makefile (new) ##\n      @@\n  -:  ----------- >  5:  dbaad4753c1 cmake: optionally build `scalar`, too\n  -:  ----------- >  6:  1b0328fa236 ci: also run the `scalar` tests\n  4:  53224e506ea !  7:  cca604ef326 scalar: 'register' sets recommended config and starts maintenance\n     @@ contrib/scalar/scalar.c\n      +\t\t{ \"commitGraph.generationVersion\", \"1\" },\n      +\t\t{ \"core.autoCRLF\", \"false\" },\n      +\t\t{ \"core.safeCRLF\", \"false\" },\n     ++\t\t{ \"fetch.showForcedUpdates\", \"false\" },\n      +\t\t{ NULL, NULL },\n      +\t};\n      +\tint i;\n  5:  3591e53700b =  8:  9fea89cd161 scalar: 'unregister' stops background maintenance\n  6:  fe04ae7ec66 =  9:  5e077bf892b scalar: let 'unregister' handle a deleted enlistment directory gracefully\n  7:  136aec439fd = 10:  dfa0c470989 scalar: implement 'scalar list'\n  8:  ed24a5e9b4c ! 11:  febefe39886 scalar: implement the `clone` subcommand\n     @@ contrib/scalar/t/t9099-scalar.sh: PATH=$PWD/..:$PATH\n       \n       . ../../../t/test-lib.sh\n       \n     -+GIT_TEST_MAINT_SCHEDULER=\"crontab:test-tool crontab ../cron.txt\"\n     ++GIT_TEST_MAINT_SCHEDULER=\"crontab:test-tool crontab ../cron.txt,launchctl:true,schtasks:true\"\n      +export GIT_TEST_MAINT_SCHEDULER\n      +\n       test_expect_success 'scalar shows a usage' '\n  9:  5ee54a9e25f = 12:  2677bcff335 scalar: teach 'clone' to support the --single-branch option\n 10:  3361eb8f091 = 13:  99affb84284 scalar: implement the `run` command\n 11:  c4778c1dc5f = 14:  69e2242240b scalar: allow reconfiguring an existing enlistment\n 12:  cd824e9e483 = 15:  0068c18aa62 scalar: teach 'reconfigure' to optionally handle all registered enlistments\n 13:  843026d5481 = 16:  d5218523a38 scalar: implement the `delete` command\n 14:  5ca169b3f3a = 17:  96a803416b5 scalar: implement the `version` command\n 15:  e3a6eea0534 <  -:  ----------- scalar: accept -C and -c options before the subcommand\n\n-- \ngitgitgadget\n"},{"id":"441480","messageId":"3aa095dc824ea7f659e6c11aa2c21be6cadbe985.1637158762.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v7.git.1637158762.gitgitgadget@gmail.com","subject":"[PATCH v7 01/17] scalar: add a README with a roadmap","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-17T14:19:06Z","receivedAt":"2021-11-17T14:19:30Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe Scalar command will be contributed incrementally, over a bunch of\npatch series. Let's document what Scalar is about, and then describe the\npatch series that are planned.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/README.md | 71 ++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 71 insertions(+)\n create mode 100644 contrib/scalar/README.md\n\ndiff --git a/contrib/scalar/README.md b/contrib/scalar/README.md\nnew file mode 100644\nindex 00000000000..7898a683ba5\n--- /dev/null\n+++ b/contrib/scalar/README.md\n@@ -0,0 +1,71 @@\n+# Scalar - an opinionated repository management tool\n+\n+Scalar is an add-on to Git, helping Git scale to very large repositories and\n+worktrees. Originally implemented in C# using .NET Core, based on the learnings\n+from the VFS for Git project, most of the techniques developed by the Scalar\n+project have been integrated into core Git already:\n+\n+* partial clone,\n+* commit graphs,\n+* multi-pack index,\n+* sparse checkout (cone mode),\n+* scheduled background maintenance,\n+* etc\n+\n+This directory contains the remaining parts of Scalar that are not (yet) in\n+core Git.\n+\n+## Roadmap\n+\n+The idea is to populate this directory via incremental patch series and\n+eventually move to a top-level directory next to `gitk-git/` and to `git-gui/`. The\n+current plan involves the following patch series:\n+\n+- `scalar-the-beginning`: The initial patch series which sets up\n+  `contrib/scalar/` and populates it with a minimal `scalar` command that\n+  demonstrates the fundamental ideas.\n+\n+- `scalar-c-and-C`: The `scalar` command learns about two options that can be\n+  specified before the command, `-c <key>=<value>` and `-C <directory>`.\n+\n+- `scalar-diagnose`: The `scalar` command is taught the `diagnose` subcommand.\n+\n+- `scalar-and-builtin-fsmonitor`: The built-in FSMonitor is enabled in `scalar\n+  init` and in `scalar clone`, for an enormous performance boost when working\n+  in large worktrees. This patch series necessarily depends on Jeff Hostetler's\n+  FSMonitor patch series to be integrated into Git.\n+\n+- `scalar-gentler-config-locking`: Scalar enlistments are registered in the\n+  user's Git config. This usually does not represent any problem because it is\n+  rare for a user to register an enlistment. However, in Scalar's functional\n+  tests, Scalar enlistments are created galore, and in parallel, which can lead\n+  to lock contention. This patch series works around that problem by re-trying\n+  to lock the config file in a gentle fashion.\n+\n+- `scalar-extra-docs`: Add some extensive documentation that has been written\n+  in the original Scalar project (all subject to discussion, of course).\n+\n+- `optionally-install-scalar`: Now that Scalar is feature (and documentation)\n+  complete and is verified in CI builds, let's offer to install it.\n+\n+- `move-scalar-to-toplevel`: Now that Scalar is complete, let's move it next to\n+  `gitk-git/` and to `git-gui/`, making it a top-level command.\n+\n+The following two patch series exist, but there is no plan to integrate them\n+into Git's source tree:\n+\n+- `scalar-with-gvfs`: The primary purpose of this patch series is to support\n+  existing Scalar users whose repositories are hosted in Azure Repos (which\n+  does not support Git's partial clones, but supports its predecessor, the GVFS\n+  protocol, which is used by Scalar to emulate the partial clone).\n+\n+  Since the GVFS protocol will never be supported by core Git, this patch\n+  series will remain in Microsoft's fork of Git.\n+\n+- `run-scalar-functional-tests`: The Scalar project developed a quite\n+  comprehensive set of integration tests (or, \"Functional Tests\"). They are the\n+  sole remaining part of the original C#-based Scalar project, and this patch\n+  adds a GitHub workflow that runs them all.\n+\n+  Since the tests partially depend on features that are only provided in the\n+  `scalar-with-gvfs` patch series, this patch cannot be upstreamed.\n-- \ngitgitgadget\n\n"},{"id":"441482","messageId":"e0693cc713c15de09d12fe4cfe1f971a3f1dd86b.1637158762.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v7.git.1637158762.gitgitgadget@gmail.com","subject":"[PATCH v7 02/17] scalar: create a rudimentary executable","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-17T14:19:07Z","receivedAt":"2021-11-17T14:19:42Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe idea of Scalar (https://github.com/microsoft/scalar), and before\nthat, of VFS for Git, has always been to prove that Git _can_ scale, and\nto upstream whatever strategies have been demonstrated to help.\n\nWith this patch, we start the journey from that C# project to move what\nis left to Git's own `contrib/` directory, reimplementing it in pure C,\nwith the intention to facilitate integrating the functionality into core\nGit all while maintaining backwards-compatibility for existing Scalar\nusers (which will be much easier when both live in the same worktree).\nIt has always been the plan to contribute all of the proven strategies\nback to core Git.\n\nFor example, while the virtual filesystem provided by VFS for Git helped\nthe team developing the Windows operating system to move onto Git, while\ntrying to upstream it we realized that it cannot be done: getting the\nvirtual filesystem to work (which we only managed to implement fully on\nWindows, but not on, say, macOS or Linux), and the required server-side\nsupport for the GVFS protocol, made this not quite feasible.\n\nThe Scalar project learned from that and tackled the problem with\ndifferent tactics: instead of pretending to Git that the working\ndirectory is fully populated, it _specifically_ teaches Git about\npartial clone (which is based on VFS for Git's cache server), about\nsparse checkout (which VFS for Git tried to do transparently, in the\nfile system layer), and regularly runs maintenance tasks to keep the\nrepository in a healthy state.\n\nWith partial clone, sparse checkout and `git maintenance` having been\nupstreamed, there is little left that `scalar.exe` does which `git.exe`\ncannot do. One such thing is that `scalar clone <url>` will\nautomatically set up a partial, sparse clone, and configure\nknown-helpful settings from the start.\n\nSo let's bring this convenience into Git's tree.\n\nThe idea here is that you can (optionally) build Scalar via\n\n\tmake -C contrib/scalar/\n\nThis will build the `scalar` executable and put it into the\ncontrib/scalar/ subdirectory.\n\nThe slightly awkward addition of the `contrib/scalar/*` bits to the\ntop-level `Makefile` are actually really required: we want to link to\n`libgit.a`, which means that we will need to use the very same `CFLAGS`\nand `LDFLAGS` as the rest of Git.\n\nAn early development version of this patch tried to replicate all the\nconditional code in `contrib/scalar/Makefile` (e.g. `NO_POLL`) just like\n`contrib/svn-fe/Makefile` used to do before it was retired. It turned\nout to be quite the whack-a-mole game: the SHA-1-related flags, the\nflags enabling/disabling `compat/poll/`, `compat/regex/`,\n`compat/win32mmap.c` & friends depending on the current platform... To\nput it mildly: it was a major mess.\n\nInstead, this patch makes minimal changes to the top-level `Makefile` so\nthat the bits in `contrib/scalar/` can be compiled and linked, and\nadds a `contrib/scalar/Makefile` that uses the top-level `Makefile` in a\nmost minimal way to do the actual compiling.\n\nNote: With this commit, we only establish the infrastructure, no\nScalar functionality is implemented yet; We will do that incrementally\nover the next few commits.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Makefile                  |  9 +++++++++\n contrib/scalar/.gitignore |  2 ++\n contrib/scalar/Makefile   | 34 ++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.c   | 36 ++++++++++++++++++++++++++++++++++++\n 4 files changed, 81 insertions(+)\n create mode 100644 contrib/scalar/.gitignore\n create mode 100644 contrib/scalar/Makefile\n create mode 100644 contrib/scalar/scalar.c\n\ndiff --git a/Makefile b/Makefile\nindex 12be39ac497..fe898aeea08 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2456,6 +2456,11 @@ OBJECTS += $(FUZZ_OBJS)\n ifndef NO_CURL\n \tOBJECTS += http.o http-walker.o remote-curl.o\n endif\n+\n+SCALAR_SOURCES := contrib/scalar/scalar.c\n+SCALAR_OBJECTS := $(SCALAR_SOURCES:c=o)\n+OBJECTS += $(SCALAR_OBJECTS)\n+\n .PHONY: objects\n objects: $(OBJECTS)\n \n@@ -2589,6 +2594,10 @@ $(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n \t\t$(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)\n \n+contrib/scalar/scalar$X: $(SCALAR_OBJECTS) GIT-LDFLAGS $(GITLIBS)\n+\t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \\\n+\t\t$(filter %.o,$^) $(LIBS)\n+\n $(LIB_FILE): $(LIB_OBJS)\n \t$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^\n \ndiff --git a/contrib/scalar/.gitignore b/contrib/scalar/.gitignore\nnew file mode 100644\nindex 00000000000..ff3d47e84d0\n--- /dev/null\n+++ b/contrib/scalar/.gitignore\n@@ -0,0 +1,2 @@\n+/*.exe\n+/scalar\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nnew file mode 100644\nindex 00000000000..f6f0036f0fa\n--- /dev/null\n+++ b/contrib/scalar/Makefile\n@@ -0,0 +1,34 @@\n+QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir\n+QUIET_SUBDIR1  =\n+\n+ifneq ($(findstring s,$(MAKEFLAGS)),s)\n+ifndef V\n+\tQUIET_SUBDIR0  = +@subdir=\n+\tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n+\t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n+else\n+\texport V\n+endif\n+endif\n+\n+all:\n+\n+include ../../config.mak.uname\n+-include ../../config.mak.autogen\n+-include ../../config.mak\n+\n+TARGETS = scalar$(X) scalar.o\n+GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n+\n+all: scalar$(X)\n+\n+$(GITLIBS):\n+\t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)\n+\n+$(TARGETS): $(GITLIBS) scalar.c\n+\t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)\n+\n+clean:\n+\t$(RM) $(TARGETS)\n+\n+.PHONY: $(GITLIBS) all clean FORCE\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nnew file mode 100644\nindex 00000000000..7cff29e0fcd\n--- /dev/null\n+++ b/contrib/scalar/scalar.c\n@@ -0,0 +1,36 @@\n+/*\n+ * The Scalar command-line interface.\n+ */\n+\n+#include \"cache.h\"\n+#include \"gettext.h\"\n+#include \"parse-options.h\"\n+\n+static struct {\n+\tconst char *name;\n+\tint (*fn)(int, const char **);\n+} builtins[] = {\n+\t{ NULL, NULL},\n+};\n+\n+int cmd_main(int argc, const char **argv)\n+{\n+\tstruct strbuf scalar_usage = STRBUF_INIT;\n+\tint i;\n+\n+\tif (argc > 1) {\n+\t\targv++;\n+\t\targc--;\n+\n+\t\tfor (i = 0; builtins[i].name; i++)\n+\t\t\tif (!strcmp(builtins[i].name, argv[0]))\n+\t\t\t\treturn !!builtins[i].fn(argc, argv);\n+\t}\n+\n+\tstrbuf_addstr(&scalar_usage,\n+\t\t      N_(\"scalar <command> [<options>]\\n\\nCommands:\\n\"));\n+\tfor (i = 0; builtins[i].name; i++)\n+\t\tstrbuf_addf(&scalar_usage, \"\\t%s\\n\", builtins[i].name);\n+\n+\tusage(scalar_usage.buf);\n+}\n-- \ngitgitgadget\n\n"},{"id":"441483","messageId":"d80627615f82051919ce3c5f89d2e42ce63ad8fd.1637158762.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v7.git.1637158762.gitgitgadget@gmail.com","subject":"[PATCH v7 03/17] scalar: start documenting the command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-17T14:19:08Z","receivedAt":"2021-11-17T14:20:02Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nLet's build up the documentation for the Scalar command along with the\npatches that implement its functionality.\n\nNote: To discourage the feature-incomplete documentation from being\nmistaken for the complete thing, we do not yet provide any way to build\nHTML or manual pages from the text file.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.txt | 38 ++++++++++++++++++++++++++++++++++++++\n 1 file changed, 38 insertions(+)\n create mode 100644 contrib/scalar/scalar.txt\n\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nnew file mode 100644\nindex 00000000000..5f7131861a5\n--- /dev/null\n+++ b/contrib/scalar/scalar.txt\n@@ -0,0 +1,38 @@\n+scalar(1)\n+=========\n+\n+NAME\n+----\n+scalar - an opinionated repository management tool\n+\n+SYNOPSIS\n+--------\n+[verse]\n+scalar <command> [<options>]\n+\n+DESCRIPTION\n+-----------\n+\n+Scalar is an opinionated repository management tool. By creating new\n+repositories or registering existing repositories with Scalar, your Git\n+experience will speed up. Scalar sets advanced Git config settings,\n+maintains your repositories in the background, and helps reduce data sent\n+across the network.\n+\n+An important Scalar concept is the enlistment: this is the top-level directory\n+of the project. It usually contains the subdirectory `src/` which is a Git\n+worktree. This encourages the separation between tracked files (inside `src/`)\n+and untracked files, such as build artifacts (outside `src/`). When registering\n+an existing Git worktree with Scalar whose name is not `src`, the enlistment\n+will be identical to the worktree.\n+\n+The `scalar` command implements various subcommands, and different options\n+depending on the subcommand.\n+\n+SEE ALSO\n+--------\n+linkgit:git-maintenance[1].\n+\n+Scalar\n+---\n+Associated with the linkgit:git[1] suite\n-- \ngitgitgadget\n\n"},{"id":"441484","messageId":"9da1616849e6aa33e68ae68876217eef07e503bc.1637158762.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v7.git.1637158762.gitgitgadget@gmail.com","subject":"[PATCH v7 04/17] scalar: create test infrastructure","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-17T14:19:09Z","receivedAt":"2021-11-17T14:20:03Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nTo test the Scalar command, create a test script in contrib/scalar/t\nthat is executed as `make -C contrib/scalar test`. Since Scalar has no\nmeaningful capabilities yet, the only test is rather simple. We will add\nmore tests in subsequent commits that introduce corresponding, new\nfunctionality.\n\nNote: This test script is intended to test `scalar` only lightly, even\nafter all of the functionality is implemented.\n\nA more comprehensive functional (or: integration) test suite can be\nfound at https://github.com/microsoft/scalar; It is used in the workflow\nhttps://github.com/microsoft/git/blob/HEAD/.github/workflows/scalar-functional-tests.yml\nin Microsoft's Git fork. This test suite performs end-to-end tests with\na real remote repository, and is run as part of the regular CI and PR\nbuilds in that fork.\n\nSince those tests require some functionality supported only by\nMicrosoft's Git fork (\"GVFS protocol\"), there is no intention to port\nthat fuller test suite to `contrib/scalar/`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/Makefile          | 17 +++++--\n contrib/scalar/t/Makefile        | 78 ++++++++++++++++++++++++++++++++\n contrib/scalar/t/t9099-scalar.sh | 17 +++++++\n 3 files changed, 109 insertions(+), 3 deletions(-)\n create mode 100644 contrib/scalar/t/Makefile\n create mode 100755 contrib/scalar/t/t9099-scalar.sh\n\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nindex f6f0036f0fa..231b1ee1796 100644\n--- a/contrib/scalar/Makefile\n+++ b/contrib/scalar/Makefile\n@@ -3,6 +3,7 @@ QUIET_SUBDIR1  =\n \n ifneq ($(findstring s,$(MAKEFLAGS)),s)\n ifndef V\n+\tQUIET_GEN      = @echo '   ' GEN $@;\n \tQUIET_SUBDIR0  = +@subdir=\n \tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n \t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n@@ -20,7 +21,7 @@ include ../../config.mak.uname\n TARGETS = scalar$(X) scalar.o\n GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n \n-all: scalar$(X)\n+all: scalar$(X) ../../bin-wrappers/scalar\n \n $(GITLIBS):\n \t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)\n@@ -29,6 +30,16 @@ $(TARGETS): $(GITLIBS) scalar.c\n \t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)\n \n clean:\n-\t$(RM) $(TARGETS)\n+\t$(RM) $(TARGETS) ../../bin-wrappers/scalar\n \n-.PHONY: $(GITLIBS) all clean FORCE\n+../../bin-wrappers/scalar: ../../wrap-for-bin.sh Makefile\n+\t@mkdir -p ../../bin-wrappers\n+\t$(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \\\n+\t     -e 's|@@BUILD_DIR@@|$(shell cd ../.. && pwd)|' \\\n+\t     -e 's|@@PROG@@|contrib/scalar/scalar$(X)|' < $< > $@ && \\\n+\tchmod +x $@\n+\n+test: all\n+\t$(MAKE) -C t\n+\n+.PHONY: $(GITLIBS) all clean test FORCE\ndiff --git a/contrib/scalar/t/Makefile b/contrib/scalar/t/Makefile\nnew file mode 100644\nindex 00000000000..6170672bb37\n--- /dev/null\n+++ b/contrib/scalar/t/Makefile\n@@ -0,0 +1,78 @@\n+# Run scalar tests\n+#\n+# Copyright (c) 2005,2021 Junio C Hamano, Johannes Schindelin\n+#\n+\n+-include ../../../config.mak.autogen\n+-include ../../../config.mak\n+\n+SHELL_PATH ?= $(SHELL)\n+PERL_PATH ?= /usr/bin/perl\n+RM ?= rm -f\n+PROVE ?= prove\n+DEFAULT_TEST_TARGET ?= test\n+TEST_LINT ?= test-lint\n+\n+ifdef TEST_OUTPUT_DIRECTORY\n+TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results\n+else\n+TEST_RESULTS_DIRECTORY = ../../../t/test-results\n+endif\n+\n+# Shell quote;\n+SHELL_PATH_SQ = $(subst ','\\'',$(SHELL_PATH))\n+PERL_PATH_SQ = $(subst ','\\'',$(PERL_PATH))\n+TEST_RESULTS_DIRECTORY_SQ = $(subst ','\\'',$(TEST_RESULTS_DIRECTORY))\n+\n+T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))\n+\n+all: $(DEFAULT_TEST_TARGET)\n+\n+test: $(TEST_LINT)\n+\t$(MAKE) aggregate-results-and-cleanup\n+\n+prove: $(TEST_LINT)\n+\t@echo \"*** prove ***\"; GIT_CONFIG=.git/config $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)\n+\t$(MAKE) clean-except-prove-cache\n+\n+$(T):\n+\t@echo \"*** $@ ***\"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)\n+\n+clean-except-prove-cache:\n+\t$(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)'\n+\t$(RM) -r valgrind/bin\n+\n+clean: clean-except-prove-cache\n+\t$(RM) .prove\n+\n+test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax\n+\n+test-lint-duplicates:\n+\t@dups=`echo $(T) | tr ' ' '\\n' | sed 's/-.*//' | sort | uniq -d` && \\\n+\t\ttest -z \"$$dups\" || { \\\n+\t\techo >&2 \"duplicate test numbers:\" $$dups; exit 1; }\n+\n+test-lint-executable:\n+\t@bad=`for i in $(T); do test -x \"$$i\" || echo $$i; done` && \\\n+\t\ttest -z \"$$bad\" || { \\\n+\t\techo >&2 \"non-executable tests:\" $$bad; exit 1; }\n+\n+test-lint-shell-syntax:\n+\t@'$(PERL_PATH_SQ)' ../../../t/check-non-portable-shell.pl $(T)\n+\n+aggregate-results-and-cleanup: $(T)\n+\t$(MAKE) aggregate-results\n+\t$(MAKE) clean\n+\n+aggregate-results:\n+\tfor f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \\\n+\t\techo \"$$f\"; \\\n+\tdone | '$(SHELL_PATH_SQ)' ../../../t/aggregate-results.sh\n+\n+valgrind:\n+\t$(MAKE) GIT_TEST_OPTS=\"$(GIT_TEST_OPTS) --valgrind\"\n+\n+test-results:\n+\tmkdir -p test-results\n+\n+.PHONY: $(T) aggregate-results clean valgrind\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nnew file mode 100755\nindex 00000000000..16f2b72b126\n--- /dev/null\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -0,0 +1,17 @@\n+#!/bin/sh\n+\n+test_description='test the `scalar` command'\n+\n+TEST_DIRECTORY=$PWD/../../../t\n+export TEST_DIRECTORY\n+\n+# Make it work with --no-bin-wrappers\n+PATH=$PWD/..:$PATH\n+\n+. ../../../t/test-lib.sh\n+\n+test_expect_success 'scalar shows a usage' '\n+\ttest_expect_code 129 scalar -h\n+'\n+\n+test_done\n-- \ngitgitgadget\n\n"},{"id":"441485","messageId":"dbaad4753c156487e91b766ebd3c9a39d68c1b12.1637158762.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v7.git.1637158762.gitgitgadget@gmail.com","subject":"[PATCH v7 05/17] cmake: optionally build `scalar`, too","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-17T14:19:10Z","receivedAt":"2021-11-17T14:20:04Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe CMake configuration unfortunately does not let us encapsulate all\n(or at least the vast majority) of Scalar's build definition in the\n`contrib/scalar/` subdirectory.\n\nTo alleviate that somewhat, we guard the inclusion of Scalar via the\n`INCLUDE_SCALAR` environment variable.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/buildsystems/CMakeLists.txt | 14 ++++++++++++++\n 1 file changed, 14 insertions(+)\n\ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex fd1399c440f..dd7496b0322 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -729,6 +729,13 @@ if(CURL_FOUND)\n \tendif()\n endif()\n \n+if(DEFINED ENV{INCLUDE_SCALAR} AND NOT ENV{INCLUDE_SCALAR} STREQUAL \"\")\n+\tadd_executable(scalar ${CMAKE_SOURCE_DIR}/contrib/scalar/scalar.c)\n+\ttarget_link_libraries(scalar common-main)\n+\tset_target_properties(scalar PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/contrib/scalar)\n+\tset_target_properties(scalar PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/contrib/scalar)\n+endif()\n+\n parse_makefile_for_executables(git_builtin_extra \"BUILT_INS\")\n \n option(SKIP_DASHED_BUILT_INS \"Skip hardlinking the dashed versions of the built-ins\")\n@@ -953,6 +960,13 @@ string(REPLACE \"@@BUILD_DIR@@\" \"${CMAKE_BINARY_DIR}\" content \"${content}\")\n string(REPLACE \"@@PROG@@\" \"git-cvsserver\" content \"${content}\")\n file(WRITE ${CMAKE_BINARY_DIR}/bin-wrappers/git-cvsserver ${content})\n \n+if(DEFINED ENV{INCLUDE_SCALAR} AND NOT ENV{INCLUDE_SCALAR} STREQUAL \"\")\n+\tfile(STRINGS ${CMAKE_SOURCE_DIR}/wrap-for-bin.sh content NEWLINE_CONSUME)\n+\tstring(REPLACE \"@@BUILD_DIR@@\" \"${CMAKE_BINARY_DIR}\" content \"${content}\")\n+\tstring(REPLACE \"@@PROG@@\" \"contrib/scalar/scalar${EXE_EXTENSION}\" content \"${content}\")\n+\tfile(WRITE ${CMAKE_BINARY_DIR}/bin-wrappers/scalar ${content})\n+endif()\n+\n #options for configuring test options\n option(PERL_TESTS \"Perform tests that use perl\" ON)\n option(PYTHON_TESTS \"Perform tests that use python\" ON)\n-- \ngitgitgadget\n\n"},{"id":"441486","messageId":"dfa0c4709890acb522f791c84af37bed71a4ba1d.1637158762.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v7.git.1637158762.gitgitgadget@gmail.com","subject":"[PATCH v7 10/17] scalar: implement 'scalar list'","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-17T14:19:15Z","receivedAt":"2021-11-17T14:20:09Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe produced list simply consists of those repositories registered under\nthe multi-valued `scalar.repo` config setting in the user's Git config.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 11 +++++++++++\n contrib/scalar/scalar.txt | 11 ++++++++++-\n 2 files changed, 21 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 097d3bd478b..4feacd8d62b 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -251,6 +251,16 @@ static int unregister_dir(void)\n \treturn res;\n }\n \n+static int cmd_list(int argc, const char **argv)\n+{\n+\tif (argc != 1)\n+\t\tdie(_(\"`scalar list` does not take arguments\"));\n+\n+\tif (run_git(\"config\", \"--global\", \"--get-all\", \"scalar.repo\", NULL) < 0)\n+\t\treturn -1;\n+\treturn 0;\n+}\n+\n static int cmd_register(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -337,6 +347,7 @@ static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n \t{ NULL, NULL},\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex d9a79984492..f93e3d00efd 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,6 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n+scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n \n@@ -28,11 +29,19 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand.\n+depending on the subcommand. With the exception of `list`, all subcommands\n+expect to be run in an enlistment.\n \n COMMANDS\n --------\n \n+List\n+~~~~\n+\n+list::\n+\tList enlistments that are currently registered by Scalar. This\n+\tsubcommand does not need to be run inside an enlistment.\n+\n Register\n ~~~~~~~~\n \n-- \ngitgitgadget\n\n"},{"id":"441487","messageId":"febefe398869cff60d8295ae23d8b9449273d4a3.1637158762.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v7.git.1637158762.gitgitgadget@gmail.com","subject":"[PATCH v7 11/17] scalar: implement the `clone` subcommand","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-17T14:19:16Z","receivedAt":"2021-11-17T14:20:24Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis implements Scalar's opinionated `clone` command: it tries to use a\npartial clone and sets up a sparse checkout by default. In contrast to\n`git clone`, `scalar clone` sets up the worktree in the `src/`\nsubdirectory, to encourage a separation between the source files and the\nbuild output (which helps Git tremendously because it avoids untracked\nfiles that have to be specifically ignored when refreshing the index).\n\nAlso, it registers the repository for regular, scheduled maintenance,\nand configures a flurry of configuration settings based on the\nexperience and experiments of the Microsoft Windows and the Microsoft\nOffice development teams.\n\nNote: since the `scalar clone` command is by far the most commonly\ncalled `scalar` subcommand, we document it at the top of the manual\npage.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 201 +++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        |  32 ++++-\n contrib/scalar/t/t9099-scalar.sh |  32 +++++\n 3 files changed, 262 insertions(+), 3 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 4feacd8d62b..43f83dde33b 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -7,6 +7,7 @@\n #include \"parse-options.h\"\n #include \"config.h\"\n #include \"run-command.h\"\n+#include \"refs.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -251,6 +252,205 @@ static int unregister_dir(void)\n \treturn res;\n }\n \n+/* printf-style interface, expects `<key>=<value>` argument */\n+static int set_config(const char *fmt, ...)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tchar *value;\n+\tint res;\n+\tva_list args;\n+\n+\tva_start(args, fmt);\n+\tstrbuf_vaddf(&buf, fmt, args);\n+\tva_end(args);\n+\n+\tvalue = strchr(buf.buf, '=');\n+\tif (value)\n+\t\t*(value++) = '\\0';\n+\tres = git_config_set_gently(buf.buf, value);\n+\tstrbuf_release(&buf);\n+\n+\treturn res;\n+}\n+\n+static char *remote_default_branch(const char *url)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tstruct strbuf out = STRBUF_INIT;\n+\n+\tcp.git_cmd = 1;\n+\tstrvec_pushl(&cp.args, \"ls-remote\", \"--symref\", url, \"HEAD\", NULL);\n+\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n+\t\tconst char *line = out.buf;\n+\n+\t\twhile (*line) {\n+\t\t\tconst char *eol = strchrnul(line, '\\n'), *p;\n+\t\t\tsize_t len = eol - line;\n+\t\t\tchar *branch;\n+\n+\t\t\tif (!skip_prefix(line, \"ref: \", &p) ||\n+\t\t\t    !strip_suffix_mem(line, &len, \"\\tHEAD\")) {\n+\t\t\t\tline = eol + (*eol == '\\n');\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\teol = line + len;\n+\t\t\tif (skip_prefix(p, \"refs/heads/\", &p)) {\n+\t\t\t\tbranch = xstrndup(p, eol - p);\n+\t\t\t\tstrbuf_release(&out);\n+\t\t\t\treturn branch;\n+\t\t\t}\n+\n+\t\t\terror(_(\"remote HEAD is not a branch: '%.*s'\"),\n+\t\t\t      (int)(eol - p), p);\n+\t\t\tstrbuf_release(&out);\n+\t\t\treturn NULL;\n+\t\t}\n+\t}\n+\twarning(_(\"failed to get default branch name from remote; \"\n+\t\t  \"using local default\"));\n+\tstrbuf_reset(&out);\n+\n+\tchild_process_init(&cp);\n+\tcp.git_cmd = 1;\n+\tstrvec_pushl(&cp.args, \"symbolic-ref\", \"--short\", \"HEAD\", NULL);\n+\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n+\t\tstrbuf_trim(&out);\n+\t\treturn strbuf_detach(&out, NULL);\n+\t}\n+\n+\tstrbuf_release(&out);\n+\terror(_(\"failed to get default branch name\"));\n+\treturn NULL;\n+}\n+\n+static int cmd_clone(int argc, const char **argv)\n+{\n+\tconst char *branch = NULL;\n+\tint full_clone = 0;\n+\tstruct option clone_options[] = {\n+\t\tOPT_STRING('b', \"branch\", &branch, N_(\"<branch>\"),\n+\t\t\t   N_(\"branch to checkout after clone\")),\n+\t\tOPT_BOOL(0, \"full-clone\", &full_clone,\n+\t\t\t N_(\"when cloning, create full working directory\")),\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const clone_usage[] = {\n+\t\tN_(\"scalar clone [<options>] [--] <repo> [<dir>]\"),\n+\t\tNULL\n+\t};\n+\tconst char *url;\n+\tchar *enlistment = NULL, *dir = NULL;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint res;\n+\n+\targc = parse_options(argc, argv, NULL, clone_options, clone_usage, 0);\n+\n+\tif (argc == 2) {\n+\t\turl = argv[0];\n+\t\tenlistment = xstrdup(argv[1]);\n+\t} else if (argc == 1) {\n+\t\turl = argv[0];\n+\n+\t\tstrbuf_addstr(&buf, url);\n+\t\t/* Strip trailing slashes, if any */\n+\t\twhile (buf.len > 0 && is_dir_sep(buf.buf[buf.len - 1]))\n+\t\t\tstrbuf_setlen(&buf, buf.len - 1);\n+\t\t/* Strip suffix `.git`, if any */\n+\t\tstrbuf_strip_suffix(&buf, \".git\");\n+\n+\t\tenlistment = find_last_dir_sep(buf.buf);\n+\t\tif (!enlistment) {\n+\t\t\tdie(_(\"cannot deduce worktree name from '%s'\"), url);\n+\t\t}\n+\t\tenlistment = xstrdup(enlistment + 1);\n+\t} else {\n+\t\tusage_msg_opt(_(\"You must specify a repository to clone.\"),\n+\t\t\t      clone_usage, clone_options);\n+\t}\n+\n+\tif (is_directory(enlistment))\n+\t\tdie(_(\"directory '%s' exists already\"), enlistment);\n+\n+\tdir = xstrfmt(\"%s/src\", enlistment);\n+\n+\tstrbuf_reset(&buf);\n+\tif (branch)\n+\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", branch);\n+\telse {\n+\t\tchar *b = repo_default_branch_name(the_repository, 1);\n+\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", b);\n+\t\tfree(b);\n+\t}\n+\n+\tif ((res = run_git(\"-c\", buf.buf, \"init\", \"--\", dir, NULL)))\n+\t\tgoto cleanup;\n+\n+\tif (chdir(dir) < 0) {\n+\t\tres = error_errno(_(\"could not switch to '%s'\"), dir);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tsetup_git_directory();\n+\n+\t/* common-main already logs `argv` */\n+\ttrace2_def_repo(the_repository);\n+\n+\tif (!branch && !(branch = remote_default_branch(url))) {\n+\t\tres = error(_(\"failed to get default branch for '%s'\"), url);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tif (set_config(\"remote.origin.url=%s\", url) ||\n+\t    set_config(\"remote.origin.fetch=\"\n+\t\t       \"+refs/heads/*:refs/remotes/origin/*\") ||\n+\t    set_config(\"remote.origin.promisor=true\") ||\n+\t    set_config(\"remote.origin.partialCloneFilter=blob:none\")) {\n+\t\tres = error(_(\"could not configure remote in '%s'\"), dir);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tif (!full_clone &&\n+\t    (res = run_git(\"sparse-checkout\", \"init\", \"--cone\", NULL)))\n+\t\tgoto cleanup;\n+\n+\tif (set_recommended_config())\n+\t\treturn error(_(\"could not configure '%s'\"), dir);\n+\n+\tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL))) {\n+\t\twarning(_(\"partial clone failed; attempting full clone\"));\n+\n+\t\tif (set_config(\"remote.origin.promisor\") ||\n+\t\t    set_config(\"remote.origin.partialCloneFilter\")) {\n+\t\t\tres = error(_(\"could not configure for full clone\"));\n+\t\t\tgoto cleanup;\n+\t\t}\n+\n+\t\tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL)))\n+\t\t\tgoto cleanup;\n+\t}\n+\n+\tif ((res = set_config(\"branch.%s.remote=origin\", branch)))\n+\t\tgoto cleanup;\n+\tif ((res = set_config(\"branch.%s.merge=refs/heads/%s\",\n+\t\t\t      branch, branch)))\n+\t\tgoto cleanup;\n+\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addf(&buf, \"origin/%s\", branch);\n+\tres = run_git(\"checkout\", \"-f\", \"-t\", buf.buf, NULL);\n+\tif (res)\n+\t\tgoto cleanup;\n+\n+\tres = register_dir();\n+\n+cleanup:\n+\tfree(enlistment);\n+\tfree(dir);\n+\tstrbuf_release(&buf);\n+\treturn res;\n+}\n+\n static int cmd_list(int argc, const char **argv)\n {\n \tif (argc != 1)\n@@ -347,6 +547,7 @@ static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"clone\", cmd_clone },\n \t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex f93e3d00efd..e8730967f16 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,6 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n+scalar clone [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n@@ -29,12 +30,37 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand. With the exception of `list`, all subcommands\n-expect to be run in an enlistment.\n+depending on the subcommand. With the exception of `clone` and `list`, all\n+subcommands expect to be run in an enlistment.\n \n COMMANDS\n --------\n \n+Clone\n+~~~~~\n+\n+clone [<options>] <url> [<enlistment>]::\n+\tClones the specified repository, similar to linkgit:git-clone[1]. By\n+\tdefault, only commit and tree objects are cloned. Once finished, the\n+\tworktree is located at `<enlistment>/src`.\n++\n+The sparse-checkout feature is enabled (except when run with `--full-clone`)\n+and the only files present are those in the top-level directory. Use\n+`git sparse-checkout set` to expand the set of directories you want to see,\n+or `git sparse-checkout disable` to expand to all files (see\n+linkgit:git-sparse-checkout[1] for more details). You can explore the\n+subdirectories outside your sparse-checkout by using `git ls-tree\n+HEAD[:<directory>]`.\n+\n+-b <name>::\n+--branch <name>::\n+\tInstead of checking out the branch pointed to by the cloned\n+\trepository's HEAD, check out the `<name>` branch instead.\n+\n+--[no-]full-clone::\n+\tA sparse-checkout is initialized by default. This behavior can be\n+\tturned off via `--full-clone`.\n+\n List\n ~~~~\n \n@@ -64,7 +90,7 @@ unregister [<enlistment>]::\n \n SEE ALSO\n --------\n-linkgit:git-maintenance[1].\n+linkgit:git-clone[1], linkgit:git-maintenance[1].\n \n Scalar\n ---\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex ef0e8d680d5..984d69e8f75 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -10,6 +10,9 @@ PATH=$PWD/..:$PATH\n \n . ../../../t/test-lib.sh\n \n+GIT_TEST_MAINT_SCHEDULER=\"crontab:test-tool crontab ../cron.txt,launchctl:true,schtasks:true\"\n+export GIT_TEST_MAINT_SCHEDULER\n+\n test_expect_success 'scalar shows a usage' '\n \ttest_expect_code 129 scalar -h\n '\n@@ -29,4 +32,33 @@ test_expect_success 'scalar unregister' '\n \t! grep -F \"$(pwd)/vanish/src\" scalar.repos\n '\n \n+test_expect_success 'set up repository to clone' '\n+\ttest_commit first &&\n+\ttest_commit second &&\n+\ttest_commit third &&\n+\tgit switch -c parallel first &&\n+\tmkdir -p 1/2 &&\n+\ttest_commit 1/2/3 &&\n+\tgit config uploadPack.allowFilter true &&\n+\tgit config uploadPack.allowAnySHA1InWant true\n+'\n+\n+test_expect_success 'scalar clone' '\n+\tsecond=$(git rev-parse --verify second:second.t) &&\n+\tscalar clone \"file://$(pwd)\" cloned &&\n+\t(\n+\t\tcd cloned/src &&\n+\n+\t\tgit config --get --global --fixed-value maintenance.repo \\\n+\t\t\t\"$(pwd)\" &&\n+\n+\t\ttest_path_is_missing 1/2 &&\n+\t\ttest_must_fail git rev-list --missing=print $second &&\n+\t\tgit rev-list $second &&\n+\t\tgit cat-file blob $second >actual &&\n+\t\techo \"second\" >expect &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"441488","messageId":"9fea89cd161bfa6facdc142402b2098d2de463ef.1637158762.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v7.git.1637158762.gitgitgadget@gmail.com","subject":"[PATCH v7 08/17] scalar: 'unregister' stops background maintenance","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-17T14:19:13Z","receivedAt":"2021-11-17T14:20:24Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nJust like `scalar register` starts the scheduled background maintenance,\n`scalar unregister` stops it. Note that we use `git maintenance start`\nin `scalar register`, but we do not use `git maintenance stop` in\n`scalar unregister`: this would stop maintenance for _all_ repositories,\nnot just for the one we want to unregister.\n\nThe `unregister` command also removes the corresponding entry from the\n`[scalar]` section in the global Git config.\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 50 ++++++++++++++++++++++++++++++++-------\n contrib/scalar/scalar.txt |  8 +++++++\n 2 files changed, 50 insertions(+), 8 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 03d5f84c764..bab0271c37d 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -198,12 +198,12 @@ static int set_recommended_config(void)\n \treturn 0;\n }\n \n-static int start_maintenance(void)\n+static int toggle_maintenance(int enable)\n {\n-\treturn run_git(\"maintenance\", \"start\", NULL);\n+\treturn run_git(\"maintenance\", enable ? \"start\" : \"unregister\", NULL);\n }\n \n-static int add_enlistment(void)\n+static int add_or_remove_enlistment(int add)\n {\n \tint res;\n \n@@ -214,24 +214,39 @@ static int add_enlistment(void)\n \t\t      \"scalar.repo\", the_repository->worktree, NULL);\n \n \t/*\n-\t * If the setting is already there, then do nothing.\n+\t * If we want to add and the setting is already there, then do nothing.\n+\t * If we want to remove and the setting is not there, then do nothing.\n \t */\n-\tif (!res)\n+\tif ((add && !res) || (!add && res))\n \t\treturn 0;\n \n-\treturn run_git(\"config\", \"--global\", \"--add\",\n+\treturn run_git(\"config\", \"--global\", add ? \"--add\" : \"--unset\",\n+\t\t       add ? \"--no-fixed-value\" : \"--fixed-value\",\n \t\t       \"scalar.repo\", the_repository->worktree, NULL);\n }\n \n static int register_dir(void)\n {\n-\tint res = add_enlistment();\n+\tint res = add_or_remove_enlistment(1);\n \n \tif (!res)\n \t\tres = set_recommended_config();\n \n \tif (!res)\n-\t\tres = start_maintenance();\n+\t\tres = toggle_maintenance(1);\n+\n+\treturn res;\n+}\n+\n+static int unregister_dir(void)\n+{\n+\tint res = 0;\n+\n+\tif (toggle_maintenance(0) < 0)\n+\t\tres = -1;\n+\n+\tif (add_or_remove_enlistment(0) < 0)\n+\t\tres = -1;\n \n \treturn res;\n }\n@@ -254,11 +269,30 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_unregister(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar unregister [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn unregister_dir();\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n \t{ \"register\", cmd_register },\n+\t{ \"unregister\", cmd_unregister },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 568987064b2..d9a79984492 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -9,6 +9,7 @@ SYNOPSIS\n --------\n [verse]\n scalar register [<enlistment>]\n+scalar unregister [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -45,6 +46,13 @@ Note: when this subcommand is called in a worktree that is called `src/`, its\n parent directory is considered to be the Scalar enlistment. If the worktree is\n _not_ called `src/`, it itself will be considered to be the Scalar enlistment.\n \n+Unregister\n+~~~~~~~~~~\n+\n+unregister [<enlistment>]::\n+\tRemove the specified repository from the list of repositories\n+\tregistered with Scalar and stop the scheduled background maintenance.\n+\n SEE ALSO\n --------\n linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"441489","messageId":"5e077bf892b5fadb46bcf7c31cc2a4c6cf6b6200.1637158762.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v7.git.1637158762.gitgitgadget@gmail.com","subject":"[PATCH v7 09/17] scalar: let 'unregister' handle a deleted enlistment directory gracefully","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-17T14:19:14Z","receivedAt":"2021-11-17T14:20:24Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nWhen a user deleted an enlistment manually, let's be generous and\n_still_ unregister it.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 46 ++++++++++++++++++++++++++++++++\n contrib/scalar/t/t9099-scalar.sh | 15 +++++++++++\n 2 files changed, 61 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex bab0271c37d..097d3bd478b 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -269,6 +269,24 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int remove_deleted_enlistment(struct strbuf *path)\n+{\n+\tint res = 0;\n+\tstrbuf_realpath_forgiving(path, path->buf, 1);\n+\n+\tif (run_git(\"config\", \"--global\",\n+\t\t    \"--unset\", \"--fixed-value\",\n+\t\t    \"scalar.repo\", path->buf, NULL) < 0)\n+\t\tres = -1;\n+\n+\tif (run_git(\"config\", \"--global\",\n+\t\t    \"--unset\", \"--fixed-value\",\n+\t\t    \"maintenance.repo\", path->buf, NULL) < 0)\n+\t\tres = -1;\n+\n+\treturn res;\n+}\n+\n static int cmd_unregister(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -282,6 +300,34 @@ static int cmd_unregister(int argc, const char **argv)\n \targc = parse_options(argc, argv, NULL, options,\n \t\t\t     usage, 0);\n \n+\t/*\n+\t * Be forgiving when the enlistment or worktree does not even exist any\n+\t * longer; This can be the case if a user deleted the worktree by\n+\t * mistake and _still_ wants to unregister the thing.\n+\t */\n+\tif (argc == 1) {\n+\t\tstruct strbuf src_path = STRBUF_INIT, workdir_path = STRBUF_INIT;\n+\n+\t\tstrbuf_addf(&src_path, \"%s/src/.git\", argv[0]);\n+\t\tstrbuf_addf(&workdir_path, \"%s/.git\", argv[0]);\n+\t\tif (!is_directory(src_path.buf) && !is_directory(workdir_path.buf)) {\n+\t\t\t/* remove possible matching registrations */\n+\t\t\tint res = -1;\n+\n+\t\t\tstrbuf_strip_suffix(&src_path, \"/.git\");\n+\t\t\tres = remove_deleted_enlistment(&src_path) && res;\n+\n+\t\t\tstrbuf_strip_suffix(&workdir_path, \"/.git\");\n+\t\t\tres = remove_deleted_enlistment(&workdir_path) && res;\n+\n+\t\t\tstrbuf_release(&src_path);\n+\t\t\tstrbuf_release(&workdir_path);\n+\t\t\treturn res;\n+\t\t}\n+\t\tstrbuf_release(&src_path);\n+\t\tstrbuf_release(&workdir_path);\n+\t}\n+\n \tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n \n \treturn unregister_dir();\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 16f2b72b126..ef0e8d680d5 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -14,4 +14,19 @@ test_expect_success 'scalar shows a usage' '\n \ttest_expect_code 129 scalar -h\n '\n \n+test_expect_success 'scalar unregister' '\n+\tgit init vanish/src &&\n+\tscalar register vanish/src &&\n+\tgit config --get --global --fixed-value \\\n+\t\tmaintenance.repo \"$(pwd)/vanish/src\" &&\n+\tscalar list >scalar.repos &&\n+\tgrep -F \"$(pwd)/vanish/src\" scalar.repos &&\n+\trm -rf vanish/src/.git &&\n+\tscalar unregister vanish &&\n+\ttest_must_fail git config --get --global --fixed-value \\\n+\t\tmaintenance.repo \"$(pwd)/vanish/src\" &&\n+\tscalar list >scalar.repos &&\n+\t! grep -F \"$(pwd)/vanish/src\" scalar.repos\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"441490","messageId":"2677bcff335a7e408f5ea786343e765e71837ba7.1637158762.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v7.git.1637158762.gitgitgadget@gmail.com","subject":"[PATCH v7 12/17] scalar: teach 'clone' to support the --single-branch option","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-17T14:19:17Z","receivedAt":"2021-11-17T14:20:28Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nJust like `git clone`, the `scalar clone` command now also offers to\nrestrict the clone to a single branch.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          |  9 +++++++--\n contrib/scalar/scalar.txt        | 12 +++++++++++-\n contrib/scalar/t/t9099-scalar.sh |  6 +++++-\n 3 files changed, 23 insertions(+), 4 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 43f83dde33b..516a75be3c4 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -327,12 +327,15 @@ static char *remote_default_branch(const char *url)\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n-\tint full_clone = 0;\n+\tint full_clone = 0, single_branch = 0;\n \tstruct option clone_options[] = {\n \t\tOPT_STRING('b', \"branch\", &branch, N_(\"<branch>\"),\n \t\t\t   N_(\"branch to checkout after clone\")),\n \t\tOPT_BOOL(0, \"full-clone\", &full_clone,\n \t\t\t N_(\"when cloning, create full working directory\")),\n+\t\tOPT_BOOL(0, \"single-branch\", &single_branch,\n+\t\t\t N_(\"only download metadata for the branch that will \"\n+\t\t\t    \"be checked out\")),\n \t\tOPT_END(),\n \t};\n \tconst char * const clone_usage[] = {\n@@ -403,7 +406,9 @@ static int cmd_clone(int argc, const char **argv)\n \n \tif (set_config(\"remote.origin.url=%s\", url) ||\n \t    set_config(\"remote.origin.fetch=\"\n-\t\t       \"+refs/heads/*:refs/remotes/origin/*\") ||\n+\t\t       \"+refs/heads/%s:refs/remotes/origin/%s\",\n+\t\t       single_branch ? branch : \"*\",\n+\t\t       single_branch ? branch : \"*\") ||\n \t    set_config(\"remote.origin.promisor=true\") ||\n \t    set_config(\"remote.origin.partialCloneFilter=blob:none\")) {\n \t\tres = error(_(\"could not configure remote in '%s'\"), dir);\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex e8730967f16..56f744a4aa9 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,7 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n-scalar clone [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n+scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n@@ -57,6 +57,16 @@ HEAD[:<directory>]`.\n \tInstead of checking out the branch pointed to by the cloned\n \trepository's HEAD, check out the `<name>` branch instead.\n \n+--[no-]single-branch::\n+\tClone only the history leading to the tip of a single branch, either\n+\tspecified by the `--branch` option or the primary branch remote's\n+\t`HEAD` points at.\n++\n+Further fetches into the resulting repository will only update the\n+remote-tracking branch for the branch this option was used for the initial\n+cloning. If the HEAD at the remote did not point at any branch when\n+`--single-branch` clone was made, no remote-tracking branch is created.\n+\n --[no-]full-clone::\n \tA sparse-checkout is initialized by default. This behavior can be\n \tturned off via `--full-clone`.\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 984d69e8f75..f60e086d6f9 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -45,13 +45,17 @@ test_expect_success 'set up repository to clone' '\n \n test_expect_success 'scalar clone' '\n \tsecond=$(git rev-parse --verify second:second.t) &&\n-\tscalar clone \"file://$(pwd)\" cloned &&\n+\tscalar clone \"file://$(pwd)\" cloned --single-branch &&\n \t(\n \t\tcd cloned/src &&\n \n \t\tgit config --get --global --fixed-value maintenance.repo \\\n \t\t\t\"$(pwd)\" &&\n \n+\t\tgit for-each-ref --format=\"%(refname)\" refs/remotes/origin/ >actual &&\n+\t\techo \"refs/remotes/origin/parallel\" >expect &&\n+\t\ttest_cmp expect actual &&\n+\n \t\ttest_path_is_missing 1/2 &&\n \t\ttest_must_fail git rev-list --missing=print $second &&\n \t\tgit rev-list $second &&\n-- \ngitgitgadget\n\n"},{"id":"441491","messageId":"69e2242240be441122e6526e55ffbff516cedba7.1637158762.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v7.git.1637158762.gitgitgadget@gmail.com","subject":"[PATCH v7 14/17] scalar: allow reconfiguring an existing enlistment","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-17T14:19:19Z","receivedAt":"2021-11-17T14:20:31Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis comes in handy during Scalar upgrades, or when config settings were\nmessed up by mistake.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 79 +++++++++++++++++++++-----------\n contrib/scalar/scalar.txt        |  8 ++++\n contrib/scalar/t/t9099-scalar.sh |  8 ++++\n 3 files changed, 67 insertions(+), 28 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex ca524576011..b799decbc2f 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -108,18 +108,20 @@ static int run_git(const char *arg, ...)\n \treturn res;\n }\n \n-static int set_recommended_config(void)\n+static int set_recommended_config(int reconfigure)\n {\n \tstruct {\n \t\tconst char *key;\n \t\tconst char *value;\n+\t\tint overwrite_on_reconfigure;\n \t} config[] = {\n-\t\t{ \"am.keepCR\", \"true\" },\n-\t\t{ \"core.FSCache\", \"true\" },\n-\t\t{ \"core.multiPackIndex\", \"true\" },\n-\t\t{ \"core.preloadIndex\", \"true\" },\n+\t\t/* Required */\n+\t\t{ \"am.keepCR\", \"true\", 1 },\n+\t\t{ \"core.FSCache\", \"true\", 1 },\n+\t\t{ \"core.multiPackIndex\", \"true\", 1 },\n+\t\t{ \"core.preloadIndex\", \"true\", 1 },\n #ifndef WIN32\n-\t\t{ \"core.untrackedCache\", \"true\" },\n+\t\t{ \"core.untrackedCache\", \"true\", 1 },\n #else\n \t\t/*\n \t\t * Unfortunately, Scalar's Functional Tests demonstrated\n@@ -133,28 +135,29 @@ static int set_recommended_config(void)\n \t\t * Therefore, with a sad heart, we disable this very useful\n \t\t * feature on Windows.\n \t\t */\n-\t\t{ \"core.untrackedCache\", \"false\" },\n+\t\t{ \"core.untrackedCache\", \"false\", 1 },\n #endif\n-\t\t{ \"core.logAllRefUpdates\", \"true\" },\n-\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n-\t\t{ \"credential.validate\", \"false\" }, /* GCM4W-only */\n-\t\t{ \"gc.auto\", \"0\" },\n-\t\t{ \"gui.GCWarning\", \"false\" },\n-\t\t{ \"index.threads\", \"true\" },\n-\t\t{ \"index.version\", \"4\" },\n-\t\t{ \"merge.stat\", \"false\" },\n-\t\t{ \"merge.renames\", \"false\" },\n-\t\t{ \"pack.useBitmaps\", \"false\" },\n-\t\t{ \"pack.useSparse\", \"true\" },\n-\t\t{ \"receive.autoGC\", \"false\" },\n-\t\t{ \"reset.quiet\", \"true\" },\n-\t\t{ \"feature.manyFiles\", \"false\" },\n-\t\t{ \"feature.experimental\", \"false\" },\n-\t\t{ \"fetch.unpackLimit\", \"1\" },\n-\t\t{ \"fetch.writeCommitGraph\", \"false\" },\n+\t\t{ \"core.logAllRefUpdates\", \"true\", 1 },\n+\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\", 1 },\n+\t\t{ \"credential.validate\", \"false\", 1 }, /* GCM4W-only */\n+\t\t{ \"gc.auto\", \"0\", 1 },\n+\t\t{ \"gui.GCWarning\", \"false\", 1 },\n+\t\t{ \"index.threads\", \"true\", 1 },\n+\t\t{ \"index.version\", \"4\", 1 },\n+\t\t{ \"merge.stat\", \"false\", 1 },\n+\t\t{ \"merge.renames\", \"false\", 1 },\n+\t\t{ \"pack.useBitmaps\", \"false\", 1 },\n+\t\t{ \"pack.useSparse\", \"true\", 1 },\n+\t\t{ \"receive.autoGC\", \"false\", 1 },\n+\t\t{ \"reset.quiet\", \"true\", 1 },\n+\t\t{ \"feature.manyFiles\", \"false\", 1 },\n+\t\t{ \"feature.experimental\", \"false\", 1 },\n+\t\t{ \"fetch.unpackLimit\", \"1\", 1 },\n+\t\t{ \"fetch.writeCommitGraph\", \"false\", 1 },\n #ifdef WIN32\n-\t\t{ \"http.sslBackend\", \"schannel\" },\n+\t\t{ \"http.sslBackend\", \"schannel\", 1 },\n #endif\n+\t\t/* Optional */\n \t\t{ \"status.aheadBehind\", \"false\" },\n \t\t{ \"commitGraph.generationVersion\", \"1\" },\n \t\t{ \"core.autoCRLF\", \"false\" },\n@@ -166,7 +169,8 @@ static int set_recommended_config(void)\n \tchar *value;\n \n \tfor (i = 0; config[i].key; i++) {\n-\t\tif (git_config_get_string(config[i].key, &value)) {\n+\t\tif ((reconfigure && config[i].overwrite_on_reconfigure) ||\n+\t\t    git_config_get_string(config[i].key, &value)) {\n \t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"created\");\n \t\t\tif (git_config_set_gently(config[i].key,\n \t\t\t\t\t\t  config[i].value) < 0)\n@@ -231,7 +235,7 @@ static int register_dir(void)\n \tint res = add_or_remove_enlistment(1);\n \n \tif (!res)\n-\t\tres = set_recommended_config();\n+\t\tres = set_recommended_config(0);\n \n \tif (!res)\n \t\tres = toggle_maintenance(1);\n@@ -419,7 +423,7 @@ static int cmd_clone(int argc, const char **argv)\n \t    (res = run_git(\"sparse-checkout\", \"init\", \"--cone\", NULL)))\n \t\tgoto cleanup;\n \n-\tif (set_recommended_config())\n+\tif (set_recommended_config(0))\n \t\treturn error(_(\"could not configure '%s'\"), dir);\n \n \tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL))) {\n@@ -484,6 +488,24 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_reconfigure(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar reconfigure [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn set_recommended_config(1);\n+}\n+\n static int cmd_run(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -620,6 +642,7 @@ static struct {\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n \t{ \"run\", cmd_run },\n+\t{ \"reconfigure\", cmd_reconfigure },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 39143b08324..89fd7901585 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -13,6 +13,7 @@ scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n+scalar reconfigure <enlistment>\n \n DESCRIPTION\n -----------\n@@ -117,6 +118,13 @@ opinionated default settings that make Git work more efficiently with\n large repositories. As this task is run as part of `scalar clone`\n automatically, explicit invocations of this task are rarely needed.\n \n+Reconfigure\n+~~~~~~~~~~~\n+\n+After a Scalar upgrade, or when the configuration of a Scalar enlistment\n+was somehow corrupted or changed by mistake, this subcommand allows to\n+reconfigure the enlistment.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex f60e086d6f9..fb5e2efee0a 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -65,4 +65,12 @@ test_expect_success 'scalar clone' '\n \t)\n '\n \n+test_expect_success 'scalar reconfigure' '\n+\tgit init one/src &&\n+\tscalar register one &&\n+\tgit -C one/src config core.preloadIndex false &&\n+\tscalar reconfigure one &&\n+\ttest true = \"$(git -C one/src config core.preloadIndex)\"\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"441492","messageId":"cca604ef3261cc295ed06d6f675368d98d0334d6.1637158762.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v7.git.1637158762.gitgitgadget@gmail.com","subject":"[PATCH v7 07/17] scalar: 'register' sets recommended config and starts maintenance","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-17T14:19:12Z","receivedAt":"2021-11-17T14:20:32Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nLet's start implementing the `register` command. With this commit,\nrecommended settings are configured upon `scalar register`, and Git's\nbackground maintenance is started.\n\nThe recommended config settings may very well change in the future. For\nexample, once the built-in FSMonitor is available, we will want to\nenable it upon `scalar register`. For that reason, we explicitly support\nrunning `scalar register` in an already-registered enlistment.\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 249 ++++++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt |  18 ++-\n 2 files changed, 266 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 7cff29e0fcd..03d5f84c764 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -5,11 +5,260 @@\n #include \"cache.h\"\n #include \"gettext.h\"\n #include \"parse-options.h\"\n+#include \"config.h\"\n+#include \"run-command.h\"\n+\n+/*\n+ * Remove the deepest subdirectory in the provided path string. Path must not\n+ * include a trailing path separator. Returns 1 if parent directory found,\n+ * otherwise 0.\n+ */\n+static int strbuf_parent_directory(struct strbuf *buf)\n+{\n+\tsize_t len = buf->len;\n+\tsize_t offset = offset_1st_component(buf->buf);\n+\tchar *path_sep = find_last_dir_sep(buf->buf + offset);\n+\tstrbuf_setlen(buf, path_sep ? path_sep - buf->buf : offset);\n+\n+\treturn buf->len < len;\n+}\n+\n+static void setup_enlistment_directory(int argc, const char **argv,\n+\t\t\t\t       const char * const *usagestr,\n+\t\t\t\t       const struct option *options,\n+\t\t\t\t       struct strbuf *enlistment_root)\n+{\n+\tstruct strbuf path = STRBUF_INIT;\n+\tchar *root;\n+\tint enlistment_found = 0;\n+\n+\tif (startup_info->have_repository)\n+\t\tBUG(\"gitdir already set up?!?\");\n+\n+\tif (argc > 1)\n+\t\tusage_with_options(usagestr, options);\n+\n+\t/* find the worktree, determine its corresponding root */\n+\tif (argc == 1)\n+\t\tstrbuf_add_absolute_path(&path, argv[0]);\n+\telse if (strbuf_getcwd(&path) < 0)\n+\t\tdie(_(\"need a working directory\"));\n+\n+\tstrbuf_trim_trailing_dir_sep(&path);\n+\tdo {\n+\t\tconst size_t len = path.len;\n+\n+\t\t/* check if currently in enlistment root with src/ workdir */\n+\t\tstrbuf_addstr(&path, \"/src\");\n+\t\tif (is_nonbare_repository_dir(&path)) {\n+\t\t\tif (enlistment_root)\n+\t\t\t\tstrbuf_add(enlistment_root, path.buf, len);\n+\n+\t\t\tenlistment_found = 1;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\t/* reset to original path */\n+\t\tstrbuf_setlen(&path, len);\n+\n+\t\t/* check if currently in workdir */\n+\t\tif (is_nonbare_repository_dir(&path)) {\n+\t\t\tif (enlistment_root) {\n+\t\t\t\t/*\n+\t\t\t\t * If the worktree's directory's name is `src`, the enlistment is the\n+\t\t\t\t * parent directory, otherwise it is identical to the worktree.\n+\t\t\t\t */\n+\t\t\t\troot = strip_path_suffix(path.buf, \"src\");\n+\t\t\t\tstrbuf_addstr(enlistment_root, root ? root : path.buf);\n+\t\t\t\tfree(root);\n+\t\t\t}\n+\n+\t\t\tenlistment_found = 1;\n+\t\t\tbreak;\n+\t\t}\n+\t} while (strbuf_parent_directory(&path));\n+\n+\tif (!enlistment_found)\n+\t\tdie(_(\"could not find enlistment root\"));\n+\n+\tif (chdir(path.buf) < 0)\n+\t\tdie_errno(_(\"could not switch to '%s'\"), path.buf);\n+\n+\tstrbuf_release(&path);\n+\tsetup_git_directory();\n+}\n+\n+static int run_git(const char *arg, ...)\n+{\n+\tstruct strvec argv = STRVEC_INIT;\n+\tva_list args;\n+\tconst char *p;\n+\tint res;\n+\n+\tva_start(args, arg);\n+\tstrvec_push(&argv, arg);\n+\twhile ((p = va_arg(args, const char *)))\n+\t\tstrvec_push(&argv, p);\n+\tva_end(args);\n+\n+\tres = run_command_v_opt(argv.v, RUN_GIT_CMD);\n+\n+\tstrvec_clear(&argv);\n+\treturn res;\n+}\n+\n+static int set_recommended_config(void)\n+{\n+\tstruct {\n+\t\tconst char *key;\n+\t\tconst char *value;\n+\t} config[] = {\n+\t\t{ \"am.keepCR\", \"true\" },\n+\t\t{ \"core.FSCache\", \"true\" },\n+\t\t{ \"core.multiPackIndex\", \"true\" },\n+\t\t{ \"core.preloadIndex\", \"true\" },\n+#ifndef WIN32\n+\t\t{ \"core.untrackedCache\", \"true\" },\n+#else\n+\t\t/*\n+\t\t * Unfortunately, Scalar's Functional Tests demonstrated\n+\t\t * that the untracked cache feature is unreliable on Windows\n+\t\t * (which is a bummer because that platform would benefit the\n+\t\t * most from it). For some reason, freshly created files seem\n+\t\t * not to update the directory's `lastModified` time\n+\t\t * immediately, but the untracked cache would need to rely on\n+\t\t * that.\n+\t\t *\n+\t\t * Therefore, with a sad heart, we disable this very useful\n+\t\t * feature on Windows.\n+\t\t */\n+\t\t{ \"core.untrackedCache\", \"false\" },\n+#endif\n+\t\t{ \"core.logAllRefUpdates\", \"true\" },\n+\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n+\t\t{ \"credential.validate\", \"false\" }, /* GCM4W-only */\n+\t\t{ \"gc.auto\", \"0\" },\n+\t\t{ \"gui.GCWarning\", \"false\" },\n+\t\t{ \"index.threads\", \"true\" },\n+\t\t{ \"index.version\", \"4\" },\n+\t\t{ \"merge.stat\", \"false\" },\n+\t\t{ \"merge.renames\", \"false\" },\n+\t\t{ \"pack.useBitmaps\", \"false\" },\n+\t\t{ \"pack.useSparse\", \"true\" },\n+\t\t{ \"receive.autoGC\", \"false\" },\n+\t\t{ \"reset.quiet\", \"true\" },\n+\t\t{ \"feature.manyFiles\", \"false\" },\n+\t\t{ \"feature.experimental\", \"false\" },\n+\t\t{ \"fetch.unpackLimit\", \"1\" },\n+\t\t{ \"fetch.writeCommitGraph\", \"false\" },\n+#ifdef WIN32\n+\t\t{ \"http.sslBackend\", \"schannel\" },\n+#endif\n+\t\t{ \"status.aheadBehind\", \"false\" },\n+\t\t{ \"commitGraph.generationVersion\", \"1\" },\n+\t\t{ \"core.autoCRLF\", \"false\" },\n+\t\t{ \"core.safeCRLF\", \"false\" },\n+\t\t{ \"fetch.showForcedUpdates\", \"false\" },\n+\t\t{ NULL, NULL },\n+\t};\n+\tint i;\n+\tchar *value;\n+\n+\tfor (i = 0; config[i].key; i++) {\n+\t\tif (git_config_get_string(config[i].key, &value)) {\n+\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"created\");\n+\t\t\tif (git_config_set_gently(config[i].key,\n+\t\t\t\t\t\t  config[i].value) < 0)\n+\t\t\t\treturn error(_(\"could not configure %s=%s\"),\n+\t\t\t\t\t     config[i].key, config[i].value);\n+\t\t} else {\n+\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"exists\");\n+\t\t\tfree(value);\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * The `log.excludeDecoration` setting is special because it allows\n+\t * for multiple values.\n+\t */\n+\tif (git_config_get_string(\"log.excludeDecoration\", &value)) {\n+\t\ttrace2_data_string(\"scalar\", the_repository,\n+\t\t\t\t   \"log.excludeDecoration\", \"created\");\n+\t\tif (git_config_set_multivar_gently(\"log.excludeDecoration\",\n+\t\t\t\t\t\t   \"refs/prefetch/*\",\n+\t\t\t\t\t\t   CONFIG_REGEX_NONE, 0))\n+\t\t\treturn error(_(\"could not configure \"\n+\t\t\t\t       \"log.excludeDecoration\"));\n+\t} else {\n+\t\ttrace2_data_string(\"scalar\", the_repository,\n+\t\t\t\t   \"log.excludeDecoration\", \"exists\");\n+\t\tfree(value);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int start_maintenance(void)\n+{\n+\treturn run_git(\"maintenance\", \"start\", NULL);\n+}\n+\n+static int add_enlistment(void)\n+{\n+\tint res;\n+\n+\tif (!the_repository->worktree)\n+\t\tdie(_(\"Scalar enlistments require a worktree\"));\n+\n+\tres = run_git(\"config\", \"--global\", \"--get\", \"--fixed-value\",\n+\t\t      \"scalar.repo\", the_repository->worktree, NULL);\n+\n+\t/*\n+\t * If the setting is already there, then do nothing.\n+\t */\n+\tif (!res)\n+\t\treturn 0;\n+\n+\treturn run_git(\"config\", \"--global\", \"--add\",\n+\t\t       \"scalar.repo\", the_repository->worktree, NULL);\n+}\n+\n+static int register_dir(void)\n+{\n+\tint res = add_enlistment();\n+\n+\tif (!res)\n+\t\tres = set_recommended_config();\n+\n+\tif (!res)\n+\t\tres = start_maintenance();\n+\n+\treturn res;\n+}\n+\n+static int cmd_register(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar register [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn register_dir();\n+}\n \n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"register\", cmd_register },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 5f7131861a5..568987064b2 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,7 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n-scalar <command> [<options>]\n+scalar register [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -29,6 +29,22 @@ will be identical to the worktree.\n The `scalar` command implements various subcommands, and different options\n depending on the subcommand.\n \n+COMMANDS\n+--------\n+\n+Register\n+~~~~~~~~\n+\n+register [<enlistment>]::\n+\tAdds the enlistment's repository to the list of registered repositories\n+\tand starts background maintenance. If `<enlistment>` is not provided,\n+\tthen the enlistment associated with the current working directory is\n+\tregistered.\n++\n+Note: when this subcommand is called in a worktree that is called `src/`, its\n+parent directory is considered to be the Scalar enlistment. If the worktree is\n+_not_ called `src/`, it itself will be considered to be the Scalar enlistment.\n+\n SEE ALSO\n --------\n linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"441493","messageId":"d5218523a3824052ab9b2f336665bdd8882b1996.1637158762.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v7.git.1637158762.gitgitgadget@gmail.com","subject":"[PATCH v7 16/17] scalar: implement the `delete` command","fromName":"Matthew John Cheetham via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-17T14:19:21Z","receivedAt":"2021-11-17T14:20:45Z","isPatch":true,"sender":{"key":"mjcheetham@outlook.com","avatar":"https://avatars.githubusercontent.com/u/5658207?v=4"},"body":"From: Matthew John Cheetham <mjcheetham@outlook.com>\n\nDelete an enlistment by first unregistering the repository and then\ndeleting the enlistment directory (usually the directory containing the\nworktree `src/` directory).\n\nOn Windows, if the current directory is inside the enlistment's\ndirectory, change to the parent of the enlistment directory, to allow us\nto delete the enlistment (directories used by processes e.g. as current\nworking directories cannot be deleted on Windows).\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Matthew John Cheetham <mjcheetham@outlook.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 63 ++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        |  8 ++++\n contrib/scalar/t/t9099-scalar.sh |  9 +++++\n 3 files changed, 80 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 71ca573f3af..c53697ad6a0 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -8,6 +8,8 @@\n #include \"config.h\"\n #include \"run-command.h\"\n #include \"refs.h\"\n+#include \"dir.h\"\n+#include \"packfile.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -328,6 +330,33 @@ static char *remote_default_branch(const char *url)\n \treturn NULL;\n }\n \n+static int delete_enlistment(struct strbuf *enlistment)\n+{\n+#ifdef WIN32\n+\tstruct strbuf parent = STRBUF_INIT;\n+#endif\n+\n+\tif (unregister_dir())\n+\t\tdie(_(\"failed to unregister repository\"));\n+\n+#ifdef WIN32\n+\t/*\n+\t * Change the current directory to one outside of the enlistment so\n+\t * that we may delete everything underneath it.\n+\t */\n+\tstrbuf_addbuf(&parent, enlistment);\n+\tstrbuf_parent_directory(&parent);\n+\tif (chdir(parent.buf) < 0)\n+\t\tdie_errno(_(\"could not switch to '%s'\"), parent.buf);\n+\tstrbuf_release(&parent);\n+#endif\n+\n+\tif (remove_dir_recursively(enlistment, 0))\n+\t\tdie(_(\"failed to delete enlistment directory\"));\n+\n+\treturn 0;\n+}\n+\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n@@ -688,6 +717,39 @@ static int cmd_unregister(int argc, const char **argv)\n \treturn unregister_dir();\n }\n \n+static int cmd_delete(int argc, const char **argv)\n+{\n+\tchar *cwd = xgetcwd();\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar delete <enlistment>\"),\n+\t\tNULL\n+\t};\n+\tstruct strbuf enlistment = STRBUF_INIT;\n+\tint res = 0;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tif (argc != 1)\n+\t\tusage_with_options(usage, options);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, &enlistment);\n+\n+\tif (dir_inside_of(cwd, enlistment.buf) >= 0)\n+\t\tres = error(_(\"refusing to delete current working directory\"));\n+\telse {\n+\t\tclose_object_store(the_repository->objects);\n+\t\tres = delete_enlistment(&enlistment);\n+\t}\n+\tstrbuf_release(&enlistment);\n+\tfree(cwd);\n+\n+\treturn res;\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n@@ -698,6 +760,7 @@ static struct {\n \t{ \"unregister\", cmd_unregister },\n \t{ \"run\", cmd_run },\n \t{ \"reconfigure\", cmd_reconfigure },\n+\t{ \"delete\", cmd_delete },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 737cf563c1a..f416d637289 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -14,6 +14,7 @@ scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n scalar reconfigure [ --all | <enlistment> ]\n+scalar delete <enlistment>\n \n DESCRIPTION\n -----------\n@@ -128,6 +129,13 @@ reconfigure the enlistment.\n With the `--all` option, all enlistments currently registered with Scalar\n will be reconfigured. Use this option after each Scalar upgrade.\n \n+Delete\n+~~~~~~\n+\n+delete <enlistment>::\n+\tThis subcommand lets you delete an existing Scalar enlistment from your\n+\tlocal file system, unregistering the repository.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 58af546fd84..2e1502ad45e 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -76,4 +76,13 @@ test_expect_success 'scalar reconfigure' '\n \ttest true = \"$(git -C one/src config core.preloadIndex)\"\n '\n \n+test_expect_success 'scalar delete without enlistment shows a usage' '\n+\ttest_expect_code 129 scalar delete\n+'\n+\n+test_expect_success 'scalar delete with enlistment' '\n+\tscalar delete cloned &&\n+\ttest_path_is_missing cloned\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"441494","messageId":"1b0328fa236a35c2427b82f53c32944e513580d3.1637158762.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v7.git.1637158762.gitgitgadget@gmail.com","subject":"[PATCH v7 06/17] ci: also run the `scalar` tests","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-17T14:19:11Z","receivedAt":"2021-11-17T14:20:51Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nSince Scalar depends on `libgit.a`, it makes sense to ensure in the CI\nand the PR builds that it does not get broken in case of industrious\nrefactorings of the core Git code.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n .github/workflows/main.yml | 15 +++++++++++++++\n ci/run-build-and-tests.sh  |  1 +\n ci/run-test-slice.sh       |  5 +++++\n 3 files changed, 21 insertions(+)\n\ndiff --git a/.github/workflows/main.yml b/.github/workflows/main.yml\nindex 6ed6a9e8076..6eda6be895d 100644\n--- a/.github/workflows/main.yml\n+++ b/.github/workflows/main.yml\n@@ -89,6 +89,13 @@ jobs:\n         HOME: ${{runner.workspace}}\n         NO_PERL: 1\n       run: . /etc/profile && ci/make-test-artifacts.sh artifacts\n+    - name: build Scalar\n+      shell: bash\n+      run: |\n+        make -C contrib/scalar &&\n+        mkdir -p artifacts/bin-wrappers artifacts/contrib/scalar &&\n+        cp contrib/scalar/scalar.exe artifacts/contrib/scalar/ &&\n+        cp bin-wrappers/scalar artifacts/bin-wrappers/\n     - name: zip up tracked files\n       run: git archive -o artifacts/tracked.tar.gz HEAD\n     - name: upload tracked files and build artifacts\n@@ -157,6 +164,8 @@ jobs:\n       run: compat\\vcbuild\\vcpkg_copy_dlls.bat release\n     - name: generate Visual Studio solution\n       shell: bash\n+      env:\n+        INCLUDE_SCALAR: YesPlease\n       run: |\n         cmake `pwd`/contrib/buildsystems/ -DCMAKE_PREFIX_PATH=`pwd`/compat/vcbuild/vcpkg/installed/x64-windows \\\n         -DNO_GETTEXT=YesPlease -DPERL_TESTS=OFF -DPYTHON_TESTS=OFF -DCURL_NO_CURL_CMAKE=ON\n@@ -170,6 +179,12 @@ jobs:\n       run: |\n         mkdir -p artifacts &&\n         eval \"$(make -n artifacts-tar INCLUDE_DLLS_IN_ARTIFACTS=YesPlease ARTIFACTS_DIRECTORY=artifacts NO_GETTEXT=YesPlease 2>&1 | grep ^tar)\"\n+    - name: copy Scalar\n+      shell: bash\n+      run: |\n+        mkdir -p artifacts/bin-wrappers artifacts/contrib/scalar &&\n+        cp contrib/scalar/scalar.exe artifacts/contrib/scalar/ &&\n+        cp bin-wrappers/scalar artifacts/bin-wrappers/\n     - name: zip up tracked files\n       run: git archive -o artifacts/tracked.tar.gz HEAD\n     - name: upload tracked files and build artifacts\ndiff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh\nindex cc62616d806..07cedd25ff1 100755\n--- a/ci/run-build-and-tests.sh\n+++ b/ci/run-build-and-tests.sh\n@@ -49,6 +49,7 @@ linux-gcc-4.8|pedantic)\n \tmake test\n \t;;\n esac\n+make -C contrib/scalar test\n \n check_unignored_build_artifacts\n \ndiff --git a/ci/run-test-slice.sh b/ci/run-test-slice.sh\nindex f8c2c3106a2..b741fd8f361 100755\n--- a/ci/run-test-slice.sh\n+++ b/ci/run-test-slice.sh\n@@ -14,4 +14,9 @@ make --quiet -C t T=\"$(cd t &&\n \t./helper/test-tool path-utils slice-tests \"$1\" \"$2\" t[0-9]*.sh |\n \ttr '\\n' ' ')\"\n \n+if test 0 = \"$1\"\n+then\n+\tmake -C contrib/scalar test\n+fi\n+\n check_unignored_build_artifacts\n-- \ngitgitgadget\n\n"},{"id":"441495","messageId":"96a803416b531cd7a85cdb68e54d0a7d58ddcbe8.1637158762.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v7.git.1637158762.gitgitgadget@gmail.com","subject":"[PATCH v7 17/17] scalar: implement the `version` command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-17T14:19:22Z","receivedAt":"2021-11-17T14:21:05Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe .NET version of Scalar has a `version` command. This was necessary\nbecause it was versioned independently of Git.\n\nSince Scalar is now tightly coupled with Git, it does not make sense for\nthem to show different versions. Therefore, it shows the same output as\n`git version`. For backwards-compatibility with the .NET version,\n`scalar version` prints to `stderr`, though (`git version` prints to\n`stdout` instead).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c | 39 +++++++++++++++++++++++++++++++++++++++\n 1 file changed, 39 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex c53697ad6a0..1fc4965bebb 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -10,6 +10,7 @@\n #include \"refs.h\"\n #include \"dir.h\"\n #include \"packfile.h\"\n+#include \"help.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -357,6 +358,15 @@ static int delete_enlistment(struct strbuf *enlistment)\n \treturn 0;\n }\n \n+/*\n+ * Dummy implementation; Using `get_version_info()` would cause a link error\n+ * without this.\n+ */\n+void load_builtin_commands(const char *prefix, struct cmdnames *cmds)\n+{\n+\tdie(\"not implemented\");\n+}\n+\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n@@ -750,6 +760,34 @@ static int cmd_delete(int argc, const char **argv)\n \treturn res;\n }\n \n+static int cmd_version(int argc, const char **argv)\n+{\n+\tint verbose = 0, build_options = 0;\n+\tstruct option options[] = {\n+\t\tOPT__VERBOSE(&verbose, N_(\"include Git version\")),\n+\t\tOPT_BOOL(0, \"build-options\", &build_options,\n+\t\t\t N_(\"include Git's build options\")),\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar verbose [-v | --verbose] [--build-options]\"),\n+\t\tNULL\n+\t};\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tif (argc != 0)\n+\t\tusage_with_options(usage, options);\n+\n+\tget_version_info(&buf, build_options);\n+\tfprintf(stderr, \"%s\\n\", buf.buf);\n+\tstrbuf_release(&buf);\n+\n+\treturn 0;\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n@@ -761,6 +799,7 @@ static struct {\n \t{ \"run\", cmd_run },\n \t{ \"reconfigure\", cmd_reconfigure },\n \t{ \"delete\", cmd_delete },\n+\t{ \"version\", cmd_version },\n \t{ NULL, NULL},\n };\n \n-- \ngitgitgadget\n"},{"id":"441496","messageId":"99affb84284e3c8f868104bde98de8446f57b309.1637158762.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v7.git.1637158762.gitgitgadget@gmail.com","subject":"[PATCH v7 13/17] scalar: implement the `run` command","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-17T14:19:18Z","receivedAt":"2021-11-17T14:22:15Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nNote: this subcommand is provided primarily for backwards-compatibility,\nfor existing Scalar uses. It is mostly just a shim for `git\nmaintenance`, mapping task names from the way Scalar called them to the\nway Git calls them.\n\nThe reason why those names differ? The background maintenance was first\nimplemented in Scalar, and when it was contributed as a patch series\nimplementing the `git maintenance` command, reviewers suggested better\nnames, those suggestions were accepted before the patches were\nintegrated into core Git.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 64 +++++++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt | 19 ++++++++++++\n 2 files changed, 83 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 516a75be3c4..ca524576011 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -484,6 +484,69 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_run(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tstruct {\n+\t\tconst char *arg, *task;\n+\t} tasks[] = {\n+\t\t{ \"config\", NULL },\n+\t\t{ \"commit-graph\", \"commit-graph\" },\n+\t\t{ \"fetch\", \"prefetch\" },\n+\t\t{ \"loose-objects\", \"loose-objects\" },\n+\t\t{ \"pack-files\", \"incremental-repack\" },\n+\t\t{ NULL, NULL }\n+\t};\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tconst char *usagestr[] = { NULL, NULL };\n+\tint i;\n+\n+\tstrbuf_addstr(&buf, N_(\"scalar run <task> [<enlistment>]\\nTasks:\\n\"));\n+\tfor (i = 0; tasks[i].arg; i++)\n+\t\tstrbuf_addf(&buf, \"\\t%s\\n\", tasks[i].arg);\n+\tusagestr[0] = buf.buf;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usagestr, 0);\n+\n+\tif (!argc)\n+\t\tusage_with_options(usagestr, options);\n+\n+\tif (!strcmp(\"all\", argv[0])) {\n+\t\ti = -1;\n+\t} else {\n+\t\tfor (i = 0; tasks[i].arg && strcmp(tasks[i].arg, argv[0]); i++)\n+\t\t\t; /* keep looking for the task */\n+\n+\t\tif (i > 0 && !tasks[i].arg) {\n+\t\t\terror(_(\"no such task: '%s'\"), argv[0]);\n+\t\t\tusage_with_options(usagestr, options);\n+\t\t}\n+\t}\n+\n+\targc--;\n+\targv++;\n+\tsetup_enlistment_directory(argc, argv, usagestr, options, NULL);\n+\tstrbuf_release(&buf);\n+\n+\tif (i == 0)\n+\t\treturn register_dir();\n+\n+\tif (i > 0)\n+\t\treturn run_git(\"maintenance\", \"run\",\n+\t\t\t       \"--task\", tasks[i].task, NULL);\n+\n+\tif (register_dir())\n+\t\treturn -1;\n+\tfor (i = 1; tasks[i].arg; i++)\n+\t\tif (run_git(\"maintenance\", \"run\",\n+\t\t\t    \"--task\", tasks[i].task, NULL))\n+\t\t\treturn -1;\n+\treturn 0;\n+}\n+\n static int remove_deleted_enlistment(struct strbuf *path)\n {\n \tint res = 0;\n@@ -556,6 +619,7 @@ static struct {\n \t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n+\t{ \"run\", cmd_run },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 56f744a4aa9..39143b08324 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -12,6 +12,7 @@ scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<e\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n+scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -98,6 +99,24 @@ unregister [<enlistment>]::\n \tRemove the specified repository from the list of repositories\n \tregistered with Scalar and stop the scheduled background maintenance.\n \n+Run\n+~~~\n+\n+scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]::\n+\tRun the given maintenance task (or all tasks, if `all` was specified).\n+\tExcept for `all` and `config`, this subcommand simply hands off to\n+\tlinkgit:git-maintenance[1] (mapping `fetch` to `prefetch` and\n+\t`pack-files` to `incremental-repack`).\n++\n+These tasks are run automatically as part of the scheduled maintenance,\n+as soon as the repository is registered with Scalar. It should therefore\n+not be necessary to run this subcommand manually.\n++\n+The `config` task is specific to Scalar and configures all those\n+opinionated default settings that make Git work more efficiently with\n+large repositories. As this task is run as part of `scalar clone`\n+automatically, explicit invocations of this task are rarely needed.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"441497","messageId":"0068c18aa624166706abbbd6e7f8a0bd04372637.1637158762.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v7.git.1637158762.gitgitgadget@gmail.com","subject":"[PATCH v7 15/17] scalar: teach 'reconfigure' to optionally handle all registered enlistments","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-17T14:19:20Z","receivedAt":"2021-11-17T14:22:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nAfter a Scalar upgrade, it can come in really handy if there is an easy\nway to reconfigure all Scalar enlistments. This new option offers this\nfunctionality.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 61 ++++++++++++++++++++++++++++++--\n contrib/scalar/scalar.txt        |  9 +++--\n contrib/scalar/t/t9099-scalar.sh |  3 ++\n 3 files changed, 67 insertions(+), 6 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex b799decbc2f..71ca573f3af 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -488,22 +488,77 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int get_scalar_repos(const char *key, const char *value, void *data)\n+{\n+\tstruct string_list *list = data;\n+\n+\tif (!strcmp(key, \"scalar.repo\"))\n+\t\tstring_list_append(list, value);\n+\n+\treturn 0;\n+}\n+\n static int cmd_reconfigure(int argc, const char **argv)\n {\n+\tint all = 0;\n \tstruct option options[] = {\n+\t\tOPT_BOOL('a', \"all\", &all,\n+\t\t\t N_(\"reconfigure all registered enlistments\")),\n \t\tOPT_END(),\n \t};\n \tconst char * const usage[] = {\n-\t\tN_(\"scalar reconfigure [<enlistment>]\"),\n+\t\tN_(\"scalar reconfigure [--all | <enlistment>]\"),\n \t\tNULL\n \t};\n+\tstruct string_list scalar_repos = STRING_LIST_INIT_DUP;\n+\tint i, res = 0;\n+\tstruct repository r = { NULL };\n+\tstruct strbuf commondir = STRBUF_INIT, gitdir = STRBUF_INIT;\n \n \targc = parse_options(argc, argv, NULL, options,\n \t\t\t     usage, 0);\n \n-\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\tif (!all) {\n+\t\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\t\treturn set_recommended_config(1);\n+\t}\n+\n+\tif (argc > 0)\n+\t\tusage_msg_opt(_(\"--all or <enlistment>, but not both\"),\n+\t\t\t      usage, options);\n+\n+\tgit_config(get_scalar_repos, &scalar_repos);\n \n-\treturn set_recommended_config(1);\n+\tfor (i = 0; i < scalar_repos.nr; i++) {\n+\t\tconst char *dir = scalar_repos.items[i].string;\n+\n+\t\tstrbuf_reset(&commondir);\n+\t\tstrbuf_reset(&gitdir);\n+\n+\t\tif (chdir(dir) < 0) {\n+\t\t\twarning_errno(_(\"could not switch to '%s'\"), dir);\n+\t\t\tres = -1;\n+\t\t} else if (discover_git_directory(&commondir, &gitdir) < 0) {\n+\t\t\twarning_errno(_(\"git repository gone in '%s'\"), dir);\n+\t\t\tres = -1;\n+\t\t} else {\n+\t\t\tgit_config_clear();\n+\n+\t\t\tthe_repository = &r;\n+\t\t\tr.commondir = commondir.buf;\n+\t\t\tr.gitdir = gitdir.buf;\n+\n+\t\t\tif (set_recommended_config(1) < 0)\n+\t\t\t\tres = -1;\n+\t\t}\n+\t}\n+\n+\tstring_list_clear(&scalar_repos, 1);\n+\tstrbuf_release(&commondir);\n+\tstrbuf_release(&gitdir);\n+\n+\treturn res;\n }\n \n static int cmd_run(int argc, const char **argv)\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 89fd7901585..737cf563c1a 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -13,7 +13,7 @@ scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n-scalar reconfigure <enlistment>\n+scalar reconfigure [ --all | <enlistment> ]\n \n DESCRIPTION\n -----------\n@@ -32,8 +32,8 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand. With the exception of `clone` and `list`, all\n-subcommands expect to be run in an enlistment.\n+depending on the subcommand. With the exception of `clone`, `list` and\n+`reconfigure --all`, all subcommands expect to be run in an enlistment.\n \n COMMANDS\n --------\n@@ -125,6 +125,9 @@ After a Scalar upgrade, or when the configuration of a Scalar enlistment\n was somehow corrupted or changed by mistake, this subcommand allows to\n reconfigure the enlistment.\n \n+With the `--all` option, all enlistments currently registered with Scalar\n+will be reconfigured. Use this option after each Scalar upgrade.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex fb5e2efee0a..58af546fd84 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -70,6 +70,9 @@ test_expect_success 'scalar reconfigure' '\n \tscalar register one &&\n \tgit -C one/src config core.preloadIndex false &&\n \tscalar reconfigure one &&\n+\ttest true = \"$(git -C one/src config core.preloadIndex)\" &&\n+\tgit -C one/src config core.preloadIndex false &&\n+\tscalar reconfigure -a &&\n \ttest true = \"$(git -C one/src config core.preloadIndex)\"\n '\n \n-- \ngitgitgadget\n\n"},{"id":"441502","messageId":"f674b1fe-87d4-2598-874c-7c3984d0ee92@gmail.com","threadId":"56409","inReplyTo":"3aa095dc824ea7f659e6c11aa2c21be6cadbe985.1637158762.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v7 01/17] scalar: add a README with a roadmap","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2021-11-17T15:40:51Z","receivedAt":"2021-11-17T15:41:10Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 11/17/2021 9:19 AM, Johannes Schindelin via GitGitGadget wrote:\n> From: Johannes Schindelin <johannes.schindelin@gmx.de>\n> \n> The Scalar command will be contributed incrementally, over a bunch of\n> patch series. Let's document what Scalar is about, and then describe the\n> patch series that are planned.\n> \n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  contrib/scalar/README.md | 71 ++++++++++++++++++++++++++++++++++++++++\n>  1 file changed, 71 insertions(+)\n>  create mode 100644 contrib/scalar/README.md\n> \n> diff --git a/contrib/scalar/README.md b/contrib/scalar/README.md\n> new file mode 100644\n> index 00000000000..7898a683ba5\n> --- /dev/null\n> +++ b/contrib/scalar/README.md\n> @@ -0,0 +1,71 @@\n> +# Scalar - an opinionated repository management tool\n> +\n> +Scalar is an add-on to Git, helping Git scale to very large repositories and\n> +worktrees.\n\nI would rephrase this as \"Scalar is an add-on to Git that helps users take\nadvantage of advanced performance features in Git.\"\n\nGit scales just fine, only it helps to enable some features that are off\nby default.\n\n> Originally implemented in C# using .NET Core, based on the learnings\n> +from the VFS for Git project, most of the techniques developed by the Scalar\n> +project have been integrated into core Git already:\n> +\n> +* partial clone,\n> +* commit graphs,\n> +* multi-pack index,\n> +* sparse checkout (cone mode),\n> +* scheduled background maintenance,\n> +* etc\n> +\n> +This directory contains the remaining parts of Scalar that are not (yet) in\n> +core Git.\n> +\n> +## Roadmap\n> +\n> +The idea is to populate this directory via incremental patch series and\n> +eventually move to a top-level directory next to `gitk-git/` and to `git-gui/`. The\n> +current plan involves the following patch series:\n> +\n> +- `scalar-the-beginning`: The initial patch series which sets up\n> +  `contrib/scalar/` and populates it with a minimal `scalar` command that\n> +  demonstrates the fundamental ideas.\n> +\n> +- `scalar-c-and-C`: The `scalar` command learns about two options that can be\n> +  specified before the command, `-c <key>=<value>` and `-C <directory>`.\n> +\n> +- `scalar-diagnose`: The `scalar` command is taught the `diagnose` subcommand.\n> +\n> +- `scalar-and-builtin-fsmonitor`: The built-in FSMonitor is enabled in `scalar\n> +  init` and in `scalar clone`, for an enormous performance boost when working\n> +  in large worktrees. This patch series necessarily depends on Jeff Hostetler's\n> +  FSMonitor patch series to be integrated into Git.\n\nYou say 'scalar init' but do you mean 'scalar register'?\n\n> +- `scalar-gentler-config-locking`: Scalar enlistments are registered in the\n> +  user's Git config. This usually does not represent any problem because it is\n> +  rare for a user to register an enlistment. However, in Scalar's functional\n> +  tests, Scalar enlistments are created galore, and in parallel, which can lead\n> +  to lock contention. This patch series works around that problem by re-trying\n> +  to lock the config file in a gentle fashion.\n> +\n> +- `scalar-extra-docs`: Add some extensive documentation that has been written\n> +  in the original Scalar project (all subject to discussion, of course).\n> +\n> +- `optionally-install-scalar`: Now that Scalar is feature (and documentation)\n> +  complete and is verified in CI builds, let's offer to install it.\n> +\n> +- `move-scalar-to-toplevel`: Now that Scalar is complete, let's move it next to\n> +  `gitk-git/` and to `git-gui/`, making it a top-level command.\n\nThis final one is where we can make the final call about where Scalar should\nexist in the tree and how optional it should be. This would also move the\nScalar man pages into Documentation/, along with possibly the docs from\n'scalar-extra-docs', and the tests into t/. The benefit of leaving this until\nthe end is that we can see the entirety of Scalar before making a final call.\n\n> +The following two patch series exist, but there is no plan to integrate them\n> +into Git's source tree:\n> +\n> +- `scalar-with-gvfs`: The primary purpose of this patch series is to support\n> +  existing Scalar users whose repositories are hosted in Azure Repos (which\n> +  does not support Git's partial clones, but supports its predecessor, the GVFS\n> +  protocol, which is used by Scalar to emulate the partial clone).\n> +\n> +  Since the GVFS protocol will never be supported by core Git, this patch\n> +  series will remain in Microsoft's fork of Git.\n> +\n> +- `run-scalar-functional-tests`: The Scalar project developed a quite\n> +  comprehensive set of integration tests (or, \"Functional Tests\"). They are the\n> +  sole remaining part of the original C#-based Scalar project, and this patch\n> +  adds a GitHub workflow that runs them all.\n> +\n> +  Since the tests partially depend on features that are only provided in the\n> +  `scalar-with-gvfs` patch series, this patch cannot be upstreamed.\n\nThese topics (in some form or another) exist on microsoft/git and are available\nvia GPL, so we don't intend to say \"we are withholding these patches\" but instead\nare saying \"We don't think the Git community is interested in these patches.\"\nThere are some interesting ideas there, but the implementation is too specific to\nAzure Repos to be of much help in general. These still exist mainly because the\nGVFS protocol is what Azure Repos has instead of partial clone. We are focused\ninstead on improving partial clone.\n\nThanks,\n-Stolee\n"},{"id":"441526","messageId":"CAOjrSZstAHwWxW_m0jSeDN9wgKC3p7oGJYDYRPL27q67GS7Y+g@mail.gmail.com","threadId":"56409","inReplyTo":"dbaad4753c156487e91b766ebd3c9a39d68c1b12.1637158762.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v7 05/17] cmake: optionally build `scalar`, too","fromName":"Matt Rogers","fromEmail":"mattr94@gmail.com","sentAt":"2021-11-17T21:12:52Z","receivedAt":"2021-11-17T21:13:03Z","isPatch":true,"sender":{"key":"mattr94@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5719846?v=4"},"body":"On Wed, Nov 17, 2021 at 9:49 AM Johannes Schindelin via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> From: Johannes Schindelin <johannes.schindelin@gmx.de>\n>\n> The CMake configuration unfortunately does not let us encapsulate all\n> (or at least the vast majority) of Scalar's build definition in the\n> `contrib/scalar/` subdirectory.\n\nI believe that this isn't fully correct in that you could call\nadd_subdirectory() with\na directory that isn't a subdirectory so long as you provide it an\nexplicit binary directory\nwhich is part of what we're doing here anyways. (at least it works\nfine for me with\ncmake version 3.21)\n\n>\n> To alleviate that somewhat, we guard the inclusion of Scalar via the\n> `INCLUDE_SCALAR` environment variable.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  contrib/buildsystems/CMakeLists.txt | 14 ++++++++++++++\n>  1 file changed, 14 insertions(+)\n>\n> diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\n> index fd1399c440f..dd7496b0322 100644\n> --- a/contrib/buildsystems/CMakeLists.txt\n> +++ b/contrib/buildsystems/CMakeLists.txt\n> @@ -729,6 +729,13 @@ if(CURL_FOUND)\n>         endif()\n>  endif()\n>\n> +if(DEFINED ENV{INCLUDE_SCALAR} AND NOT ENV{INCLUDE_SCALAR} STREQUAL \"\")\n> +       add_executable(scalar ${CMAKE_SOURCE_DIR}/contrib/scalar/scalar.c)\n> +       target_link_libraries(scalar common-main)\n> +       set_target_properties(scalar PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/contrib/scalar)\n> +       set_target_properties(scalar PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/contrib/scalar)\n> +endif()\n\nWould it make sense to just mark this with EXCLUDE_FROM_ALL so that\nway you don't have to have\nthe INCLUDE_SCALAR environment variable just for this?  This way\npeople who don't want\nto build scalar can still run `cmake --build .` and not have scalar\nbuilt and people who do want\nit built can just run `cmake --build . --target scalar`, alternatively\nyou could also do something like\n\nif(DEFINED ENV{INCLUDE_SCALAR} AND NOT ENV{INCLUDE_SCALAR} STREQUAL \"\")\n    add_executable(scalar ${CMAKE_SOURCE_DIR}/contrib/scalar/scalar.c)\nelse()\n    add_executable(scalar EXCLUDE_FROM_ALL\n${CMAKE_SOURCE_DIR}/contrib/scalar/scalar.c)\nendif()\n\nJust some food for thought,\n\nMatthew Rogers\n"},{"id":"441605","messageId":"nycvar.QRO.7.76.6.2111181422230.11028@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"CAOjrSZstAHwWxW_m0jSeDN9wgKC3p7oGJYDYRPL27q67GS7Y+g@mail.gmail.com","subject":"Re: [PATCH v7 05/17] cmake: optionally build `scalar`, too","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-11-18T13:32:51Z","receivedAt":"2021-11-18T13:33:04Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Matt,\n\nOn Wed, 17 Nov 2021, Matt Rogers wrote:\n\n> On Wed, Nov 17, 2021 at 9:49 AM Johannes Schindelin via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n> >\n> > From: Johannes Schindelin <johannes.schindelin@gmx.de>\n> >\n> > The CMake configuration unfortunately does not let us encapsulate all\n> > (or at least the vast majority) of Scalar's build definition in the\n> > `contrib/scalar/` subdirectory.\n>\n> I believe that this isn't fully correct in that you could call\n> add_subdirectory() with a directory that isn't a subdirectory so long as\n> you provide it an explicit binary directory which is part of what we're\n> doing here anyways. (at least it works fine for me with cmake version\n> 3.21)\n\nYou are of course correct, you _could_ put things into a subdirectory with\nCMake. However, please note that `scalar` wants to link to `libgit.a` (and\nalso link in `common-main.o`), therefore the subdirectory _still_ has to\nhave some sort of connection to the top-level configuration.\n\nI shall clarify that in the commit message in the next iteration.\n\n> > To alleviate that somewhat, we guard the inclusion of Scalar via the\n> > `INCLUDE_SCALAR` environment variable.\n> >\n> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > ---\n> >  contrib/buildsystems/CMakeLists.txt | 14 ++++++++++++++\n> >  1 file changed, 14 insertions(+)\n> >\n> > diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\n> > index fd1399c440f..dd7496b0322 100644\n> > --- a/contrib/buildsystems/CMakeLists.txt\n> > +++ b/contrib/buildsystems/CMakeLists.txt\n> > @@ -729,6 +729,13 @@ if(CURL_FOUND)\n> >         endif()\n> >  endif()\n> >\n> > +if(DEFINED ENV{INCLUDE_SCALAR} AND NOT ENV{INCLUDE_SCALAR} STREQUAL \"\")\n> > +       add_executable(scalar ${CMAKE_SOURCE_DIR}/contrib/scalar/scalar.c)\n> > +       target_link_libraries(scalar common-main)\n> > +       set_target_properties(scalar PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/contrib/scalar)\n> > +       set_target_properties(scalar PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/contrib/scalar)\n> > +endif()\n>\n> Would it make sense to just mark this with EXCLUDE_FROM_ALL so that way\n> you don't have to have the INCLUDE_SCALAR environment variable just for\n> this?  This way people who don't want to build scalar can still run\n> `cmake --build .` and not have scalar built and people who do want it\n> built can just run `cmake --build . --target scalar`, alternatively you\n> could also do something like\n>\n> if(DEFINED ENV{INCLUDE_SCALAR} AND NOT ENV{INCLUDE_SCALAR} STREQUAL \"\")\n>     add_executable(scalar ${CMAKE_SOURCE_DIR}/contrib/scalar/scalar.c)\n> else()\n>     add_executable(scalar EXCLUDE_FROM_ALL\n> ${CMAKE_SOURCE_DIR}/contrib/scalar/scalar.c)\n> endif()\n\nThe entire point of pulling this patch from a much later patch series to\nthe first Scalar patch series is to allow for easy testing in the CI\nbuild. To that end, what you propose would complexify the setup too much\nfor my liking.\n\nSince the end game seems to be for Scalar to become a top-level command\n(at least that's how I read Junio's favorable reply at\nhttps://lore.kernel.org/git/xmqq7ddnz115.fsf@gitster.g/), I suspect that\nit might not matter whether we stay with `INCLUDE_SCALAR` or change it to\nan idempotent alternative, anyway.\n\nCiao,\nDscho\n"},{"id":"441606","messageId":"nycvar.QRO.7.76.6.2111181433010.11028@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"f674b1fe-87d4-2598-874c-7c3984d0ee92@gmail.com","subject":"Re: [PATCH v7 01/17] scalar: add a README with a roadmap","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-11-18T13:51:01Z","receivedAt":"2021-11-18T13:51:21Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Stolee,\n\nOn Wed, 17 Nov 2021, Derrick Stolee wrote:\n\n> On 11/17/2021 9:19 AM, Johannes Schindelin via GitGitGadget wrote:\n> > From: Johannes Schindelin <johannes.schindelin@gmx.de>\n> >\n> > The Scalar command will be contributed incrementally, over a bunch of\n> > patch series. Let's document what Scalar is about, and then describe the\n> > patch series that are planned.\n> >\n> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > ---\n> >  contrib/scalar/README.md | 71 ++++++++++++++++++++++++++++++++++++++++\n> >  1 file changed, 71 insertions(+)\n> >  create mode 100644 contrib/scalar/README.md\n> >\n> > diff --git a/contrib/scalar/README.md b/contrib/scalar/README.md\n> > new file mode 100644\n> > index 00000000000..7898a683ba5\n> > --- /dev/null\n> > +++ b/contrib/scalar/README.md\n> > @@ -0,0 +1,71 @@\n> > +# Scalar - an opinionated repository management tool\n> > +\n> > +Scalar is an add-on to Git, helping Git scale to very large repositories and\n> > +worktrees.\n>\n> I would rephrase this as \"Scalar is an add-on to Git that helps users take\n> advantage of advanced performance features in Git.\"\n\nGood idea!\n\n> Git scales just fine, only it helps to enable some features that are off\n> by default.\n>\n> > Originally implemented in C# using .NET Core, based on the learnings\n> > +from the VFS for Git project, most of the techniques developed by the Scalar\n> > +project have been integrated into core Git already:\n> > +\n> > +* partial clone,\n> > +* commit graphs,\n> > +* multi-pack index,\n> > +* sparse checkout (cone mode),\n> > +* scheduled background maintenance,\n> > +* etc\n> > +\n> > +This directory contains the remaining parts of Scalar that are not (yet) in\n> > +core Git.\n> > +\n> > +## Roadmap\n> > +\n> > +The idea is to populate this directory via incremental patch series and\n> > +eventually move to a top-level directory next to `gitk-git/` and to `git-gui/`. The\n> > +current plan involves the following patch series:\n> > +\n> > +- `scalar-the-beginning`: The initial patch series which sets up\n> > +  `contrib/scalar/` and populates it with a minimal `scalar` command that\n> > +  demonstrates the fundamental ideas.\n> > +\n> > +- `scalar-c-and-C`: The `scalar` command learns about two options that can be\n> > +  specified before the command, `-c <key>=<value>` and `-C <directory>`.\n> > +\n> > +- `scalar-diagnose`: The `scalar` command is taught the `diagnose` subcommand.\n> > +\n> > +- `scalar-and-builtin-fsmonitor`: The built-in FSMonitor is enabled in `scalar\n> > +  init` and in `scalar clone`, for an enormous performance boost when working\n> > +  in large worktrees. This patch series necessarily depends on Jeff Hostetler's\n> > +  FSMonitor patch series to be integrated into Git.\n>\n> You say 'scalar init' but do you mean 'scalar register'?\n\nD'oh. Yes, I meant `scalar register`. I was thinking of `git init` too\nmuch when I wrote that.\n\n> > +- `scalar-gentler-config-locking`: Scalar enlistments are registered in the\n> > +  user's Git config. This usually does not represent any problem because it is\n> > +  rare for a user to register an enlistment. However, in Scalar's functional\n> > +  tests, Scalar enlistments are created galore, and in parallel, which can lead\n> > +  to lock contention. This patch series works around that problem by re-trying\n> > +  to lock the config file in a gentle fashion.\n> > +\n> > +- `scalar-extra-docs`: Add some extensive documentation that has been written\n> > +  in the original Scalar project (all subject to discussion, of course).\n> > +\n> > +- `optionally-install-scalar`: Now that Scalar is feature (and documentation)\n> > +  complete and is verified in CI builds, let's offer to install it.\n> > +\n> > +- `move-scalar-to-toplevel`: Now that Scalar is complete, let's move it next to\n> > +  `gitk-git/` and to `git-gui/`, making it a top-level command.\n>\n> This final one is where we can make the final call about where Scalar should\n> exist in the tree and how optional it should be. This would also move the\n> Scalar man pages into Documentation/, along with possibly the docs from\n> 'scalar-extra-docs', and the tests into t/. The benefit of leaving this until\n> the end is that we can see the entirety of Scalar before making a final call.\n\nYes.\n\nIt allows the current patch series to focus on the core functionality of\nScalar. The `move-scalar-to-toplevel` patch series will present an\nexcellent opportunity to discuss the merits and complications of accepting\nScalar as a top-level command into Git, without having to delay the\ncurrent patch series any further.\n\n> > +The following two patch series exist, but there is no plan to integrate them\n> > +into Git's source tree:\n> > +\n> > +- `scalar-with-gvfs`: The primary purpose of this patch series is to support\n> > +  existing Scalar users whose repositories are hosted in Azure Repos (which\n> > +  does not support Git's partial clones, but supports its predecessor, the GVFS\n> > +  protocol, which is used by Scalar to emulate the partial clone).\n> > +\n> > +  Since the GVFS protocol will never be supported by core Git, this patch\n> > +  series will remain in Microsoft's fork of Git.\n> > +\n> > +- `run-scalar-functional-tests`: The Scalar project developed a quite\n> > +  comprehensive set of integration tests (or, \"Functional Tests\"). They are the\n> > +  sole remaining part of the original C#-based Scalar project, and this patch\n> > +  adds a GitHub workflow that runs them all.\n> > +\n> > +  Since the tests partially depend on features that are only provided in the\n> > +  `scalar-with-gvfs` patch series, this patch cannot be upstreamed.\n>\n> These topics (in some form or another) exist on microsoft/git and are available\n> via GPL, so we don't intend to say \"we are withholding these patches\" but instead\n> are saying \"We don't think the Git community is interested in these patches.\"\n> There are some interesting ideas there, but the implementation is too specific to\n> Azure Repos to be of much help in general. These still exist mainly because the\n> GVFS protocol is what Azure Repos has instead of partial clone. We are focused\n> instead on improving partial clone.\n\nGood point. What I wrote does not fully reflect that. I have changed it.\n\nDo you see anything in the remainder of the patch series that still needs\nto be improved?\n\nThanks,\nDscho\n"},{"id":"441607","messageId":"211118.86zgq14jp1.gmgdl@evledraar.gmail.com","threadId":"56409","inReplyTo":"pull.1005.v7.git.1637158762.gitgitgadget@gmail.com","subject":"Re: [PATCH v7 00/17] Upstreaming the Scalar command","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-11-18T14:11:00Z","receivedAt":"2021-11-18T14:33:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Nov 17 2021, Johannes Schindelin via GitGitGadget wrote:\n\n>  * To avoid mistaking the current patch series for being feature-complete\n>    enough to unleash onto end users, I moved the Makefile rules to build\n>    HTML/manual pages to a later patch series.\n> [...]\n>  * I added two patches that I had planned on keeping in an add-on patch\n>    series for later, to build and test Scalar as part of the CI. I am still\n>    not 100% certain that it is a good idea to do so already now, but let's\n>    see what the reviewers have to say.\n\nI don't expect given [1] that I'll get a reply to this, but as noted\nthere I've had outstanding reviews of this series (including proposed\npatches) for weeks/months now that haven't been addressed, including the\npatch-on-top I suggested in the previous round of discussion [2].\n\nLooking at the new README.md it seems that the change of plan between\nv6..v7 is that after insisting that this command must stay decoupled in\ncontrib/scalar/ and have its own build/test etc. infrastructure, the\nend-goal now eventual full integration at the top-level.\n\nPeeking a branch of yours[3] there's a change[4] in it that's much like\nmy [2], some of the hunks are even the same.\n\nI'm quite happy with that end-state, but still don't understand why the\n\"scalar-extra-docs\" and \"run-scalar-functional-tests\" (in your README.md\nhere) couldn't be squashed into this first step, as suggested in my [2].\n\nThe end-state is the same, but doing it that way avoids all this\npath-based churn, setting up build systems that are then going away in\nthe short-to-medium term etc, as well as the new custom just-for-scalar\nCI integration in this v7, you'd get that and more for free with my [2].\n\nAll of that is apparently to:\n\n> [...]avoid mistaking the current patch series for being feature-complete\n> enough to unleash onto end users[. ...]\n\nWhich is fair enough, but doesn't explain why this back-and-forth churn\nis needed to reach an end-goal we apparently now almost entirely agree\non.\n\nI.e. in my [2] it's built & tested by default, but not installed. So end\nusers aren't seeing it. The small bit of proposed in-advanced reference\nto it in our documentation is there in [2], but that's trivially\ntweaked, we could defer that too. No end-user would see any of it.\n\nSo who's being helped by these intermediate steps?\n\nAs far as I can tell based on your previous comments the perceived\nnecessity these intermediate steps comes from a narrow reading of\ncontrib/README,\ni.e. that software in this sort of flux *MUST* live in contrib, even if\nwe've got plans & patches in-progress to move it out eventually.\n\nWhereas I think we can just document that it's in flux, and skip the\nintermediate churn to get to the agreed-upon end-state.\n\nAs before I'm happy to engage with you, but I must say at this point I'm\nnot getting my hopes up much.\n\nCheers.\n\n1. https://lore.kernel.org/git/211110.86czn8hyis.gmgdl@evledraar.gmail.com/\n2. https://lore.kernel.org/git/patch-1.1-86fb8d56307-20211028T185016Z-avarab@gmail.com/\n3. https://github.com/dscho/git/tree/vfs-with-scalar\n4. https://github.com/dscho/git/commit/f9b8e5d5e7e\n"},{"id":"441803","messageId":"pull.1005.v8.git.1637363024.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v7.git.1637158762.gitgitgadget@gmail.com","subject":"[PATCH v8 00/17] Upstreaming the Scalar command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-19T23:03:27Z","receivedAt":"2021-11-19T23:03:49Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"tl;dr: This series contributes the core part of the Scalar command to the\nGit project. This command provides an opinionated way to create and\nconfigure Git repositories with a focus on very large repositories.\n\nChanges since v7:\n\n * Clarified in the commit message why we cannot easily encapsulate the\n   Scalar part of the CMake configuration in contrib/scalar/.\n * Improved the README.md.\n\nChanges since v6:\n\n * Rebased on top of v2.34.0.\n * Inserted a commit that adds contrib/scalar/README.md, containing the\n   roadmap of what I have planned for Scalar.\n * The Scalar test's definition of GIT_TEST_MAINT_SCHEDULER has been\n   adjusted to accommodate for a change in v2.32.0..v2.34.0.\n * The config setting defaults now include fetch.showForcedUpdates=false,\n   which has been identified as helping with a performance issue in large\n   repositories.\n * To avoid mistaking the current patch series for being feature-complete\n   enough to unleash onto end users, I moved the Makefile rules to build\n   HTML/manual pages to a later patch series.\n * The patch that adds support for -c <key>=<value> and -C <directory> was\n   moved to its own add-on patch series: While it is obvious that those\n   options are valuable to have, an open question is whether there are other\n   \"pre-command\" options in git that would be useful, too, and I would like\n   to postpone that discussion to that date.\n * I added two patches that I had planned on keeping in an add-on patch\n   series for later, to build and test Scalar as part of the CI. I am still\n   not 100% certain that it is a good idea to do so already now, but let's\n   see what the reviewers have to say.\n\nChanges since v5:\n\n * Fixed the commit message talking about make -C contrib/scalar/Makefile.\n * Fixed the git ls-tree invocation suggested in the manual for scalar\n   clone.\n * Invoking make -C contrib/scalar, then changing a source file of libgit.a\n   and then immediately invoking make -C contrib/scalar again will now\n   implicitly rebuild libgit.a.\n\nChanges since v4:\n\n * scalar delete now refuses to delete anything if it was started from\n   within the enlistment.\n * scalar delete releases any handles to the object store before deleting\n   the enlistment.\n * The OBJECTS list in the Makefile will now include Scalar.\n * scalar register now supports secondary worktrees, in addition to the\n   primary worktree.\n\nChanges since v3:\n\n * Moved the \"Changes since\" section to the top, to make it easier to see\n   what changed.\n * Reworded the commit message of the first patch.\n * Removed the [RFC] prefix because I did not hear any objections against\n   putting this into contrib/.\n\nChanges since v2:\n\n * Adjusted the description of the list command in the manual page , as\n   suggested by Bagas.\n * Addressed two style nits in cmd_run().\n * The documentation of git reconfigure -a was improved.\n\nChanges since v1:\n\n * A couple typos were fixed\n * The code parsing the output of ls-remote was made more readable\n * The indentation used in scalar.txt now consistently uses tabs\n * We no longer hard-code core.bare = false when registering with Scalar\n\n\nBackground\n==========\n\nMicrosoft invested a lot of effort into scaling Git to the needs of the\nWindows operating system source code. Based on the experience of the first\napproach, VFS for Git, the Scalar project was started. Scalar specifically\nhas as its core goal to funnel all improvements into core Git.\n\n\nThe present\n===========\n\nThe Scalar project provides a completely functional non-virtual experience\nfor monorepos. But why stop there. The Scalar project was designed to be a\nself-destructing vehicle to allow those key concepts to be moved into core\nGit itself for the benefit of all. For example, partial clone,\nsparse-checkout, and scheduled background maintenance have already been\nupstreamed and removed from Scalar proper. This patch series provides a\nC-based implementation of the final remaining portions of the Scalar\ncommand. This will make it easier for users to experiment with the Scalar\ncommand. It will also make it substantially easier to experiment with moving\nfunctionality from Scalar into core Git, while maintaining\nbackwards-compatibility for existing Scalar users.\n\nThe C-based Scalar has been shipped to Scalar users, and can be tested by\nany interested reader: https://github.com/microsoft/git/releases/ (it offers\na Git for Windows installer, a macOS package and an Ubuntu package, Scalar\nhas been included since v2.33.0.vfs.0.0).\n\n\nNext steps\n==========\n\nSince there are existing Scalar users, I want to ensure\nbackwards-compatibility with its existing command-line interface. Keeping\nthat in mind, everything in this series is up for discussion.\n\nI obviously believe that Scalar brings a huge benefit, and think that it\nwould be ideal for all of Scalar's learnings to end up in git clone/git\ninit/git maintenance eventually. It is also conceivable, however, that the\nscalar command could graduate to be a core part of Git at some stage in the\nfuture (such a decision would probably depend highly on users' feedback).\nSee also the discussion about the architecture of Scalar\n[https://lore.kernel.org/git/b67bbef4-e4c3-b6a7-1c7f-7d405902ef8b@gmail.com/],\nkicked off by Stolee.\n\nOn top of this patch series, I have lined up a few more:\n\n 1. Implement a scalar diagnose command.\n 2. Use the built-in FSMonitor (that patch series obviously needs to wait\n    for FSMonitor to be integrated).\n 3. Modify the config machinery to be more generous about concurrent writes,\n    say, to the user-wide config.\n 4. A few patches to optionally build and install scalar as part of a\n    regular Git install (also teaching git help scalar to find the Scalar\n    documentation\n\nThese are included in my vfs-with-scalar branch thicket\n[https://github.com/dscho/git/commits/vfs-with-scalar]. On top of that, this\nbranch thicket also includes patches I do not plan on upstreaming, mainly\nbecause they are too specific either to VFS for Git, or they support Azure\nRepos (which does not offer partial clones but speaks the GVFS protocol,\nwhich can be used to emulate partial clones).\n\nOne other thing is very interesting about that vfs-with-scalar branch\nthicket: it contains a GitHub workflow which will run Scalar's quite\nextensive Functional Tests suite. This test suite is quite comprehensive and\ncaught us a lot of bugs in the past, not only in the Scalar code, but also\ncore Git.\n\n\nEpilogue\n========\n\nNow, to address some questions that I imagine every reader has who made it\nthis far:\n\n * Why not put the Scalar functionality directly into core Git, even a\n   built-in? I wanted to provide an easy way for Git contributors to \"play\n   with\" Scalar, without forcing a new top-level command into Git.\n * Why implement the Scalar command in the Git code base? Apart from\n   simplifying Scalar maintenance in the Microsoft port of Git, the tight\n   version coupling between Git and Scalar reduces the maintenance burden\n   even further. Besides, I believe that it will make it much easier to\n   shift functionality from Scalar into core Git, once we took the hurdle of\n   accepting the Scalar code into the code base.\n * Why contribute Scalar to the Git project? We are biased, of course, yet\n   our data-driven approach provides evidence that Scalar helps handling\n   huge repositories with ease. By contributing it to the core Git project,\n   we are able to share it with more users, especially some users who do not\n   want to install Microsoft's fork of Git. We also hope that a lot of\n   Scalar (maybe all of it) will end up in core Git, to benefit even more\n   users.\n\nDerrick Stolee (4):\n  scalar: 'register' sets recommended config and starts maintenance\n  scalar: 'unregister' stops background maintenance\n  scalar: implement 'scalar list'\n  scalar: implement the `run` command\n\nJohannes Schindelin (12):\n  scalar: add a README with a roadmap\n  scalar: create a rudimentary executable\n  scalar: start documenting the command\n  scalar: create test infrastructure\n  cmake: optionally build `scalar`, too\n  ci: also run the `scalar` tests\n  scalar: let 'unregister' handle a deleted enlistment directory\n    gracefully\n  scalar: implement the `clone` subcommand\n  scalar: teach 'clone' to support the --single-branch option\n  scalar: allow reconfiguring an existing enlistment\n  scalar: teach 'reconfigure' to optionally handle all registered\n    enlistments\n  scalar: implement the `version` command\n\nMatthew John Cheetham (1):\n  scalar: implement the `delete` command\n\n .github/workflows/main.yml          |  15 +\n Makefile                            |   9 +\n ci/run-build-and-tests.sh           |   1 +\n ci/run-test-slice.sh                |   5 +\n contrib/buildsystems/CMakeLists.txt |  14 +\n contrib/scalar/.gitignore           |   2 +\n contrib/scalar/Makefile             |  45 ++\n contrib/scalar/README.md            |  82 +++\n contrib/scalar/scalar.c             | 826 ++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt           | 145 +++++\n contrib/scalar/t/Makefile           |  78 +++\n contrib/scalar/t/t9099-scalar.sh    |  88 +++\n 12 files changed, 1310 insertions(+)\n create mode 100644 contrib/scalar/.gitignore\n create mode 100644 contrib/scalar/Makefile\n create mode 100644 contrib/scalar/README.md\n create mode 100644 contrib/scalar/scalar.c\n create mode 100644 contrib/scalar/scalar.txt\n create mode 100644 contrib/scalar/t/Makefile\n create mode 100755 contrib/scalar/t/t9099-scalar.sh\n\n\nbase-commit: cd3e606211bb1cf8bc57f7d76bab98cc17a150bc\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1005%2Fdscho%2Fscalar-the-beginning-v8\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1005/dscho/scalar-the-beginning-v8\nPull-Request: https://github.com/gitgitgadget/git/pull/1005\n\nRange-diff vs v7:\n\n  1:  3aa095dc824 !  1:  3a2e28275f1 scalar: add a README with a roadmap\n     @@ contrib/scalar/README.md (new)\n      @@\n      +# Scalar - an opinionated repository management tool\n      +\n     -+Scalar is an add-on to Git, helping Git scale to very large repositories and\n     -+worktrees. Originally implemented in C# using .NET Core, based on the learnings\n     -+from the VFS for Git project, most of the techniques developed by the Scalar\n     -+project have been integrated into core Git already:\n     ++Scalar is an add-on to Git that helps users take advantage of advanced\n     ++performance features in Git. Originally implemented in C# using .NET Core,\n     ++based on the learnings from the VFS for Git project, most of the techniques\n     ++developed by the Scalar project have been integrated into core Git already:\n      +\n      +* partial clone,\n      +* commit graphs,\n     @@ contrib/scalar/README.md (new)\n      +- `scalar-diagnose`: The `scalar` command is taught the `diagnose` subcommand.\n      +\n      +- `scalar-and-builtin-fsmonitor`: The built-in FSMonitor is enabled in `scalar\n     -+  init` and in `scalar clone`, for an enormous performance boost when working\n     -+  in large worktrees. This patch series necessarily depends on Jeff Hostetler's\n     -+  FSMonitor patch series to be integrated into Git.\n     ++  register` and in `scalar clone`, for an enormous performance boost when\n     ++  working in large worktrees. This patch series necessarily depends on Jeff\n     ++  Hostetler's FSMonitor patch series to be integrated into Git.\n      +\n      +- `scalar-gentler-config-locking`: Scalar enlistments are registered in the\n      +  user's Git config. This usually does not represent any problem because it is\n     @@ contrib/scalar/README.md (new)\n      +- `move-scalar-to-toplevel`: Now that Scalar is complete, let's move it next to\n      +  `gitk-git/` and to `git-gui/`, making it a top-level command.\n      +\n     -+The following two patch series exist, but there is no plan to integrate them\n     -+into Git's source tree:\n     ++The following two patch series exist in Microsoft's fork of Git and are\n     ++publicly available. There is no current plan to upstream them, not because I\n     ++want to withhold these patches, but because I don't think the Git community is\n     ++interested in these patches.\n     ++\n     ++There are some interesting ideas there, but the implementation is too specific\n     ++to Azure Repos and/or VFS for Git to be of much help in general (and also: my\n     ++colleagues tried to upstream some patches already and the enthusiasm for\n     ++integrating things related to Azure Repos and VFS for Git can be summarized in\n     ++very, very few words).\n     ++\n     ++These still exist mainly because the GVFS protocol is what Azure Repos has\n     ++instead of partial clone, while Git is focused on improving partial clone:\n      +\n      +- `scalar-with-gvfs`: The primary purpose of this patch series is to support\n      +  existing Scalar users whose repositories are hosted in Azure Repos (which\n  2:  e0693cc713c =  2:  50160d61a41 scalar: create a rudimentary executable\n  3:  d80627615f8 =  3:  74cd6410931 scalar: start documenting the command\n  4:  9da1616849e =  4:  37231a4dd07 scalar: create test infrastructure\n  5:  dbaad4753c1 !  5:  a39b9c81214 cmake: optionally build `scalar`, too\n     @@ Metadata\n       ## Commit message ##\n          cmake: optionally build `scalar`, too\n      \n     -    The CMake configuration unfortunately does not let us encapsulate all\n     -    (or at least the vast majority) of Scalar's build definition in the\n     -    `contrib/scalar/` subdirectory.\n     +    The CMake configuration unfortunately does not let us easily encapsulate\n     +    Scalar's build definition in the `contrib/scalar/` subdirectory: The\n     +    `scalar` executable needs to link in `libgit.a` and `common-main.o`, for\n     +    example.\n     +\n     +    Also, `scalar.c` includes Git's header files, which means that\n     +    `scalar.c` needs to be compiled with the very same flags as `libgit.a`\n     +    lest `scalar.o` and `libgit.a` have different ideas of, say,\n     +    `platform_SHA_CTX`, which would naturally lead to memory corruption.\n      \n          To alleviate that somewhat, we guard the inclusion of Scalar via the\n          `INCLUDE_SCALAR` environment variable.\n  6:  1b0328fa236 =  6:  8e3542e43f7 ci: also run the `scalar` tests\n  7:  cca604ef326 =  7:  385abdb8d8e scalar: 'register' sets recommended config and starts maintenance\n  8:  9fea89cd161 =  8:  64c6a75353e scalar: 'unregister' stops background maintenance\n  9:  5e077bf892b =  9:  f7fc1958b9e scalar: let 'unregister' handle a deleted enlistment directory gracefully\n 10:  dfa0c470989 = 10:  fd2680bc945 scalar: implement 'scalar list'\n 11:  febefe39886 = 11:  4966a43aad9 scalar: implement the `clone` subcommand\n 12:  2677bcff335 = 12:  b00d68b37b0 scalar: teach 'clone' to support the --single-branch option\n 13:  99affb84284 = 13:  771d826bbb1 scalar: implement the `run` command\n 14:  69e2242240b = 14:  a8b2d26a830 scalar: allow reconfiguring an existing enlistment\n 15:  0068c18aa62 = 15:  ca284ff34a2 scalar: teach 'reconfigure' to optionally handle all registered enlistments\n 16:  d5218523a38 = 16:  9983eb8912c scalar: implement the `delete` command\n 17:  96a803416b5 = 17:  889f613ab18 scalar: implement the `version` command\n\n-- \ngitgitgadget\n"},{"id":"441804","messageId":"3a2e28275f112d114bbd3ba046f440c5f917fc32.1637363025.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v8.git.1637363024.gitgitgadget@gmail.com","subject":"[PATCH v8 01/17] scalar: add a README with a roadmap","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-19T23:03:28Z","receivedAt":"2021-11-19T23:03:50Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe Scalar command will be contributed incrementally, over a bunch of\npatch series. Let's document what Scalar is about, and then describe the\npatch series that are planned.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/README.md | 82 ++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 82 insertions(+)\n create mode 100644 contrib/scalar/README.md\n\ndiff --git a/contrib/scalar/README.md b/contrib/scalar/README.md\nnew file mode 100644\nindex 00000000000..634b5771ed3\n--- /dev/null\n+++ b/contrib/scalar/README.md\n@@ -0,0 +1,82 @@\n+# Scalar - an opinionated repository management tool\n+\n+Scalar is an add-on to Git that helps users take advantage of advanced\n+performance features in Git. Originally implemented in C# using .NET Core,\n+based on the learnings from the VFS for Git project, most of the techniques\n+developed by the Scalar project have been integrated into core Git already:\n+\n+* partial clone,\n+* commit graphs,\n+* multi-pack index,\n+* sparse checkout (cone mode),\n+* scheduled background maintenance,\n+* etc\n+\n+This directory contains the remaining parts of Scalar that are not (yet) in\n+core Git.\n+\n+## Roadmap\n+\n+The idea is to populate this directory via incremental patch series and\n+eventually move to a top-level directory next to `gitk-git/` and to `git-gui/`. The\n+current plan involves the following patch series:\n+\n+- `scalar-the-beginning`: The initial patch series which sets up\n+  `contrib/scalar/` and populates it with a minimal `scalar` command that\n+  demonstrates the fundamental ideas.\n+\n+- `scalar-c-and-C`: The `scalar` command learns about two options that can be\n+  specified before the command, `-c <key>=<value>` and `-C <directory>`.\n+\n+- `scalar-diagnose`: The `scalar` command is taught the `diagnose` subcommand.\n+\n+- `scalar-and-builtin-fsmonitor`: The built-in FSMonitor is enabled in `scalar\n+  register` and in `scalar clone`, for an enormous performance boost when\n+  working in large worktrees. This patch series necessarily depends on Jeff\n+  Hostetler's FSMonitor patch series to be integrated into Git.\n+\n+- `scalar-gentler-config-locking`: Scalar enlistments are registered in the\n+  user's Git config. This usually does not represent any problem because it is\n+  rare for a user to register an enlistment. However, in Scalar's functional\n+  tests, Scalar enlistments are created galore, and in parallel, which can lead\n+  to lock contention. This patch series works around that problem by re-trying\n+  to lock the config file in a gentle fashion.\n+\n+- `scalar-extra-docs`: Add some extensive documentation that has been written\n+  in the original Scalar project (all subject to discussion, of course).\n+\n+- `optionally-install-scalar`: Now that Scalar is feature (and documentation)\n+  complete and is verified in CI builds, let's offer to install it.\n+\n+- `move-scalar-to-toplevel`: Now that Scalar is complete, let's move it next to\n+  `gitk-git/` and to `git-gui/`, making it a top-level command.\n+\n+The following two patch series exist in Microsoft's fork of Git and are\n+publicly available. There is no current plan to upstream them, not because I\n+want to withhold these patches, but because I don't think the Git community is\n+interested in these patches.\n+\n+There are some interesting ideas there, but the implementation is too specific\n+to Azure Repos and/or VFS for Git to be of much help in general (and also: my\n+colleagues tried to upstream some patches already and the enthusiasm for\n+integrating things related to Azure Repos and VFS for Git can be summarized in\n+very, very few words).\n+\n+These still exist mainly because the GVFS protocol is what Azure Repos has\n+instead of partial clone, while Git is focused on improving partial clone:\n+\n+- `scalar-with-gvfs`: The primary purpose of this patch series is to support\n+  existing Scalar users whose repositories are hosted in Azure Repos (which\n+  does not support Git's partial clones, but supports its predecessor, the GVFS\n+  protocol, which is used by Scalar to emulate the partial clone).\n+\n+  Since the GVFS protocol will never be supported by core Git, this patch\n+  series will remain in Microsoft's fork of Git.\n+\n+- `run-scalar-functional-tests`: The Scalar project developed a quite\n+  comprehensive set of integration tests (or, \"Functional Tests\"). They are the\n+  sole remaining part of the original C#-based Scalar project, and this patch\n+  adds a GitHub workflow that runs them all.\n+\n+  Since the tests partially depend on features that are only provided in the\n+  `scalar-with-gvfs` patch series, this patch cannot be upstreamed.\n-- \ngitgitgadget\n\n"},{"id":"441805","messageId":"50160d61a41c182d9b8c6c859975f89415de8482.1637363025.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v8.git.1637363024.gitgitgadget@gmail.com","subject":"[PATCH v8 02/17] scalar: create a rudimentary executable","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-19T23:03:29Z","receivedAt":"2021-11-19T23:03:52Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe idea of Scalar (https://github.com/microsoft/scalar), and before\nthat, of VFS for Git, has always been to prove that Git _can_ scale, and\nto upstream whatever strategies have been demonstrated to help.\n\nWith this patch, we start the journey from that C# project to move what\nis left to Git's own `contrib/` directory, reimplementing it in pure C,\nwith the intention to facilitate integrating the functionality into core\nGit all while maintaining backwards-compatibility for existing Scalar\nusers (which will be much easier when both live in the same worktree).\nIt has always been the plan to contribute all of the proven strategies\nback to core Git.\n\nFor example, while the virtual filesystem provided by VFS for Git helped\nthe team developing the Windows operating system to move onto Git, while\ntrying to upstream it we realized that it cannot be done: getting the\nvirtual filesystem to work (which we only managed to implement fully on\nWindows, but not on, say, macOS or Linux), and the required server-side\nsupport for the GVFS protocol, made this not quite feasible.\n\nThe Scalar project learned from that and tackled the problem with\ndifferent tactics: instead of pretending to Git that the working\ndirectory is fully populated, it _specifically_ teaches Git about\npartial clone (which is based on VFS for Git's cache server), about\nsparse checkout (which VFS for Git tried to do transparently, in the\nfile system layer), and regularly runs maintenance tasks to keep the\nrepository in a healthy state.\n\nWith partial clone, sparse checkout and `git maintenance` having been\nupstreamed, there is little left that `scalar.exe` does which `git.exe`\ncannot do. One such thing is that `scalar clone <url>` will\nautomatically set up a partial, sparse clone, and configure\nknown-helpful settings from the start.\n\nSo let's bring this convenience into Git's tree.\n\nThe idea here is that you can (optionally) build Scalar via\n\n\tmake -C contrib/scalar/\n\nThis will build the `scalar` executable and put it into the\ncontrib/scalar/ subdirectory.\n\nThe slightly awkward addition of the `contrib/scalar/*` bits to the\ntop-level `Makefile` are actually really required: we want to link to\n`libgit.a`, which means that we will need to use the very same `CFLAGS`\nand `LDFLAGS` as the rest of Git.\n\nAn early development version of this patch tried to replicate all the\nconditional code in `contrib/scalar/Makefile` (e.g. `NO_POLL`) just like\n`contrib/svn-fe/Makefile` used to do before it was retired. It turned\nout to be quite the whack-a-mole game: the SHA-1-related flags, the\nflags enabling/disabling `compat/poll/`, `compat/regex/`,\n`compat/win32mmap.c` & friends depending on the current platform... To\nput it mildly: it was a major mess.\n\nInstead, this patch makes minimal changes to the top-level `Makefile` so\nthat the bits in `contrib/scalar/` can be compiled and linked, and\nadds a `contrib/scalar/Makefile` that uses the top-level `Makefile` in a\nmost minimal way to do the actual compiling.\n\nNote: With this commit, we only establish the infrastructure, no\nScalar functionality is implemented yet; We will do that incrementally\nover the next few commits.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Makefile                  |  9 +++++++++\n contrib/scalar/.gitignore |  2 ++\n contrib/scalar/Makefile   | 34 ++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.c   | 36 ++++++++++++++++++++++++++++++++++++\n 4 files changed, 81 insertions(+)\n create mode 100644 contrib/scalar/.gitignore\n create mode 100644 contrib/scalar/Makefile\n create mode 100644 contrib/scalar/scalar.c\n\ndiff --git a/Makefile b/Makefile\nindex 12be39ac497..fe898aeea08 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2456,6 +2456,11 @@ OBJECTS += $(FUZZ_OBJS)\n ifndef NO_CURL\n \tOBJECTS += http.o http-walker.o remote-curl.o\n endif\n+\n+SCALAR_SOURCES := contrib/scalar/scalar.c\n+SCALAR_OBJECTS := $(SCALAR_SOURCES:c=o)\n+OBJECTS += $(SCALAR_OBJECTS)\n+\n .PHONY: objects\n objects: $(OBJECTS)\n \n@@ -2589,6 +2594,10 @@ $(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n \t\t$(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)\n \n+contrib/scalar/scalar$X: $(SCALAR_OBJECTS) GIT-LDFLAGS $(GITLIBS)\n+\t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \\\n+\t\t$(filter %.o,$^) $(LIBS)\n+\n $(LIB_FILE): $(LIB_OBJS)\n \t$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^\n \ndiff --git a/contrib/scalar/.gitignore b/contrib/scalar/.gitignore\nnew file mode 100644\nindex 00000000000..ff3d47e84d0\n--- /dev/null\n+++ b/contrib/scalar/.gitignore\n@@ -0,0 +1,2 @@\n+/*.exe\n+/scalar\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nnew file mode 100644\nindex 00000000000..f6f0036f0fa\n--- /dev/null\n+++ b/contrib/scalar/Makefile\n@@ -0,0 +1,34 @@\n+QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir\n+QUIET_SUBDIR1  =\n+\n+ifneq ($(findstring s,$(MAKEFLAGS)),s)\n+ifndef V\n+\tQUIET_SUBDIR0  = +@subdir=\n+\tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n+\t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n+else\n+\texport V\n+endif\n+endif\n+\n+all:\n+\n+include ../../config.mak.uname\n+-include ../../config.mak.autogen\n+-include ../../config.mak\n+\n+TARGETS = scalar$(X) scalar.o\n+GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n+\n+all: scalar$(X)\n+\n+$(GITLIBS):\n+\t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)\n+\n+$(TARGETS): $(GITLIBS) scalar.c\n+\t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)\n+\n+clean:\n+\t$(RM) $(TARGETS)\n+\n+.PHONY: $(GITLIBS) all clean FORCE\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nnew file mode 100644\nindex 00000000000..7cff29e0fcd\n--- /dev/null\n+++ b/contrib/scalar/scalar.c\n@@ -0,0 +1,36 @@\n+/*\n+ * The Scalar command-line interface.\n+ */\n+\n+#include \"cache.h\"\n+#include \"gettext.h\"\n+#include \"parse-options.h\"\n+\n+static struct {\n+\tconst char *name;\n+\tint (*fn)(int, const char **);\n+} builtins[] = {\n+\t{ NULL, NULL},\n+};\n+\n+int cmd_main(int argc, const char **argv)\n+{\n+\tstruct strbuf scalar_usage = STRBUF_INIT;\n+\tint i;\n+\n+\tif (argc > 1) {\n+\t\targv++;\n+\t\targc--;\n+\n+\t\tfor (i = 0; builtins[i].name; i++)\n+\t\t\tif (!strcmp(builtins[i].name, argv[0]))\n+\t\t\t\treturn !!builtins[i].fn(argc, argv);\n+\t}\n+\n+\tstrbuf_addstr(&scalar_usage,\n+\t\t      N_(\"scalar <command> [<options>]\\n\\nCommands:\\n\"));\n+\tfor (i = 0; builtins[i].name; i++)\n+\t\tstrbuf_addf(&scalar_usage, \"\\t%s\\n\", builtins[i].name);\n+\n+\tusage(scalar_usage.buf);\n+}\n-- \ngitgitgadget\n\n"},{"id":"441806","messageId":"74cd64109319748e532339017b3b76a4823f9c86.1637363025.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v8.git.1637363024.gitgitgadget@gmail.com","subject":"[PATCH v8 03/17] scalar: start documenting the command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-19T23:03:30Z","receivedAt":"2021-11-19T23:03:53Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nLet's build up the documentation for the Scalar command along with the\npatches that implement its functionality.\n\nNote: To discourage the feature-incomplete documentation from being\nmistaken for the complete thing, we do not yet provide any way to build\nHTML or manual pages from the text file.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.txt | 38 ++++++++++++++++++++++++++++++++++++++\n 1 file changed, 38 insertions(+)\n create mode 100644 contrib/scalar/scalar.txt\n\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nnew file mode 100644\nindex 00000000000..5f7131861a5\n--- /dev/null\n+++ b/contrib/scalar/scalar.txt\n@@ -0,0 +1,38 @@\n+scalar(1)\n+=========\n+\n+NAME\n+----\n+scalar - an opinionated repository management tool\n+\n+SYNOPSIS\n+--------\n+[verse]\n+scalar <command> [<options>]\n+\n+DESCRIPTION\n+-----------\n+\n+Scalar is an opinionated repository management tool. By creating new\n+repositories or registering existing repositories with Scalar, your Git\n+experience will speed up. Scalar sets advanced Git config settings,\n+maintains your repositories in the background, and helps reduce data sent\n+across the network.\n+\n+An important Scalar concept is the enlistment: this is the top-level directory\n+of the project. It usually contains the subdirectory `src/` which is a Git\n+worktree. This encourages the separation between tracked files (inside `src/`)\n+and untracked files, such as build artifacts (outside `src/`). When registering\n+an existing Git worktree with Scalar whose name is not `src`, the enlistment\n+will be identical to the worktree.\n+\n+The `scalar` command implements various subcommands, and different options\n+depending on the subcommand.\n+\n+SEE ALSO\n+--------\n+linkgit:git-maintenance[1].\n+\n+Scalar\n+---\n+Associated with the linkgit:git[1] suite\n-- \ngitgitgadget\n\n"},{"id":"441807","messageId":"37231a4dd07833807639c8a650185569d0c99af2.1637363025.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v8.git.1637363024.gitgitgadget@gmail.com","subject":"[PATCH v8 04/17] scalar: create test infrastructure","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-19T23:03:31Z","receivedAt":"2021-11-19T23:03:53Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nTo test the Scalar command, create a test script in contrib/scalar/t\nthat is executed as `make -C contrib/scalar test`. Since Scalar has no\nmeaningful capabilities yet, the only test is rather simple. We will add\nmore tests in subsequent commits that introduce corresponding, new\nfunctionality.\n\nNote: This test script is intended to test `scalar` only lightly, even\nafter all of the functionality is implemented.\n\nA more comprehensive functional (or: integration) test suite can be\nfound at https://github.com/microsoft/scalar; It is used in the workflow\nhttps://github.com/microsoft/git/blob/HEAD/.github/workflows/scalar-functional-tests.yml\nin Microsoft's Git fork. This test suite performs end-to-end tests with\na real remote repository, and is run as part of the regular CI and PR\nbuilds in that fork.\n\nSince those tests require some functionality supported only by\nMicrosoft's Git fork (\"GVFS protocol\"), there is no intention to port\nthat fuller test suite to `contrib/scalar/`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/Makefile          | 17 +++++--\n contrib/scalar/t/Makefile        | 78 ++++++++++++++++++++++++++++++++\n contrib/scalar/t/t9099-scalar.sh | 17 +++++++\n 3 files changed, 109 insertions(+), 3 deletions(-)\n create mode 100644 contrib/scalar/t/Makefile\n create mode 100755 contrib/scalar/t/t9099-scalar.sh\n\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nindex f6f0036f0fa..231b1ee1796 100644\n--- a/contrib/scalar/Makefile\n+++ b/contrib/scalar/Makefile\n@@ -3,6 +3,7 @@ QUIET_SUBDIR1  =\n \n ifneq ($(findstring s,$(MAKEFLAGS)),s)\n ifndef V\n+\tQUIET_GEN      = @echo '   ' GEN $@;\n \tQUIET_SUBDIR0  = +@subdir=\n \tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n \t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n@@ -20,7 +21,7 @@ include ../../config.mak.uname\n TARGETS = scalar$(X) scalar.o\n GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n \n-all: scalar$(X)\n+all: scalar$(X) ../../bin-wrappers/scalar\n \n $(GITLIBS):\n \t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)\n@@ -29,6 +30,16 @@ $(TARGETS): $(GITLIBS) scalar.c\n \t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)\n \n clean:\n-\t$(RM) $(TARGETS)\n+\t$(RM) $(TARGETS) ../../bin-wrappers/scalar\n \n-.PHONY: $(GITLIBS) all clean FORCE\n+../../bin-wrappers/scalar: ../../wrap-for-bin.sh Makefile\n+\t@mkdir -p ../../bin-wrappers\n+\t$(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \\\n+\t     -e 's|@@BUILD_DIR@@|$(shell cd ../.. && pwd)|' \\\n+\t     -e 's|@@PROG@@|contrib/scalar/scalar$(X)|' < $< > $@ && \\\n+\tchmod +x $@\n+\n+test: all\n+\t$(MAKE) -C t\n+\n+.PHONY: $(GITLIBS) all clean test FORCE\ndiff --git a/contrib/scalar/t/Makefile b/contrib/scalar/t/Makefile\nnew file mode 100644\nindex 00000000000..6170672bb37\n--- /dev/null\n+++ b/contrib/scalar/t/Makefile\n@@ -0,0 +1,78 @@\n+# Run scalar tests\n+#\n+# Copyright (c) 2005,2021 Junio C Hamano, Johannes Schindelin\n+#\n+\n+-include ../../../config.mak.autogen\n+-include ../../../config.mak\n+\n+SHELL_PATH ?= $(SHELL)\n+PERL_PATH ?= /usr/bin/perl\n+RM ?= rm -f\n+PROVE ?= prove\n+DEFAULT_TEST_TARGET ?= test\n+TEST_LINT ?= test-lint\n+\n+ifdef TEST_OUTPUT_DIRECTORY\n+TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results\n+else\n+TEST_RESULTS_DIRECTORY = ../../../t/test-results\n+endif\n+\n+# Shell quote;\n+SHELL_PATH_SQ = $(subst ','\\'',$(SHELL_PATH))\n+PERL_PATH_SQ = $(subst ','\\'',$(PERL_PATH))\n+TEST_RESULTS_DIRECTORY_SQ = $(subst ','\\'',$(TEST_RESULTS_DIRECTORY))\n+\n+T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))\n+\n+all: $(DEFAULT_TEST_TARGET)\n+\n+test: $(TEST_LINT)\n+\t$(MAKE) aggregate-results-and-cleanup\n+\n+prove: $(TEST_LINT)\n+\t@echo \"*** prove ***\"; GIT_CONFIG=.git/config $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)\n+\t$(MAKE) clean-except-prove-cache\n+\n+$(T):\n+\t@echo \"*** $@ ***\"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)\n+\n+clean-except-prove-cache:\n+\t$(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)'\n+\t$(RM) -r valgrind/bin\n+\n+clean: clean-except-prove-cache\n+\t$(RM) .prove\n+\n+test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax\n+\n+test-lint-duplicates:\n+\t@dups=`echo $(T) | tr ' ' '\\n' | sed 's/-.*//' | sort | uniq -d` && \\\n+\t\ttest -z \"$$dups\" || { \\\n+\t\techo >&2 \"duplicate test numbers:\" $$dups; exit 1; }\n+\n+test-lint-executable:\n+\t@bad=`for i in $(T); do test -x \"$$i\" || echo $$i; done` && \\\n+\t\ttest -z \"$$bad\" || { \\\n+\t\techo >&2 \"non-executable tests:\" $$bad; exit 1; }\n+\n+test-lint-shell-syntax:\n+\t@'$(PERL_PATH_SQ)' ../../../t/check-non-portable-shell.pl $(T)\n+\n+aggregate-results-and-cleanup: $(T)\n+\t$(MAKE) aggregate-results\n+\t$(MAKE) clean\n+\n+aggregate-results:\n+\tfor f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \\\n+\t\techo \"$$f\"; \\\n+\tdone | '$(SHELL_PATH_SQ)' ../../../t/aggregate-results.sh\n+\n+valgrind:\n+\t$(MAKE) GIT_TEST_OPTS=\"$(GIT_TEST_OPTS) --valgrind\"\n+\n+test-results:\n+\tmkdir -p test-results\n+\n+.PHONY: $(T) aggregate-results clean valgrind\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nnew file mode 100755\nindex 00000000000..16f2b72b126\n--- /dev/null\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -0,0 +1,17 @@\n+#!/bin/sh\n+\n+test_description='test the `scalar` command'\n+\n+TEST_DIRECTORY=$PWD/../../../t\n+export TEST_DIRECTORY\n+\n+# Make it work with --no-bin-wrappers\n+PATH=$PWD/..:$PATH\n+\n+. ../../../t/test-lib.sh\n+\n+test_expect_success 'scalar shows a usage' '\n+\ttest_expect_code 129 scalar -h\n+'\n+\n+test_done\n-- \ngitgitgadget\n\n"},{"id":"441808","messageId":"a39b9c812146f6721353a066670d248159161615.1637363025.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v8.git.1637363024.gitgitgadget@gmail.com","subject":"[PATCH v8 05/17] cmake: optionally build `scalar`, too","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-19T23:03:32Z","receivedAt":"2021-11-19T23:03:54Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe CMake configuration unfortunately does not let us easily encapsulate\nScalar's build definition in the `contrib/scalar/` subdirectory: The\n`scalar` executable needs to link in `libgit.a` and `common-main.o`, for\nexample.\n\nAlso, `scalar.c` includes Git's header files, which means that\n`scalar.c` needs to be compiled with the very same flags as `libgit.a`\nlest `scalar.o` and `libgit.a` have different ideas of, say,\n`platform_SHA_CTX`, which would naturally lead to memory corruption.\n\nTo alleviate that somewhat, we guard the inclusion of Scalar via the\n`INCLUDE_SCALAR` environment variable.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/buildsystems/CMakeLists.txt | 14 ++++++++++++++\n 1 file changed, 14 insertions(+)\n\ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex fd1399c440f..dd7496b0322 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -729,6 +729,13 @@ if(CURL_FOUND)\n \tendif()\n endif()\n \n+if(DEFINED ENV{INCLUDE_SCALAR} AND NOT ENV{INCLUDE_SCALAR} STREQUAL \"\")\n+\tadd_executable(scalar ${CMAKE_SOURCE_DIR}/contrib/scalar/scalar.c)\n+\ttarget_link_libraries(scalar common-main)\n+\tset_target_properties(scalar PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/contrib/scalar)\n+\tset_target_properties(scalar PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/contrib/scalar)\n+endif()\n+\n parse_makefile_for_executables(git_builtin_extra \"BUILT_INS\")\n \n option(SKIP_DASHED_BUILT_INS \"Skip hardlinking the dashed versions of the built-ins\")\n@@ -953,6 +960,13 @@ string(REPLACE \"@@BUILD_DIR@@\" \"${CMAKE_BINARY_DIR}\" content \"${content}\")\n string(REPLACE \"@@PROG@@\" \"git-cvsserver\" content \"${content}\")\n file(WRITE ${CMAKE_BINARY_DIR}/bin-wrappers/git-cvsserver ${content})\n \n+if(DEFINED ENV{INCLUDE_SCALAR} AND NOT ENV{INCLUDE_SCALAR} STREQUAL \"\")\n+\tfile(STRINGS ${CMAKE_SOURCE_DIR}/wrap-for-bin.sh content NEWLINE_CONSUME)\n+\tstring(REPLACE \"@@BUILD_DIR@@\" \"${CMAKE_BINARY_DIR}\" content \"${content}\")\n+\tstring(REPLACE \"@@PROG@@\" \"contrib/scalar/scalar${EXE_EXTENSION}\" content \"${content}\")\n+\tfile(WRITE ${CMAKE_BINARY_DIR}/bin-wrappers/scalar ${content})\n+endif()\n+\n #options for configuring test options\n option(PERL_TESTS \"Perform tests that use perl\" ON)\n option(PYTHON_TESTS \"Perform tests that use python\" ON)\n-- \ngitgitgadget\n\n"},{"id":"441809","messageId":"8e3542e43f7507282672dc8d0926ac1cc44b335a.1637363025.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v8.git.1637363024.gitgitgadget@gmail.com","subject":"[PATCH v8 06/17] ci: also run the `scalar` tests","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-19T23:03:33Z","receivedAt":"2021-11-19T23:03:56Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nSince Scalar depends on `libgit.a`, it makes sense to ensure in the CI\nand the PR builds that it does not get broken in case of industrious\nrefactorings of the core Git code.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n .github/workflows/main.yml | 15 +++++++++++++++\n ci/run-build-and-tests.sh  |  1 +\n ci/run-test-slice.sh       |  5 +++++\n 3 files changed, 21 insertions(+)\n\ndiff --git a/.github/workflows/main.yml b/.github/workflows/main.yml\nindex 6ed6a9e8076..6eda6be895d 100644\n--- a/.github/workflows/main.yml\n+++ b/.github/workflows/main.yml\n@@ -89,6 +89,13 @@ jobs:\n         HOME: ${{runner.workspace}}\n         NO_PERL: 1\n       run: . /etc/profile && ci/make-test-artifacts.sh artifacts\n+    - name: build Scalar\n+      shell: bash\n+      run: |\n+        make -C contrib/scalar &&\n+        mkdir -p artifacts/bin-wrappers artifacts/contrib/scalar &&\n+        cp contrib/scalar/scalar.exe artifacts/contrib/scalar/ &&\n+        cp bin-wrappers/scalar artifacts/bin-wrappers/\n     - name: zip up tracked files\n       run: git archive -o artifacts/tracked.tar.gz HEAD\n     - name: upload tracked files and build artifacts\n@@ -157,6 +164,8 @@ jobs:\n       run: compat\\vcbuild\\vcpkg_copy_dlls.bat release\n     - name: generate Visual Studio solution\n       shell: bash\n+      env:\n+        INCLUDE_SCALAR: YesPlease\n       run: |\n         cmake `pwd`/contrib/buildsystems/ -DCMAKE_PREFIX_PATH=`pwd`/compat/vcbuild/vcpkg/installed/x64-windows \\\n         -DNO_GETTEXT=YesPlease -DPERL_TESTS=OFF -DPYTHON_TESTS=OFF -DCURL_NO_CURL_CMAKE=ON\n@@ -170,6 +179,12 @@ jobs:\n       run: |\n         mkdir -p artifacts &&\n         eval \"$(make -n artifacts-tar INCLUDE_DLLS_IN_ARTIFACTS=YesPlease ARTIFACTS_DIRECTORY=artifacts NO_GETTEXT=YesPlease 2>&1 | grep ^tar)\"\n+    - name: copy Scalar\n+      shell: bash\n+      run: |\n+        mkdir -p artifacts/bin-wrappers artifacts/contrib/scalar &&\n+        cp contrib/scalar/scalar.exe artifacts/contrib/scalar/ &&\n+        cp bin-wrappers/scalar artifacts/bin-wrappers/\n     - name: zip up tracked files\n       run: git archive -o artifacts/tracked.tar.gz HEAD\n     - name: upload tracked files and build artifacts\ndiff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh\nindex cc62616d806..07cedd25ff1 100755\n--- a/ci/run-build-and-tests.sh\n+++ b/ci/run-build-and-tests.sh\n@@ -49,6 +49,7 @@ linux-gcc-4.8|pedantic)\n \tmake test\n \t;;\n esac\n+make -C contrib/scalar test\n \n check_unignored_build_artifacts\n \ndiff --git a/ci/run-test-slice.sh b/ci/run-test-slice.sh\nindex f8c2c3106a2..b741fd8f361 100755\n--- a/ci/run-test-slice.sh\n+++ b/ci/run-test-slice.sh\n@@ -14,4 +14,9 @@ make --quiet -C t T=\"$(cd t &&\n \t./helper/test-tool path-utils slice-tests \"$1\" \"$2\" t[0-9]*.sh |\n \ttr '\\n' ' ')\"\n \n+if test 0 = \"$1\"\n+then\n+\tmake -C contrib/scalar test\n+fi\n+\n check_unignored_build_artifacts\n-- \ngitgitgadget\n\n"},{"id":"441810","messageId":"385abdb8d8e4743a16d6a1dc38737322354560d5.1637363025.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v8.git.1637363024.gitgitgadget@gmail.com","subject":"[PATCH v8 07/17] scalar: 'register' sets recommended config and starts maintenance","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-19T23:03:34Z","receivedAt":"2021-11-19T23:03:57Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nLet's start implementing the `register` command. With this commit,\nrecommended settings are configured upon `scalar register`, and Git's\nbackground maintenance is started.\n\nThe recommended config settings may very well change in the future. For\nexample, once the built-in FSMonitor is available, we will want to\nenable it upon `scalar register`. For that reason, we explicitly support\nrunning `scalar register` in an already-registered enlistment.\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 249 ++++++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt |  18 ++-\n 2 files changed, 266 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 7cff29e0fcd..03d5f84c764 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -5,11 +5,260 @@\n #include \"cache.h\"\n #include \"gettext.h\"\n #include \"parse-options.h\"\n+#include \"config.h\"\n+#include \"run-command.h\"\n+\n+/*\n+ * Remove the deepest subdirectory in the provided path string. Path must not\n+ * include a trailing path separator. Returns 1 if parent directory found,\n+ * otherwise 0.\n+ */\n+static int strbuf_parent_directory(struct strbuf *buf)\n+{\n+\tsize_t len = buf->len;\n+\tsize_t offset = offset_1st_component(buf->buf);\n+\tchar *path_sep = find_last_dir_sep(buf->buf + offset);\n+\tstrbuf_setlen(buf, path_sep ? path_sep - buf->buf : offset);\n+\n+\treturn buf->len < len;\n+}\n+\n+static void setup_enlistment_directory(int argc, const char **argv,\n+\t\t\t\t       const char * const *usagestr,\n+\t\t\t\t       const struct option *options,\n+\t\t\t\t       struct strbuf *enlistment_root)\n+{\n+\tstruct strbuf path = STRBUF_INIT;\n+\tchar *root;\n+\tint enlistment_found = 0;\n+\n+\tif (startup_info->have_repository)\n+\t\tBUG(\"gitdir already set up?!?\");\n+\n+\tif (argc > 1)\n+\t\tusage_with_options(usagestr, options);\n+\n+\t/* find the worktree, determine its corresponding root */\n+\tif (argc == 1)\n+\t\tstrbuf_add_absolute_path(&path, argv[0]);\n+\telse if (strbuf_getcwd(&path) < 0)\n+\t\tdie(_(\"need a working directory\"));\n+\n+\tstrbuf_trim_trailing_dir_sep(&path);\n+\tdo {\n+\t\tconst size_t len = path.len;\n+\n+\t\t/* check if currently in enlistment root with src/ workdir */\n+\t\tstrbuf_addstr(&path, \"/src\");\n+\t\tif (is_nonbare_repository_dir(&path)) {\n+\t\t\tif (enlistment_root)\n+\t\t\t\tstrbuf_add(enlistment_root, path.buf, len);\n+\n+\t\t\tenlistment_found = 1;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\t/* reset to original path */\n+\t\tstrbuf_setlen(&path, len);\n+\n+\t\t/* check if currently in workdir */\n+\t\tif (is_nonbare_repository_dir(&path)) {\n+\t\t\tif (enlistment_root) {\n+\t\t\t\t/*\n+\t\t\t\t * If the worktree's directory's name is `src`, the enlistment is the\n+\t\t\t\t * parent directory, otherwise it is identical to the worktree.\n+\t\t\t\t */\n+\t\t\t\troot = strip_path_suffix(path.buf, \"src\");\n+\t\t\t\tstrbuf_addstr(enlistment_root, root ? root : path.buf);\n+\t\t\t\tfree(root);\n+\t\t\t}\n+\n+\t\t\tenlistment_found = 1;\n+\t\t\tbreak;\n+\t\t}\n+\t} while (strbuf_parent_directory(&path));\n+\n+\tif (!enlistment_found)\n+\t\tdie(_(\"could not find enlistment root\"));\n+\n+\tif (chdir(path.buf) < 0)\n+\t\tdie_errno(_(\"could not switch to '%s'\"), path.buf);\n+\n+\tstrbuf_release(&path);\n+\tsetup_git_directory();\n+}\n+\n+static int run_git(const char *arg, ...)\n+{\n+\tstruct strvec argv = STRVEC_INIT;\n+\tva_list args;\n+\tconst char *p;\n+\tint res;\n+\n+\tva_start(args, arg);\n+\tstrvec_push(&argv, arg);\n+\twhile ((p = va_arg(args, const char *)))\n+\t\tstrvec_push(&argv, p);\n+\tva_end(args);\n+\n+\tres = run_command_v_opt(argv.v, RUN_GIT_CMD);\n+\n+\tstrvec_clear(&argv);\n+\treturn res;\n+}\n+\n+static int set_recommended_config(void)\n+{\n+\tstruct {\n+\t\tconst char *key;\n+\t\tconst char *value;\n+\t} config[] = {\n+\t\t{ \"am.keepCR\", \"true\" },\n+\t\t{ \"core.FSCache\", \"true\" },\n+\t\t{ \"core.multiPackIndex\", \"true\" },\n+\t\t{ \"core.preloadIndex\", \"true\" },\n+#ifndef WIN32\n+\t\t{ \"core.untrackedCache\", \"true\" },\n+#else\n+\t\t/*\n+\t\t * Unfortunately, Scalar's Functional Tests demonstrated\n+\t\t * that the untracked cache feature is unreliable on Windows\n+\t\t * (which is a bummer because that platform would benefit the\n+\t\t * most from it). For some reason, freshly created files seem\n+\t\t * not to update the directory's `lastModified` time\n+\t\t * immediately, but the untracked cache would need to rely on\n+\t\t * that.\n+\t\t *\n+\t\t * Therefore, with a sad heart, we disable this very useful\n+\t\t * feature on Windows.\n+\t\t */\n+\t\t{ \"core.untrackedCache\", \"false\" },\n+#endif\n+\t\t{ \"core.logAllRefUpdates\", \"true\" },\n+\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n+\t\t{ \"credential.validate\", \"false\" }, /* GCM4W-only */\n+\t\t{ \"gc.auto\", \"0\" },\n+\t\t{ \"gui.GCWarning\", \"false\" },\n+\t\t{ \"index.threads\", \"true\" },\n+\t\t{ \"index.version\", \"4\" },\n+\t\t{ \"merge.stat\", \"false\" },\n+\t\t{ \"merge.renames\", \"false\" },\n+\t\t{ \"pack.useBitmaps\", \"false\" },\n+\t\t{ \"pack.useSparse\", \"true\" },\n+\t\t{ \"receive.autoGC\", \"false\" },\n+\t\t{ \"reset.quiet\", \"true\" },\n+\t\t{ \"feature.manyFiles\", \"false\" },\n+\t\t{ \"feature.experimental\", \"false\" },\n+\t\t{ \"fetch.unpackLimit\", \"1\" },\n+\t\t{ \"fetch.writeCommitGraph\", \"false\" },\n+#ifdef WIN32\n+\t\t{ \"http.sslBackend\", \"schannel\" },\n+#endif\n+\t\t{ \"status.aheadBehind\", \"false\" },\n+\t\t{ \"commitGraph.generationVersion\", \"1\" },\n+\t\t{ \"core.autoCRLF\", \"false\" },\n+\t\t{ \"core.safeCRLF\", \"false\" },\n+\t\t{ \"fetch.showForcedUpdates\", \"false\" },\n+\t\t{ NULL, NULL },\n+\t};\n+\tint i;\n+\tchar *value;\n+\n+\tfor (i = 0; config[i].key; i++) {\n+\t\tif (git_config_get_string(config[i].key, &value)) {\n+\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"created\");\n+\t\t\tif (git_config_set_gently(config[i].key,\n+\t\t\t\t\t\t  config[i].value) < 0)\n+\t\t\t\treturn error(_(\"could not configure %s=%s\"),\n+\t\t\t\t\t     config[i].key, config[i].value);\n+\t\t} else {\n+\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"exists\");\n+\t\t\tfree(value);\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * The `log.excludeDecoration` setting is special because it allows\n+\t * for multiple values.\n+\t */\n+\tif (git_config_get_string(\"log.excludeDecoration\", &value)) {\n+\t\ttrace2_data_string(\"scalar\", the_repository,\n+\t\t\t\t   \"log.excludeDecoration\", \"created\");\n+\t\tif (git_config_set_multivar_gently(\"log.excludeDecoration\",\n+\t\t\t\t\t\t   \"refs/prefetch/*\",\n+\t\t\t\t\t\t   CONFIG_REGEX_NONE, 0))\n+\t\t\treturn error(_(\"could not configure \"\n+\t\t\t\t       \"log.excludeDecoration\"));\n+\t} else {\n+\t\ttrace2_data_string(\"scalar\", the_repository,\n+\t\t\t\t   \"log.excludeDecoration\", \"exists\");\n+\t\tfree(value);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int start_maintenance(void)\n+{\n+\treturn run_git(\"maintenance\", \"start\", NULL);\n+}\n+\n+static int add_enlistment(void)\n+{\n+\tint res;\n+\n+\tif (!the_repository->worktree)\n+\t\tdie(_(\"Scalar enlistments require a worktree\"));\n+\n+\tres = run_git(\"config\", \"--global\", \"--get\", \"--fixed-value\",\n+\t\t      \"scalar.repo\", the_repository->worktree, NULL);\n+\n+\t/*\n+\t * If the setting is already there, then do nothing.\n+\t */\n+\tif (!res)\n+\t\treturn 0;\n+\n+\treturn run_git(\"config\", \"--global\", \"--add\",\n+\t\t       \"scalar.repo\", the_repository->worktree, NULL);\n+}\n+\n+static int register_dir(void)\n+{\n+\tint res = add_enlistment();\n+\n+\tif (!res)\n+\t\tres = set_recommended_config();\n+\n+\tif (!res)\n+\t\tres = start_maintenance();\n+\n+\treturn res;\n+}\n+\n+static int cmd_register(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar register [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn register_dir();\n+}\n \n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"register\", cmd_register },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 5f7131861a5..568987064b2 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,7 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n-scalar <command> [<options>]\n+scalar register [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -29,6 +29,22 @@ will be identical to the worktree.\n The `scalar` command implements various subcommands, and different options\n depending on the subcommand.\n \n+COMMANDS\n+--------\n+\n+Register\n+~~~~~~~~\n+\n+register [<enlistment>]::\n+\tAdds the enlistment's repository to the list of registered repositories\n+\tand starts background maintenance. If `<enlistment>` is not provided,\n+\tthen the enlistment associated with the current working directory is\n+\tregistered.\n++\n+Note: when this subcommand is called in a worktree that is called `src/`, its\n+parent directory is considered to be the Scalar enlistment. If the worktree is\n+_not_ called `src/`, it itself will be considered to be the Scalar enlistment.\n+\n SEE ALSO\n --------\n linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"441811","messageId":"64c6a75353ef10977e56c7a30266d69e8de991ba.1637363025.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v8.git.1637363024.gitgitgadget@gmail.com","subject":"[PATCH v8 08/17] scalar: 'unregister' stops background maintenance","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-19T23:03:35Z","receivedAt":"2021-11-19T23:03:58Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nJust like `scalar register` starts the scheduled background maintenance,\n`scalar unregister` stops it. Note that we use `git maintenance start`\nin `scalar register`, but we do not use `git maintenance stop` in\n`scalar unregister`: this would stop maintenance for _all_ repositories,\nnot just for the one we want to unregister.\n\nThe `unregister` command also removes the corresponding entry from the\n`[scalar]` section in the global Git config.\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 50 ++++++++++++++++++++++++++++++++-------\n contrib/scalar/scalar.txt |  8 +++++++\n 2 files changed, 50 insertions(+), 8 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 03d5f84c764..bab0271c37d 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -198,12 +198,12 @@ static int set_recommended_config(void)\n \treturn 0;\n }\n \n-static int start_maintenance(void)\n+static int toggle_maintenance(int enable)\n {\n-\treturn run_git(\"maintenance\", \"start\", NULL);\n+\treturn run_git(\"maintenance\", enable ? \"start\" : \"unregister\", NULL);\n }\n \n-static int add_enlistment(void)\n+static int add_or_remove_enlistment(int add)\n {\n \tint res;\n \n@@ -214,24 +214,39 @@ static int add_enlistment(void)\n \t\t      \"scalar.repo\", the_repository->worktree, NULL);\n \n \t/*\n-\t * If the setting is already there, then do nothing.\n+\t * If we want to add and the setting is already there, then do nothing.\n+\t * If we want to remove and the setting is not there, then do nothing.\n \t */\n-\tif (!res)\n+\tif ((add && !res) || (!add && res))\n \t\treturn 0;\n \n-\treturn run_git(\"config\", \"--global\", \"--add\",\n+\treturn run_git(\"config\", \"--global\", add ? \"--add\" : \"--unset\",\n+\t\t       add ? \"--no-fixed-value\" : \"--fixed-value\",\n \t\t       \"scalar.repo\", the_repository->worktree, NULL);\n }\n \n static int register_dir(void)\n {\n-\tint res = add_enlistment();\n+\tint res = add_or_remove_enlistment(1);\n \n \tif (!res)\n \t\tres = set_recommended_config();\n \n \tif (!res)\n-\t\tres = start_maintenance();\n+\t\tres = toggle_maintenance(1);\n+\n+\treturn res;\n+}\n+\n+static int unregister_dir(void)\n+{\n+\tint res = 0;\n+\n+\tif (toggle_maintenance(0) < 0)\n+\t\tres = -1;\n+\n+\tif (add_or_remove_enlistment(0) < 0)\n+\t\tres = -1;\n \n \treturn res;\n }\n@@ -254,11 +269,30 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_unregister(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar unregister [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn unregister_dir();\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n \t{ \"register\", cmd_register },\n+\t{ \"unregister\", cmd_unregister },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 568987064b2..d9a79984492 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -9,6 +9,7 @@ SYNOPSIS\n --------\n [verse]\n scalar register [<enlistment>]\n+scalar unregister [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -45,6 +46,13 @@ Note: when this subcommand is called in a worktree that is called `src/`, its\n parent directory is considered to be the Scalar enlistment. If the worktree is\n _not_ called `src/`, it itself will be considered to be the Scalar enlistment.\n \n+Unregister\n+~~~~~~~~~~\n+\n+unregister [<enlistment>]::\n+\tRemove the specified repository from the list of repositories\n+\tregistered with Scalar and stop the scheduled background maintenance.\n+\n SEE ALSO\n --------\n linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"441812","messageId":"f7fc1958b9e5224268077064e49b4f7337a12861.1637363025.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v8.git.1637363024.gitgitgadget@gmail.com","subject":"[PATCH v8 09/17] scalar: let 'unregister' handle a deleted enlistment directory gracefully","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-19T23:03:36Z","receivedAt":"2021-11-19T23:03:59Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nWhen a user deleted an enlistment manually, let's be generous and\n_still_ unregister it.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 46 ++++++++++++++++++++++++++++++++\n contrib/scalar/t/t9099-scalar.sh | 15 +++++++++++\n 2 files changed, 61 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex bab0271c37d..097d3bd478b 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -269,6 +269,24 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int remove_deleted_enlistment(struct strbuf *path)\n+{\n+\tint res = 0;\n+\tstrbuf_realpath_forgiving(path, path->buf, 1);\n+\n+\tif (run_git(\"config\", \"--global\",\n+\t\t    \"--unset\", \"--fixed-value\",\n+\t\t    \"scalar.repo\", path->buf, NULL) < 0)\n+\t\tres = -1;\n+\n+\tif (run_git(\"config\", \"--global\",\n+\t\t    \"--unset\", \"--fixed-value\",\n+\t\t    \"maintenance.repo\", path->buf, NULL) < 0)\n+\t\tres = -1;\n+\n+\treturn res;\n+}\n+\n static int cmd_unregister(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -282,6 +300,34 @@ static int cmd_unregister(int argc, const char **argv)\n \targc = parse_options(argc, argv, NULL, options,\n \t\t\t     usage, 0);\n \n+\t/*\n+\t * Be forgiving when the enlistment or worktree does not even exist any\n+\t * longer; This can be the case if a user deleted the worktree by\n+\t * mistake and _still_ wants to unregister the thing.\n+\t */\n+\tif (argc == 1) {\n+\t\tstruct strbuf src_path = STRBUF_INIT, workdir_path = STRBUF_INIT;\n+\n+\t\tstrbuf_addf(&src_path, \"%s/src/.git\", argv[0]);\n+\t\tstrbuf_addf(&workdir_path, \"%s/.git\", argv[0]);\n+\t\tif (!is_directory(src_path.buf) && !is_directory(workdir_path.buf)) {\n+\t\t\t/* remove possible matching registrations */\n+\t\t\tint res = -1;\n+\n+\t\t\tstrbuf_strip_suffix(&src_path, \"/.git\");\n+\t\t\tres = remove_deleted_enlistment(&src_path) && res;\n+\n+\t\t\tstrbuf_strip_suffix(&workdir_path, \"/.git\");\n+\t\t\tres = remove_deleted_enlistment(&workdir_path) && res;\n+\n+\t\t\tstrbuf_release(&src_path);\n+\t\t\tstrbuf_release(&workdir_path);\n+\t\t\treturn res;\n+\t\t}\n+\t\tstrbuf_release(&src_path);\n+\t\tstrbuf_release(&workdir_path);\n+\t}\n+\n \tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n \n \treturn unregister_dir();\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 16f2b72b126..ef0e8d680d5 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -14,4 +14,19 @@ test_expect_success 'scalar shows a usage' '\n \ttest_expect_code 129 scalar -h\n '\n \n+test_expect_success 'scalar unregister' '\n+\tgit init vanish/src &&\n+\tscalar register vanish/src &&\n+\tgit config --get --global --fixed-value \\\n+\t\tmaintenance.repo \"$(pwd)/vanish/src\" &&\n+\tscalar list >scalar.repos &&\n+\tgrep -F \"$(pwd)/vanish/src\" scalar.repos &&\n+\trm -rf vanish/src/.git &&\n+\tscalar unregister vanish &&\n+\ttest_must_fail git config --get --global --fixed-value \\\n+\t\tmaintenance.repo \"$(pwd)/vanish/src\" &&\n+\tscalar list >scalar.repos &&\n+\t! grep -F \"$(pwd)/vanish/src\" scalar.repos\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"441813","messageId":"fd2680bc945ca37467eeebc93a05dfaef6964ce9.1637363025.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v8.git.1637363024.gitgitgadget@gmail.com","subject":"[PATCH v8 10/17] scalar: implement 'scalar list'","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-19T23:03:37Z","receivedAt":"2021-11-19T23:04:00Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe produced list simply consists of those repositories registered under\nthe multi-valued `scalar.repo` config setting in the user's Git config.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 11 +++++++++++\n contrib/scalar/scalar.txt | 11 ++++++++++-\n 2 files changed, 21 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 097d3bd478b..4feacd8d62b 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -251,6 +251,16 @@ static int unregister_dir(void)\n \treturn res;\n }\n \n+static int cmd_list(int argc, const char **argv)\n+{\n+\tif (argc != 1)\n+\t\tdie(_(\"`scalar list` does not take arguments\"));\n+\n+\tif (run_git(\"config\", \"--global\", \"--get-all\", \"scalar.repo\", NULL) < 0)\n+\t\treturn -1;\n+\treturn 0;\n+}\n+\n static int cmd_register(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -337,6 +347,7 @@ static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n \t{ NULL, NULL},\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex d9a79984492..f93e3d00efd 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,6 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n+scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n \n@@ -28,11 +29,19 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand.\n+depending on the subcommand. With the exception of `list`, all subcommands\n+expect to be run in an enlistment.\n \n COMMANDS\n --------\n \n+List\n+~~~~\n+\n+list::\n+\tList enlistments that are currently registered by Scalar. This\n+\tsubcommand does not need to be run inside an enlistment.\n+\n Register\n ~~~~~~~~\n \n-- \ngitgitgadget\n\n"},{"id":"441814","messageId":"4966a43aad9d887d5530307f4e1b340b34389785.1637363025.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v8.git.1637363024.gitgitgadget@gmail.com","subject":"[PATCH v8 11/17] scalar: implement the `clone` subcommand","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-19T23:03:38Z","receivedAt":"2021-11-19T23:04:00Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis implements Scalar's opinionated `clone` command: it tries to use a\npartial clone and sets up a sparse checkout by default. In contrast to\n`git clone`, `scalar clone` sets up the worktree in the `src/`\nsubdirectory, to encourage a separation between the source files and the\nbuild output (which helps Git tremendously because it avoids untracked\nfiles that have to be specifically ignored when refreshing the index).\n\nAlso, it registers the repository for regular, scheduled maintenance,\nand configures a flurry of configuration settings based on the\nexperience and experiments of the Microsoft Windows and the Microsoft\nOffice development teams.\n\nNote: since the `scalar clone` command is by far the most commonly\ncalled `scalar` subcommand, we document it at the top of the manual\npage.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 201 +++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        |  32 ++++-\n contrib/scalar/t/t9099-scalar.sh |  32 +++++\n 3 files changed, 262 insertions(+), 3 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 4feacd8d62b..43f83dde33b 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -7,6 +7,7 @@\n #include \"parse-options.h\"\n #include \"config.h\"\n #include \"run-command.h\"\n+#include \"refs.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -251,6 +252,205 @@ static int unregister_dir(void)\n \treturn res;\n }\n \n+/* printf-style interface, expects `<key>=<value>` argument */\n+static int set_config(const char *fmt, ...)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tchar *value;\n+\tint res;\n+\tva_list args;\n+\n+\tva_start(args, fmt);\n+\tstrbuf_vaddf(&buf, fmt, args);\n+\tva_end(args);\n+\n+\tvalue = strchr(buf.buf, '=');\n+\tif (value)\n+\t\t*(value++) = '\\0';\n+\tres = git_config_set_gently(buf.buf, value);\n+\tstrbuf_release(&buf);\n+\n+\treturn res;\n+}\n+\n+static char *remote_default_branch(const char *url)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tstruct strbuf out = STRBUF_INIT;\n+\n+\tcp.git_cmd = 1;\n+\tstrvec_pushl(&cp.args, \"ls-remote\", \"--symref\", url, \"HEAD\", NULL);\n+\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n+\t\tconst char *line = out.buf;\n+\n+\t\twhile (*line) {\n+\t\t\tconst char *eol = strchrnul(line, '\\n'), *p;\n+\t\t\tsize_t len = eol - line;\n+\t\t\tchar *branch;\n+\n+\t\t\tif (!skip_prefix(line, \"ref: \", &p) ||\n+\t\t\t    !strip_suffix_mem(line, &len, \"\\tHEAD\")) {\n+\t\t\t\tline = eol + (*eol == '\\n');\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\teol = line + len;\n+\t\t\tif (skip_prefix(p, \"refs/heads/\", &p)) {\n+\t\t\t\tbranch = xstrndup(p, eol - p);\n+\t\t\t\tstrbuf_release(&out);\n+\t\t\t\treturn branch;\n+\t\t\t}\n+\n+\t\t\terror(_(\"remote HEAD is not a branch: '%.*s'\"),\n+\t\t\t      (int)(eol - p), p);\n+\t\t\tstrbuf_release(&out);\n+\t\t\treturn NULL;\n+\t\t}\n+\t}\n+\twarning(_(\"failed to get default branch name from remote; \"\n+\t\t  \"using local default\"));\n+\tstrbuf_reset(&out);\n+\n+\tchild_process_init(&cp);\n+\tcp.git_cmd = 1;\n+\tstrvec_pushl(&cp.args, \"symbolic-ref\", \"--short\", \"HEAD\", NULL);\n+\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n+\t\tstrbuf_trim(&out);\n+\t\treturn strbuf_detach(&out, NULL);\n+\t}\n+\n+\tstrbuf_release(&out);\n+\terror(_(\"failed to get default branch name\"));\n+\treturn NULL;\n+}\n+\n+static int cmd_clone(int argc, const char **argv)\n+{\n+\tconst char *branch = NULL;\n+\tint full_clone = 0;\n+\tstruct option clone_options[] = {\n+\t\tOPT_STRING('b', \"branch\", &branch, N_(\"<branch>\"),\n+\t\t\t   N_(\"branch to checkout after clone\")),\n+\t\tOPT_BOOL(0, \"full-clone\", &full_clone,\n+\t\t\t N_(\"when cloning, create full working directory\")),\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const clone_usage[] = {\n+\t\tN_(\"scalar clone [<options>] [--] <repo> [<dir>]\"),\n+\t\tNULL\n+\t};\n+\tconst char *url;\n+\tchar *enlistment = NULL, *dir = NULL;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint res;\n+\n+\targc = parse_options(argc, argv, NULL, clone_options, clone_usage, 0);\n+\n+\tif (argc == 2) {\n+\t\turl = argv[0];\n+\t\tenlistment = xstrdup(argv[1]);\n+\t} else if (argc == 1) {\n+\t\turl = argv[0];\n+\n+\t\tstrbuf_addstr(&buf, url);\n+\t\t/* Strip trailing slashes, if any */\n+\t\twhile (buf.len > 0 && is_dir_sep(buf.buf[buf.len - 1]))\n+\t\t\tstrbuf_setlen(&buf, buf.len - 1);\n+\t\t/* Strip suffix `.git`, if any */\n+\t\tstrbuf_strip_suffix(&buf, \".git\");\n+\n+\t\tenlistment = find_last_dir_sep(buf.buf);\n+\t\tif (!enlistment) {\n+\t\t\tdie(_(\"cannot deduce worktree name from '%s'\"), url);\n+\t\t}\n+\t\tenlistment = xstrdup(enlistment + 1);\n+\t} else {\n+\t\tusage_msg_opt(_(\"You must specify a repository to clone.\"),\n+\t\t\t      clone_usage, clone_options);\n+\t}\n+\n+\tif (is_directory(enlistment))\n+\t\tdie(_(\"directory '%s' exists already\"), enlistment);\n+\n+\tdir = xstrfmt(\"%s/src\", enlistment);\n+\n+\tstrbuf_reset(&buf);\n+\tif (branch)\n+\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", branch);\n+\telse {\n+\t\tchar *b = repo_default_branch_name(the_repository, 1);\n+\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", b);\n+\t\tfree(b);\n+\t}\n+\n+\tif ((res = run_git(\"-c\", buf.buf, \"init\", \"--\", dir, NULL)))\n+\t\tgoto cleanup;\n+\n+\tif (chdir(dir) < 0) {\n+\t\tres = error_errno(_(\"could not switch to '%s'\"), dir);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tsetup_git_directory();\n+\n+\t/* common-main already logs `argv` */\n+\ttrace2_def_repo(the_repository);\n+\n+\tif (!branch && !(branch = remote_default_branch(url))) {\n+\t\tres = error(_(\"failed to get default branch for '%s'\"), url);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tif (set_config(\"remote.origin.url=%s\", url) ||\n+\t    set_config(\"remote.origin.fetch=\"\n+\t\t       \"+refs/heads/*:refs/remotes/origin/*\") ||\n+\t    set_config(\"remote.origin.promisor=true\") ||\n+\t    set_config(\"remote.origin.partialCloneFilter=blob:none\")) {\n+\t\tres = error(_(\"could not configure remote in '%s'\"), dir);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tif (!full_clone &&\n+\t    (res = run_git(\"sparse-checkout\", \"init\", \"--cone\", NULL)))\n+\t\tgoto cleanup;\n+\n+\tif (set_recommended_config())\n+\t\treturn error(_(\"could not configure '%s'\"), dir);\n+\n+\tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL))) {\n+\t\twarning(_(\"partial clone failed; attempting full clone\"));\n+\n+\t\tif (set_config(\"remote.origin.promisor\") ||\n+\t\t    set_config(\"remote.origin.partialCloneFilter\")) {\n+\t\t\tres = error(_(\"could not configure for full clone\"));\n+\t\t\tgoto cleanup;\n+\t\t}\n+\n+\t\tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL)))\n+\t\t\tgoto cleanup;\n+\t}\n+\n+\tif ((res = set_config(\"branch.%s.remote=origin\", branch)))\n+\t\tgoto cleanup;\n+\tif ((res = set_config(\"branch.%s.merge=refs/heads/%s\",\n+\t\t\t      branch, branch)))\n+\t\tgoto cleanup;\n+\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addf(&buf, \"origin/%s\", branch);\n+\tres = run_git(\"checkout\", \"-f\", \"-t\", buf.buf, NULL);\n+\tif (res)\n+\t\tgoto cleanup;\n+\n+\tres = register_dir();\n+\n+cleanup:\n+\tfree(enlistment);\n+\tfree(dir);\n+\tstrbuf_release(&buf);\n+\treturn res;\n+}\n+\n static int cmd_list(int argc, const char **argv)\n {\n \tif (argc != 1)\n@@ -347,6 +547,7 @@ static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"clone\", cmd_clone },\n \t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex f93e3d00efd..e8730967f16 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,6 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n+scalar clone [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n@@ -29,12 +30,37 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand. With the exception of `list`, all subcommands\n-expect to be run in an enlistment.\n+depending on the subcommand. With the exception of `clone` and `list`, all\n+subcommands expect to be run in an enlistment.\n \n COMMANDS\n --------\n \n+Clone\n+~~~~~\n+\n+clone [<options>] <url> [<enlistment>]::\n+\tClones the specified repository, similar to linkgit:git-clone[1]. By\n+\tdefault, only commit and tree objects are cloned. Once finished, the\n+\tworktree is located at `<enlistment>/src`.\n++\n+The sparse-checkout feature is enabled (except when run with `--full-clone`)\n+and the only files present are those in the top-level directory. Use\n+`git sparse-checkout set` to expand the set of directories you want to see,\n+or `git sparse-checkout disable` to expand to all files (see\n+linkgit:git-sparse-checkout[1] for more details). You can explore the\n+subdirectories outside your sparse-checkout by using `git ls-tree\n+HEAD[:<directory>]`.\n+\n+-b <name>::\n+--branch <name>::\n+\tInstead of checking out the branch pointed to by the cloned\n+\trepository's HEAD, check out the `<name>` branch instead.\n+\n+--[no-]full-clone::\n+\tA sparse-checkout is initialized by default. This behavior can be\n+\tturned off via `--full-clone`.\n+\n List\n ~~~~\n \n@@ -64,7 +90,7 @@ unregister [<enlistment>]::\n \n SEE ALSO\n --------\n-linkgit:git-maintenance[1].\n+linkgit:git-clone[1], linkgit:git-maintenance[1].\n \n Scalar\n ---\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex ef0e8d680d5..984d69e8f75 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -10,6 +10,9 @@ PATH=$PWD/..:$PATH\n \n . ../../../t/test-lib.sh\n \n+GIT_TEST_MAINT_SCHEDULER=\"crontab:test-tool crontab ../cron.txt,launchctl:true,schtasks:true\"\n+export GIT_TEST_MAINT_SCHEDULER\n+\n test_expect_success 'scalar shows a usage' '\n \ttest_expect_code 129 scalar -h\n '\n@@ -29,4 +32,33 @@ test_expect_success 'scalar unregister' '\n \t! grep -F \"$(pwd)/vanish/src\" scalar.repos\n '\n \n+test_expect_success 'set up repository to clone' '\n+\ttest_commit first &&\n+\ttest_commit second &&\n+\ttest_commit third &&\n+\tgit switch -c parallel first &&\n+\tmkdir -p 1/2 &&\n+\ttest_commit 1/2/3 &&\n+\tgit config uploadPack.allowFilter true &&\n+\tgit config uploadPack.allowAnySHA1InWant true\n+'\n+\n+test_expect_success 'scalar clone' '\n+\tsecond=$(git rev-parse --verify second:second.t) &&\n+\tscalar clone \"file://$(pwd)\" cloned &&\n+\t(\n+\t\tcd cloned/src &&\n+\n+\t\tgit config --get --global --fixed-value maintenance.repo \\\n+\t\t\t\"$(pwd)\" &&\n+\n+\t\ttest_path_is_missing 1/2 &&\n+\t\ttest_must_fail git rev-list --missing=print $second &&\n+\t\tgit rev-list $second &&\n+\t\tgit cat-file blob $second >actual &&\n+\t\techo \"second\" >expect &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"441815","messageId":"b00d68b37b07fa9cf68c8898398eddcd4d0d9571.1637363025.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v8.git.1637363024.gitgitgadget@gmail.com","subject":"[PATCH v8 12/17] scalar: teach 'clone' to support the --single-branch option","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-19T23:03:39Z","receivedAt":"2021-11-19T23:04:01Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nJust like `git clone`, the `scalar clone` command now also offers to\nrestrict the clone to a single branch.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          |  9 +++++++--\n contrib/scalar/scalar.txt        | 12 +++++++++++-\n contrib/scalar/t/t9099-scalar.sh |  6 +++++-\n 3 files changed, 23 insertions(+), 4 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 43f83dde33b..516a75be3c4 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -327,12 +327,15 @@ static char *remote_default_branch(const char *url)\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n-\tint full_clone = 0;\n+\tint full_clone = 0, single_branch = 0;\n \tstruct option clone_options[] = {\n \t\tOPT_STRING('b', \"branch\", &branch, N_(\"<branch>\"),\n \t\t\t   N_(\"branch to checkout after clone\")),\n \t\tOPT_BOOL(0, \"full-clone\", &full_clone,\n \t\t\t N_(\"when cloning, create full working directory\")),\n+\t\tOPT_BOOL(0, \"single-branch\", &single_branch,\n+\t\t\t N_(\"only download metadata for the branch that will \"\n+\t\t\t    \"be checked out\")),\n \t\tOPT_END(),\n \t};\n \tconst char * const clone_usage[] = {\n@@ -403,7 +406,9 @@ static int cmd_clone(int argc, const char **argv)\n \n \tif (set_config(\"remote.origin.url=%s\", url) ||\n \t    set_config(\"remote.origin.fetch=\"\n-\t\t       \"+refs/heads/*:refs/remotes/origin/*\") ||\n+\t\t       \"+refs/heads/%s:refs/remotes/origin/%s\",\n+\t\t       single_branch ? branch : \"*\",\n+\t\t       single_branch ? branch : \"*\") ||\n \t    set_config(\"remote.origin.promisor=true\") ||\n \t    set_config(\"remote.origin.partialCloneFilter=blob:none\")) {\n \t\tres = error(_(\"could not configure remote in '%s'\"), dir);\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex e8730967f16..56f744a4aa9 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,7 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n-scalar clone [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n+scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n@@ -57,6 +57,16 @@ HEAD[:<directory>]`.\n \tInstead of checking out the branch pointed to by the cloned\n \trepository's HEAD, check out the `<name>` branch instead.\n \n+--[no-]single-branch::\n+\tClone only the history leading to the tip of a single branch, either\n+\tspecified by the `--branch` option or the primary branch remote's\n+\t`HEAD` points at.\n++\n+Further fetches into the resulting repository will only update the\n+remote-tracking branch for the branch this option was used for the initial\n+cloning. If the HEAD at the remote did not point at any branch when\n+`--single-branch` clone was made, no remote-tracking branch is created.\n+\n --[no-]full-clone::\n \tA sparse-checkout is initialized by default. This behavior can be\n \tturned off via `--full-clone`.\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 984d69e8f75..f60e086d6f9 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -45,13 +45,17 @@ test_expect_success 'set up repository to clone' '\n \n test_expect_success 'scalar clone' '\n \tsecond=$(git rev-parse --verify second:second.t) &&\n-\tscalar clone \"file://$(pwd)\" cloned &&\n+\tscalar clone \"file://$(pwd)\" cloned --single-branch &&\n \t(\n \t\tcd cloned/src &&\n \n \t\tgit config --get --global --fixed-value maintenance.repo \\\n \t\t\t\"$(pwd)\" &&\n \n+\t\tgit for-each-ref --format=\"%(refname)\" refs/remotes/origin/ >actual &&\n+\t\techo \"refs/remotes/origin/parallel\" >expect &&\n+\t\ttest_cmp expect actual &&\n+\n \t\ttest_path_is_missing 1/2 &&\n \t\ttest_must_fail git rev-list --missing=print $second &&\n \t\tgit rev-list $second &&\n-- \ngitgitgadget\n\n"},{"id":"441816","messageId":"771d826bbb1fb6c8ff11b3e6bdd890833289a71e.1637363025.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v8.git.1637363024.gitgitgadget@gmail.com","subject":"[PATCH v8 13/17] scalar: implement the `run` command","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-19T23:03:40Z","receivedAt":"2021-11-19T23:04:03Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nNote: this subcommand is provided primarily for backwards-compatibility,\nfor existing Scalar uses. It is mostly just a shim for `git\nmaintenance`, mapping task names from the way Scalar called them to the\nway Git calls them.\n\nThe reason why those names differ? The background maintenance was first\nimplemented in Scalar, and when it was contributed as a patch series\nimplementing the `git maintenance` command, reviewers suggested better\nnames, those suggestions were accepted before the patches were\nintegrated into core Git.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 64 +++++++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt | 19 ++++++++++++\n 2 files changed, 83 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 516a75be3c4..ca524576011 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -484,6 +484,69 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_run(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tstruct {\n+\t\tconst char *arg, *task;\n+\t} tasks[] = {\n+\t\t{ \"config\", NULL },\n+\t\t{ \"commit-graph\", \"commit-graph\" },\n+\t\t{ \"fetch\", \"prefetch\" },\n+\t\t{ \"loose-objects\", \"loose-objects\" },\n+\t\t{ \"pack-files\", \"incremental-repack\" },\n+\t\t{ NULL, NULL }\n+\t};\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tconst char *usagestr[] = { NULL, NULL };\n+\tint i;\n+\n+\tstrbuf_addstr(&buf, N_(\"scalar run <task> [<enlistment>]\\nTasks:\\n\"));\n+\tfor (i = 0; tasks[i].arg; i++)\n+\t\tstrbuf_addf(&buf, \"\\t%s\\n\", tasks[i].arg);\n+\tusagestr[0] = buf.buf;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usagestr, 0);\n+\n+\tif (!argc)\n+\t\tusage_with_options(usagestr, options);\n+\n+\tif (!strcmp(\"all\", argv[0])) {\n+\t\ti = -1;\n+\t} else {\n+\t\tfor (i = 0; tasks[i].arg && strcmp(tasks[i].arg, argv[0]); i++)\n+\t\t\t; /* keep looking for the task */\n+\n+\t\tif (i > 0 && !tasks[i].arg) {\n+\t\t\terror(_(\"no such task: '%s'\"), argv[0]);\n+\t\t\tusage_with_options(usagestr, options);\n+\t\t}\n+\t}\n+\n+\targc--;\n+\targv++;\n+\tsetup_enlistment_directory(argc, argv, usagestr, options, NULL);\n+\tstrbuf_release(&buf);\n+\n+\tif (i == 0)\n+\t\treturn register_dir();\n+\n+\tif (i > 0)\n+\t\treturn run_git(\"maintenance\", \"run\",\n+\t\t\t       \"--task\", tasks[i].task, NULL);\n+\n+\tif (register_dir())\n+\t\treturn -1;\n+\tfor (i = 1; tasks[i].arg; i++)\n+\t\tif (run_git(\"maintenance\", \"run\",\n+\t\t\t    \"--task\", tasks[i].task, NULL))\n+\t\t\treturn -1;\n+\treturn 0;\n+}\n+\n static int remove_deleted_enlistment(struct strbuf *path)\n {\n \tint res = 0;\n@@ -556,6 +619,7 @@ static struct {\n \t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n+\t{ \"run\", cmd_run },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 56f744a4aa9..39143b08324 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -12,6 +12,7 @@ scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<e\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n+scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -98,6 +99,24 @@ unregister [<enlistment>]::\n \tRemove the specified repository from the list of repositories\n \tregistered with Scalar and stop the scheduled background maintenance.\n \n+Run\n+~~~\n+\n+scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]::\n+\tRun the given maintenance task (or all tasks, if `all` was specified).\n+\tExcept for `all` and `config`, this subcommand simply hands off to\n+\tlinkgit:git-maintenance[1] (mapping `fetch` to `prefetch` and\n+\t`pack-files` to `incremental-repack`).\n++\n+These tasks are run automatically as part of the scheduled maintenance,\n+as soon as the repository is registered with Scalar. It should therefore\n+not be necessary to run this subcommand manually.\n++\n+The `config` task is specific to Scalar and configures all those\n+opinionated default settings that make Git work more efficiently with\n+large repositories. As this task is run as part of `scalar clone`\n+automatically, explicit invocations of this task are rarely needed.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"441817","messageId":"a8b2d26a8309d8ccffb51b009fc04353c44c8edf.1637363025.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v8.git.1637363024.gitgitgadget@gmail.com","subject":"[PATCH v8 14/17] scalar: allow reconfiguring an existing enlistment","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-19T23:03:41Z","receivedAt":"2021-11-19T23:04:04Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis comes in handy during Scalar upgrades, or when config settings were\nmessed up by mistake.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 79 +++++++++++++++++++++-----------\n contrib/scalar/scalar.txt        |  8 ++++\n contrib/scalar/t/t9099-scalar.sh |  8 ++++\n 3 files changed, 67 insertions(+), 28 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex ca524576011..b799decbc2f 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -108,18 +108,20 @@ static int run_git(const char *arg, ...)\n \treturn res;\n }\n \n-static int set_recommended_config(void)\n+static int set_recommended_config(int reconfigure)\n {\n \tstruct {\n \t\tconst char *key;\n \t\tconst char *value;\n+\t\tint overwrite_on_reconfigure;\n \t} config[] = {\n-\t\t{ \"am.keepCR\", \"true\" },\n-\t\t{ \"core.FSCache\", \"true\" },\n-\t\t{ \"core.multiPackIndex\", \"true\" },\n-\t\t{ \"core.preloadIndex\", \"true\" },\n+\t\t/* Required */\n+\t\t{ \"am.keepCR\", \"true\", 1 },\n+\t\t{ \"core.FSCache\", \"true\", 1 },\n+\t\t{ \"core.multiPackIndex\", \"true\", 1 },\n+\t\t{ \"core.preloadIndex\", \"true\", 1 },\n #ifndef WIN32\n-\t\t{ \"core.untrackedCache\", \"true\" },\n+\t\t{ \"core.untrackedCache\", \"true\", 1 },\n #else\n \t\t/*\n \t\t * Unfortunately, Scalar's Functional Tests demonstrated\n@@ -133,28 +135,29 @@ static int set_recommended_config(void)\n \t\t * Therefore, with a sad heart, we disable this very useful\n \t\t * feature on Windows.\n \t\t */\n-\t\t{ \"core.untrackedCache\", \"false\" },\n+\t\t{ \"core.untrackedCache\", \"false\", 1 },\n #endif\n-\t\t{ \"core.logAllRefUpdates\", \"true\" },\n-\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n-\t\t{ \"credential.validate\", \"false\" }, /* GCM4W-only */\n-\t\t{ \"gc.auto\", \"0\" },\n-\t\t{ \"gui.GCWarning\", \"false\" },\n-\t\t{ \"index.threads\", \"true\" },\n-\t\t{ \"index.version\", \"4\" },\n-\t\t{ \"merge.stat\", \"false\" },\n-\t\t{ \"merge.renames\", \"false\" },\n-\t\t{ \"pack.useBitmaps\", \"false\" },\n-\t\t{ \"pack.useSparse\", \"true\" },\n-\t\t{ \"receive.autoGC\", \"false\" },\n-\t\t{ \"reset.quiet\", \"true\" },\n-\t\t{ \"feature.manyFiles\", \"false\" },\n-\t\t{ \"feature.experimental\", \"false\" },\n-\t\t{ \"fetch.unpackLimit\", \"1\" },\n-\t\t{ \"fetch.writeCommitGraph\", \"false\" },\n+\t\t{ \"core.logAllRefUpdates\", \"true\", 1 },\n+\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\", 1 },\n+\t\t{ \"credential.validate\", \"false\", 1 }, /* GCM4W-only */\n+\t\t{ \"gc.auto\", \"0\", 1 },\n+\t\t{ \"gui.GCWarning\", \"false\", 1 },\n+\t\t{ \"index.threads\", \"true\", 1 },\n+\t\t{ \"index.version\", \"4\", 1 },\n+\t\t{ \"merge.stat\", \"false\", 1 },\n+\t\t{ \"merge.renames\", \"false\", 1 },\n+\t\t{ \"pack.useBitmaps\", \"false\", 1 },\n+\t\t{ \"pack.useSparse\", \"true\", 1 },\n+\t\t{ \"receive.autoGC\", \"false\", 1 },\n+\t\t{ \"reset.quiet\", \"true\", 1 },\n+\t\t{ \"feature.manyFiles\", \"false\", 1 },\n+\t\t{ \"feature.experimental\", \"false\", 1 },\n+\t\t{ \"fetch.unpackLimit\", \"1\", 1 },\n+\t\t{ \"fetch.writeCommitGraph\", \"false\", 1 },\n #ifdef WIN32\n-\t\t{ \"http.sslBackend\", \"schannel\" },\n+\t\t{ \"http.sslBackend\", \"schannel\", 1 },\n #endif\n+\t\t/* Optional */\n \t\t{ \"status.aheadBehind\", \"false\" },\n \t\t{ \"commitGraph.generationVersion\", \"1\" },\n \t\t{ \"core.autoCRLF\", \"false\" },\n@@ -166,7 +169,8 @@ static int set_recommended_config(void)\n \tchar *value;\n \n \tfor (i = 0; config[i].key; i++) {\n-\t\tif (git_config_get_string(config[i].key, &value)) {\n+\t\tif ((reconfigure && config[i].overwrite_on_reconfigure) ||\n+\t\t    git_config_get_string(config[i].key, &value)) {\n \t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"created\");\n \t\t\tif (git_config_set_gently(config[i].key,\n \t\t\t\t\t\t  config[i].value) < 0)\n@@ -231,7 +235,7 @@ static int register_dir(void)\n \tint res = add_or_remove_enlistment(1);\n \n \tif (!res)\n-\t\tres = set_recommended_config();\n+\t\tres = set_recommended_config(0);\n \n \tif (!res)\n \t\tres = toggle_maintenance(1);\n@@ -419,7 +423,7 @@ static int cmd_clone(int argc, const char **argv)\n \t    (res = run_git(\"sparse-checkout\", \"init\", \"--cone\", NULL)))\n \t\tgoto cleanup;\n \n-\tif (set_recommended_config())\n+\tif (set_recommended_config(0))\n \t\treturn error(_(\"could not configure '%s'\"), dir);\n \n \tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL))) {\n@@ -484,6 +488,24 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_reconfigure(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar reconfigure [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn set_recommended_config(1);\n+}\n+\n static int cmd_run(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -620,6 +642,7 @@ static struct {\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n \t{ \"run\", cmd_run },\n+\t{ \"reconfigure\", cmd_reconfigure },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 39143b08324..89fd7901585 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -13,6 +13,7 @@ scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n+scalar reconfigure <enlistment>\n \n DESCRIPTION\n -----------\n@@ -117,6 +118,13 @@ opinionated default settings that make Git work more efficiently with\n large repositories. As this task is run as part of `scalar clone`\n automatically, explicit invocations of this task are rarely needed.\n \n+Reconfigure\n+~~~~~~~~~~~\n+\n+After a Scalar upgrade, or when the configuration of a Scalar enlistment\n+was somehow corrupted or changed by mistake, this subcommand allows to\n+reconfigure the enlistment.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex f60e086d6f9..fb5e2efee0a 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -65,4 +65,12 @@ test_expect_success 'scalar clone' '\n \t)\n '\n \n+test_expect_success 'scalar reconfigure' '\n+\tgit init one/src &&\n+\tscalar register one &&\n+\tgit -C one/src config core.preloadIndex false &&\n+\tscalar reconfigure one &&\n+\ttest true = \"$(git -C one/src config core.preloadIndex)\"\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"441818","messageId":"ca284ff34a2aed846471620b11fd73a67942c9a2.1637363025.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v8.git.1637363024.gitgitgadget@gmail.com","subject":"[PATCH v8 15/17] scalar: teach 'reconfigure' to optionally handle all registered enlistments","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-19T23:03:42Z","receivedAt":"2021-11-19T23:04:06Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nAfter a Scalar upgrade, it can come in really handy if there is an easy\nway to reconfigure all Scalar enlistments. This new option offers this\nfunctionality.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 61 ++++++++++++++++++++++++++++++--\n contrib/scalar/scalar.txt        |  9 +++--\n contrib/scalar/t/t9099-scalar.sh |  3 ++\n 3 files changed, 67 insertions(+), 6 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex b799decbc2f..71ca573f3af 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -488,22 +488,77 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int get_scalar_repos(const char *key, const char *value, void *data)\n+{\n+\tstruct string_list *list = data;\n+\n+\tif (!strcmp(key, \"scalar.repo\"))\n+\t\tstring_list_append(list, value);\n+\n+\treturn 0;\n+}\n+\n static int cmd_reconfigure(int argc, const char **argv)\n {\n+\tint all = 0;\n \tstruct option options[] = {\n+\t\tOPT_BOOL('a', \"all\", &all,\n+\t\t\t N_(\"reconfigure all registered enlistments\")),\n \t\tOPT_END(),\n \t};\n \tconst char * const usage[] = {\n-\t\tN_(\"scalar reconfigure [<enlistment>]\"),\n+\t\tN_(\"scalar reconfigure [--all | <enlistment>]\"),\n \t\tNULL\n \t};\n+\tstruct string_list scalar_repos = STRING_LIST_INIT_DUP;\n+\tint i, res = 0;\n+\tstruct repository r = { NULL };\n+\tstruct strbuf commondir = STRBUF_INIT, gitdir = STRBUF_INIT;\n \n \targc = parse_options(argc, argv, NULL, options,\n \t\t\t     usage, 0);\n \n-\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\tif (!all) {\n+\t\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\t\treturn set_recommended_config(1);\n+\t}\n+\n+\tif (argc > 0)\n+\t\tusage_msg_opt(_(\"--all or <enlistment>, but not both\"),\n+\t\t\t      usage, options);\n+\n+\tgit_config(get_scalar_repos, &scalar_repos);\n \n-\treturn set_recommended_config(1);\n+\tfor (i = 0; i < scalar_repos.nr; i++) {\n+\t\tconst char *dir = scalar_repos.items[i].string;\n+\n+\t\tstrbuf_reset(&commondir);\n+\t\tstrbuf_reset(&gitdir);\n+\n+\t\tif (chdir(dir) < 0) {\n+\t\t\twarning_errno(_(\"could not switch to '%s'\"), dir);\n+\t\t\tres = -1;\n+\t\t} else if (discover_git_directory(&commondir, &gitdir) < 0) {\n+\t\t\twarning_errno(_(\"git repository gone in '%s'\"), dir);\n+\t\t\tres = -1;\n+\t\t} else {\n+\t\t\tgit_config_clear();\n+\n+\t\t\tthe_repository = &r;\n+\t\t\tr.commondir = commondir.buf;\n+\t\t\tr.gitdir = gitdir.buf;\n+\n+\t\t\tif (set_recommended_config(1) < 0)\n+\t\t\t\tres = -1;\n+\t\t}\n+\t}\n+\n+\tstring_list_clear(&scalar_repos, 1);\n+\tstrbuf_release(&commondir);\n+\tstrbuf_release(&gitdir);\n+\n+\treturn res;\n }\n \n static int cmd_run(int argc, const char **argv)\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 89fd7901585..737cf563c1a 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -13,7 +13,7 @@ scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n-scalar reconfigure <enlistment>\n+scalar reconfigure [ --all | <enlistment> ]\n \n DESCRIPTION\n -----------\n@@ -32,8 +32,8 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand. With the exception of `clone` and `list`, all\n-subcommands expect to be run in an enlistment.\n+depending on the subcommand. With the exception of `clone`, `list` and\n+`reconfigure --all`, all subcommands expect to be run in an enlistment.\n \n COMMANDS\n --------\n@@ -125,6 +125,9 @@ After a Scalar upgrade, or when the configuration of a Scalar enlistment\n was somehow corrupted or changed by mistake, this subcommand allows to\n reconfigure the enlistment.\n \n+With the `--all` option, all enlistments currently registered with Scalar\n+will be reconfigured. Use this option after each Scalar upgrade.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex fb5e2efee0a..58af546fd84 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -70,6 +70,9 @@ test_expect_success 'scalar reconfigure' '\n \tscalar register one &&\n \tgit -C one/src config core.preloadIndex false &&\n \tscalar reconfigure one &&\n+\ttest true = \"$(git -C one/src config core.preloadIndex)\" &&\n+\tgit -C one/src config core.preloadIndex false &&\n+\tscalar reconfigure -a &&\n \ttest true = \"$(git -C one/src config core.preloadIndex)\"\n '\n \n-- \ngitgitgadget\n\n"},{"id":"441819","messageId":"9983eb8912c6deede05282af617c0ede3ea52f58.1637363025.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v8.git.1637363024.gitgitgadget@gmail.com","subject":"[PATCH v8 16/17] scalar: implement the `delete` command","fromName":"Matthew John Cheetham via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-19T23:03:43Z","receivedAt":"2021-11-19T23:04:08Z","isPatch":true,"sender":{"key":"mjcheetham@outlook.com","avatar":"https://avatars.githubusercontent.com/u/5658207?v=4"},"body":"From: Matthew John Cheetham <mjcheetham@outlook.com>\n\nDelete an enlistment by first unregistering the repository and then\ndeleting the enlistment directory (usually the directory containing the\nworktree `src/` directory).\n\nOn Windows, if the current directory is inside the enlistment's\ndirectory, change to the parent of the enlistment directory, to allow us\nto delete the enlistment (directories used by processes e.g. as current\nworking directories cannot be deleted on Windows).\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Matthew John Cheetham <mjcheetham@outlook.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 63 ++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        |  8 ++++\n contrib/scalar/t/t9099-scalar.sh |  9 +++++\n 3 files changed, 80 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 71ca573f3af..c53697ad6a0 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -8,6 +8,8 @@\n #include \"config.h\"\n #include \"run-command.h\"\n #include \"refs.h\"\n+#include \"dir.h\"\n+#include \"packfile.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -328,6 +330,33 @@ static char *remote_default_branch(const char *url)\n \treturn NULL;\n }\n \n+static int delete_enlistment(struct strbuf *enlistment)\n+{\n+#ifdef WIN32\n+\tstruct strbuf parent = STRBUF_INIT;\n+#endif\n+\n+\tif (unregister_dir())\n+\t\tdie(_(\"failed to unregister repository\"));\n+\n+#ifdef WIN32\n+\t/*\n+\t * Change the current directory to one outside of the enlistment so\n+\t * that we may delete everything underneath it.\n+\t */\n+\tstrbuf_addbuf(&parent, enlistment);\n+\tstrbuf_parent_directory(&parent);\n+\tif (chdir(parent.buf) < 0)\n+\t\tdie_errno(_(\"could not switch to '%s'\"), parent.buf);\n+\tstrbuf_release(&parent);\n+#endif\n+\n+\tif (remove_dir_recursively(enlistment, 0))\n+\t\tdie(_(\"failed to delete enlistment directory\"));\n+\n+\treturn 0;\n+}\n+\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n@@ -688,6 +717,39 @@ static int cmd_unregister(int argc, const char **argv)\n \treturn unregister_dir();\n }\n \n+static int cmd_delete(int argc, const char **argv)\n+{\n+\tchar *cwd = xgetcwd();\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar delete <enlistment>\"),\n+\t\tNULL\n+\t};\n+\tstruct strbuf enlistment = STRBUF_INIT;\n+\tint res = 0;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tif (argc != 1)\n+\t\tusage_with_options(usage, options);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, &enlistment);\n+\n+\tif (dir_inside_of(cwd, enlistment.buf) >= 0)\n+\t\tres = error(_(\"refusing to delete current working directory\"));\n+\telse {\n+\t\tclose_object_store(the_repository->objects);\n+\t\tres = delete_enlistment(&enlistment);\n+\t}\n+\tstrbuf_release(&enlistment);\n+\tfree(cwd);\n+\n+\treturn res;\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n@@ -698,6 +760,7 @@ static struct {\n \t{ \"unregister\", cmd_unregister },\n \t{ \"run\", cmd_run },\n \t{ \"reconfigure\", cmd_reconfigure },\n+\t{ \"delete\", cmd_delete },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 737cf563c1a..f416d637289 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -14,6 +14,7 @@ scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n scalar reconfigure [ --all | <enlistment> ]\n+scalar delete <enlistment>\n \n DESCRIPTION\n -----------\n@@ -128,6 +129,13 @@ reconfigure the enlistment.\n With the `--all` option, all enlistments currently registered with Scalar\n will be reconfigured. Use this option after each Scalar upgrade.\n \n+Delete\n+~~~~~~\n+\n+delete <enlistment>::\n+\tThis subcommand lets you delete an existing Scalar enlistment from your\n+\tlocal file system, unregistering the repository.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 58af546fd84..2e1502ad45e 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -76,4 +76,13 @@ test_expect_success 'scalar reconfigure' '\n \ttest true = \"$(git -C one/src config core.preloadIndex)\"\n '\n \n+test_expect_success 'scalar delete without enlistment shows a usage' '\n+\ttest_expect_code 129 scalar delete\n+'\n+\n+test_expect_success 'scalar delete with enlistment' '\n+\tscalar delete cloned &&\n+\ttest_path_is_missing cloned\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"441820","messageId":"889f613ab188bdc883d85f40988a3eda2dad19a5.1637363025.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v8.git.1637363024.gitgitgadget@gmail.com","subject":"[PATCH v8 17/17] scalar: implement the `version` command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-19T23:03:44Z","receivedAt":"2021-11-19T23:04:09Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe .NET version of Scalar has a `version` command. This was necessary\nbecause it was versioned independently of Git.\n\nSince Scalar is now tightly coupled with Git, it does not make sense for\nthem to show different versions. Therefore, it shows the same output as\n`git version`. For backwards-compatibility with the .NET version,\n`scalar version` prints to `stderr`, though (`git version` prints to\n`stdout` instead).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c | 39 +++++++++++++++++++++++++++++++++++++++\n 1 file changed, 39 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex c53697ad6a0..1fc4965bebb 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -10,6 +10,7 @@\n #include \"refs.h\"\n #include \"dir.h\"\n #include \"packfile.h\"\n+#include \"help.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -357,6 +358,15 @@ static int delete_enlistment(struct strbuf *enlistment)\n \treturn 0;\n }\n \n+/*\n+ * Dummy implementation; Using `get_version_info()` would cause a link error\n+ * without this.\n+ */\n+void load_builtin_commands(const char *prefix, struct cmdnames *cmds)\n+{\n+\tdie(\"not implemented\");\n+}\n+\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n@@ -750,6 +760,34 @@ static int cmd_delete(int argc, const char **argv)\n \treturn res;\n }\n \n+static int cmd_version(int argc, const char **argv)\n+{\n+\tint verbose = 0, build_options = 0;\n+\tstruct option options[] = {\n+\t\tOPT__VERBOSE(&verbose, N_(\"include Git version\")),\n+\t\tOPT_BOOL(0, \"build-options\", &build_options,\n+\t\t\t N_(\"include Git's build options\")),\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar verbose [-v | --verbose] [--build-options]\"),\n+\t\tNULL\n+\t};\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tif (argc != 0)\n+\t\tusage_with_options(usage, options);\n+\n+\tget_version_info(&buf, build_options);\n+\tfprintf(stderr, \"%s\\n\", buf.buf);\n+\tstrbuf_release(&buf);\n+\n+\treturn 0;\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n@@ -761,6 +799,7 @@ static struct {\n \t{ \"run\", cmd_run },\n \t{ \"reconfigure\", cmd_reconfigure },\n \t{ \"delete\", cmd_delete },\n+\t{ \"version\", cmd_version },\n \t{ NULL, NULL},\n };\n \n-- \ngitgitgadget\n"},{"id":"441857","messageId":"CABPp-BG=fcKq2Ng2gan3HbBGcT7WCMhtZCP6m2xjA5BSuTekOg@mail.gmail.com","threadId":"56409","inReplyTo":"pull.1005.v8.git.1637363024.gitgitgadget@gmail.com","subject":"Re: [PATCH v8 00/17] Upstreaming the Scalar command","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-11-20T17:22:02Z","receivedAt":"2021-11-20T17:22:16Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Fri, Nov 19, 2021 at 3:03 PM Johannes Schindelin via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> tl;dr: This series contributes the core part of the Scalar command to the\n> Git project. This command provides an opinionated way to create and\n> configure Git repositories with a focus on very large repositories.\n\nI thought after\nhttps://lore.kernel.org/git/nycvar.QRO.7.76.6.2110062241150.395@tvgsbejvaqbjf.bet/\nthat you'd update merge.renames to true on what is now patch 7.  Did\nyou end up changing your mind, or was this overlooked?\n\nOther than that, this round looks good to me.  (I have no opinion on\nthe build system integration, other than that I like it being optional\nand not installed by default.)\n"},{"id":"441934","messageId":"nycvar.QRO.7.76.6.2111221317390.63@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"CABPp-BG=fcKq2Ng2gan3HbBGcT7WCMhtZCP6m2xjA5BSuTekOg@mail.gmail.com","subject":"Re: [PATCH v8 00/17] Upstreaming the Scalar command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-11-22T12:21:58Z","receivedAt":"2021-11-22T12:24:32Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Elijah,\n\nOn Sat, 20 Nov 2021, Elijah Newren wrote:\n\n> On Fri, Nov 19, 2021 at 3:03 PM Johannes Schindelin via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n> >\n> > tl;dr: This series contributes the core part of the Scalar command to\n> > the Git project. This command provides an opinionated way to create\n> > and configure Git repositories with a focus on very large\n> > repositories.\n>\n> I thought after\n> https://lore.kernel.org/git/nycvar.QRO.7.76.6.2110062241150.395@tvgsbejvaqbjf.bet/\n> that you'd update merge.renames to true on what is now patch 7.  Did\n> you end up changing your mind, or was this overlooked?\n\nOops! Thank you so much for the reminder.\n\nWill fix. I do not plan on sending out a new iteration for a few more days\nbecause I do not want to send lots of patches to the list right now,\nreviewer bandwidth seems to be stretched quite a bit already.\n\n> Other than that, this round looks good to me.  (I have no opinion on\n> the build system integration, other than that I like it being optional\n> and not installed by default.)\n\nYes, I very much wanted to keep this optional and as well-encapsulated as\npossible for the moment. (Hence the way it integrates with Git's build\nprocess.)\n\nThank you for chiming in!\n\nCiao,\nDscho\n"},{"id":"441968","messageId":"211122.865yskyw25.gmgdl@evledraar.gmail.com","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2111221317390.63@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v8 00/17] Upstreaming the Scalar command","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-11-22T16:36:03Z","receivedAt":"2021-11-22T16:50:03Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Nov 22 2021, Johannes Schindelin wrote:\n\n> Hi Elijah,\n>\n> On Sat, 20 Nov 2021, Elijah Newren wrote:\n>\n>> On Fri, Nov 19, 2021 at 3:03 PM Johannes Schindelin via GitGitGadget\n>> <gitgitgadget@gmail.com> wrote:\n>> >\n>> > tl;dr: This series contributes the core part of the Scalar command to\n>> > the Git project. This command provides an opinionated way to create\n>> > and configure Git repositories with a focus on very large\n>> > repositories.\n>>\n>> I thought after\n>> https://lore.kernel.org/git/nycvar.QRO.7.76.6.2110062241150.395@tvgsbejvaqbjf.bet/\n>> that you'd update merge.renames to true on what is now patch 7.  Did\n>> you end up changing your mind, or was this overlooked?\n>\n> Oops! Thank you so much for the reminder.\n>\n> Will fix. I do not plan on sending out a new iteration for a few more days\n> because I do not want to send lots of patches to the list right now,\n> reviewer bandwidth seems to be stretched quite a bit already.\n\nBandwidth which is further stretched by continuing to send updates to\nthis topic while ignoring outstanding feedback.\n\nI.e. \"seen\" being broken now due to a merger of this topic and another\ntopic of mine, which as noted in [1] is really just revealing an\nexisting breakage in this topic, which I sent you an unresponded-to\npatch to fix almost a month ago.\n\n>> Other than that, this round looks good to me.  (I have no opinion on\n>> the build system integration, other than that I like it being optional\n>> and not installed by default.)\n>\n> Yes, I very much wanted to keep this optional and as well-encapsulated as\n> possible for the moment. (Hence the way it integrates with Git's build\n> process.)\n>\n> Thank you for chiming in!\n\nWhatever disagreement we have about the particulars of how scalar lands\nin-tree is one thing, and I'd be the first to admit that some of the\nstuff I've been suggesting is just my opinion.\n\nBut I've also been pointing out in reviews of this series (all/most of\nwhich you've ignored) that there's specific things that are\ncategorically broken in this series, and clearly not working as you\nintend them to work. And you're simply ignoring those reports.\n\nOne of those is that your topic here changes CI in in a way that you\nclearly didn't intend, and which is an emergent unintended effect of how\nyou're approaching this scalar integration.\n\nI.e. the compile-only CI targets now doing tests as a result, which is\nbroken, and the combination of that and my CI topic revealed that\nbreakage.\n\nAnyway, as noted in [2] I was hoping to leave this whole scalar thing\nbehind, since I got tired of those reports/suggestions being\nignored. I'm only replying here again because it's clear that you don't\nunderstand some of the things this scalar topic breaks/changes, and the\nonly reason you wouldn't understand that is because you've been ignoring\nspecific reports/patches-on top that address those breakages.\n\n1. https://lore.kernel.org/git/211122.86ee78yxts.gmgdl@evledraar.gmail.com/\n2. https://lore.kernel.org/git/211110.86czn8hyis.gmgdl@evledraar.gmail.com/\n"},{"id":"442031","messageId":"nycvar.QRO.7.76.6.2111222304070.63@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"211122.865yskyw25.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH v8 00/17] Upstreaming the Scalar command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-11-22T22:08:24Z","receivedAt":"2021-11-22T22:08:40Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Ævar,\n\nOn Mon, 22 Nov 2021, Ævar Arnfjörð Bjarmason wrote:\n\n> On Mon, Nov 22 2021, Johannes Schindelin wrote:\n>\n> > On Sat, 20 Nov 2021, Elijah Newren wrote:\n> >\n> >> On Fri, Nov 19, 2021 at 3:03 PM Johannes Schindelin via GitGitGadget\n> >> <gitgitgadget@gmail.com> wrote:\n> >> >\n> >> > tl;dr: This series contributes the core part of the Scalar command to\n> >> > the Git project. This command provides an opinionated way to create\n> >> > and configure Git repositories with a focus on very large\n> >> > repositories.\n> >>\n> >> I thought after\n> >> https://lore.kernel.org/git/nycvar.QRO.7.76.6.2110062241150.395@tvgsbejvaqbjf.bet/\n> >> that you'd update merge.renames to true on what is now patch 7.  Did\n> >> you end up changing your mind, or was this overlooked?\n> >\n> > Oops! Thank you so much for the reminder.\n> >\n> > Will fix. I do not plan on sending out a new iteration for a few more days\n> > because I do not want to send lots of patches to the list right now,\n> > reviewer bandwidth seems to be stretched quite a bit already.\n>\n> Bandwidth which is further stretched by continuing to send updates to\n> this topic while ignoring outstanding feedback.\n\nThe feedback you are referring to is probably the repeated demand to\nintegrate Scalar deeply into Git's build process.\n\nAs I have tired of replying, it is not the time for that yet.\n\nRepeating that demand does not make it more sensible, nor does it\nmagically make it the right time.\n\nNor is it credible to call the build \"broken\" when it does what it is\nsupposed to do, thank you very much.\n\nCiao,\nJohannes\n"},{"id":"442067","messageId":"211123.86y25fwxup.gmgdl@evledraar.gmail.com","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2111222304070.63@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v8 00/17] Upstreaming the Scalar command","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-11-22T23:29:13Z","receivedAt":"2021-11-22T23:54:12Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Nov 22 2021, Johannes Schindelin wrote:\n\n> On Mon, 22 Nov 2021, Ævar Arnfjörð Bjarmason wrote:\n>\n>> On Mon, Nov 22 2021, Johannes Schindelin wrote:\n>>\n>> > On Sat, 20 Nov 2021, Elijah Newren wrote:\n>> >\n>> >> On Fri, Nov 19, 2021 at 3:03 PM Johannes Schindelin via GitGitGadget\n>> >> <gitgitgadget@gmail.com> wrote:\n>> >> >\n>> >> > tl;dr: This series contributes the core part of the Scalar command to\n>> >> > the Git project. This command provides an opinionated way to create\n>> >> > and configure Git repositories with a focus on very large\n>> >> > repositories.\n>> >>\n>> >> I thought after\n>> >> https://lore.kernel.org/git/nycvar.QRO.7.76.6.2110062241150.395@tvgsbejvaqbjf.bet/\n>> >> that you'd update merge.renames to true on what is now patch 7.  Did\n>> >> you end up changing your mind, or was this overlooked?\n>> >\n>> > Oops! Thank you so much for the reminder.\n>> >\n>> > Will fix. I do not plan on sending out a new iteration for a few more days\n>> > because I do not want to send lots of patches to the list right now,\n>> > reviewer bandwidth seems to be stretched quite a bit already.\n>>\n>> Bandwidth which is further stretched by continuing to send updates to\n>> this topic while ignoring outstanding feedback.\n>\n> The feedback you are referring to is probably the repeated demand to\n> integrate Scalar deeply into Git's build process.\n>\n> As I have tired of replying, it is not the time for that yet.\n>\n> Repeating that demand does not make it more sensible, nor does it\n> magically make it the right time.\n\nI'm not repeating that demand. I clearly also think the approach you're\ninsisting picking isn't a good one, but let's leave that aside.\n\nWhat I'm referring to with \"I've also been pointing out\" in the context\nyou elided is that if you:\n\n 1. Check out your topic\n 2. Apply my proposed patch on top (a newer version than on-list is\n    in avar/scalar-move-build-from-contrib-2 in my GH fork)\n 3. Push both to CI\n 4. Diff the two logs of the runs (or just manually click through\n    and inspect them)\n\nYou'd have seen before sending your version of the CI integration that\nthe difference in behavior that started with your version of the topic\nwas particular to the contrib/scalar/ integration, but not the top-level\nMakefile integration. I.e. adding the scalar tests to the previously\nbuild-only jobs.\n\nI've been noting that as clearly as I'm able to in numerous past\nexchanges. You've either ignored those reports, or like here,\nselectivtely replied only to parts of what I've told you.\n\nI.e. something like \"I'm not going 100% for the approach you\nsuggest\". Sure, I'm not saying you have to. But I also noted that the\npatch with that suggested approach can be considered a bug report\nagainst your series.\n\nThe reason that patch isn't split into two things, one fixing all the\nissues I noticed, and another implementing some \"alternate build\"\napproach is that I found that to be impossible to do.\n\nThose issues are all particular to emergent effects of the build\nintegration you're choosing to go with.\n\nE.g. in the case of \"seen\" being broken the CI simply runs \"make test\"\nas it did before, and scalar integrates into that, and you can run that\ntarget without having built anything already.\n\n> Nor is it credible to call the build \"broken\" when it does what it is\n> supposed to do, thank you very much.\n\nHere's specific commands showing that it's broken.\n\nOn your version (I've got v7 locally, but the same is true of v8):\n\n    $ make clean; make -C contrib/scalar test\n    [...]\n        CC hook.o\n        CC version.o\n        CC help.o\n        AR libgit.a\n    make[1]: Leaving directory '/home/avar/g/git'\n        SUBDIR ../..\n    make[1]: Entering directory '/home/avar/g/git'\n        * new link flags\n        CC contrib/scalar/scalar.o\n        LINK contrib/scalar/scalar\n    make[1]: Leaving directory '/home/avar/g/git'\n    make -C t\n    make[1]: Entering directory '/home/avar/g/git/contrib/scalar/t'\n    *** prove ***\n    error: GIT-BUILD-OPTIONS missing (has Git been built?).\n    t9099-scalar.sh .. Dubious, test returned 1 (wstat 256, 0x100)\n    No subtests run \n    \n    Test Summary Report\n    -------------------\n    t9099-scalar.sh (Wstat: 256 Tests: 0 Failed: 0)\n      Non-zero exit status: 1\n      Parse errors: No plan found in TAP output\n\nOn the patch I proposed to apply on top:\n\n    $ make clean; make test T=t9099-scalar.sh\n    [...]\n        CC t/helper/test-xml-encode.o\n        GEN bin-wrappers/git\n        GEN bin-wrappers/git-receive-pack\n        GEN bin-wrappers/git-shell\n        GEN bin-wrappers/git-upload-archive\n        GEN bin-wrappers/git-upload-pack\n        GEN bin-wrappers/scalar\n        GEN bin-wrappers/git-cvsserver\n        GEN bin-wrappers/test-fake-ssh\n        GEN bin-wrappers/test-tool\n        LINK t/helper/test-fake-ssh\n        LINK t/helper/test-tool\n    make -C t/ all\n    make[1]: Entering directory '/home/avar/g/git/t'\n    rm -f -r 'test-results'\n    *** prove ***\n    t9099-scalar.sh .. ok   \n    All tests successful.\n\nYou might correctly note that this doesn't work either on that version\n(or for any other existing test in t/):\n\n    $ make clean >/dev/null; make -C t T=t9099-scalar.sh \n    GIT_VERSION = 2.34.GIT-dev\n    make: Entering directory '/home/avar/g/git/t'\n    rm -f -r 'test-results'\n    *** prove ***\n    error: GIT-BUILD-OPTIONS missing (has Git been built?).\n    t9099-scalar.sh .. Dubious, test returned 1 (wstat 256, 0x100)\n    No subtests run \n    \n    Test Summary Report\n    -------------------\n    t9099-scalar.sh (Wstat: 256 Tests: 0 Failed: 0)\n      Non-zero exit status: 1\n      Parse errors: No plan found in TAP output\n\nWhich is true, but that's not broken because it's not attempting to\nbuild the top-level via some incomplete integration, but your version\nis.\n"},{"id":"442128","messageId":"nycvar.QRO.7.76.6.2111231249080.63@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"211123.86y25fwxup.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH v8 00/17] Upstreaming the Scalar command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-11-23T11:52:19Z","receivedAt":"2021-11-23T11:52:36Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Ævar,\n\nOn Tue, 23 Nov 2021, Ævar Arnfjörð Bjarmason wrote:\n\n> [...]\n>     $ make clean; make -C contrib/scalar test\n>     [...]\n>         CC hook.o\n>         CC version.o\n>         CC help.o\n>         AR libgit.a\n>     make[1]: Leaving directory '/home/avar/g/git'\n>         SUBDIR ../..\n>     make[1]: Entering directory '/home/avar/g/git'\n>         * new link flags\n>         CC contrib/scalar/scalar.o\n>         LINK contrib/scalar/scalar\n>     make[1]: Leaving directory '/home/avar/g/git'\n>     make -C t\n>     make[1]: Entering directory '/home/avar/g/git/contrib/scalar/t'\n>     *** prove ***\n>     error: GIT-BUILD-OPTIONS missing (has Git been built?).\n>     t9099-scalar.sh .. Dubious, test returned 1 (wstat 256, 0x100)\n>     No subtests run\n\nThat's cute. You seem to have missed that this is `contrib/`? The\nassumption of pretty much _everything_ in there is that Git was already\nbuilt.\n\nTry this at home: `make clean && make -C contrib/subtree/ test`\n\nYep. It \"fails\" in the same way. \"has Git been built?\".\n\nSo if that was all the evidence in favor of that misinformation \"Scalar's\nbuild is broken! Broken, broken, BROKEN!\", I think we can now let it rest.\nAt last.\n\nCiao,\nJohannes\n"},{"id":"442145","messageId":"211123.86ee77uj18.gmgdl@evledraar.gmail.com","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2111231249080.63@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v8 00/17] Upstreaming the Scalar command","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-11-23T12:45:15Z","receivedAt":"2021-11-23T12:57:11Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Nov 23 2021, Johannes Schindelin wrote:\n\n> Hi Ævar,\n>\n> On Tue, 23 Nov 2021, Ævar Arnfjörð Bjarmason wrote:\n>\n>> [...]\n>>     $ make clean; make -C contrib/scalar test\n>>     [...]\n>>         CC hook.o\n>>         CC version.o\n>>         CC help.o\n>>         AR libgit.a\n>>     make[1]: Leaving directory '/home/avar/g/git'\n>>         SUBDIR ../..\n>>     make[1]: Entering directory '/home/avar/g/git'\n>>         * new link flags\n>>         CC contrib/scalar/scalar.o\n>>         LINK contrib/scalar/scalar\n>>     make[1]: Leaving directory '/home/avar/g/git'\n>>     make -C t\n>>     make[1]: Entering directory '/home/avar/g/git/contrib/scalar/t'\n>>     *** prove ***\n>>     error: GIT-BUILD-OPTIONS missing (has Git been built?).\n>>     t9099-scalar.sh .. Dubious, test returned 1 (wstat 256, 0x100)\n>>     No subtests run\n>\n> That's cute. You seem to have missed that this is `contrib/`? The\n> assumption of pretty much _everything_ in there is that Git was already\n> built.\n>\n> Try this at home: `make clean && make -C contrib/subtree/ test`\n>\n> Yep. It \"fails\" in the same way. \"has Git been built?\".\n>\n> So if that was all the evidence in favor of that misinformation \"Scalar's\n> build is broken! Broken, broken, BROKEN!\", I think we can now let it rest.\n> At last.\n\nNo, it doesn't fail in the same way. Really, it seems like you're either\nnot fully reading through E-Mails before replying, or entirely\nmisunderstanding what I'm saying. I highlighted the difference in\n\"[...]that's not broken because[...]\" below the context you're quoting.\n\nI'm specifically pointing out the difference between how these act:\n\n    make clean; make -C contrib/subtree/ test\n    make clean; make -C t\n\nWhich fail right away without trying to build anything, and how your:\n\n    make clean; make -C contrib/scalar test\n\nWon't fail right away, but get most of the way towards building what it\nneeds at the top-level.\n\nSo yes, I fully agree with your contrib/subtree example, but it's making\nmy argument for me.\n\nAs I pointed out in the just-sent [1] the main issue is that your latest\niteration added \"make -C contrib/subtree/ test\" in the wrong place, and\nwe thus ended up running those tests in places we didn't intend. The\nmain conflict is that semantic conflict.\n\nBut that failure also highlighted that contrib/scalar/Makefile will run\na build of the top-level C code, only to error out with\n\"GIT-BUILD-OPTIONS\".\n\nThat specifically is what I consider broken. It should either actually\nwork and properly build its dependency, or not even try.\n\nEither one would be fine in this context, it's the in-between that's\nclearly (to me at least) broken. Working software should either attempt\na task and succeed, or not attempt the task at all.\n\n1. https://lore.kernel.org/git/211123.86ilwjujmd.gmgdl@evledraar.gmail.com/\n\n"},{"id":"442147","messageId":"nycvar.QRO.7.76.6.2111231403030.63@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"211123.86ee77uj18.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH v8 00/17] Upstreaming the Scalar command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-11-23T13:05:42Z","receivedAt":"2021-11-23T13:05:58Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Ævar,\n\nOn Tue, 23 Nov 2021, Ævar Arnfjörð Bjarmason wrote:\n\n> On Tue, Nov 23 2021, Johannes Schindelin wrote:\n>\n> > Hi Ævar,\n> >\n> > On Tue, 23 Nov 2021, Ævar Arnfjörð Bjarmason wrote:\n> >\n> >> [...]\n> >>     $ make clean; make -C contrib/scalar test\n> >>     [...]\n> >>         CC hook.o\n> >>         CC version.o\n> >>         CC help.o\n> >>         AR libgit.a\n> >>     make[1]: Leaving directory '/home/avar/g/git'\n> >>         SUBDIR ../..\n> >>     make[1]: Entering directory '/home/avar/g/git'\n> >>         * new link flags\n> >>         CC contrib/scalar/scalar.o\n> >>         LINK contrib/scalar/scalar\n> >>     make[1]: Leaving directory '/home/avar/g/git'\n> >>     make -C t\n> >>     make[1]: Entering directory '/home/avar/g/git/contrib/scalar/t'\n> >>     *** prove ***\n> >>     error: GIT-BUILD-OPTIONS missing (has Git been built?).\n> >>     t9099-scalar.sh .. Dubious, test returned 1 (wstat 256, 0x100)\n> >>     No subtests run\n> >\n> > That's cute. You seem to have missed that this is `contrib/`? The\n> > assumption of pretty much _everything_ in there is that Git was already\n> > built.\n> >\n> > Try this at home: `make clean && make -C contrib/subtree/ test`\n> >\n> > Yep. It \"fails\" in the same way. \"has Git been built?\".\n> >\n> > So if that was all the evidence in favor of that misinformation \"Scalar's\n> > build is broken! Broken, broken, BROKEN!\", I think we can now let it rest.\n> > At last.\n>\n> No, it doesn't fail in the same way. Really, it seems like you're either\n> not fully reading through E-Mails before replying, or entirely\n> misunderstanding what I'm saying.\n\nThat's really... fresh.\n\nI told you half a dozen times that at this point, the build works well\nenough, and that what you keep insisting is a problem simply isn't.\n\nYes, you have to build Git before building Scalar.\n\nHave you actually looked at the design of Scalar? What it does to allow\nworking with large repositories?\n\n_That_ is what counts.\n\nThat you are told to please build Git before running Scalar's tests is\nmaybe a minor annoyance, but not worth the dozens of mails and the weeks\nof delay that you caused over this.\n\nCiao,\nJohannes\n"},{"id":"442628","messageId":"50160d61a41c182d9b8c6c859975f89415de8482.1638273289.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v9.git.1638273289.gitgitgadget@gmail.com","subject":"[PATCH v9 02/17] scalar: create a rudimentary executable","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-30T11:54:34Z","receivedAt":"2021-11-30T11:55:01Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe idea of Scalar (https://github.com/microsoft/scalar), and before\nthat, of VFS for Git, has always been to prove that Git _can_ scale, and\nto upstream whatever strategies have been demonstrated to help.\n\nWith this patch, we start the journey from that C# project to move what\nis left to Git's own `contrib/` directory, reimplementing it in pure C,\nwith the intention to facilitate integrating the functionality into core\nGit all while maintaining backwards-compatibility for existing Scalar\nusers (which will be much easier when both live in the same worktree).\nIt has always been the plan to contribute all of the proven strategies\nback to core Git.\n\nFor example, while the virtual filesystem provided by VFS for Git helped\nthe team developing the Windows operating system to move onto Git, while\ntrying to upstream it we realized that it cannot be done: getting the\nvirtual filesystem to work (which we only managed to implement fully on\nWindows, but not on, say, macOS or Linux), and the required server-side\nsupport for the GVFS protocol, made this not quite feasible.\n\nThe Scalar project learned from that and tackled the problem with\ndifferent tactics: instead of pretending to Git that the working\ndirectory is fully populated, it _specifically_ teaches Git about\npartial clone (which is based on VFS for Git's cache server), about\nsparse checkout (which VFS for Git tried to do transparently, in the\nfile system layer), and regularly runs maintenance tasks to keep the\nrepository in a healthy state.\n\nWith partial clone, sparse checkout and `git maintenance` having been\nupstreamed, there is little left that `scalar.exe` does which `git.exe`\ncannot do. One such thing is that `scalar clone <url>` will\nautomatically set up a partial, sparse clone, and configure\nknown-helpful settings from the start.\n\nSo let's bring this convenience into Git's tree.\n\nThe idea here is that you can (optionally) build Scalar via\n\n\tmake -C contrib/scalar/\n\nThis will build the `scalar` executable and put it into the\ncontrib/scalar/ subdirectory.\n\nThe slightly awkward addition of the `contrib/scalar/*` bits to the\ntop-level `Makefile` are actually really required: we want to link to\n`libgit.a`, which means that we will need to use the very same `CFLAGS`\nand `LDFLAGS` as the rest of Git.\n\nAn early development version of this patch tried to replicate all the\nconditional code in `contrib/scalar/Makefile` (e.g. `NO_POLL`) just like\n`contrib/svn-fe/Makefile` used to do before it was retired. It turned\nout to be quite the whack-a-mole game: the SHA-1-related flags, the\nflags enabling/disabling `compat/poll/`, `compat/regex/`,\n`compat/win32mmap.c` & friends depending on the current platform... To\nput it mildly: it was a major mess.\n\nInstead, this patch makes minimal changes to the top-level `Makefile` so\nthat the bits in `contrib/scalar/` can be compiled and linked, and\nadds a `contrib/scalar/Makefile` that uses the top-level `Makefile` in a\nmost minimal way to do the actual compiling.\n\nNote: With this commit, we only establish the infrastructure, no\nScalar functionality is implemented yet; We will do that incrementally\nover the next few commits.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Makefile                  |  9 +++++++++\n contrib/scalar/.gitignore |  2 ++\n contrib/scalar/Makefile   | 34 ++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.c   | 36 ++++++++++++++++++++++++++++++++++++\n 4 files changed, 81 insertions(+)\n create mode 100644 contrib/scalar/.gitignore\n create mode 100644 contrib/scalar/Makefile\n create mode 100644 contrib/scalar/scalar.c\n\ndiff --git a/Makefile b/Makefile\nindex 12be39ac497..fe898aeea08 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2456,6 +2456,11 @@ OBJECTS += $(FUZZ_OBJS)\n ifndef NO_CURL\n \tOBJECTS += http.o http-walker.o remote-curl.o\n endif\n+\n+SCALAR_SOURCES := contrib/scalar/scalar.c\n+SCALAR_OBJECTS := $(SCALAR_SOURCES:c=o)\n+OBJECTS += $(SCALAR_OBJECTS)\n+\n .PHONY: objects\n objects: $(OBJECTS)\n \n@@ -2589,6 +2594,10 @@ $(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n \t\t$(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)\n \n+contrib/scalar/scalar$X: $(SCALAR_OBJECTS) GIT-LDFLAGS $(GITLIBS)\n+\t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \\\n+\t\t$(filter %.o,$^) $(LIBS)\n+\n $(LIB_FILE): $(LIB_OBJS)\n \t$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^\n \ndiff --git a/contrib/scalar/.gitignore b/contrib/scalar/.gitignore\nnew file mode 100644\nindex 00000000000..ff3d47e84d0\n--- /dev/null\n+++ b/contrib/scalar/.gitignore\n@@ -0,0 +1,2 @@\n+/*.exe\n+/scalar\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nnew file mode 100644\nindex 00000000000..f6f0036f0fa\n--- /dev/null\n+++ b/contrib/scalar/Makefile\n@@ -0,0 +1,34 @@\n+QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir\n+QUIET_SUBDIR1  =\n+\n+ifneq ($(findstring s,$(MAKEFLAGS)),s)\n+ifndef V\n+\tQUIET_SUBDIR0  = +@subdir=\n+\tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n+\t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n+else\n+\texport V\n+endif\n+endif\n+\n+all:\n+\n+include ../../config.mak.uname\n+-include ../../config.mak.autogen\n+-include ../../config.mak\n+\n+TARGETS = scalar$(X) scalar.o\n+GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n+\n+all: scalar$(X)\n+\n+$(GITLIBS):\n+\t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)\n+\n+$(TARGETS): $(GITLIBS) scalar.c\n+\t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)\n+\n+clean:\n+\t$(RM) $(TARGETS)\n+\n+.PHONY: $(GITLIBS) all clean FORCE\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nnew file mode 100644\nindex 00000000000..7cff29e0fcd\n--- /dev/null\n+++ b/contrib/scalar/scalar.c\n@@ -0,0 +1,36 @@\n+/*\n+ * The Scalar command-line interface.\n+ */\n+\n+#include \"cache.h\"\n+#include \"gettext.h\"\n+#include \"parse-options.h\"\n+\n+static struct {\n+\tconst char *name;\n+\tint (*fn)(int, const char **);\n+} builtins[] = {\n+\t{ NULL, NULL},\n+};\n+\n+int cmd_main(int argc, const char **argv)\n+{\n+\tstruct strbuf scalar_usage = STRBUF_INIT;\n+\tint i;\n+\n+\tif (argc > 1) {\n+\t\targv++;\n+\t\targc--;\n+\n+\t\tfor (i = 0; builtins[i].name; i++)\n+\t\t\tif (!strcmp(builtins[i].name, argv[0]))\n+\t\t\t\treturn !!builtins[i].fn(argc, argv);\n+\t}\n+\n+\tstrbuf_addstr(&scalar_usage,\n+\t\t      N_(\"scalar <command> [<options>]\\n\\nCommands:\\n\"));\n+\tfor (i = 0; builtins[i].name; i++)\n+\t\tstrbuf_addf(&scalar_usage, \"\\t%s\\n\", builtins[i].name);\n+\n+\tusage(scalar_usage.buf);\n+}\n-- \ngitgitgadget\n\n"},{"id":"442629","messageId":"74cd64109319748e532339017b3b76a4823f9c86.1638273289.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v9.git.1638273289.gitgitgadget@gmail.com","subject":"[PATCH v9 03/17] scalar: start documenting the command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-30T11:54:35Z","receivedAt":"2021-11-30T11:55:08Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nLet's build up the documentation for the Scalar command along with the\npatches that implement its functionality.\n\nNote: To discourage the feature-incomplete documentation from being\nmistaken for the complete thing, we do not yet provide any way to build\nHTML or manual pages from the text file.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.txt | 38 ++++++++++++++++++++++++++++++++++++++\n 1 file changed, 38 insertions(+)\n create mode 100644 contrib/scalar/scalar.txt\n\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nnew file mode 100644\nindex 00000000000..5f7131861a5\n--- /dev/null\n+++ b/contrib/scalar/scalar.txt\n@@ -0,0 +1,38 @@\n+scalar(1)\n+=========\n+\n+NAME\n+----\n+scalar - an opinionated repository management tool\n+\n+SYNOPSIS\n+--------\n+[verse]\n+scalar <command> [<options>]\n+\n+DESCRIPTION\n+-----------\n+\n+Scalar is an opinionated repository management tool. By creating new\n+repositories or registering existing repositories with Scalar, your Git\n+experience will speed up. Scalar sets advanced Git config settings,\n+maintains your repositories in the background, and helps reduce data sent\n+across the network.\n+\n+An important Scalar concept is the enlistment: this is the top-level directory\n+of the project. It usually contains the subdirectory `src/` which is a Git\n+worktree. This encourages the separation between tracked files (inside `src/`)\n+and untracked files, such as build artifacts (outside `src/`). When registering\n+an existing Git worktree with Scalar whose name is not `src`, the enlistment\n+will be identical to the worktree.\n+\n+The `scalar` command implements various subcommands, and different options\n+depending on the subcommand.\n+\n+SEE ALSO\n+--------\n+linkgit:git-maintenance[1].\n+\n+Scalar\n+---\n+Associated with the linkgit:git[1] suite\n-- \ngitgitgadget\n\n"},{"id":"442630","messageId":"pull.1005.v9.git.1638273289.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v8.git.1637363024.gitgitgadget@gmail.com","subject":"[PATCH v9 00/17] Upstreaming the Scalar command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-30T11:54:32Z","receivedAt":"2021-11-30T11:55:10Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"tl;dr: This series contributes the core part of the Scalar command to the\nGit project. This command provides a convenient way to clone/initialize very\nlarge repositories (think: monorepos).\n\nNote: This patch series' focus is entirely on Scalar, on choosing sensible\ndefaults and offering a delightful user experience around working with\nmonorepos, and not about changing any existing paradigms for contrib/ (even\nif catching up on the mail thread is likely to give interested readers that\nfalse impression).\n\nChanges since v8:\n\n * The rebase on top of v2.34.0, which changed the default merge strategy to\n   ORT, should have changed the default for merge.renames to true. This is\n   now the case.\n * Accommodate preemptively for ab/ci-updates which invalidates assumptions\n   made by this patch series that would still hold true with v2.34.0 but are\n   no longer valid in seen and would trigger CI build breakages.\n\nChanges since v7:\n\n * Clarified in the commit message why we cannot easily encapsulate the\n   Scalar part of the CMake configuration in contrib/scalar/.\n * Improved the README.md.\n\nChanges since v6:\n\n * Rebased on top of v2.34.0.\n * Inserted a commit that adds contrib/scalar/README.md, containing the\n   roadmap of what I have planned for Scalar.\n * The Scalar test's definition of GIT_TEST_MAINT_SCHEDULER has been\n   adjusted to accommodate for a change in v2.32.0..v2.34.0.\n * The config setting defaults now include fetch.showForcedUpdates=false,\n   which has been identified as helping with a performance issue in large\n   repositories.\n * To avoid mistaking the current patch series for being feature-complete\n   enough to unleash onto end users, I moved the Makefile rules to build\n   HTML/manual pages to a later patch series.\n * The patch that adds support for -c <key>=<value> and -C <directory> was\n   moved to its own add-on patch series: While it is obvious that those\n   options are valuable to have, an open question is whether there are other\n   \"pre-command\" options in git that would be useful, too, and I would like\n   to postpone that discussion to that date.\n * I added two patches that I had planned on keeping in an add-on patch\n   series for later, to build and test Scalar as part of the CI. I am still\n   not 100% certain that it is a good idea to do so already now, but let's\n   see what the reviewers have to say.\n\nChanges since v5:\n\n * Fixed the commit message talking about make -C contrib/scalar/Makefile.\n * Fixed the git ls-tree invocation suggested in the manual for scalar\n   clone.\n * Invoking make -C contrib/scalar, then changing a source file of libgit.a\n   and then immediately invoking make -C contrib/scalar again will now\n   implicitly rebuild libgit.a.\n\nChanges since v4:\n\n * scalar delete now refuses to delete anything if it was started from\n   within the enlistment.\n * scalar delete releases any handles to the object store before deleting\n   the enlistment.\n * The OBJECTS list in the Makefile will now include Scalar.\n * scalar register now supports secondary worktrees, in addition to the\n   primary worktree.\n\nChanges since v3:\n\n * Moved the \"Changes since\" section to the top, to make it easier to see\n   what changed.\n * Reworded the commit message of the first patch.\n * Removed the [RFC] prefix because I did not hear any objections against\n   putting this into contrib/.\n\nChanges since v2:\n\n * Adjusted the description of the list command in the manual page , as\n   suggested by Bagas.\n * Addressed two style nits in cmd_run().\n * The documentation of git reconfigure -a was improved.\n\nChanges since v1:\n\n * A couple typos were fixed\n * The code parsing the output of ls-remote was made more readable\n * The indentation used in scalar.txt now consistently uses tabs\n * We no longer hard-code core.bare = false when registering with Scalar\n\n\nBackground\n==========\n\nMicrosoft invested a lot of effort into scaling Git to the needs of the\nWindows operating system source code. Based on the experience of the first\napproach, VFS for Git, the Scalar project was started. Scalar specifically\nhas as its core goal to funnel all improvements into core Git.\n\n\nThe present\n===========\n\nThe Scalar project provides a completely functional non-virtual experience\nfor monorepos. But why stop there. The Scalar project was designed to be a\nself-destructing vehicle to allow those key concepts to be moved into core\nGit itself for the benefit of all. For example, partial clone,\nsparse-checkout, and scheduled background maintenance have already been\nupstreamed and removed from Scalar proper. This patch series provides a\nC-based implementation of the final remaining portions of the Scalar\ncommand. This will make it easier for users to experiment with the Scalar\ncommand. It will also make it substantially easier to experiment with moving\nfunctionality from Scalar into core Git, while maintaining\nbackwards-compatibility for existing Scalar users.\n\nThe C-based Scalar has been shipped to Scalar users, and can be tested by\nany interested reader: https://github.com/microsoft/git/releases/ (it offers\na Git for Windows installer, a macOS package and an Ubuntu package, Scalar\nhas been included since v2.33.0.vfs.0.0).\n\n\nNext steps\n==========\n\nSince there are existing Scalar users, I want to ensure\nbackwards-compatibility with its existing command-line interface. Keeping\nthat in mind, everything in this series is up for discussion.\n\nI obviously believe that Scalar brings a huge benefit, and think that it\nwould be ideal for all of Scalar's learnings to end up in git clone/git\ninit/git maintenance eventually. It is also conceivable, however, that the\nscalar command could graduate to be a core part of Git at some stage in the\nfuture (such a decision would probably depend highly on users' feedback).\nSee also the discussion about the architecture of Scalar\n[https://lore.kernel.org/git/b67bbef4-e4c3-b6a7-1c7f-7d405902ef8b@gmail.com/],\nkicked off by Stolee.\n\nOn top of this patch series, I have lined up a few more:\n\n 1. Implement a scalar diagnose command.\n 2. Use the built-in FSMonitor (that patch series obviously needs to wait\n    for FSMonitor to be integrated).\n 3. Modify the config machinery to be more generous about concurrent writes,\n    say, to the user-wide config.\n 4. A few patches to optionally build and install scalar as part of a\n    regular Git install (also teaching git help scalar to find the Scalar\n    documentation\n\nThese are included in my vfs-with-scalar branch thicket\n[https://github.com/dscho/git/commits/vfs-with-scalar]. On top of that, this\nbranch thicket also includes patches I do not plan on upstreaming, mainly\nbecause they are too specific either to VFS for Git, or they support Azure\nRepos (which does not offer partial clones but speaks the GVFS protocol,\nwhich can be used to emulate partial clones).\n\nOne other thing is very interesting about that vfs-with-scalar branch\nthicket: it contains a GitHub workflow which will run Scalar's quite\nextensive Functional Tests suite. This test suite is quite comprehensive and\ncaught us a lot of bugs in the past, not only in the Scalar code, but also\ncore Git.\n\n\nEpilogue\n========\n\nNow, to address some questions that I imagine every reader has who made it\nthis far:\n\n * Why not put the Scalar functionality directly into core Git, even a\n   built-in? I wanted to provide an easy way for Git contributors to \"play\n   with\" Scalar, without forcing a new top-level command into Git.\n * Why implement the Scalar command in the Git code base? Apart from\n   simplifying Scalar maintenance in the Microsoft port of Git, the tight\n   version coupling between Git and Scalar reduces the maintenance burden\n   even further. Besides, I believe that it will make it much easier to\n   shift functionality from Scalar into core Git, once we took the hurdle of\n   accepting the Scalar code into the code base.\n * Why contribute Scalar to the Git project? We are biased, of course, yet\n   our data-driven approach provides evidence that Scalar helps handling\n   huge repositories with ease. By contributing it to the core Git project,\n   we are able to share it with more users, especially some users who do not\n   want to install Microsoft's fork of Git. We also hope that a lot of\n   Scalar (maybe all of it) will end up in core Git, to benefit even more\n   users.\n\nDerrick Stolee (4):\n  scalar: 'register' sets recommended config and starts maintenance\n  scalar: 'unregister' stops background maintenance\n  scalar: implement 'scalar list'\n  scalar: implement the `run` command\n\nJohannes Schindelin (12):\n  scalar: add a README with a roadmap\n  scalar: create a rudimentary executable\n  scalar: start documenting the command\n  scalar: create test infrastructure\n  cmake: optionally build `scalar`, too\n  ci: also run the `scalar` tests\n  scalar: let 'unregister' handle a deleted enlistment directory\n    gracefully\n  scalar: implement the `clone` subcommand\n  scalar: teach 'clone' to support the --single-branch option\n  scalar: allow reconfiguring an existing enlistment\n  scalar: teach 'reconfigure' to optionally handle all registered\n    enlistments\n  scalar: implement the `version` command\n\nMatthew John Cheetham (1):\n  scalar: implement the `delete` command\n\n .github/workflows/main.yml          |  15 +\n Makefile                            |   9 +\n ci/run-build-and-tests.sh           |   2 +\n ci/run-test-slice.sh                |   5 +\n contrib/buildsystems/CMakeLists.txt |  14 +\n contrib/scalar/.gitignore           |   2 +\n contrib/scalar/Makefile             |  45 ++\n contrib/scalar/README.md            |  82 +++\n contrib/scalar/scalar.c             | 826 ++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt           | 145 +++++\n contrib/scalar/t/Makefile           |  78 +++\n contrib/scalar/t/t9099-scalar.sh    |  88 +++\n 12 files changed, 1311 insertions(+)\n create mode 100644 contrib/scalar/.gitignore\n create mode 100644 contrib/scalar/Makefile\n create mode 100644 contrib/scalar/README.md\n create mode 100644 contrib/scalar/scalar.c\n create mode 100644 contrib/scalar/scalar.txt\n create mode 100644 contrib/scalar/t/Makefile\n create mode 100755 contrib/scalar/t/t9099-scalar.sh\n\n\nbase-commit: cd3e606211bb1cf8bc57f7d76bab98cc17a150bc\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1005%2Fdscho%2Fscalar-the-beginning-v9\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1005/dscho/scalar-the-beginning-v9\nPull-Request: https://github.com/gitgitgadget/git/pull/1005\n\nRange-diff vs v8:\n\n  1:  3a2e28275f1 =  1:  3a2e28275f1 scalar: add a README with a roadmap\n  2:  50160d61a41 =  2:  50160d61a41 scalar: create a rudimentary executable\n  3:  74cd6410931 =  3:  74cd6410931 scalar: start documenting the command\n  4:  37231a4dd07 =  4:  37231a4dd07 scalar: create test infrastructure\n  5:  a39b9c81214 =  5:  a39b9c81214 cmake: optionally build `scalar`, too\n  6:  8e3542e43f7 !  6:  8c6762def30 ci: also run the `scalar` tests\n     @@ .github/workflows/main.yml: jobs:\n           - name: upload tracked files and build artifacts\n      \n       ## ci/run-build-and-tests.sh ##\n     -@@ ci/run-build-and-tests.sh: linux-gcc-4.8|pedantic)\n     - \tmake test\n     - \t;;\n     - esac\n     -+make -C contrib/scalar test\n     +@@ ci/run-build-and-tests.sh: esac\n       \n       check_unignored_build_artifacts\n       \n     ++make && make -C contrib/scalar test\n     ++\n     + save_good_tree\n      \n       ## ci/run-test-slice.sh ##\n      @@ ci/run-test-slice.sh: make --quiet -C t T=\"$(cd t &&\n  7:  385abdb8d8e !  7:  936ee0475ad scalar: 'register' sets recommended config and starts maintenance\n     @@ contrib/scalar/scalar.c\n      +\t\t{ \"index.threads\", \"true\" },\n      +\t\t{ \"index.version\", \"4\" },\n      +\t\t{ \"merge.stat\", \"false\" },\n     -+\t\t{ \"merge.renames\", \"false\" },\n     ++\t\t{ \"merge.renames\", \"true\" },\n      +\t\t{ \"pack.useBitmaps\", \"false\" },\n      +\t\t{ \"pack.useSparse\", \"true\" },\n      +\t\t{ \"receive.autoGC\", \"false\" },\n  8:  64c6a75353e =  8:  09a15f86c3d scalar: 'unregister' stops background maintenance\n  9:  f7fc1958b9e =  9:  42121a5764d scalar: let 'unregister' handle a deleted enlistment directory gracefully\n 10:  fd2680bc945 = 10:  6afb2eb4163 scalar: implement 'scalar list'\n 11:  4966a43aad9 = 11:  dd4e3a4b761 scalar: implement the `clone` subcommand\n 12:  b00d68b37b0 = 12:  abd9c8827cd scalar: teach 'clone' to support the --single-branch option\n 13:  771d826bbb1 = 13:  5601f82dbe1 scalar: implement the `run` command\n 14:  a8b2d26a830 ! 14:  08e4f548aa8 scalar: allow reconfiguring an existing enlistment\n     @@ contrib/scalar/scalar.c: static int set_recommended_config(void)\n      -\t\t{ \"index.threads\", \"true\" },\n      -\t\t{ \"index.version\", \"4\" },\n      -\t\t{ \"merge.stat\", \"false\" },\n     --\t\t{ \"merge.renames\", \"false\" },\n     +-\t\t{ \"merge.renames\", \"true\" },\n      -\t\t{ \"pack.useBitmaps\", \"false\" },\n      -\t\t{ \"pack.useSparse\", \"true\" },\n      -\t\t{ \"receive.autoGC\", \"false\" },\n     @@ contrib/scalar/scalar.c: static int set_recommended_config(void)\n      +\t\t{ \"index.threads\", \"true\", 1 },\n      +\t\t{ \"index.version\", \"4\", 1 },\n      +\t\t{ \"merge.stat\", \"false\", 1 },\n     -+\t\t{ \"merge.renames\", \"false\", 1 },\n     ++\t\t{ \"merge.renames\", \"true\", 1 },\n      +\t\t{ \"pack.useBitmaps\", \"false\", 1 },\n      +\t\t{ \"pack.useSparse\", \"true\", 1 },\n      +\t\t{ \"receive.autoGC\", \"false\", 1 },\n 15:  ca284ff34a2 = 15:  0cec6dbd2cb scalar: teach 'reconfigure' to optionally handle all registered enlistments\n 16:  9983eb8912c = 16:  835f1c79792 scalar: implement the `delete` command\n 17:  889f613ab18 = 17:  4ee1b701c7b scalar: implement the `version` command\n\n-- \ngitgitgadget\n"},{"id":"442631","messageId":"3a2e28275f112d114bbd3ba046f440c5f917fc32.1638273289.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v9.git.1638273289.gitgitgadget@gmail.com","subject":"[PATCH v9 01/17] scalar: add a README with a roadmap","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-30T11:54:33Z","receivedAt":"2021-11-30T11:55:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe Scalar command will be contributed incrementally, over a bunch of\npatch series. Let's document what Scalar is about, and then describe the\npatch series that are planned.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/README.md | 82 ++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 82 insertions(+)\n create mode 100644 contrib/scalar/README.md\n\ndiff --git a/contrib/scalar/README.md b/contrib/scalar/README.md\nnew file mode 100644\nindex 00000000000..634b5771ed3\n--- /dev/null\n+++ b/contrib/scalar/README.md\n@@ -0,0 +1,82 @@\n+# Scalar - an opinionated repository management tool\n+\n+Scalar is an add-on to Git that helps users take advantage of advanced\n+performance features in Git. Originally implemented in C# using .NET Core,\n+based on the learnings from the VFS for Git project, most of the techniques\n+developed by the Scalar project have been integrated into core Git already:\n+\n+* partial clone,\n+* commit graphs,\n+* multi-pack index,\n+* sparse checkout (cone mode),\n+* scheduled background maintenance,\n+* etc\n+\n+This directory contains the remaining parts of Scalar that are not (yet) in\n+core Git.\n+\n+## Roadmap\n+\n+The idea is to populate this directory via incremental patch series and\n+eventually move to a top-level directory next to `gitk-git/` and to `git-gui/`. The\n+current plan involves the following patch series:\n+\n+- `scalar-the-beginning`: The initial patch series which sets up\n+  `contrib/scalar/` and populates it with a minimal `scalar` command that\n+  demonstrates the fundamental ideas.\n+\n+- `scalar-c-and-C`: The `scalar` command learns about two options that can be\n+  specified before the command, `-c <key>=<value>` and `-C <directory>`.\n+\n+- `scalar-diagnose`: The `scalar` command is taught the `diagnose` subcommand.\n+\n+- `scalar-and-builtin-fsmonitor`: The built-in FSMonitor is enabled in `scalar\n+  register` and in `scalar clone`, for an enormous performance boost when\n+  working in large worktrees. This patch series necessarily depends on Jeff\n+  Hostetler's FSMonitor patch series to be integrated into Git.\n+\n+- `scalar-gentler-config-locking`: Scalar enlistments are registered in the\n+  user's Git config. This usually does not represent any problem because it is\n+  rare for a user to register an enlistment. However, in Scalar's functional\n+  tests, Scalar enlistments are created galore, and in parallel, which can lead\n+  to lock contention. This patch series works around that problem by re-trying\n+  to lock the config file in a gentle fashion.\n+\n+- `scalar-extra-docs`: Add some extensive documentation that has been written\n+  in the original Scalar project (all subject to discussion, of course).\n+\n+- `optionally-install-scalar`: Now that Scalar is feature (and documentation)\n+  complete and is verified in CI builds, let's offer to install it.\n+\n+- `move-scalar-to-toplevel`: Now that Scalar is complete, let's move it next to\n+  `gitk-git/` and to `git-gui/`, making it a top-level command.\n+\n+The following two patch series exist in Microsoft's fork of Git and are\n+publicly available. There is no current plan to upstream them, not because I\n+want to withhold these patches, but because I don't think the Git community is\n+interested in these patches.\n+\n+There are some interesting ideas there, but the implementation is too specific\n+to Azure Repos and/or VFS for Git to be of much help in general (and also: my\n+colleagues tried to upstream some patches already and the enthusiasm for\n+integrating things related to Azure Repos and VFS for Git can be summarized in\n+very, very few words).\n+\n+These still exist mainly because the GVFS protocol is what Azure Repos has\n+instead of partial clone, while Git is focused on improving partial clone:\n+\n+- `scalar-with-gvfs`: The primary purpose of this patch series is to support\n+  existing Scalar users whose repositories are hosted in Azure Repos (which\n+  does not support Git's partial clones, but supports its predecessor, the GVFS\n+  protocol, which is used by Scalar to emulate the partial clone).\n+\n+  Since the GVFS protocol will never be supported by core Git, this patch\n+  series will remain in Microsoft's fork of Git.\n+\n+- `run-scalar-functional-tests`: The Scalar project developed a quite\n+  comprehensive set of integration tests (or, \"Functional Tests\"). They are the\n+  sole remaining part of the original C#-based Scalar project, and this patch\n+  adds a GitHub workflow that runs them all.\n+\n+  Since the tests partially depend on features that are only provided in the\n+  `scalar-with-gvfs` patch series, this patch cannot be upstreamed.\n-- \ngitgitgadget\n\n"},{"id":"442632","messageId":"a39b9c812146f6721353a066670d248159161615.1638273289.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v9.git.1638273289.gitgitgadget@gmail.com","subject":"[PATCH v9 05/17] cmake: optionally build `scalar`, too","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-30T11:54:37Z","receivedAt":"2021-11-30T11:55:15Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe CMake configuration unfortunately does not let us easily encapsulate\nScalar's build definition in the `contrib/scalar/` subdirectory: The\n`scalar` executable needs to link in `libgit.a` and `common-main.o`, for\nexample.\n\nAlso, `scalar.c` includes Git's header files, which means that\n`scalar.c` needs to be compiled with the very same flags as `libgit.a`\nlest `scalar.o` and `libgit.a` have different ideas of, say,\n`platform_SHA_CTX`, which would naturally lead to memory corruption.\n\nTo alleviate that somewhat, we guard the inclusion of Scalar via the\n`INCLUDE_SCALAR` environment variable.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/buildsystems/CMakeLists.txt | 14 ++++++++++++++\n 1 file changed, 14 insertions(+)\n\ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex fd1399c440f..dd7496b0322 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -729,6 +729,13 @@ if(CURL_FOUND)\n \tendif()\n endif()\n \n+if(DEFINED ENV{INCLUDE_SCALAR} AND NOT ENV{INCLUDE_SCALAR} STREQUAL \"\")\n+\tadd_executable(scalar ${CMAKE_SOURCE_DIR}/contrib/scalar/scalar.c)\n+\ttarget_link_libraries(scalar common-main)\n+\tset_target_properties(scalar PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/contrib/scalar)\n+\tset_target_properties(scalar PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/contrib/scalar)\n+endif()\n+\n parse_makefile_for_executables(git_builtin_extra \"BUILT_INS\")\n \n option(SKIP_DASHED_BUILT_INS \"Skip hardlinking the dashed versions of the built-ins\")\n@@ -953,6 +960,13 @@ string(REPLACE \"@@BUILD_DIR@@\" \"${CMAKE_BINARY_DIR}\" content \"${content}\")\n string(REPLACE \"@@PROG@@\" \"git-cvsserver\" content \"${content}\")\n file(WRITE ${CMAKE_BINARY_DIR}/bin-wrappers/git-cvsserver ${content})\n \n+if(DEFINED ENV{INCLUDE_SCALAR} AND NOT ENV{INCLUDE_SCALAR} STREQUAL \"\")\n+\tfile(STRINGS ${CMAKE_SOURCE_DIR}/wrap-for-bin.sh content NEWLINE_CONSUME)\n+\tstring(REPLACE \"@@BUILD_DIR@@\" \"${CMAKE_BINARY_DIR}\" content \"${content}\")\n+\tstring(REPLACE \"@@PROG@@\" \"contrib/scalar/scalar${EXE_EXTENSION}\" content \"${content}\")\n+\tfile(WRITE ${CMAKE_BINARY_DIR}/bin-wrappers/scalar ${content})\n+endif()\n+\n #options for configuring test options\n option(PERL_TESTS \"Perform tests that use perl\" ON)\n option(PYTHON_TESTS \"Perform tests that use python\" ON)\n-- \ngitgitgadget\n\n"},{"id":"442633","messageId":"37231a4dd07833807639c8a650185569d0c99af2.1638273289.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v9.git.1638273289.gitgitgadget@gmail.com","subject":"[PATCH v9 04/17] scalar: create test infrastructure","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-30T11:54:36Z","receivedAt":"2021-11-30T11:55:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nTo test the Scalar command, create a test script in contrib/scalar/t\nthat is executed as `make -C contrib/scalar test`. Since Scalar has no\nmeaningful capabilities yet, the only test is rather simple. We will add\nmore tests in subsequent commits that introduce corresponding, new\nfunctionality.\n\nNote: This test script is intended to test `scalar` only lightly, even\nafter all of the functionality is implemented.\n\nA more comprehensive functional (or: integration) test suite can be\nfound at https://github.com/microsoft/scalar; It is used in the workflow\nhttps://github.com/microsoft/git/blob/HEAD/.github/workflows/scalar-functional-tests.yml\nin Microsoft's Git fork. This test suite performs end-to-end tests with\na real remote repository, and is run as part of the regular CI and PR\nbuilds in that fork.\n\nSince those tests require some functionality supported only by\nMicrosoft's Git fork (\"GVFS protocol\"), there is no intention to port\nthat fuller test suite to `contrib/scalar/`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/Makefile          | 17 +++++--\n contrib/scalar/t/Makefile        | 78 ++++++++++++++++++++++++++++++++\n contrib/scalar/t/t9099-scalar.sh | 17 +++++++\n 3 files changed, 109 insertions(+), 3 deletions(-)\n create mode 100644 contrib/scalar/t/Makefile\n create mode 100755 contrib/scalar/t/t9099-scalar.sh\n\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nindex f6f0036f0fa..231b1ee1796 100644\n--- a/contrib/scalar/Makefile\n+++ b/contrib/scalar/Makefile\n@@ -3,6 +3,7 @@ QUIET_SUBDIR1  =\n \n ifneq ($(findstring s,$(MAKEFLAGS)),s)\n ifndef V\n+\tQUIET_GEN      = @echo '   ' GEN $@;\n \tQUIET_SUBDIR0  = +@subdir=\n \tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n \t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n@@ -20,7 +21,7 @@ include ../../config.mak.uname\n TARGETS = scalar$(X) scalar.o\n GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n \n-all: scalar$(X)\n+all: scalar$(X) ../../bin-wrappers/scalar\n \n $(GITLIBS):\n \t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)\n@@ -29,6 +30,16 @@ $(TARGETS): $(GITLIBS) scalar.c\n \t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)\n \n clean:\n-\t$(RM) $(TARGETS)\n+\t$(RM) $(TARGETS) ../../bin-wrappers/scalar\n \n-.PHONY: $(GITLIBS) all clean FORCE\n+../../bin-wrappers/scalar: ../../wrap-for-bin.sh Makefile\n+\t@mkdir -p ../../bin-wrappers\n+\t$(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \\\n+\t     -e 's|@@BUILD_DIR@@|$(shell cd ../.. && pwd)|' \\\n+\t     -e 's|@@PROG@@|contrib/scalar/scalar$(X)|' < $< > $@ && \\\n+\tchmod +x $@\n+\n+test: all\n+\t$(MAKE) -C t\n+\n+.PHONY: $(GITLIBS) all clean test FORCE\ndiff --git a/contrib/scalar/t/Makefile b/contrib/scalar/t/Makefile\nnew file mode 100644\nindex 00000000000..6170672bb37\n--- /dev/null\n+++ b/contrib/scalar/t/Makefile\n@@ -0,0 +1,78 @@\n+# Run scalar tests\n+#\n+# Copyright (c) 2005,2021 Junio C Hamano, Johannes Schindelin\n+#\n+\n+-include ../../../config.mak.autogen\n+-include ../../../config.mak\n+\n+SHELL_PATH ?= $(SHELL)\n+PERL_PATH ?= /usr/bin/perl\n+RM ?= rm -f\n+PROVE ?= prove\n+DEFAULT_TEST_TARGET ?= test\n+TEST_LINT ?= test-lint\n+\n+ifdef TEST_OUTPUT_DIRECTORY\n+TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results\n+else\n+TEST_RESULTS_DIRECTORY = ../../../t/test-results\n+endif\n+\n+# Shell quote;\n+SHELL_PATH_SQ = $(subst ','\\'',$(SHELL_PATH))\n+PERL_PATH_SQ = $(subst ','\\'',$(PERL_PATH))\n+TEST_RESULTS_DIRECTORY_SQ = $(subst ','\\'',$(TEST_RESULTS_DIRECTORY))\n+\n+T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))\n+\n+all: $(DEFAULT_TEST_TARGET)\n+\n+test: $(TEST_LINT)\n+\t$(MAKE) aggregate-results-and-cleanup\n+\n+prove: $(TEST_LINT)\n+\t@echo \"*** prove ***\"; GIT_CONFIG=.git/config $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)\n+\t$(MAKE) clean-except-prove-cache\n+\n+$(T):\n+\t@echo \"*** $@ ***\"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)\n+\n+clean-except-prove-cache:\n+\t$(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)'\n+\t$(RM) -r valgrind/bin\n+\n+clean: clean-except-prove-cache\n+\t$(RM) .prove\n+\n+test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax\n+\n+test-lint-duplicates:\n+\t@dups=`echo $(T) | tr ' ' '\\n' | sed 's/-.*//' | sort | uniq -d` && \\\n+\t\ttest -z \"$$dups\" || { \\\n+\t\techo >&2 \"duplicate test numbers:\" $$dups; exit 1; }\n+\n+test-lint-executable:\n+\t@bad=`for i in $(T); do test -x \"$$i\" || echo $$i; done` && \\\n+\t\ttest -z \"$$bad\" || { \\\n+\t\techo >&2 \"non-executable tests:\" $$bad; exit 1; }\n+\n+test-lint-shell-syntax:\n+\t@'$(PERL_PATH_SQ)' ../../../t/check-non-portable-shell.pl $(T)\n+\n+aggregate-results-and-cleanup: $(T)\n+\t$(MAKE) aggregate-results\n+\t$(MAKE) clean\n+\n+aggregate-results:\n+\tfor f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \\\n+\t\techo \"$$f\"; \\\n+\tdone | '$(SHELL_PATH_SQ)' ../../../t/aggregate-results.sh\n+\n+valgrind:\n+\t$(MAKE) GIT_TEST_OPTS=\"$(GIT_TEST_OPTS) --valgrind\"\n+\n+test-results:\n+\tmkdir -p test-results\n+\n+.PHONY: $(T) aggregate-results clean valgrind\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nnew file mode 100755\nindex 00000000000..16f2b72b126\n--- /dev/null\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -0,0 +1,17 @@\n+#!/bin/sh\n+\n+test_description='test the `scalar` command'\n+\n+TEST_DIRECTORY=$PWD/../../../t\n+export TEST_DIRECTORY\n+\n+# Make it work with --no-bin-wrappers\n+PATH=$PWD/..:$PATH\n+\n+. ../../../t/test-lib.sh\n+\n+test_expect_success 'scalar shows a usage' '\n+\ttest_expect_code 129 scalar -h\n+'\n+\n+test_done\n-- \ngitgitgadget\n\n"},{"id":"442634","messageId":"8c6762def30e676a26d23636e924229f10fa5d09.1638273289.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v9.git.1638273289.gitgitgadget@gmail.com","subject":"[PATCH v9 06/17] ci: also run the `scalar` tests","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-30T11:54:38Z","receivedAt":"2021-11-30T11:55:22Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nSince Scalar depends on `libgit.a`, it makes sense to ensure in the CI\nand the PR builds that it does not get broken in case of industrious\nrefactorings of the core Git code.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n .github/workflows/main.yml | 15 +++++++++++++++\n ci/run-build-and-tests.sh  |  2 ++\n ci/run-test-slice.sh       |  5 +++++\n 3 files changed, 22 insertions(+)\n\ndiff --git a/.github/workflows/main.yml b/.github/workflows/main.yml\nindex 6ed6a9e8076..6eda6be895d 100644\n--- a/.github/workflows/main.yml\n+++ b/.github/workflows/main.yml\n@@ -89,6 +89,13 @@ jobs:\n         HOME: ${{runner.workspace}}\n         NO_PERL: 1\n       run: . /etc/profile && ci/make-test-artifacts.sh artifacts\n+    - name: build Scalar\n+      shell: bash\n+      run: |\n+        make -C contrib/scalar &&\n+        mkdir -p artifacts/bin-wrappers artifacts/contrib/scalar &&\n+        cp contrib/scalar/scalar.exe artifacts/contrib/scalar/ &&\n+        cp bin-wrappers/scalar artifacts/bin-wrappers/\n     - name: zip up tracked files\n       run: git archive -o artifacts/tracked.tar.gz HEAD\n     - name: upload tracked files and build artifacts\n@@ -157,6 +164,8 @@ jobs:\n       run: compat\\vcbuild\\vcpkg_copy_dlls.bat release\n     - name: generate Visual Studio solution\n       shell: bash\n+      env:\n+        INCLUDE_SCALAR: YesPlease\n       run: |\n         cmake `pwd`/contrib/buildsystems/ -DCMAKE_PREFIX_PATH=`pwd`/compat/vcbuild/vcpkg/installed/x64-windows \\\n         -DNO_GETTEXT=YesPlease -DPERL_TESTS=OFF -DPYTHON_TESTS=OFF -DCURL_NO_CURL_CMAKE=ON\n@@ -170,6 +179,12 @@ jobs:\n       run: |\n         mkdir -p artifacts &&\n         eval \"$(make -n artifacts-tar INCLUDE_DLLS_IN_ARTIFACTS=YesPlease ARTIFACTS_DIRECTORY=artifacts NO_GETTEXT=YesPlease 2>&1 | grep ^tar)\"\n+    - name: copy Scalar\n+      shell: bash\n+      run: |\n+        mkdir -p artifacts/bin-wrappers artifacts/contrib/scalar &&\n+        cp contrib/scalar/scalar.exe artifacts/contrib/scalar/ &&\n+        cp bin-wrappers/scalar artifacts/bin-wrappers/\n     - name: zip up tracked files\n       run: git archive -o artifacts/tracked.tar.gz HEAD\n     - name: upload tracked files and build artifacts\ndiff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh\nindex cc62616d806..2ef9fbfdd38 100755\n--- a/ci/run-build-and-tests.sh\n+++ b/ci/run-build-and-tests.sh\n@@ -52,4 +52,6 @@ esac\n \n check_unignored_build_artifacts\n \n+make && make -C contrib/scalar test\n+\n save_good_tree\ndiff --git a/ci/run-test-slice.sh b/ci/run-test-slice.sh\nindex f8c2c3106a2..b741fd8f361 100755\n--- a/ci/run-test-slice.sh\n+++ b/ci/run-test-slice.sh\n@@ -14,4 +14,9 @@ make --quiet -C t T=\"$(cd t &&\n \t./helper/test-tool path-utils slice-tests \"$1\" \"$2\" t[0-9]*.sh |\n \ttr '\\n' ' ')\"\n \n+if test 0 = \"$1\"\n+then\n+\tmake -C contrib/scalar test\n+fi\n+\n check_unignored_build_artifacts\n-- \ngitgitgadget\n\n"},{"id":"442635","messageId":"936ee0475ad8e06bd52fbc711faa2577dfaa08c3.1638273289.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v9.git.1638273289.gitgitgadget@gmail.com","subject":"[PATCH v9 07/17] scalar: 'register' sets recommended config and starts maintenance","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-30T11:54:39Z","receivedAt":"2021-11-30T11:55:23Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nLet's start implementing the `register` command. With this commit,\nrecommended settings are configured upon `scalar register`, and Git's\nbackground maintenance is started.\n\nThe recommended config settings may very well change in the future. For\nexample, once the built-in FSMonitor is available, we will want to\nenable it upon `scalar register`. For that reason, we explicitly support\nrunning `scalar register` in an already-registered enlistment.\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 249 ++++++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt |  18 ++-\n 2 files changed, 266 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 7cff29e0fcd..55a304442d4 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -5,11 +5,260 @@\n #include \"cache.h\"\n #include \"gettext.h\"\n #include \"parse-options.h\"\n+#include \"config.h\"\n+#include \"run-command.h\"\n+\n+/*\n+ * Remove the deepest subdirectory in the provided path string. Path must not\n+ * include a trailing path separator. Returns 1 if parent directory found,\n+ * otherwise 0.\n+ */\n+static int strbuf_parent_directory(struct strbuf *buf)\n+{\n+\tsize_t len = buf->len;\n+\tsize_t offset = offset_1st_component(buf->buf);\n+\tchar *path_sep = find_last_dir_sep(buf->buf + offset);\n+\tstrbuf_setlen(buf, path_sep ? path_sep - buf->buf : offset);\n+\n+\treturn buf->len < len;\n+}\n+\n+static void setup_enlistment_directory(int argc, const char **argv,\n+\t\t\t\t       const char * const *usagestr,\n+\t\t\t\t       const struct option *options,\n+\t\t\t\t       struct strbuf *enlistment_root)\n+{\n+\tstruct strbuf path = STRBUF_INIT;\n+\tchar *root;\n+\tint enlistment_found = 0;\n+\n+\tif (startup_info->have_repository)\n+\t\tBUG(\"gitdir already set up?!?\");\n+\n+\tif (argc > 1)\n+\t\tusage_with_options(usagestr, options);\n+\n+\t/* find the worktree, determine its corresponding root */\n+\tif (argc == 1)\n+\t\tstrbuf_add_absolute_path(&path, argv[0]);\n+\telse if (strbuf_getcwd(&path) < 0)\n+\t\tdie(_(\"need a working directory\"));\n+\n+\tstrbuf_trim_trailing_dir_sep(&path);\n+\tdo {\n+\t\tconst size_t len = path.len;\n+\n+\t\t/* check if currently in enlistment root with src/ workdir */\n+\t\tstrbuf_addstr(&path, \"/src\");\n+\t\tif (is_nonbare_repository_dir(&path)) {\n+\t\t\tif (enlistment_root)\n+\t\t\t\tstrbuf_add(enlistment_root, path.buf, len);\n+\n+\t\t\tenlistment_found = 1;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\t/* reset to original path */\n+\t\tstrbuf_setlen(&path, len);\n+\n+\t\t/* check if currently in workdir */\n+\t\tif (is_nonbare_repository_dir(&path)) {\n+\t\t\tif (enlistment_root) {\n+\t\t\t\t/*\n+\t\t\t\t * If the worktree's directory's name is `src`, the enlistment is the\n+\t\t\t\t * parent directory, otherwise it is identical to the worktree.\n+\t\t\t\t */\n+\t\t\t\troot = strip_path_suffix(path.buf, \"src\");\n+\t\t\t\tstrbuf_addstr(enlistment_root, root ? root : path.buf);\n+\t\t\t\tfree(root);\n+\t\t\t}\n+\n+\t\t\tenlistment_found = 1;\n+\t\t\tbreak;\n+\t\t}\n+\t} while (strbuf_parent_directory(&path));\n+\n+\tif (!enlistment_found)\n+\t\tdie(_(\"could not find enlistment root\"));\n+\n+\tif (chdir(path.buf) < 0)\n+\t\tdie_errno(_(\"could not switch to '%s'\"), path.buf);\n+\n+\tstrbuf_release(&path);\n+\tsetup_git_directory();\n+}\n+\n+static int run_git(const char *arg, ...)\n+{\n+\tstruct strvec argv = STRVEC_INIT;\n+\tva_list args;\n+\tconst char *p;\n+\tint res;\n+\n+\tva_start(args, arg);\n+\tstrvec_push(&argv, arg);\n+\twhile ((p = va_arg(args, const char *)))\n+\t\tstrvec_push(&argv, p);\n+\tva_end(args);\n+\n+\tres = run_command_v_opt(argv.v, RUN_GIT_CMD);\n+\n+\tstrvec_clear(&argv);\n+\treturn res;\n+}\n+\n+static int set_recommended_config(void)\n+{\n+\tstruct {\n+\t\tconst char *key;\n+\t\tconst char *value;\n+\t} config[] = {\n+\t\t{ \"am.keepCR\", \"true\" },\n+\t\t{ \"core.FSCache\", \"true\" },\n+\t\t{ \"core.multiPackIndex\", \"true\" },\n+\t\t{ \"core.preloadIndex\", \"true\" },\n+#ifndef WIN32\n+\t\t{ \"core.untrackedCache\", \"true\" },\n+#else\n+\t\t/*\n+\t\t * Unfortunately, Scalar's Functional Tests demonstrated\n+\t\t * that the untracked cache feature is unreliable on Windows\n+\t\t * (which is a bummer because that platform would benefit the\n+\t\t * most from it). For some reason, freshly created files seem\n+\t\t * not to update the directory's `lastModified` time\n+\t\t * immediately, but the untracked cache would need to rely on\n+\t\t * that.\n+\t\t *\n+\t\t * Therefore, with a sad heart, we disable this very useful\n+\t\t * feature on Windows.\n+\t\t */\n+\t\t{ \"core.untrackedCache\", \"false\" },\n+#endif\n+\t\t{ \"core.logAllRefUpdates\", \"true\" },\n+\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n+\t\t{ \"credential.validate\", \"false\" }, /* GCM4W-only */\n+\t\t{ \"gc.auto\", \"0\" },\n+\t\t{ \"gui.GCWarning\", \"false\" },\n+\t\t{ \"index.threads\", \"true\" },\n+\t\t{ \"index.version\", \"4\" },\n+\t\t{ \"merge.stat\", \"false\" },\n+\t\t{ \"merge.renames\", \"true\" },\n+\t\t{ \"pack.useBitmaps\", \"false\" },\n+\t\t{ \"pack.useSparse\", \"true\" },\n+\t\t{ \"receive.autoGC\", \"false\" },\n+\t\t{ \"reset.quiet\", \"true\" },\n+\t\t{ \"feature.manyFiles\", \"false\" },\n+\t\t{ \"feature.experimental\", \"false\" },\n+\t\t{ \"fetch.unpackLimit\", \"1\" },\n+\t\t{ \"fetch.writeCommitGraph\", \"false\" },\n+#ifdef WIN32\n+\t\t{ \"http.sslBackend\", \"schannel\" },\n+#endif\n+\t\t{ \"status.aheadBehind\", \"false\" },\n+\t\t{ \"commitGraph.generationVersion\", \"1\" },\n+\t\t{ \"core.autoCRLF\", \"false\" },\n+\t\t{ \"core.safeCRLF\", \"false\" },\n+\t\t{ \"fetch.showForcedUpdates\", \"false\" },\n+\t\t{ NULL, NULL },\n+\t};\n+\tint i;\n+\tchar *value;\n+\n+\tfor (i = 0; config[i].key; i++) {\n+\t\tif (git_config_get_string(config[i].key, &value)) {\n+\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"created\");\n+\t\t\tif (git_config_set_gently(config[i].key,\n+\t\t\t\t\t\t  config[i].value) < 0)\n+\t\t\t\treturn error(_(\"could not configure %s=%s\"),\n+\t\t\t\t\t     config[i].key, config[i].value);\n+\t\t} else {\n+\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"exists\");\n+\t\t\tfree(value);\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * The `log.excludeDecoration` setting is special because it allows\n+\t * for multiple values.\n+\t */\n+\tif (git_config_get_string(\"log.excludeDecoration\", &value)) {\n+\t\ttrace2_data_string(\"scalar\", the_repository,\n+\t\t\t\t   \"log.excludeDecoration\", \"created\");\n+\t\tif (git_config_set_multivar_gently(\"log.excludeDecoration\",\n+\t\t\t\t\t\t   \"refs/prefetch/*\",\n+\t\t\t\t\t\t   CONFIG_REGEX_NONE, 0))\n+\t\t\treturn error(_(\"could not configure \"\n+\t\t\t\t       \"log.excludeDecoration\"));\n+\t} else {\n+\t\ttrace2_data_string(\"scalar\", the_repository,\n+\t\t\t\t   \"log.excludeDecoration\", \"exists\");\n+\t\tfree(value);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int start_maintenance(void)\n+{\n+\treturn run_git(\"maintenance\", \"start\", NULL);\n+}\n+\n+static int add_enlistment(void)\n+{\n+\tint res;\n+\n+\tif (!the_repository->worktree)\n+\t\tdie(_(\"Scalar enlistments require a worktree\"));\n+\n+\tres = run_git(\"config\", \"--global\", \"--get\", \"--fixed-value\",\n+\t\t      \"scalar.repo\", the_repository->worktree, NULL);\n+\n+\t/*\n+\t * If the setting is already there, then do nothing.\n+\t */\n+\tif (!res)\n+\t\treturn 0;\n+\n+\treturn run_git(\"config\", \"--global\", \"--add\",\n+\t\t       \"scalar.repo\", the_repository->worktree, NULL);\n+}\n+\n+static int register_dir(void)\n+{\n+\tint res = add_enlistment();\n+\n+\tif (!res)\n+\t\tres = set_recommended_config();\n+\n+\tif (!res)\n+\t\tres = start_maintenance();\n+\n+\treturn res;\n+}\n+\n+static int cmd_register(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar register [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn register_dir();\n+}\n \n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"register\", cmd_register },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 5f7131861a5..568987064b2 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,7 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n-scalar <command> [<options>]\n+scalar register [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -29,6 +29,22 @@ will be identical to the worktree.\n The `scalar` command implements various subcommands, and different options\n depending on the subcommand.\n \n+COMMANDS\n+--------\n+\n+Register\n+~~~~~~~~\n+\n+register [<enlistment>]::\n+\tAdds the enlistment's repository to the list of registered repositories\n+\tand starts background maintenance. If `<enlistment>` is not provided,\n+\tthen the enlistment associated with the current working directory is\n+\tregistered.\n++\n+Note: when this subcommand is called in a worktree that is called `src/`, its\n+parent directory is considered to be the Scalar enlistment. If the worktree is\n+_not_ called `src/`, it itself will be considered to be the Scalar enlistment.\n+\n SEE ALSO\n --------\n linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"442636","messageId":"09a15f86c3d1a79d76c73e051c6895496ea0e8a7.1638273289.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v9.git.1638273289.gitgitgadget@gmail.com","subject":"[PATCH v9 08/17] scalar: 'unregister' stops background maintenance","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-30T11:54:40Z","receivedAt":"2021-11-30T11:55:27Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nJust like `scalar register` starts the scheduled background maintenance,\n`scalar unregister` stops it. Note that we use `git maintenance start`\nin `scalar register`, but we do not use `git maintenance stop` in\n`scalar unregister`: this would stop maintenance for _all_ repositories,\nnot just for the one we want to unregister.\n\nThe `unregister` command also removes the corresponding entry from the\n`[scalar]` section in the global Git config.\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 50 ++++++++++++++++++++++++++++++++-------\n contrib/scalar/scalar.txt |  8 +++++++\n 2 files changed, 50 insertions(+), 8 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 55a304442d4..9ab9dffe3ac 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -198,12 +198,12 @@ static int set_recommended_config(void)\n \treturn 0;\n }\n \n-static int start_maintenance(void)\n+static int toggle_maintenance(int enable)\n {\n-\treturn run_git(\"maintenance\", \"start\", NULL);\n+\treturn run_git(\"maintenance\", enable ? \"start\" : \"unregister\", NULL);\n }\n \n-static int add_enlistment(void)\n+static int add_or_remove_enlistment(int add)\n {\n \tint res;\n \n@@ -214,24 +214,39 @@ static int add_enlistment(void)\n \t\t      \"scalar.repo\", the_repository->worktree, NULL);\n \n \t/*\n-\t * If the setting is already there, then do nothing.\n+\t * If we want to add and the setting is already there, then do nothing.\n+\t * If we want to remove and the setting is not there, then do nothing.\n \t */\n-\tif (!res)\n+\tif ((add && !res) || (!add && res))\n \t\treturn 0;\n \n-\treturn run_git(\"config\", \"--global\", \"--add\",\n+\treturn run_git(\"config\", \"--global\", add ? \"--add\" : \"--unset\",\n+\t\t       add ? \"--no-fixed-value\" : \"--fixed-value\",\n \t\t       \"scalar.repo\", the_repository->worktree, NULL);\n }\n \n static int register_dir(void)\n {\n-\tint res = add_enlistment();\n+\tint res = add_or_remove_enlistment(1);\n \n \tif (!res)\n \t\tres = set_recommended_config();\n \n \tif (!res)\n-\t\tres = start_maintenance();\n+\t\tres = toggle_maintenance(1);\n+\n+\treturn res;\n+}\n+\n+static int unregister_dir(void)\n+{\n+\tint res = 0;\n+\n+\tif (toggle_maintenance(0) < 0)\n+\t\tres = -1;\n+\n+\tif (add_or_remove_enlistment(0) < 0)\n+\t\tres = -1;\n \n \treturn res;\n }\n@@ -254,11 +269,30 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_unregister(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar unregister [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn unregister_dir();\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n \t{ \"register\", cmd_register },\n+\t{ \"unregister\", cmd_unregister },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 568987064b2..d9a79984492 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -9,6 +9,7 @@ SYNOPSIS\n --------\n [verse]\n scalar register [<enlistment>]\n+scalar unregister [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -45,6 +46,13 @@ Note: when this subcommand is called in a worktree that is called `src/`, its\n parent directory is considered to be the Scalar enlistment. If the worktree is\n _not_ called `src/`, it itself will be considered to be the Scalar enlistment.\n \n+Unregister\n+~~~~~~~~~~\n+\n+unregister [<enlistment>]::\n+\tRemove the specified repository from the list of repositories\n+\tregistered with Scalar and stop the scheduled background maintenance.\n+\n SEE ALSO\n --------\n linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"442637","messageId":"abd9c8827cd5cd756a4d0ceab0c968f19b1b59ad.1638273289.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v9.git.1638273289.gitgitgadget@gmail.com","subject":"[PATCH v9 12/17] scalar: teach 'clone' to support the --single-branch option","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-30T11:54:44Z","receivedAt":"2021-11-30T11:55:30Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nJust like `git clone`, the `scalar clone` command now also offers to\nrestrict the clone to a single branch.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          |  9 +++++++--\n contrib/scalar/scalar.txt        | 12 +++++++++++-\n contrib/scalar/t/t9099-scalar.sh |  6 +++++-\n 3 files changed, 23 insertions(+), 4 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 60a9466421b..61b66e48aa8 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -327,12 +327,15 @@ static char *remote_default_branch(const char *url)\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n-\tint full_clone = 0;\n+\tint full_clone = 0, single_branch = 0;\n \tstruct option clone_options[] = {\n \t\tOPT_STRING('b', \"branch\", &branch, N_(\"<branch>\"),\n \t\t\t   N_(\"branch to checkout after clone\")),\n \t\tOPT_BOOL(0, \"full-clone\", &full_clone,\n \t\t\t N_(\"when cloning, create full working directory\")),\n+\t\tOPT_BOOL(0, \"single-branch\", &single_branch,\n+\t\t\t N_(\"only download metadata for the branch that will \"\n+\t\t\t    \"be checked out\")),\n \t\tOPT_END(),\n \t};\n \tconst char * const clone_usage[] = {\n@@ -403,7 +406,9 @@ static int cmd_clone(int argc, const char **argv)\n \n \tif (set_config(\"remote.origin.url=%s\", url) ||\n \t    set_config(\"remote.origin.fetch=\"\n-\t\t       \"+refs/heads/*:refs/remotes/origin/*\") ||\n+\t\t       \"+refs/heads/%s:refs/remotes/origin/%s\",\n+\t\t       single_branch ? branch : \"*\",\n+\t\t       single_branch ? branch : \"*\") ||\n \t    set_config(\"remote.origin.promisor=true\") ||\n \t    set_config(\"remote.origin.partialCloneFilter=blob:none\")) {\n \t\tres = error(_(\"could not configure remote in '%s'\"), dir);\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex e8730967f16..56f744a4aa9 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,7 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n-scalar clone [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n+scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n@@ -57,6 +57,16 @@ HEAD[:<directory>]`.\n \tInstead of checking out the branch pointed to by the cloned\n \trepository's HEAD, check out the `<name>` branch instead.\n \n+--[no-]single-branch::\n+\tClone only the history leading to the tip of a single branch, either\n+\tspecified by the `--branch` option or the primary branch remote's\n+\t`HEAD` points at.\n++\n+Further fetches into the resulting repository will only update the\n+remote-tracking branch for the branch this option was used for the initial\n+cloning. If the HEAD at the remote did not point at any branch when\n+`--single-branch` clone was made, no remote-tracking branch is created.\n+\n --[no-]full-clone::\n \tA sparse-checkout is initialized by default. This behavior can be\n \tturned off via `--full-clone`.\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 984d69e8f75..f60e086d6f9 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -45,13 +45,17 @@ test_expect_success 'set up repository to clone' '\n \n test_expect_success 'scalar clone' '\n \tsecond=$(git rev-parse --verify second:second.t) &&\n-\tscalar clone \"file://$(pwd)\" cloned &&\n+\tscalar clone \"file://$(pwd)\" cloned --single-branch &&\n \t(\n \t\tcd cloned/src &&\n \n \t\tgit config --get --global --fixed-value maintenance.repo \\\n \t\t\t\"$(pwd)\" &&\n \n+\t\tgit for-each-ref --format=\"%(refname)\" refs/remotes/origin/ >actual &&\n+\t\techo \"refs/remotes/origin/parallel\" >expect &&\n+\t\ttest_cmp expect actual &&\n+\n \t\ttest_path_is_missing 1/2 &&\n \t\ttest_must_fail git rev-list --missing=print $second &&\n \t\tgit rev-list $second &&\n-- \ngitgitgadget\n\n"},{"id":"442638","messageId":"dd4e3a4b761e6957ae33d734e09db60fb124fcba.1638273289.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v9.git.1638273289.gitgitgadget@gmail.com","subject":"[PATCH v9 11/17] scalar: implement the `clone` subcommand","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-30T11:54:43Z","receivedAt":"2021-11-30T11:55:39Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis implements Scalar's opinionated `clone` command: it tries to use a\npartial clone and sets up a sparse checkout by default. In contrast to\n`git clone`, `scalar clone` sets up the worktree in the `src/`\nsubdirectory, to encourage a separation between the source files and the\nbuild output (which helps Git tremendously because it avoids untracked\nfiles that have to be specifically ignored when refreshing the index).\n\nAlso, it registers the repository for regular, scheduled maintenance,\nand configures a flurry of configuration settings based on the\nexperience and experiments of the Microsoft Windows and the Microsoft\nOffice development teams.\n\nNote: since the `scalar clone` command is by far the most commonly\ncalled `scalar` subcommand, we document it at the top of the manual\npage.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 201 +++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        |  32 ++++-\n contrib/scalar/t/t9099-scalar.sh |  32 +++++\n 3 files changed, 262 insertions(+), 3 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 65da885c5ac..60a9466421b 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -7,6 +7,7 @@\n #include \"parse-options.h\"\n #include \"config.h\"\n #include \"run-command.h\"\n+#include \"refs.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -251,6 +252,205 @@ static int unregister_dir(void)\n \treturn res;\n }\n \n+/* printf-style interface, expects `<key>=<value>` argument */\n+static int set_config(const char *fmt, ...)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tchar *value;\n+\tint res;\n+\tva_list args;\n+\n+\tva_start(args, fmt);\n+\tstrbuf_vaddf(&buf, fmt, args);\n+\tva_end(args);\n+\n+\tvalue = strchr(buf.buf, '=');\n+\tif (value)\n+\t\t*(value++) = '\\0';\n+\tres = git_config_set_gently(buf.buf, value);\n+\tstrbuf_release(&buf);\n+\n+\treturn res;\n+}\n+\n+static char *remote_default_branch(const char *url)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tstruct strbuf out = STRBUF_INIT;\n+\n+\tcp.git_cmd = 1;\n+\tstrvec_pushl(&cp.args, \"ls-remote\", \"--symref\", url, \"HEAD\", NULL);\n+\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n+\t\tconst char *line = out.buf;\n+\n+\t\twhile (*line) {\n+\t\t\tconst char *eol = strchrnul(line, '\\n'), *p;\n+\t\t\tsize_t len = eol - line;\n+\t\t\tchar *branch;\n+\n+\t\t\tif (!skip_prefix(line, \"ref: \", &p) ||\n+\t\t\t    !strip_suffix_mem(line, &len, \"\\tHEAD\")) {\n+\t\t\t\tline = eol + (*eol == '\\n');\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\teol = line + len;\n+\t\t\tif (skip_prefix(p, \"refs/heads/\", &p)) {\n+\t\t\t\tbranch = xstrndup(p, eol - p);\n+\t\t\t\tstrbuf_release(&out);\n+\t\t\t\treturn branch;\n+\t\t\t}\n+\n+\t\t\terror(_(\"remote HEAD is not a branch: '%.*s'\"),\n+\t\t\t      (int)(eol - p), p);\n+\t\t\tstrbuf_release(&out);\n+\t\t\treturn NULL;\n+\t\t}\n+\t}\n+\twarning(_(\"failed to get default branch name from remote; \"\n+\t\t  \"using local default\"));\n+\tstrbuf_reset(&out);\n+\n+\tchild_process_init(&cp);\n+\tcp.git_cmd = 1;\n+\tstrvec_pushl(&cp.args, \"symbolic-ref\", \"--short\", \"HEAD\", NULL);\n+\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n+\t\tstrbuf_trim(&out);\n+\t\treturn strbuf_detach(&out, NULL);\n+\t}\n+\n+\tstrbuf_release(&out);\n+\terror(_(\"failed to get default branch name\"));\n+\treturn NULL;\n+}\n+\n+static int cmd_clone(int argc, const char **argv)\n+{\n+\tconst char *branch = NULL;\n+\tint full_clone = 0;\n+\tstruct option clone_options[] = {\n+\t\tOPT_STRING('b', \"branch\", &branch, N_(\"<branch>\"),\n+\t\t\t   N_(\"branch to checkout after clone\")),\n+\t\tOPT_BOOL(0, \"full-clone\", &full_clone,\n+\t\t\t N_(\"when cloning, create full working directory\")),\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const clone_usage[] = {\n+\t\tN_(\"scalar clone [<options>] [--] <repo> [<dir>]\"),\n+\t\tNULL\n+\t};\n+\tconst char *url;\n+\tchar *enlistment = NULL, *dir = NULL;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint res;\n+\n+\targc = parse_options(argc, argv, NULL, clone_options, clone_usage, 0);\n+\n+\tif (argc == 2) {\n+\t\turl = argv[0];\n+\t\tenlistment = xstrdup(argv[1]);\n+\t} else if (argc == 1) {\n+\t\turl = argv[0];\n+\n+\t\tstrbuf_addstr(&buf, url);\n+\t\t/* Strip trailing slashes, if any */\n+\t\twhile (buf.len > 0 && is_dir_sep(buf.buf[buf.len - 1]))\n+\t\t\tstrbuf_setlen(&buf, buf.len - 1);\n+\t\t/* Strip suffix `.git`, if any */\n+\t\tstrbuf_strip_suffix(&buf, \".git\");\n+\n+\t\tenlistment = find_last_dir_sep(buf.buf);\n+\t\tif (!enlistment) {\n+\t\t\tdie(_(\"cannot deduce worktree name from '%s'\"), url);\n+\t\t}\n+\t\tenlistment = xstrdup(enlistment + 1);\n+\t} else {\n+\t\tusage_msg_opt(_(\"You must specify a repository to clone.\"),\n+\t\t\t      clone_usage, clone_options);\n+\t}\n+\n+\tif (is_directory(enlistment))\n+\t\tdie(_(\"directory '%s' exists already\"), enlistment);\n+\n+\tdir = xstrfmt(\"%s/src\", enlistment);\n+\n+\tstrbuf_reset(&buf);\n+\tif (branch)\n+\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", branch);\n+\telse {\n+\t\tchar *b = repo_default_branch_name(the_repository, 1);\n+\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", b);\n+\t\tfree(b);\n+\t}\n+\n+\tif ((res = run_git(\"-c\", buf.buf, \"init\", \"--\", dir, NULL)))\n+\t\tgoto cleanup;\n+\n+\tif (chdir(dir) < 0) {\n+\t\tres = error_errno(_(\"could not switch to '%s'\"), dir);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tsetup_git_directory();\n+\n+\t/* common-main already logs `argv` */\n+\ttrace2_def_repo(the_repository);\n+\n+\tif (!branch && !(branch = remote_default_branch(url))) {\n+\t\tres = error(_(\"failed to get default branch for '%s'\"), url);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tif (set_config(\"remote.origin.url=%s\", url) ||\n+\t    set_config(\"remote.origin.fetch=\"\n+\t\t       \"+refs/heads/*:refs/remotes/origin/*\") ||\n+\t    set_config(\"remote.origin.promisor=true\") ||\n+\t    set_config(\"remote.origin.partialCloneFilter=blob:none\")) {\n+\t\tres = error(_(\"could not configure remote in '%s'\"), dir);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tif (!full_clone &&\n+\t    (res = run_git(\"sparse-checkout\", \"init\", \"--cone\", NULL)))\n+\t\tgoto cleanup;\n+\n+\tif (set_recommended_config())\n+\t\treturn error(_(\"could not configure '%s'\"), dir);\n+\n+\tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL))) {\n+\t\twarning(_(\"partial clone failed; attempting full clone\"));\n+\n+\t\tif (set_config(\"remote.origin.promisor\") ||\n+\t\t    set_config(\"remote.origin.partialCloneFilter\")) {\n+\t\t\tres = error(_(\"could not configure for full clone\"));\n+\t\t\tgoto cleanup;\n+\t\t}\n+\n+\t\tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL)))\n+\t\t\tgoto cleanup;\n+\t}\n+\n+\tif ((res = set_config(\"branch.%s.remote=origin\", branch)))\n+\t\tgoto cleanup;\n+\tif ((res = set_config(\"branch.%s.merge=refs/heads/%s\",\n+\t\t\t      branch, branch)))\n+\t\tgoto cleanup;\n+\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addf(&buf, \"origin/%s\", branch);\n+\tres = run_git(\"checkout\", \"-f\", \"-t\", buf.buf, NULL);\n+\tif (res)\n+\t\tgoto cleanup;\n+\n+\tres = register_dir();\n+\n+cleanup:\n+\tfree(enlistment);\n+\tfree(dir);\n+\tstrbuf_release(&buf);\n+\treturn res;\n+}\n+\n static int cmd_list(int argc, const char **argv)\n {\n \tif (argc != 1)\n@@ -347,6 +547,7 @@ static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"clone\", cmd_clone },\n \t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex f93e3d00efd..e8730967f16 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,6 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n+scalar clone [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n@@ -29,12 +30,37 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand. With the exception of `list`, all subcommands\n-expect to be run in an enlistment.\n+depending on the subcommand. With the exception of `clone` and `list`, all\n+subcommands expect to be run in an enlistment.\n \n COMMANDS\n --------\n \n+Clone\n+~~~~~\n+\n+clone [<options>] <url> [<enlistment>]::\n+\tClones the specified repository, similar to linkgit:git-clone[1]. By\n+\tdefault, only commit and tree objects are cloned. Once finished, the\n+\tworktree is located at `<enlistment>/src`.\n++\n+The sparse-checkout feature is enabled (except when run with `--full-clone`)\n+and the only files present are those in the top-level directory. Use\n+`git sparse-checkout set` to expand the set of directories you want to see,\n+or `git sparse-checkout disable` to expand to all files (see\n+linkgit:git-sparse-checkout[1] for more details). You can explore the\n+subdirectories outside your sparse-checkout by using `git ls-tree\n+HEAD[:<directory>]`.\n+\n+-b <name>::\n+--branch <name>::\n+\tInstead of checking out the branch pointed to by the cloned\n+\trepository's HEAD, check out the `<name>` branch instead.\n+\n+--[no-]full-clone::\n+\tA sparse-checkout is initialized by default. This behavior can be\n+\tturned off via `--full-clone`.\n+\n List\n ~~~~\n \n@@ -64,7 +90,7 @@ unregister [<enlistment>]::\n \n SEE ALSO\n --------\n-linkgit:git-maintenance[1].\n+linkgit:git-clone[1], linkgit:git-maintenance[1].\n \n Scalar\n ---\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex ef0e8d680d5..984d69e8f75 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -10,6 +10,9 @@ PATH=$PWD/..:$PATH\n \n . ../../../t/test-lib.sh\n \n+GIT_TEST_MAINT_SCHEDULER=\"crontab:test-tool crontab ../cron.txt,launchctl:true,schtasks:true\"\n+export GIT_TEST_MAINT_SCHEDULER\n+\n test_expect_success 'scalar shows a usage' '\n \ttest_expect_code 129 scalar -h\n '\n@@ -29,4 +32,33 @@ test_expect_success 'scalar unregister' '\n \t! grep -F \"$(pwd)/vanish/src\" scalar.repos\n '\n \n+test_expect_success 'set up repository to clone' '\n+\ttest_commit first &&\n+\ttest_commit second &&\n+\ttest_commit third &&\n+\tgit switch -c parallel first &&\n+\tmkdir -p 1/2 &&\n+\ttest_commit 1/2/3 &&\n+\tgit config uploadPack.allowFilter true &&\n+\tgit config uploadPack.allowAnySHA1InWant true\n+'\n+\n+test_expect_success 'scalar clone' '\n+\tsecond=$(git rev-parse --verify second:second.t) &&\n+\tscalar clone \"file://$(pwd)\" cloned &&\n+\t(\n+\t\tcd cloned/src &&\n+\n+\t\tgit config --get --global --fixed-value maintenance.repo \\\n+\t\t\t\"$(pwd)\" &&\n+\n+\t\ttest_path_is_missing 1/2 &&\n+\t\ttest_must_fail git rev-list --missing=print $second &&\n+\t\tgit rev-list $second &&\n+\t\tgit cat-file blob $second >actual &&\n+\t\techo \"second\" >expect &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"442639","messageId":"6afb2eb4163ab92d8e107674f7766281b05fbe24.1638273289.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v9.git.1638273289.gitgitgadget@gmail.com","subject":"[PATCH v9 10/17] scalar: implement 'scalar list'","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-30T11:54:42Z","receivedAt":"2021-11-30T11:55:41Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe produced list simply consists of those repositories registered under\nthe multi-valued `scalar.repo` config setting in the user's Git config.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 11 +++++++++++\n contrib/scalar/scalar.txt | 11 ++++++++++-\n 2 files changed, 21 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex ec783e72ef3..65da885c5ac 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -251,6 +251,16 @@ static int unregister_dir(void)\n \treturn res;\n }\n \n+static int cmd_list(int argc, const char **argv)\n+{\n+\tif (argc != 1)\n+\t\tdie(_(\"`scalar list` does not take arguments\"));\n+\n+\tif (run_git(\"config\", \"--global\", \"--get-all\", \"scalar.repo\", NULL) < 0)\n+\t\treturn -1;\n+\treturn 0;\n+}\n+\n static int cmd_register(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -337,6 +347,7 @@ static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n \t{ NULL, NULL},\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex d9a79984492..f93e3d00efd 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,6 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n+scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n \n@@ -28,11 +29,19 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand.\n+depending on the subcommand. With the exception of `list`, all subcommands\n+expect to be run in an enlistment.\n \n COMMANDS\n --------\n \n+List\n+~~~~\n+\n+list::\n+\tList enlistments that are currently registered by Scalar. This\n+\tsubcommand does not need to be run inside an enlistment.\n+\n Register\n ~~~~~~~~\n \n-- \ngitgitgadget\n\n"},{"id":"442640","messageId":"0cec6dbd2cbf35e2978bf98ca8bbc2f812621e62.1638273289.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v9.git.1638273289.gitgitgadget@gmail.com","subject":"[PATCH v9 15/17] scalar: teach 'reconfigure' to optionally handle all registered enlistments","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-30T11:54:47Z","receivedAt":"2021-11-30T11:55:45Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nAfter a Scalar upgrade, it can come in really handy if there is an easy\nway to reconfigure all Scalar enlistments. This new option offers this\nfunctionality.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 61 ++++++++++++++++++++++++++++++--\n contrib/scalar/scalar.txt        |  9 +++--\n contrib/scalar/t/t9099-scalar.sh |  3 ++\n 3 files changed, 67 insertions(+), 6 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex d7306b43cae..305b080663b 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -488,22 +488,77 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int get_scalar_repos(const char *key, const char *value, void *data)\n+{\n+\tstruct string_list *list = data;\n+\n+\tif (!strcmp(key, \"scalar.repo\"))\n+\t\tstring_list_append(list, value);\n+\n+\treturn 0;\n+}\n+\n static int cmd_reconfigure(int argc, const char **argv)\n {\n+\tint all = 0;\n \tstruct option options[] = {\n+\t\tOPT_BOOL('a', \"all\", &all,\n+\t\t\t N_(\"reconfigure all registered enlistments\")),\n \t\tOPT_END(),\n \t};\n \tconst char * const usage[] = {\n-\t\tN_(\"scalar reconfigure [<enlistment>]\"),\n+\t\tN_(\"scalar reconfigure [--all | <enlistment>]\"),\n \t\tNULL\n \t};\n+\tstruct string_list scalar_repos = STRING_LIST_INIT_DUP;\n+\tint i, res = 0;\n+\tstruct repository r = { NULL };\n+\tstruct strbuf commondir = STRBUF_INIT, gitdir = STRBUF_INIT;\n \n \targc = parse_options(argc, argv, NULL, options,\n \t\t\t     usage, 0);\n \n-\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\tif (!all) {\n+\t\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\t\treturn set_recommended_config(1);\n+\t}\n+\n+\tif (argc > 0)\n+\t\tusage_msg_opt(_(\"--all or <enlistment>, but not both\"),\n+\t\t\t      usage, options);\n+\n+\tgit_config(get_scalar_repos, &scalar_repos);\n \n-\treturn set_recommended_config(1);\n+\tfor (i = 0; i < scalar_repos.nr; i++) {\n+\t\tconst char *dir = scalar_repos.items[i].string;\n+\n+\t\tstrbuf_reset(&commondir);\n+\t\tstrbuf_reset(&gitdir);\n+\n+\t\tif (chdir(dir) < 0) {\n+\t\t\twarning_errno(_(\"could not switch to '%s'\"), dir);\n+\t\t\tres = -1;\n+\t\t} else if (discover_git_directory(&commondir, &gitdir) < 0) {\n+\t\t\twarning_errno(_(\"git repository gone in '%s'\"), dir);\n+\t\t\tres = -1;\n+\t\t} else {\n+\t\t\tgit_config_clear();\n+\n+\t\t\tthe_repository = &r;\n+\t\t\tr.commondir = commondir.buf;\n+\t\t\tr.gitdir = gitdir.buf;\n+\n+\t\t\tif (set_recommended_config(1) < 0)\n+\t\t\t\tres = -1;\n+\t\t}\n+\t}\n+\n+\tstring_list_clear(&scalar_repos, 1);\n+\tstrbuf_release(&commondir);\n+\tstrbuf_release(&gitdir);\n+\n+\treturn res;\n }\n \n static int cmd_run(int argc, const char **argv)\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 89fd7901585..737cf563c1a 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -13,7 +13,7 @@ scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n-scalar reconfigure <enlistment>\n+scalar reconfigure [ --all | <enlistment> ]\n \n DESCRIPTION\n -----------\n@@ -32,8 +32,8 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand. With the exception of `clone` and `list`, all\n-subcommands expect to be run in an enlistment.\n+depending on the subcommand. With the exception of `clone`, `list` and\n+`reconfigure --all`, all subcommands expect to be run in an enlistment.\n \n COMMANDS\n --------\n@@ -125,6 +125,9 @@ After a Scalar upgrade, or when the configuration of a Scalar enlistment\n was somehow corrupted or changed by mistake, this subcommand allows to\n reconfigure the enlistment.\n \n+With the `--all` option, all enlistments currently registered with Scalar\n+will be reconfigured. Use this option after each Scalar upgrade.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex fb5e2efee0a..58af546fd84 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -70,6 +70,9 @@ test_expect_success 'scalar reconfigure' '\n \tscalar register one &&\n \tgit -C one/src config core.preloadIndex false &&\n \tscalar reconfigure one &&\n+\ttest true = \"$(git -C one/src config core.preloadIndex)\" &&\n+\tgit -C one/src config core.preloadIndex false &&\n+\tscalar reconfigure -a &&\n \ttest true = \"$(git -C one/src config core.preloadIndex)\"\n '\n \n-- \ngitgitgadget\n\n"},{"id":"442641","messageId":"835f1c797922d8c9020460b9383a8f909878990a.1638273289.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v9.git.1638273289.gitgitgadget@gmail.com","subject":"[PATCH v9 16/17] scalar: implement the `delete` command","fromName":"Matthew John Cheetham via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-30T11:54:48Z","receivedAt":"2021-11-30T11:55:48Z","isPatch":true,"sender":{"key":"mjcheetham@outlook.com","avatar":"https://avatars.githubusercontent.com/u/5658207?v=4"},"body":"From: Matthew John Cheetham <mjcheetham@outlook.com>\n\nDelete an enlistment by first unregistering the repository and then\ndeleting the enlistment directory (usually the directory containing the\nworktree `src/` directory).\n\nOn Windows, if the current directory is inside the enlistment's\ndirectory, change to the parent of the enlistment directory, to allow us\nto delete the enlistment (directories used by processes e.g. as current\nworking directories cannot be deleted on Windows).\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Matthew John Cheetham <mjcheetham@outlook.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 63 ++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        |  8 ++++\n contrib/scalar/t/t9099-scalar.sh |  9 +++++\n 3 files changed, 80 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 305b080663b..d4303c7c4a2 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -8,6 +8,8 @@\n #include \"config.h\"\n #include \"run-command.h\"\n #include \"refs.h\"\n+#include \"dir.h\"\n+#include \"packfile.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -328,6 +330,33 @@ static char *remote_default_branch(const char *url)\n \treturn NULL;\n }\n \n+static int delete_enlistment(struct strbuf *enlistment)\n+{\n+#ifdef WIN32\n+\tstruct strbuf parent = STRBUF_INIT;\n+#endif\n+\n+\tif (unregister_dir())\n+\t\tdie(_(\"failed to unregister repository\"));\n+\n+#ifdef WIN32\n+\t/*\n+\t * Change the current directory to one outside of the enlistment so\n+\t * that we may delete everything underneath it.\n+\t */\n+\tstrbuf_addbuf(&parent, enlistment);\n+\tstrbuf_parent_directory(&parent);\n+\tif (chdir(parent.buf) < 0)\n+\t\tdie_errno(_(\"could not switch to '%s'\"), parent.buf);\n+\tstrbuf_release(&parent);\n+#endif\n+\n+\tif (remove_dir_recursively(enlistment, 0))\n+\t\tdie(_(\"failed to delete enlistment directory\"));\n+\n+\treturn 0;\n+}\n+\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n@@ -688,6 +717,39 @@ static int cmd_unregister(int argc, const char **argv)\n \treturn unregister_dir();\n }\n \n+static int cmd_delete(int argc, const char **argv)\n+{\n+\tchar *cwd = xgetcwd();\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar delete <enlistment>\"),\n+\t\tNULL\n+\t};\n+\tstruct strbuf enlistment = STRBUF_INIT;\n+\tint res = 0;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tif (argc != 1)\n+\t\tusage_with_options(usage, options);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, &enlistment);\n+\n+\tif (dir_inside_of(cwd, enlistment.buf) >= 0)\n+\t\tres = error(_(\"refusing to delete current working directory\"));\n+\telse {\n+\t\tclose_object_store(the_repository->objects);\n+\t\tres = delete_enlistment(&enlistment);\n+\t}\n+\tstrbuf_release(&enlistment);\n+\tfree(cwd);\n+\n+\treturn res;\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n@@ -698,6 +760,7 @@ static struct {\n \t{ \"unregister\", cmd_unregister },\n \t{ \"run\", cmd_run },\n \t{ \"reconfigure\", cmd_reconfigure },\n+\t{ \"delete\", cmd_delete },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 737cf563c1a..f416d637289 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -14,6 +14,7 @@ scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n scalar reconfigure [ --all | <enlistment> ]\n+scalar delete <enlistment>\n \n DESCRIPTION\n -----------\n@@ -128,6 +129,13 @@ reconfigure the enlistment.\n With the `--all` option, all enlistments currently registered with Scalar\n will be reconfigured. Use this option after each Scalar upgrade.\n \n+Delete\n+~~~~~~\n+\n+delete <enlistment>::\n+\tThis subcommand lets you delete an existing Scalar enlistment from your\n+\tlocal file system, unregistering the repository.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 58af546fd84..2e1502ad45e 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -76,4 +76,13 @@ test_expect_success 'scalar reconfigure' '\n \ttest true = \"$(git -C one/src config core.preloadIndex)\"\n '\n \n+test_expect_success 'scalar delete without enlistment shows a usage' '\n+\ttest_expect_code 129 scalar delete\n+'\n+\n+test_expect_success 'scalar delete with enlistment' '\n+\tscalar delete cloned &&\n+\ttest_path_is_missing cloned\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"442642","messageId":"4ee1b701c7bf36004c352da8f04525f7ef4d8e0f.1638273289.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v9.git.1638273289.gitgitgadget@gmail.com","subject":"[PATCH v9 17/17] scalar: implement the `version` command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-30T11:54:49Z","receivedAt":"2021-11-30T11:55:51Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe .NET version of Scalar has a `version` command. This was necessary\nbecause it was versioned independently of Git.\n\nSince Scalar is now tightly coupled with Git, it does not make sense for\nthem to show different versions. Therefore, it shows the same output as\n`git version`. For backwards-compatibility with the .NET version,\n`scalar version` prints to `stderr`, though (`git version` prints to\n`stdout` instead).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c | 39 +++++++++++++++++++++++++++++++++++++++\n 1 file changed, 39 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex d4303c7c4a2..1ce9c2b00e8 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -10,6 +10,7 @@\n #include \"refs.h\"\n #include \"dir.h\"\n #include \"packfile.h\"\n+#include \"help.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -357,6 +358,15 @@ static int delete_enlistment(struct strbuf *enlistment)\n \treturn 0;\n }\n \n+/*\n+ * Dummy implementation; Using `get_version_info()` would cause a link error\n+ * without this.\n+ */\n+void load_builtin_commands(const char *prefix, struct cmdnames *cmds)\n+{\n+\tdie(\"not implemented\");\n+}\n+\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n@@ -750,6 +760,34 @@ static int cmd_delete(int argc, const char **argv)\n \treturn res;\n }\n \n+static int cmd_version(int argc, const char **argv)\n+{\n+\tint verbose = 0, build_options = 0;\n+\tstruct option options[] = {\n+\t\tOPT__VERBOSE(&verbose, N_(\"include Git version\")),\n+\t\tOPT_BOOL(0, \"build-options\", &build_options,\n+\t\t\t N_(\"include Git's build options\")),\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar verbose [-v | --verbose] [--build-options]\"),\n+\t\tNULL\n+\t};\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tif (argc != 0)\n+\t\tusage_with_options(usage, options);\n+\n+\tget_version_info(&buf, build_options);\n+\tfprintf(stderr, \"%s\\n\", buf.buf);\n+\tstrbuf_release(&buf);\n+\n+\treturn 0;\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n@@ -761,6 +799,7 @@ static struct {\n \t{ \"run\", cmd_run },\n \t{ \"reconfigure\", cmd_reconfigure },\n \t{ \"delete\", cmd_delete },\n+\t{ \"version\", cmd_version },\n \t{ NULL, NULL},\n };\n \n-- \ngitgitgadget\n"},{"id":"442643","messageId":"08e4f548aa83bd636bffb51e19f58a7cc0f9967d.1638273289.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v9.git.1638273289.gitgitgadget@gmail.com","subject":"[PATCH v9 14/17] scalar: allow reconfiguring an existing enlistment","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-30T11:54:46Z","receivedAt":"2021-11-30T11:55:52Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis comes in handy during Scalar upgrades, or when config settings were\nmessed up by mistake.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 79 +++++++++++++++++++++-----------\n contrib/scalar/scalar.txt        |  8 ++++\n contrib/scalar/t/t9099-scalar.sh |  8 ++++\n 3 files changed, 67 insertions(+), 28 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex fa900e4373f..d7306b43cae 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -108,18 +108,20 @@ static int run_git(const char *arg, ...)\n \treturn res;\n }\n \n-static int set_recommended_config(void)\n+static int set_recommended_config(int reconfigure)\n {\n \tstruct {\n \t\tconst char *key;\n \t\tconst char *value;\n+\t\tint overwrite_on_reconfigure;\n \t} config[] = {\n-\t\t{ \"am.keepCR\", \"true\" },\n-\t\t{ \"core.FSCache\", \"true\" },\n-\t\t{ \"core.multiPackIndex\", \"true\" },\n-\t\t{ \"core.preloadIndex\", \"true\" },\n+\t\t/* Required */\n+\t\t{ \"am.keepCR\", \"true\", 1 },\n+\t\t{ \"core.FSCache\", \"true\", 1 },\n+\t\t{ \"core.multiPackIndex\", \"true\", 1 },\n+\t\t{ \"core.preloadIndex\", \"true\", 1 },\n #ifndef WIN32\n-\t\t{ \"core.untrackedCache\", \"true\" },\n+\t\t{ \"core.untrackedCache\", \"true\", 1 },\n #else\n \t\t/*\n \t\t * Unfortunately, Scalar's Functional Tests demonstrated\n@@ -133,28 +135,29 @@ static int set_recommended_config(void)\n \t\t * Therefore, with a sad heart, we disable this very useful\n \t\t * feature on Windows.\n \t\t */\n-\t\t{ \"core.untrackedCache\", \"false\" },\n+\t\t{ \"core.untrackedCache\", \"false\", 1 },\n #endif\n-\t\t{ \"core.logAllRefUpdates\", \"true\" },\n-\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n-\t\t{ \"credential.validate\", \"false\" }, /* GCM4W-only */\n-\t\t{ \"gc.auto\", \"0\" },\n-\t\t{ \"gui.GCWarning\", \"false\" },\n-\t\t{ \"index.threads\", \"true\" },\n-\t\t{ \"index.version\", \"4\" },\n-\t\t{ \"merge.stat\", \"false\" },\n-\t\t{ \"merge.renames\", \"true\" },\n-\t\t{ \"pack.useBitmaps\", \"false\" },\n-\t\t{ \"pack.useSparse\", \"true\" },\n-\t\t{ \"receive.autoGC\", \"false\" },\n-\t\t{ \"reset.quiet\", \"true\" },\n-\t\t{ \"feature.manyFiles\", \"false\" },\n-\t\t{ \"feature.experimental\", \"false\" },\n-\t\t{ \"fetch.unpackLimit\", \"1\" },\n-\t\t{ \"fetch.writeCommitGraph\", \"false\" },\n+\t\t{ \"core.logAllRefUpdates\", \"true\", 1 },\n+\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\", 1 },\n+\t\t{ \"credential.validate\", \"false\", 1 }, /* GCM4W-only */\n+\t\t{ \"gc.auto\", \"0\", 1 },\n+\t\t{ \"gui.GCWarning\", \"false\", 1 },\n+\t\t{ \"index.threads\", \"true\", 1 },\n+\t\t{ \"index.version\", \"4\", 1 },\n+\t\t{ \"merge.stat\", \"false\", 1 },\n+\t\t{ \"merge.renames\", \"true\", 1 },\n+\t\t{ \"pack.useBitmaps\", \"false\", 1 },\n+\t\t{ \"pack.useSparse\", \"true\", 1 },\n+\t\t{ \"receive.autoGC\", \"false\", 1 },\n+\t\t{ \"reset.quiet\", \"true\", 1 },\n+\t\t{ \"feature.manyFiles\", \"false\", 1 },\n+\t\t{ \"feature.experimental\", \"false\", 1 },\n+\t\t{ \"fetch.unpackLimit\", \"1\", 1 },\n+\t\t{ \"fetch.writeCommitGraph\", \"false\", 1 },\n #ifdef WIN32\n-\t\t{ \"http.sslBackend\", \"schannel\" },\n+\t\t{ \"http.sslBackend\", \"schannel\", 1 },\n #endif\n+\t\t/* Optional */\n \t\t{ \"status.aheadBehind\", \"false\" },\n \t\t{ \"commitGraph.generationVersion\", \"1\" },\n \t\t{ \"core.autoCRLF\", \"false\" },\n@@ -166,7 +169,8 @@ static int set_recommended_config(void)\n \tchar *value;\n \n \tfor (i = 0; config[i].key; i++) {\n-\t\tif (git_config_get_string(config[i].key, &value)) {\n+\t\tif ((reconfigure && config[i].overwrite_on_reconfigure) ||\n+\t\t    git_config_get_string(config[i].key, &value)) {\n \t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"created\");\n \t\t\tif (git_config_set_gently(config[i].key,\n \t\t\t\t\t\t  config[i].value) < 0)\n@@ -231,7 +235,7 @@ static int register_dir(void)\n \tint res = add_or_remove_enlistment(1);\n \n \tif (!res)\n-\t\tres = set_recommended_config();\n+\t\tres = set_recommended_config(0);\n \n \tif (!res)\n \t\tres = toggle_maintenance(1);\n@@ -419,7 +423,7 @@ static int cmd_clone(int argc, const char **argv)\n \t    (res = run_git(\"sparse-checkout\", \"init\", \"--cone\", NULL)))\n \t\tgoto cleanup;\n \n-\tif (set_recommended_config())\n+\tif (set_recommended_config(0))\n \t\treturn error(_(\"could not configure '%s'\"), dir);\n \n \tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL))) {\n@@ -484,6 +488,24 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_reconfigure(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar reconfigure [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn set_recommended_config(1);\n+}\n+\n static int cmd_run(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -620,6 +642,7 @@ static struct {\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n \t{ \"run\", cmd_run },\n+\t{ \"reconfigure\", cmd_reconfigure },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 39143b08324..89fd7901585 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -13,6 +13,7 @@ scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n+scalar reconfigure <enlistment>\n \n DESCRIPTION\n -----------\n@@ -117,6 +118,13 @@ opinionated default settings that make Git work more efficiently with\n large repositories. As this task is run as part of `scalar clone`\n automatically, explicit invocations of this task are rarely needed.\n \n+Reconfigure\n+~~~~~~~~~~~\n+\n+After a Scalar upgrade, or when the configuration of a Scalar enlistment\n+was somehow corrupted or changed by mistake, this subcommand allows to\n+reconfigure the enlistment.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex f60e086d6f9..fb5e2efee0a 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -65,4 +65,12 @@ test_expect_success 'scalar clone' '\n \t)\n '\n \n+test_expect_success 'scalar reconfigure' '\n+\tgit init one/src &&\n+\tscalar register one &&\n+\tgit -C one/src config core.preloadIndex false &&\n+\tscalar reconfigure one &&\n+\ttest true = \"$(git -C one/src config core.preloadIndex)\"\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"442644","messageId":"5601f82dbe171bdaa9acf8d6b76c97d8a6024dd8.1638273289.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v9.git.1638273289.gitgitgadget@gmail.com","subject":"[PATCH v9 13/17] scalar: implement the `run` command","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-30T11:54:45Z","receivedAt":"2021-11-30T11:55:53Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nNote: this subcommand is provided primarily for backwards-compatibility,\nfor existing Scalar uses. It is mostly just a shim for `git\nmaintenance`, mapping task names from the way Scalar called them to the\nway Git calls them.\n\nThe reason why those names differ? The background maintenance was first\nimplemented in Scalar, and when it was contributed as a patch series\nimplementing the `git maintenance` command, reviewers suggested better\nnames, those suggestions were accepted before the patches were\nintegrated into core Git.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 64 +++++++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt | 19 ++++++++++++\n 2 files changed, 83 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 61b66e48aa8..fa900e4373f 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -484,6 +484,69 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_run(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tstruct {\n+\t\tconst char *arg, *task;\n+\t} tasks[] = {\n+\t\t{ \"config\", NULL },\n+\t\t{ \"commit-graph\", \"commit-graph\" },\n+\t\t{ \"fetch\", \"prefetch\" },\n+\t\t{ \"loose-objects\", \"loose-objects\" },\n+\t\t{ \"pack-files\", \"incremental-repack\" },\n+\t\t{ NULL, NULL }\n+\t};\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tconst char *usagestr[] = { NULL, NULL };\n+\tint i;\n+\n+\tstrbuf_addstr(&buf, N_(\"scalar run <task> [<enlistment>]\\nTasks:\\n\"));\n+\tfor (i = 0; tasks[i].arg; i++)\n+\t\tstrbuf_addf(&buf, \"\\t%s\\n\", tasks[i].arg);\n+\tusagestr[0] = buf.buf;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usagestr, 0);\n+\n+\tif (!argc)\n+\t\tusage_with_options(usagestr, options);\n+\n+\tif (!strcmp(\"all\", argv[0])) {\n+\t\ti = -1;\n+\t} else {\n+\t\tfor (i = 0; tasks[i].arg && strcmp(tasks[i].arg, argv[0]); i++)\n+\t\t\t; /* keep looking for the task */\n+\n+\t\tif (i > 0 && !tasks[i].arg) {\n+\t\t\terror(_(\"no such task: '%s'\"), argv[0]);\n+\t\t\tusage_with_options(usagestr, options);\n+\t\t}\n+\t}\n+\n+\targc--;\n+\targv++;\n+\tsetup_enlistment_directory(argc, argv, usagestr, options, NULL);\n+\tstrbuf_release(&buf);\n+\n+\tif (i == 0)\n+\t\treturn register_dir();\n+\n+\tif (i > 0)\n+\t\treturn run_git(\"maintenance\", \"run\",\n+\t\t\t       \"--task\", tasks[i].task, NULL);\n+\n+\tif (register_dir())\n+\t\treturn -1;\n+\tfor (i = 1; tasks[i].arg; i++)\n+\t\tif (run_git(\"maintenance\", \"run\",\n+\t\t\t    \"--task\", tasks[i].task, NULL))\n+\t\t\treturn -1;\n+\treturn 0;\n+}\n+\n static int remove_deleted_enlistment(struct strbuf *path)\n {\n \tint res = 0;\n@@ -556,6 +619,7 @@ static struct {\n \t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n+\t{ \"run\", cmd_run },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 56f744a4aa9..39143b08324 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -12,6 +12,7 @@ scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<e\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n+scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -98,6 +99,24 @@ unregister [<enlistment>]::\n \tRemove the specified repository from the list of repositories\n \tregistered with Scalar and stop the scheduled background maintenance.\n \n+Run\n+~~~\n+\n+scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]::\n+\tRun the given maintenance task (or all tasks, if `all` was specified).\n+\tExcept for `all` and `config`, this subcommand simply hands off to\n+\tlinkgit:git-maintenance[1] (mapping `fetch` to `prefetch` and\n+\t`pack-files` to `incremental-repack`).\n++\n+These tasks are run automatically as part of the scheduled maintenance,\n+as soon as the repository is registered with Scalar. It should therefore\n+not be necessary to run this subcommand manually.\n++\n+The `config` task is specific to Scalar and configures all those\n+opinionated default settings that make Git work more efficiently with\n+large repositories. As this task is run as part of `scalar clone`\n+automatically, explicit invocations of this task are rarely needed.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"442645","messageId":"42121a5764d509e1bf87f6883f436a9bc7f010c6.1638273289.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v9.git.1638273289.gitgitgadget@gmail.com","subject":"[PATCH v9 09/17] scalar: let 'unregister' handle a deleted enlistment directory gracefully","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-30T11:54:41Z","receivedAt":"2021-11-30T11:55:56Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nWhen a user deleted an enlistment manually, let's be generous and\n_still_ unregister it.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 46 ++++++++++++++++++++++++++++++++\n contrib/scalar/t/t9099-scalar.sh | 15 +++++++++++\n 2 files changed, 61 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 9ab9dffe3ac..ec783e72ef3 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -269,6 +269,24 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int remove_deleted_enlistment(struct strbuf *path)\n+{\n+\tint res = 0;\n+\tstrbuf_realpath_forgiving(path, path->buf, 1);\n+\n+\tif (run_git(\"config\", \"--global\",\n+\t\t    \"--unset\", \"--fixed-value\",\n+\t\t    \"scalar.repo\", path->buf, NULL) < 0)\n+\t\tres = -1;\n+\n+\tif (run_git(\"config\", \"--global\",\n+\t\t    \"--unset\", \"--fixed-value\",\n+\t\t    \"maintenance.repo\", path->buf, NULL) < 0)\n+\t\tres = -1;\n+\n+\treturn res;\n+}\n+\n static int cmd_unregister(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -282,6 +300,34 @@ static int cmd_unregister(int argc, const char **argv)\n \targc = parse_options(argc, argv, NULL, options,\n \t\t\t     usage, 0);\n \n+\t/*\n+\t * Be forgiving when the enlistment or worktree does not even exist any\n+\t * longer; This can be the case if a user deleted the worktree by\n+\t * mistake and _still_ wants to unregister the thing.\n+\t */\n+\tif (argc == 1) {\n+\t\tstruct strbuf src_path = STRBUF_INIT, workdir_path = STRBUF_INIT;\n+\n+\t\tstrbuf_addf(&src_path, \"%s/src/.git\", argv[0]);\n+\t\tstrbuf_addf(&workdir_path, \"%s/.git\", argv[0]);\n+\t\tif (!is_directory(src_path.buf) && !is_directory(workdir_path.buf)) {\n+\t\t\t/* remove possible matching registrations */\n+\t\t\tint res = -1;\n+\n+\t\t\tstrbuf_strip_suffix(&src_path, \"/.git\");\n+\t\t\tres = remove_deleted_enlistment(&src_path) && res;\n+\n+\t\t\tstrbuf_strip_suffix(&workdir_path, \"/.git\");\n+\t\t\tres = remove_deleted_enlistment(&workdir_path) && res;\n+\n+\t\t\tstrbuf_release(&src_path);\n+\t\t\tstrbuf_release(&workdir_path);\n+\t\t\treturn res;\n+\t\t}\n+\t\tstrbuf_release(&src_path);\n+\t\tstrbuf_release(&workdir_path);\n+\t}\n+\n \tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n \n \treturn unregister_dir();\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 16f2b72b126..ef0e8d680d5 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -14,4 +14,19 @@ test_expect_success 'scalar shows a usage' '\n \ttest_expect_code 129 scalar -h\n '\n \n+test_expect_success 'scalar unregister' '\n+\tgit init vanish/src &&\n+\tscalar register vanish/src &&\n+\tgit config --get --global --fixed-value \\\n+\t\tmaintenance.repo \"$(pwd)/vanish/src\" &&\n+\tscalar list >scalar.repos &&\n+\tgrep -F \"$(pwd)/vanish/src\" scalar.repos &&\n+\trm -rf vanish/src/.git &&\n+\tscalar unregister vanish &&\n+\ttest_must_fail git config --get --global --fixed-value \\\n+\t\tmaintenance.repo \"$(pwd)/vanish/src\" &&\n+\tscalar list >scalar.repos &&\n+\t! grep -F \"$(pwd)/vanish/src\" scalar.repos\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"442646","messageId":"211130.86mtlleqhm.gmgdl@evledraar.gmail.com","threadId":"56409","inReplyTo":"pull.1005.v9.git.1638273289.gitgitgadget@gmail.com","subject":"Re: [PATCH v9 00/17] Upstreaming the Scalar command","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-11-30T12:16:37Z","receivedAt":"2021-11-30T13:13:14Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Nov 30 2021, Johannes Schindelin via GitGitGadget wrote:\n\n> [...]\n>  * The rebase on top of v2.34.0, which changed the default merge strategy to\n>    ORT, should have changed the default for merge.renames to true. This is\n>    now the case.\n>  * Accommodate preemptively for ab/ci-updates which invalidates assumptions\n>    made by this patch series that would still hold true with v2.34.0 but are\n>    no longer valid in seen and would trigger CI build breakages.\n> [...]\n>   1:  3a2e28275f1 =  1:  3a2e28275f1 scalar: add a README with a roadmap\n>   2:  50160d61a41 =  2:  50160d61a41 scalar: create a rudimentary executable\n>   3:  74cd6410931 =  3:  74cd6410931 scalar: start documenting the command\n>   4:  37231a4dd07 =  4:  37231a4dd07 scalar: create test infrastructure\n>   5:  a39b9c81214 =  5:  a39b9c81214 cmake: optionally build `scalar`, too\n>   6:  8e3542e43f7 !  6:  8c6762def30 ci: also run the `scalar` tests\n>      @@ .github/workflows/main.yml: jobs:\n>            - name: upload tracked files and build artifacts\n>       \n>        ## ci/run-build-and-tests.sh ##\n>      -@@ ci/run-build-and-tests.sh: linux-gcc-4.8|pedantic)\n>      - \tmake test\n>      - \t;;\n>      - esac\n>      -+make -C contrib/scalar test\n>      +@@ ci/run-build-and-tests.sh: esac\n>        \n>        check_unignored_build_artifacts\n>        \n>      ++make && make -C contrib/scalar test\n>      ++\n>      + save_good_tree\n\nThis gets rid of the hard CI failure we saw in \"seen\" but still carries\nforward the logic error noted in [1] and of the \"pedantic\"\ncompilation-only job now running tests, which isn't what that job is\nsupposed to be doing.\n\nSee the \"Don't run the tests\" comment in cebead1ebfb (ci: run a pedantic\nbuild as part of the GitHub workflow, 2021-08-08). As noted before you\ncan see that at the tail-end of your own CI output[2] on top of\n\"master\".\n\nThere's other seemingly unintended interactions with the ci/ code on\n\"master\" here (which I also also noted in previous threads). FWIW the\nnaïve merge with ab/ci-updates happens to fix at least one of\nthose. I.e. the issue here of \"linux-gcc\" and \"linux-clang\" only running\nthe scalar tests in one half of their test modes, but running everything\nelse in both.\n\nI think it's clear from past exchanges that you vehemently disagree with\nmy proposed direction for solving these and other issues in one fell\nswoop[3], which is fair enough.\n\nBut I really don't think it's OK to continue to ignore reports from me\nof specific issues in this series just because you don't like that\nlarger set of fixes [3].\n\nI honestly don't care if you'd pick that up as-is, just as long as\noutstanding issues it addressed are either fixed, or commit\nmessages/docs are updated to note that the bugs/behavior changes are\nintentional.\n\nIn this case I think it would be fine to keep the patch as-is and have\nthe commit message argue for why the scalar tests should be a special\nsnowflake in being the only tests that are run in \"pedantic\".\n\nOr to just fix the seemingly unintentional behavior change in some\nsmaller way. I think the \"added thusly\" comment I hade in [4] should be\nthe easiest way to do that (well, [3] is easier, but let's leave that\naside...).\n\n1. https://lore.kernel.org/git/211122.86ee78yxts.gmgdl@evledraar.gmail.com/\n2. https://github.com/gitgitgadget/git/runs/4292915519?check_suite_focus=true\n3. https://lore.kernel.org/git/patch-1.1-86fb8d56307-20211028T185016Z-avarab@gmail.com/\n4. https://lore.kernel.org/git/211123.86ilwjujmd.gmgdl@evledraar.gmail.com/\n"},{"id":"442649","messageId":"211130.86ee6xepgw.gmgdl@evledraar.gmail.com","threadId":"56409","inReplyTo":"37231a4dd07833807639c8a650185569d0c99af2.1637363025.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v8 04/17] scalar: create test infrastructure","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-11-30T13:27:03Z","receivedAt":"2021-11-30T13:35:23Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Nov 19 2021, Johannes Schindelin via GitGitGadget wrote:\n\n> From: Johannes Schindelin <johannes.schindelin@gmx.de>\n> [...]\n> +-include ../../../config.mak.autogen\n> +-include ../../../config.mak\n> +\n> +SHELL_PATH ?= $(SHELL)\n> +PERL_PATH ?= /usr/bin/perl\n> +RM ?= rm -f\n> +PROVE ?= prove\n> +DEFAULT_TEST_TARGET ?= test\n> +TEST_LINT ?= test-lint\n> +\n> +ifdef TEST_OUTPUT_DIRECTORY\n> +TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results\n> +else\n> +TEST_RESULTS_DIRECTORY = ../../../t/test-results\n> +endif\n\nSo here we'll inject our output into t/test-results[...]\n\n> +T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))\n\n...end up with a $(T) with just the one scalar test...\n\n> +test-lint-duplicates:\n> +\t@dups=`echo $(T) | tr ' ' '\\n' | sed 's/-.*//' | sort | uniq -d` && \\\n> +\t\ttest -z \"$$dups\" || { \\\n> +\t\techo >&2 \"duplicate test numbers:\" $$dups; exit 1; }\n> +\n\n...so I *think* (but haven't checked) that print-test-failures.sh will\nwork with the later CI integration, but both t/Makefile & this one\nassume in some ways that test numbers are unique, this now lives in the\nsame namespace, but neither check the two for conflicts.\n\nWe only have the one t9099 test here, so unless we add another one in\nthe top-level t/ that should be OK, but is there any reason to carry\nthis at all? It seems pointless given the above.\n\nI think to make this meaningful we'd need to teach t/Makefile to have a\nT_ALL variable or something, derived from its T, and have that wildcard\nthe scalar tests and other contrib tests and check them for namespacing\nconflicts.\n\n> +test-results:\n> +\tmkdir -p test-results\n> +\n\nThis target seems to be unused, don't we always use the top-level\nt/test-results? This seems to be copy/pasting also dead code from\ncontrib/subtree/t/Makefile.\n"},{"id":"442658","messageId":"nycvar.QRO.7.76.6.2111301450030.63@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"211130.86mtlleqhm.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH v9 00/17] Upstreaming the Scalar command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-11-30T14:11:19Z","receivedAt":"2021-11-30T14:13:48Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Ævar,\n\nOn Tue, 30 Nov 2021, Ævar Arnfjörð Bjarmason wrote:\n\n> On Tue, Nov 30 2021, Johannes Schindelin via GitGitGadget wrote:\n>\n> > [...]\n\nUnfortunately, you clipped the most important part, the part that I put in\nthere mostly for your benefit. So let me repeat it once again:\n\nThis patch series' focus is entirely on Scalar, on choosing sensible\ndefaults and offering a delightful user experience around working with\nmonorepos, and not about changing any existing paradigms for contrib/.\n\nI do see that you want to drag the conversation back to discussing the\nbuild process, and the CI integration. And on changing the way things are\ndone in `contrib/`. You've made that point abundantly clear. I just don't\nsee how that could possibly improve Scalar. I mean, if it failed during\nthe past 4 months, why expect any different outcome in the future.\n\nCiao,\nJohannes\n"},{"id":"442669","messageId":"211130.861r2xelmx.gmgdl@evledraar.gmail.com","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2111301450030.63@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v9 00/17] Upstreaming the Scalar command","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-11-30T14:50:08Z","receivedAt":"2021-11-30T15:03:27Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Nov 30 2021, Johannes Schindelin wrote:\n\n> Hi Ævar,\n>\n> On Tue, 30 Nov 2021, Ævar Arnfjörð Bjarmason wrote:\n>\n>> On Tue, Nov 30 2021, Johannes Schindelin via GitGitGadget wrote:\n>>\n>> > [...]\n>\n> Unfortunately, you clipped the most important part, the part that I put in\n> there mostly for your benefit. So let me repeat it once again:\n>\n> This patch series' focus is entirely on Scalar, on choosing sensible\n> defaults and offering a delightful user experience around working with\n> monorepos, and not about changing any existing paradigms for contrib/.\n>\n> I do see that you want to drag the conversation back to discussing the\n> build process, and the CI integration. And on changing the way things are\n> done in `contrib/`. You've made that point abundantly clear. I just don't\n> see how that could possibly improve Scalar. I mean, if it failed during\n> the past 4 months, why expect any different outcome in the future.\n\nThe seemingly unintentional behavior change in CI jobs that aren't\nscalar jobs you're introducing started in v7 of this series, submitted\non November 17th:\n\n    https://lore.kernel.org/git/1b0328fa236a35c2427b82f53c32944e513580d3.1637158762.git.gitgitgadget@gmail.com/\n\nSo as far as any CI testing is concerned we're talking about just under\n2 weeks.\n\nI really don't see how that and other unintentional behavior changes in\nthe CI on top of \"master\" have anything to do with \"the build process\"\nin the sense that we've discussed as part of the greater scalar\nintegration topic in the past.\n\nI only linked to those thread(s) because some of that behavior changing\n(i.e. now running tests in a previously compile-only job) is apparent\nwhen either running with my patch-on-top of this series, or it was\ndiscussed in some threads related to that & the merger with\nab/ci-updates.\n\nIs it intentional that the previously compile-only \"pedantic\" job is now\nrunning the scalar tests?\n"},{"id":"442783","messageId":"xmqqtufs1a39.fsf@gitster.g","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2111301450030.63@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v9 00/17] Upstreaming the Scalar command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-12-01T17:58:02Z","receivedAt":"2021-12-01T17:59:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> This patch series' focus is entirely on Scalar, on choosing sensible\n> defaults and offering a delightful user experience around working with\n> monorepos, and not about changing any existing paradigms for contrib/.\n\nSorry, but I am confused.\n\nThe change to add \"make &&\" before testing scalar is a good change\nthat allows CI to work with \"existing paradigm for contrib/\" that is\n\"you need to build the top before doing anything in contrib/\".\n\nBut none of the contrib/ stuff is tested in the pedantic job, but if\nI understand correctly, we start some (namely, scalar) stuff in it\ntested there, deviating from existing practice.  Is that intended?\n"},{"id":"442870","messageId":"nycvar.QRO.7.76.6.2112021549330.63@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"xmqqtufs1a39.fsf@gitster.g","subject":"Re: [PATCH v9 00/17] Upstreaming the Scalar command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-12-02T14:53:32Z","receivedAt":"2021-12-02T14:53:57Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 1 Dec 2021, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>\n> > This patch series' focus is entirely on Scalar, on choosing sensible\n> > defaults and offering a delightful user experience around working with\n> > monorepos, and not about changing any existing paradigms for contrib/.\n>\n> Sorry, but I am confused.\n>\n> The change to add \"make &&\" before testing scalar is a good change\n> that allows CI to work with \"existing paradigm for contrib/\" that is\n> \"you need to build the top before doing anything in contrib/\".\n>\n> But none of the contrib/ stuff is tested in the pedantic job, but if\n> I understand correctly, we start some (namely, scalar) stuff in it\n> tested there, deviating from existing practice.  Is that intended?\n\nNo, it was not intended. It was not even intended to integrate Scalar this\ntightly with Git's CI, but since you did not move along `js/scalar` into\n`next` for the past weeks, when no reviewer had anything to add to the\nactual code in `contrib/scalar/` nor were there any objections to\nintegrate it, I made the mistake of assuming that you agreed with Ævar\nthat such a tight integration into Git's CI was desired.\n\nHowever, I do not want to make the mistake again of assuming what your\nthoughts are, so let me ask you directly: from your perspective, what is\nstopping the inclusion of `js/scalar` into `next`?\n\nI am patiently and eagerly awaiting your answer,\nDscho\n"},{"id":"442882","messageId":"xmqq1r2vvszo.fsf@gitster.g","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2112021549330.63@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v9 00/17] Upstreaming the Scalar command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-12-02T17:03:55Z","receivedAt":"2021-12-02T17:04:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> No, it was not intended. It was not even intended to integrate Scalar this\n> tightly with Git's CI, but since you did not move along `js/scalar` into\n> `next` for the past weeks, when no reviewer had anything to add to the\n> actual code in `contrib/scalar/` nor were there any objections to\n> integrate it, I made the mistake of assuming that you agreed with Ævar\n> that such a tight integration into Git's CI was desired.\n\nOK, sorry to hear that we had miscommunication.  \n\nI took the lack of comments an indication that people are not either\ninterested in it, or viewing it as not-quite-ready-yet and waiting\nfor a \"more or less done\" version.\n\nI think the CI updates from Ævar would be one of the things we'd\nhave early in 'next' in this cycle, so if the topic does not play\nnice with it, the perception that it is not yet part of the regular\nCI testing would continue, I am afraid.\n\nThanks.\n"},{"id":"442894","messageId":"CABPp-BGaHHUuQqvvDLBSaRG7C=SzS-ykOLi4HCdDDXQHgHjmJA@mail.gmail.com","threadId":"56409","inReplyTo":"xmqq1r2vvszo.fsf@gitster.g","subject":"Re: [PATCH v9 00/17] Upstreaming the Scalar command","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-12-02T17:39:12Z","receivedAt":"2021-12-02T17:39:33Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Thu, Dec 2, 2021 at 9:03 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>\n> > No, it was not intended. It was not even intended to integrate Scalar this\n> > tightly with Git's CI, but since you did not move along `js/scalar` into\n> > `next` for the past weeks, when no reviewer had anything to add to the\n> > actual code in `contrib/scalar/` nor were there any objections to\n> > integrate it, I made the mistake of assuming that you agreed with Ævar\n> > that such a tight integration into Git's CI was desired.\n>\n> OK, sorry to hear that we had miscommunication.\n>\n> I took the lack of comments an indication that people are not either\n> interested in it, or viewing it as not-quite-ready-yet and waiting\n> for a \"more or less done\" version.\n\nSorry that my work project rendered me unable to respond for over a month.\n\n> I think the CI updates from Ævar would be one of the things we'd\n> have early in 'next' in this cycle, so if the topic does not play\n> nice with it, the perception that it is not yet part of the regular\n> CI testing would continue, I am afraid.\n\nI think I missed the answer.  I believe Johannes was curious if he\nreverted the recent CI testing of scalar he added in v7 (which would\nas a side effect make it play nicely with Ævar's CI updates), if the\nresulting version of js/scalar would be acceptable for next.\n\n\n(If my opinion matters: I'd be in favor.  In more detail: Personally,\nwhile I think CI testing would be nice once we have a functionally\nuseful scalar, the CI tests of this early version aren't really\nnetting us anything.  And they're blocking future scalar series\nunnecessarily.  Johannes already said he had planned CI testing for a\nfuture series, so I'd rather just take this version of js/scalar minus\nthe CI integration for next.)\n"},{"id":"442908","messageId":"xmqq35navofg.fsf@gitster.g","threadId":"56409","inReplyTo":"CABPp-BGaHHUuQqvvDLBSaRG7C=SzS-ykOLi4HCdDDXQHgHjmJA@mail.gmail.com","subject":"Re: [PATCH v9 00/17] Upstreaming the Scalar command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-12-02T18:42:27Z","receivedAt":"2021-12-02T18:42:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n> while I think CI testing would be nice once we have a functionally\n> useful scalar, the CI tests of this early version aren't really\n> netting us anything.  And they're blocking future scalar series\n> unnecessarily.  Johannes already said he had planned CI testing for a\n> future series, so I'd rather just take this version of js/scalar minus\n> the CI integration for next.)\n\nYeah, with less stomping on each others' toes, things may flow\nsmoother.  As long as people are happy with the core part, that\nsounds like the best approach forward.\n\n"},{"id":"442977","messageId":"pull.1005.v10.git.1638538470.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v9.git.1638273289.gitgitgadget@gmail.com","subject":"[PATCH v10 00/15] Upstreaming the Scalar command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-03T13:34:14Z","receivedAt":"2021-12-03T13:34:34Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"tl;dr: This series contributes the core part of the Scalar command to the\nGit project. This command provides a convenient way to clone/initialize very\nlarge repositories (think: monorepos).\n\nNote: This patch series' focus is entirely on Scalar, on choosing sensible\ndefaults and offering a delightful user experience around working with\nmonorepos, and not about changing any existing paradigms for contrib/ (even\nif catching up on the mail thread is likely to give interested readers that\nfalse impression).\n\nChanges since v9:\n\n * The patches to build Scalar and run its tests as part of Git's CI/PR,\n   have been dropped because a recent unrelated patch series does not\n   interact well with them.\n\nChanges since v8:\n\n * The rebase on top of v2.34.0, which changed the default merge strategy to\n   ORT, should have changed the default for merge.renames to true. This is\n   now the case.\n * Accommodate preemptively for ab/ci-updates which invalidates assumptions\n   made by this patch series that would still hold true with v2.34.0 but are\n   no longer valid in seen and would trigger CI build breakages.\n\nChanges since v7:\n\n * Clarified in the commit message why we cannot easily encapsulate the\n   Scalar part of the CMake configuration in contrib/scalar/.\n * Improved the README.md.\n\nChanges since v6:\n\n * Rebased on top of v2.34.0.\n * Inserted a commit that adds contrib/scalar/README.md, containing the\n   roadmap of what I have planned for Scalar.\n * The Scalar test's definition of GIT_TEST_MAINT_SCHEDULER has been\n   adjusted to accommodate for a change in v2.32.0..v2.34.0.\n * The config setting defaults now include fetch.showForcedUpdates=false,\n   which has been identified as helping with a performance issue in large\n   repositories.\n * To avoid mistaking the current patch series for being feature-complete\n   enough to unleash onto end users, I moved the Makefile rules to build\n   HTML/manual pages to a later patch series.\n * The patch that adds support for -c <key>=<value> and -C <directory> was\n   moved to its own add-on patch series: While it is obvious that those\n   options are valuable to have, an open question is whether there are other\n   \"pre-command\" options in git that would be useful, too, and I would like\n   to postpone that discussion to that date.\n * I added two patches that I had planned on keeping in an add-on patch\n   series for later, to build and test Scalar as part of the CI. I am still\n   not 100% certain that it is a good idea to do so already now, but let's\n   see what the reviewers have to say.\n\nChanges since v5:\n\n * Fixed the commit message talking about make -C contrib/scalar/Makefile.\n * Fixed the git ls-tree invocation suggested in the manual for scalar\n   clone.\n * Invoking make -C contrib/scalar, then changing a source file of libgit.a\n   and then immediately invoking make -C contrib/scalar again will now\n   implicitly rebuild libgit.a.\n\nChanges since v4:\n\n * scalar delete now refuses to delete anything if it was started from\n   within the enlistment.\n * scalar delete releases any handles to the object store before deleting\n   the enlistment.\n * The OBJECTS list in the Makefile will now include Scalar.\n * scalar register now supports secondary worktrees, in addition to the\n   primary worktree.\n\nChanges since v3:\n\n * Moved the \"Changes since\" section to the top, to make it easier to see\n   what changed.\n * Reworded the commit message of the first patch.\n * Removed the [RFC] prefix because I did not hear any objections against\n   putting this into contrib/.\n\nChanges since v2:\n\n * Adjusted the description of the list command in the manual page , as\n   suggested by Bagas.\n * Addressed two style nits in cmd_run().\n * The documentation of git reconfigure -a was improved.\n\nChanges since v1:\n\n * A couple typos were fixed\n * The code parsing the output of ls-remote was made more readable\n * The indentation used in scalar.txt now consistently uses tabs\n * We no longer hard-code core.bare = false when registering with Scalar\n\n\nBackground\n==========\n\nMicrosoft invested a lot of effort into scaling Git to the needs of the\nWindows operating system source code. Based on the experience of the first\napproach, VFS for Git, the Scalar project was started. Scalar specifically\nhas as its core goal to funnel all improvements into core Git.\n\n\nThe present\n===========\n\nThe Scalar project provides a completely functional non-virtual experience\nfor monorepos. But why stop there. The Scalar project was designed to be a\nself-destructing vehicle to allow those key concepts to be moved into core\nGit itself for the benefit of all. For example, partial clone,\nsparse-checkout, and scheduled background maintenance have already been\nupstreamed and removed from Scalar proper. This patch series provides a\nC-based implementation of the final remaining portions of the Scalar\ncommand. This will make it easier for users to experiment with the Scalar\ncommand. It will also make it substantially easier to experiment with moving\nfunctionality from Scalar into core Git, while maintaining\nbackwards-compatibility for existing Scalar users.\n\nThe C-based Scalar has been shipped to Scalar users, and can be tested by\nany interested reader: https://github.com/microsoft/git/releases/ (it offers\na Git for Windows installer, a macOS package and an Ubuntu package, Scalar\nhas been included since v2.33.0.vfs.0.0).\n\n\nNext steps\n==========\n\nSince there are existing Scalar users, I want to ensure\nbackwards-compatibility with its existing command-line interface. Keeping\nthat in mind, everything in this series is up for discussion.\n\nI obviously believe that Scalar brings a huge benefit, and think that it\nwould be ideal for all of Scalar's learnings to end up in git clone/git\ninit/git maintenance eventually. It is also conceivable, however, that the\nscalar command could graduate to be a core part of Git at some stage in the\nfuture (such a decision would probably depend highly on users' feedback).\nSee also the discussion about the architecture of Scalar\n[https://lore.kernel.org/git/b67bbef4-e4c3-b6a7-1c7f-7d405902ef8b@gmail.com/],\nkicked off by Stolee.\n\nOn top of this patch series, I have lined up a few more:\n\n 1. Implement a scalar diagnose command.\n 2. Use the built-in FSMonitor (that patch series obviously needs to wait\n    for FSMonitor to be integrated).\n 3. Modify the config machinery to be more generous about concurrent writes,\n    say, to the user-wide config.\n 4. A few patches to optionally build and install scalar as part of a\n    regular Git install (also teaching git help scalar to find the Scalar\n    documentation\n\nThese are included in my vfs-with-scalar branch thicket\n[https://github.com/dscho/git/commits/vfs-with-scalar]. On top of that, this\nbranch thicket also includes patches I do not plan on upstreaming, mainly\nbecause they are too specific either to VFS for Git, or they support Azure\nRepos (which does not offer partial clones but speaks the GVFS protocol,\nwhich can be used to emulate partial clones).\n\nOne other thing is very interesting about that vfs-with-scalar branch\nthicket: it contains a GitHub workflow which will run Scalar's quite\nextensive Functional Tests suite. This test suite is quite comprehensive and\ncaught us a lot of bugs in the past, not only in the Scalar code, but also\ncore Git.\n\n\nEpilogue\n========\n\nNow, to address some questions that I imagine every reader has who made it\nthis far:\n\n * Why not put the Scalar functionality directly into core Git, even a\n   built-in? I wanted to provide an easy way for Git contributors to \"play\n   with\" Scalar, without forcing a new top-level command into Git.\n * Why implement the Scalar command in the Git code base? Apart from\n   simplifying Scalar maintenance in the Microsoft port of Git, the tight\n   version coupling between Git and Scalar reduces the maintenance burden\n   even further. Besides, I believe that it will make it much easier to\n   shift functionality from Scalar into core Git, once we took the hurdle of\n   accepting the Scalar code into the code base.\n * Why contribute Scalar to the Git project? We are biased, of course, yet\n   our data-driven approach provides evidence that Scalar helps handling\n   huge repositories with ease. By contributing it to the core Git project,\n   we are able to share it with more users, especially some users who do not\n   want to install Microsoft's fork of Git. We also hope that a lot of\n   Scalar (maybe all of it) will end up in core Git, to benefit even more\n   users.\n\nDerrick Stolee (4):\n  scalar: 'register' sets recommended config and starts maintenance\n  scalar: 'unregister' stops background maintenance\n  scalar: implement 'scalar list'\n  scalar: implement the `run` command\n\nJohannes Schindelin (10):\n  scalar: add a README with a roadmap\n  scalar: create a rudimentary executable\n  scalar: start documenting the command\n  scalar: create test infrastructure\n  scalar: let 'unregister' handle a deleted enlistment directory\n    gracefully\n  scalar: implement the `clone` subcommand\n  scalar: teach 'clone' to support the --single-branch option\n  scalar: allow reconfiguring an existing enlistment\n  scalar: teach 'reconfigure' to optionally handle all registered\n    enlistments\n  scalar: implement the `version` command\n\nMatthew John Cheetham (1):\n  scalar: implement the `delete` command\n\n Makefile                         |   9 +\n contrib/scalar/.gitignore        |   2 +\n contrib/scalar/Makefile          |  45 ++\n contrib/scalar/README.md         |  82 +++\n contrib/scalar/scalar.c          | 826 +++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        | 145 ++++++\n contrib/scalar/t/Makefile        |  78 +++\n contrib/scalar/t/t9099-scalar.sh |  88 ++++\n 8 files changed, 1275 insertions(+)\n create mode 100644 contrib/scalar/.gitignore\n create mode 100644 contrib/scalar/Makefile\n create mode 100644 contrib/scalar/README.md\n create mode 100644 contrib/scalar/scalar.c\n create mode 100644 contrib/scalar/scalar.txt\n create mode 100644 contrib/scalar/t/Makefile\n create mode 100755 contrib/scalar/t/t9099-scalar.sh\n\n\nbase-commit: cd3e606211bb1cf8bc57f7d76bab98cc17a150bc\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1005%2Fdscho%2Fscalar-the-beginning-v10\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1005/dscho/scalar-the-beginning-v10\nPull-Request: https://github.com/gitgitgadget/git/pull/1005\n\nRange-diff vs v9:\n\n  1:  3a2e28275f1 =  1:  3a2e28275f1 scalar: add a README with a roadmap\n  2:  50160d61a41 =  2:  50160d61a41 scalar: create a rudimentary executable\n  3:  74cd6410931 =  3:  74cd6410931 scalar: start documenting the command\n  4:  37231a4dd07 =  4:  37231a4dd07 scalar: create test infrastructure\n  5:  a39b9c81214 <  -:  ----------- cmake: optionally build `scalar`, too\n  6:  8c6762def30 <  -:  ----------- ci: also run the `scalar` tests\n  7:  936ee0475ad =  5:  4439ab4de0b scalar: 'register' sets recommended config and starts maintenance\n  8:  09a15f86c3d =  6:  376056066a0 scalar: 'unregister' stops background maintenance\n  9:  42121a5764d =  7:  c865e89beb3 scalar: let 'unregister' handle a deleted enlistment directory gracefully\n 10:  6afb2eb4163 =  8:  3f8b0abd7d6 scalar: implement 'scalar list'\n 11:  dd4e3a4b761 =  9:  60659c47196 scalar: implement the `clone` subcommand\n 12:  abd9c8827cd = 10:  45aca840764 scalar: teach 'clone' to support the --single-branch option\n 13:  5601f82dbe1 = 11:  15e649a1734 scalar: implement the `run` command\n 14:  08e4f548aa8 = 12:  2a3fb40bd9a scalar: allow reconfiguring an existing enlistment\n 15:  0cec6dbd2cb = 13:  efd808a0c4a scalar: teach 'reconfigure' to optionally handle all registered enlistments\n 16:  835f1c79792 = 14:  8b69462b906 scalar: implement the `delete` command\n 17:  4ee1b701c7b = 15:  b5f416d79b4 scalar: implement the `version` command\n\n-- \ngitgitgadget\n"},{"id":"442978","messageId":"3a2e28275f112d114bbd3ba046f440c5f917fc32.1638538470.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v10.git.1638538470.gitgitgadget@gmail.com","subject":"[PATCH v10 01/15] scalar: add a README with a roadmap","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-03T13:34:15Z","receivedAt":"2021-12-03T13:34:36Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe Scalar command will be contributed incrementally, over a bunch of\npatch series. Let's document what Scalar is about, and then describe the\npatch series that are planned.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/README.md | 82 ++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 82 insertions(+)\n create mode 100644 contrib/scalar/README.md\n\ndiff --git a/contrib/scalar/README.md b/contrib/scalar/README.md\nnew file mode 100644\nindex 00000000000..634b5771ed3\n--- /dev/null\n+++ b/contrib/scalar/README.md\n@@ -0,0 +1,82 @@\n+# Scalar - an opinionated repository management tool\n+\n+Scalar is an add-on to Git that helps users take advantage of advanced\n+performance features in Git. Originally implemented in C# using .NET Core,\n+based on the learnings from the VFS for Git project, most of the techniques\n+developed by the Scalar project have been integrated into core Git already:\n+\n+* partial clone,\n+* commit graphs,\n+* multi-pack index,\n+* sparse checkout (cone mode),\n+* scheduled background maintenance,\n+* etc\n+\n+This directory contains the remaining parts of Scalar that are not (yet) in\n+core Git.\n+\n+## Roadmap\n+\n+The idea is to populate this directory via incremental patch series and\n+eventually move to a top-level directory next to `gitk-git/` and to `git-gui/`. The\n+current plan involves the following patch series:\n+\n+- `scalar-the-beginning`: The initial patch series which sets up\n+  `contrib/scalar/` and populates it with a minimal `scalar` command that\n+  demonstrates the fundamental ideas.\n+\n+- `scalar-c-and-C`: The `scalar` command learns about two options that can be\n+  specified before the command, `-c <key>=<value>` and `-C <directory>`.\n+\n+- `scalar-diagnose`: The `scalar` command is taught the `diagnose` subcommand.\n+\n+- `scalar-and-builtin-fsmonitor`: The built-in FSMonitor is enabled in `scalar\n+  register` and in `scalar clone`, for an enormous performance boost when\n+  working in large worktrees. This patch series necessarily depends on Jeff\n+  Hostetler's FSMonitor patch series to be integrated into Git.\n+\n+- `scalar-gentler-config-locking`: Scalar enlistments are registered in the\n+  user's Git config. This usually does not represent any problem because it is\n+  rare for a user to register an enlistment. However, in Scalar's functional\n+  tests, Scalar enlistments are created galore, and in parallel, which can lead\n+  to lock contention. This patch series works around that problem by re-trying\n+  to lock the config file in a gentle fashion.\n+\n+- `scalar-extra-docs`: Add some extensive documentation that has been written\n+  in the original Scalar project (all subject to discussion, of course).\n+\n+- `optionally-install-scalar`: Now that Scalar is feature (and documentation)\n+  complete and is verified in CI builds, let's offer to install it.\n+\n+- `move-scalar-to-toplevel`: Now that Scalar is complete, let's move it next to\n+  `gitk-git/` and to `git-gui/`, making it a top-level command.\n+\n+The following two patch series exist in Microsoft's fork of Git and are\n+publicly available. There is no current plan to upstream them, not because I\n+want to withhold these patches, but because I don't think the Git community is\n+interested in these patches.\n+\n+There are some interesting ideas there, but the implementation is too specific\n+to Azure Repos and/or VFS for Git to be of much help in general (and also: my\n+colleagues tried to upstream some patches already and the enthusiasm for\n+integrating things related to Azure Repos and VFS for Git can be summarized in\n+very, very few words).\n+\n+These still exist mainly because the GVFS protocol is what Azure Repos has\n+instead of partial clone, while Git is focused on improving partial clone:\n+\n+- `scalar-with-gvfs`: The primary purpose of this patch series is to support\n+  existing Scalar users whose repositories are hosted in Azure Repos (which\n+  does not support Git's partial clones, but supports its predecessor, the GVFS\n+  protocol, which is used by Scalar to emulate the partial clone).\n+\n+  Since the GVFS protocol will never be supported by core Git, this patch\n+  series will remain in Microsoft's fork of Git.\n+\n+- `run-scalar-functional-tests`: The Scalar project developed a quite\n+  comprehensive set of integration tests (or, \"Functional Tests\"). They are the\n+  sole remaining part of the original C#-based Scalar project, and this patch\n+  adds a GitHub workflow that runs them all.\n+\n+  Since the tests partially depend on features that are only provided in the\n+  `scalar-with-gvfs` patch series, this patch cannot be upstreamed.\n-- \ngitgitgadget\n\n"},{"id":"442979","messageId":"50160d61a41c182d9b8c6c859975f89415de8482.1638538470.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v10.git.1638538470.gitgitgadget@gmail.com","subject":"[PATCH v10 02/15] scalar: create a rudimentary executable","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-03T13:34:16Z","receivedAt":"2021-12-03T13:34:41Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe idea of Scalar (https://github.com/microsoft/scalar), and before\nthat, of VFS for Git, has always been to prove that Git _can_ scale, and\nto upstream whatever strategies have been demonstrated to help.\n\nWith this patch, we start the journey from that C# project to move what\nis left to Git's own `contrib/` directory, reimplementing it in pure C,\nwith the intention to facilitate integrating the functionality into core\nGit all while maintaining backwards-compatibility for existing Scalar\nusers (which will be much easier when both live in the same worktree).\nIt has always been the plan to contribute all of the proven strategies\nback to core Git.\n\nFor example, while the virtual filesystem provided by VFS for Git helped\nthe team developing the Windows operating system to move onto Git, while\ntrying to upstream it we realized that it cannot be done: getting the\nvirtual filesystem to work (which we only managed to implement fully on\nWindows, but not on, say, macOS or Linux), and the required server-side\nsupport for the GVFS protocol, made this not quite feasible.\n\nThe Scalar project learned from that and tackled the problem with\ndifferent tactics: instead of pretending to Git that the working\ndirectory is fully populated, it _specifically_ teaches Git about\npartial clone (which is based on VFS for Git's cache server), about\nsparse checkout (which VFS for Git tried to do transparently, in the\nfile system layer), and regularly runs maintenance tasks to keep the\nrepository in a healthy state.\n\nWith partial clone, sparse checkout and `git maintenance` having been\nupstreamed, there is little left that `scalar.exe` does which `git.exe`\ncannot do. One such thing is that `scalar clone <url>` will\nautomatically set up a partial, sparse clone, and configure\nknown-helpful settings from the start.\n\nSo let's bring this convenience into Git's tree.\n\nThe idea here is that you can (optionally) build Scalar via\n\n\tmake -C contrib/scalar/\n\nThis will build the `scalar` executable and put it into the\ncontrib/scalar/ subdirectory.\n\nThe slightly awkward addition of the `contrib/scalar/*` bits to the\ntop-level `Makefile` are actually really required: we want to link to\n`libgit.a`, which means that we will need to use the very same `CFLAGS`\nand `LDFLAGS` as the rest of Git.\n\nAn early development version of this patch tried to replicate all the\nconditional code in `contrib/scalar/Makefile` (e.g. `NO_POLL`) just like\n`contrib/svn-fe/Makefile` used to do before it was retired. It turned\nout to be quite the whack-a-mole game: the SHA-1-related flags, the\nflags enabling/disabling `compat/poll/`, `compat/regex/`,\n`compat/win32mmap.c` & friends depending on the current platform... To\nput it mildly: it was a major mess.\n\nInstead, this patch makes minimal changes to the top-level `Makefile` so\nthat the bits in `contrib/scalar/` can be compiled and linked, and\nadds a `contrib/scalar/Makefile` that uses the top-level `Makefile` in a\nmost minimal way to do the actual compiling.\n\nNote: With this commit, we only establish the infrastructure, no\nScalar functionality is implemented yet; We will do that incrementally\nover the next few commits.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Makefile                  |  9 +++++++++\n contrib/scalar/.gitignore |  2 ++\n contrib/scalar/Makefile   | 34 ++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.c   | 36 ++++++++++++++++++++++++++++++++++++\n 4 files changed, 81 insertions(+)\n create mode 100644 contrib/scalar/.gitignore\n create mode 100644 contrib/scalar/Makefile\n create mode 100644 contrib/scalar/scalar.c\n\ndiff --git a/Makefile b/Makefile\nindex 12be39ac497..fe898aeea08 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2456,6 +2456,11 @@ OBJECTS += $(FUZZ_OBJS)\n ifndef NO_CURL\n \tOBJECTS += http.o http-walker.o remote-curl.o\n endif\n+\n+SCALAR_SOURCES := contrib/scalar/scalar.c\n+SCALAR_OBJECTS := $(SCALAR_SOURCES:c=o)\n+OBJECTS += $(SCALAR_OBJECTS)\n+\n .PHONY: objects\n objects: $(OBJECTS)\n \n@@ -2589,6 +2594,10 @@ $(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n \t\t$(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)\n \n+contrib/scalar/scalar$X: $(SCALAR_OBJECTS) GIT-LDFLAGS $(GITLIBS)\n+\t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \\\n+\t\t$(filter %.o,$^) $(LIBS)\n+\n $(LIB_FILE): $(LIB_OBJS)\n \t$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^\n \ndiff --git a/contrib/scalar/.gitignore b/contrib/scalar/.gitignore\nnew file mode 100644\nindex 00000000000..ff3d47e84d0\n--- /dev/null\n+++ b/contrib/scalar/.gitignore\n@@ -0,0 +1,2 @@\n+/*.exe\n+/scalar\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nnew file mode 100644\nindex 00000000000..f6f0036f0fa\n--- /dev/null\n+++ b/contrib/scalar/Makefile\n@@ -0,0 +1,34 @@\n+QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir\n+QUIET_SUBDIR1  =\n+\n+ifneq ($(findstring s,$(MAKEFLAGS)),s)\n+ifndef V\n+\tQUIET_SUBDIR0  = +@subdir=\n+\tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n+\t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n+else\n+\texport V\n+endif\n+endif\n+\n+all:\n+\n+include ../../config.mak.uname\n+-include ../../config.mak.autogen\n+-include ../../config.mak\n+\n+TARGETS = scalar$(X) scalar.o\n+GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n+\n+all: scalar$(X)\n+\n+$(GITLIBS):\n+\t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)\n+\n+$(TARGETS): $(GITLIBS) scalar.c\n+\t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)\n+\n+clean:\n+\t$(RM) $(TARGETS)\n+\n+.PHONY: $(GITLIBS) all clean FORCE\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nnew file mode 100644\nindex 00000000000..7cff29e0fcd\n--- /dev/null\n+++ b/contrib/scalar/scalar.c\n@@ -0,0 +1,36 @@\n+/*\n+ * The Scalar command-line interface.\n+ */\n+\n+#include \"cache.h\"\n+#include \"gettext.h\"\n+#include \"parse-options.h\"\n+\n+static struct {\n+\tconst char *name;\n+\tint (*fn)(int, const char **);\n+} builtins[] = {\n+\t{ NULL, NULL},\n+};\n+\n+int cmd_main(int argc, const char **argv)\n+{\n+\tstruct strbuf scalar_usage = STRBUF_INIT;\n+\tint i;\n+\n+\tif (argc > 1) {\n+\t\targv++;\n+\t\targc--;\n+\n+\t\tfor (i = 0; builtins[i].name; i++)\n+\t\t\tif (!strcmp(builtins[i].name, argv[0]))\n+\t\t\t\treturn !!builtins[i].fn(argc, argv);\n+\t}\n+\n+\tstrbuf_addstr(&scalar_usage,\n+\t\t      N_(\"scalar <command> [<options>]\\n\\nCommands:\\n\"));\n+\tfor (i = 0; builtins[i].name; i++)\n+\t\tstrbuf_addf(&scalar_usage, \"\\t%s\\n\", builtins[i].name);\n+\n+\tusage(scalar_usage.buf);\n+}\n-- \ngitgitgadget\n\n"},{"id":"442980","messageId":"74cd64109319748e532339017b3b76a4823f9c86.1638538470.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v10.git.1638538470.gitgitgadget@gmail.com","subject":"[PATCH v10 03/15] scalar: start documenting the command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-03T13:34:17Z","receivedAt":"2021-12-03T13:34:43Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nLet's build up the documentation for the Scalar command along with the\npatches that implement its functionality.\n\nNote: To discourage the feature-incomplete documentation from being\nmistaken for the complete thing, we do not yet provide any way to build\nHTML or manual pages from the text file.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.txt | 38 ++++++++++++++++++++++++++++++++++++++\n 1 file changed, 38 insertions(+)\n create mode 100644 contrib/scalar/scalar.txt\n\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nnew file mode 100644\nindex 00000000000..5f7131861a5\n--- /dev/null\n+++ b/contrib/scalar/scalar.txt\n@@ -0,0 +1,38 @@\n+scalar(1)\n+=========\n+\n+NAME\n+----\n+scalar - an opinionated repository management tool\n+\n+SYNOPSIS\n+--------\n+[verse]\n+scalar <command> [<options>]\n+\n+DESCRIPTION\n+-----------\n+\n+Scalar is an opinionated repository management tool. By creating new\n+repositories or registering existing repositories with Scalar, your Git\n+experience will speed up. Scalar sets advanced Git config settings,\n+maintains your repositories in the background, and helps reduce data sent\n+across the network.\n+\n+An important Scalar concept is the enlistment: this is the top-level directory\n+of the project. It usually contains the subdirectory `src/` which is a Git\n+worktree. This encourages the separation between tracked files (inside `src/`)\n+and untracked files, such as build artifacts (outside `src/`). When registering\n+an existing Git worktree with Scalar whose name is not `src`, the enlistment\n+will be identical to the worktree.\n+\n+The `scalar` command implements various subcommands, and different options\n+depending on the subcommand.\n+\n+SEE ALSO\n+--------\n+linkgit:git-maintenance[1].\n+\n+Scalar\n+---\n+Associated with the linkgit:git[1] suite\n-- \ngitgitgadget\n\n"},{"id":"442981","messageId":"37231a4dd07833807639c8a650185569d0c99af2.1638538470.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v10.git.1638538470.gitgitgadget@gmail.com","subject":"[PATCH v10 04/15] scalar: create test infrastructure","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-03T13:34:18Z","receivedAt":"2021-12-03T13:34:44Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nTo test the Scalar command, create a test script in contrib/scalar/t\nthat is executed as `make -C contrib/scalar test`. Since Scalar has no\nmeaningful capabilities yet, the only test is rather simple. We will add\nmore tests in subsequent commits that introduce corresponding, new\nfunctionality.\n\nNote: This test script is intended to test `scalar` only lightly, even\nafter all of the functionality is implemented.\n\nA more comprehensive functional (or: integration) test suite can be\nfound at https://github.com/microsoft/scalar; It is used in the workflow\nhttps://github.com/microsoft/git/blob/HEAD/.github/workflows/scalar-functional-tests.yml\nin Microsoft's Git fork. This test suite performs end-to-end tests with\na real remote repository, and is run as part of the regular CI and PR\nbuilds in that fork.\n\nSince those tests require some functionality supported only by\nMicrosoft's Git fork (\"GVFS protocol\"), there is no intention to port\nthat fuller test suite to `contrib/scalar/`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/Makefile          | 17 +++++--\n contrib/scalar/t/Makefile        | 78 ++++++++++++++++++++++++++++++++\n contrib/scalar/t/t9099-scalar.sh | 17 +++++++\n 3 files changed, 109 insertions(+), 3 deletions(-)\n create mode 100644 contrib/scalar/t/Makefile\n create mode 100755 contrib/scalar/t/t9099-scalar.sh\n\ndiff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile\nindex f6f0036f0fa..231b1ee1796 100644\n--- a/contrib/scalar/Makefile\n+++ b/contrib/scalar/Makefile\n@@ -3,6 +3,7 @@ QUIET_SUBDIR1  =\n \n ifneq ($(findstring s,$(MAKEFLAGS)),s)\n ifndef V\n+\tQUIET_GEN      = @echo '   ' GEN $@;\n \tQUIET_SUBDIR0  = +@subdir=\n \tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n \t\t\t $(MAKE) $(PRINT_DIR) -C $$subdir\n@@ -20,7 +21,7 @@ include ../../config.mak.uname\n TARGETS = scalar$(X) scalar.o\n GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a\n \n-all: scalar$(X)\n+all: scalar$(X) ../../bin-wrappers/scalar\n \n $(GITLIBS):\n \t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)\n@@ -29,6 +30,16 @@ $(TARGETS): $(GITLIBS) scalar.c\n \t$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)\n \n clean:\n-\t$(RM) $(TARGETS)\n+\t$(RM) $(TARGETS) ../../bin-wrappers/scalar\n \n-.PHONY: $(GITLIBS) all clean FORCE\n+../../bin-wrappers/scalar: ../../wrap-for-bin.sh Makefile\n+\t@mkdir -p ../../bin-wrappers\n+\t$(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \\\n+\t     -e 's|@@BUILD_DIR@@|$(shell cd ../.. && pwd)|' \\\n+\t     -e 's|@@PROG@@|contrib/scalar/scalar$(X)|' < $< > $@ && \\\n+\tchmod +x $@\n+\n+test: all\n+\t$(MAKE) -C t\n+\n+.PHONY: $(GITLIBS) all clean test FORCE\ndiff --git a/contrib/scalar/t/Makefile b/contrib/scalar/t/Makefile\nnew file mode 100644\nindex 00000000000..6170672bb37\n--- /dev/null\n+++ b/contrib/scalar/t/Makefile\n@@ -0,0 +1,78 @@\n+# Run scalar tests\n+#\n+# Copyright (c) 2005,2021 Junio C Hamano, Johannes Schindelin\n+#\n+\n+-include ../../../config.mak.autogen\n+-include ../../../config.mak\n+\n+SHELL_PATH ?= $(SHELL)\n+PERL_PATH ?= /usr/bin/perl\n+RM ?= rm -f\n+PROVE ?= prove\n+DEFAULT_TEST_TARGET ?= test\n+TEST_LINT ?= test-lint\n+\n+ifdef TEST_OUTPUT_DIRECTORY\n+TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results\n+else\n+TEST_RESULTS_DIRECTORY = ../../../t/test-results\n+endif\n+\n+# Shell quote;\n+SHELL_PATH_SQ = $(subst ','\\'',$(SHELL_PATH))\n+PERL_PATH_SQ = $(subst ','\\'',$(PERL_PATH))\n+TEST_RESULTS_DIRECTORY_SQ = $(subst ','\\'',$(TEST_RESULTS_DIRECTORY))\n+\n+T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))\n+\n+all: $(DEFAULT_TEST_TARGET)\n+\n+test: $(TEST_LINT)\n+\t$(MAKE) aggregate-results-and-cleanup\n+\n+prove: $(TEST_LINT)\n+\t@echo \"*** prove ***\"; GIT_CONFIG=.git/config $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)\n+\t$(MAKE) clean-except-prove-cache\n+\n+$(T):\n+\t@echo \"*** $@ ***\"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)\n+\n+clean-except-prove-cache:\n+\t$(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)'\n+\t$(RM) -r valgrind/bin\n+\n+clean: clean-except-prove-cache\n+\t$(RM) .prove\n+\n+test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax\n+\n+test-lint-duplicates:\n+\t@dups=`echo $(T) | tr ' ' '\\n' | sed 's/-.*//' | sort | uniq -d` && \\\n+\t\ttest -z \"$$dups\" || { \\\n+\t\techo >&2 \"duplicate test numbers:\" $$dups; exit 1; }\n+\n+test-lint-executable:\n+\t@bad=`for i in $(T); do test -x \"$$i\" || echo $$i; done` && \\\n+\t\ttest -z \"$$bad\" || { \\\n+\t\techo >&2 \"non-executable tests:\" $$bad; exit 1; }\n+\n+test-lint-shell-syntax:\n+\t@'$(PERL_PATH_SQ)' ../../../t/check-non-portable-shell.pl $(T)\n+\n+aggregate-results-and-cleanup: $(T)\n+\t$(MAKE) aggregate-results\n+\t$(MAKE) clean\n+\n+aggregate-results:\n+\tfor f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \\\n+\t\techo \"$$f\"; \\\n+\tdone | '$(SHELL_PATH_SQ)' ../../../t/aggregate-results.sh\n+\n+valgrind:\n+\t$(MAKE) GIT_TEST_OPTS=\"$(GIT_TEST_OPTS) --valgrind\"\n+\n+test-results:\n+\tmkdir -p test-results\n+\n+.PHONY: $(T) aggregate-results clean valgrind\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nnew file mode 100755\nindex 00000000000..16f2b72b126\n--- /dev/null\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -0,0 +1,17 @@\n+#!/bin/sh\n+\n+test_description='test the `scalar` command'\n+\n+TEST_DIRECTORY=$PWD/../../../t\n+export TEST_DIRECTORY\n+\n+# Make it work with --no-bin-wrappers\n+PATH=$PWD/..:$PATH\n+\n+. ../../../t/test-lib.sh\n+\n+test_expect_success 'scalar shows a usage' '\n+\ttest_expect_code 129 scalar -h\n+'\n+\n+test_done\n-- \ngitgitgadget\n\n"},{"id":"442982","messageId":"4439ab4de0bc3f48a6bdcf4b5165b16fad792ebd.1638538470.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v10.git.1638538470.gitgitgadget@gmail.com","subject":"[PATCH v10 05/15] scalar: 'register' sets recommended config and starts maintenance","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-03T13:34:19Z","receivedAt":"2021-12-03T13:34:44Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nLet's start implementing the `register` command. With this commit,\nrecommended settings are configured upon `scalar register`, and Git's\nbackground maintenance is started.\n\nThe recommended config settings may very well change in the future. For\nexample, once the built-in FSMonitor is available, we will want to\nenable it upon `scalar register`. For that reason, we explicitly support\nrunning `scalar register` in an already-registered enlistment.\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 249 ++++++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt |  18 ++-\n 2 files changed, 266 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 7cff29e0fcd..55a304442d4 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -5,11 +5,260 @@\n #include \"cache.h\"\n #include \"gettext.h\"\n #include \"parse-options.h\"\n+#include \"config.h\"\n+#include \"run-command.h\"\n+\n+/*\n+ * Remove the deepest subdirectory in the provided path string. Path must not\n+ * include a trailing path separator. Returns 1 if parent directory found,\n+ * otherwise 0.\n+ */\n+static int strbuf_parent_directory(struct strbuf *buf)\n+{\n+\tsize_t len = buf->len;\n+\tsize_t offset = offset_1st_component(buf->buf);\n+\tchar *path_sep = find_last_dir_sep(buf->buf + offset);\n+\tstrbuf_setlen(buf, path_sep ? path_sep - buf->buf : offset);\n+\n+\treturn buf->len < len;\n+}\n+\n+static void setup_enlistment_directory(int argc, const char **argv,\n+\t\t\t\t       const char * const *usagestr,\n+\t\t\t\t       const struct option *options,\n+\t\t\t\t       struct strbuf *enlistment_root)\n+{\n+\tstruct strbuf path = STRBUF_INIT;\n+\tchar *root;\n+\tint enlistment_found = 0;\n+\n+\tif (startup_info->have_repository)\n+\t\tBUG(\"gitdir already set up?!?\");\n+\n+\tif (argc > 1)\n+\t\tusage_with_options(usagestr, options);\n+\n+\t/* find the worktree, determine its corresponding root */\n+\tif (argc == 1)\n+\t\tstrbuf_add_absolute_path(&path, argv[0]);\n+\telse if (strbuf_getcwd(&path) < 0)\n+\t\tdie(_(\"need a working directory\"));\n+\n+\tstrbuf_trim_trailing_dir_sep(&path);\n+\tdo {\n+\t\tconst size_t len = path.len;\n+\n+\t\t/* check if currently in enlistment root with src/ workdir */\n+\t\tstrbuf_addstr(&path, \"/src\");\n+\t\tif (is_nonbare_repository_dir(&path)) {\n+\t\t\tif (enlistment_root)\n+\t\t\t\tstrbuf_add(enlistment_root, path.buf, len);\n+\n+\t\t\tenlistment_found = 1;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\t/* reset to original path */\n+\t\tstrbuf_setlen(&path, len);\n+\n+\t\t/* check if currently in workdir */\n+\t\tif (is_nonbare_repository_dir(&path)) {\n+\t\t\tif (enlistment_root) {\n+\t\t\t\t/*\n+\t\t\t\t * If the worktree's directory's name is `src`, the enlistment is the\n+\t\t\t\t * parent directory, otherwise it is identical to the worktree.\n+\t\t\t\t */\n+\t\t\t\troot = strip_path_suffix(path.buf, \"src\");\n+\t\t\t\tstrbuf_addstr(enlistment_root, root ? root : path.buf);\n+\t\t\t\tfree(root);\n+\t\t\t}\n+\n+\t\t\tenlistment_found = 1;\n+\t\t\tbreak;\n+\t\t}\n+\t} while (strbuf_parent_directory(&path));\n+\n+\tif (!enlistment_found)\n+\t\tdie(_(\"could not find enlistment root\"));\n+\n+\tif (chdir(path.buf) < 0)\n+\t\tdie_errno(_(\"could not switch to '%s'\"), path.buf);\n+\n+\tstrbuf_release(&path);\n+\tsetup_git_directory();\n+}\n+\n+static int run_git(const char *arg, ...)\n+{\n+\tstruct strvec argv = STRVEC_INIT;\n+\tva_list args;\n+\tconst char *p;\n+\tint res;\n+\n+\tva_start(args, arg);\n+\tstrvec_push(&argv, arg);\n+\twhile ((p = va_arg(args, const char *)))\n+\t\tstrvec_push(&argv, p);\n+\tva_end(args);\n+\n+\tres = run_command_v_opt(argv.v, RUN_GIT_CMD);\n+\n+\tstrvec_clear(&argv);\n+\treturn res;\n+}\n+\n+static int set_recommended_config(void)\n+{\n+\tstruct {\n+\t\tconst char *key;\n+\t\tconst char *value;\n+\t} config[] = {\n+\t\t{ \"am.keepCR\", \"true\" },\n+\t\t{ \"core.FSCache\", \"true\" },\n+\t\t{ \"core.multiPackIndex\", \"true\" },\n+\t\t{ \"core.preloadIndex\", \"true\" },\n+#ifndef WIN32\n+\t\t{ \"core.untrackedCache\", \"true\" },\n+#else\n+\t\t/*\n+\t\t * Unfortunately, Scalar's Functional Tests demonstrated\n+\t\t * that the untracked cache feature is unreliable on Windows\n+\t\t * (which is a bummer because that platform would benefit the\n+\t\t * most from it). For some reason, freshly created files seem\n+\t\t * not to update the directory's `lastModified` time\n+\t\t * immediately, but the untracked cache would need to rely on\n+\t\t * that.\n+\t\t *\n+\t\t * Therefore, with a sad heart, we disable this very useful\n+\t\t * feature on Windows.\n+\t\t */\n+\t\t{ \"core.untrackedCache\", \"false\" },\n+#endif\n+\t\t{ \"core.logAllRefUpdates\", \"true\" },\n+\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n+\t\t{ \"credential.validate\", \"false\" }, /* GCM4W-only */\n+\t\t{ \"gc.auto\", \"0\" },\n+\t\t{ \"gui.GCWarning\", \"false\" },\n+\t\t{ \"index.threads\", \"true\" },\n+\t\t{ \"index.version\", \"4\" },\n+\t\t{ \"merge.stat\", \"false\" },\n+\t\t{ \"merge.renames\", \"true\" },\n+\t\t{ \"pack.useBitmaps\", \"false\" },\n+\t\t{ \"pack.useSparse\", \"true\" },\n+\t\t{ \"receive.autoGC\", \"false\" },\n+\t\t{ \"reset.quiet\", \"true\" },\n+\t\t{ \"feature.manyFiles\", \"false\" },\n+\t\t{ \"feature.experimental\", \"false\" },\n+\t\t{ \"fetch.unpackLimit\", \"1\" },\n+\t\t{ \"fetch.writeCommitGraph\", \"false\" },\n+#ifdef WIN32\n+\t\t{ \"http.sslBackend\", \"schannel\" },\n+#endif\n+\t\t{ \"status.aheadBehind\", \"false\" },\n+\t\t{ \"commitGraph.generationVersion\", \"1\" },\n+\t\t{ \"core.autoCRLF\", \"false\" },\n+\t\t{ \"core.safeCRLF\", \"false\" },\n+\t\t{ \"fetch.showForcedUpdates\", \"false\" },\n+\t\t{ NULL, NULL },\n+\t};\n+\tint i;\n+\tchar *value;\n+\n+\tfor (i = 0; config[i].key; i++) {\n+\t\tif (git_config_get_string(config[i].key, &value)) {\n+\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"created\");\n+\t\t\tif (git_config_set_gently(config[i].key,\n+\t\t\t\t\t\t  config[i].value) < 0)\n+\t\t\t\treturn error(_(\"could not configure %s=%s\"),\n+\t\t\t\t\t     config[i].key, config[i].value);\n+\t\t} else {\n+\t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"exists\");\n+\t\t\tfree(value);\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * The `log.excludeDecoration` setting is special because it allows\n+\t * for multiple values.\n+\t */\n+\tif (git_config_get_string(\"log.excludeDecoration\", &value)) {\n+\t\ttrace2_data_string(\"scalar\", the_repository,\n+\t\t\t\t   \"log.excludeDecoration\", \"created\");\n+\t\tif (git_config_set_multivar_gently(\"log.excludeDecoration\",\n+\t\t\t\t\t\t   \"refs/prefetch/*\",\n+\t\t\t\t\t\t   CONFIG_REGEX_NONE, 0))\n+\t\t\treturn error(_(\"could not configure \"\n+\t\t\t\t       \"log.excludeDecoration\"));\n+\t} else {\n+\t\ttrace2_data_string(\"scalar\", the_repository,\n+\t\t\t\t   \"log.excludeDecoration\", \"exists\");\n+\t\tfree(value);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int start_maintenance(void)\n+{\n+\treturn run_git(\"maintenance\", \"start\", NULL);\n+}\n+\n+static int add_enlistment(void)\n+{\n+\tint res;\n+\n+\tif (!the_repository->worktree)\n+\t\tdie(_(\"Scalar enlistments require a worktree\"));\n+\n+\tres = run_git(\"config\", \"--global\", \"--get\", \"--fixed-value\",\n+\t\t      \"scalar.repo\", the_repository->worktree, NULL);\n+\n+\t/*\n+\t * If the setting is already there, then do nothing.\n+\t */\n+\tif (!res)\n+\t\treturn 0;\n+\n+\treturn run_git(\"config\", \"--global\", \"--add\",\n+\t\t       \"scalar.repo\", the_repository->worktree, NULL);\n+}\n+\n+static int register_dir(void)\n+{\n+\tint res = add_enlistment();\n+\n+\tif (!res)\n+\t\tres = set_recommended_config();\n+\n+\tif (!res)\n+\t\tres = start_maintenance();\n+\n+\treturn res;\n+}\n+\n+static int cmd_register(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar register [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn register_dir();\n+}\n \n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"register\", cmd_register },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 5f7131861a5..568987064b2 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,7 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n-scalar <command> [<options>]\n+scalar register [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -29,6 +29,22 @@ will be identical to the worktree.\n The `scalar` command implements various subcommands, and different options\n depending on the subcommand.\n \n+COMMANDS\n+--------\n+\n+Register\n+~~~~~~~~\n+\n+register [<enlistment>]::\n+\tAdds the enlistment's repository to the list of registered repositories\n+\tand starts background maintenance. If `<enlistment>` is not provided,\n+\tthen the enlistment associated with the current working directory is\n+\tregistered.\n++\n+Note: when this subcommand is called in a worktree that is called `src/`, its\n+parent directory is considered to be the Scalar enlistment. If the worktree is\n+_not_ called `src/`, it itself will be considered to be the Scalar enlistment.\n+\n SEE ALSO\n --------\n linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"442983","messageId":"376056066a09f19eae4e7bfade076c210a9944a6.1638538470.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v10.git.1638538470.gitgitgadget@gmail.com","subject":"[PATCH v10 06/15] scalar: 'unregister' stops background maintenance","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-03T13:34:20Z","receivedAt":"2021-12-03T13:34:49Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nJust like `scalar register` starts the scheduled background maintenance,\n`scalar unregister` stops it. Note that we use `git maintenance start`\nin `scalar register`, but we do not use `git maintenance stop` in\n`scalar unregister`: this would stop maintenance for _all_ repositories,\nnot just for the one we want to unregister.\n\nThe `unregister` command also removes the corresponding entry from the\n`[scalar]` section in the global Git config.\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 50 ++++++++++++++++++++++++++++++++-------\n contrib/scalar/scalar.txt |  8 +++++++\n 2 files changed, 50 insertions(+), 8 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 55a304442d4..9ab9dffe3ac 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -198,12 +198,12 @@ static int set_recommended_config(void)\n \treturn 0;\n }\n \n-static int start_maintenance(void)\n+static int toggle_maintenance(int enable)\n {\n-\treturn run_git(\"maintenance\", \"start\", NULL);\n+\treturn run_git(\"maintenance\", enable ? \"start\" : \"unregister\", NULL);\n }\n \n-static int add_enlistment(void)\n+static int add_or_remove_enlistment(int add)\n {\n \tint res;\n \n@@ -214,24 +214,39 @@ static int add_enlistment(void)\n \t\t      \"scalar.repo\", the_repository->worktree, NULL);\n \n \t/*\n-\t * If the setting is already there, then do nothing.\n+\t * If we want to add and the setting is already there, then do nothing.\n+\t * If we want to remove and the setting is not there, then do nothing.\n \t */\n-\tif (!res)\n+\tif ((add && !res) || (!add && res))\n \t\treturn 0;\n \n-\treturn run_git(\"config\", \"--global\", \"--add\",\n+\treturn run_git(\"config\", \"--global\", add ? \"--add\" : \"--unset\",\n+\t\t       add ? \"--no-fixed-value\" : \"--fixed-value\",\n \t\t       \"scalar.repo\", the_repository->worktree, NULL);\n }\n \n static int register_dir(void)\n {\n-\tint res = add_enlistment();\n+\tint res = add_or_remove_enlistment(1);\n \n \tif (!res)\n \t\tres = set_recommended_config();\n \n \tif (!res)\n-\t\tres = start_maintenance();\n+\t\tres = toggle_maintenance(1);\n+\n+\treturn res;\n+}\n+\n+static int unregister_dir(void)\n+{\n+\tint res = 0;\n+\n+\tif (toggle_maintenance(0) < 0)\n+\t\tres = -1;\n+\n+\tif (add_or_remove_enlistment(0) < 0)\n+\t\tres = -1;\n \n \treturn res;\n }\n@@ -254,11 +269,30 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_unregister(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar unregister [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn unregister_dir();\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n \t{ \"register\", cmd_register },\n+\t{ \"unregister\", cmd_unregister },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 568987064b2..d9a79984492 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -9,6 +9,7 @@ SYNOPSIS\n --------\n [verse]\n scalar register [<enlistment>]\n+scalar unregister [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -45,6 +46,13 @@ Note: when this subcommand is called in a worktree that is called `src/`, its\n parent directory is considered to be the Scalar enlistment. If the worktree is\n _not_ called `src/`, it itself will be considered to be the Scalar enlistment.\n \n+Unregister\n+~~~~~~~~~~\n+\n+unregister [<enlistment>]::\n+\tRemove the specified repository from the list of repositories\n+\tregistered with Scalar and stop the scheduled background maintenance.\n+\n SEE ALSO\n --------\n linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"442984","messageId":"60659c4719678891dc98207e666d1da548c2259d.1638538470.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v10.git.1638538470.gitgitgadget@gmail.com","subject":"[PATCH v10 09/15] scalar: implement the `clone` subcommand","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-03T13:34:23Z","receivedAt":"2021-12-03T13:34:51Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis implements Scalar's opinionated `clone` command: it tries to use a\npartial clone and sets up a sparse checkout by default. In contrast to\n`git clone`, `scalar clone` sets up the worktree in the `src/`\nsubdirectory, to encourage a separation between the source files and the\nbuild output (which helps Git tremendously because it avoids untracked\nfiles that have to be specifically ignored when refreshing the index).\n\nAlso, it registers the repository for regular, scheduled maintenance,\nand configures a flurry of configuration settings based on the\nexperience and experiments of the Microsoft Windows and the Microsoft\nOffice development teams.\n\nNote: since the `scalar clone` command is by far the most commonly\ncalled `scalar` subcommand, we document it at the top of the manual\npage.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 201 +++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        |  32 ++++-\n contrib/scalar/t/t9099-scalar.sh |  32 +++++\n 3 files changed, 262 insertions(+), 3 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 65da885c5ac..60a9466421b 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -7,6 +7,7 @@\n #include \"parse-options.h\"\n #include \"config.h\"\n #include \"run-command.h\"\n+#include \"refs.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -251,6 +252,205 @@ static int unregister_dir(void)\n \treturn res;\n }\n \n+/* printf-style interface, expects `<key>=<value>` argument */\n+static int set_config(const char *fmt, ...)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tchar *value;\n+\tint res;\n+\tva_list args;\n+\n+\tva_start(args, fmt);\n+\tstrbuf_vaddf(&buf, fmt, args);\n+\tva_end(args);\n+\n+\tvalue = strchr(buf.buf, '=');\n+\tif (value)\n+\t\t*(value++) = '\\0';\n+\tres = git_config_set_gently(buf.buf, value);\n+\tstrbuf_release(&buf);\n+\n+\treturn res;\n+}\n+\n+static char *remote_default_branch(const char *url)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tstruct strbuf out = STRBUF_INIT;\n+\n+\tcp.git_cmd = 1;\n+\tstrvec_pushl(&cp.args, \"ls-remote\", \"--symref\", url, \"HEAD\", NULL);\n+\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n+\t\tconst char *line = out.buf;\n+\n+\t\twhile (*line) {\n+\t\t\tconst char *eol = strchrnul(line, '\\n'), *p;\n+\t\t\tsize_t len = eol - line;\n+\t\t\tchar *branch;\n+\n+\t\t\tif (!skip_prefix(line, \"ref: \", &p) ||\n+\t\t\t    !strip_suffix_mem(line, &len, \"\\tHEAD\")) {\n+\t\t\t\tline = eol + (*eol == '\\n');\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\teol = line + len;\n+\t\t\tif (skip_prefix(p, \"refs/heads/\", &p)) {\n+\t\t\t\tbranch = xstrndup(p, eol - p);\n+\t\t\t\tstrbuf_release(&out);\n+\t\t\t\treturn branch;\n+\t\t\t}\n+\n+\t\t\terror(_(\"remote HEAD is not a branch: '%.*s'\"),\n+\t\t\t      (int)(eol - p), p);\n+\t\t\tstrbuf_release(&out);\n+\t\t\treturn NULL;\n+\t\t}\n+\t}\n+\twarning(_(\"failed to get default branch name from remote; \"\n+\t\t  \"using local default\"));\n+\tstrbuf_reset(&out);\n+\n+\tchild_process_init(&cp);\n+\tcp.git_cmd = 1;\n+\tstrvec_pushl(&cp.args, \"symbolic-ref\", \"--short\", \"HEAD\", NULL);\n+\tif (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {\n+\t\tstrbuf_trim(&out);\n+\t\treturn strbuf_detach(&out, NULL);\n+\t}\n+\n+\tstrbuf_release(&out);\n+\terror(_(\"failed to get default branch name\"));\n+\treturn NULL;\n+}\n+\n+static int cmd_clone(int argc, const char **argv)\n+{\n+\tconst char *branch = NULL;\n+\tint full_clone = 0;\n+\tstruct option clone_options[] = {\n+\t\tOPT_STRING('b', \"branch\", &branch, N_(\"<branch>\"),\n+\t\t\t   N_(\"branch to checkout after clone\")),\n+\t\tOPT_BOOL(0, \"full-clone\", &full_clone,\n+\t\t\t N_(\"when cloning, create full working directory\")),\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const clone_usage[] = {\n+\t\tN_(\"scalar clone [<options>] [--] <repo> [<dir>]\"),\n+\t\tNULL\n+\t};\n+\tconst char *url;\n+\tchar *enlistment = NULL, *dir = NULL;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint res;\n+\n+\targc = parse_options(argc, argv, NULL, clone_options, clone_usage, 0);\n+\n+\tif (argc == 2) {\n+\t\turl = argv[0];\n+\t\tenlistment = xstrdup(argv[1]);\n+\t} else if (argc == 1) {\n+\t\turl = argv[0];\n+\n+\t\tstrbuf_addstr(&buf, url);\n+\t\t/* Strip trailing slashes, if any */\n+\t\twhile (buf.len > 0 && is_dir_sep(buf.buf[buf.len - 1]))\n+\t\t\tstrbuf_setlen(&buf, buf.len - 1);\n+\t\t/* Strip suffix `.git`, if any */\n+\t\tstrbuf_strip_suffix(&buf, \".git\");\n+\n+\t\tenlistment = find_last_dir_sep(buf.buf);\n+\t\tif (!enlistment) {\n+\t\t\tdie(_(\"cannot deduce worktree name from '%s'\"), url);\n+\t\t}\n+\t\tenlistment = xstrdup(enlistment + 1);\n+\t} else {\n+\t\tusage_msg_opt(_(\"You must specify a repository to clone.\"),\n+\t\t\t      clone_usage, clone_options);\n+\t}\n+\n+\tif (is_directory(enlistment))\n+\t\tdie(_(\"directory '%s' exists already\"), enlistment);\n+\n+\tdir = xstrfmt(\"%s/src\", enlistment);\n+\n+\tstrbuf_reset(&buf);\n+\tif (branch)\n+\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", branch);\n+\telse {\n+\t\tchar *b = repo_default_branch_name(the_repository, 1);\n+\t\tstrbuf_addf(&buf, \"init.defaultBranch=%s\", b);\n+\t\tfree(b);\n+\t}\n+\n+\tif ((res = run_git(\"-c\", buf.buf, \"init\", \"--\", dir, NULL)))\n+\t\tgoto cleanup;\n+\n+\tif (chdir(dir) < 0) {\n+\t\tres = error_errno(_(\"could not switch to '%s'\"), dir);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tsetup_git_directory();\n+\n+\t/* common-main already logs `argv` */\n+\ttrace2_def_repo(the_repository);\n+\n+\tif (!branch && !(branch = remote_default_branch(url))) {\n+\t\tres = error(_(\"failed to get default branch for '%s'\"), url);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tif (set_config(\"remote.origin.url=%s\", url) ||\n+\t    set_config(\"remote.origin.fetch=\"\n+\t\t       \"+refs/heads/*:refs/remotes/origin/*\") ||\n+\t    set_config(\"remote.origin.promisor=true\") ||\n+\t    set_config(\"remote.origin.partialCloneFilter=blob:none\")) {\n+\t\tres = error(_(\"could not configure remote in '%s'\"), dir);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tif (!full_clone &&\n+\t    (res = run_git(\"sparse-checkout\", \"init\", \"--cone\", NULL)))\n+\t\tgoto cleanup;\n+\n+\tif (set_recommended_config())\n+\t\treturn error(_(\"could not configure '%s'\"), dir);\n+\n+\tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL))) {\n+\t\twarning(_(\"partial clone failed; attempting full clone\"));\n+\n+\t\tif (set_config(\"remote.origin.promisor\") ||\n+\t\t    set_config(\"remote.origin.partialCloneFilter\")) {\n+\t\t\tres = error(_(\"could not configure for full clone\"));\n+\t\t\tgoto cleanup;\n+\t\t}\n+\n+\t\tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL)))\n+\t\t\tgoto cleanup;\n+\t}\n+\n+\tif ((res = set_config(\"branch.%s.remote=origin\", branch)))\n+\t\tgoto cleanup;\n+\tif ((res = set_config(\"branch.%s.merge=refs/heads/%s\",\n+\t\t\t      branch, branch)))\n+\t\tgoto cleanup;\n+\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addf(&buf, \"origin/%s\", branch);\n+\tres = run_git(\"checkout\", \"-f\", \"-t\", buf.buf, NULL);\n+\tif (res)\n+\t\tgoto cleanup;\n+\n+\tres = register_dir();\n+\n+cleanup:\n+\tfree(enlistment);\n+\tfree(dir);\n+\tstrbuf_release(&buf);\n+\treturn res;\n+}\n+\n static int cmd_list(int argc, const char **argv)\n {\n \tif (argc != 1)\n@@ -347,6 +547,7 @@ static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"clone\", cmd_clone },\n \t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex f93e3d00efd..e8730967f16 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,6 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n+scalar clone [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n@@ -29,12 +30,37 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand. With the exception of `list`, all subcommands\n-expect to be run in an enlistment.\n+depending on the subcommand. With the exception of `clone` and `list`, all\n+subcommands expect to be run in an enlistment.\n \n COMMANDS\n --------\n \n+Clone\n+~~~~~\n+\n+clone [<options>] <url> [<enlistment>]::\n+\tClones the specified repository, similar to linkgit:git-clone[1]. By\n+\tdefault, only commit and tree objects are cloned. Once finished, the\n+\tworktree is located at `<enlistment>/src`.\n++\n+The sparse-checkout feature is enabled (except when run with `--full-clone`)\n+and the only files present are those in the top-level directory. Use\n+`git sparse-checkout set` to expand the set of directories you want to see,\n+or `git sparse-checkout disable` to expand to all files (see\n+linkgit:git-sparse-checkout[1] for more details). You can explore the\n+subdirectories outside your sparse-checkout by using `git ls-tree\n+HEAD[:<directory>]`.\n+\n+-b <name>::\n+--branch <name>::\n+\tInstead of checking out the branch pointed to by the cloned\n+\trepository's HEAD, check out the `<name>` branch instead.\n+\n+--[no-]full-clone::\n+\tA sparse-checkout is initialized by default. This behavior can be\n+\tturned off via `--full-clone`.\n+\n List\n ~~~~\n \n@@ -64,7 +90,7 @@ unregister [<enlistment>]::\n \n SEE ALSO\n --------\n-linkgit:git-maintenance[1].\n+linkgit:git-clone[1], linkgit:git-maintenance[1].\n \n Scalar\n ---\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex ef0e8d680d5..984d69e8f75 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -10,6 +10,9 @@ PATH=$PWD/..:$PATH\n \n . ../../../t/test-lib.sh\n \n+GIT_TEST_MAINT_SCHEDULER=\"crontab:test-tool crontab ../cron.txt,launchctl:true,schtasks:true\"\n+export GIT_TEST_MAINT_SCHEDULER\n+\n test_expect_success 'scalar shows a usage' '\n \ttest_expect_code 129 scalar -h\n '\n@@ -29,4 +32,33 @@ test_expect_success 'scalar unregister' '\n \t! grep -F \"$(pwd)/vanish/src\" scalar.repos\n '\n \n+test_expect_success 'set up repository to clone' '\n+\ttest_commit first &&\n+\ttest_commit second &&\n+\ttest_commit third &&\n+\tgit switch -c parallel first &&\n+\tmkdir -p 1/2 &&\n+\ttest_commit 1/2/3 &&\n+\tgit config uploadPack.allowFilter true &&\n+\tgit config uploadPack.allowAnySHA1InWant true\n+'\n+\n+test_expect_success 'scalar clone' '\n+\tsecond=$(git rev-parse --verify second:second.t) &&\n+\tscalar clone \"file://$(pwd)\" cloned &&\n+\t(\n+\t\tcd cloned/src &&\n+\n+\t\tgit config --get --global --fixed-value maintenance.repo \\\n+\t\t\t\"$(pwd)\" &&\n+\n+\t\ttest_path_is_missing 1/2 &&\n+\t\ttest_must_fail git rev-list --missing=print $second &&\n+\t\tgit rev-list $second &&\n+\t\tgit cat-file blob $second >actual &&\n+\t\techo \"second\" >expect &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"442985","messageId":"3f8b0abd7d67da77f7b8013814269549bd3ed878.1638538470.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v10.git.1638538470.gitgitgadget@gmail.com","subject":"[PATCH v10 08/15] scalar: implement 'scalar list'","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-03T13:34:22Z","receivedAt":"2021-12-03T13:34:54Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe produced list simply consists of those repositories registered under\nthe multi-valued `scalar.repo` config setting in the user's Git config.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 11 +++++++++++\n contrib/scalar/scalar.txt | 11 ++++++++++-\n 2 files changed, 21 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex ec783e72ef3..65da885c5ac 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -251,6 +251,16 @@ static int unregister_dir(void)\n \treturn res;\n }\n \n+static int cmd_list(int argc, const char **argv)\n+{\n+\tif (argc != 1)\n+\t\tdie(_(\"`scalar list` does not take arguments\"));\n+\n+\tif (run_git(\"config\", \"--global\", \"--get-all\", \"scalar.repo\", NULL) < 0)\n+\t\treturn -1;\n+\treturn 0;\n+}\n+\n static int cmd_register(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -337,6 +347,7 @@ static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n } builtins[] = {\n+\t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n \t{ NULL, NULL},\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex d9a79984492..f93e3d00efd 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,6 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n+scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n \n@@ -28,11 +29,19 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand.\n+depending on the subcommand. With the exception of `list`, all subcommands\n+expect to be run in an enlistment.\n \n COMMANDS\n --------\n \n+List\n+~~~~\n+\n+list::\n+\tList enlistments that are currently registered by Scalar. This\n+\tsubcommand does not need to be run inside an enlistment.\n+\n Register\n ~~~~~~~~\n \n-- \ngitgitgadget\n\n"},{"id":"442986","messageId":"c865e89beb31fba6e1fe17be76f2a7bb0098cb6e.1638538470.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v10.git.1638538470.gitgitgadget@gmail.com","subject":"[PATCH v10 07/15] scalar: let 'unregister' handle a deleted enlistment directory gracefully","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-03T13:34:21Z","receivedAt":"2021-12-03T13:34:55Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nWhen a user deleted an enlistment manually, let's be generous and\n_still_ unregister it.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 46 ++++++++++++++++++++++++++++++++\n contrib/scalar/t/t9099-scalar.sh | 15 +++++++++++\n 2 files changed, 61 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 9ab9dffe3ac..ec783e72ef3 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -269,6 +269,24 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int remove_deleted_enlistment(struct strbuf *path)\n+{\n+\tint res = 0;\n+\tstrbuf_realpath_forgiving(path, path->buf, 1);\n+\n+\tif (run_git(\"config\", \"--global\",\n+\t\t    \"--unset\", \"--fixed-value\",\n+\t\t    \"scalar.repo\", path->buf, NULL) < 0)\n+\t\tres = -1;\n+\n+\tif (run_git(\"config\", \"--global\",\n+\t\t    \"--unset\", \"--fixed-value\",\n+\t\t    \"maintenance.repo\", path->buf, NULL) < 0)\n+\t\tres = -1;\n+\n+\treturn res;\n+}\n+\n static int cmd_unregister(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -282,6 +300,34 @@ static int cmd_unregister(int argc, const char **argv)\n \targc = parse_options(argc, argv, NULL, options,\n \t\t\t     usage, 0);\n \n+\t/*\n+\t * Be forgiving when the enlistment or worktree does not even exist any\n+\t * longer; This can be the case if a user deleted the worktree by\n+\t * mistake and _still_ wants to unregister the thing.\n+\t */\n+\tif (argc == 1) {\n+\t\tstruct strbuf src_path = STRBUF_INIT, workdir_path = STRBUF_INIT;\n+\n+\t\tstrbuf_addf(&src_path, \"%s/src/.git\", argv[0]);\n+\t\tstrbuf_addf(&workdir_path, \"%s/.git\", argv[0]);\n+\t\tif (!is_directory(src_path.buf) && !is_directory(workdir_path.buf)) {\n+\t\t\t/* remove possible matching registrations */\n+\t\t\tint res = -1;\n+\n+\t\t\tstrbuf_strip_suffix(&src_path, \"/.git\");\n+\t\t\tres = remove_deleted_enlistment(&src_path) && res;\n+\n+\t\t\tstrbuf_strip_suffix(&workdir_path, \"/.git\");\n+\t\t\tres = remove_deleted_enlistment(&workdir_path) && res;\n+\n+\t\t\tstrbuf_release(&src_path);\n+\t\t\tstrbuf_release(&workdir_path);\n+\t\t\treturn res;\n+\t\t}\n+\t\tstrbuf_release(&src_path);\n+\t\tstrbuf_release(&workdir_path);\n+\t}\n+\n \tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n \n \treturn unregister_dir();\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 16f2b72b126..ef0e8d680d5 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -14,4 +14,19 @@ test_expect_success 'scalar shows a usage' '\n \ttest_expect_code 129 scalar -h\n '\n \n+test_expect_success 'scalar unregister' '\n+\tgit init vanish/src &&\n+\tscalar register vanish/src &&\n+\tgit config --get --global --fixed-value \\\n+\t\tmaintenance.repo \"$(pwd)/vanish/src\" &&\n+\tscalar list >scalar.repos &&\n+\tgrep -F \"$(pwd)/vanish/src\" scalar.repos &&\n+\trm -rf vanish/src/.git &&\n+\tscalar unregister vanish &&\n+\ttest_must_fail git config --get --global --fixed-value \\\n+\t\tmaintenance.repo \"$(pwd)/vanish/src\" &&\n+\tscalar list >scalar.repos &&\n+\t! grep -F \"$(pwd)/vanish/src\" scalar.repos\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"442987","messageId":"45aca840764d1e4f99b5dc1e1a0c5e8b1532da03.1638538470.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v10.git.1638538470.gitgitgadget@gmail.com","subject":"[PATCH v10 10/15] scalar: teach 'clone' to support the --single-branch option","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-03T13:34:24Z","receivedAt":"2021-12-03T13:34:55Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nJust like `git clone`, the `scalar clone` command now also offers to\nrestrict the clone to a single branch.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          |  9 +++++++--\n contrib/scalar/scalar.txt        | 12 +++++++++++-\n contrib/scalar/t/t9099-scalar.sh |  6 +++++-\n 3 files changed, 23 insertions(+), 4 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 60a9466421b..61b66e48aa8 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -327,12 +327,15 @@ static char *remote_default_branch(const char *url)\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n-\tint full_clone = 0;\n+\tint full_clone = 0, single_branch = 0;\n \tstruct option clone_options[] = {\n \t\tOPT_STRING('b', \"branch\", &branch, N_(\"<branch>\"),\n \t\t\t   N_(\"branch to checkout after clone\")),\n \t\tOPT_BOOL(0, \"full-clone\", &full_clone,\n \t\t\t N_(\"when cloning, create full working directory\")),\n+\t\tOPT_BOOL(0, \"single-branch\", &single_branch,\n+\t\t\t N_(\"only download metadata for the branch that will \"\n+\t\t\t    \"be checked out\")),\n \t\tOPT_END(),\n \t};\n \tconst char * const clone_usage[] = {\n@@ -403,7 +406,9 @@ static int cmd_clone(int argc, const char **argv)\n \n \tif (set_config(\"remote.origin.url=%s\", url) ||\n \t    set_config(\"remote.origin.fetch=\"\n-\t\t       \"+refs/heads/*:refs/remotes/origin/*\") ||\n+\t\t       \"+refs/heads/%s:refs/remotes/origin/%s\",\n+\t\t       single_branch ? branch : \"*\",\n+\t\t       single_branch ? branch : \"*\") ||\n \t    set_config(\"remote.origin.promisor=true\") ||\n \t    set_config(\"remote.origin.partialCloneFilter=blob:none\")) {\n \t\tres = error(_(\"could not configure remote in '%s'\"), dir);\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex e8730967f16..56f744a4aa9 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -8,7 +8,7 @@ scalar - an opinionated repository management tool\n SYNOPSIS\n --------\n [verse]\n-scalar clone [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n+scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<enlistment>]\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n@@ -57,6 +57,16 @@ HEAD[:<directory>]`.\n \tInstead of checking out the branch pointed to by the cloned\n \trepository's HEAD, check out the `<name>` branch instead.\n \n+--[no-]single-branch::\n+\tClone only the history leading to the tip of a single branch, either\n+\tspecified by the `--branch` option or the primary branch remote's\n+\t`HEAD` points at.\n++\n+Further fetches into the resulting repository will only update the\n+remote-tracking branch for the branch this option was used for the initial\n+cloning. If the HEAD at the remote did not point at any branch when\n+`--single-branch` clone was made, no remote-tracking branch is created.\n+\n --[no-]full-clone::\n \tA sparse-checkout is initialized by default. This behavior can be\n \tturned off via `--full-clone`.\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 984d69e8f75..f60e086d6f9 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -45,13 +45,17 @@ test_expect_success 'set up repository to clone' '\n \n test_expect_success 'scalar clone' '\n \tsecond=$(git rev-parse --verify second:second.t) &&\n-\tscalar clone \"file://$(pwd)\" cloned &&\n+\tscalar clone \"file://$(pwd)\" cloned --single-branch &&\n \t(\n \t\tcd cloned/src &&\n \n \t\tgit config --get --global --fixed-value maintenance.repo \\\n \t\t\t\"$(pwd)\" &&\n \n+\t\tgit for-each-ref --format=\"%(refname)\" refs/remotes/origin/ >actual &&\n+\t\techo \"refs/remotes/origin/parallel\" >expect &&\n+\t\ttest_cmp expect actual &&\n+\n \t\ttest_path_is_missing 1/2 &&\n \t\ttest_must_fail git rev-list --missing=print $second &&\n \t\tgit rev-list $second &&\n-- \ngitgitgadget\n\n"},{"id":"442988","messageId":"15e649a173429732974c5c5d795565f360984095.1638538470.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v10.git.1638538470.gitgitgadget@gmail.com","subject":"[PATCH v10 11/15] scalar: implement the `run` command","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-03T13:34:25Z","receivedAt":"2021-12-03T13:34:57Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nNote: this subcommand is provided primarily for backwards-compatibility,\nfor existing Scalar uses. It is mostly just a shim for `git\nmaintenance`, mapping task names from the way Scalar called them to the\nway Git calls them.\n\nThe reason why those names differ? The background maintenance was first\nimplemented in Scalar, and when it was contributed as a patch series\nimplementing the `git maintenance` command, reviewers suggested better\nnames, those suggestions were accepted before the patches were\nintegrated into core Git.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c   | 64 +++++++++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt | 19 ++++++++++++\n 2 files changed, 83 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 61b66e48aa8..fa900e4373f 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -484,6 +484,69 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_run(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tstruct {\n+\t\tconst char *arg, *task;\n+\t} tasks[] = {\n+\t\t{ \"config\", NULL },\n+\t\t{ \"commit-graph\", \"commit-graph\" },\n+\t\t{ \"fetch\", \"prefetch\" },\n+\t\t{ \"loose-objects\", \"loose-objects\" },\n+\t\t{ \"pack-files\", \"incremental-repack\" },\n+\t\t{ NULL, NULL }\n+\t};\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tconst char *usagestr[] = { NULL, NULL };\n+\tint i;\n+\n+\tstrbuf_addstr(&buf, N_(\"scalar run <task> [<enlistment>]\\nTasks:\\n\"));\n+\tfor (i = 0; tasks[i].arg; i++)\n+\t\tstrbuf_addf(&buf, \"\\t%s\\n\", tasks[i].arg);\n+\tusagestr[0] = buf.buf;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usagestr, 0);\n+\n+\tif (!argc)\n+\t\tusage_with_options(usagestr, options);\n+\n+\tif (!strcmp(\"all\", argv[0])) {\n+\t\ti = -1;\n+\t} else {\n+\t\tfor (i = 0; tasks[i].arg && strcmp(tasks[i].arg, argv[0]); i++)\n+\t\t\t; /* keep looking for the task */\n+\n+\t\tif (i > 0 && !tasks[i].arg) {\n+\t\t\terror(_(\"no such task: '%s'\"), argv[0]);\n+\t\t\tusage_with_options(usagestr, options);\n+\t\t}\n+\t}\n+\n+\targc--;\n+\targv++;\n+\tsetup_enlistment_directory(argc, argv, usagestr, options, NULL);\n+\tstrbuf_release(&buf);\n+\n+\tif (i == 0)\n+\t\treturn register_dir();\n+\n+\tif (i > 0)\n+\t\treturn run_git(\"maintenance\", \"run\",\n+\t\t\t       \"--task\", tasks[i].task, NULL);\n+\n+\tif (register_dir())\n+\t\treturn -1;\n+\tfor (i = 1; tasks[i].arg; i++)\n+\t\tif (run_git(\"maintenance\", \"run\",\n+\t\t\t    \"--task\", tasks[i].task, NULL))\n+\t\t\treturn -1;\n+\treturn 0;\n+}\n+\n static int remove_deleted_enlistment(struct strbuf *path)\n {\n \tint res = 0;\n@@ -556,6 +619,7 @@ static struct {\n \t{ \"list\", cmd_list },\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n+\t{ \"run\", cmd_run },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 56f744a4aa9..39143b08324 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -12,6 +12,7 @@ scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<e\n scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n+scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n \n DESCRIPTION\n -----------\n@@ -98,6 +99,24 @@ unregister [<enlistment>]::\n \tRemove the specified repository from the list of repositories\n \tregistered with Scalar and stop the scheduled background maintenance.\n \n+Run\n+~~~\n+\n+scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]::\n+\tRun the given maintenance task (or all tasks, if `all` was specified).\n+\tExcept for `all` and `config`, this subcommand simply hands off to\n+\tlinkgit:git-maintenance[1] (mapping `fetch` to `prefetch` and\n+\t`pack-files` to `incremental-repack`).\n++\n+These tasks are run automatically as part of the scheduled maintenance,\n+as soon as the repository is registered with Scalar. It should therefore\n+not be necessary to run this subcommand manually.\n++\n+The `config` task is specific to Scalar and configures all those\n+opinionated default settings that make Git work more efficiently with\n+large repositories. As this task is run as part of `scalar clone`\n+automatically, explicit invocations of this task are rarely needed.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\n-- \ngitgitgadget\n\n"},{"id":"442989","messageId":"2a3fb40bd9a4a3aa18a8265a50d4c52a06705a9f.1638538470.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v10.git.1638538470.gitgitgadget@gmail.com","subject":"[PATCH v10 12/15] scalar: allow reconfiguring an existing enlistment","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-03T13:34:26Z","receivedAt":"2021-12-03T13:34:58Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis comes in handy during Scalar upgrades, or when config settings were\nmessed up by mistake.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 79 +++++++++++++++++++++-----------\n contrib/scalar/scalar.txt        |  8 ++++\n contrib/scalar/t/t9099-scalar.sh |  8 ++++\n 3 files changed, 67 insertions(+), 28 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex fa900e4373f..d7306b43cae 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -108,18 +108,20 @@ static int run_git(const char *arg, ...)\n \treturn res;\n }\n \n-static int set_recommended_config(void)\n+static int set_recommended_config(int reconfigure)\n {\n \tstruct {\n \t\tconst char *key;\n \t\tconst char *value;\n+\t\tint overwrite_on_reconfigure;\n \t} config[] = {\n-\t\t{ \"am.keepCR\", \"true\" },\n-\t\t{ \"core.FSCache\", \"true\" },\n-\t\t{ \"core.multiPackIndex\", \"true\" },\n-\t\t{ \"core.preloadIndex\", \"true\" },\n+\t\t/* Required */\n+\t\t{ \"am.keepCR\", \"true\", 1 },\n+\t\t{ \"core.FSCache\", \"true\", 1 },\n+\t\t{ \"core.multiPackIndex\", \"true\", 1 },\n+\t\t{ \"core.preloadIndex\", \"true\", 1 },\n #ifndef WIN32\n-\t\t{ \"core.untrackedCache\", \"true\" },\n+\t\t{ \"core.untrackedCache\", \"true\", 1 },\n #else\n \t\t/*\n \t\t * Unfortunately, Scalar's Functional Tests demonstrated\n@@ -133,28 +135,29 @@ static int set_recommended_config(void)\n \t\t * Therefore, with a sad heart, we disable this very useful\n \t\t * feature on Windows.\n \t\t */\n-\t\t{ \"core.untrackedCache\", \"false\" },\n+\t\t{ \"core.untrackedCache\", \"false\", 1 },\n #endif\n-\t\t{ \"core.logAllRefUpdates\", \"true\" },\n-\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\" },\n-\t\t{ \"credential.validate\", \"false\" }, /* GCM4W-only */\n-\t\t{ \"gc.auto\", \"0\" },\n-\t\t{ \"gui.GCWarning\", \"false\" },\n-\t\t{ \"index.threads\", \"true\" },\n-\t\t{ \"index.version\", \"4\" },\n-\t\t{ \"merge.stat\", \"false\" },\n-\t\t{ \"merge.renames\", \"true\" },\n-\t\t{ \"pack.useBitmaps\", \"false\" },\n-\t\t{ \"pack.useSparse\", \"true\" },\n-\t\t{ \"receive.autoGC\", \"false\" },\n-\t\t{ \"reset.quiet\", \"true\" },\n-\t\t{ \"feature.manyFiles\", \"false\" },\n-\t\t{ \"feature.experimental\", \"false\" },\n-\t\t{ \"fetch.unpackLimit\", \"1\" },\n-\t\t{ \"fetch.writeCommitGraph\", \"false\" },\n+\t\t{ \"core.logAllRefUpdates\", \"true\", 1 },\n+\t\t{ \"credential.https://dev.azure.com.useHttpPath\", \"true\", 1 },\n+\t\t{ \"credential.validate\", \"false\", 1 }, /* GCM4W-only */\n+\t\t{ \"gc.auto\", \"0\", 1 },\n+\t\t{ \"gui.GCWarning\", \"false\", 1 },\n+\t\t{ \"index.threads\", \"true\", 1 },\n+\t\t{ \"index.version\", \"4\", 1 },\n+\t\t{ \"merge.stat\", \"false\", 1 },\n+\t\t{ \"merge.renames\", \"true\", 1 },\n+\t\t{ \"pack.useBitmaps\", \"false\", 1 },\n+\t\t{ \"pack.useSparse\", \"true\", 1 },\n+\t\t{ \"receive.autoGC\", \"false\", 1 },\n+\t\t{ \"reset.quiet\", \"true\", 1 },\n+\t\t{ \"feature.manyFiles\", \"false\", 1 },\n+\t\t{ \"feature.experimental\", \"false\", 1 },\n+\t\t{ \"fetch.unpackLimit\", \"1\", 1 },\n+\t\t{ \"fetch.writeCommitGraph\", \"false\", 1 },\n #ifdef WIN32\n-\t\t{ \"http.sslBackend\", \"schannel\" },\n+\t\t{ \"http.sslBackend\", \"schannel\", 1 },\n #endif\n+\t\t/* Optional */\n \t\t{ \"status.aheadBehind\", \"false\" },\n \t\t{ \"commitGraph.generationVersion\", \"1\" },\n \t\t{ \"core.autoCRLF\", \"false\" },\n@@ -166,7 +169,8 @@ static int set_recommended_config(void)\n \tchar *value;\n \n \tfor (i = 0; config[i].key; i++) {\n-\t\tif (git_config_get_string(config[i].key, &value)) {\n+\t\tif ((reconfigure && config[i].overwrite_on_reconfigure) ||\n+\t\t    git_config_get_string(config[i].key, &value)) {\n \t\t\ttrace2_data_string(\"scalar\", the_repository, config[i].key, \"created\");\n \t\t\tif (git_config_set_gently(config[i].key,\n \t\t\t\t\t\t  config[i].value) < 0)\n@@ -231,7 +235,7 @@ static int register_dir(void)\n \tint res = add_or_remove_enlistment(1);\n \n \tif (!res)\n-\t\tres = set_recommended_config();\n+\t\tres = set_recommended_config(0);\n \n \tif (!res)\n \t\tres = toggle_maintenance(1);\n@@ -419,7 +423,7 @@ static int cmd_clone(int argc, const char **argv)\n \t    (res = run_git(\"sparse-checkout\", \"init\", \"--cone\", NULL)))\n \t\tgoto cleanup;\n \n-\tif (set_recommended_config())\n+\tif (set_recommended_config(0))\n \t\treturn error(_(\"could not configure '%s'\"), dir);\n \n \tif ((res = run_git(\"fetch\", \"--quiet\", \"origin\", NULL))) {\n@@ -484,6 +488,24 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int cmd_reconfigure(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar reconfigure [<enlistment>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\treturn set_recommended_config(1);\n+}\n+\n static int cmd_run(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -620,6 +642,7 @@ static struct {\n \t{ \"register\", cmd_register },\n \t{ \"unregister\", cmd_unregister },\n \t{ \"run\", cmd_run },\n+\t{ \"reconfigure\", cmd_reconfigure },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 39143b08324..89fd7901585 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -13,6 +13,7 @@ scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n+scalar reconfigure <enlistment>\n \n DESCRIPTION\n -----------\n@@ -117,6 +118,13 @@ opinionated default settings that make Git work more efficiently with\n large repositories. As this task is run as part of `scalar clone`\n automatically, explicit invocations of this task are rarely needed.\n \n+Reconfigure\n+~~~~~~~~~~~\n+\n+After a Scalar upgrade, or when the configuration of a Scalar enlistment\n+was somehow corrupted or changed by mistake, this subcommand allows to\n+reconfigure the enlistment.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex f60e086d6f9..fb5e2efee0a 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -65,4 +65,12 @@ test_expect_success 'scalar clone' '\n \t)\n '\n \n+test_expect_success 'scalar reconfigure' '\n+\tgit init one/src &&\n+\tscalar register one &&\n+\tgit -C one/src config core.preloadIndex false &&\n+\tscalar reconfigure one &&\n+\ttest true = \"$(git -C one/src config core.preloadIndex)\"\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"442990","messageId":"efd808a0c4a5e5101e97fbc4b705d8fb7d256322.1638538470.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v10.git.1638538470.gitgitgadget@gmail.com","subject":"[PATCH v10 13/15] scalar: teach 'reconfigure' to optionally handle all registered enlistments","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-03T13:34:27Z","receivedAt":"2021-12-03T13:34:59Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nAfter a Scalar upgrade, it can come in really handy if there is an easy\nway to reconfigure all Scalar enlistments. This new option offers this\nfunctionality.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 61 ++++++++++++++++++++++++++++++--\n contrib/scalar/scalar.txt        |  9 +++--\n contrib/scalar/t/t9099-scalar.sh |  3 ++\n 3 files changed, 67 insertions(+), 6 deletions(-)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex d7306b43cae..305b080663b 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -488,22 +488,77 @@ static int cmd_register(int argc, const char **argv)\n \treturn register_dir();\n }\n \n+static int get_scalar_repos(const char *key, const char *value, void *data)\n+{\n+\tstruct string_list *list = data;\n+\n+\tif (!strcmp(key, \"scalar.repo\"))\n+\t\tstring_list_append(list, value);\n+\n+\treturn 0;\n+}\n+\n static int cmd_reconfigure(int argc, const char **argv)\n {\n+\tint all = 0;\n \tstruct option options[] = {\n+\t\tOPT_BOOL('a', \"all\", &all,\n+\t\t\t N_(\"reconfigure all registered enlistments\")),\n \t\tOPT_END(),\n \t};\n \tconst char * const usage[] = {\n-\t\tN_(\"scalar reconfigure [<enlistment>]\"),\n+\t\tN_(\"scalar reconfigure [--all | <enlistment>]\"),\n \t\tNULL\n \t};\n+\tstruct string_list scalar_repos = STRING_LIST_INIT_DUP;\n+\tint i, res = 0;\n+\tstruct repository r = { NULL };\n+\tstruct strbuf commondir = STRBUF_INIT, gitdir = STRBUF_INIT;\n \n \targc = parse_options(argc, argv, NULL, options,\n \t\t\t     usage, 0);\n \n-\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\tif (!all) {\n+\t\tsetup_enlistment_directory(argc, argv, usage, options, NULL);\n+\n+\t\treturn set_recommended_config(1);\n+\t}\n+\n+\tif (argc > 0)\n+\t\tusage_msg_opt(_(\"--all or <enlistment>, but not both\"),\n+\t\t\t      usage, options);\n+\n+\tgit_config(get_scalar_repos, &scalar_repos);\n \n-\treturn set_recommended_config(1);\n+\tfor (i = 0; i < scalar_repos.nr; i++) {\n+\t\tconst char *dir = scalar_repos.items[i].string;\n+\n+\t\tstrbuf_reset(&commondir);\n+\t\tstrbuf_reset(&gitdir);\n+\n+\t\tif (chdir(dir) < 0) {\n+\t\t\twarning_errno(_(\"could not switch to '%s'\"), dir);\n+\t\t\tres = -1;\n+\t\t} else if (discover_git_directory(&commondir, &gitdir) < 0) {\n+\t\t\twarning_errno(_(\"git repository gone in '%s'\"), dir);\n+\t\t\tres = -1;\n+\t\t} else {\n+\t\t\tgit_config_clear();\n+\n+\t\t\tthe_repository = &r;\n+\t\t\tr.commondir = commondir.buf;\n+\t\t\tr.gitdir = gitdir.buf;\n+\n+\t\t\tif (set_recommended_config(1) < 0)\n+\t\t\t\tres = -1;\n+\t\t}\n+\t}\n+\n+\tstring_list_clear(&scalar_repos, 1);\n+\tstrbuf_release(&commondir);\n+\tstrbuf_release(&gitdir);\n+\n+\treturn res;\n }\n \n static int cmd_run(int argc, const char **argv)\ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 89fd7901585..737cf563c1a 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -13,7 +13,7 @@ scalar list\n scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n-scalar reconfigure <enlistment>\n+scalar reconfigure [ --all | <enlistment> ]\n \n DESCRIPTION\n -----------\n@@ -32,8 +32,8 @@ an existing Git worktree with Scalar whose name is not `src`, the enlistment\n will be identical to the worktree.\n \n The `scalar` command implements various subcommands, and different options\n-depending on the subcommand. With the exception of `clone` and `list`, all\n-subcommands expect to be run in an enlistment.\n+depending on the subcommand. With the exception of `clone`, `list` and\n+`reconfigure --all`, all subcommands expect to be run in an enlistment.\n \n COMMANDS\n --------\n@@ -125,6 +125,9 @@ After a Scalar upgrade, or when the configuration of a Scalar enlistment\n was somehow corrupted or changed by mistake, this subcommand allows to\n reconfigure the enlistment.\n \n+With the `--all` option, all enlistments currently registered with Scalar\n+will be reconfigured. Use this option after each Scalar upgrade.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex fb5e2efee0a..58af546fd84 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -70,6 +70,9 @@ test_expect_success 'scalar reconfigure' '\n \tscalar register one &&\n \tgit -C one/src config core.preloadIndex false &&\n \tscalar reconfigure one &&\n+\ttest true = \"$(git -C one/src config core.preloadIndex)\" &&\n+\tgit -C one/src config core.preloadIndex false &&\n+\tscalar reconfigure -a &&\n \ttest true = \"$(git -C one/src config core.preloadIndex)\"\n '\n \n-- \ngitgitgadget\n\n"},{"id":"442991","messageId":"8b69462b906437c16f3f46300279674ad2455bba.1638538470.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v10.git.1638538470.gitgitgadget@gmail.com","subject":"[PATCH v10 14/15] scalar: implement the `delete` command","fromName":"Matthew John Cheetham via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-03T13:34:28Z","receivedAt":"2021-12-03T13:35:00Z","isPatch":true,"sender":{"key":"mjcheetham@outlook.com","avatar":"https://avatars.githubusercontent.com/u/5658207?v=4"},"body":"From: Matthew John Cheetham <mjcheetham@outlook.com>\n\nDelete an enlistment by first unregistering the repository and then\ndeleting the enlistment directory (usually the directory containing the\nworktree `src/` directory).\n\nOn Windows, if the current directory is inside the enlistment's\ndirectory, change to the parent of the enlistment directory, to allow us\nto delete the enlistment (directories used by processes e.g. as current\nworking directories cannot be deleted on Windows).\n\nCo-authored-by: Victoria Dye <vdye@github.com>\nSigned-off-by: Matthew John Cheetham <mjcheetham@outlook.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c          | 63 ++++++++++++++++++++++++++++++++\n contrib/scalar/scalar.txt        |  8 ++++\n contrib/scalar/t/t9099-scalar.sh |  9 +++++\n 3 files changed, 80 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex 305b080663b..d4303c7c4a2 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -8,6 +8,8 @@\n #include \"config.h\"\n #include \"run-command.h\"\n #include \"refs.h\"\n+#include \"dir.h\"\n+#include \"packfile.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -328,6 +330,33 @@ static char *remote_default_branch(const char *url)\n \treturn NULL;\n }\n \n+static int delete_enlistment(struct strbuf *enlistment)\n+{\n+#ifdef WIN32\n+\tstruct strbuf parent = STRBUF_INIT;\n+#endif\n+\n+\tif (unregister_dir())\n+\t\tdie(_(\"failed to unregister repository\"));\n+\n+#ifdef WIN32\n+\t/*\n+\t * Change the current directory to one outside of the enlistment so\n+\t * that we may delete everything underneath it.\n+\t */\n+\tstrbuf_addbuf(&parent, enlistment);\n+\tstrbuf_parent_directory(&parent);\n+\tif (chdir(parent.buf) < 0)\n+\t\tdie_errno(_(\"could not switch to '%s'\"), parent.buf);\n+\tstrbuf_release(&parent);\n+#endif\n+\n+\tif (remove_dir_recursively(enlistment, 0))\n+\t\tdie(_(\"failed to delete enlistment directory\"));\n+\n+\treturn 0;\n+}\n+\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n@@ -688,6 +717,39 @@ static int cmd_unregister(int argc, const char **argv)\n \treturn unregister_dir();\n }\n \n+static int cmd_delete(int argc, const char **argv)\n+{\n+\tchar *cwd = xgetcwd();\n+\tstruct option options[] = {\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar delete <enlistment>\"),\n+\t\tNULL\n+\t};\n+\tstruct strbuf enlistment = STRBUF_INIT;\n+\tint res = 0;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tif (argc != 1)\n+\t\tusage_with_options(usage, options);\n+\n+\tsetup_enlistment_directory(argc, argv, usage, options, &enlistment);\n+\n+\tif (dir_inside_of(cwd, enlistment.buf) >= 0)\n+\t\tres = error(_(\"refusing to delete current working directory\"));\n+\telse {\n+\t\tclose_object_store(the_repository->objects);\n+\t\tres = delete_enlistment(&enlistment);\n+\t}\n+\tstrbuf_release(&enlistment);\n+\tfree(cwd);\n+\n+\treturn res;\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n@@ -698,6 +760,7 @@ static struct {\n \t{ \"unregister\", cmd_unregister },\n \t{ \"run\", cmd_run },\n \t{ \"reconfigure\", cmd_reconfigure },\n+\t{ \"delete\", cmd_delete },\n \t{ NULL, NULL},\n };\n \ndiff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt\nindex 737cf563c1a..f416d637289 100644\n--- a/contrib/scalar/scalar.txt\n+++ b/contrib/scalar/scalar.txt\n@@ -14,6 +14,7 @@ scalar register [<enlistment>]\n scalar unregister [<enlistment>]\n scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]\n scalar reconfigure [ --all | <enlistment> ]\n+scalar delete <enlistment>\n \n DESCRIPTION\n -----------\n@@ -128,6 +129,13 @@ reconfigure the enlistment.\n With the `--all` option, all enlistments currently registered with Scalar\n will be reconfigured. Use this option after each Scalar upgrade.\n \n+Delete\n+~~~~~~\n+\n+delete <enlistment>::\n+\tThis subcommand lets you delete an existing Scalar enlistment from your\n+\tlocal file system, unregistering the repository.\n+\n SEE ALSO\n --------\n linkgit:git-clone[1], linkgit:git-maintenance[1].\ndiff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh\nindex 58af546fd84..2e1502ad45e 100755\n--- a/contrib/scalar/t/t9099-scalar.sh\n+++ b/contrib/scalar/t/t9099-scalar.sh\n@@ -76,4 +76,13 @@ test_expect_success 'scalar reconfigure' '\n \ttest true = \"$(git -C one/src config core.preloadIndex)\"\n '\n \n+test_expect_success 'scalar delete without enlistment shows a usage' '\n+\ttest_expect_code 129 scalar delete\n+'\n+\n+test_expect_success 'scalar delete with enlistment' '\n+\tscalar delete cloned &&\n+\ttest_path_is_missing cloned\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"442992","messageId":"b5f416d79b4a3d8388f0081380044d28474ede50.1638538470.git.gitgitgadget@gmail.com","threadId":"56409","inReplyTo":"pull.1005.v10.git.1638538470.gitgitgadget@gmail.com","subject":"[PATCH v10 15/15] scalar: implement the `version` command","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-03T13:34:29Z","receivedAt":"2021-12-03T13:35:03Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe .NET version of Scalar has a `version` command. This was necessary\nbecause it was versioned independently of Git.\n\nSince Scalar is now tightly coupled with Git, it does not make sense for\nthem to show different versions. Therefore, it shows the same output as\n`git version`. For backwards-compatibility with the .NET version,\n`scalar version` prints to `stderr`, though (`git version` prints to\n`stdout` instead).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n contrib/scalar/scalar.c | 39 +++++++++++++++++++++++++++++++++++++++\n 1 file changed, 39 insertions(+)\n\ndiff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c\nindex d4303c7c4a2..1ce9c2b00e8 100644\n--- a/contrib/scalar/scalar.c\n+++ b/contrib/scalar/scalar.c\n@@ -10,6 +10,7 @@\n #include \"refs.h\"\n #include \"dir.h\"\n #include \"packfile.h\"\n+#include \"help.h\"\n \n /*\n  * Remove the deepest subdirectory in the provided path string. Path must not\n@@ -357,6 +358,15 @@ static int delete_enlistment(struct strbuf *enlistment)\n \treturn 0;\n }\n \n+/*\n+ * Dummy implementation; Using `get_version_info()` would cause a link error\n+ * without this.\n+ */\n+void load_builtin_commands(const char *prefix, struct cmdnames *cmds)\n+{\n+\tdie(\"not implemented\");\n+}\n+\n static int cmd_clone(int argc, const char **argv)\n {\n \tconst char *branch = NULL;\n@@ -750,6 +760,34 @@ static int cmd_delete(int argc, const char **argv)\n \treturn res;\n }\n \n+static int cmd_version(int argc, const char **argv)\n+{\n+\tint verbose = 0, build_options = 0;\n+\tstruct option options[] = {\n+\t\tOPT__VERBOSE(&verbose, N_(\"include Git version\")),\n+\t\tOPT_BOOL(0, \"build-options\", &build_options,\n+\t\t\t N_(\"include Git's build options\")),\n+\t\tOPT_END(),\n+\t};\n+\tconst char * const usage[] = {\n+\t\tN_(\"scalar verbose [-v | --verbose] [--build-options]\"),\n+\t\tNULL\n+\t};\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     usage, 0);\n+\n+\tif (argc != 0)\n+\t\tusage_with_options(usage, options);\n+\n+\tget_version_info(&buf, build_options);\n+\tfprintf(stderr, \"%s\\n\", buf.buf);\n+\tstrbuf_release(&buf);\n+\n+\treturn 0;\n+}\n+\n static struct {\n \tconst char *name;\n \tint (*fn)(int, const char **);\n@@ -761,6 +799,7 @@ static struct {\n \t{ \"run\", cmd_run },\n \t{ \"reconfigure\", cmd_reconfigure },\n \t{ \"delete\", cmd_delete },\n+\t{ \"version\", cmd_version },\n \t{ NULL, NULL},\n };\n \n-- \ngitgitgadget\n"},{"id":"443003","messageId":"CABPp-BGpe9Q5k22Yu8a=1xwu=pZYSeNQoqEgf+DN07cU4EB1ew@mail.gmail.com","threadId":"56409","inReplyTo":"pull.1005.v10.git.1638538470.gitgitgadget@gmail.com","subject":"Re: [PATCH v10 00/15] Upstreaming the Scalar command","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-12-03T15:48:04Z","receivedAt":"2021-12-03T15:48:20Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Fri, Dec 3, 2021 at 5:34 AM Johannes Schindelin via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> tl;dr: This series contributes the core part of the Scalar command to the\n> Git project. This command provides a convenient way to clone/initialize very\n> large repositories (think: monorepos).\n>\n> Note: This patch series' focus is entirely on Scalar, on choosing sensible\n> defaults and offering a delightful user experience around working with\n> monorepos, and not about changing any existing paradigms for contrib/ (even\n> if catching up on the mail thread is likely to give interested readers that\n> false impression).\n>\n> Changes since v9:\n>\n>  * The patches to build Scalar and run its tests as part of Git's CI/PR,\n>    have been dropped because a recent unrelated patch series does not\n>    interact well with them.\n\n\ni.e. basically undoing this:\n\n...\n> Changes since v6:\n...\n>  * I added two patches that I had planned on keeping in an add-on patch\n>    series for later, to build and test Scalar as part of the CI. I am still\n>    not 100% certain that it is a good idea to do so already now, but let's\n>    see what the reviewers have to say.\n\n...and returning to the original plan:\n\n...\n> On top of this patch series, I have lined up a few more:\n...\n>  4. A few patches to optionally build and install scalar as part of a\n>     regular Git install (also teaching git help scalar to find the Scalar\n>     documentation\n\nAvoiding the issues and adding the CI later seems reasonable to me.\nYou addressed the last of my points in v9; I think this version is\ngood to go.  But one quick comment...\n\n> These are included in my vfs-with-scalar branch thicket\n> [https://github.com/dscho/git/commits/vfs-with-scalar]. On top of that, this\n> branch thicket also includes patches I do not plan on upstreaming, mainly\n> because they are too specific either to VFS for Git, or they support Azure\n> Repos (which does not offer partial clones but speaks the GVFS protocol,\n> which can be used to emulate partial clones).\n>\n> One other thing is very interesting about that vfs-with-scalar branch\n> thicket: it contains a GitHub workflow which will run Scalar's quite\n> extensive Functional Tests suite. This test suite is quite comprehensive and\n> caught us a lot of bugs in the past, not only in the Scalar code, but also\n> core Git.\n\nFrom your wording it sounds like the plan might not include moving\nthese tests over.  Perhaps it doesn't make sense to move them all\nover, but since they've caught problems in both Scalar and core Git,\nit would be nice to see many of those tests come to Git as well as\npart of a future follow on series.\n"},{"id":"443102","messageId":"xmqq4k7nmksi.fsf@gitster.g","threadId":"56409","inReplyTo":"CABPp-BGpe9Q5k22Yu8a=1xwu=pZYSeNQoqEgf+DN07cU4EB1ew@mail.gmail.com","subject":"Re: [PATCH v10 00/15] Upstreaming the Scalar command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-12-05T10:02:37Z","receivedAt":"2021-12-05T10:02:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n> From your wording it sounds like the plan might not include moving\n> these tests over.  Perhaps it doesn't make sense to move them all\n> over, but since they've caught problems in both Scalar and core Git,\n> it would be nice to see many of those tests come to Git as well as\n> part of a future follow on series.\n\nYeah, we may be initially queuing this without tests for expediency,\nbut a production code cannot go forever without CI tests to ensure\ncontinued code health.  People make changes in other parts of the\nsystem Scalar may depend on and unknowingly break some assumption\nScalar makes on it.\n\n"},{"id":"443347","messageId":"211207.86ilw0matb.gmgdl@evledraar.gmail.com","threadId":"56409","inReplyTo":"xmqq4k7nmksi.fsf@gitster.g","subject":"Re: [PATCH v10 00/15] Upstreaming the Scalar command","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-12-07T20:05:31Z","receivedAt":"2021-12-07T20:15:00Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sun, Dec 05 2021, Junio C Hamano wrote:\n\n> Elijah Newren <newren@gmail.com> writes:\n>\n>> From your wording it sounds like the plan might not include moving\n>> these tests over.  Perhaps it doesn't make sense to move them all\n>> over, but since they've caught problems in both Scalar and core Git,\n>> it would be nice to see many of those tests come to Git as well as\n>> part of a future follow on series.\n>\n> Yeah, we may be initially queuing this without tests for expediency,\n> but a production code cannot go forever without CI tests to ensure\n> continued code health.  People make changes in other parts of the\n> system Scalar may depend on and unknowingly break some assumption\n> Scalar makes on it.\n\nIn this case there really isn't any reason not to have the tests go in\nat the same time. The explanation in the v10 CL is:\n    \n    Changes since v9:\n    \n     * The patches to build Scalar and run its tests as part of Git's CI/PR,\n       have been dropped because a recent unrelated patch series does not\n       interact well with them.\n\nThat assessment isn't correct.\n\nThe change in v8->v9 of adding a \"make &&\" before the \"test\" was only\nnecessary because of a logic error in the v8 version. Yes it broke\nbecause the \"scalar test\" target didn't know how to build its\nprerequisites, but the real underlying issue is that it was even trying\nat that point. It had no business running in the static-analysis target\nwhere we hadn't built git already.\n\nNow v9->v10 has\ndropped the tests entirely, allegedly due to an interaction with my\nab/ci-updates, but there's nothing new there that isn't also the case on\n\"master\".\n\nBut we can have our cake and eat it too.\n\nThe below patch on top of v9 would make the scalar tests do the right\nthing. I.e. whenever we do a \"make test\" we'll run the scalar tests\ntoo.\n\nThe code changes somewhat with ab/ci-updates, but the conflict with\njs/scalar is mostly textual, not semantic (and as I've pointed out, to\nthe extent that ab/ci-updates changed anything it made things a bit\nbetter for js/scalar).\n\nI'd really like to see this scalar series land, but I really don't see\nwhy it's necessary to entirety eject the CI test coverage due to what's\na rather trivilly solved issue.\n\nAs I've noted ad-nauseum at this point I think the necessity for the\nbelow patch is rather silly, this should just nicely integrate with\n\"make test\", but <brokenrecord.gif>. But even without that IMO better\napproach it's clearly rather trivial to make this series have test\ncoverage.\n\nIt was just broken because it added a test run to the \"pedantic\" run,\nand didn't properly integrate with the multi-\"make test\" runs on\n\"master\" , both of which are addressed by the patch below.\n\ndiff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh\nindex 2ef9fbfdd38..af99699f82b 100755\n--- a/ci/run-build-and-tests.sh\n+++ b/ci/run-build-and-tests.sh\n@@ -15,6 +15,26 @@ then\n \texport DEVOPTS=pedantic\n fi\n \n+make() {\n+\tscalar_tests=\n+\tfor target\n+\tdo\n+\t\tif test $target = \"test\"\n+\t\tthen\n+\t\t\tscalar_tests=t\n+\t\tfi\n+\tdone\n+\n+\t# Do whatever we would have done with \"make\"\n+\tcommand make \"$@\"\n+\n+\t# Running tests? Run scalar tests too\n+\tif test -n \"$scalar_tests\"\n+\tthen\n+\t\tcommand make -C contrib/scalar test\n+\tfi\n+}\n+\n make\n case \"$jobname\" in\n linux-gcc)\n@@ -52,6 +72,4 @@ esac\n \n check_unignored_build_artifacts\n \n-make && make -C contrib/scalar test\n-\n save_good_tree\n"},{"id":"443416","messageId":"nycvar.QRO.7.76.6.2112081207320.90@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"xmqq4k7nmksi.fsf@gitster.g","subject":"Re: [PATCH v10 00/15] Upstreaming the Scalar command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-12-08T11:15:41Z","receivedAt":"2021-12-08T11:16:00Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Sun, 5 Dec 2021, Junio C Hamano wrote:\n\n> Elijah Newren <newren@gmail.com> writes:\n>\n> > From your wording it sounds like the plan might not include moving\n> > these tests over.  Perhaps it doesn't make sense to move them all\n> > over, but since they've caught problems in both Scalar and core Git,\n> > it would be nice to see many of those tests come to Git as well as\n> > part of a future follow on series.\n>\n> Yeah, we may be initially queuing this without tests for expediency,\n> but a production code cannot go forever without CI tests to ensure\n> continued code health.  People make changes in other parts of the\n> system Scalar may depend on and unknowingly break some assumption\n> Scalar makes on it.\n\nThe Scalar Functional Tests were designed with Azure Repos in mind, i.e.\nthey specifically verify that the `gvfs-helper` (emulating Partial Clone\nusing the predecessor of Partial Clone, the GVFS protocol) manages to\naccess the repositories in the intended way.\n\nI do not know off-hand how entangled the GVFS part is in the test suite,\nbut from what I recall, every single test starts with cloning a test\nrepository. From Azure Repos. Using the `gvfs-helper`.\n\nWhich means that the `gvfs-helper` would need to be upstreamed and be\nmaintained in the git.git repository proper.\n\nPreviously I was under the impression that that might be met with grumpy\nrejection.\n\nI do realize, though, that clarity of intention has been missing from this\nmail thread all around, so let me ask point blank: Junio, do you want me\nto include upstreaming `gvfs-helper` in the overall Scalar plan?\n\nCiao,\nDscho\n"},{"id":"443417","messageId":"nycvar.QRO.7.76.6.2112081217450.90@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"xmqq35navofg.fsf@gitster.g","subject":"Re: [PATCH v9 00/17] Upstreaming the Scalar command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-12-08T11:26:43Z","receivedAt":"2021-12-08T11:27:01Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Thu, 2 Dec 2021, Junio C Hamano wrote:\n\n> Elijah Newren <newren@gmail.com> writes:\n>\n> > while I think CI testing would be nice once we have a functionally\n> > useful scalar, the CI tests of this early version aren't really\n> > netting us anything.  And they're blocking future scalar series\n> > unnecessarily.  Johannes already said he had planned CI testing for a\n> > future series, so I'd rather just take this version of js/scalar minus\n> > the CI integration for next.)\n>\n> Yeah, with less stomping on each others' toes, things may flow\n> smoother.\n\nI also would find it nice if that was the case.\n\nIf I remember correctly, you mentioned quite a couple of times that you\nexpect, particularly oldtimers on this list, to be mindful when\ncontributing patch series, and to delay patches that would interfere with\nother patch series that are already in flight.\n\nI saw with sorrow that this rule was ignored a couple of times recently,\neven with new contributors, and I sincerely hope that we can unignore that\nrule again.\n\n> As long as people are happy with the core part, that sounds like the\n> best approach forward.\n\nThank you. I have to admit that it was quite frustrating to see so many\nobstacles being put in my way, only to end up at pretty much the same\nplace as I had been over a month ago. So it is extra gratifying to hear\nthat you move forward with the Scalar patch series. I truly believe that\nour users will be better off for it.\n\nThanks,\nDscho\n"},{"id":"443441","messageId":"211208.86k0gfkzym.gmgdl@evledraar.gmail.com","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2112081207320.90@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v10 00/15] Upstreaming the Scalar command","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-12-08T13:04:53Z","receivedAt":"2021-12-08T13:07:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Dec 08 2021, Johannes Schindelin wrote:\n\n> Hi Junio,\n>\n> On Sun, 5 Dec 2021, Junio C Hamano wrote:\n>\n>> Elijah Newren <newren@gmail.com> writes:\n>>\n>> > From your wording it sounds like the plan might not include moving\n>> > these tests over.  Perhaps it doesn't make sense to move them all\n>> > over, but since they've caught problems in both Scalar and core Git,\n>> > it would be nice to see many of those tests come to Git as well as\n>> > part of a future follow on series.\n>>\n>> Yeah, we may be initially queuing this without tests for expediency,\n>> but a production code cannot go forever without CI tests to ensure\n>> continued code health.  People make changes in other parts of the\n>> system Scalar may depend on and unknowingly break some assumption\n>> Scalar makes on it.\n>\n> The Scalar Functional Tests were designed with Azure Repos in mind, i.e.\n> they specifically verify that the `gvfs-helper` (emulating Partial Clone\n> using the predecessor of Partial Clone, the GVFS protocol) manages to\n> access the repositories in the intended way.\n>\n> I do not know off-hand how entangled the GVFS part is in the test suite,\n> but from what I recall, every single test starts with cloning a test\n> repository. From Azure Repos. Using the `gvfs-helper`.\n>\n> Which means that the `gvfs-helper` would need to be upstreamed and be\n> maintained in the git.git repository proper.\n>\n> Previously I was under the impression that that might be met with grumpy\n> rejection.\n>\n> I do realize, though, that clarity of intention has been missing from this\n> mail thread all around, so let me ask point blank: Junio, do you want me\n> to include upstreaming `gvfs-helper` in the overall Scalar plan?\n\nAn alternate way would be be to have our own tests build git, and then\nclone and build those third party repos and test them.\n\nI had a patch to do that for git-annex. I think it would be a good idea\nto pursue it in general for prominent downstream projects as part of\nsome extended integration testing:\nhttps://lore.kernel.org/git/20170516203712.15921-1-avarab@gmail.com/\n"},{"id":"443445","messageId":"f1cd174f-607c-0083-abef-93eff37a8480@gmail.com","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2112081207320.90@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v10 00/15] Upstreaming the Scalar command","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2021-12-08T14:17:27Z","receivedAt":"2021-12-08T14:17:33Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 12/8/2021 6:15 AM, Johannes Schindelin wrote:\n> Hi Junio,\n> \n> On Sun, 5 Dec 2021, Junio C Hamano wrote:\n> \n>> Elijah Newren <newren@gmail.com> writes:\n>>\n>>> From your wording it sounds like the plan might not include moving\n>>> these tests over.  Perhaps it doesn't make sense to move them all\n>>> over, but since they've caught problems in both Scalar and core Git,\n>>> it would be nice to see many of those tests come to Git as well as\n>>> part of a future follow on series.\n\nMoving the C# test suite over doesn't make a lot of sense. We also\nare re-using the test suite from VFS for Git, which is probably overkill\nhere. Those tests were created due to issues that arose with the virtual\nfilesystem (paired with the GVFS protocol for finding missing objects)\nand most of them probably don't test anything interesting in Scalar.\n\nWhen we _do_ find something interesting in that suite, we port over the\ntest as a normal Git test so the regression is avoided in the future.\n\nWe work to test the -rc0 version of every release with our custom patches\nin microsoft/git and then run them through the Scalar and VFS for Git\nfunctional tests as a necessary step before releasing to our internal\nusers. Since we are doing that already, it is a better use of time to\nport tests that actually matter when they come up rather than port the\nentire test suite.\n\n>> Yeah, we may be initially queuing this without tests for expediency,\n>> but a production code cannot go forever without CI tests to ensure\n>> continued code health.  People make changes in other parts of the\n>> system Scalar may depend on and unknowingly break some assumption\n>> Scalar makes on it.\n\nI think it is important to keep in mind that the Scalar features that\nare being submitted here are getting Git-style tests included. The only\nthing that is missing right now is a firm link with Git's CI system,\nwhich can be added quickly once things have calmed down in the build\nsystem.\n\nIf we are interested in doing something more substantial that is\ncloser to the Scalar functional tests, then it is important to know\nthat those tests are running against a production server to clone\ndata and fetch it dynamically throughout. That is not exactly something\nwe have done in the Git test suite before.\n\nIn fact, I don't think Scalar introduces anything novel here: if we\nwant to add more coverage of running Git commands while in a\nsparse-checkout _and_ partial clone _and_ have a lot of optional config\nset, then we can do that independently of Scalar. 'scalar clone' just\nsets up a repository in a state that an expert user could do themselves,\nso should we spend a lot of effort creating that environment in our\ntest suite?\n\nWe have this already in some form:\n\n1. t1091 and t1092 try to cover important sparse-checkout behavior.\n\n2. t0410, t5616, and others try to cover important partial clone\n   behavior.\n\n3. Our GIT_TEST_* variables that are enabled in one of our CI runs\n   test many of the advanced config options enabled by Scalar.\n\nThe thing that is missing is \"all of these things at once\" which\nwould be difficult to do across the test suite with our current test\ndesign. I'm happy to provide the service of checking the Scalar\nfunctional tests before each release as an expensive way to check\nthat combination of configuration without adding that cost to every\nCI run and developer inner loop.\n \n> The Scalar Functional Tests were designed with Azure Repos in mind, i.e.\n> they specifically verify that the `gvfs-helper` (emulating Partial Clone\n> using the predecessor of Partial Clone, the GVFS protocol) manages to\n> access the repositories in the intended way.\n> \n> I do not know off-hand how entangled the GVFS part is in the test suite,\n> but from what I recall, every single test starts with cloning a test\n> repository. From Azure Repos. Using the `gvfs-helper`.\n\nThere is a single test that checks that 'scalar clone' against github.com\nworks appropriately [1]. We don't duplicate all of the other tests in\nthis environment.\n\n[1] https://github.com/microsoft/scalar/blob/68b6e70d77f1c7c13be9f35848a042604f3fb2f1/Scalar.FunctionalTests/Tests/MultiEnlistmentTests/ScalarCloneFromGithub.cs\n\n> Which means that the `gvfs-helper` would need to be upstreamed and be\n> maintained in the git.git repository proper.\n> \n> Previously I was under the impression that that might be met with grumpy\n> rejection.\n> \n> I do realize, though, that clarity of intention has been missing from this\n> mail thread all around, so let me ask point blank: Junio, do you want me\n> to include upstreaming `gvfs-helper` in the overall Scalar plan?\n\nI, for one, don't think that has much value for the core Git project.\n\nThanks,\n-Stolee\n"},{"id":"443493","messageId":"CABPp-BFmNiqY=NfN7Ys3XE8wYBn1EQ_War+0QLq96Tk7FO6zfg@mail.gmail.com","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2112081207320.90@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v10 00/15] Upstreaming the Scalar command","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-12-08T18:29:13Z","receivedAt":"2021-12-08T18:29:29Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"I know this was directed to Junio, but I feel like it was my earlier\ncomment that accidentally opened this can of worms, so if my opinion\nhelps resolve it at all...\n\nOn Wed, Dec 8, 2021 at 3:15 AM Johannes Schindelin\n<Johannes.Schindelin@gmx.de> wrote:\n>\n> Hi Junio,\n>\n> On Sun, 5 Dec 2021, Junio C Hamano wrote:\n>\n> > Elijah Newren <newren@gmail.com> writes:\n> >\n> > > From your wording it sounds like the plan might not include moving\n> > > these tests over.  Perhaps it doesn't make sense to move them all\n> > > over, but since they've caught problems in both Scalar and core Git,\n> > > it would be nice to see many of those tests come to Git as well as\n> > > part of a future follow on series.\n> >\n> > Yeah, we may be initially queuing this without tests for expediency,\n> > but a production code cannot go forever without CI tests to ensure\n> > continued code health.  People make changes in other parts of the\n> > system Scalar may depend on and unknowingly break some assumption\n> > Scalar makes on it.\n>\n> The Scalar Functional Tests were designed with Azure Repos in mind, i.e.\n> they specifically verify that the `gvfs-helper` (emulating Partial Clone\n> using the predecessor of Partial Clone, the GVFS protocol) manages to\n> access the repositories in the intended way.\n>\n> I do not know off-hand how entangled the GVFS part is in the test suite,\n> but from what I recall, every single test starts with cloning a test\n> repository. From Azure Repos. Using the `gvfs-helper`.\n>\n> Which means that the `gvfs-helper` would need to be upstreamed and be\n> maintained in the git.git repository proper.\n\nAh, sorry, I was remembering this from an earlier cover letter of yours:\n\n\"\"\"\nBut it was realized that many of these key concepts were independent of the\nactual VFS and its projection of the working directory. The Scalar project\nwas created to make that separation, refine the key concepts, and then\nextract those features into the new Scalar command.\n\"\"\"\n\nwhen I read\n\n\"\"\"\nOne other thing is very interesting about that vfs-with-scalar branch\nthicket: it contains a GitHub workflow which will run Scalar's quite\nextensive Functional Tests suite. This test suite is quite comprehensive and\ncaught us a lot of bugs in the past, not only in the Scalar code, but also\ncore Git.\n\"\"\"\n\nand I was thinking (despite the branch name) that you had some scalar\n+ git (w/o gvfs) tests that were interesting but not planning to\nupstream.  I agree that if they're gvfs + scalar + git then they make\nsense to keep internal to your work, though I hope that for any bugs\nyour internal testcases find in git, that you find an upstreamable\ntestcase to submit.  I believe Stolee has done exactly that in the\npast, so just more of that would be good.\n\n> Previously I was under the impression that that might be met with grumpy\n> rejection.\n>\n> I do realize, though, that clarity of intention has been missing from this\n> mail thread all around, so let me ask point blank: Junio, do you want me\n> to include upstreaming `gvfs-helper` in the overall Scalar plan?\n\nI know I'm not Junio, but if my opinion matters, I don't think that\nneeds to be part of the plan.\n"},{"id":"443513","messageId":"xmqqh7bi27o9.fsf@gitster.g","threadId":"56409","inReplyTo":"211207.86ilw0matb.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH v10 00/15] Upstreaming the Scalar command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-12-08T19:55:18Z","receivedAt":"2021-12-08T19:55:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> It was just broken because it added a test run to the \"pedantic\" run,\n> and didn't properly integrate with the multi-\"make test\" runs on\n> \"master\" , both of which are addressed by the patch below.\n>\n> diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh\n> index 2ef9fbfdd38..af99699f82b 100755\n> --- a/ci/run-build-and-tests.sh\n> +++ b/ci/run-build-and-tests.sh\n> @@ -15,6 +15,26 @@ then\n>  \texport DEVOPTS=pedantic\n>  fi\n>  \n> +make() {\n> +\tscalar_tests=\n> +\tfor target\n> +\tdo\n> +\t\tif test $target = \"test\"\n> +\t\tthen\n> +\t\t\tscalar_tests=t\n> +\t\tfi\n> +\tdone\n> +\n> +\t# Do whatever we would have done with \"make\"\n> +\tcommand make \"$@\"\n> +\n> +\t# Running tests? Run scalar tests too\n> +\tif test -n \"$scalar_tests\"\n> +\tthen\n> +\t\tcommand make -C contrib/scalar test\n> +\tfi\n> +}\n> +\n>  make\n>  case \"$jobname\" in\n>  linux-gcc)\n> @@ -52,6 +72,4 @@ esac\n>  \n>  check_unignored_build_artifacts\n>  \n> -make && make -C contrib/scalar test\n> -\n>  save_good_tree\n\nThat is an interesting way to demonstrate how orthogonal the issues\nare, which in turn means that it is not such a big deal to add back\nthe coverage to the part that goes to contrib/scalar/.  As the actual\nimplementation, it is a bit too icky, though.\n"},{"id":"443515","messageId":"xmqq8rwu278d.fsf_-_@gitster.g","threadId":"56409","inReplyTo":"xmqqh7bi27o9.fsf@gitster.g","subject":"[RFC/PATCH] Makefile: add test-all target","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-12-08T20:04:50Z","receivedAt":"2021-12-08T20:04:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"We ship contrib/ stuff within our primary source tree but except for\nthe completion scripts that are tested from our primary test suite,\ntheir test suites are not run in the CI.\n\nTeach the main Makefile a \"test-extra\" target, which goes into each\npackage in contrib/ whose Makefile has its own \"test\" target and\nruns \"make test\" there.  Add a \"test-all\" target to make it easy to\ndrive both the primary tests and these contrib tests from CI and use\nit.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\nJunio C Hamano <gitster@pobox.com> writes:\n\n> That is an interesting way to demonstrate how orthogonal the issues\n> are, which in turn means that it is not such a big deal to add back\n> the coverage to the part that goes to contrib/scalar/.  As the actual\n> implementation, it is a bit too icky, though.\n\nSo, how about doing it this way?  This is based on 'master' and does\nnot cover contrib/scalar, but if we want to go this route, it should\nbe trivial to do it on top of a merge of ab/ci-updates and js/scalar\ninto 'master'.  Good idea?  Terrible idea?  Not good enough?\n\n Makefile                  | 12 +++++++++++-\n ci/run-build-and-tests.sh | 10 +++++-----\n 2 files changed, 16 insertions(+), 6 deletions(-)\n\ndiff --git i/Makefile w/Makefile\nindex d56c0e4aad..ca14558e3c 100644\n--- i/Makefile\n+++ w/Makefile\n@@ -2878,10 +2878,20 @@ export TEST_NO_MALLOC_CHECK\n test: all\n \t$(MAKE) -C t/ all\n \n+# Additional tests from places in contrib/ that are prepared to take\n+# \"make -C $there test\", but expects that the primary build is done\n+# already.\n+test-extra: all\n+\t$(MAKE) -C contrib/diff-highlight test\n+\t$(MAKE) -C contrib/mw-to-git test\n+\t$(MAKE) -C contrib/subtree test\n+\n+test-all:: test test-extra\n+\n perf: all\n \t$(MAKE) -C t/perf/ all\n \n-.PHONY: test perf\n+.PHONY: test test-extra test-all perf\n \n .PRECIOUS: $(TEST_OBJS)\n \ndiff --git i/ci/run-build-and-tests.sh w/ci/run-build-and-tests.sh\nindex cc62616d80..9da0f26665 100755\n--- i/ci/run-build-and-tests.sh\n+++ w/ci/run-build-and-tests.sh\n@@ -19,7 +19,7 @@ make\n case \"$jobname\" in\n linux-gcc)\n \texport GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n-\tmake test\n+\tmake test-all\n \texport GIT_TEST_SPLIT_INDEX=yes\n \texport GIT_TEST_MERGE_ALGORITHM=recursive\n \texport GIT_TEST_FULL_IN_PACK_ARRAY=true\n@@ -33,20 +33,20 @@ linux-gcc)\n \texport GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master\n \texport GIT_TEST_WRITE_REV_INDEX=1\n \texport GIT_TEST_CHECKOUT_WORKERS=2\n-\tmake test\n+\tmake test-all\n \t;;\n linux-clang)\n \texport GIT_TEST_DEFAULT_HASH=sha1\n-\tmake test\n+\tmake test-all\n \texport GIT_TEST_DEFAULT_HASH=sha256\n-\tmake test\n+\tmake test-all\n \t;;\n linux-gcc-4.8|pedantic)\n \t# Don't run the tests; we only care about whether Git can be\n \t# built with GCC 4.8 or with pedantic\n \t;;\n *)\n-\tmake test\n+\tmake test-all\n \t;;\n esac\n \n"},{"id":"443520","messageId":"4dbca719-a1df-27e9-6e07-dea3f2e2638b@gmail.com","threadId":"56409","inReplyTo":"xmqq8rwu278d.fsf_-_@gitster.g","subject":"Re: [RFC/PATCH] Makefile: add test-all target","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2021-12-08T21:30:06Z","receivedAt":"2021-12-08T21:30:11Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 12/8/2021 3:04 PM, Junio C Hamano wrote:\n> We ship contrib/ stuff within our primary source tree but except for\n> the completion scripts that are tested from our primary test suite,\n> their test suites are not run in the CI.\n> \n> Teach the main Makefile a \"test-extra\" target, which goes into each\n> package in contrib/ whose Makefile has its own \"test\" target and\n> runs \"make test\" there.  Add a \"test-all\" target to make it easy to\n> drive both the primary tests and these contrib tests from CI and use\n> it.\n\n> So, how about doing it this way?  This is based on 'master' and does\n> not cover contrib/scalar, but if we want to go this route, it should\n> be trivial to do it on top of a merge of ab/ci-updates and js/scalar\n> into 'master'.  Good idea?  Terrible idea?  Not good enough?\n\n> +# Additional tests from places in contrib/ that are prepared to take\n> +# \"make -C $there test\", but expects that the primary build is done\n> +# already.\n> +test-extra: all\n> +\t$(MAKE) -C contrib/diff-highlight test\n> +\t$(MAKE) -C contrib/mw-to-git test\n> +\t$(MAKE) -C contrib/subtree test\n\nI like how this is obviously extendible to include contrib/scalar\nin a later change, then remove it when Scalar moves.\n\n> +test-all:: test test-extra\n\nAnd this test-all implies that test runs before test-extra, so\nlibgit.a is compiled appropriately.\n\nI think this approach looks good to me.\n\n> diff --git i/ci/run-build-and-tests.sh w/ci/run-build-and-tests.sh\n> index cc62616d80..9da0f26665 100755\n> --- i/ci/run-build-and-tests.sh\n> +++ w/ci/run-build-and-tests.sh\n> @@ -19,7 +19,7 @@ make\n>  case \"$jobname\" in\n>  linux-gcc)\n>  \texport GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n> -\tmake test\n> +\tmake test-all\n\nSince we are now building and testing things that we have not been\ntesting recently, it is worth checking that we don't have any work\nto do to make this pass. I assume that you've run 'make test-all'\non your own machine. It will be good to see what the full action\nreports (probably all good).\n\nThanks,\n-Stolee\n"},{"id":"443533","messageId":"YbEpE7UI+ovnNQyj@coredump.intra.peff.net","threadId":"56409","inReplyTo":"xmqq8rwu278d.fsf_-_@gitster.g","subject":"Re: [RFC/PATCH] Makefile: add test-all target","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-12-08T21:52:19Z","receivedAt":"2021-12-08T21:52:22Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Dec 08, 2021 at 12:04:50PM -0800, Junio C Hamano wrote:\n\n> > That is an interesting way to demonstrate how orthogonal the issues\n> > are, which in turn means that it is not such a big deal to add back\n> > the coverage to the part that goes to contrib/scalar/.  As the actual\n> > implementation, it is a bit too icky, though.\n> \n> So, how about doing it this way?  This is based on 'master' and does\n> not cover contrib/scalar, but if we want to go this route, it should\n> be trivial to do it on top of a merge of ab/ci-updates and js/scalar\n> into 'master'.  Good idea?  Terrible idea?  Not good enough?\n\nI don't mind the general direction, but...\n\n> +# Additional tests from places in contrib/ that are prepared to take\n> +# \"make -C $there test\", but expects that the primary build is done\n> +# already.\n> +test-extra: all\n> +\t$(MAKE) -C contrib/diff-highlight test\n> +\t$(MAKE) -C contrib/mw-to-git test\n> +\t$(MAKE) -C contrib/subtree test\n\nI'm not sure of the quality of tests in some of the contrib stuff. The\ntests in diff-highlight worked for me when I added them, but it's not\nlike I ever run them regularly, or that they've been tested on a wide\nvariety of platforms.\n\nSo I think this is as likely to cause somebody a headache due to a dumb\nportability problem or random bitrot as it is to actually find a bug. I\nguess test-extra wouldn't be run by default, but only via CI, so maybe\nthat limits the blast radius sufficiently.\n\nFor diff-highlight in particular, you need to have a working perl, so\nyou'd probably want to at least wrap it with a NO_PERL ifndef. For\nmw-to-git, you need to have MediaWiki::API installed, though I think the\ntests at least notice this and skip everything if you don't.\n\n-Peff\n"},{"id":"443534","messageId":"xmqqilvyybx2.fsf@gitster.g","threadId":"56409","inReplyTo":"4dbca719-a1df-27e9-6e07-dea3f2e2638b@gmail.com","subject":"Re: [RFC/PATCH] Makefile: add test-all target","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-12-08T22:22:33Z","receivedAt":"2021-12-08T22:22:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Derrick Stolee <stolee@gmail.com> writes:\n\n>> +test-extra: all\n>> +\t$(MAKE) -C contrib/diff-highlight test\n>> +\t$(MAKE) -C contrib/mw-to-git test\n>> +\t$(MAKE) -C contrib/subtree test\n>\n> I like how this is obviously extendible to include contrib/scalar\n> in a later change, then remove it when Scalar moves.\n>\n>> +test-all:: test test-extra\n>\n> And this test-all implies that test runs before test-extra, so\n> libgit.a is compiled appropriately.\n\nI do not think this implies the ordering between the main test and\nthe extra test.  \"make test-all\" actually makes a confusing mess on\nthe terminal by conflating outputs from the main test and tests run\nin contrib.\n\nBut because test-extra depends on all, we are keeping the assumption\nthat Makefiles in contrib/ may assume that the primary build has\nalready been done.\n\n>> diff --git i/ci/run-build-and-tests.sh w/ci/run-build-and-tests.sh\n>> index cc62616d80..9da0f26665 100755\n>> --- i/ci/run-build-and-tests.sh\n>> +++ w/ci/run-build-and-tests.sh\n>> @@ -19,7 +19,7 @@ make\n>>  case \"$jobname\" in\n>>  linux-gcc)\n>>  \texport GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n>> -\tmake test\n>> +\tmake test-all\n>\n> Since we are now building and testing things that we have not been\n> testing recently, it is worth checking that we don't have any work\n> to do to make this pass. I assume that you've run 'make test-all'\n> on your own machine. It will be good to see what the full action\n> reports (probably all good).\n\nYes, I am tempted to queue this at the tip of 'seen'.\n\n\n\n"},{"id":"443535","messageId":"xmqqczm6ybrh.fsf@gitster.g","threadId":"56409","inReplyTo":"YbEpE7UI+ovnNQyj@coredump.intra.peff.net","subject":"Re: [RFC/PATCH] Makefile: add test-all target","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-12-08T22:25:54Z","receivedAt":"2021-12-08T22:25:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> For diff-highlight in particular, you need to have a working perl, so\n> you'd probably want to at least wrap it with a NO_PERL ifndef. For\n> mw-to-git, you need to have MediaWiki::API installed, though I think the\n> tests at least notice this and skip everything if you don't.\n\nI know I locally lack MediaWiki::API and saw the test skipped\neverything.  I do not know that would be true on a box without any\nperl, though.\n"},{"id":"443560","messageId":"xmqq8rwuv3i2.fsf@gitster.g","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2112081207320.90@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v10 00/15] Upstreaming the Scalar command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-12-09T03:52:37Z","receivedAt":"2021-12-09T03:52:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> The Scalar Functional Tests were designed with Azure Repos in mind, i.e.\n> they specifically verify that the `gvfs-helper` (emulating Partial Clone\n> using the predecessor of Partial Clone, the GVFS protocol) manages to\n> access the repositories in the intended way.\n> ...\n> I do realize, though, that clarity of intention has been missing from this\n> mail thread all around, so let me ask point blank: Junio, do you want me\n> to include upstreaming `gvfs-helper` in the overall Scalar plan?\n\nSorry, I do not follow.\n\nWhat I was lamenting about was the lack of CI test coverage of stuff\nthat is already being considered to go 'next'.  Specifically, since\ncontrib/scalar/Makefile in 'seen' has a 'test' target, it would be a\nshame not to exercise it, when we should be able to do so in the CI\nfairly easily.\n\nI fail to see what gvfs-helper has to do with anything in the\ncontext of advancing the js/scalar topic as we have today.  If \"The\nScalar Functional Tests\" that were designed with Azure Repos in mind\nis not a good fit to come into contrib/scalar/, it is fine not to\nhave it here---lack of it would not make the test target you have in\ncontrib/scalar/Makefile any less valuable, I would think.\n\nUnless you are saying that \"make -C contrib/scalar test\" is useless,\nthat is.  But I do not think that is the case.\n\n\n\n"},{"id":"443561","messageId":"xmqq35n2v31e.fsf@gitster.g","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2112081217450.90@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v9 00/17] Upstreaming the Scalar command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-12-09T04:02:37Z","receivedAt":"2021-12-09T04:02:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> If I remember correctly, you mentioned quite a couple of times that you\n> expect, particularly oldtimers on this list, to be mindful when\n> contributing patch series, and to delay patches that would interfere with\n> other patch series that are already in flight.\n>\n> I saw with sorrow that this rule was ignored a couple of times recently,\n> even with new contributors, and I sincerely hope that we can unignore that\n> rule again.\n\nSorry, but I am not sure what you are complaining about.\n\nIn general, I do try to ask more experienced and competent folks to\nbear more burden when playing the role of a traffic coordinator, as\nthey are more capable of doing so to help the process.  \n\nRelative importance and complexity of the topics also play a role,\nso it is also possible that a more junior contributor may be asked\nto yield for a more urgent or a less complex topic.\n\nI would give strong preference to things that are already in 'next',\nof course.  There has to be an extraordinary reason before we kick\nsomething out of 'next', only to yield the way to allow another\ntopic to graduate first.\n\nSo, I am not sure if there is a need to unignore any rule here.\n\nThanks.\n\n"},{"id":"443562","messageId":"211209.86fsr2l8vn.gmgdl@evledraar.gmail.com","threadId":"56409","inReplyTo":"xmqq8rwu278d.fsf_-_@gitster.g","subject":"Re: [RFC/PATCH] Makefile: add test-all target","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-12-09T03:44:11Z","receivedAt":"2021-12-09T04:06:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Dec 08 2021, Junio C Hamano wrote:\n\n> We ship contrib/ stuff within our primary source tree but except for\n> the completion scripts that are tested from our primary test suite,\n> their test suites are not run in the CI.\n>\n> Teach the main Makefile a \"test-extra\" target, which goes into each\n> package in contrib/ whose Makefile has its own \"test\" target and\n> runs \"make test\" there.  Add a \"test-all\" target to make it easy to\n> drive both the primary tests and these contrib tests from CI and use\n> it.\n>\n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n>> That is an interesting way to demonstrate how orthogonal the issues\n>> are, which in turn means that it is not such a big deal to add back\n>> the coverage to the part that goes to contrib/scalar/.  As the actual\n>> implementation, it is a bit too icky, though.\n>\n> So, how about doing it this way?  This is based on 'master' and does\n> not cover contrib/scalar, but if we want to go this route, it should\n> be trivial to do it on top of a merge of ab/ci-updates and js/scalar\n> into 'master'.  Good idea?  Terrible idea?  Not good enough?\n\nWith the caveat that I think the greater direction here makes no sense,\ni.e. scalar didn't need its own build system etc. in the first place, so\nhaving hack-upon-hack to fix various integration issues is clearly worse\nthan just having it behave like everything else....\n\n... then yes, adding this to the top-level Makefile makes more sense....\n\n>  Makefile                  | 12 +++++++++++-\n>  ci/run-build-and-tests.sh | 10 +++++-----\n>  2 files changed, 16 insertions(+), 6 deletions(-)\n>\n> diff --git i/Makefile w/Makefile\n> index d56c0e4aad..ca14558e3c 100644\n> --- i/Makefile\n> +++ w/Makefile\n> @@ -2878,10 +2878,20 @@ export TEST_NO_MALLOC_CHECK\n>  test: all\n>  \t$(MAKE) -C t/ all\n>  \n> +# Additional tests from places in contrib/ that are prepared to take\n> +# \"make -C $there test\", but expects that the primary build is done\n> +# already.\n> +test-extra: all\n> +\t$(MAKE) -C contrib/diff-highlight test\n> +\t$(MAKE) -C contrib/mw-to-git test\n> +\t$(MAKE) -C contrib/subtree test\n> +\n> +test-all:: test test-extra\n> +\n>  perf: all\n>  \t$(MAKE) -C t/perf/ all\n>  \n> -.PHONY: test perf\n> +.PHONY: test test-extra test-all perf\n>  \n>  .PRECIOUS: $(TEST_OBJS)\n\nWhich, if we're nitpicking this would be better, i.e. it allows them to\nrun in parallel, as they won't be defined by only one rule, and will be\nlistede individuall in the test-all and test-extra prereqs:\n\ndiff --git a/Makefile b/Makefile\nindex d892dbc6c6e..3f47c9f58ad 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2878,15 +2878,25 @@ export TEST_NO_MALLOC_CHECK\n test: all\n \t$(MAKE) -C t/ all\n \n+define TMPL_test-extra\n+TEST_EXTRA_TARGETS += test-$(1)\n+.PHONY: test-$(1)\n+test-$(1): all\n+\t$$(MAKE) -C $(1) test\n+endef\n+\n # Additional tests from places in contrib/ that are prepared to take\n # \"make -C $there test\", but expects that the primary build is done\n # already.\n-test-extra: all\n-\t$(MAKE) -C contrib/diff-highlight test\n-\t$(MAKE) -C contrib/mw-to-git test\n-\t$(MAKE) -C contrib/subtree test\n+$(eval $(call TMPL_test-extra,contrib/diff-highlight))\n+$(eval $(call TMPL_test-extra,contrib/mw-to-git))\n+$(eval $(call TMPL_test-extra,contrib/subtree))\n+\n+.PHONY: test-extra\n+test-extra:: all $(TEST_EXTRA_TARGETS)\n \n-test-all:: test test-extra\n+.PHONY: test-all\n+test-all: test $(TEST_EXTRA_TARGETS)\n \n perf: all\n \t$(MAKE) -C t/perf/ all\n\n> diff --git i/ci/run-build-and-tests.sh w/ci/run-build-and-tests.sh\n> index cc62616d80..9da0f26665 100755\n> --- i/ci/run-build-and-tests.sh\n> +++ w/ci/run-build-and-tests.sh\n> @@ -19,7 +19,7 @@ make\n>  case \"$jobname\" in\n>  linux-gcc)\n>  \texport GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n> -\tmake test\n> +\tmake test-all\n> [...]\n\nBut I think we're expanding the scope quite a bit here. The reason we\nwere talking about testing scalar by default is because it uses\nlibgit.a, so it's not decoupled at all, whereas the \"contrib\" programs\nare only using the built \"git\" command.\n\nI think it would probably be good to test these anyway, but it's an\nargument beyond that which applies to scalar.\n\nI also share Jeff's general concerns that the other stuff in contrib may\nnot be all that stable.\n\nBut I don't see why we should be pursuing this direction of running\ncertain tests in CI only, as opposed to just under \"make test\", that\ndistinction is something new in js/scalar (before that we run libgit.a\ntest *modes* in CI, but not a different set of tests).\n"},{"id":"443657","messageId":"xmqqczm5u0d4.fsf@gitster.g","threadId":"56409","inReplyTo":"YbEpE7UI+ovnNQyj@coredump.intra.peff.net","subject":"Re: [RFC/PATCH] Makefile: add test-all target","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-12-09T17:57:59Z","receivedAt":"2021-12-09T17:58:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> I don't mind the general direction, but...\n>\n>> +# Additional tests from places in contrib/ that are prepared to take\n>> +# \"make -C $there test\", but expects that the primary build is done\n>> +# already.\n>> +test-extra: all\n>> +\t$(MAKE) -C contrib/diff-highlight test\n>> +\t$(MAKE) -C contrib/mw-to-git test\n>> +\t$(MAKE) -C contrib/subtree test\n>\n> I'm not sure of the quality of tests in some of the contrib stuff. The\n> tests in diff-highlight worked for me when I added them, but it's not\n> like I ever run them regularly, or that they've been tested on a wide\n> variety of platforms.\n>\n> So I think this is as likely to cause somebody a headache due to a dumb\n> portability problem or random bitrot as it is to actually find a bug. I\n> guess test-extra wouldn't be run by default, but only via CI, so maybe\n> that limits the blast radius sufficiently.\n\nYeah, that is the exact thought I had when I did it.  Anybody who is\nnot aware of test target other than 'test' will not be hurt, and we\nexplicitly make the CI aware of 'test-all' to trigger it.  But as\nlong as somebody bothered to write the tests, exercising them to\nreveal bitrot-bugs either in the tested contrib stuff or the tests\nthemselves to be fixed or removed would be a good thing to do.\n\nAn updated version of the posted patch is in 'seen' that also covers\ncredential/netrc; https://github.com/git/git/runs/4465323829 shows\nthe logs from its jobs.\n\nIt is not particularly interesting that most of the jobs are marked\nas failed, as t1092 was broken the same way in my local test.  What\nI found interesting from my quick scan of randomly chosen jobs are\nthat (1) nobody seemed to have failed test-extra, and (2) nobody had\nmediawiki installed to test mw-to-git.\n\nSo I am tempted to do\n\ntest-extra: all\n\t$(MAKE) -C contrib/credential/netrc test\n\t$(MAKE) -C contrib/diff-highlight test\n\t: $(MAKE) -C contrib/mw-to-git test\n\t$(MAKE) -C contrib/subtree test\n\nin the topic itself, while adding\n\n\t$(MAKE) -C contrib/scalar test\n\nbefore the subtree test (alphabetically) when it is merged to 'seen'\nwith the js/scalar topic.\n"},{"id":"443659","messageId":"xmqq4k7htzpk.fsf@gitster.g","threadId":"56409","inReplyTo":"211209.86fsr2l8vn.gmgdl@evledraar.gmail.com","subject":"Re: [RFC/PATCH] Makefile: add test-all target","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-12-09T18:12:07Z","receivedAt":"2021-12-09T18:12:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n>> So, how about doing it this way?  This is based on 'master' and does\n>> not cover contrib/scalar, but if we want to go this route, it should\n>> be trivial to do it on top of a merge of ab/ci-updates and js/scalar\n>> into 'master'.  Good idea?  Terrible idea?  Not good enough?\n>\n> With the caveat that I think the greater direction here makes no sense,\n> i.e. scalar didn't need its own build system etc. in the first place, so\n> having hack-upon-hack to fix various integration issues is clearly worse\n> than just having it behave like everything else....\n\nWe decided to start Scalar in contrib/, as it hasn't been proven\nthat Scalar is in a good enough shape to deserve to be in this tree,\nand we are giving it a chance by adding it to contrib/ first, hoping\nthat it may graduate to the more official status someday [*].\n\nAnd 'test-extra' is a way to give test coverage to things already in\ncontrib/ that has 'test' target in their Makefile.  When js/scalar\ngets merged to a tree with 'test-extra' target, it may be tested in\nthat target, too, because we want to have it behave like everything\nelse.\n\n\n[Footnote]\n\n*1* You may not like the \"try unproven things in contrib/ first and\n    then we may graduate it later\" approach, but that particular\n    ship has sailed and this is not a time to complain and waste\n    project's time.\n\n"},{"id":"443726","messageId":"211210.86a6h9duay.gmgdl@evledraar.gmail.com","threadId":"56409","inReplyTo":"xmqq4k7htzpk.fsf@gitster.g","subject":"Re: [RFC/PATCH] Makefile: add test-all target","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-12-10T02:38:53Z","receivedAt":"2021-12-10T03:15:43Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Dec 09 2021, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>>> So, how about doing it this way?  This is based on 'master' and does\n>>> not cover contrib/scalar, but if we want to go this route, it should\n>>> be trivial to do it on top of a merge of ab/ci-updates and js/scalar\n>>> into 'master'.  Good idea?  Terrible idea?  Not good enough?\n>>\n>> With the caveat that I think the greater direction here makes no sense,\n>> i.e. scalar didn't need its own build system etc. in the first place, so\n>> having hack-upon-hack to fix various integration issues is clearly worse\n>> than just having it behave like everything else....\n>\n> We decided to start Scalar in contrib/, as it hasn't been proven\n> that Scalar is in a good enough shape to deserve to be in this tree,\n> and we are giving it a chance by adding it to contrib/ first, hoping\n> that it may graduate to the more official status someday [*].\n>\n> And 'test-extra' is a way to give test coverage to things already in\n> contrib/ that has 'test' target in their Makefile.  When js/scalar\n> gets merged to a tree with 'test-extra' target, it may be tested in\n> that target, too, because we want to have it behave like everything\n> else.\n>\n> [Footnote]\n>\n\nI'm referring to the divide between testing things in CI v.s. \"make\ntest\" making no sense. Not the path at which scalar is stored in-tree,\nwhich I don't care about, except insofar as it's used as a proxy for\nbehavior that doesn't make sense.\n\nScalar uses libgit.a, and is built by default in the js/scalar topic. So\nwe don't have a choice to really ignore it. E.g. there's part of the\nrefs.h API used only by it. If you're changing that API you need to test\nagainst scalar.\n\nWhich makes sense, and I'd like to have scalar in-tree.\n\nI just don't think it makes any sense that I edit say refs.[ch], run\n\"make test\" locally, but only see that something broke in scalar's\nspecific use of libgit.a later when I look at GitHub CI.\n\nIf you're preparing a series for submission you'll need to get the CI\npassing. Except for portability issues etc. it should be trivial to run\nthe same set of tests locally as we run in CI, that's the case now with\nany change you make to libgit and its consumers.\n\nWith the scalar topic we lose that 1=1 mapping. I don't think doing that\nin the name of it living in contrib makes sense.\n\nIf I'm preparing patches for submission I'll need to get CI passing, so\nI'll need to fix those tests & behavior either way as it's\nin-tree. Knowing about the failures later-not-sooner wastes more time,\nnot less.\n\n> *1* You may not like the \"try unproven things in contrib/ first and\n>     then we may graduate it later\" approach, but that particular\n>     ship has sailed and this is not a time to complain and waste\n>     project's time.\n\nWe have t/t9902-completion.sh testing contrib/completion/, and we run\nthat under \"make test\" and in CI alike, the same goes for\nt/t1021-rerere-in-workdir.sh and contrib/workdir/git-new-workdir, we've\ngot similar (but optional) tests for contrib/credential in t/ too.\n\nThe reason we do that with the completion is because some changes to\ne.g. tweak getopts will need to have a corresponding change to the\ncompletion.\n\nThe reason we've not done that with contrib/{subtree,mw-to-git}/ is\nbecause those are thoroughly in the category of only incidentally being\nin-tree.\n\nI.e. we don't have any reason to think that testing those would stress\ncore features of git itself any more than testing say out-of-tree\nsoftware like git-lfs or git-annex.\n\nTesting those is still interesting, but that's for the benefit of that\nsoftware itself not bitrotting, not that we're likely to introduce\nin-tree breakages by changing an API and missing one of its users.\n\nScalar is thoroughly on the \"completion\" side of that divide, not\n\"subtree\".\n"},{"id":"443752","messageId":"YbMR3ePaJHtYKReP@coredump.intra.peff.net","threadId":"56409","inReplyTo":"xmqqczm5u0d4.fsf@gitster.g","subject":"Re: [RFC/PATCH] Makefile: add test-all target","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-12-10T08:37:49Z","receivedAt":"2021-12-10T08:37:52Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Dec 09, 2021 at 09:57:59AM -0800, Junio C Hamano wrote:\n\n> > So I think this is as likely to cause somebody a headache due to a dumb\n> > portability problem or random bitrot as it is to actually find a bug. I\n> > guess test-extra wouldn't be run by default, but only via CI, so maybe\n> > that limits the blast radius sufficiently.\n> \n> Yeah, that is the exact thought I had when I did it.  Anybody who is\n> not aware of test target other than 'test' will not be hurt, and we\n> explicitly make the CI aware of 'test-all' to trigger it.  But as\n> long as somebody bothered to write the tests, exercising them to\n> reveal bitrot-bugs either in the tested contrib stuff or the tests\n> themselves to be fixed or removed would be a good thing to do.\n\nI'm don't have strong feelings on it either way. But if we think those\ntests are worth running in CI, then...\n\n> So I am tempted to do\n> \n> test-extra: all\n> \t$(MAKE) -C contrib/credential/netrc test\n> \t$(MAKE) -C contrib/diff-highlight test\n> \t: $(MAKE) -C contrib/mw-to-git test\n> \t$(MAKE) -C contrib/subtree test\n\n...we'd probably want to keep running mw-to-git tests, and teach one of\nthe CI environments to install the appropriate perl modules to avoid\nskipping them.\n\n-Peff\n"},{"id":"443754","messageId":"YbMUw70vfxJ+hJW3@coredump.intra.peff.net","threadId":"56409","inReplyTo":"211210.86a6h9duay.gmgdl@evledraar.gmail.com","subject":"Re: [RFC/PATCH] Makefile: add test-all target","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-12-10T08:50:11Z","receivedAt":"2021-12-10T08:50:14Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Dec 10, 2021 at 03:38:53AM +0100, Ævar Arnfjörð Bjarmason wrote:\n\n> I just don't think it makes any sense that I edit say refs.[ch], run\n> \"make test\" locally, but only see that something broke in scalar's\n> specific use of libgit.a later when I look at GitHub CI.\n\nI'm definitely sympathetic to this. Having been surprised by CI failure\non something that worked locally is annoying at best, and downright\nfrustrating when you can't easily reproduce the problem.\n\nBut isn't that already true for most of the value that CI provides?\nWhile part of its purpose may be a back-stop for folks who don't run\n\"make test\" locally, I think the biggest value is that it covers a much\nwider variety of platforms and scenarios that you don't get out of \"make\ntest\" already.\n\nIn some of those cases you can reproduce the problem locally by tweaking\nbuild or test knobs. But in others it can be quite a bit more\nchallenging (e.g., something that segfaults only on Windows). At least\nin the proposed change here you'd only be a \"make test-all\" away from\nreproducing the problem locally.\n\nI dunno. I don't feel that strongly either way about whether scalar\ntests should be part of \"make test\". Mostly just observing that this is\nnot exactly a new case.\n\n> If I'm preparing patches for submission I'll need to get CI passing, so\n> I'll need to fix those tests & behavior either way as it's\n> in-tree. Knowing about the failures later-not-sooner wastes more time,\n> not less.\n\nI think there's probably a tradeoff here. How often you get a \"late\"\nnotification of a bug (and how much of your time that wastes) versus how\nmuch time you spend locally running tests that you don't care about.\n\nI do agree that CI presents a bit of a conundrum for stuff at the edge\nof the project. It's become a de facto requirement for it to pass. In\ngeneral that's good. But it means that features which were introduced\nunder the notion of \"the people who care about this area will tend to\nits maintenance\" slowly become _everybody's_ problem as soon as they\nhave any CI coverage. Another example here is the cmake stuff. Or the\nrecent discussion about \"-x\" and bash.\n\nI wonder if there's a good way to make some CI results informational,\nrather than \"failing\". I.e., run scalar tests via CI, but if you're not\nworking on scalar, you don't have to care. Folks who are interested in\nthe area would keep tabs on those results and make sure that Junio's\ntree stays passing.\n\nThat view disagrees with the final paragraph here, though:\n\n> The reason we do that with the completion is because some changes to\n> e.g. tweak getopts will need to have a corresponding change to the\n> completion.\n> \n> The reason we've not done that with contrib/{subtree,mw-to-git}/ is\n> because those are thoroughly in the category of only incidentally being\n> in-tree.\n> [...]\n> Scalar is thoroughly on the \"completion\" side of that divide, not\n> \"subtree\".\n\nI haven't followed the discussion closely, but in my mind \"scalar\" was\nstill in the \"it may live in-tree for convenience, but people who aren't\nworking on it don't necessarily need to care about it\" camp. Maybe\nthat's not the plan, though.\n\n-Peff\n"},{"id":"443770","messageId":"211210.86tufgdbv1.gmgdl@evledraar.gmail.com","threadId":"56409","inReplyTo":"YbMUw70vfxJ+hJW3@coredump.intra.peff.net","subject":"Re: [RFC/PATCH] Makefile: add test-all target","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-12-10T09:30:12Z","receivedAt":"2021-12-10T09:53:58Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Dec 10 2021, Jeff King wrote:\n\n> On Fri, Dec 10, 2021 at 03:38:53AM +0100, Ævar Arnfjörð Bjarmason wrote:\n>\n>> I just don't think it makes any sense that I edit say refs.[ch], run\n>> \"make test\" locally, but only see that something broke in scalar's\n>> specific use of libgit.a later when I look at GitHub CI.\n>\n> I'm definitely sympathetic to this. Having been surprised by CI failure\n> on something that worked locally is annoying at best, and downright\n> frustrating when you can't easily reproduce the problem.\n>\n> But isn't that already true for most of the value that CI provides?\n> While part of its purpose may be a back-stop for folks who don't run\n> \"make test\" locally, I think the biggest value is that it covers a much\n> wider variety of platforms and scenarios that you don't get out of \"make\n> test\" already.\n>\n> In some of those cases you can reproduce the problem locally by tweaking\n> build or test knobs. But in others it can be quite a bit more\n> challenging (e.g., something that segfaults only on Windows). At least\n> in the proposed change here you'd only be a \"make test-all\" away from\n> reproducing the problem locally.\n>\n> I dunno. I don't feel that strongly either way about whether scalar\n> tests should be part of \"make test\". Mostly just observing that this is\n> not exactly a new case.\n\nYes. I'm not saying that \"make test\" should always run what a full CI\nrun covers.\n\nJust that a proposed change that's really only adding one-more-test-file\ntesting a thing in contrib in the sense that we test\nt/t9902-completion.sh should similarly be part of \"make test\".\n\n>> If I'm preparing patches for submission I'll need to get CI passing, so\n>> I'll need to fix those tests & behavior either way as it's\n>> in-tree. Knowing about the failures later-not-sooner wastes more time,\n>> not less.\n>\n> I think there's probably a tradeoff here. How often you get a \"late\"\n> notification of a bug (and how much of your time that wastes) versus how\n> much time you spend locally running tests that you don't care about.\n>\n> I do agree that CI presents a bit of a conundrum for stuff at the edge\n> of the project. It's become a de facto requirement for it to pass. In\n> general that's good. But it means that features which were introduced\n> under the notion of \"the people who care about this area will tend to\n> its maintenance\" slowly become _everybody's_ problem as soon as they\n> have any CI coverage. Another example here is the cmake stuff. Or the\n> recent discussion about \"-x\" and bash.\n>\n> I wonder if there's a good way to make some CI results informational,\n> rather than \"failing\". I.e., run scalar tests via CI, but if you're not\n> working on scalar, you don't have to care. Folks who are interested in\n> the area would keep tabs on those results and make sure that Junio's\n> tree stays passing.\n\nI think if we're not caring about its failures in combination with\ngit.git changes there wouldn't be much point in having it in-tree at\nall. That would just be like what we've got with git-cinnabar.git.\n\nI would like it in tree. I just don' think the test/CI setup needs to be\na special snowflake.\n\n> That view disagrees with the final paragraph here, though:\n>\n>> The reason we do that with the completion is because some changes to\n>> e.g. tweak getopts will need to have a corresponding change to the\n>> completion.\n>> \n>> The reason we've not done that with contrib/{subtree,mw-to-git}/ is\n>> because those are thoroughly in the category of only incidentally being\n>> in-tree.\n>> [...]\n>> Scalar is thoroughly on the \"completion\" side of that divide, not\n>> \"subtree\".\n>\n> I haven't followed the discussion closely, but in my mind \"scalar\" was\n> still in the \"it may live in-tree for convenience, but people who aren't\n> working on it don't necessarily need to care about it\" camp. Maybe\n> that's not the plan, though.\n\nSince v1 of the series[1] it's been compiled unconditionally, and there\nhave been tests. We just didn't run the tests.\n\nIn v6 the tests started being run as part of CI, which was ejected in\nv10 due to \"[an] unrelated patch series does not interact well with\nthem\", which as I noted upthread in [2] isn't accurate, so I think the\nstated reason for ejecting the CI from the proposed topic doesn't\nreflect reality.\n\nSince then 1d855a6b335 (Merge branch 'ab/ci-updates' into next,\n2021-12-07) landed, so I'd think that any narrow tweaks to get the CI\nworking could be based on top of that topic.\n\n1. https://lore.kernel.org/git/pull.1005.git.1630359290.gitgitgadget@gmail.com/\n2. https://lore.kernel.org/git/211207.86ilw0matb.gmgdl@evledraar.gmail.com/\n"},{"id":"443848","messageId":"nycvar.QRO.7.76.6.2112110003530.90@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"xmqq8rwu278d.fsf_-_@gitster.g","subject":"Re: [RFC/PATCH] Makefile: add test-all target","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-12-10T23:14:49Z","receivedAt":"2021-12-10T23:15:03Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 8 Dec 2021, Junio C Hamano wrote:\n\n> We ship contrib/ stuff within our primary source tree but except for\n> the completion scripts that are tested from our primary test suite,\n> their test suites are not run in the CI.\n>\n> Teach the main Makefile a \"test-extra\" target, which goes into each\n> package in contrib/ whose Makefile has its own \"test\" target and\n> runs \"make test\" there.  Add a \"test-all\" target to make it easy to\n> drive both the primary tests and these contrib tests from CI and use\n> it.\n\nThat sends a strong message that the stuff in contrib/ is now fully under\nyour maintenance, i.e. first-class supported.\n\nIf I were you, I wouldn't.\n\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n> > That is an interesting way to demonstrate how orthogonal the issues\n> > are, which in turn means that it is not such a big deal to add back\n> > the coverage to the part that goes to contrib/scalar/.\n\nI'd rather focus, _some_ focus, on the actual Scalar idea and code.\n\n> > As the actual implementation, it is a bit too icky, though.\n>\n> So, how about doing it this way?  This is based on 'master' and does\n> not cover contrib/scalar, but if we want to go this route, it should\n> be trivial to do it on top of a merge of ab/ci-updates and js/scalar\n> into 'master'.  Good idea?  Terrible idea?  Not good enough?\n\nPeff mentioned a couple of times how tedious it is to address CI failures\ne.g. in the Windows part of Git's CI runs.\n\nSo it makes only sense to avoid the same problem with contrib/scalar/\naltogether, especially as long as you keep saying that you are still\nuncertain whether it will make it into Git as a top-level command.\n\nWhich is a strong argument in favor of just leaving the CI part of\ncontrib/scalar/ out for now, and let it remain _my_ responsibility to\nreact to any build/test problems arising from unrelated patch series\nentering `seen`.\n\nDoing it that way would also have the benefit of allowing more focus on\nthe actual code in contrib/scalar/scalar.c.\n\nNot that it needs more review, I don't think, as both Stolee and Elijah\ngave their thumbs-up already, and I've not received any feedback that\nwould require further changes to `scalar.c`, at least as of _this_ patch\nseries.\n\nCiao,\nDscho\n"},{"id":"443849","messageId":"CABPp-BHbOwXcV-FqNLpCVhLPpw3yWLV0J1MQEB++D_SHb9j7Bw@mail.gmail.com","threadId":"56409","inReplyTo":"xmqq4k7htzpk.fsf@gitster.g","subject":"Re: [RFC/PATCH] Makefile: add test-all target","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-12-10T23:27:52Z","receivedAt":"2021-12-10T23:28:06Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Thu, Dec 9, 2021 at 10:12 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n> >> So, how about doing it this way?  This is based on 'master' and does\n> >> not cover contrib/scalar, but if we want to go this route, it should\n> >> be trivial to do it on top of a merge of ab/ci-updates and js/scalar\n> >> into 'master'.  Good idea?  Terrible idea?  Not good enough?\n> >\n> > With the caveat that I think the greater direction here makes no sense,\n> > i.e. scalar didn't need its own build system etc. in the first place, so\n> > having hack-upon-hack to fix various integration issues is clearly worse\n> > than just having it behave like everything else....\n>\n> We decided to start Scalar in contrib/, as it hasn't been proven\n> that Scalar is in a good enough shape to deserve to be in this tree,\n> and we are giving it a chance by adding it to contrib/ first, hoping\n> that it may graduate to the more official status someday [*].\n\nIs that the hope?  I thought the wish was for it to eventually\n\"disappear\" rather than \"graduate\", as per the following bits of\nDscho's cover letter:\n\n\"\"\"\nThe Scalar project was designed to be a self-destructing vehicle...For\nexample, partial clone, sparse-checkout, and scheduled background\nmaintenance have already been upstreamed and removed from Scalar\nproper...[Adding Scalar to contrib will] make it substantially easier\nto experiment with moving functionality from Scalar into core Git.\n\"\"\"\n"},{"id":"443850","messageId":"nycvar.QRO.7.76.6.2112110015170.90@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"YbMUw70vfxJ+hJW3@coredump.intra.peff.net","subject":"Re: [RFC/PATCH] Makefile: add test-all target","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-12-10T23:43:43Z","receivedAt":"2021-12-10T23:44:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Peff,\n\nOn Fri, 10 Dec 2021, Jeff King wrote:\n\n> On Fri, Dec 10, 2021 at 03:38:53AM +0100, Ævar Arnfjörð Bjarmason wrote:\n>\n> > I just don't think it makes any sense that I edit say refs.[ch], run\n> > \"make test\" locally, but only see that something broke in scalar's\n> > specific use of libgit.a later when I look at GitHub CI.\n>\n> I'm definitely sympathetic to this. Having been surprised by CI failure\n> on something that worked locally is annoying at best, and downright\n> frustrating when you can't easily reproduce the problem.\n\nI feel your frustration. Same here.\n\n> But isn't that already true for most of the value that CI provides?\n> While part of its purpose may be a back-stop for folks who don't run\n> \"make test\" locally, I think the biggest value is that it covers a much\n> wider variety of platforms and scenarios that you don't get out of \"make\n> test\" already.\n>\n> In some of those cases you can reproduce the problem locally by tweaking\n> build or test knobs. But in others it can be quite a bit more\n> challenging (e.g., something that segfaults only on Windows). At least\n> in the proposed change here you'd only be a \"make test-all\" away from\n> reproducing the problem locally.\n>\n> I dunno. I don't feel that strongly either way about whether scalar\n> tests should be part of \"make test\". Mostly just observing that this is\n> not exactly a new case.\n\nIt isn't a new case.\n\nWhat is new is that we are talking about CI for patches targeting contrib/\nspecifically to introduce something cautiously that still has a chance of\nnot ending up in Git proper (for whatever reasons), as Junio seems to\nbe anxious to not give any premature \"go\" to integrate Scalar fully.\n\nIn that light, I am somewhat surprised that we are still discussing\nputting a burden on contributors having to adapt contrib/scalar/ to\ntheir changes, when Junio still endeavors the option of not accepting\nthat to-be-adapted code into core Git, after all.\n\nI fully expected everybody to be on board with leaving the responsibility\nto keep contrib/scalar/ building and passing the tests to _me_, until the\nday Scalar is accepted as a full Git command (which might not happen).\n\n> > If I'm preparing patches for submission I'll need to get CI passing, so\n> > I'll need to fix those tests & behavior either way as it's\n> > in-tree. Knowing about the failures later-not-sooner wastes more time,\n> > not less.\n>\n> I think there's probably a tradeoff here. How often you get a \"late\"\n> notification of a bug (and how much of your time that wastes) versus how\n> much time you spend locally running tests that you don't care about.\n>\n> I do agree that CI presents a bit of a conundrum for stuff at the edge\n> of the project. It's become a de facto requirement for it to pass. In\n> general that's good. But it means that features which were introduced\n> under the notion of \"the people who care about this area will tend to\n> its maintenance\" slowly become _everybody's_ problem as soon as they\n> have any CI coverage. Another example here is the cmake stuff. Or the\n> recent discussion about \"-x\" and bash.\n>\n> I wonder if there's a good way to make some CI results informational,\n> rather than \"failing\". I.e., run scalar tests via CI, but if you're not\n> working on scalar, you don't have to care. Folks who are interested in\n> the area would keep tabs on those results and make sure that Junio's\n> tree stays passing.\n>\n> That view disagrees with the final paragraph here, though:\n>\n> > The reason we do that with the completion is because some changes to\n> > e.g. tweak getopts will need to have a corresponding change to the\n> > completion.\n> >\n> > The reason we've not done that with contrib/{subtree,mw-to-git}/ is\n> > because those are thoroughly in the category of only incidentally being\n> > in-tree.\n> > [...]\n> > Scalar is thoroughly on the \"completion\" side of that divide, not\n> > \"subtree\".\n>\n> I haven't followed the discussion closely, but in my mind \"scalar\" was\n> still in the \"it may live in-tree for convenience, but people who aren't\n> working on it don't necessarily need to care about it\" camp. Maybe\n> that's not the plan, though.\n\nI had hoped for a clearer answer from Junio where he sees Scalar in the\nlong term, for now he seems to be undecided.\n\nAs a consequence, I kept targeting contrib/scalar/ with this first patch\nseries, to leave the door open for keeping it in contrib/ as a \"not\nmaintained by Junio!\" part of the tree.\n\nThat is independent, of course, of my intention to keep maintaining\nScalar's code (once we get a few steps further, that is, because we're\nstill quite stuck here, the Scalar patch series has not seen any concerns\nin the last half dozen iterations about its design nor about its actual\ncode). I intend to keep maintainig the Scalar code no matter whether it\nlives in contrib/ or whether it will be turned into a first-class command\nwhose source code lives in the top-level directory.\n\nSo yes, from my side I do not understand at all where this notion comes\nfrom that contrib/scalar/ should be treated any different than\ncontrib/subtree/ for now. At least until contrib/scalar/ is\nfeature-complete, that won't change.\n\nBut of course, we can keep discussing back and forth the build process of\nScalar, whether it should be tested in CI or not, whether it should be in\ncontrib/ or in the top-level directory or not in Git at all, without\ngetting the Scalar patches anywhere, for the next few years, in which case\nthe outcome of that discussion will be completely moot because the Scalar\npatches would still be as stuck as they are right now. In which case it\nwould be super annoying for any contributor who had to adapt the code in\ncontrib/scalar/ to code changes in libgit.a, for no value in return\nwhatsoever. So far, that contributor has been me.\n\nI sincerely hope that it won't come to that, and that we can move forward\nwith this here patch series, with the next ones I have lined up to make\nScalar feature-complete, and _then_ discuss the merits of making Scalar a\nfirst-class Git command or not. At that point we will automatically have\nthe answer whether to build Scalar and run its tests as part of Git's CI.\n\nCiao,\nDscho\n"},{"id":"443851","messageId":"nycvar.QRO.7.76.6.2112110044010.90@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"xmqq8rwuv3i2.fsf@gitster.g","subject":"Re: [PATCH v10 00/15] Upstreaming the Scalar command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-12-11T00:29:44Z","receivedAt":"2021-12-11T00:29:55Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 8 Dec 2021, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>\n> > The Scalar Functional Tests were designed with Azure Repos in mind, i.e.\n> > they specifically verify that the `gvfs-helper` (emulating Partial Clone\n> > using the predecessor of Partial Clone, the GVFS protocol) manages to\n> > access the repositories in the intended way.\n> > ...\n> > I do realize, though, that clarity of intention has been missing from this\n> > mail thread all around, so let me ask point blank: Junio, do you want me\n> > to include upstreaming `gvfs-helper` in the overall Scalar plan?\n>\n> Sorry, I do not follow.\n\nIn\nhttps://lore.kernel.org/git/CABPp-BGpe9Q5k22Yu8a=1xwu=pZYSeNQoqEgf+DN07cU4EB1ew@mail.gmail.com/\n(i.e. in the great great grand parent of this mail), you specifically\nreplied to my mentioning Scalar's Functional Test suite:\n\n\t> > One other thing is very interesting about that vfs-with-scalar\n\t> > branch thicket: it contains a GitHub workflow which will run\n\t> > Scalar's quite extensive Functional Tests suite. This test\n\t> > suite is quite comprehensive and caught us a lot of bugs in\n\t> > the past, not only in the Scalar code, but also core Git.\n\t>\n\t> From your wording it sounds like the plan might not include\n\t> moving these tests over.  Perhaps it doesn't make sense to move\n\t> them all over, but since they've caught problems in both Scalar\n\t> and core Git, it would be nice to see many of those tests come\n\t> to Git as well as part of a future follow on series.\n\nI had mentioned a couple of times that I had no intention to move Scalar's\nFunction Tests into contrib/scalar/, and your wording \"it would be nice to\nsee many of those tests come to Git as well\" made it sound as if you\ndisagreed with that intention.\n\nBut it was not a clear \"please do port them over\" nor a \"nah, we don't\nwant that test suite implemented in C# and requiring, for the most part,\naccess to a dedicacted Azure Repo\".\n\nHence I was asking for a clear answer to the question whether you want me\nto spend time on preparing a patch series to contribute Scalar's\nFunctional Tests to contrib/scalar/ as well.\n\nI _suspect_ your clear answer, if you are willing to give it as clearly,\nto be \"no, we do not do integration tests here, and besides, C# is not a\nlanguage we want to add to Git's tree\".\n\n> What I was lamenting about was the lack of CI test coverage of stuff\n> that is already being considered to go 'next'.  Specifically, since\n> contrib/scalar/Makefile in 'seen' has a 'test' target, it would be a\n> shame not to exercise it, when we should be able to do so in the CI\n> fairly easily.\n\nWe do have a very different understanding of \"fairly easily\" in that case.\nThree iterations, and three weeks time spent on implementing what you\nsuggest, only to see broken by the merge of the `ab/ci-updates` patch\nseries, suggesting a fixup for the incorrect merge, seeing that fixup\nrejected, and then more discussing, all of that does not strike me as\n\"fairly easily\". It strikes me as \"a lot of time and effort was spent,\nmostly stepping on toes\".\n\nGranted, if `ab/ci-updates` would not have happened, it would have been\nmuch easier. Or if `ab/ci-updates` had waited until `js/scalar` advanced\nto `next`. But the way it happened was (unnecessarily?) un-easy.\n\n> I fail to see what gvfs-helper has to do with anything in the\n> context of advancing the js/scalar topic as we have today.\n\nOkay, okay! I was just asking about gvfs-helper because that would be\nrequired to port over Scalar's Functional Tests. The same Functional Tests\nthat I heard you mentioning would be \"nice to see\" to \"come to Git as\nwell\".\n\n> If \"The Scalar Functional Tests\" that were designed with Azure Repos in\n> mind is not a good fit to come into contrib/scalar/, it is fine not to\n> have it here---lack of it would not make the test target you have in\n> contrib/scalar/Makefile any less valuable, I would think.\n\nThe test target won't go anywhere, no worries. Just like the test target\nin contrib/subtree/ does not go anywhere.\n\nAnd just like `contrib/subtree/`, it does not have to be run as part of\nGit's CI build.\n\n> Unless you are saying that \"make -C contrib/scalar test\" is useless,\n> that is.  But I do not think that is the case.\n\nIt is as useful as `make -C contrib/subtree test`. Which, as Ævar will\nreadily offer, is broken, because it does not ensure that top-level `make\nall` is executed and therefore in a fresh checkout will fail.\n\nOf course, I disagree that it is \"broken\". It works as designed. It is in\nthe contrib/ part of the tree, i.e. safely in the realm of \"you have to\nbuild Git first, and then the thing in contrib/\". In other words, the idea\nto \"fix\" this kind of \"broken\"ness is a solution in search of a problem.\n\nAnd as I have said multiple times, I still think that having Scalar's code\nin contrib/ is a good spot to experiment with it. It sends the right\nsignal of \"this is not really something we promise to maintain just yet\".\nIt is a logical place for code that developers can build themselves, but\nthat is not built and installed with Git by default.\n\nHaving it in the Git tree will give interested developers a chance who\nwant to clone a large repository on Linux, without having to touch\nanything with \"Microsoft\" in its repository name.\n\nHaving it in the Git tree will give interested developers a chance to\nexperiment with things like \"let's try to let `scalar clone` _not_\nclone into `<enlistment>/src/`, but instead create a bare clone in\n`<enlistment>/.git` and make `<enlistment>/src/` a worktree\". Things like\nthat.\n\nI would find those things quite a bit more useful than to force regular\nGit contributors who want to change libgit.a (even if it is just pointless\nrefactoring) to pay attention to contrib/scalar/ in CI, when there is\nstill no clear answer whether Scalar will even become a first-class Git\ncommand eventually (which I hope it will, of course).\n\nCiao,\nDscho\n"},{"id":"443857","messageId":"211211.86mtl7c2b9.gmgdl@evledraar.gmail.com","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2112110044010.90@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v10 00/15] Upstreaming the Scalar command","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-12-11T01:07:32Z","receivedAt":"2021-12-11T02:17:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sat, Dec 11 2021, Johannes Schindelin wrote:\n\n> Hi Junio,\n> [...]\n> We do have a very different understanding of \"fairly easily\" in that case.\n> Three iterations, and three weeks time spent on implementing what you\n> suggest, only to see broken by the merge of the `ab/ci-updates` patch\n> series, suggesting a fixup for the incorrect merge, seeing that fixup\n> rejected, and then more discussing, all of that does not strike me as\n> \"fairly easily\". It strikes me as \"a lot of time and effort was spent,\n> mostly stepping on toes\".\n\nI sent you a working path to a fixup in [1] on the 23rd of November\nwhere we won't go from running zero tests in compile-only to running\njust the scalar test.\n\nJunio replied[2] (\"the above\" referring to [1]):\n\n    I think the above shows that it is a bug in the topic itself,\n\nYou didn't reply further in that fixup thread, and then your v9 re-roll\na week later still had the same issue[3] discussed therein. I again\npointed that out[4]:\n\n    Is it intentional that the previously compile-only \"pedantic\" job is now\n    running the scalar tests?\n\nYou didn't reply, but in your v10 decided to make the current iteration\nof this series have no CI testing at all, and cited the interaction with\nab/ci-updates[4]:\n\n    because a recent unrelated patch series does not interact well with them.\n\nWhich I think is clearly inaccurate, because...\n\n> Granted, if `ab/ci-updates` would not have happened, it would have been\n> much easier. Or if `ab/ci-updates` had waited until `js/scalar` advanced\n> to `next`. But the way it happened was (unnecessarily?) un-easy.\n\n...your initial patch to run the scalar tests in CI[5] was part of v7, and\nhad the issue described above. It pre-dates the v1 of ab/ci-updates\nbeing on-list by a couple of days[6].\n\nSo yes, I do think it was \"easy\", as in that was an easy fix-up. You\njust didn't follow up on it and submitted re-rolls with the already\nnoted breakage.\n\nI don't blame you for that, maybe you were busy, it slipped through\netc.\n\nBut I don't accept that delays in this topic are my fault, or something\nto the effect that that this whole saga represents some failure of the\nreview process.\n\nOur topics textually/semantically conflicted, it happens. I offered a\nfixup & way forward. Fixing it was trivial, and still is. You just\ndidn't follow-up.\n\n> [...]\n>> If \"The Scalar Functional Tests\" that were designed with Azure Repos in\n>> mind is not a good fit to come into contrib/scalar/, it is fine not to\n>> have it here---lack of it would not make the test target you have in\n>> contrib/scalar/Makefile any less valuable, I would think.\n>\n> The test target won't go anywhere, no worries. Just like the test target\n> in contrib/subtree/ does not go anywhere.\n>\n> And just like `contrib/subtree/`, it does not have to be run as part of\n> Git's CI build.\n\nBut unlike contrib/completion, which we do run as part of Git's CI\nbuild[7]?\n\n>> Unless you are saying that \"make -C contrib/scalar test\" is useless,\n>> that is.  But I do not think that is the case.\n>\n> It is as useful as `make -C contrib/subtree test`. Which, as Ævar will\n> readily offer, is broken, because it does not ensure that top-level `make\n> all` is executed and therefore in a fresh checkout will fail.\n\nBefore the scalar topic there was only one \"make\" entry point to build\nlibgit.a, contrib/scalar/Makefile makes that two. That was the immediate\nprompt for the fixup discussion in [1].\n\nSo no, I won't offer that \"make -C contrib/subtree test\" is broken, it\ndoesn't try to build libgit.a and errors out right away if git isn't\nbuilt.\n\nYour scalar patches do try, get most of the way there, and fail.\n\nYour bicycle isn't broken if it doesn't make coffee, but if your fridge\nhas a built-in coffee maker and it doesn't work it's broken, at least as\nit pertains to its coffee making function.\n\nI think I made that distinction clear in [8], but apparently not clear\nenough, as you seem to be under the impression that I was conveying the\nopposite of the idea I was trying to get across.\n\n> Of course, I disagree that it is \"broken\". It works as designed. It is in\n> the contrib/ part of the tree, i.e. safely in the realm of \"you have to\n> build Git first, and then the thing in contrib/\". In other words, the idea\n> to \"fix\" this kind of \"broken\"ness is a solution in search of a problem.\n\nI agree with that, but it's your proposed patches that contain the build\nintegration you're describing as unnecessary for \"contrib/subtree/\". In\nv8->v8 of the series you changed the CI integration from:\n\n    make -C contrib/scalar test\n\nTo:\n\n    make && make -C contrib/scalar test\n\nWhile keeping the bits in contrib/scalar/Makefile that made it go most\nof the way towards a working \"libgit.a\" useful for testing, but it\nbreaks before we get everything we need to run the \"test\" target.\n\nWhich I find to be odd given the above comparison to contib/subtree/. If\nyou have to build git first at the top level why is it trying and\nfailing to build git? \"contrib/subtree\" doesn't.\n\n> [...]\n> I would find those things quite a bit more useful than to force regular\n> Git contributors who want to change libgit.a (even if it is just pointless\n> refactoring) to pay attention to contrib/scalar/ in CI, when there is\n> still no clear answer whether Scalar will even become a first-class Git\n> command eventually (which I hope it will, of course).\n\nIt's in-tree, scalar.c is compiled by default, so they'll have to choice\nbut to pay attention to it.\n\nThe question is whether we should have test and CI coverage for code in\nthat state.\n\n1. https://lore.kernel.org/git/211123.86ilwjujmd.gmgdl@evledraar.gmail.com/\n2. https://lore.kernel.org/git/xmqqo86a92jm.fsf@gitster.g/\n3. https://lore.kernel.org/git/pull.1005.v10.git.1638538470.gitgitgadget@gmail.com/\n4. https://lore.kernel.org/git/211130.861r2xelmx.gmgdl@evledraar.gmail.com/\n5. https://lore.kernel.org/git/1b0328fa236a35c2427b82f53c32944e513580d3.1637158762.git.gitgitgadget@gmail.com/\n6. https://lore.kernel.org/git/cover-0.2-00000000000-20211119T135343Z-avarab@gmail.com/\n7. https://lore.kernel.org/git/211210.86a6h9duay.gmgdl@evledraar.gmail.com/\n8. https://lore.kernel.org/git/211123.86ee77uj18.gmgdl@evledraar.gmail.com/\n9. https://lore.kernel.org/git/pull.1005.v9.git.1638273289.gitgitgadget@gmail.com/\n"},{"id":"443865","messageId":"CABPp-BGt=AcCg-74SSpS7iPFOCKnWE7Q7+CruvALLxKnqXGRuw@mail.gmail.com","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2112110044010.90@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v10 00/15] Upstreaming the Scalar command","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-12-11T05:15:15Z","receivedAt":"2021-12-11T05:15:30Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Hi Dscho,\n\nOn Fri, Dec 10, 2021 at 4:29 PM Johannes Schindelin\n<Johannes.Schindelin@gmx.de> wrote:\n>\n> Hi Junio,\n>\n> On Wed, 8 Dec 2021, Junio C Hamano wrote:\n>\n> > Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> >\n> > > The Scalar Functional Tests were designed with Azure Repos in mind, i.e.\n> > > they specifically verify that the `gvfs-helper` (emulating Partial Clone\n> > > using the predecessor of Partial Clone, the GVFS protocol) manages to\n> > > access the repositories in the intended way.\n> > > ...\n> > > I do realize, though, that clarity of intention has been missing from this\n> > > mail thread all around, so let me ask point blank: Junio, do you want me\n> > > to include upstreaming `gvfs-helper` in the overall Scalar plan?\n> >\n> > Sorry, I do not follow.\n>\n> In\n> https://lore.kernel.org/git/CABPp-BGpe9Q5k22Yu8a=1xwu=pZYSeNQoqEgf+DN07cU4EB1ew@mail.gmail.com/\n> (i.e. in the great great grand parent of this mail), you specifically\n> replied to my mentioning Scalar's Functional Test suite:\n>\n>         > > One other thing is very interesting about that vfs-with-scalar\n>         > > branch thicket: it contains a GitHub workflow which will run\n>         > > Scalar's quite extensive Functional Tests suite. This test\n>         > > suite is quite comprehensive and caught us a lot of bugs in\n>         > > the past, not only in the Scalar code, but also core Git.\n>         >\n>         > From your wording it sounds like the plan might not include\n>         > moving these tests over.  Perhaps it doesn't make sense to move\n>         > them all over, but since they've caught problems in both Scalar\n>         > and core Git, it would be nice to see many of those tests come\n>         > to Git as well as part of a future follow on series.\n\nThis is me and my email you are quoting; these aren't Junio's words.\nI'm afraid my confusion may have snowballed for others here.  Sorry\nabout that.\n\nI simply misunderstood at the time -- I thought there were scalar-only\ntests (rather than scalar+gvfs tests) that were not being considered\nfor upstreaming.  As I mentioned before[1], I'm sorry for the\nconfusion and seemingly opening an unrelated can of worms.  I agree\nthat we don't need gvfs tests, or tests that combine gvfs with other\nthings like scalar, or c# tests.\n\n[1] https://lore.kernel.org/git/CABPp-BFmNiqY=NfN7Ys3XE8wYBn1EQ_War+0QLq96Tk7FO6zfg@mail.gmail.com/\n"},{"id":"443886","messageId":"29e4f791-e543-f90c-8926-b7e7b0f9397c@gmail.com","threadId":"56409","inReplyTo":"xmqq8rwu278d.fsf_-_@gitster.g","subject":"Re: [RFC/PATCH] Makefile: add test-all target","fromName":"Bagas Sanjaya","fromEmail":"bagasdotme@gmail.com","sentAt":"2021-12-11T11:08:59Z","receivedAt":"2021-12-11T11:09:07Z","isPatch":true,"sender":{"key":"bagasdotme@gmail.com","avatar":"https://avatars.githubusercontent.com/u/40219486?v=4"},"body":"On 09/12/21 03.04, Junio C Hamano wrote:\n> We ship contrib/ stuff within our primary source tree but except for\n> the completion scripts that are tested from our primary test suite,\n> their test suites are not run in the CI.\n> \n> Teach the main Makefile a \"test-extra\" target, which goes into each\n> package in contrib/ whose Makefile has its own \"test\" target and\n> runs \"make test\" there.  Add a \"test-all\" target to make it easy to\n> drive both the primary tests and these contrib tests from CI and use\n> it.\n> \n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n\nNo test failures found with test-all on my system.\n\nTested-by: Bagas Sanjaya <bagasdotme@gmail.com>\n\n-- \nAn old man doll... just what I always wanted! - Clara\n"},{"id":"443887","messageId":"nycvar.QRO.7.76.6.2112111445380.90@tvgsbejvaqbjf.bet","threadId":"56409","inReplyTo":"CABPp-BGt=AcCg-74SSpS7iPFOCKnWE7Q7+CruvALLxKnqXGRuw@mail.gmail.com","subject":"Re: [PATCH v10 00/15] Upstreaming the Scalar command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-12-11T13:46:02Z","receivedAt":"2021-12-11T13:46:15Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Elijah,\n\nOn Fri, 10 Dec 2021, Elijah Newren wrote:\n\n> On Fri, Dec 10, 2021 at 4:29 PM Johannes Schindelin\n> <Johannes.Schindelin@gmx.de> wrote:\n> >\n> > On Wed, 8 Dec 2021, Junio C Hamano wrote:\n> >\n> > > Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> > >\n> > > > The Scalar Functional Tests were designed with Azure Repos in mind, i.e.\n> > > > they specifically verify that the `gvfs-helper` (emulating Partial Clone\n> > > > using the predecessor of Partial Clone, the GVFS protocol) manages to\n> > > > access the repositories in the intended way.\n> > > > ...\n> > > > I do realize, though, that clarity of intention has been missing from this\n> > > > mail thread all around, so let me ask point blank: Junio, do you want me\n> > > > to include upstreaming `gvfs-helper` in the overall Scalar plan?\n> > >\n> > > Sorry, I do not follow.\n> >\n> > In\n> > https://lore.kernel.org/git/CABPp-BGpe9Q5k22Yu8a=1xwu=pZYSeNQoqEgf+DN07cU4EB1ew@mail.gmail.com/\n> > (i.e. in the great great grand parent of this mail), you specifically\n> > replied to my mentioning Scalar's Functional Test suite:\n> >\n> >         > > One other thing is very interesting about that vfs-with-scalar\n> >         > > branch thicket: it contains a GitHub workflow which will run\n> >         > > Scalar's quite extensive Functional Tests suite. This test\n> >         > > suite is quite comprehensive and caught us a lot of bugs in\n> >         > > the past, not only in the Scalar code, but also core Git.\n> >         >\n> >         > From your wording it sounds like the plan might not include\n> >         > moving these tests over.  Perhaps it doesn't make sense to move\n> >         > them all over, but since they've caught problems in both Scalar\n> >         > and core Git, it would be nice to see many of those tests come\n> >         > to Git as well as part of a future follow on series.\n>\n> This is me and my email you are quoting; these aren't Junio's words.\n> I'm afraid my confusion may have snowballed for others here.  Sorry\n> about that.\n>\n> I simply misunderstood at the time -- I thought there were scalar-only\n> tests (rather than scalar+gvfs tests) that were not being considered\n> for upstreaming.  As I mentioned before[1], I'm sorry for the\n> confusion and seemingly opening an unrelated can of worms.  I agree\n> that we don't need gvfs tests, or tests that combine gvfs with other\n> things like scalar, or c# tests.\n>\n> [1] https://lore.kernel.org/git/CABPp-BFmNiqY=NfN7Ys3XE8wYBn1EQ_War+0QLq96Tk7FO6zfg@mail.gmail.com/\n\nNo worries, I am glad it is sorted out now.\n\nCiao,\nDscho\n"},{"id":"443964","messageId":"xmqqh7bcgaki.fsf@gitster.g","threadId":"56409","inReplyTo":"nycvar.QRO.7.76.6.2112110003530.90@tvgsbejvaqbjf.bet","subject":"Re: [RFC/PATCH] Makefile: add test-all target","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-12-13T08:42:37Z","receivedAt":"2021-12-13T08:42:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n>> Teach the main Makefile a \"test-extra\" target, which goes into each\n>> package in contrib/ whose Makefile has its own \"test\" target and\n>> runs \"make test\" there.  Add a \"test-all\" target to make it easy to\n>> drive both the primary tests and these contrib tests from CI and use\n>> it.\n>\n> That sends a strong message that the stuff in contrib/ is now fully under\n> your maintenance, i.e. first-class supported.\n\nI do not think running tests on stuff in contrib/ sends any such\nmessage.  It primarily helps _us_ to catch more regressions than we\nmay otherwise miss.  By the way, this is not limited to contrib/; if\nwe had tests for gitk, we would have caught the recent regression in\n\"diff -m\" before it got inflicted on the general public, but that\nwould not have been just to help \"gitk\", but to help keep \"diff -m\"\nsane and stable [*].\n\nBy running tests on in-tree contrib/ like scalar, at least we would\nnotice when we are making breaking changes.  At least, the need for\nscalar (either for the API broken by such a change to be kept\nunchanged or done in a different way, or the code that uses the API\non the scalar side to be updated) would be noticed earlier than\nstuff totally outside and not even in contrib/.\n\nOf course, you have to bear the burden of (A) changing the way\nscalar uses the API, or (B) participating in the design of the\nchange to the API that may break scalar's use so that everybody\nincluding scalar would be happy, or both.  It's not like I am\nresponsible for everything that happens in the tree, and it is our\nshared responsibility to maintain the health of the codebase.  It is\nnot limited to stuff inside or outside contrib/.\n\nThere are projects that want to use libgit.a by binding us as a\nsubmodule and without interacting with us very much.  And they are\non their own when we change the internals.  Do you mean that you\nwant to make scalar into the same status as they are?\n\n> Not that it needs more review, I don't think, as both Stolee and Elijah\n> gave their thumbs-up already, and I've not received any feedback that\n> would require further changes to `scalar.c`, at least as of _this_ patch\n> series.\n\nSo that argues even more to have a way to make sure we catch\nunintended breakages by any future mindless tree-wide \"clean-ups\"\nand interface changes, no?\n\n\n[Footnote]\n\n* I just double checked the candidates for \"test-extra\" to see if\n  they are meant to run with a random Git they happen to see on the\n  $PATH, or they are designed to test with the version of Git we\n  just built, and it seems it is the latter for the ones nominated\n  in the test-extra patch.  Otherwise it would indeed reduce the\n  benefit in half---we are not helping to catch regressions in the\n  core stuff in such a case.\n"},{"id":"443968","messageId":"xmqqzgp4eum2.fsf@gitster.g","threadId":"56409","inReplyTo":"CABPp-BHbOwXcV-FqNLpCVhLPpw3yWLV0J1MQEB++D_SHb9j7Bw@mail.gmail.com","subject":"Re: [RFC/PATCH] Makefile: add test-all target","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-12-13T09:12:37Z","receivedAt":"2021-12-13T09:12:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n> On Thu, Dec 9, 2021 at 10:12 AM Junio C Hamano <gitster@pobox.com> wrote:\n>>\n>> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>>\n>> >> So, how about doing it this way?  This is based on 'master' and does\n>> >> not cover contrib/scalar, but if we want to go this route, it should\n>> >> be trivial to do it on top of a merge of ab/ci-updates and js/scalar\n>> >> into 'master'.  Good idea?  Terrible idea?  Not good enough?\n>> >\n>> > With the caveat that I think the greater direction here makes no sense,\n>> > i.e. scalar didn't need its own build system etc. in the first place, so\n>> > having hack-upon-hack to fix various integration issues is clearly worse\n>> > than just having it behave like everything else....\n>>\n>> We decided to start Scalar in contrib/, as it hasn't been proven\n>> that Scalar is in a good enough shape to deserve to be in this tree,\n>> and we are giving it a chance by adding it to contrib/ first, hoping\n>> that it may graduate to the more official status someday [*].\n>\n> Is that the hope?  I thought the wish was for it to eventually\n> \"disappear\" rather than \"graduate\", as per the following bits of\n> Dscho's cover letter:\n>\n> \"\"\"\n> The Scalar project was designed to be a self-destructing vehicle...For\n> example, partial clone, sparse-checkout, and scheduled background\n> maintenance have already been upstreamed and removed from Scalar\n> proper...[Adding Scalar to contrib will] make it substantially easier\n> to experiment with moving functionality from Scalar into core Git.\n> \"\"\"\n\nI can go either way, but my impression from Dscho's messages has\nalways been that there is no strong reason to switch existing scalar\nusers to say \"git clone <options that give behaviour like scalar>\"\nwhen their fingers and scripts are used to say \"scalar <this>\", and\na very thin shell may remain in some form in the ideal world.\n\n\n"},{"id":"443969","messageId":"xmqqtufceulz.fsf@gitster.g","threadId":"56409","inReplyTo":"YbMR3ePaJHtYKReP@coredump.intra.peff.net","subject":"Re: [RFC/PATCH] Makefile: add test-all target","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-12-13T09:12:40Z","receivedAt":"2021-12-13T09:12:47Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> I'm don't have strong feelings on it either way. But if we think those\n> tests are worth running in CI, then...\n>\n>> So I am tempted to do\n>> \n>> test-extra: all\n>> \t$(MAKE) -C contrib/credential/netrc test\n>> \t$(MAKE) -C contrib/diff-highlight test\n>> \t: $(MAKE) -C contrib/mw-to-git test\n>> \t$(MAKE) -C contrib/subtree test\n>\n> ...we'd probably want to keep running mw-to-git tests, and teach one of\n> the CI environments to install the appropriate perl modules to avoid\n> skipping them.\n\nI saw netrc credential helper break on one of the jobs that lack\nPerl, so the test there needs to be fixed before we can include it\nin test-extra.\n\n\n\n"},{"id":"444102","messageId":"YbiZKYFR8kilit+0@coredump.intra.peff.net","threadId":"56409","inReplyTo":"xmqqh7bcgaki.fsf@gitster.g","subject":"Re: [RFC/PATCH] Makefile: add test-all target","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-12-14T13:16:25Z","receivedAt":"2021-12-14T13:16:28Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Dec 13, 2021 at 12:42:37AM -0800, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> >> Teach the main Makefile a \"test-extra\" target, which goes into each\n> >> package in contrib/ whose Makefile has its own \"test\" target and\n> >> runs \"make test\" there.  Add a \"test-all\" target to make it easy to\n> >> drive both the primary tests and these contrib tests from CI and use\n> >> it.\n> >\n> > That sends a strong message that the stuff in contrib/ is now fully under\n> > your maintenance, i.e. first-class supported.\n> \n> I do not think running tests on stuff in contrib/ sends any such\n> message.  It primarily helps _us_ to catch more regressions than we\n> may otherwise miss.  By the way, this is not limited to contrib/; if\n> we had tests for gitk, we would have caught the recent regression in\n> \"diff -m\" before it got inflicted on the general public, but that\n> would not have been just to help \"gitk\", but to help keep \"diff -m\"\n> sane and stable [*].\n\nI'd actually be a lot more sympathetic to automatically running gitk\ntests, because it's just consuming the public API of git (i.e., the\nscriptable plumbing interface). If we accidentally break that, it is the\nproblem of the person who made the breaking change, and we would want\nthem to know it as soon as possible.\n\nWith something like scalar, though, it is adding new callers of the\nprivate API. It might be useful for somebody doing tree-wide refactoring\nto know they've broken something there. But it might also be a hassle,\nbecause now they have to care about fixing it, if they are interested in\nun-breaking their build (or un-breaking CI). The scalar code is now\ntheir problem, even though it's \"just\" in contrib/.\n\nIn other words, it comes down to a question of where the burden for\nfixing things lies. Of course it is nice if somebody doing tree-wide\nrefactoring fixes up scalar, too. But by making it optional to build\nand/or test stuff in contrib/ (rather than tying it to \"make all\" or to\nCI), it lets people decide how nice they want to be.\n\nFor other stuff in contrib/, I'm not sure to what degree it applies.\ndiff-highlight is pretty standalone for instance. I guess it _could_ be\nbroken by a public-API change in Git, but I find it pretty unlikely.\n\n> Of course, you have to bear the burden of (A) changing the way\n> scalar uses the API, or (B) participating in the design of the\n> change to the API that may break scalar's use so that everybody\n> including scalar would be happy, or both.  It's not like I am\n> responsible for everything that happens in the tree, and it is our\n> shared responsibility to maintain the health of the codebase.  It is\n> not limited to stuff inside or outside contrib/.\n> \n> There are projects that want to use libgit.a by binding us as a\n> submodule and without interacting with us very much.  And they are\n> on their own when we change the internals.  Do you mean that you\n> want to make scalar into the same status as they are?\n\nI kind of thought that final paragraph was the plan, at least to start\nwith.\n\n-Peff\n"},{"id":"444103","messageId":"YbiZiqLQnFkxLMAA@coredump.intra.peff.net","threadId":"56409","inReplyTo":"YbiZKYFR8kilit+0@coredump.intra.peff.net","subject":"Re: [RFC/PATCH] Makefile: add test-all target","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-12-14T13:18:02Z","receivedAt":"2021-12-14T13:18:05Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Dec 14, 2021 at 08:16:26AM -0500, Jeff King wrote:\n\n> > There are projects that want to use libgit.a by binding us as a\n> > submodule and without interacting with us very much.  And they are\n> > on their own when we change the internals.  Do you mean that you\n> > want to make scalar into the same status as they are?\n> \n> I kind of thought that final paragraph was the plan, at least to start\n> with.\n\nOh, and just to be clear: I am really OK with either direction. I'm only\nclaiming that I think both approaches are self-consistent and are making\na tradeoff (finding bugs earlier, versus shifting burden of bug-fixing\naround).\n\n-Peff\n"}]}