使所有 Visual Studio 项目与库保持同步
场景
我有一个包含项目 A、B 和 C 的库。
我有两个解决方案。解决方案 1 包含项目 A 的副本,解决方案 2 包含项目 A 和 B 的副本。
当我构建解决方案 1 时,应该发生以下情况:
当我构建解决方案 2 时,应该发生以下情况:
我该如何做到这一点?
我可以使用版本控制系统或现成的文件同步软件来实现自动化吗?或者我需要推出自己的解决方案吗?
如果我确实构建了自己的解决方案,我对它如何工作有一些想法,但我很感激您可能拥有的任何输入:
可能是一个简单的控制台应用程序,带有用于指定“源解决方案”的命令行开关”,例如:
c:\Program Files\Library Syncronizer\LibSync.exe /solution:解决方案 1
用于注册包含库项目的活动解决方案的 XML 文件。可能的格式:
<前><代码><解决方案> <解决方案> <名称>解决方案1 <路径>c:\...\Projects\解决方案 1 <解决方案> <名称>解决方案2 <路径>c:\...\Projects\解决方案 2程序将执行以下操作:
- 读入源解决方案
- 确定其拥有哪些库项目
- 将这些项目的文件夹复制到库
- 循环遍历 XML 文件中的每个解决方案(源代码除外)
- 根据需要复制所有库项目的文件夹
- 也许还可能发生一些备份操作,以防同步过程覆盖重要的内容。
这听起来在概念上相对简单,但这可能会产生我没有想到的严重的意外后果。如果确实如此,希望有人会警告我:)
更新 - 我复制项目文件夹的动机是什么?
一句话 - 版本控制。
如果我将库项目保存在单独的文件夹中,并且仅在我的各种解决方案中链接到它们(而不是在我的解决方案文件夹中实际找到这些文件夹),我的版本控制存储库最终将不包含我的库项目的源代码。因此,如果我更新到“三个版本前”,并且我需要对我的库方法之一进行细微更改,则代码不存在。
我的解决方法是在我的库存储库中的修订版本中添加标签,其中包含“解决方案 1 - 版本 2.5.3”之类的内容,但这非常笨重。如果我正在处理解决方案 1 的“三个版本之前”和解决方案 2 的当前版本,事情会变得非常尴尬。现在,解决方案 2 将指向库项目的旧版本,这使得它可能无法使用并测试,直到完成旧版本的解决方案 1 的工作。
如果我使用副本,所有解决方案都将在其存储库中包含库源代码,并且我可以随时轻松返回到它到。
我应该在这里指出,我一直在使用 Tortoise HG (Mercurial) 进行版本控制。
无论如何,我愿意接受解决这个问题的任何方案。它不必涉及复制项目文件夹——这只是我能想到的确保我的所有版本控制存储库都是完整的、独立的包的唯一方法。
更新 2
首先,请注意。我使用 Mercurial (TortoiseHG) 进行版本控制,而不是 SVN。如果绝对必要的话我可以改变,但我真的更喜欢 Mercurial。
根据迄今为止的回复,我决定放弃“双向复制”的想法,并返回参考我的图书馆项目。这是一个新图表:
然而,我仍然有相同的目标:
- 每个解决方案的最新版本都使用最新的库代码
- 每个存储库一个解决方案/应用程序
- 每个存储库包含所有源代码,包括库项目
- 一切都尽可能自动化最大程度地减少错误风险
目标 #1 是通过引用库项目而不是使用副本来自动实现的,目标 #2 只是我如何设置存储库的问题,但目标 #3 和目标 #4 仍然难以捉摸。
对于 Mercurial,有一个 子存储库功能 似乎可以处理我的情况,但是正如文档所示,这仍然被认为是实验性的/有风险的。
目前,我认为一个好的解决方法可能是将库项目的备份副本存储在我的解决方案文件夹中。当我说“备份副本”时,我指的是字面意思。我仍然会引用库项目——这些副本只是为了确保所有源代码最终都在我的存储库中(目标#3)。为了满足目标 4,可以使用工作室中的构建后事件来自动执行这些备份。
我欢迎您对此的任何想法。
Scenario
I have a Library that contains Projects A, B, and C.
I have two solutions. Solution 1 includes a copy of Project A, and Solution 2 includes a copy of Projects A and B.
When I build Solution 1, here's what should happen:
When I build Solution 2, here's what should happen:
How can I do this?
Is this something I could automate with a version control system or off-the-shelf file syncing software? Or do I need to roll my own solution?
If I do build my own solution, I have some thoughts on how it could work, but I'd appreciate any input you may have:
Could be a simple console app with a command-line switch for specifying the "source solution", for example:
c:\Program Files\Library Syncronizer\LibSync.exe /solution:Solution 1
XML file used to register active solutions that contain library projects. Possible format:
<solutions> <solution> <name>Solution1</name> <path>c:\...\Projects\Solution 1</path> </solution> <solution> <name>Solution2</name> <path>c:\...\Projects\Solution 2</path> </solution> <!-- more solutions --> </solutions>
Program would do the following:
- Read in source solution
- Determine what library projects it has
- Copy the folders for those projects to the library
- Loop through each solution in the XML file (except the source)
- Copy the folders for all library projects as necessary
- Perhaps some backup operation could also occur, just in case the syncing process overwrites something important.
This sounds relatively simple in concept, but this may have serious unintended consequences I'm not thinking of. Hopefully someone will warn me if it does :)
Update - What is my motivation for copying project folders?
In a word - Version Control.
If I keep the library projects in a separate folder and only link to them in my various solutions (rather than physically locate the folders in my solution folders), my version control repository ends up not containing the source code to my library projects. So, if I update to "three version ago", and I need to make a minor change to one of my library methods, the code is not there.
My workaround for this has been to add tags to the revisions in my library's repository that say things like "Solution 1 - Version 2.5.3", but this is pretty clunky. And things get really awkward if I'm working on "three version ago of" of Solution 1 and the current version of Solution 2. Now, Solution 2 will be pointing to an old version of the library projects, which makes it potentially impossible to work with and test until I'm done working on the old version of Solution 1.
If I were working with copies instead, all solutions would contain the library source code in their repositories, and I could go back to it easily any time I need to.
I should note here that I've been using Tortoise HG (Mercurial) for version control.
Anyway, I'm open to any solution to this problem. It doesn't have to involve copying project folders around--that's just the only thing I could think of to ensure that all my version control repositories are complete, stand-alone packages.
Update 2
First of all, just a note. I'm using Mercurial (TortoiseHG) for version control, not SVN. I could change if absolutely necessary, but I really prefer Mercurial.
Based on responses so far, I've decided to do away with the "bi-directional copying" idea, and go back to referencing my library projects. Here's a new diagram:
I continue to have the same goals, however:
- Latest version of each solution is using latest library code
- One solution/application per repository
- Each repository contains all source code, including library projects
- Everything is as automated as possible to minimize the risk of mistakes
Goal #1 is taken care of automatically by referencing the library projects instead of using copies, and Goal #2 is just a matter of how my set up my repositories, but Goals #3 and #4 remain elusive.
With Mercurial, there is a subrepositories feature that seems like it would handle my situation, but as the documentation indicates, this is still considered experimental/risky.
For the moment, I'm thinking a good workaround might be to just store backup copies of the library projects in my Solution folders. When I say "backup copies", I mean that literally. I would still be referencing the library projects--the copies would be solely for the purpose of ensuring all source code ends up in my repository (Goal #3). To satisfy Goal #4, these backups could be automated using a post-build event in studio.
I welcome your thoughts on any of this.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
考虑为什么您的应用程序需要使用不同版本的库。如果你正确地设计你的库,并确保在升级它们时不会破坏任何东西(使用持续集成和单元测试,以及测试所有依赖的项目),那么在大多数情况下最好的(最简单、最干净的)方法就是简单地让所有应用程序运行相同版本的库。我什至想说,如果您的应用程序无法运行相同的库版本,则表明您的库未正确设计/实现/维护,您应该重新考虑您的方法。
另一种方法是为每个应用程序使用一个分支,因此每个应用程序都有自己独立的库副本(在项目和库之间使用相对引用,然后您可以在分支中重新定位代码)。这允许应用程序构建不同版本的库,但确实具有分支的所有缺点 - 库的两个副本,因此您必须小心不要在错误的分支中进行编辑;潜在的令人讨厌的合并等。
我建议将图书馆项目移入图书馆。您的图表显示了应用程序解决方案构建库代码并将其推送到库,然后将构建的库文件吸回到项目中 - 双向复制,哎呀!只需将库项目放入库中并构建库,然后从项目中引用该库 - 单向副本。您可能认为这意味着您必须构建两个解决方案(库和应用程序),但实际上您可以随时在应用程序解决方案中添加/删除库项目,因此这种布局不需要两阶段构建过程。但库和应用程序之间的逻辑区别要清晰得多。
Consider why your Applications need to work with different versions of the libraries. If you design your libraries properly, and make sure you don't break anything while upgrading them (use continuous integration and unit tests, as well as testing all dependent projects) then the best (simplest, cleanest) approach in most cases is simply to have all Applications run off the same version of the Libraries. I would go so far as to say that if your Applications can't run off the same Library version, that tells you that your Libraries are not designed/implemented/maintained properly, and you should reconsider your approach.
Another approach is to use a branch for each Application, so each has its own independent copy of the Libraries (use a relative reference between the projects and the libraries and then you can relocate the code in the branches). This allows the Applications to build off different versions of the libraries, but does have all the disadvantages of branching, though - two copies of the libraries so you have to be careful not to edit in the wrong branch; the potential for nasty merges, etc.
I'd recommend moving the Library projects into the Library. Your diagram shows the Application solutions building library code and pushing it up to the library, then sucking the built library files back down into the projects - a bi-directional copy, eek! Just place the library projects in the library and build the library, then reference that library from your projects - a unidirecitonal copy. You may think this would mean you have to build two solutions (Library and then Application), but you can actually add/remove library projects in your application Solution whenever you wish, so this layout does not necessitate a two-stage build process. But the logical distinction between the library and application is much cleaner.
我们解决类似问题的方法正是您在更新 2 中描述的,但在您建议“备份副本”的地方,我们使用克隆,它们是我们工作流程中不可或缺的一部分。
我的解决方案。
我们使用克隆和命名分支的组合来允许我们控制特定应用程序(示例中的解决方案)的库何时更新。
我们所做的是为每个库建立一个核心存储库,其中包含该库的主线分支。当我们创建一个新应用程序时,我们将所有相关库(每个库使用一个 Mercurial 存储库,其中一个库可能包含该库及其测试应用程序)克隆到新应用程序旁边的目录中,并引用该应用程序中的库。
对此应用程序的库的任何提交都将提交到根据应用程序命名的分支。当应用程序完成时,我们检查对库的更改,然后将适当的更改合并回主线,创建新的主线。这还有一个优点,即将来您可以通过查看更改提交到哪个分支来轻松识别哪个应用程序对库造成了特定更改。
每当我们接触一个应用程序时,我们都可以选择是继续使用旧的库,还是更新到该库的最新主线版本(甚至另一个项目开发版本)。库更改不会推送到使用它们的每个应用程序(有些可能是多年未触及的遗留应用程序,并且与它们提供的库运行良好),它们是由需要新库的应用程序拉入的给定版本的库中的设施。
我为什么要这样做?
我在这里试图避免的问题是我在以前的公司经常遇到的问题。
我会让我的应用程序与给定的库一起使用。其他人会更改他们的应用程序的库,以便它执行他们需要的操作,但下次我去编辑我的项目时,它不会编译。然后,我将不得不花时间修复我的项目,以按照库现在想要的方式进行操作,或者花时间恢复对库所做的不适当的更改(然后这些更改将级联回其他开发人员)。
通过本地项目克隆,每个应用程序都可以选择何时更新到更新的库版本,并且从机会管理的角度来看,我们可以在将库合并回主线之前根据应用程序要求审查库的更改。
这是一个使用 SourceSafe 等传统 VCS 永远无法实现的工作流程,但与 Hg 等 DVCS 配合使用效果非常好。
针对这些难以实现的目标的具体解决方案。
尝试回答你难以捉摸的目标
通过将库克隆到靠近应用程序的目录中,您将获得一组可以一起管理的存储库,并且最终可能会很好地转换为“应用程序”超级存储库的子存储库。
我使用存储在应用程序目录中的一堆批处理文件来执行此操作。
例如,给定的应用程序“_projects.bat”可能包含:
然后我有批处理文件来执行有用的同步操作,例如检查将使用“__incoming.bat:提取哪些文件:
并检查将使用“__outgoing.bat”推送哪些文件:
或者实际上使用“__push.bat”执行这些操作:
和“__pull.bat”:
而“__update.bat”将所有内容更新到最新版本(在同一分支内,您仍然必须在当前分支之外显式更新):
其中之一我最喜欢的是“__ids.bat”,它显示了当前正在使用的变更集(和分支),并且最重要的是,它们是否有未提交的未提交更改:
每当我开始进行应用程序更新时,我都会检查是否有更新库,下拉所有合适的内容,更新到我想要的版本,然后继续开发,
请注意文件名中的下划线只是将它们推到目录列表的顶部,以便更容易找到它们。 )
未来的选择。
最终我想通过使用子存储库来完全自动化此工作流程(因此整个应用程序及其所有库的状态都记录在一个应用程序变更集中),但是虽然它在 Mercurial、TortoiseHG 中变得非常稳定整合似乎不是一个优先事项。上次我尝试使用 THG 提交工具时(大约 0.8.3),我无法让 THG 提交工具执行任何有用的操作。我真的应该用 0.9.3 重新测试,但我不记得在变更日志中看到过任何关于子存储库的注释,所以除非人们告诉我 THG 子存储库支持已被悄悄添加,否则这是不值得的。
事实上,批处理文件工作得很好。我的待办事项列表中的下一件事是“__clone.bat”,它应该使克隆应用程序及其所有库变得更加容易,但我没有时间让它工作。
如果有人想尝试一下,我很想在这个帖子中看到回复。 *8')
保重,希望这会有所帮助,
马克............
The way we solve a similar problem is just what you describe in update 2, but where you suggest 'backup copies', we use clones and they are an integral part of our workflow.
My solution.
We use a combination of clones and named branches to allow us to control when the libraries for a particular application (solution in your example) are updated.
What we do is have a core repository for each of our libraries which contains the mainline branch of that library. When we create a new application we clone all of the relevant libraries (we use one mercurial repo per library, where a library may contain the library and it's test application) into directories beside the new application and reference the libraries in that application.
Any commits to the libraries for this application are committed to a branch named according to the application. When an application is complete, we review the changes to the library and then merge appropriate changes back into the mainline, creating a new mainline. This also has the advantage that in the future, you can easily identify which application caused a particular change to a library by looking at which branch that change was committed to.
Whenever we touch an application, we can chose whether to continue using the old libraries, or update to a more recent mainline version (or even another projects development version) of the library. Library changes aren't pushed out to every application which uses them (some might be legacy applications which haven't been touched for years and are working fine with the library they were suplied with), they are pulled in by applications which need the new facilities in a given version of the library.
Why do I do it this way?
The problem I'm trying to avoid here is one I constantly ran into at a previous company.
I would get my application working with a given library. Someone else would change the library for their application so that it did what they needed and the next time I went to edit my project, it wouldn't compile. I would then have to spend time fixing up my project to beghave in the way the library now wanted or spend time to revert inappropriate changes made to the library (which would then cascade back to the other developer).
With local project clones, every application gets to chose when it updates to a more recent library version and from a chance management perspective, we get to review changes to libraries due to application requirements before they get merged back into the mainline.
This is a workflow which would never have been possible with a traditional VCS like SourceSafe, but works beautifully with a DVCS like Hg.
Specific solutions to those elusive goals.
To try and answer your elusive goals
By cloning libraries into the directories close to your application, you get a set of repositories which can be managed together and possibly eventually will convert nicely into sub-repos of an 'application' super-repo.
I do this with a bunch of batch files which I store in my application directory.
For instance a given applications "_projects.bat" might contain:
Then I have batch files to do useful synchronisation things like check what files would be pulled with "__incoming.bat:
and check what would be pushed with "__outgoing.bat":
Or actually do those operations with "__push.bat":
and "__pull.bat":
while "__update.bat" updates everything to the latest version (within the same branch, you still have to explicitly update outside the current branch):
One of my favourites is "__ids.bat", which shows what changesets (and branched) are currently in use and critically, whether they have uncomitted changes outstanding:
Whenever I come to start work on an application update, I check to see if there are updated libraries, pull down everything which is appropriate, update to the versions I want and then continue development.
Note the underscores in the filenames are just there to push them to the top of the directory lists, to make them easier to find. *8')
Future options.
Eventually I would like to fully automate this workflow by using sub-repos (so the state of the whole application and all of it's libraries are recorded in one application changeset), but while it is getting pretty stable in Mercurial, TortoiseHG integration doesn't seem to be a priority. I couldn't get the THG commit tool to do anything useful the last time I tried it (around 0.8.3). I really should retest with 0.9.3 but I can't remember seeing any notes about sub-repos in the changelogs, so it isn't worth it unless people tell me THG sub-repo support has been silently added.
As it is, the batch files work quite well. The next thing on my todo list is a "__clone.bat" which should make cloning the application and all of it's libraries a whole lot easier, but I haven't had the time to get that working.
If anyone fancies having a go though, I'd love to see a reply in this thread. *8')
Take care, hope this helps,
Mark..........
这听起来与 Mercurial 中的子存储库支持的用途完全相同。如果项目 A 和项目 B 等是单独的存储库,您可以将它们作为解决方案 1 和 2 中的子存储库。 1 和 2 中的
.hgsub
文件本身进行版本控制并指向特定修订在 A、B 和 C 中,因此您始终可以在每个解决方案中使用相同的版本进行构建,但无需使它们保持同步。将更改从解决方案移回到库变得很容易,并且如果需要,还可以进行分支。不要让 wiki 页面上提到的“1.3 中的测试版”欺骗了您。 Mercurial 现在已更新至 1.4.2,子存储库保持原样。
This sounds exactly like what the sub repository support in mercurial is for. If project A and project B, etc. are are separate repositories you can can make them sub-repos within Solutions 1 and 2. The
.hgsub
files in 1 and 2 are versioned themselves and point to specific revisions within A, B, and C, so you can always build with the same version in each Solution, but need not keep them in lock-step. Moving changes back from the solutions to the libraries becomes easy, and if desired, branchable.Don't let the "beta in 1.3" mention on that wiki page fool you. Mercurial is up to 1.4.2 now and subrepos are staying as they are.
我不确定我是否理解你们“图书馆”的用途?
为什么这两个解决方案实际上没有引用同一个项目,而是引用了一个副本?
如果目的是能够在“库”的不同分支上工作,那么您所描述的就是由 SVN 等源代码控制软件处理的事情。
您将拥有“主干”(您的图书馆),然后从主干创建两个分支。在适当的里程碑处,您可以将分支合并回主干,并将主干合并到其他分支中。
I'm not sure I understand the purpose of your "Library"?
Why do the two solutions not actually reference the same project rather than a copy?
If the purpose is to be able to work on different branches of the "Library" then what you are describing is the sort of thing handled by source code control software like SVN.
You would hava "trunk" (your library) then two branches created from the trunk. At appropriate milestones you would merge a branch back into the trunk and be merging the trunk down into other branches.
抱歉,我没有阅读本页的全文。但!简短的回答是:Visual Studio 会执行您想要的所有必要的自动化。
我们有类似的结构。 Solution1、Solution2 和包含多个项目的公共库。我们在 SVN 下的文件夹结构如下:
正如您在此处看到的禁止复制。这两种解决方案都只是保留对
..\CommonLibrary\ProjectX
的引用。当我对存储库执行 SVN-Update 命令后,Visual Studio 询问我“ProjectX 已更改”。您想重新加载吗?我单击“是”并继续编码。
PS:也看看这个软件 http://svnnotifier.tigris.org/ 它可以更新任意数量的自动(通过计时器)或通过鼠标单击存储库本地副本。
Sorry I didn't read whole text on this page. BUT! The short answer is: Visual Studio does all necessary automation you want.
We have similar structure. Solution1, Solution2, and common library with several projects in it. Our folder structure under SVN is following:
As you can see no copying here. Both solutions simply keeps reference to
..\CommonLibrary\ProjectX
.After I do SVN-Update command for repository the Visual Studio asks me 'ProjectX was changed. Do you want to reload it?' I click 'yes' and proceed my coding.
PS: also look at this software http://svnnotifier.tigris.org/ It can update any number of repository local copies automatically (by a timer) or by a mouse click.