代码之家  ›  专栏  ›  技术社区  ›  Ram Rachum

Git内部:将“Git merge one file”修改为不使用工作树

git
  •  9
  • Ram Rachum  · 技术社区  · 13 年前

    是否可以修改默认值 git-merge-one-file 程序在不接触工作树的情况下执行索引中的所有操作,使其完全未修改?

    更新和详细信息

    所以我现在明白了,如果不使用工作树,文件级合并(即合并作用于文件中的行而不是整个文件)是不可能发生的。(不同于对整个文件进行合并。)所以我必须使用工作树。

    另一个细节:我同意该解决方案仅在合并可以自动完成而无需手动解决的情况下有效。如果合并不是自动的,那么它只显示一条错误消息也没关系。(当然,还要保持一切清洁。)

    另一个细节:我没有使用 git合并一个文件 直接地,我在这个脚本中使用它: https://gist.github.com/cool-RR/6575042

    我试着听从@torek的建议,使用一个临时工作树(正如你在脚本中看到的那样),因为这似乎是迄今为止最好的方向。问题是,我得到了以下错误:

    git checkout-index: my_file is not in the cache
    error: my_file: cannot add to the index - missing --add option?
    

    我在谷歌上搜索了这些错误消息,但找不到任何有用的信息。

    知道该怎么办吗?

    5 回复  |  直到 13 年前
        1
  •  5
  •   torek    13 年前

    虽然git需要一个地方来完成它的工作,但在“合并一个文件”操作期间,您可以将它指向不同的工作树位置。

    我不知道这是否/如何“开箱即用” merge-one-file ,但要设置的env变量为 GIT_WORK_TREE :

    env GIT_WORK_TREE=/some/where/else git ...
    

    (你可以省略 env 大多数,但不是所有的外壳)。

    一个或多或少等效的方法可能“感觉更安全”:-)或出于某些目的更方便,它是在另一个目录中工作,并使用 GIT_DIR 至回购地点:

    cd /some/where/else
    env GIT_DIR=/place/with/repo/.git git ...
    

    您甚至可以将它们组合起来,同时设置 吉特迪尔 和 GIT_WORK_TREE(GIT_WORK_TREE) .

        2
  •  2
  •   CharlesB Craig McQueen    13 年前

    不需要,但最简单的方法是隐藏、合并、暂存和取消隐藏:

    git stash save
    git merge-file foo.txt base-foo.txt their-foo.txt
    git add foo.txt
    git stash pop
    

    如果你不想隐藏,那么你只能选择diff和patch:将工作树更改保存到补丁中,删除工作树更改,进行必要的更改,然后重新应用补丁

    git diff -p --raw foo.txt > foo.txt.diff
    git checkout -- foo.txt
    git merge-file foo.txt base-foo.txt their-foo.txt
    patch -p1 < foo.txt.diff    
    
        3
  •  2
  •   jthill    13 年前

    要合并两个不同文件中的更改,必须检查它们的内容:合并更改就是处理文件内容。内容的工作是在工作树中完成的。在其他地方做这项工作,假装它不是一棵工作树,这只是文字游戏。

    如果您想在进行合并时保持当前工作树不变,请使用另一个工作树。 git克隆很便宜,它是为这样的东西构建的:

    # merge into branch m2 from branch m1 but leave your (non-m2) worktree untouched:
    git clone --no-checkout -b m2 . ../m2-work
    cd ../m2-work
    git reset    # this will warn about the empty worktree, you could instead do
    #              git read-tree HEAD to get the same effect without the chatter
    git merge origin/m1
    git push origin m2
    

    注意 --no-checkout 在克隆上。Merge必须有一个工作树来完成它的工作,但除了需要比较的内容之外,它不关心任何实际的文件内容。

        4
  •  2
  •   Community Mohan Dere    6 年前

    git中的合并是以下各项之间的三元合并:

    • 来源(' remote '或' theirs ',您要合并的内容)
    • 目的地(' local '或' ours ',它始终是工作树,HEAD在其中检出)
    • 共同祖先(或 base ')

    请参阅 地方的 ', ' 基础 ', ' 遥远的 ', ' merged “如图所示” git rebase , keeping track of ' local ' and ' remote ' ".
    你可以在“ git revert does not work as expected ".

    git read-tree 在“ Subtree Merging “和” Git Objects “(以及 you are using in your gist )是关于合并 树 (用于子树合并),而不是文件内容(blob)。
    git write-tree 可以用来创建树对象,但它的文档确实提到了“索引必须处于完全合并状态。”(当你想使用索引合并文件时,这有点困难)。

    这个 git index ( documented here )有记录你表演的内容吗( 合并 '结果),作为合并解决方案的一部分。
    它没有关于文件内容的所有信息,只有 指针 (“索引条目”)到所述内容。这根本不是进行合并的正确结构。


    甚至 git-merge-one-file.sh 脚本本身确实提到:

    require_work_tree
    

    该函数来自 git-sh-setup.sh 脚本(请参阅 its documentation ):

    test "$(git rev-parse --is-inside-work-tree 2>/dev/null)" = true ||
    die "fatal: $0 cannot be used without a working tree."
    

    该要求来自 commit 6aaeca90 ( peff Jeff King ):

    合并一个文件工具早于 GIT_WORK_TREE .

    在大多数情况下, merge-one-file 只适用于 GIT_WORK_TREE(GIT_WORK_TREE) ; 它的大部分重物都是由尊重的管道命令完成的 GIT_WORK_TREE(GIT_WORK_TREE) 正确地


    如果你 真正地 不需要使用工作树,您可以尝试走为 正在合并笔记 :
    notes-merge.c 确实创建了自己的工作树以进行合并 git notes .

        5
  •  1
  •   VonC    13 年前

    Jeff King 帮助我解决了这个问题,我更新了脚本以使其工作:

    https://gist.github.com/cool-RR/6575042

    #!bash
    if [ -n "$2" ]; then
      export SOURCE=$1 ;
      export DESTINATION=$2 ;
    else
      export SOURCE=HEAD ;
      export DESTINATION=$1 ;
    fi
    
    export GIT_INDEX_FILE=`git rev-parse --show-toplevel`/.git/aux-merge-index ;
    export GIT_WORK_TREE=`create_temporary_folder gm_`;
    echo $GIT_INDEX_FILE
    trap 'rm -f '"'$GIT_INDEX_FILE'"'; rm -rf '"'$GIT_WORK_TREE'" 0 1 2 3 15 ;
    mkdir $GIT_WORK_TREE/.git
    set -e ;
    git read-tree -im `git merge-base $DESTINATION $SOURCE` $DESTINATION $SOURCE ;
    #echo Finished read-tree
    #sleep 1000
    git merge-index git-merge-one-file -a
    #echo Finished merge-index
    git write-tree \
    | xargs -i@ git commit-tree @ -p $DESTINATION -p $SOURCE -m "Merge $SOURCE into $DESTINATION" \
    | xargs git update-ref -m"Merge $SOURCE into $DESTINATION" refs/heads/$DESTINATION ;
    exit 0