Hello, I've been looking at the way how source links work, and I find their current behavior highly irritating. I would like to share my observations and propose a fix to make things a lot more obvious (without losing functionality). Please bear with me as I try to describe things as completely as necessary; sorry I can't make this short. I'll use the terms 'link package' for a package that links to another package, and 'target package' for a package that another package links to. Theory ====== When you ignore the space saving aspect, the key functionality of link packages is to record relative changes to packages. When the target package changes, these changes propagate to link packages (at least conceptually), and the link packages get rebuilt as well. Picture this as follows (with revision numbers in parentheses, time progressing from top to bottom, and expanded links): target(1) target(2) <--- link(1) link(2) target(3) We start at target(2). link(1) is created against target(2): initially there are no changes, so link(1) and target(2) are identical. Then link(1) is changed, resulting in link(2). The server computes and stores the diff between target(2) and link(2); let us call this diff(target(2), link(2)). Next, target(2) is changed, resulting in target(3). This triggers a rebuild of target(3) and of a package with the changes in link(2) applied to target(3). Let us call this apply(diff(target(2), link(2)), target(3)). Another way of saying this is that the build service merges the changes between target(2) and its two descendants target(3) and link(2) and builds the result. Let us call this merge(target(3), target(2), link(2)). Current behavior and problems ============================= When checking out a link package with osc, you get an expanded package by default: the diff stored in the link gets applied to the most recent version of the target package. This is similar to a merge, but without looking at the common ancestor between the two versions, and there are a number of problems: * You will not get the same state out of the build service that you have checked in. * If the changes in the target package and the link package overlap, the diff will not apply, and you will get nothing out of the build service at all. * Applying patches does not actually guarantee that the changes will be applied correctly and in the correct places. You may end up with subtle mismerges. * When you create another revision of the link package, you will not know that an implicit "merge" has even happened, and you will not be able to verify the results. * You will not be able to identify which revisions were made in the link or target package since when the two diverged. * The build service does not record the revision of the target package that it generated its diffs against. This means that there is no reliable way to reconstruct the original version of a link; one can only guess which revision of the target package the link might have been generated against based on timestamps. No locking is done across packages during checkins, so the timestamps are not guaranteed to be synchronized across packages, either. Concurrent checkins can lead to wrong guesses no matter how smart the guess algorithm is. * Because the server does not record which revision a link was generated against (the common ancestor when merging), it is not possible to actually perform a correct three-way merge. Any attempt to still try that is broken in one way or another. Proposed improvements ===================== * When creating a revision of a link package, always store the revision of the target package that the diff was generated against. * When checking out a link, always expand it against the revision of the target package that the link was created against. That way, links cannot break (unless the target package is deleted). The expansion could be done client or server side; the result would be the same either way: exactly what the user checked in; no magic, and no surprises. If the server has more recent revisions of the target package available, indicate to the user to perform a merge (either mandatorily or optionally; see the next item). * When committing a link package, either (a) make sure that the user has been working against the most recent version of the target package, or (b) allow commits to be done against older versions of the target package. Both (a) and (b) require the client to send the revision of the target package it has been using as part of the commit, but that is easy. * Merging could be implemented as three-way merge, either server or client side, using merge(1) or diff3(1). It's not that hard to implement that from scratch. What's more, when importing build service packages into a version control system, merging would come for free. Other version control systems, starting back with rcs(1), use merge(1) or a variant thereof. I have played around with the two when implementing the --merge option in GNU patch, and after giving it some real thought, I also went with the merge(1) format because the results are a lot more practical.) Thanks for your help with this! Andreas -- To unsubscribe, e-mail: opensuse-packaging+unsubscribe@opensuse.org For additional commands, e-mail: opensuse-packaging+help@opensuse.org