Use repositories instead of workspace folders

# Use repositories instead of workspace folders

TL;DR: We use the wrong abstraction in the code. If we change the abstraction, we'll simplify the code significantly, make future work on the codebase much easier.

## What is a workspace?

Workspace is the content of an open VS Code window. It can either contain one open folder or multiple folders if you use [Multi-root Workspace](https://code.visualstudio.com/docs/editor/multi-root-workspaces).

| single foldre in the workspace | mutli-root workspace |
| ---- | ---- |
| ![e3c8ae52e5a24d82bfd88b406d150871](/uploads/828ff723636f899733269b1e1e2eb2f6/e3c8ae52e5a24d82bfd88b406d150871.png) | ![2e96f2863e554495b225c1770e775aa4](/uploads/305198895c82e53256e795ae03ef36d4/2e96f2863e554495b225c1770e775aa4.png) |


GitLab Workflow uses the workspace folder to run the `git` command. The most common use case is this:

```mermaid
graph TD;
A[Obtains workspaceFolder from VS Code] --"/workspace/extension"--> B["Run git remote -v in that folder to get remote URL"]
B --"git@gitlab.com:gitlab-org/gitlab-vscode-extension.git"--> C["parse namespace and project from the remote"]
C --"gitlab-org/gitlab-vscode-extension"--> D["Run API queries for this project"]
```

### The problem with using `workspaceFolders`

The problem with this approach is that **the `workspaceFolder` doesn't have 1:1 relationship with repository**. You can open the VS Code in any folder (`code .` in the command line). If we have the following folder structure:

```
.
└── workspace/
    ├── projectA/
    │   ├── .git
    │   ├── frontend/
    │   │   └── src
    │   └── backend
    └── projectB/
        ├── .git
        └── src/
```

This structure allows for the following three scenarios:

1. Open `projectB` (`cd workspace/projectB && code .`) - ✅ This will work fine because the `workspaceFolder` is a git repository at the same time
2. Open `frontend` (`cd workspace/projectA/frontend && code .`) - ⚠️This now works, because there is still only one repository for the `workspaceFolder`. But it caused us trouble in the past:
	- 🐞 [Open active file on GitLab does not resolve from git repository root](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/issues/327)
3. Open `workspace` (`cd workspace && code .`) - ❌ The extension will run `git` command in the folder called `worskpace` and think that there are no repositories. `workspace` folder contains two repositories.
    - 🐞 [Does not support workspace folders containing multiple repositories](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/issues/216)


## Better approach: Using repositories

The key insight is that the `workspaceFolder` <--> git repository 1:n relationship makes it very hard to keep the extension git information in sync with VS Code.

We've recently introduced the VS Code Git extension to our codebase ([Support VSCode "git clone" command](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/issues/222)) and now we can use the way VS Code recognizes repositories.

### Folder that contains multiple repositories

| explorer| source control (git) tree view | GitLab Workflow tree view |
| -- | -- | -- |
| ![cbdebfffbbb54267b27496f93ffd4718](/uploads/567e82034650e94b0b2e07499ec3acbf/cbdebfffbbb54267b27496f93ffd4718.png) | ![74b38c20d8b544f99efe3a0b4ed18367](/uploads/d157e0084e1c5dcc7b410da04f28c75c/74b38c20d8b544f99efe3a0b4ed18367.png) | ![0197b75bb9b6465bab9c1459eb5fcd84](/uploads/2813581eb23320951549eeab427c6f88/0197b75bb9b6465bab9c1459eb5fcd84.png) |

### Benefits of switching to repositories

- Benefits of changing the abstraction
  - Clear abstraction. GitLab project = git repository. Easy to understand for new and existing contributors.
- Benefits of switching to the VS Code git extension
  - **Single source of truth for the git information**, if user sees 4 repostiories in the SCM tree view, they'll see up to 4 in the GitLab Workflow TreeView
  - Less dependency on `execa` module to run our custom `git` commands and parse them. Running the git commands ourselves is tricky from a security perspective (e.g. [Client side code execution](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/issues/170))


### Drawbacks of switching to repositories

There is no drawback (that I can think of) of **changing the abstraction**. However, there is a drawback of using the VS Code Git extension:

- Dependency on the VS Code Git extension means that if there is a bug or a missing feature, we are not fully in control, and we can only contribute the fix upstream and wait for a release.
	- There is plenty of popular extensions (GitHub Pull Request, Git Lenses) depending on the same API, the "severe bug" scenario is not very likely.
	- We can still use `execa` for any missing git commands/features, but I don't know of any.

## Getting technical

**(some experience with the extension code is recommended)**

I have implemented a POC of this change in [refactor-to-use-git-repositories](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/tree/refactor-to-use-git-repositories) branch. The potential for simplifying the code got me excited enough to write this issue.

### Opportunity for simplifying the codebase

The way we now obtain information about the git repository or how we call API is quite complex. If we create a wrapper around the `Repository` that we get from VS Code Git extension, we can reduce a significant amount of code and complexity from the extension[^1].

<details>
	<summary>Good example</summary>

In this example, we already built the GitLabNewService to be decoupled from the repository logic.

[Getting the `GitLabNewService`](https://gitlab.com/gitlab-org/gitlab-vscode-extension/blob/42ba142134b0ace97ae56a36f2f1131eba212a39/src/service_factory.ts#L16-18)

```ts
export async function createGitLabNewService(workspaceFolder: string): Promise<GitLabNewService> {
  return new GitLabNewService(await getInstanceUrl(workspaceFolder));
}
```

[Using the new service](https://gitlab.com/gitlab-org/gitlab-vscode-extension/blob/c012837326fca58ec4dcf4e85fe94f3483a261f3/src/commands/insert_snippet.ts#L34-37):

```
 const gitService = createGitService(workspaceFolder);
  const gitLabService = await createGitLabNewService(workspaceFolder);
  const remote = await gitService.fetchGitRemote();
  const snippets = await gitLabService.getSnippets(`${remote.namespace}/${remote.project}`);
```

</details>

<details>
	<summary>Bad example</summary>

The old GitLabService is incredibly coupled with the workspace and git logic:

```ts
export async function getAllGitlabWorkspaces(): Promise<GitLabWorkspace[]> {
  if (!vscode.workspace.workspaceFolders) {
    return [];
  }
  const projectsWithUri = vscode.workspace.workspaceFolders.map(async workspaceFolder => {
    const uri = workspaceFolder.uri.fsPath;
    try {
      const currentProject = await fetchCurrentProject(uri);
      return {
        label: currentProject?.name ?? basename(uri),
        uri,
      };
    } catch (e) {
      logError(e);
      return { label: basename(uri), uri, error: true };
    }
  });

  return Promise.all(projectsWithUri);
}
```

```ts
export async function fetchCurrentProject(workspaceFolder: string): Promise<GitLabProject | null> {
  try {
    const remote = await createGitService(workspaceFolder).fetchGitRemote();

    return await fetchProjectData(remote, workspaceFolder);
  } catch (e) {
    throw new ApiError(e, 'get current project');
  }
}
```

</details>

I imagine the new repository centric approach like this:

```js
const activeEditor = vscode.workspace.activeEditor;
const gitlabRepository = repositoryManager.getRepositoryForFile(activeEditor.document.uri);
const snippets = await gitlabRepository.gitlabService.getSnippets();
const activeBranch = await gitlabRepository.git.getActiveBranch();
```

## Related

- [Consistent handling of remotes, instances and branches](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/issues/260)
- [Replace the library for interacting with git](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/issues/203)

[^1]: I'm confident that even with covering the new logic with unit tests, we'll end up with less code in total.

Story points: 3.0