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.
