a73x

f5d62365

Add --label filter to issue list and patch list

a73x   2026-08-09 16:50

Commit message
Add --label filter to issue list and patch list

Add a repeatable --label flag to both `issue list` and `patch list`
that matches items carrying any of the given labels (OR semantics).
The filter is applied by the shared filter_sort_paginate() helper
before sorting and before limit/offset pagination, so it composes
correctly with --all, --archived, --limit/--offset, and --json.

Listable grows a labels() method (default: empty slice) so types
without a label concept are unaffected. PatchState currently has no
way to acquire labels (there is no `patch label` command, unlike
`issue label`), so `patch list --label` accepts the flag but will
filter out every patch until patch labeling is added separately.

Fixes 65853fc9.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

src/cli.rs
Old New
@@ -18,12 +18,20 @@ pub trait Listable {
18 fn last_updated(&self) -> &str; 18 fn last_updated(&self) -> &str;
19 fn created_at(&self) -> &str; 19 fn created_at(&self) -> &str;
20 fn title(&self) -> &str; 20 fn title(&self) -> &str;
21 /// Labels this item carries, used for `--label` filtering. Types with no
22 /// label concept return an empty slice, so the filter is a no-op for them.
23 fn labels(&self) -> &[String] {
24 &[]
25 }
21 } 26 }
22 27
23 /// Filter by open status, sort by mode, then apply offset/limit pagination. 28 /// Filter by open status and labels, sort by mode, then apply offset/limit
29 /// pagination. `labels` uses OR semantics: an item matches if it carries any
30 /// of the given labels. An empty `labels` slice disables the filter.
24 pub fn filter_sort_paginate<T: Listable>( 31 pub fn filter_sort_paginate<T: Listable>(
25 items: Vec<T>, 32 items: Vec<T>,
26 show_closed: bool, 33 show_closed: bool,
34 labels: &[String],
27 sort: SortMode, 35 sort: SortMode,
28 offset: Option<usize>, 36 offset: Option<usize>,
29 limit: Option<usize>, 37 limit: Option<usize>,
@@ -31,6 +39,7 @@ pub fn filter_sort_paginate<T: Listable>(
31 let mut filtered: Vec<T> = items 39 let mut filtered: Vec<T> = items
32 .into_iter() 40 .into_iter()
33 .filter(|item| show_closed || item.is_open()) 41 .filter(|item| show_closed || item.is_open())
42 .filter(|item| labels.is_empty() || item.labels().iter().any(|l| labels.contains(l)))
34 .collect(); 43 .collect();
35 match sort { 44 match sort {
36 SortMode::Recent => filtered.sort_by(|a, b| b.last_updated().cmp(a.last_updated())), 45 SortMode::Recent => filtered.sort_by(|a, b| b.last_updated().cmp(a.last_updated())),
@@ -168,6 +177,9 @@ pub enum IssueCmd {
168 /// Sort order: recent (default), created, alpha 177 /// Sort order: recent (default), created, alpha
169 #[arg(long, default_value = "recent")] 178 #[arg(long, default_value = "recent")]
170 sort: SortMode, 179 sort: SortMode,
180 /// Filter by label (repeatable; matches issues carrying any of the given labels)
181 #[arg(long)]
182 label: Vec<String>,
171 }, 183 },
172 /// Show issue details 184 /// Show issue details
173 Show { 185 Show {
@@ -330,6 +342,9 @@ pub enum PatchCmd {
330 /// Sort order: recent (default), created, alpha 342 /// Sort order: recent (default), created, alpha
331 #[arg(long, default_value = "recent")] 343 #[arg(long, default_value = "recent")]
332 sort: SortMode, 344 sort: SortMode,
345 /// Filter by label (repeatable; matches patches carrying any of the given labels)
346 #[arg(long)]
347 label: Vec<String>,
333 }, 348 },
334 /// Show patch details 349 /// Show patch details
335 Show { 350 Show {
src/issue.rs
Old New
@@ -48,9 +48,10 @@ pub fn list(
48 limit: Option<usize>, 48 limit: Option<usize>,
49 offset: Option<usize>, 49 offset: Option<usize>,
50 sort: SortMode, 50 sort: SortMode,
51 labels: &[String],
51 ) -> Result<Vec<ListEntry>, crate::error::Error> { 52 ) -> Result<Vec<ListEntry>, crate::error::Error> {
52 let issues = load_issues(repo, show_archived)?; 53 let issues = load_issues(repo, show_archived)?;
53 let filtered = cli::filter_sort_paginate(issues, show_closed, sort, offset, limit); 54 let filtered = cli::filter_sort_paginate(issues, show_closed, labels, sort, offset, limit);
54 let entries = filtered 55 let entries = filtered
55 .into_iter() 56 .into_iter()
56 .map(|issue| { 57 .map(|issue| {
@@ -61,6 +62,7 @@ pub fn list(
61 Ok(entries) 62 Ok(entries)
62 } 63 }
63 64
65 #[allow(clippy::too_many_arguments)]
64 pub fn list_to_writer( 66 pub fn list_to_writer(
65 repo: &Repository, 67 repo: &Repository,
66 show_closed: bool, 68 show_closed: bool,
@@ -68,9 +70,10 @@ pub fn list_to_writer(
68 limit: Option<usize>, 70 limit: Option<usize>,
69 offset: Option<usize>, 71 offset: Option<usize>,
70 sort: SortMode, 72 sort: SortMode,
73 labels: &[String],
71 writer: &mut dyn std::io::Write, 74 writer: &mut dyn std::io::Write,
72 ) -> Result<(), crate::error::Error> { 75 ) -> Result<(), crate::error::Error> {
73 let entries = list(repo, show_closed, show_archived, limit, offset, sort)?; 76 let entries = list(repo, show_closed, show_archived, limit, offset, sort, labels)?;
74 if entries.is_empty() { 77 if entries.is_empty() {
75 writeln!(writer, "No issues found.").ok(); 78 writeln!(writer, "No issues found.").ok();
76 return Ok(()); 79 return Ok(());
@@ -120,9 +123,10 @@ pub fn list_json(
120 show_closed: bool, 123 show_closed: bool,
121 show_archived: bool, 124 show_archived: bool,
122 sort: SortMode, 125 sort: SortMode,
126 labels: &[String],
123 ) -> Result<String, crate::error::Error> { 127 ) -> Result<String, crate::error::Error> {
124 let issues = load_issues(repo, show_archived)?; 128 let issues = load_issues(repo, show_archived)?;
125 let filtered = cli::filter_sort_paginate(issues, show_closed, sort, None, None); 129 let filtered = cli::filter_sort_paginate(issues, show_closed, labels, sort, None, None);
126 Ok(serde_json::to_string_pretty(&filtered)?) 130 Ok(serde_json::to_string_pretty(&filtered)?)
127 } 131 }
128 132
src/lib.rs
Old New
@@ -129,13 +129,14 @@ pub fn run(cli: cli::Cli, repo: &Repository) -> Result<(), error::Error> {
129 offset, 129 offset,
130 json, 130 json,
131 sort, 131 sort,
132 label,
132 } => { 133 } => {
133 if json { 134 if json {
134 let output = issue::list_json(repo, all, archived, sort)?; 135 let output = issue::list_json(repo, all, archived, sort, &label)?;
135 println!("{}", output); 136 println!("{}", output);
136 return Ok(()); 137 return Ok(());
137 } 138 }
138 let entries = issue::list(repo, all, archived, limit, offset, sort)?; 139 let entries = issue::list(repo, all, archived, limit, offset, sort, &label)?;
139 if entries.is_empty() { 140 if entries.is_empty() {
140 println!("No issues found."); 141 println!("No issues found.");
141 } else { 142 } else {
@@ -347,13 +348,14 @@ pub fn run(cli: cli::Cli, repo: &Repository) -> Result<(), error::Error> {
347 offset, 348 offset,
348 json, 349 json,
349 sort, 350 sort,
351 label,
350 } => { 352 } => {
351 if json { 353 if json {
352 let output = patch::list_json(repo, all, archived, sort)?; 354 let output = patch::list_json(repo, all, archived, sort, &label)?;
353 println!("{}", output); 355 println!("{}", output);
354 return Ok(()); 356 return Ok(());
355 } 357 }
356 let entries = patch::list(repo, all, archived, limit, offset, sort)?; 358 let entries = patch::list(repo, all, archived, limit, offset, sort, &label)?;
357 if entries.is_empty() { 359 if entries.is_empty() {
358 println!("No patches found."); 360 println!("No patches found.");
359 } else { 361 } else {
src/patch.rs
Old New
@@ -170,13 +170,14 @@ pub fn list(
170 limit: Option<usize>, 170 limit: Option<usize>,
171 offset: Option<usize>, 171 offset: Option<usize>,
172 sort: SortMode, 172 sort: SortMode,
173 labels: &[String],
173 ) -> Result<Vec<ListEntry>, crate::error::Error> { 174 ) -> Result<Vec<ListEntry>, crate::error::Error> {
174 let patches = if show_archived { 175 let patches = if show_archived {
175 state::list_patches_with_archived(repo)? 176 state::list_patches_with_archived(repo)?
176 } else { 177 } else {
177 state::list_patches(repo)? 178 state::list_patches(repo)?
178 }; 179 };
179 let filtered = cli::filter_sort_paginate(patches, show_closed, sort, offset, limit); 180 let filtered = cli::filter_sort_paginate(patches, show_closed, labels, sort, offset, limit);
180 let entries = filtered 181 let entries = filtered
181 .into_iter() 182 .into_iter()
182 .map(|patch| { 183 .map(|patch| {
@@ -187,6 +188,7 @@ pub fn list(
187 Ok(entries) 188 Ok(entries)
188 } 189 }
189 190
191 #[allow(clippy::too_many_arguments)]
190 pub fn list_to_writer( 192 pub fn list_to_writer(
191 repo: &Repository, 193 repo: &Repository,
192 show_closed: bool, 194 show_closed: bool,
@@ -194,9 +196,10 @@ pub fn list_to_writer(
194 limit: Option<usize>, 196 limit: Option<usize>,
195 offset: Option<usize>, 197 offset: Option<usize>,
196 sort: SortMode, 198 sort: SortMode,
199 labels: &[String],
197 writer: &mut dyn std::io::Write, 200 writer: &mut dyn std::io::Write,
198 ) -> Result<(), crate::error::Error> { 201 ) -> Result<(), crate::error::Error> {
199 let entries = list(repo, show_closed, show_archived, limit, offset, sort)?; 202 let entries = list(repo, show_closed, show_archived, limit, offset, sort, labels)?;
200 if entries.is_empty() { 203 if entries.is_empty() {
201 writeln!(writer, "No patches found.").ok(); 204 writeln!(writer, "No patches found.").ok();
202 return Ok(()); 205 return Ok(());
@@ -227,8 +230,9 @@ pub fn list_json(
227 show_closed: bool, 230 show_closed: bool,
228 show_archived: bool, 231 show_archived: bool,
229 sort: SortMode, 232 sort: SortMode,
233 labels: &[String],
230 ) -> Result<String, crate::error::Error> { 234 ) -> Result<String, crate::error::Error> {
231 let entries = list(repo, show_closed, show_archived, None, None, sort)?; 235 let entries = list(repo, show_closed, show_archived, None, None, sort, labels)?;
232 let patches: Vec<&PatchState> = entries.iter().map(|e| &e.patch).collect(); 236 let patches: Vec<&PatchState> = entries.iter().map(|e| &e.patch).collect();
233 Ok(serde_json::to_string_pretty(&patches)?) 237 Ok(serde_json::to_string_pretty(&patches)?)
234 } 238 }
src/state.rs
Old New
@@ -253,6 +253,9 @@ impl crate::cli::Listable for IssueState {
253 fn title(&self) -> &str { 253 fn title(&self) -> &str {
254 &self.title 254 &self.title
255 } 255 }
256 fn labels(&self) -> &[String] {
257 &self.labels
258 }
256 } 259 }
257 260
258 impl crate::cli::Listable for PatchState { 261 impl crate::cli::Listable for PatchState {
tests/cli_test.rs
Old New
@@ -278,6 +278,93 @@ fn test_issue_label_shown_in_list() {
278 assert!(out.contains("enhancement")); 278 assert!(out.contains("enhancement"));
279 } 279 }
280 280
281 #[test]
282 fn test_issue_list_filters_by_label() {
283 let repo = TestRepo::new("Alice", "alice@example.com");
284 let bug_id = repo.issue_open("A bug");
285 repo.issue_open("No label");
286 repo.run_ok(&["issue", "label", &bug_id, "bug"]);
287
288 let out = repo.run_ok(&["issue", "list", "--label", "bug"]);
289 assert!(out.contains("A bug"));
290 assert!(!out.contains("No label"));
291 }
292
293 #[test]
294 fn test_issue_list_label_or_semantics() {
295 let repo = TestRepo::new("Alice", "alice@example.com");
296 let bug_id = repo.issue_open("A bug");
297 let docs_id = repo.issue_open("Docs issue");
298 repo.issue_open("Unrelated");
299 repo.run_ok(&["issue", "label", &bug_id, "bug"]);
300 repo.run_ok(&["issue", "label", &docs_id, "docs"]);
301
302 let out = repo.run_ok(&["issue", "list", "--label", "bug", "--label", "docs"]);
303 assert!(out.contains("A bug"));
304 assert!(out.contains("Docs issue"));
305 assert!(!out.contains("Unrelated"));
306 }
307
308 #[test]
309 fn test_issue_list_label_no_match() {
310 let repo = TestRepo::new("Alice", "alice@example.com");
311 repo.issue_open("Unlabeled");
312
313 let out = repo.run_ok(&["issue", "list", "--label", "nonexistent"]);
314 assert!(out.contains("No issues found"));
315 }
316
317 #[test]
318 fn test_issue_list_label_composes_with_all_archived() {
319 let repo = TestRepo::new("Alice", "alice@example.com");
320 let id = repo.issue_open("Closed labeled");
321 repo.run_ok(&["issue", "label", &id, "bug"]);
322 repo.run_ok(&["issue", "close", &id]);
323
324 // Closed issues are archived, so --label alone (without --all --archived)
325 // must not show it, even though it carries a matching label.
326 let out = repo.run_ok(&["issue", "list", "--label", "bug"]);
327 assert!(!out.contains("Closed labeled"));
328
329 let out = repo.run_ok(&["issue", "list", "--all", "--archived", "--label", "bug"]);
330 assert!(out.contains("Closed labeled"));
331 }
332
333 #[test]
334 fn test_issue_list_label_limit_offset_applies_after_filter() {
335 let repo = TestRepo::new("Alice", "alice@example.com");
336 let a = repo.issue_open("Alpha");
337 let _b = repo.issue_open("Beta unlabeled");
338 let c = repo.issue_open("Gamma");
339 repo.run_ok(&["issue", "label", &a, "keep"]);
340 repo.run_ok(&["issue", "label", &c, "keep"]);
341
342 // Only "Alpha" and "Gamma" carry the "keep" label. Sorting alphabetically
343 // and requesting a single result with offset 1 should skip "Alpha" and
344 // land on "Gamma" -- proving limit/offset apply to the filtered set, not
345 // the unfiltered one (which would offset past "Beta unlabeled" instead).
346 let out = repo.run_ok(&[
347 "issue", "list", "--label", "keep", "--sort", "alpha", "--offset", "1", "-n", "1",
348 ]);
349 assert!(out.contains("Gamma"));
350 assert!(!out.contains("Alpha"));
351 assert!(!out.contains("Beta"));
352 }
353
354 #[test]
355 fn test_issue_list_label_json() {
356 let repo = TestRepo::new("Alice", "alice@example.com");
357 let bug_id = repo.issue_open("A bug");
358 repo.issue_open("No label");
359 repo.run_ok(&["issue", "label", &bug_id, "bug"]);
360
361 let out = repo.run_ok(&["issue", "list", "--json", "--label", "bug"]);
362 let parsed: serde_json::Value = serde_json::from_str(&out).unwrap();
363 let arr = parsed.as_array().unwrap();
364 assert_eq!(arr.len(), 1);
365 assert_eq!(arr[0]["title"], "A bug");
366 }
367
281 // =========================================================================== 368 // ===========================================================================
282 // Issue assignees 369 // Issue assignees
283 // =========================================================================== 370 // ===========================================================================
@@ -505,6 +592,23 @@ fn test_patch_list_filters_by_status() {
505 } 592 }
506 593
507 #[test] 594 #[test]
595 fn test_patch_list_label_flag_accepted() {
596 // Patches have no way to carry labels yet (there is no `patch label`
597 // command, unlike `issue label`), so `--label` can never match anything
598 // today. The flag must still parse -- including repeated occurrences --
599 // and correctly filter every patch out rather than erroring or being
600 // silently ignored.
601 let repo = TestRepo::new("Alice", "alice@example.com");
602 repo.patch_create("Open patch");
603
604 let out = repo.run_ok(&["patch", "list"]);
605 assert!(out.contains("Open patch"));
606
607 let out = repo.run_ok(&["patch", "list", "--label", "bug", "--label", "docs"]);
608 assert!(out.contains("No patches found"));
609 }
610
611 #[test]
508 fn test_patch_comment() { 612 fn test_patch_comment() {
509 let repo = TestRepo::new("Alice", "alice@example.com"); 613 let repo = TestRepo::new("Alice", "alice@example.com");
510 let id = repo.patch_create("Review me"); 614 let id = repo.patch_create("Review me");
tests/collab_test.rs
Old New
@@ -1253,6 +1253,7 @@ fn capture_issue_list(
1253 limit, 1253 limit,
1254 offset, 1254 offset,
1255 git_collab::cli::SortMode::Recent, 1255 git_collab::cli::SortMode::Recent,
1256 &[],
1256 &mut buf, 1257 &mut buf,
1257 ) 1258 )
1258 .unwrap(); 1259 .unwrap();
@@ -1274,6 +1275,7 @@ fn capture_patch_list(
1274 limit, 1275 limit,
1275 offset, 1276 offset,
1276 git_collab::cli::SortMode::Recent, 1277 git_collab::cli::SortMode::Recent,
1278 &[],
1277 &mut buf, 1279 &mut buf,
1278 ) 1280 )
1279 .unwrap(); 1281 .unwrap();
@@ -1490,7 +1492,7 @@ fn test_issue_list_json_output() {
1490 open_issue(&repo, &bob(), "Issue two"); 1492 open_issue(&repo, &bob(), "Issue two");
1491 1493
1492 let json_str = 1494 let json_str =
1493 git_collab::issue::list_json(&repo, false, false, git_collab::cli::SortMode::Recent) 1495 git_collab::issue::list_json(&repo, false, false, git_collab::cli::SortMode::Recent, &[])
1494 .unwrap(); 1496 .unwrap();
1495 let value: serde_json::Value = serde_json::from_str(&json_str).unwrap(); 1497 let value: serde_json::Value = serde_json::from_str(&json_str).unwrap();
1496 let arr = value.as_array().unwrap(); 1498 let arr = value.as_array().unwrap();
@@ -1512,14 +1514,14 @@ fn test_issue_list_json_filters_closed() {
1512 1514
1513 // Without --all, only open issues 1515 // Without --all, only open issues
1514 let json_str = 1516 let json_str =
1515 git_collab::issue::list_json(&repo, false, false, git_collab::cli::SortMode::Recent) 1517 git_collab::issue::list_json(&repo, false, false, git_collab::cli::SortMode::Recent, &[])
1516 .unwrap(); 1518 .unwrap();
1517 let value: serde_json::Value = serde_json::from_str(&json_str).unwrap(); 1519 let value: serde_json::Value = serde_json::from_str(&json_str).unwrap();
1518 assert_eq!(value.as_array().unwrap().len(), 1); 1520 assert_eq!(value.as_array().unwrap().len(), 1);
1519 1521
1520 // With --all, both 1522 // With --all, both
1521 let json_str = 1523 let json_str =
1522 git_collab::issue::list_json(&repo, true, false, git_collab::cli::SortMode::Recent) 1524 git_collab::issue::list_json(&repo, true, false, git_collab::cli::SortMode::Recent, &[])
1523 .unwrap(); 1525 .unwrap();
1524 let value: serde_json::Value = serde_json::from_str(&json_str).unwrap(); 1526 let value: serde_json::Value = serde_json::from_str(&json_str).unwrap();
1525 assert_eq!(value.as_array().unwrap().len(), 2); 1527 assert_eq!(value.as_array().unwrap().len(), 2);
@@ -1548,7 +1550,7 @@ fn test_patch_list_json_output() {
1548 create_patch(&repo, &bob(), "Patch two"); 1550 create_patch(&repo, &bob(), "Patch two");
1549 1551
1550 let json_str = 1552 let json_str =
1551 git_collab::patch::list_json(&repo, false, false, git_collab::cli::SortMode::Recent) 1553 git_collab::patch::list_json(&repo, false, false, git_collab::cli::SortMode::Recent, &[])
1552 .unwrap(); 1554 .unwrap();
1553 let value: serde_json::Value = serde_json::from_str(&json_str).unwrap(); 1555 let value: serde_json::Value = serde_json::from_str(&json_str).unwrap();
1554 let arr = value.as_array().unwrap(); 1556 let arr = value.as_array().unwrap();
tests/sort_test.rs
Old New
@@ -142,7 +142,7 @@ fn test_issue_default_sort_by_recency() {
142 142
143 // Default sort = recent: issue A (last_updated=2025-12) should come first 143 // Default sort = recent: issue A (last_updated=2025-12) should come first
144 let entries = 144 let entries =
145 git_collab::issue::list(&repo, true, false, None, None, SortMode::Recent).unwrap(); 145 git_collab::issue::list(&repo, true, false, None, None, SortMode::Recent, &[]).unwrap();
146 assert_eq!(entries.len(), 2); 146 assert_eq!(entries.len(), 2);
147 assert_eq!(entries[0].issue.title, "Alpha issue"); 147 assert_eq!(entries[0].issue.title, "Alpha issue");
148 assert_eq!(entries[1].issue.title, "Beta issue"); 148 assert_eq!(entries[1].issue.title, "Beta issue");
@@ -168,7 +168,7 @@ fn test_issue_sort_by_created() {
168 168
169 // Sort by created: B (2025-06) comes first (descending) 169 // Sort by created: B (2025-06) comes first (descending)
170 let entries = 170 let entries =
171 git_collab::issue::list(&repo, true, false, None, None, SortMode::Created).unwrap(); 171 git_collab::issue::list(&repo, true, false, None, None, SortMode::Created, &[]).unwrap();
172 assert_eq!(entries.len(), 2); 172 assert_eq!(entries.len(), 2);
173 assert_eq!(entries[0].issue.title, "Beta issue"); 173 assert_eq!(entries[0].issue.title, "Beta issue");
174 assert_eq!(entries[1].issue.title, "Alpha issue"); 174 assert_eq!(entries[1].issue.title, "Alpha issue");
@@ -183,7 +183,8 @@ fn test_issue_sort_alpha() {
183 open_issue_at(&repo, &alice(), "Apple issue", "2025-06-01T00:00:00Z"); 183 open_issue_at(&repo, &alice(), "Apple issue", "2025-06-01T00:00:00Z");
184 open_issue_at(&repo, &alice(), "Mango issue", "2025-03-01T00:00:00Z"); 184 open_issue_at(&repo, &alice(), "Mango issue", "2025-03-01T00:00:00Z");
185 185
186 let entries = git_collab::issue::list(&repo, true, false, None, None, SortMode::Alpha).unwrap(); 186 let entries =
187 git_collab::issue::list(&repo, true, false, None, None, SortMode::Alpha, &[]).unwrap();
187 assert_eq!(entries.len(), 3); 188 assert_eq!(entries.len(), 3);
188 assert_eq!(entries[0].issue.title, "Apple issue"); 189 assert_eq!(entries[0].issue.title, "Apple issue");
189 assert_eq!(entries[1].issue.title, "Mango issue"); 190 assert_eq!(entries[1].issue.title, "Mango issue");
@@ -230,7 +231,7 @@ fn test_patch_default_sort_by_recency() {
230 let (_, _) = create_patch_at(&repo, &alice(), "Beta patch", "2025-06-01T00:00:00Z"); 231 let (_, _) = create_patch_at(&repo, &alice(), "Beta patch", "2025-06-01T00:00:00Z");
231 232
232 let entries = 233 let entries =
233 git_collab::patch::list(&repo, true, false, None, None, SortMode::Recent).unwrap(); 234 git_collab::patch::list(&repo, true, false, None, None, SortMode::Recent, &[]).unwrap();
234 assert_eq!(entries.len(), 2); 235 assert_eq!(entries.len(), 2);
235 assert_eq!(entries[0].patch.title, "Alpha patch"); 236 assert_eq!(entries[0].patch.title, "Alpha patch");
236 assert_eq!(entries[1].patch.title, "Beta patch"); 237 assert_eq!(entries[1].patch.title, "Beta patch");
@@ -253,7 +254,7 @@ fn test_patch_sort_by_created() {
253 let (_, _) = create_patch_at(&repo, &alice(), "Beta patch", "2025-06-01T00:00:00Z"); 254 let (_, _) = create_patch_at(&repo, &alice(), "Beta patch", "2025-06-01T00:00:00Z");
254 255
255 let entries = 256 let entries =
256 git_collab::patch::list(&repo, true, false, None, None, SortMode::Created).unwrap(); 257 git_collab::patch::list(&repo, true, false, None, None, SortMode::Created, &[]).unwrap();
257 assert_eq!(entries.len(), 2); 258 assert_eq!(entries.len(), 2);
258 assert_eq!(entries[0].patch.title, "Beta patch"); 259 assert_eq!(entries[0].patch.title, "Beta patch");
259 assert_eq!(entries[1].patch.title, "Alpha patch"); 260 assert_eq!(entries[1].patch.title, "Alpha patch");
@@ -268,7 +269,8 @@ fn test_patch_sort_alpha() {
268 create_patch_at(&repo, &alice(), "Apple patch", "2025-06-01T00:00:00Z"); 269 create_patch_at(&repo, &alice(), "Apple patch", "2025-06-01T00:00:00Z");
269 create_patch_at(&repo, &alice(), "Mango patch", "2025-03-01T00:00:00Z"); 270 create_patch_at(&repo, &alice(), "Mango patch", "2025-03-01T00:00:00Z");
270 271
271 let entries = git_collab::patch::list(&repo, true, false, None, None, SortMode::Alpha).unwrap(); 272 let entries =
273 git_collab::patch::list(&repo, true, false, None, None, SortMode::Alpha, &[]).unwrap();
272 assert_eq!(entries.len(), 3); 274 assert_eq!(entries.len(), 3);
273 assert_eq!(entries[0].patch.title, "Apple patch"); 275 assert_eq!(entries[0].patch.title, "Apple patch");
274 assert_eq!(entries[1].patch.title, "Mango patch"); 276 assert_eq!(entries[1].patch.title, "Mango patch");