Skip to main content

Overview

Neo Git Graph supports workspaces containing multiple Git repositories. The extension automatically discovers repositories in your workspace and provides a dropdown to switch between them.

Repository Discovery

Automatic Scanning

When VS Code opens, the extension scans workspace folders for Git repositories:
// From repoManager.ts:187-201
private async searchWorkspaceForRepos() {
  let rootFolders = vscode.workspace.workspaceFolders,
    changes = false;
  if (typeof rootFolders !== "undefined") {
    for (let i = 0; i < rootFolders.length; i++) {
      if (await this.searchDirectoryForRepos(
          getPathFromUri(rootFolders[i].uri),
          this.maxDepthOfRepoSearch
        )
      ) changes = true;
    }
  }
  if (changes) this.sendRepos();
}

Search Depth

Configure how deep the extension searches for repositories:
{
  "neo-git-graph.maxDepthOfRepoSearch": 0
}
Only checks if workspace folders themselves are Git repositoriesExample:
workspace/
├── .git/          ← Found
├── project-a/
│   └── .git/      ← Not found
└── project-b/
    └── .git/      ← Not found
Increasing search depth can slow down VS Code startup for large workspaces with many directories.

Repository Switching

Using the Dropdown

1

Open Git Graph

Click the Git Graph status bar item or run the command
2

Locate Repository Dropdown

Find “Repo:” dropdown at the top-left of the graph view
3

Select Repository

Click the dropdown and choose from discovered repositories
4

View Updates

Graph immediately loads commits from the selected repository

Last Active Repository

The extension remembers your last viewed repository:
// From gitGraphView.ts:222-225
if (msg.repo !== this.currentRepo) {
  this.currentRepo = msg.repo;
  this.extensionState.setLastActiveRepo(msg.repo);
  this.repoFileWatcher.start(msg.repo);
}
When reopening Git Graph, the last active repository is automatically selected.

Repository State Persistence

Per-Repository Settings

Each repository maintains its own state:
// From types.ts:44-47
export interface GitRepoState {
  columnWidths: number[] | null;
}

export type GitRepoSet = { [repo: string]: GitRepoState };
Persisted State:
  • Column widths in the commit table
  • View preferences (future features)

Saving State

// From repoManager.ts:181-184
public setRepoState(repo: string, state: GitRepoState) {
  this.repos[repo] = state;
  this.extensionState.saveRepos(this.repos);
}
State is saved automatically when you adjust column widths or close the Git Graph view.

File System Watching

Automatic Repository Detection

The extension watches workspace folders for new repositories:
// From repoManager.ts:257-263
private startWatchingFolder(path: string) {
  let watcher = vscode.workspace.createFileSystemWatcher(path + "/**");
  watcher.onDidCreate((uri) => this.onWatcherCreate(uri));
  watcher.onDidChange((uri) => this.onWatcherChange(uri));
  watcher.onDidDelete((uri) => this.onWatcherDelete(uri));
  this.folderWatchers[path] = watcher;
}

Detection Events

When a new .git directory is detected:
// From repoManager.ts:268-276
private async onWatcherCreate(uri: vscode.Uri) {
  let path = getPathFromUri(uri);
  if (path.indexOf("/.git/") > -1) return;
  if (path.endsWith("/.git")) path = path.slice(0, -5);
  
  this.createEventPaths.push(path);
  setTimeout(() => this.processCreateEvents(), 1000);
}
Repository is automatically added to the dropdown
When a .git directory is removed:
private onWatcherDelete(uri: vscode.Uri) {
  let path = getPathFromUri(uri);
  if (this.removeReposWithinFolder(path)) this.sendRepos();
}
Repository is removed from the dropdown
When .git directory is modified:
  • Validates repository still exists
  • Updates repository list if needed

Workspace Folder Management

Adding Folders

When workspace folders are added:
// From repoManager.ts:39-49
this.folderChangeHandler = vscode.workspace.onDidChangeWorkspaceFolders(async (e) => {
  if (e.added.length > 0) {
    let path, changes = false;
    for (let i = 0; i < e.added.length; i++) {
      path = getPathFromUri(e.added[i].uri);
      if (await this.searchDirectoryForRepos(path, this.maxDepthOfRepoSearch)) 
        changes = true;
      this.startWatchingFolder(path);
    }
    if (changes) this.sendRepos();
  }
});
1

Folder Added to Workspace

User adds folder via File > Add Folder to Workspace
2

Repository Search

Extension searches for Git repositories in the new folder
3

File Watching Started

File system watcher monitors the folder for future changes
4

Dropdown Updated

Any discovered repositories appear in the dropdown

Removing Folders

When workspace folders are removed:
// From repoManager.ts:50-59
if (e.removed.length > 0) {
  let changes = false, path;
  for (let i = 0; i < e.removed.length; i++) {
    path = getPathFromUri(e.removed[i].uri);
    if (this.removeReposWithinFolder(path)) changes = true;
    this.stopWatchingFolder(path);
  }
  if (changes) this.sendRepos();
}
Repositories within removed folders are automatically cleaned up.

Repository Validation

Existence Checking

The extension periodically validates that repositories still exist:
// From repoManager.ts:163-180
public checkReposExist() {
  return new Promise<boolean>((resolve) => {
    let repoPaths = Object.keys(this.repos), changes = false;
    evalPromises(repoPaths, 3, (path) => 
      this.dataSource.isGitRepository(path)
    ).then((results) => {
      for (let i = 0; i < repoPaths.length; i++) {
        if (!results[i]) {
          this.removeRepo(repoPaths[i]);
          changes = true;
        }
      }
      if (changes) this.sendRepos();
      resolve(changes);
    });
  });
}
Validation Method:
git rev-parse --git-dir
Validation runs when:
  • Git Graph is opened
  • Workspace folders change
  • Manual refresh is triggered

Multi-Repo Workflows

Monorepo Support

monorepo/
├── .git/
├── frontend/
├── backend/
└── shared/
One repository in dropdown, shows all commits

Workspace Recommendations

  • Use default search depth of 0-1
  • All repositories easily accessible
  • Minimal performance impact
  • Consider organizing into separate workspaces
  • Or use VS Code multi-root workspaces
  • Limit search depth to avoid scanning overhead
  • Increase search depth as needed
  • Be aware of performance impact
  • Consider if nesting is necessary (e.g., git submodules)

Status Bar Integration

Repository Count

The status bar shows the number of discovered repositories:
// From repoManager.ts:157-162
private sendRepos() {
  let repos = this.getRepos();
  let numRepos = Object.keys(repos).length;
  this.statusBarItem.setNumRepos(numRepos);
  if (this.viewCallback !== null) this.viewCallback(repos, numRepos);
}

No Repositories

Status bar item hidden or shows warning

One Repository

Status bar shows Git Graph icon, no count

Multiple Repositories

Status bar shows count: “Git Graph (3)“

Repository Removal

When Repositories Are Removed

1

No Longer in Workspace

Repository path is not under any workspace folder
2

.git Directory Deleted

Repository’s .git directory was deleted or moved
3

Workspace Folder Removed

Folder containing repository was removed from workspace
4

Validation Failure

git rev-parse --git-dir returns an error

Cleanup Process

// From repoManager.ts:134-137
private removeRepo(repo: string) {
  delete this.repos[repo];
  this.extensionState.saveRepos(this.repos);
}
Removing a repository from the dropdown does not delete the repository from disk, only from the extension’s tracking list.

Performance Optimization

Repository searching uses promise-based concurrency:
// From repoManager.ts:231-237
resolve(
  (await evalPromises(dirs, 2, (dir) =>
    this.searchDirectoryForRepos(dir, maxDepth - 1)
  )).indexOf(true) > -1
);
Concurrency: 2 directories searched simultaneously

Debouncing File System Events

File system events are debounced to avoid excessive processing:
// From repoManager.ts:274-276
this.createEventPaths.push(path);
if (this.processCreateEventsTimeout !== null) 
  clearTimeout(this.processCreateEventsTimeout);
this.processCreateEventsTimeout = setTimeout(() => 
  this.processCreateEvents(), 1000
);
Debounce delay: 1000ms (1 second)

Troubleshooting

Repository Not Detected

Repository may be nested deeper than maxDepthOfRepoSearch settingIncrease the depth:
{
  "neo-git-graph.maxDepthOfRepoSearch": 2
}
Verify .git directory exists and is valid:
cd /path/to/repo
git status
Ensure repository is within a VS Code workspace folder
On Linux, you may hit inotify limits with many repositories:
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Repository Disappeared

1

Check if Deleted

Verify .git directory still exists on disk
2

Reload Window

Try reloading VS Code window (Cmd/Ctrl + R)
3

Re-add Folder

Remove and re-add workspace folder
4

Manual Validation

Close and reopen Git Graph to trigger validation
SettingTypeDefaultDescription
maxDepthOfRepoSearchnumber0How deep to search for repositories
showStatusBarItembooleantrueShow Git Graph in status bar

Best Practices

  • Group related repositories in workspace folders
  • Use descriptive folder names
  • Consider VS Code multi-root workspaces for complex setups
  • Limit to 10-20 repositories per workspace
  • Use multiple VS Code windows for more repositories
  • Consider workspace file organization

Build docs developers (and LLMs) love