1

I have gotten into the habit of using the following command to quickly open relevant files in my editor for code reviews.

git diff --name-only master... | xargs realpath | someEditor

But recently been encountering a problem where the first or second file might have been deleted and the editor will produce an error message and not open the rest of the files forcing me to open them one by one.

Is there a similar command I can type that will skip the files that do not exist?

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
Brad
  • 741
  • 7
  • 17
  • According to the man page for the command, "all but the last component must exist". – Jeff Holt Mar 04 '20 at 04:15
  • Use `readlink` and it has explicit options about how to handle files that don't exist. – Charles Duffy Mar 04 '20 at 04:29
  • @CharlesDuffy readlink doesn't appear to output anything. Also yeah I get the absolute path bit but guess im used to php realpath where path must exist. – Brad Mar 04 '20 at 06:48
  • readlink has options (`-f`, `-m`, etc). Which one is selected is important to choosing its behavior. – Charles Duffy Mar 04 '20 at 13:08
  • BTW, note that `xargs` is unreliable when names have whitespace in them unless you use either `-0` (with NUL-delimited input and output) or `-d $'\n'` (with newline-delimited input and output, but requires GNU tools). – Charles Duffy Mar 04 '20 at 13:46

1 Answers1

1

Narrowly Addressing The Problem: With Readlink

Since you're on Ubuntu (which has GNU readlink), you can use readlink -e, which has the exact behavior you're hoping that realpath will provide:

git diff --name-only master... \
  | xargs -d $'\n' readlink -e -- \
  | xargs -d $'\n' someEditor

Since you're on Ubuntu, which provides GNU xargs, this code will correctly handling filenames with spaces, literal quotes, or literal backslashes on account of using -d $'\n' on both xargs invocations.


Narrowly Addressing The Problem: With Realpath

If we want to stick with your existing tools (which is to say, realpath instead of readlink) and just add an existence test, that could look like:

git diff --name-only master... \
  | xargs -d $'\n' sh -c 'for f; do [ -e "$f" ] && realpath "$f"; done' _ \
  | xargs -d $'\n' someEditor

Implementation Advice

By the way -- consider encapsulating your desired implementation in a shell function in your ~/.bashrc. That might be something like...

forEachChangedFile {
  local -a files
  readarray -t files < <(
    git diff --name-only "${branchExpr:-master...}" \
      | xargs -d $'\n' readlink -e --
  )
  if (( $# )); then
    "$@" "${files[@]}"
  else
    "${EDITOR:-someEditor}" "${files[@]}"
  fi
}

...later used as:

forEachChangedClient vim

or

branchExpr=someBranch..someOtherBranch forEachChangedFile emacs-client

The advantage of not using xargs for the final invocation of the editor is that it leaves the editor's stdin clear, so you can use it for editors that communicate with the user over that channel.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • thanks for the insights. I was hoping there was some less complicated way I could just type without writing a script. But making a cross-platform script for macOS and Ubuntu is probably the best and I'll use part of your answer so marked as the best answer. – Brad Mar 29 '20 at 09:40