@@ -20,16 +20,18 @@ var (
20
20
branchPrefix string
21
21
branchSuffix string
22
22
branchRegex string
23
- labels []string
23
+ selectLabel string
24
+ selectLabels []string
25
+ addLabels []string
24
26
assignees []string
25
- mustBeGreen bool
27
+ requireCI bool
26
28
mustBeApproved bool
27
29
autoclose bool
28
30
updateBranch bool
29
31
ignoreLabel string
30
- selectLabel string
32
+ ignoreLabels [] string
31
33
reposFile string
32
- minCombine int
34
+ minimum int
33
35
)
34
36
35
37
// NewRootCmd creates the root command for the gh-combine CLI
@@ -38,31 +40,74 @@ func NewRootCmd() *cobra.Command {
38
40
Use : "combine [repo1,repo2,...]" ,
39
41
Short : "Combine multiple pull requests into a single PR" ,
40
42
Long : `Combine multiple pull requests that match specific criteria into a single PR.
41
- Examples:
42
- gh combine octocat/hello-world
43
- gh combine octocat/repo1,octocat/repo2
44
- gh combine --file repos.txt
45
- gh combine octocat/hello-world --branch-prefix dependabot-
46
- gh combine octocat/hello-world --branch-regex "dependabot/.*"
47
- gh combine octocat/hello-world --labels security,dependencies
48
- gh combine octocat/hello-world --select-label dependencies` ,
43
+ Examples:
44
+ # Basic usage with a single repository
45
+ gh combine octocat/hello-world
46
+
47
+ # Multiple repositories (comma-separated)
48
+ gh combine octocat/repo1,octocat/repo2
49
+
50
+ # Using a file with repository names (one per line)
51
+ gh combine --file repos.txt
52
+
53
+ # Filter PRs by branch name
54
+ gh combine octocat/hello-world --branch-prefix dependabot-
55
+ gh combine octocat/hello-world --branch-suffix -update
56
+ gh combine octocat/hello-world --branch-regex "dependabot/.*"
57
+
58
+ # Filter PRs by labels
59
+ gh combine octocat/hello-world --label dependencies # PRs must have this single label
60
+ gh combine octocat/hello-world --labels security,dependencies # PRs must have ALL these labels
61
+
62
+ # Exclude PRs by labels
63
+ gh combine octocat/hello-world --ignore-label wip # Ignore PRs with this label
64
+ gh combine octocat/hello-world --ignore-labels wip,draft # Ignore PRs with ANY of these labels
65
+
66
+ # Set requirements for PRs to be combined
67
+ gh combine octocat/hello-world --require-ci # Only include PRs with passing CI
68
+ gh combine octocat/hello-world --require-approved # Only include approved PRs
69
+ gh combine octocat/hello-world --minimum 3 # Need at least 3 matching PRs
70
+
71
+ # Add metadata to combined PR
72
+ gh combine octocat/hello-world --add-labels security,dependencies # Add these labels to the new PR
73
+ gh combine octocat/hello-world --assignees octocat,hubot # Assign users to the new PR
74
+
75
+ # Additional options
76
+ gh combine octocat/hello-world --autoclose # Close source PRs when combined PR is merged
77
+ gh combine octocat/hello-world --update-branch # Update the branch of the combined PR` ,
49
78
RunE : runCombine ,
50
79
}
51
80
52
81
// Add flags
53
82
rootCmd .Flags ().StringVar (& branchPrefix , "branch-prefix" , "" , "Branch prefix to filter PRs" )
54
83
rootCmd .Flags ().StringVar (& branchSuffix , "branch-suffix" , "" , "Branch suffix to filter PRs" )
55
84
rootCmd .Flags ().StringVar (& branchRegex , "branch-regex" , "" , "Regex pattern to filter PRs by branch name" )
56
- rootCmd .Flags ().StringSliceVar (& labels , "labels" , nil , "Comma-separated list of labels to add to the combined PR" )
85
+
86
+ // Label selection flags - singular and plural forms
87
+ rootCmd .Flags ().StringVar (& selectLabel , "label" , "" , "Only include PRs with this specific label" )
88
+ rootCmd .Flags ().StringSliceVar (& selectLabels , "labels" , nil , "Only include PRs with ALL these labels (comma-separated)" )
89
+
90
+ // Label ignoring flags - singular and plural forms
91
+ rootCmd .Flags ().StringVar (& ignoreLabel , "ignore-label" , "" , "Ignore PRs with this specific label" )
92
+ rootCmd .Flags ().StringSliceVar (& ignoreLabels , "ignore-labels" , nil , "Ignore PRs with ANY of these labels (comma-separated)" )
93
+
94
+ // Labels to add to the combined PR
95
+ rootCmd .Flags ().StringSliceVar (& addLabels , "add-labels" , nil , "Comma-separated list of labels to add to the combined PR" )
96
+
97
+ // Other flags
57
98
rootCmd .Flags ().StringSliceVar (& assignees , "assignees" , nil , "Comma-separated list of users to assign to the combined PR" )
58
- rootCmd .Flags ().BoolVar (& mustBeGreen , "require-green " , false , "Only include PRs with passing CI checks" )
99
+ rootCmd .Flags ().BoolVar (& requireCI , "require-ci " , false , "Only include PRs with passing CI checks" )
59
100
rootCmd .Flags ().BoolVar (& mustBeApproved , "require-approved" , false , "Only include PRs that have been approved" )
60
101
rootCmd .Flags ().BoolVar (& autoclose , "autoclose" , false , "Close source PRs when combined PR is merged" )
61
102
rootCmd .Flags ().BoolVar (& updateBranch , "update-branch" , false , "Update the branch of the combined PR if possible" )
62
- rootCmd .Flags ().StringVar (& ignoreLabel , "ignore-label" , "" , "Ignore PRs with this label" )
63
- rootCmd .Flags ().StringVar (& selectLabel , "select-label" , "" , "Only include PRs with this label" )
64
103
rootCmd .Flags ().StringVar (& reposFile , "file" , "" , "File containing repository names, one per line" )
65
- rootCmd .Flags ().IntVar (& minCombine , "min-combine" , 2 , "Minimum number of PRs to combine" )
104
+ rootCmd .Flags ().IntVar (& minimum , "minimum" , 2 , "Minimum number of PRs to combine" )
105
+
106
+ // Add deprecated flags for backward compatibility
107
+ // rootCmd.Flags().IntVar(&minimum, "min-combine", 2, "Minimum number of PRs to combine (deprecated, use --minimum)")
108
+
109
+ // Mark deprecated flags
110
+ // rootCmd.Flags().MarkDeprecated("min-combine", "use --minimum instead")
66
111
67
112
return rootCmd
68
113
}
@@ -139,7 +184,8 @@ func validateInputs(args []string) error {
139
184
140
185
// Warn if no filtering options are provided at all
141
186
if branchPrefix == "" && branchSuffix == "" && branchRegex == "" &&
142
- ignoreLabel == "" && selectLabel == "" && ! mustBeGreen && ! mustBeApproved {
187
+ ignoreLabel == "" && selectLabel == "" && len (selectLabels ) == 0 &&
188
+ ! requireCI && ! mustBeApproved {
143
189
Logger .Warn ("No filtering options specified. This will attempt to combine ALL open pull requests." )
144
190
}
145
191
@@ -264,8 +310,8 @@ func executeCombineCommand(ctx context.Context, spinner *Spinner, repos []string
264
310
}
265
311
266
312
// Check if we have enough PRs to combine
267
- if len (matchedPRs ) < minCombine {
268
- Logger .Debug ("Not enough PRs match criteria" , "repo" , repo , "matched" , len (matchedPRs ), "required" , minCombine )
313
+ if len (matchedPRs ) < minimum {
314
+ Logger .Debug ("Not enough PRs match criteria" , "repo" , repo , "matched" , len (matchedPRs ), "required" , minimum )
269
315
continue
270
316
}
271
317
@@ -332,11 +378,12 @@ func branchMatchesCriteria(branch string) bool {
332
378
// labelsMatchCriteria checks if PR labels match the label filtering criteria
333
379
func labelsMatchCriteria (prLabels []struct { Name string }) bool {
334
380
// If no label filters are specified, all PRs pass this check
335
- if ignoreLabel == "" && selectLabel == "" {
381
+ if ignoreLabel == "" && len (ignoreLabels ) == 0 &&
382
+ selectLabel == "" && len (selectLabels ) == 0 {
336
383
return true
337
384
}
338
385
339
- // Check for ignore label
386
+ // Check for ignore label (singular)
340
387
if ignoreLabel != "" {
341
388
for _ , label := range prLabels {
342
389
if label .Name == ignoreLabel {
@@ -345,7 +392,18 @@ func labelsMatchCriteria(prLabels []struct{ Name string }) bool {
345
392
}
346
393
}
347
394
348
- // Check for select label
395
+ // Check for ignore labels (plural)
396
+ if len (ignoreLabels ) > 0 {
397
+ for _ , ignoreL := range ignoreLabels {
398
+ for _ , prLabel := range prLabels {
399
+ if prLabel .Name == ignoreL {
400
+ return false
401
+ }
402
+ }
403
+ }
404
+ }
405
+
406
+ // Check for select label (singular)
349
407
if selectLabel != "" {
350
408
found := false
351
409
for _ , label := range prLabels {
@@ -359,5 +417,21 @@ func labelsMatchCriteria(prLabels []struct{ Name string }) bool {
359
417
}
360
418
}
361
419
420
+ // Check for select labels (plural)
421
+ if len (selectLabels ) > 0 {
422
+ for _ , requiredLabel := range selectLabels {
423
+ found := false
424
+ for _ , prLabel := range prLabels {
425
+ if prLabel .Name == requiredLabel {
426
+ found = true
427
+ break
428
+ }
429
+ }
430
+ if ! found {
431
+ return false
432
+ }
433
+ }
434
+ }
435
+
362
436
return true
363
437
}
0 commit comments