use super::*;
use crate::repo_read_object::to_hex;
use crate::test_support::{executable, fixture, git, git_output, remove};
#[test]
fn entries_equal_the_listing_in_every_version() {
let root = fixture("repo-read-index");
let repo = root.join("repo");
std::fs::create_dir(&repo).expect("dir");
git(repo.as_path(), &["init", "--quiet", "--initial-branch=main"]);
std::fs::create_dir_all(repo.join("d/e")).expect("dirs");
for name in ["a", "d/b", "d/e/c", "d/e/cc", "z"] {
std::fs::write(repo.join(name), format!("{name}\n")).expect("write");
}
git(repo.as_path(), &["add", "--all"]);
git(repo.as_path(), &["commit", "--quiet", "--message=m"]);
for version in ["2", "4", "3"] {
git(repo.as_path(), &["update-index", "--index-version", version]);
let bytes = std::fs::read(repo.join(".git/index")).expect("index");
let entries = parse_index(bytes.as_slice(), ObjectFormat::Sha1).expect("parsed");
let listing = String::from_utf8(git_output(repo.as_path(), &["ls-files", "--stage"]).stdout).expect("UTF-8");
let mine: Vec<String> = entries.iter().map(|entry| { return format!("{:06o} {} {}\t{}", entry.mode, to_hex(entry.id.as_slice()), entry.stage, String::from_utf8_lossy(entry.path.as_slice())); }).collect();
assert_eq!(mine, listing.lines().map(str::to_string).collect::<Vec<String>>(), "version {version}");
}
std::fs::write(repo.join("later"), "x\n").expect("later");
git(repo.as_path(), &["add", "--intent-to-add", "later"]);
let bytes = std::fs::read(repo.join(".git/index")).expect("index");
let entries = parse_index(bytes.as_slice(), ObjectFormat::Sha1).expect("parsed");
assert!(entries.iter().any(|entry| { return entry.extended & INTENT_TO_ADD != 0; }));
assert_eq!(&bytes[4..8], &[0, 0, 0, 3], "an extended entry makes the file version 3");
remove(root.as_path());
}
#[test]
fn a_reserved_extended_flag_is_declined() {
let root = fixture("repo-read-index-reserved");
let repo = root.join("repo");
std::fs::create_dir(&repo).expect("dir");
git(repo.as_path(), &["init", "--quiet", "--initial-branch=main"]);
std::fs::write(repo.join("only"), "x\n").expect("only");
git(repo.as_path(), &["add", "--intent-to-add", "only"]);
let bytes = std::fs::read(repo.join(".git/index")).expect("index");
let entries = parse_index(bytes.as_slice(), ObjectFormat::Sha1).expect("parsed");
assert_eq!(entries.len(), 1);
let extended_at: usize = 12 + 40 + 20 + 2;
let mut reserved = bytes.clone();
reserved[extended_at + 1] |= 0x01;
assert!(parse_index(reserved.as_slice(), ObjectFormat::Sha1).is_none());
remove(root.as_path());
}
#[test]
fn what_cannot_be_read_is_declined() {
let root = fixture("repo-read-index-declined");
let repo = root.join("repo");
std::fs::create_dir(&repo).expect("dir");
git(repo.as_path(), &["init", "--quiet", "--initial-branch=main"]);
std::fs::write(repo.join("a"), "a\n").expect("a");
git(repo.as_path(), &["add", "a"]);
let plain = std::fs::read(repo.join(".git/index")).expect("index");
assert!(parse_index(plain.as_slice(), ObjectFormat::Sha1).is_some());
assert!(parse_index(plain.as_slice(), ObjectFormat::Sha256).is_none(), "a SHA-1 file read as SHA-256");
assert!(parse_index(&plain[..plain.len() - 1], ObjectFormat::Sha1).is_none(), "a truncated file");
let mut corrupt = plain.clone();
corrupt[3] = b'X';
assert!(parse_index(corrupt.as_slice(), ObjectFormat::Sha1).is_none(), "a bad signature");
git(repo.as_path(), &["update-index", "--split-index"]);
let split = std::fs::read(repo.join(".git/index")).expect("index");
assert!(parse_index(split.as_slice(), ObjectFormat::Sha1).is_none(), "a split index");
remove(root.as_path());
}
#[test]
fn an_index_with_the_monitor_and_untracked_cache_extensions_is_read() {
let root = fixture("repo-read-index-monitor");
let repo = root.join("repo");
std::fs::create_dir(&repo).expect("dir");
git(repo.as_path(), &["init", "--quiet", "--initial-branch=main"]);
for name in ["a", "b"] {
std::fs::write(repo.join(name), format!("{name}\n")).expect("write");
}
git(repo.as_path(), &["add", "--all"]);
git(repo.as_path(), &["commit", "--quiet", "--message=m"]);
let hook = root.join("monitor-hook");
executable(hook.as_path(), b"#!/bin/sh\nprintf 'token\\0'\n");
let monitored = format!("core.fsmonitor={}", hook.display());
git(repo.as_path(), &["-c", monitored.as_str(), "update-index", "--fsmonitor"]);
git(repo.as_path(), &["-c", monitored.as_str(), "-c", "core.untrackedCache=true", "update-index", "--untracked-cache"]);
git(repo.as_path(), &["-c", monitored.as_str(), "-c", "core.untrackedCache=true", "status", "--short"]);
let bytes = std::fs::read(repo.join(".git/index")).expect("index");
for signature in [b"FSMN", b"UNTR"] {
assert!(bytes.windows(4).any(|window| { return window == signature; }), "{} is in the index", String::from_utf8_lossy(signature));
}
let entries = parse_index(bytes.as_slice(), ObjectFormat::Sha1).expect("parsed");
let listing = String::from_utf8(git_output(repo.as_path(), &["ls-files", "--stage"]).stdout).expect("UTF-8");
let mine: Vec<String> = entries.iter().map(|entry| { return format!("{:06o} {} {}\t{}", entry.mode, to_hex(entry.id.as_slice()), entry.stage, String::from_utf8_lossy(entry.path.as_slice())); }).collect();
assert_eq!(mine, listing.lines().map(str::to_string).collect::<Vec<String>>());
remove(root.as_path());
}