Asbjørn Ulsberg
Asbjørn Ulsberg

Reputation: 8820

Create Git patches for two files across several renames

I want to move two files from one repository to another. The files were originally added as:

  1. /src/init/Price.cs
  2. /tests/init/PriceTests.cs

The two files were later renamed to:

  1. /src/init/PriceValue.cs
  2. /tests/init/PriceValueTests.cs

And then moved to:

  1. /src/moved/PriceValue.cs
  2. /tests/moved/PriceValueTests.cs

I've tried to go by this description to create a set of patches for these files, but I'm unsure how to pass in the six different paths the files have existed on.

I've managed to find all the commit IDs affecting PriceValue.cs (across renames and moves), but passing those IDs to Git fails with the following error message:

$ git format-patch -o /tmp/pricevaluepatches $(git log --all dfeeb 6966b 9f882 …)
-bash: /usr/local/bin/git: Argument list too long

So, how do I create a set of patches for this that only contains the changes to the mentioned files, but contains it across one rename and one move of each file?

Upvotes: 8

Views: 847

Answers (3)

Jan Zerebecki
Jan Zerebecki

Reputation: 865

To achieve the goal from the question via a merge (instead of individual patch moving via format-patch as the question asked) one can remove all other files in a new commit and then merge that commit across repositories into the target repository (adapted from https://stackoverflow.com/a/10548919/7496656 ):

cd path/to/project-a
git checkout -b for-b master # or whichever branch you want to merge from
git rm -r .
git checkout HEAD -- src/moved/PriceValue.cs tests/moved/PriceValueTests.cs
git commit
cd path/to/project-b
git remote add project-a path/to/project-a
git fetch project-a
git merge --allow-unrelated-histories project-a/for-b
git remote remove project-a
cd path/to/project-a
git branch -D for-b

This has the advantage that all the history is there, the commit IDs stay the same and there is no need to juggle individual commits nor find them. This may have the disadvantage that a linearised view (like git log) as opposed to the graph view (like gitk) probably becomes more confusing as the number of unrelated commits gets greater.

One could additionally filter repository project-a before the merge, to hide unrelated files or commits. However the downside of that are: If you do this across a repository merge more than once, possibly also in the other direction, it makes the history less clean as the common history only occurs multiple times (once for each merge). This would also make this solution more difficult than your initially tried one. This would also have the downside that the commit IDs don't stay the same and thus it would not be as easy to find which were the same commit across repositories.

Upvotes: 1

Nils Werner
Nils Werner

Reputation: 36859

You can get the patches of a few specific files but not earlier than commit sha1 using

git format-patch sha1 -- file1 file2 ...

Any of the files can be an old file (prior to a rename) or an existing file.

If you want all commits until commit sha1 you can use

git format-patch --root sha1 -- file1 file2 ...

So in your case, all commits until now (HEAD) of your six files:

git format-patch --root HEAD -- /src/init/Price.cs /src/init/PriceValue.cs /src/moved/PriceValue.cs /src/init/PriceTests.cs /src/init/PriceValueTests.cs /src/moved/PriceValueTests.cs

Upvotes: 6

eftshift0
eftshift0

Reputation: 30327

Hmm..... Assuming I want to keep the patch files the way they are, what I would do is get the patch file applied on a branch so that I can then cherry-pick it on the right branch.

So, suppose I have on my master branch a file named /tests/moved/PriceValueTests.cs and I want to apply on it a patch that has the name /tests/init/PriceTests.cs instead. Assuming I don't want to hack the patch file, what I would do is:

  • create a temporary branch from my master
  • checkout temp branch
  • rename the file to the same path that the patch file has (and commit, of course)
  • apply patch file on temporary branch (should work now that the file path has a matching file)
  • commit on temporary branch
  • checkout master
  • cherry-pick last revision from temporary branch

This is so that git can track the name change and be able to apply it successfully. I've done it a number of times and git's file rename algorithm tends to get it right.

Upvotes: 1

Related Questions