I use the legacy/community maintained ForceCLI project to do delta deployments when not using Salesforce DX. It's pretty easy to get a basic workflow going using a shell script.
You can do something like this:
git diff [...] --name-only --diff-filter=ACM | force push -f -
What this does is get a list of changed files between two Git revisions (you'd replace
[...] with either a reference to a start and end commit, or an expression like
HEAD~1 for a delta of
HEAD against the previous commit) and list their paths. It then pipes those paths into
force, which will automatically construct a
package.xml and run the delta deployment.
I've found a few issues with ForceCLI around delta-deploying unpacked Static Resources and Lightning Components (I believe the latter has already been fixed, but may not have been released yet), but those issues are more or less easily worked around with a little Bash. For static resources, I use a script that zips them up and then pipes the
git diff output through
sed to squash all references to static resource elements to a reference to the zip file, e.g.
sed -E 's#^src/staticresources/([^/]+)/.+#src/staticresources/\1\.resource#
Note that one thing this approach doesn't handle is deletions. I delete metadata so rarely that I have not invested the effort to script delta deletions. Since deletions can be run either pre- or post-deploy and sometimes need to be one or the other, I've felt the effort to introspect the desired delete operation off of a commit message was more than it was worth.