2012-01-26 35 views
17

是否可以配置Git使用我配置的difftool與git add --patchgit add - 與difftool的補丁

我想通過我自己的difftool選擇要添加到索引中的更改。

+0

是混帳甚至覆蓋在計算器上?我認爲這對於[SuperUser](http://superuser.com/)會是一個更好的問題。 – qJake

+0

你可能是對的。是否有一個遷移按鈕來移動它? – HaxElit

+0

不,要讓MOD做到這一點,或只是再問一次,如果你不想等待。 – qJake

回答

9

不,不幸的是。

我想我可以看到工作 - Git根據索引中的內容生成一個臨時文件,並將它與當前工作樹版本的副本一起傳遞給difftool(以保護您不做進一步更改),您可以使用difftool將某些更改移至索引版本,然後一旦保存並退出,即可對該修改後的索引版本中的任何內容進行分段。請注意,這將需要difftool也是一個編輯器,並不是所有有效的difftools;其中一些僅用於查看差異。還要注意,這基本上繞過全部git add -p。你不會有任何正常的界面來在不同的區域間移動,分割區域等等。 difftool將完全負責所有這些。

如果你的difftool功能齊全,可以做這種事情,那麼我想你可以寫一個腳本來完成它。大綱,並沒有真正的錯誤保護,處理的特殊情況下,並沒有經過充分測試:

#!/bin/bash 
tmpdir=$(mktemp -d) 
git diff --name-only | 
while read file; do 
    cp "$file" $tmpdir 
    # this has your changes in it 
    work_tree_version="$tmpdir/$file" 
    # this has the pristine version 
    index_version=$(git checkout-index --temp "$file") 
    # and now you bring changes from the work tree version into the index version, 
    # within the difftool, and save the index version and quit when done 
    my_difftool "$work_tree_version" "$index_version" 

    # swap files around to run git add 
    mv "$file" "$work_tree_version" 
    mv "$index_version" "$file" 
    git add "$file" 
    mv "$work_tree_version" "$file" 
    # you could also do this by calculating the diff and applying it directly to the index 
    # git diff --no-index -- "$file" "$original_index_version" | git apply --cached 

rm -r $tmpdir 

可能很多的方法來改善這種(二進制文件?)對不起,我現在沒有時間小心和徹底。

+0

我想也許我問了這個錯誤。我可以使用我的difftool(他們對我來說是一樣的)嗎?這樣我可以選擇所有我想添加到索引中的更改。我會更新這個問題。 – HaxElit

+0

這是一個非常酷的想法。我可以製作一個腳本,然後添加一個git別名'git diffadd'或其他東西。我會盡量清理你的代碼,並使其更健壯。謝謝! – HaxElit

+0

@HaxElit:如果你想出了一些可靠的東西,請隨時將其編輯到我的答案中,或張貼你自己的! – Cascabel

-1

不幸的不是。

我目前知道的唯一的UI是混帳GUI的一部分,當作爲

git gui citool 

其他UI調用是交互式控制檯界面時援引爲

git add -i 

混帳difftool允許一些不同的工具選項,但不是添加界面。

+0

我不確定我看到了這一點的相關性;操作系統正在詢問'git add --patch | -p',它可以讓你有選擇地選擇該補丁的塊。 'git gui citool'絕對不會那樣做,所以它是無關緊要的。 'git add -i'能夠調用'git add -p',所以這是一種迂迴的方式來完成OP已經知道的事情,否則就不會做他想做的事情。所以你的答案的實質是「不」,我覺得我很好地覆蓋。 – Cascabel

1

這裏是my script爲此,它打開kdiff3爲您執行2文件合併。如果你不喜歡kdiff3,請提供你自己的值MERGETOOLMERGECMD(但你會瘋了,不喜歡kdiff3)。

爲避免意外,此腳本嘗試模仿參數和錯誤代碼git add -p。 (它處理文件和目錄的列表。)

此外,它正確地處理各種極端情況,包括:

  • 用戶試圖運行在非git的目錄中的腳本(使用錯誤中止)
  • 用戶完成之前打Ctrl+C (早退出)
  • 用戶拒絕保存difftool內合併結果(那麼就不要使用它,而是繼續前進到下一個文件)
  • 的difftool有意想不到的錯誤(提前停止)

用法示例:

$ ## With kdiff3 (default): 
$ add-with-mergetool myfile1.txt 
$ add-with-mergetool some-directory 

$ ## ...or with custom mergetool: 
$ export MERGETOOL='opendiff' 
$ export MERGECMD='$LOCAL $REMOTE -merge $MERGED' 
$ add-with-mergetool some-directory/*.py 
#!/bin/bash 
# 
# add-with-mergetool 
# Author: Stuart Berg (http://github.com/stuarteberg) 
# 
# This little script is like 'git add --patch', except that 
# it launches a merge-tool to perform the merge. 

# TODO: For now, this script hard-codes MERGETOOL and MERGECMD for kdiff3. 
#  Modify those variables for your own tool if you wish. 
#  In the future, it would be nice if we could somehow read 
#  MERGETOOL and MERGECMD from the user's git-config. 

# Configure for kdiff3 
# (and hide warnings on about modalSession, from kdiff3 on OSX) 
MERGETOOL=${MERGETOOL-kdiff3} 
MERGECMD=${MERGECMD-'"${MERGETOOL}" "${LOCAL}" "${REMOTE}" -o "${MERGED}"'\ 
        2>&1 | grep -iv modalSession} 

main() { 
    check_for_errors "[email protected]" 
    process_all "[email protected]" 
} 

check_for_errors() { 
    which "${MERGETOOL}" > /dev/null 
    if [[ $? == 1 ]]; then 
     echo "Error: Can't find mergetool: '${MERGETOOL}'" 1>&2 
     exit 1 
    fi 

    if [[ "$1" == "-h" ]]; then 
     echo "Usage: $(basename $0) [<pathspec>...]" 1>&2 
     exit 0 
    fi 

    # Exit early if we're not in a git repo 
    git status > /dev/null || exit $? 
} 

process_all() { 
    repo_toplevel=$(git rev-parse --show-toplevel) 

    # If no args given, add everything (like 'git add -p') 
    if [[ $# == 0 ]]; then 
     set -- "$repo_toplevel" 
    fi 

    # For each given file/directory... 
    args=("[email protected]") 
    for arg in "${args[@]}" 
    do 
     # Find the modified file(s) 
     changed_files=($(git diff --name-only -- "$arg")) 
     (
      # Switch to toplevel, to easily handle 'git diff' output 
      cd "$repo_toplevel" 

      # For each modified file... 
      for f in "${changed_files[@]}" 
      do 
       if [[ $startmsg_shown != "yes" ]]; then 
        echo "Starting $(basename $0). Use Ctrl+C to stop early." 
        echo "To skip a file, quit ${MERGETOOL} without saving." 
        echo 
        startmsg_shown="yes" 
       fi 

       # This is where the magic happens.    
       patch_file_and_add "$f" 
      done 
     ) || exit $? # exit early if loop body failed 
    done 
} 

# This helper function launches the mergetool for a single file, 
# and then adds it to the git index (if the user saved the new file). 
patch_file_and_add() { 
    f="$1" 
    git show :"$f" > "$f.from_index" # Copy from the index 
    (
     set -e 
     trap "echo && exit 130" INT # Ctrl+C should trigger abnormal exit 

     # Execute 2-file merge 
     echo "Launching ${MERGETOOL} for '$f'." 
     LOCAL="$f.from_index" 
     REMOTE="$f" 
     MERGED="$f.to_add" 
     eval "${MERGECMD}" 

     if [[ -e "$f.to_add" ]]; then 
      mv "$f" "$f.from_working" # Backup original from working-tree 
      mv "$f.to_add" "$f"  # Replace with patched version 
      git add "$f"    # Add to the index 
      mv "$f.from_working" "$f" # Restore the working-tree version 
     fi 
    ) 
    status=$? 
    rm "$f.from_index" # Discard the old index version 
    if [ $status == 130 ]; then 
     echo "User interrupted." 1>&2 
     exit $status 
    elif [ $status != 0 ]; then 
     echo "Error: Interactive add-patch stopped early!" 1>&2 
     exit $status 
    fi 
} 

main "[email protected]"