|
14 | 14 | import java.net.URLEncoder; |
15 | 15 | import java.nio.charset.StandardCharsets; |
16 | 16 | import java.nio.file.Path; |
| 17 | +import java.time.OffsetDateTime; |
| 18 | +import java.time.format.DateTimeFormatter; |
17 | 19 | import java.util.ArrayList; |
18 | 20 | import java.util.List; |
19 | 21 |
|
@@ -498,6 +500,78 @@ public String getLatestCommitHash(String workspaceId, String repoIdOrSlug, Strin |
498 | 500 | } |
499 | 501 | } |
500 | 502 |
|
| 503 | + @Override |
| 504 | + public List<VcsCommit> getCommitHistory(String workspaceId, String repoIdOrSlug, String branchOrCommit, int limit) throws IOException { |
| 505 | + String projectPath = workspaceId + "/" + repoIdOrSlug; |
| 506 | + String encodedPath = URLEncoder.encode(projectPath, StandardCharsets.UTF_8); |
| 507 | + String encodedRef = URLEncoder.encode(branchOrCommit, StandardCharsets.UTF_8); |
| 508 | + int perPage = Math.min(limit, 100); // GitLab max per_page is 100 |
| 509 | + |
| 510 | + String url = baseUrl + "/projects/" + encodedPath + "/repository/commits?ref_name=" + encodedRef + "&per_page=" + perPage; |
| 511 | + |
| 512 | + List<VcsCommit> commits = new ArrayList<>(); |
| 513 | + |
| 514 | + while (url != null && commits.size() < limit) { |
| 515 | + Request request = createGetRequest(url); |
| 516 | + try (Response response = httpClient.newCall(request).execute()) { |
| 517 | + if (!response.isSuccessful()) { |
| 518 | + throw createException("get commit history", response); |
| 519 | + } |
| 520 | + |
| 521 | + JsonNode root = objectMapper.readTree(response.body().string()); |
| 522 | + if (root == null || !root.isArray()) break; |
| 523 | + |
| 524 | + for (JsonNode commitNode : root) { |
| 525 | + if (commits.size() >= limit) break; |
| 526 | + |
| 527 | + String hash = getTextOrNull(commitNode, "id"); |
| 528 | + if (hash == null) continue; |
| 529 | + |
| 530 | + String message = getTextOrNull(commitNode, "message"); |
| 531 | + String authorName = getTextOrNull(commitNode, "author_name"); |
| 532 | + String authorEmail = getTextOrNull(commitNode, "author_email"); |
| 533 | + OffsetDateTime timestamp = null; |
| 534 | + |
| 535 | + String dateStr = getTextOrNull(commitNode, "authored_date"); |
| 536 | + if (dateStr != null) { |
| 537 | + try { |
| 538 | + timestamp = OffsetDateTime.parse(dateStr, DateTimeFormatter.ISO_OFFSET_DATE_TIME); |
| 539 | + } catch (Exception e) { |
| 540 | + log.debug("Could not parse commit date '{}': {}", dateStr, e.getMessage()); |
| 541 | + } |
| 542 | + } |
| 543 | + |
| 544 | + List<String> parentHashes = new ArrayList<>(); |
| 545 | + JsonNode parentsArray = commitNode.get("parent_ids"); |
| 546 | + if (parentsArray != null && parentsArray.isArray()) { |
| 547 | + for (JsonNode parentId : parentsArray) { |
| 548 | + if (!parentId.isNull()) { |
| 549 | + parentHashes.add(parentId.asText()); |
| 550 | + } |
| 551 | + } |
| 552 | + } |
| 553 | + |
| 554 | + commits.add(new VcsCommit(hash, message, authorName, authorEmail, timestamp, parentHashes)); |
| 555 | + } |
| 556 | + |
| 557 | + // Follow pagination via Link header or X-Next-Page |
| 558 | + if (commits.size() < limit) { |
| 559 | + String nextPage = response.header("X-Next-Page"); |
| 560 | + if (nextPage != null && !nextPage.isEmpty()) { |
| 561 | + url = baseUrl + "/projects/" + encodedPath + "/repository/commits?ref_name=" + encodedRef |
| 562 | + + "&per_page=" + perPage + "&page=" + nextPage; |
| 563 | + } else { |
| 564 | + url = null; |
| 565 | + } |
| 566 | + } else { |
| 567 | + url = null; |
| 568 | + } |
| 569 | + } |
| 570 | + } |
| 571 | + |
| 572 | + return commits; |
| 573 | + } |
| 574 | + |
501 | 575 | @Override |
502 | 576 | public String getBranchDiff(String workspaceId, String repoIdOrSlug, String baseBranch, String compareBranch) throws IOException { |
503 | 577 | // GitLab: GET /projects/:id/repository/compare |
|
0 commit comments