How to create a Git alias with nested commands with parameters?
Asked Answered
S

1

5

In my dotfiles I have the following function which works:

function undelete {
  git checkout $(git rev-list -n 1 HEAD -- "$1")^ -- "$1"
}

…which I use like this:

$ undelete /path/to/deleted/file.txt

I'd like to scope this command since it's a git command.

How do I create a git alias so that I can use this git alias command?

$ git undelete /path/to/deleted/file.txt

Here are two, of my attempts which do not work:

git config --global alias.undelete "!f() { git checkout $(git rev-list -n 1 HEAD -- $1)^ -- $1; }; f"
git config --global alias.undelete "!sh -c 'git checkout $(git rev-list -n 1 HEAD -- $1)^ -- $1' -"
Sobriety answered 9/5, 2017 at 22:45 Comment(3)
You're using doublequotes, so the shell does command and parameter and history expansion on the contents. Use single quotes so the shell passes the unexpanded contents to the git config command. Use '\'' to embed a singlequote in a singlequoted string.Rhombohedral
@Rhombohedral - Thank you! I'm curious, why didn't you post as an answer?Sobriety
It's not exactly what comments are supposed to be for, but low-effort tossoffs aren't exactly what answers are for either, whether not I've got that I'm-sure-this-is-right feeling. Leaving unchecked short starter-material answers as comments so people like @torek who do a professionally thorough job of it every time can do that and get proper credit without worrying about stepping on anybody's toes seems like a workable compromise.Rhombohedral
D
4

It is possible to do this with aliases (see jthill's comment):

git config --global alias.undelete '!f() { git checkout $(git rev-list -n 1 HEAD -- $1)^ -- $1; }; f'
git config --global alias.undelete '!sh -c "git checkout $(git rev-list -n 1 HEAD -- $1)^ -- $1" -'

I recommend writing anything complicated as a shell script:

#! /bin/sh
#
# git-undelete: find path in recent history and extract
. git-sh-setup # see $(git --exec-path)/git-sh-setup

... more stuff here if/as appropriate ...
for path do
    rev=$(git rev-list -n 1 HEAD -- "$path") || exit 1
    git checkout ${rev}^ -- "$path" || exit 1
done

(the for loop is intended to make it allow multiple path names to "undelete").

Name the script git-undelete, put it in your $PATH (I put scripts in $HOME/scripts), and any time you run git undelete, Git will find your git-undelete script and run it (with $PATH modified to have git --exec-path up front, so that the . git-sh-setup works).

Downbow answered 9/5, 2017 at 23:16 Comment(2)
Thanks for your recommendation! Hope you don't mind that I added examples of the answer I was looking for above your recommended answer. I'll definitely use your recommended solution in the future, however for the use case I had, the one liner was the solution I needed.Sobriety
No problem - and I spotted a bug in my prototype script, "$1" should have been "$path".Downbow

© 2022 - 2024 — McMap. All rights reserved.