Merge changes I36da735c,I89dde294
* changes:
ChangeRebuilderImpl: Support deleting patch set 1 of a change
DeleteDraftPatchSetIT: Convert to extension API
diff --git a/Documentation/cmd-index-activate.txt b/Documentation/cmd-index-activate.txt
index 37783ef..6cb7781 100644
--- a/Documentation/cmd-index-activate.txt
+++ b/Documentation/cmd-index-activate.txt
@@ -5,7 +5,7 @@
== SYNOPSIS
--
-'ssh' -p @SSH_PORT@ @SSH_HOST@ 'gerrit index activate <index>'
+'ssh' -p <port> <host> 'gerrit index activate <index>'
--
== DESCRIPTION
diff --git a/Documentation/cmd-index-start.txt b/Documentation/cmd-index-start.txt
index cee283e..0a481e5 100644
--- a/Documentation/cmd-index-start.txt
+++ b/Documentation/cmd-index-start.txt
@@ -5,7 +5,7 @@
== SYNOPSIS
--
-'ssh' -p @SSH_PORT@ @SSH_HOST@ 'gerrit index start <index>'
+'ssh' -p <port> <host> 'gerrit index start <index>'
--
== DESCRIPTION
diff --git a/Documentation/cmd-query.txt b/Documentation/cmd-query.txt
index 0ff59d4..090781b 100644
--- a/Documentation/cmd-query.txt
+++ b/Documentation/cmd-query.txt
@@ -54,15 +54,17 @@
--current-patch-set::
Include information about the current patch set in the results.
+ Note that the information will only be included when the current
+ patch set is visible to the caller.
--patch-sets::
- Include information about all patch sets. If combined with
- the --current-patch-set flag then the current patch set
- information will be output twice, once in each field.
+ Include information about all patch sets visible to the caller.
+ If combined with the --current-patch-set flag then the current patch
+ set information will be output twice, once in each field.
--all-approvals::
- Include information about all patch sets along with the
- approval information for each patch set. If combined with
+ Include information about all patch sets visible to the caller along
+ with the approval information for each patch set. If combined with
the --current-patch-set flag then the current patch set
information will be output twice, once in each field.
@@ -76,7 +78,7 @@
--comments::
Include comments for all changes. If combined with the
--patch-sets flag then all inline/file comments are included for
- each patch set.
+ each patch set that is visible to the caller.
--commit-message::
Include the full commit message in the change description.
diff --git a/Documentation/pgm-init.txt b/Documentation/pgm-init.txt
index aab8aa0..715d589 100644
--- a/Documentation/pgm-init.txt
+++ b/Documentation/pgm-init.txt
@@ -13,6 +13,7 @@
[--list-plugins]
[--install-plugin=<PLUGIN_NAME>]
[--install-all-plugins]
+ [--secure-store-lib]
[--dev]
[--skip-all-downloads]
[--skip-download=<LIBRARY_NAME>]
@@ -63,6 +64,12 @@
This option also works in batch mode. This option cannot be supplied
alongside --install-plugin.
+--secure-store-lib::
+ Path to the jar providing the chosen
+ link:dev-plugins.html#secure-store[SecureStore] implementation class.
+ This option is used the same way as the --new-secure-store-lib option
+ documented in link:pgm-SwitchSecureStore.html[SwitchSecureStore].
+
--install-plugin::
Automatically install plugin with given name without asking.
This option also works in batch mode. This option may be supplied
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index f46092d..da17806 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -1953,6 +1953,9 @@
If only the content type is required, callers should use HEAD to
avoid downloading the encoded file contents.
+If the `base` parameter is set to true, the returned content is from the
+revision that the edit is based on.
+
.Response
----
HTTP/1.1 200 OK
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index fd49487..1955c39 100644
--- a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -496,6 +496,11 @@
revision(r).submit();
}
+ protected PushOneCommit.Result amendChangeAsDraft(String changeId)
+ throws Exception {
+ return amendChange(changeId, "refs/drafts/master");
+ }
+
protected ChangeInfo info(String id)
throws RestApiException {
return gApi.changes().id(id).info();
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/EditPreferencesIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/EditPreferencesIT.java
index df85d0f..dd5bcbb 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/EditPreferencesIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/EditPreferencesIT.java
@@ -43,6 +43,7 @@
assertThat(out.hideLineNumbers).isNull();
assertThat(out.matchBrackets).isTrue();
assertThat(out.autoCloseBrackets).isNull();
+ assertThat(out.showBase).isNull();
assertThat(out.theme).isEqualTo(Theme.DEFAULT);
assertThat(out.keyMapType).isEqualTo(KeyMapType.DEFAULT);
@@ -58,6 +59,7 @@
out.hideLineNumbers = true;
out.matchBrackets = false;
out.autoCloseBrackets = true;
+ out.showBase = true;
out.theme = Theme.TWILIGHT;
out.keyMapType = KeyMapType.EMACS;
@@ -92,6 +94,7 @@
assertThat(out.hideLineNumbers).isEqualTo(in.hideLineNumbers);
assertThat(out.matchBrackets).isNull();
assertThat(out.autoCloseBrackets).isEqualTo(in.autoCloseBrackets);
+ assertThat(out.showBase).isEqualTo(in.showBase);
assertThat(out.theme).isEqualTo(in.theme);
assertThat(out.keyMapType).isEqualTo(in.keyMapType);
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/edit/ChangeEditIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/edit/ChangeEditIT.java
index beedfef..b34951d 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/edit/ChangeEditIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/edit/ChangeEditIT.java
@@ -602,6 +602,11 @@
r.assertOK();
assertThat(readContentFromJson(r)).isEqualTo(
StringUtils.newStringUtf8(CONTENT_NEW2));
+
+ r = adminRestSession.getJsonAccept(urlEditFile(true));
+ r.assertOK();
+ assertThat(readContentFromJson(r)).isEqualTo(
+ StringUtils.newStringUtf8(CONTENT_OLD));
}
@Test
@@ -811,9 +816,14 @@
}
private String urlEditFile() {
+ return urlEditFile(false);
+ }
+
+ private String urlEditFile(boolean base) {
return urlEdit()
+ "/"
- + FILE_NAME;
+ + FILE_NAME
+ + (base ? "?base" : "");
}
private String urlGetFiles() {
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java
index cbaf789..bc206b4 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java
@@ -32,6 +32,7 @@
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.changes.ReviewInput.NotifyHandling;
+import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.client.InheritableBoolean;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeMessageInfo;
@@ -46,12 +47,14 @@
import com.google.gerrit.testutil.TestTimeUtil;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.transport.PushResult;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.Collection;
+import java.util.List;
import java.util.Set;
public abstract class AbstractPushForReview extends AbstractDaemonTest {
@@ -488,4 +491,37 @@
r.assertErrorStatus(
"not Signed-off-by author/committer/uploader in commit message footer");
}
+
+ @Test
+ public void testPushSameCommitTwiceUsingMagicBranchBaseOption()
+ throws Exception {
+ grant(Permission.PUSH, project, "refs/heads/master");
+ PushOneCommit.Result rBase = pushTo("refs/heads/master");
+ rBase.assertOkStatus();
+
+ gApi.projects()
+ .name(project.get())
+ .branch("foo")
+ .create(new BranchInput());
+
+ PushOneCommit push =
+ pushFactory.create(db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT,
+ "b.txt", "anotherContent");
+
+ PushOneCommit.Result r = push.to("refs/for/master");
+ r.assertOkStatus();
+
+ PushResult pr = GitUtil.pushHead(
+ testRepo, "refs/for/foo%base=" + rBase.getCommit().name(), false, false);
+ assertThat(pr.getMessages()).contains("changes: new: 1, refs: 1, done");
+
+ List<ChangeInfo> changes = query(r.getCommit().name());
+ assertThat(changes).hasSize(2);
+ ChangeInfo c1 = get(changes.get(0).id);
+ ChangeInfo c2 = get(changes.get(1).id);
+ assertThat(c1.project).isEqualTo(c2.project);
+ assertThat(c1.branch).isNotEqualTo(c2.branch);
+ assertThat(c1.changeId).isEqualTo(c2.changeId);
+ assertThat(c1.currentRevision).isEqualTo(c2.currentRevision);
+ }
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java
index 24d0e097..a6ea4d2 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java
@@ -28,10 +28,11 @@
import com.google.gerrit.server.config.AnonymousCowardNameProvider;
import com.google.gerrit.server.config.GetServerInfo.ServerInfo;
-import java.nio.file.Path;
-import java.nio.file.Files;
import org.junit.Test;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
public class ServerInfoIT extends AbstractDaemonTest {
@Test
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/AccessIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/AccessIT.java
index 057d902..9a5dfeb 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/AccessIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/AccessIT.java
@@ -14,6 +14,7 @@
package com.google.gerrit.acceptance.rest.project;
import static com.google.common.truth.Truth.assertThat;
+
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.server.config.AllProjectsNameProvider;
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/QueryIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/QueryIT.java
index 3733cd1..2865ff87 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/QueryIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/QueryIT.java
@@ -16,11 +16,13 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assert_;
+import static com.google.gerrit.acceptance.GitUtil.initSsh;
import com.google.common.collect.Lists;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.SshSession;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.client.Side;
@@ -300,13 +302,39 @@
assertThat(changes.get(0).submitRecords.size()).isEqualTo(1);
}
+ @Test
+ public void testQueryWithNonVisibleCurrentPatchSet() throws Exception {
+ String changeId = createChange().getChangeId();
+ amendChangeAsDraft(changeId);
+ String query = "--current-patch-set --patch-sets " + changeId;
+ List<ChangeAttribute> changes = executeSuccessfulQuery(query);
+ assertThat(changes.size()).isEqualTo(1);
+ assertThat(changes.get(0).patchSets).isNotNull();
+ assertThat(changes.get(0).patchSets).hasSize(2);
+ assertThat(changes.get(0).currentPatchSet).isNotNull();
+
+ SshSession userSession = new SshSession(server, user);
+ initSsh(user);
+ userSession.open();
+ changes = executeSuccessfulQuery(query, userSession);
+ assertThat(changes.size()).isEqualTo(1);
+ assertThat(changes.get(0).patchSets).hasSize(1);
+ assertThat(changes.get(0).currentPatchSet).isNull();
+ userSession.close();
+ }
+
+ private List<ChangeAttribute> executeSuccessfulQuery(String params,
+ SshSession session) throws Exception {
+ String rawResponse =
+ session.exec("gerrit query --format=JSON " + params);
+ assert_().withFailureMessage(session.getError())
+ .that(session.hasError()).isFalse();
+ return getChanges(rawResponse);
+ }
+
private List<ChangeAttribute> executeSuccessfulQuery(String params)
throws Exception {
- String rawResponse =
- adminSshSession.exec("gerrit query --format=JSON " + params);
- assert_().withFailureMessage(adminSshSession.getError())
- .that(adminSshSession.hasError()).isFalse();
- return getChanges(rawResponse);
+ return executeSuccessfulQuery(params, adminSshSession);
}
private static List<ChangeAttribute> getChanges(String rawResponse) {
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/EditPreferencesInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/EditPreferencesInfo.java
index ab11612..1f7b84a 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/EditPreferencesInfo.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/EditPreferencesInfo.java
@@ -27,6 +27,7 @@
public Boolean hideLineNumbers;
public Boolean matchBrackets;
public Boolean autoCloseBrackets;
+ public Boolean showBase;
public Theme theme;
public KeyMapType keyMapType;
@@ -43,6 +44,7 @@
i.hideLineNumbers = false;
i.matchBrackets = true;
i.autoCloseBrackets = false;
+ i.showBase = false;
i.theme = Theme.DEFAULT;
i.keyMapType = KeyMapType.DEFAULT;
return i;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/EditPreferences.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/EditPreferences.java
index 8ed7f76..c710fe1 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/EditPreferences.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/EditPreferences.java
@@ -33,6 +33,7 @@
p.hideLineNumbers(in.hideLineNumbers);
p.matchBrackets(in.matchBrackets);
p.autoCloseBrackets(in.autoCloseBrackets);
+ p.showBase(in.showBase);
p.theme(in.theme);
p.keyMapType(in.keyMapType);
return p;
@@ -50,6 +51,7 @@
p.hideLineNumbers = hideLineNumbers();
p.matchBrackets = matchBrackets();
p.autoCloseBrackets = autoCloseBrackets();
+ p.showBase = showBase();
p.theme = theme();
p.keyMapType = keyMapType();
return p;
@@ -76,6 +78,7 @@
public final native void hideLineNumbers(boolean s) /*-{ this.hide_line_numbers = s }-*/;
public final native void matchBrackets(boolean m) /*-{ this.match_brackets = m }-*/;
public final native void autoCloseBrackets(boolean c) /*-{ this.auto_close_brackets = c }-*/;
+ public final native void showBase(boolean s) /*-{ this.show_base = s }-*/;
public final Theme theme() {
String s = themeRaw();
@@ -112,6 +115,7 @@
public final native boolean hideLineNumbers() /*-{ return this.hide_line_numbers || false }-*/;
public final native boolean matchBrackets() /*-{ return this.match_brackets || false }-*/;
public final native boolean autoCloseBrackets() /*-{ return this.auto_close_brackets || false }-*/;
+ public final native boolean showBase() /*-{ return this.show_base || false }-*/;
private native int get(String n, int d) /*-{ return this.hasOwnProperty(n) ? this[n] : d }-*/;
protected EditPreferences() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java
index b499530..e29048a 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java
@@ -46,6 +46,8 @@
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
+import com.google.gwt.event.dom.client.KeyPressEvent;
+import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
@@ -139,6 +141,14 @@
}
},
KeyDownEvent.getType());
+ addDomHandler(
+ new KeyPressHandler() {
+ @Override
+ public void onKeyPress(KeyPressEvent e) {
+ e.stopPropagation();
+ }
+ },
+ KeyPressEvent.getType());
}
@Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeEditApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeEditApi.java
index 0928cd8..f86ddf7 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeEditApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeEditApi.java
@@ -28,7 +28,7 @@
/** REST API helpers to remotely edit a change. */
public class ChangeEditApi {
/** Get file (or commit message) contents. */
- public static void get(PatchSet.Id id, String path,
+ public static void get(PatchSet.Id id, String path, boolean base,
HttpCallback<NativeString> cb) {
RestApi api;
if (id.get() != 0) {
@@ -36,13 +36,19 @@
// exist for the caller, or is not currently active.
api = ChangeApi.revision(id).view("files").id(path).view("content");
} else if (Patch.COMMIT_MSG.equals(path)) {
- api = editMessage(id.getParentKey().get());
+ api = editMessage(id.getParentKey().get()).addParameter("base", base);
} else {
- api = editFile(id.getParentKey().get(), path);
+ api = editFile(id.getParentKey().get(), path).addParameter("base", base);
}
api.get(cb);
}
+ /** Get file (or commit message) contents of the edit. */
+ public static void get(PatchSet.Id id, String path,
+ HttpCallback<NativeString> cb) {
+ get(id, path, false, cb);
+ }
+
/** Get meta info for change edit. */
public static void getMeta(PatchSet.Id id, String path,
AsyncCallback<EditFileInfo> cb) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.java
index d2b740e..39b85cf 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.java
@@ -42,6 +42,7 @@
import com.google.gwt.user.client.ui.ImageResourceRenderer;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwtorm.client.KeyUtil;
+
import net.codemirror.lib.CodeMirror;
import java.util.List;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.java
index 4ca4a63..0a27ea1 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.java
@@ -69,6 +69,7 @@
@UiField ToggleButton lineNumbers;
@UiField ToggleButton matchBrackets;
@UiField ToggleButton autoCloseBrackets;
+ @UiField ToggleButton showBase;
@UiField ListBox theme;
@UiField ListBox keyMap;
@UiField Button apply;
@@ -104,6 +105,7 @@
lineNumbers.setValue(prefs.hideLineNumbers());
matchBrackets.setValue(prefs.matchBrackets());
autoCloseBrackets.setValue(prefs.autoCloseBrackets());
+ showBase.setValue(prefs.showBase());
setTheme(prefs.theme());
setKeyMapType(prefs.keyMapType());
}
@@ -114,7 +116,7 @@
if (v != null && v.length() > 0) {
prefs.tabSize(Math.max(1, Integer.parseInt(v)));
if (view != null) {
- view.getEditor().setOption("tabSize", v);
+ view.setOption("tabSize", v);
}
}
}
@@ -149,7 +151,7 @@
// don't let user shoot himself in the foot.
prefs.cursorBlinkRate(Math.max(0, Integer.parseInt(v)));
if (view != null) {
- view.getEditor().setOption("cursorBlinkRate", prefs.cursorBlinkRate());
+ view.setOption("cursorBlinkRate", prefs.cursorBlinkRate());
}
}
}
@@ -159,7 +161,7 @@
prefs.hideTopMenu(!e.getValue());
if (view != null) {
Gerrit.setHeaderVisible(!prefs.hideTopMenu());
- view.resizeCodeMirror();
+ view.adjustHeight();
}
}
@@ -199,7 +201,7 @@
void onMatchBrackets(ValueChangeEvent<Boolean> e) {
prefs.matchBrackets(e.getValue());
if (view != null) {
- view.getEditor().setOption("matchBrackets", prefs.matchBrackets());
+ view.setOption("matchBrackets", prefs.matchBrackets());
}
}
@@ -211,6 +213,15 @@
}
}
+ @UiHandler("showBase")
+ void onShowBase(ValueChangeEvent<Boolean> e) {
+ Boolean value = e.getValue();
+ prefs.showBase(value);
+ if (view != null) {
+ view.showBase.setValue(value, true);
+ }
+ }
+
@UiHandler("theme")
void onTheme(@SuppressWarnings("unused") ChangeEvent e) {
final Theme newTheme = Theme.valueOf(theme.getValue(theme.getSelectedIndex()));
@@ -219,13 +230,7 @@
ThemeLoader.loadTheme(newTheme, new GerritCallback<Void>() {
@Override
public void onSuccess(Void result) {
- view.getEditor().operation(new Runnable() {
- @Override
- public void run() {
- String t = newTheme.name().toLowerCase();
- view.getEditor().setOption("theme", t);
- }
- });
+ view.setTheme(newTheme);
}
});
}
@@ -237,7 +242,7 @@
keyMap.getValue(keyMap.getSelectedIndex()));
prefs.keyMapType(keyMapType);
if (view != null) {
- view.getEditor().setOption("keyMap", keyMapType.name().toLowerCase());
+ view.setOption("keyMap", keyMapType.name().toLowerCase());
}
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.ui.xml
index c07ac56..b5b0b01 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.ui.xml
@@ -245,6 +245,13 @@
</g:ToggleButton></td>
</tr>
<tr>
+ <th><ui:msg>Show Base Version</ui:msg></th>
+ <td><g:ToggleButton ui:field='showBase'>
+ <g:upFace><ui:msg>Hide</ui:msg></g:upFace>
+ <g:downFace><ui:msg>Show</ui:msg></g:downFace>
+ </g:ToggleButton></td>
+ </tr>
+ <tr>
<td></td>
<td>
<g:Button ui:field='apply' styleName='{style.apply}'>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java
index 67df4c6..b7adf3b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java
@@ -43,6 +43,7 @@
import com.google.gerrit.client.ui.Screen;
import com.google.gerrit.common.PageLinks;
import com.google.gerrit.extensions.client.KeyMapType;
+import com.google.gerrit.extensions.client.Theme;
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gwt.core.client.GWT;
@@ -50,11 +51,14 @@
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
+import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.resources.client.CssResource;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
@@ -63,17 +67,21 @@
import com.google.gwt.user.client.Window.ClosingHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.ImageResourceRenderer;
import com.google.gwtexpui.globalkey.client.GlobalKey;
import com.google.gwtexpui.safehtml.client.SafeHtml;
+import net.codemirror.addon.AddonInjector;
+import net.codemirror.addon.Addons;
import net.codemirror.lib.CodeMirror;
import net.codemirror.lib.CodeMirror.ChangesHandler;
import net.codemirror.lib.CodeMirror.CommandRunner;
import net.codemirror.lib.Configuration;
import net.codemirror.lib.KeyMap;
+import net.codemirror.lib.MergeView;
import net.codemirror.lib.Pos;
import net.codemirror.mode.ModeInfo;
import net.codemirror.mode.ModeInjector;
@@ -85,14 +93,23 @@
interface Binder extends UiBinder<HTMLPanel, EditScreen> {}
private static final Binder uiBinder = GWT.create(Binder.class);
+ interface Style extends CssResource {
+ String fullWidth();
+ String base();
+ String hideBase();
+ }
+
private final PatchSet.Id base;
private final PatchSet.Id revision;
private final String path;
private final int startLine;
private EditPreferences prefs;
private EditPreferencesAction editPrefsAction;
- private CodeMirror cm;
+ private MergeView mv;
+ private CodeMirror cmBase;
+ private CodeMirror cmEdit;
private HttpResponse<NativeString> content;
+ private HttpResponse<NativeString> baseContent;
private EditFileInfo editFileInfo;
private JsArray<DiffWebLinkInfo> diffLinks;
@@ -103,9 +120,11 @@
@UiField Element cursLine;
@UiField Element cursCol;
@UiField Element dirty;
+ @UiField CheckBox showBase;
@UiField Button close;
@UiField Button save;
@UiField Element editor;
+ @UiField Style style;
private HandlerRegistration resizeHandler;
private HandlerRegistration closeHandler;
@@ -145,9 +164,21 @@
public void onSuccess(Void result) {
// Load theme after CM library to ensure theme can override CSS.
ThemeLoader.loadTheme(prefs.theme(), themeCallback);
-
group2.done();
- group3.done();
+
+ new AddonInjector().add(Addons.I.merge_bundled().getName()).inject(
+ new AsyncCallback<Void>() {
+ @Override
+ public void onFailure(Throwable caught) {
+ }
+
+ @Override
+ public void onSuccess(Void result) {
+ if (!prefs.showBase() || revision.get() > 0) {
+ group3.done();
+ }
+ }
+ });
}
@Override
@@ -180,13 +211,30 @@
public void onFailure(Throwable e) {
}
}));
+
+ if (prefs.showBase()) {
+ ChangeEditApi.get(revision, path, true /* base */,
+ group1.addFinal(new HttpCallback<NativeString>() {
+ @Override
+ public void onSuccess(HttpResponse<NativeString> fc) {
+ baseContent = fc;
+ group3.done();
+ }
+
+ @Override
+ public void onFailure(Throwable e) {
+ }
+ }));
+ } else {
+ group1.done();
+ }
} else {
// TODO(davido): We probably want to create dedicated GET EditScreenMeta
// REST endpoint. Abuse GET diff for now, as it retrieves links we need.
DiffApi.diff(revision, path)
.base(base)
.webLinksOnly()
- .get(group1.add(new AsyncCallback<DiffInfo>() {
+ .get(group1.addFinal(new AsyncCallback<DiffInfo>() {
@Override
public void onSuccess(DiffInfo diffInfo) {
diffLinks = diffInfo.webLinks();
@@ -205,6 +253,10 @@
@Override
public void onSuccess(HttpResponse<NativeString> fc) {
content = fc;
+ if (revision.get() > 0) {
+ baseContent = fc;
+ }
+
if (prefs.syntaxHighlighting()) {
injectMode(fc.getContentType(), modeCallback);
} else {
@@ -227,14 +279,16 @@
group3.addListener(new ScreenLoadCallback<Void>(this) {
@Override
protected void preDisplay(Void result) {
- initEditor(content);
+ initEditor();
renderLinks(editFileInfo, diffLinks);
editFileInfo = null;
diffLinks = null;
+
+ showBase.setValue(prefs.showBase(), true);
+ cmBase.refresh();
}
});
- group1.done();
}
@Override
@@ -251,14 +305,15 @@
localKeyMap.on("Ctrl-S", save());
}
- cm.addKeyMap(localKeyMap);
+ cmBase.addKeyMap(localKeyMap);
+ cmEdit.addKeyMap(localKeyMap);
}
private Runnable gotoLine() {
return new Runnable() {
@Override
public void run() {
- cm.execCommand("jumpToLine");
+ cmEdit.execCommand("jumpToLine");
}
};
}
@@ -274,36 +329,36 @@
resizeHandler = Window.addResizeHandler(new ResizeHandler() {
@Override
public void onResize(ResizeEvent event) {
- cm.adjustHeight(header.getOffsetHeight());
+ adjustHeight();
}
});
closeHandler = Window.addWindowClosingHandler(new ClosingHandler() {
@Override
public void onWindowClosing(ClosingEvent event) {
- if (!cm.isClean(generation)) {
+ if (!cmEdit.isClean(generation)) {
event.setMessage(EditConstants.I.closeUnsavedChanges());
}
}
});
- generation = cm.changeGeneration(true);
+ generation = cmEdit.changeGeneration(true);
setClean(true);
- cm.on(new ChangesHandler() {
+ cmEdit.on(new ChangesHandler() {
@Override
public void handle(CodeMirror cm) {
setClean(cm.isClean(generation));
}
});
- cm.adjustHeight(header.getOffsetHeight());
- cm.on("cursorActivity", updateCursorPosition());
+ adjustHeight();
+ cmEdit.on("cursorActivity", updateCursorPosition());
setShowTabs(prefs.showTabs());
setLineLength(prefs.lineLength());
- cm.refresh();
- cm.focus();
+ cmEdit.refresh();
+ cmEdit.focus();
if (startLine > 0) {
- cm.scrollToLine(startLine);
+ cmEdit.scrollToLine(startLine);
}
updateActiveLine();
editPrefsAction = new EditPreferencesAction(this, prefs);
@@ -312,8 +367,11 @@
@Override
protected void onUnload() {
super.onUnload();
- if (cm != null) {
- cm.getWrapperElement().removeFromParent();
+ if (cmBase != null) {
+ cmBase.getWrapperElement().removeFromParent();
+ }
+ if (cmEdit != null) {
+ cmEdit.getWrapperElement().removeFromParent();
}
if (resizeHandler != null) {
resizeHandler.removeHandler();
@@ -327,7 +385,7 @@
}
CodeMirror getEditor() {
- return cm;
+ return cmEdit;
}
@UiHandler("editSettings")
@@ -342,41 +400,127 @@
@UiHandler("close")
void onClose(@SuppressWarnings("unused") ClickEvent e) {
- if (cm.isClean(generation)
+ if (cmEdit.isClean(generation)
|| Window.confirm(EditConstants.I.cancelUnsavedChanges())) {
upToChange();
}
}
+ private void displayBase() {
+ cmBase.getWrapperElement().getParentElement()
+ .removeClassName(style.hideBase());
+ cmEdit.getWrapperElement().getParentElement()
+ .removeClassName(style.fullWidth());
+ mv.getGapElement().removeClassName(style.hideBase());
+ setCmBaseValue();
+ setLineLength(prefs.lineLength());
+ cmBase.refresh();
+ }
+
+ @UiHandler("showBase")
+ void onShowBase(ValueChangeEvent<Boolean> e) {
+ boolean shouldShow = e.getValue();
+ if (shouldShow) {
+ if (baseContent == null) {
+ ChangeEditApi.get(revision, path, true /* base */,
+ new HttpCallback<NativeString>() {
+ @Override
+ public void onSuccess(HttpResponse<NativeString> fc) {
+ baseContent = fc;
+ displayBase();
+ }
+
+ @Override
+ public void onFailure(Throwable e) {
+ }
+ });
+ } else {
+ displayBase();
+ }
+ } else {
+ cmBase.getWrapperElement().getParentElement()
+ .addClassName(style.hideBase());
+ cmEdit.getWrapperElement().getParentElement()
+ .addClassName(style.fullWidth());
+ mv.getGapElement().addClassName(style.hideBase());
+ }
+ mv.setShowDifferences(shouldShow);
+ }
+
+ void setOption(String option, String value) {
+ cmBase.setOption(option, value);
+ cmEdit.setOption(option, value);
+ }
+
+ void setOption(String option, boolean value) {
+ cmBase.setOption(option, value);
+ cmEdit.setOption(option, value);
+ }
+
+ void setOption(String option, double value) {
+ cmBase.setOption(option, value);
+ cmEdit.setOption(option, value);
+ }
+
+ void setTheme(final Theme newTheme) {
+ cmBase.operation(new Runnable() {
+ @Override
+ public void run() {
+ cmBase.setOption("theme", newTheme.name().toLowerCase());
+ }
+ });
+ cmEdit.operation(new Runnable() {
+ @Override
+ public void run() {
+ cmEdit.setOption("theme", newTheme.name().toLowerCase());
+ }
+ });
+ }
+
void setLineLength(int length) {
- cm.extras().lineLength(
- Patch.COMMIT_MSG.equals(path) ? 72 : length);
+ int adjustedLength = Patch.COMMIT_MSG.equals(path) ? 72 : length;
+ cmBase.extras().lineLength(adjustedLength);
+ cmEdit.extras().lineLength(adjustedLength);
}
void setIndentUnit(int indent) {
- cm.setOption("indentUnit",
- Patch.COMMIT_MSG.equals(path) ? 2 : indent);
+ cmEdit.setOption("indentUnit", Patch.COMMIT_MSG.equals(path) ? 2 : indent);
}
void setShowLineNumbers(boolean show) {
- cm.setOption("lineNumbers", show);
+ cmBase.setOption("lineNumbers", show);
+ cmEdit.setOption("lineNumbers", show);
}
void setShowWhitespaceErrors(final boolean show) {
- cm.operation(new Runnable() {
+ cmBase.operation(new Runnable() {
@Override
public void run() {
- cm.setOption("showTrailingSpace", show);
+ cmBase.setOption("showTrailingSpace", show);
+ }
+ });
+ cmEdit.operation(new Runnable() {
+ @Override
+ public void run() {
+ cmEdit.setOption("showTrailingSpace", show);
}
});
}
void setShowTabs(boolean show) {
- cm.extras().showTabs(show);
+ cmBase.extras().showTabs(show);
+ cmEdit.extras().showTabs(show);
}
- void resizeCodeMirror() {
- cm.adjustHeight(header.getOffsetHeight());
+ void adjustHeight() {
+ int height = header.getOffsetHeight();
+ int rest = Gerrit.getHeaderFooterHeight()
+ + height
+ + 5; // Estimate
+ mv.getGapElement().getStyle().setHeight(
+ Window.getClientHeight() - rest, Unit.PX);
+ cmBase.adjustHeight(height);
+ cmEdit.adjustHeight(height);
}
void setSyntaxHighlighting(boolean b) {
@@ -386,7 +530,8 @@
injectMode(mode, new AsyncCallback<Void>() {
@Override
public void onSuccess(Void result) {
- cm.setOption("mode", mode);
+ cmBase.setOption("mode", mode);
+ cmEdit.setOption("mode", mode);
}
@Override
@@ -395,7 +540,8 @@
}
});
} else {
- cm.setOption("mode", (String) null);
+ cmBase.setOption("mode", (String) null);
+ cmEdit.setOption("mode", (String) null);
}
}
@@ -403,16 +549,16 @@
Gerrit.display(PageLinks.toChangeInEditMode(revision.getParentKey()));
}
- private void initEditor(HttpResponse<NativeString> file) {
+ private void initEditor() {
ModeInfo mode = null;
- String content = "";
- if (file != null && file.getResult() != null) {
- content = file.getResult().asString();
+ String editContent = "";
+ if (content != null && content.getResult() != null) {
+ editContent = content.getResult().asString();
if (prefs.syntaxHighlighting()) {
- mode = ModeInfo.findMode(file.getContentType(), path);
+ mode = ModeInfo.findMode(content.getContentType(), path);
}
}
- cm = CodeMirror.create(editor, Configuration.create()
+ mv = MergeView.create(editor, Configuration.create()
.set("autoCloseBrackets", prefs.autoCloseBrackets())
.set("cursorBlinkRate", prefs.cursorBlinkRate())
.set("cursorHeight", 0.85)
@@ -422,13 +568,19 @@
.set("lineWrapping", false)
.set("matchBrackets", prefs.matchBrackets())
.set("mode", mode != null ? mode.mime() : null)
- .set("readOnly", false)
+ .set("origLeft", editContent)
.set("scrollbarStyle", "overlay")
.set("showTrailingSpace", prefs.showWhitespaceErrors())
.set("styleSelectedText", true)
.set("tabSize", prefs.tabSize())
.set("theme", prefs.theme().name().toLowerCase())
- .set("value", content));
+ .set("value", ""));
+
+ cmBase = mv.leftOriginal();
+ cmBase.getWrapperElement().addClassName(style.base());
+ cmEdit = mv.editor();
+ setCmBaseValue();
+ cmEdit.setValue(editContent);
CodeMirror.addCommand("save", new CommandRunner() {
@Override
@@ -487,7 +639,7 @@
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
- cm.operation(new Runnable() {
+ cmEdit.operation(new Runnable() {
@Override
public void run() {
updateActiveLine();
@@ -500,10 +652,10 @@
}
private void updateActiveLine() {
- Pos p = cm.getCursor("end");
+ Pos p = cmEdit.getCursor("end");
cursLine.setInnerText(Integer.toString(p.line() + 1));
cursCol.setInnerText(Integer.toString(p.ch() + 1));
- cm.extras().activeLine(cm.getLineHandleVisualStart(p.line()));
+ cmEdit.extras().activeLine(cmEdit.getLineHandleVisualStart(p.line()));
}
private void setClean(boolean clean) {
@@ -516,23 +668,23 @@
return new Runnable() {
@Override
public void run() {
- if (!cm.isClean(generation)) {
+ if (!cmEdit.isClean(generation)) {
close.setEnabled(false);
- String text = cm.getValue();
+ String text = cmEdit.getValue();
if (Patch.COMMIT_MSG.equals(path)) {
String trimmed = text.trim() + "\r";
if (!trimmed.equals(text)) {
text = trimmed;
- cm.setValue(text);
+ cmEdit.setValue(text);
}
}
- final int g = cm.changeGeneration(false);
+ final int g = cmEdit.changeGeneration(false);
ChangeEditApi.put(revision.getParentKey().get(), path, text,
new GerritCallback<VoidResult>() {
@Override
public void onSuccess(VoidResult result) {
generation = g;
- setClean(cm.isClean(g));
+ setClean(cmEdit.isClean(g));
}
@Override
public void onFailure(final Throwable caught) {
@@ -547,4 +699,10 @@
private void injectMode(String type, AsyncCallback<Void> cb) {
new ModeInjector().add(type).inject(cb);
}
+
+ private void setCmBaseValue() {
+ cmBase.setValue(baseContent != null && baseContent.getResult() != null
+ ? baseContent.getResult().asString()
+ : "");
+ }
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.ui.xml
index 88af398..34282c8 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.ui.xml
@@ -17,8 +17,11 @@
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'>
<ui:with field='ico' type='com.google.gerrit.client.GerritResources'/>
- <ui:style gss='false'>
+ <ui:style gss='false' type='com.google.gerrit.client.editor.EditScreen.Style'>
@external .CodeMirror, .CodeMirror-cursor;
+ @external .CodeMirror-merge-2pane, .CodeMirror-merge-pane;
+ @external .CodeMirror-merge-gap;
+ @external .CodeMirror-scroll, .CodeMirror-overlayscroll-vertical;
.header {
position: relative;
@@ -123,12 +126,28 @@
cursor: pointer;
outline: none;
}
+
+ .hideBase.CodeMirror-merge-pane {
+ display: none;
+ }
+
+ .hideBase.CodeMirror-merge-gap {
+ display: none;
+ }
+
+ .CodeMirror-merge-2pane .fullWidth.CodeMirror-merge-pane {
+ width: 100%;
+ }
+
+ /* Hide the vertical scrollbar on the base side. The edit side controls
+ both views */
+ .base .CodeMirror-scroll { margin-right: -42px; }
+ .base .CodeMirror-overlayscroll-vertical { display: none !important; }
</ui:style>
<g:HTMLPanel styleName='{style.header}'>
<div class='{style.headerLine}' ui:field='header'>
<div class='{style.headerButtons}'>
<g:Button ui:field='close'
- styleName=''
title='Close file and return to change'>
<ui:attribute name='title'/>
<div><ui:msg>Close</ui:msg></div>
@@ -142,6 +161,11 @@
</div>
<span class='{style.path}'><span ui:field='project'/> / <span ui:field='filePath'/></span>
<div class='{style.navigation}'>
+ <g:Label text='Show Base' styleName='{style.linkPanel}'></g:Label>
+ <g:CheckBox ui:field='showBase' checked='true' styleName='{style.linkPanel}'
+ title='Show Base Version'>
+ <ui:attribute name='title'/>
+ </g:CheckBox>
<g:FlowPanel ui:field='linkPanel' styleName='{style.linkPanel}'/>
<g:Image
ui:field='editSettings'
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/CallbackGroup.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/CallbackGroup.java
index 071ca72..009deaf 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/CallbackGroup.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/CallbackGroup.java
@@ -78,23 +78,7 @@
public <T> HttpCallback<T> add(HttpCallback<T> cb) {
checkFinalAdded();
- if (failed) {
- cb.onFailure(failedThrowable);
- return new HttpCallback<T>() {
- @Override
- public void onSuccess(HttpResponse<T> result) {
- }
-
- @Override
- public void onFailure(Throwable caught) {
- }
- };
- }
-
- HttpCallbackImpl<T> w = new HttpCallbackImpl<>(cb);
- callbacks.add(w);
- remaining.add(w);
- return w;
+ return handleAdd(cb);
}
public <T> Callback<T> addFinal(final AsyncCallback<T> cb) {
@@ -103,6 +87,12 @@
return handleAdd(cb);
}
+ public <T> HttpCallback<T> addFinal(final HttpCallback<T> cb) {
+ checkFinalAdded();
+ finalAdded = true;
+ return handleAdd(cb);
+ }
+
public void done() {
finalAdded = true;
apply();
@@ -161,6 +151,26 @@
return wrapper;
}
+ private <T> HttpCallback<T> handleAdd(HttpCallback<T> cb) {
+ if (failed) {
+ cb.onFailure(failedThrowable);
+ return new HttpCallback<T>() {
+ @Override
+ public void onSuccess(HttpResponse<T> result) {
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ }
+ };
+ }
+
+ HttpCallbackImpl<T> w = new HttpCallbackImpl<>(cb);
+ callbacks.add(w);
+ remaining.add(w);
+ return w;
+ }
+
private void checkFinalAdded() {
if (finalAdded) {
throw new IllegalStateException("final callback already added");
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/addon/AddonInjector.java b/gerrit-gwtui/src/main/java/net/codemirror/addon/AddonInjector.java
new file mode 100644
index 0000000..efed451
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/net/codemirror/addon/AddonInjector.java
@@ -0,0 +1,95 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package net.codemirror.addon;
+
+import com.google.gwt.safehtml.shared.SafeUri;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import net.codemirror.lib.Loader;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class AddonInjector {
+ private static final Map<String, SafeUri> addonUris = new HashMap<>();
+ static {
+ addonUris.put(Addons.I.merge_bundled().getName(),
+ Addons.I.merge_bundled().getSafeUri());
+ }
+
+ public static SafeUri getAddonScriptUri(String addon) {
+ return addonUris.get(addon);
+ }
+
+ private static boolean canLoad(String addon) {
+ return getAddonScriptUri(addon) != null;
+ }
+
+ private final Set<String> loading = new HashSet<>();
+ private int pending;
+ private AsyncCallback<Void> appCallback;
+
+ public AddonInjector add(String name) {
+ if (name == null) {
+ return this;
+ }
+
+ if (!canLoad(name)) {
+ Logger.getLogger("net.codemirror").log(
+ Level.WARNING,
+ "CodeMirror addon " + name + " not configured.");
+ return this;
+ }
+
+ loading.add(name);
+ return this;
+ }
+
+ public void inject(AsyncCallback<Void> appCallback) {
+ this.appCallback = appCallback;
+ for (String addon : loading) {
+ beginLoading(addon);
+ }
+ if (pending == 0) {
+ appCallback.onSuccess(null);
+ }
+ }
+
+ private void beginLoading(final String addon) {
+ pending++;
+ Loader.injectScript(
+ getAddonScriptUri(addon),
+ new AsyncCallback<Void>() {
+ @Override
+ public void onSuccess(Void result) {
+ pending--;
+ if (pending == 0) {
+ appCallback.onSuccess(null);
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ if (--pending == 0) {
+ appCallback.onFailure(caught);
+ }
+ }
+ });
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/addon/Addons.java b/gerrit-gwtui/src/main/java/net/codemirror/addon/Addons.java
new file mode 100644
index 0000000..5365cc5
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/net/codemirror/addon/Addons.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package net.codemirror.addon;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.resources.client.ClientBundle;
+import com.google.gwt.resources.client.DataResource;
+import com.google.gwt.resources.client.DataResource.DoNotEmbed;
+
+public interface Addons extends ClientBundle {
+ public static final Addons I = GWT.create(Addons.class);
+
+ @Source("merge_bundled.js") @DoNotEmbed DataResource merge_bundled();
+}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/MergeView.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/MergeView.java
new file mode 100644
index 0000000..d7e1430
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/MergeView.java
@@ -0,0 +1,51 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package net.codemirror.lib;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.dom.client.Element;
+
+/** Object that represents a text marker within CodeMirror */
+public class MergeView extends JavaScriptObject {
+ public static MergeView create(Element p, Configuration cfg) {
+ MergeView mv = newMergeView(p, cfg);
+ Extras.attach(mv.leftOriginal());
+ Extras.attach(mv.editor());
+ return mv;
+ }
+
+ private static native MergeView newMergeView(Element p, Configuration cfg) /*-{
+ return $wnd.CodeMirror.MergeView(p, cfg);
+ }-*/;
+
+ public final native CodeMirror leftOriginal() /*-{
+ return this.leftOriginal();
+ }-*/;
+
+ public final native CodeMirror editor() /*-{
+ return this.editor();
+ }-*/;
+
+ public final native void setShowDifferences(boolean b) /*-{
+ this.setShowDifferences(b);
+ }-*/;
+
+ public final native Element getGapElement() /*-{
+ return $doc.getElementsByClassName("CodeMirror-merge-gap")[0];
+ }-*/;
+
+ protected MergeView() {
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java b/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java
index fb7bfad..a081197 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java
@@ -327,6 +327,7 @@
return effectiveGroups;
}
+ @SuppressWarnings("deprecation")
@Override
public Set<Change.Id> getStarredChanges() {
if (starredChanges == null) {
@@ -354,6 +355,7 @@
starredChanges = null;
}
+ @SuppressWarnings("deprecation")
public void asyncStarredChanges() {
if (starredChanges == null && starredChangesUtil != null) {
starredQuery = starredChangesUtil.queryFromIndex(accountId);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
index 0e2f811..b8bd905 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
@@ -16,10 +16,10 @@
import static com.google.gerrit.server.account.CapabilityUtils.checkRequiresCapability;
+import com.google.gerrit.extensions.api.access.ProjectAccessInfo;
import com.google.gerrit.extensions.api.projects.BranchApi;
import com.google.gerrit.extensions.api.projects.BranchInfo;
import com.google.gerrit.extensions.api.projects.ChildProjectApi;
-import com.google.gerrit.extensions.api.access.ProjectAccessInfo;
import com.google.gerrit.extensions.api.projects.ProjectApi;
import com.google.gerrit.extensions.api.projects.ProjectInput;
import com.google.gerrit.extensions.api.projects.PutDescriptionInput;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeEdits.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeEdits.java
index c821cf6..e508659 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeEdits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeEdits.java
@@ -441,10 +441,14 @@
}
}
- @Singleton
public static class Get implements RestReadView<ChangeEditResource> {
private final FileContentUtil fileContentUtil;
+ @Option(name = "--base", aliases = {"-b"},
+ usage = "whether to load the content on the base revision instead of the"
+ + " change edit")
+ private boolean base;
+
@Inject
Get(FileContentUtil fileContentUtil) {
this.fileContentUtil = fileContentUtil;
@@ -454,9 +458,13 @@
public Response<?> apply(ChangeEditResource rsrc)
throws IOException {
try {
+ ChangeEdit edit = rsrc.getChangeEdit();
return Response.ok(fileContentUtil.getContent(
rsrc.getControl().getProjectControl().getProjectState(),
- ObjectId.fromString(rsrc.getChangeEdit().getRevision().get()),
+ base
+ ? ObjectId.fromString(
+ edit.getBasePatchSet().getRevision().get())
+ : ObjectId.fromString(edit.getRevision().get()),
rsrc.getPath()));
} catch (ResourceNotFoundException rnfe) {
return Response.none();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
index efa6174..7984c76b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -1503,8 +1503,8 @@
private void selectNewAndReplacedChangesFromMagicBranch() {
newChanges = Lists.newArrayList();
- SetMultimap<ObjectId, Ref> existing = changeRefsById();
- GroupCollector groupCollector = GroupCollector.create(refsById, db, psUtil,
+ SetMultimap<ObjectId, Ref> existing = HashMultimap.create();
+ GroupCollector groupCollector = GroupCollector.create(changeRefsById(), db, psUtil,
notesFactory, project.getNameKey());
rp.getRevWalk().reset();
@@ -1525,6 +1525,7 @@
} else {
markHeadsAsUninteresting(
rp.getRevWalk(),
+ existing,
magicBranch.ctl != null ? magicBranch.ctl.getRefName() : null);
}
@@ -1681,15 +1682,23 @@
}
}
- private void markHeadsAsUninteresting(RevWalk rw, @Nullable String forRef) {
+ private void markHeadsAsUninteresting(
+ final RevWalk walk,
+ SetMultimap<ObjectId, Ref> existing,
+ @Nullable String forRef) {
for (Ref ref : allRefs.values()) {
- if ((ref.getName().startsWith(R_HEADS) || ref.getName().equals(forRef))
- && ref.getObjectId() != null) {
+ if (ref.getObjectId() == null) {
+ continue;
+ } else if (ref.getName().startsWith(REFS_CHANGES)) {
+ existing.put(ref.getObjectId(), ref);
+ } else if (ref.getName().startsWith(R_HEADS)
+ || (forRef != null && forRef.equals(ref.getName()))) {
try {
- rw.markUninteresting(rw.parseCommit(ref.getObjectId()));
+ walk.markUninteresting(walk.parseCommit(ref.getObjectId()));
} catch (IOException e) {
log.warn(String.format("Invalid ref %s in %s",
ref.getName(), project.getName()), e);
+ continue;
}
}
}
@@ -2332,11 +2341,11 @@
if (!(parsedObject instanceof RevCommit)) {
return;
}
+ SetMultimap<ObjectId, Ref> existing = HashMultimap.create();
walk.markStart((RevCommit)parsedObject);
- markHeadsAsUninteresting(walk, cmd.getRefName());
- Set<ObjectId> existing = changeRefsById().keySet();
+ markHeadsAsUninteresting(walk, existing, cmd.getRefName());
for (RevCommit c; (c = walk.next()) != null;) {
- if (existing.contains(c)) {
+ if (existing.keySet().contains(c)) {
continue;
} else if (!validCommit(walk, ctl, cmd, c)) {
break;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategyFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategyFactory.java
index ce34a53..1e3fdbe 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategyFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategyFactory.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server.git.strategy;
-import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.api.changes.ReviewInput.NotifyHandling;
+import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
index a4c10ee..76dd030 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -21,6 +21,8 @@
import com.google.auto.value.AutoValue;
import com.google.common.base.MoreObjects;
import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
@@ -833,13 +835,29 @@
return patchSets;
}
- public void setPatchSets(List<PatchSet> patchSets) {
+ /**
+ * @return patches for the change visible to the current user.
+ * @throws OrmException an error occurred reading the database.
+ */
+ public Collection<PatchSet> visiblePatchSets() throws OrmException {
+ return FluentIterable.from(patchSets()).filter(new Predicate<PatchSet>() {
+ @Override
+ public boolean apply(PatchSet input) {
+ try {
+ return changeControl().isPatchVisible(input, db);
+ } catch (OrmException e) {
+ return false;
+ }
+ }}).toList();
+ }
+
+public void setPatchSets(Collection<PatchSet> patchSets) {
this.currentPatchSet = null;
this.patchSets = patchSets;
}
/**
- * @return patch set with the given ID, or null if it does not exist.
+ * @return patch with the given ID, or null if it does not exist.
* @throws OrmException an error occurred reading the database.
*/
public PatchSet patchSet(PatchSet.Id psId) throws OrmException {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index 2679f29..230bf31 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -660,6 +660,7 @@
return Predicate.or(p);
}
+ @SuppressWarnings("deprecation")
private Predicate<ChangeData> starredby(Account.Id who)
throws QueryParseException {
return args.getSchema().hasField(ChangeField.STARREDBY)
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LabelPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LabelPredicate.java
index 83364c3..2e2454d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LabelPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LabelPredicate.java
@@ -141,10 +141,10 @@
List<Predicate<ChangeData>> r =
Lists.newArrayListWithCapacity(2 * MAX_LABEL_VALUE);
for (int i = 1; i <= MAX_LABEL_VALUE; i++) {
- r.add(not(equalsLabelPredicate(args, label, i)));
- r.add(not(equalsLabelPredicate(args, label, -i)));
+ r.add(equalsLabelPredicate(args, label, i));
+ r.add(equalsLabelPredicate(args, label, -i));
}
- return and(r);
+ return not(or(r));
}
private static Predicate<ChangeData> equalsLabelPredicate(Args args,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
index 8f1e48f..00ecdb2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
@@ -275,14 +275,14 @@
}
if (includePatchSets) {
- eventFactory.addPatchSets(db, rw, c, d.patchSets(),
+ eventFactory.addPatchSets(db, rw, c, d.visiblePatchSets(),
includeApprovals ? d.approvals().asMap() : null,
includeFiles, d.change(), labelTypes);
}
if (includeCurrentPatchSet) {
PatchSet current = d.currentPatchSet();
- if (current != null) {
+ if (current != null && cc.isPatchVisible(current, d.db())) {
c.currentPatchSet =
eventFactory.asPatchSetAttribute(db, rw, d.change(), current);
eventFactory.addApprovals(c.currentPatchSet,
@@ -302,7 +302,7 @@
if (includeComments) {
eventFactory.addComments(c, d.messages());
if (includePatchSets) {
- eventFactory.addPatchSets(db, rw, c, d.patchSets(),
+ eventFactory.addPatchSets(db, rw, c, d.visiblePatchSets(),
includeApprovals ? d.approvals().asMap() : null,
includeFiles, d.change(), labelTypes);
for (PatchSetAttribute attribute : c.patchSets) {
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index 6e2f9c9..97776f1 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -95,6 +95,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -536,47 +537,97 @@
public void byLabel() throws Exception {
accountManager.authenticate(AuthRequest.forUser("anotheruser"));
TestRepository<Repo> repo = createProject("repo");
- ChangeInserter ins = newChange(repo);
- Change change = insert(repo, ins);
+ ChangeInserter ins = newChange(repo, null, null, null, null);
+ ChangeInserter ins2 = newChange(repo, null, null, null, null);
+ ChangeInserter ins3 = newChange(repo, null, null, null, null);
+ ChangeInserter ins4 = newChange(repo, null, null, null, null);
+ ChangeInserter ins5 = newChange(repo, null, null, null, null);
- gApi.changes().id(change.getId().get()).current()
- .review(new ReviewInput().label("Code-Review", 1));
+ Change reviewMinus2Change = insert(repo, ins);
+ gApi.changes().id(reviewMinus2Change.getId().get()).current()
+ .review(ReviewInput.reject());
+
+ Change reviewMinus1Change = insert(repo, ins2);
+ gApi.changes().id(reviewMinus1Change.getId().get()).current()
+ .review(ReviewInput.dislike());
+
+ Change noLabelChange = insert(repo, ins3);
+
+ Change reviewPlus1Change = insert(repo, ins4);
+ gApi.changes().id(reviewPlus1Change.getId().get()).current()
+ .review(ReviewInput.recommend());
+
+ Change reviewPlus2Change = insert(repo, ins5);
+ gApi.changes().id(reviewPlus2Change.getId().get()).current()
+ .review(ReviewInput.approve());
+
Map<String, Short> m = gApi.changes()
- .id(change.getId().get())
+ .id(reviewPlus1Change.getId().get())
.reviewer(user.getAccountId().toString())
.votes();
assertThat(m).hasSize(1);
assertThat(m).containsEntry("Code-Review", new Short((short)1));
- assertQuery("label:Code-Review=-2");
- assertQuery("label:Code-Review-2");
- assertQuery("label:Code-Review=-1");
- assertQuery("label:Code-Review-1");
- assertQuery("label:Code-Review=0");
- assertQuery("label:Code-Review=+1", change);
- assertQuery("label:Code-Review=1", change);
- assertQuery("label:Code-Review+1", change);
- assertQuery("label:Code-Review=+2");
- assertQuery("label:Code-Review=2");
- assertQuery("label:Code-Review+2");
+ Map<Integer, Change> changes = new LinkedHashMap<>(5);
+ changes.put(2, reviewPlus2Change);
+ changes.put(1, reviewPlus1Change);
+ changes.put(0, noLabelChange);
+ changes.put(-1, reviewMinus1Change);
+ changes.put(-2, reviewMinus2Change);
- assertQuery("label:Code-Review>=0", change);
- assertQuery("label:Code-Review>0", change);
- assertQuery("label:Code-Review>=1", change);
- assertQuery("label:Code-Review>1");
- assertQuery("label:Code-Review>=2");
+ assertQuery("label:Code-Review=-2", reviewMinus2Change);
+ assertQuery("label:Code-Review-2", reviewMinus2Change);
+ assertQuery("label:Code-Review=-1", reviewMinus1Change);
+ assertQuery("label:Code-Review-1", reviewMinus1Change);
+ assertQuery("label:Code-Review=0", noLabelChange);
+ assertQuery("label:Code-Review=+1", reviewPlus1Change);
+ assertQuery("label:Code-Review=1", reviewPlus1Change);
+ assertQuery("label:Code-Review+1", reviewPlus1Change);
+ assertQuery("label:Code-Review=+2", reviewPlus2Change);
+ assertQuery("label:Code-Review=2", reviewPlus2Change);
+ assertQuery("label:Code-Review+2", reviewPlus2Change);
- assertQuery("label: Code-Review<=2", change);
- assertQuery("label: Code-Review<2", change);
- assertQuery("label: Code-Review<=1", change);
- assertQuery("label:Code-Review<1");
- assertQuery("label:Code-Review<=0");
+ assertQuery("label:Code-Review>-3", codeReviewInRange(changes, -2, 2));
+ assertQuery("label:Code-Review>=-2", codeReviewInRange(changes, -2, 2));
+ assertQuery("label:Code-Review>-2", codeReviewInRange(changes, -1, 2));
+ assertQuery("label:Code-Review>=-1", codeReviewInRange(changes, -1, 2));
+ assertQuery("label:Code-Review>-1", codeReviewInRange(changes, 0, 2));
+ assertQuery("label:Code-Review>=0", codeReviewInRange(changes, 0, 2));
+ assertQuery("label:Code-Review>0", codeReviewInRange(changes, 1, 2));
+ assertQuery("label:Code-Review>=1", codeReviewInRange(changes, 1, 2));
+ assertQuery("label:Code-Review>1", reviewPlus2Change);
+ assertQuery("label:Code-Review>=2", reviewPlus2Change);
+ assertQuery("label:Code-Review>2");
+
+ assertQuery("label:Code-Review<=2", codeReviewInRange(changes, -2, 2));
+ assertQuery("label:Code-Review<2", codeReviewInRange(changes, -2, 1));
+ assertQuery("label:Code-Review<=1", codeReviewInRange(changes, -2, 1));
+ assertQuery("label:Code-Review<1", codeReviewInRange(changes, -2, 0));
+ assertQuery("label:Code-Review<=0", codeReviewInRange(changes, -2, 0));
+ assertQuery("label:Code-Review<0", codeReviewInRange(changes, -2, -1));
+ assertQuery("label:Code-Review<=-1", codeReviewInRange(changes, -2, -1));
+ assertQuery("label:Code-Review<-1", reviewMinus2Change);
+ assertQuery("label:Code-Review<=-2", reviewMinus2Change);
+ assertQuery("label:Code-Review<-2");
assertQuery("label:Code-Review=+1,anotheruser");
- assertQuery("label:Code-Review=+1,user", change);
- assertQuery("label:Code-Review=+1,user=user", change);
- assertQuery("label:Code-Review=+1,Administrators", change);
- assertQuery("label:Code-Review=+1,group=Administrators", change);
+ assertQuery("label:Code-Review=+1,user", reviewPlus1Change);
+ assertQuery("label:Code-Review=+1,user=user", reviewPlus1Change);
+ assertQuery("label:Code-Review=+1,Administrators", reviewPlus1Change);
+ assertQuery("label:Code-Review=+1,group=Administrators", reviewPlus1Change);
+ }
+
+ private Change[] codeReviewInRange(Map<Integer, Change> changes, int start,
+ int end) {
+ int size = 0;
+ Change[] range = new Change[end - start + 1];
+ for (int i : changes.keySet()) {
+ if (i >= start && i <= end) {
+ range[size] = changes.get(i);
+ size++;
+ }
+ }
+ return range;
}
private String createGroup(String name, String owner) throws Exception {
diff --git a/lib/codemirror/BUCK b/lib/codemirror/BUCK
index 59d24b3..9b8a146 100644
--- a/lib/codemirror/BUCK
+++ b/lib/codemirror/BUCK
@@ -23,6 +23,18 @@
visibility = [],
)
+DIFF_MATCH_PATCH_VERSION = '20121119-1'
+DIFF_MATCH_PATCH_TOP = ('META-INF/resources/webjars/google-diff-match-patch/%s'
+ % DIFF_MATCH_PATCH_VERSION)
+
+maven_jar(
+ name = 'diff-match-patch',
+ id = 'org.webjars:google-diff-match-patch:' + DIFF_MATCH_PATCH_VERSION,
+ sha1 = '0cf1782dbcb8359d95070da9176059a5a9d37709',
+ license = 'Apache2.0',
+ attach_source = False,
+)
+
for archive, suffix, top in [('codemirror-original', '', TOP), ('codemirror-minified', '_r', TOP_MINIFIED)]:
# Main JavaScript and addons
genrule(
@@ -58,11 +70,12 @@
genrule (
name = 'mode_%s%s' % (n, suffix),
cmd = ';'.join([
- "echo '/** @license' >$OUT",
- 'unzip -p $(location :%s) %s/LICENSE >>$OUT' % (archive, top),
- "echo '*/' >>$OUT",
- 'unzip -p $(location :%s) %s/mode/%s/%s.js >>$OUT' % (archive, top, n, n),
- ]),
+ "echo '/** @license' >$OUT",
+ 'unzip -p $(location :%s) %s/LICENSE >>$OUT' % (archive, top),
+ "echo '*/' >>$OUT",
+ 'unzip -p $(location :%s) %s/mode/%s/%s.js >>$OUT' % (archive, top, n, n),
+ ]
+ ),
out = 'mode_%s%s.js' % (n, suffix),
)
@@ -80,19 +93,39 @@
out = 'theme_%s%s.css' % (n, suffix),
)
+ # Merge Addon bundled with diff-match-patch
+ genrule(
+ name = 'addon_merge%s' % suffix,
+ cmd = ';'.join([
+ "echo '/** @license' >$OUT",
+ 'unzip -p $(location :%s) %s/LICENSE >>$OUT' % (archive, top),
+ "echo '*/\n' >>$OUT",
+ "echo '// The google-diff-match-patch library is from https://google-diff-match-patch.googlecode.com/svn-history/r106/trunk/javascript/diff_match_patch.js\n' >> $OUT",
+ "echo '/** @license' >>$OUT",
+ 'cat $(location //lib:LICENSE-Apache2.0) >>$OUT',
+ "echo '*/' >>$OUT",
+ 'unzip -p $(location :diff-match-patch) %s/diff_match_patch.js >>$OUT' % DIFF_MATCH_PATCH_TOP,
+ "echo ';' >> $OUT",
+ 'unzip -p $(location :%s) %s/addon/merge/merge.js >>$OUT' % (archive, top)
+ ]
+ ),
+ out = 'addon_merge%s.js' % suffix,
+ )
+
# Jar packaging
genrule(
name = 'jar' + suffix,
cmd = ';'.join([
'cd $TMP',
- 'mkdir -p net/codemirror/{lib,mode,theme}',
+ 'mkdir -p net/codemirror/{addon,lib,mode,theme}',
'cp $(location :css%s) net/codemirror/lib/cm.css' % suffix,
'cp $(location :cm%s) net/codemirror/lib/cm.js' % suffix]
+ ['cp $(location :mode_%s%s) net/codemirror/mode/%s.js' % (n, suffix, n)
for n in CM_MODES]
+ ['cp $(location :theme_%s%s) net/codemirror/theme/%s.css' % (n, suffix, n)
for n in CM_THEMES]
- + ['zip -qr $OUT net/codemirror/{lib,mode,theme}']),
+ + ['cp $(location :addon_merge%s) net/codemirror/addon/merge_bundled.js' % suffix]
+ + ['zip -qr $OUT net/codemirror/{addon,lib,mode,theme}']),
out = 'codemirror%s.jar' % suffix,
)
diff --git a/lib/codemirror/cm.defs b/lib/codemirror/cm.defs
index 29f1c77..baf2ce5 100644
--- a/lib/codemirror/cm.defs
+++ b/lib/codemirror/cm.defs
@@ -1,6 +1,7 @@
CM_CSS = [
'lib/codemirror.css',
'addon/dialog/dialog.css',
+ 'addon/merge/merge.css',
'addon/scroll/simplescrollbars.css',
'addon/search/matchesonscrollbar.css',
'addon/lint/lint.css',
diff --git a/plugins/replication b/plugins/replication
index d5cd908..b80cd81 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit d5cd908c0d7938a5d253d49f68ed352bbc7449cf
+Subproject commit b80cd8168ae8ba065c0186b1ddfec366a6368cb6