mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-06 13:35:50 +01:00
Merge pull request #512 from mrkm4ntr/create-branch-ui
(refs #394) Create branch from Web UI
This commit is contained in:
@@ -114,7 +114,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
repo.html.commits(if(path.isEmpty) Nil else path.split("/").toList, branchName, repository,
|
repo.html.commits(if(path.isEmpty) Nil else path.split("/").toList, branchName, repository,
|
||||||
logs.splitWith{ (commit1, commit2) =>
|
logs.splitWith{ (commit1, commit2) =>
|
||||||
view.helpers.date(commit1.commitTime) == view.helpers.date(commit2.commitTime)
|
view.helpers.date(commit1.commitTime) == view.helpers.date(commit2.commitTime)
|
||||||
}, page, hasNext)
|
}, page, hasNext, hasWritePermission(repository.owner, repository.name, context.loginAccount))
|
||||||
case Left(_) => NotFound
|
case Left(_) => NotFound
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -241,6 +241,24 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a branch.
|
||||||
|
*/
|
||||||
|
post("/:owner/:repository/branches")(collaboratorsOnly { repository =>
|
||||||
|
val newBranchName = params.getOrElse("new", halt(400))
|
||||||
|
val fromBranchName = params.getOrElse("from", halt(400))
|
||||||
|
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
|
||||||
|
JGitUtil.createBranch(git, fromBranchName, newBranchName)
|
||||||
|
} match {
|
||||||
|
case Right(message) =>
|
||||||
|
flash += "info" -> message
|
||||||
|
redirect(s"/${repository.owner}/${repository.name}/tree/${StringUtil.urlEncode(newBranchName).replace("%2F", "/")}")
|
||||||
|
case Left(message) =>
|
||||||
|
flash += "error" -> message
|
||||||
|
redirect(s"/${repository.owner}/${repository.name}/tree/${fromBranchName}")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes branch.
|
* Deletes branch.
|
||||||
*/
|
*/
|
||||||
@@ -333,7 +351,8 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
repo.html.files(revision, repository,
|
repo.html.files(revision, repository,
|
||||||
if(path == ".") Nil else path.split("/").toList, // current path
|
if(path == ".") Nil else path.split("/").toList, // current path
|
||||||
new JGitUtil.CommitInfo(lastModifiedCommit), // last modified commit
|
new JGitUtil.CommitInfo(lastModifiedCommit), // last modified commit
|
||||||
files, readme, hasWritePermission(repository.owner, repository.name, context.loginAccount))
|
files, readme, hasWritePermission(repository.owner, repository.name, context.loginAccount),
|
||||||
|
flash.get("info"), flash.get("error"))
|
||||||
}
|
}
|
||||||
} getOrElse NotFound
|
} getOrElse NotFound
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import org.eclipse.jgit.treewalk.filter._
|
|||||||
import org.eclipse.jgit.diff.DiffEntry.ChangeType
|
import org.eclipse.jgit.diff.DiffEntry.ChangeType
|
||||||
import org.eclipse.jgit.errors.{ConfigInvalidException, MissingObjectException}
|
import org.eclipse.jgit.errors.{ConfigInvalidException, MissingObjectException}
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import org.eclipse.jgit.api.errors.NoHeadException
|
import org.eclipse.jgit.api.errors.{JGitInternalException, InvalidRefNameException, RefAlreadyExistsException, NoHeadException}
|
||||||
import service.RepositoryService
|
import service.RepositoryService
|
||||||
import org.eclipse.jgit.dircache.DirCacheEntry
|
import org.eclipse.jgit.dircache.DirCacheEntry
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
@@ -507,6 +507,17 @@ object JGitUtil {
|
|||||||
}.find(_._1 != null)
|
}.find(_._1 != null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def createBranch(git: Git, fromBranch: String, newBranch: String) = {
|
||||||
|
try {
|
||||||
|
git.branchCreate().setStartPoint(fromBranch).setName(newBranch).call()
|
||||||
|
Right("Branch created.")
|
||||||
|
} catch {
|
||||||
|
case e: RefAlreadyExistsException => Left("Sorry, that branch already exists.")
|
||||||
|
// JGitInternalException occurs when new branch name is 'a' and the branch whose name is 'a/*' exists.
|
||||||
|
case _: InvalidRefNameException | _: JGitInternalException => Left("Sorry, that name is invalid.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def createDirCacheEntry(path: String, mode: FileMode, objectId: ObjectId): DirCacheEntry = {
|
def createDirCacheEntry(path: String, mode: FileMode, objectId: ObjectId): DirCacheEntry = {
|
||||||
val entry = new DirCacheEntry(path)
|
val entry = new DirCacheEntry(path)
|
||||||
entry.setFileMode(mode)
|
entry.setFileMode(mode)
|
||||||
|
|||||||
62
src/main/twirl/helper/branchcontrol.scala.html
Normal file
62
src/main/twirl/helper/branchcontrol.scala.html
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
@(branch: String = "",
|
||||||
|
repository: service.RepositoryService.RepositoryInfo,
|
||||||
|
hasWritePermission: Boolean)(body: Html)(implicit context: app.Context)
|
||||||
|
@import context._
|
||||||
|
@import view.helpers._
|
||||||
|
@helper.html.dropdown(
|
||||||
|
value = if(branch.length == 40) branch.substring(0, 10) else branch,
|
||||||
|
prefix = if(branch.length == 40) "tree" else if(repository.branchList.contains(branch)) "branch" else "tree",
|
||||||
|
mini = true
|
||||||
|
) {
|
||||||
|
<li><div id="branch-control-title">Switch branches<button id="branch-control-close" class="pull-right">×</button></div></li>
|
||||||
|
<li><input id="branch-control-input" type="text" placeholder="Find or create branch ..."/></li>
|
||||||
|
@body
|
||||||
|
@if(hasWritePermission) {
|
||||||
|
<li id="create-branch" style="display: none;">
|
||||||
|
<a><form action="@url(repository)/branches" method="post" style="margin: 0;">
|
||||||
|
<span class="new-branch-name">Create branch: <span class="new-branch"></span></span>
|
||||||
|
<br><span style="padding-left: 17px;">from '@branch'</span>
|
||||||
|
<input type="hidden" name="new">
|
||||||
|
<input type="hidden" name="from" value="@branch">
|
||||||
|
</form></a>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<script>
|
||||||
|
$(function(){
|
||||||
|
$('#branch-control-input').parent().click(function(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
|
$('#branch-control-close').click(function() {
|
||||||
|
$('[data-toggle="dropdown"]').parent().removeClass('open');
|
||||||
|
});
|
||||||
|
$('#branch-control-input').keyup(function() {
|
||||||
|
var inputVal = $('#branch-control-input').val();
|
||||||
|
$.each($('#branch-control-input').parent().parent().find('a'), function(index, elem) {
|
||||||
|
if (!inputVal || !elem.text.trim() || elem.text.trim().lastIndexOf(inputVal, 0) >= 0) {
|
||||||
|
$(elem).parent().show();
|
||||||
|
} else {
|
||||||
|
$(elem).parent().hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
@if(hasWritePermission) {
|
||||||
|
if (inputVal) {
|
||||||
|
$('#create-branch').parent().find('li:last-child').show().find('.new-branch').text(inputVal);
|
||||||
|
} else {
|
||||||
|
$('#create-branch').parent().find('li:last-child').hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
@if(hasWritePermission) {
|
||||||
|
$('#create-branch').click(function() {
|
||||||
|
$(this).find('input[name="new"]').val($('.dropdown-menu input').val())
|
||||||
|
$(this).find('form').submit()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$('.btn-group').click(function() {
|
||||||
|
$('#branch-control-input').val('');
|
||||||
|
$('.dropdown-menu li').show();
|
||||||
|
$('#create-branch').hide();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
7
src/main/twirl/helper/error.scala.html
Normal file
7
src/main/twirl/helper/error.scala.html
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
@(error: Option[Any])
|
||||||
|
@if(error.isDefined){
|
||||||
|
<div class='alert alert-danger'>
|
||||||
|
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||||
|
@error
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
@(info: Option[Any])
|
@(info: Option[Any])
|
||||||
@if(info.isDefined){
|
@if(info.isDefined){
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||||
@info
|
@info
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
@(active: String,
|
@(active: String,
|
||||||
repository: service.RepositoryService.RepositoryInfo,
|
repository: service.RepositoryService.RepositoryInfo,
|
||||||
id: Option[String] = None,
|
id: Option[String] = None,
|
||||||
expand: Boolean = false)(body: Html)(implicit context: app.Context)
|
expand: Boolean = false,
|
||||||
|
info: Option[Any] = None,
|
||||||
|
error: Option[Any] = None)(body: Html)(implicit context: app.Context)
|
||||||
@import context._
|
@import context._
|
||||||
@import view.helpers._
|
@import view.helpers._
|
||||||
|
|
||||||
@@ -31,6 +33,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
@helper.html.information(info)
|
||||||
|
@helper.html.error(error)
|
||||||
@if(repository.commitCount > 0){
|
@if(repository.commitCount > 0){
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<div class="input-prepend">
|
<div class="input-prepend">
|
||||||
|
|||||||
@@ -9,10 +9,10 @@
|
|||||||
@html.main(s"${repository.owner}/${repository.name}", Some(repository)) {
|
@html.main(s"${repository.owner}/${repository.name}", Some(repository)) {
|
||||||
@html.menu("code", repository){
|
@html.menu("code", repository){
|
||||||
<div class="head">
|
<div class="head">
|
||||||
@helper.html.dropdown(
|
@helper.html.branchcontrol(
|
||||||
value = if(branch.length == 40) branch.substring(0, 10) else branch,
|
branch,
|
||||||
prefix = if(branch.length == 40) "tree" else if(repository.branchList.contains(branch)) "branch" else "tree",
|
repository,
|
||||||
mini = true
|
hasWritePermission
|
||||||
){
|
){
|
||||||
@repository.branchList.map { x =>
|
@repository.branchList.map { x =>
|
||||||
<li><a href="@url(repository)/blob/@encodeRefName(x)/@pathList.mkString("/")">@helper.html.checkicon(x == branch) @x</a></li>
|
<li><a href="@url(repository)/blob/@encodeRefName(x)/@pathList.mkString("/")">@helper.html.checkicon(x == branch) @x</a></li>
|
||||||
|
|||||||
@@ -3,16 +3,17 @@
|
|||||||
repository: service.RepositoryService.RepositoryInfo,
|
repository: service.RepositoryService.RepositoryInfo,
|
||||||
commits: Seq[Seq[util.JGitUtil.CommitInfo]],
|
commits: Seq[Seq[util.JGitUtil.CommitInfo]],
|
||||||
page: Int,
|
page: Int,
|
||||||
hasNext: Boolean)(implicit context: app.Context)
|
hasNext: Boolean,
|
||||||
|
hasWritePermission: Boolean)(implicit context: app.Context)
|
||||||
@import context._
|
@import context._
|
||||||
@import view.helpers._
|
@import view.helpers._
|
||||||
@html.main(s"${repository.owner}/${repository.name}", Some(repository)) {
|
@html.main(s"${repository.owner}/${repository.name}", Some(repository)) {
|
||||||
@html.menu("code", repository){
|
@html.menu("code", repository){
|
||||||
<div class="head">
|
<div class="head">
|
||||||
@helper.html.dropdown(
|
@helper.html.branchcontrol(
|
||||||
value = if(branch.length == 40) branch.substring(0, 10) else branch,
|
branch,
|
||||||
prefix = if(branch.length == 40) "tree" else if(repository.branchList.contains(branch)) "branch" else "tree",
|
repository,
|
||||||
mini = true
|
hasWritePermission
|
||||||
){
|
){
|
||||||
@repository.branchList.map { x =>
|
@repository.branchList.map { x =>
|
||||||
<li><a href="@url(repository)/commits/@encodeRefName(x)">@helper.html.checkicon(x == branch) @x</a></li>
|
<li><a href="@url(repository)/commits/@encodeRefName(x)">@helper.html.checkicon(x == branch) @x</a></li>
|
||||||
|
|||||||
@@ -4,16 +4,18 @@
|
|||||||
latestCommit: util.JGitUtil.CommitInfo,
|
latestCommit: util.JGitUtil.CommitInfo,
|
||||||
files: List[util.JGitUtil.FileInfo],
|
files: List[util.JGitUtil.FileInfo],
|
||||||
readme: Option[(List[String], String)],
|
readme: Option[(List[String], String)],
|
||||||
hasWritePermission: Boolean)(implicit context: app.Context)
|
hasWritePermission: Boolean,
|
||||||
|
info: Option[Any] = None,
|
||||||
|
error: Option[Any] = None)(implicit context: app.Context)
|
||||||
@import context._
|
@import context._
|
||||||
@import view.helpers._
|
@import view.helpers._
|
||||||
@html.main(s"${repository.owner}/${repository.name}", Some(repository)) {
|
@html.main(s"${repository.owner}/${repository.name}", Some(repository)) {
|
||||||
@html.menu("code", repository, Some(branch), pathList.isEmpty){
|
@html.menu("code", repository, Some(branch), pathList.isEmpty, info, error){
|
||||||
<div class="head">
|
<div class="head">
|
||||||
@helper.html.dropdown(
|
@helper.html.branchcontrol(
|
||||||
value = if(branch.length == 40) branch.substring(0, 10) else branch,
|
branch,
|
||||||
prefix = if(branch.length == 40) "tree" else if(repository.branchList.contains(branch)) "branch" else "tree",
|
repository,
|
||||||
mini = true
|
hasWritePermission
|
||||||
){
|
){
|
||||||
@repository.branchList.map { x =>
|
@repository.branchList.map { x =>
|
||||||
<li><a href="@url(repository)/tree/@encodeRefName(x)">@helper.html.checkicon(x == branch) @x</a></li>
|
<li><a href="@url(repository)/tree/@encodeRefName(x)">@helper.html.checkicon(x == branch) @x</a></li>
|
||||||
|
|||||||
@@ -617,6 +617,30 @@ span.simplified-path {
|
|||||||
color: #888;
|
color: #888;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#branch-control-title {
|
||||||
|
margin: 5px 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#branch-control-close {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: #aaa;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#branch-control-input {
|
||||||
|
border: solid 1px #ccc;
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-branch-name {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.2em;
|
||||||
|
padding-left: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
/* nav pulls group */
|
/* nav pulls group */
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
|
|||||||
Reference in New Issue
Block a user