def commitRangeFromReview(db, user, review, filter, file_ids): edges = cursor = db.cursor() if filter == "pending": cursor.execute("""SELECT DISTINCT changesets.parent, changesets.child FROM changesets JOIN reviewfiles ON (reviewfiles.changeset=changesets.id) JOIN reviewuserfiles ON (reviewuserfiles.file=reviewfiles.id) WHERE reviewfiles.review=%s AND reviewuserfiles.uid=%s AND reviewfiles.state='pending'""", (review.id, user.id)) elif filter == "reviewable": cursor.execute("""SELECT DISTINCT changesets.parent, changesets.child FROM changesets JOIN reviewfiles ON (reviewfiles.changeset=changesets.id) JOIN reviewuserfiles ON (reviewuserfiles.file=reviewfiles.id) WHERE reviewfiles.review=%s AND reviewuserfiles.uid=%s""", (review.id, user.id)) elif filter == "relevant": filters = review_filters.Filters() filters.load(db, review=review, user=user) cursor.execute("""SELECT DISTINCT changesets.parent, changesets.child, reviewfiles.file, reviewuserfiles.uid IS NOT NULL FROM changesets JOIN reviewfiles ON (reviewfiles.changeset=changesets.id) LEFT OUTER JOIN reviewuserfiles ON (reviewuserfiles.file=reviewfiles.id AND reviewuserfiles.uid=%s) WHERE reviewfiles.review=%s""", (user.id, review.id)) edges = set() for parent_id, child_id, file_id, is_reviewer in cursor: if is_reviewer or filters.isRelevant(db, user, file_id): edges.add((parent_id, child_id)) elif filter == "files": assert len(file_ids) != 0 cursor.execute("""SELECT DISTINCT changesets.parent, changesets.child FROM changesets JOIN reviewchangesets ON (reviewchangesets.changeset=changesets.id) JOIN fileversions ON (fileversions.changeset=changesets.id) WHERE reviewchangesets.review=%s AND fileversions.file=ANY (%s)""", (review.id, list(file_ids))) else: raise Exception, "invalid filter: %s" % filter listed_commits = set() with_pending = set() for parent_id, child_id in edges: listed_commits.add(child_id) with_pending.add((parent_id, child_id)) if len(listed_commits) == 1: return None, gitutils.Commit.fromId(db, review.repository, child_id).sha1, list(listed_commits), listed_commits if filter in ("reviewable", "relevant", "files"): cursor.execute("SELECT child FROM changesets JOIN reviewchangesets ON (changeset=id) WHERE review=%s", (review.id,)) all_commits = [gitutils.Commit.fromId(db, review.repository, commit_id) for (commit_id,) in cursor] commitset = CommitSet(review.branch.commits) tails = commitset.getFilteredTails(review.repository) if len(commitset) == 0: raise Exception, "empty commit-set" elif len(tails) > 1: ancestor = review.repository.getCommonAncestor(tails) paths = [] cursor.execute("SELECT DISTINCT file FROM reviewfiles WHERE review=%s", (review.id,)) files_in_review = set(file_id for (file_id,) in cursor) if filter == "files": files_in_review &= file_ids paths_in_review = set(dbutils.describe_file(db, file_id) for file_id in files_in_review) paths_in_upstreams = set() for tail in tails: paths_in_upstream = set(review.repository.run("diff", "--name-only", "%s..%s" % (ancestor, tail)).splitlines()) paths_in_upstreams |= paths_in_upstream paths.append((tail, paths_in_upstream)) overlapping_changes = paths_in_review & paths_in_upstreams if overlapping_changes: candidates = [] for index1, data in enumerate(paths): for index2, (tail, paths_in_upstream) in enumerate(paths): if index1 != index2 and paths_in_upstream & paths_in_review: break else: candidates.append(data) else: candidates = paths if not candidates: paths.sort(cmp=lambda a, b: cmp(len(a[1]), len(b[1]))) url = "/%s/%s..%s?file=%s" % (review.repository.name, paths[0][0][:8], review.branch.head.sha1[:8], ",".join(map(str, sorted(files_in_review)))) message = """\ <p>It is not possible to generate a diff of the requested set of commits that contains only changes from those commits.</p> <p>The following files would contain unrelated changes:<p> <pre style='padding-left: 2em'>%s</pre> <p>You can use the URL below if you want to view this diff anyway, including the unrelated changes.</p> <pre style='padding-left: 2em'><a href='%s'>%s%s</a></pre>""" % ("\n".join(sorted(overlapping_changes)), url, dbutils.getURLPrefix(db), url) raise page.utils.DisplayMessage(title="Impossible Diff", body=message, review=review, html=True) else: candidates.sort(cmp=lambda a, b: cmp(len(b[1]), len(a[1]))) return candidates[0][0], review.branch.head.sha1, all_commits, listed_commits elif len(tails) == 0: raise Exception, "impossible commit-set (%r)" % commitset return tails.pop(), review.branch.head.sha1, all_commits, listed_commits
def getFilteredTails(self): commitset = CommitSet(self.branch.commits) return commitset.getFilteredTails(self.branch.repository)