【问题标题】:committing to a branch that's not checked out提交到未签出的分支
【发布时间】:2011-01-29 16:40:32
【问题描述】:

我正在使用 git 在几台不同的机器上对我的主目录进行版本控制。我希望他们每个人都使用单独的分支,并且都从一个公共分支中提取。因此,大多数提交都应该提交到该公共分支,除非正在提交特定于该机器的某些内容,在这种情况下,提交应该转到已签出的特定于机器的分支。在这种情况下,切换分支显然不是一个很好的选择。

this post 中提到我想做的事情是不可能的,但我发现这个答案相当直率,可能没有考虑使用管道命令的可能性。不幸的是,我没有足够的声誉来评论该线程。我宁愿怀疑有什么方法可以做到这一点,并希望通过询问你们这些好人来节省自己一个小时或几个小时的时间来寻找答案。

那么是否可以在不先检查该分支的情况下提交到不同的分支?理想情况下,我希望以与 git commit 通常相同的方式使用索引。

【问题讨论】:

  • 我同意,顺便说一句 - 其他问题根本没有得到令人满意的回答。除了 HEAD 之外,你可能不应该承诺任何地方,但如果你决定你真的想要,你可以做到。这个问题值得回答。
  • 希望你还在看这个 - 我记得 git apply--cached 选项。它使这变得如此容易 - 看看我的答案。

标签: git


【解决方案1】:

我相信做你想做的最好的方法是在特定于机器的分支上进行提交,然后使用git rebase 移动它们。这与我对自己的主目录所做的大致相同 - 与您的情况基本相同。

# make a new branch starting from branch machine_1
git checkout -b move_to_master      
# make whatever commits you need to
git rebase --onto master machine_1 move_to_master
git checkout master
git merge move_to_master  # this is a fast-forward
git checkout machine_1
git merge master

如果您在创建 move_to_master 之前不小心提交到 machine_1,只需创建 move_to_master,然后将 machine_1 重置回它所属的位置,然后按照其余步骤操作。

但是,您的问题值得回答,我在底部提供了更多替代方案。

不在当前分支上创建提交

注意事项:非常非常小心!这是可怕的东西!

可能提交到未使用管道命令签出的分支,但这不是非常可取的。你必须让你的索引进入你想要的状态(这可能很棘手),然后你可以使用git commit-tree

git commit-tree -p $PARENT_COMMIT < $COMMIT_MESSAGE_FILE

这会将新创建的提交对象的 SHA1 打印到标准输出;假设PARENT_COMMIT 是一个分支提示,那么您必须使用git update-ref 将分支更新到它:

git update-ref -m "commit: [commit subject]" $BRANCH $NEW_SHA1

如果您正在编写脚本,您可以使用git update-ref -m ... $(git commit tree ...) 以单行方式实现它。这是最可怕的一步。如果你更新引用你的另一个分支到错误的地方,那就太糟糕了。不过,您仍然可以使用 git reflog show $BRANCH 找出将其重置回的位置。

无论如何,这只是简单的部分。真正困难的是在不实际检查文件的情况下使索引进入您想要的状态。您可能会使用的两个常用命令:

  • git read-tree - 将树信息读入索引,但根本不更新工作树(git checkout 大致相当于 git read-tree、git checkout-index 和 git update-ref HEAD)。您可以使用它来使索引包含该未签出分支的内容,而不是 HEAD。
  • git update-index - 修改索引。您可以使用它在工作树的索引中添加、删除或刷新文件。
  • git checkout-index - 将给定路径从索引复制到工作树中。使用 read-tree 后,您可以使用它来获取要更改的单个文件,对其进行修改,然后使用 update-index 将其放回原处。 “修改它”步骤可能很复杂 - 例如,在执行所有这些操作之前,您可以使用 git diff 创建一个补丁,然后在此处使用 git apply 应用它。
  • git apply --cached 使用 --cached 选项,这会将补丁直接应用于索引中的版本,而无需触及工作树。因此,您可以创建一个差异,读取另一个分支的树,将其应用于索引,提交树,然后您就设置好了。这可能是最棒的方法了。

这一切如此困难的原因是,所有让您访问其所有强大合并功能的 git 命令都依赖于工作树中的文件。当您考虑它时,您正在尝试执行的任务是合并 - 您在一个分支上有一个差异,并且您想将它应用到另一个分支上。获得结果的方法是进行三路合并,使用原始分支上的差异、与另一个分支的共同祖先以及另一个分支的尖端。如果没有检出其他分支,您将无法真正进行此合并!

像往常一样使用管道命令执行操作时,您应该非常小心地了解一切是如何工作的,以免严重破坏您的存储库。也就是说,在重组现有存储库(由其他人创建的存储库......不要问)时,我实际上已经使用它来发挥很大的作用。我只是在这些情况下重新安排提交 - 使用读取树而不是通常更新索引 - 所以它比你可能尝试做的要简单得多。

替代方法

说了这么多,您还可以采取其他几种方法来完成您想做的事情。

  • 克隆您的存储库。如果您只跟踪配置文件,这不会占用太多额外空间,而且事情会容易得多。如果你真的很着迷,你甚至可以使用git new-workdir(链接到 git.git 的 HEAD 中的版本)脚本只创建一个工作目录,而不复制其余的 repo(它使用 .git 中的符号链接目录)。请记住要小心将一个 workdir 提交到另一个已签出的分支 - 另一个最终会导致其工作树不同步。

  • 编写一个单一提交包装脚本 - 这是对所有这些选项的另一个分支进行单一提交最接近的方法:

    git commit
    orig_branch=$(git symbolic-ref HEAD)
    orig_branch=${orig_branch#refs/heads/}
    git checkout master
    git cherry-pick $orig_branch
    git checkout $orig_branch
    git reset --hard HEAD^
    

【讨论】:

  • 很好地描述了在 HEAD 之外提交所需的管道。在处理索引时,也可以使用GIT_INDEX_FILE 来避免在编写自定义提交时踩到默认索引。除非工作树超级大,否则我建议使用 Jefromi 的第一种替代方法(clone/git-new-workdir)。在公共分支上提交(在 clone/alt-workdir 中),推送它(如果使用克隆),然后将其合并到特定于机器的分支中。它应该更干净,更不容易出错(因为你有完整的工作树来处理合并冲突等)。
  • 您还将拥有带有cherry-pick/rebase 选项的完整工作树——它们只涉及一点分支交换。我也忘记了 GIT_INDEX_FILE - 我打算写一切都可以选择,但只有 read-tree 可以,然后我显然有点分心了。
  • 很有启发性。我不太使用那些管道命令。 +1
【解决方案2】:

我为我的 Visual Git Reference 项目这样做。我运行make gh-pages,它构建网站并将其提交到gh-pages 分支,而无需切换分支。请参阅我的 GitHub repo,尤其是文件 Makefilepublish。我可能应该像上面提到的克里斯约翰森那样使用$GIT_INDEX_FILE,但这似乎工作正常。

【讨论】:

    【解决方案3】:

    我可能误解了您的请求,但请告诉我这是否是您要查找的内容。

    您从特定于机器的分支开始。您进行了更改,但不是提交到特定于机器的分支,而是希望将更改提交到公共分支。你应该做的是

    git stash              # save your changes for later
    git checkout common    # switch to the common branch
    git stash apply        # recall the stashed changes
    git commit             # commit as appropriate (on the common branch)
    git stash drop         # kill off the last stashed changes (this is optional)
    git checkout machine1  # go to your machine-specific branch
    git rebase common      # put your machine-specific changes after the common ones
    git checkout machine2  # [repeat]
    git rebase common
    

    或者,您可以使用git merge 而不是git rebase,如果您的更改不能这样应用的话。

    这适用于您的情况吗?

    【讨论】:

    • 我已经决定我实际上并不喜欢 stash 解决方案,尽管我最初也建议过它。问题是,如果您只想提交一些更改,事情会变得有点复杂,即使--keep-index 可用。对于一个好的单提交包装器,请参阅我的帖子底部。
    • 我在这种情况下使用git stash 的问题是它需要切换分支。如果一个 cron 作业在我中间进行时,可能会出现问题,因为特定于机器的配置/命令将不存在。或者,如果我分心并在不同的外壳中做某事..
    • Jefromi 提供了解决此问题的详细方法,并且您已相应地选择了他的答案。我会警惕您有一个未提交更改的工作目录的想法,但是由于目录上的外部依赖项(例如cron),您无法更改目录中的内容。也许您可以在另一个目录中进行更改,在那里进行测试,然后将这些更改推送到cron 使用的部署目录。只是我的 0.02 美元。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-25
    • 2013-12-30
    • 2020-11-03
    • 2016-07-19
    • 2018-10-31
    • 1970-01-01
    相关资源
    最近更新 更多