Skip to main content

lit/commands/
show.rs

1use crate::core::{find_repo_root, Object, ObjectHash};
2use crate::response::{ShowResponse, TreeEntryInfo};
3use crate::storage::ObjectStore;
4
5/// Resolve what the user typed to an object hash.
6///
7/// Accepts `HEAD`, a branch name, a tag name, or a literal hash. An earlier
8/// version gated the literal-hash case on `object.len() == 64`, but a Lit hash
9/// is 192 characters (SHA3-512 followed by BLAKE3), so that arm never ran and
10/// hashes only worked by falling through the ref lookups unchanged. `HEAD` had
11/// no case at all and resolved to itself, so `lit show HEAD` always reported
12/// the object as missing.
13fn resolve_object(
14    repo_root: &std::path::Path,
15    object: String,
16) -> Result<ObjectHash, crate::errors::LitError> {
17    use crate::core::{read_head, read_ref};
18
19    if object == "HEAD" {
20        // read_head gives the current branch, or the hash itself when detached.
21        let head = read_head(repo_root)?;
22        return Ok(match read_ref(repo_root, &format!("heads/{}", head)) {
23            Ok(hash) => ObjectHash::from_hex(hash),
24            Err(_) => ObjectHash::from_hex(head),
25        });
26    }
27
28    // A name that matches no ref is taken to be a hash already.
29    let resolved = read_ref(repo_root, &format!("heads/{}", object))
30        .or_else(|_| read_ref(repo_root, &format!("tags/{}", object)))
31        .unwrap_or(object);
32    Ok(ObjectHash::from_hex(resolved))
33}
34
35pub fn execute(object: String) -> Result<ShowResponse, crate::errors::LitError> {
36    let repo_root = find_repo_root()?;
37    let store = ObjectStore::new(&repo_root);
38
39    let hash = resolve_object(&repo_root, object)?;
40
41    let obj = store.read(&hash)?;
42
43    match obj {
44        Object::Commit(commit) => Ok(ShowResponse::Commit {
45            hash: hash.to_string(),
46            author: commit.author,
47            timestamp: commit.timestamp,
48            message: commit.message,
49        }),
50        Object::Tree(tree) => {
51            let entries = tree
52                .entries
53                .into_iter()
54                .map(|e| TreeEntryInfo {
55                    mode: e.mode,
56                    object_type: e.object_type,
57                    hash: e.hash.to_string(),
58                    name: e.name,
59                })
60                .collect();
61            Ok(ShowResponse::Tree {
62                hash: hash.to_string(),
63                entries,
64            })
65        }
66        Object::Blob(blob) => {
67            let content = std::str::from_utf8(&blob.content)
68                .map(|s| s.to_string())
69                .ok();
70            let is_binary = content.is_none();
71            Ok(ShowResponse::Blob {
72                hash: hash.to_string(),
73                size: blob.content.len(),
74                content,
75                is_binary,
76            })
77        }
78        Object::Tag(tag) => Ok(ShowResponse::Commit {
79            hash: hash.to_string(),
80            author: tag.tagger.clone(),
81            timestamp: tag.timestamp,
82            message: format!(
83                "tag {}\nTarget: {}\n\n{}",
84                tag.tag_name,
85                tag.target.as_str(),
86                tag.message
87            ),
88        }),
89    }
90}