Skip to main content

weavatrix_git/
repository_set_query.rs

1use std::{
2    collections::{BTreeMap, BTreeSet},
3    thread,
4};
5
6use crate::{
7    CommitSnapshot, HistoryOptions, ObjectId, RepositoryHistory, RepositoryId, RepositorySet,
8    Result, SharedCommit, TreeChange,
9};
10
11#[derive(Clone, Debug, Eq, PartialEq)]
12pub struct RepositorySnapshot {
13    pub repository: RepositoryId,
14    pub snapshot: CommitSnapshot,
15}
16
17#[derive(Clone, Debug, Eq, PartialEq)]
18pub struct RepositoryTimelineEntry {
19    pub repository: RepositoryId,
20    pub id: ObjectId,
21    pub tree: ObjectId,
22    pub parents: Vec<ObjectId>,
23    pub committer_time: i64,
24}
25
26#[derive(Clone, Debug, Eq, PartialEq)]
27pub struct RepositoryChangeSet {
28    pub repository: RepositoryId,
29    pub old: ObjectId,
30    pub new: ObjectId,
31    pub changes: Vec<TreeChange>,
32}
33
34impl RepositorySet {
35    pub fn histories_from(
36        &self,
37        revision: &str,
38        options: HistoryOptions,
39    ) -> Result<Vec<RepositoryHistory>> {
40        self.ids()
41            .map(|id| history(self, id, revision, options))
42            .collect()
43    }
44
45    pub fn histories_from_parallel(
46        &self,
47        revision: &str,
48        options: HistoryOptions,
49    ) -> Result<Vec<RepositoryHistory>> {
50        thread::scope(|scope| {
51            self.ids()
52                .map(|id| scope.spawn(move || history(self, id, revision, options)))
53                .collect::<Vec<_>>()
54                .into_iter()
55                .map(join)
56                .collect()
57        })
58    }
59
60    pub fn snapshots(&self, revision: &str) -> Result<Vec<RepositorySnapshot>> {
61        self.ids().map(|id| snapshot(self, id, revision)).collect()
62    }
63
64    pub fn snapshots_parallel(&self, revision: &str) -> Result<Vec<RepositorySnapshot>> {
65        thread::scope(|scope| {
66            self.ids()
67                .map(|id| scope.spawn(move || snapshot(self, id, revision)))
68                .collect::<Vec<_>>()
69                .into_iter()
70                .map(join)
71                .collect()
72        })
73    }
74
75    pub fn timeline(
76        &self,
77        revision: &str,
78        options: HistoryOptions,
79    ) -> Result<Vec<RepositoryTimelineEntry>> {
80        let mut entries = thread::scope(|scope| {
81            self.ids()
82                .map(|id| scope.spawn(move || repository_timeline(self, id, revision, options)))
83                .collect::<Vec<_>>()
84                .into_iter()
85                .map(join)
86                .collect::<Result<Vec<_>>>()
87        })?
88        .into_iter()
89        .flatten()
90        .collect::<Vec<_>>();
91        entries.sort_unstable_by(|left, right| {
92            right
93                .committer_time
94                .cmp(&left.committer_time)
95                .then_with(|| left.repository.cmp(&right.repository))
96                .then_with(|| left.id.cmp(&right.id))
97        });
98        Ok(entries)
99    }
100
101    pub fn shared_commits_from(
102        &self,
103        revision: &str,
104        options: HistoryOptions,
105    ) -> Result<Vec<SharedCommit>> {
106        let mut repositories = BTreeMap::<ObjectId, BTreeSet<RepositoryId>>::new();
107        for history in self.histories_from_parallel(revision, options)? {
108            for id in history.commits {
109                repositories
110                    .entry(id)
111                    .or_default()
112                    .insert(history.repository);
113            }
114        }
115        Ok(repositories
116            .into_iter()
117            .filter_map(|(id, repositories)| {
118                (repositories.len() > 1).then(|| SharedCommit {
119                    id,
120                    repositories: repositories.into_iter().collect(),
121                })
122            })
123            .collect())
124    }
125
126    pub fn changes(
127        &self,
128        old_revision: &str,
129        new_revision: &str,
130    ) -> Result<Vec<RepositoryChangeSet>> {
131        self.ids()
132            .map(|id| changes(self, id, old_revision, new_revision))
133            .collect()
134    }
135
136    pub fn changes_parallel(
137        &self,
138        old_revision: &str,
139        new_revision: &str,
140    ) -> Result<Vec<RepositoryChangeSet>> {
141        thread::scope(|scope| {
142            self.ids()
143                .map(|id| scope.spawn(move || changes(self, id, old_revision, new_revision)))
144                .collect::<Vec<_>>()
145                .into_iter()
146                .map(join)
147                .collect()
148        })
149    }
150}
151
152fn history(
153    set: &RepositorySet,
154    id: RepositoryId,
155    revision: &str,
156    options: HistoryOptions,
157) -> Result<RepositoryHistory> {
158    let repository = set.get(id)?;
159    let head = repository.resolve(revision)?;
160    Ok(RepositoryHistory {
161        repository: id,
162        head,
163        commits: repository.history_ids(head, options)?,
164    })
165}
166
167fn snapshot(set: &RepositorySet, id: RepositoryId, revision: &str) -> Result<RepositorySnapshot> {
168    Ok(RepositorySnapshot {
169        repository: id,
170        snapshot: set.get(id)?.snapshot(revision)?,
171    })
172}
173
174fn repository_timeline(
175    set: &RepositorySet,
176    id: RepositoryId,
177    revision: &str,
178    options: HistoryOptions,
179) -> Result<Vec<RepositoryTimelineEntry>> {
180    let repository = set.get(id)?;
181    let start = repository.resolve(revision)?;
182    repository
183        .history_ids(start, options)?
184        .into_iter()
185        .map(|commit_id| {
186            let commit = repository.commit_metadata(commit_id)?;
187            Ok(RepositoryTimelineEntry {
188                repository: id,
189                id: commit.id,
190                tree: commit.tree,
191                parents: commit.parents,
192                committer_time: commit.committer_time,
193            })
194        })
195        .collect()
196}
197
198fn changes(
199    set: &RepositorySet,
200    id: RepositoryId,
201    old_revision: &str,
202    new_revision: &str,
203) -> Result<RepositoryChangeSet> {
204    let repository = set.get(id)?;
205    let old = repository.resolve(old_revision)?;
206    let new = repository.resolve(new_revision)?;
207    Ok(RepositoryChangeSet {
208        repository: id,
209        old,
210        new,
211        changes: repository.diff_commits(old, new)?,
212    })
213}
214
215fn join<T>(handle: thread::ScopedJoinHandle<'_, Result<T>>) -> Result<T> {
216    handle
217        .join()
218        .map_err(|_| crate::GitError::Unsupported("repository worker panicked".to_owned()))?
219}