Merge branch 'stable-2.11' into stable-2.12

* stable-2.11:
  Fix Postgresql JDBC driver leaking memory

Change-Id: Ie4c0ff60f098fbc606aba56a027ff8a0884e3a5d
diff --git a/.buckconfig b/.buckconfig
index e4a19f1..51318f3 100644
--- a/.buckconfig
+++ b/.buckconfig
@@ -1,5 +1,4 @@
 [alias]
-  all = //:all
   api = //:api
   api_deploy = //tools/maven:api_deploy
   api_install = //tools/maven:api_install
@@ -9,9 +8,11 @@
   docs = //Documentation:searchfree
   firefox = //:firefox
   gerrit = //:gerrit
+  headless = //:headless
   release = //:release
   safari = //:safari
   soyc = //gerrit-gwtui:ui_soyc
+  soyc_r = //gerrit-gwtui:ui_soyc_r
   withdocs = //:withdocs
 
 [buildfile]
@@ -25,4 +26,4 @@
 
 [cache]
   mode = dir
-  dir = ~/.gerritcodereview/buck-cache/cache
+  dir = ~/.gerritcodereview/buck-cache/locally-built-artifacts
diff --git a/.buckversion b/.buckversion
index 9c09744..9daac2c 100644
--- a/.buckversion
+++ b/.buckversion
@@ -1 +1 @@
-79d36de9f5284f6e833cca81867d6088a25685fb
+1b03b4313b91b634bd604fc3487a05f877e59dee
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..1f149cf
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,11 @@
+# http://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+[*]
+end_of_line = lf
+insert_final_newline = true
+charset = utf-8
+indent_style = space
+indent_size = 2
diff --git a/.gitignore b/.gitignore
index c30cee6..32a1826 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,5 +19,7 @@
 /local.properties
 *.pyc
 /gwt-unitCache
+.DS_Store
 *.swp
 *.asc
+/bin/
diff --git a/.mailmap b/.mailmap
index 75aea08..c8e2f82 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1,21 +1,43 @@
-Adrian Görler <adrian.goerler@sap.com>        Adrian Goerler <adrian.goerler@sap.com>
-Alex Ryazantsev <alex.ryazantsev@gmail.com>   alex <alex.ryazantsev@gmail.com>
-Alex Ryazantsev <alex.ryazantsev@gmail.com>   alex.ryazantsev <alex.ryazantsev@gmail.com>
-Carlos Eduardo Baldacin <carloseduardo.baldacin@sonyericsson.com> carloseduardo.baldacin <carloseduardo.baldacin@sonyericsson.com>
-Deniz Türkoglu <deniz@spotify.com>            Deniz Turkoglu <deniz@spotify.com>
-Deniz Türkoglu <deniz@spotify.com>            Deniz Türkoglu <deniz@spotify.com>
-Edwin Kempin <edwin.kempin@sap.com>           Edwin Kempin <edwin.kempin@gmail.com>
-Hugo Arès <hugo.ares@ericsson.com>            Hugo Ares <hugo.ares@ericsson.com>
-Jason Huntley <jhuntley@houghtonassociates.com> jhuntley <jhuntley@houghtonassociates.com>
-Johan Björk <jbjoerk@gmail.com>               Johan Bjork <phb@spotify.com>
+Adrian Görler <adrian.goerler@sap.com>                                                      Adrian Goerler <adrian.goerler@sap.com>
+Ahaan Ugale <ahaanugale@gmail.com>                                                          <augale@codeaurora.org>
+Alex Blewitt <alex.blewitt@gmail.com>                                                       <alex.blewitt@gs.com>
+Alex Ryazantsev <alex.ryazantsev@gmail.com>                                                 alex <alex.ryazantsev@gmail.com>
+Alex Ryazantsev <alex.ryazantsev@gmail.com>                                                 alex.ryazantsev <alex.ryazantsev@gmail.com>
+Brad Larson <bklarson@gmail.com>                                                            <brad.larson@garmin.com>
+Bruce Zu <bruce.zu@sonymobile.com>                                                          <bruce.zu@sonyericsson.com>
+Carlos Eduardo Baldacin <carloseduardo.baldacin@sonyericsson.com>                           carloseduardo.baldacin <carloseduardo.baldacin@sonyericsson.com>
+David Ostrovsky <david@ostrovsky.org>                                                       <d.ostrovsky@gmx.de>
+Deniz Türkoglu <deniz@spotify.com>                                                          Deniz Türkoglu <deniz@spotify.com>
+Deniz Türkoglu <deniz@spotify.com>                                                          Deniz Turkoglu <deniz@spotify.com>
+Edwin Kempin <edwin.kempin@sap.com>                                                         Edwin Kempin <edwin.kempin@gmail.com>
+Eryk Szymanski <eryksz@gmail.com>                                                           <eryksz@google.com>
+Fredrik Luthander <fredrik.luthander@sonymobile.com>                                        <fredrik@gandaraj.com>
+Fredrik Luthander <fredrik.luthander@sonymobile.com>                                        <fredrik.luthander@sonyericsson.com>
+Gustaf Lundh <gustaf.lundh@sonymobile.com>                                                  <gustaf.lundh@sonyericsson.com>
+Hugo Arès <hugo.ares@ericsson.com>                                                          Hugo Ares <hugo.ares@ericsson.com>
+Jason Huntley <jhuntley@houghtonassociates.com>                                             jhuntley <jhuntley@houghtonassociates.com>
+Jiří Engelthaler <EngyCZ@gmail.com>                                                         <engycz@gmail.com>
+Joe Onorato <onoratoj@gmail.com>                                                            <joeo@android.com>
+Johan Björk <jbjoerk@gmail.com>                                                             Johan Bjork <phb@spotify.com>
 Lincoln Oliveira Campos Do Nascimento <lincoln.oliveiracamposdonascimento@sonyericsson.com> lincoln <lincoln.oliveiracamposdonascimento@sonyericsson.com>
-Mônica Dionísio <monica.dionisio@sonyericsson.com> monica.dionisio <monica.dionisio@sonyericsson.com>
-Peter Jönsson <peter.joensson@gmail.com>      Peter Jönsson <peter.joensson@gmail.com>
-Rafael Rabelo Silva <rafael.rabelosilva@sonyericsson.com> rafael.rabelosilva <rafael.rabelosilva@sonyericsson.com>
-Saša Živkov <sasa.zivkov@sap.com>             Sasa Zivkov <sasa.zivkov@sap.com>
-Saša Živkov <sasa.zivkov@sap.com>             Saša Živkov <zivkov@gmail.com>
-Shawn Pearce <sop@google.com>                 Shawn O. Pearce <sop@google.com>
-Tomas Westling <thomas.westling@sonyericsson.com> thomas.westling <thomas.westling@sonyericsson.com>
-Ulrik Sjölin <ulrik.sjolin@sonyericsson.com>  Ulrik Sjolin <ulrik.sjolin@gmail.com>
-Ulrik Sjölin <ulrik.sjolin@sonyericsson.com>  Ulrik Sjolin <ulrik.sjolin@sonyericsson.com>
-Ulrik Sjölin <ulrik.sjolin@sonyericsson.com>  Ulrik Sjölin <ulrik.sjolin@sonyericsson.com>
+Luca Milanesio <luca.milanesio@gmail.com>                                                   <luca@gitent-scm.com>
+Magnus Bäck <baeck@google.com>                                                              <magnus.back@sonyericsson.com>
+Mark Derricutt <mark.derricutt@smxemail.com>                                                <mark@talios.com>
+Martin Fick <mfick@codeaurora.org>                                                          <mogulguy10@gmail.com>
+Martin Fick <mfick@codeaurora.org>                                                          <mogulguy@yahoo.com>
+Michael Zhou <moz@google.com>                                                               <zhoumotongxue008@gmail.com>
+Mônica Dionísio <monica.dionisio@sonyericsson.com>                                          monica.dionisio <monica.dionisio@sonyericsson.com>
+Nasser Grainawi <nasser@grainawi.org>                                                       <nasser@codeaurora.org>
+Nasser Grainawi <nasser@grainawi.org>                                                       <nasserg@quicinc.com>
+Peter Jönsson <peter.joensson@gmail.com>                                                    Peter Jönsson <peter.joensson@gmail.com>
+Rafael Rabelo Silva <rafael.rabelosilva@sonyericsson.com>                                   rafael.rabelosilva <rafael.rabelosilva@sonyericsson.com>
+Richard Möhn <richard.moehn@posteo.de>                                                      <richard.moehn@fu-berlin.de>
+Saša Živkov <sasa.zivkov@sap.com>                                                           Sasa Zivkov <sasa.zivkov@sap.com>
+Saša Živkov <sasa.zivkov@sap.com>                                                           Saša Živkov <zivkov@gmail.com>
+Shawn Pearce <sop@google.com>                                                               Shawn O. Pearce <sop@google.com>
+Tomas Westling <thomas.westling@sonyericsson.com>                                           thomas.westling <thomas.westling@sonyericsson.com>
+Ulrik Sjölin <ulrik.sjolin@sonyericsson.com>                                                <ulrik.sjolin@gmail.com>
+Ulrik Sjölin <ulrik.sjolin@sonyericsson.com>                                                Ulrik Sjolin <ulrik.sjolin@gmail.com>
+Ulrik Sjölin <ulrik.sjolin@sonyericsson.com>                                                Ulrik Sjölin <ulrik.sjolin@sonyericsson.com>
+Ulrik Sjölin <ulrik.sjolin@sonyericsson.com>                                                Ulrik Sjolin <ulrik.sjolin@sonyericsson.com>
+Zalán Blénessy <zalanb@axis.com>                                                            Zalan Blenessy <zalanb@axis.com>
diff --git a/.settings/edu.umd.cs.findbugs.core.prefs b/.settings/edu.umd.cs.findbugs.core.prefs
new file mode 100644
index 0000000..4dfcf2d
--- /dev/null
+++ b/.settings/edu.umd.cs.findbugs.core.prefs
@@ -0,0 +1,143 @@
+#FindBugs User Preferences
+#Fri Mar 20 17:07:10 JST 2015
+cloud_id=edu.umd.cs.findbugs.cloud.doNothingCloud
+detectorAppendingToAnObjectOutputStream=AppendingToAnObjectOutputStream|true
+detectorAtomicityProblem=AtomicityProblem|true
+detectorBadAppletConstructor=BadAppletConstructor|false
+detectorBadResultSetAccess=BadResultSetAccess|true
+detectorBadSyntaxForRegularExpression=BadSyntaxForRegularExpression|true
+detectorBadUseOfReturnValue=BadUseOfReturnValue|true
+detectorBadlyOverriddenAdapter=BadlyOverriddenAdapter|true
+detectorBooleanReturnNull=BooleanReturnNull|true
+detectorCallToUnsupportedMethod=CallToUnsupportedMethod|false
+detectorCheckExpectedWarnings=CheckExpectedWarnings|false
+detectorCheckImmutableAnnotation=CheckImmutableAnnotation|true
+detectorCheckRelaxingNullnessAnnotation=CheckRelaxingNullnessAnnotation|true
+detectorCheckTypeQualifiers=CheckTypeQualifiers|true
+detectorCloneIdiom=CloneIdiom|true
+detectorComparatorIdiom=ComparatorIdiom|true
+detectorConfusedInheritance=ConfusedInheritance|true
+detectorConfusionBetweenInheritedAndOuterMethod=ConfusionBetweenInheritedAndOuterMethod|true
+detectorCovariantArrayAssignment=CovariantArrayAssignment|false
+detectorCrossSiteScripting=CrossSiteScripting|true
+detectorDefaultEncodingDetector=DefaultEncodingDetector|true
+detectorDoInsideDoPrivileged=DoInsideDoPrivileged|true
+detectorDontCatchIllegalMonitorStateException=DontCatchIllegalMonitorStateException|true
+detectorDontIgnoreResultOfPutIfAbsent=DontIgnoreResultOfPutIfAbsent|true
+detectorDontUseEnum=DontUseEnum|true
+detectorDroppedException=DroppedException|true
+detectorDumbMethodInvocations=DumbMethodInvocations|true
+detectorDumbMethods=DumbMethods|true
+detectorDuplicateBranches=DuplicateBranches|true
+detectorEmptyZipFileEntry=EmptyZipFileEntry|false
+detectorEqualsOperandShouldHaveClassCompatibleWithThis=EqualsOperandShouldHaveClassCompatibleWithThis|true
+detectorExplicitSerialization=ExplicitSerialization|true
+detectorFinalizerNullsFields=FinalizerNullsFields|true
+detectorFindBadCast2=FindBadCast2|true
+detectorFindBadForLoop=FindBadForLoop|true
+detectorFindCircularDependencies=FindCircularDependencies|false
+detectorFindComparatorProblems=FindComparatorProblems|true
+detectorFindDeadLocalStores=FindDeadLocalStores|true
+detectorFindDoubleCheck=FindDoubleCheck|true
+detectorFindEmptySynchronizedBlock=FindEmptySynchronizedBlock|true
+detectorFindFieldSelfAssignment=FindFieldSelfAssignment|true
+detectorFindFinalizeInvocations=FindFinalizeInvocations|true
+detectorFindFloatEquality=FindFloatEquality|true
+detectorFindHEmismatch=FindHEmismatch|true
+detectorFindInconsistentSync2=FindInconsistentSync2|true
+detectorFindJSR166LockMonitorenter=FindJSR166LockMonitorenter|true
+detectorFindLocalSelfAssignment2=FindLocalSelfAssignment2|true
+detectorFindMaskedFields=FindMaskedFields|true
+detectorFindMismatchedWaitOrNotify=FindMismatchedWaitOrNotify|true
+detectorFindNakedNotify=FindNakedNotify|true
+detectorFindNonShortCircuit=FindNonShortCircuit|true
+detectorFindNullDeref=FindNullDeref|true
+detectorFindNullDerefsInvolvingNonShortCircuitEvaluation=FindNullDerefsInvolvingNonShortCircuitEvaluation|true
+detectorFindOpenStream=FindOpenStream|true
+detectorFindPuzzlers=FindPuzzlers|true
+detectorFindRefComparison=FindRefComparison|true
+detectorFindReturnRef=FindReturnRef|true
+detectorFindRoughConstants=FindRoughConstants|true
+detectorFindRunInvocations=FindRunInvocations|true
+detectorFindSelfComparison=FindSelfComparison|true
+detectorFindSelfComparison2=FindSelfComparison2|true
+detectorFindSleepWithLockHeld=FindSleepWithLockHeld|true
+detectorFindSpinLoop=FindSpinLoop|true
+detectorFindSqlInjection=FindSqlInjection|true
+detectorFindTwoLockWait=FindTwoLockWait|true
+detectorFindUncalledPrivateMethods=FindUncalledPrivateMethods|true
+detectorFindUnconditionalWait=FindUnconditionalWait|true
+detectorFindUninitializedGet=FindUninitializedGet|true
+detectorFindUnrelatedTypesInGenericContainer=FindUnrelatedTypesInGenericContainer|true
+detectorFindUnreleasedLock=FindUnreleasedLock|true
+detectorFindUnsatisfiedObligation=FindUnsatisfiedObligation|true
+detectorFindUnsyncGet=FindUnsyncGet|true
+detectorFindUseOfNonSerializableValue=FindUseOfNonSerializableValue|true
+detectorFindUselessControlFlow=FindUselessControlFlow|true
+detectorFindUselessObjects=FindUselessObjects|true
+detectorFormatStringChecker=FormatStringChecker|true
+detectorHugeSharedStringConstants=HugeSharedStringConstants|true
+detectorIDivResultCastToDouble=IDivResultCastToDouble|true
+detectorIncompatMask=IncompatMask|true
+detectorInconsistentAnnotations=InconsistentAnnotations|true
+detectorInefficientIndexOf=InefficientIndexOf|true
+detectorInefficientInitializationInsideLoop=InefficientInitializationInsideLoop|false
+detectorInefficientMemberAccess=InefficientMemberAccess|false
+detectorInefficientToArray=InefficientToArray|true
+detectorInfiniteLoop=InfiniteLoop|true
+detectorInfiniteRecursiveLoop=InfiniteRecursiveLoop|true
+detectorInheritanceUnsafeGetResource=InheritanceUnsafeGetResource|true
+detectorInitializationChain=InitializationChain|true
+detectorInitializeNonnullFieldsInConstructor=InitializeNonnullFieldsInConstructor|true
+detectorInstantiateStaticClass=InstantiateStaticClass|true
+detectorIntCast2LongAsInstant=IntCast2LongAsInstant|true
+detectorInvalidJUnitTest=InvalidJUnitTest|true
+detectorIteratorIdioms=IteratorIdioms|true
+detectorLazyInit=LazyInit|true
+detectorLoadOfKnownNullValue=LoadOfKnownNullValue|true
+detectorLostLoggerDueToWeakReference=LostLoggerDueToWeakReference|true
+detectorMethodReturnCheck=MethodReturnCheck|true
+detectorMultithreadedInstanceAccess=MultithreadedInstanceAccess|true
+detectorMutableEnum=MutableEnum|true
+detectorMutableLock=MutableLock|true
+detectorMutableStaticFields=MutableStaticFields|true
+detectorNaming=Naming|true
+detectorNoteUnconditionalParamDerefs=NoteUnconditionalParamDerefs|true
+detectorNumberConstructor=NumberConstructor|true
+detectorOptionalReturnNull=OptionalReturnNull|true
+detectorOverridingEqualsNotSymmetrical=OverridingEqualsNotSymmetrical|true
+detectorPreferZeroLengthArrays=PreferZeroLengthArrays|true
+detectorPublicSemaphores=PublicSemaphores|false
+detectorQuestionableBooleanAssignment=QuestionableBooleanAssignment|true
+detectorReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass=ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass|true
+detectorReadReturnShouldBeChecked=ReadReturnShouldBeChecked|true
+detectorRedundantConditions=RedundantConditions|true
+detectorRedundantInterfaces=RedundantInterfaces|true
+detectorRepeatedConditionals=RepeatedConditionals|true
+detectorRuntimeExceptionCapture=RuntimeExceptionCapture|true
+detectorSerializableIdiom=SerializableIdiom|true
+detectorStartInConstructor=StartInConstructor|true
+detectorStaticCalendarDetector=StaticCalendarDetector|true
+detectorStringConcatenation=StringConcatenation|true
+detectorSuperfluousInstanceOf=SuperfluousInstanceOf|true
+detectorSuspiciousThreadInterrupted=SuspiciousThreadInterrupted|true
+detectorSwitchFallthrough=SwitchFallthrough|true
+detectorSynchronizationOnSharedBuiltinConstant=SynchronizationOnSharedBuiltinConstant|true
+detectorSynchronizeAndNullCheckField=SynchronizeAndNullCheckField|true
+detectorSynchronizeOnClassLiteralNotGetClass=SynchronizeOnClassLiteralNotGetClass|true
+detectorSynchronizingOnContentsOfFieldToProtectField=SynchronizingOnContentsOfFieldToProtectField|true
+detectorURLProblems=URLProblems|true
+detectorUncallableMethodOfAnonymousClass=UncallableMethodOfAnonymousClass|true
+detectorUnnecessaryMath=UnnecessaryMath|true
+detectorUnreadFields=UnreadFields|true
+detectorUselessSubclassMethod=UselessSubclassMethod|false
+detectorVarArgsProblems=VarArgsProblems|true
+detectorVolatileUsage=VolatileUsage|true
+detectorWaitInLoop=WaitInLoop|true
+detectorWrongMapIterator=WrongMapIterator|true
+detectorXMLFactoryBypass=XMLFactoryBypass|true
+detector_threshold=2
+effort=default
+filter_settings=Medium|BAD_PRACTICE,CORRECTNESS,EXPERIMENTAL,MALICIOUS_CODE,MT_CORRECTNESS,PERFORMANCE,SECURITY,STYLE|false|12
+filter_settings_neg=NOISE,I18N|
+run_at_full_build=false
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
index 0fa494d..8f5678f 100644
--- a/.settings/org.eclipse.jdt.core.prefs
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -5,9 +5,17 @@
 org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
 org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
 org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
 org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
 org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=ignore
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
 org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
 org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
 org.eclipse.jdt.core.compiler.problem.deadCode=warning
@@ -16,7 +24,8 @@
 org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
 org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
 org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
 org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
 org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
 org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
@@ -28,12 +37,25 @@
 org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
 org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
 org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
 org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
 org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
 org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
 org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
 org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
 org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=protected
 org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
 org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
@@ -77,6 +99,7 @@
 org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
 org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
 org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore
 org.eclipse.jdt.core.compiler.problem.unusedImport=warning
 org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
 org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
@@ -93,6 +116,7 @@
 org.eclipse.jdt.core.compiler.source=1.7
 org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
@@ -103,15 +127,18 @@
 org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16
 org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
 org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
 org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
 org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
 org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
 org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
 org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
 org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
 org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
 org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
 org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
 org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
 org.eclipse.jdt.core.formatter.blank_lines_after_package=1
 org.eclipse.jdt.core.formatter.blank_lines_before_field=0
@@ -131,6 +158,7 @@
 org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
 org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
 org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
 org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
 org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
 org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
@@ -147,10 +175,16 @@
 org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
 org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
 org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
 org.eclipse.jdt.core.formatter.compact_else_if=true
 org.eclipse.jdt.core.formatter.continuation_indentation=2
 org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
 org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
 org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
 org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
 org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
@@ -162,10 +196,16 @@
 org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
 org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
 org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
 org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
 org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
 org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
 org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
 org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
 org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
 org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
@@ -213,6 +253,7 @@
 org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
 org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
 org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
 org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
 org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
 org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
@@ -231,12 +272,14 @@
 org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
 org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
 org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
 org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
 org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
 org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
 org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
 org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
 org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
 org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
 org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
 org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
@@ -260,6 +303,7 @@
 org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
 org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
 org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
 org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
 org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
 org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
@@ -287,6 +331,7 @@
 org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
 org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
 org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
 org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
 org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
 org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
@@ -315,6 +360,7 @@
 org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
 org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
 org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
 org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
 org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
 org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
@@ -324,6 +370,7 @@
 org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
 org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
 org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
 org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
 org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
 org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
@@ -347,5 +394,9 @@
 org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
 org.eclipse.jdt.core.formatter.tabulation.char=space
 org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_on_off_tags=false
 org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
 org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
+org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter
diff --git a/.settings/org.eclipse.jdt.ui.prefs b/.settings/org.eclipse.jdt.ui.prefs
index d4218a5..d990610 100644
--- a/.settings/org.eclipse.jdt.ui.prefs
+++ b/.settings/org.eclipse.jdt.ui.prefs
@@ -1,10 +1,9 @@
-#Wed Jul 29 11:31:38 PDT 2009
 eclipse.preferences.version=1
 editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
 formatter_profile=_Google Format
-formatter_settings_version=11
+formatter_settings_version=12
 org.eclipse.jdt.ui.ignorelowercasenames=true
-org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax;
+org.eclipse.jdt.ui.importorder=\#;com.google;com;dk;eu;junit;net;org;java;javax;
 org.eclipse.jdt.ui.ondemandthreshold=99
 org.eclipse.jdt.ui.staticondemandthreshold=99
 org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
diff --git a/BUCK b/BUCK
index 2cd3fa8..c986874 100644
--- a/BUCK
+++ b/BUCK
@@ -1,13 +1,17 @@
 include_defs('//tools/build.defs')
 
 gerrit_war(name = 'gerrit')
+gerrit_war(name = 'headless', ui = None)
 gerrit_war(name = 'chrome',   ui = 'ui_chrome')
 gerrit_war(name = 'firefox',  ui = 'ui_firefox')
 gerrit_war(name = 'safari',   ui = 'ui_safari')
 gerrit_war(name = 'withdocs', docs = True)
-gerrit_war(name = 'release',  docs = True, context = ['//plugins:core'],  visibility = ['//tools/maven:'])
+gerrit_war(name = 'release',  ui = 'ui_optdbg_r', docs = True, context = ['//plugins:core'],  visibility = ['//tools/maven:'])
 
 API_DEPS = [
+  '//gerrit-acceptance-framework:acceptance-framework',
+  '//gerrit-acceptance-framework:acceptance-framework-src',
+  '//gerrit-acceptance-framework:acceptance-framework-javadoc',
   '//gerrit-extension-api:extension-api',
   '//gerrit-extension-api:extension-api-src',
   '//gerrit-extension-api:extension-api-javadoc',
@@ -19,22 +23,7 @@
   '//gerrit-plugin-gwtui:gwtui-api-javadoc',
 ]
 
-genrule(
+zip_file(
   name = 'api',
-  cmd = ';'.join(
-    ['cd $TMP'] +
-    ['ln -s $(location %s) .' % n for n in API_DEPS] +
-    ['zip -q0 $OUT *']),
-  deps = API_DEPS,
-  out = 'api.zip',
-)
-
-genrule(
-  name = 'all',
-  cmd = 'echo done >$OUT',
-  deps = [
-    ':api',
-    ':release',
-  ],
-  out = '__fake.all__',
+  srcs = API_DEPS,
 )
diff --git a/Documentation/BUCK b/Documentation/BUCK
index 48a6525..126bf1f 100644
--- a/Documentation/BUCK
+++ b/Documentation/BUCK
@@ -3,7 +3,8 @@
 include_defs('//tools/git.defs')
 
 DOC_DIR = 'Documentation'
-MAIN = ['//gerrit-pgm:pgm', '//gerrit-gwtui:ui_module']
+JSUI = '//gerrit-gwtui:ui_module'
+MAIN = '//gerrit-pgm:pgm'
 SRCS = glob(['*.txt'], excludes = ['licenses.txt'])
 
 genasciidoc(
@@ -29,11 +30,22 @@
 
 genrule(
   name = 'licenses.txt',
-  cmd = '$(exe :gen_licenses) >$OUT',
-  deps = [':gen_licenses'] + MAIN,
+  cmd = '$(exe :gen_licenses) --asciidoc '
+    + '--classpath $(classpath %s) ' % MAIN
+    + '--classpath $(classpath %s) ' % JSUI
+    + MAIN + ' ' + JSUI + ' >$OUT',
   out = 'licenses.txt',
 )
 
+# Required by Google for gerrit-review.
+genrule(
+  name = 'js_licenses.txt',
+  cmd = '$(exe :gen_licenses) --partial '
+    + '--classpath $(classpath %s) ' % JSUI
+    + JSUI + ' >$OUT',
+  out = 'js_licenses.txt',
+)
+
 genrule(
   name = 'doc.css',
   srcs = ['doc.css.in'],
diff --git a/Documentation/Makefile b/Documentation/Makefile
deleted file mode 100644
index aed9e90..0000000
--- a/Documentation/Makefile
+++ /dev/null
@@ -1,50 +0,0 @@
-# Copyright (C) 2009 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.
-
-ASCIIDOC       ?= asciidoc
-ASCIIDOC_EXTRA ?=
-ASCIIDOC_VER   ?= 8.6.3
-
-all: html
-
-clean:
-	rm -f *.html
-
-ASCIIDOC_EXE := $(shell which $(ASCIIDOC))
-ifeq ($(wildcard $(ASCIIDOC_EXE)),)
-  $(error $(ASCIIDOC) must be available)
-else
-  ASCIIDOC_OK := $(shell expr `asciidoc --version | cut -f2 -d' '` \>= $(ASCIIDOC_VER))
-  ifeq ($(ASCIIDOC_OK),0)
-    $(error $(ASCIIDOC) version $(ASCIIDOC_VER) or higher is required)
-  endif
-endif
-
-DOC_HTML := $(patsubst %.txt,%.html,$(wildcard *.txt))
-REVISION := $(shell git describe HEAD | sed s/^v//)
-
-html: $(DOC_HTML)
-
-$(DOC_HTML): %.html : %.txt
-	@echo "FORMAT $@"
-	@rm -f $@+ $@
-	@$(ASCIIDOC) -a toc \
-		-a data-uri \
-		-a 'revision=$(REVISION)' \
-		-a 'newline=\n' \
-		-b xhtml11 \
-		-f asciidoc.conf \
-		$(ASCIIDOC_EXTRA) \
-		-o $@+ $<
-	@mv $@+ $@
diff --git a/Documentation/access-control.txt b/Documentation/access-control.txt
index acd33c0..0758c5c 100644
--- a/Documentation/access-control.txt
+++ b/Documentation/access-control.txt
@@ -1244,6 +1244,22 @@
 a replication task or a user initiated task such as an upload-pack or
 receive-pack.
 
+[[capability_maintainServer]]
+=== Maintain Server
+
+Allow basic, constrained server maintenance tasks, such as flushing caches and
+reindexing changes. Does not grant arbitrary database access, read/write, or
+ACL management permissions, as might the
+<<capability_administrateServer,administrate server capability>>.
+
+Implies the following capabilities:
+
+* <<capability_flushCaches,Flush Caches>>
+* <<capability_kill,Kill Task>>
+* <<capability_runGC,Run Garbage Collection>>
+* <<capability_viewCaches,View Caches>>
+* <<capability_viewQueue,View Queue>>
+
 [[capability_modifyAccount]]
 === Modify Account
 
diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf
deleted file mode 100644
index 2fe6213..0000000
--- a/Documentation/asciidoc.conf
+++ /dev/null
@@ -1,29 +0,0 @@
-[attributes]
-asterisk=&#42;
-plus=&#43;
-caret=&#94;
-startsb=&#91;
-endsb=&#93;
-tilde=&#126;
-
-[specialsections]
-GERRIT=gerrituplink
-
-[gerrituplink]
-<hr style="
-  height: 2px;
-  color: silver;
-  margin-top: 1.2em;
-  margin-bottom: 0.5em;
-">
-
-[macros]
-(?u)^(?P<name>get)::(?P<target>\S*?)$=#
-
-[get-blockmacro]
-<a id="{target}" onmousedown="javascript:
-  var i =  document.URL.lastIndexOf('/Documentation/');
-  var url = document.URL.substring(0, i) + '{target}';
-  document.getElementById('{target}').href = url;">
-    GET {target} HTTP/1.0
-</a>
diff --git a/Documentation/cmd-create-project.txt b/Documentation/cmd-create-project.txt
index 31b4538..d1108b5 100644
--- a/Documentation/cmd-create-project.txt
+++ b/Documentation/cmd-create-project.txt
@@ -21,7 +21,7 @@
   [--empty-commit]
   [--max-object-size-limit <N>]
   [--plugin-config <PARAM> ...]
-  { <NAME> | --name <NAME> }
+  { <NAME> }
 --
 
 == DESCRIPTION
@@ -49,11 +49,6 @@
 	Required; name of the new project to create.  If name ends
 	with `.git` the suffix will be automatically removed.
 
---name::
--n::
-	Deprecated alias for the <NAME> argument. This option may
-	be removed in a future release.
-
 --branch::
 -b::
 	Name of the initial branch(es) in the newly created project.
diff --git a/Documentation/cmd-flush-caches.txt b/Documentation/cmd-flush-caches.txt
index d93d47c..aa9790d 100644
--- a/Documentation/cmd-flush-caches.txt
+++ b/Documentation/cmd-flush-caches.txt
@@ -21,9 +21,16 @@
 If no options are supplied, defaults to `--all`.
 
 == ACCESS
-Caller must be a member of the privileged 'Administrators' group,
-or in a group that have been granted
-link:access-control.html#capability_flushCaches[the 'Flush Caches' global capability].
+
+The caller must be a member of a group that is granted one of the
+following capabilities:
+
+* link:access-control.html#capability_flushCaches[Flush Caches] (any cache
+  except "web_sessions")
+* link:access-control.html#capability_maintainServer[Maintain Server] (any cache
+  including "web_sessions")
+* link:access-control.html#capability_administrateServer[Administrate Server]
+  (any cache including "web_sessions")
 
 == SCRIPTING
 This command is intended to be used in scripts.
diff --git a/Documentation/cmd-gc.txt b/Documentation/cmd-gc.txt
index a890f1b..b7388a1 100644
--- a/Documentation/cmd-gc.txt
+++ b/Documentation/cmd-gc.txt
@@ -8,6 +8,7 @@
 'ssh' -p <port> <host> 'gerrit gc'
   [--all]
   [--show-progress]
+  [--aggressive]
   <NAME> ...
 --
 
@@ -44,6 +45,9 @@
 --show-progress::
 	If specified progress information is shown.
 
+--aggressive::
+	If an aggressive garbage collection should be done.
+
 == EXAMPLES
 
 Run the Git garbage collection for the projects 'myProject' and
diff --git a/Documentation/cmd-index.txt b/Documentation/cmd-index.txt
index 665ff8d..90212fb 100644
--- a/Documentation/cmd-index.txt
+++ b/Documentation/cmd-index.txt
@@ -75,6 +75,9 @@
 link:cmd-review.html[gerrit review]::
 	Verify, approve and/or submit a patch set from the command line.
 
+link:cmd-set-head.html[gerrit set-head]::
+	Change the HEAD reference of a project.
+
 link:cmd-set-reviewers.html[gerrit set-reviewers]::
 	Add or remove reviewers on a change.
 
@@ -117,7 +120,7 @@
 link:cmd-gsql.html[gerrit gsql]::
 	Administrative interface to active database.
 
-link:cmd-index-index.html[gerrit index activate]::
+link:cmd-index-activate.html[gerrit index activate]::
 	Activate the latest index version available.
 
 link:cmd-index-start.html[gerrit index start]::
diff --git a/Documentation/cmd-review.txt b/Documentation/cmd-review.txt
index 70a695e..0590337 100644
--- a/Documentation/cmd-review.txt
+++ b/Documentation/cmd-review.txt
@@ -128,6 +128,12 @@
 	or invalid value) and votes that are not permitted for the user are
 	silently ignored.
 
+--strict-labels::
+	Require ability to vote on all specified labels before reviewing change.
+	If the vote is invalid (invalid label or invalid name), the vote is not
+	permitted for the user, or the vote is on an outdated or closed patch set,
+	return an error instead of silently discarding the vote.
+
 == ACCESS
 Any user who has configured an SSH key.
 
diff --git a/Documentation/cmd-set-head.txt b/Documentation/cmd-set-head.txt
new file mode 100644
index 0000000..d74caaa
--- /dev/null
+++ b/Documentation/cmd-set-head.txt
@@ -0,0 +1,45 @@
+= gerrit set-head
+
+== NAME
+gerrit set-head - Change a project's HEAD.
+
+== SYNOPSIS
+--
+'ssh' -p <port> <host> 'gerrit set-head' <NAME>
+  --new-head <REF>
+--
+
+== DESCRIPTION
+Modifies a given project's HEAD reference.
+
+The command is argument-safe, that is, if no argument is given the
+previous settings are kept intact.
+
+== ACCESS
+Caller must be an owner of the given project.
+
+== SCRIPTING
+This command is intended to be used in scripts.
+
+== OPTIONS
+<NAME>::
+    Required; name of the project to change the HEAD. If name ends
+    with `.git` the suffix will be automatically removed.
+
+--new-head::
+    Required; name of the ref that should be set as new HEAD. The
+    'refs/heads/' prefix can be omitted.
+
+== EXAMPLES
+Change HEAD of project `example` to `stable-2.11` branch:
+
+====
+    $ ssh -p 29418 review.example.com gerrit set-head example --new-head stable-2.11
+====
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-show-caches.txt b/Documentation/cmd-show-caches.txt
index dd79d8b..5d6ab20 100644
--- a/Documentation/cmd-show-caches.txt
+++ b/Documentation/cmd-show-caches.txt
@@ -30,14 +30,17 @@
 	Width of the output table.
 
 == ACCESS
-The caller must be a member of a group that is granted the
-link:access-control.html#capability_viewCaches[View Caches] capability
-or the link:access-control.html#capability_administrateServer[
-Administrate Server] capability.
+The caller must be a member of a group that is granted one of the
+following capabilities:
+
+* link:access-control.html#capability_viewCaches[View Caches]
+* link:access-control.html#capability_maintainServer[Maintain Server]
+* link:access-control.html#capability_administrateServer[Administrate Server]
 
 The summary information about SSH, threads, tasks, memory and JVM are
 only printed out if the caller is a member of a group that is granted
 the link:access-control.html#capability_administrateServer[Administrate
+Server] or link:access-control.html#capability_maintainServer[Maintain
 Server] capability.
 
 == SCRIPTING
diff --git a/Documentation/cmd-show-connections.txt b/Documentation/cmd-show-connections.txt
index a694fb3..81eb174 100644
--- a/Documentation/cmd-show-connections.txt
+++ b/Documentation/cmd-show-connections.txt
@@ -40,6 +40,7 @@
 
 Start::
 	Time (local to the server) that this connection started.
+	Only valid for MINA backend.
 
 Idle::
 	Time since the last data transfer on this connection.
@@ -47,6 +48,7 @@
 	connection keep-alive, but also an encrypted keep alive
 	higher up in the SSH protocol stack.  That higher keep
 	alive resets the idle timer, about once a minute.
+	Only valid for MINA backend.
 
 User::
 	The username of the account that is authenticated on this
diff --git a/Documentation/cmd-stream-events.txt b/Documentation/cmd-stream-events.txt
index c754f35..dcdbb07 100644
--- a/Documentation/cmd-stream-events.txt
+++ b/Documentation/cmd-stream-events.txt
@@ -153,6 +153,19 @@
 eventCreatedOn:: Time in seconds since the UNIX epoch when this event was
 created.
 
+=== Project Created
+
+Sent when a new project has been created.
+
+type:: "project-created"
+
+projectName:: The created project name
+
+projectHead:: The created project head name
+
+eventCreatedOn:: Time in seconds since the UNIX epoch when this event was
+created.
+
 === Merge Failed
 
 Sent when a change has failed to be merged into the git repository.
diff --git a/Documentation/config-cla.txt b/Documentation/config-cla.txt
index 624135c..4c8d04a 100644
--- a/Documentation/config-cla.txt
+++ b/Documentation/config-cla.txt
@@ -29,7 +29,6 @@
 ====
   [contributor-agreement "Individual"]
     description = If you are going to be contributing code on your own, this is the one you want. You can sign this one online.
-    requireContactInformation = true
     agreementUrl = static/cla_individual.html
     autoVerify = group CLA Accepted - Individual
     accepted = group CLA Accepted - Individual
@@ -52,11 +51,6 @@
 Short text describing the contributor agreement. This text will appear
 when the user selects an agreement.
 
-[[contributor-agreement.name.requireContactInformation]]contributor-agreement.<name>.requireContactInformation::
-+
-True if the user must provide contact information when signing a
-contributor agreement. Default is false.
-
 [[contributor-agreement.name.agreementUrl]]contributor-agreement.<name>.agreementUrl::
 +
 An absolute URL or a relative path to an HTML file containing the text
diff --git a/Documentation/config-contact.txt b/Documentation/config-contact.txt
deleted file mode 100644
index e0795be..0000000
--- a/Documentation/config-contact.txt
+++ /dev/null
@@ -1,213 +0,0 @@
-= Gerrit Code Review - Contact Information
-
-To help ensure contributor privacy, but still support gathering of
-contributor agreements as necessary, Gerrit encrypts all offline
-contact information gathered from users.  This data is shipped to
-another server, typically at a different location, to make it more
-difficult for an attacker to obtain.
-
-This feature is optional.  If the crypto APIs aren't installed
-and the `contactstore.url` setting in `gerrit.config` is not set,
-Gerrit will not collect contact information from users.
-
-
-== Setup
-
-Ensure Bouncy Castle Crypto API is available in the web application's
-CLASSPATH (e.g. in `'JETTY_HOME'/lib/plus` for Jetty).  Gerrit needs
-both `bcprov-jdk\*-*.jar` and `bcpg-jdk\*-*.jar` to be provided
-for the contact encryption to work.
-
-* link:http://www.bouncycastle.org/latest_releases.html[Bouncy Castle Crypto API]
-
-Ensure a proper JCE policy file is installed.  By default most
-JRE installations forbid the use of a strong key, resulting in
-SecurityException messages when trying to encrypt the contact data.
-You need to obtain a strong JCE policy file and install it by hand.
-Look for the 'Unlimited Strength Jurisdiction Policy' download.
-
-* link:http://java.sun.com/javase/downloads/index.jsp[Java SE Downloads]
-
-Create a public/private key pair for contact data handling.
-Generate the keys on a protected system, where the resulting
-private key is unlikely to fall into the wrong hands.
-
-====
-  gpg --gen-key
-====
-
-Select to use a `DSA and Elgamal` key type, as the public key will
-be used for data encryption.
-
-The information chosen for name, email and comment fields can be
-anything reasonable which would identify the contact store of this
-Gerrit instance.  It is probably a good idea to not use a real
-person's name here, but instead some sort of organizational role.
-The actual values chosen don't matter later, and are only to help
-document the purpose of the key.
-
-Choose a fairly long expiration period, such as 20 years.  For most
-Gerrit instances, contact data will be written once, and rarely,
-if ever, read back.
-
-Export the public key for Gerrit to use during encryption.  The
-public key must be stored in a file called `contact_information.pub`
-and reside inside of the `site_config` directory.  Armoring it
-during export makes it easier to transport between systems, as
-you can easily copy-and-paste the text.  Gerrit can read both the
-armored and unarmored formats.
-
-====
-  gpg --export --armor KEYEMAIL >$site_path/etc/contact_information.pub
-====
-
-Consider storing the private key with some sort of key escrow
-service within your organization.  Without the private key it
-is impossible to recover contact records.
-
-Install a contact store implementation somewhere to receive
-the contact records.  To be really paranoid, Gerrit always
-ships the data to another HTTP server, preferably over HTTPS.
-Existing open-source server implementations can be found in the
-gerrit-contactstore project.
-
-* link:https://code.google.com/p/gerrit/source/checkout?repo=contactstore[gerrit-contactstore]
-
-Configure `'$site_path'/etc/gerrit.config` with the contact store's
-URL (in `contactstore.url`), and if needed, APPSEC value (in
-`contactstore.appsec`):
-
-====
-  git config --file $site_path/etc/gerrit.config appsec.url https://...
-  git config --file $site_path/etc/gerrit.config appsec.appsec sekret
-====
-
-
-== Contact Store Protocol
-
-To implement a new contact store, the following details are useful.
-
-Gerrit connects to the contact store by sending a standard
-`application/x-www-form-urlencoded` within an HTTP POST request
-sent to the store URL (the exact URL that is in contactstore.url)
-with the following form fields in the body:
-
-* APPSEC
-+
-A shared secret "password" that should be known only to Gerrit
-and the contact store.  The contact store should test this value to
-deter spamming of the contact store by outside parties.  Gerrit reads
-this from contactstore.appsec.
-
-* account_id
-+
-Unique account_id value from the Gerrit database for the account
-the contact information belongs to.  Base 10 integer.
-
-* email
-+
-Preferred email address of the account.  May facilitate lookups in
-the contact store at a future date.  May be omitted or the empty
-string if the user hasn't chosen a preferred email.
-
-* filed
-+
-Seconds since the UNIX epoch of when the contact information
-was filed.  May be omitted or the empty string if Gerrit
-doesn't think the supplied contact information is valid enough.
-
-* data
-+
-Encrypted account data as an armored ASCII blob.  This is usually
-several KB of text data as a single string, with embedded newlines
-to break the lines at about 70-75 characters per line.  Data can
-be decoded using GnuPG with the correct private key.
-
-Upon successful store, the contact store application should respond
-with HTTP status code `200` and a body consisting only of `OK`
-(or `OK\n`).  Any other response code or body is considered to be
-a failure by Gerrit.
-
-Using `https://` for the store URL is *highly* encouraged, as it
-prevents man-in-the-middle attacks from reading the shared secret
-APPSEC token, or messing with the data field.
-
-=== Data Format
-
-Once decrypted the `data` field looks something like the following:
-
-----
-Account-Id: 1001240
-Date: 2009-02-23 20:32:32.852 UTC
-Full-Name: John Doe
-Preferred-Email: jdoe@example.com
-Identity: jd15@some-isp.com
-Identity: jdoe@example.com <http://jdoe.blogger.com/>
-Address:
-	123 Any Street
-	Any Town, Somewhere
-Country: USA
-Phone-Number: +1 (555) 555-1212
-Fax-Number: 555.1200
-----
-
-The fields are as follows:
-
-* `Account-Id`
-+
-Value of the `account_id` field in the metadata database.  This is
-a unique key for this account, and links all data records to it.
-
-* `Date`
-+
-Date and time of when this contact record was submitted by the user.
-Written in an ISO formatted date/time string (`YYYY-MM-DD hh:mm:ss`),
-in the UTC timezone.
-
-* `Full-Name`
-+
-The `full_name` field of the account record when the user submitted
-the contact information.  This should be the user's given name and
-family name.
-
-* `Preferred-Email`
-+
-The `preferred_email` field of the account record when the user
-submitted the contact information.  This should be one of the emails
-listed in the `Identity` field.
-
-* `Identity`
-+
-This field occurs once for each `account_external_id` record
-in the database for this account.  The email address is listed,
-and if the user is using OpenID authentication, the OpenID claimed
-identity follows in brackets (`<...>`).  Identity lines without an
-OpenID identity are usually created by sending an email containing
-a unique hyperlink that the user must visit to setup the identity.
-
-* `Address`
-+
-Free form text, as entered by the user.  This should describe some
-location that physical documents could be sent to, but it is not
-verified, so users can enter pretty much anything here.  Each line
-is prefixed with a single TAB character, but is otherwise exactly
-as entered.
-
-* `Country`
-+
-Free form text, as entered by the user.  This should be some sort
-of country name or ISO country abbreviation, but it is not verified,
-so it can be pretty much anything.
-
-* `Phone-Number`, `Fax-Number`
-+
-Free form text, as entered by the user.  The format here can be
-anything, and as the example shows, may not even be consistent in
-the same record.
-
-GERRIT
-------
-Part of link:index.html[Gerrit Code Review]
-
-SEARCHBOX
----------
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index e65861a..bc64d7f 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -237,6 +237,13 @@
 +
 Default is -1, permitting infinite time between authentications.
 
+[[auth.registerEmailPrivateKey]]auth.registerEmailPrivateKey::
++
+Private key to use when generating an email verification token.
++
+If not set, a random key is generated when running the
+link:pgm-init.html[site initialization].
+
 [[auth.maxRegisterEmailTokenAge]]auth.maxRegisterEmailTokenAge::
 +
 Time in seconds before an email verification token sent to a user in
@@ -430,8 +437,9 @@
 [[auth.gitBasicAuth]]auth.gitBasicAuth::
 +
 If true then Git over HTTP and HTTP/S traffic is authenticated using
-standard BasicAuth and the credentials are validated using the same
-auth method as configured for the Gerrit Web UI.
+standard BasicAuth and the credentials are validated against the randomly
+generated HTTP password or against LDAP when it is configured as Gerrit
+Web UI authentication method.
 +
 This parameter affects git over HTTP traffic and access to the REST
 API. If set to false then Gerrit will authenticate through DIGEST
@@ -474,6 +482,16 @@
 +
 Default is true.
 
+[[auth.allowRegisterNewEmail]]auth.allowRegisterNewEmail::
++
+Whether users are allowed to register new email addresses.
++
+In addition for the HTTP authentication type
+link:#auth.httpemailheader[auth.httpemailheader] must *not* be set to
+enable registration of new email addresses.
++
+By default, true.
+
 [[cache]]
 === Section cache
 
@@ -862,6 +880,13 @@
 +
 Default is "Submit".
 
+[[change.submitLabelWithParents]]change.submitLabelWithParents::
++
+Label name for the submit button if the change has parents which will
+be submitted together with this change.
++
+Default is "Submit including parents".
+
 [[change.submitTooltip]]change.submitTooltip::
 +
 Tooltip for the submit button.  Variables available for replacement
@@ -871,6 +896,44 @@
 +
 Default is "Submit patch set ${patchSet} into ${branch}".
 
+[[change.submitTooltipAncestors]]change.submitTooltipAncestors::
++
+Tooltip for the submit button if there are ancestors which would
+also be submitted by submitting the change. Additionally to the variables
+as in link:#change.submitTooltip[change.submitTooltip], there is the
+variable `${submitSize}` indicating the number of changes which are
+submitted.
++
+Default is "Submit all ${topicSize} changes of the same topic (${submitSize}
+changes including ancestors and other changes related by topic)".
+
+[[change.submitWholeTopic]]change.submitWholeTopic (*Experimental*)::
++
+Determines if the submit button submits the whole topic instead of
+just the current change.
++
+Default is false.
+
+[[change.submitTopicLabel]]change.submitTopicLabel::
++
+If `change.submitWholeTopic` is set and a change has a topic,
+the label name for the submit button is given here instead of
+the configuration `change.submitLabel`.
++
+Defaults to "Submit whole topic"
+
+[[change.submitTopicTooltip]]change.submitTopicTooltip::
++
+If `change.submitWholeTopic` is configuerd to true and a change has a
+topic, this configuration determines the tooltip for the submit button
+instead of `change.submitTooltip`. The variable `${topicSize}` is available
+for the number of changes in the same topic to be submitted. The number of
+all changes to be submitted is in the variable `${submitSize}`.
++
+Defaults to "Submit all ${topicSize} changes of the same topic
+(${submitSize} changes including ancestors and other
+changes related by topic)".
+
 [[change.replyLabel]]change.replyLabel::
 +
 Label name for the reply button. In the user interface an ellipsis (…)
@@ -886,6 +949,82 @@
 Default is "Reply and score". In the user interface it becomes "Reply
 and score (Shortcut: a)".
 
+[[changeCleanup]]
+=== Section changeCleanup
+
+This section allows to configure change cleanups and schedules them to
+run periodically.
+
+[[changeCleanup.abandonAfter]]changeCleanup.abandonAfter::
++
+Period of inactivity after which open changes should be abandoned
+automatically.
++
+By default `0`, never abandon open changes.
++
+[WARNING] Auto-Abandoning changes may confuse/annoy users. When
+enabling this, make sure to choose a reasonably large grace period and
+inform users in advance.
++
+The following suffixes are supported to define the time unit:
++
+* `d, day, days`
+* `w, week, weeks` (`1 week` is treated as `7 days`)
+* `mon, month, months` (`1 month` is treated as `30 days`)
+* `y, year, years` (`1 year` is treated as `365 days`)
+
+[[changeCleanup.abandonIfMergeable]]changeCleanup.abandonIfMergeable::
++
+Whether changes which are mergeable should be auto-abandoned.
++
+By default `true`.
+
+[[changeCleanup.abandonMessage]]changeCleanup.abandonMessage::
++
+Change message that should be posted when a change is abandoned.
++
+'${URL}' can be used as a placeholder for the Gerrit web URL.
++
+By default "Auto-Abandoned due to inactivity, see
+${URL}Documentation/user-change-cleanup.html#auto-abandon\n\n
+If this change is still wanted it should be restored.".
+
+[[changeCleanup.startTime]]changeCleanup.startTime::
++
+Start time to define the first execution of the change cleanups.
+If the configured `'changeCleanup.interval'` is shorter than
+`'changeCleanup.startTime - now'` the start time will be preponed by
+the maximum integral multiple of `'changeCleanup.interval'` so that the
+start time is still in the future.
++
+----
+<day of week> <hours>:<minutes>
+or
+<hours>:<minutes>
+
+<day of week> : Mon, Tue, Wed, Thu, Fri, Sat, Sun
+<hours>       : 00-23
+<minutes>     : 0-59
+----
+
+
+[[changeCleanup.interval]]changeCleanup.interval::
++
+Interval for periodic repetition of triggering the change cleanups.
+The interval must be larger than zero. The following suffixes are supported
+to define the time unit for the interval:
++
+* `s, sec, second, seconds`
+* `m, min, minute, minutes`
+* `h, hr, hour, hours`
+* `d, day, days`
+* `w, week, weeks` (`1 week` is treated as `7 days`)
+* `mon, month, months` (`1 month` is treated as `30 days`)
+* `y, year, years` (`1 year` is treated as `365 days`)
+
+link:#schedule-examples[Schedule examples] can be found in the
+link:#gc[gc] section.
+
 [[changeMerge]]
 === Section changeMerge
 
@@ -937,7 +1076,7 @@
 ----
 [commentlink "changeid"]
   match = (I[0-9a-f]{8,40})
-  link = "#q,$1"
+  link = "#/q/$1"
 
 [commentlink "bugzilla"]
   match = "(bug\\s+#?)(\\d+)"
@@ -1011,22 +1150,6 @@
 link:rest-api-projects.html#get-config[REST API].
 
 
-[[contactstore]]
-=== Section contactstore
-
-[[contactstore.url]]contactstore.url::
-+
-URL of the web based contact store Gerrit will send any offline
-contact information to when it collects the data from users as part
-of a contributor agreement.
-+
-See link:config-contact.html[Contact Information].
-
-[[contactstore.appsec]]contactstore.appsec::
-+
-Shared secret of the web based contact store.
-
-
 [[container]]
 === Section container
 
@@ -1193,7 +1316,7 @@
 [[core.useRecursiveMerge]]core.useRecursiveMerge::
 +
 Use JGit's recursive merger for three-way merges. This only affects
-projects configured to automatically resolve conflicts.
+projects that allow content merges.
 +
 As explained in this
 link:http://codicesoftware.blogspot.com/2011/09/merge-recursive-strategy.html[
@@ -1223,21 +1346,37 @@
 used to automatically create correct database.driver and database.url
 values to open the connection.
 +
-* `POSTGRESQL`
+* `DB2`
 +
-Connect to a PostgreSQL database server.
+Connect to a DB2 database server.
++
+* `DERBY`
++
+Connect to an Apache Derby database server.
 +
 * `H2`
 +
 Connect to a local embedded H2 database.
 +
+* `JDBC`
++
+Connect using a JDBC driver class name and URL.
++
+* `MAXDB`
++
+Connect to an SAP MaxDb database server.
++
 * `MYSQL`
 +
 Connect to a MySQL database server.
 +
-* `JDBC`
+* `ORACLE`
 +
-Connect using a JDBC driver class name and URL.
+Connect to an Oracle database server.
++
+* `POSTGRESQL`
++
+Connect to a PostgreSQL database server.
 
 +
 If not specified, database.driver and database.url are used as-is,
@@ -1430,10 +1569,35 @@
 If `download.scheme` is not specified, SSH, HTTP and Anonymous HTTP
 downloads are allowed.
 
+[[download.checkForHiddenChangeRefs]]download.checkForHiddenChangeRefs::
++
+Whether the download commands should be adapted when the change refs
+are hidden.
++
+Git has a configuration option to hide refs from the initial
+advertisement (`uploadpack.hideRefs`). This option can be used to hide
+the change refs from the client. As consequence fetching changes by
+change ref does not work anymore. However by setting
+`uploadpack.allowTipSha1InWant` to `true` fetching changes by commit ID
+is possible. If `download.checkForHiddenChangeRefs` is set to `true`
+the git download commands use the commit ID instead of the change ref
+when a project is configured like this.
++
+Example git configuration on a project:
++
+----
+[uploadpack]
+  hideRefs = refs/changes/
+  hideRefs = refs/cache-automerge/
+  allowTipSha1InWant = true
+----
++
+By default `false`.
+
 [[download.archive]]download.archive::
 +
 Specifies which archive formats, if any, should be offered on the change
-screen:
+screen and supported for `git-upload-archive` operation:
 +
 ----
 [download]
@@ -1441,11 +1605,17 @@
   archive = tbz2
   archive = tgz
   archive = txz
+  archive = zip
 ----
 
 If `download.archive` is not specified defaults to all archive
 commands. Set to `off` or empty string to disable.
 
+Zip is not supported because it may be interpreted by a Java plugin as a
+valid JAR file, whose code would have access to cookies on the domain.
+For this reason `zip` format is always excluded from formats offered
+through the `Download` drop down or accessible in the REST API.
+
 [[gc]]
 === Section gc
 
@@ -1453,6 +1623,15 @@
 to run periodically. It will be triggered and executed sequentially for all
 projects.
 
+[[gc.aggressive]]gc.aggressive::
++
+Determines if scheduled garbage collections and garbage collections triggered
+through Web-UI should run in aggressive mode or not. Aggressive garbage
+collections are more expensive but may lead to significantly smaller
+repositories.
++
+Valid values are "true" and "false," default is "false".
+
 [[gc.startTime]]gc.startTime::
 +
 Start time to define the first execution of the git garbage collection.
@@ -1485,6 +1664,7 @@
 * `mon, month, months` (`1 month` is treated as `30 days`)
 * `y, year, years` (`1 year` is treated as `365 days`)
 
+[[schedule-examples]]
 Examples::
 +
 ----
@@ -1559,6 +1739,28 @@
 by the system administrator, and might not even be running on the
 same host as Gerrit.
 
+[[gerrit.docUrl]]gerrit.docUrl::
++
+Optional base URL for documentation, under which one can find
+"index.html", "rest-api.html", etc. Used as the base for the fixed set
+of links in the "Documentation" tab. A slash is implicitly appended.
+(For finer control over the top menu, consider writing a
+link:dev-plugins.html#top-menu-extensions[plugin].)
++
+If unset or empty, the documentation tab will only be shown if
+`/Documentation/index.html` can be reached by the browser at app load
+time.
+
+[[gerrit.editGpgKeys]]gerrit.editGpgKeys::
++
+If enabled and server-side signed push validation is also
+link:#receive.enableSignedPush[enabled], enable the
+link:rest-api-accounts.html#list-gpg-keys[REST API endpoints] and web UI
+for editing GPG keys. If disabled, GPG keys can only be added by
+administrators with direct git access to All-Users.
++
+Defaults to true.
+
 [[gerrit.installCommitMsgHookCommand]]gerrit.installCommitMsgHookCommand::
 +
 Optional command to install the `commit-msg` hook. Typically of the
@@ -1723,13 +1925,13 @@
 +
 Valid values are the characters '*', '(' and ')'.
 
-[[gitweb.linkDrafts]]gitweb.urlEncode::
+[[gitweb.urlEncode]]gitweb.urlEncode::
 +
 Whether or not Gerrit should encode the generated viewer URL.
 +
 Gerrit composes the viewer URL using information about the project, branch, file
 or commit of the target object to be displayed. Typically viewers such as CGit
-and GitWeb do need those parts to be encoded, including the '/' in project's name,
+and gitweb do need those parts to be encoded, including the '/' in project's name,
 for being correctly parsed.
 However other viewers could instead require an unencoded URL (e.g. GitHub web
 based viewer)
@@ -1805,6 +2007,11 @@
 Optional filename for the hashtags changed hook, if not specified then
 `hashtags-changed` will be used.
 
+[[hooks.projectCreatedHook]]hooks.projectCreatedHook::
++
+Optional filename for the project created hook, if not specified then
+`project-created` will be used.
+
 [[hooks.mergeFailedHook]]hooks.mergeFailedHook::
 +
 Optional filename for the merge failed hook, if not specified then
@@ -2042,7 +2249,7 @@
 thread pool waiting for a worker thread to become available.
 0 sets the queue size to the Integer.MAX_VALUE.
 +
-By default 50.
+By default 200.
 
 [[httpd.maxWait]]httpd.maxWait::
 +
@@ -2136,19 +2343,17 @@
 +
 A link:http://lucene.apache.org/[Lucene] index is used.
 +
-* `SOLR`
-+
-A link:https://cwiki.apache.org/confluence/display/solr/SolrCloud[
-SolrCloud] index is used.
 
 +
 By default, `LUCENE`.
 
 [[index.threads]]index.threads::
 +
-Number of threads to use for indexing in normal interactive operations.
+Number of threads to use for indexing in normal interactive operations. Setting
+it to 0 disables the dedicated thread pool and indexing will be done in the same
+thread as the operation.
 +
-Defaults to 1 if not set, or set to a negative value (unless
+Defaults to 0 if not set, or set to a negative value (unless
 link:#changeMerge.interactiveThreadPoolSize[changeMerge.interactiveThreadPoolSize]
 is iset).
 
@@ -2161,6 +2366,49 @@
 CPUs as returned by the JVM (unless
 link:#changeMerge.threadPoolSize[changeMerge.threadPoolSize] is set).
 
+[[index.onlineUpgrade]]index.onlineUpgrade::
++
+Whether to upgrade to new index schema versions while the server is
+running. This is recommended as it prevents additional downtime during
+Gerrit version upgrades (avoiding the need for an offline reindex step
+using Reindex), but can add additional server load during the upgrade.
++
+If set to false, there is no way to upgrade the index schema to take
+advantage of new search features without restarting the server.
++
+Defaults to true.
+
+[[index.maxLimit]]index.maxLimit::
++
+Maximum limit to allow for search queries. Requesting results above this
+limit will truncate the list (but will still set `_more_changes` on
+result lists). Set to 0 for no limit.
++
+Defaults to no limit.
+
+[[index.maxPages]]index.maxPages::
++
+Maximum number of pages of search results to allow, as index
+implementations may have to scan through large numbers of skipped
+results when searching with an offset. Requesting results starting past
+this threshold times the requested limit will result in an error. Set to
+0 for no limit.
++
+Defaults to no limit.
+
+[[index.maxTerms]]index.maxTerms::
++
+Maximum number of leaf terms to allow in a query. Too-large queries may
+perform poorly, so setting this option causes query parsing to fail fast
+before attempting to send them to the secondary index. Should this limit
+be reached, database is used instead of index as applicable.
++
+When the index type is `LUCENE`, also sets the maximum number of clauses
+permitted per BooleanQuery. This is so that all enforced query limits
+are the same.
++
+Defaults to 1024.
+
 ==== Lucene configuration
 
 Open and closed changes are indexed in separate indexes named
@@ -2168,14 +2416,6 @@
 
 The following settings are only used when the index type is `LUCENE`.
 
-[[index.defaultMaxClauseCount]]index.defaultMaxClauseCount::
-+
-Only used when the type is `LUCENE`.
-+
-Sets the maximum number of clauses permitted per BooleanQuery.
-+
-Defaults to 1024.
-
 [[index.name.ramBufferSize]]index.name.ramBufferSize::
 +
 Determines the amount of RAM that may be used for buffering added documents
@@ -2219,7 +2459,6 @@
 ----
 [index]
   type = LUCENE
-  defaultMaxClauseCount = 2048
 
 [index "changes_open"]
   ramBufferSize = 60 m
@@ -2230,17 +2469,6 @@
   maxBufferedDocs = 500
 ----
 
-==== Solr configuration
-
-Open and closed changes are indexed in separate indexes named
-'changes_open' and 'changes_closed' respectively.
-
-The following settings are only used when the index type is `SOLR`.
-
-[[index.url]]index.url::
-+
-URL of the index server.
-
 [[ldap]]
 === Section ldap
 
@@ -2414,7 +2642,10 @@
 example `${userPrincipalName.localPart}` would provide only 'user'.
 +
 If set, users will be unable to modify their SSH username field, as
-Gerrit will populate it only from the LDAP data.
+Gerrit will populate it only from the LDAP data. Note that once the
+username has been set it cannot be changed, therefore it is
+recommended not to make changes to this setting that would cause the
+value to differ, as this will prevent users from logging in.
 +
 Default is `uid` for RFC 2307 servers,
 and `${sAMAccountName.toLowerCase}` for Active Directory.
@@ -2592,6 +2823,22 @@
   javaOptions = -Dcom.sun.jndi.ldap.connect.pool.timeout=300000
 ----
 
+[[log]]
+=== Section log
+
+[[log.jsonLogging]]log.jsonLogging::
++
+If set to true, enables error logging in JSON format (file name: "logs/error_log.json").
++
+Defaults to false.
+
+[[log.textLogging]]log.textLogging::
++
+If set to true, enables error logging in regular plain text format. Can only be disabled
+if `jsonLogging` is enabled.
++
+Defaults to true.
+
 [[mimetype]]
 === Section mimetype
 
@@ -2668,22 +2915,63 @@
 and SSH.  If set to true Administrators can install new plugins
 remotely, or disable existing plugins.  Defaults to false.
 
+[[plugins.jsLoadTimeout]]plugins.jsLoadTimeout::
++
+Set the timeout value for loading JavaScript plugins in Gerrit UI.
+Values can be specified using standard time unit abbreviations ('ms',
+'sec', 'min', etc.).
++
+Default is 5 seconds. Negative values will be converted to 0.
 
 [[receive]]
 === Section receive
 
-This section is used to set who can execute the 'receive-pack' and
-to limit the maximum Git object size that 'receive-pack' will accept.
-'receive-pack' is what runs on the server during a user's push or
-repo upload command. It also contains some advanced options for tuning the
-behavior of Gerrit's 'receive-pack' mechanism.
+This section is used to configure behavior of the 'receive-pack'
+handler, which responds to 'git push' requests.
 
-----
-[receive]
-  allowGroup = GROUP_ALLOWED_TO_EXECUTE
-  allowGroup = YET_ANOTHER_GROUP_ALLOWED_TO_EXECUTE
-  maxObjectSizeLimit = 40 m
-----
+[[receive.allowGroup]]receive.allowGroup::
++
+Name of the groups of users that are allowed to execute
+'receive-pack' on the server. One or more groups can be set.
++
+If no groups are added, any user will be allowed to execute
+'receive-pack' on the server.
+
+[[receive.certNonceSeed]]receive.certNonceSeed::
++
+If set to a non-empty value and server-side signed push validation is
+link:#receive.enableSignedPush[enabled], use this value as the seed to
+the HMAC SHA-1 nonce generator. If unset, a 64-byte random seed will be
+generated at server startup.
++
+As this is used as the seed of a cryptographic algorithm, it is
+recommended to be placed in link:#secure-config[`secure.config`].
++
+Defaults to unset.
+
+[[receive.certNonceSlop]]receive.certNonceSlop::
++
+When validating the nonce passed as part of the signed push protocol,
+accept valid nonces up to this many seconds old. This allows
+certificate verification to work over HTTP where there is a lag between
+the HTTP response providing the nonce to sign and the next request
+containing the signed nonce. This can be significant on large
+repositories, since the lag also includes the time to count objects on
+the client.
++
+Default is 5 minutes.
+
+[[receive.changeUpdateThreads]]receive.changeUpdateThreads::
++
+Number of threads to perform change creation or patch set updates
+concurrently. Each thread uses its own database connection from
+the database connection pool, and if all threads are busy then
+main receive thread will also perform a change creation or patch
+set update.
++
+Defaults to 1, using only the main receive thread. This feature is for
+databases with very high latency that can benefit from concurrent
+operations when multiple changes are impacted at once.
 
 [[receive.checkMagicRefs]]receive.checkMagicRefs::
 +
@@ -2713,13 +3001,30 @@
 +
 Default is true.
 
-[[receive.allowGroup]]receive.allowGroup::
+[[receive.enableSignedPush]]receive.enableSignedPush::
 +
-Name of the groups of users that are allowed to execute
-'receive-pack' on the server. One or more groups can be set.
+If true, server-side signed push validation is enabled.
 +
-If no groups are added, any user will be allowed to execute
-'receive-pack' on the server.
+When a client pushes with `git push --signed`, this ensures that the
+push certificate is valid and signed with a valid public key stored in
+the `refs/meta/gpg-keys` branch of `All-Users`.
++
+Defaults to false.
+
+[[receive.maxBatchChanges]]receive.maxBatchChanges::
++
+The maximum number of changes that Gerrit allows to be pushed
+in a batch for review. When this number is exceeded Gerrit rejects
+the push with an error message.
++
+May be overridden for certain groups by specifying a limit in the
+link:access-control.html#capability_batchChangesLimit['Batch Changes Limit']
+global capability.
++
+This setting can be used to prevent users from uploading large
+number of changes for review by mistake.
++
+Default is zero, no limit.
 
 [[receive.maxObjectSizeLimit]]receive.maxObjectSizeLimit::
 +
@@ -2740,20 +3045,13 @@
 +
 Common unit suffixes of 'k', 'm', or 'g' are supported.
 
-[[receive.maxBatchChanges]]receive.maxBatchChanges::
+[[receive.maxTrustDepth]]receive.maxTrustDepth::
 +
-The maximum number of changes that Gerrit allows to be pushed
-in a batch for review. When this number is exceeded Gerrit rejects
-the push with an error message.
+If signed push validation is link:#receive.enableSignedPush[enabled],
+set to the maximum depth to search when checking if a key is
+link:#receive.trustedKey[trusted].
 +
-May be overridden for certain groups by specifying a limit in the
-link:access-control.html#capability_batchChangesLimit['Batch Changes Limit']
-global capability.
-+
-This setting can be used to prevent users from uploading large
-number of changes for review by mistake.
-+
-Default is zero, no limit.
+Default is 0, meaning only explicitly trusted keys are allowed.
 
 [[receive.threadPoolSize]]receive.threadPoolSize::
 +
@@ -2762,18 +3060,6 @@
 +
 Defaults to the number of available CPUs according to the Java runtime.
 
-[[receive.changeUpdateThreads]]receive.changeUpdateThreads::
-+
-Number of threads to perform change creation or patch set updates
-concurrently. Each thread uses its own database connection from
-the database connection pool, and if all threads are busy then
-main receive thread will also perform a change creation or patch
-set update.
-+
-Defaults to 1, using only the main receive thread. This feature is for
-databases with very high latency that can benefit from concurrent
-operations when multiple changes are impacted at once.
-
 [[receive.timeout]]receive.timeout::
 +
 Overall timeout on the time taken to process the change data in
@@ -2782,9 +3068,28 @@
 be specified using standard time unit abbreviations ('ms', 'sec',
 'min', etc.).
 +
-Default is 2 minutes. If no unit is specified, milliseconds
+Default is 4 minutes. If no unit is specified, milliseconds
 is assumed.
 
+[[receive.trustedKey]]receive.trustedKey::
++
+List of GPG key fingerprints that should be considered trust roots by
+the server when signed push validation is
+link:#receive.enableSignedPush[enabled]. A key is trusted by the server
+if it is either in this list, or a path of trust signatures leads from
+the key to a configured trust root. The maximum length of the path is
+determined by link:#receive.maxTrustDepth[`receive.maxTrustDepth`].
++
+Key fingerprints can be displayed with `gpg --list-keys
+--with-fingerprint`.
++
+Trust signatures can be added to a key using the `tsign` command to
+link:https://www.gnupg.org/documentation/manuals/gnupg/OpenPGP-Key-Management.html[
+`gpg --edit-key`], after which the signed key should be re-uploaded.
++
+If no keys are specified, web-of-trust checks are disabled. This is the
+default behavior.
+
 
 [[repository]]
 === Section repository
@@ -2799,9 +3104,22 @@
   ownerGroup = Registered Users
 ----
 
-[NOTE]
-Currently only the repository name `*` is supported.
-This is a wildcard designating all repositories.
+The only matching patterns supported are exact match or wildcard matching which
+can be specified by ending the name with a `*`. If a project matches more than one
+repository configuration, then the configuration from the more precise match
+will be used. In the following example, the default submit type for a project
+named `project/plugins/a` would be `CHERRY_PICK`.
+
+----
+[repository "project/*"]
+  defaultSubmitType = MERGE_IF_NECESSARY
+[repository "project/plugins/*"]
+  defaultSubmitType = CHERRY_PICK
+----
+
+[NOTE] All properties are used from the matching repository configuration. In
+the previous example, all properties will be used from `project/plugins/\*`
+section and no properties will be inherited nor overridden from `project/*`.
 
 [[repository.name.defaultSubmitType]]repository.<name>.defaultSubmitType::
 +
@@ -2919,7 +3237,7 @@
 <<user.name,user.name>> and <<user.email,user.email>>, or guessed
 from the local operating system.
 +
-* 'Code Review' `<`'review'`@`'example.com'`>`
+* `Code Review <review@example.com>`
 +
 If set to a name and email address in brackets, Gerrit will use
 this name and email address for any messages, overriding the name
@@ -3036,6 +3354,23 @@
 [[sshd]]
 === Section sshd
 
+[[sshd.enableCompression]]sshd.enableCompression::
++
+In the general case, we want to disable transparent compression, since
+the majority of our data transfer is highly compressed Git pack files
+and we cannot make them any smaller than they already are.
++
+However, if there are CPU in abundance and the server is reachable
+through slow networks, gits with huge amount of refs can benefit from
+SSH-compression since git does not compress the ref announcement during
+handshake.
++
+Compression can be especially useful when Gerrit slaves are being used
+for the larger clones and fetches and the master server mostly takes
+small receive-packs.
++
+By default, `false`.
+
 [[sshd.backend]]sshd.backend::
 +
 Starting from version 0.9.0 Apache SSHD project added support for NIO2
@@ -3049,12 +3384,12 @@
 Specifies the local addresses the internal SSHD should listen
 for connections on.  The following forms may be used to specify
 an address.  In any form, `:'port'` may be omitted to use the
-default of 29418.
+default of `29418`.
 +
-* 'hostname':'port' (for example `review.example.com:29418`)
-* 'IPv4':'port' (for example `10.0.0.1:29418`)
-* ['IPv6']:'port' (for example `[ff02::1]:29418`)
-* +*:'port'+ (for example `+*:29418+`)
+* `'hostname':'port'` (for example `review.example.com:29418`)
+* `'IPv4':'port'` (for example `10.0.0.1:29418`)
+* `['IPv6']:'port'` (for example `[ff02::1]:29418`)
+* `+*:'port'+` (for example `+*:29418+`)
 
 +
 --
@@ -3063,7 +3398,7 @@
 
 To disable the internal SSHD, set listenAddress to `off`.
 
-By default, *:29418.
+By default, `*:29418`.
 --
 
 [[sshd.advertisedAddress]]sshd.advertisedAddress::
@@ -3074,16 +3409,16 @@
 22. The following forms may be used to specify an address.  In any
 form, `:'port'` may be omitted to use the default SSH port of 22.
 
-* 'hostname':'port' (for example `review.example.com:22`)
-* 'IPv4':'port' (for example `10.0.0.1:29418`)
-* ['IPv6']:'port' (for example `[ff02::1]:29418`)
+* `'hostname':'port'` (for example `review.example.com:22`)
+* `'IPv4':'port'` (for example `10.0.0.1:29418`)
+* `['IPv6']:'port'` (for example `[ff02::1]:29418`)
 
 +
 --
 If multiple values are supplied, the daemon will advertise all
 of them.
 
-By default, sshd.listenAddress.
+By default uses the value of `sshd.listenAddress`.
 --
 
 [[sshd.tcpKeepAlive]]sshd.tcpKeepAlive::
@@ -3093,7 +3428,7 @@
 +
 Only effective when `sshd.backend` is set to `MINA`.
 +
-By default, true.
+By default, `true`.
 
 [[sshd.threads]]sshd.threads::
 +
@@ -3119,7 +3454,7 @@
 than the total number of threads allocated in sshd.threads, then the
 value of sshd.threads is increased to accommodate the requested value.
 +
-By default, 0.
+By default is 1 on single core node, 2 otherwise.
 
 [[sshd.streamThreads]]sshd.streamThreads::
 +
@@ -3194,8 +3529,8 @@
 to the default ciphers, cipher names starting with `-` are removed
 from the default cipher set.
 +
-Supported ciphers: aes128-cbc, aes128-cbc, aes256-cbc, blowfish-cbc,
-3des-cbc, none.
+Supported ciphers: `aes128-cbc`, `aes128-cbc`, `aes256-cbc`, `blowfish-cbc`,
+`3des-cbc`, `none`.
 +
 By default, all supported ciphers except `none` are available.
 
@@ -3207,7 +3542,8 @@
 are enabled in addition to the default MACs, MAC names starting with
 `-` are removed from the default MACs.
 +
-Supported MACs: hmac-md5, hmac-md5-96, hmac-sha1, hmac-sha1-96.
+Supported MACs: `hmac-md5`, `hmac-md5-96`, `hmac-sha1`, `hmac-sha1-96`,
+`hmac-sha2-256`, `hmac-sha2-512`.
 +
 By default, all supported MACs are available.
 
@@ -3247,7 +3583,7 @@
 `log4j.appender` with the name `sshd_log` can be configured to overwrite
 programmatic configuration.
 +
-By default, true.
+By default, `true`.
 
 [[sshd.rekeyBytesLimit]]sshd.rekeyBytesLimit::
 +
@@ -3256,7 +3592,7 @@
 +
 By default, 1073741824 (bytes, 1GB).
 +
-The rekeyBytesLimit cannot be set to lower than 32.
+The `rekeyBytesLimit` cannot be set to lower than 32.
 
 [[sshd.rekeyTimeLimit]]sshd.rekeyTimeLimit::
 +
@@ -3295,7 +3631,9 @@
 
 [[suggest.fullTextSearch]]suggest.fullTextSearch::
 +
-If 'true' the reviewer completion suggestions will be based on a full text search.
+If `true` the reviewer completion suggestions will be based on a full text search.
++
+By default `false`.
 
 [[suggest.from]]suggest.from::
 +
@@ -3512,6 +3850,58 @@
 If no groups are added, any user will be allowed to execute
 'upload-pack' on the server.
 
+[[urlAlias]]
+=== Section urlAlias
+
+URL aliases define regular expressions for URL tokens that are mapped
+to target URL tokens.
+
+Each URL alias must be specified in its own subsection. The subsection
+name should be a descriptive name. It must be unique, but is not
+interpreted in any way.
+
+The URL aliases are applied in no particular order. The first matching
+URL alias is used and further matches are ignored.
+
+URL aliases can be used to map plugin screens into the Gerrit URL
+namespace, or to replace Gerrit screens by plugin screens.
+
+Example:
+
+----
+[urlAlias "MyPluginScreen"]
+  match = /myscreen/(.*)
+  token = /x/myplugin/myscreen/$1
+[urlAlias "MyChangeScreen"]
+  match = /c/(.*)
+  token = /x/myplugin/c/$1
+----
+
+[[urlAlias.match]]urlAlias.match::
++
+A regular expression for a URL token.
++
+The matched URL token is replaced by `urlAlias.token`.
+
+[[urlAlias.token]]urlAlias.token::
++
+The target URL token.
++
+It can contain placeholders for the groups matched by the
+`urlAlias.match` regular expression: `$1` for the first matched group,
+`$2` for the second matched group, etc.
+
+[[submodule]]
+=== Section submodule
+
+[[submodule.verbosesuperprojectupdate]]submodule.verboseSuperprojectUpdate
++
+When using link:user-submodules.html#automatic_update[automatic superproject updates]
+this option will determine if the submodule commit messages are included into
+the commit message of the superproject update.
++
+By default this is true.
+
 
 [[user]]
 === Section user
@@ -3541,7 +3931,7 @@
 By default "Anonymous Coward" is used.
 
 
-== File `etc/secure.config`
+== [[secure.config]]File `etc/secure.config`
 The optional file `'$site_path'/etc/secure.config` overrides (or
 supplements) the settings supplied by `'$site_path'/etc/gerrit.config`.
 The file should be readable only by the daemon process and can be
@@ -3552,7 +3942,6 @@
 ----
 [auth]
   registerEmailPrivateKey = 2zHNrXE2bsoylzUqDxZp0H1cqUmjgWb6
-  restTokenPrivateKey = 7e40PzCjlUKOnXATvcBNXH6oyiu+r0dFk2c=
 
 [database]
   username = webuser
diff --git a/Documentation/config-gitweb.txt b/Documentation/config-gitweb.txt
index 2311184..63eaffd 100644
--- a/Documentation/config-gitweb.txt
+++ b/Documentation/config-gitweb.txt
@@ -67,12 +67,12 @@
 
 For the external configuration, gitweb runs under the control of an
 external web server, and Gerrit access controls are not enforced. Gerrit
-provides configuration parameters for integration with GitWeb.
+provides configuration parameters for integration with gitweb.
 
 [[linuxGitWeb]]
 ==== Linux Installation
 
-===== Install GitWeb
+===== Install Gitweb
 
 On Ubuntu:
 
@@ -86,7 +86,7 @@
   $ yum install gitweb
 ====
 
-===== Configure GitWeb
+===== Configure Gitweb
 
 
 Update `/etc/gitweb.conf`, add the public GIT repositories:
@@ -161,7 +161,7 @@
 [[WindowsGitWeb]]
 ==== Windows Installation
 
-Instructions are available for installing the GitWeb module distributed with
+Instructions are available for installing the gitweb module distributed with
 MsysGit:
 
 link:https://github.com/msysgit/msysgit/wiki/GitWeb[GitWeb]
@@ -176,7 +176,7 @@
 tech note useful for configuring Apache Service to run under another account.
 You must grant the new account link:http://technet.microsoft.com/en-us/library/cc794944(WS.10).aspx["run as service"] permission:
 
-The GitWeb version in msysgit is missing several important and required
+The gitweb version in msysgit is missing several important and required
 perl modules, including CGI.pm. The perl included with the msysgit distro 1.7.8
 is broken.. The link:http://groups.google.com/group/msysgit/browse_thread/thread/ba3501f1f0ed95af[unicore folder is missing along with utf8_heavy.pl and CGI.pm]. You can
 verify by checking for perl modules. From an msys console, execute the
@@ -207,7 +207,7 @@
 
 copy the contents of lib into `msysgit/lib/perl5/5.8.8` and overwrite existing files.
 
-==== Enable GitWeb Integration
+==== Enable Gitweb Integration
 
 To enable the external gitweb integration, set
 link:config-gerrit.html#gitweb.url[gitweb.url] with the URL of your
@@ -219,7 +219,7 @@
 namespace is available.
 
 ----
-$ git config -f $site_path/etc/gerrit.config gitweb.cgi $PATH_TO_GITWEB/gitweb.cgi
+$ git config -f $site_path/etc/gerrit.config --unset gitweb.cgi
 $ git config -f $site_path/etc/gerrit.config gitweb.url https://gitweb.corporation.com
 ----
 
diff --git a/Documentation/config-hooks.txt b/Documentation/config-hooks.txt
index ce908b9..3068cc5 100644
--- a/Documentation/config-hooks.txt
+++ b/Documentation/config-hooks.txt
@@ -110,6 +110,14 @@
   ref-updated --oldrev <old rev> --newrev <new rev> --refname <ref name> --project <project name> --submitter <submitter>
 ====
 
+=== project-created
+
+Called whenever a project has been created.
+
+====
+  project-created --project <project name> --head <head name>
+====
+
 === reviewer-added
 
 Called whenever a reviewer is added to a change.
diff --git a/Documentation/config-labels.txt b/Documentation/config-labels.txt
index ce49d31..a40c5f3 100644
--- a/Documentation/config-labels.txt
+++ b/Documentation/config-labels.txt
@@ -243,7 +243,7 @@
 This is the case if the change was rebased onto a different parent.
 This can be used to enable sticky approvals, reducing turn-around for
 trivial rebases prior to submitting a change.
-It is recommended to enable this for the Code-Review label.
+For the pre-installed Code-Review label this is enabled by default.
 Defaults to false.
 
 [[label_copyAllScoresIfNoCodeChange]]
@@ -255,7 +255,8 @@
 the commit message is different. This can be used to enable sticky
 approvals on labels that only depend on the code, reducing turn-around
 if only the commit message is changed prior to submitting a change.
-It is recommended to enable this for the Verified label if enabled.
+For the Verified label that is installed by the link:pgm-init.html[init]
+site program this is enabled by default.
 Defaults to false.
 
 [[label_copyAllScoresIfNoChange]]
diff --git a/Documentation/config-login-register.txt b/Documentation/config-login-register.txt
index 76f47ed..2e775b4 100644
--- a/Documentation/config-login-register.txt
+++ b/Documentation/config-login-register.txt
@@ -89,7 +89,8 @@
 
 * Real name (visible name in Gerrit)
 * Register your email (it must be confirmed later)
-* Select a username with which to communicate with Gerrit over ssh+git
+* Select a username with which to communicate with Gerrit over ssh+git. Note
+that once saved, the username cannot be changed.
 
 * The server will ask you for an RSA public key.
 That's the key we generated above, and it's time to make sure that Gerrit knows
diff --git a/Documentation/config-mail.txt b/Documentation/config-mail.txt
index 62d0219..da213a8 100644
--- a/Documentation/config-mail.txt
+++ b/Documentation/config-mail.txt
@@ -28,6 +28,12 @@
 to a change being abandoned.  It is a `ChangeEmail`: see `ChangeSubject.vm` and
 `ChangeFooter.vm`.
 
+=== AddKey.vm
+
+The `AddKey.vm` template will determine the contents of the email related to
+SSH and GPG keys being added to a user account. This notification is not sent
+when the key is administratively added to another user account.
+
 === ChangeFooter.vm
 
 The `ChangeFooter.vm` template will determine the contents of the footer
diff --git a/Documentation/config-plugins.txt b/Documentation/config-plugins.txt
index 3f84bda..ca95099 100644
--- a/Documentation/config-plugins.txt
+++ b/Documentation/config-plugins.txt
@@ -102,6 +102,16 @@
 link:https://gerrit.googlesource.com/plugins/reviewnotes/+doc/master/src/main/resources/Documentation/about.md[
 Documentation]
 
+[[review-strategy]]
+=== review-strategy
+
+This plugin allows users to configure different review strategies.
+
+link:https://gerrit-review.googlesource.com/#/admin/projects/plugins/review-strategy[
+Project] |
+link:https://gerrit.googlesource.com/plugins/review-strategy/+/master/src/main/resources/Documentation/about.md[
+Documentation]
+
 [[singleusergroup]]
 === singleusergroup
 
@@ -118,9 +128,12 @@
 these plugins.
 
 The Gerrit Project doesn't provide binaries for these plugins, but
-there are some public services, like the
-link:https://ci.gerritforge.com/[CI Server from GerritForge], that
-offer the download of ready plugin jars.
+there are some public services that offer the download of pre-built
+plugin jars:
+
+* link:https://gerrit-ci.gerritforge.com[CI Server from GerritForge]
+* link:http://builds.quelltextlich.at/gerrit/nightly/index.html[
+  CI Server from Quelltextlich]
 
 The following list gives an overview about available plugins, but the
 list may not be complete. You may discover more plugins on
@@ -166,7 +179,7 @@
 
 This plugin allows the rendering of Git repository branch network in a
 graphical HTML5 Canvas. It is mainly intended to be used as a
-"project link" in a GitWeb configuration or by other Gerrit GWT UI
+"project link" in a gitweb configuration or by other Gerrit GWT UI
 plugins to be plugged elsewhere in Gerrit.
 
 link:https://gerrit-review.googlesource.com/#/admin/projects/plugins/branch-network[
@@ -367,6 +380,19 @@
 https://gerrit.googlesource.com/plugins/javamelody/+doc/master/src/main/resources/Documentation/config.md[
 Configuration]
 
+[[labelui]]
+=== labelui
+
+The labelui plugin adds a user preference that allows users to choose a
+table control to render the labels/approvals on the change screen
+(similar to how labels/approvals were rendered on the old change
+screen).
+
+link:https://gerrit-review.googlesource.com/#/admin/projects/plugins/labelui[
+Project] |
+link:https://gerrit.googlesource.com/plugins/labelui/+doc/master/src/main/resources/Documentation/about.md[
+Documentation]
+
 [[menuextender]]
 === menuextender
 
@@ -396,6 +422,25 @@
 link:https://gerrit.googlesource.com/plugins/motd/+doc/master/src/main/resources/Documentation/config.md[
 Configuration]
 
+[[oauth-authentication-provider]]
+=== OAuth authentication provider
+This plugin enables Gerrit to use OAuth2 protocol for authentication.
+Two different OAuth providers are supported:
+
+* GitHub
+* Google
+
+https://github.com/davido/gerrit-oauth-provider[Project] |
+https://github.com/davido/gerrit-oauth-provider/wiki/Getting-Started[Configuration]
+
+[[owners]]
+=== owners
+This plugin provides a Prolog predicate `add_owner_approval/3` that
+appends `label('Owner-Approval', need(_))` to a provided list.
+
+link:https://gerrit-review.googlesource.com/#/admin/projects/plugins/owners[Project] |
+link:https://gerrit.googlesource.com/plugins/owners/+/refs/heads/master/README.md[Documentation]
+
 [[project-download-commands]]
 === project-download-commands
 
@@ -428,6 +473,31 @@
 link:https://gerrit.googlesource.com/plugins/quota/+doc/master/src/main/resources/Documentation/config.md[
 Configuration]
 
+[[ref-protection]]
+=== ref-protection
+
+A plugin that protects against commits being lost.
+
+Backups of deleted or non-fast-forward updated refs are created under the
+`refs/backups/` namespace.
+
+link:https://gerrit-review.googlesource.com/#/admin/projects/plugins/ref-protection[
+Project] |
+link:https://gerrit.googlesource.com/plugins/ref-protection/+/refs/heads/stable-2.11/src/main/resources/Documentation/about.md[
+Documentation]
+
+[[reparent]]
+=== reparent
+
+A plugin that provides project reparenting as a self-service for project owners.
+
+link:https://gerrit-review.googlesource.com/#/admin/projects/plugins/reparent[
+Project] |
+link:https://gerrit.googlesource.com/plugins/reparent/+doc/master/src/main/resources/Documentation/about.md[
+Documentation] |
+link:https://gerrit.googlesource.com/plugins/reparent/+doc/master/src/main/resources/Documentation/config.md[
+Configuration]
+
 [[reviewers]]
 === reviewers
 
diff --git a/Documentation/config-project-config.txt b/Documentation/config-project-config.txt
index 276117b..7d03681 100644
--- a/Documentation/config-project-config.txt
+++ b/Documentation/config-project-config.txt
@@ -154,6 +154,27 @@
 +
 The default value for this is true, false disables the checks.
 
+[[receive.enableSignedPush]]receive.enableSignedPush::
++
+Controls whether server-side signed push validation is enabled on the
+project. Only has an effect if signed push validation is enabled on the
+server; see the link:config-gerrit.html#receive.enableSignedPush[global
+configuration] for details.
++
+Default is `INHERIT`, which means that this property is inherited from
+the parent project.
+
+[[receive.requireSignedPush]]receive.requireSignedPush::
++
+Controls whether server-side signed push validation is required on the
+project. Only has an effect if signed push validation is enabled on the
+server, and link:#receive.enableSignedPush is set on the project. See
+the link:config-gerrit.html#receive.enableSignedPush[global
+configuration] for details.
++
+Default is `INHERIT`, which means that this property is inherited from
+the parent project.
+
 [[submit-section]]
 === Submit section
 
diff --git a/Documentation/config-reverseproxy.txt b/Documentation/config-reverseproxy.txt
index 7f30b14..eff777b 100644
--- a/Documentation/config-reverseproxy.txt
+++ b/Documentation/config-reverseproxy.txt
@@ -48,6 +48,8 @@
 	  <Proxy *>
 	    Order deny,allow
 	    Allow from all
+	    # Use following line instead of the previous two on Apache >= 2.4
+	    # Require all granted
 	  </Proxy>
 
 	  AllowEncodedSlashes On
@@ -97,7 +99,7 @@
 	  listen 80;
 	  server_name review.example.com;
 
-	  location /r/ {
+	  location ^~ /r/ {
 	    proxy_pass        http://127.0.0.1:8081;
 	    proxy_set_header  X-Forwarded-For $remote_addr;
 	    proxy_set_header  Host $host;
diff --git a/Documentation/config-themes.txt b/Documentation/config-themes.txt
index 5c3a448..b165e37 100644
--- a/Documentation/config-themes.txt
+++ b/Documentation/config-themes.txt
@@ -133,9 +133,6 @@
 block in the header or footer will execute before Gerrit has defined
 the function and is ready to register the hook callback.
 
-The function `gerrit_addHistoryHook` is deprecated and may be
-removed in a future release.
-
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/database-setup.txt b/Documentation/database-setup.txt
index 4f854fb..d720287 100644
--- a/Documentation/database-setup.txt
+++ b/Documentation/database-setup.txt
@@ -18,6 +18,21 @@
 
 If this option interests you, you might want to consider link:install-quick.html[the quick guide].
 
+[[createdb_derby]]
+=== Apache Derby
+
+If Derby is selected, Gerrit will automatically set up the embedded Derby
+database as backend so no set up or configuration is necessary.
+
+Currently only support for embedded mode is added. There are two other
+deployment options for Apache Derby that can be added later:
+
+* link:http://db.apache.org/derby/papers/DerbyTut/ns_intro.html#Network+Server+Options[
+Derby Network Server (standalone mode)]
+
+* link:http://db.apache.org/derby/papers/DerbyTut/ns_intro.html#Embedded+Server[
+Embedded Server (hybrid mode)]
+
 [[createdb_postgres]]
 === PostgreSQL
 
@@ -146,6 +161,51 @@
 Visit SAP MaxDB's link:http://maxdb.sap.com/documentation/[documentation] for further
 information regarding using SAP MaxDB.
 
+[[createdb_db2]]
+=== DB2
+
+IBM DB2 is a supported database for running Gerrit Code Review. However it is
+recommended only for environments where you intend to run Gerrit on an existing
+DB2 installation to reduce administrative overhead.
+
+Create a system wide user for the Gerrit application, and grant the user
+full rights on the newly created database:
+
+----
+  db2 => create database gerrit
+  db2 => connect to gerrit
+  db2 => grant connect,accessctrl,dataaccess,dbadm,secadm on database to gerrit2;
+----
+
+JDBC driver db2jcc4.jar and db2jcc_license_cu.jar must be obtained
+from your DB2 distribution. Gerrit initialization process tries to copy
+it from a known location:
+
+----
+/opt/ibm/db2/V10.5/java/db2jcc4.jar
+/opt/ibm/db2/V10.5/java/db2jcc_license_cu.jar
+----
+
+If these files cannot be located at this place, then an alternative location
+can be provided during init step execution.
+
+Sample database section in $site_path/etc/gerrit.config:
+
+----
+[database]
+        type = db2
+        database = gerrit
+        hostname = localhost
+        username = gerrit2
+        port = 50001
+----
+
+Sample database section in $site_path/etc/secure.config:
+
+----
+[database]
+        password = secret_pasword
+----
 
 GERRIT
 ------
diff --git a/Documentation/dev-buck.txt b/Documentation/dev-buck.txt
index 2eb478c..ec8515f 100644
--- a/Documentation/dev-buck.txt
+++ b/Documentation/dev-buck.txt
@@ -7,7 +7,7 @@
 
 There is currently no binary distribution of Buck, so it has to be manually
 built and installed.  Apache Ant is required.  Currently only Linux and Mac
-OS are supported.  Buck requires Python version 2.7 to be installed.
+OS are supported.
 
 Clone the git and build it:
 
@@ -50,6 +50,10 @@
 script from `./scripts/buck_completion.bash` in the buck project.  Refer
 to the script's header comments for installation instructions.
 
+== Prerequisites
+
+Buck requires Python version 2.7 to be installed. The Maven download toolchain
+requires `curl` to be installed.
 
 [[eclipse]]
 == Eclipse Integration
@@ -63,17 +67,7 @@
   tools/eclipse/project.py
 ----
 
-In Eclipse, choose 'Import existing project' and select the `gerrit` project
-from the current working directory.
-
-Expand the `gerrit` project, right-click on the `buck-out` folder, select
-'Properties', and then under 'Attributes' check 'Derived'.
-
-Note that if you make any changes in the project configuration
-that get saved to the `.project` file, for example adding Resource
-Filters on a folder, they will be overwritten the next time you run
-`tools/eclipse/project.py`.
-
+and then follow the link:dev-eclipse.html#setup[setup instructions].
 
 === Refreshing the Classpath
 
@@ -112,10 +106,24 @@
 The output executable WAR will be placed in:
 
 ----
-  buck-out/gen/gerrit.war
+  buck-out/gen/gerrit/gerrit.war
 ----
 
 
+=== Headless Mode
+
+To build Gerrit in headless mode, i.e. without the GWT Web UI:
+
+----
+  buck build headless
+----
+
+The output executable WAR will be placed in:
+
+----
+  buck-out/gen/headless/headless.war
+----
+
 === Extension and Plugin API JAR Files
 
 To build the extension, plugin and GWT API JAR files:
@@ -129,8 +137,8 @@
 
 ----
   buck-out/gen/gerrit-plugin-api/plugin-api.jar
+  buck-out/gen/gerrit-plugin-api/plugin-api-javadoc/plugin-api-javadoc.jar
   buck-out/gen/gerrit-plugin-api/plugin-api-src.jar
-  buck-out/gen/gerrit-plugin-api/plugin-api-javadoc.jar
 ----
 
 Install {extension,plugin,gwt}-api to the local maven repository:
@@ -162,7 +170,7 @@
 The JAR files will also be packaged in:
 
 ----
-  buck-out/gen/plugins/core.zip
+  buck-out/gen/plugins/core/core.zip
 ----
 
 To build a specific plugin:
@@ -216,7 +224,7 @@
 The html files will also be bundled into `searchfree.zip` in this location:
 
 ----
-  buck-out/gen/Documentation/searchfree.zip
+  buck-out/gen/Documentation/searchfree/searchfree.zip
 ----
 
 To build the executable WAR with the documentation included:
@@ -228,7 +236,7 @@
 The WAR file will be placed in:
 
 ----
-  buck-out/gen/withdocs.war
+  buck-out/gen/withdocs/withdocs.war
 ----
 
 [[soyc]]
@@ -264,54 +272,47 @@
 The output release WAR will be placed in:
 
 ----
-  buck-out/gen/release.war
-----
-
-[[all]]
-=== Combined build target
-
-To build release and api targets, a combined build target is provided:
-
-----
-  buck build all
+  buck-out/gen/release/release.war
 ----
 
 [[tests]]
 == Running Unit Tests
 
-To run all tests including acceptance tests:
+To run all tests including acceptance tests (but not flaky tests):
 
 ----
-  buck test
+  buck test --exclude flaky
 ----
 
-To exclude slow tests:
+To exclude flaky and slow tests:
 
 ----
-  buck test --all --exclude slow
+  buck test --exclude flaky slow
 ----
 
-To include a specific group of acceptance tests:
+To run only a specific group of acceptance tests:
 
 ----
-  buck test --all --include api
+  buck test --include api
 ----
 
 The following groups of tests are currently supported:
 
+* acceptance
 * api
 * edit
+* flaky
 * git
 * pgm
 * rest
 * server
 * ssh
+* slow
 
-To run a specific test, e.g. the acceptance test
-`com.google.gerrit.acceptance.git.HttpPushForReviewIT`:
+To run a specific test group, e.g. the rest-account test group:
 
 ----
-  buck test //gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git:HttpPushForReviewIT
+  buck test //gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account:rest-account
 ----
 
 To create test coverage report:
@@ -370,62 +371,6 @@
  )
 ----
 
-== Building against unpublished JARs, that change frequently
-
-If a dependent Gerrit library is undergoing active development it must be
-recompiled and the change must be reflected in the Buck build process. For
-example testing Gerrit against changed JGit snapshot version. After building
-JGit library, the artifacts are created in local Maven build directory, e. g.:
-
-----
-  mvn package
-  /home/<user>/projects/jgit/org.eclipse.jgit/target/org.eclipse.jgit-3.3.0-SNAPSHOT.jar
-  /home/<user>/projects/jgit/org.eclipse.jgit/target/org.eclipse.jgit-3.3.0-SNAPSHOT-sources.jar
-----
-
-If as usual, installation of the build artifacts takes place in local maven
-repository, then the Buck build must fetch them from there with normal
-`download_file.py` process. Disadvantage of this approach is that Buck cache
-invalidation must occur to refresh the artifacts after next
-change-compile-install round trip.
-
-To shorten that workflow and take the installation of the artifacts to the
-local Maven repository and fetching it again from there out of the picture,
-`local_jar()` method is used instead of `maven_jar()`:
-
-[source,python]
-----
- local_jar(
-   name = 'jgit',
-   jar = '/home/<user>/projects/jgit/org.eclipse.jgit/target/org.eclipse.jgit-3.3.0-SNAPSHOT.jar',
-   src = '/home/<user>/projects/jgit/org.eclipse.jgit/target/org.eclipse.jgit-3.3.0-SNAPSHOT-sources.jar',
-   deps = [':ewah']
- )
-----
-
-This creates a symlink to the Buck targets direct against artifacts in
-another project's Maven target directory:
-
-----
-  buck-out/gen/lib/jgit/jgit.jar ->
-  /home/<user>/projects/jgit/org.eclipse.jgit/target/org.eclipse.jgit-3.3.0-SNAPSHOT.jar
-----
-
-After `buck clean` and `buck build lib/jgit:jgit` the symbolic link that was
-created the first time is lost due to Buck's caching mechanism. This means that
-when a new version of the local artifact is deployed (by running `mvn package`
-in the JGit project in the example above), Buck is not aware of it, because it
-still has a stale version of it in its cache.
-
-To solve this problem and re-create the symbolic link, you don't need to wipe out
-the entire Buck cache. Just rebuilding the target with the `--no-cache` option
-does the job:
-
-----
-  buck clean
-  buck build --no-cache lib/jgit:jgit
-----
-
 == Building against artifacts from custom Maven repositories
 
 To build against custom Maven repositories, two modes of operations are
@@ -489,7 +434,7 @@
 === Cleaning The Buck Cache
 
 The cache for the Gerrit Code Review project is located in
-`~/.gerritcodereview/buck-cache/cache`.
+`~/.gerritcodereview/buck-cache/locally-built-artifacts`.
 
 The Buck cache should never need to be manually deleted. If you find yourself
 deleting the Buck cache regularly, then it is likely that there is something
@@ -498,11 +443,12 @@
 If you really do need to clean the cache manually, then:
 
 ----
- rm -rf ~/.gerritcodereview/buck-cache/cache
+ rm -rf ~/.gerritcodereview/buck-cache/locally-built-artifacts
 ----
 
-Note that the root `buck-cache` folder should not be deleted as this is where
-downloaded artifacts are stored.
+Note that the root `buck-cache` folder should not be deleted as it also contains
+the `downloaded-artifacts` directory, which holds the artifacts that got
+downloaded (not built locally).
 
 [[buck-daemon]]
 === Using Buck daemon
@@ -593,29 +539,41 @@
 needs to be repeated, the unit test cache for that test must be removed first:
 
 ----
-  rm -rf buck-out/bin/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/.AddRemoveGroupMembersIT/
+  rm -rf buck-out/bin/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/.rest-account/
 ----
 
 After clearing the cache, the test can be run again:
 
 ----
-  buck test //gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group:AddRemoveGroupMembersIT
-  TESTING //gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group:AddRemoveGroupMembersIT
-  PASS  14,9s  8 Passed   0 Failed   com.google.gerrit.acceptance.rest.group.AddRemoveGroupMembersIT
+  buck test //gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account:rest-account
+  [-] TESTING...FINISHED 12,3s (12 PASS/0 FAIL)
+  RESULTS FOR //gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account:rest-account
+  PASS     970ms  2 Passed   0 Skipped   0 Failed   com.google.gerrit.acceptance.rest.account.CapabilitiesIT
+  PASS     999ms  1 Passed   0 Skipped   0 Failed   com.google.gerrit.acceptance.rest.account.EditPreferencesIT
+  PASS      1,2s  1 Passed   0 Skipped   0 Failed   com.google.gerrit.acceptance.rest.account.GetAccountDetailIT
+  PASS     951ms  2 Passed   0 Skipped   0 Failed   com.google.gerrit.acceptance.rest.account.GetAccountIT
+  PASS      6,4s  2 Passed   0 Skipped   0 Failed   com.google.gerrit.acceptance.rest.account.GetDiffPreferencesIT
+  PASS      1,2s  4 Passed   0 Skipped   0 Failed   com.google.gerrit.acceptance.rest.account.PutUsernameIT
   TESTS PASSED
 ----
 
 An alternative approach is to use Buck's `--filters` (`-f`) option:
 
 ----
-  buck test -f 'com.google.gerrit.acceptance.rest.change.SubmitByMergeAlwaysIT'
-  TESTING SELECTED TESTS
-  PASS  14,5s  6 Passed   0 Failed   com.google.gerrit.acceptance.rest.change.SubmitByMergeAlwaysIT
+  buck test -f 'com.google.gerrit.acceptance.rest.account.CapabilitiesIT'
+  Using buckd.
+  [-] PROCESSING BUCK FILES...FINISHED 1,0s [100%]
+  [-] BUILDING...FINISHED 2,8s [100%] (334/701 JOBS, 110 UPDATED, 5,1% CACHE MISS)
+  [-] TESTING...FINISHED 9,2s (6 PASS/0 FAIL)
+  RESULTS FOR SELECTED TESTS
+  PASS      8,0s  2 Passed   0 Skipped   0 Failed   com.google.gerrit.acceptance.rest.account.CapabilitiesIT
+  PASS    <100ms  4 Passed   0 Skipped   0 Failed   //tools:util_test
   TESTS PASSED
 ----
 
 When this option is used, the cache is disabled per design and doesn't need to
-be explicitly deleted.
+be explicitly deleted. Note, that this is a known issue, that python tests are
+always executed.
 
 Note that when this option is used, the whole unit test cache is dropped, so
 repeating the
@@ -633,6 +591,64 @@
 buck test --no-results-cache
 ----
 
+== Upgrading Buck
+
+The following tests should be executed, when Buck version is upgraded:
+
+* buck build release
+* buck build api_install
+* buck test
+* buck build gerrit, change some sources in gerrit-server project,
+  repeat buck build gerrit and verify that gerrit.war was updated
+* install and verify new gerrit site
+* upgrade and verify existing gerrit site
+* reindex existing gerrit site
+* verify that tools/eclipse/project.py produces sane Eclipse project
+* verify that tools/eclipse/project.py --src generates sources as well
+* verify that unit test execution from Eclipse works
+* verify that daemon started from Eclipse works
+* verify that GWT SDM debug session started from Eclipse works
+
+== Known issues and bugs
+
+=== Symbolic links and `watchman`
+
+`Buck` with activated `Watchman` has currently a
+[known bug](https://github.com/facebook/buck/issues/341) related to
+symbolic links. The symbolic links are used very often with external
+plugins, that are linked per symbolic link to the plugins directory.
+With this use case Buck is failing to rebuild the plugin artefact
+after it was built. All attempts to convince Buck to rebuild will fail.
+The only known way to recover is to weep out `buck-out` directory. The
+better workaround is to avoid using Watchman in this specific use case.
+Watchman can either be de-installed or disabled. See
+link:#buck-daemon[Using Buck daemon] section above how to temporarily
+disable `buckd`.
+
+=== Re-triggering rule execution
+
+There is no way to re-trigger custom rules with side effects, like
+`api_{deploy|install}`. This is a `genrule()` that depends on Java sources
+and is deploying the Plugin API through custom Python script to the local or
+remote Maven repositories. When for some reasons the deployment was undone,
+there is no supported way to re-trigger the execution of `api_{deploy|install}`
+targets. That's because `--no-cache` option will ignore the `Buck` cache, but
+there is no way to ignore `buck-out` directory. To overcome this Buck's design
+limitation new `tools/maven/api.py` script was added, that always re-triggers
+installation or deployment of Plugin API to local or Central Maven repository.
+
+```
+  tools/maven/api.py {deploy|install}
+```
+
+Dry run mode is also supported:
+
+```
+  tools/maven/api.py -n {deploy|install}
+```
+
+With this script the deployment would re-trigger on every invocation.
+
 == Troubleshooting Buck
 
 In some cases problems with Buck itself need to be investigated. See for example
diff --git a/Documentation/dev-contributing.txt b/Documentation/dev-contributing.txt
index 72d7ddf..2d96b84 100644
--- a/Documentation/dev-contributing.txt
+++ b/Documentation/dev-contributing.txt
@@ -78,7 +78,8 @@
   * Followed by one or more explanatory paragraphs
   * Use the present tense (fix instead of fixed)
   * Use the past tense when describing the status before this commit
-  * Include a `Bug: Issue <#>` line if fixing a Gerrit issue
+  * Include a `Bug: Issue <#>` line if fixing a Gerrit issue, or a
+    `Feature: Issue <#>` line if implementing a feature request.
   * Include a `Change-Id` line
 
 === Setting up Vim for Git commit message
@@ -334,6 +335,8 @@
 plugins that have a dependency on GWT.
 * Update the GWT version in the archetype metadata in the
 `gerrit-plugin-gwt-archetype`.
+* Update the version of `gwt-maven-plugin` in the example pom.xml file in
+link:dev-plugins.html[dev-plugins].
 * Update to the same GWT version in the `gwtjsonrpc` project, and release a
 new version.
 
diff --git a/Documentation/dev-design.txt b/Documentation/dev-design.txt
index 905b5f1..bdd2a68 100644
--- a/Documentation/dev-design.txt
+++ b/Documentation/dev-design.txt
@@ -66,7 +66,7 @@
 on a J2EE servlet container and an SQL database.
 
 * link:http://video.google.com/videoplay?docid=-8502904076440714866[Mondrian Code Review On The Web]
-* link:http://code.google.com/p/rietveld/[Rietveld - Code Review for Subversion]
+* link:https://github.com/rietveld-codereview/rietveld[Rietveld - Code Review for Subversion]
 * link:http://eagain.net/gitweb/?p=gitosis.git;a=blob;f=README.rst;hb=HEAD[Gitosis README]
 * link:http://source.android.com/[Android Open Source Project]
 
@@ -166,7 +166,7 @@
 requires that the OpenID provider selected by a user must be
 online and operating in order to authenticate that user.
 
-* link:http://code.google.com/webtoolkit/[Google Web Toolkit (GWT)]
+* link:http://www.gwtproject.org/[Google Web Toolkit (GWT)]
 * link:http://www.kernel.org/pub/software/scm/git/docs/gitrepository-layout.html[Git Repository Format]
 * link:http://www.postgresql.org/about/[About PostgreSQL]
 * link:http://openid.net/developers/specs/[OpenID Specifications]
@@ -183,9 +183,9 @@
 
 Gerrit is developed as a self-hosting open source project:
 
-* link:http://code.google.com/p/gerrit/[Project Homepage]
-* link:http://code.google.com/p/gerrit/downloads/list[Release Versions]
-* link:http://code.google.com/p/gerrit/source/checkout[Source]
+* link:https://www.gerritcodereview.com/[Project Homepage]
+* link:https://www.gerritcodereview.com/download/index.html[Release Versions]
+* link:https://gerrit.googlesource.com/gerrit[Source]
 * link:https://bugs.chromium.org/p/gerrit/issues/list[Issue Tracking]
 * link:https://review.source.android.com/[Change Review]
 
diff --git a/Documentation/dev-eclipse.txt b/Documentation/dev-eclipse.txt
index b3525a1..b8d01e8 100644
--- a/Documentation/dev-eclipse.txt
+++ b/Documentation/dev-eclipse.txt
@@ -7,6 +7,35 @@
 runtime debugging environment.
 
 
+[[setup]]
+== Project Setup
+
+In your Eclipse installation's `eclipse.ini` file, add the following line in
+the `vmargs` section:
+
+----
+  -DmaxCompiledUnitsAtOnce=10000
+----
+
+Without this setting, annotation processing does not work reliably and the
+build is likely to fail with errors like:
+
+----
+  Could not write generated class ... javax.annotation.processing.FilerException: Source file already created
+----
+
+In Eclipse, choose 'Import existing project' and select the `gerrit` project
+from the current working directory.
+
+Expand the `gerrit` project, right-click on the `buck-out` folder, select
+'Properties', and then under 'Attributes' check 'Derived'.
+
+Note that if you make any changes in the project configuration
+that get saved to the `.project` file, for example adding Resource
+Filters on a folder, they will be overwritten the next time you run
+`tools/eclipse/project.py`.
+
+
 [[Formatting]]
 == Code Formatter Settings
 
@@ -79,6 +108,26 @@
 recompiling.
 * To reflect your changes in the debug session, click `Dev Mode On` then `Compile`.
 
+
+=== Running GWT Debug Mode for Gerrit plugins
+
+A Gerrit plugin can expose GWT module and its implementation can be inspected
+in the SDM debug session.
+
+`codeserver` needs two additional inputs to expose the plugin module in the SDM
+debug session: the module name and the source folder location. For example the
+module name and source folder of `cookbook-plugin` should be added in the local
+copy of the `gerrit_gwt_debug` configuration:
+
+----
+  com.googlesource.gerrit.plugins.cookbook.HelloForm \
+  -src ${resource_loc:/gerrit}/plugins/cookbook-plugin/src/main/java \
+  -- --console-log [...]
+----
+
+After doing that, both the Gerrit core and plugin GWT modules can be activated
+during SDM (debug session)[http://imgur.com/HFXZ5No].
+
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index 828ea32..188ce14 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -36,7 +36,7 @@
 ----
 mvn archetype:generate -DarchetypeGroupId=com.google.gerrit \
     -DarchetypeArtifactId=gerrit-plugin-archetype \
-    -DarchetypeVersion=2.11.5 \
+    -DarchetypeVersion=2.12.9 \
     -DgroupId=com.googlesource.gerrit.plugins.testplugin \
     -DartifactId=testplugin
 ----
@@ -283,6 +283,7 @@
     private final ConsoleUI ui;
     private final AllProjectsConfig allProjectsConfig;
 
+    @Inject
     public MyInitStep(@PluginName String pluginName, ConsoleUI ui,
         AllProjectsConfig allProjectsConfig) {
       this.pluginName = pluginName;
@@ -404,6 +405,10 @@
 +
 Publication of usage data
 
+* `com.google.gerrit.extensions.events.GarbageCollectorListener`:
++
+Garbage collection ran on a project
+
 [[stream-events]]
 == Sending Events to the Events Stream
 
@@ -762,6 +767,17 @@
 can be notified when this configuration parameter is updated on a
 project.
 
+[[configuring-groups]]
+=== Referencing groups in `project.config`
+
+Plugins can refer to groups so that when they are renamed, the project
+config will also be updated in this section. The proper format to use is
+the string representation of a GroupReference, as shown below.
+
+----
+Group[group_name / group_uuid]
+----
+
 [[project-specific-configuration]]
 == Project Specific Configuration in own config file
 
@@ -958,6 +974,108 @@
 [[ui_extension]]
 == UI Extension
 
+[[panels]]
+=== Panels
+
+GWT plugins can contribute panels to Gerrit screens.
+
+Gerrit screens define extension points where plugins can add GWT
+panels with custom controls:
+
+* Change Screen:
+** `GerritUiExtensionPoint.CHANGE_SCREEN_HEADER`:
++
+Panel will be shown in the header bar to the right of the change
+status.
+
+** `GerritUiExtensionPoint.CHANGE_SCREEN_HEADER_RIGHT_OF_BUTTONS`:
++
+Panel will be shown in the header bar on the right side of the buttons.
+
+** `GerritUiExtensionPoint.CHANGE_SCREEN_HEADER_RIGHT_OF_POP_DOWNS`:
++
+Panel will be shown in the header bar on the right side of the pop down
+buttons.
+
+** `GerritUiExtensionPoint.CHANGE_SCREEN_BELOW_CHANGE_INFO_BLOCK`:
++
+Panel will be shown below the change info block.
+
+** The following parameters are provided:
+*** `GerritUiExtensionPoint.Key.CHANGE_INFO`:
++
+The link:rest-api-changes.html#change-info[ChangeInfo] entity for the
+current change.
+
+* Project Info Screen:
+** `GerritUiExtensionPoint.PROJECT_INFO_SCREEN_TOP`:
++
+Panel will be shown at the top of the screen.
+
+** `GerritUiExtensionPoint.PROJECT_INFO_SCREEN_BOTTOM`:
++
+Panel will be shown at the bottom of the screen.
+
+** The following parameters are provided:
+*** `GerritUiExtensionPoint.Key.PROJECT_NAME`:
++
+The name of the project.
+
+* User Password Screen:
+** `GerritUiExtensionPoint.PASSWORD_SCREEN_BOTTOM`:
++
+Panel will be shown at the bottom of the screen.
+
+** The following parameters are provided:
+*** `GerritUiExtensionPoint.Key.ACCOUNT_INFO`:
++
+The link:rest-api-accounts.html#account-info[AccountInfo] entity for
+the current user.
+
+* User Preferences Screen:
+** `GerritUiExtensionPoint.PREFERENCES_SCREEN_BOTTOM`:
++
+Panel will be shown at the bottom of the screen.
+
+** The following parameters are provided:
+*** `GerritUiExtensionPoint.Key.ACCOUNT_INFO`:
++
+The link:rest-api-accounts.html#account-info[AccountInfo] entity for
+the current user.
+
+* User Profile Screen:
+** `GerritUiExtensionPoint.PROFILE_SCREEN_BOTTOM`:
++
+Panel will be shown at the bottom of the screen below the grid with the
+profile data.
+
+** The following parameters are provided:
+*** `GerritUiExtensionPoint.Key.ACCOUNT_INFO`:
++
+The link:rest-api-accounts.html#account-info[AccountInfo] entity for
+the current user.
+
+Example panel:
+[source,java]
+----
+public class MyPlugin extends PluginEntryPoint {
+  @Override
+  public void onPluginLoad() {
+    Plugin.get().panel(GerritUiExtensionPoint.CHANGE_SCREEN_BELOW_CHANGE_INFO_BLOCK,
+        new Panel.EntryPoint() {
+          @Override
+          public void onLoad(Panel panel) {
+            panel.setWidget(new InlineLabel("My Panel for change "
+                + panel.getInt(GerritUiExtensionPoint.Key.CHANGE_ID, -1));
+          }
+        });
+  }
+}
+----
+
+[[actions]]
+=== Actions
+
 Plugins can contribute UI actions on core Gerrit pages. This is useful
 for workflow customization or exposing plugin functionality through the
 UI in addition to SSH commands and the REST API.
@@ -1519,7 +1637,7 @@
 <plugin>
   <groupId>org.codehaus.mojo</groupId>
   <artifactId>gwt-maven-plugin</artifactId>
-  <version>2.5.1</version>
+  <version>2.7.0</version>
   <configuration>
     <module>com.googlesource.gerrit.plugins.myplugin.HelloPlugin</module>
     <disableClassMetadata>true</disableClassMetadata>
@@ -1657,6 +1775,40 @@
 }
 ----
 
+[[user-settings-screen]]
+== Add User Settings Screen
+
+A link:#gwt_ui_extension[GWT plugin] can implement a user settings
+screen that is integrated into the Gerrit user settings menu.
+
+Example settings screen:
+[source,java]
+----
+public class MyPlugin extends PluginEntryPoint {
+  @Override
+  public void onPluginLoad() {
+    Plugin.get().settingsScreen("my-preferences", "My Preferences",
+        new Screen.EntryPoint() {
+          @Override
+          public void onLoad(Screen screen) {
+            screen.setPageTitle("Settings");
+            screen.add(new InlineLabel("My Preferences"));
+            screen.show();
+          }
+    });
+  }
+}
+----
+
+By defining an link:config-gerrit.html#urlAlias[urlAlias] Gerrit
+administrators can map plugin screens into the Gerrit URL namespace or
+even replace Gerrit screens by plugin screens.
+
+Plugins may also programatically add URL aliases in the preferences of
+of a user. This way certain screens can be replaced for certain users.
+E.g. the plugin may offer a user preferences setting for choosing a
+screen that then sets/unsets a URL alias for the user.
+
 [[settings-screen]]
 == Plugin Settings Screen
 
@@ -1717,17 +1869,18 @@
 [[data-directory]]
 == Data Directory
 
-Plugins can request a data directory with a `@PluginData` File
-dependency. A data directory will be created automatically by the
-server in `$site_path/data/$plugin_name` and passed to the plugin.
+Plugins can request a data directory with a `@PluginData` Path (or File,
+deprecated) dependency. A data directory will be created automatically
+by the server in `$site_path/data/$plugin_name` and passed to the
+plugin.
 
 Plugins can use this to store any data they want.
 
 [source,java]
 ----
 @Inject
-MyType(@PluginData java.io.File myDir) {
-  new FileInputStream(new File(myDir, "my.config"));
+MyType(@PluginData java.nio.file.Path myDir) {
+  this.in = Files.newInputStream(myDir.resolve("my.config"));
 }
 ----
 
@@ -1762,15 +1915,29 @@
 [[download-commands]]
 == Download Commands
 
-Gerrit offers commands for downloading changes using different
-download schemes (e.g. for downloading via different network
-protocols). Plugins can contribute download schemes and download
-commands by implementing
-`com.google.gerrit.extensions.config.DownloadScheme` and
-`com.google.gerrit.extensions.config.DownloadCommand`.
+Gerrit offers commands for downloading changes and cloning projects
+using different download schemes (e.g. for downloading via different
+network protocols). Plugins can contribute download schemes, download
+commands and clone commands by implementing
+`com.google.gerrit.extensions.config.DownloadScheme`,
+`com.google.gerrit.extensions.config.DownloadCommand` and
+`com.google.gerrit.extensions.config.CloneCommand`.
 
-The download schemes and download commands which are used most often
-are provided by the Gerrit core plugin `download-commands`.
+The download schemes, download commands and clone commands which are
+used most often are provided by the Gerrit core plugin
+`download-commands`.
+
+[[included-in]]
+== Included In
+
+For merged changes the link:user-review-ui.html#included-in[Included In]
+drop-down panel shows the branches and tags in which the change is
+included.
+
+Plugins can add additional systems in which the change can be included
+by implementing `com.google.gerrit.extensions.config.ExternalIncludedIn`,
+e.g. a plugin can provide a list of servers on which the change was
+deployed.
 
 [[links-to-external-tools]]
 == Links To External Tools
@@ -1831,8 +1998,8 @@
 prefix is configurable by setting the `Gerrit-HttpDocumentationPrefix`
 attribute.
 
-Documentation may be written in
-link:http://daringfireball.net/projects/markdown/[Markdown] style
+Documentation may be written in the Markdown flavor
+link:https://github.com/sirthias/pegdown[pegdown]
 if the file name ends with `.md`. Gerrit will automatically convert
 Markdown to HTML if accessed with extension `.html`.
 
@@ -1933,6 +2100,40 @@
 Disabled plugins can be re-enabled using the
 link:cmd-plugin-enable.html[plugin enable] command.
 
+== Known issues and bugs
+
+=== Error handling in UI when using the REST API
+
+When a plugin invokes a REST endpoint in the UI, it provides an
+`AsyncCallback` to handle the result. At the moment the
+`onFailure(Throwable)` of the callback is never invoked, even if there
+is an error. Errors are always handled by the Gerrit core UI which
+shows the error dialog. This means currently plugins cannot do any
+error handling and e.g. ignore expected errors.
+
+In the following example the REST endpoint would return '404 Not Found'
+if there is no HTTP password and the Gerrit core UI would display an
+error dialog for this. However having no HTTP password is not an error
+and the plugin may like to handle this case.
+
+[source,java]
+----
+new RestApi("accounts").id("self").view("password.http")
+    .get(new AsyncCallback<NativeString>() {
+
+  @Override
+  public void onSuccess(NativeString httpPassword) {
+    // TODO
+  }
+
+  @Override
+  public void onFailure(Throwable caught) {
+    // never invoked
+  }
+});
+----
+
+
 == SEE ALSO
 
 * link:js-api.html[JavaScript API]
diff --git a/Documentation/dev-readme.txt b/Documentation/dev-readme.txt
index f59f2fc..4a5c0bd 100644
--- a/Documentation/dev-readme.txt
+++ b/Documentation/dev-readme.txt
@@ -56,6 +56,17 @@
 Eclipse integration and then open it as Eclipse project in IDEA.
 You need the Eclipse plugin activated in IntelliJ IDEA.
 
+Once you start compiling using both buck and your Gerrit project in
+IDEA, you will likely need to mark the below directories as generated
+sources roots. You can do so using the IDEA "Project" view. In the
+context menu of each one of these, use "Mark Directory As" to mark
+them as "Generated Sources Root":
+
+----
+  __auto_value_tests_gen__
+  __httpd_gen__
+  __server_gen__
+----
 
 == Mac OS X
 
@@ -76,7 +87,7 @@
 testing site for development use:
 
 ----
-  java -jar buck-out/gen/gerrit.war init -d ../gerrit_testsite
+  java -jar buck-out/gen/gerrit/gerrit.war init -d ../gerrit_testsite
 ----
 
 Accept defaults by pressing Enter until 'init' completes, or add
@@ -119,7 +130,7 @@
 copying to the test site:
 
 ----
-  java -jar buck-out/gen/gerrit.war daemon -d ../gerrit_testsite
+  java -jar buck-out/gen/gerrit/gerrit.war daemon -d ../gerrit_testsite
 ----
 
 === Running the Daemon with Gerrit Inspector
@@ -138,7 +149,7 @@
 command used to launch the daemon:
 
 ----
-  java -jar buck-out/gen/gerrit.war daemon -d ../gerrit_testsite -s
+  java -jar buck-out/gen/gerrit/gerrit.war daemon -d ../gerrit_testsite -s
 ----
 
 Gerrit Inspector examines Java libraries first, then loads
@@ -165,7 +176,7 @@
 command line.  If the daemon is not currently running:
 
 ----
-  java -jar buck-out/gen/gerrit.war gsql -d ../gerrit_testsite
+  java -jar buck-out/gen/gerrit/gerrit.war gsql -d ../gerrit_testsite
 ----
 
 Or, if it is running and the database is in use, connect over SSH
diff --git a/Documentation/dev-release-deploy-config.txt b/Documentation/dev-release-deploy-config.txt
index f3397a4..b2c5358 100644
--- a/Documentation/dev-release-deploy-config.txt
+++ b/Documentation/dev-release-deploy-config.txt
@@ -10,12 +10,11 @@
 To be able to publish artifacts to Maven Central some preparations must
 be done:
 
-* Create a Sonatype account as described in the
-link:https://docs.sonatype.org/display/Repository/Sonatype+OSS+Maven+Repository+Usage+Guide#SonatypeOSSMavenRepositoryUsageGuide-2.Signup[
-Sonatype OSS Maven Repository Usage Guide].
+* Create an account on
+link:https://issues.sonatype.org/secure/Signup!default.jspa[Sonatype's Jira].
 +
 Sonatype is the company that runs Maven Central and you need a Sonatype
-account for uploading artifacts to Maven Central.
+account to be able to upload artifacts to Maven Central.
 
 * Configure your Sonatype user and password in `~/.m2/settings.xml`:
 +
@@ -75,7 +74,7 @@
 
 Gerrit Subproject Artifacts are stored on
 link:https://developers.google.com/storage/[Google Cloud Storage].
-Via the link:https://code.google.com/apis/console/?noredirect[API Console] the
+Via the link:https://console.developers.google.com/project/164060093628[Developers Console] the
 Gerrit maintainers have access to the `Gerrit Code Review` project.
 This projects host several buckets for storing Gerrit artifacts:
 
@@ -90,13 +89,14 @@
 
 To upload artifacts to a bucket the user must authenticate with a
 username and password. The username and password need to be retrieved
-from the link:https://code.google.com/apis/console/?noredirect[API Console]:
+from the link:https://console.developers.google.com/project/164060093628[
+Google Developers Console]:
 
-* Go to the `Gerrit Code Review` project
-* In the menu on the left select `Google Cloud Storage` >
-`Interoperable Access`
-* Use the `Access Key` as username
-* Click under `Secret` on the `Show` button to find the password
+* In the menu on the left select `Storage` -> `Cloud Storage` >
+> `Storage access`
+* Select the `Interoperability` tab
+* If no keys are listed under `Interoperable storage access keys`, select "Create a new key"
+* Use the `Access Key` as username, and `Secret` as the password
 
 To make the username and password known to Maven, they must be
 configured in the `~/.m2/settings.xml` file.
diff --git a/Documentation/dev-release.txt b/Documentation/dev-release.txt
index 3d3104c..3157214 100644
--- a/Documentation/dev-release.txt
+++ b/Documentation/dev-release.txt
@@ -84,8 +84,8 @@
 . link:#subproject[Release Subprojects]
 . link:#build-gerrit[Build the Gerrit Release]
 . link:#publish-gerrit[Publish the Gerrit Release]
-.. link:#extension-and-plugin-api[Publish the Extension and Plugin API Jars]
-.. link:#publish-gerrit-war[Publish the Gerrit WAR (with Core Plugins)]
+.. link:#publish-to-maven-central[Publish the Gerrit artifacts to Maven Central]
+.. link:#publish-to-google-storage[Publish the Gerrit WAR to Google Storage]
 .. link:#push-stable[Push the Stable Branch]
 .. link:#push-tag[Push the Release Tag]
 .. link:#upload-documentation[Upload the Documentation]
@@ -157,7 +157,8 @@
 * Build the Gerrit WAR and API JARs
 +
 ----
-  buck build release
+  buck clean
+  buck build --no-cache release
   buck build api_install
 ----
 
@@ -167,15 +168,6 @@
 [[publish-gerrit]]
 === Publish the Gerrit Release
 
-[[publish-gerrit-war]]
-==== Publish the Gerrit WAR (with Core Plugins)
-
-* Upload the WAR to the Google Cloud Storage
-
-** go to https://console.developers.google.com/project/164060093628/storage/gerrit-releases/
-** make sure you are signed in with your Gmail account
-** manually upload the Gerrit WAR file by using the `Upload` button
-
 [[publish-to-maven-central]]
 ==== Publish the Gerrit artifacts to Maven Central
 
@@ -230,10 +222,6 @@
 link:https://oss.sonatype.org/[Sonatype Nexus Server].
 
 * Verify the staging repository
-+
-How to do this is described in the
-link:https://docs.sonatype.org/display/Repository/Sonatype+OSS+Maven+Repository+Usage+Guide#SonatypeOSSMavenRepositoryUsageGuide-8.a.1.ClosingaStagingRepository[
-Sonatype OSS Maven Repository Usage Guide].
 
 ** Go to the link:https://oss.sonatype.org/[Sonatype Nexus Server] and
 sign in with your Sonatype credentials.
@@ -305,6 +293,13 @@
 ** Select `com.google.gerrit` as `Project`.
 
 
+[[publish-to-google-storage]]
+==== Publish the Gerrit WAR to the Google Cloud Storage
+
+* go to https://console.developers.google.com/project/164060093628/storage/gerrit-releases/
+* make sure you are signed in with your Gmail account
+* manually upload the Gerrit WAR file by using the `Upload` button
+
 [[push-stable]]
 ==== Push the Stable Branch
 
@@ -352,18 +347,13 @@
 
 * Upload the html files manually via web browser to the
 link:https://console.developers.google.com/project/164060093628/storage/gerrit-documentation/[
-gerrit-documentation] storage bucket. The `gerrit-documentation`
-storage bucket is accessible via the
-link:https://cloud.google.com/console[Google Developers Console].
+gerrit-documentation] storage bucket.
 
 [[update-links]]
-==== Update Google Code project links
+==== Update homepage links
 
-* Go to http://code.google.com/p/gerrit/admin
-* Update the documentation link in the `Resources` section of the
-Description text, and in the `Links` section.
-* Add a link to the new release notes in the `News` section of the
-Description text
+Upload a change on the link:https://gerrit-review.googlesource.com/#/admin/projects/homepage[
+homepage project] to change the version numbers to the new version.
 
 [[update-issues]]
 ==== Update the Issues
diff --git a/Documentation/doc.css.in b/Documentation/doc.css.in
index 6be89f6..429e81c 100644
--- a/Documentation/doc.css.in
+++ b/Documentation/doc.css.in
@@ -17,6 +17,10 @@
   border-bottom: 2px solid silver;
 }
 
+h1 {
+  margin-top: 1.5em;
+}
+
 p {
   margin: 0.5em 0 0.5em 0;
 }
diff --git a/Documentation/gen_licenses.py b/Documentation/gen_licenses.py
index af7cd88..db3480b 100755
--- a/Documentation/gen_licenses.py
+++ b/Documentation/gen_licenses.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
 # Copyright (C) 2013 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,6 +17,7 @@
 
 from __future__ import print_function
 
+import argparse
 from collections import defaultdict, deque
 from os import chdir, path
 import re
@@ -24,7 +25,13 @@
 from subprocess import Popen, PIPE
 from sys import stdout, stderr
 
-MAIN = ['//gerrit-pgm:pgm', '//gerrit-gwtui:ui_module']
+parser = argparse.ArgumentParser()
+parser.add_argument('--asciidoc', action='store_true')
+parser.add_argument('--partial', action='store_true')
+parser.add_argument('--classpath', action='append')
+parser.add_argument('targets', nargs='+')
+args = parser.parse_args()
+
 KNOWN_PROVIDED_DEPS = [
   '//lib/bouncycastle:bcpg',
   '//lib/bouncycastle:bcpkix',
@@ -35,14 +42,25 @@
   graph = defaultdict(list)
   while not path.isfile('.buckconfig'):
     chdir('..')
+  # TODO(davido): use passed in classpath from Buck instead
   p = Popen(
-    ['buck', 'audit', 'classpath', '--dot'] + MAIN,
+    ['buck', 'audit', 'classpath', '--dot'] + args.targets,
     stdout = PIPE)
   for line in p.stdout:
     m = re.search(r'"(//.*?)" -> "(//.*?)";', line)
     if not m:
       continue
     target, dep = m.group(1), m.group(2)
+    if args.partial:
+      if dep == '//lib/codemirror:js_minifier':
+        if target == '//lib/codemirror:js':
+          continue
+        if target.startswith('//lib/codemirror:mode_'):
+          continue
+      if target == '//gerrit-gwtui:ui_module' and \
+         dep == '//gerrit-gwtexpui:CSS':
+        continue
+
     # Dependencies included in provided_deps set are contained in audit
     # classpath and must be sorted out. That's safe thing to do because
     # they are not included in the final artifact.
@@ -60,7 +78,7 @@
 graph = parse_graph()
 licenses = defaultdict(set)
 
-queue = deque(MAIN)
+queue = deque(args.targets)
 while queue:
   target = queue.popleft()
   for dep in graph[target]:
@@ -70,7 +88,8 @@
   queue.extend(graph[target])
 used = sorted(licenses.keys())
 
-print("""\
+if args.asciidoc:
+  print("""\
 Gerrit Code Review - Licenses
 =============================
 
@@ -122,26 +141,33 @@
 for n in used:
   libs = sorted(licenses[n])
   name = n[len('//lib:LICENSE-'):]
-  print()
-  print('[[%s]]' % name.replace('.', '_'))
-  print(name)
-  print('~' * len(name))
-  print()
+  if args.asciidoc:
+    print()
+    print('[[%s]]' % name.replace('.', '_'))
+    print(name)
+    print('~' * len(name))
+    print()
+  else:
+    print()
+    print(name)
+    print('--')
   for d in libs:
     if d.startswith('//lib:') or d.startswith('//lib/'):
       p = d[len('//lib:'):]
     else:
       p = d[d.index(':')+1:].lower()
     print('* ' + p)
-  print()
-  print('[[license]]')
-  print('[verse]')
-  print('--')
+  if args.asciidoc:
+    print()
+    print('[[license]]')
+    print('[verse]')
+    print('--')
   with open(n[2:].replace(':', '/')) as fd:
     copyfileobj(fd, stdout)
   print('--')
 
-print("""
+if args.asciidoc:
+  print("""
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/index.txt b/Documentation/index.txt
index 9b477ae..63e3be6 100644
--- a/Documentation/index.txt
+++ b/Documentation/index.txt
@@ -7,23 +7,21 @@
 .. link:intro-project-owner.html[Project Owner Guide]
 .. link:http://source.android.com/submit-patches/workflow[Default Android Workflow] (external)
 . Web
-.. Registering a new Gerrit account
 .. link:user-review-ui.html[Reviewing Changes]
 .. link:user-search.html[Searching Changes]
 .. link:user-inline-edit.html[Manipulating Changes in Browser]
 .. link:user-notify.html[Subscribing to Email Notifications]
 . SSH
-.. SSH connection details
+.. link:user-upload.html#ssh[SSH connection details]
 .. link:cmd-index.html[Command Line Tools]
 . Git
-.. Git connection details
 .. Commands, scenarios
 ... link:user-upload.html[Uploading Changes]
 ... link:error-messages.html[Error Messages]
 .. Changes
 ... link:user-changeid.html[Change-Id Lines]
 ... link:user-signedoffby.html[Signed-off-by Lines]
-.. Patch sets
+... link:user-change-cleanup.html[Change Cleanup]
 
 == Project Management
 . link:project-configuration.html[Project Configuration]
@@ -31,13 +29,13 @@
 .. link:config-labels.html[Review Labels]
 .. link:config-project-config.html[Access Controls Configuration Format]
 . Multi-project management
-.. Submodules
-.. Repo
+.. link:user-submodules.html[Submodules]
+.. link:https://source.android.com/source/using-repo.html[Repo] (external)
 . Prolog rules
 .. link:prolog-cookbook.html[Prolog Cookbook]
 .. link:prolog-change-facts.html[Prolog Facts for Gerrit Changes]
 . link:user-submodules.html[Subscribing to Git Submodules]
-. Project sunset
+. link:intro-project-owner.html#project-deletion[Project deletion]
 
 == Customization and Integration
 . link:user-dashboards.html[Dashboards]
@@ -52,15 +50,9 @@
 == Server Administration
 . link:install.html[Installation Guide]
 . link:config-gerrit.html[System Settings]
-. Backup
-. Performance tuning
-.. link:cmd-index.html[Command Line Tools]
-.. Reading show-caches efficiently
-.. How to read stats from the JVM
-. High availability
-. Replication
+. link:cmd-index.html[Command Line Tools]
+. link:config-plugins.html#replication[Replication]
 . link:config-plugins.html[Plugins]
-. link:config-contact.html[User Contact Information]
 . link:config-reverseproxy.html[Reverse Proxy]
 . link:config-auto-site-initialization.html[Automatic Site Initialization on Startup]
 . link:pgm-index.html[Server Side Administrative Tools]
@@ -76,7 +68,6 @@
 .. link:dev-build-plugins.html[Building Gerrit plugins]
 .. link:js-api.html[JavaScript Plugin API]
 .. link:config-validation.html[Validation Interfaces]
-. Documentation formatting guide for contributions
 . link:dev-design.html[System Design]
 . link:i18n-readme.html[i18n Support]
 
@@ -86,11 +77,11 @@
 
 == Resources
 * link:licenses.html[Licenses and Notices]
-* link:http://code.google.com/p/gerrit/[Homepage]
-* link:http://gerrit-releases.storage.googleapis.com/index.html[Downloads]
+* link:https://www.gerritcodereview.com/[Homepage]
+* link:https://www.gerritcodereview.com/download/index.html[Downloads]
 * link:https://bugs.chromium.org/p/gerrit/issues/list[Issue Tracking]
-* link:http://code.google.com/p/gerrit/source/checkout[Source Code]
-* link:http://code.google.com/p/gerrit/wiki/Background[A History of Gerrit Code Review]
+* link:https://gerrit.googlesource.com/gerrit[Source Code]
+* link:https://www.gerritcodereview.com/about.md[A History of Gerrit Code Review]
 
 SEARCHBOX
 ---------
diff --git a/Documentation/install-quick.txt b/Documentation/install-quick.txt
index f4c12a9..2623256 100644
--- a/Documentation/install-quick.txt
+++ b/Documentation/install-quick.txt
@@ -58,11 +58,11 @@
 
 You can choose from different versions to download from here:
 
-* http://code.google.com/p/gerrit/downloads/list[A list of releases available]
+* https://www.gerritcodereview.com/download/index.html[A list of releases available]
 
 This tutorial is based on version 2.2.2, and you can download that from this link
 
-* http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.2.2.war[Link to the 2.2.2 war archive]
+* https://www.gerritcodereview.com/download/gerrit-2.2.2.war[Link to the 2.2.2 war archive]
 
 
 [[initialization]]
@@ -121,7 +121,7 @@
 This is done via the SSH port:
 
 ----
-  user@host:~$ ssh -p 29418 user@localhost gerrit create-project --empty-commit --name demo-project
+  user@host:~$ ssh -p 29418 user@localhost gerrit create-project demo-project --empty-commit
   user@host:~$
 ----
 
@@ -134,7 +134,7 @@
 First you have to create the project.  This is done via the SSH port:
 
 ----
-  user@host:~$ ssh -p 29418 user@localhost gerrit create-project --name demo-project
+  user@host:~$ ssh -p 29418 user@localhost gerrit create-project demo-project
   user@host:~$
 ----
 
diff --git a/Documentation/install.txt b/Documentation/install.txt
index df2f0dd..3f7d1c1 100644
--- a/Documentation/install.txt
+++ b/Documentation/install.txt
@@ -45,7 +45,7 @@
 == Download Gerrit
 
 Current and past binary releases of Gerrit can be obtained from
-the link:https://gerrit-releases.storage.googleapis.com/index.html[
+the link:https://www.gerritcodereview.com/download/index.html[
 Gerrit Releases site].
 
 Download any current `*.war` package. The war will be referred to as
diff --git a/Documentation/intro-project-owner.txt b/Documentation/intro-project-owner.txt
index f5d5277..dfffe57 100644
--- a/Documentation/intro-project-owner.txt
+++ b/Documentation/intro-project-owner.txt
@@ -63,7 +63,7 @@
 commit message is provided you can also see from the history why the
 access rights were modified.
 
-If a Git browser such as GitWeb is configured for the Gerrit server you
+If a Git browser such as gitweb is configured for the Gerrit server you
 can find a link to the history of the `project.config` file in the
 Web UI. Otherwise you may inspect the history locally. If you have
 cloned the repository you can do this by executing the following
@@ -238,7 +238,7 @@
 === Submit Type
 
 An important decision for a project is the choice of the submit type
-and the content merge setting (aka `Automatically resolve conflicts`).
+and the content merge setting (see the `Allow content merges` option).
 The link:project-configuration.html#submit_type[submit type] is the method
 Gerrit uses to submit a change to the project. The submit type defines
 what Gerrit should do on submit of a change if the destination branch
diff --git a/Documentation/intro-user.txt b/Documentation/intro-user.txt
index 4677307..b9bdad0 100644
--- a/Documentation/intro-user.txt
+++ b/Documentation/intro-user.txt
@@ -29,6 +29,9 @@
 
 * link:http://eclipse.org/mylyn/[Mylyn Gerrit Connector]: Gerrit
   integration with Mylyn
+* link:https://github.com/uwolfer/gerrit-intellij-plugin[Gerrit
+  IntelliJ Plugin]: Gerrit integration with the
+  link:http://www.jetbrains.com/idea/[IntelliJ Platform]
 * link:https://play.google.com/store/apps/details?id=com.jbirdvegas.mgerrit[
   mGerrit]: Android client for Gerrit
 * link:https://github.com/stackforge/gertty[Gertty]: Console-based
@@ -560,11 +563,23 @@
 auto-merge on push] to benefit from the automatic merge/rebase on
 server side while pushing directly into the repository.
 
+[[user-refs]]
+== User Refs
+
+User configuration data such as link:#preferences[preferences] is
+stored in the `All-Users` project under a per-user ref.  The user's
+ref is based on the user's account id which is an integer.  The user
+refs are sharded by the last two digits (`+nn+`) in the refname,
+leading to refs of the format `+refs/users/nn/accountid+`.
+
 [[preferences]]
 == Preferences
 
 There are several options to control the rendering in the Gerrit web UI.
 Users can configure their preferences under `Settings` > `Preferences`.
+The user's preferences are stored in a `git config` style file named
+`preferences.config` under the link:#user-refs[user's ref] in the
+`All-Users` project.
 
 The following preferences can be configured:
 
diff --git a/Documentation/js-api.txt b/Documentation/js-api.txt
index 43e4336..03ff5a5 100644
--- a/Documentation/js-api.txt
+++ b/Documentation/js-api.txt
@@ -60,11 +60,25 @@
   a string, otherwise the result is a JavaScript object or array,
   as described in the relevant REST API documentation.
 
+[[self_getServerInfo]]
+=== self.getServerInfo()
+Returns the server's link:rest-api-config.html#server-info[ServerInfo]
+data.
+
 [[self_getCurrentUser]]
 === self.getCurrentUser()
 Returns the currently signed in user's AccountInfo data; empty account
 data if no user is currently signed in.
 
+[[Gerrit_getUserPreferences]]
+=== Gerrit.getUserPreferences()
+Returns the preferences of the currently signed in user; the default
+preferences if no user is currently signed in.
+
+[[Gerrit_refreshUserPreferences]]
+=== Gerrit.refreshUserPreferences()
+Refreshes the preferences of the current user.
+
 [[self_getPluginName]]
 === self.getPluginName()
 Returns the name this plugin was installed as by the server
diff --git a/Documentation/json.txt b/Documentation/json.txt
index feef1a1..32fa472 100644
--- a/Documentation/json.txt
+++ b/Documentation/json.txt
@@ -43,9 +43,6 @@
 
   DRAFT;; Change is a draft change that only consists of draft patchsets.
 
-  SUBMITTED;; Change has been submitted and is in the merge queue.
-  It may be waiting for one or more dependencies.
-
   MERGED;; Change has been merged to its branch.
 
   ABANDONED;; Change was abandoned by its owner or administrator.
@@ -156,7 +153,7 @@
 
 newRev:: The new value the ref was updated to.
 
-refName:: Ref name within project.
+refName:: Full ref name within project.
 
 project:: Project path in Gerrit.
 
diff --git a/Documentation/man/.gitignore b/Documentation/man/.gitignore
deleted file mode 100644
index 7993a55..0000000
--- a/Documentation/man/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-gerrit*
diff --git a/Documentation/man/Makefile b/Documentation/man/Makefile
deleted file mode 100644
index 945f215..0000000
--- a/Documentation/man/Makefile
+++ /dev/null
@@ -1,64 +0,0 @@
-# Copyright (C) 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-A2X ?= a2x
-
-all: man
-
-clean:
-	@rm -f gerrit-*
-
-CMD_CORE_SSH_CMD := \
-cmd-ban-commit.txt     \
-cmd-create-account.txt \
-cmd-create-group.txt   \
-cmd-create-project.txt \
-cmd-flush-caches.txt   \
-cmd-gc.txt             \
-cmd-gsql.txt           \
-cmd-ls-groups.txt      \
-cmd-ls-projects.txt    \
-cmd-ls-user-refs.txt   \
-cmd-query.txt          \
-cmd-rename-group.txt   \
-cmd-review.txt         \
-cmd-set-account.txt    \
-cmd-set-members.txt    \
-cmd-set-project-parent.txt \
-cmd-set-project.txt    \
-cmd-set-reviewers.txt  \
-cmd-show-caches.txt    \
-cmd-show-connections.txt   \
-cmd-show-queue.txt     \
-cmd-stream-events.txt  \
-cmd-test-submit-rule.txt   \
-cmd-version.txt
-
-GERRIT_CORE_SSH_CMD := $(patsubst cmd-%,gerrit-%,$(CMD_CORE_SSH_CMD))
-DOC_MAN := $(patsubst %.txt,%.1,$(GERRIT_CORE_SSH_CMD))
-
-man: $(GERRIT_CORE_SSH_CMD) $(DOC_MAN)
-
-$(GERRIT_CORE_SSH_CMD) : gerrit-%.txt : ../cmd-%.txt
-	@cp $< $@
-
-$(DOC_MAN) : %.1 : %.txt
-	@echo "creating man page for $@ ..."
-	@rm -f $@
-	@$(eval TITLE := $(join $(basename $<),\(1\)))
-	@$(eval SEPERATOR := $(shell echo $(TITLE) | sed 's/./=/g'))
-	@sed -i -re '1s/^.*$//$(TITLE)/' $<
-	@sed -i -re '2s/^=.*/$(SEPERATOR)/' $<
-	@sed -i -re '6s/^gerrit\s+(\w)/gerrit-\1/' $<
-	@$(A2X) --doctype manpage --format manpage $<
diff --git a/Documentation/pgm-init.txt b/Documentation/pgm-init.txt
index 39cd70d..6aa3a74 100644
--- a/Documentation/pgm-init.txt
+++ b/Documentation/pgm-init.txt
@@ -11,6 +11,7 @@
 	[--no-auto-start]
 	[--list-plugins]
 	[--install-plugin=<PLUGIN_NAME>]
+        [--dev]
 --
 
 == DESCRIPTION
@@ -51,6 +52,10 @@
 	This option may be supplied more than once to install multiple
 	plugins.
 
+--dev::
+	Install in developer mode. Default configuration settings are
+	chosen to run the Gerrit server as a developer.
+
 == CONTEXT
 This command can only be run on a server which has direct
 connectivity to the metadata database, and local access to the
diff --git a/Documentation/pgm-reindex.txt b/Documentation/pgm-reindex.txt
index e1d8e8b..bf09e0c 100644
--- a/Documentation/pgm-reindex.txt
+++ b/Documentation/pgm-reindex.txt
@@ -24,9 +24,6 @@
 --verbose::
 	Output debug information for each change.
 
---dry-run::
-	Dry run.  Don't write anything to index.
-
 == CONTEXT
 The secondary index must be enabled. See
 link:config-gerrit.html#index.type[index.type].
diff --git a/Documentation/project-configuration.txt b/Documentation/project-configuration.txt
index 1cecabf..6d7c6d0 100644
--- a/Documentation/project-configuration.txt
+++ b/Documentation/project-configuration.txt
@@ -114,7 +114,7 @@
 the same file has also been changed on the other side of the merge.
 
 [[content_merge]]
-If `Automatically resolve conflicts` is enabled, Gerrit will try
+If `Allow content merges` is enabled, Gerrit will try
 to do a content merge when a path conflict occurs.
 
 [[project-state]]
diff --git a/Documentation/prolog-cookbook.txt b/Documentation/prolog-cookbook.txt
index b15c283..b53da4b 100644
--- a/Documentation/prolog-cookbook.txt
+++ b/Documentation/prolog-cookbook.txt
@@ -56,7 +56,7 @@
 examples.
 
 == Prolog in Gerrit
-Gerrit uses its own link:https://code.google.com/p/prolog-cafe/[fork] of the
+Gerrit uses its own link:https://gerrit.googlesource.com/prolog-cafe/[fork] of the
 original link:http://kaminari.istc.kobe-u.ac.jp/PrologCafe/[prolog-cafe]
 project. Gerrit embeds the prolog-cafe library and can interpret Prolog programs
 at runtime.
diff --git a/Documentation/replace_macros.py b/Documentation/replace_macros.py
index 8deee62..fec4a58 100755
--- a/Documentation/replace_macros.py
+++ b/Documentation/replace_macros.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
 # Copyright (C) 2013 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index a510e8a..5459306 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -117,6 +117,36 @@
   }
 ----
 
+[[get-detail]]
+=== Get Account Details
+--
+'GET /accounts/link:#account-id[\{account-id\}]/detail'
+--
+
+Retrieves the details of an account as an link:#account-detail-info[
+AccountDetailInfo] entity.
+
+.Request
+----
+  GET /accounts/self/detail HTTP/1.0
+----
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  {
+    "registered_on": "2015-07-23 07:01:09.296000000",
+    "_account_id": 1000096,
+    "name": "John Doe",
+    "email": "john.doe@example.com",
+    "username": "john"
+  }
+----
+
 [[get-account-name]]
 === Get Account Name
 --
@@ -223,6 +253,30 @@
 
 If the account does not have a username the response is "`404 Not Found`".
 
+[[set-username]]
+=== Set Username
+--
+'PUT /accounts/link:#account-id[\{account-id\}]/username'
+--
+
+The new username must be provided in the request body inside
+a link:#username-input[UsernameInput] entity.
+
+Once set, the username cannot be changed or deleted. If attempted this
+fails with "`405 Method Not Allowed`".
+
+.Request
+----
+  PUT /accounts/self/username HTTP/1.0
+  Content-Type: application/json; charset=UTF-8
+
+  {
+    "username": "jdoe"
+  }
+----
+
+As response the new username is returned.
+
 [[get-active]]
 === Get Active
 --
@@ -633,6 +687,148 @@
   HTTP/1.1 204 No Content
 ----
 
+[[list-gpg-keys]]
+=== List GPG Keys
+--
+'GET /accounts/link:#account-id[\{account-id\}]/gpgkeys'
+--
+
+Returns the GPG keys of an account.
+
+.Request
+----
+  GET /accounts/self/gpgkeys HTTP/1.0
+----
+
+As a response, the GPG keys of the account are returned as a map of
+link:#gpg-key-info[GpgKeyInfo] entities, keyed by ID.
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  {
+    "AFC8A49B": {
+      "fingerprint": "0192 723D 42D1 0C5B 32A6  E1E0 9350 9E4B AFC8 A49B",
+      "user_ids": [
+        "John Doe \u003cjohn.doe@example.com\u003e"
+      ],
+      "key": "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: BCPG v1.52\n\nmQENBFXUpNcBCACv4paCiyKxZ0EcKy8VaWVNkJlNebRBiyw9WxU85wPOq5Gz/3GT\nRQwKqeY0SxVdQT8VNBw2sBe2m6eqcfZ2iKmesSlbXMe15DA7k8Bg4zEpQ0tXNG1L\nhceZDVQ1Xk06T2sgkunaiPsXi82nwN3UWYtDXxX4is5e6xBNL48Jgz4lbqo6+8D5\nvsVYiYMx4AwRkJyt/oA3IZAtSlY8Yd445nY14VPcnsGRwGWTLyZv9gxKHRUppVhQ\nE3o6ePXKEVgmONnQ4CjqmkGwWZvjMF2EPtAxvQLAuFa8Hqtkq5cgfgVkv/Vrcln4\nnQZVoMm3a3f5ODii2tQzNh6+7LL1bpqAmVEtABEBAAG0H0pvaG4gRG9lIDxqb2hu\nLmRvZUBleGFtcGxlLmNvbT6JATgEEwECACIFAlXUpNcCGwMGCwkIBwMCBhUIAgkK\nCwQWAgMBAh4BAheAAAoJEJNQnkuvyKSbfjoH/2OcSQOu1kJ20ndjhgY2yNChm7gd\ntU7TEBbB0TsLeazkrrLtKvrpW5+CRe07ZAG9HOtp3DikwAyrhSxhlYgVsQDhgB8q\nG0tYiZtQ88YyYrncCQ4hwknrcWXVW9bK3V4ZauxzPv3ADSloyR9tMURw5iHCIeL5\nfIw/pLvA3RjPMx4Sfow/bqRCUELua39prGw5Tv8a2ZRFbj2sgP5j8lUFegyJPQ4z\ntJhe6zZvKOzvIyxHO8llLmdrImsXRL9eqroWGs0VYqe6baQpY6xpSjbYK0J5HYcg\nTO+/u80JI+ROTMHE6unGp5Pgh/xIz6Wd34E0lWL1eOyNfGiPLyRWn1d0",
+      "status": "TRUSTED",
+      "problems": [],
+    },
+  }
+----
+
+[[get-gpg-key]]
+=== Get GPG Key
+--
+'GET /accounts/link:#account-id[\{account-id\}]/gpgkeys/link:#gpg-key-id[\{gpg-key-id\}]'
+--
+
+Retrieves a GPG key of a user.
+
+.Request
+----
+  GET /accounts/self/gpgkeys/AFC8A49B HTTP/1.0
+----
+
+As a response, a link:#gpg-key-info[GpgKeyInfo] entity is returned that
+describes the GPG key.
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  {
+    "id": "AFC8A49B",
+    "fingerprint": "0192 723D 42D1 0C5B 32A6  E1E0 9350 9E4B AFC8 A49B",
+    "user_ids": [
+      "John Doe \u003cjohn.doe@example.com\u003e"
+    ],
+    "key": "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: BCPG v1.52\n\nmQENBFXUpNcBCACv4paCiyKxZ0EcKy8VaWVNkJlNebRBiyw9WxU85wPOq5Gz/3GT\nRQwKqeY0SxVdQT8VNBw2sBe2m6eqcfZ2iKmesSlbXMe15DA7k8Bg4zEpQ0tXNG1L\nhceZDVQ1Xk06T2sgkunaiPsXi82nwN3UWYtDXxX4is5e6xBNL48Jgz4lbqo6+8D5\nvsVYiYMx4AwRkJyt/oA3IZAtSlY8Yd445nY14VPcnsGRwGWTLyZv9gxKHRUppVhQ\nE3o6ePXKEVgmONnQ4CjqmkGwWZvjMF2EPtAxvQLAuFa8Hqtkq5cgfgVkv/Vrcln4\nnQZVoMm3a3f5ODii2tQzNh6+7LL1bpqAmVEtABEBAAG0H0pvaG4gRG9lIDxqb2hu\nLmRvZUBleGFtcGxlLmNvbT6JATgEEwECACIFAlXUpNcCGwMGCwkIBwMCBhUIAgkK\nCwQWAgMBAh4BAheAAAoJEJNQnkuvyKSbfjoH/2OcSQOu1kJ20ndjhgY2yNChm7gd\ntU7TEBbB0TsLeazkrrLtKvrpW5+CRe07ZAG9HOtp3DikwAyrhSxhlYgVsQDhgB8q\nG0tYiZtQ88YyYrncCQ4hwknrcWXVW9bK3V4ZauxzPv3ADSloyR9tMURw5iHCIeL5\nfIw/pLvA3RjPMx4Sfow/bqRCUELua39prGw5Tv8a2ZRFbj2sgP5j8lUFegyJPQ4z\ntJhe6zZvKOzvIyxHO8llLmdrImsXRL9eqroWGs0VYqe6baQpY6xpSjbYK0J5HYcg\nTO+/u80JI+ROTMHE6unGp5Pgh/xIz6Wd34E0lWL1eOyNfGiPLyRWn1d0",
+    "status": "TRUSTED",
+    "problems": [],
+  }
+----
+
+[[add-delete-gpg-keys]]
+=== Add/Delete GPG Keys
+--
+'POST /accounts/link:#account-id[\{account-id\}]/gpgkeys'
+--
+
+Add or delete one or more GPG keys for a user.
+
+The changes must be provided in the request body as a
+link:#gpg-keys-input[GpgKeysInput] entity. Each new GPG key is provided in
+ASCII armored format, and must contain a self-signed certification
+matching a registered email or other identity of the user.
+
+.Request
+----
+  POST /accounts/link:#account-id[\{account-id\}]/gpgkeys
+  Content-Type: application/json
+
+  {
+    "add": [
+      "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: GnuPG v1\n\nmQENBFXUpNcBCACv4paCiyKxZ0EcKy8VaWVNkJlNebRBiyw9WxU85wPOq5Gz/3GT\nRQwKqeY0SxVdQT8VNBw2sBe2m6eqcfZ2iKmesSlbXMe15DA7k8Bg4zEpQ0tXNG1L\nhceZDVQ1Xk06T2sgkunaiPsXi82nwN3UWYtDXxX4is5e6xBNL48Jgz4lbqo6+8D5\nvsVYiYMx4AwRkJyt/oA3IZAtSlY8Yd445nY14VPcnsGRwGWTLyZv9gxKHRUppVhQ\nE3o6ePXKEVgmONnQ4CjqmkGwWZvjMF2EPtAxvQLAuFa8Hqtkq5cgfgVkv/Vrcln4\nnQZVoMm3a3f5ODii2tQzNh6+7LL1bpqAmVEtABEBAAG0H0pvaG4gRG9lIDxqb2hu\nLmRvZUBleGFtcGxlLmNvbT6JATgEEwECACIFAlXUpNcCGwMGCwkIBwMCBhUIAgkK\nCwQWAgMBAh4BAheAAAoJEJNQnkuvyKSbfjoH/2OcSQOu1kJ20ndjhgY2yNChm7gd\ntU7TEBbB0TsLeazkrrLtKvrpW5+CRe07ZAG9HOtp3DikwAyrhSxhlYgVsQDhgB8q\nG0tYiZtQ88YyYrncCQ4hwknrcWXVW9bK3V4ZauxzPv3ADSloyR9tMURw5iHCIeL5\nfIw/pLvA3RjPMx4Sfow/bqRCUELua39prGw5Tv8a2ZRFbj2sgP5j8lUFegyJPQ4z\ntJhe6zZvKOzvIyxHO8llLmdrImsXRL9eqroWGs0VYqe6baQpY6xpSjbYK0J5HYcg\nTO+/u80JI+ROTMHE6unGp5Pgh/xIz6Wd34E0lWL1eOyNfGiPLyRWn1d0yZO5AQ0E\nVdSk1wEIALUycrH2HK9zQYdR/KJo1yJJuaextLWsYYn881yDQo/p06U5vXOZ28lG\nAq/Xs96woVZPbgME6FyQzhf20Z2sbr+5bNo3OcEKaKX3Eo/sWwSJ7bXbGLDxMf4S\netfY1WDC+4rTqE30JuC++nQviPRdCcZf0AEgM6TxVhYEMVYwV787YO1IH62EBICM\nSkIONOfnusNZ4Skgjq9OzakOOpROZ4tki5cH/5oSDgdcaGPy1CFDpL9fG6er2zzk\nsw3qCbraqZrrlgpinWcAduiao67U/dV18O6OjYzrt33fTKZ0+bXhk1h1gloC21MQ\nya0CXlnfR/FOQhvuK0RlbR3cMfhZQscAEQEAAYkBHwQYAQIACQUCVdSk1wIbDAAK\nCRCTUJ5Lr8ikm8+QB/4uE+AlvFQFh9W8koPdfk7CJF7wdgZZ2NDtktvLL71WuMK8\nPOmf9f5JtcLCX4iJxGzcWogAR5ed20NgUoHUg7jn9Xm3fvP+kiqL6WqPhjazd89h\nk06v9hPE65kp4wb0fQqDrtWfP1lFGuh77rQgISt3Y4QutDl49vXS183JAfGPxFxx\n8FgGcfNwL2LVObvqCA0WLqeIrQVbniBPFGocE3yA/0W9BB/xtolpKfgMMsqGRMeu\n9oIsNxB2oE61OsqjUtGsnKQi8k5CZbhJaql4S89vwS+efK0R+mo+0N55b0XxRlCS\nfaURgAcjarQzJnG0hUps2GNO/+nM7UyyJAGfHlh5\n=EdXO\n-----END PGP PUBLIC KEY BLOCK-----\n"
+    ],
+    "delete": [
+      "DEADBEEF",
+    ]
+  }'
+----
+
+As a response, the modified GPG keys are returned as a map of
+link:#gpg-key-info[GpgKeyInfo] entities, keyed by ID. Deleted keys are
+represented by an empty object.
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  {
+    "AFC8A49B": {
+      "fingerprint": "0192 723D 42D1 0C5B 32A6  E1E0 9350 9E4B AFC8 A49B",
+      "user_ids": [
+        "John Doe \u003cjohn.doe@example.com\u003e"
+      ],
+      "key": "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: BCPG v1.52\n\nmQENBFXUpNcBCACv4paCiyKxZ0EcKy8VaWVNkJlNebRBiyw9WxU85wPOq5Gz/3GT\nRQwKqeY0SxVdQT8VNBw2sBe2m6eqcfZ2iKmesSlbXMe15DA7k8Bg4zEpQ0tXNG1L\nhceZDVQ1Xk06T2sgkunaiPsXi82nwN3UWYtDXxX4is5e6xBNL48Jgz4lbqo6+8D5\nvsVYiYMx4AwRkJyt/oA3IZAtSlY8Yd445nY14VPcnsGRwGWTLyZv9gxKHRUppVhQ\nE3o6ePXKEVgmONnQ4CjqmkGwWZvjMF2EPtAxvQLAuFa8Hqtkq5cgfgVkv/Vrcln4\nnQZVoMm3a3f5ODii2tQzNh6+7LL1bpqAmVEtABEBAAG0H0pvaG4gRG9lIDxqb2hu\nLmRvZUBleGFtcGxlLmNvbT6JATgEEwECACIFAlXUpNcCGwMGCwkIBwMCBhUIAgkK\nCwQWAgMBAh4BAheAAAoJEJNQnkuvyKSbfjoH/2OcSQOu1kJ20ndjhgY2yNChm7gd\ntU7TEBbB0TsLeazkrrLtKvrpW5+CRe07ZAG9HOtp3DikwAyrhSxhlYgVsQDhgB8q\nG0tYiZtQ88YyYrncCQ4hwknrcWXVW9bK3V4ZauxzPv3ADSloyR9tMURw5iHCIeL5\nfIw/pLvA3RjPMx4Sfow/bqRCUELua39prGw5Tv8a2ZRFbj2sgP5j8lUFegyJPQ4z\ntJhe6zZvKOzvIyxHO8llLmdrImsXRL9eqroWGs0VYqe6baQpY6xpSjbYK0J5HYcg\nTO+/u80JI+ROTMHE6unGp5Pgh/xIz6Wd34E0lWL1eOyNfGiPLyRWn1d0"
+      "status": "TRUSTED",
+      "problems": [],
+    }
+    "DEADBEEF": {}
+  }
+----
+
+[[delete-gpg-key]]
+=== Delete GPG Key
+--
+'DELETE /accounts/link:#account-id[\{account-id\}]/gpgkeys/link:#gpg-key-id[\{gpg-key-id\}]'
+--
+
+Deletes a GPG key of a user.
+
+.Request
+----
+  DELETE /accounts/self/gpgkeys/AFC8A49B HTTP/1.0
+----
+
+.Response
+----
+  HTTP/1.1 204 No Content
+----
+
 [[list-account-capabilities]]
 === List Account Capabilities
 --
@@ -1063,9 +1259,10 @@
   {
     "context": 10,
     "theme": "DEFAULT",
-    "ignore_whitespace": "IGNORE_ALL_SPACE",
+    "ignore_whitespace": "IGNORE_ALL",
     "intraline_difference": true,
     "line_length": 100,
+    "cursor_blink_rate": 500,
     "show_tabs": true,
     "show_whitespace_errors": true,
     "syntax_highlighting": true,
@@ -1086,15 +1283,16 @@
 
 .Request
 ----
-  GET /a/accounts/self/preferences.diff HTTP/1.0
+  PUT /a/accounts/self/preferences.diff HTTP/1.0
   Content-Type: application/json; charset=UTF-8
 
   {
     "context": 10,
     "theme": "ECLIPSE",
-    "ignore_whitespace": "IGNORE_ALL_SPACE",
+    "ignore_whitespace": "IGNORE_ALL",
     "intraline_difference": true,
     "line_length": 100,
+    "cursor_blink_rate": 500,
     "show_line_endings": true,
     "show_tabs": true,
     "show_whitespace_errors": true,
@@ -1116,7 +1314,7 @@
   {
     "context": 10,
     "theme": "ECLIPSE",
-    "ignore_whitespace": "IGNORE_ALL_SPACE",
+    "ignore_whitespace": "IGNORE_ALL",
     "intraline_difference": true,
     "line_length": 100,
     "show_line_endings": true,
@@ -1127,6 +1325,84 @@
   }
 ----
 
+[[get-edit-preferences]]
+=== Get Edit Preferences
+--
+'GET /accounts/link:#account-id[\{account-id\}]/preferences.edit'
+--
+
+Retrieves the edit preferences of a user.
+
+.Request
+----
+  GET /a/accounts/self/preferences.edit HTTP/1.0
+----
+
+As result the edit preferences of the user are returned as a
+link:#edit-preferences-info[EditPreferencesInfo] entity.
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json;charset=UTF-8
+
+  )]}'
+  {
+    "theme": "ECLIPSE",
+    "key_map_type": "VIM",
+    "tab_size": 4,
+    "line_length": 80,
+    "cursor_blink_rate": 530,
+    "hide_top_menu": true,
+    "show_whitespace_errors": true,
+    "hide_line_numbers": true,
+    "match_brackets": true,
+    "line_wrapping": false,
+    "auto_close_brackets": true
+  }
+----
+
+[[set-edit-preferences]]
+=== Set Edit Preferences
+--
+'PUT /accounts/link:#account-id[\{account-id\}]/preferences.edit'
+--
+
+Sets the edit preferences of a user.
+
+The new edit preferences must be provided in the request body as a
+link:#edit-preferences-info[EditPreferencesInfo] entity.
+
+.Request
+----
+  PUT /a/accounts/self/preferences.edit HTTP/1.0
+  Content-Type: application/json;charset=UTF-8
+
+  {
+    "theme": "ECLIPSE",
+    "key_map_type": "VIM",
+    "tab_size": 4,
+    "line_length": 80,
+    "cursor_blink_rate": 530,
+    "hide_top_menu": true,
+    "show_tabs": true,
+    "show_whitespace_errors": true,
+    "syntax_highlighting": true,
+    "hide_line_numbers": true,
+    "match_brackets": true,
+    "line_wrapping": false,
+    "auto_close_brackets": true
+  }
+----
+
+The response is "`204 No Content`"
+
+.Response
+----
+  HTTP/1.1 204 No Content
+----
+
 [[get-starred-changes]]
 === Get Starred Changes
 --
@@ -1241,10 +1517,32 @@
 === \{ssh-key-id\}
 The sequence number of the SSH key.
 
+[[gpg-key-id]]
+=== \{gpg-key-id\}
+A GPG key identifier, either the 8-character hex key reported by
+`gpg --list-keys`, or the 40-character hex fingerprint (whitespace is
+ignored) reported by `gpg --list-keys --with-fingerprint`.
+
 
 [[json-entities]]
 == JSON Entities
 
+[[account-detail-info]]
+=== AccountDetailInfo
+The `AccountDetailInfo` entity contains detailled information about an
+account.
+
+`AccountDetailInfo` has the same fields as link:#account-info[
+AccountInfo]. In addition `AccountDetailInfo` has the following fields:
+
+[options="header",cols="1,^1,5"]
+|=================================
+|Field Name          ||Description
+|`registered_on`     ||
+The link:rest-api.html#timestamp[timestamp] of when the account was
+registered.
+|=================================
+
 [[account-info]]
 === AccountInfo
 The `AccountInfo` entity contains information about an account.
@@ -1327,6 +1625,9 @@
 capability.
 |`killTask`          |not set if `false`|Whether the user has the
 link:access-control.html#capability_kill[Kill Task] capability.
+|`maintainServer`    |not set if `false`|Whether the user has the
+link:access-control.html#capability_maintainServer[Maintain
+Server] capability.
 |`priority`          |not set if `INTERACTIVE`|The name of the thread
 pool used by the user, see link:access-control.html#capability_priority[
 Priority] capability.
@@ -1372,12 +1673,15 @@
 |`ignore_whitespace`           ||
 Whether whitespace changes should be ignored and if yes, which
 whitespace changes should be ignored. +
-Allowed values are `IGNORE_NONE`, `IGNORE_SPACE_AT_EOL`,
-`IGNORE_SPACE_CHANGE`, `IGNORE_ALL_SPACE`.
+Allowed values are `IGNORE_NONE`, `IGNORE_TRAILING`,
+`IGNORE_LEADING_AND_TRAILING`, `IGNORE_ALL`.
 |`intraline_difference`        |not set if `false`|
 Whether intraline differences should be highlighted.
 |`line_length`                 ||
 Number of characters that should be displayed in one line.
+|`cursor_blink_rate`           ||
+Half-period in milliseconds used for cursor blinking.
+Setting it to 0 disables cursor blinking.
 |`manual_review`               |not set if `false`|
 Whether the 'Reviewed' flag should not be set automatically on a patch
 when it is viewed.
@@ -1399,7 +1703,7 @@
 |`syntax_highlighting`         |not set if `false`|
 Whether syntax highlighting should be enabled.
 |`hide_top_menu`               |not set if `false`|
-If true the top menu header and site header is hidden.
+If true the top menu header and site header are hidden.
 |`auto_hide_diff_table_header` |not set if `false`|
 If true the diff table header is automatically hidden when
 scrolling down more than half of a page.
@@ -1410,6 +1714,10 @@
 |'hide_empty_pane'             |not set if `false`|
 Whether empty panes should be hidden. The left pane is empty when a
 file was added; the right pane is empty when a file was deleted.
+|`match_brackets`              |not set if `false`|
+Whether matching brackets should be highlighted.
+|`line_wrapping`               |not set if `false`|
+Whether to enable line wrapping or not.
 |===========================================
 
 [[diff-preferences-input]]
@@ -1428,8 +1736,8 @@
 |`ignore_whitespace`           |optional|
 Whether whitespace changes should be ignored and if yes, which
 whitespace changes should be ignored. +
-Allowed values are `IGNORE_NONE`, `IGNORE_SPACE_AT_EOL`,
-`IGNORE_SPACE_CHANGE`, `IGNORE_ALL_SPACE`.
+Allowed values are `IGNORE_NONE`, `IGNORE_TRAILING`,
+`IGNORE_LEADING_AND_TRAILING`, `IGNORE_ALL`.
 |`intraline_difference`        |optional|
 Whether intraline differences should be highlighted.
 |`line_length`                 |optional|
@@ -1463,6 +1771,48 @@
 True if the line numbers should be hidden.
 |`tab_size`                    |optional|
 Number of spaces that should be used to display one tab.
+|`line_wrapping`               |optional|
+Whether to enable line wrapping or not.
+|===========================================
+
+[[edit-preferences-info]]
+=== EditPreferencesInfo
+The `EditPreferencesInfo` entity contains information about the edit
+preferences of a user.
+
+[options="header",cols="1,^1,5"]
+|===========================================
+|Field Name                    ||Description
+|`theme`                       ||
+The CodeMirror theme. Currently only a subset of light and dark
+CodeMirror themes are supported. Light themes `DEFAULT`, `ECLIPSE`,
+`ELEGANT`, `NEAT`. Dark themes `MIDNIGHT`, `NIGHT`, `TWILIGHT`.
+|`key_map_type`                ||
+The CodeMirror key map. Currently only a subset of key maps are
+supported: `DEFAULT`, `EMACS`, `VIM`.
+|`tab_size`                    ||
+Number of spaces that should be used to display one tab.
+|`line_length`                 ||
+Number of characters that should be displayed per line.
+|`cursor_blink_rate`           ||
+Half-period in milliseconds used for cursor blinking.
+Setting it to 0 disables cursor blinking.
+|`hide_top_menu`               |not set if `false`|
+If true the top menu header and site header is hidden.
+|`show_tabs`                   |not set if `false`|
+Whether tabs should be shown.
+|`show_whitespace_errors`      |not set if `false`|
+Whether whitespace errors should be shown.
+|`syntax_highlighting`         |not set if `false`|
+Whether syntax highlighting should be enabled.
+|`hide_line_numbers`           |not set if `false`|
+Whether line numbers should be hidden.
+|`match_brackets`              |not set if `false`|
+Whether matching brackets should be highlighted.
+|`line_wrapping`               |not set if `false`|
+Whether to enable line wrapping or not.
+|`auto_close_brackets`         |not set if `false`|
+Whether brackets and quotes should be auto-closed during typing.
 |===========================================
 
 [[email-info]]
@@ -1504,6 +1854,41 @@
 confirmation.
 |==============================
 
+[[gpg-key-info]]
+=== GpgKeyInfo
+The `GpgKeyInfo` entity contains information about a GPG public key.
+
+[options="header",cols="1,^1,5"]
+|========================
+|Field Name   ||Description
+|`id`         |Not set in map context|The 8-char hex GPG key ID.
+|`fingerprint`|Not set for deleted keys|The 40-char (plus spaces) hex GPG key fingerprint.
+|`user_ids`   |Not set for deleted keys|
+link:https://tools.ietf.org/html/rfc4880#section-5.11[OpenPGP User IDs]
+associated with the public key.
+|`key`        |Not set for deleted keys|ASCII armored public key material.
+|`status`     |Not set for deleted keys|
+The result of server-side checks on the key; one of `BAD`, `OK`, or `TRUSTED`.
+`BAD` keys have serious problems and should not be used. If a key is `OK,
+inspecting only that key found no problems, but the system does not fully trust
+the key's origin. A `TRUSTED` key is valid, and the system knows enough about
+the key and its origin to trust it.
+|`problems`   |Not set for deleted keys|
+A list of human-readable problem strings found in the course of checking whether
+the key is valid and trusted.
+|========================
+
+[[gpg-keys-input]]
+=== GpgKeysInput
+The `GpgKeysInput` entity contains information for adding/deleting GPG keys.
+
+[options="header",cols="1,6"]
+|========================
+|Field Name|Description
+|`add`     |List of ASCII armored public key strings to add.
+|`delete`  |List of link:#gpg-key-id[`\{gpg-key-id\}`]s to delete.
+|========================
+
 [[http-password-input]]
 === HttpPasswordInput
 The `HttpPasswordInput` entity contains information for setting/generating
@@ -1526,42 +1911,50 @@
 The `PreferencesInfo` entity contains information about a user's preferences.
 
 [options="header",cols="1,^1,5"]
-|=====================================
-|Field Name              ||Description
-|`changes_per_page`               ||
+|============================================
+|Field Name                     ||Description
+|`changes_per_page`             ||
 The number of changes to show on each page.
 Allowed values are `10`, `25`, `50`, `100`.
-|`show_site_header`   |not set if `false`|
+|`show_site_header`             |not set if `false`|
 Whether the site header should be shown.
-|`use_flash_clipboard`     |not set if `false`|
+|`use_flash_clipboard`          |not set if `false`|
 Whether to use the flash clipboard widget.
-|`download_scheme`      ||
-The type of download URL the user prefers to use.
-|`download_command`     ||
+|`download_scheme`              ||
+The type of download URL the user prefers to use. May be any key from
+the `schemes` map in
+link:rest-api-config.html#download-info[DownloadInfo].
+|`download_command`             ||
 The type of download command the user prefers to use.
-|`copy_self_on_email`       |not set if `false`|
+|`copy_self_on_email`           |not set if `false`|
 Whether to CC me on comments I write.
-|`date_format`         ||
+|`date_format`                  ||
 The format to display the date in.
 Allowed values are `STD`, `US`, `ISO`, `EURO`, `UK`.
-|`time_format`     ||
+|`time_format`                  ||
 The format to display the time in.
 Allowed values are `HHMM_12`, `HHMM_24`.
-|`relative_date_in_change_table`  |not set if `false`|
+|`relative_date_in_change_table`|not set if `false`|
 Whether to show relative dates in the changes table.
-|`size_bar_in_change_table`      |not set if `false`|
+|`size_bar_in_change_table`     |not set if `false`|
 Whether to show the change sizes as colored bars in the change table.
-|`legacycid_in_change_table`      |not set if `false`|
+|`legacycid_in_change_table`    |not set if `false`|
 Whether to show change number in the change table.
-|`mute_common_path_prefixes` |not set if `false`|
+|`mute_common_path_prefixes`    |not set if `false`|
 Whether to mute common path prefixes in file names in the file table.
-|`review_category_strategy`   ||
+|`review_category_strategy`     ||
 The strategy used to displayed info in the review category column.
 Allowed values are `NONE`, `NAME`, `EMAIL`, `USERNAME`, `ABBREV`.
-|`diff_view`     ||
+|`diff_view`                    ||
 The type of diff view to show.
 Allowed values are `SIDE_BY_SIDE`, `UNIFIED_DIFF`.
-|=====================================
+|`my`                           ||
+The menu items of the `MY` top menu as a list of
+link:rest-api-config.html#top-menu-item-info[TopMenuItemInfo] entities.
+|`url_aliases`                  |optional|
+A map of URL path pairs, where the first URL path is an alias for the
+second URL path.
+|============================================
 
 [[preferences-input]]
 === PreferencesInput
@@ -1569,42 +1962,48 @@
 user preferences. Fields which are not set will not be updated.
 
 [options="header",cols="1,^1,5"]
-|=====================================
-|Field Name              ||Description
-|`changes_per_page`               |optional|
+|============================================
+|Field Name                     ||Description
+|`changes_per_page`             |optional|
 The number of changes to show on each page.
 Allowed values are `10`, `25`, `50`, `100`.
-|`show_site_header`   |optional|
+|`show_site_header`             |optional|
 Whether the site header should be shown.
-|`use_flash_clipboard`     |optional|
+|`use_flash_clipboard`          |optional|
 Whether to use the flash clipboard widget.
-|`download_scheme`      |optional|
+|`download_scheme`              |optional|
 The type of download URL the user prefers to use.
-|`download_command`     |optional|
+|`download_command`             |optional|
 The type of download command the user prefers to use.
-|`copy_self_on_email`       |optional|
+|`copy_self_on_email`           |optional|
 Whether to CC me on comments I write.
-|`date_format`         |optional|
+|`date_format`                  |optional|
 The format to display the date in.
 Allowed values are `STD`, `US`, `ISO`, `EURO`, `UK`.
-|`time_format`     |optional|
+|`time_format`                  |optional|
 The format to display the time in.
 Allowed values are `HHMM_12`, `HHMM_24`.
-|`relative_date_in_change_table`  |optional|
+|`relative_date_in_change_table`|optional|
 Whether to show relative dates in the changes table.
-|`size_bar_in_change_table`      |optional|
+|`size_bar_in_change_table`     |optional|
 Whether to show the change sizes as colored bars in the change table.
-|`legacycid_in_change_table`      |optional|
+|`legacycid_in_change_table`    |optional|
 Whether to show change number in the change table.
-|`mute_common_path_prefixes` |optional|
+|`mute_common_path_prefixes`    |optional|
 Whether to mute common path prefixes in file names in the file table.
-|`review_category_strategy`   |optional|
+|`review_category_strategy`     |optional|
 The strategy used to displayed info in the review category column.
 Allowed values are `NONE`, `NAME`, `EMAIL`, `USERNAME`, `ABBREV`.
-|`diff_view`     |optional|
+|`diff_view`                    |optional|
 The type of diff view to show.
 Allowed values are `SIDE_BY_SIDE`, `UNIFIED_DIFF`.
-|=====================================
+|`my`                           |optional|
+The menu items of the `MY` top menu as a list of
+link:rest-api-config.html#top-menu-item-info[TopMenuItemInfo] entities.
+|`url_aliases`                  |optional|
+A map of URL path pairs, where the first URL path is an alias for the
+second URL path.
+|============================================
 
 [[query-limit-info]]
 === QueryLimitInfo
@@ -1634,6 +2033,17 @@
 |`valid`         ||Whether the SSH key is valid.
 |=============================
 
+[[username-input]]
+=== UsernameInput
+The `UsernameInput` entity contains information for setting the
+username for an account.
+
+[options="header",cols="1,6"]
+|=======================
+|Field Name |Description
+|`username` |The new username of the account.
+|=======================
+
 
 GERRIT
 ------
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index 51c3a65..aa9417b 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -224,20 +224,13 @@
 * `ALL_REVISIONS`: describe all revisions, not just current.
 --
 
-[[download_commands]]
+[[download-commands]]
 --
 * `DOWNLOAD_COMMANDS`: include the `commands` field in the
   link:#fetch-info[FetchInfo] for revisions. Only valid when the
   `CURRENT_REVISION` or `ALL_REVISIONS` option is selected.
 --
 
-[[draft_comments]]
---
-* `DRAFT_COMMENTS`: include the `has_draft_comments` field for
-  revisions. Only valid when the `CURRENT_REVISION` or `ALL_REVISIONS`
-  option is selected.
---
-
 [[current-commit]]
 --
 * `CURRENT_COMMIT`: parse and output all header fields from the
@@ -294,8 +287,13 @@
 
 [[reviewed]]
 --
-* `REVIEWED`: include the `reviewed` field if the caller is
-  authenticated and has commented on the current revision.
+* `REVIEWED`: include the `reviewed` field if all of the following are
+  true:
+  * the change is open
+  * the caller is authenticated
+  * the caller has commented on the change more recently than the last update
+    from the change owner, i.e. this change would show up in the results of
+    link:user-search.html#reviewedby[reviewedby:self].
 --
 
 [[web-links]]
@@ -310,6 +308,21 @@
 * `CHECK`: include potential problems with the change.
 --
 
+[[commit-footers]]
+--
+* `COMMIT_FOOTERS`: include the full commit message with
+  Gerrit-specific commit footers in the
+  link:#revision-info[RevisionInfo].
+--
+
+[[push-certificates]]
+--
+* `PUSH_CERTIFICATES`: include push certificate information in the
+  link:#revision-info[RevisionInfo]. Ignored if signed push is not
+  link:config-gerrit.html#receive.enableSignedPush[enabled] on the
+  server.
+--
+
 .Request
 ----
   GET /changes/?q=97&o=CURRENT_REVISION&o=CURRENT_COMMIT&o=CURRENT_FILES&o=DOWNLOAD_COMMANDS HTTP/1.0
@@ -400,29 +413,36 @@
           },
           "files": {
             "gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeCache.java": {
-              "lines_deleted": 8
+              "lines_deleted": 8,
+              "size_delta": -412
             },
             "gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeDetailCache.java": {
-              "lines_inserted": 1
+              "lines_inserted": 1,
+              "size_delta": 23
             },
             "gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java": {
               "lines_inserted": 11,
-              "lines_deleted": 19
+              "lines_deleted": 19,
+              "size_delta": -298
             },
             "gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java": {
               "lines_inserted": 23,
-              "lines_deleted": 20
+              "lines_deleted": 20,
+              "size_delta": 132
             },
             "gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/StarCache.java": {
               "status": "D",
-              "lines_deleted": 139
+              "lines_deleted": 139,
+              "size_delta": -5512
             },
             "gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/StarredChanges.java": {
               "status": "A",
-              "lines_inserted": 204
+              "lines_inserted": 204,
+              "size_delta": 8345
             },
             "gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Screen.java": {
-              "lines_deleted": 9
+              "lines_deleted": 9,
+              "size_delta": -343
             }
           }
         }
@@ -711,9 +731,6 @@
 
 Deletes the topic of a change.
 
-The request body does not need to include a link:#topic-input[
-TopicInput] entity if no review comment is added.
-
 Please note that some proxies prohibit request bodies for DELETE
 requests. In this case, if you want to specify a commit message, use
 link:#set-topic[PUT] to delete the topic.
@@ -1061,6 +1078,272 @@
   blocked by Verified
 ----
 
+[[submitted-together]]
+=== Changes submitted together
+--
+'GET /changes/link:#change-id[\{change-id\}]/submitted_together'
+--
+
+Returns a list of all changes which are submitted when
+link:#submit-change[\{submit\}] is called for this change,
+including the current change itself.
+
+An empty list is returned if this change will be submitted
+by itself (no other changes).
+
+.Request
+----
+  GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/submitted_together HTTP/1.0
+  Content-Type: application/json; charset=UTF-8
+----
+
+The return value is a list of changes in the same format as in
+link:#list-changes[\{listing changes\}] with the options
+link:#labels[\{LABELS\}], link:#detailed-labels[\{DETAILED_LABELS\}],
+link:#current-revision[\{CURRENT_REVISION\}],
+link:#current-commit[\{CURRENT_COMMIT\}] set.
+The list consists of:
+
+* The given change.
+* If link:config-gerrit.html#change.submitWholeTopic[`change.submitWholeTopic`]
+  is enabled, include all open changes with the same topic.
+* For each change whose submit type is not CHERRY_PICK, include unmerged
+  ancestors targeting the same branch.
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json; charset=UTF-8
+
+)]}'
+[
+  {
+    "id": "gerrit~master~I1ffe09a505e25f15ce1521bcfb222e51e62c2a14",
+    "project": "gerrit",
+    "branch": "master",
+    "hashtags": [],
+    "change_id": "I1ffe09a505e25f15ce1521bcfb222e51e62c2a14",
+    "subject": "ChangeMergeQueue: Rewrite such that it works on set of changes",
+    "status": "NEW",
+    "created": "2015-05-01 15:39:57.979000000",
+    "updated": "2015-05-20 19:25:21.592000000",
+    "mergeable": true,
+    "insertions": 303,
+    "deletions": 210,
+    "_number": 1779,
+    "owner": {
+      "_account_id": 1000000
+    },
+    "labels": {
+      "Code-Review": {
+        "approved": {
+          "_account_id": 1000000
+        },
+        "all": [
+          {
+            "value": 2,
+            "date": "2015-05-20 19:25:21.592000000",
+            "_account_id": 1000000
+          }
+        ],
+        "values": {
+          "-2": "This shall not be merged",
+          "-1": "I would prefer this is not merged as is",
+          " 0": "No score",
+          "+1": "Looks good to me, but someone else must approve",
+          "+2": "Looks good to me, approved"
+        },
+        "default_value": 0
+      },
+      "Verified": {
+        "approved": {
+          "_account_id": 1000000
+        },
+        "all": [
+          {
+            "value": 1,
+            "date": "2015-05-20 19:25:21.592000000",
+            "_account_id": 1000000
+          }
+        ],
+        "values": {
+          "-1": "Fails",
+          " 0": "No score",
+          "+1": "Verified"
+        },
+        "default_value": 0
+      }
+    },
+    "permitted_labels": {
+      "Code-Review": [
+        "-2",
+        "-1",
+        " 0",
+        "+1",
+        "+2"
+      ],
+      "Verified": [
+        "-1",
+        " 0",
+        "+1"
+      ]
+    },
+    "removable_reviewers": [
+      {
+        "_account_id": 1000000
+      }
+    ],
+    "current_revision": "9adb9f4c7b40eeee0646e235de818d09164d7379",
+    "revisions": {
+      "9adb9f4c7b40eeee0646e235de818d09164d7379": {
+        "_number": 1,
+        "created": "2015-05-01 15:39:57.979000000",
+        "uploader": {
+          "_account_id": 1000000
+        },
+        "ref": "refs/changes/79/1779/1",
+        "fetch": {},
+        "commit": {
+          "parents": [
+            {
+              "commit": "2d3176497a2747faed075f163707e57d9f961a1c",
+              "subject": "Merge changes from topic \u0027submodule-subscription-tests-and-fixes-3\u0027"
+            }
+          ],
+          "author": {
+            "name": "Stefan Beller",
+            "email": "sbeller@google.com",
+            "date": "2015-04-29 21:36:52.000000000",
+            "tz": -420
+          },
+          "committer": {
+            "name": "Stefan Beller",
+            "email": "sbeller@google.com",
+            "date": "2015-05-01 00:11:16.000000000",
+            "tz": -420
+          },
+          "subject": "ChangeMergeQueue: Rewrite such that it works on set of changes",
+          "message": "ChangeMergeQueue: Rewrite such that it works on set of changes\n\nChangeMergeQueue used to work on branches rather than sets of changes.\nThis change is a first step to merge sets of changes (e.g. grouped by a\ntopic and `changes.submitWholeTopic` enabled) in an atomic fashion.\nThis change doesn\u0027t aim to implement these changes, but only as a step\ntowards it.\n\nMergeOp keeps its functionality and behavior as is. A new class\nMergeOpMapper is introduced which will map the set of changes to\nthe set of branches. Additionally the MergeOpMapper is also\nresponsible for the threading done right now, which was part of\nthe ChangeMergeQueue before.\n\nChange-Id: I1ffe09a505e25f15ce1521bcfb222e51e62c2a14\n"
+        }
+      }
+    }
+  },
+  {
+    "id": "gerrit~master~I7fe807e63792b3d26776fd1422e5e790a5697e22",
+    "project": "gerrit",
+    "branch": "master",
+    "hashtags": [],
+    "change_id": "I7fe807e63792b3d26776fd1422e5e790a5697e22",
+    "subject": "AbstractSubmoduleSubscription: Split up createSubscription",
+    "status": "NEW",
+    "created": "2015-05-01 15:39:57.979000000",
+    "updated": "2015-05-20 19:25:21.546000000",
+    "mergeable": true,
+    "insertions": 15,
+    "deletions": 6,
+    "_number": 1780,
+    "owner": {
+      "_account_id": 1000000
+    },
+    "labels": {
+      "Code-Review": {
+        "approved": {
+          "_account_id": 1000000
+        },
+        "all": [
+          {
+            "value": 2,
+            "date": "2015-05-20 19:25:21.546000000",
+            "_account_id": 1000000
+          }
+        ],
+        "values": {
+          "-2": "This shall not be merged",
+          "-1": "I would prefer this is not merged as is",
+          " 0": "No score",
+          "+1": "Looks good to me, but someone else must approve",
+          "+2": "Looks good to me, approved"
+        },
+        "default_value": 0
+      },
+      "Verified": {
+        "approved": {
+          "_account_id": 1000000
+        },
+        "all": [
+          {
+            "value": 1,
+            "date": "2015-05-20 19:25:21.546000000",
+            "_account_id": 1000000
+          }
+        ],
+        "values": {
+          "-1": "Fails",
+          " 0": "No score",
+          "+1": "Verified"
+        },
+        "default_value": 0
+      }
+    },
+    "permitted_labels": {
+      "Code-Review": [
+        "-2",
+        "-1",
+        " 0",
+        "+1",
+        "+2"
+      ],
+      "Verified": [
+        "-1",
+        " 0",
+        "+1"
+      ]
+    },
+    "removable_reviewers": [
+      {
+        "_account_id": 1000000
+      }
+    ],
+    "current_revision": "1bd7c12a38854a2c6de426feec28800623f492c4",
+    "revisions": {
+      "1bd7c12a38854a2c6de426feec28800623f492c4": {
+        "_number": 1,
+        "created": "2015-05-01 15:39:57.979000000",
+        "uploader": {
+          "_account_id": 1000000
+        },
+        "ref": "refs/changes/80/1780/1",
+        "fetch": {},
+        "commit": {
+          "parents": [
+            {
+              "commit": "9adb9f4c7b40eeee0646e235de818d09164d7379",
+              "subject": "ChangeMergeQueue: Rewrite such that it works on set of changes"
+            }
+          ],
+          "author": {
+            "name": "Stefan Beller",
+            "email": "sbeller@google.com",
+            "date": "2015-04-25 00:11:59.000000000",
+            "tz": -420
+          },
+          "committer": {
+            "name": "Stefan Beller",
+            "email": "sbeller@google.com",
+            "date": "2015-05-01 00:11:16.000000000",
+            "tz": -420
+          },
+          "subject": "AbstractSubmoduleSubscription: Split up createSubscription",
+          "message": "AbstractSubmoduleSubscription: Split up createSubscription\n\nLater we want to have subscriptions to more submodules, so we need to\nfind a way to add more submodule entries into the file. By splitting up\nthe createSubscription() method, that is very easy by using the\naddSubmoduleSubscription method multiple times.\n\nChange-Id: I7fe807e63792b3d26776fd1422e5e790a5697e22\n"
+        }
+      }
+    }
+  }
+]
+----
+
+
 [[publish-draft-change]]
 === Publish Draft Change
 --
@@ -1146,6 +1429,109 @@
   HTTP/1.1 204 No Content
 ----
 
+[[list-change-comments]]
+=== List Change Comments
+--
+'GET /changes/link:#change-id[\{change-id\}]/comments'
+--
+
+Lists the published comments of all revisions of the change.
+
+Returns a map of file paths to lists of link:#comment-info[CommentInfo]
+entries. The entries in the map are sorted by file path, and the
+comments for each path are sorted by patch set number. Each comment has
+the `patch_set` and `author` fields set.
+
+.Request
+----
+  GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/comments HTTP/1.0
+----
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  {
+    "gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java": [
+      {
+        "patch_set": 1,
+        "id": "TvcXrmjM",
+        "line": 23,
+        "message": "[nit] trailing whitespace",
+        "updated": "2013-02-26 15:40:43.986000000"
+        "author": {
+          "_account_id": 1000096,
+          "name": "John Doe",
+          "email": "john.doe@example.com"
+        }
+      },
+      {
+        "patch_set": 2,
+        "id": "TveXwFiA",
+        "line": 49,
+        "in_reply_to": "TfYX-Iuo",
+        "message": "Done",
+        "updated": "2013-02-26 15:40:45.328000000"
+        "author": {
+          "_account_id": 1000097,
+          "name": "Jane Roe",
+          "email": "jane.roe@example.com"
+        }
+      }
+    ]
+  }
+----
+
+[[list-change-drafts]]
+=== List Change Drafts
+--
+'GET /changes/link:#change-id[\{change-id\}]/drafts'
+--
+
+Lists the draft comments of all revisions of the change that belong to
+the calling user.
+
+Returns a map of file paths to lists of link:#comment-info[CommentInfo]
+entries. The entries in the map are sorted by file path, and the
+comments for each path are sorted by patch set number. Each comment has
+the `patch_set` field set, and no `author`.
+
+.Request
+----
+  GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/drafts HTTP/1.0
+----
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  {
+    "gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java": [
+      {
+        "patch_set": 1,
+        "id": "TvcXrmjM",
+        "line": 23,
+        "message": "[nit] trailing whitespace",
+        "updated": "2013-02-26 15:40:43.986000000"
+      },
+      {
+        "patch_set": 2,
+        "id": "TveXwFiA",
+        "line": 49,
+        "in_reply_to": "TfYX-Iuo",
+        "message": "Done",
+        "updated": "2013-02-26 15:40:45.328000000"
+      }
+    ]
+  }
+----
+
 [[check-change]]
 === Check change
 --
@@ -1206,6 +1592,9 @@
 /check], and additionally fixes any problems that can be fixed
 automatically. The returned field values reflect any fixes.
 
+Some fixes have options controlling their behavior, which can be set in the
+link:#fix-input[FixInput] entity body.
+
 Only the change owner, a project owner, or an administrator may fix changes.
 
 .Request
@@ -1859,6 +2248,7 @@
 
   )]}'
   {
+    "commit": "674ac754f91e64a0efb8087e59a176484bd534d1",
     "parents": [
       {
         "commit": "1eee2c9d8f352483781e772f35dc586a69ff5646",
@@ -2105,6 +2495,7 @@
         "_change_number": 58478,
         "_revision_number": 2,
         "_current_revision_number": 2
+        "status": "NEW"
       },
       {
         "change_id": "I5e4fc08ce34d33c090c9e0bf320de1b17309f774",
@@ -2126,6 +2517,7 @@
         "_change_number": 58081,
         "_revision_number": 10,
         "_current_revision_number": 10
+        "status": "NEW"
       }
     ]
   }
@@ -2303,18 +2695,10 @@
 
 Submits a revision.
 
-The request body only needs to include a link:#submit-input[
-SubmitInput] entity if the request should wait for the merge to
-complete.
-
 .Request
 ----
   POST /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/674ac754f91e64a0efb8087e59a176484bd534d1/submit HTTP/1.0
   Content-Type: application/json; charset=UTF-8
-
-  {
-    "wait_for_merge": true
-  }
 ----
 
 As response a link:#submit-info[SubmitInfo] entity is returned that
@@ -2583,7 +2967,7 @@
 ----
 
 [[list-drafts]]
-=== List Drafts
+=== List Revision Drafts
 --
 'GET /changes/link:#change-id[\{change-id\}]/revisions/link:#revision-id[\{revision-id\}]/drafts/'
 --
@@ -2591,9 +2975,8 @@
 Lists the draft comments of a revision that belong to the calling
 user.
 
-As result a map is returned that maps the file path to a list of
-link:#comment-info[CommentInfo] entries. The entries in the map are
-sorted by file path.
+Returns a map of file paths to lists of link:#comment-info[CommentInfo]
+entries. The entries in the map are sorted by file path.
 
 .Request
 ----
@@ -2762,7 +3145,7 @@
 ----
 
 [[list-comments]]
-=== List Comments
+=== List Revision Comments
 --
 'GET /changes/link:#change-id[\{change-id\}]/revisions/link:#revision-id[\{revision-id\}]/comments/'
 --
@@ -2880,11 +3263,13 @@
   {
     "/COMMIT_MSG": {
       "status": "A",
-      "lines_inserted": 7
+      "lines_inserted": 7,
+      "size_delta": 551
     },
     "gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java": {
       "lines_inserted": 5,
-      "lines_deleted": 3
+      "lines_deleted": 3,
+      "size_delta": 98
     }
   }
 ----
@@ -2952,6 +3337,63 @@
 `application/json` the content is returned as JSON string and
 `X-FYI-Content-Encoding` is set to `json`.
 
+[[get-safe-content]]
+=== Download Content
+--
+'GET /changes/link:#change-id[\{change-id\}]/revisions/link:#revision-id[\{revision-id\}]/files/link:#file-id[\{file-id\}]/download'
+--
+
+Downloads the content of a file from a certain revision, in a safe format
+that poses no risk for inadvertent execution of untrusted code.
+
+If the content type is defined as safe, the binary file content is returned
+verbatim. If the content type is not safe, the file is stored inside a ZIP
+file, containing a single entry with a random, unpredictable name having the
+same base and suffix as the true filename. The ZIP file is returned in
+verbatim binary form.
+
+See link:config-gerrit.html#mimetype.name.safe[Gerrit config documentation]
+for information about safe file type configuration.
+
+The HTTP resource Content-Type is dependent on the file type: the
+applicable type for safe files, or "application/zip" for unsafe files.
+
+The optional, integer-valued `parent` parameter can be specified to request
+the named file from a parent commit of the specified revision. The value is
+the 1-based index of the parent's position in the commit object. If the
+parameter is omitted or the value non-positive, the patch set is referenced.
+
+Filenames are decorated with a suffix of `_new` for the current patch,
+`_old` for the only parent, or `_oldN` for the Nth parent of many.
+
+.Request
+----
+  GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/674ac754f91e64a0efb8087e59a176484bd534d1/files/website%2Freleases%2Flogo.png/safe_content HTTP/1.0
+----
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment; filename="logo.png"
+  Content-Type: image/png
+
+  `[binary data for logo.png]`
+----
+
+.Request
+----
+  GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/674ac754f91e64a0efb8087e59a176484bd534d1/files/gerrit-server%2Fsrc%2Fmain%2Fjava%2Fcom%2Fgoogle%2Fgerrit%2Fserver%2Fproject%2FRefControl.java/safe_content?suffix=new HTTP/1.0
+----
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: Content-Disposition:attachment; filename="RefControl_new-931cdb73ae9d97eb500a3533455b055d90b99944.java.zip"
+  Content-Type:application/zip
+
+  `[binary ZIP archive containing a single file, "RefControl_new-cb218df1337df48a0e7ab30a49a8067ac7321881.java"]`
+----
+
 [[get-diff]]
 === Get Diff
 --
@@ -3387,8 +3829,7 @@
 |`subject`            ||
 The subject of the change (header line of the commit message).
 |`status`             ||
-The status of the change (`NEW`, `SUBMITTED`, `MERGED`, `ABANDONED`,
-`DRAFT`).
+The status of the change (`NEW`, `MERGED`, `ABANDONED`, `DRAFT`).
 |`created`            ||
 The link:rest-api.html#timestamp[timestamp] of when the change was
 created.
@@ -3491,6 +3932,9 @@
 [options="header",cols="1,^1,5"]
 |===========================
 |Field Name    ||Description
+|`patch_set`   |optional|
+The patch set number for the comment; only set in contexts where +
+comments may be returned for multiple patch sets.
 |`id`          ||The URL encoded UUID of the comment.
 |`path`        |optional|
 The path of the file for which the inline comment was done. +
@@ -3573,25 +4017,28 @@
 === CommitInfo
 The `CommitInfo` entity contains information about a commit.
 
-[options="header",cols="1,6"]
-|==========================
-|Field Name    |Description
-|`commit`      |The commit ID.
-|`parents`     |
+[options="header",cols="1,^1,5"]
+|===========================
+|Field Name    ||Description
+|`commit`      |Optional|
+The commit ID. Not set if included in a link:#revision-info[
+RevisionInfo] entity that is contained in a map which has the commit ID
+as key.
+|`parents`     ||
 The parent commits of this commit as a list of
 link:#commit-info[CommitInfo] entities. In each parent
 only the `commit` and `subject` fields are populated.
-|`author`      |The author of the commit as a
+|`author`      ||The author of the commit as a
 link:#git-person-info[GitPersonInfo] entity.
-|`committer`   |The committer of the commit as a
+|`committer`   ||The committer of the commit as a
 link:#git-person-info[GitPersonInfo] entity.
-|`subject`     |
+|`subject`     ||
 The subject of the commit (header line of the commit message).
-|`message`     |The commit message.
+|`message`     ||The commit message.
 |`web_links`   |optional|
 Links to the commit in external sites as a list of
 link:#web-link-info[WebLinkInfo] entities.
-|==========================
+|===========================
 
 [[diff-content]]
 === DiffContent
@@ -3739,7 +4186,7 @@
 |`commands`    |optional|
 The download commands for this patch set as a map that maps the command
 names to the commands. +
-Only set if link:#download_commands[download commands] are requested.
+Only set if link:#download-commands[download commands] are requested.
 |==========================
 
 [[file-info]]
@@ -3763,8 +4210,25 @@
 |`lines_deleted` |optional|
 Number of deleted lines. +
 Not set for binary files or if no lines were deleted.
+|`size_delta`    ||
+Number of bytes by which the file size increased/decreased.
 |=============================
 
+[[fix-input]]
+=== FixInput
+The `FixInput` entity contains options for fixing commits using the
+link:#fix-change[fix change] endpoint.
+
+[options="header",cols="1,6"]
+|==========================
+|Field Name                          |Description
+|`delete_patch_set_if_commit_missing`|If true, delete patch sets from the
+database if they refer to missing commit options.
+|`expect_merged_as`                  |If set, check that the change is
+merged into the destination branch as this exact SHA-1. If not, insert
+a new patch set referring to this commit.
+|==========================
+
 [[git-person-info]]
 === GitPersonInfo
 The `GitPersonInfo` entity contains information about the
@@ -3797,14 +4261,17 @@
 The `IncludedInInfo` entity contains information about the branches a
 change was merged into and tags it was tagged with.
 
-[options="header",cols="1,6"]
-|==========================
-|Field Name |Description
-|`branches` | The list of branches this change was merged into.
+[options="header",cols="1,^1,5"]
+|=======================
+|Field Name||Description
+|`branches`||The list of branches this change was merged into.
 Each branch is listed without the 'refs/head/' prefix.
-|`tags`     | The list of tags this change was tagged with.
+|`tags`    ||The list of tags this change was tagged with.
 Each tag is listed without the 'refs/tags/' prefix.
-|==========================
+|`external`|optional|A map that maps a name to a list of external
+systems that include this change, e.g. a list of servers on which this
+change is deployed.
+|=======================
 
 [[label-info]]
 === LabelInfo
@@ -3901,6 +4368,23 @@
 outcome of the fix.
 |===========================
 
+[[push-certificate-info]]
+=== PushCertificateInfo
+The `PushCertificateInfo` entity contains information about a push
+certificate provided when the user pushed for review with `git push
+--signed HEAD:refs/for/<branch>`. Only used when signed push is
+link:config-gerrit.html#receive.enableSignedPush[enabled] on the server.
+
+[options="header",cols="1,6"]
+|===========================
+|Field Name|Description
+|`certificate`|Signed certificate payload and GPG signature block.
+|`key`        |
+Information about the key that signed the push, along with any problems
+found while checking the signature or the key itself, as a
+link:rest-api-accounts.html#gpg-key-info[GpgKeyInfo] entity.
+|===========================
+
 [[rebase-input]]
 === RebaseInput
 The `RebaseInput` entity contains information for changing parent when rebasing.
@@ -3931,6 +4415,8 @@
 |`_change_number`          |optional|The change number.
 |`_revision_number`        |optional|The revision number.
 |`_current_revision_number`|optional|The current revision number.
+|`status`                  |optional|The status of the change. The status of
+the change is one of (`NEW`, `MERGED`, `ABANDONED`, `DRAFT`).
 |===========================
 
 [[related-changes-info]]
@@ -3990,34 +4476,38 @@
 
 [options="header",cols="1,^1,5"]
 |============================
-|Field Name     ||Description
-|`message`      |optional|
+|Field Name               ||Description
+|`message`                |optional|
 The message to be added as review comment.
-|`labels`       |optional|
+|`labels`                 |optional|
 The votes that should be added to the revision as a map that maps the
 label names to the voting values.
-|`comments`     |optional|
+|`comments`               |optional|
 The comments that should be added as a map that maps a file path to a
 list of link:#comment-input[CommentInput] entities.
-|`strict_labels`|`true` if not set|
+|`strict_labels`          |`true` if not set|
 Whether all labels are required to be within the user's permitted ranges
 based on access controls. +
 If `true`, attempting to use a label not granted to the user will fail
 the entire modify operation early. +
 If `false`, the operation will execute anyway, but the proposed labels
 will be modified to be the "best" value allowed by the access controls.
-|`drafts`      |optional|
+|`drafts`                 |optional|
 Draft handling that defines how draft comments are handled that are
 already in the database but that were not also described in this
 input. +
-Allowed values are `DELETE`, `PUBLISH` and `KEEP`. +
+Allowed values are `DELETE`, `PUBLISH`, `PUBLISH_ALL_REVISIONS` and
+`KEEP`. All values except `PUBLISH_ALL_REVISIONS` operate only on drafts
+for a single revision. +
 If not set, the default is `DELETE`.
-|`notify`      |optional|
+|`notify`                 |optional|
 Notify handling that defines to whom email notifications should be sent
 after the review is stored. +
 Allowed values are `NONE`, `OWNER`, `OWNER_REVIEWERS` and `ALL`. +
 If not set, the default is `ALL`.
-|`on_behalf_of`|optional|
+|`omit_duplicate_comments`|optional|
+If `true`, comments with the same content at the same place will be omitted.
+|`on_behalf_of`           |optional|
 link:rest-api-accounts.html#account-id[\{account-id\}] the review
 should be posted on behalf of. To use this option the caller must
 have been granted `labelAs-NAME` permission for all keys of labels.
@@ -4073,9 +4563,6 @@
 |===========================
 |Field Name    ||Description
 |`draft`       |not set if `false`|Whether the patch set is a draft.
-|`has_draft_comments`       |not set if `false`|Whether the patch
-set has one or more draft comments by the calling user. Only set if
-link:#draft_comments[DRAFT_COMMENTS] option is requested.
 |`_number`     ||The patch set number.
 |`created`     ||
 The link:rest-api.html#timestamp[timestamp] of when the patch set was
@@ -4105,6 +4592,18 @@
 |`reviewed`     |optional|
 Indicates whether the caller is authenticated and has commented on the
 current revision. Only set if link:#reviewed[REVIEWED] option is requested.
+|`messageWithFooter` |optional|
+If the link:#commit-footers[COMMIT_FOOTERS] option is requested and
+this is the current patch set, contains the full commit message with
+Gerrit-specific commit footers, as if this revision were submitted
+using the link:project-configuration.html#cherry_pick[Cherry Pick]
+submit type.
+|`push_certificate` |optional|
+If the link:#push-certificates[PUSH_CERTIFICATES] option is requested,
+contains the push certificate provided by the user when uploading this
+patch set as a link:#push-certificate-info[PushCertificateInfo] entity.
+This field is always set if the option is requested; if no push
+certificate was provided, it is set to an empty object.
 |===========================
 
 [[rule-input]]
@@ -4129,15 +4628,15 @@
 The `SubmitInfo` entity contains information about the change status
 after submitting.
 
-[options="header",cols="1,6"]
+[options="header",cols="1,^1,5"]
 |==========================
-|Field Name    |Description
-|`status`      |
-The status of the change after submitting, can be `MERGED` or
-`SUBMITTED`. +
-If `wait_for_merge` in the link:#submit-input[SubmitInput] was set to
-`false` the returned status is `SUBMITTED` and the caller can't know
-whether the change could be merged successfully.
+|Field Name    ||Description
+|`status`      ||
+The status of the change after submitting is `MERGED`.
++
+As `wait_for_merge` in the link:#submit-input[SubmitInput] is deprecated and
+the request always waits for the merge to be completed, you can expect
+`MERGED` to be returned here.
 |`on_behalf_of`|optional|
 The link:rest-api-accounts.html#account-id[\{account-id\}] of the user on
 whose behalf the action should be done. To use this option the caller must
@@ -4156,11 +4655,8 @@
 [options="header",cols="1,^1,5"]
 |===========================
 |Field Name      ||Description
-|`wait_for_merge`|`false` if not set|
-Whether the request should wait for the merge to complete. +
-If `false` the request returns immediately after the change has been
-added to the merge queue and the caller can't know whether the change
-could be merged successfully.
+|`wait_for_merge`|Deprecated, always `true`|
+Whether the request should wait for the merge to complete.
 |===========================
 
 [[submit-record]]
diff --git a/Documentation/rest-api-config.txt b/Documentation/rest-api-config.txt
index 0ee6966..b1b795c 100644
--- a/Documentation/rest-api-config.txt
+++ b/Documentation/rest-api-config.txt
@@ -30,6 +30,129 @@
   "2.7"
 ----
 
+[[get-info]]
+=== Get Server Info
+--
+'GET /config/server/info'
+--
+
+Returns the information about the Gerrit server configuration.
+
+.Request
+----
+  GET /config/server/info HTTP/1.0
+----
+
+As result a link:#server-info[ServerInfo] entity is returned.
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  {
+    "auth": {
+      "auth_type": "LDAP",
+      "editable_account_fields": [
+        "FULL_NAME",
+        "REGISTER_NEW_EMAIL"
+      ]
+    },
+    "download": {
+      "schemes": {
+        "anonymous http": {
+          "url": "http://gerrithost:8080/${project}",
+          "commands": {
+            "Checkout": "git fetch http://gerrithost:8080/${project} ${ref} \u0026\u0026 git checkout FETCH_HEAD",
+            "Format Patch": "git fetch http://gerrithost:8080/${project} ${ref} \u0026\u0026 git format-patch -1 --stdout FETCH_HEAD",
+            "Pull": "git pull http://gerrithost:8080/${project} ${ref}",
+            "Cherry Pick": "git fetch http://gerrithost:8080/${project} ${ref} \u0026\u0026 git cherry-pick FETCH_HEAD"
+          },
+          "clone_commands": {
+            "Clone": "git clone http://gerrithost:8080/${project}",
+            "Clone with commit-msg hook": "git clone http://gerrithost:8080/${project} \u0026\u0026 scp -p -P 29418 jdoe@gerrithost:hooks/commit-msg ${project}/.git/hooks/"
+          }
+        },
+        "http": {
+          "url": "http://jdoe@gerrithost:8080/${project}",
+          "is_auth_required": true,
+          "is_auth_supported": true,
+          "commands": {
+            "Checkout": "git fetch http://jdoe@gerrithost:8080/${project} ${ref} \u0026\u0026 git checkout FETCH_HEAD",
+            "Format Patch": "git fetch http://jdoe@gerrithost:8080/${project} ${ref} \u0026\u0026 git format-patch -1 --stdout FETCH_HEAD",
+            "Pull": "git pull http://jdoe@gerrithost:8080/${project} ${ref}",
+            "Cherry Pick": "git fetch http://jdoe@gerrithost:8080/${project} ${ref} \u0026\u0026 git cherry-pick FETCH_HEAD"
+          },
+          "clone_commands": {
+            "Clone": "git clone http://jdoe@gerrithost:8080/${project}",
+            "Clone with commit-msg hook": "git clone http://jdoe@gerrithost:8080/${project} \u0026\u0026 scp -p -P 29418 jdoe@gerrithost:hooks/commit-msg ${project}/.git/hooks/"
+          }
+        },
+        "ssh": {
+          "url": "ssh://jdoe@gerrithost:29418/${project}",
+          "is_auth_required": true,
+          "is_auth_supported": true,
+          "commands": {
+            "Checkout": "git fetch ssh://jdoe@gerrithost:29418/${project} ${ref} \u0026\u0026 git checkout FETCH_HEAD",
+            "Format Patch": "git fetch ssh://jdoe@gerrithost:29418/${project} ${ref} \u0026\u0026 git format-patch -1 --stdout FETCH_HEAD",
+            "Pull": "git pull ssh://jdoe@gerrithost:29418/${project} ${ref}",
+            "Cherry Pick": "git fetch ssh://jdoe@gerrithost:29418/${project} ${ref} \u0026\u0026 git cherry-pick FETCH_HEAD"
+          },
+          "clone_commands": {
+            "Clone": "git clone ssh://jdoe@gerrithost:29418/${project}",
+            "Clone with commit-msg hook": "git clone ssh://jdoe@gerrithost:29418/${project} \u0026\u0026 scp -p -P 29418 jdoe@gerrithost:hooks/commit-msg ${project}/.git/hooks/"
+          }
+        }
+      },
+      "archives": [
+        "tgz",
+        "tar",
+        "tbz2",
+        "txz"
+      ]
+    },
+    "gerrit": {
+      "all_projects": "All-Projects",
+      "all_users": "All-Users"
+    },
+    "sshd": {},
+    "suggest": {
+      "from": 0
+    },
+    "user": {
+      "anonymous_coward_name": "Anonymous Coward"
+    }
+  }
+----
+
+[[confirm-email]]
+=== Confirm Email
+--
+'PUT /config/server/email.confirm'
+--
+
+Confirms that the user owns an email address.
+
+The email token must be provided in the request body inside
+an link:#email-confirmation-input[EmailConfirmationInput] entity.
+
+.Request
+----
+  PUT /config/server/email.confirm HTTP/1.0
+  Content-Type: application/json; charset=UTF-8
+
+  {
+    "token": "Enim+QNbAo6TV8Hur8WwoUypI6apG7qBPvF+bw==$MTAwMDAwNDp0ZXN0QHRlc3QuZGU="
+  }
+----
+
+The response is "`204 No Content`".
+
+If the token is invalid or if it's the token of another user the
+request fails and the response is "`422 Unprocessable Entity`".
+
+
 [[list-caches]]
 === List Caches
 --
@@ -38,10 +161,12 @@
 
 Lists the caches of the server. Caches defined by plugins are included.
 
-The caller must be a member of a group that is granted the
-link:access-control.html#capability_viewCaches[View Caches] capability
-or the link:access-control.html#capability_administrateServer[
-Administrate Server] capability.
+The caller must be a member of a group that is granted one of the
+following capabilities:
+
+* link:access-control.html#capability_viewCaches[View Caches]
+* link:access-control.html#capability_maintainServer[Maintain Server]
+* link:access-control.html#capability_administrateServer[Administrate Server]
 
 As result a map of link:#cache-info[CacheInfo] entities is returned.
 
@@ -396,10 +521,12 @@
 
 Retrieves information about a cache.
 
-The caller must be a member of a group that is granted the
-link:access-control.html#capability_viewCaches[View Caches] capability
-or the link:access-control.html#capability_administrateServer[
-Administrate Server] capability.
+The caller must be a member of a group that is granted one of the
+following capabilities:
+
+* link:access-control.html#capability_viewCaches[View Caches]
+* link:access-control.html#capability_maintainServer[Maintain Server]
+* link:access-control.html#capability_administrateServer[Administrate Server]
 
 As result a link:#cache-info[CacheInfo] entity is returned.
 
@@ -435,15 +562,15 @@
 
 Flushes a cache.
 
-The caller must be a member of a group that is granted the
-link:access-control.html#capability_flushCaches[Flush Caches] capability
-or the link:access-control.html#capability_administrateServer[
-Administrate Server] capability.
+The caller must be a member of a group that is granted one of the
+following capabilities:
 
-The "web_sessions" cache can only be flushed if the caller is member of
-a group that is granted the
-link:access-control.html#capability_administrateServer[Administrate
-Server] capability.
+* link:access-control.html#capability_flushCaches[Flush Caches] (any cache
+  except "web_sessions")
+* link:access-control.html#capability_maintainServer[Maintain Server] (any cache
+  including "web_sessions")
+* link:access-control.html#capability_administrateServer[Administrate Server]
+  (any cache including "web_sessions")
 
 .Request
 ----
@@ -652,10 +779,12 @@
 is associated with. Tasks operating on other projects, or that do not
 have a specific project, are hidden.
 
-Members of a group that is granted the
-link:access-control.html#capability_viewQueue[View Queue] capability or
-the link:access-control.html#capability_administrateServer[Administrate
-Server] capability can see all tasks.
+The caller must be a member of a group that is granted one of the
+following capabilities:
+
+* link:access-control.html#capability_viewQueue[View Queue]
+* link:access-control.html#capability_maintainServer[Maintain Server]
+* link:access-control.html#capability_administrateServer[Administrate Server]
 
 As result a list of link:#task-info[TaskInfo] entities is returned.
 
@@ -704,10 +833,12 @@
 is associated with. Tasks operating on other projects, or that do not
 have a specific project, are hidden.
 
-Members of a group that is granted the
-link:access-control.html#capability_viewQueue[View Queue] capability or
-the link:access-control.html#capability_administrateServer[Administrate
-Server] capability can see all tasks.
+The caller must be a member of a group that is granted one of the
+following capabilities:
+
+* link:access-control.html#capability_viewQueue[View Queue]
+* link:access-control.html#capability_maintainServer[Maintain Server]
+* link:access-control.html#capability_administrateServer[Administrate Server]
 
 As result a link:#task-info[TaskInfo] entity is returned.
 
@@ -740,19 +871,23 @@
 Kills a task from the background work queue that the Gerrit daemon
 is currently performing, or will perform in the near future.
 
-The caller must be a member of a group that is granted the
-link:access-control.html#capability_kill[Kill Task] capability
-or the link:access-control.html#capability_administrateServer[
-Administrate Server] capability.
+The caller must be a member of a group that is granted one of the
+following capabilities:
+
+* link:access-control.html#capability_kill[Kill Task]
+* link:access-control.html#capability_maintainServer[Maintain Server]
+* link:access-control.html#capability_administrateServer[Administrate Server]
 
 End-users may see a task only if they can also see the project the task
 is associated with. Tasks operating on other projects, or that do not
 have a specific project, are hidden.
 
-Members of a group that is granted the
-link:access-control.html#capability_viewQueue[View Queue] capability or
-the link:access-control.html#capability_administrateServer[Administrate
-Server] capability can see all tasks.
+Members of a group granted one of the following capabilities may view
+all tasks:
+
+* link:access-control.html#capability_viewQueue[View Queue]
+* link:access-control.html#capability_maintainServer[Maintain Server]
+* link:access-control.html#capability_administrateServer[Administrate Server]
 
 .Request
 ----
@@ -822,6 +957,59 @@
 [[json-entities]]
 == JSON Entities
 
+[[auth-info]]
+=== AuthInfo
+The `AuthInfo` entity contains information about the authentication
+configuration of the Gerrit server.
+
+[options="header",cols="1,^1,5"]
+|==========================================
+|Field Name                   ||Description
+|`type`                       ||
+The link:config-gerrit.html#auth.type[authentication type] that is
+configured on the server. Can be `OPENID`, `OPENID_SSO`, `OAUTH`,
+`HTTP`, `HTTP_LDAP`, `CLIENT_SSL_CERT_LDAP`, `LDAP`, `LDAP_BIND`,
+`CUSTOM_EXTENSION` or `DEVELOPMENT_BECOME_ANY_ACCOUNT`.
+|`use_contributor_agreements` |not set if `false`|
+Whether link:config-gerrit.html#auth.contributorAgreements[contributor
+agreements] are required.
+|`editable_account_fields`    ||
+List of account fields that are editable. Possible values are
+`FULL_NAME`, `USER_NAME` and `REGISTER_NEW_EMAIL`.
+|`login_url`                  |optional|
+The link:config-gerrit.html#auth.loginUrl[login URL]. Only set if
+link:config-gerrit.html#auth.type[authentication type] is `HTTP` or
+`HTTP_LDAP`.
+|`login_text`                 |optional|
+The link:config-gerrit.html#auth.loginText[login text]. Only set if
+link:config-gerrit.html#auth.type[authentication type] is `HTTP` or
+`HTTP_LDAP`.
+|`switch_account_url`         |optional|
+The link:config-gerrit.html#auth.switchAccountUrl[URL to switch
+accounts].
+|`register_url`               |optional|
+The link:config-gerrit.html#auth.registerUrl[register URL]. Only set if
+link:config-gerrit.html#auth.type[authentication type] is `LDAP`,
+`LDAP_BIND` or `CUSTOM_EXTENSION`.
+|`register_text`              |optional|
+The link:config-gerrit.html#auth.registerText[register text]. Only set
+if link:config-gerrit.html#auth.type[authentication type] is `LDAP`,
+`LDAP_BIND` or `CUSTOM_EXTENSION`.
+|`edit_full_name_url`         |optional|
+The link:config-gerrit.html#auth.editFullNameUrl[URL to edit the full
+name]. Only set if link:config-gerrit.html#auth.type[authentication
+type] is `LDAP`, `LDAP_BIND` or `CUSTOM_EXTENSION`.
+|`http_password_url`          |optional|
+The link:config-gerrit.html#auth.httpPasswordUrl[URL to obtain an HTTP
+password]. Only set if link:config-gerrit.html#auth.type[authentication
+type] is `CUSTOM_EXTENSION`.
+|`is_git_basic_auth`          |optional, not set if `false`|
+Whether link:config-gerrit.html#auth.gitBasicAuth[basic authentication
+is used for Git over HTTP/HTTPS]. Only set if
+link:config-gerrit.html#auth.type[authentication type] is is `LDAP` or
+`LDAP_BIND`.
+|==========================================
+
 [[cache-info]]
 === CacheInfo
 The `CacheInfo` entity contains information about a cache.
@@ -878,6 +1066,98 @@
 |`name`               |capability name
 |=================================
 
+[[change-config-info]]
+=== ChangeConfigInfo
+The `ChangeConfigInfo` entity contains information about Gerrit
+configuration from the link:config-gerrit.html#change[change]
+section.
+
+[options="header",cols="1,^1,5"]
+|=============================
+|Field Name           ||Description
+|`allow_drafts`       |not set if `false`|
+link:config-gerrit.html#change.allowDrafts[Whether draft workflow is
+allowed].
+|`large_change`       ||
+link:config-gerrit.html#change.largeChange[Number of changed lines from
+which on a change is considered as a large change].
+|`reply_label`        ||
+link:config-gerrit.html#change.replyTooltip[Label name for the reply
+button].
+|`reply_tooltip`      ||
+link:config-gerrit.html#change.replyTooltip[Tooltip for the reply
+button].
+|`update_delay`       ||
+link:config-gerrit.html#change.updateDelay[How often in seconds the web
+interface should poll for updates to the currently open change].
+|`submit_whole_topic` ||
+link:config-gerrit.html#change.submitWholeTopic[A configuration if
+the whole topic is submitted].
+|=============================
+
+[[download-info]]
+=== DownloadInfo
+The `DownloadInfo` entity contains information about supported download
+options.
+
+[options="header",cols="1,6"]
+|=======================
+|Field Name |Description
+|`schemes`  |
+The supported download schemes as a map which maps the scheme name to a
+of link:#download-scheme-info[DownloadSchemeInfo] entity.
+|`archives` |
+List of supported archive formats. Possible values are `tgz`, `tar`,
+`tbz2` and `txz`.
+|=======================
+
+[[download-scheme-info]]
+=== DownloadSchemeInfo
+The `DownloadSchemeInfo` entity contains information about a supported
+download scheme and its commands.
+
+[options="header",cols="1,^1,5"]
+|=================================
+|Field Name          ||Description
+|`url`               ||
+The URL of the download scheme, where '${project}' is used as
+placeholder for the project name.
+|`is_auth_required`  |not set if `false`|
+Whether this download scheme requires authentication.
+|`is_auth_supported` |not set if `false`|
+Whether this download scheme supports authentication.
+|`commands`          ||
+Download commands as a map which maps the command name to the download
+command. In the download command '${project}' is used as
+placeholder for the project name, and '${ref}' is used as
+placeholder for the (change) ref.
+
+Empty, if accessed anonymously and the download scheme requires
+authentication.
+|`clone_commands`    ||
+Clone commands as a map which maps the command name to the clone
+command. In the clone command '${project}' is used as
+placeholder for the project name and '${project-base-name}' as name
+for the project base name (e.g. for a project 'foo/bar' '${project}'
+is a placeholder for 'foo/bar' and '${project-base-name}' is a
+placeholder for 'bar').
+
+Empty, if accessed anonymously and the download scheme requires
+authentication.
+|=================================
+
+[[email-confirmation-input]]
+=== EmailConfirmationInput
+The `EmailConfirmationInput` entity contains information for confirming
+an email address.
+
+[options="header",cols="1,6"]
+|=======================
+|Field Name |Description
+|`token`    |
+The token that was sent by mail to a newly registered email address.
+|=======================
+
 [[entries-info]]
 === EntriesInfo
 The `EntriesInfo` entity contains information about the entries in a
@@ -896,6 +1176,80 @@
 `g`: gigabytes). Only set for disk caches.
 |==================================
 
+[[gerrit-info]]
+=== GerritInfo
+The `GerritInfo` entity contains information about Gerrit
+configuration from the link:config-gerrit.html#gerrit[gerrit] section.
+
+[options="header",cols="1,^1,5"]
+|=================================
+|Field Name          ||Description
+|`all_projects_name` ||
+Name of the link:config-gerrit.html#gerrit.allProjects[root project].
+|`all_users_name`    ||
+Name of the link:config-gerrit.html#gerrit.allUsers[project in which
+meta data of all users is stored].
+|`doc_url`           |optional|
+Custom base URL where Gerrit server documentation is located.
+(Documentation may still be available at /Documentation relative to the
+Gerrit base path even if this value is unset.)
+|`edit_gpg_keys`     |not set if `false`|
+Whether to enable the web UI for editing GPG keys.
+|`report_bug_url`    |optional|
+link:config-gerrit.html#gerrit.reportBugUrl[URL to report bugs].
+|`report_bug_text`   |optional, not set if default|
+link:config-gerrit.html#gerrit.reportBugText[Display text for report
+bugs link].
+|=================================
+
+[[git-web-info]]
+=== GitwebInfo
+The `GitwebInfo` entity contains information about the
+link:config-gerrit.html#gitweb[gitweb] configuration.
+
+[options="header",cols="1,6"]
+|=======================
+|Field Name |Description
+|`url`      |
+The link:config-gerrit.html#gitweb.url[gitweb base URL].
+|`type`     |
+The link:config-gerrit.html#gitweb.type[gitweb type] as
+link:#git-web-type-info[GitwebTypeInfo] entity.
+|=======================
+
+[[git-web-type-info]]
+=== GitwebTypeInfo
+The `GitwebTypeInfo` entity contains information about the
+link:config-gerrit.html#gitweb[gitweb] configuration.
+
+[options="header",cols="1,^1,5"]
+|=============================
+|Field Name      ||Description
+|`name`          ||
+The link:config-gerrit.html#gitweb.linkname[gitweb link name].
+|`revision`      |optional|
+The link:config-gerrit.html#gitweb.revision[gitweb revision pattern].
+|`project`       |optional|
+The link:config-gerrit.html#gitweb.project[gitweb project pattern].
+|`branch`        |optional|
+The link:config-gerrit.html#gitweb.branch[gitweb branch pattern].
+|`root_tree`     |optional|
+The link:config-gerrit.html#gitweb.roottree[gitweb root tree pattern].
+|`file`          |optional|
+The link:config-gerrit.html#gitweb.file[gitweb file pattern].
+|`file_history`  |optional|
+The link:config-gerrit.html#gitweb.filehistory[gitweb file history
+pattern].
+|`path_separator`||
+The link:config-gerrit.html#gitweb.pathSeparator[gitweb path separator].
+|`link_drafts`   |optional|
+link:config-gerrit.html#gitweb.linkDrafts[Whether Gerrit should provide
+links to gitweb on draft patch set.]
+|`url_encode`    |optional|
+link:config-gerrit.html#gitweb.urlEncode[Whether Gerrit should encode
+the generated viewer URL.]
+|=============================
+
 [[hit-ration-info]]
 === HitRatioInfo
 The `HitRatioInfo` entity contains information about the hit ratio of a
@@ -958,6 +1312,106 @@
 The number of open files.
 |============================
 
+[[plugin-config-info]]
+=== PluginConfigInfo
+The `PluginConfigInfo` entity contains information about Gerrit
+extensions by plugins.
+
+[options="header",cols="1,^1,5"]
+|===========================
+|Field Name    ||Description
+|`has_avatars` |not set if `false`|
+Whether an avatar provider is registered.
+|===========================
+
+[[receive-info]]
+=== ReceiveInfo
+The `ReceiveInfo` entity contains information about the configuration
+of git-receive-pack behavior on the server.
+
+[options="header",cols="1,^1,5"]
+|=======================================
+|Field Name        ||Description
+|`enableSignedPush`|optional|
+Whether signed push validation support is enabled on the server; see the
+link:config-gerrit.html#receive.certNonceSeed[global configuration] for
+details.
+|=======================================
+
+[[server-info]]
+=== ServerInfo
+The `ServerInfo` entity contains information about the configuration of
+the Gerrit server.
+
+[options="header",cols="1,^1,5"]
+|=======================================
+|Field Name                ||Description
+|`auth`                    ||
+Information about the authentication configuration as
+link:#auth-info[AuthInfo] entity.
+|`change`                  ||
+Information about the configuration from the
+link:config-gerrit.html#change[change] section as
+link:#change-config-info[ChangeConfigInfo] entity.
+|`download`                ||
+Information about the configured download options as
+link:#download-info[DownloadInfo] entity.
+information about Gerrit
+|`gerrit`                  ||
+Information about the configuration from the
+link:config-gerrit.html#gerrit[gerrit] section as link:#gerrit-info[
+GerritInfo] entity.
+|`gitweb `                 |optional|
+Information about the link:config-gerrit.html#gitweb[gitweb]
+|`plugin `                 ||
+Information about Gerrit extensions by plugins as
+link:#plugin-config-info[PluginConfigInfo] entity.
+|`receive`                 |optional|
+Information about the receive-pack configuration as a
+link:#receive-info[ReceiveInfo] entity.
+|`sshd`                    |optional|
+Information about the configuration from the
+link:config-gerrit.html#sshd[sshd] section as link:#sshd-info[SshdInfo]
+entity. Not set if SSHD is disabled.
+|`suggest`                 ||
+Information about the configuration from the
+link:config-gerrit.html#suggest[suggest] section as link:#suggest-info[
+SuggestInfo] entity.
+|`url_aliases`             |optional|
+A map of URL aliases, where a regular expression for an URL token is
+mapped to a target URL token. The target URL token can contain
+placeholders for the groups matched by the regular expression: `$1` for
+the first matched group, `$2` for the second matched group, etc.
+|`user`                    ||
+Information about the configuration from the
+link:config-gerrit.html#user[user] section as link:#user-config-info[
+UserConfigInfo] entity.
+|=======================================
+
+[[sshd-info]]
+=== SshdInfo
+The `SshdInfo` entity contains information about Gerrit
+configuration from the link:config-gerrit.html#sshd[sshd]
+section.
+
+This entity doesn't contain any data, but the presence of this (empty)
+entity in the link:#server-info[ServerInfo] entity means that SSHD is
+enabled on the server.
+
+[[suggest-info]]
+=== SuggestInfo
+The `SuggestInfo` entity contains information about Gerrit
+configuration from the link:config-gerrit.html#suggest[suggest]
+section.
+
+[options="header",cols="1,6"]
+|=======================
+|Field Name |Description
+|`from`     |
+The link:config-gerrit.html#suggest.from[number of characters] that a
+user must have typed before suggestions are provided.
+|=======================
+
 [[summary-info]]
 === SummaryInfo
 The `SummaryInfo` entity contains information about the current state
@@ -1069,6 +1523,21 @@
 |`id`       |optional|The `id` attribute of the menu item link.
 |========================
 
+[[user-config-info]]
+=== UserConfigInfo
+The `UserConfigInfo` entity contains information about Gerrit
+configuration from the link:config-gerrit.html#user[user] section.
+
+[options="header",cols="1,6"]
+|====================================
+|Field Name              |Description
+|`anonymous_coward_name` |
+link:config-gerrit.html#user.anonymousCoward[Username] that is
+displayed in the Gerrit Web UI and in e-mail notifications if the full
+name of the user is not set.
+|====================================
+
+
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/rest-api-groups.txt b/Documentation/rest-api-groups.txt
index fbd3ba5..e0df4ca 100644
--- a/Documentation/rest-api-groups.txt
+++ b/Documentation/rest-api-groups.txt
@@ -172,6 +172,46 @@
   GET /groups/?n=25&S=50 HTTP/1.0
 ----
 
+[[suggest-group]]
+==== Suggest Group
+The `suggest` option indicates a user-entered string that
+should be auto-completed to group names.
+If this option is set and `n` is not set, then `n` defaults to 10.
+
+When using this option,
+the `project` or `p` option can be used to name the current project,
+to allow context-dependent suggestions.
+
+Not compatible with `visible-to-all`, `owned`, `user`, `match`, `q`,
+or `S`.
+(Attempts to use one of those options combined with `suggest` will
+error out.)
+
+.Request
+----
+  GET /groups/?suggest=ad&p=All-Projects HTTP/1.0
+----
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  {
+    "Administrators": {
+      "url": "#/admin/groups/uuid-59b92f35489e62c80d1ab1bf0c2d17843038df8b",
+      "options": {},
+      "description": "Gerrit Site Administrators",
+      "group_id": 1,
+      "owner": "Administrators",
+      "owner_id": "59b92f35489e62c80d1ab1bf0c2d17843038df8b",
+      "id": "59b92f35489e62c80d1ab1bf0c2d17843038df8b"
+    }
+  }
+----
+
 [[get-group]]
 === Get Group
 --
@@ -592,6 +632,89 @@
   }
 ----
 
+[[get-audit-log]]
+=== Get Audit Log
+--
+'GET /groups/link:#group-id[\{group-id\}]/log.audit'
+--
+
+Gets the audit log of a Gerrit internal group.
+
+.Request
+----
+  GET /groups/9999c971bb4ab872aab759d8c49833ee6b9ff320/log.audit HTTP/1.0
+----
+
+As response a list of link:#group-audit-event-info[GroupAuditEventInfo]
+entities is returned that describes the audit events of the group. The
+returned audit events are sorted by date in reverse order so that the
+newest audit event comes first.
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  [
+    {
+      "member": {
+        "url": "#/admin/groups/uuid-fdda826a0815859ab48d22a05a43472f0f55f89a",
+        "options": {},
+        "group_id": 3,
+        "owner": "Administrators",
+        "owner_id": "e56678641565e7f59dd5c6878f5bcbc842bf150a",
+        "id": "fdda826a0815859ab48d22a05a43472f0f55f89a",
+        "name": "MyGroup"
+      },
+      "type": "REMOVE_GROUP",
+      "user": {
+        "_account_id": 1000000,
+        "name": "Administrator",
+        "email": "admin@example.com",
+        "username": "admin"
+      },
+      "date": "2015-07-03 09:22:26.348000000"
+    },
+    {
+      "member": {
+        "url": "#/admin/groups/uuid-fdda826a0815859ab48d22a05a43472f0f55f89a",
+        "options": {},
+        "group_id": 3,
+        "owner": "Administrators",
+        "owner_id": "e56678641565e7f59dd5c6878f5bcbc842bf150a",
+        "id": "fdda826a0815859ab48d22a05a43472f0f55f89a",
+        "name": "MyGroup"
+      },
+      "type": "ADD_GROUP",
+      "user": {
+        "_account_id": 1000000,
+        "name": "Administrator",
+        "email": "admin@example.com",
+        "username": "admin"
+      },
+      "date": "2015-07-03 08:43:36.592000000"
+    },
+    {
+      "member": {
+        "_account_id": 1000000,
+        "name": "Administrator",
+        "email": "admin@example.com",
+        "username": "admin"
+      },
+      "type": "ADD_USER",
+      "user": {
+        "_account_id": 1000001,
+        "name": "John Doe",
+        "email": "john.doe@example.com",
+        "username": "jdoe"
+      },
+      "date": "2015-07-01 13:36:36.602000000"
+    }
+  ]
+----
+
 [[group-member-endpoints]]
 == Group Member Endpoints
 
@@ -1108,6 +1231,38 @@
 [[json-entities]]
 == JSON Entities
 
+[[group-audit-event-info]]
+=== GroupAuditEventInfo
+The `GroupAuditEventInfo` entity contains information about an audit
+event of a group.
+
+[options="header",cols="1,6"]
+|======================
+|Field Name|Description
+|`member`  |
+The group member that is added/removed. If `type` is `ADD_USER` or
+`REMOVE_USER` the member is returned as detailed
+link:rest-api-accounts.html#account-info[AccountInfo] entity, if `type`
+is `ADD_GROUP` or `REMOVE_GROUP` the member is returned as
+link:#group-info[GroupInfo] entity.
+|`type`    |
+The event type, can be: `ADD_USER`, `REMOVE_USER`, `ADD_GROUP` or
+`REMOVE_GROUP`.
+
+`ADD_USER`: A user was added as member to the group.
+
+`REMOVE_USER`: A user member was removed from the group.
+
+`ADD_GROUP`: A group was included as member in the group.
+
+`REMOVE_GROUP`: An included group was removed from the group.
+|`user`    |
+The user that did the add/remove as detailed
+link:rest-api-accounts.html#account-info[AccountInfo] entity.
+|`date`    |
+The timestamp of the event.
+|======================
+
 [[group-info]]
 === GroupInfo
 The `GroupInfo` entity contains information about a group. This can be
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index 3baaaa8..986ccd8 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -731,6 +731,8 @@
     "use_content_merge": "INHERIT",
     "use_signed_off_by": "INHERIT",
     "create_new_change_for_all_not_in_target": "INHERIT",
+    "enable_signed_push": "INHERIT",
+    "require_signed_push": "INHERIT",
     "require_change_id": "TRUE",
     "max_object_size_limit": "10m",
     "submit_type": "REBASE_IF_NECESSARY",
@@ -774,6 +776,16 @@
       "configured_value": "TRUE",
       "inherited_value": true
     },
+    "enable_signed_push": {
+      "value": true,
+      "configured_value": "INHERIT",
+      "inherited_value": false
+    },
+    "require_signed_push": {
+      "value": false,
+      "configured_value": "INHERIT",
+      "inherited_value": false
+    },
     "max_object_size_limit": {
       "value": "10m",
       "configured_value": "10m",
@@ -1447,6 +1459,80 @@
   ]
 ----
 
+[[tag-options]]
+==== Tag Options
+
+Limit(n)::
+Limit the number of tags to be included in the results.
++
+.Request
+----
+  GET /projects/work%2Fmy-project/tags?n=2 HTTP/1.0
+----
++
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  [
+    {
+      "ref": "refs/tags/v1.0",
+      "revision": "49ce77fdcfd3398dc0dedbe016d1a425fd52d666",
+      "object": "1624f5af8ae89148d1a3730df8c290413e3dcf30",
+      "message": "Annotated tag",
+      "tagger": {
+        "name": "David Pursehouse",
+        "email": "david.pursehouse@sonymobile.com",
+        "date": "2014-10-06 07:35:03.000000000",
+        "tz": 540
+      }
+    },
+    {
+      "ref": "refs/tags/v2.0",
+      "revision": "1624f5af8ae89148d1a3730df8c290413e3dcf30"
+    }
+  ]
+----
+
+Skip(s)::
+Skip the given number of tags from the beginning of the list.
++
+.Request
+----
+  GET /projects/work%2Fmy-project/tags?n=2&s=1 HTTP/1.0
+----
++
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  [
+    {
+      "ref": "refs/tags/v2.0",
+      "revision": "1624f5af8ae89148d1a3730df8c290413e3dcf30"
+    },
+    {
+      "ref": "refs/tags/v3.0",
+      "revision": "c628685b3c5a3614571ecb5c8fceb85db9112313",
+      "object": "1624f5af8ae89148d1a3730df8c290413e3dcf30",
+      "message": "Signed tag\n-----BEGIN PGP SIGNATURE-----\nVersion: GnuPG v1.4.11 (GNU/Linux)\n\niQEcBAABAgAGBQJUMlqYAAoJEPI2qVPgglptp7MH/j+KFcittFbxfSnZjUl8n5IZ\nveZo7wE+syjD9sUbMH4EGv0WYeVjphNTyViBof+stGTNkB0VQzLWI8+uWmOeiJ4a\nzj0LsbDOxodOEMI5tifo02L7r4Lzj++EbqtKv8IUq2kzYoQ2xjhKfFiGjeYOn008\n9PGnhNblEHZgCHguGR6GsfN8bfA2XNl9B5Ysl5ybX1kAVs/TuLZR4oDMZ/pW2S75\nhuyNnSgcgq7vl2gLGefuPs9lxkg5Fj3GZr7XPZk4pt/x1oiH7yXxV4UzrUwg2CC1\nfHrBlNbQ4uJNY8TFcwof52Z0cagM5Qb/ZSLglHbqEDGA68HPqbwf5z2hQyU2/U4\u003d\n\u003dZtUX\n-----END PGP SIGNATURE-----",
+      "tagger": {
+        "name": "David Pursehouse",
+        "email": "david.pursehouse@sonymobile.com",
+        "date": "2014-10-06 09:02:16.000000000",
+        "tz": 540
+      }
+    }
+  ]
+----
+
+
 [[get-tag]]
 === Get Tag
 --
@@ -1902,6 +1988,14 @@
 valid link:user-changeid.html[Change-Id] footer in any commit uploaded
 for review is required. This does not apply to commits pushed directly
 to a branch or tag.
+|`enable_signed_push`|
+optional, not set if signed push is disabled|
+link:#inherited-boolean-info[InheritedBooleanInfo] that tells whether
+signed push validation is enabled on the project.
+|`require_signed_push`|
+optional, not set if signed push is disabled
+link:#inherited-boolean-info[InheritedBooleanInfo] that tells whether
+signed push validation is required on the project.
 |`max_object_size_limit`     ||
 The link:config-gerrit.html#receive.maxObjectSizeLimit[max object size
 limit] of this project as a link:#max-object-size-limit-info[
@@ -2116,6 +2210,8 @@
 |Field Name      ||Description
 |`show_progress` |`false` if not set|
 Whether progress information should be shown.
+|`aggressive`    |`false` if not set|
+Whether an aggressive garbage collection should be done.
 |=============================
 
 [[head-input]]
diff --git a/Documentation/user-change-cleanup.txt b/Documentation/user-change-cleanup.txt
new file mode 100644
index 0000000..e569f38
--- /dev/null
+++ b/Documentation/user-change-cleanup.txt
@@ -0,0 +1,28 @@
+= Gerrit Code Review - Change Cleanup
+
+Gerrit administrators may configure
+link:config-gerrit.html#changeCleanup[change cleanups] that are
+executed periodically.
+
+[[auto-abandon]]
+== Auto-Abandon
+
+This cleanup job automatically abandons open changes that have been
+inactive for a defined time.
+
+Abandoning old inactive changes has the following advantages:
+
+* it signals change authors that changes are considered outdated
+* it keeps dashboards clean
+* it reduces the load on the server (for open changes the mergeability
+  flag is recomputed whenever a change is merged)
+
+If a change is still wanted it can be restored by clicking on the
+`Restore` button.
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/user-inline-edit.txt b/Documentation/user-inline-edit.txt
index 49821af..05932df 100644
--- a/Documentation/user-inline-edit.txt
+++ b/Documentation/user-inline-edit.txt
@@ -44,10 +44,9 @@
 While in edit mode, it is possible to add new files to the change by clicking
 the 'Add...' button at the top of the file list.
 
-Files can be removed from the change, or restored, by clicking the icon to the
-left of the file name. Reverting a file in the change is also supported and is
-achieved in two steps: remove file from the change and restore the file in the
-change.
+File changes can be reverted or files can be removed from the change or
+deleted files can be restored, by clicking the icons to the left of the file
+name.
 
 To switch from edit mode back to review mode, click the 'Done Editing' button.
 
@@ -147,13 +146,18 @@
 granted the link:access-control.html#capability_accessDatabase[accessDatabase]
 global capability are able to access change edit refs.
 
+[[search-for-change-edits]]
+
+To search change edits from the UI the link:user-search.html#has[has:edit]
+predicate can be used.
+
+Alternatively change edits can be accessed through "My => Edits" dashboard.
+
 [[not-implemented-features]]
 == Not Implemented Features
 
-* [PENDING CHANGE]
-The inline editor uses settings decided from the user's diff preferences, but those
-preferences are only modifiable from the side-by-side diff screen. It should be possible
-to open the preferences also from within the editor.
+* Support default configuration options for inline editor that an
+administrator has set in `refs/users/default:preferences.config` file.
 
 * Allow to rename files that are already contained in the change (from the file table).
 The same rename file dialog can be used with preselected and disabled original file
@@ -174,9 +178,6 @@
 ** "save-when-file-was-changed" or
 ** "close-when-no-changes"
 
-* Allow to activate different key maps, supported by CM: Emacs, Sublime, Vim. Load key
-maps dynamically. Currently default mode is used.
-
 * Implement conflict resolution during rebase of change edit using inline edit
 feature by creating new edit on top of current patch set with auto merge content
 
diff --git a/Documentation/user-named-destinations.txt b/Documentation/user-named-destinations.txt
new file mode 100644
index 0000000..1b6f143
--- /dev/null
+++ b/Documentation/user-named-destinations.txt
@@ -0,0 +1,32 @@
+= Gerrit Code Review - Named Destinations
+
+[[user-named-destinations]]
+== User Named Destinations
+It is possible to define named destination sets on a user level.
+To do this, define the named destination sets in files named after
+each destination set in the `destinations` directory of the user's
+account ref in the `All-Users` project.  The user's account ref is
+based on the user's account id which is an integer.  The account
+refs are sharded by the last two digits (`+nn+`) in the refname,
+leading to refs of the format `+refs/users/nn/accountid+`.  The
+user's destination files are a 2 column tab delimited file.  Each
+row in a destination file represents a single destination in the
+named set.  The left column represents the ref of the destination,
+and the right column represents the project of the destination.
+
+Example destination file named `destinations/myreviews`:
+
+----
+# Ref            	Project
+#
+refs/heads/master	gerrit
+refs/heads/stable-2.11	gerrit
+refs/heads/master	plugins/cookbook-plugin
+----
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/user-named-queries.txt b/Documentation/user-named-queries.txt
new file mode 100644
index 0000000..e79b3da
--- /dev/null
+++ b/Documentation/user-named-queries.txt
@@ -0,0 +1,28 @@
+= Gerrit Code Review - Named Queries
+
+[[user-named-queries]]
+== User Named Queries
+It is possible to define named queries on a user level. To do
+this, define the named queries in the `queries` file under the
+link:intro-user.html#user-refs[user's ref] in the `All-Users` project.  The
+user's queries file is a 2 column tab delimited file.  The left
+column represents the name of the query, and the right column
+represents the query expression represented by the name.
+
+Example queries file:
+
+----
+# Name         	Query
+#
+selfapproved   	owner:self label:code-review+2,user=self
+blocked        	label:code-review-2 OR label:verified-1
+# Note below how to reference your own named queries in other named queries
+ready          	label:code-review+2 label:verified+1 -query:blocked status:open
+----
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/user-review-ui.txt b/Documentation/user-review-ui.txt
index dddbadc..859765c 100644
--- a/Documentation/user-review-ui.txt
+++ b/Documentation/user-review-ui.txt
@@ -53,18 +53,6 @@
 +
 The change has all necessary approvals and may be submitted.
 
-- [[submitted-merge-pending]]`Submitted, Merge Pending`:
-+
-The change was submitted and was added to the merge queue.
-+
-The change stays in the merge queue if it depends on a change that is
-still in review. In this case it will get automatically merged when all
-dependency changes have been merged.
-+
-This status can also mean that the change depends on an abandoned
-change or on an outdated patch set of another change. In this case you
-may want to rebase the change.
-
 - [[merged]]`Merged`:
 +
 The change was successfully merged into the destination branch.
@@ -94,7 +82,7 @@
 displayed with a copy-to-clipboard icon that allows the ID to be copied
 into the clipboard.
 
-If a Git web browser, such as GitWeb or Gitiles, is configured, there
+If a Git web browser, such as gitweb or Gitiles, is configured, there
 is also a link to the commit in the Git web browser.
 
 image::images/user-review-ui-change-screen-commit-info.png[width=800, link="images/user-review-ui-change-screen-commit-info.png"]
@@ -198,10 +186,6 @@
 The `Submit` button is available if the change is submittable and
 the link:access-control.html#category_submit[Submit] access right is
 assigned.
-+
-It is also possible to submit changes that have merge conflicts. This
-allows to do the conflict resolution for a change series in a single
-merge commit and submit the changes in reverse order.
 
 ** [[abandon]]`Abandon`:
 +
@@ -408,9 +392,6 @@
 switch between them. The patch sets are sorted in descending order so
 that the current patch set is always on top.
 
-Patch sets that have unpublished draft comments are marked by a comment
-icon.
-
 Draft patch sets are marked with `DRAFT`.
 
 image::images/user-review-ui-change-screen-patch-set-list.png[width=800, link="images/user-review-ui-change-screen-patch-set-list.png"]
@@ -539,12 +520,18 @@
 
 ** [[closed-ancestor]]Black Dot:
 +
-Indicates a merged ancestor, e.g. the commit was directly pushed into
+Indicates a closed ancestor, e.g. the commit was directly pushed into
 the repository bypassing code review, or the ancestor change was
 reviewed and submitted on another branch. The latter may indicate that
 the user has accidentally pushed the commit to the wrong branch, e.g.
 the commit was done on `branch-a`, but was then pushed to
 `refs/for/branch-b`.
+A black dot is also present if the change was abandoned.
+
+** [[closed-ancestor-abandoned]]Strikethrough Subject:
++
+When the commit is abandoned, its subject line will be striked
+through.
 
 +
 image::images/user-review-ui-change-screen-related-changes-indicators.png[width=800, link="images/user-review-ui-change-screen-related-changes-indicators.png"]
@@ -568,6 +555,17 @@
 +
 image::images/user-review-ui-change-screen-same-topic.png[width=800, link="images/user-review-ui-change-screen-same-topic.png"]
 
+- [[submitted-together]]`Submitted Together`:
++
+This tab page shows changes that will be submitted together with the
+currently viewed change, when clicking the submit button. It includes
+ancestors of the current patch set.
++
+This may include changes and its ancestors with the same topic if
+`change.submitWholeTopic` is enabled. Only open changes with the
+same topic are included in the list.
++
+
 - [[cherry-picks]]`Cherry-Picks`:
 +
 This tab page shows changes with the same link:user-changeid.html[
@@ -1089,6 +1087,15 @@
 +
 Large files that exceed 4000 lines will not be fully rendered.
 
+- [[line-wrapping]]`Line Wrapping`:
++
+Controls weather to enable line wrapping or not.
++
+If `false` is selected then line wrapping is disabled.
+This is the default option.
++
+If `true` is selected then line wrapping is enabled.
+
 [[keyboard-shortcuts]]
 == Keyboard Shortcuts
 
diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt
index 3de45d2..b2b3614 100644
--- a/Documentation/user-search.txt
+++ b/Documentation/user-search.txt
@@ -88,6 +88,12 @@
 as a legacy numerical 'ID' such as 15183, or a newer style Change-Id
 that was scraped out of the commit message.
 
+[[destination]]
+destination:'NAME'::
++
+Changes which match the current user's destination named 'NAME'.
+(see link:user-named-destinations.html[Named Destinations]).
+
 [[owner]]
 owner:'USER', o:'USER'::
 +
@@ -99,6 +105,12 @@
 +
 Changes originally submitted by a user in 'GROUP'.
 
+[[query]]
+query:'NAME'::
++
+Changes which match the current user's query named 'NAME'
+(see link:user-named-queries.html[Named Queries]).
+
 [[reviewer]]
 reviewer:'USER', r:'USER'::
 +
@@ -147,18 +159,23 @@
 link:http://www.brics.dk/automaton/[dk.brics.automaton
 library] is used for evaluation of such patterns.
 
-[[topic]]
-topic:'TOPIC'::
+[[intopic]]
+intopic:'TOPIC'::
 +
-Changes whose designated topic at upload was 'TOPIC'.  This is
-often combined with 'branch:' and 'project:' operators to select
-all related changes in a series.
+Changes whose designated topic contains 'TOPIC', using a full-text search.
 +
 If 'TOPIC' starts with `^` it matches topic names by regular
 expression patterns.  The
 link:http://www.brics.dk/automaton/[dk.brics.automaton
 library] is used for evaluation of such patterns.
 
+[[topic]]
+topic:'TOPIC'::
++
+Changes whose designated topic matches 'TOPIC' exactly.  This is
+often combined with 'branch:' and 'project:' operators to select
+all related changes in a series.
+
 [[ref]]
 ref:'REF'::
 +
@@ -240,6 +257,10 @@
 Same as 'is:starred', true if the change has been starred by the
 current user.
 
+has:edit::
++
+True if the change has inline edit created by the current user.
+
 [[is]]
 is:starred::
 +
@@ -253,8 +274,8 @@
 
 is:reviewed::
 +
-True if there is at least one non-zero score on the change, in any
-approval category, by any user.
+True if any user has commented on the change more recently than the
+last update (comment or patch set) from the change owner.
 
 is:owner::
 +
@@ -268,7 +289,7 @@
 
 is:open, is:pending::
 +
-True if the change is either open or submitted, merge pending.
+True if the change is open.
 
 is:draft::
 +
@@ -278,7 +299,7 @@
 +
 True if the change is either merged or abandoned.
 
-is:submitted, is:merged, is:abandoned::
+is:merged, is:abandoned::
 +
 Same as <<status,status:'STATE'>>.
 
@@ -291,17 +312,13 @@
 [[status]]
 status:open, status:pending::
 +
-True if the change state is either 'review in progress' or 'submitted,
-merge pending'.
+True if the change state is 'review in progress'.
 
 status:reviewed::
 +
-Same as 'is:reviewed', matches if there is at least one non-zero
-score on the change, in any approval category, by any user.
-
-status:submitted::
-+
-Change has been submitted, but is waiting for a dependency.
+Same as 'is:reviewed', matches if any user has commented on the change
+more recently than the last update (comment or patch set) from the
+change owner.
 
 status:closed::
 +
@@ -327,6 +344,38 @@
 Valid relations are >=, >, <=, <, or no relation, which will match if the
 number of lines is exactly equal.
 
+[[commentby]]
+commentby:'USER'::
++
+Changes containing a top-level or inline comment by 'USER'. The special
+case of `commentby:self` will find changes where the caller has
+commented.
+
+[[from]]
+from:'USER'::
++
+Changes containing a top-level or inline comment by 'USER', or owned by
+'USER'. Equivalent to `(owner:USER OR commentby:USER)`.
+
+[[reviewedby]]
+reviewedby:'USER'::
++
+Changes where 'USER' has commented on the change more recently than the
+last update (comment or patch set) from the change owner.
+
+[[author]]
+author:'AUTHOR'::
++
+Changes where 'AUTHOR' is the author of the current patch set. 'AUTHOR' may be
+the author's exact email address, or part of the name or email address.
+
+[[committer]]
+committer:'COMMITTER'::
++
+Changes where 'COMMITTER' is the committer of the current patch set.
+'COMMITTER' may be the committer's exact email address, or part of the name or
+email address.
+
 
 == Argument Quoting
 
diff --git a/Documentation/user-submodules.txt b/Documentation/user-submodules.txt
index 8411595..151ac71 100644
--- a/Documentation/user-submodules.txt
+++ b/Documentation/user-submodules.txt
@@ -97,6 +97,7 @@
 gitlinks and a .gitmodules file with all required info) and if so,
 a new submodule subscription is registered.
 
+[[automatic_update]]
 == Automatic Update of Superprojects
 
 After a superproject is subscribed to a submodule, it is not
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b8de6e2
--- /dev/null
+++ b/README.md
@@ -0,0 +1,79 @@
+# Gerrit Code Review
+
+[Gerrit](https://www.gerritcodereview.com) is a code review and project
+management tool for Git based projects.
+
+## Objective
+
+Gerrit makes reviews easier by showing changes in a side-by-side display,
+and allowing inline comments to be added by any reviewer.
+
+Gerrit simplifies Git based project maintainership by permitting any
+authorized user to submit changes to the master Git repository, rather
+than requiring all approved changes to be merged in by hand by the project
+maintainer.
+
+## Documentation
+
+For information about how to install and use Gerrit, refer to
+[the documentation](https://gerrit-review.googlesource.com/Documentation/index.html).
+
+## Source
+
+Our canonical Git repository is located on [googlesource.com](https://gerrit.googlesource.com/gerrit).
+There is a mirror of the repository on [Github](https://github.com/gerrit-review/gerrit).
+
+## Reporting bugs
+
+Please report bugs on the [issue tracker](https://bugs.chromium.org/p/gerrit/issues/list).
+
+## Contribute
+
+Gerrit is the work of hundreds of contributors. We appreciate your help!
+
+Please read the [contribution guidelines](https://gerrit.googlesource.com/gerrit/+/master/SUBMITTING_PATCHES).
+
+Note that we do not accept Pull Requests via the Github mirror.
+
+## Getting in contact
+
+The IRC channel on freenode is #gerrit. An archive is available at:
+[echelog.com](http://echelog.com/logs/browse/gerrit).
+
+The Developer Mailing list is [repo-discuss on Google Groups](https://groups.google.com/forum/#!forum/repo-discuss).
+
+## License
+
+Gerrit is provided under the Apache License 2.0.
+
+## Build
+
+Install [Buck](http://facebook.github.io/buck/setup/install.html) and run the following:
+
+        git clone --recursive https://gerrit.googlesource.com/gerrit
+        cd gerrit && buck build release
+
+## Install binary packages (Deb/Rpm)
+
+The instruction how to configure GerritForge/BinTray repositories is
+[here](http://gitenterprise.me/2015/02/27/gerrit-2-10-rpm-and-debian-packages-available)
+
+On Debian/Ubuntu run:
+
+        apt-get update & apt-get install gerrit=<version>-<release>
+
+_NOTE: release is a counter that starts with 1 and indicates the number of packages that have
+been released with the same version of the software._
+
+On CentOS/RedHat run:
+
+        yum clean all && yum install gerrit-<version>[-<release>]
+
+_NOTE: release is optional. Last released package of the version is installed if the release
+number is omitted._
+
+## Events
+
+- November 7-8 2015: Gerrit User Conference, Mountain View. ([Register](http://goo.gl/forms/fifi2YQTc7)).
+- November 9-13 2015: Gerrit Hackathon, Mountain View. (Invitation Only).
+- March 2016: Gerrit Hackathon, Berlin. (Details to be confirmed).
diff --git a/ReleaseNotes/ReleaseNotes-2.0.10.txt b/ReleaseNotes/ReleaseNotes-2.0.10.txt
index 6fd7087..695be4f 100644
--- a/ReleaseNotes/ReleaseNotes-2.0.10.txt
+++ b/ReleaseNotes/ReleaseNotes-2.0.10.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.0.10 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 
 New Features
diff --git a/ReleaseNotes/ReleaseNotes-2.0.11.txt b/ReleaseNotes/ReleaseNotes-2.0.11.txt
index 0767873..62f2a18 100644
--- a/ReleaseNotes/ReleaseNotes-2.0.11.txt
+++ b/ReleaseNotes/ReleaseNotes-2.0.11.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.0.11 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 *WARNING: This version contains a schema change.*
 
diff --git a/ReleaseNotes/ReleaseNotes-2.0.12.txt b/ReleaseNotes/ReleaseNotes-2.0.12.txt
index c82bab0..eb28e2e 100644
--- a/ReleaseNotes/ReleaseNotes-2.0.12.txt
+++ b/ReleaseNotes/ReleaseNotes-2.0.12.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.0.12 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 *WARNING: This version contains a schema change.*
 
diff --git a/ReleaseNotes/ReleaseNotes-2.0.13.txt b/ReleaseNotes/ReleaseNotes-2.0.13.txt
index da84013..8ec13a8 100644
--- a/ReleaseNotes/ReleaseNotes-2.0.13.txt
+++ b/ReleaseNotes/ReleaseNotes-2.0.13.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.0.13.1 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 *WARNING: This version contains a major configuration change.*
 
diff --git a/ReleaseNotes/ReleaseNotes-2.0.14.txt b/ReleaseNotes/ReleaseNotes-2.0.14.txt
index 77ae3ba..de58035 100644
--- a/ReleaseNotes/ReleaseNotes-2.0.14.txt
+++ b/ReleaseNotes/ReleaseNotes-2.0.14.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.0.14.1 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 *WARNING: This version contains a schema change* (since 2.0.13)
 
diff --git a/ReleaseNotes/ReleaseNotes-2.0.15.txt b/ReleaseNotes/ReleaseNotes-2.0.15.txt
index 7de1f76..a87cba1 100644
--- a/ReleaseNotes/ReleaseNotes-2.0.15.txt
+++ b/ReleaseNotes/ReleaseNotes-2.0.15.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.0.15 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 Schema Change
 -------------
diff --git a/ReleaseNotes/ReleaseNotes-2.0.16.txt b/ReleaseNotes/ReleaseNotes-2.0.16.txt
index 5dedd8c..4d0252d 100644
--- a/ReleaseNotes/ReleaseNotes-2.0.16.txt
+++ b/ReleaseNotes/ReleaseNotes-2.0.16.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.0.16 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 Schema Change
 -------------
diff --git a/ReleaseNotes/ReleaseNotes-2.0.17.txt b/ReleaseNotes/ReleaseNotes-2.0.17.txt
index cf556cb..493a64b 100644
--- a/ReleaseNotes/ReleaseNotes-2.0.17.txt
+++ b/ReleaseNotes/ReleaseNotes-2.0.17.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.0.17 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 Schema Change
 -------------
diff --git a/ReleaseNotes/ReleaseNotes-2.0.18.txt b/ReleaseNotes/ReleaseNotes-2.0.18.txt
index 18c5abe..df635d9 100644
--- a/ReleaseNotes/ReleaseNotes-2.0.18.txt
+++ b/ReleaseNotes/ReleaseNotes-2.0.18.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.0.18 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 Important Notices
 -----------------
diff --git a/ReleaseNotes/ReleaseNotes-2.0.19.txt b/ReleaseNotes/ReleaseNotes-2.0.19.txt
index 6c33e6b..0e114c8 100644
--- a/ReleaseNotes/ReleaseNotes-2.0.19.txt
+++ b/ReleaseNotes/ReleaseNotes-2.0.19.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.0.19.2 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 Important Notices
 -----------------
diff --git a/ReleaseNotes/ReleaseNotes-2.0.2.txt b/ReleaseNotes/ReleaseNotes-2.0.2.txt
index 473cf3d..b2d5b98 100644
--- a/ReleaseNotes/ReleaseNotes-2.0.2.txt
+++ b/ReleaseNotes/ReleaseNotes-2.0.2.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.0.2 is now available for download:
 
-link:http://code.google.com/p/gerrit/[http://code.google.com/p/gerrit/]
+link:https://www.gerritcodereview.com/[https://www.gerritcodereview.com/]
 
 Important Notes
 ---------------
diff --git a/ReleaseNotes/ReleaseNotes-2.0.20.txt b/ReleaseNotes/ReleaseNotes-2.0.20.txt
index d46f74d..527de8e 100644
--- a/ReleaseNotes/ReleaseNotes-2.0.20.txt
+++ b/ReleaseNotes/ReleaseNotes-2.0.20.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.0.20 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 Schema Change
 -------------
diff --git a/ReleaseNotes/ReleaseNotes-2.0.21.txt b/ReleaseNotes/ReleaseNotes-2.0.21.txt
index 053ec89..34ab581 100644
--- a/ReleaseNotes/ReleaseNotes-2.0.21.txt
+++ b/ReleaseNotes/ReleaseNotes-2.0.21.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.0.21 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 
 Schema Change
diff --git a/ReleaseNotes/ReleaseNotes-2.0.22.txt b/ReleaseNotes/ReleaseNotes-2.0.22.txt
index 3a35421..faaca81 100644
--- a/ReleaseNotes/ReleaseNotes-2.0.22.txt
+++ b/ReleaseNotes/ReleaseNotes-2.0.22.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.0.22 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 Schema Change
 -------------
diff --git a/ReleaseNotes/ReleaseNotes-2.0.23.txt b/ReleaseNotes/ReleaseNotes-2.0.23.txt
index db841cc..16488d4 100644
--- a/ReleaseNotes/ReleaseNotes-2.0.23.txt
+++ b/ReleaseNotes/ReleaseNotes-2.0.23.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.0.23 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 Schema Change
 -------------
diff --git a/ReleaseNotes/ReleaseNotes-2.0.24.txt b/ReleaseNotes/ReleaseNotes-2.0.24.txt
index 7e0a617..1f08582 100644
--- a/ReleaseNotes/ReleaseNotes-2.0.24.txt
+++ b/ReleaseNotes/ReleaseNotes-2.0.24.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.0.24 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 
 Schema Change
diff --git a/ReleaseNotes/ReleaseNotes-2.0.3.txt b/ReleaseNotes/ReleaseNotes-2.0.3.txt
index 848f739..6bf3510 100644
--- a/ReleaseNotes/ReleaseNotes-2.0.3.txt
+++ b/ReleaseNotes/ReleaseNotes-2.0.3.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.0.3 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 I would like to express a big thank you to Brad Larson for diving into
 Gerrit and coming up with the implementation for  "Add reviewer to an
diff --git a/ReleaseNotes/ReleaseNotes-2.0.4.txt b/ReleaseNotes/ReleaseNotes-2.0.4.txt
index 4869233..fec2425 100644
--- a/ReleaseNotes/ReleaseNotes-2.0.4.txt
+++ b/ReleaseNotes/ReleaseNotes-2.0.4.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.0.4 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 *WARNING: This version contains a schema change.*
 
diff --git a/ReleaseNotes/ReleaseNotes-2.0.5.txt b/ReleaseNotes/ReleaseNotes-2.0.5.txt
index 78389fc..70116d3 100644
--- a/ReleaseNotes/ReleaseNotes-2.0.5.txt
+++ b/ReleaseNotes/ReleaseNotes-2.0.5.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.0.5 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 WARNING: This version contains a schema change.
 
diff --git a/ReleaseNotes/ReleaseNotes-2.0.6.txt b/ReleaseNotes/ReleaseNotes-2.0.6.txt
index f9444cc..9d0af33 100644
--- a/ReleaseNotes/ReleaseNotes-2.0.6.txt
+++ b/ReleaseNotes/ReleaseNotes-2.0.6.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.0.6 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 New Features
 ------------
diff --git a/ReleaseNotes/ReleaseNotes-2.0.7.txt b/ReleaseNotes/ReleaseNotes-2.0.7.txt
index 3e70233..afc7784 100644
--- a/ReleaseNotes/ReleaseNotes-2.0.7.txt
+++ b/ReleaseNotes/ReleaseNotes-2.0.7.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.0.7 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 Of note is the WAR file doubled in size.  This is due to the switch to openid4java for the OpenID relying party implementation, as it is more compliant to the OpenID 2.0 draft standard than the prior relying party, dyuproject.
 
diff --git a/ReleaseNotes/ReleaseNotes-2.0.8.txt b/ReleaseNotes/ReleaseNotes-2.0.8.txt
index 07429cc..4b2d10a5 100644
--- a/ReleaseNotes/ReleaseNotes-2.0.8.txt
+++ b/ReleaseNotes/ReleaseNotes-2.0.8.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.0.8 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 *WARNING: This version contains a schema change.*
 
diff --git a/ReleaseNotes/ReleaseNotes-2.0.9.txt b/ReleaseNotes/ReleaseNotes-2.0.9.txt
index e9fed5f..d2a9196 100644
--- a/ReleaseNotes/ReleaseNotes-2.0.9.txt
+++ b/ReleaseNotes/ReleaseNotes-2.0.9.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.0.9 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 *WARNING: This version contains schema changes.*
 
diff --git a/ReleaseNotes/ReleaseNotes-2.1.1.txt b/ReleaseNotes/ReleaseNotes-2.1.1.txt
index 5f02146..9d795b6 100644
--- a/ReleaseNotes/ReleaseNotes-2.1.1.txt
+++ b/ReleaseNotes/ReleaseNotes-2.1.1.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.1.1.1 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 Schema Change
 -------------
diff --git a/ReleaseNotes/ReleaseNotes-2.1.10.txt b/ReleaseNotes/ReleaseNotes-2.1.10.txt
index 9d43bf4..5464267 100644
--- a/ReleaseNotes/ReleaseNotes-2.1.10.txt
+++ b/ReleaseNotes/ReleaseNotes-2.1.10.txt
@@ -3,7 +3,7 @@
 
 There are no schema changes from link:ReleaseNotes-2.1.9.html[2.1.9].
 
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.1.10.war[https://gerrit-releases.storage.googleapis.com/gerrit-2.1.10.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.1.10.war[https://www.gerritcodereview.com/download/gerrit-2.1.10.war]
 
 Bug Fixes
 ---------
diff --git a/ReleaseNotes/ReleaseNotes-2.1.2.1.txt b/ReleaseNotes/ReleaseNotes-2.1.2.1.txt
index 3d42558..ae5d912 100644
--- a/ReleaseNotes/ReleaseNotes-2.1.2.1.txt
+++ b/ReleaseNotes/ReleaseNotes-2.1.2.1.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.1.2.1 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 Bug Fixes
 ---------
diff --git a/ReleaseNotes/ReleaseNotes-2.1.2.2.txt b/ReleaseNotes/ReleaseNotes-2.1.2.2.txt
index ccf25d6..6565833 100644
--- a/ReleaseNotes/ReleaseNotes-2.1.2.2.txt
+++ b/ReleaseNotes/ReleaseNotes-2.1.2.2.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.1.2.2 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 Bug Fixes
 ---------
diff --git a/ReleaseNotes/ReleaseNotes-2.1.2.3.txt b/ReleaseNotes/ReleaseNotes-2.1.2.3.txt
index d551ad8..3cfbdd1 100644
--- a/ReleaseNotes/ReleaseNotes-2.1.2.3.txt
+++ b/ReleaseNotes/ReleaseNotes-2.1.2.3.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.1.2.3 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 Bug Fixes
 ---------
diff --git a/ReleaseNotes/ReleaseNotes-2.1.2.4.txt b/ReleaseNotes/ReleaseNotes-2.1.2.4.txt
index f96c74a..5e863f7 100644
--- a/ReleaseNotes/ReleaseNotes-2.1.2.4.txt
+++ b/ReleaseNotes/ReleaseNotes-2.1.2.4.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.1.2.4 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 New Features
 ------------
diff --git a/ReleaseNotes/ReleaseNotes-2.1.2.5.txt b/ReleaseNotes/ReleaseNotes-2.1.2.5.txt
index 1cd5ae8..6e9a49e 100644
--- a/ReleaseNotes/ReleaseNotes-2.1.2.5.txt
+++ b/ReleaseNotes/ReleaseNotes-2.1.2.5.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.1.2.5 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 Bug Fixes
 ---------
diff --git a/ReleaseNotes/ReleaseNotes-2.1.2.txt b/ReleaseNotes/ReleaseNotes-2.1.2.txt
index ca69e86..e0d8c12 100644
--- a/ReleaseNotes/ReleaseNotes-2.1.2.txt
+++ b/ReleaseNotes/ReleaseNotes-2.1.2.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.1.2 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 Schema Change
 -------------
diff --git a/ReleaseNotes/ReleaseNotes-2.1.3.txt b/ReleaseNotes/ReleaseNotes-2.1.3.txt
index 44b6b56..f4faf32 100644
--- a/ReleaseNotes/ReleaseNotes-2.1.3.txt
+++ b/ReleaseNotes/ReleaseNotes-2.1.3.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.1.3 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 Schema Change
 -------------
diff --git a/ReleaseNotes/ReleaseNotes-2.1.4.txt b/ReleaseNotes/ReleaseNotes-2.1.4.txt
index 639229a..3e25163 100644
--- a/ReleaseNotes/ReleaseNotes-2.1.4.txt
+++ b/ReleaseNotes/ReleaseNotes-2.1.4.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.1.4 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 Schema Change
 -------------
diff --git a/ReleaseNotes/ReleaseNotes-2.1.5.txt b/ReleaseNotes/ReleaseNotes-2.1.5.txt
index b55aedf..4934223 100644
--- a/ReleaseNotes/ReleaseNotes-2.1.5.txt
+++ b/ReleaseNotes/ReleaseNotes-2.1.5.txt
@@ -3,7 +3,7 @@
 
 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]
+link:https://www.gerritcodereview.com/download/gerrit-2.1.5.war[https://www.gerritcodereview.com/download/gerrit-2.1.5.war]
 
 This is primarily 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.
diff --git a/ReleaseNotes/ReleaseNotes-2.1.6.1.txt b/ReleaseNotes/ReleaseNotes-2.1.6.1.txt
index d531a6c..a490c0a 100644
--- a/ReleaseNotes/ReleaseNotes-2.1.6.1.txt
+++ b/ReleaseNotes/ReleaseNotes-2.1.6.1.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.1.6.1 is now available:
 
-link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.1.6.1.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.1.6.1.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.1.6.1.war[https://www.gerritcodereview.com/download/gerrit-2.1.6.1.war]
 
 Schema Change
 -------------
diff --git a/ReleaseNotes/ReleaseNotes-2.1.6.txt b/ReleaseNotes/ReleaseNotes-2.1.6.txt
index ce65f1a..520b2a6 100644
--- a/ReleaseNotes/ReleaseNotes-2.1.6.txt
+++ b/ReleaseNotes/ReleaseNotes-2.1.6.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.1.6 is now available:
 
-link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.1.6.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.1.6.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.1.6.war[https://www.gerritcodereview.com/download/gerrit-2.1.6.war]
 
 Schema Change
 -------------
diff --git a/ReleaseNotes/ReleaseNotes-2.1.7.2.txt b/ReleaseNotes/ReleaseNotes-2.1.7.2.txt
index 802f1c7..fdd7725 100644
--- a/ReleaseNotes/ReleaseNotes-2.1.7.2.txt
+++ b/ReleaseNotes/ReleaseNotes-2.1.7.2.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.1.7.2 is now available:
 
-link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.1.7.2.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.1.7.2.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.1.7.2.war[https://www.gerritcodereview.com/download/gerrit-2.1.7.2.war]
 
 Bug Fixes
 ---------
diff --git a/ReleaseNotes/ReleaseNotes-2.1.7.txt b/ReleaseNotes/ReleaseNotes-2.1.7.txt
index b12713c..5123279 100644
--- a/ReleaseNotes/ReleaseNotes-2.1.7.txt
+++ b/ReleaseNotes/ReleaseNotes-2.1.7.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.1.7 is now available:
 
-link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.1.7.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.1.7.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.1.7.war[https://www.gerritcodereview.com/download/gerrit-2.1.7.war]
 
 Schema Change
 -------------
diff --git a/ReleaseNotes/ReleaseNotes-2.1.8.txt b/ReleaseNotes/ReleaseNotes-2.1.8.txt
index fcfd225..476e312 100644
--- a/ReleaseNotes/ReleaseNotes-2.1.8.txt
+++ b/ReleaseNotes/ReleaseNotes-2.1.8.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.1.8 is now available:
 
-link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.1.8.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.1.8.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.1.8.war[https://www.gerritcodereview.com/download/gerrit-2.1.8.war]
 
 New Features
 ------------
diff --git a/ReleaseNotes/ReleaseNotes-2.1.9.txt b/ReleaseNotes/ReleaseNotes-2.1.9.txt
index 728d7cc..2efc5b6 100644
--- a/ReleaseNotes/ReleaseNotes-2.1.9.txt
+++ b/ReleaseNotes/ReleaseNotes-2.1.9.txt
@@ -3,7 +3,7 @@
 
 There are no schema changes from link:ReleaseNotes-2.1.8.html[2.1.8].
 
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.1.9.war[https://gerrit-releases.storage.googleapis.com/gerrit-2.1.9.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.1.9.war[https://www.gerritcodereview.com/download/gerrit-2.1.9.war]
 
 Bug Fixes
 ---------
diff --git a/ReleaseNotes/ReleaseNotes-2.1.txt b/ReleaseNotes/ReleaseNotes-2.1.txt
index e1a27b0..127ab09 100644
--- a/ReleaseNotes/ReleaseNotes-2.1.txt
+++ b/ReleaseNotes/ReleaseNotes-2.1.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.1 is now available in the usual location:
 
-link:http://code.google.com/p/gerrit/downloads/list[http://code.google.com/p/gerrit/downloads/list]
+link:https://www.gerritcodereview.com/download/index.html[https://www.gerritcodereview.com/download/index.html]
 
 
 New site_path Layout
diff --git a/ReleaseNotes/ReleaseNotes-2.10.1.txt b/ReleaseNotes/ReleaseNotes-2.10.1.txt
index df70b64..3065492 100644
--- a/ReleaseNotes/ReleaseNotes-2.10.1.txt
+++ b/ReleaseNotes/ReleaseNotes-2.10.1.txt
@@ -4,8 +4,8 @@
 There are no schema changes from link:ReleaseNotes-2.10.html[2.10].
 
 Download:
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.10.1.war[
-https://gerrit-releases.storage.googleapis.com/gerrit-2.10.1.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.10.1.war[
+https://www.gerritcodereview.com/download/gerrit-2.10.1.war]
 
 Bug Fixes
 ---------
diff --git a/ReleaseNotes/ReleaseNotes-2.10.2.txt b/ReleaseNotes/ReleaseNotes-2.10.2.txt
index 2ca5505..ac7c866 100644
--- a/ReleaseNotes/ReleaseNotes-2.10.2.txt
+++ b/ReleaseNotes/ReleaseNotes-2.10.2.txt
@@ -4,8 +4,8 @@
 There are no schema changes from link:ReleaseNotes-2.10.1.html[2.10.1].
 
 Download:
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.10.2.war[
-https://gerrit-releases.storage.googleapis.com/gerrit-2.10.2.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.10.2.war[
+https://www.gerritcodereview.com/download/gerrit-2.10.2.war]
 
 Bug Fixes
 ---------
diff --git a/ReleaseNotes/ReleaseNotes-2.10.3.1.txt b/ReleaseNotes/ReleaseNotes-2.10.3.1.txt
index 2b90a6d..39312eb 100644
--- a/ReleaseNotes/ReleaseNotes-2.10.3.1.txt
+++ b/ReleaseNotes/ReleaseNotes-2.10.3.1.txt
@@ -4,8 +4,8 @@
 There are no schema changes from link:ReleaseNotes-2.10.3.html[2.10.3].
 
 Download:
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.10.3.1.war[
-https://gerrit-releases.storage.googleapis.com/gerrit-2.10.3.1.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.10.3.1.war[
+https://www.gerritcodereview.com/download/gerrit-2.10.3.1.war]
 
 The 2.10.3 release packaged wrong version of the core plugins due to a bug
 in our buck build scripts. This version fixes this issue.
diff --git a/ReleaseNotes/ReleaseNotes-2.10.3.txt b/ReleaseNotes/ReleaseNotes-2.10.3.txt
index 052840d..f7a69c3 100644
--- a/ReleaseNotes/ReleaseNotes-2.10.3.txt
+++ b/ReleaseNotes/ReleaseNotes-2.10.3.txt
@@ -2,8 +2,8 @@
 ===============================
 
 Download:
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.10.3.war[
-https://gerrit-releases.storage.googleapis.com/gerrit-2.10.3.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.10.3.war[
+https://www.gerritcodereview.com/download/gerrit-2.10.3.war]
 
 Important Notes
 ---------------
diff --git a/ReleaseNotes/ReleaseNotes-2.10.4.txt b/ReleaseNotes/ReleaseNotes-2.10.4.txt
index e221549..c16e7e9 100644
--- a/ReleaseNotes/ReleaseNotes-2.10.4.txt
+++ b/ReleaseNotes/ReleaseNotes-2.10.4.txt
@@ -4,8 +4,8 @@
 There are no schema changes from link:ReleaseNotes-2.10.3.1.html[2.10.3.1].
 
 Download:
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.10.4.war[
-https://gerrit-releases.storage.googleapis.com/gerrit-2.10.4.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.10.4.war[
+https://www.gerritcodereview.com/download/gerrit-2.10.4.war]
 
 New Features
 ------------
diff --git a/ReleaseNotes/ReleaseNotes-2.10.txt b/ReleaseNotes/ReleaseNotes-2.10.txt
index 18d0f34..f6bd951 100644
--- a/ReleaseNotes/ReleaseNotes-2.10.txt
+++ b/ReleaseNotes/ReleaseNotes-2.10.txt
@@ -4,8 +4,8 @@
 
 Gerrit 2.10 is now available:
 
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.10.war[
-https://gerrit-releases.storage.googleapis.com/gerrit-2.10.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.10.war[
+https://www.gerritcodereview.com/download/gerrit-2.10.war]
 
 Gerrit 2.10 includes the bug fixes done with
 link:ReleaseNotes-2.9.1.html[Gerrit 2.9.1],
diff --git a/ReleaseNotes/ReleaseNotes-2.11.1.txt b/ReleaseNotes/ReleaseNotes-2.11.1.txt
index a070b86..be19fc5 100644
--- a/ReleaseNotes/ReleaseNotes-2.11.1.txt
+++ b/ReleaseNotes/ReleaseNotes-2.11.1.txt
@@ -3,8 +3,8 @@
 
 Gerrit 2.11.1 is now available:
 
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.11.1.war[
-https://gerrit-releases.storage.googleapis.com/gerrit-2.11.1.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.11.1.war[
+https://www.gerritcodereview.com/download/gerrit-2.11.1.war]
 
 Gerrit 2.11.1 includes the bug fixes done with
 link:ReleaseNotes-2.10.4.html[Gerrit 2.10.4] and
diff --git a/ReleaseNotes/ReleaseNotes-2.11.txt b/ReleaseNotes/ReleaseNotes-2.11.txt
index 90519dc..44c5398 100644
--- a/ReleaseNotes/ReleaseNotes-2.11.txt
+++ b/ReleaseNotes/ReleaseNotes-2.11.txt
@@ -4,8 +4,8 @@
 
 Gerrit 2.11 is now available:
 
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.11.war[
-https://gerrit-releases.storage.googleapis.com/gerrit-2.11.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.11.war[
+https://www.gerritcodereview.com/download/gerrit-2.11.war]
 
 Gerrit 2.11 includes the bug fixes done with
 link:ReleaseNotes-2.10.1.html[Gerrit 2.10.1],
diff --git a/ReleaseNotes/ReleaseNotes-2.12.1.txt b/ReleaseNotes/ReleaseNotes-2.12.1.txt
new file mode 100644
index 0000000..f49de7d
--- /dev/null
+++ b/ReleaseNotes/ReleaseNotes-2.12.1.txt
@@ -0,0 +1,236 @@
+Release notes for Gerrit 2.12.1
+===============================
+
+Gerrit 2.12.1 is now available:
+
+link:https://gerrit-releases.storage.googleapis.com/gerrit-2.12.1.war[
+https://gerrit-releases.storage.googleapis.com/gerrit-2.12.1.war]
+
+Gerrit 2.12.1 includes the bug fixes done with
+link:ReleaseNotes-2.11.6.html[Gerrit 2.11.6] and
+link:ReleaseNotes-2.11.7.html[Gerrit 2.11.7]. These bug fixes are *not*
+listed in these release notes.
+
+Schema Upgrade
+--------------
+
+*WARNING:* This version includes a manual schema upgrade when upgrading
+from 2.12.
+
+When upgrading a site that is already running version 2.12, the `patch_sets`
+table must be manually migrated using the `gerrit gsql` SSH command or the
+`gqsl` site program.
+
+For the default H2 database, execute the command:
+
+----
+  alter table patch_sets modify push_certficate clob;
+----
+
+For MySQL, execute the command:
+
+----
+  alter table patch_sets modify push_certficate text;
+----
+
+For PostgreSQL, execute the command:
+
+----
+  alter table patch_sets alter column push_certficate type text;
+----
+
+For other database types, execute the appropriate equivalent command.
+
+Note that the misspelled `push_certficate` is the actual name of the
+column.
+
+When upgrading from a version earlier than 2.12, this manual step is not
+necessary and should be omitted.
+
+
+Bug Fixes
+---------
+
+General
+~~~~~~~
+
+* Fix column type for signed push certificates.
++
+The column type `VARCHAR(255)` was too small, preventing some PGP push
+certificates from being stored.
+
+* Add the `DRAFT_COMMENTS` option to the list changes REST API endpoint
+and mark it as deprecated.
++
+It was removed in version 2.12 because it's not needed any more by the UI,
+but this caused failures for clients that still use it.
++
+Now it is added back, although it does not do anything and is marked as
+deprecated.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3669[Issue 3669]:
+Fix schema migration when migrating to 2.12.x directly from a version
+earlier than 2.11.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3733[Issue 3733]:
+Correctly detect symlinked log directory on startup.
++
+If `$site_path/logs` was a symlink, the server would not start.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3871[Issue 3871]:
+Throw an explicit exception when failing to load a change from the database.
++
+If a change could not be loaded from the database, for example if it was
+manually removed from the changes table but references to it were remaining
+in other tables, a null change was returned which would then lead to an
+'Internal Server Error' that was difficult to track down. Now an error is
+raised earlier which will help administrators to find the root cause.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3743[Issue 3743]:
+Use submitter identity as committer when using 'Rebase if Necessary' merge
+strategy.
++
+When submitting a change that required rebase, the committer was being
+set to 'Gerrit Code Review' instead of the name of the submitter.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3758[Issue 3758]:
+Fix serving of static resources when deployed in application container.
++
+When deployed in a container, for example Tomcat, it was not possible to
+load the UI because static content could not be loaded from the WAR file.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3790[Issue 3790]:
+When deployed in a container, for example Tomcat, the 'Documentation' menu
+was missing.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3786[Issue 3786]:
+Fix SQL statement syntax in schema migration.
++
+An extra semicolon was preventing migration from 2.11.x to 2.12 when using
+an Oracle database.
+
+* Send email using email queue instead of the default queue.
++
+Some emails sent asynchronously were already being sent using that queue
+but some were not. This was confusing for a gerrit administrator because
+if there is a build up of `send-email` tasks in the queue, he would
+think that increasing `sendemail.threadPoolSize` would help but it did not
+because some of the email were sent using the default queue which is
+configurable using `execution.defaultThreadPoolSize`.
+
+* Fix XSRF token cookie to honor `auth.cookieSecure` setting.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3767[Issue 3767]:
+Fix replication of first patch set for new changes.
++
+When new changes were pushed from the command line, the first patch
+set did not get replicated to destinations.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3771[Issue 3771]:
+Remove `index.defaultMaxClauseCount` configuration option.
++
+When `index.maxTerms` was either not set (thus no limit) or set to a value
+higher than `index.defaultMaxClauseCount` it was possible that viewing the
+related changes tab could cause a 'Too many clauses' error for changes that
+have a lot of related changes.
++
+The `index.defaultMaxClauseCount` configuration option is removed, and the
+existing `index.maxTerms` is reused. The default value of `index.maxTerms`
+is reduced from 'no limit' to 1024.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3919[Issue 3919]:
+Explicitly set parent project to 'All-Projects' when a project is created
+without giving the parent.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3948[Issue 3948]:
+Fix submit of project parent updates on `refs/meta/config`.
++
+When submitting a change on `refs/meta/config` to update a project's parent,
+the error 'The change must be submitted by a Gerrit administrator' was being
+displayed even when the submitter was an admin. The submit was successful
+when clicking 'Submit' a second time.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3811[Issue 3811]:
+Fix submittability of merge commits that resolve merge conflicts.
++
+If a series of changes contained a change that conflicted with the destination
+branch, but the conflict was solved by a merge commit at the tip of the
+series, the series was not submittable.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3883[Issue 3883]:
+Respect the `core.commentchar` setting from `.gitconfig` in `commit-msg` hook.
+
+UI
+~~
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3894[Issue 3894]:
+Fix display of 'Related changes' after change is rebased in web UI:
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3071[Issue 3071]:
+Fix display of submodule differences in side-by-side view.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3718[Issue 3718]:
+Hide avatar images when no avatars are available.
++
+The UI was showing a transparent empty image with a border.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3731[Issue 3731]:
+Fix syntax higlighting of tcl files.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3863[Issue 3863]:
+Fix display of active row marker in tag list.
++
+Clicking on one of the rows would cause the tag name to disappear.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=1207[Issue 1207]:
+Fix keyboard shortcuts for non-US keyboards on side-by-side diff screen.
++
+The forward/backward navigation keys `[` and `]` only worked on keyboards where
+these characters could be typed without using any modifier key (like CTRL, ALT,
+etc..).
++
+Note that the problem still exists on the unified diff screen.
+
+* Improve tooltip on 'Submit' button when 'Submit whole topic' is enabled
+and the topic can't be submitted due to some changes not being ready.
+
+Plugins
+~~~~~~~
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3821[Issue 3821]:
+Fix repeated reloading of plugins when running on OpenJDK 8.
++
+OpenJDK 8 uses nanotime precision for file modification time on systems that
+are POSIX 2008 compatible. This leads to precision incompatibility when
+comparing the plugin's JAR file timestamp, resulting in the plugin being
+reloaded every minute.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3741[Issue 3741]:
+Fix handling of merge validation exceptions emitted by plugins.
++
+If a plugin raised an exception, it was reported to the user as 'Change is
+new', rather than 'Missing dependency'.
+
+* Allow plugins to get the caller in merge validation requests.
++
+Plugins that implement the `MergeValidationListener` interface now get the
+caller (the user who initiated the merge) in the `onPreMerge` method.
++
+Existing plugins that implement this interface must be adapted to the new
+method signature.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3892[Issue 3892]:
+Allow plugins to suggest reviewers based on either change or project
+resources.
+
+Documentation
+~~~~~~~~~~~~~
+
+* Update documentation of `commentlink` to reflect changed search URL.
+
+* Add missing documentation of valid `database.type` values.
+
+Upgrades
+--------
+
+* Upgrade JGit to 4.1.2.201602141800-r.
diff --git a/ReleaseNotes/ReleaseNotes-2.12.2.txt b/ReleaseNotes/ReleaseNotes-2.12.2.txt
new file mode 100644
index 0000000..500b015
--- /dev/null
+++ b/ReleaseNotes/ReleaseNotes-2.12.2.txt
@@ -0,0 +1,73 @@
+Release notes for Gerrit 2.12.2
+===============================
+
+Gerrit 2.12.2 is now available:
+
+link:https://gerrit-releases.storage.googleapis.com/gerrit-2.12.2.war[
+https://gerrit-releases.storage.googleapis.com/gerrit-2.12.2.war]
+
+Schema Upgrade
+--------------
+
+*WARNING:* There are no schema changes from link:ReleaseNotes-2.12.1.html[
+2.12.1] but a manual schema upgrade is necessary when upgrading from 2.12.
+
+When upgrading a site that is already running version 2.12, the `patch_sets`
+table must be manually migrated using the `gerrit gsql` SSH command or the
+`gqsl` site program.
+
+For the default H2 database, execute the command:
+
+----
+  alter table patch_sets modify push_certficate clob;
+----
+
+For MySQL, execute the command:
+
+----
+  alter table patch_sets modify push_certficate text;
+----
+
+For PostgreSQL, execute the command:
+
+----
+  alter table patch_sets alter column push_certficate type text;
+----
+
+For other database types, execute the appropriate equivalent command.
+
+Note that the misspelled `push_certficate` is the actual name of the
+column.
+
+When upgrading from a version earlier than 2.12, or from 2.12.1 having already
+done the migration, this manual step is not necessary and should be omitted.
+
+
+Bug Fixes
+---------
+
+* Upgrade Apache commons-collections to version 3.2.2.
++
+Includes a fix for a link:https://issues.apache.org/jira/browse/COLLECTIONS-580[
+remote code execution exploit].
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3919[Issue 3919]:
+Explicitly set parent project to 'All-Projects' when a project is created
+without giving the parent.
+
+* Don't add message twice on abandon or restore via ssh review command.
++
+When abandoning or reviewing a change via the ssh `review` command, and
+providing a message with the `--message` option, the message was added to
+the change twice.
+
+* Clear the input box after cancelling add reviewer action.
++
+When the action was cancelled, the content of the input box was still
+there when opening it again.
+
+* Fix internal server error when aborting ssh command.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3969[Issue 3969]:
+Fix internal server error when submitting a change with 'Rebase If Necessary'
+strategy.
diff --git a/ReleaseNotes/ReleaseNotes-2.12.3.txt b/ReleaseNotes/ReleaseNotes-2.12.3.txt
new file mode 100644
index 0000000..dba10e9
--- /dev/null
+++ b/ReleaseNotes/ReleaseNotes-2.12.3.txt
@@ -0,0 +1,117 @@
+Release notes for Gerrit 2.12.3
+===============================
+
+Gerrit 2.12.3 is now available:
+
+link:https://gerrit-releases.storage.googleapis.com/gerrit-2.12.3.war[
+https://gerrit-releases.storage.googleapis.com/gerrit-2.12.3.war]
+
+Gerrit 2.12.3 includes the bug fixes done with
+link:ReleaseNotes-2.11.8.html[Gerrit 2.11.8] and
+link:ReleaseNotes-2.11.9.html[Gerrit 2.11.9]. These bug fixes are *not*
+listed in these release notes.
+
+Schema Upgrade
+--------------
+
+*WARNING:* There are no schema changes from link:ReleaseNotes-2.12.2.html[
+2.12.2] but a manual schema upgrade is necessary when upgrading from 2.12.
+
+When upgrading a site that is already running version 2.12, the `patch_sets`
+table must be manually migrated using the `gerrit gsql` SSH command or the
+`gqsl` site program.
+
+For the default H2 database, execute the command:
+
+----
+  alter table patch_sets modify push_certficate clob;
+----
+
+For MySQL, execute the command:
+
+----
+  alter table patch_sets modify push_certficate text;
+----
+
+For PostgreSQL, execute the command:
+
+----
+  alter table patch_sets alter column push_certficate type text;
+----
+
+For other database types, execute the appropriate equivalent command.
+
+Note that the misspelled `push_certficate` is the actual name of the
+column.
+
+When upgrading from a version earlier than 2.12, or from 2.12.1 or 2.12.2
+having already done the migration, this manual step is not necessary and
+should be omitted.
+
+
+Bug Fixes
+---------
+
+* Fix SSL security issue in the SMTP email relay.
++
+The hostname of the SSL socket was not verified. This made the read
+from the socket insecure since without verifying the hostname it may
+be link:https://www.cs.utexas.edu/~shmat/shmat_ccs12.pdf[vulnerable
+to a man-in-the-middle attack].
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=3895[Issue 3895]:
+Fix failure to submit with 'Rebase if Necessary' after changes were reordered
+with interactive rebase.
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=4052[Issue 4052]:
+Fix failure to start server after upgrade from version 2.9.4.
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=3891[Issue 3891]:
+Fix query with `label:` operator and zero value.
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=4112[Issue 4112]:
+Fix failure to submit changes caused by empty user edit ref.
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=4087[Issue 4087]:
+Fix failure to submit change when a branch is created on the change ref.
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=4155[Issue 4155]:
+Fix tags REST API to correctly return all tags.
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=4154[Issue 4154]:
+Add support for `.team` and several more TLDs in email address validation.
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=4163[Issue 4163]:
+Prevent removal of non-voting reviewers on submit of change.
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=2647[Issue 2647]:
+Fix usage of `CTRL-C` on change screen.
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=4236[Issue 4236]:
+Fix internal error when pushing an amended commit with the `%edit` option.
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=3426[Issue 3426]:
+Fix pushing changes with `%base` option or `newChangeForAllNotInTarget` option.
+
+* Show 'Submitted Together' tab for changes with same topic.
+
+* Improve submit button tooltip messages shown when change is not submittable.
+
+* Fix firing of the `topic-changed` hook.
+
+* Remove `--dry-run` option from the `Reindex` site program.
++
+The implementation of the option was removed, but the option was mistakenly
+added back to the command and did not actually work.
+
+* Print proper task names in the output of the `show-queues` command.
+
+* Replication plugin: Double check if a ref is missing locally before deleting
+from remote.
+
+* Show an error message when trying to add a non-existent group to an ACL.
+
+Updates
+-------
+
+* Update commons-validator to 1.5.1.
diff --git a/ReleaseNotes/ReleaseNotes-2.12.4.txt b/ReleaseNotes/ReleaseNotes-2.12.4.txt
new file mode 100644
index 0000000..64252c6
--- /dev/null
+++ b/ReleaseNotes/ReleaseNotes-2.12.4.txt
@@ -0,0 +1,128 @@
+= Release notes for Gerrit 2.12.4
+
+Gerrit 2.12.4 is now available:
+
+link:https://gerrit-releases.storage.googleapis.com/gerrit-2.12.4.war[
+https://gerrit-releases.storage.googleapis.com/gerrit-2.12.4.war]
+
+== Schema Upgrade
+
+*WARNING:* There are no schema changes from link:ReleaseNotes-2.12.3.html[
+2.12.3] but a manual schema upgrade is necessary when upgrading from 2.12.
+
+When upgrading a site that is already running version 2.12, the `patch_sets`
+table must be manually migrated using the `gerrit gsql` SSH command or the
+`gqsl` site program.
+
+For the default H2 database, execute the command:
+
+----
+  alter table patch_sets modify push_certficate clob;
+----
+
+For MySQL, execute the command:
+
+----
+  alter table patch_sets modify push_certficate text;
+----
+
+For PostgreSQL, execute the command:
+
+----
+  alter table patch_sets alter column push_certficate type text;
+----
+
+For other database types, execute the appropriate equivalent command.
+
+Note that the misspelled `push_certficate` is the actual name of the
+column.
+
+When upgrading from a version earlier than 2.12, or from 2.12.1 or 2.12.2
+having already done the migration, this manual step is not necessary and
+should be omitted.
+
+== Known Issues
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=4323[Issue 4323]:
+'value too long for type character varying(255)' in patch_sets table when
+migrating to schema version 108.
++
+This error may occur under some circumstances when running the schema
+migration from an earlier version of Gerrit.
++
+On sites where this occurs, it can be fixed with a manual schema update
+according to the comments in the issue.
+
+== Bug Fixes
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=4400[Issue 4400]:
+Fix `AlreadyClosedException` in Lucene index.
++
+If a Lucene indexing thread was interrupted by an SSH connection being
+closed, this would also close file handles being used to read the index.
++
+Lucene queries are now executed on background threads to isolate them
+from SSH threads.
++
+This may also reduce latency for user dashboards on a multi-core system as
+each query for the different sections can now run on separate threads and
+return results when ready.
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=4249[Issue 4249]:
+Fix 'Duplicate stages not allowed' error during indexing.
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=4238[Issue 4238]:
+Fix 'not found' error when browsing tree in gitweb.
++
+The `refs/heads/` prefix was incorrectly being added to `HEAD`, causing a
+'404 Not Found' error.
+
+* Allow to read repositories that do not end with `.git`.
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=4262[Issue 4262]:
+Fix GPG push certificate for first patch set of new changes.
++
+The GPG certificate was not being set for the first patch set of new
+changes.
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=4296[Issue 4296]:
+Fix internal error when a query does not contain any token.
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=4241[Issue 4241]:
+Fix 'Cannot format velocity template' error when sending notification emails.
+
+* Fix `sshd.idleTimeout` setting being ignored.
++
+The `sshd.idleTimeout` setting was not being correctly set on the SSHD
+backend, causing idle sessions to not time out.
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=4324[Issue 4324]:
+Set the correct uploader on new patch sets created via the inline editor.
+
+* Log a warning instead of failing when invalid commentlinks are configured.
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=4136[Issue 4136]:
+Fix support for `HEAD` requests in the REST API.
++
+Sending a `HEAD` request failed with '404 Not Found'.
+
+* Return proper error response when trying to confirm an email that is already
+used by another user.
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=4318[Issue 4318]
+Fix 'Rebase if Necessary' merge strategy to prevent introducing a duplicate
+commit when submitting a merge commit.
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=4332[Issue 4332]:
+Allow `local` as a valid TLD for outgoing emails.
+
+* Bypass hostname verification when `sendemail.sslVerify` is disabled.
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=4398[Issue 4398]:
+Replication: Consider ref visibility when scheduling replication.
++
+It was possible for refs to be replicated to remotes despite not being
+visible to groups mentioned in the `authGroup` setting.
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=4036[Issue 4036]:
+Fix hanging query when using `is:watched` without authentication.
diff --git a/ReleaseNotes/ReleaseNotes-2.12.5.txt b/ReleaseNotes/ReleaseNotes-2.12.5.txt
new file mode 100644
index 0000000..12d6870
--- /dev/null
+++ b/ReleaseNotes/ReleaseNotes-2.12.5.txt
@@ -0,0 +1,101 @@
+= Release notes for Gerrit 2.12.5
+
+Gerrit 2.12.5 is now available:
+
+link:https://gerrit-releases.storage.googleapis.com/gerrit-2.12.5.war[
+https://gerrit-releases.storage.googleapis.com/gerrit-2.12.5.war]
+
+== Schema Upgrade
+
+*WARNING:* There are no schema changes from link:ReleaseNotes-2.12.4.html[
+2.12.4] but a manual schema upgrade is necessary when upgrading from 2.12.
+
+When upgrading a site that is already running version 2.12, the `patch_sets`
+table must be manually migrated using the `gerrit gsql` SSH command or the
+`gqsl` site program.
+
+For the default H2 database, execute the command:
+
+----
+  alter table patch_sets modify push_certficate clob;
+----
+
+For MySQL, execute the command:
+
+----
+  alter table patch_sets modify push_certficate text;
+----
+
+For PostgreSQL, execute the command:
+
+----
+  alter table patch_sets alter column push_certficate type text;
+----
+
+For other database types, execute the appropriate equivalent command.
+
+Note that the misspelled `push_certficate` is the actual name of the
+column.
+
+When upgrading from a version earlier than 2.12, or from 2.12.1 or 2.12.2
+having already done the migration, this manual step is not necessary and
+should be omitted.
+
+== Known Issues
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=4323[Issue 4323]:
+'value too long for type character varying(255)' in patch_sets table when
+migrating to schema version 108.
++
+This error may occur under some circumstances when running the schema
+migration from an earlier version of Gerrit.
++
+On sites where this occurs, it can be fixed with a manual schema update
+according to the comments in the issue.
+
+== New Features
+
+* New preference to enable line wrapping in diff screen and inline editor.
+
+== Bug Fixes
+
+* Fix the diff and edit preference dialogs for smaller screens.
++
+On smaller screens the options at the bottom of the dialogs would
+get cut off, making it difficult to change them.
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=4521[Issue 4521]:
+Fix internal server error during validation of email addresses.
++
+When creating a new account or adding a new email address to an existing
+account, the email validation crashed.
+
+* Lucene stability improvements.
++
+Each Lucene index is now written using a dedicated background thread. Lucene
+threads may not be cancelled, to prevent interruptions while writing.
+
+* Don't try to change username that is already set.
++
+Since Gerrit version 2.1.4 it is not allowed to change the username once
+it has been set, and attempting to do so results in an exception.
++
+If `ldap.accountSshUserName` is set in the `gerrit.config` using
+`${userPrincipalName.localPart}` to initialize the username from the user's
+email address, and then the email address is changed, the username gets
+resolved to something different and the account manager tried to change it.
+As a result, an exception was raised and the user could no longer log in.
++
+Instead of trying to change the username, a warning is logged.
+
+* link:https://bugs.chromium.org/p/gerrit/issues/detail?id=4006[Issue 4006]:
+Prevent search limit parameter from exceeding maximum integer value.
+
+* Fix internal server error when generating task names.
+
+* Print proper names for query tasks in the output of the `show-queue` command.
+
+* Double-check change status when auto-abandoning changes.
++
+It was possible that changes could be updated in the time between the query
+results being returned and the change being abandoned.
diff --git a/ReleaseNotes/ReleaseNotes-2.12.txt b/ReleaseNotes/ReleaseNotes-2.12.txt
new file mode 100644
index 0000000..e8e8aec
--- /dev/null
+++ b/ReleaseNotes/ReleaseNotes-2.12.txt
@@ -0,0 +1,592 @@
+Release notes for Gerrit 2.12
+=============================
+
+
+Gerrit 2.12 is now available:
+
+link:https://www.gerritcodereview.com/download/gerrit-2.12.war[
+https://www.gerritcodereview.com/download/gerrit-2.12.war]
+
+Important Notes
+---------------
+
+*WARNING:* This release contains schema changes.  To upgrade:
+----
+  java -jar gerrit.war init -d site_path
+----
+
+*WARNING:* To use online reindexing when upgrading to 2.12.x, the server must
+first be upgraded to 2.8 (or 2.9) and then through 2.10 and 2.11 to 2.12.x. If
+reindexing will be done offline, you may ignore this warning and upgrade directly
+to 2.12.x.
+
+*WARNING:* When upgrading from version 2.8.4 or older with a site that uses
+Bouncy Castle Crypto, new versions of the libraries will be downloaded. The old
+libraries should be manually removed from site's `lib` folder to prevent the
+startup failure described in
+link:https://code.google.com/p/gerrit/issues/detail?id=3084[issue 3084].
+
+*WARNING:* The Solr secondary index is no longer supported. With this release
+the only supported secondary index is Lucene.
+
+*WARNING:* The format of the `ref-updated` event has changed. Users of the
+link:https://wiki.jenkins-ci.org/display/JENKINS/Gerrit+Trigger[
+Jenkins Gerrit Trigger plugin] with jobs triggering on `ref-updated` should
+upgrade to at least
+link:https://wiki.jenkins-ci.org/display/JENKINS/Gerrit+Trigger#GerritTrigger-Version2.15.1%28releasedSept142015%29[
+version 2.15.1]. If an upgrade of the plugin is not possible, a workaround is
+to change the branch configuration to type `Path` with a pattern like
+`refs/*/master` instead of `Plain` and `master`.
+
+
+Release Highlights
+------------------
+
+This release includes the following new features. See the sections below for
+further details.
+
+* New change submission workflows: 'Submit Whole Topic' and 'Submitted Together'.
+
+* Support for GPG Keys and signed pushes.
+
+
+New Features
+------------
+
+New Change Submission Workflows
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* New 'Submit Whole Topic' setting.
++
+When the
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/config-gerrit.html#change.submitWholeTopic[
+`change.submitWholeTopic`] setting is enabled, all changes belonging to the same
+topic will be submitted at the same time.
++
+This setting should be considered experimental, and is disabled by default.
+
+* Submission of changes may include ancestors.
++
+If a change is submitted that has submittable ancestor changes, those changes
+will also be submitted.
+
+* The merge queue is removed.
++
+Changes that cannot be submitted due to missing dependencies will no longer
+enter the 'Submitted, Merge Pending' state.
+
+
+GPG Keys and Signed Pushes
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* Signed push can be enabled by setting
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/config-gerrit.html#receive.enableSignedPush[
+`receive.enableSignedPush`] to true.
++
+When a client pushes with `git push --signed`, Gerrit ensures that the push
+certificate is valid and signed with a valid public key stored in the
+`refs/meta/gpg-keys` branch of the `All-Users` repository.
+
+* When signed push is enabled, and
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/config-gerrit.html#gerrit.editGpgKeys[
+`gerrit.editGpgKeys`] is set to true, users may upload their public GPG
+key via the REST API or UI.
++
+If this setting is not enabled, GPG keys may only be added by administrators
+with direct access to the `All-Users` repository.
+
+* Administrators may also configure
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/config-gerrit.html#receive.certNonceSeed[
+`receive.certNonceSeed`] and
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/config-gerrit.html#receive.certNonceSlop[
+`receive.certNonceSlop`].
+
+
+Secondary Index
+~~~~~~~~~~~~~~~
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=3333[Issue 3333]:
+Support searching for changes by author and committer.
++
+Changes are indexed by the git author and committer of the latest patch set,
+and can be searched with the `author:` and `committer:` operators.
++
+Changes are matched on either the exact whole email address, or on parts of the
+name or email address.
+
+* Add `from:` search operator to match by owner of change or author of comments.
+
+* Add `commentby:` search operator to search by author of comments.
+
+* Change the `topic:` search operator to search by the exact topic name.
+
+* Add `intopic:` search operator to search by topics containing the search term.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=3291[Issue 3291]:
+Add `has:edit` search operator to match changes that have edit revisions on them.
+
+* Allow configuration of maximum query size.
++
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/config-gerrit.html#index.maxTerms[
+`index.maxTerms`] can be set to limit the number of leaf index terms.
+
+* Expose Lucene index writers for plugins.
++
+Plugins can now reconfigure various Lucene performance related parameters
+at runtime.
+
+* Make Lucene index writers auto-commit writers.
++
+Plugins can now temporarily turn on auto-committing in situations where it makes
+sense to enforce all changes to be written to disk ASAP.
+
+
+UI
+~~
+
+General
+^^^^^^^
+
+* Edit and diff preferences can be modified from the user preferences screen.
++
+Previously it was only possible to edit these preferences from the actual
+diff and edit screens.
+
+* Add 'Edits' to the 'My' dashboard menu to list changes on which the user
+has an unpublished edit revision.
+
+* Support for URL aliases.
++
+Administrators may define
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/config-gerrit.html#urlAlias[
+URL aliases] to map plugin screens into the Gerrit URL namespace.
++
+Plugins may use user-specific URL aliases to replace certain screens for certain
+users.
+
+
+Project Screen
+^^^^^^^^^^^^^^
+
+* New tab to list the project's tags, similar to the branch list.
+
+
+Inline Editor
+^^^^^^^^^^^^^
+
+* Store and load edit preferences in git.
++
+Edit preferences are stored and loaded to/from the `All-Users` repository.
+
+* Add 'auto close brackets' feature.
+
+* Add 'match brackets' feature.
+
+* Make the cursor blink rate customizable.
+
+* Add support for Emacs and Vim key maps.
+
+
+Change Screen
+^^^^^^^^^^^^^
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=3318[Issue 3318]:
+Highlight 'Reply' button if there are draft comments on any patch set.
++
+If any patch set of the change has a draft comment by the current user,
+the 'Reply' button is highlighted.
++
+The icons depicting draft comments are removed from the revisions drop-down
+list.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1100[Issue 1100]:
+Publish all draft comments when replying to a change.
++
+All draft comments, including those on older patch sets, are published when
+replying to a change.
+
+* Show file size increase/decrease for binary files.
+
+* Show uploader if different from change owner.
+
+* Show push certificate status.
+
+* Show change subject as tooltip on related changes list.
++
+This helps to identify changes when the subject is truncated in the list.
+
+
+Side-By-Side Diff
+^^^^^^^^^^^^^^^^^
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=3293[Issue 3293]:
+Add syntax highlighting for Puppet.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=3447[Issue 3447]:
+Add syntax highlighting for VHDL.
+
+
+Group Screen
+^^^^^^^^^^^^
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1479[Issue 1479]:
+The group screen now includes an 'Audit Log' panel showing member additions,
+removals, and the user who made the change.
+
+
+API
+~~~
+
+Several new APIs are added.
+
+Accounts
+^^^^^^^^
+
+* Suggest accounts.
+
+Tags
+^^^^
+
+* List tags.
+
+* Get tag.
+
+
+REST API
+~~~~~~~~
+
+New REST API endpoints and new options on existing endpoints.
+
+
+Accounts
+^^^^^^^^
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/rest-api-accounts.html#set-username[
+Set Username]: Set the username of an account.
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/rest-api-accounts.html#get-detail[
+Get Account Details]: Get the details of an account.
++
+In addition to the
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/rest-api-accounts.html#account-info[
+AccountInfo] fields returned by the existing
+ link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/rest-api-accounts.html#get-account[
+Get Account] endpoint, the new REST endpoint returns the registration date of
+the account and the timestamp of when contact information was filed for this
+account.
+
+
+Changes
+^^^^^^^
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/rest-api-changes.html#set-review[
+Set Review]: Add an option to omit duplicate comments.
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/rest-api-changes.html#get-safe-content[
+Download Content]: Download the content of a file from a certain revision, in a
+safe format that poses no risk for inadvertent execution of untrusted code.
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/rest-api-changes.html#submitted-together[
+Get Submitted Together]: Get the list of all changes that will be submitted at
+the same time as the change.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=1100[Issue 1100]:
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/rest-api-changes.html#set-review[
+Set Review]: Add an option to publish draft comments on all revisions.
+
+Config
+^^^^^^
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/rest-api-config.html#get-info[
+Get Server Info]: Return information about the Gerrit server configuration.
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/rest-api-config.html#confirm-email[
+Confirm Email]: Confirm that the user owns an email address.
+
+
+Groups
+^^^^^^
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/rest-api-groups.html#list-group[
+List Groups]: Add option to suggest groups.
++
+This allows group auto-completion to be used in a plugin's UI.
+
+*  link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/rest-api-groups.html#get-audit-log[
+Get Audit Log]: Get the audit log of a Gerrit internal group, showing member
+additions, removals, and the user who made the change.
+
+
+Projects
+^^^^^^^^
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/rest-api-projects.html#run-gc[
+Run GC]: Add `aggressive` option to specify whether or not to run an aggressive
+garbage collection.
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/rest-api-projects.html#list-tags[
+List Tags]: Support filtering by substring and regex, and pagination with
+`--start` and `--end`.
+
+
+SSH
+~~~
+
+* Add support for ZLib Compression.
++
+To enable compression use the
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/config-gerrit.html#sshd.enableCompression[
+`sshd.enableCompression` setting].
+
+* Add support for hmac-sha2-256 and hmac-sha2-512 as MACs.
+
+Plugins
+~~~~~~~
+
+General
+^^^^^^^
+
+* Gerrit client can now pass JavaScriptObjects to extension panels.
+
+* New UI extension point for header bar in change screen.
+
+* New UI extension point to password screen.
+
+* New UI extension points to project info screen.
+
+* New UI extension point for pop down buttons on change screen.
+
+* New UI extension point for buttons in header bar on change screen.
+
+* New UI extension point at bottom of the user preferences screen.
+
+* New UI extension point for the 'Included In' drop-down panel.
++
+By implementing the
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/dev-plugins.html#included-in[
+Included In interface], plugins may add entries to the 'Included In' dropdown
+menu on the change screen.
+
+* Plugins can extend Gerrit screens with GWT controls.
+
+* Plugins can add custom settings screens.
+
+* Referencing groups in `project.config`.
++
+Plugins can refer to groups so that when they are renamed, the project
+config will also be updated in this section.
+
+* API
+
+** Allow to use `CurrentSchemaVersion`.
+
+** Allow to use `InternalChangeQuery.query()`.
+
+** Allow to use `JdbcUtil.port()`.
+
+** Allow to use GWTORM `Key` classes.
+
+
+Other
+~~~~~
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=3401[Issue 3401]:
+Add option to
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/config-gerrit.html#sendemail.allowRegisterNewEmail[
+disable registration of new email addresses].
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=2061[Issue 2061]
+Add Support for `git-upload-archive`.
++
+This allows use the standard `git archive` command to create an archive
+of the content of a repository.
+
+* Add a background job to automatically abandon inactive changes.
++
+The
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/config-gerrit.html#changeCleanup[
+changeCleanup] configuration can be set to periodically check for inactive
+changes and automatically abandon them.
+
+* Add support for the
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/database-setup.html#createdb_db2[
+DB2 database].
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=3441[Issue 3441]:
+Add support for the
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/database-setup.html#createdb_derby[
+Apache Derby database].
+
+* Download commands plugin: Use commit IDs for download commands when change refs are hidden.
++
+Git has a configuration option to hide refs from the initial advertisement
+(`uploadpack.hideRefs`). This option can be used to hide the change refs from
+the client. As consequence this prevented fetching changes by change ref from
+working.
++
+Setting `download.checkForHiddenChangeRefs` in the `gerrit.config` to true
+allows the download commands plugin to check for hidden change refs.
+
+* Add a new 'Maintain Server' global capability.
++
+Members of a group with the 'Maintain Server' capability may view caches, tasks,
+and queues, and invoke the index REST API on changes.
+
+
+Bug Fixes
+---------
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=3499[Issue 3499]:
+Fix syntax highlighting of raw string literals in go.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=3643[Issue 3643]:
+Fix syntax highlighting of ES6 string templating using backticks.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=3653[Issue 3653]:
+Correct timezone in sshd log after DST change.
++
+When encountering a DST switch, the timezone wasn't updated until
+the server was reloaded.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=3306[Issue 3306]:
+Allow admins to read, push and create on `refs/users/default`.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=3212[Issue 3212]:
+Fix failure to run `init` when `--site-path` option is not explicitly given.
+
+* Make email validation case insensitive.
++
+While link:https://tools.ietf.org/html/rfc5321#section-2.3.11[
+RFC 5321 section 2.3.11] allows for the local-part (the part left of
+the '@') of an email address to be case sensitive, the domain portion is
+case insensitive according to
+link:https://tools.ietf.org/html/rfc1035#section-3.1[RFC 1035 section 3.1].
+And in practice, even the local-part is typically case insensitive also.
+
+* `commit-msg` hook: Don't add `Change-Id` line on temporary commits.
++
+Commits created with `git commit --fixup` or `git commit --squash` are not
+intended to be pushed to Gerrit, and don't need a `Change-Id` line.
++
+This also prevents changes from being accidentally uploaded, at least for
+projects that have the 'Require Change-Id' configuration enabled.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=3444[Issue 3444]:
+download-commands plugin: Fix clone with commit-msg hook when project name
+contains '/'.
+
+* Use full ref name in `refName` attribute of `ref-updated` events.
++
+The link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/json.html#refUpdate[
+refUpdate attribute] in `ref-updated` events did not include the full name
+of the ref in the `refName` attribute, i.e. `master` was used instead of
+`refs/heads/master`.
++
+Support for the new format is added in
+link:https://wiki.jenkins-ci.org/display/JENKINS/Gerrit+Trigger#GerritTrigger-Version2.15.1%28releasedSept142015%29[
+version 2.15.1 of the Jenkins Gerrit Trigger plugin].
++
+Users who are unable to upgrade the plugin may instead change the
+trigger's branch configuration to type `Path` with a pattern like
+`refs/*/master` instead of `Plain` and `master`.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3714[Issue 3714]:
+Improve visibility of comments on dark themes.
+
+* Fix highlighting of search results and trailing whitespaces in intraline
+diff chunks.
+
+* Fix server error when listing annotated/signed tag that has no tagger info.
+
+* Don't create new account when claimed OAuth identity is unknown.
++
+The Claimed Identity feature was enabled to support old Google OpenID accounts,
+that cannot be activated anymore. In some corner cases, when for example the URL
+is not from the production Gerrit site, for example on a staging instance, the
+OpenID identity may deviate from the original one. In case of mismatch, the lookup
+of the user for the claimed identity would fail, causing a new account to be
+created.
+
+* Suggest to upgrade installed plugins per default during site initialization
+to new Gerrit version.
++
+The default was 'No' which resulted in some sites not upgrading core
+plugins and running the wrong versions.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3698[Issue 3698]:
+Fix creation of the administrator user on databases with pre-allocated
+auto-increment column values.
++
+When using a database configuration where auto-increment column values are
+pre-allocated, it was possible that the 'Administrators' group was created
+with an ID other than `1`. In this case, the created admin user was not added
+to the correct group, and did not have the correct admin permissions.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3018[Issue 3018]:
+Fix query for changes using a label with a group operator.
++
+The `group` operator was being ignored when searching for changes with labels
+because the search index does not contain group information.
+
+* Fix online reindexing of changes that don't already exist in the index.
++
+Changes are now always reloaded from the database during online reindex.
+
+* Fix reading of plugin documentation.
++
+Under some circumstances it was possible to fail with an IO error.
+
+Documentation Updates
+---------------------
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=412[Issue 412]:
+Update documentation of `commentlink.match` regular expression to clarify
+that the expression is applied to the rendered HTML.
+
+* Remove warning about unstable change edit REST API endpoints.
++
+These endpoints should be considered stable since version 2.11.
+
+* Document that `ldap.groupBase` and `ldap.accountBase` are repeatable.
+
+Upgrades
+--------
+
+* Upgrade Asciidoctor to 1.5.2
+
+* Upgrade AutoValue to 1.1
+
+* Upgrade Bouncy Castle to 1.52
+
+* Upgrade CodeMirror to 5.7
+
+* Upgrade gson to 2.3.1
+
+* Upgrade guava to 19.0-RC2
+
+* Upgrade gwtorm to 1.14-20-gec13fdc
+
+* Upgrade H2 to 1.3.176
+
+* Upgrade httpcomponents to 4.4.1
+
+* Upgrade Jetty to 9.2.13.v20150730
+
+* Upgrade JGit to 4.1.1.201511131810-r
+
+* Upgrade joda-time to 2.8
+
+* Upgrade JRuby to 1.7.18
+
+* Upgrade jsch to 0.1.53
+
+* Upgrade JUnit to 4.11
+
+* Upgrade Lucene to 5.3.0
+
+* Upgrade Prolog Cafe 1.4.1
+
+* Upgrade servlet API to 8.0.24
+
+* Upgrade Truth to version 0.27
+
diff --git a/ReleaseNotes/ReleaseNotes-2.2.0.txt b/ReleaseNotes/ReleaseNotes-2.2.0.txt
index 58605fe..5938f66 100644
--- a/ReleaseNotes/ReleaseNotes-2.2.0.txt
+++ b/ReleaseNotes/ReleaseNotes-2.2.0.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.2.0 is now available:
 
-link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.2.0.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.2.0.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.2.0.war[https://www.gerritcodereview.com/download/gerrit-2.2.0.war]
 
 Schema Change
 -------------
diff --git a/ReleaseNotes/ReleaseNotes-2.2.1.txt b/ReleaseNotes/ReleaseNotes-2.2.1.txt
index ff8040e..6a4829e 100644
--- a/ReleaseNotes/ReleaseNotes-2.2.1.txt
+++ b/ReleaseNotes/ReleaseNotes-2.2.1.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.2.1 is now available:
 
-link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.2.1.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.2.1.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.2.1.war[https://www.gerritcodereview.com/download/gerrit-2.2.1.war]
 
 Schema Change
 -------------
diff --git a/ReleaseNotes/ReleaseNotes-2.2.2.1.txt b/ReleaseNotes/ReleaseNotes-2.2.2.1.txt
index 4613787..aabe03a 100644
--- a/ReleaseNotes/ReleaseNotes-2.2.2.1.txt
+++ b/ReleaseNotes/ReleaseNotes-2.2.2.1.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.2.2.1 is now available:
 
-link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.2.2.1.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.2.2.1.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.2.2.1.war[https://www.gerritcodereview.com/download/gerrit-2.2.2.1.war]
 
 
 There are no schema changes from 2.2.2.  However, if upgrading from
diff --git a/ReleaseNotes/ReleaseNotes-2.2.2.2.txt b/ReleaseNotes/ReleaseNotes-2.2.2.2.txt
index f7c299e..2747ab0 100644
--- a/ReleaseNotes/ReleaseNotes-2.2.2.2.txt
+++ b/ReleaseNotes/ReleaseNotes-2.2.2.2.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.2.2.2 is now available:
 
-link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.2.2.2.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.2.2.2.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.2.2.2.war[https://www.gerritcodereview.com/download/gerrit-2.2.2.2.war]
 
 There are no schema changes from 2.2.2, or 2.2.2.1.
 
diff --git a/ReleaseNotes/ReleaseNotes-2.2.2.txt b/ReleaseNotes/ReleaseNotes-2.2.2.txt
index 2c14fa1..3889dcc 100644
--- a/ReleaseNotes/ReleaseNotes-2.2.2.txt
+++ b/ReleaseNotes/ReleaseNotes-2.2.2.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.2.2 is now available:
 
-link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.2.2.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.2.2.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.2.2.war[https://www.gerritcodereview.com/download/gerrit-2.2.2.war]
 
 Schema Change
 -------------
diff --git a/ReleaseNotes/ReleaseNotes-2.3.1.txt b/ReleaseNotes/ReleaseNotes-2.3.1.txt
index c37fbe4..8914c69 100644
--- a/ReleaseNotes/ReleaseNotes-2.3.1.txt
+++ b/ReleaseNotes/ReleaseNotes-2.3.1.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.3.1 is now available:
 
-link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.3.1.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.3.1.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.3.1.war[https://www.gerritcodereview.com/download/gerrit-2.3.1.war]
 
 There are no schema changes from 2.3.
 
diff --git a/ReleaseNotes/ReleaseNotes-2.3.txt b/ReleaseNotes/ReleaseNotes-2.3.txt
index cf371c8..9cdc886 100644
--- a/ReleaseNotes/ReleaseNotes-2.3.txt
+++ b/ReleaseNotes/ReleaseNotes-2.3.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.3 is now available:
 
-link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.3.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.3.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.3.war[https://www.gerritcodereview.com/download/gerrit-2.3.war]
 
 Schema Change
 -------------
diff --git a/ReleaseNotes/ReleaseNotes-2.4.1.txt b/ReleaseNotes/ReleaseNotes-2.4.1.txt
index 15dc1d3..dbe6c4b 100644
--- a/ReleaseNotes/ReleaseNotes-2.4.1.txt
+++ b/ReleaseNotes/ReleaseNotes-2.4.1.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.4.1 is now available:
 
-link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.4.1.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.4.1.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.4.1.war[https://www.gerritcodereview.com/download/gerrit-2.4.1.war]
 
 
 There are no schema changes from 2.4.  However, if upgrading from
diff --git a/ReleaseNotes/ReleaseNotes-2.4.2.txt b/ReleaseNotes/ReleaseNotes-2.4.2.txt
index 9ae9fd7..5652d15 100644
--- a/ReleaseNotes/ReleaseNotes-2.4.2.txt
+++ b/ReleaseNotes/ReleaseNotes-2.4.2.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.4.2 is now available:
 
-link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.4.2.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.4.2.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.4.2.war[https://www.gerritcodereview.com/download/gerrit-2.4.2.war]
 
 There are no schema changes from 2.4, or 2.4.1.
 
diff --git a/ReleaseNotes/ReleaseNotes-2.4.3.txt b/ReleaseNotes/ReleaseNotes-2.4.3.txt
index c9c2d2c..6745564 100644
--- a/ReleaseNotes/ReleaseNotes-2.4.3.txt
+++ b/ReleaseNotes/ReleaseNotes-2.4.3.txt
@@ -3,7 +3,7 @@
 
 There are no schema changes from link:ReleaseNotes-2.4.2.html[2.4.2].
 
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.4.3.war[https://gerrit-releases.storage.googleapis.com/gerrit-2.4.3.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.4.3.war[https://www.gerritcodereview.com/download/gerrit-2.4.3.war]
 
 Bug Fixes
 ---------
diff --git a/ReleaseNotes/ReleaseNotes-2.4.4.txt b/ReleaseNotes/ReleaseNotes-2.4.4.txt
index de9e2cb..5570271 100644
--- a/ReleaseNotes/ReleaseNotes-2.4.4.txt
+++ b/ReleaseNotes/ReleaseNotes-2.4.4.txt
@@ -3,7 +3,7 @@
 
 There are no schema changes from link:ReleaseNotes-2.4.4.html[2.4.4].
 
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.4.4.war[https://gerrit-releases.storage.googleapis.com/gerrit-2.4.4.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.4.4.war[https://www.gerritcodereview.com/download/gerrit-2.4.4.war]
 
 Bug Fixes
 ---------
diff --git a/ReleaseNotes/ReleaseNotes-2.4.txt b/ReleaseNotes/ReleaseNotes-2.4.txt
index 6f10c0b..0e11550 100644
--- a/ReleaseNotes/ReleaseNotes-2.4.txt
+++ b/ReleaseNotes/ReleaseNotes-2.4.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.4 is now available:
 
-link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.4.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.4.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.4.war[https://www.gerritcodereview.com/download/gerrit-2.4.war]
 
 Schema Change
 -------------
diff --git a/ReleaseNotes/ReleaseNotes-2.5.1.txt b/ReleaseNotes/ReleaseNotes-2.5.1.txt
index 6fc0dc5..6e6a481 100644
--- a/ReleaseNotes/ReleaseNotes-2.5.1.txt
+++ b/ReleaseNotes/ReleaseNotes-2.5.1.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.5.1 is now available:
 
-link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-full-2.5.1.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-full-2.5.1.war]
+link:https://www.gerritcodereview.com/download/gerrit-full-2.5.1.war[https://www.gerritcodereview.com/download/gerrit-full-2.5.1.war]
 
 There are no schema changes from 2.5, or 2.5.1.
 
diff --git a/ReleaseNotes/ReleaseNotes-2.5.2.txt b/ReleaseNotes/ReleaseNotes-2.5.2.txt
index f87328e..cc436ac 100644
--- a/ReleaseNotes/ReleaseNotes-2.5.2.txt
+++ b/ReleaseNotes/ReleaseNotes-2.5.2.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.5.2 is now available:
 
-link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-full-2.5.2.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-full-2.5.2.war]
+link:https://www.gerritcodereview.com/download/gerrit-full-2.5.2.war[https://www.gerritcodereview.com/download/gerrit-full-2.5.2.war]
 
 There are no schema changes from 2.5, or 2.5.1.
 
diff --git a/ReleaseNotes/ReleaseNotes-2.5.3.txt b/ReleaseNotes/ReleaseNotes-2.5.3.txt
index 60efa7a..8e9db0c 100644
--- a/ReleaseNotes/ReleaseNotes-2.5.3.txt
+++ b/ReleaseNotes/ReleaseNotes-2.5.3.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.5.3 is now available:
 
-link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.5.3.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.5.3.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.5.3.war[https://www.gerritcodereview.com/download/gerrit-2.5.3.war]
 
 There are no schema changes from any of the 2.5.x versions.
 
diff --git a/ReleaseNotes/ReleaseNotes-2.5.4.txt b/ReleaseNotes/ReleaseNotes-2.5.4.txt
index 1657d9b..4d51528 100644
--- a/ReleaseNotes/ReleaseNotes-2.5.4.txt
+++ b/ReleaseNotes/ReleaseNotes-2.5.4.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.5.4 is now available:
 
-link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.5.4.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-2.5.4.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.5.4.war[https://www.gerritcodereview.com/download/gerrit-2.5.4.war]
 
 There are no schema changes from any of the 2.5.x versions.
 
diff --git a/ReleaseNotes/ReleaseNotes-2.5.5.txt b/ReleaseNotes/ReleaseNotes-2.5.5.txt
index 57b6d24..146fd40 100644
--- a/ReleaseNotes/ReleaseNotes-2.5.5.txt
+++ b/ReleaseNotes/ReleaseNotes-2.5.5.txt
@@ -3,7 +3,7 @@
 
 There are no schema changes from link:ReleaseNotes-2.5.4.html[2.5.4].
 
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.5.5.war[https://gerrit-releases.storage.googleapis.com/gerrit-2.5.5.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.5.5.war[https://www.gerritcodereview.com/download/gerrit-2.5.5.war]
 
 Bug Fixes
 ---------
diff --git a/ReleaseNotes/ReleaseNotes-2.5.6.txt b/ReleaseNotes/ReleaseNotes-2.5.6.txt
index 2e9e888..b1e88f9 100644
--- a/ReleaseNotes/ReleaseNotes-2.5.6.txt
+++ b/ReleaseNotes/ReleaseNotes-2.5.6.txt
@@ -3,7 +3,7 @@
 
 There are no schema changes from link:ReleaseNotes-2.5.6.html[2.5.6].
 
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.5.6.war[https://gerrit-releases.storage.googleapis.com/gerrit-2.5.6.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.5.6.war[https://www.gerritcodereview.com/download/gerrit-2.5.6.war]
 
 Bug Fixes
 ---------
diff --git a/ReleaseNotes/ReleaseNotes-2.5.txt b/ReleaseNotes/ReleaseNotes-2.5.txt
index 4abed47..8519ee9 100644
--- a/ReleaseNotes/ReleaseNotes-2.5.txt
+++ b/ReleaseNotes/ReleaseNotes-2.5.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.5 is now available:
 
-link:http://code.google.com/p/gerrit/downloads/detail?name=gerrit-full-2.5.war[http://code.google.com/p/gerrit/downloads/detail?name=gerrit-full-2.5.war]
+link:https://www.gerritcodereview.com/download/gerrit-full-2.5.war[https://www.gerritcodereview.com/download/gerrit-full-2.5.war]
 
 Gerrit 2.5 includes the bug fixes done with
 link:ReleaseNotes-2.4.1.html[Gerrit 2.4.1] and
diff --git a/ReleaseNotes/ReleaseNotes-2.6.1.txt b/ReleaseNotes/ReleaseNotes-2.6.1.txt
index e163b43d..e43b077 100644
--- a/ReleaseNotes/ReleaseNotes-2.6.1.txt
+++ b/ReleaseNotes/ReleaseNotes-2.6.1.txt
@@ -3,7 +3,7 @@
 
 There are no schema changes from link:ReleaseNotes-2.6.html[2.6].
 
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.6.1.war[https://gerrit-releases.storage.googleapis.com/gerrit-2.6.1.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.6.1.war[https://www.gerritcodereview.com/download/gerrit-2.6.1.war]
 
 Bug Fixes
 ---------
diff --git a/ReleaseNotes/ReleaseNotes-2.6.txt b/ReleaseNotes/ReleaseNotes-2.6.txt
index a000a62..dfd5d80 100644
--- a/ReleaseNotes/ReleaseNotes-2.6.txt
+++ b/ReleaseNotes/ReleaseNotes-2.6.txt
@@ -3,7 +3,7 @@
 
 Gerrit 2.6 is now available:
 
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.6.war[https://gerrit-releases.storage.googleapis.com/gerrit-2.6.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.6.war[https://www.gerritcodereview.com/download/gerrit-2.6.war]
 
 Gerrit 2.6 includes the bug fixes done with
 link:ReleaseNotes-2.5.1.html[Gerrit 2.5.1],
diff --git a/ReleaseNotes/ReleaseNotes-2.7.txt b/ReleaseNotes/ReleaseNotes-2.7.txt
index 5133c04..9782b08 100644
--- a/ReleaseNotes/ReleaseNotes-2.7.txt
+++ b/ReleaseNotes/ReleaseNotes-2.7.txt
@@ -4,8 +4,8 @@
 
 Gerrit 2.7 is now available:
 
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.7.war[
-https://gerrit-releases.storage.googleapis.com/gerrit-2.7.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.7.war[
+https://www.gerritcodereview.com/download/gerrit-2.7.war]
 
 Gerrit 2.7 includes the bug fixes done with link:ReleaseNotes-2.6.1.html[Gerrit 2.6.1].
 These bug fixes are *not* listed in these release notes.
diff --git a/ReleaseNotes/ReleaseNotes-2.8.1.txt b/ReleaseNotes/ReleaseNotes-2.8.1.txt
index b155f80..26414a1 100644
--- a/ReleaseNotes/ReleaseNotes-2.8.1.txt
+++ b/ReleaseNotes/ReleaseNotes-2.8.1.txt
@@ -3,7 +3,7 @@
 
 There are no schema changes from link:ReleaseNotes-2.8.html[2.8].
 
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.8.1.war[https://gerrit-releases.storage.googleapis.com/gerrit-2.8.1.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.8.1.war[https://www.gerritcodereview.com/download/gerrit-2.8.1.war]
 
 Bug Fixes
 ---------
diff --git a/ReleaseNotes/ReleaseNotes-2.8.2.txt b/ReleaseNotes/ReleaseNotes-2.8.2.txt
index e963ff4..926db02 100644
--- a/ReleaseNotes/ReleaseNotes-2.8.2.txt
+++ b/ReleaseNotes/ReleaseNotes-2.8.2.txt
@@ -4,8 +4,8 @@
 There are no schema changes from link:ReleaseNotes-2.8.1.html[2.8.1].
 
 Download:
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.8.2.war[
-https://gerrit-releases.storage.googleapis.com/gerrit-2.8.2.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.8.2.war[
+https://www.gerritcodereview.com/download/gerrit-2.8.2.war]
 
 
 Lucene Index
diff --git a/ReleaseNotes/ReleaseNotes-2.8.3.txt b/ReleaseNotes/ReleaseNotes-2.8.3.txt
index 6cf66c8..2bd4aa7 100644
--- a/ReleaseNotes/ReleaseNotes-2.8.3.txt
+++ b/ReleaseNotes/ReleaseNotes-2.8.3.txt
@@ -4,8 +4,8 @@
 There are no schema changes from link:ReleaseNotes-2.8.2.html[2.8.2].
 
 Download:
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.8.3.war[
-https://gerrit-releases.storage.googleapis.com/gerrit-2.8.3.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.8.3.war[
+https://www.gerritcodereview.com/download/gerrit-2.8.3.war]
 
 
 Bug Fixes
diff --git a/ReleaseNotes/ReleaseNotes-2.8.4.txt b/ReleaseNotes/ReleaseNotes-2.8.4.txt
index 1719294..b80ac17 100644
--- a/ReleaseNotes/ReleaseNotes-2.8.4.txt
+++ b/ReleaseNotes/ReleaseNotes-2.8.4.txt
@@ -4,8 +4,8 @@
 There are no schema changes from link:ReleaseNotes-2.8.3.html[2.8.3].
 
 Download:
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.8.4.war[
-https://gerrit-releases.storage.googleapis.com/gerrit-2.8.4.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.8.4.war[
+https://www.gerritcodereview.com/download/gerrit-2.8.4.war]
 
 
 Bug Fixes
diff --git a/ReleaseNotes/ReleaseNotes-2.8.5.txt b/ReleaseNotes/ReleaseNotes-2.8.5.txt
index 4785642..db18083 100644
--- a/ReleaseNotes/ReleaseNotes-2.8.5.txt
+++ b/ReleaseNotes/ReleaseNotes-2.8.5.txt
@@ -2,8 +2,8 @@
 ==============================
 
 Download:
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.8.5.war[
-https://gerrit-releases.storage.googleapis.com/gerrit-2.8.5.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.8.5.war[
+https://www.gerritcodereview.com/download/gerrit-2.8.5.war]
 
 Schema Changes and Upgrades
 ---------------------------
diff --git a/ReleaseNotes/ReleaseNotes-2.8.6.1.txt b/ReleaseNotes/ReleaseNotes-2.8.6.1.txt
index 2783ec6..d1ed9e9 100644
--- a/ReleaseNotes/ReleaseNotes-2.8.6.1.txt
+++ b/ReleaseNotes/ReleaseNotes-2.8.6.1.txt
@@ -4,8 +4,8 @@
 There are no schema changes from link:ReleaseNotes-2.8.6.html[2.8.6].
 
 Download:
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.8.6.1.war[
-https://gerrit-releases.storage.googleapis.com/gerrit-2.8.6.1.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.8.6.1.war[
+https://www.gerritcodereview.com/download/gerrit-2.8.6.1.war]
 
 Bug Fixes
 ---------
diff --git a/ReleaseNotes/ReleaseNotes-2.8.6.txt b/ReleaseNotes/ReleaseNotes-2.8.6.txt
index 3a132e4..ab79a20 100644
--- a/ReleaseNotes/ReleaseNotes-2.8.6.txt
+++ b/ReleaseNotes/ReleaseNotes-2.8.6.txt
@@ -4,8 +4,8 @@
 There are no schema changes from link:ReleaseNotes-2.8.5.html[2.8.5].
 
 Download:
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.8.6.war[
-https://gerrit-releases.storage.googleapis.com/gerrit-2.8.6.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.8.6.war[
+https://www.gerritcodereview.com/download/gerrit-2.8.6.war]
 
 *Warning*: Support for MySQL's MyISAM storage engine is discontinued.
 Only transactional storage engines are supported.
diff --git a/ReleaseNotes/ReleaseNotes-2.8.txt b/ReleaseNotes/ReleaseNotes-2.8.txt
index 92cdda2..2d1dc7a 100644
--- a/ReleaseNotes/ReleaseNotes-2.8.txt
+++ b/ReleaseNotes/ReleaseNotes-2.8.txt
@@ -4,8 +4,8 @@
 
 Gerrit 2.8 is now available:
 
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.8.war[
-https://gerrit-releases.storage.googleapis.com/gerrit-2.8.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.8.war[
+https://www.gerritcodereview.com/download/gerrit-2.8.war]
 
 
 Schema Change
diff --git a/ReleaseNotes/ReleaseNotes-2.9.1.txt b/ReleaseNotes/ReleaseNotes-2.9.1.txt
index 656b5b2..3377df4 100644
--- a/ReleaseNotes/ReleaseNotes-2.9.1.txt
+++ b/ReleaseNotes/ReleaseNotes-2.9.1.txt
@@ -4,8 +4,8 @@
 There are no schema changes from link:ReleaseNotes-2.9.html[2.9].
 
 Download:
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.9.1.war[
-https://gerrit-releases.storage.googleapis.com/gerrit-2.9.1.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.9.1.war[
+https://www.gerritcodereview.com/download/gerrit-2.9.1.war]
 
 *WARNING:* When upgrading from version 2.8.4 or older with a site that uses
 Bouncy Castle Crypto, new versions of the libraries will be downloaded. The old
diff --git a/ReleaseNotes/ReleaseNotes-2.9.2.txt b/ReleaseNotes/ReleaseNotes-2.9.2.txt
index a586b45..4e5de01 100644
--- a/ReleaseNotes/ReleaseNotes-2.9.2.txt
+++ b/ReleaseNotes/ReleaseNotes-2.9.2.txt
@@ -2,8 +2,8 @@
 ==============================
 
 Download:
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.9.2.war[
-https://gerrit-releases.storage.googleapis.com/gerrit-2.9.2.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.9.2.war[
+https://www.gerritcodereview.com/download/gerrit-2.9.2.war]
 
 Important Notes
 ---------------
diff --git a/ReleaseNotes/ReleaseNotes-2.9.3.txt b/ReleaseNotes/ReleaseNotes-2.9.3.txt
index f3fcf16..e6c8573 100644
--- a/ReleaseNotes/ReleaseNotes-2.9.3.txt
+++ b/ReleaseNotes/ReleaseNotes-2.9.3.txt
@@ -2,8 +2,8 @@
 ==============================
 
 Download:
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.9.3.war[
-https://gerrit-releases.storage.googleapis.com/gerrit-2.9.3.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.9.3.war[
+https://www.gerritcodereview.com/download/gerrit-2.9.3.war]
 
 Important Notes
 ---------------
diff --git a/ReleaseNotes/ReleaseNotes-2.9.4.txt b/ReleaseNotes/ReleaseNotes-2.9.4.txt
index 0a7010d..5063489 100644
--- a/ReleaseNotes/ReleaseNotes-2.9.4.txt
+++ b/ReleaseNotes/ReleaseNotes-2.9.4.txt
@@ -2,8 +2,8 @@
 ==============================
 
 Download:
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.9.4.war[
-https://gerrit-releases.storage.googleapis.com/gerrit-2.9.4.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.9.4.war[
+https://www.gerritcodereview.com/download/gerrit-2.9.4.war]
 
 Important Notes
 ---------------
diff --git a/ReleaseNotes/ReleaseNotes-2.9.txt b/ReleaseNotes/ReleaseNotes-2.9.txt
index de5c665..3387f98 100644
--- a/ReleaseNotes/ReleaseNotes-2.9.txt
+++ b/ReleaseNotes/ReleaseNotes-2.9.txt
@@ -4,8 +4,8 @@
 
 Gerrit 2.9 is now available:
 
-link:https://gerrit-releases.storage.googleapis.com/gerrit-2.9.war[
-https://gerrit-releases.storage.googleapis.com/gerrit-2.9.war]
+link:https://www.gerritcodereview.com/download/gerrit-2.9.war[
+https://www.gerritcodereview.com/download/gerrit-2.9.war]
 
 *WARNING:* Support for Java 1.6 has been discontinued.
 As of Gerrit 2.9, Java 1.7 is required.
diff --git a/ReleaseNotes/index.txt b/ReleaseNotes/index.txt
index 41d80a6..4cfb15a 100644
--- a/ReleaseNotes/index.txt
+++ b/ReleaseNotes/index.txt
@@ -1,6 +1,16 @@
 Gerrit Code Review - Release Notes
 ==================================
 
+[[2_12]]
+Version 2.12.x
+--------------
+* link:ReleaseNotes-2.12.5.html[2.12.5]
+* link:ReleaseNotes-2.12.4.html[2.12.4]
+* link:ReleaseNotes-2.12.3.html[2.12.3]
+* link:ReleaseNotes-2.12.2.html[2.12.2]
+* link:ReleaseNotes-2.12.1.html[2.12.1]
+* link:ReleaseNotes-2.12.html[2.12]
+
 [[2_11]]
 Version 2.11.x
 --------------
@@ -156,4 +166,4 @@
 
 GERRIT
 ------
-Part of link:http://code.google.com/p/gerrit/[Gerrit Code Review]
+Part of link:https://www.gerritcodereview.com/[Gerrit Code Review]
diff --git a/VERSION b/VERSION
index 3fbbc46..1bcaa4b 100644
--- a/VERSION
+++ b/VERSION
@@ -2,4 +2,4 @@
 # Used by :api_install and :api_deploy targets
 # when talking to the destination repository.
 #
-GERRIT_VERSION = '2.11.12'
+GERRIT_VERSION = '2.12.9'
diff --git a/bucklets/gerrit_plugin.bucklet b/bucklets/gerrit_plugin.bucklet
index ae7e1a2..367fe71 100644
--- a/bucklets/gerrit_plugin.bucklet
+++ b/bucklets/gerrit_plugin.bucklet
@@ -14,7 +14,8 @@
 # When compiling from standalone cookbook-plugin, bucklets directory points
 # to cloned bucklets library that includes real gerrit_plugin.bucklet code.
 
+GERRIT_GWT_API = ['//gerrit-plugin-gwtui:gwtui-api']
 GERRIT_PLUGIN_API = ['//gerrit-plugin-api:lib']
-GERRIT_GWT_API = ['//gerrit-plugin-gwtui/gerrit:gwtui-api']
+GERRIT_TESTS = ['//gerrit-acceptance-framework:lib']
 
 STANDALONE_MODE = False
diff --git a/bucklets/local_jar.bucklet b/bucklets/local_jar.bucklet
deleted file mode 120000
index 8904824..0000000
--- a/bucklets/local_jar.bucklet
+++ /dev/null
@@ -1 +0,0 @@
-../lib/local.defs
\ No newline at end of file
diff --git a/contrib/abandon_stale.py b/contrib/abandon_stale.py
new file mode 100755
index 0000000..32edf84
--- /dev/null
+++ b/contrib/abandon_stale.py
@@ -0,0 +1,185 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# The MIT License
+#
+# Copyright 2014 Sony Mobile Communications. All rights reserved.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+""" Script to abandon stale changes from the review server.
+
+Fetches a list of open changes that have not been updated since a
+given age in months or years (default 6 months), and then abandons them.
+
+Assumes that the user's credentials are in the .netrc file.  Supports
+either basic or digest authentication.
+
+Example to abandon changes that have not been updated for 3 months:
+
+  ./abandon_stale --gerrit-url http://review.example.com/ --age 3months
+
+Supports dry-run mode to only list the stale changes but not actually
+abandon them.
+
+Requires pygerrit (https://github.com/sonyxperiadev/pygerrit).
+
+"""
+
+import logging
+import optparse
+import re
+import sys
+
+from pygerrit.rest import GerritRestAPI
+from pygerrit.rest.auth import HTTPBasicAuthFromNetrc, HTTPDigestAuthFromNetrc
+
+
+def _main():
+    parser = optparse.OptionParser()
+    parser.add_option('-g', '--gerrit-url', dest='gerrit_url',
+                      metavar='URL',
+                      default=None,
+                      help='gerrit server URL')
+    parser.add_option('-b', '--basic-auth', dest='basic_auth',
+                      action='store_true',
+                      help='use HTTP basic authentication instead of digest')
+    parser.add_option('-n', '--dry-run', dest='dry_run',
+                      action='store_true',
+                      help='enable dry-run mode: show stale changes but do '
+                           'not abandon them')
+    parser.add_option('-a', '--age', dest='age',
+                      metavar='AGE',
+                      default="6months",
+                      help='age of change since last update '
+                           '(default: %default)')
+    parser.add_option('-m', '--message', dest='message',
+                      metavar='STRING', default=None,
+                      help='Custom message to append to abandon message')
+    parser.add_option('--exclude-branch', dest='exclude_branches',
+                      metavar='BRANCH_NAME',
+                      default=[],
+                      action='append',
+                      help='Do not abandon changes on given branch')
+    parser.add_option('--exclude-project', dest='exclude_projects',
+                      metavar='PROJECT_NAME',
+                      default=[],
+                      action='append',
+                      help='Do not abandon changes on given project')
+    parser.add_option('--owner', dest='owner',
+                      metavar='USERNAME',
+                      default=None,
+                      action='store',
+                      help='Only abandon changes owned by the given user')
+    parser.add_option('-v', '--verbose', dest='verbose',
+                      action='store_true',
+                      help='enable verbose (debug) logging')
+
+    (options, _args) = parser.parse_args()
+
+    level = logging.DEBUG if options.verbose else logging.INFO
+    logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s',
+                        level=level)
+
+    if not options.gerrit_url:
+        logging.error("Gerrit URL is required")
+        return 1
+
+    pattern = re.compile(r"^([\d]+)(month[s]?|year[s]?|week[s]?)")
+    match = pattern.match(options.age)
+    if not match:
+        logging.error("Invalid age: %s", options.age)
+        return 1
+    message = "Abandoning after %s %s or more of inactivity." % \
+        (match.group(1), match.group(2))
+
+    if options.basic_auth:
+        auth_type = HTTPBasicAuthFromNetrc
+    else:
+        auth_type = HTTPDigestAuthFromNetrc
+
+    try:
+        auth = auth_type(url=options.gerrit_url)
+        gerrit = GerritRestAPI(url=options.gerrit_url, auth=auth)
+    except Exception as e:
+        logging.error(e)
+        return 1
+
+    logging.info(message)
+    try:
+        stale_changes = []
+        offset = 0
+        step = 500
+        query_terms = ["status:new", "age:%s" % options.age] + \
+                      ["-branch:%s" % b for b in options.exclude_branches] + \
+                      ["-project:%s" % p for p in options.exclude_projects]
+        if options.owner:
+            query_terms += ["owner:%s" % options.owner]
+        query = "%20".join(query_terms)
+        while True:
+            q = query + "&n=%d&S=%d" % (step, offset)
+            logging.debug("Query: %s", q)
+            url = "/changes/?q=" + q
+            result = gerrit.get(url)
+            logging.debug("%d changes", len(result))
+            if not result:
+                break
+            stale_changes += result
+            last = result[-1]
+            if "_more_changes" in last:
+                logging.debug("More...")
+                offset += step
+            else:
+                break
+    except Exception as e:
+        logging.error(e)
+        return 1
+
+    abandoned = 0
+    errors = 0
+    abandon_message = message
+    if options.message:
+        abandon_message += "\n\n" + options.message
+    for change in stale_changes:
+        number = change["_number"]
+        try:
+            owner = change["owner"]["name"]
+        except:
+            owner = "Unknown"
+        subject = change["subject"]
+        if len(subject) > 70:
+            subject = subject[:65] + " [...]"
+        change_id = change["id"]
+        logging.info("%s (%s): %s", number, owner, subject)
+        if options.dry_run:
+            continue
+
+        try:
+            gerrit.post("/changes/" + change_id + "/abandon",
+                        data='{"message" : "%s"}' % abandon_message)
+            abandoned += 1
+        except Exception as e:
+            errors += 1
+            logging.error(e)
+    logging.info("Total %d stale open changes", len(stale_changes))
+    if not options.dry_run:
+        logging.info("Abandoned %d changes. %d errors.", abandoned, errors)
+
+if __name__ == "__main__":
+    sys.exit(_main())
diff --git a/contrib/convertkey/BUCK b/contrib/convertkey/BUCK
new file mode 100644
index 0000000..40ad9c4
--- /dev/null
+++ b/contrib/convertkey/BUCK
@@ -0,0 +1,50 @@
+include_defs('//lib/maven.defs')
+
+genrule(
+  name = 'bcprov__unsign',
+  cmd = ' && '.join([
+    'unzip -qd $TMP $(location //lib/bouncycastle:bcprov)',
+    'cd $TMP',
+    'zip -Drq $OUT . -x META-INF/\*.RSA META-INF/\*.DSA META-INF/\*.SF META-INF/\*.LIST',
+  ]),
+  out = 'bcprov-unsigned.jar',
+)
+
+prebuilt_jar(
+  name = 'bcprov',
+  binary_jar = ':bcprov__unsign',
+)
+
+genrule(
+  name = 'bcpkix__unsign',
+  cmd = ' && '.join([
+    'unzip -qd $TMP $(location //lib/bouncycastle:bcpkix)',
+    'cd $TMP',
+    'zip -Drq $OUT . -x META-INF/\*.RSA META-INF/\*.DSA META-INF/\*.SF META-INF/\*.LIST',
+  ]),
+  out = 'bcpkix-unsigned.jar',
+)
+
+prebuilt_jar(
+  name = 'bcpkix',
+  binary_jar = ':bcpkix__unsign',
+)
+
+java_library(
+  name = 'convertkey__lib',
+  srcs = glob(['src/main/java/**/*.java']),
+  deps = [
+    ':bcprov',
+    ':bcpkix',
+    '//lib:jsch',
+    '//lib/log:nop',
+    '//lib/mina:sshd',
+  ],
+)
+
+java_binary(
+  name = 'convertkey',
+  deps = [':convertkey__lib'],
+  main_class = 'com.googlesource.gerrit.convertkey.ConvertKey',
+)
+
diff --git a/contrib/convertkey/src/main/java/com/googlesource/gerrit/convertkey/ConvertKey.java b/contrib/convertkey/src/main/java/com/googlesource/gerrit/convertkey/ConvertKey.java
new file mode 100644
index 0000000..5c6ef58
--- /dev/null
+++ b/contrib/convertkey/src/main/java/com/googlesource/gerrit/convertkey/ConvertKey.java
@@ -0,0 +1,73 @@
+// Copyright (C) 2015 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.googlesource.gerrit.convertkey;
+
+import com.jcraft.jsch.HostKey;
+import com.jcraft.jsch.JSchException;
+
+import org.apache.sshd.common.util.Buffer;
+import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
+
+import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.security.KeyPair;
+import java.security.GeneralSecurityException;
+
+public class ConvertKey {
+  public static void main(String[] args)
+      throws GeneralSecurityException, JSchException, IOException {
+    SimpleGeneratorHostKeyProvider p;
+
+    if (args.length != 1) {
+      System.err.println("Error: requires path to the SSH host key");
+      return;
+    } else {
+      File file = new File(args[0]);
+      if (!file.exists() || !file.isFile() || !file.canRead()) {
+        System.err.println("Error: ssh key should exist and be readable");
+        return;
+      }
+    }
+
+    p = new SimpleGeneratorHostKeyProvider();
+    // Gerrit's SSH "simple" keys are always RSA.
+    p.setPath(args[0]);
+    p.setAlgorithm("RSA");
+    Iterable<KeyPair> keys = p.loadKeys(); // forces the key to generate.
+    for (KeyPair k : keys) {
+      System.out.println("Public Key (" + k.getPublic().getAlgorithm() + "):");
+      // From Gerrit's SshDaemon class; use JSch to get the public
+      // key/type
+      final Buffer buf = new Buffer();
+      buf.putRawPublicKey(k.getPublic());
+      final byte[] keyBin = buf.getCompactData();
+      HostKey pub = new HostKey("localhost", keyBin);
+      System.out.println(pub.getType() + " " + pub.getKey());
+      System.out.println("Private Key:");
+      // Use Bouncy Castle to write the private key back in PEM format
+      // (PKCS#1)
+      // http://stackoverflow.com/questions/25129822/export-rsa-public-key-to-pem-string-using-java
+      StringWriter privout = new StringWriter();
+      JcaPEMWriter privWriter = new JcaPEMWriter(privout);
+      privWriter.writeObject(k.getPrivate());
+      privWriter.close();
+      System.out.println(privout);
+    }
+  }
+
+}
diff --git a/contrib/git-push-review b/contrib/git-push-review
index 898b023..e77785a 100755
--- a/contrib/git-push-review
+++ b/contrib/git-push-review
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
 # Copyright (C) 2014 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/gerrit-acceptance-framework/BUCK b/gerrit-acceptance-framework/BUCK
new file mode 100644
index 0000000..d8f0276
--- /dev/null
+++ b/gerrit-acceptance-framework/BUCK
@@ -0,0 +1,87 @@
+SRCS = glob(['src/test/java/com/google/gerrit/acceptance/*.java'])
+
+DEPS = [
+  '//gerrit-gpg:gpg',
+  '//gerrit-pgm:daemon',
+  '//gerrit-pgm:util-nodep',
+  '//gerrit-server:testutil',
+  '//lib/auto:auto-value',
+  '//lib/httpcomponents:fluent-hc',
+  '//lib/httpcomponents:httpclient',
+  '//lib/httpcomponents:httpcore',
+  '//lib/jgit:junit',
+  '//lib/log:impl_log4j',
+  '//lib/log:log4j',
+]
+
+PROVIDED = [
+  '//gerrit-common:annotations',
+  '//gerrit-common:server',
+  '//gerrit-extension-api:api',
+  '//gerrit-httpd:httpd',
+  '//gerrit-lucene:lucene',
+  '//gerrit-pgm:init',
+  '//gerrit-reviewdb:server',
+  '//gerrit-server:server',
+  '//lib:gson',
+  '//lib/jgit:jgit',
+  '//lib:jsch',
+  '//lib/mina:sshd',
+  '//lib:servlet-api-3_1',
+]
+
+java_binary(
+  name = 'acceptance-framework',
+  deps = [':lib'],
+  visibility = ['PUBLIC'],
+)
+
+java_library(
+  name = 'lib',
+  srcs = SRCS,
+  exported_deps = DEPS + [
+    '//lib:truth',
+  ],
+  provided_deps = PROVIDED + [
+    '//lib:gwtorm',
+    '//lib/guice:guice',
+    '//lib/guice:guice-assistedinject',
+    '//lib/guice:guice-servlet',
+  ],
+  visibility = ['PUBLIC'],
+)
+
+java_sources(
+  name = 'src',
+  srcs = SRCS,
+  visibility = ['PUBLIC'],
+)
+
+# The above java_sources produces a .jar somewhere in the depths of
+# buck-out, but it does not bring it to
+# buck-out/gen/gerrit-acceptance-framework/gerrit-acceptance-framework-src.jar.
+# We fix that by the following java_binary.
+java_binary(
+  name = 'acceptance-framework-src',
+  deps = [ ':src' ],
+  visibility = ['PUBLIC'],
+)
+
+java_doc(
+  name = 'acceptance-framework-javadoc',
+  title = 'Gerrit Acceptance Test Framework Documentation',
+  pkgs = [' com.google.gerrit.acceptance'],
+  paths = ['src/test/java'],
+  srcs = SRCS,
+  deps = DEPS + PROVIDED + [
+    '//lib:guava',
+    '//lib/guice:guice-assistedinject',
+    '//lib/guice:guice_library',
+    '//lib/guice:guice-servlet',
+    '//lib/guice:javax-inject',
+    '//lib:gwtorm_client',
+    '//lib:junit__jar',
+    '//lib:truth__jar',
+  ],
+  visibility = ['PUBLIC'],
+)
diff --git a/gerrit-acceptance-framework/pom.xml b/gerrit-acceptance-framework/pom.xml
new file mode 100644
index 0000000..28f9527
--- /dev/null
+++ b/gerrit-acceptance-framework/pom.xml
@@ -0,0 +1,59 @@
+<project>
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>com.google.gerrit</groupId>
+  <artifactId>gerrit-acceptance-framework</artifactId>
+  <version>2.12.9</version>
+  <packaging>jar</packaging>
+  <name>Gerrit Code Review - Acceptance Test Framework</name>
+  <description>API for Gerrit Plugins</description>
+  <url>https://www.gerritcodereview.com/</url>
+
+  <licenses>
+    <license>
+      <name>The Apache Software License, Version 2.0</name>
+      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+      <distribution>repo</distribution>
+    </license>
+  </licenses>
+
+  <scm>
+    <url>https://gerrit.googlesource.com/gerrit</url>
+    <connection>https://gerrit.googlesource.com/gerrit</connection>
+  </scm>
+
+  <developers>
+    <developer>
+      <name>Dave Borowitz</name>
+    </developer>
+    <developer>
+      <name>David Pursehouse</name>
+    </developer>
+    <developer>
+      <name>Edwin Kempin</name>
+    </developer>
+    <developer>
+      <name>Martin Fick</name>
+    </developer>
+    <developer>
+      <name>Saša Živkov</name>
+    </developer>
+    <developer>
+      <name>Shawn Pearce</name>
+    </developer>
+  </developers>
+
+  <mailingLists>
+    <mailingList>
+      <name>Repo and Gerrit Discussion</name>
+      <post>repo-discuss@googlegroups.com</post>
+      <subscribe>https://groups.google.com/forum/#!forum/repo-discuss</subscribe>
+      <unsubscribe>https://groups.google.com/forum/#!forum/repo-discuss</unsubscribe>
+      <archive>https://groups.google.com/forum/#!forum/repo-discuss</archive>
+    </mailingList>
+  </mailingLists>
+
+  <issueManagement>
+    <url>https://bugs.chromium.org/p/gerrit/issues/list</url>
+    <system>Gerrit Issue Tracker</system>
+  </issueManagement>
+</project>
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
new file mode 100644
index 0000000..9194371
--- /dev/null
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -0,0 +1,665 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.GitUtil.initSsh;
+import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.server.project.Util.block;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Strings;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import com.google.common.primitives.Chars;
+import com.google.gerrit.acceptance.AcceptanceTestRequestScope.Context;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.extensions.api.GerritApi;
+import com.google.gerrit.extensions.api.changes.ReviewInput;
+import com.google.gerrit.extensions.api.changes.RevisionApi;
+import com.google.gerrit.extensions.api.projects.BranchApi;
+import com.google.gerrit.extensions.api.projects.BranchInput;
+import com.google.gerrit.extensions.api.projects.ProjectInput;
+import com.google.gerrit.extensions.client.InheritableBoolean;
+import com.google.gerrit.extensions.client.ListChangesOption;
+import com.google.gerrit.extensions.client.SubmitType;
+import com.google.gerrit.extensions.common.ActionInfo;
+import com.google.gerrit.extensions.common.ChangeInfo;
+import com.google.gerrit.extensions.common.EditInfo;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.reviewdb.client.Branch;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.AnonymousUser;
+import com.google.gerrit.server.GerritPersonIdent;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.OutputFormat;
+import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.MetaDataUpdate;
+import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.index.ChangeIndexer;
+import com.google.gerrit.server.notedb.NotesMigration;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.Util;
+import com.google.gerrit.server.query.change.InternalChangeQuery;
+import com.google.gerrit.testutil.ConfigSuite;
+import com.google.gerrit.testutil.TempFileUtil;
+import com.google.gson.Gson;
+import com.google.gwtorm.server.SchemaFactory;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.util.Providers;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.Transport;
+import org.junit.AfterClass;
+import org.junit.Rule;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+import org.junit.runners.model.Statement;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+@RunWith(ConfigSuite.class)
+public abstract class AbstractDaemonTest {
+  private static GerritServer commonServer;
+
+  @ConfigSuite.Parameter
+  public Config baseConfig;
+
+  @ConfigSuite.Name
+  private String configName;
+
+  @Inject
+  protected AllProjectsName allProjects;
+
+  @Inject
+  protected AccountCreator accounts;
+
+  @Inject
+  private SchemaFactory<ReviewDb> reviewDbProvider;
+
+  @Inject
+  protected GerritApi gApi;
+
+  @Inject
+  protected AcceptanceTestRequestScope atrScope;
+
+  @Inject
+  protected AccountCache accountCache;
+
+  @Inject
+  private IdentifiedUser.GenericFactory identifiedUserFactory;
+
+  @Inject
+  protected PushOneCommit.Factory pushFactory;
+
+  @Inject
+  protected MetaDataUpdate.Server metaDataUpdateFactory;
+
+  @Inject
+  protected ProjectCache projectCache;
+
+  @Inject
+  protected GroupCache groupCache;
+
+  @Inject
+  protected GitRepositoryManager repoManager;
+
+  @Inject
+  protected ChangeIndexer indexer;
+
+  @Inject
+  protected Provider<InternalChangeQuery> queryProvider;
+
+  @Inject
+  @CanonicalWebUrl
+  protected Provider<String> canonicalWebUrl;
+
+  @Inject
+  @GerritServerConfig
+  protected Config cfg;
+
+  @Inject
+  private InProcessProtocol inProcessProtocol;
+
+  @Inject
+  private Provider<AnonymousUser> anonymousUser;
+
+  @Inject
+  @GerritPersonIdent
+  protected Provider<PersonIdent> serverIdent;
+
+  protected TestRepository<InMemoryRepository> testRepo;
+  protected GerritServer server;
+  protected TestAccount admin;
+  protected TestAccount user;
+  protected RestSession adminSession;
+  protected RestSession userSession;
+  protected SshSession sshSession;
+  protected ReviewDb db;
+  protected Project.NameKey project;
+
+  @Inject
+  protected NotesMigration notesMigration;
+
+  @Rule
+  public ExpectedException exception = ExpectedException.none();
+
+  private String resourcePrefix;
+  private List<Repository> toClose;
+
+  @Rule
+  public TestRule testRunner = new TestRule() {
+    @Override
+    public Statement apply(final Statement base, final Description description) {
+      return new Statement() {
+        @Override
+        public void evaluate() throws Throwable {
+          beforeTest(description);
+          try {
+            base.evaluate();
+          } finally {
+            afterTest();
+          }
+        }
+      };
+    }
+  };
+
+  @Rule
+  public TemporaryFolder tempSiteDir = new TemporaryFolder();
+
+  @AfterClass
+  public static void stopCommonServer() throws Exception {
+    if (commonServer != null) {
+      commonServer.stop();
+      commonServer = null;
+    }
+    TempFileUtil.cleanup();
+  }
+
+  protected static Config submitWholeTopicEnabledConfig() {
+    Config cfg = new Config();
+    cfg.setBoolean("change", null, "submitWholeTopic", true);
+    return cfg;
+  }
+
+  protected static Config allowDraftsDisabledConfig() {
+    Config cfg = new Config();
+    cfg.setBoolean("change", null, "allowDrafts", false);
+    return cfg;
+  }
+
+  protected boolean isAllowDrafts() {
+    return cfg.getBoolean("change", "allowDrafts", true);
+  }
+
+  protected boolean isSubmitWholeTopicEnabled() {
+    return cfg.getBoolean("change", null, "submitWholeTopic", false);
+  }
+
+  private static boolean isNoteDbTestEnabled() {
+    final String[] RUN_FLAGS = {"yes", "y", "true"};
+    String value = System.getenv("GERRIT_ENABLE_NOTEDB");
+    return value != null &&
+        Arrays.asList(RUN_FLAGS).contains(value.toLowerCase());
+  }
+
+  protected void beforeTest(Description description) throws Exception {
+    GerritServer.Description classDesc =
+      GerritServer.Description.forTestClass(description, configName);
+    GerritServer.Description methodDesc =
+      GerritServer.Description.forTestMethod(description, configName);
+
+    if (isNoteDbTestEnabled()) {
+      NotesMigration.setAllEnabledConfig(baseConfig);
+    }
+    baseConfig.setString("gerrit", null, "tempSiteDir",
+        tempSiteDir.getRoot().getPath());
+    if (classDesc.equals(methodDesc) && !classDesc.sandboxed() &&
+        !methodDesc.sandboxed()) {
+      if (commonServer == null) {
+        commonServer = GerritServer.start(classDesc, baseConfig);
+      }
+      server = commonServer;
+    } else {
+      server = GerritServer.start(methodDesc, baseConfig);
+    }
+
+    server.getTestInjector().injectMembers(this);
+    Transport.register(inProcessProtocol);
+    toClose = Collections.synchronizedList(new ArrayList<Repository>());
+    admin = accounts.admin();
+    user = accounts.user();
+
+    // Evict cached user state in case tests modify it.
+    accountCache.evict(admin.getId());
+    accountCache.evict(user.getId());
+
+    adminSession = new RestSession(server, admin);
+    userSession = new RestSession(server, user);
+    initSsh(admin);
+    db = reviewDbProvider.open();
+    Context ctx = newRequestContext(admin);
+    atrScope.set(ctx);
+    sshSession = ctx.getSession();
+    sshSession.open();
+    resourcePrefix = UNSAFE_PROJECT_NAME.matcher(
+        description.getClassName() + "_"
+        + description.getMethodName() + "_").replaceAll("");
+
+    project = createProject(projectInput(description));
+    testRepo = cloneProject(project, getCloneAsAccount(description));
+  }
+
+  private TestAccount getCloneAsAccount(Description description) {
+    TestProjectInput ann = description.getAnnotation(TestProjectInput.class);
+    return accounts.get(ann != null ? ann.cloneAs() : "admin");
+  }
+
+  private ProjectInput projectInput(Description description) {
+    ProjectInput in = new ProjectInput();
+    TestProjectInput ann = description.getAnnotation(TestProjectInput.class);
+    in.name = name("project");
+    if (ann != null) {
+      in.parent = Strings.emptyToNull(ann.parent());
+      in.description = Strings.emptyToNull(ann.description());
+      in.createEmptyCommit = ann.createEmptyCommit();
+      in.submitType = ann.submitType();
+      in.useContentMerge = ann.useContributorAgreements();
+      in.useSignedOffBy = ann.useSignedOffBy();
+      in.useContentMerge = ann.useContentMerge();
+    } else {
+      // Defaults should match TestProjectConfig, omitting nullable values.
+      in.createEmptyCommit = true;
+    }
+    updateProjectInput(in);
+    return in;
+  }
+
+  private static final Pattern UNSAFE_PROJECT_NAME =
+      Pattern.compile("[^a-zA-Z0-9._/-]+");
+
+  protected Git git() {
+    return testRepo.git();
+  }
+
+  protected InMemoryRepository repo() {
+    return testRepo.getRepository();
+  }
+
+  /**
+   * Return a resource name scoped to this test method.
+   * <p>
+   * Test methods in a single class by default share a running server. For any
+   * resource name you require to be unique to a test method, wrap it in a call
+   * to this method.
+   *
+   * @param name resource name (group, project, topic, etc.)
+   * @return name prefixed by a string unique to this test method.
+   */
+  protected String name(String name) {
+    return resourcePrefix + name;
+  }
+
+  protected Project.NameKey createProject(String nameSuffix)
+      throws RestApiException {
+    return createProject(nameSuffix, null);
+  }
+
+  protected Project.NameKey createProject(String nameSuffix,
+      Project.NameKey parent) throws RestApiException {
+    // Default for createEmptyCommit should match TestProjectConfig.
+    return createProject(nameSuffix, parent, true);
+  }
+
+  protected Project.NameKey createProject(String nameSuffix,
+      Project.NameKey parent, boolean createEmptyCommit)
+      throws RestApiException {
+    return createProject(
+        nameSuffix, parent, createEmptyCommit, SubmitType.MERGE_IF_NECESSARY);
+  }
+
+  protected Project.NameKey createProject(String nameSuffix,
+      Project.NameKey parent, boolean createEmptyCommit, SubmitType submitType)
+      throws RestApiException {
+    ProjectInput in = new ProjectInput();
+    in.name = name(nameSuffix);
+    in.parent = parent != null ? parent.get() : null;
+    in.createEmptyCommit = createEmptyCommit;
+    in.submitType = submitType;
+    return createProject(in);
+  }
+
+  private Project.NameKey createProject(ProjectInput in)
+      throws RestApiException {
+    gApi.projects().create(in);
+    return new Project.NameKey(in.name);
+  }
+
+  /**
+   * Modify a project input before creating the initial test project.
+   *
+   * @param in input; may be modified in place.
+   */
+  protected void updateProjectInput(ProjectInput in) {
+    // Default implementation does nothing.
+  }
+
+  protected TestRepository<InMemoryRepository> cloneProject(Project.NameKey p)
+      throws Exception {
+    return cloneProject(p, admin);
+  }
+
+  protected TestRepository<InMemoryRepository> cloneProject(Project.NameKey p,
+      TestAccount testAccount) throws Exception {
+    InProcessProtocol.Context ctx = new InProcessProtocol.Context(
+        reviewDbProvider, identifiedUserFactory, testAccount.getId(), p);
+    Repository repo = repoManager.openRepository(p);
+    toClose.add(repo);
+    return GitUtil.cloneProject(
+        p, inProcessProtocol.register(ctx, repo).toString());
+  }
+
+  private void afterTest() throws Exception {
+    Transport.unregister(inProcessProtocol);
+    for (Repository repo : toClose) {
+      repo.close();
+    }
+    db.close();
+    sshSession.close();
+    if (server != commonServer) {
+      server.stop();
+    }
+  }
+
+  protected TestRepository<?>.CommitBuilder commitBuilder() throws Exception {
+    return testRepo.branch("HEAD").commit().insertChangeId();
+  }
+
+  protected TestRepository<?>.CommitBuilder amendBuilder() throws Exception {
+    ObjectId head = repo().exactRef("HEAD").getObjectId();
+    TestRepository<?>.CommitBuilder b = testRepo.amendRef("HEAD");
+    Optional<String> id = GitUtil.getChangeId(testRepo, head);
+    // TestRepository behaves like "git commit --amend -m foo", which does not
+    // preserve an existing Change-Id. Tests probably want this.
+    if (id.isPresent()) {
+      b.insertChangeId(id.get().substring(1));
+    } else {
+      b.insertChangeId();
+    }
+    return b;
+  }
+
+  protected PushOneCommit.Result createChange() throws Exception {
+    PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
+    PushOneCommit.Result result = push.to("refs/for/master");
+    result.assertOkStatus();
+    return result;
+  }
+
+  protected BranchApi createBranchWithRevision(Branch.NameKey branch,
+      String revision) throws Exception {
+    BranchInput in = new BranchInput();
+    in.revision = revision;
+    return gApi.projects()
+        .name(branch.getParentKey().get())
+        .branch(branch.get())
+        .create(in);
+  }
+
+  private static final List<Character> RANDOM =
+      Chars.asList(new char[]{'a','b','c','d','e','f','g','h'});
+  protected PushOneCommit.Result amendChange(String changeId)
+      throws Exception {
+    return amendChange(changeId, "refs/for/master");
+  }
+
+  protected PushOneCommit.Result amendChange(String changeId, String ref)
+      throws Exception {
+    Collections.shuffle(RANDOM);
+    PushOneCommit push =
+        pushFactory.create(db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT,
+            PushOneCommit.FILE_NAME, new String(Chars.toArray(RANDOM)), changeId);
+    return push.to(ref);
+  }
+
+  protected PushOneCommit.Result amendChangeAsDraft(String changeId)
+      throws Exception {
+    return amendChange(changeId, "refs/drafts/master");
+  }
+
+  protected ChangeInfo info(String id)
+      throws RestApiException {
+    return gApi.changes().id(id).info();
+  }
+
+  protected ChangeInfo get(String id)
+      throws RestApiException {
+    return gApi.changes().id(id).get();
+  }
+
+  protected EditInfo getEdit(String id)
+      throws RestApiException {
+    return gApi.changes().id(id).getEdit();
+  }
+
+  protected ChangeInfo get(String id, ListChangesOption... options)
+      throws RestApiException {
+    return gApi.changes().id(id).get(
+        Sets.newEnumSet(Arrays.asList(options), ListChangesOption.class));
+  }
+
+  protected List<ChangeInfo> query(String q) throws RestApiException {
+    return gApi.changes().query(q).get();
+  }
+
+  private Context newRequestContext(TestAccount account) {
+    return atrScope.newContext(reviewDbProvider, new SshSession(server, admin),
+        identifiedUserFactory.create(Providers.of(db), account.getId()));
+  }
+
+  protected Context setApiUser(TestAccount account) {
+    return atrScope.set(newRequestContext(account));
+  }
+
+  protected Context setApiUserAnonymous() {
+    return atrScope.newContext(reviewDbProvider, null, anonymousUser.get());
+  }
+
+  protected static Gson newGson() {
+    return OutputFormat.JSON_COMPACT.newGson();
+  }
+
+  protected RevisionApi revision(PushOneCommit.Result r) throws Exception {
+    return gApi.changes()
+        .id(r.getChangeId())
+        .current();
+  }
+
+  protected void allow(String permission, AccountGroup.UUID id, String ref)
+      throws Exception {
+    ProjectConfig cfg = projectCache.checkedGet(project).getConfig();
+    Util.allow(cfg, permission, id, ref);
+    saveProjectConfig(project, cfg);
+  }
+
+  protected void allowGlobalCapabilities(AccountGroup.UUID id,
+      String... capabilityNames) throws Exception {
+    allowGlobalCapabilities(id, Arrays.asList(capabilityNames));
+  }
+
+  protected void allowGlobalCapabilities(AccountGroup.UUID id,
+      Iterable<String> capabilityNames) throws Exception {
+    ProjectConfig cfg = projectCache.checkedGet(allProjects).getConfig();
+    for (String capabilityName : capabilityNames) {
+      Util.allow(cfg, capabilityName, id);
+    }
+    saveProjectConfig(allProjects, cfg);
+  }
+
+  protected void removeGlobalCapabilities(AccountGroup.UUID id,
+      String... capabilityNames) throws Exception {
+    removeGlobalCapabilities(id, Arrays.asList(capabilityNames));
+  }
+
+  protected void removeGlobalCapabilities(AccountGroup.UUID id,
+      Iterable<String> capabilityNames) throws Exception {
+    ProjectConfig cfg = projectCache.checkedGet(allProjects).getConfig();
+    for (String capabilityName : capabilityNames) {
+      Util.remove(cfg, capabilityName, id);
+    }
+    saveProjectConfig(allProjects, cfg);
+  }
+
+  protected void setUseContributorAgreements(InheritableBoolean value)
+      throws Exception {
+    MetaDataUpdate md = metaDataUpdateFactory.create(project);
+    ProjectConfig config = ProjectConfig.read(md);
+    config.getProject().setUseContributorAgreements(value);
+    config.commit(md);
+    projectCache.evict(config.getProject());
+  }
+
+  protected void setUseSignedOffBy(InheritableBoolean value)
+      throws Exception {
+    MetaDataUpdate md = metaDataUpdateFactory.create(project);
+    ProjectConfig config = ProjectConfig.read(md);
+    config.getProject().setUseSignedOffBy(value);
+    config.commit(md);
+    projectCache.evict(config.getProject());
+  }
+
+  protected void deny(String permission, AccountGroup.UUID id, String ref)
+      throws Exception {
+    ProjectConfig cfg = projectCache.checkedGet(project).getConfig();
+    Util.deny(cfg, permission, id, ref);
+    saveProjectConfig(project, cfg);
+  }
+
+  protected void saveProjectConfig(Project.NameKey p, ProjectConfig cfg)
+      throws Exception {
+    MetaDataUpdate md = metaDataUpdateFactory.create(p);
+    try {
+      cfg.commit(md);
+    } finally {
+      md.close();
+    }
+    projectCache.evict(cfg.getProject());
+  }
+
+  protected void grant(String permission, Project.NameKey project, String ref)
+      throws RepositoryNotFoundException, IOException, ConfigInvalidException {
+    grant(permission, project, ref, false);
+  }
+
+  protected void grant(String permission, Project.NameKey project, String ref,
+      boolean force) throws RepositoryNotFoundException, IOException,
+      ConfigInvalidException {
+    MetaDataUpdate md = metaDataUpdateFactory.create(project);
+    md.setMessage(String.format("Grant %s on %s", permission, ref));
+    ProjectConfig config = ProjectConfig.read(md);
+    AccessSection s = config.getAccessSection(ref, true);
+    Permission p = s.getPermission(permission, true);
+    AccountGroup adminGroup = groupCache.get(new AccountGroup.NameKey("Administrators"));
+    PermissionRule rule = new PermissionRule(config.resolve(adminGroup));
+    rule.setForce(force);
+    p.add(rule);
+    config.commit(md);
+    projectCache.evict(config.getProject());
+  }
+
+  protected void blockRead(Project.NameKey project, String ref) throws Exception {
+    ProjectConfig cfg = projectCache.checkedGet(project).getConfig();
+    block(cfg, Permission.READ, REGISTERED_USERS, ref);
+    saveProjectConfig(project, cfg);
+  }
+
+  protected void blockForgeCommitter(Project.NameKey project, String ref)
+      throws Exception {
+    ProjectConfig cfg = projectCache.checkedGet(project).getConfig();
+    block(cfg, Permission.FORGE_COMMITTER, REGISTERED_USERS, ref);
+    saveProjectConfig(project, cfg);
+  }
+
+  protected PushOneCommit.Result pushTo(String ref) throws Exception {
+    PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
+    return push.to(ref);
+  }
+
+  protected void approve(String id) throws Exception {
+    gApi.changes()
+      .id(id)
+      .revision("current")
+      .review(ReviewInput.approve());
+  }
+
+  protected Map<String, ActionInfo> getActions(String id) throws Exception {
+    return gApi.changes()
+      .id(id)
+      .revision(1)
+      .actions();
+  }
+
+  protected void assertSubmittedTogether(String chId, String... expected)
+      throws Exception {
+    List<ChangeInfo> actual = gApi.changes().id(chId).submittedTogether();
+    assertThat(actual).hasSize(expected.length);
+    assertThat(Iterables.transform(actual,
+        new Function<ChangeInfo, String>() {
+      @Override
+      public String apply(ChangeInfo input) {
+        return input.changeId;
+      }
+    })).containsExactly((Object[])expected).inOrder();
+  }
+
+  protected TestRepository<?> createProjectWithPush(String name,
+      @Nullable Project.NameKey parent,
+      SubmitType submitType) throws Exception {
+    Project.NameKey project = createProject(name, parent, true, submitType);
+    grant(Permission.PUSH, project, "refs/heads/*");
+    grant(Permission.SUBMIT, project, "refs/for/refs/heads/*");
+    return cloneProject(project);
+  }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java
similarity index 89%
rename from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java
rename to gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java
index 2a578c2..34379a1 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java
@@ -23,6 +23,7 @@
 import com.google.gerrit.server.util.RequestContext;
 import com.google.gerrit.server.util.ThreadLocalRequestContext;
 import com.google.gerrit.server.util.ThreadLocalRequestScopePropagator;
+import com.google.gerrit.testutil.DisabledReviewDb;
 import com.google.gwtorm.server.SchemaFactory;
 import com.google.inject.Inject;
 import com.google.inject.Key;
@@ -41,7 +42,7 @@
   private static final Key<RequestScopedReviewDbProvider> DB_KEY =
       Key.get(RequestScopedReviewDbProvider.class);
 
-  public class Context implements RequestContext {
+  public static class Context implements RequestContext {
     private final RequestCleanup cleanup = new RequestCleanup();
     private final Map<Key<?>, Object> map = Maps.newHashMap();
     private final SchemaFactory<ReviewDb> schemaFactory;
@@ -75,7 +76,7 @@
     }
 
     @Override
-    public CurrentUser getCurrentUser() {
+    public CurrentUser getUser() {
       if (user == null) {
         throw new IllegalStateException("user == null, forgot to set it?");
       }
@@ -152,7 +153,7 @@
   }
 
   private Context newContinuingContext(Context ctx) {
-    return new Context(ctx, ctx.getSession(), ctx.getCurrentUser());
+    return new Context(ctx, ctx.getSession(), ctx.getUser());
   }
 
   public Context set(Context ctx) {
@@ -162,6 +163,25 @@
     return old;
   }
 
+  public Context get() {
+    return current.get();
+  }
+
+  public Context disableDb() {
+    Context old = current.get();
+    SchemaFactory<ReviewDb> sf = new SchemaFactory<ReviewDb>() {
+      @Override
+      public ReviewDb open() {
+        return new DisabledReviewDb();
+      }
+    };
+    Context ctx = new Context(sf, old.session, old.user, old.created);
+
+    current.set(ctx);
+    local.setContext(ctx);
+    return old;
+  }
+
   /** Returns exactly one instance per command executed. */
   static final Scope REQUEST = new Scope() {
     @Override
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AccountCreator.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AccountCreator.java
similarity index 86%
rename from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AccountCreator.java
rename to gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AccountCreator.java
index a376332..9eaf266 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AccountCreator.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AccountCreator.java
@@ -14,6 +14,8 @@
 
 package com.google.gerrit.acceptance;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import com.google.gerrit.common.TimeUtil;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountExternalId;
@@ -28,6 +30,7 @@
 import com.google.gwtorm.server.OrmException;
 import com.google.gwtorm.server.SchemaFactory;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
 
 import com.jcraft.jsch.JSch;
 import com.jcraft.jsch.JSchException;
@@ -36,8 +39,12 @@
 import java.io.ByteArrayOutputStream;
 import java.io.UnsupportedEncodingException;
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 
+@Singleton
 public class AccountCreator {
+  private final Map<String, TestAccount> accounts;
 
   private SchemaFactory<ReviewDb> reviewDbProvider;
   private GroupCache groupCache;
@@ -49,6 +56,7 @@
   AccountCreator(SchemaFactory<ReviewDb> schema, GroupCache groupCache,
       SshKeyCache sshKeyCache, AccountCache accountCache,
       AccountByEmailCache byEmailCache) {
+    accounts = new HashMap<>();
     reviewDbProvider = schema;
     this.groupCache = groupCache;
     this.sshKeyCache = sshKeyCache;
@@ -56,11 +64,14 @@
     this.byEmailCache = byEmailCache;
   }
 
-  public TestAccount create(String username, String email, String fullName,
-      String... groups)
+  public synchronized TestAccount create(String username, String email,
+      String fullName, String... groups)
       throws OrmException, UnsupportedEncodingException, JSchException {
-    ReviewDb db = reviewDbProvider.open();
-    try {
+    TestAccount account = accounts.get(username);
+    if (account != null) {
+      return account;
+    }
+    try (ReviewDb db = reviewDbProvider.open()) {
       Account.Id id = new Account.Id(db.nextAccountId());
       KeyPair sshKey = genSshKey();
       AccountSshKey key =
@@ -99,9 +110,10 @@
       accountCache.evictByUsername(username);
       byEmailCache.evict(email);
 
-      return new TestAccount(id, username, email, fullName, sshKey, httpPass);
-    } finally {
-      db.close();
+      account =
+          new TestAccount(id, username, email, fullName, sshKey, httpPass);
+      accounts.put(username, account);
+      return account;
     }
   }
 
@@ -137,6 +149,12 @@
     return create("user2", "user2@example.com", "User2");
   }
 
+  public TestAccount get(String username) {
+    return checkNotNull(
+        accounts.get(username),
+        "No TestAccount created for %s", username);
+  }
+
   private AccountExternalId.Key getEmailKey(String email) {
     return new AccountExternalId.Key(AccountExternalId.SCHEME_MAILTO, email);
   }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ConfigAnnotationParser.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/ConfigAnnotationParser.java
similarity index 75%
rename from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ConfigAnnotationParser.java
rename to gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/ConfigAnnotationParser.java
index 07d0f50..b07ed30 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ConfigAnnotationParser.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/ConfigAnnotationParser.java
@@ -15,11 +15,13 @@
 package com.google.gerrit.acceptance;
 
 import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
 
 import org.eclipse.jgit.lib.Config;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 
 class ConfigAnnotationParser {
   private static Splitter splitter = Splitter.on(".").trimResults();
@@ -45,9 +47,19 @@
   private static void parseAnnotation(Config cfg, GerritConfig c) {
     ArrayList<String> l = Lists.newArrayList(splitter.split(c.name()));
     if (l.size() == 2) {
-      cfg.setString(l.get(0), null, l.get(1), c.value());
+      if (!Strings.isNullOrEmpty(c.value())) {
+        cfg.setString(l.get(0), null, l.get(1), c.value());
+      } else {
+        String[] values = c.values();
+        cfg.setStringList(l.get(0), null, l.get(1), Arrays.asList(values));
+      }
     } else if (l.size() == 3) {
-      cfg.setString(l.get(0), l.get(1), l.get(2), c.value());
+      if (!Strings.isNullOrEmpty(c.value())) {
+        cfg.setString(l.get(0), l.get(1), l.get(2), c.value());
+      } else {
+        cfg.setStringList(l.get(0), l.get(1), l.get(2),
+            Arrays.asList(c.value()));
+      }
     } else {
       throw new IllegalArgumentException(
           "GerritConfig.name must be of the format"
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GcAssert.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GcAssert.java
similarity index 82%
rename from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GcAssert.java
rename to gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GcAssert.java
index 4c7289b2..5f8a8ed 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GcAssert.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GcAssert.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.acceptance;
 
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assert_;
 
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.git.GitRepositoryManager;
@@ -39,23 +39,26 @@
   public void assertHasPackFile(Project.NameKey... projects)
       throws RepositoryNotFoundException, IOException {
     for (Project.NameKey p : projects) {
-      assertTrue("Project " + p.get() + " has no pack files.",
-          getPackFiles(p).length > 0);
+      assert_()
+        .withFailureMessage("Project " + p.get() + " has no pack files.")
+        .that(getPackFiles(p))
+        .isNotEmpty();
     }
   }
 
   public void assertHasNoPackFile(Project.NameKey... projects)
       throws RepositoryNotFoundException, IOException {
     for (Project.NameKey p : projects) {
-      assertTrue("Project " + p.get() + " has pack files.",
-          getPackFiles(p).length == 0);
+      assert_()
+        .withFailureMessage("Project " + p.get() + " has pack files.")
+        .that(getPackFiles(p))
+        .isEmpty();
     }
   }
 
   private String[] getPackFiles(Project.NameKey p)
       throws RepositoryNotFoundException, IOException {
-    Repository repo = repoManager.openRepository(p);
-    try {
+    try (Repository repo = repoManager.openRepository(p)) {
       File packDir = new File(repo.getDirectory(), "objects/pack");
       return packDir.list(new FilenameFilter() {
         @Override
@@ -63,8 +66,6 @@
           return name.endsWith(".pack");
         }
       });
-    } finally {
-      repo.close();
     }
   }
 }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritConfig.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritConfig.java
similarity index 93%
rename from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritConfig.java
rename to gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritConfig.java
index 5cb1229..4b956a2 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritConfig.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritConfig.java
@@ -24,5 +24,6 @@
 @Retention(RUNTIME)
 public @interface GerritConfig {
   String name();
-  String value();
+  String value() default "";
+  String[] values() default "";
 }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritConfigs.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritConfigs.java
similarity index 100%
rename from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritConfigs.java
rename to gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritConfigs.java
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritServer.java
similarity index 62%
rename from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java
rename to gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritServer.java
index 3be8195..5b0d311 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritServer.java
@@ -14,15 +14,21 @@
 
 package com.google.gerrit.acceptance;
 
+import com.google.auto.value.AutoValue;
+import com.google.common.base.MoreObjects;
 import com.google.common.collect.ImmutableList;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.lucene.LuceneIndexModule;
 import com.google.gerrit.pgm.Daemon;
 import com.google.gerrit.pgm.Init;
-import com.google.gerrit.server.config.FactoryModule;
 import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.git.AsyncReceiveCommits;
 import com.google.gerrit.server.index.ChangeSchemas;
+import com.google.gerrit.server.ssh.NoSshModule;
 import com.google.gerrit.server.util.SocketUtil;
 import com.google.gerrit.server.util.SystemLog;
+import com.google.gerrit.testutil.FakeEmailSender;
 import com.google.gerrit.testutil.TempFileUtil;
 import com.google.inject.Injector;
 import com.google.inject.Key;
@@ -35,6 +41,7 @@
 import org.eclipse.jgit.util.FS;
 
 import java.io.File;
+import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
@@ -47,10 +54,69 @@
 import java.util.concurrent.TimeUnit;
 
 public class GerritServer {
+  @AutoValue
+  abstract static class Description {
+    static Description forTestClass(org.junit.runner.Description testDesc,
+        String configName) {
+      return new AutoValue_GerritServer_Description(
+          configName,
+          true, // @UseLocalDisk is only valid on methods.
+          !has(NoHttpd.class, testDesc.getTestClass()),
+          has(Sandboxed.class, testDesc.getTestClass()),
+          null, // @GerritConfig is only valid on methods.
+          null); // @GerritConfigs is only valid on methods.
+
+    }
+
+    static Description forTestMethod(org.junit.runner.Description testDesc,
+        String configName) {
+      return new AutoValue_GerritServer_Description(
+          configName,
+          testDesc.getAnnotation(UseLocalDisk.class) == null,
+          testDesc.getAnnotation(NoHttpd.class) == null
+            && !has(NoHttpd.class, testDesc.getTestClass()),
+          testDesc.getAnnotation(Sandboxed.class) != null ||
+              has(Sandboxed.class, testDesc.getTestClass()),
+          testDesc.getAnnotation(GerritConfig.class),
+          testDesc.getAnnotation(GerritConfigs.class));
+    }
+
+    private static boolean has(
+        Class<? extends Annotation> annotation, Class<?> clazz) {
+      for (; clazz != null; clazz = clazz.getSuperclass()) {
+        if (clazz.getAnnotation(annotation) != null) {
+          return true;
+        }
+      }
+      return false;
+    }
+
+    @Nullable abstract String configName();
+    abstract boolean memory();
+    abstract boolean httpd();
+    abstract boolean sandboxed();
+    @Nullable abstract GerritConfig config();
+    @Nullable abstract GerritConfigs configs();
+
+    private Config buildConfig(Config baseConfig) {
+      if (configs() != null && config() != null) {
+        throw new IllegalStateException(
+            "Use either @GerritConfigs or @GerritConfig not both");
+      }
+      if (configs() != null) {
+        return ConfigAnnotationParser.parse(baseConfig, configs());
+      } else if (config() != null) {
+        return ConfigAnnotationParser.parse(baseConfig, config());
+      } else {
+        return baseConfig;
+      }
+    }
+  }
 
   /** Returns fully started Gerrit server */
-  static GerritServer start(Config cfg, boolean memory, boolean enableHttpd)
+  static GerritServer start(Description desc, Config baseConfig)
       throws Exception {
+    Config cfg = desc.buildConfig(baseConfig);
     Logger.getLogger("com.google.gerrit").setLevel(Level.DEBUG);
     final CyclicBarrier serverStarted = new CyclicBarrier(2);
     final Daemon daemon = new Daemon(new Runnable() {
@@ -58,17 +124,16 @@
       public void run() {
         try {
           serverStarted.await();
-        } catch (InterruptedException e) {
-          throw new RuntimeException(e);
-        } catch (BrokenBarrierException e) {
+        } catch (InterruptedException | BrokenBarrierException e) {
           throw new RuntimeException(e);
         }
       }
     });
+    daemon.setEmailModuleForTesting(new FakeEmailSender.Module());
 
     final File site;
     ExecutorService daemonService = null;
-    if (memory) {
+    if (desc.memory()) {
       site = null;
       mergeTestConfig(cfg);
       // Set the log4j configuration to an invalid one to prevent system logs
@@ -78,10 +143,9 @@
       cfg.setBoolean("sshd", null, "requestLog", false);
       cfg.setBoolean("index", "lucene", "testInmemory", true);
       cfg.setString("gitweb", null, "cgi", "");
-      daemon.setEnableHttpd(enableHttpd);
+      daemon.setEnableHttpd(desc.httpd());
       daemon.setLuceneModule(new LuceneIndexModule(
-          ChangeSchemas.getLatest().getVersion(),
-          Runtime.getRuntime().availableProcessors(), null));
+          ChangeSchemas.getLatest().getVersion(), 0, null));
       daemon.setDatabaseForTesting(ImmutableList.<Module>of(
           new InMemoryTestingDatabaseModule(cfg)));
       daemon.start();
@@ -91,10 +155,11 @@
       daemonService.submit(new Callable<Void>() {
         @Override
         public Void call() throws Exception {
-          int rc = daemon.main(new String[] {"-d", site.getPath(), "--headless" });
+          int rc = daemon.main(new String[] {
+              "-d", site.getPath(),
+              "--headless", "--console-log", "--show-stack-trace"});
           if (rc != 0) {
-            System.out.println("Failed to start Gerrit daemon. Check "
-                + site.getPath() + "/logs/error_log");
+            System.err.println("Failed to start Gerrit daemon");
             serverStarted.reset();
           }
           return null;
@@ -105,7 +170,7 @@
     }
 
     Injector i = createTestInjector(daemon);
-    return new GerritServer(i, daemon, daemonService);
+    return new GerritServer(desc, i, daemon, daemonService);
   }
 
   private static File initSite(Config base) throws Exception {
@@ -136,11 +201,17 @@
     cfg.setString("httpd", null, "listenUrl", url);
     cfg.setString("sshd", null, "listenAddress", forceEphemeralPort);
     cfg.setBoolean("sshd", null, "testUseInsecureRandom", true);
-    cfg.setString("cache", null, "directory", null);
+    cfg.unset("cache", null, "directory");
     cfg.setString("gerrit", null, "basePath", "git");
-    cfg.setBoolean("sendemail", null, "enable", false);
+    cfg.setBoolean("sendemail", null, "enable", true);
+    cfg.setInt("sendemail", null, "threadPoolSize", 0);
     cfg.setInt("cache", "projects", "checkFrequency", 0);
     cfg.setInt("plugins", null, "checkFrequency", 0);
+
+    cfg.setInt("sshd", null, "threads", 1);
+    cfg.setInt("sshd", null, "commandStartThreads", 1);
+    cfg.setInt("receive", null, "threadPoolSize", 1);
+    cfg.setInt("index", null, "threads", 1);
   }
 
   private static Injector createTestInjector(Daemon daemon) throws Exception {
@@ -150,6 +221,9 @@
       protected void configure() {
         bind(AccountCreator.class);
         factory(PushOneCommit.Factory.class);
+        install(InProcessProtocol.module());
+        install(new NoSshModule());
+        install(new AsyncReceiveCommits.Module());
       }
     };
     return sysInjector.createChildInjector(module);
@@ -167,6 +241,8 @@
     return InetAddress.getLoopbackAddress();
   }
 
+  private final Description desc;
+
   private Daemon daemon;
   private ExecutorService daemonService;
   private Injector testInjector;
@@ -174,8 +250,9 @@
   private InetSocketAddress sshdAddress;
   private InetSocketAddress httpAddress;
 
-  private GerritServer(Injector testInjector, Daemon daemon,
+  private GerritServer(Description desc, Injector testInjector, Daemon daemon,
       ExecutorService daemonService) {
+    this.desc = desc;
     this.testInjector = testInjector;
     this.daemon = daemon;
     this.daemonService = daemonService;
@@ -207,6 +284,10 @@
     return testInjector;
   }
 
+  Description getDescription() {
+    return desc;
+  }
+
   void stop() throws Exception {
     daemon.getLifecycleManager().stop();
     if (daemonService != null) {
@@ -216,4 +297,9 @@
     }
     RepositoryCache.clear();
   }
+
+  @Override
+  public String toString() {
+    return MoreObjects.toStringHelper(this).addValue(desc).toString();
+  }
 }
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GitUtil.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GitUtil.java
new file mode 100644
index 0000000..e8f8925
--- /dev/null
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GitUtil.java
@@ -0,0 +1,165 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Iterables;
+import com.google.common.primitives.Ints;
+import com.google.gerrit.common.FooterConstants;
+import com.google.gerrit.reviewdb.client.Project;
+
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+
+import org.eclipse.jgit.api.FetchCommand;
+import org.eclipse.jgit.api.PushCommand;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.transport.FetchResult;
+import org.eclipse.jgit.transport.JschConfigSessionFactory;
+import org.eclipse.jgit.transport.OpenSshConfig.Host;
+import org.eclipse.jgit.transport.PushResult;
+import org.eclipse.jgit.transport.RefSpec;
+import org.eclipse.jgit.transport.SshSessionFactory;
+import org.eclipse.jgit.util.FS;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class GitUtil {
+  private static final AtomicInteger testRepoCount = new AtomicInteger();
+  private static final int TEST_REPO_WINDOW_DAYS = 2;
+
+  public static void initSsh(final TestAccount a) {
+    final Properties config = new Properties();
+    config.put("StrictHostKeyChecking", "no");
+    JSch.setConfig(config);
+
+    // register a JschConfigSessionFactory that adds the private key as identity
+    // to the JSch instance of JGit so that SSH communication via JGit can
+    // succeed
+    SshSessionFactory.setInstance(new JschConfigSessionFactory() {
+      @Override
+      protected void configure(Host hc, Session session) {
+        try {
+          final JSch jsch = getJSch(hc, FS.DETECTED);
+          jsch.addIdentity("KeyPair", a.privateKey(),
+              a.sshKey.getPublicKeyBlob(), null);
+        } catch (JSchException e) {
+          throw new RuntimeException(e);
+        }
+      }
+    });
+  }
+
+  /**
+   * Create a new {@link TestRepository} with a distinct commit clock.
+   * <p>
+   * It is very easy for tests to create commits with identical subjects and
+   * trees; if such commits also have identical authors/committers, then the
+   * computed Change-Id is identical as well. Tests may generally assume that
+   * Change-Ids are unique, so to ensure this, we provision TestRepository
+   * instances with non-overlapping commit clock times.
+   * <p>
+   * Space test repos 1 day apart, which allows for about 86k ticks per repo
+   * before overlapping, and about 8k instances per process before hitting
+   * JGit's year 2038 limit.
+   *
+   * @param repo repository to wrap.
+   * @return wrapped test repository with distinct commit time space.
+   */
+  public static <R extends Repository> TestRepository<R> newTestRepository(
+      R repo) throws IOException {
+    TestRepository<R> tr = new TestRepository<>(repo);
+    tr.tick(Ints.checkedCast(TimeUnit.SECONDS.convert(
+        testRepoCount.getAndIncrement() * TEST_REPO_WINDOW_DAYS,
+        TimeUnit.DAYS)));
+    return tr;
+  }
+
+  public static TestRepository<InMemoryRepository> cloneProject(
+      Project.NameKey project, String uri) throws Exception {
+    DfsRepositoryDescription desc =
+        new DfsRepositoryDescription("clone of " + project.get());
+    InMemoryRepository dest = new InMemoryRepository.Builder()
+        .setRepositoryDescription(desc)
+        // SshTransport depends on a real FS to read ~/.ssh/config, but
+        // InMemoryRepository by default uses a null FS.
+        // TODO(dborowitz): Remove when we no longer depend on SSH.
+        .setFS(FS.detect())
+        .build();
+    Config cfg = dest.getConfig();
+    cfg.setString("remote", "origin", "url", uri);
+    cfg.setString("remote", "origin", "fetch",
+        "+refs/heads/*:refs/remotes/origin/*");
+    TestRepository<InMemoryRepository> testRepo = newTestRepository(dest);
+    FetchResult result = testRepo.git().fetch().setRemote("origin").call();
+    String originMaster = "refs/remotes/origin/master";
+    if (result.getTrackingRefUpdate(originMaster) != null) {
+      testRepo.reset(originMaster);
+    }
+    return testRepo;
+  }
+
+  public static TestRepository<InMemoryRepository> cloneProject(
+      Project.NameKey project, SshSession sshSession) throws Exception {
+    return cloneProject(project, sshSession.getUrl() + "/" + project.get());
+  }
+
+  public static void fetch(TestRepository<?> testRepo, String spec)
+      throws GitAPIException {
+    FetchCommand fetch = testRepo.git().fetch();
+    fetch.setRefSpecs(new RefSpec(spec));
+    fetch.call();
+  }
+
+  public static PushResult pushHead(TestRepository<?> testRepo, String ref,
+      boolean pushTags) throws GitAPIException {
+    return pushHead(testRepo, ref, pushTags, false);
+  }
+
+  public static PushResult pushHead(TestRepository<?> testRepo, String ref,
+      boolean pushTags, boolean force) throws GitAPIException {
+    PushCommand pushCmd = testRepo.git().push();
+    pushCmd.setForce(force);
+    pushCmd.setRefSpecs(new RefSpec("HEAD:" + ref));
+    if (pushTags) {
+      pushCmd.setPushTags();
+    }
+    Iterable<PushResult> r = pushCmd.call();
+    return Iterables.getOnlyElement(r);
+  }
+
+  public static Optional<String> getChangeId(TestRepository<?> tr, ObjectId id)
+      throws IOException {
+    RevCommit c = tr.getRevWalk().parseCommit(id);
+    tr.getRevWalk().parseBody(c);
+    List<String> ids = c.getFooterLines(FooterConstants.CHANGE_ID);
+    if (ids.isEmpty()) {
+      return Optional.absent();
+    }
+    return Optional.of(ids.get(ids.size() - 1));
+  }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/HttpResponse.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/HttpResponse.java
similarity index 88%
rename from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/HttpResponse.java
rename to gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/HttpResponse.java
index 872c912..390cae3 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/HttpResponse.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/HttpResponse.java
@@ -51,6 +51,16 @@
     return response.getStatusLine().getStatusCode();
   }
 
+  public String getContentType() {
+    return response.getFirstHeader("X-FYI-Content-Type").getValue();
+  }
+
+  public boolean hasContent() {
+    Preconditions.checkNotNull(response,
+        "Response is not initialized.");
+    return response.getEntity() != null;
+  }
+
   public String getEntityContent() throws IOException {
     Preconditions.checkNotNull(response,
         "Response is not initialized.");
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/HttpSession.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/HttpSession.java
new file mode 100644
index 0000000..1e0920e
--- /dev/null
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/HttpSession.java
@@ -0,0 +1,43 @@
+// Copyright (C) 2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance;
+
+import com.google.common.base.CharMatcher;
+
+import org.apache.http.HttpHost;
+import org.apache.http.client.fluent.Executor;
+import org.apache.http.client.fluent.Request;
+
+import java.io.IOException;
+import java.net.URI;
+
+public class HttpSession {
+
+  protected final String url;
+  private final Executor executor;
+
+  public HttpSession(GerritServer server, TestAccount account) {
+    this.url = CharMatcher.is('/').trimTrailingFrom(server.getUrl());
+    URI uri = URI.create(url);
+    this.executor = Executor
+        .newInstance()
+        .auth(new HttpHost(uri.getHost(), uri.getPort()),
+            account.username, account.httpPassword);
+  }
+
+  protected RestResponse execute(Request request) throws IOException {
+    return new RestResponse(executor.execute(request).returnResponse());
+  }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
similarity index 94%
rename from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
rename to gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
index 8548b5c..634db7c 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
@@ -43,7 +43,8 @@
 import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
 import org.eclipse.jgit.lib.Config;
 
-import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 
 class InMemoryTestingDatabaseModule extends LifecycleModule {
   private final Config cfg;
@@ -58,9 +59,10 @@
       .annotatedWith(GerritServerConfig.class)
       .toInstance(cfg);
 
-    bind(File.class)
+    // TODO(dborowitz): Use jimfs.
+    bind(Path.class)
       .annotatedWith(SitePath.class)
-      .toInstance(new File("UNIT_TEST_GERRIT_SITE"));
+      .toInstance(Paths.get(cfg.getString("gerrit", null, "tempSiteDir")));
 
     bind(GitRepositoryManager.class)
       .toInstance(new InMemoryRepositoryManager());
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/InProcessProtocol.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/InProcessProtocol.java
new file mode 100644
index 0000000..c16eed7
--- /dev/null
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/InProcessProtocol.java
@@ -0,0 +1,351 @@
+// Copyright (C) 2015 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.acceptance;
+
+import com.google.common.collect.Lists;
+import com.google.gerrit.acceptance.InProcessProtocol.Context;
+import com.google.gerrit.common.data.Capable;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.AccessPath;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.RemotePeer;
+import com.google.gerrit.server.RequestCleanup;
+import com.google.gerrit.server.config.GerritRequestModule;
+import com.google.gerrit.server.config.RequestScopedReviewDbProvider;
+import com.google.gerrit.server.git.AsyncReceiveCommits;
+import com.google.gerrit.server.git.ChangeCache;
+import com.google.gerrit.server.git.ReceiveCommits;
+import com.google.gerrit.server.git.ReceivePackInitializer;
+import com.google.gerrit.server.git.TagCache;
+import com.google.gerrit.server.git.TransferConfig;
+import com.google.gerrit.server.git.VisibleRefFilter;
+import com.google.gerrit.server.git.validators.UploadValidators;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.util.RequestContext;
+import com.google.gerrit.server.util.RequestScopePropagator;
+import com.google.gerrit.server.util.ThreadLocalRequestContext;
+import com.google.gerrit.server.util.ThreadLocalRequestScopePropagator;
+import com.google.gwtorm.server.SchemaFactory;
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import com.google.inject.OutOfScopeException;
+import com.google.inject.Provider;
+import com.google.inject.Provides;
+import com.google.inject.Scope;
+import com.google.inject.servlet.RequestScoped;
+import com.google.inject.util.Providers;
+
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.PostReceiveHook;
+import org.eclipse.jgit.transport.PostReceiveHookChain;
+import org.eclipse.jgit.transport.PreUploadHook;
+import org.eclipse.jgit.transport.PreUploadHookChain;
+import org.eclipse.jgit.transport.ReceivePack;
+import org.eclipse.jgit.transport.TestProtocol;
+import org.eclipse.jgit.transport.UploadPack;
+import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
+import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
+import org.eclipse.jgit.transport.resolver.UploadPackFactory;
+
+import java.io.IOException;
+import java.net.SocketAddress;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+class InProcessProtocol extends TestProtocol<Context> {
+  static Module module() {
+    return new AbstractModule() {
+      @Override
+      public void configure() {
+        install(new GerritRequestModule());
+        bind(RequestScopePropagator.class).to(Propagator.class);
+        bindScope(RequestScoped.class, InProcessProtocol.REQUEST);
+      }
+
+      @Provides
+      @RemotePeer
+      SocketAddress getSocketAddress() {
+        // TODO(dborowitz): Could potentially fake this with thread ID or
+        // something.
+        throw new OutOfScopeException("No remote peer in acceptance tests");
+      }
+    };
+  }
+
+  private static final Scope REQUEST = new Scope() {
+    @Override
+    public <T> Provider<T> scope(final Key<T> key, final Provider<T> creator) {
+      return new Provider<T>() {
+        @Override
+        public T get() {
+          Context ctx = current.get();
+          if (ctx == null) {
+            throw new OutOfScopeException("Not in TestProtocol scope");
+          }
+          return ctx.get(key, creator);
+        }
+
+        @Override
+        public String toString() {
+          return String.format("%s[%s]", creator, REQUEST);
+        }
+      };
+    }
+
+    @Override
+    public String toString() {
+      return "InProcessProtocol.REQUEST";
+    }
+  };
+
+  private static class Propagator
+      extends ThreadLocalRequestScopePropagator<Context> {
+    @Inject
+    Propagator(ThreadLocalRequestContext local,
+        Provider<RequestScopedReviewDbProvider> dbProviderProvider) {
+      super(REQUEST, current, local, dbProviderProvider);
+    }
+
+    @Override
+    protected Context continuingContext(Context ctx) {
+      return ctx.newContinuingContext();
+    }
+  }
+
+  private static final ThreadLocal<Context> current = new ThreadLocal<>();
+
+  // TODO(dborowitz): Merge this with AcceptanceTestRequestScope.
+  /**
+   * Multi-purpose session/context object.
+   * <p>
+   * Confusingly, Gerrit has two ideas of what a "context" object is:
+   * one for Guice {@link RequestScoped}, and one for its own simplified
+   * version of request scoping using {@link ThreadLocalRequestContext}.
+   * This class provides both, in essence just delegating the {@code
+   * ThreadLocalRequestContext} scoping to the Guice scoping mechanism.
+   * <p>
+   * It is also used as the session type for {@code UploadPackFactory} and
+   * {@code ReceivePackFactory}, since, after all, it encapsulates all the
+   * information about a single request.
+   */
+  static class Context implements RequestContext {
+    private static final Key<RequestScopedReviewDbProvider> DB_KEY =
+        Key.get(RequestScopedReviewDbProvider.class);
+    private static final Key<RequestCleanup> RC_KEY =
+        Key.get(RequestCleanup.class);
+    private static final Key<CurrentUser> USER_KEY = Key.get(CurrentUser.class);
+
+    private final SchemaFactory<ReviewDb> schemaFactory;
+    private final IdentifiedUser.GenericFactory userFactory;
+    private final Account.Id accountId;
+    private final Project.NameKey project;
+    private final RequestCleanup cleanup;
+    private final Map<Key<?>, Object> map;
+
+    Context(SchemaFactory<ReviewDb> schemaFactory,
+        IdentifiedUser.GenericFactory userFactory,
+        Account.Id accountId,
+        Project.NameKey project) {
+      this.schemaFactory = schemaFactory;
+      this.userFactory = userFactory;
+      this.accountId = accountId;
+      this.project = project;
+      map = new HashMap<>();
+      cleanup = new RequestCleanup();
+      map.put(DB_KEY,
+          new RequestScopedReviewDbProvider(
+            schemaFactory, Providers.of(cleanup)));
+      map.put(RC_KEY, cleanup);
+
+      IdentifiedUser user = userFactory.create(accountId);
+      user.setAccessPath(AccessPath.GIT);
+      map.put(USER_KEY, user);
+    }
+
+    private Context newContinuingContext() {
+      return new Context(schemaFactory, userFactory, accountId, project);
+    }
+
+    @Override
+    public CurrentUser getUser() {
+      return get(USER_KEY, null);
+    }
+
+    @Override
+    public Provider<ReviewDb> getReviewDbProvider() {
+      return get(DB_KEY, null);
+    }
+
+    private synchronized <T> T get(Key<T> key, Provider<T> creator) {
+      @SuppressWarnings("unchecked")
+      T t = (T) map.get(key);
+      if (t == null) {
+        t = creator.get();
+        map.put(key, t);
+      }
+      return t;
+    }
+  }
+
+  private static class Upload implements UploadPackFactory<Context> {
+    private final Provider<ReviewDb> dbProvider;
+    private final Provider<CurrentUser> userProvider;
+    private final TagCache tagCache;
+    private final ChangeCache changeCache;
+    private final ProjectControl.GenericFactory projectControlFactory;
+    private final TransferConfig transferConfig;
+    private final DynamicSet<PreUploadHook> preUploadHooks;
+    private final UploadValidators.Factory uploadValidatorsFactory;
+    private final ThreadLocalRequestContext threadContext;
+
+    @Inject
+    Upload(
+        Provider<ReviewDb> dbProvider,
+        Provider<CurrentUser> userProvider,
+        TagCache tagCache,
+        ChangeCache changeCache,
+        ProjectControl.GenericFactory projectControlFactory,
+        TransferConfig transferConfig,
+        DynamicSet<PreUploadHook> preUploadHooks,
+        UploadValidators.Factory uploadValidatorsFactory,
+        ThreadLocalRequestContext threadContext) {
+      this.dbProvider = dbProvider;
+      this.userProvider = userProvider;
+      this.tagCache = tagCache;
+      this.changeCache = changeCache;
+      this.projectControlFactory = projectControlFactory;
+      this.transferConfig = transferConfig;
+      this.preUploadHooks = preUploadHooks;
+      this.uploadValidatorsFactory = uploadValidatorsFactory;
+      this.threadContext = threadContext;
+    }
+
+    @Override
+    public UploadPack create(Context req, final Repository repo)
+        throws ServiceNotAuthorizedException {
+      // Set the request context, but don't bother unsetting, since we don't
+      // have an easy way to run code when this instance is done being used.
+      // Each operation is run in its own thread, so we don't need to recover
+      // its original context anyway.
+      threadContext.setContext(req);
+      current.set(req);
+      try {
+        ProjectControl ctl = projectControlFactory.controlFor(
+            req.project, userProvider.get());
+        if (!ctl.canRunUploadPack()) {
+          throw new ServiceNotAuthorizedException();
+        }
+
+        UploadPack up = new UploadPack(repo);
+        up.setPackConfig(transferConfig.getPackConfig());
+        up.setTimeout(transferConfig.getTimeout());
+
+        if (!ctl.allRefsAreVisible()) {
+          up.setAdvertiseRefsHook(new VisibleRefFilter(
+              tagCache, changeCache, repo, ctl, dbProvider.get(), true));
+        }
+        List<PreUploadHook> hooks = Lists.newArrayList(preUploadHooks);
+        hooks.add(uploadValidatorsFactory.create(
+            ctl.getProject(), repo, "localhost-test"));
+        up.setPreUploadHook(PreUploadHookChain.newChain(hooks));
+        return up;
+      } catch (NoSuchProjectException | IOException e) {
+        throw new RuntimeException(e);
+      }
+    }
+  }
+
+  private static class Receive implements ReceivePackFactory<Context> {
+    private final Provider<CurrentUser> userProvider;
+    private final ProjectControl.GenericFactory projectControlFactory;
+    private final AsyncReceiveCommits.Factory factory;
+    private final TransferConfig config;
+    private final DynamicSet<ReceivePackInitializer> receivePackInitializers;
+    private final DynamicSet<PostReceiveHook> postReceiveHooks;
+    private final ThreadLocalRequestContext threadContext;
+
+    @Inject
+    Receive(
+        Provider<CurrentUser> userProvider,
+        ProjectControl.GenericFactory projectControlFactory,
+        AsyncReceiveCommits.Factory factory,
+        TransferConfig config,
+        DynamicSet<ReceivePackInitializer> receivePackInitializers,
+        DynamicSet<PostReceiveHook> postReceiveHooks,
+        ThreadLocalRequestContext threadContext) {
+      this.userProvider = userProvider;
+      this.projectControlFactory = projectControlFactory;
+      this.factory = factory;
+      this.config = config;
+      this.receivePackInitializers = receivePackInitializers;
+      this.postReceiveHooks = postReceiveHooks;
+      this.threadContext = threadContext;
+    }
+
+    @Override
+    public ReceivePack create(final Context req, Repository db)
+        throws ServiceNotAuthorizedException {
+      // Set the request context, but don't bother unsetting, since we don't
+      // have an easy way to run code when this instance is done being used.
+      // Each operation is run in its own thread, so we don't need to recover
+      // its original context anyway.
+      threadContext.setContext(req);
+      current.set(req);
+      try {
+        ProjectControl ctl =
+            projectControlFactory.controlFor(req.project, userProvider.get());
+        if (!ctl.canRunReceivePack()) {
+          throw new ServiceNotAuthorizedException();
+        }
+
+        ReceiveCommits rc = factory.create(ctl, db).getReceiveCommits();
+        ReceivePack rp = rc.getReceivePack();
+
+        Capable r = rc.canUpload();
+        if (r != Capable.OK) {
+          throw new ServiceNotAuthorizedException();
+        }
+
+        rp.setRefLogIdent(ctl.getUser().asIdentifiedUser().newRefLogIdent());
+        rp.setTimeout(config.getTimeout());
+        rp.setMaxObjectSizeLimit(config.getMaxObjectSizeLimit());
+
+        for (ReceivePackInitializer initializer : receivePackInitializers) {
+          initializer.init(ctl.getProject().getNameKey(), rp);
+        }
+
+        rp.setPostReceiveHook(PostReceiveHookChain.newChain(
+            Lists.newArrayList(postReceiveHooks)));
+        return rp;
+      } catch (NoSuchProjectException | IOException e) {
+        throw new RuntimeException(e);
+      }
+    }
+  }
+
+  @Inject
+  InProcessProtocol(Upload uploadPackFactory,
+      Receive receivePackFactory) {
+    super(uploadPackFactory, receivePackFactory);
+  }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/MergeableFileBasedConfig.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/MergeableFileBasedConfig.java
similarity index 100%
rename from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/MergeableFileBasedConfig.java
rename to gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/MergeableFileBasedConfig.java
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/NoHttpd.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/NoHttpd.java
similarity index 100%
rename from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/NoHttpd.java
rename to gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/NoHttpd.java
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/PluginDaemonTest.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/PluginDaemonTest.java
new file mode 100644
index 0000000..f0b9f46
--- /dev/null
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/PluginDaemonTest.java
@@ -0,0 +1,220 @@
+// Copyright (C) 2015 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.acceptance;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Strings;
+import com.google.gerrit.server.config.SitePaths;
+
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
+import org.junit.runner.Description;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.ProcessBuilder.Redirect;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public abstract class PluginDaemonTest extends AbstractDaemonTest {
+
+  private static final String BUCKLC = "buck";
+  private static final String BUCKOUT = "buck-out";
+
+  private Path gen;
+  private Path testSite;
+  private Path pluginRoot;
+  private Path pluginsSitePath;
+  private Path pluginSubPath;
+  private Path pluginSource;
+  private String pluginName;
+  private boolean standalone;
+
+  @Override
+  protected void beforeTest(Description description) throws Exception {
+    locatePaths();
+    retrievePluginName();
+    buildPluginJar();
+    createTestSiteDirs();
+    copyJarToTestSite();
+    super.beforeTest(description);
+  }
+
+  protected void setPluginConfigString(String name, String value)
+      throws IOException, ConfigInvalidException {
+    SitePaths sitePath = new SitePaths(testSite);
+    FileBasedConfig cfg = getGerritConfigFile(sitePath);
+    cfg.load();
+    cfg.setString("plugin", pluginName, name, value);
+    cfg.save();
+  }
+
+  private FileBasedConfig getGerritConfigFile(SitePaths sitePath)
+      throws IOException {
+    FileBasedConfig cfg =
+        new FileBasedConfig(sitePath.gerrit_config.toFile(), FS.DETECTED);
+    if (!cfg.getFile().exists()) {
+      Path etc_path = Files.createDirectories(sitePath.etc_dir);
+      Files.createFile(etc_path.resolve("gerrit.config"));
+    }
+    return cfg;
+  }
+
+  private void locatePaths() {
+    URL pluginClassesUrl =
+        getClass().getProtectionDomain().getCodeSource().getLocation();
+    Path basePath = Paths.get(pluginClassesUrl.getPath()).getParent();
+
+    int idx = 0;
+    int buckOutIdx = 0;
+    int pluginsIdx = 0;
+    for (Path subPath : basePath) {
+      if (subPath.endsWith("plugins")) {
+        pluginsIdx = idx;
+      }
+      if (subPath.endsWith(BUCKOUT)) {
+        buckOutIdx = idx;
+      }
+      idx++;
+    }
+    standalone = checkStandalone(basePath);
+    pluginRoot = basePath.getRoot().resolve(basePath.subpath(0, buckOutIdx));
+    gen = pluginRoot.resolve(BUCKOUT).resolve("gen");
+
+    if (standalone) {
+      pluginSource = pluginRoot;
+    } else {
+      pluginSubPath = basePath.subpath(pluginsIdx, pluginsIdx + 2);
+      pluginSource = pluginRoot.resolve(pluginSubPath);
+    }
+  }
+
+  private boolean checkStandalone(Path basePath) {
+    String pathCharStringOrNone = "[a-zA-Z0-9._-]*?";
+    Pattern pattern = Pattern.compile(pathCharStringOrNone + "gerrit" +
+        pathCharStringOrNone);
+    Path partialPath = basePath;
+    for (int i = basePath.getNameCount(); i > 0; i--) {
+      int count = partialPath.getNameCount();
+      if (count > 1) {
+        String gerritDirCandidate =
+            partialPath.subpath(count - 2, count - 1).toString();
+        if (pattern.matcher(gerritDirCandidate).matches()) {
+          if (partialPath.endsWith(gerritDirCandidate + "/" + BUCKOUT)) {
+            return false;
+          }
+        }
+      }
+      partialPath = partialPath.getParent();
+    }
+    return true;
+  }
+
+  private void retrievePluginName() throws IOException {
+    Path buckFile = pluginSource.resolve("BUCK");
+    byte[] bytes = Files.readAllBytes(buckFile);
+    String buckContent =
+        new String(bytes, UTF_8).replaceAll("\\s+", "");
+    Matcher matcher =
+        Pattern.compile("gerrit_plugin\\(name='(.*?)'").matcher(buckContent);
+    if (matcher.find()) {
+      pluginName = matcher.group(1);
+    }
+    if (Strings.isNullOrEmpty(pluginName)) {
+      if (standalone) {
+        pluginName = pluginRoot.getFileName().toString();
+      } else {
+        pluginName = pluginSubPath.getFileName().toString();
+      }
+    }
+  }
+
+  private void buildPluginJar() throws IOException, InterruptedException {
+    Properties properties = loadBuckProperties();
+    String buck =
+        MoreObjects.firstNonNull(properties.getProperty(BUCKLC), BUCKLC);
+    String target;
+    if (standalone) {
+      target = "//:" + pluginName;
+    } else {
+      target = pluginSubPath.toString();
+    }
+
+    ProcessBuilder processBuilder =
+        new ProcessBuilder(buck, "build", target).directory(pluginRoot.toFile())
+            .redirectErrorStream(true);
+    // otherwise plugin jar creation fails:
+    processBuilder.environment().put("NO_BUCKD", "1");
+
+    Path forceJar = pluginSource.resolve("src/main/java/ForceJarIfMissing.java");
+    // if exists after cancelled test:
+    Files.deleteIfExists(forceJar);
+
+    Files.createFile(forceJar);
+    testSite = tempSiteDir.getRoot().toPath();
+
+    // otherwise process often hangs:
+    Path log = testSite.resolve("log");
+    processBuilder.redirectErrorStream(true);
+    processBuilder.redirectOutput(Redirect.appendTo(log.toFile()));
+
+    try {
+      processBuilder.start().waitFor();
+    } finally {
+      Files.delete(forceJar);
+      // otherwise jar not made next time if missing again:
+      processBuilder.start().waitFor();
+    }
+  }
+
+  private Properties loadBuckProperties() throws IOException {
+    Properties properties = new Properties();
+    Path propertiesPath = gen.resolve(Paths.get("tools/buck/buck.properties"));
+    if (Files.exists(propertiesPath)) {
+      try (InputStream in = Files.newInputStream(propertiesPath)) {
+        properties.load(in);
+      }
+    }
+    return properties;
+  }
+
+  private void createTestSiteDirs() throws IOException {
+    SitePaths sitePath = new SitePaths(testSite);
+    pluginsSitePath = Files.createDirectories(sitePath.plugins_dir);
+    Files.createDirectories(sitePath.tmp_dir);
+    Files.createDirectories(sitePath.etc_dir);
+  }
+
+  private void copyJarToTestSite() throws IOException {
+    Path pluginOut;
+    if (standalone) {
+      pluginOut = gen;
+    } else {
+      pluginOut = gen.resolve(pluginSubPath);
+    }
+    Path jar = pluginOut.resolve(pluginName + ".jar");
+    Path dest = pluginsSitePath.resolve(jar.getFileName());
+    Files.copy(jar, dest, StandardCopyOption.REPLACE_EXISTING);
+  }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/PushOneCommit.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/PushOneCommit.java
similarity index 69%
rename from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/PushOneCommit.java
rename to gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/PushOneCommit.java
index 67b6f51..6a090fd 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/PushOneCommit.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/PushOneCommit.java
@@ -15,17 +15,11 @@
 package com.google.gerrit.acceptance;
 
 import static com.google.common.truth.Truth.assertThat;
-import static com.google.gerrit.acceptance.GitUtil.add;
-import static com.google.gerrit.acceptance.GitUtil.amendCommit;
-import static com.google.gerrit.acceptance.GitUtil.createCommit;
 import static com.google.gerrit.acceptance.GitUtil.pushHead;
 
-import com.google.common.base.Function;
 import com.google.common.base.Strings;
 import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
-import com.google.gerrit.acceptance.GitUtil.Commit;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
@@ -40,12 +34,8 @@
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
 
-import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.api.TagCommand;
-import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
-import org.eclipse.jgit.api.errors.GitAPIException;
-import org.eclipse.jgit.api.errors.InvalidTagNameException;
-import org.eclipse.jgit.api.errors.NoHeadException;
+import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.revwalk.RevCommit;
@@ -53,23 +43,40 @@
 import org.eclipse.jgit.transport.RemoteRefUpdate;
 import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
 
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Set;
+import java.util.List;
 
 public class PushOneCommit {
   public static final String SUBJECT = "test commit";
   public static final String FILE_NAME = "a.txt";
   public static final String FILE_CONTENT = "some content";
+  public static final String PATCH =
+      "From %s Mon Sep 17 00:00:00 2001\n" +
+      "From: Administrator <admin@example.com>\n" +
+      "Date: %s\n" +
+      "Subject: [PATCH] test commit\n" +
+      "\n" +
+      "Change-Id: %s\n" +
+      "---\n" +
+      "\n" +
+      "diff --git a/a.txt b/a.txt\n" +
+      "new file mode 100644\n" +
+      "index 0000000..f0eec86\n" +
+      "--- /dev/null\n" +
+      "+++ b/a.txt\n" +
+      "@@ -0,0 +1 @@\n" +
+      "+some content\n" +
+      "\\ No newline at end of file\n";
 
   public interface Factory {
     PushOneCommit create(
         ReviewDb db,
-        PersonIdent i);
+        PersonIdent i,
+        TestRepository<?> testRepo);
 
     PushOneCommit create(
         ReviewDb db,
         PersonIdent i,
+        TestRepository<?> testRepo,
         @Assisted("subject") String subject,
         @Assisted("fileName") String fileName,
         @Assisted("content") String content);
@@ -77,6 +84,7 @@
     PushOneCommit create(
         ReviewDb db,
         PersonIdent i,
+        TestRepository<?> testRepo,
         @Assisted("subject") String subject,
         @Assisted("fileName") String fileName,
         @Assisted("content") String content,
@@ -106,7 +114,7 @@
   private final ApprovalsUtil approvalsUtil;
   private final Provider<InternalChangeQuery> queryProvider;
   private final ReviewDb db;
-  private final PersonIdent i;
+  private final TestRepository<?> testRepo;
 
   private final String subject;
   private final String fileName;
@@ -115,14 +123,17 @@
   private Tag tag;
   private boolean force;
 
+  private final TestRepository<?>.CommitBuilder commitBuilder;
+
   @AssistedInject
   PushOneCommit(ChangeNotes.Factory notesFactory,
       ApprovalsUtil approvalsUtil,
       Provider<InternalChangeQuery> queryProvider,
       @Assisted ReviewDb db,
-      @Assisted PersonIdent i) {
+      @Assisted PersonIdent i,
+      @Assisted TestRepository<?> testRepo) throws Exception {
     this(notesFactory, approvalsUtil, queryProvider,
-        db, i, SUBJECT, FILE_NAME, FILE_CONTENT);
+        db, i, testRepo, SUBJECT, FILE_NAME, FILE_CONTENT);
   }
 
   @AssistedInject
@@ -131,11 +142,12 @@
       Provider<InternalChangeQuery> queryProvider,
       @Assisted ReviewDb db,
       @Assisted PersonIdent i,
+      @Assisted TestRepository<?> testRepo,
       @Assisted("subject") String subject,
       @Assisted("fileName") String fileName,
-      @Assisted("content") String content) {
+      @Assisted("content") String content) throws Exception {
     this(notesFactory, approvalsUtil, queryProvider,
-        db, i, subject, fileName, content, null);
+        db, i, testRepo, subject, fileName, content, null);
   }
 
   @AssistedInject
@@ -144,42 +156,55 @@
       Provider<InternalChangeQuery> queryProvider,
       @Assisted ReviewDb db,
       @Assisted PersonIdent i,
+      @Assisted TestRepository<?> testRepo,
       @Assisted("subject") String subject,
       @Assisted("fileName") String fileName,
       @Assisted("content") String content,
-      @Nullable @Assisted("changeId") String changeId) {
+      @Nullable @Assisted("changeId") String changeId) throws Exception {
     this.db = db;
+    this.testRepo = testRepo;
     this.notesFactory = notesFactory;
     this.approvalsUtil = approvalsUtil;
     this.queryProvider = queryProvider;
-    this.i = i;
     this.subject = subject;
     this.fileName = fileName;
     this.content = content;
     this.changeId = changeId;
-  }
-
-  public Result to(Git git, String ref) throws GitAPIException, IOException {
-    add(git, fileName, content);
-    return execute(git, ref);
-  }
-
-  public Result rm(Git git, String ref) throws GitAPIException {
-    GitUtil.rm(git, fileName);
-    return execute(git, ref);
-  }
-
-  private Result execute(Git git, String ref) throws GitAPIException,
-      ConcurrentRefUpdateException, InvalidTagNameException, NoHeadException {
-    Commit c;
     if (changeId != null) {
-      c = amendCommit(git, i, subject, changeId);
+      commitBuilder = testRepo.amendRef("HEAD")
+          .insertChangeId(changeId.substring(1));
     } else {
-      c = createCommit(git, i, subject);
-      changeId = c.getChangeId();
+      commitBuilder = testRepo.branch("HEAD").commit().insertChangeId();
+    }
+    commitBuilder.message(subject)
+      .author(i)
+      .committer(new PersonIdent(i, testRepo.getDate()));
+  }
+
+  public void setParents(List<RevCommit> parents) throws Exception {
+    commitBuilder.noParents();
+    for (RevCommit p : parents) {
+      commitBuilder.parent(p);
+    }
+  }
+
+  public Result to(String ref) throws Exception {
+    commitBuilder.add(fileName, content);
+    return execute(ref);
+  }
+
+  public Result rm(String ref) throws Exception {
+    commitBuilder.rm(fileName);
+    return execute(ref);
+  }
+
+  public Result execute(String ref) throws Exception {
+    RevCommit c = commitBuilder.create();
+    if (changeId == null) {
+      changeId = GitUtil.getChangeId(testRepo, c).get();
     }
     if (tag != null) {
-      TagCommand tagCommand = git.tag().setName(tag.name);
+      TagCommand tagCommand = testRepo.git().tag().setName(tag.name);
       if (tag instanceof AnnotatedTag) {
         AnnotatedTag annotatedTag = (AnnotatedTag)tag;
         tagCommand.setAnnotated(true)
@@ -190,7 +215,8 @@
       }
       tagCommand.call();
     }
-    return new Result(ref, pushHead(git, ref, tag != null, force), c, subject);
+    return new Result(ref, pushHead(testRepo, ref, tag != null, force), c,
+        subject);
   }
 
   public void setTag(final Tag tag) {
@@ -204,10 +230,10 @@
   public class Result {
     private final String ref;
     private final PushResult result;
-    private final Commit commit;
+    private final RevCommit commit;
     private final String resSubj;
 
-    private Result(String ref, PushResult resSubj, Commit commit,
+    private Result(String ref, PushResult resSubj, RevCommit commit,
         String subject) {
       this.ref = ref;
       this.result = resSubj;
@@ -217,7 +243,7 @@
 
     public ChangeData getChange() throws OrmException {
       return Iterables.getOnlyElement(
-          queryProvider.get().byKeyPrefix(commit.getChangeId()));
+          queryProvider.get().byKeyPrefix(changeId));
     }
 
     public PatchSet getPatchSet() throws OrmException {
@@ -229,47 +255,33 @@
     }
 
     public String getChangeId() {
-      return commit.getChangeId();
+      return changeId;
     }
 
     public ObjectId getCommitId() {
-      return commit.getCommit().getId();
+      return commit;
     }
 
     public RevCommit getCommit() {
-      return commit.getCommit();
+      return commit;
     }
 
     public void assertChange(Change.Status expectedStatus,
         String expectedTopic, TestAccount... expectedReviewers)
         throws OrmException {
       Change c = getChange().change();
-      assertThat(resSubj).isEqualTo(c.getSubject());
-      assertThat(expectedStatus).isEqualTo(c.getStatus());
-      assertThat(expectedTopic).isEqualTo(Strings.emptyToNull(c.getTopic()));
+      assertThat(c.getSubject()).isEqualTo(resSubj);
+      assertThat(c.getStatus()).isEqualTo(expectedStatus);
+      assertThat(Strings.emptyToNull(c.getTopic())).isEqualTo(expectedTopic);
       assertReviewers(c, expectedReviewers);
     }
 
     private void assertReviewers(Change c, TestAccount... expectedReviewers)
         throws OrmException {
-      Set<Account.Id> expectedReviewerIds =
-          Sets.newHashSet(Lists.transform(Arrays.asList(expectedReviewers),
-              new Function<TestAccount, Account.Id>() {
-                @Override
-                public Account.Id apply(TestAccount a) {
-                  return a.id;
-                }
-              }));
-
-      for (Account.Id accountId
-          : approvalsUtil.getReviewers(db, notesFactory.create(c)).values()) {
-        assertThat(expectedReviewerIds.remove(accountId))
-          .named("unexpected reviewer " + accountId)
-          .isTrue();
-      }
-      assertThat((Iterable<?>)expectedReviewerIds)
-        .named("missing reviewers: " + expectedReviewerIds)
-        .isEmpty();
+      Iterable<Account.Id> actualIds =
+          approvalsUtil.getReviewers(db, notesFactory.create(c)).values();
+      assertThat(actualIds).containsExactlyElementsIn(
+          Sets.newHashSet(TestAccount.ids(expectedReviewers)));
     }
 
     public void assertOkStatus() {
@@ -280,6 +292,13 @@
       assertStatus(Status.REJECTED_OTHER_REASON, expectedMessage);
     }
 
+    public void assertErrorStatus() {
+      RemoteRefUpdate refUpdate = result.getRemoteUpdate(ref);
+      assertThat(refUpdate.getStatus())
+        .named(message(refUpdate))
+        .isEqualTo(Status.REJECTED_OTHER_REASON);
+    }
+
     private void assertStatus(Status expectedStatus, String expectedMessage) {
       RemoteRefUpdate refUpdate = result.getRemoteUpdate(ref);
       assertThat(refUpdate.getStatus())
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/RestResponse.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/RestResponse.java
similarity index 87%
rename from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/RestResponse.java
rename to gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/RestResponse.java
index 6c7dbfe..261b894 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/RestResponse.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/RestResponse.java
@@ -15,11 +15,11 @@
 package com.google.gerrit.acceptance;
 
 import static com.google.gerrit.httpd.restapi.RestApiServlet.JSON_MAGIC;
+import static java.nio.charset.StandardCharsets.UTF_8;
 
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.Reader;
-import java.nio.charset.StandardCharsets;
 
 public class RestResponse extends HttpResponse {
 
@@ -30,9 +30,8 @@
   @Override
   public Reader getReader() throws IllegalStateException, IOException {
     if (reader == null && response.getEntity() != null) {
-      reader =
-          new InputStreamReader(response.getEntity().getContent(),
-              StandardCharsets.UTF_8);
+      reader = new InputStreamReader(
+          response.getEntity().getContent(), UTF_8);
       reader.skip(JSON_MAGIC.length);
     }
     return reader;
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/RestSession.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/RestSession.java
similarity index 74%
rename from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/RestSession.java
rename to gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/RestSession.java
index bf6f928..4b22d0a 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/RestSession.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/RestSession.java
@@ -14,17 +14,15 @@
 
 package com.google.gerrit.acceptance;
 
-import com.google.common.base.Charsets;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
 import com.google.common.base.Preconditions;
 import com.google.common.net.HttpHeaders;
 import com.google.gerrit.extensions.restapi.RawInput;
 import com.google.gerrit.server.OutputFormat;
 
 import org.apache.http.Header;
-import org.apache.http.client.methods.HttpDelete;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.fluent.Request;
 import org.apache.http.entity.BufferedHttpEntity;
 import org.apache.http.entity.InputStreamEntity;
 import org.apache.http.entity.StringEntity;
@@ -33,7 +31,6 @@
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
 
 public class RestSession extends HttpSession {
 
@@ -41,7 +38,6 @@
     super(server, account);
   }
 
-  @Override
   public RestResponse get(String endPoint) throws IOException {
     return getWithHeader(endPoint, null);
   }
@@ -53,11 +49,11 @@
 
   private RestResponse getWithHeader(String endPoint, Header header)
       throws IOException {
-    HttpGet get = new HttpGet(url + "/a" + endPoint);
+    Request get = Request.Get(url + "/a" + endPoint);
     if (header != null) {
       get.addHeader(header);
     }
-    return new RestResponse(getClient().execute(get));
+    return execute(get);
   }
 
   public RestResponse put(String endPoint) throws IOException {
@@ -65,25 +61,38 @@
   }
 
   public RestResponse put(String endPoint, Object content) throws IOException {
-    HttpPut put = new HttpPut(url + "/a" + endPoint);
+    return putWithHeader(endPoint, null, content);
+  }
+
+  public RestResponse putWithHeader(String endPoint, Header header)
+      throws IOException {
+    return putWithHeader(endPoint, header, null);
+  }
+
+  public RestResponse putWithHeader(String endPoint, Header header,
+      Object content) throws IOException {
+    Request put = Request.Put(url + "/a" + endPoint);
+    if (header != null) {
+      put.addHeader(header);
+    }
     if (content != null) {
       put.addHeader(new BasicHeader("Content-Type", "application/json"));
-      put.setEntity(new StringEntity(
+      put.body(new StringEntity(
           OutputFormat.JSON_COMPACT.newGson().toJson(content),
-          Charsets.UTF_8.name()));
+          UTF_8));
     }
-    return new RestResponse(getClient().execute(put));
+    return execute(put);
   }
 
   public RestResponse putRaw(String endPoint, RawInput stream) throws IOException {
     Preconditions.checkNotNull(stream);
-    HttpPut put = new HttpPut(url + "/a" + endPoint);
+    Request put = Request.Put(url + "/a" + endPoint);
     put.addHeader(new BasicHeader("Content-Type", stream.getContentType()));
-    put.setEntity(new BufferedHttpEntity(
+    put.body(new BufferedHttpEntity(
         new InputStreamEntity(
             stream.getInputStream(),
             stream.getContentLength())));
-    return new RestResponse(getClient().execute(put));
+    return execute(put);
   }
 
   public RestResponse post(String endPoint) throws IOException {
@@ -91,24 +100,26 @@
   }
 
   public RestResponse post(String endPoint, Object content) throws IOException {
-    HttpPost post = new HttpPost(url + "/a" + endPoint);
+    Request post = Request.Post(url + "/a" + endPoint);
     if (content != null) {
       post.addHeader(new BasicHeader("Content-Type", "application/json"));
-      post.setEntity(new StringEntity(
+      post.body(new StringEntity(
           OutputFormat.JSON_COMPACT.newGson().toJson(content),
-          Charsets.UTF_8.name()));
+          UTF_8));
     }
-    return new RestResponse(getClient().execute(post));
+    return execute(post);
   }
 
   public RestResponse delete(String endPoint) throws IOException {
-    HttpDelete delete = new HttpDelete(url + "/a" + endPoint);
-    return new RestResponse(getClient().execute(delete));
+    return execute(Request.Delete(url + "/a" + endPoint));
   }
 
+  public RestResponse head(String endPoint) throws IOException {
+    return execute(Request.Head(url + "/a" + endPoint));
+  }
 
   public static RawInput newRawInput(String content) {
-    return newRawInput(content.getBytes(StandardCharsets.UTF_8));
+    return newRawInput(content.getBytes(UTF_8));
   }
 
   public static RawInput newRawInput(final byte[] bytes) {
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritConfig.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/Sandboxed.java
similarity index 82%
copy from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritConfig.java
copy to gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/Sandboxed.java
index 5cb1229..11446e0 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritConfig.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/Sandboxed.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 The Android Open Source Project
+// Copyright (C) 2016 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -15,14 +15,13 @@
 package com.google.gerrit.acceptance;
 
 import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
-@Target({METHOD})
+@Target({TYPE, METHOD})
 @Retention(RUNTIME)
-public @interface GerritConfig {
-  String name();
-  String value();
+public @interface Sandboxed {
 }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/SshSession.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/SshSession.java
similarity index 85%
rename from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/SshSession.java
rename to gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/SshSession.java
index 701b337..794f832 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/SshSession.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/SshSession.java
@@ -42,11 +42,12 @@
   }
 
   @SuppressWarnings("resource")
-  public String exec(String command) throws JSchException, IOException {
+  public String exec(String command, InputStream opt) throws JSchException,
+      IOException {
     ChannelExec channel = (ChannelExec) getSession().openChannel("exec");
     try {
       channel.setCommand(command);
-      channel.setInputStream(null);
+      channel.setInputStream(opt);
       InputStream in = channel.getInputStream();
       channel.connect();
 
@@ -60,6 +61,20 @@
     }
   }
 
+  public InputStream exec2(String command, InputStream opt) throws JSchException,
+      IOException {
+    ChannelExec channel = (ChannelExec) getSession().openChannel("exec");
+    channel.setCommand(command);
+    channel.setInputStream(opt);
+    InputStream in = channel.getInputStream();
+    channel.connect();
+    return in;
+  }
+
+  public String exec(String command) throws JSchException, IOException {
+    return exec(command, null);
+  }
+
   public boolean hasError() {
     return error != null;
   }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/TestAccount.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/TestAccount.java
similarity index 64%
rename from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/TestAccount.java
rename to gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/TestAccount.java
index bd5f19f..4a6d22d 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/TestAccount.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/TestAccount.java
@@ -14,6 +14,8 @@
 
 package com.google.gerrit.acceptance;
 
+import com.google.common.base.Function;
+import com.google.common.collect.FluentIterable;
 import com.google.gerrit.reviewdb.client.Account;
 
 import com.jcraft.jsch.KeyPair;
@@ -21,9 +23,38 @@
 import org.eclipse.jgit.lib.PersonIdent;
 
 import java.io.ByteArrayOutputStream;
-
+import java.util.Arrays;
 
 public class TestAccount {
+  public static FluentIterable<Account.Id> ids(
+      Iterable<TestAccount> accounts) {
+    return FluentIterable.from(accounts)
+        .transform(new Function<TestAccount, Account.Id>() {
+          @Override
+          public Account.Id apply(TestAccount in) {
+            return in.id;
+          }
+        });
+  }
+
+  public static FluentIterable<Account.Id> ids(TestAccount... accounts) {
+    return ids(Arrays.asList(accounts));
+  }
+
+  public static FluentIterable<String> names(Iterable<TestAccount> accounts) {
+    return FluentIterable.from(accounts)
+        .transform(new Function<TestAccount, String>() {
+          @Override
+          public String apply(TestAccount in) {
+            return in.fullName;
+          }
+        });
+  }
+
+  public static FluentIterable<String> names(TestAccount... accounts) {
+    return names(Arrays.asList(accounts));
+  }
+
   public final Account.Id id;
   public final String username;
   public final String email;
@@ -48,7 +79,7 @@
   }
 
   public PersonIdent getIdent() {
-    return new PersonIdent(username, email);
+    return new PersonIdent(fullName, email);
   }
 
   public String getHttpUrl(GerritServer server) {
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/TestProjectInput.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/TestProjectInput.java
new file mode 100644
index 0000000..4ad37e2
--- /dev/null
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/TestProjectInput.java
@@ -0,0 +1,49 @@
+// Copyright (C) 2015 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.acceptance;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.gerrit.extensions.client.InheritableBoolean;
+import com.google.gerrit.extensions.client.SubmitType;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Target({METHOD})
+@Retention(RUNTIME)
+public @interface TestProjectInput {
+  // Fields from ProjectInput for creating the project.
+
+  String parent() default "";
+  boolean createEmptyCommit() default true;
+  String description() default "";
+
+  // These may be null in a ProjectInput, but annotations do not allow null
+  // default values. Thus these defaults should match ProjectConfig.
+  SubmitType submitType() default SubmitType.MERGE_IF_NECESSARY;
+  InheritableBoolean useContributorAgreements()
+      default InheritableBoolean.INHERIT;
+  InheritableBoolean useSignedOffBy() default InheritableBoolean.INHERIT;
+  InheritableBoolean useContentMerge() default InheritableBoolean.INHERIT;
+  InheritableBoolean requireChangeId() default InheritableBoolean.INHERIT;
+
+
+  // Fields specific to acceptance test behavior.
+
+  /** Username to use for initial clone, passed to {@link AccountCreator}. */
+  String cloneAs() default "admin";
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/UseLocalDisk.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/UseLocalDisk.java
similarity index 100%
rename from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/UseLocalDisk.java
rename to gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/UseLocalDisk.java
diff --git a/gerrit-acceptance-tests/BUCK b/gerrit-acceptance-tests/BUCK
index c7bea4e..0a39ea7 100644
--- a/gerrit-acceptance-tests/BUCK
+++ b/gerrit-acceptance-tests/BUCK
@@ -2,9 +2,11 @@
   name = 'lib',
   srcs = glob(['src/test/java/com/google/gerrit/acceptance/*.java']),
   exported_deps = [
+    '//gerrit-acceptance-framework:lib',
     '//gerrit-common:annotations',
     '//gerrit-common:server',
     '//gerrit-extension-api:api',
+    '//gerrit-gpg:testutil',
     '//gerrit-launcher:launcher',
     '//gerrit-lucene:lucene',
     '//gerrit-httpd:httpd',
@@ -13,32 +15,28 @@
     '//gerrit-pgm:util',
     '//gerrit-reviewdb:server',
     '//gerrit-server:server',
-    '//gerrit-server/src/main/prolog:common',
     '//gerrit-server:testutil',
+    '//gerrit-server/src/main/prolog:common',
     '//gerrit-sshd:sshd',
 
     '//lib:args4j',
     '//lib:gson',
-    '//lib:guava',
     '//lib:gwtjsonrpc',
     '//lib:gwtorm',
     '//lib:h2',
     '//lib:jsch',
-    '//lib:junit',
     '//lib:servlet-api-3_1',
-    '//lib:truth',
 
-    '//lib/httpcomponents:httpclient',
-    '//lib/httpcomponents:httpcore',
-    '//lib/log:impl_log4j',
-    '//lib/log:log4j',
+    '//lib/bouncycastle:bcpg',
+    '//lib/bouncycastle:bcprov',
     '//lib/guice:guice',
     '//lib/guice:guice-assistedinject',
+    '//lib/guice:guice-servlet',
     '//lib/jgit:jgit',
-    '//lib/jgit:junit',
     '//lib/mina:sshd',
   ],
   visibility = [
+    '//gerrit-plugin-api/...',
     '//tools/eclipse:classpath',
     '//gerrit-acceptance-tests/...',
   ],
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
deleted file mode 100644
index 056b4ed..0000000
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ /dev/null
@@ -1,391 +0,0 @@
-// Copyright (C) 2013 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.acceptance;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.gerrit.acceptance.GitUtil.cloneProject;
-import static com.google.gerrit.acceptance.GitUtil.createProject;
-import static com.google.gerrit.acceptance.GitUtil.initSsh;
-import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static com.google.gerrit.server.project.Util.block;
-
-import com.google.common.base.Joiner;
-import com.google.common.primitives.Chars;
-import com.google.gerrit.acceptance.AcceptanceTestRequestScope.Context;
-import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.Permission;
-import com.google.gerrit.common.data.PermissionRule;
-import com.google.gerrit.extensions.api.GerritApi;
-import com.google.gerrit.extensions.api.changes.RevisionApi;
-import com.google.gerrit.extensions.client.InheritableBoolean;
-import com.google.gerrit.extensions.client.ListChangesOption;
-import com.google.gerrit.extensions.common.ChangeInfo;
-import com.google.gerrit.extensions.common.EditInfo;
-import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.OutputFormat;
-import com.google.gerrit.server.account.GroupCache;
-import com.google.gerrit.server.config.AllProjectsName;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.MetaDataUpdate;
-import com.google.gerrit.server.git.ProjectConfig;
-import com.google.gerrit.server.index.ChangeIndexer;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.Util;
-import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gerrit.testutil.ConfigSuite;
-import com.google.gerrit.testutil.TempFileUtil;
-import com.google.gson.Gson;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.util.Providers;
-
-import org.apache.http.HttpStatus;
-import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.api.errors.GitAPIException;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.Config;
-import org.junit.Rule;
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runner.RunWith;
-import org.junit.runners.model.Statement;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.List;
-
-@RunWith(ConfigSuite.class)
-public abstract class AbstractDaemonTest {
-  @ConfigSuite.Parameter
-  public Config baseConfig;
-
-  @Inject
-  protected AllProjectsName allProjects;
-
-  @Inject
-  protected AccountCreator accounts;
-
-  @Inject
-  private SchemaFactory<ReviewDb> reviewDbProvider;
-
-  @Inject
-  protected GerritApi gApi;
-
-  @Inject
-  private AcceptanceTestRequestScope atrScope;
-
-  @Inject
-  private IdentifiedUser.GenericFactory identifiedUserFactory;
-
-  @Inject
-  protected PushOneCommit.Factory pushFactory;
-
-  @Inject
-  protected MetaDataUpdate.Server metaDataUpdateFactory;
-
-  @Inject
-  protected ProjectCache projectCache;
-
-  @Inject
-  protected GroupCache groupCache;
-
-  @Inject
-  protected GitRepositoryManager repoManager;
-
-  @Inject
-  protected ChangeIndexer indexer;
-
-  @Inject
-  protected Provider<InternalChangeQuery> queryProvider;
-
-  @Inject
-  protected @GerritServerConfig Config cfg;
-
-  protected Git git;
-  protected GerritServer server;
-  protected TestAccount admin;
-  protected TestAccount user;
-  protected RestSession adminSession;
-  protected RestSession userSession;
-  protected SshSession sshSession;
-  protected ReviewDb db;
-  protected Project.NameKey project;
-
-  @Rule
-  public TestRule testRunner = new TestRule() {
-    @Override
-    public Statement apply(final Statement base, final Description description) {
-      return new Statement() {
-        @Override
-        public void evaluate() throws Throwable {
-          boolean mem = description.getAnnotation(UseLocalDisk.class) == null;
-          boolean enableHttpd = description.getAnnotation(NoHttpd.class) == null
-              && description.getTestClass().getAnnotation(NoHttpd.class) == null;
-          beforeTest(config(description), mem, enableHttpd);
-          base.evaluate();
-          afterTest();
-        }
-      };
-    }
-  };
-
-  private Config config(Description description) {
-    GerritConfigs cfgs = description.getAnnotation(GerritConfigs.class);
-    GerritConfig cfg = description.getAnnotation(GerritConfig.class);
-    if (cfgs != null && cfg != null) {
-      throw new IllegalStateException("Use either @GerritConfigs or @GerritConfig not both");
-    }
-    if (cfgs != null) {
-      return ConfigAnnotationParser.parse(baseConfig, cfgs);
-    } else if (cfg != null) {
-      return ConfigAnnotationParser.parse(baseConfig, cfg);
-    } else {
-      return baseConfig;
-    }
-  }
-
-  protected static Config submitWholeTopicEnabledConfig() {
-    Config cfg = new Config();
-    cfg.setBoolean("change", null, "submitWholeTopic", true);
-    return cfg;
-  }
-
-  protected static Config allowDraftsDisabledConfig() {
-    Config cfg = new Config();
-    cfg.setBoolean("change", null, "allowDrafts", false);
-    return cfg;
-  }
-
-  protected boolean isAllowDrafts() {
-    return cfg.getBoolean("change", "allowDrafts", true);
-  }
-
-  protected boolean isSubmitWholeTopicEnabled() {
-    return cfg.getBoolean("change", null, "submitWholeTopic", false);
-  }
-
-  private void beforeTest(Config cfg, boolean memory, boolean enableHttpd) throws Exception {
-    server = startServer(cfg, memory, enableHttpd);
-    server.getTestInjector().injectMembers(this);
-    admin = accounts.admin();
-    user = accounts.user();
-    adminSession = new RestSession(server, admin);
-    userSession = new RestSession(server, user);
-    initSsh(admin);
-    db = reviewDbProvider.open();
-    Context ctx = newRequestContext(admin);
-    atrScope.set(ctx);
-    sshSession = ctx.getSession();
-    project = new Project.NameKey("p");
-    createProject(sshSession, project.get());
-    git = cloneProject(sshSession.getUrl() + "/" + project.get());
-  }
-
-  protected GerritServer startServer(Config cfg, boolean memory,
-      boolean enableHttpd) throws Exception {
-    return GerritServer.start(cfg, memory, enableHttpd);
-  }
-
-  private void afterTest() throws Exception {
-    db.close();
-    sshSession.close();
-    server.stop();
-    TempFileUtil.cleanup();
-  }
-
-  protected PushOneCommit.Result createChange() throws GitAPIException,
-      IOException {
-    PushOneCommit push = pushFactory.create(db, admin.getIdent());
-    return push.to(git, "refs/for/master");
-  }
-
-  private static final List<Character> RANDOM =
-      Chars.asList(new char[]{'a','b','c','d','e','f','g','h'});
-  protected PushOneCommit.Result amendChange(String changeId)
-      throws GitAPIException, IOException {
-    return amendChange(changeId, "refs/for/master");
-  }
-
-  protected PushOneCommit.Result amendChange(String changeId, String ref)
-      throws GitAPIException, IOException {
-    Collections.shuffle(RANDOM);
-    PushOneCommit push =
-        pushFactory.create(db, admin.getIdent(), PushOneCommit.SUBJECT,
-            PushOneCommit.FILE_NAME, new String(Chars.toArray(RANDOM)), changeId);
-    return push.to(git, ref);
-  }
-
-  protected PushOneCommit.Result amendChangeAsDraft(String changeId)
-      throws GitAPIException, IOException {
-    return amendChange(changeId, "refs/drafts/master");
-  }
-
-  protected ChangeInfo getChange(String changeId, ListChangesOption... options)
-      throws IOException {
-    return getChange(adminSession, changeId, options);
-  }
-
-  protected ChangeInfo getChange(RestSession session, String changeId,
-      ListChangesOption... options) throws IOException {
-    String q = options.length > 0 ? "?o=" + Joiner.on("&o=").join(options) : "";
-    RestResponse r = session.get("/changes/" + changeId + q);
-    assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK);
-    return newGson().fromJson(r.getReader(), ChangeInfo.class);
-  }
-
-  protected ChangeInfo info(String id)
-      throws RestApiException {
-    return gApi.changes().id(id).info();
-  }
-
-  protected ChangeInfo get(String id)
-      throws RestApiException {
-    return gApi.changes().id(id).get();
-  }
-
-  protected EditInfo getEdit(String id)
-      throws RestApiException {
-    return gApi.changes().id(id).getEdit();
-  }
-
-  protected ChangeInfo get(String id, ListChangesOption... options)
-      throws RestApiException {
-    EnumSet<ListChangesOption> s = EnumSet.noneOf(ListChangesOption.class);
-    s.addAll(Arrays.asList(options));
-    return gApi.changes().id(id).get(s);
-  }
-
-  protected List<ChangeInfo> query(String q) throws RestApiException {
-    return gApi.changes().query(q).get();
-  }
-
-  private Context newRequestContext(TestAccount account) {
-    return atrScope.newContext(reviewDbProvider, new SshSession(server, admin),
-        identifiedUserFactory.create(Providers.of(db), account.getId()));
-  }
-
-  protected Context setApiUser(TestAccount account) {
-    return atrScope.set(newRequestContext(account));
-  }
-
-  protected static Gson newGson() {
-    return OutputFormat.JSON_COMPACT.newGson();
-  }
-
-  protected RevisionApi revision(PushOneCommit.Result r) throws Exception {
-    return gApi.changes()
-        .id(r.getChangeId())
-        .current();
-  }
-
-  protected void allow(String permission, AccountGroup.UUID id, String ref)
-      throws Exception {
-    ProjectConfig cfg = projectCache.checkedGet(project).getConfig();
-    Util.allow(cfg, permission, id, ref);
-    saveProjectConfig(project, cfg);
-  }
-
-  protected void allowGlobalCapability(String capabilityName,
-      AccountGroup.UUID id) throws Exception {
-    ProjectConfig cfg = projectCache.checkedGet(allProjects).getConfig();
-    Util.allow(cfg, capabilityName, id);
-    saveProjectConfig(allProjects, cfg);
-  }
-
-  protected void setUseContributorAgreements(InheritableBoolean value)
-      throws Exception {
-    MetaDataUpdate md = metaDataUpdateFactory.create(project);
-    ProjectConfig config = ProjectConfig.read(md);
-    config.getProject().setUseContributorAgreements(value);
-    config.commit(md);
-    projectCache.evict(config.getProject());
-  }
-
-  protected void setUseSignedOffBy(InheritableBoolean value)
-      throws Exception {
-    MetaDataUpdate md = metaDataUpdateFactory.create(project);
-    ProjectConfig config = ProjectConfig.read(md);
-    config.getProject().setUseSignedOffBy(value);
-    config.commit(md);
-    projectCache.evict(config.getProject());
-  }
-
-  protected void deny(String permission, AccountGroup.UUID id, String ref)
-      throws Exception {
-    ProjectConfig cfg = projectCache.checkedGet(project).getConfig();
-    Util.deny(cfg, permission, id, ref);
-    saveProjectConfig(project, cfg);
-  }
-
-  protected void saveProjectConfig(Project.NameKey p, ProjectConfig cfg)
-      throws Exception {
-    MetaDataUpdate md = metaDataUpdateFactory.create(p);
-    try {
-      cfg.commit(md);
-    } finally {
-      md.close();
-    }
-    projectCache.evict(cfg.getProject());
-  }
-
-  protected void grant(String permission, Project.NameKey project, String ref)
-      throws RepositoryNotFoundException, IOException, ConfigInvalidException {
-    grant(permission, project, ref, false);
-  }
-
-  protected void grant(String permission, Project.NameKey project, String ref,
-      boolean force) throws RepositoryNotFoundException, IOException,
-      ConfigInvalidException {
-    MetaDataUpdate md = metaDataUpdateFactory.create(project);
-    md.setMessage(String.format("Grant %s on %s", permission, ref));
-    ProjectConfig config = ProjectConfig.read(md);
-    AccessSection s = config.getAccessSection(ref, true);
-    Permission p = s.getPermission(permission, true);
-    AccountGroup adminGroup = groupCache.get(new AccountGroup.NameKey("Administrators"));
-    PermissionRule rule = new PermissionRule(config.resolve(adminGroup));
-    rule.setForce(force);
-    p.add(rule);
-    config.commit(md);
-    projectCache.evict(config.getProject());
-  }
-
-  protected void blockRead(Project.NameKey project, String ref) throws Exception {
-    ProjectConfig cfg = projectCache.checkedGet(project).getConfig();
-    block(cfg, Permission.READ, REGISTERED_USERS, ref);
-    saveProjectConfig(project, cfg);
-  }
-
-  protected void blockForgeCommitter(Project.NameKey project, String ref)
-      throws Exception {
-    ProjectConfig cfg = projectCache.checkedGet(project).getConfig();
-    block(cfg, Permission.FORGE_COMMITTER, REGISTERED_USERS, ref);
-    saveProjectConfig(project, cfg);
-  }
-
-  protected PushOneCommit.Result pushTo(String ref) throws GitAPIException,
-      IOException {
-    PushOneCommit push = pushFactory.create(db, admin.getIdent());
-    return push.to(git, ref);
-  }
-}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GitUtil.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GitUtil.java
deleted file mode 100644
index dee36ef..0000000
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GitUtil.java
+++ /dev/null
@@ -1,231 +0,0 @@
-// Copyright (C) 2013 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.acceptance;
-
-import static com.google.common.base.Preconditions.checkState;
-
-import com.google.common.collect.Iterables;
-import com.google.gerrit.common.FooterConstants;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.testutil.TempFileUtil;
-
-import com.jcraft.jsch.JSch;
-import com.jcraft.jsch.JSchException;
-import com.jcraft.jsch.Session;
-
-import org.eclipse.jgit.api.AddCommand;
-import org.eclipse.jgit.api.CheckoutCommand;
-import org.eclipse.jgit.api.CloneCommand;
-import org.eclipse.jgit.api.CommitCommand;
-import org.eclipse.jgit.api.FetchCommand;
-import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.api.PushCommand;
-import org.eclipse.jgit.api.errors.GitAPIException;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.transport.JschConfigSessionFactory;
-import org.eclipse.jgit.transport.OpenSshConfig.Host;
-import org.eclipse.jgit.transport.PushResult;
-import org.eclipse.jgit.transport.RefSpec;
-import org.eclipse.jgit.transport.SshSessionFactory;
-import org.eclipse.jgit.util.ChangeIdUtil;
-import org.eclipse.jgit.util.FS;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.nio.charset.StandardCharsets;
-import java.util.List;
-import java.util.Properties;
-
-public class GitUtil {
-
-  public static void initSsh(final TestAccount a) {
-    final Properties config = new Properties();
-    config.put("StrictHostKeyChecking", "no");
-    JSch.setConfig(config);
-
-    // register a JschConfigSessionFactory that adds the private key as identity
-    // to the JSch instance of JGit so that SSH communication via JGit can
-    // succeed
-    SshSessionFactory.setInstance(new JschConfigSessionFactory() {
-      @Override
-      protected void configure(Host hc, Session session) {
-        try {
-          final JSch jsch = getJSch(hc, FS.DETECTED);
-          jsch.addIdentity("KeyPair", a.privateKey(),
-              a.sshKey.getPublicKeyBlob(), null);
-        } catch (JSchException e) {
-          throw new RuntimeException(e);
-        }
-      }
-    });
-  }
-
-  public static void createProject(SshSession s, String name)
-      throws JSchException, IOException {
-    createProject(s, name, null);
-  }
-
-  public static void createProject(SshSession s, String name, Project.NameKey parent)
-      throws JSchException, IOException {
-    createProject(s, name, parent, true);
-  }
-
-  public static void createProject(SshSession s, String name,
-      Project.NameKey parent, boolean emptyCommit)
-      throws JSchException, IOException {
-    StringBuilder b = new StringBuilder();
-    b.append("gerrit create-project");
-    if (emptyCommit) {
-      b.append(" --empty-commit");
-    }
-    b.append(" --name \"");
-    b.append(name);
-    b.append("\"");
-    if (parent != null) {
-      b.append(" --parent \"");
-      b.append(parent.get());
-      b.append("\"");
-    }
-    s.exec(b.toString());
-    if (s.hasError()) {
-      throw new IllegalStateException(
-          "gerrit create-project returned error: " + s.getError());
-    }
-  }
-
-  public static Git cloneProject(String url) throws GitAPIException, IOException {
-    return cloneProject(url, true);
-  }
-
-  public static Git cloneProject(String url, boolean checkout) throws GitAPIException, IOException {
-    final File gitDir = TempFileUtil.createTempDirectory();
-    final CloneCommand cloneCmd = Git.cloneRepository();
-    cloneCmd.setURI(url);
-    cloneCmd.setDirectory(gitDir);
-    cloneCmd.setNoCheckout(!checkout);
-    return cloneCmd.call();
-  }
-
-  public static void add(Git git, String path, String content)
-      throws GitAPIException, IOException {
-    File f = new File(git.getRepository().getDirectory().getParentFile(), path);
-    File p = f.getParentFile();
-    if (!p.exists() && !p.mkdirs()) {
-      throw new IOException("failed to create dir: " + p.getAbsolutePath());
-    }
-    FileOutputStream s = new FileOutputStream(f);
-    BufferedWriter out = new BufferedWriter(
-        new OutputStreamWriter(s, StandardCharsets.UTF_8));
-    try {
-      out.write(content);
-    } finally {
-      out.close();
-    }
-
-    final AddCommand addCmd = git.add();
-    addCmd.addFilepattern(path);
-    addCmd.call();
-  }
-
-  public static void rm(Git gApi, String path)
-      throws GitAPIException {
-    gApi.rm()
-        .addFilepattern(path)
-        .call();
-  }
-
-  public static Commit createCommit(Git git, PersonIdent i, String msg)
-      throws GitAPIException {
-    return createCommit(git, i, msg, null);
-  }
-
-  public static Commit amendCommit(Git git, PersonIdent i, String msg, String changeId)
-      throws GitAPIException {
-    msg = ChangeIdUtil.insertId(msg, ObjectId.fromString(changeId.substring(1)));
-    return createCommit(git, i, msg, changeId);
-  }
-
-  private static Commit createCommit(Git git, PersonIdent i, String msg,
-      String changeId) throws GitAPIException {
-
-    final CommitCommand commitCmd = git.commit();
-    commitCmd.setAmend(changeId != null);
-    commitCmd.setAuthor(i);
-    commitCmd.setCommitter(i);
-    commitCmd.setMessage(msg);
-    commitCmd.setInsertChangeId(changeId == null);
-
-    RevCommit c = commitCmd.call();
-
-    List<String> ids = c.getFooterLines(FooterConstants.CHANGE_ID);
-    checkState(ids.size() >= 1,
-        "No Change-Id found in new commit:\n%s", c.getFullMessage());
-    changeId = ids.get(ids.size() - 1);
-
-    return new Commit(c, changeId);
-  }
-
-  public static void fetch(Git git, String spec) throws GitAPIException {
-    FetchCommand fetch = git.fetch();
-    fetch.setRefSpecs(new RefSpec(spec));
-    fetch.call();
-  }
-
-  public static void checkout(Git git, String name) throws GitAPIException {
-    CheckoutCommand checkout = git.checkout();
-    checkout.setName(name);
-    checkout.call();
-  }
-
-  public static PushResult pushHead(Git git, String ref, boolean pushTags)
-      throws GitAPIException {
-    return pushHead(git, ref, pushTags, false);
-  }
-
-  public static PushResult pushHead(Git git, String ref, boolean pushTags,
-      boolean force) throws GitAPIException {
-    PushCommand pushCmd = git.push();
-    pushCmd.setForce(force);
-    pushCmd.setRefSpecs(new RefSpec("HEAD:" + ref));
-    if (pushTags) {
-      pushCmd.setPushTags();
-    }
-    Iterable<PushResult> r = pushCmd.call();
-    return Iterables.getOnlyElement(r);
-  }
-
-  public static class Commit {
-    private final RevCommit commit;
-    private final String changeId;
-
-    Commit(RevCommit commit, String changeId) {
-      this.commit = commit;
-      this.changeId = changeId;
-    }
-
-    public RevCommit getCommit() {
-      return commit;
-    }
-
-    public String getChangeId() {
-      return changeId;
-    }
-  }
-}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/HttpSession.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/HttpSession.java
deleted file mode 100644
index f765e7a..0000000
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/HttpSession.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.acceptance;
-
-import com.google.common.base.CharMatcher;
-
-import org.apache.http.auth.AuthScope;
-import org.apache.http.auth.UsernamePasswordCredentials;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.BasicCredentialsProvider;
-import org.apache.http.impl.client.HttpClientBuilder;
-
-import java.io.IOException;
-import java.net.URI;
-
-public class HttpSession {
-
-  protected final String url;
-  private final TestAccount account;
-  private HttpClient client;
-
-  public HttpSession(GerritServer server, TestAccount account) {
-    this.url = CharMatcher.is('/').trimTrailingFrom(server.getUrl());
-    this.account = account;
-  }
-
-  public HttpResponse get(String path) throws IOException {
-    HttpGet get = new HttpGet(url + path);
-    return new HttpResponse(getClient().execute(get));
-  }
-
-  protected HttpClient getClient() {
-    if (client == null) {
-      URI uri = URI.create(url);
-      BasicCredentialsProvider creds = new BasicCredentialsProvider();
-      creds.setCredentials(new AuthScope(uri.getHost(), uri.getPort()),
-          new UsernamePasswordCredentials(account.username,
-              account.httpPassword));
-      client = HttpClientBuilder
-          .create()
-          .setDefaultCredentialsProvider(creds)
-          .setMaxConnPerRoute(512)
-          .setMaxConnTotal(1024)
-          .build();
-    }
-    return client;
-  }
-}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/SandboxTest.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/SandboxTest.java
new file mode 100644
index 0000000..6ead346
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/SandboxTest.java
@@ -0,0 +1,50 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.server.account.PutUsername;
+
+import org.apache.http.HttpStatus;
+import org.junit.After;
+import org.junit.Test;
+
+@Sandboxed
+public class SandboxTest extends AbstractDaemonTest {
+  @After
+  public void addUser() throws Exception {
+    PutUsername.Input in = new PutUsername.Input();
+    in.username = "sandboxuser";
+    RestResponse r =
+        adminSession.put("/accounts/sandboxuser", in);
+    assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_CREATED);
+  }
+
+  private void testUserNotPresent() throws Exception {
+    RestResponse r = adminSession.get("/accounts/sandboxuser");
+    assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NOT_FOUND);
+  }
+
+  @Test
+  public void testUserNotPresent1() throws Exception {
+    testUserNotPresent();
+  }
+
+  @Test
+  public void testUserNotPresent2() throws Exception {
+    testUserNotPresent();
+  }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index 8945a22..b6a54b6 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -14,15 +14,120 @@
 
 package com.google.gerrit.acceptance.api.accounts;
 
+import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assert_;
+import static com.google.gerrit.gpg.PublicKeyStore.REFS_GPG_KEYS;
+import static com.google.gerrit.gpg.PublicKeyStore.keyToString;
+import static com.google.gerrit.gpg.testutil.TestKeys.allValidKeys;
+import static com.google.gerrit.gpg.testutil.TestKeys.validKeyWithExpiration;
+import static com.google.gerrit.gpg.testutil.TestKeys.validKeyWithSecondUserId;
+import static com.google.gerrit.gpg.testutil.TestKeys.validKeyWithoutExpiration;
+import static java.nio.charset.StandardCharsets.UTF_8;
 
+import com.google.common.base.Function;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.BaseEncoding;
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.extensions.api.accounts.EmailInput;
 import com.google.gerrit.extensions.common.AccountInfo;
+import com.google.gerrit.extensions.common.GpgKeyInfo;
+import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.gpg.Fingerprint;
+import com.google.gerrit.gpg.PublicKeyStore;
+import com.google.gerrit.gpg.server.GpgKeys;
+import com.google.gerrit.gpg.testutil.TestKey;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.AccountExternalId;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.testutil.ConfigSuite;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
 
+import org.bouncycastle.bcpg.ArmoredOutputStream;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPPublicKeyRing;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.PushCertificateIdent;
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
 public class AccountIT extends AbstractDaemonTest {
+  @ConfigSuite.Default
+  public static Config enableSignedPushConfig() {
+    Config cfg = new Config();
+    cfg.setBoolean("receive", null, "enableSignedPush", true);
+    return cfg;
+  }
+
+  @Inject
+  private Provider<PublicKeyStore> publicKeyStoreProvider;
+
+  @Inject
+  private AllUsersName allUsers;
+
+  private List<AccountExternalId> savedExternalIds;
+
+  @Before
+  public void saveExternalIds() throws Exception {
+    savedExternalIds = new ArrayList<>();
+    savedExternalIds.addAll(getExternalIds(admin));
+    savedExternalIds.addAll(getExternalIds(user));
+  }
+
+  @After
+  public void restoreExternalIds() throws Exception {
+    db.accountExternalIds().delete(getExternalIds(admin));
+    db.accountExternalIds().delete(getExternalIds(user));
+    db.accountExternalIds().insert(savedExternalIds);
+  }
+
+  @After
+  public void clearPublicKeyStore() throws Exception {
+    try (Repository repo = repoManager.openRepository(allUsers)) {
+      Ref ref = repo.exactRef(REFS_GPG_KEYS);
+      if (ref != null) {
+        RefUpdate ru = repo.updateRef(REFS_GPG_KEYS);
+        ru.setForceUpdate(true);
+        assertThat(ru.delete()).isEqualTo(RefUpdate.Result.FORCED);
+      }
+    }
+  }
+
+  private List<AccountExternalId> getExternalIds(TestAccount account)
+      throws Exception {
+    return db.accountExternalIds().byAccount(account.getId()).toList();
+  }
+
+  @After
+  public void deleteGpgKeys() throws Exception {
+    String ref = REFS_GPG_KEYS;
+    try (Repository repo = repoManager.openRepository(allUsers)) {
+      if (repo.getRefDatabase().exactRef(ref) != null) {
+        RefUpdate ru = repo.updateRef(ref);
+        ru.setForceUpdate(true);
+        assert_().withFailureMessage("Failed to delete " + ref)
+            .that(ru.delete()).isEqualTo(RefUpdate.Result.FORCED);
+      }
+    }
+  }
 
   @Test
   public void get() throws Exception {
@@ -49,14 +154,284 @@
   @Test
   public void starUnstarChange() throws Exception {
     PushOneCommit.Result r = createChange();
-    String triplet = "p~master~" + r.getChangeId();
+    String triplet = project.get() + "~master~" + r.getChangeId();
     gApi.accounts()
         .self()
         .starChange(triplet);
-    assertThat(getChange(triplet).starred).isTrue();
+    assertThat(info(triplet).starred).isTrue();
     gApi.accounts()
         .self()
         .unstarChange(triplet);
-    assertThat(getChange(triplet).starred).isNull();
+    assertThat(info(triplet).starred).isNull();
+  }
+
+  @Test
+  public void suggestAccounts() throws Exception {
+    String adminUsername = "admin";
+    List<AccountInfo> result = gApi.accounts()
+        .suggestAccounts().withQuery(adminUsername).get();
+    assertThat(result).hasSize(1);
+    assertThat(result.get(0).username).isEqualTo(adminUsername);
+
+    List<AccountInfo> resultShortcutApi = gApi.accounts()
+        .suggestAccounts(adminUsername).get();
+    assertThat(resultShortcutApi).hasSize(result.size());
+
+    List<AccountInfo> emptyResult = gApi.accounts()
+        .suggestAccounts("unknown").get();
+    assertThat(emptyResult).isEmpty();
+  }
+
+  @Test
+  public void addEmail() throws Exception {
+    List<String> emails = ImmutableList.of(
+        "new.email@example.com", "new.email@example.systems");
+    for (String email : emails) {
+      EmailInput input = new EmailInput();
+      input.email = email;
+      input.noConfirmation = true;
+      gApi.accounts().self().addEmail(input);
+    }
+  }
+
+  @Test
+  public void addInvalidEmail() throws Exception {
+    EmailInput input  = new EmailInput();
+    input.email = "invalid@";
+    input.noConfirmation = true;
+
+    exception.expect(BadRequestException.class);
+    exception.expectMessage("invalid email address");
+    gApi.accounts().self().addEmail(input);
+  }
+
+  @Test
+  public void addGpgKey() throws Exception {
+    TestKey key = validKeyWithoutExpiration();
+    String id = key.getKeyIdString();
+    addExternalIdEmail(admin, "test1@example.com");
+
+    assertKeyMapContains(key, addGpgKey(key.getPublicKeyArmored()));
+    assertKeys(key);
+
+    setApiUser(user);
+    exception.expect(ResourceNotFoundException.class);
+    exception.expectMessage(id);
+    gApi.accounts().self().gpgKey(id).get();
+  }
+
+  @Test
+  public void reAddExistingGpgKey() throws Exception {
+    addExternalIdEmail(admin, "test5@example.com");
+    TestKey key = validKeyWithSecondUserId();
+    String id = key.getKeyIdString();
+    PGPPublicKey pk = key.getPublicKey();
+
+    GpgKeyInfo info = addGpgKey(armor(pk)).get(id);
+    assertThat(info.userIds).hasSize(2);
+    assertIteratorSize(2, getOnlyKeyFromStore(key).getUserIDs());
+
+    pk = PGPPublicKey.removeCertification(pk, "foo:myId");
+    info = addGpgKey(armor(pk)).get(id);
+    assertThat(info.userIds).hasSize(1);
+    assertIteratorSize(1, getOnlyKeyFromStore(key).getUserIDs());
+  }
+
+  @Test
+  public void addOtherUsersGpgKey_Conflict() throws Exception {
+    // Both users have a matching external ID for this key.
+    addExternalIdEmail(admin, "test5@example.com");
+    AccountExternalId extId = new AccountExternalId(
+        user.getId(), new AccountExternalId.Key("foo:myId"));
+
+    db.accountExternalIds().insert(Collections.singleton(extId));
+
+    TestKey key = validKeyWithSecondUserId();
+    addGpgKey(key.getPublicKeyArmored());
+    setApiUser(user);
+
+    exception.expect(ResourceConflictException.class);
+    exception.expectMessage("GPG key already associated with another account");
+    addGpgKey(key.getPublicKeyArmored());
+  }
+
+  @Test
+  public void listGpgKeys() throws Exception {
+    List<TestKey> keys = allValidKeys();
+    List<String> toAdd = new ArrayList<>(keys.size());
+    for (TestKey key : keys) {
+      addExternalIdEmail(admin,
+          PushCertificateIdent.parse(key.getFirstUserId()).getEmailAddress());
+      toAdd.add(key.getPublicKeyArmored());
+    }
+    gApi.accounts().self().putGpgKeys(toAdd, ImmutableList.<String> of());
+    assertKeys(keys);
+  }
+
+  @Test
+  public void deleteGpgKey() throws Exception {
+    TestKey key = validKeyWithoutExpiration();
+    String id = key.getKeyIdString();
+    addExternalIdEmail(admin, "test1@example.com");
+    addGpgKey(key.getPublicKeyArmored());
+    assertKeys(key);
+
+    gApi.accounts().self().gpgKey(id).delete();
+    assertKeys();
+
+    exception.expect(ResourceNotFoundException.class);
+    exception.expectMessage(id);
+    gApi.accounts().self().gpgKey(id).get();
+  }
+
+  @Test
+  public void addAndRemoveGpgKeys() throws Exception {
+    for (TestKey key : allValidKeys()) {
+      addExternalIdEmail(admin,
+          PushCertificateIdent.parse(key.getFirstUserId()).getEmailAddress());
+    }
+    TestKey key1 = validKeyWithoutExpiration();
+    TestKey key2 = validKeyWithExpiration();
+    TestKey key5 = validKeyWithSecondUserId();
+
+    Map<String, GpgKeyInfo> infos = gApi.accounts().self().putGpgKeys(
+        ImmutableList.of(
+          key1.getPublicKeyArmored(),
+          key2.getPublicKeyArmored()),
+        ImmutableList.of(key5.getKeyIdString()));
+    assertThat(infos.keySet())
+        .containsExactly(key1.getKeyIdString(), key2.getKeyIdString());
+    assertKeys(key1, key2);
+
+    infos = gApi.accounts().self().putGpgKeys(
+        ImmutableList.of(key5.getPublicKeyArmored()),
+        ImmutableList.of(key1.getKeyIdString()));
+    assertThat(infos.keySet())
+        .containsExactly(key1.getKeyIdString(), key5.getKeyIdString());
+    assertKeyMapContains(key5, infos);
+    assertThat(infos.get(key1.getKeyIdString()).key).isNull();
+    assertKeys(key2, key5);
+
+    exception.expect(BadRequestException.class);
+    exception.expectMessage("Cannot both add and delete key: "
+        + keyToString(key2.getPublicKey()));
+    infos = gApi.accounts().self().putGpgKeys(
+        ImmutableList.of(key2.getPublicKeyArmored()),
+        ImmutableList.of(key2.getKeyIdString()));
+  }
+
+  private PGPPublicKey getOnlyKeyFromStore(TestKey key) throws Exception {
+    try (PublicKeyStore store = publicKeyStoreProvider.get()) {
+      Iterable<PGPPublicKeyRing> keys = store.get(key.getKeyId());
+      assertThat(keys).hasSize(1);
+      return keys.iterator().next().getPublicKey();
+    }
+  }
+
+  private static String armor(PGPPublicKey key) throws Exception {
+    ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
+    try (ArmoredOutputStream aout = new ArmoredOutputStream(out)) {
+      key.encode(aout);
+    }
+    return new String(out.toByteArray(), UTF_8);
+  }
+
+  @SuppressWarnings({"unchecked", "rawtypes"})
+  private static void assertIteratorSize(int size, Iterator it) {
+    assertThat(ImmutableList.copyOf(it)).hasSize(size);
+  }
+
+  private static void assertKeyMapContains(TestKey expected,
+      Map<String, GpgKeyInfo> actualMap) {
+    GpgKeyInfo actual = actualMap.get(expected.getKeyIdString());
+    assertThat(actual).isNotNull();
+    assertThat(actual.id).isNull();
+    actual.id = expected.getKeyIdString();
+    assertKeyEquals(expected, actual);
+  }
+
+  private void assertKeys(TestKey... expectedKeys) throws Exception {
+    assertKeys(Arrays.asList(expectedKeys));
+  }
+
+  private void assertKeys(Iterable<TestKey> expectedKeys) throws Exception {
+    // Check via API.
+    FluentIterable<TestKey> expected = FluentIterable.from(expectedKeys);
+    Map<String, GpgKeyInfo> keyMap = gApi.accounts().self().listGpgKeys();
+    assertThat(keyMap.keySet())
+        .named("keys returned by listGpgKeys()")
+        .containsExactlyElementsIn(
+          expected.transform(new Function<TestKey, String>() {
+            @Override
+            public String apply(TestKey in) {
+              return in.getKeyIdString();
+            }
+          }));
+
+    for (TestKey key : expected) {
+      assertKeyEquals(key, gApi.accounts().self().gpgKey(
+          key.getKeyIdString()).get());
+      assertKeyEquals(key, gApi.accounts().self().gpgKey(
+          Fingerprint.toString(key.getPublicKey().getFingerprint())).get());
+      assertKeyMapContains(key, keyMap);
+    }
+
+    // Check raw external IDs.
+    Account.Id currAccountId = atrScope.get().getUser().getAccountId();
+    assertThat(
+        GpgKeys.getGpgExtIds(db, currAccountId)
+          .transform(new Function<AccountExternalId, String>() {
+            @Override
+            public String apply(AccountExternalId in) {
+              return in.getSchemeRest();
+            }
+          }))
+        .named("external IDs in database")
+        .containsExactlyElementsIn(
+            expected.transform(new Function<TestKey, String>() {
+              @Override
+              public String apply(TestKey in) {
+                return BaseEncoding.base16().encode(
+                    in.getPublicKey().getFingerprint());
+              }
+            }));
+
+    // Check raw stored keys.
+    for (TestKey key : expected) {
+      getOnlyKeyFromStore(key);
+    }
+  }
+
+  private static void assertKeyEquals(TestKey expected, GpgKeyInfo actual) {
+    String id = expected.getKeyIdString();
+    assertThat(actual.id).named(id).isEqualTo(id);
+    assertThat(actual.fingerprint).named(id).isEqualTo(
+        Fingerprint.toString(expected.getPublicKey().getFingerprint()));
+    @SuppressWarnings("unchecked")
+    List<String> userIds =
+        ImmutableList.copyOf(expected.getPublicKey().getUserIDs());
+    assertThat(actual.userIds).named(id).containsExactlyElementsIn(userIds);
+    assertThat(actual.key).named(id)
+        .startsWith("-----BEGIN PGP PUBLIC KEY BLOCK-----\n");
+    assertThat(actual.status).isEqualTo(GpgKeyInfo.Status.TRUSTED);
+    assertThat(actual.problems).isEmpty();
+  }
+
+  private void addExternalIdEmail(TestAccount account, String email)
+      throws Exception {
+    checkNotNull(email);
+    AccountExternalId extId = new AccountExternalId(
+        account.getId(), new AccountExternalId.Key(name("test"), email));
+    extId.setEmailAddress(email);
+    db.accountExternalIds().insert(Collections.singleton(extId));
+    // Clear saved AccountState and AccountExternalIds.
+    accountCache.evict(account.getId());
+    setApiUser(account);
+  }
+
+  private Map<String, GpgKeyInfo> addGpgKey(String armored) throws Exception {
+    return gApi.accounts().self().putGpgKeys(
+        ImmutableList.of(armored),
+        ImmutableList.<String> of());
   }
 }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/BUCK b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/BUCK
index 1152d88..814dcf4 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/BUCK
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/BUCK
@@ -1,6 +1,7 @@
 include_defs('//gerrit-acceptance-tests/tests.defs')
 
 acceptance_tests(
+  group = 'api-account',
   srcs = glob(['*IT.java']),
   labels = ['api'],
 )
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/BUCK b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/BUCK
index 1152d88..5db2054 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/BUCK
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/BUCK
@@ -1,6 +1,7 @@
 include_defs('//gerrit-acceptance-tests/tests.defs')
 
 acceptance_tests(
+  group = 'api-change',
   srcs = glob(['*IT.java']),
   labels = ['api'],
 )
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 5a2e05c..d92a887 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -15,6 +15,14 @@
 package com.google.gerrit.acceptance.api.change;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.PushOneCommit.FILE_NAME;
+import static com.google.gerrit.acceptance.PushOneCommit.SUBJECT;
+import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
+import static com.google.gerrit.server.group.SystemGroupBackend.CHANGE_OWNER;
+import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.server.project.Util.blockLabel;
+import static com.google.gerrit.server.project.Util.category;
+import static com.google.gerrit.server.project.Util.value;
 
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
@@ -22,22 +30,35 @@
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.NoHttpd;
 import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.TestProjectInput;
+import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.extensions.api.changes.AddReviewerInput;
 import com.google.gerrit.extensions.api.changes.RebaseInput;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
+import com.google.gerrit.extensions.api.changes.RevisionApi;
 import com.google.gerrit.extensions.client.ChangeStatus;
 import com.google.gerrit.extensions.client.ListChangesOption;
 import com.google.gerrit.extensions.common.ApprovalInfo;
 import com.google.gerrit.extensions.common.ChangeInfo;
+import com.google.gerrit