{"thread":{"id":"14488","subject":"[PATCH,RFC] Implement 'git rm --if-missing'","startedAt":"2008-07-16T18:00:50Z","lastAt":"2008-07-16T19:43:27Z","messageCount":6,"participants":["Ciaran McCreesh","Petr Baudis","Avery Pennarun","Junio C Hamano","Peter Baumann","David Christensen"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"83538","messageId":"1216231250-21141-1-git-send-email-ciaran.mccreesh@googlemail.com","threadId":"14488","inReplyTo":null,"subject":"[PATCH,RFC] Implement 'git rm --if-missing'","fromName":"Ciaran McCreesh","fromEmail":"ciaran.mccreesh@googlemail.com","sentAt":"2008-07-16T18:00:50Z","receivedAt":"2008-07-16T18:00:50Z","isPatch":true,"sender":{"key":"ciaran.mccreesh@googlemail.com","avatar":"https://gravatar.com/avatar/2b6d636bccfecedc57b9980bd56beb13679184f4b26bc7239b166ed93e9b210e?d=mp&s=160"},"body":"git rm --if-missing will only remove files if they've already been removed from\ndisk.\n\nSigned-off-by: Ciaran McCreesh <ciaran.mccreesh@googlemail.com>\n---\n\nThere's nothing here that can't be done using git update-index, but git rm\nis less scary.\n\nRegarding exit status: I'm not sure whether exit status should be based upon\nwhether any files were actually removed, or whether it should be based upon\nwhether or not all of the supplied patterns were matched. I've gone for the\nlatter, so that 'git rm --if-missing -r .' succeeds if there's nothing to\nremove.\n\nI'm not sure whether 'missing' is the best word. '--if-noent' might be more\nappropriate, but less familiar to some. Or is this worth a short option?\n\n Documentation/git-rm.txt |    8 +++++++-\n builtin-rm.c             |    9 ++++++++-\n t/t3600-rm.sh            |   43 +++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 58 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt\nindex 4d0c495..f9335f3 100644\n--- a/Documentation/git-rm.txt\n+++ b/Documentation/git-rm.txt\n@@ -7,7 +7,8 @@ git-rm - Remove files from the working tree and from the index\n \n SYNOPSIS\n --------\n-'git rm' [-f] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <file>...\n+'git rm' [-f] [-n] [-r] [--cached] [--ignore-unmatch] [--if-missing]\n+\t  [--quiet] [--] <file>...\n \n DESCRIPTION\n -----------\n@@ -61,6 +62,11 @@ OPTIONS\n --ignore-unmatch::\n \tExit with a zero status even if no files matched.\n \n+--if-missing::\n+\tOnly remove files if they have been removed from disk. Exit status\n+\tis still based upon whether matches succeed, not whether a remove\n+\tactually took place.\n+\n -q::\n --quiet::\n \t'git-rm' normally outputs one line (in the form of an \"rm\" command)\ndiff --git a/builtin-rm.c b/builtin-rm.c\nindex 22c9bd1..4b89705 100644\n--- a/builtin-rm.c\n+++ b/builtin-rm.c\n@@ -125,7 +125,7 @@ static int check_local_mod(unsigned char *head, int index_only)\n static struct lock_file lock_file;\n \n static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;\n-static int ignore_unmatch = 0;\n+static int ignore_unmatch = 0, if_missing = 0;\n \n static struct option builtin_rm_options[] = {\n \tOPT__DRY_RUN(&show_only),\n@@ -135,6 +135,7 @@ static struct option builtin_rm_options[] = {\n \tOPT_BOOLEAN('r', NULL,             &recursive,  \"allow recursive removal\"),\n \tOPT_BOOLEAN( 0 , \"ignore-unmatch\", &ignore_unmatch,\n \t\t\t\t\"exit with a zero status even if nothing matched\"),\n+\tOPT_BOOLEAN( 0 , \"if-missing\",     &if_missing, \"only remove missing files\"),\n \tOPT_END(),\n };\n \n@@ -168,6 +169,12 @@ int cmd_rm(int argc, const char **argv, const char *prefix)\n \t\tstruct cache_entry *ce = active_cache[i];\n \t\tif (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen))\n \t\t\tcontinue;\n+\t\tif (if_missing)\n+\t\t{\n+\t\t\tstruct stat st;\n+\t\t\tif ((lstat(ce->name, &st) == 0) || (errno != ENOENT))\n+\t\t\t\tcontinue;\n+\t\t}\n \t\tadd_list(ce->name);\n \t}\n \ndiff --git a/t/t3600-rm.sh b/t/t3600-rm.sh\nindex f542f0a..c7c1810 100755\n--- a/t/t3600-rm.sh\n+++ b/t/t3600-rm.sh\n@@ -143,6 +143,45 @@ test_expect_success '\"rm\" command suppressed with --quiet' '\n \tgit commit -m \"remove file from rm --quiet test\"\n '\n \n+test_expect_success 'Test that \"rm --if-missing\" works' '\n+\techo frotz > test-file &&\n+\techo frotz > other-file &&\n+\tgit add test-file other-file &&\n+\tgit commit -m \"add files from rm --if-missing test\" &&\n+\trm test-file &&\n+\tgit rm --if-missing test-file other-file &&\n+\t! git ls-files --error-unmatch test-file &&\n+\tgit ls-files --error-unmatch other-file &&\n+\tgit rm other-file &&\n+\tgit commit -m \"remove file from rm --if-missing test\"\n+'\n+\n+test_expect_success 'Test that \"rm --if-missing -r *\" works' '\n+\techo frotz > test-file &&\n+\tmkdir -p frotz &&\n+\techo frotz > frotz/other-file &&\n+\tgit add test-file frotz/other-file &&\n+\tgit commit -m \"add file from rm --if-missing -r * test\" &&\n+\trm frotz/other-file\n+\tgit rm --if-missing --ignore-unmatch -r \\* &&\n+\tgit ls-files --error-unmatch test-file &&\n+\tgit rm test-file &&\n+\tgit commit -m \"remove file from rm --missing -r * test &&\n+\t! test -d frotz\"\n+'\n+\n+test_expect_success 'Test that \"rm --if-missing -r *\" works even if nothing is removed' '\n+\techo frotz > test-file &&\n+\tmkdir -p frotz &&\n+\techo frotz > frotz/other-file &&\n+\tgit add test-file frotz/other-file &&\n+\tgit commit -m \"add file from rm --if-missing -r * test\" &&\n+\tgit rm --if-missing --ignore-unmatch -r \\* &&\n+\tgit rm test-file frotz/other-file &&\n+\tgit commit -m \"remove file from rm --missing -r * test &&\n+\t! test -d frotz\"\n+'\n+\n # Now, failure cases.\n test_expect_success 'Re-add foo and baz' '\n \tgit add foo baz &&\n@@ -217,4 +256,8 @@ test_expect_success 'Remove nonexistent file returns nonzero exit status' '\n \t! git rm nonexistent\n '\n \n+test_expect_success 'Test that \"rm --if-missing nonexistent\" fails' '\n+\t! git rm --if-missing nonexistent\n+'\n+\n test_done\n-- \n1.5.6.3.385.g7c3f1\n"},{"id":"83539","messageId":"20080716180617.GO32184@machine.or.cz","threadId":"14488","inReplyTo":"1216231250-21141-1-git-send-email-ciaran.mccreesh@googlemail.com","subject":"Re: [PATCH,RFC] Implement 'git rm --if-missing'","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2008-07-16T18:06:17Z","receivedAt":"2008-07-16T18:06:17Z","isPatch":true,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"  Hi,\n\nOn Wed, Jul 16, 2008 at 07:00:50PM +0100, Ciaran McCreesh wrote:\n> git rm --if-missing will only remove files if they've already been removed from\n> disk.\n> \n> Signed-off-by: Ciaran McCreesh <ciaran.mccreesh@googlemail.com>\n\n  what is the usage scenario? The porcelain options space is a precious\nresource, so please explain why do you need this and who is going to use\nit (especially with such a long name).\n\n\n\t\t\t\tPetr \"Pasky\" Baudis\n"},{"id":"83542","messageId":"32541b130807161117h54e97825o9119655f73138341@mail.gmail.com","threadId":"14488","inReplyTo":"20080716180617.GO32184@machine.or.cz","subject":"Re: [PATCH,RFC] Implement 'git rm --if-missing'","fromName":"Avery Pennarun","fromEmail":"apenwarr@gmail.com","sentAt":"2008-07-16T18:17:33Z","receivedAt":"2008-07-16T18:17:33Z","isPatch":true,"sender":{"key":"apenwarr@gmail.com","avatar":"https://avatars.githubusercontent.com/u/20592?v=4"},"body":"On 7/16/08, Petr Baudis <pasky@suse.cz> wrote:\n>  On Wed, Jul 16, 2008 at 07:00:50PM +0100, Ciaran McCreesh wrote:\n>  > git rm --if-missing will only remove files if they've already been removed from\n>  > disk.\n>  >\n>  > Signed-off-by: Ciaran McCreesh <ciaran.mccreesh@googlemail.com>\n>\n>   what is the usage scenario? The porcelain options space is a precious\n>  resource, so please explain why do you need this and who is going to use\n>  it (especially with such a long name).\n\nI see the idea here: right now you can do:\n\n         touch a b c\n         git add .\n\nAnd have it auto-add all the new files, so \"git commit\" will work.\nBut there is no equivalent for rm, because for obvious reasons,\n\n        rm b c\n        git rm .\n\nDoesn't do the same thing.  And \"git add .\" doesn't auto-recognize\ndeletions, which probably also makes sense.\n\n\"git commit -a\", on the other hand, will automatically commit all\ndeletions for you.  But you don't always want to commit *all* your\nchanges just because you want to commit all your deletions.\n\nThat said, --if-missing is a bit unwieldy.  I don't have a better\nsuggestion though.\n\nHave fun,\n\nAvery\n"},{"id":"83551","messageId":"7vtzepr7g5.fsf@gitster.siamese.dyndns.org","threadId":"14488","inReplyTo":"1216231250-21141-1-git-send-email-ciaran.mccreesh@googlemail.com","subject":"Re: [PATCH,RFC] Implement 'git rm --if-missing'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-07-16T18:48:42Z","receivedAt":"2008-07-16T18:48:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ciaran McCreesh <ciaran.mccreesh@googlemail.com> writes:\n\n> git rm --if-missing will only remove files if they've already been removed from\n> disk.\n\nThis probably is a borderline with feaping creaturism.  What's the use of\nit in a real workflow that you need this for?\n\n\"git add -u\" may be too broad in that it also adds anything modified, but\nso is --if-missing too broad in that it removes anything removed, and if\nyou are going to limit by giving pathspecs _anyway_, then...\n\nOld timers might just do:\n\n\tgit diff --name-only --diff-filter=D |\n        git update-index --remove --stdin\n\n;-)\n\n> diff --git a/builtin-rm.c b/builtin-rm.c\n> index 22c9bd1..4b89705 100644\n> --- a/builtin-rm.c\n> +++ b/builtin-rm.c\n> @@ -125,7 +125,7 @@ static int check_local_mod(unsigned char *head, int index_only)\n>  static struct lock_file lock_file;\n>  \n>  static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;\n> -static int ignore_unmatch = 0;\n> +static int ignore_unmatch = 0, if_missing = 0;\n\nNot your fault in entirety, but we should drop these \" = 0\"\ninitializations for static variables in a clean-up patch.\n\n>  static struct option builtin_rm_options[] = {\n>  \tOPT__DRY_RUN(&show_only),\n> @@ -135,6 +135,7 @@ static struct option builtin_rm_options[] = {\n>  \tOPT_BOOLEAN('r', NULL,             &recursive,  \"allow recursive removal\"),\n>  \tOPT_BOOLEAN( 0 , \"ignore-unmatch\", &ignore_unmatch,\n>  \t\t\t\t\"exit with a zero status even if nothing matched\"),\n> +\tOPT_BOOLEAN( 0 , \"if-missing\",     &if_missing, \"only remove missing files\"),\n\nPerhaps the command should error out if some of the named files still\nexist in the working tree?\n\n> @@ -168,6 +169,12 @@ int cmd_rm(int argc, const char **argv, const char *prefix)\n>  \t\tstruct cache_entry *ce = active_cache[i];\n>  \t\tif (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen))\n>  \t\t\tcontinue;\n> +\t\tif (if_missing)\n> +\t\t{\n> +\t\t\tstruct stat st;\n> +\t\t\tif ((lstat(ce->name, &st) == 0) || (errno != ENOENT))\n> +\t\t\t\tcontinue;\n> +\t\t}\n\n (1) (Style).  Opening brace comes on the same line as \"if ()\".\n\n (2) (Design). How should this new option interact with --cached mode of\n     operation?\n\n (3) (Design). Shouldn't \"git rm --if-missing\" without any pathspec remove\n     all missing paths from the index?\n\n (4) If lstat fails due to I/O error or something, you do not continue and\n     add that path you did not get ENOENT for to the kill-list.  Is that\n     desirable?\n\n (5) I wonder if lstat() is enough here.  \n\n     Consider:\n\n\t- current commit has \"kernel\" symlink to \"linux-2.6/\" directory but\n          you want to remove kernel and move directory linux-2.6 to it, so:\n\n          - you run \"rm kernel; mv linux-2.6 kernel\"\n\n\t  - then you run \"git rm --if-missing -- kernel\"\n\n     What should the command do?\n"},{"id":"83553","messageId":"20080716185811.GA3517@xp.machine.xx","threadId":"14488","inReplyTo":"7vtzepr7g5.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH,RFC] Implement 'git rm --if-missing'","fromName":"Peter Baumann","fromEmail":"waste.manager@gmx.de","sentAt":"2008-07-16T18:58:11Z","receivedAt":"2008-07-16T18:58:11Z","isPatch":true,"sender":{"key":"waste.manager@gmx.de","avatar":null},"body":"On Wed, Jul 16, 2008 at 11:48:42AM -0700, Junio C Hamano wrote:\n> Ciaran McCreesh <ciaran.mccreesh@googlemail.com> writes:\n> \n> > git rm --if-missing will only remove files if they've already been removed from\n> > disk.\n> \n> This probably is a borderline with feaping creaturism.  What's the use of\n> it in a real workflow that you need this for?\n> \n> \"git add -u\" may be too broad in that it also adds anything modified, but\n> so is --if-missing too broad in that it removes anything removed, and if\n> you are going to limit by giving pathspecs _anyway_, then...\n> \n> Old timers might just do:\n> \n> \tgit diff --name-only --diff-filter=D |\n>         git update-index --remove --stdin\n> \n> ;-)\n> \n\nAh. This comes in handy. I already searched for a command to delete all\nmissing files. After reading through the fine manual of 'git rm', I went\nto git update-index but didn't come up with a solution to my problem.\n\nBut I have to say, an argument to 'git rm' would be preferable than the\nabove plumping.\n\n-Peter\n"},{"id":"83576","messageId":"D5EB71BB-FEA1-4900-AB72-914367B029AB@endpoint.com","threadId":"14488","inReplyTo":"20080716185811.GA3517@xp.machine.xx","subject":"Re: [PATCH,RFC] Implement 'git rm --if-missing'","fromName":"David Christensen","fromEmail":"david@endpoint.com","sentAt":"2008-07-16T19:43:27Z","receivedAt":"2008-07-16T19:43:27Z","isPatch":true,"sender":{"key":"david@endpoint.com","avatar":"https://gravatar.com/avatar/6089b35cc409d9d15ab439753a213d7528cd5e0a04f3917fa452d8dc45296612?d=mp&s=160"},"body":"On Jul 16, 2008, at 1:58 PM, Peter Baumann wrote:\n\n> On Wed, Jul 16, 2008 at 11:48:42AM -0700, Junio C Hamano wrote:\n>> Ciaran McCreesh <ciaran.mccreesh@googlemail.com> writes:\n>>\n>>> git rm --if-missing will only remove files if they've already been  \n>>> removed from\n>>> disk.\n>>\n>> This probably is a borderline with feaping creaturism.  What's the  \n>> use of\n>> it in a real workflow that you need this for?\n>>\n>> \"git add -u\" may be too broad in that it also adds anything  \n>> modified, but\n>> so is --if-missing too broad in that it removes anything removed,  \n>> and if\n>> you are going to limit by giving pathspecs _anyway_, then...\n>>\n>> Old timers might just do:\n>>\n>> \tgit diff --name-only --diff-filter=D |\n>>        git update-index --remove --stdin\n>>\n>> ;-)\n>>\n>\n> Ah. This comes in handy. I already searched for a command to delete  \n> all\n> missing files. After reading through the fine manual of 'git rm', I  \n> went\n> to git update-index but didn't come up with a solution to my problem.\n>\n> But I have to say, an argument to 'git rm' would be preferable than  \n> the\n> above plumping.\n\n\nWouldn't:\n\ngit rm $(git ls-files --deleted)\n\ndo the trick, or am I missing something?\n\nRegards,\n\nDavid\n--\nDavid Christensen\nEnd Point Corporation\ndavid@endpoint.com\n"}]}