Merge "Add --change-url parameter to hooks."
diff --git a/Documentation/cmd-ls-projects.txt b/Documentation/cmd-ls-projects.txt
index 1200e18..1e7b4cc 100644
--- a/Documentation/cmd-ls-projects.txt
+++ b/Documentation/cmd-ls-projects.txt
@@ -32,6 +32,11 @@
 \-b::
 	Name of the branch for which the command will display the sha of each project.
 
+\--tree::
+\-t::
+	Displays project inheritance in a tree-like format.
+	This option does not work together with the show-branch option.
+
 EXAMPLES
 --------
 
diff --git a/ReleaseNotes/ReleaseNotes-2.1.2.5.txt b/ReleaseNotes/ReleaseNotes-2.1.2.5.txt
new file mode 100644
index 0000000..1cd5ae8
--- /dev/null
+++ b/ReleaseNotes/ReleaseNotes-2.1.2.5.txt
@@ -0,0 +1,24 @@
+Release notes for Gerrit 2.1.2.5
+================================
+
+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]
+
+Bug Fixes
+---------
+
+* issue 390 Resolve objects going missing
++
+Clients disconnecting from the SSH server sometimes caused an
+interrupt to be delivered to their corresponding server work thread.
+That interrupt delivered at the wrong time caused a file to be
+closed unexpectedly, resulting in JGit marking the file as invalid
+and thereby losing access to its contents.  Fixed by serializing
+access to the file.
+
+* ps: Fix implementation to alias to gerrit show-queue
++
+The SSH command `ps` was meant to be an alias for `gerrit show-queue`
+but due to a copy-and-paste error was actually an alias for a
+different command.  Fixed.
diff --git a/ReleaseNotes/index.txt b/ReleaseNotes/index.txt
index acf20a3..4a9cc3f 100644
--- a/ReleaseNotes/index.txt
+++ b/ReleaseNotes/index.txt
@@ -4,6 +4,7 @@
 [[2_1]]
 Version 2.1.x
 -------------
+* link:ReleaseNotes-2.1.2.5.html[2.1.2.5]
 * link:ReleaseNotes-2.1.2.4.html[2.1.2.4]
 * link:ReleaseNotes-2.1.2.3.html[2.1.2.3]
 * link:ReleaseNotes-2.1.2.2.html[2.1.2.2]
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AgreementInfoFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AgreementInfoFactory.java
index 8e22741..f638d48 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AgreementInfoFactory.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AgreementInfoFactory.java
@@ -25,6 +25,7 @@
 import com.google.inject.Inject;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -49,11 +50,18 @@
   public AgreementInfo call() throws Exception {
     final List<AccountAgreement> userAccepted =
         db.accountAgreements().byAccount(user.getAccountId()).toList();
+
+    Collections.reverse(userAccepted);
+
     final List<AccountGroupAgreement> groupAccepted =
         new ArrayList<AccountGroupAgreement>();
     for (final AccountGroup.Id groupId : user.getEffectiveGroups()) {
-      groupAccepted.addAll(db.accountGroupAgreements().byGroup(groupId)
-          .toList());
+      final List<AccountGroupAgreement> temp =
+          db.accountGroupAgreements().byGroup(groupId).toList();
+
+      Collections.reverse(temp);
+
+      groupAccepted.addAll(temp);
     }
 
     final Map<ContributorAgreement.Id, ContributorAgreement> agreements =
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountAgreementAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountAgreementAccess.java
index b9f8905..f65af5e 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountAgreementAccess.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountAgreementAccess.java
@@ -25,6 +25,6 @@
   @PrimaryKey("key")
   AccountAgreement get(AccountAgreement.Key key) throws OrmException;
 
-  @Query("WHERE key.accountId = ? ORDER BY acceptedOn DESC")
+  @Query("WHERE key.accountId = ? ORDER BY acceptedOn")
   ResultSet<AccountAgreement> byAccount(Account.Id id) throws OrmException;
 }
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupAgreementAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupAgreementAccess.java
index d471934..d4a1fcd 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupAgreementAccess.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupAgreementAccess.java
@@ -25,7 +25,7 @@
   @PrimaryKey("key")
   AccountGroupAgreement get(AccountGroupAgreement.Key key) throws OrmException;
 
-  @Query("WHERE key.groupId = ? ORDER BY acceptedOn DESC")
+  @Query("WHERE key.groupId = ? ORDER BY acceptedOn")
   ResultSet<AccountGroupAgreement> byGroup(AccountGroup.Id id)
       throws OrmException;
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
index 6e8ae14..4ae0304 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -291,8 +291,12 @@
     ContributorAgreement bestCla = null;
 
     OUTER: for (AccountGroup.Id groupId : currentUser.getEffectiveGroups()) {
-      for (final AccountGroupAgreement a : db.accountGroupAgreements().byGroup(
-          groupId)) {
+      final List<AccountGroupAgreement> temp =
+          db.accountGroupAgreements().byGroup(groupId).toList();
+
+      Collections.reverse(temp);
+
+      for (final AccountGroupAgreement a : temp) {
         final ContributorAgreement cla =
             db.contributorAgreements().get(a.getAgreementId());
         if (cla == null) {
@@ -306,8 +310,12 @@
     }
 
     if (bestAgreement == null) {
-      for (final AccountAgreement a : db.accountAgreements().byAccount(
-          currentUser.getAccountId()).toList()) {
+      final List<AccountAgreement> temp =
+          db.accountAgreements().byAccount(currentUser.getAccountId()).toList();
+
+      Collections.reverse(temp);
+
+      for (final AccountAgreement a : temp) {
         final ContributorAgreement cla =
             db.contributorAgreements().get(a.getAgreementId());
         if (cla == null) {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListProjects.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListProjects.java
index fda0448..7618432 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListProjects.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListProjects.java
@@ -33,8 +33,16 @@
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.TreeMap;
 
 final class ListProjects extends BaseCommand {
+  private static final String NODE_PREFIX = "|-- ";
+  private static final String LAST_NODE_PREFIX = "`-- ";
+  private static final String DEFAULT_TAB_SEPARATOR = "|";
+  private static final String NOT_VISIBLE_PROJECT = "(x)";
+
   @Inject
   private ReviewDb db;
 
@@ -54,6 +62,12 @@
   @Option(name = "--show-branch", aliases = {"-b"}, usage = "displays the sha of each project in the specified branch")
   private String showBranch;
 
+  @Option(name = "--tree", aliases = {"-t"}, usage = "displays project inheritance in a tree-like format\n" +
+      "this option does not work together with the show-branch option")
+  private boolean showTree;
+
+  private String currentTabSeparator = DEFAULT_TAB_SEPARATOR;
+
   @Override
   public void start(final Environment env) {
     startThread(new CommandRunnable() {
@@ -66,7 +80,18 @@
   }
 
   private void display() throws Failure {
+    if (showTree && (showBranch != null)) {
+      throw new UnloggedFailure(1, "fatal: --tree and --show-branch options are not compatible.");
+    }
+
     final PrintWriter stdout = toPrintWriter(out);
+
+    TreeMap<String, TreeNode> treeMap = null;
+
+    if (showTree) {
+      treeMap = new TreeMap<String, TreeNode>();
+    }
+
     try {
       for (final Project p : db.projects().all()) {
         if (p.getNameKey().equals(wildProject)) {
@@ -83,27 +108,58 @@
         }
 
         final ProjectControl pctl = e.controlFor(currentUser);
-        if (!pctl.isVisible()) {
-          // Require the project itself to be visible to the user.
-          //
-          continue;
-        }
 
-        if (showBranch != null) {
-          final Ref ref = getBranchRef(p.getNameKey());
-          if (ref == null || ref.getObjectId() == null
-              || !pctl.controlForRef(ref.getLeaf().getName()).isVisible()) {
-            // No branch, or the user can't see this branch, so skip it.
+        if (!showTree) {
+
+          if (!pctl.isVisible()) {
+            // Require the project itself to be visible to the user.
             //
             continue;
           }
 
-          stdout.print(ref.getObjectId().name());
-          stdout.print(' ');
+          if (showBranch != null) {
+            final Ref ref = getBranchRef(p.getNameKey());
+            if (ref == null || ref.getObjectId() == null
+                || !pctl.controlForRef(ref.getLeaf().getName()).isVisible()) {
+              // No branch, or the user can't see this branch, so skip it.
+              //
+              continue;
+            }
+
+            stdout.print(ref.getObjectId().name());
+            stdout.print(' ');
+          }
+
+          stdout.print(p.getName() + "\n");
+        } else {
+          treeMap.put(p.getName(), new TreeNode(p, pctl.isVisible()));
+        }
+      }
+
+      if (showTree && treeMap.size() > 0) {
+        final List<TreeNode> sortedNodes = new ArrayList<TreeNode>();
+
+        // Builds the inheritance tree using a list.
+        //
+        for (final TreeNode key : treeMap.values()) {
+          final String parentName = key.getParentName();
+          if (parentName != null) {
+            final TreeNode node = treeMap.get((String)parentName);
+            if (node != null) {
+              node.addChild(key);
+            } else {
+              sortedNodes.add(key);
+            }
+          } else {
+            sortedNodes.add(key);
+          }
         }
 
-        stdout.print(p.getName());
-        stdout.println();
+        // Builds a fake root node, which contains the sorted projects.
+        //
+        final TreeNode fakeRoot = new TreeNode(null, sortedNodes, false);
+        printElement(stdout, fakeRoot, -1, false, sortedNodes.get(sortedNodes.size() - 1));
+        stdout.flush();
       }
     } catch (OrmException e) {
       throw new Failure(1, "fatal: database error", e);
@@ -124,4 +180,139 @@
       return null;
     }
   }
+
+  /** Class created to manipulate the nodes of the project inheritance tree **/
+  private static class TreeNode {
+    private final List<TreeNode> children;
+    private final Project project;
+    private final boolean isVisible;
+
+    /**
+     * Constructor
+     * @param p Project
+     */
+    public TreeNode(Project p, boolean visible) {
+      this.children = new ArrayList<TreeNode>();
+      this.project = p;
+      this.isVisible = visible;
+    }
+
+    /**
+     * Constructor used for creating the fake node
+     * @param p Project
+     * @param c List of nodes
+     */
+    public TreeNode(Project p, List<TreeNode> c, boolean visible) {
+      this.children = c;
+      this.project = p;
+      this.isVisible = visible;
+    }
+
+    /**
+     * Returns if the the node is leaf
+     * @return True if is lead, false, otherwise
+     */
+    public boolean isLeaf() {
+      return children.size() == 0;
+    }
+
+    /**
+     * Returns the project parent name
+     * @return Project parent name
+     */
+    public String getParentName() {
+      if (project.getParent() != null) {
+        return project.getParent().get();
+      }
+
+      return null;
+    }
+
+    /**
+     * Adds a child to the list
+     * @param node TreeNode child
+     */
+    public void addChild(TreeNode node) {
+      children.add(node);
+    }
+
+    /**
+     * Returns the project instance
+     * @return Project instance
+     */
+    public Project getProject() {
+      return project;
+    }
+
+    /**
+     * Returns the list of children nodes
+     * @return List of children nodes
+     */
+    public List<TreeNode> getChildren() {
+      return children;
+    }
+
+    /**
+     * Returns if the project is visible to the user
+     * @return True if is visible, false, otherwise
+     */
+    public boolean isVisible() {
+      return isVisible;
+    }
+  }
+
+  /**
+   * Used to display the project inheritance tree recursively
+   * @param stdout PrintWriter used do print
+   * @param node Tree node
+   * @param level Current level of the tree
+   * @param isLast True, if is the last node of a level, false, otherwise
+   * @param lastParentNode Last "root" parent node
+   */
+  private void printElement(final PrintWriter stdout, TreeNode node, int level, boolean isLast,
+      final TreeNode lastParentNode) {
+    // Checks if is not the "fake" root project.
+    //
+    if (node.getProject() != null) {
+
+      // Check if is not the last "root" parent node,
+      // so the "|" separator will not longer be needed.
+      //
+      if (!currentTabSeparator.equals(" ")) {
+        final String nodeProject = node.getProject().getName();
+        final String lastParentProject = lastParentNode.getProject().getName();
+
+        if (nodeProject.equals(lastParentProject)) {
+          currentTabSeparator = " ";
+        }
+      }
+
+      if (level > 0) {
+        stdout.print(String.format("%-" + 4 * level + "s", currentTabSeparator));
+      }
+
+      final String prefix = isLast ? LAST_NODE_PREFIX : NODE_PREFIX ;
+
+      String printout;
+
+      if (node.isVisible()) {
+        printout = prefix + node.getProject().getName();
+      } else {
+        printout = prefix + NOT_VISIBLE_PROJECT;
+      }
+
+      stdout.print(printout + "\n");
+    }
+
+    if (node.isLeaf()) {
+      return;
+    } else {
+      final List<TreeNode> children = node.getChildren();
+      ++level;
+      for(TreeNode treeNode : children) {
+        final boolean isLastIndex = children.indexOf(treeNode) == children.size() - 1;
+        printElement(stdout, treeNode, level, isLastIndex, lastParentNode);
+      }
+    }
+  }
 }