98

Can I embed the following bash shell code:

for name in $(git diff --name-only $1); do git difftool $1 $name & done

directly into the creation of a git alias:

git config --global alias.diffall ***my-bash-code-here***

This leads on from my previous question/answer on SO, where I put the code into a .sh file and then aliased to the file:

git config --global alias.diffall '!sh diffall.sh'

But in the never-ending quest for simplicity, there's gotta be a way to skip the file and insert code directly into the alias? I can't figure out the format...

Community
  • 1
  • 1
Seba Illingworth
  • 5,672
  • 3
  • 27
  • 25
  • Note that `sh` and `bash` are two different shells (and when started under the sh name, bash disables some features; so they remain different even when `sh` is a symlink to `bash`). Strictly speaking, you're embedding a sh script, not a bash script, and attempts to use bash-only syntax would fail. – Charles Duffy Sep 13 '22 at 15:51

5 Answers5

109
git config --global alias.diffall '!sh diffall.sh'

This is redundant in one way. If you are going to add 'diffall.sh' into your $PATH anyway, why not save it as 'git-diffall', and save yourself from declaring an alias. Yes, "git diffall" will run it.

u0b34a0f6ae
  • 48,117
  • 14
  • 92
  • 101
  • 1
    That. Is great. I initially worried that I would accidentally run something, since I've written a few shell script to do git stuff, and given them the prefix 'git-', But my shell (zsh) doesn't autocomplete them, so I'd have to type them explicitly. And if I want autocomplete behavior, I can declare a git alias to its own subcommand, which is also a requirement that I make the link from script to subcommand explicit. – Jon Carter Oct 19 '15 at 17:18
  • 1
    Yup, and it's a binary you just need the bang: `! binary` –  Mar 11 '16 at 14:51
  • 2
    Doesn't work for me, using inside an alias `!sh git checkout $(git log --branches -1 --pretty=format:'%D' | sed 's/.*, //g')` results in `/usr/lib/git-core/git: /usr/lib/git-core/git: cannot execute binary file`. Tried it both with and without `sh` in the beginning. – Hi-Angel Feb 04 '19 at 10:11
44

To run commands inside of a git alias, and in particular to pass arguments to those commands, you will likely have to create a temporary function which you then immediately invoke:

$ vim ~/.gitconfig
...
[alias]
    # compare:
    foo = "! echo begin arg=$1/$2/end"
    foo2 = "!f() { echo "begin arg=$1/$2/end"; }; f"

In this example, the function is probably what you need (and is also more flexible as to what you can do in a single "statement"); and you can probably tell that for both options, the remaining args to the git command are simply passed as args to the alias, regardless if it's "echo" or "f"; invoking the function simply consumes the args, ignoring what's not explicitly used:

$ git foo a b c
begin arg=a/b/end a b c

$ git foo2 a b c
begin arg=a/b/end

Another example (lists all aliases, based on matching pattern) (note: you can keep reusing the same function name "f()" throughout the .gitconfig):

[alias]
    alias = "!f() { git config --get-regexp "^alias.${1}$" ; }; f"

The first returns the alias for just "foo$", the second for "foo.*":

$ git alias foo
alias.foo ! echo begin arg=$1/$2/end

$ git alias 'foo.*'
alias.foo ! echo begin arg=$1/$2/end
alias.foo2 !f() { echo begin arg=$1/$2/end; }; f

(nb: actual results may vary based on shell; I'm using this with bash on Linux, Unix & Cygwin (Windows).)

michael
  • 9,161
  • 2
  • 52
  • 49
  • https://git.wiki.kernel.org/index.php/Aliases#What.27s_new.3F implicitly recommends using `sh -c`, that way you don't have to worry what the user's login shell is. – clacke Jun 30 '16 at 13:12
  • the function syntax I've provided is intentionally portable across quite a few shells (sh/ksh/bash/zsh), and will be faster than launching a subprocess. I'd recommend still using the function syntax, unless there's a specific portability issue on a specific platform. – michael May 24 '17 at 23:27
  • Your comment brought me back to this answer, and I'm probably going to use your way. It saves one level of quotation, and those levels are crazy enough as it is. :-) – clacke May 25 '17 at 23:07
  • This looks like a nice article describing what you're doing: https://www.atlassian.com/blog/git/advanced-git-aliases – Gabriel Staples Aug 31 '18 at 15:45
  • Note: I've posted a follow-up question regarding this answer here: https://stackoverflow.com/questions/52123145/how-to-replace-internal-git-commands-such-as-git-status-with-custom-aliases. "How to replace internal git commands, such as `git status`, with custom aliases" – Gabriel Staples Aug 31 '18 at 22:15
27

I couldn't find in documentation, but if you create a script "git-<name>" in path, you can call it with "git name" in your repo.

See:

$ cd ~/bin
$ echo "echo I love this log:
>pwd
>git log --graph  --summary --decorate --all" > git-logg
$ chmod +x git-logg
$ cd /path/to/your/repo
$ git logg
I love this log:
/path/to/your/repo
* commit 3c94be44e4119228cc681fc7e11e553c4e77ad04 (whatever-branch)
| Author: myself <my@Laptop.(none)>
| Date:   Fri Apr 1 16:47:20 2011 +0200
| 
|     would have been better not to do it at all
| 
...
$

So, you can write any alias you like with this (rather obscure) way too.

Even more you can add autocompletion to that new command defining a function. Info here

$ _git_logg ()
{
  # you can return anything here for the autocompletion for example all the branches
  __gitcomp_nl "$(__git_refs)" 
}
albfan
  • 12,542
  • 4
  • 61
  • 80
17

Adding these 2 line to your .git/config file should do the trick.

[alias]
    diffall = '!for name in $(git diff --name-only $1); do git difftool $1 $name & done'

Edit: presumably the git-config version works too, but I like to keep my aliases in the config file for ease of management.

There is a nice page on the git wiki that explains aliases very clearly: http://git.or.cz/gitwiki/Aliases In particular, read 'advanced aliases with arguments'

David Claridge
  • 6,159
  • 2
  • 27
  • 25
  • git config --global will store your aliases on $HOME/.gitconfig, where (IMO) they are even easier to manage; almost no aliases need to be repository specific. (Repo specific go into repo/.git/config) – u0b34a0f6ae Aug 21 '09 at 01:31
  • Thanks David, but it just won't work for me - that or any of the variations in the great link you included. Maybe I should have mentioned that I'm running in Windows environment? Thanks Kaizer, agreed, this is 'global' for me. – Seba Illingworth Aug 21 '09 at 01:48
  • These days, https://git.wiki.kernel.org/index.php/Aliases#Advanced is a good place to look. (the git or cz link redirects there, but this section in particular mentions running complex things with arguments) – clacke Jun 30 '16 at 13:14
-4

(From ProGit documentation: http://progit.org/book/ch7-3.html)

Have you tried to add a script in .git/hooks?

For example, if you create a script .git/hooks/post-checkout:

#!/bin/bash

echo "This is run after a 'git checkout'"

and then run the command:

$ git checkout master
Switched to branch 'master'
This is run after a 'git checkout'
Denian
  • 737
  • 2
  • 8
  • 17