84

I have two repositories, one is the main repo for a library, and the other is a project using that library.

If I make a fix to the in the subservient project, I'd like an easy way to apply that patch back upstream.

The file's location is different in each repository.

  • Main repo: www.playdar.org/static/playdar.js
  • Project: playlick.com/lib/playdar.js

I tried using git format-patch -- lib/playdar.js on the playlick project, and then git am on the main playdar repo, but the differing file locations in the patch file raised an error.

Is there an easy way to apply the patch from a given commit on a given file to another arbitrary file elsewhere?

For bonus points, what if the file you want to apply the patch to isn't in a git repository?

CharlesB
  • 75,315
  • 26
  • 174
  • 199
James Wheare
  • 4,379
  • 1
  • 22
  • 22
  • similar: question: http://stackoverflow.com/questions/3367254/how-can-i-modify-the-file-path-in-a-set-of-git-patches – koppor Apr 23 '12 at 19:00

7 Answers7

122

If manually editing the patch file is out of the question or infeasible, this can be done with standard options (available in git apply, git format-patch and GNU patch).

  1. -p<n> removes n leading directories from the paths in the patch.

  2. After processing -p, --directory=<root> prepends root to each of the paths in the patch before applying.

Example

So, for your example, to take a patch that was originally on static/playdar.js and apply it to lib/playdar.js, you would run:

$ cat patch_file | git am     \ 
          -p1                 \ # remove 1 leading directory ('static/')
         --directory='lib/'     # prepend 'lib/'
vergenzt
  • 8,013
  • 4
  • 29
  • 42
  • 1
    Any chance making this top answer? This is far easier than manually editing a patch file. – weston Oct 13 '17 at 16:07
  • Sure, this is a better/easier answer, though @araqnid's answer is still good to know. – James Wheare Nov 07 '17 at 12:36
  • Relevant to adjusting the directory after first trying without `--directory`: https://stackoverflow.com/questions/24121709/git-error-previous-rebase-directory-git-rebase-apply-still-exists-but-mbox-giv – Ioannis Filippidis May 18 '18 at 02:42
38

The patch produced by git format-patch is simply a text file-- you can edit the diff headers so that it modifies a different path.

So for instance it would have produced something like this:

diff --git a/lib/playdar.js b/lib/playdar.js
index 1234567..89abcde
-- a/lib/playdar.js
++ b/lib/playdar.js

All you have to do is change lib/playdar.js to static/playdar.js and then run the patch through git am"

The patch should be readable by the standard GNU patch utility for people who don't have git--- but don't run format-patch with the -M, -C etc. options to produce rename patches in that case, because the support for them isn't universal.

Ioannis Filippidis
  • 8,272
  • 8
  • 66
  • 96
araqnid
  • 108,587
  • 20
  • 147
  • 127
  • 1
    Revisiting this page later… This is a better answer to the question asked than the previous "winner" that suggested submodules. – James Wheare Sep 20 '10 at 12:59
4

Assuming both projects are git projects, it sounds like that submodules would be the perfect fit for you. This allows a git project dynamically link to another git project, essentially baking a git repo right inside another git repo, both having their own distinct lives.

In other words, add "main repo" as a submodule in "project". Whenever you commit/push new stuff in "main repo", you just git pull them back into "project".

Henrik Paul
  • 63,711
  • 30
  • 82
  • 93
  • Hmm, having read through the submodule docs, this doesn't sound like "an easy way" though it may well be the most robust. It looks like I'd have to create a submodule containing just the `playdar.js` file then include that in both other projects (I don't want everything else from `www.playdar.org` in the `playlick.com` project) I might just resort to manually editing the patch files for now to be honest. Or continue to copy paste between the two. Cheers. – James Wheare Jun 01 '09 at 14:08
  • Here's a clear and thorough tutorial and getting started with git submodule for anyone else stumbling upon this question: http://book.git-scm.com/5_submodules.html – James Wheare Jun 01 '09 at 14:10
2

Using the --relative option to format-patch can improve the abstraction (hide irrelevant details about the repository from which the patch was generated).

[repository-with-changes]
git format-patch --relative=(path-to-library) (base-commit-for-patch) ## 'HEAD~1'

I have found the --3way option to be required when applying the patch (to avoid does not exist in index error) -- your mileage may vary. Using --directory=(...) is likely only necessary if your target path is not the root of the repository.

[repository-to-update]
git am --3way --directory=(path-to-library) (patch-file)

  • format-patch will create one patch file per commit to the current branch since 'base'.

  • The documentation for the --relative option seems to be missing in some cases, but it appears to work anyway (as of version 2.7.4).

Brent Bradburn
  • 40,766
  • 12
  • 126
  • 136
2

To complete Henrik's answer, and to go for the bonus point

what if the file you want to apply the patch to isn't in a git repository?

If you have access to the directories of the file candidate for a patch coming from a git repository, you could first transform that tree of directories/files into a git repository itself! ('git init': a git repository is just a .git within a root directory after all).
Then you would set that repo as a submodule for your main project.

Community
  • 1
  • 1
VonC
  • 1,042,979
  • 435
  • 3,649
  • 4,283
1

You can add a new remote and pull from it. Article with details.

$ cd <path-to-repoB>
$ git remote add repoA <git-URL-for-repoA>
$ git pull repoA
Der_Meister
  • 4,413
  • 2
  • 38
  • 47
1

You can just remove (rename) temporarily the main repository.

cd to/main/project
mv .git .git_
cd to/sub/project
git apply patchname
cd -
mv .git_ .git
ya.teck
  • 1,443
  • 21
  • 30