I'm working with a project that has a lot of submodules. Like, a shockingly large number of submodules. Each submodule is a miniature project in its own right, with its own version history, version numbers, etc. The "master project" is then used to build all of them and integrate them into an installable Linux image. This works great, except for when we want to look at all of the changes from one version of the project to the next. As one might expect, using something like `git difftool --tool=meld --dir-diff prior-version-tag` doesn't work right. Rather than showing the changes within the submodules, git simply shows the submodules themselves, with contents such as 'Subproject commit idontactuallywanttotypeasha256hashhere'. I don't really want to see that, I want to see the contents of the submodules. According to `man git-difftool`, the `git difftool` command supports all of the same options as `git diff`: git difftool is a frontend to git diff and accepts the same options and arguments. `git diff` supports a `--submodule=diff` argument, which will show me exactly the info I want to see, but in a massive long diff file that is painful to read and easy to miss stuff in. So, I tried running `git difftool --submodule=diff --tool=meld --dir-diff prior-version-tag` and... it did exactly the same thing as what it does without `--submodule=diff`. It didn't crash, it didn't complain that I gave it a bad argument, it just... pretended like the option wasn't there. After experimenting and a bit of research, I don't think git has a way of doing this yet. I found someone else on the mailing list mentioning this same problem, and it was suggested that they use something along the lines of `git submodule foreach git diff`. The problem with that is it assumes that all of the submodules are going to have the exact same version tags as the "master repo", which they don't, by design. Trying this on my repo results in an error `fatal: ambiguous reference 'prior-version-tag': unknown revision or path not in the working tree`. So that won't work. Due to my unwillingness to be subjected to reading massive git diff output, I went ahead and wrote a script that basically emulates the behavior I want out of `git difftool`. This works, but it's non-ideal for a number of reasons (it's hardcoded to use meld, it's written in bash, it's not integrated into git, it does nothing about the fact that the git manpage is incorrect when it comes to difftool's feature set, etc.). If it was just me and the devs I work with using this, I probably wouldn't mind it so much, but the problem is we expect people who use our code to also audit our code, and it's decidedly non-ideal that people would have to install a special script on their system just to be able to run a user-friendly diff against our code for audit purposes. So... are there any particular roadblocks to making this work? Anything I should be aware of aside from standard contribution guidelines before sending a patch? Thanks, Aaron P.S.: The script I wrote for emulating `git difftool --submodule=diff --tool=meld --dir-diff`: #!/bin/bash ## Copyright (C) 2024 - 2024 ENCRYPTED SUPPORT LP <adrelanos@xxxxxxxxxx> ## See the file COPYING for copying conditions. (AGPLv3+ licensed.) set -x set -e set -o pipefail set -o errtrace set -o nounset set -o pipefail cleanup() { rm "${git_diff_file}" rm -rf "${git_difftool_current_dir}" rm -rf "${git_difftool_selected_rev_dir}" } trap cleanup EXIT pushd "$(git rev-parse --show-toplevel)" git_ref="${1:-}" [ -z "${git_ref}" ] && exit 1 git_diff_file="$(mktemp)" git_difftool_current_dir="$(mktemp -d)" git_difftool_selected_rev_dir="$(mktemp -d)" git diff --submodule=diff "${git_ref}" > "${git_diff_file}" || exit 1; git_diff_file_line_seek='n' git_diff_target_file='' while read -r git_diff_line; do if [[ "${git_diff_line}" =~ ^diff\ --git\ a/ ]]; then git_diff_file_line_seek='y' continue elif [ "${git_diff_file_line_seek}" = 'y' ]; then if [[ "${git_diff_line}" =~ ^'+++ b/' ]]; then # trim off the +++ b/ at the start git_diff_target_file="${git_diff_line:6}" git_diff_target_dirname="$(dirname "${git_diff_target_file}")" mkdir -p "${git_difftool_selected_rev_dir}/${git_diff_target_dirname}" mkdir -p "${git_difftool_current_dir}/${git_diff_target_dirname}" if [ -f "${git_diff_target_file}" ]; then cp "${git_diff_target_file}" "${git_difftool_selected_rev_dir}/${git_diff_target_file}" cp "${git_diff_target_file}" "${git_difftool_current_dir}/${git_diff_target_file}" fi git_diff_file_line_seek='n' fi fi done < "${git_diff_file}" pushd "${git_difftool_selected_rev_dir}" patch -R -p1 < "${git_diff_file}" popd # git_difftool_selected_rev_dir popd # git rev-parse --show-toplevel meld "${git_difftool_current_dir}" "${git_difftool_selected_rev_dir}"
Attachment:
pgpd20Q1oqZVI.pgp
Description: OpenPGP digital signature