1use std::path::Path;
16
17pub const LOG_COMMAND: &str = "journalctl --user --unit 'cli-git@*.service'";
19
20pub const LINGER_COMMAND: &str = "loginctl enable-linger";
22
23pub const RESTART_COMMAND: &str = "systemctl --user restart 'cli-git@*.service'";
25
26pub const CHECK_COMMANDS: [&str; 2] = ["git log -1", "git status"];
28
29pub const STARTING_NOTICE: &str = "Starting the commit service...";
31
32pub const UPDATING_NOTICE: &str = "Updating the commit service...";
34
35#[derive(Clone, Copy, Debug, Eq, PartialEq)]
45pub enum Platform {
46 Windows,
48 Bsd,
50 LinuxWithoutUserServices,
52 Other,
54}
55
56#[derive(Clone, Debug, Eq, PartialEq)]
66pub enum ClientFailure {
67 DidNotStart,
69 NoActivation(Platform),
71 OtherAccount,
73 MayNotHaveLanded,
75 NoCommonVersion,
77 OtherRelease,
79 RepositoryFull(String),
81 RepositoryReadOnly(String),
83 BranchKeepsChanging { branch: String, times: usize },
85 BranchMoved { branch: String },
87 BranchMovedMessageSaved { branch: String, file: String, amend: bool },
89 BranchSwitched { branch: String, now: String, file: Option<String> },
92 WaitedTooLong { minutes: u64 },
94 Refused,
96 IndexInUse(String),
98 Unavailable,
100 ProgramMissing(String),
102 CouldNotStart(String),
104 UnitInTheWay(String),
106 WorkNotHandedOver(String),
108 ContentPruned,
110}
111
112pub const NOTHING_CHANGED: &str = "Nothing in the repository changed.";
114
115pub fn render(failure: &ClientFailure) -> String {
125 return match failure {
126 ClientFailure::DidNotStart => format!(
127 "The commit service did not start, so this commit was not made. Its log shows why: `{LOG_COMMAND}`. \
128 Run the commit again after fixing that. {NOTHING_CHANGED}"
129 ),
130 ClientFailure::NoActivation(platform) => format!(
131 "This computer cannot start the commit service on demand, so this commit was not made. {} {NOTHING_CHANGED}",
132 platform_remedy(*platform)
133 ),
134 ClientFailure::OtherAccount => format!(
135 "Another account runs the commit service here, so this commit was not made. Use your own account, or ask \
136 the other account to stop its service, then run the commit again. {NOTHING_CHANGED}"
137 ),
138 ClientFailure::MayNotHaveLanded => format!(
139 "The commit service stopped before it confirmed this commit. Run `{}` to see whether it was made, and `{}` \
140 to see what is still staged. If it was not made, run the commit again.",
141 CHECK_COMMANDS[0], CHECK_COMMANDS[1]
142 ),
143 ClientFailure::NoCommonVersion => format!(
144 "This git is older than the installed commit service and cannot work with it, so this commit was not made. \
145 Update the git wrapper with the tool that installed it, and run the commit again. {NOTHING_CHANGED}"
146 ),
147 ClientFailure::OtherRelease => format!(
148 "The commit service that is running was started from another release of this git wrapper and did not stop when \
149 asked, so this commit was not made. Restart it with `{RESTART_COMMAND}`, and run the commit again. {NOTHING_CHANGED}"
150 ),
151 ClientFailure::RepositoryFull(path) => format!(
152 "There is no space left for {path}, so this commit was not made. Free some space and run the commit again. \
153 {NOTHING_CHANGED}"
154 ),
155 ClientFailure::RepositoryReadOnly(path) => format!(
156 "{path} cannot be written to, so this commit was not made. Make the repository writable (check its \
157 permissions and whether the disk is mounted read-only) and run the commit again. {NOTHING_CHANGED}"
158 ),
159 ClientFailure::BranchKeepsChanging { branch, times } => format!(
160 "The branch {branch} changed {times} times while this commit was being prepared, so it was not made. Run \
161 the commit again; if other people commit to the same branch at the same moment, it will succeed once they \
162 pause. {NOTHING_CHANGED}"
163 ),
164 ClientFailure::BranchMoved { branch } => format!(
165 "The branch {branch} moved while this commit was being prepared, and a commit that replaces the last one, or one \
166 that was written or signed by hand, cannot be moved onto a different last commit, so it was not made. Run the \
167 commit again to make it on the branch as it is now. {NOTHING_CHANGED}"
168 ),
169 ClientFailure::BranchMovedMessageSaved { branch, file, amend } => format!(
170 "The branch {branch} moved while this commit was being prepared, so it was not made. The message you wrote is saved \
171 in {file}. Run `git commit {}-F {file}` to make the commit on the branch as it is now. {NOTHING_CHANGED}",
172 if *amend { "--amend " } else { "" }
173 ),
174 ClientFailure::BranchSwitched { branch, now, file } => format!(
175 "The worktree switched from {branch} to {now} while this commit was being prepared, so it was not made. {} Switch back \
176 to {branch} and run the commit again, or run it here to commit on {now}. {NOTHING_CHANGED}",
177 match file {
178 Some(saved) => format!("The message you wrote is saved in {saved}; `git commit -F {saved}` uses it."),
179 None => String::new(),
180 }
181 ),
182 ClientFailure::WaitedTooLong { minutes } => format!(
183 "This commit waited {minutes} minutes behind other commits and was not made. Run it again later, or raise \
184 the waiting limit in the settings for the wrapper. {NOTHING_CHANGED}"
185 ),
186 ClientFailure::Refused => format!(
187 "The commit service refused this commit because it names a path or a value that cannot be committed, so it \
188 was not made. Check the paths you passed to the commit. {NOTHING_CHANGED}"
189 ),
190 ClientFailure::IndexInUse(holder) => format!(
191 "{holder} is using this repository's index, so this commit was not made. Wait for it to finish, or close \
192 it, and run the commit again. {NOTHING_CHANGED}"
193 ),
194 ClientFailure::ProgramMissing(path) => format!(
195 "The commit service program was not found at {path}, so this commit was not made. Reinstall the git wrapper \
196 with the tool that installed it, so that the program sits next to it, then run the commit again. \
197 {NOTHING_CHANGED}"
198 ),
199 ClientFailure::UnitInTheWay(path) => format!(
200 "The file `{path}` has the name of a settings file of the commit service but was not written by the git wrapper, \
201 so the commit service could not be set up and this commit was not made. Move or delete that file, then run the \
202 commit again. {NOTHING_CHANGED}"
203 ),
204 ClientFailure::WorkNotHandedOver(reason) => format!(
205 "Your commit is saved. Its backup push and upkeep could not be handed to the commit service: {reason}. \
206 Run `git push` when ready."
207 ),
208 ClientFailure::ContentPruned => format!(
209 "The files of this commit were removed from the repository while the commit was being made, so it was not made. \
210 Git removes content that no branch or index refers to yet when `git gc --prune=now` or `git prune` runs in the \
211 same repository, and Git's documentation says that `--prune=now` risks failures and corruption when another \
212 process writes to the repository at the same moment. Run the commit again once the cleanup has finished, and avoid `--prune=now` while commits are \
213 being made (the default `git gc` keeps new content for two weeks). If nothing like that was running, run \
214 `git fsck` to check the repository. {NOTHING_CHANGED}"
215 ),
216 ClientFailure::CouldNotStart(reason) => format!(
217 "The commit service could not be set up, so this commit was not made: {reason}. Fix that and run the commit \
218 again. {NOTHING_CHANGED}"
219 ),
220 ClientFailure::Unavailable => format!(
221 "The commit service could not finish this commit, so it was not made. Run the commit again; if it fails \
222 again, look at the service's log with `{LOG_COMMAND}`. {NOTHING_CHANGED}"
223 ),
224 };
225}
226
227pub fn platform_remedy(platform: Platform) -> String {
237 return match platform {
238 Platform::Windows => String::from(
239 "Committing through this wrapper is not supported on Windows yet. Use the Linux or macOS machine for commits \
240 to this repository.",
241 ),
242 Platform::Bsd => String::from(
243 "Committing through this wrapper is not supported on this system yet. Use a Linux or macOS machine for \
244 commits to this repository.",
245 ),
246 Platform::LinuxWithoutUserServices => format!(
247 "This account has no user service manager. Turn it on with `{LINGER_COMMAND}` (the system must run \
248 systemd), or commit from a system that does."
249 ),
250 Platform::Other => String::from(
251 "Committing through this wrapper is not supported on this system. Use a Linux or macOS machine for \
252 commits to this repository.",
253 ),
254 };
255}
256
257pub fn position_notice(number: usize, of: usize) -> String {
267 let ahead: usize = number.saturating_sub(1);
268 let _ = of;
269 return match ahead {
270 0 => String::from("This commit is next."),
271 1 => String::from("Waiting for 1 earlier commit."),
272 many => format!("Waiting for {many} earlier commits."),
273 };
274}
275
276pub const DENYLIST: [&str; 22] = [
279 "protocol", "journal", "request id", "daemon", "socket", "errno", "epipe", "econnrefused", "eagain", "flock",
280 "lock file", "peer", "frame", "handshake", "batch", "oid", "fast-import", "unix domain", "abstract", "pid",
281 "uid", "os error",
282];
283
284pub fn path_text(path: &Path) -> String {
286 return path.display().to_string();
287}
288
289#[cfg(test)]
291#[path = "service_messages_tests.rs"]
292mod tests;