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 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  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.CheckboxOption;
  57. import com.gitblit.wicket.panels.ConditionalChoiceOption;
  58. import com.gitblit.wicket.panels.RepositoryNamePanel;
  59. public class NewRepositoryPage extends RootSubPage {
  60. private final RepositoryModel repositoryModel;
  61. private IModel<Boolean> addReadmeModel;
  62. private Model<String> gitignoreModel;
  63. private IModel<Boolean> addGitflowModel;
  64. private IModel<Boolean> addGitignoreModel;
  65. private AccessPolicyPanel accessPolicyPanel;
  66. private RepositoryNamePanel namePanel;
  67. public NewRepositoryPage() {
  68. // create constructor
  69. super();
  70. repositoryModel = new RepositoryModel();
  71. setupPage(getString("gb.newRepository"), "");
  72. setStatelessHint(false);
  73. setOutputMarkupId(true);
  74. }
  75. @Override
  76. protected boolean requiresPageMap() {
  77. return true;
  78. }
  79. @Override
  80. protected Class<? extends BasePage> getRootNavPageClass() {
  81. return RepositoriesPage.class;
  82. }
  83. @Override
  84. protected void onInitialize() {
  85. super.onInitialize();
  86. CompoundPropertyModel<RepositoryModel> rModel = new CompoundPropertyModel<>(repositoryModel);
  87. Form<RepositoryModel> form = new Form<RepositoryModel>("editForm", rModel) {
  88. private static final long serialVersionUID = 1L;
  89. @Override
  90. protected void onSubmit() {
  91. try {
  92. if (!namePanel.updateModel(repositoryModel)) {
  93. return;
  94. }
  95. accessPolicyPanel.updateModel(repositoryModel);
  96. repositoryModel.owners = new ArrayList<String>();
  97. repositoryModel.owners.add(GitBlitWebSession.get().getUsername());
  98. // setup branch defaults
  99. boolean useGitFlow = addGitflowModel.getObject();
  100. repositoryModel.HEAD = Constants.R_MASTER;
  101. repositoryModel.mergeTo = Constants.MASTER;
  102. if (useGitFlow) {
  103. // tickets normally merge to develop unless they are hotfixes
  104. repositoryModel.mergeTo = Constants.DEVELOP;
  105. }
  106. repositoryModel.allowForks = app().settings().getBoolean(Keys.web.allowForking, true);
  107. // optionally generate an initial commit
  108. boolean addReadme = addReadmeModel.getObject();
  109. String gitignore = null;
  110. boolean addGitignore = addGitignoreModel.getObject();
  111. if (addGitignore) {
  112. gitignore = gitignoreModel.getObject();
  113. if (StringUtils.isEmpty(gitignore)) {
  114. throw new GitBlitException(getString("gb.pleaseSelectGitIgnore"));
  115. }
  116. }
  117. // init the repository
  118. app().gitblit().updateRepositoryModel(repositoryModel.name, repositoryModel, true);
  119. // optionally create an initial commit
  120. initialCommit(repositoryModel, addReadme, gitignore, useGitFlow);
  121. } catch (GitBlitException e) {
  122. error(e.getMessage());
  123. namePanel.resetModel(repositoryModel);
  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 CheckboxOption("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 ConditionalChoiceOption<String>("addGitIgnore",
  174. getString("gb.initWithGitignore"),
  175. getString("gb.initWithGitignoreDescription"),
  176. addGitignoreModel,
  177. gitignoreModel,
  178. gitignores));
  179. // TODO consider gitflow at creation (ticket-55)
  180. addGitflowModel = Model.of(false);
  181. form.add(new CheckboxOption("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. PersonIdent author = new PersonIdent(user.getDisplayName(), user.emailAddress);
  210. DirCache newIndex = DirCache.newInCore();
  211. DirCacheBuilder indexBuilder = newIndex.builder();
  212. if (addReadme) {
  213. // insert a README
  214. String title = StringUtils.stripDotGit(StringUtils.getLastPathElement(repositoryModel.name));
  215. String description = repositoryModel.description == null ? "" : repositoryModel.description;
  216. String readme = String.format("## %s\n\n%s\n\n", title, description);
  217. byte [] bytes = readme.getBytes(Constants.ENCODING);
  218. DirCacheEntry entry = new DirCacheEntry("README.md");
  219. entry.setLength(bytes.length);
  220. entry.setLastModified(System.currentTimeMillis());
  221. entry.setFileMode(FileMode.REGULAR_FILE);
  222. entry.setObjectId(odi.insert(org.eclipse.jgit.lib.Constants.OBJ_BLOB, bytes));
  223. indexBuilder.add(entry);
  224. }
  225. if (!StringUtils.isEmpty(gitignore)) {
  226. // insert a .gitignore file
  227. File dir = app().runtime().getFileOrFolder(Keys.git.gitignoreFolder, "${baseFolder}/gitignore");
  228. File file = new File(dir, gitignore + ".gitignore");
  229. if (file.exists() && file.length() > 0) {
  230. byte [] bytes = FileUtils.readContent(file);
  231. if (!ArrayUtils.isEmpty(bytes)) {
  232. DirCacheEntry entry = new DirCacheEntry(".gitignore");
  233. entry.setLength(bytes.length);
  234. entry.setLastModified(System.currentTimeMillis());
  235. entry.setFileMode(FileMode.REGULAR_FILE);
  236. entry.setObjectId(odi.insert(org.eclipse.jgit.lib.Constants.OBJ_BLOB, bytes));
  237. indexBuilder.add(entry);
  238. }
  239. }
  240. }
  241. if (addGitFlow) {
  242. // insert a .gitflow file
  243. Config config = new Config();
  244. config.setString("gitflow", null, "masterBranch", Constants.MASTER);
  245. config.setString("gitflow", null, "developBranch", Constants.DEVELOP);
  246. config.setString("gitflow", null, "featureBranchPrefix", "feature/");
  247. config.setString("gitflow", null, "releaseBranchPrefix", "release/");
  248. config.setString("gitflow", null, "hotfixBranchPrefix", "hotfix/");
  249. config.setString("gitflow", null, "supportBranchPrefix", "support/");
  250. config.setString("gitflow", null, "versionTagPrefix", "");
  251. byte [] bytes = config.toText().getBytes(Constants.ENCODING);
  252. DirCacheEntry entry = new DirCacheEntry(".gitflow");
  253. entry.setLength(bytes.length);
  254. entry.setLastModified(System.currentTimeMillis());
  255. entry.setFileMode(FileMode.REGULAR_FILE);
  256. entry.setObjectId(odi.insert(org.eclipse.jgit.lib.Constants.OBJ_BLOB, bytes));
  257. indexBuilder.add(entry);
  258. }
  259. indexBuilder.finish();
  260. if (newIndex.getEntryCount() == 0) {
  261. // nothing to commit
  262. return false;
  263. }
  264. ObjectId treeId = newIndex.writeTree(odi);
  265. // Create a commit object
  266. CommitBuilder commit = new CommitBuilder();
  267. commit.setAuthor(author);
  268. commit.setCommitter(author);
  269. commit.setEncoding(Constants.ENCODING);
  270. commit.setMessage("Initial commit");
  271. commit.setTreeId(treeId);
  272. // Insert the commit into the repository
  273. ObjectId commitId = odi.insert(commit);
  274. odi.flush();
  275. // set the branch refs
  276. RevWalk revWalk = new RevWalk(db);
  277. try {
  278. // set the master branch
  279. RevCommit revCommit = revWalk.parseCommit(commitId);
  280. RefUpdate masterRef = db.updateRef(Constants.R_MASTER);
  281. masterRef.setNewObjectId(commitId);
  282. masterRef.setRefLogMessage("commit: " + revCommit.getShortMessage(), false);
  283. Result masterRC = masterRef.update();
  284. switch (masterRC) {
  285. case NEW:
  286. success = true;
  287. break;
  288. default:
  289. success = false;
  290. }
  291. if (addGitFlow) {
  292. // set the develop branch for git-flow
  293. RefUpdate developRef = db.updateRef(Constants.R_DEVELOP);
  294. developRef.setNewObjectId(commitId);
  295. developRef.setRefLogMessage("commit: " + revCommit.getShortMessage(), false);
  296. Result developRC = developRef.update();
  297. switch (developRC) {
  298. case NEW:
  299. success = true;
  300. break;
  301. default:
  302. success = false;
  303. }
  304. }
  305. } finally {
  306. revWalk.release();
  307. }
  308. } catch (UnsupportedEncodingException e) {
  309. logger().error(null, e);
  310. } catch (IOException e) {
  311. logger().error(null, e);
  312. } finally {
  313. odi.release();
  314. db.close();
  315. }
  316. return success;
  317. }
  318. }