libgit2 / libgit2/libgit2sharp

[RFC] Batched low level index operations

Open
#908 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C#
Stars
3.5k
Forks
925
PR merge metrics
No merged PRs in 30d

Description

In the light of https://github.com/libgit2/libgit2sharp/pull/907, I was wondering if we should take this approach one step further.

Each call to repo.Stage() will

  • Perform a diff between the working directory and the Index in order to determine what additions/removals should be promoted to the staging area
  • Perform additions/deletions in the in memory Index
  • Eventually persist the in memory Index to the disk

Each call to repo.Index.Add() (or Remove()) will

  • Perform an addition (or a deletion) in the in memory Index
  • Persist the in memory Index to the disk

As such, invoking repo.Index.Add() or repo.Index.Remove() will be more efficient than a call to repo.Stage().

We want to allow the user to switch to the command line git.git, between each call to LibGit2Sharp, and find the repository in a coherent state. This is the requirement that compels us to persist the in-memory Index at the very end of each Index related method.

However, would clients of the library require to perform many sequential calls to the lower level methods repo.Index.Add()|Remove(), the Index would be persisted as part of each call. Which may not be very efficient.

Considering this above, would a need for an IndexUpdater exist?

We may expose something like in the Index type:

public virtual void Update(params Action<IndexUpdater>[] actions)

which would allow the client to write such code:

repo.Index.Update(i => i.Add(path), i => i.Remove(otherPath))

Or even some more elaborate custom thingies such as

private void StageAllAdditionsRemovalsAndModifications(IRepository repo)
{
    // Compare the Workdir against the Index without attempting at detecting Copies/Renames
    var changes = repo.Diff.Compare<TreeChanges>(null, true,
        compareOptions: new CompareOptions { Similarity = SimilarityOptions.None });

    repo.Index.Update(i =>
    {
        // We first clean the Index from unwanted entries...
        foreach (TreeEntryChanges treeEntryChanges in changes
            .Where(tec => tec.Status == ChangeKind.Deleted))
        {
            i.Remove(treeEntryChanges.Path);
        }

        // ...then insert in the Index the additions/modifications.
        foreach (TreeEntryChanges treeEntryChanges in changes
            .Where(tec => tec.Status == ChangeKind.Added || tec.Status == ChangeKind.Modified))
        {
            i.Add(treeEntryChanges.Path);
        }
    });
}

And allow us to only persist the Index only once, when all the actions have been performed by the IndexUpdater.

Thoughts?

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read PR #907 and the existing Index.Add() and Index.Remove() entry points first. Compare the proposed Index.Update/IndexUpdater API with the requirement that the repository remain coherent between calls; done would require a maintainer-approved design and defined implementation scope, which this RFC does not yet provide.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp, git
Domain
backend-api-design
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.