~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/knotless

git that lives in your Atmosphere account

knotless.dev
1 collaborator

knotless 0.13.0 Latest 2 packages, 1 image

1
10

A merge commit's page shows what the merge brought in

Merged chadtmiller.com asked to merge into main from chadtmiller.com/knotless:serve/merge-commit-diff
crates/knotless-serve/src/web/repo.rs+4−2
@@ -695,7 +695,9 @@ pub struct Change {
files: Vec<FileDiffView>,
}
/// `dev.knotless.getCommit`.
/// `dev.knotless.getCommit`. A merge's diff is against its first parent:
/// what it brought into the branch it was made on, where git's own would
/// show only what differs from both, nothing at all for a clean merge.
pub async fn get_commit(
State(app): State<Arc<App>>,
Query(p): Query<RepoParams>,
@@ -707,7 +709,7 @@ pub async fn get_commit(
let c = shown(&app, &repo, log).await.remove(0);
let files = files(
&repo.git,
&["show", "--format=", "--patch"],
&["show", "--format=", "--patch", "--diff-merges=first-parent"],
&c.sha,
&[],
Rows::All,
crates/knotless-serve/tests/web.rs+36−0
@@ -882,6 +882,42 @@ fn browsing_a_repository() {
}
}
/// A merge commit's page shows what it brought in, against its first
/// parent, and not what its branch did meanwhile.
#[test]
fn a_merge_commit_shows_what_it_brought_in() {
let site = serve(true);
let s = person(account(), &site);
let url = s.url("merged");
let work = s.work("w", 0);
write(&work, "begun.txt", b"begun\n");
s.git(&work, &["add", "-A"]);
s.git(&work, &["commit", "-q", "-m", "Begin"]);
s.git(&work, &["checkout", "-q", "-b", "topic"]);
write(&work, "brought.txt", b"brought\n");
s.git(&work, &["add", "-A"]);
s.git(&work, &["commit", "-q", "-m", "Bring"]);
s.git(&work, &["checkout", "-q", "main"]);
write(&work, "meanwhile.txt", b"meanwhile\n");
s.git(&work, &["add", "-A"]);
s.git(&work, &["commit", "-q", "-m", "Meanwhile"]);
s.git(
&work,
&["merge", "-q", "--no-ff", "-m", "Merge topic", "topic"],
);
let merge = s.git(&work, &["rev-parse", "HEAD"]);
s.git(&work, &["push", "-q", &url, "main"]);
let uri = format!("at://{}/dev.knotless.repo/merged", s.account.did);
let commit = xrpc("getCommit", &[("repo", &uri), ("sha", &merge)]);
wait_until(|| site.status(&commit).0 == 200);
let change = site.json(&commit);
let files = change["files"].as_array().unwrap();
assert_eq!(files.len(), 1, "{change}");
assert_eq!(files[0]["new"], "brought.txt");
assert_eq!(files[0]["added"], 1);
}
/// Who last changed each line of a file, as the blame page shows it.
#[test]
fn blaming_a_file() {
docs/reference/api.md+1−1
@@ -571,7 +571,7 @@ name it has now.
| `dev.knotless.getFile?repo=&ref=&path=` | a file, highlighted or rendered |
| `dev.knotless.getBlame?repo=&ref=&path=` | who last changed each line of a text file, as `git blame` finds it: its text, highlighted; its lines in runs (`hunks`), each with its commit and, where the commit's parent had the file, `previous`, that parent and the file's path there; and each commit once. 400 for a binary or a file over 256 KiB |
| `dev.knotless.listCommits?repo=&ref=&path=&limit=&cursor=&subjectOnly=` | history, 50 commits a page by default, with the newest checks of each; with `subjectOnly`, each message only to its first line |
| `dev.knotless.getCommit?repo=&sha=` | a commit and its diff |
| `dev.knotless.getCommit?repo=&sha=` | a commit and its diff, a merge's against its first parent |
| `dev.knotless.listBranches?repo=&limit=&cursor=&commits=` | the branches, the default first, 100 a page by default; with `commits`, each with its tip and that commit's newest checks |
| `dev.knotless.listTags?repo=&limit=&cursor=&commits=` | the tags that name a commit, the newest commit first, 100 a page by default; with `commits`, each with its commit and that commit's newest checks |
| `dev.knotless.listItems?repo=&kind=&state=&q=&limit=&cursor=` | the open or closed pulls or issues that the filter `q` matches ([collaboration.md](../design/collaboration.md#filtering-pulls-and-issues)), `review-requested:@me` and `assignee:@me` the asker, each with `draft` for a draft pull, the `reviewers` asked, its base's required reviewers among them and named again in `requiredReviewers`, and the `assignees`, 50 a page by default, how many of each, and the repository's labels and the items' authors, for a filter to name |