Bash 函数查找所有 Git 提交,其中文件(其名称与正则表达式匹配)已*更改*
Bash function to find all Git commits in which a file (whose name matches a regex) has *changed*
我有 ,它搜索存储库中文件名与正则表达式匹配的所有文件。它当前查找所有提交,其中文件 存在 。如何更改它以便它只在每次提交中编辑(创建、更改或删除)的文件中搜索?
这是我做这个功能的初衷。我很惊讶地看到结果比预期的要广泛得多。我尝试这样做的原因:很久以前我创建了一个文件,并且在现在和那时之间的某个时候,我不小心从中删除了一个重要部分。我想要一个包含此文件已更改的所有点(提交)的列表,以便我可以快速返回到包含缺失部分的版本,并将其粘贴回当前提交版本。
:<<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://whosebug.com/users/459089/handyman5
which is based on this SO question:
-
The main section of this function was authored by Stack Overflow user
SwankSwashbucklers.
- https://whosebug.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 [searchterm]: Searches the current git repository for the file name that matches a regular expression.\n"
正文:
gitf() {
#Exit if no parameter is provided (if it's the empty string)
param=$(echo "" | 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
}
使用git diff-tree -r --name-only --no-commit-id
(可能使用--stdin
)而不是git ls-tree -r --name-only
。如果您对合并感兴趣,请使用 -m
或 -c
,如果您想分别考虑重命名和复制检测,请使用 -M
或 -C
。
或更好地解析 git diff-tree -r
.
的输出
铌。给出的问题代码严重次优(除其他外,您多次检查相同的提交)。
如果您可以接受 shell glob 模式而不是成熟的正则表达式,请考虑
git log -p --diff-filter=AMD --branches --tags -- "foo*bar.sh"
使用 -p
,您会看到增量以及提交消息、作者、SHA1 等。--diff-filter=AMD
选项仅选择那些有问题的文件的提交 A添加,M修改,或D删除。要搜索远程以及本地分支和标签,请使用 --all
而不是 --branches --tags
。最后,请注意引入路径模式的 --
,您需要引用它以允许 git 执行 glob 匹配。
您可以浏览并使用 git diff
来查看每次提交之间发生的变化。像这样:
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
我想出了这个功能,它是基于Greg Bacon's answer。我最初想要正则表达式,但 globs 很适合我。我还预计需要一个循环函数,但只需要 git log
行即可。
首先,一个效用函数:
#https://whosebug.com/questions/369758/how-to-trim-whitespace-from-bash-variable#comment21953456_3232433
alias trim="sed -e 's/^[[:space:]]*//g' -e 's/[[:space:]]*$//g'"
文档 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://whosebug.com/questions/28119379/bash-function-to-find-all-git-commits-in-which-a-file-whose-name-matches-a-rege/28120305
- https://whosebug.com/questions/28094136/bash-function-to-search-git-repository-for-a-filename-that-matches-regex/28095750
- https://whosebug.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://whosebug.com/a/28119940/2736496
by Stack Overflow user Greg Bacon
- https://whosebug.com/users/123109/greg-bacon
With thanks to SwankSwashbucklers
- https://whosebug.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"
Body:
gitf() {
#Exit if no parameter is provided (if it's the empty string)
param=$(echo "" | trim)
echo "$param"
if [ -z "$param" ] #http://tldp.org/LDP/abs/html/comparison-ops.html
then
echo "Required parameter missing. Cancelled"; return
fi
#https://whosebug.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"
}
示例输出:
$ 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
我有
这是我做这个功能的初衷。我很惊讶地看到结果比预期的要广泛得多。我尝试这样做的原因:很久以前我创建了一个文件,并且在现在和那时之间的某个时候,我不小心从中删除了一个重要部分。我想要一个包含此文件已更改的所有点(提交)的列表,以便我可以快速返回到包含缺失部分的版本,并将其粘贴回当前提交版本。
:<<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://whosebug.com/users/459089/handyman5
which is based on this SO question:
-
The main section of this function was authored by Stack Overflow user
SwankSwashbucklers.
- https://whosebug.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 [searchterm]: Searches the current git repository for the file name that matches a regular expression.\n"
正文:
gitf() {
#Exit if no parameter is provided (if it's the empty string)
param=$(echo "" | 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
}
使用git diff-tree -r --name-only --no-commit-id
(可能使用--stdin
)而不是git ls-tree -r --name-only
。如果您对合并感兴趣,请使用 -m
或 -c
,如果您想分别考虑重命名和复制检测,请使用 -M
或 -C
。
或更好地解析 git diff-tree -r
.
铌。给出的问题代码严重次优(除其他外,您多次检查相同的提交)。
如果您可以接受 shell glob 模式而不是成熟的正则表达式,请考虑
git log -p --diff-filter=AMD --branches --tags -- "foo*bar.sh"
使用 -p
,您会看到增量以及提交消息、作者、SHA1 等。--diff-filter=AMD
选项仅选择那些有问题的文件的提交 A添加,M修改,或D删除。要搜索远程以及本地分支和标签,请使用 --all
而不是 --branches --tags
。最后,请注意引入路径模式的 --
,您需要引用它以允许 git 执行 glob 匹配。
您可以浏览并使用 git diff
来查看每次提交之间发生的变化。像这样:
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
我想出了这个功能,它是基于Greg Bacon's answer。我最初想要正则表达式,但 globs 很适合我。我还预计需要一个循环函数,但只需要 git log
行即可。
首先,一个效用函数:
#https://whosebug.com/questions/369758/how-to-trim-whitespace-from-bash-variable#comment21953456_3232433
alias trim="sed -e 's/^[[:space:]]*//g' -e 's/[[:space:]]*$//g'"
文档 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://whosebug.com/questions/28119379/bash-function-to-find-all-git-commits-in-which-a-file-whose-name-matches-a-rege/28120305
- https://whosebug.com/questions/28094136/bash-function-to-search-git-repository-for-a-filename-that-matches-regex/28095750
- https://whosebug.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://whosebug.com/a/28119940/2736496
by Stack Overflow user Greg Bacon
- https://whosebug.com/users/123109/greg-bacon
With thanks to SwankSwashbucklers
- https://whosebug.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"
Body:
gitf() {
#Exit if no parameter is provided (if it's the empty string)
param=$(echo "" | trim)
echo "$param"
if [ -z "$param" ] #http://tldp.org/LDP/abs/html/comparison-ops.html
then
echo "Required parameter missing. Cancelled"; return
fi
#https://whosebug.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"
}
示例输出:
$ 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