Skip to content

apply --interactive passes a missing destination path to external diff for added files #5079

@escape0707

Description

@escape0707

Describe the bug

When diff.command is configured, pressing d at an apply --interactive prompt for an added file passes the missing destination path to the external diff command.

This differs from chezmoi diff, which passes /dev/null for the missing side. External diff tools such as difftastic fail with No such file when given the missing destination path.

To reproduce

With difftastic installed as difft, run this in a temporary directory:

$ tmp=$(mktemp -d)
$ mkdir -p "$tmp/home/.config/chezmoi" "$tmp/source"
$ printf '[diff]\ncommand = "difft"\n' > "$tmp/home/.config/chezmoi/chezmoi.toml"
$ printf 'hello\n' > "$tmp/source/dot_file"
$ HOME="$tmp/home" chezmoi -S "$tmp/source" -D "$tmp/home" \
    apply --interactive --no-tty "$tmp/home/.file" <<<'d'

Observed output:

Apply .file (diff/yes/no/all/quit)? No such file: /tmp/tmp.QpIuQgK7pW/home/.file
chezmoi: .file: exit status 2

For comparison, chezmoi diff on the same target succeeds:

$ HOME="$tmp/home" chezmoi -S "$tmp/source" -D "$tmp/home" diff "$tmp/home/.file"
/tmp/chezmoi-diff2066257932/.file --- Text
1 hello
2

Expected behavior

chezmoi prints diff normally and not exit.

Output of command with the --verbose flag

$ HOME="$tmp/home" chezmoi -S "$tmp/source" -D "$tmp/home" \
    --verbose apply --interactive --no-tty "$tmp/home/.file" <<<'d'
Apply .file (diff/yes/no/all/quit)? No such file: /tmp/tmp.QpIuQgK7pW/home/.file
chezmoi: .file: exit status 2

Output of chezmoi doctor

Details
$ chezmoi doctor
RESULT    CHECK                       MESSAGE
warning   version                     v2.70.4, built at 2026-05-20T07:44:27Z
ok        latest-version              v2.70.4
ok        os-arch                     linux/amd64 (Arch Linux)
ok        go-version                  go1.26.3-X:nodwarf5 (gc)
ok        executable                  /usr/bin/chezmoi
ok        config-file                 found ~/.config/chezmoi/chezmoi.toml
ok        git-command                 found git
ok        shell-command               found bash

Additional context

This kinda break my difft mentality.. So I'm using a wrapper as workaround for now:

> cat private_dot_local/bin/executable_chezmoi-difft 
#!/usr/bin/env bash
set -euo pipefail

args=("$@")

if ((${#args[@]} >= 2)); then
    old_path_index=$((${#args[@]} - 2))
    new_path_index=$((${#args[@]} - 1))

    for path_index in "$old_path_index" "$new_path_index"; do
        path=${args[$path_index]}
        if [[ $path != /dev/null && ! -e $path ]]; then
            args[$path_index]=/dev/null
        fi
    done
fi

exec difft "${args[@]}"

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions