4fd458e4
Drop --label from patch list; patches have no labels
a73x 2026-08-09 16:50
Commit message
src/cli.rs
| Old | New | ||
|---|---|---|---|
| @@ -342,9 +342,6 @@ pub enum PatchCmd { | |||
| 342 | /// Sort order: recent (default), created, alpha | 342 | /// Sort order: recent (default), created, alpha |
| 343 | #[arg(long, default_value = "recent")] | 343 | #[arg(long, default_value = "recent")] |
| 344 | 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>, | ||
| 348 | }, | 345 | }, |
| 349 | /// Show patch details | 346 | /// Show patch details |
| 350 | Show { | 347 | Show { |
src/lib.rs
| Old | New | ||
|---|---|---|---|
| @@ -348,14 +348,13 @@ pub fn run(cli: cli::Cli, repo: &Repository) -> Result<(), error::Error> { | |||
| 348 | offset, | 348 | offset, |
| 349 | json, | 349 | json, |
| 350 | sort, | 350 | sort, |
| 351 | label, | ||
| 352 | } => { | 351 | } => { |
| 353 | if json { | 352 | if json { |
| 354 | let output = patch::list_json(repo, all, archived, sort, &label)?; | 353 | let output = patch::list_json(repo, all, archived, sort)?; |
| 355 | println!("{}", output); | 354 | println!("{}", output); |
| 356 | return Ok(()); | 355 | return Ok(()); |
| 357 | } | 356 | } |
| 358 | let entries = patch::list(repo, all, archived, limit, offset, sort, &label)?; | 357 | let entries = patch::list(repo, all, archived, limit, offset, sort)?; |
| 359 | if entries.is_empty() { | 358 | if entries.is_empty() { |
| 360 | println!("No patches found."); | 359 | println!("No patches found."); |
| 361 | } else { | 360 | } else { |
src/patch.rs
| Old | New | ||
|---|---|---|---|
| @@ -170,14 +170,16 @@ 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], | ||
| 174 | ) -> Result<Vec<ListEntry>, crate::error::Error> { | 173 | ) -> Result<Vec<ListEntry>, crate::error::Error> { |
| 175 | let patches = if show_archived { | 174 | let patches = if show_archived { |
| 176 | state::list_patches_with_archived(repo)? | 175 | state::list_patches_with_archived(repo)? |
| 177 | } else { | 176 | } else { |
| 178 | state::list_patches(repo)? | 177 | state::list_patches(repo)? |
| 179 | }; | 178 | }; |
| 180 | let filtered = cli::filter_sort_paginate(patches, show_closed, labels, sort, offset, limit); | 179 | // Patches have no labelling mechanism yet (no `patch label` command, no |
| 180 | // `labels` field on PatchState), so there is no CLI flag to plumb a | ||
| 181 | // label filter through here. Pass an empty slice, which is a no-op. | ||
| 182 | let filtered = cli::filter_sort_paginate(patches, show_closed, &[], sort, offset, limit); | ||
| 181 | let entries = filtered | 183 | let entries = filtered |
| 182 | .into_iter() | 184 | .into_iter() |
| 183 | .map(|patch| { | 185 | .map(|patch| { |
| @@ -188,7 +190,6 @@ pub fn list( | |||
| 188 | Ok(entries) | 190 | Ok(entries) |
| 189 | } | 191 | } |
| 190 | 192 | ||
| 191 | #[allow(clippy::too_many_arguments)] | ||
| 192 | pub fn list_to_writer( | 193 | pub fn list_to_writer( |
| 193 | repo: &Repository, | 194 | repo: &Repository, |
| 194 | show_closed: bool, | 195 | show_closed: bool, |
| @@ -196,10 +197,9 @@ pub fn list_to_writer( | |||
| 196 | limit: Option<usize>, | 197 | limit: Option<usize>, |
| 197 | offset: Option<usize>, | 198 | offset: Option<usize>, |
| 198 | sort: SortMode, | 199 | sort: SortMode, |
| 199 | labels: &[String], | ||
| 200 | writer: &mut dyn std::io::Write, | 200 | writer: &mut dyn std::io::Write, |
| 201 | ) -> Result<(), crate::error::Error> { | 201 | ) -> Result<(), crate::error::Error> { |
| 202 | let entries = list(repo, show_closed, show_archived, limit, offset, sort, labels)?; | 202 | let entries = list(repo, show_closed, show_archived, limit, offset, sort)?; |
| 203 | if entries.is_empty() { | 203 | if entries.is_empty() { |
| 204 | writeln!(writer, "No patches found.").ok(); | 204 | writeln!(writer, "No patches found.").ok(); |
| 205 | return Ok(()); | 205 | return Ok(()); |
| @@ -230,9 +230,8 @@ pub fn list_json( | |||
| 230 | show_closed: bool, | 230 | show_closed: bool, |
| 231 | show_archived: bool, | 231 | show_archived: bool, |
| 232 | sort: SortMode, | 232 | sort: SortMode, |
| 233 | labels: &[String], | ||
| 234 | ) -> Result<String, crate::error::Error> { | 233 | ) -> Result<String, crate::error::Error> { |
| 235 | let entries = list(repo, show_closed, show_archived, None, None, sort, labels)?; | 234 | let entries = list(repo, show_closed, show_archived, None, None, sort)?; |
| 236 | let patches: Vec<&PatchState> = entries.iter().map(|e| &e.patch).collect(); | 235 | let patches: Vec<&PatchState> = entries.iter().map(|e| &e.patch).collect(); |
| 237 | Ok(serde_json::to_string_pretty(&patches)?) | 236 | Ok(serde_json::to_string_pretty(&patches)?) |
| 238 | } | 237 | } |
tests/cli_test.rs
| Old | New | ||
|---|---|---|---|
| @@ -592,20 +592,28 @@ fn test_patch_list_filters_by_status() { | |||
| 592 | } | 592 | } |
| 593 | 593 | ||
| 594 | #[test] | 594 | #[test] |
| 595 | fn test_patch_list_label_flag_accepted() { | 595 | fn test_patch_list_rejects_label_flag() { |
| 596 | // Patches have no way to carry labels yet (there is no `patch label` | 596 | // Patches have no way to carry labels (there is no `patch label` |
| 597 | // command, unlike `issue label`), so `--label` can never match anything | 597 | // command, unlike `issue label`, and no `labels` field on PatchState). |
| 598 | // today. The flag must still parse -- including repeated occurrences -- | 598 | // A `--label` flag that always returns an empty list would be worse |
| 599 | // and correctly filter every patch out rather than erroring or being | 599 | // than no flag at all -- it looks like "no patches have this label" |
| 600 | // silently ignored. | 600 | // when really the concept doesn't exist. So `patch list` must reject |
| 601 | // `--label` outright as an unknown argument rather than silently | ||
| 602 | // accepting and no-op-filtering it. | ||
| 601 | let repo = TestRepo::new("Alice", "alice@example.com"); | 603 | let repo = TestRepo::new("Alice", "alice@example.com"); |
| 602 | repo.patch_create("Open patch"); | 604 | repo.patch_create("Open patch"); |
| 603 | 605 | ||
| 604 | let out = repo.run_ok(&["patch", "list"]); | 606 | let output = repo.run(&["patch", "list", "--label", "bug"]); |
| 605 | assert!(out.contains("Open patch")); | 607 | assert!( |
| 606 | 608 | !output.status.success(), | |
| 607 | let out = repo.run_ok(&["patch", "list", "--label", "bug", "--label", "docs"]); | 609 | "patch list --label should be rejected" |
| 608 | assert!(out.contains("No patches found")); | 610 | ); |
| 611 | let stderr = String::from_utf8(output.stderr).unwrap(); | ||
| 612 | assert!( | ||
| 613 | stderr.contains("unexpected argument") || stderr.contains("unrecognized"), | ||
| 614 | "expected an unknown-argument error, got: {}", | ||
| 615 | stderr | ||
| 616 | ); | ||
| 609 | } | 617 | } |
| 610 | 618 | ||
| 611 | #[test] | 619 | #[test] |
tests/collab_test.rs
| Old | New | ||
|---|---|---|---|
| @@ -1275,7 +1275,6 @@ fn capture_patch_list( | |||
| 1275 | limit, | 1275 | limit, |
| 1276 | offset, | 1276 | offset, |
| 1277 | git_collab::cli::SortMode::Recent, | 1277 | git_collab::cli::SortMode::Recent, |
| 1278 | &[], | ||
| 1279 | &mut buf, | 1278 | &mut buf, |
| 1280 | ) | 1279 | ) |
| 1281 | .unwrap(); | 1280 | .unwrap(); |
| @@ -1550,7 +1549,7 @@ fn test_patch_list_json_output() { | |||
| 1550 | create_patch(&repo, &bob(), "Patch two"); | 1549 | create_patch(&repo, &bob(), "Patch two"); |
| 1551 | 1550 | ||
| 1552 | let json_str = | 1551 | let json_str = |
| 1553 | git_collab::patch::list_json(&repo, false, false, git_collab::cli::SortMode::Recent, &[]) | 1552 | git_collab::patch::list_json(&repo, false, false, git_collab::cli::SortMode::Recent) |
| 1554 | .unwrap(); | 1553 | .unwrap(); |
| 1555 | let value: serde_json::Value = serde_json::from_str(&json_str).unwrap(); | 1554 | let value: serde_json::Value = serde_json::from_str(&json_str).unwrap(); |
| 1556 | let arr = value.as_array().unwrap(); | 1555 | let arr = value.as_array().unwrap(); |
tests/sort_test.rs
| Old | New | ||
|---|---|---|---|
| @@ -231,7 +231,7 @@ fn test_patch_default_sort_by_recency() { | |||
| 231 | 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"); |
| 232 | 232 | ||
| 233 | let entries = | 233 | let entries = |
| 234 | 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(); |
| 235 | assert_eq!(entries.len(), 2); | 235 | assert_eq!(entries.len(), 2); |
| 236 | assert_eq!(entries[0].patch.title, "Alpha patch"); | 236 | assert_eq!(entries[0].patch.title, "Alpha patch"); |
| 237 | assert_eq!(entries[1].patch.title, "Beta patch"); | 237 | assert_eq!(entries[1].patch.title, "Beta patch"); |
| @@ -254,7 +254,7 @@ fn test_patch_sort_by_created() { | |||
| 254 | 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"); |
| 255 | 255 | ||
| 256 | let entries = | 256 | let entries = |
| 257 | 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(); |
| 258 | assert_eq!(entries.len(), 2); | 258 | assert_eq!(entries.len(), 2); |
| 259 | assert_eq!(entries[0].patch.title, "Beta patch"); | 259 | assert_eq!(entries[0].patch.title, "Beta patch"); |
| 260 | assert_eq!(entries[1].patch.title, "Alpha patch"); | 260 | assert_eq!(entries[1].patch.title, "Alpha patch"); |
| @@ -269,8 +269,7 @@ fn test_patch_sort_alpha() { | |||
| 269 | 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"); |
| 270 | 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"); |
| 271 | 271 | ||
| 272 | let entries = | 272 | let entries = git_collab::patch::list(&repo, true, false, None, None, SortMode::Alpha).unwrap(); |
| 273 | git_collab::patch::list(&repo, true, false, None, None, SortMode::Alpha, &[]).unwrap(); | ||
| 274 | assert_eq!(entries.len(), 3); | 273 | assert_eq!(entries.len(), 3); |
| 275 | assert_eq!(entries[0].patch.title, "Apple patch"); | 274 | assert_eq!(entries[0].patch.title, "Apple patch"); |
| 276 | assert_eq!(entries[1].patch.title, "Mango patch"); | 275 | assert_eq!(entries[1].patch.title, "Mango patch"); |