2

I can't seem to add an alias for git add **/

I think those two asterisks is causing an issue, or it could be that forward slash. How do I solve this?

So far I have tried

alias ga='git add **/' 

When I run the above alias in my terminal, I issue this command ga somename.java which would be an equivalent if alias was not there would be git add **/somename.java.

I have also tried adding more asterisks, slashes, dollar sign followed by a quote, etc.

6
  • What command do you want to run exactly? Do you want ga to run git add **/ or are you expecting it to take arguments so that if you run ga foo bar it will actually execute git add **/foo and git add **/bar?
    – terdon
    Commented Apr 12, 2021 at 19:17
  • ga foo should expand to git add **/foo Commented Apr 12, 2021 at 21:32
  • I will never run ga foo bar. It seems Giles also went on to the same tangent. I don't even know what ga foo bar is. I will just run ga 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. So git 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. Commented Apr 12, 2021 at 21:38
  • If you'd be doing ga file1 file2, that's ga foo bar for purposes of the above. You mean to tell us you run a separate git add for every single file? Commented Apr 13, 2021 at 4:03
  • I would recommend not doing so. Because you might have *.class files, *~ backup files from your editor, etc. Commented Apr 13, 2021 at 5:20

2 Answers 2

10

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:

  1. The alias ga expands to noglob ga.
  2. 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.
  3. 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.
  4. $^~@ 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.
  5. $~@ 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.
  6. 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.
  7. 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 ..

2

Instead of relying on shell extglobs at all, you can tell find to do a recursive search and run git add for you.

ga() {
  local -a find_args
  local arg

  for arg in "$@"; do
    find_args+=( -o -name "$arg" )
  done

  find . '(' -false "${find_args[@]}" ')' -exec git add -- {} +
}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .