将Git嵌入企业开发系统中


知道并知道如何使用git的开发人员最近增长了一个数量级。 您习惯了命令执行的速度。 您习惯了分支机构的便利和更改的轻松回滚。 解决冲突是如此普遍,以至于程序员习惯于英勇地解决不应该解决的冲突。


Directum的团队正在开发用于平台解决方案的开发工具。 如果您看到1C,那么您可以粗略地想象一下我们“客户”(应用程序开发人员)的工作环境。 应用程序开发人员使用此开发工具为客户创建应用程序解决方案。


我们的团队面临着简化申请人生活的任务。 我们为Visual Studio,ReSharper和IDEA的现代芯片所宠爱。 申请人要求我们将git开箱即用地集成到该工具中。


这就是困难。 在每种类型的实体(合同,报告,目录,模块)的工具中,都可能存在锁定。 一位开发人员开始编辑实体类型并阻止它,直到完成更改并将其提交到服务器。 此时,其他开发人员将相同类型的实体视为只读。 该开发使人想起了在SVN中工作或在多个用户之间通过邮件发送Word文档的过程。 我一次想要所有东西,但也许只有一个。


每种类型的实体可以具有许多处理程序(打开文档,在保存之前进行验证,写入数据库),您需要在其中编写与实体的特定实例配合使用的代码。 例如,锁定按钮,向用户显示消息或为表演者创建新任务。 该平台提供的API框架内的所有代码。 处理程序是其中包含许多方法的类。 当两个人需要用代码修复同一文件时,则无法执行此操作,因为平台会阻止整个实体类型以及相关代码。


我们的从业人员一路过关斩将。 他们悄悄地给自己分了一份我们开发环境的“非法”副本,注释掉了阻塞部分,并将我们的提交合并到了自己身上。 应用程序代码保存在git下,通过第三方工具(git bash,SourceTree和其他工具)提交。 我们得出结论:


  1. 我们的团队低估了应用程序开发人员适应该平台的意愿。 巨大的尊重和荣誉!
  2. 他们提出的解决方案不适合生产。 有了git,一个人的手就可以解开,并且他可以创建任何东西。 要支持所有的多样性将是愚蠢的,不要劫持。 另外,有必要教育平台客户。 记录平台的所有git命令都会使文档团队发疯。


您想从Git获得什么


因此,用git out放弃生产是不好的。 我们决定以某种方式封装主要操作的逻辑并限制其数量。 至少对于第一个版本。 尽可能减少了参赛队伍,并保持:


  • 状态
  • 提交
  • 重设--hard到HEAD
  • 重置为最后的“服务器”提交

对于第一个发行版,他们决定拒绝与分支机构合作。 并不是说这很困难,只是团队没有满足时间资源。


我们的合作伙伴会定期发送他们的应用程序开发并询问:“某些事情对我们不起作用。我们在做错什么?”。 在这种情况下,应用程序会使用其他人的开发来加载自身并查看代码。 过去这样工作:


  1. 开发人员随开发人员一起删除了归档文件;
  2. 在配置中更改了本地数据库;
  3. 把别人的发展倒在自己的基础上;
  4. 调试,发现错误;
  5. 提出的建议;
  6. 他把自己的发展归还了

新方法不适合旧方法。 我不得不砸头。 该团队提出了两种解决此问题的方法:


  1. 将所有开发内容存储在一个git存储库中。 如有必要,请与其他人共同决定创建一个临时分支。
  2. 将不同团队的开发存储在不同的存储库中。 将加载到环境中的文件夹的设置移到配置文件中。

我们决定走第二条路。 第一个似乎很难实现,而且通过分支切换更容易使自己陷入困境。


但是第二个也不甜。 上述命令不仅应在同一个存储库中运行,而且应同时运行多个。 来自不同存储库的实体类型是否发生变化? 我们在一个窗口中显示它们。 对于应用程序开发人员来说,这更加方便和透明。 通过按下提交按钮,该工具会将更改提交到每个存储库。 因此,“在幕后”的拉/推/复位命令适用于物理上不同的存储库。



Libgit2sharp


要使用git,我们从两个选项中进行选择:


  1. 使用系统上安装的git,将其拉到Process.Start并解析输出。
  2. 使用libgit2sharp,它可以通过pinvoke拉到libgit2库。

在我们看来,使用现成的库是一个合理的解决方案。 是徒劳的。 我稍后再告诉您原因。 最初,图书馆为我们提供了快速推出可运行原型的机会。


第一次开发迭代


大约一个月就可以实施。 实际上,拧紧git很快,而且大多数时候我们试图修复已经打开的伤口,因为我们已经切断了存储源文件的旧机制。 git status返回的所有内容都返回到接口。 单击每个文件将显示差异。 它看起来像一个git gui界面。


二次开发迭代


第一种选择是提供过多信息。 对于每种类型的实体,一次都会关联许多文件。 这些文件产生了噪音,并且不清楚哪些类型的实体发生了变化以及究竟发生了什么变化。


按实体类型对文件进行分组。 每个文件都有一个易于理解的名称,与GUI中的名称相同。 实体类型元数据在JSON中进行了描述。 它们还需要以人类可读的格式呈现。 使用jsondiffpatch库开始分析json版本“之前”和“之后”的变化,然后他们编写了自己的JSON比较实现(以下称为JSONdiff)。 我们通过产生人类可读记录的分析器来运行比较结果。 从视图中隐藏了许多文件,从而在更改树中保留了一个简单的条目。


最终结果如下:



libgit2遇到问题


Libgit2产生了大量意外的惊喜。 与某些人打交道超出了合理时间的能力。 我会告诉你我所记得的。


某些标准操作会导致意外和难以复制。 “本机库未提供任何错误”告诉​​我们包装器。 太好了 您正在诅咒,正在调试中重建本机库,正在重复以前删除的案例,但在调试模式下不会崩溃。 在发行版本中进行重建,然后再次下降。


如果第三方工具(例如SourceTree)与libgit2sharp并行运行,则commit可能不会提交某些文件。 或在某些文件上显示差异时冻结。 一旦尝试调试,就无法复制。


在我们的一个应用程序中, git status的实现耗时40秒。 四十卡尔! 同时,从控制台启动的git可以正常运行一秒钟。 我花了几天的时间进行整理。 搜索更改时,Libgit2会查看文件夹的文件属性,并将其与索引中的条目进行比较。 如果修改时间不同,则说明文件夹中的内容已更改,您需要查看内部和/或查看文件。 如果一切都没有改变,那么您不应该爬进去。 显然,控制台git中也进行了这种优化。 我不知道是什么原因,但只是git索引中的一个人mtime发生了变化。 因此,git每次都会检查存储库中所有文件的内容是否有更改。


在发布时,我们的团队fetch + rebase + autostash了应用程序团队的意愿,并用fetch + rebase + autostash代替了git pull 。 然后,我们发现了许多错误,包括“本机库没有提供错误”。


状态,提取和重新设置工作的时间比调用控制台命令的时间长得多。


自动合并


开发中的文件分为两种类型:


  1. 应用程序在开发工具中看到的文件。 例如,代码,图像,资源。 这些文件需要像git一样合并。
  2. 开发环境创建的JSON文件,但是应用程序开发人员只能以GUI的形式看到它们。 他们需要自动解决冲突。
  3. 使用开发工具时会自动重新创建的生成文件。 这些文件不会进入存储库,该工具会立即小心地放置.gitignore。

通过一种新方法,两个不同的施加器能够更改相同类型的实体。


例如,Sasha将更改有关如何在数据库中存储实体类型的信息并为save事件编写处理程序,而Sergey将为实体的表示样式。 从git的角度来看,这不会是冲突,并且两个更改都将合并而不会带来复杂性。


然后,Sasha更改了Property1属性并为其设置了处理程序。 Sergey创建了Property2属性并设置了处理程序。 如果从上面看情况,尽管从git的角度来看,相同的文件也会受到影响,但它们的更改不会冲突。
我希望该工具能够自行解决这种情况。


在发生冲突时合并两个JSON的示例算法:


  1. 从JSON基本git下载。


  2. 从我们的JSON gita下载。


  3. 从git下载他们的JSON。


  4. 使用jsondiff,我们形成base->我们的软件补丁并应用于它们。 生成的JSON称为P1。


  5. 使用jsondiff,我们形成base->他们的软件补丁并应用于我们的补丁。 生成的JSON称为P2。


  6. 理想情况下,在应用补丁P1 === P2之后。 如果是这样,则将P1写入磁盘。


  7. 在不完美的情况下(当确实存在冲突时),我们建议用户在P1和P2之间进行选择,并具有用双手完成的能力。 我们将选择内容写入磁盘。

合并后,我们检查是否达到没有验证错误的状态。 如果您尚未到达,请取消合并并要求用户重复。 这不是最佳解决方案,但至少可以保证第二次或第三次合并不会发生令人不愉快的后果。


总结


  1. 屠夫很高兴他们可以合法使用。
  2. git的引入加快了开发速度。
  3. 自动合并通常看起来很神奇。
  4. 我们将对libgit2的未来拒绝放在了一起,以调用git进程。

Source: https://habr.com/ru/post/zh-CN412787/


All Articles