{"thread":{"id":"49967","subject":"Retrieving a file in git that was deleted and committed","startedAt":"2018-12-07T06:49:36Z","lastAt":"2018-12-11T09:46:59Z","messageCount":12,"participants":["biswaranjan panda","Bryan Turner","Jeff King","Sergey Organov","Elijah Newren","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"364733","messageId":"CADHAf1Y_d=-9By4jC2xd+BmWJgfGmBNUr=uSuQtfuHDrarN4kw@mail.gmail.com","threadId":"49967","inReplyTo":null,"subject":"Retrieving a file in git that was deleted and committed","fromName":"biswaranjan panda","fromEmail":"biswaranjan.nitrkl@gmail.com","sentAt":"2018-12-07T06:49:23Z","receivedAt":"2018-12-07T06:49:36Z","isPatch":false,"sender":{"key":"biswaranjan.nitrkl@gmail.com","avatar":null},"body":"I have the following scenario:\n\nOn a branch A, I deleted a file foo.txt and committed the change. Then\nI did a bunch of other changes.\nNow I want to undelete foo.txt.\n\nOne way is to checkout a separate branch B where the file is present.\nThen checkout A. Then do\ngit checkout B -- path_to_file\n\nWhile this does gets the file back, the file shows up as a new file to\nbe committed. Once I commit it, git blame doesn't show the old history\nfor the file.\n\nI would appreciate if anyone knows how to preserve git blame history.\n"},{"id":"364734","messageId":"CAGyf7-FUHMEq_FfPNrH6uT2b-nCd_wi=Aww+OUuoDem11DROGA@mail.gmail.com","threadId":"49967","inReplyTo":"CADHAf1Y_d=-9By4jC2xd+BmWJgfGmBNUr=uSuQtfuHDrarN4kw@mail.gmail.com","subject":"Re: Retrieving a file in git that was deleted and committed","fromName":"Bryan Turner","fromEmail":"bturner@atlassian.com","sentAt":"2018-12-07T06:55:21Z","receivedAt":"2018-12-07T06:55:35Z","isPatch":false,"sender":{"key":"bturner@atlassian.com","avatar":"https://gravatar.com/avatar/16bcf3167981c1ef7c804e502642366d888a35b0d0b0a4ca01fdc442aa1acb1e?d=mp&s=160"},"body":"On Thu, Dec 6, 2018 at 10:49 PM biswaranjan panda\n<biswaranjan.nitrkl@gmail.com> wrote:\n>\n> I have the following scenario:\n>\n> On a branch A, I deleted a file foo.txt and committed the change. Then\n> I did a bunch of other changes.\n> Now I want to undelete foo.txt.\n>\n> One way is to checkout a separate branch B where the file is present.\n> Then checkout A. Then do\n> git checkout B -- path_to_file\n\nIt doesn't change anything, but note that you don't need to checkout B\nfirst, to restore the file. If you know a commit SHA where the file is\npresent, \"git checkout SHA -- path_to_file\" will pull back the file as\nit existed at that commit.\n\n>\n> While this does gets the file back, the file shows up as a new file to\n> be committed. Once I commit it, git blame doesn't show the old history\n> for the file.\n>\n> I would appreciate if anyone knows how to preserve git blame history\n\nIt's not possible, as far as I'm aware. While the new file has the\nsame name as the old file, to Git they are two unrelated entries that\nhappen to reside at the same path. Even things like \"git log --follow\"\nwon't consider the file to be related to its previous history.\n\nBryan\n"},{"id":"364735","messageId":"CADHAf1Y8or_frf=Ecu-82z-jo06NKe7oqo1cxtsZsOxhKKxjAw@mail.gmail.com","threadId":"49967","inReplyTo":"CAGyf7-FUHMEq_FfPNrH6uT2b-nCd_wi=Aww+OUuoDem11DROGA@mail.gmail.com","subject":"Re: Retrieving a file in git that was deleted and committed","fromName":"biswaranjan panda","fromEmail":"biswaranjan.nitrkl@gmail.com","sentAt":"2018-12-07T07:07:00Z","receivedAt":"2018-12-07T07:07:15Z","isPatch":false,"sender":{"key":"biswaranjan.nitrkl@gmail.com","avatar":null},"body":"Thanks! Strangely git log --follow does work.\nOn Thu, Dec 6, 2018 at 10:55 PM Bryan Turner <bturner@atlassian.com> wrote:\n>\n> On Thu, Dec 6, 2018 at 10:49 PM biswaranjan panda\n> <biswaranjan.nitrkl@gmail.com> wrote:\n> >\n> > I have the following scenario:\n> >\n> > On a branch A, I deleted a file foo.txt and committed the change. Then\n> > I did a bunch of other changes.\n> > Now I want to undelete foo.txt.\n> >\n> > One way is to checkout a separate branch B where the file is present.\n> > Then checkout A. Then do\n> > git checkout B -- path_to_file\n>\n> It doesn't change anything, but note that you don't need to checkout B\n> first, to restore the file. If you know a commit SHA where the file is\n> present, \"git checkout SHA -- path_to_file\" will pull back the file as\n> it existed at that commit.\n>\n> >\n> > While this does gets the file back, the file shows up as a new file to\n> > be committed. Once I commit it, git blame doesn't show the old history\n> > for the file.\n> >\n> > I would appreciate if anyone knows how to preserve git blame history\n>\n> It's not possible, as far as I'm aware. While the new file has the\n> same name as the old file, to Git they are two unrelated entries that\n> happen to reside at the same path. Even things like \"git log --follow\"\n> won't consider the file to be related to its previous history.\n>\n> Bryan\n\n\n\n-- \nThanks,\n-Biswa\n"},{"id":"364736","messageId":"20181207072004.GA32603@sigill.intra.peff.net","threadId":"49967","inReplyTo":"CADHAf1Y8or_frf=Ecu-82z-jo06NKe7oqo1cxtsZsOxhKKxjAw@mail.gmail.com","subject":"Re: Retrieving a file in git that was deleted and committed","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2018-12-07T07:20:04Z","receivedAt":"2018-12-07T07:26:49Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Dec 06, 2018 at 11:07:00PM -0800, biswaranjan panda wrote:\n\n> Thanks! Strangely git log --follow does work.\n\nI suspect it would work even without --follow. When you limit a log\ntraversal with a pathspec, like:\n\n  git log foo\n\nthat is not about following some continuous stream of content, but\nrather just applying that pathspec to the diff of each commit, and\npruning ones where it did not change. So even if there are gaps where\nthe file did not exist, we continue to apply the pathspec to the older\ncommits.\n\nTools like git-blame will _not_ work, though, as they really are trying\nto track the content as they walk back through history. And Once all of\nthe content seems to appear from nowhere in your new commit, that seems\nlike a dead end.\n\nIn theory there could be some machine-readable annotation in the commit\nobject (or in a note created after the fact) to say \"even though 'foo'\nis a new file here, it came from $commit:foo\".  And then git-blame could\nkeep following the content there. But such a feature does not yet exist.\n\n-Peff\n"},{"id":"364737","messageId":"CAGyf7-EkyGOi02fqMcCPBzj-=wpsH4zCgvP5VhOoKMdG+wfoLw@mail.gmail.com","threadId":"49967","inReplyTo":"20181207072004.GA32603@sigill.intra.peff.net","subject":"Re: Retrieving a file in git that was deleted and committed","fromName":"Bryan Turner","fromEmail":"bturner@atlassian.com","sentAt":"2018-12-07T07:37:36Z","receivedAt":"2018-12-07T07:37:50Z","isPatch":false,"sender":{"key":"bturner@atlassian.com","avatar":"https://gravatar.com/avatar/16bcf3167981c1ef7c804e502642366d888a35b0d0b0a4ca01fdc442aa1acb1e?d=mp&s=160"},"body":"On Thu, Dec 6, 2018 at 11:26 PM Jeff King <peff@peff.net> wrote:\n>\n> On Thu, Dec 06, 2018 at 11:07:00PM -0800, biswaranjan panda wrote:\n>\n> > Thanks! Strangely git log --follow does work.\n>\n> I suspect it would work even without --follow. When you limit a log\n> traversal with a pathspec, like:\n>\n>   git log foo\n>\n> that is not about following some continuous stream of content, but\n> rather just applying that pathspec to the diff of each commit, and\n> pruning ones where it did not change. So even if there are gaps where\n> the file did not exist, we continue to apply the pathspec to the older\n> commits.\n\nAh, of course. Thanks for the clarification, Jeff. And my apologies to\nBiswaranjan Panda for the incorrect information.\n\n>\n> Tools like git-blame will _not_ work, though, as they really are trying\n> to track the content as they walk back through history. And Once all of\n> the content seems to appear from nowhere in your new commit, that seems\n> like a dead end.\n>\n> In theory there could be some machine-readable annotation in the commit\n> object (or in a note created after the fact) to say \"even though 'foo'\n> is a new file here, it came from $commit:foo\".  And then git-blame could\n> keep following the content there. But such a feature does not yet exist.\n>\n> -Peff\n"},{"id":"364756","messageId":"CADHAf1bH5Aaw3-5WvoHawjXUXL9B-YNvh+AYU1fpGbUe=c0E+A@mail.gmail.com","threadId":"49967","inReplyTo":"CAGyf7-EkyGOi02fqMcCPBzj-=wpsH4zCgvP5VhOoKMdG+wfoLw@mail.gmail.com","subject":"Re: Retrieving a file in git that was deleted and committed","fromName":"biswaranjan panda","fromEmail":"biswaranjan.nitrkl@gmail.com","sentAt":"2018-12-07T21:50:57Z","receivedAt":"2018-12-07T21:51:12Z","isPatch":false,"sender":{"key":"biswaranjan.nitrkl@gmail.com","avatar":null},"body":"On Thu, Dec 6, 2018 at 11:26 PM Jeff King <peff@peff.net> wrote:\n>>\n>> On Thu, Dec 06, 2018 at 11:07:00PM -0800, biswaranjan panda wrote:\n>>\n> >> Thanks! Strangely git log --follow does work.\n>>\n>> I suspect it would work even without --follow. When you limit a log\n>> traversal with a pathspec, like:\n>>\n>>   git log foo\n>>\n>> that is not about following some continuous stream of content, but\n>> rather just applying that pathspec to the diff of each commit, and\n>> pruning ones where it did not change. So even if there are gaps where\n>> the file did not exist, we continue to apply the pathspec to the older\n>> commits.\n\n> Ah, of course. Thanks for the clarification, Jeff. And my > apologies to\n> Biswaranjan Panda for the incorrect information.\n\nThanks Jeff and Bryan! However, I am curious that if there were a way\nto tell git blame to skip a commit (the one which added the file again\nand maybe the one which deleted it originally) while it walks back\nthrough history, then it should just get back the\nentire history right ?\nOn Thu, Dec 6, 2018 at 11:37 PM Bryan Turner <bturner@atlassian.com> wrote:\n>\n> On Thu, Dec 6, 2018 at 11:26 PM Jeff King <peff@peff.net> wrote:\n> >\n> > On Thu, Dec 06, 2018 at 11:07:00PM -0800, biswaranjan panda wrote:\n> >\n> > > Thanks! Strangely git log --follow does work.\n> >\n> > I suspect it would work even without --follow. When you limit a log\n> > traversal with a pathspec, like:\n> >\n> >   git log foo\n> >\n> > that is not about following some continuous stream of content, but\n> > rather just applying that pathspec to the diff of each commit, and\n> > pruning ones where it did not change. So even if there are gaps where\n> > the file did not exist, we continue to apply the pathspec to the older\n> > commits.\n>\n> Ah, of course. Thanks for the clarification, Jeff. And my apologies to\n> Biswaranjan Panda for the incorrect information.\n>\n> >\n> > Tools like git-blame will _not_ work, though, as they really are trying\n> > to track the content as they walk back through history. And Once all of\n> > the content seems to appear from nowhere in your new commit, that seems\n> > like a dead end.\n> >\n> > In theory there could be some machine-readable annotation in the commit\n> > object (or in a note created after the fact) to say \"even though 'foo'\n> > is a new file here, it came from $commit:foo\".  And then git-blame could\n> > keep following the content there. But such a feature does not yet exist.\n> >\n> > -Peff\n\n\n\n-- \nThanks,\n-Biswa\n"},{"id":"364798","messageId":"20181208072915.GA20697@sigill.intra.peff.net","threadId":"49967","inReplyTo":"CADHAf1bH5Aaw3-5WvoHawjXUXL9B-YNvh+AYU1fpGbUe=c0E+A@mail.gmail.com","subject":"Re: Retrieving a file in git that was deleted and committed","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2018-12-08T07:29:15Z","receivedAt":"2018-12-08T07:33:07Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Dec 07, 2018 at 01:50:57PM -0800, biswaranjan panda wrote:\n\n> Thanks Jeff and Bryan! However, I am curious that if there were a way\n> to tell git blame to skip a commit (the one which added the file again\n> and maybe the one which deleted it originally) while it walks back\n> through history, then it should just get back the\n> entire history right ?\n\nNot easily. ;)\n\nYou can feed a set of revisions to git-blame with the \"-S\" option, but I\ndon't offhand know how it handles diffs (I think it would have to still\ndiff each commit against its parent, since history is non-linear, and a\nlist is inherently linear). You might want to experiment with that.\n\nOther than that, you can play with git-replace to produce a fake\nhistory, as if the deletion never happened. But note that will affect\nall commands, not just one particular blame. It might be a neat way to\nplay with blame, but I doubt I'd leave the replacement in place in the\nlong term.\n\n-Peff\n"},{"id":"364822","messageId":"CADHAf1Z=L2CeHax3nsP108yQr9G9xp+mhr+_F_zBcukr0kEFpQ@mail.gmail.com","threadId":"49967","inReplyTo":"20181208072915.GA20697@sigill.intra.peff.net","subject":"Re: Retrieving a file in git that was deleted and committed","fromName":"biswaranjan panda","fromEmail":"biswaranjan.nitrkl@gmail.com","sentAt":"2018-12-09T00:07:43Z","receivedAt":"2018-12-09T00:07:58Z","isPatch":false,"sender":{"key":"biswaranjan.nitrkl@gmail.com","avatar":null},"body":">You can feed a set of revisions to git-blame with the \"-S\" option, but I\n>don't offhand know how it handles diffs (I think it would have to still\n>diff each commit against its parent, since history is non-linear, and a\n>list is inherently linear). You might want to experiment with that.\n\n>Other than that, you can play with git-replace to produce a fake\n>history, as if the deletion never happened. But note that will affect\n>all commands, not just one particular blame. It might be a neat way to\n>play with blame, but I doubt I'd leave the replacement in place in the\n>long term.\n\n> -Peff\n\nAh I see. Will try git-replace. Thanks!\n\nOn Fri, Dec 7, 2018 at 11:29 PM Jeff King <peff@peff.net> wrote:\n>\n> On Fri, Dec 07, 2018 at 01:50:57PM -0800, biswaranjan panda wrote:\n>\n> > Thanks Jeff and Bryan! However, I am curious that if there were a way\n> > to tell git blame to skip a commit (the one which added the file again\n> > and maybe the one which deleted it originally) while it walks back\n> > through history, then it should just get back the\n> > entire history right ?\n>\n> Not easily. ;)\n>\n> You can feed a set of revisions to git-blame with the \"-S\" option, but I\n> don't offhand know how it handles diffs (I think it would have to still\n> diff each commit against its parent, since history is non-linear, and a\n> list is inherently linear). You might want to experiment with that.\n>\n> Other than that, you can play with git-replace to produce a fake\n> history, as if the deletion never happened. But note that will affect\n> all commands, not just one particular blame. It might be a neat way to\n> play with blame, but I doubt I'd leave the replacement in place in the\n> long term.\n>\n> -Peff\n\n\n\n-- \nThanks,\n-Biswa\n"},{"id":"364926","messageId":"87sgz58lh5.fsf@javad.com","threadId":"49967","inReplyTo":"CADHAf1Y_d=-9By4jC2xd+BmWJgfGmBNUr=uSuQtfuHDrarN4kw@mail.gmail.com","subject":"Re: Retrieving a file in git that was deleted and committed","fromName":"Sergey Organov","fromEmail":"sorganov@gmail.com","sentAt":"2018-12-10T15:12:06Z","receivedAt":"2018-12-10T15:20:45Z","isPatch":false,"sender":{"key":"sorganov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8501568?v=4"},"body":"biswaranjan panda <biswaranjan.nitrkl@gmail.com> writes:\n\n> I have the following scenario:\n>\n> On a branch A, I deleted a file foo.txt and committed the change. Then\n> I did a bunch of other changes.\n> Now I want to undelete foo.txt.\n\n[...]\n\n> I would appreciate if anyone knows how to preserve git blame history.\n\nProvided you deleted the file by mistake and you didn't yet publish the\nhistory, just rewrite the history, fixing the commit that deleted the\nfile. \"git rebase -i\" is a suitable way to do it.\n\n-- Sergey\n"},{"id":"364978","messageId":"CABPp-BHjR7Wq-D9tFMyPHZE1ogL5udOt8ri1rN3E1CasfD-2PQ@mail.gmail.com","threadId":"49967","inReplyTo":"20181207072004.GA32603@sigill.intra.peff.net","subject":"Re: Retrieving a file in git that was deleted and committed","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2018-12-10T21:33:18Z","receivedAt":"2018-12-10T21:33:33Z","isPatch":false,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Thu, Dec 6, 2018 at 11:48 PM Jeff King <peff@peff.net> wrote:\n>\n> On Thu, Dec 06, 2018 at 11:07:00PM -0800, biswaranjan panda wrote:\n>\n> > Thanks! Strangely git log --follow does work.\n>\n> I suspect it would work even without --follow. When you limit a log\n> traversal with a pathspec, like:\n>\n>   git log foo\n>\n> that is not about following some continuous stream of content, but\n> rather just applying that pathspec to the diff of each commit, and\n> pruning ones where it did not change. So even if there are gaps where\n> the file did not exist, we continue to apply the pathspec to the older\n> commits.\n>\n> Tools like git-blame will _not_ work, though, as they really are trying\n> to track the content as they walk back through history. And Once all of\n> the content seems to appear from nowhere in your new commit, that seems\n> like a dead end.\n>\n> In theory there could be some machine-readable annotation in the commit\n> object (or in a note created after the fact) to say \"even though 'foo'\n> is a new file here, it came from $commit:foo\".  And then git-blame could\n> keep following the content there. But such a feature does not yet exist.\n>\n> -Peff\n\nHmm...sure, if the file is deleted on the only relevant branch through\nhistory...but what if there were another branch where it weren't\ndeleted?  What does git blame do then?\n\nIn other words, do NOT restore the file as biswaranjan suggested, but\ninstead restore it this way[1]:\n\ngit checkout -b keep-foo $REVISION_BEFORE_FOO_DELETED\ngit commit --allow-empty -m \"We want to keep foo\"\ngit checkout A\ngit merge --no-commit keep-foo\ngit checkout keep-foo -- foo.txt\ngit commit\n\n\nNow, when you run\n  git blame foo.txt\n\nblame should notice that foo.txt didn't exist in the first parent\nhistory on A, so it won't bother walking it to find that at some point\nfoo.txt did exist there.  Instead, it'll walk down the second parent\nand follow its history, where it should keep walking back and show all\nthe old changes...right?  Or did I mess up my testcase and\nmisunderstand something somehow?\n\n\nElijah\n\n[1] Sidenote: it seems like those commands I gave should have simplified down to\n\ngit merge --no-commit --no-ff $REVISION_BEFORE_FOO_DELETED\ngit checkout $REVISION_BEFORE_FOO_DELETED -- foo.txt\ngit commit\n\nBut that seems to error out with \"Already up-to-date\"...even when\ntesting with older versions of git, so it wasn't any of my changes.\nNot sure why the --no-ff flag doesn't work.\n"},{"id":"365002","messageId":"xmqq8t0woo6y.fsf@gitster-ct.c.googlers.com","threadId":"49967","inReplyTo":"20181208072915.GA20697@sigill.intra.peff.net","subject":"Re: Retrieving a file in git that was deleted and committed","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-12-11T01:19:01Z","receivedAt":"2018-12-11T01:19:06Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> You can feed a set of revisions to git-blame with the \"-S\" option, but I\n> don't offhand know how it handles diffs (I think it would have to still\n> diff each commit against its parent, since history is non-linear, and a\n> list is inherently linear).\n\nIt would diff each commit against its parent specified by the file\ngiven to the -S option.  The contents of the file is fed directly to\nthe graft mechanism to make the fake history in effect while the\n\"blame\" runs, and you are not limited to a linear history.\n"},{"id":"365047","messageId":"20181211094633.GA31588@sigill.intra.peff.net","threadId":"49967","inReplyTo":"CABPp-BHjR7Wq-D9tFMyPHZE1ogL5udOt8ri1rN3E1CasfD-2PQ@mail.gmail.com","subject":"Re: Retrieving a file in git that was deleted and committed","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2018-12-11T09:46:33Z","receivedAt":"2018-12-11T09:46:59Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Dec 10, 2018 at 01:33:18PM -0800, Elijah Newren wrote:\n\n> Hmm...sure, if the file is deleted on the only relevant branch through\n> history...but what if there were another branch where it weren't\n> deleted?  What does git blame do then?\n> \n> In other words, do NOT restore the file as biswaranjan suggested, but\n> instead restore it this way[1]:\n> \n> git checkout -b keep-foo $REVISION_BEFORE_FOO_DELETED\n> git commit --allow-empty -m \"We want to keep foo\"\n> git checkout A\n> git merge --no-commit keep-foo\n> git checkout keep-foo -- foo.txt\n> git commit\n> \n> \n> Now, when you run\n>   git blame foo.txt\n> \n> blame should notice that foo.txt didn't exist in the first parent\n> history on A, so it won't bother walking it to find that at some point\n> foo.txt did exist there.  Instead, it'll walk down the second parent\n> and follow its history, where it should keep walking back and show all\n> the old changes...right?  Or did I mess up my testcase and\n> misunderstand something somehow?\n\nYeah, I think that should work, and is a clever way of representing in\nthe actual history graph what you're we're trying to express. And it\nshouldn't have any real downsides.\n\n-Peff\n"}]}