Domanda

git filter-branch --env-filter '
export GIT_AUTHOR_EMAIL="foo@example.com"
export GIT_AUTHOR_NAME="foo"' -- commita..commitb

Results in Which ref do you want to rewrite?

So it seems that filter-branch doesn't allow you to use range notation use a range between two arbitrary refs.

What is the most straight forward way of running a filter over a range of consecutive commits (somewhere within the history of a branch) if this approach isn't possible.

È stato utile?

Soluzione 2

The cleanest solution I found was to do the following:

  1. Create a temporary branch at refb.
  2. Apply the branch filter on refa..temp.
  3. Rebase onto the temporary branch and delete it.

ie.

git branch temp refb

git filter-branch --env-filter '
export GIT_AUTHOR_EMAIL="foo@example.com"' refa..temp

git rebase temp
git branch --delete temp

Altri suggerimenti

You cannot apply the filter-branch in the middle of the history, as said by @kan. You must apply from your known commit to the end of the history

git filter-branch --env-filter '...' SHA1..HEAD

Filter-branch can check for the commit author or other information, to chose to change or not the commit, so there are ways to accomplish what you want, see https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History, look for "Changing Email Addresses Globally"

Remember: if you have pushed the commits to a public repository you should not user filter-branch

git filter-branch does accept range notation, but the end of the range needs to be a reference, not the ID of a commit.

git checkout -b tofilter commitb
git filter-branch .... commita..tofilter

If given just commits, it would not know what ref to update with the filtered branch.

Enclose you filter commands in an if-statement that checks for that range. You can check whether a commit is within a given range with this command:

git rev-list start..end | grep **fullsha**

The current commit will be stored in $GIT_COMMIT in your filter. So your filter becomes:

git filter-branch --env-filter '
  if git rev-list commita..commitb | grep $GIT_COMMIT; then
    export GIT_AUTHOR_EMAIL="foo@example.com"
    export GIT_AUTHOR_NAME="foo"
  fi' -- ^commita --all

If you want to only rewrite your current branch, replace --all with HEAD

You cannot just override commits in a middle of a history, because sha1 of a commit depends on a parent's. So, the git doesn't know where do you want point your HEAD reference after the filtration. So, you should rewrite all up to the HEAD.

Example:

A---B---C---D---E---F   master
            \
             \--G---H   branch

if you want filter commits B and C you should also filter all commits after: D, E, F, G, H. So, that's why git tells you to use a ref at the end of the range, so that it just not finishes up with a detached head.

After you modify B and C commits and stop will look like this:

A---B---C---D---E---F   master
\           \
 \           \--G---H   branch
  \-B'--C'      (HEAD or a temporary TAG?..)      

So, the master and branch will be untouched. I don' think this is that you want. It means you must override all commits. The history will be then:

A---B---C---D---E---F   (loose end, will be garbage collected one day)
\           \
 \           \--G---H   (loose end, will be garbage collected one day)
  \-B'--C'--D'--E'--F'  master
            \
             \--G'--H'  branch

I do it this way.

Let's say you want to filter the content of a branch called branch-you-are-filtering.

Assume that there's an ancestor commit to that branch, with a ref called ref-for-commit-to-stop-at.

git filter-branch --commit-filter 'YOUR_FILTER_COMMANDS' branch-you-are-filtering...ref-for-commit-to-stop-at

After executing, the commit at ref-for-commit-to-stop-at will not be altered. All the filtered\changed commits in branch branch-you-are-filtering will be based on ref-for-commit-to-stop-at.

Whether or not you're using --commit-filter or something else is up to you.

The solution from @Acron seems wrong to me. I would suggest following to change between refa and refb including both hashes:

  1. git tag master.bak
  2. git reset --hard refa
  3. git filter-branch --env-filter ' export GIT_AUTHOR_EMAIL="foo@example.com"' refa^..master
  4. git cherry-pick refb..master.bak
  5. git tag -d master.bak

Use filter-branch's --setup parm and some shell power:

git filter-branch --setup '
for id in `git rev-list commitA..commitB`; do
         eval filterfor_$id=rewrite
done
rewrite() {
        GIT_AUTHOR_NAME="Frederick. O. Oosball"
        GIT_AUTHOR_EMAIL=foobar@example.org
}
' --env-filter 'eval \$filterfor_$GIT_COMMIT'
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top