Skip to content
Permalink
Browse files

Document edit capability via ProseMirror submodule #974

+ New docEdit page with links from docPage and docList
+ Bespoke menu system with full screen edit mode
+ npm required for building client side scripts
+ Ant script added for BuildUI which performs npm commands
+ Update font-awesome to 4.5.0
+ Factor out to JGitUtils common code in BranchTicketService for EditFilePage
	+ getTreeEntries
	+ commitIndex
+ Merge capability for document editing
  • Loading branch information...
paulsputer committed Jan 11, 2016
1 parent 86401c3 commit 2754961708ff0eb6030faf6db2edf8fbdae227d5
@@ -1,3 +1,6 @@
[submodule "src/main/distrib/data/gitignore"]
path = src/main/distrib/data/gitignore
url = https://github.com/github/gitignore.git
[submodule "src/main/js/prosemirror"]
path = src/main/js/prosemirror
url = https://github.com/ProseMirror/prosemirror.git
@@ -1056,4 +1056,21 @@
<mx:install />
</target>


<!--
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Build Gitblit UI via npm
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-->
<target name="buildUI" description="Build Gitblit UI via npm">
<exec executable="npm" dir="src/main/js/" failonerror="true" vmlauncher="false" searchpath="true" >
<arg value="install" />
</exec>

<exec executable="npm" dir="src/main/js/" failonerror="true" vmlauncher="false" searchpath="true" >
<arg value="run" />
<arg value="build" />
</exec>
</target>

</project>
@@ -19,7 +19,6 @@
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -31,21 +30,17 @@
import java.util.concurrent.atomic.AtomicLong;

import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.events.RefsChangedEvent;
import org.eclipse.jgit.events.RefsChangedListener;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefRename;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -338,7 +333,7 @@ private void writeTicketsFile(Repository db, String file, String content, String
Set<String> ignorePaths = new HashSet<String>();
ignorePaths.add(file);

for (DirCacheEntry entry : getTreeEntries(db, ignorePaths)) {
for (DirCacheEntry entry : JGitUtils.getTreeEntries(db, BRANCH, ignorePaths)) {
builder.add(entry);
}

@@ -804,7 +799,7 @@ private DirCache createIndex(Repository db, long ticketId, Change change)
}
}

for (DirCacheEntry entry : getTreeEntries(db, ignorePaths)) {
for (DirCacheEntry entry : JGitUtils.getTreeEntries(db, BRANCH, ignorePaths)) {
builder.add(entry);
}

@@ -816,108 +811,18 @@ private DirCache createIndex(Repository db, long ticketId, Change change)
return newIndex;
}

/**
* Returns all tree entries that do not match the ignore paths.
*
* @param db
* @param ignorePaths
* @param dcBuilder
* @throws IOException
*/
private List<DirCacheEntry> getTreeEntries(Repository db, Collection<String> ignorePaths) throws IOException {
List<DirCacheEntry> list = new ArrayList<DirCacheEntry>();
TreeWalk tw = null;
try {
ObjectId treeId = db.resolve(BRANCH + "^{tree}");
if (treeId == null) {
// branch does not exist yet, could be migrating tickets
return list;
}
tw = new TreeWalk(db);
int hIdx = tw.addTree(treeId);
tw.setRecursive(true);

while (tw.next()) {
String path = tw.getPathString();
CanonicalTreeParser hTree = null;
if (hIdx != -1) {
hTree = tw.getTree(hIdx, CanonicalTreeParser.class);
}
if (!ignorePaths.contains(path)) {
// add all other tree entries
if (hTree != null) {
final DirCacheEntry entry = new DirCacheEntry(path);
entry.setObjectId(hTree.getEntryObjectId());
entry.setFileMode(hTree.getEntryFileMode());
list.add(entry);
}
}
}
} finally {
if (tw != null) {
tw.close();
}
}
return list;
}

private boolean commitIndex(Repository db, DirCache index, String author, String message) throws IOException, ConcurrentRefUpdateException {
final boolean forceCommit = true;
boolean success = false;

ObjectId headId = db.resolve(BRANCH + "^{commit}");
if (headId == null) {
// create the branch
createTicketsBranch(db);
headId = db.resolve(BRANCH + "^{commit}");
}
ObjectInserter odi = db.newObjectInserter();
try {
// Create the in-memory index of the new/updated ticket
ObjectId indexTreeId = index.writeTree(odi);

// Create a commit object
PersonIdent ident = new PersonIdent(author, "gitblit@localhost");
CommitBuilder commit = new CommitBuilder();
commit.setAuthor(ident);
commit.setCommitter(ident);
commit.setEncoding(Constants.ENCODING);
commit.setMessage(message);
commit.setParentId(headId);
commit.setTreeId(indexTreeId);

// Insert the commit into the repository
ObjectId commitId = odi.insert(commit);
odi.flush();

RevWalk revWalk = new RevWalk(db);
try {
RevCommit revCommit = revWalk.parseCommit(commitId);
RefUpdate ru = db.updateRef(BRANCH);
ru.setNewObjectId(commitId);
ru.setExpectedOldObjectId(headId);
ru.setRefLogMessage("commit: " + revCommit.getShortMessage(), false);
Result rc = ru.forceUpdate();
switch (rc) {
case NEW:
case FORCED:
case FAST_FORWARD:
success = true;
break;
case REJECTED:
case LOCK_FAILURE:
throw new ConcurrentRefUpdateException(JGitText.get().couldNotLockHEAD,
ru.getRef(), rc);
default:
throw new JGitInternalException(MessageFormat.format(
JGitText.get().updatingRefFailed, BRANCH, commitId.toString(),
rc));
}
} finally {
revWalk.close();
}
} finally {
odi.close();
}

success = JGitUtils.commitIndex(db, BRANCH, index, headId, forceCommit, author, "gitblit@localhost", message);

return success;
}

@@ -21,6 +21,7 @@
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
@@ -36,16 +37,21 @@
import org.eclipse.jgit.api.FetchCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.TagCommand;
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffEntry.ChangeType;
import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.diff.RawTextComparator;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.LargeObjectException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.StopWalkException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.BlobBasedConfig;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Constants;
@@ -63,6 +69,7 @@
import org.eclipse.jgit.lib.TreeFormatter;
import org.eclipse.jgit.merge.MergeStrategy;
import org.eclipse.jgit.merge.RecursiveMerger;
import org.eclipse.jgit.merge.ThreeWayMerger;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
@@ -75,6 +82,7 @@
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.FetchResult;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
import org.eclipse.jgit.treewalk.filter.OrTreeFilter;
@@ -2597,4 +2605,127 @@ public static String getLfsRepositoryUrl(String baseURL, String repositoryName,
+ "objects/" + oid;

}

/**
* Returns all tree entries that do not match the ignore paths.
*
* @param db
* @param ignorePaths
* @param dcBuilder
* @throws IOException
*/
public static List<DirCacheEntry> getTreeEntries(Repository db, String branch, Collection<String> ignorePaths) throws IOException {
List<DirCacheEntry> list = new ArrayList<DirCacheEntry>();
TreeWalk tw = null;
try {
ObjectId treeId = db.resolve(branch + "^{tree}");
if (treeId == null) {
// branch does not exist yet
return list;
}
tw = new TreeWalk(db);
int hIdx = tw.addTree(treeId);
tw.setRecursive(true);

while (tw.next()) {
String path = tw.getPathString();
CanonicalTreeParser hTree = null;
if (hIdx != -1) {
hTree = tw.getTree(hIdx, CanonicalTreeParser.class);
}
if (!ignorePaths.contains(path)) {
// add all other tree entries
if (hTree != null) {
final DirCacheEntry entry = new DirCacheEntry(path);
entry.setObjectId(hTree.getEntryObjectId());
entry.setFileMode(hTree.getEntryFileMode());
list.add(entry);
}
}
}
} finally {
if (tw != null) {
tw.close();
}
}
return list;
}

public static boolean commitIndex(Repository db, String branch, DirCache index,
ObjectId parentId, boolean forceCommit,
String author, String authorEmail, String message) throws IOException, ConcurrentRefUpdateException {
boolean success = false;

ObjectId headId = db.resolve(branch + "^{commit}");
ObjectId baseId = parentId;
if (baseId == null || headId == null) { return false; }

ObjectInserter odi = db.newObjectInserter();
try {
// Create the in-memory index of the new/updated ticket
ObjectId indexTreeId = index.writeTree(odi);

// Create a commit object
PersonIdent ident = new PersonIdent(author, authorEmail);

if (forceCommit == false) {
ThreeWayMerger merger = MergeStrategy.RECURSIVE.newMerger(db, true);
merger.setObjectInserter(odi);
merger.setBase(baseId);
boolean mergeSuccess = merger.merge(indexTreeId, headId);

if (mergeSuccess) {
indexTreeId = merger.getResultTreeId();
} else {
//Manual merge required
return false;
}
}

CommitBuilder commit = new CommitBuilder();
commit.setAuthor(ident);
commit.setCommitter(ident);
commit.setEncoding(com.gitblit.Constants.ENCODING);
commit.setMessage(message);
commit.setParentId(headId);
commit.setTreeId(indexTreeId);

// Insert the commit into the repository
ObjectId commitId = odi.insert(commit);
odi.flush();

RevWalk revWalk = new RevWalk(db);
try {
RevCommit revCommit = revWalk.parseCommit(commitId);
RefUpdate ru = db.updateRef(branch);
ru.setForceUpdate(forceCommit);
ru.setNewObjectId(commitId);
ru.setExpectedOldObjectId(headId);
ru.setRefLogMessage("commit: " + revCommit.getShortMessage(), false);
Result rc = ru.update();

switch (rc) {
case NEW:
case FORCED:
case FAST_FORWARD:
success = true;
break;
case REJECTED:
case LOCK_FAILURE:
throw new ConcurrentRefUpdateException(JGitText.get().couldNotLockHEAD,
ru.getRef(), rc);
default:
throw new JGitInternalException(MessageFormat.format(
JGitText.get().updatingRefFailed, branch, commitId.toString(),
rc));
}
} finally {
revWalk.close();
}
} finally {
odi.close();
}
return success;
}

}
@@ -12,7 +12,7 @@
<div class="docs" style="margin-top: -10px;">
<!-- doc nav links -->
<div style="float: right;" class="docnav">
<a wicket:id="blameLink"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="rawLink"><wicket:message key="gb.raw"></wicket:message></a>
<a wicket:id="editLink"><wicket:message key="gb.edit"></wicket:message></a> | <a wicket:id="blameLink"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="rawLink"><wicket:message key="gb.raw"></wicket:message></a>
</div>

<!-- document content -->
@@ -24,7 +24,7 @@
<div class="docs">
<!-- doc nav links -->
<div style="float: right;" class="docnav">
<a wicket:id="blameLink"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="rawLink"><wicket:message key="gb.raw"></wicket:message></a>
<a wicket:id="editLink"><wicket:message key="gb.edit"></wicket:message></a> | <a wicket:id="blameLink"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="rawLink"><wicket:message key="gb.raw"></wicket:message></a>
</div>

<!-- document content -->
@@ -85,6 +85,8 @@ public DocPage(PageParameters params) {
}

// document page links
fragment.add(new BookmarkablePageLink<Void>("editLink", EditFilePage.class,
WicketUtils.newPathParameter(repositoryName, objectId, documentPath)));
fragment.add(new BookmarkablePageLink<Void>("blameLink", BlamePage.class,
WicketUtils.newPathParameter(repositoryName, objectId, documentPath)));
fragment.add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class,

0 comments on commit 2754961

Please sign in to comment.
You can’t perform that action at this time.