+6
-3
appview/pages/templates/repo/pulls/fragments/pullCompareBranches.html
+6
-3
appview/pages/templates/repo/pulls/fragments/pullCompareBranches.html
···
1
1
{{ define "repo/pulls/fragments/pullCompareBranches" }}
2
2
<div id="patch-upload">
3
-
<label for="targetBranch" class="dark:text-white"
4
-
>select a branch</label
5
-
>
3
+
<label for="targetBranch" class="dark:text-white">select a branch</label>
6
4
<div class="flex flex-wrap gap-2 items-center">
7
5
<select
8
6
name="sourceBranch"
···
26
24
{{ end }}
27
25
</select>
28
26
</div>
27
+
</div>
28
+
29
+
<div class="flex items-center gap-2">
30
+
<input type="checkbox" id="isStacked" name="isStacked" value="on">
31
+
<label for="isStacked" class="my-0 py-0 normal-case font-normal">Submit as stacked PRs</label>
29
32
</div>
30
33
31
34
<p class="mt-4">
+7
-1
appview/pages/templates/repo/pulls/fragments/pullCompareForks.html
+7
-1
appview/pages/templates/repo/pulls/fragments/pullCompareForks.html
···
3
3
<label for="forkSelect" class="dark:text-white"
4
4
>select a fork to compare</label
5
5
>
6
-
<div class="flex flex-wrap gap-4 items-center mb-4">
6
+
<div class="flex flex-wrap gap-4 items-center">
7
7
<div class="flex flex-wrap gap-2 items-center">
8
8
<select
9
9
id="forkSelect"
···
39
39
</div>
40
40
</div>
41
41
</div>
42
+
43
+
<div class="flex items-center gap-2">
44
+
<input type="checkbox" id="isStacked" name="isStacked" value="on">
45
+
<label for="isStacked" class="my-0 py-0 normal-case font-normal">Submit as stacked PRs</label>
46
+
</div>
47
+
42
48
<p class="mt-4">
43
49
Title and description are optional; if left out, they will be extracted
44
50
from the first commit.
+59
-61
appview/pages/templates/repo/pulls/new.html
+59
-61
appview/pages/templates/repo/pulls/new.html
···
3
3
{{ define "repoContent" }}
4
4
<form
5
5
hx-post="/{{ .RepoInfo.FullName }}/pulls/new"
6
-
class="mt-6 space-y-6"
7
6
hx-indicator="#create-pull-spinner"
8
7
hx-swap="none"
9
8
>
10
-
<div class="flex flex-col gap-4">
11
-
<label>configure your pull request</label>
12
-
13
-
<p>First, choose a target branch on {{ .RepoInfo.FullName }}.</p>
14
-
<div class="pb-2">
9
+
<div class="flex flex-col gap-6">
10
+
<div class="flex gap-2 items-center">
11
+
<p>First, choose a target branch on {{ .RepoInfo.FullName }}:</p>
12
+
<div>
15
13
<select
16
-
required
17
-
name="targetBranch"
18
-
class="p-1 border border-gray-200 bg-white dark:bg-gray-700 dark:text-white dark:border-gray-600"
19
-
>
20
-
<option disabled selected>target branch</option>
21
-
{{ range .Branches }}
22
-
<option value="{{ .Reference.Name }}" class="py-1" {{if .IsDefault}}selected{{end}}>
23
-
{{ .Reference.Name }}
24
-
</option>
25
-
{{ end }}
14
+
required
15
+
name="targetBranch"
16
+
class="p-1 border border-gray-200 bg-white dark:bg-gray-700 dark:text-white dark:border-gray-600"
17
+
>
18
+
<option disabled selected>target branch</option>
19
+
{{ range .Branches }}
20
+
<option value="{{ .Reference.Name }}" class="py-1" {{if .IsDefault}}selected{{end}}>
21
+
{{ .Reference.Name }}
22
+
</option>
23
+
{{ end }}
26
24
</select>
25
+
</div>
27
26
</div>
28
27
29
-
<p>Next, choose a pull strategy.</p>
30
-
<nav class="flex space-x-4 items-end">
31
-
<button
32
-
type="button"
33
-
class="px-3 py-2 pb-2 btn"
34
-
hx-get="/{{ .RepoInfo.FullName }}/pulls/new/patch-upload"
35
-
hx-target="#patch-strategy"
36
-
hx-swap="innerHTML"
37
-
>
38
-
paste patch
39
-
</button>
40
-
41
-
{{ if .RepoInfo.Roles.IsPushAllowed }}
42
-
<span class="text-sm text-gray-500 dark:text-gray-400 pb-2">
43
-
or
44
-
</span>
45
-
<button
46
-
type="button"
47
-
class="px-3 py-2 pb-2 btn"
48
-
hx-get="/{{ .RepoInfo.FullName }}/pulls/new/compare-branches"
49
-
hx-target="#patch-strategy"
50
-
hx-swap="innerHTML"
51
-
>
52
-
compare branches
53
-
</button>
54
-
{{ end }}
55
-
28
+
<div class="flex flex-col gap-2">
29
+
<p>Next, choose a pull strategy.</p>
30
+
<nav class="flex space-x-4 items-center">
31
+
<button
32
+
type="button"
33
+
class="btn"
34
+
hx-get="/{{ .RepoInfo.FullName }}/pulls/new/patch-upload"
35
+
hx-target="#patch-strategy"
36
+
hx-swap="innerHTML"
37
+
>
38
+
paste patch
39
+
</button>
56
40
57
-
<span class="text-sm text-gray-500 dark:text-gray-400 pb-2">
58
-
or
59
-
</span>
60
-
<button
61
-
type="button"
62
-
class="px-3 py-2 pb-2 btn"
63
-
hx-get="/{{ .RepoInfo.FullName }}/pulls/new/compare-forks"
64
-
hx-target="#patch-strategy"
65
-
hx-swap="innerHTML"
66
-
>
67
-
compare forks
68
-
</button>
69
-
</nav>
41
+
{{ if .RepoInfo.Roles.IsPushAllowed }}
42
+
<span class="text-sm text-gray-500 dark:text-gray-400">
43
+
or
44
+
</span>
45
+
<button
46
+
type="button"
47
+
class="btn"
48
+
hx-get="/{{ .RepoInfo.FullName }}/pulls/new/compare-branches"
49
+
hx-target="#patch-strategy"
50
+
hx-swap="innerHTML"
51
+
>
52
+
compare branches
53
+
</button>
54
+
{{ end }}
70
55
71
-
<section id="patch-strategy">
72
-
{{ template "repo/pulls/fragments/pullPatchUpload" . }}
73
-
</section>
74
56
75
-
<p id="patch-preview"></p>
57
+
<span class="text-sm text-gray-500 dark:text-gray-400">
58
+
or
59
+
</span>
60
+
<button
61
+
type="button"
62
+
class="btn"
63
+
hx-get="/{{ .RepoInfo.FullName }}/pulls/new/compare-forks"
64
+
hx-target="#patch-strategy"
65
+
hx-swap="innerHTML"
66
+
>
67
+
compare forks
68
+
</button>
69
+
</nav>
70
+
<section id="patch-strategy" class="flex flex-col gap-2">
71
+
{{ template "repo/pulls/fragments/pullPatchUpload" . }}
72
+
</section>
76
73
77
-
<div id="patch-error" class="error dark:text-red-300"></div>
74
+
<div id="patch-error" class="error dark:text-red-300"></div>
75
+
</div>
78
76
79
77
<div>
80
78
<label for="title" class="dark:text-white">write a title</label>
+24
-9
appview/state/pull.go
+24
-9
appview/state/pull.go
···
633
633
isBranchBased := isPushAllowed && sourceBranch != "" && fromFork == ""
634
634
isForkBased := fromFork != "" && sourceBranch != ""
635
635
isPatchBased := patch != "" && !isBranchBased && !isForkBased
636
+
isStacked := r.FormValue("isStacked") == "on"
636
637
637
638
if isPatchBased && !patchutil.IsFormatPatch(patch) {
638
639
if title == "" {
···
678
679
s.pages.Notice(w, "pull", "This knot doesn't support branch-based pull requests. Try another way?")
679
680
return
680
681
}
681
-
s.handleBranchBasedPull(w, r, f, user, title, body, targetBranch, sourceBranch)
682
+
s.handleBranchBasedPull(w, r, f, user, title, body, targetBranch, sourceBranch, isStacked)
682
683
} else if isForkBased {
683
684
if !caps.PullRequests.ForkSubmissions {
684
685
s.pages.Notice(w, "pull", "This knot doesn't support fork-based pull requests. Try another way?")
685
686
return
686
687
}
687
-
s.handleForkBasedPull(w, r, f, user, fromFork, title, body, targetBranch, sourceBranch)
688
+
s.handleForkBasedPull(w, r, f, user, fromFork, title, body, targetBranch, sourceBranch, isStacked)
688
689
} else if isPatchBased {
689
690
if !caps.PullRequests.PatchSubmissions {
690
691
s.pages.Notice(w, "pull", "This knot doesn't support patch-based pull requests. Send your patch over email.")
691
692
return
692
693
}
693
-
s.handlePatchBasedPull(w, r, f, user, title, body, targetBranch, patch)
694
+
s.handlePatchBasedPull(w, r, f, user, title, body, targetBranch, patch, isStacked)
694
695
}
695
696
return
696
697
}
697
698
}
698
699
699
-
func (s *State) handleBranchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *oauth.User, title, body, targetBranch, sourceBranch string) {
700
+
func (s *State) handleBranchBasedPull(
701
+
w http.ResponseWriter,
702
+
r *http.Request,
703
+
f *FullyResolvedRepo,
704
+
user *oauth.User,
705
+
title,
706
+
body,
707
+
targetBranch,
708
+
sourceBranch string,
709
+
isStacked bool,
710
+
) {
700
711
pullSource := &db.PullSource{
701
712
Branch: sourceBranch,
702
713
}
···
727
738
return
728
739
}
729
740
730
-
s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, sourceRev, pullSource, recordPullSource)
741
+
s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, sourceRev, pullSource, recordPullSource, isStacked)
731
742
}
732
743
733
-
func (s *State) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *oauth.User, title, body, targetBranch, patch string) {
744
+
func (s *State) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *oauth.User, title, body, targetBranch, patch string, isStacked bool) {
734
745
if !patchutil.IsPatchValid(patch) {
735
746
s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.")
736
747
return
737
748
}
738
749
739
-
s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, "", nil, nil)
750
+
s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, "", nil, nil, isStacked)
740
751
}
741
752
742
-
func (s *State) handleForkBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *oauth.User, forkRepo string, title, body, targetBranch, sourceBranch string) {
753
+
func (s *State) handleForkBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *oauth.User, forkRepo string, title, body, targetBranch, sourceBranch string, isStacked bool) {
743
754
fork, err := db.GetForkByDid(s.db, user.Did, forkRepo)
744
755
if errors.Is(err, sql.ErrNoRows) {
745
756
s.pages.Notice(w, "pull", "No such fork.")
···
816
827
s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, sourceRev, &db.PullSource{
817
828
Branch: sourceBranch,
818
829
RepoAt: &forkAtUri,
819
-
}, &tangled.RepoPull_Source{Branch: sourceBranch, Repo: &fork.AtUri})
830
+
}, &tangled.RepoPull_Source{Branch: sourceBranch, Repo: &fork.AtUri}, isStacked)
820
831
}
821
832
822
833
func (s *State) createPullRequest(
···
829
840
sourceRev string,
830
841
pullSource *db.PullSource,
831
842
recordPullSource *tangled.RepoPull_Source,
843
+
isStacked bool,
832
844
) {
845
+
if isStacked {
846
+
}
847
+
833
848
tx, err := s.db.BeginTx(r.Context(), nil)
834
849
if err != nil {
835
850
log.Println("failed to start tx")