Skip to content

Commit

Permalink
Re-insert some important instances of GVFS
Browse files Browse the repository at this point in the history
Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
  • Loading branch information
derrickstolee committed Aug 10, 2019
1 parent ff040c6 commit 9f402b5
Show file tree
Hide file tree
Showing 17 changed files with 33 additions and 269 deletions.
238 changes: 1 addition & 237 deletions Protocol.md
Original file line number Diff line number Diff line change
@@ -1,237 +1 @@
# The Scalar Protocol (v1)

The Scalar network protocol consists of four operations on three endpoints. In summary:
* `GET /scalar/objects/{objectId}`
* Provides a single object in loose-object format
* `POST /scalar/objects`
* Provides one or more objects in packfile or streaming loose object format
* `GET /scalar/prefetch[?lastPackTimestamp={secondsSinceEpoch}]`
* Provides one or more packfiles of non-blobs and optionally packfile indexes in a streaming format
* `POST /scalar/sizes`
* Provides the uncompressed, undeltified size of one or more objects
* `GET /scalar/config`
* Provides server-set client configuration options

# `GET /scalar/objects/{objectId}`
Will return a single object in compressed loose object format, which can be directly
written to `.git/xx/yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy` if desired. The request/response looks
similar to the "Dumb Protocol" as described [here](https://git-scm.com/book/en/v2/Git-Internals-Transfer-Protocols).

# `POST /scalar/objects`
Will return multiple objects, possibly more than the client requested based on request parameters.

The request consists of a JSON body with the following format:
```
{
"objectIds" : [ {JSON array of SHA-1 object IDs, as strings} ],
"commitDepth" : {positive integer}
}
```

For example,
```
{
"objectIds" : [
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
],
"commitDepth" : 1
}
```

## `Accept: application/x-git-packfile` (the default)

If
* An `Accept` header of `application/x-git-packfile` is specified, or
* No `Accept` header is specified

then a git packfile, indexable via `index-pack`, will be returned to the client.

If `objectIds` includes a `commit`, then all `tree`s recursively referenced by that commit are also returned.
If any other object type is requested (`tree`, `blob`, or `tag`), then only that object will be returned.

`commitDepth` - if the requested object is a `commit`, all parents up to `n` levels deep will be returned, along
with all their trees as previously described. Does not include any `blob`s.

## `Accept: application/x-scalar-loose-objects`

**NOTE**: This format is currently only supposed by the cache server, not by VSTS.

To enable scenarios where multiple objects are required, but less overhead would be incurred by using pre-existing
loose objects (e.g. on a caching proxy), an alternative, packfile-like response format that contains loose objects
is also supported.

To receive objects in this format, the client **MUST** supply an `Accept` header of `application/x-scalar-loose-objects`
to the `POST /scalar/objects` endpoint. Otherwise, the response format will be `application/x-git-packfile`.

This format will **NOT** perform any `commit` to `tree` expansion, and will return an error if a `commitDepth`
greater than `1` is supplied. Said another way, this `Accept`/return type has no concept of "implicitly-requested"
objects.

### Version 1
* Integers are signed and little-endian, unless otherwise specified
* Byte offset 0 is the first byte in the file
* Index offset 0 is the first byte in the first element of an array
* `num_objects` represents the variable number of objects in the file/response

```
Count Size (bytes) Chunk Description
HEADER
+-------------------------------------------------------------------------------+
1 | 5 | UTF-8 encoded 'Scalar ' |
| 1 | Unsigned byte version number. Currently, 1. |
+-------------------------------------------------------------------------------+
OBJECT CONTENT
+-------------------------------------------------------------------------------+
num_objects | 20 | SHA-1 ID of the object. |
| 8 | Signed-long length of the object. |
| variable | Compressed, raw loose object content. |
+-------------------------------------------------------------------------------+
TRAILER
+-------------------------------------------------------------------------------+
1 | 20 | Zero bytes |
+-------------------------------------------------------------------------------+
```

# `GET /scalar/prefetch[?lastPackTimestamp={secondsSinceEpoch}]`

To enable the reuse of already-existing packfiles and indexes, a custom format for transmitting these files
is supported. The `prefetch` endpoint will return one or more packfiles of **non-blob** objects.

If the optional `lastPackTimestamp` query parameter is supplied, only packs created by the server
after the specific Unix epoch time (approximately, ±10 minutes or so) will be returned. Generally, these packs
will contain only objects introduced to the repository after that UTC-based timestamp, but will not contain
**all** objects introduced after that timestamp.

A media-type of `application/x-scalar-timestamped-packfiles-indexes` will be returned from this endpoint.

## Response format

* Integers are signed and little-endian, unless otherwise specified
* Byte offset 0 is the first byte in the file
* Index offset 0 is the first byte in the first element of an array
* `num_packs` represents the variable number of packs in the file/response

### Version 1

```
Count Size (bytes) Chunk Description
HEADER
+-------------------------------------------------------------------------------+
1 | 5 | UTF-8 encoded 'GPRE ' |
| 1 | Unsigned byte version number. Currently, 1. |
+-------------------------------------------------------------------------------+
CONTENT
+-------------------------------------------------------------------------------+
1 | 2 | Unsigned short number of packs. `num_packs`. |
+-------------------------------------------------------------------------------+
+-------------------------------------------------------------------------------+
num_packs | 8 | Signed-long pack timestamp in seconds since UTC epoch. |
| 8 | Signed-long length of the pack. |
| 8 | Signed-long length of the pack index. -1 indicates no index. |
| variable | Pack contents. |
| variable | Pack index contents. |
+-------------------------------------------------------------------------------+
```

Packs **MUST** be sent in increasing `timestamp` order. In the case of a failed connection, this allows the
client to keep the packs it received successfully and "resume" by sending the highest completed timestamp.

# `POST /scalar/sizes`
Will return the uncompressed, undeltified length of the requested objects in JSON format.

The request consists of a JSON body with the following format:
```
[ {JSON array of SHA-1 object IDs, as strings} ]
```

For example, a request of:
```
[
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
]
```

will result in a a response like:
```
[
{
"Id" : "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"Size" : 123
},
{
"Id" : "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"Size" : 456
}
]
```

# `GET /scalar/config`
This optional endpoint will return all server-set Scalar client configuration options. It currently
provides:

* A set of allowed Scalar client version ranges, in order to block older clients from running in
certain scenarios. For example, a data corruption bug may be found and encouraging clients to
avoid that version is desirable.
* A list of available cache servers, each describing their url and default-ness with a friendly name
that users can use to inform which cache server to use. Note that the names "None" and "User Defined"
are reserved by Scalar. Any caches with these names may cause undefined behavior in the Scalar client.

An example response is provided below. Note that the `null` `"Max"` value is only allowed for the last
(or greatest) range, since it logically excludes greater version numbers from having an effect.
```
{
"AllowedGvfsClientVersions": [{
"Max": {
"Major": 0,
"Minor": 4,
"Build": 0,
"Revision": 0
},
"Min": {
"Major": 0,
"Minor": 2,
"Build": 0,
"Revision": 0
}
}, {
"Max": {
"Major": 0,
"Minor": 5,
"Build": 0,
"Revision": 0
},
"Min": {
"Major": 0,
"Minor": 4,
"Build": 17009,
"Revision": 1
}
}, {
"Max": null,
"Min": {
"Major": 0,
"Minor": 5,
"Build": 16326,
"Revision": 1
}
}],
"CacheServers": [{
"Url": "https://redmond-cache-machine/repo-id",
"Name": "Redmond",
"GlobalDefault": true
}, {
"Url": "https://dublin-cache-machine/repo-id",
"Name": "Dublin",
"GlobalDefault": false
}]
}
```
See Protocol.md in microsoft/vfsforgit.
4 changes: 2 additions & 2 deletions Scalar.Build/GenerateG4WNugetReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ public override bool Execute()
@"<?xml version=""1.0"" encoding=""utf-8""?>
<!-- This file is autogenerated by Scalar.PreBuild.CreateG4WNugetReference. Any changes made directly in this file will be lost. -->
<packages>
<package id=""GitForWindows.Scalar.Installer"" version=""{0}"" targetFramework=""net461"" />
<package id=""GitForWindows.Scalar.Portable"" version=""{0}"" targetFramework=""net461"" />
<package id=""GitForWindows.GVFS.Installer"" version=""{0}"" targetFramework=""net461"" />
<package id=""GitForWindows.GVFS.Portable"" version=""{0}"" targetFramework=""net461"" />
</packages>",
this.GitPackageVersion));

Expand Down
2 changes: 1 addition & 1 deletion Scalar.Build/GenerateGitVersionConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public override bool Execute()

string installerDirectory = Path.Combine(
this.PackagesPath,
"GitForWindows.Scalar.Installer." + this.GitPackageVersion,
"GitForWindows.GVFS.Installer." + this.GitPackageVersion,
"tools");

if (!Directory.Exists(installerDirectory))
Expand Down
4 changes: 2 additions & 2 deletions Scalar.Build/GenerateScalarInstallersNuspec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ public override bool Execute()
</metadata>
<files>
<file src=""{0}"" target=""Scalar"" />
<file src=""{1}\GitForWindows.Scalar.Installer.{2}\tools\*"" target=""G4W"" />
<file src=""{1}\GitForWindows.Scalar.Portable.{2}\tools\*"" target=""G4W"" />
<file src=""{1}\GitForWindows.GVFS.Installer.{2}\tools\*"" target=""G4W"" />
<file src=""{1}\GitForWindows.GVFS.Portable.{2}\tools\*"" target=""G4W"" />
</files>
</package>",
this.ScalarSetupPath,
Expand Down
8 changes: 4 additions & 4 deletions Scalar.Build/GenerateVersionInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ public override bool Execute()
this.VersionHeader,
string.Format(
@"
#define Scalar_FILE_VERSION {0}
#define Scalar_FILE_VERSION_STRING ""{1}""
#define Scalar_PRODUCT_VERSION {0}
#define Scalar_PRODUCT_VERSION_STRING ""{1}""
#define GVFS_FILE_VERSION {0}
#define GVFS_FILE_VERSION_STRING ""{1}""
#define GVFS_PRODUCT_VERSION {0}
#define GVFS_PRODUCT_VERSION_STRING ""{1}""
",
commaDelimetedVersion,
this.Version));
Expand Down
6 changes: 3 additions & 3 deletions Scalar.Common/Http/CacheServerInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ namespace Scalar.Common.Http
{
public class CacheServerInfo
{
private const string ObjectsEndpointSuffix = "/scalar/objects";
private const string PrefetchEndpointSuffix = "/scalar/prefetch";
private const string SizesEndpointSuffix = "/scalar/sizes";
private const string ObjectsEndpointSuffix = "/gvfs/objects";
private const string PrefetchEndpointSuffix = "/gvfs/prefetch";
private const string SizesEndpointSuffix = "/gvfs/sizes";

[JsonConstructor]
public CacheServerInfo(string url, string name, bool globalDefault = false)
Expand Down
16 changes: 8 additions & 8 deletions Scalar.Common/ScalarConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ public static partial class ScalarConstants

public const string ScalarEtwProviderName = "Microsoft.Git.Scalar";
public const string WorkingDirectoryRootName = "src";
public const string UnattendedEnvironmentVariable = "Scalar_UNATTENDED";

public const string UnattendedEnvironmentVariable = "Scalar_UNATTENDED";

public const string DefaultScalarCacheFolderName = ".scalarCache";

public const string GitIsNotInstalledError = "Could not find git.exe. Ensure that Git is installed.";
Expand Down Expand Up @@ -55,18 +55,18 @@ public static class Service

public static class MediaTypes
{
public const string PrefetchPackFilesAndIndexesMediaType = "application/x-scalar-timestamped-packfiles-indexes";
public const string PrefetchPackFilesAndIndexesMediaType = "application/x-gvfs-timestamped-packfiles-indexes";
public const string LooseObjectMediaType = "application/x-git-loose-object";
public const string CustomLooseObjectsMediaType = "application/x-scalar-loose-objects";
public const string CustomLooseObjectsMediaType = "application/x-gvfs-loose-objects";
public const string PackFileMediaType = "application/x-git-packfile";
}

public static class Endpoints
{
public const string ScalarConfig = "/scalar/config";
public const string ScalarObjects = "/scalar/objects";
public const string ScalarPrefetch = "/scalar/prefetch";
public const string ScalarSizes = "/scalar/sizes";
public const string ScalarConfig = "/gvfs/config";
public const string ScalarObjects = "/gvfs/objects";
public const string ScalarPrefetch = "/gvfs/prefetch";
public const string ScalarSizes = "/gvfs/sizes";
public const string InfoRefs = "/info/refs?service=git-upload-pack";
}

Expand Down
2 changes: 1 addition & 1 deletion Scalar.FunctionalTests/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public static void Initialize()
{
CurrentDirectory = Path.GetFullPath(Path.GetDirectoryName(Environment.GetCommandLineArgs()[0]));

RepoToClone = @"https://scalar.visualstudio.com/ci/_git/ForTests";
RepoToClone = @"https://gvfs.visualstudio.com/ci/_git/ForTests";
Commitish = @"FunctionalTests/20180214";

if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
Expand Down
2 changes: 1 addition & 1 deletion Scalar.Installer.Mac/CreateMacInstaller.sh
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ function CreateScalarDistribution()
function CreateMetaDistribution()
{
GITVERSION="$($Scalar_SCRIPTDIR/GetGitVersionNumber.sh)"
GITINSTALLERPKGPATH="$(find $Scalar_PACKAGESDIR/gitformac.scalar.installer/$GITVERSION -type f -name *.pkg)" || exit 1
GITINSTALLERPKGPATH="$(find $Scalar_PACKAGESDIR/gitformac.gvfs.installer/$GITVERSION -type f -name *.pkg)" || exit 1

GITPKGNAME="${GITINSTALLERPKGPATH##*/}"
GITINSTALLERPKGNAME="${GITPKGNAME%.pkg}"
Expand Down
2 changes: 1 addition & 1 deletion Scalar.Installer.Windows/Setup.iss
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

; General documentation on how to use InnoSetup scripts: http://www.jrsoftware.org/ishelp/index.php

#define VCRuntimeDir PackagesDir + "\Scalar.VCRuntime.0.2.0-build\lib\x64"
#define VCRuntimeDir PackagesDir + "\GVFS.VCRuntime.0.2.0-build\lib\x64"
#define ScalarDir BuildOutputDir + "\Scalar.Windows\bin\" + PlatformAndConfiguration
#define ScalarCommonDir BuildOutputDir + "\Scalar.Common\bin\" + PlatformAndConfiguration + "\netstandard2.0"
#define ServiceDir BuildOutputDir + "\Scalar.Service.Windows\bin\" + PlatformAndConfiguration
Expand Down
2 changes: 1 addition & 1 deletion Scalar.UnitTests/Common/SHA1UtilTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Scalar.UnitTests.Common
[TestFixture]
public class SHA1UtilTests
{
private const string TestString = "c:\\Repos\\Scalar\\src\\.gittattributes";
private const string TestString = "c:\\Repos\\GVFS\\src\\.gittattributes";
private const string TestResultSha1 = "ced5ad9680c1a05e9100680c2b3432de23bb7d6d";
private const string TestResultHex = "633a5c5265706f735c475646535c7372635c2e6769747461747472696275746573";

Expand Down
2 changes: 1 addition & 1 deletion Scalar/CommandLine/ScalarVerb.cs
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ protected ServerScalarConfig QueryScalarConfig(ITracer tracer, ScalarEnlistment
"Querying remote for config",
suppressGvfsLogMessage: true))
{
this.ReportErrorAndExit(tracer, "Unable to query /scalar/config" + Environment.NewLine + errorMessage);
this.ReportErrorAndExit(tracer, "Unable to query /gvfs/config" + Environment.NewLine + errorMessage);
}

return serverScalarConfig;
Expand Down
4 changes: 2 additions & 2 deletions Scalar/packages.config
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CommandLineParser" version="2.1.1-beta" targetFramework="net461" />
<package id="Scalar.VCRuntime" version="0.2.0-build" targetFramework="native" />
<package id="GVFS.VCRuntime" version="0.2.0-build" targetFramework="native" />
<package id="Microsoft.Data.Sqlite" version="2.2.4" targetFramework="net461" />
<package id="Microsoft.Data.Sqlite.Core" version="2.2.4" targetFramework="net461" />
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net461" />
Expand All @@ -24,4 +24,4 @@
<package id="SQLitePCLRaw.lib.e_sqlite3.v110_xp" version="1.1.12" targetFramework="net461" />
<package id="SQLitePCLRaw.provider.e_sqlite3.net45" version="1.1.12" targetFramework="net461" />
<package id="StyleCop.Analyzers" version="1.0.2" targetFramework="net461" developmentDependency="true" />
</packages>
</packages>
Loading

0 comments on commit 9f402b5

Please sign in to comment.