Add function to create new branch with commit on UI and start PR (#2571)
1 parent e91d903 commit d97f7c6025fbd5d602f924bd829df81bf6949021
@onukura onukura authored on 3 Nov 2020
GitHub committed on 3 Nov 2020
Showing 4 changed files
View
532
src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala
import gitbucket.core.util.SyntaxSugars._
import gitbucket.core.util.Implicits._
import gitbucket.core.util.Directory._
import gitbucket.core.model.{Account, CommitState, CommitStatus}
import gitbucket.core.util.JGitUtil.CommitInfo
import gitbucket.core.service.RepositoryService.RepositoryInfo
import gitbucket.core.util.JGitUtil.{CommitInfo, createBranch}
import gitbucket.core.view
import gitbucket.core.view.helpers
import org.apache.commons.compress.archivers.{ArchiveEntry, ArchiveOutputStream}
import org.apache.commons.compress.archivers.tar.{TarArchiveEntry, TarArchiveOutputStream}
case class UploadForm(
branch: String,
path: String,
uploadFiles: String,
message: Option[String]
message: Option[String],
commit: String,
newBranch: Boolean
)
 
case class EditorForm(
branch: String,
charset: String,
lineSeparator: String,
newFileName: String,
oldFileName: Option[String],
commit: String
commit: String,
newBranch: Boolean
)
 
case class DeleteForm(
branch: String,
path: String,
message: Option[String],
fileName: String,
commit: String
commit: String,
newBranch: Boolean
)
 
case class CommentForm(
fileName: Option[String],
"branch" -> trim(label("Branch", text(required))),
"path" -> trim(label("Path", text())),
"uploadFiles" -> trim(label("Upload files", text(required))),
"message" -> trim(label("Message", optional(text()))),
"commit" -> trim(label("Commit", text(required, conflict))),
"newBranch" -> trim(label("New Branch", boolean()))
)(UploadForm.apply)
 
val editorForm = mapping(
"branch" -> trim(label("Branch", text(required))),
"charset" -> trim(label("Charset", text(required))),
"lineSeparator" -> trim(label("Line Separator", text(required))),
"newFileName" -> trim(label("Filename", text(required))),
"oldFileName" -> trim(label("Old filename", optional(text()))),
"commit" -> trim(label("Commit", text(required, conflict)))
"commit" -> trim(label("Commit", text(required, conflict))),
"newBranch" -> trim(label("New Branch", boolean()))
)(EditorForm.apply)
 
val deleteForm = mapping(
"branch" -> trim(label("Branch", text(required))),
"path" -> trim(label("Path", text())),
"message" -> trim(label("Message", optional(text()))),
"fileName" -> trim(label("Filename", text(required))),
"commit" -> trim(label("Commit", text(required, conflict)))
"commit" -> trim(label("Commit", text(required, conflict))),
"newBranch" -> trim(label("New Branch", boolean()))
)(DeleteForm.apply)
 
val commentForm = mapping(
"fileName" -> trim(label("Filename", optional(text()))),
get("/:owner/:repository/upload/*")(writableUsersOnly { repository =>
val (branch, path) = repository.splitPath(multiParams("splat").head)
val protectedBranch = getProtectedBranchInfo(repository.owner, repository.name, branch)
.needStatusCheck(context.loginAccount.get.userName)
html.upload(branch, repository, if (path.length == 0) Nil else path.split("/").toList, protectedBranch)
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
html.upload(
branch,
repository,
if (path.length == 0) Nil else path.split("/").toList,
protectedBranch,
revCommit.name
)
}
})
 
post("/:owner/:repository/upload", uploadForm)(writableUsersOnly { (form, repository) =>
val files = form.uploadFiles.split("\n").map { line =>
val newFiles = files.map { file =>
file.copy(name = if (form.path.length == 0) file.name else s"${form.path}/${file.name}")
}
 
commitFiles(
repository = repository,
branch = form.branch,
path = form.path,
files = files.toIndexedSeq,
message = form.message.getOrElse("Add files via upload"),
loginAccount = context.loginAccount.get,
settings = context.settings
) {
case (git, headTip, builder, inserter) =>
JGitUtil.processTree(git, headTip) { (path, tree) =>
if (!newFiles.exists(_.name.contains(path))) {
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
if (form.newBranch) {
val newBranchName = createNewBranchForPullRequest(repository, form.branch)
val objectId = _commit(newBranchName)
val issueId =
createIssueAndPullRequest(repository, form.branch, newBranchName, form.commit, objectId.name, form.message)
redirect(s"/${repository.owner}/${repository.name}/pull/${issueId}")
} else {
_commit(form.branch)
if (form.path.length == 0) {
redirect(s"/${repository.owner}/${repository.name}/tree/${form.branch}")
} else {
redirect(s"/${repository.owner}/${repository.name}/tree/${form.branch}/${form.path}")
}
}
 
def _commit(branchName: String): ObjectId = {
commitFiles(
repository = repository,
branch = branchName,
path = form.path,
files = files.toIndexedSeq,
message = form.message.getOrElse("Add files via upload"),
loginAccount = context.loginAccount.get,
settings = context.settings
) {
case (git, headTip, builder, inserter) =>
JGitUtil.processTree(git, headTip) { (path, tree) =>
if (!newFiles.exists(_.name.contains(path))) {
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
}
}
}
 
newFiles.foreach { file =>
val bytes =
FileUtils.readFileToByteArray(new File(getTemporaryDir(session.getId), FileUtil.checkFilename(file.id)))
builder.add(
JGitUtil.createDirCacheEntry(file.name, FileMode.REGULAR_FILE, inserter.insert(Constants.OBJ_BLOB, bytes))
)
builder.finish()
}
}
 
if (form.path.length == 0) {
redirect(s"/${repository.owner}/${repository.name}/tree/${form.branch}")
} else {
redirect(s"/${repository.owner}/${repository.name}/tree/${form.branch}/${form.path}")
 
newFiles.foreach { file =>
val bytes =
FileUtils.readFileToByteArray(new File(getTemporaryDir(session.getId), FileUtil.checkFilename(file.id)))
builder.add(
JGitUtil.createDirCacheEntry(file.name, FileMode.REGULAR_FILE, inserter.insert(Constants.OBJ_BLOB, bytes))
)
builder.finish()
}
}
}
})
 
get("/:owner/:repository/edit/*")(writableUsersOnly { repository =>
}
})
 
post("/:owner/:repository/create", editorForm)(writableUsersOnly { (form, repository) =>
commitFile(
repository = repository,
branch = form.branch,
path = form.path,
newFileName = Some(form.newFileName),
oldFileName = None,
content = appendNewLine(convertLineSeparator(form.content, form.lineSeparator), form.lineSeparator),
charset = form.charset,
message = form.message.getOrElse(s"Create ${form.newFileName}"),
commit = form.commit,
if (form.newBranch) {
val newBranchName = createNewBranchForPullRequest(repository, form.branch)
val objectId = _commit(newBranchName)
val issueId =
createIssueAndPullRequest(repository, form.branch, newBranchName, form.commit, objectId.name, form.message)
redirect(s"/${repository.owner}/${repository.name}/pull/${issueId}")
} else {
_commit(form.branch)
redirect(
s"/${repository.owner}/${repository.name}/blob/${form.branch}/${if (form.path.length == 0) urlEncode(form.newFileName)
else s"${form.path}/${urlEncode(form.newFileName)}"}"
)
}
 
def _commit(branchName: String): ObjectId = {
commitFile(
repository = repository,
branch = branchName,
path = form.path,
newFileName = Some(form.newFileName),
oldFileName = None,
content = appendNewLine(convertLineSeparator(form.content, form.lineSeparator), form.lineSeparator),
charset = form.charset,
message = form.message.getOrElse(s"Create ${form.newFileName}"),
commit = form.commit,
loginAccount = context.loginAccount.get,
settings = context.settings
)
}
})
 
post("/:owner/:repository/update", editorForm)(writableUsersOnly { (form, repository) =>
if (form.newBranch) {
val newBranchName = createNewBranchForPullRequest(repository, form.branch)
val objectId = _commit(newBranchName)
val issueId =
createIssueAndPullRequest(repository, form.branch, newBranchName, form.commit, objectId.name, form.message)
redirect(s"/${repository.owner}/${repository.name}/pull/${issueId}")
} else {
_commit(form.branch)
redirect(
s"/${repository.owner}/${repository.name}/blob/${urlEncode(form.branch)}/${if (form.path.length == 0) urlEncode(form.newFileName)
else s"${form.path}/${urlEncode(form.newFileName)}"}"
)
}
 
def _commit(branchName: String): ObjectId = {
commitFile(
repository = repository,
branch = branchName,
path = form.path,
newFileName = Some(form.newFileName),
oldFileName = form.oldFileName,
content = appendNewLine(convertLineSeparator(form.content, form.lineSeparator), form.lineSeparator),
charset = form.charset,
message = if (form.oldFileName.contains(form.newFileName)) {
form.message.getOrElse(s"Update ${form.newFileName}")
} else {
form.message.getOrElse(s"Rename ${form.oldFileName.get} to ${form.newFileName}")
},
commit = form.commit,
loginAccount = context.loginAccount.get,
settings = context.settings
)
}
})
 
post("/:owner/:repository/remove", deleteForm)(writableUsersOnly { (form, repository) =>
if (form.newBranch) {
val newBranchName = createNewBranchForPullRequest(repository, form.branch)
val objectId = _commit(newBranchName)
val issueId =
createIssueAndPullRequest(repository, form.branch, newBranchName, form.commit, objectId.name, form.message)
redirect(s"/${repository.owner}/${repository.name}/pull/${issueId}")
} else {
_commit(form.branch)
redirect(
s"/${repository.owner}/${repository.name}/tree/${form.branch}${if (form.path.length == 0) ""
else "/" + form.path}"
)
}
 
def _commit(branchName: String): ObjectId = {
commitFile(
repository = repository,
branch = branchName,
path = form.path,
newFileName = None,
oldFileName = Some(form.fileName),
content = "",
charset = "",
message = form.message.getOrElse(s"Delete ${form.fileName}"),
commit = form.commit,
loginAccount = context.loginAccount.get,
settings = context.settings
)
}
})
 
private def getNewBranchName(repository: RepositoryInfo): String = {
var i = 1
val branchNamePrefix = cutTail(context.loginAccount.get.userName.replaceAll("[^a-zA-Z0-9-_]", "-"), 25)
while (repository.branchList.exists(p => p.contains(s"$branchNamePrefix-patch-$i"))) {
i += 1
}
s"$branchNamePrefix-patch-$i"
}
 
private def createNewBranchForPullRequest(repository: RepositoryInfo, baseBranchName: String): String = {
val newBranchName = getNewBranchName(repository)
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
JGitUtil.createBranch(git, baseBranchName, newBranchName)
}
newBranchName
}
 
private def createIssueAndPullRequest(
repository: RepositoryInfo,
baseBranch: String,
requestBranch: String,
commitIdFrom: String,
commitIdTo: String,
commitMessage: Option[String]
): Int = {
val issueId = insertIssue(
owner = repository.owner,
repository = repository.name,
loginUser = context.loginAccount.get.userName,
title = requestBranch,
content = commitMessage,
assignedUserName = None,
milestoneId = None,
priorityId = None,
isPullRequest = true
)
createPullRequest(
originRepository = repository,
issueId = issueId,
originBranch = baseBranch,
requestUserName = repository.owner,
requestRepositoryName = repository.name,
requestBranch = requestBranch,
commitIdFrom = commitIdFrom,
commitIdTo = commitIdTo,
isDraft = false,
loginAccount = context.loginAccount.get,
settings = context.settings
)
 
redirect(
s"/${repository.owner}/${repository.name}/blob/${form.branch}/${if (form.path.length == 0) urlEncode(form.newFileName)
else s"${form.path}/${urlEncode(form.newFileName)}"}"
)
})
 
post("/:owner/:repository/update", editorForm)(writableUsersOnly { (form, repository) =>
commitFile(
repository = repository,
branch = form.branch,
path = form.path,
newFileName = Some(form.newFileName),
oldFileName = form.oldFileName,
content = appendNewLine(convertLineSeparator(form.content, form.lineSeparator), form.lineSeparator),
charset = form.charset,
message = if (form.oldFileName.contains(form.newFileName)) {
form.message.getOrElse(s"Update ${form.newFileName}")
} else {
form.message.getOrElse(s"Rename ${form.oldFileName.get} to ${form.newFileName}")
},
commit = form.commit,
loginAccount = context.loginAccount.get,
settings = context.settings
)
 
redirect(
s"/${repository.owner}/${repository.name}/blob/${urlEncode(form.branch)}/${if (form.path.length == 0) urlEncode(form.newFileName)
else s"${form.path}/${urlEncode(form.newFileName)}"}"
)
})
 
post("/:owner/:repository/remove", deleteForm)(writableUsersOnly { (form, repository) =>
commitFile(
repository = repository,
branch = form.branch,
path = form.path,
newFileName = None,
oldFileName = Some(form.fileName),
content = "",
charset = "",
message = form.message.getOrElse(s"Delete ${form.fileName}"),
commit = form.commit,
loginAccount = context.loginAccount.get,
settings = context.settings
)
 
redirect(
s"/${repository.owner}/${repository.name}/tree/${form.branch}${if (form.path.length == 0) "" else "/" + form.path}"
)
})
issueId
}
 
get("/:owner/:repository/raw/*")(referrersOnly { repository =>
val (id, path) = repository.splitPath(multiParams("splat").head)
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
View
12
src/main/twirl/gitbucket/core/repo/delete.scala.html
</div>
<div class="form-group">
<input type="text" name="message" class="form-control"/>
</div>
<div class="form-group">
<div class="col-md-12">
<label class="radio">
<input type="radio" id="newBranch" name="newBranch" value="false" checked>
<i class='octicon octicon-git-commit'></i><span>Commit directory to the <strong style="background-color: lightblue; font-family: Consolas">@branch</strong> branch.</span>
</label>
<label class="radio">
<input type="radio" id="newBranch" name="newBranch" value="true">
<i class='octicon octicon-git-pull-request'></i><span>Create a <strong>new branch</strong> for this commit and start a pull request.</span>
</label>
</div>
</div>
<div style="text-align: right;">
<a href="@helpers.url(repository)/blob/@helpers.encodeRefName((branch :: pathList ::: List(fileName)).mkString("/"))" class="btn btn-default">Cancel</a>
<input type="submit" id="commitButton" class="btn btn-success" value="Commit changes"/>
</div>
View
src/main/twirl/gitbucket/core/repo/editor.scala.html
View
src/main/twirl/gitbucket/core/repo/upload.scala.html