假设我检查了我的“主”分支。我已经对“master”进行了一些生产更改,现在我想将我的“实验”分支改为最新的master。但是,我想在不修改工作副本中的任何文件的情况下执行此操作。从本质上讲,我希望所有的魔法都发生在.git目录中,而不会触及工作副本。
如果不是“不要修改我的工作副本”的要求,这只是一个问题:
# current branch is master
git checkout experimental
git rebase master
git checkout master
我真正的问题是,这会修改我的工作副本中的时间戳,即使我通过查看与我开始的完全相同的内容结束。一旦我运行“git checkout experimental”,任何包含实验分支中的更改的文件都会将其mtime设置为当前时间 - 因此,自上次重新实验以来,在master中更改的所有文件也是如此。因为mtimes已经改变了,所以像构建工具这样的东西会让人觉得他们需要再做一些工作,即使在我完成时,文件的内容实际上并没有改变。 (就我而言,如果项目文件的时间戳发生变化,Visual Studio认为需要花费大量时间来卸载和重新加载项目。)我想避免这种情况。
有没有办法一步完成上述所有操作,而无需修改工作副本中的任何内容 (假设在rebase期间没有冲突) 的吗
(如果是冲突,我的偏好是显示错误然后中止整个操作,而不修改任何时间戳。但这只是我的偏好,而不是硬性要求 - 我不知道一切皆有可能。)
当然我可以编写一个脚本来捕获mtimes,运行git,然后重置mtimes;但似乎Git已经有办法在没有打扰工作副本的情况下执行rebase这样的事情,因为rebase实际上是关于增量,而不是文件的实际内容。
答案 0 :(得分:32)
有没有办法一步完成上述所有操作,而无需修改工作副本中的任何内容?
遗憾的是,这是不可能的(没有创建工作副本的可修改副本 - see also Petr's answer),因为git在工作树上执行所有merge-y操作(真正的合并,樱桃挑选,rebase,补丁应用程序) 。之前已多次提及此问题,例如one of the knowledgeable Jakub Narębski's answers:
合并(或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
当然,假设您的原始回购中未检验到实验。如果是,则执行类似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
,而不是推送。这将自然地触及原始和重新定位的实验分支之间的任何文件,但这绝对是正确的行为。 (当然,这不是你给出的例子,但我希望这些指示是通用的!)
答案 1 :(得分:28)
从git 2.5开始,更好的解决方案是使用第二个worktree。
git存储库可以支持多个工作树,允许您一次签出多个分支。
$ git worktree add ../second-copy experimental
$ cd ../second-copy/
$ git rebase master experimental
就是这样。之后,如果您愿意,可以rm -rf second-copy
,或者将来保留更多的折扣。
$ git rebase master experimental
答案 2 :(得分:11)
我已经在linux上创建了一个小脚本。它基于Jefromi的答案和一些补充(主要是设置替代,因此不复制对象数据库,只拉动所需的分支)。有些人可能觉得它很有用: https://github.com/encukou/bin/blob/master/oot-rebase
如果它没有做到你喜欢的,欢迎拉请求:)
答案 3 :(得分:5)
与创建存储库的克隆类似,我发现使用多个工作来完成这样的事情会更加整洁。另外一个克隆将使用大量的磁盘空间,这几乎不会使用。
https://github.com/git/git/blob/master/contrib/workdir/git-new-workdir
你创建一个像这样的新工作区:
git-new-workdir project-dir new-workdir branch
然后你可以把它看作是克隆,除了你原来的workdir中的提取和提交将在这里反映出来(虽然不在没有重新检查的工作分支上)。唯一的例外是,如果您有子模块,则默认情况下为每个workdir单独完成这些子模块。坦率地说,我从未参与其中,因为我试图避免使用子模块。
所以基本上只是:
cd new-workdir
git checkout experimental
git rebase master
不是一个命令,而是非常简单。
这种方法(如克隆方法)优于下面的存储方法的一个优点是,如果您的工作目录中当前正在执行代码(或者某些进程正在使用它们),则它不会被中断。
此处未提及的另一个选项是在当前工作目录中执行此操作,但会存储您的更改,以便您可以立即恢复工作目录状态。
# current branch is master (with changes to working state)
git stash -u
git checkout experimental
git rebase master
git checkout master
git stash pop
如果您有任何新文件,请确保使用stash -u
,否则它们将不会被隐藏。再一次,不是一步,而是非常干净和简单。
答案 4 :(得分:1)
我也很喜欢它,但这对我没有希望:
如果指定了
<branch>
,则为git rebase 将执行自动git结帐 在做任何事之前。 否则它仍然是当前的 分支。
答案 5 :(得分:1)
正如其他人所说的那样,如果不触及工作目录就无法重新分支分支(即使是建议的替代方法,如创建新的克隆或工作树也无法改变这一事实;这些替代方案确实没有触及您当前的工作目录,但只能通过创建一个新的工作树。)
对于您要更新的分支要在当前工作树(或其父代)上进行重新定位的特殊情况,可以&#34; rebase&#34;另一个分支没有不必要地触摸文件 这种特殊情况经常发生在你正在使用git工作流程的地方,你正在处理许多分支,这些分支都是从主要的&#34; master&#34;分支(定期更新到远程主分支)。
为了说明,假设Git存储库具有以下结构:
repo
- commitA
- commitB
- commitC <-- master <-- OtherBranch based on master
- commitD <-- First commit in otherBranch
- commitE <-- Second commit in OtherBranch
- commitD <-- Unrelated commit in current working tree
为了举例,我们假设&#34; OtherBranch&#34;已分支&#34; master&#34;,并且您当前的工作树也基于&#34; master&#34;。 您的工作流程通常从使用远程版本更新本地主分支开始......
# Fetch commits from remote "origin" and update the master branch:
# If your current branch is identical to master
git pull origin master
# If your current branch has extra commits on top of master
git pull --rebase origin master
# If you don't want to touch your current branch
git fetch origin master:master
...然后你摆弄当前的分支并做一些耗时的编译。最后,您决定要使用OtherBranch
。这个OtherBranch
应该在master
上重新定义(最好是文件系统操作最少)。以下部分将说明如何。
以下解决方案是git方式:
git checkout OtherBranch
git rebase master # or git rebase origin/master
缺点是第一个命令会更改当前工作树的日期,即使第二个命令将恢复文件。
要最小化触摸文件的数量,您需要签出新的基本分支,然后使用git cherry-pick
在基本分支顶部的OtherBranch
中应用所有额外提交。
在执行任何操作之前,您需要在OtherBranch
中识别提交。
git log OtherBranch
显示了对OtherBranch的提交(如果您尚未更改OtherBranch
,则主要有用)git reflog
显示对本地存储库中分支的更改(如果您已更新分支并出错,则非常有用)。在当前示例中,您将发现OtherBranch
上的最后一次提交是commitE
。您可以使用git log commitE
查看之前的提交列表(或者如果您想要更短的列表git log --oneline commitE
)。如果查看列表,您将看到基本提交为commitC
。
现在您知道基本提交是commitC
而最后一次提交是commitE
,您可以将其从之前的#34; master&#34;到#34;以及#34; master&#34;)如下:
# Replace the old OtherBranch with "master" and switch to it.
git checkout -B OtherBranch master
# Cherry-pick commits starting from commitC and ending at commitE.
cherry-pick commitC^..commitE
或者(如果您想在替换OtherBranch
之前成功完成&#34; rebase&#34;)
# Create new branch NewOtherBranch based off "master" and switch to it.
git checkout -b NewOtherBranch master
# Cherry-pick commits starting from commitC and ending at commitE.
cherry-pick commitC^..commitE
# Replace the old branch with the current branch (-M = --move --force)
git branch -M OtherBranch
在git中重新分支分支需要将当前分支切换到要更新的分支(OtherBranch
)。
使用git rebase
工作流程,会发生以下情况:
OtherBranch
(可能分支出一个非常古老的基础分支)。步骤1和步骤3触摸许多文件,但最终许多触摸的文件实际上没有改变。
我的方法将步骤1和3合并到步骤3中,因此触摸的文件数量最少。唯一触及的文件是:
OtherBranch
。答案 6 :(得分:-1)
所以你想要在结账之前为一个分支完成一个rebase?我真的看不出原因,因为如果你不检查那个分支你就无法解决它。为什么要修改一个你不工作的分支?结帐时,它会改变你的mtime,然后进行rebase。 rebase将触摸已更改的文件,当然您需要重建它们。
但是,解决此问题的一种简单方法是使用其他工作树进行rebase。只需将环境变量GIT_WORK_TREE设置为其他工作树即可。只是不要忘记让你的HEAD与你的工作树相匹配。
根据他所在的分支以及推动的内容,推送到非裸仓可能是危险的。一个更好的解决方案是从珍贵的工作树中获取回购。 例如:
` orgbranch = $(git rev-parse HEAD)
mkdir / tmp / tmp_wd
cp -r!(。git)/ tmp / tmp_wd
导出GIT_WORK_TREE = / tmp / tmp_wd
git checkout branch1
git rebase master
git checkout $ orgbranch
导出GIT_WORK_TREE =
rm -rf / tmp / tmp_wd`