我们使用 git 来管理我们应用的代码仓库,有一个我还没有遇到过的情况,但我想是很常见的。
我们想暂时删除一个功能,然后在将来的某个时候重新添加它。我试图想象支持这一点的分支结构,或者我们是否应该做一些简单的事情,比如从代码中删除该功能,并在准备重新添加它时,从提交历史中重新创建它。
谁能指出我处理这种情况的正确方向?
我们使用 git 来管理我们应用的代码仓库,有一个我还没有遇到过的情况,但我想是很常见的。
我们想暂时删除一个功能,然后在将来的某个时候重新添加它。我试图想象支持这一点的分支结构,或者我们是否应该做一些简单的事情,比如从代码中删除该功能,并在准备重新添加它时,从提交历史中重新创建它。
谁能指出我处理这种情况的正确方向?
您可以暂时删除在 Git 存储库历史之前的功能的一种方法是进行提交以删除该功能。然后,当您想重新添加该功能时,只需还原将其取出的提交。这将做一个反向补丁,这意味着它将反向应用更改,这将有效地重新添加该功能:
git revert <sha of commit that removed the feature>
如果您想确保以后可以轻松地重新添加该功能,同时使其与对代码的更改保持同步,您可以在删除它后立即创建一个单独的功能分支,然后只处理该分支像任何其他功能分支一样,并通过频繁地对其master
(或develop
分支,如果这是您想要的方式)重新定位来保持同步,随时解决冲突。
所以基本上,你会想做这样的事情(如果你使用GitHub Flow或Git Flow分支策略并不重要,两者都使用最终合并到主线的功能分支的概念开发。为简单起见,我将在此示例中使用 GitHub Flow):
# On master branch
git commit -m "Remove feature X" # Creates commit 1234567...
# Now make feature branch
git checkout -b saved-feature
# Immediately put the feature back in the feature branch
git revert 1234567
# When you want to sync branch with master, just use rebase.
# Rebase allows you to sync frequently, since it doesn't
# leave behind a bunch of merge commits.
#
# From the feature branch:
git rebase master # Resolve any conflicts as needed.
# N commits later, you decide it's time to merge the feature
# back in. You can use fast-forward or non-fast-forward merge,
# it's up to you.
#
# Using fast-forward merge with master checked out (assuming
# feature branch was just rebased onto master):
git merge saved-feature
# Or forcing a merge commit, if that's what you want:
git merge --no-ff saved-feature
假设您saved-feature
经常与master
(或者develop
,如果您使用的是)保持同步,并在进行中解决冲突,那么重新合并该功能应该没有问题。
参考文档:
这是一个应该奏效的策略。听起来你的工作已经融入了你的项目,所以这就是我要做的。首先选择你的起点,对我来说通常是dev
分支(假设还有一个master branch
)。分拆新分支,该分支将是从您的项目中删除的功能
git checkout -b dev_feature_removed
同时旋转一个分支,该分支将是项目中维护的那个特性。
git checkout -b dev_feature_sustained
现在进行编码和测试,您需要确保此功能已正确且完全删除dev_feature_removed
,一旦您确定是这种情况,将该分支重新合并到生产环境中。在我的情况下,开发以进行进一步测试,然后进入 master 上线。
同时,您也可以将其他分支保留dev_feature_sustained
在您的仓库中。您可以将 dev 合并到此分支中以使其保持同步,还可以添加到已删除的功能(错误修复或新的花里胡哨)中,以便通过将其合并回 dev(在我的情况下可能是您的主)。
此功能的返回可能会导致合并冲突,具体取决于您的功能的紧密耦合程度。由于你的早于回购,听起来你无论如何都会产生冲突,因为合并策略只能做这么多。但是,由于您将拥有两棵完整的提交树,一棵带有该功能,另一棵没有,您将知道您的功能重新连接到您的项目的每个点的存在。因此,您将拥有将其放回项目中所需的一切。这就是我将在我的情况下起草的内容。祝你好运,伙计。
这是一个具体示例,显示了John Galt 回答中的策略:
$ git log --graph --decorate --oneline
* d1d201b (HEAD, restore-b) Merge branch 'prod' into restore-b
|\
| * 18d759f (prod) add feature e in prod
* | 191037e Merge branch 'prod' into restore-b
|\ \
| |/
| * e0de1be add feature d in production
* | a122936 Revert "remove feature b in production"
|/
* d3e2c42 remove feature b in production
* 5369ecf existing three features
基本上,restore-b
总是包含所有内容prod
以及功能的恢复(提交a122936
)。当您在 上进行新提交时prod
,它们会被合并到restore-b
中,因此无论何时您准备好恢复该功能,它都是一个简单的快速合并。
一种更简单的方法是避免创建a112936
提交和restore-b
分支,直到您准备好恢复该功能。创建和更新restore-b
分支的好处是,与其他更改的任何冲突都可以及时解决(希望在编写冲突代码后不久由编写冲突代码的开发人员解决)。这使功能保持“新鲜”和“现成”,准备好包含在生产版本中,而无需额外的开发工作。
我会在代码本身中解决这个问题。添加一个功能映射(基本上是每个功能的布尔标志),然后根据需要启用/禁用功能,而无需实际从存储库中删除代码/逻辑。
配置文件中的内容很简单,例如:
<?php
$features = array(
'news' => true,
'events' => true,
'shop' => false
);
然后在您相应的控制器中:
<?php
class ShopController extends AbstractController {
public function __construct() {
// $features array would be passed in somehow;
// maybe via a dependency injection container
if (!$features['shop']) {
// feature is disabled, so just send 404 page for now
throw new ResourceNotFoundException();
}
}
}
注意:以上是半伪代码。