git log --name-status --pretty=format: | sed '/^$/d' | cut --fields=2- | sort | uniq --count | sort --numeric-sort --reverse | head
prints the 10 most edited files in a Git repo (based on this). I want to create a Git alias most-edited
for this command with one caveat: the command should put any extra arguments into the git log
command. This would allow me to run git most-edited 'foo bar/' baz/
to get the most edited files in the foo bar
(note the space) and baz
directories.
In a script this would be as simple as adding -- "$@"
to the git log
command. However, if I create an alias most-edited = !git log --name-status --pretty=format: -- "$@" | sed '/^$/d' | cut --fields=2- | sort | uniq --count | sort --numeric-sort --reverse | head
the arguments are instead passed to the last command in the pipeline, head
. Is there some way to pass the arguments to git log
?
CodePudding user response:
In the alias, define a function followed by an immediate call to the function. When the alias is expanded, any additional arguments on the command line become arguments to that function.
most-edited = '!f () { git log --name-status --pretty=format: -- "$@" | sed '/^$/d' | cut --fields=2- | sort | uniq --count | sort --numeric-sort --reverse | head; }; f'
CodePudding user response:
TL;DR: you can also use "!git log --name-status --pretty=format: \"$@\" | sed '/^$/d' | cut --fields=2- | sort | uniq --count | sort --numeric-sort --reverse | head #"
. The final #
is required.
Long
Both iBug's answer ("write your own script and make it Git-callable") and chepner's answer ("use an alias that invokes a shell function") work fine, and are fully generic. There's one more possibility here, which is quite tricky, but also illustrative. I like to use the wc
command to help show how the arguments get broken up:
[alias]
visargs = "!wc head \"$@\" tail #"
Running:
GIT_TRACE=1 git visargs "one two" three
produces this output:
18:58:34.031386 git.c:702 trace: exec: git-visargs 'one two' three
18:58:34.031749 run-command.c:663 trace: run_command: git-visargs 'one two' three
18:58:34.032434 run-command.c:663 trace: run_command: 'wc head "$@" tail #' 'one two' three
wc: head: open: No such file or directory
wc: one two: open: No such file or directory
wc: three: open: No such file or directory
wc: tail: open: No such file or directory
0 0 0 total
Note how the arguments were passed through to wc
correctly: one two
, as a single argument, got through as a single argument, and three
got through as a single argument as well. The wc
command got the arguments head
and tail
separately, and did not get a repeated set of arguments, even though, as we can see from the trace
lines, the command run was really:
trace: run_command: 'wc head "$@" tail #' 'one two' three
The first expression is fed straight to sh
as its -c
argument. The remaining expressions are $1
, $2
, and so on. That's why the $@
works where it is, but also why we need the comment character: without the #
, the arguments get tacked onto the final part of the alias expansion.
CodePudding user response:
Save your shell script to a file named git-most-edited
somewhere in the $PATH
, like ~/.local/bin/
, and do chmod 755
on the file.
#!/bin/sh
git log --name-status --pretty=format: -- "$@" | sed '/^$/d' | cut --fields=2- | sort | uniq --count | sort --numeric-sort --reverse | head
You can then run git most-edited
and Git will invoke your shell script for you.