Skip to content

Commit a860421

Browse files
committed
Handle correctly the initialization of a repository on adding a non-git directory
1 parent b9176a5 commit a860421

File tree

9 files changed

+104
-73
lines changed

9 files changed

+104
-73
lines changed

apps/desktop/src/components/ChromeHeader.svelte

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
import { ircEnabled } from '$lib/config/uiFeatureFlags';
99
import { IRC_SERVICE } from '$lib/irc/ircService.svelte';
1010
import { MODE_SERVICE } from '$lib/mode/modeService';
11-
import { handleAddProjectOutcome } from '$lib/project/project';
1211
import { PROJECTS_SERVICE } from '$lib/project/projectsService';
12+
import { useAddProject } from '$lib/project/useProjects.svelte';
1313
import { ircPath, projectPath, isWorkspacePath } from '$lib/routes/routes.svelte';
1414
import { UI_STATE } from '$lib/state/uiState.svelte';
1515
import { inject } from '@gitbutler/core/context';
@@ -47,6 +47,8 @@
4747
const singleBranchMode = $derived($settingsStore?.featureFlags.singleBranch ?? false);
4848
const backend = inject(BACKEND);
4949
50+
const { addProject } = useAddProject();
51+
5052
const mode = $derived(modeService.mode({ projectId }));
5153
const currentMode = $derived(mode.response);
5254
const currentBranchName = $derived.by(() => {
@@ -188,14 +190,7 @@
188190
onClick={async () => {
189191
newProjectLoading = true;
190192
try {
191-
const outcome = await projectsService.addProject();
192-
if (!outcome) {
193-
// User cancelled the project creation
194-
newProjectLoading = false;
195-
return;
196-
}
197-
198-
handleAddProjectOutcome(outcome, (project) => goto(projectPath(project.id)));
193+
await addProject();
199194
} finally {
200195
newProjectLoading = false;
201196
}

apps/desktop/src/components/CloneForm.svelte

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@
55
import { OnboardingEvent, POSTHOG_WRAPPER } from '$lib/analytics/posthog';
66
import { BACKEND } from '$lib/backend';
77
import { GIT_SERVICE } from '$lib/git/gitService';
8-
import { handleAddProjectOutcome } from '$lib/project/project';
9-
import { PROJECTS_SERVICE } from '$lib/project/projectsService';
10-
import { projectPath } from '$lib/routes/routes.svelte';
8+
import { useAddProject } from '$lib/project/useProjects.svelte';
119
import { parseRemoteUrl } from '$lib/url/gitUrl';
1210
import { inject } from '@gitbutler/core/context';
1311
import { persisted } from '@gitbutler/shared/persisted';
@@ -16,7 +14,6 @@
1614
import * as Sentry from '@sentry/sveltekit';
1715
import { onMount } from 'svelte';
1816
19-
const projectsService = inject(PROJECTS_SERVICE);
2017
const gitService = inject(GIT_SERVICE);
2118
const posthog = inject(POSTHOG_WRAPPER);
2219
const backend = inject(BACKEND);
@@ -62,6 +59,14 @@
6259
return String(error);
6360
}
6461
62+
const { addProject } = useAddProject(() => {
63+
posthog.captureOnboarding(
64+
OnboardingEvent.ClonedProjectFailed,
65+
'Failed to add project after cloning'
66+
);
67+
throw new Error('Failed to add project after cloning.');
68+
});
69+
6570
async function cloneRepository() {
6671
loading = true;
6772
savedTargetDirPath.set(targetDirPath);
@@ -88,16 +93,7 @@
8893
await gitService.cloneRepo(repositoryUrl, targetDir);
8994
9095
posthog.captureOnboarding(OnboardingEvent.ClonedProject);
91-
const outcome = await projectsService.addProject(targetDir);
92-
if (!outcome) {
93-
posthog.captureOnboarding(
94-
OnboardingEvent.ClonedProjectFailed,
95-
'Failed to add project after cloning'
96-
);
97-
throw new Error('Failed to add project after cloning.');
98-
}
99-
100-
handleAddProjectOutcome(outcome, (project) => goto(projectPath(project.id)));
96+
await addProject(targetDir);
10197
} catch (e) {
10298
Sentry.captureException(e);
10399
const errorMessage = getErrorMessage(e);

apps/desktop/src/components/FileMenuAction.svelte

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,19 @@
11
<script lang="ts">
22
import { goto } from '$app/navigation';
3-
import { handleAddProjectOutcome } from '$lib/project/project';
4-
import { PROJECTS_SERVICE } from '$lib/project/projectsService';
5-
import { clonePath, projectPath } from '$lib/routes/routes.svelte';
3+
import { useAddProject } from '$lib/project/useProjects.svelte';
4+
import { clonePath } from '$lib/routes/routes.svelte';
65
import { SHORTCUT_SERVICE } from '$lib/shortcuts/shortcutService';
76
import { inject } from '@gitbutler/core/context';
87
import { mergeUnlisten } from '@gitbutler/ui/utils/mergeUnlisten';
98
10-
const projectsService = inject(PROJECTS_SERVICE);
119
const shortcutService = inject(SHORTCUT_SERVICE);
1210
11+
const { addProject } = useAddProject();
12+
1313
$effect(() =>
1414
mergeUnlisten(
1515
shortcutService.on('add-local-repo', async () => {
16-
const outcome = await projectsService.addProject();
17-
if (!outcome) {
18-
// User cancelled the project creation
19-
return;
20-
}
21-
handleAddProjectOutcome(outcome, (project) => goto(projectPath(project.id)));
16+
await addProject();
2217
}),
2318
shortcutService.on('clone-repo', async () => {
2419
goto(clonePath());

apps/desktop/src/components/ProjectSwitcher.svelte

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts">
22
import { goto } from '$app/navigation';
3-
import { handleAddProjectOutcome } from '$lib/project/project';
43
import { PROJECTS_SERVICE } from '$lib/project/projectsService';
4+
import { useAddProject } from '$lib/project/useProjects.svelte';
55
import { projectPath } from '$lib/routes/routes.svelte';
66
import { inject } from '@gitbutler/core/context';
77
import { Button, OptionsGroup, Select, SelectItem } from '@gitbutler/ui';
@@ -22,6 +22,8 @@
2222
2323
let newProjectLoading = $state(false);
2424
let cloneProjectLoading = $state(false);
25+
26+
const { addProject } = useAddProject();
2527
</script>
2628

2729
<div class="project-switcher">
@@ -48,13 +50,7 @@
4850
onClick={async () => {
4951
newProjectLoading = true;
5052
try {
51-
const outcome = await projectsService.addProject();
52-
if (!outcome) {
53-
// User cancelled the project creation
54-
newProjectLoading = false;
55-
return;
56-
}
57-
handleAddProjectOutcome(outcome, (project) => goto(projectPath(project.id)));
53+
await addProject();
5854
} finally {
5955
newProjectLoading = false;
6056
}

apps/desktop/src/components/Welcome.svelte

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import { OnboardingEvent, POSTHOG_WRAPPER } from '$lib/analytics/posthog';
77
import cloneRepoSvg from '$lib/assets/welcome/clone-repo.svg?raw';
88
import newProjectSvg from '$lib/assets/welcome/new-local-project.svg?raw';
9+
import { showToast } from '$lib/notifications/toasts';
910
import { handleAddProjectOutcome } from '$lib/project/project';
1011
import { PROJECTS_SERVICE } from '$lib/project/projectsService';
1112
import { inject } from '@gitbutler/core/context';
@@ -17,23 +18,33 @@
1718
let newProjectLoading = $state(false);
1819
let directoryInputElement = $state<HTMLInputElement | undefined>();
1920
20-
async function onNewProject() {
21-
newProjectLoading = true;
21+
async function addProject(path: string) {
2222
try {
23-
const testDirectoryPath = directoryInputElement?.value;
24-
const outcome = await projectsService.addProject(testDirectoryPath ?? '');
25-
23+
const outcome = await projectsService.addProject(path);
2624
posthog.captureOnboarding(OnboardingEvent.AddLocalProject);
25+
2726
if (outcome) {
28-
handleAddProjectOutcome(outcome);
27+
handleAddProjectOutcome(outcome, async (path: string) => {
28+
await projectsService.initGitRepository(path);
29+
showToast({
30+
title: 'Repository Initialized',
31+
message: `Git repository has been successfully initialized at ${path}. Loading project...`,
32+
style: 'info'
33+
});
34+
await addProject(path);
35+
});
2936
}
3037
} catch (e: unknown) {
3138
posthog.captureOnboarding(OnboardingEvent.AddLocalProjectFailed, e);
32-
} finally {
33-
newProjectLoading = false;
3439
}
3540
}
3641
42+
async function onNewProject() {
43+
newProjectLoading = true;
44+
await addProject(directoryInputElement?.value ?? '');
45+
newProjectLoading = false;
46+
}
47+
3748
async function onCloneProject() {
3849
goto('/onboarding/clone');
3950
}

apps/desktop/src/lib/project/project.ts

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { goto } from '$app/navigation';
2-
import { showError, showToast } from '$lib/notifications/toasts';
2+
import { showToast } from '$lib/notifications/toasts';
33
import { projectPath } from '$lib/routes/routes.svelte';
44
import { TestId } from '@gitbutler/ui';
55
import type { ForgeName } from '$lib/forge/interface/forge';
@@ -85,15 +85,19 @@ export type AddProjectOutcome =
8585
/**
8686
* The error message received
8787
*/
88-
subject: string;
88+
subject: {
89+
path: string;
90+
message: string;
91+
};
8992
};
9093

9194
/**
9295
* Correctly handle the outcome of an addProject operation by passing the project to the callback or
93-
* showing toasts as necessary.
96+
* showing toasts as necessary.'get this - needs a refactor probably';
9497
*/
9598
export function handleAddProjectOutcome(
9699
outcome: AddProjectOutcome,
100+
onInitialize: (path: string) => Promise<void>,
97101
onAdded?: (project: Project) => void
98102
): true {
99103
switch (outcome.type) {
@@ -169,23 +173,9 @@ export function handleAddProjectOutcome(
169173
extraAction: {
170174
label: 'Initialize Repository',
171175
onClick: async (dismiss) => {
172-
try {
173-
const projectPath = 'get this - needs a refactor probably';
174-
await projectsService.initGitRepository(projectPath);
175-
dismiss();
176-
showToast({
177-
title: 'Repository Initialized',
178-
message: `Git repository has been successfully initialized at ${projectPath}. Loading project...`,
179-
style: 'info'
180-
});
181-
const projectId = 'TODO: get this from somewhere';
182-
// Retry setting the project active
183-
await setActiveProjectOrRedirect(projectId);
184-
} catch (initError) {
185-
console.error('Failed to initialize repository:', initError);
186-
dismiss();
187-
showError('Failed to initialize repository', initError);
188-
}
176+
const projectPath = outcome.subject.path;
177+
await onInitialize(projectPath);
178+
dismiss();
189179
}
190180
}
191181
});
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { goto } from '$app/navigation';
2+
import { showToast } from '$lib/notifications/toasts';
3+
import { handleAddProjectOutcome } from '$lib/project/project';
4+
import { PROJECTS_SERVICE } from '$lib/project/projectsService';
5+
import { projectPath } from '$lib/routes/routes.svelte';
6+
import { inject } from '@gitbutler/core/context';
7+
8+
export function useAddProject(onMissingOutcome?: () => void) {
9+
const projectsService = inject(PROJECTS_SERVICE);
10+
11+
async function addProject(path?: string) {
12+
const outcome = await projectsService.addProject(path);
13+
14+
if (outcome) {
15+
handleAddProjectOutcome(
16+
outcome,
17+
async (path: string) => {
18+
await projectsService.initGitRepository(path);
19+
showToast({
20+
title: 'Repository Initialized',
21+
message: `Git repository has been successfully initialized at ${path}. Loading project...`,
22+
style: 'info'
23+
});
24+
await addProject(path);
25+
},
26+
(project) => goto(projectPath(project.id))
27+
);
28+
} else {
29+
onMissingOutcome?.();
30+
}
31+
}
32+
33+
return { addProject };
34+
}

crates/gitbutler-project/src/controller.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ use anyhow::{anyhow, bail, Context, Result};
44
use gitbutler_error::error;
55

66
use super::{storage, storage::UpdateRequest, Project, ProjectId};
7-
use crate::{project::AddProjectOutcome, AuthKey};
7+
use crate::{
8+
project::{AddProjectOutcome, NotAGitRepositoryOutcome},
9+
AuthKey,
10+
};
811

912
#[derive(Clone, Debug)]
1013
pub(crate) struct Controller {
@@ -97,7 +100,12 @@ impl Controller {
97100
}
98101
},
99102
Err(err) => {
100-
return Ok(AddProjectOutcome::NotAGitRepository(err.to_string()));
103+
return Ok(AddProjectOutcome::NotAGitRepository(
104+
NotAGitRepositoryOutcome {
105+
path: path.to_path_buf(),
106+
message: err.to_string(),
107+
},
108+
));
101109
}
102110
}
103111

crates/gitbutler-project/src/project.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,12 @@ impl Project {
183183
}
184184
}
185185

186+
#[derive(Debug, Serialize, Deserialize, Clone)]
187+
pub struct NotAGitRepositoryOutcome {
188+
pub path: PathBuf,
189+
pub message: String,
190+
}
191+
186192
#[derive(Debug, Serialize, Deserialize, Clone, strum::Display)]
187193
#[serde(rename_all = "camelCase", tag = "type", content = "subject")]
188194
pub enum AddProjectOutcome {
@@ -194,7 +200,7 @@ pub enum AddProjectOutcome {
194200
NonMainWorktree,
195201
NoWorkdir,
196202
NoDotGitDirectory,
197-
NotAGitRepository(String),
203+
NotAGitRepository(NotAGitRepositoryOutcome),
198204
}
199205

200206
impl AddProjectOutcome {
@@ -229,7 +235,7 @@ impl AddProjectOutcome {
229235
Err(anyhow::anyhow!("no .git directory found in repository"))
230236
}
231237
AddProjectOutcome::NotAGitRepository(msg) => {
232-
Err(anyhow::anyhow!("not a git repository: {}", msg))
238+
Err(anyhow::anyhow!("not a git repository: {}", msg.message))
233239
}
234240
}
235241
}

0 commit comments

Comments
 (0)