{"thread":{"id":"15091","subject":"Re: How can a custom merge tool get access to file shell variables?","startedAt":"2008-08-19T18:13:24Z","lastAt":"2008-08-21T23:31:26Z","messageCount":4,"participants":["Keith Amidon","Charles Bailey"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"87738","messageId":"1219169604.12921.17.camel@kea-nicira-lt.nicira.com","threadId":"15091","inReplyTo":null,"subject":"How can a custom merge tool get access to file shell variables?","fromName":"Keith Amidon","fromEmail":"keith@nicira.com","sentAt":"2008-08-19T18:13:24Z","receivedAt":"2008-08-19T18:13:24Z","isPatch":false,"sender":{"key":"keith@nicira.com","avatar":null},"body":"I'm in a situation where I have to fairly regularly merge two different\ntrees where multiple files are being simultaneously edited in both trees\nand whitespace in some of the files is different in both trees as well.\nI would like to be able to use git mergetool to walk me through\nresolving conflicts in merges.  My preferred graphical merge tool is\nxxdiff but in 3-way mode it doesn't allow you to ignore whitespace while\ndoing the diff, only while displaying.  As a result, all these files\nappear to have one giant diff and separating out what has really changed\nis a giant pain.  Since my use-case is simple, I thought I would create\na custom mergetool as a shell script that invokes xxdiff in 2-way mode\nand moves around files to get what I need.  I've pasted that script at\nthe end of the message.\n\nI've added this script as custom mergetool as indicated in comments at\nthe top of the script, but it does not work.  It appears to me that the\nproblem is that the shell variables containing the required filenames\n(BASE, LOCAL, REMOTE, MERGED) are not set.  Looking\nat /usr/bin/git-mergetool, it appears that custom merge tools are called\nwithout arguments in a subshell and those variables are not exported\nanywhere so they are not in the environment of the subshell.  Given\nthis, how can a custom mergetool ever get access to this information to\ndo its work?\n\nIf there is another better way to try to accomplish this (other than\nforcing whitespace to be the same in both trees for now!) I'd appreciate\nit if someone would clue me in!\n\n             --- Keith\n\n\n#!/bin/bash\n#\n# Perform a merge for GIT in which whitespace is ignored.  Because the\n# underlying diff tools don't seem to be able to ignore whitespace on\n# a three-way merge, a 2-way merge is done where the first file is a\n# copy of the remote file to be merged and the second file is copy of\n# the current file from the branch into which files are being merged.\n# The success of the merge is determined by whether a merged file is\n# written out from xxdiff.  If you don't save the merged file, the\n# merge will be aborted.\n#\n# This script should be configured as a merge tool in git as follows:\n#\n# git-config --global --add mergetool.xxdiff-2way-ignorews.cmd\nxxdiff-2way-ignorews-git-mergetool\n# git-config --global --add mergetool.xxdiff-2way-ignorews.trustExitCode\ntrue\n# \n\nrtmpfile=\"\"\nltmpfile=\"\"\n\nfunction remove_tmpfiles () {\n    if [ -n \"$ltmpfile\" ]; then\n        rm -f \"$ltmpfile\"\n    fi\n    if [ -n \"$rtmpfile\" ]; then\n        rm -f \"$rtmpfile\"\n        rm -f \"$rtmpfile.merge\"\n    fi\n}\n\nfunction die () {\n    printf \"%b\" \"$1\"\n    remove_tmpfiles\n    exit 1\n}\n\n[ -f \"$LOCAL\" ] || die \"Local file does not exists: $LOCAL\\n\"\n[ -f \"$REMOTE\" ] || die \"Remote file does not exists: $REMOTE\\n\"\n\n# Be really paranoid and copy git's temp files to new ones so we aren't\n# playing around with git's files...\nltmpfile=$(mktemp --tmpdir xxdiff-2way-tmp.remote.XXXXXXXXXX)\ncp \"$LOCAL\" \"$ltmpfile\" || die \"Could not copy local file to temporary\nmerge file.\\nLocal file is: $LOCAL\\nTemporary merge file is: $ltmpfile\n\\n\"\n\nrtmpfile=$(mktemp --tmpdir xxdiff-2way-tmp.local.XXXXXXXXXX)\ncp \"$REMOTE\" \"$rtmpfile\" || die \"Could not copy remote file to temporary\nmerge result file.\\nLocal file is: $LOCAL\\nTemporary merge file is:\n$tmpfile\\n\"\n\nxxdiff -w \"$rtmpfile\" \"$ltmpfile\"\n\nif [ -f \"$rtmpfile.merge\" ]; then\n    if [ -f \"$MERGED\" ]; then\n        printf \"Merged file already exists, removing so merge result can\nbe renamed.\\n\"\n        rm -f \"$MERGED\"\n    fi\n    mv \"$rtmpfile.merge\" \"$MERGED\" || die \"Could not rename merge result\nto merged file: $MERGED\\n\"\n    remove_tmpfiles\nelse\n    die \"Did not find merge output file from xxdiff, aborting merge.\\n\"\nfi\n"},{"id":"87726","messageId":"1219170751.12921.27.camel@kea-nicira-lt.nicira.com","threadId":"15091","inReplyTo":"1219170004.12921.19.camel@kea-nicira-lt.nicira.com","subject":"Re: How can a custom merge tool get access to file shell variables?","fromName":"Keith Amidon","fromEmail":"keith@nicira.com","sentAt":"2008-08-19T18:32:31Z","receivedAt":"2008-08-19T18:32:31Z","isPatch":false,"sender":{"key":"keith@nicira.com","avatar":null},"body":"In case anyone is interested, the following does exactly what I want.\nHopefully this example will help someone else that wants to do something\nsimilar avoid spending time on writing an unnecessary shell script\nwrapper tool.\n\n[mergetool \"xxdiff-2way-ignorews\"]\n    cmd = xxdiff -w $REMOTE $LOCAL --merged-filename $MERGED\n    trustExitCode = false\n\nI think I was thrown off by the description of mergetool.<name>.cmd in\nthe git-config man page.  While on close reading it is definitely\ncorrect, for me at least it seemed natural to assume that the invoked\ncommand was supposed to get its information from the environment, not\nthat the command line itself could substitute from the environment.\nWould an example such as the above in the man page might help direct\npeople toward the best way to do this?\n\n           --- Keith\n\nOn Tue, 2008-08-19 at 11:20 -0700, Keith Amidon wrote:\n> Please ignore this.  I was not effective enough in looking for an\n> answer even though I did search the archives a bit before writing\n> this.  The answer is here:\n> \n> http://article.gmane.org/gmane.comp.version-control.git/82371/match=mergetool+export\n> \n> I'm off to try a custom way of invoking xxdiff that does what I want. \n> \n> --- Keith\n> \n> \n> On Tue, 2008-08-19 at 11:13 -0700, Keith Amidon wrote: \n> > I'm in a situation where I have to fairly regularly merge two different\n> > trees where multiple files are being simultaneously edited in both trees\n> > and whitespace in some of the files is different in both trees as well.\n> > I would like to be able to use git mergetool to walk me through\n> > resolving conflicts in merges.  My preferred graphical merge tool is\n> > xxdiff but in 3-way mode it doesn't allow you to ignore whitespace while\n> > doing the diff, only while displaying.  As a result, all these files\n> > appear to have one giant diff and separating out what has really changed\n> > is a giant pain.  Since my use-case is simple, I thought I would create\n> > a custom mergetool as a shell script that invokes xxdiff in 2-way mode\n> > and moves around files to get what I need.  I've pasted that script at\n> > the end of the message.\n> > \n> > I've added this script as custom mergetool as indicated in comments at\n> > the top of the script, but it does not work.  It appears to me that the\n> > problem is that the shell variables containing the required filenames\n> > (BASE, LOCAL, REMOTE, MERGED) are not set.  Looking\n> > at /usr/bin/git-mergetool, it appears that custom merge tools are called\n> > without arguments in a subshell and those variables are not exported\n> > anywhere so they are not in the environment of the subshell.  Given\n> > this, how can a custom mergetool ever get access to this information to\n> > do its work?\n> > \n> > If there is another better way to try to accomplish this (other than\n> > forcing whitespace to be the same in both trees for now!) I'd appreciate\n> > it if someone would clue me in!\n> > \n> >              --- Keith\n> > \n> > \n> > #!/bin/bash\n> > #\n> > # Perform a merge for GIT in which whitespace is ignored.  Because the\n> > # underlying diff tools don't seem to be able to ignore whitespace on\n> > # a three-way merge, a 2-way merge is done where the first file is a\n> > # copy of the remote file to be merged and the second file is copy of\n> > # the current file from the branch into which files are being merged.\n> > # The success of the merge is determined by whether a merged file is\n> > # written out from xxdiff.  If you don't save the merged file, the\n> > # merge will be aborted.\n> > #\n> > # This script should be configured as a merge tool in git as follows:\n> > #\n> > # git-config --global --add mergetool.xxdiff-2way-ignorews.cmd\n> > xxdiff-2way-ignorews-git-mergetool\n> > # git-config --global --add mergetool.xxdiff-2way-ignorews.trustExitCode\n> > true\n> > # \n> > \n> > rtmpfile=\"\"\n> > ltmpfile=\"\"\n> > \n> > function remove_tmpfiles () {\n> >     if [ -n \"$ltmpfile\" ]; then\n> >         rm -f \"$ltmpfile\"\n> >     fi\n> >     if [ -n \"$rtmpfile\" ]; then\n> >         rm -f \"$rtmpfile\"\n> >         rm -f \"$rtmpfile.merge\"\n> >     fi\n> > }\n> > \n> > function die () {\n> >     printf \"%b\" \"$1\"\n> >     remove_tmpfiles\n> >     exit 1\n> > }\n> > \n> > [ -f \"$LOCAL\" ] || die \"Local file does not exists: $LOCAL\\n\"\n> > [ -f \"$REMOTE\" ] || die \"Remote file does not exists: $REMOTE\\n\"\n> > \n> > # Be really paranoid and copy git's temp files to new ones so we aren't\n> > # playing around with git's files...\n> > ltmpfile=$(mktemp --tmpdir xxdiff-2way-tmp.remote.XXXXXXXXXX)\n> > cp \"$LOCAL\" \"$ltmpfile\" || die \"Could not copy local file to temporary\n> > merge file.\\nLocal file is: $LOCAL\\nTemporary merge file is: $ltmpfile\n> > \\n\"\n> > \n> > rtmpfile=$(mktemp --tmpdir xxdiff-2way-tmp.local.XXXXXXXXXX)\n> > cp \"$REMOTE\" \"$rtmpfile\" || die \"Could not copy remote file to temporary\n> > merge result file.\\nLocal file is: $LOCAL\\nTemporary merge file is:\n> > $tmpfile\\n\"\n> > \n> > xxdiff -w \"$rtmpfile\" \"$ltmpfile\"\n> > \n> > if [ -f \"$rtmpfile.merge\" ]; then\n> >     if [ -f \"$MERGED\" ]; then\n> >         printf \"Merged file already exists, removing so merge result can\n> > be renamed.\\n\"\n> >         rm -f \"$MERGED\"\n> >     fi\n> >     mv \"$rtmpfile.merge\" \"$MERGED\" || die \"Could not rename merge result\n> > to merged file: $MERGED\\n\"\n> >     remove_tmpfiles\n> > else\n> >     die \"Did not find merge output file from xxdiff, aborting merge.\\n\"\n> > fi\n> > \n"},{"id":"88053","messageId":"20080821221524.GA25429@hashpling.org","threadId":"15091","inReplyTo":"1219170751.12921.27.camel@kea-nicira-lt.nicira.com","subject":"Re: How can a custom merge tool get access to file shell variables?","fromName":"Charles Bailey","fromEmail":"charles@hashpling.org","sentAt":"2008-08-21T22:15:24Z","receivedAt":"2008-08-21T22:15:24Z","isPatch":false,"sender":{"key":"charles@hashpling.org","avatar":"https://avatars.githubusercontent.com/u/1668475?v=4"},"body":"On Tue, Aug 19, 2008 at 11:32:31AM -0700, Keith Amidon wrote:\n> In case anyone is interested, the following does exactly what I want.\n> Hopefully this example will help someone else that wants to do something\n> similar avoid spending time on writing an unnecessary shell script\n> wrapper tool.\n> \n> [mergetool \"xxdiff-2way-ignorews\"]\n>     cmd = xxdiff -w $REMOTE $LOCAL --merged-filename $MERGED\n>     trustExitCode = false\n> \n> I think I was thrown off by the description of mergetool.<name>.cmd in\n> the git-config man page.  While on close reading it is definitely\n> correct, for me at least it seemed natural to assume that the invoked\n> command was supposed to get its information from the environment, not\n> that the command line itself could substitute from the environment.\n> Would an example such as the above in the man page might help direct\n> people toward the best way to do this?\n> \n>            --- Keith\n\nDid you also try the git mergetool man page (not that it's much\nbetter!)?\n\nThe point of the custom mergetool patch was to make using a new,\npreviously unknown merge tool a 'simple' configuration exercise rather\nthan a patch or scripting exercise. At the time, an 'eval' approach\nwas the compromise between ease of implementation and preventing\nenvironmental pollution.\n\nIt's not the most beautiful of solutions, especially since escaping\nquotes and spaces in either .gitconfig or in a git config command line\nof something that is later going to be expanded by the shell is\nsomething of a mind bender.\n\nAn example in the documentation would be a really good idea - you are\nnot the first person to have asked about how to use the custom merge\ntool feature. I'm feeling a little guilty about not adding my name to\nthe man page when I submitted the patch. It's not Ted's fault that the\ncustom merge tool section is badly explained; it's mine.\n\n-- \nCharles Bailey\nhttp://ccgi.hashpling.plus.com/blog/\n"},{"id":"88057","messageId":"1219361486.16860.50.camel@kea-nicira-lt.nicira.com","threadId":"15091","inReplyTo":"20080821221524.GA25429@hashpling.org","subject":"Re: How can a custom merge tool get access to file shell variables?","fromName":"Keith Amidon","fromEmail":"keith@nicira.com","sentAt":"2008-08-21T23:31:26Z","receivedAt":"2008-08-21T23:31:26Z","isPatch":false,"sender":{"key":"keith@nicira.com","avatar":null},"body":"﻿Hi Charles,\n\nThanks for your reply to my issue.  To answer your question, I did read\nthe git mergetool man page too.  For some reason I felt that the\ninformation in the git config page was more complete/specific for this\ncase.  Anyway, now I understand how it works (and quoting issues I\nhaven't had to worry about because I don't have weird filenames aside),\nthe setup actually works very nicely.  If you would be interested and\ncould provide some guidance on roughly what you would like to see I'd be\nhappy to take a crack at an initial modification to the git mergetool\nman page to include an example similar to mine below with proper quoting\nso paths with spaces in them work, etc.\n\nThanks again for your response and for the excellent work you and\neveryone involved in git development have been doing.\n\n             --- Keith\n\nP.S. -- Sorry if you get two copies Charles.  My previous version of\nthis message was rejected by the mailing list because I forgot to send\nw/o HTML.\n\nOn Thu, 2008-08-21 at 23:15 +0100, Charles Bailey wrote:\n> Did you also try the git mergetool man page (not that it's much\n> better!)?\n> \n> The point of the custom mergetool patch was to make using a new,\n> previously unknown merge tool a 'simple' configuration exercise rather\n> than a patch or scripting exercise. At the time, an 'eval' approach\n> was the compromise between ease of implementation and preventing\n> environmental pollution.\n> \n> It's not the most beautiful of solutions, especially since escaping\n> quotes and spaces in either .gitconfig or in a git config command line\n> of something that is later going to be expanded by the shell is\n> something of a mind bender.\n> \n> An example in the documentation would be a really good idea - you are\n> not the first person to have asked about how to use the custom merge\n> tool feature. I'm feeling a little guilty about not adding my name to\n> the man page when I submitted the patch. It's not Ted's fault that the\n> custom merge tool section is badly explained; it's mine.\n> \n"}]}