You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

NewRepositoryPage.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. /*
  2. * Copyright 2014 gitblit.com.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.gitblit.wicket.pages;
  17. import java.io.File;
  18. import java.io.IOException;
  19. import java.io.UnsupportedEncodingException;
  20. import java.util.ArrayList;
  21. import java.util.Collections;
  22. import java.util.List;
  23. import org.apache.wicket.behavior.SimpleAttributeModifier;
  24. import org.apache.wicket.markup.html.form.Button;
  25. import org.apache.wicket.markup.html.form.Form;
  26. import org.apache.wicket.model.CompoundPropertyModel;
  27. import org.apache.wicket.model.IModel;
  28. import org.apache.wicket.model.Model;
  29. import org.eclipse.jgit.dircache.DirCache;
  30. import org.eclipse.jgit.dircache.DirCacheBuilder;
  31. import org.eclipse.jgit.dircache.DirCacheEntry;
  32. import org.eclipse.jgit.lib.CommitBuilder;
  33. import org.eclipse.jgit.lib.Config;
  34. import org.eclipse.jgit.lib.FileMode;
  35. import org.eclipse.jgit.lib.ObjectId;
  36. import org.eclipse.jgit.lib.ObjectInserter;
  37. import org.eclipse.jgit.lib.PersonIdent;
  38. import org.eclipse.jgit.lib.RefUpdate;
  39. import org.eclipse.jgit.lib.RefUpdate.Result;
  40. import org.eclipse.jgit.lib.Repository;
  41. import org.eclipse.jgit.revwalk.RevCommit;
  42. import org.eclipse.jgit.revwalk.RevWalk;
  43. import com.gitblit.Constants;
  44. import com.gitblit.Constants.AccessRestrictionType;
  45. import com.gitblit.Constants.AuthorizationControl;
  46. import com.gitblit.GitBlitException;
  47. import com.gitblit.Keys;
  48. import com.gitblit.models.RepositoryModel;
  49. import com.gitblit.models.UserModel;
  50. import com.gitblit.utils.ArrayUtils;
  51. import com.gitblit.utils.FileUtils;
  52. import com.gitblit.utils.StringUtils;
  53. import com.gitblit.wicket.GitBlitWebSession;
  54. import com.gitblit.wicket.WicketUtils;
  55. import com.gitblit.wicket.panels.AccessPolicyPanel;
  56. import com.gitblit.wicket.panels.BooleanChoiceOption;
  57. import com.gitblit.wicket.panels.BooleanOption;
  58. import com.gitblit.wicket.panels.RepositoryNamePanel;
  59. import com.google.common.base.Optional;
  60. public class NewRepositoryPage extends RootSubPage {
  61. private final RepositoryModel repositoryModel;
  62. private IModel<Boolean> addReadmeModel;
  63. private Model<String> gitignoreModel;
  64. private IModel<Boolean> addGitflowModel;
  65. private IModel<Boolean> addGitignoreModel;
  66. private AccessPolicyPanel accessPolicyPanel;
  67. private RepositoryNamePanel namePanel;
  68. public NewRepositoryPage() {
  69. // create constructor
  70. super();
  71. repositoryModel = new RepositoryModel();
  72. setupPage(getString("gb.newRepository"), "");
  73. setStatelessHint(false);
  74. setOutputMarkupId(true);
  75. }
  76. @Override
  77. protected boolean requiresPageMap() {
  78. return true;
  79. }
  80. @Override
  81. protected Class<? extends BasePage> getRootNavPageClass() {
  82. return RepositoriesPage.class;
  83. }
  84. @Override
  85. protected void onInitialize() {
  86. super.onInitialize();
  87. CompoundPropertyModel<RepositoryModel> rModel = new CompoundPropertyModel<>(repositoryModel);
  88. Form<RepositoryModel> form = new Form<RepositoryModel>("editForm", rModel) {
  89. private static final long serialVersionUID = 1L;
  90. @Override
  91. protected void onSubmit() {
  92. try {
  93. if (!namePanel.updateModel(repositoryModel)) {
  94. return;
  95. }
  96. accessPolicyPanel.updateModel(repositoryModel);
  97. repositoryModel.owners = new ArrayList<String>();
  98. repositoryModel.owners.add(GitBlitWebSession.get().getUsername());
  99. // setup branch defaults
  100. boolean useGitFlow = addGitflowModel.getObject();
  101. repositoryModel.HEAD = Constants.R_MASTER;
  102. repositoryModel.mergeTo = Constants.MASTER;
  103. if (useGitFlow) {
  104. // tickets normally merge to develop unless they are hotfixes
  105. repositoryModel.mergeTo = Constants.DEVELOP;
  106. }
  107. repositoryModel.allowForks = app().settings().getBoolean(Keys.web.allowForking, true);
  108. // optionally generate an initial commit
  109. boolean addReadme = addReadmeModel.getObject();
  110. String gitignore = null;
  111. boolean addGitignore = addGitignoreModel.getObject();
  112. if (addGitignore) {
  113. gitignore = gitignoreModel.getObject();
  114. if (StringUtils.isEmpty(gitignore)) {
  115. throw new GitBlitException(getString("gb.pleaseSelectGitIgnore"));
  116. }
  117. }
  118. // init the repository
  119. app().gitblit().updateRepositoryModel(repositoryModel.name, repositoryModel, true);
  120. // optionally create an initial commit
  121. initialCommit(repositoryModel, addReadme, gitignore, useGitFlow);
  122. } catch (GitBlitException e) {
  123. error(e.getMessage());
  124. return;
  125. }
  126. setRedirect(true);
  127. setResponsePage(SummaryPage.class, WicketUtils.newRepositoryParameter(repositoryModel.name));
  128. }
  129. };
  130. // do not let the browser pre-populate these fields
  131. form.add(new SimpleAttributeModifier("autocomplete", "off"));
  132. namePanel = new RepositoryNamePanel("namePanel", repositoryModel);
  133. form.add(namePanel);
  134. // prepare the default access controls
  135. AccessRestrictionType defaultRestriction = AccessRestrictionType.fromName(
  136. app().settings().getString(Keys.git.defaultAccessRestriction, AccessRestrictionType.PUSH.name()));
  137. if (AccessRestrictionType.NONE == defaultRestriction) {
  138. defaultRestriction = AccessRestrictionType.PUSH;
  139. }
  140. AuthorizationControl defaultControl = AuthorizationControl.fromName(
  141. app().settings().getString(Keys.git.defaultAuthorizationControl, AuthorizationControl.NAMED.name()));
  142. if (AuthorizationControl.AUTHENTICATED == defaultControl) {
  143. defaultRestriction = AccessRestrictionType.PUSH;
  144. }
  145. repositoryModel.authorizationControl = defaultControl;
  146. repositoryModel.accessRestriction = defaultRestriction;
  147. accessPolicyPanel = new AccessPolicyPanel("accessPolicyPanel", repositoryModel);
  148. form.add(accessPolicyPanel);
  149. //
  150. // initial commit options
  151. //
  152. // add README
  153. addReadmeModel = Model.of(false);
  154. form.add(new BooleanOption("addReadme",
  155. getString("gb.initWithReadme"),
  156. getString("gb.initWithReadmeDescription"),
  157. addReadmeModel));
  158. // add .gitignore
  159. File gitignoreDir = app().runtime().getFileOrFolder(Keys.git.gitignoreFolder, "${baseFolder}/gitignore");
  160. File [] files = gitignoreDir.listFiles();
  161. if (files == null) {
  162. files = new File[0];
  163. }
  164. List<String> gitignores = new ArrayList<String>();
  165. for (File file : files) {
  166. if (file.isFile() && file.getName().endsWith(".gitignore")) {
  167. gitignores.add(StringUtils.stripFileExtension(file.getName()));
  168. }
  169. }
  170. Collections.sort(gitignores);
  171. gitignoreModel = Model.of("");
  172. addGitignoreModel = Model.of(false);
  173. form.add(new BooleanChoiceOption<String>("addGitIgnore",
  174. getString("gb.initWithGitignore"),
  175. getString("gb.initWithGitignoreDescription"),
  176. addGitignoreModel,
  177. gitignoreModel,
  178. gitignores).setVisible(gitignores.size() > 0));
  179. // TODO consider gitflow at creation (ticket-55)
  180. addGitflowModel = Model.of(false);
  181. form.add(new BooleanOption("addGitFlow",
  182. "Include a .gitflow file",
  183. "This will generate a config file which guides Git clients in setting up Gitflow branches.",
  184. addGitflowModel).setVisible(false));
  185. form.add(new Button("create"));
  186. add(form);
  187. }
  188. /**
  189. * Prepare the initial commit for the repository.
  190. *
  191. * @param repository
  192. * @param addReadme
  193. * @param gitignore
  194. * @param addGitFlow
  195. * @return true if an initial commit was created
  196. */
  197. protected boolean initialCommit(RepositoryModel repository, boolean addReadme, String gitignore,
  198. boolean addGitFlow) {
  199. boolean initialCommit = addReadme || !StringUtils.isEmpty(gitignore) || addGitFlow;
  200. if (!initialCommit) {
  201. return false;
  202. }
  203. // build an initial commit
  204. boolean success = false;
  205. Repository db = app().repositories().getRepository(repositoryModel.name);
  206. ObjectInserter odi = db.newObjectInserter();
  207. try {
  208. UserModel user = GitBlitWebSession.get().getUser();
  209. String email = Optional.fromNullable(user.emailAddress).or(user.username + "@" + "gitblit");
  210. PersonIdent author = new PersonIdent(user.getDisplayName(), email);
  211. DirCache newIndex = DirCache.newInCore();
  212. DirCacheBuilder indexBuilder = newIndex.builder();
  213. if (addReadme) {
  214. // insert a README
  215. String title = StringUtils.stripDotGit(StringUtils.getLastPathElement(repositoryModel.name));
  216. String description = repositoryModel.description == null ? "" : repositoryModel.description;
  217. String readme = String.format("## %s\n\n%s\n\n", title, description);
  218. byte [] bytes = readme.getBytes(Constants.ENCODING);
  219. DirCacheEntry entry = new DirCacheEntry("README.md");
  220. entry.setLength(bytes.length);
  221. entry.setLastModified(System.currentTimeMillis());
  222. entry.setFileMode(FileMode.REGULAR_FILE);
  223. entry.setObjectId(odi.insert(org.eclipse.jgit.lib.Constants.OBJ_BLOB, bytes));
  224. indexBuilder.add(entry);
  225. }
  226. if (!StringUtils.isEmpty(gitignore)) {
  227. // insert a .gitignore file
  228. File dir = app().runtime().getFileOrFolder(Keys.git.gitignoreFolder, "${baseFolder}/gitignore");
  229. File file = new File(dir, gitignore + ".gitignore");
  230. if (file.exists() && file.length() > 0) {
  231. byte [] bytes = FileUtils.readContent(file);
  232. if (!ArrayUtils.isEmpty(bytes)) {
  233. DirCacheEntry entry = new DirCacheEntry(".gitignore");
  234. entry.setLength(bytes.length);
  235. entry.setLastModified(System.currentTimeMillis());
  236. entry.setFileMode(FileMode.REGULAR_FILE);
  237. entry.setObjectId(odi.insert(org.eclipse.jgit.lib.Constants.OBJ_BLOB, bytes));
  238. indexBuilder.add(entry);
  239. }
  240. }
  241. }
  242. if (addGitFlow) {
  243. // insert a .gitflow file
  244. Config config = new Config();
  245. config.setString("gitflow", null, "masterBranch", Constants.MASTER);
  246. config.setString("gitflow", null, "developBranch", Constants.DEVELOP);
  247. config.setString("gitflow", null, "featureBranchPrefix", "feature/");
  248. config.setString("gitflow", null, "releaseBranchPrefix", "release/");
  249. config.setString("gitflow", null, "hotfixBranchPrefix", "hotfix/");
  250. config.setString("gitflow", null, "supportBranchPrefix", "support/");
  251. config.setString("gitflow", null, "versionTagPrefix", "");
  252. byte [] bytes = config.toText().getBytes(Constants.ENCODING);
  253. DirCacheEntry entry = new DirCacheEntry(".gitflow");
  254. entry.setLength(bytes.length);
  255. entry.setLastModified(System.currentTimeMillis());
  256. entry.setFileMode(FileMode.REGULAR_FILE);
  257. entry.setObjectId(odi.insert(org.eclipse.jgit.lib.Constants.OBJ_BLOB, bytes));
  258. indexBuilder.add(entry);
  259. }
  260. indexBuilder.finish();
  261. if (newIndex.getEntryCount() == 0) {
  262. // nothing to commit
  263. return false;
  264. }
  265. ObjectId treeId = newIndex.writeTree(odi);
  266. // Create a commit object
  267. CommitBuilder commit = new CommitBuilder();
  268. commit.setAuthor(author);
  269. commit.setCommitter(author);
  270. commit.setEncoding(Constants.ENCODING);
  271. commit.setMessage("Initial commit");
  272. commit.setTreeId(treeId);
  273. // Insert the commit into the repository
  274. ObjectId commitId = odi.insert(commit);
  275. odi.flush();
  276. // set the branch refs
  277. RevWalk revWalk = new RevWalk(db);
  278. try {
  279. // set the master branch
  280. RevCommit revCommit = revWalk.parseCommit(commitId);
  281. RefUpdate masterRef = db.updateRef(Constants.R_MASTER);
  282. masterRef.setNewObjectId(commitId);
  283. masterRef.setRefLogMessage("commit: " + revCommit.getShortMessage(), false);
  284. Result masterRC = masterRef.update();
  285. switch (masterRC) {
  286. case NEW:
  287. success = true;
  288. break;
  289. default:
  290. success = false;
  291. }
  292. if (addGitFlow) {
  293. // set the develop branch for git-flow
  294. RefUpdate developRef = db.updateRef(Constants.R_DEVELOP);
  295. developRef.setNewObjectId(commitId);
  296. developRef.setRefLogMessage("commit: " + revCommit.getShortMessage(), false);
  297. Result developRC = developRef.update();
  298. switch (developRC) {
  299. case NEW:
  300. success = true;
  301. break;
  302. default:
  303. success = false;
  304. }
  305. }
  306. } finally {
  307. revWalk.close();
  308. }
  309. } catch (UnsupportedEncodingException e) {
  310. logger().error(null, e);
  311. } catch (IOException e) {
  312. logger().error(null, e);
  313. } finally {
  314. odi.close();
  315. db.close();
  316. }
  317. return success;
  318. }
  319. }