Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions lib/stage.sh
Original file line number Diff line number Diff line change
@@ -1,15 +1,34 @@
#!/usr/bin/env bash
# stage.sh — Git index mutations for readable note staging

# Restore one tracked path to the exact index entry present before staging.
restore_tracked_note_index() {
local repo="$1" readable_path="$2" mode="$3" blob="$4"
if ! git -C "$repo" update-index --cacheinfo \
"$mode" "$blob" "$readable_path"; then
echo "Error: failed to restore index entry after staging refusal: $readable_path" >&2
return 1
fi
}

# Stage one readable note. Existing tracked plaintext paths are renormalized so
# a newly-added clean filter replaces their unchanged plaintext index blobs.
# Restore the prior entry if post-staging ciphertext verification refuses it.
stage_readable_note() {
local repo="$1" notes_dir="$2" relpath="$3"
local readable_path="$notes_dir/$relpath"
local tracked_readable=false
local original_entry original_metadata original_mode original_blob original_stage

if git -C "$repo" ls-files --error-unmatch -- "$readable_path" >/dev/null 2>&1; then
tracked_readable=true
original_entry=$(git -C "$repo" ls-files --stage -- "$readable_path") || return 1
original_metadata="${original_entry%%$'\t'*}"
read -r original_mode original_blob original_stage <<< "$original_metadata"
if [ "$original_stage" != "0" ] || [ -z "$original_blob" ]; then
echo "Error: tracked readable note has no ordinary index entry: $relpath" >&2
return 1
fi
git -C "$repo" add -f --renormalize -- "$readable_path"
else
git -C "$repo" add -f -- "$readable_path"
Expand All @@ -18,6 +37,10 @@ stage_readable_note() {
local index_blob head_blob="" blob_state blob_status=0
if ! index_blob=$(git -C "$repo" rev-parse ":$readable_path" 2>/dev/null); then
echo "Error: staging did not create an index entry for: $relpath" >&2
if $tracked_readable; then
restore_tracked_note_index \
"$repo" "$readable_path" "$original_mode" "$original_blob" || return 1
fi
return 1
fi
if git -C "$repo" cat-file -e "HEAD:$readable_path" 2>/dev/null; then
Expand All @@ -26,6 +49,8 @@ stage_readable_note() {
if $tracked_readable && [ -n "$head_blob" ] && [ "$index_blob" = "$head_blob" ]; then
echo "Error: staging produced no index change for tracked readable note: $relpath" >&2
echo "Ensure setup configured an active encryption filter, then retry." >&2
restore_tracked_note_index \
"$repo" "$readable_path" "$original_mode" "$original_blob" || return 1
return 1
fi

Expand All @@ -34,11 +59,15 @@ stage_readable_note() {
|| blob_status=$?
if [ "$blob_status" -ne 0 ]; then
echo "Error: failed to inspect staged note blob: $relpath" >&2
restore_tracked_note_index \
"$repo" "$readable_path" "$original_mode" "$original_blob" || return 1
return "$blob_status"
fi
if [ "$blob_state" != "encrypted" ]; then
echo "Error: staged tracked readable note is not git-crypt encrypted: $relpath" >&2
echo "Ensure setup configured an active encryption filter, then retry." >&2
restore_tracked_note_index \
"$repo" "$readable_path" "$original_mode" "$original_blob" || return 1
return 1
fi
fi
Expand Down
11 changes: 8 additions & 3 deletions test/stage.bats
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,13 @@ setup() {
[[ "$output" == *"legacy.md"* ]]
}

@test "notes stage rejects a changed tracked blob that remains plaintext" {
@test "notes stage restores a changed tracked blob that remains plaintext" {
echo "# Legacy plaintext" > "$NOTES_CALLER_PWD/notes/legacy.md"
git -C "$NOTES_CALLER_PWD" add -f notes/legacy.md
git -C "$NOTES_CALLER_PWD" commit -q --no-verify -m "legacy plaintext note"

local original_blob
original_blob=$(git -C "$NOTES_CALLER_PWD" rev-parse :notes/legacy.md)
local clean_filter="$BATS_TEST_TMPDIR/plaintext-clean-filter"
cat > "$clean_filter" <<'SH'
#!/usr/bin/env bash
Expand All @@ -64,9 +66,12 @@ SH
[ "$status" -ne 0 ]
[[ "$output" == *"not git-crypt encrypted"* ]]
[[ "$output" == *"legacy.md"* ]]
run git -C "$NOTES_CALLER_PWD" rev-parse :notes/legacy.md
[ "$output" = "$original_blob" ]
run git -C "$NOTES_CALLER_PWD" diff --cached --name-only -- notes/legacy.md
[ -z "$output" ]
run git -C "$NOTES_CALLER_PWD" show :notes/legacy.md
[[ "$output" == FILTERED:* ]]
[[ "$output" != *"GITCRYPT"* ]]
[ "$output" = "# Legacy plaintext" ]
}

@test "notes stage: no args requires explicit scope" {
Expand Down
Loading