{"thread":{"id":"3923","subject":"[RFC/PATCH] Add git-unresolve <paths>...","startedAt":"2006-04-19T20:01:28Z","lastAt":"2006-04-20T00:33:02Z","messageCount":11,"participants":["Junio C Hamano","Carl Worth"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"18886","messageId":"7vu08p72sn.fsf@assigned-by-dhcp.cox.net","threadId":"3923","inReplyTo":null,"subject":"[RFC/PATCH] Add git-unresolve <paths>...","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-04-19T20:01:28Z","receivedAt":"2006-04-19T20:01:28Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This is an attempt to address the issue raised on #git channel\nrecently by Carl Worth.\n\nAfter a conflicted automerge, \"git diff\" shows a combined diff\nto give you how the tentative automerge result differs from\nwhat came from each branch.  During a complex merge, it is\ntempting to be able to resolve a few paths at a time, mark\nthem \"I've dealt with them\" with git-update-index to unclutter\nthe next \"git diff\" output, and keep going.  However, when the\nfinal result does not compile or otherwise found to be a\nmismerge, the workflow to fix the mismerged paths suddenly\nchanges to \"git diff HEAD -- path\" (to get a diff from our\nHEAD before merging) and \"git diff MERGE_HEAD -- path\" (to get\na diff from theirs), and it cannot show the combined anymore.\n\nWith git-unresolve <paths>..., the versions from our branch and\ntheir branch for specified blobs are placed in stage #2 and\nstage #3, without touching the working tree files.  This gives\nyou the combined diff back for easier review, along with\n\"diff --ours\" and \"diff --theirs\".\n\nOne thing it does not do is to place the base in stage #1; this\nmeans \"diff --base\" would behave differently between the run\nimmediately after a conflicted three-way merge, and the run\nafter an update-index by mistake followed by a git-unresolve.\n\nWe could theoretically run merge-base between HEAD and\nMERGE_HEAD to find which tree to place in stage #1, but\nreviewing \"diff --base\" is not that useful so....\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n * comes on top of the previous two \"extended sha1\" cleanups.\n\n .gitignore  |    1 \n Makefile    |    3 +\n cache.h     |    1 \n sha1_name.c |    6 +-\n unresolve.c |  145 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 151 insertions(+), 5 deletions(-)\n\ndiff --git a/.gitignore b/.gitignore\nindex b5959d6..1e4ba7b 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -111,6 +111,7 @@ git-tag\n git-tar-tree\n git-unpack-file\n git-unpack-objects\n+git-unresolve\n git-update-index\n git-update-ref\n git-update-server-info\ndiff --git a/Makefile b/Makefile\nindex 8aed3af..85938c0 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -165,7 +165,8 @@ PROGRAMS = \\\n \tgit-upload-pack$X git-verify-pack$X git-write-tree$X \\\n \tgit-update-ref$X git-symbolic-ref$X git-check-ref-format$X \\\n \tgit-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \\\n-\tgit-describe$X git-merge-tree$X git-blame$X git-imap-send$X\n+\tgit-describe$X git-merge-tree$X git-blame$X git-imap-send$X \\\n+\tgit-unresolve$X\n \n BUILT_INS = git-log$X\n \ndiff --git a/cache.h b/cache.h\nindex 69801b0..a5f1eb3 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -235,6 +235,7 @@ #define DEFAULT_ABBREV 7\n extern int get_sha1(const char *str, unsigned char *sha1);\n extern int get_sha1_hex(const char *hex, unsigned char *sha1);\n extern char *sha1_to_hex(const unsigned char *sha1);\t/* static buffer result! */\n+extern int get_tree_entry(const unsigned char *, const char *, unsigned char *, unsigned *);\n extern int read_ref(const char *filename, unsigned char *sha1);\n extern const char *resolve_ref(const char *path, unsigned char *sha1, int);\n extern int create_symref(const char *git_HEAD, const char *refs_heads_master);\ndiff --git a/sha1_name.c b/sha1_name.c\nindex 7ad20b5..68b1275 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -450,8 +450,6 @@ static int get_sha1_1(const char *name, \n \treturn get_short_sha1(name, len, sha1, 0);\n }\n \n-static int get_tree_entry(const unsigned char *, const char *, unsigned char *, unsigned *);\n-\n static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result, unsigned *mode)\n {\n \tint namelen = strlen(name);\n@@ -487,13 +485,13 @@ static int find_tree_entry(struct tree_d\n \treturn -1;\n }\n \n-static int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned char *sha1, unsigned *mode)\n+int get_tree_entry(const unsigned char *ent_sha1, const char *name, unsigned char *sha1, unsigned *mode)\n {\n \tint retval;\n \tvoid *tree;\n \tstruct tree_desc t;\n \n-\ttree = read_object_with_reference(tree_sha1, tree_type, &t.size, NULL);\n+\ttree = read_object_with_reference(ent_sha1, tree_type, &t.size, NULL);\n \tif (!tree)\n \t\treturn -1;\n \tt.buf = tree;\ndiff --git a/unresolve.c b/unresolve.c\nnew file mode 100644\nindex 0000000..14655f7\n--- /dev/null\n+++ b/unresolve.c\n@@ -0,0 +1,145 @@\n+#include \"cache.h\"\n+\n+static const char unresolve_usage[] =\n+\"git-unresolve <paths>...\";\n+\n+static struct cache_file cache_file;\n+static unsigned char head_sha1[20];\n+static unsigned char merge_head_sha1[20];\n+\n+static struct cache_entry *read_one_ent(const char *which,\n+\t\t\t\t\tunsigned char *ent, const char *path,\n+\t\t\t\t\tint namelen, int stage)\n+{\n+\tunsigned mode;\n+\tunsigned char sha1[20];\n+\tint size;\n+\tstruct cache_entry *ce;\n+\n+\tif (get_tree_entry(ent, path, sha1, &mode)) {\n+\t\terror(\"%s: not in %s branch.\", path, which);\n+\t\treturn NULL;\n+\t}\n+\tif (mode == S_IFDIR) {\n+\t\terror(\"%s: not a blob in %s branch.\", path, which);\n+\t\treturn NULL;\n+\t}\n+\tsize = cache_entry_size(namelen);\n+\tce = xcalloc(1, size);\n+\n+\tmemcpy(ce->sha1, sha1, 20);\n+\tmemcpy(ce->name, path, namelen);\n+\tce->ce_flags = create_ce_flags(namelen, stage);\n+\tce->ce_mode = create_ce_mode(mode);\n+\treturn ce;\n+}\n+\n+static int unresolve_one(const char *path)\n+{\n+\tint namelen = strlen(path);\n+\tint pos;\n+\tint ret = 0;\n+\tstruct cache_entry *ce_2 = NULL, *ce_3 = NULL;\n+\n+\t/* See if there is such entry in the index. */\n+\tpos = cache_name_pos(path, namelen);\n+\tif (pos < 0) {\n+\t\t/* If there isn't, either it is unmerged, or\n+\t\t * resolved as \"removed\" by mistake.  We do not\n+\t\t * want to do anything in the former case.\n+\t\t */\n+\t\tpos = -pos-1;\n+\t\tif (pos < active_nr) {\n+\t\t\tstruct cache_entry *ce = active_cache[pos];\n+\t\t\tif (ce_namelen(ce) == namelen &&\n+\t\t\t    !memcmp(ce->name, path, namelen)) {\n+\t\t\t\tfprintf(stderr,\n+\t\t\t\t\t\"%s: skipping still unmerged path.\\n\",\n+\t\t\t\t\tpath);\n+\t\t\t\tgoto free_return;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t/* Grab blobs from given path from HEAD and MERGE_HEAD,\n+\t * stuff HEAD version in stage #2,\n+\t * stuff MERGE_HEAD version in stage #3.\n+\t */\n+\tce_2 = read_one_ent(\"our\", head_sha1, path, namelen, 2);\n+\tce_3 = read_one_ent(\"their\", merge_head_sha1, path, namelen, 3);\n+\n+\tif (!ce_2 || !ce_3) {\n+\t\tret = -1;\n+\t\tgoto free_return;\n+\t}\n+\tif (!memcmp(ce_2->sha1, ce_3->sha1, 20) &&\n+\t    ce_2->ce_mode == ce_3->ce_mode) {\n+\t\tfprintf(stderr, \"%s: identical in both, skipping.\\n\",\n+\t\t\tpath);\n+\t\tgoto free_return;\n+\t}\n+\n+\tremove_file_from_cache(path);\n+\tif (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) {\n+\t\terror(\"%s: cannot add our version to the index.\", path);\n+\t\tret = -1;\n+\t\tgoto free_return;\n+\t}\n+\tif (!add_cache_entry(ce_3, ADD_CACHE_OK_TO_ADD))\n+\t\treturn 0;\n+\terror(\"%s: cannot add their version to the index.\", path);\n+\tret = -1;\n+ free_return:\n+\tfree(ce_2);\n+\tfree(ce_3);\n+\treturn ret;\n+}\n+\n+static void read_head_pointers(void)\n+{\n+\tif (read_ref(git_path(\"HEAD\"), head_sha1))\n+\t\tdie(\"Cannot read HEAD -- no initial commit yet?\");\n+\tif (read_ref(git_path(\"MERGE_HEAD\"), merge_head_sha1)) {\n+\t\tfprintf(stderr, \"Not in the middle of a merge.\\n\");\n+\t\texit(0);\n+\t}\n+}\n+\n+int main(int ac, char **av)\n+{\n+\tint i;\n+\tint err = 0;\n+\tint newfd;\n+\n+\tif (ac < 2)\n+\t\tusage(unresolve_usage);\n+\n+\tgit_config(git_default_config);\n+\n+\t/* Read HEAD and MERGE_HEAD; if MERGE_HEAD does not exist, we\n+\t * are not doing a merge, so exit with success status.\n+\t */\n+\tread_head_pointers();\n+\n+\t/* Otherwise we would need to update the cache. */\n+\tnewfd= hold_index_file_for_update(&cache_file, get_index_file());\n+\tif (newfd < 0)\n+\t\tdie(\"unable to create new cachefile\");\n+\n+\tif (read_cache() < 0)\n+\t\tdie(\"cache corrupted\");\n+\n+\tfor (i = 1; i < ac; i++) {\n+\t\tchar *arg = av[i];\n+\t\terr |= unresolve_one(arg);\n+\t}\n+\tif (err)\n+\t\tdie(\"Error encountered; index not updated.\");\n+\n+\tif (active_cache_changed) {\n+\t\tif (write_cache(newfd, active_cache, active_nr) ||\n+\t\t    commit_index_file(&cache_file))\n+\t\t\tdie(\"Unable to write new cachefile\");\n+\t}\n+\treturn 0;\n+}\n-- \n1.3.0.g2c4a\n"},{"id":"18889","messageId":"87acah6zk6.wl%cworth@cworth.org","threadId":"3923","inReplyTo":"7vu08p72sn.fsf@assigned-by-dhcp.cox.net","subject":"Re: [RFC/PATCH] Add git-unresolve <paths>...","fromName":"Carl Worth","fromEmail":"cworth@cworth.org","sentAt":"2006-04-19T21:11:21Z","receivedAt":"2006-04-19T21:11:21Z","isPatch":true,"sender":{"key":"cworth@cworth.org","avatar":"https://gravatar.com/avatar/3746dc28cde609bdbd7f939058356e7e2bbd16d21e32274df0725eb3d998bc5b?d=mp&s=160"},"body":"On Wed, 19 Apr 2006 13:01:28 -0700, Junio C Hamano wrote:\n> \n> This is an attempt to address the issue raised on #git channel\n> recently by Carl Worth.\n...\n> With git-unresolve <paths>..., the versions from our branch and\n> their branch for specified blobs are placed in stage #2 and\n> stage #3, without touching the working tree files.  This gives\n> you the combined diff back for easier review, along with\n> \"diff --ours\" and \"diff --theirs\".\n\nThanks. This looks quite interesting.\n\nThe name of git-unresolve seems reasonable on its own. In fact,\ngit-merge already uses \"resolve\" to describe manual conflict\nresolution. For example:\n\n\techo \"Using the $best_strategy to prepare resolving by hand.\"\n\nBut this does introduce an unfortunate semantic clash with the\nexisting git-resolve, (which is an automated merge tool of some sort).\nI don't know much about the existing git-resolve, but a recent thread\nsuggests it is a non-useful relic and people shouldn't be using it.\n\nIt would be nice if the complementary operations of manually\nresolving and unresolving a merge conflict had complementary command\nnames. Would it be feasible to cop-opt the git-resolve name as a\nsynonym for update-index? (I certainly wouldn't mind a shorter name\nfor that operation.)\n\nIn fact, git-resolve could even go beyond being a synonym for\nupdate-index. For example, it might also complain if it notices\nconflict markers in the file. That seems like it would be a useful\nconvenience, (and no functionality would be robbed, since update-index\nwould still be available for anyone who needs to commit content that\nlooks like conflict markers).\n\nAnother thing I've been meaning to suggest soon is better output for\nwhen an automatic merge fails. Currently, the final message is:\n\n\tAutomatic merge failed; fix up by hand\n\nThat's not much guidance for a new user that perhaps is only used to\n\"git commit -a\" and \"git pull\" that usually works. Without getting too\nverbose, this might be improved with something like:\n\n\tAutomatic merge failed; fix conflicts by hand, then commit the result\n\nIn addition, if git-commit is run with unmerged files, it could also\nprovide some extra guidance. Perhaps something like this, (just after\nthe list of \"needs merge\" files mentioned by git-update-index):\n\n\tFix any conflicts in these files, then git-resolve each and commit again\n\n-Carl\n"},{"id":"18891","messageId":"7v8xq16y31.fsf@assigned-by-dhcp.cox.net","threadId":"3923","inReplyTo":"87acah6zk6.wl%cworth@cworth.org","subject":"Re: [RFC/PATCH] Add git-unresolve <paths>...","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-04-19T21:43:14Z","receivedAt":"2006-04-19T21:43:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Carl Worth <cworth@cworth.org> writes:\n\n> But this does introduce an unfortunate semantic clash with the\n> existing git-resolve, (which is an automated merge tool of some sort).\n> I don't know much about the existing git-resolve, but a recent thread\n> suggests it is a non-useful relic and people shouldn't be using it.\n\nIt is useful in a quick-and-dirty way, but does the same thing\nas 'git merge -s resolve' and that is why people discussed\nabout removing it.  It has semantics quite different from\nupdate-index, so I'd ignore the synonym part of your\ndiscussion.\n\nTime for a quick raise-hand.  Does anybody still use 'git\nresolve'?  Maybe we could remove it by 1.4?\n\n> It would be nice if the complementary operations of manually\n> resolving and unresolving a merge conflict had complementary command\n> names.\n\nTrue.  I considered two other possibilities.\n\n * \"git unmerge\", because it creates unmerged index entries, and\n\n * \"git update-index --unmerge\", because this is just a special\n   kind of updates to the index file.\n\n> ... For example, it might also complain if it notices\n> conflict markers in the file. That seems like it would be a useful\n> convenience.\n\nSince you should compile test the merge result before going\nanywhere, and the primary target of git is to manage sources, it\nmight not matter in most of the case, but for non-sources and non\ncompiled languages, that certainly is an issue.\n\nWhen it _does_ matter, you can have a customized pre-commit hook\nto look for the conflict markers, like this:\n\ndiff --git a/templates/hooks--pre-commit b/templates/hooks--pre-commit\nindex 43d3b6e..723a9ef 100644\n--- a/templates/hooks--pre-commit\n+++ b/templates/hooks--pre-commit\n@@ -61,6 +61,9 @@ perl -e '\n \t    if (/^\\s* \t/) {\n \t\tbad_line(\"indent SP followed by a TAB\", $_);\n \t    }\n+\t    if (/^(?:[<>=]){7}/) {\n+\t\tbad_line(\"unresolved merge conflict\", $_);\n+\t    }\n \t}\n     }\n     exit($found_bad);\n\nAs usual, you can defeat the check with \"git commit --no-verify\"\nfor unlikely false matches.\n\n> That's not much guidance for a new user that perhaps is only used to\n> \"git commit -a\" and \"git pull\" that usually works. Without getting too\n> verbose, this might be improved with something like:\n>\n> \tAutomatic merge failed; fix conflicts by hand, then commit the result\n\nThat should be an easy patch ;-).\n\ndiff --git a/git-merge.sh b/git-merge.sh\nindex 78ab422..b834e79 100755\n--- a/git-merge.sh\n+++ b/git-merge.sh\n@@ -335,5 +335,5 @@ Conflicts:\n \tthen\n \t\tgit-rerere\n \tfi\n-\tdie \"Automatic merge failed; fix up by hand\"\n+\tdie \"Automatic merge failed; fix conflicts and then commit the result.\"\n fi\n"},{"id":"18895","messageId":"7vwtdl5irl.fsf_-_@assigned-by-dhcp.cox.net","threadId":"3923","inReplyTo":"7v8xq16y31.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH] git-merge: a bit more readable user guidance.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-04-19T21:59:26Z","receivedAt":"2006-04-19T21:59:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"We said \"fix up by hand\" after failed automerge, which was a big\n\"Huh?  Now what?\".  Be a bit more explicit without being too\nverbose. Suggested by Carl Worth.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n\n---\n\n git-merge.sh |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\naec8e89ca3d38f9d0d4d287d874e8b3859d68787\ndiff --git a/git-merge.sh b/git-merge.sh\nindex 78ab422..b834e79 100755\n--- a/git-merge.sh\n+++ b/git-merge.sh\n@@ -335,5 +335,5 @@ Conflicts:\n \tthen\n \t\tgit-rerere\n \tfi\n-\tdie \"Automatic merge failed; fix up by hand\"\n+\tdie \"Automatic merge failed; fix conflicts and then commit the result.\"\n fi\n-- \n1.3.0.g2c4a\n"},{"id":"18896","messageId":"7vpsjd5iqw.fsf_-_@assigned-by-dhcp.cox.net","threadId":"3923","inReplyTo":"7v8xq16y31.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH] pre-commit hook: complain about conflict markers.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-04-19T21:59:51Z","receivedAt":"2006-04-19T21:59:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Several <<< or === or >>> characters at the beginning of a line\nis very likely to be leftover conflict markers from a failed\nautomerge the user resolved incorrectly, so detect them.\n\nAs usual, this can be defeated with \"git commit --no-verify\" if\nyou really do want to have those files, just like changes that\nintroduce trailing whitespaces.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n\n---\n\n templates/hooks--pre-commit |    3 +++\n 1 files changed, 3 insertions(+), 0 deletions(-)\n\n6be63d37efc766756922a9b96ed8fe7a332f133a\ndiff --git a/templates/hooks--pre-commit b/templates/hooks--pre-commit\nindex 43d3b6e..723a9ef 100644\n--- a/templates/hooks--pre-commit\n+++ b/templates/hooks--pre-commit\n@@ -61,6 +61,9 @@ perl -e '\n \t    if (/^\\s* \t/) {\n \t\tbad_line(\"indent SP followed by a TAB\", $_);\n \t    }\n+\t    if (/^(?:[<>=]){7}/) {\n+\t\tbad_line(\"unresolved merge conflict\", $_);\n+\t    }\n \t}\n     }\n     exit($found_bad);\n-- \n1.3.0.g2c4a\n"},{"id":"18897","messageId":"87wtdl2o5o.wl%cworth@cworth.org","threadId":"3923","inReplyTo":"7v8xq16y31.fsf@assigned-by-dhcp.cox.net","subject":"Re: [RFC/PATCH] Add git-unresolve <paths>...","fromName":"Carl Worth","fromEmail":"cworth@cworth.org","sentAt":"2006-04-19T22:31:15Z","receivedAt":"2006-04-19T22:31:15Z","isPatch":true,"sender":{"key":"cworth@cworth.org","avatar":"https://gravatar.com/avatar/3746dc28cde609bdbd7f939058356e7e2bbd16d21e32274df0725eb3d998bc5b?d=mp&s=160"},"body":"On Wed, 19 Apr 2006 14:43:14 -0700, Junio C Hamano wrote:\n>\n> > It would be nice if the complementary operations of manually\n> > resolving and unresolving a merge conflict had complementary command\n> > names.\n> \n> True.  I considered two other possibilities.\n> \n>  * \"git unmerge\", because it creates unmerged index entries, and\n\nThis has the same problem as git-unresolve as compared to the old\ngit-resolve. Namely, it would provide an \"unmerge\" command that\nsyntactically looks like a complement to \"merge\" but in fact is an\nentirely separate, (operating on different object types, etc.)\n\n>  * \"git update-index --unmerge\", because this is just a special\n>    kind of updates to the index file.\n\nThat seems like a very reasonable place to have this functionality (if\nneeded). Compared to things like \"update-index --add\" and\n\"update-index --remove\" the desire for this operation is likely much\nmore rare, so perhaps doesn't merit its own command.\n\nMeanwhile, I still think it's worth re-considering the original\nproblem. \n\nAfter a failed merge, I get a multi-parent diff from \"git\ndiff\". However, after updating the index, I can't find any way to get\nmulti-parent diffs anymore.\n\nI'd still like to be able to do that, even when I know that what I\nhave in the index is good, and I don't want to undo it. So the\nproposed unresolve (or update-index --unmerge, or whatever) is still\nnot totally satisfactory.\n\nIn this state, \"git commit\" is still multi-parent aware and will be\nmaking a merged commit, (by examining .git/MERGE_HEAD). Wouldn't it be\nreasonable to make \"git diff --cached\" also be aware of this mid-merge\nstate and display a multi-parent diff? at least as an option?\n\n-Carl\n\nPS. As a more bizarre suggestion, I've been idly wondering for some\ntime if it would make sense to provide names to allow the working tree\nand the index to be treated as pseudo, in-progress objects. For\nexample, much of my initial (and occasionally residual) confusion\nregarding \"git diff\" would be alleviated if I could run commands\nsomething like:\n\n\tgit diff HEAD TREE\n\tgit diff HEAD INDEX\n\tgit diff INDEX TREE\n\nFor this usage, perhaps INDEX and TREE are not the actual names that\ngit supports, but just some convenient refs that a user has set\nup. And I don't have any proposal for what would be appropriate\nlow-level names for git to use for these pseudo-objects.\n\nBut I would sure be happy if I could just use refs like the above.\n\nThen, if a concept like that existed, it should be rather\nstraightforward to achieve the multi-parent HEAD,MERGE_HEAD -> INDEX\ndiff that I'm after. It could probably come in handy in quite a few\nother situations as well.\n\nBut I can imagine the idea might also break down quite badly. So just\nlet me know where it is totally stupid/infeasible.\n"},{"id":"18898","messageId":"7vk69l5gi1.fsf@assigned-by-dhcp.cox.net","threadId":"3923","inReplyTo":"87wtdl2o5o.wl%cworth@cworth.org","subject":"Re: [RFC/PATCH] Add git-unresolve <paths>...","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-04-19T22:48:22Z","receivedAt":"2006-04-19T22:48:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Carl Worth <cworth@cworth.org> writes:\n\n> Meanwhile, I still think it's worth re-considering the original\n> problem. \n\nMaybe you are misunderstanding something in a major way,...\n\n> After a failed merge, I get a multi-parent diff from \"git\n> diff\". However, after updating the index, I can't find any way to get\n> multi-parent diffs anymore.\n\n... and that is exactly what this \"unmerge to re-populate higher\norder stages\" does.  The multi-parent diff you have been looking\nat failed merge _is_ generated by comparing the higher order\nstages and your working tree file.\n\n> I'd still like to be able to do that, even when I know that what I\n> have in the index is good, and I don't want to undo it.\n\nYou cannot it have both ways.  Either you want to have the unmerged\nentries (so that you *can* view the combined diff), or you do\nnot want to have them (then you obviously cannot view the\ncombined diff between the working tree file and these stages).\n"},{"id":"18900","messageId":"7vmzeh3ypu.fsf@assigned-by-dhcp.cox.net","threadId":"3923","inReplyTo":"87wtdl2o5o.wl%cworth@cworth.org","subject":"Re: [RFC/PATCH] Add git-unresolve <paths>...","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-04-19T23:57:49Z","receivedAt":"2006-04-19T23:57:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Carl Worth <cworth@cworth.org> writes:\n\n> Meanwhile, I still think it's worth re-considering the original\n> problem. \n>\n> After a failed merge, I get a multi-parent diff from \"git\n> diff\". However, after updating the index, I can't find any way to get\n> multi-parent diffs anymore.\n>\n> I'd still like to be able to do that, even when I know that what I\n> have in the index is good, and I don't want to undo it. So the\n> proposed unresolve (or update-index --unmerge, or whatever) is still\n> not totally satisfactory.\n\nI suspect this is just a misunderstanding caused by insufficient\nexplanation, so let's try this a bit differently.\n\nWith the patch applied (or use \"next\" branch I'll be pushing out\nshortly), let's try the core-tutorial example up to the point\nwhere we need to make a merge commit and get conflict.\n\nThe easiest way to do it is to interrupt the t/t1200-tutorial.sh\nsequence, like this:\n\n------------\ndiff --git a/t/t1200-tutorial.sh b/t/t1200-tutorial.sh\nindex 16b3ea9..a51bb50 100755\n--- a/t/t1200-tutorial.sh\n+++ b/t/t1200-tutorial.sh\n@@ -105,6 +105,8 @@ git commit -m 'Some fun.' -i hello examp\n \n test_expect_failure 'git resolve now fails' 'git resolve HEAD mybranch \"Merge work in mybranch\"'\n \n+test_done\n+\n cat > hello << EOF\n Hello World\n It's a new day for git\n------------\n\nNow, go to t/ directory and run this:\n\n------------\n$ cd t\n$ ./t1200-tutorial.sh\n------------\n\n... which will stop after saying:\n\n------------\n*   ok 13: git resolve now fails\n* passed all 13 test(s)\n------------\n\nThen go to t/trash directory to find the initial part of\nTutorial already run for us.  We have created a repository with\ntwo files (`example` and `hello`), with two branches (`master`\nand `mybranch`), and we are on `master`.  Both branches touched\n`hello` in a conflicting way, and we have just pulled the other\nbranch into `master`, which conflicted, and hand resolved.\n\nWe can see the conflict like this:\n\n------------\n$ git diff\ndiff --cc hello\nindex ba42a2a,cc44c73..0000000\n--- a/hello\n+++ b/hello\n@@@ -1,2 -1,3 +1,7 @@@\n  Hello World\n++<<<<<<< .merge_file_4wwUQv\n +Play, play, play\n++=======\n+ It's a new day for git\n+ Work, work, work\n++>>>>>>> .merge_file_6Ie4jx\n------------\n\nand we follow the tutorial to resolve it to see how the combined\ndiff looks like:\n\n------------\n$ cat >hello <<\\EOF\nHello World\nPlay, play, play\nIt's a new day for git\nWork, work, work\nEOF\n$ git diff\ndiff --cc hello\nindex ba42a2a,cc44c73..0000000\n--- a/hello\n+++ b/hello\n@@@ -1,2 -1,3 +1,4 @@@\n  Hello World\n +Play, play, play\n+ It's a new day for git\n+ Work, work, work\n------------\n\nStill with me so?  Now, let's say there was something wrong with\nthe above merge, but at this point we did not know about it.\nFor now, we are satisfied with this and run update-index:\n\n------------\n$ git update-index hello\n$ git diff\n------------\n\nIt was conflicting before, we hand-resolved and we said we are\ndone.  We do not see \"git diff\" output anymore, so we can\nconcentrate on other files.\n\nBut later (much later) we find out that there was something\nwrong with this hand-resolve and now we would want to fix it.\nThe new command, \"git unresolve\" is designed to help us exactly\nin this situation:\n\n------------\n$ git unresolve hello\n$ git diff\ndiff --cc hello\nindex ba42a2a,cc44c73..0000000\n--- a/hello\n+++ b/hello\n@@@ -1,2 -1,3 +1,4 @@@\n  Hello World\n +Play, play, play\n+ It's a new day for git\n+ Work, work, work\n------------\n\nThen we realize that the \"Play\" line is not something we would\nwant in the merge result, because we are in serious business.\nSo let's drop it from the result and view the diff again:\n\n------------\n$ mv hello hellooo\n$ sed -e '2d' <hellooo >hello\n$ rm hellooo \n$ git diff\ndiff --cc hello\nindex ba42a2a,cc44c73..0000000\n--- a/hello\n+++ b/hello\n------------\n\nOops, what happened?  There is no diff?\n\nThe default option \"dense combined\" (--cc) is stronger than\n\"combined\" in that it omits hunks that have differences from\nonly one parent, and in this case we took \"their\" version, so\nthere is no interesting combined merge remaining to be seen.\n\nIf you want to view such not-so-interesting hunks, you could\ngive different option to \"git diff\", like so:\n\n------------\n$ git diff -c -p\ndiff --combined hello\nindex ba42a2a,cc44c73..0000000\n--- a/hello\n+++ b/hello\n@@@ -1,2 -1,3 +1,3 @@@\n  Hello World\n- Play, play, play\n+ It's a new day for git\n+ Work, work, work\n------------\n\nAlso we can use different options like '--ours' (view diff from\nour branch) and '--theirs' (diff from their branch):\n\n------------\n$ git diff --ours hello\n* Unmerged path hello\ndiff --git a/hello b/hello\nindex ba42a2a..cc44c73 100644\n--- a/hello\n+++ b/hello\n@@ -1,2 +1,3 @@\n Hello World\n-Play, play, play\n+It's a new day for git\n+Work, work, work\n$ git diff --theirs hello\n* Unmerged path hello\ndiff --git a/hello b/hello\n------------\n"},{"id":"18901","messageId":"87slo92jz0.wl%cworth@cworth.org","threadId":"3923","inReplyTo":"7vk69l5gi1.fsf@assigned-by-dhcp.cox.net","subject":"Re: [RFC/PATCH] Add git-unresolve <paths>...","fromName":"Carl Worth","fromEmail":"cworth@cworth.org","sentAt":"2006-04-20T00:01:39Z","receivedAt":"2006-04-20T00:01:39Z","isPatch":true,"sender":{"key":"cworth@cworth.org","avatar":"https://gravatar.com/avatar/3746dc28cde609bdbd7f939058356e7e2bbd16d21e32274df0725eb3d998bc5b?d=mp&s=160"},"body":"On Wed, 19 Apr 2006 15:48:22 -0700, Junio C Hamano wrote:\n> Carl Worth <cworth@cworth.org> writes:\n> \n> > Meanwhile, I still think it's worth re-considering the original\n> > problem. \n> \n> Maybe you are misunderstanding something in a major way,...\n\nIt's possible, but it's also possible I'm just failing to express\nmyself clearly.\n\n> > After a failed merge, I get a multi-parent diff from \"git\n> > diff\". However, after updating the index, I can't find any way to get\n> > multi-parent diffs anymore.\n> \n> ... and that is exactly what this \"unmerge to re-populate higher\n> order stages\" does.  The multi-parent diff you have been looking\n> at failed merge _is_ generated by comparing the higher order\n> stages and your working tree file.\n\nBut unmerging puts my index back into a not-ready-to-commit state,\nright? If so, then that's what I don't like from this proposal.\n\n> > I'd still like to be able to do that, even when I know that what I\n> > have in the index is good, and I don't want to undo it.\n> \n> You cannot it have both ways.  Either you want to have the unmerged\n> entries (so that you *can* view the combined diff), or you do\n> not want to have them (then you obviously cannot view the\n> combined diff between the working tree file and these stages).\n\nI don't understand. Why would the entire diff operation have to happen\ninside the index? As soon as I'm done with the merge commit I can do\n\"git show\" and see a multi-parent diff. That doesn't have to muck with\nmy index, does it?\n\nBy, example, (using the merge from the git tutorial), just before\nupdating the index I get a lovely multi-parent diff:\n\n\t$ git diff\n\tdiff --cc hello\n\tindex 06fa6a2,cc44c73..0000000\n\t--- a/hello\n\t+++ b/hello\n\t@@@ -1,3 -1,3 +1,4 @@@\n\t  Hello World\n\t  It's a new day for git\n\t +Play, play, play\n\t+ Work, work, work\n\nthen I can update the index:\n\n\t$ git update-index hello\n\nand at this point I know I can see a single-parent diff before\ncommitting:\n\n\t$ git diff --cached\n\tdiff --git a/hello b/hello\n\tindex 06fa6a2..8798bdc 100644\n\t--- a/hello\n\t+++ b/hello\n\t@@ -1,3 +1,4 @@\n\t Hello World\n\t It's a new day for git\n\t Play, play, play\n\t+Work, work, work\n\nBut what I want to do instead is at this point get the multi-parent\ndiff.\n\nFinally, after I commit, it's easy to get multi-parent diff again:\n\n\t$ git commit -m \"Resolved merge conflict\"\n\t$ PAGER= git show\n\tcommit 547599946cf5dfb04af804e9d5451beb17662dca\n\tMerge: 35e0e9b... 698ac13...\n\tAuthor: Carl Worth <cworth@raht.cworth.org>\n\tDate:   Wed Apr 19 16:51:55 2006 -0700\n\t\n\t    Resolved merge conflict\n\t\n\tdiff --cc hello\n\tindex 06fa6a2,cc44c73..8798bdc\n\t--- a/hello\n\t+++ b/hello\n\t@@@ -1,3 -1,3 +1,4 @@@\n\t  Hello World\n\t  It's a new day for git\n\t +Play, play, play\n\t+ Work, work, work\n\nSo why can't I get that same result before committing without changing\nmy index?\n\nIf the answer is that current implementation doesn't provide for that,\n(that is, it provides either an in-index multi-diff for files in\nmultiple stages, OR it provides a multi-parent diff for a multi-parent\ncommit object), then that's fine. But that doesn't seem like any\nfundamental limitation preventing what I'd like to have.\n\nAm I making more sense now? Or am I still missing something?\n\n-Carl\n"},{"id":"18902","messageId":"87r73t2jd3.wl%cworth@cworth.org","threadId":"3923","inReplyTo":"7vmzeh3ypu.fsf@assigned-by-dhcp.cox.net","subject":"Re: [RFC/PATCH] Add git-unresolve <paths>...","fromName":"Carl Worth","fromEmail":"cworth@cworth.org","sentAt":"2006-04-20T00:14:48Z","receivedAt":"2006-04-20T00:14:48Z","isPatch":true,"sender":{"key":"cworth@cworth.org","avatar":"https://gravatar.com/avatar/3746dc28cde609bdbd7f939058356e7e2bbd16d21e32274df0725eb3d998bc5b?d=mp&s=160"},"body":"On Wed, 19 Apr 2006 16:57:49 -0700, Junio C Hamano wrote:\n> \n> I suspect this is just a misunderstanding caused by insufficient\n> explanation, so let's try this a bit differently.\n\nJunio,\n\nThanks for the careful explanation.\n\n> With the patch applied (or use \"next\" branch I'll be pushing out\n> shortly), let's try the core-tutorial example up to the point\n> where we need to make a merge commit and get conflict.\n\nHeh. We dropped back to the identical example with our\ncrossed-in-flight messages.\n\n> But later (much later) we find out that there was something\n> wrong with this hand-resolve and now we would want to fix it.\n> The new command, \"git unresolve\" is designed to help us exactly\n> in this situation:\n\nYes. And this was in fact the question I had asked in IRC. How to get\nthe diff again when I realize the file I updated is wrong.\n\nAnd as you point out, git-unresolve does just the trick here.\n\nThe question I asked in my latest message is basically just \"Shouldn't\nthere be a way to get the diff from the several parents to the\nindex?\". That's slightly different, but it is the functionality I was\nlooking for when I was trying to recover from my update-of-botched-merge.\n\nAnd its useful separately as part of the pre-commit-review I've said I\nalways want to be able to do, (and as you designed \"git status -v\" to\nprovide).\n\nSo there's the final piece I'd like here. I think \"git status -a -v\"\nshould provide a multi-parent diff when merging, as should \"git status\n-v\" after manually doing an update-index while merging.\n\n-Carl\n"},{"id":"18904","messageId":"7vacah3x35.fsf@assigned-by-dhcp.cox.net","threadId":"3923","inReplyTo":"87r73t2jd3.wl%cworth@cworth.org","subject":"Re: [RFC/PATCH] Add git-unresolve <paths>...","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-04-20T00:33:02Z","receivedAt":"2006-04-20T00:33:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Carl Worth <cworth@cworth.org> writes:\n\n> Thanks for the careful explanation.\n\nYou're welcome.  You owe me a patch that typo-fixes and\npretty-prints my message, and drop it somewhere in\nDocumentation/howto hierarchy for future reference ;-).\n\n> So there's the final piece I'd like here. I think \"git status -a -v\"\n> should provide a multi-parent diff when merging, as should \"git status\n> -v\" after manually doing an update-index while merging.\n\nI'll keep that in mind, but honestly I am not very interested in\nthat particular use case, if only because you can already do\nthat by committing, and running \"git show\".\n\nIf you do not like how the resulting merge commit looks like, we\nhave \"git commit --amend\" and \"git reset --hard HEAD^\" for you\nalready.\n\nI (or Linus) _may_ end up doing \"git diff tree1 tree2 tree3...\"\nas part of the planned diff rewrite, but that would be only\nif/when doing things in such a generic way is not much more\ntrouble than the current \"we only do one of index-vs-files,\ntree-vs-index, tree-vs-files-via-index or tree-vs-tree and\nnothing else\" model.\n"}]}