2015-01-23 3 views
5

Ich habe the following bash function, die nach allen Dateien in einem Repository sucht, deren Dateiname einem regulären Ausdruck entspricht. Es findet derzeit alle Commits, in denen eine Datei existiert. Wie kann dies geändert werden, so dass es nur unter Dateien, die bearbeitet wurden (erstellt, geändert oder gelöscht) in jedem Commit?Bash-Funktion, um alle Git-Commits zu finden, in denen eine Datei (deren Name einem Regex entspricht) * geändert wurde *

Das war meine ursprüngliche Absicht für die Funktion. Ich war überrascht, dass die Ergebnisse viel breiter als erwartet waren. Der Grund, warum ich das versuche: Ich habe vor langer Zeit eine Datei erstellt, und irgendwann habe ich versehentlich einen wichtigen Abschnitt daraus gelöscht. Ich möchte eine Liste aller Punkte (commits), an denen diese Datei geändert wurde, so dass ich schnell zu der Version zurückkehren kann, die den fehlenden Abschnitt enthält, und ihn wieder in die aktuelle-commmit-Version einfügen.

:<<COMMENT 
    Searches all commits in the current git repository containing a file whose name matches a regular expression. 

    Usage: gitf <regex> 

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

    The original version of this function was 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 

    The main section of this function was authored by Stack Overflow user 
    SwankSwashbucklers. 
    - https://stackoverflow.com/users/2615252/swankswashbucklers 
    - https://stackoverflow.com/a/28095750/2736496 

    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" 

Körper:

gitf() { 
    #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 
     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 

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

Antwort

3

Wenn Sie mit einem Shell-Glob-Muster eher als eine ausgewachsene regex, betrachten leben können

git log -p --diff-filter=AMD --branches --tags -- "foo*bar.sh" 

Mit -p finden Sie die Deltas zusammen mit der Nachricht begehen, Autor, SHA1, etc. Die --diff-filter=AMD Option wählt nur die Commits aus, in denen die betreffenden Dateien A dded, M odified oder D eleted sind. Verwenden Sie --all lieber als --branches --tags, um Remotes sowie lokale Verzweigungen und Tags zu durchsuchen. Schließlich notieren Sie die --, die Pfadmuster einführt, die Sie angeben möchten, damit git den Glob-Abgleich durchführen kann.

+1

Beachten Sie, dass Sie mit modernem Git ein erweitertes Glob-Pattern verwenden können, wobei '**' eine beliebige Anzahl von Pfadkomponenten abgleichen kann (d. H. Auch '/', die nicht mit '*' übereinstimmt). –

+0

Dies druckt den Körper jeder Datei aus. Basierend auf der akzeptierten Antwort in [diese Frage] (http://stackoverflow.com/questions/14207414/how-to-show-changed-file-name-only-with-git-log), die '--name- nur 'und' --online' Optionen verbessern es dramatisch: 'git log -p - nur -name --oneline --diff-filter = AMD --branches --tips -" * 05 _ * "'. – aliteralmind

4

Verwenden git diff-tree -r --name-only --no-commit-id (vielleicht mit --stdin) statt git ls-tree -r --name-only. Verwenden Sie -m oder -c, wenn Sie an Zusammenführungen, -M oder -C interessiert sind, wenn Sie Erkennung berücksichtigen, umbenennen und kopieren möchten.

Oder besser parse Ausgabe von git diff-tree -r.

Nb. Der Code in Frage ist ernsthaft suboptimal (unter anderem überprüfen Sie mehrmals die gleichen Commits).

+0

Können Sie einige Ihrer vorgeschlagenen Verbesserungen näher erläutern? – aliteralmind

+1

@aliteralmind: Verwenden Sie 'git rev-list --branches' einmal für alle Zweige anstatt einmal pro Zweig. –

2

Sie könnten gehen und git diff verwenden, um zu sehen, was sich zwischen den einzelnen Commits geändert hat. Etwas wie folgt aus:

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

ich kam mit dieser Funktion auf, die auf Greg Bacon's answer basiert. Ich wollte eigentlich Regex, aber die Kugeln passen gut zur Rechnung. Ich erwartete auch, dass eine Looping-Funktion erforderlich wäre, aber die einzige git log Linie ist alles, was benötigt wird.

Zuerst wird eine Nutzenfunktion:

#https://stackoverflow.com/questions/369758/how-to-trim-whitespace-from-bash-variable#comment21953456_3232433 
alias trim="sed -e 's/^[[:space:]]*//g' -e 's/[[:space:]]*\$//g'" 

Dokumentation Header:

:<<COMMENT 
    Searches all commits in the current git repository containing a file 
    that has *changed*, whose name matches a glob. If the glob does not 
    contain any asterisks, then it is surrounded by them on both sides. 


    Usage: 
     gitf "05"  #Equivalent to "*05*" 
     gitf "05_*" 

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

    See: 
    - https://stackoverflow.com/questions/28119379/bash-function-to-find-all-git-commits-in-which-a-file-whose-name-matches-a-rege/28120305 
    - https://stackoverflow.com/questions/28094136/bash-function-to-search-git-repository-for-a-filename-that-matches-regex/28095750 
    - https://stackoverflow.com/questions/372506/how-can-i-search-git-branches-for-a-file-or-directory/372654#372654 

    The main "git log" line is based on this answer 
    - https://stackoverflow.com/a/28119940/2736496 
    by Stack Overflow user Greg Bacon 
    - https://stackoverflow.com/users/123109/greg-bacon 

    With thanks to SwankSwashbucklers 
    - https://stackoverflow.com/users/2615252/swankswashbucklers 

    Short description: Stored in GITF_DESC 
COMMENT 
#GITF_DESC: For "aliaf" command (with an 'f'). Must end with a newline. 
GITF_DESC="gitf [glob]: Searches all commits in the current git repository containing a file that has *changed*, whose name matches a glob.\n" 

Körper:

gitf() { 
    #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 

    #https://stackoverflow.com/questions/229551/string-contains-in-bash/229606#229606 
    if [[ $param != *"*"* ]] 
    then 
    param="*$param*" 
    fi 

    echo "Searching for \"$param\"..." 

    git log -p --name-only --oneline --diff-filter=AMD --branches --tags -- "$param" 
} 

Beispiel Ausgabe:

$ gitf 05_ 
05_ 
Searching for "*05_*"... 
14e5cdd Quick save (no message): 01-21-2015__14_36_11 
non_django_files/wordpress_posts/templates/05_login_remember_me.html 
2efdeb1 Part four final. Changed auth/tests in post to auth/tests_login_basic. 
non_django_files/wordpress_posts/templates/05_login_remember_me.html 
526ca01 Part four final. Renamed auth/tests to test_basic_login, so Java doesn't need to parse the py file in future par 
non_django_files/wordpress_posts/templates/05_login_remember_me.html 
7c227f3 Escaped unescaped dollar-signs in initial_script_sh snippet, and added delete-all-but-.git command in comment at 
non_django_files/wordpress_posts/templates/05_login_remember_me.html 
e68a30a Part four final, moved post output folder into wordpress_posts. 
non_django_files/wordpress_posts/templates/05_login_remember_me.html 
3c5e4ec Part two final. Corrections/minor changes to all posts. 
non_django_files/wordpress_posts/templates/05_login_remember_me.html 
3a7dac9 Finished part one. 
non_django_files/wordpress_posts/templates/05_login_remember_me.html 
f87540e Initial commit 
non_django_files/wordpress_posts/templates/05_login_remember_me.html 
Verwandte Themen