зеркало из https://github.com/microsoft/git.git
Merge branch 'scalar'
This merges the upstreamable part of the Scalar patches. Minor merge conflicts (caused by the gvfs-helper) were resolved trivially. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This commit is contained in:
Коммит
f4b6ff78f5
|
@ -194,6 +194,7 @@
|
|||
/config-list.h
|
||||
/command-list.h
|
||||
/hook-list.h
|
||||
/scalar
|
||||
*.tar.gz
|
||||
*.dsc
|
||||
*.deb
|
||||
|
|
|
@ -22,6 +22,8 @@ MAN1_TXT += git.txt
|
|||
MAN1_TXT += gitk.txt
|
||||
MAN1_TXT += gitweb.txt
|
||||
|
||||
MAN1_TXT += scalar.txt
|
||||
|
||||
# man5 / man7 guides (note: new guides should also be added to command-list.txt)
|
||||
MAN5_TXT += gitattributes.txt
|
||||
MAN5_TXT += githooks.txt
|
||||
|
|
|
@ -791,3 +791,12 @@ core.abbrev::
|
|||
If set to "no", no abbreviation is made and the object names
|
||||
are shown in their full length.
|
||||
The minimum length is 4.
|
||||
|
||||
core.configWriteLockTimeoutMS::
|
||||
When processes try to write to the config concurrently, it is likely
|
||||
that one process "wins" and the other process(es) fail to lock the
|
||||
config file. By configuring a timeout larger than zero, Git can be
|
||||
told to try to lock the config again a couple times within the
|
||||
specified timeout. If the timeout is configure to zero (which is the
|
||||
default), Git will fail immediately when the config is already
|
||||
locked.
|
||||
|
|
|
@ -162,6 +162,6 @@ SEE ALSO
|
|||
--------
|
||||
linkgit:git-clone[1], linkgit:git-maintenance[1].
|
||||
|
||||
Scalar
|
||||
GIT
|
||||
---
|
||||
Associated with the linkgit:git[1] suite
|
||||
Part of the linkgit:git[1] suite
|
|
@ -0,0 +1,51 @@
|
|||
Frequently Asked Questions
|
||||
==========================
|
||||
|
||||
Using Scalar
|
||||
------------
|
||||
|
||||
### I don't want a sparse clone, I want every file after I clone!
|
||||
|
||||
Run `scalar clone --full-clone <url>` to initialize your repo to include
|
||||
every file. You can switch to a sparse-checkout later by running
|
||||
`git sparse-checkout init --cone`.
|
||||
|
||||
### I already cloned without `--full-clone`. How do I get everything?
|
||||
|
||||
Run `git sparse-checkout disable`.
|
||||
|
||||
Scalar Design Decisions
|
||||
-----------------------
|
||||
|
||||
There may be many design decisions within Scalar that are confusing at first
|
||||
glance. Some of them may cause friction when you use Scalar with your existing
|
||||
repos and existing habits.
|
||||
|
||||
> Scalar has the most benefit when users design repositories
|
||||
> with efficient patterns.
|
||||
|
||||
For example: Scalar uses the sparse-checkout feature to limit the size of the
|
||||
working directory within a large monorepo. It is designed to work efficiently
|
||||
with monorepos that are highly componentized, allowing most developers to
|
||||
need many fewer files in their daily work.
|
||||
|
||||
### Why does `scalar clone` create a `<repo>/src` folder?
|
||||
|
||||
Scalar uses a file system watcher to keep track of changes under this `src` folder.
|
||||
Any activity in this folder is assumed to be important to Git operations. By
|
||||
creating the `src` folder, we are making it easy for your build system to
|
||||
create output folders outside the `src` directory. We commonly see systems
|
||||
create folders for build outputs and package downloads. Scalar itself creates
|
||||
these folders during its builds.
|
||||
|
||||
Your build system may create build artifacts such as `.obj` or `.lib` files
|
||||
next to your source code. These are commonly "hidden" from Git using
|
||||
`.gitignore` files. Having such artifacts in your source tree creates
|
||||
additional work for Git because it needs to look at these files and match them
|
||||
against the `.gitignore` patterns.
|
||||
|
||||
By following the `src` pattern Scalar tries to establish and placing your build
|
||||
intermediates and outputs parallel with the `src` folder and not inside it,
|
||||
you can help optimize Git command performance for developers in the repository
|
||||
by limiting the number of files Git needs to consider for many common
|
||||
operations.
|
|
@ -0,0 +1,98 @@
|
|||
Getting Started
|
||||
===============
|
||||
|
||||
Registering existing Git repos
|
||||
------------------------------
|
||||
|
||||
To add a repository to the list of registered repos, run `scalar register [<path>]`.
|
||||
If `<path>` is not provided, then the "current repository" is discovered from
|
||||
the working directory by scanning the parent paths for a path containing a `.git`
|
||||
folder, possibly inside a `src` folder.
|
||||
|
||||
To see which repositories are currently tracked by the service, run
|
||||
`scalar list`.
|
||||
|
||||
Run `scalar unregister [<path>]` to remove the repo from this list.
|
||||
|
||||
Creating a new Scalar clone
|
||||
---------------------------------------------------
|
||||
|
||||
The `clone` verb creates a local enlistment of a remote repository using the
|
||||
partial clone feature available e.g. on GitHub.
|
||||
|
||||
|
||||
```
|
||||
scalar clone [options] <url> [<dir>]
|
||||
```
|
||||
|
||||
Create a local copy of the repository at `<url>`. If specified, create the `<dir>`
|
||||
directory and place the repository there. Otherwise, the last section of the `<url>`
|
||||
will be used for `<dir>`.
|
||||
|
||||
At the end, the repo is located at `<dir>/src`. By default, the sparse-checkout
|
||||
feature is enabled and the only files present are those in the root of your
|
||||
Git repository. Use `git sparse-checkout set` to expand the set of directories
|
||||
you want to see, or `git sparse-checkout disable` to expand to all files. You
|
||||
can explore the subdirectories outside your sparse-checkout specification using
|
||||
`git ls-tree HEAD`.
|
||||
|
||||
### Sparse Repo Mode
|
||||
|
||||
By default, Scalar reduces your working directory to only the files at the
|
||||
root of the repository. You need to add the folders you care about to build up
|
||||
to your working set.
|
||||
|
||||
* `scalar clone <url>`
|
||||
* Please choose the **Clone with HTTPS** option in the `Clone Repository` dialog in Azure Repos, not **Clone with SSH**.
|
||||
* `cd <root>\src`
|
||||
* At this point, your `src` directory only contains files that appear in your root
|
||||
tree. No folders are populated.
|
||||
* Set the directory list for your sparse-checkout using:
|
||||
1. `git sparse-checkout set <dir1> <dir2> ...`
|
||||
2. `git sparse-checkout set --stdin < dir-list.txt`
|
||||
* Run git commands as you normally would.
|
||||
* To fully populate your working directory, run `git sparse-checkout disable`.
|
||||
|
||||
If instead you want to start with all files on-disk, you can clone with the
|
||||
`--full-clone` option. To enable sparse-checkout after the fact, run
|
||||
`git sparse-checkout init --cone`. This will initialize your sparse-checkout
|
||||
patterns to only match the files at root.
|
||||
|
||||
If you are unfamiliar with what directories are available in the repository,
|
||||
then you can run `git ls-tree -d --name-only HEAD` to discover the directories
|
||||
at root, or `git ls-tree -d --name-only HEAD <path>` to discover the directories
|
||||
in `<path>`.
|
||||
|
||||
### Options
|
||||
|
||||
These options allow a user to customize their initial enlistment.
|
||||
|
||||
* `--full-clone`: If specified, do not initialize the sparse-checkout feature.
|
||||
All files will be present in your `src` directory. This uses a Git partial
|
||||
clone: blobs are downloaded on demand.
|
||||
|
||||
* `--branch=<ref>`: Specify the branch to checkout after clone.
|
||||
|
||||
### Advanced Options
|
||||
|
||||
The options below are not intended for use by a typical user. These are
|
||||
usually used by build machines to create a temporary enlistment that
|
||||
operates on a single commit.
|
||||
|
||||
* `--single-branch`: Use this option to only download metadata for the branch
|
||||
that will be checked out. This is helpful for build machines that target
|
||||
a remote with many branches. Any `git fetch` commands after the clone will
|
||||
still ask for all branches.
|
||||
|
||||
* `--no-prefetch`: Use this option to not prefetch commits after clone. This
|
||||
is not recommended for anyone planning to use their clone for history
|
||||
traversal. Use of this option will make commands like `git log` or
|
||||
`git pull` extremely slow and is therefore not recommended.
|
||||
|
||||
Removing a Scalar Clone
|
||||
-----------------------
|
||||
|
||||
Since the `scalar clone` command sets up a file-system watcher (when available),
|
||||
that watcher could prevent deleting the enlistment. Run `scalar delete <path>`
|
||||
from outside of your enlistment to unregister the enlistment from the filesystem
|
||||
watcher and delete the enlistment at `<path>`.
|
|
@ -0,0 +1,50 @@
|
|||
Scalar: Enabling Git at Scale
|
||||
=============================
|
||||
|
||||
Scalar is a tool that helps Git scale to some of the largest Git repositories.
|
||||
It achieves this by enabling some advanced Git features, such as:
|
||||
|
||||
* *Partial clone:* reduces time to get a working repository by not
|
||||
downloading all Git objects right away.
|
||||
|
||||
* *Background prefetch:* downloads Git object data from all remotes every
|
||||
hour, reducing the amount of time for foreground `git fetch` calls.
|
||||
|
||||
* *Sparse-checkout:* limits the size of your working directory.
|
||||
|
||||
* *File system monitor:* tracks the recently modified files and eliminates
|
||||
the need for Git to scan the entire worktree.
|
||||
|
||||
* *Commit-graph:* accelerates commit walks and reachability calculations,
|
||||
speeding up commands like `git log`.
|
||||
|
||||
* *Multi-pack-index:* enables fast object lookups across many pack-files.
|
||||
|
||||
* *Incremental repack:* Repacks the packed Git data into fewer pack-file
|
||||
without disrupting concurrent commands by using the multi-pack-index.
|
||||
|
||||
By running `scalar register` in any Git repo, Scalar will automatically enable
|
||||
these features for that repo (except partial clone) and start running suggested
|
||||
maintenance in the background using
|
||||
[the `git maintenance` feature](https://git-scm.com/docs/git-maintenance).
|
||||
|
||||
Repos cloned with the `scalar clone` command use partial clone to significantly
|
||||
reduce the amount of data required to get started using a repository. By
|
||||
delaying all blob downloads until they are required, Scalar allows you to work
|
||||
with very large repositories quickly.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
* [Getting Started](getting-started.md): Get started with Scalar.
|
||||
Includes `scalar register`, `scalar unregister`, `scalar clone`, and
|
||||
`scalar delete`.
|
||||
|
||||
* [Troubleshooting](troubleshooting.md):
|
||||
Collect diagnostic information or update custom settings. Includes
|
||||
`scalar diagnose`.
|
||||
|
||||
* [The Philosophy of Scalar](philosophy.md): Why does Scalar work the way
|
||||
it does, and how do we make decisions about its future?
|
||||
|
||||
* [Frequently Asked Questions](faq.md)
|
|
@ -0,0 +1,66 @@
|
|||
The Philosophy of Scalar
|
||||
========================
|
||||
|
||||
The team building Scalar has **opinions** about Git performance. Scalar
|
||||
takes out the guesswork by automatically configuring your Git repositories
|
||||
to take advantage of the latest and greatest features. It is difficult to
|
||||
say that these are the absolute best settings for every repository, but
|
||||
these settings do work for some of the largest repositories in the world.
|
||||
|
||||
Scalar intends to do very little more than the standard Git client. We
|
||||
actively implement new features into Git instead of Scalar, then update
|
||||
Scalar only to configure those new settings. In particular, we ported
|
||||
features like background maintenance to Git to make Scalar simpler and
|
||||
make Git more powerful.
|
||||
|
||||
Services such as GitHub support partial clone , a standard adopted by the Git
|
||||
project to download only part of the Git objects when cloning, and fetching
|
||||
further objects on demand. If your hosting service supports partial clone, then
|
||||
we absolutely recommend it as a way to greatly speed up your clone and fetch
|
||||
times and to reduce how much disk space your Git repository requires. Scalar
|
||||
will help with this!
|
||||
|
||||
Most of the value of Scalar can be found in the core Git client. However, most
|
||||
of the advanced features that really optimize Git's performance are off by
|
||||
default for compatibility reasons. To really take advantage of Git's latest and
|
||||
greatest features, you either need to study the [`git config`
|
||||
documentation](https://git-scm.com/docs/git-config) and regularly read [the Git
|
||||
release notes](https://github.com/git/git/tree/master/Documentation/RelNotes).
|
||||
Even if you do all that work and customize your Git settings on your machines,
|
||||
you likely will want to share those settings with other team members. Or, you
|
||||
can just use Scalar!
|
||||
|
||||
Using `scalar register` on an existing Git repository will give you these
|
||||
benefits:
|
||||
|
||||
* Additional compression of your `.git/index` file.
|
||||
* Hourly background `git fetch` operations, keeping you in-sync with your
|
||||
remotes.
|
||||
* Advanced data structures, such as the `commit-graph` and `multi-pack-index`
|
||||
are updated automatically in the background.
|
||||
* If using macOS or Windows, then Scalar configures Git's builtin File System
|
||||
Monitor, providing faster commands such as `git status` or `git add`.
|
||||
|
||||
Additionally, if you use `scalar clone` to create a new repository, then
|
||||
you will automatically get these benefits:
|
||||
|
||||
* Use Git's partial clone feature to only download the files you need for
|
||||
your current checkout.
|
||||
* Use Git's [sparse-checkout feature][sparse-checkout] to minimize the
|
||||
number of files required in your working directory.
|
||||
[Read more about sparse-checkout here.][sparse-checkout-blog]
|
||||
* Create the Git repository inside `<repo-name>/src` to make it easy to
|
||||
place build artifacts outside of the Git repository, such as in
|
||||
`<repo-name>/bin` or `<repo-name>/packages`.
|
||||
|
||||
We also admit that these **opinions** can always be improved! If you have
|
||||
an idea of how to improve our setup, consider
|
||||
[creating an issue](https://github.com/microsoft/scalar/issues/new) or
|
||||
contributing a pull request! Some [existing](https://github.com/microsoft/scalar/issues/382)
|
||||
[issues](https://github.com/microsoft/scalar/issues/388) have already
|
||||
improved our configuration settings and roadmap!
|
||||
|
||||
[gvfs-protocol]: https://github.com/microsoft/VFSForGit/blob/HEAD/Protocol.md
|
||||
[microsoft-git]: https://github.com/microsoft/git
|
||||
[sparse-checkout]: https://git-scm.com/docs/git-sparse-checkout
|
||||
[sparse-checkout-blog]: https://github.blog/2020-01-17-bring-your-monorepo-down-to-size-with-sparse-checkout/
|
|
@ -0,0 +1,20 @@
|
|||
Troubleshooting
|
||||
===============
|
||||
|
||||
Diagnosing Issues
|
||||
-----------------
|
||||
|
||||
The `scalar diagnose` command collects logs and config details for the current
|
||||
repository. The resulting zip file helps root-cause issues.
|
||||
|
||||
When run inside your repository, creates a zip file containing several important
|
||||
files for that repository. This includes:
|
||||
|
||||
* Configuration files from your `.git` folder, such as the `config` file,
|
||||
`index`, `hooks`, and `refs`.
|
||||
|
||||
* A summary of your Git object database, including the number of loose objects
|
||||
and the names and sizes of pack-files.
|
||||
|
||||
As the `diagnose` command completes, it provides the path of the resulting
|
||||
zip file. This zip can be attached to bug reports to make the analysis easier.
|
17
Makefile
17
Makefile
|
@ -692,6 +692,11 @@ all:: $(FUZZ_OBJS)
|
|||
|
||||
FUZZ_PROGRAMS += $(patsubst %.o,%,$(FUZZ_OBJS))
|
||||
|
||||
SCALAR_OBJS := scalar.o
|
||||
|
||||
PROGRAMS += scalar$(X)
|
||||
BINDIR_PROGRAMS_NEED_X += scalar
|
||||
|
||||
# Empty...
|
||||
EXTRA_PROGRAMS =
|
||||
|
||||
|
@ -2547,6 +2552,7 @@ OBJECTS += $(GIT_OBJS)
|
|||
OBJECTS += $(PROGRAM_OBJS)
|
||||
OBJECTS += $(TEST_OBJS)
|
||||
OBJECTS += $(XDIFF_OBJS)
|
||||
OBJECTS += $(SCALAR_OBJS)
|
||||
OBJECTS += $(FUZZ_OBJS)
|
||||
OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)
|
||||
|
||||
|
@ -2554,10 +2560,6 @@ ifndef NO_CURL
|
|||
OBJECTS += http.o http-walker.o remote-curl.o
|
||||
endif
|
||||
|
||||
SCALAR_SOURCES := contrib/scalar/scalar.c
|
||||
SCALAR_OBJECTS := $(SCALAR_SOURCES:c=o)
|
||||
OBJECTS += $(SCALAR_OBJECTS)
|
||||
|
||||
.PHONY: objects
|
||||
objects: $(OBJECTS)
|
||||
|
||||
|
@ -2696,9 +2698,8 @@ $(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS
|
|||
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
|
||||
$(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)
|
||||
|
||||
contrib/scalar/scalar$X: $(SCALAR_OBJECTS) GIT-LDFLAGS $(GITLIBS)
|
||||
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \
|
||||
$(filter %.o,$^) $(LIBS)
|
||||
scalar$X: $(SCALAR_OBJS) GIT-LDFLAGS $(GITLIBS)
|
||||
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
|
||||
|
||||
git-gvfs-helper$X: gvfs-helper.o http.o GIT-LDFLAGS $(GITLIBS)
|
||||
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
|
||||
|
@ -3071,7 +3072,7 @@ bin-wrappers/%: wrap-for-bin.sh
|
|||
$(call mkdir_p_parent_template)
|
||||
$(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
|
||||
-e 's|@@BUILD_DIR@@|$(shell pwd)|' \
|
||||
-e 's|@@PROG@@|$(patsubst test-%,t/helper/test-%$(X),$(@F))$(patsubst git%,$(X),$(filter $(@F),$(BINDIR_PROGRAMS_NEED_X)))|' < $< > $@ && \
|
||||
-e 's|@@PROG@@|$(patsubst test-%,t/helper/test-%$(X),$(@F))$(patsubst scalar,$(X),$(patsubst git%,$(X),$(filter $(@F),$(BINDIR_PROGRAMS_NEED_X))))|' < $< > $@ && \
|
||||
chmod +x $@
|
||||
|
||||
# GNU make supports exporting all variables by "export" without parameters.
|
||||
|
|
|
@ -430,6 +430,8 @@ static const char *cmd_to_page(const char *git_cmd)
|
|||
return git_cmd;
|
||||
else if (is_git_command(git_cmd))
|
||||
return xstrfmt("git-%s", git_cmd);
|
||||
else if (!strcmp("scalar", git_cmd))
|
||||
return xstrdup(git_cmd);
|
||||
else
|
||||
return xstrfmt("git%s", git_cmd);
|
||||
}
|
||||
|
|
8
config.c
8
config.c
|
@ -3244,6 +3244,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
|
|||
const char *value_pattern,
|
||||
unsigned flags)
|
||||
{
|
||||
static unsigned long timeout_ms = ULONG_MAX;
|
||||
int fd = -1, in_fd = -1;
|
||||
int ret;
|
||||
struct lock_file lock = LOCK_INIT;
|
||||
|
@ -3264,11 +3265,16 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
|
|||
if (!config_filename)
|
||||
config_filename = filename_buf = git_pathdup("config");
|
||||
|
||||
if ((long)timeout_ms < 0 &&
|
||||
git_config_get_ulong("core.configWriteLockTimeoutMS", &timeout_ms))
|
||||
timeout_ms = 0;
|
||||
|
||||
/*
|
||||
* The lock serves a purpose in addition to locking: the new
|
||||
* contents of .git/config will be written into it.
|
||||
*/
|
||||
fd = hold_lock_file_for_update(&lock, config_filename, 0);
|
||||
fd = hold_lock_file_for_update_timeout(&lock, config_filename, 0,
|
||||
timeout_ms);
|
||||
if (fd < 0) {
|
||||
error_errno(_("could not lock config file %s"), config_filename);
|
||||
ret = CONFIG_NO_LOCK;
|
||||
|
|
|
@ -792,6 +792,9 @@ target_link_libraries(git-sh-i18n--envsubst common-main)
|
|||
add_executable(git-shell ${CMAKE_SOURCE_DIR}/shell.c)
|
||||
target_link_libraries(git-shell common-main)
|
||||
|
||||
add_executable(scalar ${CMAKE_SOURCE_DIR}/scalar.c)
|
||||
target_link_libraries(scalar common-main)
|
||||
|
||||
if(CURL_FOUND)
|
||||
add_library(http_obj OBJECT ${CMAKE_SOURCE_DIR}/http.c)
|
||||
|
||||
|
@ -1015,7 +1018,7 @@ endif()
|
|||
|
||||
#wrapper scripts
|
||||
set(wrapper_scripts
|
||||
git git-upload-pack git-receive-pack git-upload-archive git-shell git-remote-ext)
|
||||
git git-upload-pack git-receive-pack git-upload-archive git-shell git-remote-ext scalar)
|
||||
|
||||
set(wrapper_test_scripts
|
||||
test-fake-ssh test-tool)
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
/*.exe
|
||||
/scalar
|
|
@ -1,35 +0,0 @@
|
|||
# The default target of this Makefile is...
|
||||
all::
|
||||
|
||||
# Import tree-wide shared Makefile behavior and libraries
|
||||
include ../../shared.mak
|
||||
|
||||
include ../../config.mak.uname
|
||||
-include ../../config.mak.autogen
|
||||
-include ../../config.mak
|
||||
|
||||
TARGETS = scalar$(X) scalar.o
|
||||
GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a
|
||||
|
||||
all:: scalar$(X) ../../bin-wrappers/scalar
|
||||
|
||||
$(GITLIBS):
|
||||
$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)
|
||||
|
||||
$(TARGETS): $(GITLIBS) scalar.c
|
||||
$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)
|
||||
|
||||
clean:
|
||||
$(RM) $(TARGETS) ../../bin-wrappers/scalar
|
||||
|
||||
../../bin-wrappers/scalar: ../../wrap-for-bin.sh Makefile
|
||||
@mkdir -p ../../bin-wrappers
|
||||
$(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
|
||||
-e 's|@@BUILD_DIR@@|$(shell cd ../.. && pwd)|' \
|
||||
-e 's|@@PROG@@|contrib/scalar/scalar$(X)|' < $< > $@ && \
|
||||
chmod +x $@
|
||||
|
||||
test: all
|
||||
$(MAKE) -C t
|
||||
|
||||
.PHONY: $(GITLIBS) all clean test FORCE
|
|
@ -1,82 +0,0 @@
|
|||
# Scalar - an opinionated repository management tool
|
||||
|
||||
Scalar is an add-on to Git that helps users take advantage of advanced
|
||||
performance features in Git. Originally implemented in C# using .NET Core,
|
||||
based on the learnings from the VFS for Git project, most of the techniques
|
||||
developed by the Scalar project have been integrated into core Git already:
|
||||
|
||||
* partial clone,
|
||||
* commit graphs,
|
||||
* multi-pack index,
|
||||
* sparse checkout (cone mode),
|
||||
* scheduled background maintenance,
|
||||
* etc
|
||||
|
||||
This directory contains the remaining parts of Scalar that are not (yet) in
|
||||
core Git.
|
||||
|
||||
## Roadmap
|
||||
|
||||
The idea is to populate this directory via incremental patch series and
|
||||
eventually move to a top-level directory next to `gitk-git/` and to `git-gui/`. The
|
||||
current plan involves the following patch series:
|
||||
|
||||
- `scalar-the-beginning`: The initial patch series which sets up
|
||||
`contrib/scalar/` and populates it with a minimal `scalar` command that
|
||||
demonstrates the fundamental ideas.
|
||||
|
||||
- `scalar-c-and-C`: The `scalar` command learns about two options that can be
|
||||
specified before the command, `-c <key>=<value>` and `-C <directory>`.
|
||||
|
||||
- `scalar-diagnose`: The `scalar` command is taught the `diagnose` subcommand.
|
||||
|
||||
- `scalar-and-builtin-fsmonitor`: The built-in FSMonitor is enabled in `scalar
|
||||
register` and in `scalar clone`, for an enormous performance boost when
|
||||
working in large worktrees. This patch series necessarily depends on Jeff
|
||||
Hostetler's FSMonitor patch series to be integrated into Git.
|
||||
|
||||
- `scalar-gentler-config-locking`: Scalar enlistments are registered in the
|
||||
user's Git config. This usually does not represent any problem because it is
|
||||
rare for a user to register an enlistment. However, in Scalar's functional
|
||||
tests, Scalar enlistments are created galore, and in parallel, which can lead
|
||||
to lock contention. This patch series works around that problem by re-trying
|
||||
to lock the config file in a gentle fashion.
|
||||
|
||||
- `scalar-extra-docs`: Add some extensive documentation that has been written
|
||||
in the original Scalar project (all subject to discussion, of course).
|
||||
|
||||
- `optionally-install-scalar`: Now that Scalar is feature (and documentation)
|
||||
complete and is verified in CI builds, let's offer to install it.
|
||||
|
||||
- `move-scalar-to-toplevel`: Now that Scalar is complete, let's move it next to
|
||||
`gitk-git/` and to `git-gui/`, making it a top-level command.
|
||||
|
||||
The following two patch series exist in Microsoft's fork of Git and are
|
||||
publicly available. There is no current plan to upstream them, not because I
|
||||
want to withhold these patches, but because I don't think the Git community is
|
||||
interested in these patches.
|
||||
|
||||
There are some interesting ideas there, but the implementation is too specific
|
||||
to Azure Repos and/or VFS for Git to be of much help in general (and also: my
|
||||
colleagues tried to upstream some patches already and the enthusiasm for
|
||||
integrating things related to Azure Repos and VFS for Git can be summarized in
|
||||
very, very few words).
|
||||
|
||||
These still exist mainly because the GVFS protocol is what Azure Repos has
|
||||
instead of partial clone, while Git is focused on improving partial clone:
|
||||
|
||||
- `scalar-with-gvfs`: The primary purpose of this patch series is to support
|
||||
existing Scalar users whose repositories are hosted in Azure Repos (which
|
||||
does not support Git's partial clones, but supports its predecessor, the GVFS
|
||||
protocol, which is used by Scalar to emulate the partial clone).
|
||||
|
||||
Since the GVFS protocol will never be supported by core Git, this patch
|
||||
series will remain in Microsoft's fork of Git.
|
||||
|
||||
- `run-scalar-functional-tests`: The Scalar project developed a quite
|
||||
comprehensive set of integration tests (or, "Functional Tests"). They are the
|
||||
sole remaining part of the original C#-based Scalar project, and this patch
|
||||
adds a GitHub workflow that runs them all.
|
||||
|
||||
Since the tests partially depend on features that are only provided in the
|
||||
`scalar-with-gvfs` patch series, this patch cannot be upstreamed.
|
|
@ -1,81 +0,0 @@
|
|||
# Import tree-wide shared Makefile behavior and libraries
|
||||
include ../../../shared.mak
|
||||
|
||||
# Run scalar tests
|
||||
#
|
||||
# Copyright (c) 2005,2021 Junio C Hamano, Johannes Schindelin
|
||||
#
|
||||
|
||||
-include ../../../config.mak.autogen
|
||||
-include ../../../config.mak
|
||||
|
||||
SHELL_PATH ?= $(SHELL)
|
||||
PERL_PATH ?= /usr/bin/perl
|
||||
RM ?= rm -f
|
||||
PROVE ?= prove
|
||||
DEFAULT_TEST_TARGET ?= test
|
||||
TEST_LINT ?= test-lint
|
||||
|
||||
ifdef TEST_OUTPUT_DIRECTORY
|
||||
TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results
|
||||
else
|
||||
TEST_RESULTS_DIRECTORY = ../../../t/test-results
|
||||
endif
|
||||
|
||||
# Shell quote;
|
||||
SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
|
||||
PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
|
||||
TEST_RESULTS_DIRECTORY_SQ = $(subst ','\'',$(TEST_RESULTS_DIRECTORY))
|
||||
|
||||
T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))
|
||||
|
||||
all: $(DEFAULT_TEST_TARGET)
|
||||
|
||||
test: $(TEST_LINT)
|
||||
$(MAKE) aggregate-results-and-cleanup
|
||||
|
||||
prove: $(TEST_LINT)
|
||||
@echo "*** prove ***"; GIT_CONFIG=.git/config $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)
|
||||
$(MAKE) clean-except-prove-cache
|
||||
|
||||
$(T):
|
||||
@echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
|
||||
|
||||
clean-except-prove-cache:
|
||||
$(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)'
|
||||
$(RM) -r valgrind/bin
|
||||
|
||||
clean: clean-except-prove-cache
|
||||
$(RM) .prove
|
||||
|
||||
test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax
|
||||
|
||||
test-lint-duplicates:
|
||||
@dups=`echo $(T) | tr ' ' '\n' | sed 's/-.*//' | sort | uniq -d` && \
|
||||
test -z "$$dups" || { \
|
||||
echo >&2 "duplicate test numbers:" $$dups; exit 1; }
|
||||
|
||||
test-lint-executable:
|
||||
@bad=`for i in $(T); do test -x "$$i" || echo $$i; done` && \
|
||||
test -z "$$bad" || { \
|
||||
echo >&2 "non-executable tests:" $$bad; exit 1; }
|
||||
|
||||
test-lint-shell-syntax:
|
||||
@'$(PERL_PATH_SQ)' ../../../t/check-non-portable-shell.pl $(T)
|
||||
|
||||
aggregate-results-and-cleanup: $(T)
|
||||
$(MAKE) aggregate-results
|
||||
$(MAKE) clean
|
||||
|
||||
aggregate-results:
|
||||
for f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \
|
||||
echo "$$f"; \
|
||||
done | '$(SHELL_PATH_SQ)' ../../../t/aggregate-results.sh
|
||||
|
||||
valgrind:
|
||||
$(MAKE) GIT_TEST_OPTS="$(GIT_TEST_OPTS) --valgrind"
|
||||
|
||||
test-results:
|
||||
mkdir -p test-results
|
||||
|
||||
.PHONY: $(T) aggregate-results clean valgrind
|
|
@ -13,6 +13,8 @@
|
|||
#include "help.h"
|
||||
#include "archive.h"
|
||||
#include "object-store.h"
|
||||
#include "simple-ipc.h"
|
||||
#include "fsmonitor-ipc.h"
|
||||
|
||||
/*
|
||||
* Remove the deepest subdirectory in the provided path string. Path must not
|
||||
|
@ -53,6 +55,9 @@ static void setup_enlistment_directory(int argc, const char **argv,
|
|||
die(_("need a working directory"));
|
||||
|
||||
strbuf_trim_trailing_dir_sep(&path);
|
||||
#ifdef GIT_WINDOWS_NATIVE
|
||||
convert_slashes(path.buf);
|
||||
#endif
|
||||
do {
|
||||
const size_t len = path.len;
|
||||
|
||||
|
@ -96,12 +101,14 @@ static void setup_enlistment_directory(int argc, const char **argv,
|
|||
setup_git_directory();
|
||||
}
|
||||
|
||||
static int git_retries = 3;
|
||||
|
||||
static int run_git(const char *arg, ...)
|
||||
{
|
||||
struct strvec argv = STRVEC_INIT;
|
||||
va_list args;
|
||||
const char *p;
|
||||
int res;
|
||||
int res, attempts;
|
||||
|
||||
va_start(args, arg);
|
||||
strvec_push(&argv, arg);
|
||||
|
@ -109,7 +116,10 @@ static int run_git(const char *arg, ...)
|
|||
strvec_push(&argv, p);
|
||||
va_end(args);
|
||||
|
||||
res = run_command_v_opt(argv.v, RUN_GIT_CMD);
|
||||
for (attempts = 0, res = 1;
|
||||
res && attempts < git_retries;
|
||||
attempts++)
|
||||
res = run_command_v_opt(argv.v, RUN_GIT_CMD);
|
||||
|
||||
strvec_clear(&argv);
|
||||
return res;
|
||||
|
@ -169,6 +179,13 @@ static int set_recommended_config(int reconfigure)
|
|||
{ "core.autoCRLF", "false" },
|
||||
{ "core.safeCRLF", "false" },
|
||||
{ "fetch.showForcedUpdates", "false" },
|
||||
#ifdef HAVE_FSMONITOR_DAEMON_BACKEND
|
||||
/*
|
||||
* Enable the built-in FSMonitor on supported platforms.
|
||||
*/
|
||||
{ "core.fsmonitor", "true" },
|
||||
#endif
|
||||
{ "core.configWriteLockTimeoutMS", "150" },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
int i;
|
||||
|
@ -211,16 +228,25 @@ static int set_recommended_config(int reconfigure)
|
|||
|
||||
static int toggle_maintenance(int enable)
|
||||
{
|
||||
unsigned long ul;
|
||||
|
||||
if (git_config_get_ulong("core.configWriteLockTimeoutMS", &ul))
|
||||
git_config_push_parameter("core.configWriteLockTimeoutMS=150");
|
||||
|
||||
return run_git("maintenance", enable ? "start" : "unregister", NULL);
|
||||
}
|
||||
|
||||
static int add_or_remove_enlistment(int add)
|
||||
{
|
||||
int res;
|
||||
unsigned long ul;
|
||||
|
||||
if (!the_repository->worktree)
|
||||
die(_("Scalar enlistments require a worktree"));
|
||||
|
||||
if (git_config_get_ulong("core.configWriteLockTimeoutMS", &ul))
|
||||
git_config_push_parameter("core.configWriteLockTimeoutMS=150");
|
||||
|
||||
res = run_git("config", "--global", "--get", "--fixed-value",
|
||||
"scalar.repo", the_repository->worktree, NULL);
|
||||
|
||||
|
@ -236,6 +262,56 @@ static int add_or_remove_enlistment(int add)
|
|||
"scalar.repo", the_repository->worktree, NULL);
|
||||
}
|
||||
|
||||
static int start_fsmonitor_daemon(void)
|
||||
{
|
||||
#ifdef HAVE_FSMONITOR_DAEMON_BACKEND
|
||||
struct strbuf err = STRBUF_INIT;
|
||||
struct child_process cp = CHILD_PROCESS_INIT;
|
||||
|
||||
cp.git_cmd = 1;
|
||||
strvec_pushl(&cp.args, "fsmonitor--daemon", "start", NULL);
|
||||
if (!pipe_command(&cp, NULL, 0, NULL, 0, &err, 0)) {
|
||||
strbuf_release(&err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fsmonitor_ipc__get_state() != IPC_STATE__LISTENING) {
|
||||
write_in_full(2, err.buf, err.len);
|
||||
strbuf_release(&err);
|
||||
return error(_("could not start the FSMonitor daemon"));
|
||||
}
|
||||
|
||||
strbuf_release(&err);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stop_fsmonitor_daemon(void)
|
||||
{
|
||||
#ifdef HAVE_FSMONITOR_DAEMON_BACKEND
|
||||
struct strbuf err = STRBUF_INIT;
|
||||
struct child_process cp = CHILD_PROCESS_INIT;
|
||||
|
||||
cp.git_cmd = 1;
|
||||
strvec_pushl(&cp.args, "fsmonitor--daemon", "stop", NULL);
|
||||
if (!pipe_command(&cp, NULL, 0, NULL, 0, &err, 0)) {
|
||||
strbuf_release(&err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fsmonitor_ipc__get_state() == IPC_STATE__LISTENING) {
|
||||
write_in_full(2, err.buf, err.len);
|
||||
strbuf_release(&err);
|
||||
return error(_("could not stop the FSMonitor daemon"));
|
||||
}
|
||||
|
||||
strbuf_release(&err);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int register_dir(void)
|
||||
{
|
||||
int res = add_or_remove_enlistment(1);
|
||||
|
@ -246,6 +322,9 @@ static int register_dir(void)
|
|||
if (!res)
|
||||
res = toggle_maintenance(1);
|
||||
|
||||
if (!res)
|
||||
res = start_fsmonitor_daemon();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -259,6 +338,9 @@ static int unregister_dir(void)
|
|||
if (add_or_remove_enlistment(0) < 0)
|
||||
res = -1;
|
||||
|
||||
if (stop_fsmonitor_daemon() < 0)
|
||||
res = -1;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -1046,6 +1128,25 @@ static int cmd_delete(int argc, const char **argv)
|
|||
return res;
|
||||
}
|
||||
|
||||
static int cmd_help(int argc, const char **argv)
|
||||
{
|
||||
struct option options[] = {
|
||||
OPT_END(),
|
||||
};
|
||||
const char * const usage[] = {
|
||||
N_("scalar help"),
|
||||
NULL
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, NULL, options,
|
||||
usage, 0);
|
||||
|
||||
if (argc != 0)
|
||||
usage_with_options(usage, options);
|
||||
|
||||
return run_git("help", "scalar", NULL);
|
||||
}
|
||||
|
||||
static int cmd_version(int argc, const char **argv)
|
||||
{
|
||||
int verbose = 0, build_options = 0;
|
||||
|
@ -1085,6 +1186,7 @@ static struct {
|
|||
{ "run", cmd_run },
|
||||
{ "reconfigure", cmd_reconfigure },
|
||||
{ "delete", cmd_delete },
|
||||
{ "help", cmd_help },
|
||||
{ "version", cmd_version },
|
||||
{ "diagnose", cmd_diagnose },
|
||||
{ NULL, NULL},
|
||||
|
@ -1118,6 +1220,9 @@ int cmd_main(int argc, const char **argv)
|
|||
argv++;
|
||||
argc--;
|
||||
|
||||
if (!strcmp(argv[0], "config"))
|
||||
argv[0] = "reconfigure";
|
||||
|
||||
for (i = 0; builtins[i].name; i++)
|
||||
if (!strcmp(builtins[i].name, argv[0]))
|
||||
return !!builtins[i].fn(argc, argv);
|
|
@ -2,21 +2,26 @@
|
|||
|
||||
test_description='test the `scalar` command'
|
||||
|
||||
TEST_DIRECTORY=$PWD/../../../t
|
||||
export TEST_DIRECTORY
|
||||
|
||||
# Make it work with --no-bin-wrappers
|
||||
PATH=$PWD/..:$PATH
|
||||
|
||||
. ../../../t/test-lib.sh
|
||||
. ./test-lib.sh
|
||||
|
||||
GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab ../cron.txt,launchctl:true,schtasks:true"
|
||||
export GIT_TEST_MAINT_SCHEDULER
|
||||
|
||||
test_lazy_prereq BUILTIN_FSMONITOR '
|
||||
git version --build-options | grep -q "feature:.*fsmonitor--daemon"
|
||||
'
|
||||
|
||||
test_expect_success 'scalar shows a usage' '
|
||||
test_expect_code 129 scalar -h
|
||||
'
|
||||
|
||||
test_expect_success BUILTIN_FSMONITOR 'scalar register starts fsmon daemon' '
|
||||
git init test/src &&
|
||||
test_must_fail git -C test/src fsmonitor--daemon status &&
|
||||
scalar register test/src &&
|
||||
git -C test/src fsmonitor--daemon status
|
||||
'
|
||||
|
||||
test_expect_success 'scalar unregister' '
|
||||
git init vanish/src &&
|
||||
scalar register vanish/src &&
|
Загрузка…
Ссылка в новой задаче