1

I'm attempting to implement Handyman5's gist (which is a comment on this answer, in this question) as a bash function in my .bashrc file.

:<<COMMENT
   Searches all commits in the current git repository for a file that matches a regular expression.

   Usage: gitf <regex>

   Parameter is required, and must be at least one non-whitespace character.

   Based on the GitHub gist
   - https://gist.github.com/anonymous/62d981890eccb48a99dc
   written by Stack Overflow user Handyman5
   - https://stackoverflow.com/users/459089/handyman5
   which is based on this SO question:
   - https://stackoverflow.com/questions/372506/how-can-i-search-git-branches-for-a-file-or-directory/372654#372654

   Short description: Stored in GITF_DESC
COMMENT
#GITF_DESC: For "aliaf" command (with an 'f'). Must end with a newline.
GITF_DESC="gitf [searchterm]: Searches the current git repository for the file name that matches a regular expression.\n"

The function body:

gitf()  {
   set +x
   #Exit if no parameter is provided (if it's the empty string)
      param=$(echo "$1" | trim)
      echo "$param"
      if [ -z "$param" ]  #http://tldp.org/LDP/abs/html/comparison-ops.html
      then
        echo "Required parameter missing. Cancelled"; return
      fi

   wasFound="0";
   LOC=refs/remotes/origin # to search local branches only: 'refs/heads'
   ref="%(refname)"
   for branch in `git for-each-ref --format="$ref" $LOC`; do
      found=$(git ls-tree -r --name-only $branch | grep "$param")
      if [ $? -eq 0 ]; then
         echo "${branch#$LOC/}: $ref:"; echo " $found"
         wasFound="1";
      fi
   done

   if ["$wasFound" -eq 0]; then
      echo "No files in this repository match '$param'."
   fi
   set -x
}

I'm trying to get it to print out the commit it's in, so I can quickly check it out. But it's only printing out the file path, despite the echos. Any guidance to what I'm missing?

Current output for gitf "05":

$ gitf "05"
+ gitf 05
+ set +x
05
master:
non_django_files/wordpress_posts/build_java/BuildPart05.java
non_django_files/wordpress_posts/templates/05_login_remember_me.html
[1: command not found
++ history -a

I'm pretty sure the last two lines are an unrelated .bashrc or .inputrc issue, but I'm including them in case they're relevant.


@SwankSwashbucklers: Thank you for basically doing my work for me (I'm new to both Bash and Git, so I say that genuinely). The output is now very clear and useful. I do notice that it's missing some hits. Here is a small repository (2mb zip) for instance. Searching for "05" responds with

$ gitf 05
05
master: 14e5cdd:
  non_django_files/wordpress_posts/build_java/BuildPart05.java
  non_django_files/wordpress_posts/templates/05_login_remember_me.html
master: 2efdeb1:
  non_django_files/wordpress_posts/build_java/BuildPart05.java
  non_django_files/wordpress_posts/templates/05_login_remember_me.html

These two commits are the most recent two if that means anything. 05_login_remember_me.html also exists in at least f87540e (the very first commit), 3a7dac9, and 3c5e4ec.

Community
  • 1
  • 1
aliteralmind
  • 18,274
  • 16
  • 66
  • 102
  • 5
    One thing is you need a space after a `[` because this is really a command, so `if [ "$wasFound" -eq 0 ]; then`. – zerodiff Jan 22 '15 at 17:05
  • 1
    @zerodiff, Yeah I noticed that as well. That seems to be what's causing the line: `[1: command not found` in his output – neatnick Jan 22 '15 at 17:08
  • 4
    That's because `[` is a command, not just syntax. Any command needs a space between it and its first argument. and `[` requires its last argument to be the word `]` – glenn jackman Jan 22 '15 at 17:11

1 Answers1

2

The script as it is right now just seems to be searching through the branches. It sounds like you want it to search through the branches and all the commits on those branches. To do that you need to add another for loop to your script that iterates through the commits on each of the branches you are searching though. Something like this:

for branch in `git for-each-ref --format="$ref" $LOC`; 
do
    for commit in `git rev-list $branch | grep -oP ^.\{7\}`; 
    do
        found=$(git ls-tree -r --name-only $commit | grep "$param")
        if [ $? -eq 0 ]; 
        then
            echo "${branch#$LOC/}: $commit:"; echo " $found"
            wasFound="1";
        fi
    done
done

You could also clean up the output of your script by indenting the paths to the files, and by adding space in between the different commits. Like this:

for branch in `git for-each-ref --format="$ref" $LOC`; 
do
    for commit in `git rev-list $branch | grep -oP ^.\{7\}`; 
    do
        found=$(git ls-tree -r --name-only $commit | grep "$param")
        if [ $? -eq 0 ]; 
        then
            echo "${branch#$LOC/}: $commit:"
            while read line;
            do
                echo "  $line"
            done < <(echo "$found")
            wasFound="1";
        fi
    done
done

And, as mentioned in the comments, you need to add space after and before the brackets of your if statement. Like this: if [ "$wasFound" -eq 0 ];

neatnick
  • 1,447
  • 1
  • 19
  • 29
  • This is great, thank you. I've posted a follow up at the bottom of my question. – aliteralmind Jan 23 '15 at 18:12
  • @aliteralmind, The zip in your follow up doesn't seem to contain a .git, so I can't really do any debugging. But, if I were to make a guess, I'd say you were missing the `--more` argument off the `git show-branch` command. – neatnick Jan 23 '15 at 18:51
  • I've pasted your code exactly, and it does contain the `--more` argument. – aliteralmind Jan 23 '15 at 18:53
  • The zip is the whole repository now: http://aliteralmind.com/docs/computer/programming/z_other/stackoverflow_post_data/djauth_lifecycle_final.zip It's only about 2mb. – aliteralmind Jan 23 '15 at 18:58
  • @aliteralmind, The issue seemed to be with `git show-branch`, I've updated the script to use `git rev-list` instead. Try it; it should work now. – neatnick Jan 23 '15 at 19:22
  • It does list all commits now, but there's one remaining issue: The goal is to find files that were changed or created in a commit. It seems to be listing all commits in which the file exists. I wasn't clear, because I didn't realize the distinction. I'm going to look into the rev-list command. – aliteralmind Jan 23 '15 at 20:42
  • @aliteralmind, If you want to ask another question trying to solve the new use case, I'd be happy to help you out with it. You should accept this answer if it solved the original question though. – neatnick Jan 23 '15 at 20:49
  • Agreed. Will post a followup. – aliteralmind Jan 23 '15 at 21:17
  • Followup: http://stackoverflow.com/questions/28119379/bash-function-to-find-all-git-commits-containing-a-file-whose-name-matches-a-re – aliteralmind Jan 23 '15 at 21:57