Record file copy operation with Git

GitFileCopy

Git Problem Overview


When I move a file in git using git-mv the status shows that the file has been renamed and even if I alter some portions it still considers to be almost the same thing (which is good because it lets me follow the history of it).

When I copy a file the original file has some history I'd like to associate with the new copy.

I have tried moving the file then trying to re-checkout in the original location - once moved git won't let me checkout the original location.

I have tried doing a filesystem copy and then adding the file - git lists it as a new file.

Is there any way to make git record a file copy operation in a similar way to how it records a file rename/move where the history can be traced back to the original file?

Git Solutions


Solution 1 - Git

Git does not do rename tracking nor copy tracking, which means it doesn't record renames or copies. What it does instead is rename and copy detection. You can request rename detection in git diff (and git show) by using the -M option, you can request additional copy detection in changed files by using the -C option instead, and you can request more expensive copy detection among all files with -C -C. See the git-diff manpage.

-C -C implies -C, and -C implies -M.

-M is a shortcut for --find-renames, -C means --find-copies and -C -C can also be spelled out as --find-copies-harder.

You can also configure git to always do rename detection by setting diff.renames to a boolean true value (e.g. true or 1), and you can request git to do copy detection too by setting it to copy or copies. See the git-config manpage.

Check also the -l option to git diff and the related config variable diff.renameLimit.


Note that git log <pathspec> works differently in Git: here <pathspec> is set of path delimiters, where path can be a (sub)directory name. It filters and simplifies history before rename and copy detection comes into play. If you want to follow renames and copies, use git log --follow <filename> (which currently is a bit limited, and works only for a single file).

Solution 2 - Git

If for some reason you cannot turn on copy detection as in Jakub Narębski's answer, you can force Git to detect the history of the copied file in three commits:

  • Instead of copying, switch to a new branch and move the file to its new location there.
  • Re-add the original file there.
  • Merge the new branch to the original branch with the no-fast-forward option --no-ff.

Credits to Raymond Chen. What follows is his procedure. Say the file is named orig, and you want the duplicate to be named apple:

git checkout -b dup # create and switch to branch

git mv orig apple # make the duplicate
git commit -m "duplicate orig to apple"

git checkout HEAD~ orig # bring back the original
git commit -m "restore orig"

git checkout - # switch back to source branch
git merge --no-ff dup # merge dup into source branch

2020-05-19: The above solution has the advantages of not changing the log of the original file, not creating a merge conflict, and being shorter. The former solution had four commits:

  • Instead of copying, switch to a new branch and move the file to its new location there.
  • Switch to the original branch and rename the file.
  • Merge the new branch into the original branch, resolving the trivial conflict by keeping both files.
  • Restore the original filename in a separate commit.

(Solution taken from https://stackoverflow.com/a/44036771/1389680.)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionHexdollView Question on Stackoverflow
Solution 1 - GitJakub NarębskiView Answer on Stackoverflow
Solution 2 - GitRobert PollakView Answer on Stackoverflow