{"thread":{"id":"57624","subject":"[WIP]: make merge nicer to the user","startedAt":"2022-03-27T15:42:13Z","lastAt":"2022-06-10T12:58:54Z","messageCount":27,"participants":["Guillaume Cogoni","Jonathan","Ævar Arnfjörð Bjarmason","Junio C Hamano","Jonathan Bressat","Matthieu Moy"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"452436","messageId":"CAA0Qn1sBF=PAduCQCXbYkeu4cphw7O+AnvwFNMWijuKYskaT8g@mail.gmail.com","threadId":"57624","inReplyTo":null,"subject":"[WIP]: make merge nicer to the user","fromName":"Guillaume Cogoni","fromEmail":"cogoni.guillaume@gmail.com","sentAt":"2022-03-27T15:41:57Z","receivedAt":"2022-03-27T15:42:13Z","isPatch":false,"sender":{"key":"cogoni.guillaume@gmail.com","avatar":"https://avatars.githubusercontent.com/u/60919643?v=4"},"body":"Hi,\nWe were working on a patch to make merge nicer to the user on\ntracked/untracked merge conflicts.\nYou can see that idea on this page:\nhttps://git.wiki.kernel.org/index.php/SmallProjectsIdeas\n\nWhen merging a commit which has tracked files with the same name as local\nuntracked files, Git refuses to proceed.\nWe want to change this behaviour. The idea is to check if the untracked and\nthe tracked file has the same content, so we can overwrite it.\n\n\nExamples of use cases where it can be interesting:\nThe scenarios are the following:\n\nA team member is modifying the templates for a website we are working on.\nThey are adding some images to the images directory (but forgets to add\nthem under source control).\nThey are sending the images by mail, later, to me.\nI'm adding the images under the source control and pushing them to\nGitHub together with other changes\nThey cannot pull updates from GitHub because Git doesn't want to\noverwrite their files.\nSource : https://stackoverflow.com/questions/1125968/how-do-i-force-git-pull-to-overwrite-local-files\n\nWhen using rsync to get files from a distant directory, but then those\nfiles are pushed on a repo from the distant directory, you don't want to\nreset the change when you just need to pull the repo because files are\nthe same.\n\n\nThe following parts is our test file:\n\ndiff --git a/t/t7615-merge-conflict.sh b/t/t7615-merge-conflict.sh\nnew file mode 100644\nindex 0000000000..4d89fe99ed\n--- /dev/null\n+++ b/t/t7615-merge-conflict.sh\n@@ -0,0 +1,47 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2022 Cogoni Guillaume and Bressat Jonathan\n+#\n+test_description='merge conflitct'\n+. ./test-lib.sh\n+\n+test_expect_success '[FAST_FORWARD] merge conflict when untracked\nfile and tracked file have the same name and content' '\n+ echo content >readme.md &&\n+ test_commit \"README\" readme.md &&\n+ git branch B &&\n+ git checkout -b A &&\n+ echo content >file &&\n+ test_commit \"tracked_file\" file &&\n+ git switch B &&\n+ echo content >file &&\n+ test_merge merge A\n+'\n+\n+test_expect_success '[MERGE] merge conflict when untracked file and\ntracked file have the same name and content' '\n+ echo content >readme.md &&\n+ test_commit \"README\" readme.md &&\n+ git branch A &&\n+ git checkout -b B &&\n+ echo content1 >file1 &&\n+ test_commit \"B_tracked_file\" file1 &&\n+ git checkout A &&\n+ echo content2 >file2 &&\n+ test_commit \"A_tracked_file\" file2 &&\n+ git switch B &&\n+ echo content2 >file2 &&\n+ test_merge merge A\n+'\n+\n+test_expect_thatfailure 'merge conflict when untracked file and tracked\nfile have not the same content but the same name' '\n+ echo content >readme.md &&\n+ test_commit \"README\" readme.md &&\n+ git branch B &&\n+ git checkout -b A &&\n+ echo content1 >file &&\n+ test_commit \"tracked_file\" file &&\n+ git switch B &&\n+ echo content2 >file &&\n+ test_merge merge A\n+'\n+\n+test_done\nThose tests must have assert in the end but it's just to explain our idea.\n\nOur research lead us to these functions:\n\nverify_absent_1() from /unpack-trees.c seems to be called for all files\nand it check if a file from the merged branch exists in the current\nbranch in regard of the name and the path (In our test above, if a file\nfrom the branch A exist in the branch B.). Then call check_ok_to_remove()\nfrom /unpack-trees.c when an untracked file with the same name than a\ntracked file on the merged branch is spotted.\n\nstatic int verify_absent_1(const struct cache_entry *ce,\nenum unpack_trees_error_types error_type,\nenum absent_checking_type absent_type,\nstruct unpack_trees_options *o);\n\nstatic int check_ok_to_remove(const char *name, int len, int dtype,\nconst struct cache_entry *ce, struct stat *st,\nenum unpack_trees_error_types error_type,\nenum absent_checking_type absent_type,\nstruct unpack_trees_options *o);\n\n\nWe think that a good way to solve this problem is to check the hash\nof the tracked and untracked file in check_ok_to_remove and then if\nthey are similar we can\noverwrite it (return 0). The hash is in ce->object_id.\nIn fact, it's more efficient than decompress the file and check\nthe content.\n\nDo you think, we are going in the good way, and is it a good idea ?\n\nthanks for your help and review.\n\nGuillaume Cogoni and\nJonathan Bressat\n"},{"id":"453456","messageId":"20220412191556.21135-1-Jonathan.bressat@etu.univ-lyon1.fr","threadId":"57624","inReplyTo":"CAA0Qn1sBF=PAduCQCXbYkeu4cphw7O+AnvwFNMWijuKYskaT8g@mail.gmail.com","subject":"[PATCH 0/1] Be nicer to the user on tracked/untracked merge conflicts","fromName":"Jonathan","fromEmail":"git.jonathan.bressat@gmail.com","sentAt":"2022-04-12T19:15:55Z","receivedAt":"2022-04-12T19:17:50Z","isPatch":true,"sender":{"key":"git.jonathan.bressat@gmail.com","avatar":null},"body":"When doing a merge while there is untracked files with the same name\nas merged files, git refuses to proceed. This patch make git overwrite\nfiles if their content are the same.\n\nWe added a statement to check_ok_to_remove() (unpack-trees.c) \nwith ie_modified() (read-cache.c) to test if the untracked file \nhas the same content as the merged one. It seems to work well \nwith all three o->result, o->dst_index and o->src_index,\nWe are not sure of what is the usage of those three, did we used it\nproperly?\n\nOur tests need some improvement, for example using test_commit,\nand testing more possibilities, it's not a real patch, just \nto comfirm if we are on the right track.\n\nThe next idea is when it's a fastforward, attempt to merge the\nuntracked file and the upstream version (like if the file had\njust been committed, but without introducing an extra commit).\n\nyou can see this idea here: \nhttps://git.wiki.kernel.org/index.php/SmallProjectsIdeas#Be_nicer_to_the_user_on_tracked.2Funtracked_merge_conflicts\n\nQuestions:\nThe old behaviour was here for technical reasons?\nThe new behavior that we introduce here become the default one?\nIf the old behavior was important for some people or for some reasons,\nwe can set a global variable to switch between the old and the new one.\nAnd if we define a global variable, should we print a warning to let \nusers know that there is a new behavior when a merge is called and that\nhe can switch between the old and new one.\nFor some reason, test_commit make the merge not working like if it's the\nold behaviour of merge, I dont understand why ?\n\nJonathan (1):\n  Merge with untracked file that are the same without failure and test\n\n t/t7615-merge-untracked.sh | 79 ++++++++++++++++++++++++++++++++++++++\n unpack-trees.c             |  4 ++\n 2 files changed, 83 insertions(+)\n create mode 100755 t/t7615-merge-untracked.sh\n\n-- \n2.35.1.7.gc8609858e0.dirty\n\n"},{"id":"453457","messageId":"20220412191556.21135-2-Jonathan.bressat@etu.univ-lyon1.fr","threadId":"57624","inReplyTo":"20220412191556.21135-1-Jonathan.bressat@etu.univ-lyon1.fr","subject":"[PATCH 1/1] Merge with untracked file that are the same without failure and test","fromName":"Jonathan","fromEmail":"git.jonathan.bressat@gmail.com","sentAt":"2022-04-12T19:15:56Z","receivedAt":"2022-04-12T19:18:01Z","isPatch":true,"sender":{"key":"git.jonathan.bressat@gmail.com","avatar":null},"body":"When doing a merge while there is untracked files with the same name\nas merged files, git refuses to proceed. This commit change this\nbehavior and make git overwrite files if their contents are the same.\nThis new behaviour is more pleasant for a user and will never be a\nfrustrating moment.\n\nAdd a if statement that check if the file has the same content as the\nmerged file thanks to the function ie_modified() (read-cache.c).\nie_modified () checks the status of both files, if they are different,\nit verifies their contents.\n\nAdd new tests that need to pass to confirm that the new feature works.\n\nCo-authored-by: COGONI Guillaume <cogoni.guillaume@gmail.com>\n---\n t/t7615-merge-untracked.sh | 79 ++++++++++++++++++++++++++++++++++++++\n unpack-trees.c             |  4 ++\n 2 files changed, 83 insertions(+)\n create mode 100755 t/t7615-merge-untracked.sh\n\ndiff --git a/t/t7615-merge-untracked.sh b/t/t7615-merge-untracked.sh\nnew file mode 100755\nindex 0000000000..71a34041d2\n--- /dev/null\n+++ b/t/t7615-merge-untracked.sh\n@@ -0,0 +1,79 @@\n+#!/bin/sh\n+\n+test_description='test when merge with untracked file'\n+\n+. ./test-lib.sh\n+\n+\n+test_expect_success 'overwrite the file when fastforward and the same content' '\n+    echo content >README.md &&\n+    test_commit \"init\" README.md &&\n+    git branch A &&\n+    git checkout -b B &&\n+    echo content >file &&\n+    git add file &&\n+    git commit -m \"tracked\" &&\n+    git switch A &&\n+    echo content >file &&\n+    git merge B\n+'\n+\n+test_expect_success 'merge fail with fastforward and different content' '\n+    rm * &&\n+    rm -r .git &&\n+    git init &&\n+    echo content >README.md &&\n+    test_commit \"init\" README.md &&\n+    git branch A &&\n+    git checkout -b B &&\n+    echo content >file &&\n+    git add file &&\n+    git commit -m \"tracked\" &&\n+    git switch A &&\n+    echo dif >file &&\n+    test_must_fail git merge B\n+'\n+\n+test_expect_success 'normal merge with untracked with the same content' '\n+    rm * &&\n+    rm -r .git &&\n+    git init &&\n+    echo content >README.md &&\n+    test_commit \"init\" README.md &&\n+    git branch A &&\n+    git checkout -b B &&\n+    echo content >fileB &&\n+    echo content >file &&\n+    git add fileB &&\n+    git add file &&\n+    git commit -m \"tracked\" &&\n+    git switch A &&\n+    echo content >fileA &&\n+    git add fileA &&\n+    git commit -m \"exA\" &&\n+    echo content >file &&\n+    git merge B -m \"merge\"\n+'\n+\n+test_expect_success 'normal merge fail when untracked with different content' '\n+    rm * &&\n+    rm -r .git &&\n+    git init &&\n+    echo content >README.md &&\n+    test_commit \"init\" README.md &&\n+    git branch A &&\n+    git checkout -b B &&\n+    echo content >fileB &&\n+    echo content >file &&\n+    git add fileB &&\n+    git add file &&\n+    git commit -m \"tracked\" &&\n+    git switch A &&\n+    echo content >fileA &&\n+    git add fileA &&\n+    git commit -m \"exA\" &&\n+    echo dif >file &&\n+    test_must_fail git merge B -m \"merge\"\n+'\n+\n+test_done\n\\ No newline at end of file\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 360844bda3..834aca0da9 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -2259,6 +2259,10 @@ static int check_ok_to_remove(const char *name, int len, int dtype,\n \t\t\treturn 0;\n \t}\n \n+\tif (!ie_modified(&o->result, ce, st, 0))\n+\t\treturn 0;\n+\n+\n \treturn add_rejected_path(o, error_type, name);\n }\n \n-- \n2.35.1.7.gc8609858e0.dirty\n\n"},{"id":"453459","messageId":"220412.86czhmgl68.gmgdl@evledraar.gmail.com","threadId":"57624","inReplyTo":"20220412191556.21135-2-Jonathan.bressat@etu.univ-lyon1.fr","subject":"Re: [PATCH 1/1] Merge with untracked file that are the same without failure and test","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-12T19:21:39Z","receivedAt":"2022-04-12T19:24:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Apr 12 2022, Jonathan wrote:\n\n> When doing a merge while there is untracked files with the same name\n> as merged files, git refuses to proceed. This commit change this\n> behavior and make git overwrite files if their contents are the same.\n> This new behaviour is more pleasant for a user and will never be a\n> frustrating moment.\n>\n> Add a if statement that check if the file has the same content as the\n> merged file thanks to the function ie_modified() (read-cache.c).\n> ie_modified () checks the status of both files, if they are different,\n> it verifies their contents.\n>\n> Add new tests that need to pass to confirm that the new feature works.\n>\n> Co-authored-by: COGONI Guillaume <cogoni.guillaume@gmail.com>\n> ---\n>  t/t7615-merge-untracked.sh | 79 ++++++++++++++++++++++++++++++++++++++\n>  unpack-trees.c             |  4 ++\n>  2 files changed, 83 insertions(+)\n>  create mode 100755 t/t7615-merge-untracked.sh\n>\n> diff --git a/t/t7615-merge-untracked.sh b/t/t7615-merge-untracked.sh\n> new file mode 100755\n> index 0000000000..71a34041d2\n> --- /dev/null\n> +++ b/t/t7615-merge-untracked.sh\n> @@ -0,0 +1,79 @@\n> +#!/bin/sh\n> +\n> +test_description='test when merge with untracked file'\n> +\n> +. ./test-lib.sh\n> +\n> +\n\nToo much whitespace.\n\n> +test_expect_success 'overwrite the file when fastforward and the same content' '\n> +    echo content >README.md &&\n\nThe coding style in this project is TAB-indent, not 4 spaces\n> +    test_commit \"init\" README.md &&\n> +    git branch A &&\n> +    git checkout -b B &&\n> +    echo content >file &&\n> +    git add file &&\n> +    git commit -m \"tracked\" &&\n\n\nCan't these and a lot of the test also just use test_commit, you can do\nthis sort of thing with its multi-param invocation, if you're trying to\nspecifically avoid tags there's an option for that.\n\n> +    git switch A &&\n> +    echo content >file &&\n> +    git merge B\n> +'\n> +\n> +test_expect_success 'merge fail with fastforward and different content' '\n> +    rm * &&\n> +    rm -r .git &&\n\nCan we just set this up in a \"git init repo\" or whatever instead?\n\n> +    git init &&\n> +    echo content >README.md &&\n> +    test_commit \"init\" README.md &&\n> +    git branch A &&\n> +    git checkout -b B &&\n> +    echo content >file &&\n> +    git add file &&\n> +    git commit -m \"tracked\" &&\n> +    git switch A &&\n> +    echo dif >file &&\n> +    test_must_fail git merge B\n\nAnd thendo this in a sub-shell?\n\n> +'\n> +\n> +test_expect_success 'normal merge with untracked with the same content' '\n> +    rm * &&\n> +    rm -r .git &&\n\nPlease use test_when_finished in the tests themselves for teardown,\nrather than having the \"next test\" do the cleanup after the last one.\n\n> +    git init &&\n> +    echo content >README.md &&\n> +    test_commit \"init\" README.md &&\n> +    git branch A &&\n> +    git checkout -b B &&\n> +    echo content >fileB &&\n> +    echo content >file &&\n> +    git add fileB &&\n> +    git add file &&\n> +    git commit -m \"tracked\" &&\n> +    git switch A &&\n> +    echo content >fileA &&\n> +    git add fileA &&\n> +    git commit -m \"exA\" &&\n> +    echo content >file &&\n> +    git merge B -m \"merge\"\n> +'\n> +\n> +test_expect_success 'normal merge fail when untracked with different content' '\n> +    rm * &&\n> +    rm -r .git &&\n> +    git init &&\n> +    echo content >README.md &&\n> +    test_commit \"init\" README.md &&\n> +    git branch A &&\n> +    git checkout -b B &&\n> +    echo content >fileB &&\n> +    echo content >file &&\n> +    git add fileB &&\n> +    git add file &&\n> +    git commit -m \"tracked\" &&\n> +    git switch A &&\n> +    echo content >fileA &&\n> +    git add fileA &&\n> +    git commit -m \"exA\" &&\n> +    echo dif >file &&\n> +    test_must_fail git merge B -m \"merge\"\n> +'\n> +\n> +test_done\n> \\ No newline at end of file\n\nGit's telling you something here... :)\n\n> diff --git a/unpack-trees.c b/unpack-trees.c\n> index 360844bda3..834aca0da9 100644\n> --- a/unpack-trees.c\n> +++ b/unpack-trees.c\n> @@ -2259,6 +2259,10 @@ static int check_ok_to_remove(const char *name, int len, int dtype,\n>  \t\t\treturn 0;\n>  \t}\n>  \n> +\tif (!ie_modified(&o->result, ce, st, 0))\n> +\t\treturn 0;\n> +\n> +\n\nToo much whitespace.\n\n>  \treturn add_rejected_path(o, error_type, name);\n>  }\n\n"},{"id":"453461","messageId":"220412.868rsagkus.gmgdl@evledraar.gmail.com","threadId":"57624","inReplyTo":"20220412191556.21135-1-Jonathan.bressat@etu.univ-lyon1.fr","subject":"Re: [PATCH 0/1] Be nicer to the user on tracked/untracked merge conflicts","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-12T19:24:34Z","receivedAt":"2022-04-12T19:31:02Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Apr 12 2022, Jonathan wrote:\n\n> When doing a merge while there is untracked files with the same name\n> as merged files, git refuses to proceed. This patch make git overwrite\n> files if their content are the same.\n>\n> We added a statement to check_ok_to_remove() (unpack-trees.c) \n> with ie_modified() (read-cache.c) to test if the untracked file \n> has the same content as the merged one. It seems to work well \n> with all three o->result, o->dst_index and o->src_index,\n> We are not sure of what is the usage of those three, did we used it\n> properly?\n>\n> Our tests need some improvement, for example using test_commit,\n> and testing more possibilities, it's not a real patch, just \n> to comfirm if we are on the right track.\n>\n> The next idea is when it's a fastforward, attempt to merge the\n> untracked file and the upstream version (like if the file had\n> just been committed, but without introducing an extra commit).\n>\n> you can see this idea here: \n> https://git.wiki.kernel.org/index.php/SmallProjectsIdeas#Be_nicer_to_the_user_on_tracked.2Funtracked_merge_conflicts\n\nI left some comments on the patch itself, but structurally it wolud be\nreally nice to make this and similar changes:\n\n 1. Test for current behavior\n 2. Change behavior and relevant (new) tests\n\nRather than the current one-step, that would also communicate that wiki\nlink (and better) via code.\n\n> Questions:\n> The old behaviour was here for technical reasons?\n> The new behavior that we introduce here become the default one?\n> If the old behavior was important for some people or for some reasons,\n> we can set a global variable to switch between the old and the new one.\n> And if we define a global variable, should we print a warning to let \n> users know that there is a new behavior when a merge is called and that\n> he can switch between the old and new one.\n\nI don't know if we need a config etc., but FWIW my first reaction to\nthis is that it's a bit iffy/fragile, i.e. before this we'd basically\nerror out and say \"fix your index/working tree\".\n\nBut now just because the newly merged content happens to be identical\nwe'll silently merge it over that \"staged\" content?\n\nAnyway, I can also see how that would be useful for some people.\n\nI've personally been annoyed by a subset of this behavior in the past, I\ncan't remember if it's with merge or rebase that we'll refuse to do\nanything because we have a locally modified/staged (can't remember) file\n\"X\", even though \"X\" won't be touched at all if the merge/rebase\nhappens.\n\nBut I haven't wanted git to have quite this level of DWYM behavior in\nthis area, just my 0.02.\n\n> For some reason, test_commit make the merge not working like if it's the\n> old behaviour of merge, I dont understand why ?\n\nAh, I left some comments on \"why not test_commit\"...\n\nDo you have an example of such a non-working case? I'm not sure why it\nwouldn't work.\n"},{"id":"453514","messageId":"xmqqfsmg97ac.fsf@gitster.g","threadId":"57624","inReplyTo":"20220412191556.21135-2-Jonathan.bressat@etu.univ-lyon1.fr","subject":"Re: [PATCH 1/1] Merge with untracked file that are the same without failure and test","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-13T18:18:03Z","receivedAt":"2022-04-13T18:18:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jonathan <git.jonathan.bressat@gmail.com> writes:\n\n> diff --git a/unpack-trees.c b/unpack-trees.c\n> index 360844bda3..834aca0da9 100644\n> --- a/unpack-trees.c\n> +++ b/unpack-trees.c\n> @@ -2259,6 +2259,10 @@ static int check_ok_to_remove(const char *name, int len, int dtype,\n>  \t\t\treturn 0;\n>  \t}\n>  \n> +\tif (!ie_modified(&o->result, ce, st, 0))\n> +\t\treturn 0;\n> +\n> +\n>  \treturn add_rejected_path(o, error_type, name);\n>  }\n\nIt probably is better to step back a bit and take a wider look at\nthe original to assess this change.\n\nThe only two callers of this function appears in this function.\n\n        /*\n         * We do not want to remove or overwrite a working tree file that\n         * is not tracked, unless it is ignored.\n         */\n        static int verify_absent_1(const struct cache_entry *ce,\n                                   enum unpack_trees_error_types error_type,\n                                   struct unpack_trees_options *o)\n        {\n\t\t...\n\nNotice what the comment in front says?  Does this patch change the\nbehaviour from what the comment tells us it does?  We should adjust\nthe comment to the new world order if it does.\n\nThe existing code before the pre-context of the hunk reads like\nthis:\n\n            /*\n             * The previous round may already have decided to\n             * delete this path, which is in a subdirectory that\n             * is being replaced with a blob.\n             */\n            result = index_file_exists(&o->result, name, len, 0);\n            if (result) {\n                    if (result->ce_flags & CE_REMOVE)\n                            return 0;\n            }\n\nWe've called index_file_exists(), and the new code added here does\nnot take the outcome into account.\n\nIf we truly care the case \"we have _UNTRACKED_ path and it happens\nto be identical to what we are going to resolve to anyway\",\nshouldn't we be making sure that the <name,len> refers to an\nuntracked path by checking if result is NULL here?  If so,\n\n\tif (result) {\n\t\t...\n-\t}\n+\t} else if (!ie_modified(...)) {\n+\t\treturn 0;\n+\t}\n\treturn add_rejected_path(...);\n\nis what you want, perhaps?\n\nOr if we have cases where we have a tracked path and ask this\nfunction if it is OK to remove, should the same reasoning you\ninvented to deal with untracked paths equally apply?  If that is the\ncase, then the code may be OK but the proposed log message to\nexplain and justify the change needs to be updated to explain what\nhappens in such a case (or how such a case will not happen).\n\nThanks.\n\n"},{"id":"453645","messageId":"CANteD_zkvDiMBT2M3_eL_HV5v=ckg=fJQxEGrvDx5ttMAdSdUg@mail.gmail.com","threadId":"57624","inReplyTo":"220412.868rsagkus.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH 0/1] Be nicer to the user on tracked/untracked merge conflicts","fromName":"Jonathan Bressat","fromEmail":"git.jonathan.bressat@gmail.com","sentAt":"2022-04-14T08:57:18Z","receivedAt":"2022-04-14T08:57:39Z","isPatch":true,"sender":{"key":"git.jonathan.bressat@gmail.com","avatar":null},"body":"On Tue, Apr 12 2022, Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n> Do you have an example of such a non-working case? I'm not sure why it\n> wouldn't work.\n\nFor exemple this test fail:\n\n+test_expect_success 'overwrite the file when fastforward and the same\ncontent' '\n+ echo content >README.md &&\n+ test_commit \"init\" README.md &&\n+ git branch A &&\n+ git checkout -b B &&\n+ echo content >file &&\n+ test_commit \"tracked\" file &&\n+ git checkout A &&\n+ echo content >file &&\n+ git merge B\n+'\n\nbut this one works:\n\n+test_expect_success 'overwrite the file when fastforward and the same\ncontent' '\n+ echo content >README.md &&\n+ test_commit \"init\" README.md &&\n+ git branch A &&\n+ git checkout -b B &&\n+ echo content >file &&\n+ git add file &&\n+ test_commit \"tracked\" &&\n+ git checkout A &&\n+ echo content >file &&\n+ git merge B\n+'\n\nwill send you a new version of our patch soon.\nThanks to your reviews and help.\n\nJonathan BRESSAT and\nGuillaume COGONI\n"},{"id":"454406","messageId":"20220425202721.20066-1-git.jonathan.bressat@gmail.com","threadId":"57624","inReplyTo":"xmqqfsmg97ac.fsf@gitster.g","subject":"[PATCH v1 0/2] Be nicer to the user on tracked/untracked merge conflicts","fromName":"Jonathan","fromEmail":"git.jonathan.bressat@gmail.com","sentAt":"2022-04-25T20:27:19Z","receivedAt":"2022-04-25T20:29:05Z","isPatch":true,"sender":{"key":"git.jonathan.bressat@gmail.com","avatar":null},"body":"Sorry for being a bit slow to answer.\n\nJunio C Hamano <gitster@pobox.com> wrote:\n> \tif (result) {\n> \t\t...\n> -\t}\n> +\t} else if (!ie_modified(...)) {\n> +\t\treturn 0;\n> +\t}\n> \treturn add_rejected_path(...);\n> \n> is what you want, perhaps?\n\nYes, but that made us ask if it can be a good idea to extend our patch\nto overwrite all unstaged file. However if we want to overwrite all\nunstaged file even tracked one just this may not be enough:\n\nif (result) {\n\t...\n}\n\n+if (!ie_modified(...)) {\n+\treturn 0;\n+}\n\nBecause with this merge still fail for unstaged file that has the same\ncontent, because unstaged file are not exactly treated the same way.\n\nOur patch broke some test in t6436-merge-overwrite.sh so we think that\nwe need to modify those tests to make them follow the patch.\n\nThanks for your reviews\n\nJonathan (2):\n  t7615: test how merge behave when there is untracked file\n  merge with untracked file that are the same without failure\n\n t/t7615-merge-untracked.sh | 63 ++++++++++++++++++++++++++++++++++++++\n unpack-trees.c             |  5 ++-\n 2 files changed, 67 insertions(+), 1 deletion(-)\n create mode 100755 t/t7615-merge-untracked.sh\n\nInterdiff vs v0 :\ndiff --git a/t/t7615-merge-untracked.sh b/t/t7615-merge-untracked.sh\nindex 71a34041d2..99f8bae4c0 100755\n--- a/t/t7615-merge-untracked.sh\n+++ b/t/t7615-merge-untracked.sh\n@@ -2,78 +2,62 @@\n \n test_description='test when merge with untracked file'\n \n+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n+\n . ./test-lib.sh\n \n+test_expect_success 'setup' '\n+\ttest_commit \"init\" README.md \"content\" &&\n+\tgit checkout -b A\n+'\n+\n+test_expect_success 'fastforward overwrite untracked file that has the same content' '\n+\ttest_when_finished \"git branch -D B && git reset --hard init && git clean --force\" &&\n+\tgit checkout -b B &&\n+\ttest_commit --no-tag \"tracked\" file \"content\" &&\n+\tgit checkout A &&\n+\techo content >file &&\n+\tgit merge B\n+'\n \n-test_expect_success 'overwrite the file when fastforward and the same content' '\n-    echo content >README.md &&\n-    test_commit \"init\" README.md &&\n-    git branch A &&\n-    git checkout -b B &&\n-    echo content >file &&\n-    git add file &&\n-    git commit -m \"tracked\" &&\n-    git switch A &&\n-    echo content >file &&\n-    git merge B\n+test_expect_success 'fastforward fail when untracked file has different content' '\n+\ttest_when_finished \"git branch -D B && git reset --hard init && git clean --force\" &&\n+\tgit checkout -b B &&\n+\ttest_commit --no-tag \"tracked\" file \"content\" &&\n+\tgit switch A &&\n+\techo other >file &&\n+\ttest_must_fail git merge B\n '\n \n-test_expect_success 'merge fail with fastforward and different content' '\n-    rm * &&\n-    rm -r .git &&\n-    git init &&\n-    echo content >README.md &&\n-    test_commit \"init\" README.md &&\n-    git branch A &&\n-    git checkout -b B &&\n-    echo content >file &&\n-    git add file &&\n-    git commit -m \"tracked\" &&\n-    git switch A &&\n-    echo dif >file &&\n-    test_must_fail git merge B\n+test_expect_success 'normal merge overwrite untracked file that has the same content' '\n+\ttest_when_finished \"git branch -D B && git reset --hard init && git clean --force\" &&\n+\tgit checkout -b B &&\n+\ttest_commit --no-tag \"tracked\" file \"content\" fileB \"content\" &&\n+\tgit switch A &&\n+\ttest_commit --no-tag \"exA\" fileA \"content\" &&\n+\techo content >file &&\n+\tgit merge B\n '\n \n-test_expect_success 'normal merge with untracked with the same content' '\n-    rm * &&\n-    rm -r .git &&\n-    git init &&\n-    echo content >README.md &&\n-    test_commit \"init\" README.md &&\n-    git branch A &&\n-    git checkout -b B &&\n-    echo content >fileB &&\n-    echo content >file &&\n-    git add fileB &&\n-    git add file &&\n-    git commit -m \"tracked\" &&\n-    git switch A &&\n-    echo content >fileA &&\n-    git add fileA &&\n-    git commit -m \"exA\" &&\n-    echo content >file &&\n-    git merge B -m \"merge\"\n+test_expect_success 'normal merge fail when untracked file has different content' '\n+\ttest_when_finished \"git branch -D B && git reset --hard init && git clean --force\" &&\n+\tgit checkout -b B &&\n+\ttest_commit --no-tag \"tracked\" file \"content\" fileB \"content\" &&\n+\tgit switch A &&\n+\ttest_commit --no-tag \"exA\" fileA \"content\" &&\n+\techo dif >file &&\n+\ttest_must_fail git merge B\n '\n \n-test_expect_success 'normal merge fail when untracked with different content' '\n-    rm * &&\n-    rm -r .git &&\n-    git init &&\n-    echo content >README.md &&\n-    test_commit \"init\" README.md &&\n-    git branch A &&\n-    git checkout -b B &&\n-    echo content >fileB &&\n-    echo content >file &&\n-    git add fileB &&\n-    git add file &&\n-    git commit -m \"tracked\" &&\n-    git switch A &&\n-    echo content >fileA &&\n-    git add fileA &&\n-    git commit -m \"exA\" &&\n-    echo dif >file &&\n-    test_must_fail git merge B -m \"merge\"\n+test_expect_success 'merge fail when tracked file modification is unstaged' '\n+\ttest_when_finished \"git branch -D B && git reset --hard init && git clean --force\" &&\n+\ttest_commit --no-tag \"unstaged\" file \"other\" &&\n+\tgit checkout -b B &&\n+\ttest_commit --no-tag \"staged\" file \"content\" &&\n+\tgit switch A &&\n+\techo content >file &&\n+\ttest_must_fail git merge B\n '\n \n-test_done\n\\ No newline at end of file\n+test_done\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 834aca0da9..61e06c04be 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -2257,18 +2257,17 @@ static int check_ok_to_remove(const char *name, int len, int dtype,\n \tif (result) {\n \t\tif (result->ce_flags & CE_REMOVE)\n \t\t\treturn 0;\n-\t}\n-\n-\tif (!ie_modified(&o->result, ce, st, 0))\n+\t} else if (!ie_modified(&o->result, ce, st, 0)) {\n \t\treturn 0;\n-\n+\t}\n \n \treturn add_rejected_path(o, error_type, name);\n }\n \n /*\n  * We do not want to remove or overwrite a working tree file that\n- * is not tracked, unless it is ignored.\n+ * is not tracked, unless it is ignored and unless it has the same\n+ * content than the merged file.\n  */\n static int verify_absent_1(const struct cache_entry *ce,\n \t\t\t   enum unpack_trees_error_types error_type,\n-- \n2.35.1.7.gc8609858e0.dirty\n\n"},{"id":"454407","messageId":"20220425202721.20066-2-git.jonathan.bressat@gmail.com","threadId":"57624","inReplyTo":"20220425202721.20066-1-git.jonathan.bressat@gmail.com","subject":"[PATCH v1 1/2] t7615: test how merge behave when there is untracked file","fromName":"Jonathan","fromEmail":"git.jonathan.bressat@gmail.com","sentAt":"2022-04-25T20:27:20Z","receivedAt":"2022-04-25T20:29:16Z","isPatch":true,"sender":{"key":"git.jonathan.bressat@gmail.com","avatar":null},"body":"when there is untracked file that has the same name than file in the\nmerged branch git refuse to proceed, even when the file has the same\ncontent\n\nt6436 test a similar thing but not especially with same content file\n\nSigned-off-by: Jonathan <git.jonathan.bressat@gmail.com>\nSigned-off-by: COGONI Guillaume <cogoni.guillaume@gmail.com>\n---\n t/t7615-merge-untracked.sh | 63 ++++++++++++++++++++++++++++++++++++++\n 1 file changed, 63 insertions(+)\n create mode 100755 t/t7615-merge-untracked.sh\n\ndiff --git a/t/t7615-merge-untracked.sh b/t/t7615-merge-untracked.sh\nnew file mode 100755\nindex 0000000000..053e6b80ee\n--- /dev/null\n+++ b/t/t7615-merge-untracked.sh\n@@ -0,0 +1,63 @@\n+#!/bin/sh\n+\n+test_description='test when merge with untracked file'\n+\n+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit \"init\" README.md \"content\" &&\n+\tgit checkout -b A\n+'\n+\n+test_expect_success 'fastforward fail when untracked file has the same content' '\n+\ttest_when_finished \"git branch -D B && git reset --hard init && git clean --force\" &&\n+\tgit checkout -b B &&\n+\ttest_commit --no-tag \"tracked\" file \"content\" &&\n+\tgit checkout A &&\n+\techo content >file &&\n+\ttest_must_fail git merge B\n+'\n+\n+test_expect_success 'fastforward fail when untracked file has different content' '\n+\ttest_when_finished \"git branch -D B && git reset --hard init && git clean --force\" &&\n+\tgit checkout -b B &&\n+\ttest_commit --no-tag \"tracked\" file \"content\" &&\n+\tgit switch A &&\n+\techo other >file &&\n+\ttest_must_fail git merge B\n+'\n+\n+test_expect_success 'normal merge fail when untracked file has the same content' '\n+\ttest_when_finished \"git branch -D B && git reset --hard init && git clean --force\" &&\n+\tgit checkout -b B &&\n+\ttest_commit --no-tag \"tracked\" file \"content\" fileB \"content\" &&\n+\tgit switch A &&\n+\ttest_commit --no-tag \"exA\" fileA \"content\" &&\n+\techo content >file &&\n+\ttest_must_fail git merge B\n+'\n+\n+test_expect_success 'normal merge fail when untracked file has different content' '\n+\ttest_when_finished \"git branch -D B && git reset --hard init && git clean --force\" &&\n+\tgit checkout -b B &&\n+\ttest_commit --no-tag \"tracked\" file \"content\" fileB \"content\" &&\n+\tgit switch A &&\n+\ttest_commit --no-tag \"exA\" fileA \"content\" &&\n+\techo dif >file &&\n+\ttest_must_fail git merge B\n+'\n+\n+test_expect_success 'merge fail when tracked file modification is unstaged' '\n+\ttest_when_finished \"git branch -D B && git reset --hard init && git clean --force\" &&\n+\ttest_commit --no-tag \"unstaged\" file \"other\" &&\n+\tgit checkout -b B &&\n+\ttest_commit --no-tag \"staged\" file \"content\" &&\n+\tgit switch A &&\n+\techo content >file &&\n+\ttest_must_fail git merge B\n+'\n+\n+test_done\n-- \n2.35.1.7.gc8609858e0.dirty\n\n"},{"id":"454408","messageId":"20220425202721.20066-3-git.jonathan.bressat@gmail.com","threadId":"57624","inReplyTo":"20220425202721.20066-1-git.jonathan.bressat@gmail.com","subject":"[PATCH v1 2/2] merge with untracked file that are the same without failure","fromName":"Jonathan","fromEmail":"git.jonathan.bressat@gmail.com","sentAt":"2022-04-25T20:27:21Z","receivedAt":"2022-04-25T20:29:17Z","isPatch":true,"sender":{"key":"git.jonathan.bressat@gmail.com","avatar":null},"body":"In unpack-trees.c in the check_ok_to_remove() function: add a new\nstatement, if the file has the same content as the merged file, it\ncan be removed.\n\ntest this new behaviour in t7615.\n\nSigned-off-by: Jonathan <git.jonathan.bressat@gmail.com>\nSigned-off-by: COGONI Guillaume <cogoni.guillaume@gmail.com>\n---\n t/t7615-merge-untracked.sh | 8 ++++----\n unpack-trees.c             | 5 ++++-\n 2 files changed, 8 insertions(+), 5 deletions(-)\n\ndiff --git a/t/t7615-merge-untracked.sh b/t/t7615-merge-untracked.sh\nindex 053e6b80ee..99f8bae4c0 100755\n--- a/t/t7615-merge-untracked.sh\n+++ b/t/t7615-merge-untracked.sh\n@@ -12,13 +12,13 @@ test_expect_success 'setup' '\n \tgit checkout -b A\n '\n \n-test_expect_success 'fastforward fail when untracked file has the same content' '\n+test_expect_success 'fastforward overwrite untracked file that has the same content' '\n \ttest_when_finished \"git branch -D B && git reset --hard init && git clean --force\" &&\n \tgit checkout -b B &&\n \ttest_commit --no-tag \"tracked\" file \"content\" &&\n \tgit checkout A &&\n \techo content >file &&\n-\ttest_must_fail git merge B\n+\tgit merge B\n '\n \n test_expect_success 'fastforward fail when untracked file has different content' '\n@@ -30,14 +30,14 @@ test_expect_success 'fastforward fail when untracked file has different content'\n \ttest_must_fail git merge B\n '\n \n-test_expect_success 'normal merge fail when untracked file has the same content' '\n+test_expect_success 'normal merge overwrite untracked file that has the same content' '\n \ttest_when_finished \"git branch -D B && git reset --hard init && git clean --force\" &&\n \tgit checkout -b B &&\n \ttest_commit --no-tag \"tracked\" file \"content\" fileB \"content\" &&\n \tgit switch A &&\n \ttest_commit --no-tag \"exA\" fileA \"content\" &&\n \techo content >file &&\n-\ttest_must_fail git merge B\n+\tgit merge B\n '\n \n test_expect_success 'normal merge fail when untracked file has different content' '\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 360844bda3..61e06c04be 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -2257,6 +2257,8 @@ static int check_ok_to_remove(const char *name, int len, int dtype,\n \tif (result) {\n \t\tif (result->ce_flags & CE_REMOVE)\n \t\t\treturn 0;\n+\t} else if (!ie_modified(&o->result, ce, st, 0)) {\n+\t\treturn 0;\n \t}\n \n \treturn add_rejected_path(o, error_type, name);\n@@ -2264,7 +2266,8 @@ static int check_ok_to_remove(const char *name, int len, int dtype,\n \n /*\n  * We do not want to remove or overwrite a working tree file that\n- * is not tracked, unless it is ignored.\n+ * is not tracked, unless it is ignored and unless it has the same\n+ * content than the merged file.\n  */\n static int verify_absent_1(const struct cache_entry *ce,\n \t\t\t   enum unpack_trees_error_types error_type,\n-- \n2.35.1.7.gc8609858e0.dirty\n\n"},{"id":"454410","messageId":"xmqqczh4vp6e.fsf@gitster.g","threadId":"57624","inReplyTo":"20220425202721.20066-1-git.jonathan.bressat@gmail.com","subject":"Re: [PATCH v1 0/2] Be nicer to the user on tracked/untracked merge conflicts","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-25T21:16:57Z","receivedAt":"2022-04-25T21:17:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jonathan <git.jonathan.bressat@gmail.com> writes:\n\n> Because with this merge still fail for unstaged file that has the same\n> content, because unstaged file are not exactly treated the same way.\n\nCorrect.  If you want to do this correctly, you'd need to make sure\nthat you'd clobber untracked files ONLY when you are not losing any\ninformation.\n\nAnd even with that, I think some existing users will be hurt with\nthis change in a huge way.  They may have untracked change locally\nbecause they are not quite done with it yet, and somebody else\nthrows a pull request at them that has the same change as the local\nmodification.\n\nThey make a trial merge, look at the result, and discard it because\nthere are also unwanted changes in the branch they pulled into.\n\n    $ git pull $URL $branch ;# responding to the pull request\n    ... examine the result, finding it unsatisfactory ...\n    $ git reset --hard ORIG_HEAD\n    ... now we are back to where we started; well not really ...\n\nNow, without this change, \"git pull\" used to stop until they stashed\naway the untracked change safely.  But with this change, \"git pull\"\nwill succeed, and then \"reset --hard\" will discard it together with\nother changes that came to us from $URL/$branch.  They lost their\nlocal, uncommitted change.\n\nAnd \"but you can pull the equivalent out of $URL/$branch\" is not a\ngood excuse.  They may not notice the lossage long after having\ndealt with this pull request (there are busy people who are handling\nmany pull requests from many people) and they have been relying on\n\"git pull\" that never clobbers their local uncommitted changes.  And\nwhen they noticed the lossage, they may not even remember which one\nof pull requests happened to have an identical change as their local\nchange to cause this lossage, simply because \"git pull\" that used to\nstop just continued without a noise.\n\nSo, I am not sure if this is really a good idea to begin with.  It\ncertainly would make it slightly simpler in a trivial case, but it\nsurely looks like a dangerous behaviour change, especially if it is\ndone unconditionally.\n\n> Our patch broke some test in t6436-merge-overwrite.sh so we think that\n> we need to modify those tests to make them follow the patch.\n\nWait.  Isn't it backwards?  The existing tests _may_ be casting an\nundesirable current behaviour in stone, but most of the time it is\nprotecting existing user's expectations.  If you have an untracked\nfile, you can rest assured that they won't be clobbered by a merge.\n\nSo we'd need to think twice and carefully examine if it makes sense\nto update the expectations.  I haven't read the change to the tests,\nso I cannot tell which case it is.\n\nThanks.\n"},{"id":"454414","messageId":"CAA0Qn1u50ncejNtWs1AV5tcXjFC-jnmnvjFkBDQyqU4Wcvoy0g@mail.gmail.com","threadId":"57624","inReplyTo":"xmqqczh4vp6e.fsf@gitster.g","subject":"Re: [PATCH v1 0/2] Be nicer to the user on tracked/untracked merge conflicts","fromName":"Guillaume Cogoni","fromEmail":"cogoni.guillaume@gmail.com","sentAt":"2022-04-25T22:28:57Z","receivedAt":"2022-04-25T22:29:57Z","isPatch":true,"sender":{"key":"cogoni.guillaume@gmail.com","avatar":"https://avatars.githubusercontent.com/u/60919643?v=4"},"body":" Junio C Hamano <gitster@pobox.com> writes:\n\n> So, I am not sure if this is really a good idea to begin with.  It\n> certainly would make it slightly simpler in a trivial case, but it\n> surely looks like a dangerous behaviour change, especially if it is\n> done unconditionally.\n\nCan we create a configuration variable to avoid this problem?\nWe keep the old behavior by default, and make a configuration variable\nfor people who wants to have this new behavior, but if the user set the variable\na message informs it about the problem that you mention.\n\nOr, we add an option like git pull --doSomething.\n\nMaybe, we can think about another behaviour.\nWhen the user git pull and this error occurs:\nerror: The following untracked working tree files would be overwritten by merge:\nfile1.txt\nfile2.txt\nPlease move or remove them before you merge.\nAborting\nWe don't abort, but we prompt a yes/no for each file, if the user\nwants to remove it.\n\nWe just make suggestions, we will think more about it.\n\n> Wait.  Isn't it backwards?  The existing tests _may_ be casting an\n> undesirable current behaviour in stone, but most of the time it is\n> protecting existing user's expectations.  If you have an untracked\n> file, you can rest assured that they won't be clobbered by a merge.\n\n> So we'd need to think twice and carefully examine if it makes sense\n> to update the expectations.  I haven't read the change to the tests,\n> so I cannot tell which case it is.\n\nYes, we'll figure out a solution, if there is one.\n\nThanks for your review and your quick response, it give us a lot of information,\n\nCOGONI Guillaume and BRESSAT Jonathan\n"},{"id":"454416","messageId":"xmqqee1ku5ca.fsf@gitster.g","threadId":"57624","inReplyTo":"CAA0Qn1u50ncejNtWs1AV5tcXjFC-jnmnvjFkBDQyqU4Wcvoy0g@mail.gmail.com","subject":"Re: [PATCH v1 0/2] Be nicer to the user on tracked/untracked merge conflicts","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-25T23:10:45Z","receivedAt":"2022-04-25T23:10:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Guillaume Cogoni <cogoni.guillaume@gmail.com> writes:\n\n>> So, I am not sure if this is really a good idea to begin with.  It\n>> certainly would make it slightly simpler in a trivial case, but it\n>> surely looks like a dangerous behaviour change, especially if it is\n>> done unconditionally.\n>\n> Can we create a configuration variable to avoid this problem?\n> We keep the old behavior by default, and make a configuration variable\n> for people who wants to have this new behavior, but if the user set the variable\n> a message informs it about the problem that you mention.\n>\n> Or, we add an option like git pull --doSomething.\n\nProbably a command line option (\"git merge\" would probably want the\nsame one) plus a configuration varaible to give it the default (the\nlatter is optional).\n\n> Maybe, we can think about another behaviour.\n> When the user git pull and this error occurs:\n> error: The following untracked working tree files would be overwritten by merge:\n> file1.txt\n> file2.txt\n> Please move or remove them before you merge.\n> Aborting\n> We don't abort, but we prompt a yes/no for each file, if the user\n> wants to remove it.\n\nI doubt this would fly as-is.  Especially if the action that is\noffered by the prompt is \"remove\", not \"move\", as that implies we\nare not prepared against loss of information.\n\nThere is no indication whether the untracked file1.txt matches the\ncontents we are pulling in.  Most of the time, it is very unlikely\nthat the contents being lost is identical to what the other side\nhas, so answering \"yes\" to the prompt means \"No, I do not care about\nmy garbage, and it is OK that it will forever be lost.\"  I do not\nthink we want to be encouraging people to habitually make such a\nstatement.  If we move (instead of removing) them away to somewhere,\nand give users to easily recover them after running \"git pull\", it\nmight become more palatable.\n\nI wonder if this whole thing is an attempt to work around whatever\n\"stash --untracked\" fails to do well (or perhaps there are no such\nshortcomings, but just the users are not made aware of the command\nenough).  If you have these two untracked files (file1.txt and\nfile2.txt) are \"in the way\" for a merge to succeed, I have to wonder\nif \"Please move or remove\" message that was introduced by 23cbf11b\n(merge-recursive: porcelain messages for checkout, 2010-08-11) is\nstill giving a good piece of advice to users today.\n\nWould \"git stash push -u file1.txt file2.txt\" be an easier and safer\nalternative that lets you take these files back later?  Back in\n2010, when 23cbf11b was current, \"git stash\" was a shell script and\nit seems there was no \"untracked\" option, so from that point of\nview, \"move or remove\" may have been the best they could do.\n\nNote that I never use \"git stash\" with \"untracked\" option, so I do\nnot know if it works well in this context already, or we need more\nwork before it becomes usable in this scenario.  But it smells like\nit is exactly what we might want to use in such a situation to stash\naway these untracked file1.txt and file2.txt while running the\nmerge, while allowing us to recover them after running the merge or\ndiscarding it.  I dunno.\n"},{"id":"454420","messageId":"243b40ef-a720-46aa-6657-87ac8d3c3bdc@univ-lyon1.fr","threadId":"57624","inReplyTo":"fdd9f13d14e942f3a1572866761b9580@SAMBXP02.univ-lyon1.fr","subject":"Re: [PATCH v1 0/2] Be nicer to the user on tracked/untracked merge conflicts","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@univ-lyon1.fr","sentAt":"2022-04-26T06:38:43Z","receivedAt":"2022-04-26T06:38:53Z","isPatch":true,"sender":{"key":"matthieu.moy@univ-lyon1.fr","avatar":"https://gravatar.com/avatar/8ab83b763226bd297b59ddd1463a8bfc852924577191233f73a953b86888fe0c?d=mp&s=160"},"body":"On 4/26/22 00:28, Guillaume Cogoni wrote:\n>   Junio C Hamano <gitster@pobox.com> writes:\n> \n>> So, I am not sure if this is really a good idea to begin with.  It\n>> certainly would make it slightly simpler in a trivial case, but it\n>> surely looks like a dangerous behaviour change, especially if it is\n>> done unconditionally.\n> \n> Can we create a configuration variable to avoid this problem?\n> We keep the old behavior by default, and make a configuration variable\n> for people who wants to have this new behavior, but if the user set the variable\n> a message informs it about the problem that you mention.\n> \n> Or, we add an option like git pull --doSomething.\n> \n> Maybe, we can think about another behaviour.\n> When the user git pull and this error occurs:\n> error: The following untracked working tree files would be overwritten by merge:\n> file1.txt\n> file2.txt\n> Please move or remove them before you merge.\n> Aborting\n> We don't abort, but we prompt a yes/no for each file, if the user\n> wants to remove it.\n\nGit very rarely goes interactive like this (only a few special command \nlike git send-email, git clean -i, git add -i/-p prompt the user).\n\nPrompting the user in the middle of an operation has several drawbacks:\n\n- When the command is launched from a script, the script may work most \nof the time, and sometimes pause on an interactive prompt which wasn't \nexpected from the author of the script. This can be a bit nasty when the \nscript isn't ran from a place where you can type to the standard input \nof the command or when its output is redirected.\n\n- Asking for each individual file can be tedious when there are many \nfiles. Similarly, \"rm -i\" (plain rm, not \"git rm\") is a nice safety \nmeasure, but not really convenient to me at least.\n\nIn this particular case, actually, I can't imagine a sane behavior when \nthe user wants a mix of \"yes\" / \"no\". If a single untracked file \nconflicts with what's being merged, the merge aborts, even if you're OK \nto replace other files. So I can only imagine a single yes/no answer. \nAnd then the question can be replaced with a suggestion to re-run with a \ncommand-line flag when all the conflicting files are unmodified.\n\n-- \nMatthieu Moy\nhttps://matthieu-moy.fr/\n"},{"id":"454421","messageId":"26b353a6-3c31-8f35-a282-877788c00bbf@univ-lyon1.fr","threadId":"57624","inReplyTo":"eca66375d8b34154856b7da303bf96d7@SAMBXP02.univ-lyon1.fr","subject":"Re: [PATCH v1 1/2] t7615: test how merge behave when there is untracked file","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@univ-lyon1.fr","sentAt":"2022-04-26T06:48:09Z","receivedAt":"2022-04-26T06:48:18Z","isPatch":true,"sender":{"key":"matthieu.moy@univ-lyon1.fr","avatar":"https://gravatar.com/avatar/8ab83b763226bd297b59ddd1463a8bfc852924577191233f73a953b86888fe0c?d=mp&s=160"},"body":"On 4/25/22 22:27, Jonathan wrote:\n> when there is untracked file that has the same name than file in the\n> merged branch git refuse to proceed, even when the file has the same\n> content\n> \n> t6436 test a similar thing but not especially with same content file\n\nWrite your commit message like normal english: capitalize start of \nsentence, and period at the end (we omit the period in the subject line, \nthough).\n\n> +test_expect_success 'fastforward fail when untracked file has the same content' '\n\nHere and other test names: third person => s (fail_s_, and overwrite_s_ \nin the next patch).\n\n> +\ttest_when_finished \"git branch -D B && git reset --hard init && git clean --force\" &&\n> +\tgit checkout -b B &&\n> +\ttest_commit --no-tag \"tracked\" file \"content\" &&\n> +\tgit checkout A &&\n> +\techo content >file &&\n> +\ttest_must_fail git merge B\n\nIt would make sense to grep for the correct error message in the output, \nbut maybe that's overkill.\n\n-- \nMatthieu Moy\nhttps://matthieu-moy.fr/\n"},{"id":"454449","messageId":"xmqq35hzsu0d.fsf@gitster.g","threadId":"57624","inReplyTo":"243b40ef-a720-46aa-6657-87ac8d3c3bdc@univ-lyon1.fr","subject":"Re: [PATCH v1 0/2] Be nicer to the user on tracked/untracked merge conflicts","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-26T16:13:06Z","receivedAt":"2022-04-26T16:13:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Matthieu Moy <Matthieu.Moy@univ-lyon1.fr> writes:\n\n> Git very rarely goes interactive like this (only a few special command\n> like git send-email, git clean -i, git add -i/-p prompt the user).\n>\n> Prompting the user in the middle of an operation has several drawbacks:\n> ...\n> In this particular case, actually, I can't imagine a sane behavior\n> when the user wants a mix of \"yes\" / \"no\". If a single untracked file \n> conflicts with what's being merged, the merge aborts, even if you're\n> OK to replace other files. So I can only imagine a single yes/no\n> answer. And then the question can be replaced with a suggestion to\n> re-run with a command-line flag when all the conflicting files are\n> unmodified.\n\nNicely explained.  Thanks.\n"},{"id":"454572","messageId":"CANteD_zD9ViBi5woHycU_CR1rJcv7YjKDFDiKTA8de04yrTs5Q@mail.gmail.com","threadId":"57624","inReplyTo":"xmqq35hzsu0d.fsf@gitster.g","subject":"Re: [PATCH v1 0/2] Be nicer to the user on tracked/untracked merge conflicts","fromName":"Jonathan Bressat","fromEmail":"git.jonathan.bressat@gmail.com","sentAt":"2022-04-28T10:33:34Z","receivedAt":"2022-04-28T10:33:50Z","isPatch":true,"sender":{"key":"git.jonathan.bressat@gmail.com","avatar":null},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Probably a command line option (\"git merge\" would probably want the\n> same one) plus a configuration varaible to give it the default (the\n> latter is optional).\n\nFirst, we think that add an option to pull and merge is more suited to\nour situation, and next, it could be good to add the configuration\nvariable\n\nIn unpack-trees.c there is a list of files that cause problem with merge.\nWe want to split this list to list files that have the same content, then if\nall the files have the same content, we can suggest to use the option\nto overwrite those files.\nThen we can modify the error message to show the files that have the\nsame content apart.\n\n> I wonder if this whole thing is an attempt to work around whatever\n> \"stash --untracked\" fails to do well (or perhaps there are no such\n> shortcomings, but just the users are not made aware of the command\n> enough).  If you have these two untracked files (file1.txt and\n> file2.txt) are \"in the way\" for a merge to succeed, I have to wonder\n> if \"Please move or remove\" message that was introduced by 23cbf11b\n> (merge-recursive: porcelain messages for checkout, 2010-08-11) is\n> still giving a good piece of advice to users today.\n\nWe got a similar idea, but we finally decide that it was not a very good\napproach because it's not efficient if we have a lot of files or some big files.\nAnd because if there are files that doesn't block the merge, we treat them\nanyway and they will move from the work tree, it's a bit overkill.\n\n> Note that I never use \"git stash\" with \"untracked\" option, so I do\n>  not know if it works well in this context already, or we need more\n> work before it becomes usable in this scenario.  But it smells like\n> it is exactly what we might want to use in such a situation to stash\n> away these untracked file1.txt and file2.txt while running the\n> merge, while allowing us to recover them after running the merge or\n> discarding it.  I dunno.\n\nIndeed, git stash works well with this kind of problem, however an option\nwould be easier in that specific case.\n\nThanks for you're helpfull review, you always give us a lot of good\ninformation and ideas.\n\nCogoni Guillaume and\nBressat Jonathan\n"},{"id":"456314","messageId":"20220527195545.33984-1-git.jonathan.bressat@gmail.com","threadId":"57624","inReplyTo":"CANteD_zD9ViBi5woHycU_CR1rJcv7YjKDFDiKTA8de04yrTs5Q@mail.gmail.com","subject":"[PATCH v2 0/4] Be nicer to the user on tracked/untracked merge conflicts","fromName":"Jonathan Bressat","fromEmail":"git.jonathan.bressat@gmail.com","sentAt":"2022-05-27T19:55:41Z","receivedAt":"2022-05-27T19:56:25Z","isPatch":true,"sender":{"key":"git.jonathan.bressat@gmail.com","avatar":null},"body":"We improve our last patch with adding an option to merge and pull command and a\nconfiguration variable.\nThis make the user able to modify the behavior of merge when it meet untracked files\nthat have the same content than files in merged branch.\n\nWe kept the old behavior as default.\n\nthanks\n\nJonathan Bressat and \nGuillaume Cogoni\n\nJonathan Bressat (4):\n  t6436: tests how merge behave when there is untracked file with the\n    same content\n  merge with untracked file that are the same without failure\n  add configuration variable corresponding to --overwrite-same-content\n  error message now advice to use the new option\n\n Documentation/config/merge.txt |  5 ++\n Documentation/git-merge.txt    |  4 ++\n builtin/checkout.c             |  1 +\n builtin/merge.c                |  9 +++-\n builtin/pull.c                 |  8 +++-\n cache.h                        |  3 +-\n merge-ort.c                    |  1 +\n merge-recursive.h              |  1 +\n merge.c                        |  4 +-\n sequencer.c                    |  2 +-\n t/t6436-merge-overwrite.sh     | 34 ++++++++++++++\n t/t7615-merge-untracked.sh     | 84 ++++++++++++++++++++++++++++++++++\n unpack-trees.c                 | 27 +++++++++--\n unpack-trees.h                 |  2 +\n 14 files changed, 174 insertions(+), 11 deletions(-)\n create mode 100755 t/t7615-merge-untracked.sh\n\nInterdiff contre v1 :\ndiff --git a/Documentation/config/merge.txt b/Documentation/config/merge.txt\nindex 99e83dd36e..2824dd19c7 100644\n--- a/Documentation/config/merge.txt\n+++ b/Documentation/config/merge.txt\n@@ -89,6 +89,11 @@ merge.autoStash::\n \t`--autostash` options of linkgit:git-merge[1].\n \tDefaults to false.\n \n+merge.overwritesamecontent::\n+    When set to true, it will modify the behavior of git merge \n+    to overwrite untracked files that have the same name and \n+    content than files in the merged commit.\t\n+\n merge.tool::\n \tControls which merge tool is used by linkgit:git-mergetool[1].\n \tThe list below shows the valid built-in values.\ndiff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt\nindex 3125473cc1..ceda0271c2 100644\n--- a/Documentation/git-merge.txt\n+++ b/Documentation/git-merge.txt\n@@ -100,6 +100,10 @@ will be appended to the specified message.\n \tSilently overwrite ignored files from the merge result. This\n \tis the default behavior. Use `--no-overwrite-ignore` to abort.\n \n+--overwrite-same-content::\n+       Silently overwrite untracked files that have the same content \n+       and name than files in the merged commit from the merge result.\n+\n --abort::\n \tAbort the current conflict resolution process, and\n \ttry to reconstruct the pre-merge state. If an autostash entry is\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex cc804ba8e1..1b1d1813c7 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -760,6 +760,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\t\t       &new_branch_info->commit->object.oid :\n \t\t\t\t       &new_branch_info->oid, NULL);\n \t\ttopts.preserve_ignored = !opts->overwrite_ignore;\n+\t\ttopts.overwrite_same_content = 0;/* FIXME: opts->overwrite_same_content */\n \t\ttree = parse_tree_indirect(old_branch_info->commit ?\n \t\t\t\t\t   &old_branch_info->commit->object.oid :\n \t\t\t\t\t   the_hash_algo->empty_tree);\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 74e53cf20a..936cb8480d 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -68,6 +68,7 @@ static int option_edit = -1;\n static int allow_trivial = 1, have_message, verify_signatures;\n static int check_trust_level = 1;\n static int overwrite_ignore = 1;\n+static int overwrite_same_content;\n static struct strbuf merge_msg = STRBUF_INIT;\n static struct strategy **use_strategies;\n static size_t use_strategies_nr, use_strategies_alloc;\n@@ -305,6 +306,7 @@ static struct option builtin_merge_options[] = {\n \tOPT_BOOL(0, \"overwrite-ignore\", &overwrite_ignore, N_(\"update ignored files (default)\")),\n \tOPT_BOOL(0, \"signoff\", &signoff, N_(\"add a Signed-off-by trailer\")),\n \tOPT_BOOL(0, \"no-verify\", &no_verify, N_(\"bypass pre-merge-commit and commit-msg hooks\")),\n+\tOPT_BOOL(0, \"overwrite-same-content\", &overwrite_same_content, N_(\"overwrite untracked file with the same content and name\")),\n \tOPT_END()\n };\n \n@@ -656,6 +658,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n \t} else if (!strcmp(k, \"merge.autostash\")) {\n \t\tautostash = git_config_bool(k, v);\n \t\treturn 0;\n+\t} else if (!strcmp(k,\"merge.overwritesamecontent\")) {\n+\t\toverwrite_same_content = git_config_bool(k, v);\n \t}\n \n \tstatus = fmt_merge_msg_config(k, v, cb);\n@@ -684,6 +688,7 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,\n \topts.trivial_merges_only = 1;\n \topts.merge = 1;\n \topts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */\n+\topts.overwrite_same_content = overwrite_same_content;\n \ttrees[nr_trees] = parse_tree_indirect(common);\n \tif (!trees[nr_trees++])\n \t\treturn -1;\n@@ -746,6 +751,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,\n \n \t\to.branch1 = head_arg;\n \t\to.branch2 = merge_remote_util(remoteheads->item)->name;\n+\t\to.overwrite_same_content = overwrite_same_content;\n \n \t\tfor (j = common; j; j = j->next)\n \t\t\tcommit_list_insert(j->item, &reversed);\n@@ -1573,7 +1579,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \t\tif (checkout_fast_forward(the_repository,\n \t\t\t\t\t  &head_commit->object.oid,\n \t\t\t\t\t  &commit->object.oid,\n-\t\t\t\t\t  overwrite_ignore)) {\n+\t\t\t\t\t  overwrite_ignore,\n+\t\t\t\t\t  overwrite_same_content)) {\n \t\t\tapply_autostash(git_path_merge_autostash(the_repository));\n \t\t\tret = 1;\n \t\t\tgoto done;\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 100cbf9fb8..46ef68e721 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -92,6 +92,7 @@ static struct strvec opt_strategies = STRVEC_INIT;\n static struct strvec opt_strategy_opts = STRVEC_INIT;\n static char *opt_gpg_sign;\n static int opt_allow_unrelated_histories;\n+static int opt_overwrite_same_content;\n \n /* Options passed to git-fetch */\n static char *opt_all;\n@@ -182,6 +183,7 @@ static struct option pull_options[] = {\n \tOPT_SET_INT(0, \"allow-unrelated-histories\",\n \t\t    &opt_allow_unrelated_histories,\n \t\t    N_(\"allow merging unrelated histories\"), 1),\n+\tOPT_BOOL(0, \"overwrite-same-content\", &opt_overwrite_same_content, N_(\"overwrite untracked file with the same content and name\")),\n \n \t/* Options passed to git-fetch */\n \tOPT_GROUP(N_(\"Options related to fetching\")),\n@@ -612,7 +614,7 @@ static int pull_into_void(const struct object_id *merge_head,\n \t */\n \tif (checkout_fast_forward(the_repository,\n \t\t\t\t  the_hash_algo->empty_tree,\n-\t\t\t\t  merge_head, 0))\n+\t\t\t\t  merge_head, 0, opt_overwrite_same_content))\n \t\treturn 1;\n \n \tif (update_ref(\"initial pull\", \"HEAD\", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR))\n@@ -679,6 +681,8 @@ static int run_merge(void)\n \t\tstrvec_pushf(&args, \"--cleanup=%s\", cleanup_arg);\n \tif (opt_ff)\n \t\tstrvec_push(&args, opt_ff);\n+\tif (opt_overwrite_same_content)\n+\t\tstrvec_push(&args, \"--overwrite-same-content\");\n \tif (opt_verify)\n \t\tstrvec_push(&args, opt_verify);\n \tif (opt_verify_signatures)\n@@ -1078,7 +1082,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \t\t\t\"commit %s.\"), oid_to_hex(&orig_head));\n \n \t\tif (checkout_fast_forward(the_repository, &orig_head,\n-\t\t\t\t\t  &curr_head, 0))\n+\t\t\t\t\t  &curr_head, 0, opt_overwrite_same_content))\n \t\t\tdie(_(\"Cannot fast-forward your working tree.\\n\"\n \t\t\t\t\"After making sure that you saved anything precious from\\n\"\n \t\t\t\t\"$ git diff %s\\n\"\ndiff --git a/cache.h b/cache.h\nindex 281f00ab1b..163367ab41 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1858,7 +1858,8 @@ int try_merge_command(struct repository *r,\n int checkout_fast_forward(struct repository *r,\n \t\t\t  const struct object_id *from,\n \t\t\t  const struct object_id *to,\n-\t\t\t  int overwrite_ignore);\n+\t\t\t  int overwrite_ignore,\n+\t\t\t  int overwrite_same_content);\n \n \n int sane_execvp(const char *file, char *const argv[]);\ndiff --git a/merge-ort.c b/merge-ort.c\nindex c319797021..a8d1496b4a 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -4066,6 +4066,7 @@ static int checkout(struct merge_options *opt,\n \tunpack_opts.verbose_update = (opt->verbosity > 2);\n \tunpack_opts.fn = twoway_merge;\n \tunpack_opts.preserve_ignored = 0; /* FIXME: !opts->overwrite_ignore */\n+\tunpack_opts.overwrite_same_content = opt->overwrite_same_content;\n \tparse_tree(prev);\n \tinit_tree_desc(&trees[0], prev->buffer, prev->size);\n \tparse_tree(next);\ndiff --git a/merge-recursive.h b/merge-recursive.h\nindex 0795a1d3ec..6f8c5678e0 100644\n--- a/merge-recursive.h\n+++ b/merge-recursive.h\n@@ -46,6 +46,7 @@ struct merge_options {\n \t/* miscellaneous control options */\n \tconst char *subtree_shift;\n \tunsigned renormalize : 1;\n+\tint overwrite_same_content;\n \n \t/* internal fields used by the implementation */\n \tstruct merge_options_internal *priv;\ndiff --git a/merge.c b/merge.c\nindex 2382ff66d3..410c92d235 100644\n--- a/merge.c\n+++ b/merge.c\n@@ -47,7 +47,8 @@ int try_merge_command(struct repository *r,\n int checkout_fast_forward(struct repository *r,\n \t\t\t  const struct object_id *head,\n \t\t\t  const struct object_id *remote,\n-\t\t\t  int overwrite_ignore)\n+\t\t\t  int overwrite_ignore,\n+\t\t\t  int overwrite_same_content)\n {\n \tstruct tree *trees[MAX_UNPACK_TREES];\n \tstruct unpack_trees_options opts;\n@@ -80,6 +81,7 @@ int checkout_fast_forward(struct repository *r,\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.preserve_ignored = !overwrite_ignore;\n+\topts.overwrite_same_content = overwrite_same_content;\n \n \topts.head_idx = 1;\n \topts.src_index = r->index;\ndiff --git a/sequencer.c b/sequencer.c\nindex 5213d16e97..d11802c542 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -529,7 +529,7 @@ static int fast_forward_to(struct repository *r,\n \tstruct strbuf err = STRBUF_INIT;\n \n \trepo_read_index(r);\n-\tif (checkout_fast_forward(r, from, to, 1))\n+\tif (checkout_fast_forward(r, from, to, 1, 0))\n \t\treturn -1; /* the callee should have complained already */\n \n \tstrbuf_addf(&sb, _(\"%s: fast-forward\"), _(action_name(opts)));\ndiff --git a/t/t6436-merge-overwrite.sh b/t/t6436-merge-overwrite.sh\nindex c0b7bd7c3f..bb323b1ee3 100755\n--- a/t/t6436-merge-overwrite.sh\n+++ b/t/t6436-merge-overwrite.sh\n@@ -204,4 +204,38 @@ test_expect_success 'will not clobber WT/index when merging into unborn' '\n \tgrep bar untracked-file\n '\n \n+test_expect_success 'create branch A' '\n+\tgit reset --hard c0 &&\n+\tgit checkout -b A\n+'\n+\n+test_expect_success 'fastforward will not overwrite untracked file with the same content' '\n+\ttest_when_finished \"git branch -D B && git reset --hard c0 && git clean --force\" &&\n+\tgit checkout -b B &&\n+\ttest_commit --no-tag \"tracked\" file \"content\" &&\n+\tgit checkout A &&\n+\techo content >file &&\n+\ttest_must_fail git merge B\n+'\n+\n+test_expect_success 'will not overwrite untracked file with the same content' '\n+\ttest_when_finished \"git branch -D B && git reset --hard c0 && git clean --force\" &&\n+\tgit checkout -b B &&\n+\ttest_commit --no-tag \"tracked\" file \"content\" fileB \"content\" &&\n+\tgit checkout A &&\n+\ttest_commit --no-tag \"exA\" fileA \"content\" &&\n+\techo content >file &&\n+\ttest_must_fail git merge B\n+'\n+\n+test_expect_success 'will not overwrite unstaged file with the same content' '\n+\ttest_when_finished \"git branch -D B && git reset --hard c0 && git clean --force\" &&\n+\ttest_commit --no-tag \"unstaged\" file \"other\" &&\n+\tgit checkout -b B &&\n+\ttest_commit --no-tag \"staged\" file \"content\" &&\n+\tgit checkout A &&\n+\techo content >file &&\n+\ttest_must_fail git merge B\n+'\n+\n test_done\ndiff --git a/t/t7615-merge-untracked.sh b/t/t7615-merge-untracked.sh\nindex 99f8bae4c0..cfefd8f473 100755\n--- a/t/t7615-merge-untracked.sh\n+++ b/t/t7615-merge-untracked.sh\n@@ -1,6 +1,6 @@\n #!/bin/sh\n \n-test_description='test when merge with untracked file'\n+test_description='test when merge with untracked files and the option --overwrite-same-content'\n \n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n@@ -18,7 +18,7 @@ test_expect_success 'fastforward overwrite untracked file that has the same cont\n \ttest_commit --no-tag \"tracked\" file \"content\" &&\n \tgit checkout A &&\n \techo content >file &&\n-\tgit merge B\n+\tgit merge --overwrite-same-content B\n '\n \n test_expect_success 'fastforward fail when untracked file has different content' '\n@@ -27,7 +27,7 @@ test_expect_success 'fastforward fail when untracked file has different content'\n \ttest_commit --no-tag \"tracked\" file \"content\" &&\n \tgit switch A &&\n \techo other >file &&\n-\ttest_must_fail git merge B\n+\ttest_must_fail git merge --overwrite-same-content B\n '\n \n test_expect_success 'normal merge overwrite untracked file that has the same content' '\n@@ -37,7 +37,7 @@ test_expect_success 'normal merge overwrite untracked file that has the same con\n \tgit switch A &&\n \ttest_commit --no-tag \"exA\" fileA \"content\" &&\n \techo content >file &&\n-\tgit merge B\n+\tgit merge --overwrite-same-content B\n '\n \n test_expect_success 'normal merge fail when untracked file has different content' '\n@@ -47,7 +47,7 @@ test_expect_success 'normal merge fail when untracked file has different content\n \tgit switch A &&\n \ttest_commit --no-tag \"exA\" fileA \"content\" &&\n \techo dif >file &&\n-\ttest_must_fail git merge B\n+\ttest_must_fail git merge --overwrite-same-content B\n '\n \n test_expect_success 'merge fail when tracked file modification is unstaged' '\n@@ -57,7 +57,28 @@ test_expect_success 'merge fail when tracked file modification is unstaged' '\n \ttest_commit --no-tag \"staged\" file \"content\" &&\n \tgit switch A &&\n \techo content >file &&\n-\ttest_must_fail git merge B\n+\ttest_must_fail git merge --overwrite-same-content B\n+'\n+\n+test_expect_success 'fastforward overwrite untracked file that has the same content with the configuration variable' '\n+\ttest_when_finished \"git branch -D B && git reset --hard init && git clean --force\" &&\n+\ttest_config merge.overwritesamecontent true &&\n+\tgit checkout -b B &&\n+\ttest_commit --no-tag \"tracked\" file \"content\" &&\n+\tgit checkout A &&\n+\techo content >file &&\n+\tgit merge B\n+'\n+\n+test_expect_success 'normal merge overwrite untracked file that has the same content with the configuration variable' '\n+\ttest_when_finished \"git branch -D B && git reset --hard init && git clean --force\" &&\n+\ttest_config merge.overwritesamecontent true &&\n+\tgit checkout -b B &&\n+\ttest_commit --no-tag \"tracked\" file \"content\" fileB \"content\" &&\n+\tgit switch A &&\n+\ttest_commit --no-tag \"exA\" fileA \"content\" &&\n+\techo content >file &&\n+\tgit merge B\n '\n \n test_done\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 61e06c04be..6c660b084b 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -158,17 +158,17 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,\n \tif (!strcmp(cmd, \"checkout\"))\n \t\tmsg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE)\n \t\t      ? _(\"The following untracked working tree files would be overwritten by checkout:\\n%%s\"\n-\t\t\t  \"Please move or remove them before you switch branches.\")\n+\t\t\t  \"Please move or remove them before you switch branches.%%s\")\n \t\t      : _(\"The following untracked working tree files would be overwritten by checkout:\\n%%s\");\n \telse if (!strcmp(cmd, \"merge\"))\n \t\tmsg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE)\n \t\t      ? _(\"The following untracked working tree files would be overwritten by merge:\\n%%s\"\n-\t\t\t  \"Please move or remove them before you merge.\")\n+\t\t\t  \"Please move or remove them before you merge.%%s\")\n \t\t      : _(\"The following untracked working tree files would be overwritten by merge:\\n%%s\");\n \telse\n \t\tmsg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE)\n \t\t      ? _(\"The following untracked working tree files would be overwritten by %s:\\n%%s\"\n-\t\t\t  \"Please move or remove them before you %s.\")\n+\t\t\t  \"Please move or remove them before you %s.%%s\")\n \t\t      : _(\"The following untracked working tree files would be overwritten by %s:\\n%%s\");\n \tmsgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] =\n \t\tstrvec_pushf(&opts->msgs_to_free, msg, cmd, cmd);\n@@ -251,6 +251,14 @@ static void display_error_msgs(struct unpack_trees_options *o)\n {\n \tint e;\n \tunsigned error_displayed = 0;\n+\tconst char *can_overwrite_msg;\n+\n+\tif (o->can_overwrite) {\n+\t\tcan_overwrite_msg = _(\"\\nYou can also rerun the command with --overwrite-same-content to overwrite files with same content.\");\n+\t} else {\n+\t\tcan_overwrite_msg = \"\";\n+\t}\n+\n \tfor (e = 0; e < NB_UNPACK_TREES_ERROR_TYPES; e++) {\n \t\tstruct string_list *rejects = &o->unpack_rejects[e];\n \n@@ -261,7 +269,8 @@ static void display_error_msgs(struct unpack_trees_options *o)\n \t\t\terror_displayed = 1;\n \t\t\tfor (i = 0; i < rejects->nr; i++)\n \t\t\t\tstrbuf_addf(&path, \"\\t%s\\n\", rejects->items[i].string);\n-\t\t\terror(ERRORMSG(o, e), super_prefixed(path.buf));\n+\n+\t\t\terror(ERRORMSG(o, e), super_prefixed(path.buf), can_overwrite_msg);\n \t\t\tstrbuf_release(&path);\n \t\t}\n \t\tstring_list_clear(rejects, 0);\n@@ -1711,6 +1720,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n \tstruct pattern_list pl;\n \tint free_pattern_list = 0;\n \tstruct dir_struct dir = DIR_INIT;\n+\to->can_overwrite = 1;\n \n \tif (o->reset == UNPACK_RESET_INVALID)\n \t\tBUG(\"o->reset had a value of 1; should be UNPACK_TREES_*_UNTRACKED\");\n@@ -2257,8 +2267,12 @@ static int check_ok_to_remove(const char *name, int len, int dtype,\n \tif (result) {\n \t\tif (result->ce_flags & CE_REMOVE)\n \t\t\treturn 0;\n-\t} else if (!ie_modified(&o->result, ce, st, 0)) {\n-\t\treturn 0;\n+\t} else if (ce && !ie_modified(o->src_index, ce, st, 0)) {\n+\t\tif(o->overwrite_same_content) {\n+\t\t\treturn 0;\n+\t\t}\n+\t} else {\n+\t\to->can_overwrite = 0;\n \t}\n \n \treturn add_rejected_path(o, error_type, name);\n@@ -2266,8 +2280,8 @@ static int check_ok_to_remove(const char *name, int len, int dtype,\n \n /*\n  * We do not want to remove or overwrite a working tree file that\n- * is not tracked, unless it is ignored and unless it has the same\n- * content than the merged file.\n+ * is not tracked, unless it is ignored or it has the same content\n+ * than the merged file with the option --overwrite_same_content.\n  */\n static int verify_absent_1(const struct cache_entry *ce,\n \t\t\t   enum unpack_trees_error_types error_type,\ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex efb9edfbb2..2be74ce5bf 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -71,6 +71,8 @@ struct unpack_trees_options {\n \t\t     quiet,\n \t\t     exiting_early,\n \t\t     show_all_errors,\n+\t\t     overwrite_same_content,\n+\t\t     can_overwrite,\n \t\t     dry_run;\n \tenum unpack_trees_reset_type reset;\n \tconst char *prefix;\n-- \n2.35.1.10.g88248585b1.dirty\n\n"},{"id":"456315","messageId":"20220527195545.33984-2-git.jonathan.bressat@gmail.com","threadId":"57624","inReplyTo":"20220527195545.33984-1-git.jonathan.bressat@gmail.com","subject":"[PATCH v2 1/4] t6436: tests how merge behave when there is untracked file with the same content","fromName":"Jonathan Bressat","fromEmail":"git.jonathan.bressat@gmail.com","sentAt":"2022-05-27T19:55:42Z","receivedAt":"2022-05-27T19:56:31Z","isPatch":true,"sender":{"key":"git.jonathan.bressat@gmail.com","avatar":null},"body":"add test to show explicitly that merge doesn't overwrite untracked files\nor unstaged even when they have the same content than files int the\nmerged commit\n\nSigned-off-by: Jonathan Bressat <git.jonathan.bressat@gmail.com>\nSigned-off-by: COGONI Guillaume <cogoni.guillaume@gmail.com>\n---\n t/t6436-merge-overwrite.sh | 34 ++++++++++++++++++++++++++++++++++\n 1 file changed, 34 insertions(+)\n\ndiff --git a/t/t6436-merge-overwrite.sh b/t/t6436-merge-overwrite.sh\nindex c0b7bd7c3f..bb323b1ee3 100755\n--- a/t/t6436-merge-overwrite.sh\n+++ b/t/t6436-merge-overwrite.sh\n@@ -204,4 +204,38 @@ test_expect_success 'will not clobber WT/index when merging into unborn' '\n \tgrep bar untracked-file\n '\n \n+test_expect_success 'create branch A' '\n+\tgit reset --hard c0 &&\n+\tgit checkout -b A\n+'\n+\n+test_expect_success 'fastforward will not overwrite untracked file with the same content' '\n+\ttest_when_finished \"git branch -D B && git reset --hard c0 && git clean --force\" &&\n+\tgit checkout -b B &&\n+\ttest_commit --no-tag \"tracked\" file \"content\" &&\n+\tgit checkout A &&\n+\techo content >file &&\n+\ttest_must_fail git merge B\n+'\n+\n+test_expect_success 'will not overwrite untracked file with the same content' '\n+\ttest_when_finished \"git branch -D B && git reset --hard c0 && git clean --force\" &&\n+\tgit checkout -b B &&\n+\ttest_commit --no-tag \"tracked\" file \"content\" fileB \"content\" &&\n+\tgit checkout A &&\n+\ttest_commit --no-tag \"exA\" fileA \"content\" &&\n+\techo content >file &&\n+\ttest_must_fail git merge B\n+'\n+\n+test_expect_success 'will not overwrite unstaged file with the same content' '\n+\ttest_when_finished \"git branch -D B && git reset --hard c0 && git clean --force\" &&\n+\ttest_commit --no-tag \"unstaged\" file \"other\" &&\n+\tgit checkout -b B &&\n+\ttest_commit --no-tag \"staged\" file \"content\" &&\n+\tgit checkout A &&\n+\techo content >file &&\n+\ttest_must_fail git merge B\n+'\n+\n test_done\n-- \n2.35.1.10.g88248585b1.dirty\n\n"},{"id":"456316","messageId":"20220527195545.33984-3-git.jonathan.bressat@gmail.com","threadId":"57624","inReplyTo":"20220527195545.33984-1-git.jonathan.bressat@gmail.com","subject":"[PATCH v2 2/4] merge with untracked file that are the same without failure","fromName":"Jonathan Bressat","fromEmail":"git.jonathan.bressat@gmail.com","sentAt":"2022-05-27T19:55:43Z","receivedAt":"2022-05-27T19:56:38Z","isPatch":true,"sender":{"key":"git.jonathan.bressat@gmail.com","avatar":null},"body":"Keep the old behavior as default.\n\nAdd the option --overwrite-same-content, when this option is used merge\nwill overwrite untracked file that have the same content.\n\nIt make the merge nicer to the user, usefull for a simple utilisation,\nfor exemple if you copy and paste files from another project and then\nyou decide to pull this project, git will not proceed even if you didn't\nmodify those files.\n\nt7615 tests this new behavior.\n\nSigned-off-by: Jonathan Bressat <git.jonathan.bressat@gmail.com>\nSigned-off-by: COGONI Guillaume <cogoni.guillaume@gmail.com>\n---\n Documentation/git-merge.txt |  4 +++\n builtin/merge.c             |  7 ++++-\n builtin/pull.c              |  8 +++--\n cache.h                     |  3 +-\n merge-ort.c                 |  1 +\n merge-recursive.h           |  1 +\n merge.c                     |  4 ++-\n sequencer.c                 |  2 +-\n t/t7615-merge-untracked.sh  | 63 +++++++++++++++++++++++++++++++++++++\n unpack-trees.c              |  7 ++++-\n unpack-trees.h              |  1 +\n 11 files changed, 94 insertions(+), 7 deletions(-)\n create mode 100755 t/t7615-merge-untracked.sh\n\ndiff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt\nindex 3125473cc1..ceda0271c2 100644\n--- a/Documentation/git-merge.txt\n+++ b/Documentation/git-merge.txt\n@@ -100,6 +100,10 @@ will be appended to the specified message.\n \tSilently overwrite ignored files from the merge result. This\n \tis the default behavior. Use `--no-overwrite-ignore` to abort.\n \n+--overwrite-same-content::\n+       Silently overwrite untracked files that have the same content \n+       and name than files in the merged commit from the merge result.\n+\n --abort::\n \tAbort the current conflict resolution process, and\n \ttry to reconstruct the pre-merge state. If an autostash entry is\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 74e53cf20a..fffae81068 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -68,6 +68,7 @@ static int option_edit = -1;\n static int allow_trivial = 1, have_message, verify_signatures;\n static int check_trust_level = 1;\n static int overwrite_ignore = 1;\n+static int overwrite_same_content;\n static struct strbuf merge_msg = STRBUF_INIT;\n static struct strategy **use_strategies;\n static size_t use_strategies_nr, use_strategies_alloc;\n@@ -305,6 +306,7 @@ static struct option builtin_merge_options[] = {\n \tOPT_BOOL(0, \"overwrite-ignore\", &overwrite_ignore, N_(\"update ignored files (default)\")),\n \tOPT_BOOL(0, \"signoff\", &signoff, N_(\"add a Signed-off-by trailer\")),\n \tOPT_BOOL(0, \"no-verify\", &no_verify, N_(\"bypass pre-merge-commit and commit-msg hooks\")),\n+\tOPT_BOOL(0, \"overwrite-same-content\", &overwrite_same_content, N_(\"overwrite untracked file with the same content and name\")),\n \tOPT_END()\n };\n \n@@ -684,6 +686,7 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,\n \topts.trivial_merges_only = 1;\n \topts.merge = 1;\n \topts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */\n+\topts.overwrite_same_content = overwrite_same_content;\n \ttrees[nr_trees] = parse_tree_indirect(common);\n \tif (!trees[nr_trees++])\n \t\treturn -1;\n@@ -746,6 +749,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,\n \n \t\to.branch1 = head_arg;\n \t\to.branch2 = merge_remote_util(remoteheads->item)->name;\n+\t\to.overwrite_same_content = overwrite_same_content;\n \n \t\tfor (j = common; j; j = j->next)\n \t\t\tcommit_list_insert(j->item, &reversed);\n@@ -1573,7 +1577,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \t\tif (checkout_fast_forward(the_repository,\n \t\t\t\t\t  &head_commit->object.oid,\n \t\t\t\t\t  &commit->object.oid,\n-\t\t\t\t\t  overwrite_ignore)) {\n+\t\t\t\t\t  overwrite_ignore,\n+\t\t\t\t\t  overwrite_same_content)) {\n \t\t\tapply_autostash(git_path_merge_autostash(the_repository));\n \t\t\tret = 1;\n \t\t\tgoto done;\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 100cbf9fb8..46ef68e721 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -92,6 +92,7 @@ static struct strvec opt_strategies = STRVEC_INIT;\n static struct strvec opt_strategy_opts = STRVEC_INIT;\n static char *opt_gpg_sign;\n static int opt_allow_unrelated_histories;\n+static int opt_overwrite_same_content;\n \n /* Options passed to git-fetch */\n static char *opt_all;\n@@ -182,6 +183,7 @@ static struct option pull_options[] = {\n \tOPT_SET_INT(0, \"allow-unrelated-histories\",\n \t\t    &opt_allow_unrelated_histories,\n \t\t    N_(\"allow merging unrelated histories\"), 1),\n+\tOPT_BOOL(0, \"overwrite-same-content\", &opt_overwrite_same_content, N_(\"overwrite untracked file with the same content and name\")),\n \n \t/* Options passed to git-fetch */\n \tOPT_GROUP(N_(\"Options related to fetching\")),\n@@ -612,7 +614,7 @@ static int pull_into_void(const struct object_id *merge_head,\n \t */\n \tif (checkout_fast_forward(the_repository,\n \t\t\t\t  the_hash_algo->empty_tree,\n-\t\t\t\t  merge_head, 0))\n+\t\t\t\t  merge_head, 0, opt_overwrite_same_content))\n \t\treturn 1;\n \n \tif (update_ref(\"initial pull\", \"HEAD\", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR))\n@@ -679,6 +681,8 @@ static int run_merge(void)\n \t\tstrvec_pushf(&args, \"--cleanup=%s\", cleanup_arg);\n \tif (opt_ff)\n \t\tstrvec_push(&args, opt_ff);\n+\tif (opt_overwrite_same_content)\n+\t\tstrvec_push(&args, \"--overwrite-same-content\");\n \tif (opt_verify)\n \t\tstrvec_push(&args, opt_verify);\n \tif (opt_verify_signatures)\n@@ -1078,7 +1082,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \t\t\t\"commit %s.\"), oid_to_hex(&orig_head));\n \n \t\tif (checkout_fast_forward(the_repository, &orig_head,\n-\t\t\t\t\t  &curr_head, 0))\n+\t\t\t\t\t  &curr_head, 0, opt_overwrite_same_content))\n \t\t\tdie(_(\"Cannot fast-forward your working tree.\\n\"\n \t\t\t\t\"After making sure that you saved anything precious from\\n\"\n \t\t\t\t\"$ git diff %s\\n\"\ndiff --git a/cache.h b/cache.h\nindex 281f00ab1b..163367ab41 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1858,7 +1858,8 @@ int try_merge_command(struct repository *r,\n int checkout_fast_forward(struct repository *r,\n \t\t\t  const struct object_id *from,\n \t\t\t  const struct object_id *to,\n-\t\t\t  int overwrite_ignore);\n+\t\t\t  int overwrite_ignore,\n+\t\t\t  int overwrite_same_content);\n \n \n int sane_execvp(const char *file, char *const argv[]);\ndiff --git a/merge-ort.c b/merge-ort.c\nindex c319797021..a8d1496b4a 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -4066,6 +4066,7 @@ static int checkout(struct merge_options *opt,\n \tunpack_opts.verbose_update = (opt->verbosity > 2);\n \tunpack_opts.fn = twoway_merge;\n \tunpack_opts.preserve_ignored = 0; /* FIXME: !opts->overwrite_ignore */\n+\tunpack_opts.overwrite_same_content = opt->overwrite_same_content;\n \tparse_tree(prev);\n \tinit_tree_desc(&trees[0], prev->buffer, prev->size);\n \tparse_tree(next);\ndiff --git a/merge-recursive.h b/merge-recursive.h\nindex 0795a1d3ec..6f8c5678e0 100644\n--- a/merge-recursive.h\n+++ b/merge-recursive.h\n@@ -46,6 +46,7 @@ struct merge_options {\n \t/* miscellaneous control options */\n \tconst char *subtree_shift;\n \tunsigned renormalize : 1;\n+\tint overwrite_same_content;\n \n \t/* internal fields used by the implementation */\n \tstruct merge_options_internal *priv;\ndiff --git a/merge.c b/merge.c\nindex 2382ff66d3..410c92d235 100644\n--- a/merge.c\n+++ b/merge.c\n@@ -47,7 +47,8 @@ int try_merge_command(struct repository *r,\n int checkout_fast_forward(struct repository *r,\n \t\t\t  const struct object_id *head,\n \t\t\t  const struct object_id *remote,\n-\t\t\t  int overwrite_ignore)\n+\t\t\t  int overwrite_ignore,\n+\t\t\t  int overwrite_same_content)\n {\n \tstruct tree *trees[MAX_UNPACK_TREES];\n \tstruct unpack_trees_options opts;\n@@ -80,6 +81,7 @@ int checkout_fast_forward(struct repository *r,\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.preserve_ignored = !overwrite_ignore;\n+\topts.overwrite_same_content = overwrite_same_content;\n \n \topts.head_idx = 1;\n \topts.src_index = r->index;\ndiff --git a/sequencer.c b/sequencer.c\nindex 5213d16e97..d11802c542 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -529,7 +529,7 @@ static int fast_forward_to(struct repository *r,\n \tstruct strbuf err = STRBUF_INIT;\n \n \trepo_read_index(r);\n-\tif (checkout_fast_forward(r, from, to, 1))\n+\tif (checkout_fast_forward(r, from, to, 1, 0))\n \t\treturn -1; /* the callee should have complained already */\n \n \tstrbuf_addf(&sb, _(\"%s: fast-forward\"), _(action_name(opts)));\ndiff --git a/t/t7615-merge-untracked.sh b/t/t7615-merge-untracked.sh\nnew file mode 100755\nindex 0000000000..05a34cf03f\n--- /dev/null\n+++ b/t/t7615-merge-untracked.sh\n@@ -0,0 +1,63 @@\n+#!/bin/sh\n+\n+test_description='test when merge with untracked files and the option --overwrite-same-content'\n+\n+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit \"init\" README.md \"content\" &&\n+\tgit checkout -b A\n+'\n+\n+test_expect_success 'fastforward overwrite untracked file that has the same content' '\n+\ttest_when_finished \"git branch -D B && git reset --hard init && git clean --force\" &&\n+\tgit checkout -b B &&\n+\ttest_commit --no-tag \"tracked\" file \"content\" &&\n+\tgit checkout A &&\n+\techo content >file &&\n+\tgit merge --overwrite-same-content B\n+'\n+\n+test_expect_success 'fastforward fail when untracked file has different content' '\n+\ttest_when_finished \"git branch -D B && git reset --hard init && git clean --force\" &&\n+\tgit checkout -b B &&\n+\ttest_commit --no-tag \"tracked\" file \"content\" &&\n+\tgit switch A &&\n+\techo other >file &&\n+\ttest_must_fail git merge --overwrite-same-content B\n+'\n+\n+test_expect_success 'normal merge overwrite untracked file that has the same content' '\n+\ttest_when_finished \"git branch -D B && git reset --hard init && git clean --force\" &&\n+\tgit checkout -b B &&\n+\ttest_commit --no-tag \"tracked\" file \"content\" fileB \"content\" &&\n+\tgit switch A &&\n+\ttest_commit --no-tag \"exA\" fileA \"content\" &&\n+\techo content >file &&\n+\tgit merge --overwrite-same-content B\n+'\n+\n+test_expect_success 'normal merge fail when untracked file has different content' '\n+\ttest_when_finished \"git branch -D B && git reset --hard init && git clean --force\" &&\n+\tgit checkout -b B &&\n+\ttest_commit --no-tag \"tracked\" file \"content\" fileB \"content\" &&\n+\tgit switch A &&\n+\ttest_commit --no-tag \"exA\" fileA \"content\" &&\n+\techo dif >file &&\n+\ttest_must_fail git merge --overwrite-same-content B\n+'\n+\n+test_expect_success 'merge fail when tracked file modification is unstaged' '\n+\ttest_when_finished \"git branch -D B && git reset --hard init && git clean --force\" &&\n+\ttest_commit --no-tag \"unstaged\" file \"other\" &&\n+\tgit checkout -b B &&\n+\ttest_commit --no-tag \"staged\" file \"content\" &&\n+\tgit switch A &&\n+\techo content >file &&\n+\ttest_must_fail git merge --overwrite-same-content B\n+'\n+\n+test_done\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 360844bda3..1a52be723e 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -2257,6 +2257,10 @@ static int check_ok_to_remove(const char *name, int len, int dtype,\n \tif (result) {\n \t\tif (result->ce_flags & CE_REMOVE)\n \t\t\treturn 0;\n+\t} else if (ce && !ie_modified(o->src_index, ce, st, 0)) {\n+\t\tif(o->overwrite_same_content) {\n+\t\t\treturn 0;\n+\t\t}\n \t}\n \n \treturn add_rejected_path(o, error_type, name);\n@@ -2264,7 +2268,8 @@ static int check_ok_to_remove(const char *name, int len, int dtype,\n \n /*\n  * We do not want to remove or overwrite a working tree file that\n- * is not tracked, unless it is ignored.\n+ * is not tracked, unless it is ignored or it has the same content\n+ * than the merged file with the option --overwrite_same_content.\n  */\n static int verify_absent_1(const struct cache_entry *ce,\n \t\t\t   enum unpack_trees_error_types error_type,\ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex efb9edfbb2..ebe4be0b35 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -71,6 +71,7 @@ struct unpack_trees_options {\n \t\t     quiet,\n \t\t     exiting_early,\n \t\t     show_all_errors,\n+\t\t     overwrite_same_content,\n \t\t     dry_run;\n \tenum unpack_trees_reset_type reset;\n \tconst char *prefix;\n-- \n2.35.1.10.g88248585b1.dirty\n\n"},{"id":"456317","messageId":"20220527195545.33984-4-git.jonathan.bressat@gmail.com","threadId":"57624","inReplyTo":"20220527195545.33984-1-git.jonathan.bressat@gmail.com","subject":"[PATCH v2 3/4] add configuration variable corresponding to --overwrite-same-content","fromName":"Jonathan Bressat","fromEmail":"git.jonathan.bressat@gmail.com","sentAt":"2022-05-27T19:55:44Z","receivedAt":"2022-05-27T19:56:39Z","isPatch":true,"sender":{"key":"git.jonathan.bressat@gmail.com","avatar":null},"body":"Configuration variable merge.overwritesamecontent corresponding to\nthe --overwrite-same-content option.\n\nThis allow merge to overwrite untracked files that have the same\ncontent, a configuration variable is interressant because some people\nmay want this activated as default to not have to use the option every\ntime\n\nSigned-off-by: Jonathan Bressat <git.jonathan.bressat@gmail.com>\nSigned-off-by: COGONI Guillaume <cogoni.guillaume@gmail.com>\n---\n Documentation/config/merge.txt |  5 +++++\n builtin/merge.c                |  2 ++\n t/t7615-merge-untracked.sh     | 21 +++++++++++++++++++++\n 3 files changed, 28 insertions(+)\n\ndiff --git a/Documentation/config/merge.txt b/Documentation/config/merge.txt\nindex 99e83dd36e..2824dd19c7 100644\n--- a/Documentation/config/merge.txt\n+++ b/Documentation/config/merge.txt\n@@ -89,6 +89,11 @@ merge.autoStash::\n \t`--autostash` options of linkgit:git-merge[1].\n \tDefaults to false.\n \n+merge.overwritesamecontent::\n+    When set to true, it will modify the behavior of git merge \n+    to overwrite untracked files that have the same name and \n+    content than files in the merged commit.\t\n+\n merge.tool::\n \tControls which merge tool is used by linkgit:git-mergetool[1].\n \tThe list below shows the valid built-in values.\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex fffae81068..936cb8480d 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -658,6 +658,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n \t} else if (!strcmp(k, \"merge.autostash\")) {\n \t\tautostash = git_config_bool(k, v);\n \t\treturn 0;\n+\t} else if (!strcmp(k,\"merge.overwritesamecontent\")) {\n+\t\toverwrite_same_content = git_config_bool(k, v);\n \t}\n \n \tstatus = fmt_merge_msg_config(k, v, cb);\ndiff --git a/t/t7615-merge-untracked.sh b/t/t7615-merge-untracked.sh\nindex 05a34cf03f..cfefd8f473 100755\n--- a/t/t7615-merge-untracked.sh\n+++ b/t/t7615-merge-untracked.sh\n@@ -60,4 +60,25 @@ test_expect_success 'merge fail when tracked file modification is unstaged' '\n \ttest_must_fail git merge --overwrite-same-content B\n '\n \n+test_expect_success 'fastforward overwrite untracked file that has the same content with the configuration variable' '\n+\ttest_when_finished \"git branch -D B && git reset --hard init && git clean --force\" &&\n+\ttest_config merge.overwritesamecontent true &&\n+\tgit checkout -b B &&\n+\ttest_commit --no-tag \"tracked\" file \"content\" &&\n+\tgit checkout A &&\n+\techo content >file &&\n+\tgit merge B\n+'\n+\n+test_expect_success 'normal merge overwrite untracked file that has the same content with the configuration variable' '\n+\ttest_when_finished \"git branch -D B && git reset --hard init && git clean --force\" &&\n+\ttest_config merge.overwritesamecontent true &&\n+\tgit checkout -b B &&\n+\ttest_commit --no-tag \"tracked\" file \"content\" fileB \"content\" &&\n+\tgit switch A &&\n+\ttest_commit --no-tag \"exA\" fileA \"content\" &&\n+\techo content >file &&\n+\tgit merge B\n+'\n+\n test_done\n-- \n2.35.1.10.g88248585b1.dirty\n\n"},{"id":"456318","messageId":"20220527195545.33984-5-git.jonathan.bressat@gmail.com","threadId":"57624","inReplyTo":"20220527195545.33984-1-git.jonathan.bressat@gmail.com","subject":"[PATCH v2 4/4] error message now advice to use the new option","fromName":"Jonathan Bressat","fromEmail":"git.jonathan.bressat@gmail.com","sentAt":"2022-05-27T19:55:45Z","receivedAt":"2022-05-27T19:56:42Z","isPatch":true,"sender":{"key":"git.jonathan.bressat@gmail.com","avatar":null},"body":"When all the untracked files in the working tree have the same content\nthan the files in merged branch then the error message advice to use\nthe --overwrite-same-content option.\n\nSigned-off-by: Jonathan Bressat <git.jonathan.bressat@gmail.com>\nSigned-off-by: COGONI Guillaume <cogoni.guillaume@gmail.com>\n---\n builtin/checkout.c |  1 +\n unpack-trees.c     | 20 ++++++++++++++++----\n unpack-trees.h     |  1 +\n 3 files changed, 18 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex cc804ba8e1..1b1d1813c7 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -760,6 +760,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\t\t       &new_branch_info->commit->object.oid :\n \t\t\t\t       &new_branch_info->oid, NULL);\n \t\ttopts.preserve_ignored = !opts->overwrite_ignore;\n+\t\ttopts.overwrite_same_content = 0;/* FIXME: opts->overwrite_same_content */\n \t\ttree = parse_tree_indirect(old_branch_info->commit ?\n \t\t\t\t\t   &old_branch_info->commit->object.oid :\n \t\t\t\t\t   the_hash_algo->empty_tree);\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 1a52be723e..6c660b084b 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -158,17 +158,17 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,\n \tif (!strcmp(cmd, \"checkout\"))\n \t\tmsg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE)\n \t\t      ? _(\"The following untracked working tree files would be overwritten by checkout:\\n%%s\"\n-\t\t\t  \"Please move or remove them before you switch branches.\")\n+\t\t\t  \"Please move or remove them before you switch branches.%%s\")\n \t\t      : _(\"The following untracked working tree files would be overwritten by checkout:\\n%%s\");\n \telse if (!strcmp(cmd, \"merge\"))\n \t\tmsg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE)\n \t\t      ? _(\"The following untracked working tree files would be overwritten by merge:\\n%%s\"\n-\t\t\t  \"Please move or remove them before you merge.\")\n+\t\t\t  \"Please move or remove them before you merge.%%s\")\n \t\t      : _(\"The following untracked working tree files would be overwritten by merge:\\n%%s\");\n \telse\n \t\tmsg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE)\n \t\t      ? _(\"The following untracked working tree files would be overwritten by %s:\\n%%s\"\n-\t\t\t  \"Please move or remove them before you %s.\")\n+\t\t\t  \"Please move or remove them before you %s.%%s\")\n \t\t      : _(\"The following untracked working tree files would be overwritten by %s:\\n%%s\");\n \tmsgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] =\n \t\tstrvec_pushf(&opts->msgs_to_free, msg, cmd, cmd);\n@@ -251,6 +251,14 @@ static void display_error_msgs(struct unpack_trees_options *o)\n {\n \tint e;\n \tunsigned error_displayed = 0;\n+\tconst char *can_overwrite_msg;\n+\n+\tif (o->can_overwrite) {\n+\t\tcan_overwrite_msg = _(\"\\nYou can also rerun the command with --overwrite-same-content to overwrite files with same content.\");\n+\t} else {\n+\t\tcan_overwrite_msg = \"\";\n+\t}\n+\n \tfor (e = 0; e < NB_UNPACK_TREES_ERROR_TYPES; e++) {\n \t\tstruct string_list *rejects = &o->unpack_rejects[e];\n \n@@ -261,7 +269,8 @@ static void display_error_msgs(struct unpack_trees_options *o)\n \t\t\terror_displayed = 1;\n \t\t\tfor (i = 0; i < rejects->nr; i++)\n \t\t\t\tstrbuf_addf(&path, \"\\t%s\\n\", rejects->items[i].string);\n-\t\t\terror(ERRORMSG(o, e), super_prefixed(path.buf));\n+\n+\t\t\terror(ERRORMSG(o, e), super_prefixed(path.buf), can_overwrite_msg);\n \t\t\tstrbuf_release(&path);\n \t\t}\n \t\tstring_list_clear(rejects, 0);\n@@ -1711,6 +1720,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n \tstruct pattern_list pl;\n \tint free_pattern_list = 0;\n \tstruct dir_struct dir = DIR_INIT;\n+\to->can_overwrite = 1;\n \n \tif (o->reset == UNPACK_RESET_INVALID)\n \t\tBUG(\"o->reset had a value of 1; should be UNPACK_TREES_*_UNTRACKED\");\n@@ -2261,6 +2271,8 @@ static int check_ok_to_remove(const char *name, int len, int dtype,\n \t\tif(o->overwrite_same_content) {\n \t\t\treturn 0;\n \t\t}\n+\t} else {\n+\t\to->can_overwrite = 0;\n \t}\n \n \treturn add_rejected_path(o, error_type, name);\ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex ebe4be0b35..2be74ce5bf 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -72,6 +72,7 @@ struct unpack_trees_options {\n \t\t     exiting_early,\n \t\t     show_all_errors,\n \t\t     overwrite_same_content,\n+\t\t     can_overwrite,\n \t\t     dry_run;\n \tenum unpack_trees_reset_type reset;\n \tconst char *prefix;\n-- \n2.35.1.10.g88248585b1.dirty\n\n"},{"id":"456656","messageId":"88e41596-d369-958f-f75c-c0b2cd86c2b5@univ-lyon1.fr","threadId":"57624","inReplyTo":"82beb916d9c44a069f30ec4ff261e3be@SAMBXP02.univ-lyon1.fr","subject":"Re: [PATCH v2 4/4] error message now advice to use the new option","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@univ-lyon1.fr","sentAt":"2022-06-04T09:45:13Z","receivedAt":"2022-06-04T09:46:48Z","isPatch":true,"sender":{"key":"matthieu.moy@univ-lyon1.fr","avatar":"https://gravatar.com/avatar/8ab83b763226bd297b59ddd1463a8bfc852924577191233f73a953b86888fe0c?d=mp&s=160"},"body":"On 5/27/22 21:55, Jonathan Bressat wrote:\n > Subject: Re: [PATCH v2 4/4] error message now advice to use the new \noption\n\nCommit messages are usually written with imperative tone. The \"now\" \ndoesn't add information, the reader can guess that the commit message \ndescribes the new behavior.\n\n\"suggest --overwrite-same-content in error message when appropriate\" ?\n\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -760,6 +760,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n>   \t\t\t\t       &new_branch_info->commit->object.oid :\n>   \t\t\t\t       &new_branch_info->oid, NULL);\n>   \t\ttopts.preserve_ignored = !opts->overwrite_ignore;\n> +\t\ttopts.overwrite_same_content = 0;/* FIXME: opts->overwrite_same_content */\n\nWhy not use opts->overwrite_same_content in the code rather than saying \nyou should in a comment?\n\nActually, doesn't this hunk belong to the previous commit?\n\nThe rest of the patch looks good to me.\n\n-- \nMatthieu Moy\nhttps://matthieu-moy.fr/\n"},{"id":"456657","messageId":"2c6ee145-5116-25db-9fbf-bcffe67a9360@univ-lyon1.fr","threadId":"57624","inReplyTo":"dfea1d98c15047428b1a11adbc002eef@SAMBXP02.univ-lyon1.fr","subject":"Re: [PATCH v2 1/4] t6436: tests how merge behave when there is untracked file with the same content","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@univ-lyon1.fr","sentAt":"2022-06-04T09:44:56Z","receivedAt":"2022-06-04T09:46:49Z","isPatch":true,"sender":{"key":"matthieu.moy@univ-lyon1.fr","avatar":"https://gravatar.com/avatar/8ab83b763226bd297b59ddd1463a8bfc852924577191233f73a953b86888fe0c?d=mp&s=160"},"body":"On 5/27/22 21:55, Jonathan Bressat wrote:\n> add test to show explicitly that merge doesn't overwrite untracked files\n> or unstaged even when they have the same content than files int the\n> merged commit\n\nNit: capital at the beginning of the sentence, period at the end.\n\n\"untracked files or unstaged\" -> \"untracked or unstaged files\"\n\n> +test_expect_success 'create branch A' '\n> +\tgit reset --hard c0 &&\n> +\tgit checkout -b A\n> +'\n> +\n> +test_expect_success 'fastforward will not overwrite untracked file with the same content' '\n\nGit usually spells fast-forward with a hyphen, not fastforward.\n\n> +\ttest_when_finished \"git branch -D B && git reset --hard c0 && git clean --force\" &&\n> +\tgit checkout -b B &&\n> +\ttest_commit --no-tag \"tracked\" file \"content\" &&\n> +\tgit checkout A &&\n> +\techo content >file &&\n> +\ttest_must_fail git merge B\n\nOther tests in the same file test a bit more: the file mustn't be \ntouched. It's a very important thing with Git: 99% of the times, when an \noperation fails, it fails before starting any change on-disk, as opposed \nto \"I started messing up with your repo, I can't go further, go fix the \nmess yourself\" ;-).\n\nThe way it's done is by creating a file with the content, using \"cp\" \ninstead of \"echo >\" and \"test_cmp\" to check the content.\n\nOther tests also check the absence of .git/MERGE_HEAD, which seems to be \na sensible thing to do.\n\n> +test_expect_success 'will not overwrite untracked file with the same content' '\n> +\ttest_when_finished \"git branch -D B && git reset --hard c0 && git clean --force\" &&\n> +\tgit checkout -b B &&\n> +\ttest_commit --no-tag \"tracked\" file \"content\" fileB \"content\" &&\n> +\tgit checkout A &&\n> +\ttest_commit --no-tag \"exA\" fileA \"content\" &&\n> +\techo content >file &&\n> +\ttest_must_fail git merge B\n> +'\n> +\n> +test_expect_success 'will not overwrite unstaged file with the same content' '\n> +\ttest_when_finished \"git branch -D B && git reset --hard c0 && git clean --force\" &&\n> +\ttest_commit --no-tag \"unstaged\" file \"other\" &&\n> +\tgit checkout -b B &&\n> +\ttest_commit --no-tag \"staged\" file \"content\" &&\n> +\tgit checkout A &&\n> +\techo content >file &&\n> +\ttest_must_fail git merge B\n> +'\n\nAs discussed IRL, I think two more cases should be tested:\n\n- index matches commit being merged, but the worktree file doesn't\n- worktree file doesn't match content, but index does\n\nin both cases, I'd expect the old and the new behavior to abort the \nmerge. Perhaps there are use-cases where one would expect a successful \nmerge silently, but for rare corner-cases, it's safe to ask the user to \nfix the situation manually and too much magic can only confuse the user.\n\n-- \nMatthieu Moy\nhttps://matthieu-moy.fr/\n"},{"id":"456658","messageId":"4808601e-d05e-0bfc-177f-bfa46154fe22@univ-lyon1.fr","threadId":"57624","inReplyTo":"be2297bdcd724c3f8abfde2d5d74fb18@SAMBXP02.univ-lyon1.fr","subject":"Re: [PATCH v2 2/4] merge with untracked file that are the same without failure","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@univ-lyon1.fr","sentAt":"2022-06-04T09:45:04Z","receivedAt":"2022-06-04T09:46:49Z","isPatch":true,"sender":{"key":"matthieu.moy@univ-lyon1.fr","avatar":"https://gravatar.com/avatar/8ab83b763226bd297b59ddd1463a8bfc852924577191233f73a953b86888fe0c?d=mp&s=160"},"body":"On 5/27/22 21:55, Jonathan Bressat wrote:\n> Keep the old behavior as default.\n> \n> Add the option --overwrite-same-content, when this option is used merge\n> will overwrite untracked file that have the same content.\n> \n> It make the merge nicer to the user, usefull for a simple utilisation,\n\nmake_s_\n\nusefull -> useful\n\nutilisation -> use\n\n> for exemple if you copy and paste files from another project and then\n\nex_a_mple.\n\n> you decide to pull this project, git will not proceed even if you didn't\n> modify those files.\n\nI'd avoid saying \"you\" in a commit message. \"the user\" seems clearer to me.\n\nAlso, don't use the future to talk about the behavior before the patch, \nit's really confusing. Actually, the commit message talks about the \nprevious behavior, but doesn't really document the new one.\n\n> +--overwrite-same-content::\n> +       Silently overwrite untracked files that have the same content\n> +       and name than files in the merged commit from the merge result.\n\nI don't understand what \"in the merged commit from the merge result\" means.\n\nPerhaps \"overwrite\" is not the best name. We actually re-use the file \nwithout touching it.\n\n> --- /dev/null\n> +++ b/t/t7615-merge-untracked.sh\n\nWhy a new file? These are minor variants of the ones you just added in \nthe previous commit, and would deserve being written next to them.\n\n> +test_expect_success 'fastforward overwrite untracked file that has the same content' '\n> +test_expect_success 'fastforward fail when untracked file has different content' '\n> +test_expect_success 'normal merge overwrite untracked file that has the same content' '\n> +test_expect_success 'normal merge fail when untracked file has different content' '\n> +test_expect_success 'merge fail when tracked file modification is unstaged' '\n\nWe're making a lot of tests, very similar to each other and very similar \nto other existing ones. I think we've reached the point where we need to \nrefactor a bit and write one generic function that covers\n\n- index state : same / different\n- worktree state : same / different\n- --overwrite-untracked : present / absent\n- kind of merge : fast-forward / real merge\n\nand then call this function with the appropriate set of parameters. \nEither the function can be called within tests (each test becoming a \none-liner), or perhaps the function can call test_expect_success and \nthen we can write stg like\n\nfor index in same different\ndo\n\tfor worktree in same different\n\tdo\n\t...\n\t\trun_test_merge $index $worktree ....\n\tdone\ndone\n\n> --- a/unpack-trees.c\n> +++ b/unpack-trees.c\n> @@ -2257,6 +2257,10 @@ static int check_ok_to_remove(const char *name, int len, int dtype,\n>   \tif (result) {\n>   \t\tif (result->ce_flags & CE_REMOVE)\n>   \t\t\treturn 0;\n> +\t} else if (ce && !ie_modified(o->src_index, ce, st, 0)) {\n> +\t\tif(o->overwrite_same_content) {\n> +\t\t\treturn 0;\n> +\t\t}\n\nThis looks good, but honestly I'm a bit lost between o->src_index, \no->dst_index and o.result, so the review of someone more familiar with \nthis part of the codebase would be welcome.\n\n> + * is not tracked, unless it is ignored or it has the same content\n> + * than the merged file with the option --overwrite_same_content.\n\n\"same content _as_\".\n\n-- \nMatthieu Moy\nhttps://matthieu-moy.fr/\n"},{"id":"456660","messageId":"253c9744-60b6-c571-4962-62d2d6808d69@univ-lyon1.fr","threadId":"57624","inReplyTo":"4efbe7d9c95841c691f51954670a1d9f@SAMBXP02.univ-lyon1.fr","subject":"Re: [PATCH v2 3/4] add configuration variable corresponding to --overwrite-same-content","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@univ-lyon1.fr","sentAt":"2022-06-04T09:49:23Z","receivedAt":"2022-06-04T09:49:51Z","isPatch":true,"sender":{"key":"matthieu.moy@univ-lyon1.fr","avatar":"https://gravatar.com/avatar/8ab83b763226bd297b59ddd1463a8bfc852924577191233f73a953b86888fe0c?d=mp&s=160"},"body":"On 5/27/22 21:55, Jonathan Bressat wrote:\n> Configuration variable merge.overwritesamecontent corresponding to\n\nWe usually write them as camelCase for readability (but options are \ncase-insensitive).\n\n> +merge.overwritesamecontent::\n> +    When set to true, it will modify the behavior of git merge\n\n\"When set to true, modify\", the future is not needed.\n\n> +    to overwrite untracked files that have the same name and\n> +    content than files in the merged commit.\t\n\nThe doc should mention the command-line option too, and say which one \ntakes precedence when both are specified (as it is the case for many \nother options).\n\n-- \nMatthieu Moy\nhttps://matthieu-moy.fr/\n"},{"id":"457018","messageId":"CAA0Qn1vtPy8GhO-vON9YFPZoN0s2EwwLn3LUUNs1e3N1i5KyBA@mail.gmail.com","threadId":"57624","inReplyTo":"88e41596-d369-958f-f75c-c0b2cd86c2b5@univ-lyon1.fr","subject":"Re: [PATCH v2 4/4] error message now advice to use the new option","fromName":"Guillaume Cogoni","fromEmail":"cogoni.guillaume@gmail.com","sentAt":"2022-06-10T12:58:41Z","receivedAt":"2022-06-10T12:58:54Z","isPatch":true,"sender":{"key":"cogoni.guillaume@gmail.com","avatar":"https://avatars.githubusercontent.com/u/60919643?v=4"},"body":"Hello,\n\nThanks for your reviews.\nWe will take in consideration what you say for the next version.\n\nSincerely,\nGuillaume COGONI and Jonathan BRESSAT\n\nOn Sat, Jun 4, 2022 at 4:57 PM Matthieu Moy <Matthieu.Moy@univ-lyon1.fr> wrote:\n>\n> On 5/27/22 21:55, Jonathan Bressat wrote:\n>  > Subject: Re: [PATCH v2 4/4] error message now advice to use the new\n> option\n>\n> Commit messages are usually written with imperative tone. The \"now\"\n> doesn't add information, the reader can guess that the commit message\n> describes the new behavior.\n>\n> \"suggest --overwrite-same-content in error message when appropriate\" ?\n>\n> > --- a/builtin/checkout.c\n> > +++ b/builtin/checkout.c\n> > @@ -760,6 +760,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n> >                                      &new_branch_info->commit->object.oid :\n> >                                      &new_branch_info->oid, NULL);\n> >               topts.preserve_ignored = !opts->overwrite_ignore;\n> > +             topts.overwrite_same_content = 0;/* FIXME: opts->overwrite_same_content */\n>\n> Why not use opts->overwrite_same_content in the code rather than saying\n> you should in a comment?\n>\n> Actually, doesn't this hunk belong to the previous commit?\n>\n> The rest of the patch looks good to me.\n>\n> --\n> Matthieu Moy\n> https://matthieu-moy.fr/\n"}]}