git Create Tree from Arbitrary Directory (works in bare repository) I made a script for a project I'm working on to create a tree object from a filesystem directory, without any working tree operations (can be run in a bare repository). The tree can be inserted into (a) commit(s) by using an index filter that invokes git read-tree --prefix=&lt;/...&gt;.

#!/bin/bash
# createSubtreeRec &lt;path&gt;
# Creates a subtree with all the contents of path
# does not need to be in a work tree.
# Tree and related objects are added to the repository (unreachable)
# recursive.
# Prints the line that should be used to include the built tree in a parent
#   in the form used by git mktree on stdout. This might be a blob or tree.
#   &lt;mode&gt; &lt;type&gt; &lt;hash&gt;    &lt;name&gt;
#         ^space ^space    ^tab
function createSubtreeRec () {
    local path="$1"
    local hash=""
    local type=""
    local mode=""
    local treeData=""
    #echo "" &gt;&amp;2
    if [ -d "$path" ]; then
        for fullpath in $path/*; do
            # edited from my original post to simplify the code
            treeData="$(printf '%s\n%s' "$treeData" "$(createSubtreeRec "$fullpath")")"
        done
        # ignore empty dirs
        if [ "$treeData" ]; then
            # remove leading \n
            treeData="${treeData:1}"
            #echo "$treeData" &gt;&amp;2
            hash="$(echo "$treeData" | git mktree)"
            printf '040000 tree %s\t%s' $hash "$(basename "$path")"
        fi
    elif [ -f "$path" ]; then
        hash=$(git hash-object "$path" -w)
        type=blob
        mode=100644
        if [ -x "$path" ]; then
            mode=100755
        fi
        printf '%s blob %s\t%s' $mode $hash "$(basename "$path")"
    fi
    #echo "" &gt;&amp;2
}

# createSubtree &lt;workdir&gt; &lt;path&gt;
# Creates a subtree starting in &lt;workdir&gt; with the names in &lt;path&gt; as recursive trees,
# Use to get a tree that does not contain &lt;workdir&gt; but contains all of &lt;path&gt; 
#   and all contents beneath it.
# Prints the top tree's hash on standard output.
function createSubtree () {
    local workdir="$1"
    local path="$2"
    # Get tree from
    if [ "$path" != "" ]; then
        local entry=$(createSubtreeRec "$workdir/$path")
    else
        local entry=$(createSubtreeRec "$workdir")
    fi
    if [ "$entry" ]; then
        while [ "$path" ] &amp;&amp; [ "$path" != "." ] ; do
            local hash=$(echo "$entry" | git mktree)
            path="$(dirname "$path")"
            local entry="$(printf '%s %s %s\t%s' '040000' tree $hash $(basename "$path"))"
        done
        printf '%s\n' $hash
    fi
}


createSubtreeRec outputs the full tree data line because it can sometimes output blobs.

This code is a significant part of my runtime, so any performance improving observations would be appreciated.

Part of me just wants this available online as it would have helped me out, and I can't find any git builtin commands for this.
Consider portable shell
Do we really need Bash for this?  If we can manage without local variables and string indexing, then we could use a leaner, more portable shell instead.  Certainly, there's no benefit using the non-standard function keyword - just omit it.
We probably do want local in the recursive function, but it's good to be clear that we've made the choice for a good reason.
Add some error checking options
set -eu -o pipefail

Use more quoting
We don't want $path or $fullpath to be split:
    for fullpath in &quot;$path&quot;/*; do
        f=$(basename &quot;$fullpath&quot;)

There's some more needed later:
            treeData=$(printf '%s\n%s %s %s\t%s' &quot;$treeData&quot; $mode $type $hash &quot;$f&quot;)
        elif [ -d &quot;$path/$f&quot; ]; then
            # recurse
            treeData=$(printf '%s\n%s' &quot;$treeData&quot; &quot;$(createSubtreeRec &quot;$fullpath&quot;)&quot;)

Shellcheck can't know that $hash will always be a single token, so it recommends more quoting than is actually necessary.