Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 43 additions & 5 deletions Module/README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
# GenHTTP.Modules.Git

Serves **virtual git repositories** from a [GenHTTP](https://genhttp.org/) handler. Clones, fetches and pushes are
answered from your own data model via the smart HTTP protocol - without a repository on disk and without the `git`
binary.
A C# library that turns your .NET application into a **git server for your own data**. Users clone, fetch and push
with the regular `git` client, while every request is answered from your data model (a database, a blob store, an
API, ...) - without a repository on disk and without the `git` binary on the server.

Use it to offer versioned content (documents, configurations, apps, snippets) to developers, CI pipelines and agents
that know how to use git.

The package is a module for [GenHTTP](https://genhttp.org/), a lightweight, embeddable web server framework for
.NET. It provides a request handler that speaks git's smart HTTP protocol, which you can host in a GenHTTP
application or map to a path of an existing ASP.NET Core application.

## Getting started

Add the package to a project hosting a GenHTTP server:
Create a console application and add the module along with a GenHTTP engine, which runs the server:

```sh
dotnet new console -n GitDemo
cd GitDemo

dotnet add package GenHTTP.Modules.Git
dotnet add package GenHTTP.Engine.Internal
dotnet add package GenHTTP.Modules.Practices
```

Serve a repository via `GitServer.Create()`. The `InMemoryGitRepository` behaves like a bare repository and is a good
Expand Down Expand Up @@ -43,6 +52,35 @@ await Host.Create()
git clone http://localhost:8080/ hello
```

## Using ASP.NET Core

With the [GenHTTP.Adapters.AspNetCore](https://www.nuget.org/packages/GenHTTP.Adapters.AspNetCore/) package, the
handler can be mapped to a path of an existing ASP.NET Core application:

```sh
dotnet add package GenHTTP.Modules.Git
dotnet add package GenHTTP.Adapters.AspNetCore
```

```csharp
using GenHTTP.Adapters.AspNetCore;

using GenHTTP.Modules.Git;

var builder = WebApplication.CreateBuilder(args);

// Kestrel accepts 30 MB request bodies by default, allow larger pushes
builder.WebHost.ConfigureKestrel(o => o.Limits.MaxRequestBodySize = 128 * 1024 * 1024);

var app = builder.Build();

// serve the repository (created as shown above) at /repo,
// all other requests are handled by your application
app.Map("/repo", GitServer.Create().Repository(repository));

await app.RunAsync();
```

## Serving your own data

Implement `IGitRepository` to describe your data as branches, tags, commits and files:
Expand Down Expand Up @@ -101,7 +139,7 @@ public async ValueTask PushAsync(GitPush push)
- Truncated histories (e.g. deleted old versions) are served as shallow repositories
- Pushes with deltas, push options (`git push -o`) and messages
- Regular files, executables and symbolic links, loaded lazily
- Single or multiple repositories per handler, all GenHTTP engines and concerns (e.g. authentication)
- Single or multiple repositories per handler, all GenHTTP engines and concerns (e.g. authentication), ASP.NET Core
- Protection against crafted requests (size and file limits, validated file names)

Annotated tags, submodules, partial clones (`--filter`), the dumb HTTP protocol and SHA-256 repositories are not
Expand Down
103 changes: 96 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,131 @@
# Virtual Git Server

A [GenHTTP](https://genhttp.org/) module that serves **virtual git repositories**: clones, fetches and pushes are
answered from your own data model over the smart HTTP protocol, without a repository on disk and without the `git`
binary. Use it to expose versioned content (documents, configurations, apps, snippets) to developers, CI pipelines and
agents that know how to use git.
A C# library that turns your .NET application into a **git server for your own data**. Users clone, fetch and push
with the regular `git` client, while every request is answered from your data model (a database, a blob store, an
API, ...) - without a repository on disk and without the `git` binary on the server.

Use it to expose versioned content (documents, configurations, apps, snippets) to developers, CI pipelines and coding
agents that know how to use git, instead of building custom sync tools.

[![nuget Package](https://img.shields.io/nuget/v/GenHTTP.Modules.Git.svg)](https://www.nuget.org/packages/GenHTTP.Modules.Git/) [![Build](https://github.com/Kaliumhexacyanoferrat/virtual-git-server/actions/workflows/build.yml/badge.svg)](https://github.com/Kaliumhexacyanoferrat/virtual-git-server/actions/workflows/build.yml) [![View - Documentation](https://img.shields.io/badge/view-Documentation-AB54FF)](https://kaliumhexacyanoferrat.github.io/virtual-git-server/)

## How it fits in

The library is distributed as the NuGet package `GenHTTP.Modules.Git`. It is a module for
[GenHTTP](https://genhttp.org/), a lightweight, embeddable web server framework for .NET: the module provides a
request handler that speaks git's smart HTTP protocol, and you decide where in your web application it lives.

You can use it in two ways:

- **In a GenHTTP application** - the module plugs into a GenHTTP server like any other handler, either as the only
content or next to websites, APIs and files ([Getting Started](#getting-started)).
- **In an ASP.NET Core application** - an adapter maps the handler to a path of your existing `WebApplication`, next
to your controllers and minimal APIs ([Using ASP.NET Core](#using-aspnet-core)).

In both cases, your code describes the repository (branches, commits and files) and decides what to do with pushes;
the module handles the git protocol.

## Getting Started

Add the `GenHTTP.Modules.Git` package to your project and serve a repository via `GitServer.Create()`:
You need the [.NET SDK](https://dotnet.microsoft.com/download) (10 or newer). Create a console application and add
the module along with a GenHTTP engine to run the server:

```sh
dotnet new console -n GitDemo
cd GitDemo

dotnet add package GenHTTP.Modules.Git
dotnet add package GenHTTP.Engine.Internal
dotnet add package GenHTTP.Modules.Practices
```

Replace the content of `Program.cs` to serve a repository via `GitServer.Create()`:

```csharp
using GenHTTP.Engine.Internal;

using GenHTTP.Modules.Git;
using GenHTTP.Modules.Practices;

// a repository that lives in memory, useful for trying things out
var repository = new InMemoryGitRepository();

await repository.CommitAsync("main", GitTree.Create()
.Add("README.md", "# Hello World")
.Build(), "Initial commit");

// the handler answering git requests
var git = GitServer.Create()
.Repository(repository);

// a GenHTTP server listening on port 8080
await Host.Create()
.Handler(git)
.Defaults()
.RunAsync();
```

Start the application with `dotnet run` and use git as with any other server:

```sh
git clone http://localhost:8080/ hello
```

To serve your own data, implement `IGitRepository` (read-only) or `IWritableGitRepository` (accepting pushes):
To combine the repository with other content, add it to a layout from the `GenHTTP.Modules.Layouting` package
(e.g. `Layout.Create().Add("repo", git)` to serve it at `/repo`). See the [GenHTTP documentation](https://genhttp.org/documentation/) for everything else a GenHTTP
server can do.

## Using ASP.NET Core

If you already have an ASP.NET Core application, the
[GenHTTP.Adapters.AspNetCore](https://www.nuget.org/packages/GenHTTP.Adapters.AspNetCore/) package lets you map the
git handler to a path, so you do not need to run a separate server:

```sh
dotnet add package GenHTTP.Modules.Git
dotnet add package GenHTTP.Adapters.AspNetCore
```

```csharp
using GenHTTP.Adapters.AspNetCore;

using GenHTTP.Modules.Git;

var repository = new InMemoryGitRepository();

await repository.CommitAsync("main", GitTree.Create()
.Add("README.md", "# Hello World")
.Build(), "Initial commit");

var builder = WebApplication.CreateBuilder(args);

// Kestrel accepts 30 MB request bodies by default, allow larger pushes
builder.WebHost.ConfigureKestrel(o => o.Limits.MaxRequestBodySize = 128 * 1024 * 1024);

var app = builder.Build();

// serve the repository at /repo
app.Map("/repo", GitServer.Create().Repository(repository));

// the rest of your application
app.MapGet("/", () => "Hello from ASP.NET Core");

await app.RunAsync();
```

```sh
# use the address your application listens on
git clone http://localhost:5000/repo hello
```

The handler only answers requests below the mapped path, all other requests are passed on to the rest of your
application. The [documentation](https://kaliumhexacyanoferrat.github.io/virtual-git-server/aspnet-core/) covers
multiple repositories and authentication with ASP.NET Core.

## Serving your own data

The in-memory repository is a good start. To serve your own data, implement `IGitRepository` (read-only) or
`IWritableGitRepository` (accepting pushes):

```csharp
public sealed class DocumentRepository : IWritableGitRepository
Expand Down Expand Up @@ -69,7 +158,7 @@ commit ids stable, how pushes are validated and shows a complete example of a ve
| Pushing | Creating, updating and deleting branches and tags, deltas, push options, `remote:` messages |
| History | Truncated histories (e.g. deleted old versions) are served as shallow repositories |
| Files | Regular files, executables and symbolic links, loaded on demand |
| Hosting | Single or multiple repositories, any GenHTTP engine, concerns such as authentication |
| Hosting | Single or multiple repositories, any GenHTTP engine or ASP.NET Core, concerns such as authentication |

Not supported: annotated tags, submodules, partial clones (`--filter`), the dumb HTTP protocol and SHA-256
repositories. See [protocol support](https://kaliumhexacyanoferrat.github.io/virtual-git-server/protocol/) for details.
Expand Down
93 changes: 93 additions & 0 deletions docs/aspnet-core.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# ASP.NET Core

The module is a regular GenHTTP handler, so it does not require a GenHTTP server. With the
[GenHTTP.Adapters.AspNetCore](https://www.nuget.org/packages/GenHTTP.Adapters.AspNetCore/) package, it can be
mapped to a path of an existing ASP.NET Core application and runs next to your controllers, Razor pages and
minimal APIs.

## Installation

```sh
dotnet add package GenHTTP.Modules.Git
dotnet add package GenHTTP.Adapters.AspNetCore
```

## Mapping a repository

The adapter adds a `Map` overload to `WebApplication` that accepts GenHTTP handlers:

```csharp
using GenHTTP.Adapters.AspNetCore;

using GenHTTP.Modules.Git;

var repository = new InMemoryGitRepository();

await repository.CommitAsync("main", GitTree.Create()
.Add("README.md", "# Hello World")
.Build(), "Initial commit");

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(o => o.Limits.MaxRequestBodySize = 128 * 1024 * 1024);

var app = builder.Build();

app.Map("/repo", GitServer.Create().Repository(repository));

app.MapGet("/", () => "Hello from ASP.NET Core");

await app.RunAsync();
```

```sh
# use the address your application listens on
git clone http://localhost:5000/repo hello
```

Requests below the mapped path are handled by the git server, all other requests continue through the ASP.NET Core
pipeline.

The builder returned by `GitServer.Create()` is configured exactly as described in [Hosting](hosting.md), so limits,
resolvers and concerns work the same way.

## Request size

Kestrel rejects request bodies larger than 30 MB by default, while the module accepts pushes up to 128 MB
(`MaximumPushSize`). Larger pushes then fail with HTTP 413 before they reach the module, so raise the Kestrel limit
to at least the configured push size, as shown above. If the application runs behind IIS or a reverse proxy, their
limits apply as well.

## Multiple repositories

Resolvers work the same way as with a GenHTTP server. The path segment below the mapped path selects the
repository:

```csharp
app.Map("/git", GitServer.Create()
.Repositories(async (request, name) => await store.FindRepositoryAsync(name)));

// git clone http://localhost:5000/git/my-project
// git clone http://localhost:5000/git/my-project.git
```

## Authentication

GenHTTP concerns can be added to the builder, so the [authentication](hosting.md#authentication) described for
GenHTTP servers also works within ASP.NET Core:

```sh
dotnet add package GenHTTP.Modules.Authentication
```

```csharp
using GenHTTP.Modules.Authentication;

app.Map("/repo", GitServer.Create()
.Repository(repository)
.Add(BasicAuthentication.Create().Add("jane", "secret")));
```

The handler is invoked by the adapter outside of the ASP.NET Core routing, so endpoint metadata such as
`RequireAuthorization()` does not apply to it. Use a GenHTTP concern or a middleware registered before the mapping
to protect the repository.
17 changes: 14 additions & 3 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
# Getting Started

The module is a .NET library that runs within a [GenHTTP](https://genhttp.org/) web server. This page creates a
small console application hosting such a server. If you would like to add a repository to an existing ASP.NET Core
application instead, see [ASP.NET Core](aspnet-core.md).

## Installation

Add the module to a project hosting a GenHTTP server:
You need the [.NET SDK](https://dotnet.microsoft.com/download) (10 or newer). Create a console application and add
the module along with a GenHTTP engine, which runs the server:

```sh
dotnet new console -n GitDemo
cd GitDemo

dotnet add package GenHTTP.Modules.Git
dotnet add package GenHTTP.Engine.Internal
dotnet add package GenHTTP.Modules.Practices
```

The module targets .NET 10 and .NET 11 and works with every GenHTTP engine (internal, Kestrel and ioxide).
The module targets .NET 10 and .NET 11 and works with every GenHTTP engine (internal, Kestrel and ioxide). The
`Practices` package adds recommended defaults such as compression to the server.

## Serving a repository

Expand Down Expand Up @@ -39,7 +50,7 @@ await Host.Create()
.RunAsync();
```

Run the project and use git as you would with any other server:
Run the project with `dotnet run` and use git as you would with any other server:

```sh
git clone http://localhost:8080/ hello
Expand Down
3 changes: 2 additions & 1 deletion docs/hosting.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ searched for in the newest 10,000 commits of the repository.

## Engines and concerns

The handler works with all GenHTTP engines. Concerns such as compression or client caching added via `Defaults()` do
The handler works with all GenHTTP engines and can also be used within an ASP.NET Core application, see
[ASP.NET Core](aspnet-core.md). Concerns such as compression or client caching added via `Defaults()` do
not interfere with git: responses are marked as not cacheable, and request bodies compressed by git are detected
regardless of whether a decompression concern already handled them.

Expand Down
Loading
Loading