repos / git-pr

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

commit
f40e6cb
parent
55c06b3
author
Eric Bower
date
2026-02-25 21:00:58 -0500 EST
feat: ability to delete repo
5 files changed,  +82, -1
M cli.go
M pr.go
M CHANGELOG.md
+4, -0
 1@@ -8,6 +8,10 @@ Use spec: https://common-changelog.org/
 2 
 3 ## v2026-02-25
 4 
 5+### Added
 6+
 7+- Ability to delete repo `ssh pr.pico.sh repo rm {name}`, must provide `--write` to persist
 8+
 9 ### Changed
10 
11 - Replaced `--comment` flag which was a string into a bool and now require comment to be provided by stdin for commands `accept`, `close`, and `reopen`
M __snapshots__/e2e_test.snap
+14, -0
 1@@ -52,3 +52,17 @@ admin/ai 9    ps-13      pr_created
 2 admin/ai 9               pr_status_changed         {"status":"accepted","comment":"nice work"}
 3 
 4 ---
 5+
 6+[TestE2E - 5]
 7+ID RepoID           Name                       Status     Patchsets User        Date
 8+10 contributor/bin  feat: lets build an rnn    [open]     1         contributor 
 9+9  admin/ai         feat: lets build an rnn    [accepted] 1         contributor 
10+8  admin/ai         feat: lets build an rnn    [accepted] 2         contributor 
11+6  contributor/test Closed patch with review   [closed]   2         contributor 
12+5  contributor/test Accepted patch with review [accepted] 2         contributor 
13+4  contributor/test Reviewed patch             [open]     2         contributor 
14+3  contributor/test Closed patch (contributor) [closed]   1         contributor 
15+2  contributor/test Closed patch (admin)       [closed]   1         contributor 
16+1  admin/test       Accepted patch             [accepted] 1         contributor 
17+
18+---
M cli.go
+46, -1
 1@@ -388,7 +388,52 @@ To get started, submit a new patch request:
 2 								}
 3 							}
 4 
 5-							sesh.Printf("repo created: %s/%s", user.Name, repo.Name)
 6+							sesh.Printf("repo created: %s/%s\n", user.Name, repo.Name)
 7+							return nil
 8+						},
 9+					},
10+					{
11+						Name:      "rm",
12+						Usage:     "Delete repo and associated patch requests",
13+						Args:      true,
14+						ArgsUsage: "[repoName]",
15+						Flags: []cli.Flag{
16+							&cli.BoolFlag{
17+								Name:  "write",
18+								Usage: "Are you sure you want to delete the repo and all patch requests?",
19+							},
20+						},
21+						Action: func(cCtx *cli.Context) error {
22+							user, err := pr.GetUserByPubkey(pubkey)
23+							if err != nil {
24+								return errNotExist(be.Cfg.Host, pubkey)
25+							}
26+
27+							args := cCtx.Args()
28+							if !args.Present() {
29+								return fmt.Errorf("need repo name argument")
30+							}
31+							rawRepoNs := args.First()
32+							_, repoName := be.SplitRepoNs(rawRepoNs)
33+							repo, _ := pr.GetRepoByName(user, repoName)
34+							if repo == nil {
35+								return fmt.Errorf("repo does not exist: %s/%s", user.Name, repoName)
36+							}
37+							err = be.CanCreateRepo(repo, user)
38+							if err != nil {
39+								return err
40+							}
41+
42+							if cCtx.Bool("write") {
43+								err = pr.DeleteRepo(user, repoName)
44+								if err != nil {
45+									return err
46+								}
47+							} else {
48+								sesh.Println("Must provide `--write` flag to persist changes")
49+							}
50+
51+							sesh.Printf("repo deleted: %s/%s\n", user.Name, repo.Name)
52 							return nil
53 						},
54 					},
M e2e_test.go
+8, -0
 1@@ -137,6 +137,14 @@ func testMultiTenantE2E(t *testing.T) {
 2 	actual, err = suite.userKey.Cmd(nil, "logs --repo admin/ai")
 3 	bail(err)
 4 	snaps.MatchSnapshot(t, actual)
 5+
 6+	t.Log("Delete repo")
 7+	suite.userKey.MustCmd(nil, "repo rm --write ai")
 8+
 9+	t.Log("Snapshot test ls command with ai prs removed")
10+	actual, err = suite.userKey.Cmd(nil, "pr ls")
11+	bail(err)
12+	snaps.MatchSnapshot(t, actual)
13 }
14 
15 type TestSuite struct {
M pr.go
+10, -0
 1@@ -31,6 +31,7 @@ type GitPatchRequest interface {
 2 	GetRepoByID(repoID int64) (*Repo, error)
 3 	GetRepoByName(user *User, repoName string) (*Repo, error)
 4 	CreateRepo(user *User, repoName string) (*Repo, error)
 5+	DeleteRepo(user *User, repoName string) error
 6 	RegisterUser(pubkey, name string) (*User, error)
 7 	IsBanned(pubkey, ipAddress string) error
 8 	SubmitPatchRequest(repoID int64, userID int64, patchset io.Reader) (*PatchRequest, error)
 9@@ -126,6 +127,15 @@ func (pr PrCmd) CreateRepo(user *User, repoName string) (*Repo, error) {
10 	return pr.GetRepoByID(repoID)
11 }
12 
13+func (pr PrCmd) DeleteRepo(user *User, repoName string) error {
14+	_, err := pr.Backend.DB.Exec(
15+		"DELETE FROM repos WHERE user_id=? AND name=?",
16+		user.ID,
17+		repoName,
18+	)
19+	return err
20+}
21+
22 func (pr PrCmd) GetRepoByID(repoID int64) (*Repo, error) {
23 	var repo Repo
24 	err := pr.Backend.DB.Get(&repo, "SELECT * FROM repos WHERE id=?", repoID)