git.rs

  1pub mod blame;
  2pub mod commit;
  3mod hosting_provider;
  4mod remote;
  5pub mod repository;
  6pub mod status;
  7
  8pub use crate::hosting_provider::*;
  9pub use crate::remote::*;
 10use anyhow::{Context as _, Result};
 11pub use git2 as libgit;
 12use gpui::{Action, actions};
 13pub use repository::WORK_DIRECTORY_REPO_PATH;
 14use schemars::JsonSchema;
 15use serde::{Deserialize, Serialize};
 16use std::ffi::OsStr;
 17use std::fmt;
 18use std::str::FromStr;
 19use std::sync::LazyLock;
 20
 21pub static DOT_GIT: LazyLock<&'static OsStr> = LazyLock::new(|| OsStr::new(".git"));
 22pub static GITIGNORE: LazyLock<&'static OsStr> = LazyLock::new(|| OsStr::new(".gitignore"));
 23pub static FSMONITOR_DAEMON: LazyLock<&'static OsStr> =
 24    LazyLock::new(|| OsStr::new("fsmonitor--daemon"));
 25pub static LFS_DIR: LazyLock<&'static OsStr> = LazyLock::new(|| OsStr::new("lfs"));
 26pub static COMMIT_MESSAGE: LazyLock<&'static OsStr> =
 27    LazyLock::new(|| OsStr::new("COMMIT_EDITMSG"));
 28pub static INDEX_LOCK: LazyLock<&'static OsStr> = LazyLock::new(|| OsStr::new("index.lock"));
 29
 30actions!(
 31    git,
 32    [
 33        // per-hunk
 34        ToggleStaged,
 35        StageAndNext,
 36        UnstageAndNext,
 37        #[action(deprecated_aliases = ["editor::RevertSelectedHunks"])]
 38        Restore,
 39        // per-file
 40        #[action(deprecated_aliases = ["editor::ToggleGitBlame"])]
 41        Blame,
 42        StageFile,
 43        UnstageFile,
 44        // repo-wide
 45        StageAll,
 46        UnstageAll,
 47        RestoreTrackedFiles,
 48        TrashUntrackedFiles,
 49        Uncommit,
 50        Push,
 51        PushTo,
 52        ForcePush,
 53        Pull,
 54        Fetch,
 55        FetchFrom,
 56        Commit,
 57        Amend,
 58        Cancel,
 59        ExpandCommitEditor,
 60        GenerateCommitMessage,
 61        Init,
 62        OpenModifiedFiles,
 63    ]
 64);
 65
 66#[derive(Clone, Debug, Default, PartialEq, Deserialize, JsonSchema, Action)]
 67#[action(namespace = git, deprecated_aliases = ["editor::RevertFile"])]
 68#[serde(deny_unknown_fields)]
 69pub struct RestoreFile {
 70    #[serde(default)]
 71    pub skip_prompt: bool,
 72}
 73
 74/// The length of a Git short SHA.
 75pub const SHORT_SHA_LENGTH: usize = 7;
 76
 77#[derive(Clone, Copy, Eq, Hash, PartialEq)]
 78pub struct Oid(libgit::Oid);
 79
 80impl Oid {
 81    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
 82        let oid = libgit::Oid::from_bytes(bytes).context("failed to parse bytes into git oid")?;
 83        Ok(Self(oid))
 84    }
 85
 86    pub fn as_bytes(&self) -> &[u8] {
 87        self.0.as_bytes()
 88    }
 89
 90    pub(crate) fn is_zero(&self) -> bool {
 91        self.0.is_zero()
 92    }
 93
 94    /// Returns this [`Oid`] as a short SHA.
 95    pub fn display_short(&self) -> String {
 96        self.to_string().chars().take(SHORT_SHA_LENGTH).collect()
 97    }
 98}
 99
100impl FromStr for Oid {
101    type Err = anyhow::Error;
102
103    fn from_str(s: &str) -> std::prelude::v1::Result<Self, Self::Err> {
104        libgit::Oid::from_str(s)
105            .context("parsing git oid")
106            .map(Self)
107    }
108}
109
110impl fmt::Debug for Oid {
111    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112        fmt::Display::fmt(self, f)
113    }
114}
115
116impl fmt::Display for Oid {
117    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118        self.0.fmt(f)
119    }
120}
121
122impl Serialize for Oid {
123    fn serialize<S>(&self, serializer: S) -> std::prelude::v1::Result<S::Ok, S::Error>
124    where
125        S: serde::Serializer,
126    {
127        serializer.serialize_str(&self.0.to_string())
128    }
129}
130
131impl<'de> Deserialize<'de> for Oid {
132    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
133    where
134        D: serde::Deserializer<'de>,
135    {
136        let s = String::deserialize(deserializer)?;
137        s.parse::<Oid>().map_err(serde::de::Error::custom)
138    }
139}
140
141impl Default for Oid {
142    fn default() -> Self {
143        Self(libgit::Oid::zero())
144    }
145}
146
147impl From<Oid> for u32 {
148    fn from(oid: Oid) -> Self {
149        let bytes = oid.0.as_bytes();
150        debug_assert!(bytes.len() > 4);
151
152        let mut u32_bytes: [u8; 4] = [0; 4];
153        u32_bytes.copy_from_slice(&bytes[..4]);
154
155        u32::from_ne_bytes(u32_bytes)
156    }
157}
158
159impl From<Oid> for usize {
160    fn from(oid: Oid) -> Self {
161        let bytes = oid.0.as_bytes();
162        debug_assert!(bytes.len() > 8);
163
164        let mut u64_bytes: [u8; 8] = [0; 8];
165        u64_bytes.copy_from_slice(&bytes[..8]);
166
167        u64::from_ne_bytes(u64_bytes) as usize
168    }
169}