0

When I do a git status, I get something like this:

modified   a/mega/super/duper/long/path/to/the/file/foo.php
modified   a/mega/super/duper/long/path/to/the/file/another/folder/bar.php
modified   a/mega/super/duper/long/path/to/the/file/someotherfolder/blahblah.php

If I want to see what has changes for the bar.php-file (line 2) how do I do that?

I was hoping to be able to do something like this:

git diff */bar.php
git diff bar.php

And that git could tab-complete or read my mind. :-)


Idea 1: Writing it out:

It's quite extensive having to write:

git diff a/mega/super/duper/long/path/to/the/file/another/folder/bar.php

Idea 2: Use the mouse + Copy/paste

I could use the mouse and copy-paste, but that too seems like a poor solution. Taking the fingers away from the keyboard usually isn't the solution. And I have to do it quite a lot.

Idea 3: Git add patch

By running git add -p, I automatically goes into an interactive mode, being able to do a bunch of stuff. But it's not quite what I'm after.

Idea 4: Using sed

I found this SO-question: git diff just the first file, which suggests this:

git diff $(git diff --name-only | sed -n '1 p')

I could then setup an Alfred-snippet or a bash-script that prints that. Whereafter I can change the 1 to the number of file I would like to see.

This is close to being good.

Idea 5: Bash-script to copy file-name of nth file to clipboard

Another way would be to make a bashscript, that copies the nth-file to the clipboard. Then I could do it in two swift commands, like this:

./copy-file-name-for-first-file-in-git-status.sh     # Named to make easy to understand.
git diff [PASTE]

This would actually also solve it for the bonus-question below.


Bonus question - Same problem for git add and git log

The same issue is present, if I don't want to do a git add ., but want to add files individually in the deep nested folders. Then I also have to type my way through the entire path, for every single file.

Zeth
  • 2,273
  • 4
  • 43
  • 91
  • Does this answer your question? [View the change history of a file using Git versioning](https://stackoverflow.com/questions/278192/view-the-change-history-of-a-file-using-git-versioning) – Liam Feb 03 '22 at 15:42
  • 1
    You could make a bash function that uses `find` to get the full path of some file and then call `git diff` for you. – 0stone0 Feb 03 '22 at 15:44
  • @Liam Thanks for weighing in. I'm afraid that doesn't answer my question. In that question, you're faced with the same issue as I am, just with `git log` instead. One have to still type out the entire path to the files, which is what I'm trying to get around. – Zeth Feb 03 '22 at 15:47
  • @0stone0 - Good idea! I'm not that strong with bash-scripting. But that's definitely a good way to go. However, it also had to handle, in case that `find`-command found several files. For instance, if I ran: `git diff $(gitfind *.php)`, then it would return both `foo.php`, `bar.php` and `blahblah.php` (for the provided example). – Zeth Feb 03 '22 at 15:50
  • 1
    git diff can accept multple values. But you can include that in the function. What do you expect to happen? ALways take the first path from find? Cancel if there are multiple files found? – 0stone0 Feb 03 '22 at 15:50
  • What if `bar.php` was in multiple folders? – Liam Feb 03 '22 at 15:51
  • @0stone0 ... If it could diff them both, that would be awesome! But if that's troublesome, then just cancel with a warning, telling me to be more specific. – Zeth Feb 03 '22 at 15:55
  • I'm inclined to say this isn't a git question at all. the finding the file and diff are two different things. The file will exist locally, so introducing git into the equation seems pointless. Once you get a command that works you can add it as a custom command https://gitbetter.substack.com/p/automate-repetitive-tasks-with-custom – Liam Feb 03 '22 at 15:55
  • Good point, @Liam . I was hoping that git had a solution to this. But if it doesn't, then it's pivoting towards becoming a bash-question. I added that tag. Thanks! – Zeth Feb 03 '22 at 16:01
  • 2
    `git diff -- **/bar.php` works fine (when `shopt -s globstar` has been previously run). What's your question here? – Charles Duffy Feb 03 '22 at 16:05
  • 1
    BTW, `anything $(find ...)` is an antipattern, code that's innately buggy and [should never be run by anyone](http://mywiki.wooledge.org/BashPitfalls#for_f_in_.24.28ls_.2A.mp3.29). A typical safer replacement looks like `find ... -exec anything {} +` – Charles Duffy Feb 03 '22 at 16:06
  • 1
    One thing to be aware of, btw, is that globs are evaluated *by your shell*, before the command you're running (like git) is ever started. So git has no chance to look for _historical_ files that don't exist: The glob is replaced with a list of matching files that exist right now _before git is even started_ as long as any such files exist at all. – Charles Duffy Feb 03 '22 at 16:07
  • `git diff -- "*/bar.php"` — in that case `"*/bar.php"` is expanded by Git, not Bash. – phd Feb 03 '22 at 16:22

2 Answers2

0

For ease of use, how about a Git alias rather than an external function? You can put it in your global config and you won't have to mess with remembering the new function or adding it to the path.

As you can see below it will run the diff with only a (potentially) very short substring to identify the file. It's a regex search and will gracefully handle cases when you specify a regex that matches no files or more than one file.

Add this to your git config:

[alias]
        diffr = "!a() { myfile=$(git status | awk '/modified:/ {print $2}' |grep "$1") ; cnt=$(git status |awk '/modified:/ {print $2}'|grep "$1" |wc -l) ;if [ $cnt -eq 1 ] ; then git diff $myfile;else printf '%s Matched %s files\n' $1 $cnt;fi; if ! [ $cnt -eq 0 ] ; then git status |grep "$1"; fi; }; a"

Run it with git diffr "<regex>"

Here's the status of the test repo:

me@mypc MINGW64 /c/repos/aliastest (master)
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   mega/super/duper/long/path/to/the/file/another/folder/bar.php
        modified:   mega/super/duper/long/path/to/the/file/foo.php
        modified:   mega/super/duper/long/path/to/the/file/someotherfolder/blahblah.php

no changes added to commit (use "git add" and/or "git commit -a")

Here's a successful diff for a file with 'foo' in the name (when it's the only matching file):

me@mypc MINGW64 /c/repos/aliastest (master)
$ git diffr foo
warning: LF will be replaced by CRLF in mega/super/duper/long/path/to/the/file/foo.php.
The file will have its original line endings in your working directory
diff --git a/mega/super/duper/long/path/to/the/file/foo.php b/mega/super/duper/long/path/to/the/file/foo.php
index 90be1f3..294186e 100644
--- a/mega/super/duper/long/path/to/the/file/foo.php
+++ b/mega/super/duper/long/path/to/the/file/foo.php
@@ -1 +1 @@
-before
+after

This is how it handles the invalid case where the regex matched more than one file:

me@mypc MINGW64 /c/repos/aliastest (master)
$ git diffr "folder/b[al]"
folder/b[al] Matched 2 files
        modified:   mega/super/duper/long/path/to/the/file/another/folder/bar.php
        modified:   mega/super/duper/long/path/to/the/file/someotherfolder/blahblah.php

Here it is handling a regex that matched no files:

me@mypc MINGW64 /c/repos/aliastest (master)
$ git diffr "sirnotappearinginthisfilm"
sirnotappearinginthisfilm Matched 0 files

Aside from all the escaping it's just a plain Bash function so you can edit it as you please. You could call a separate executable in place of the Bash function but then you'd have external dependencies. Minor adjustments to the script could fulfill your git add request as well. If I find time to write it I'll post it here. Similar functionality with git log seems like a different animal entirely since your possible results can be enormous.

I have had the same desire to keep my hands on the keyboard and the same problems with Git on the command line, but until now I hadn't really thought of how to solve this one. I'll be using this solution from now on whether or not anyone else does, so thanks for the suggestion.

LinuxDisciple
  • 2,289
  • 16
  • 19
0

This works for me:

git diff **/bar.php 

You may need to run shopt -s globstar first, but I didn't on my system.

Holly Cummins
  • 10,767
  • 3
  • 23
  • 25