Source Control API

The Source Control API allows extension authors to define Source Control Management (SCM) features. There is a slim, yet powerful API surface which allows many different SCM systems to be integrated in Visual Studio Code, while having a common user interface with all of them.

VS Code SCM

VS Code itself ships with one Source Control provider, the Git extension, which is the best reference for this API and is a great starting point if you’d like to contribute your very own SCM provider. There are other great examples in the Marketplace such as the SVN extension.

This documentation will help you build an extension which can make any SCM system work with VS Code.

Note: that you can always refer to the vscode namespace API reference in our documentation.

Source Control Model

A SourceControl is the entity responsible for populating the Source Control model with resource states, instances of SourceControlResourceState. Resource states are themselves organized in groups, instances of SourceControlResourceGroup.

You can create a new SourceControl with vscode.scm.createSourceControl.

In order to better understand how these three entities correlate with each other, let’s take Git as an example. Consider the following output of git status:

  1. vsce master* git status
  2. On branch master
  3. Your branch is up-to-date with 'origin/master'.
  4. Changes to be committed:
  5. (use "git reset HEAD <file>..." to unstage)
  6. modified: README.md
  7. renamed: src/api.ts -> src/test/api.ts
  8. Changes not staged for commit:
  9. (use "git add/rm <file>..." to update what will be committed)
  10. (use "git checkout -- <file>..." to discard changes in working directory)
  11. deleted: .travis.yml
  12. modified: README.md

There are many things going on in this workspace. First, the README.md file has been modified, staged and then modified once again. Second, the src/api.ts file has been moved to src/test/api.ts and that move was staged. Finally, the .travis.yml file has been deleted.

For this workspace, Git defines two resource groups: the working tree and the index. Each file change within that group is resource state:

  • Index - resource group
    • README.md, modified - resource state
    • src/test/api.ts, renamed from src/api.ts - resource state
  • Working Tree - resource group
    • .travis.yml, deleted - resource state
    • README.md, modified - resource state

Note how the same file, README.md, is part of two distinct resource states.

Here’s how Git creates this model:

  1. function createResourceUri(relativePath: string): vscode.Uri {
  2. const absolutePath = path.join(vscode.workspace.rootPath, relativePath);
  3. return vscode.Uri.file(absolutePath);
  4. }
  5. const gitSCM = vscode.scm.createSourceControl('git', 'Git');
  6. const index = gitSCM.createResourceGroup('index', 'Index');
  7. index.resourceStates = [
  8. { resourceUri: createResourceUri('README.md') },
  9. { resourceUri: createResourceUri('src/test/api.ts') }
  10. ];
  11. const workingTree = gitSCM.createResourceGroup('workingTree', 'Changes');
  12. workingTree.resourceStates = [
  13. { resourceUri: createResourceUri('.travis.yml') },
  14. { resourceUri: createResourceUri('README.md') }
  15. ];

Changes made to the source control and resource groups will be propagated to the Source Control view.

Source Control View

VS Code is able to populate the Source Control view, as the Source Control model changes. Resource states are customizable using SourceControlResourceDecorations:

  1. export interface SourceControlResourceState {
  2. readonly decorations?: SourceControlResourceDecorations;
  3. }

The previous example would be sufficient to populate a simple list in the Source Control view, but there are many user interactions that the user might want to perform with each resource. For instance, what happens when the user clicks a resource state? The resource state can optionally provide a command to handle this action:

  1. export interface SourceControlResourceState {
  2. readonly command?: Command;
  3. }

Menus

There are five Source Control menu ids where you can place menu items, in order to provide the user with a much richer user interface.

The scm/title menu is located to the right of the SCM view title. The menu items in the navigation group will be inline, while all the others will be within the drop-down menu.

The scm/resourceGroup/context and scm/resourceState/context are similar. The former will let you customize resource groups, while the later refers to resource states. Place menu items in the inline group to have them inline. All other menu item groups will be represented in a context menu usually accessible using the mouse right-click. Commands called from within these menus will have the respective resource states on passed as arguments. Note that the SCM view supports multiple selection thus a command might receive more than one resource at a time in its arguments.

For example, Git supports staging multiple files by adding the git.stage command to the scm/resourceState/context menu and using such a method declaration:

  1. stage(...resourceStates: SourceControlResourceState[]): Promise<void>;

When creating them, SourceControl and SourceControlResourceGroup instances require you to provide an id string. These values will be populated in the scmProvider and scmResourceGroup context keys, respectively. You can rely on these context keys in the when clauses of your menu items. Here’s how Git is able to show a menu item for its git.stage command:

  1. {
  2. "command": "git.stage",
  3. "when": "scmProvider == git && scmResourceGroup == merge",
  4. "group": "inline"
  5. }

The scm/change/title allows you to contribute commands to the title bar of an inline change. The command will be passed as arguments the URI of the document, the array of changes within it, and the index of the change which the inline change affordance is currently focused on. For example, here’s the declaration of the stageChange Git command, which is contributed to this menu:

  1. async stageChange(uri: Uri, changes: LineChange[], index: number): Promise<void>;

The scm/sourceControl menu is located contextually near SourceControl instances:

source control menu

Finally, the scm/change/title menu is related to the Quick Diff experience, showcased further ahead. It lets you contribute commands which are specific to code changes.

SCM Input Box

The Source Control Input Box, located atop of each Source Control view, allows the user to input a message. You can get (and set) this message in order to perform operations. In Git, for example, this is used as the commit box, in which users type in commit messages and git commit commands pick them up.

  1. export interface SourceControlInputBox {
  2. value: string;
  3. }
  4. export interface SourceControl {
  5. readonly inputBox: SourceControlInputBox;
  6. }

The user can type Ctrl+Enter (or Cmd+Enter on macOS) to accept any message. You can handle this event by providing a acceptInputCommand to your SourceControl instance.

  1. export interface SourceControl {
  2. readonly acceptInputCommand?: Command;
  3. }

Quick Diff

VS Code also supports displaying quick diff editor gutter decorations. Clicking those decorations will reveal an inline diff experience, to which you can contribute contextual commands:

SCM quick diff

These decorations are computed by VS Code itself. All you need to do is provide VS Code with the original contents of any given file.

  1. export interface SourceControl {
  2. quickDiffProvider?: QuickDiffProvider;
  3. }

Using a QuickDiffProvider, your implementation is able to tell VS Code what’s the Uri of the original resource that matches the resource which Uri is provided as an argument.

You can combine this API with the registerTextDocumentContentProvider method in the workspace namespace, which lets you provide contents for arbitrary resources, given a Uri.

Next steps

To learn more about VS Code extensibility model, try these topics: