Search code examples
gitgit-diffgit-hash

Is it possible to create a Git hash object outside a Git directory?


I am trying to get the git diff between 2 strings. The following command works:

git diff $(echo "my first string" | git hash-object -w --stdin) $(echo "my second string" | git hash-object -w --stdin)  --word-diff

However, it fails if not executed inside a Git directory.

I believe this portion of the command is failing:

echo "my first string" | git hash-object -w --stdin

Is there any way around this so that it can be executed outside a Git directory?


Solution

  • I believe this portion of the command is failing:

    echo "my first string" | git hash-object -w --stdin
    

    Is there any way around this so that it can be executed outside a git directory?

    The problem you are having is because of the -w option that you pass to the git hash-object command. That option requires an existing repository as it has a side effect of writing the object into the git database.

    Proof:

    $ echo "my first string" | git hash-object -w --stdin
    fatal: Not a git repository (or any parent up to mount point /home)
    Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
    
    $ echo "my first string" | git hash-object --stdin
    3616fdee3ac48e5db02fbf9d5e1c2941cfa3e165
    

    However, since your final goal is to obtain the git diff between two given strings you have to have a git repository if you want to do it with the help of git hash-object1. To this end you can generate a temporary empty repository:

    $ tmpgitrepo="$(mktemp -d)"
    
    $ git init "$tmpgitrepo"
    Initialized empty Git repository in /tmp/tmp.MqBqDI1ytM/.git/
    
    $ (export GIT_DIR="$tmpgitrepo"/.git; git diff $(echo "my first string" | git hash-object -w --stdin) $(echo "my second string" | git hash-object -w --stdin)  --word-diff)
    diff --git a/3616fdee3ac48e5db02fbf9d5e1c2941cfa3e165 b/2ab8560d75d92363c8cb128fb70b615129c63371
    index 3616fde..2ab8560 100644
    --- a/3616fdee3ac48e5db02fbf9d5e1c2941cfa3e165
    +++ b/2ab8560d75d92363c8cb128fb70b615129c63371
    @@ -1 +1 @@
    my [-first-]{+second+} string
    
    $ rm -rf "$tmpgitrepo"
    

    This approach can be packaged into a bash function:

    git-diff-strings()
    (
        local tmpgitrepo="$(mktemp -d)"
        trap "rm -rf $tmpgitrepo" EXIT
        git init "$tmpgitrepo" &> /dev/null
        export GIT_DIR="$tmpgitrepo"/.git
        local s1="$1"
        local s2="$2"
        shift 2
        git diff $(git hash-object -w --stdin <<< "$s1") $(git hash-object -w --stdin <<< "$s2") "$@"
    )
    

    Usage:

    git-diff-strings <string1> <string2> [git-diff-options]
    

    Example:

    git-diff-strings "first string" "second string" --word-diff
    

    1 Note that you can git diff two strings by creating 2 temporary files containing those strings, in which case you don't need a git repository.