Merge "Upgrade JGit to 3.5.0.201409071800-rc1"
diff --git a/Documentation/access-control.txt b/Documentation/access-control.txt
index 2f92714..fef22b8 100644
--- a/Documentation/access-control.txt
+++ b/Documentation/access-control.txt
@@ -820,6 +820,17 @@
edited on open changes.
+[[category_edit_hashtags]]
+=== Edit Hashtags
+
+This category permits users to add or remove hashtags on a change that
+is uploaded for review.
+
+The change owner, branch owners, project owners, and site administrators
+can always edit or remove hashtags (even without having the `Edit Hashtags`
+access right assigned).
+
+
[[example_roles]]
== Examples of typical roles in a project
diff --git a/Documentation/cmd-review.txt b/Documentation/cmd-review.txt
index 12d91ae..70a695e 100644
--- a/Documentation/cmd-review.txt
+++ b/Documentation/cmd-review.txt
@@ -13,6 +13,7 @@
[--notify <NOTIFYHANDLING> | -n <NOTIFYHANDLING>]
[--submit | -s]
[--abandon | --restore]
+ [--rebase]
[--publish]
[--json | -j]
[--delete]
@@ -64,7 +65,7 @@
link:rest-api-changes.html#review-input[ReviewInput] entity for the
format.
(option is mutually exclusive with --submit, --restore, --publish, --delete,
- --abandon and --message)
+ --abandon, --message and --rebase)
--notify::
-n::
@@ -85,17 +86,22 @@
--abandon::
Abandon the specified change(s).
- (option is mutually exclusive with --submit, --restore, --publish, --delete
- and --json)
+ (option is mutually exclusive with --submit, --restore, --publish, --delete,
+ --rebase and --json)
--restore::
Restore the specified abandoned change(s).
(option is mutually exclusive with --abandon and --json)
+--rebase::
+ Rebase the specified change(s).
+ (option is mutually exclusive with --abandon, --submit, --delete and --json)
+
--submit::
-s::
Submit the specified patch set(s) for merging.
- (option is mutually exclusive with --abandon, --publish --delete and --json)
+ (option is mutually exclusive with --abandon, --publish --delete, --rebase
+ and --json)
--publish::
Publish the specified draft patch set(s).
@@ -104,8 +110,8 @@
--delete::
Delete the specified draft patch set(s).
- (option is mutually exclusive with --submit, --restore, --abandon, --publish
- and --json)
+ (option is mutually exclusive with --submit, --restore, --abandon, --publish,
+ --rebase and --json)
--code-review::
--verified::
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 01b41d5..63bd695 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -1502,6 +1502,12 @@
Default change screen UI to direct users to. Valid values are
`OLD_UI` and `CHANGE_SCREEN2`. Default is `CHANGE_SCREEN2`.
+[[gerrit.disableReverseDnsLookup]]gerrit.disableReverseDnsLookup::
++
+Disables reverse DNS lookup during computing ref log entry for identified user.
++
+Defaults to false.
+
[[gitweb]]
=== Section gitweb
diff --git a/Documentation/dev-buck.txt b/Documentation/dev-buck.txt
index 414033b..8121a63 100644
--- a/Documentation/dev-buck.txt
+++ b/Documentation/dev-buck.txt
@@ -32,10 +32,11 @@
Note that the buck executable needs to be available in all shell sessions,
so also make sure it is appended to the path globally.
-Add a symbolic link in `~/bin` to the buck executable:
+Add a symbolic link in `~/bin` to the buck and buckd executables:
----
ln -s `pwd`/bin/buck ~/bin/
+ ln -s `pwd`/bin/buckd ~/bin/
----
Verify that `buck` is accessible:
@@ -45,8 +46,8 @@
----
To enable autocompletion of buck commands, install the autocompletion
-script from `./scripts/bash_completion` in the buck project. Refer to
-the script's header comments for installation instructions.
+script from `./scripts/buck_completion.bash` in the buck project. Refer
+to the script's header comments for installation instructions.
[[eclipse]]
diff --git a/Documentation/dev-eclipse.txt b/Documentation/dev-eclipse.txt
index d8db555..ba55268 100644
--- a/Documentation/dev-eclipse.txt
+++ b/Documentation/dev-eclipse.txt
@@ -69,12 +69,6 @@
=== Running Super Dev Mode
-Install dependencies:
-
-----
- buck build codeserver
-----
-
Due to codeserver not correctly identifying user agent problem, that was
already fixed upstream but not released yet, the used user agent must be
explicitly set in `GerritGwtUI.gwt.xml` for SDM to work:
@@ -91,8 +85,7 @@
<set-property name="user.agent" value="safari" />
----
-* Select in Eclipse Run -> Debug Configurations `gerrit_gwt_codeserver.launch`
-* Select in Eclipse Run -> Debug Configurations `gerrit_daemon.launch`
+* Select in Eclipse Run -> Debug Configurations `gerrit_gwt_sdm_debug.launch`
* Only once: add bookmarks for `Dev Mode On/Off` from codeserver URL:
`http://localhost:9876/` to your bookmark bar
* Make sure to activate source maps feature in your browser
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index 507e0e4..4346cf7 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -1259,6 +1259,26 @@
}
----
+`MenuItems` that are bound for the `MenuEntry` with the name
+`GerritTopMenu.PROJECTS` can contain a `${projectName}` placeholder
+which is automatically replaced by the actual project name.
+
+E.g. plugins may register an link:#http[HTTP Servlet] to handle project
+specific requests and add an menu item for this:
+
+[source,java]
+---
+ new MenuItem("My Screen", "/plugins/myplugin/project/${projectName}");
+---
+
+This also enables plugins to provide menu items for project aware
+screens:
+
+[source,java]
+---
+ new MenuItem("My Screen", "/x/my-screen/for/${projectName}");
+---
+
If no Guice modules are declared in the manifest, the top menu extension may use
auto-registration by providing an `@Listen` annotation:
@@ -1739,10 +1759,11 @@
private String name = "MyLink";
private String placeHolderUrlProjectCommit = "http://my.tool.com/project=%s/commit=%s";
+ private String imageUrl = "http://placehold.it/16x16.gif";
@Override
public String getLinkName() {
- return name ;
+ return name;
}
@Override
@@ -1750,6 +1771,10 @@
return String.format(placeHolderUrlProjectCommit, project, commit);
}
+ @Override
+ public String getImageUrl() {
+ return imageUrl;
+ }
}
----
diff --git a/Documentation/js-api.txt b/Documentation/js-api.txt
index 883198a..e41eb15 100644
--- a/Documentation/js-api.txt
+++ b/Documentation/js-api.txt
@@ -168,7 +168,7 @@
self.onAction(type, view_name, callback);
----
-* type: `'change'`, `'revision'`, `'project'`, or `'branch'`
+* type: `'change'`, `'edit'`, `'revision'`, `'project'`, or `'branch'`
indicating which type of resource the `UiAction` was bound to
in the server.
@@ -838,8 +838,8 @@
Gerrit.onAction(type, view_name, callback);
----
-* type: `'change'`, `'revision'`, `'project'` or `'branch'` indicating
- what sort of resource the `UiAction` was bound to in the server.
+* type: `'change'`, `'edit'`, `'revision'`, `'project'` or `'branch'`
+ indicating what sort of resource the `UiAction` was bound to in the server.
* view_name: string appearing in URLs to name the view. This is the
second argument of the `get()`, `post()`, `put()`, and `delete()`
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index da3130e..9744bcf 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -1163,7 +1163,8 @@
can be max one edit per user per change. Edits aren't tracked in the database.
When request parameter `list` is provided the response also includes the file
list. When `base` request parameter is provided the file list is computed
-against this base revision.
+against this base revision. When request parameter `download-commands` is
+provided fetch info map is also included.
.Response
----
@@ -3815,6 +3816,7 @@
|Field Name|Description
|`name` |The link name.
|`url` |The link URL.
+|`image_url`|URL to the icon of the link.
|======================
[[edit-info]]
@@ -3826,6 +3828,14 @@
|Field Name ||Description
|`commit` ||The commit of change edit as
link:#commit-info[CommitInfo] entity.
+|`actions` ||
+Actions the caller might be able to perform on this change edit. The
+information is a map of view name to link:#action-info[ActionInfo]
+entities.
+|`fetch` ||
+Information about how to fetch this patch set. The fetch information is
+provided as a map that maps the protocol name ("`git`", "`http`",
+"`ssh`") to link:#fetch-info[FetchInfo] entities.
|`files` |optional|
The files of the change edit as a map that maps the file names to
link:#file-info[FileInfo] entities.
diff --git a/Documentation/user-review-ui.txt b/Documentation/user-review-ui.txt
index 1c3967d..bb1aeef 100644
--- a/Documentation/user-review-ui.txt
+++ b/Documentation/user-review-ui.txt
@@ -98,7 +98,7 @@
person's changes that have the same status as the currently viewed
change.
-The commit ID and the link:user-changeid.html[Change-Id] are both
+The commit ID, the parent commit(s) and the link:user-changeid.html[Change-Id] are
displayed with a copy-to-clipboard icon that allows the ID to be copied
into the clipboard.
@@ -107,8 +107,7 @@
image::images/user-review-ui-change-screen-commit-info.png[width=800, link="images/user-review-ui-change-screen-commit-info.png"]
-If a merge commit is viewed this is highlighted by an icon. In this
-case the parent commits are also shown.
+If a merge commit is viewed this is highlighted by an icon.
image::images/user-review-ui-change-screen-commit-info-merge-commit.png[width=800, link="images/user-review-ui-change-screen-commit-info-merge-commit.png"]
diff --git a/ReleaseNotes/ReleaseNotes-2.11.txt b/ReleaseNotes/ReleaseNotes-2.11.txt
new file mode 100644
index 0000000..23f3247
--- /dev/null
+++ b/ReleaseNotes/ReleaseNotes-2.11.txt
@@ -0,0 +1,69 @@
+Release notes for Gerrit 2.11
+=============================
+
+
+Gerrit 2.11 is now available:
+
+link:https://gerrit-releases.storage.googleapis.com/gerrit-2.11.war[
+https://gerrit-releases.storage.googleapis.com/gerrit-2.11.war]
+
+Important Notes
+---------------
+
+
+*WARNING:* This release contains schema changes. To upgrade:
+----
+ java -jar gerrit.war init -d site_path
+ java -jar gerrit.war reindex --recheck-mergeable -d site_path
+----
+
+Release Highlights
+------------------
+
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=505[Issue 505]:
+Code changes can be done directly in browser.
++
+Files can be added, deleted, restored or amended directly in browser
+in context of change edit. Change edits can be published, deleted and
+rebased on top of the latest patch set.
+
+
+New Features
+------------
+
+
+Web UI
+~~~~~~
+
+TODO
+
+Global
+^^^^^^
+
+TODO
+
+REST
+~~~~
+
+TODO
+
+SSH
+~~~
+
+TODO
+
+Plugins
+~~~~~~~
+
+TODO
+
+Other
+~~~~~
+
+TODO
+
+Upgrades
+--------
+
+TODO
diff --git a/ReleaseNotes/ReleaseNotes-2.9.1.txt b/ReleaseNotes/ReleaseNotes-2.9.1.txt
new file mode 100644
index 0000000..77d2e22
--- /dev/null
+++ b/ReleaseNotes/ReleaseNotes-2.9.1.txt
@@ -0,0 +1,99 @@
+Release notes for Gerrit 2.9.1
+==============================
+
+There are no schema changes from link:ReleaseNotes-2.9.html[2.9].
+
+Download:
+link:https://gerrit-releases.storage.googleapis.com/gerrit-2.9.1.war[
+https://gerrit-releases.storage.googleapis.com/gerrit-2.9.1.war]
+
+Bug Fixes
+---------
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2801[Issue 2801]:
+Set default for review SSH command to `notify=ALL`.
++
+In 2.9 the default was incorrectly set to `notify=NONE`, which prevented
+mail notifications from being sent for review comments that were added by
+build jobs based on the Gerrit Trigger plugin.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2879[Issue 2879]:
+Remove fixed limit of results returned by secondary index query.
++
+The limit was hard-coded to 1000 results, which overrode the value set in
+the global query limit capability.
+
+* Don't require secondary index when running server in daemon mode.
++
+The server failed to start if a secondary index was not present when starting
+the daemon in slave mode.
++
+Now the daemon can be started in slave mode without requiring the index
+to be present.
++
+The reindex program and the ssh query command are no longer available on
+a server that is running in slave mode.
+
+* Add full names for options on list groups REST API.
+
+* Add full names for options on list projects REST API.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2878[Issue 2878]:
+Make `-S` an alias of `--start` in changes query REST API.
+
+* Run change hooks and ref-updated events after indexing is done.
++
+The change hooks and ref-updated events were run parallel to the change
+(re)indexing. This meant that the event-stream sent events to the clients
+before the change indexing was finished.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2877[Issue 2877]:
+Fix NullPointerException when ReviewInput's message is empty.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2500[Issue 2500],
+link:https://code.google.com/p/gerrit/issues/detail?id=1748[Issue 1748]:
+Fix replication of tags.
+
+* Fix NullPointerException in `/projects/{name}/children?recursive` when a
+project has a parent project that is does not exist.
+
+* Fix NullPointerException when submitting review with inline comments via REST.
+
+* Improve error logging in MergeabilityChecker.
+
+* Gracefully skip mergeability checking on broken changes.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2861[Issue 2861]:
+Replace "line" with "end_line" when range is given in inline comment.
++
+Also update the documentation with an example of a range comment.
+
+* Fix mutual exclusivity of --delete and --submit review command options.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2848[Issue 2848]:
+Add support for CSharp syntax highlighting.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2831[Issue 2831]:
+Add missing call to ref-updated hook for submodule updates.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2773[Issue 2773]
+Fix stale dates in committer field.
+
+* Prevent NullPointerException when trying to add an account that doesn't
+exist as a reviewer.
+
+* Fix potential NullPointerException in cherry-pick submit strategy.
+
+* Add `--start` option to skip changes in ssh `query` command.
+
+* Fix loading of javascript plugins when using non-root Gerrit URLs.
++
+When Gerrit is not on the root URL path the javascript plugins failed to
+load because of the exact matching required on the request URL.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2279[Issue 2279]:
+Display parents for all changes, not only merge commits.
++
+In the new change screen the parent commit is now also shown for regular
+commits, as well as merge commits. This makes it consistent with the old
+change screen.
diff --git a/ReleaseNotes/index.txt b/ReleaseNotes/index.txt
index 2588d5b..8e7cac6 100644
--- a/ReleaseNotes/index.txt
+++ b/ReleaseNotes/index.txt
@@ -5,6 +5,7 @@
Version 2.9.x
-------------
* link:ReleaseNotes-2.9.html[2.9]
+* link:ReleaseNotes-2.9.1.html[2.9.1]
[[2_8]]
Version 2.8.x
diff --git a/bucklets/java_doc.bucklet b/bucklets/java_doc.bucklet
new file mode 120000
index 0000000..cc8b6db
--- /dev/null
+++ b/bucklets/java_doc.bucklet
@@ -0,0 +1 @@
+../tools/java_doc.defs
\ No newline at end of file
diff --git a/bucklets/java_sources.bucklet b/bucklets/java_sources.bucklet
new file mode 120000
index 0000000..8a1a5dd
--- /dev/null
+++ b/bucklets/java_sources.bucklet
@@ -0,0 +1 @@
+../tools/java_sources.defs
\ No newline at end of file
diff --git a/bucklets/local_jar.bucklet b/bucklets/local_jar.bucklet
new file mode 120000
index 0000000..8904824
--- /dev/null
+++ b/bucklets/local_jar.bucklet
@@ -0,0 +1 @@
+../lib/local.defs
\ No newline at end of file
diff --git a/bucklets/maven_package.bucklet b/bucklets/maven_package.bucklet
new file mode 120000
index 0000000..b5f5ea8
--- /dev/null
+++ b/bucklets/maven_package.bucklet
@@ -0,0 +1 @@
+../tools/maven/package.defs
\ No newline at end of file
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java b/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
index c0382da..b804607 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
@@ -50,6 +50,10 @@
return toChange(c.getId());
}
+ public static String toChangeInEditMode(Change.Id c) {
+ return "/c/" + c + ",edit/";
+ }
+
public static String toChange(final Change.Id c) {
return "/c/" + c + "/";
}
@@ -68,7 +72,7 @@
}
public static String toChange(final PatchSet.Id ps) {
- return "/c/" + ps.getParentKey() + "/" + ps.get();
+ return "/c/" + ps.getParentKey() + "/" + ps.getId();
}
public static String toProject(final Project.NameKey p) {
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java
index e067f06..448ce86 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java
@@ -29,6 +29,7 @@
protected boolean canAbandon;
protected boolean canEditCommitMessage;
protected boolean canCherryPick;
+ protected boolean canEditHashtags;
protected boolean canPublish;
protected boolean canRebase;
protected boolean canRestore;
@@ -93,6 +94,14 @@
canCherryPick = a;
}
+ public boolean getCanEditHashtags() {
+ return canEditHashtags;
+ }
+
+ public void setCanEditHashtags(final boolean a) {
+ canEditHashtags = a;
+ }
+
public boolean canPublish() {
return canPublish;
}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GlobalCapability.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GlobalCapability.java
index fccf3b3..c6c2d50 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GlobalCapability.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GlobalCapability.java
@@ -73,6 +73,9 @@
/** Maximum result limit per executed query. */
public static final String QUERY_LIMIT = "queryLimit";
+ /** Default result limit per executed query. */
+ public static final int DEFAULT_MAX_QUERY_LIMIT = 500;
+
/** Ability to impersonate another user. */
public static final String RUN_AS = "runAs";
@@ -150,7 +153,7 @@
return new PermissionRange.WithDefaults(
varName,
0, Integer.MAX_VALUE,
- 0, 500);
+ 0, DEFAULT_MAX_QUERY_LIMIT);
}
return null;
}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/Permission.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/Permission.java
index 2379b4a..2d9965e 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/Permission.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/Permission.java
@@ -24,6 +24,7 @@
public static final String ABANDON = "abandon";
public static final String CREATE = "create";
public static final String DELETE_DRAFTS = "deleteDrafts";
+ public static final String EDIT_HASHTAGS = "editHashtags";
public static final String EDIT_TOPIC_NAME = "editTopicName";
public static final String FORGE_AUTHOR = "forgeAuthor";
public static final String FORGE_COMMITTER = "forgeCommitter";
@@ -68,6 +69,7 @@
NAMES_LC.add(SUBMIT_AS.toLowerCase());
NAMES_LC.add(VIEW_DRAFTS.toLowerCase());
NAMES_LC.add(EDIT_TOPIC_NAME.toLowerCase());
+ NAMES_LC.add(EDIT_HASHTAGS.toLowerCase());
NAMES_LC.add(DELETE_DRAFTS.toLowerCase());
NAMES_LC.add(PUBLISH_DRAFTS.toLowerCase());
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ReviewResult.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ReviewResult.java
index f82f434..76785d8 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ReviewResult.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ReviewResult.java
@@ -85,7 +85,10 @@
DEST_BRANCH_NOT_FOUND,
/** Not permitted to edit the topic name */
- EDIT_TOPIC_NAME_NOT_PERMITTED
+ EDIT_TOPIC_NAME_NOT_PERMITTED,
+
+ /** Not permitted to edit the hashtags */
+ EDIT_HASHTAGS_NOT_PERMITTED
}
protected Type type;
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/EditInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/EditInfo.java
index 4946cb9..ddfcac7 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/EditInfo.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/EditInfo.java
@@ -18,5 +18,7 @@
public class EditInfo {
public CommitInfo commit;
+ public Map<String, ActionInfo> actions;
+ public Map<String, FetchInfo> fetch;
public Map<String, FileInfo> files;
}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/WebLinkInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/WebLinkInfo.java
index 7695c8c..9f9e909 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/WebLinkInfo.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/WebLinkInfo.java
@@ -16,10 +16,12 @@
public class WebLinkInfo {
public String name;
+ public String imageUrl;
public String url;
- public WebLinkInfo(String name, String url) {
+ public WebLinkInfo(String name, String imageUrl, String url) {
this.name = name;
+ this.imageUrl = imageUrl;
this.url = url;
}
}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/WebLink.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/WebLink.java
index 19d9ab7..581098b 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/WebLink.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/WebLink.java
@@ -18,7 +18,15 @@
/**
* The link-name displayed in UI.
*
- * @return name of link.
+ * @return name of link or title of the link if image URL is available.
*/
String getLinkName();
+
+ /**
+ * URL of image to be displayed
+ *
+ * @return URL to image for link or null for using a text-only link.
+ * Recommended image size is 16x16.
+ */
+ String getImageUrl();
}
diff --git a/gerrit-gwtdebug/BUCK b/gerrit-gwtdebug/BUCK
index a926773..2e8949b 100644
--- a/gerrit-gwtdebug/BUCK
+++ b/gerrit-gwtdebug/BUCK
@@ -1,11 +1,18 @@
java_library(
name = 'gwtdebug',
- srcs = ['src/main/java/com/google/gerrit/gwtdebug/GerritDebugLauncher.java'],
+ srcs = glob(['src/main/java/**/*.java']),
deps = [
+ '//gerrit-pgm:pgm',
+ '//gerrit-pgm:util',
+ '//gerrit-util-cli:cli',
'//lib/gwt:dev',
+ '//lib/gwt:codeserver',
'//lib/jetty:server',
'//lib/jetty:servlet',
+ '//lib/jetty:servlets',
'//lib/jetty:webapp',
+ '//lib/log:api',
+ '//lib/log:log4j',
],
visibility = ['//tools/eclipse:classpath'],
)
diff --git a/gerrit-gwtdebug/src/main/java/com/google/gerrit/gwtdebug/GerritSDMLauncher.java b/gerrit-gwtdebug/src/main/java/com/google/gerrit/gwtdebug/GerritSDMLauncher.java
new file mode 100644
index 0000000..bc26330
--- /dev/null
+++ b/gerrit-gwtdebug/src/main/java/com/google/gerrit/gwtdebug/GerritSDMLauncher.java
@@ -0,0 +1,80 @@
+// Copyright (C) 2014 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 com.google.gerrit.gwtdebug;
+
+import com.google.gerrit.pgm.Daemon;
+import com.google.gwt.dev.codeserver.CodeServer;
+import com.google.gwt.dev.codeserver.Options;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class GerritSDMLauncher {
+ private static final Logger log = LoggerFactory.getLogger(GerritSDMLauncher.class);
+
+ public static void main(String[] argv) throws Exception {
+ GerritSDMLauncher launcher = new GerritSDMLauncher();
+ launcher.mainImpl(argv);
+ }
+
+ private int mainImpl(String[] argv) {
+ List<String> sdmLauncherOptions = new ArrayList<>();
+ List<String> daemonLauncherOptions = new ArrayList<>();
+
+ // Separator between Daemon and Codeserver parameters is "--"
+ boolean daemonArgumentSeparator = false;
+ int i = 0;
+ for (; i < argv.length; i++) {
+ if (!argv[i].equals("--")) {
+ sdmLauncherOptions.add(argv[i]);
+ } else {
+ daemonArgumentSeparator = true;
+ break;
+ }
+ }
+ if (daemonArgumentSeparator) {
+ ++i;
+ for (; i < argv.length; i++) {
+ daemonLauncherOptions.add(argv[i]);
+ }
+ }
+
+ Options options = new Options();
+ if (!options.parseArgs(sdmLauncherOptions.toArray(
+ new String[sdmLauncherOptions.size()]))) {
+ log.error("Failed to parse codeserver arguments");
+ return 1;
+ }
+
+ CodeServer.main(options);
+
+ try {
+ int r = new Daemon().main(daemonLauncherOptions.toArray(
+ new String[daemonLauncherOptions.size()]));
+ if (r != 0) {
+ log.error("Daemon exited with return code: " + r);
+ return 1;
+ }
+ } catch (Exception e) {
+ log.error("Cannot start daemon", e);
+ return 1;
+ }
+
+ return 0;
+ }
+}
diff --git a/gerrit-gwtdebug/src/main/java/com/google/gwt/dev/codeserver/WebServer.java b/gerrit-gwtdebug/src/main/java/com/google/gwt/dev/codeserver/WebServer.java
new file mode 100644
index 0000000..8eb1300
--- /dev/null
+++ b/gerrit-gwtdebug/src/main/java/com/google/gwt/dev/codeserver/WebServer.java
@@ -0,0 +1,513 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * 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 com.google.gwt.dev.codeserver;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.dev.json.JsonArray;
+import com.google.gwt.dev.json.JsonObject;
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.server.HttpConnection;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.servlets.GzipFilter;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.ServletException;
+import javax.servlet.DispatcherType;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * The web server for Super Dev Mode, also known as the code server. The URLs handled include:
+ * <ul>
+ * <li>HTML pages for the front page and module pages</li>
+ * <li>JavaScript that implementing the bookmarklets</li>
+ * <li>The web API for recompiling a GWT app</li>
+ * <li>The output files and log files from the GWT compiler</li>
+ * <li>Java source code (for source-level debugging)</li>
+ * </ul>
+ *
+ * <p>EXPERIMENTAL. There is no authentication, encryption, or XSS protection, so this server is
+ * only safe to run on localhost.</p>
+ */
+// This file was copied from GWT project and adjusted to run against
+// Jetty 9.2.2. The original diff can be found here:
+// https://gwt-review.googlesource.com/#/c/7857/13/dev/codeserver/java/com/google/gwt/dev/codeserver/WebServer.java
+public class WebServer {
+
+ private static final Pattern SAFE_DIRECTORY =
+ Pattern.compile("([a-zA-Z0-9_-]+\\.)*[a-zA-Z0-9_-]+"); // no extension needed
+
+ private static final Pattern SAFE_FILENAME =
+ Pattern.compile("([a-zA-Z0-9_-]+\\.)+[a-zA-Z0-9_-]+"); // an extension is required
+
+ private static final Pattern SAFE_MODULE_PATH =
+ Pattern.compile("/(" + SAFE_DIRECTORY + ")/$");
+
+ static final Pattern SAFE_DIRECTORY_PATH =
+ Pattern.compile("/(" + SAFE_DIRECTORY + "/)+$");
+
+ /* visible for testing */
+ static final Pattern SAFE_FILE_PATH =
+ Pattern.compile("/(" + SAFE_DIRECTORY + "/)+" + SAFE_FILENAME + "$");
+
+ private static final Pattern SAFE_CALLBACK =
+ Pattern.compile("([a-zA-Z_][a-zA-Z0-9_]*\\.)*[a-zA-Z_][a-zA-Z0-9_]*");
+
+ private static final MimeTypes MIME_TYPES = new MimeTypes();
+
+ private final SourceHandler handler;
+
+ private final Modules modules;
+
+ private final String bindAddress;
+ private final int port;
+ private final TreeLogger logger;
+ private Server server;
+
+ WebServer(SourceHandler handler, Modules modules, String bindAddress, int port,
+ TreeLogger logger) {
+ this.handler = handler;
+ this.modules = modules;
+ this.bindAddress = bindAddress;
+ this.port = port;
+ this.logger = logger;
+ }
+
+ @SuppressWarnings("serial")
+ public void start() throws UnableToCompleteException {
+
+ Server newServer = new Server();
+ ServerConnector connector = new ServerConnector(newServer);
+ connector.setHost(bindAddress);
+ connector.setPort(port);
+ connector.setReuseAddress(false);
+ connector.setSoLingerTime(0);
+ newServer.addConnector(connector);
+
+ ServletContextHandler handler = new ServletContextHandler(ServletContextHandler.SESSIONS);
+ handler.setContextPath("/");
+ handler.addServlet(new ServletHolder(new HttpServlet() {
+ @Override
+ protected void doGet(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ handleRequest(request.getPathInfo(), request, response);
+ }
+ }), "/*");
+ handler.addFilter(GzipFilter.class, "/*", EnumSet.allOf(DispatcherType.class));
+ newServer.setHandler(handler);
+ try {
+ newServer.start();
+ } catch (Exception e) {
+ logger.log(TreeLogger.ERROR, "cannot start web server", e);
+ throw new UnableToCompleteException();
+ }
+ this.server = newServer;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public void stop() throws Exception {
+ server.stop();
+ server = null;
+ }
+
+ /**
+ * Returns the location of the compiler output. (Changes after every recompile.)
+ */
+ public File getCurrentWarDir(String moduleName) {
+ return modules.get(moduleName).getWarDir();
+ }
+
+ private void handleRequest(String target, HttpServletRequest request,
+ HttpServletResponse response)
+ throws IOException {
+
+ if (request.getMethod().equalsIgnoreCase("get")) {
+ doGet(target, request, response);
+ }
+ }
+
+ private void doGet(String target, HttpServletRequest request, HttpServletResponse response)
+ throws IOException {
+
+ if (!target.endsWith(".cache.js")) {
+ // Make sure IE9 doesn't cache any pages.
+ // (Nearly all pages may change on server restart.)
+ PageUtil.setNoCacheHeaders(response);
+ }
+
+ if (target.equals("/")) {
+ setHandled(request);
+ JsonObject config = makeConfig();
+ PageUtil.sendJsonAndHtml("config", config, "frontpage.html", response, logger);
+ return;
+ }
+
+ if (target.equals("/dev_mode_on.js")) {
+ setHandled(request);
+ JsonObject config = makeConfig();
+ PageUtil
+ .sendJsonAndJavaScript("__gwt_codeserver_config", config, "dev_mode_on.js", response,
+ logger);
+ return;
+ }
+
+ // Recompile on request from the bookmarklet.
+ // This is a GET because a bookmarklet can call it from a different origin (JSONP).
+ if (target.startsWith("/recompile/")) {
+ setHandled(request);
+ String moduleName = target.substring("/recompile/".length());
+ ModuleState moduleState = modules.get(moduleName);
+ if (moduleState == null) {
+ response.sendError(HttpServletResponse.SC_NOT_FOUND);
+ logger.log(TreeLogger.WARN, "not found: " + target);
+ return;
+ }
+
+ // We are passing properties from an unauthenticated GET request directly to the compiler.
+ // This should be safe, but only because these are binding properties. For each binding
+ // property, you can only choose from a set of predefined values. So all an attacker can do is
+ // cause a spurious recompile, resulting in an unexpected permutation being loaded later.
+ //
+ // It would be unsafe to allow a configuration property to be changed.
+ boolean ok = moduleState.recompile(getBindingProperties(request));
+
+ JsonObject config = makeConfig();
+ config.put("status", ok ? "ok" : "failed");
+ sendJsonpPage(config, request, response);
+ return;
+ }
+
+ if (target.startsWith("/log/")) {
+ setHandled(request);
+ String moduleName = target.substring("/log/".length());
+ File file = modules.get(moduleName).getCompileLog();
+ sendLogPage(moduleName, file, response);
+ return;
+ }
+
+ if (target.equals("/favicon.ico")) {
+ InputStream faviconStream = getClass().getResourceAsStream("favicon.ico");
+ if (faviconStream != null) {
+ setHandled(request);
+ // IE8 will not load the favicon in an img tag with the default MIME type,
+ // so use "image/x-icon" instead.
+ PageUtil.sendStream("image/x-icon", faviconStream, response);
+ }
+ return;
+ }
+
+ if (target.equals("/policies/")) {
+ setHandled(request);
+ sendPolicyIndex(response);
+ return;
+ }
+
+ Matcher matcher = SAFE_MODULE_PATH.matcher(target);
+ if (matcher.matches()) {
+ setHandled(request);
+ sendModulePage(matcher.group(1), response);
+ return;
+ }
+
+ matcher = SAFE_DIRECTORY_PATH.matcher(target);
+ if (matcher.matches() && handler.isSourceMapRequest(target)) {
+ setHandled(request);
+ handler.handle(target, request, response);
+ return;
+ }
+
+ matcher = SAFE_FILE_PATH.matcher(target);
+ if (matcher.matches()) {
+ setHandled(request);
+ if (handler.isSourceMapRequest(target)) {
+ handler.handle(target, request, response);
+ return;
+ }
+ if (target.startsWith("/policies/")) {
+ sendPolicyFile(target, response);
+ return;
+ }
+ sendOutputFile(target, request, response);
+ return;
+ }
+
+ logger.log(TreeLogger.WARN, "ignored get request: " + target);
+ }
+
+ private void sendOutputFile(String target, HttpServletRequest request,
+ HttpServletResponse response) throws IOException {
+
+ int secondSlash = target.indexOf('/', 1);
+ String moduleName = target.substring(1, secondSlash);
+ ModuleState moduleState = modules.get(moduleName);
+
+ File file = moduleState.getOutputFile(target);
+ if (!file.isFile()) {
+ // perhaps it's compressed
+ file = moduleState.getOutputFile(target + ".gz");
+ if (!file.isFile()) {
+ response.sendError(HttpServletResponse.SC_NOT_FOUND);
+ logger.log(TreeLogger.WARN, "not found: " + file.toString());
+ return;
+ }
+ if (!request.getHeader("Accept-Encoding").contains("gzip")) {
+ response.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
+ logger.log(TreeLogger.WARN, "client doesn't accept gzip; bailing");
+ return;
+ }
+ response.setHeader("Content-Encoding", "gzip");
+ }
+
+ if (target.endsWith(".cache.js")) {
+ response.setHeader("X-SourceMap", sourceMapLocationForModule(moduleName));
+ }
+ response.setHeader("Access-Control-Allow-Origin", "*");
+ String mimeType = guessMimeType(target);
+ PageUtil.sendFile(mimeType, file, response);
+ }
+
+ private void sendModulePage(String moduleName, HttpServletResponse response) throws IOException {
+ ModuleState module = modules.get(moduleName);
+ if (module == null) {
+ response.sendError(HttpServletResponse.SC_NOT_FOUND);
+ logger.log(TreeLogger.WARN, "module not found: " + moduleName);
+ return;
+ }
+ PageUtil
+ .sendJsonAndHtml("config", module.getTemplateVariables(), "modulepage.html", response,
+ logger);
+ }
+
+ private void sendPolicyIndex(HttpServletResponse response) throws IOException {
+
+ response.setContentType("text/html");
+
+ HtmlWriter out = new HtmlWriter(response.getWriter());
+
+ out.startTag("html").nl();
+ out.startTag("head").nl();
+ out.startTag("title").text("Policy Files").endTag("title").nl();
+ out.endTag("head");
+ out.startTag("body");
+
+ out.startTag("h1").text("Policy Files").endTag("h1").nl();
+
+ for (String moduleName : modules) {
+ ModuleState module = modules.get(moduleName);
+ File manifest = module.getExtraFile("rpcPolicyManifest/manifest.txt");
+ if (manifest.isFile()) {
+ out.startTag("h2").text(moduleName).endTag("h2").nl();
+
+ out.startTag("table").nl();
+ String text = PageUtil.loadFile(manifest);
+ for (String line : text.split("\n")) {
+ line = line.trim();
+ if (line.isEmpty() || line.startsWith("#")) {
+ continue;
+ }
+ String[] fields = line.split(", ");
+ if (fields.length < 2) {
+ continue;
+ }
+
+ String serviceName = fields[0];
+ String policyFileName = fields[1];
+
+ String serviceUrl = SourceHandler.SOURCEMAP_PATH + moduleName + "/" +
+ serviceName.replace('.', '/') + ".java";
+ String policyUrl = "/policies/" + policyFileName;
+
+ out.startTag("tr");
+
+ out.startTag("td");
+ out.startTag("a", "href=", serviceUrl).text(serviceName).endTag("a");
+ out.endTag("td");
+
+ out.startTag("td");
+ out.startTag("a", "href=", policyUrl).text(policyFileName).endTag("a");
+ out.endTag("td");
+
+ out.endTag("tr").nl();
+ }
+ out.endTag("table").nl();
+ }
+ }
+
+ out.endTag("body").nl();
+ out.endTag("html").nl();
+ }
+
+ private void sendPolicyFile(String target, HttpServletResponse response) throws IOException {
+ int secondSlash = target.indexOf('/', 1);
+ if (secondSlash < 1) {
+ response.sendError(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+ String rest = target.substring(secondSlash + 1);
+ if (rest.contains("/") || !rest.endsWith(".gwt.rpc")) {
+ response.sendError(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+
+ for (String moduleName : modules) {
+ ModuleState module = modules.get(moduleName);
+ File policy = module.getOutputFile(moduleName + "/" + rest);
+ if (policy.isFile()) {
+ PageUtil.sendFile("text/plain", policy, response);
+ return;
+ }
+ }
+
+ logger.log(TreeLogger.Type.WARN, "policy file not found: " + rest);
+ response.sendError(HttpServletResponse.SC_NOT_FOUND);
+ }
+
+ private JsonObject makeConfig() {
+ JsonArray moduleNames = new JsonArray();
+ for (String module : modules) {
+ moduleNames.add(module);
+ }
+ JsonObject config = JsonObject.create();
+ config.put("moduleNames", moduleNames);
+ return config;
+ }
+
+ private void sendJsonpPage(JsonObject json, HttpServletRequest request,
+ HttpServletResponse response) throws IOException {
+
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.setContentType("application/javascript");
+ PrintWriter out = response.getWriter();
+
+ String callbackExpression = request.getParameter("_callback");
+ if (callbackExpression == null || !SAFE_CALLBACK.matcher(callbackExpression).matches()) {
+ logger.log(TreeLogger.ERROR, "invalid callback: " + callbackExpression);
+ out.print("/* invalid callback parameter */");
+ return;
+ }
+
+ out.print(callbackExpression + "(");
+ json.write(out);
+ out.println(");");
+ }
+
+ /**
+ * Sends the log file as html with errors highlighted in red.
+ */
+ private void sendLogPage(String moduleName, File file, HttpServletResponse response)
+ throws IOException {
+ BufferedReader reader = new BufferedReader(new FileReader(file));
+
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.setContentType("text/html");
+ response.setHeader("Content-Style-Type", "text/css");
+
+ HtmlWriter out = new HtmlWriter(response.getWriter());
+ out.startTag("html").nl();
+ out.startTag("head").nl();
+ out.startTag("title").text(moduleName + " compile log").endTag("title").nl();
+ out.startTag("style").nl();
+ out.text(".error { color: red; font-weight: bold; }").nl();
+ out.endTag("style").nl();
+ out.endTag("head").nl();
+ out.startTag("body").nl();
+ sendLogAsHtml(reader, out);
+ out.endTag("body").nl();
+ out.endTag("html").nl();
+ }
+
+ private static final Pattern ERROR_PATTERN = Pattern.compile("\\[ERROR\\]");
+
+ /**
+ * Copies in to out line by line, escaping each line for html characters and highlighting
+ * error lines. Closes <code>in</code> when done.
+ */
+ private static void sendLogAsHtml(BufferedReader in, HtmlWriter out) throws IOException {
+ try {
+ out.startTag("pre").nl();
+ String line = in.readLine();
+ while (line != null) {
+ Matcher m = ERROR_PATTERN.matcher(line);
+ boolean error = m.find();
+ if (error) {
+ out.startTag("span", "class=", "error");
+ }
+ out.text(line);
+ if (error) {
+ out.endTag("span");
+ }
+ out.nl(); // the readLine doesn't include the newline.
+ line = in.readLine();
+ }
+ out.endTag("pre").nl();
+ } finally {
+ in.close();
+ }
+ }
+
+ /* visible for testing */
+ static String guessMimeType(String filename) {
+ String mimeType = MIME_TYPES.getMimeByExtension(filename);
+ return mimeType != null ? mimeType : "";
+ }
+
+ /**
+ * Returns the binding properties from the web page where dev mode is being used. (As passed in
+ * by dev_mode_on.js in a JSONP request to "/recompile".)
+ */
+ private Map<String, String> getBindingProperties(HttpServletRequest request) {
+ Map<String, String> result = new HashMap<String, String>();
+ for (Object key : request.getParameterMap().keySet()) {
+ String propName = (String) key;
+ if (!propName.equals("_callback")) {
+ result.put(propName, request.getParameter(propName));
+ }
+ }
+ return result;
+ }
+
+ public static String sourceMapLocationForModule(String moduleName) {
+ return SourceHandler.SOURCEMAP_PATH + moduleName +
+ "/gwtSourceMap.json";
+ }
+
+ private static void setHandled(HttpServletRequest request) {
+ Request baseRequest = (request instanceof Request) ? (Request) request :
+ HttpConnection.getCurrentConnection().getHttpChannel().getRequest();
+ baseRequest.setHandled(true);
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
index 2ad9a5e..cc8e997 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
@@ -66,6 +66,7 @@
import com.google.gerrit.client.admin.ProjectScreen;
import com.google.gerrit.client.api.ExtensionScreen;
import com.google.gerrit.client.change.ChangeScreen2;
+import com.google.gerrit.client.change.FileTable;
import com.google.gerrit.client.changes.AccountDashboardScreen;
import com.google.gerrit.client.changes.ChangeScreen;
import com.google.gerrit.client.changes.CustomDashboardScreen;
@@ -148,7 +149,7 @@
if (diffBase != null) {
p.append(diffBase.get()).append("..");
}
- p.append(revision.get()).append("/").append(KeyUtil.encode(fileName));
+ p.append(revision.getId()).append("/").append(KeyUtil.encode(fileName));
if (type != null && !type.isEmpty()) {
p.append(",").append(type);
}
@@ -535,9 +536,15 @@
}
if (rest.isEmpty()) {
- Gerrit.display(token, panel== null
+ FileTable.Mode mode = FileTable.Mode.REVIEW;
+ if (panel != null
+ && (panel.equals("edit") || panel.startsWith("edit/"))) {
+ mode = FileTable.Mode.EDIT;
+ panel = null;
+ }
+ Gerrit.display(token, panel == null
? (isChangeScreen2()
- ? new ChangeScreen2(id, null, null, false)
+ ? new ChangeScreen2(id, null, null, false, mode)
: new ChangeScreen(id))
: new NotFoundScreen());
return;
@@ -553,16 +560,14 @@
rest = "";
}
- PatchSet.Id base;
+ PatchSet.Id base = null;
PatchSet.Id ps;
int dotdot = psIdStr.indexOf("..");
if (1 <= dotdot) {
base = new PatchSet.Id(id, Integer.parseInt(psIdStr.substring(0, dotdot)));
- ps = new PatchSet.Id(id, Integer.parseInt(psIdStr.substring(dotdot + 2)));
- } else {
- base = null;
- ps = new PatchSet.Id(id, Integer.parseInt(psIdStr));
+ psIdStr = psIdStr.substring(dotdot + 2);
}
+ ps = toPsId(id, psIdStr);
if (!rest.isEmpty()) {
DisplaySide side = DisplaySide.B;
@@ -587,7 +592,7 @@
base != null
? String.valueOf(base.get())
: null,
- String.valueOf(ps.get()), false)
+ String.valueOf(ps.get()), false, FileTable.Mode.REVIEW)
: new ChangeScreen(id));
} else if ("publish".equals(panel)) {
publish(ps);
@@ -597,6 +602,12 @@
}
}
+ private static PatchSet.Id toPsId(Change.Id id, String psIdStr) {
+ return new PatchSet.Id(id, psIdStr.equals("edit")
+ ? 0
+ : Integer.parseInt(psIdStr));
+ }
+
private static void extension(final String token) {
ExtensionScreen view = new ExtensionScreen(skip(token));
if (view.isFound()) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
index 5b96ba0..d74b744 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
@@ -51,6 +51,7 @@
import com.google.gerrit.reviewdb.client.AccountDiffPreference;
import com.google.gerrit.reviewdb.client.AccountGeneralPreferences;
import com.google.gerrit.reviewdb.client.AuthType;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gwt.aria.client.Roles;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
@@ -764,14 +765,21 @@
public void onSuccess(TopMenuList result) {
List<TopMenu> topMenuExtensions = Natives.asList(result);
for (TopMenu menu : topMenuExtensions) {
- LinkMenuBar existingBar = menuBars.get(menu.getName());
+ String name = menu.getName();
+ LinkMenuBar existingBar = menuBars.get(name);
LinkMenuBar bar = existingBar != null ? existingBar : new LinkMenuBar();
- for (TopMenuItem item : Natives.asList(menu.getItems())) {
- addExtensionLink(bar, item);
+ if (GerritTopMenu.PROJECTS.menuName.equals(name)) {
+ for (TopMenuItem item : Natives.asList(menu.getItems())) {
+ addProjectLink(bar, item);
+ }
+ } else {
+ for (TopMenuItem item : Natives.asList(menu.getItems())) {
+ addExtensionLink(bar, item);
+ }
}
if (existingBar == null) {
- menuBars.put(menu.getName(), bar);
- menuLeft.add(bar, menu.getName());
+ menuBars.put(name, bar);
+ menuLeft.add(bar, name);
}
}
}
@@ -890,6 +898,40 @@
});
}
+ private static LinkMenuItem addProjectLink(LinkMenuBar m, TopMenuItem item) {
+ LinkMenuItem i = new ProjectLinkMenuItem(item.getName(), item.getUrl()) {
+ @Override
+ protected void onScreenLoad(Project.NameKey project) {
+ String p = panel.replace("${projectName}", project.get());
+ if (panel.startsWith("/x/")) {
+ setTargetHistoryToken(p);
+ } else if (isAbsolute(panel)) {
+ getElement().setPropertyString("href", p);
+ } else {
+ getElement().setPropertyString("href", selfRedirect(p));
+ }
+ }
+
+ @Override
+ public void go() {
+ String href = getElement().getPropertyString("href");
+ if (href.startsWith("#")) {
+ super.go();
+ } else {
+ Window.open(href, getElement().getPropertyString("target"), "");
+ }
+ }
+ };
+ if (item.getTarget() != null && !item.getTarget().isEmpty()) {
+ i.getElement().setAttribute("target", item.getTarget());
+ }
+ if (item.getId() != null) {
+ i.getElement().setAttribute("id", item.getId());
+ }
+ m.addItem(i);
+ return i;
+ }
+
private static void addDiffLink(final LinkMenuBar m, final String text,
final PatchScreen.Type type) {
m.addItem(new LinkMenuItem(text, "") {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritResources.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritResources.java
index 2b78fa4..d106ad5 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritResources.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritResources.java
@@ -49,6 +49,9 @@
@Source("redNot.png")
public ImageResource redNot();
+ @Source("editUndo.png")
+ public ImageResource editUndo();
+
@Source("downloadIcon.png")
public ImageResource downloadIcon();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/WebLinkInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/WebLinkInfo.java
index 64b9cb8..3fbc4ff 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/WebLinkInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/WebLinkInfo.java
@@ -19,6 +19,7 @@
public class WebLinkInfo extends JavaScriptObject {
public final native String name() /*-{ return this.name; }-*/;
+ public final native String imageUrl() /*-{ return this.image_url; }-*/;
public final native String url() /*-{ return this.url; }-*/;
protected WebLinkInfo() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/actions/ActionButton.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/actions/ActionButton.java
index ab94a75c..2e2d314 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/actions/ActionButton.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/actions/ActionButton.java
@@ -16,9 +16,11 @@
import com.google.gerrit.client.api.ActionContext;
import com.google.gerrit.client.api.ChangeGlue;
+import com.google.gerrit.client.api.EditGlue;
import com.google.gerrit.client.api.ProjectGlue;
import com.google.gerrit.client.api.RevisionGlue;
import com.google.gerrit.client.changes.ChangeInfo;
+import com.google.gerrit.client.changes.ChangeInfo.EditInfo;
import com.google.gerrit.client.changes.ChangeInfo.RevisionInfo;
import com.google.gerrit.client.projects.BranchInfo;
import com.google.gerrit.reviewdb.client.Project;
@@ -31,30 +33,37 @@
private final Project.NameKey project;
private final BranchInfo branch;
private final ChangeInfo change;
+ private final EditInfo edit;
private final RevisionInfo revision;
private final ActionInfo action;
private ActionContext ctx;
public ActionButton(Project.NameKey project, ActionInfo action) {
- this(project, null, null, null, action);
+ this(project, null, null, null, null, action);
}
public ActionButton(Project.NameKey project, BranchInfo branch,
ActionInfo action) {
- this(project, branch, null, null, action);
+ this(project, branch, null, null, null, action);
}
public ActionButton(ChangeInfo change, ActionInfo action) {
- this(change, null, action);
+ this(null, null, change, null, null, action);
}
public ActionButton(ChangeInfo change, RevisionInfo revision,
ActionInfo action) {
- this(null, null, change, revision, action);
+ this(null, null, change, null, revision, action);
+ }
+
+ public ActionButton(ChangeInfo change, EditInfo edit,
+ ActionInfo action) {
+ this(null, null, change, edit, null, action);
}
private ActionButton(Project.NameKey project, BranchInfo branch,
- ChangeInfo change, RevisionInfo revision, ActionInfo action) {
+ ChangeInfo change, EditInfo edit, RevisionInfo revision,
+ ActionInfo action) {
super(new SafeHtmlBuilder()
.openDiv()
.append(action.label())
@@ -67,6 +76,7 @@
this.project = project;
this.branch = branch;
this.change = change;
+ this.edit = edit;
this.revision = revision;
this.action = action;
}
@@ -81,6 +91,8 @@
if (revision != null) {
RevisionGlue.onAction(change, revision, action, this);
+ } else if (edit != null) {
+ EditGlue.onAction(change, edit, action, this);
} else if (change != null) {
ChangeGlue.onAction(change, action, this);
} else if (branch != null) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
index df79380..1bb0386e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
@@ -109,6 +109,7 @@
abandon, \
create, \
deleteDrafts, \
+ editHashtags, \
editTopicName, \
forgeAuthor, \
forgeCommitter, \
@@ -129,6 +130,7 @@
abandon = Abandon
create = Create Reference
deleteDrafts = Delete Drafts
+editHashtags = Edit Hashtags
editTopicName = Edit Topic Name
forgeAuthor = Forge Author Identity
forgeCommitter = Forge Committer Identity
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
index fa1a21f..e81162f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
@@ -239,8 +239,16 @@
for (WebLinkInfo weblink : webLinks) {
Anchor a = new Anchor();
- a.setText("(" + weblink.name() + ")");
a.setHref(weblink.url());
+ if (weblink.imageUrl() != null && !weblink.imageUrl().isEmpty()) {
+ Image img = new Image();
+ img.setAltText(weblink.name());
+ img.setUrl(weblink.imageUrl());
+ img.setTitle(weblink.name());
+ a.getElement().appendChild(img.getElement());
+ } else {
+ a.setText("(" + weblink.name() + ")");
+ }
p.add(a);
}
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ActionContext.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ActionContext.java
index 7490d82..7bcac5f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ActionContext.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ActionContext.java
@@ -17,6 +17,7 @@
import com.google.gerrit.client.actions.ActionButton;
import com.google.gerrit.client.actions.ActionInfo;
import com.google.gerrit.client.changes.ChangeInfo;
+import com.google.gerrit.client.changes.ChangeInfo.EditInfo;
import com.google.gerrit.client.changes.ChangeInfo.RevisionInfo;
import com.google.gerrit.client.projects.BranchInfo;
import com.google.gerrit.client.rpc.GerritCallback;
@@ -137,6 +138,7 @@
final native void set(ActionInfo a) /*-{ this.action=a; }-*/;
final native void set(ChangeInfo c) /*-{ this.change=c; }-*/;
+ final native void set(EditInfo e) /*-{ this.edit=e; }-*/;
final native void set(Project.NameKey p) /*-{ this.project=p; }-*/;
final native void set(BranchInfo b) /*-{ this.branch=b }-*/;
final native void set(RevisionInfo r) /*-{ this.revision=r; }-*/;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ApiGlue.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ApiGlue.java
index 8da896e..b0b9e66 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ApiGlue.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ApiGlue.java
@@ -41,6 +41,7 @@
plugins: {},
screens: {},
change_actions: {},
+ edit_actions: {},
revision_actions: {},
project_actions: {},
branch_actions: {},
@@ -71,6 +72,7 @@
_onAction: function (p,t,n,c) {
var i = p+'~'+n;
if ('change' == t) this.change_actions[i]=c;
+ else if ('edit' == t) this.edit_actions[i]=c;
else if ('revision' == t) this.revision_actions[i]=c;
else if ('project' == t) this.project_actions[i]=c;
else if ('branch' == t) this.branch_actions[i]=c;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/EditGlue.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/EditGlue.java
new file mode 100644
index 0000000..ebcafb8
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/EditGlue.java
@@ -0,0 +1,54 @@
+// Copyright (C) 2014 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 com.google.gerrit.client.api;
+
+import com.google.gerrit.client.actions.ActionButton;
+import com.google.gerrit.client.actions.ActionInfo;
+import com.google.gerrit.client.changes.ChangeApi;
+import com.google.gerrit.client.changes.ChangeInfo;
+import com.google.gerrit.client.changes.ChangeInfo.EditInfo;
+import com.google.gerrit.client.rpc.RestApi;
+import com.google.gwt.core.client.JavaScriptObject;
+
+public class EditGlue {
+ public static void onAction(
+ ChangeInfo change,
+ EditInfo edit,
+ ActionInfo action,
+ ActionButton button) {
+ RestApi api = ChangeApi.edit(
+ change.legacy_id().get())
+ .view(action.id());
+
+ JavaScriptObject f = get(action.id());
+ if (f != null) {
+ ActionContext c = ActionContext.create(api);
+ c.set(action);
+ c.set(change);
+ c.set(edit);
+ c.button(button);
+ ApiGlue.invoke(f, c);
+ } else {
+ DefaultActions.invoke(change, action, api);
+ }
+ }
+
+ private static final native JavaScriptObject get(String id) /*-{
+ return $wnd.Gerrit.edit_actions[id];
+ }-*/;
+
+ private EditGlue() {
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.java
index 906efca..1123bf3 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.java
@@ -19,6 +19,7 @@
import com.google.gerrit.client.actions.ActionInfo;
import com.google.gerrit.client.changes.ChangeInfo;
import com.google.gerrit.client.changes.ChangeInfo.CommitInfo;
+import com.google.gerrit.client.changes.ChangeInfo.EditInfo;
import com.google.gerrit.client.changes.ChangeInfo.RevisionInfo;
import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.reviewdb.client.Change;
@@ -46,6 +47,9 @@
@UiField Button cherrypick;
@UiField Button deleteChange;
@UiField Button deleteRevision;
+ @UiField Button deleteEdit;
+ @UiField Button publishEdit;
+ @UiField Button rebaseEdit;
@UiField Button publish;
@UiField Button rebase;
@UiField Button revert;
@@ -84,6 +88,7 @@
initChangeActions(info, hasUser);
initRevisionActions(info, revInfo, hasUser);
+ initEditActions(info, info.edit(), hasUser);
}
private void initChangeActions(ChangeInfo info, boolean hasUser) {
@@ -103,6 +108,26 @@
}
}
+ private void initEditActions(ChangeInfo info, EditInfo editInfo,
+ boolean hasUser) {
+ if (!info.has_edit() || !info.current_revision().equals(editInfo.name())) {
+ return;
+ }
+ NativeMap<ActionInfo> actions = editInfo.has_actions()
+ ? editInfo.actions()
+ : NativeMap.<ActionInfo> create();
+ actions.copyKeysIntoChildren("id");
+
+ if (hasUser) {
+ a2b(actions, "/", deleteEdit);
+ a2b(actions, "publish", publishEdit);
+ a2b(actions, "rebase", rebaseEdit);
+ for (String id : filterNonCore(actions)) {
+ add(new ActionButton(info, editInfo, actions.get(id)));
+ }
+ }
+ }
+
private void initRevisionActions(ChangeInfo info, RevisionInfo revInfo,
boolean hasUser) {
NativeMap<ActionInfo> actions = revInfo.has_actions()
@@ -164,6 +189,21 @@
DraftActions.publish(changeId, revision);
}
+ @UiHandler("deleteEdit")
+ void onDeleteEdit(ClickEvent e) {
+ EditActions.deleteEdit(changeId);
+ }
+
+ @UiHandler("publishEdit")
+ void onPublishEdit(ClickEvent e) {
+ EditActions.publishEdit(changeId);
+ }
+
+ @UiHandler("rebaseEdit")
+ void onRebaseEdit(ClickEvent e) {
+ EditActions.rebaseEdit(changeId);
+ }
+
@UiHandler("deleteRevision")
void onDeleteRevision(ClickEvent e) {
DraftActions.delete(changeId, revision);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.ui.xml
index cb2b37f..c18d7f3 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.ui.xml
@@ -92,6 +92,15 @@
<g:Button ui:field='publish' styleName='' visible='false'>
<div><ui:msg>Publish</ui:msg></div>
</g:Button>
+ <g:Button ui:field='deleteEdit' styleName='' visible='false'>
+ <div><ui:msg>Delete Edit</ui:msg></div>
+ </g:Button>
+ <g:Button ui:field='publishEdit' styleName='' visible='false'>
+ <div><ui:msg>Publish Edit</ui:msg></div>
+ </g:Button>
+ <g:Button ui:field='rebaseEdit' styleName='' visible='false'>
+ <div><ui:msg>Rebase Edit</ui:msg></div>
+ </g:Button>
<g:Button ui:field='abandon' styleName='{style.red}' visible='false'>
<div><ui:msg>Abandon</ui:msg></div>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeConstants.java
index 11a1824..9bcc24f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeConstants.java
@@ -21,6 +21,9 @@
String nextChange();
String openChange();
String reviewedFileTitle();
+ String editFileInline();
+ String removeFileInline();
+ String restoreFileInline();
String openLastFile();
String openCommitMessage();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeConstants.properties
index 289e9b4..e16f5be 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeConstants.properties
@@ -2,6 +2,9 @@
nextChange = Next related change
openChange = Open related change
reviewedFileTitle = Mark file as reviewed (Shortcut: r)
+editFileInline = Edit file inline
+removeFileInline = Remove file inline
+restoreFileInline = Restore file inline
openLastFile = Open last file
openCommitMessage = Open commit message
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeMessages.java
index 595a6c9..55f5470 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeMessages.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeMessages.java
@@ -17,7 +17,7 @@
import com.google.gwt.i18n.client.Messages;
public interface ChangeMessages extends Messages {
- String patchSets(int currentlyViewedPatchSet, int currentPatchSet);
+ String patchSets(String currentlyViewedPatchSet, String currentPatchSet);
String changeWithNoRevisions(int changeId);
String relatedChanges(int count);
String relatedChanges(String count);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.java
index 69cd3fc..ad763f8 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.java
@@ -24,6 +24,7 @@
import com.google.gerrit.client.changes.ChangeApi;
import com.google.gerrit.client.changes.ChangeInfo;
import com.google.gerrit.client.changes.ChangeInfo.CommitInfo;
+import com.google.gerrit.client.changes.ChangeInfo.EditInfo;
import com.google.gerrit.client.changes.ChangeInfo.MessageInfo;
import com.google.gerrit.client.changes.ChangeInfo.RevisionInfo;
import com.google.gerrit.client.changes.ChangeList;
@@ -125,6 +126,7 @@
private String revision;
private ChangeInfo changeInfo;
private CommentLinkProcessor commentLinkProcessor;
+ private EditInfo edit;
private KeyCommandSet keysNavigation;
private KeyCommandSet keysAction;
@@ -134,6 +136,7 @@
private UpdateAvailableBar updateAvailable;
private boolean openReplyBox;
private boolean loaded;
+ private FileTable.Mode fileTableMode;
@UiField HTMLPanel headerLine;
@UiField Style style;
@@ -170,6 +173,9 @@
@UiField Button download;
@UiField Button reply;
@UiField Button openAll;
+ @UiField Button editMode;
+ @UiField Button reviewMode;
+ @UiField Button addFile;
@UiField Button expandAll;
@UiField Button collapseAll;
@UiField Button editMessage;
@@ -180,12 +186,15 @@
private IncludedInAction includedInAction;
private PatchSetsAction patchSetsAction;
private DownloadAction downloadAction;
+ private EditFileAction editFileAction;
- public ChangeScreen2(Change.Id changeId, String base, String revision, boolean openReplyBox) {
+ public ChangeScreen2(Change.Id changeId, String base, String revision,
+ boolean openReplyBox, FileTable.Mode mode) {
this.changeId = changeId;
this.base = normalize(base);
this.revision = normalize(revision);
this.openReplyBox = openReplyBox;
+ this.fileTableMode = mode;
add(uiBinder.createAndBindUi(this));
}
@@ -196,13 +205,24 @@
@Override
protected void onLoad() {
super.onLoad();
- loadChangeInfo(true, new GerritCallback<ChangeInfo>() {
- @Override
- public void onSuccess(ChangeInfo info) {
- info.init();
- loadConfigInfo(info, base);
- }
- });
+ CallbackGroup group = new CallbackGroup();
+ if (Gerrit.isSignedIn()) {
+ ChangeApi.editWithFiles(changeId.get(), group.add(
+ new GerritCallback<EditInfo>() {
+ @Override
+ public void onSuccess(EditInfo result) {
+ edit = result;
+ }
+ }));
+ }
+ loadChangeInfo(true, group.addFinal(
+ new GerritCallback<ChangeInfo>() {
+ @Override
+ public void onSuccess(ChangeInfo info) {
+ info.init();
+ loadConfigInfo(info, base);
+ }
+ }));
}
void loadChangeInfo(boolean fg, AsyncCallback<ChangeInfo> cb) {
@@ -340,17 +360,17 @@
}
private void initRevisionsAction(ChangeInfo info, String revision) {
- int currentPatchSet;
+ String currentPatchSet;
if (info.current_revision() != null
&& info.revisions().containsKey(info.current_revision())) {
- currentPatchSet = info.revision(info.current_revision())._number();
+ currentPatchSet = info.revision(info.current_revision()).id();
} else {
JsArray<RevisionInfo> revList = info.revisions().values();
RevisionInfo.sortRevisionInfoByNumber(revList);
- currentPatchSet = revList.get(revList.length() - 1)._number();
+ currentPatchSet = revList.get(revList.length() - 1).id();
}
- int currentlyViewedPatchSet = info.revision(revision)._number();
+ String currentlyViewedPatchSet = info.revision(revision).id();
patchSetsText.setInnerText(Resources.M.patchSets(
currentlyViewedPatchSet, currentPatchSet));
patchSetsAction = new PatchSetsAction(
@@ -392,6 +412,18 @@
null)));
}
+ private void initEditMode(ChangeInfo info) {
+ if (Gerrit.isSignedIn() && info.status() == Status.NEW) {
+ editMode.setVisible(fileTableMode == FileTable.Mode.REVIEW);
+ addFile.setVisible(!editMode.isVisible());
+ reviewMode.setVisible(!editMode.isVisible());
+ }
+ RevisionInfo rev = info.revision(revision);
+ editFileAction = new EditFileAction(
+ new PatchSet.Id(changeId, rev._number()),
+ "", "", style, editMessage, reply);
+ }
+
private void initEditMessageAction(ChangeInfo info, String revision) {
NativeMap<ActionInfo> actions = info.revision(revision).actions();
if (actions != null && actions.containsKey("message")) {
@@ -520,6 +552,37 @@
files.openAll();
}
+ @UiHandler("editMode")
+ void onEditMode(ClickEvent e) {
+ fileTableMode = FileTable.Mode.EDIT;
+ refreshFileTable();
+ editMode.setVisible(false);
+ addFile.setVisible(true);
+ reviewMode.setVisible(true);
+ }
+
+ @UiHandler("reviewMode")
+ void onReviewMode(ClickEvent e) {
+ fileTableMode = FileTable.Mode.REVIEW;
+ refreshFileTable();
+ editMode.setVisible(true);
+ addFile.setVisible(false);
+ reviewMode.setVisible(false);
+ }
+
+ @UiHandler("addFile")
+ void onAddFile(ClickEvent e) {
+ editFileAction.onEdit();
+ }
+
+ private void refreshFileTable() {
+ int idx = diffBase.getSelectedIndex();
+ if (0 <= idx) {
+ String n = diffBase.getValue(idx);
+ loadConfigInfo(changeInfo, !n.isEmpty() ? n : null);
+ }
+ }
+
@UiHandler("expandAll")
void onExpandAll(ClickEvent e) {
int n = history.getWidgetCount();
@@ -551,11 +614,57 @@
private void loadConfigInfo(final ChangeInfo info, final String base) {
info.revisions().copyKeysIntoChildren("name");
+ if (edit != null) {
+ edit.set_name(edit.commit().commit());
+ info.set_edit(edit);
+ if (edit.has_files()) {
+ edit.files().copyKeysIntoChildren("path");
+ }
+ info.revisions().put(edit.name(), RevisionInfo.fromEdit(edit));
+ JsArray<RevisionInfo> list = info.revisions().values();
+
+ // Edit is converted to a regular revision (with number = 0) and
+ // added to the list of revisions. Additionally under certain
+ // circumstances change edit is assigned to be the current revision
+ // and is selected to be shown on the change screen.
+ // We have two different strategies to assign edit to the current ps:
+ // 1. revision == null: no revision is selected, so use the edit only
+ // if it is based on the latest patch set
+ // 2. edit was selected explicitly from ps drop down:
+ // use the edit regardless of which patch set it is based on
+ if (revision == null) {
+ RevisionInfo.sortRevisionInfoByNumber(list);
+ RevisionInfo rev = list.get(list.length() - 1);
+ if (rev.is_edit()) {
+ info.set_current_revision(rev.name());
+ }
+ } else if (revision.equals("edit") || revision.equals("0")) {
+ for (int i = 0; i < list.length(); i++) {
+ RevisionInfo r = list.get(i);
+ if (r.is_edit()) {
+ info.set_current_revision(r.name());
+ break;
+ }
+ }
+ }
+ }
final RevisionInfo rev = resolveRevisionToDisplay(info);
final RevisionInfo b = resolveRevisionOrPatchSetId(info, base, null);
CallbackGroup group = new CallbackGroup();
- loadDiff(b, rev, myLastReply(info), group);
+ if (rev.is_edit()) {
+ NativeMap<JsArray<CommentInfo>> emptyComment = NativeMap.create();
+ files.set(
+ b != null ? new PatchSet.Id(changeId, b._number()) : null,
+ new PatchSet.Id(changeId, rev._number()),
+ style, editMessage, reply);
+ files.setValue(info.edit().files(), myLastReply(info),
+ emptyComment,
+ emptyComment,
+ fileTableMode);
+ } else {
+ loadDiff(b, rev, myLastReply(info), group);
+ }
loadCommit(rev, group);
if (loaded) {
@@ -600,10 +709,12 @@
group.add(new AsyncCallback<NativeMap<FileInfo>>() {
@Override
public void onSuccess(NativeMap<FileInfo> m) {
- files.setRevisions(
+ files.set(
base != null ? new PatchSet.Id(changeId, base._number()) : null,
- new PatchSet.Id(changeId, rev._number()));
- files.setValue(m, myLastReply, comments.get(0), drafts.get(0));
+ new PatchSet.Id(changeId, rev._number()),
+ style, editMessage, reply);
+ files.setValue(m, myLastReply, comments.get(0),
+ drafts.get(0), fileTableMode);
}
@Override
@@ -611,7 +722,7 @@
}
}));
- if (Gerrit.isSignedIn()) {
+ if (Gerrit.isSignedIn() && fileTableMode == FileTable.Mode.REVIEW) {
ChangeApi.revision(changeId.get(), rev.name())
.view("files")
.addParameterTrue("reviewed")
@@ -671,6 +782,9 @@
}
private void loadCommit(final RevisionInfo rev, CallbackGroup group) {
+ if (rev.is_edit()) {
+ return;
+ }
ChangeApi.revision(changeId.get(), rev.name())
.view("commit")
.get(group.add(new AsyncCallback<CommitInfo>() {
@@ -772,10 +886,14 @@
private void renderChangeInfo(ChangeInfo info) {
changeInfo = info;
lastDisplayedUpdate = info.updated();
+ RevisionInfo revisionInfo = info.revision(revision);
boolean current = info.status().isOpen()
- && revision.equals(info.current_revision());
+ && revision.equals(info.current_revision())
+ && !revisionInfo.is_edit();
- if (!current && info.status() == Change.Status.NEW) {
+ if (revisionInfo.is_edit()) {
+ statusText.setInnerText(Util.C.changeEdit());
+ } else if (!current && info.status() == Change.Status.NEW) {
statusText.setInnerText(Util.C.notCurrent());
labels.setVisible(false);
} else {
@@ -791,6 +909,7 @@
initDownloadAction(info, revision);
initProjectLinks(info);
initBranchLink(info);
+ initEditMode(info);
actions.display(info, revision);
star.setValue(info.starred());
@@ -882,7 +1001,7 @@
for (int i = list.length() - 1; i >= 0; i--) {
RevisionInfo r = list.get(i);
diffBase.addItem(
- r._number() + ": " + r.name().substring(0, 6),
+ r.id() + ": " + r.name().substring(0, 6),
r.name());
if (r.name().equals(revision)) {
SelectElement.as(diffBase.getElement()).getOptions()
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.ui.xml
index 2b09ef9..e66a947 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.ui.xml
@@ -462,6 +462,27 @@
<div class='{style.diffBase}'>
<ui:msg>Diff against: <g:ListBox ui:field='diffBase' styleName=''/></ui:msg>
</div>
+ <g:Button ui:field='editMode'
+ styleName=''
+ visible='false'
+ title='Switch file table to edit mode'>
+ <ui:attribute name='title'/>
+ <div><ui:msg>Edit</ui:msg></div>
+ </g:Button>
+ <g:Button ui:field='reviewMode'
+ styleName=''
+ visible='false'
+ title='Done with edit mode'>
+ <ui:attribute name='title'/>
+ <div><ui:msg>Done</ui:msg></div>
+ </g:Button>
+ <g:Button ui:field='addFile'
+ title='Add file to this change'
+ styleName=''
+ visible='false'>
+ <ui:attribute name='title'/>
+ <div><ui:msg>Add…</ui:msg></div>
+ </g:Button>
</div>
</div>
<c:FileTable ui:field='files'/>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java
index 62a9373..c5c66c1 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java
@@ -30,15 +30,12 @@
import com.google.gerrit.common.PageLinks;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JsArray;
-import com.google.gwt.dom.client.AnchorElement;
import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.TableCellElement;
import com.google.gwt.event.dom.client.ClickEvent;
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;
-import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
@@ -66,7 +63,7 @@
@UiField FlowPanel committerPanel;
@UiField Image mergeCommit;
@UiField CopyableLabel commitName;
- @UiField TableCellElement webLinkCell;
+ @UiField FlowPanel webLinkPanel;
@UiField Element parents;
@UiField FlowPanel parentCommits;
@UiField FlowPanel parentWebLinks;
@@ -121,30 +118,39 @@
if (revInfo.commit().parents().length() > 1) {
mergeCommit.setVisible(true);
- setParents(change.project(), revInfo.commit().parents());
}
+ setParents(change.project(), revInfo.commit().parents());
}
private void setWebLinks(ChangeInfo change, String revision,
RevisionInfo revInfo) {
GitwebLink gw = Gerrit.getGitwebLink();
if (gw != null && gw.canLink(revInfo)) {
- addWebLink(gw.toRevision(change.project(), revision), gw.getLinkName());
+ addWebLink(gw.toRevision(change.project(), revision),
+ gw.getLinkName(), null);
}
JsArray<WebLinkInfo> links = revInfo.web_links();
if (links != null) {
for (WebLinkInfo link : Natives.asList(links)) {
- addWebLink(link.url(), parenthesize(link.name()));
+ addWebLink(link.url(), parenthesize(link.name()), link.imageUrl());
}
}
}
- private void addWebLink(String href, String name) {
- AnchorElement a = DOM.createAnchor().cast();
+ private void addWebLink(String href, String name, String imageUrl) {
+ Anchor a = new Anchor();
a.setHref(href);
- a.setInnerText(name);
- webLinkCell.appendChild(a);
+ if (imageUrl != null && !imageUrl.isEmpty()) {
+ Image img = new Image();
+ img.setAltText(name);
+ img.setUrl(imageUrl);
+ img.setTitle(name);
+ a.getElement().appendChild(img.getElement());
+ } else {
+ a.setText(name);
+ }
+ webLinkPanel.add(a);
}
private void setParents(String project, JsArray<CommitInfo> commits) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.ui.xml
index c1bed07..a645cad 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.ui.xml
@@ -81,9 +81,12 @@
right: -16px;
}
<!-- To make room for the copyableLabel from the adjacent column -->
- .webLinkCell a:first-child {
+ .webLinkPanel a:first-child {
margin-left:16px;
}
+ .webLinkPanel>a {
+ margin-left:2px;
+ }
.parentWebLink {
margin-left:16px;
display: block;
@@ -152,10 +155,12 @@
</g:Image>
</th>
<td><clippy:CopyableLabel styleName='{style.clippy}' ui:field='commitName'/></td>
- <td ui:field='webLinkCell' class='{style.webLinkCell}'></td>
+ <td>
+ <g:FlowPanel ui:field='webLinkPanel' styleName='{style.webLinkPanel}'/>
+ </td>
</tr>
<tr ui:field='parents' style='display: none'>
- <th><ui:msg>Parents</ui:msg></th>
+ <th><ui:msg>Parent(s)</ui:msg></th>
<td>
<g:FlowPanel ui:field='parentCommits'/>
</td>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java
index 09335c1..96cbc28 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java
@@ -18,6 +18,7 @@
import com.google.gerrit.client.account.AccountApi;
import com.google.gerrit.client.changes.ChangeApi;
import com.google.gerrit.client.changes.ChangeInfo;
+import com.google.gerrit.client.changes.ChangeInfo.EditInfo;
import com.google.gerrit.client.changes.ChangeInfo.FetchInfo;
import com.google.gerrit.client.changes.ChangeList;
import com.google.gerrit.client.rpc.NativeMap;
@@ -78,23 +79,38 @@
@Override
protected void onLoad() {
if (fetch == null) {
- RestApi call = ChangeApi.detail(change.legacy_id().get());
- ChangeList.addOptions(call, EnumSet.of(
- revision.equals(change.current_revision())
- ? ListChangesOption.CURRENT_REVISION
- : ListChangesOption.ALL_REVISIONS,
- ListChangesOption.DOWNLOAD_COMMANDS));
- call.get(new AsyncCallback<ChangeInfo>() {
- @Override
- public void onSuccess(ChangeInfo result) {
- fetch = result.revision(revision).fetch();
- renderScheme();
- }
+ if (psId.get() == 0) {
+ ChangeApi.editWithCommands(change.legacy_id().get()).get(
+ new AsyncCallback<EditInfo>() {
+ @Override
+ public void onSuccess(EditInfo result) {
+ fetch = result.fetch();
+ renderScheme();
+ }
- @Override
- public void onFailure(Throwable caught) {
- }
- });
+ @Override
+ public void onFailure(Throwable caught) {
+ }
+ });
+ } else {
+ RestApi call = ChangeApi.detail(change.legacy_id().get());
+ ChangeList.addOptions(call, EnumSet.of(
+ revision.equals(change.current_revision())
+ ? ListChangesOption.CURRENT_REVISION
+ : ListChangesOption.ALL_REVISIONS,
+ ListChangesOption.DOWNLOAD_COMMANDS));
+ call.get(new AsyncCallback<ChangeInfo>() {
+ @Override
+ public void onSuccess(ChangeInfo result) {
+ fetch = result.revision(revision).fetch();
+ renderScheme();
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ }
+ });
+ }
}
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditActions.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditActions.java
new file mode 100644
index 0000000..ec9a375
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditActions.java
@@ -0,0 +1,56 @@
+// Copyright (C) 2014 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 com.google.gerrit.client.change;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.changes.ChangeApi;
+import com.google.gerrit.client.changes.SubmitFailureDialog;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gwt.core.client.JavaScriptObject;
+
+public class EditActions {
+
+ static void deleteEdit(Change.Id id) {
+ ChangeApi.deleteEdit(id.get(), cs(id));
+ }
+
+ static void publishEdit(Change.Id id) {
+ ChangeApi.publishEdit(id.get(), cs(id));
+ }
+
+ static void rebaseEdit(Change.Id id) {
+ ChangeApi.rebaseEdit(id.get(), cs(id));
+ }
+
+ public static GerritCallback<JavaScriptObject> cs(
+ final Change.Id id) {
+ return new GerritCallback<JavaScriptObject>() {
+ public void onSuccess(JavaScriptObject result) {
+ Gerrit.display(PageLinks.toChange(id));
+ }
+
+ public void onFailure(Throwable err) {
+ if (SubmitFailureDialog.isConflict(err)) {
+ new SubmitFailureDialog(err.getMessage()).center();
+ Gerrit.display(PageLinks.toChange(id));
+ } else {
+ super.onFailure(err);
+ }
+ }
+ };
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditFileAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditFileAction.java
new file mode 100644
index 0000000..d0c8fe7
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditFileAction.java
@@ -0,0 +1,80 @@
+//Copyright (C) 2013 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 com.google.gerrit.client.change;
+
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gwt.event.logical.shared.CloseEvent;
+import com.google.gwt.event.logical.shared.CloseHandler;
+import com.google.gwt.user.client.ui.PopupPanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.gwtexpui.globalkey.client.GlobalKey;
+import com.google.gwtexpui.user.client.PluginSafePopupPanel;
+
+class EditFileAction {
+ private final PatchSet.Id id;
+ private final String content;
+ private final String file;
+ private final ChangeScreen2.Style style;
+ private final Widget editMessageButton;
+ private final Widget relativeTo;
+
+ private EditFileBox editBox;
+ private PopupPanel popup;
+
+ EditFileAction(
+ PatchSet.Id id,
+ String content,
+ String file,
+ ChangeScreen2.Style style,
+ Widget editButton,
+ Widget replyButton) {
+ this.id = id;
+ this.content = content;
+ this.file = file;
+ this.style = style;
+ this.editMessageButton = editButton;
+ this.relativeTo = replyButton;
+ }
+
+ void onEdit() {
+ if (popup != null) {
+ popup.hide();
+ return;
+ }
+
+ if (editBox == null) {
+ editBox = new EditFileBox(
+ id,
+ content,
+ file);
+ }
+
+ final PluginSafePopupPanel p = new PluginSafePopupPanel(true);
+ p.setStyleName(style.replyBox());
+ p.addAutoHidePartner(editMessageButton.getElement());
+ p.addCloseHandler(new CloseHandler<PopupPanel>() {
+ @Override
+ public void onClose(CloseEvent<PopupPanel> event) {
+ if (popup == p) {
+ popup = null;
+ }
+ }
+ });
+ p.add(editBox);
+ p.showRelativeTo(relativeTo);
+ GlobalKey.dialog(p);
+ popup = p;
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditFileBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditFileBox.java
new file mode 100644
index 0000000..803990f
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditFileBox.java
@@ -0,0 +1,110 @@
+//Copyright (C) 2013 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 com.google.gerrit.client.change;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.VoidResult;
+import com.google.gerrit.client.changes.ChangeFileApi;
+import com.google.gerrit.client.ui.TextBoxChangeListener;
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.core.client.Scheduler.ScheduledCommand;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.uibinder.client.UiHandler;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.HTMLPanel;
+import com.google.gwt.user.client.ui.PopupPanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.gwtexpui.globalkey.client.NpTextArea;
+
+class EditFileBox extends Composite {
+ interface Binder extends UiBinder<HTMLPanel, EditFileBox> {}
+ private static final Binder uiBinder = GWT.create(Binder.class);
+
+ final private PatchSet.Id id;
+ final private String fileName;
+ final private String fileContent;
+
+ @UiField FileTextBox file;
+ @UiField NpTextArea content;
+ @UiField Button save;
+ @UiField Button cancel;
+
+ EditFileBox(
+ PatchSet.Id id,
+ String fileC,
+ String fileName) {
+ this.id = id;
+ this.fileName = fileName;
+ this.fileContent = fileC;
+ initWidget(uiBinder.createAndBindUi(this));
+ new TextBoxChangeListener(content) {
+ public void onTextChanged(String newText) {
+ save.setEnabled(!file.getText().trim().isEmpty()
+ && !newText.trim().equals(fileContent));
+ }
+ };
+ }
+
+ @Override
+ protected void onLoad() {
+ file.set(id, content);
+ file.setText(fileName);
+ file.setEnabled(fileName.isEmpty());
+ content.setText(fileContent);
+ save.setEnabled(false);
+ Scheduler.get().scheduleDeferred(new ScheduledCommand() {
+ @Override
+ public void execute() {
+ file.setFocus(true);
+ }});
+ }
+
+ @UiHandler("save")
+ void onSave(ClickEvent e) {
+ ChangeFileApi.putContent(id, file.getText(), content.getText(),
+ new AsyncCallback<VoidResult>() {
+ @Override
+ public void onSuccess(VoidResult result) {
+ Gerrit.display(PageLinks.toChangeInEditMode(id.getParentKey()));
+ hide();
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ }
+ });
+ }
+
+ @UiHandler("cancel")
+ void onCancel(ClickEvent e) {
+ hide();
+ }
+
+ protected void hide() {
+ for (Widget w = getParent(); w != null; w = w.getParent()) {
+ if (w instanceof PopupPanel) {
+ ((PopupPanel) w).hide();
+ break;
+ }
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditFileBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditFileBox.ui.xml
new file mode 100644
index 0000000..3a0e0be
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditFileBox.ui.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2013 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.
+-->
+<ui:UiBinder
+ xmlns:ui='urn:ui:com.google.gwt.uibinder'
+ xmlns:c='urn:import:com.google.gwtexpui.globalkey.client'
+ xmlns:f='urn:import:com.google.gerrit.client.change'
+ xmlns:g='urn:import:com.google.gwt.user.client.ui'>
+ <ui:with field='res' type='com.google.gerrit.client.change.Resources'/>
+ <ui:style>
+ .fileContent {
+ background-color: white;
+ font-family: monospace;
+ }
+ .cancel { float: right; }
+ </ui:style>
+ <g:HTMLPanel>
+ <div class='{res.style.section}'>
+ <div>
+ <ui:msg>Path:</ui:msg>
+ </div>
+ <div>
+ <f:FileTextBox ui:field='file' visibleLength='79'/>
+ </div>
+ <div>
+ <ui:msg>Content:</ui:msg>
+ </div>
+ <c:NpTextArea
+ visibleLines='30'
+ characterWidth='78'
+ styleName='{style.fileContent}'
+ ui:field='content'/>
+ </div>
+ <div class='{res.style.section}'>
+ <g:Button ui:field='save'
+ title='Create new revision edit'
+ styleName='{res.style.button}'>
+ <ui:attribute name='title'/>
+ <div><ui:msg>Save</ui:msg></div>
+ </g:Button>
+ <g:Button ui:field='cancel'
+ styleName='{res.style.button}'
+ addStyleNames='{style.cancel}'>
+ <div>Cancel</div>
+ </g:Button>
+ </div>
+ </g:HTMLPanel>
+</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditMessageBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditMessageBox.java
index 700638a..264465d 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditMessageBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditMessageBox.java
@@ -55,6 +55,7 @@
this.revision = revision;
this.originalMessage = msg.trim();
initWidget(uiBinder.createAndBindUi(this));
+ message.getElement().setAttribute("wrap", "off");
message.setText("");
new TextBoxChangeListener(message) {
public void onTextChanged(String newText) {
@@ -96,7 +97,7 @@
hide();
}
- private void hide() {
+ protected void hide() {
for (Widget w = getParent(); w != null; w = w.getParent()) {
if (w instanceof PopupPanel) {
((PopupPanel) w).hide();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java
index e097006..4aea912 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java
@@ -16,20 +16,25 @@
import com.google.gerrit.client.Dispatcher;
import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.VoidResult;
import com.google.gerrit.client.changes.ChangeApi;
+import com.google.gerrit.client.changes.ChangeFileApi;
import com.google.gerrit.client.changes.CommentInfo;
import com.google.gerrit.client.changes.ReviewInfo;
import com.google.gerrit.client.changes.Util;
import com.google.gerrit.client.diff.FileInfo;
import com.google.gerrit.client.patches.PatchUtil;
import com.google.gerrit.client.rpc.CallbackGroup;
+import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.client.rpc.Natives;
import com.google.gerrit.client.rpc.RestApi;
import com.google.gerrit.client.ui.NavigationTable;
+import com.google.gerrit.common.PageLinks;
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.Patch.ChangeType;
import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.reviewdb.client.PatchSet.Id;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.JsArrayString;
@@ -46,8 +51,11 @@
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.EventListener;
import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
+import com.google.gwt.user.client.ui.ImageResourceRenderer;
+import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.impl.HyperlinkImpl;
import com.google.gwtexpui.globalkey.client.KeyCommand;
import com.google.gwtexpui.progress.client.ProgressBar;
@@ -55,7 +63,7 @@
import java.sql.Timestamp;
-class FileTable extends FlowPanel {
+public class FileTable extends FlowPanel {
static final FileTableResources R = GWT
.create(FileTableResources.class);
@@ -80,20 +88,42 @@
String deltaColumn2();
String inserted();
String deleted();
+ String editButton();
+ String removeButton();
}
+ public static enum Mode {
+ REVIEW,
+ EDIT
+ }
+
+ private static final String DELETE;
+ private static final String EDIT;
+ private static final String RESTORE;
private static final String REVIEWED;
private static final String OPEN;
private static final int C_PATH = 3;
private static final HyperlinkImpl link = GWT.create(HyperlinkImpl.class);
static {
+ DELETE = DOM.createUniqueId().replace('-', '_');
+ EDIT = DOM.createUniqueId().replace('-', '_');
+ RESTORE = DOM.createUniqueId().replace('-', '_');
REVIEWED = DOM.createUniqueId().replace('-', '_');
OPEN = DOM.createUniqueId().replace('-', '_');
- init(REVIEWED, OPEN);
+ init(DELETE, EDIT, RESTORE, REVIEWED, OPEN);
}
- private static final native void init(String r, String o) /*-{
+ private static final native void init(String d, String e, String t, String r, String o) /*-{
+ $wnd[d] = $entry(function(e,i) {
+ @com.google.gerrit.client.change.FileTable::onDelete(Lcom/google/gwt/dom/client/NativeEvent;I)(e,i)
+ });
+ $wnd[e] = $entry(function(e,i) {
+ @com.google.gerrit.client.change.FileTable::onEdit(Lcom/google/gwt/dom/client/NativeEvent;I)(e,i)
+ });
+ $wnd[t] = $entry(function(e,i) {
+ @com.google.gerrit.client.change.FileTable::onRestore(Lcom/google/gwt/dom/client/NativeEvent;I)(e,i)
+ });
$wnd[r] = $entry(function(e,i) {
@com.google.gerrit.client.change.FileTable::onReviewed(Lcom/google/gwt/dom/client/NativeEvent;I)(e,i)
});
@@ -102,6 +132,27 @@
});
}-*/;
+ private static void onEdit(NativeEvent e, int idx) {
+ MyTable t = getMyTable(e);
+ if (t != null) {
+ t.onEdit(idx);
+ }
+ }
+
+ private static void onDelete(NativeEvent e, int idx) {
+ MyTable t = getMyTable(e);
+ if (t != null) {
+ t.onDelete(idx);
+ }
+ }
+
+ private static void onRestore(NativeEvent e, int idx) {
+ MyTable t = getMyTable(e);
+ if (t != null) {
+ t.onRestore(idx);
+ }
+ }
+
private static void onReviewed(NativeEvent e, int idx) {
MyTable t = getMyTable(e);
if (t != null) {
@@ -139,6 +190,9 @@
private boolean register;
private JsArrayString reviewed;
private String scrollToPath;
+ private ChangeScreen2.Style style;
+ private Widget editButton;
+ private Widget replyButton;
@Override
protected void onLoad() {
@@ -146,20 +200,25 @@
R.css().ensureInjected();
}
- void setRevisions(PatchSet.Id base, PatchSet.Id curr) {
+ public void set(Id base, Id curr, ChangeScreen2.Style style,
+ Widget editButton, Widget replyButton) {
this.base = base;
this.curr = curr;
+ this.style = style;
+ this.editButton = editButton;
+ this.replyButton = replyButton;
}
void setValue(NativeMap<FileInfo> fileMap,
Timestamp myLastReply,
NativeMap<JsArray<CommentInfo>> comments,
- NativeMap<JsArray<CommentInfo>> drafts) {
+ NativeMap<JsArray<CommentInfo>> drafts,
+ Mode mode) {
JsArray<FileInfo> list = fileMap.values();
FileInfo.sortFileInfoByPath(list);
DisplayCommand cmd = new DisplayCommand(fileMap, list,
- myLastReply, comments, drafts);
+ myLastReply, comments, drafts, mode);
if (cmd.execute()) {
cmd.showProgressBar();
Scheduler.get().scheduleIncremental(cmd);
@@ -262,6 +321,52 @@
+ curr.toString());
}
+ void onEdit(int idx) {
+ final String path = list.get(idx).path();
+ ChangeFileApi.getContent(curr, path,
+ new GerritCallback<String>() {
+ @Override
+ public void onSuccess(String result) {
+ EditFileAction edit = new EditFileAction(
+ curr, result, path,
+ style, editButton, replyButton);
+ edit.onEdit();
+ }
+ });
+ }
+
+ void onDelete(int idx) {
+ String path = list.get(idx).path();
+ ChangeFileApi.deleteContent(curr, path,
+ new AsyncCallback<VoidResult>() {
+ @Override
+ public void onSuccess(VoidResult result) {
+ Gerrit.display(PageLinks.toChangeInEditMode(
+ curr.getParentKey()));
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ }
+ });
+ }
+
+ void onRestore(int idx) {
+ String path = list.get(idx).path();
+ ChangeFileApi.restoreContent(curr, path,
+ new AsyncCallback<VoidResult>() {
+ @Override
+ public void onSuccess(VoidResult result) {
+ Gerrit.display(PageLinks.toChangeInEditMode(
+ curr.getParentKey()));
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ }
+ });
+ }
+
void onReviewed(InputElement checkbox, int idx) {
setReviewed(list.get(idx), checkbox.isChecked());
}
@@ -358,6 +463,7 @@
private final NativeMap<JsArray<CommentInfo>> comments;
private final NativeMap<JsArray<CommentInfo>> drafts;
private final boolean hasUser;
+ private final Mode mode;
private boolean attached;
private int row;
private double start;
@@ -371,13 +477,15 @@
JsArray<FileInfo> list,
Timestamp myLastReply,
NativeMap<JsArray<CommentInfo>> comments,
- NativeMap<JsArray<CommentInfo>> drafts) {
+ NativeMap<JsArray<CommentInfo>> drafts,
+ Mode mode) {
this.table = new MyTable(map, list);
this.list = list;
this.myLastReply = myLastReply;
this.comments = comments;
this.drafts = drafts;
this.hasUser = Gerrit.isSignedIn();
+ this.mode = mode;
table.addStyleName(R.css().table());
}
@@ -449,7 +557,12 @@
private void header(SafeHtmlBuilder sb) {
sb.openTr().setStyleName(R.css().nohover());
sb.openTh().setStyleName(R.css().pointer()).closeTh();
- sb.openTh().setStyleName(R.css().reviewed()).closeTh();
+ if (mode == Mode.REVIEW) {
+ sb.openTh().setStyleName(R.css().reviewed()).closeTh();
+ } else {
+ sb.openTh().setStyleName(R.css().editButton()).closeTh();
+ sb.openTh().setStyleName(R.css().removeButton()).closeTh();
+ }
sb.openTh().setStyleName(R.css().status()).closeTh();
sb.openTh().append(Util.C.patchTableColumnName()).closeTh();
sb.openTh()
@@ -466,9 +579,18 @@
private void render(SafeHtmlBuilder sb, FileInfo info) {
sb.openTr();
sb.openTd().setStyleName(R.css().pointer()).closeTd();
- columnReviewed(sb, info);
+ if (mode == Mode.REVIEW) {
+ columnReviewed(sb, info);
+ } else {
+ columnEdit(sb, info);
+ columnDeleteRestore(sb, info);
+ }
columnStatus(sb, info);
- columnPath(sb, info);
+ if (mode == Mode.REVIEW) {
+ columnPath(sb, info);
+ } else {
+ columnPathEdit(sb, info);
+ }
columnComments(sb, info);
columnDelta1(sb, info);
columnDelta2(sb, info);
@@ -487,6 +609,67 @@
sb.closeTd();
}
+ private void columnEdit(SafeHtmlBuilder sb, FileInfo info) {
+ sb.openTd().setStyleName(R.css().editButton());
+ if (hasUser && isEditable(info)) {
+ if (!Patch.COMMIT_MSG.equals(info.path())) {
+ sb.openElement("button")
+ .setAttribute("title", Resources.C.editFileInline())
+ .setAttribute("onclick", EDIT + "(event," + info._row() + ")")
+ .append(new ImageResourceRenderer().render(Gerrit.RESOURCES.edit()))
+ .closeElement("button");
+ }
+ }
+ sb.closeTd();
+ }
+
+ private void columnPathEdit(SafeHtmlBuilder sb, FileInfo info) {
+ sb.openTd().setStyleName(R.css().pathColumn());
+ String path = info.path();
+ if (!Patch.COMMIT_MSG.equals(path)) {
+ sb.openAnchor()
+ .setAttribute("onclick", (isEditable(info) ? EDIT : RESTORE)
+ + "(event," + info._row() + ")");
+ int commonPrefixLen = commonPrefix(path);
+ if (commonPrefixLen > 0) {
+ sb.openSpan().setStyleName(R.css().commonPrefix())
+ .append(path.substring(0, commonPrefixLen))
+ .closeSpan();
+ }
+ sb.append(path.substring(commonPrefixLen));
+ sb.closeAnchor();
+ } else {
+ sb.append(Util.C.commitMessage());
+ }
+ sb.closeTd();
+ }
+
+ private void columnDeleteRestore(SafeHtmlBuilder sb, FileInfo info) {
+ sb.openTd().setStyleName(R.css().removeButton());
+ if (hasUser) {
+ if (!Patch.COMMIT_MSG.equals(info.path())) {
+ boolean editable = isEditable(info);
+ sb.openElement("button")
+ .setAttribute("title", editable
+ ? Resources.C.removeFileInline()
+ : Resources.C.restoreFileInline())
+ .setAttribute("onclick", (editable ? DELETE : RESTORE)
+ + "(event," + info._row() + ")")
+ .append(new ImageResourceRenderer().render(editable
+ ? Gerrit.RESOURCES.redNot()
+ : Gerrit.RESOURCES.editUndo()))
+ .closeElement("button");
+ }
+ }
+ sb.closeTd();
+ }
+
+ private boolean isEditable(FileInfo info) {
+ String status = info.status();
+ return status == null
+ || !ChangeType.DELETED.matches(status);
+ }
+
private void columnStatus(SafeHtmlBuilder sb, FileInfo info) {
sb.openTd().setStyleName(R.css().status());
if (!Patch.COMMIT_MSG.equals(info.path())
@@ -629,7 +812,12 @@
private void footer(SafeHtmlBuilder sb) {
sb.openTr().setStyleName(R.css().nohover());
sb.openTh().setStyleName(R.css().pointer()).closeTh();
- sb.openTh().setStyleName(R.css().reviewed()).closeTh();
+ if (mode == Mode.REVIEW) {
+ sb.openTh().setStyleName(R.css().reviewed()).closeTh();
+ } else {
+ sb.openTh().setStyleName(R.css().editButton()).closeTh();
+ sb.openTh().setStyleName(R.css().editButton()).closeTh();
+ }
sb.openTh().setStyleName(R.css().status()).closeTh();
sb.openTd().closeTd(); // path
sb.openTd().setAttribute("colspan", 3).closeTd(); // comments
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTextBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTextBox.java
new file mode 100644
index 0000000..52d2a25
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTextBox.java
@@ -0,0 +1,70 @@
+// Copyright (C) 2014 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 com.google.gerrit.client.change;
+
+import com.google.gerrit.client.changes.ChangeFileApi;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.rpc.RestApi;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gwt.event.dom.client.BlurEvent;
+import com.google.gwt.event.dom.client.BlurHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwtexpui.globalkey.client.NpTextArea;
+import com.google.gwtexpui.globalkey.client.NpTextBox;
+
+class FileTextBox extends NpTextBox {
+ private HandlerRegistration blurHandler;
+ private NpTextArea textArea;
+ private PatchSet.Id id;
+
+ @Override
+ protected void onLoad() {
+ blurHandler = addBlurHandler(new BlurHandler() {
+ @Override
+ public void onBlur(BlurEvent event) {
+ loadFileContent();
+ }
+ });
+ }
+
+ @Override
+ protected void onUnload() {
+ super.onUnload();
+ blurHandler.removeHandler();
+ }
+
+ void set(PatchSet.Id id, NpTextArea content) {
+ this.id = id;
+ this.textArea = content;
+ }
+
+ private void loadFileContent() {
+ ChangeFileApi.getContent(id, getText(), new GerritCallback<String>() {
+ @Override
+ public void onSuccess(String result) {
+ textArea.setText(result);
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ if (RestApi.isNotFound(caught)) {
+ // that means that the file doesn't exist in the repository
+ } else {
+ super.onFailure(caught);
+ }
+ }
+ });
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.java
index 2fa721e..92dfff4 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.java
@@ -19,8 +19,11 @@
import com.google.gerrit.client.changes.ChangeApi;
import com.google.gerrit.client.changes.ChangeInfo;
import com.google.gerrit.client.changes.ChangeInfo.CommitInfo;
+import com.google.gerrit.client.changes.ChangeInfo.EditInfo;
import com.google.gerrit.client.changes.ChangeInfo.RevisionInfo;
import com.google.gerrit.client.changes.ChangeList;
+import com.google.gerrit.client.rpc.CallbackGroup;
+import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.client.rpc.Natives;
import com.google.gerrit.client.rpc.RestApi;
@@ -57,6 +60,7 @@
private static final String OPEN;
private static final HyperlinkImpl link = GWT.create(HyperlinkImpl.class);
+ private EditInfo edit;
static {
OPEN = DOM.createUniqueId().replace('-', '_');
@@ -116,14 +120,31 @@
@Override
protected void onLoad() {
if (!loaded) {
+ CallbackGroup group = new CallbackGroup();
+ if (Gerrit.isSignedIn()) {
+ // TODO(davido): It shouldn't be necessary to make this call.
+ // PatchSetsBox is constructed via PatchSetsAction which is
+ // only initialized by CS2 after loading the EditInfo in that path.
+ ChangeApi.edit(changeId.get(), group.add(
+ new GerritCallback<EditInfo>() {
+ @Override
+ public void onSuccess(EditInfo result) {
+ edit = result;
+ }
+ }));
+ }
RestApi call = ChangeApi.detail(changeId.get());
ChangeList.addOptions(call, EnumSet.of(
ListChangesOption.ALL_COMMITS,
ListChangesOption.ALL_REVISIONS,
ListChangesOption.DRAFT_COMMENTS));
- call.get(new AsyncCallback<ChangeInfo>() {
+ call.get(group.addFinal(new AsyncCallback<ChangeInfo>() {
@Override
public void onSuccess(ChangeInfo result) {
+ if (edit != null) {
+ edit.set_name(edit.commit().commit());
+ result.revisions().put(edit.name(), RevisionInfo.fromEdit(edit));
+ }
render(result.revisions());
loaded = true;
}
@@ -131,7 +152,7 @@
@Override
public void onFailure(Throwable caught) {
}
- });
+ }));
}
}
@@ -189,7 +210,7 @@
.closeSpan()
.append(' ');
}
- sb.append(r._number());
+ sb.append(r.id());
sb.closeTd();
sb.openTd()
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/file_table.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/file_table.css
index 4b695d2..2802f39 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/file_table.css
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/file_table.css
@@ -40,6 +40,7 @@
}
.pathColumn a {
color: #000;
+ cursor: pointer;
}
.commonPrefix {
color: #888;
@@ -85,3 +86,21 @@
background-color: #d44;
}
+.editButton button {
+ cursor: pointer;
+ padding: 0;
+ margin: 0 0 0 5px;
+ border: 0;
+ background-color: transparent;
+ white-space: nowrap;
+}
+
+.removeButton button {
+ cursor: pointer;
+ padding: 0;
+ margin: 0 0 0 5px;
+ border: 0;
+ background-color: transparent;
+ white-space: nowrap;
+}
+
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
index 406ffd9..88ba6b4 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
@@ -14,6 +14,7 @@
package com.google.gerrit.client.changes;
+import com.google.gerrit.client.changes.ChangeInfo.EditInfo;
import com.google.gerrit.client.changes.ChangeInfo.IncludedInInfo;
import com.google.gerrit.client.rpc.NativeString;
import com.google.gerrit.client.rpc.RestApi;
@@ -68,6 +69,22 @@
return call(id, "detail");
}
+ public static void edit(int id, AsyncCallback<EditInfo> cb) {
+ edit(id).get(cb);
+ }
+
+ public static void editWithFiles(int id, AsyncCallback<EditInfo> cb) {
+ edit(id).addParameter("list", true).get(cb);
+ }
+
+ public static RestApi edit(int id) {
+ return change(id).view("edit");
+ }
+
+ public static RestApi editWithCommands(int id) {
+ return edit(id).addParameter("download-commands", true);
+ }
+
public static void includedIn(int id, AsyncCallback<IncludedInInfo> cb) {
call(id, "in").get(cb);
}
@@ -142,6 +159,23 @@
revision(id, commit).delete(cb);
}
+ /** Delete change edit. */
+ public static void deleteEdit(int id, AsyncCallback<JavaScriptObject> cb) {
+ edit(id).delete(cb);
+ }
+
+ /** Publish change edit. */
+ public static void publishEdit(int id, AsyncCallback<JavaScriptObject> cb) {
+ JavaScriptObject in = JavaScriptObject.createObject();
+ change(id).view("publish_edit").post(in, cb);
+ }
+
+ /** Rebase change edit on latest patch set. */
+ public static void rebaseEdit(int id, AsyncCallback<JavaScriptObject> cb) {
+ JavaScriptObject in = JavaScriptObject.createObject();
+ change(id).view("rebase_edit").post(in, cb);
+ }
+
/** Rebase a revision onto the branch tip. */
public static void rebase(int id, String commit, AsyncCallback<ChangeInfo> cb) {
JavaScriptObject in = JavaScriptObject.createObject();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java
index 03c58d2..2bbb429 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java
@@ -25,6 +25,7 @@
String readyToSubmit();
String mergeConflict();
String notCurrent();
+ String changeEdit();
String myDashboardTitle();
String unknownDashboardTitle();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties
index c904cac..df1e2c5 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties
@@ -6,6 +6,7 @@
readyToSubmit = Ready to Submit
mergeConflict = Merge Conflict
notCurrent = Not Current
+changeEdit = Change Edit
starredHeading = Starred Changes
watchedHeading = Open Changes of Watched Projects
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeFileApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeFileApi.java
new file mode 100644
index 0000000..fc15017
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeFileApi.java
@@ -0,0 +1,102 @@
+// Copyright (C) 2014 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 com.google.gerrit.client.changes;
+
+import com.google.gerrit.client.VoidResult;
+import com.google.gerrit.client.rpc.NativeString;
+import com.google.gerrit.client.rpc.RestApi;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+/**
+ * A collection of static methods which work on the Gerrit REST API for specific
+ * files in a change.
+ */
+public class ChangeFileApi {
+ static abstract class CallbackWrapper<I, O> implements AsyncCallback<I> {
+ protected AsyncCallback<O> wrapped;
+
+ public CallbackWrapper(AsyncCallback<O> callback) {
+ wrapped = callback;
+ }
+
+ @Override
+ public abstract void onSuccess(I result);
+
+ @Override
+ public void onFailure(Throwable caught) {
+ wrapped.onFailure(caught);
+ }
+ }
+
+ /** Get the contents of a File in a PatchSet or cange edit. */
+ public static void getContent(PatchSet.Id id, String filename,
+ AsyncCallback<String> cb) {
+ contentEditOrPs(id, filename).get(
+ new CallbackWrapper<NativeString, String>(cb) {
+ @Override
+ public void onSuccess(NativeString b64) {
+ wrapped.onSuccess(b64decode(b64.asString()));
+ }
+ });
+ }
+
+ /** Put contents into a File in a change edit. */
+ public static void putContent(PatchSet.Id id, String filename,
+ String content, AsyncCallback<VoidResult> result) {
+ contentEdit(id.getParentKey(), filename).put(content, result);
+ }
+
+ /** Restore contents of a File in a change edit. */
+ public static void restoreContent(PatchSet.Id id, String filename,
+ AsyncCallback<VoidResult> result) {
+ Input in = Input.create();
+ in.path(filename);
+ in.restore(true);
+ ChangeApi.edit(id.getParentKey().get()).post(in, result);
+ }
+
+ /** Delete a file from a change edit. */
+ public static void deleteContent(PatchSet.Id id, String filename,
+ AsyncCallback<VoidResult> result) {
+ contentEdit(id.getParentKey(), filename).delete(result);
+ }
+
+ private static RestApi contentEditOrPs(PatchSet.Id id, String filename) {
+ return id.get() == 0
+ ? contentEdit(id.getParentKey(), filename)
+ : ChangeApi.revision(id).view("files").id(filename).view("content");
+ }
+
+ private static RestApi contentEdit(Change.Id id, String filename) {
+ return ChangeApi.edit(id.get()).id(filename);
+ }
+
+ private static native String b64decode(String a) /*-{ return window.atob(a); }-*/;
+
+ private static class Input extends JavaScriptObject {
+ final native void path(String p) /*-{ if(p)this.path=p; }-*/;
+ final native void restore(boolean r) /*-{ if(r)this.restore=r; }-*/;
+
+ static Input create() {
+ return (Input) createObject();
+ }
+
+ protected Input() {
+ }
+ }
+}
\ No newline at end of file
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfo.java
index c79c45e..b90a743 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfo.java
@@ -24,6 +24,7 @@
import com.google.gerrit.common.data.LabelValue;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
@@ -99,9 +100,13 @@
public final native NativeMap<LabelInfo> all_labels() /*-{ return this.labels; }-*/;
public final native LabelInfo label(String n) /*-{ return this.labels[n]; }-*/;
public final native String current_revision() /*-{ return this.current_revision; }-*/;
+ public final native void set_current_revision(String r) /*-{ this.current_revision = r; }-*/;
public final native NativeMap<RevisionInfo> revisions() /*-{ return this.revisions; }-*/;
public final native RevisionInfo revision(String n) /*-{ return this.revisions[n]; }-*/;
public final native JsArray<MessageInfo> messages() /*-{ return this.messages; }-*/;
+ public final native void set_edit(EditInfo edit) /*-{ this.edit = edit; }-*/;
+ public final native EditInfo edit() /*-{ return this.edit; }-*/;
+ public final native boolean has_edit() /*-{ return this.hasOwnProperty('edit') }-*/;
public final native boolean has_permitted_labels()
/*-{ return this.hasOwnProperty('permitted_labels') }-*/;
@@ -206,8 +211,15 @@
public static class EditInfo extends JavaScriptObject {
public final native String name() /*-{ return this.name; }-*/;
+ public final native String set_name(String n) /*-{ this.name = n; }-*/;
public final native CommitInfo commit() /*-{ return this.commit; }-*/;
+ public final native boolean has_actions() /*-{ return this.hasOwnProperty('actions') }-*/;
+ public final native NativeMap<ActionInfo> actions() /*-{ return this.actions; }-*/;
+
+ public final native boolean has_fetch() /*-{ return this.hasOwnProperty('fetch') }-*/;
+ public final native NativeMap<FetchInfo> fetch() /*-{ return this.fetch; }-*/;
+
public final native boolean has_files() /*-{ return this.hasOwnProperty('files') }-*/;
public final native NativeMap<FileInfo> files() /*-{ return this.files; }-*/;
@@ -216,10 +228,21 @@
}
public static class RevisionInfo extends JavaScriptObject {
+ public static RevisionInfo fromEdit(EditInfo edit) {
+ RevisionInfo revisionInfo = createObject().cast();
+ revisionInfo.takeFromEdit(edit);
+ return revisionInfo;
+ }
+ private final native void takeFromEdit(EditInfo edit) /*-{
+ this._number = 0;
+ this.name = edit.name;
+ this.commit = edit.commit;
+ }-*/;
public final native int _number() /*-{ return this._number; }-*/;
public final native String name() /*-{ return this.name; }-*/;
public final native boolean draft() /*-{ return this.draft || false; }-*/;
public final native boolean has_draft_comments() /*-{ return this.has_draft_comments || false; }-*/;
+ public final native boolean is_edit() /*-{ return this._number == 0; }-*/;
public final native CommitInfo commit() /*-{ return this.commit; }-*/;
public final native void set_commit(CommitInfo c) /*-{ this.commit = c; }-*/;
@@ -234,14 +257,45 @@
public final native JsArray<WebLinkInfo> web_links() /*-{ return this.web_links; }-*/;
public static void sortRevisionInfoByNumber(JsArray<RevisionInfo> list) {
+ final int editParent = findEditParent(list);
Collections.sort(Natives.asList(list), new Comparator<RevisionInfo>() {
@Override
public int compare(RevisionInfo a, RevisionInfo b) {
- return a._number() - b._number();
+ return num(a) - num(b);
+ }
+
+ private int num(RevisionInfo r) {
+ return !r.is_edit() ? 2 * (r._number() - 1) + 1 : 2 * editParent;
}
});
}
+ private static int findEditParent(JsArray<RevisionInfo> list) {
+ for (int i = 0; i < list.length(); i++) {
+ // edit under revisions?
+ RevisionInfo editInfo = list.get(i);
+ if (editInfo.is_edit()) {
+ // parent commit is parent patch set revision
+ CommitInfo commit = editInfo.commit().parents().get(0);
+ String parentRevision = commit.commit();
+ // find parent
+ for (int j = 0; j < list.length(); j++) {
+ RevisionInfo parentInfo = list.get(j);
+ String name = parentInfo.name();
+ if (name.equals(parentRevision)) {
+ // found parent pacth set number
+ return parentInfo._number();
+ }
+ }
+ }
+ }
+ return -1;
+ }
+
+ public final String id() {
+ return PatchSet.Id.toId(_number());
+ }
+
protected RevisionInfo () {
}
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.java
index 2b3e2be..212af9a 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.java
@@ -101,8 +101,7 @@
SafeHtml.setInnerHTML(filePath, formatPath(path, null, null));
up.setTargetHistoryToken(PageLinks.toChange(
patchSetId.getParentKey(),
- base != null ? String.valueOf(base.get()) : null,
- String.valueOf(patchSetId.get())));
+ base != null ? base.getId() : null, patchSetId.getId()));
}
private static SafeHtml formatPath(String path, String project, String commit) {
@@ -191,7 +190,7 @@
GitwebLink gw = Gerrit.getGitwebLink();
if (gw != null) {
for (RevisionInfo rev : Natives.asList(info.revisions().values())) {
- if (rev._number() == patchSetId.get()) {
+ if (patchSetId.getId().equals(rev.id())) {
String c = rev.name();
SafeHtml.setInnerHTML(filePath, formatPath(path, info.project(), c));
SafeHtml.setInnerHTML(project, new SafeHtmlBuilder()
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox2.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox2.java
index 7154c9f..64c20d6 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox2.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox2.java
@@ -85,10 +85,10 @@
}
for (int i = 0; i < list.length(); i++) {
RevisionInfo r = list.get(i);
- InlineHyperlink link = createLink(
- String.valueOf(r._number()), new PatchSet.Id(changeId, r._number()));
+ InlineHyperlink link = createLink(r.id(),
+ new PatchSet.Id(changeId, r._number()));
linkPanel.add(link);
- if (revision != null && r._number() == revision.get()) {
+ if (revision != null && r.id().equals(revision.getId())) {
selectedLink = link;
}
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide2.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide2.java
index 22df01f..2d276d6 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide2.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide2.java
@@ -21,8 +21,10 @@
import com.google.gerrit.client.JumpKeys;
import com.google.gerrit.client.account.DiffPreferences;
import com.google.gerrit.client.change.ChangeScreen2;
+import com.google.gerrit.client.change.FileTable;
import com.google.gerrit.client.changes.ChangeApi;
import com.google.gerrit.client.changes.ChangeInfo;
+import com.google.gerrit.client.changes.ChangeInfo.EditInfo;
import com.google.gerrit.client.changes.ChangeInfo.RevisionInfo;
import com.google.gerrit.client.changes.ChangeList;
import com.google.gerrit.client.diff.DiffInfo.FileMeta;
@@ -119,6 +121,7 @@
private ScrollSynchronizer scrollSynchronizer;
private DiffInfo diff;
private FileSize fileSize;
+ private EditInfo edit;
private ChunkManager chunkManager;
private CommentManager commentManager;
private SkipManager skipManager;
@@ -190,6 +193,16 @@
}
}));
+ if (Gerrit.isSignedIn()) {
+ ChangeApi.edit(changeId.get(), group.add(
+ new GerritCallback<EditInfo>() {
+ @Override
+ public void onSuccess(EditInfo result) {
+ edit = result;
+ }
+ }));
+ }
+
final CommentsCollections comments = new CommentsCollections();
comments.load(base, revision, path, group);
@@ -200,6 +213,11 @@
@Override
public void onSuccess(ChangeInfo info) {
info.revisions().copyKeysIntoChildren("name");
+ if (edit != null) {
+ edit.set_name(edit.commit().commit());
+ info.set_edit(edit);
+ info.revisions().put(edit.name(), RevisionInfo.fromEdit(edit));
+ }
JsArray<RevisionInfo> list = info.revisions().values();
RevisionInfo.sortRevisionInfoByNumber(list);
diffTable.set(prefs, list, diff);
@@ -798,11 +816,12 @@
group.addListener(new GerritCallback<Void>() {
@Override
public void onSuccess(Void result) {
- String b = base != null ? String.valueOf(base.get()) : null;
- String rev = String.valueOf(revision.get());
+ String b = base != null ? base.getId() : null;
+ String rev = revision.getId();
Gerrit.display(
PageLinks.toChange(changeId, b, rev),
- new ChangeScreen2(changeId, b, rev, openReplyBox));
+ new ChangeScreen2(changeId, b, rev, openReplyBox,
+ FileTable.Mode.REVIEW));
}
});
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UpToChangeCommand2.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UpToChangeCommand2.java
index 7071e7f..7a3ec45 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UpToChangeCommand2.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UpToChangeCommand2.java
@@ -33,6 +33,6 @@
public void onKeyPress(final KeyPressEvent event) {
Gerrit.display(PageLinks.toChange(
revision.getParentKey(),
- String.valueOf(revision.get())));
+ revision.getId()));
}
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editUndo.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editUndo.png
new file mode 100644
index 0000000..4790e10
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editUndo.png
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectLinkMenuItem.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectLinkMenuItem.java
index 2917505..119f5ef 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectLinkMenuItem.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectLinkMenuItem.java
@@ -19,7 +19,7 @@
import com.google.gerrit.reviewdb.client.Project;
public class ProjectLinkMenuItem extends LinkMenuItem {
- private final String panel;
+ protected final String panel;
public ProjectLinkMenuItem(String text, String panel) {
super(text, "");
@@ -38,10 +38,14 @@
if (projectKey != null) {
setVisible(true);
- setTargetHistoryToken(Dispatcher.toProjectAdmin(projectKey, panel));
+ onScreenLoad(projectKey);
} else {
setVisible(false);
}
super.onScreenLoad(event);
}
+
+ protected void onScreenLoad(Project.NameKey project) {
+ setTargetHistoryToken(Dispatcher.toProjectAdmin(project, panel));
+ }
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginModule.java
index 365a222..2016942 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginModule.java
@@ -14,6 +14,9 @@
package com.google.gerrit.httpd.plugins;
+import com.google.gerrit.httpd.resources.Resource;
+import com.google.gerrit.httpd.resources.ResourceKey;
+import com.google.gerrit.httpd.resources.ResourceWeigher;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.plugins.HttpModuleGenerator;
import com.google.gerrit.server.plugins.ReloadPluginListener;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
index 2b7c684..90ce894 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
@@ -26,6 +26,9 @@
import com.google.common.collect.Maps;
import com.google.common.net.HttpHeaders;
import com.google.gerrit.extensions.registration.RegistrationHandle;
+import com.google.gerrit.httpd.resources.Resource;
+import com.google.gerrit.httpd.resources.ResourceKey;
+import com.google.gerrit.httpd.resources.SmallResource;
import com.google.gerrit.httpd.restapi.RestApiServlet;
import com.google.gerrit.server.MimeUtilFileTypeRegistry;
import com.google.gerrit.server.config.CanonicalWebUrl;
@@ -259,7 +262,7 @@
}
String file = pathInfo.substring(1);
- ResourceKey key = new ResourceKey(holder.plugin, file);
+ PluginResourceKey key = new PluginResourceKey(holder.plugin, file);
Resource rsc = resourceCache.getIfPresent(key);
if (rsc != null && req.getHeader(HttpHeaders.IF_MODIFIED_SINCE) == null) {
rsc.send(req, res);
@@ -364,7 +367,7 @@
private void sendAutoIndex(PluginContentScanner scanner,
String prefix, String pluginName,
- ResourceKey cacheKey, HttpServletResponse res,long lastModifiedTime)
+ PluginResourceKey cacheKey, HttpServletResponse res,long lastModifiedTime)
throws IOException {
List<PluginEntry> cmds = Lists.newArrayList();
List<PluginEntry> servlets = Lists.newArrayList();
@@ -437,7 +440,7 @@
}
private void sendMarkdownAsHtml(String md, String pluginName,
- ResourceKey cacheKey, HttpServletResponse res, long lastModifiedTime)
+ PluginResourceKey cacheKey, HttpServletResponse res, long lastModifiedTime)
throws UnsupportedEncodingException, IOException {
Map<String, String> macros = Maps.newHashMap();
macros.put("PLUGIN", pluginName);
@@ -540,7 +543,7 @@
}
private void sendMarkdownAsHtml(PluginContentScanner scanner, PluginEntry entry,
- String pluginName, ResourceKey key, HttpServletResponse res)
+ String pluginName, PluginResourceKey key, HttpServletResponse res)
throws IOException {
byte[] rawmd = readWholeEntry(scanner, entry);
String encoding = null;
@@ -560,7 +563,7 @@
}
private void sendResource(PluginContentScanner scanner, PluginEntry entry,
- ResourceKey key, HttpServletResponse res)
+ PluginResourceKey key, HttpServletResponse res)
throws IOException {
byte[] data = null;
Optional<Long> size = entry.getSize();
@@ -605,10 +608,11 @@
}
}
- private void sendJsPlugin(Plugin plugin, ResourceKey key,
+ private void sendJsPlugin(Plugin plugin, PluginResourceKey key,
HttpServletRequest req, HttpServletResponse res) throws IOException {
File pluginFile = plugin.getSrcFile();
- if (req.getRequestURI().equals(getJsPluginPath(plugin)) && pluginFile.exists()) {
+ if (req.getRequestURI().endsWith(getJsPluginPath(plugin))
+ && pluginFile.exists()) {
res.setHeader("Content-Length", Long.toString(pluginFile.length()));
res.setContentType("application/javascript");
writeToResponse(res, new FileInputStream(pluginFile));
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ResourceKey.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/PluginResourceKey.java
similarity index 80%
rename from gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ResourceKey.java
rename to gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/PluginResourceKey.java
index 068d6b4..4c3c515 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ResourceKey.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/PluginResourceKey.java
@@ -14,18 +14,19 @@
package com.google.gerrit.httpd.plugins;
+import com.google.gerrit.httpd.resources.ResourceKey;
import com.google.gerrit.server.plugins.Plugin;
-final class ResourceKey {
+final class PluginResourceKey implements ResourceKey {
private final Plugin.CacheKey plugin;
private final String resource;
- ResourceKey(Plugin p, String r) {
+ PluginResourceKey(Plugin p, String r) {
this.plugin = p.getCacheKey();
this.resource = r;
}
- int weigh() {
+ public int weigh() {
return resource.length() * 2;
}
@@ -36,8 +37,8 @@
@Override
public boolean equals(Object other) {
- if (other instanceof ResourceKey) {
- ResourceKey rk = (ResourceKey) other;
+ if (other instanceof PluginResourceKey) {
+ PluginResourceKey rk = (PluginResourceKey) other;
return plugin == rk.plugin && resource.equals(rk.resource);
}
return false;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/Resource.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/resources/Resource.java
similarity index 60%
rename from gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/Resource.java
rename to gerrit-httpd/src/main/java/com/google/gerrit/httpd/resources/Resource.java
index b361fdc..b6d9a75 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/Resource.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/resources/Resource.java
@@ -12,39 +12,48 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.httpd.plugins;
+package com.google.gerrit.httpd.resources;
import com.google.gwtexpui.server.CacheHeaders;
import java.io.IOException;
+import java.io.Serializable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-abstract class Resource {
- static final Resource NOT_FOUND = new Resource() {
+public abstract class Resource implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ public static final Resource NOT_FOUND = new Resource() {
+ private static final long serialVersionUID = 1L;
+
@Override
- int weigh() {
+ public int weigh() {
return 0;
}
@Override
- void send(HttpServletRequest req, HttpServletResponse res)
+ public void send(HttpServletRequest req, HttpServletResponse res)
throws IOException {
CacheHeaders.setNotCacheable(res);
res.sendError(HttpServletResponse.SC_NOT_FOUND);
}
@Override
- boolean isUnchanged(long latestModifiedDate) {
+ public boolean isUnchanged(long latestModifiedDate) {
return false;
}
+
+ protected Object readResolve() {
+ return NOT_FOUND;
+ }
};
- abstract boolean isUnchanged(long latestModifiedDate);
+ public abstract boolean isUnchanged(long latestModifiedDate);
- abstract int weigh();
+ public abstract int weigh();
- abstract void send(HttpServletRequest req, HttpServletResponse res)
+ public abstract void send(HttpServletRequest req, HttpServletResponse res)
throws IOException;
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ResourceWeigher.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/resources/ResourceKey.java
similarity index 63%
copy from gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ResourceWeigher.java
copy to gerrit-httpd/src/main/java/com/google/gerrit/httpd/resources/ResourceKey.java
index 2514272..ef5a1df 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ResourceWeigher.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/resources/ResourceKey.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 The Android Open Source Project
+// Copyright (C) 2014 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.
@@ -12,13 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.httpd.plugins;
+package com.google.gerrit.httpd.resources;
-import com.google.common.cache.Weigher;
-
-class ResourceWeigher implements Weigher<ResourceKey, Resource> {
- @Override
- public int weigh(ResourceKey key, Resource value) {
- return key.weigh() + value.weigh();
- }
+public interface ResourceKey {
+ public int weigh();
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ResourceWeigher.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/resources/ResourceWeigher.java
similarity index 86%
rename from gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ResourceWeigher.java
rename to gerrit-httpd/src/main/java/com/google/gerrit/httpd/resources/ResourceWeigher.java
index 2514272..8e997c7 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ResourceWeigher.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/resources/ResourceWeigher.java
@@ -12,11 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.httpd.plugins;
+package com.google.gerrit.httpd.resources;
import com.google.common.cache.Weigher;
-class ResourceWeigher implements Weigher<ResourceKey, Resource> {
+public class ResourceWeigher implements Weigher<ResourceKey, Resource> {
@Override
public int weigh(ResourceKey key, Resource value) {
return key.weigh() + value.weigh();
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/SmallResource.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/resources/SmallResource.java
similarity index 78%
rename from gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/SmallResource.java
rename to gerrit-httpd/src/main/java/com/google/gerrit/httpd/resources/SmallResource.java
index 2a3da57..d057269 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/SmallResource.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/resources/SmallResource.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.httpd.plugins;
+package com.google.gerrit.httpd.resources;
import com.google.common.net.HttpHeaders;
import com.google.gerrit.common.Nullable;
@@ -22,38 +22,39 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-final class SmallResource extends Resource {
+public final class SmallResource extends Resource {
+ private static final long serialVersionUID = 1L;
private final byte[] data;
private String contentType;
private String characterEncoding;
private long lastModified;
- SmallResource(byte[] data) {
+ public SmallResource(byte[] data) {
this.data = data;
}
- SmallResource setLastModified(long when) {
+ public SmallResource setLastModified(long when) {
this.lastModified = when;
return this;
}
- SmallResource setContentType(String contentType) {
+ public SmallResource setContentType(String contentType) {
this.contentType = contentType;
return this;
}
- SmallResource setCharacterEncoding(@Nullable String enc) {
+ public SmallResource setCharacterEncoding(@Nullable String enc) {
this.characterEncoding = enc;
return this;
}
@Override
- int weigh() {
+ public int weigh() {
return contentType.length() * 2 + data.length;
}
@Override
- void send(HttpServletRequest req, HttpServletResponse res)
+ public void send(HttpServletRequest req, HttpServletResponse res)
throws IOException {
if (0 < lastModified) {
long ifModifiedSince = req.getDateHeader(HttpHeaders.IF_MODIFIED_SINCE);
@@ -73,7 +74,7 @@
}
@Override
- boolean isUnchanged(long lastModified) {
+ public boolean isUnchanged(long lastModified) {
return this.lastModified == lastModified;
}
}
diff --git a/gerrit-pgm/BUCK b/gerrit-pgm/BUCK
index cffc0c1..3de5a2a 100644
--- a/gerrit-pgm/BUCK
+++ b/gerrit-pgm/BUCK
@@ -79,6 +79,7 @@
],
visibility = [
'//gerrit-acceptance-tests/...',
+ '//gerrit-gwtdebug:gwtdebug',
'//gerrit-war:',
],
)
@@ -133,6 +134,7 @@
visibility = [
'//:',
'//gerrit-acceptance-tests/...',
+ '//gerrit-gwtdebug:gwtdebug',
'//tools/eclipse:classpath',
'//Documentation:licenses.txt',
],
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
index c73e9d7..5e68602 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
@@ -57,6 +57,7 @@
import com.google.gerrit.server.git.GarbageCollectionRunner;
import com.google.gerrit.server.git.ReceiveCommitsExecutorModule;
import com.google.gerrit.server.git.WorkQueue;
+import com.google.gerrit.server.index.DummyIndexModule;
import com.google.gerrit.server.index.IndexModule;
import com.google.gerrit.server.index.IndexModule.IndexType;
import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier;
@@ -361,6 +362,9 @@
}
private AbstractModule createIndexModule() {
+ if (slave) {
+ return new DummyIndexModule();
+ }
IndexType indexType = IndexModule.getIndexType(cfgInjector);
switch (indexType) {
case LUCENE:
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/RebuildNotedb.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/RebuildNotedb.java
index 6996e49..4593aef 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/RebuildNotedb.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/RebuildNotedb.java
@@ -89,6 +89,11 @@
dbManager.start();
sysInjector = createSysInjector();
+ NotesMigration notesMigration = sysInjector.getInstance(
+ NotesMigration.class);
+ if (!notesMigration.enabled()) {
+ die("Notedb is not enabled.");
+ }
LifecycleManager sysManager = new LifecycleManager();
sysManager.add(sysInjector);
sysManager.start();
@@ -202,7 +207,6 @@
install(dbInjector.getInstance(BatchProgramModule.class));
install(new BatchGitModule());
install(new NoteDbModule());
- bind(NotesMigration.class).toInstance(NotesMigration.allEnabled());
}
});
}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Reindex.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Reindex.java
index 6ade6a9..cf4fdbf 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Reindex.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Reindex.java
@@ -18,6 +18,7 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
+import com.google.gerrit.common.Die;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.lucene.LuceneIndexModule;
import com.google.gerrit.pgm.util.BatchGitModule;
@@ -91,6 +92,7 @@
mustHaveValidSite();
dbInjector = createDbInjector(MULTI_USER);
threads = ThreadLimiter.limitThreads(dbInjector, threads);
+ checkNotSlaveMode();
disableLuceneAutomaticCommit();
if (version == null) {
version = ChangeSchemas.getLatest().getVersion();
@@ -119,6 +121,14 @@
return result;
}
+ private void checkNotSlaveMode() throws Die {
+ Config cfg = dbInjector.getInstance(
+ Key.get(Config.class, GerritServerConfig.class));
+ if (cfg.getBoolean("container", "slave", false)) {
+ throw die("Cannot run reindex in slave mode");
+ }
+ }
+
private Injector createSysInjector() {
List<Module> modules = Lists.newArrayList();
Module changeIndexModule;
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java
index ec98465..8f9f1f4 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java
@@ -32,6 +32,8 @@
import com.google.gerrit.server.change.ChangeKindCacheImpl;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.CanonicalWebUrlProvider;
+import com.google.gerrit.server.config.DisableReverseDnsLookup;
+import com.google.gerrit.server.config.DisableReverseDnsLookupProvider;
import com.google.gerrit.server.config.FactoryModule;
import com.google.gerrit.server.git.ChangeCache;
import com.google.gerrit.server.git.TagCache;
@@ -81,6 +83,8 @@
.toProvider(CommentLinkProvider.class).in(SINGLETON);
bind(String.class).annotatedWith(CanonicalWebUrl.class)
.toProvider(CanonicalWebUrlProvider.class);
+ bind(Boolean.class).annotatedWith(DisableReverseDnsLookup.class)
+ .toProvider(DisableReverseDnsLookupProvider.class).in(SINGLETON);
bind(IdentifiedUser.class)
.toProvider(Providers.<IdentifiedUser> of(null));
bind(CurrentUser.class).to(IdentifiedUser.class);
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteLibraryBasedDataSourceProvider.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteLibraryBasedDataSourceProvider.java
index 0614b53..f5a7fb0 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteLibraryBasedDataSourceProvider.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteLibraryBasedDataSourceProvider.java
@@ -24,9 +24,6 @@
import org.eclipse.jgit.lib.Config;
import java.io.File;
-import java.io.FileFilter;
-import java.util.Arrays;
-import java.util.Comparator;
import javax.sql.DataSource;
@@ -47,34 +44,9 @@
public synchronized DataSource get() {
if (!init) {
- loadSiteLib();
+ SiteLibraryLoaderUtil.loadSiteLib(libdir);
init = true;
}
return super.get();
}
-
- private void loadSiteLib() {
- File[] jars = libdir.listFiles(new FileFilter() {
- @Override
- public boolean accept(File path) {
- String name = path.getName();
- return (name.endsWith(".jar") || name.endsWith(".zip"))
- && path.isFile();
- }
- });
- if (jars != null && 0 < jars.length) {
- Arrays.sort(jars, new Comparator<File>() {
- @Override
- public int compare(File a, File b) {
- // Sort by reverse last-modified time so newer JARs are first.
- int cmp = Long.compare(b.lastModified(), a.lastModified());
- if (cmp != 0) {
- return cmp;
- }
- return a.getName().compareTo(b.getName());
- }
- });
- IoUtil.loadJARs(jars);
- }
- }
}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteLibraryLoaderUtil.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteLibraryLoaderUtil.java
new file mode 100644
index 0000000..b18a769
--- /dev/null
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteLibraryLoaderUtil.java
@@ -0,0 +1,51 @@
+// Copyright (C) 2014 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 com.google.gerrit.pgm.util;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.util.Arrays;
+import java.util.Comparator;
+
+public final class SiteLibraryLoaderUtil {
+
+ public static void loadSiteLib(File libdir) {
+ File[] jars = libdir.listFiles(new FileFilter() {
+ @Override
+ public boolean accept(File path) {
+ String name = path.getName();
+ return (name.endsWith(".jar") || name.endsWith(".zip"))
+ && path.isFile();
+ }
+ });
+ if (jars != null && 0 < jars.length) {
+ Arrays.sort(jars, new Comparator<File>() {
+ @Override
+ public int compare(File a, File b) {
+ // Sort by reverse last-modified time so newer JARs are first.
+ int cmp = Long.compare(b.lastModified(), a.lastModified());
+ if (cmp != 0) {
+ return cmp;
+ }
+ return a.getName().compareTo(b.getName());
+ }
+ });
+ IoUtil.loadJARs(jars);
+ }
+ }
+
+ private SiteLibraryLoaderUtil() {
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java
index 3d156f9..d4fb311 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java
@@ -14,6 +14,8 @@
package com.google.gerrit.reviewdb.client;
+import static com.google.gerrit.reviewdb.client.RefNames.REFS_USER;
+
import com.google.gwtorm.client.Column;
import com.google.gwtorm.client.IntKey;
@@ -105,6 +107,16 @@
return r;
}
+ public static Id fromRef(String name) {
+ if (name == null) {
+ return null;
+ }
+ if (name.startsWith(REFS_USER)) {
+ return fromRefPart(name.substring(REFS_USER.length()));
+ }
+ return null;
+ }
+
/**
* Parse an Account.Id out of a part of a ref-name.
*
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchSet.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchSet.java
index 5d6f119..48623bd 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchSet.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchSet.java
@@ -112,6 +112,16 @@
}
return Integer.parseInt(ref.substring(ps));
}
+
+ public String getId() {
+ return toId(patchSetId);
+ }
+
+ public static String toId(int number) {
+ return number == 0
+ ? "edit"
+ : String.valueOf(number);
+ }
}
@Column(id = 1, name = Column.NONE)
diff --git a/gerrit-reviewdb/src/test/java/com/google/gerrit/reviewdb/client/AccountTest.java b/gerrit-reviewdb/src/test/java/com/google/gerrit/reviewdb/client/AccountTest.java
index f5c75f0..eaf1743 100644
--- a/gerrit-reviewdb/src/test/java/com/google/gerrit/reviewdb/client/AccountTest.java
+++ b/gerrit-reviewdb/src/test/java/com/google/gerrit/reviewdb/client/AccountTest.java
@@ -21,6 +21,26 @@
public class AccountTest {
@Test
+ public void parseRefName() {
+ assertRef(1, "refs/users/01/1");
+ assertRef(1, "refs/users/01/1-drafts");
+ assertRef(1, "refs/users/01/1-drafts/2");
+
+ assertNotRef(null);
+ assertNotRef("");
+
+ // Invalid characters.
+ assertNotRef("refs/users/01a/1");
+ assertNotRef("refs/users/01/a1");
+
+ // Mismatched shard.
+ assertNotRef("refs/users/01/23");
+
+ // Shard too short.
+ assertNotRef("refs/users/1/1");
+ }
+
+ @Test
public void parseRefNameParts() {
assertRefPart(1, "01/1");
assertRefPart(1, "01/1-drafts");
@@ -43,6 +63,14 @@
assertNotRefPart("1/1");
}
+ private static void assertRef(int accountId, String refName) {
+ assertEquals(new Account.Id(accountId), Account.Id.fromRef(refName));
+ }
+
+ private static void assertNotRef(String refName) {
+ assertNull(Account.Id.fromRef(refName));
+ }
+
private static void assertRefPart(int accountId, String refName) {
assertEquals(new Account.Id(accountId), Account.Id.fromRefPart(refName));
}
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 6d798cc..bb16710 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
@@ -33,6 +33,7 @@
import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.gerrit.server.config.DisableReverseDnsLookup;
import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
@@ -70,6 +71,7 @@
private final Provider<String> canonicalUrl;
private final AccountCache accountCache;
private final GroupBackend groupBackend;
+ private final Boolean disableReverseDnsLookup;
@Inject
public GenericFactory(
@@ -77,6 +79,7 @@
AuthConfig authConfig,
@AnonymousCowardName String anonymousCowardName,
@CanonicalWebUrl Provider<String> canonicalUrl,
+ @DisableReverseDnsLookup Boolean disableReverseDnsLookup,
AccountCache accountCache,
GroupBackend groupBackend) {
this.capabilityControlFactory = capabilityControlFactory;
@@ -85,6 +88,7 @@
this.canonicalUrl = canonicalUrl;
this.accountCache = accountCache;
this.groupBackend = groupBackend;
+ this.disableReverseDnsLookup = disableReverseDnsLookup;
}
public IdentifiedUser create(final Account.Id id) {
@@ -92,22 +96,22 @@
}
public IdentifiedUser create(Provider<ReviewDb> db, Account.Id id) {
- return new IdentifiedUser(capabilityControlFactory,
- authConfig, anonymousCowardName, canonicalUrl, accountCache,
- groupBackend, null, db, id, null);
+ return new IdentifiedUser(capabilityControlFactory, authConfig,
+ anonymousCowardName, canonicalUrl, accountCache, groupBackend,
+ disableReverseDnsLookup, null, db, id, null);
}
public IdentifiedUser create(SocketAddress remotePeer, Account.Id id) {
- return new IdentifiedUser(capabilityControlFactory,
- authConfig, anonymousCowardName, canonicalUrl, accountCache,
- groupBackend, Providers.of(remotePeer), null, id, null);
+ return new IdentifiedUser(capabilityControlFactory, authConfig,
+ anonymousCowardName, canonicalUrl, accountCache, groupBackend,
+ disableReverseDnsLookup, Providers.of(remotePeer), null, id, null);
}
public CurrentUser runAs(SocketAddress remotePeer, Account.Id id,
@Nullable CurrentUser caller) {
- return new IdentifiedUser(capabilityControlFactory,
- authConfig, anonymousCowardName, canonicalUrl, accountCache,
- groupBackend, Providers.of(remotePeer), null, id, caller);
+ return new IdentifiedUser(capabilityControlFactory, authConfig,
+ anonymousCowardName, canonicalUrl, accountCache, groupBackend,
+ disableReverseDnsLookup, Providers.of(remotePeer), null, id, caller);
}
}
@@ -125,6 +129,7 @@
private final Provider<String> canonicalUrl;
private final AccountCache accountCache;
private final GroupBackend groupBackend;
+ private final Boolean disableReverseDnsLookup;
private final Provider<SocketAddress> remotePeerProvider;
private final Provider<ReviewDb> dbProvider;
@@ -137,6 +142,7 @@
final @CanonicalWebUrl Provider<String> canonicalUrl,
final AccountCache accountCache,
final GroupBackend groupBackend,
+ final @DisableReverseDnsLookup Boolean disableReverseDnsLookup,
final @RemotePeer Provider<SocketAddress> remotePeerProvider,
final Provider<ReviewDb> dbProvider) {
@@ -146,21 +152,22 @@
this.canonicalUrl = canonicalUrl;
this.accountCache = accountCache;
this.groupBackend = groupBackend;
+ this.disableReverseDnsLookup = disableReverseDnsLookup;
this.remotePeerProvider = remotePeerProvider;
this.dbProvider = dbProvider;
}
public IdentifiedUser create(Account.Id id) {
- return new IdentifiedUser(capabilityControlFactory,
- authConfig, anonymousCowardName, canonicalUrl, accountCache,
- groupBackend, remotePeerProvider, dbProvider, id, null);
+ return new IdentifiedUser(capabilityControlFactory, authConfig,
+ anonymousCowardName, canonicalUrl, accountCache, groupBackend,
+ disableReverseDnsLookup, remotePeerProvider, dbProvider, id, null);
}
public IdentifiedUser runAs(Account.Id id, CurrentUser caller) {
- return new IdentifiedUser(capabilityControlFactory,
- authConfig, anonymousCowardName, canonicalUrl, accountCache,
- groupBackend, remotePeerProvider, dbProvider, id, caller);
+ return new IdentifiedUser(capabilityControlFactory, authConfig,
+ anonymousCowardName, canonicalUrl, accountCache, groupBackend,
+ disableReverseDnsLookup, remotePeerProvider, dbProvider, id, caller);
}
}
@@ -177,6 +184,7 @@
private final AuthConfig authConfig;
private final GroupBackend groupBackend;
private final String anonymousCowardName;
+ private final Boolean disableReverseDnsLookup;
@Nullable
private final Provider<SocketAddress> remotePeerProvider;
@@ -194,6 +202,7 @@
private Collection<AccountProjectWatch> notificationFilters;
private CurrentUser realUser;
+
private IdentifiedUser(
CapabilityControl.Factory capabilityControlFactory,
final AuthConfig authConfig,
@@ -201,6 +210,7 @@
final Provider<String> canonicalUrl,
final AccountCache accountCache,
final GroupBackend groupBackend,
+ final Boolean disableReverseDnsLookup,
@Nullable final Provider<SocketAddress> remotePeerProvider,
@Nullable final Provider<ReviewDb> dbProvider,
final Account.Id id,
@@ -211,6 +221,7 @@
this.groupBackend = groupBackend;
this.authConfig = authConfig;
this.anonymousCowardName = anonymousCowardName;
+ this.disableReverseDnsLookup = disableReverseDnsLookup;
this.remotePeerProvider = remotePeerProvider;
this.dbProvider = dbProvider;
this.accountId = id;
@@ -383,7 +394,7 @@
final InetSocketAddress sa = (InetSocketAddress) remotePeer;
final InetAddress in = sa.getAddress();
- host = in != null ? in.getCanonicalHostName() : sa.getHostName();
+ host = in != null ? getHost(in) : sa.getHostName();
}
}
if (host == null || host.isEmpty()) {
@@ -444,4 +455,11 @@
public boolean isIdentifiedUser() {
return true;
}
+
+ private String getHost(final InetAddress in) {
+ if (Boolean.FALSE.equals(disableReverseDnsLookup)) {
+ return in.getCanonicalHostName();
+ }
+ return in.getHostAddress();
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/WebLinks.java b/gerrit-server/src/main/java/com/google/gerrit/server/WebLinks.java
index fe07100..ab6e840 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/WebLinks.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/WebLinks.java
@@ -37,6 +37,7 @@
List<WebLinkInfo> links = Lists.newArrayList();
for (PatchSetWebLink webLink : patchSetLinks) {
links.add(new WebLinkInfo(webLink.getLinkName(),
+ webLink.getImageUrl(),
webLink.getPatchSetUrl(project, commit)));
}
return links;
@@ -46,6 +47,7 @@
List<WebLinkInfo> links = Lists.newArrayList();
for (ProjectWebLink webLink : projectLinks) {
links.add(new WebLinkInfo(webLink.getLinkName(),
+ webLink.getImageUrl(),
webLink.getProjectUrl(project)));
}
return links;
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 246f6e6..f10d5d5 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
@@ -239,6 +239,8 @@
}
}
+ // TODO(davido): Turn the boolean options to ChangeEditOption enum,
+ // like it's already the case for ListChangesOption/ListGroupsOption
static class Detail implements RestReadView<ChangeResource> {
private final ChangeEditUtil editUtil;
private final ChangeEditJson editJson;
@@ -251,6 +253,9 @@
@Option(name = "--list", metaVar = "LIST")
boolean list;
+ @Option(name = "--download-commands", metaVar = "download-commands")
+ boolean downloadCommands;
+
@Inject
Detail(ChangeEditUtil editUtil,
ChangeEditJson editJson,
@@ -271,7 +276,7 @@
return Response.none();
}
- EditInfo editInfo = editJson.toEditInfo(edit.get());
+ EditInfo editInfo = editJson.toEditInfo(edit.get(), downloadCommands);
if (list) {
PatchSet basePatchSet = null;
if (base != null) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
index 8b70eca..70559a6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
@@ -884,21 +884,29 @@
r.put(schemeName, fetchInfo);
if (has(DOWNLOAD_COMMANDS)) {
- for (DynamicMap.Entry<DownloadCommand> e2 : downloadCommands) {
- String commandName = e2.getExportName();
- DownloadCommand command = e2.getProvider().get();
- String c = command.getCommand(scheme, projectName, refName);
- if (c != null) {
- addCommand(fetchInfo, commandName, c);
- }
- }
+ populateFetchMap(scheme, downloadCommands, projectName, refName,
+ fetchInfo);
}
}
return r;
}
- private void addCommand(FetchInfo fetchInfo, String commandName, String c) {
+ public static void populateFetchMap(DownloadScheme scheme,
+ DynamicMap<DownloadCommand> commands, String projectName,
+ String refName, FetchInfo fetchInfo) {
+ for (DynamicMap.Entry<DownloadCommand> e2 : commands) {
+ String commandName = e2.getExportName();
+ DownloadCommand command = e2.getProvider().get();
+ String c = command.getCommand(scheme, projectName, refName);
+ if (c != null) {
+ addCommand(fetchInfo, commandName, c);
+ }
+ }
+ }
+
+ private static void addCommand(FetchInfo fetchInfo, String commandName,
+ String c) {
if (fetchInfo.commands == null) {
fetchInfo.commands = Maps.newTreeMap();
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDiff.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDiff.java
index 6457a12..c7b06df 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDiff.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDiff.java
@@ -22,6 +22,7 @@
import com.google.gerrit.common.data.PatchScript;
import com.google.gerrit.common.data.PatchScript.DisplayMethod;
import com.google.gerrit.common.data.PatchScript.FileMode;
+import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.CacheControl;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -36,6 +37,7 @@
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.git.LargeObjectException;
import com.google.gerrit.server.patch.PatchScriptFactory;
+import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
@@ -53,6 +55,7 @@
import org.kohsuke.args4j.spi.Parameters;
import org.kohsuke.args4j.spi.Setter;
+import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -84,7 +87,8 @@
@Override
public Response<Result> apply(FileResource resource)
- throws ResourceConflictException, ResourceNotFoundException, OrmException {
+ throws ResourceConflictException, ResourceNotFoundException,
+ OrmException, AuthException, InvalidChangeOperationException, IOException {
PatchSet.Id basePatchSet = null;
if (base != null) {
RevisionResource baseResource = revisions.parse(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Revisions.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Revisions.java
index 239aae2..8f8de47 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Revisions.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Revisions.java
@@ -14,8 +14,10 @@
package com.google.gerrit.server.change;
+import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -24,11 +26,15 @@
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.edit.ChangeEdit;
+import com.google.gerrit.server.edit.ChangeEditUtil;
+import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
+import java.io.IOException;
import java.util.Collections;
import java.util.List;
@@ -36,12 +42,15 @@
public class Revisions implements ChildCollection<ChangeResource, RevisionResource> {
private final DynamicMap<RestView<RevisionResource>> views;
private final Provider<ReviewDb> dbProvider;
+ private final ChangeEditUtil editUtil;
@Inject
Revisions(DynamicMap<RestView<RevisionResource>> views,
- Provider<ReviewDb> dbProvider) {
+ Provider<ReviewDb> dbProvider,
+ ChangeEditUtil editUtil) {
this.views = views;
this.dbProvider = dbProvider;
+ this.editUtil = editUtil;
}
@Override
@@ -87,7 +96,9 @@
throws OrmException {
ReviewDb db = dbProvider.get();
- if (id.length() < 6 && id.matches("^[1-9][0-9]{0,4}$")) {
+ if (id.equals("0")) {
+ return loadEdit(change, null);
+ } else if (id.length() < 6 && id.matches("^[1-9][0-9]{0,4}$")) {
// Legacy patch set number syntax.
PatchSet ps = dbProvider.get().patchSets().get(new PatchSet.Id(
change.getChange().getId(),
@@ -107,7 +118,11 @@
// for all patch sets in the change.
RevId revid = new RevId(id);
if (revid.isComplete()) {
- return db.patchSets().byRevision(revid).toList();
+ List<PatchSet> list = db.patchSets().byRevision(revid).toList();
+ if (list.isEmpty()) {
+ return loadEdit(change, revid);
+ }
+ return list;
} else {
return db.patchSets().byRevisionRange(revid, revid.max()).toList();
}
@@ -122,4 +137,22 @@
return out;
}
}
+
+ private List<PatchSet> loadEdit(ChangeResource change, RevId revid)
+ throws OrmException {
+ try {
+ Optional<ChangeEdit> edit = editUtil.byChange(change.getChange());
+ if (edit.isPresent()) {
+ PatchSet ps = new PatchSet(new PatchSet.Id(
+ change.getChange().getId(), 0));
+ ps.setRevision(edit.get().getRevision());
+ if (revid == null || edit.get().getRevision().equals(revid)) {
+ return Collections.singletonList(ps);
+ }
+ }
+ } catch (AuthException | IOException | InvalidChangeOperationException e) {
+ throw new OrmException(e);
+ }
+ return Collections.emptyList();
+ }
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ResourceWeigher.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/DisableReverseDnsLookup.java
similarity index 62%
copy from gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ResourceWeigher.java
copy to gerrit-server/src/main/java/com/google/gerrit/server/config/DisableReverseDnsLookup.java
index 2514272..04712f9 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ResourceWeigher.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/DisableReverseDnsLookup.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 The Android Open Source Project
+// Copyright (C) 2014 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.
@@ -12,13 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.httpd.plugins;
+package com.google.gerrit.server.config;
-import com.google.common.cache.Weigher;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
-class ResourceWeigher implements Weigher<ResourceKey, Resource> {
- @Override
- public int weigh(ResourceKey key, Resource value) {
- return key.weigh() + value.weigh();
- }
+import com.google.inject.BindingAnnotation;
+
+import java.lang.annotation.Retention;
+
+@Retention(RUNTIME)
+@BindingAnnotation
+public @interface DisableReverseDnsLookup {
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/DisableReverseDnsLookupProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/DisableReverseDnsLookupProvider.java
new file mode 100644
index 0000000..8c42714
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/DisableReverseDnsLookupProvider.java
@@ -0,0 +1,35 @@
+// Copyright (C) 2014 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 com.google.gerrit.server.config;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import org.eclipse.jgit.lib.Config;
+
+public class DisableReverseDnsLookupProvider implements Provider<Boolean> {
+ private final boolean disableReverseDnsLookup;
+
+ @Inject
+ DisableReverseDnsLookupProvider(@GerritServerConfig Config config) {
+ disableReverseDnsLookup =
+ config.getBoolean("gerrit", null, "disableReverseDnsLookup", false);
+ }
+
+ @Override
+ public Boolean get() {
+ return disableReverseDnsLookup;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 8c686d5..8df2261 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -233,6 +233,8 @@
bind(FromAddressGenerator.class).toProvider(
FromAddressGeneratorProvider.class).in(SINGLETON);
bind(WebLinks.class).toProvider(WebLinksProvider.class).in(SINGLETON);
+ bind(Boolean.class).annotatedWith(DisableReverseDnsLookup.class)
+ .toProvider(DisableReverseDnsLookupProvider.class).in(SINGLETON);
bind(PatchSetInfoFactory.class);
bind(IdentifiedUser.GenericFactory.class).in(SINGLETON);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditJson.java
index be88004..3c31fc2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditJson.java
@@ -15,20 +15,52 @@
package com.google.gerrit.server.edit;
import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.gerrit.extensions.common.ActionInfo;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.common.EditInfo;
+import com.google.gerrit.extensions.common.FetchInfo;
+import com.google.gerrit.extensions.config.DownloadCommand;
+import com.google.gerrit.extensions.config.DownloadScheme;
+import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.webui.PrivateInternals_UiActionDescription;
+import com.google.gerrit.extensions.webui.UiAction;
+import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.CommonConverters;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.change.ChangeJson;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.eclipse.jgit.revwalk.RevCommit;
import java.io.IOException;
+import java.util.Map;
@Singleton
public class ChangeEditJson {
- public EditInfo toEditInfo(ChangeEdit edit) throws IOException {
+ private final DynamicMap<DownloadCommand> downloadCommands;
+ private final DynamicMap<DownloadScheme> downloadSchemes;
+ private final Provider<CurrentUser> userProvider;
+
+ @Inject
+ ChangeEditJson(DynamicMap<DownloadCommand> downloadCommand,
+ DynamicMap<DownloadScheme> downloadSchemes,
+ Provider<CurrentUser> userProvider) {
+ this.downloadCommands = downloadCommand;
+ this.downloadSchemes = downloadSchemes;
+ this.userProvider = userProvider;
+ }
+
+ public EditInfo toEditInfo(ChangeEdit edit, boolean downloadCommands)
+ throws IOException {
EditInfo out = new EditInfo();
out.commit = fillCommit(edit.getEditCommit());
+ out.actions = fillActions(edit);
+ if (downloadCommands) {
+ out.fetch = fillFetchMap(edit);
+ }
return out;
}
@@ -48,4 +80,61 @@
return commit;
}
+
+ private static Map<String, ActionInfo> fillActions(ChangeEdit edit) {
+ Map<String, ActionInfo> actions = Maps.newTreeMap();
+
+ UiAction.Description descr = new UiAction.Description();
+ PrivateInternals_UiActionDescription.setId(descr, "/");
+ PrivateInternals_UiActionDescription.setMethod(descr, "DELETE");
+ descr.setTitle("Delete edit");
+ actions.put(descr.getId(), new ActionInfo(descr));
+
+ // Only expose publish action when the edit is on top of current ps
+ PatchSet.Id current = edit.getChange().currentPatchSetId();
+ PatchSet basePs = edit.getBasePatchSet();
+ if (basePs.getId().equals(current)) {
+ descr = new UiAction.Description();
+ PrivateInternals_UiActionDescription.setId(descr, "publish");
+ PrivateInternals_UiActionDescription.setMethod(descr, "POST");
+ descr.setTitle("Publish edit");
+ actions.put(descr.getId(), new ActionInfo(descr));
+ } else {
+ descr = new UiAction.Description();
+ PrivateInternals_UiActionDescription.setId(descr, "rebase");
+ PrivateInternals_UiActionDescription.setMethod(descr, "POST");
+ descr.setTitle("Rebase edit");
+ actions.put(descr.getId(), new ActionInfo(descr));
+ }
+
+ return actions;
+ }
+
+ private Map<String, FetchInfo> fillFetchMap(ChangeEdit edit) {
+ Map<String, FetchInfo> r = Maps.newLinkedHashMap();
+ for (DynamicMap.Entry<DownloadScheme> e : downloadSchemes) {
+ String schemeName = e.getExportName();
+ DownloadScheme scheme = e.getProvider().get();
+ if (!scheme.isEnabled()
+ || (scheme.isAuthRequired()
+ && !userProvider.get().isIdentifiedUser())) {
+ continue;
+ }
+
+ // No fluff, just stuff
+ if (!scheme.isAuthSupported()) {
+ continue;
+ }
+
+ String projectName = edit.getChange().getProject().get();
+ String refName = edit.getRefName();
+ FetchInfo fetchInfo = new FetchInfo(scheme.getUrl(projectName), refName);
+ r.put(schemeName, fetchInfo);
+
+ ChangeJson.populateFetchMap(scheme, downloadCommands, projectName,
+ refName, fetchInfo);
+ }
+
+ return r;
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditModifier.java b/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditModifier.java
index 7ea324f..d088698 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditModifier.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditModifier.java
@@ -152,6 +152,10 @@
ObjectInserter inserter = repo.newObjectInserter();
try {
RevCommit editCommit = edit.getEditCommit();
+ if (editCommit.getParentCount() == 0) {
+ throw new InvalidChangeOperationException(
+ "Rebase edit against root commit not implemented");
+ }
RevCommit mergeTip = rw.parseCommit(ObjectId.fromString(
current.getRevision().get()));
ThreeWayMerger m = MergeStrategy.RESOLVE.newMerger(repo, true);
@@ -251,6 +255,10 @@
RevCommit base = rw.parseCommit(ObjectId.fromString(
basePs.getRevision().get()));
+ if (base.getParentCount() == 0) {
+ throw new InvalidChangeOperationException(
+ "Modify edit against root commit not implemented");
+ }
ObjectId newTree = writeNewTree(op, repo, rw, inserter,
prevEdit, reader, file, content, base);
if (ObjectId.equals(newTree, prevEdit.getTree())) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java
index ba50776..a9c6da0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java
@@ -275,7 +275,9 @@
ObjectInserter inserter, RevCommit parent, RevCommit edit)
throws IOException {
CommitBuilder mergeCommit = new CommitBuilder();
- mergeCommit.setParentIds(parent.getParent(0));
+ for (int i = 0; i < parent.getParentCount(); i++) {
+ mergeCommit.addParentId(parent.getParent(i));
+ }
mergeCommit.setAuthor(parent.getAuthorIdent());
mergeCommit.setMessage(parent.getFullMessage());
mergeCommit.setCommitter(edit.getCommitterIdent());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java
index 18ec7c6e..5824c6f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java
@@ -613,9 +613,11 @@
}
if (topics.size() == 1) {
- return String.format("Merge topic '%s'", Iterables.getFirst(topics, null));
+ return String.format("Merge changes from topic '%s'",
+ Iterables.getFirst(topics, null));
} else if (topics.size() > 1) {
- return String.format("Merge topics '%s'", Joiner.on("', '").join(topics));
+ return String.format("Merge changes from topics '%s'",
+ Joiner.on("', '").join(topics));
} else {
return String.format("Merge changes %s%s",
Joiner.on(',').join(Iterables.transform(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/CommitValidationMessage.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/CommitValidationMessage.java
index ab86317..a778482 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/CommitValidationMessage.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/CommitValidationMessage.java
@@ -14,20 +14,8 @@
package com.google.gerrit.server.git.validators;
-public class CommitValidationMessage {
- private final String message;
- private final boolean isError;
-
- public CommitValidationMessage(final String message, final boolean isError) {
- this.message = message;
- this.isError = isError;
- }
-
- public String getMessage() {
- return message;
- }
-
- public boolean isError() {
- return isError;
+public class CommitValidationMessage extends ValidationMessage {
+ public CommitValidationMessage(String message, boolean isError) {
+ super(message, isError);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/ValidationMessage.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/ValidationMessage.java
new file mode 100644
index 0000000..e1098aa
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/ValidationMessage.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2014 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 com.google.gerrit.server.git.validators;
+
+public class ValidationMessage {
+ private final String message;
+ private final boolean isError;
+
+ public ValidationMessage(String message, boolean isError) {
+ this.message = message;
+ this.isError = isError;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public boolean isError() {
+ return isError;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java
index fa7e91b..386f0e5d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java
@@ -79,7 +79,7 @@
@Override
public String get(ChangeData input, FillArgs args)
throws OrmException {
- return ChangeStatusPredicate.VALUES.get(
+ return ChangeStatusPredicate.canonicalize(
input.change().getStatus());
}
};
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/DummyIndex.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/DummyIndex.java
new file mode 100644
index 0000000..47ea8c2
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/DummyIndex.java
@@ -0,0 +1,56 @@
+// Copyright (C) 2014 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 com.google.gerrit.server.index;
+
+import com.google.gerrit.server.query.Predicate;
+import com.google.gerrit.server.query.QueryParseException;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.query.change.ChangeDataSource;
+
+import java.io.IOException;
+
+public class DummyIndex implements ChangeIndex {
+
+ @Override
+ public Schema<ChangeData> getSchema() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public void replace(ChangeData cd) throws IOException {
+ }
+
+ @Override
+ public void delete(ChangeData cd) throws IOException {
+ }
+
+ @Override
+ public void deleteAll() throws IOException {
+ }
+
+ @Override
+ public ChangeDataSource getSource(Predicate<ChangeData> p, int start,
+ int limit) throws QueryParseException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void markReady(boolean ready) throws IOException {
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ResourceWeigher.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/DummyIndexModule.java
similarity index 64%
copy from gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ResourceWeigher.java
copy to gerrit-server/src/main/java/com/google/gerrit/server/index/DummyIndexModule.java
index 2514272..dd7d2d8 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ResourceWeigher.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/DummyIndexModule.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 The Android Open Source Project
+// Copyright (C) 2014 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.
@@ -12,13 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.httpd.plugins;
+package com.google.gerrit.server.index;
-import com.google.common.cache.Weigher;
+import com.google.inject.AbstractModule;
-class ResourceWeigher implements Weigher<ResourceKey, Resource> {
+public class DummyIndexModule extends AbstractModule {
+
@Override
- public int weigh(ResourceKey key, Resource value) {
- return key.weigh() + value.weigh();
+ protected void configure() {
+ install(new IndexModule(1));
+ bind(ChangeIndex.class).toInstance(new DummyIndex());
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexRewriteImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexRewriteImpl.java
index f759f12..40ac213 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexRewriteImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexRewriteImpl.java
@@ -14,7 +14,8 @@
package com.google.gerrit.server.index;
-import com.google.common.annotations.VisibleForTesting;
+import static com.google.gerrit.common.data.GlobalCapability.DEFAULT_MAX_QUERY_LIMIT;
+
import com.google.common.base.MoreObjects;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
@@ -61,9 +62,6 @@
CLOSED_STATUSES = Sets.immutableEnumSet(closed);
}
- @VisibleForTesting
- static final int MAX_LIMIT = 1000;
-
/**
* Get the set of statuses that changes matching the given predicate may have.
*
@@ -135,13 +133,12 @@
throws QueryParseException {
ChangeIndex index = indexes.getSearchIndex();
in = basicRewrites.rewrite(in);
- int limit =
- MoreObjects.firstNonNull(ChangeQueryBuilder.getLimit(in), MAX_LIMIT);
+ int limit = MoreObjects.firstNonNull(
+ ChangeQueryBuilder.getLimit(in), DEFAULT_MAX_QUERY_LIMIT);
// Increase the limit rather than skipping, since we don't know how many
// skipped results would have been filtered out by the enclosing AndSource.
limit += start;
limit = Math.max(limit, 1);
- limit = Math.min(limit, MAX_LIMIT);
Predicate<ChangeData> out = rewriteImpl(in, index, limit);
if (in == out || out instanceof IndexPredicate) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
index 9d93d89..f190f1f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
@@ -27,12 +27,16 @@
/** View of contents at a single ref related to some change. **/
public abstract class AbstractChangeNotes<T> extends VersionedMetaData {
- private boolean loaded;
protected final GitRepositoryManager repoManager;
+ protected final NotesMigration migration;
private final Change.Id changeId;
- AbstractChangeNotes(GitRepositoryManager repoManager, Change.Id changeId) {
+ private boolean loaded;
+
+ AbstractChangeNotes(GitRepositoryManager repoManager,
+ NotesMigration migration, Change.Id changeId) {
this.repoManager = repoManager;
+ this.migration = migration;
this.changeId = changeId;
}
@@ -41,25 +45,33 @@
}
public T load() throws OrmException {
- if (!loaded) {
- Repository repo;
- try {
- repo = repoManager.openMetadataRepository(getProjectName());
- } catch (IOException e) {
- throw new OrmException(e);
- }
- try {
- load(repo);
- loaded = true;
- } catch (ConfigInvalidException | IOException e) {
- throw new OrmException(e);
- } finally {
- repo.close();
- }
+ if (loaded) {
+ return self();
+ }
+ if (!migration.enabled()) {
+ loadDefaults();
+ return self();
+ }
+ Repository repo;
+ try {
+ repo = repoManager.openMetadataRepository(getProjectName());
+ } catch (IOException e) {
+ throw new OrmException(e);
+ }
+ try {
+ load(repo);
+ loaded = true;
+ } catch (ConfigInvalidException | IOException e) {
+ throw new OrmException(e);
+ } finally {
+ repo.close();
}
return self();
}
+ /** Load default values for any instance variables when notedb is disabled. */
+ protected abstract void loadDefaults();
+
/**
* @return the NameKey for the project where the notes should be stored,
* which is not necessarily the same as the change's project.
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java
index 6286332..c561a0d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java
@@ -27,6 +27,7 @@
public class ChangeNoteUtil {
static final String GERRIT_PLACEHOLDER_HOST = "gerrit";
+ static final FooterKey FOOTER_HASHTAGS = new FooterKey("Hashtags");
static final FooterKey FOOTER_LABEL = new FooterKey("Label");
static final FooterKey FOOTER_PATCH_SET = new FooterKey("Patch-set");
static final FooterKey FOOTER_STATUS = new FooterKey("Status");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java
index 425d310..9a72752 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java
@@ -22,6 +22,7 @@
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Ordering;
import com.google.common.collect.Table;
@@ -112,17 +113,21 @@
@Singleton
public static class Factory {
private final GitRepositoryManager repoManager;
+ private final NotesMigration migration;
private final AllUsersNameProvider allUsersProvider;
@VisibleForTesting
@Inject
- public Factory(GitRepositoryManager repoManager, AllUsersNameProvider allUsersProvider) {
+ public Factory(GitRepositoryManager repoManager,
+ NotesMigration migration,
+ AllUsersNameProvider allUsersProvider) {
this.repoManager = repoManager;
+ this.migration = migration;
this.allUsersProvider = allUsersProvider;
}
public ChangeNotes create(Change change) {
- return new ChangeNotes(repoManager, allUsersProvider, change);
+ return new ChangeNotes(repoManager, migration, allUsersProvider, change);
}
}
@@ -134,16 +139,16 @@
private ImmutableListMultimap<PatchSet.Id, ChangeMessage> changeMessages;
private ImmutableListMultimap<PatchSet.Id, PatchLineComment> commentsForBase;
private ImmutableListMultimap<PatchSet.Id, PatchLineComment> commentsForPS;
+ private ImmutableSet<String> hashtags;
NoteMap noteMap;
private final AllUsersName allUsers;
private DraftCommentNotes draftCommentNotes;
- @Inject
@VisibleForTesting
- public ChangeNotes(GitRepositoryManager repoManager,
+ public ChangeNotes(GitRepositoryManager repoManager, NotesMigration migration,
AllUsersNameProvider allUsersProvider, Change change) {
- super(repoManager, change.getId());
+ super(repoManager, migration, change.getId());
this.allUsers = allUsersProvider.get();
this.change = new Change(change);
}
@@ -160,6 +165,10 @@
return reviewers;
}
+ public ImmutableSet<String> getHashtags() {
+ return hashtags;
+ }
+
/**
* @return a list of all users who have ever been a reviewer on this change.
*/
@@ -214,8 +223,8 @@
throws OrmException {
if (draftCommentNotes == null ||
!author.equals(draftCommentNotes.getAuthor())) {
- draftCommentNotes = new DraftCommentNotes(repoManager, allUsers,
- getChangeId(), author);
+ draftCommentNotes = new DraftCommentNotes(repoManager, migration,
+ allUsers, getChangeId(), author);
draftCommentNotes.load();
}
}
@@ -272,6 +281,11 @@
commentsForPS = ImmutableListMultimap.copyOf(parser.commentsForPs);
noteMap = parser.commentNoteMap;
+ if (parser.hashtags != null) {
+ hashtags = ImmutableSet.copyOf(parser.hashtags);
+ } else {
+ hashtags = ImmutableSet.of();
+ }
ImmutableSetMultimap.Builder<ReviewerState, Account.Id> reviewers =
ImmutableSetMultimap.builder();
for (Map.Entry<Account.Id, ReviewerState> e
@@ -290,7 +304,8 @@
}
}
- private void loadDefaults() {
+ @Override
+ protected void loadDefaults() {
approvals = ImmutableListMultimap.of();
reviewers = ImmutableSetMultimap.of();
submitRecords = ImmutableList.of();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotesParser.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
index 83cdc68..4d3043b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.notedb;
+import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_HASHTAGS;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_LABEL;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_PATCH_SET;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_STATUS;
@@ -22,6 +23,7 @@
import com.google.common.base.Enums;
import com.google.common.base.Optional;
+import com.google.common.base.Splitter;
import com.google.common.base.Supplier;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableListMultimap;
@@ -29,6 +31,7 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
import com.google.common.collect.Table;
import com.google.common.collect.Tables;
import com.google.common.primitives.Ints;
@@ -63,6 +66,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
class ChangeNotesParser implements AutoCloseable {
final Map<Account.Id, ReviewerState> reviewers;
@@ -72,6 +76,7 @@
final Multimap<PatchSet.Id, PatchLineComment> commentsForBase;
NoteMap commentNoteMap;
Change.Status status;
+ Set<String> hashtags;
private final Change.Id changeId;
private final ObjectId tip;
@@ -143,6 +148,7 @@
PatchSet.Id psId = parsePatchSetId(commit);
Account.Id accountId = parseIdent(commit);
parseChangeMessage(psId, accountId, commit);
+ parseHashtags(commit);
if (submitRecords.isEmpty()) {
@@ -162,6 +168,20 @@
}
}
+ private void parseHashtags(RevCommit commit) throws ConfigInvalidException {
+ // Commits are parsed in reverse order and only the last set of hashtags should be used.
+ if (hashtags != null) {
+ return;
+ }
+ List<String> hashtagsLines = commit.getFooterLines(FOOTER_HASHTAGS);
+ if (hashtagsLines.isEmpty()) {
+ return;
+ } else if (hashtagsLines.size() > 1) {
+ throw expectedOneFooter(FOOTER_HASHTAGS, hashtagsLines);
+ }
+ hashtags = Sets.newHashSet(Splitter.on(',').split(hashtagsLines.get(0)));
+ }
+
private Change.Status parseStatus(RevCommit commit)
throws ConfigInvalidException {
List<String> statusLines = commit.getFooterLines(FOOTER_STATUS);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeUpdate.java
index d494a55..1e9e4dd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeUpdate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeUpdate.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.notedb;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_HASHTAGS;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_LABEL;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_PATCH_SET;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_STATUS;
@@ -22,6 +23,7 @@
import static com.google.gerrit.server.notedb.CommentsInNotesUtil.getCommentPsId;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
@@ -62,6 +64,7 @@
import java.util.Date;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* A delta to apply to a change.
@@ -93,6 +96,7 @@
private final CommentsInNotesUtil commentsUtil;
private List<PatchLineComment> commentsForBase;
private List<PatchLineComment> commentsForPs;
+ private Set<String> hashtags;
private String changeMessage;
private ChangeNotes notes;
@@ -342,6 +346,10 @@
}
+ public void setHashtags(Set<String> hashtags) {
+ this.hashtags = hashtags;
+ }
+
public void putReviewer(Account.Id reviewer, ReviewerState type) {
checkArgument(type != ReviewerState.REMOVED, "invalid ReviewerType");
reviewers.put(reviewer, type);
@@ -448,6 +456,10 @@
addFooter(msg, FOOTER_STATUS, status.name().toLowerCase());
}
+ if (hashtags != null) {
+ addFooter(msg, FOOTER_HASHTAGS, Joiner.on(",").join(hashtags));
+ }
+
for (Map.Entry<Account.Id, ReviewerState> e : reviewers.entrySet()) {
Account account = accountCache.get(e.getKey()).getAccount();
PersonIdent ident = newIdent(account, when);
@@ -507,7 +519,8 @@
&& reviewers.isEmpty()
&& status == null
&& subject == null
- && submitRecords == null;
+ && submitRecords == null
+ && hashtags == null;
}
private static StringBuilder addFooter(StringBuilder sb, FooterKey footer) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/DraftCommentNotes.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
index 2f2c97f..d40358b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
@@ -48,19 +48,22 @@
@Singleton
public static class Factory {
private final GitRepositoryManager repoManager;
+ private final NotesMigration migration;
private final AllUsersName draftsProject;
@VisibleForTesting
@Inject
public Factory(GitRepositoryManager repoManager,
+ NotesMigration migration,
AllUsersNameProvider allUsers) {
this.repoManager = repoManager;
+ this.migration = migration;
this.draftsProject = allUsers.get();
}
public DraftCommentNotes create(Change.Id changeId, Account.Id accountId) {
- return new DraftCommentNotes(repoManager, draftsProject, changeId,
- accountId);
+ return new DraftCommentNotes(repoManager, migration, draftsProject,
+ changeId, accountId);
}
}
@@ -71,9 +74,9 @@
private final Table<PatchSet.Id, String, PatchLineComment> draftPsComments;
private NoteMap noteMap;
- DraftCommentNotes(GitRepositoryManager repoManager,
+ DraftCommentNotes(GitRepositoryManager repoManager, NotesMigration migration,
AllUsersName draftsProject, Change.Id changeId, Account.Id author) {
- super(repoManager, changeId);
+ super(repoManager, migration, changeId);
this.draftsProject = draftsProject;
this.author = author;
@@ -150,6 +153,11 @@
}
@Override
+ protected void loadDefaults() {
+ // Do nothing; tables are final and initialized in constructor.
+ }
+
+ @Override
protected Project.NameKey getProjectName() {
return draftsProject;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchScriptFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchScriptFactory.java
index e2aa2c8..919b41a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchScriptFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchScriptFactory.java
@@ -14,9 +14,11 @@
package com.google.gerrit.server.patch;
+import com.google.common.base.Optional;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.CommentDetail;
import com.google.gerrit.common.data.PatchScript;
+import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountDiffPreference;
import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
@@ -31,10 +33,13 @@
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PatchLineCommentsUtil;
import com.google.gerrit.server.account.AccountInfoCacheFactory;
+import com.google.gerrit.server.edit.ChangeEdit;
+import com.google.gerrit.server.edit.ChangeEditUtil;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.LargeObjectException;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
@@ -80,6 +85,7 @@
private final PatchSet.Id psa;
private final PatchSet.Id psb;
private final AccountDiffPreference diffPrefs;
+ private final ChangeEditUtil editReader;
private final Change.Id changeId;
private boolean loadHistory = true;
@@ -99,6 +105,7 @@
final PatchListCache patchListCache, final ReviewDb db,
final AccountInfoCacheFactory.Factory aicFactory,
PatchLineCommentsUtil plcUtil,
+ ChangeEditUtil editReader,
@Assisted ChangeControl control,
@Assisted final String fileName,
@Assisted("patchSetA") @Nullable final PatchSet.Id patchSetA,
@@ -111,6 +118,7 @@
this.control = control;
this.aicFactory = aicFactory;
this.plcUtil = plcUtil;
+ this.editReader = editReader;
this.fileName = fileName;
this.psa = patchSetA;
@@ -130,7 +138,8 @@
@Override
public PatchScript call() throws OrmException, NoSuchChangeException,
- LargeObjectException {
+ LargeObjectException, AuthException,
+ InvalidChangeOperationException, IOException {
validatePatchSetId(psa);
validatePatchSetId(psb);
@@ -197,12 +206,16 @@
}
private ObjectId toObjectId(final ReviewDb db, final PatchSet.Id psId)
- throws OrmException, NoSuchChangeException {
+ throws OrmException, NoSuchChangeException, AuthException,
+ InvalidChangeOperationException, NoSuchChangeException, IOException {
if (!changeId.equals(psId.getParentKey())) {
throw new NoSuchChangeException(changeId);
}
- final PatchSet ps = db.patchSets().get(psId);
+ if (psId.get() == 0) {
+ return getEditRev();
+ }
+ PatchSet ps = db.patchSets().get(psId);
if (ps == null || ps.getRevision() == null
|| ps.getRevision().get() == null) {
throw new NoSuchChangeException(changeId);
@@ -216,6 +229,15 @@
}
}
+ private ObjectId getEditRev() throws AuthException,
+ NoSuchChangeException, IOException, InvalidChangeOperationException {
+ Optional<ChangeEdit> edit = editReader.byChange(change);
+ if (edit.isPresent()) {
+ return edit.get().getRef().getObjectId();
+ }
+ throw new NoSuchChangeException(change.getId());
+ }
+
private void validatePatchSetId(final PatchSet.Id psId)
throws NoSuchChangeException {
if (psId == null) { // OK, means use base;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
index 37aafe0..e93f69c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
@@ -413,6 +413,16 @@
}
}
+ /** Can this user edit the hashtag name? */
+ public boolean canEditHashtags() {
+ return isOwner() // owner (aka creator) of the change can edit hashtags
+ || getRefControl().isOwner() // branch owner can edit hashtags
+ || getProjectControl().isOwner() // project owner can edit hashtags
+ || getCurrentUser().getCapabilities().canAdministrateServer() // site administers are god
+ || getRefControl().canEditHashtags(); // user can edit hashtag on a specific ref
+ }
+
+
public List<SubmitRecord> getSubmitRecords(ReviewDb db, PatchSet patchSet) {
return canSubmit(db, patchSet, null, false, true, false);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetConfig.java
index e0bc7e6..bb91097 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetConfig.java
@@ -17,13 +17,11 @@
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.AllProjectsNameProvider;
import com.google.gerrit.server.config.PluginConfigFactory;
import com.google.gerrit.server.config.ProjectConfigEntry;
import com.google.gerrit.server.git.TransferConfig;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
@Singleton
@@ -40,8 +38,7 @@
DynamicMap<ProjectConfigEntry> pluginConfigEntries,
PluginConfigFactory cfgFactory,
AllProjectsNameProvider allProjects,
- DynamicMap<RestView<ProjectResource>> views,
- Provider<CurrentUser> currentUser) {
+ DynamicMap<RestView<ProjectResource>> views) {
this.config = config;
this.pluginConfigEntries = pluginConfigEntries;
this.allProjects = allProjects;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
index 9848faa..d3a8b69 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
@@ -424,6 +424,11 @@
return canPerform(Permission.EDIT_TOPIC_NAME);
}
+ /** @return true if this user can edit hashtag names. */
+ public boolean canEditHashtags() {
+ return canPerform(Permission.EDIT_HASHTAGS);
+ }
+
/** @return true if this user can force edit topic names. */
public boolean canForceEditTopicName() {
return canForcePerform(Permission.EDIT_TOPIC_NAME);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BasicChangeRewrites.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BasicChangeRewrites.java
index 37d96ea..e853656 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BasicChangeRewrites.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BasicChangeRewrites.java
@@ -37,40 +37,37 @@
new QueryRewriter.Definition<ChangeData, BasicChangeRewrites>(
BasicChangeRewrites.class, BUILDER);
- protected final Provider<ReviewDb> dbProvider;
-
@Inject
- public BasicChangeRewrites(Provider<ReviewDb> dbProvider) {
+ public BasicChangeRewrites() {
super(mydef);
- this.dbProvider = dbProvider;
}
@Rewrite("-status:open")
@NoCostComputation
public Predicate<ChangeData> r00_notOpen() {
- return ChangeStatusPredicate.closed(dbProvider);
+ return ChangeStatusPredicate.closed();
}
@Rewrite("-status:closed")
@NoCostComputation
public Predicate<ChangeData> r00_notClosed() {
- return ChangeStatusPredicate.open(dbProvider);
+ return ChangeStatusPredicate.open();
}
@SuppressWarnings("unchecked")
@NoCostComputation
@Rewrite("-status:merged")
public Predicate<ChangeData> r00_notMerged() {
- return or(ChangeStatusPredicate.open(dbProvider),
- new ChangeStatusPredicate(Change.Status.ABANDONED));
+ return or(ChangeStatusPredicate.open(),
+ ChangeStatusPredicate.forStatus(Change.Status.ABANDONED));
}
@SuppressWarnings("unchecked")
@NoCostComputation
@Rewrite("-status:abandoned")
public Predicate<ChangeData> r00_notAbandoned() {
- return or(ChangeStatusPredicate.open(dbProvider),
- new ChangeStatusPredicate(Change.Status.MERGED));
+ return or(ChangeStatusPredicate.open(),
+ ChangeStatusPredicate.forStatus(Change.Status.MERGED));
}
@NoCostComputation
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 fabe61c..09e4e5e 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
@@ -276,22 +276,15 @@
@Operator
public Predicate<ChangeData> status(String statusName) {
- if ("open".equals(statusName) || "pending".equals(statusName)) {
- return status_open();
-
- } else if ("closed".equals(statusName)) {
- return ChangeStatusPredicate.closed(args.db);
-
- } else if ("reviewed".equalsIgnoreCase(statusName)) {
+ if ("reviewed".equalsIgnoreCase(statusName)) {
return new IsReviewedPredicate();
-
} else {
- return new ChangeStatusPredicate(statusName);
+ return ChangeStatusPredicate.parse(statusName);
}
}
public Predicate<ChangeData> status_open() {
- return ChangeStatusPredicate.open(args.db);
+ return ChangeStatusPredicate.open();
}
@Operator
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java
index cea6af8..7d1cdd1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java
@@ -14,20 +14,18 @@
package com.google.gerrit.server.query.change;
-import static com.google.common.base.Preconditions.checkArgument;
-
-import com.google.common.collect.ImmutableBiMap;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Change.Status;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.index.ChangeField;
import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.query.Predicate;
import com.google.gwtorm.server.OrmException;
-import com.google.inject.Provider;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.TreeMap;
/**
* Predicate for a {@link Status}.
@@ -35,49 +33,68 @@
* The actual name of this operator can differ, it usually comes as {@code
* status:} but may also be {@code is:} to help do-what-i-meanery for end-users
* searching for changes. Either operator name has the same meaning.
+ * <p>
+ * Status names are looked up by prefix case-insensitively.
*/
public final class ChangeStatusPredicate extends IndexPredicate<ChangeData> {
- public static final ImmutableBiMap<Change.Status, String> VALUES;
+ private static final TreeMap<String, Predicate<ChangeData>> PREDICATES;
+ private static final Predicate<ChangeData> CLOSED;
+ private static final Predicate<ChangeData> OPEN;
static {
- ImmutableBiMap.Builder<Change.Status, String> values =
- ImmutableBiMap.builder();
+ PREDICATES = new TreeMap<>();
+ List<Predicate<ChangeData>> open = new ArrayList<>();
+ List<Predicate<ChangeData>> closed = new ArrayList<>();
+
for (Change.Status s : Change.Status.values()) {
- values.put(s, s.name().toLowerCase());
+ ChangeStatusPredicate p = new ChangeStatusPredicate(s);
+ PREDICATES.put(canonicalize(s), p);
+ (s.isOpen() ? open : closed).add(p);
}
- VALUES = values.build();
+
+ CLOSED = Predicate.or(closed);
+ OPEN = Predicate.or(open);
+
+ PREDICATES.put("closed", CLOSED);
+ PREDICATES.put("open", OPEN);
+ PREDICATES.put("pending", OPEN);
}
- public static Predicate<ChangeData> open(Provider<ReviewDb> dbProvider) {
- List<Predicate<ChangeData>> r = new ArrayList<>(4);
- for (final Change.Status e : Change.Status.values()) {
- if (e.isOpen()) {
- r.add(new ChangeStatusPredicate(e));
- }
- }
- return r.size() == 1 ? r.get(0) : or(r);
+ public static String canonicalize(Change.Status status) {
+ return status.name().toLowerCase();
}
- public static Predicate<ChangeData> closed(Provider<ReviewDb> dbProvider) {
- List<Predicate<ChangeData>> r = new ArrayList<>(4);
- for (final Change.Status e : Change.Status.values()) {
- if (e.isClosed()) {
- r.add(new ChangeStatusPredicate(e));
+ public static Predicate<ChangeData> parse(String value) {
+ String lower = value.toLowerCase();
+ NavigableMap<String, Predicate<ChangeData>> head =
+ PREDICATES.tailMap(lower, true);
+ if (!head.isEmpty()) {
+ // Assume no statuses share a common prefix so we can only walk one entry.
+ Map.Entry<String, Predicate<ChangeData>> e =
+ head.entrySet().iterator().next();
+ if (e.getKey().startsWith(lower)) {
+ return e.getValue();
}
}
- return r.size() == 1 ? r.get(0) : or(r);
+ throw new IllegalArgumentException("invalid change status: " + value);
+ }
+
+ public static Predicate<ChangeData> forStatus(Change.Status status) {
+ return parse(status.name());
+ }
+
+ public static Predicate<ChangeData> open() {
+ return OPEN;
+ }
+
+ public static Predicate<ChangeData> closed() {
+ return CLOSED;
}
private final Change.Status status;
- ChangeStatusPredicate(String value) {
- super(ChangeField.STATUS, value);
- status = VALUES.inverse().get(value);
- checkArgument(status != null, "invalid change status: %s", value);
- }
-
- ChangeStatusPredicate(Change.Status status) {
- super(ChangeField.STATUS, VALUES.get(status));
+ private ChangeStatusPredicate(Change.Status status) {
+ super(ChangeField.STATUS, canonicalize(status));
this.status = status;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/securestore/DefaultSecureStore.java b/gerrit-server/src/main/java/com/google/gerrit/server/securestore/DefaultSecureStore.java
index e0e8237..9a07354 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/securestore/DefaultSecureStore.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/securestore/DefaultSecureStore.java
@@ -15,7 +15,6 @@
package com.google.gerrit.server.securestore;
import com.google.gerrit.common.FileUtil;
-import com.google.gerrit.extensions.annotations.Export;
import com.google.gerrit.server.config.SitePaths;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -27,12 +26,10 @@
import java.io.File;
import java.io.IOException;
+import java.util.List;
@Singleton
-@Export(DefaultSecureStore.NAME)
public class DefaultSecureStore implements SecureStore {
- public static final String NAME = "default";
-
private final FileBasedConfig sec;
@Inject
@@ -52,6 +49,11 @@
}
@Override
+ public String[] getList(String section, String subsection, String name) {
+ return sec.getStringList(section, subsection, name);
+ }
+
+ @Override
public void set(String section, String subsection, String name, String value) {
if (value != null) {
sec.setString(section, subsection, name, value);
@@ -62,6 +64,17 @@
}
@Override
+ public void setList(String section, String subsection, String name,
+ List<String> values) {
+ if (values != null) {
+ sec.setStringList(section, subsection, name, values);
+ } else {
+ sec.unset(section, subsection, name);
+ }
+ save();
+ }
+
+ @Override
public void unset(String section, String subsection, String name) {
sec.unset(section, subsection, name);
save();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/securestore/SecureStore.java b/gerrit-server/src/main/java/com/google/gerrit/server/securestore/SecureStore.java
index 3fe00f4..4c54cc8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/securestore/SecureStore.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/securestore/SecureStore.java
@@ -16,12 +16,18 @@
import com.google.gerrit.extensions.annotations.ExtensionPoint;
+import java.util.List;
+
@ExtensionPoint
public interface SecureStore {
String get(String section, String subsection, String name);
+ String[] getList(String section, String subsection, String name);
+
void set(String section, String subsection, String name, String value);
+ void setList(String section, String subsection, String name, List<String> values);
+
void unset(String section, String subsection, String name);
}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java
index 0d5be51..0265d9d 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java
@@ -55,6 +55,7 @@
import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.config.AnonymousCowardNameProvider;
import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.gerrit.server.config.DisableReverseDnsLookup;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitModule;
@@ -126,6 +127,7 @@
@Inject private GetComment getComment;
@Inject private IdentifiedUser.GenericFactory userFactory;
@Inject private InMemoryRepositoryManager repoManager;
+ @Inject private NotesMigration migration;
@Inject private PatchLineCommentsUtil plcUtil;
@Before
@@ -180,6 +182,8 @@
.toProvider(AnonymousCowardNameProvider.class);
bind(String.class).annotatedWith(CanonicalWebUrl.class)
.toInstance("http://localhost:8080/");
+ bind(Boolean.class).annotatedWith(DisableReverseDnsLookup.class)
+ .toInstance(Boolean.FALSE);
bind(GroupBackend.class).to(SystemGroupBackend.class).in(SINGLETON);
bind(AccountCache.class).toInstance(accountCache);
bind(GitReferenceUpdated.class)
@@ -294,7 +298,8 @@
}
private ChangeControl stubChangeControl(Change c) throws OrmException {
- return TestChanges.stubChangeControl(repoManager, c, allUsers, changeOwner);
+ return TestChanges.stubChangeControl(
+ repoManager, migration, c, allUsers, changeOwner);
}
private Change newChange() {
@@ -302,7 +307,8 @@
}
private ChangeUpdate newUpdate(Change c, final IdentifiedUser user) throws Exception {
- return TestChanges.newUpdate(injector, repoManager, c, allUsers, user);
+ return TestChanges.newUpdate(
+ injector, repoManager, migration, c, allUsers, user);
}
@Test
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexRewriteTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexRewriteTest.java
index c8275e8..1fa946f 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexRewriteTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexRewriteTest.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.index;
+import static com.google.gerrit.common.data.GlobalCapability.DEFAULT_MAX_QUERY_LIMIT;
import static com.google.gerrit.reviewdb.client.Change.Status.ABANDONED;
import static com.google.gerrit.reviewdb.client.Change.Status.DRAFT;
import static com.google.gerrit.reviewdb.client.Change.Status.MERGED;
@@ -53,9 +54,7 @@
indexes = new IndexCollection();
indexes.setSearchIndex(index);
queryBuilder = new FakeQueryBuilder(indexes);
- rewrite = new IndexRewriteImpl(
- indexes,
- new BasicChangeRewrites(null));
+ rewrite = new IndexRewriteImpl(indexes, new BasicChangeRewrites());
}
@Test
@@ -180,13 +179,6 @@
}
@Test
- public void testStartDoesNotExceedMaxLimit() throws Exception {
- Predicate<ChangeData> in = parse("file:a");
- assertEquals(query(in), rewrite.rewrite(in, 0));
- assertEquals(query(in), rewrite.rewrite(in, 1));
- }
-
- @Test
public void testGetPossibleStatus() throws Exception {
assertEquals(EnumSet.allOf(Change.Status.class), status("file:a"));
assertEquals(EnumSet.of(NEW), status("is:new"));
@@ -233,7 +225,7 @@
private IndexedChangeQuery query(Predicate<ChangeData> p)
throws QueryParseException {
- return query(p, IndexRewriteImpl.MAX_LIMIT);
+ return query(p, DEFAULT_MAX_QUERY_LIMIT);
}
private IndexedChangeQuery query(Predicate<ChangeData> p, int limit)
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
index 8b2d5e5..635ccbd 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
@@ -38,6 +38,7 @@
import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.config.AnonymousCowardNameProvider;
import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.gerrit.server.config.DisableReverseDnsLookup;
import com.google.gerrit.server.config.FactoryModule;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
@@ -75,6 +76,8 @@
private static final TimeZone TZ =
TimeZone.getTimeZone("America/Los_Angeles");
+ private static final NotesMigration MIGRATION = NotesMigration.allEnabled();
+
protected Account.Id otherUserId;
protected FakeAccountCache accountCache;
protected IdentifiedUser changeOwner;
@@ -116,7 +119,7 @@
@Override
public void configure() {
install(new GitModule());
- bind(NotesMigration.class).toInstance(NotesMigration.allEnabled());
+ bind(NotesMigration.class).toInstance(MIGRATION);
bind(GitRepositoryManager.class).toInstance(repoManager);
bind(ProjectCache.class).toProvider(Providers.<ProjectCache> of(null));
bind(CapabilityControl.Factory.class)
@@ -127,6 +130,8 @@
.toProvider(AnonymousCowardNameProvider.class);
bind(String.class).annotatedWith(CanonicalWebUrl.class)
.toInstance("http://localhost:8080/");
+ bind(Boolean.class).annotatedWith(DisableReverseDnsLookup.class)
+ .toInstance(Boolean.FALSE);
bind(Realm.class).to(FakeRealm.class);
bind(GroupBackend.class).to(SystemGroupBackend.class).in(SINGLETON);
bind(AccountCache.class).toInstance(accountCache);
@@ -170,11 +175,12 @@
protected ChangeUpdate newUpdate(Change c, IdentifiedUser user)
throws OrmException {
- return TestChanges.newUpdate(injector, repoManager, c, allUsers, user);
+ return TestChanges.newUpdate(
+ injector, repoManager, MIGRATION, c, allUsers, user);
}
protected ChangeNotes newNotes(Change c) throws OrmException {
- return new ChangeNotes(repoManager, allUsers, c).load();
+ return new ChangeNotes(repoManager, MIGRATION, allUsers, c).load();
}
protected static SubmitRecord submitRecord(String status,
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeNotesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeNotesTest.java
index 06a48eb..d043407 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeNotesTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeNotesTest.java
@@ -58,6 +58,7 @@
import java.io.IOException;
import java.sql.Timestamp;
import java.util.ArrayList;
+import java.util.LinkedHashSet;
import java.util.List;
public class ChangeNotesTest extends AbstractChangeNotesTest {
@@ -339,6 +340,39 @@
}
@Test
+ public void hashtagCommit() throws Exception {
+ Change c = newChange();
+ ChangeUpdate update = newUpdate(c, changeOwner);
+ LinkedHashSet<String> hashtags = new LinkedHashSet<String>();
+ hashtags.add("tag1");
+ hashtags.add("tag2");
+ update.setHashtags(hashtags);
+ update.commit();
+ RevWalk walk = new RevWalk(repo);
+ try {
+ RevCommit commit = walk.parseCommit(update.getRevision());
+ walk.parseBody(commit);
+ assertTrue(commit.getFullMessage().endsWith("Hashtags: tag1,tag2\n"));
+ } finally {
+ walk.release();
+ }
+ }
+
+ @Test
+ public void hashtagChangeNotes() throws Exception {
+ Change c = newChange();
+ ChangeUpdate update = newUpdate(c, changeOwner);
+ LinkedHashSet<String> hashtags = new LinkedHashSet<String>();
+ hashtags.add("tag1");
+ hashtags.add("tag2");
+ update.setHashtags(hashtags);
+ update.commit();
+
+ ChangeNotes notes = newNotes(c);
+ assertEquals(hashtags, notes.getHashtags());
+ }
+
+ @Test
public void emptyExceptSubject() throws Exception {
ChangeUpdate update = newUpdate(newChange(), changeOwner);
update.setSubject("Create change");
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java b/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java
index ba292d6..ae1ca56 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java
@@ -46,6 +46,7 @@
import com.google.gerrit.server.config.AnonymousCowardNameProvider;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.CanonicalWebUrlProvider;
+import com.google.gerrit.server.config.DisableReverseDnsLookup;
import com.google.gerrit.server.config.FactoryModule;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
@@ -269,6 +270,8 @@
bind(GroupBackend.class).to(SystemGroupBackend.class);
bind(String.class).annotatedWith(CanonicalWebUrl.class)
.toProvider(CanonicalWebUrlProvider.class);
+ bind(Boolean.class).annotatedWith(DisableReverseDnsLookup.class)
+ .toInstance(Boolean.FALSE);
bind(String.class).annotatedWith(AnonymousCowardName.class)
.toProvider(AnonymousCowardNameProvider.class);
bind(ChangeKindCache.class).to(ChangeKindCacheImpl.NoCache.class);
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 3af5cb4..c9fdfbd 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
@@ -20,6 +20,7 @@
import static java.util.concurrent.TimeUnit.MINUTES;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
@@ -29,6 +30,7 @@
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.projects.ProjectInput;
+import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.reviewdb.client.Account;
@@ -200,6 +202,7 @@
ins2.insert();
assertResultEquals(change1, queryOne("status:new"));
+ assertResultEquals(change1, queryOne("status:NEW"));
assertResultEquals(change1, queryOne("is:new"));
assertResultEquals(change2, queryOne("status:merged"));
assertResultEquals(change2, queryOne("is:merged"));
@@ -226,6 +229,17 @@
assertEquals(2, results.size());
assertResultEquals(change2, results.get(0));
assertResultEquals(change1, results.get(1));
+
+ assertEquals(2, query("status:OPEN").size());
+ assertEquals(2, query("status:o").size());
+ assertEquals(2, query("status:op").size());
+ assertEquals(2, query("status:ope").size());
+ assertEquals(2, query("status:pending").size());
+ assertEquals(2, query("status:PENDING").size());
+ assertEquals(2, query("status:p").size());
+ assertEquals(2, query("status:pe").size());
+ assertEquals(2, query("status:pen").size());
+
results = query("is:open");
assertEquals(2, results.size());
assertResultEquals(change2, results.get(0));
@@ -253,6 +267,15 @@
assertEquals(2, results.size());
assertResultEquals(change2, results.get(0));
assertResultEquals(change1, results.get(1));
+
+ assertEquals(2, query("status:CLOSED").size());
+ assertEquals(2, query("status:c").size());
+ assertEquals(2, query("status:cl").size());
+ assertEquals(2, query("status:clo").size());
+ assertEquals(2, query("status:clos").size());
+ assertEquals(2, query("status:close").size());
+ assertEquals(2, query("status:closed").size());
+
results = query("is:closed");
assertEquals(2, results.size());
assertResultEquals(change2, results.get(0));
@@ -260,6 +283,28 @@
}
@Test
+ public void byStatusPrefix() throws Exception {
+ TestRepository<InMemoryRepository> repo = createProject("repo");
+ ChangeInserter ins1 = newChange(repo, null, null, null, null);
+ Change change1 = ins1.getChange();
+ change1.setStatus(Change.Status.NEW);
+ ins1.insert();
+ ChangeInserter ins2 = newChange(repo, null, null, null, null);
+ Change change2 = ins2.getChange();
+ change2.setStatus(Change.Status.MERGED);
+ ins2.insert();
+
+ assertResultEquals(change1, queryOne("status:n"));
+ assertResultEquals(change1, queryOne("status:ne"));
+ assertResultEquals(change1, queryOne("status:new"));
+ assertResultEquals(change1, queryOne("status:N"));
+ assertResultEquals(change1, queryOne("status:nE"));
+ assertResultEquals(change1, queryOne("status:neW"));
+ assertBadQuery("status:nx");
+ assertBadQuery("status:newx");
+ }
+
+ @Test
public void byCommit() throws Exception {
TestRepository<InMemoryRepository> repo = createProject("repo");
ChangeInserter ins = newChange(repo, null, null, null, null);
@@ -941,6 +986,15 @@
assertEquals(message, expected.getId().get(), actual._number);
}
+ protected void assertBadQuery(Object query) throws Exception {
+ try {
+ query(query);
+ fail("expected BadRequestException for query: " + query);
+ } catch (BadRequestException e) {
+ // Expected.
+ }
+ }
+
protected TestRepository<InMemoryRepository> createProject(String name)
throws Exception {
CreateProject create = projectFactory.create(name);
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/TestChanges.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/TestChanges.java
index 9c1b8ed..2ddb41e 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/testutil/TestChanges.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/TestChanges.java
@@ -30,6 +30,7 @@
import com.google.gerrit.server.notedb.ChangeDraftUpdate;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ChangeUpdate;
+import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.util.TimeUtil;
import com.google.gwtorm.server.OrmException;
@@ -64,7 +65,7 @@
}
public static ChangeUpdate newUpdate(Injector injector,
- GitRepositoryManager repoManager, Change c,
+ GitRepositoryManager repoManager, NotesMigration migration, Change c,
final AllUsersNameProvider allUsers, final IdentifiedUser user)
throws OrmException {
return injector.createChildInjector(new FactoryModule() {
@@ -76,18 +77,19 @@
bind(AllUsersName.class).toProvider(allUsers);
}
}).getInstance(ChangeUpdate.Factory.class).create(
- stubChangeControl(repoManager, c, allUsers, user), TimeUtil.nowTs(),
- Ordering.<String> natural());
+ stubChangeControl(repoManager, migration, c, allUsers, user),
+ TimeUtil.nowTs(), Ordering.<String> natural());
}
public static ChangeControl stubChangeControl(
- GitRepositoryManager repoManager, Change c, AllUsersNameProvider allUsers,
+ GitRepositoryManager repoManager, NotesMigration migration,
+ Change c, AllUsersNameProvider allUsers,
IdentifiedUser user) throws OrmException {
ChangeControl ctl = EasyMock.createNiceMock(ChangeControl.class);
expect(ctl.getChange()).andStubReturn(c);
expect(ctl.getCurrentUser()).andStubReturn(user);
- ChangeNotes notes = new ChangeNotes(repoManager, allUsers, c);
- notes = notes.load();
+ ChangeNotes notes = new ChangeNotes(repoManager, migration, allUsers, c)
+ .load();
expect(ctl.getNotes()).andStubReturn(notes);
EasyMock.replay(ctl);
return ctl;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Query.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Query.java
index a6a6932..63b7ab6 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Query.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Query.java
@@ -14,8 +14,6 @@
package com.google.gerrit.sshd.commands;
-import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
-
import com.google.gerrit.server.query.change.QueryProcessor;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
@@ -26,8 +24,7 @@
import java.util.List;
-@CommandMetaData(name = "query", description = "Query the change database",
- runsAt = MASTER_OR_SLAVE)
+@CommandMetaData(name = "query", description = "Query the change database")
class Query extends SshCommand {
@Inject
private QueryProcessor processor;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
index f85b1b3..7753961 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
@@ -105,6 +105,9 @@
@Option(name = "--restore", usage = "restore the specified abandoned change(s)")
private boolean restoreChange;
+ @Option(name = "--rebase", usage = "rebase the specified change(s)")
+ private boolean rebaseChange;
+
@Option(name = "--submit", aliases = "-s", usage = "submit the specified patch set(s)")
private boolean submitChange;
@@ -154,6 +157,9 @@
if (deleteDraftPatchSet) {
throw error("abandon and delete actions are mutually exclusive");
}
+ if (rebaseChange) {
+ throw error("abandon and rebase actions are mutually exclusive");
+ }
}
if (publishPatchSet) {
if (restoreChange) {
@@ -185,6 +191,17 @@
if (changeComment != null) {
throw error("json and message are mutually exclusive");
}
+ if (rebaseChange) {
+ throw error("json and rebase actions are mutually exclusive");
+ }
+ }
+ if (rebaseChange) {
+ if (deleteDraftPatchSet) {
+ throw error("rebase and delete actions are mutually exclusive");
+ }
+ if (submitChange) {
+ throw error("rebase and submit actions are mutually exclusive");
+ }
}
if (deleteDraftPatchSet && submitChange) {
throw error("delete and submit actions are mutually exclusive");
@@ -285,6 +302,10 @@
applyReview(patchSet, review);
}
+ if (rebaseChange){
+ revisionApi(patchSet).rebase();
+ }
+
if (submitChange) {
revisionApi(patchSet).submit();
}
diff --git a/lib/gwt/BUCK b/lib/gwt/BUCK
index 3bf514d..3e9908b 100644
--- a/lib/gwt/BUCK
+++ b/lib/gwt/BUCK
@@ -31,7 +31,6 @@
license = 'Apache2.0',
deps = [
':dev',
- ':legacy-jetty-servlet-aggregate',
],
attach_source = False,
)
@@ -72,22 +71,3 @@
license = 'Apache2.0',
visibility = [],
)
-
-maven_jar(
- name = 'legacy-jetty-servlet-aggregate',
- id = 'org.eclipse.jetty.aggregate:jetty-servlet:8.1.12.v20130726',
- sha1 = '4d0f0cb6e5a54de01be46717a7ab48d0b45dcadd',
- license = 'Apache2.0',
- attach_source = False,
- deps = [':legacy-jetty-servlets'],
- visibility = [],
-)
-
-maven_jar(
- name = 'legacy-jetty-servlets',
- id = 'org.eclipse.jetty:jetty-servlets:8.1.12.v20130726',
- sha1 = '4ebc6894b899fee0c3597697d11f255ce9214bbf',
- license = 'Apache2.0',
- attach_source = False,
- visibility = [],
-)
diff --git a/lib/jetty/BUCK b/lib/jetty/BUCK
index 13e9774..2e3d84d 100644
--- a/lib/jetty/BUCK
+++ b/lib/jetty/BUCK
@@ -39,6 +39,18 @@
)
maven_jar(
+ name = 'servlets',
+ id = 'org.eclipse.jetty:jetty-servlets:' + VERSION,
+ sha1 = '36053479af8213a14d320845e5b5b1b595778f74',
+ license = 'Apache2.0',
+ exclude = EXCLUDE,
+ visibility = [
+ '//tools/eclipse:classpath',
+ '//gerrit-gwtdebug:gwtdebug',
+ ],
+)
+
+maven_jar(
name = 'xml',
id = 'org.eclipse.jetty:jetty-xml:' + VERSION,
sha1 = '0d589789eb98d31160d11413b6323b9ea4569046',
diff --git a/lib/local.defs b/lib/local.defs
new file mode 100644
index 0000000..6eec581
--- /dev/null
+++ b/lib/local.defs
@@ -0,0 +1,33 @@
+def local_jar(
+ name,
+ jar,
+ src = None,
+ deps = [],
+ visibility = ['PUBLIC']):
+ binjar = name + '.jar'
+ srcjar = name + '-src.jar'
+ genrule(
+ name = '%s__local_bin' % name,
+ cmd = 'ln -s %s $OUT' % jar,
+ out = binjar)
+ if src:
+ genrule(
+ name = '%s__local_src' % name,
+ cmd = 'ln -s %s $OUT' % src,
+ out = srcjar)
+ prebuilt_jar(
+ name = '%s_src' % name,
+ binary_jar = ':%s__local_src' % name,
+ visibility = visibility,
+ )
+ else:
+ srcjar = None
+
+ prebuilt_jar(
+ name = name,
+ deps = deps,
+ binary_jar = ':%s__local_bin' % name,
+ source_jar = ':%s__local_src' % name if srcjar else None,
+ visibility = visibility,
+ )
+
diff --git a/lib/maven.defs b/lib/maven.defs
index 5f4006f..f11bd3c 100644
--- a/lib/maven.defs
+++ b/lib/maven.defs
@@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+include_defs('//lib/local.defs')
+
ATLASSIAN = 'ATLASSIAN:'
GERRIT = 'GERRIT:'
GERRIT_API = 'GERRIT_API:'
@@ -40,7 +42,8 @@
sha1 = '', bin_sha1 = '', src_sha1 = '',
repository = MAVEN_CENTRAL,
attach_source = True,
- visibility = ['PUBLIC']):
+ visibility = ['PUBLIC'],
+ local_license = False):
from os import path
parts = id.split(':')
@@ -89,7 +92,10 @@
cmd = ' '.join(cmd),
out = binjar,
)
- license = ['//lib:LICENSE-' + license]
+ license = ':LICENSE-' + license
+ if not local_license:
+ license = '//lib' + license
+ license = [license]
if src_sha1 or attach_source:
cmd = ['$(exe //tools:download_file)', '-o', '$OUT', '-u', srcurl]
@@ -135,35 +141,3 @@
visibility = visibility,
)
-def local_jar(
- name,
- jar,
- src = None,
- deps = [],
- visibility = ['PUBLIC']):
- binjar = name + '.jar'
- srcjar = name + '-src.jar'
- genrule(
- name = '%s__local_bin' % name,
- cmd = 'ln -s %s $OUT' % jar,
- out = binjar)
- if src:
- genrule(
- name = '%s__local_src' % name,
- cmd = 'ln -s %s $OUT' % src,
- out = srcjar)
- prebuilt_jar(
- name = '%s_src' % name,
- binary_jar = ':%s__local_src' % name,
- visibility = visibility,
- )
- else:
- srcjar = None
-
- prebuilt_jar(
- name = name,
- deps = deps,
- binary_jar = ':%s__local_bin' % name,
- source_jar = ':%s__local_src' % name if srcjar else None,
- visibility = visibility,
- )
diff --git a/plugins/cookbook-plugin b/plugins/cookbook-plugin
index 42bf649..58088ee 160000
--- a/plugins/cookbook-plugin
+++ b/plugins/cookbook-plugin
@@ -1 +1 @@
-Subproject commit 42bf6496536c6025566b6ababfe51578d80ea144
+Subproject commit 58088ee235a8c8aa6d6b7851844a1d2fc6d850bf
diff --git a/plugins/replication b/plugins/replication
index 2fb0ec9..319c590 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit 2fb0ec996c1211f7a7df1da4aff85954ad59d95c
+Subproject commit 319c5905d60bb13f3a62196f05a959ea016ac9b8
diff --git a/tools/default.defs b/tools/default.defs
index 4ebb4e4..2239026 100644
--- a/tools/default.defs
+++ b/tools/default.defs
@@ -15,6 +15,8 @@
# Rule definitions loaded by default into every BUCK file.
include_defs('//tools/gwt-constants.defs')
+include_defs('//tools/java_doc.defs')
+include_defs('//tools/java_sources.defs')
import copy
def genantlr(
@@ -144,52 +146,3 @@
] + static_jars,
visibility = visibility,
)
-
-def java_sources(
- name,
- srcs,
- visibility = []
- ):
- java_library(
- name = name,
- resources = srcs,
- visibility = visibility,
- )
-
-def java_doc(
- name,
- title,
- pkg,
- paths,
- srcs = [],
- deps = [],
- visibility = [],
- do_it_wrong = False,
- ):
- if do_it_wrong:
- sourcepath = paths
- else:
- sourcepath = ['$SRCDIR/' + n for n in paths]
- genrule(
- name = name,
- cmd = ' '.join([
- 'while ! test -f .buckconfig; do cd ..; done;',
- 'javadoc',
- '-quiet',
- '-protected',
- '-encoding UTF-8',
- '-charset UTF-8',
- '-notimestamp',
- '-windowtitle "' + title + '"',
- '-link http://docs.oracle.com/javase/7/docs/api',
- '-subpackages ' + pkg,
- '-sourcepath ',
- ':'.join(sourcepath),
- ' -classpath ',
- ':'.join(['$(location %s)' % n for n in deps]),
- '-d $TMP',
- ]) + ';jar cf $OUT -C $TMP .',
- srcs = srcs,
- out = name + '.jar',
- visibility = visibility,
-)
diff --git a/tools/eclipse/BUCK b/tools/eclipse/BUCK
index 2cc66a0..598f727 100644
--- a/tools/eclipse/BUCK
+++ b/tools/eclipse/BUCK
@@ -19,6 +19,8 @@
'//lib/bouncycastle:bcprov',
'//lib/bouncycastle:bcpg',
'//lib/bouncycastle:bcpkix',
+ '//lib/gwt:codeserver',
+ '//lib/jetty:servlets',
'//lib/jetty:webapp',
'//lib/prolog:compiler_lib',
'//Documentation:index_lib',
diff --git a/tools/eclipse/gerrit_gwt_codeserver.launch b/tools/eclipse/gerrit_gwt_sdm_debug.launch
similarity index 69%
rename from tools/eclipse/gerrit_gwt_codeserver.launch
rename to tools/eclipse/gerrit_gwt_sdm_debug.launch
index e7f5206..cd5313a 100644
--- a/tools/eclipse/gerrit_gwt_codeserver.launch
+++ b/tools/eclipse/gerrit_gwt_sdm_debug.launch
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
-<listEntry value="/gerrit"/>
+<listEntry value="/gerrit/gerrit-gwtdebug/src/main/java/com/google/gerrit/gwtdebug/GerritSDMLauncher.java"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
-<listEntry value="4"/>
+<listEntry value="1"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
@@ -12,14 +12,12 @@
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7" javaProject="gerrit" path="1" type="4"/> "/>
-<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/gerrit/buck-out/gen/lib/gwt/legacy-jetty-servlet-aggregate/jetty-servlet-8.1.12.v20130726.jar" path="3" type="2"/> "/>
-<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/gerrit/buck-out/gen/lib/gwt/codeserver/gwt-codeserver-2.6.1.jar" path="3" type="2"/> "/>
-<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/gerrit/buck-out/gen/lib/gwt/legacy-jetty-servlets/jetty-servlets-8.1.12.v20130726.jar" path="3" type="2"/> "/>
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath"> <memento exportedEntriesOnly="false" project="gerrit"/> </runtimeClasspathEntry> "/>
+<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/gerrit/buck-out/gen/lib/gwt/codeserver/gwt-codeserver-2.6.1.jar" path="3" type="2"/> "/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
-<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.codeserver.CodeServer"/>
-<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-noprecompile -src ${resource_loc:/gerrit} -workDir ${resource_loc:/gerrit}/buck-out/gen/gerrit-gwtui com.google.gerrit.GerritGwtUI"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gerrit.gwtdebug.GerritSDMLauncher"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-noprecompile -src ${resource_loc:/gerrit} -workDir ${resource_loc:/gerrit}/buck-out/gen/gerrit-gwtui com.google.gerrit.GerritGwtUI -- --console-log --show-stack-trace -d ${resource_loc:/gerrit}/../gerrit_testsite"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="gerrit"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx1024M -XX:MaxPermSize=256M"/>
</launchConfiguration>
diff --git a/tools/eclipse/project.py b/tools/eclipse/project.py
index 2008316..133e8b4 100755
--- a/tools/eclipse/project.py
+++ b/tools/eclipse/project.py
@@ -138,7 +138,7 @@
if path.exists(p):
classpathentry('src', p, out=o)
- for libs in [lib, gwt_lib]:
+ for libs in [gwt_lib, lib]:
for j in sorted(libs):
s = None
if j.endswith('.jar'):
diff --git a/tools/java_doc.defs b/tools/java_doc.defs
new file mode 100644
index 0000000..d117bda
--- /dev/null
+++ b/tools/java_doc.defs
@@ -0,0 +1,37 @@
+def java_doc(
+ name,
+ title,
+ pkg,
+ paths,
+ srcs = [],
+ deps = [],
+ visibility = [],
+ do_it_wrong = False,
+ ):
+ if do_it_wrong:
+ sourcepath = paths
+ else:
+ sourcepath = ['$SRCDIR/' + n for n in paths]
+ genrule(
+ name = name,
+ cmd = ' '.join([
+ 'while ! test -f .buckconfig; do cd ..; done;',
+ 'javadoc',
+ '-quiet',
+ '-protected',
+ '-encoding UTF-8',
+ '-charset UTF-8',
+ '-notimestamp',
+ '-windowtitle "' + title + '"',
+ '-link http://docs.oracle.com/javase/7/docs/api',
+ '-subpackages ' + pkg,
+ '-sourcepath ',
+ ':'.join(sourcepath),
+ ' -classpath ',
+ ':'.join(['$(location %s)' % n for n in deps]),
+ '-d $TMP',
+ ]) + ';jar cf $OUT -C $TMP .',
+ srcs = srcs,
+ out = name + '.jar',
+ visibility = visibility,
+)
diff --git a/tools/java_sources.defs b/tools/java_sources.defs
new file mode 100644
index 0000000..0b3974e
--- /dev/null
+++ b/tools/java_sources.defs
@@ -0,0 +1,10 @@
+def java_sources(
+ name,
+ srcs,
+ visibility = []
+ ):
+ java_library(
+ name = name,
+ resources = srcs,
+ visibility = visibility,
+ )