You've already forked agentic-coding-workflow
功能补齐、查看git提交历史
This commit is contained in:
@@ -0,0 +1,61 @@
|
|||||||
|
package git
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Commit 表示一条 git log 记录。
|
||||||
|
type Commit struct {
|
||||||
|
Hash string
|
||||||
|
Author string
|
||||||
|
Email string
|
||||||
|
Date time.Time
|
||||||
|
Subject string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log 返回 dir 的最近 n 条提交历史。n <= 0 时默认 50 条。
|
||||||
|
func (r *DefaultRunner) Log(ctx context.Context, dir string, n int) ([]Commit, error) {
|
||||||
|
ctx, cancel := withCtx(ctx, r.cfg.OpsTimeout)
|
||||||
|
defer cancel()
|
||||||
|
if n <= 0 {
|
||||||
|
n = 50
|
||||||
|
}
|
||||||
|
out, _, err := r.exec(ctx, dir, nil, "log", "--format=%H%x00%an%x00%ae%x00%aI%x00%s%x00", "-n", strconv.Itoa(n))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return parseLog(out), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseLog(buf []byte) []Commit {
|
||||||
|
if len(buf) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
parts := strings.Split(string(buf), "\x00")
|
||||||
|
var commits []Commit
|
||||||
|
// parts 以空字符串结尾(因为 format 最后有 %x00),所以每 5 个字段为一组。
|
||||||
|
for i := 0; i+4 < len(parts); i += 5 {
|
||||||
|
hash := parts[i]
|
||||||
|
if hash == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
author := parts[i+1]
|
||||||
|
email := parts[i+2]
|
||||||
|
dateStr := parts[i+3]
|
||||||
|
subject := parts[i+4]
|
||||||
|
|
||||||
|
date, _ := time.Parse(time.RFC3339, dateStr)
|
||||||
|
|
||||||
|
commits = append(commits, Commit{
|
||||||
|
Hash: hash,
|
||||||
|
Author: author,
|
||||||
|
Email: email,
|
||||||
|
Date: date,
|
||||||
|
Subject: subject,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return commits
|
||||||
|
}
|
||||||
@@ -33,6 +33,7 @@ type Runner interface {
|
|||||||
WorktreeList(ctx context.Context, dir string) ([]Worktree, error)
|
WorktreeList(ctx context.Context, dir string) ([]Worktree, error)
|
||||||
ListRemoteBranches(ctx context.Context, dir string, cred *Credential) ([]string, error)
|
ListRemoteBranches(ctx context.Context, dir string, cred *Credential) ([]string, error)
|
||||||
Checkout(ctx context.Context, dir, branch string) error
|
Checkout(ctx context.Context, dir, branch string) error
|
||||||
|
Log(ctx context.Context, dir string, n int) ([]Commit, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileStatus 表示 `git status --porcelain=v1` 的一行。
|
// FileStatus 表示 `git status --porcelain=v1` 的一行。
|
||||||
|
|||||||
@@ -105,6 +105,14 @@ func (f *fakeGitOpsSvc) PushOnWorktree(_ context.Context, _ workspace.Caller, wt
|
|||||||
f.lastTarget = "worktree:" + wtID.String()
|
f.lastTarget = "worktree:" + wtID.String()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
func (f *fakeGitOpsSvc) LogOnMain(_ context.Context, _ workspace.Caller, wsID uuid.UUID, _ int) ([]git.Commit, error) {
|
||||||
|
f.lastTarget = "main:" + wsID.String()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
func (f *fakeGitOpsSvc) LogOnWorktree(_ context.Context, _ workspace.Caller, wtID uuid.UUID, _ int) ([]git.Commit, error) {
|
||||||
|
f.lastTarget = "worktree:" + wtID.String()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// wsTestDeps 在 testDeps 基础上挂一个已存在的 workspace + worktree 服务。
|
// wsTestDeps 在 testDeps 基础上挂一个已存在的 workspace + worktree 服务。
|
||||||
func wsTestDeps() (ServerDeps, *fakeWorkspaceSvc, *fakeWorktreeSvc, *fakeGitOpsSvc) {
|
func wsTestDeps() (ServerDeps, *fakeWorkspaceSvc, *fakeWorktreeSvc, *fakeGitOpsSvc) {
|
||||||
|
|||||||
@@ -167,10 +167,12 @@ type GitOpsService interface {
|
|||||||
StatusOnMain(ctx context.Context, c Caller, wsID uuid.UUID) ([]git.FileStatus, error)
|
StatusOnMain(ctx context.Context, c Caller, wsID uuid.UUID) ([]git.FileStatus, error)
|
||||||
CommitOnMain(ctx context.Context, c Caller, wsID uuid.UUID, in CommitInput) (string, error)
|
CommitOnMain(ctx context.Context, c Caller, wsID uuid.UUID, in CommitInput) (string, error)
|
||||||
PushOnMain(ctx context.Context, c Caller, wsID uuid.UUID) error
|
PushOnMain(ctx context.Context, c Caller, wsID uuid.UUID) error
|
||||||
|
LogOnMain(ctx context.Context, c Caller, wsID uuid.UUID, n int) ([]git.Commit, error)
|
||||||
|
|
||||||
StatusOnWorktree(ctx context.Context, c Caller, wtID uuid.UUID) ([]git.FileStatus, error)
|
StatusOnWorktree(ctx context.Context, c Caller, wtID uuid.UUID) ([]git.FileStatus, error)
|
||||||
CommitOnWorktree(ctx context.Context, c Caller, wtID uuid.UUID, in CommitInput) (string, error)
|
CommitOnWorktree(ctx context.Context, c Caller, wtID uuid.UUID, in CommitInput) (string, error)
|
||||||
PushOnWorktree(ctx context.Context, c Caller, wtID uuid.UUID) error
|
PushOnWorktree(ctx context.Context, c Caller, wtID uuid.UUID) error
|
||||||
|
LogOnWorktree(ctx context.Context, c Caller, wtID uuid.UUID, n int) ([]git.Commit, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Input DTO =====
|
// ===== Input DTO =====
|
||||||
|
|||||||
@@ -119,3 +119,15 @@ type commitResp struct {
|
|||||||
type branchListResp struct {
|
type branchListResp struct {
|
||||||
Branches []string `json:"branches"`
|
Branches []string `json:"branches"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type commitLogResp struct {
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
Author string `json:"author"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Date time.Time `json:"date"`
|
||||||
|
Subject string `json:"subject"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func toCommitLogResp(c git.Commit) commitLogResp {
|
||||||
|
return commitLogResp{Hash: c.Hash, Author: c.Author, Email: c.Email, Date: c.Date, Subject: c.Subject}
|
||||||
|
}
|
||||||
|
|||||||
@@ -79,6 +79,18 @@ func (s *gitOpsService) PushOnMain(ctx context.Context, c Caller, wsID uuid.UUID
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *gitOpsService) LogOnMain(ctx context.Context, c Caller, wsID uuid.UUID, n int) ([]git.Commit, error) {
|
||||||
|
ws, err := s.guardWorkspaceRead(ctx, c, wsID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
commits, err := s.gitr.Log(ctx, ws.MainPath, n)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errs.Wrap(err, errs.CodeGitCmdFailed, "git log")
|
||||||
|
}
|
||||||
|
return commits, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ===== Worktree =====
|
// ===== Worktree =====
|
||||||
|
|
||||||
func (s *gitOpsService) StatusOnWorktree(ctx context.Context, c Caller, wtID uuid.UUID) ([]git.FileStatus, error) {
|
func (s *gitOpsService) StatusOnWorktree(ctx context.Context, c Caller, wtID uuid.UUID) ([]git.FileStatus, error) {
|
||||||
@@ -133,6 +145,18 @@ func (s *gitOpsService) PushOnWorktree(ctx context.Context, c Caller, wtID uuid.
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *gitOpsService) LogOnWorktree(ctx context.Context, c Caller, wtID uuid.UUID, n int) ([]git.Commit, error) {
|
||||||
|
wt, _, err := s.guardWorktreeRead(ctx, c, wtID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
commits, err := s.gitr.Log(ctx, wt.Path, n)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errs.Wrap(err, errs.CodeGitCmdFailed, "git log")
|
||||||
|
}
|
||||||
|
return commits, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ===== guards =====
|
// ===== guards =====
|
||||||
|
|
||||||
func (s *gitOpsService) guardWorkspaceRead(ctx context.Context, c Caller, wsID uuid.UUID) (*Workspace, error) {
|
func (s *gitOpsService) guardWorkspaceRead(ctx context.Context, c Caller, wsID uuid.UUID) (*Workspace, error) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@@ -59,6 +60,7 @@ func (h *Handler) Mount(r chi.Router) {
|
|||||||
r.Get("/main/status", h.statusOnMain)
|
r.Get("/main/status", h.statusOnMain)
|
||||||
r.Post("/main/commit", h.commitOnMain)
|
r.Post("/main/commit", h.commitOnMain)
|
||||||
r.Post("/main/push", h.pushOnMain)
|
r.Post("/main/push", h.pushOnMain)
|
||||||
|
r.Get("/main/log", h.logOnMain)
|
||||||
|
|
||||||
r.Get("/worktrees", h.listWorktrees)
|
r.Get("/worktrees", h.listWorktrees)
|
||||||
r.Post("/worktrees", h.createWorktree)
|
r.Post("/worktrees", h.createWorktree)
|
||||||
@@ -71,6 +73,7 @@ func (h *Handler) Mount(r chi.Router) {
|
|||||||
r.Get("/status", h.statusOnWorktree)
|
r.Get("/status", h.statusOnWorktree)
|
||||||
r.Post("/commit", h.commitOnWorktree)
|
r.Post("/commit", h.commitOnWorktree)
|
||||||
r.Post("/push", h.pushOnWorktree)
|
r.Post("/push", h.pushOnWorktree)
|
||||||
|
r.Get("/log", h.logOnWorktree)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -536,3 +539,59 @@ func (h *Handler) pushGeneric(w http.ResponseWriter, r *http.Request, onMain boo
|
|||||||
}
|
}
|
||||||
w.WriteHeader(http.StatusNoContent)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handler) logOnMain(w http.ResponseWriter, r *http.Request) {
|
||||||
|
h.logGeneric(w, r, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) logOnWorktree(w http.ResponseWriter, r *http.Request) {
|
||||||
|
h.logGeneric(w, r, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) logGeneric(w http.ResponseWriter, r *http.Request, onMain bool) {
|
||||||
|
c, err := h.caller(r)
|
||||||
|
if err != nil {
|
||||||
|
writeErr(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
param := "wsID"
|
||||||
|
if !onMain {
|
||||||
|
param = "wtID"
|
||||||
|
}
|
||||||
|
id, err := parseUUID(chi.URLParam(r, param))
|
||||||
|
if err != nil {
|
||||||
|
writeErr(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n := parseLogN(r)
|
||||||
|
var commits []git.Commit
|
||||||
|
if onMain {
|
||||||
|
commits, err = h.gop.LogOnMain(r.Context(), c, id, n)
|
||||||
|
} else {
|
||||||
|
commits, err = h.gop.LogOnWorktree(r.Context(), c, id, n)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
writeErr(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp := make([]commitLogResp, 0, len(commits))
|
||||||
|
for _, cc := range commits {
|
||||||
|
resp = append(resp, toCommitLogResp(cc))
|
||||||
|
}
|
||||||
|
writeJSON(w, http.StatusOK, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseLogN(r *http.Request) int {
|
||||||
|
nStr := r.URL.Query().Get("n")
|
||||||
|
if nStr == "" {
|
||||||
|
return 50
|
||||||
|
}
|
||||||
|
n, err := strconv.Atoi(nStr)
|
||||||
|
if err != nil || n <= 0 {
|
||||||
|
return 50
|
||||||
|
}
|
||||||
|
if n > 200 {
|
||||||
|
return 200
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|||||||
@@ -226,11 +226,14 @@ func (f *fakeGit) WorktreeRemove(_ context.Context, _, _ string) error
|
|||||||
func (f *fakeGit) WorktreeList(_ context.Context, _ string) ([]git.Worktree, error) {
|
func (f *fakeGit) WorktreeList(_ context.Context, _ string) ([]git.Worktree, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
func (f *fakeGit) FetchRemoteBranch(_ context.Context, _, _ string, _ *git.Credential) error { return nil }
|
func (f *fakeGit) FetchRemoteBranch(_ context.Context, _, _ string, _ *git.Credential) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
func (f *fakeGit) Checkout(_ context.Context, _, _ string) error { return nil }
|
func (f *fakeGit) Checkout(_ context.Context, _, _ string) error { return nil }
|
||||||
func (f *fakeGit) ListRemoteBranches(_ context.Context, _ string, _ *git.Credential) ([]string, error) {
|
func (f *fakeGit) ListRemoteBranches(_ context.Context, _ string, _ *git.Credential) ([]string, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
func (f *fakeGit) Log(_ context.Context, _ string, _ int) ([]git.Commit, error) { return nil, nil }
|
||||||
|
|
||||||
// trackGit 包装 git.Runner,记录 FetchRemoteBranch / Checkout 调用以供断言。
|
// trackGit 包装 git.Runner,记录 FetchRemoteBranch / Checkout 调用以供断言。
|
||||||
// 嵌入 Runner 接口本身可避免为每个方法重复转发。
|
// 嵌入 Runner 接口本身可避免为每个方法重复转发。
|
||||||
|
|||||||
@@ -94,3 +94,11 @@ export interface CommitBody {
|
|||||||
export interface CommitResp {
|
export interface CommitResp {
|
||||||
sha: string
|
sha: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CommitLog {
|
||||||
|
hash: string
|
||||||
|
author: string
|
||||||
|
email: string
|
||||||
|
date: string
|
||||||
|
subject: string
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import type {
|
|||||||
CreateWorktreeBody,
|
CreateWorktreeBody,
|
||||||
CommitBody,
|
CommitBody,
|
||||||
CommitResp,
|
CommitResp,
|
||||||
|
CommitLog,
|
||||||
} from './types'
|
} from './types'
|
||||||
|
|
||||||
const enc = (v: string) => encodeURIComponent(v)
|
const enc = (v: string) => encodeURIComponent(v)
|
||||||
@@ -63,10 +64,14 @@ export const workspacesApi = {
|
|||||||
request<CommitResp>(`/api/v1/workspaces/${enc(wsID)}/main/commit`, { method: 'POST', body }),
|
request<CommitResp>(`/api/v1/workspaces/${enc(wsID)}/main/commit`, { method: 'POST', body }),
|
||||||
pushOnMain: (wsID: string) =>
|
pushOnMain: (wsID: string) =>
|
||||||
request<void>(`/api/v1/workspaces/${enc(wsID)}/main/push`, { method: 'POST' }),
|
request<void>(`/api/v1/workspaces/${enc(wsID)}/main/push`, { method: 'POST' }),
|
||||||
|
logOnMain: (wsID: string, n = 50) =>
|
||||||
|
request<CommitLog[]>(`/api/v1/workspaces/${enc(wsID)}/main/log?n=${n}`),
|
||||||
statusOnWorktree: (wtID: string) =>
|
statusOnWorktree: (wtID: string) =>
|
||||||
request<FileStatus[]>(`/api/v1/worktrees/${enc(wtID)}/status`),
|
request<FileStatus[]>(`/api/v1/worktrees/${enc(wtID)}/status`),
|
||||||
commitOnWorktree: (wtID: string, body: CommitBody) =>
|
commitOnWorktree: (wtID: string, body: CommitBody) =>
|
||||||
request<CommitResp>(`/api/v1/worktrees/${enc(wtID)}/commit`, { method: 'POST', body }),
|
request<CommitResp>(`/api/v1/worktrees/${enc(wtID)}/commit`, { method: 'POST', body }),
|
||||||
pushOnWorktree: (wtID: string) =>
|
pushOnWorktree: (wtID: string) =>
|
||||||
request<void>(`/api/v1/worktrees/${enc(wtID)}/push`, { method: 'POST' }),
|
request<void>(`/api/v1/worktrees/${enc(wtID)}/push`, { method: 'POST' }),
|
||||||
|
logOnWorktree: (wtID: string, n = 50) =>
|
||||||
|
request<CommitLog[]>(`/api/v1/worktrees/${enc(wtID)}/log?n=${n}`),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,15 @@
|
|||||||
:ws="store.current"
|
:ws="store.current"
|
||||||
/>
|
/>
|
||||||
</NTabPane>
|
</NTabPane>
|
||||||
|
<NTabPane
|
||||||
|
name="commits"
|
||||||
|
tab="Commits"
|
||||||
|
>
|
||||||
|
<CommitHistoryTab
|
||||||
|
v-if="store.current"
|
||||||
|
:ws="store.current"
|
||||||
|
/>
|
||||||
|
</NTabPane>
|
||||||
<NTabPane
|
<NTabPane
|
||||||
name="runs"
|
name="runs"
|
||||||
tab="Run"
|
tab="Run"
|
||||||
@@ -66,13 +75,14 @@ import { useWorkspacesStore } from '@/stores/workspaces'
|
|||||||
import SyncBar from './components/SyncBar.vue'
|
import SyncBar from './components/SyncBar.vue'
|
||||||
import WorktreesTab from './components/WorktreesTab.vue'
|
import WorktreesTab from './components/WorktreesTab.vue'
|
||||||
import MainTab from './components/MainTab.vue'
|
import MainTab from './components/MainTab.vue'
|
||||||
|
import CommitHistoryTab from './components/CommitHistoryTab.vue'
|
||||||
import SettingsTab from './components/SettingsTab.vue'
|
import SettingsTab from './components/SettingsTab.vue'
|
||||||
import RunsTab from './components/RunsTab.vue'
|
import RunsTab from './components/RunsTab.vue'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const store = useWorkspacesStore()
|
const store = useWorkspacesStore()
|
||||||
const msg = useMessage()
|
const msg = useMessage()
|
||||||
const tab = ref<'worktrees' | 'main' | 'settings' | 'runs'>('worktrees')
|
const tab = ref<'worktrees' | 'main' | 'commits' | 'settings' | 'runs'>('worktrees')
|
||||||
|
|
||||||
const projectSlug = computed(() => route.params.slug as string)
|
const projectSlug = computed(() => route.params.slug as string)
|
||||||
const wsSlug = computed(() => route.params.wsSlug as string)
|
const wsSlug = computed(() => route.params.wsSlug as string)
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
<template>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<!-- 目标选择:main 或某个 worktree -->
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<span class="text-sm text-gray-500">查看目标</span>
|
||||||
|
<NSelect
|
||||||
|
v-model:value="target"
|
||||||
|
:options="targetOptions"
|
||||||
|
size="small"
|
||||||
|
class="w-64"
|
||||||
|
/>
|
||||||
|
<NButton
|
||||||
|
size="small"
|
||||||
|
:loading="loading"
|
||||||
|
@click="refresh"
|
||||||
|
>
|
||||||
|
刷新
|
||||||
|
</NButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<NDataTable
|
||||||
|
:columns="columns"
|
||||||
|
:data="commits"
|
||||||
|
:loading="loading"
|
||||||
|
size="small"
|
||||||
|
:scroll-x="800"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, h, onMounted, ref, watch } from 'vue'
|
||||||
|
import { NButton, NDataTable, NSelect, NTag, NTooltip } from 'naive-ui'
|
||||||
|
import type { DataTableColumns } from 'naive-ui'
|
||||||
|
|
||||||
|
import { workspacesApi } from '@/api/workspaces'
|
||||||
|
import { useWorkspacesStore } from '@/stores/workspaces'
|
||||||
|
import type { CommitLog, Workspace } from '@/api/types'
|
||||||
|
|
||||||
|
const props = defineProps<{ ws: Workspace }>()
|
||||||
|
const store = useWorkspacesStore()
|
||||||
|
|
||||||
|
const target = ref<string>('main')
|
||||||
|
const commits = ref<CommitLog[]>([])
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
const targetOptions = computed(() => {
|
||||||
|
const opts = [{ label: `Main (${props.ws.default_branch})`, value: 'main' }]
|
||||||
|
for (const wt of store.worktrees) {
|
||||||
|
opts.push({ label: wt.branch, value: wt.id })
|
||||||
|
}
|
||||||
|
return opts
|
||||||
|
})
|
||||||
|
|
||||||
|
function formatDate(d: string) {
|
||||||
|
return new Date(d).toLocaleString('zh-CN', { hour12: false })
|
||||||
|
}
|
||||||
|
|
||||||
|
function shortHash(hash: string) {
|
||||||
|
return hash.slice(0, 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns = computed<DataTableColumns<CommitLog>>(() => [
|
||||||
|
{
|
||||||
|
title: 'Commit',
|
||||||
|
key: 'hash',
|
||||||
|
width: 100,
|
||||||
|
fixed: 'left',
|
||||||
|
render(row: CommitLog) {
|
||||||
|
return h(
|
||||||
|
NTag,
|
||||||
|
{ size: 'tiny', type: 'default' },
|
||||||
|
{ default: () => shortHash(row.hash) },
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '提交信息',
|
||||||
|
key: 'subject',
|
||||||
|
ellipsis: { tooltip: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '作者',
|
||||||
|
key: 'author',
|
||||||
|
width: 140,
|
||||||
|
render(row: CommitLog) {
|
||||||
|
return h(
|
||||||
|
NTooltip,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
trigger: () => h('span', row.author),
|
||||||
|
default: () => row.email,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '时间',
|
||||||
|
key: 'date',
|
||||||
|
width: 160,
|
||||||
|
render(row: CommitLog) {
|
||||||
|
return h('span', { class: 'text-xs text-gray-500' }, formatDate(row.date))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
async function refresh() {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
if (target.value === 'main') {
|
||||||
|
commits.value = await workspacesApi.logOnMain(props.ws.id)
|
||||||
|
} else {
|
||||||
|
commits.value = await workspacesApi.logOnWorktree(target.value)
|
||||||
|
}
|
||||||
|
} catch (e: unknown) {
|
||||||
|
commits.value = []
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(target, refresh)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
refresh()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
Reference in New Issue
Block a user