{"thread":{"id":"47325","subject":"[SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge, no working tree file changes)","startedAt":"2017-11-26T23:01:30Z","lastAt":"2017-12-11T01:13:27Z","messageCount":26,"participants":["Igor Djordjevic","Johannes Sixt","Chris Nerwert","Junio C Hamano","Alexei Lozovsky","Phillip Wood"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"333555","messageId":"8998e832-f49f-4de4-eb8d-a7934fba97b5@gmail.com","threadId":"47325","inReplyTo":null,"subject":"[SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge, no working tree file changes)","fromName":"Igor Djordjevic","fromEmail":"igor.d.djordjevic@gmail.com","sentAt":"2017-11-26T22:35:36Z","receivedAt":"2017-11-26T23:01:30Z","isPatch":false,"sender":{"key":"igor.d.djordjevic@gmail.com","avatar":null},"body":"Hi to all,\n\nHere`s my humble attempt on (once more) scratching a use case which \nseems to be desired occasionally through the years, obviously not \nenough to be actually implemented yet, but might be worth some more \nattention... :)\n\nFor the reference, some past mentions of (more or less) similar ideas \nare listed at the end of this e-mail[*1*], where I`m also Cc-ing \npeople previously involved, might be still sharing interest - as this \nis my first \"new thread\" list message, I`m sorry if this is not an \nencouraged practice, please let me know.\n\nApproach discussed here could have a few more useful applications, \nbut one seems to be standing out the most - in case where multiple \ntopic branches are temporarily merged for integration testing, it \ncould be very useful to be able to post \"hotfix\" commits to merged \nbranches directly, _without actually switching to them_ (and thus \nwithout touching working tree files), and still keeping everything \nmerged, in one go.\n\nExample starting point is \"master\" branch with 3 topic branches (A, \nB, C), to be (throwaway) merged for integration testing inside \ntemporary \"test\" branch:\n\n(1)        o---o---A (topicA)\n          /\n         /\n        /\n    ---o---o---M (master, test, HEAD)\n        \\   \\\n         \\   o---B (topicB)\n          \\\n           o---o---C (topicC)\n\n\nThis is what we end up with once \"master\" and topic branches are \nmerged in merge commit M1 inside temporary \"test\" branch for further \nintegration testing:\n\n(2)        o---o---A (topicA)\n          /         \\\n         /           M1 (test, HEAD)\n        /           /||\n    ---o---o---M---/ || (master)\n        \\   \\       / |\n         \\   o---B-/  | (topicB)\n          \\           |\n           o---o---C--/ (topicC)\n\n\nUpon discovery of a fix needed inside \"topicA\", hotfix changes X \nshould be committed to \"topicA\" branch and re-merged inside merge \ncommit M2 on temporary integration \"test\" branch (previous temporary \nmerge commit M1 is thrown away as uninteresting):\n\n(3)        o---o---A---X (topicA)\n          /             \\\n         /               M2 (test, HEAD)\n        /               /||\n    ---o---o---M-------/ || (master)\n        \\   \\           / |\n         \\   o---B-----/  | (topicB)\n          \\              /\n           o---o---C----/ (topicC)\n\n\nNow, usually advised approach to get from (2) to (3), where one is \nexpected to switch to desired topic branch, commit the change, and \nthen re-merge everything again into integration testing branch, is \narguably a bit tiresome, but where it actually fails short is the \nfact that branch switching back and forth (for each hotfix commit) \ncould possibly keep changing a lot of files otherwise untouched by \nthe hotfix changes we`re committing (but different between branches), \ncausing build systems to needlessly waste what could be significant \ntime compiling them again and again.\n\nExample script proposes using something like this instead:\n\n(4) git commit --onto-parent topicA\n     \n... in above-mentioned case (2) to commit X onto \"topicA\" branch \ndirectly, re-merging all previously merged integration testing topic \nbranches at the same time, reaching state (3) without any \nintermediate branch switching (and without touching working tree, \nthus without needless recompilation).\n\nOnce integration tests pass, integration test branch will be thrown \naway and new commits on each topic branch should still be properly \ntested - we`re just deferring it not to do it in the middle of \nintegration testing, saving some (or a lot) needless rebuild cycles \nat the same time (or in the first place, even).\n\nScripts in series:\n  [1/3]: setup.sh\n  [2/3]: git-merge-one-file--cached\n  [3/3]: git-commit--onto-parent.sh\n\nRegards, Buga\n\n[*1*] Some previous list mentions of similar functionality, in order \n of appearance, latest on top (I kind of remember seeing more, but \n can`t find them now, please feel free to add here, or notify more \n people interested in the past):\n\n - [PATCH/RFC] git-post: the opposite of git-cherry-pick (2017-10-05)\n   https://public-inbox.org/git/c6b52120-98bf-d685-6dc0-3c83e9e80d30@kdbg.org/\n\n - \"groups of files\" in Git? (2017-07-11)\n   https://public-inbox.org/git/CAEcERAz3vYekvJ8SM1FfdAVsP3LMVqA1O3yoJVThvg-0fPtVCg@mail.gmail.com/\n\n - Making a (quick) commit to another branch (2013-04-27)\n   https://public-inbox.org/git/517BDB6D.8040809@cedarsoft.com/\n\n - Commit to other branch (2010-05-31)\n   https://public-inbox.org/git/4C03D9C1.1060404@cedarsoft.com/\n\n - [RFC] git commit --branch (2006-05-29)\n   https://public-inbox.org/git/20060529202851.GE14325@admingilde.org/\n\n - n-heads and patch dependency chains (2006-04-03)\n   https://public-inbox.org/git/4430D352.4010707@vilain.net/\n\n - Multi-headed branches (hydra? :)) for basic patch calculus (2006-04-02)\n   https://public-inbox.org/git/1143950852.21233.23.camel@localhost.localdomain/\n"},{"id":"333556","messageId":"397276a1-8f2b-117d-ce51-84ab832ce562@gmail.com","threadId":"47325","inReplyTo":"8998e832-f49f-4de4-eb8d-a7934fba97b5@gmail.com","subject":"[SCRIPT/RFC 1/3] setup.sh","fromName":"Igor Djordjevic","fromEmail":"igor.d.djordjevic@gmail.com","sentAt":"2017-11-26T22:36:20Z","receivedAt":"2017-11-26T23:01:40Z","isPatch":false,"sender":{"key":"igor.d.djordjevic@gmail.com","avatar":null},"body":"On 26/11/2017 23:35, Igor Djordjevic wrote:\n> \n> This is what we end up with once \"master\" and topic branches are \n> merged in merge commit M1 inside temporary \"test\" branch for further \n> integration testing:\n> \n> (2)        o---o---A (topicA)\n>           /         \\\n>          /           M1 (test, HEAD)\n>         /           /||\n>     ---o---o---M---/ || (master)\n>         \\   \\       / |\n>          \\   o---B-/  | (topicB)\n>           \\           |\n>            o---o---C--/ (topicC)\n\nTo begin with, you can use provided \"setup.sh\"[*1*] script, putting \nyou straight into position shown on graph (2) above, with addition of \ntag \"A\" and remote branch \"origin/topicA\" so you could try using \nthese as \"--onto-parent\" values, too.\n\nAs seen in there, change \"X\" is already made and staged, so you can \nnow just run something like:\n\n\tgit-commit--onto-parent.sh --onto-parent topicA\n\n... to see the logic in action.\n \nInstead of \"topicA\", you may try providing tag \"A\", remote branch \n\"origin/topicA\" or even plain commit hash. It`s interesting to add \n\"--amend\" option into the mix, too, and see what happens. Also, you \ncan try using \"topicB\" and see the commit fail (as it doesn`t merge \ncleanly).\n\nAll this while \"test.txt\" file doesn`t get modified on disk (nor \nwould any other file) - being desired behaviour, as we didn`t \nactually change anything inside the working tree, but just amended \nhistory of how we got here, so recompilation isn`t needlessly \ntriggered :)\n\np.s. Note these two lines near the end:\n\n\tsed -i '4iX1' test.txt  # works with simple patch apply\n\tsed -i '17iX2' test.txt # needs three-way file merge\n\nYou can play with it, commenting out one or the other and observing \nhow it influences \"git commit --onto-parent\" in regards of the parent \nprovided.\n\nRegards, Buga\n\n[*1*] \"setup.sh\", can clean previous setup run as well, but commented \n out here for safety, not to unexpectedly delete something for unwary \n user.\n--- 8< ---\n#!/bin/sh\n\n#rm -rf ./.git\n#rm -f ./test.txt\n\ngit init\n\ntouch ./test.txt\ngit add -- test.txt\n\nfor i in {1..10}\ndo\n\techo $i >>test.txt\t\n\tgit commit -am \"$i\"\ndone\n\necho M >>test.txt\ngit commit -am \"M\"\n\ngit checkout -b topicA HEAD~2\n\nfor i in 1 2\ndo\n\tsed -i \"${i}iA${i}\" test.txt\n\tgit commit -am \"A$i\"\ndone\nsed -i '3iA' test.txt\ngit commit -am \"A\"\ngit tag A\n\n# simulate remote branch\nmkdir -p ./.git/refs/remotes/origin &&\necho $(git rev-parse HEAD^0) >$_/topicA\n\ngit checkout -b topicB master^\n\nsed -i '4iB1' test.txt\ngit commit -am \"B1\"\nsed -i '5iB' test.txt\ngit commit -am \"B\"\n\ngit checkout -b topicC master~2\n\nfor i in 1 2\ndo\n\tj=`expr \"$i\" + 5`\n\tsed -i \"${j}iC${i}\" test.txt\n\tgit commit -am \"C$i\"\ndone\nsed -i \"8iC\" test.txt\ngit commit -am \"C\"\n\ngit checkout -b test master\ngit merge --no-edit topicA topicB topicC\n\nsed -i '4iX1' test.txt  # works with simple patch apply\nsed -i '17iX2' test.txt # needs three-way file merge\ngit add -- test.txt\n\necho\ngit log --all --decorate --oneline --graph\necho\ngit diff --cached\n"},{"id":"333557","messageId":"7ea28777-1e68-09e3-5f39-4ca0291e3f36@gmail.com","threadId":"47325","inReplyTo":"8998e832-f49f-4de4-eb8d-a7934fba97b5@gmail.com","subject":"[SCRIPT/RFC 3/3] git-commit--onto-parent.sh","fromName":"Igor Djordjevic","fromEmail":"igor.d.djordjevic@gmail.com","sentAt":"2017-11-26T22:45:10Z","receivedAt":"2017-11-26T23:01:42Z","isPatch":false,"sender":{"key":"igor.d.djordjevic@gmail.com","avatar":null},"body":"Finally, \"git-commit--onto-parent.sh\"[*1*] shows an initial script \nversion for you to examine, test out and hopefully comment on :)\n\nEspecially interesting part might be index-only three-way file merge, \nthrough usage of \"git-merge-one-file--cached\" script. Of course, this \nstill only works for some trivial resolutions, where in case of more \ncomplex ones, involving unresolved conflicts, we back-out and fail. \nStill, it should be more powerful than `git-apply`.\n\nConsider this proof of concept and work in progress, an idea where \nI`d like feedback on everything you come up with or find interesting, \neven parameter name possibly used instead of \"--onto-parent\" (and its \nshort version), or approach in general.\n\nFor example, it might make sense to separate commit creation (on \ncurrent HEAD`s parent) and its actual re-merging into integration \ntest branch, where \"--remerge\" (or something) parameter would be used \non top of \"--onto-parent\" to trigger both, if/when desired.\n\nAnother direction to think in might be introducing more general \n\"--onto\" parameter, too (or instead), without \"parent\" restriction, \nallowing to record a commit on top of any arbitrary commit (other \nthan HEAD). This could even be defaulted to \"git commit <commit-ish>\" \n(no option needed), where current \"git commit\" behaviour would then \njust be a special case of omitted <commit-ish> defaulting to HEAD, \naligning well with other Git commands sharing the same behaviour.\n\nAlas, rewind to present...\n\nPlease do note that I`m still relatively new to Git, and pretty new \nto both Linux and scripting in general (on Windows as well), and the \nwhole concept of open-source software contributing, even, so please \nbare with me (or at least don`t get upset too much, lol), and do feel \nfree to share your thoughts and remarks, even the trivial or harsh \nones -- I`m grateful to learn and expand my knowledge, hopefully \nproducing something useful in return :) Heck, might be I`m totally \noff-track here as well.\n\np.s. For some context - nowadays I mostly work in Delphi, and \noccasionally in C#, though through last 20 years I`ve been involved \nwith C, Pascal, Basic, but also PHP, JavaScript, and whatnot - even \ngood old assembly from time to time, when needed :)\n\nRegards, Buga\n\n[*1*] \"git-commit--onto-parent.sh\", probably too heavily commented in \n the first place, but as I`m new to everything here I kind of feel the \n plain words might unfortunately describe my intention a bit better \n than my code, for now at least.\n--- 8< ---\n#!/bin/sh\n#\n# Copyright (c) 2017 Igor Djordjevic\n\ni=$#\nwhile test $i != 0\ndo\n\t#\n\t# Parameter parsing might be uninteresting here, as the whole\n\t# script is currently just a wrapper around `git commit`, for a\n\t# functionality that conceptually belongs there directly.\n\t#\n\tcase \"$1\" in\n\t--onto-parent=*)\n\t\tonto_parent=\"${1#*=}\"\n\n\t\tshift && i=$(expr $i - 1)\n\t\t;;\n\t--onto-parent)\n\t\tshift && i=$(expr $i - 1)\n\t\tonto_parent=\"$1\"\n\n\t\tshift && i=$(expr $i - 1)\n\t\t;;\n\t-a|--a|--al|--all)\n\t\tall=t\n\n\t\t#\n\t\t# For now, `git commit` \"--all\" option is special-cased in\n\t\t# terms of being stripped out of the original command line\n\t\t# (to be passed to `git commit`) and processed manually, as\n\t\t# once commit is to be made, due to states of index and\n\t\t# working tree, \"--all\" is most probably NOT what the user\n\t\t# wants nor expects ;)\n\t\t#\n\t\tshift && i=$(expr $i - 1)\n\t\t;;\n\t*)\n\t\t# parameters to pass down to `git commit`\n\t\tset -- \"$@\" \"$1\"\n\t\tshift && i=$(expr $i - 1)\n\t\t;;\n\tesac\ndone\n\nmain () {\n\t#\n\t# Store current HEAD (ref or commit) and verify that\n\t# --onto-parent is valid and amongst its parents.\n\t#\n\thead=\"$(git symbolic-ref --short --quiet HEAD)\" ||\n\thead=\"$(git rev-parse HEAD^0)\" &&\n\tverify_onto_parent \"$head\" \"$onto_parent\" || exit 1\n\n\t#\n\t# As both HEAD and \"--onto-parent\" could be refs, where underlying\n\t# commits could change, store original commits for later parents\n\t# processing, getting updated parent list for new/updated\n\t# merge commit.\n\t#\n\thead_commit=\"$(git rev-parse \"$head\"^0)\" &&\n\tonto_parent_commit=\"$(git rev-parse \"$onto_parent\"^0)\" || exit 1\n\n\t#\n\t# Custom processing of stripped \"--all\" parameter - if we were to\n\t# just pass it to `git commit`, \"--all\" would most probably yield\n\t# an unexpected result in the eyes of the user, as it would include\n\t# _all changes from all the other merge commit parents as well_,\n\t# not just the changes we may actually wanted to \"push down\"\n\t# (commit) onto specified parent (what would `git diff` show),\n\t# due to state of index and working tree at the time of commit.\n\t#\n\tif test -n \"$all\"\n\tthen\n\t\tgit add --update\n\tfi\n\n\t#\n\t# Abort if no cached changes, nothing to be committed.\n\t#\n\tgit diff --cached --quiet\n\tif test $? -eq 0\n\tthen\n\t\tprintf >&2 '%s\\n' \"error: no changes added to commit\"\n\t\texit 1\n\tfi\n\n\t#\n\t# Backup current index to be restored (and committed) in the end.\n\t#\n\tmerge_index=\"$(git write-tree)\" || exit 1\n\n\t#\n\t# Reset index to destination parent (without touching working\n\t# tree), and try applying cached changes.\n\t#\n\t# In case changes do not apply cleanly onto desired parent, abort.\n\t#\n\tapply_changes \"$head_commit\" \"$onto_parent_commit\" \"$merge_index\" \"$onto_parent\" || exit 1\n\n\t#\n\t# Move HEAD to specified parent (without touching working tree)\n\t# to prepare to record a commit there, and make the commit,\n\t# passing parameters through to `git commit`.\n\t#\n\t# In case of error, or when `git commit` didn`t actually make\n\t# the commit, like when --dry-run parameter is provided (for\n\t# example), abort, as there is nothing to do - no new commit, no\n\t# need to produce the \"updated\" merge commit, either.\n\t#\n\t# Note that we don`t abort right away, as restoring original\n\t# index and HEAD position is needed all the same, so we\n\t# potentially abort only once that is done, a bit further below.\n\t#\n\t# [ This is something that could be thought of a bit more,\n\t# might be forbidding passing through of some `git commit`\n\t# parameters in the first place, like --dry-run...? ]\n\t#\n\tmove_head \"$onto_parent\" &&\n\tgit commit \"$@\"\n\tif test $? -ne 0 || {\n\t\tnew_parent_commit=\"$(git rev-parse HEAD^0)\"\n\t\ttest \"$new_parent_commit\" = \"$onto_parent_commit\"\n\t}\n\tthen\n\t\tno_commit=t\n\tfi\n\n\t#\n\t# Remove entry from HEAD reflog, not to pollute it with\n\t# uninteresting in-between steps we take, leaking implementation\n\t# details to end user.\n\t#\n\t# We do left it inside corresponding branch reflog where commit\n\t# is made (if $onto_parent was a branch), though, as that`s where\n\t# it still matters.\n\t#\n\tgit reflog delete HEAD@{0}\n\n\t#\n\t# Restore original index state and move HEAD to original position,\n\t# (still not touching working tree), aborting if previously\n\t# signalled.\n\t#\n\tgit read-tree --reset \"$merge_index\" &&\n\tmove_head \"$head\" &&\n\ttest -z \"$no_commit\" || exit 1\n\n\t#\n\t# Drop original HEAD merge commit to have it replaced by\n\t# upcoming \"updated\" merge commit.\n\t#\n\t# This step is needed for eventually getting an expected merge\n\t# message out of \"git fmt-merge-msg\", as it seems HEAD dependent\n\t# as well, beside being input format picky already...?\n\t#\n\t#git update-ref --create-reflog -m \"reset: moving to HEAD^\" HEAD HEAD^ || exit 1\n\treflog_ref=\"$(git symbolic-ref --short --quiet HEAD)\"\n\tgit update-ref HEAD HEAD^ || exit 1\n\n\t#\n\t# Remove both HEAD and underlying reference reflog entries this\n\t# time, as here we really want to mask previous step completely,\n\t# being taken just to satisfy \"git fmt-merge-msg\" expectations.\n\t#\n\tif test -n \"$reflog_ref\"\n\tthen\n\t\tgit reflog delete \"$reflog_ref\"@{0}\n\tfi\n\tgit reflog delete HEAD@{0}\n\n\t#\n\t# Prepare \"updated\" merge commit message and parent list.\n\t#\n\tif test -n \"$(git rev-parse --verify --quiet $head_commit^2^{commit})\"\n\tthen\n\t\tmerge_parents=\"$(get_merge_parents \"$head_commit\" \"$onto_parent\" \"$onto_parent_commit\" \"$new_parent_commit\")\" &&\n\t\tmerge_message=\"$(get_merge_message \"$merge_parents\")\" || exit 1\n\telse\n\t\t#\n\t\t# As we`re actually selling the option as \"--onto-parent\" and\n\t\t# not \"--onto-MERGE-parent\", we might as well properly support\n\t\t# a special case where HEAD commit is not a merge.\n\t\t#\n\t\t# Existing HEAD commit will come after commit to be made,\n\t\t# basically being kind of rebased onto new commit (but still\n\t\t# not touching working tree), where we can then also reuse\n\t\t# original HEAD commit authorship, and message, too (instead\n\t\t# of building a merge one).\n\t\t#\n\t\tmerge_parents=\"$new_parent_commit\" &&\n\t\tmerge_message=\"$(git show -s --format=%B \"$head_commit\")\" &&\n\t\treuse_authorship $head_commit || exit 1\n\tfi\n\tmerge_parent_commits=\"$(get_merge_parent_commits \"$merge_parents\")\" &&\n\n\t#\n\t# Do the actual commit, updating HEAD accordingly.\n\t#\n\tmerge_commit=\"$(printf '%s\\n' \"$merge_message\" |\n\tgit commit-tree \"$merge_index\" $merge_parent_commits)\" &&\n\tgit update-ref --create-reflog -m \"$merge_message\" HEAD \"$merge_commit\" || exit 1\n}\n\nverify_onto_parent () {\n\t#\n\t# $1 starting point head (ref or commit)\n\t# $2 parent of $1 to commit onto (ref or commit)\n\t#\n\tlocal head=\"$1\"\n\tlocal onto_parent=\"$2\"\n\n\tif test -z \"$onto_parent\"\n\tthen\n\t\tprintf >&2 '%s\\n' \"error: no parent provided\"\n\t\tprintf >&2 '%s\\n' \"(use \\\"--onto-parent <commit-ish>\\\"\"\n\t\treturn 1;\n\tfi\n\n\tif test -z \"$(git rev-parse --verify --quiet \"$onto_parent\"^{commit})\"\n\tthen\n\t\tprintf >&2 '%s\\n' \"error: '$onto_parent' not valid commit object\"\n\t\treturn 1\n\tfi\n\n\tlocal onto_parent_commit=\"$(git rev-parse \"$onto_parent\"^0)\"\n\tfor parent_commit in $(git rev-parse $head^@)\n\tdo\n\t\tif test \"$parent_commit\" = \"$onto_parent_commit\"\n\t\tthen\n\t\t\treturn 0\n\t\tfi\n\tdone\n\n\tprintf >&2 '%s\\n' \"error: '$onto_parent' not parent of '$head'\"\n\treturn 1\n}\n\napply_changes () {\n\t#\n\t# $1 original/starting point HEAD commit\n\t# $2 parent commit of $1 to apply changes to\n\t# $3 index with changes on top of $1 to apply/merge onto $2\n\t# $4 original parameter value of $2 (ref or commit), used for\n\t#    prettier message only\n\t#\n\tlocal head_commit=\"$1\"\n\tlocal onto_parent_commit=\"$2\"\n\tlocal merge_index=\"$3\"\n\tlocal onto_parent=\"$4\"\n\n\tgit read-tree --reset $onto_parent_commit &&\n\n\t#\n\t# Attempt simple patching first - take differences between\n\t# $head_commit and $merge_index and try applying to current index\n\t# (previously reset to $onto_parent_commit).\n\t#\n\tgit diff-tree --binary --patch --find-renames --find-copies $head_commit $merge_index |\n\tgit apply --cached 2>/dev/null &&\n\treturn 0\n\n\tprintf '%s\\n' \"Unable to apply cleanly onto '$onto_parent', trying simple merge\"\n\n\t#\n\t# A bit more aggressive approach - try merging with resolving\n\t# trivial conflicts on tree level only (involving file as a whole,\n\t# no conflicts inside file itself).\n\t#\n\t# Note that we take $head_commit as merge-base, producing such\n\t# three-way merge result that basically all changes between\n\t# $onto_parent_commit and $head_commit are reversed, as they`re\n\t# also included inside $merge_index, where only differences\n\t# between $head_commit and $merge_index are applied (in a\n\t# three-way merge manner) to $onto_parent_commit, being exactly\n\t# what we want here.\n\t#\n\tgit read-tree -i -m --aggressive $head_commit $onto_parent_commit $merge_index || exit 1\n\tgit write-tree >/dev/null 2>&1 &&\n\treturn 0\n\n\tprintf '%s\\n' \"Simple merge did not work, trying automatic merge\"\n\n\t#\n\t# Final attempt - try merging with resolving trivial conflicts on\n\t# file level, too (conflicts inside file itself).\n\t#\n\t# Notice usage of \"git-merge-one-file--cached\" script here, being\n\t# a slightly tweaked version of original \"git-merge-one-file\",\n\t# not touching working tree but stuffing trivial three-way\n\t# file merge resolution back into index directly.\n\t#\n\t# If still left with conflicts that need to be resolved manually,\n\t# abort... and go home, you`re drunk.\n\t#\n\tif ! git merge-index -o git-merge-one-file--cached -a\n\tthen\n\t\t# abort, cleanup\n\t\tgit read-tree --reset $merge_index\n\t\texit 1\n\tfi\n}\n\nmove_head () {\n\t#\n\t# $1 destination ref or commit\n\t#\n\t# Move HEAD to $1 without touching the working tree.\n\t#\n\t# Kind of \"soft checkout\", where original \"git checkout\" touches\n\t# the working tree, and \"git reset --soft\" does not move HEAD,\n\t# both undesired here.\n\t#\n\tlocal destination=\"$1\"\n\n\tlocal destination_commit=\"$(git rev-parse --verify --quiet $destination^0)\" ||\n\t{\n\t\tprintf >&2 '%s\\n' \"fatal: invalid reference: $destination\"\n\t\treturn 1\n\t}\n\t#local reflog_message=\"$(get_checkout_reflog_message $destination)\"\n\tlocal destination_ref=\"$(git rev-parse --symbolic-full-name $destination)\"\n\n\tcase \"$destination_ref\" in\n\trefs/heads/*)\n\t\t# can`t use \"update-ref --no-deref\" as it writes commit only,\n\t\t# instead of ref, essentially detaching HEAD to that commit\n\t\t#git symbolic-ref -m \"$reflog_message\" HEAD \"$destination_ref\"\n\t\tgit symbolic-ref HEAD \"$destination_ref\"\n\t\t;;\n\trefs/tags/*|\\\n\trefs/remotes/*|\\\n\t\"\")\n\t\t# can`t use \"symbolic-ref\" as it refuses to write commit only,\n\t\t# expecting a valid ref instead (value inside \"refs/\")\n\t\t#git update-ref --create-reflog -m \"$reflog_message\" --no-deref HEAD \"$destination_commit\"\n\t\tgit update-ref --no-deref HEAD \"$destination_commit\"\n\n\t\t# mask this step as end-user uninteresting implementation detail\n\t\tgit reflog delete HEAD@{0}\n\t\t;;\n\t*)\n\t\tprintf >&2 '%s\\n' \"fatal: invalid reference: $destination_ref\"\n\t\treturn 1\n\t\t;;\n\tesac\n\n\treturn 0\n}\n\nget_merge_parents () {\n\t#\n\t# $1 original/starting point HEAD commit\n\t# $2 parent of $1 to commit onto (ref or commit)\n\t# $3 original commit of $2 (if $2 is ref, otherwise equals $2)\n\t# $4 new commit (onto $2, to be new merge parent)\n\t#\n\t# Walk original merge commit parents to find the one we`re posting\n\t# onto (or amending, even), and update/replace it accordingly with\n\t# new commit, becoming a new parent of upcoming new/updated merge\n\t# commit.\n\t#\n\t# Where possible, prefer taking ref over commit, making for a\n\t# prettier merge commit message.\n\t#\n\tlocal head_commit=\"$1\"\n\tlocal onto_parent=\"$2\"\n\tlocal onto_parent_old_commit=\"$3\"\n\tlocal new_parent_commit=\"$4\"\n\tlocal merge_parents=\n\n\tlocal onto_parent_new_commit=\"$(git rev-parse $onto_parent^0)\"\n\n\tfor parent_commit in $(git rev-parse $head_commit^@)\n\tdo\n\t\tlocal merge_parent=\n\n\t\tif test \"$parent_commit\" = \"$onto_parent_old_commit\"\n\t\tthen\n\t\t\tif test \"$onto_parent_new_commit\" = \"$new_parent_commit\"\n\t\t\tthen\n\t\t\t\t# $onto_parent is a branch (updateable ref)\n\t\t\t\tmerge_parent=\"$onto_parent\"\n\t\t\telse\n\t\t\t\tmerge_parent=\"$new_parent_commit\"\n\t\t\tfi\n\t\telse\n\t\t\tparent_ref=\"$(git for-each-ref --points-at $parent_commit --count=1 --format=\"%(refname)\")\"\n\t\t\tif test -n \"$parent_ref\"\n\t\t\tthen\n\t\t\t\tmerge_parent=\"$parent_ref\"\n\t\t\telse\n\t\t\t\tmerge_parent=\"$parent_commit\"\n\t\t\tfi\n\t\tfi\n\n\t\t# echo to flatten whitespace\n\t\tmerge_parents=\"$(echo $merge_parents $merge_parent)\"\n\tdone\n\n\tif test -n \"$merge_parents\"\n\tthen\n\t\tprintf '%s\\n' \"$merge_parents\"\n\t\treturn 0\n\telse\n\t\treturn 1\n\tfi\n}\n\nget_merge_message () {\n\t#\n\t# $@ merge_parents\n\t#\n\t# Provide to-be merge commit message using\n\t# existing `git fmt-merge-msg` machinery.\n\t#\n\tlocal merge_heads=\"$(get_merge_heads $@)\" &&\n\tlocal merge_message=\"$(printf \"$merge_heads\" | git fmt-merge-msg)\"\n\n\tif test -n \"$merge_message\"\n\tthen\n\t\tprintf '%s\\n' \"$merge_message\"\n\t\treturn 0\n\telse\n\t\treturn 1\n\tfi\n}\n\nget_merge_heads () {\n\t#\n\t# $@ merge_parents\n\t#\n\t# Provide input for `git fmt-merge-msg` to get\n\t# nicely formatted merge commit message.\n\t#\n\t# Final result loosely mimics FETCH_HEAD file layout.\n\t#\n\tlocal merge_heads=\n\tlocal merge_head=\n\n\t#\n\t# Skip the first parent, as that is the original merge\n\t# destination, where we`re only interested in parents to be\n\t# merged into it.\n\t#\n\tshift\n\n\tfor merge_parent in $@\n\tdo\n\t\tlocal merge_parent_ref=\"$(git rev-parse --symbolic-full-name $merge_parent)\"\n\t\tlocal merge_parent_commit=\"$(git rev-parse $merge_parent)\"\n\n\t\tcase \"$merge_parent_ref\" in\n\t\trefs/heads/*)\n\t\t\tmerge_head=\"$merge_parent_commit\\t\\tbranch '${merge_parent_ref#refs/heads/}' of .\"\n\t\t\t;;\n\t\trefs/tags/*)\n\t\t\tmerge_head=\"$merge_parent_commit\\t\\ttag '${merge_parent_ref#refs/tags/}' of .\"\n\t\t\t;;\n\t\trefs/remotes/*)\n\t\t\tmerge_head=\"$merge_parent_commit\\t\\tremote-tracking branch '${merge_parent_ref#refs/remotes/}' of .\"\n\t\t\t;;\n\t\t*)\n\t\t\tmerge_head=\"$merge_parent_commit\\t\\t'$(git rev-parse --short $merge_parent_commit)' of .\"\n\t\t\t;;\n\t\tesac\n\n\t\tmerge_heads=\"$merge_heads$merge_head\\n\"\n\tdone\n\n\tif test -n \"$merge_heads\"\n\tthen\n\t\t# '\\n' already appended\n\t\tprintf '%s' \"$merge_heads\"\n\t\treturn 0\n\telse\n\t\treturn 1\n\tfi\n}\n\nreuse_authorship () {\n\t#\n\t# $1 commit to reuse authorship from\n\t#\n\tlocal commit=\"$1\"\n\n\tGIT_AUTHOR_NAME=\"$(git show -s --format=%an $commit)\"\n\tGIT_AUTHOR_EMAIL=\"$(git show -s --format=%ae $commit)\"\n\tGIT_AUTHOR_DATE=\"$(git show -s --format=%at $commit)\"\n\n\texport GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE\n}\n\nget_merge_parent_commits () {\n\t#\n\t# $@ merge_parents (might contain ref)\n\t#\n\t# Provide to-be merge commit parent parameters\n\t# in format suitable for `git commit-tree`.\n\t#\n\tlocal merge_parent_commits=\n\n\tfor merge_parent in $@\n\tdo\n\t\tmerge_parent_commits=\"$(printf '%s\\n' \"$merge_parent_commits -p $(git rev-parse $merge_parent^0)\")\"\n\tdone\n\n\tif test -n \"$merge_parent_commits\"\n\tthen\n\t\tprintf '%s\\n' \"$merge_parent_commits\"\n\t\treturn 0\n\telse\n\t\treturn 1\n\tfi\n}\n\nget_checkout_reflog_message () {\n\t#\n\t# $1 destination ref or commit\n\t#\n\tlocal destination=\"$1\"\n\tlocal source=\n\n\tsource=\"$(git symbolic-ref --short --quiet HEAD)\" ||\n\tsource=\"$(git rev-parse HEAD^0)\"\n\n\tprintf '%s' \"checkout: moving from $source to $destination\"\n}\n\nmain \"$@\"\n"},{"id":"333558","messageId":"d60812ab-a4d9-6d39-9624-3280ef1ccd21@gmail.com","threadId":"47325","inReplyTo":"8998e832-f49f-4de4-eb8d-a7934fba97b5@gmail.com","subject":"[SCRIPT/RFC 2/3] git-merge-one-file--cached","fromName":"Igor Djordjevic","fromEmail":"igor.d.djordjevic@gmail.com","sentAt":"2017-11-26T22:36:58Z","receivedAt":"2017-11-26T23:01:43Z","isPatch":false,"sender":{"key":"igor.d.djordjevic@gmail.com","avatar":null},"body":"Original \"git-merge-one-file\" script is slightly tweaked here into \n\"git-merge-one-file--cached\"[*1*] to allow (still trivial) _index-only_ \nthree-way file merge, not touching files inside working tree.\n\nProof of concept, not thoroughly tested, original content left in, \ncommented out. Feel free to comment/polish.\n\nTo make it available, I guess it would be best placed beside existing \n\"git-merge-one-file\" script...?\n\nRegards, Buga\n\n[*1*] \"git-merge-one-file--cached\"\n--- 8< ---\n#!/bin/sh\n#\n# Copyright (c) Linus Torvalds, 2005\n#\n# ---\n# Original \"git-merge-one-file\" script slightly tweaked into\n# \"git-merge-one-file--cached\" to allow (still trivial) index-only\n# three-way file merge, not touching files inside working tree.\n#\n# Proof of concept, not thoroughly tested, original content left in,\n# commented out.\n# ---\n#\n# This is the git per-file merge script, called with\n#\n#   $1 - original file SHA1 (or empty)\n#   $2 - file in branch1 SHA1 (or empty)\n#   $3 - file in branch2 SHA1 (or empty)\n#   $4 - pathname in repository\n#   $5 - original file mode (or empty)\n#   $6 - file in branch1 mode (or empty)\n#   $7 - file in branch2 mode (or empty)\n#\n# Handle some trivial cases.. The _really_ trivial cases have\n# been handled already by git read-tree, but that one doesn't\n# do any merges that might change the tree layout.\n\nUSAGE='<orig blob> <our blob> <their blob> <path>'\nUSAGE=\"$USAGE <orig mode> <our mode> <their mode>\"\nLONG_USAGE=\"usage: git merge-one-file $USAGE\n\nBlob ids and modes should be empty for missing files.\"\n\nSUBDIRECTORY_OK=Yes\n. git-sh-setup\ncd_to_toplevel\nrequire_work_tree\n\nif test $# != 7\nthen\n\techo \"$LONG_USAGE\"\n\texit 1\nfi\n\ncase \"${1:-.}${2:-.}${3:-.}\" in\n#\n# Deleted in both or deleted in one and unchanged in the other\n#\n\"$1..\" | \"$1.$1\" | \"$1$1.\")\n\tif { test -z \"$6\" && test \"$5\" != \"$7\"; } ||\n\t   { test -z \"$7\" && test \"$5\" != \"$6\"; }\n\tthen\n\t\techo \"ERROR: File $4 deleted on one branch but had its\" >&2\n\t\techo \"ERROR: permissions changed on the other.\" >&2\n\t\texit 1\n\tfi\n\n\tif test -n \"$2\"\n\tthen\n\t\techo \"Removing $4\"\n\t# else\n\t\t# read-tree checked that index matches HEAD already,\n\t\t# so we know we do not have this path tracked.\n\t\t# there may be an unrelated working tree file here,\n\t\t# which we should just leave unmolested.  Make sure\n\t\t# we do not have it in the index, though.\n\t\t# exec git update-index --remove -- \"$4\"\n\tfi\n\t# if test -f \"$4\"\n\t# then\n\t\t# rm -f -- \"$4\" &&\n\t\t# rmdir -p \"$(expr \"z$4\" : 'z\\(.*\\)/')\" 2>/dev/null || :\n\t# fi &&\n\t\texec git update-index --remove --cacheinfo \"$6\",\"$2\",\"$4\"\n\t;;\n\n#\n# Added in one.\n#\n\".$2.\")\n\t# the other side did not add and we added so there is nothing\n\t# to be done, except making the path merged.\n\texec git update-index --add --cacheinfo \"$6\",\"$2\",\"$4\"\n\t;;\n\"..$3\")\n\techo \"Adding $4\"\n\tif test -f \"$4\"\n\tthen\n\t\techo \"ERROR: untracked $4 is overwritten by the merge.\" >&2\n\t\texit 1\n\tfi\n\tgit update-index --add --cacheinfo \"$7\",\"$3\",\"$4\" # &&\n\t\t# exec git checkout-index -u -f -- \"$4\"\n\t;;\n\n#\n# Added in both, identically (check for same permissions).\n#\n\".$3$2\")\n\tif test \"$6\" != \"$7\"\n\tthen\n\t\techo \"ERROR: File $4 added identically in both branches,\" >&2\n\t\techo \"ERROR: but permissions conflict $6->$7.\" >&2\n\t\texit 1\n\tfi\n\techo \"Adding $4\"\n\tgit update-index --add --cacheinfo \"$6\",\"$2\",\"$4\" # &&\n\t\t# exec git checkout-index -u -f -- \"$4\"\n\t;;\n\n#\n# Modified in both, but differently.\n#\n\"$1$2$3\" | \".$2$3\")\n\n\tcase \",$6,$7,\" in\n\t*,120000,*)\n\t\techo \"ERROR: $4: Not merging symbolic link changes.\" >&2\n\t\texit 1\n\t\t;;\n\t*,160000,*)\n\t\techo \"ERROR: $4: Not merging conflicting submodule changes.\" >&2\n\t\texit 1\n\t\t;;\n\tesac\n\n\tsrc1=$(git unpack-file $2)\n\tsrc2=$(git unpack-file $3)\n\tcase \"$1\" in\n\t'')\n\t\techo \"Added $4 in both, but differently.\"\n\t\torig=$(git unpack-file e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)\n\t\t;;\n\t*)\n\t\techo \"Auto-merging $4\"\n\t\torig=$(git unpack-file $1)\n\t\t;;\n\tesac\n\n\tgit merge-file \"$src1\" \"$orig\" \"$src2\"\n\tret=$?\n\tmsg=\n\tif test $ret != 0 || test -z \"$1\"\n\tthen\n\t\tmsg='content conflict'\n\t\tret=1\n\tfi\n\n\t# Create the working tree file, using \"our tree\" version from the\n\t# index, and then store the result of the merge.\n\t# git checkout-index -f --stage=2 -- \"$4\" && cat \"$src1\" >\"$4\" || exit 1\n\t# rm -f -- \"$orig\" \"$src1\" \"$src2\"\n\n\tif test \"$6\" != \"$7\"\n\tthen\n\t\tif test -n \"$msg\"\n\t\tthen\n\t\t\tmsg=\"$msg, \"\n\t\tfi\n\t\tmsg=\"${msg}permissions conflict: $5->$6,$7\"\n\t\tret=1\n\tfi\n\n\tif test $ret = 0\n\tthen\n\t\tmerge_blob=$(git hash-object -w --path=\"$4\" -- \"$src1\")\n\tfi\n\trm -f -- \"$orig\" \"$src1\" \"$src2\"\n\n\tif test $ret != 0\n\tthen\n\t\techo \"ERROR: $msg in $4\" >&2\n\t\texit 1\n\tfi\n\t# exec git update-index -- \"$4\"\n\texec git update-index --cacheinfo \"$6\",\"$merge_blob\",\"$4\"\n\t;;\n\n*)\n\techo \"ERROR: $4: Not handling case $1 -> $2 -> $3\" >&2\n\t;;\nesac\nexit 1\n"},{"id":"333643","messageId":"d5f243a5-6e35-f3fc-4daf-6e1376bef897@kdbg.org","threadId":"47325","inReplyTo":"8998e832-f49f-4de4-eb8d-a7934fba97b5@gmail.com","subject":"Re: [SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge, no working tree file changes)","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2017-11-27T21:54:03Z","receivedAt":"2017-11-27T21:54:12Z","isPatch":false,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 26.11.2017 um 23:35 schrieb Igor Djordjevic:\n> Approach discussed here could have a few more useful applications,\n> but one seems to be standing out the most - in case where multiple\n> topic branches are temporarily merged for integration testing, it\n> could be very useful to be able to post \"hotfix\" commits to merged\n> branches directly, _without actually switching to them_ (and thus\n> without touching working tree files), and still keeping everything\n> merged, in one go.\n> \n> Example starting point is \"master\" branch with 3 topic branches (A,\n> B, C), to be (throwaway) merged for integration testing inside\n> temporary \"test\" branch:\n> \n> (1)        o---o---A (topicA)\n>            /\n>           /\n>          /\n>      ---o---o---M (master, test, HEAD)\n>          \\   \\\n>           \\   o---B (topicB)\n>            \\\n>             o---o---C (topicC)\n> \n> \n> This is what we end up with once \"master\" and topic branches are\n> merged in merge commit M1 inside temporary \"test\" branch for further\n> integration testing:\n> \n> (2)        o---o---A (topicA)\n>            /         \\\n>           /           M1 (test, HEAD)\n>          /           /||\n>      ---o---o---M---/ || (master)\n>          \\   \\       / |\n>           \\   o---B-/  | (topicB)\n>            \\           |\n>             o---o---C--/ (topicC)\n> \n> \n> Upon discovery of a fix needed inside \"topicA\", hotfix changes X\n> should be committed to \"topicA\" branch and re-merged inside merge\n> commit M2 on temporary integration \"test\" branch (previous temporary\n> merge commit M1 is thrown away as uninteresting):\n> \n> (3)        o---o---A---X (topicA)\n>            /             \\\n>           /               M2 (test, HEAD)\n>          /               /||\n>      ---o---o---M-------/ || (master)\n>          \\   \\           / |\n>           \\   o---B-----/  | (topicB)\n>            \\              /\n>             o---o---C----/ (topicC)\n\nI my opinion, putting the focus on integration merge commits and the \ndesire to automate the re-merge step brings in a LOT of complexity in \nthe implementation for a very specific use-case that does not \nnecessarily help other cases.\n\nFor example, in my daily work, I have encountered situations where, \nwhile working on one topic, I made a hot-fix for a different topic. \nThere is no demand for a merge step in this scenario.\n\nIn your scenario above, it would certainly not be too bad if you forgo \nthe automatic merge and have the user issue a merge command manually. \nThe resulting history could look like this:\n\n(3)         o---o---A---X    (topicA)\n            /         \\   \\\n           /           M1--M2 (test, HEAD)\n          /           /||\n      ---o---o---M---' ||     (master)\n          \\   \\       / |\n           \\   o-----B /      (topicB)\n            \\         /\n             o---o---C        (topicC)\n\nI.e., commit --onto-parent A produced commit X, but M2 was then a \nregular manual merge. (Of course, I am assuming that the merge commits \nare dispensible, and only the resulting tree is of interest.)\n\nMoreover, you seem to assume that an integration branch is an octopus \nmerge, that can be re-created easily. I would say that this a very, very \nexceptional situation.\n\n----\n\nAt this point, I spent five minutes thinking of how I would use commit \n--onto-parent if I did not have git-post.\n\nWhile on the integration branch, I typically make separate commits for \neach fix, mostly because the bugs are discovered and fixed not \nsimultaneously, but over time. So, I have a small number of commits that \nI distribute later using my git-post script. But that does not have to \nbe so. I think I could work with a git commit --onto-parent feature as \nlong as it does not attempt to make a merge commit for me. (I would hate \nthat.)\n\nSometimes, however I have two bug fixes in the worktree, ready to be \ncommitted. Then the ability to pass pathspec to git commit is useful. \nDoes your implementation support this use case (partially staged \nworktree changes)?\n\nThanks,\n-- Hannes\n"},{"id":"333664","messageId":"203a75c8-0c58-253c-2c18-05450f7ae49b@gmail.com","threadId":"47325","inReplyTo":"d5f243a5-6e35-f3fc-4daf-6e1376bef897@kdbg.org","subject":"Re: [SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge, no working tree file changes)","fromName":"Igor Djordjevic","fromEmail":"igor.d.djordjevic@gmail.com","sentAt":"2017-11-28T01:15:52Z","receivedAt":"2017-11-28T01:16:02Z","isPatch":false,"sender":{"key":"igor.d.djordjevic@gmail.com","avatar":null},"body":"Hi Johannes,\n\nOn 27/11/2017 22:54, Johannes Sixt wrote:\n> \n> I my opinion, putting the focus on integration merge commits and the\n> desire to automate the re-merge step brings in a LOT of complexity in\n> the implementation for a very specific use-case that does not\n> necessarily help other cases.\n\nIt might seem more complex than it is, until you examine the guts to \nsee how it really works :)\n\nBasic concept is pretty simple, as I actually don`t automate \nanything, at least not in terms of what would manual user steps look \nlike - for example, there`s no real re-merge step in terms of \nactually redoing the merge, I just reuse what was already there in a \nvery clean way, I would think (supported by my current, humble \nknowledge, still).\n\nThe only merge that could possibly ever happen is upon committing \ndesired subset of changes onto parent, and that shouldn`t be too \ncomplex by definition, otherwise that commit doesn`t really belong \nthere in the first place, if it can`t be meaningfully applied where \nwe want it (for now, at least).\n\nThat said, the whole operation of \"posting on parent and re-merging \neverything\", the way it looks like from the outside, could end just \nwith a simple diff-apply-commit-commit internally, no merges at all. \nOnly if simple `git apply` fails, we try some trivial merging - and \nall that inside separate (parent) index only, not touching original \nHEAD index nor working tree, staying pristine for the whole process, \nuntouched.\n\nOnce done, you should be in the very same situation you started from, \nnothing changed, just having your history tweaked a bit to tell a \ndifferent story on how you got there (now including a commit you \nposted on your HEAD`s parent).\n\nOtherwise, I agree that explained use case might be a bit specific, \nbut that is only because I recognized that one to be the most \ninteresting to initially present (not to say one of more complex \ncases) - to me, at least, but it is certainly not the only one.\n\nDon`t let \"usual/preferred/recommended\" Git workflow distract you too \nmuch - one of the reasons I made this is because it also allows _kind \nof_ \"vanilla Git\" patch queue, where you can quickly work on top of \nthe merge head, pushing commits onto parents below, being tips of \nyour \"queues\", putting you up to speed without a need to ever switch \na branch (hypothetically), until satisfied with what you have, where \nyou can slow down and polish each branch separately, as usual.\n\nLike working on multiple branches at the same time, in the manner \nsimilar to what `git add --patch` allows in regards to working on \nmultiple commits at the same time. This just takes it on yet another \nlevel... hopefully :)\n\n> For example, in my daily work, I have encountered situations where,\n> while working on one topic, I made a hot-fix for a different topic.\n> There is no demand for a merge step in this scenario.\n> \n> In your scenario above, it would certainly not be too bad if you\n> forgo the automatic merge and have the user issue a merge command\n> manually. The resulting history could look like this:\n> \n> (3)         o---o---A---X    (topicA)\n>            /         \\   \\\n>           /           M1--M2 (test, HEAD)\n>          /           /||\n>      ---o---o---M---' ||     (master)\n>          \\   \\       / |\n>           \\   o-----B /      (topicB)\n>            \\         /\n>             o---o---C        (topicC)\n> \n> I.e., commit --onto-parent A produced commit X, but M2 was then a\n> regular manual merge. (Of course, I am assuming that the merge\n> commits are dispensible, and only the resulting tree is of\n> interest.)\n\nI see - and what you`re asking for is what I already envisioned and \nhoped to get some more feedback about, here`s excerpt from \n[SCRIPT/RFC 3/3] git-commit--onto-parent.sh[1] (I guess you didn`t \nhave time to checked that one yet?):\n\n  For example, it might make sense to separate commit creation (on \n  current HEAD`s parent) and its actual re-merging into integration \n  test branch, where \"--remerge\" (or something) parameter would be used \n  on top of \"--onto-parent\" to trigger both, if/when desired.\n  \n  Another direction to think in might be introducing more general \n  \"--onto\" parameter, too (or instead), without \"parent\" restriction, \n  allowing to record a commit on top of any arbitrary commit (other \n  than HEAD). This could even be defaulted to \"git commit <commit-ish>\" \n  (no option needed), where current \"git commit\" behaviour would then \n  just be a special case of omitted <commit-ish> defaulting to HEAD, \n  aligning well with other Git commands sharing the same behaviour.\n\nSo I definitely look forward decoupling these two ((1) commit to \nparent and (2) remerge), with enough discussion flowing :)\n\nHeck, even \"to parent\" is an artificial/imposed restriction now, in \nreality you could commit on top of any other commit you want (without \nswitching branches)... but let`s take one step at a time.\n\nJust note that omitting the remerge step is what actually makes the \nlogic more complex, as we now need to change the original situation, \ntoo, both HEAD index and working tree, to remove changes which we \ncommitted elsewhere (without \"merging\" back in).\n\nBut it is interesting case to look into ;)\n\n> Moreover, you seem to assume that an integration branch is an octopus\n> merge, that can be re-created easily. I would say that this a very,\n> very exceptional situation.\n\nActually, I make no assumptions - head of \"integration branch\" \ndoesn`t even have to be a merge commit. And as we are not really \n\"remerging\" anything, there is no need to recreate anything ;) (what \nI tried to explain at the beginning of this e-mail).\n\nThe sole point is that your current situation inside HEAD doesn`t \nchange at all, no matter how complex it is, we just rewrite history \non how we got there (to include new commit onto desired parent).\n\n> At this point, I spent five minutes thinking of how I would use\n> commit --onto-parent if I did not have git-post.\n> \n> While on the integration branch, I typically make separate commits\n> for each fix, mostly because the bugs are discovered and fixed not\n> simultaneously, but over time. So, I have a small number of commits\n> that I distribute later using my git-post script. But that does not\n> have to be so. I think I could work with a git commit --onto-parent\n> feature as long as it does not attempt to make a merge commit for me.\n> (I would hate that.)\n\nI would be interested in looking into this, thanks for your feedback.\n\nBut, thinking about it more, you do realize that, without \"updated \nmerge\", that would be removing committed changes from where you`re \ncurrently working at (once committed where you want it), is that what \nyou expect?\n\nBecause it seems a bit strange, as you loose overview on how all \nthose commits work together, and what you did so far, even, so some \nfuture merge might yield unexpected conflicts, which could have been \navoided.\n\nFrom what I understand, your `git-post` makes a commit where you are \n_and copies_ it over to destination, so you end up with same changes \nin two places (two commits). More, you seem to keep working on top of \nyour previous commit(s), which seems logical, later \"posting\" it \nwhere desired - so after the fact. It also means you do still have \nall your \"fix\" commits in HEAD while you work on them (and until you \ngit-post and abandon them, discarding the integration branch).\n\nBut `git commit --onto-parent` proposed here does the commit on \ndestination only, where later \"remerge\" is crucial part of still \nkeeping that commit changes in the current HEAD working tree as well, \nso you can still keep working on top of them.\n\nCould it be that you`re looking at `git commit --onto-parent` from an \nunexpected perspective (through your `git-post`, being conceptually \ndifferent)...?\n\nThough your use case graph (3) above should mean you`re very well \naware of implications, I guess, so I don`t know... :)\n\n> Sometimes, however I have two bug fixes in the worktree, ready to be\n> committed. Then the ability to pass pathspec to git commit is useful.\n> Does your implementation support this use case (partially staged\n> worktree changes)?\n\nFrom what the idea is, it should work with anything that already \nworks with plain `git commit`, as script is currently just a wrapper \naround it, providing additional functionality to speed things up.\n\nSo yes, `--onto-parent` works with `--patch`, `--amend`, etc :) If \nnot, that`s a bug which I`d appreciate being reported.\n\nMight be some `git commit` options don`t have sense in combination \nwith `--onto-parent` as well, so those could be restricted, once \nrecognized.\n\n... aaand that said, I`ve just noticed that pathspec doesn`t actually \nwork, for no good reason. If you add path through `git add` first, it \nwill work, but not through `git commit --onto-parent -- <pathspec>` \ndirectly, which I need to address. Currently, it complains about no \nchanges added to commit. Thanks for spotting! ;)\n\n> Thanks,\n> -- Hannes\n\nThank you for your time and valuable feedback, and one from a real \nlife use-case perspective.\n\nRegards, Buga\n\n[1] https://public-inbox.org/git/d5f243a5-6e35-f3fc-4daf-6e1376bef897@kdbg.org/T/#m72f45ad7a8f1c733266a875bca087ee82cc781e7\n"},{"id":"333797","messageId":"ea156b8b-29d8-7501-b5a5-a29cfbd7d1d6@kdbg.org","threadId":"47325","inReplyTo":"203a75c8-0c58-253c-2c18-05450f7ae49b@gmail.com","subject":"Re: [SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge, no working tree file changes)","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2017-11-29T19:11:02Z","receivedAt":"2017-11-29T19:11:13Z","isPatch":false,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 28.11.2017 um 02:15 schrieb Igor Djordjevic:\n> On 27/11/2017 22:54, Johannes Sixt wrote:\n>>\n>> I my opinion, putting the focus on integration merge commits and the\n>> desire to automate the re-merge step brings in a LOT of complexity in\n>> the implementation for a very specific use-case that does not\n>> necessarily help other cases.\n> \n> It might seem more complex than it is, until you examine the guts to\n> see how it really works :)\n> \n> Basic concept is pretty simple, as I actually don`t automate\n> anything, at least not in terms of what would manual user steps look\n> like - for example, there`s no real re-merge step in terms of\n> actually redoing the merge, I just reuse what was already there in a\n> very clean way, I would think (supported by my current, humble\n> knowledge, still).\n> \n> The only merge that could possibly ever happen is upon committing\n> desired subset of changes onto parent, and that shouldn`t be too\n> complex by definition, otherwise that commit doesn`t really belong\n> there in the first place, if it can`t be meaningfully applied where\n> we want it (for now, at least).\n> \n> That said, the whole operation of \"posting on parent and re-merging\n> everything\", the way it looks like from the outside, could end just\n> with a simple diff-apply-commit-commit internally, no merges at all.\n> Only if simple `git apply` fails, we try some trivial merging - and\n> all that inside separate (parent) index only, not touching original\n> HEAD index nor working tree, staying pristine for the whole process,\n> untouched.\n> \n> Once done, you should be in the very same situation you started from,\n> nothing changed, just having your history tweaked a bit to tell a\n> different story on how you got there (now including a commit you\n> posted on your HEAD`s parent).\n\nOk, then please explain, how this process should work in my workflow and \nwith the `commit --onto-parent` feature that you have in mind. I have an \nintegration branch (which is a throw-away type, so you can mangle it in \nany way you want); it is a series of merges:\n\n  ...A    ...C          <- topics A, C\n      \\       \\\n    ---o---o---o---o    <- integration\n          /       /\n      ...B    ...D      <- topics B, D\n\nNow I find a bug in topic B. Assume that the merges of C and D have \ntextual conflicts with the integration branch (but not with B) and/or \nmay be evil. What should I do?\n\nWith git-post, I make a fixup commit commit on the integration branch, \nthen `git post B && git merge B`:\n\n  ...A    ...C                  <- topics A, C\n      \\       \\\n    ---o---o---o---o---f---F    <- integration\n          /       /       /\n      ...B    ...D       /      <- topic D\n          \\             /\n           f'----------'        <- topic B\n\nThe merge F does not introduce any changes on the integration branch, so \nI do not need it, but it helps keep topic B off radar when I ask `git \nbranch --no-merged` later.\n\n> Don`t let \"usual/preferred/recommended\" Git workflow distract you too\n> much - one of the reasons I made this is because it also allows _kind\n> of_ \"vanilla Git\" patch queue, where you can quickly work on top of\n> the merge head, pushing commits onto parents below, being tips of\n> your \"queues\", putting you up to speed without a need to ever switch\n> a branch (hypothetically), until satisfied with what you have, where\n> you can slow down and polish each branch separately, as usual.\n> \n> Like working on multiple branches at the same time, in the manner\n> similar to what `git add --patch` allows in regards to working on\n> multiple commits at the same time. This just takes it on yet another\n> level... hopefully :)\n\n'kay, I'm not eagerly waiting for this particular next level (I prefer \nto keep things plain and simple), but I would never say this were a \nbroken workflow. ;)\n\n>> In your scenario above, it would certainly not be too bad if you\n>> forgo the automatic merge and have the user issue a merge command\n>> manually. The resulting history could look like this:\n>>\n>> (3)         o---o---A---X    (topicA)\n>>             /         \\   \\\n>>            /           M1--M2 (test, HEAD)\n>>           /           /||\n>>       ---o---o---M---' ||     (master)\n>>           \\   \\       / |\n>>            \\   o-----B /      (topicB)\n>>             \\         /\n>>              o---o---C        (topicC)\n>>\n>> I.e., commit --onto-parent A produced commit X, but M2 was then a\n>> regular manual merge. (Of course, I am assuming that the merge\n>> commits are dispensible, and only the resulting tree is of\n>> interest.)\n> \n> I see - and what you`re asking for is what I already envisioned and\n> hoped to get some more feedback about, here`s excerpt from\n> [SCRIPT/RFC 3/3] git-commit--onto-parent.sh[1] (I guess you didn`t\n> have time to checked that one yet?):\n\nI did have a brief look, but I stopped when I saw\n\n\t# Remove entry from HEAD reflog, not to pollute it with\n\t# uninteresting in-between steps we take, leaking implementation\n\t# details to end user.\n\nIt's a clear sign for me that's something wrong. It is not just reflogs \nthat can become stale, but all operations that follow the `git commit` \ncan fail. How do you clean up such a mess?\n\n> \n>    For example, it might make sense to separate commit creation (on\n>    current HEAD`s parent) and its actual re-merging into integration\n>    test branch, where \"--remerge\" (or something) parameter would be used\n>    on top of \"--onto-parent\" to trigger both, if/when desired.\n>    \n>    Another direction to think in might be introducing more general\n>    \"--onto\" parameter, too (or instead), without \"parent\" restriction,\n>    allowing to record a commit on top of any arbitrary commit (other\n>    than HEAD). This could even be defaulted to \"git commit <commit-ish>\"\n>    (no option needed), where current \"git commit\" behaviour would then\n>    just be a special case of omitted <commit-ish> defaulting to HEAD,\n>    aligning well with other Git commands sharing the same behaviour.\n> \n> So I definitely look forward decoupling these two ((1) commit to\n> parent and (2) remerge), with enough discussion flowing :)\n> \n> Heck, even \"to parent\" is an artificial/imposed restriction now, in\n> reality you could commit on top of any other commit you want (without\n> switching branches)... but let`s take one step at a time.\n> \n> Just note that omitting the remerge step is what actually makes the\n> logic more complex, as we now need to change the original situation,\n> too, both HEAD index and working tree, to remove changes which we\n> committed elsewhere (without \"merging\" back in).\n\nThe \"complex situation\" I had in mind was not so much about the user's \nview, but how the implementation has to work when there are errors.\n\n>>From what I understand, your `git-post` makes a commit where you are \n> _and copies_ it over to destination, so you end up with same changes\n> in two places (two commits). More, you seem to keep working on top of\n> your previous commit(s), which seems logical, later \"posting\" it\n> where desired - so after the fact. It also means you do still have\n> all your \"fix\" commits in HEAD while you work on them (and until you\n> git-post and abandon them, discarding the integration branch).\n\nJust to clarify: git-post only copies existing commits, but does not \nmake the original itself.\n\n> \n> But `git commit --onto-parent` proposed here does the commit on\n> destination only, where later \"remerge\" is crucial part of still\n> keeping that commit changes in the current HEAD working tree as well,\n> so you can still keep working on top of them.\n> \n> Could it be that you`re looking at `git commit --onto-parent` from an\n> unexpected perspective (through your `git-post`, being conceptually\n> different)...?\n\nThat may be the case. I'm very interested in your answer to my challenge \nabove.\n\n-- Hannes\n"},{"id":"333818","messageId":"741dfedc-07f8-24fb-ebe2-940f8b2639d4@gmail.com","threadId":"47325","inReplyTo":"ea156b8b-29d8-7501-b5a5-a29cfbd7d1d6@kdbg.org","subject":"Re: [SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge, no working tree file changes)","fromName":"Igor Djordjevic","fromEmail":"igor.d.djordjevic@gmail.com","sentAt":"2017-11-29T23:10:21Z","receivedAt":"2017-11-29T23:10:33Z","isPatch":false,"sender":{"key":"igor.d.djordjevic@gmail.com","avatar":null},"body":"Hi Hannes,\n\nOn 29/11/2017 20:11, Johannes Sixt wrote:\n> \n> Ok, then please explain, how this process should work in my workflow\n> and with the `commit --onto-parent` feature that you have in mind. I\n> have an integration branch (which is a throw-away type, so you can\n> mangle it in any way you want); it is a series of merges:\n> \n>  ...A    ...C            <- topics A, C\n>      \\       \\ E\n>    ---o---o---o---o I    <- integration\n>          /       /\n>      ...B    ...D        <- topics B, D\n> \n> Now I find a bug in topic B. Assume that the merges of C and D have\n> textual conflicts with the integration branch (but not with B) and/or\n> may be evil. What should I do?\n> \n> With git-post, I make a fixup commit commit on the integration\n> branch, then `git post B && git merge B`:\n> \n>  ...A    ...C                  <- topics A, C\n>      \\       \\\n>    ---o---o---o---o---f---F    <- integration\n>          /       /       /\n>      ...B    ...D       /      <- topic D\n>          \\             /\n>           f'----------'        <- topic B\n> \n> The merge F does not introduce any changes on the integration branch,\n> so I do not need it, but it helps keep topic B off radar when I ask\n> `git branch --no-merged` later.\n\nBut you`re not committing (posting) on your HEAD`s (direct) parent in \nthe first place (topic B), so `commit --onto-parent` isn`t right tool \nfor the job... yet :)\n\nTo make it easier to explain, I marked your integration branch \ninitial head with \"I\" in the quote above (commit merging-in branch \nD), and marked commit merging-in branch C with \"E\".\n\nHEAD being currently on commit \"I\", you can only use `--onto-parent` \noption to commit onto \"E\" or \"D\", being parents of \"I\".\n\nTo work with `--onto-parent` and be able to commit on top of any of \nthe topic branches, you would need a situation like this instead:\n\n (1)  ...C      <- topic C\n         |\n    ...A |      <- topic A\n        \\|\n      ...o I    <- integration\n        /|\n    ...B |      <- topic B\n         |\n      ...D      <- topic D\n\nWith `commit --onto-parent` you would skip `git post B && git merge \nB` steps, where \"fixup commit\" would be done with `--onto-parent B`, \nSo you end up in situation like this:\n\n (2)      ...C      <- topic C\n             |\n        ...A |      <- topic A\n            \\|\n          ...o I'   <- integration\n            /|\n    ...B---f |      <- topic B\n             |\n          ...D      <- topic D\n\nState of index and working tree files in your F and my I' commit is \nexactly the same (I' = I + f), where in my case (2) history looks \nlike \"f\" was part of topic B from the start, before integration \nmerge happened.\n\nBUT, all this said, I understand that your starting position, where \nnot all topic branches are merged at the same time (possibly to keep \nconflict resolution sane), is probably more often to be encountered \nin real-life work... and as existing `--onto-parent` machinery should \nbe able to support it already, I`m looking forward to make it happen :)\n\nOnce there, starting from your initial position:\n\n>    ...A    ...C            <- topics A, C\n>        \\       \\ E\n>      ---o---o---o---o I    <- integration <- HEAD\n>            /       /\n>        ...B    ...D        <- topics B, D\n\n... and doing something like `git commit --onto B --merge` would yield:\n \n (3) ...A    ...C            <- topics A, C\n         \\       \\ E\n       ---o---o---o---o I'   <- integration\n             /       /|\n         ...B    ...D |      <- topic D\n             \\        |\n              f-------'      <- topic B\n\n... where (I' = I + f) is still true. If that`s preferred in some \ncases, it could even look like this instead:\n\n (4) ...A    ...C             <- topics A, C\n         \\       \\ E  I\n       ---o---o---o---o---F   <- integration\n             /       /   /\n         ...B    ...D   /     <- topic D\n             \\         /\n              f-------'       <- topic B\n\n... where F(4) = I'(3), so similar situation, just that we don`t \ndiscard I but post F on top of it.\n\nGood thing is all necessary logic should already be in place, I just \nneed to think a bit about the most sane user interface, and get back \nto you. Thanks for invaluable input so far :)\n\nOf course, do feel free to drop any ideas you come up with as well, \non how `git commit` user interface/options leading to (3) or (4) \nshould look like (they could both be supported).\n\n> > Like working on multiple branches at the same time, in the manner\n> > similar to what `git add --patch` allows in regards to working on\n> > multiple commits at the same time. This just takes it on yet another\n> > level... hopefully :)\n> \n> 'kay, I'm not eagerly waiting for this particular next level (I\n> prefer to keep things plain and simple), but I would never say this\n> were a broken workflow. ;)\n\nHehe, thanks, I guess :) Simplicity, but user-oriented, is the major \npoint here, where you can work on unrelated patch series at the same \ntime (patch queues?), without a need to switch branches, while you \nstill make \"per series\" history as you go, without a need to do it \nafter the fact.\n\nIt`s a matter of possibilities (where Git usually excels), so one can \nchoose for himself, depending on the situation at hand.\n\n> > [SCRIPT/RFC 3/3] git-commit--onto-parent.sh[1] (I guess you didn`t\n> > have time to checked that one yet?):\n> \n> I did have a brief look, but I stopped when I saw\n> \n>     # Remove entry from HEAD reflog, not to pollute it with\n>     # uninteresting in-between steps we take, leaking implementation\n>     # details to end user.\n> \n> It's a clear sign for me that's something wrong. It is not just\n> reflogs that can become stale, but all operations that follow the\n> `git commit` can fail. How do you clean up such a mess?\n\nI would respectfully disagree about it being wrong per se, but that \nsaid, \"cleaning up\" reflogs was a last minute design decision so we \ndon`t show uninteresting states - from user`s point of view, all that \nhappened is commit on top of --onto-parent, and merging that commit \ninto where he already was (instead of the commit he was on). \n\nImplementation details on how we got there should be irrelevant, even \nmore if we failed to do so, cluttering reflog with unimportant \nleftovers.\n\nCommands are chained, and in case _anything_ goes wrong, we just bail \nout and restore original index and HEAD position. As working tree is \nnot touched at all, no cleanup needed there, so everything is pretty \nsimple. Of course, valid tests still should/need to be made, to make \nsure everything is covered, but there really isn`t much to cover in \nthe first place.\n\nBut this is open for discussion, of course, and code for ref-logging \nall steps is still inside the script, just commented out. Heck, \nthere`s even get_checkout_reflog_message() function at the end of the \nscript, used to mimic real checkout reflog message, that`s currently \nunused ;)\n\n> The \"complex situation\" I had in mind was not so much about the\n> user's view, but how the implementation has to work when there are\n> errors.\n\nSimple - abort. Restore backed up index and HEAD state is all that`s \nto it, and it looks like we never issued the command to begin with.\n\n> > From what I understand, your `git-post` makes a commit where you\n> > are _and copies_ it over to destination, so you end up with same\n> > changes in two places (two commits). More, you seem to keep working\n> > on top of your previous commit(s), which seems logical, later\n> > \"posting\" it where desired - so after the fact. It also means you\n> > do still have all your \"fix\" commits in HEAD while you work on them\n> > (and until you git-post and abandon them, discarding the\n> > integration branch).\n> \n> Just to clarify: git-post only copies existing commits, but does not\n> make the original itself.\n\nYes, I understand that (hopefully communicated from the second part \nof the quote), but I see my wording of that first sentence was a bit \nunfortunate there (to say the least, lol), thanks for clarifying.\n\n> > Could it be that you`re looking at `git commit --onto-parent` from\n> > an unexpected perspective (through your `git-post`, being\n> > conceptually different)...?\n> \n> That may be the case. I'm very interested in your answer to my\n> challenge above.\n\nFrom everything said above, it seems so - at the moment, but I`m \nlooking forward making it work for you, too, as it does make sense \n\"out in the wild\", in real-life scenarios.\n\nThank you again, for inspiring discussion.\n\nRegards, Buga\n"},{"id":"333875","messageId":"0F30BEF8-A9F7-43B9-BC89-4B9CD7AF3E16@gmail.com","threadId":"47325","inReplyTo":"8998e832-f49f-4de4-eb8d-a7934fba97b5@gmail.com","subject":"Re: [SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge, no working tree file changes)","fromName":"Chris Nerwert","fromEmail":"a.lozovsky@gmail.com","sentAt":"2017-11-30T22:40:21Z","receivedAt":"2017-11-30T22:40:33Z","isPatch":false,"sender":{"key":"a.lozovsky@gmail.com","avatar":"https://gravatar.com/avatar/8bb8ff5ec366dd64bd8e08f768082934513da367ae047bcb0039292e4ed6bda5?d=mp&s=160"},"body":"On Nov 27, 2017, at 00:35, Igor Djordjevic <igor.d.djordjevic@gmail.com> wrote:\n> Approach discussed here could have a few more useful applications, \n> but one seems to be standing out the most - in case where multiple \n> topic branches are temporarily merged for integration testing, it \n> could be very useful to be able to post \"hotfix\" commits to merged \n> branches directly, _without actually switching to them_ (and thus \n> without touching working tree files), and still keeping everything \n> merged, in one go.\nI'm actually doing the described workflow quite often with git rebase when working on a topic. Given the following structure:\n\n  ---o               (master)\n      \\\n       o---A---B---C (topic)\n\nWhen I want to make changes to commit A one option is to make them directly on topic, then do \"git commit --fixup A\", and then eventual interactive rebase onto master will clean them up:\n\n  ---o                     (master)\n      \\\n       o---A---B---C---f!A (topic)\n\nHowever, sometimes this breaks when changes in B or C conflict somehow with A (which may happen quite a lot during development of a topic), so the rebase will not apply cleanly. So sometimes I make a temporary branch from A, commit the fixup there:\n\n  ---o               (master)\n      \\\n       o---A---B---C (topic)\n            \\\n             f!A     (temp)\n\nand then use \"git rebase --onto temp A topic\" to move the topic back on track:\n\n  ---o                     (master)\n      \\\n       o---A---f!A         (temp)\n                  \\\n                   B'---C' (topic)\n\nafter which the final cleanup rebase is much easier to do.\n\nObviously, all the branch switching and rebasing does take its tall on file modifications."},{"id":"333924","messageId":"33e97533-716b-e1cc-6aa0-bf8941225319@kdbg.org","threadId":"47325","inReplyTo":"741dfedc-07f8-24fb-ebe2-940f8b2639d4@gmail.com","subject":"Re: [SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge, no working tree file changes)","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2017-12-01T17:23:43Z","receivedAt":"2017-12-01T17:23:58Z","isPatch":false,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 30.11.2017 um 00:10 schrieb Igor Djordjevic:\n> On 29/11/2017 20:11, Johannes Sixt wrote:\n>> With git-post, I make a fixup commit commit on the integration\n>> branch, then `git post B && git merge B`:\n>>\n>>   ...A    ...C                  <- topics A, C\n>>       \\       \\\n>>     ---o---o---o---o---f---F    <- integration\n>>           /       /       /\n>>       ...B    ...D       /      <- topic D\n>>           \\             /\n>>            f'----------'        <- topic B\n>>\n>> The merge F does not introduce any changes on the integration branch,\n>> so I do not need it, but it helps keep topic B off radar when I ask\n>> `git branch --no-merged` later.\n> \n> But you`re not committing (posting) on your HEAD`s (direct) parent in\n> the first place (topic B), so `commit --onto-parent` isn`t right tool\n> for the job... yet :)\n\nOhh..kay.\n\n> To work with `--onto-parent` and be able to commit on top of any of\n> the topic branches, you would need a situation like this instead:\n> \n>   (1)  ...C      <- topic C\n>           |\n>      ...A |      <- topic A\n>          \\|\n>        ...o I    <- integration\n>          /|\n>      ...B |      <- topic B\n>           |\n>        ...D      <- topic D\n\nThis is a very, VERY exotic workflow, I would say. How would you \nconstruct commit I when three or more topics have conflicts? \nmerge-octopus does not support this use-case.\n\n> With `commit --onto-parent` you would skip `git post B && git merge\n> B` steps, where \"fixup commit\" would be done with `--onto-parent B`,\n> So you end up in situation like this:\n> \n>   (2)      ...C      <- topic C\n>               |\n>          ...A |      <- topic A\n>              \\|\n>            ...o I'   <- integration\n>              /|\n>      ...B---f |      <- topic B\n>               |\n>            ...D      <- topic D\n> \n> State of index and working tree files in your F and my I' commit is\n> exactly the same (I' = I + f), where in my case (2) history looks\n> like \"f\" was part of topic B from the start, before integration\n> merge happened.\n> \n> BUT, all this said, I understand that your starting position, where\n> not all topic branches are merged at the same time (possibly to keep\n> conflict resolution sane), is probably more often to be encountered\n> in real-life work... and as existing `--onto-parent` machinery should\n> be able to support it already, I`m looking forward to make it happen :)\n> \n> Once there, starting from your initial position:\n> \n>>     ...A    ...C            <- topics A, C\n>>         \\       \\ E\n>>       ---o---o---o---o I    <- integration <- HEAD\n>>             /       /\n>>         ...B    ...D        <- topics B, D\n> \n> ... and doing something like `git commit --onto B --merge` would yield:\n>   \n>   (3) ...A    ...C            <- topics A, C\n>           \\       \\ E\n>         ---o---o---o---o I'   <- integration\n>               /       /|\n>           ...B    ...D |      <- topic D\n>               \\        |\n>                f-------'      <- topic B\n> \n> ... where (I' = I + f) is still true.\n\nI am not used to this picture. I would not think that it is totally \nunacceptable, but it still has a hmm-factor.\n\n> If that`s preferred in some\n> cases, it could even look like this instead:\n> \n>   (4) ...A    ...C             <- topics A, C\n>           \\       \\ E  I\n>         ---o---o---o---o---F   <- integration\n>               /       /   /\n>           ...B    ...D   /     <- topic D\n>               \\         /\n>                f-------'       <- topic B\n> \n> ... where F(4) = I'(3), so similar situation, just that we don`t\n> discard I but post F on top of it.\n\nThis is very acceptable.\n\nNevertheless, IMO, it is not the task of git-commit to re-compute a \nmerge commit. It would be OK that it commits changes on top of a branch \nthat is not checked out. Perhaps it would even be OK to remove the \nchange from the current workspace (index and worktree), because it will \nreturn in the form of a merge later, but producing that merge is \ncertainly not the task of git-commit.\n\n-- Hannes\n"},{"id":"334025","messageId":"bfc4df2c-2e31-bb83-832d-92e41d09b976@gmail.com","threadId":"47325","inReplyTo":"0F30BEF8-A9F7-43B9-BC89-4B9CD7AF3E16@gmail.com","subject":"Re: [SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge, no working tree file changes)","fromName":"Igor Djordjevic","fromEmail":"igor.d.djordjevic@gmail.com","sentAt":"2017-12-03T23:01:30Z","receivedAt":"2017-12-03T23:01:40Z","isPatch":false,"sender":{"key":"igor.d.djordjevic@gmail.com","avatar":null},"body":"Hi Chris,\n\nOn 30/11/2017 23:40, Chris Nerwert wrote:\n> \n> I'm actually doing the described workflow quite often with git rebase\n> when working on a topic. Given the following structure:\n> \n>   ---o               (master)\n>       \\\n>        o---A---B---C (topic)\n> \n> When I want to make changes to commit A one option is to make them\n> directly on topic, then do \"git commit --fixup A\", and then eventual\n> interactive rebase onto master will clean them up:\n> \n>   ---o                     (master)\n>       \\\n>        o---A---B---C---f!A (topic)\n> \n> However, sometimes this breaks when changes in B or C conflict\n> somehow with A (which may happen quite a lot during development of a\n> topic), so the rebase will not apply cleanly. So sometimes I make a\n> temporary branch from A, commit the fixup there:\n> \n>   ---o               (master)\n>       \\\n>        o---A---B---C (topic)\n>             \\\n>              f!A     (temp)\n> \n> and then use \"git rebase --onto temp A topic\" to move the topic back\n> on track:\n> \n>   ---o                     (master)\n>       \\\n>        o---A---f!A         (temp)\n>                   \\\n>                    B'---C' (topic)\n> \n> after which the final cleanup rebase is much easier to do.\n> \n> Obviously, all the branch switching and rebasing does take its tall\n> on file modifications.\n\nFrom use case you described (and which I often experience myself), it \nseems plain \"git commit --onto A\" would be of help here, committing \nfixup onto A directly, without a need to switch to it (branch or \nnot), a case I`m discussing with Hannes in that other sub-thread[1] of \nthis e-mail, too.\n\nBut from there, your flow takes a different direction, using rebase, \nwhile this whole thread started around some merge-like functionality.\n\nI can imagine a user interface doing what you (and I) would like, \nsomething like:\n\n(1) git commit --onto A --rebase\n\n..., where your changes would first be committed onto commit A, and \nthen commits from A (excluded) to HEAD (included) rebased onto this \nnew commit.\n\nBUT, as far as it seems to me, rebase currently touches working tree \nfor each operation (am I wrong here?), so once the rebase sequence is \ninitiated, it would internally still need to checkout to your new \nfixup commit (on top of A), and then proceed applying changes and \nchanging working tree with each commit being rebased, overall failing \nto address your main concern - needless (untouched) file \nmodifications, even in case of no conflicts.\n\nI find this scenario quite interesting as well, but I`m afraid it may \ncurrently be out of scope of what I`m trying to accomplish with \"git \ncommit --onto[-parent]\", for the most part because it looks like it \nwould need \"index only rebase\" first (not touching working tree, that \nis)...?\n\nIf we had that, it would/should be pretty easy to add it into the mix \nwith \"git commit --onto\" here, ending up with something as imagined \nin command line (1) above :) I`ll make a note of it, thanks.\n\nAny further help appreciated, of course :)\n\nRegards, Buga\n\n[1] https://public-inbox.org/git/0F30BEF8-A9F7-43B9-BC89-4B9CD7AF3E16@gmail.com/T/#me830a80d745df60ae8bd6a2e67eee4bd4dabf56c\n"},{"id":"334030","messageId":"7ae3ffd5-147d-55d2-9630-da12c429d631@gmail.com","threadId":"47325","inReplyTo":"33e97533-716b-e1cc-6aa0-bf8941225319@kdbg.org","subject":"Re: [SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge, no working tree file changes)","fromName":"Igor Djordjevic","fromEmail":"igor.d.djordjevic@gmail.com","sentAt":"2017-12-04T02:33:35Z","receivedAt":"2017-12-04T02:33:43Z","isPatch":false,"sender":{"key":"igor.d.djordjevic@gmail.com","avatar":null},"body":"Hi Hannes,\n\nOn 01/12/2017 18:23, Johannes Sixt wrote:\n> \n> > To work with `--onto-parent` and be able to commit on top of any of\n> > the topic branches, you would need a situation like this instead:\n> >\n> >   (1)  ...C      <- topic C\n> >           |\n> >      ...A |      <- topic A\n> >          \\|\n> >        ...o I    <- integration\n> >          /|\n> >      ...B |      <- topic B\n> >           |\n> >        ...D      <- topic D\n> \n> This is a very, VERY exotic workflow, I would say. How would you\n> construct commit I when three or more topics have conflicts?\n> merge-octopus does not support this use-case.\n\nBut I`m not interested in constructing commit I in the first place, \nonly help working with it once it`s already there (which shouldn`t be \ntoo uncommon in case of unrelated, non-conflicting topics) - but I \nalready agreed my example could have been a bit too esoteric, \ndistracting attention from the important part :)\n\nI`ll continue on this further below, where you commented on those \nmore common scenarios.\n\nHere, let`s try to look at the whole situation from a \"patch queue\" \nperspective instead, starting from something like this:\n\n(5) ---O---I  <- integration <- HEAD\n\n... and then making progress like this - first, commit A1 onto O, \nstarting \"topicA\" branch at the same time, even, maybe using syntax \nlike `git commit --onto-parent O -b topicA`:\n\n(6) ---O---J  <- integration <- HEAD               [ J = I + A1 ]\n        \\ /\n         A1   <- topicA\n\n..., then commit B1 onto O to start \"topicB\" branch:\n\n(7)      B1   <- topicB\n        / \\\n    ---O---K  <- integration <- HEAD               [ K = J + B1 ]\n        \\ /\n         A1   <- topicA\n\n..., then add one more commit (patch) onto B1:\n\n(8)      B1---B2   <- topicB\n        /      \\\n    ---O--------L  <- integration <- HEAD          [ L = K + B2 ]\n        \\      /\n         A1---/    <- topicA\n\n..., and one more, B3:\n\n(9)      B1---B2---B3   <- topicB\n        /           \\\n    ---O-------------M  <- integration <- HEAD     [ M = L + B3 ]\n        \\           /\n         A1--------/    <- topicA\n\nWe can also start a new topic branch (queue), commit C1:\n\n(10)     B1---B2---B3   <- topicB\n        /           \\\n    ---O-------------N  <- integration <- HEAD     [ N = M + C1 ]\n       |\\           /|\n       | A1--------/ /  <- topicA\n       \\            /\n        C1---------/    <- topicC\n\nAnd lets make one more \"topicA\" related commit A2:\n\n(11)     B1---B2---B3   <- topicB\n        /           \\\n    ---O-------------P  <- integration <- HEAD     [ P = N + A2 ]\n       |\\           /|\n       | A1---A2---/ /  <- topicA\n       \\            /\n        C1---------/    <- topicC\n\n\nNotice how HEAD never leaves \"integration\" branch, and underlying \ncommit is recreated each time? It`s like a live branch we`re working \non, but we`re not actually committing to.\n\nNo branch switching (and no working tree file changes caused by it), \nand we`re working on multiple topics/branches simultaneously, being \nable to instantly test their mutual interaction as we go, but also \ncreating their separate (and \"clean\") histories at the same time.\n\nI guess this would make most sense with simple topics we _could_ \npractically work on at the same time without making our life too \ncomplicated - what stands for \"git add --patch\", too, when working on \nmultiple commits at the same time.\n\nOnce satisfied, of course each topic branch would need to be tested \nseparately, and each commit, even - all the same as with \"git add \n--patch\" commits.\n\nAnd \"git add --patch\" can still be used here, too, to distribute \npartial changes, currently existing together inside the working tree, \nto different topic branches, at the time of making the commit itself.\n\nDoes this approach make more sense in regards to \"git commit \n--onto-parent\" functionality I`m having in mind? Or I`m dreaming too \nmuch here...? :)\n\n> > Once there, starting from your initial position:\n> >\n> > >     ...A    ...C            <- topics A, C\n> > >         \\       \\ E\n> > >       ---o---o---o---o I    <- integration <- HEAD\n> > >             /       /\n> > >         ...B    ...D        <- topics B, D\n> >\n> > ... and doing something like `git commit --onto B --merge` would\n> > yield:\n> > \n> >     (3) ...A    ...C            <- topics A, C\n> >           \\       \\ E\n> >         ---o---o---o---o I'   <- integration\n> >               /       /|\n> >           ...B    ...D |      <- topic D\n> >               \\        |\n> >                f-------'      <- topic B\n> >\n> > ... where (I' = I + f) is still true.\n> \n> I am not used to this picture. I would not think that it is totally\n> unacceptable, but it still has a hmm-factor.\n\nMain idea is not to pile up uninteresting merge commits inside \n(throwaway) integration branch, needlessly complicating history, but \npretend as we just made the integration merge, where fixup commit f \nwas already existing in its topic branch prior the merge.\n\n> > If that`s preferred in some\n> > cases, it could even look like this instead:\n> >\n> >   (4) ...A    ...C             <- topics A, C\n> >           \\       \\ E  I\n> >         ---o---o---o---o---F   <- integration\n> >               /       /   /\n> >           ...B    ...D   /     <- topic D\n> >               \\         /\n> >                f-------'       <- topic B\n> >\n> > ... where F(4) = I'(3), so similar situation, just that we don`t\n> > discard I but post F on top of it.\n> \n> This is very acceptable.\n\nAnd I can see logic behind this case, too (alongside that other one, \nwhere they can both be supported, for different scenarios).\n\n> Nevertheless, IMO, it is not the task of git-commit to re-compute a\n> merge commit. It would be OK that it commits changes on top of a\n> branch that is not checked out. Perhaps it would even be OK to remove\n> the change from the current workspace (index and worktree), because\n> it will return in the form of a merge later, but producing that merge\n> is certainly not the task of git-commit.\n\nJust to make sure we`re on the same page - you mean conceptually, or \npractically? Or both...? :)\n\nBecause practically, we don`t do any merge operations/computations \n(as can be seen inside the script), so we really don`t produce any \nmerges :)\n\nHaving a merge commit being updated as an outcome is just a \nconsequence of altering our history to pretend fixup commit already \nexisted elsewhere, being brought into our current state from there - \nand that \"current state\" is updated accordingly to reflect the change \nwe made in the history that led to it.\n\nLet`s look at a different example, no merges involved:\n\n(12) ---A---B---C---D  <- HEAD\n\nHere, we can do `git commit --onto-parent C` to make the history look \nlike this:\n\n(13) ---A---B---C---E---D'  <- HEAD\n\n..., where we practically inserted commit E between our HEAD and \ncommit C, causing D to be updated to D' accordingly (rebased, \npractically).\n\nIdea is the same with merges involved, updating HEAD commit to \nreflect altered history.\n\nIn situation (12), we could also do `git commit --onto-parent C \n--amend`, ending up with history like this instead:\n\n(14) ---A---B---F---D'  <- HEAD\n\n..., where F is amended C, E being added to it, instead of being a \nseparate commit.\n\nSo there are quite some possibilities here.\n\nBut as said, I can understand a wish to have a commit elsewhere \nwithout \"merging\" it in, too (changes removed from current workspace), \nor \"merging\" it inside a separate commit (not updating existing \nHEAD one).\n\nAnd saying that, making the merge commit at the same time could then \nbe observed as no more than a shortcut, too, like being able to \ncreate a new branch on checkout using \"-b\" option.\n\nSo even conceptually, it would make sense to wish for something like \nthis:\n\n(15) git commit --onto B --merge\n\n..., producing that graph (4) shown in the quote above, I would \nthink? \n\nBut even in that case, do note no merge is really happening, we just \nalter history of our current state, where we already are.\n\nSplitting changes out of HEAD to a commit directly preceding it, on a \ndifferent branch, and still keeping those changes in HEAD, naturally \ncauses our current HEAD state to have multiple parents, what is \nperceived as a merge commit, even without a merge operation leading \nto it.\n\nThus, `git commit` seems it should be up to the task, whichever \napproach user opts for :)\n\nRegards, Buga\n"},{"id":"334265","messageId":"39323748-282c-5881-2bfa-de622bb8b765@kdbg.org","threadId":"47325","inReplyTo":"7ae3ffd5-147d-55d2-9630-da12c429d631@gmail.com","subject":"Re: [SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge, no working tree file changes)","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2017-12-06T18:34:26Z","receivedAt":"2017-12-06T18:34:59Z","isPatch":false,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"I am sorry for not responding in detail. I think we've reached a mutual \nunderstanding of our workflows.\n\nThough, from the ideas you tossed around most recently, you seem to want \nto make git-commit into a kitchen-sink for everything. I have my doubts \nthat this will be a welcome change. Just because new commits are created \ndoes not mean that the feature must live in git-commit.\n\n-- Hannes\n\n"},{"id":"334266","messageId":"CAPc5daWupO6DMOMFGn=XjUCG-JMYc4eyo8+TmAsdWcAOHXzwWg@mail.gmail.com","threadId":"47325","inReplyTo":"39323748-282c-5881-2bfa-de622bb8b765@kdbg.org","subject":"Re: [SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge, no working tree file changes)","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-12-06T18:40:24Z","receivedAt":"2017-12-06T18:41:09Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"On Wed, Dec 6, 2017 at 10:34 AM, Johannes Sixt <j6t@kdbg.org> wrote:\n> I am sorry for not responding in detail. I think we've reached a mutual\n> understanding of our workflows.\n>\n> Though, from the ideas you tossed around most recently, you seem to want to\n> make git-commit into a kitchen-sink for everything. I have my doubts that\n> this will be a welcome change. Just because new commits are created does not\n> mean that the feature must live in git-commit.\n\nNicely put.\n"},{"id":"334393","messageId":"f9a94a62-9541-e019-8ab3-9fc9cfe2c43f@gmail.com","threadId":"47325","inReplyTo":"CAPc5daWupO6DMOMFGn=XjUCG-JMYc4eyo8+TmAsdWcAOHXzwWg@mail.gmail.com","subject":"Re: [SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge, no working tree file changes)","fromName":"Igor Djordjevic","fromEmail":"igor.d.djordjevic@gmail.com","sentAt":"2017-12-08T00:15:25Z","receivedAt":"2017-12-08T00:15:49Z","isPatch":false,"sender":{"key":"igor.d.djordjevic@gmail.com","avatar":null},"body":"On 06/12/2017 19:34, Johannes Sixt wrote:\n> \n> I am sorry for not responding in detail. I think we've reached a \n> mutual understanding of our workflows.\n\nNo problem, thanks for your time so far.\n\nThere might be one more thing I should address, possibly left unclear \nfrom my previous message, but I`ll leave that for a follow-up e-mail, \nnot being that important at the moment for the topic itself.\n\nOn 06/12/2017 19:40, Junio C Hamano wrote:\n> \n> > Though, from the ideas you tossed around most recently, you seem to\n> > want to make git-commit into a kitchen-sink for everything. I have\n> > my doubts that this will be a welcome change. Just because new\n> > commits are created does not mean that the feature must live in\n> > git-commit.\n> \n> Nicely put.\n\nYeah, I understand that might have felt cluttering, besides also \nbeing out of scope of the original topic idea. Thanks for the reality \ncheck (to both).\n\nTo get back on track, and regarding what`s already been said, would \nhaving something like this(1) feel useful?\n\n(1) git commit --onto <commit>\n\nSo in previously mentioned situation:\n\n(2) ...A    ...C            <- topics A, C\n        \\       \\\n      ---o---o---o---o I    <- integration <- HEAD\n            /       /\n        ...B    ...D        <- topics B, D\n\n... it would allow committing changes F inside HEAD on top of B \ndirectly, no checkout / branch switching needed, getting to:\n\n(3) ...A    ...C            <- topics A, C\n        \\       \\\n      ---o---o---o---o I    <- integration <- HEAD\n            /       /\n        ...B    ...D        <- topic D\n            \\\n             F              <- topic B\n\nSo the most conservative approach, where changes F are removed from \nHEAD index and working tree, leaving it up to the user to decide if \nhe will then merge them back in (or do something else).\n\nI stress the major selling point here still being avoiding branch \nswitching back and forth in order to commit a fixup on a different \nbranch, which could otherwise trigger needless rebuilds, being \nsignificant in large projects.\n\nAnd thanks to that `git-merge-one-file--cached`[1] script, we are \nalso able to resolve some more of trivial conflicts when applying F \nonto B, using three-way file merge when needed, but still not \ntouching working tree (contrary to original `git-merge-one-file`).\n\nRegards, Buga\n\n[1] https://public-inbox.org/git/CAPc5daWupO6DMOMFGn=XjUCG-JMYc4eyo8+TmAsdWcAOHXzwWg@mail.gmail.com/T/#mcb3953542dc265516e3ab1bff006ff1b5b85126a\n"},{"id":"334451","messageId":"xmqqo9n99ohc.fsf@gitster.mtv.corp.google.com","threadId":"47325","inReplyTo":"f9a94a62-9541-e019-8ab3-9fc9cfe2c43f@gmail.com","subject":"Re: [SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge, no working tree file changes)","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-12-08T16:24:47Z","receivedAt":"2017-12-08T16:24:56Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:\n\n> To get back on track, and regarding what`s already been said, would \n> having something like this(1) feel useful?\n>\n> (1) git commit --onto <commit>\n\nAre you asking me if _I_ find it useful?  It is not a very useful\nquestion to ask, as I've taken things that I do not find useful\nmyself.\n\nHaving said that, I do not see me personally using it.  You keep\nclaiming that committing without ever materializing the exact state\nthat is committed in the working tree is a good thing.\n\nI do not subscribe to that view.  \n\nI'd rather do a quick fix-up on top (which ensures that at least the\nfix-up works in the context of the tip), and then \"rebase -i\" to\nmove it a more appropriate place in the history (during which I have\na chance to ensure that the fix-up works in the context it is\nintended to apply to).\n\nI know that every time I say this, people who prefer to commit\nthings that never existed in the working tree will say \"but we'll\ntest it later after we make these commit without having their state\nin the working tree\".  But I also know better that \"later\" often do\nnot come, ever, at least for people like me ;-).\n\nThe amount of work _required_ to record the fix-up at its final\nresting place deeper in the history would be larger with \"rebase -i\"\napproach, simply because approaches like \"commit --onto\" and \"git\npost\" that throw a new commit deep in the history would not require\never materializing it in the working tree.  But because I care about\nwhat I am actually committing, and because I am just lazy as any\nother human (if not more), I'd prefer an apporach that _forces_ me\nto have a checkout of the exact state that I'd be committing.  That\nwould prod me to actually looking at and testing the state after the\nchange in the context it is meant to go.\n"},{"id":"334523","messageId":"a3510c14-23e9-d1d9-0847-b60451f8e15d@gmail.com","threadId":"47325","inReplyTo":"xmqqo9n99ohc.fsf@gitster.mtv.corp.google.com","subject":"Re: [SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge, no working tree file changes)","fromName":"Igor Djordjevic","fromEmail":"igor.d.djordjevic@gmail.com","sentAt":"2017-12-08T23:54:06Z","receivedAt":"2017-12-08T23:54:21Z","isPatch":false,"sender":{"key":"igor.d.djordjevic@gmail.com","avatar":null},"body":"On 08/12/2017 17:24, Junio C Hamano wrote:\n> \n> > To get back on track, and regarding what`s already been said,\n> > would having something like this(1) feel useful?\n> >\n> > (1) git commit --onto <commit>\n> \n> Are you asking me if _I_ find it useful?  It is not a very useful\n> question to ask, as I've taken things that I do not find useful\n> myself.\n\nIt was also (kind of shy and subtle) \"would you take it?\", indeed, \nbut I do value your personal opinion here, too, being a recognized \ndeveloper, and one really knowing the Git (mailing list) community on \ntop of it, so I appreciate you addressed both sides of the question.\n\nAnd it was partly addressed to Hannes, but more for a confirmation, I \nguess, him being the one to favor such a flow in the first place, \nover what I initially suggested.\n\n> Having said that, I do not see me personally using it. You keep\n> claiming that committing without ever materializing the exact state\n> that is committed in the working tree is a good thing.\n> \n> I do not subscribe to that view.  \n\nNo - and I find it an important difference to note - just that it \nmight be acceptable / more preferable _in certain situations_, where \nthe only alternative seems to be wasting (significant) amount of time \non needless rebuilds of many files (just because of branch switching, \notherwise untouched by the changes we`re interested in).\n\nIf this is perceived a too uncommon/exotic case to worth addressing \nis a different matter, though.\n\n> I'd rather do a quick fix-up on top (which ensures that at least the\n> fix-up works in the context of the tip), and then \"rebase -i\" to\n> move it a more appropriate place in the history (during which I have\n> a chance to ensure that the fix-up works in the context it is\n> intended to apply to).\n\nChris reported in this very topic[1] that sometimes, due to conflicts \nwith later commits, \"checkout > commit > [checkout >] rebase --onto\" \nis \"much easier to do\", where \"commit --fixup > rebase -i\" \"breaks\" \n(does not apply cleanly).\n\n> I know that every time I say this, people who prefer to commit\n> things that never existed in the working tree will say \"but we'll\n> test it later after we make these commit without having their state\n> in the working tree\".  But I also know better that \"later\" often do\n> not come, ever, at least for people like me ;-).\n\nNo comment here ;)\n\n> The amount of work _required_ to record the fix-up at its final\n> resting place deeper in the history would be larger with \"rebase -i\"\n> approach, simply because approaches like \"commit --onto\" and \"git\n> post\" that throw a new commit deep in the history would not require\n> ever materializing it in the working tree.  But because I care about\n> what I am actually committing, and because I am just lazy as any\n> other human (if not more), I'd prefer an apporach that _forces_ me\n> to have a checkout of the exact state that I'd be committing.  That\n> would prod me to actually looking at and testing the state after the\n> change in the context it is meant to go.\n\nAll that I agree with, too.\n\nBut that said, I do find `git add --patch` invaluable (for example), \nwhere one can still opt to commit right away (and test later ;)), or \ndo a proper `git stash push --keep-index` first in order to actually \ncheck/test the exact state/context before committing.\n\nOne of the biggest advantages I see in using Git is that it provides \nso many possibilities, where there is not necessarily a single \n\"correct\" way to do something - depending on the (sub)context, the \ndecision on \"_the_ correct\" way can be deferred to the user himself.\n\nGit (usually) does not judge, except in cases where something is \nconsidered \"plain wrong\" - still different than \"might not be the \nbest approach\", but not necessarily a wrong one, either.\n\nBut I do realize it also means more chances for beginner users to \nshoot themselves in the foot, blaming it on Git, so even if just a \nmatter of personal taste, a more restrictive preference from the Git \nmaintainer is understandable :)\n\nRegards, Buga\n\n[1] https://public-inbox.org/git/CAPc5daWupO6DMOMFGn=XjUCG-JMYc4eyo8+TmAsdWcAOHXzwWg@mail.gmail.com/T/#m989306ab9327e15f14027cfd74ae8c5bf487affb\n"},{"id":"334525","messageId":"D842B04A-9331-4F26-8F19-B61F6F13FC79@gmail.com","threadId":"47325","inReplyTo":"a3510c14-23e9-d1d9-0847-b60451f8e15d@gmail.com","subject":"Re: [SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge, no working tree file changes)","fromName":"Alexei Lozovsky","fromEmail":"a.lozovsky@gmail.com","sentAt":"2017-12-09T02:18:35Z","receivedAt":"2017-12-09T02:18:47Z","isPatch":false,"sender":{"key":"a.lozovsky@gmail.com","avatar":"https://gravatar.com/avatar/8bb8ff5ec366dd64bd8e08f768082934513da367ae047bcb0039292e4ed6bda5?d=mp&s=160"},"body":"On Dec 9, 2017, at 01:54, Igor Djordjevic wrote:\n> On 08/12/2017 17:24, Junio C Hamano wrote:\n>> I'd rather do a quick fix-up on top (which ensures that at least the\n>> fix-up works in the context of the tip), and then \"rebase -i\" to\n>> move it a more appropriate place in the history (during which I have\n>> a chance to ensure that the fix-up works in the context it is\n>> intended to apply to).\n> \n> Chris reported in this very topic[1] that sometimes, due to conflicts \n> with later commits, \"checkout > commit > [checkout >] rebase --onto\" \n> is \"much easier to do\", where \"commit --fixup > rebase -i\" \"breaks\" \n> (does not apply cleanly).\n\nIt was more of a rant about conflict resolution by rebase rather than\na concern about modification time of files. While I'd prefer git to\nnot touch the source tree unnecessarily, it's not really a big deal\nfor me if it does and some parts of the project need to be rebuilt.\n\nThe situations which tend to cause conflicts for me usually look\nlike this.\n\n  ---A---B\n\nSay, a file has this line added somewhere in commit A:\n\n  +int some_function(); \n\nThen commit B adds another line:\n\n   int some_function(); \n  +int another_function();\n\nThen I realize that I would like to have commit A to introduce some\nother_function() as well, so I add a fixup:\n\n  ---A---B---f!A\n\nwith the following diff:\n\n   int some_function(); \n  +int other_function();\n   int another_function();\n\nAnd then I often find that \"rebase -i --autosquash\" fails to apply\nthe commit B because it expects slightly different context around\nthe changed lines.\n\nHowever, sometimes I see that if I do this:\n\n  ---A---B\n      \\\n       f!A\n\nthen \"rebase --onto f!A A B\" succeeds, nicely resolving the conflict\nwithout bothering me. No idea why. I kinda hoped that you may know\nthis magic and incorporate it into \"commit --onto\" which will allow\nto immediately get to the result of the rebase:\n\n  ---A---f!A---B'\n\nwithout spelling it all manually.\n\n(And yeah, I'm actually Alexei, not Chris. That was my MUA being dumb\nand using an old pseudonym than Google insists I'm called by.)\n"},{"id":"334526","messageId":"92643df4-f54e-cd31-da4a-138ec314655a@gmail.com","threadId":"47325","inReplyTo":"D842B04A-9331-4F26-8F19-B61F6F13FC79@gmail.com","subject":"Re: [SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge, no working tree file changes)","fromName":"Igor Djordjevic","fromEmail":"igor.d.djordjevic@gmail.com","sentAt":"2017-12-09T03:03:53Z","receivedAt":"2017-12-09T03:04:03Z","isPatch":false,"sender":{"key":"igor.d.djordjevic@gmail.com","avatar":null},"body":"Hi Alexei,\n\nOn 09/12/2017 03:18, Alexei Lozovsky wrote:\n> \n> > Chris reported in this very topic[1] that sometimes, due to\n> > conflicts with later commits, \"checkout > commit > [checkout >]\n> > rebase --onto\" is \"much easier to do\", where \"commit --fixup >\n> > rebase -i\" \"breaks\" (does not apply cleanly).\n> \n> It was more of a rant about conflict resolution by rebase rather than\n> a concern about modification time of files. While I'd prefer git to\n> not touch the source tree unnecessarily, it's not really a big deal\n> for me if it does and some parts of the project need to be rebuilt.\n\nNevertheless, I found it valuable in supporting the case where \n\"commit --fixup > rebase -i\" seems to require even more work than \notherwise necessary :)\n\nBut thanks for clarifying, anyway, it does feel like `git rebase -i \n--autosquash` could be smarter in this regards, if `git rebase \n--onto` does it better...?\n\nEven though your explanation seems clear, having a real, easily \nreproducible case would help as well, I guess.\n\n> I kinda hoped that you may know this magic and incorporate it into \n> \"commit --onto\" which will allow to immediately get to the result of \n> the rebase:\n> \n>   ---A---f!A---B'\n> \n> without spelling it all manually.\n\nIf you mind enough to be bothered testing it out, might be even \nexisting/initial state of originally proposed `git commit \n--onto-parent` script would work for you, as it does incorporate some \ntrivial three-way merge resolution.\n\nIn your starting situation:\n\n    ---A---B\n\n... you would just do something like:\n\n    git commit --onto-parent A\n\n... hopefully ending up in the desired state (hopefully = conflicts \nautomatically resolved):\n\n    ---A---C---B'\n\nYou could even do this instead:\n\n    git commit --onto-parent A --amend\n\n... ending up with:\n\n    ---A'---B'\n\n... as that is basically what you wanted in the first place ;)\n\n> (And yeah, I'm actually Alexei, not Chris. That was my MUA being\n> dumb and using an old pseudonym than Google insists I'm called by.)\n\nAh, sorry for the confusion :)\n\nRegards, Buga\n"},{"id":"334544","messageId":"4a92e34c-d713-25d3-e1ac-100525011d3f@talktalk.net","threadId":"47325","inReplyTo":"92643df4-f54e-cd31-da4a-138ec314655a@gmail.com","subject":"Re: [SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge, noworking tree file changes)","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-12-09T19:01:17Z","receivedAt":"2017-12-09T19:01:29Z","isPatch":false,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Igor\n\nOn 09/12/17 03:03, Igor Djordjevic wrote:\n> \n> Hi Alexei,\n> \n> On 09/12/2017 03:18, Alexei Lozovsky wrote:\n>>\n>>> Chris reported in this very topic[1] that sometimes, due to\n>>> conflicts with later commits, \"checkout > commit > [checkout >]\n>>> rebase --onto\" is \"much easier to do\", where \"commit --fixup >\n>>> rebase -i\" \"breaks\" (does not apply cleanly).\n>>\n>> It was more of a rant about conflict resolution by rebase rather than\n>> a concern about modification time of files. While I'd prefer git to\n>> not touch the source tree unnecessarily, it's not really a big deal\n>> for me if it does and some parts of the project need to be rebuilt.\n> \n> Nevertheless, I found it valuable in supporting the case where \n> \"commit --fixup > rebase -i\" seems to require even more work than \n> otherwise necessary :)\n> \n> But thanks for clarifying, anyway, it does feel like `git rebase -i \n> --autosquash` could be smarter in this regards, if `git rebase \n> --onto` does it better...?\n\nCreating the fixup directly on A rather than on top of B avoids the\nconflicting merge B f!A A. Creating the fixup on top of B and then using\ngit commit --onto A would suffer from the same conflicts as rebase does.\nI don't think there is any way for 'git rebase --autosquash' to avoid\nthe conflicts unless it used a special fixup merge strategy that somehow\ntook advantage of the DAG to resolve the conflicts by realizing they\ncome from a later commit. However I don't think that could be\nimplemented reliably as sometimes one wants those conflicting lines from\nthe later commit to be moved to the earlier commit with the fixup.\n\nBest Wishes\n\nPhillip\n\n> \n> Even though your explanation seems clear, having a real, easily \n> reproducible case would help as well, I guess.\n> \n>> I kinda hoped that you may know this magic and incorporate it into \n>> \"commit --onto\" which will allow to immediately get to the result of \n>> the rebase:\n>>\n>>   ---A---f!A---B'\n>>\n>> without spelling it all manually.\n> \n> If you mind enough to be bothered testing it out, might be even \n> existing/initial state of originally proposed `git commit \n> --onto-parent` script would work for you, as it does incorporate some \n> trivial three-way merge resolution.\n> \n> In your starting situation:\n> \n>     ---A---B\n> \n> .... you would just do something like:\n> \n>     git commit --onto-parent A\n> \n> .... hopefully ending up in the desired state (hopefully = conflicts \n> automatically resolved):\n> \n>     ---A---C---B'\n> \n> You could even do this instead:\n> \n>     git commit --onto-parent A --amend\n> \n> .... ending up with:\n> \n>     ---A'---B'\n> \n> .... as that is basically what you wanted in the first place ;)\n> \n>> (And yeah, I'm actually Alexei, not Chris. That was my MUA being\n>> dumb and using an old pseudonym than Google insists I'm called by.)\n> \n> Ah, sorry for the confusion :)\n> \n> Regards, Buga\n> \n\n"},{"id":"334546","messageId":"3393df67-4170-2ccb-0365-bac6e43c4466@philandanna.no-ip.org","threadId":"47325","inReplyTo":"92643df4-f54e-cd31-da4a-138ec314655a@gmail.com","subject":"Re: [SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge,noworking tree file changes)","fromName":"Phillip Wood","fromEmail":"phil@philandanna.no-ip.org","sentAt":null,"receivedAt":"2017-12-09T19:09:35Z","isPatch":false,"sender":{"key":"phil@philandanna.no-ip.org","avatar":null},"body":"\nHi Igor\n\nOn 09/12/17 03:03, Igor Djordjevic wrote:\n> \n> Hi Alexei,\n> \n> On 09/12/2017 03:18, Alexei Lozovsky wrote:\n>>\n>>> Chris reported in this very topic[1] that sometimes, due to\n>>> conflicts with later commits, \"checkout > commit > [checkout >]\n>>> rebase --onto\" is \"much easier to do\", where \"commit --fixup >\n>>> rebase -i\" \"breaks\" (does not apply cleanly).\n>>\n>> It was more of a rant about conflict resolution by rebase rather than\n>> a concern about modification time of files. While I'd prefer git to\n>> not touch the source tree unnecessarily, it's not really a big deal\n>> for me if it does and some parts of the project need to be rebuilt.\n> \n> Nevertheless, I found it valuable in supporting the case where \n> \"commit --fixup > rebase -i\" seems to require even more work than \n> otherwise necessary :)\n> \n> But thanks for clarifying, anyway, it does feel like `git rebase -i \n> --autosquash` could be smarter in this regards, if `git rebase \n> --onto` does it better...?\n\nCreating the fixup directly on A rather than on top of B avoids the\nconflicting merge B f!A A. Creating the fixup on top of B and then using\ngit commit --onto A would suffer from the same conflicts as rebase does.\nI don't think there is any way for 'git rebase --autosquash' to avoid\nthe conflicts unless it used a special fixup merge strategy that somehow\ntook advantage of the DAG to resolve the conflicts by realizing they\ncome from a later commit. However I don't think that could be\nimplemented reliably as sometimes one wants those conflicting lines from\nthe later commit to be moved to the earlier commit with the fixup.\n\nBest Wishes\n\nPhillip\n\n> \n> Even though your explanation seems clear, having a real, easily \n> reproducible case would help as well, I guess.\n> \n>> I kinda hoped that you may know this magic and incorporate it into \n>> \"commit --onto\" which will allow to immediately get to the result of \n>> the rebase:\n>>\n>>   ---A---f!A---B'\n>>\n>> without spelling it all manually.\n> \n> If you mind enough to be bothered testing it out, might be even \n> existing/initial state of originally proposed `git commit \n> --onto-parent` script would work for you, as it does incorporate some \n> trivial three-way merge resolution.\n> \n> In your starting situation:\n> \n>     ---A---B\n> \n> .... you would just do something like:\n> \n>     git commit --onto-parent A\n> \n> .... hopefully ending up in the desired state (hopefully = conflicts \n> automatically resolved):\n> \n>     ---A---C---B'\n> \n> You could even do this instead:\n> \n>     git commit --onto-parent A --amend\n> \n> .... ending up with:\n> \n>     ---A'---B'\n> \n> .... as that is basically what you wanted in the first place ;)\n> \n>> (And yeah, I'm actually Alexei, not Chris. That was my MUA being\n>> dumb and using an old pseudonym than Google insists I'm called by.)\n> \n> Ah, sorry for the confusion :)\n> \n> Regards, Buga\n> \n\n"},{"id":"334556","messageId":"da74fb2c-c452-4716-91d2-182f945b4254@gmail.com","threadId":"47325","inReplyTo":"4a92e34c-d713-25d3-e1ac-100525011d3f@talktalk.net","subject":"Re: [SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge, noworking tree file changes)","fromName":"Igor Djordjevic","fromEmail":"igor.d.djordjevic@gmail.com","sentAt":"2017-12-10T01:20:12Z","receivedAt":"2017-12-10T01:20:26Z","isPatch":false,"sender":{"key":"igor.d.djordjevic@gmail.com","avatar":null},"body":"Hi Philip,\n\nOn 09/12/2017 20:01, Phillip Wood wrote:\n> \n> > But thanks for clarifying, anyway, it does feel like `git rebase\n> > -i --autosquash` could be smarter in this regards, if `git rebase \n> > --onto` does it better...?\n> \n> Creating the fixup directly on A rather than on top of B avoids the \n> conflicting merge B f!A A. Creating the fixup on top of B and then\n> using git commit --onto A would suffer from the same conflicts as\n> rebase does.\n\nI`m a bit confused here, as you`re replying to the part where we \nstrictly discussed `rebase --autosquash` versus `rebase --onto`, \nhaving the latter succeed where the former fails - but you`re \nmentioning `git _commit_ --onto` instead, comparing it with `rebase`... \nand which one of the two (\"--autosquash\", I assume)?\n\nEven further, while I do seem to understand (and agree with) what \nyou`re talking about with `commit --onto` and `rebase --autosquah` \nsuffering from the same conflicts in attempt to take f!A, originally \ncreated on top of B, and apply it on top of A - the thing is that \nAlexei actually pointed to B being the problematic one, failing to \nrebase on top of already (successfully) autosquashed A' (where A' = A \n+ f!A, fixup applied through --autosquash), while it doesn`t fail \nrebasing --onto f!A when f!A is being committed on top of A directly \n(and not through --autosquash).\n\nIn that (very?) specific case, proposed `git commit --onto-parent`[1] \ndoesn`t suffer from this, as once f!A is successfully applied onto A \n(either squashed in with --amend, or on top of it), we take original \nf!A _snapshot_ (not patch!) made on top of B, and just \"declare\" it \nB` (being equal to B + f!A, which we already know, and being \ncorrect), without a need to (try to) apply B patch on top of fixed-up \nA to create B', as `rebase` does (and fails).\n\n> I don't think there is any way for 'git rebase --autosquash' to\n> avoid the conflicts unless it used a special fixup merge strategy\n> that somehow took advantage of the DAG to resolve the conflicts by\n> realizing they come from a later commit. However I don't think that\n> could be implemented reliably as sometimes one wants those\n> conflicting lines from the later commit to be moved to the earlier\n> commit with the fixup.\n\nI think I agree on this part being tricky (if possible at all), but I \nalso think this is not what Alexei was complaining about, nor what we \nwere discussing (as I tried to explain above) - but please do correct \nme if I misunderstood you.\n\nThat said, and what I mentioned already, we might really benefit from \nsimple test case(s), showing \"rebase --autosquash\" failing where \n\"rebase --onto\" works, as Alexei explained, giving some more (and \nfirm) context to the discussion.\n\nI *think* I`ve experienced this in the past myself, but now I can`t \nseem to wrap my head around a reproducible example just yet... :$\n\nRegards, Buga\n\n[1] https://public-inbox.org/git/4a92e34c-d713-25d3-e1ac-100525011d3f@talktalk.net/T/#m72f45ad7a8f1c733266a875bca087ee82cc781e7\n"},{"id":"334562","messageId":"82da4317-6b50-f60d-6d8f-50fc47579c56@talktalk.net","threadId":"47325","inReplyTo":"da74fb2c-c452-4716-91d2-182f945b4254@gmail.com","subject":"Re: [SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge,noworking tree file changes)","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-12-10T12:22:35Z","receivedAt":"2017-12-10T12:22:47Z","isPatch":false,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 10/12/17 01:20, Igor Djordjevic wrote:\n> \n> Hi Philip,\n> \n> On 09/12/2017 20:01, Phillip Wood wrote:\n>>\n>>> But thanks for clarifying, anyway, it does feel like `git rebase\n>>> -i --autosquash` could be smarter in this regards, if `git rebase \n>>> --onto` does it better...?\n>>\n>> Creating the fixup directly on A rather than on top of B avoids the \n>> conflicting merge B f!A A. Creating the fixup on top of B and then\n>> using git commit --onto A would suffer from the same conflicts as\n>> rebase does.\n> \n> I`m a bit confused here, as you`re replying to the part where we \n> strictly discussed `rebase --autosquash` versus `rebase --onto`, \n> having the latter succeed where the former fails\n\nSorry I should have been clearer. The point I was somewhat obliquely\nmaking was that 'rebase --onto' succeeds where 'rebase --autosquash'\nfails not because it is smarter but because it is doing something\ndifferent. Specifically it avoids the conflicting merge to create A' as\nthe user has already created that commit in the temporary branch\n\n\n> - but you`re \n> mentioning `git _commit_ --onto` instead, comparing it with `rebase`... \n> and which one of the two (\"--autosquash\", I assume)?\n\nYes because in an earlier message you said\n\n> If you mind enough to be bothered testing it out, might be even\n> existing/initial state of originally proposed `git commit\n> --onto-parent` script would work for you, as it does incorporate some\n> trivial three-way merge resolution.\n>\n> In your starting situation:\n>\n>     ---A---B\n>\n> .... you would just do something like:\n>\n>     git commit --onto-parent A\n>\n> .... hopefully ending up in the desired state (hopefully = conflicts\n> automatically resolved):\n>\n>     ---A---C---B'\n\nand I was pointing out that this would involve performing the same merge\nas 'rebase --autosquash' which has conflicts\n\n> \n> Even further, while I do seem to understand (and agree with) what \n> you`re talking about with `commit --onto` and `rebase --autosquah` \n> suffering from the same conflicts in attempt to take f!A, originally \n> created on top of B, and apply it on top of A - the thing is that \n> Alexei actually pointed to B being the problematic one, failing to \n> rebase on top of already (successfully) autosquashed A' (where A' = A \n> + f!A, fixup applied through --autosquash), while it doesn`t fail \n> rebasing --onto f!A when f!A is being committed on top of A directly \n> (and not through --autosquash).\n\nI understood Alexei to mean that it was merging the f!A into A that\ncaused conflicts due to the fact that f!A has conflicting context that\nwas introduced in B. After all B' the rebased B is merge A A' B whether\nit is created by 'rebase --autosquash' or 'rebase --onto'. A' must be\nthe same in both cases or one is applying a different fix.\n\nI've found conflicts arising from moving fixups can be quite common, so\nthese days I tend to edit the commit to be fixed up directly. I have a\nscript git-amend that does something like\n\ntarget=$(git rev-parse --verify \"$1\") && GIT_SEQUENCE_EDITOR=\"sed -i\ns/^pick $target/edit $target/\" rebase -ik $target^\n\nso I can just type 'git amend <commit>' to make this easier\n\n> \n> In that (very?) specific case, proposed `git commit --onto-parent`[1] \n> doesn`t suffer from this, as once f!A is successfully applied onto A \n> (either squashed in with --amend, or on top of it), we take original \n> f!A _snapshot_ (not patch!) made on top of B, and just \"declare\" it \n> B` (being equal to B + f!A, which we already know, and being \n> correct), without a need to (try to) apply B patch on top of fixed-up \n> A to create B', as `rebase` does (and fails).\n\nAh I understand, but that only works when you're fixing up HEAD~1. If\nyou had A-B-C-f!A you have to recreate B with a merge.\n\n> \n>> I don't think there is any way for 'git rebase --autosquash' to\n>> avoid the conflicts unless it used a special fixup merge strategy\n>> that somehow took advantage of the DAG to resolve the conflicts by\n>> realizing they come from a later commit. However I don't think that\n>> could be implemented reliably as sometimes one wants those\n>> conflicting lines from the later commit to be moved to the earlier\n>> commit with the fixup.\n> \n> I think I agree on this part being tricky (if possible at all), but I \n> also think this is not what Alexei was complaining about, nor what we \n> were discussing (as I tried to explain above) - but please do correct \n> me if I misunderstood you.\n\nNo, I don't think Alexei was complaining about that directly, but if\nsuch a solution existed he (and everyone else) wouldn't have to bother\nwith the --onto approach in the case where merging the fixup creates\nconflicts.\n\nBest Wishes\n\n\nPhillip\n\n> \n> That said, and what I mentioned already, we might really benefit from \n> simple test case(s), showing \"rebase --autosquash\" failing where \n> \"rebase --onto\" works, as Alexei explained, giving some more (and \n> firm) context to the discussion.\n>\n> \n> I *think* I`ve experienced this in the past myself, but now I can`t \n> seem to wrap my head around a reproducible example just yet... :$\n> \n> Regards, Buga\n> \n> [1] https://public-inbox.org/git/4a92e34c-d713-25d3-e1ac-100525011d3f@talktalk.net/T/#m72f45ad7a8f1c733266a875bca087ee82cc781e7\n> \n\n"},{"id":"334583","messageId":"36d2b05b-8b68-a157-99ed-44050ac34ab6@gmail.com","threadId":"47325","inReplyTo":"82da4317-6b50-f60d-6d8f-50fc47579c56@talktalk.net","subject":"Re: [SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge,noworking tree file changes)","fromName":"Igor Djordjevic","fromEmail":"igor.d.djordjevic@gmail.com","sentAt":"2017-12-10T23:17:32Z","receivedAt":"2017-12-10T23:17:48Z","isPatch":false,"sender":{"key":"igor.d.djordjevic@gmail.com","avatar":null},"body":"Hi Philip,\n\nOn 10/12/2017 13:22, Phillip Wood wrote:\n> \n> Sorry I should have been clearer. The point I was somewhat obliquely \n> making was that 'rebase --onto' succeeds where 'rebase --autosquash' \n> fails not because it is smarter but because it is doing something \n> different. Specifically it avoids the conflicting merge to create A'\n> as the user has already created that commit in the temporary branch\n\nNo problem, and thanks for clarifying, I understand and agree to all \nthat with you. I was just pointing that it wasn`t something I was \ncommenting to (nor specially interested in), because of what Alexei \nactually wrote - here`s his quote (emphasis mine):\n\n  \"And then I often find that \"rebase -i --autosquash\" _fails to apply\n  the commit B_ because it expects slightly different context around\n  the changed lines.\"\n\nFrom there, it seemed pretty clear he perceived the failure not \ncoming from creating A', but applying B on top of it, and that is \nwhat got my attention. But, read below...\n\n> > - but you`re mentioning `git _commit_ --onto` instead, comparing it\n> > with `rebase`... and which one of the two (\"--autosquash\", I\n> > assume)?\n> \n> Yes because in an earlier message you said\n> \n> > If you mind enough to be bothered testing it out, might be even \n> > existing/initial state of originally proposed `git commit \n> > --onto-parent` script would work for you, as it does incorporate\n> > some trivial three-way merge resolution.\n> >\n> > In your starting situation:\n> >\n> >     ---A---B\n> >\n> > .... you would just do something like:\n> >\n> >     git commit --onto-parent A\n> >\n> > .... hopefully ending up in the desired state (hopefully =\n> > conflicts automatically resolved):\n> >\n> >     ---A---C---B'\n> \n> and I was pointing out that this would involve performing the same\n> merge as 'rebase --autosquash' which has conflicts\n\nYeah, what I assumed (and agreed to), thanks for confirmation. What \nmade me a bit uncertain was that you left that part of my earlier \nmessage quoted _after_ your inline reply to it, thus making overall \ncontext a bit difficult to be exactly sure in :P\n\n> I understood Alexei to mean that it was merging the f!A into A that \n> caused conflicts due to the fact that f!A has conflicting context\n> that was introduced in B. After all B' the rebased B is merge A A' B\n> whether it is created by 'rebase --autosquash' or 'rebase --onto'. A'\n> must be the same in both cases or one is applying a different fix.\n\nYes, I understand and agree you might be right, what you are talking \nabout being what he actually _meant_, but because that is not what he \n_wrote_, I wanted to see an example of it, (still?) hoping that he \nreally did mean what he wrote (commit B being the problematic one), \nas then there would be a possibility for improvement.\n\nAnd your analysis seems correct, and that`s what I was afraid of as \nwell - but wasn`t really sure, especially as I seem to remember \nsomething similar from my own (humble) experience, thus leaving a \npossibility for an example to prove differently.\n\nBut if that is absolutely impossible, as you claim, like not even due \nto some commit squashing, some edge case, or something - and I don`t \nfeel like I have enough knowledge/experience to judge that myself at \nthe moment - then you have to be right, and what he wrote is really \nnot what he meant... nor what I thought I remembered from my own past \nexperience, either :/ Nor there is any chance for improvement here, \nunfortunately, I guess.\n\nStill, I hope for that example...! :D\n\n> I've found conflicts arising from moving fixups can be quite common,\n> so these days I tend to edit the commit to be fixed up directly. I\n> have a script git-amend that does something like\n> \n> target=$(git rev-parse --verify \"$1\") && GIT_SEQUENCE_EDITOR=\"sed -i\n> s/^pick $target/edit $target/\" rebase -ik $target^\n> \n> so I can just type 'git amend <commit>' to make this easier\n\nThis is useful, thanks. I have something like `git commit --amend \n<commit>` on my wish list for quite some time :) Still not getting to \nlook into it, though.\n\n> > In that (very?) specific case, proposed `git commit\n> > --onto-parent`[1] doesn`t suffer from this, as once f!A is\n> > successfully applied onto A (either squashed in with --amend, or on\n> > top of it), we take original f!A _snapshot_ (not patch!) made on\n> > top of B, and just \"declare\" it B` (being equal to B + f!A, which\n> > we already know, and being correct), without a need to (try to)\n> > apply B patch on top of fixed-up A to create B', as `rebase` does\n> > (and fails).\n> \n> Ah I understand, but that only works when you're fixing up HEAD~1.\n> If you had A-B-C-f!A you have to recreate B with a merge.\n\nYes, and thus the notion of what he mentioned as being a \"(very?) \nspecific case\" ;) That initial/draft version of \"git commit \n--onto-parent\" script I sent to the list[1] operates on the first \nparent commit only, indeed, though its main point/purpose had nothing \nto do with smarter merges, but just not touching the working tree \nwhile at it, if possible.\n\n> > > I don't think there is any way for 'git rebase --autosquash' to \n> > > avoid the conflicts unless it used a special fixup merge\n> > > strategy that somehow took advantage of the DAG to resolve the\n> > > conflicts by realizing they come from a later commit. However I\n> > > don't think that could be implemented reliably as sometimes one\n> > > wants those conflicting lines from the later commit to be moved\n> > > to the earlier commit with the fixup.\n> >\n> > I think I agree on this part being tricky (if possible at all), but\n> > I also think this is not what Alexei was complaining about, nor\n> > what we were discussing (as I tried to explain above) - but please\n> > do correct me if I misunderstood you.\n> \n> No, I don't think Alexei was complaining about that directly, but if \n> such a solution existed he (and everyone else) wouldn't have to\n> bother with the --onto approach in the case where merging the fixup\n> creates conflicts.\n\nYes, I think we understand each other now (unfortunately, I guess, as \nthat also means there is nothing more to add to it, in terms of \nimproving existing situation). Thank you for your thoughts :)\n\nRegards, Buga\n\n[1] https://public-inbox.org/git/4a92e34c-d713-25d3-e1ac-100525011d3f@talktalk.net/T/#m72f45ad7a8f1c733266a875bca087ee82cc781e7\n"},{"id":"334586","messageId":"EC2DA0F0-7DA2-412B-AB9E-5F8B0CD12F57@gmail.com","threadId":"47325","inReplyTo":"82da4317-6b50-f60d-6d8f-50fc47579c56@talktalk.net","subject":"Re: [SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge,noworking tree file changes)","fromName":"Alexei Lozovsky","fromEmail":"a.lozovsky@gmail.com","sentAt":"2017-12-11T01:00:07Z","receivedAt":"2017-12-11T01:00:18Z","isPatch":false,"sender":{"key":"a.lozovsky@gmail.com","avatar":"https://gravatar.com/avatar/8bb8ff5ec366dd64bd8e08f768082934513da367ae047bcb0039292e4ed6bda5?d=mp&s=160"},"body":"On Dec 10, 2017, at 14:22, Phillip Wood wrote:\n> \n> I've found conflicts arising from moving fixups can be quite common, so\n> these days I tend to edit the commit to be fixed up directly. I have a\n> script git-amend that does something like\n> \n> target=$(git rev-parse --verify \"$1\") && GIT_SEQUENCE_EDITOR=\"sed -i\n> s/^pick $target/edit $target/\" rebase -ik $target^\n> \n> so I can just type 'git amend <commit>' to make this easier\n\nHm... I just realized that using \"edit\" command during interactive rebase\nshould probably be the same as the strategy with a temporary branch and\nrebase --onto I described earlier. I should fix my habits, I guess.\n"},{"id":"334587","messageId":"1BA66B37-0BD2-417F-AAD5-3F3FA83A3A5E@gmail.com","threadId":"47325","inReplyTo":"36d2b05b-8b68-a157-99ed-44050ac34ab6@gmail.com","subject":"Re: [SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge,noworking tree file changes)","fromName":"Alexei Lozovsky","fromEmail":"a.lozovsky@gmail.com","sentAt":"2017-12-11T01:13:19Z","receivedAt":"2017-12-11T01:13:27Z","isPatch":false,"sender":{"key":"a.lozovsky@gmail.com","avatar":"https://gravatar.com/avatar/8bb8ff5ec366dd64bd8e08f768082934513da367ae047bcb0039292e4ed6bda5?d=mp&s=160"},"body":"On Dec 11, 2017, at 01:17, Igor Djordjevic wrote:\n> On 10/12/2017 13:22, Phillip Wood wrote:\n>> I understood Alexei to mean that it was merging the f!A into A that \n>> caused conflicts due to the fact that f!A has conflicting context\n>> that was introduced in B. After all B' the rebased B is merge A A' B\n>> whether it is created by 'rebase --autosquash' or 'rebase --onto'. A'\n>> must be the same in both cases or one is applying a different fix.\n> \n> Yes, I understand and agree you might be right, what you are talking \n> about being what he actually _meant_, but because that is not what he \n> _wrote_, I wanted to see an example of it, (still?) hoping that he \n> really did mean what he wrote (commit B being the problematic one), \n> as then there would be a possibility for improvement.\n\nI'm not really good at remembering the exact details, so if you ask\nfor a testimony then I'm not sure whether it's the conflicts in the\nfixups or the later commits that I was annoyed by :) I'm also not\nreally versed in the technical details of rebasing, so I cannot give\nan educated guess on which one is more likely to cause conflicts.\n\n> Still, I hope for that example...! :D\n\nI keep this thread pinned, so I hope to provide a more concrete example\nas soon as I encounter the conflicting situation again in the wild. I'm\nnot sure that I am able to construct a relevant example artificially.\n"}]}