我正在编写一个编程教程项目,我希望本教程的示例源代码具有与教程进度相关的有意义的修订历史记录。不可避免的是,我不会在第一次完全正确地完成所有教程提交,并且我不希望修改历史与提交以某种方式修改教程的提交混乱。我认为这意味着我需要两个级别的版本控制:一个与教程用户相关的内部版本,以及一个跟踪我如何重写内部版本历史的外部版本。
我从其他SO问题(例如'Is it possible to have a git repo inside another git repo')中看到Git忽略了子目录中的.git。这似乎排除了我至少有一个版本控制级别的git。
有人可以推荐一种策略来跟踪内容的更改,并重写该历史记录吗?
答案 0 :(得分:1)
在git中,有一个稍微更高级别的概念,即已发布的分支&#39 ;,一个分支,包含您希望全世界看到的清理提交;以及未发布的分支,你保留作为草案区域,用于提交你还在抛光。
未发布的分支通常会有更多的提交,因为你正在写作并经常提交(对吗?!)。然后使用git rebase -i
进行清理并将干净的历史记录推送到已发布的分支中,然后将其推送到远程仓库以供所有人享用。
以下页面提供了更多详细信息,这也是一般的git最佳实践集合:http://sethrobertson.github.io/GitBestPractices/#sausage
答案 1 :(得分:1)
我可以想到两种方法,它们都使用了一些管道,现有的瓷器都是用其他东西建造的。
第一种方式最简单,但我最近才知道它甚至可能 1 并且怀疑一些经验丰富的git用户会认为它是一种怪物。问题是,在这里它是一个非常有用的怪物,并且在过去两个特征之间的争论中,有用的"有时证明更有用......所以:
第一种方法:
您可以直接跟踪嵌套存储库中也跟踪的内容。一旦git跟踪目录中的任何内容,它将完全 2 忽略您随后在那里创建的任何存储库。
从您的问题中可以看出,您已经获得了整齐可分割的部分,因此,从顶部开始:
使用存根(或当前)初始内容
创建一个完美的普通存储库# from the top:
# create and commit the empty skeleton
git init book
cd !$
mkdir -p sect{1,2,3}
touch {.,!$}/.gitignore
# copy in any initial content here
git add .
git ls-files -s # to see exactly what you've done so far
git commit -m 'initial skeleton'
创建子存储库以独立跟踪各个部分
# now git is directly tracking content in each section, and commands in the
# parent will _ignore_ the existence of any nested repositories you subsequently
# create, but not there worktrees (because of the existing tracked content). viz.:
( cd sect1
git init
git add .
git commit -m 'initial skeleton'
git branch publishing-history
)
^1^2
^2^3
独立自由地处理每个部分
现在,您可以在多个存储库中跟踪这些部分,并且可以完全独立地处理每个部分:
cd sect1
# work work commit commit lalala
# ... do whatever in the other repos
发布所有版块的合并当前内容
并且是时候在每个子目录中发布当前内容了。让他们的内容全部清理出来,并从其中任何一个,只做一次,
cd ..
git add -A .
git commit
published=`git rev-parse HEAD`
你已经完成了。如何记录行为:
在每个部分记录行为,以供参考
for section in sect*; do
cd $section
git update-ref refs/heads/publishing-history $(
# log where the checked-out commit was published
git commit-tree \
-p publishing-history \
-p `git rev-parse HEAD` \
-m "## published in main repository commit $published ##" \
HEAD^{tree} # just `HEAD:` will work too
)
cd ..
done
您选择发布的提交或顺序没有限制。这就是为什么Linus称git a"愚蠢的内容跟踪器":没有抽象的核心。分支正确记录了这些提交的顺序,内容和祖先。
commit-tree和update-ref的便捷链接。
构建重写的独立发布历史记录
git symbolic-ref HEAD refs/heads/newmaster
并按上述方式发布您喜欢的任何已签出提交序列。 publish-history
分支将忠实地记录您发布的内容和时间。
你可以看到这是怎么回事,对吗?您可以使用commit-tree
和update-ref
从已提交的内容构建任意历史记录。如果父repo中的提交序列不是您想要的,请通过直接提交正确的树序列将其替换为您想要的完全不同的历史记录。要在父存储库中记录单独的注释,请在其上使用发布历史构造。
请注意:如果您开始进行大量的历史记录重写,并且构建新序列所涉及的检查开始显得很麻烦,那么git可以帮助您解决问题。
}时,请从gitcore-tutorial开始。第二种方式
这种方式取代了"发布组合的当前内容"从完全独立的存储库中获取和操作树,而不是使用上面的重叠存储库方法。然后发布步骤
cd ../main
git read-tree --empty
for repo in sect{1,2,3}; do
( cd ../$repo
tag -f fetchme HEAD^{tree}
)
git fetch ../sect1 fetchme
git read-tree -m --prefix=sect1 FETCH_HEAD
done
git commit
但这样做的缺点是,您不仅要明确同步重复的工作树,还需要更多的工作树副本,以启用任何整个项目测试,而无需发布(如上所述)每个版本的#39 ;重新去测试。
也许它只是一种精神状态,但这种方式看起来足够笨重,我认为它不值得追求。
随机说明:
git clean -dfx
没有清除嵌套repos的工作树,git显然只会在嵌套的.git
有用时忽略它。嗯。这可能会以有用的方式滥用。
如果您想保护嵌入式存储库免受随机rm -rf sect3
的攻击,您可以使用git submodule
使用的方法,
mv .git /someplace/safer
echo gitdir: /someplace/safer >.git
并且核弹后的重建是mkdir -p
和echo
1 See here for the question that taught me this was possible
2 事实证明git clean
确实识别嵌套的存储库,并且不会清除它们。所以git clean -dfx
仍然是安全的。 更多有用的行为: - )