(repo: Path, *, quest_id: str, limit: int = 80)
| 114 | |
| 115 | |
| 116 | def list_commit_canvas(repo: Path, *, quest_id: str, limit: int = 80) -> dict[str, Any]: |
| 117 | resolved_limit = max(1, min(int(limit), 200)) |
| 118 | head = head_commit(repo) |
| 119 | current_ref = current_branch(repo) |
| 120 | commits = _git_commit_canvas_log(repo, limit=resolved_limit) |
| 121 | nodes: list[dict[str, Any]] = [] |
| 122 | edges: list[dict[str, Any]] = [] |
| 123 | seen_shas = {item["sha"] for item in commits if str(item.get("sha") or "").strip()} |
| 124 | |
| 125 | for commit in commits: |
| 126 | sha = str(commit.get("sha") or "").strip() |
| 127 | if not sha: |
| 128 | continue |
| 129 | detail = commit_detail(repo, sha=sha) |
| 130 | parents = [str(item).strip() for item in (detail.get("parents") or []) if str(item).strip()] |
| 131 | files = detail.get("files") or [] |
| 132 | changed_paths = [ |
| 133 | str(item.get("path") or "").strip() |
| 134 | for item in files |
| 135 | if str(item.get("path") or "").strip() |
| 136 | ] |
| 137 | node = { |
| 138 | "sha": sha, |
| 139 | "short_sha": str(detail.get("short_sha") or commit.get("short_sha") or sha[:7]).strip(), |
| 140 | "parents": parents, |
| 141 | "subject": str(detail.get("subject") or commit.get("subject") or "").strip() or sha[:7], |
| 142 | "body_preview": _body_preview(str(detail.get("body") or "").strip()), |
| 143 | "authored_at": str(detail.get("authored_at") or commit.get("authored_at") or "").strip() or None, |
| 144 | "author_name": str(detail.get("author_name") or commit.get("author_name") or "").strip() or None, |
| 145 | "branch_refs": _normalize_branch_refs(commit.get("decorations")), |
| 146 | "current": bool(head and sha == head), |
| 147 | "active_workspace": bool(head and sha == head), |
| 148 | "changed_paths": changed_paths, |
| 149 | "file_count": int(detail.get("file_count") or len(files)), |
| 150 | "added": int(((detail.get("stats") or {}) if isinstance(detail.get("stats"), dict) else {}).get("added") or 0), |
| 151 | "removed": int(((detail.get("stats") or {}) if isinstance(detail.get("stats"), dict) else {}).get("removed") or 0), |
| 152 | "compare_base": parents[0] if parents else None, |
| 153 | "compare_head": sha, |
| 154 | "selection_type": "git_commit_node", |
| 155 | } |
| 156 | nodes.append(node) |
| 157 | for parent in parents: |
| 158 | if parent in seen_shas: |
| 159 | edges.append( |
| 160 | { |
| 161 | "from": parent, |
| 162 | "to": sha, |
| 163 | "relation": "parent", |
| 164 | } |
| 165 | ) |
| 166 | |
| 167 | return { |
| 168 | "quest_id": quest_id, |
| 169 | "workspace_mode": "copilot", |
| 170 | "head": head, |
| 171 | "current_ref": current_ref, |
| 172 | "nodes": nodes, |
| 173 | "edges": edges, |
no test coverage detected