How do I git attach a patch to a previous revision?

Suppose I have a branch with the following history:

 A - B - C - D

Between B and C, I modified a bunch of files, including a specific file, name it foo.txt.

Then, in the D edition, I changed the same file, foo.txt.

Meanwhile, my friend took a picture of my directory in B and decided that he would somehow modify foo.txt. Let me call this revision E. If they were part of the same repository, they would look like this:

 A - B - C - D
      \
       E

However, they are not, we just have ABCD in my repository, and then it sends me a patch for the differences between B and E.

Since I mixed up with foo.txt, I can’t just apply the patch directly to D, the context for diff will not match, it expects me to be on or around B.

It seems like I would like to create a hypothetical repository tree that I mentioned above, and then merge between D and E in this repository in order to replay my C and D changes together in the correct order.

So my questions are:

  • Is this the most sensible way to apply changes based on historical “baselines”?
  • Are there any flaws that I do not see?
  • How to execute this in git cleanly?
+7
git
source share
2 answers

You are right that you want to take advantage of the git merge faculties. There are a few more options. The most indicative of what actually happened is probably a merge, but the method leading to a modified version of E applied on top of D is also not so bad. Tell us how to do it!

If the patch contains the blob to which it belongs (the output from git format-patch does), then git am is able to perform a three-way merge! The diff blob SHA1 entries look like this:

 diff --git a/foo.txt b/foo.txt index ca1df77..2c98844 100644 

If you have it, you're in luck. Just use git am --3way <patch> . Unfortunately, git am requires an email-style header that creates a format patch, so if the patch came from git diff instead of git format-patch , you need to get a little confused. You can add it yourself:

 From: Bobby Tables <bobby@drop.org> Date: 2 Nov 2010 Subject: [PATCH] protect against injection attack <original diff> 

git am should work with this, and if you don’t get everything exactly how you wanted it, you can always git commit --amend fix it. (You can also try using git apply --build-fake-ancestor=foo.txt <patch> , but that really doesn't work in a friendly way. I think git am trickster is easier.)

If the patch does not contain any bla SHA1 (i.e. it was created using the diff command, not the git command), tell your friend again how to use git, and I'm sure you can still put it off. You know which version of foo.txt the patch should use! To get SHA1 from commit B, use:

 git ls-tree <SHA1 of B>:<directory containing foo.txt> 

This will be one of the given drops. (I know there is a direct path, but I just can't think about it right now.) Then you can add a fake git diff header. Suppose its hash is abcdef12:

 diff --git a/foo.txt b/foo.txt index abcdef12.. 

Git doesn't really need anything except the first hash; although git diff output would have a final hash and mode, am does not look for it, so you can leave by leaving it. (And yes, I just checked it. I haven’t done this before!)

This will lead to a story like A - B - C - D - E' , where E' is your patch for a friend, but applies to D ; this is the result of a three-way merge between the contents in D , B and your friend's patch.

Finally, if you do not want to cheat any of this, you can do as you said:

 git checkout -b bobby <SHA1 of B> # apply the patch git commit --author="Bobby Tables <bobby@drop.org>" git checkout master git merge bobby # or `git cherry-pick bobby` to grab the single commit and apply to master # or `git rebase bobby master` to rebase C and D onto B 
+6
source share

I looked around - I swear there should be a way to do this automatically in git, but at the moment it's best to do what you said.

Assuming your commit in B has identifier 12345... :

 $ git checkout 12345 Note: checking out '12345....'. You are in 'detached HEAD' state. [...] $ git checkout -b friends_change $ git apply < patchfile.patch $ git status [... something interesting ...] $ git commit -m "Applying friend patches to foo" $ git checkout master $ git merge friends_change 

Of course, you can reinstall instead if you haven't published the story to anyone:

 $ git rebase friends_change 

This will create a tree like this:

 A - B - C - D (old master) \ E - C' - D' (new master) 
+1
source share

All Articles