Merge branch 'da/mergetool-temporary-directory'
authorJunio C Hamano <gitster@pobox.com>
Tue, 21 Oct 2014 20:28:42 +0000 (13:28 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 21 Oct 2014 20:28:42 +0000 (13:28 -0700)
Allow a temporary directory specified to be used while running "git
mergetool" backend.

* da/mergetool-temporary-directory:
t7610-mergetool: add test cases for mergetool.writeToTemp
mergetool: add an option for writing to a temporary directory

1  2 
Documentation/config.txt
git-mergetool.sh
t/t7610-mergetool.sh
diff --combined Documentation/config.txt
index 04a1e2f37e938f004c899d3fb6c59d282c2071ce,c305aff660ff3e51a969dd90bb030d0fc281f8f4..be6cf35c02fbf9f12f221f7bd6e687691e430daf
@@@ -381,7 -381,7 +381,7 @@@ false), while all other repositories ar
  core.worktree::
        Set the path to the root of the working tree.
        This can be overridden by the GIT_WORK_TREE environment
 -      variable and the '--work-tree' command line option.
 +      variable and the '--work-tree' command-line option.
        The value can be an absolute path or relative to the path to
        the .git directory, which is either specified by --git-dir
        or GIT_DIR, or automatically discovered.
@@@ -489,7 -489,7 +489,7 @@@ core.deltaBaseCacheLimit:
        to avoid unpacking and decompressing frequently used base
        objects multiple times.
  +
 -Default is 16 MiB on all platforms.  This should be reasonable
 +Default is 96 MiB on all platforms.  This should be reasonable
  for all users/operating systems, except on the largest projects.
  You probably do not need to adjust this value.
  +
@@@ -499,8 -499,7 +499,8 @@@ core.bigFileThreshold:
        Files larger than this size are stored deflated, without
        attempting delta compression.  Storing large files without
        delta compression avoids excessive memory usage, at the
 -      slight expense of increased disk usage.
 +      slight expense of increased disk usage. Additionally files
 +      larger than this size are always treated as binary.
  +
  Default is 512 MiB on all platforms.  This should be reasonable
  for most projects as source code and other text files can still
@@@ -524,7 -523,7 +524,7 @@@ core.askpass:
        environment variable. If not set, fall back to the value of the
        'SSH_ASKPASS' environment variable or, failing that, a simple password
        prompt. The external program shall be given a suitable prompt as
 -      command line argument and write the password on its STDOUT.
 +      command-line argument and write the password on its STDOUT.
  
  core.attributesfile::
        In addition to '.gitattributes' (per-directory) and
@@@ -545,9 -544,6 +545,9 @@@ core.commentchar:
        messages consider a line that begins with this character
        commented, and removes them after the editor returns
        (default '#').
 ++
 +If set to "auto", `git-commit` would select a character that is not
 +the beginning character of any line in existing commit messages.
  
  sequence.editor::
        Text editor used by `git rebase -i` for editing the rebase instruction file.
@@@ -562,19 -558,14 +562,19 @@@ core.pager:
        configuration, then `$PAGER`, and then the default chosen at
        compile time (usually 'less').
  +
 -When the `LESS` environment variable is unset, Git sets it to `FRSX`
 +When the `LESS` environment variable is unset, Git sets it to `FRX`
  (if `LESS` environment variable is set, Git does not change it at
  all).  If you want to selectively override Git's default setting
 -for `LESS`, you can set `core.pager` to e.g. `less -+S`.  This will
 +for `LESS`, you can set `core.pager` to e.g. `less -S`.  This will
  be passed to the shell by Git, which will translate the final
 -command to `LESS=FRSX less -+S`. The environment tells the command
 -to set the `S` option to chop long lines but the command line
 -resets it to the default to fold long lines.
 +command to `LESS=FRX less -S`. The environment does not set the
 +`S` option but the command line does, instructing less to truncate
 +long lines. Similarly, setting `core.pager` to `less -+F` will
 +deactivate the `F` option specified by the environment from the
 +command-line, deactivating the "quit if one screen" behavior of
 +`less`.  One can specifically activate some flags for particular
 +commands: for example, setting `pager.blame` to `less -S` enables
 +line truncation only for `git blame`.
  +
  Likewise, when the `LV` environment variable is unset, Git sets it
  to `-c`.  You can override this setting by exporting `LV` with
@@@ -622,9 -613,9 +622,9 @@@ core.preloadindex:
  +
  This can speed up operations like 'git diff' and 'git status' especially
  on filesystems like NFS that have weak caching semantics and thus
 -relatively high IO latencies.  With this set to 'true', Git will do the
 +relatively high IO latencies.  When enabled, Git will do the
  index comparison to the filesystem data in parallel, allowing
 -overlapping IO's.
 +overlapping IO's.  Defaults to true.
  
  core.createObject::
        You can set this to 'link', in which case a hardlink followed by
@@@ -1123,10 -1114,6 +1123,10 @@@ format.signature:
        Set this variable to the empty string ("") to suppress
        signature generation.
  
 +format.signaturefile::
 +      Works just like format.signature except the contents of the
 +      file specified by this variable will be used as the signature.
 +
  format.suffix::
        The default for format-patch is to output files with the suffix
        `.patch`. Use this variable to change that suffix (make sure to
@@@ -1337,7 -1324,7 +1337,7 @@@ grep.extendedRegexp:
  gpg.program::
        Use this custom program instead of "gpg" found on $PATH when
        making or verifying a PGP signature. The program must support the
 -      same command line interface as GPG, namely, to verify a detached
 +      same command-line interface as GPG, namely, to verify a detached
        signature, "gpg --verify $file - <$signature" is run, and the
        program is expected to signal a good signature by exiting with
        code 0, and to generate an ascii-armored detached signature, the
@@@ -1768,6 -1755,12 +1768,12 @@@ mergetool.keepTemporaries:
        preserved, otherwise they will be removed after the tool has
        exited. Defaults to `false`.
  
+ mergetool.writeToTemp::
+       Git writes temporary 'BASE', 'LOCAL', and 'REMOTE' versions of
+       conflicting files in the worktree by default.  Git will attempt
+       to use a temporary directory for these files when set `true`.
+       Defaults to `false`.
  mergetool.prompt::
        Prompt before each invocation of the merge resolution program.
  
@@@ -1906,7 -1899,12 +1912,7 @@@ pack.useBitmaps:
        you are debugging pack bitmaps.
  
  pack.writebitmaps::
 -      When true, git will write a bitmap index when packing all
 -      objects to disk (e.g., when `git repack -a` is run).  This
 -      index can speed up the "counting objects" phase of subsequent
 -      packs created for clones and fetches, at the cost of some disk
 -      space and extra time spent on the initial repack.  Defaults to
 -      false.
 +      This is a deprecated synonym for `repack.writeBitmaps`.
  
  pack.writeBitmapHashCache::
        When true, git will include a "hash cache" section in the bitmap
@@@ -2044,25 -2042,6 +2050,25 @@@ receive.autogc:
        receiving data from git-push and updating refs.  You can stop
        it by setting this variable to false.
  
 +receive.certnonceseed::
 +      By setting this variable to a string, `git receive-pack`
 +      will accept a `git push --signed` and verifies it by using
 +      a "nonce" protected by HMAC using this string as a secret
 +      key.
 +
 +receive.certnonceslop::
 +      When a `git push --signed` sent a push certificate with a
 +      "nonce" that was issued by a receive-pack serving the same
 +      repository within this many seconds, export the "nonce"
 +      found in the certificate to `GIT_PUSH_CERT_NONCE` to the
 +      hooks (instead of what the receive-pack asked the sending
 +      side to include).  This may allow writing checks in
 +      `pre-receive` and `post-receive` a bit easier.  Instead of
 +      checking `GIT_PUSH_CERT_NONCE_SLOP` environment variable
 +      that records by how many seconds the nonce is stale to
 +      decide if they want to accept the certificate, they only
 +      can check `GIT_PUSH_CERT_NONCE_STATUS` is `OK`.
 +
  receive.fsckObjects::
        If it is set to true, git-receive-pack will check all received
        objects. It will abort in the case of a malformed object or a
@@@ -2202,15 -2181,7 +2208,15 @@@ repack.packKeptObjects:
        `--pack-kept-objects` was passed. See linkgit:git-repack[1] for
        details. Defaults to `false` normally, but `true` if a bitmap
        index is being written (either via `--write-bitmap-index` or
 -      `pack.writeBitmaps`).
 +      `repack.writeBitmaps`).
 +
 +repack.writeBitmaps::
 +      When true, git will write a bitmap index when packing all
 +      objects to disk (e.g., when `git repack -a` is run).  This
 +      index can speed up the "counting objects" phase of subsequent
 +      packs created for clones and fetches, at the cost of some disk
 +      space and extra time spent on the initial repack.  Defaults to
 +      false.
  
  rerere.autoupdate::
        When set to true, `git-rerere` updates the index with the
@@@ -2332,7 -2303,7 +2338,7 @@@ status.submodulesummary:
        exception to that rule is that status and commit will show staged
        submodule changes. To
        also view the summary for ignored submodules you can either use
 -      the --ignore-submodules=dirty command line option or the 'git
 +      the --ignore-submodules=dirty command-line option or the 'git
        submodule summary' command, which shows a similar output but does
        not honor these settings.
  
@@@ -2354,7 -2325,7 +2360,7 @@@ submodule.<name>.branch:
  submodule.<name>.fetchRecurseSubmodules::
        This option can be used to control recursive fetching of this
        submodule. It can be overridden by using the --[no-]recurse-submodules
 -      command line option to "git fetch" and "git pull".
 +      command-line option to "git fetch" and "git pull".
        This setting will override that from in the linkgit:gitmodules[5]
        file.
  
@@@ -2374,11 -2345,6 +2380,11 @@@ submodule.<name>.ignore:
        "--ignore-submodules" option. The 'git submodule' commands are not
        affected by this setting.
  
 +tag.sort::
 +      This variable controls the sort ordering of tags when displayed by
 +      linkgit:git-tag[1]. Without the "--sort=<value>" option provided, the
 +      value of this variable will be used as the default.
 +
  tar.umask::
        This variable can be used to restrict the permission bits of
        tar archive entries.  The default is 0002, which turns off the
diff --combined git-mergetool.sh
index ec644d542d9754fa09ac7fb1b0f1f932a9bedbed,83863f2288a6a5252e55a6a8d1ffe7876e27bfab..ff050e58ff3b752113105ba3ea5e20c6b633aae9
  
  USAGE='[--tool=tool] [--tool-help] [-y|--no-prompt|--prompt] [file to merge] ...'
  SUBDIRECTORY_OK=Yes
 +NONGIT_OK=Yes
  OPTIONS_SPEC=
  TOOL_MODE=merge
  . git-sh-setup
  . git-mergetool--lib
 -require_work_tree
  
  # Returns true if the mode reflects a symlink
  is_symlink () {
@@@ -37,6 -37,19 +37,19 @@@ base_present () 
        test -n "$base_mode"
  }
  
+ mergetool_tmpdir_init () {
+       if test "$(git config --bool mergetool.writeToTemp)" != true
+       then
+               MERGETOOL_TMPDIR=.
+               return 0
+       fi
+       if MERGETOOL_TMPDIR=$(mktemp -d -t "git-mergetool-XXXXXX" 2>/dev/null)
+       then
+               return 0
+       fi
+       die "error: mktemp is needed when 'mergetool.writeToTemp' is true"
+ }
  cleanup_temp_files () {
        if test "$1" = --save-backup
        then
        else
                rm -f -- "$LOCAL" "$REMOTE" "$BASE" "$BACKUP"
        fi
+       if test "$MERGETOOL_TMPDIR" != "."
+       then
+               rmdir "$MERGETOOL_TMPDIR"
+       fi
  }
  
  describe_file () {
@@@ -205,7 -222,7 +222,7 @@@ checkout_staged_file () 
                "$(git checkout-index --temp --stage="$1" "$2" 2>/dev/null)" \
                : '\([^ ]*\)    ')
  
 -      if test $? -eq 0 -a -n "$tmpfile"
 +      if test $? -eq 0 && test -n "$tmpfile"
        then
                mv -- "$(git rev-parse --show-cdup)$tmpfile" "$3"
        else
@@@ -235,10 -252,20 +252,20 @@@ merge_file () 
                BASE=$MERGED
                ext=
        fi
-       BACKUP="./${BASE}_BACKUP_$$$ext"
-       LOCAL="./${BASE}_LOCAL_$$$ext"
-       REMOTE="./${BASE}_REMOTE_$$$ext"
-       BASE="./${BASE}_BASE_$$$ext"
+       mergetool_tmpdir_init
+       if test "$MERGETOOL_TMPDIR" != "."
+       then
+               # If we're using a temporary directory then write to the
+               # top-level of that directory.
+               BASE=${BASE##*/}
+       fi
+       BACKUP="$MERGETOOL_TMPDIR/${BASE}_BACKUP_$$$ext"
+       LOCAL="$MERGETOOL_TMPDIR/${BASE}_LOCAL_$$$ext"
+       REMOTE="$MERGETOOL_TMPDIR/${BASE}_REMOTE_$$$ext"
+       BASE="$MERGETOOL_TMPDIR/${BASE}_BASE_$$$ext"
  
        base_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}')
        local_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}')
        checkout_staged_file 2 "$MERGED" "$LOCAL"
        checkout_staged_file 3 "$MERGED" "$REMOTE"
  
 -      if test -z "$local_mode" -o -z "$remote_mode"
 +      if test -z "$local_mode" || test -z "$remote_mode"
        then
                echo "Deleted merge conflict for '$MERGED':"
                describe_file "$local_mode" "local" "$LOCAL"
        echo "Normal merge conflict for '$MERGED':"
        describe_file "$local_mode" "local" "$LOCAL"
        describe_file "$remote_mode" "remote" "$REMOTE"
 -      if "$prompt" = true
 +      if test "$guessed_merge_tool" = true || test "$prompt" = true
        then
                printf "Hit return to start merge resolution tool (%s): " "$merge_tool"
                read ans || return 1
        return 0
  }
  
 -prompt=$(git config --bool mergetool.prompt || echo true)
 +prompt=$(git config --bool mergetool.prompt)
 +guessed_merge_tool=false
  
  while test $# != 0
  do
        case "$1" in
 +      --tool-help=*)
 +              TOOL_MODE=${1#--tool-help=}
 +              show_tool_help
 +              ;;
        --tool-help)
                show_tool_help
                ;;
@@@ -382,19 -404,9 +409,19 @@@ prompt_after_failed_merge () 
        done
  }
  
 +git_dir_init
 +require_work_tree
 +
  if test -z "$merge_tool"
  then
 -      merge_tool=$(get_merge_tool "$merge_tool") || exit
 +      # Check if a merge tool has been configured
 +      merge_tool=$(get_configured_merge_tool)
 +      # Try to guess an appropriate merge tool if no tool has been set.
 +      if test -z "$merge_tool"
 +      then
 +              merge_tool=$(guess_merge_tool) || exit
 +              guessed_merge_tool=true
 +      fi
  fi
  merge_keep_backup="$(git config --bool mergetool.keepBackup || echo true)"
  merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo false)"
diff --combined t/t7610-mergetool.sh
index 3502ec9fe567af2a029c0c0d6faa3b189be8717f,4fec633f8afcfd072fb0f62574f1c4b6ff81fbf6..7eeb207b32b48041037488f42e645b8b12742690
@@@ -14,7 -14,7 +14,7 @@@ Testing basic merge tool invocation
  # running mergetool
  
  test_expect_success 'setup' '
 -      git config rerere.enabled true &&
 +      test_config rerere.enabled true &&
        echo master >file1 &&
        echo master spaced >"spaced name" &&
        echo master file11 >file11 &&
@@@ -129,7 -129,7 +129,7 @@@ test_expect_success 'mergetool crlf' 
        git submodule update -N &&
        test "$(cat submod/bar)" = "master submodule" &&
        git commit -m "branch1 resolved with mergetool - autocrlf" &&
 -      git config core.autocrlf false &&
 +      test_config core.autocrlf false &&
        git reset --hard
  '
  
@@@ -176,7 -176,7 +176,7 @@@ test_expect_success 'mergetool skips au
  test_expect_success 'mergetool merges all from subdir' '
        (
                cd subdir &&
 -              git config rerere.enabled false &&
 +              test_config rerere.enabled false &&
                test_must_fail git merge master &&
                ( yes "r" | git mergetool ../submod ) &&
                ( yes "d" "d" | git mergetool --no-prompt ) &&
  '
  
  test_expect_success 'mergetool skips resolved paths when rerere is active' '
 -      git config rerere.enabled true &&
 +      test_config rerere.enabled true &&
        rm -rf .git/rr-cache &&
        git checkout -b test5 branch1 &&
        git submodule update -N &&
  '
  
  test_expect_success 'conflicted stash sets up rerere'  '
 -      git config rerere.enabled true &&
 +      test_config rerere.enabled true &&
        git checkout stash1 &&
        echo "Conflicting stash content" >file11 &&
        git stash &&
  
  test_expect_success 'mergetool takes partial path' '
        git reset --hard &&
 -      git config rerere.enabled false &&
 +      test_config rerere.enabled false &&
        git checkout -b test12 branch1 &&
        git submodule update -N &&
        test_must_fail git merge master &&
@@@ -514,4 -514,27 +514,27 @@@ test_expect_success 'custom commands ov
        git reset --hard master >/dev/null 2>&1
  '
  
+ test_expect_success 'filenames seen by tools start with ./' '
+       git checkout -b test15 branch1 &&
+       test_config mergetool.writeToTemp false &&
+       test_config mergetool.myecho.cmd "echo \"\$LOCAL\"" &&
+       test_config mergetool.myecho.trustExitCode true &&
+       test_must_fail git merge master &&
+       git mergetool --no-prompt --tool myecho -- both >actual &&
+       grep ^\./both_LOCAL_ actual >/dev/null &&
+       git reset --hard master >/dev/null 2>&1
+ '
+ test_expect_success 'temporary filenames are used with mergetool.writeToTemp' '
+       git checkout -b test16 branch1 &&
+       test_config mergetool.writeToTemp true &&
+       test_config mergetool.myecho.cmd "echo \"\$LOCAL\"" &&
+       test_config mergetool.myecho.trustExitCode true &&
+       test_must_fail git merge master &&
+       git mergetool --no-prompt --tool myecho -- both >actual &&
+       test_must_fail grep ^\./both_LOCAL_ actual >/dev/null &&
+       grep /both_LOCAL_ actual >/dev/null &&
+       git reset --hard master >/dev/null 2>&1
+ '
  test_done