我们是如何构建撤消的

2020-05-27 22:40:39

Mac上的Tower 4.0引入了一个特殊功能:Undo现在可以在我们的Git客户端中使用!鉴于我们远不是第一个实现撤销的应用程序,您可能会问自己,能够在塔中点击CMD+Z有什么特别之处?鉴于Tower中的操作可以在引擎盖下执行相当复杂的Git操作,该功能有比看上去更多的东西。在保证用户的工作和数据安全的同时,使撤消操作感觉直观,这方面做了大量的工作。在这篇文章中,我们将带您看一看Tower开发的幕后,描述撤消功能是如何构思和实现的。

正如前面提到的,撤销功能的想法并不新鲜,而且这个想法确实存在于我们的路线图上很长一段时间了。在转移到基于订阅的模式之前,我们试图使大功能与主要版本保持一致,最初考虑将Undo用于Tower 3,但没有实现。如今,更大的功能并不依赖于主要版本,但是撤销功能在范围和实现时间上对我们来说仍然有点不寻常。我们通常会尝试在较短的冲刺时间内添加内容,但是撤销功能的开发需要很长一段时间。我们也想提供一个完整的、完美的功能,而不是逐件发布。

对于Git中的一些动作,撤销后的预期结果是显而易见的,反转动作也很容易。对于其他人来说,结果不太清楚,实施也很复杂。从一开始,这个功能的工作就包括实现显而易见的案例,在现实世界的场景中尝试它们,看看它们是否感觉和工作像预期的那样,而对更复杂的案例进行迭代,以确保它们是健壮的,感觉和简单的案例一样自然和直观。

撤消功能的工作断断续续地进行-虽然该功能的第一次提交发生在2017年3月,但大部分开发都是后来的,而在此期间,其他工作占据了优先地位。我们的首席技术官Alex和Mac开发人员Heiko从概念验证开始,为一些在撤消后所需状态清晰而简单的操作实现撤消-例如,撤消删除分支的操作。在Tower中,Git操作作为离散操作的实现允许我们一次撤消一个案例,为一个操作实现和调整它,而不影响另一个操作。该功能实际上是随Tower一起提供的,但通过开发者标志对最终用户禁用。这使得我们可以在很长一段时间内在自己的日常工作中使用和测试该功能。

能够在我们的日常工作中测试该功能是有用的,原因有几个。首先,我们想知道这个功能是否真的有用--我们真的会使用它吗?此外,我们必须为每一项行动找到最佳时机:什么感觉是对的,什么是错的,有没有什么是我们期望能够撤销的,但是没有呢?如前所述,在某些情况下,决定正确的结果和实现撤消相对简单,而在其他情况下,则明显不那么简单。

随着塔的其他方面的工作的进行,撤消也在形成。一般来说,人们似乎低估了即将到来的工作的复杂性,在这种情况下也是如此:虽然我们知道这项功能会带来挑战,但也许我们并没有确切地意识到它会有多大的挑战性。用霍夫施塔特定律的话说,即使考虑到霍夫施塔特定律,它的时间也总是比你预期的要长。

将一项功能作为噱头来实现,与实际让其感觉自然、直观和坚实之间存在很大差距。在Tower中,操作相对于UI异步发生,因此我们马上面临着确保撤消功能可以处理这一点的挑战-例如,重复撤消和重做某些操作不会导致某些中断或数据丢失。

为了撤消一些更复杂的操作,我们必须为用户执行的每个步骤保存额外的数据,以便在用户决定撤消的情况下拥有返回到先前状态所需的所有信息。这方面的一个示例是涉及工作树的一些操作。工作树很复杂,文件有许多不同的潜在状态:文件可以修改、暂存,它们可能会有合并冲突,等等。

作为一个具体的例子,假设您重新将一个分支重新设置为另一个分支的基础。当您的提交在目标分支上重放时,在其中一个过程中,您会在多个文件中遇到合并冲突。此时,Git(和Tower)将停止,让您解决合并冲突。这里有几个有趣的撤销用例。用户可能会解决几个有合并冲突的文件,结果发现其中一个文件没有以正确的方式解决,然后决定撤消以返回到解决该文件。此外,用户可能会解决该提交的合并冲突,并中止或继续执行重新基址操作,而只想撤消并返回到上一次重新基址步骤。

这两个场景的解决方案都涉及为工作树实现快照。当用户对存储库执行操作时,这些快照会捕获每个步骤中的工作树状态,并允许Tower在撤消时进行备份。在撤消单个文件的已解决合并冲突的情况下,撤消实质上只意味着返回到上一个快照。当继续或中止重设基地然后撤消时,在Git中实际上没有办法返回到以前的状态,这里,Tower有一种机制可以在重设基地时重放先前采取的步骤(使用快照),以便在撤消之前返回到状态。对于如何实现这一点,有几种选择--我们可以重用用户执行REBASE序列时创建的提交,并在撤消后将状态恢复到实际的提交散列,但这意味着跟踪内部文件中Git保存的REBASE的状态。这是我们要避免的,因为这是一个实现细节,可以更改-Tower只使用终端用户可用的相同Git命令。使用快照和重放这些内容的解决方案不会产生相同的提交散列,但是这些提交的内容是相同的。

在恢复某些以前的状态时,还有另一种情况需要牢记:用户可以在Tower中执行操作,然后在命令行上使用存储库,然后返回Tower并尝试撤消在此处完成的最后一个操作。这可能是一种不太可能的情况,但仍可能不会导致中断或数据丢失。在上述类型的工作流中,这是通过使用补丁程序实现快照的事实来处理的-如果存在冲突更改,补丁程序将根本不会应用。

正如前面提到的,在开发这个功能和其他功能的过程中,Alex和Heiko能够测试和开发Undo功能,确保它按预期执行。撤销功能(希望)不是你在Tower中每十分钟使用一次的功能,但当你使用它时,它给人的感觉是自然和健壮的,这一点非常重要。为此,真实世界的测试和迭代是关键--该功能的关键部分是单元测试的,但是很难编写测试来测试一个功能的使用感觉有多直观。

我们发现这个特性确实派上了用场--Alex回忆了错误提交的瞬间,他意识到,他不需要去查找、删除并创建一个新的标记,只需按下CMD+Z即可。当工作树的快照在更复杂的流程中实现和使用时,这些也就到位了。

2019年12月,该功能在Tower测试版中击中了现实世界的测试者。这一过程相当平淡无奇--发现了一些实际上起源于代码其他部分的错误,但通过撤销暴露了这些错误。2020年2月初,这一功能在Tower 4.0中发布-从反馈来看,这让许多用户感到高兴。从用户的角度看,撤销是一个如此简单的操作,但是可以对底层的Git存储库产生如此广泛的影响,所有这些结合在一起的方式使其使用起来非常令人满意。

例如,我们发现能够从合并错误的分支、进行错误的提交或删除错误的文件中快速返回,这在我们的日常工作中很有用。当然,我们在内部使用Tower,而开发Tower需要大量摆弄Git存储库来测试它-在这些场景中,该功能确实非常方便!总而言之,我们对这一功能非常满意,也很高兴我们的用户似乎也喜欢它!

虽然我对git cli很在行,但我确实使用Tower来进行合并和合并等。今天我使用撤销功能回滚了一个本地comit,它可以无缝地工作。令人印象深刻。总比摆弄reflog要好。

塔楼刚刚增加了一个多么棒的功能啊!您现在可以CMD-Z一些相当复杂的GIT工作流。🤯我知道很多人都信奉开源或基于编辑器的GIT客户端(即。免费)但是塔楼对我来说每一分钱都是物有所值的。

在我们关于Tower的工作中,我们不断努力让我们的用户更有效率,以最简单、最直观的方式提供对Git强大功能的访问。此外,像Tower这样的图形用户界面的好处之一是可发现性-您可能会遇到您不知道存在的功能。命令行非常强大,有它的好处,但可发现性不一定是其中之一。

撤消功能提供了如何实现此愿景的完美示例。根据上下文的不同,简单的撤消操作可能会导致对底层Git存储库执行各种操作。该功能对初学者和Git老手都很有用,而且尽可能容易发现。您可以不用停下来考虑幕后发生的事情就可以使用它,并且希望您每次都能发现它做了您期望的事情!

撤销的故事有一个很好的结尾。当Alex和Heiko开始考虑在Tower中启用部分隐藏时,他们认为这应该很容易,因为Git从2.13.2开始支持部分隐藏。然而,事实证明,Git在这起案件中的行为并不完全是我们预期和希望的。然而,撤消功能的快照工作允许我们解决这个问题,实现了一个功能,如果不是早期的撤消工作,这个功能可能就不会发生!。在4.3号塔楼释放了部分藏匿物品。

第一时间了解塔台博客的新内容,以及通过电子邮件赠送和赠品。

加入100,000多名开发人员和设计师的行列,第一时间了解塔楼博客的新内容,以及通过电子邮件赠送和赠品。

我已阅读并接受隐私政策。我明白我可以随时通过点击任何电子邮件中的取消订阅链接来取消订阅。