Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Git - Mark file as new instead of moved/copied

Tags:

git

Is there any way to force git to consider a file as new instead of moved/copied?

Use case:

  1. I have a large file, index.js
  2. I'm factoring out a small class from that file into Helper.js (only 10% of the index.js) and renaming index.js to MyLib.js. MyLib.js will have some minor changes related to importing symbols from Helper.js.
  3. I need to recreate index.js as a new, 2-line file, that only re-exports symbols from Helper.js and MyLib.js.

I want the commit history to record renaming index.js to MyLib.js and treat the 2-line index.js as new, but git instead treats MyLib.js as completely new, and index.js as having lost 99% of its contents, down to only those 2 lines.

like image 382
notGeek Avatar asked Mar 09 '17 16:03

notGeek


People also ask

Does git track renamed files?

Git keeps track of changes to files in the working directory of a repository by their name. When you move or rename a file, Git doesn't see that a file was moved; it sees that there's a file with a new filename, and the file with the old filename was deleted (even if the contents remain the same).

Does git move keep history?

Moving and renaming files in version control systems rather than deleting and re-creating them is done to preserve their history. For example, when a file has been moved into a new directory, you'll still be interested in the previous versions of the file before it was moved.

How do I remove unchanged files in git?

Remove every file from Git's index. git rm --cached -r .


1 Answers

The short answer is : no, git does not allow you to store information about how files were moved.

git tracks content, not diffs.

When git displays the information :

$ git diff --name-status HEAD^ HEAD
M    fileA          # fileA has been modified
R    oldB -> fileB  # fileB has been renamed
A    fileC          # fileC has been created

it actually has computed this information by comparing the contents of the two commits. It has not stored the information : "actually fileC was copied from fileA, and fileA was re-created as a new file".

If two files have the same name in both commit, git diff will always compute "this file has been modified".


Option 1 : keep the history you have, and live with it.

Option 2 : you could try to do it in two commits

  • create a first commit, where the only action is renaming file index.js to MyLib.js,

    • if you need the code to "work" -- for example so that unit tests or integration tests can be run on this commit -- update other modules so that they import MyLib.js instead of index.js
  • create a second commit, where you apply the modifications you actually want to see

    • extract a small class from MyLib.js to Helper.js,
    • create a new index.js file with two lines and exported symbols,
    • if you had modified the imports in the first commit, modify them again in this second commit.

With option 2, some git commands (git rebase, or git log --follow for example) would detect the renaming step in the repo's history, because they always inspect the history one commit at a time.

Some other commands, that do not look at a commit per commit diff but at a "global" diff, would still behave as option 1.

For example : if you open a Merge Request (think github, gitlab, Azure Devops ...), the Merge Request interface would still present you with :

  • file index.js has been modified,
  • file MyLib.js is an entirely new file
like image 87
LeGEC Avatar answered Oct 10 '22 20:10

LeGEC