What does the exclamation mark mean in git config alias?
Asked Answered
T

3

51

I just see a git config command as below:

git config --global alias.out \!"git fetch; git cherry origin/HEAD -v"

what does it mean? Is it equal to this:

git config --global alias.out '!git fetch; git cherry origin/HEAD -v',

I am confused with the exclamation mark,any help will be appreciated.

Tamp answered 13/1, 2014 at 4:32 Comment(2)
I thought the exclamation mark meant that the alias is a shell command, rather than a git command. I can't remember 100%, though.Marthamarthe
Note that if you alias a command like lg1 = log --graph --format... you can't alias that as though it's a subcommand (lg = lg1 will not work), but you need to use ! as in lg = !"git lg1"Md
L
59

From the git-config manual:

If the alias expansion is prefixed with an exclamation point, it will be treated as a shell command.

Laurent answered 13/1, 2014 at 4:46 Comment(1)
“Note that shell commands will be executed from the top-level directory of a repository, which may not necessarily be the current directory.” → useful, i.e. for (example) git config --global alias.conf "!cat .git/config"Burgess
W
9

The most-upvoted answer says only that the ! makes the git alias a "shell command". So...what does that mean!? That simply begs my immediate follow-up question:

What does executing a git alias as a "shell command" mean and do?

@Frank Nocke graciously answers that question in his comment here. His comment really should be another answer (so I could have upvoted it), so here is my attempt at making it an answer:

Let's quote some more of the manual from man git commit (my emphasis added):

If the alias expansion is prefixed with an exclamation point, it will be treated as a shell command. For example, defining alias.new = !gitk --all --not ORIG_HEAD, the invocation git new is equivalent to running the shell command gitk --all --not ORIG_HEAD. Note that shell commands will be executed from the top-level directory of a repository, which may not necessarily be the current directory. GIT_PREFIX is set as returned by running git rev-parse --show-prefix from the original current directory. See git-rev-parse(1).

See also (thank you @Martin C. for pointing this out!): https://git-scm.com/book/en/v2/Git-Basics-Git-Aliases, which says (emphasis added):

As you can tell, Git simply replaces the new command with whatever you alias it for. However, maybe you want to run an external command, rather than a Git subcommand. In that case, you start the command with a ! character. This is useful if you write your own tools that work with a Git repository. We can demonstrate by aliasing git visual to run gitk:

$ git config --global alias.visual '!gitk'

So, the ! indicates that the git alias is accessing a regular shell command instead of a git subcommand, and it also causes git to execute this shell command in the root directory of the git repository instead of in the directory you are currently in.

So, if you run this command to give you a git alias to git add and git commit in one git add-commit command (source):

git config --global alias.add-commit '!git add -A && git commit'

...then you'll get this in your ~/.gitconfig file:

[alias]
    add-commit = !git add -A && git commit

Now you can use that alias like this:

git add-commit -m 'My commit message'

And that alias is run as though it was a regular shell command run at the root dir of the git repo, so it's kind of like doing this apparently:

starting_dir="$PWD"
# see: https://mcmap.net/q/13693/-is-there-a-way-to-get-the-git-root-directory-in-one-command
repo_root_dir="$(git rev-parse --show-toplevel)"
cd "$repo_root_dir"
git add -A && git commit -m 'My commit message'
cd "$starting_dir"

So, if you're not a big fan of git aliases, and just want to make it a bash function instead, you could do that by adding the following to the bottom of your ~/.bashrc file instead:

git-add-commit() {
    starting_dir="$PWD"
    # See: https://mcmap.net/q/13693/-is-there-a-way-to-get-the-git-root-directory-in-one-command
    repo_root_dir="$(git rev-parse --show-toplevel)"
    cd "$repo_root_dir"
    git add -A
    git commit "$@"
    cd "starting_dir"
}

Then run it like this (notice the extra dash after git):

git-add-commit -m 'My commit message'

OR, make that a stand-alone executable bash script in a file called git-add-commit inside a folder in your PATH (such as at ~/bin/git-add-commit), and with the #!/usr/bin/env bash shebang line at the top, and then you can run the command the same as the git alias, withOUT the hyphen after "git", like this:

git add-commit -m 'My commit message'

This works because the git executable apparently scans all directories in your PATH variable for all executables named git-ANYTHING and allows them to be run as git ANYTHING.

See also:

  1. Is there a way to get the git root directory in one command?
  2. Git add and commit in one command
  3. *****https://git-scm.com/book/en/v2/Git-Basics-Git-Aliases
Williamswilliamsburg answered 16/2, 2022 at 22:46 Comment(1)
An excellent and exhaustive answer to a seemingly obscure and concealed feature of git which provides a high level rundown of the feature and consequently demystifies it as a simple shell script. Perfectly complete and relevant. Every git user should read this after a year of git usage.Caiaphas
L
5

From https://git-scm.com/book/en/v2/Git-Basics-Git-Aliases

However, maybe you want to run an external command, rather than a Git subcommand. In that case, you start the command with a ! character

So if you only want to use git subcommand, you don't need to use ! here.

But if you want to use git command/external command, then you need !.

Litigate answered 24/8, 2018 at 8:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.