{"thread":{"id":"10771","subject":"[PATCH] builtin-commit: Refresh cache after adding files.","startedAt":"2007-11-09T16:40:27Z","lastAt":"2007-11-10T09:02:29Z","messageCount":14,"participants":["Kristian Høgsberg","Johannes Schindelin","Junio C Hamano","Linus Torvalds"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"59080","messageId":"1194626427-2419-1-git-send-email-krh@redhat.com","threadId":"10771","inReplyTo":null,"subject":"[PATCH] builtin-commit: Refresh cache after adding files.","fromName":"Kristian Høgsberg","fromEmail":"krh@redhat.com","sentAt":"2007-11-09T16:40:27Z","receivedAt":"2007-11-09T16:40:27Z","isPatch":true,"sender":{"key":"krh@redhat.com","avatar":"https://gravatar.com/avatar/763dee6f9594ac474f725b137a39565792928e583ddf59b32befc2907409027e?d=mp&s=160"},"body":"This fixes the race in the last test int t3700.\n\nSigned-off-by: Kristian Høgsberg <krh@redhat.com>\n---\n\nI'm basically just copying what git-commit.sh does, but I'll admit to not\nquite understanding why it's necessary.  It does fix the race in t3700 though.\nApplies on top of pu.\n\ncheers,\nKristian\n\n builtin-commit.c |    2 ++\n 1 files changed, 2 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin-commit.c b/builtin-commit.c\nindex 7dc8977..19862df 100644\n--- a/builtin-commit.c\n+++ b/builtin-commit.c\n@@ -81,6 +81,7 @@ static char *prepare_index(const char **files, const char *prefix)\n \n \tif (all || also) {\n \t\tadd_files_to_cache(verbose, also ? prefix : NULL, files);\n+\t\trefresh_cache(REFRESH_QUIET);\n \t\tif (write_cache(fd, active_cache, active_nr) || close(fd))\n \t\t\tdie(\"unable to write new_index file\");\n \t\treturn lock_file.filename;\n@@ -110,6 +111,7 @@ static char *prepare_index(const char **files, const char *prefix)\n \tfd = hold_lock_file_for_update(next_index_lock,\n \t\t\t\t       git_path(\"next-index-%d\", getpid()), 1);\n \tadd_files_to_cache(verbose, prefix, files);\n+\trefresh_cache(REFRESH_QUIET);\n \tif (write_cache(fd, active_cache, active_nr) || close(fd))\n \t\tdie(\"unable to write new_index file\");\n \n-- \n1.5.3.4.206.g58ba4\n"},{"id":"59084","messageId":"Pine.LNX.4.64.0711091702190.4362@racer.site","threadId":"10771","inReplyTo":"1194626427-2419-1-git-send-email-krh@redhat.com","subject":"Re: [PATCH] builtin-commit: Refresh cache after adding files.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-11-09T17:05:17Z","receivedAt":"2007-11-09T17:05:17Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Fri, 9 Nov 2007, Kristian Høgsberg wrote:\n\n> This fixes the race in the last test int t3700.\n\nWell, it is not a race.  My fault.  I thought it was.\n\nWhat you basically did was to make sure that the index is up-to-date after \nadding the files.  You might even want to say that in the commit message, \nand only then say that it fixes t3700, too.\n\nThanks,\nDscho\n"},{"id":"59085","messageId":"1194628412.30909.7.camel@hinata.boston.redhat.com","threadId":"10771","inReplyTo":"Pine.LNX.4.64.0711091702190.4362@racer.site","subject":"Re: [PATCH] builtin-commit: Refresh cache after adding files.","fromName":"Kristian Høgsberg","fromEmail":"krh@redhat.com","sentAt":"2007-11-09T17:13:32Z","receivedAt":"2007-11-09T17:13:32Z","isPatch":true,"sender":{"key":"krh@redhat.com","avatar":"https://gravatar.com/avatar/763dee6f9594ac474f725b137a39565792928e583ddf59b32befc2907409027e?d=mp&s=160"},"body":"On Fri, 2007-11-09 at 17:05 +0000, Johannes Schindelin wrote:\n> Hi,\n> \n> On Fri, 9 Nov 2007, Kristian Høgsberg wrote:\n> \n> > This fixes the race in the last test int t3700.\n> \n> Well, it is not a race.  My fault.  I thought it was.\n> \n> What you basically did was to make sure that the index is up-to-date after \n> adding the files.  You might even want to say that in the commit message, \n> and only then say that it fixes t3700, too.\n\nOK, I guess what I was wondering was why write_cache() doesn't write out\nan up-to-date index.  I'll send an updated patch.  Do we need a call to\nrefresh_cache() when we update the user cache but commit an index\ncreated from read_tree+add_files?  I.e. after the add_files_to_index()\ncall on line 97?  The shell script doesn't do this, it only runs\nupdate-index --refresh for the index that gets committed.\n\nKristian\n"},{"id":"59089","messageId":"Pine.LNX.4.64.0711091722520.4362@racer.site","threadId":"10771","inReplyTo":"1194628412.30909.7.camel@hinata.boston.redhat.com","subject":"Re: [PATCH] builtin-commit: Refresh cache after adding files.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-11-09T17:24:39Z","receivedAt":"2007-11-09T17:24:39Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Fri, 9 Nov 2007, Kristian H?gsberg wrote:\n\n> On Fri, 2007-11-09 at 17:05 +0000, Johannes Schindelin wrote:\n> \n> > On Fri, 9 Nov 2007, Kristian Høgsberg wrote:\n> > \n> > > This fixes the race in the last test int t3700.\n> > \n> > Well, it is not a race.  My fault.  I thought it was.\n> > \n> > What you basically did was to make sure that the index is up-to-date \n> > after adding the files.  You might even want to say that in the commit \n> > message, and only then say that it fixes t3700, too.\n> \n> OK, I guess what I was wondering was why write_cache() doesn't write out\n> an up-to-date index.\n\nwrite_cache() only writes the index, it does not update it.\n\n> Do we need a call to refresh_cache() when we update the user cache but \n> commit an index created from read_tree+add_files?  I.e. after the \n> add_files_to_index() call on line 97?  The shell script doesn't do this, \n> it only runs update-index --refresh for the index that gets committed.\n\nI think it would be sane to do so.\n\nIIUC this basically means that \"git add <file> && git commit\" should do \nthe same to the cache as \"git commit <file>\".\n\nThanks,\nDscho\n"},{"id":"59094","messageId":"1194629893.30909.12.camel@hinata.boston.redhat.com","threadId":"10771","inReplyTo":"Pine.LNX.4.64.0711091722520.4362@racer.site","subject":"Re: [PATCH] builtin-commit: Refresh cache after adding files.","fromName":"Kristian Høgsberg","fromEmail":"krh@redhat.com","sentAt":"2007-11-09T17:38:13Z","receivedAt":"2007-11-09T17:38:13Z","isPatch":true,"sender":{"key":"krh@redhat.com","avatar":"https://gravatar.com/avatar/763dee6f9594ac474f725b137a39565792928e583ddf59b32befc2907409027e?d=mp&s=160"},"body":"On Fri, 2007-11-09 at 17:24 +0000, Johannes Schindelin wrote:\n> Hi,\n> \n> On Fri, 9 Nov 2007, Kristian H?gsberg wrote:\n> \n> > On Fri, 2007-11-09 at 17:05 +0000, Johannes Schindelin wrote:\n> > \n> > > On Fri, 9 Nov 2007, Kristian Høgsberg wrote:\n> > > \n> > > > This fixes the race in the last test int t3700.\n> > > \n> > > Well, it is not a race.  My fault.  I thought it was.\n> > > \n> > > What you basically did was to make sure that the index is up-to-date \n> > > after adding the files.  You might even want to say that in the commit \n> > > message, and only then say that it fixes t3700, too.\n> > \n> > OK, I guess what I was wondering was why write_cache() doesn't write out\n> > an up-to-date index.\n> \n> write_cache() only writes the index, it does not update it.\n> \n> > Do we need a call to refresh_cache() when we update the user cache but \n> > commit an index created from read_tree+add_files?  I.e. after the \n> > add_files_to_index() call on line 97?  The shell script doesn't do this, \n> > it only runs update-index --refresh for the index that gets committed.\n> \n> I think it would be sane to do so.\n> \n> IIUC this basically means that \"git add <file> && git commit\" should do \n> the same to the cache as \"git commit <file>\".\n\nNo, that's equivalent to \"git commit -i <file>\".  If you just say \"git\ncommit <file>\", that will create a temporary index initialized to HEAD,\nadd file to that index and the regular (user) index, and then commit the\ntemporary index file.  The shell script doesn't refresh the regular\nindex in this case.  I think it makes sense to add that, but it will be\na subtle difference in behaviour.\n\ncheers,\nKristian\n"},{"id":"59106","messageId":"7vhcjvtgz5.fsf@gitster.siamese.dyndns.org","threadId":"10771","inReplyTo":"Pine.LNX.4.64.0711091702190.4362@racer.site","subject":"Re: [PATCH] builtin-commit: Refresh cache after adding files.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-11-09T18:24:14Z","receivedAt":"2007-11-09T18:24:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> On Fri, 9 Nov 2007, Kristian Høgsberg wrote:\n>\n>> This fixes the race in the last test int t3700.\n>\n> Well, it is not a race.  My fault.  I thought it was.\n>\n> What you basically did was to make sure that the index is up-to-date after \n> adding the files.  You might even want to say that in the commit message, \n> and only then say that it fixes t3700, too.\n\nAh, it all makes sense.  I should have been more relaxed and\ncareful when I first read your t3700 patch.\n\nIf this were a breakage in racy-git-avoidance code, then\nrefresh_cache() Kristian added (or \"add --refresh\" immediately\nafter \"git commit\" in the test script) would have been fooled by\nthe same raciness and wouldn't have changed a thing.\n\nThis discussion exposes a problem with add_files_to_cache()\nfunction.\n\nTry this in a clean repository that tracks Makefile:\n\n\t$ git diff-files --name-only    ;# no output\n        $ touch Makefile\n        $ git diff-files --name-only\n\tMakefile\n        $ git add -u\n        $ git diff-files --name-only\n\tMakefile\n        $ git add Makefile\n        $ git diff-files --name-only    ;# no output\n\nI think this is a broken behaviour.  As long as we are adding\nthe contents from a path on the filesystem to the corresponding\nlocation in the index, it should sync the stat bits for the\nentry in the index with the filesystem to make the entry\nstat-clean.  IOW, we should not see stat-dirtiness reported\nafter \"add -u\".\n\nThe funny thing is that add_files_to_cache() run by \"git add -u\"\ncalls add_file_to_cache() to add the updated contents for\ntouched paths, but the latter function is used in the more\nexplicit \"git add Makefile\" codepath, without any magic\npostprocessing after the function returns to sync the stat\ninfo.  IOW, both \"add -u\" and \"add Makefile\" ends up calling\nadd_file_to_cache(\"Makefile\") and I do not see why we are\ngetting different results.\n\nAnd add_file_to_cache(), which calls add_file_to_index() on\nthe_index, does call the fill_stat_cache_info() to sync the stat\ninformation by itself, as it should be.  I am still puzzled with\nthis.\n\nSo I think Kristian's two refresh_cache() do fix the issue, but\nat the same time I _think_ it is a workaround for broken\nadd_files_to_cache() behaviour, which is what we should fix.\n"},{"id":"59112","messageId":"1194633596.30909.18.camel@hinata.boston.redhat.com","threadId":"10771","inReplyTo":"7vhcjvtgz5.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] builtin-commit: Refresh cache after adding files.","fromName":"Kristian Høgsberg","fromEmail":"krh@redhat.com","sentAt":"2007-11-09T18:39:56Z","receivedAt":"2007-11-09T18:39:56Z","isPatch":true,"sender":{"key":"krh@redhat.com","avatar":"https://gravatar.com/avatar/763dee6f9594ac474f725b137a39565792928e583ddf59b32befc2907409027e?d=mp&s=160"},"body":"\nOn Fri, 2007-11-09 at 10:24 -0800, Junio C Hamano wrote:\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > On Fri, 9 Nov 2007, Kristian Høgsberg wrote:\n> >\n> >> This fixes the race in the last test int t3700.\n> >\n> > Well, it is not a race.  My fault.  I thought it was.\n> >\n> > What you basically did was to make sure that the index is up-to-date after \n> > adding the files.  You might even want to say that in the commit message, \n> > and only then say that it fixes t3700, too.\n> \n> Ah, it all makes sense.  I should have been more relaxed and\n> careful when I first read your t3700 patch.\n> \n> If this were a breakage in racy-git-avoidance code, then\n> refresh_cache() Kristian added (or \"add --refresh\" immediately\n> after \"git commit\" in the test script) would have been fooled by\n> the same raciness and wouldn't have changed a thing.\n> \n> This discussion exposes a problem with add_files_to_cache()\n> function.\n> \n> Try this in a clean repository that tracks Makefile:\n> \n> \t$ git diff-files --name-only    ;# no output\n>         $ touch Makefile\n>         $ git diff-files --name-only\n> \tMakefile\n>         $ git add -u\n>         $ git diff-files --name-only\n> \tMakefile\n>         $ git add Makefile\n>         $ git diff-files --name-only    ;# no output\n> \n> I think this is a broken behaviour.  As long as we are adding\n> the contents from a path on the filesystem to the corresponding\n> location in the index, it should sync the stat bits for the\n> entry in the index with the filesystem to make the entry\n> stat-clean.  IOW, we should not see stat-dirtiness reported\n> after \"add -u\".\n\nYup, that's what I expected, and why I couldn't quite understand why the\nrefresh was necessary.\n\n> The funny thing is that add_files_to_cache() run by \"git add -u\"\n> calls add_file_to_cache() to add the updated contents for\n> touched paths, but the latter function is used in the more\n> explicit \"git add Makefile\" codepath, without any magic\n> postprocessing after the function returns to sync the stat\n> info.  IOW, both \"add -u\" and \"add Makefile\" ends up calling\n> add_file_to_cache(\"Makefile\") and I do not see why we are\n> getting different results.\n\nThere is some timing involved in this, which may explain the different\nresults you see.  On my laptop I can trigger the add_files_to_cache()\nproblem roughly 4 out of 5 times, but on my faster desktop, it can't\ntrigger it.\n\n> And add_file_to_cache(), which calls add_file_to_index() on\n> the_index, does call the fill_stat_cache_info() to sync the stat\n> information by itself, as it should be.  I am still puzzled with\n> this.\n> \n> So I think Kristian's two refresh_cache() do fix the issue, but\n> at the same time I _think_ it is a workaround for broken\n> add_files_to_cache() behaviour, which is what we should fix.\n\nOK, I'll resend the patch with a better description, and add a refresh\ncall for the user index too, for the git commit <file> case.\n\ncheers,\nKristian\n"},{"id":"59160","messageId":"7v640ari76.fsf@gitster.siamese.dyndns.org","threadId":"10771","inReplyTo":"7vhcjvtgz5.fsf@gitster.siamese.dyndns.org","subject":"Re: *[PATCH] builtin-commit: Refresh cache after adding files.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-11-10T01:40:45Z","receivedAt":"2007-11-10T01:40:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> This discussion exposes a problem with add_files_to_cache()\n> function.\n> ...\n> And add_file_to_cache(), which calls add_file_to_index() on\n> the_index, does call the fill_stat_cache_info() to sync the stat\n> information by itself, as it should be.  I am still puzzled with\n> this.\n\nBlech.  I think it is 0781b8a9b2fe760fc4ed519a3a26e4b9bd6ccffe\n(add_file_to_index: skip rehashing if the cached stat already\nmatches) that broke \"git add\".  I get the same problem not just\nwith \"git add -u\" but also with an explicit \"git add Makefile\".\n\nIn add_file_to_index(), we check if ie_modified() says if the\npath is modified, but that is actually a very wrong check in\nthat function.  ie_modified() knows the racy git condition and\ndigs deeper to find if the file in the work tree is really\ndifferent.  We end up saying \"ah, Ok, if that is the same, then\nwe do not add it to the index again\", which is why we do not\nupdate the stat info in this case.\n\nInstead, we should ask ie_match_stat() which answers \"does not\nmatch\" for a entry that _could_ be racily clean, so that we make\nsure we re-add such an entry to clear the stat info.\n\nThis applies to maint and all the way up.\n\nI also suspect that run_diff_files() that is called from\nbuiltin-add.c:update() needs to tell ie_match_stat() to assume\nthat a racily clean path is unclean (that is what bit (1<<1) of\nthe third parameter to ie_match_stat() is about), so that \"add\n-u\" will call the add_file_to_index() function, but that would\nbe a bigger patch.\n\n---\n\n read-cache.c |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/read-cache.c b/read-cache.c\nindex 928e8fa..75e2d46 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -420,7 +420,7 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)\n \tpos = index_name_pos(istate, ce->name, namelen);\n \tif (0 <= pos &&\n \t    !ce_stage(istate->cache[pos]) &&\n-\t    !ie_modified(istate, istate->cache[pos], &st, 1)) {\n+\t    !ie_match_stat(istate, istate->cache[pos], &st, 3)) {\n \t\t/* Nothing changed, really */\n \t\tfree(ce);\n \t\treturn 0;\n"},{"id":"59162","messageId":"Pine.LNX.4.64.0711100201310.4362@racer.site","threadId":"10771","inReplyTo":"7v640ari76.fsf@gitster.siamese.dyndns.org","subject":"Re: *[PATCH] builtin-commit: Refresh cache after adding files.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-11-10T02:02:31Z","receivedAt":"2007-11-10T02:02:31Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Fri, 9 Nov 2007, Junio C Hamano wrote:\n\n> Junio C Hamano <gitster@pobox.com> writes:\n> \n> > This discussion exposes a problem with add_files_to_cache()\n> > function.\n> > ...\n> > And add_file_to_cache(), which calls add_file_to_index() on\n> > the_index, does call the fill_stat_cache_info() to sync the stat\n> > information by itself, as it should be.  I am still puzzled with\n> > this.\n> \n> Blech.  I think it is 0781b8a9b2fe760fc4ed519a3a26e4b9bd6ccffe \n> (add_file_to_index: skip rehashing if the cached stat already matches) \n> that broke \"git add\".  I get the same problem not just with \"git add -u\" \n> but also with an explicit \"git add Makefile\".\n\nBut I think that we still need to refresh the index.  It outputs which \nfiles names were new, and which ones were removed.  Otherwise git commit \nwould be eerily silent on those.\n\nCiao,\nDscho\n"},{"id":"59163","messageId":"7vzlxmq1oj.fsf_-_@gitster.siamese.dyndns.org","threadId":"10771","inReplyTo":"7vhcjvtgz5.fsf@gitster.siamese.dyndns.org","subject":"[PATCH] git-add: make the entry stat-clean after re-adding the same contents","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-11-10T02:22:52Z","receivedAt":"2007-11-10T02:22:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Earlier in commit 0781b8a9b2fe760fc4ed519a3a26e4b9bd6ccffe\n(add_file_to_index: skip rehashing if the cached stat already\nmatches), add_file_to_index() were taught not to re-add the path\nif it already matches the index.\n\nThe change meant well, but was not executed quite right.  It\nused ie_modified() to see if the file on the work tree is really\ndifferent from the index, and skipped adding the contents if the\nfunction says \"not modified\".\n\nThis was wrong.  There are three possible comparison results\nbetween the index and the file in the work tree:\n\n - with lstat(2) we _know_ they are different.  E.g. if the\n   length or the owner in the cached stat information is\n   different from the length we just obtained from lstat(2), we\n   can tell the file is modified without looking at the actual\n   contents.\n\n - with lstat(2) we _know_ they are the same.  The same length,\n   the same owner, the same everything (but this has a twist, as\n   described below).\n\n - we cannot tell from lstat(2) information alone and need to go\n   to the filesystem to actually compare.\n\nThe last case arises from what we call 'racy git' situation,\nthat can be caused with this sequence:\n\n    $ echo hello >file\n    $ git add file\n    $ echo aeiou >file ;# the same length\n\nIf the second \"echo\" is done within the same filesystem\ntimestamp granularity as the first \"echo\", then the timestamp\nrecorded by \"git add\" and the timestamp we get from lstat(2)\nwill be the same, and we can mistakenly say the file is not\nmodified.  The path is called 'racily clean'.  We need to\nreliably detect racily clean paths are in fact modified.\n\nTo solve this problem, when we write out the index, we mark the\nindex entry that has the same timestamp as the index file itself\n(that is the time from the point of view of the filesystem) to\ntell any later code that does the lstat(2) comparison not to\ntrust the cached stat info, and ie_modified() then actually goes\nto the filesystem to compare the contents for such a path.\n\nThat's all good, but it should not be used for this \"git add\"\noptimization, as the goal of \"git add\" is to actually update the\npath in the index and make it stat-clean.  With the false\noptimization, we did _not_ cause any data loss (after all, what\nwe failed to do was only to update the cached stat information),\nbut it made the following sequence leave the file stat dirty:\n\n    $ echo hello >file\n    $ git add file\n    $ echo hello >file ;# the same contents\n    $ git add file\n\nThe solution is not to use ie_modified() which goes to the\nfilesystem to see if it is really clean, but instead use\nie_match_stat() with \"assume racily clean paths are dirty\"\noption, to force re-adding of such a path.\n\nThere was another problem with \"git add -u\".  The codepath\nshares the same issue when adding the paths that are found to be\nmodified, but in addition, it asked \"git diff-files\" machinery\nrun_diff_files() function (which is \"git diff-files\") to list\nthe paths that are modified.  But \"git diff-files\" machinery\nuses the same ie_modified() call so that it does not report\nracily clean _and_ actually clean paths as modified, which is\nnot what we want.\n\nThe patch allows the callers of run_diff_files() to pass the\nsame \"assume racily clean paths are dirty\" option, and makes\n\"git-add -u\" codepath to use that option, to discover and re-add\nracily clean _and_ actually clean paths.\n\nWe could further optimize on top of this patch to differentiate\nthe case where the path really needs re-adding (i.e. the content\nof the racily clean entry was indeed different) and the case\nwhere only the cached stat information needs to be refreshed\n(i.e. the racily clean entry was actually clean), but I do not\nthink it is worth it.\n\nThis patch applies to maint and all the way up.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n\n * Blech.  This took longer than I thought.  It's only cached\n   stat info and does not cause real data breakage, but I think\n   it is a good thing to fix nevertheless.\n\n builtin-add.c |    2 +-\n diff-lib.c    |    6 ++++--\n diff.h        |    2 +-\n read-cache.c  |    2 +-\n 4 files changed, 7 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin-add.c b/builtin-add.c\nindex 373f87f..b6d6bbe 100644\n--- a/builtin-add.c\n+++ b/builtin-add.c\n@@ -123,7 +123,7 @@ static void update(int verbose, const char *prefix, const char **files)\n \trev.diffopt.format_callback_data = &verbose;\n \tif (read_cache() < 0)\n \t\tdie(\"index file corrupt\");\n-\trun_diff_files(&rev, 0);\n+\trun_diff_files(&rev, 2);\n }\n \n static void refresh(int verbose, const char **pathspec)\ndiff --git a/diff-lib.c b/diff-lib.c\nindex da55713..b83f650 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -332,10 +332,12 @@ int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv)\n \treturn run_diff_files(revs, silent_on_removed);\n }\n \n-int run_diff_files(struct rev_info *revs, int silent_on_removed)\n+int run_diff_files(struct rev_info *revs, int option)\n {\n \tint entries, i;\n \tint diff_unmerged_stage = revs->max_count;\n+\tint silent_on_removed = option & 01;\n+\tint assume_racy_is_modified = option & 02;\n \n \tif (diff_unmerged_stage < 0)\n \t\tdiff_unmerged_stage = 2;\n@@ -441,7 +443,7 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)\n \t\t\t\t       ce->sha1, ce->name, NULL);\n \t\t\tcontinue;\n \t\t}\n-\t\tchanged = ce_match_stat(ce, &st, 0);\n+\t\tchanged = ce_match_stat(ce, &st, assume_racy_is_modified);\n \t\tif (!changed && !revs->diffopt.find_copies_harder)\n \t\t\tcontinue;\n \t\toldmode = ntohl(ce->ce_mode);\ndiff --git a/diff.h b/diff.h\nindex 4546aad..040e18c 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -224,7 +224,7 @@ extern void diff_flush(struct diff_options*);\n \n extern const char *diff_unique_abbrev(const unsigned char *, int);\n \n-extern int run_diff_files(struct rev_info *revs, int silent_on_removed);\n+extern int run_diff_files(struct rev_info *revs, int option);\n extern int setup_diff_no_index(struct rev_info *revs,\n \t\tint argc, const char ** argv, int nongit, const char *prefix);\n extern int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv);\ndiff --git a/read-cache.c b/read-cache.c\nindex 928e8fa..75e2d46 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -420,7 +420,7 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)\n \tpos = index_name_pos(istate, ce->name, namelen);\n \tif (0 <= pos &&\n \t    !ce_stage(istate->cache[pos]) &&\n-\t    !ie_modified(istate, istate->cache[pos], &st, 1)) {\n+\t    !ie_match_stat(istate, istate->cache[pos], &st, 3)) {\n \t\t/* Nothing changed, really */\n \t\tfree(ce);\n \t\treturn 0;\n-- \n1.5.3.5.1651.g30bf0\n"},{"id":"59164","messageId":"7vve8aq1io.fsf@gitster.siamese.dyndns.org","threadId":"10771","inReplyTo":"Pine.LNX.4.64.0711100201310.4362@racer.site","subject":"Re: *[PATCH] builtin-commit: Refresh cache after adding files.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-11-10T02:26:23Z","receivedAt":"2007-11-10T02:26:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> But I think that we still need to refresh the index.\n\nYou are correct.  See\n\n    http://article.gmane.org/gmane.comp.version-control.git/64254\n"},{"id":"59165","messageId":"alpine.LFD.0.999.0711091840120.15101@woody.linux-foundation.org","threadId":"10771","inReplyTo":"7vzlxmq1oj.fsf_-_@gitster.siamese.dyndns.org","subject":"Re: [PATCH] git-add: make the entry stat-clean after re-adding the same contents","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-11-10T02:43:07Z","receivedAt":"2007-11-10T02:43:07Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Fri, 9 Nov 2007, Junio C Hamano wrote:\n>  \n> -int run_diff_files(struct rev_info *revs, int silent_on_removed)\n> +int run_diff_files(struct rev_info *revs, int option)\n\nWouldn't it be much better to now \n - make it \"unsigned int flags\"\n - create a few enums or #define's to make the usage be more readable?\n\nBecause this:\n\n>-       run_diff_files(&rev, 0);\n>+       run_diff_files(&rev, 2);\n> -\t    !ie_modified(istate, istate->cache[pos], &st, 1)) {\n> +\t    !ie_match_stat(istate, istate->cache[pos], &st, 3)) {\n\njust went from subtle to \"incredibly non-obvious\".\n\nI realize that 3 is \"silent + assume_racy\" and 2 is just \"assume_racy\", \nbut I may realize that now, and in a month? I'd need to look it up.\n\n\t\tLinus\n"},{"id":"59180","messageId":"7v1waypj82.fsf_-_@gitster.siamese.dyndns.org","threadId":"10771","inReplyTo":"alpine.LFD.0.999.0711091840120.15101@woody.linux-foundation.org","subject":"[PATCH 1/2] ce_match_stat, run_diff_files: use symbolic constants for readability","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-11-10T09:01:33Z","receivedAt":"2007-11-10T09:01:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"ce_match_stat() can be told:\n\n (1) to ignore CE_VALID bit (used under \"assume unchanged\" mode)\n     and perform the stat comparison anyway;\n\n (2) not to perform the contents comparison for racily clean\n     entries and report mismatch of cached stat information;\n\nusing its \"option\" parameter.  Give them symbolic constants.\n\nSimilarly, run_diff_files() can be told not to report anything\non removed paths.  Also give it a symbolic constant for that.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n\n Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n > On Fri, 9 Nov 2007, Junio C Hamano wrote:\n >>  \n >> -int run_diff_files(struct rev_info *revs, int silent_on_removed)\n >> +int run_diff_files(struct rev_info *revs, int option)\n >\n > Wouldn't it be much better to now \n >  - make it \"unsigned int flags\"\n >  - create a few enums or #define's to make the usage be more readable?\n >\n > Because this:\n >\n >>-       run_diff_files(&rev, 0);\n >>+       run_diff_files(&rev, 2);\n >> -\t    !ie_modified(istate, istate->cache[pos], &st, 1)) {\n >> +\t    !ie_match_stat(istate, istate->cache[pos], &st, 3)) {\n >\n > just went from subtle to \"incredibly non-obvious\".\n\n That really is true.  Apparently I am getting much slower\n lately.  This is to just introduce the constants and change the\n types.\n\n builtin-apply.c |    2 +-\n cache.h         |   14 ++++++++++----\n check-racy.c    |    2 +-\n diff-lib.c      |   16 +++++++++-------\n diff.h          |    4 +++-\n entry.c         |    2 +-\n read-cache.c    |   47 ++++++++++++++++++++++++++++++-----------------\n unpack-trees.c  |    4 ++--\n 8 files changed, 57 insertions(+), 34 deletions(-)\n\ndiff --git a/builtin-apply.c b/builtin-apply.c\nindex 5cc90e6..0fff02e 100644\n--- a/builtin-apply.c\n+++ b/builtin-apply.c\n@@ -2099,7 +2099,7 @@ static int verify_index_match(struct cache_entry *ce, struct stat *st)\n \t\t\treturn -1;\n \t\treturn 0;\n \t}\n-\treturn ce_match_stat(ce, st, 1);\n+\treturn ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID);\n }\n \n static int check_patch(struct patch *patch, struct patch *prev_patch)\ndiff --git a/cache.h b/cache.h\nindex fc195bc..31af16a 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -174,8 +174,8 @@ extern struct index_state the_index;\n #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))\n #define add_file_to_cache(path, verbose) add_file_to_index(&the_index, (path), (verbose))\n #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL)\n-#define ce_match_stat(ce, st, really) ie_match_stat(&the_index, (ce), (st), (really))\n-#define ce_modified(ce, st, really) ie_modified(&the_index, (ce), (st), (really))\n+#define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))\n+#define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))\n #endif\n \n enum object_type {\n@@ -266,8 +266,14 @@ extern int remove_file_from_index(struct index_state *, const char *path);\n extern int add_file_to_index(struct index_state *, const char *path, int verbose);\n extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);\n extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);\n-extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat *, int);\n-extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, int);\n+\n+/* do stat comparison even if CE_VALID is true */\n+#define CE_MATCH_IGNORE_VALID\t\t01\n+/* do not check the contents but report dirty on racily-clean entries */\n+#define CE_MATCH_RACY_IS_DIRTY\t02\n+extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat *, unsigned int);\n+extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, unsigned int);\n+\n extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);\n extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);\n extern int read_fd(int fd, char **return_buf, unsigned long *return_size);\ndiff --git a/check-racy.c b/check-racy.c\nindex d6a08b4..00d92a1 100644\n--- a/check-racy.c\n+++ b/check-racy.c\n@@ -18,7 +18,7 @@ int main(int ac, char **av)\n \n \t\tif (ce_match_stat(ce, &st, 0))\n \t\t\tdirty++;\n-\t\telse if (ce_match_stat(ce, &st, 2))\n+\t\telse if (ce_match_stat(ce, &st, CE_MATCH_RACY_IS_DIRTY))\n \t\t\tracy++;\n \t\telse\n \t\t\tclean++;\ndiff --git a/diff-lib.c b/diff-lib.c\nindex da55713..9f8afbe 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -173,9 +173,10 @@ static int is_in_index(const char *path)\n }\n \n static int handle_diff_files_args(struct rev_info *revs,\n-\t\tint argc, const char **argv, int *silent)\n+\t\t\t\t  int argc, const char **argv,\n+\t\t\t\t  unsigned int *options)\n {\n-\t*silent = 0;\n+\t*options = 0;\n \n \t/* revs->max_count == -2 means --no-index */\n \twhile (1 < argc && argv[1][0] == '-') {\n@@ -192,7 +193,7 @@ static int handle_diff_files_args(struct rev_info *revs,\n \t\t\trevs->diffopt.no_index = 1;\n \t\t}\n \t\telse if (!strcmp(argv[1], \"-q\"))\n-\t\t\t*silent = 1;\n+\t\t\t*options |= DIFF_SILENT_ON_REMOVED;\n \t\telse\n \t\t\treturn error(\"invalid option: %s\", argv[1]);\n \t\targv++; argc--;\n@@ -305,9 +306,9 @@ int setup_diff_no_index(struct rev_info *revs,\n \n int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv)\n {\n-\tint silent_on_removed;\n+\tunsigned int options;\n \n-\tif (handle_diff_files_args(revs, argc, argv, &silent_on_removed))\n+\tif (handle_diff_files_args(revs, argc, argv, &options))\n \t\treturn -1;\n \n \tif (revs->diffopt.no_index) {\n@@ -329,13 +330,14 @@ int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv)\n \t\tperror(\"read_cache\");\n \t\treturn -1;\n \t}\n-\treturn run_diff_files(revs, silent_on_removed);\n+\treturn run_diff_files(revs, options);\n }\n \n-int run_diff_files(struct rev_info *revs, int silent_on_removed)\n+int run_diff_files(struct rev_info *revs, unsigned int option)\n {\n \tint entries, i;\n \tint diff_unmerged_stage = revs->max_count;\n+\tint silent_on_removed = option & DIFF_SILENT_ON_REMOVED;\n \n \tif (diff_unmerged_stage < 0)\n \t\tdiff_unmerged_stage = 2;\ndiff --git a/diff.h b/diff.h\nindex 4546aad..de533da 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -224,7 +224,9 @@ extern void diff_flush(struct diff_options*);\n \n extern const char *diff_unique_abbrev(const unsigned char *, int);\n \n-extern int run_diff_files(struct rev_info *revs, int silent_on_removed);\n+/* do not report anything on removed paths */\n+#define DIFF_SILENT_ON_REMOVED 01\n+extern int run_diff_files(struct rev_info *revs, unsigned int option);\n extern int setup_diff_no_index(struct rev_info *revs,\n \t\tint argc, const char ** argv, int nongit, const char *prefix);\n extern int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv);\ndiff --git a/entry.c b/entry.c\nindex fc3a506..ef88f62 100644\n--- a/entry.c\n+++ b/entry.c\n@@ -200,7 +200,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t\n \tstrcpy(path + len, ce->name);\n \n \tif (!lstat(path, &st)) {\n-\t\tunsigned changed = ce_match_stat(ce, &st, 1);\n+\t\tunsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID);\n \t\tif (!changed)\n \t\t\treturn 0;\n \t\tif (!state->force) {\ndiff --git a/read-cache.c b/read-cache.c\nindex 928e8fa..9e4d4a9 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -194,11 +194,12 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)\n }\n \n int ie_match_stat(struct index_state *istate,\n-\t\t  struct cache_entry *ce, struct stat *st, int options)\n+\t\t  struct cache_entry *ce, struct stat *st,\n+\t\t  unsigned int options)\n {\n \tunsigned int changed;\n-\tint ignore_valid = options & 01;\n-\tint assume_racy_is_modified = options & 02;\n+\tint ignore_valid = options & CE_MATCH_IGNORE_VALID;\n+\tint assume_racy_is_modified = options & CE_MATCH_RACY_IS_DIRTY;\n \n \t/*\n \t * If it's marked as always valid in the index, it's\n@@ -238,10 +239,11 @@ int ie_match_stat(struct index_state *istate,\n }\n \n int ie_modified(struct index_state *istate,\n-\t\tstruct cache_entry *ce, struct stat *st, int really)\n+\t\tstruct cache_entry *ce, struct stat *st, unsigned int options)\n {\n \tint changed, changed_fs;\n-\tchanged = ie_match_stat(istate, ce, st, really);\n+\n+\tchanged = ie_match_stat(istate, ce, st, options);\n \tif (!changed)\n \t\treturn 0;\n \t/*\n@@ -420,7 +422,7 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)\n \tpos = index_name_pos(istate, ce->name, namelen);\n \tif (0 <= pos &&\n \t    !ce_stage(istate->cache[pos]) &&\n-\t    !ie_modified(istate, istate->cache[pos], &st, 1)) {\n+\t    !ie_modified(istate, istate->cache[pos], &st, CE_MATCH_IGNORE_VALID)) {\n \t\t/* Nothing changed, really */\n \t\tfree(ce);\n \t\treturn 0;\n@@ -782,11 +784,13 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti\n  * to link up the stat cache details with the proper files.\n  */\n static struct cache_entry *refresh_cache_ent(struct index_state *istate,\n-\t\t\t\t\t     struct cache_entry *ce, int really, int *err)\n+\t\t\t\t\t     struct cache_entry *ce,\n+\t\t\t\t\t     unsigned int options, int *err)\n {\n \tstruct stat st;\n \tstruct cache_entry *updated;\n \tint changed, size;\n+\tint ignore_valid = options & CE_MATCH_IGNORE_VALID;\n \n \tif (lstat(ce->name, &st) < 0) {\n \t\tif (err)\n@@ -794,16 +798,23 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,\n \t\treturn NULL;\n \t}\n \n-\tchanged = ie_match_stat(istate, ce, &st, really);\n+\tchanged = ie_match_stat(istate, ce, &st, options);\n \tif (!changed) {\n-\t\tif (really && assume_unchanged &&\n+\t\t/*\n+\t\t * The path is unchanged.  If we were told to ignore\n+\t\t * valid bit, then we did the actual stat check and\n+\t\t * found that the entry is unmodified.  If the entry\n+\t\t * is not marked VALID, this is the place to mark it\n+\t\t * valid again, under \"assume unchanged\" mode.\n+\t\t */\n+\t\tif (ignore_valid && assume_unchanged &&\n \t\t    !(ce->ce_flags & htons(CE_VALID)))\n \t\t\t; /* mark this one VALID again */\n \t\telse\n \t\t\treturn ce;\n \t}\n \n-\tif (ie_modified(istate, ce, &st, really)) {\n+\tif (ie_modified(istate, ce, &st, options)) {\n \t\tif (err)\n \t\t\t*err = EINVAL;\n \t\treturn NULL;\n@@ -814,13 +825,14 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,\n \tmemcpy(updated, ce, size);\n \tfill_stat_cache_info(updated, &st);\n \n-\t/* In this case, if really is not set, we should leave\n-\t * CE_VALID bit alone.  Otherwise, paths marked with\n-\t * --no-assume-unchanged (i.e. things to be edited) will\n-\t * reacquire CE_VALID bit automatically, which is not\n-\t * really what we want.\n+\t/*\n+\t * If ignore_valid is not set, we should leave CE_VALID bit\n+\t * alone.  Otherwise, paths marked with --no-assume-unchanged\n+\t * (i.e. things to be edited) will reacquire CE_VALID bit\n+\t * automatically, which is not really what we want.\n \t */\n-\tif (!really && assume_unchanged && !(ce->ce_flags & htons(CE_VALID)))\n+\tif (!ignore_valid && assume_unchanged &&\n+\t    !(ce->ce_flags & htons(CE_VALID)))\n \t\tupdated->ce_flags &= ~htons(CE_VALID);\n \n \treturn updated;\n@@ -834,6 +846,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p\n \tint allow_unmerged = (flags & REFRESH_UNMERGED) != 0;\n \tint quiet = (flags & REFRESH_QUIET) != 0;\n \tint not_new = (flags & REFRESH_IGNORE_MISSING) != 0;\n+\tunsigned int options = really ? CE_MATCH_IGNORE_VALID : 0;\n \n \tfor (i = 0; i < istate->cache_nr; i++) {\n \t\tstruct cache_entry *ce, *new;\n@@ -855,7 +868,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p\n \t\tif (pathspec && !match_pathspec(pathspec, ce->name, strlen(ce->name), 0, seen))\n \t\t\tcontinue;\n \n-\t\tnew = refresh_cache_ent(istate, ce, really, &cache_errno);\n+\t\tnew = refresh_cache_ent(istate, ce, options, &cache_errno);\n \t\tif (new == ce)\n \t\t\tcontinue;\n \t\tif (!new) {\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex ccfeb6e..9411c67 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -406,7 +406,7 @@ static void verify_uptodate(struct cache_entry *ce,\n \t\treturn;\n \n \tif (!lstat(ce->name, &st)) {\n-\t\tunsigned changed = ce_match_stat(ce, &st, 1);\n+\t\tunsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID);\n \t\tif (!changed)\n \t\t\treturn;\n \t\t/*\n@@ -927,7 +927,7 @@ int oneway_merge(struct cache_entry **src,\n \t\tif (o->reset) {\n \t\t\tstruct stat st;\n \t\t\tif (lstat(old->name, &st) ||\n-\t\t\t    ce_match_stat(old, &st, 1))\n+\t\t\t    ce_match_stat(old, &st, CE_MATCH_IGNORE_VALID))\n \t\t\t\told->ce_flags |= htons(CE_UPDATE);\n \t\t}\n \t\treturn keep_entry(old, o);\n-- \n1.5.3.5.1651.g30bf0\n"},{"id":"59181","messageId":"7vve8ao4m2.fsf_-_@gitster.siamese.dyndns.org","threadId":"10771","inReplyTo":"alpine.LFD.0.999.0711091840120.15101@woody.linux-foundation.org","subject":"[PATCH 2/2] git-add: make the entry stat-clean after re-adding the same contents","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-11-10T09:02:29Z","receivedAt":"2007-11-10T09:02:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Earlier in commit 0781b8a9b2fe760fc4ed519a3a26e4b9bd6ccffe\n(add_file_to_index: skip rehashing if the cached stat already\nmatches), add_file_to_index() were taught not to re-add the path\nif it already matches the index.\n\nThe change meant well, but was not executed quite right.  It\nused ie_modified() to see if the file on the work tree is really\ndifferent from the index, and skipped adding the contents if the\nfunction says \"not modified\".\n\nThis was wrong.  There are three possible comparison results\nbetween the index and the file in the work tree:\n\n - with lstat(2) we _know_ they are different.  E.g. if the\n   length or the owner in the cached stat information is\n   different from the length we just obtained from lstat(2), we\n   can tell the file is modified without looking at the actual\n   contents.\n\n - with lstat(2) we _know_ they are the same.  The same length,\n   the same owner, the same everything (but this has a twist, as\n   described below).\n\n - we cannot tell from lstat(2) information alone and need to go\n   to the filesystem to actually compare.\n\nThe last case arises from what we call 'racy git' situation,\nthat can be caused with this sequence:\n\n    $ echo hello >file\n    $ git add file\n    $ echo aeiou >file ;# the same length\n\nIf the second \"echo\" is done within the same filesystem\ntimestamp granularity as the first \"echo\", then the timestamp\nrecorded by \"git add\" and the timestamp we get from lstat(2)\nwill be the same, and we can mistakenly say the file is not\nmodified.  The path is called 'racily clean'.  We need to\nreliably detect racily clean paths are in fact modified.\n\nTo solve this problem, when we write out the index, we mark the\nindex entry that has the same timestamp as the index file itself\n(that is the time from the point of view of the filesystem) to\ntell any later code that does the lstat(2) comparison not to\ntrust the cached stat info, and ie_modified() then actually goes\nto the filesystem to compare the contents for such a path.\n\nThat's all good, but it should not be used for this \"git add\"\noptimization, as the goal of \"git add\" is to actually update the\npath in the index and make it stat-clean.  With the false\noptimization, we did _not_ cause any data loss (after all, what\nwe failed to do was only to update the cached stat information),\nbut it made the following sequence leave the file stat dirty:\n\n    $ echo hello >file\n    $ git add file\n    $ echo hello >file ;# the same contents\n    $ git add file\n\nThe solution is not to use ie_modified() which goes to the\nfilesystem to see if it is really clean, but instead use\nie_match_stat() with \"assume racily clean paths are dirty\"\noption, to force re-adding of such a path.\n\nThere was another problem with \"git add -u\".  The codepath\nshares the same issue when adding the paths that are found to be\nmodified, but in addition, it asked \"git diff-files\" machinery\nrun_diff_files() function (which is \"git diff-files\") to list\nthe paths that are modified.  But \"git diff-files\" machinery\nuses the same ie_modified() call so that it does not report\nracily clean _and_ actually clean paths as modified, which is\nnot what we want.\n\nThe patch allows the callers of run_diff_files() to pass the\nsame \"assume racily clean paths are dirty\" option, and makes\n\"git-add -u\" codepath to use that option, to discover and re-add\nracily clean _and_ actually clean paths.\n\nWe could further optimize on top of this patch to differentiate\nthe case where the path really needs re-adding (i.e. the content\nof the racily clean entry was indeed different) and the case\nwhere only the cached stat information needs to be refreshed\n(i.e. the racily clean entry was actually clean), but I do not\nthink it is worth it.\n\nThis patch applies to maint and all the way up.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n\n * And this is a respin of the earlier fix, using the new\n   constants.\n\n builtin-add.c |    2 +-\n diff-lib.c    |    4 +++-\n diff.h        |    2 ++\n read-cache.c  |    3 ++-\n 4 files changed, 8 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin-add.c b/builtin-add.c\nindex 373f87f..e072320 100644\n--- a/builtin-add.c\n+++ b/builtin-add.c\n@@ -123,7 +123,7 @@ static void update(int verbose, const char *prefix, const char **files)\n \trev.diffopt.format_callback_data = &verbose;\n \tif (read_cache() < 0)\n \t\tdie(\"index file corrupt\");\n-\trun_diff_files(&rev, 0);\n+\trun_diff_files(&rev, DIFF_RACY_IS_MODIFIED);\n }\n \n static void refresh(int verbose, const char **pathspec)\ndiff --git a/diff-lib.c b/diff-lib.c\nindex 9f8afbe..ec1b5e3 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -338,6 +338,8 @@ int run_diff_files(struct rev_info *revs, unsigned int option)\n \tint entries, i;\n \tint diff_unmerged_stage = revs->max_count;\n \tint silent_on_removed = option & DIFF_SILENT_ON_REMOVED;\n+\tunsigned ce_option = ((option & DIFF_RACY_IS_MODIFIED)\n+\t\t\t      ? CE_MATCH_RACY_IS_DIRTY : 0);\n \n \tif (diff_unmerged_stage < 0)\n \t\tdiff_unmerged_stage = 2;\n@@ -443,7 +445,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)\n \t\t\t\t       ce->sha1, ce->name, NULL);\n \t\t\tcontinue;\n \t\t}\n-\t\tchanged = ce_match_stat(ce, &st, 0);\n+\t\tchanged = ce_match_stat(ce, &st, ce_option);\n \t\tif (!changed && !revs->diffopt.find_copies_harder)\n \t\t\tcontinue;\n \t\toldmode = ntohl(ce->ce_mode);\ndiff --git a/diff.h b/diff.h\nindex de533da..efaa8f7 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -226,6 +226,8 @@ extern const char *diff_unique_abbrev(const unsigned char *, int);\n \n /* do not report anything on removed paths */\n #define DIFF_SILENT_ON_REMOVED 01\n+/* report racily-clean paths as modified */\n+#define DIFF_RACY_IS_MODIFIED 02\n extern int run_diff_files(struct rev_info *revs, unsigned int option);\n extern int setup_diff_no_index(struct rev_info *revs,\n \t\tint argc, const char ** argv, int nongit, const char *prefix);\ndiff --git a/read-cache.c b/read-cache.c\nindex 9e4d4a9..c3dbf89 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -388,6 +388,7 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)\n \tint size, namelen, pos;\n \tstruct stat st;\n \tstruct cache_entry *ce;\n+\tunsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY;\n \n \tif (lstat(path, &st))\n \t\tdie(\"%s: unable to stat (%s)\", path, strerror(errno));\n@@ -422,7 +423,7 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)\n \tpos = index_name_pos(istate, ce->name, namelen);\n \tif (0 <= pos &&\n \t    !ce_stage(istate->cache[pos]) &&\n-\t    !ie_modified(istate, istate->cache[pos], &st, CE_MATCH_IGNORE_VALID)) {\n+\t    !ie_match_stat(istate, istate->cache[pos], &st, ce_option)) {\n \t\t/* Nothing changed, really */\n \t\tfree(ce);\n \t\treturn 0;\n-- \n1.5.3.5.1651.g30bf0\n"}]}