diff --git a/models/issues/pull.go b/models/issues/pull.go index 333516ae908..4557488167c 100644 --- a/models/issues/pull.go +++ b/models/issues/pull.go @@ -407,6 +407,21 @@ func (pr *PullRequest) GetGitHeadRefName() string { // TODO: make it return RefN return git.RefNameFromPullIndex(pr.Index).String() } +func (pr *PullRequest) GetInstructionsCliArgs() (ret struct { + BaseBranchArg string + HeadBranchArg string + LocalBranchArg string +}, +) { + ret.BaseBranchArg = util.ShellEscape(pr.BaseBranch) + ret.HeadBranchArg = util.ShellEscape(pr.HeadBranch) + ret.LocalBranchArg = ret.HeadBranchArg + if pr.HeadRepo != nil && pr.HeadRepoID != pr.BaseRepoID { + ret.LocalBranchArg = util.ShellEscape(pr.HeadRepo.OwnerName) + "-" + ret.HeadBranchArg + } + return ret +} + // GetReviewCommentsCount returns the number of review comments made on the diff of a PR review (not including comments on commits or issues in a PR) func (pr *PullRequest) GetReviewCommentsCount(ctx context.Context) int { opts := FindCommentsOptions{ diff --git a/modules/util/shellquote.go b/modules/util/shellquote.go index 434dc4236e0..89933e58c28 100644 --- a/modules/util/shellquote.go +++ b/modules/util/shellquote.go @@ -36,6 +36,7 @@ import "strings" const ( tildePrefix = '~' + commentPrefix = '#' needsEscape = " \t\n|&;()<>${}[]*?!\"'`\\" needsSingleQuote = "!\n" ) @@ -74,7 +75,7 @@ func ShellEscape(toEscape string) string { } // Now for simplicity we'll look at the rest of the string - if !strings.ContainsAny(toEscape[start:], needsEscape) { + if !strings.ContainsAny(toEscape[start:], needsEscape) && toEscape[0] != commentPrefix { return toEscape } diff --git a/modules/util/shellquote_test.go b/modules/util/shellquote_test.go index 4ef5ce69809..6387e08912b 100644 --- a/modules/util/shellquote_test.go +++ b/modules/util/shellquote_test.go @@ -75,6 +75,10 @@ func TestShellEscape(t *testing.T) { "Double quote and escape `...", "~/gitea`", "~/\"gitea\\`\"", + }, { + "Double quote leading #", + "#123", + `"#123"`, }, { "Double quotes can handle a number of things without having to escape them but not everything ...", "~/ ${gitea} `gitea` [gitea] (gitea) \"gitea\" \\gitea\\ 'gitea'", diff --git a/services/repository/gitgraph/graph.go b/services/repository/gitgraph/graph.go index cc56cf5544f..4670b490687 100644 --- a/services/repository/gitgraph/graph.go +++ b/services/repository/gitgraph/graph.go @@ -7,20 +7,26 @@ import ( "bufio" "bytes" "context" + "strings" "gitea.dev/modules/git" "gitea.dev/modules/git/gitcmd" "gitea.dev/modules/setting" ) +const gitLogGraphFormatSep = "^" // disallowed char in git ref names + // GetCommitGraph return a list of commit (GraphItems) from all branches func GetCommitGraph(ctx context.Context, gitRepo *git.Repository, page, maxAllowedColors int, hidePRRefs bool, refs, files []string) (*Graph, error) { - format := "DATA:%D|%H|%ad|%h|%s" - - if page == 0 { - page = 1 - } + format := "DATA:" + strings.Join([]string{ + "%D", // ref names without the " (", ")" wrapping. + "%H", // commit hash + "%ad", // author date (format respects --date= option) + "%h", // abbreviated commit hash + "%s", // subject + }, gitLogGraphFormatSep) + page = max(page, 1) graphCmd := gitcmd.NewCommand("log", "--graph", "--date-order", "--decorate=full") if hidePRRefs { @@ -31,7 +37,7 @@ func GetCommitGraph(ctx context.Context, gitRepo *git.Repository, page, maxAllow graphCmd.AddArguments("--tags", "--branches") } - graphCmd.AddArguments("-C", "-M", "--date=iso-strict"). + graphCmd.AddArguments("--find-copies", "--find-renames", "--date=iso-strict"). AddOptionFormat("-n %d", setting.UI.GraphMaxCommitNum*page). AddOptionFormat("--pretty=format:%s", format) diff --git a/services/repository/gitgraph/graph_models.go b/services/repository/gitgraph/graph_models.go index dd98f010a12..3a9e3962258 100644 --- a/services/repository/gitgraph/graph_models.go +++ b/services/repository/gitgraph/graph_models.go @@ -216,7 +216,7 @@ func parseGitTime(timeStr string) time.Time { // NewCommit creates a new commit from a provided line func NewCommit(row, column int, line []byte) (*Commit, error) { - data := bytes.SplitN(line, []byte("|"), 5) + data := bytes.SplitN(line, []byte(gitLogGraphFormatSep), 5) if len(data) < 5 { return nil, fmt.Errorf("malformed data section on line %d with commit: %s", row, string(line)) } diff --git a/services/repository/gitgraph/graph_test.go b/services/repository/gitgraph/graph_test.go index 7886ae203ca..5e16432d486 100644 --- a/services/repository/gitgraph/graph_test.go +++ b/services/repository/gitgraph/graph_test.go @@ -35,7 +35,7 @@ func BenchmarkGetCommitGraph(b *testing.B) { } func BenchmarkParseCommitString(b *testing.B) { - testString := "* DATA:|4e61bacab44e9b4730e44a6615d04098dd3a8eaf|2016-12-20 21:10:41 +0100|4e61bac|Add route for graph" + testString := "* DATA:^4e61bacab44e9b4730e44a6615d04098dd3a8eaf^2016-12-20 21:10:41 +0100^4e61bac^Add route for graph" parser := &Parser{} parser.Reset() @@ -224,14 +224,14 @@ func TestParseGlyphs(t *testing.T) { } func TestCommitStringParsing(t *testing.T) { - dataFirstPart := "* DATA:|4e61bacab44e9b4730e44a6615d04098dd3a8eaf|2016-12-20 21:10:41 +0100|4e61bac|" + dataFirstPart := "* DATA:^4e61bacab44e9b4730e44a6615d04098dd3a8eaf^2016-12-20 21:10:41 +0100^4e61bac^" tests := []struct { shouldPass bool testName string commitMessage string }{ {true, "normal", "not a fancy message"}, - {true, "extra pipe", "An extra pipe: |"}, + {true, "extra sep", "An extra sep"}, {true, "extra 'Data:'", "DATA: might be trouble"}, } diff --git a/templates/repo/issue/view_content/pull_merge_instruction.tmpl b/templates/repo/issue/view_content/pull_merge_instruction.tmpl index 4cb20e90c6c..15cd892a11c 100644 --- a/templates/repo/issue/view_content/pull_merge_instruction.tmpl +++ b/templates/repo/issue/view_content/pull_merge_instruction.tmpl @@ -9,18 +9,15 @@

{{ctx.Locale.Tr "repo.pulls.cmd_instruction_checkout_title"}}

{{ctx.Locale.Tr "repo.pulls.cmd_instruction_checkout_desc"}} - {{$localBranch := $pull.HeadBranch}} - {{if ne $pull.HeadRepo.ID $pull.BaseRepo.ID}} - {{$localBranch = print $pull.HeadRepo.OwnerName "-" $pull.HeadBranch}} - {{end}} + {{$args := $pull.GetInstructionsCliArgs}}
{{$gitRemoteName := ctx.RootData.SystemConfig.Repository.GitGuideRemoteName.Value ctx}} {{if eq $pull.Flow 0}} -
git fetch -u {{if ne $pull.HeadRepo.ID $pull.BaseRepo.ID}}{{$pull.HeadRepo.HTMLURL ctx}}{{else}}{{$gitRemoteName}}{{end}} {{$pull.HeadBranch}}:{{$localBranch}}
+
git fetch -u {{if ne $pull.HeadRepo.ID $pull.BaseRepo.ID}}{{$pull.HeadRepo.HTMLURL ctx}}{{else}}{{$gitRemoteName}}{{end}} {{$args.HeadBranchArg}}:{{$args.LocalBranchArg}}
{{else}} -
git fetch -u {{$gitRemoteName}} {{$pull.GetGitHeadRefName}}:{{$localBranch}}
+
git fetch -u {{$gitRemoteName}} {{$pull.GetGitHeadRefName}}:{{$args.LocalBranchArg}}
{{end}} -
git checkout {{$localBranch}}
+
git checkout {{$args.LocalBranchArg}}
{{if $data.ShowMergeInstructions}}
@@ -32,32 +29,32 @@
-
git checkout {{$pull.BaseBranch}}
-
git merge --no-ff {{$localBranch}}
+
git checkout {{$args.BaseBranchArg}}
+
git merge --no-ff {{$args.LocalBranchArg}}
-
git checkout {{$pull.BaseBranch}}
-
git merge --ff-only {{$localBranch}}
+
git checkout {{$args.BaseBranchArg}}
+
git merge --ff-only {{$args.LocalBranchArg}}
-
git checkout {{$localBranch}}
-
git rebase {{$pull.BaseBranch}}
-
git checkout {{$pull.BaseBranch}}
-
git merge --no-ff {{$localBranch}}
+
git checkout {{$args.LocalBranchArg}}
+
git rebase {{$args.BaseBranchArg}}
+
git checkout {{$args.BaseBranchArg}}
+
git merge --no-ff {{$args.LocalBranchArg}}
-
git checkout {{$pull.BaseBranch}}
-
git merge --squash {{$localBranch}}
+
git checkout {{$args.BaseBranchArg}}
+
git merge --squash {{$args.LocalBranchArg}}
-
git checkout {{$pull.BaseBranch}}
-
git merge --ff-only {{$localBranch}}
+
git checkout {{$args.BaseBranchArg}}
+
git merge --ff-only {{$args.LocalBranchArg}}
-
git checkout {{$pull.BaseBranch}}
-
git merge {{$localBranch}}
+
git checkout {{$args.BaseBranchArg}}
+
git merge {{$args.LocalBranchArg}}
-
git push {{$gitRemoteName}} {{$pull.BaseBranch}}
+
git push {{$gitRemoteName}} {{$args.BaseBranchArg}}
{{end}}