{"thread":{"id":"36866","subject":"[PATCH v4 2/4] replace: add test for --graft","startedAt":"2014-06-06T20:25:31Z","lastAt":"2014-06-06T20:25:35Z","messageCount":5,"participants":["Christian Couder"],"isPatch":true,"patchVersion":4,"patchTotal":4},"messages":[{"id":"243574","messageId":"20140606201741.3722.96819.chriscool@tuxfamily.org","threadId":"36866","inReplyTo":null,"subject":"[PATCH v4 0/4] Add --graft option to git replace","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2014-06-06T20:25:31Z","receivedAt":"2014-06-06T20:25:31Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"Here is a small patch series to implement:\n\n        git replace [-f] --graft <commit> [<parent>...]\n\nThis patch series goes on top of the patch series that\nimplements --edit.\n\nThe changes since v3, thanks to Junio, are:\n\n- remove function read_sha1_commit() and its call as well as a\n  call to parse_commit_buffer(), as lookup_commit_or_die()\n  already reads the commit buffer and parses it (patch 1/4)\n\n- source git-sh-setup and other small cosmetic changes in\n  convert-grafts-to-replace-refs.sh (patch 4/4)\n\nChristian Couder (4):\n  replace: add --graft option\n  replace: add test for --graft\n  Documentation: replace: add --graft option\n  contrib: add convert-grafts-to-replace-refs.sh\n\n Documentation/git-replace.txt             | 10 +++++\n builtin/replace.c                         | 62 ++++++++++++++++++++++++++++++-\n contrib/convert-grafts-to-replace-refs.sh | 28 ++++++++++++++\n t/t6050-replace.sh                        | 12 ++++++\n 4 files changed, 111 insertions(+), 1 deletion(-)\n create mode 100755 contrib/convert-grafts-to-replace-refs.sh\n\n-- \n2.0.0.rc0.40.gd30ccc4\n"},{"id":"243576","messageId":"20140606202536.3722.29681.chriscool@tuxfamily.org","threadId":"36866","inReplyTo":"20140606201741.3722.96819.chriscool@tuxfamily.org","subject":"[PATCH v4 1/4] replace: add --graft option","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2014-06-06T20:25:32Z","receivedAt":"2014-06-06T20:25:32Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"The usage string for this option is:\n\ngit replace [-f] --graft <commit> [<parent>...]\n\nFirst we create a new commit that is the same as <commit>\nexcept that its parents are [<parents>...]\n\nThen we create a replace ref that replace <commit> with\nthe commit we just created.\n\nWith this new option, it should be straightforward to\nconvert grafts to replace refs.\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\n---\n builtin/replace.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 61 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/replace.c b/builtin/replace.c\nindex 1bb491d..91eda61 100644\n--- a/builtin/replace.c\n+++ b/builtin/replace.c\n@@ -17,6 +17,7 @@\n static const char * const git_replace_usage[] = {\n \tN_(\"git replace [-f] <object> <replacement>\"),\n \tN_(\"git replace [-f] --edit <object>\"),\n+\tN_(\"git replace [-f] --graft <commit> [<parent>...]\"),\n \tN_(\"git replace -d <object>...\"),\n \tN_(\"git replace [--format=<format>] [-l [<pattern>]]\"),\n \tNULL\n@@ -294,6 +295,54 @@ static int edit_and_replace(const char *object_ref, int force)\n \treturn replace_object_sha1(object_ref, old, \"replacement\", new, force);\n }\n \n+static int create_graft(int argc, const char **argv, int force)\n+{\n+\tunsigned char old[20], new[20];\n+\tconst char *old_ref = argv[0];\n+\tstruct commit *commit;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tstruct strbuf new_parents = STRBUF_INIT;\n+\tconst char *parent_start, *parent_end;\n+\tint i;\n+\n+\tif (get_sha1(old_ref, old) < 0)\n+\t\tdie(_(\"Not a valid object name: '%s'\"), old_ref);\n+\tcommit = lookup_commit_or_die(old, old_ref);\n+\n+\t/* find existing parents */\n+\tstrbuf_addstr(&buf, commit->buffer);\n+\tparent_start = buf.buf;\n+\tparent_start += 46; /* \"tree \" + \"hex sha1\" + \"\\n\" */\n+\tparent_end = parent_start;\n+\n+\twhile (starts_with(parent_end, \"parent \"))\n+\t\tparent_end += 48; /* \"parent \" + \"hex sha1\" + \"\\n\" */\n+\n+\t/* prepare new parents */\n+\tfor (i = 1; i < argc; i++) {\n+\t\tunsigned char sha1[20];\n+\t\tif (get_sha1(argv[i], sha1) < 0)\n+\t\t\tdie(_(\"Not a valid object name: '%s'\"), argv[i]);\n+\t\tlookup_commit_or_die(sha1, argv[i]);\n+\t\tstrbuf_addf(&new_parents, \"parent %s\\n\", sha1_to_hex(sha1));\n+\t}\n+\n+\t/* replace existing parents with new ones */\n+\tstrbuf_splice(&buf, parent_start - buf.buf, parent_end - parent_start,\n+\t\t      new_parents.buf, new_parents.len);\n+\n+\tif (write_sha1_file(buf.buf, buf.len, commit_type, new))\n+\t\tdie(_(\"could not write replacement commit for: '%s'\"), old_ref);\n+\n+\tstrbuf_release(&new_parents);\n+\tstrbuf_release(&buf);\n+\n+\tif (!hashcmp(old, new))\n+\t\treturn error(\"new commit is the same as the old one: '%s'\", sha1_to_hex(old));\n+\n+\treturn replace_object_sha1(old_ref, old, \"replacement\", new, force);\n+}\n+\n int cmd_replace(int argc, const char **argv, const char *prefix)\n {\n \tint force = 0;\n@@ -303,12 +352,14 @@ int cmd_replace(int argc, const char **argv, const char *prefix)\n \t\tMODE_LIST,\n \t\tMODE_DELETE,\n \t\tMODE_EDIT,\n+\t\tMODE_GRAFT,\n \t\tMODE_REPLACE\n \t} cmdmode = MODE_UNSPECIFIED;\n \tstruct option options[] = {\n \t\tOPT_CMDMODE('l', \"list\", &cmdmode, N_(\"list replace refs\"), MODE_LIST),\n \t\tOPT_CMDMODE('d', \"delete\", &cmdmode, N_(\"delete replace refs\"), MODE_DELETE),\n \t\tOPT_CMDMODE('e', \"edit\", &cmdmode, N_(\"edit existing object\"), MODE_EDIT),\n+\t\tOPT_CMDMODE('g', \"graft\", &cmdmode, N_(\"change a commit's parents\"), MODE_GRAFT),\n \t\tOPT_BOOL('f', \"force\", &force, N_(\"replace the ref if it exists\")),\n \t\tOPT_STRING(0, \"format\", &format, N_(\"format\"), N_(\"use this format\")),\n \t\tOPT_END()\n@@ -325,7 +376,10 @@ int cmd_replace(int argc, const char **argv, const char *prefix)\n \t\tusage_msg_opt(\"--format cannot be used when not listing\",\n \t\t\t      git_replace_usage, options);\n \n-\tif (force && cmdmode != MODE_REPLACE && cmdmode != MODE_EDIT)\n+\tif (force &&\n+\t    cmdmode != MODE_REPLACE &&\n+\t    cmdmode != MODE_EDIT &&\n+\t    cmdmode != MODE_GRAFT)\n \t\tusage_msg_opt(\"-f only makes sense when writing a replacement\",\n \t\t\t      git_replace_usage, options);\n \n@@ -348,6 +402,12 @@ int cmd_replace(int argc, const char **argv, const char *prefix)\n \t\t\t\t      git_replace_usage, options);\n \t\treturn edit_and_replace(argv[0], force);\n \n+\tcase MODE_GRAFT:\n+\t\tif (argc < 1)\n+\t\t\tusage_msg_opt(\"-g needs at least one argument\",\n+\t\t\t\t      git_replace_usage, options);\n+\t\treturn create_graft(argc, argv, force);\n+\n \tcase MODE_LIST:\n \t\tif (argc > 1)\n \t\t\tusage_msg_opt(\"only one pattern can be given with -l\",\n-- \n2.0.0.rc0.40.gd30ccc4\n"},{"id":"243573","messageId":"20140606202536.3722.42995.chriscool@tuxfamily.org","threadId":"36866","inReplyTo":"20140606201741.3722.96819.chriscool@tuxfamily.org","subject":"[PATCH v4 2/4] replace: add test for --graft","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2014-06-06T20:25:33Z","receivedAt":"2014-06-06T20:25:33Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"Signed-off-by: Christian Couder <chriscool@tuxfamily.org>\n---\n t/t6050-replace.sh | 12 ++++++++++++\n 1 file changed, 12 insertions(+)\n\ndiff --git a/t/t6050-replace.sh b/t/t6050-replace.sh\nindex 68b3cb2..ca45a84 100755\n--- a/t/t6050-replace.sh\n+++ b/t/t6050-replace.sh\n@@ -351,4 +351,16 @@ test_expect_success 'replace ref cleanup' '\n \ttest -z \"$(git replace)\"\n '\n \n+test_expect_success '--graft with and without already replaced object' '\n+\ttest $(git log --oneline | wc -l) = 7 &&\n+\tgit replace --graft $HASH5 &&\n+\ttest $(git log --oneline | wc -l) = 3 &&\n+\tgit cat-file -p $HASH5 | test_must_fail grep parent &&\n+\ttest_must_fail git replace --graft $HASH5 $HASH4 $HASH3 &&\n+\tgit replace --force -g $HASH5 $HASH4 $HASH3 &&\n+\tgit cat-file -p $HASH5 | grep \"parent $HASH4\" &&\n+\tgit cat-file -p $HASH5 | grep \"parent $HASH3\" &&\n+\tgit replace -d $HASH5\n+'\n+\n test_done\n-- \n2.0.0.rc0.40.gd30ccc4\n"},{"id":"243577","messageId":"20140606202536.3722.22418.chriscool@tuxfamily.org","threadId":"36866","inReplyTo":"20140606201741.3722.96819.chriscool@tuxfamily.org","subject":"[PATCH v4 3/4] Documentation: replace: add --graft option","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2014-06-06T20:25:34Z","receivedAt":"2014-06-06T20:25:34Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"Signed-off-by: Christian Couder <chriscool@tuxfamily.org>\n---\n Documentation/git-replace.txt | 8 ++++++++\n 1 file changed, 8 insertions(+)\n\ndiff --git a/Documentation/git-replace.txt b/Documentation/git-replace.txt\nindex 61461b9..491875e 100644\n--- a/Documentation/git-replace.txt\n+++ b/Documentation/git-replace.txt\n@@ -10,6 +10,7 @@ SYNOPSIS\n [verse]\n 'git replace' [-f] <object> <replacement>\n 'git replace' [-f] --edit <object>\n+'git replace' [-f] --graft <commit> [<parent>...]\n 'git replace' -d <object>...\n 'git replace' [--format=<format>] [-l [<pattern>]]\n \n@@ -73,6 +74,13 @@ OPTIONS\n \tnewly created object. See linkgit:git-var[1] for details about\n \thow the editor will be chosen.\n \n+--graft <commit> [<parent>...]::\n+\tCreate a graft commit. A new commit is created with the same\n+\tcontent as <commit> except that its parents will be\n+\t[<parent>...] instead of <commit>'s parents. A replacement ref\n+\tis then created to replace <commit> with the newly created\n+\tcommit.\n+\n -l <pattern>::\n --list <pattern>::\n \tList replace refs for objects that match the given pattern (or\n-- \n2.0.0.rc0.40.gd30ccc4\n"},{"id":"243575","messageId":"20140606202536.3722.64861.chriscool@tuxfamily.org","threadId":"36866","inReplyTo":"20140606201741.3722.96819.chriscool@tuxfamily.org","subject":"[PATCH v4 4/4] contrib: add convert-grafts-to-replace-refs.sh","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2014-06-06T20:25:35Z","receivedAt":"2014-06-06T20:25:35Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"This patch adds into contrib/ an example script to convert\ngrafts from an existing grafts file into replace refs using\nthe new --graft option of \"git replace\".\n\nWhile at it let's mention this new script in the\n\"git replace\" documentation for the --graft option.\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\n---\n Documentation/git-replace.txt             |  4 +++-\n contrib/convert-grafts-to-replace-refs.sh | 28 ++++++++++++++++++++++++++++\n 2 files changed, 31 insertions(+), 1 deletion(-)\n create mode 100755 contrib/convert-grafts-to-replace-refs.sh\n\ndiff --git a/Documentation/git-replace.txt b/Documentation/git-replace.txt\nindex 491875e..e1be2e6 100644\n--- a/Documentation/git-replace.txt\n+++ b/Documentation/git-replace.txt\n@@ -79,7 +79,9 @@ OPTIONS\n \tcontent as <commit> except that its parents will be\n \t[<parent>...] instead of <commit>'s parents. A replacement ref\n \tis then created to replace <commit> with the newly created\n-\tcommit.\n+\tcommit. See contrib/convert-grafts-to-replace-refs.sh for an\n+\texample script based on this option that can convert grafts to\n+\treplace refs.\n \n -l <pattern>::\n --list <pattern>::\ndiff --git a/contrib/convert-grafts-to-replace-refs.sh b/contrib/convert-grafts-to-replace-refs.sh\nnew file mode 100755\nindex 0000000..0cbc917\n--- /dev/null\n+++ b/contrib/convert-grafts-to-replace-refs.sh\n@@ -0,0 +1,28 @@\n+#!/bin/sh\n+\n+# You should execute this script in the repository where you\n+# want to convert grafts to replace refs.\n+\n+GRAFTS_FILE=\"${GIT_DIR:-.git}/info/grafts\"\n+\n+. $(git --exec-path)/git-sh-setup\n+\n+test -f \"$GRAFTS_FILE\" || die \"Could not find graft file: '$GRAFTS_FILE'\"\n+\n+grep '^[^# ]' \"$GRAFTS_FILE\" |\n+while read definition\n+do\n+\tif test -n \"$definition\"\n+\tthen\n+\t\techo \"Converting: $definition\"\n+\t\tgit replace --graft $definition ||\n+\t\t\tdie \"Conversion failed for: $definition\"\n+\tfi\n+done\n+\n+mv \"$GRAFTS_FILE\" \"$GRAFTS_FILE.bak\" ||\n+\tdie \"Could not rename '$GRAFTS_FILE' to '$GRAFTS_FILE.bak'\"\n+\n+echo \"Success!\"\n+echo \"All the grafts in '$GRAFTS_FILE' have been converted to replace refs!\"\n+echo \"The grafts file '$GRAFTS_FILE' has been renamed: '$GRAFTS_FILE.bak'\"\n-- \n2.0.0.rc0.40.gd30ccc4\n"}]}