{"thread":{"id":"24269","subject":"[PATCH v6 0/3] Merge renormalization, config renamed","startedAt":"2010-07-02T19:20:46Z","lastAt":"2010-08-05T19:02:08Z","messageCount":35,"participants":["Eyvind Bernhardsen","Junio C Hamano","Jonathan Nieder","Ævar Arnfjörð Bjarmason","Bert Wesarg"],"isPatch":true,"patchVersion":6,"patchTotal":3},"messages":[{"id":"144705","messageId":"cover.1278093311.git.eyvind.bernhardsen@gmail.com","threadId":"24269","inReplyTo":null,"subject":"[PATCH v6 0/3] Merge renormalization, config renamed","fromName":"Eyvind Bernhardsen","fromEmail":"eyvind.bernhardsen@gmail.com","sentAt":"2010-07-02T19:20:46Z","receivedAt":"2010-07-02T19:20:46Z","isPatch":true,"sender":{"key":"eyvind.bernhardsen@gmail.com","avatar":"https://avatars.githubusercontent.com/u/106762?v=4"},"body":"Here's a new version of the merge normalization series that renames the\nconfiguration variable.  Since \"merge.renormalize\" got the best\nresponse, I went with that.\n\nJunio, I hope you don't mind that I squashed your patch to introduce the\nconfig variable into the first patch.  It seemed a bit out-of-place as a\nseparate commit.\n\n- Eyvind\n\nEyvind Bernhardsen (3):\n  Avoid conflicts when merging branches with mixed normalization\n  Try normalizing files to avoid delete/modify conflicts when merging\n  Don't expand CRLFs when normalizing text during merge\n\n Documentation/gitattributes.txt |   34 ++++++++++++++++++++\n Documentation/merge-config.txt  |   10 ++++++\n builtin/merge.c                 |    3 ++\n cache.h                         |    2 +\n convert.c                       |   37 +++++++++++++++++++----\n environment.c                   |    1 +\n ll-merge.c                      |   15 +++++++++\n merge-recursive.c               |   51 +++++++++++++++++++++++++++++-\n t/t6038-merge-text-auto.sh      |   64 +++++++++++++++++++++++++++++++++++++++\n 9 files changed, 209 insertions(+), 8 deletions(-)\n create mode 100755 t/t6038-merge-text-auto.sh\n\n-- \n1.7.1.575.g383de\n"},{"id":"144711","messageId":"42dab0b23c001f3981180cc9e0069c4dbb49ae4a.1278093311.git.eyvind.bernhardsen@gmail.com","threadId":"24269","inReplyTo":"cover.1278093311.git.eyvind.bernhardsen@gmail.com","subject":"[PATCH v6 1/3] Avoid conflicts when merging branches with mixed normalization","fromName":"Eyvind Bernhardsen","fromEmail":"eyvind.bernhardsen@gmail.com","sentAt":"2010-07-02T19:20:47Z","receivedAt":"2010-07-02T19:20:47Z","isPatch":true,"sender":{"key":"eyvind.bernhardsen@gmail.com","avatar":"https://avatars.githubusercontent.com/u/106762?v=4"},"body":"Currently, merging across changes in line ending normalization is\npainful since files containing CRLF will conflict with normalized files,\neven if the only difference between the two versions is the line\nendings.  Additionally, any \"real\" merge conflicts that exist are\nobscured because every line in the file has a conflict.\n\nAssume you start out with a repo that has a lot of text files with CRLF\nchecked in (A):\n\n      o---C\n     /     \\\n    A---B---D\n\nB: Add \"* text=auto\" to .gitattributes and normalize all files to\n   LF-only\n\nC: Modify some of the text files\n\nD: Try to merge C\n\nYou will get a ridiculous number of LF/CRLF conflicts when trying to\nmerge C into D, since the repository contents for C are \"wrong\" wrt the\nnew .gitattributes file.\n\nFix ll-merge so that the \"base\", \"theirs\" and \"ours\" stages are passed\nthrough convert_to_worktree() and convert_to_git() before a three-way\nmerge.  This ensures that all three stages are normalized in the same\nway, removing from consideration differences that are only due to\nnormalization.\n\nThis feature is optional for now since it changes a low-level mechanism\nand is not necessary for the majority of users.  The \"merge.renormalize\"\nconfig variable enables it.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com>\n---\n Documentation/gitattributes.txt |   34 ++++++++++++++++++++\n Documentation/merge-config.txt  |   10 ++++++\n builtin/merge.c                 |    3 ++\n cache.h                         |    2 +\n convert.c                       |   16 ++++++++-\n environment.c                   |    1 +\n ll-merge.c                      |   15 +++++++++\n t/t6038-merge-text-auto.sh      |   64 +++++++++++++++++++++++++++++++++++++++\n 8 files changed, 143 insertions(+), 2 deletions(-)\n create mode 100755 t/t6038-merge-text-auto.sh\n\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex 564586b..da553ff 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -317,6 +317,17 @@ command is \"cat\").\n \tsmudge = cat\n ------------------------\n \n+For best results, `clean` should not alter its output further if it is\n+run twice (\"clean->clean\" should be equivalent to \"clean\"), and\n+multiple `smudge` commands should not alter `clean`'s output\n+(\"smudge->smudge->clean\" should be equivalent to \"clean\").  See the\n+section on merging below.\n+\n+The \"indent\" filter is well-behaved in this regard: it will not modify\n+input that is already correctly indented.  In this case, the lack of a\n+smudge filter means that the clean filter _must_ accept its own output\n+without modifying it.\n+\n \n Interaction between checkin/checkout attributes\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n@@ -331,6 +342,29 @@ In the check-out codepath, the blob content is first converted\n with `text`, and then `ident` and fed to `filter`.\n \n \n+Merging branches with differing checkin/checkout attributes\n+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n+\n+If you have added attributes to a file that cause the canonical\n+repository format for that file to change, such as adding a\n+clean/smudge filter or text/eol/ident attributes, merging anything\n+where the attribute is not in place would normally cause merge\n+conflicts.\n+\n+To prevent these unnecessary merge conflicts, git can be told to run a\n+virtual check-out and check-in of all three stages of a file when\n+resolving a three-way merge by setting the `merge.renormalize`\n+configuration variable.  This prevents changes caused by check-in\n+conversion from causing spurious merge conflicts when a converted file\n+is merged with an unconverted file.\n+\n+As long as a \"smudge->clean\" results in the same output as a \"clean\"\n+even on files that are already smudged, this strategy will\n+automatically resolve all filter-related conflicts.  Filters that do\n+not act in this way may cause additional merge conflicts that must be\n+resolved manually.\n+\n+\n Generating diff text\n ~~~~~~~~~~~~~~~~~~~~\n \ndiff --git a/Documentation/merge-config.txt b/Documentation/merge-config.txt\nindex a403155..b72f533 100644\n--- a/Documentation/merge-config.txt\n+++ b/Documentation/merge-config.txt\n@@ -15,6 +15,16 @@ merge.renameLimit::\n \tduring a merge; if not specified, defaults to the value of\n \tdiff.renameLimit.\n \n+merge.renormalize::\n+\tTell git that canonical representation of files in the\n+\trepository has changed over time (e.g. earlier commits record\n+\ttext files with CRLF line endings, but recent ones use LF line\n+\tendings).  In such a repository, git can convert the data\n+\trecorded in commits to a canonical form before performing a\n+\tmerge to reduce unnecessary conflicts.  For more information,\n+\tsee section \"Merging branches with differing checkin/checkout\n+\tattributes\" in linkgit:gitattributes[5].\n+\n merge.stat::\n \tWhether to print the diffstat between ORIG_HEAD and the merge result\n \tat the end of the merge.  True by default.\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 37ce4f5..b836e9c 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -503,6 +503,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n \t\treturn git_config_string(&pull_octopus, k, v);\n \telse if (!strcmp(k, \"merge.log\") || !strcmp(k, \"merge.summary\"))\n \t\toption_log = git_config_bool(k, v);\n+\telse if (!strcmp(k, \"merge.renormalize\")) {\n+\t\tmerge_renormalize = git_config_bool(k, v);\n+\t}\n \treturn git_diff_ui_config(k, v, cb);\n }\n \ndiff --git a/cache.h b/cache.h\nindex c9fa3df..ed73da8 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -551,6 +551,7 @@ extern int read_replace_refs;\n extern int fsync_object_files;\n extern int core_preload_index;\n extern int core_apply_sparse_checkout;\n+extern int merge_renormalize;\n \n enum safe_crlf {\n \tSAFE_CRLF_FALSE = 0,\n@@ -1054,6 +1055,7 @@ extern void trace_argv_printf(const char **argv, const char *format, ...);\n extern int convert_to_git(const char *path, const char *src, size_t len,\n                           struct strbuf *dst, enum safe_crlf checksafe);\n extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);\n+extern int renormalize_buffer(const char *path, const char *src, size_t len, struct strbuf *dst);\n \n /* add */\n /*\ndiff --git a/convert.c b/convert.c\nindex e41a31e..0203be8 100644\n--- a/convert.c\n+++ b/convert.c\n@@ -93,7 +93,8 @@ static int is_binary(unsigned long size, struct text_stat *stats)\n \treturn 0;\n }\n \n-static enum eol determine_output_conversion(enum action action) {\n+static enum eol determine_output_conversion(enum action action)\n+{\n \tswitch (action) {\n \tcase CRLF_BINARY:\n \t\treturn EOL_UNSET;\n@@ -693,7 +694,8 @@ static int git_path_check_ident(const char *path, struct git_attr_check *check)\n \treturn !!ATTR_TRUE(value);\n }\n \n-enum action determine_action(enum action text_attr, enum eol eol_attr) {\n+static enum action determine_action(enum action text_attr, enum eol eol_attr)\n+{\n \tif (text_attr == CRLF_BINARY)\n \t\treturn CRLF_BINARY;\n \tif (eol_attr == EOL_LF)\n@@ -773,3 +775,13 @@ int convert_to_working_tree(const char *path, const char *src, size_t len, struc\n \t}\n \treturn ret | apply_filter(path, src, len, dst, filter);\n }\n+\n+int renormalize_buffer(const char *path, const char *src, size_t len, struct strbuf *dst)\n+{\n+\tint ret = convert_to_working_tree(path, src, len, dst);\n+\tif (ret) {\n+\t\tsrc = dst->buf;\n+\t\tlen = dst->len;\n+\t}\n+\treturn ret | convert_to_git(path, src, len, dst, 0);\n+}\ndiff --git a/environment.c b/environment.c\nindex 83d38d3..81a3682 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -53,6 +53,7 @@ enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;\n char *notes_ref_name;\n int grafts_replace_parents = 1;\n int core_apply_sparse_checkout;\n+int merge_renormalize;\n \n /* Parallel index stat data preload? */\n int core_preload_index = 0;\ndiff --git a/ll-merge.c b/ll-merge.c\nindex 3764a1a..5068fe0 100644\n--- a/ll-merge.c\n+++ b/ll-merge.c\n@@ -321,6 +321,16 @@ static int git_path_check_merge(const char *path, struct git_attr_check check[2]\n \treturn git_checkattr(path, 2, check);\n }\n \n+static void normalize_file(mmfile_t *mm, const char *path)\n+{\n+\tstruct strbuf strbuf = STRBUF_INIT;\n+\tif (renormalize_buffer(path, mm->ptr, mm->size, &strbuf)) {\n+\t\tfree(mm->ptr);\n+\t\tmm->size = strbuf.len;\n+\t\tmm->ptr = strbuf_detach(&strbuf, NULL);\n+\t}\n+}\n+\n int ll_merge(mmbuffer_t *result_buf,\n \t     const char *path,\n \t     mmfile_t *ancestor, const char *ancestor_label,\n@@ -334,6 +344,11 @@ int ll_merge(mmbuffer_t *result_buf,\n \tconst struct ll_merge_driver *driver;\n \tint virtual_ancestor = flag & 01;\n \n+\tif (merge_renormalize) {\n+\t\tnormalize_file(ancestor, path);\n+\t\tnormalize_file(ours, path);\n+\t\tnormalize_file(theirs, path);\n+\t}\n \tif (!git_path_check_merge(path, check)) {\n \t\tll_driver_name = check[0].value;\n \t\tif (check[1].value) {\ndiff --git a/t/t6038-merge-text-auto.sh b/t/t6038-merge-text-auto.sh\nnew file mode 100755\nindex 0000000..127baf8\n--- /dev/null\n+++ b/t/t6038-merge-text-auto.sh\n@@ -0,0 +1,64 @@\n+#!/bin/sh\n+\n+test_description='CRLF merge conflict across text=auto change'\n+\n+. ./test-lib.sh\n+\n+test_expect_success setup '\n+\tgit config merge.renormalize true &&\n+\tgit config core.autocrlf false &&\n+\techo first line | append_cr >file &&\n+\techo first line >control_file &&\n+\techo only line >inert_file &&\n+\tgit add file control_file inert_file &&\n+\tgit commit -m \"Initial\" &&\n+\tgit tag initial &&\n+\tgit branch side &&\n+\techo \"* text=auto\" >.gitattributes &&\n+\ttouch file &&\n+\tgit add .gitattributes file &&\n+\tgit commit -m \"normalize file\" &&\n+\techo same line | append_cr >>file &&\n+\techo same line >>control_file &&\n+\tgit add file control_file &&\n+\tgit commit -m \"add line from a\" &&\n+\tgit tag a &&\n+\tgit rm .gitattributes &&\n+\trm file &&\n+\tgit checkout file &&\n+\tgit commit -m \"remove .gitattributes\" &&\n+\tgit tag c &&\n+\tgit checkout side &&\n+\techo same line | append_cr >>file &&\n+\techo same line >>control_file &&\n+\tgit add file control_file &&\n+\tgit commit -m \"add line from b\" &&\n+\tgit tag b &&\n+\tgit checkout master\n+'\n+\n+test_expect_success 'Check merging after setting text=auto' '\n+\tgit reset --hard a &&\n+\tgit merge b &&\n+\tcat file | remove_cr >file.temp &&\n+\ttest_cmp file file.temp\n+'\n+\n+test_expect_success 'Check merging addition of text=auto' '\n+\tgit reset --hard b &&\n+\tgit merge a &&\n+\tcat file | remove_cr >file.temp &&\n+\ttest_cmp file file.temp\n+'\n+\n+test_expect_failure 'Test delete/normalize conflict' '\n+\tgit checkout side &&\n+\tgit reset --hard initial &&\n+\tgit rm file &&\n+\tgit commit -m \"remove file\" &&\n+\tgit checkout master &&\n+\tgit reset --hard a^ &&\n+\tgit merge side\n+'\n+\n+test_done\n-- \n1.7.1.575.g383de\n"},{"id":"144707","messageId":"0652be563a15e811d053d59532eff62ca5684203.1278093311.git.eyvind.bernhardsen@gmail.com","threadId":"24269","inReplyTo":"cover.1278093311.git.eyvind.bernhardsen@gmail.com","subject":"[PATCH v6 2/3] Try normalizing files to avoid delete/modify conflicts when merging","fromName":"Eyvind Bernhardsen","fromEmail":"eyvind.bernhardsen@gmail.com","sentAt":"2010-07-02T19:20:48Z","receivedAt":"2010-07-02T19:20:48Z","isPatch":true,"sender":{"key":"eyvind.bernhardsen@gmail.com","avatar":"https://avatars.githubusercontent.com/u/106762?v=4"},"body":"If a file is modified due to normalization on one branch, and deleted on\nanother, a merge of the two branches will result in a delete/modify\nconflict for that file even if it is otherwise unchanged.\n\nTry to avoid the conflict by normalizing and comparing the \"base\" file\nand the modified file when their sha1s differ.  If they compare equal,\nthe file is considered unmodified and is deleted.\n\nSigned-off-by: Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n merge-recursive.c          |   51 ++++++++++++++++++++++++++++++++++++++++++-\n t/t6038-merge-text-auto.sh |    2 +-\n 2 files changed, 50 insertions(+), 3 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 856e98c..593c0b1 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1056,6 +1056,53 @@ static unsigned char *stage_sha(const unsigned char *sha, unsigned mode)\n \treturn (is_null_sha1(sha) || mode == 0) ? NULL: (unsigned char *)sha;\n }\n \n+static int read_sha1_strbuf(const unsigned char *sha1, struct strbuf *dst)\n+{\n+\tvoid *buf;\n+\tenum object_type type;\n+\tunsigned long size;\n+\tbuf = read_sha1_file(sha1, &type, &size);\n+\tif (!buf)\n+\t\treturn error(\"cannot read object %s\", sha1_to_hex(sha1));\n+\tif (type != OBJ_BLOB) {\n+\t\tfree(buf);\n+\t\treturn error(\"object %s is not a blob\", sha1_to_hex(sha1));\n+\t}\n+\tstrbuf_attach(dst, buf, size, size + 1);\n+\treturn 0;\n+}\n+\n+static int blob_unchanged(const unsigned char *o_sha,\n+\t\t\t  const unsigned char *a_sha,\n+\t\t\t  const char *path)\n+{\n+\tstruct strbuf o = STRBUF_INIT;\n+\tstruct strbuf a = STRBUF_INIT;\n+\tint ret = 0; /* assume changed for safety */\n+\n+\tif (sha_eq(o_sha, a_sha))\n+\t\treturn 1;\n+\tif (!merge_renormalize)\n+\t\treturn 0;\n+\n+\tassert(o_sha && a_sha);\n+\tif (read_sha1_strbuf(o_sha, &o) || read_sha1_strbuf(a_sha, &a))\n+\t\tgoto error_return;\n+\t/*\n+\t * Note: binary | is used so that both renormalizations are\n+\t * performed.  Comparison can be skipped if both files are\n+\t * unchanged since their sha1s have already been compared.\n+\t */\n+\tif (renormalize_buffer(path, o.buf, o.len, &o) |\n+\t    renormalize_buffer(path, a.buf, o.len, &a))\n+\t\tret = (o.len == a.len && !memcmp(o.buf, a.buf, o.len));\n+\n+error_return:\n+\tstrbuf_release(&o);\n+\tstrbuf_release(&a);\n+\treturn ret;\n+}\n+\n /* Per entry merge function */\n static int process_entry(struct merge_options *o,\n \t\t\t const char *path, struct stage_data *entry)\n@@ -1075,8 +1122,8 @@ static int process_entry(struct merge_options *o,\n \tif (o_sha && (!a_sha || !b_sha)) {\n \t\t/* Case A: Deleted in one */\n \t\tif ((!a_sha && !b_sha) ||\n-\t\t    (sha_eq(a_sha, o_sha) && !b_sha) ||\n-\t\t    (!a_sha && sha_eq(b_sha, o_sha))) {\n+\t\t    (!b_sha && blob_unchanged(o_sha, a_sha, path)) ||\n+\t\t    (!a_sha && blob_unchanged(o_sha, b_sha, path))) {\n \t\t\t/* Deleted in both or deleted in one and\n \t\t\t * unchanged in the other */\n \t\t\tif (a_sha)\ndiff --git a/t/t6038-merge-text-auto.sh b/t/t6038-merge-text-auto.sh\nindex 127baf8..d1ab86e 100755\n--- a/t/t6038-merge-text-auto.sh\n+++ b/t/t6038-merge-text-auto.sh\n@@ -51,7 +51,7 @@ test_expect_success 'Check merging addition of text=auto' '\n \ttest_cmp file file.temp\n '\n \n-test_expect_failure 'Test delete/normalize conflict' '\n+test_expect_success 'Test delete/normalize conflict' '\n \tgit checkout side &&\n \tgit reset --hard initial &&\n \tgit rm file &&\n-- \n1.7.1.575.g383de\n"},{"id":"144706","messageId":"6a8530fa3cba7cbc14882a9f5979d13452ab52b5.1278093311.git.eyvind.bernhardsen@gmail.com","threadId":"24269","inReplyTo":"cover.1278093311.git.eyvind.bernhardsen@gmail.com","subject":"[PATCH v6 3/3] Don't expand CRLFs when normalizing text during merge","fromName":"Eyvind Bernhardsen","fromEmail":"eyvind.bernhardsen@gmail.com","sentAt":"2010-07-02T19:20:49Z","receivedAt":"2010-07-02T19:20:49Z","isPatch":true,"sender":{"key":"eyvind.bernhardsen@gmail.com","avatar":"https://avatars.githubusercontent.com/u/106762?v=4"},"body":"Disable CRLF expansion when convert_to_working_tree() is called from\nnormalize_buffer().  This improves performance when merging branches\nwith conflicting line endings when core.eol=crlf or core.autocrlf=true\nby making the normalization act as if core.eol=lf.\n\nSigned-off-by: Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n convert.c |   27 ++++++++++++++++++++-------\n 1 files changed, 20 insertions(+), 7 deletions(-)\n\ndiff --git a/convert.c b/convert.c\nindex 0203be8..01de9a8 100644\n--- a/convert.c\n+++ b/convert.c\n@@ -741,7 +741,9 @@ int convert_to_git(const char *path, const char *src, size_t len,\n \treturn ret | ident_to_git(path, src, len, dst, ident);\n }\n \n-int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst)\n+static int convert_to_working_tree_internal(const char *path, const char *src,\n+\t\t\t\t\t    size_t len, struct strbuf *dst,\n+\t\t\t\t\t    int normalizing)\n {\n \tstruct git_attr_check check[5];\n \tenum action action = CRLF_GUESS;\n@@ -767,18 +769,29 @@ int convert_to_working_tree(const char *path, const char *src, size_t len, struc\n \t\tsrc = dst->buf;\n \t\tlen = dst->len;\n \t}\n-\taction = determine_action(action, eol_attr);\n-\tret |= crlf_to_worktree(path, src, len, dst, action);\n-\tif (ret) {\n-\t\tsrc = dst->buf;\n-\t\tlen = dst->len;\n+\t/*\n+\t * CRLF conversion can be skipped if normalizing, unless there\n+\t * is a smudge filter.  The filter might expect CRLFs.\n+\t */\n+\tif (filter || !normalizing) {\n+\t\taction = determine_action(action, eol_attr);\n+\t\tret |= crlf_to_worktree(path, src, len, dst, action);\n+\t\tif (ret) {\n+\t\t\tsrc = dst->buf;\n+\t\t\tlen = dst->len;\n+\t\t}\n \t}\n \treturn ret | apply_filter(path, src, len, dst, filter);\n }\n \n+int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst)\n+{\n+\treturn convert_to_working_tree_internal(path, src, len, dst, 0);\n+}\n+\n int renormalize_buffer(const char *path, const char *src, size_t len, struct strbuf *dst)\n {\n-\tint ret = convert_to_working_tree(path, src, len, dst);\n+\tint ret = convert_to_working_tree_internal(path, src, len, dst, 1);\n \tif (ret) {\n \t\tsrc = dst->buf;\n \t\tlen = dst->len;\n-- \n1.7.1.575.g383de\n"},{"id":"144727","messageId":"7vk4pdzema.fsf@alter.siamese.dyndns.org","threadId":"24269","inReplyTo":"cover.1278093311.git.eyvind.bernhardsen@gmail.com","subject":"Re: [PATCH v6 0/3] Merge renormalization, config renamed","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-07-02T22:46:21Z","receivedAt":"2010-07-02T22:46:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com> writes:\n\n> Here's a new version of the merge normalization series that renames the\n> configuration variable.  Since \"merge.renormalize\" got the best\n> response, I went with that.\n>\n> Junio, I hope you don't mind that I squashed your patch to introduce the\n> config variable into the first patch.  It seemed a bit out-of-place as a\n> separate commit.\n\nIt was meant to be squashed into the first one.  Thanks.\n"},{"id":"147087","messageId":"20100804031935.GA19699@burratino","threadId":"24269","inReplyTo":"cover.1278093311.git.eyvind.bernhardsen@gmail.com","subject":"[PATCH/RFC eb/double-convert-before-merge 0/6] merge -Xrenormalize","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-08-04T03:19:35Z","receivedAt":"2010-08-04T03:19:35Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi Eyvind,\n\nEyvind Bernhardsen wrote:\n\n> Here's a new version of the merge normalization series that renames the\n> configuration variable.  Since \"merge.renormalize\" got the best\n> response, I went with that.\n\nI have no idea how the following branch happened in my local git tree,\nbut it’s here now so maybe it’s worth something.  Somehow I got the\nidea that the merge_renormalize variable was a nice short-term\nprotection but long-term scary, and so a few patches emerged to\nbanish it.\n\nPlease let me emphasize that in their current form I do not think\nthese patches should be very usable.\n\nBut for later, they tell a nice story.  The idea is that the\n\"merge.renormalize\" is not really about the behavior of the \"git\nmerge\" command but about the merge-recursive driver; and so it\nshould be usable everywhere else that driver is used, too.\n\nSo in an ideal world, you would be able to, e.g.:\n\n\tgit checkout -m -Xrenormalize otherbranch\n\nor\n\n\tgit revert -Xrenormalize otherpatch\n\nor\n\n\tgit pull --rebase -Xrenormalize\n\nOf course, the plumbing is not all there yet; many commands that\nuse merge drivers cannot pass through arbitrary options through\nyet.\n\nWell, enough talking.  Here are the patches; what do you think?\n\nPatch 1 teaches blob_unchanged() to stop paying attention to the\nmerge_recursive variable.  It uses a parameter instead.  So from then\non, it can watch, bemused, a neutral party.\n\nPatch 2 adds and respects a renormalize option in the merge_options\nstruct.  It defaults to the value of the merge_recursive global to\nsave callers (in particular, \"git merge\") the pain of adjusting.\n\nPatch 3 lets ll_merge merge callers decide whether to renormalize,\ntoo.  Most callers never renormalize, since they are not \"git merge\"\n(and this is noted in new comments).\n\nPatch 4 is a sort of a digression.  It makes \"git rerere\" produce nice\nhelp output with the \"git rerere -h\" option.\n\nPatch 5 lets rerere callers decide whether to renormalize, too.  Most\ncallers never renormalize.  But looking at that patch reveals a\nserious problem: there are all kinds of other merge options (e.g.,\n-Xsubtree) that rerere callers are unable to control.\n\nFollowing the principle that scripted users should have about as\nmuch power as internal ones, patch 5 reluctantly exposes the\nrenormalize option to rerere as a new --renormalize option.  It\nshould be -Xrenormalize instead.\n\nPatch 6 eliminates the merge_recursive global once and for all, by\nteaching merge_recursive callers to set the renormalize merge option\nappropriately (and removing the backward-compatibility default).\nCallers that are not \"git merge\" still never use renormalize.\n\nFollowing the principle that scripted users should have as much\npower as internal ones, it also exposes the nice interface of an\n-Xrenormalize option.  Some redundancy between builtin/merge and\nbuiltin/merge-recursive is noticed but left for another topic.\n\nJonathan Nieder (6):\n  merge-trees: push choice to renormalize away from low level\n  merge-trees: let caller decide whether to renormalize\n  ll-merge: let caller decide whether to renormalize\n  rerere: migrate to parse-options API\n  rerere: let caller decide whether to renormalize\n  merge-recursive: add -Xrenormalize option\n\n Documentation/merge-strategies.txt |    8 +++++\n builtin/checkout.c                 |   13 ++++++++-\n builtin/commit.c                   |    4 ++\n builtin/merge-recursive.c          |    2 +\n builtin/merge.c                    |   24 +++++++++++----\n builtin/rerere.c                   |   56 ++++++++++++++++++++---------------\n builtin/revert.c                   |   18 ++++++++++-\n cache.h                            |    1 -\n environment.c                      |    1 -\n ll-merge.c                         |    4 +-\n ll-merge.h                         |    2 +-\n merge-file.c                       |    2 +-\n merge-recursive.c                  |   11 ++++--\n merge-recursive.h                  |    1 +\n rerere.c                           |   20 ++++++++----\n rerere.h                           |    1 +\n 16 files changed, 117 insertions(+), 51 deletions(-)\n\n-- \n1.7.2.1.544.ga752d.dirty\n"},{"id":"147088","messageId":"20100804032043.GB19699@burratino","threadId":"24269","inReplyTo":"20100804031935.GA19699@burratino","subject":"[PATCH 1/6] merge-trees: push choice to renormalize away from low level","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-08-04T03:20:43Z","receivedAt":"2010-08-04T03:20:43Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"The merge machinery decides whether to resmudge and clean relevant\nentries based on the global merge_renormalize setting, which is set by\n\"git merge\" based on its configuration (and left alone by other\ncommands).\n\nA nicer interface would make that decision a parameter to merge_trees\nso callers would pass in a choice made on a call-by-call basis.\nStart by making blob_unchanged stop examining the merge_renormalize\nglobal.\n\nIn other words, this change is a trivial no-op, but it brings us\ncloser to something good.\n\nCc: Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n merge-recursive.c |    9 +++++----\n 1 files changed, 5 insertions(+), 4 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 5ad8fc9..2b55fc2 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1074,7 +1074,7 @@ static int read_sha1_strbuf(const unsigned char *sha1, struct strbuf *dst)\n \n static int blob_unchanged(const unsigned char *o_sha,\n \t\t\t  const unsigned char *a_sha,\n-\t\t\t  const char *path)\n+\t\t\t  int renormalize, const char *path)\n {\n \tstruct strbuf o = STRBUF_INIT;\n \tstruct strbuf a = STRBUF_INIT;\n@@ -1082,7 +1082,7 @@ static int blob_unchanged(const unsigned char *o_sha,\n \n \tif (sha_eq(o_sha, a_sha))\n \t\treturn 1;\n-\tif (!merge_renormalize)\n+\tif (!renormalize)\n \t\treturn 0;\n \n \tassert(o_sha && a_sha);\n@@ -1112,6 +1112,7 @@ static int process_entry(struct merge_options *o,\n \tprint_index_entry(\"\\tpath: \", entry);\n \t*/\n \tint clean_merge = 1;\n+\tint normalize = merge_renormalize;\n \tunsigned o_mode = entry->stages[1].mode;\n \tunsigned a_mode = entry->stages[2].mode;\n \tunsigned b_mode = entry->stages[3].mode;\n@@ -1122,8 +1123,8 @@ static int process_entry(struct merge_options *o,\n \tif (o_sha && (!a_sha || !b_sha)) {\n \t\t/* Case A: Deleted in one */\n \t\tif ((!a_sha && !b_sha) ||\n-\t\t    (!b_sha && blob_unchanged(o_sha, a_sha, path)) ||\n-\t\t    (!a_sha && blob_unchanged(o_sha, b_sha, path))) {\n+\t\t    (!b_sha && blob_unchanged(o_sha, a_sha, normalize, path)) ||\n+\t\t    (!a_sha && blob_unchanged(o_sha, b_sha, normalize, path))) {\n \t\t\t/* Deleted in both or deleted in one and\n \t\t\t * unchanged in the other */\n \t\t\tif (a_sha)\n-- \n1.7.2.1.544.ga752d.dirty\n"},{"id":"147089","messageId":"20100804032110.GC19699@burratino","threadId":"24269","inReplyTo":"20100804031935.GA19699@burratino","subject":"[PATCH 2/6] merge-trees: let caller decide whether to renormalize","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-08-04T03:21:10Z","receivedAt":"2010-08-04T03:21:10Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Add a \"renormalize\" option to struct merge_options so callers can\ndecide on a case-by-case basis whether the merge is likely to have\noverlapped with a change in smudge/clean rules.  The option defaults\nto the global merge_renormalize setting for now.\n\nNo change in behavior intended.\n\nCc: Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n merge-recursive.c |    3 ++-\n merge-recursive.h |    1 +\n 2 files changed, 3 insertions(+), 1 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 2b55fc2..8a49844 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1112,7 +1112,7 @@ static int process_entry(struct merge_options *o,\n \tprint_index_entry(\"\\tpath: \", entry);\n \t*/\n \tint clean_merge = 1;\n-\tint normalize = merge_renormalize;\n+\tint normalize = o->renormalize;\n \tunsigned o_mode = entry->stages[1].mode;\n \tunsigned a_mode = entry->stages[2].mode;\n \tunsigned b_mode = entry->stages[3].mode;\n@@ -1484,6 +1484,7 @@ void init_merge_options(struct merge_options *o)\n \to->buffer_output = 1;\n \to->diff_rename_limit = -1;\n \to->merge_rename_limit = -1;\n+\to->renormalize = merge_renormalize;\n \tgit_config(merge_recursive_config, o);\n \tif (getenv(\"GIT_MERGE_VERBOSITY\"))\n \t\to->verbosity =\ndiff --git a/merge-recursive.h b/merge-recursive.h\nindex b831293..cdb97e9 100644\n--- a/merge-recursive.h\n+++ b/merge-recursive.h\n@@ -17,6 +17,7 @@ struct merge_options {\n \tint verbosity;\n \tint diff_rename_limit;\n \tint merge_rename_limit;\n+\tint renormalize;\n \tint call_depth;\n \tstruct strbuf obuf;\n \tstruct string_list current_file_set;\n-- \n1.7.2.1.544.ga752d.dirty\n"},{"id":"147090","messageId":"20100804032144.GD19699@burratino","threadId":"24269","inReplyTo":"20100804031935.GA19699@burratino","subject":"[PATCH 3/6] ll-merge: let caller decide whether to renormalize","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-08-04T03:21:44Z","receivedAt":"2010-08-04T03:21:44Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Add a \"renormalize\" parameter to ll_merge so callers can decide on a\ncase-by-case basis whether the merge is likely to have overlapped with\na change in smudge/clean rules.  This reveals a few commands that have\nnot been taking that situation into account, though it does not fix\nthem.\n\nNo change in behavior intended.\n\nCc: Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n builtin/checkout.c |    6 +++++-\n ll-merge.c         |    4 ++--\n ll-merge.h         |    2 +-\n merge-file.c       |    2 +-\n merge-recursive.c  |    1 +\n rerere.c           |   17 +++++++++++------\n 6 files changed, 21 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 1994be9..5a7ae03 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -150,8 +150,12 @@ static int checkout_merged(int pos, struct checkout *state)\n \tread_mmblob(&ours, active_cache[pos+1]->sha1);\n \tread_mmblob(&theirs, active_cache[pos+2]->sha1);\n \n+\t/*\n+\t * NEEDSWORK: re-create conflicts from merges with\n+\t * merge.renormalize set, too\n+\t */\n \tstatus = ll_merge(&result_buf, path, &ancestor, \"base\",\n-\t\t\t  &ours, \"ours\", &theirs, \"theirs\", 0);\n+\t\t\t  &ours, \"ours\", &theirs, \"theirs\", 0, 0);\n \tfree(ancestor.ptr);\n \tfree(ours.ptr);\n \tfree(theirs.ptr);\ndiff --git a/ll-merge.c b/ll-merge.c\nindex 5068fe0..0a0e04d 100644\n--- a/ll-merge.c\n+++ b/ll-merge.c\n@@ -336,7 +336,7 @@ int ll_merge(mmbuffer_t *result_buf,\n \t     mmfile_t *ancestor, const char *ancestor_label,\n \t     mmfile_t *ours, const char *our_label,\n \t     mmfile_t *theirs, const char *their_label,\n-\t     int flag)\n+\t     int renormalize, int flag)\n {\n \tstatic struct git_attr_check check[2];\n \tconst char *ll_driver_name = NULL;\n@@ -344,7 +344,7 @@ int ll_merge(mmbuffer_t *result_buf,\n \tconst struct ll_merge_driver *driver;\n \tint virtual_ancestor = flag & 01;\n \n-\tif (merge_renormalize) {\n+\tif (renormalize) {\n \t\tnormalize_file(ancestor, path);\n \t\tnormalize_file(ours, path);\n \t\tnormalize_file(theirs, path);\ndiff --git a/ll-merge.h b/ll-merge.h\nindex 57754cc..dd81a1e 100644\n--- a/ll-merge.h\n+++ b/ll-merge.h\n@@ -10,7 +10,7 @@ int ll_merge(mmbuffer_t *result_buf,\n \t     mmfile_t *ancestor, const char *ancestor_label,\n \t     mmfile_t *ours, const char *our_label,\n \t     mmfile_t *theirs, const char *their_label,\n-\t     int flag);\n+\t     int renormalize, int flag);\n \n int ll_merge_marker_size(const char *path);\n \ndiff --git a/merge-file.c b/merge-file.c\nindex db4d0d5..5afdc01 100644\n--- a/merge-file.c\n+++ b/merge-file.c\n@@ -37,7 +37,7 @@ static void *three_way_filemerge(const char *path, mmfile_t *base, mmfile_t *our\n \t * common ancestor.\n \t */\n \tmerge_status = ll_merge(&res, path, base, NULL,\n-\t\t\t\tour, \".our\", their, \".their\", 0);\n+\t\t\t\tour, \".our\", their, \".their\", 0, 0);\n \tif (merge_status < 0)\n \t\treturn NULL;\n \ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 8a49844..4838939 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -647,6 +647,7 @@ static int merge_3way(struct merge_options *o,\n \n \tmerge_status = ll_merge(result_buf, a->path, &orig, base_name,\n \t\t\t\t&src1, name1, &src2, name2,\n+\t\t\t\to->renormalize,\n \t\t\t\t(!!o->call_depth) | (favor << 1));\n \n \tfree(name1);\ndiff --git a/rerere.c b/rerere.c\nindex 2197890..17dcc3c 100644\n--- a/rerere.c\n+++ b/rerere.c\n@@ -319,9 +319,13 @@ static int handle_cache(const char *path, unsigned char *sha1, const char *outpu\n \t\tif (!mmfile[i].ptr && !mmfile[i].size)\n \t\t\tmmfile[i].ptr = xstrdup(\"\");\n \t}\n+\t/*\n+\t * NEEDSWORK: handle conflicts from merges with\n+\t * merge.renormalize set, too\n+\t */\n \tll_merge(&result, path, &mmfile[0], NULL,\n \t\t &mmfile[1], \"ours\",\n-\t\t &mmfile[2], \"theirs\", 0);\n+\t\t &mmfile[2], \"theirs\", 0, 0);\n \tfor (i = 0; i < 3; i++)\n \t\tfree(mmfile[i].ptr);\n \n@@ -361,7 +365,7 @@ static int find_conflict(struct string_list *conflict)\n \treturn 0;\n }\n \n-static int merge(const char *name, const char *path)\n+static int merge(const char *name, int renormalize, const char *path)\n {\n \tint ret;\n \tmmfile_t cur = {NULL, 0}, base = {NULL, 0}, other = {NULL, 0};\n@@ -376,7 +380,8 @@ static int merge(const char *name, const char *path)\n \t\tret = 1;\n \t\tgoto out;\n \t}\n-\tret = ll_merge(&result, path, &base, NULL, &cur, \"\", &other, \"\", 0);\n+\tret = ll_merge(&result, path, &base, NULL,\n+\t\t\t&cur, \"\", &other, \"\", renormalize, 0);\n \tif (!ret) {\n \t\tFILE *f = fopen(path, \"w\");\n \t\tif (!f)\n@@ -424,7 +429,7 @@ static int update_paths(struct string_list *update)\n \treturn status;\n }\n \n-static int do_plain_rerere(struct string_list *rr, int fd)\n+static int do_plain_rerere(struct string_list *rr, int fd, int renormalize)\n {\n \tstruct string_list conflict = { NULL, 0, 0, 1 };\n \tstruct string_list update = { NULL, 0, 0, 1 };\n@@ -469,7 +474,7 @@ static int do_plain_rerere(struct string_list *rr, int fd)\n \t\tconst char *name = (const char *)rr->items[i].util;\n \n \t\tif (has_rerere_resolution(name)) {\n-\t\t\tif (!merge(name, path)) {\n+\t\t\tif (!merge(name, renormalize, path)) {\n \t\t\t\tif (rerere_autoupdate)\n \t\t\t\t\tstring_list_insert(path, &update);\n \t\t\t\tfprintf(stderr,\n@@ -553,7 +558,7 @@ int rerere(int flags)\n \tfd = setup_rerere(&merge_rr, flags);\n \tif (fd < 0)\n \t\treturn 0;\n-\treturn do_plain_rerere(&merge_rr, fd);\n+\treturn do_plain_rerere(&merge_rr, fd, merge_renormalize);\n }\n \n static int rerere_forget_one_path(const char *path, struct string_list *rr)\n-- \n1.7.2.1.544.ga752d.dirty\n"},{"id":"147091","messageId":"20100804032216.GE19699@burratino","threadId":"24269","inReplyTo":"20100804031935.GA19699@burratino","subject":"[PATCH 4/6] rerere: migrate to parse-options API","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-08-04T03:22:16Z","receivedAt":"2010-08-04T03:22:16Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n builtin/rerere.c |   52 ++++++++++++++++++++++++++++------------------------\n 1 files changed, 28 insertions(+), 24 deletions(-)\n\ndiff --git a/builtin/rerere.c b/builtin/rerere.c\nindex 0048f9e..295fe75 100644\n--- a/builtin/rerere.c\n+++ b/builtin/rerere.c\n@@ -1,13 +1,16 @@\n #include \"builtin.h\"\n #include \"cache.h\"\n #include \"dir.h\"\n+#include \"parse-options.h\"\n #include \"string-list.h\"\n #include \"rerere.h\"\n #include \"xdiff/xdiff.h\"\n #include \"xdiff-interface.h\"\n \n-static const char git_rerere_usage[] =\n-\"git rerere [clear | status | diff | gc]\";\n+static const char * const rerere_usage[] = {\n+\t\"git rerere [clear | status | diff | gc]\",\n+\tNULL,\n+};\n \n /* these values are days */\n static int cutoff_noresolve = 15;\n@@ -103,25 +106,26 @@ static int diff_two(const char *file1, const char *label1,\n int cmd_rerere(int argc, const char **argv, const char *prefix)\n {\n \tstruct string_list merge_rr = { NULL, 0, 0, 1 };\n-\tint i, fd, flags = 0;\n-\n-\tif (2 < argc) {\n-\t\tif (!strcmp(argv[1], \"-h\"))\n-\t\t\tusage(git_rerere_usage);\n-\t\tif (!strcmp(argv[1], \"--rerere-autoupdate\"))\n-\t\t\tflags = RERERE_AUTOUPDATE;\n-\t\telse if (!strcmp(argv[1], \"--no-rerere-autoupdate\"))\n-\t\t\tflags = RERERE_NOAUTOUPDATE;\n-\t\tif (flags) {\n-\t\t\targc--;\n-\t\t\targv++;\n-\t\t}\n-\t}\n-\tif (argc < 2)\n+\tint i, fd, autoupdate = -1, flags = 0;\n+\n+\tstruct option options[] = {\n+\t\tOPT_SET_INT(0, \"rerere-autoupdate\", &autoupdate,\n+\t\t\t\"register clean resolutions in index\", 1),\n+\t\tOPT_END(),\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, rerere_usage, 0);\n+\n+\tif (autoupdate == 1)\n+\t\tflags = RERERE_AUTOUPDATE;\n+\tif (autoupdate == 0)\n+\t\tflags = RERERE_NOAUTOUPDATE;\n+\n+\tif (argc < 1)\n \t\treturn rerere(flags);\n \n-\tif (!strcmp(argv[1], \"forget\")) {\n-\t\tconst char **pathspec = get_pathspec(prefix, argv + 2);\n+\tif (!strcmp(argv[0], \"forget\")) {\n+\t\tconst char **pathspec = get_pathspec(prefix, argv + 1);\n \t\treturn rerere_forget(pathspec);\n \t}\n \n@@ -129,26 +133,26 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)\n \tif (fd < 0)\n \t\treturn 0;\n \n-\tif (!strcmp(argv[1], \"clear\")) {\n+\tif (!strcmp(argv[0], \"clear\")) {\n \t\tfor (i = 0; i < merge_rr.nr; i++) {\n \t\t\tconst char *name = (const char *)merge_rr.items[i].util;\n \t\t\tif (!has_rerere_resolution(name))\n \t\t\t\tunlink_rr_item(name);\n \t\t}\n \t\tunlink_or_warn(git_path(\"rr-cache/MERGE_RR\"));\n-\t} else if (!strcmp(argv[1], \"gc\"))\n+\t} else if (!strcmp(argv[0], \"gc\"))\n \t\tgarbage_collect(&merge_rr);\n-\telse if (!strcmp(argv[1], \"status\"))\n+\telse if (!strcmp(argv[0], \"status\"))\n \t\tfor (i = 0; i < merge_rr.nr; i++)\n \t\t\tprintf(\"%s\\n\", merge_rr.items[i].string);\n-\telse if (!strcmp(argv[1], \"diff\"))\n+\telse if (!strcmp(argv[0], \"diff\"))\n \t\tfor (i = 0; i < merge_rr.nr; i++) {\n \t\t\tconst char *path = merge_rr.items[i].string;\n \t\t\tconst char *name = (const char *)merge_rr.items[i].util;\n \t\t\tdiff_two(rerere_path(name, \"preimage\"), path, path, path);\n \t\t}\n \telse\n-\t\tusage(git_rerere_usage);\n+\t\tusage_with_options(rerere_usage, options);\n \n \tstring_list_clear(&merge_rr, 1);\n \treturn 0;\n-- \n1.7.2.1.544.ga752d.dirty\n"},{"id":"147092","messageId":"20100804032338.GF19699@burratino","threadId":"24269","inReplyTo":"20100804031935.GA19699@burratino","subject":"[PATCH 5/6] rerere: let caller decide whether to renormalize","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-08-04T03:23:38Z","receivedAt":"2010-08-04T03:23:38Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Add a RERERE_RENORMALIZE flag to rerere so callers can decide\ncase-by-case whether the merge is likely to have overlapped with a\nchange in smudge/clean rules.\n\nThis is only a change in internal plumbing.  Many callers do not have\na way to use that setting; this patch does not change that.\n\nNEEDSWORK: this is a step in the wrong direction.  rerere needs\nan -s option to use an arbitrary merge strategy and a -X option to\npass arbitrary options to that driver.  And maybe the options used\nin a merge should be recorded somewhere to help rerere repeat it.\n\nThis treats the renormalize option specially anyway, to support the\nexisting \"git merge\" behavior of using -Xrenormalize in its rerere\ncall when configured to do so.  It reluctant exposes that option in\nthe rerere command as --renormalize for the sake of experimentation,\ntoo, but it does not advertise it.\n\nCc: Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n builtin/commit.c |    4 ++++\n builtin/merge.c  |    7 +++++--\n builtin/rerere.c |    8 ++++++--\n builtin/revert.c |   11 +++++++++--\n rerere.c         |    5 +++--\n rerere.h         |    1 +\n 6 files changed, 28 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 3d99cf9..56c998f 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1377,6 +1377,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t\t     \"new_index file. Check that disk is not full or quota is\\n\"\n \t\t     \"not exceeded, and then \\\"git reset HEAD\\\" to recover.\");\n \n+\t/*\n+\t * NEEDSWORK: use RERERE_RENORMALIZE after a merge with\n+\t * merge.renormalize set\n+\t */\n \trerere(0);\n \trun_hook(get_index_file(), \"post-commit\", NULL);\n \tif (amend && !no_post_rewrite) {\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex b836e9c..823b76b 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -822,7 +822,7 @@ static int finish_automerge(struct commit_list *common,\n static int suggest_conflicts(void)\n {\n \tFILE *fp;\n-\tint pos;\n+\tint pos, flag;\n \n \tfp = fopen(git_path(\"MERGE_MSG\"), \"a\");\n \tif (!fp)\n@@ -841,7 +841,10 @@ static int suggest_conflicts(void)\n \t\t}\n \t}\n \tfclose(fp);\n-\trerere(allow_rerere_auto);\n+\tflag = allow_rerere_auto;\n+\tif (merge_renormalize)\n+\t\tflag |= RERERE_RENORMALIZE;\n+\trerere(flag);\n \tprintf(\"Automatic merge failed; \"\n \t\t\t\"fix conflicts and then commit the result.\\n\");\n \treturn 1;\ndiff --git a/builtin/rerere.c b/builtin/rerere.c\nindex 295fe75..4009c01 100644\n--- a/builtin/rerere.c\n+++ b/builtin/rerere.c\n@@ -111,15 +111,19 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)\n \tstruct option options[] = {\n \t\tOPT_SET_INT(0, \"rerere-autoupdate\", &autoupdate,\n \t\t\t\"register clean resolutions in index\", 1),\n+\t\t{ OPTION_BIT, 0, \"renormalize-before-merge\", &flags, NULL,\n+\t\t\t\"simplify conflict hunks for normalization changes\",\n+\t\t\tPARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL,\n+\t\t\tRERERE_RENORMALIZE },\n \t\tOPT_END(),\n \t};\n \n \targc = parse_options(argc, argv, prefix, options, rerere_usage, 0);\n \n \tif (autoupdate == 1)\n-\t\tflags = RERERE_AUTOUPDATE;\n+\t\tflags |= RERERE_AUTOUPDATE;\n \tif (autoupdate == 0)\n-\t\tflags = RERERE_NOAUTOUPDATE;\n+\t\tflags |= RERERE_NOAUTOUPDATE;\n \n \tif (argc < 1)\n \t\treturn rerere(flags);\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 853e9e4..c694801 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -338,7 +338,7 @@ static void do_recursive_merge(struct commit *base, struct commit *next,\n \trollback_lock_file(&index_lock);\n \n \tif (!clean) {\n-\t\tint i;\n+\t\tint i, flags;\n \t\tstrbuf_addstr(msgbuf, \"\\nConflicts:\\n\\n\");\n \t\tfor (i = 0; i < active_nr;) {\n \t\t\tstruct cache_entry *ce = active_cache[i++];\n@@ -354,7 +354,10 @@ static void do_recursive_merge(struct commit *base, struct commit *next,\n \t\twrite_message(msgbuf, defmsg);\n \t\tfprintf(stderr, \"Automatic %s failed.%s\\n\",\n \t\t\tme, help_msg());\n-\t\trerere(allow_rerere_auto);\n+\t\tflags = allow_rerere_auto;\n+\t\tif (o.renormalize)\n+\t\t\tflags |= RERERE_RENORMALIZE;\n+\t\trerere(flags);\n \t\texit(1);\n \t}\n \twrite_message(msgbuf, defmsg);\n@@ -481,6 +484,10 @@ static int do_pick_commit(void)\n \t\tif (res) {\n \t\t\tfprintf(stderr, \"Automatic %s with strategy %s failed.%s\\n\",\n \t\t\t\tme, strategy, help_msg());\n+\t\t\t/*\n+\t\t\t * NEEDSWORK: use RERERE_RENORMALIZE after\n+\t\t\t * a merge with merge.renormalize set\n+\t\t\t */\n \t\t\trerere(allow_rerere_auto);\n \t\t\texit(1);\n \t\t}\ndiff --git a/rerere.c b/rerere.c\nindex 17dcc3c..8767024 100644\n--- a/rerere.c\n+++ b/rerere.c\n@@ -553,12 +553,13 @@ int setup_rerere(struct string_list *merge_rr, int flags)\n int rerere(int flags)\n {\n \tstruct string_list merge_rr = { NULL, 0, 0, 1 };\n-\tint fd;\n+\tint fd, renormalize;\n \n \tfd = setup_rerere(&merge_rr, flags);\n \tif (fd < 0)\n \t\treturn 0;\n-\treturn do_plain_rerere(&merge_rr, fd, merge_renormalize);\n+\trenormalize = (flags & RERERE_RENORMALIZE) ? 1 : 0;\n+\treturn do_plain_rerere(&merge_rr, fd, renormalize);\n }\n \n static int rerere_forget_one_path(const char *path, struct string_list *rr)\ndiff --git a/rerere.h b/rerere.h\nindex eaa9004..a73555f 100644\n--- a/rerere.h\n+++ b/rerere.h\n@@ -5,6 +5,7 @@\n \n #define RERERE_AUTOUPDATE   01\n #define RERERE_NOAUTOUPDATE 02\n+#define RERERE_RENORMALIZE  04\n \n extern int setup_rerere(struct string_list *, int);\n extern int rerere(int);\n-- \n1.7.2.1.544.ga752d.dirty\n"},{"id":"147093","messageId":"20100804032937.GG19699@burratino","threadId":"24269","inReplyTo":"20100804031935.GA19699@burratino","subject":"[PATCH 6/6] merge-recursive: add -Xrenormalize option","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-08-04T03:29:37Z","receivedAt":"2010-08-04T03:29:37Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"This adds a -Xrenormalize option to override the\nmerge.renormalize configuration.  There is no way to override\nit in the negative yet, but hopefully this suggests how.\n\nThe good part: this destroys the global merge_renormalize\nvariable.\n\nThe bad part: merge.renormalize is still not honored for\nmost commands.  And it reveals lots of places that -X has not\nbeen plumbed in.\n\nCc: Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\nThere you have it.  As I said, this is pretty rough.  Places I would\nbe happy to see this go:\n\n - add tests and documentation\n - -Xno-renormalize to override [merge] renormalize in the negative\n - checkout -X and rerere -X\n\nI probably will not have time to work on this topic again for a while,\nso I would be very happy if someone takes ideas from it and takes it\nsomewhere else before I get a chance.  If there are just minor\ncleanups to do, I can maintain this series for a short while, though.\n\nThoughts?\n\n Documentation/merge-strategies.txt |    8 ++++++++\n builtin/checkout.c                 |    7 +++++++\n builtin/merge-recursive.c          |    2 ++\n builtin/merge.c                    |   19 +++++++++++++------\n builtin/revert.c                   |    7 +++++++\n cache.h                            |    1 -\n environment.c                      |    1 -\n merge-recursive.c                  |    2 +-\n 8 files changed, 38 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/merge-strategies.txt b/Documentation/merge-strategies.txt\nindex a5bc1db..350c3c7 100644\n--- a/Documentation/merge-strategies.txt\n+++ b/Documentation/merge-strategies.txt\n@@ -40,6 +40,14 @@ the other tree did, declaring 'our' history contains all that happened in it.\n theirs;;\n \tThis is opposite of 'ours'.\n \n+renormalize;;\n+\tThis runs a virtual check-out and check-in of all three stages\n+\tof a file when resolving a three-way merge.  This option is\n+\tmeant to be used when merging branches with different clean\n+\tfilters or end-of-line normalization rules.  See \"Merging\n+\tbranches with differing checkin/checkout attributes\" in\n+\tlinkgit:gitattributes[5] for details.\n+\n subtree[=path];;\n \tThis option is a more advanced form of 'subtree' strategy, where\n \tthe strategy makes a guess on how two trees must be shifted to\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 5a7ae03..00fd4cd 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -437,6 +437,13 @@ static int merge_working_tree(struct checkout_opts *opts,\n \t\t\t */\n \n \t\t\tadd_files_to_cache(NULL, NULL, 0);\n+\t\t\t/*\n+\t\t\t * NEEDSWORK: carrying over local changes\n+\t\t\t * when branches have different end-of-line\n+\t\t\t * normalization (or clean+smudge rules) is\n+\t\t\t * a pain; plumb in an option to set\n+\t\t\t * o.renormalize?\n+\t\t\t */\n \t\t\tinit_merge_options(&o);\n \t\t\to.verbosity = 0;\n \t\t\twork = write_tree_from_memory(&o);\ndiff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c\nindex d8875d5..2cc84d9 100644\n--- a/builtin/merge-recursive.c\n+++ b/builtin/merge-recursive.c\n@@ -45,6 +45,8 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)\n \t\t\t\to.subtree_shift = \"\";\n \t\t\telse if (!prefixcmp(arg+2, \"subtree=\"))\n \t\t\t\to.subtree_shift = arg + 10;\n+\t\t\telse if (!strcmp(arg+2, \"renormalize\"))\n+\t\t\t\to.renormalize = 1;\n \t\t\telse\n \t\t\t\tdie(\"Unknown option %s\", arg);\n \t\t\tcontinue;\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 823b76b..ad0aa5a 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -54,6 +54,7 @@ static size_t use_strategies_nr, use_strategies_alloc;\n static const char **xopts;\n static size_t xopts_nr, xopts_alloc;\n static const char *branch;\n+static int option_renormalize;\n static int verbosity;\n static int allow_rerere_auto;\n \n@@ -503,9 +504,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n \t\treturn git_config_string(&pull_octopus, k, v);\n \telse if (!strcmp(k, \"merge.log\") || !strcmp(k, \"merge.summary\"))\n \t\toption_log = git_config_bool(k, v);\n-\telse if (!strcmp(k, \"merge.renormalize\")) {\n-\t\tmerge_renormalize = git_config_bool(k, v);\n-\t}\n+\telse if (!strcmp(k, \"merge.renormalize\"))\n+\t\toption_renormalize = git_config_bool(k, v);\n \treturn git_diff_ui_config(k, v, cb);\n }\n \n@@ -627,6 +627,11 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,\n \t\tif (!strcmp(strategy, \"subtree\"))\n \t\t\to.subtree_shift = \"\";\n \n+\t\to.renormalize = option_renormalize;\n+\n+\t\t/*\n+\t\t * NEEDSWORK: merge with table in builtin/merge-recursive\n+\t\t */\n \t\tfor (x = 0; x < xopts_nr; x++) {\n \t\t\tif (!strcmp(xopts[x], \"ours\"))\n \t\t\t\to.recursive_variant = MERGE_RECURSIVE_OURS;\n@@ -636,6 +641,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,\n \t\t\t\to.subtree_shift = \"\";\n \t\t\telse if (!prefixcmp(xopts[x], \"subtree=\"))\n \t\t\t\to.subtree_shift = xopts[x]+8;\n+\t\t\telse if (!strcmp(xopts[x], \"renormalize\"))\n+\t\t\t\to.renormalize = 1;\n \t\t\telse\n \t\t\t\tdie(\"Unknown option for merge-recursive: -X%s\", xopts[x]);\n \t\t}\n@@ -819,7 +826,7 @@ static int finish_automerge(struct commit_list *common,\n \treturn 0;\n }\n \n-static int suggest_conflicts(void)\n+static int suggest_conflicts(int renormalizing)\n {\n \tFILE *fp;\n \tint pos, flag;\n@@ -842,7 +849,7 @@ static int suggest_conflicts(void)\n \t}\n \tfclose(fp);\n \tflag = allow_rerere_auto;\n-\tif (merge_renormalize)\n+\tif (renormalizing)\n \t\tflag |= RERERE_RENORMALIZE;\n \trerere(flag);\n \tprintf(\"Automatic merge failed; \"\n@@ -1307,5 +1314,5 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \t\t\t\"stopped before committing as requested\\n\");\n \t\treturn 0;\n \t} else\n-\t\treturn suggest_conflicts();\n+\t\treturn suggest_conflicts(option_renormalize);\n }\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex c694801..1999252 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -318,6 +318,13 @@ static void do_recursive_merge(struct commit *base, struct commit *next,\n \tindex_fd = hold_locked_index(&index_lock, 1);\n \n \tread_cache();\n+\n+\t/*\n+\t * NEEDSWORK: cherry-picking between branches with\n+\t * different end-of-line normalization is a pain;\n+\t * plumb in an option to set o.renormalize?\n+\t * (or better: arbitrary -X options)\n+\t */\n \tinit_merge_options(&o);\n \to.ancestor = base ? base_label : \"(empty tree)\";\n \to.branch1 = \"HEAD\";\ndiff --git a/cache.h b/cache.h\nindex ed73da8..aa725b0 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -551,7 +551,6 @@ extern int read_replace_refs;\n extern int fsync_object_files;\n extern int core_preload_index;\n extern int core_apply_sparse_checkout;\n-extern int merge_renormalize;\n \n enum safe_crlf {\n \tSAFE_CRLF_FALSE = 0,\ndiff --git a/environment.c b/environment.c\nindex 81a3682..83d38d3 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -53,7 +53,6 @@ enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;\n char *notes_ref_name;\n int grafts_replace_parents = 1;\n int core_apply_sparse_checkout;\n-int merge_renormalize;\n \n /* Parallel index stat data preload? */\n int core_preload_index = 0;\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 4838939..73f2768 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1485,7 +1485,7 @@ void init_merge_options(struct merge_options *o)\n \to->buffer_output = 1;\n \to->diff_rename_limit = -1;\n \to->merge_rename_limit = -1;\n-\to->renormalize = merge_renormalize;\n+\to->renormalize = 0;\n \tgit_config(merge_recursive_config, o);\n \tif (getenv(\"GIT_MERGE_VERBOSITY\"))\n \t\to->verbosity =\n-- \n1.7.2.1.544.ga752d.dirty\n"},{"id":"147119","messageId":"7v39uugses.fsf@alter.siamese.dyndns.org","threadId":"24269","inReplyTo":"20100804031935.GA19699@burratino","subject":"Re: [PATCH/RFC eb/double-convert-before-merge 0/6] merge -Xrenormalize","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-08-04T18:10:51Z","receivedAt":"2010-08-04T18:10:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jonathan Nieder <jrnieder@gmail.com> writes:\n\n> But for later, they tell a nice story.  The idea is that the\n> \"merge.renormalize\" is not really about the behavior of the \"git\n> merge\" command but about the merge-recursive driver; and so it\n> should be usable everywhere else that driver is used, too.\n> ...\n> So in an ideal world, you would be able to, e.g.:\n>\n> \tgit checkout -m -Xrenormalize otherbranch\n>\n> or\n>\n> \tgit revert -Xrenormalize otherpatch\n>\n> or\n>\n> \tgit pull --rebase -Xrenormalize\n>\n> Of course, the plumbing is not all there yet; many commands that\n> use merge drivers cannot pass through arbitrary options through\n> yet.\n>\n> Well, enough talking.  Here are the patches; what do you think?\n\nNicely summarized.\n"},{"id":"147120","messageId":"7vvd7qfdsa.fsf@alter.siamese.dyndns.org","threadId":"24269","inReplyTo":"20100804032144.GD19699@burratino","subject":"Re: [PATCH 3/6] ll-merge: let caller decide whether to renormalize","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-08-04T18:12:05Z","receivedAt":"2010-08-04T18:12:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jonathan Nieder <jrnieder@gmail.com> writes:\n\n> Add a \"renormalize\" parameter to ll_merge so callers can decide on a\n> case-by-case basis whether the merge is likely to have overlapped with\n> a change in smudge/clean rules.  This reveals a few commands that have\n> not been taking that situation into account, though it does not fix\n> them.\n>\n> No change in behavior intended.\n\nLooking nice so far; I however think a separate \"renormalize\" parameter\nshould be folded into \"flag\" word, perhaps with a few new macros to make\nit easier to access renormalize, virtual-ancestor, and favor-side parts\nit.\n"},{"id":"147121","messageId":"7vocdifdrk.fsf@alter.siamese.dyndns.org","threadId":"24269","inReplyTo":"20100804032338.GF19699@burratino","subject":"Re: [PATCH 5/6] rerere: let caller decide whether to renormalize","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-08-04T18:12:31Z","receivedAt":"2010-08-04T18:12:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jonathan Nieder <jrnieder@gmail.com> writes:\n\n> NEEDSWORK: this is a step in the wrong direction.  rerere needs\n> an -s option to use an arbitrary merge strategy and a -X option to\n> pass arbitrary options to that driver.\n\nIf you are talking about \"rerere\", I strongly disagree with that.\n\n1. \"-s\"\n\n   A \"merge strategy\" deals with the shape of the history (e.g. common\n   ancestor selection or synthesis for the purpose of 3-way merge) and the\n   shape of the trees (e.g. rename detection, subtree shifting).  Starting\n   from two commits, it decides, based on the shape of the history, what\n   three trees your tree-level 3-way merge would operate on, and then\n   based on the shape of the trees, decides the pairing of blobs to run\n   the 3-way merge at the file-content level.\n\n   When \"rerere\" is invoked, a strategy already has dealt with all of the\n   above, and \"rerere\" only works on the (half-completed) result of that.\n   It is purely a three-way merge at the file-contents level and there is\n   no room for a \"strategy\" to get involved.  It makes direct calls to\n   ll_merge() exactly for this reason.\n\n2. \"-X\"\n\n   In the \"merge -X<opt>\" syntax, \"-X\" does not stand for \"low level\n   details\"; it just means \"eXternal callout\".  It is there just to tell\n   the \"merge\" front-end \"You may not understand this yourself, but the\n   program you call does, so just pass it along\".\n\n   IOW, we want to be able to pass --<opt> through the frontend to the\n   backend, without having to tell all the options any possible backends\n   may know to the frontend.  Just to make it easier to parse and tell\n   which ones are the front-end options and which ones are not (for both\n   machines and humans), we say -X<opt> to the frontend.  Then \"merge\"\n   turns that -X<opt> into \"--<opt>\" and give it to the strategy.\n\n   A command that natively knows about an option is correct to take an\n   option as \"--<option>\", e.g. \"merge--recursive --renormalize\".  You\n   trigger it by saying \"merge -Xrenormalize\" from the frontend.\n\n\nTo recap: it is absolutely the right thing to do to introduce a new\n\"rerere --renormalize\" option, like your patch did.  Doing anything else\nIS a step in the wrong direction.\n"},{"id":"147164","messageId":"20100805110822.GB13779@burratino","threadId":"24269","inReplyTo":"7vocdifdrk.fsf@alter.siamese.dyndns.org","subject":"[PATCH/RFC v2 0/12] Re: rerere: let caller decide whether to renormalize","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-08-05T11:08:22Z","receivedAt":"2010-08-05T11:08:22Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Junio C Hamano wrote:\n\n> 1. \"-s\"\n[...]\n>    When \"rerere\" is invoked,\n[...]\n>    It is purely a three-way merge at the file-contents level and there is\n>    no room for a \"strategy\" to get involved.  It makes direct calls to\n>    ll_merge() exactly for this reason.\n> \n> 2. \"-X\"\n[...]\n>    A command that natively knows about an option is correct to take an\n>    option as \"--<option>\", e.g. \"merge--recursive --renormalize\".  You\n>    trigger it by saying \"merge -Xrenormalize\" from the frontend.\n> \n> \n> To recap: it is absolutely the right thing to do to introduce a new\n> \"rerere --renormalize\" option, like your patch did.  Doing anything else\n> IS a step in the wrong direction.\n\nOnce you say it like that, it makes sense. :)\n\nOr rather, rerere should not take --renormalize at all.  Patch 11 below\nhas an explanation.\n\nJonathan Nieder (12):\n  t6038 (merge.renormalize): style nitpicks\n  t6038 (merge.renormalize): try checkout -m and cherry-pick\n  t6038 (merge.renormalize): check that it can be turned off\n  merge-trees: push choice to renormalize away from low level\n  merge-trees: let caller decide whether to renormalize\n  Documentation/technical: document ll_merge\n  ll-merge: make flag easier to populate\n  ll-merge: let caller decide whether to renormalize\n  t4200 (rerere): modernize style\n  rerere: migrate to parse-options API\n  rerere: never renormalize\n  merge-recursive --renormalize\n\n Documentation/merge-strategies.txt    |   12 +\n Documentation/technical/api-merge.txt |   73 ++++++\n builtin/checkout.c                    |   11 +\n builtin/merge-recursive.c             |    4 +\n builtin/merge.c                       |   19 ++-\n builtin/rerere.c                      |   52 +++--\n builtin/revert.c                      |    7 +\n cache.h                               |    1 -\n environment.c                         |    1 -\n ll-merge.c                            |   11 +-\n ll-merge.h                            |   15 ++\n merge-recursive.c                     |   14 +-\n merge-recursive.h                     |    1 +\n rerere.c                              |    4 +\n t/t4200-rerere.sh                     |  394 ++++++++++++++++++++++-----------\n t/t6038-merge-text-auto.sh            |  143 +++++++++++-\n 16 files changed, 588 insertions(+), 174 deletions(-)\n create mode 100644 Documentation/technical/api-merge.txt\n\n-- \n1.7.2.1.544.ga752d.dirty\n"},{"id":"147165","messageId":"20100805110933.GC13779@burratino","threadId":"24269","inReplyTo":"20100805110822.GB13779@burratino","subject":"[PATCH 01/12] t6038 (merge.renormalize): style nitpicks","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-08-05T11:09:33Z","receivedAt":"2010-08-05T11:09:33Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Some tweaks to simplify adding and running tests.\n\n - Use test_tick for predictable, sort of realistic commit dates;\n\n - Use test_cmp as \"test_cmp expected actual\" --- some crazy\n   content that was not expected should cause the test to fail;\n\n - Remove and re-add all files at the start of each test so the\n   worktree is easier to think about;\n\n - Avoid using cat where not necessary for clarity.\n\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n t/t6038-merge-text-auto.sh |   52 +++++++++++++++++++++++++++++++++++++------\n 1 files changed, 44 insertions(+), 8 deletions(-)\n\ndiff --git a/t/t6038-merge-text-auto.sh b/t/t6038-merge-text-auto.sh\nindex d1ab86e..e21b5d2 100755\n--- a/t/t6038-merge-text-auto.sh\n+++ b/t/t6038-merge-text-auto.sh\n@@ -1,58 +1,94 @@\n #!/bin/sh\n \n-test_description='CRLF merge conflict across text=auto change'\n+test_description='CRLF merge conflict across text=auto change\n+\n+* [master] remove .gitattributes\n+ ! [side] add line from b\n+--\n+ + [side] add line from b\n+*  [master] remove .gitattributes\n+*  [master^] add line from a\n+*  [master~2] normalize file\n+*+ [side^] Initial\n+'\n \n . ./test-lib.sh\n \n test_expect_success setup '\n \tgit config merge.renormalize true &&\n \tgit config core.autocrlf false &&\n+\n \techo first line | append_cr >file &&\n \techo first line >control_file &&\n \techo only line >inert_file &&\n+\n \tgit add file control_file inert_file &&\n+\ttest_tick &&\n \tgit commit -m \"Initial\" &&\n \tgit tag initial &&\n \tgit branch side &&\n+\n \techo \"* text=auto\" >.gitattributes &&\n \ttouch file &&\n \tgit add .gitattributes file &&\n+\ttest_tick &&\n \tgit commit -m \"normalize file\" &&\n+\n \techo same line | append_cr >>file &&\n \techo same line >>control_file &&\n \tgit add file control_file &&\n+\ttest_tick &&\n \tgit commit -m \"add line from a\" &&\n \tgit tag a &&\n+\n \tgit rm .gitattributes &&\n \trm file &&\n \tgit checkout file &&\n+\ttest_tick &&\n \tgit commit -m \"remove .gitattributes\" &&\n \tgit tag c &&\n+\n \tgit checkout side &&\n \techo same line | append_cr >>file &&\n \techo same line >>control_file &&\n \tgit add file control_file &&\n+\ttest_tick &&\n \tgit commit -m \"add line from b\" &&\n \tgit tag b &&\n+\n \tgit checkout master\n '\n \n-test_expect_success 'Check merging after setting text=auto' '\n+test_expect_success 'Merge after setting text=auto' '\n+\tcat <<-\\EOF >expected &&\n+\tfirst line\n+\tsame line\n+\tEOF\n+\n+\tgit rm -fr . &&\n+\trm -f .gitattributes &&\n \tgit reset --hard a &&\n \tgit merge b &&\n-\tcat file | remove_cr >file.temp &&\n-\ttest_cmp file file.temp\n+\ttest_cmp expected file\n '\n \n-test_expect_success 'Check merging addition of text=auto' '\n+test_expect_success 'Merge addition of text=auto' '\n+\tcat <<-\\EOF >expected &&\n+\tfirst line\n+\tsame line\n+\tEOF\n+\n+\tgit rm -fr . &&\n+\trm -f .gitattributes &&\n \tgit reset --hard b &&\n \tgit merge a &&\n-\tcat file | remove_cr >file.temp &&\n-\ttest_cmp file file.temp\n+\ttest_cmp expected file\n '\n \n test_expect_success 'Test delete/normalize conflict' '\n-\tgit checkout side &&\n+\tgit checkout -f side &&\n+\tgit rm -fr . &&\n+\trm -f .gitattributes &&\n \tgit reset --hard initial &&\n \tgit rm file &&\n \tgit commit -m \"remove file\" &&\n-- \n1.7.2.1.544.ga752d.dirty\n"},{"id":"147166","messageId":"20100805111112.GD13779@burratino","threadId":"24269","inReplyTo":"20100805110822.GB13779@burratino","subject":"[PATCH 02/12] t6038 (merge.renormalize): try checkout -m and cherry-pick","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-08-05T11:11:12Z","receivedAt":"2010-08-05T11:11:12Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"checkout -m and cherry-pick have not been wired up to respect\nmerge.renormalize, but a naïve user would not know that.\n\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\nThis series does not change these to expect success, but they\nare part of the same thought process.\n\n t/t6038-merge-text-auto.sh |   41 +++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 41 insertions(+), 0 deletions(-)\n\ndiff --git a/t/t6038-merge-text-auto.sh b/t/t6038-merge-text-auto.sh\nindex e21b5d2..a7ea4b6 100755\n--- a/t/t6038-merge-text-auto.sh\n+++ b/t/t6038-merge-text-auto.sh\n@@ -85,6 +85,47 @@ test_expect_success 'Merge addition of text=auto' '\n \ttest_cmp expected file\n '\n \n+test_expect_failure 'checkout -m after setting text=auto' '\n+\tcat <<-\\EOF >expected &&\n+\tfirst line\n+\tsame line\n+\tEOF\n+\n+\tgit rm -fr . &&\n+\trm -f .gitattributes &&\n+\tgit reset --hard initial &&\n+\tgit checkout a -- . &&\n+\tgit checkout -m b &&\n+\ttest_cmp expected file\n+'\n+\n+test_expect_failure 'checkout -m addition of text=auto' '\n+\tcat <<-\\EOF >expected &&\n+\tfirst line\n+\tsame line\n+\tEOF\n+\n+\tgit rm -fr . &&\n+\trm -f .gitattributes file &&\n+\tgit reset --hard initial &&\n+\tgit checkout b -- . &&\n+\tgit checkout -m a &&\n+\ttest_cmp expected file\n+'\n+\n+test_expect_failure 'cherry-pick patch from after text=auto was added' '\n+\tappend_cr <<-\\EOF >expected &&\n+\tfirst line\n+\tsame line\n+\tEOF\n+\n+\tgit rm -fr . &&\n+\tgit reset --hard b &&\n+\ttest_must_fail git cherry-pick a >err 2>&1 &&\n+\tgrep \"[Nn]othing added\" err &&\n+\ttest_cmp expected file\n+'\n+\n test_expect_success 'Test delete/normalize conflict' '\n \tgit checkout -f side &&\n \tgit rm -fr . &&\n-- \n1.7.2.1.544.ga752d.dirty\n"},{"id":"147167","messageId":"20100805111304.GE13779@burratino","threadId":"24269","inReplyTo":"20100805110822.GB13779@burratino","subject":"[PATCH 03/12] t6038 (merge.renormalize): check that it can be turned off","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-08-05T11:13:04Z","receivedAt":"2010-08-05T11:13:04Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"An unusual sort of person (not me) may even enjoy the conflicts\nfrom line-ending changes.  But more importantly, it is useful to\ndocument that behavior so we can more easily notice if it changes\nin an uncontrolled way while no one is watching.\n\nCc: Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n t/t6038-merge-text-auto.sh |   50 +++++++++++++++++++++++++++++++++++++++++++-\n 1 files changed, 49 insertions(+), 1 deletions(-)\n\ndiff --git a/t/t6038-merge-text-auto.sh b/t/t6038-merge-text-auto.sh\nindex a7ea4b6..52d0dc4 100755\n--- a/t/t6038-merge-text-auto.sh\n+++ b/t/t6038-merge-text-auto.sh\n@@ -15,7 +15,6 @@ test_description='CRLF merge conflict across text=auto change\n . ./test-lib.sh\n \n test_expect_success setup '\n-\tgit config merge.renormalize true &&\n \tgit config core.autocrlf false &&\n \n \techo first line | append_cr >file &&\n@@ -59,12 +58,19 @@ test_expect_success setup '\n \tgit checkout master\n '\n \n+test_expect_success 'set up fuzz_conflict() helper' '\n+\tfuzz_conflict() {\n+\t\tsed -e \"s/^\\([<>=]......\\) .*/\\1/\" \"$@\"\n+\t}\n+'\n+\n test_expect_success 'Merge after setting text=auto' '\n \tcat <<-\\EOF >expected &&\n \tfirst line\n \tsame line\n \tEOF\n \n+\tgit config merge.renormalize true &&\n \tgit rm -fr . &&\n \trm -f .gitattributes &&\n \tgit reset --hard a &&\n@@ -78,6 +84,7 @@ test_expect_success 'Merge addition of text=auto' '\n \tsame line\n \tEOF\n \n+\tgit config merge.renormalize true &&\n \tgit rm -fr . &&\n \trm -f .gitattributes &&\n \tgit reset --hard b &&\n@@ -85,12 +92,51 @@ test_expect_success 'Merge addition of text=auto' '\n \ttest_cmp expected file\n '\n \n+test_expect_success 'Detect CRLF/LF conflict after setting text=auto' '\n+\tq_to_cr <<-\\EOF >expected &&\n+\t<<<<<<<\n+\tfirst line\n+\tsame line\n+\t=======\n+\tfirst lineQ\n+\tsame lineQ\n+\t>>>>>>>\n+\tEOF\n+\n+\tgit config merge.renormalize false &&\n+\trm -f .gitattributes &&\n+\tgit reset --hard a &&\n+\ttest_must_fail git merge b &&\n+\tfuzz_conflict file >file.fuzzy &&\n+\ttest_cmp expected file.fuzzy\n+'\n+\n+test_expect_success 'Detect LF/CRLF conflict from addition of text=auto' '\n+\tq_to_cr <<-\\EOF >expected &&\n+\t<<<<<<<\n+\tfirst lineQ\n+\tsame lineQ\n+\t=======\n+\tfirst line\n+\tsame line\n+\t>>>>>>>\n+\tEOF\n+\n+\tgit config merge.renormalize false &&\n+\trm -f .gitattributes &&\n+\tgit reset --hard b &&\n+\ttest_must_fail git merge a &&\n+\tfuzz_conflict file >file.fuzzy &&\n+\ttest_cmp expected file.fuzzy\n+'\n+\n test_expect_failure 'checkout -m after setting text=auto' '\n \tcat <<-\\EOF >expected &&\n \tfirst line\n \tsame line\n \tEOF\n \n+\tgit config merge.renormalize true &&\n \tgit rm -fr . &&\n \trm -f .gitattributes &&\n \tgit reset --hard initial &&\n@@ -105,6 +151,7 @@ test_expect_failure 'checkout -m addition of text=auto' '\n \tsame line\n \tEOF\n \n+\tgit config merge.renormalize true &&\n \tgit rm -fr . &&\n \trm -f .gitattributes file &&\n \tgit reset --hard initial &&\n@@ -119,6 +166,7 @@ test_expect_failure 'cherry-pick patch from after text=auto was added' '\n \tsame line\n \tEOF\n \n+\tgit config merge.renormalize true &&\n \tgit rm -fr . &&\n \tgit reset --hard b &&\n \ttest_must_fail git cherry-pick a >err 2>&1 &&\n-- \n1.7.2.1.544.ga752d.dirty\n"},{"id":"147168","messageId":"20100805111349.GF13779@burratino","threadId":"24269","inReplyTo":"20100805110822.GB13779@burratino","subject":"[PATCH 04/12] merge-trees: push choice to renormalize away from low level","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-08-05T11:13:49Z","receivedAt":"2010-08-05T11:13:49Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"The merge machinery decides whether to resmudge and clean relevant\nentries based on the global merge_renormalize setting, which is set by\n\"git merge\" based on its configuration (and left alone by other\ncommands).\n\nA nicer interface would make that decision a parameter to merge_trees\nso callers would pass in a choice made on a call-by-call basis.\nStart by making blob_unchanged stop examining the merge_renormalize\nglobal.\n\nIn other words, this change is a trivial no-op, but it brings us\ncloser to something good.\n\nCc: Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\nUnchanged from v1.\n\n merge-recursive.c |    9 +++++----\n 1 files changed, 5 insertions(+), 4 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 5ad8fc9..2b55fc2 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1074,7 +1074,7 @@ static int read_sha1_strbuf(const unsigned char *sha1, struct strbuf *dst)\n \n static int blob_unchanged(const unsigned char *o_sha,\n \t\t\t  const unsigned char *a_sha,\n-\t\t\t  const char *path)\n+\t\t\t  int renormalize, const char *path)\n {\n \tstruct strbuf o = STRBUF_INIT;\n \tstruct strbuf a = STRBUF_INIT;\n@@ -1082,7 +1082,7 @@ static int blob_unchanged(const unsigned char *o_sha,\n \n \tif (sha_eq(o_sha, a_sha))\n \t\treturn 1;\n-\tif (!merge_renormalize)\n+\tif (!renormalize)\n \t\treturn 0;\n \n \tassert(o_sha && a_sha);\n@@ -1112,6 +1112,7 @@ static int process_entry(struct merge_options *o,\n \tprint_index_entry(\"\\tpath: \", entry);\n \t*/\n \tint clean_merge = 1;\n+\tint normalize = merge_renormalize;\n \tunsigned o_mode = entry->stages[1].mode;\n \tunsigned a_mode = entry->stages[2].mode;\n \tunsigned b_mode = entry->stages[3].mode;\n@@ -1122,8 +1123,8 @@ static int process_entry(struct merge_options *o,\n \tif (o_sha && (!a_sha || !b_sha)) {\n \t\t/* Case A: Deleted in one */\n \t\tif ((!a_sha && !b_sha) ||\n-\t\t    (!b_sha && blob_unchanged(o_sha, a_sha, path)) ||\n-\t\t    (!a_sha && blob_unchanged(o_sha, b_sha, path))) {\n+\t\t    (!b_sha && blob_unchanged(o_sha, a_sha, normalize, path)) ||\n+\t\t    (!a_sha && blob_unchanged(o_sha, b_sha, normalize, path))) {\n \t\t\t/* Deleted in both or deleted in one and\n \t\t\t * unchanged in the other */\n \t\t\tif (a_sha)\n-- \n1.7.2.1.544.ga752d.dirty\n"},{"id":"147169","messageId":"20100805111532.GG13779@burratino","threadId":"24269","inReplyTo":"20100805110822.GB13779@burratino","subject":"[PATCH 05/12] merge-trees: let caller decide whether to renormalize","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-08-05T11:15:32Z","receivedAt":"2010-08-05T11:15:32Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Add a \"renormalize\" option to struct merge_options so callers can\ndecide on a case-by-case basis whether the merge is likely to have\noverlapped with a change in smudge/clean rules.  The option defaults\nto the global merge_renormalize setting for now.\n\nNo change in behavior intended.\n\nCc: Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\nThe renormalize option now is advertized to only require one bit.\nMaybe some optimizer will take advantage of that.\n\n merge-recursive.c |    3 ++-\n merge-recursive.h |    1 +\n 2 files changed, 3 insertions(+), 1 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 2b55fc2..8a49844 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1112,7 +1112,7 @@ static int process_entry(struct merge_options *o,\n \tprint_index_entry(\"\\tpath: \", entry);\n \t*/\n \tint clean_merge = 1;\n-\tint normalize = merge_renormalize;\n+\tint normalize = o->renormalize;\n \tunsigned o_mode = entry->stages[1].mode;\n \tunsigned a_mode = entry->stages[2].mode;\n \tunsigned b_mode = entry->stages[3].mode;\n@@ -1484,6 +1484,7 @@ void init_merge_options(struct merge_options *o)\n \to->buffer_output = 1;\n \to->diff_rename_limit = -1;\n \to->merge_rename_limit = -1;\n+\to->renormalize = merge_renormalize;\n \tgit_config(merge_recursive_config, o);\n \tif (getenv(\"GIT_MERGE_VERBOSITY\"))\n \t\to->verbosity =\ndiff --git a/merge-recursive.h b/merge-recursive.h\nindex b831293..c5fbe79 100644\n--- a/merge-recursive.h\n+++ b/merge-recursive.h\n@@ -14,6 +14,7 @@ struct merge_options {\n \t} recursive_variant;\n \tconst char *subtree_shift;\n \tunsigned buffer_output : 1;\n+\tunsigned renormalize : 1;\n \tint verbosity;\n \tint diff_rename_limit;\n \tint merge_rename_limit;\n-- \n1.7.2.1.544.ga752d.dirty\n"},{"id":"147170","messageId":"20100805111651.GH13779@burratino","threadId":"24269","inReplyTo":"20100805110822.GB13779@burratino","subject":"[PATCH 06/12] Documentation/technical: document ll_merge","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-08-05T11:16:51Z","receivedAt":"2010-08-05T11:16:51Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Cc: Junio C Hamano <gitster@pobox.com>\nCc: Avery Pennarun <apenwarr@gmail.com>\nCc: Bert Wesarg <bert.wesarg@googlemail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\nSane?\n\n Documentation/technical/api-merge.txt |   70 +++++++++++++++++++++++++++++++++\n 1 files changed, 70 insertions(+), 0 deletions(-)\n create mode 100644 Documentation/technical/api-merge.txt\n\ndiff --git a/Documentation/technical/api-merge.txt b/Documentation/technical/api-merge.txt\nnew file mode 100644\nindex 0000000..01a89d6\n--- /dev/null\n+++ b/Documentation/technical/api-merge.txt\n@@ -0,0 +1,70 @@\n+merge API\n+=========\n+\n+The merge API helps a program to reconcile two competing sets of\n+improvements to some files (e.g., unregistered changes from the work\n+tree versus changes involved in switching to a new branch), reporting\n+conflicts if found.  The library called through this API is\n+responsible for a few things.\n+\n+ * determining which trees to merge (recursive ancestor consolidation);\n+\n+ * lining up corresponding files in the trees to be merged (rename\n+   detection, subtree shifting), reporting edge cases like add/add\n+   and rename/rename conflicts to the user;\n+\n+ * performing a three-way merge of corresponding files, taking\n+   path-specific merge drivers (specified in `.gitattributes`)\n+   into account.\n+\n+Low-level (single file) merge\n+-----------------------------\n+\n+`ll_merge`::\n+\n+\tPerform a three-way single-file merge in core.  This is\n+\ta thin wrapper around `xdl_merge` that takes the path and\n+\tany merge backend specified in `.gitattributes` or\n+\t`.git/info/attributes` into account.  Returns 0 for a\n+\tclean merge.\n+\n+The caller:\n+\n+1. allocates an mmbuffer_t variable for the result;\n+2. allocates and fills variables with the file's original content\n+   and two modified versions (using `read_mmfile`, for example);\n+3. calls ll_merge();\n+4. reads the output from result_buf.ptr and result_buf.size;\n+5. releases buffers when finished (free(ancestor.ptr); free(ours.ptr);\n+   free(theirs.ptr); free(result_buf.ptr);).\n+\n+If the modifications do not merge cleanly, `ll_merge` will return a\n+nonzero value and `result_buf` will generally include a description of\n+the conflict bracketed by markers such as the traditional `<<<<<<<`\n+and `>>>>>>>`.\n+\n+The `ancestor_label`, `our_label`, and `their_label` parameters are\n+used to label the different sides of a conflict if the merge driver\n+supports this.\n+\n+The `flag` parameter is a bitfield:\n+\n+ - The least significant bit indicates whether this is an internal\n+   merge to consolidate ancestors for a recursive merge.\n+\n+ - The next two bits allow local conflicts to be automatically\n+   resolved in favor of one side or the other (as in 'git merge-file'\n+   `--ours`/`--theirs`/`--union` for 01, 10, and 11, respectively).\n+\n+Everything else\n+---------------\n+\n+Talk about <merge-recursive.h> and merge_file():\n+\n+ - merge_trees() to merge with rename detection\n+ - merge_recursive() for ancestor consolidation\n+ - try_merge_command() for other strategies\n+ - conflict format\n+ - merge options\n+\n+(Daniel, Miklos, Stephan, JC)\n-- \n1.7.2.1.544.ga752d.dirty\n"},{"id":"147171","messageId":"20100805111738.GI13779@burratino","threadId":"24269","inReplyTo":"20100805110822.GB13779@burratino","subject":"[PATCH 07/12] ll-merge: make flag easier to populate","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-08-05T11:17:38Z","receivedAt":"2010-08-05T11:17:38Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"ll_merge() takes its options in a flag word, which has a few\nadvantages:\n\n - options flags can be cheaply passed around in registers, while\n   an option struct passed by pointer cannot;\n\n - callers can easily pass 0 without trouble for no options,\n   while an option struct passed by value would not allow that.\n\nThe downside is that code to populate and access the flag word can be\nsomewhat opaque.  Mitigate that with a few macros.\n\nCc: Avery Pennarun <apenwarr@gmail.com>\nCc: Bert Wesarg <bert.wesarg@googlemail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n Documentation/technical/api-merge.txt |   11 +++++++----\n ll-merge.c                            |    9 +++++----\n ll-merge.h                            |   14 ++++++++++++++\n merge-recursive.c                     |    3 ++-\n 4 files changed, 28 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/technical/api-merge.txt b/Documentation/technical/api-merge.txt\nindex 01a89d6..a7e050b 100644\n--- a/Documentation/technical/api-merge.txt\n+++ b/Documentation/technical/api-merge.txt\n@@ -49,12 +49,15 @@ supports this.\n \n The `flag` parameter is a bitfield:\n \n- - The least significant bit indicates whether this is an internal\n-   merge to consolidate ancestors for a recursive merge.\n+ - The `LL_OPT_VIRTUAL_ANCESTOR` bit indicates whether this is an\n+   internal merge to consolidate ancestors for a recursive merge.\n \n- - The next two bits allow local conflicts to be automatically\n+ - The `LL_OPT_FAVOR_MASK` bits allow local conflicts to be automatically\n    resolved in favor of one side or the other (as in 'git merge-file'\n-   `--ours`/`--theirs`/`--union` for 01, 10, and 11, respectively).\n+   `--ours`/`--theirs`/`--union`).\n+   They can be populated by `create_ll_flag`, whose argument can be\n+   `XDL_MERGE_FAVOR_OURS`, `XDL_MERGE_FAVOR_THEIRS`, or\n+   `XDL_MERGE_FAVOR_UNION`.\n \n Everything else\n ---------------\ndiff --git a/ll-merge.c b/ll-merge.c\nindex 5068fe0..290f764 100644\n--- a/ll-merge.c\n+++ b/ll-merge.c\n@@ -46,7 +46,7 @@ static int ll_binary_merge(const struct ll_merge_driver *drv_unused,\n \t * or common ancestor for an internal merge.  Still return\n \t * \"conflicted merge\" status.\n \t */\n-\tmmfile_t *stolen = (flag & 01) ? orig : src1;\n+\tmmfile_t *stolen = (flag & LL_OPT_VIRTUAL_ANCESTOR) ? orig : src1;\n \n \tresult->ptr = stolen->ptr;\n \tresult->size = stolen->size;\n@@ -79,7 +79,7 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,\n \n \tmemset(&xmp, 0, sizeof(xmp));\n \txmp.level = XDL_MERGE_ZEALOUS;\n-\txmp.favor= (flag >> 1) & 03;\n+\txmp.favor = ll_opt_favor(flag);\n \tif (git_xmerge_style >= 0)\n \t\txmp.style = git_xmerge_style;\n \tif (marker_size > 0)\n@@ -99,7 +99,8 @@ static int ll_union_merge(const struct ll_merge_driver *drv_unused,\n \t\t\t  int flag, int marker_size)\n {\n \t/* Use union favor */\n-\tflag = (flag & 1) | (XDL_MERGE_FAVOR_UNION << 1);\n+\tflag = (flag & LL_OPT_VIRTUAL_ANCESTOR) |\n+\t       create_ll_flag(XDL_MERGE_FAVOR_UNION);\n \treturn ll_xdl_merge(drv_unused, result, path_unused,\n \t\t\t    orig, NULL, src1, NULL, src2, NULL,\n \t\t\t    flag, marker_size);\n@@ -342,7 +343,7 @@ int ll_merge(mmbuffer_t *result_buf,\n \tconst char *ll_driver_name = NULL;\n \tint marker_size = DEFAULT_CONFLICT_MARKER_SIZE;\n \tconst struct ll_merge_driver *driver;\n-\tint virtual_ancestor = flag & 01;\n+\tint virtual_ancestor = flag & LL_OPT_VIRTUAL_ANCESTOR;\n \n \tif (merge_renormalize) {\n \t\tnormalize_file(ancestor, path);\ndiff --git a/ll-merge.h b/ll-merge.h\nindex 57754cc..5990271 100644\n--- a/ll-merge.h\n+++ b/ll-merge.h\n@@ -5,6 +5,20 @@\n #ifndef LL_MERGE_H\n #define LL_MERGE_H\n \n+#define LL_OPT_VIRTUAL_ANCESTOR\t(1 << 0)\n+#define LL_OPT_FAVOR_MASK\t((1 << 1) | (1 << 2))\n+#define LL_OPT_FAVOR_SHIFT 1\n+\n+static inline int ll_opt_favor(int flag)\n+{\n+\treturn (flag & LL_OPT_FAVOR_MASK) >> LL_OPT_FAVOR_SHIFT;\n+}\n+\n+static inline int create_ll_flag(int favor)\n+{\n+\treturn ((favor << LL_OPT_FAVOR_SHIFT) & LL_OPT_FAVOR_MASK);\n+}\n+\n int ll_merge(mmbuffer_t *result_buf,\n \t     const char *path,\n \t     mmfile_t *ancestor, const char *ancestor_label,\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 8a49844..c0c9f0c 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -647,7 +647,8 @@ static int merge_3way(struct merge_options *o,\n \n \tmerge_status = ll_merge(result_buf, a->path, &orig, base_name,\n \t\t\t\t&src1, name1, &src2, name2,\n-\t\t\t\t(!!o->call_depth) | (favor << 1));\n+\t\t\t\t((o->call_depth ? LL_OPT_VIRTUAL_ANCESTOR : 0) |\n+\t\t\t\t create_ll_flag(favor)));\n \n \tfree(name1);\n \tfree(name2);\n-- \n1.7.2.1.544.ga752d.dirty\n"},{"id":"147172","messageId":"20100805112458.GJ13779@burratino","threadId":"24269","inReplyTo":"20100805110822.GB13779@burratino","subject":"[PATCH 08/12] ll-merge: let caller decide whether to renormalize","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-08-05T11:24:58Z","receivedAt":"2010-08-05T11:24:58Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Add a “renormalize” bit to the ll-merge options word so callers can\ndecide on a case-by-case basis whether the merge is likely to have\noverlapped with a change in smudge/clean rules.\n\nThis reveals a few commands that have not been taking that situation\ninto account, though it does not fix them.\n\nNo functional change intended.\n\nCc: Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com>\nImproved-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\nUses the flag word now.\n\nThe new option is not exposed through “git merge-file”, just like\nthe virtual-ancestor option isn’t, but that is only from laziness.\nExposing it would make tests this easier, too.\n\nSome worries:\n\n - \"checkout -m\" does not do convert_to_worktree() but it should;\n - \"rerere forget\" has not been introduced to the wonderful world\n   of smudge filters, either.\n\n builtin/checkout.c |    4 ++++\n ll-merge.c         |    6 +++---\n ll-merge.h         |    1 +\n merge-recursive.c  |    1 +\n rerere.c           |   15 ++++++++++-----\n 5 files changed, 19 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 1994be9..a0c00d3 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -150,6 +150,10 @@ static int checkout_merged(int pos, struct checkout *state)\n \tread_mmblob(&ours, active_cache[pos+1]->sha1);\n \tread_mmblob(&theirs, active_cache[pos+2]->sha1);\n \n+\t/*\n+\t * NEEDSWORK: re-create conflicts from merges with\n+\t * merge.renormalize set, too\n+\t */\n \tstatus = ll_merge(&result_buf, path, &ancestor, \"base\",\n \t\t\t  &ours, \"ours\", &theirs, \"theirs\", 0);\n \tfree(ancestor.ptr);\ndiff --git a/ll-merge.c b/ll-merge.c\nindex 290f764..6bb3095 100644\n--- a/ll-merge.c\n+++ b/ll-merge.c\n@@ -99,8 +99,8 @@ static int ll_union_merge(const struct ll_merge_driver *drv_unused,\n \t\t\t  int flag, int marker_size)\n {\n \t/* Use union favor */\n-\tflag = (flag & LL_OPT_VIRTUAL_ANCESTOR) |\n-\t       create_ll_flag(XDL_MERGE_FAVOR_UNION);\n+\tflag &= ~LL_OPT_FAVOR_MASK;\n+\tflag |= create_ll_flag(XDL_MERGE_FAVOR_UNION);\n \treturn ll_xdl_merge(drv_unused, result, path_unused,\n \t\t\t    orig, NULL, src1, NULL, src2, NULL,\n \t\t\t    flag, marker_size);\n@@ -345,7 +345,7 @@ int ll_merge(mmbuffer_t *result_buf,\n \tconst struct ll_merge_driver *driver;\n \tint virtual_ancestor = flag & LL_OPT_VIRTUAL_ANCESTOR;\n \n-\tif (merge_renormalize) {\n+\tif (flag & LL_OPT_RENORMALIZE) {\n \t\tnormalize_file(ancestor, path);\n \t\tnormalize_file(ours, path);\n \t\tnormalize_file(theirs, path);\ndiff --git a/ll-merge.h b/ll-merge.h\nindex 5990271..ff7ca87 100644\n--- a/ll-merge.h\n+++ b/ll-merge.h\n@@ -8,6 +8,7 @@\n #define LL_OPT_VIRTUAL_ANCESTOR\t(1 << 0)\n #define LL_OPT_FAVOR_MASK\t((1 << 1) | (1 << 2))\n #define LL_OPT_FAVOR_SHIFT 1\n+#define LL_OPT_RENORMALIZE\t(1 << 3)\n \n static inline int ll_opt_favor(int flag)\n {\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex c0c9f0c..23f7a4d 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -648,6 +648,7 @@ static int merge_3way(struct merge_options *o,\n \tmerge_status = ll_merge(result_buf, a->path, &orig, base_name,\n \t\t\t\t&src1, name1, &src2, name2,\n \t\t\t\t((o->call_depth ? LL_OPT_VIRTUAL_ANCESTOR : 0) |\n+\t\t\t\t (o->renormalize ? LL_OPT_RENORMALIZE : 0) |\n \t\t\t\t create_ll_flag(favor)));\n \n \tfree(name1);\ndiff --git a/rerere.c b/rerere.c\nindex 2197890..9dd4c7e 100644\n--- a/rerere.c\n+++ b/rerere.c\n@@ -319,6 +319,10 @@ static int handle_cache(const char *path, unsigned char *sha1, const char *outpu\n \t\tif (!mmfile[i].ptr && !mmfile[i].size)\n \t\t\tmmfile[i].ptr = xstrdup(\"\");\n \t}\n+\t/*\n+\t * NEEDSWORK: handle conflicts from merges with\n+\t * merge.renormalize set, too\n+\t */\n \tll_merge(&result, path, &mmfile[0], NULL,\n \t\t &mmfile[1], \"ours\",\n \t\t &mmfile[2], \"theirs\", 0);\n@@ -361,7 +365,7 @@ static int find_conflict(struct string_list *conflict)\n \treturn 0;\n }\n \n-static int merge(const char *name, const char *path)\n+static int merge(const char *name, int renormalize, const char *path)\n {\n \tint ret;\n \tmmfile_t cur = {NULL, 0}, base = {NULL, 0}, other = {NULL, 0};\n@@ -376,7 +380,8 @@ static int merge(const char *name, const char *path)\n \t\tret = 1;\n \t\tgoto out;\n \t}\n-\tret = ll_merge(&result, path, &base, NULL, &cur, \"\", &other, \"\", 0);\n+\tret = ll_merge(&result, path, &base, NULL, &cur, \"\", &other, \"\",\n+\t\t\trenormalize ? LL_OPT_RENORMALIZE : 0);\n \tif (!ret) {\n \t\tFILE *f = fopen(path, \"w\");\n \t\tif (!f)\n@@ -424,7 +429,7 @@ static int update_paths(struct string_list *update)\n \treturn status;\n }\n \n-static int do_plain_rerere(struct string_list *rr, int fd)\n+static int do_plain_rerere(struct string_list *rr, int fd, int renormalize)\n {\n \tstruct string_list conflict = { NULL, 0, 0, 1 };\n \tstruct string_list update = { NULL, 0, 0, 1 };\n@@ -469,7 +474,7 @@ static int do_plain_rerere(struct string_list *rr, int fd)\n \t\tconst char *name = (const char *)rr->items[i].util;\n \n \t\tif (has_rerere_resolution(name)) {\n-\t\t\tif (!merge(name, path)) {\n+\t\t\tif (!merge(name, renormalize, path)) {\n \t\t\t\tif (rerere_autoupdate)\n \t\t\t\t\tstring_list_insert(path, &update);\n \t\t\t\tfprintf(stderr,\n@@ -553,7 +558,7 @@ int rerere(int flags)\n \tfd = setup_rerere(&merge_rr, flags);\n \tif (fd < 0)\n \t\treturn 0;\n-\treturn do_plain_rerere(&merge_rr, fd);\n+\treturn do_plain_rerere(&merge_rr, fd, merge_renormalize);\n }\n \n static int rerere_forget_one_path(const char *path, struct string_list *rr)\n-- \n1.7.2.1.544.ga752d.dirty\n"},{"id":"147173","messageId":"20100805112534.GK13779@burratino","threadId":"24269","inReplyTo":"20100805110822.GB13779@burratino","subject":"[PATCH 09/12] t4200 (rerere): modernize style","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-08-05T11:25:34Z","receivedAt":"2010-08-05T11:25:34Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Guard all test code with test_expect_success to make the\nscript easier to follow.  While at it, pick some other nits:\n\n - use test_tick (more than we have to, to be realistic);\n\n - 'single quotes' and \\escaped HERE documents where possible\n   simplify review for escaping problems;\n\n - omit whitespace after >redirection operators for\n   consistency with other tests;\n\n - use \"update-index --refresh\" instead of testing that\n   \"ls-files -u\" output is empty, since the former produces\n   nicer output on failure;\n\n - compare to expected nonempty \"ls-files -u\" output instead\n   of counting lines when it is expected to be nonempty.\n\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n t/t4200-rerere.sh |  303 +++++++++++++++++++++++++++++++----------------------\n 1 files changed, 179 insertions(+), 124 deletions(-)\n\ndiff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh\nindex 70856d0..3ed4d1a 100755\n--- a/t/t4200-rerere.sh\n+++ b/t/t4200-rerere.sh\n@@ -4,237 +4,292 @@\n #\n \n test_description='git rerere\n+\n+! [fifth] version1\n+ ! [first] first\n+  ! [fourth] version1\n+   ! [master] initial\n+    ! [second] prefer first over second\n+     ! [third] version2\n+------\n+     + [third] version2\n++      [fifth] version1\n+  +    [fourth] version1\n++ +  + [third^] third\n+    -  [second] prefer first over second\n+ +  +  [first] first\n+    +  [second^] second\n+++++++ [master] initial\n '\n \n . ./test-lib.sh\n \n-test_expect_success 'setup' \"\n-\tcat > a1 <<- EOF &&\n+test_expect_success 'setup' '\n+\tcat >a1 <<-\\EOF &&\n \tSome title\n \t==========\n-\tWhether 'tis nobler in the mind to suffer\n+\tWhether '\\''tis nobler in the mind to suffer\n \tThe slings and arrows of outrageous fortune,\n \tOr to take arms against a sea of troubles,\n \tAnd by opposing end them? To die: to sleep;\n \tNo more; and by a sleep to say we end\n \tThe heart-ache and the thousand natural shocks\n-\tThat flesh is heir to, 'tis a consummation\n-\tDevoutly to be wish'd.\n+\tThat flesh is heir to, '\\''tis a consummation\n+\tDevoutly to be wish'\\''d.\n \tEOF\n \n \tgit add a1 &&\n+\ttest_tick &&\n \tgit commit -q -a -m initial &&\n \n-\tgit checkout -b first &&\n-\tcat >> a1 <<- EOF &&\n+\tcat >>a1 <<-\\EOF &&\n \tSome title\n \t==========\n \tTo die, to sleep;\n-\tTo sleep: perchance to dream: ay, there's the rub;\n+\tTo sleep: perchance to dream: ay, there'\\''s the rub;\n \tFor in that sleep of death what dreams may come\n \tWhen we have shuffled off this mortal coil,\n-\tMust give us pause: there's the respect\n+\tMust give us pause: there'\\''s the respect\n \tThat makes calamity of so long life;\n \tEOF\n+\n+\tgit checkout -b first &&\n+\ttest_tick &&\n \tgit commit -q -a -m first &&\n \n \tgit checkout -b second master &&\n \tgit show first:a1 |\n-\tsed -e 's/To die, t/To die! T/' -e 's/Some title/Some Title/' > a1 &&\n-\techo '* END *' >>a1 &&\n+\tsed -e \"s/To die, t/To die! T/\" -e \"s/Some title/Some Title/\" >a1 &&\n+\techo \"* END *\" >>a1 &&\n+\ttest_tick &&\n \tgit commit -q -a -m second\n-\"\n+'\n \n test_expect_success 'nothing recorded without rerere' '\n-\t(rm -rf .git/rr-cache; git config rerere.enabled false) &&\n+\trm -rf .git/rr-cache &&\n+\tgit config rerere.enabled false &&\n \ttest_must_fail git merge first &&\n \t! test -d .git/rr-cache\n '\n \n-# activate rerere, old style\n-test_expect_success 'conflicting merge' '\n+test_expect_success 'activate rerere, old style (conflicting merge)' '\n \tgit reset --hard &&\n \tmkdir .git/rr-cache &&\n-\tgit config --unset rerere.enabled &&\n-\ttest_must_fail git merge first\n-'\n+\ttest_might_fail git config --unset rerere.enabled &&\n+\ttest_must_fail git merge first &&\n \n-sha1=$(perl -pe 's/\t.*//' .git/MERGE_RR)\n-rr=.git/rr-cache/$sha1\n-test_expect_success 'recorded preimage' \"grep ^=======$ $rr/preimage\"\n+\tsha1=$(perl -pe \"s/\t.*//\" .git/MERGE_RR) &&\n+\trr=.git/rr-cache/$sha1 &&\n+\tgrep \"^=======\\$\" $rr/preimage &&\n+\t! test -f $rr/postimage &&\n+\t! test -f $rr/thisimage\n+'\n \n test_expect_success 'rerere.enabled works, too' '\n \trm -rf .git/rr-cache &&\n \tgit config rerere.enabled true &&\n \tgit reset --hard &&\n \ttest_must_fail git merge first &&\n+\n+\tsha1=$(perl -pe \"s/\t.*//\" .git/MERGE_RR) &&\n+\trr=.git/rr-cache/$sha1 &&\n \tgrep ^=======$ $rr/preimage\n '\n \n-test_expect_success 'no postimage or thisimage yet' \\\n-\t\"test ! -f $rr/postimage -a ! -f $rr/thisimage\"\n+test_expect_success 'set up rr-cache' '\n+\trm -rf .git/rr-cache &&\n+\tgit config rerere.enabled true &&\n+\tgit reset --hard &&\n+\ttest_must_fail git merge first &&\n+\tsha1=$(perl -pe \"s/\t.*//\" .git/MERGE_RR) &&\n+\trr=.git/rr-cache/$sha1\n+'\n \n-test_expect_success 'preimage has right number of lines' '\n+test_expect_success 'rr-cache looks sane' '\n+\t# no postimage or thisimage yet\n+\t! test -f $rr/postimage &&\n+\t! test -f $rr/thisimage &&\n \n+\t# preimage has right number of lines\n \tcnt=$(sed -ne \"/^<<<<<<</,/^>>>>>>>/p\" $rr/preimage | wc -l) &&\n+\techo $cnt &&\n \ttest $cnt = 13\n-\n '\n \n-git show first:a1 > a1\n-\n-cat > expect << EOF\n---- a/a1\n-+++ b/a1\n-@@ -1,4 +1,4 @@\n--Some Title\n-+Some title\n- ==========\n- Whether 'tis nobler in the mind to suffer\n- The slings and arrows of outrageous fortune,\n-@@ -8,21 +8,11 @@\n- The heart-ache and the thousand natural shocks\n- That flesh is heir to, 'tis a consummation\n- Devoutly to be wish'd.\n--<<<<<<<\n--Some Title\n--==========\n--To die! To sleep;\n--=======\n- Some title\n- ==========\n- To die, to sleep;\n-->>>>>>>\n- To sleep: perchance to dream: ay, there's the rub;\n- For in that sleep of death what dreams may come\n- When we have shuffled off this mortal coil,\n- Must give us pause: there's the respect\n- That makes calamity of so long life;\n--<<<<<<<\n--=======\n--* END *\n-->>>>>>>\n-EOF\n-git rerere diff > out\n-\n-test_expect_success 'rerere diff' 'test_cmp expect out'\n-\n-cat > expect << EOF\n-a1\n-EOF\n-\n-git rerere status > out\n+test_expect_success 'rerere diff' '\n+\tgit show first:a1 >a1 &&\n+\tcat >expect <<-\\EOF &&\n+\t--- a/a1\n+\t+++ b/a1\n+\t@@ -1,4 +1,4 @@\n+\t-Some Title\n+\t+Some title\n+\t ==========\n+\t Whether '\\''tis nobler in the mind to suffer\n+\t The slings and arrows of outrageous fortune,\n+\t@@ -8,21 +8,11 @@\n+\t The heart-ache and the thousand natural shocks\n+\t That flesh is heir to, '\\''tis a consummation\n+\t Devoutly to be wish'\\''d.\n+\t-<<<<<<<\n+\t-Some Title\n+\t-==========\n+\t-To die! To sleep;\n+\t-=======\n+\t Some title\n+\t ==========\n+\t To die, to sleep;\n+\t->>>>>>>\n+\t To sleep: perchance to dream: ay, there'\\''s the rub;\n+\t For in that sleep of death what dreams may come\n+\t When we have shuffled off this mortal coil,\n+\t Must give us pause: there'\\''s the respect\n+\t That makes calamity of so long life;\n+\t-<<<<<<<\n+\t-=======\n+\t-* END *\n+\t->>>>>>>\n+\tEOF\n+\tgit rerere diff >out &&\n+\ttest_cmp expect out\n+'\n \n-test_expect_success 'rerere status' 'test_cmp expect out'\n+test_expect_success 'rerere status' '\n+\techo a1 >expect &&\n+\tgit rerere status >out &&\n+\ttest_cmp expect out\n+'\n \n-test_expect_success 'commit succeeds' \\\n-\t\"git commit -q -a -m 'prefer first over second'\"\n+test_expect_success 'first postimage wins' '\n+\tgit show first:a1 | sed \"s/To die: t/To die! T/\" >expect &&\n \n-test_expect_success 'recorded postimage' \"test -f $rr/postimage\"\n+\tgit commit -q -a -m \"prefer first over second\" &&\n+\ttest -f $rr/postimage &&\n \n-test_expect_success 'another conflicting merge' '\n \tgit checkout -b third master &&\n-\tgit show second^:a1 | sed \"s/To die: t/To die! T/\" > a1 &&\n+\tgit show second^:a1 | sed \"s/To die: t/To die! T/\" >a1 &&\n \tgit commit -q -a -m third &&\n-\ttest_must_fail git pull . first\n-'\n-\n-git show first:a1 | sed 's/To die: t/To die! T/' > expect\n-test_expect_success 'rerere kicked in' \"! grep ^=======$ a1\"\n-\n-test_expect_success 'rerere prefers first change' 'test_cmp a1 expect'\n-\n-rm $rr/postimage\n-echo \"$sha1\ta1\" | perl -pe 'y/\\012/\\000/' > .git/MERGE_RR\n-\n-test_expect_success 'rerere clear' 'git rerere clear'\n-\n-test_expect_success 'clear removed the directory' \"test ! -d $rr\"\n \n-mkdir $rr\n-echo Hello > $rr/preimage\n-echo World > $rr/postimage\n-\n-sha2=4000000000000000000000000000000000000000\n-rr2=.git/rr-cache/$sha2\n-mkdir $rr2\n-echo Hello > $rr2/preimage\n+\ttest_must_fail git pull . first &&\n+\t# rerere kicked in\n+\t! grep \"^=======\\$\" a1 &&\n+\ttest_cmp expect a1\n+'\n \n-almost_15_days_ago=$((60-15*86400))\n-just_over_15_days_ago=$((-1-15*86400))\n-almost_60_days_ago=$((60-60*86400))\n-just_over_60_days_ago=$((-1-60*86400))\n+test_expect_success 'rerere clear' '\n+\trm $rr/postimage &&\n+\techo \"$sha1\ta1\" | perl -pe \"y/\\012/\\000/\" >.git/MERGE_RR &&\n+\tgit rerere clear &&\n+\t! test -d $rr\n+'\n \n-test-chmtime =$almost_60_days_ago $rr/preimage\n-test-chmtime =$almost_15_days_ago $rr2/preimage\n+test_expect_success 'set up for garbage collection tests' '\n+\tmkdir -p $rr &&\n+\techo Hello >$rr/preimage &&\n+\techo World >$rr/postimage &&\n \n-test_expect_success 'garbage collection (part1)' 'git rerere gc'\n+\tsha2=4000000000000000000000000000000000000000 &&\n+\trr2=.git/rr-cache/$sha2 &&\n+\tmkdir $rr2 &&\n+\techo Hello >$rr2/preimage &&\n \n-test_expect_success 'young records still live' \\\n-\t\"test -f $rr/preimage && test -f $rr2/preimage\"\n+\talmost_15_days_ago=$((60-15*86400)) &&\n+\tjust_over_15_days_ago=$((-1-15*86400)) &&\n+\talmost_60_days_ago=$((60-60*86400)) &&\n+\tjust_over_60_days_ago=$((-1-60*86400)) &&\n \n-test-chmtime =$just_over_60_days_ago $rr/preimage\n-test-chmtime =$just_over_15_days_ago $rr2/preimage\n+\ttest-chmtime =$almost_60_days_ago $rr/preimage &&\n+\ttest-chmtime =$almost_15_days_ago $rr2/preimage\n+'\n \n-test_expect_success 'garbage collection (part2)' 'git rerere gc'\n+test_expect_success 'garbage collection preserves young records' '\n+\tgit rerere gc &&\n+\ttest -f $rr/preimage &&\n+\ttest -f $rr2/preimage\n+'\n \n-test_expect_success 'old records rest in peace' \\\n-\t\"test ! -f $rr/preimage && test ! -f $rr2/preimage\"\n+test_expect_success 'old records rest in peace' '\n+\ttest-chmtime =$just_over_60_days_ago $rr/preimage &&\n+\ttest-chmtime =$just_over_15_days_ago $rr2/preimage &&\n+\tgit rerere gc &&\n+\t! test -f $rr/preimage &&\n+\t! test -f $rr2/preimage\n+'\n \n-test_expect_success 'file2 added differently in two branches' '\n+test_expect_success 'setup: file2 added differently in two branches' '\n \tgit reset --hard &&\n+\n \tgit checkout -b fourth &&\n-\techo Hallo > file2 &&\n+\techo Hallo >file2 &&\n \tgit add file2 &&\n+\ttest_tick &&\n \tgit commit -m version1 &&\n+\n \tgit checkout third &&\n-\techo Bello > file2 &&\n+\techo Bello >file2 &&\n \tgit add file2 &&\n+\ttest_tick &&\n \tgit commit -m version2 &&\n+\n \ttest_must_fail git merge fourth &&\n-\techo Cello > file2 &&\n+\techo Cello >file2 &&\n \tgit add file2 &&\n \tgit commit -m resolution\n '\n \n test_expect_success 'resolution was recorded properly' '\n+\techo Cello >expected &&\n+\n \tgit reset --hard HEAD~2 &&\n \tgit checkout -b fifth &&\n-\techo Hallo > file3 &&\n+\n+\techo Hallo >file3 &&\n \tgit add file3 &&\n+\ttest_tick &&\n \tgit commit -m version1 &&\n+\n \tgit checkout third &&\n-\techo Bello > file3 &&\n+\techo Bello >file3 &&\n \tgit add file3 &&\n+\ttest_tick &&\n \tgit commit -m version2 &&\n \tgit tag version2 &&\n+\n \ttest_must_fail git merge fifth &&\n-\ttest Cello = \"$(cat file3)\" &&\n-\ttest 0 != $(git ls-files -u | wc -l)\n+\ttest_cmp expected file3 &&\n+\ttest_must_fail git update-index --refresh\n '\n \n test_expect_success 'rerere.autoupdate' '\n-\tgit config rerere.autoupdate true\n+\tgit config rerere.autoupdate true &&\n \tgit reset --hard &&\n \tgit checkout version2 &&\n \ttest_must_fail git merge fifth &&\n-\ttest 0 = $(git ls-files -u | wc -l)\n+\tgit update-index --refresh\n '\n \n test_expect_success 'merge --rerere-autoupdate' '\n-\tgit config --unset rerere.autoupdate\n+\ttest_might_fail git config --unset rerere.autoupdate &&\n \tgit reset --hard &&\n \tgit checkout version2 &&\n \ttest_must_fail git merge --rerere-autoupdate fifth &&\n-\ttest 0 = $(git ls-files -u | wc -l)\n+\tgit update-index --refresh\n '\n \n test_expect_success 'merge --no-rerere-autoupdate' '\n-\tgit config rerere.autoupdate true\n+\theadblob=$(git rev-parse version2:file3) &&\n+\tmergeblob=$(git rev-parse fifth:file3) &&\n+\tcat >expected <<-EOF &&\n+\t100644 $headblob 2\tfile3\n+\t100644 $mergeblob 3\tfile3\n+\tEOF\n+\n+\tgit config rerere.autoupdate true &&\n \tgit reset --hard &&\n \tgit checkout version2 &&\n \ttest_must_fail git merge --no-rerere-autoupdate fifth &&\n-\ttest 2 = $(git ls-files -u | wc -l)\n+\tgit ls-files -u >actual &&\n+\ttest_cmp expected actual\n '\n \n test_done\n-- \n1.7.2.1.544.ga752d.dirty\n"},{"id":"147174","messageId":"20100805112837.GL13779@burratino","threadId":"24269","inReplyTo":"20100805110822.GB13779@burratino","subject":"[PATCH 10/12] rerere: migrate to parse-options API","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-08-05T11:28:37Z","receivedAt":"2010-08-05T11:28:37Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\nThe tests don’t pass for me with the old implementation; I think \"2 < argc\"\nshould have been \"2 <= argc\".  Well, no use dwelling in the past.\n\n builtin/rerere.c  |   52 ++++++++++++++++--------------\n t/t4200-rerere.sh |   91 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 119 insertions(+), 24 deletions(-)\n\ndiff --git a/builtin/rerere.c b/builtin/rerere.c\nindex 0048f9e..295fe75 100644\n--- a/builtin/rerere.c\n+++ b/builtin/rerere.c\n@@ -1,13 +1,16 @@\n #include \"builtin.h\"\n #include \"cache.h\"\n #include \"dir.h\"\n+#include \"parse-options.h\"\n #include \"string-list.h\"\n #include \"rerere.h\"\n #include \"xdiff/xdiff.h\"\n #include \"xdiff-interface.h\"\n \n-static const char git_rerere_usage[] =\n-\"git rerere [clear | status | diff | gc]\";\n+static const char * const rerere_usage[] = {\n+\t\"git rerere [clear | status | diff | gc]\",\n+\tNULL,\n+};\n \n /* these values are days */\n static int cutoff_noresolve = 15;\n@@ -103,25 +106,26 @@ static int diff_two(const char *file1, const char *label1,\n int cmd_rerere(int argc, const char **argv, const char *prefix)\n {\n \tstruct string_list merge_rr = { NULL, 0, 0, 1 };\n-\tint i, fd, flags = 0;\n-\n-\tif (2 < argc) {\n-\t\tif (!strcmp(argv[1], \"-h\"))\n-\t\t\tusage(git_rerere_usage);\n-\t\tif (!strcmp(argv[1], \"--rerere-autoupdate\"))\n-\t\t\tflags = RERERE_AUTOUPDATE;\n-\t\telse if (!strcmp(argv[1], \"--no-rerere-autoupdate\"))\n-\t\t\tflags = RERERE_NOAUTOUPDATE;\n-\t\tif (flags) {\n-\t\t\targc--;\n-\t\t\targv++;\n-\t\t}\n-\t}\n-\tif (argc < 2)\n+\tint i, fd, autoupdate = -1, flags = 0;\n+\n+\tstruct option options[] = {\n+\t\tOPT_SET_INT(0, \"rerere-autoupdate\", &autoupdate,\n+\t\t\t\"register clean resolutions in index\", 1),\n+\t\tOPT_END(),\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, rerere_usage, 0);\n+\n+\tif (autoupdate == 1)\n+\t\tflags = RERERE_AUTOUPDATE;\n+\tif (autoupdate == 0)\n+\t\tflags = RERERE_NOAUTOUPDATE;\n+\n+\tif (argc < 1)\n \t\treturn rerere(flags);\n \n-\tif (!strcmp(argv[1], \"forget\")) {\n-\t\tconst char **pathspec = get_pathspec(prefix, argv + 2);\n+\tif (!strcmp(argv[0], \"forget\")) {\n+\t\tconst char **pathspec = get_pathspec(prefix, argv + 1);\n \t\treturn rerere_forget(pathspec);\n \t}\n \n@@ -129,26 +133,26 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)\n \tif (fd < 0)\n \t\treturn 0;\n \n-\tif (!strcmp(argv[1], \"clear\")) {\n+\tif (!strcmp(argv[0], \"clear\")) {\n \t\tfor (i = 0; i < merge_rr.nr; i++) {\n \t\t\tconst char *name = (const char *)merge_rr.items[i].util;\n \t\t\tif (!has_rerere_resolution(name))\n \t\t\t\tunlink_rr_item(name);\n \t\t}\n \t\tunlink_or_warn(git_path(\"rr-cache/MERGE_RR\"));\n-\t} else if (!strcmp(argv[1], \"gc\"))\n+\t} else if (!strcmp(argv[0], \"gc\"))\n \t\tgarbage_collect(&merge_rr);\n-\telse if (!strcmp(argv[1], \"status\"))\n+\telse if (!strcmp(argv[0], \"status\"))\n \t\tfor (i = 0; i < merge_rr.nr; i++)\n \t\t\tprintf(\"%s\\n\", merge_rr.items[i].string);\n-\telse if (!strcmp(argv[1], \"diff\"))\n+\telse if (!strcmp(argv[0], \"diff\"))\n \t\tfor (i = 0; i < merge_rr.nr; i++) {\n \t\t\tconst char *path = merge_rr.items[i].string;\n \t\t\tconst char *name = (const char *)merge_rr.items[i].util;\n \t\t\tdiff_two(rerere_path(name, \"preimage\"), path, path, path);\n \t\t}\n \telse\n-\t\tusage(git_rerere_usage);\n+\t\tusage_with_options(rerere_usage, options);\n \n \tstring_list_clear(&merge_rr, 1);\n \treturn 0;\ndiff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh\nindex 3ed4d1a..876f09a 100755\n--- a/t/t4200-rerere.sh\n+++ b/t/t4200-rerere.sh\n@@ -292,4 +292,95 @@ test_expect_success 'merge --no-rerere-autoupdate' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'set up an unresolved merge' '\n+\theadblob=$(git rev-parse version2:file3) &&\n+\tmergeblob=$(git rev-parse fifth:file3) &&\n+\tcat >expected.unresolved <<-EOF &&\n+\t100644 $headblob 2\tfile3\n+\t100644 $mergeblob 3\tfile3\n+\tEOF\n+\n+\ttest_might_fail git config --unset rerere.autoupdate &&\n+\tgit reset --hard &&\n+\tgit checkout version2 &&\n+\tfifth=$(git rev-parse fifth) &&\n+\techo \"$fifth\t\tbranch 'fifth' of .\" |\n+\tgit fmt-merge-msg >msg &&\n+\tancestor=$(git merge-base version2 fifth) &&\n+\ttest_must_fail git merge-recursive \"$ancestor\" -- HEAD fifth &&\n+\n+\tgit ls-files --stage >failedmerge &&\n+\tcp file3 file3.conflict &&\n+\n+\tgit ls-files -u >actual &&\n+\ttest_cmp expected.unresolved actual\n+'\n+\n+test_expect_success 'explicit rerere' '\n+\ttest_might_fail git config --unset rerere.autoupdate &&\n+\tgit rm -fr --cached . &&\n+\tgit update-index --index-info <failedmerge &&\n+\tcp file3.conflict file3 &&\n+\ttest_must_fail git update-index --refresh -q &&\n+\n+\tgit rerere &&\n+\tgit ls-files -u >actual &&\n+\ttest_cmp expected.unresolved actual\n+'\n+\n+test_expect_success 'explicit rerere with autoupdate' '\n+\tgit config rerere.autoupdate true &&\n+\tgit rm -fr --cached . &&\n+\tgit update-index --index-info <failedmerge &&\n+\tcp file3.conflict file3 &&\n+\ttest_must_fail git update-index --refresh -q &&\n+\n+\tgit rerere &&\n+\tgit update-index --refresh\n+'\n+\n+test_expect_success 'explicit rerere --rerere-autoupdate overrides' '\n+\tgit config rerere.autoupdate false &&\n+\tgit rm -fr --cached . &&\n+\tgit update-index --index-info <failedmerge &&\n+\tcp file3.conflict file3 &&\n+\tgit rerere &&\n+\tgit ls-files -u >actual1 &&\n+\n+\tgit rm -fr --cached . &&\n+\tgit update-index --index-info <failedmerge &&\n+\tcp file3.conflict file3 &&\n+\tgit rerere --rerere-autoupdate &&\n+\tgit update-index --refresh &&\n+\n+\tgit rm -fr --cached . &&\n+\tgit update-index --index-info <failedmerge &&\n+\tcp file3.conflict file3 &&\n+\tgit rerere --rerere-autoupdate --no-rerere-autoupdate &&\n+\tgit ls-files -u >actual2 &&\n+\n+\tgit rm -fr --cached . &&\n+\tgit update-index --index-info <failedmerge &&\n+\tcp file3.conflict file3 &&\n+\tgit rerere --rerere-autoupdate --no-rerere-autoupdate --rerere-autoupdate &&\n+\tgit update-index --refresh &&\n+\n+\ttest_cmp expected.unresolved actual1 &&\n+\ttest_cmp expected.unresolved actual2\n+'\n+\n+test_expect_success 'rerere --no-no-rerere-autoupdate' '\n+\tgit rm -fr --cached . &&\n+\tgit update-index --index-info <failedmerge &&\n+\tcp file3.conflict file3 &&\n+\ttest_must_fail git rerere --no-no-rerere-autoupdate 2>err &&\n+\tgrep [Uu]sage err &&\n+\ttest_must_fail git update-index --refresh\n+'\n+\n+test_expect_success 'rerere -h' '\n+\ttest_must_fail git rerere -h >help &&\n+\tgrep [Uu]sage help\n+'\n+\n test_done\n-- \n1.7.2.1.544.ga752d.dirty\n"},{"id":"147175","messageId":"20100805113026.GM13779@burratino","threadId":"24269","inReplyTo":"20100805110822.GB13779@burratino","subject":"[PATCH 11/12] rerere: never renormalize","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-08-05T11:30:26Z","receivedAt":"2010-08-05T11:30:26Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"plain rerere performs three tasks; let us consider how the new\nmerge.renormalize option should apply to each.\n\nAfter an unsuccessful merge, rerere records conflict hunks from the\nwork tree under .git/rr-cache.  If the merge was performed with\nmerge.renormalize enabled, both sides of the conflict hunk use the\ncurrent work tree’s end-of-line and smudge rules; there is not really\nmuch of a choice.\n\nAfter a successful manual resolution, rerere records the postimage.\nHere, also, the file will be in the current work tree’s canonical\nformat and there is not much to do about it.\n\nWhen encountering that conflict again, merge looks up the preimage\nand postimage using the conflict hunk as a key and runs a three-way\nmerge to apply that resolution to the work tree.  Since the conflict\nhunk used the current work tree’s canonical format, chances are the\nversion in the work tree, the preimage, and the postimage will, too.\nIn fact using the merge.renormalize machinery is exactly the wrong\nthing to do, since its result has been run through convert_to_git\nand therefore is not suitable for writing to the work tree.\n\nThe only affected caller is \"git merge\".\n\nNEEDSWORK: lacks test\n\nCc: Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n rerere.c |   11 +++++------\n 1 files changed, 5 insertions(+), 6 deletions(-)\n\ndiff --git a/rerere.c b/rerere.c\nindex 9dd4c7e..e40af0d 100644\n--- a/rerere.c\n+++ b/rerere.c\n@@ -365,7 +365,7 @@ static int find_conflict(struct string_list *conflict)\n \treturn 0;\n }\n \n-static int merge(const char *name, int renormalize, const char *path)\n+static int merge(const char *name, const char *path)\n {\n \tint ret;\n \tmmfile_t cur = {NULL, 0}, base = {NULL, 0}, other = {NULL, 0};\n@@ -380,8 +380,7 @@ static int merge(const char *name, int renormalize, const char *path)\n \t\tret = 1;\n \t\tgoto out;\n \t}\n-\tret = ll_merge(&result, path, &base, NULL, &cur, \"\", &other, \"\",\n-\t\t\trenormalize ? LL_OPT_RENORMALIZE : 0);\n+\tret = ll_merge(&result, path, &base, NULL, &cur, \"\", &other, \"\", 0);\n \tif (!ret) {\n \t\tFILE *f = fopen(path, \"w\");\n \t\tif (!f)\n@@ -429,7 +428,7 @@ static int update_paths(struct string_list *update)\n \treturn status;\n }\n \n-static int do_plain_rerere(struct string_list *rr, int fd, int renormalize)\n+static int do_plain_rerere(struct string_list *rr, int fd)\n {\n \tstruct string_list conflict = { NULL, 0, 0, 1 };\n \tstruct string_list update = { NULL, 0, 0, 1 };\n@@ -474,7 +473,7 @@ static int do_plain_rerere(struct string_list *rr, int fd, int renormalize)\n \t\tconst char *name = (const char *)rr->items[i].util;\n \n \t\tif (has_rerere_resolution(name)) {\n-\t\t\tif (!merge(name, renormalize, path)) {\n+\t\t\tif (!merge(name, path)) {\n \t\t\t\tif (rerere_autoupdate)\n \t\t\t\t\tstring_list_insert(path, &update);\n \t\t\t\tfprintf(stderr,\n@@ -558,7 +557,7 @@ int rerere(int flags)\n \tfd = setup_rerere(&merge_rr, flags);\n \tif (fd < 0)\n \t\treturn 0;\n-\treturn do_plain_rerere(&merge_rr, fd, merge_renormalize);\n+\treturn do_plain_rerere(&merge_rr, fd);\n }\n \n static int rerere_forget_one_path(const char *path, struct string_list *rr)\n-- \n1.7.2.1.544.ga752d.dirty\n"},{"id":"147176","messageId":"20100805113240.GN13779@burratino","threadId":"24269","inReplyTo":"20100805110822.GB13779@burratino","subject":"[PATCH 12/12] merge-recursive --renormalize","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-08-05T11:32:41Z","receivedAt":"2010-08-05T11:32:41Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Teach \"git merge-recursive\" a --renormalize option to enable the\nmerge.renormalize configuration.  The --no-renormalize option can\nbe used to override it in the negative.\n\nSo in the future, you might be able to, e.g.:\n\n\tgit checkout -m -Xrenormalize otherbranch\n\nor\n\n\tgit revert -Xrenormalize otherpatch\n\nor\n\n\tgit pull --rebase -Xrenormalize\n\nThe bad part: merge.renormalize is still not honored for most\ncommands.  And it reveals lots of places that -X has not been plumbed\nin (so we get \"git merge -Xrenormalize\" but not much else).\n\nNEEDSWORK: tests\n\nCc: Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n - Commit message is rewritten\n - Introduce --no-renormalize, too\n - Adapt to rerere’s new lack of support for --renormalize\n\nThat’s it for this round.  Thanks again.\n\n Documentation/merge-strategies.txt |   12 ++++++++++++\n builtin/checkout.c                 |    7 +++++++\n builtin/merge-recursive.c          |    4 ++++\n builtin/merge.c                    |   19 ++++++++++++++-----\n builtin/revert.c                   |    7 +++++++\n cache.h                            |    1 -\n environment.c                      |    1 -\n merge-recursive.c                  |    2 +-\n 8 files changed, 45 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/merge-strategies.txt b/Documentation/merge-strategies.txt\nindex a5bc1db..049313d 100644\n--- a/Documentation/merge-strategies.txt\n+++ b/Documentation/merge-strategies.txt\n@@ -40,6 +40,18 @@ the other tree did, declaring 'our' history contains all that happened in it.\n theirs;;\n \tThis is opposite of 'ours'.\n \n+renormalize;;\n+\tThis runs a virtual check-out and check-in of all three stages\n+\tof a file when resolving a three-way merge.  This option is\n+\tmeant to be used when merging branches with different clean\n+\tfilters or end-of-line normalization rules.  See \"Merging\n+\tbranches with differing checkin/checkout attributes\" in\n+\tlinkgit:gitattributes[5] for details.\n+\n+no-renormalize;;\n+\tDisables the `renormalize` option.  This overrides the\n+\t`merge.renormalize` configuration variable.\n+\n subtree[=path];;\n \tThis option is a more advanced form of 'subtree' strategy, where\n \tthe strategy makes a guess on how two trees must be shifted to\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex a0c00d3..24b67d5 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -437,6 +437,13 @@ static int merge_working_tree(struct checkout_opts *opts,\n \t\t\t */\n \n \t\t\tadd_files_to_cache(NULL, NULL, 0);\n+\t\t\t/*\n+\t\t\t * NEEDSWORK: carrying over local changes\n+\t\t\t * when branches have different end-of-line\n+\t\t\t * normalization (or clean+smudge rules) is\n+\t\t\t * a pain; plumb in an option to set\n+\t\t\t * o.renormalize?\n+\t\t\t */\n \t\t\tinit_merge_options(&o);\n \t\t\to.verbosity = 0;\n \t\t\twork = write_tree_from_memory(&o);\ndiff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c\nindex d8875d5..c2d4677 100644\n--- a/builtin/merge-recursive.c\n+++ b/builtin/merge-recursive.c\n@@ -45,6 +45,10 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)\n \t\t\t\to.subtree_shift = \"\";\n \t\t\telse if (!prefixcmp(arg+2, \"subtree=\"))\n \t\t\t\to.subtree_shift = arg + 10;\n+\t\t\telse if (!strcmp(arg+2, \"renormalize\"))\n+\t\t\t\to.renormalize = 1;\n+\t\t\telse if (!strcmp(arg+2, \"no-renormalize\"))\n+\t\t\t\to.renormalize = 0;\n \t\t\telse\n \t\t\t\tdie(\"Unknown option %s\", arg);\n \t\t\tcontinue;\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex b836e9c..037cd47 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -54,6 +54,7 @@ static size_t use_strategies_nr, use_strategies_alloc;\n static const char **xopts;\n static size_t xopts_nr, xopts_alloc;\n static const char *branch;\n+static int option_renormalize;\n static int verbosity;\n static int allow_rerere_auto;\n \n@@ -503,9 +504,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n \t\treturn git_config_string(&pull_octopus, k, v);\n \telse if (!strcmp(k, \"merge.log\") || !strcmp(k, \"merge.summary\"))\n \t\toption_log = git_config_bool(k, v);\n-\telse if (!strcmp(k, \"merge.renormalize\")) {\n-\t\tmerge_renormalize = git_config_bool(k, v);\n-\t}\n+\telse if (!strcmp(k, \"merge.renormalize\"))\n+\t\toption_renormalize = git_config_bool(k, v);\n \treturn git_diff_ui_config(k, v, cb);\n }\n \n@@ -627,6 +627,11 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,\n \t\tif (!strcmp(strategy, \"subtree\"))\n \t\t\to.subtree_shift = \"\";\n \n+\t\to.renormalize = option_renormalize;\n+\n+\t\t/*\n+\t\t * NEEDSWORK: merge with table in builtin/merge-recursive\n+\t\t */\n \t\tfor (x = 0; x < xopts_nr; x++) {\n \t\t\tif (!strcmp(xopts[x], \"ours\"))\n \t\t\t\to.recursive_variant = MERGE_RECURSIVE_OURS;\n@@ -636,6 +641,10 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,\n \t\t\t\to.subtree_shift = \"\";\n \t\t\telse if (!prefixcmp(xopts[x], \"subtree=\"))\n \t\t\t\to.subtree_shift = xopts[x]+8;\n+\t\t\telse if (!strcmp(xopts[x], \"renormalize\"))\n+\t\t\t\to.renormalize = 1;\n+\t\t\telse if (!strcmp(xopts[x], \"no-renormalize\"))\n+\t\t\t\to.renormalize = 0;\n \t\t\telse\n \t\t\t\tdie(\"Unknown option for merge-recursive: -X%s\", xopts[x]);\n \t\t}\n@@ -819,7 +828,7 @@ static int finish_automerge(struct commit_list *common,\n \treturn 0;\n }\n \n-static int suggest_conflicts(void)\n+static int suggest_conflicts(int renormalizing)\n {\n \tFILE *fp;\n \tint pos;\n@@ -1304,5 +1313,5 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \t\t\t\"stopped before committing as requested\\n\");\n \t\treturn 0;\n \t} else\n-\t\treturn suggest_conflicts();\n+\t\treturn suggest_conflicts(option_renormalize);\n }\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 853e9e4..1113253 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -318,6 +318,13 @@ static void do_recursive_merge(struct commit *base, struct commit *next,\n \tindex_fd = hold_locked_index(&index_lock, 1);\n \n \tread_cache();\n+\n+\t/*\n+\t * NEEDSWORK: cherry-picking between branches with\n+\t * different end-of-line normalization is a pain;\n+\t * plumb in an option to set o.renormalize?\n+\t * (or better: arbitrary -X options)\n+\t */\n \tinit_merge_options(&o);\n \to.ancestor = base ? base_label : \"(empty tree)\";\n \to.branch1 = \"HEAD\";\ndiff --git a/cache.h b/cache.h\nindex ed73da8..aa725b0 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -551,7 +551,6 @@ extern int read_replace_refs;\n extern int fsync_object_files;\n extern int core_preload_index;\n extern int core_apply_sparse_checkout;\n-extern int merge_renormalize;\n \n enum safe_crlf {\n \tSAFE_CRLF_FALSE = 0,\ndiff --git a/environment.c b/environment.c\nindex 81a3682..83d38d3 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -53,7 +53,6 @@ enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;\n char *notes_ref_name;\n int grafts_replace_parents = 1;\n int core_apply_sparse_checkout;\n-int merge_renormalize;\n \n /* Parallel index stat data preload? */\n int core_preload_index = 0;\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 23f7a4d..762b549 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1486,7 +1486,7 @@ void init_merge_options(struct merge_options *o)\n \to->buffer_output = 1;\n \to->diff_rename_limit = -1;\n \to->merge_rename_limit = -1;\n-\to->renormalize = merge_renormalize;\n+\to->renormalize = 0;\n \tgit_config(merge_recursive_config, o);\n \tif (getenv(\"GIT_MERGE_VERBOSITY\"))\n \t\to->verbosity =\n-- \n1.7.2.1.544.ga752d.dirty\n"},{"id":"147177","messageId":"AANLkTikv3oYapOVWmxkt2eqwGWQKMAQOCmruShSiHjKv@mail.gmail.com","threadId":"24269","inReplyTo":"20100805110933.GC13779@burratino","subject":"Re: [PATCH 01/12] t6038 (merge.renormalize): style nitpicks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2010-08-05T11:41:36Z","receivedAt":"2010-08-05T11:41:36Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"On Thu, Aug 5, 2010 at 11:09, Jonathan Nieder <jrnieder@gmail.com> wrote:\n> Some tweaks to simplify adding and running tests.\n>\n>  - Use test_tick for predictable, sort of realistic commit dates;\n\n\n>        git checkout side &&\n>        echo same line | append_cr >>file &&\n>        echo same line >>control_file &&\n>        git add file control_file &&\n> +       test_tick &&\n>        git commit -m \"add line from b\" &&\n>        git tag b &&\n\nFWIW this looks like it could use Dmitry's \"test-lib.sh: introduce 4th\nargument to test_commit() specifying a tag name\" patch. Maybe that\ngoes for most of these git add/tick/commit/tag combos, i.e. they don't\nreally need $commit_message != $tagname.\n"},{"id":"147180","messageId":"20100805115423.GP13779@burratino","threadId":"24269","inReplyTo":"AANLkTikv3oYapOVWmxkt2eqwGWQKMAQOCmruShSiHjKv@mail.gmail.com","subject":"Re: [PATCH 01/12] t6038 (merge.renormalize): style nitpicks","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-08-05T11:54:23Z","receivedAt":"2010-08-05T11:54:23Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ævar Arnfjörð Bjarmason wrote:\n> On Thu, Aug 5, 2010 at 11:09, Jonathan Nieder <jrnieder@gmail.com> wrote:\n\n>>        git checkout side &&\n>>        echo same line | append_cr >>file &&\n>>        echo same line >>control_file &&\n>>        git add file control_file &&\n>> +       test_tick &&\n>>        git commit -m \"add line from b\" &&\n>>        git tag b &&\n>\n> FWIW this looks like it could use Dmitry's \"test-lib.sh: introduce 4th\n> argument to test_commit() specifying a tag name\" patch.\n\nIn this example I am not confident the file has content suitable for\necho.\n\nThe discussion brings to mind something[1] I thought wise in a\ndifferent context:\n\n\t“I mentioned earlier that UNIX was not especially suited\n\tto applications involving vast quantities of data. The\n\treason is this: files are limited in size to 64K bytes.\n\tThe reason for this is not particularly defensible, but\n\tit has to do with the fact that the PDP-11 word size is\n\t16 bits.\n\n\tThere are a couple of ways around this problem. One of\n\tthem is simply to split one large logical file into\n\tseveral smaller actual files.  This approach works for a\n\twhile. The limitation here comes from the fact that\n\tdirectories are searched in a linear fashion. Thus if the\n\tare a vast number of files, it can become quite\n\ttime-consuming tosearch directories to find the files\n\tthey contain. We have not noticed this to be a problem,\n\tso far, it is only a worry.\n\n\tAnother way around the small file size is to use a disk\n\tas a special file. For various reasons, when an entire\n\tdisk drive is accessed as a special file, the size\n\tlimitation does not occur. Thus one can set up a program\n\twhich manages its own data-- in effect is its own,\n\tspecial-purpose file system-- and expect reasonable results.\n\n\tThis again bears on the general versus special purpose\n\tsystem: it probably is more efficient anyway to do your\n\town data management, provided the extra labor is worth\n\tthe cost.”\n\nOf course the tradeoffs are completely different here but it is worth\nbearing in mind the underlying process: sometimes a too general\nfacility only gets in the way unless all the facets of how it should\nbe used have been carefully understood (i.e., good interfaces\nsometimes evolve by excluding the special cases until the missed\nbenefit from not including them is overwhelming).\n\nSorry for the ramble.  Another way to say it: I am happy to see\ntest_commit be made more useful, but if extra-weird cases do not fit\nit, please do not take that as a failing.\n\n[1] http://cm.bell-labs.com/cm/cs/who/dmr/notes.html\n"},{"id":"147181","messageId":"AANLkTi=9GwZgiQHpBLN_L14==Pir0Gs=DosZHF4wg9zi@mail.gmail.com","threadId":"24269","inReplyTo":"20100805111738.GI13779@burratino","subject":"Re: [PATCH 07/12] ll-merge: make flag easier to populate","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2010-08-05T12:12:30Z","receivedAt":"2010-08-05T12:12:30Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"On Thu, Aug 5, 2010 at 13:17, Jonathan Nieder <jrnieder@gmail.com> wrote:\n> ll_merge() takes its options in a flag word, which has a few\n> advantages:\n>\n>  - options flags can be cheaply passed around in registers, while\n>   an option struct passed by pointer cannot;\n>\n>  - callers can easily pass 0 without trouble for no options,\n>   while an option struct passed by value would not allow that.\n>\n> The downside is that code to populate and access the flag word can be\n> somewhat opaque.  Mitigate that with a few macros.\n>\n> Cc: Avery Pennarun <apenwarr@gmail.com>\n> Cc: Bert Wesarg <bert.wesarg@googlemail.com>\n> Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>\n> ---\n>  Documentation/technical/api-merge.txt |   11 +++++++----\n>  ll-merge.c                            |    9 +++++----\n>  ll-merge.h                            |   14 ++++++++++++++\n>  merge-recursive.c                     |    3 ++-\n>  4 files changed, 28 insertions(+), 9 deletions(-)\n>\n> diff --git a/Documentation/technical/api-merge.txt b/Documentation/technical/api-merge.txt\n> index 01a89d6..a7e050b 100644\n> --- a/Documentation/technical/api-merge.txt\n> +++ b/Documentation/technical/api-merge.txt\n> @@ -49,12 +49,15 @@ supports this.\n>\n>  The `flag` parameter is a bitfield:\n>\n> - - The least significant bit indicates whether this is an internal\n> -   merge to consolidate ancestors for a recursive merge.\n> + - The `LL_OPT_VIRTUAL_ANCESTOR` bit indicates whether this is an\n> +   internal merge to consolidate ancestors for a recursive merge.\n>\n> - - The next two bits allow local conflicts to be automatically\n> + - The `LL_OPT_FAVOR_MASK` bits allow local conflicts to be automatically\n>    resolved in favor of one side or the other (as in 'git merge-file'\n> -   `--ours`/`--theirs`/`--union` for 01, 10, and 11, respectively).\n> +   `--ours`/`--theirs`/`--union`).\n> +   They can be populated by `create_ll_flag`, whose argument can be\n> +   `XDL_MERGE_FAVOR_OURS`, `XDL_MERGE_FAVOR_THEIRS`, or\n> +   `XDL_MERGE_FAVOR_UNION`.\n>\n>  Everything else\n>  ---------------\n> diff --git a/ll-merge.c b/ll-merge.c\n> index 5068fe0..290f764 100644\n> --- a/ll-merge.c\n> +++ b/ll-merge.c\n> @@ -46,7 +46,7 @@ static int ll_binary_merge(const struct ll_merge_driver *drv_unused,\n>         * or common ancestor for an internal merge.  Still return\n>         * \"conflicted merge\" status.\n>         */\n> -       mmfile_t *stolen = (flag & 01) ? orig : src1;\n> +       mmfile_t *stolen = (flag & LL_OPT_VIRTUAL_ANCESTOR) ? orig : src1;\n>\n>        result->ptr = stolen->ptr;\n>        result->size = stolen->size;\n> @@ -79,7 +79,7 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,\n>\n>        memset(&xmp, 0, sizeof(xmp));\n>        xmp.level = XDL_MERGE_ZEALOUS;\n> -       xmp.favor= (flag >> 1) & 03;\n> +       xmp.favor = ll_opt_favor(flag);\n>        if (git_xmerge_style >= 0)\n>                xmp.style = git_xmerge_style;\n>        if (marker_size > 0)\n> @@ -99,7 +99,8 @@ static int ll_union_merge(const struct ll_merge_driver *drv_unused,\n>                          int flag, int marker_size)\n>  {\n>        /* Use union favor */\n> -       flag = (flag & 1) | (XDL_MERGE_FAVOR_UNION << 1);\n> +       flag = (flag & LL_OPT_VIRTUAL_ANCESTOR) |\n> +              create_ll_flag(XDL_MERGE_FAVOR_UNION);\n>        return ll_xdl_merge(drv_unused, result, path_unused,\n>                            orig, NULL, src1, NULL, src2, NULL,\n>                            flag, marker_size);\n> @@ -342,7 +343,7 @@ int ll_merge(mmbuffer_t *result_buf,\n>        const char *ll_driver_name = NULL;\n>        int marker_size = DEFAULT_CONFLICT_MARKER_SIZE;\n>        const struct ll_merge_driver *driver;\n> -       int virtual_ancestor = flag & 01;\n> +       int virtual_ancestor = flag & LL_OPT_VIRTUAL_ANCESTOR;\n>\n>        if (merge_renormalize) {\n>                normalize_file(ancestor, path);\n> diff --git a/ll-merge.h b/ll-merge.h\n> index 57754cc..5990271 100644\n> --- a/ll-merge.h\n> +++ b/ll-merge.h\n> @@ -5,6 +5,20 @@\n>  #ifndef LL_MERGE_H\n>  #define LL_MERGE_H\n>\n> +#define LL_OPT_VIRTUAL_ANCESTOR        (1 << 0)\n> +#define LL_OPT_FAVOR_MASK      ((1 << 1) | (1 << 2))\n> +#define LL_OPT_FAVOR_SHIFT 1\n> +\n> +static inline int ll_opt_favor(int flag)\n> +{\n> +       return (flag & LL_OPT_FAVOR_MASK) >> LL_OPT_FAVOR_SHIFT;\n> +}\n> +\n> +static inline int create_ll_flag(int favor)\n> +{\n> +       return ((favor << LL_OPT_FAVOR_SHIFT) & LL_OPT_FAVOR_MASK);\n> +}\n> +\n\nThese two function names do not suggests that these are symmetric. How\nabout get_ll_flavor() and create_ll_flavor()? Or flavor_to_ll_flag()\nand ll_flag_to_flavor().\n\nRegards,\nBert\n\n>  int ll_merge(mmbuffer_t *result_buf,\n>             const char *path,\n>             mmfile_t *ancestor, const char *ancestor_label,\n> diff --git a/merge-recursive.c b/merge-recursive.c\n> index 8a49844..c0c9f0c 100644\n> --- a/merge-recursive.c\n> +++ b/merge-recursive.c\n> @@ -647,7 +647,8 @@ static int merge_3way(struct merge_options *o,\n>\n>        merge_status = ll_merge(result_buf, a->path, &orig, base_name,\n>                                &src1, name1, &src2, name2,\n> -                               (!!o->call_depth) | (favor << 1));\n> +                               ((o->call_depth ? LL_OPT_VIRTUAL_ANCESTOR : 0) |\n> +                                create_ll_flag(favor)));\n>\n>        free(name1);\n>        free(name2);\n> --\n> 1.7.2.1.544.ga752d.dirty\n>\n>\n"},{"id":"147183","messageId":"20100805121658.GA23334@burratino","threadId":"24269","inReplyTo":"AANLkTi=9GwZgiQHpBLN_L14==Pir0Gs=DosZHF4wg9zi@mail.gmail.com","subject":"Re: [PATCH 07/12] ll-merge: make flag easier to populate","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-08-05T12:16:58Z","receivedAt":"2010-08-05T12:16:58Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Bert Wesarg wrote:\n> On Thu, Aug 5, 2010 at 13:17, Jonathan Nieder <jrnieder@gmail.com> wrote:\n\n>> +static inline int ll_opt_favor(int flag)\n>> +{\n>> +       return (flag & LL_OPT_FAVOR_MASK) >> LL_OPT_FAVOR_SHIFT;\n>> +}\n>> +\n>> +static inline int create_ll_flag(int favor)\n>> +{\n>> +       return ((favor << LL_OPT_FAVOR_SHIFT) & LL_OPT_FAVOR_MASK);\n>> +}\n>> +\n[...]\n> Or flavor_to_ll_flag()\n> and ll_flag_to_flavor().\n\nSounds reasonable.  (Well, except this is “favor” as in “favor our\nside” or “favor their side” rather than “flavor of the month”.)  Patch?\n"},{"id":"147186","messageId":"AANLkTinz2hbbYcq=cQ=wmkK+Kg5W6Xf4r8yjYp1211ZU@mail.gmail.com","threadId":"24269","inReplyTo":"20100805121658.GA23334@burratino","subject":"Re: [PATCH 07/12] ll-merge: make flag easier to populate","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2010-08-05T13:05:31Z","receivedAt":"2010-08-05T13:05:31Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"On Thu, Aug 5, 2010 at 14:16, Jonathan Nieder <jrnieder@gmail.com> wrote:\n> Bert Wesarg wrote:\n>> On Thu, Aug 5, 2010 at 13:17, Jonathan Nieder <jrnieder@gmail.com> wrote:\n>\n>>> +static inline int ll_opt_favor(int flag)\n>>> +{\n>>> +       return (flag & LL_OPT_FAVOR_MASK) >> LL_OPT_FAVOR_SHIFT;\n>>> +}\n>>> +\n>>> +static inline int create_ll_flag(int favor)\n>>> +{\n>>> +       return ((favor << LL_OPT_FAVOR_SHIFT) & LL_OPT_FAVOR_MASK);\n>>> +}\n>>> +\n> [...]\n>> Or flavor_to_ll_flag()\n>> and ll_flag_to_flavor().\n>\n> Sounds reasonable.  (Well, except this is “favor” as in “favor our\n> side” or “favor their side” rather than “flavor of the month”.)  Patch?\n\nSorry for this typo. Do you really want a squash patch for this renaming?\n\nBert\n\n>\n"},{"id":"147189","messageId":"20100805131132.GA25070@burratino","threadId":"24269","inReplyTo":"AANLkTinz2hbbYcq=cQ=wmkK+Kg5W6Xf4r8yjYp1211ZU@mail.gmail.com","subject":"Re: [PATCH 07/12] ll-merge: make flag easier to populate","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-08-05T13:11:32Z","receivedAt":"2010-08-05T13:11:32Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Bert Wesarg wrote:\n> On Thu, Aug 5, 2010 at 14:16, Jonathan Nieder <jrnieder@gmail.com> wrote:\n>> Bert Wesarg wrote:\n\n>>> Or flavor_to_ll_flag()\n>>> and ll_flag_to_flavor().\n>>\n>> Sounds reasonable.  (Well, except this is “favor” as in “favor our\n>> side” or “favor their side” rather than “flavor of the month”.)  Patch?\n>\n> Sorry for this typo. Do you really want a squash patch for this renaming?\n\nYes, that would make my life easier.  But if you are short on time, I\ncan get to it myself later.\n"},{"id":"147234","messageId":"DB014985-CC03-4DBA-B801-C9B80933AB0B@gmail.com","threadId":"24269","inReplyTo":"20100805110822.GB13779@burratino","subject":"Re: [PATCH/RFC v2 0/12] Re: rerere: let caller decide whether to renormalize","fromName":"Eyvind Bernhardsen","fromEmail":"eyvind.bernhardsen@gmail.com","sentAt":"2010-08-05T19:02:08Z","receivedAt":"2010-08-05T19:02:08Z","isPatch":true,"sender":{"key":"eyvind.bernhardsen@gmail.com","avatar":"https://avatars.githubusercontent.com/u/106762?v=4"},"body":"Hi Jonathan,\n\nSorry I'm late to respond, I've just caught up with work after a long summer holiday.  It's back to working with complicated merges across normalization boundaries now, though.  This series looks like it's taking my hack in a sane direction, so for what it's worth:\n\nAcked-by: Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com>\n\nMeanwhile, I've hit an annoyance: currently, files that are introduced with CRLFs on a non-normalized branch are left alone in the merged branch, causing the classical core.autocrlf problem of marking every line in such a file as changed if it is so much as touched.\n\nI think the renormalization setting makes much more sense if the end result of the merge is normalized, so I'm working on a patch to make it do that.\n\n- Eyvind\n"}]}