Git - 防止在合并时添加开发目录
Git - prevent a development directory from being added on merge
我是一名程序员,更熟悉 C# 或 Python 等高级编程语言,但我最近开始深入研究 Git 的复杂性。上下文:我正在开发一个游戏,我有一个 development
分支,其中有一个特定的 'Development'
目录,为了保持 master
干净。
我来到了 this answer,这几乎完成了工作;但是,此方法虽然不会 提交 对目录的更改,但 仍会在 master
分支 上创建它,这意味着每次合并后,我仍然需要返回并手动删除文件。
我正在设置一个别名 git publish
来自动执行此任务,为了完整起见,我将在此处 post:
[alias]
publish = "!f(){ git checkout master \
&& git merge --no-commit --no-ff development \
&& git reset -- Assets/Development/ \
&& git commit \
&& git push; };f"
我想知道,有什么方法可以防止在 master
上创建 Assets/Development/
?
旁注:摆弄 .gitignore
无济于事,因为 .gitignore
的本质并不是真正的“要忽略的文件”,而是“抑制某些投诉的文件” , and/or 在某些情况下可以随意销毁”。 (这并不是说你不应该因为其他原因调整 .gitignore
,只是说它对解决这个合并问题没有帮助,因为你真的 想要那些文件在开发分支中版本化。)
您现有的解决方法(以及可能改进它的方法)
一个技巧基本上就是您现在正在做的:进行合并(使用 --no-commit
),然后删除不需要的内容。
剥离步骤是这样的:
git reset -- Assets/Development/
这使用 --mixed
(默认,并且在给定路径时强制要求您)形式的 git reset
,它设置(或“重置”,实际上)索引以匹配指定的树指定的路径。这里的路径是 Assests/Development/
,即该子目录中的所有内容。指定的树,嗯,没有指定——所以它默认为 HEAD
,或者更确切地说,HEAD
的树。 HEAD
是当前分支上的当前提交,当然是 master
因为我们刚刚检查过它;并且 master
根本不包含 Assets/Development/
,因此重新设置的索引现在不再具有该子树。
简而言之,因为 Assets/Development/
不在 HEAD
中,这会撤消 git merge
对 Assets/Development
中所有内容所做的 git add
。问题是,虽然这会从 index 中删除条目,但会将它们留在 work tree.
中
有人可能会尝试 git reset --hard -- Assets/Development/
但 git 不会那样做:您不能在提供路径时指定重置模式。不过,这还剩下三个更明显的选择:
添加 rm -r Assets/Development
(就在 git reset
之前或之后,甚至在 commit
步骤之后)
这会删除文件,因此随着它们从索引和工作树中消失,您可以提交并完成。
使用git rm -rf Assets/Development/
(代替git reset
)
这会删除索引条目并且会删除工作树文件(和目录),因此您可以提交并完成。
提交后使用git clean
。这可能比您想要的更多。
最主要的是,你可能只需要这样做一次,而不是重复。这取决于您在此过程中所做的更改。
关于合并的更多信息
当你运行 git merge <commit>
, git 首先找到合并基.
合并基础被定义为L最低C共同A祖先(LCA ) 在图中两个节点之间的提交图中。这两个节点是您当前的提交——在本例中是分支 master
的尖端——以及您给 git merge
的提交,在本例中是分支 development
的尖端提交。 LCA 的定义需要一些图论,但粗略地说,它是 both 提交历史上的“最近”提交。
要了解这是如何工作的,绘制部分提交图会有所帮助。这里我们有 master
和 development
作为两个分支提示,两个分支结构(提交链)最终连接起来:
... <- o <- * <- o <- ... <- o <-- master
\
o <- o <- ... <- o <-- development
标记为 *
的提交是合并基础:它是 development
和 master
重新加入的点,并且提交 *
并且每个较早的提交都在 两个个分支,而不是只在一个分支上。
此时 merge
所做的是生成两个差异:一个从 *
到当前提交的提示(master
上的提示提交),一个来自 *
*
给对方,目标,提交(development
的提示)。
在 master
中你有一些(可能不是太多)自 *
以来的变化,在 development
中你有一堆变化,包括添加 Assets/Development/
.
merge
命令合并了更改,其中包括添加不需要的目录。全部完成后,git 进行新的提交(--no-ff
步骤保证了这一点;如果合并基础提交 *
是 master
的提示,这不是我们绘制的但是在第一次合并时是可能的,git 会尝试做一个标签快进而不是进行实际合并)。
结果是一个现在看起来更像这样的图表(我将停止绘制向左的箭头,请注意历史向左移动,即,后来的提交指向早期的):
...--o--*--o-...-o---o <-- master
\ /
o--o-...-o <-- development
现在您继续在 development
上开发,做出更多新的提交。让我们将 *
替换为 o
,因为它不再是特例。
...--o--o--o-...-o---o <-- master
\ /
o--o-...-o--o--o <-- development
并且在某个时候你决定再次合并,所以你查看 master
并告诉 git merge
找到 development
的(新)尖端并计算合并基数。
合并基础再次(有点松散)是 两个 分支上的第一个提交。让我们找一下:它和以前一样吗?从 master
和 development
的尖端开始,从每个尖端开始,用两种不同的颜色为节点着色。一旦一个节点获得两种颜色,这就是一个共同的祖先。 最接近提示的是“最低”,即合并基础。
我不会画颜色,但是如果你自己做练习,你会看到是这个节点:
...--o--o--o-...-o---o <-- master
\ /
o--o-...-*--o--o <-- development
现在标记为 *
的节点可以从 master
(通过跟随第二个父节点向下和向左)和从 development
(通过跟随链向左)到达。它是最近的此类节点,因此它是合并基础。
Git 现在将合并基础 *
与两个提示进行比较。 master
的尖端应该几乎没有任何变化(基本上只有从前一个合并基础和新的 master
尖端之间的 o
节点拾取的任何东西),以及任何自上次合并以来您对 development
分支所做的更改。
如果这些更改影响 Assets/Development
中的现有文件,这些更改将导致合并冲突:
CONFLICT (modify/delete): Assets/Development/foo deleted in HEAD
and modified in development. Version development of
Assets/Development/foo left in tree.
Automatic merge failed; fix conflicts and then commit the result.
请注意,您不需要 --no-commit
。
如果这些更改在 Assets/Development/
中添加 new 文件,另一方面,merge 会很乐意将它们添加到您即将进行的提交中,因此您可以这样做需要 --no-commit
来避免添加任何此类文件。
如果需要,您可以再次使用 git rm -rf
来丢弃任何冲突 and/or 添加的文件。
我是一名程序员,更熟悉 C# 或 Python 等高级编程语言,但我最近开始深入研究 Git 的复杂性。上下文:我正在开发一个游戏,我有一个 development
分支,其中有一个特定的 'Development'
目录,为了保持 master
干净。
我来到了 this answer,这几乎完成了工作;但是,此方法虽然不会 提交 对目录的更改,但 仍会在 master
分支 上创建它,这意味着每次合并后,我仍然需要返回并手动删除文件。
我正在设置一个别名 git publish
来自动执行此任务,为了完整起见,我将在此处 post:
[alias]
publish = "!f(){ git checkout master \
&& git merge --no-commit --no-ff development \
&& git reset -- Assets/Development/ \
&& git commit \
&& git push; };f"
我想知道,有什么方法可以防止在 master
上创建 Assets/Development/
?
旁注:摆弄 .gitignore
无济于事,因为 .gitignore
的本质并不是真正的“要忽略的文件”,而是“抑制某些投诉的文件” , and/or 在某些情况下可以随意销毁”。 (这并不是说你不应该因为其他原因调整 .gitignore
,只是说它对解决这个合并问题没有帮助,因为你真的 想要那些文件在开发分支中版本化。)
您现有的解决方法(以及可能改进它的方法)
一个技巧基本上就是您现在正在做的:进行合并(使用 --no-commit
),然后删除不需要的内容。
剥离步骤是这样的:
git reset -- Assets/Development/
这使用 --mixed
(默认,并且在给定路径时强制要求您)形式的 git reset
,它设置(或“重置”,实际上)索引以匹配指定的树指定的路径。这里的路径是 Assests/Development/
,即该子目录中的所有内容。指定的树,嗯,没有指定——所以它默认为 HEAD
,或者更确切地说,HEAD
的树。 HEAD
是当前分支上的当前提交,当然是 master
因为我们刚刚检查过它;并且 master
根本不包含 Assets/Development/
,因此重新设置的索引现在不再具有该子树。
简而言之,因为 Assets/Development/
不在 HEAD
中,这会撤消 git merge
对 Assets/Development
中所有内容所做的 git add
。问题是,虽然这会从 index 中删除条目,但会将它们留在 work tree.
有人可能会尝试 git reset --hard -- Assets/Development/
但 git 不会那样做:您不能在提供路径时指定重置模式。不过,这还剩下三个更明显的选择:
添加
rm -r Assets/Development
(就在git reset
之前或之后,甚至在commit
步骤之后)这会删除文件,因此随着它们从索引和工作树中消失,您可以提交并完成。
使用
git rm -rf Assets/Development/
(代替git reset
)这会删除索引条目并且会删除工作树文件(和目录),因此您可以提交并完成。
提交后使用
git clean
。这可能比您想要的更多。
最主要的是,你可能只需要这样做一次,而不是重复。这取决于您在此过程中所做的更改。
关于合并的更多信息
当你运行 git merge <commit>
, git 首先找到合并基.
合并基础被定义为L最低C共同A祖先(LCA ) 在图中两个节点之间的提交图中。这两个节点是您当前的提交——在本例中是分支 master
的尖端——以及您给 git merge
的提交,在本例中是分支 development
的尖端提交。 LCA 的定义需要一些图论,但粗略地说,它是 both 提交历史上的“最近”提交。
要了解这是如何工作的,绘制部分提交图会有所帮助。这里我们有 master
和 development
作为两个分支提示,两个分支结构(提交链)最终连接起来:
... <- o <- * <- o <- ... <- o <-- master
\
o <- o <- ... <- o <-- development
标记为 *
的提交是合并基础:它是 development
和 master
重新加入的点,并且提交 *
并且每个较早的提交都在 两个个分支,而不是只在一个分支上。
此时 merge
所做的是生成两个差异:一个从 *
到当前提交的提示(master
上的提示提交),一个来自 *
*
给对方,目标,提交(development
的提示)。
在 master
中你有一些(可能不是太多)自 *
以来的变化,在 development
中你有一堆变化,包括添加 Assets/Development/
.
merge
命令合并了更改,其中包括添加不需要的目录。全部完成后,git 进行新的提交(--no-ff
步骤保证了这一点;如果合并基础提交 *
是 master
的提示,这不是我们绘制的但是在第一次合并时是可能的,git 会尝试做一个标签快进而不是进行实际合并)。
结果是一个现在看起来更像这样的图表(我将停止绘制向左的箭头,请注意历史向左移动,即,后来的提交指向早期的):
...--o--*--o-...-o---o <-- master
\ /
o--o-...-o <-- development
现在您继续在 development
上开发,做出更多新的提交。让我们将 *
替换为 o
,因为它不再是特例。
...--o--o--o-...-o---o <-- master
\ /
o--o-...-o--o--o <-- development
并且在某个时候你决定再次合并,所以你查看 master
并告诉 git merge
找到 development
的(新)尖端并计算合并基数。
合并基础再次(有点松散)是 两个 分支上的第一个提交。让我们找一下:它和以前一样吗?从 master
和 development
的尖端开始,从每个尖端开始,用两种不同的颜色为节点着色。一旦一个节点获得两种颜色,这就是一个共同的祖先。 最接近提示的是“最低”,即合并基础。
我不会画颜色,但是如果你自己做练习,你会看到是这个节点:
...--o--o--o-...-o---o <-- master
\ /
o--o-...-*--o--o <-- development
现在标记为 *
的节点可以从 master
(通过跟随第二个父节点向下和向左)和从 development
(通过跟随链向左)到达。它是最近的此类节点,因此它是合并基础。
Git 现在将合并基础 *
与两个提示进行比较。 master
的尖端应该几乎没有任何变化(基本上只有从前一个合并基础和新的 master
尖端之间的 o
节点拾取的任何东西),以及任何自上次合并以来您对 development
分支所做的更改。
如果这些更改影响 Assets/Development
中的现有文件,这些更改将导致合并冲突:
CONFLICT (modify/delete): Assets/Development/foo deleted in HEAD
and modified in development. Version development of
Assets/Development/foo left in tree.
Automatic merge failed; fix conflicts and then commit the result.
请注意,您不需要 --no-commit
。
如果这些更改在 Assets/Development/
中添加 new 文件,另一方面,merge 会很乐意将它们添加到您即将进行的提交中,因此您可以这样做需要 --no-commit
来避免添加任何此类文件。
如果需要,您可以再次使用 git rm -rf
来丢弃任何冲突 and/or 添加的文件。