Skip to main content

git_policy_cli/
service_instance.rs

1//! What:
2//!  Which commit service instance belongs to a worktree: a name made from the worktree's own Git directory, and the
3//!  socket, the unit instances and the journal directory that name gives.
4//! Why:
5//!  One service instance runs per worktree, the way Bazel runs one server per workspace, so a wait in one worktree (a foreign
6//!  holder of its index lock, a slow push) never delays another's commits. A worktree's own Git directory (the main
7//!  worktree's `.git`, or `.git/worktrees/<id>` of a linked one) holds its `HEAD`, its index and its work records, and Git
8//!  allows one branch to be checked out in one worktree only, so the instances never write the same branch
9//!  (`doc/planning/cli-git-resident-service.md`, "One service per worktree"). The name is a hash because an escaped path
10//!  does not fit a unit name for an ordinary home directory, and the hash is the same in the wrapper and in the service,
11//!  which are built together. A collision of two worktrees would only make one instance serve both, never land anywhere
12//!  wrong.
13//!
14//! In TS you'd write (pseudocode):
15//! ```ts
16//! const socket = join(runtimeDir, 'cli-git', `${instanceOf(gitDir)}.sock`);
17//! ```
18
19use std::path::{Path, PathBuf};
20
21/// Directory of the sockets under the user's runtime directory.
22pub const SOCKET_DIRECTORY: &str = "cli-git";
23
24/// The socket of the one service every worktree of an account shared until the release that gave each worktree its own. A
25///  service still listening here is asked to retire and its units are removed.
26pub const LEGACY_SOCKET_RELATIVE: &str = "cli-git/service.sock";
27
28/// The number of hexadecimal digits of an instance name.
29pub const INSTANCE_LENGTH: usize = 32;
30
31/// The 128-bit FNV-1a offset basis.
32const FNV_OFFSET: u128 = 0x6c62_272e_07bb_0142_62b8_2175_6295_c58d;
33
34/// The 128-bit FNV-1a prime.
35const FNV_PRIME: u128 = 0x0000_0000_0100_0000_0000_0000_0000_013b;
36
37/// What:
38///  The instance name of a worktree: 32 hexadecimal digits of the FNV-1a hash of its real Git directory path.
39/// Why:
40///   The real path makes two spellings of one directory (a link, a relative part) one instance. A directory that cannot be
41///  resolved is hashed as given.
42///
43/// In TS you'd write (pseudocode):
44/// ```ts
45/// instanceOf(gitDir) // '3f9a...' (32 digits)
46/// ```
47pub fn instance_of(git_dir: &Path) -> String {
48    use std::os::unix::ffi::OsStrExt;
49    let real: PathBuf = std::fs::canonicalize(git_dir).unwrap_or_else(|_| { return git_dir.to_path_buf(); });
50    let mut hash: u128 = FNV_OFFSET;
51    for byte in real.as_os_str().as_bytes() {
52        hash ^= u128::from(*byte);
53        hash = hash.wrapping_mul(FNV_PRIME);
54    }
55    return format!("{hash:032x}");
56}
57
58/// The socket of an instance under the user's runtime directory.
59pub fn socket_path(runtime_dir: &Path, instance: &str) -> PathBuf {
60    return runtime_dir.join(SOCKET_DIRECTORY).join(format!("{instance}.sock"));
61}
62
63/// The socket of the worktree whose own Git directory is `git_dir`.
64pub fn socket_for(runtime_dir: &Path, git_dir: &Path) -> PathBuf {
65    return socket_path(runtime_dir, instance_of(git_dir).as_str());
66}
67
68/// The name of the socket unit instance of a worktree.
69pub fn socket_unit_instance(instance: &str) -> String {
70    return format!("cli-git@{instance}.socket");
71}
72
73/// The name of the service unit instance of a worktree.
74pub fn service_unit_instance(instance: &str) -> String {
75    return format!("cli-git@{instance}.service");
76}
77
78/// Instance controls stay out of the release executable.
79#[cfg(test)]
80#[path = "service_instance_tests.rs"]
81mod tests;