ubr/git/local_commit/
untracked_commit.rs1use std::fmt::Debug;
2
3use anyhow::Context;
4use git2::{Commit, Repository};
5
6use crate::git::{local_commit::CommitMetadata, GitRepo};
7
8use super::TrackedCommit;
9
10pub struct UnTrackedCommit<'repo> {
11 repo: &'repo Repository,
12 git_repo: &'repo GitRepo,
13 commit: Commit<'repo>,
14}
15
16impl<'repo> UnTrackedCommit<'repo> {
17 pub fn new(repo: &'repo Repository, git_repo: &'repo GitRepo, commit: Commit<'repo>) -> Self {
18 Self {
19 repo,
20 git_repo,
21 commit,
22 }
23 }
24 pub fn as_commit(&self) -> &Commit {
25 &self.commit
26 }
27
28 pub fn commit(self) -> Commit<'repo> {
29 self.commit
30 }
31
32 pub(crate) fn rebase(self, parent_commit: &Commit<'_>) -> anyhow::Result<Self> {
33 let mut index = self
34 .repo
35 .cherrypick_commit(self.as_commit(), parent_commit, 0, None)?;
36 let new_commit = {
37 let signature = self.as_commit().author();
38 let tree_id = index.write_tree_to(self.repo)?;
39 let tree = self.repo.find_tree(tree_id)?;
40 let new_commit_id = self.repo.commit(
41 None,
42 &signature,
43 &signature,
44 self.commit.message().expect("Not valid UTF-8 message"),
45 &tree,
46 &[parent_commit],
47 )?;
48 self.repo.find_commit(new_commit_id)?
49 };
50 Ok(UnTrackedCommit {
51 repo: self.repo,
52 git_repo: self.git_repo,
53 commit: new_commit,
54 })
55 }
56
57 pub(crate) fn track(self) -> anyhow::Result<TrackedCommit<'repo>> {
58 let commit_msg = self
59 .as_commit()
60 .message()
61 .context("Commit message is not valid UTF-8")?;
62
63 let branch_name = self.generate_remote_branch_name(commit_msg)?;
64 let origin_main_commit = self.git_repo.base_commit()?;
65 let mut complete_index = self
66 .repo
67 .cherrypick_commit(self.as_commit(), &origin_main_commit, 0, None)
68 .context("Cherry picking directly on master")?;
69
70 if complete_index.has_conflicts() {
71 anyhow::bail!("There are conflicts");
72 }
73
74 let tree_id = complete_index.write_tree_to(self.repo)?;
75 let tree = self.repo.find_tree(tree_id)?;
76
77 let remote_commit = {
78 let signature = self.as_commit().author();
79 self.repo.commit(
80 None,
81 &signature,
82 &signature,
83 commit_msg,
84 &tree,
85 &[&origin_main_commit],
86 )?
87 };
88
89 let meta_data = CommitMetadata {
91 remote_branch_name: std::borrow::Cow::Owned(branch_name),
92 remote_commit,
93 };
94 self.git_repo.save_meta_data(self.as_commit(), &meta_data)?;
95 Ok(TrackedCommit::new(
96 self.repo,
97 self.git_repo,
98 self.commit,
99 meta_data,
100 ))
101 }
102
103 fn generate_remote_branch_name(&self, commit_msg: &str) -> anyhow::Result<String> {
104 let branch_name = {
105 let title = commit_msg
106 .lines()
107 .next()
108 .expect("Must have at least one line");
109 title
110 .replace(
111 |c: char| !(c.is_ascii_alphanumeric() || c == '-' || c == '_'),
112 "-",
113 )
114 .to_ascii_lowercase()
115 };
116 Ok(branch_name)
117 }
118}
119
120impl Debug for UnTrackedCommit<'_> {
121 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122 let commit = &self.commit;
123 write!(
124 f,
125 "Untracked Commit: {:?} {:?}",
126 commit.id(),
127 commit.message()
128 )
129 }
130}