summaryrefslogtreecommitdiff
path: root/HACKING
blob: 773d24720d8b1dbdc7e2c923e5725e6de4b9a701 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
#!/bin/less

   Version 0.8c re-implements the wrapper in C, to allow better
   cooperation between plugins via dependencies.  This probably makes
   a lot of this document out-dated, but a lot of it is still good, it
   describes the workings of the plugins, and the repo structure.

   I know this file is long* and boring. I try to keep it preened and
   informative, but would rather spend my time actually hacking. If
   you think you can help this file, have at it! ~ Luke Shumaker

   * at 200+ lines, it's longer than any rvs source file

- - -cut-here- - 8< - - - - - - - - - - - - - - - - - - - - - - - - -
//////////////////////////////////////////////////////////////////////
                               rvs 0.7.0
	retroactive versioning system
	          a versioning system that allows you to check
	            in commit 2 before commit 1
//////////////////////////////////////////////////////////////////////

                              hacking

  build basics

   One of the coolest things about rvs (IMO) is how modular and
   customizable it is.  If you wish to create a fork of rvs, it would
   be a good idea to give it a different name.  This can be done
   without even changing a single source file!  Simply run configure
   with `--name=NAME' option (variables that have `rvs' in the name
   will not be renamed, but the user will never know this unless they
   peek at the code).

   Other options to configure and their default values are:

   	name='rvs'
   		The name of the program. Note that unlike most
   		variables, you should NOT call this in Makefiles
   		(`$(name)'), but use `$(rvs)' instead.
   	RVS='$(bindir)/$(rvs)'
   		Where the executable will be. `$(rvs)' is the same as
   		`$(name)' (see above). In this document it is
   		sometimes referred to as the `wrapper'.
   	SHELL='/bin/sh'
   		The shell that will be used by make, and the shell
   		that scripts will run in. (Perhaps we should make
   		these separate variables?)
   	prefix='/usr/local'
   		Standard GNU variable. Many of you will want to
   		change this to `/usr', or `/home/USER_NAME'
   	exec_prefix='$(prefix)'
   		Standard GNU variable.
   	bindir='$(exec_prefix)/bin'
   		Standard GNU variable.
   	sbindir='$(exec_prefix)/sbin'
   		Standard GNU variable.
   	libexecdir='$(exec_prefix)/libexec'
   		Standard GNU variable.  The plugins will be installed
   		at $(libexecdir)/$(name)
   	srcdir=$(readlink -f `dirname "$0"`)
   		Where the source code is.  The default value evaluates
   		to the directory of the `configure' script.

   These can either be changed by manually changing `configure' or by
   running it like:
   	./configure --VAR_NAME=VALUE

   In this document, the `$(VAR_NAME)' refers to whatever you set the
   value to.

   The configure script will run on any instance of `Makefile.in' in
   $(srcdir).

   Currently, rvs is distributed with 2 plugins. `repo'[sitory] is
   the core of the rvs file database. `users' handles all usernames,
   settings, and other info.

  commands

   By itself, rvs does almost nothing.  All real functionality is
   provided by plugins.  When you invoke rvs, you do so in the format
   `rvs COMMAND'. Plugins work by adding new commands.

   Most commands are provided by plugins, however, a few are built in:
      init
      	Initializes a repository in the current directory. It creates
      	`./.$(name)', and runs any initalizers for
      	plugins.
      install PLUGIN DIR
      	Installs PLUGIN, for which the files are found in DIR. Unless
      	an older version is already installed, it adds the plugin to
      	have lowest priority (see `$(libexecdir)/$(rvs)/plugins').
      uninstall PLUGIN
      	Uninstalls PLUGIN. Did that really need to be said?

   A plugin is simply a collection of independent executable files.
   When rvs is invoked, it looks for the COMMAND in a few places:
     1) checks if COMMAND is an built-in command
     2) looks at the file `$(libexecdir)/$(rvs)/plugins'. This is a
        newline-delimited list of plugin names.
     3) loops through the directories `$(libexecdir)/$(rvs)/PLUGIN',
        where PLUGIN is one of the values in
        `$(libexecdir)/$(rvs)/plugins', in the order they appear in
        the file.
     4) if rvs does not find the command, it reports an error

   If you would like to contribute a plugin, or distribute one with a
   fork, all you have to do is put it in `$(srcdir)/plugins/NAME'. The
   main Makefile will recognize any directory in `$(srcdir)/plugins/'
   as a plugin, and will run `make -C `plugins/NAME/Makefile'. It
   should create a directory (mine use `plugins/NAME/out') to put ALL,
   and ONLY the final files to be used by that plugin.

========this is outdated but I don't really want to keep editing it ==

  build system

   The build system rvs uses is rather simple.
   `./configure' does two things:
   	* create a sed script (`var.sed')
   	* run every instance of `$(srcdir)/Makefile.in' through
   	  `var.sed' to generate a proper `Makefile'

    `var.sed' contains all configuration variables. When it processes
    a file every instance of `@VAR_NAME@' is replaced by that
    variable's value. This makes `configure' act much like a GNU
    package `configure'. Note that this replacement only happens for
    defined variables.

   Most of these can easily be changed _after_ `compilation' also:
   	VER       	line 4 of the wrapper
   	SHELL     	line 1 of each shell script
   	prefix    	simply move the wrapper
   	bindir    	simply move the wrapper
   	libexecdir	move the directory, then update the line
   	          	`RVSDIR=...' in the wrapper

   I have designed this system to be extremely modular. As you may
   have figured out, each bit on functionality is in it's own
   executable, and the file you call when you type `rvs' is just a
   wrapper for them.

   The wrapper is quite simple in mechanism. It simply checks if
   `$$libdir$$/COMMAND_NAME' exists, and if so, runs that file.

   The `rvs commit' command is also quite simple in design. It takes a
   single argument; the file to commit. If no target is specified, it
   defaults to `./'. It checks which type of file the target is, and
   runs `rvs commit.FILE_CODE TARGET'. The file codes are as follows:

   	block       (buffered) special b
   	character (unbuffered) special c
   	directory                      d
   	named pipe (FIFO)              p
   	regular file                   f
   	symbolic link                  l
   	socket                         s
   	door (Solaris only)            D

   As you probably noticed (if you've looked at the blueprints or
   source files), only directories and regular files have been
   implemented as of rvs 0.7.0.

   After `rvs commit' has done this, it creates a meta-file for that
   commit. The meta file contains author, copyright owner, license
   data, file permissions, timestamps, etc. The id of the meta-file is
   written to stdout, and used to later check out that commit.

   get works in much the same way as commit, only it reads the file
   type from the meta-file, then runs `rvs get.FILE_CODE COMMIT_ID'.

   If you implement any other file types, save the commit and get
   functions in the proper locations.

     Any commit function should take a  filename   as an argument and
     output the according commit id to stdout. stderr should be used
     for all messages.

     Any  get   function should take the commit id as an argument and
     uses stdout only if verbose, or to report errors.

   To summarize, the modules are separate programs and communicate via
   pipes, which is generally considered bad-design, and libraries
   should be used instead. I deliberately broke this because:

    1. incompatible licenses can be used for different modules
    2. modules can be added/removed on the fly
    3. one can use any language to write new modules, without
       having to worry about bindings`$(libexecdir)/$(rvs)

  $$libdir$$/lib/

   THIS WAS WRITTEN FOR rvs 0.6.2
   THIS PORTION OF THE CODE IS BEING REVISED IN rvs 0.6.3

   I have created two 'libraries' for use by rvs components. They
   provide several functions that can be accessed by shell script by
   using the code:
   	source "`rvs -d`/lib/stdio"
   or
   	source "`rvs -d`/lib/rvsdb"
   (`rvs -d' returns $$libdir$$)
   stdio provides several functions for printing messages:
   	verbose MESSAGE
   	   print MESSAGE to stdout only if $verbose is set to '-v'
   	out MESSAGE
   	   print MESSAGE to stdout if $verbose isn't set to '-q'
   	warn MESSAGE
   	   print "rvs: MESSAGE" to stderr
   	error MESSAGE
   	   print "rvs: MESSAGE" and a quick message about `rvs --help'
   	   (hinting at user error) to stderr, then exit with error
   	fatal MESSAGE
   	   For internal error. Print "rvs: MESSAGE" to stderr and exit
   	   with error
   	version
   	   print the version information and exit with success

   rvsdb provides several functions and variables for dealing with the
   rvs database:
   	getid FILE
   	   returns what the id of a given file would be if it were in
   	   the database. This is used to know where to put files when
   	   committing them. In 0.5.8-0.6.3 this is just the sha1sum of
   	   the file
   	NOTE: the "log*" functions aren't very stable or developed
   	loginit FILE
   	   initialize an rvs log at FILE. These logs are used for
   	   metafiles, directory listings, and (probably) eventually
   	   checkout logs
   	lograw LOG
   	   prepare LOG for reading, and write the output to stdout (to
   	   pipe into another function). This is mostly for internal
   	   use by rvsdb
   	logread LOG VAR
   	   read variable VAR from the logfile LOG
   	logwrite LOG VAR VAL
   	   set variable VAR to VAL in the logfile LOG
   	logfind LOG VAL
   	   return all the names of variables in logfile LOG who have
   	   the value VAL

  the database (.rvs/*)

   So, what are all these files doing in this database? The scheme is
   fairly simple. In the `.rvs' directory there is 1 directory, the
   `files' directory. There used to be a folder for tmpfiles (0.5.8-
   0.6.2), but no more (0.6.3+). If you need a tmpfile, just run:
   	FILENAME=$(mktemp -t)
   The `files' directory is where all the data is kept.

   When any file is committed, whether it be a regular file, a
   directory, a link, or any other type of file, 2 files are created
   in `.rvs/files', the "raw-file" and the "meta-file". When we speak
   of file IDs, we mean the filename of the corresponding file in
   `.rvs/files' in rvs 0.5.8-0.6.3 this is just the sha1sum of the
   file. The meta-file stores everything not part of the file itself;
   the filename, file-type, author, copyright owner, file permissions,
   timestamps, etc, and the ID of the corresponding raw-file. In the
   case of an regular file, the raw-file contains the file itself. For
   directories, it contains pointers to each file inside the
   directory.

   $$libdir$$/commit calls $$libdir$$/commit.FILETYPE to generate the
   raw-file, but generates the meta-file itself (in `.rvs/tmp', then
   calls commit.f to commit the meta-file). It then returns the ID of
   the meta-file. Therefore, the user never deals with the IDs of
   raw-files, and doesn't need to. The metafiles contain pointers to
   the corresponding raw-files.

   To keep things modular, commit.f is the ONLY thing that should
   actually put files in `.rvs/files', and get.f the only thing that
   should get them. Everything else should call them.

   The repo and init are the ONLY things that should have `.rvs'
   hardcoded into them. If the repository directory is needed, then
   use `rvs repo'. Again, in the core, these should only be the .f
   functions, however, it will be useful to plugins.

   Why do we have the `files' directory, why don't we just put
   everything in `.rvs'? This way:
    * There is a spot to put user data, when we get around to writing
      it

  final thoughts

   I have set up bazaar repository at Launchpad:
   	https://launchpad.net/rvs
   Only until rvs becomes self-hosting.

   If anyone needs any help, let me know, either via email, or via
   Launchpad, I'll be happy to help/would love to have your help!

~ Luke Shumaker <LukeShu@sbcglobal.net>
Happy Hacking!