func mirrorRepoToReview(repo repository.Repo, tool review.Tool, syncToRemote bool) {
	if syncToRemote {
		if err := repo.PullUpdates(); err != nil {
			log.Printf("Failed to pull updates for the repo %v: %v\n", repo, err)
			return
		}
	}

	stateHash := repo.GetRepoStateHash()
	if processedStates[repo.GetPath()] != stateHash {
		log.Print("Mirroring repo: ", repo)
		for _, revision := range repo.ListNotedRevisions(request.Ref) {
			existingComments[revision] = comment.ParseAllValid(repo.GetNotes(comment.Ref, revision))
			for _, req := range request.ParseAllValid(repo.GetNotes(request.Ref, revision)) {
				tool.EnsureRequestExists(repo, revision, req, existingComments[revision])
			}
		}
		openReviews[repo.GetPath()] = tool.ListOpenReviews(repo)
		processedStates[repo.GetPath()] = stateHash
		tool.Refresh(repo)
	}
	for _, review := range openReviews[repo.GetPath()] {
		if reviewCommit := review.GetFirstCommit(repo); reviewCommit != nil {
			log.Println("Processing review: ", *reviewCommit)
			revisionComments := getExistingComments(*reviewCommit)
			log.Printf("Loaded %d comments for %v\n", len(revisionComments), *reviewCommit)
			for _, c := range review.LoadComments() {
				if !hasOverlap(c, revisionComments) {
					// The comment is new.
					note, err := c.Write()
					if err != nil {
						log.Fatal(err)
					}
					log.Printf("Appending a comment: %s", string(note))
					repo.AppendNote(comment.Ref, *reviewCommit, note, c.Author)
				} else {
					log.Printf("Skipping '%v', as it has already been written\n", c)
				}
			}
		}
	}
	if syncToRemote {
		if err := repo.PushUpdates(); err != nil {
			log.Printf("Failed to push updates to the repo %v: %v\n", repo, err)
		}
	}
}
func mirrorRepoToReview(repo repository.Repo, tool review_utils.Tool, syncToRemote bool) {
	if syncToRemote {
		repo.PullNotes("origin", "refs/notes/devtools/*")
	}

	stateHash, err := repo.GetRepoStateHash()
	if err != nil {
		log.Fatal(err)
	}
	if processedStates[repo.GetPath()] != stateHash {
		log.Print("Mirroring repo: ", repo)
		for _, r := range review.ListAll(repo) {
			reviewJson, err := r.GetJSON()
			if err != nil {
				log.Fatal(err)
			}
			log.Println("Mirroring review: ", reviewJson)
			existingComments[r.Revision] = r.Comments
			reviewDetails, err := r.Details()
			if err == nil {
				tool.EnsureRequestExists(repo, *reviewDetails)
			}
		}
		openReviews[repo.GetPath()] = tool.ListOpenReviews(repo)
		processedStates[repo.GetPath()] = stateHash
		tool.Refresh(repo)
	}
ReviewLoop:
	for _, phabricatorReview := range openReviews[repo.GetPath()] {
		if reviewCommit := phabricatorReview.GetFirstCommit(repo); reviewCommit != "" {
			log.Println("Processing review: ", reviewCommit)
			r, err := review.GetSummary(repo, reviewCommit)
			if err != nil {
				log.Fatal(err)
			} else if r == nil {
				log.Printf("Skipping unknown review %q", reviewCommit)
				continue ReviewLoop
			}
			revisionComments := existingComments[reviewCommit]
			log.Printf("Loaded %d comments for %v\n", len(revisionComments), reviewCommit)
			for _, c := range phabricatorReview.LoadComments() {
				if !hasOverlap(c, revisionComments) {
					// The comment is new.
					note, err := c.Write()
					if err != nil {
						log.Fatal(err)
					}
					log.Printf("Appending a comment: %s", string(note))
					repo.AppendNote(comment.Ref, reviewCommit, note)
				} else {
					log.Printf("Skipping '%v', as it has already been written\n", c)
				}
			}
		}
	}
	if syncToRemote {
		if err := repo.PushNotes("origin", "refs/notes/devtools/*"); err != nil {
			log.Printf("Failed to push updates to the repo %v: %v\n", repo, err)
		}
	}
}