repos / git-pr

a self-hosted git collaboration server
git clone https://github.com/picosh/git-pr.git

commit
29141e6
parent
47d3799
author
Eric Bower
date
2024-06-22 23:27:56 -0400 EDT
feat: print patch request sub slice
2 files changed,  +133, -17
M cli.go
M cli.go
+126, -11
  1@@ -3,8 +3,8 @@ package git
  2 import (
  3 	"fmt"
  4 	"io"
  5-	"path/filepath"
  6 	"strconv"
  7+	"strings"
  8 	"text/tabwriter"
  9 	"time"
 10 
 11@@ -23,6 +23,70 @@ func getPrID(str string) (int64, error) {
 12 	return prID, err
 13 }
 14 
 15+type Ranger struct {
 16+	Left  int
 17+	Right int
 18+}
 19+
 20+func parseRange(rnge string, sliceLen int) (*Ranger, error) {
 21+	items := strings.Split(rnge, ":")
 22+	left := 0
 23+	var err error
 24+	if items[0] != "" {
 25+		left, err = strconv.Atoi(items[0])
 26+		if err != nil {
 27+			return nil, fmt.Errorf("first value before `:` must provide number")
 28+		}
 29+	}
 30+
 31+	if left < 0 {
 32+		return nil, fmt.Errorf("first value must be >= 0")
 33+	}
 34+
 35+	if left >= sliceLen {
 36+		return nil, fmt.Errorf("first value must be less than number of patches")
 37+	}
 38+
 39+	if len(items) == 1 {
 40+		return &Ranger{
 41+			Left:  left,
 42+			Right: left,
 43+		}, nil
 44+	}
 45+
 46+	if items[1] == "" {
 47+		return &Ranger{Left: left, Right: sliceLen - 1}, nil
 48+	}
 49+
 50+	right, err := strconv.Atoi(items[1])
 51+	if err != nil {
 52+		return nil, fmt.Errorf("second value after `:` must provide number")
 53+	}
 54+
 55+	if left > right {
 56+		return nil, fmt.Errorf("second value must be greater than first value")
 57+	}
 58+
 59+	if right >= sliceLen {
 60+		return nil, fmt.Errorf("second value must be less than number of patches")
 61+	}
 62+
 63+	return &Ranger{
 64+		Left:  left,
 65+		Right: right,
 66+	}, nil
 67+}
 68+
 69+func filterPatches(ranger *Ranger, patches []*Patch) []*Patch {
 70+	opatches := []*Patch{}
 71+	if ranger.Left == ranger.Right {
 72+		opatches = []*Patch{patches[ranger.Left]}
 73+	} else {
 74+		opatches = patches[ranger.Left:ranger.Right]
 75+	}
 76+	return opatches
 77+}
 78+
 79 func NewCli(sesh ssh.Session, be *Backend, pr GitPatchRequest) *cli.App {
 80 	desc := `Patch requests (PR) are the simplest way to submit, review, and accept changes to your git repository.
 81 Here's how it works:
 82@@ -45,7 +109,7 @@ Here's how it works:
 83 	app := &cli.App{
 84 		Name:        "ssh",
 85 		Description: desc,
 86-		Usage:       "Collaborate with external contributors to your project",
 87+		Usage:       "Collaborate with contributors for your git project",
 88 		Writer:      sesh,
 89 		ErrWriter:   sesh,
 90 		Commands: []*cli.Command{
 91@@ -76,13 +140,12 @@ Here's how it works:
 92 						return err
 93 					}
 94 					writer := NewTabWriter(sesh)
 95-					fmt.Fprintln(writer, "ID\tDir")
 96+					fmt.Fprintln(writer, "ID")
 97 					for _, repo := range repos {
 98 						fmt.Fprintf(
 99 							writer,
100-							"%s\t%s\n",
101+							"%s\n",
102 							utils.SanitizeRepo(repo.ID),
103-							filepath.Join(be.ReposDir(), repo.ID),
104 						)
105 					}
106 					writer.Flush()
107@@ -91,7 +154,7 @@ Here's how it works:
108 			},
109 			{
110 				Name:  "logs",
111-				Usage: "List event logs by on filters",
112+				Usage: "List event logs with filters",
113 				Args:  true,
114 				Flags: []cli.Flag{
115 					&cli.Int64Flag{
116@@ -232,6 +295,13 @@ Here's how it works:
117 						Usage:     "Print the patches for a PR",
118 						Args:      true,
119 						ArgsUsage: "[prID]",
120+						Flags: []cli.Flag{
121+							&cli.StringFlag{
122+								Name:    "filter",
123+								Usage:   "Only print patches in sequence range (x:y) (x:) (:y)",
124+								Aliases: []string{"f"},
125+							},
126+						},
127 						Action: func(cCtx *cli.Context) error {
128 							prID, err := getPrID(cCtx.Args().First())
129 							if err != nil {
130@@ -248,7 +318,17 @@ Here's how it works:
131 								return nil
132 							}
133 
134-							for idx, patch := range patches {
135+							rnge := cCtx.String("filter")
136+							opatches := patches
137+							if rnge != "" {
138+								ranger, err := parseRange(rnge, len(patches))
139+								if err != nil {
140+									return err
141+								}
142+								opatches = filterPatches(ranger, patches)
143+							}
144+
145+							for idx, patch := range opatches {
146 								wish.Println(sesh, patch.RawText)
147 								if idx < len(patches)-1 {
148 									wish.Printf(sesh, "\n\n\n")
149@@ -263,6 +343,13 @@ Here's how it works:
150 						Usage:     "Print PR with diff stats",
151 						Args:      true,
152 						ArgsUsage: "[prID]",
153+						Flags: []cli.Flag{
154+							&cli.StringFlag{
155+								Name:    "filter",
156+								Usage:   "Only print patches in sequence range (x:y) (x:) (:y)",
157+								Aliases: []string{"f"},
158+							},
159+						},
160 						Action: func(cCtx *cli.Context) error {
161 							prID, err := getPrID(cCtx.Args().First())
162 							if err != nil {
163@@ -289,7 +376,17 @@ Here's how it works:
164 								return err
165 							}
166 
167-							for _, patch := range patches {
168+							rnge := cCtx.String("filter")
169+							opatches := patches
170+							if rnge != "" {
171+								ranger, err := parseRange(rnge, len(patches))
172+								if err != nil {
173+									return err
174+								}
175+								opatches = filterPatches(ranger, patches)
176+							}
177+
178+							for _, patch := range opatches {
179 								reviewTxt := ""
180 								if patch.Review {
181 									reviewTxt = "[review]"
182@@ -316,6 +413,13 @@ Here's how it works:
183 						Usage:     "List patches in PRs",
184 						Args:      true,
185 						ArgsUsage: "[prID]",
186+						Flags: []cli.Flag{
187+							&cli.StringFlag{
188+								Name:    "filter",
189+								Usage:   "Only print patches in sequence range (x:y) (x:) (:y)",
190+								Aliases: []string{"f"},
191+							},
192+						},
193 						Action: func(cCtx *cli.Context) error {
194 							prID, err := getPrID(cCtx.Args().First())
195 							if err != nil {
196@@ -341,16 +445,27 @@ Here's how it works:
197 								return err
198 							}
199 
200+							rnge := cCtx.String("filter")
201+							opatches := patches
202+							if rnge != "" {
203+								ranger, err := parseRange(rnge, len(patches))
204+								if err != nil {
205+									return err
206+								}
207+								opatches = filterPatches(ranger, patches)
208+							}
209+
210 							w := NewTabWriter(sesh)
211-							fmt.Fprintln(w, "Title\tStatus\tCommit\tAuthor\tDate")
212-							for _, patch := range patches {
213+							fmt.Fprintln(w, "Idx\tTitle\tStatus\tCommit\tAuthor\tDate")
214+							for idx, patch := range opatches {
215 								reviewTxt := ""
216 								if patch.Review {
217 									reviewTxt = "[review]"
218 								}
219 								fmt.Fprintf(
220 									w,
221-									"%s\t%s\t%s\t%s <%s>\t%s\n",
222+									"%d\t%s\t%s\t%s\t%s <%s>\t%s\n",
223+									idx,
224 									patch.Title,
225 									reviewTxt,
226 									truncateSha(patch.CommitSha),
M tmpl/pr-detail.html
+7, -6
 1@@ -13,15 +13,16 @@
 2 
 3 <main class="group">
 4   <div class="group">
 5-    {{range .Patches}}
 6+    {{range $idx, $val := .Patches}}
 7     <div class="box">
 8-      <h2 class="text-lg m-0 p-0">
 9-        {{if .Review}}<code>REVIEW</code>{{end}}
10-        <a href="#{{.Url}}">{{.Title}}</a>
11+      <h2 class="text-lg m-0 p-0 mb">
12+        <code>#{{$idx}}</code>
13+        {{if $val.Review}}<code>REVIEW</code>{{end}}
14+        <a href="#{{$val.Url}}">{{$val.Title}}</a>
15       </h2>
16       <div class="group-h text-sm">
17-        <code>{{.AuthorName}} &lt;{{.AuthorEmail}}&gt;</code>
18-        <date>{{.AuthorDate}}</date>
19+        <code>{{$val.AuthorName}} &lt;{{$val.AuthorEmail}}&gt;</code>
20+        <date>{{$val.AuthorDate}}</date>
21       </div>
22     </div>
23     {{else}}