如何将一个大的 Git 分支拆分成许多较小的分支?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12540446/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I split up a large Git branch into lots of smaller branches?
提问by Michael Parker
I have imported from SVN into Git, now I have one big branch, like this:
我已经从 SVN 导入 Git,现在我有一个大分支,像这样:
- work on feature C
- work on feature B
- work on feature C
- work on feature C
- work on feature B
- work on feature A
- 处理功能 C
- 在功能 B 上工作
- 处理功能 C
- 处理功能 C
- 在功能 B 上工作
- 处理功能 A
I want separate feature branches, for A, B, C. I'm cherry picking commits to new branches but this doesn't remove them from the original branch so I have to manually track which ones I have pulled out.
我想要单独的功能分支,对于 A、B、C。我正在挑选对新分支的提交,但这不会将它们从原始分支中删除,所以我必须手动跟踪我已经拉出的那些分支。
There are around 800 commits to split up, and maybe 50 features/bugfixes.
大约有 800 个提交要拆分,可能还有 50 个功能/错误修复。
It would be nice to have the ones I have pulled out reflected this way somehow in the git log, so I know which ones I have already done. Is this possible?
让我提取的那些以某种方式反映在 git 日志中会很好,所以我知道我已经完成了哪些。这可能吗?
I can rebase the entire branch, skipping the commits I have pulled out, but I'm worried this will cause lots of conflicts. I don't want to resolve 500 conflicts every time I pull a commit out.
我可以重新设置整个分支,跳过我已经退出的提交,但我担心这会导致很多冲突。我不想在每次拉出提交时解决 500 个冲突。
What's the best method of pulling out commits from one uber branch onto smaller feature branches, whilst keeping track of your progress?
将提交从一个 uber 分支提取到较小的功能分支,同时跟踪您的进度的最佳方法是什么?
回答by willoller
What I do in this case is use interactive rebase.
在这种情况下,我所做的是使用交互式 rebase。
At your HEAD
, create your branches A
, B
, and C
. Also create a "backup" branch (you could name it backup
) in case things go wrong and you need your original HEAD
back.
在你的HEAD
,你创建分支A
,B
和C
。还要创建一个“备份”分支(您可以命名backup
),以防万一出现问题并且您需要原始HEAD
备份。
git branch feature-a
git branch feature-b
git branch feature-c
git-branch backup-before-rebase
Then, create a branch at the commit you want them to start from, maybe at a convenient stable commit. Call it new_trunk
or something.
然后,在您希望它们开始的提交处创建一个分支,也许在一个方便的稳定提交处。叫它new_trunk
什么的。
git checkout HEAD~50 ## this will be the new tree-trunk
git branch new_trunk
Then, do interactive rebase
s and pick out the commits you want to keep in that branch. Used this way, it's basically like cherry-pick
ing in bulk.
然后,执行交互式rebase
s 并选择要保留在该分支中的提交。这么用,基本就跟cherry-pick
ing一样了。
git checkout feature-a
git rebase -i new_trunk ## -i is for "Interactive"
When you're done, you should have 3 branches with separate histories starting from new_trunk
and a backup
branch reflecting the old HEAD
if you still need it.
完成后,您应该有 3 个具有不同历史记录new_trunk
的backup
分支,HEAD
如果您仍然需要它,则应该有一个反映旧的分支。
回答by Josef Cech
Personally I would really consider pros and cons of such large changes (once more if you've already done this). If you run into conflicts (which is in large rebase/cherry-pick annoying and hard-to-solve by itself) you will probably have tough times when merging features back to your "master" branch.
就我个人而言,我真的会考虑如此大的变化的利弊(如果你已经这样做了,再考虑一次)。如果遇到冲突(在大型 rebase/cherry-pick 中很烦人且本身难以解决),在将功能合并回“主”分支时可能会遇到困难。
Wouldn't be better/easier to freeze your big-branch, get it "done" (or "good enough") and make new feature-branches on it? (Or exclude only some branches?)
冻结你的大分支,让它“完成”(或“足够好”)并在其上创建新的功能分支不是更好/更容易吗?(或者只排除一些分支?)
But to your question:
但是对于你的问题:
If you want to track changes/missing commits automatically use git cherry command.
如果您想自动跟踪更改/丢失的提交,请使用 git cherry 命令。
git cherry featureBranch bigBranch
If there were no conflicts while cherrypicking or rebasing your feature branch you can use previous code with some additional pipes:
如果在挑选或重新设定功能分支时没有冲突,您可以使用带有一些附加管道的先前代码:
git cherry featureBranch bigBranch | awk '{ print "pick " }' | tee remaining
This will print (and save to file called "remaining") commits missing in featureBranch. You can add this to interactive rebase on bigBranch to throw away commits you don't want anymore. (Maybe you can script it even more with "ed" editor as git editor and passing commands to standard input of interactive rebase but I didn't tried it.)
这将打印(并保存到名为“剩余”的文件)在 featureBranch 中丢失的提交。您可以将此添加到 bigBranch 上的交互式 rebase 以丢弃您不再需要的提交。(也许您可以使用“ed”编辑器作为 git 编辑器编写更多脚本并将命令传递给交互式 rebase 的标准输入,但我没有尝试过。)
回答by r0hitsharma
Just to simplify willoller's answerfurther,
只是为了进一步简化威洛勒的回答,
make the feature branches, and backup, in case
制作功能分支,并备份,以防万一
git branch feature-a
git branch feature-b
git branch feature-c
git branch backup-before-rebase
then checkout a feature branch and do an interactive rebase from the commit you want them to start from
然后签出一个功能分支并从您希望它们开始的提交中进行交互式变基
git checkout feature-a
git rebase -i <safecommit>
enter code here
If you want some feature branches to share some commits to keep your tree clean, don't create the later feature branch at the start, but once you've got a rebased feature branch and then use the shared commit reference as your next safecommit
如果您希望某些功能分支共享一些提交以保持树清洁,请不要在开始时创建后面的功能分支,但是一旦您获得了重新定位的功能分支,然后使用共享的提交引用作为您的下一个安全提交
#on branch feature-a
git checkout -b feature-d
git rebase -i <sharedcommit>
回答by Michael Parker
Another method I have just found out about, is using "git notes".
我刚刚发现的另一种方法是使用“git notes”。
http://alblue.bandlem.com/2011/11/git-tip-of-week-git-notes.html
http://alblue.bandlem.com/2011/11/git-tip-of-week-git-notes.html
This feature allows adding comments to existing commits without actually changing the branch / requiring a rebase. One method of tracking which commits have been pulled out is to add a git note to each one:
此功能允许向现有提交添加注释,而无需实际更改分支/需要重新设置基准。跟踪哪些提交已被提取的一种方法是为每个提交添加一个 git 注释:
Cherry-picked to features\xyz 925a5239d4fbcf7ad7cd656020793f83275ef45b
Cherry-picked to features\xyz 925a5239d4fbcf7ad7cd656020793f83275ef45b
This could help in a largely manual process - you could write a little script to cherry pick a commit to a particular branch then add the relevant git note back to the original commit.
这在很大程度上有助于手动过程 - 您可以编写一个小脚本来挑选特定分支的提交,然后将相关的 git 注释添加回原始提交。
Alternatively, if you want to get really funky, you could automate the whole process, by:
或者,如果你想变得非常时髦,你可以通过以下方式自动化整个过程:
- Add a git note to every commit, saying which feature branch you want it cherry-picked to:
TOCHERRYPICK: features\xyz
- Write a script to scan all the git notes, and automatically create all the feature branches and cherry-pick the correct selected commits. It could then change the git note to
CHERRYPICKED: features\xxx at 925a5239d4fbcf7ad7cd656020793f83275ef45b
to allow the tool to be re-run later to pick out more commits. - If you are really keen to make it prominent when a commit has been cherry picked, you could also automate the creation of a tag with a similar name:
CHERRYPICKED:<branch>:SHA
- 为每个提交添加一个 git 注释,说明您希望它选择哪个功能分支:
TOCHERRYPICK: features\xyz
- 编写一个脚本来扫描所有 git 笔记,并自动创建所有功能分支并挑选正确的选定提交。然后它可以将 git note 更改
CHERRYPICKED: features\xxx at 925a5239d4fbcf7ad7cd656020793f83275ef45b
为允许稍后重新运行该工具以挑选更多提交。 - 如果你真的很想在一个提交被选中时让它突出,你也可以自动创建一个具有相似名称的标签:
CHERRYPICKED:<branch>:SHA
回答by Ivan
I honestly wouldn't do this unless you have a huge list of commits that need to split up and they are very independent features, i.e. not altering the same line where there would be conflicts to resolve.
老实说,除非您有大量需要拆分的提交列表并且它们是非常独立的功能,否则我不会这样做,即在需要解决冲突的地方不更改同一行。
As others have suggested, create a new branch for each feature and use git rebase --interactive
to include the desired commits.
正如其他人所建议的那样,为每个功能创建一个新分支并用于git rebase --interactive
包含所需的提交。
To ensure none go astray, create the contents of the git-rebase-todo
files by
为确保不会误入歧途,请通过以下方式创建git-rebase-todo
文件的内容
- editing a list of all the desired commits and classifying them by feature
- separating the list of commits into separate files
- 编辑所有所需提交的列表并按功能对它们进行分类
- 将提交列表分成单独的文件
You can create the list of commits by using a command like
您可以使用类似的命令创建提交列表
git log --oneline --reverse 44e19^... > log.txt
to display commit 44e19 onwards. This will give you a file like this
显示提交 44e19 以后。这会给你一个这样的文件
44e1936 dSRGratuities (SummaryRecord)
67fedda Receipt Report HEADER: 20! multiply by Paym_FieldCount
69d70e2 Receipt Report: Payment
....
which when edited (to add classification: feature a,b,c etc) might look like my sorted.txt
编辑后(添加分类:特征 a、b、c 等)可能看起来像我的 sorted.txt
c 44e1936 dSRGratuities (SummaryRecord)
a 67fedda Receipt Report HEADER: 20! multiply by Paym_FieldCount
b 69d70e2 Receipt Report: Payment
c abea7db Receipt Report: Cashback
a cf96185 Receipt Report: Gratuity
c 70e987a Receipt Report: use amount tendered for printing
a 7722ac8 Receipt Report: use amount tendered for calculations
c 47f1754 Receipt Report: store amount tendered
b b69a73f Receipt Report: Use enum Paym_FieldCount
a 9a0b471 Receipt Report HEADER: enum PaymentEntries (with Paym_FieldCount)
c ad67e79 Use SharpReport enum
b 3c510c6 enum SharpReport
a e470e07 m_Gratuities m_dSSGratuities (SalesSummary)
b 4e0c3e4 m_Gratuities m_szGratuities (SalesSummaryRecord)
b bd054f7 _gx_fn_Cashback
Then script in your favorite scripting language to turn the sorted list into a collection of git-rebase-todo
files. Your script might resemble the one I just wrote.
然后使用您最喜欢的脚本语言编写脚本,将排序后的列表转换为git-rebase-todo
文件集合。您的脚本可能类似于我刚刚编写的脚本。
foreachline text sorted.txt {
set fields [split $text { }]
set branch [lindex $fields 0]
set commit [lindex $fields 1]
set comment [string range $text 10 end]
set command "echo pick $commit $comment"
exec cmd /S /C $command >> $branch.txt
}
The script reads the commit sorting file line by line and splits by a space character { } to get the two fields branch
and commit
, and takes a substring (characters 10 onwards) for a description of the commit. The description isn't required but it's useful for us humans to check for mistakes.
该脚本逐行读取提交排序文件,并用空格字符 { } 拆分以获得两个字段branch
and commit
,并采用子字符串(字符 10 起)来描述提交。描述不是必需的,但它对我们人类检查错误很有用。
It then puts a line into the appropriate git-rebase-todo
file, creating one file per feature. I hacked this by executing a very ugly Windows echo string >> file
command.
然后将一行放入适当的git-rebase-todo
文件中,为每个特征创建一个文件。我通过执行一个非常丑陋的 Windowsecho string >> file
命令来破解它。
This creates a number of files, e.g. my file a.txt
这会创建许多文件,例如我的文件 a.txt
pick 67fedda Receipt Report HEADER: 20! multiply by Paym_FieldCount
pick cf96185 Receipt Report: Gratuity
pick 7722ac8 Receipt Report: use amount tendered for calculations
pick 9a0b471 Receipt Report HEADER: enum PaymentEntries (with Paym_FieldCount)
pick e470e07 m_Gratuities m_dSSGratuities (SalesSummary)
The whole thing is ugly. I don't recommend it unless you have to do it and are good at writing scripts.
整件事都很丑陋。我不推荐它,除非您必须这样做并且擅长编写脚本。
I wrote the text above some time ago, and I have had a little rethink about things. Above I implied that this is a lot of work and not worth doing, but I have since seen situations where it looks like someone has done the above and it has been very worthwhile.
前段时间写了上面的文字,对事情有了一些反思。上面我暗示这是很多工作,不值得做,但是我看到了看起来有人做了上面的事情并且非常值得的情况。
I remember releases of Visual Studio for MFC/C++ where each new release would have compiler changes, IDE changes, MFC improvements, and run on a later version of Windows. This meant that if you wanted to get your compiler away from VS6 and Windows XP you might have to make language changes to satisfy the compiler, and function call changes to satisfy MFC, etc.
我记得 Visual Studio for MFC/C++ 的版本,其中每个新版本都会有编译器更改、IDE 更改、MFC 改进,并在更高版本的 Windows 上运行。这意味着如果您想让编译器远离 VS6 和 Windows XP,您可能需要更改语言以满足编译器的要求,并更改函数调用以满足 MFC 等要求。
Now suppose that Microsoft had taken weekly backups as they developed Visual Studio, and someone methodically took the old backups and committed the code changes into a version control system like Git. Then they started classifying the changes ...
现在假设微软在开发 Visual Studio 时每周进行一次备份,有人有条不紊地进行旧备份并将代码更改提交到像 Git 这样的版本控制系统中。然后他们开始对变化进行分类......
- a. = compiler changes
- b. = library changes
- c. = IDE changes
d. = security improvements
etc.
- 一种。= 编译器更改
- 湾 = 库更改
- C。= IDE 更改
d. = 安全改进
等等。
Microsoft could create branches for each of these, and start to have the latest and greatest IDE (c
included), running on the newest Windows and still capable of compiling old legacy programs using the language (no a
) and libraries (no b
) they were written for.
Microsoft 可以为其中的每一个创建分支,并开始拥有最新和最好的 IDE(c
包括),在最新的 Windows 上运行,并且仍然能够使用它们编写的语言(否a
)和库(否b
)编译旧的遗留程序.
Developers previously locked into legacy software could then make improvements in a logical and incremental fashion, e.g. language changes and library changes independently of each other, and do it on the latest and greatest Visual Studio without having to pass through all the intermediate versions.
以前锁定在遗留软件中的开发人员然后可以以逻辑和增量方式进行改进,例如相互独立的语言更改和库更改,并在最新和最好的 Visual Studio 上进行,而无需通过所有中间版本。
Example
例子
<LanguageStandard>stdcpp14</LanguageStandard>
Now I am not saying this is what has happened, but it seems to me that the recent versions of Visual Studio are far better at allowing legacy programs to be updated, rather than thrown away and (never?) rewritten, and it appears to me to be due to version controlling and organizing old software changes into logical branches: compiler versions, DLL/library versions.
现在我并不是说这就是发生的事情,但在我看来,最近版本的 Visual Studio 在允许更新遗留程序方面要好得多,而不是扔掉和(从不?)重写,在我看来由于版本控制和将旧软件更改组织到逻辑分支中:编译器版本、DLL/库版本。
So, I can see occasions where splitting a huge number of old commits into distinct branches may be worthwhile.
因此,我可以看到将大量旧提交拆分为不同分支的情况可能是值得的。
On Visual Studio 2019 I can add the line
在 Visual Studio 2019 上,我可以添加行
<PlatformToolset>v141_xp</PlatformToolset>
to a configuration file and manage to compile and run an old windows program which failed to compile and link with VS 2015, and VS 2017. It looks very much like someone at Microsoft has rebased performance and security improvements onto some old software while leaving out the breaking changes
that often come with modernization.
到一个配置文件并设法编译和运行一个旧的 Windows 程序,该程序无法编译并与 VS 2015 和 VS 2017 链接。看起来很像微软的某个人已经将性能和安全改进重新定位到一些旧软件上,同时忽略了breaking changes
这通常伴随着现代化。