aboutsummaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorMx Kookie <kookie@spacekookie.de>2020-11-03 23:37:50 +0100
committerMx Kookie <kookie@spacekookie.de>2020-12-21 05:19:32 +0100
commit6ef94bb64d42b9b73ad2f9f74efb99012c8e6d1c (patch)
treeae6ab9c08f6e50a2dfe2bfc8f28d3f5237da5077 /apps
parent70fe187f1e118b6e63e512b6718635561682dad4 (diff)
octopus: implement full recursive branch parsing
This code implements a parsing strategy that uses lazy iterators to traverse a branch graph. An iterator is constructed for a starting point on a branch, and new iterators are spawned for every merge that is encountered. To get all commits in a repository, simply do as test.rs: queue new work to a channel, that you poll from until no more branches have been discovered. This code is somewhat suboptimal. For one, get_parent() is way too complex, and could use some refactoring. Secondly, the semantics of `BranchCommit::Branch(...)` are unclear from the outside, and the fact that simple merge commits will be returned via `BranchCommit::Commit`, while subsequent merge commits need to use `BranchCommit::Merge(...)` is inconsistent and should be fixed before doing any sort of public release!
Diffstat (limited to 'apps')
-rw-r--r--apps/servers/octopus/supergit/src/bin/test.rs11
-rw-r--r--apps/servers/octopus/supergit/src/branch.rs84
2 files changed, 70 insertions, 25 deletions
diff --git a/apps/servers/octopus/supergit/src/bin/test.rs b/apps/servers/octopus/supergit/src/bin/test.rs
index 505580a860ce..5398d7539490 100644
--- a/apps/servers/octopus/supergit/src/bin/test.rs
+++ b/apps/servers/octopus/supergit/src/bin/test.rs
@@ -25,12 +25,19 @@ fn main() {
// Iterate over all branch iterators we get
while let Some(biter) = rx.recv().ok() {
use BranchCommit::*;
+ println!(
+ "{}: {}",
+ biter.current().id_str(),
+ biter.current().summary()
+ );
+
biter.for_each(|bc| match bc {
Commit(c) => println!("{}: {}", c.id_str(), c.summary()),
- Merge(c, b) => {
- // println!("[MERGE] {}: {}", c.id_str(), c.summary());
+ Merge(Some(c), b) => {
+ println!("{}: {}", c.id_str(), c.summary());
// tx.send(b.get_all()).unwrap();
}
+ Merge(_, b) => {} //tx.send(b.get_all()).unwrap(),
_ => todo!(),
});
}
diff --git a/apps/servers/octopus/supergit/src/branch.rs b/apps/servers/octopus/supergit/src/branch.rs
index e0f779b8a8f0..1dd3fffab370 100644
--- a/apps/servers/octopus/supergit/src/branch.rs
+++ b/apps/servers/octopus/supergit/src/branch.rs
@@ -1,6 +1,6 @@
use crate::{Commit, HashId};
use git2::Repository;
-use std::sync::Arc;
+use std::{mem, sync::Arc};
/// Abstraction for a branch history slice
///
@@ -100,6 +100,10 @@ impl BranchIter {
}
}
+ pub fn current(&self) -> Commit {
+ Commit::new(&self.repo, self.last.clone()).unwrap()
+ }
+
/// Get a commit object, if it exists
fn find_commit(&self, id: &HashId) -> Option<Commit> {
Commit::new(&self.repo, id.clone())
@@ -113,23 +117,59 @@ impl BranchIter {
}
/// Get the parent, set the last, and return BranchCommit (maybe)
- fn get_parent(&self, last: Option<Commit>) -> Option<(BranchCommit, IterCmd)> {
+ fn get_parent(&self, last: Option<Commit>, cmd: IterCmd) -> Option<(BranchCommit, IterCmd)> {
+ if let Some(id) = cmd.take() {
+ let commit = Commit::new(&self.repo, id).unwrap();
+
+ return match commit.parent_count() {
+ // Special case: if the previous commit was a merge,
+ // but this was the first commit in the history, we
+ // need to return it here or else it will be forgotten
+ // about!
+ 0 | 1 => Some((BranchCommit::Commit(commit), IterCmd::Step)),
+ 2 => {
+ let p1 = commit.first_parent().unwrap();
+ let p2 = commit.parent(1).unwrap();
+
+ Some((
+ BranchCommit::Merge(
+ // Here we return a commit via the merge
+ // field, because otherwise it will be
+ // dropped! Because we just skipped
+ // because of a merge (because merges are
+ // normal commit parents).
+ Some(commit.clone()),
+ Branch::without_name(&self.repo, p2.id),
+ ),
+ IterCmd::Skip(p1.id),
+ ))
+ }
+ _ => todo!(),
+ };
+ }
+
+ // This code is only entered when we are checking for the parents
last.and_then(|c| match c.parent_count() {
// No parent means we've reached the end of the branch
0 => None,
// One parent is a normal commit
- 1 => Some((
- BranchCommit::Commit(c.first_parent().unwrap()),
- IterCmd::Step,
- )),
+ 1 => {
+ let parent = c.first_parent().unwrap();
+ Some((BranchCommit::Commit(parent), IterCmd::Step))
+ }
// Two parents is a normal merge commit
- 2 => Some((
- BranchCommit::Merge(
- c.clone(),
- Branch::without_name(&self.repo, c.parent(1).unwrap().id),
- ),
- IterCmd::Skip(c.parent(0).unwrap().id),
- )),
+ 2 => {
+ let p1 = c.first_parent().unwrap();
+ let p2 = c.parent(1).unwrap();
+
+ Some((
+ // Set the Merge commit field to None because it's
+ // used to communicate special states (like a
+ // merge after another merge).
+ BranchCommit::Merge(None, Branch::without_name(&self.repo, p2.id)),
+ IterCmd::Skip(p1.id),
+ ))
+ }
// More or negative parents means the universe is ending
_ => panic!("Octopus merges are not implemented yet!"),
})
@@ -140,20 +180,17 @@ impl Iterator for BranchIter {
type Item = BranchCommit;
fn next(&mut self) -> Option<Self::Item> {
- let cid = std::mem::replace(&mut self.cmd, IterCmd::Step)
- .take()
- .unwrap_or_else(|| self.last.clone());
+ let cmd = mem::replace(&mut self.cmd, IterCmd::Step);
+ let last = self.find_commit(&self.last);
- let last = self.find_commit(&cid);
-
match self.limit {
// Get commits forever
- SegLimit::None => self.get_parent(last).map(|bc| self.set_last(bc)),
+ SegLimit::None => self.get_parent(last, cmd).map(|bc| self.set_last(bc)),
// Get commits until hitting a certain ID
SegLimit::Commit(ended, _) if ended => None,
SegLimit::Commit(_, ref c) => {
let c = c.clone();
- self.get_parent(last)
+ self.get_parent(last, cmd)
.map(|(bc, cmd)| {
// Set iterator to "done" if we have reached the commit
if bc.id() == c {
@@ -173,13 +210,14 @@ impl Iterator for BranchIter {
}
*curr += 1;
- self.get_parent(last).map(|bc| self.set_last(bc))
+ self.get_parent(last, cmd).map(|bc| self.set_last(bc))
}
}
}
}
/// Specify how to trace actions on the iterator
+#[derive(Debug)]
enum IterCmd {
/// Set the last commit to an ID
Step,
@@ -215,7 +253,7 @@ pub enum BranchCommit {
/// A single commit
Commit(Commit),
/// A merge commit from one other branch
- Merge(Commit, Branch),
+ Merge(Option<Commit>, Branch),
/// An octopus merge with multiple branches
Octopus(Commit, Vec<Branch>),
}
@@ -225,7 +263,7 @@ impl BranchCommit {
use BranchCommit::*;
match self {
Commit(ref c) => &c.id,
- Merge(ref c, _) => &c.id,
+ Merge(_, ref b) => &b.head,
Octopus(ref c, _) => &c.id,
}
.clone()