Search code examples
gitdebugginggit-filter

Getting git to show specific filenames it is running content filters on


I'm debugging the workings of a configured git content filter (nbstripout) and I'm trying to get GIT_TRACE to show me the files it's operating on, but it doesn't. Consider:

$ GIT_TRACE=1 git pull origin master
[...] removed irrelevant sections of the output
16:49:28.846707 run-command.c:640       trace: run_command: git merge FETCH_HEAD
16:49:28.849309 git.c:344               trace: built-in: git merge FETCH_HEAD
Updating 1ea49ad..ae0ba93
16:49:28.863291 run-command.c:640       trace: run_command: nbstripout
16:49:28.864700 run-command.c:640       trace: run_command: nbstripout
16:49:28.866060 run-command.c:640       trace: run_command: nbstripout
[...] many more of the same

How can I get GIT_TRACE's run_command to show the arguments passed to the filter? I looked at various other debug environment variables in git's manual, but I don't see anything that would enable that level of debug.

I'm aware of git check-attr, but I'd like to see the run time trace on which files it's running the filter on and with which arguments.

git version 2.17.1


Solution

  • I posted this question on the git mailing list and Jeff King provided a workaround as an answer. With Jeff's permission I'm sharing his answer here:


    GIT_TRACE should always show the arguments. But unless you specify arguments in the clean/smudge filter config, then Git won't pass any. The stdin/stdout stream is all that matters.

    So e.g.:

      $ echo '* filter=foo' >.gitattributes
      $ git config filter.foo.clean 'myfilter'
      $ GIT_TRACE=1 git add .
      19:42:16.516401 [pid=14112] git.c:415             trace: built-in: git add .
      19:42:16.517454 [pid=14112] run-command.c:637     trace: run_command: myfilter
    
      $ git config filter.foo.clean 'myfilter --foo'
      $ touch .gitattributes ;# make sure we actually read it again ;)
      $ GIT_TRACE=1 git add .
      19:42:58.122942 [pid=14156] git.c:415             trace: built-in: git add .
      19:42:58.124023 [pid=14156] run-command.c:637     trace: run_command: 'myfilter \
    --foo'
    

    You can use "%f" to pass the name of the file, like:

      $ git config filter.foo.clean 'myfilter %f'
      $ touch .gitattributes
      $ GIT_TRACE=1 git add .
      19:44:51.187177 [pid=14318] git.c:415             trace: built-in: git add .
      19:44:51.188256 [pid=14318] run-command.c:637     trace: run_command: 'myfilter \
    '\''.gitattributes'\'''
    

    Of course that won't be helpful if your filter actually respects the argument. For a "clean" filter that might be OK (e.g., if it just tells your filter to read from the filesystem instead of stdin), but it's almost certainly not what you want for a "smudge" filter.

    You can work around it with some shell hackery:

      git config filter.foo.clean 'f() { echo >&2 "cleaning $1"; myfilter ...; }; f %f'
    

    and then even without GIT_TRACE, you get:

      $ git add .
      cleaning .gitattributes
    

    Or if you really just want to trigger for GIT_TRACE, try just this:

      $ git config filter.foo.clean 'f() { myfilter; }; f %f'
      19:52:52.874064 [pid=14719] git.c:415             trace: built-in: git add .
      19:52:52.875115 [pid=14719] run-command.c:637     trace: run_command: 'f() { \
    myfilter; }; f '\''.gitattributes'\'''
    

    There you get the name in the trace output, but the invoked command doesn't actually do anything with it.


    So I ended up using:

    [filter "nbstripout"]
        clean  = "f() { echo >&2 \"clean: nbstripout $1\"; nbstripout; }; f %f"
        smudge = "f() { echo >&2 \"smudge: cat $1\"; cat; }; f %f"
        required = true
    

    and now I get the filenames logged when run with GIT_TRACE=1, and without it.


    In a followup Jeff suggested that one day git may support this directly and not need a workaround.