This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

knot2/xrpc: hidden staging ref read

Lewis: May this revision serve well! <did:plc:3fwecdnvtcscjnrx2p4n7alz>

author did:plc:3fwecdnvtcscjnrx2p4n7a… date (Jul 28, 2026, 5:14 PM +0300) commit 2df65b74 parent eca1e96e change-id xvmwuoss
+92
+10
knot2/crates/knot-git/src/repo.rs
··· 566 566 } 567 567 } 568 568 569 + pub fn hidden_ref_commit(&self, spec: &str) -> Option<Oid> { 570 + let name = match spec.starts_with("refs/") { 571 + true => RefName::new(spec.to_string()), 572 + false => RefName::new(format!("refs/{spec}")), 573 + } 574 + .ok() 575 + .filter(is_hidden)?; 576 + self.find_ref(&name).ok().flatten() 577 + } 578 + 569 579 fn direct_target(&self, reference: &gix::Reference<'_>, depth: usize) -> Option<gix::ObjectId> { 570 580 match (depth, reference.follow()) { 571 581 (_, None) => reference.try_id().map(|id| id.detach()),
+11
knot2/crates/knot-xrpc/src/reads.rs
··· 137 137 } 138 138 .ok_or_else(ref_not_found)?; 139 139 let commit = repo.peel_to_commit(oid).map_err(|_| ref_not_found())?; 140 + if hidden_staging_commit(repo, refspec) == Some(commit) { 141 + return Ok(commit); 142 + } 140 143 match repo.reachable_from_public(commit) { 141 144 Ok(true) => Ok(commit), 142 145 Ok(false) => Err(ref_not_found()), 143 146 Err(error) => Err(error.into()), 144 147 } 148 + } 149 + 150 + fn hidden_staging_commit(repo: &Repo, refspec: &str) -> Option<Oid> { 151 + repo.hidden_ref_commit(refspec) 152 + .and_then(|oid| repo.peel_to_commit(oid).ok()) 145 153 } 146 154 147 155 struct LimitWriter { ··· 989 997 .resolve_revision(rev) 990 998 .and_then(|oid| repo.peel_to_commit(oid).ok()) 991 999 .ok_or_else(revision_not_found)?; 1000 + if hidden_staging_commit(&repo, rev) == Some(commit) { 1001 + return Ok(commit); 1002 + } 992 1003 match repo.reachable_from_public(commit) { 993 1004 Ok(true) => Ok(commit), 994 1005 Ok(false) => Err(revision_not_found()),
+71
knot2/crates/knot-xrpc/tests/reads.rs
··· 894 894 } 895 895 896 896 #[tokio::test] 897 + async fn a_hidden_staging_ref_resolves_for_fork_comparison_reads() { 898 + let world = World::new(); 899 + let (did, work) = seeded(&world, "limpet"); 900 + let bare = world.layout.repo_path(&did).unwrap(); 901 + 902 + sh_git(work.path(), &["checkout", "-q", "-b", "upstream"]); 903 + commit_file( 904 + work.path(), 905 + "upstream.txt", 906 + b"upstream\n", 907 + "upstream moved", 908 + "2026-06-01T12:50:00+02:00", 909 + ); 910 + let upstream = sh_git(work.path(), &["rev-parse", "HEAD"]); 911 + sh_git( 912 + work.path(), 913 + &[ 914 + "push", 915 + "-q", 916 + bare.to_str().unwrap(), 917 + "HEAD:refs/hidden/main/main", 918 + ], 919 + ); 920 + sh_git(work.path(), &["checkout", "-q", "main"]); 921 + commit_file( 922 + work.path(), 923 + "ours.txt", 924 + b"ours\n", 925 + "fork work", 926 + "2026-06-01T12:55:00+02:00", 927 + ); 928 + sh_git( 929 + work.path(), 930 + &["push", "-q", bare.to_str().unwrap(), "HEAD:refs/heads/main"], 931 + ); 932 + 933 + let comparison = get_json( 934 + &world, 935 + &format!("/xrpc/sh.tangled.repo.compare?repo={did}&rev1=hidden/main/main&rev2=main"), 936 + ) 937 + .await; 938 + assert_eq!(comparison["rev1"].as_str().unwrap(), upstream); 939 + assert!(!comparison["format_patch"].as_array().unwrap().is_empty()); 940 + 941 + let log = get_json( 942 + &world, 943 + &format!("/xrpc/sh.tangled.repo.log?repo={did}&ref=refs/hidden/main/main"), 944 + ) 945 + .await; 946 + assert!(!log["commits"].as_array().unwrap().is_empty()); 947 + 948 + let (status, _) = get_error( 949 + &world, 950 + &format!("/xrpc/sh.tangled.repo.log?repo={did}&ref={upstream}"), 951 + ) 952 + .await; 953 + assert_eq!( 954 + status, 955 + StatusCode::NOT_FOUND, 956 + "a raw oid reachable only through the hidden ref mustn't resolve" 957 + ); 958 + let (status, error) = get_error( 959 + &world, 960 + &format!("/xrpc/sh.tangled.repo.compare?repo={did}&rev1={upstream}&rev2=main"), 961 + ) 962 + .await; 963 + assert_eq!(status, StatusCode::BAD_REQUEST); 964 + assert_eq!(error, "RevisionNotFound"); 965 + } 966 + 967 + #[tokio::test] 897 968 async fn the_cob_ref_namespace_is_invisible_across_every_read() { 898 969 let world = World::new(); 899 970 let (did, work) = seeded(&world, "anemone");