1use std::path::{Path, PathBuf};
2
3use eyre::Context;
4use tracing::instrument;
5
6use crate::core::eventlog::EventTransactionId;
7
8use super::{FileMode, GitRunInfo, GitRunOpts, GitRunResult, MaybeZeroOid, NonZeroOid, Repo, Tree};
9
10#[derive(Copy, Clone, Debug)]
12pub enum Stage {
13 Stage0,
15
16 Stage1,
18
19 Stage2,
21
22 Stage3,
24}
25
26impl Stage {
27 pub(super) fn get_trailer(&self) -> &'static str {
28 match self {
29 Stage::Stage0 => "Branchless-stage-0",
30 Stage::Stage1 => "Branchless-stage-1",
31 Stage::Stage2 => "Branchless-stage-2",
32 Stage::Stage3 => "Branchless-stage-3",
33 }
34 }
35}
36
37impl From<Stage> for i32 {
38 fn from(stage: Stage) -> Self {
39 match stage {
40 Stage::Stage0 => 0,
41 Stage::Stage1 => 1,
42 Stage::Stage2 => 2,
43 Stage::Stage3 => 3,
44 }
45 }
46}
47
48#[derive(Clone, Debug, PartialEq, Eq, Hash)]
50pub struct IndexEntry {
51 pub(super) oid: MaybeZeroOid,
52 pub(super) file_mode: FileMode,
53}
54
55pub struct Index {
57 pub(super) inner: git2::Index,
58}
59
60impl std::fmt::Debug for Index {
61 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62 write!(f, "<Index>")
63 }
64}
65
66impl Index {
67 pub fn has_conflicts(&self) -> bool {
69 self.inner.has_conflicts()
70 }
71
72 pub fn get_entry(&self, path: &Path) -> Option<IndexEntry> {
74 self.get_entry_in_stage(path, Stage::Stage0)
75 }
76
77 pub fn get_entry_in_stage(&self, path: &Path, stage: Stage) -> Option<IndexEntry> {
79 self.inner
80 .get_path(path, i32::from(stage))
81 .map(|entry| IndexEntry {
82 oid: entry.id.into(),
83 file_mode: {
84 let mode = i32::try_from(entry.mode).unwrap();
87 FileMode::from(mode)
88 },
89 })
90 }
91
92 pub fn update_from_tree(&mut self, tree: &Tree) -> eyre::Result<()> {
94 self.inner.read_tree(&tree.inner)?;
95 self.inner.write().wrap_err("writing index")
96 }
97}
98
99#[allow(missing_docs)]
101#[derive(Clone, Debug)]
102pub enum UpdateIndexCommand {
103 Delete {
104 path: PathBuf,
105 },
106 Update {
107 path: PathBuf,
108 stage: Stage,
109 mode: FileMode,
110 oid: NonZeroOid,
111 },
112}
113
114#[instrument]
119pub fn update_index(
120 git_run_info: &GitRunInfo,
121 repo: &Repo,
122 index: &Index,
123 event_tx_id: EventTransactionId,
124 commands: &[UpdateIndexCommand],
125) -> eyre::Result<()> {
126 let stdin = {
127 let mut buf = Vec::new();
128 for command in commands {
129 use std::io::Write;
130
131 match command {
132 UpdateIndexCommand::Delete { path } => {
133 write!(
134 &mut buf,
135 "0 {zero} 0\t{path}\0",
136 zero = MaybeZeroOid::Zero,
137 path = path.display(),
138 )?;
139 }
140
141 UpdateIndexCommand::Update {
142 path,
143 stage,
144 mode,
145 oid,
146 } => {
147 write!(
148 &mut buf,
149 "{mode} {sha1} {stage}\t{path}\0",
150 sha1 = oid,
151 stage = i32::from(*stage),
152 path = path.display(),
153 )?;
154 }
155 }
156 }
157 buf
158 };
159
160 let GitRunResult { .. } = git_run_info
161 .run_silent(
162 repo,
163 Some(event_tx_id),
164 &["update-index", "-z", "--index-info"],
165 GitRunOpts {
166 treat_git_failure_as_error: true,
167 stdin: Some(stdin),
168 },
169 )
170 .wrap_err("Updating index")?;
171 Ok(())
172}