Git内部:修改`git-merge-one-file`以不使用工作树

时间:2013-09-13 12:52:59

标签: git

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

更新和详细信息

所以我现在明白,如果不使用工作树,就不会发生文件级合并(合并在文件中的行而不是整个文件中)。 (与整个文件的合并不同。)所以我将不得不使用工作树。

另一个细节:我可以使用该解决方案仅在合并可以自动完成而无需手动解决的情况下工作。如果合并不是自动的,它只是显示错误消息是没关系的。 (当然,保持一切都干净。)

另一个细节:我没有直接使用git-merge-one-file,我在这个脚本中使用它: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 个答案:

答案 0 :(得分:5)

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

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

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

(你可以省略env大部分但不是全部的炮弹。

或多或少等效的方法可能“感觉更安全”:-)或者更方便用于某些目的是在另一个目录中工作,并使用GIT_DIR到repo的位置:

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

您甚至可以将它们组合在一起,同时设置GIT_DIRGIT_WORK_TREE

答案 1 :(得分:2)

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

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    

答案 2 :(得分:2)

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

如果要在进行合并时保持当前的工作树不变,请使用其他工作树。 git clone很便宜,它是为这样的东西构建的:

# 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。合并必须有一个工作树来完成它的工作,但它并不关心除了需要比较的文件内容之外的任何实际文件内容。

答案 3 :(得分:2)

git中的合并是以下三方合并:

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

请参阅“git rebase, keeping track of 'local' and 'remote'”中说明的“local”,“base”,“remote”,“merged”。
您可以在“git revert does not work as expected”中看到一个示例。

Subtree Merging”和“Git Objects”(以及you are using in your gist)中提到的

git read-tree是关于合并(用于子树合并) ,而不是文件内容(blob) git write-tree可用于创建树对象,但其文档确实提到“索引必须处于完全合并状态”。 (当您想使用索引合并文件时有点难。)

git indexdocumented here)用于记录您上传的内容(“merged”结果),作为合并解析的一部分,来自您的工作树。 /> 它没有关于文件内容的所有信息,只有指针(“索引条目”)到所述内容。这不是合并的正确结构。


即使是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 6aaeca90peff Jeff King):

  

合并单文件工具早于GIT_WORK_TREE的发明。

     

在大多数情况下,merge-one-file仅适用于GIT_WORK_TREE;它的大部分繁重工作都是由管道命令完成的,这些命令正确地尊重GIT_WORK_TREE


如果确实不需要使用工作树,您可以尝试选择合并笔记选择的路线:
notes-merge.c会创建自己的工作树来合并git notes

答案 4 :(得分:1)

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