{"thread":{"id":"65695","subject":"git mv after the fact","startedAt":"2026-05-26T13:06:19Z","lastAt":"2026-05-28T20:36:23Z","messageCount":10,"participants":["Frieder Hannenheim","Chris Torek","Ben Knoble","Junio C Hamano","Tim Tassonis"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"544109","messageId":"02663c67-01ad-4dd1-aae6-9e9706f3d040@fhannenheim.net","threadId":"65695","inReplyTo":null,"subject":"git mv after the fact","fromName":"Frieder Hannenheim","fromEmail":"mail@fhannenheim.net","sentAt":"2026-05-26T12:57:02Z","receivedAt":"2026-05-26T13:06:19Z","isPatch":false,"body":"Hi,\n\nI'd like to propose a new flag for git mv, that updates the index\nlike git mv normally would but does not move the file. This would come\nin handy when the file has already been moved, or is renamed by some\nexternal tool and the user wants to fix up the index afterwards.\n\nThank you for considering this.\n\nSincerely,\nFrieder Hannenheim\n\n"},{"id":"544112","messageId":"CAPx1Gvd9+z0th9whCbcA60_bWproPp+kwp3qDmhQOe4G=0=E6A@mail.gmail.com","threadId":"65695","inReplyTo":"02663c67-01ad-4dd1-aae6-9e9706f3d040@fhannenheim.net","subject":"Re: git mv after the fact","fromName":"Chris Torek","fromEmail":"chris.torek@gmail.com","sentAt":"2026-05-26T16:40:43Z","receivedAt":"2026-05-26T16:40:57Z","isPatch":false,"body":"On Tue, May 26, 2026 at 6:18 AM Frieder Hannenheim <mail@fhannenheim.net> wrote:\n> I'd like to propose a new flag for git mv, that updates the index\n> like git mv normally would but does not move the file. ...\n\nYou may already know this, but technically no flag is needed:\nyou can just \"git add\" the new name and \"git rm\" the old one,\nwith the same effect.\n\nA flag for \"git mv\" would be convenient (and slightly more\nefficient, not in terms of storage but in terms of CPU time\nspent discovering that the contents under the new name\nalready exist in the object database). But Git will discover\nthe rename on its own in the usual way regardless of how\nyou get to that point.\n\nChris\n"},{"id":"544119","messageId":"1343ca51-c8a8-4a86-9b46-02468a92b98f@fhannenheim.net","threadId":"65695","inReplyTo":"CAPx1Gvd9+z0th9whCbcA60_bWproPp+kwp3qDmhQOe4G=0=E6A@mail.gmail.com","subject":"Re: git mv after the fact","fromName":"Frieder Hannenheim","fromEmail":"mail@fhannenheim.net","sentAt":"2026-05-26T16:50:39Z","receivedAt":"2026-05-26T16:50:43Z","isPatch":false,"body":"In my particular use case I changed a patch to be a git patch with a\ncommit message and different filename so the move was not discovered\nautomatically. But I'm not sure if I staged the files so maybe it would\nhave been discovered.\n\nFrieder\n\nOn 26.05.26 18:40, Chris Torek wrote:\n> On Tue, May 26, 2026 at 6:18 AM Frieder Hannenheim <mail@fhannenheim.net> wrote:\n>> I'd like to propose a new flag for git mv, that updates the index\n>> like git mv normally would but does not move the file. ...\n> You may already know this, but technically no flag is needed:\n> you can just \"git add\" the new name and \"git rm\" the old one,\n> with the same effect.\n>\n> A flag for \"git mv\" would be convenient (and slightly more\n> efficient, not in terms of storage but in terms of CPU time\n> spent discovering that the contents under the new name\n> already exist in the object database). But Git will discover\n> the rename on its own in the usual way regardless of how\n> you get to that point.\n>\n> Chris\n"},{"id":"544127","messageId":"85D5E150-BD78-4FC5-83DC-10DEBAC6EA4B@gmail.com","threadId":"65695","inReplyTo":"CAPx1Gvd9+z0th9whCbcA60_bWproPp+kwp3qDmhQOe4G=0=E6A@mail.gmail.com","subject":"Re: git mv after the fact","fromName":"Ben Knoble","fromEmail":"ben.knoble@gmail.com","sentAt":"2026-05-26T21:29:02Z","receivedAt":"2026-05-26T21:29:15Z","isPatch":false,"body":"\n> Le 26 mai 2026 à 12:46, Chris Torek <chris.torek@gmail.com> a écrit :\n> \n> ﻿On Tue, May 26, 2026 at 6:18 AM Frieder Hannenheim <mail@fhannenheim.net> wrote:\n>> I'd like to propose a new flag for git mv, that updates the index\n>> like git mv normally would but does not move the file. ...\n> \n> You may already know this, but technically no flag is needed:\n> you can just \"git add\" the new name and \"git rm\" the old one,\n> with the same effect.\n\nOr indeed, « git add old new » should also work, I think.\n\n> A flag for \"git mv\" would be convenient (and slightly more\n> efficient, not in terms of storage but in terms of CPU time\n> spent discovering that the contents under the new name\n> already exist in the object database). But Git will discover\n> the rename on its own in the usual way regardless of how\n> you get to that point.\n> \n> Chris\n"},{"id":"544132","messageId":"xmqqy0h5lfa0.fsf@gitster.g","threadId":"65695","inReplyTo":"CAPx1Gvd9+z0th9whCbcA60_bWproPp+kwp3qDmhQOe4G=0=E6A@mail.gmail.com","subject":"Re: git mv after the fact","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-05-27T03:09:27Z","receivedAt":"2026-05-27T03:09:29Z","isPatch":false,"body":"Chris Torek <chris.torek@gmail.com> writes:\n\n> On Tue, May 26, 2026 at 6:18 AM Frieder Hannenheim <mail@fhannenheim.net> wrote:\n>> I'd like to propose a new flag for git mv, that updates the index\n>> like git mv normally would but does not move the file. ...\n>\n> You may already know this, but technically no flag is needed:\n> you can just \"git add\" the new name and \"git rm\" the old one,\n> with the same effect.\n\nCorrect.\n\n> A flag for \"git mv\" would be convenient (and slightly more\n> efficient, not in terms of storage but in terms of CPU time\n> spent discovering that the contents under the new name\n> already exist in the object database).\n\nMay be convenient, but I do not get the \"efficient\" part.  Do you\nmean that for two operations \"add\" and \"rm\", you need to spend two\nindex writes plus one file contents hash, as opposed to one index\nrite without having to do any contents hash?\n\n> But Git will discover\n> the rename on its own in the usual way regardless of how\n> you get to that point.\n\nThis is not incorrect per-se, but it is a confusing thing to say to\nsomebody who does not know the equivalence of \"mv\" and \"rm + add\".\nIt would not be clear to them that you are not talking about what\nhappens during \"mv\" or \"rm + add\", but about what happend during\n\"git log -M\", \"git diff -M\", etc.\n\nThere is \"git rm --cached\" that can be used to recover from an\n\"oops, I removed the file from the filesystem without telling Git\".\n\n    $ date >new-file.txt\n    $ git add new-file.txt\n    $ rm new-file.txt\n    $ git rm --cached new-file.txt\n\nI think the requested \"feature\" is not all that outrageous.  It\nwould be a similar value as a morning-after correction measure for\n\"oops, I moved the file in the filesystem without telling Git\".\n\n    $ date >old-file.txt\n    $ git add old-file.txt\n    $ mv old-file.txt new-file.txt\n    $ git mv --cached old-file.txt new-file.txt\n\nThanks.\n\n"},{"id":"544133","messageId":"CAPx1GvetxY1T7cuFN_xe51EURr-ED2BqW3E82jj90ko3PSYSyg@mail.gmail.com","threadId":"65695","inReplyTo":"xmqqy0h5lfa0.fsf@gitster.g","subject":"Re: git mv after the fact","fromName":"Chris Torek","fromEmail":"chris.torek@gmail.com","sentAt":"2026-05-27T03:19:17Z","receivedAt":"2026-05-27T03:19:30Z","isPatch":false,"body":"> Chris Torek <chris.torek@gmail.com> writes:\n>> A flag for \"git mv\" would be convenient (and slightly moreefficient ...\n>\n\nOn Tue, May 26, 2026 at 8:09 PM Junio C Hamano <gitster@pobox.com> wrote:\n> May be convenient, but I do not get the \"efficient\" part.\n\nA normal `git mv` renames the index entry and the file in the working\ntree without running `git add` on the *contents*, so there's no new hash\ncomputation.  Presumably a `git mv --after foo bar` would do the same: verify\nthat there is no existing `bar` in the index, that there is an existing `foo` in\nthe index, and that there is no `foo` but there is a `bar` in the working tree,\nand then it would rename (add-and-remove, really, because of sorting)\nthe index entry, without scanning the working tree contents.\n\nIn other words, we skip reading the 3 terabyte file, or whatever.\n\nAnyway, comparing to `git rm --cached`:\n\n> I think the requested \"feature\" is not all that outrageous.  It\n> would be a similar value as a morning-after correction measure for\n> \"oops, I moved the file in the filesystem without telling Git\".\n\nI agree, but I also don't see it as valuable enough to bother\nwriting a proper implementation.\n\nI did write it up as a shell script though, long ago. Adding it here\nvia gmail would mess with white space so I'll just provide a link\nto the file on GitHub:\n\nhttps://github.com/chris3torek/scripts/blob/master/git-mv-after\n\nChris\n"},{"id":"544135","messageId":"983b4ba0-d2b0-4d29-aabe-9e7b3987d514@decentral.ch","threadId":"65695","inReplyTo":"xmqqy0h5lfa0.fsf@gitster.g","subject":"Re: git mv after the fact","fromName":"Tim Tassonis","fromEmail":"stuff@decentral.ch","sentAt":"2026-05-27T03:27:14Z","receivedAt":"2026-05-27T04:00:26Z","isPatch":false,"body":"Yeah, and while we're at at it: why not another patch for\n\ngit rm \nfile_i_deleted_but_didnt_tell_git_and_dont_want_an_error_message_because_thats_offensive\n\nbecause that's always very rude, too, telling me to have to use\n\ngit rm -f\n\nBecause I also am very sensitive and don't like to be told I fucked up \nand have to be more specific about what I actually want. That's just \ntoxic, man.\n\n\n\n\nOn 5/27/26 05:09, Junio C Hamano wrote:\n> Chris Torek <chris.torek@gmail.com> writes:\n> \n>> On Tue, May 26, 2026 at 6:18 AM Frieder Hannenheim <mail@fhannenheim.net> wrote:\n>>> I'd like to propose a new flag for git mv, that updates the index\n>>> like git mv normally would but does not move the file. ...\n>>\n>> You may already know this, but technically no flag is needed:\n>> you can just \"git add\" the new name and \"git rm\" the old one,\n>> with the same effect.\n> \n> Correct.\n> \n>> A flag for \"git mv\" would be convenient (and slightly more\n>> efficient, not in terms of storage but in terms of CPU time\n>> spent discovering that the contents under the new name\n>> already exist in the object database).\n> \n> May be convenient, but I do not get the \"efficient\" part.  Do you\n> mean that for two operations \"add\" and \"rm\", you need to spend two\n> index writes plus one file contents hash, as opposed to one index\n> rite without having to do any contents hash?\n> \n>> But Git will discover\n>> the rename on its own in the usual way regardless of how\n>> you get to that point.\n> \n> This is not incorrect per-se, but it is a confusing thing to say to\n> somebody who does not know the equivalence of \"mv\" and \"rm + add\".\n> It would not be clear to them that you are not talking about what\n> happens during \"mv\" or \"rm + add\", but about what happend during\n> \"git log -M\", \"git diff -M\", etc.\n> \n> There is \"git rm --cached\" that can be used to recover from an\n> \"oops, I removed the file from the filesystem without telling Git\".\n> \n>      $ date >new-file.txt\n>      $ git add new-file.txt\n>      $ rm new-file.txt\n>      $ git rm --cached new-file.txt\n> \n> I think the requested \"feature\" is not all that outrageous.  It\n> would be a similar value as a morning-after correction measure for\n> \"oops, I moved the file in the filesystem without telling Git\".\n> \n>      $ date >old-file.txt\n>      $ git add old-file.txt\n>      $ mv old-file.txt new-file.txt\n>      $ git mv --cached old-file.txt new-file.txt\n> \n> Thanks.\n> \n> \n\n-- \ndecentral.ch - IT Stuff\nTim Tassonis\nBadenerstrasse 219\n8003 Zürich\nstuff@decentral.ch\n+41 79 229 36 17\n\n"},{"id":"544199","messageId":"877bootp3l.fsf@gitster.g","threadId":"65695","inReplyTo":"CAPx1GvetxY1T7cuFN_xe51EURr-ED2BqW3E82jj90ko3PSYSyg@mail.gmail.com","subject":"Re: git mv after the fact","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-05-27T23:22:22Z","receivedAt":"2026-05-27T23:22:29Z","isPatch":false,"body":"Chris Torek <chris.torek@gmail.com> writes:\n\n>> Chris Torek <chris.torek@gmail.com> writes:\n>>> A flag for \"git mv\" would be convenient (and slightly moreefficient ...\n>>\n>\n> On Tue, May 26, 2026 at 8:09 PM Junio C Hamano <gitster@pobox.com> wrote:\n>> May be convenient, but I do not get the \"efficient\" part.\n>\n> A normal `git mv` renames the index entry and the file in the working\n> tree without running `git add` on the *contents*, so there's no new hash\n> computation.  Presumably a `git mv --after foo bar` would do the same: verify\n> that there is no existing `bar` in the index, that there is an existing `foo` in\n> the index, and that there is no `foo` but there is a `bar` in the working tree,\n> and then it would rename (add-and-remove, really, because of sorting)\n> the index entry, without scanning the working tree contents.\n>\n> In other words, we skip reading the 3 terabyte file, or whatever.\n\nYup, that matches what I wrote.  We do not rehash and we only write\nthe index just once.\n\n> Anyway, comparing to `git rm --cached`:\n>\n>> I think the requested \"feature\" is not all that outrageous.  It\n>> would be a similar value as a morning-after correction measure for\n>> \"oops, I moved the file in the filesystem without telling Git\".\n>\n> I agree, but I also don't see it as valuable enough to bother\n> writing a proper implementation.\n\nYup, I was merely agreeing with your \"convenient\" comment.  I do not\nthink it is such a high-priority item for any active developers\namong us to drop something they are doing and writing it instead.\n"},{"id":"544228","messageId":"1FEDBC47-5DDB-4C42-A7C7-695630D330BF@gmail.com","threadId":"65695","inReplyTo":"877bootp3l.fsf@gitster.g","subject":"Re: git mv after the fact","fromName":"Ben Knoble","fromEmail":"ben.knoble@gmail.com","sentAt":"2026-05-28T14:28:51Z","receivedAt":"2026-05-28T14:29:04Z","isPatch":false,"body":"\n> Le 27 mai 2026 à 19:24, Junio C Hamano <gitster@pobox.com> a écrit :\n> \n> ﻿Chris Torek <chris.torek@gmail.com> writes:\n> \n>>> Chris Torek <chris.torek@gmail.com> writes:\n>>>> A flag for \"git mv\" would be convenient (and slightly moreefficient ...\n>>> \n>> \n>> On Tue, May 26, 2026 at 8:09 PM Junio C Hamano <gitster@pobox.com> wrote:\n>>> May be convenient, but I do not get the \"efficient\" part.\n>> \n>> A normal `git mv` renames the index entry and the file in the working\n>> tree without running `git add` on the *contents*, so there's no new hash\n>> computation.  Presumably a `git mv --after foo bar` would do the same: verify\n>> that there is no existing `bar` in the index, that there is an existing `foo` in\n>> the index, and that there is no `foo` but there is a `bar` in the working tree,\n>> and then it would rename (add-and-remove, really, because of sorting)\n>> the index entry, without scanning the working tree contents.\n>> \n>> In other words, we skip reading the 3 terabyte file, or whatever.\n> \n> Yup, that matches what I wrote.  We do not rehash and we only write\n> the index just once.\n\nOne thing I wondered: if we don’t have an exact move but assume (by not hashing), doesn’t that mean the index would differ from what’s on disk? I originally thought that might be a problem, but the more I thought the more I realized that’s a fairly typical state anyway.\n\nJust seemed like a potential footgun to me, but perhaps not worth worrying about. "},{"id":"544238","messageId":"xmqq5x47jmpm.fsf@gitster.g","threadId":"65695","inReplyTo":"1FEDBC47-5DDB-4C42-A7C7-695630D330BF@gmail.com","subject":"Re: git mv after the fact","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-05-28T20:36:21Z","receivedAt":"2026-05-28T20:36:23Z","isPatch":false,"body":"Ben Knoble <ben.knoble@gmail.com> writes:\n\n>> Le 27 mai 2026 à 19:24, Junio C Hamano <gitster@pobox.com> a écrit :\n>> \n>> ﻿Chris Torek <chris.torek@gmail.com> writes:\n>> \n>>>> Chris Torek <chris.torek@gmail.com> writes:\n>>>>> A flag for \"git mv\" would be convenient (and slightly moreefficient ...\n>>>> \n>>> \n>>> On Tue, May 26, 2026 at 8:09 PM Junio C Hamano <gitster@pobox.com> wrote:\n>>>> May be convenient, but I do not get the \"efficient\" part.\n>>> \n>>> A normal `git mv` renames the index entry and the file in the working\n>>> tree without running `git add` on the *contents*, so there's no new hash\n>>> computation.  Presumably a `git mv --after foo bar` would do the same: verify\n>>> that there is no existing `bar` in the index, that there is an existing `foo` in\n>>> the index, and that there is no `foo` but there is a `bar` in the working tree,\n>>> and then it would rename (add-and-remove, really, because of sorting)\n>>> the index entry, without scanning the working tree contents.\n>>> \n>>> In other words, we skip reading the 3 terabyte file, or whatever.\n>> \n>> Yup, that matches what I wrote.  We do not rehash and we only write\n>> the index just once.\n>\n> One thing I wondered: if we don’t have an exact move but assume (by not hashing), doesn’t that mean the index would differ from what’s on disk? I originally thought that might be a problem, but the more I thought the more I realized that’s a fairly typical state anyway.\n>\n> Just seemed like a potential footgun to me, but perhaps not worth worrying about. \n\nGood point.  Actually with or without --after/--cached, we do not\nhave to rehash, and more importantly, we should not rehash.\n\nAs you can do this already\n\n    $ date >old.txt\n    $ git add old.txt\n    $ date >>old.txt\n    ... now old.txt is _dirty_\n    $ git mv old.txt new.txt\n\nI do not think it is unusual to have the contents you have in your\nworking tree files diverge from the contents you last 'git add'ed to\nthe index.  You do *not* want to rehash.  The index entry for new.txt\nmust be left not-up-to-date, which is achieved in the above sequence\nby retaining the file timestamp of old.txt at the \"git add\" time\neven after \"git mv\" (i.e. new.txt has the timestamp and size of the\nold.txt after it got the second \"date\" output, the index entry\nrecords one generation old one, and would not match).  If we do the\n\"index-entry only\" move, i.e.\n\n    $ date >old.txt\n    $ git add old.txt\n    $ date >>old.txt\n    ... now old.txt is _dirty_\n    $ mv old.txt new.txt\n    ... oops, we forgot to tell git\n    $ git mv --cached old.txt new.txt\n\nwe need to make sure that we leave the cache entry dirty for\nnew.txt in the index.\n\n"}]}