我可以在不修改我的工作副本的情况下重新绑定Git分支吗?

Joe*_*ite 58 git rebase

假设我检查了我的"主"分支.我已经对"master"进行了一些生产更改,现在我想将我的"实验"分支改为最新的master.但是,我想在不修改工作副本中的任何文件的情况下执行此操作.从本质上讲,我希望所有的魔法都发生在.git目录中,而不需要触及工作副本.

如果不是"不要修改我的工作副本"要求,这只是一个问题:

# current branch is master
git checkout experimental
git rebase master
git checkout master
Run Code Online (Sandbox Code Playgroud)

我真正的问题是,这会修改我的工作副本中的时间戳,即使我通过查看与我开始的完全相同的内容结束.一旦我运行"git checkout experimental",任何包含实验分支中的更改的文件都会将其mtime设置为当前时间 - 因此,自上次重新实验以来,在master中更改的所有文件也是如此.因为mtimes已经改变了,所以像构建工具这样的东西会让人觉得他们需要再做一些工作,即使在我完成时,文件的内容实际上并没有改变.(就我而言,如果项目文件的时间戳发生变化,Visual Studio认为需要花费大量时间来卸载和重新加载项目.)我想避免这种情况.

有没有办法一步完成上述所有操作,而无需修改工作副本中的任何内容 (假设在rebase期间没有冲突)

(如果冲突,我更倾向于将显示该错误,然后中止整个操作,而不用修改任何时间戳但是,这只是我的偏好,而不是硬性要求-我不知道什么是一切皆有可能.)

当然我可以编写一个脚本来捕获mtimes,运行git,然后重置mtimes; 但似乎Git已经有办法在没有打扰工作副本的情况下执行rebase这样的事情,因为rebase实际上是关于增量,而不是文件的实际内容.

Cra*_*lin 34

git 2.5开始,更好的解决方案是使用第二个工作.

git存储库可以支持多个工作树,允许您一次签出多个分支.

$ git worktree add ../second-copy experimental
$ cd ../second-copy/
$ git rebase master experimental
Run Code Online (Sandbox Code Playgroud)

就是这样.之后,您可以rm -rf second-copy根据需要,或将来保留更多的折扣.

$ git rebase master experimental
Run Code Online (Sandbox Code Playgroud)


Cas*_*bel 33

有没有办法一步完成上述所有操作,而无需修改工作副本中的任何内容?

遗憾的是,这是不可能的(没有创建工作副本的可修改副本 - 参见Petr的答案),因为git在工作树上执行所有merge-y操作(真正的合并,樱桃挑选,rebase,补丁应用程序).之前曾多次提到这一点,例如知识渊博的JakubNarębski的答案之一:

合并(或rebase)无法在不触及工作目录(和索引)的情况下工作,因为可能存在必须使用工作目录(和/或索引)解决的合并冲突.

是的,这是一个设计决定,但这是一个非常容易理解的问题 - 构建在内存中尝试合并所需的所有结构是一件苦差事,然后一旦遇到冲突,就将所有内容转储到工作树,相反,你可以在工作树中首先完成它.(我不是一个git开发人员;不要把它当作绝对完整的事实.可能有其他原因.)

我的建议,而不是编写脚本来完成所有mtime操作,只是克隆存储库,在克隆中执行rebase,然后将其推回到原始存储库:

git clone project project-for-rebase
cd project-for-rebase
git branch experimental origin/experimental
git rebase master experimental
git push origin experimental
Run Code Online (Sandbox Code Playgroud)

当然,这假设您的原始回购没有检查实验.如果它是,而不是推动,你会做一些类似git fetch ../project-for-rebase experimental; git reset --hard FETCH_HEAD或更可读的东西,git remote add for-rebase ../project-for-rebase; git fetch for-rebase; git reset --hard for-rebase/experimental.这将自然地触及原始和重新定位的实验分支之间的任何文件,但这绝对是正确的行为.(当然,这不是你给出的例子,但我希望这些指示是通用的!)

  • 出色的解释原因,以及出色的替代方案.谢谢! (2认同)
  • @DaveAbrahams我认为很明显我认为这是不可能的*没有Petr的答案所创造的工作树*的副本.但投你喜欢的方式. (2认同)

Pet*_*rin 13

我已经在linux上创建了一个小脚本.它基于Jefromi的答案和一些补充(主要是设置替代,因此不复制对象数据库,只拉动所需的分支).有些人可能觉得它很有用:https: //github.com/encukou/bin/blob/master/oot-rebase

如果它没有做你喜欢的,欢迎拉请求:)

  • 默认情况下,Git 不允许推送到当前分支,即使使用 -f (2认同)

dpw*_*ell 6

与创建存储库的克隆类似,我发现利用多个工作来完成这样的事情会更加整洁.克隆也会占用大量磁盘空间,这几乎不会占用任何空间.

https://github.com/git/git/blob/master/contrib/workdir/git-new-workdir

你创建一个像这样的新工作区:

git-new-workdir project-dir new-workdir branch
Run Code Online (Sandbox Code Playgroud)

然后,您可以将其视为克隆,除了原始workdir中的提取和提交将在此处反映(尽管不在没有重新检查的工作分支上).唯一的例外是,如果你有子模块,那么默认情况下这些子模块是为每个workdir单独完成的.坦率地说,我从来没有考虑到这一点,因为我试图避免子模块.

所以基本上只是:

cd new-workdir
git checkout experimental
git rebase master
Run Code Online (Sandbox Code Playgroud)

不完全是一个命令,但非常简单.

这种方法(如克隆方法)优于下面的存储方法的一个优点是,如果您的工作目录中当前正在执行代码(或者某些进程正在使用它们),则不会中断它.


此处未提及的另一个选项是在当前工作目录中执行此操作,但存储更改以便您可以立即恢复工作目录状态.

# current branch is master (with changes to working state)
git stash -u
git checkout experimental
git rebase master
git checkout master
git stash pop
Run Code Online (Sandbox Code Playgroud)

stash -u如果您有任何新文件,请确保使用,否则它们将不会被隐藏.再说一次,不是一步,而是非常干净和简单.