简体   繁体   中英

git edit authors name in commit range, script

I would like to create a git script which replaces author "A" with author "B" in a specified commit range (abcd..dcba). How can I do that? Here is what I have so far:

#!/bin/sh
#
# git-myCommand
#
#
git filter-branch --commit-filter '
    if  [ git rev-list --boundary 02e60c8..dc4c65f ]; then
        if [ "$GIT_AUTHOR_NAME" = "A" ];then
            GIT_AUTHOR_NAME="B";
        fi;
    fi;
git commit-tree "$@"'

problem is that git says

Rewrite $SHA1-Number git commit-tree: line 48: [: too many arguments

Is the git rev-list --boundary command even legal in an if statement? if i remove the if [git rev-list ... ] statement it'll work.

Ideal would be if I can add input variables. So the command would look like

git myCommand $AuthorExpression $AuthorReplace $CommitA..$CommitB

Edit: Some thinks that this question has already been asked. for those please read the problem above again and compare it wit the previously asked questions. AFAIK THIS question (test if commits are between commit A and commit B) has not been asked yet. If i'm wrong, sorry for wasting your time. Thanks to those who have tried to answer that question!

If you want to use parameters for the script, simply do

#!/bin/sh
#
# git-myCommand
#
#
AuthorExpression=$1
AuthorReplace=$2
CommitA=$3
CommitB=$4
git filter-branch --commit-filter "
    if git rev-list --boundary '$CommitA'..'$CommitB'; then
        if [ \"\$GIT_AUTHOR_NAME\" = '$AuthorExpression' ];then
            GIT_AUTHOR_NAME='$AuthorReplace';
        fi;
    fi;
git commit-tree \"\$@\""

However, note that you will probably want to edit GIT_COMMITTER_NAME , GIT_COMMITTER_EMAIL and GIT_AUTHOR_EMAIL as well. See this answer .

You can substitute variables from your shell script into the script that you pass in to git filter-branch . You just need to be careful to get the quoting right. Since you're in single quotes, you can't substitute the variable directly, but you can end the single quotes and open a double quoted string for the substitution, then close the double quoted string and open the single quoted string back up again; the strings will be appended after expansion. And within the single quoted string, you should include quotes that will surround the expanded string from the variable, in case it contains spaces or other characters.

Rather than trying to limit the revisions within the filter script, it's better to just pass the revisions in to git filter-branch so that it will only ever do anything to those commits. git filter-branch takes the same arguments as git rev-list at the end of the command (though you have to delimit them with a -- in order for it to interpret --boundary as an rev-list argument).

#!/bin/sh
#
# git-myCommand
#
#
AuthorExpression=$1
AuthorReplace=$2
CommitA=$3
CommitB=$4
git filter-branch --commit-filter '
    if [ "$GIT_AUTHOR_NAME" = "'"$AuthorExpression"'" ];then
        GIT_AUTHOR_NAME="'"$AuthorReplace"'";
    fi;
git commit-tree "$@"' -- --boundary "$CommitA".."$CommitB"

edit : Missed the problem with git rev-list when first answering.

Others have addressed parameter quoting. The big remaining problem is the test:

if [ git-rev-list ... ]

The commit filter is just a shell script fragment, so this tells the shell to run the command:

[ git-rev-list ... ]

and test whether the exit status of that command is zero (true) or not (false). The [ command, also known as test , does not have any git built-ins.

What you seem to want is to tell whether the original commit being filtered is a member of the range A .. B inclusive. Let's first note this, from the filter-branch documentation:

The filters are applied in the order as listed below. The <command> argument is always evaluated in the shell context using the eval command (with the notable exception of the commit filter, for technical reasons). Prior to that, the $GIT_COMMIT environment variable will be set to contain the id of the commit being rewritten.

Thus, you could write something like this:

A=$(git rev-parse $3) || exit 1
B=$(git rev-parse $4) || exit 1
git filter-branch --commit-filter "
    if git merge-base --is-ancestor \$GIT_COMMIT $B &&
       ! git merge-base --is-ancestor \$GIT_COMMIT $A^; then

The first merge-base tests whether $GIT_COMMIT is an ancestor of the higher rev (is equal to $B or comes "before" it topologically). The second excludes commits that come before the lower rev ( $A ), using the hat-suffix to avoid excluding $A itself.

Here's a variant of Brian Campbell's script using this technique (well, I also changed the quote expansion technique too).

#! /bin/sh
case $# in
4) ;;
*) echo "usage: $0 oldauthor newauthor firstrev lastrev" >&2; exit 1;;
esac

AuthorExpression=$1
AuthorReplace=$2
A=$(git rev-parse --verify $3^{commit}) || exit 1
B=$(git rev-parse --verify $4^{commit}) || exit 1

git filter-branch --commit-filter "
    if git merge-base --is-ancestor \$GIT_COMMIT $B &&
       ! git merge-base --is-ancestor \$GIT_COMMIT $A^; then
       if [ \"\$GIT_AUTHOR_NAME\" = '$AuthorExpression' ]; then
       GIT_AUTHOR_NAME='$AuthorReplace'
       fi
    fi
git commit-tree \"\$@\""

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM