How to apply a Git patch to a file with a different name and path?
Asked Answered
C

5

147

I have two repositories. In one, I make changes to file ./hello.test. I commit the changes and create a patch from that commit with git format-patch -1 HEAD. Now, I have a second repository that contains a file that has the same contents as hello.test but is placed in a different directory under a different name: ./blue/red/hi.test. How do I go about applying the aforementioned patch to the hi.test file? I tried git am --directory='blue/red' < patch_file but that of course complains that the files are not named the same (which I thought Git didn't care about?). I know I could probably edit the diff to apply to that specific file but I'm looking for a command solution.

Counterpressure answered 13/5, 2013 at 15:58 Comment(2)
Related to: https://mcmap.net/q/49056/-how-can-i-modify-the-file-path-in-a-set-of-git-patches/1959808Hypocoristic
Meld is absolutely fantastic for stuff like that...Hofmann
S
148

You could create the patch using git diff and then apply it using the patch utility, which allows you to specify the file you want to apply the diff to.

For example:

cd first-repo
git diff HEAD^ -- hello.test > ~/patch_file

cd ../second-repo
patch -p1 blue/red/hi.test ~/patch_file
Sargeant answered 13/5, 2013 at 16:38 Comment(5)
Ah, nice, did not think of that. However, is there any way to do this with Git commands so that commit data (date&time, commit author, commit message) is kept the same?Counterpressure
It's possible that there's something you can do with am or apply, but I can't find it. If you find yourself duplicating changes a lot, there might be a better solution using submodules, or whatever your language of choice provides to share code (e.g. in Ruby you could extract the duplicate code as a gem).Sargeant
This is actually documentation-related (source files are XMLs). Submodules are not really an option as I would have to make a strong case for them in our existing infrastructure.Counterpressure
On Windows, one can use git-bash, which is included in Git for Windows. git-bash includes several Unix commands, including patch.Zarathustra
Is the -p1 flag to patch necessary? It seems to have worked for me without it.Jacquelyn
R
95

There is a simple solution that does not involve manual patch editing nor external script.

In the first repository (this may also export a range of commit, add the -1 flag if you want to select only one commit) :

git format-patch --relative <committish> --stdout > ~/patch

In the second repository :

git am --directory blue/red/ ~/patch

Instead of using --relative in git format-patch, another solution is to use -p<n> option in git am to strip n directories from the path of the patches, as mentioned in a answer to a similar question.

It is also possible to run git format-patch --relative <committish> without the --stdout, and it will generate a set of .patch files. These files can then be fed directly to git am with git am --directory blue/red/ path/to/*.patch.

Raddled answered 27/4, 2015 at 6:36 Comment(9)
This still relies on the fact that the file names are the same, right?Counterpressure
Should be noted that the --directory option appears to require you to specify the full path of the directory relative to the repo root; something like --directory=./ while chdir'd into a subdirectory in the repo won't work.Stingaree
Using --3way helps with does not exist in index: git am --3way --directory (relative-path) (patch)Gasworks
Use -k key in both commands to don't strip the first line of the commit message.Hinojosa
Using --3way not only helps with "does not exist in index" errors (as pointed out by @nobar), but also allows you to cleanly handle merge conflicts. Instead of leaving conflicted files untouched, a conflict block is added that can then be resolved.Stoner
is there a way to to run git am --directory ./ *.patch so that the paths in the patch are applierd to the current directory ?Atheistic
Good answer. Do not forget the -p<n> if you need to strip parent folders, and --reject if you encounter "patch does not apply" (it will apply the hunks it can and leave you the rest for manual application).Gaullism
didn't work in Windows. But worked like a charm in WSL. LINUX RULEZZZVapor
Note that if both directories are cloned on the same machine, you can pipe the output of one command to the other one: git -C /path/to/first/repo/sub/dir format-patch --relative -1 --stdout <committish> | git am --directory other/dir -3Auditory
T
9

Building upon the answer by @georgebrock, here's a solution I used:

First, create the patch files as usual (eg. git format-patch commitA..commitB).

Then make sure that your target repository is clean (there should be no changed or untracked files) and apply the patches like this:

cd second-repo
git am ~/00*.patch

For every patch file you will get an error like "error: XYZ does not exist in index". You can now apply this patch file manually:

patch --directory blue/red < ~/0001-*.patch
git add -a
git am --continue

You have to do these three steps for each patch file.

This will preserve the original commit message etc. without requiring any special git format-patch command or editing the patch files.

Termitarium answered 2/10, 2018 at 11:52 Comment(2)
Good answer, I think this is the best foundation for any kind of "non-standard" patch manipulation. I do it in 3 steps. (1) Commit to text - git format-patch -1 commitA --stdout > thing.diff ; (2) Edit the patch file until it will do what I need; (3) Text to commit git am --3way thing.diff which has the advantage that you can accept the parts of the patch that apply cleanly, and use git's standard conflict resolution process for the parts that don't.Imbed
With current git versions, the current diff can be shown with git am --show-current-patch=diff, so the patch step can be changed to git am --show-current-patch=diff | patch --directory blue/red. This saves you from remembering which patch file to specify in each commit.Volotta
P
2

I understand the two files are exactly the same in your situation, thus the patch is likely to succeed.

However, in case you want to apply a patch to a similar, but not exactly the same file, or you want to do an interactive patching, you will use three way merge.

Say you modified File A, let's denote A~1 as the previous version, and you want to apply the diff between A~1 to A to File B.

Open a three way merge tool, for instance Beyond Compare, the path of left panel is A, middle panel is the common ancestor so the path is A~1, the path of right panel is B. Then, the lower panel shows the result of applying the diff between A~1 to A to File B.

The following figure illustrates the idea.

enter image description here

Participate answered 12/3, 2018 at 22:2 Comment(0)
T
0

FYI: I recently had problems trying to download a patch from Github and applying it to a local file (which was an "override" in a new location).

git am wouldn't apply the patch either because the file was "not in index" or "dirty." But, I found that the simple patch command could apply the patch. It did prompt me for the name of the file to be patched.

Got the job done, anyway ...

Tunicle answered 17/3, 2020 at 21:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.