22

我想做一个简单的版本控制系统,但我不知道如何构造我的数据和我的代码。

这是一个简短的例子:

  1. 用户登录
  2. 上传文件时,用户有两种选择:
    • 提交新文件
    • 提交文件的新版本

用户应该能够看到树。(不同版本)树最多只能有 2 个级别:

|
|--File_A_0
 \--File_A_1
 \--File_A_2
 \--File_A_3
 \--File_A_4

文件也有2种类型,一个final(这是最新批准的版本)和一个draft version(最新上传的文件)文件将物理存储在服务器上。每个文件由一个用户(或多个)拥有,并且只有一个组。

编辑:组代表一组文档,文档一次只能由一个组拥有。用户不依赖于组。

开始编辑:

这是我所做的,但效率不高!

id_article | relative_group_id | id_group | title | submited | date | abstract | reference | draft_version | count | status

id_draft | id_file | version | date

但是很难管理,很难扩展。我认为这是因为组参数...

结束编辑

所以问题是:

  • 我怎样才能架构化我的数据库?
  • 什么样的信息应该对这个作品的版本有用?
  • 文件夹、文件的结构是什么样的?
  • 你有什么样的提示,提示你做这种工作?

(应用程序是用PHP和Zend Framework开发的,数据库应该是mysql或postgresql)

4

13 回答 13

50

For God's sake, don't. You really don't want to go down this road.

Stop and think about the bigger picture for a moment. You want to keep earlier versions of documents, which means that at some point, somebody is going to want to see some of those earlier versions, right? And then they are going to ask, "What's the difference between version 3 and version 7"? And then they are going to say, "I want to roll back to version 3, but keep some of the changes that I put in version 5, ummm, ok?"

Version control is non-trivial, and there's no need to reinvent the wheel-- there are lots of viable version control systems out there, some of them free, even.

In the long run, it will be much easier to learn the API of one of these systems, and code a web front-end that offers your users the subset of features they are looking for (now.)

You wouldn't code a text editor for your users, would you?

于 2009-06-15T18:53:08.907 回答
18

你可能会从那里得到灵感。


关于您的评论:

至于数据库结构,你可以试试这种结构(MySQL sql):

CREATE TABLE `Users` (
       `UserID` INT NOT NULL AUTO_INCREMENT
     , `UserName` CHAR(50) NOT NULL
     , `UserLogin` CHAR(20) NOT NULL
     , PRIMARY KEY (`UserID`)
);

CREATE TABLE `Groups` (
       `GroupID` INT NOT NULL AUTO_INCREMENT
     , `GroupName` CHAR(20) NOT NULL
     , PRIMARY KEY (`GroupID`)
);

CREATE TABLE `Documents` (
       `DocID` INT NOT NULL AUTO_INCREMENT
     , `GroupID` INT NOT NULL
     , `DocName` CHAR(50) NOT NULL
     , `DocDateCreated` DATETIME NOT NULL
     , PRIMARY KEY (`DocID`)
     , INDEX (`GroupID`)
     , CONSTRAINT `FK_Documents_1` FOREIGN KEY (`GroupID`)
                  REFERENCES `Groups` (`GroupID`)
);

CREATE TABLE `Revisions` (
       `RevID` INT NOT NULL AUTO_INCREMENT
     , `DocID` INT
     , `RevUserFileName` CHAR(30) NOT NULL
     , `RevServerFilePath` CHAR(255) NOT NULL
     , `RevDateUpload` DATETIME NOT NULL
     , `RevAccepted` BOOLEAN NOT NULL
     , PRIMARY KEY (`RevID`)
     , INDEX (`DocID`)
     , CONSTRAINT `FK_Revisions_1` FOREIGN KEY (`DocID`)
                  REFERENCES `Documents` (`DocID`)
);

CREATE TABLE `M2M_UserRev` (
       `UserID` INT NOT NULL
     , `RevID` INT NOT NULL
     , INDEX (`UserID`)
     , CONSTRAINT `FK_M2M_UserRev_1` FOREIGN KEY (`UserID`)
                  REFERENCES `Users` (`UserID`)
     , INDEX (`RevID`)
     , CONSTRAINT `FK_M2M_UserRev_2` FOREIGN KEY (`RevID`)
                  REFERENCES `Revisions` (`RevID`)
);

Documents is a logical container, and Revisions contains actual links to the files. Whenever a person updates a new file, create an entry in each of these tables, the one in Revisions containing a link to the one inserted in Documents.

The table M2M_UserRev allows to associate several users to each revision of a document.

When you update a document, insert only in Revisions, with alink to the corresponding Document. To know which document to link to, you may use naming conventions, or asking the user to select the right document.

For the file system architecture of your files, it really doesn't matter. I would just rename my files to something unique before they are stored on the server, and keep the user file name in the database. Just store the files renamed in a folder anywhere, and keep the path to it in the database. This way, you know how to rename it when the user asks for it. You may as well keep the original name given by the user if you are sure it will be unique, but I wouldn't rely on it too much. You may soon see two different revisions having the same name and one overwriting the other on your file system.

于 2009-06-09T21:45:34.417 回答
13

Database schema


To keep it exremely simple, I would choose the following database design. I'm separating the "file" (same as a filesystem file) concept from the "document" (the gerarchic group of documents) concept.

User entity:

  • userId
  • userName

Group entity:

  • groupId
  • groupName

File entity:

  • fileId (a sequence)
  • fileName (the name the user gives to the file)
  • filesystemFullPath
  • uploadTime
  • uploaderId (id of the uploader User)
  • ownerGroupId

Document entity:

  • documentId
  • parentDocumentId
  • fileId
  • versionNumber
  • creationTime
  • isApproved

Every time a new file is uploaded, a "File" record is created, and also a new "Document". If it's the first time that file is uploaded, parentDocumentId for that document would be NULL. Otherwise, the new document record would point to the first version.

The "isApproved" field (boolean) would handle the document being a draft or an approved revision.
You get the latest draft of a document simply ordering descending by version number or upload time.

Hints


From how you describe the problem, you should analyze better those aspects, before moving to database schema design:

  • which is the role of the "group" entity?
  • how are groups/users/files related?
  • what if two users of different groups try to upload the same document?
  • will you need folders? (probably you will; my solution is still valid, giving a type, "folder" or "document", to the "document" entity)

Hope this helps.

于 2009-06-13T11:03:16.530 回答
9

Might an existing version-control solution work better than rolling your own? Subversion can be made to do most of what you want, and it's right there.

于 2009-06-13T15:57:15.277 回答
6

Creating a rich data structure in a traditional relational database such as MySQL can often be difficult, and there are much better ways of going about it. When working with a path based data structure with a hierarchy I like to create a flat-file based system that uses a data-serialization format such as JSON to store information about a specific file, directory or an entire repository.

This way you can use current available tools to navigate and manipulate the structure easily, and you can read, edit and understand the structure easily. XML is good for this too - it's slightly more verbose than JSON but easy to read and good for messaging and other XML-based systems too.

A quick example. If we have a repository that has a directory and three files. Looking at it front on it will look like this:

/repo
  /folder
    code.php
  file.txt
  image.jpg

We can have a metadata folder, which contains our JSON files, hidden from the OS, at the root of each directory, which describe that directory's contents. This is how traditional versioning systems work, except they use a custom language instead of JSON.

/repo
  */.folderdata*
  /code
    */.folderdata*
    code.php
  file.txt
  image.jpg

Each .folderdata folder could contain it's own structure that we can use to organize the folder's data properly. Each .folderdata folder could then be compressed to save disk space. If we look at the .folderdata folder inside the /code directory:

*/.folderdata*
  /revisions
    code.php.r1
    code.php.r2
    code.php.r3
  folderstructure.json
  filerevisions.json

The folder structure defines the structure of our folder, where the files and folders are in relation to one another etc. This could look something like this:

{
  '.':        'code',
  '..':       'repo',
  'code.php': {
    'author_id': 11543,
    'author_name': 'Jamie Rumbelow',
    'file_hash': 'a26hb3vpq22'
    'access': 'public'
  }
}

This allows us to associate metadata about that file, check for authenticity and integrity, keep persistent data, specify file attributes and do much more. We can then keep information about specific revisions in the filerevisions.json file:

{
  'code.php': [
    1: {
      'commit': 'ah32mncnj654oidfd',
      'commit_author_id': 11543,
      'commit_author_name': 'Jamie Rumbelow',
      'commit_message': 'Made some changes to code.php',
      'additions': 2,
      'subtractions': 4
    },
    2: {
      'commit': 'ljk4klj34khn5nkk5',
      'commit_author_id': 18676,
      'commit_author_name': 'Jo Johnson',
      'commit_message': 'Fixed Jamie\'s bad code!',
      'additions': 2,
      'subtractions': 0
    },
    3: {
      'commit': '77sdnjhhh4ife943r',
      'commit_author_id': 11543,
      'commit_author_name': 'Jamie Rumbelow',
      'commit_message': 'Whoah, showstopper found and fixed',
      'additions': 8,
      'subtractions': 5
    },
  ]
}

This is a basic outline plan for a file versioning system - I like this idea and how it works, and I've used JSON in the past to great effect with rich datastructures like this. This sort of data just isn't suitable for a relational database such as MySQL - as you get more revisions and more files the database will grow bigger and bigger, this way you can stagger the revisions across multiple files, keep backups of everything, make sure you have persistent data across interfaces and platforms etc.

Hope this has given you some insight, and hopefully it'll provide some food for thought for the community too!

于 2009-06-15T11:22:58.830 回答
2

For a database schema, you likely need two sets of information, files and file versions. When a new file is stored an initial version is created as well. The latest approved version would have to be stored explicitly, while the newest version can be selected from the versions table (either by finding the highest version related to the file, or the newest date if you store when they are created)

files(id,name,approved_version)
file_versions(id,fileId)

file versions could then be stored using their ids (eg., '/fileId/versionId' or '/fileId/versionId_fileName') on the server, with their original name stored in the database.

于 2009-06-09T23:51:24.787 回答
2

I recently built a simple versioning system for some static data entities. The requirement was to have an 'Active' version and 0 or 1 'pending' versions.

In the end, my versioned entity had the following attributes relevant to versioning.

VersionNumber (int/long) ActiveVersionFlag (boolean)

Where:-

  • only 1 entity can be ActiveVersionFlag = 'Y'
  • only 1 entity can be Version number > the 'Active' version (i.e. the 'pending' version)

The kind of operations I allowed were

Clone current version.

  • Fail if there is already a version > the Versionnumber of the 'Active' version
  • Copy all of the data to the new version
  • increment the version number by one

Activate Pending Version

  • Fail if the specified version is not the 'Active' version + 1
  • find the 'Active' version and set its ActiveVersionFlag to 'N'
  • set the ActiveVersionFlag of the 'pending' version to 'Y'

Delete Pending Version

  • Delete the Pending Entity

This was reasonably successfull and my users now clone and activate all the time :)

Michael

于 2009-06-19T05:09:25.247 回答
1

Start from an existing content management system, done in PHP and MySQL if those are your requirements, such as eZ Publish, or Knowledgetree. For rapid testing of these applications, Bitnami provides quick-to-install "stacks" of these as well (WAMP-stacks on steroids).

Then you can tailor these applications to your organizations needs, and stay up-to-date with the changes upstream.

于 2009-06-17T15:54:06.510 回答
1

As an alternative to my previous post, if you think a hierarchical structure would be best, you may want to use flat-file storage, and expose an API through a Web service.

The server would have its data root directory, and you can store groups (of files) in folders, with a root meta-data entry in each folder. (XML perhaps?)

Then you can use an existing revision control tool wrapped in an API, or roll your own, keeping revisions of files in a revisions folder underneath the item in the folder. Check for revisions, and do file I/O with file I/O commands. Expose the API to the Web application, or other client application, and let the server determine file permissions and user mapping through the XML files.

Migrate servers? Zip and copy. Cross platform? Zip and copy. Backup? Zip and copy.

It's the flat-file back-end that I love about Mercurial DVCS, for example.

Of course, in this little example, the .rev files, could have dates, times, compression, etc, etc, defined in the revisions.xml file. When you want to access one of these revisions, you expose an AccessFile() method, which your server application will look at the revisions.xml, and determine how to open that file, whether access is granted, etc.

So you have

DATA
| + ROOT
| | . metadata.xml
| | |
| | + REVISIONS
| | | . revisionsdata.xml
| | | . documenta.doc.00.rev
| | | . documenta.doc.01.rev
| | | . documentb.ppt.00.rev
| | | . documentb.ppt.03.rev
| | |___
| | |
| | . documenta.doc
| | . documentb.ppt
| | |
| | + GROUP_A
| | | . metadata.xml
| | | |
| | | + REVISIONS
| | | | . revisionsdata.xml
| | | | . documentc.doc.00.rev
| | | | . documentc.doc.01.rev
| | | | . documentd.ppt.00.rev
| | | | . documentd.ppt.03.rev
| | | |___
| | |
| | | . documentc.doc
| | | . documentd.ppt
| | |___
| | |
| | + GROUP_B
| | | . metadata.xml
| | |___
| |
| |___
|
|___
于 2009-06-18T20:07:12.007 回答
0

Check out ProjectPier (originally ActiveCollab). It has a system similar to this and you could look at their source.

于 2009-06-09T21:50:08.557 回答
0

Uploading files is to 1990-ty =) Look at Google Wave! You can just build your entire application around their 'version control' framework.

于 2009-06-13T09:01:09.490 回答
0

It's not as simple as it looks. Read this article by Eric Sink on the implications of storage of storing these files.

Perhaps a better question would be what sort of files are being loaded, and do they lend themselves well to versioning (like text files)

于 2009-06-16T09:07:28.307 回答
0

I think this describes the perfect system for versioning

http://tom.preston-werner.com/2009/05/19/the-git-parable.html

于 2009-07-06T09:36:58.233 回答