How can I make git am / git apply work "fuzzy" like the patch command

Viewed 11978

I've been using git-format-patch and git-am to apply changes from one repository to another. The file structures are the same but there are some changes in the repository I'm apply to which cause most patches to fail a few hunks. But most of the patch hunks apply with a little but of fuzzyness in the line numbers.

As far as I can tell git-am apply a very strict interpretation so rejects all these patches outright.

So my workflow has become

$ git am ../the-patch.patch
# Fails because the patch doesn't apply cleanly
$ patch -p1 < ../the-patch.patch
# Applies most of the hunks, leaves .rej files for the ones that conflict
# Fix the conflicting hunks manually
$ git am --continue

It would be nice if I didn't have to run the command line patch and could just have that happen as part of the am command.

Running with the --reject flag seems to create a .rej file with all the hunks in the file if any conflict, which isn't what I want.

Running with the --3way flag fails with

fatal: sha1 information is lacking or useless (the-file.java).
Repository lacks necessary blobs to fall back on 3-way merge.
Cannot fall back to three-way merge.

Which I presume is because the change set this was based on is not in the repository I'm merging to.

Is there any way to make git-am apply the patch with fuzzy matching like the raw patch command does and only create .rej files containing the hunks that failed?

6 Answers

You can do the following:

git am /path/to/some.patch
patch -p1 < /path/to/some.patch
git add .
git am --continue

That would apply the patch and keep the commit message, etc.

How about:

git_apply-fuzzy() {
   if ! git am "$@"; then
      local patch="$(git rev-parse --show-toplevel)/.git/rebase-apply/patch"
      if [ -e "$patch" ] && patch --dry-run -p1 < "$patch"; then
          if patch -p1 < "$patch"; then
              sed -n -e '/^+++ b\//{s///;p}' "$patch" | xargs git add &&
              git am --continue &&
              return 0 ||
              patch -p1 -R < "$patch"
          fi
      fi
      git am --abort
      return 1
   fi
}
git_apply-fuzzy mypatch.patch

It tries 'git am' and, if it fails, try patch (reverting if anything strange happens)

There is --recount parameter, but only for git-apply command, so theoretical sequence would be:

git mailsplit ...   
git mailinfo ...
git apply --recount ...
git add ...
git commit ...
Related