Skip to content

Commit

Permalink
add sync command full function
Browse files Browse the repository at this point in the history
  • Loading branch information
Opeyem1a committed Oct 29, 2024
1 parent f21515f commit a98c01e
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 25 deletions.
2 changes: 1 addition & 1 deletion VISION.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,6 @@ This is v0.2.X

This is v0.3.X

13. Build `sync` 🟠
13. Build `sync` * (tentative, needs testing)

This is v1.
77 changes: 66 additions & 11 deletions src/commands/sync.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import ErrorDisplay from '../components/error-display.js';
import React, { useCallback } from 'react';
import React, { useCallback, useState } from 'react';
import { Action, useAction } from '../hooks/use-action.js';
import { CommandConfig, CommandProps } from '../types.js';
import { ConfirmStatement } from '../components/confirm-statement.js';
import { Loading } from '../components/loading.js';
import { RecursiveRebaser } from '../components/recursive-rebaser.js';
import { SelectRootBranch } from '../components/select-root-branch.js';
Expand Down Expand Up @@ -37,10 +38,7 @@ const DoSync = ({
rootBranchName: string;
currentBranchName: string;
}) => {
const result = useSyncAction({
rootBranchName,
currentBranchName,
});
const result = useSyncAction({ rootBranchName });

if (result.isError) {
return <ErrorDisplay error={result.error} />;
Expand All @@ -50,7 +48,30 @@ const DoSync = ({
return <Loading />;
}

// todo: prompt to delete branches
const { contestedBranch, deleteBranch, skipContestedBranch } = result;

if (contestedBranch) {
return (
<ConfirmStatement
statement={
<Text>
It seems like{' '}
<Text color="yellow" bold>
{contestedBranch}
</Text>{' '}
was deleted in the remote repository. Delete it locally?
</Text>
}
onAccept={() => {
if (contestedBranch) void deleteBranch(contestedBranch);
}}
onDeny={() => {
if (contestedBranch) skipContestedBranch(contestedBranch);
}}
/>
);
}

return (
<RecursiveRebaser
baseBranch={rootBranchName}
Expand All @@ -60,28 +81,62 @@ const DoSync = ({
);
};

type UseSyncActionResult = Action;
type UseSyncActionResult = Action & {
deleteBranch: (branch: string) => Promise<void>;
skipContestedBranch: (branch: string) => void;
contestedBranch: string | undefined;
};

const useSyncAction = ({
currentBranchName,
rootBranchName,
}: {
currentBranchName: string;
rootBranchName: string;
}): UseSyncActionResult => {
const git = useGit();
const { currentTree, removeBranch } = useTree();
const [allContestedBranches, setAllContestedBranches] = useState<string[]>(
[]
);

const skipContestedBranch = useCallback((branch: string) => {
setAllContestedBranches((prev) => prev.filter((b) => b !== branch));
}, []);

const deleteBranch = useCallback(
async (branch: string) => {
// do the git branch delete first, since this is more error-prone
await git.branchDelete(branch);
removeBranch(branch);
skipContestedBranch(branch);
},
[git, skipContestedBranch]
);

const performAction = useCallback(async () => {
// todo: unsure if this is the correct condition
if (!currentTree.length) return;

await git.checkout(rootBranchName);
await git.pull();
await git.checkout(currentBranchName);
}, [git]);

for (const node of currentTree) {
const closedOnRemote = await git.isClosedOnRemote(node.key);
if (closedOnRemote) {
setAllContestedBranches((prev) => [...prev, node.key]);
}
}
}, [git, currentTree]);

const action = useAction({
asyncAction: performAction,
});

return {
...action,
// always get the first one, we're filtering the array until it is empty
contestedBranch: allContestedBranches[0],
deleteBranch,
skipContestedBranch,
} as UseSyncActionResult;
};

Expand Down
12 changes: 10 additions & 2 deletions src/services/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface GitService {
isClosedOnRemote: (branch: string) => Promise<boolean>;
fetchPrune: () => Promise<void>;
pull: () => Promise<void>;
branchDelete: (branch: string) => Promise<void>;
}

export const createGitService = ({
Expand Down Expand Up @@ -141,14 +142,18 @@ export const createGitService = ({
return ontoBranchLatestHash !== commonAncestorCommit;
},
isClosedOnRemote: async (branch: string) => {
const { all } = await gitEngine.branch(['-a']);
// this is only accurate is "git fetch -p" or some equivalent has been run
// recently enough to have up-to-date information in the refs
const { all, branches } = await gitEngine.branch(['-a', '-vv']);
const remoteBranchName = `remotes/origin/${branch}`;

if (all.includes(remoteBranchName)) {
// if remote is still in the ref, then it's still a living remote branch in the upstream
return false;
}

if (all.includes(branch)) {
const label: string = branches?.[branch]?.label ?? '';
if (label.includes(`[origin/${branch}: gone]`)) {
return true;
}

Expand All @@ -160,5 +165,8 @@ export const createGitService = ({
pull: async () => {
await gitEngine.pull(['--ff-only', '--prune']);
},
branchDelete: async (branch: string) => {
await gitEngine.deleteLocalBranch(branch, true);
},
};
};
14 changes: 7 additions & 7 deletions src/services/tree.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,12 @@ describe('tree service is working', () => {
expect(get()).to.deep.equal([
{ key: 'root', parent: null },
{ key: 'branch_a', parent: 'root' },
{ key: 'branch_b_a', parent: 'branch_b' },
{ key: 'branch_b_b', parent: 'branch_b' },
{ key: 'branch_b_a', parent: 'root' },
{ key: 'branch_b_b', parent: 'root' },
]);
});

it('can remove parent branches without removing the child branches', () => {
it("removes parent branches without removing the child branches, but reattaches them to tree at the removed branch's parent", () => {
const { registerRoot, attachTo, removeBranch, get } =
createTreeService();

Expand All @@ -135,7 +135,7 @@ describe('tree service is working', () => {
expect(get()).to.deep.equal([
{ key: 'root', parent: null },
{ key: 'branch_b', parent: 'root' },
{ key: 'branch_a_a', parent: 'branch_a' },
{ key: 'branch_a_a', parent: 'root' },
{ key: 'branch_b_a', parent: 'branch_b' },
{ key: 'branch_b_b', parent: 'branch_b' },
]);
Expand All @@ -144,9 +144,9 @@ describe('tree service is working', () => {

expect(get()).to.deep.equal([
{ key: 'root', parent: null },
{ key: 'branch_a_a', parent: 'branch_a' },
{ key: 'branch_b_a', parent: 'branch_b' },
{ key: 'branch_b_b', parent: 'branch_b' },
{ key: 'branch_a_a', parent: 'root' },
{ key: 'branch_b_a', parent: 'root' },
{ key: 'branch_b_b', parent: 'root' },
]);
});
});
14 changes: 10 additions & 4 deletions src/services/tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,16 @@ const removeBranch = (
throw Error('Cannot remove root branch');
}

_saveTree(
tree.filter((b) => b.key !== branchToRemove.key),
deps
);
const removedBranchParent = branchToRemove.parent;
const treeWithBranchRemoved = tree
.filter((b) => b.key !== branchToRemove.key)
.map((b) => {
if (b.parent === branchToRemove.key)
return { ...b, parent: removedBranchParent };
return b;
});

_saveTree(treeWithBranchRemoved, deps);

deps.setCurrentTree(_readTree(deps));
return branchToRemove;
Expand Down

0 comments on commit a98c01e

Please sign in to comment.