ga somename.java
is short for git add **/ somename.java
. The first argument to the alias is not concatenated to the last word inside the alias. You can think of it this way: the space that you type after ga
is not removed.
To do anything more complex than give a command an alternate name or pass arguments to a command, use a function instead of an alias.
Here's a zsh version which is pretty versatile.
function ga {
git add **/$^~@(.N)
}
alias ga='noglob ga'
Here's how it works for ga foo* bar
:
- The alias
ga
expands to noglob ga
.
- Thanks to the
noglob
precommand modifier, noglob ga foo* bar
does not expand the wildcard in foo*
, and instead passes foo*
literally as an argument to ga
.
- Since
ga
(the one in noglob ga …
) isn't the first word in the command, it is not looked up as an alias, and therefore it refers to the function, which is called with the arguments foo*
and bar
.
$^~@
uses the ${^spec}
and ${~spec}
parameter expansion forms. The modifier ^
causes **/
to be prepended to each element of the array $@
, resulting in **/foo*
and **/bar
. Without this modifier, **/$@
would expand to **/foo*
and bar
.
$~@
causes the wildcard characters in the arguments to be expanded now. This way, the pattern foo*
is not expanded by itself: what's expanded is **/foo*
, so this matches things like sub/dir/foobar
.
- The
.
glob qualifier causes only regular files to be matched. Make it -.
to also match symbolic links to regular files, or @.
to match all symbolic links in addition to regular files.
- The
N
glob qualifier causes patterns that match nothing to be omitted. Don't put (N)
if you prefer to have an error if one of the patterns doesn't match anything.
About the only nice thing that's missing here is completion, which is doable but I think not worth the trouble here.
In bash, you can't have it as nice. There's no way to prevent wildcards from being expanded immediately, so ga foo*
will be equivalent to ga foo1 foo2
if the directory content is
.git …
foo1
foo2
subdir
subdir/foobar
which will miss subdir/foobar
.
In bash, since you need to quote wildcards in arguments, you might as well rely on Git's own pattern matching in a pathspec. Note in particular that *
matches directory separators, i.e. it has the shell **
behavior built in.
function ga {
local x
for x in "$@"; do
git add "*/$x"
done
}
ga 'foo*' bar
On a final note, if you keep your .gitignore
up-to-date and commit often, you'll rarely need anything other than git add .
.
ga
to rungit add **/
or are you expecting it to take arguments so that if you runga foo bar
it will actually executegit add **/foo
andgit add **/bar
? – terdon Apr 12 '21 at 19:17ga foo
should expand togit add **/foo
– Faraz Durrani Apr 12 '21 at 21:32ga foo bar
. It seems Giles also went on to the same tangent. I don't even know whatga foo bar is
. I will just runga foo
. That's it. And if needed I will run it multiple times replacing foo with whatever. And I will always run it from the root of the project. In the end, I just want to stage changes in foo file in git. foo has a long directory stucture prepended to it. I want to avoid typing. Sogit add **/foo
is a shortcut to add a particular file without copying the whole directory path prepended to it. I want to create an alias that would even shorten this part. – Faraz Durrani Apr 12 '21 at 21:38ga file1 file2
, that'sga foo bar
for purposes of the above. You mean to tell us you run a separategit add
for every single file? – Charles Duffy Apr 13 '21 at 04:03*.class
files,*~
backup files from your editor, etc. – Basile Starynkevitch Apr 13 '21 at 05:20