1

I'm doing several refactors on a project. So sometimes I need to rename the file extension, for example index.js -> index.ts, and change many lines on this file.

Because that git wrongly mark index.js as deleted and index.ts as a new file, that could be bad on code review and logs.

Is there some way to explicitly say to git "hey, I renamed index.js to index.ts"?

Edit

The approach using git add / git rm / git mv didn't work, because git still say that I created a new file and removed other.

Also I should not use two commits to that (one for rename and one for update its content), because the first commit will be broken, since the build steps for a .js file is different than a .ts file

macabeus
  • 4,156
  • 5
  • 37
  • 66
  • Yes. Git move. --- How much of the contents of the file did you change? – evolutionxbox Feb 14 '20 at 16:09
  • @evolutionxbox I tried to use this command, but I'm receiving an error: `fatal: bad source, source=front/src/index.jsx, destination=front/src/index.tsx` – macabeus Feb 14 '20 at 16:12
  • And I changed many lines on this file, the enough to git can't autodetect that I just renamed this file. – macabeus Feb 14 '20 at 16:12
  • @MarekR Nop, because git can't detect automatically that, because I changed many lines – macabeus Feb 14 '20 at 16:13
  • 1
    Basically `Do the move and the modify in separate commits.` and you done. – Marek R Feb 14 '20 at 16:13
  • @Macabeus read the answers to linked question more carefully. – Marek R Feb 14 '20 at 16:15
  • @MarekR The approach using `git add / git rm / git mv` didn't work, because git still say that I created a new file and removed other. If I use two commits to that (one for rename and one for update its content), the first commit will be broken, since the build steps for a `.js` file is different than a `.ts` file – macabeus Feb 14 '20 at 16:31

3 Answers3

9

Is there some way to explicitly say to git "hey, I renamed index.js to index.ts"?

No.

That's all there is to it. Git does not store changes to files. Git stores snapshots of files. You had, in snapshot X, a file named index.js. You have, in snapshot Y, a file named index.ts.

When comparing snapshot X to snapshot Y, you can ask Git to detect renames. The detection is based on two facts:

  1. In snapshot X, there was a file named index.js and in snapshot Y, that file does not exist, but a new file named index.ts does exist; so these two names can be put into a list of candidates for rename detection.

  2. Now that the list of candidates for rename detection is filled with all file names that vanished from the left (X) commit and appeared in the right (Y) commit, Git will compare each pair of files. If the content of those two files is sufficiently similar, Git will temporarily pair-up that pair, remembering this similarity index value. Having run through all possible pairings, Git will take the pairing that has the best similarity index, and call that file "renamed".

Whenever you have Git compare snapshots X and Y—for any commit hashes X and Y, really—you get to tell Git:

  1. Do, or do not, use the rename detector;
  2. If using the rename detector, what's the minimum similarity threshold to temporarily pair-up files?

With the git diff command, the rename detector defaults to off before Git 2.9 and on in Git 2.9 and later. The similarity threshold value defaults to "50% similar" in all versions of Git. Use the -M flag to turn the detector on and set the similarity threshold.

When using other Git commands, other flags and arguments may allow you to enable rename detection and set the threshold—or, in some cases, not so much. For instance, git log --follow turns on the rename detector but limits it to one file name only and does not let you set the threshold.

The git status command used to always enable rename detection and set the threshold to 50%. It now obeys git config settings for enabling or disabling rename detection, but still has no way to set the threshold.

Again, this rename detection occurs when you compare the two commits. The commits themselves are just snapshots, of all of your files. Two different git diff commands, with different rename detection setups, will show different sets of operations that result in replacing the first commit with the second one. The git diff command does not show what someone actually did. It shows, instead, some sequence of operations that will produce the same result.

torek
  • 448,244
  • 59
  • 642
  • 775
  • Thank you. I'm a little unhappy because that. Maybe would be useful to be able to add on snapshot "hey, I renamed this file, ignore the threshold on this case". – macabeus Feb 14 '20 at 18:26
  • @Macabeus: That would help in the parent/child crossing, but wouldn't do any good when comparing a grandparent with a grandchild (Git doesn't look at the intervening commits). In any case there's no place to put such things at the moment, and no plans to add one that I've seen. – torek Feb 14 '20 at 20:07
  • I sent an email to git's mailing list saying this suggestion: https://lore.kernel.org/git/CANnkH-ViK9qySZGi=xbcE4YDiskhLxDsH21DMxTEHi6=X0EZuQ@mail.gmail.com/ – macabeus Feb 14 '20 at 21:26
1

No, there is no way in git to explicitly mark a file as moved.

git mv ... will do the same as git rm ...; git add ....

Git will automatically detect a remove and add in the same commit if the changes to the file are not too great. You can specify a "similarity index" with the -M option to certain commands git log. Setting this lower, e.g. -M30% means that only 30% of the lines in the old and new files have to match for the file to be considered a rename.

This is just one of the features of git to get used to. See:

How to make git mark a deleted and a new file as a file move?

How to REALLY show logs of renamed files with git? etc.

rjmunro
  • 27,203
  • 20
  • 110
  • 132
0

Git will usually notice if you moved the file, if you didn't muck around with the internals of the file too much. Either way, this is a bad practice and you should endeavor to always explicity move the file with git itself.

git mv index.js index.ts

Typically I strongly prefer that the move and file alterations be done as separate commits.

If the damage is already done in that regard... it may be helpful to copy out the file you've changed, and revert the repo back. Then you can rename the file, and copy the alterations on top of it.

Nam G VU
  • 33,193
  • 69
  • 233
  • 372
Jacobm001
  • 4,431
  • 4
  • 30
  • 51
  • Well... I can't have just a commit renaming `index.js` to `index.ts`, because this new file can't be build – macabeus Feb 14 '20 at 16:19
  • 1
    @Macabeus: If your limitation on what you can and cannot commit to version control is what can be built, then you're using version control wrong. – Jacobm001 Feb 14 '20 at 16:21
  • @Macabeus: I understand that may be the case with a protected master branch, but this is the kind of work branches are for. – Jacobm001 Feb 14 '20 at 16:21
  • No, I'm not using `master` branch. I'm using another branch and I could rewrite its history. Anyway, I tried to create a new commit just renaming the file and amending the changes, but it didn't work. The commit say "`index.js` removed, and `index.ts` added" – macabeus Feb 14 '20 at 16:23