Merge branch 'stable'
* stable: (30 commits) Clarify the upgrade instructions Draft release notes for 2.1.5 documentation: Document why hook --change-url might be missing documentation: Fix rendering errors in gerrit.config Allow ; and & to seperate parameters in gitweb Include a quick summary of the size of a change in email Display the size of a patch (lines added/removed) Fix clearing of topic during replace Fix inherited Read Access +2 not inheriting Add some basic RefControl tests for delegated ownership Fix branch owner adding exclusive ACL Optimize RegexFilePredicate for common matches Correct copyright headers to AOSP Don't expose /COMMIT_MSG as a modified file in ChangeData Don't enable dk.brics.automaton's optional syntax Fix NPE while matching file:^ pattern on deleted files Fixed unused imports in OutgoingEmail Don't show /COMMIT_MSG in change emails Fix NPE during merge failure on new branch Honor user's syntax coloring preference in unified view ... Conflicts: gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchScriptBuilder.java gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java gerrit-server/src/main/java/com/google/gerrit/server/mail/OutgoingEmail.java gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCacheImpl.java gerrit-server/src/main/java/com/google/gerrit/server/patch/Text.java Change-Id: I80bfdb55872fa4bdf4f751c337b609e7ec809605
This commit is contained in:
@@ -62,7 +62,7 @@ EXAMPLES
|
|||||||
Create a new user account called `watcher`:
|
Create a new user account called `watcher`:
|
||||||
|
|
||||||
====
|
====
|
||||||
$ cat ~/.ssh/id_watcher.pub | ssh -p 29418 review.example.com gerrit create-user --ssh-key - watcher
|
$ cat ~/.ssh/id_watcher.pub | ssh -p 29418 review.example.com gerrit create-account --ssh-key - watcher
|
||||||
====
|
====
|
||||||
|
|
||||||
GERRIT
|
GERRIT
|
||||||
|
@@ -735,11 +735,11 @@ Multiple schemes are supported:
|
|||||||
+
|
+
|
||||||
* `http`
|
* `http`
|
||||||
+
|
+
|
||||||
HTTP download is allowed.
|
Authenticated HTTP download is allowed.
|
||||||
+
|
+
|
||||||
* `ssh`
|
* `ssh`
|
||||||
+
|
+
|
||||||
SSH download is allowed.
|
Authenticated SSH download is allowed.
|
||||||
+
|
+
|
||||||
* `anon_http`
|
* `anon_http`
|
||||||
+
|
+
|
||||||
@@ -747,18 +747,20 @@ Anonymous HTTP download is allowed.
|
|||||||
+
|
+
|
||||||
* `anon_git`
|
* `anon_git`
|
||||||
+
|
+
|
||||||
Anonymous Git download is allowed.
|
Anonymous Git download is allowed. This is not default, it is also
|
||||||
This is not default, it is also necessary to fill gerrit.canonicalGitUrl variable.
|
necessary to set <<gerrit.canonicalGitUrl,gerrit.canonicalGitUrl>>
|
||||||
|
variable.
|
||||||
+
|
+
|
||||||
* `repo_download`
|
* `repo_download`
|
||||||
+
|
+
|
||||||
Gerrit advertises patch set downloads with the `repo download` command,
|
Gerrit advertises patch set downloads with the `repo download`
|
||||||
assuming that all projects managed by this instance are generally worked
|
command, assuming that all projects managed by this instance are
|
||||||
on with the repo multi-repository tool.
|
generally worked on with the repo multi-repository tool. This is
|
||||||
This is not default, as not all instances will deploy repo.
|
not default, as not all instances will deploy repo.
|
||||||
|
|
||||||
+
|
+
|
||||||
If download.scheme is not specified, SSH, HTTP and Anonymous HTTP downloads are allowed.
|
If download.scheme is not specified, SSH, HTTP and Anonymous HTTP
|
||||||
|
downloads are allowed.
|
||||||
|
|
||||||
[[gerrit]]Section gerrit
|
[[gerrit]]Section gerrit
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@@ -78,6 +78,15 @@ the values of hooks.patchsetCreatedHook, hooks.commentAddedHook,
|
|||||||
hooks.changeMergedHook and hooks.changeAbandonedHook for the
|
hooks.changeMergedHook and hooks.changeAbandonedHook for the
|
||||||
filenames for the hooks.
|
filenames for the hooks.
|
||||||
|
|
||||||
|
Missing Change URLs
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
If link:config-gerrit.html#gerrit.canonicalWebUrl[gerrit.canonicalWebUrl]
|
||||||
|
is not set in `gerrit.config` the `\--change-url` flag may not be
|
||||||
|
passed to all hooks. Hooks started out of an SSH context (for example
|
||||||
|
the patchset-created hook) don't know the server's web URL, unless
|
||||||
|
this variable is configured.
|
||||||
|
|
||||||
See Also
|
See Also
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
173
ReleaseNotes/ReleaseNotes-2.1.5.txt
Normal file
173
ReleaseNotes/ReleaseNotes-2.1.5.txt
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
Release notes for Gerrit 2.1.5
|
||||||
|
==============================
|
||||||
|
|
||||||
|
Gerrit 2.1.5 is now available:
|
||||||
|
|
||||||
|
link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.1.5.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.1.5.war]
|
||||||
|
|
||||||
|
This is primarly a bug fix release to 2.1.4, but some additional
|
||||||
|
new features were included so its named 2.1.5 rather than 2.1.4.1.
|
||||||
|
|
||||||
|
Upgrade Instructions
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
If upgrading from version 2.1.4, simply replace the WAR file in
|
||||||
|
`'site_path'/bin/gerrit.war` and restart Gerrit.
|
||||||
|
|
||||||
|
If upgrading from version 2.1.3 or earlier, stop Gerrit, use
|
||||||
|
`java -jar gerrit.war init -d 'site_path'` to upgrade the schema,
|
||||||
|
and restart Gerrit.
|
||||||
|
|
||||||
|
New Features
|
||||||
|
------------
|
||||||
|
|
||||||
|
Web UI
|
||||||
|
~~~~~~
|
||||||
|
* issue 361 Enable commenting on commit messages
|
||||||
|
+
|
||||||
|
The commit message of a change can now be commented on inline, and
|
||||||
|
even compared between patch sets, just like any other file contents.
|
||||||
|
The message is presented as a magical file called 'Commit Message',
|
||||||
|
in the first row of every change.
|
||||||
|
|
||||||
|
* issue 312 Implement 'Restore Change' to undo 'Abandon Change'
|
||||||
|
+
|
||||||
|
Any user who can abandon a change (the change owner, project owner,
|
||||||
|
or any site administrator) can now restore the change from Abandoned
|
||||||
|
status back to Review in Progress.
|
||||||
|
|
||||||
|
* issue 583 Enable/disable download protocols
|
||||||
|
+
|
||||||
|
The new download section in `gerrit.config` controls how the patch
|
||||||
|
set download links are presented in the web UI. Administrators
|
||||||
|
can use this section to enable `repo download`, `git://`, or to
|
||||||
|
disable `http://` style URLs. This section replaces the older
|
||||||
|
repo.showDownloadCommand.
|
||||||
|
|
||||||
|
* issue 499 Display the size of a patch (lines added/removed)
|
||||||
|
+
|
||||||
|
A 'diffstat' is shown for each file, summarizing the size of the
|
||||||
|
change on that file in terms of number of lines added or deleted.
|
||||||
|
|
||||||
|
Email Notifications
|
||||||
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
* issue 452 Include a quick summary of the size of a change in email
|
||||||
|
+
|
||||||
|
After the file listing, a summary totaling the number of files
|
||||||
|
changed, lines added, and lines removed is displayed. This may
|
||||||
|
help reviewers to get a quick estimation on the time required for
|
||||||
|
them to review the change.
|
||||||
|
|
||||||
|
Bug Fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
Web UI
|
||||||
|
~~~~~~
|
||||||
|
* issue 639 Fix keyboard shortcuts under Chrome/Safari
|
||||||
|
+
|
||||||
|
Keyboard shortcuts didn't work properly on modern WebKit browsers
|
||||||
|
like Chrome and Safari. We kept trying to blame this on the browser,
|
||||||
|
but it was Gerrit Code Review at fault. The UI was using the wrong
|
||||||
|
listener type to receive keyboard events in comment editors. Fixed.
|
||||||
|
|
||||||
|
* Make 'u' go up to the last change listing
|
||||||
|
+
|
||||||
|
Previously the 'u' key on a change page was hardcoded to take
|
||||||
|
the user to their own dashboard. However, if they arrived at the
|
||||||
|
change through a query such as `is:starred status:open`, this was
|
||||||
|
quite annoying, as the query had to be started over again to move
|
||||||
|
to the next matching change. Now the 'u' key goes back to the
|
||||||
|
query results.
|
||||||
|
|
||||||
|
* issue 671 Honor user's syntax coloring preference in unified view
|
||||||
|
+
|
||||||
|
The user's syntax coloring preference was always ignored in the
|
||||||
|
unified view, even though the side-by-side view honored it. Fixed.
|
||||||
|
|
||||||
|
* issue 651 Display stars in dependency tables
|
||||||
|
+
|
||||||
|
The 'Depends On' and 'Needed By' tables on a change page did not
|
||||||
|
show the current user's star settings, even though the star icon
|
||||||
|
is present and will toggle the user's starred flag for that change.
|
||||||
|
Fixed.
|
||||||
|
|
||||||
|
Access Control
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
* issue 672 Fix branch owner adding exclusive ACL
|
||||||
|
+
|
||||||
|
Branch owners could not add exclusive ACLs within their branch
|
||||||
|
namespace. This was caused by the server trying to match the leading
|
||||||
|
`-` entered by the branch administrator against patterns that did
|
||||||
|
not contain `-`, and therefore always failed. Fixed by removing
|
||||||
|
the magical `-` from the proposed new specification before testing
|
||||||
|
the access rights.
|
||||||
|
|
||||||
|
* '@' in ref specs shouldn't be magical.
|
||||||
|
+
|
||||||
|
The dk.brics.automaton package that is used to handle regular
|
||||||
|
expressions on branch access patterns supports '@' to mean
|
||||||
|
"any string". We don't want that behavior. Fixed by disabling
|
||||||
|
the optional features of dk.brics.automaton, thereby making '@'
|
||||||
|
mean a literal '@' sign as expected.
|
||||||
|
|
||||||
|
* issue 668 Fix inherited Read Access +2 not inheriting
|
||||||
|
+
|
||||||
|
Upload access (aka Read +2) did not inherit properly from the parent
|
||||||
|
project (e.g. '\-- All Projects \--') if there was any branch level
|
||||||
|
Read access control within the local project. This was a coding
|
||||||
|
bug which failed to consider the project inheritance if any branch
|
||||||
|
(not just the one being uploaded to) denied upload access.
|
||||||
|
|
||||||
|
Misc.
|
||||||
|
~~~~~
|
||||||
|
* issue 641 Don't pass null arguments to hooks
|
||||||
|
+
|
||||||
|
Some hooks crashed inside of the server during invocation because the
|
||||||
|
`gerrit.canonicalWebUrl` variable wasn't configured, and the hook
|
||||||
|
was started out of an SSH or background thread context, so the URL
|
||||||
|
couldn't be assumed from the current request. The bug was worked
|
||||||
|
around by not passing the `\--change-url` flag in these cases.
|
||||||
|
Administrators whose hooks always need the flag should configure
|
||||||
|
`gerrit.canonicalWebUrl`.
|
||||||
|
|
||||||
|
* issue 652 Fix NPE during merge failure on new branch
|
||||||
|
+
|
||||||
|
Submitting a change with a missing dependency to a new branch
|
||||||
|
resulted in a NullPointerException in the server, because the server
|
||||||
|
tried to create the branch anyway, even though there was no commit
|
||||||
|
ready because one or more dependencies were missing. Fixed.
|
||||||
|
|
||||||
|
* Fix NPE while matching `file:^` pattern on deleted files
|
||||||
|
+
|
||||||
|
Sending email notifications crashed with NullPointerException if the
|
||||||
|
change contained a deleted file and one or more users had a project
|
||||||
|
watch on that project using a `file:^` pattern in their filter.
|
||||||
|
Fixed.
|
||||||
|
|
||||||
|
* issue 658 Allow to use refspec shortcuts for push replication
|
||||||
|
+
|
||||||
|
A push refspec of `refs/heads/\*` in replication.config is now
|
||||||
|
supported as a shorthand notation for `refs/heads/\*:refs/heads/\*`.
|
||||||
|
|
||||||
|
* issue 676 Fix clearing of topic during replace
|
||||||
|
+
|
||||||
|
The topic was cleared if a replacement patch set was uploaded without
|
||||||
|
the topic name. The topic is now left as-is during replacement
|
||||||
|
if no new topic was supplied. If a new topic is supplied, it is
|
||||||
|
changed to match the new topic given.
|
||||||
|
|
||||||
|
* Allow ; and & to seperate parameters in gitweb
|
||||||
|
+
|
||||||
|
gitweb.cgi accepts either ';' or '&' between parameters, but
|
||||||
|
Gerrit Code Review was only accepting the ';' syntax. Fixed
|
||||||
|
to support both.
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
* Fixed example for gerrit create-account.
|
||||||
|
* gerrit.sh: Correct /etc/default path in error message
|
||||||
|
|
||||||
|
Version
|
||||||
|
-------
|
||||||
|
|
||||||
|
2765ff9e5f821100e9ca671f4d502b5c938457a5
|
@@ -4,6 +4,7 @@ Gerrit Code Review - Release Notes
|
|||||||
[[2_1]]
|
[[2_1]]
|
||||||
Version 2.1.x
|
Version 2.1.x
|
||||||
-------------
|
-------------
|
||||||
|
* link:ReleaseNotes-2.1.5.html[2.1.5]
|
||||||
* link:ReleaseNotes-2.1.4.html[2.1.4]
|
* link:ReleaseNotes-2.1.4.html[2.1.4]
|
||||||
* link:ReleaseNotes-2.1.3.html[2.1.3]
|
* link:ReleaseNotes-2.1.3.html[2.1.3]
|
||||||
* link:ReleaseNotes-2.1.2.5.html[2.1.2.5]
|
* link:ReleaseNotes-2.1.2.5.html[2.1.2.5]
|
||||||
|
@@ -16,6 +16,7 @@ package com.google.gerrit.client;
|
|||||||
|
|
||||||
import com.google.gerrit.client.auth.openid.OpenIdSignInDialog;
|
import com.google.gerrit.client.auth.openid.OpenIdSignInDialog;
|
||||||
import com.google.gerrit.client.auth.userpass.UserPassSignInDialog;
|
import com.google.gerrit.client.auth.userpass.UserPassSignInDialog;
|
||||||
|
import com.google.gerrit.client.changes.ChangeListScreen;
|
||||||
import com.google.gerrit.client.rpc.GerritCallback;
|
import com.google.gerrit.client.rpc.GerritCallback;
|
||||||
import com.google.gerrit.client.ui.LinkMenuBar;
|
import com.google.gerrit.client.ui.LinkMenuBar;
|
||||||
import com.google.gerrit.client.ui.LinkMenuItem;
|
import com.google.gerrit.client.ui.LinkMenuItem;
|
||||||
@@ -81,6 +82,8 @@ public class Gerrit implements EntryPoint {
|
|||||||
private static final Dispatcher dispatcher = new Dispatcher();
|
private static final Dispatcher dispatcher = new Dispatcher();
|
||||||
private static ViewSite<Screen> body;
|
private static ViewSite<Screen> body;
|
||||||
|
|
||||||
|
private static String lastChangeListToken;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
SYSTEM_SVC = GWT.create(SystemInfoService.class);
|
SYSTEM_SVC = GWT.create(SystemInfoService.class);
|
||||||
JsonUtil.bind(SYSTEM_SVC, "rpc/SystemInfoService");
|
JsonUtil.bind(SYSTEM_SVC, "rpc/SystemInfoService");
|
||||||
@@ -91,6 +94,16 @@ public class Gerrit implements EntryPoint {
|
|||||||
Window.Location.reload();
|
Window.Location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void displayLastChangeList() {
|
||||||
|
if (lastChangeListToken != null) {
|
||||||
|
display(lastChangeListToken);
|
||||||
|
} else if (isSignedIn()) {
|
||||||
|
display(PageLinks.MINE);
|
||||||
|
} else {
|
||||||
|
display(PageLinks.toChangeQuery("status:open"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the screen at the given location, displaying when ready.
|
* Load the screen at the given location, displaying when ready.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -365,6 +378,11 @@ public class Gerrit implements EntryPoint {
|
|||||||
dispatchHistoryHooks(token);
|
dispatchHistoryHooks(token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (view instanceof ChangeListScreen) {
|
||||||
|
lastChangeListToken = token;
|
||||||
|
}
|
||||||
|
|
||||||
super.onShowView(view);
|
super.onShowView(view);
|
||||||
view.onShowView();
|
view.onShowView();
|
||||||
}
|
}
|
||||||
|
@@ -145,6 +145,7 @@ public interface GerritCss extends CssResource {
|
|||||||
String patchSetLink();
|
String patchSetLink();
|
||||||
String patchSetRevision();
|
String patchSetRevision();
|
||||||
String patchSetUserIdentity();
|
String patchSetUserIdentity();
|
||||||
|
String patchSizeCell();
|
||||||
String permalink();
|
String permalink();
|
||||||
String posscore();
|
String posscore();
|
||||||
String projectAdminApprovalCategoryRangeLine();
|
String projectAdminApprovalCategoryRangeLine();
|
||||||
|
@@ -109,7 +109,7 @@ class SshPanel extends Composite {
|
|||||||
addTxt = new NpTextArea();
|
addTxt = new NpTextArea();
|
||||||
addTxt.setVisibleLines(12);
|
addTxt.setVisibleLines(12);
|
||||||
addTxt.setCharacterWidth(80);
|
addTxt.setCharacterWidth(80);
|
||||||
DOM.setElementPropertyBoolean(addTxt.getElement(), "spellcheck", false);
|
addTxt.setSpellCheck(false);
|
||||||
addKeyBlock.add(addTxt);
|
addKeyBlock.add(addTxt);
|
||||||
|
|
||||||
final HorizontalPanel buttons = new HorizontalPanel();
|
final HorizontalPanel buttons = new HorizontalPanel();
|
||||||
|
@@ -1,4 +1,16 @@
|
|||||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
// Copyright (C) 2010 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.admin;
|
package com.google.gerrit.client.admin;
|
||||||
|
|
||||||
|
@@ -64,7 +64,7 @@ public class AbandonChangeDialog extends AutoCenterDialogBox implements CloseHan
|
|||||||
message = new NpTextArea();
|
message = new NpTextArea();
|
||||||
message.setCharacterWidth(60);
|
message.setCharacterWidth(60);
|
||||||
message.setVisibleLines(10);
|
message.setVisibleLines(10);
|
||||||
DOM.setElementPropertyBoolean(message.getElement(), "spellcheck", true);
|
message.setSpellCheck(true);
|
||||||
mwrap.add(message);
|
mwrap.add(message);
|
||||||
|
|
||||||
final FlowPanel buttonPanel = new FlowPanel();
|
final FlowPanel buttonPanel = new FlowPanel();
|
||||||
|
@@ -25,7 +25,7 @@ import com.google.gerrit.common.data.AccountInfo;
|
|||||||
import com.google.gerrit.reviewdb.Account;
|
import com.google.gerrit.reviewdb.Account;
|
||||||
|
|
||||||
|
|
||||||
public class AccountDashboardScreen extends Screen {
|
public class AccountDashboardScreen extends Screen implements ChangeListScreen {
|
||||||
private final Account.Id ownerId;
|
private final Account.Id ownerId;
|
||||||
private ChangeTable table;
|
private ChangeTable table;
|
||||||
private ChangeTable.Section byOwner;
|
private ChangeTable.Section byOwner;
|
||||||
|
@@ -46,7 +46,7 @@ public interface ChangeConstants extends Constants {
|
|||||||
String changeTableStar();
|
String changeTableStar();
|
||||||
String changeTablePagePrev();
|
String changeTablePagePrev();
|
||||||
String changeTablePageNext();
|
String changeTablePageNext();
|
||||||
String upToDashboard();
|
String upToChangeList();
|
||||||
String expandCollapseDependencies();
|
String expandCollapseDependencies();
|
||||||
String previousPatchSet();
|
String previousPatchSet();
|
||||||
String nextPatchSet();
|
String nextPatchSet();
|
||||||
@@ -54,11 +54,13 @@ public interface ChangeConstants extends Constants {
|
|||||||
|
|
||||||
String patchTableColumnName();
|
String patchTableColumnName();
|
||||||
String patchTableColumnComments();
|
String patchTableColumnComments();
|
||||||
|
String patchTableColumnSize();
|
||||||
String patchTableColumnDiff();
|
String patchTableColumnDiff();
|
||||||
String patchTableDiffSideBySide();
|
String patchTableDiffSideBySide();
|
||||||
String patchTableDiffUnified();
|
String patchTableDiffUnified();
|
||||||
String patchTableDownloadPreImage();
|
String patchTableDownloadPreImage();
|
||||||
String patchTableDownloadPostImage();
|
String patchTableDownloadPostImage();
|
||||||
|
String commitMessage();
|
||||||
|
|
||||||
String patchTablePrev();
|
String patchTablePrev();
|
||||||
String patchTableNext();
|
String patchTableNext();
|
||||||
|
@@ -26,7 +26,7 @@ changeTableOpen = Open change
|
|||||||
changeTableStar = Star (or unstar) change
|
changeTableStar = Star (or unstar) change
|
||||||
changeTablePagePrev = Previous page of changes
|
changeTablePagePrev = Previous page of changes
|
||||||
changeTablePageNext = Next page of changes
|
changeTablePageNext = Next page of changes
|
||||||
upToDashboard = Up to dashboard
|
upToChangeList = Up to change list
|
||||||
expandCollapseDependencies = Expands / Collapses dependencies section
|
expandCollapseDependencies = Expands / Collapses dependencies section
|
||||||
previousPatchSet = Previous patch set
|
previousPatchSet = Previous patch set
|
||||||
nextPatchSet = Next patch set
|
nextPatchSet = Next patch set
|
||||||
@@ -34,11 +34,13 @@ keyPublishComments = Review and publish comments
|
|||||||
|
|
||||||
patchTableColumnName = File Path
|
patchTableColumnName = File Path
|
||||||
patchTableColumnComments = Comments
|
patchTableColumnComments = Comments
|
||||||
|
patchTableColumnSize = Size
|
||||||
patchTableColumnDiff = Diff
|
patchTableColumnDiff = Diff
|
||||||
patchTableDiffSideBySide = Side-by-Side
|
patchTableDiffSideBySide = Side-by-Side
|
||||||
patchTableDiffUnified = Unified
|
patchTableDiffUnified = Unified
|
||||||
patchTableDownloadPreImage = old
|
patchTableDownloadPreImage = old
|
||||||
patchTableDownloadPostImage = new
|
patchTableDownloadPostImage = new
|
||||||
|
commitMessage = Commit Message
|
||||||
|
|
||||||
patchTablePrev = Previous file
|
patchTablePrev = Previous file
|
||||||
patchTableNext = Next file
|
patchTableNext = Next file
|
||||||
|
@@ -0,0 +1,18 @@
|
|||||||
|
// Copyright (C) 2010 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;
|
||||||
|
|
||||||
|
public interface ChangeListScreen {
|
||||||
|
}
|
@@ -31,6 +31,8 @@ public interface ChangeMessages extends Messages {
|
|||||||
|
|
||||||
String patchTableComments(@PluralCount int count);
|
String patchTableComments(@PluralCount int count);
|
||||||
String patchTableDrafts(@PluralCount int count);
|
String patchTableDrafts(@PluralCount int count);
|
||||||
|
String patchTableSize_Modify(int insertions, int deletions);
|
||||||
|
String patchTableSize_Lines(@PluralCount int insertions);
|
||||||
|
|
||||||
String removeReviewer(String fullName);
|
String removeReviewer(String fullName);
|
||||||
String messageWrittenOn(String date);
|
String messageWrittenOn(String date);
|
||||||
|
@@ -12,6 +12,8 @@ submitPatchSet = Submit Patch Set {0}
|
|||||||
|
|
||||||
patchTableComments = {0} comments
|
patchTableComments = {0} comments
|
||||||
patchTableDrafts = {0} drafts
|
patchTableDrafts = {0} drafts
|
||||||
|
patchTableSize_Modify = +{0}, -{1}
|
||||||
|
patchTableSize_Lines = {0} lines
|
||||||
|
|
||||||
removeReviewer = Remove reviewer {0}
|
removeReviewer = Remove reviewer {0}
|
||||||
messageWrittenOn = on {0}
|
messageWrittenOn = on {0}
|
||||||
|
@@ -3,3 +3,6 @@ patchTableComments = {0} comments
|
|||||||
|
|
||||||
patchTableDrafts[one] = 1 draft
|
patchTableDrafts[one] = 1 draft
|
||||||
patchTableDrafts = {0} drafts
|
patchTableDrafts = {0} drafts
|
||||||
|
|
||||||
|
patchTableSize_Lines[one] = 1 line
|
||||||
|
patchTableSize_Lines = {0} lines
|
||||||
|
@@ -22,7 +22,6 @@ import com.google.gerrit.client.ui.ExpandAllCommand;
|
|||||||
import com.google.gerrit.client.ui.LinkMenuBar;
|
import com.google.gerrit.client.ui.LinkMenuBar;
|
||||||
import com.google.gerrit.client.ui.NeedsSignInKeyCommand;
|
import com.google.gerrit.client.ui.NeedsSignInKeyCommand;
|
||||||
import com.google.gerrit.client.ui.Screen;
|
import com.google.gerrit.client.ui.Screen;
|
||||||
import com.google.gerrit.common.PageLinks;
|
|
||||||
import com.google.gerrit.common.data.AccountInfo;
|
import com.google.gerrit.common.data.AccountInfo;
|
||||||
import com.google.gerrit.common.data.AccountInfoCache;
|
import com.google.gerrit.common.data.AccountInfoCache;
|
||||||
import com.google.gerrit.common.data.ChangeDetail;
|
import com.google.gerrit.common.data.ChangeDetail;
|
||||||
@@ -145,7 +144,7 @@ public class ChangeScreen extends Screen {
|
|||||||
|
|
||||||
keysNavigation = new KeyCommandSet(Gerrit.C.sectionNavigation());
|
keysNavigation = new KeyCommandSet(Gerrit.C.sectionNavigation());
|
||||||
keysAction = new KeyCommandSet(Gerrit.C.sectionActions());
|
keysAction = new KeyCommandSet(Gerrit.C.sectionActions());
|
||||||
keysNavigation.add(new DashboardKeyCommand(0, 'u', Util.C.upToDashboard()));
|
keysNavigation.add(new UpToListKeyCommand(0, 'u', Util.C.upToChangeList()));
|
||||||
keysNavigation.add(new ExpandCollapseDependencySectionKeyCommand(0, 'd', Util.C.expandCollapseDependencies()));
|
keysNavigation.add(new ExpandCollapseDependencySectionKeyCommand(0, 'd', Util.C.expandCollapseDependencies()));
|
||||||
|
|
||||||
if (Gerrit.isSignedIn()) {
|
if (Gerrit.isSignedIn()) {
|
||||||
@@ -346,18 +345,14 @@ public class ChangeScreen extends Screen {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DashboardKeyCommand extends KeyCommand {
|
public class UpToListKeyCommand extends KeyCommand {
|
||||||
public DashboardKeyCommand(int mask, char key, String help) {
|
public UpToListKeyCommand(int mask, char key, String help) {
|
||||||
super(mask, key, help);
|
super(mask, key, help);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onKeyPress(final KeyPressEvent event) {
|
public void onKeyPress(final KeyPressEvent event) {
|
||||||
if (Gerrit.isSignedIn()) {
|
Gerrit.displayLastChangeList();
|
||||||
Gerrit.display(PageLinks.MINE);
|
|
||||||
} else {
|
|
||||||
Gerrit.display(PageLinks.toChangeQuery("status:open"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,64 +0,0 @@
|
|||||||
// Copyright (C) 2008 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.rpc.ScreenLoadCallback;
|
|
||||||
import com.google.gerrit.client.ui.AccountScreen;
|
|
||||||
import com.google.gerrit.common.data.SingleListChangeInfo;
|
|
||||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
|
||||||
|
|
||||||
|
|
||||||
public abstract class MineSingleListScreen extends AccountScreen {
|
|
||||||
private final String anchor;
|
|
||||||
private ChangeTable table;
|
|
||||||
private ChangeTable.Section drafts;
|
|
||||||
|
|
||||||
protected MineSingleListScreen(final String historyToken) {
|
|
||||||
anchor = historyToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onInitUI() {
|
|
||||||
super.onInitUI();
|
|
||||||
table = new ChangeTable();
|
|
||||||
drafts = new ChangeTable.Section();
|
|
||||||
|
|
||||||
table.addSection(drafts);
|
|
||||||
table.setSavePointerId(anchor);
|
|
||||||
|
|
||||||
add(table);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void registerKeys() {
|
|
||||||
super.registerKeys();
|
|
||||||
table.setRegisterKeys(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected AsyncCallback<SingleListChangeInfo> loadCallback() {
|
|
||||||
return new ScreenLoadCallback<SingleListChangeInfo>(this) {
|
|
||||||
@Override
|
|
||||||
protected void preDisplay(final SingleListChangeInfo result) {
|
|
||||||
display(result);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private void display(final SingleListChangeInfo result) {
|
|
||||||
table.setAccountInfoCache(result.getAccounts());
|
|
||||||
drafts.display(result.getChanges());
|
|
||||||
table.finishDisplay();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -14,14 +14,12 @@
|
|||||||
|
|
||||||
package com.google.gerrit.client.changes;
|
package com.google.gerrit.client.changes;
|
||||||
|
|
||||||
|
import com.google.gerrit.client.Dispatcher;
|
||||||
import com.google.gerrit.client.FormatUtil;
|
import com.google.gerrit.client.FormatUtil;
|
||||||
import com.google.gerrit.client.Gerrit;
|
import com.google.gerrit.client.Gerrit;
|
||||||
import com.google.gerrit.client.rpc.GerritCallback;
|
import com.google.gerrit.client.rpc.GerritCallback;
|
||||||
import com.google.gerrit.client.ui.AccountDashboardLink;
|
import com.google.gerrit.client.ui.AccountDashboardLink;
|
||||||
import com.google.gerrit.client.ui.ComplexDisclosurePanel;
|
import com.google.gerrit.client.ui.ComplexDisclosurePanel;
|
||||||
import com.google.gerrit.client.ui.PatchLink;
|
|
||||||
import com.google.gerrit.client.ui.PatchLink.SideBySide;
|
|
||||||
import com.google.gerrit.client.ui.PatchLink.Unified;
|
|
||||||
import com.google.gerrit.common.data.ChangeDetail;
|
import com.google.gerrit.common.data.ChangeDetail;
|
||||||
import com.google.gerrit.common.data.GitwebLink;
|
import com.google.gerrit.common.data.GitwebLink;
|
||||||
import com.google.gerrit.common.data.PatchSetDetail;
|
import com.google.gerrit.common.data.PatchSetDetail;
|
||||||
@@ -447,9 +445,8 @@ class PatchSetComplexDisclosurePanel extends ComplexDisclosurePanel implements O
|
|||||||
@Override
|
@Override
|
||||||
public void onClick(ClickEvent event) {
|
public void onClick(ClickEvent event) {
|
||||||
for (Patch p : detail.getPatches()) {
|
for (Patch p : detail.getPatches()) {
|
||||||
SideBySide link = new PatchLink.SideBySide(p.getFileName(), p.getKey(), 0, null, null);
|
|
||||||
Window.open(Window.Location.getPath() + "#"
|
Window.open(Window.Location.getPath() + "#"
|
||||||
+ link.getTargetHistoryToken(), "_blank", null);
|
+ Dispatcher.toPatchSideBySide(p.getKey()), "_blank", null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -461,9 +458,8 @@ class PatchSetComplexDisclosurePanel extends ComplexDisclosurePanel implements O
|
|||||||
@Override
|
@Override
|
||||||
public void onClick(ClickEvent event) {
|
public void onClick(ClickEvent event) {
|
||||||
for (Patch p : detail.getPatches()) {
|
for (Patch p : detail.getPatches()) {
|
||||||
Unified link = new PatchLink.Unified(p.getFileName(), p.getKey(), 0, null, null);
|
|
||||||
Window.open(Window.Location.getPath() + "#"
|
Window.open(Window.Location.getPath() + "#"
|
||||||
+ link.getTargetHistoryToken(), "_blank", null);
|
+ Dispatcher.toPatchUnified(p.getKey()), "_blank", null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -22,6 +22,7 @@ import com.google.gerrit.client.ui.PatchLink;
|
|||||||
import com.google.gerrit.common.data.PatchSetDetail;
|
import com.google.gerrit.common.data.PatchSetDetail;
|
||||||
import com.google.gerrit.reviewdb.Patch;
|
import com.google.gerrit.reviewdb.Patch;
|
||||||
import com.google.gerrit.reviewdb.Patch.Key;
|
import com.google.gerrit.reviewdb.Patch.Key;
|
||||||
|
import com.google.gerrit.reviewdb.Patch.PatchType;
|
||||||
import com.google.gwt.core.client.GWT;
|
import com.google.gwt.core.client.GWT;
|
||||||
import com.google.gwt.event.dom.client.ClickEvent;
|
import com.google.gwt.event.dom.client.ClickEvent;
|
||||||
import com.google.gwt.event.dom.client.ClickHandler;
|
import com.google.gwt.event.dom.client.ClickHandler;
|
||||||
@@ -211,7 +212,7 @@ public class PatchTable extends Composite {
|
|||||||
// Note: use '/' here and not File.pathSeparator since git paths
|
// Note: use '/' here and not File.pathSeparator since git paths
|
||||||
// are always separated by /
|
// are always separated by /
|
||||||
//
|
//
|
||||||
String fileName = patch.getFileName();
|
String fileName = getDisplayFileName(patch);
|
||||||
int s = fileName.lastIndexOf('/');
|
int s = fileName.lastIndexOf('/');
|
||||||
if (s >= 0) {
|
if (s >= 0) {
|
||||||
fileName = fileName.substring(s + 1);
|
fileName = fileName.substring(s + 1);
|
||||||
@@ -219,6 +220,17 @@ public class PatchTable extends Composite {
|
|||||||
return fileName;
|
return fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getDisplayFileName(Patch patch) {
|
||||||
|
return getDisplayFileName(patch.getKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getDisplayFileName(Patch.Key patchKey) {
|
||||||
|
if (Patch.COMMIT_MSG.equals(patchKey.get())) {
|
||||||
|
return Util.C.commitMessage();
|
||||||
|
}
|
||||||
|
return patchKey.get();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the reviewed status for the given patch.
|
* Update the reviewed status for the given patch.
|
||||||
*/
|
*/
|
||||||
@@ -231,7 +243,8 @@ public class PatchTable extends Composite {
|
|||||||
private class MyTable extends NavigationTable<Patch> {
|
private class MyTable extends NavigationTable<Patch> {
|
||||||
private static final int C_PATH = 2;
|
private static final int C_PATH = 2;
|
||||||
private static final int C_DRAFT = 3;
|
private static final int C_DRAFT = 3;
|
||||||
private static final int C_SIDEBYSIDE = 4;
|
private static final int C_SIZE = 4;
|
||||||
|
private static final int C_SIDEBYSIDE = 5;
|
||||||
private int activeRow = -1;
|
private int activeRow = -1;
|
||||||
|
|
||||||
MyTable() {
|
MyTable() {
|
||||||
@@ -326,12 +339,12 @@ public class PatchTable extends Composite {
|
|||||||
Widget nameCol;
|
Widget nameCol;
|
||||||
if (patch.getPatchType() == Patch.PatchType.UNIFIED) {
|
if (patch.getPatchType() == Patch.PatchType.UNIFIED) {
|
||||||
nameCol =
|
nameCol =
|
||||||
new PatchLink.SideBySide(patch.getFileName(), patch.getKey(),
|
new PatchLink.SideBySide(getDisplayFileName(patch), patch.getKey(),
|
||||||
row - 1, detail, PatchTable.this);
|
row - 1, detail, PatchTable.this);
|
||||||
} else {
|
} else {
|
||||||
nameCol =
|
nameCol =
|
||||||
new PatchLink.Unified(patch.getFileName(), patch.getKey(), row - 1,
|
new PatchLink.Unified(getDisplayFileName(patch), patch.getKey(),
|
||||||
detail, PatchTable.this);
|
row - 1, detail, PatchTable.this);
|
||||||
}
|
}
|
||||||
if (patch.getSourceFileName() != null) {
|
if (patch.getSourceFileName() != null) {
|
||||||
final String text;
|
final String text;
|
||||||
@@ -393,6 +406,12 @@ public class PatchTable extends Composite {
|
|||||||
m.append(Util.C.patchTableColumnComments());
|
m.append(Util.C.patchTableColumnComments());
|
||||||
m.closeTd();
|
m.closeTd();
|
||||||
|
|
||||||
|
// "Size"
|
||||||
|
m.openTd();
|
||||||
|
m.setStyleName(Gerrit.RESOURCES.css().dataHeader());
|
||||||
|
m.append(Util.C.patchTableColumnSize());
|
||||||
|
m.closeTd();
|
||||||
|
|
||||||
// "Diff"
|
// "Diff"
|
||||||
m.openTd();
|
m.openTd();
|
||||||
m.setStyleName(Gerrit.RESOURCES.css().dataHeader());
|
m.setStyleName(Gerrit.RESOURCES.css().dataHeader());
|
||||||
@@ -423,7 +442,11 @@ public class PatchTable extends Composite {
|
|||||||
|
|
||||||
m.openTd();
|
m.openTd();
|
||||||
m.setStyleName(Gerrit.RESOURCES.css().changeTypeCell());
|
m.setStyleName(Gerrit.RESOURCES.css().changeTypeCell());
|
||||||
m.append(p.getChangeType().getCode());
|
if (Patch.COMMIT_MSG.equals(p.getFileName())) {
|
||||||
|
m.nbsp();
|
||||||
|
} else {
|
||||||
|
m.append(p.getChangeType().getCode());
|
||||||
|
}
|
||||||
m.closeTd();
|
m.closeTd();
|
||||||
|
|
||||||
m.openTd();
|
m.openTd();
|
||||||
@@ -437,6 +460,12 @@ public class PatchTable extends Composite {
|
|||||||
appendCommentCount(m, p);
|
appendCommentCount(m, p);
|
||||||
m.closeTd();
|
m.closeTd();
|
||||||
|
|
||||||
|
m.openTd();
|
||||||
|
m.addStyleName(Gerrit.RESOURCES.css().dataCell());
|
||||||
|
m.addStyleName(Gerrit.RESOURCES.css().patchSizeCell());
|
||||||
|
appendSize(m, p);
|
||||||
|
m.closeTd();
|
||||||
|
|
||||||
switch (p.getPatchType()) {
|
switch (p.getPatchType()) {
|
||||||
case UNIFIED:
|
case UNIFIED:
|
||||||
openlink(m, 2);
|
openlink(m, 2);
|
||||||
@@ -498,6 +527,29 @@ public class PatchTable extends Composite {
|
|||||||
m.closeTr();
|
m.closeTr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void appendTotals(final SafeHtmlBuilder m, int ins, int dels) {
|
||||||
|
m.openTr();
|
||||||
|
|
||||||
|
m.openTd();
|
||||||
|
m.addStyleName(Gerrit.RESOURCES.css().iconCell());
|
||||||
|
m.addStyleName(Gerrit.RESOURCES.css().noborder());
|
||||||
|
m.nbsp();
|
||||||
|
m.closeTd();
|
||||||
|
|
||||||
|
m.openTd();
|
||||||
|
m.setAttribute("colspan", C_SIZE - 1);
|
||||||
|
m.closeTd();
|
||||||
|
|
||||||
|
m.openTd();
|
||||||
|
m.addStyleName(Gerrit.RESOURCES.css().dataCell());
|
||||||
|
m.addStyleName(Gerrit.RESOURCES.css().patchSizeCell());
|
||||||
|
m.addStyleName(Gerrit.RESOURCES.css().leftMostCell());
|
||||||
|
m.append(Util.M.patchTableSize_Modify(ins, dels));
|
||||||
|
m.closeTd();
|
||||||
|
|
||||||
|
m.closeTr();
|
||||||
|
}
|
||||||
|
|
||||||
void appendCommentCount(final SafeHtmlBuilder m, final Patch p) {
|
void appendCommentCount(final SafeHtmlBuilder m, final Patch p) {
|
||||||
if (p.getCommentCount() > 0) {
|
if (p.getCommentCount() > 0) {
|
||||||
m.append(Util.M.patchTableComments(p.getCommentCount()));
|
m.append(Util.M.patchTableComments(p.getCommentCount()));
|
||||||
@@ -513,6 +565,34 @@ public class PatchTable extends Composite {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void appendSize(final SafeHtmlBuilder m, final Patch p) {
|
||||||
|
if (Patch.COMMIT_MSG.equals(p.getFileName())) {
|
||||||
|
m.nbsp();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.getPatchType() == PatchType.UNIFIED) {
|
||||||
|
int ins = p.getInsertions();
|
||||||
|
int dels = p.getDeletions();
|
||||||
|
|
||||||
|
switch (p.getChangeType()) {
|
||||||
|
case ADDED:
|
||||||
|
m.append(Util.M.patchTableSize_Lines(ins));
|
||||||
|
break;
|
||||||
|
case DELETED:
|
||||||
|
m.nbsp();
|
||||||
|
break;
|
||||||
|
case MODIFIED:
|
||||||
|
case COPIED:
|
||||||
|
case RENAMED:
|
||||||
|
m.append(Util.M.patchTableSize_Modify(ins, dels));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m.nbsp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void openlink(final SafeHtmlBuilder m, final int colspan) {
|
private void openlink(final SafeHtmlBuilder m, final int colspan) {
|
||||||
m.openTd();
|
m.openTd();
|
||||||
m.addStyleName(Gerrit.RESOURCES.css().dataCell());
|
m.addStyleName(Gerrit.RESOURCES.css().dataCell());
|
||||||
@@ -584,6 +664,9 @@ public class PatchTable extends Composite {
|
|||||||
private double start;
|
private double start;
|
||||||
private ProgressBar meter;
|
private ProgressBar meter;
|
||||||
|
|
||||||
|
private int insertions;
|
||||||
|
private int deletions;
|
||||||
|
|
||||||
private DisplayCommand(final List<Patch> list) {
|
private DisplayCommand(final List<Patch> list) {
|
||||||
this.table = new MyTable();
|
this.table = new MyTable();
|
||||||
this.list = list;
|
this.list = list;
|
||||||
@@ -612,14 +695,19 @@ public class PatchTable extends Composite {
|
|||||||
case 0:
|
case 0:
|
||||||
if (row == 0) {
|
if (row == 0) {
|
||||||
table.appendHeader(nc);
|
table.appendHeader(nc);
|
||||||
|
table.appendRow(nc, list.get(row++));
|
||||||
}
|
}
|
||||||
while (row < list.size()) {
|
while (row < list.size()) {
|
||||||
table.appendRow(nc, list.get(row));
|
Patch p = list.get(row);
|
||||||
|
insertions += p.getInsertions();
|
||||||
|
deletions += p.getDeletions();
|
||||||
|
table.appendRow(nc, p);
|
||||||
if ((++row % 10) == 0 && longRunning()) {
|
if ((++row % 10) == 0 && longRunning()) {
|
||||||
updateMeter();
|
updateMeter();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
table.appendTotals(nc, insertions, deletions);
|
||||||
table.resetHtml(nc);
|
table.resetHtml(nc);
|
||||||
nc = null;
|
nc = null;
|
||||||
stage = 1;
|
stage = 1;
|
||||||
|
@@ -36,7 +36,6 @@ import com.google.gerrit.reviewdb.PatchSet;
|
|||||||
import com.google.gerrit.reviewdb.PatchSetApproval;
|
import com.google.gerrit.reviewdb.PatchSetApproval;
|
||||||
import com.google.gwt.event.dom.client.ClickEvent;
|
import com.google.gwt.event.dom.client.ClickEvent;
|
||||||
import com.google.gwt.event.dom.client.ClickHandler;
|
import com.google.gwt.event.dom.client.ClickHandler;
|
||||||
import com.google.gwt.user.client.DOM;
|
|
||||||
import com.google.gwt.user.client.ui.Button;
|
import com.google.gwt.user.client.ui.Button;
|
||||||
import com.google.gwt.user.client.ui.FlowPanel;
|
import com.google.gwt.user.client.ui.FlowPanel;
|
||||||
import com.google.gwt.user.client.ui.FormPanel;
|
import com.google.gwt.user.client.ui.FormPanel;
|
||||||
@@ -201,7 +200,7 @@ public class PublishCommentScreen extends AccountScreen implements
|
|||||||
message = new NpTextArea();
|
message = new NpTextArea();
|
||||||
message.setCharacterWidth(60);
|
message.setCharacterWidth(60);
|
||||||
message.setVisibleLines(10);
|
message.setVisibleLines(10);
|
||||||
DOM.setElementPropertyBoolean(message.getElement(), "spellcheck", true);
|
message.setSpellCheck(true);
|
||||||
mwrap.add(message);
|
mwrap.add(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,7 +280,8 @@ public class PublishCommentScreen extends AccountScreen implements
|
|||||||
draftsPanel.add(panel);
|
draftsPanel.add(panel);
|
||||||
// Parent table can be null here since we are not showing any
|
// Parent table can be null here since we are not showing any
|
||||||
// next/previous links
|
// next/previous links
|
||||||
panel.add(new PatchLink.SideBySide(fn, patchKey, 0, null, null));
|
panel.add(new PatchLink.SideBySide(PatchTable
|
||||||
|
.getDisplayFileName(patchKey), patchKey, 0, null, null));
|
||||||
priorFile = fn;
|
priorFile = fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,7 +24,8 @@ import com.google.gwt.user.client.rpc.AsyncCallback;
|
|||||||
import com.google.gwtorm.client.KeyUtil;
|
import com.google.gwtorm.client.KeyUtil;
|
||||||
|
|
||||||
|
|
||||||
public class QueryScreen extends PagedSingleListScreen {
|
public class QueryScreen extends PagedSingleListScreen implements
|
||||||
|
ChangeListScreen {
|
||||||
public static QueryScreen forQuery(String query) {
|
public static QueryScreen forQuery(String query) {
|
||||||
return forQuery(query, PageLinks.TOP);
|
return forQuery(query, PageLinks.TOP);
|
||||||
}
|
}
|
||||||
|
@@ -415,6 +415,14 @@
|
|||||||
color: #ff5555;
|
color: #ff5555;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.changeTable .patchSizeCell {
|
||||||
|
text-align: right;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.changeTable td.noborder {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
.changeTable .filePathCell {
|
.changeTable .filePathCell {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
@@ -23,9 +23,8 @@ import com.google.gwt.event.dom.client.ClickHandler;
|
|||||||
import com.google.gwt.event.dom.client.DoubleClickEvent;
|
import com.google.gwt.event.dom.client.DoubleClickEvent;
|
||||||
import com.google.gwt.event.dom.client.DoubleClickHandler;
|
import com.google.gwt.event.dom.client.DoubleClickHandler;
|
||||||
import com.google.gwt.event.dom.client.KeyCodes;
|
import com.google.gwt.event.dom.client.KeyCodes;
|
||||||
import com.google.gwt.event.dom.client.KeyPressEvent;
|
import com.google.gwt.event.dom.client.KeyDownEvent;
|
||||||
import com.google.gwt.event.dom.client.KeyPressHandler;
|
import com.google.gwt.event.dom.client.KeyDownHandler;
|
||||||
import com.google.gwt.user.client.DOM;
|
|
||||||
import com.google.gwt.user.client.Timer;
|
import com.google.gwt.user.client.Timer;
|
||||||
import com.google.gwt.user.client.Window;
|
import com.google.gwt.user.client.Window;
|
||||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||||
@@ -79,13 +78,14 @@ public class CommentEditorPanel extends CommentPanel implements ClickHandler,
|
|||||||
text.setText(comment.getMessage());
|
text.setText(comment.getMessage());
|
||||||
text.setCharacterWidth(INITIAL_COLS);
|
text.setCharacterWidth(INITIAL_COLS);
|
||||||
text.setVisibleLines(INITIAL_LINES);
|
text.setVisibleLines(INITIAL_LINES);
|
||||||
DOM.setElementPropertyBoolean(text.getElement(), "spellcheck", true);
|
text.setSpellCheck(true);
|
||||||
text.addKeyPressHandler(new KeyPressHandler() {
|
text.addKeyDownHandler(new KeyDownHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void onKeyPress(final KeyPressEvent event) {
|
public void onKeyDown(final KeyDownEvent event) {
|
||||||
if (event.getCharCode() == KeyCodes.KEY_ESCAPE
|
if (event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE
|
||||||
&& !event.isAnyModifierKeyDown()) {
|
&& !event.isAnyModifierKeyDown()) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
if (isNew()) {
|
if (isNew()) {
|
||||||
onDiscard();
|
onDiscard();
|
||||||
} else {
|
} else {
|
||||||
@@ -96,13 +96,15 @@ public class CommentEditorPanel extends CommentPanel implements ClickHandler,
|
|||||||
|
|
||||||
if ((event.isControlKeyDown() || event.isMetaKeyDown())
|
if ((event.isControlKeyDown() || event.isMetaKeyDown())
|
||||||
&& !event.isAltKeyDown() && !event.isShiftKeyDown()) {
|
&& !event.isAltKeyDown() && !event.isShiftKeyDown()) {
|
||||||
switch (event.getCharCode()) {
|
switch (event.getNativeKeyCode()) {
|
||||||
case 's':
|
case 's':
|
||||||
|
case 'S':
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
onSave(NULL_CALLBACK);
|
onSave(NULL_CALLBACK);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 'd':
|
case 'd':
|
||||||
|
case 'D':
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (isNew()) {
|
if (isNew()) {
|
||||||
onDiscard();
|
onDiscard();
|
||||||
@@ -182,7 +184,13 @@ public class CommentEditorPanel extends CommentPanel implements ClickHandler,
|
|||||||
setMessageTextVisible(!inEdit);
|
setMessageTextVisible(!inEdit);
|
||||||
edit.setVisible(!inEdit);
|
edit.setVisible(!inEdit);
|
||||||
|
|
||||||
text.setVisible(inEdit);
|
if (inEdit) {
|
||||||
|
text.setVisible(true);
|
||||||
|
} else {
|
||||||
|
text.setFocus(false);
|
||||||
|
text.setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
save.setVisible(inEdit);
|
save.setVisible(inEdit);
|
||||||
cancel.setVisible(inEdit && !isNew());
|
cancel.setVisible(inEdit && !isNew());
|
||||||
discard.setVisible(inEdit);
|
discard.setVisible(inEdit);
|
||||||
@@ -241,6 +249,7 @@ public class CommentEditorPanel extends CommentPanel implements ClickHandler,
|
|||||||
}
|
}
|
||||||
|
|
||||||
comment.setMessage(txt);
|
comment.setMessage(txt);
|
||||||
|
text.setFocus(false);
|
||||||
text.setReadOnly(true);
|
text.setReadOnly(true);
|
||||||
save.setEnabled(false);
|
save.setEnabled(false);
|
||||||
cancel.setEnabled(false);
|
cancel.setEnabled(false);
|
||||||
@@ -262,6 +271,7 @@ public class CommentEditorPanel extends CommentPanel implements ClickHandler,
|
|||||||
@Override
|
@Override
|
||||||
public void onFailure(final Throwable caught) {
|
public void onFailure(final Throwable caught) {
|
||||||
text.setReadOnly(false);
|
text.setReadOnly(false);
|
||||||
|
text.setFocus(true);
|
||||||
save.setEnabled(true);
|
save.setEnabled(true);
|
||||||
cancel.setEnabled(true);
|
cancel.setEnabled(true);
|
||||||
discard.setEnabled(true);
|
discard.setEnabled(true);
|
||||||
@@ -281,10 +291,12 @@ public class CommentEditorPanel extends CommentPanel implements ClickHandler,
|
|||||||
private void onDiscard() {
|
private void onDiscard() {
|
||||||
expandTimer.cancel();
|
expandTimer.cancel();
|
||||||
if (isNew()) {
|
if (isNew()) {
|
||||||
|
text.setFocus(false);
|
||||||
removeUI();
|
removeUI();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
text.setFocus(false);
|
||||||
text.setReadOnly(true);
|
text.setReadOnly(true);
|
||||||
save.setEnabled(false);
|
save.setEnabled(false);
|
||||||
cancel.setEnabled(false);
|
cancel.setEnabled(false);
|
||||||
@@ -300,6 +312,7 @@ public class CommentEditorPanel extends CommentPanel implements ClickHandler,
|
|||||||
@Override
|
@Override
|
||||||
public void onFailure(final Throwable caught) {
|
public void onFailure(final Throwable caught) {
|
||||||
text.setReadOnly(false);
|
text.setReadOnly(false);
|
||||||
|
text.setFocus(true);
|
||||||
save.setEnabled(true);
|
save.setEnabled(true);
|
||||||
cancel.setEnabled(true);
|
cancel.setEnabled(true);
|
||||||
discard.setEnabled(true);
|
discard.setEnabled(true);
|
||||||
|
@@ -80,9 +80,6 @@ public abstract class PatchScreen extends Screen implements
|
|||||||
public Unified(final Patch.Key id, final int patchIndex,
|
public Unified(final Patch.Key id, final int patchIndex,
|
||||||
final PatchSetDetail patchSetDetail, final PatchTable patchTable) {
|
final PatchSetDetail patchSetDetail, final PatchTable patchTable) {
|
||||||
super(id, patchIndex, patchSetDetail, patchTable);
|
super(id, patchIndex, patchSetDetail, patchTable);
|
||||||
final AccountDiffPreference dp = settingsPanel.getValue();
|
|
||||||
dp.setSyntaxHighlighting(false);
|
|
||||||
settingsPanel.setValue(dp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -395,7 +392,7 @@ public abstract class PatchScreen extends Screen implements
|
|||||||
|
|
||||||
private void onResult(final PatchScript script, final boolean isFirst) {
|
private void onResult(final PatchScript script, final boolean isFirst) {
|
||||||
final Change.Key cid = script.getChangeId();
|
final Change.Key cid = script.getChangeId();
|
||||||
final String path = patchKey.get();
|
final String path = PatchTable.getDisplayFileName(patchKey);
|
||||||
String fileName = path;
|
String fileName = path;
|
||||||
final int last = fileName.lastIndexOf('/');
|
final int last = fileName.lastIndexOf('/');
|
||||||
if (last >= 0) {
|
if (last >= 0) {
|
||||||
|
@@ -118,7 +118,7 @@ public abstract class NavigationTable<RowItem> extends FancyFlexTable<RowItem> {
|
|||||||
row++;
|
row++;
|
||||||
} else if (sEnd < cTop) {
|
} else if (sEnd < cTop) {
|
||||||
row--;
|
row--;
|
||||||
} else if (getRowItem(row) != null) {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -354,7 +354,7 @@ class GitWebServlet extends HttpServlet {
|
|||||||
private static Map<String, String> getParameters(final HttpServletRequest req)
|
private static Map<String, String> getParameters(final HttpServletRequest req)
|
||||||
throws UnsupportedEncodingException {
|
throws UnsupportedEncodingException {
|
||||||
final Map<String, String> params = new HashMap<String, String>();
|
final Map<String, String> params = new HashMap<String, String>();
|
||||||
for (final String pair : req.getQueryString().split(";")) {
|
for (final String pair : req.getQueryString().split("[&;]")) {
|
||||||
final int eq = pair.indexOf('=');
|
final int eq = pair.indexOf('=');
|
||||||
if (0 < eq) {
|
if (0 < eq) {
|
||||||
String name = pair.substring(0, eq);
|
String name = pair.substring(0, eq);
|
||||||
|
@@ -223,8 +223,7 @@ public class ChangeDetailFactory extends Handler<ChangeDetail> {
|
|||||||
for (final Change.Id a : ancestorOrder) {
|
for (final Change.Id a : ancestorOrder) {
|
||||||
final Change ac = m.get(a);
|
final Change ac = m.get(a);
|
||||||
if (ac != null) {
|
if (ac != null) {
|
||||||
aic.want(ac.getOwner());
|
dependsOn.add(newChangeInfo(ac));
|
||||||
dependsOn.add(new ChangeInfo(ac));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,8 +231,7 @@ public class ChangeDetailFactory extends Handler<ChangeDetail> {
|
|||||||
for (final Change.Id a : descendants) {
|
for (final Change.Id a : descendants) {
|
||||||
final Change ac = m.get(a);
|
final Change ac = m.get(a);
|
||||||
if (ac != null) {
|
if (ac != null) {
|
||||||
aic.want(ac.getOwner());
|
neededBy.add(newChangeInfo(ac));
|
||||||
neededBy.add(new ChangeInfo(ac));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,4 +244,15 @@ public class ChangeDetailFactory extends Handler<ChangeDetail> {
|
|||||||
detail.setDependsOn(dependsOn);
|
detail.setDependsOn(dependsOn);
|
||||||
detail.setNeededBy(neededBy);
|
detail.setNeededBy(neededBy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ChangeInfo newChangeInfo(final Change ac) {
|
||||||
|
aic.want(ac.getOwner());
|
||||||
|
ChangeInfo ci = new ChangeInfo(ac);
|
||||||
|
ci.setStarred(isStarred(ac));
|
||||||
|
return ci;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isStarred(final Change ac) {
|
||||||
|
return control.getCurrentUser().getStarredChanges().contains(ac.getId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -65,6 +65,7 @@ class PatchScriptBuilder {
|
|||||||
private Repository db;
|
private Repository db;
|
||||||
private Change change;
|
private Change change;
|
||||||
private AccountDiffPreference diffPrefs;
|
private AccountDiffPreference diffPrefs;
|
||||||
|
private boolean againstParent;
|
||||||
private ObjectId aId;
|
private ObjectId aId;
|
||||||
private ObjectId bId;
|
private ObjectId bId;
|
||||||
|
|
||||||
@@ -101,7 +102,8 @@ class PatchScriptBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setTrees(final ObjectId a, final ObjectId b) {
|
void setTrees(final boolean ap, final ObjectId a, final ObjectId b) {
|
||||||
|
againstParent = ap;
|
||||||
aId = a;
|
aId = a;
|
||||||
bId = b;
|
bId = b;
|
||||||
}
|
}
|
||||||
@@ -364,34 +366,55 @@ class PatchScriptBuilder {
|
|||||||
|
|
||||||
void resolve(final Side other, final ObjectId within) throws IOException {
|
void resolve(final Side other, final ObjectId within) throws IOException {
|
||||||
try {
|
try {
|
||||||
final TreeWalk tw = find(within);
|
final boolean reuse;
|
||||||
|
if (Patch.COMMIT_MSG.equals(path)) {
|
||||||
id = tw != null ? tw.getObjectId(0) : ObjectId.zeroId();
|
if (againstParent && (aId == within || within.equals(aId))) {
|
||||||
mode = tw != null ? tw.getFileMode(0) : FileMode.MISSING;
|
id = ObjectId.zeroId();
|
||||||
|
src = Text.EMPTY;
|
||||||
final boolean reuse =
|
srcContent = Text.NO_BYTES;
|
||||||
other != null && other.id.equals(id) && other.mode == mode;
|
mode = FileMode.MISSING;
|
||||||
|
displayMethod = DisplayMethod.NONE;
|
||||||
if (reuse) {
|
} else {
|
||||||
srcContent = other.srcContent;
|
id = within;
|
||||||
|
src = Text.forCommit(db, within);
|
||||||
} else if (mode.getObjectType() == Constants.OBJ_BLOB) {
|
srcContent = src.getContent();
|
||||||
srcContent = Text.asByteArray(db.open(id, Constants.OBJ_BLOB));
|
if (src == Text.EMPTY) {
|
||||||
|
mode = FileMode.MISSING;
|
||||||
|
displayMethod = DisplayMethod.NONE;
|
||||||
|
} else {
|
||||||
|
mode = FileMode.REGULAR_FILE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reuse = false;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
srcContent = Text.NO_BYTES;
|
final TreeWalk tw = find(within);
|
||||||
}
|
|
||||||
|
|
||||||
if (reuse) {
|
id = tw != null ? tw.getObjectId(0) : ObjectId.zeroId();
|
||||||
mimeType = other.mimeType;
|
mode = tw != null ? tw.getFileMode(0) : FileMode.MISSING;
|
||||||
displayMethod = other.displayMethod;
|
reuse = other != null && other.id.equals(id) && other.mode == mode;
|
||||||
src = other.src;
|
|
||||||
|
|
||||||
} else if (srcContent.length > 0 && FileMode.SYMLINK != mode) {
|
if (reuse) {
|
||||||
mimeType = registry.getMimeType(path, srcContent);
|
srcContent = other.srcContent;
|
||||||
if ("image".equals(mimeType.getMediaType())
|
|
||||||
&& registry.isSafeInline(mimeType)) {
|
} else if (mode.getObjectType() == Constants.OBJ_BLOB) {
|
||||||
displayMethod = DisplayMethod.IMG;
|
srcContent = Text.asByteArray(db.open(id, Constants.OBJ_BLOB));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
srcContent = Text.NO_BYTES;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reuse) {
|
||||||
|
mimeType = other.mimeType;
|
||||||
|
displayMethod = other.displayMethod;
|
||||||
|
src = other.src;
|
||||||
|
|
||||||
|
} else if (srcContent.length > 0 && FileMode.SYMLINK != mode) {
|
||||||
|
mimeType = registry.getMimeType(path, srcContent);
|
||||||
|
if ("image".equals(mimeType.getMediaType())
|
||||||
|
&& registry.isSafeInline(mimeType)) {
|
||||||
|
displayMethod = DisplayMethod.IMG;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -177,7 +177,7 @@ class PatchScriptFactory extends Handler<PatchScript> {
|
|||||||
b.setRepository(git);
|
b.setRepository(git);
|
||||||
b.setChange(change);
|
b.setChange(change);
|
||||||
b.setDiffPrefs(dp);
|
b.setDiffPrefs(dp);
|
||||||
b.setTrees(list.getOldId(), list.getNewId());
|
b.setTrees(list.isAgainstParent(), list.getOldId(), list.getNewId());
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -175,14 +175,14 @@ class AddRefRight extends Handler<ProjectDetail> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exclusive) {
|
|
||||||
refPattern = "-" + refPattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!projectControl.controlForRef(refPattern).isOwner()) {
|
if (!projectControl.controlForRef(refPattern).isOwner()) {
|
||||||
throw new NoSuchRefException(refPattern);
|
throw new NoSuchRefException(refPattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (exclusive) {
|
||||||
|
refPattern = "-" + refPattern;
|
||||||
|
}
|
||||||
|
|
||||||
final AccountGroup group = groupCache.get(groupName);
|
final AccountGroup group = groupCache.get(groupName);
|
||||||
if (group == null) {
|
if (group == null) {
|
||||||
throw new NoSuchGroupException(groupName);
|
throw new NoSuchGroupException(groupName);
|
||||||
|
@@ -373,7 +373,7 @@ case "$ACTION" in
|
|||||||
printf '%s' "Starting Gerrit Code Review: "
|
printf '%s' "Starting Gerrit Code Review: "
|
||||||
|
|
||||||
if test 1 = "$NO_START" ; then
|
if test 1 = "$NO_START" ; then
|
||||||
echo "Not starting gerrit - NO_START=1 in /etc/default/gerrit"
|
echo "Not starting gerrit - NO_START=1 in /etc/default/gerritcodereview"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@@ -19,6 +19,9 @@ import com.google.gwtorm.client.StringKey;
|
|||||||
|
|
||||||
/** A single modified file in a {@link PatchSet}. */
|
/** A single modified file in a {@link PatchSet}. */
|
||||||
public final class Patch {
|
public final class Patch {
|
||||||
|
/** Magical file name which represents the commit message. */
|
||||||
|
public static final String COMMIT_MSG = "/COMMIT_MSG";
|
||||||
|
|
||||||
public static class Key extends StringKey<PatchSet.Id> {
|
public static class Key extends StringKey<PatchSet.Id> {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@@ -191,6 +194,12 @@ public final class Patch {
|
|||||||
/** Number of drafts by the current user; not persisted in the datastore. */
|
/** Number of drafts by the current user; not persisted in the datastore. */
|
||||||
protected int nbrDrafts;
|
protected int nbrDrafts;
|
||||||
|
|
||||||
|
/** Number of lines added to the file. */
|
||||||
|
protected int insertions;
|
||||||
|
|
||||||
|
/** Number of lines deleted from the file. */
|
||||||
|
protected int deletions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Original if {@link #changeType} is {@link ChangeType#COPIED} or
|
* Original if {@link #changeType} is {@link ChangeType#COPIED} or
|
||||||
* {@link ChangeType#RENAMED}.
|
* {@link ChangeType#RENAMED}.
|
||||||
@@ -229,6 +238,22 @@ public final class Patch {
|
|||||||
nbrDrafts = n;
|
nbrDrafts = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getInsertions() {
|
||||||
|
return insertions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInsertions(int n) {
|
||||||
|
insertions = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDeletions() {
|
||||||
|
return deletions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeletions(int n) {
|
||||||
|
deletions = n;
|
||||||
|
}
|
||||||
|
|
||||||
public ChangeType getChangeType() {
|
public ChangeType getChangeType() {
|
||||||
return ChangeType.forCode(changeType);
|
return ChangeType.forCode(changeType);
|
||||||
}
|
}
|
||||||
|
@@ -21,6 +21,7 @@ import com.google.gerrit.reviewdb.ApprovalCategory;
|
|||||||
import com.google.gerrit.reviewdb.ApprovalCategoryValue;
|
import com.google.gerrit.reviewdb.ApprovalCategoryValue;
|
||||||
import com.google.gerrit.reviewdb.Change;
|
import com.google.gerrit.reviewdb.Change;
|
||||||
import com.google.gerrit.reviewdb.PatchSet;
|
import com.google.gerrit.reviewdb.PatchSet;
|
||||||
|
import com.google.gerrit.reviewdb.Project;
|
||||||
import com.google.gerrit.server.IdentifiedUser;
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
import com.google.gerrit.server.account.AccountCache;
|
import com.google.gerrit.server.account.AccountCache;
|
||||||
import com.google.gerrit.server.account.AccountState;
|
import com.google.gerrit.server.account.AccountState;
|
||||||
@@ -41,6 +42,8 @@ import com.google.gerrit.server.project.ProjectControl;
|
|||||||
import com.google.gerrit.server.project.ProjectState;
|
import com.google.gerrit.server.project.ProjectState;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -168,14 +171,23 @@ public class ChangeHookRunner {
|
|||||||
* @param change Change to get repo for,
|
* @param change Change to get repo for,
|
||||||
* @return Repository or null.
|
* @return Repository or null.
|
||||||
*/
|
*/
|
||||||
private Repository getRepo(final Change change) {
|
private Repository openRepository(final Change change) {
|
||||||
|
Project.NameKey name = change.getProject();
|
||||||
try {
|
try {
|
||||||
return repoManager.openRepository(change.getProject().get());
|
return repoManager.openRepository(name.get());
|
||||||
} catch (Exception ex) {
|
} catch (RepositoryNotFoundException err) {
|
||||||
|
log.warn("Cannot open repository " + name.get(), err);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addArg(List<String> args, String name, String value) {
|
||||||
|
if (value != null) {
|
||||||
|
args.add(name);
|
||||||
|
args.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fire the Patchset Created Hook.
|
* Fire the Patchset Created Hook.
|
||||||
*
|
*
|
||||||
@@ -192,24 +204,15 @@ public class ChangeHookRunner {
|
|||||||
fireEvent(change, event);
|
fireEvent(change, event);
|
||||||
|
|
||||||
final List<String> args = new ArrayList<String>();
|
final List<String> args = new ArrayList<String>();
|
||||||
args.add(patchsetCreatedHook.getAbsolutePath());
|
addArg(args, "--change", event.change.id);
|
||||||
|
addArg(args, "--change-url", event.change.url);
|
||||||
|
addArg(args, "--project", event.change.project);
|
||||||
|
addArg(args, "--branch", event.change.branch);
|
||||||
|
addArg(args, "--uploader", getDisplayName(uploader.getAccount()));
|
||||||
|
addArg(args, "--commit", event.patchSet.revision);
|
||||||
|
addArg(args, "--patchset", event.patchSet.number);
|
||||||
|
|
||||||
args.add("--change");
|
runHook(openRepository(change), patchsetCreatedHook, args);
|
||||||
args.add(event.change.id);
|
|
||||||
args.add("--change-url");
|
|
||||||
args.add(event.change.url);
|
|
||||||
args.add("--project");
|
|
||||||
args.add(event.change.project);
|
|
||||||
args.add("--branch");
|
|
||||||
args.add(event.change.branch);
|
|
||||||
args.add("--uploader");
|
|
||||||
args.add(getDisplayName(uploader.getAccount()));
|
|
||||||
args.add("--commit");
|
|
||||||
args.add(event.patchSet.revision);
|
|
||||||
args.add("--patchset");
|
|
||||||
args.add(event.patchSet.number);
|
|
||||||
|
|
||||||
runHook(getRepo(change), args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -240,28 +243,18 @@ public class ChangeHookRunner {
|
|||||||
fireEvent(change, event);
|
fireEvent(change, event);
|
||||||
|
|
||||||
final List<String> args = new ArrayList<String>();
|
final List<String> args = new ArrayList<String>();
|
||||||
args.add(commentAddedHook.getAbsolutePath());
|
addArg(args, "--change", event.change.id);
|
||||||
|
addArg(args, "--change-url", event.change.url);
|
||||||
args.add("--change");
|
addArg(args, "--project", event.change.project);
|
||||||
args.add(event.change.id);
|
addArg(args, "--branch", event.change.branch);
|
||||||
args.add("--change-url");
|
addArg(args, "--author", getDisplayName(account));
|
||||||
args.add(event.change.url);
|
addArg(args, "--commit", event.patchSet.revision);
|
||||||
args.add("--project");
|
addArg(args, "--comment", comment == null ? "" : comment);
|
||||||
args.add(event.change.project);
|
|
||||||
args.add("--branch");
|
|
||||||
args.add(event.change.branch);
|
|
||||||
args.add("--author");
|
|
||||||
args.add(getDisplayName(account));
|
|
||||||
args.add("--commit");
|
|
||||||
args.add(event.patchSet.revision);
|
|
||||||
args.add("--comment");
|
|
||||||
args.add(comment == null ? "" : comment);
|
|
||||||
for (Map.Entry<ApprovalCategory.Id, ApprovalCategoryValue.Id> approval : approvals.entrySet()) {
|
for (Map.Entry<ApprovalCategory.Id, ApprovalCategoryValue.Id> approval : approvals.entrySet()) {
|
||||||
args.add("--" + approval.getKey().get());
|
addArg(args, "--" + approval.getKey().get(), Short.toString(approval.getValue().get()));
|
||||||
args.add(Short.toString(approval.getValue().get()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
runHook(getRepo(change), args);
|
runHook(openRepository(change), commentAddedHook, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -280,22 +273,14 @@ public class ChangeHookRunner {
|
|||||||
fireEvent(change, event);
|
fireEvent(change, event);
|
||||||
|
|
||||||
final List<String> args = new ArrayList<String>();
|
final List<String> args = new ArrayList<String>();
|
||||||
args.add(changeMergedHook.getAbsolutePath());
|
addArg(args, "--change", event.change.id);
|
||||||
|
addArg(args, "--change-url", event.change.url);
|
||||||
|
addArg(args, "--project", event.change.project);
|
||||||
|
addArg(args, "--branch", event.change.branch);
|
||||||
|
addArg(args, "--submitter", getDisplayName(account));
|
||||||
|
addArg(args, "--commit", event.patchSet.revision);
|
||||||
|
|
||||||
args.add("--change");
|
runHook(openRepository(change), changeMergedHook, args);
|
||||||
args.add(event.change.id);
|
|
||||||
args.add("--change-url");
|
|
||||||
args.add(event.change.url);
|
|
||||||
args.add("--project");
|
|
||||||
args.add(event.change.project);
|
|
||||||
args.add("--branch");
|
|
||||||
args.add(event.change.branch);
|
|
||||||
args.add("--submitter");
|
|
||||||
args.add(getDisplayName(account));
|
|
||||||
args.add("--commit");
|
|
||||||
args.add(event.patchSet.revision);
|
|
||||||
|
|
||||||
runHook(getRepo(change), args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -314,22 +299,14 @@ public class ChangeHookRunner {
|
|||||||
fireEvent(change, event);
|
fireEvent(change, event);
|
||||||
|
|
||||||
final List<String> args = new ArrayList<String>();
|
final List<String> args = new ArrayList<String>();
|
||||||
args.add(changeAbandonedHook.getAbsolutePath());
|
addArg(args, "--change", event.change.id);
|
||||||
|
addArg(args, "--change-url", event.change.url);
|
||||||
|
addArg(args, "--project", event.change.project);
|
||||||
|
addArg(args, "--branch", event.change.branch);
|
||||||
|
addArg(args, "--abandoner", getDisplayName(account));
|
||||||
|
addArg(args, "--reason", reason == null ? "" : reason);
|
||||||
|
|
||||||
args.add("--change");
|
runHook(openRepository(change), changeAbandonedHook, args);
|
||||||
args.add(event.change.id);
|
|
||||||
args.add("--change-url");
|
|
||||||
args.add(event.change.url);
|
|
||||||
args.add("--project");
|
|
||||||
args.add(event.change.project);
|
|
||||||
args.add("--branch");
|
|
||||||
args.add(event.change.branch);
|
|
||||||
args.add("--abandoner");
|
|
||||||
args.add(getDisplayName(account));
|
|
||||||
args.add("--reason");
|
|
||||||
args.add(reason == null ? "" : reason);
|
|
||||||
|
|
||||||
runHook(getRepo(change), args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -348,22 +325,14 @@ public class ChangeHookRunner {
|
|||||||
fireEvent(change, event);
|
fireEvent(change, event);
|
||||||
|
|
||||||
final List<String> args = new ArrayList<String>();
|
final List<String> args = new ArrayList<String>();
|
||||||
args.add(changeRestoredHook.getAbsolutePath());
|
addArg(args, "--change", event.change.id);
|
||||||
|
addArg(args, "--change-url", event.change.url);
|
||||||
|
addArg(args, "--project", event.change.project);
|
||||||
|
addArg(args, "--branch", event.change.branch);
|
||||||
|
addArg(args, "--restorer", getDisplayName(account));
|
||||||
|
addArg(args, "--reason", reason == null ? "" : reason);
|
||||||
|
|
||||||
args.add("--change");
|
runHook(openRepository(change), changeRestoredHook, args);
|
||||||
args.add(event.change.id);
|
|
||||||
args.add("--change-url");
|
|
||||||
args.add(event.change.url);
|
|
||||||
args.add("--project");
|
|
||||||
args.add(event.change.project);
|
|
||||||
args.add("--branch");
|
|
||||||
args.add(event.change.branch);
|
|
||||||
args.add("--restorer");
|
|
||||||
args.add(getDisplayName(account));
|
|
||||||
args.add("--reason");
|
|
||||||
args.add(reason == null ? "" : reason);
|
|
||||||
|
|
||||||
runHook(getRepo(change), args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fireEvent(final Change change, final ChangeEvent event) {
|
private void fireEvent(final Change change, final ChangeEvent event) {
|
||||||
@@ -416,53 +385,76 @@ public class ChangeHookRunner {
|
|||||||
return "Anonymous Coward";
|
return "Anonymous Coward";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run a hook.
|
* Run a hook.
|
||||||
*
|
*
|
||||||
* @param repo Repo to run the hook for.
|
* @param repo repository to run the hook for.
|
||||||
* @param args Arguments to use to run the hook.
|
* @param hook the hook to execute.
|
||||||
*/
|
* @param args Arguments to use to run the hook.
|
||||||
private synchronized void runHook(final Repository repo, final List<String> args) {
|
*/
|
||||||
if (repo == null) {
|
private synchronized void runHook(Repository repo, File hook,
|
||||||
log.error("No repo found for hook.");
|
List<String> args) {
|
||||||
return;
|
if (repo != null) {
|
||||||
}
|
if (hook.exists()) {
|
||||||
|
hookQueue.execute(new HookTask(repo, hook, args));
|
||||||
hookQueue.execute(new Runnable() {
|
} else {
|
||||||
@Override
|
repo.close();
|
||||||
public void run() {
|
}
|
||||||
try {
|
|
||||||
if (new File(args.get(0)).exists()) {
|
|
||||||
final ProcessBuilder pb = new ProcessBuilder(args);
|
|
||||||
pb.redirectErrorStream(true);
|
|
||||||
pb.directory(repo.getDirectory());
|
|
||||||
final Map<String, String> env = pb.environment();
|
|
||||||
env.put("GIT_DIR", repo.getDirectory().getAbsolutePath());
|
|
||||||
|
|
||||||
Process ps = pb.start();
|
|
||||||
ps.getOutputStream().close();
|
|
||||||
|
|
||||||
BufferedReader br = new BufferedReader(new InputStreamReader(ps.getInputStream()));
|
|
||||||
try {
|
|
||||||
String line;
|
|
||||||
while ((line = br.readLine()) != null) {
|
|
||||||
log.info("hook output: " + line);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
br.close();
|
|
||||||
} catch (IOException e2) {
|
|
||||||
}
|
|
||||||
|
|
||||||
ps.waitFor();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
log.error("Unexpected error during hook execution", e);
|
|
||||||
} finally {
|
|
||||||
repo.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class HookTask implements Runnable {
|
||||||
|
private final Repository repo;
|
||||||
|
private final File hook;
|
||||||
|
private final List<String> args;
|
||||||
|
|
||||||
|
private HookTask(Repository repo, File hook, List<String> args) {
|
||||||
|
this.repo = repo;
|
||||||
|
this.hook = hook;
|
||||||
|
this.args = args;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
final List<String> argv = new ArrayList<String>(1 + args.size());
|
||||||
|
argv.add(hook.getAbsolutePath());
|
||||||
|
argv.addAll(args);
|
||||||
|
|
||||||
|
final ProcessBuilder pb = new ProcessBuilder(argv);
|
||||||
|
pb.redirectErrorStream(true);
|
||||||
|
pb.directory(repo.getDirectory());
|
||||||
|
|
||||||
|
final Map<String, String> env = pb.environment();
|
||||||
|
env.put("GIT_DIR", repo.getDirectory().getAbsolutePath());
|
||||||
|
|
||||||
|
Process ps = pb.start();
|
||||||
|
ps.getOutputStream().close();
|
||||||
|
|
||||||
|
BufferedReader br =
|
||||||
|
new BufferedReader(new InputStreamReader(ps.getInputStream()));
|
||||||
|
try {
|
||||||
|
String line;
|
||||||
|
while ((line = br.readLine()) != null) {
|
||||||
|
log.info("hook[" + hook.getName() + "] output: " + line);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
br.close();
|
||||||
|
} catch (IOException closeErr) {
|
||||||
|
}
|
||||||
|
ps.waitFor();
|
||||||
|
}
|
||||||
|
} catch (Throwable err) {
|
||||||
|
log.error("Error running hook " + hook.getAbsolutePath(), err);
|
||||||
|
} finally {
|
||||||
|
repo.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "hook " + hook.getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -847,7 +847,7 @@ public class MergeOp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateBranch() throws MergeException {
|
private void updateBranch() throws MergeException {
|
||||||
if (branchTip == null || branchTip != mergeTip) {
|
if (mergeTip != null && (branchTip == null || branchTip != mergeTip)) {
|
||||||
branchUpdate.setForceUpdate(false);
|
branchUpdate.setForceUpdate(false);
|
||||||
branchUpdate.setNewObjectId(mergeTip);
|
branchUpdate.setNewObjectId(mergeTip);
|
||||||
branchUpdate.setRefLogMessage("merged", true);
|
branchUpdate.setRefLogMessage("merged", true);
|
||||||
|
@@ -620,12 +620,11 @@ public class ReceiveCommits implements PreReceiveHook, PostReceiveHook {
|
|||||||
|
|
||||||
if (split < destBranchName.length()) {
|
if (split < destBranchName.length()) {
|
||||||
destTopicName = destBranchName.substring(split + 1);
|
destTopicName = destBranchName.substring(split + 1);
|
||||||
|
if (destTopicName.isEmpty()) {
|
||||||
|
destTopicName = null;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// We use empty string here to denote the topic wasn't
|
destTopicName = null;
|
||||||
// supplied, but the caller used the syntax that allows
|
|
||||||
// for a topic to be given.
|
|
||||||
//
|
|
||||||
destTopicName = "";
|
|
||||||
}
|
}
|
||||||
destBranch = new Branch.NameKey(project.getNameKey(), //
|
destBranch = new Branch.NameKey(project.getNameKey(), //
|
||||||
destBranchName.substring(0, split));
|
destBranchName.substring(0, split));
|
||||||
@@ -876,7 +875,7 @@ public class ReceiveCommits implements PreReceiveHook, PostReceiveHook {
|
|||||||
|
|
||||||
final Change change =
|
final Change change =
|
||||||
new Change(changeKey, new Change.Id(db.nextChangeId()), me, destBranch);
|
new Change(changeKey, new Change.Id(db.nextChangeId()), me, destBranch);
|
||||||
change.setTopic(destTopicName.isEmpty() ? null : destTopicName);
|
change.setTopic(destTopicName);
|
||||||
change.nextPatchSetId();
|
change.nextPatchSetId();
|
||||||
|
|
||||||
final PatchSet ps = new PatchSet(change.currPatchSetId());
|
final PatchSet ps = new PatchSet(change.currPatchSetId());
|
||||||
@@ -1177,9 +1176,7 @@ public class ReceiveCommits implements PreReceiveHook, PostReceiveHook {
|
|||||||
public Change update(Change change) {
|
public Change update(Change change) {
|
||||||
if (change.getStatus().isOpen()) {
|
if (change.getStatus().isOpen()) {
|
||||||
if (destTopicName != null) {
|
if (destTopicName != null) {
|
||||||
change.setTopic(destTopicName.isEmpty() //
|
change.setTopic(destTopicName);
|
||||||
? null //
|
|
||||||
: destTopicName);
|
|
||||||
}
|
}
|
||||||
change.setStatus(Change.Status.NEW);
|
change.setStatus(Change.Status.NEW);
|
||||||
change.setCurrentPatchSet(result.info);
|
change.setCurrentPatchSet(result.info);
|
||||||
|
@@ -19,6 +19,7 @@ import com.google.gerrit.reviewdb.AccountGroup;
|
|||||||
import com.google.gerrit.reviewdb.AccountProjectWatch;
|
import com.google.gerrit.reviewdb.AccountProjectWatch;
|
||||||
import com.google.gerrit.reviewdb.Change;
|
import com.google.gerrit.reviewdb.Change;
|
||||||
import com.google.gerrit.reviewdb.ChangeMessage;
|
import com.google.gerrit.reviewdb.ChangeMessage;
|
||||||
|
import com.google.gerrit.reviewdb.Patch;
|
||||||
import com.google.gerrit.reviewdb.PatchSet;
|
import com.google.gerrit.reviewdb.PatchSet;
|
||||||
import com.google.gerrit.reviewdb.PatchSetApproval;
|
import com.google.gerrit.reviewdb.PatchSetApproval;
|
||||||
import com.google.gerrit.reviewdb.PatchSetInfo;
|
import com.google.gerrit.reviewdb.PatchSetInfo;
|
||||||
@@ -34,6 +35,7 @@ import com.google.gerrit.server.query.change.ChangeData;
|
|||||||
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
|
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
|
||||||
import com.google.gwtorm.client.OrmException;
|
import com.google.gwtorm.client.OrmException;
|
||||||
|
|
||||||
|
import java.text.MessageFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@@ -222,9 +224,20 @@ public abstract class ChangeEmail extends OutgoingEmail {
|
|||||||
|
|
||||||
if (patchSet != null) {
|
if (patchSet != null) {
|
||||||
detail.append("---\n");
|
detail.append("---\n");
|
||||||
for (PatchListEntry p : getPatchList().getPatches()) {
|
PatchList patchList = getPatchList();
|
||||||
|
for (PatchListEntry p : patchList.getPatches()) {
|
||||||
|
if (Patch.COMMIT_MSG.equals(p.getNewName())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
detail.append(p.getChangeType().getCode() + " " + p.getNewName() + "\n");
|
detail.append(p.getChangeType().getCode() + " " + p.getNewName() + "\n");
|
||||||
}
|
}
|
||||||
|
detail.append(MessageFormat.format("" //
|
||||||
|
+ "{0,choice,0#0 files|1#1 file|1<{0} files} changed, " //
|
||||||
|
+ "{1,choice,0#0 insertions|1#1 insertion|1<{1} insertions}(+), " //
|
||||||
|
+ "{2,choice,0#0 deletions|1#1 deletion|1<{2} deletions}(-)" //
|
||||||
|
+ "\n", patchList.getPatches().size() - 1, //
|
||||||
|
patchList.getInsertions(), //
|
||||||
|
patchList.getDeletions()));
|
||||||
detail.append("\n");
|
detail.append("\n");
|
||||||
}
|
}
|
||||||
return detail.toString();
|
return detail.toString();
|
||||||
|
@@ -26,6 +26,7 @@ import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
|||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -50,9 +51,13 @@ public class CommentSender extends ReplyToChangeSender {
|
|||||||
Set<String> paths = new HashSet<String>();
|
Set<String> paths = new HashSet<String>();
|
||||||
for (PatchLineComment c : plc) {
|
for (PatchLineComment c : plc) {
|
||||||
Patch.Key p = c.getKey().getParentKey();
|
Patch.Key p = c.getKey().getParentKey();
|
||||||
paths.add(p.getFileName());
|
if (!Patch.COMMIT_MSG.equals(p.getFileName())) {
|
||||||
|
paths.add(p.getFileName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
changeData.setCurrentFilePaths(paths);
|
String[] names = paths.toArray(new String[paths.size()]);
|
||||||
|
Arrays.sort(names);
|
||||||
|
changeData.setCurrentFilePaths(names);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -65,7 +70,7 @@ public class CommentSender extends ReplyToChangeSender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void formatChange() throws EmailException {
|
public void formatChange() throws EmailException {
|
||||||
appendText(velocifyFile("Comment.vm"));
|
appendText(velocifyFile("Comment.vm"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,9 +90,13 @@ public class CommentSender extends ReplyToChangeSender {
|
|||||||
|
|
||||||
if (!pk.equals(currentFileKey)) {
|
if (!pk.equals(currentFileKey)) {
|
||||||
cmts.append("....................................................\n");
|
cmts.append("....................................................\n");
|
||||||
cmts.append("File ");
|
if (Patch.COMMIT_MSG.equals(pk.get())) {
|
||||||
cmts.append(pk.get());
|
cmts.append("Commit Message\n");
|
||||||
cmts.append("\n");
|
} else {
|
||||||
|
cmts.append("File ");
|
||||||
|
cmts.append(pk.get());
|
||||||
|
cmts.append("\n");
|
||||||
|
}
|
||||||
currentFileKey = pk;
|
currentFileKey = pk;
|
||||||
|
|
||||||
if (patchList != null) {
|
if (patchList != null) {
|
||||||
|
@@ -20,10 +20,9 @@ import com.google.gerrit.server.account.AccountState;
|
|||||||
import com.google.gerrit.server.mail.EmailHeader.AddressList;
|
import com.google.gerrit.server.mail.EmailHeader.AddressList;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.apache.velocity.VelocityContext;
|
||||||
import org.apache.velocity.app.Velocity;
|
import org.apache.velocity.app.Velocity;
|
||||||
import org.apache.velocity.exception.ResourceNotFoundException;
|
import org.apache.velocity.exception.ResourceNotFoundException;
|
||||||
import org.apache.velocity.VelocityContext;
|
|
||||||
|
|
||||||
import org.eclipse.jgit.util.SystemReader;
|
import org.eclipse.jgit.util.SystemReader;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -40,7 +39,6 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
/** Sends an email to one or more interested parties. */
|
/** Sends an email to one or more interested parties. */
|
||||||
public abstract class OutgoingEmail {
|
public abstract class OutgoingEmail {
|
||||||
private static final Logger log = LoggerFactory.getLogger(OutgoingEmail.class);
|
private static final Logger log = LoggerFactory.getLogger(OutgoingEmail.class);
|
||||||
|
@@ -50,14 +50,28 @@ public class PatchFile {
|
|||||||
|
|
||||||
final RevWalk rw = new RevWalk(repo);
|
final RevWalk rw = new RevWalk(repo);
|
||||||
final RevCommit bCommit = rw.parseCommit(patchList.getNewId());
|
final RevCommit bCommit = rw.parseCommit(patchList.getNewId());
|
||||||
if (patchList.getOldId() != null) {
|
|
||||||
aTree = rw.parseTree(patchList.getOldId());
|
if (Patch.COMMIT_MSG.equals(fileName)) {
|
||||||
|
if (patchList.isAgainstParent()) {
|
||||||
|
a = Text.EMPTY;
|
||||||
|
} else {
|
||||||
|
a = Text.forCommit(repo, patchList.getOldId());
|
||||||
|
}
|
||||||
|
b = Text.forCommit(repo, bCommit);
|
||||||
|
|
||||||
|
aTree = null;
|
||||||
|
bTree = null;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
final RevCommit p = bCommit.getParent(0);
|
if (patchList.getOldId() != null) {
|
||||||
rw.parseHeaders(p);
|
aTree = rw.parseTree(patchList.getOldId());
|
||||||
aTree = p.getTree();
|
} else {
|
||||||
|
final RevCommit p = bCommit.getParent(0);
|
||||||
|
rw.parseHeaders(p);
|
||||||
|
aTree = p.getTree();
|
||||||
|
}
|
||||||
|
bTree = bCommit.getTree();
|
||||||
}
|
}
|
||||||
bTree = bCommit.getTree();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -60,15 +60,28 @@ public class PatchList implements Serializable {
|
|||||||
private transient ObjectId oldId;
|
private transient ObjectId oldId;
|
||||||
private transient ObjectId newId;
|
private transient ObjectId newId;
|
||||||
private transient boolean intralineDifference;
|
private transient boolean intralineDifference;
|
||||||
|
private transient boolean againstParent;
|
||||||
|
private transient int insertions;
|
||||||
|
private transient int deletions;
|
||||||
private transient PatchListEntry[] patches;
|
private transient PatchListEntry[] patches;
|
||||||
|
|
||||||
PatchList(@Nullable final AnyObjectId oldId, final AnyObjectId newId,
|
PatchList(@Nullable final AnyObjectId oldId, final AnyObjectId newId,
|
||||||
final boolean intralineDifference, final PatchListEntry[] patches) {
|
final boolean intralineDifference, final boolean againstParent,
|
||||||
|
final PatchListEntry[] patches) {
|
||||||
this.oldId = oldId != null ? oldId.copy() : null;
|
this.oldId = oldId != null ? oldId.copy() : null;
|
||||||
this.newId = newId.copy();
|
this.newId = newId.copy();
|
||||||
this.intralineDifference = intralineDifference;
|
this.intralineDifference = intralineDifference;
|
||||||
|
this.againstParent = againstParent;
|
||||||
|
|
||||||
|
// We assume index 0 contains the magic commit message entry.
|
||||||
|
if (patches.length > 1) {
|
||||||
|
Arrays.sort(patches, 1, patches.length, PATCH_CMP);
|
||||||
|
}
|
||||||
|
for (int i = 1; i < patches.length; i++) {
|
||||||
|
insertions += patches[i].getInsertions();
|
||||||
|
deletions += patches[i].getDeletions();
|
||||||
|
}
|
||||||
|
|
||||||
Arrays.sort(patches, PATCH_CMP);
|
|
||||||
this.patches = patches;
|
this.patches = patches;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,6 +106,21 @@ public class PatchList implements Serializable {
|
|||||||
return intralineDifference;
|
return intralineDifference;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return true if {@link #getOldId} is {@link #getNewId}'s ancestor. */
|
||||||
|
public boolean isAgainstParent() {
|
||||||
|
return againstParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return total number of new lines added. */
|
||||||
|
public int getInsertions() {
|
||||||
|
return insertions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return total number of lines removed. */
|
||||||
|
public int getDeletions() {
|
||||||
|
return deletions;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a sorted, modifiable list of all files in this list.
|
* Get a sorted, modifiable list of all files in this list.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -144,6 +172,9 @@ public class PatchList implements Serializable {
|
|||||||
writeCanBeNull(out, oldId);
|
writeCanBeNull(out, oldId);
|
||||||
writeNotNull(out, newId);
|
writeNotNull(out, newId);
|
||||||
writeVarInt32(out, intralineDifference ? 1 : 0);
|
writeVarInt32(out, intralineDifference ? 1 : 0);
|
||||||
|
writeVarInt32(out, againstParent ? 1 : 0);
|
||||||
|
writeVarInt32(out, insertions);
|
||||||
|
writeVarInt32(out, deletions);
|
||||||
writeVarInt32(out, patches.length);
|
writeVarInt32(out, patches.length);
|
||||||
for (PatchListEntry p : patches) {
|
for (PatchListEntry p : patches) {
|
||||||
p.writeTo(out);
|
p.writeTo(out);
|
||||||
@@ -161,6 +192,9 @@ public class PatchList implements Serializable {
|
|||||||
oldId = readCanBeNull(in);
|
oldId = readCanBeNull(in);
|
||||||
newId = readNotNull(in);
|
newId = readNotNull(in);
|
||||||
intralineDifference = readVarInt32(in) != 0;
|
intralineDifference = readVarInt32(in) != 0;
|
||||||
|
againstParent = readVarInt32(in) != 0;
|
||||||
|
insertions = readVarInt32(in);
|
||||||
|
deletions = readVarInt32(in);
|
||||||
final int cnt = readVarInt32(in);
|
final int cnt = readVarInt32(in);
|
||||||
final PatchListEntry[] all = new PatchListEntry[cnt];
|
final PatchListEntry[] all = new PatchListEntry[cnt];
|
||||||
for (int i = 0; i < all.length; i++) {
|
for (int i = 0; i < all.length; i++) {
|
||||||
|
@@ -62,6 +62,7 @@ package com.google.gerrit.server.patch;
|
|||||||
|
|
||||||
|
|
||||||
import com.google.gerrit.reviewdb.Change;
|
import com.google.gerrit.reviewdb.Change;
|
||||||
|
import com.google.gerrit.reviewdb.Patch;
|
||||||
import com.google.gerrit.reviewdb.PatchSet;
|
import com.google.gerrit.reviewdb.PatchSet;
|
||||||
import com.google.gerrit.reviewdb.Project;
|
import com.google.gerrit.reviewdb.Project;
|
||||||
import com.google.gerrit.reviewdb.AccountDiffPreference.Whitespace;
|
import com.google.gerrit.reviewdb.AccountDiffPreference.Whitespace;
|
||||||
@@ -80,6 +81,7 @@ import com.google.inject.name.Named;
|
|||||||
import org.eclipse.jgit.diff.DiffEntry;
|
import org.eclipse.jgit.diff.DiffEntry;
|
||||||
import org.eclipse.jgit.diff.DiffFormatter;
|
import org.eclipse.jgit.diff.DiffFormatter;
|
||||||
import org.eclipse.jgit.diff.Edit;
|
import org.eclipse.jgit.diff.Edit;
|
||||||
|
import org.eclipse.jgit.diff.EditList;
|
||||||
import org.eclipse.jgit.diff.MyersDiff;
|
import org.eclipse.jgit.diff.MyersDiff;
|
||||||
import org.eclipse.jgit.diff.RawText;
|
import org.eclipse.jgit.diff.RawText;
|
||||||
import org.eclipse.jgit.diff.RawTextIgnoreAllWhitespace;
|
import org.eclipse.jgit.diff.RawTextIgnoreAllWhitespace;
|
||||||
@@ -88,7 +90,6 @@ import org.eclipse.jgit.diff.RawTextIgnoreWhitespaceChange;
|
|||||||
import org.eclipse.jgit.diff.RenameDetector;
|
import org.eclipse.jgit.diff.RenameDetector;
|
||||||
import org.eclipse.jgit.diff.ReplaceEdit;
|
import org.eclipse.jgit.diff.ReplaceEdit;
|
||||||
import org.eclipse.jgit.errors.MissingObjectException;
|
import org.eclipse.jgit.errors.MissingObjectException;
|
||||||
import org.eclipse.jgit.lib.AnyObjectId;
|
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.Constants;
|
||||||
import org.eclipse.jgit.lib.FileMode;
|
import org.eclipse.jgit.lib.FileMode;
|
||||||
@@ -99,6 +100,7 @@ import org.eclipse.jgit.lib.Repository;
|
|||||||
import org.eclipse.jgit.patch.FileHeader;
|
import org.eclipse.jgit.patch.FileHeader;
|
||||||
import org.eclipse.jgit.patch.FileHeader.PatchType;
|
import org.eclipse.jgit.patch.FileHeader.PatchType;
|
||||||
import org.eclipse.jgit.revwalk.RevCommit;
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
|
import org.eclipse.jgit.revwalk.RevObject;
|
||||||
import org.eclipse.jgit.revwalk.RevTree;
|
import org.eclipse.jgit.revwalk.RevTree;
|
||||||
import org.eclipse.jgit.revwalk.RevWalk;
|
import org.eclipse.jgit.revwalk.RevWalk;
|
||||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||||
@@ -186,15 +188,50 @@ public class PatchListCacheImpl implements PatchListCache {
|
|||||||
final Repository repo) throws IOException {
|
final Repository repo) throws IOException {
|
||||||
// TODO(jeffschu) correctly handle merge commits
|
// TODO(jeffschu) correctly handle merge commits
|
||||||
|
|
||||||
final RevWalk rw = new RevWalk(repo);
|
RawText.Factory rawTextFactory;
|
||||||
final RevCommit b = rw.parseCommit(key.getNewId());
|
switch (key.getWhitespace()) {
|
||||||
final AnyObjectId a = aFor(key, repo, b);
|
case IGNORE_ALL_SPACE:
|
||||||
|
rawTextFactory = RawTextIgnoreAllWhitespace.FACTORY;
|
||||||
if (a == null) {
|
break;
|
||||||
return new PatchList(a, b, computeIntraline, new PatchListEntry[0]);
|
case IGNORE_SPACE_AT_EOL:
|
||||||
|
rawTextFactory = RawTextIgnoreTrailingWhitespace.FACTORY;
|
||||||
|
break;
|
||||||
|
case IGNORE_SPACE_CHANGE:
|
||||||
|
rawTextFactory = RawTextIgnoreWhitespaceChange.FACTORY;
|
||||||
|
break;
|
||||||
|
case IGNORE_NONE:
|
||||||
|
default:
|
||||||
|
rawTextFactory = RawText.FACTORY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
final RevWalk rw = new RevWalk(repo);
|
||||||
|
final RevCommit b = rw.parseCommit(key.getNewId());
|
||||||
|
final RevObject a = aFor(key, repo, rw, b);
|
||||||
|
|
||||||
|
if (a == null) {
|
||||||
|
// This is a merge commit, compared to its ancestor.
|
||||||
|
//
|
||||||
|
final PatchListEntry[] entries = new PatchListEntry[1];
|
||||||
|
entries[0] = newCommitMessage(rawTextFactory, repo, null, b);
|
||||||
|
return new PatchList(a, b, computeIntraline, true, entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
final boolean againstParent =
|
||||||
|
b.getParentCount() > 0 && b.getParent(0) == a;
|
||||||
|
|
||||||
|
RevCommit aCommit;
|
||||||
|
RevTree aTree;
|
||||||
|
if (a instanceof RevCommit) {
|
||||||
|
aCommit = (RevCommit) a;
|
||||||
|
aTree = aCommit.getTree();
|
||||||
|
} else if (a instanceof RevTree) {
|
||||||
|
aCommit = null;
|
||||||
|
aTree = (RevTree) a;
|
||||||
|
} else {
|
||||||
|
throw new IOException("Unexpected type: " + a.getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
RevTree aTree = rw.parseTree(a);
|
|
||||||
RevTree bTree = b.getTree();
|
RevTree bTree = b.getTree();
|
||||||
|
|
||||||
final TreeWalk walk = new TreeWalk(repo);
|
final TreeWalk walk = new TreeWalk(repo);
|
||||||
@@ -206,32 +243,53 @@ public class PatchListCacheImpl implements PatchListCache {
|
|||||||
|
|
||||||
DiffFormatter df = new DiffFormatter(DisabledOutputStream.INSTANCE);
|
DiffFormatter df = new DiffFormatter(DisabledOutputStream.INSTANCE);
|
||||||
df.setRepository(repo);
|
df.setRepository(repo);
|
||||||
switch (key.getWhitespace()) {
|
df.setRawTextFactory(rawTextFactory);
|
||||||
case IGNORE_ALL_SPACE:
|
|
||||||
df.setRawTextFactory(RawTextIgnoreAllWhitespace.FACTORY);
|
|
||||||
break;
|
|
||||||
case IGNORE_NONE:
|
|
||||||
df.setRawTextFactory(RawText.FACTORY);
|
|
||||||
break;
|
|
||||||
case IGNORE_SPACE_AT_EOL:
|
|
||||||
df.setRawTextFactory(RawTextIgnoreTrailingWhitespace.FACTORY);
|
|
||||||
break;
|
|
||||||
case IGNORE_SPACE_CHANGE:
|
|
||||||
df.setRawTextFactory(RawTextIgnoreWhitespaceChange.FACTORY);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
RenameDetector rd = new RenameDetector(repo);
|
RenameDetector rd = new RenameDetector(repo);
|
||||||
rd.addAll(DiffEntry.scan(walk));
|
rd.addAll(DiffEntry.scan(walk));
|
||||||
List<DiffEntry> diffEntries = rd.compute();
|
List<DiffEntry> diffEntries = rd.compute();
|
||||||
|
|
||||||
final int cnt = diffEntries.size();
|
final int cnt = diffEntries.size();
|
||||||
final PatchListEntry[] entries = new PatchListEntry[cnt];
|
final PatchListEntry[] entries = new PatchListEntry[1 + cnt];
|
||||||
|
entries[0] = newCommitMessage(rawTextFactory, repo, //
|
||||||
|
againstParent ? null : aCommit, b);
|
||||||
for (int i = 0; i < cnt; i++) {
|
for (int i = 0; i < cnt; i++) {
|
||||||
FileHeader fh = df.createFileHeader(diffEntries.get(i));
|
FileHeader fh = df.createFileHeader(diffEntries.get(i));
|
||||||
entries[i] = newEntry(repo, aTree, bTree, fh);
|
entries[1 + i] = newEntry(repo, aTree, bTree, fh);
|
||||||
}
|
}
|
||||||
return new PatchList(a, b, computeIntraline, entries);
|
return new PatchList(a, b, computeIntraline, againstParent, entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PatchListEntry newCommitMessage(
|
||||||
|
final RawText.Factory rawTextFactory, final Repository repo,
|
||||||
|
final RevCommit aCommit, final RevCommit bCommit) throws IOException {
|
||||||
|
StringBuilder hdr = new StringBuilder();
|
||||||
|
|
||||||
|
hdr.append("diff --git");
|
||||||
|
if (aCommit != null) {
|
||||||
|
hdr.append(" a/" + Patch.COMMIT_MSG);
|
||||||
|
} else {
|
||||||
|
hdr.append(" " + FileHeader.DEV_NULL);
|
||||||
|
}
|
||||||
|
hdr.append(" b/" + Patch.COMMIT_MSG);
|
||||||
|
hdr.append("\n");
|
||||||
|
|
||||||
|
if (aCommit != null) {
|
||||||
|
hdr.append("--- a/" + Patch.COMMIT_MSG + "\n");
|
||||||
|
} else {
|
||||||
|
hdr.append("--- " + FileHeader.DEV_NULL + "\n");
|
||||||
|
}
|
||||||
|
hdr.append("+++ b/" + Patch.COMMIT_MSG + "\n");
|
||||||
|
|
||||||
|
Text aText = aCommit != null ? Text.forCommit(repo, aCommit) : Text.EMPTY;
|
||||||
|
Text bText = Text.forCommit(repo, bCommit);
|
||||||
|
|
||||||
|
byte[] rawHdr = hdr.toString().getBytes("UTF-8");
|
||||||
|
RawText aRawText = rawTextFactory.create(aText.getContent());
|
||||||
|
RawText bRawText = rawTextFactory.create(bText.getContent());
|
||||||
|
EditList edits = new MyersDiff(aRawText, bRawText).getEdits();
|
||||||
|
FileHeader fh = new FileHeader(rawHdr, edits, PatchType.UNIFIED);
|
||||||
|
return newEntry(repo, aText, bText, edits, null, null, fh);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PatchListEntry newEntry(Repository repo, RevTree aTree,
|
private PatchListEntry newEntry(Repository repo, RevTree aTree,
|
||||||
@@ -263,9 +321,12 @@ public class PatchListCacheImpl implements PatchListCache {
|
|||||||
return new PatchListEntry(fileHeader, edits);
|
return new PatchListEntry(fileHeader, edits);
|
||||||
}
|
}
|
||||||
|
|
||||||
Text aContent = null;
|
return newEntry(repo, null, null, edits, aTree, bTree, fileHeader);
|
||||||
Text bContent = null;
|
}
|
||||||
|
|
||||||
|
private PatchListEntry newEntry(Repository repo, Text aContent,
|
||||||
|
Text bContent, List<Edit> edits, RevTree aTree, RevTree bTree,
|
||||||
|
FileHeader fileHeader) throws IOException {
|
||||||
for (int i = 0; i < edits.size(); i++) {
|
for (int i = 0; i < edits.size(); i++) {
|
||||||
Edit e = edits.get(i);
|
Edit e = edits.get(i);
|
||||||
|
|
||||||
@@ -545,17 +606,21 @@ public class PatchListCacheImpl implements PatchListCache {
|
|||||||
return new Text(ldr.getCachedBytes());
|
return new Text(ldr.getCachedBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AnyObjectId aFor(final PatchListKey key,
|
private static RevObject aFor(final PatchListKey key,
|
||||||
final Repository repo, final RevCommit b) throws IOException {
|
final Repository repo, final RevWalk rw, final RevCommit b)
|
||||||
|
throws IOException {
|
||||||
if (key.getOldId() != null) {
|
if (key.getOldId() != null) {
|
||||||
return key.getOldId();
|
return rw.parseAny(key.getOldId());
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (b.getParentCount()) {
|
switch (b.getParentCount()) {
|
||||||
case 0:
|
case 0:
|
||||||
return emptyTree(repo);
|
return rw.parseAny(emptyTree(repo));
|
||||||
case 1:
|
case 1:{
|
||||||
return b.getParent(0);
|
RevCommit r = b.getParent(0);
|
||||||
|
rw.parseBody(r);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
// merge commit, return null to force combined diff behavior
|
// merge commit, return null to force combined diff behavior
|
||||||
return null;
|
return null;
|
||||||
|
@@ -50,7 +50,7 @@ public class PatchListEntry {
|
|||||||
|
|
||||||
static PatchListEntry empty(final String fileName) {
|
static PatchListEntry empty(final String fileName) {
|
||||||
return new PatchListEntry(ChangeType.MODIFIED, PatchType.UNIFIED, null,
|
return new PatchListEntry(ChangeType.MODIFIED, PatchType.UNIFIED, null,
|
||||||
fileName, EMPTY_HEADER, Collections.<Edit> emptyList());
|
fileName, EMPTY_HEADER, Collections.<Edit> emptyList(), 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ChangeType changeType;
|
private final ChangeType changeType;
|
||||||
@@ -59,6 +59,8 @@ public class PatchListEntry {
|
|||||||
private final String newName;
|
private final String newName;
|
||||||
private final byte[] header;
|
private final byte[] header;
|
||||||
private final List<Edit> edits;
|
private final List<Edit> edits;
|
||||||
|
private final int insertions;
|
||||||
|
private final int deletions;
|
||||||
|
|
||||||
PatchListEntry(final FileHeader hdr, List<Edit> editList) {
|
PatchListEntry(final FileHeader hdr, List<Edit> editList) {
|
||||||
changeType = toChangeType(hdr);
|
changeType = toChangeType(hdr);
|
||||||
@@ -96,17 +98,29 @@ public class PatchListEntry {
|
|||||||
} else {
|
} else {
|
||||||
edits = Collections.unmodifiableList(editList);
|
edits = Collections.unmodifiableList(editList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ins = 0;
|
||||||
|
int del = 0;
|
||||||
|
for (Edit e : editList) {
|
||||||
|
del += e.getEndA() - e.getBeginA();
|
||||||
|
ins += e.getEndB() - e.getBeginB();
|
||||||
|
}
|
||||||
|
insertions = ins;
|
||||||
|
deletions = del;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PatchListEntry(final ChangeType changeType,
|
private PatchListEntry(final ChangeType changeType,
|
||||||
final PatchType patchType, final String oldName, final String newName,
|
final PatchType patchType, final String oldName, final String newName,
|
||||||
final byte[] header, final List<Edit> edits) {
|
final byte[] header, final List<Edit> edits, final int insertions,
|
||||||
|
final int deletions) {
|
||||||
this.changeType = changeType;
|
this.changeType = changeType;
|
||||||
this.patchType = patchType;
|
this.patchType = patchType;
|
||||||
this.oldName = oldName;
|
this.oldName = oldName;
|
||||||
this.newName = newName;
|
this.newName = newName;
|
||||||
this.header = header;
|
this.header = header;
|
||||||
this.edits = edits;
|
this.edits = edits;
|
||||||
|
this.insertions = insertions;
|
||||||
|
this.deletions = deletions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChangeType getChangeType() {
|
public ChangeType getChangeType() {
|
||||||
@@ -129,6 +143,14 @@ public class PatchListEntry {
|
|||||||
return edits;
|
return edits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getInsertions() {
|
||||||
|
return insertions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDeletions() {
|
||||||
|
return deletions;
|
||||||
|
}
|
||||||
|
|
||||||
public List<String> getHeaderLines() {
|
public List<String> getHeaderLines() {
|
||||||
final IntList m = RawParseUtils.lineMap(header, 0, header.length);
|
final IntList m = RawParseUtils.lineMap(header, 0, header.length);
|
||||||
final List<String> headerLines = new ArrayList<String>(m.size() - 1);
|
final List<String> headerLines = new ArrayList<String>(m.size() - 1);
|
||||||
@@ -145,6 +167,8 @@ public class PatchListEntry {
|
|||||||
p.setChangeType(getChangeType());
|
p.setChangeType(getChangeType());
|
||||||
p.setPatchType(getPatchType());
|
p.setPatchType(getPatchType());
|
||||||
p.setSourceFileName(getOldName());
|
p.setSourceFileName(getOldName());
|
||||||
|
p.setInsertions(insertions);
|
||||||
|
p.setDeletions(deletions);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,6 +178,8 @@ public class PatchListEntry {
|
|||||||
writeString(out, oldName);
|
writeString(out, oldName);
|
||||||
writeString(out, newName);
|
writeString(out, newName);
|
||||||
writeBytes(out, header);
|
writeBytes(out, header);
|
||||||
|
writeVarInt32(out, insertions);
|
||||||
|
writeVarInt32(out, deletions);
|
||||||
|
|
||||||
writeVarInt32(out, edits.size());
|
writeVarInt32(out, edits.size());
|
||||||
for (final Edit e : edits) {
|
for (final Edit e : edits) {
|
||||||
@@ -184,6 +210,8 @@ public class PatchListEntry {
|
|||||||
final String oldName = readString(in);
|
final String oldName = readString(in);
|
||||||
final String newName = readString(in);
|
final String newName = readString(in);
|
||||||
final byte[] hdr = readBytes(in);
|
final byte[] hdr = readBytes(in);
|
||||||
|
final int ins = readVarInt32(in);
|
||||||
|
final int del = readVarInt32(in);
|
||||||
|
|
||||||
final int editCount = readVarInt32(in);
|
final int editCount = readVarInt32(in);
|
||||||
final Edit[] editArray = new Edit[editCount];
|
final Edit[] editArray = new Edit[editCount];
|
||||||
@@ -201,7 +229,7 @@ public class PatchListEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new PatchListEntry(changeType, patchType, oldName, newName, hdr,
|
return new PatchListEntry(changeType, patchType, oldName, newName, hdr,
|
||||||
toList(editArray));
|
toList(editArray), ins, del);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Edit> toList(Edit[] l) {
|
private static List<Edit> toList(Edit[] l) {
|
||||||
@@ -213,7 +241,7 @@ public class PatchListEntry {
|
|||||||
final int endA = readVarInt32(in);
|
final int endA = readVarInt32(in);
|
||||||
final int beginB = readVarInt32(in);
|
final int beginB = readVarInt32(in);
|
||||||
final int endB = readVarInt32(in);
|
final int endB = readVarInt32(in);
|
||||||
return new Edit(beginA, endA, beginB, endB);
|
return new Edit(beginA, endA, beginB, endB);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] compact(final FileHeader h) {
|
private static byte[] compact(final FileHeader h) {
|
||||||
|
@@ -35,7 +35,7 @@ import java.io.Serializable;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public class PatchListKey implements Serializable {
|
public class PatchListKey implements Serializable {
|
||||||
static final long serialVersionUID = 13L;
|
static final long serialVersionUID = 15L;
|
||||||
|
|
||||||
private transient ObjectId oldId;
|
private transient ObjectId oldId;
|
||||||
private transient ObjectId newId;
|
private transient ObjectId newId;
|
||||||
|
@@ -17,7 +17,12 @@ package com.google.gerrit.server.patch;
|
|||||||
import org.eclipse.jgit.diff.RawText;
|
import org.eclipse.jgit.diff.RawText;
|
||||||
import org.eclipse.jgit.errors.LargeObjectException;
|
import org.eclipse.jgit.errors.LargeObjectException;
|
||||||
import org.eclipse.jgit.errors.MissingObjectException;
|
import org.eclipse.jgit.errors.MissingObjectException;
|
||||||
|
import org.eclipse.jgit.lib.AnyObjectId;
|
||||||
import org.eclipse.jgit.lib.ObjectLoader;
|
import org.eclipse.jgit.lib.ObjectLoader;
|
||||||
|
import org.eclipse.jgit.lib.PersonIdent;
|
||||||
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
|
import org.eclipse.jgit.revwalk.RevWalk;
|
||||||
import org.eclipse.jgit.util.IO;
|
import org.eclipse.jgit.util.IO;
|
||||||
import org.eclipse.jgit.util.RawParseUtils;
|
import org.eclipse.jgit.util.RawParseUtils;
|
||||||
import org.mozilla.universalchardet.UniversalDetector;
|
import org.mozilla.universalchardet.UniversalDetector;
|
||||||
@@ -29,6 +34,7 @@ import java.io.InputStream;
|
|||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.IllegalCharsetNameException;
|
import java.nio.charset.IllegalCharsetNameException;
|
||||||
import java.nio.charset.UnsupportedCharsetException;
|
import java.nio.charset.UnsupportedCharsetException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
|
||||||
public class Text extends RawText {
|
public class Text extends RawText {
|
||||||
private static final Logger log = LoggerFactory.getLogger(Text.class);
|
private static final Logger log = LoggerFactory.getLogger(Text.class);
|
||||||
@@ -38,6 +44,75 @@ public class Text extends RawText {
|
|||||||
public static final byte[] NO_BYTES = {};
|
public static final byte[] NO_BYTES = {};
|
||||||
public static final Text EMPTY = new Text(NO_BYTES);
|
public static final Text EMPTY = new Text(NO_BYTES);
|
||||||
|
|
||||||
|
public static Text forCommit(Repository db, AnyObjectId commitId)
|
||||||
|
throws IOException {
|
||||||
|
RevWalk rw = new RevWalk(db);
|
||||||
|
try {
|
||||||
|
RevCommit c;
|
||||||
|
if (commitId instanceof RevCommit) {
|
||||||
|
c = (RevCommit) commitId;
|
||||||
|
} else {
|
||||||
|
c = rw.parseCommit(commitId);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
switch (c.getParentCount()) {
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case 1: {
|
||||||
|
RevCommit p = c.getParent(0);
|
||||||
|
rw.parseBody(p);
|
||||||
|
b.append("Parent: ");
|
||||||
|
b.append(p.abbreviate(db, 8).name());
|
||||||
|
b.append(" (");
|
||||||
|
b.append(p.getShortMessage());
|
||||||
|
b.append(")\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
for (int i = 0; i < c.getParentCount(); i++) {
|
||||||
|
RevCommit p = c.getParent(i);
|
||||||
|
rw.parseBody(p);
|
||||||
|
b.append(i == 0 ? "Merge Of: " : " ");
|
||||||
|
b.append(p.abbreviate(db, 8).name());
|
||||||
|
b.append(" (");
|
||||||
|
b.append(p.getShortMessage());
|
||||||
|
b.append(")\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
appendPersonIdent(b, "Author", c.getAuthorIdent());
|
||||||
|
appendPersonIdent(b, "Commit", c.getCommitterIdent());
|
||||||
|
b.append("\n");
|
||||||
|
b.append(c.getFullMessage());
|
||||||
|
return new Text(b.toString().getBytes("UTF-8"));
|
||||||
|
} finally {
|
||||||
|
rw.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void appendPersonIdent(StringBuilder b, String field,
|
||||||
|
PersonIdent person) {
|
||||||
|
if (person != null) {
|
||||||
|
b.append(field + ": ");
|
||||||
|
if (person.getName() != null) {
|
||||||
|
b.append(" ");
|
||||||
|
b.append(person.getName());
|
||||||
|
}
|
||||||
|
if (person.getEmailAddress() != null) {
|
||||||
|
b.append(" <");
|
||||||
|
b.append(person.getEmailAddress());
|
||||||
|
b.append(">");
|
||||||
|
}
|
||||||
|
b.append("\n");
|
||||||
|
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ZZZ");
|
||||||
|
sdf.setTimeZone(person.getTimeZone());
|
||||||
|
b.append(field + "Date: ");
|
||||||
|
b.append(sdf.format(person.getWhen()));
|
||||||
|
b.append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static String asString(byte[] content, String encoding) {
|
public static String asString(byte[] content, String encoding) {
|
||||||
return new String(content, charset(content, encoding));
|
return new String(content, charset(content, encoding));
|
||||||
}
|
}
|
||||||
|
@@ -177,16 +177,17 @@ public class ProjectControl {
|
|||||||
short requireValue) {
|
short requireValue) {
|
||||||
final Set<AccountGroup.Id> groups = user.getEffectiveGroups();
|
final Set<AccountGroup.Id> groups = user.getEffectiveGroups();
|
||||||
int val = Integer.MIN_VALUE;
|
int val = Integer.MIN_VALUE;
|
||||||
boolean local = false;
|
|
||||||
|
|
||||||
for (final RefRight pr : state.getLocalRights(actionId)) {
|
for (final RefRight pr : state.getLocalRights(actionId)) {
|
||||||
if (groups.contains(pr.getAccountGroupId())) {
|
if (groups.contains(pr.getAccountGroupId())) {
|
||||||
val = Math.max(pr.getMaxValue(), val);
|
val = Math.max(pr.getMaxValue(), val);
|
||||||
local = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (val >= requireValue) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!local && actionId.canInheritFromWildProject()) {
|
if (actionId.canInheritFromWildProject()) {
|
||||||
for (final RefRight pr : state.getInheritedRights(actionId)) {
|
for (final RefRight pr : state.getInheritedRights(actionId)) {
|
||||||
if (groups.contains(pr.getAccountGroupId())) {
|
if (groups.contains(pr.getAccountGroupId())) {
|
||||||
val = Math.max(pr.getMaxValue(), val);
|
val = Math.max(pr.getMaxValue(), val);
|
||||||
|
@@ -97,6 +97,10 @@ public class ProjectState {
|
|||||||
return inheritedRights;
|
return inheritedRights;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setInheritedRights(Collection<RefRight> all) {
|
||||||
|
inheritedRights = all;
|
||||||
|
}
|
||||||
|
|
||||||
private Collection<RefRight> computeInheritedRights() {
|
private Collection<RefRight> computeInheritedRights() {
|
||||||
if (isSpecialWildProject()) {
|
if (isSpecialWildProject()) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
|
@@ -576,6 +576,6 @@ public class RefControl {
|
|||||||
if (isRE(refPattern)) {
|
if (isRE(refPattern)) {
|
||||||
refPattern = refPattern.substring(1);
|
refPattern = refPattern.substring(1);
|
||||||
}
|
}
|
||||||
return new RegExp(refPattern);
|
return new RegExp(refPattern, RegExp.NONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
package com.google.gerrit.server.query.change;
|
package com.google.gerrit.server.query.change;
|
||||||
|
|
||||||
import com.google.gerrit.reviewdb.Change;
|
import com.google.gerrit.reviewdb.Change;
|
||||||
|
import com.google.gerrit.reviewdb.Patch;
|
||||||
import com.google.gerrit.reviewdb.PatchLineComment;
|
import com.google.gerrit.reviewdb.PatchLineComment;
|
||||||
import com.google.gerrit.reviewdb.PatchSet;
|
import com.google.gerrit.reviewdb.PatchSet;
|
||||||
import com.google.gerrit.reviewdb.PatchSetApproval;
|
import com.google.gerrit.reviewdb.PatchSetApproval;
|
||||||
@@ -28,6 +29,7 @@ import com.google.gwtorm.client.OrmException;
|
|||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -38,7 +40,7 @@ public class ChangeData {
|
|||||||
private Collection<PatchSet> patches;
|
private Collection<PatchSet> patches;
|
||||||
private Collection<PatchSetApproval> approvals;
|
private Collection<PatchSetApproval> approvals;
|
||||||
private Collection<PatchSetApproval> currentApprovals;
|
private Collection<PatchSetApproval> currentApprovals;
|
||||||
private Collection<String> currentFiles;
|
private String[] currentFiles;
|
||||||
private Collection<PatchLineComment> comments;
|
private Collection<PatchLineComment> comments;
|
||||||
private Collection<TrackingId> trackingIds;
|
private Collection<TrackingId> trackingIds;
|
||||||
private CurrentUser visibleTo;
|
private CurrentUser visibleTo;
|
||||||
@@ -52,11 +54,11 @@ public class ChangeData {
|
|||||||
change = c;
|
change = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCurrentFilePaths(Collection<String> filePaths) {
|
public void setCurrentFilePaths(String[] filePaths) {
|
||||||
currentFiles = filePaths;
|
currentFiles = filePaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<String> currentFilePaths(Provider<ReviewDb> db,
|
public String[] currentFilePaths(Provider<ReviewDb> db,
|
||||||
PatchListCache cache) throws OrmException {
|
PatchListCache cache) throws OrmException {
|
||||||
if (currentFiles == null) {
|
if (currentFiles == null) {
|
||||||
Change c = change(db);
|
Change c = change(db);
|
||||||
@@ -71,22 +73,24 @@ public class ChangeData {
|
|||||||
PatchList p = cache.get(c, ps);
|
PatchList p = cache.get(c, ps);
|
||||||
List<String> r = new ArrayList<String>(p.getPatches().size());
|
List<String> r = new ArrayList<String>(p.getPatches().size());
|
||||||
for (PatchListEntry e : p.getPatches()) {
|
for (PatchListEntry e : p.getPatches()) {
|
||||||
|
if (Patch.COMMIT_MSG.equals(e.getNewName())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
switch (e.getChangeType()) {
|
switch (e.getChangeType()) {
|
||||||
case ADDED:
|
case ADDED:
|
||||||
case MODIFIED:
|
case MODIFIED:
|
||||||
|
case DELETED:
|
||||||
case COPIED:
|
case COPIED:
|
||||||
r.add(e.getNewName());
|
r.add(e.getNewName());
|
||||||
break;
|
break;
|
||||||
case DELETED:
|
|
||||||
r.add(e.getOldName());
|
|
||||||
break;
|
|
||||||
case RENAMED:
|
case RENAMED:
|
||||||
r.add(e.getOldName());
|
r.add(e.getOldName());
|
||||||
r.add(e.getNewName());
|
r.add(e.getNewName());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentFiles = r;
|
currentFiles = r.toArray(new String[r.size()]);
|
||||||
|
Arrays.sort(currentFiles);
|
||||||
}
|
}
|
||||||
return currentFiles;
|
return currentFiles;
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,16 @@
|
|||||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
// Copyright (C) 2010 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.query.change;
|
package com.google.gerrit.server.query.change;
|
||||||
|
|
||||||
@@ -8,35 +20,75 @@ import com.google.gerrit.server.query.OperatorPredicate;
|
|||||||
import com.google.gwtorm.client.OrmException;
|
import com.google.gwtorm.client.OrmException;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
|
|
||||||
import java.util.Collection;
|
import dk.brics.automaton.Automaton;
|
||||||
import java.util.regex.Pattern;
|
import dk.brics.automaton.RegExp;
|
||||||
import java.util.regex.PatternSyntaxException;
|
import dk.brics.automaton.RunAutomaton;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
class RegexFilePredicate extends OperatorPredicate<ChangeData> {
|
class RegexFilePredicate extends OperatorPredicate<ChangeData> {
|
||||||
private final Provider<ReviewDb> db;
|
private final Provider<ReviewDb> db;
|
||||||
private final PatchListCache cache;
|
private final PatchListCache cache;
|
||||||
private final Pattern pattern;
|
private final RunAutomaton pattern;
|
||||||
|
|
||||||
|
private final String prefixBegin;
|
||||||
|
private final String prefixEnd;
|
||||||
|
private final int prefixLen;
|
||||||
|
private final boolean prefixOnly;
|
||||||
|
|
||||||
RegexFilePredicate(Provider<ReviewDb> db, PatchListCache plc, String re) {
|
RegexFilePredicate(Provider<ReviewDb> db, PatchListCache plc, String re) {
|
||||||
super(ChangeQueryBuilder.FIELD_FILE, re);
|
super(ChangeQueryBuilder.FIELD_FILE, re);
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.cache = plc;
|
this.cache = plc;
|
||||||
try {
|
|
||||||
this.pattern = Pattern.compile(re);
|
if (re.startsWith("^")) {
|
||||||
} catch (PatternSyntaxException e) {
|
re = re.substring(1);
|
||||||
throw new IllegalArgumentException(e.getMessage());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (re.endsWith("$") && !re.endsWith("\\$")) {
|
||||||
|
re = re.substring(0, re.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Automaton automaton = new RegExp(re).toAutomaton();
|
||||||
|
prefixBegin = automaton.getCommonPrefix();
|
||||||
|
prefixLen = prefixBegin.length();
|
||||||
|
|
||||||
|
if (0 < prefixLen) {
|
||||||
|
char max = (char) (prefixBegin.charAt(prefixLen - 1) + 1);
|
||||||
|
prefixEnd = prefixBegin.substring(0, prefixLen - 1) + max;
|
||||||
|
prefixOnly = re.equals(prefixBegin + ".*");
|
||||||
|
} else {
|
||||||
|
prefixEnd = "";
|
||||||
|
prefixOnly = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pattern = prefixOnly ? null : new RunAutomaton(automaton);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean match(ChangeData object) throws OrmException {
|
public boolean match(ChangeData object) throws OrmException {
|
||||||
Collection<String> files = object.currentFilePaths(db, cache);
|
String[] files = object.currentFilePaths(db, cache);
|
||||||
if (files != null) {
|
if (files != null) {
|
||||||
for (String path : files) {
|
int begin, end;
|
||||||
if (pattern.matcher(path).find()) {
|
|
||||||
|
if (0 < prefixLen) {
|
||||||
|
begin = find(files, prefixBegin);
|
||||||
|
end = find(files, prefixEnd);
|
||||||
|
} else {
|
||||||
|
begin = 0;
|
||||||
|
end = files.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prefixOnly) {
|
||||||
|
return begin < end;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (begin < end) {
|
||||||
|
if (pattern.run(files[begin++])) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -48,6 +100,11 @@ class RegexFilePredicate extends OperatorPredicate<ChangeData> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int find(String[] files, String p) {
|
||||||
|
int r = Arrays.binarySearch(files, p);
|
||||||
|
return r < 0 ? -(r + 1) : r;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getCost() {
|
public int getCost() {
|
||||||
return 1;
|
return 1;
|
||||||
|
@@ -1,4 +1,16 @@
|
|||||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
// Copyright (C) 2010 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.query.change;
|
package com.google.gerrit.server.query.change;
|
||||||
|
|
||||||
|
@@ -0,0 +1,258 @@
|
|||||||
|
// Copyright (C) 2010 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.project;
|
||||||
|
|
||||||
|
import static com.google.gerrit.reviewdb.ApprovalCategory.OWN;
|
||||||
|
import static com.google.gerrit.reviewdb.ApprovalCategory.READ;
|
||||||
|
|
||||||
|
import com.google.gerrit.reviewdb.AccountGroup;
|
||||||
|
import com.google.gerrit.reviewdb.AccountProjectWatch;
|
||||||
|
import com.google.gerrit.reviewdb.ApprovalCategory;
|
||||||
|
import com.google.gerrit.reviewdb.Change;
|
||||||
|
import com.google.gerrit.reviewdb.Project;
|
||||||
|
import com.google.gerrit.reviewdb.RefRight;
|
||||||
|
import com.google.gerrit.reviewdb.SystemConfig;
|
||||||
|
import com.google.gerrit.reviewdb.RefRight.RefPattern;
|
||||||
|
import com.google.gerrit.server.AccessPath;
|
||||||
|
import com.google.gerrit.server.AnonymousUser;
|
||||||
|
import com.google.gerrit.server.CurrentUser;
|
||||||
|
import com.google.gerrit.server.config.AuthConfig;
|
||||||
|
import com.google.gerrit.server.config.GerritServerConfig;
|
||||||
|
import com.google.inject.AbstractModule;
|
||||||
|
import com.google.inject.Guice;
|
||||||
|
import com.google.inject.Injector;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
import org.eclipse.jgit.lib.Config;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class RefControlTest extends TestCase {
|
||||||
|
public void testOwnerProject() {
|
||||||
|
local.add(grant(OWN, admin, "refs/*", 1));
|
||||||
|
|
||||||
|
ProjectControl uBlah = user(devs);
|
||||||
|
ProjectControl uAdmin = user(devs, admin);
|
||||||
|
|
||||||
|
assertFalse("not owner", uBlah.isOwner());
|
||||||
|
assertTrue("is owner", uAdmin.isOwner());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testBranchDelegation1() {
|
||||||
|
local.add(grant(OWN, admin, "refs/*", 1));
|
||||||
|
local.add(grant(OWN, devs, "refs/heads/x/*", 1));
|
||||||
|
|
||||||
|
ProjectControl uDev = user(devs);
|
||||||
|
assertFalse("not owner", uDev.isOwner());
|
||||||
|
assertTrue("owns ref", uDev.isOwnerAnyRef());
|
||||||
|
|
||||||
|
assertOwner("refs/heads/x/*", uDev);
|
||||||
|
assertOwner("refs/heads/x/y", uDev);
|
||||||
|
assertOwner("refs/heads/x/y/*", uDev);
|
||||||
|
|
||||||
|
assertNotOwner("refs/*", uDev);
|
||||||
|
assertNotOwner("refs/heads/master", uDev);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testBranchDelegation2() {
|
||||||
|
local.add(grant(OWN, admin, "refs/*", 1));
|
||||||
|
local.add(grant(OWN, devs, "refs/heads/x/*", 1));
|
||||||
|
local.add(grant(OWN, fixers, "-refs/heads/x/y/*", 1));
|
||||||
|
|
||||||
|
ProjectControl uDev = user(devs);
|
||||||
|
assertFalse("not owner", uDev.isOwner());
|
||||||
|
assertTrue("owns ref", uDev.isOwnerAnyRef());
|
||||||
|
|
||||||
|
assertOwner("refs/heads/x/*", uDev);
|
||||||
|
assertOwner("refs/heads/x/y", uDev);
|
||||||
|
assertNotOwner("refs/*", uDev);
|
||||||
|
assertNotOwner("refs/heads/master", uDev);
|
||||||
|
assertNotOwner("refs/heads/x/y/*", uDev);
|
||||||
|
|
||||||
|
ProjectControl uFix = user(fixers);
|
||||||
|
assertFalse("not owner", uFix.isOwner());
|
||||||
|
assertTrue("owns ref", uFix.isOwnerAnyRef());
|
||||||
|
|
||||||
|
assertOwner("refs/heads/x/y/*", uFix);
|
||||||
|
assertOwner("refs/heads/x/y/bar", uFix);
|
||||||
|
assertNotOwner("refs/heads/x/*", uFix);
|
||||||
|
assertNotOwner("refs/heads/x/y", uFix);
|
||||||
|
assertNotOwner("refs/*", uFix);
|
||||||
|
assertNotOwner("refs/heads/master", uFix);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testInheritRead_SingleBranchDeniesUpload() {
|
||||||
|
inherited.add(grant(READ, registered, "refs/*", 1, 2));
|
||||||
|
local.add(grant(READ, registered, "-refs/heads/foobar", 1, 1));
|
||||||
|
|
||||||
|
ProjectControl u = user();
|
||||||
|
assertTrue("can upload", u.canUploadToAtLeastOneRef());
|
||||||
|
|
||||||
|
assertTrue("can upload refs/heads/master", //
|
||||||
|
u.controlForRef("refs/heads/master").canUpload());
|
||||||
|
|
||||||
|
assertFalse("deny refs/heads/foobar", //
|
||||||
|
u.controlForRef("refs/heads/foobar").canUpload());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testInheritRead_SingleBranchDoesNotOverrideInherited() {
|
||||||
|
inherited.add(grant(READ, registered, "refs/*", 1, 2));
|
||||||
|
local.add(grant(READ, registered, "refs/heads/foobar", 1, 1));
|
||||||
|
|
||||||
|
ProjectControl u = user();
|
||||||
|
assertTrue("can upload", u.canUploadToAtLeastOneRef());
|
||||||
|
|
||||||
|
assertTrue("can upload refs/heads/master", //
|
||||||
|
u.controlForRef("refs/heads/master").canUpload());
|
||||||
|
|
||||||
|
assertTrue("can upload refs/heads/foobar", //
|
||||||
|
u.controlForRef("refs/heads/foobar").canUpload());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCannotUploadToAnyRef() {
|
||||||
|
inherited.add(grant(READ, registered, "refs/*", 1, 1));
|
||||||
|
local.add(grant(READ, devs, "refs/heads/*",1,2));
|
||||||
|
|
||||||
|
ProjectControl u = user();
|
||||||
|
assertFalse("cannot upload", u.canUploadToAtLeastOneRef());
|
||||||
|
assertFalse("cannot upload refs/heads/master", //
|
||||||
|
u.controlForRef("refs/heads/master").canUpload());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
private final Project.NameKey projectNameKey = new Project.NameKey("test");
|
||||||
|
private final AccountGroup.Id admin = new AccountGroup.Id(1);
|
||||||
|
private final AccountGroup.Id anonymous = new AccountGroup.Id(2);
|
||||||
|
private final AccountGroup.Id registered = new AccountGroup.Id(3);
|
||||||
|
|
||||||
|
private final AccountGroup.Id devs = new AccountGroup.Id(4);
|
||||||
|
private final AccountGroup.Id fixers = new AccountGroup.Id(5);
|
||||||
|
|
||||||
|
private final AuthConfig authConfig;
|
||||||
|
private final AnonymousUser anonymousUser;
|
||||||
|
|
||||||
|
public RefControlTest() {
|
||||||
|
final SystemConfig systemConfig = SystemConfig.create();
|
||||||
|
systemConfig.adminGroupId = admin;
|
||||||
|
systemConfig.anonymousGroupId = anonymous;
|
||||||
|
systemConfig.registeredGroupId = registered;
|
||||||
|
systemConfig.batchUsersGroupId = anonymous;
|
||||||
|
try {
|
||||||
|
byte[] bin = "abcdefghijklmnopqrstuvwxyz".getBytes("UTF-8");
|
||||||
|
systemConfig.registerEmailPrivateKey = Base64.encodeBase64String(bin);
|
||||||
|
} catch (UnsupportedEncodingException err) {
|
||||||
|
throw new RuntimeException("Cannot encode key", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
Injector injector = Guice.createInjector(new AbstractModule() {
|
||||||
|
@Override
|
||||||
|
protected void configure() {
|
||||||
|
bind(Config.class) //
|
||||||
|
.annotatedWith(GerritServerConfig.class) //
|
||||||
|
.toInstance(new Config());
|
||||||
|
|
||||||
|
bind(SystemConfig.class).toInstance(systemConfig);
|
||||||
|
bind(AuthConfig.class);
|
||||||
|
bind(AnonymousUser.class);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
authConfig = injector.getInstance(AuthConfig.class);
|
||||||
|
anonymousUser = injector.getInstance(AnonymousUser.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<RefRight> local;
|
||||||
|
private List<RefRight> inherited;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
local = new ArrayList<RefRight>();
|
||||||
|
inherited = new ArrayList<RefRight>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertOwner(String ref, ProjectControl u) {
|
||||||
|
assertTrue("OWN " + ref, u.controlForRef(ref).isOwner());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertNotOwner(String ref, ProjectControl u) {
|
||||||
|
assertFalse("NOT OWN " + ref, u.controlForRef(ref).isOwner());
|
||||||
|
}
|
||||||
|
|
||||||
|
private RefRight grant(ApprovalCategory.Id categoryId, AccountGroup.Id group,
|
||||||
|
String ref, int maxValue) {
|
||||||
|
return grant(categoryId, group, ref, maxValue, maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RefRight grant(ApprovalCategory.Id categoryId, AccountGroup.Id group,
|
||||||
|
String ref, int minValue, int maxValue) {
|
||||||
|
RefRight right =
|
||||||
|
new RefRight(new RefRight.Key(projectNameKey, new RefPattern(ref),
|
||||||
|
categoryId, group));
|
||||||
|
right.setMinValue((short) minValue);
|
||||||
|
right.setMaxValue((short) maxValue);
|
||||||
|
return right;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProjectControl user(AccountGroup.Id... memberOf) {
|
||||||
|
return new ProjectControl(new MockUser(memberOf), newProjectState());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProjectState newProjectState() {
|
||||||
|
ProjectCache projectCache = null;
|
||||||
|
Project.NameKey wildProject = null;
|
||||||
|
ProjectState ps =
|
||||||
|
new ProjectState(anonymousUser, projectCache, wildProject, new Project(
|
||||||
|
projectNameKey), local);
|
||||||
|
ps.setInheritedRights(inherited);
|
||||||
|
return ps;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MockUser extends CurrentUser {
|
||||||
|
private final Set<AccountGroup.Id> groups;
|
||||||
|
|
||||||
|
MockUser(AccountGroup.Id[] groupId) {
|
||||||
|
super(AccessPath.UNKNOWN, RefControlTest.this.authConfig);
|
||||||
|
groups = new HashSet<AccountGroup.Id>(Arrays.asList(groupId));
|
||||||
|
groups.add(registered);
|
||||||
|
groups.add(anonymous);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<AccountGroup.Id> getEffectiveGroups() {
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Change.Id> getStarredChanges() {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<AccountProjectWatch> getNotificationFilters() {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,82 @@
|
|||||||
|
// Copyright (C) 2010 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.query.change;
|
||||||
|
|
||||||
|
import com.google.gerrit.reviewdb.Change;
|
||||||
|
import com.google.gwtorm.client.OrmException;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class RegexFilePredicateTest extends TestCase {
|
||||||
|
public void testPrefixOnlyOptimization() throws OrmException {
|
||||||
|
RegexFilePredicate p = predicate("^a/b/.*");
|
||||||
|
assertTrue(p.match(change("a/b/source.c")));
|
||||||
|
assertFalse(p.match(change("source.c")));
|
||||||
|
|
||||||
|
assertTrue(p.match(change("a/b/source.c", "a/c/test")));
|
||||||
|
assertFalse(p.match(change("a/bb/source.c")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testPrefixReducesSearchSpace() throws OrmException {
|
||||||
|
RegexFilePredicate p = predicate("^a/b/.*\\.[ch]");
|
||||||
|
assertTrue(p.match(change("a/b/source.c")));
|
||||||
|
assertFalse(p.match(change("a/b/source.res")));
|
||||||
|
assertFalse(p.match(change("source.res")));
|
||||||
|
|
||||||
|
assertTrue(p.match(change("a/b/a.a", "a/b/a.d", "a/b/a.h")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testFileExtension_Constant() throws OrmException {
|
||||||
|
RegexFilePredicate p = predicate("^.*\\.res");
|
||||||
|
assertTrue(p.match(change("test.res")));
|
||||||
|
assertTrue(p.match(change("foo/bar/test.res")));
|
||||||
|
assertFalse(p.match(change("test.res.bar")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testFileExtension_CharacterGroup() throws OrmException {
|
||||||
|
RegexFilePredicate p = predicate("^.*\\.[ch]");
|
||||||
|
assertTrue(p.match(change("test.c")));
|
||||||
|
assertTrue(p.match(change("test.h")));
|
||||||
|
assertFalse(p.match(change("test.C")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEndOfString() throws OrmException {
|
||||||
|
assertTrue(predicate("^a$").match(change("a")));
|
||||||
|
assertFalse(predicate("^a$").match(change("a$")));
|
||||||
|
|
||||||
|
assertFalse(predicate("^a\\$").match(change("a")));
|
||||||
|
assertTrue(predicate("^a\\$").match(change("a$")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testExactMatch() throws OrmException {
|
||||||
|
RegexFilePredicate p = predicate("^foo.c");
|
||||||
|
assertTrue(p.match(change("foo.c")));
|
||||||
|
assertFalse(p.match(change("foo.cc")));
|
||||||
|
assertFalse(p.match(change("bar.c")));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RegexFilePredicate predicate(String pattern) {
|
||||||
|
return new RegexFilePredicate(null, null, pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ChangeData change(String... files) {
|
||||||
|
Arrays.sort(files);
|
||||||
|
ChangeData cd = new ChangeData(new Change.Id(1));
|
||||||
|
cd.setCurrentFilePaths(files);
|
||||||
|
return cd;
|
||||||
|
}
|
||||||
|
}
|
2
pom.xml
2
pom.xml
@@ -49,7 +49,7 @@ limitations under the License.
|
|||||||
<jgitVersion>0.8.4.242-g09130b8</jgitVersion>
|
<jgitVersion>0.8.4.242-g09130b8</jgitVersion>
|
||||||
<gwtormVersion>1.1.4</gwtormVersion>
|
<gwtormVersion>1.1.4</gwtormVersion>
|
||||||
<gwtjsonrpcVersion>1.2.2</gwtjsonrpcVersion>
|
<gwtjsonrpcVersion>1.2.2</gwtjsonrpcVersion>
|
||||||
<gwtexpuiVersion>1.2.1</gwtexpuiVersion>
|
<gwtexpuiVersion>1.2.2</gwtexpuiVersion>
|
||||||
<gwtVersion>2.0.4</gwtVersion>
|
<gwtVersion>2.0.4</gwtVersion>
|
||||||
<slf4jVersion>1.6.1</slf4jVersion>
|
<slf4jVersion>1.6.1</slf4jVersion>
|
||||||
<guiceVersion>2.0</guiceVersion>
|
<guiceVersion>2.0</guiceVersion>
|
||||||
|
Reference in New Issue
Block a user