diff --git a/src/main/scala/app/RepositoryViewerController.scala b/src/main/scala/app/RepositoryViewerController.scala index 18a9f3f..5b5b11b 100644 --- a/src/main/scala/app/RepositoryViewerController.scala +++ b/src/main/scala/app/RepositoryViewerController.scala @@ -114,7 +114,7 @@ repo.html.commits(if(path.isEmpty) Nil else path.split("/").toList, branchName, repository, logs.splitWith{ (commit1, commit2) => view.helpers.date(commit1.commitTime) == view.helpers.date(commit2.commitTime) - }, page, hasNext) + }, page, hasNext, hasWritePermission(repository.owner, repository.name, context.loginAccount)) case Left(_) => NotFound } } @@ -242,6 +242,24 @@ }) /** + * 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. */ get("/:owner/:repository/delete/*")(collaboratorsOnly { repository => @@ -333,7 +351,8 @@ repo.html.files(revision, repository, if(path == ".") Nil else path.split("/").toList, // current path 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 } diff --git a/src/main/scala/util/JGitUtil.scala b/src/main/scala/util/JGitUtil.scala index 0e0493d..5ea43a3 100644 --- a/src/main/scala/util/JGitUtil.scala +++ b/src/main/scala/util/JGitUtil.scala @@ -14,7 +14,7 @@ import org.eclipse.jgit.diff.DiffEntry.ChangeType import org.eclipse.jgit.errors.{ConfigInvalidException, MissingObjectException} 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 org.eclipse.jgit.dircache.DirCacheEntry import org.slf4j.LoggerFactory @@ -507,6 +507,17 @@ }.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 = { val entry = new DirCacheEntry(path) entry.setFileMode(mode) diff --git a/src/main/twirl/helper/branchcontrol.scala.html b/src/main/twirl/helper/branchcontrol.scala.html new file mode 100644 index 0000000..5381688 --- /dev/null +++ b/src/main/twirl/helper/branchcontrol.scala.html @@ -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 +) { +
  • Switch branches
  • +
  • + @body + @if(hasWritePermission) { + + } +} + diff --git a/src/main/twirl/helper/error.scala.html b/src/main/twirl/helper/error.scala.html new file mode 100644 index 0000000..00f43e2 --- /dev/null +++ b/src/main/twirl/helper/error.scala.html @@ -0,0 +1,7 @@ +@(error: Option[Any]) +@if(error.isDefined){ +
    + + @error +
    +} \ No newline at end of file diff --git a/src/main/twirl/helper/information.scala.html b/src/main/twirl/helper/information.scala.html index d8bcff5..ff382a2 100644 --- a/src/main/twirl/helper/information.scala.html +++ b/src/main/twirl/helper/information.scala.html @@ -1,7 +1,7 @@ @(info: Option[Any]) @if(info.isDefined){
    - + @info
    } diff --git a/src/main/twirl/menu.scala.html b/src/main/twirl/menu.scala.html index 8076c82..7ac1dc8 100644 --- a/src/main/twirl/menu.scala.html +++ b/src/main/twirl/menu.scala.html @@ -1,7 +1,9 @@ @(active: String, repository: service.RepositoryService.RepositoryInfo, 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 view.helpers._ @@ -31,6 +33,8 @@ }
    + @helper.html.information(info) + @helper.html.error(error) @if(repository.commitCount > 0){
    diff --git a/src/main/twirl/repo/blob.scala.html b/src/main/twirl/repo/blob.scala.html index 68d3870..549d2e8 100644 --- a/src/main/twirl/repo/blob.scala.html +++ b/src/main/twirl/repo/blob.scala.html @@ -9,10 +9,10 @@ @html.main(s"${repository.owner}/${repository.name}", Some(repository)) { @html.menu("code", repository){
    - @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 + @helper.html.branchcontrol( + branch, + repository, + hasWritePermission ){ @repository.branchList.map { x =>
  • @helper.html.checkicon(x == branch) @x
  • diff --git a/src/main/twirl/repo/commits.scala.html b/src/main/twirl/repo/commits.scala.html index f0aaf3b..443ab07 100644 --- a/src/main/twirl/repo/commits.scala.html +++ b/src/main/twirl/repo/commits.scala.html @@ -3,16 +3,17 @@ repository: service.RepositoryService.RepositoryInfo, commits: Seq[Seq[util.JGitUtil.CommitInfo]], page: Int, - hasNext: Boolean)(implicit context: app.Context) + hasNext: Boolean, + hasWritePermission: Boolean)(implicit context: app.Context) @import context._ @import view.helpers._ @html.main(s"${repository.owner}/${repository.name}", Some(repository)) { @html.menu("code", repository){
    - @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 + @helper.html.branchcontrol( + branch, + repository, + hasWritePermission ){ @repository.branchList.map { x =>
  • @helper.html.checkicon(x == branch) @x
  • diff --git a/src/main/twirl/repo/files.scala.html b/src/main/twirl/repo/files.scala.html index ab14364..96e4a61 100644 --- a/src/main/twirl/repo/files.scala.html +++ b/src/main/twirl/repo/files.scala.html @@ -4,16 +4,18 @@ latestCommit: util.JGitUtil.CommitInfo, files: List[util.JGitUtil.FileInfo], 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 view.helpers._ @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){
    - @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 + @helper.html.branchcontrol( + branch, + repository, + hasWritePermission ){ @repository.branchList.map { x =>
  • @helper.html.checkicon(x == branch) @x
  • diff --git a/src/main/webapp/assets/common/css/gitbucket.css b/src/main/webapp/assets/common/css/gitbucket.css index c8c972d..6fd7455 100644 --- a/src/main/webapp/assets/common/css/gitbucket.css +++ b/src/main/webapp/assets/common/css/gitbucket.css @@ -617,6 +617,30 @@ 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 */ /****************************************************************************/