mirror of
https://github.com/aljazceru/claude-code-viewer.git
synced 2025-12-27 18:24:23 +01:00
feat: Filter git revisions to show only base and current branches (#47)
* feat: Unify git revisions API to current-revisions endpoint - Add getCurrentRevisions to GitController returning base branch, current branch, head, and commits - Implement findBaseBranch in GitService to identify base branch from commit history - Add getCommitsBetweenBranches to get commits between base and target branch - Remove separate branches and commits API endpoints - Update frontend to use unified gitCurrentRevisionsQuery - Replace useGitBranches and useGitCommits with useGitCurrentRevisions hook 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat: Display current branch in session header - Move useGitCurrentRevisions hook to SessionPageMain level - Display current branch badge with GitBranchIcon in session header - Pass revisionsData from parent to DiffModal to avoid duplicate API calls - Update DiffModalProps to accept optional revisionsData parameter - Show current branch between project path and session ID badges 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { Trans } from "@lingui/react";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import {
|
||||
GitBranchIcon,
|
||||
GitCompareIcon,
|
||||
LoaderIcon,
|
||||
MenuIcon,
|
||||
@@ -17,6 +18,7 @@ import { Badge } from "../../../../../../components/ui/badge";
|
||||
import { honoClient } from "../../../../../../lib/api/client";
|
||||
import { useProject } from "../../../hooks/useProject";
|
||||
import { firstUserMessageToTitle } from "../../../services/firstCommandToTitle";
|
||||
import { useGitCurrentRevisions } from "../hooks/useGit";
|
||||
import { useSession } from "../hooks/useSession";
|
||||
import { useSessionProcess } from "../hooks/useSessionProcess";
|
||||
import { ConversationList } from "./conversationList/ConversationList";
|
||||
@@ -39,6 +41,7 @@ export const SessionPageMain: FC<{
|
||||
const { currentPermissionRequest, isDialogOpen, onPermissionResponse } =
|
||||
usePermissionRequests();
|
||||
const [isDiffModalOpen, setIsDiffModalOpen] = useState(false);
|
||||
const { data: revisionsData } = useGitCurrentRevisions(projectId);
|
||||
|
||||
const abortTask = useMutation({
|
||||
mutationFn: async (sessionProcessId: string) => {
|
||||
@@ -122,6 +125,15 @@ export const SessionPageMain: FC<{
|
||||
{project.meta.projectPath ?? project.claudeProjectPath}
|
||||
</Badge>
|
||||
)}
|
||||
{revisionsData?.success && revisionsData.data.currentBranch && (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="h-6 sm:h-8 text-xs sm:text-sm flex items-center gap-1"
|
||||
>
|
||||
<GitBranchIcon className="w-3 h-3 sm:w-3.5 sm:h-3.5" />
|
||||
{revisionsData.data.currentBranch.name}
|
||||
</Badge>
|
||||
)}
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="h-6 sm:h-8 text-xs sm:text-sm flex items-center"
|
||||
@@ -256,6 +268,7 @@ export const SessionPageMain: FC<{
|
||||
projectId={projectId}
|
||||
isOpen={isDiffModalOpen}
|
||||
onOpenChange={setIsDiffModalOpen}
|
||||
revisionsData={revisionsData}
|
||||
/>
|
||||
|
||||
{/* Permission Dialog */}
|
||||
|
||||
@@ -25,8 +25,7 @@ import { cn } from "@/lib/utils";
|
||||
import {
|
||||
useCommitAndPush,
|
||||
useCommitFiles,
|
||||
useGitBranches,
|
||||
useGitCommits,
|
||||
useGitCurrentRevisions,
|
||||
useGitDiff,
|
||||
usePushCommits,
|
||||
} from "../../hooks/useGit";
|
||||
@@ -141,6 +140,7 @@ export const DiffModal: FC<DiffModalProps> = ({
|
||||
projectId,
|
||||
defaultCompareFrom = "HEAD",
|
||||
defaultCompareTo = "working",
|
||||
revisionsData: parentRevisionsData,
|
||||
}) => {
|
||||
const { i18n } = useLingui();
|
||||
const commitMessageId = useId();
|
||||
@@ -158,11 +158,10 @@ export const DiffModal: FC<DiffModalProps> = ({
|
||||
// Commit section collapse state (default: collapsed)
|
||||
const [isCommitSectionExpanded, setIsCommitSectionExpanded] = useState(false);
|
||||
|
||||
// API hooks
|
||||
const { data: branchesData, isLoading: isLoadingBranches } =
|
||||
useGitBranches(projectId);
|
||||
const { data: commitsData, isLoading: isLoadingCommits } =
|
||||
useGitCommits(projectId);
|
||||
// API hooks - use parent data if available, otherwise fetch
|
||||
const { data: fetchedRevisionsData, isLoading: isLoadingRevisions } =
|
||||
useGitCurrentRevisions(projectId);
|
||||
const revisionsData = parentRevisionsData ?? fetchedRevisionsData;
|
||||
const {
|
||||
mutate: getDiff,
|
||||
data: diffData,
|
||||
@@ -173,9 +172,9 @@ export const DiffModal: FC<DiffModalProps> = ({
|
||||
const pushMutation = usePushCommits(projectId);
|
||||
const commitAndPushMutation = useCommitAndPush(projectId);
|
||||
|
||||
// Transform branches and commits data to GitRef format
|
||||
// Transform revisions data to GitRef format
|
||||
const gitRefs: GitRef[] =
|
||||
branchesData?.success && branchesData.data
|
||||
revisionsData?.success && revisionsData.data
|
||||
? [
|
||||
{
|
||||
name: "working" as const,
|
||||
@@ -187,21 +186,35 @@ export const DiffModal: FC<DiffModalProps> = ({
|
||||
type: "commit" as const,
|
||||
displayName: "HEAD",
|
||||
},
|
||||
...branchesData.data.map((branch) => ({
|
||||
name: `branch:${branch.name}` as const,
|
||||
type: "branch" as const,
|
||||
displayName: branch.name + (branch.current ? " (current)" : ""),
|
||||
sha: branch.commit,
|
||||
})),
|
||||
// Add commits from current branch
|
||||
...(commitsData?.success && commitsData.data
|
||||
? commitsData.data.map((commit) => ({
|
||||
name: `commit:${commit.sha}` as const,
|
||||
type: "commit" as const,
|
||||
displayName: `${commit.message.substring(0, 50)}${commit.message.length > 50 ? "..." : ""}`,
|
||||
sha: commit.sha,
|
||||
}))
|
||||
// Add base branch if exists
|
||||
...(revisionsData.data.baseBranch
|
||||
? [
|
||||
{
|
||||
name: `branch:${revisionsData.data.baseBranch.name}` as const,
|
||||
type: "branch" as const,
|
||||
displayName: `${revisionsData.data.baseBranch.name} (base)`,
|
||||
sha: revisionsData.data.baseBranch.commit,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
// Add current branch if exists
|
||||
...(revisionsData.data.currentBranch
|
||||
? [
|
||||
{
|
||||
name: `branch:${revisionsData.data.currentBranch.name}` as const,
|
||||
type: "branch" as const,
|
||||
displayName: `${revisionsData.data.currentBranch.name} (current)`,
|
||||
sha: revisionsData.data.currentBranch.commit,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
// Add commits from current branch
|
||||
...revisionsData.data.commits.map((commit) => ({
|
||||
name: `commit:${commit.sha}` as const,
|
||||
type: "commit" as const,
|
||||
displayName: `${commit.message.substring(0, 50)}${commit.message.length > 50 ? "..." : ""}`,
|
||||
sha: commit.sha,
|
||||
})),
|
||||
]
|
||||
: [];
|
||||
|
||||
@@ -397,10 +410,7 @@ export const DiffModal: FC<DiffModalProps> = ({
|
||||
<Button
|
||||
onClick={handleCompare}
|
||||
disabled={
|
||||
isDiffLoading ||
|
||||
isLoadingBranches ||
|
||||
isLoadingCommits ||
|
||||
compareFrom === compareTo
|
||||
isDiffLoading || isLoadingRevisions || compareFrom === compareTo
|
||||
}
|
||||
className="sm:self-end w-full sm:w-auto"
|
||||
>
|
||||
|
||||
@@ -45,4 +45,36 @@ export interface DiffModalProps {
|
||||
onOpenChange: (open: boolean) => void;
|
||||
defaultCompareFrom?: string;
|
||||
defaultCompareTo?: string;
|
||||
revisionsData?:
|
||||
| {
|
||||
success: true;
|
||||
data: {
|
||||
baseBranch: {
|
||||
name: string;
|
||||
current: boolean;
|
||||
remote?: string;
|
||||
commit: string;
|
||||
ahead?: number;
|
||||
behind?: number;
|
||||
} | null;
|
||||
currentBranch: {
|
||||
name: string;
|
||||
current: boolean;
|
||||
remote?: string;
|
||||
commit: string;
|
||||
ahead?: number;
|
||||
behind?: number;
|
||||
} | null;
|
||||
head: string | null;
|
||||
commits: Array<{
|
||||
sha: string;
|
||||
message: string;
|
||||
author: string;
|
||||
date: string;
|
||||
}>;
|
||||
};
|
||||
}
|
||||
| {
|
||||
success: false;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,22 +1,11 @@
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import { honoClient } from "@/lib/api/client";
|
||||
import {
|
||||
gitBranchesQuery,
|
||||
gitCommitsQuery,
|
||||
} from "../../../../../../lib/api/queries";
|
||||
import { gitCurrentRevisionsQuery } from "../../../../../../lib/api/queries";
|
||||
|
||||
export const useGitBranches = (projectId: string) => {
|
||||
export const useGitCurrentRevisions = (projectId: string) => {
|
||||
return useQuery({
|
||||
queryKey: gitBranchesQuery(projectId).queryKey,
|
||||
queryFn: gitBranchesQuery(projectId).queryFn,
|
||||
staleTime: 30000, // 30 seconds
|
||||
});
|
||||
};
|
||||
|
||||
export const useGitCommits = (projectId: string) => {
|
||||
return useQuery({
|
||||
queryKey: gitCommitsQuery(projectId).queryKey,
|
||||
queryFn: gitCommitsQuery(projectId).queryFn,
|
||||
queryKey: gitCurrentRevisionsQuery(projectId).queryKey,
|
||||
queryFn: gitCurrentRevisionsQuery(projectId).queryFn,
|
||||
staleTime: 30000, // 30 seconds
|
||||
});
|
||||
};
|
||||
|
||||
@@ -132,36 +132,20 @@ export const sessionProcessesQuery = {
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const gitBranchesQuery = (projectId: string) =>
|
||||
export const gitCurrentRevisionsQuery = (projectId: string) =>
|
||||
({
|
||||
queryKey: ["git", "branches", projectId],
|
||||
queryKey: ["git", "current-revisions", projectId],
|
||||
queryFn: async () => {
|
||||
const response = await honoClient.api.projects[
|
||||
":projectId"
|
||||
].git.branches.$get({
|
||||
const response = await honoClient.api.projects[":projectId"].git[
|
||||
"current-revisions"
|
||||
].$get({
|
||||
param: { projectId },
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch branches: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
},
|
||||
}) as const;
|
||||
|
||||
export const gitCommitsQuery = (projectId: string) =>
|
||||
({
|
||||
queryKey: ["git", "commits", projectId],
|
||||
queryFn: async () => {
|
||||
const response = await honoClient.api.projects[
|
||||
":projectId"
|
||||
].git.commits.$get({
|
||||
param: { projectId },
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch commits: ${response.statusText}`);
|
||||
throw new Error(
|
||||
`Failed to fetch current revisions: ${response.statusText}`,
|
||||
);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
|
||||
@@ -10,71 +10,6 @@ const LayerImpl = Effect.gen(function* () {
|
||||
const gitService = yield* GitService;
|
||||
const projectRepository = yield* ProjectRepository;
|
||||
|
||||
const getGitBranches = (options: { projectId: string }) =>
|
||||
Effect.gen(function* () {
|
||||
const { projectId } = options;
|
||||
|
||||
const { project } = yield* projectRepository.getProject(projectId);
|
||||
|
||||
if (project.meta.projectPath === null) {
|
||||
return {
|
||||
response: { error: "Project path not found" },
|
||||
status: 400,
|
||||
} as const satisfies ControllerResponse;
|
||||
}
|
||||
|
||||
const projectPath = project.meta.projectPath;
|
||||
const branches = yield* Effect.either(
|
||||
gitService.getBranches(projectPath),
|
||||
);
|
||||
|
||||
if (Either.isLeft(branches)) {
|
||||
return {
|
||||
response: {
|
||||
success: false,
|
||||
},
|
||||
status: 200,
|
||||
} as const satisfies ControllerResponse;
|
||||
}
|
||||
|
||||
return {
|
||||
response: branches.right,
|
||||
status: 200,
|
||||
} as const satisfies ControllerResponse;
|
||||
});
|
||||
|
||||
const getGitCommits = (options: { projectId: string }) =>
|
||||
Effect.gen(function* () {
|
||||
const { projectId } = options;
|
||||
|
||||
const { project } = yield* projectRepository.getProject(projectId);
|
||||
|
||||
if (project.meta.projectPath === null) {
|
||||
return {
|
||||
response: { error: "Project path not found" },
|
||||
status: 400,
|
||||
} as const satisfies ControllerResponse;
|
||||
}
|
||||
|
||||
const projectPath = project.meta.projectPath;
|
||||
|
||||
const commits = yield* Effect.either(gitService.getCommits(projectPath));
|
||||
|
||||
if (Either.isLeft(commits)) {
|
||||
return {
|
||||
response: {
|
||||
success: false,
|
||||
},
|
||||
status: 200,
|
||||
} as const satisfies ControllerResponse;
|
||||
}
|
||||
|
||||
return {
|
||||
response: commits.right,
|
||||
status: 200,
|
||||
} as const satisfies ControllerResponse;
|
||||
});
|
||||
|
||||
const getGitDiff = (options: {
|
||||
projectId: string;
|
||||
fromRef: string;
|
||||
@@ -334,13 +269,115 @@ const LayerImpl = Effect.gen(function* () {
|
||||
} as const satisfies ControllerResponse;
|
||||
});
|
||||
|
||||
const getCurrentRevisions = (options: { projectId: string }) =>
|
||||
Effect.gen(function* () {
|
||||
const { projectId } = options;
|
||||
|
||||
const { project } = yield* projectRepository.getProject(projectId);
|
||||
|
||||
if (project.meta.projectPath === null) {
|
||||
return {
|
||||
response: { error: "Project path not found" },
|
||||
status: 400,
|
||||
} as const satisfies ControllerResponse;
|
||||
}
|
||||
|
||||
const projectPath = project.meta.projectPath;
|
||||
|
||||
// Get current branch
|
||||
const currentBranchResult = yield* Effect.either(
|
||||
gitService.getCurrentBranch(projectPath),
|
||||
);
|
||||
|
||||
if (Either.isLeft(currentBranchResult)) {
|
||||
return {
|
||||
response: {
|
||||
success: false,
|
||||
},
|
||||
status: 200,
|
||||
} as const satisfies ControllerResponse;
|
||||
}
|
||||
|
||||
const currentBranch = currentBranchResult.right;
|
||||
|
||||
// Find base branch
|
||||
const baseBranchResult = yield* Effect.either(
|
||||
gitService.findBaseBranch(projectPath, currentBranch),
|
||||
);
|
||||
|
||||
// Get all branches to extract branch details
|
||||
const allBranchesResult = yield* Effect.either(
|
||||
gitService.getBranches(projectPath),
|
||||
);
|
||||
|
||||
if (Either.isLeft(allBranchesResult)) {
|
||||
return {
|
||||
response: {
|
||||
success: false,
|
||||
},
|
||||
status: 200,
|
||||
} as const satisfies ControllerResponse;
|
||||
}
|
||||
|
||||
const allBranches = allBranchesResult.right.data;
|
||||
|
||||
// Find current branch details
|
||||
const currentBranchDetails = allBranches.find(
|
||||
(branch) => branch.name === currentBranch,
|
||||
);
|
||||
|
||||
// Find base branch details if exists
|
||||
let baseBranchDetails: (typeof allBranches)[number] | undefined;
|
||||
if (Either.isRight(baseBranchResult) && baseBranchResult.right !== null) {
|
||||
const baseBranchName = baseBranchResult.right.branch;
|
||||
baseBranchDetails = allBranches.find(
|
||||
(branch) => branch.name === baseBranchName,
|
||||
);
|
||||
}
|
||||
|
||||
// Get commits if base branch exists
|
||||
let commits: Array<{
|
||||
sha: string;
|
||||
message: string;
|
||||
author: string;
|
||||
date: string;
|
||||
}> = [];
|
||||
|
||||
if (Either.isRight(baseBranchResult) && baseBranchResult.right !== null) {
|
||||
const baseBranchHash = baseBranchResult.right.hash;
|
||||
const commitsResult = yield* Effect.either(
|
||||
gitService.getCommitsBetweenBranches(
|
||||
projectPath,
|
||||
baseBranchHash,
|
||||
"HEAD",
|
||||
),
|
||||
);
|
||||
|
||||
if (Either.isRight(commitsResult)) {
|
||||
commits = commitsResult.right.data;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
response: {
|
||||
success: true,
|
||||
data: {
|
||||
baseBranch: baseBranchDetails ?? null,
|
||||
currentBranch: currentBranchDetails ?? null,
|
||||
head: currentBranchDetails?.commit ?? null,
|
||||
commits,
|
||||
},
|
||||
},
|
||||
status: 200,
|
||||
} as const satisfies ControllerResponse;
|
||||
});
|
||||
|
||||
return {
|
||||
getGitBranches,
|
||||
getGitCommits,
|
||||
getGitDiff,
|
||||
commitFiles,
|
||||
pushCommits,
|
||||
commitAndPush,
|
||||
getCurrentRevisions,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -69,3 +69,41 @@ describe("GitService.push", () => {
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("GitService.findBaseBranch", () => {
|
||||
test("should return null when no base branch is found", async () => {
|
||||
const gitService = await Effect.runPromise(
|
||||
GitService.pipe(Effect.provide(testLayer)),
|
||||
);
|
||||
|
||||
const result = await Effect.runPromise(
|
||||
Effect.either(
|
||||
gitService.findBaseBranch("/tmp/nonexistent", "feature-branch"),
|
||||
).pipe(Effect.provide(NodeContext.layer)),
|
||||
);
|
||||
|
||||
// Should fail due to missing repo
|
||||
expect(Either.isLeft(result)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("GitService.getCommitsBetweenBranches", () => {
|
||||
test("should fail with missing repo", async () => {
|
||||
const gitService = await Effect.runPromise(
|
||||
GitService.pipe(Effect.provide(testLayer)),
|
||||
);
|
||||
|
||||
const result = await Effect.runPromise(
|
||||
Effect.either(
|
||||
gitService.getCommitsBetweenBranches(
|
||||
"/tmp/nonexistent",
|
||||
"base-branch",
|
||||
"HEAD",
|
||||
),
|
||||
).pipe(Effect.provide(NodeContext.layer)),
|
||||
);
|
||||
|
||||
// Should fail due to missing repo
|
||||
expect(Either.isLeft(result)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -226,6 +226,168 @@ const LayerImpl = Effect.gen(function* () {
|
||||
return { branch, output: "success" };
|
||||
});
|
||||
|
||||
const getBranchHash = (cwd: string, branchName: string) =>
|
||||
Effect.gen(function* () {
|
||||
const result = yield* execGitCommand(["rev-parse", branchName], cwd).pipe(
|
||||
Effect.map((output) => output.trim().split("\n")[0] ?? null),
|
||||
);
|
||||
return result;
|
||||
});
|
||||
|
||||
const getBranchNamesByCommitHash = (cwd: string, hash: string) =>
|
||||
Effect.gen(function* () {
|
||||
const result = yield* execGitCommand(
|
||||
["branch", "--contains", hash, "--format=%(refname:short)"],
|
||||
cwd,
|
||||
);
|
||||
return result
|
||||
.split("\n")
|
||||
.map((line) => line.trim())
|
||||
.filter((line) => line !== "");
|
||||
});
|
||||
|
||||
const compareCommitHash = (
|
||||
cwd: string,
|
||||
targetHash: string,
|
||||
compareHash: string,
|
||||
) =>
|
||||
Effect.gen(function* () {
|
||||
const aheadResult = yield* execGitCommand(
|
||||
["rev-list", `${targetHash}..${compareHash}`],
|
||||
cwd,
|
||||
);
|
||||
const aheadCounts = aheadResult
|
||||
.split("\n")
|
||||
.map((line) => line.trim())
|
||||
.filter((line) => line !== "").length;
|
||||
|
||||
const behindResult = yield* execGitCommand(
|
||||
["rev-list", `${compareHash}..${targetHash}`],
|
||||
cwd,
|
||||
);
|
||||
const behindCounts = behindResult
|
||||
.split("\n")
|
||||
.map((line) => line.trim())
|
||||
.filter((line) => line !== "").length;
|
||||
|
||||
if (aheadCounts === 0 && behindCounts === 0) {
|
||||
return "un-related" as const;
|
||||
}
|
||||
|
||||
if (aheadCounts > 0) {
|
||||
return "ahead" as const;
|
||||
}
|
||||
|
||||
if (behindCounts > 0) {
|
||||
return "behind" as const;
|
||||
}
|
||||
|
||||
return "un-related" as const;
|
||||
});
|
||||
|
||||
const getCommitsWithParent = (
|
||||
cwd: string,
|
||||
options: { offset: number; limit: number },
|
||||
) =>
|
||||
Effect.gen(function* () {
|
||||
const { offset, limit } = options;
|
||||
const result = yield* execGitCommand(
|
||||
[
|
||||
"log",
|
||||
"-n",
|
||||
String(limit),
|
||||
"--skip",
|
||||
String(offset),
|
||||
"--graph",
|
||||
"--pretty=format:%h %p",
|
||||
],
|
||||
cwd,
|
||||
);
|
||||
|
||||
const lines = result
|
||||
.split("\n")
|
||||
.map((line) => line.trim())
|
||||
.filter((line) => line !== "");
|
||||
|
||||
const commits: Array<{ current: string; parent: string }> = [];
|
||||
|
||||
for (const line of lines) {
|
||||
const match = /^\* (?<current>.+) (?<parent>.+)$/.exec(line);
|
||||
if (match?.groups?.current && match.groups.parent) {
|
||||
commits.push({
|
||||
current: match.groups.current,
|
||||
parent: match.groups.parent,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return commits;
|
||||
});
|
||||
|
||||
const findBaseBranch = (cwd: string, targetBranch: string) =>
|
||||
Effect.gen(function* () {
|
||||
let offset = 0;
|
||||
const limit = 20;
|
||||
|
||||
while (offset < 100) {
|
||||
const commits = yield* getCommitsWithParent(cwd, { offset, limit });
|
||||
|
||||
for (const commit of commits) {
|
||||
const branchNames = yield* getBranchNamesByCommitHash(
|
||||
cwd,
|
||||
commit.current,
|
||||
);
|
||||
|
||||
if (!branchNames.includes(targetBranch)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const otherBranchNames = branchNames.filter(
|
||||
(branchName) => branchName !== targetBranch,
|
||||
);
|
||||
|
||||
if (otherBranchNames.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const branchName of otherBranchNames) {
|
||||
const comparison = yield* compareCommitHash(
|
||||
cwd,
|
||||
targetBranch,
|
||||
branchName,
|
||||
);
|
||||
|
||||
if (comparison === "behind") {
|
||||
return { branch: branchName, hash: commit.current };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
offset += limit;
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
const getCommitsBetweenBranches = (
|
||||
cwd: string,
|
||||
baseBranch: string,
|
||||
targetBranch: string,
|
||||
) =>
|
||||
Effect.gen(function* () {
|
||||
const result = yield* execGitCommand(
|
||||
[
|
||||
"log",
|
||||
`${baseBranch}..${targetBranch}`,
|
||||
"--format=%H|%s|%an|%ad",
|
||||
"--date=iso",
|
||||
],
|
||||
cwd,
|
||||
);
|
||||
|
||||
return parseGitCommitsOutput(result);
|
||||
});
|
||||
|
||||
return {
|
||||
getBranches,
|
||||
getCurrentBranch,
|
||||
@@ -234,6 +396,12 @@ const LayerImpl = Effect.gen(function* () {
|
||||
stageFiles,
|
||||
commit,
|
||||
push,
|
||||
getBranchHash,
|
||||
getBranchNamesByCommitHash,
|
||||
compareCommitHash,
|
||||
getCommitsWithParent,
|
||||
findBaseBranch,
|
||||
getCommitsBetweenBranches,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -196,23 +196,11 @@ export const routes = (app: HonoAppType) =>
|
||||
* GitController Routes
|
||||
*/
|
||||
|
||||
.get("/api/projects/:projectId/git/branches", async (c) => {
|
||||
.get("/api/projects/:projectId/git/current-revisions", async (c) => {
|
||||
const response = await effectToResponse(
|
||||
c,
|
||||
gitController
|
||||
.getGitBranches({
|
||||
...c.req.param(),
|
||||
})
|
||||
.pipe(Effect.provide(runtime)),
|
||||
);
|
||||
return response;
|
||||
})
|
||||
|
||||
.get("/api/projects/:projectId/git/commits", async (c) => {
|
||||
const response = await effectToResponse(
|
||||
c,
|
||||
gitController
|
||||
.getGitCommits({
|
||||
.getCurrentRevisions({
|
||||
...c.req.param(),
|
||||
})
|
||||
.pipe(Effect.provide(runtime)),
|
||||
|
||||
Reference in New Issue
Block a user