summaryrefslogtreecommitdiffstats
path: root/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/IncludedInDetailFactory.java
blob: c07ee517bd3431f565005d44f046298343a60290 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// Copyright (C) 2010 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.gerrit.httpd.rpc.changedetail;

import com.google.gerrit.common.data.IncludedInDetail;
import com.google.gerrit.common.errors.InvalidRevisionException;
import com.google.gerrit.httpd.rpc.Handler;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;

import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/** Creates a {@link IncludedInDetail} of a {@link Change}. */
class IncludedInDetailFactory extends Handler<IncludedInDetail> {
  private static final Logger log =
      LoggerFactory.getLogger(IncludedInDetailFactory.class);

  interface Factory {
    IncludedInDetailFactory create(Change.Id id);
  }

  private final ReviewDb db;
  private final ChangeControl.Factory changeControlFactory;
  private final GitRepositoryManager repoManager;
  private final Change.Id changeId;

  private IncludedInDetail detail;
  private ChangeControl control;

  @Inject
  IncludedInDetailFactory(final ReviewDb db,
      final ChangeControl.Factory changeControlFactory,
      final GitRepositoryManager repoManager, @Assisted final Change.Id changeId) {
    this.changeControlFactory = changeControlFactory;
    this.repoManager = repoManager;
    this.changeId = changeId;
    this.db = db;
  }

  @Override
  public IncludedInDetail call() throws OrmException, NoSuchChangeException,
      IOException, InvalidRevisionException {
    control = changeControlFactory.validateFor(changeId);

    final PatchSet patch =
        db.patchSets().get(control.getChange().currentPatchSetId());
    final Repository repo =
        repoManager.openRepository(control.getProject().getNameKey());
    try {
      final RevWalk rw = new RevWalk(repo);
      try {
        rw.setRetainBody(false);

        final RevCommit rev;
        try {
          rev = rw.parseCommit(ObjectId.fromString(patch.getRevision().get()));
        } catch (IncorrectObjectTypeException err) {
          throw new InvalidRevisionException();
        } catch (MissingObjectException err) {
          throw new InvalidRevisionException();
        }

        detail = new IncludedInDetail();
        detail.setBranches(includedIn(repo, rw, rev, Constants.R_HEADS));
        detail.setTags(includedIn(repo, rw, rev, Constants.R_TAGS));

        return detail;
      } finally {
        rw.release();
      }
    } finally {
      repo.close();
    }
  }

  private List<String> includedIn(final Repository repo, final RevWalk rw,
      final RevCommit rev, final String namespace) throws IOException,
      MissingObjectException, IncorrectObjectTypeException {
    final List<String> result = new ArrayList<String>();
    for (final Ref ref : repo.getRefDatabase().getRefs(namespace).values()) {
      final RevCommit tip;
      try {
        tip = rw.parseCommit(ref.getObjectId());
      } catch (IncorrectObjectTypeException notCommit) {
        // Its OK for a tag reference to point to a blob or a tree, this
        // is common in the Linux kernel or git.git repository.
        //
        continue;
      } catch (MissingObjectException notHere) {
        // Log the problem with this branch, but keep processing.
        //
        log.warn("Reference " + ref.getName() + " in " + repo.getDirectory()
            + " points to dangling object " + ref.getObjectId());
        continue;
      }

      if (rw.isMergedInto(rev, tip)) {
        result.add(ref.getName().substring(namespace.length()));
      }
    }
    return result;
  }
}