updated TODO list
[drupal_versioncontrol_git.git] / versioncontrol_git.module
blobefca6f31409343646d0cbf6fe132fcf43a0ffb1c
1 <?php
2 // $Id: versioncontrol_git.module,v 1.25 2007/11/10 00:32:25 jpetso Exp $
4 // Update methods.
5 define('VERSIONCONTROL_GIT_UPDATE_CRON', 0);
6 define('VERSIONCONTROL_GIT_UPDATE_XGIT', 1);
8 // The admin and user edit pages.
9 include_once(drupal_get_path('module', 'versioncontrol_git') .'/versioncontrol_git.admin.inc');
11 /**
12  * Implementation of hook_versioncontrol_backends().
13  *
14  * @return
15  *   A structured array containing information about this known backends.
16  *   Array key is the unique string identifier of the version control system.
17  *   The corresponding array values are again structured arrays and consist
18  *   of elements with the following keys:
19  *
20  *   'name': The user-visible name of the VCS.
21  *   'description': A short description of the backend, if possible not longer
22  *                  than one or two sentences.
23  *   'capabilities': An array listing optional capabilities, in addition to the
24  *                   required functionality like retrieval of detailed
25  *                   commit information. Array values can be an arbitrary
26  *                   combination of VERSIONCONTROL_CAPABILITY_* values. If no
27  *                   additional capabilities are supported by the backend,
28  *                   this array will be empty.
29  *   'autoadd': An array listing which tables should be managed by
30  *              Version Control API instead of doing it manually in
31  *              the backend. Array values can be an arbitrary combination of
32  *              VERSIONCONTROL_AUTOADD_* values. If no array additions
33  *              should be automatically managed, this array will be empty.
34  */
35 function versioncontrol_git_versioncontrol_backends() {
36   return array(
37     // The array key is up to 8 characters long, and used as unique identifier
38     // for this VCS, in functions, URLs and in the database.
39     'git' => array(
40       // The user-visible name of the VCS.
41       'name' => 'GIT',
43       // A short description of the VCS, if possible not longer than one or two sentences.
44       'description' => t('GIT (the stupid content tracker) is a fast, scalable, distributed revision control system with an             unusually rich command set that provides both high-level operations and full access to internals.'),
46       // A list of optional capabilities, in addition to the required retrieval
47       // of detailed commit information.
48       'capabilities' => array(
49         // Able to cancel commits if the committer lacks permissions
50         // to commit to specific paths and/or branches.
51         VERSIONCONTROL_CAPABILITY_COMMIT_RESTRICTIONS,
52         // Able to cancel branch or tag assignments if the committer lacks
53         // permissions to create/update/delete those.
54         VERSIONCONTROL_CAPABILITY_BRANCH_TAG_RESTRICTIONS,
55       ),
57       // An array listing which tables should be managed by Version Control API
58       // instead of doing it manually in the backend.
59       'flags' => array(
60         // versioncontrol_insert_repository() will automatically insert
61         // array elements from $repository['git_specific'] into
62         // {versioncontrol_git_repositories} and versioncontrol_get_repositories()
63         // will automatically fetch it from there.
64         VERSIONCONTROL_FLAG_AUTOADD_REPOSITORIES,
65         // versioncontrol_insert_commit() will automatically insert
66         // array elements from $commit['git_specific'] into
67         // {versioncontrol_git_commits} and versioncontrol_get_commits()
68         // will automatically fetch it from there.
69         VERSIONCONTROL_FLAG_AUTOADD_COMMITS,
70       ),
71     ),
72   );
76 /**
77  * Implementation of [versioncontrol_backend]_get_commit_actions():
78  * Retrieve detailed information about what happened in a single commit.
79  *
80  * @param $commit
81  *   The commit whose actions should be retrieved.
82  *
83  * @return
84  *   A structured array containing the exact details of what happened to
85  *   each item in this commit. Array keys are the current/new paths, also for
86  *   VERSIONCONTROL_ACTION_DELETED actions even if the file actually doesn't
87  *   exist anymore. The corresponding array values are again structured arrays
88  *   and consist of elements with the following keys:
89  *
90  *   'action': Specifies how the item was modified.
91  *             One of the predefined VERSIONCONTROL_ACTION_* values.
92  *   'modified': Boolean value, specifies if a file was modified in addition
93  *               to the other action in the 'action' element of the array.
94  *               Only exists for the VERSIONCONTROL_ACTION_MOVED
95  *               and VERSIONCONTROL_ACTION_COPIED actions.
96  *   'current item': The updated state of the modified item. Exists for all
97  *                   actions except VERSIONCONTROL_ACTION_DELETED.
98  *   'source items': An array with the previous state(s) of the modified item.
99  *                   Path and branch will always be the same as in the current
100  *                   item except for the VERSIONCONTROL_ACTION_MOVED,
101  *                   VERSIONCONTROL_ACTION_COPIED and
102  *                   VERSIONCONTROL_ACTION_MERGED actions.
103  *                   Exists for all actions except VERSIONCONTROL_ACTION_ADDED.
105  *   Item values are structured arrays and consist of elements
106  *   with the following keys:
108  *   'type': Specifies the item type, which is either
109  *           VERSIONCONTROL_ITEM_FILE or VERSIONCONTROL_ITEM_DIRECTORY.
110  *   'path': The path of the item at the specific revision.
111  *   'revision': The (file-level) revision when the item was changed.
112  *               If there is no such revision (which may be the case for
113  *               directory items) then the 'revision' element is
114  *               an empty string.
115  *   '[xxx]_specific': May be set by the backend to remember additional
116  *                     item info. ("[xxx]" is the unique string identifier
117  *                     of the respective version control system.)
118  */
119 function versioncontrol_git_get_commit_actions($commit) {
120   $actions = array();
121   $result = db_query('SELECT item_revision_id, action, type, path,
122                              revision, source_revision
123                       FROM {versioncontrol_git_item_revisions}
124                       WHERE commit_id = %d', $commit['commit_id']);
126   while ($item_revision = db_fetch_object($result)) {
127     $action = array(
128       'action' => $item_revision->action,
129     );
131     if ($item_revision->action != VERSIONCONTROL_ACTION_DELETED) {
132       $action['current item'] = array(
133         'type' => $item_revision->type,
134         'path' => $item_revision->path,
135         'revision' => $item_revision->revision,
136         'git_specific' => array(
137           'item_revision_id' => $item_revision->item_revision_id,
138           'selected_branch_id' => $commit['git_specific']['branch_id'],
139           'selected_date' => $commit['date'],
140         ),
141       );
142     }
143     if ($item_revision->action != VERSIONCONTROL_ACTION_ADDED) {
144       $action['source items'] = array(array(
145         'type' => $item_revision->type,
146         'path' => $item_revision->path,
147         'revision' => $item_revision->source_revision,
148         'git_specific' => array(
149           'item_revision_id' => $item_revision->item_revision_id,
150           'selected_branch_id' => $commit['git_specific']['branch_id'],
151           'selected_date' => $commit['date'],
152         ),
153       ));
154     }
156     $actions[$item_revision->path] = $action;
157   }
159   return $actions;
163  * Implementation of [versioncontrol_backend]_get_directory_item():
164  * Retrieve the item of the deepest-level directory in the repository that is
165  * common to all the changed/branched/tagged items in a commit, branch or
166  * tag operation. In other words, this function gets you the item
167  * for $operation['directory'].
169  * @param $operation
170  *   The commit, branch or tag operation whose deepest-level
171  *   changed/branched/tagged directory should be retrieved.
173  * @return
174  *   The requested directory item. Item values are structured arrays and
175  *   consist of elements with the following keys:
177  *   - 'type': Specifies the item type, which in this case can only be
178  *        VERSIONCONTROL_ITEM_DIRECTORY.
179  *   - 'path': The path of the directory, which will be the same
180  *        as $operation['directory'].
181  *   - 'revision': The (file-level) revision when the item was last changed.
182  *        If there is no such revision (which may be the case for
183  *        directory items) then the 'revision' element is an empty string.
184  *   - '[xxx]_specific': May be set by the backend to remember additional
185  *        item info. ("[xxx]" is the unique string identifier
186  *        of the respective version control system.)
187  */
188 function versioncontrol_git_get_directory_item($operation) {
189   $item = array(
190     'type' => VERSIONCONTROL_ITEM_DIRECTORY,
191     'path' => $operation['directory'],
192     'revision' => '',
193     'git_specific' => array(
194       'selected_date' => $operation['date'],
195     ),
196   );
197   if (isset($operation['git_specific']['branch_id'])) { // it's a commit or branch
198     $item['selected_branch_id'] = $operation['git_specific']['branch_id'];
199   }
200   if (isset($operation['tag_op_id'])) { // it's a tag
201     $item['selected_tag_op'] = $operation;
202   }
203   return $item;
207  * Implementation of [versioncontrol_backend]_get_commit_branches():
208  * Retrieve the branches that have been affected by the given commit.
210  * @return
211  *   An array of strings that identify a branch in the respective repository,
212  *   or an empty array if no branches were affected at all. (For GIT, there
213  *   should always be a exactly one branch in the resulting array.)
214  */
215 function versioncontrol_git_get_commit_branches($commit) {
216   if (!isset($commit['git_specific']['branch_id'])) {
217     return array();
218   }
220   $branch = versioncontrol_get_branch($commit['git_specific']['branch_id']);
221   if (!isset($branch)) {
222     return array(); // should only happen in case of database inconsistencies
223   }
224   return array($branch['branch_name']);
228  * Retrieve the set of items that were affected by a branch operation.
230  * @param $branch
231  *   The branch operation whose items should be retrieved. This is an array
232  *   like the one returned by versioncontrol_get_branch_operation().
234  * @return
235  *   An array of all items that were affected by the branching operation.
236  *   An empty result array means that the whole repository has been branched.
237  *   Item values are structured arrays and consist of elements
238  *   with the following keys:
240  *   - 'type': Specifies the item type, which is either
241  *        VERSIONCONTROL_ITEM_FILE or VERSIONCONTROL_ITEM_DIRECTORY.
242  *   - 'path': The path of the item at the specific revision.
243  *   - 'revision': The (file-level) revision when the item was changed.
244  *        If there is no such revision (which may be the case for
245  *        directory items) then the 'revision' element is an empty string.
246  *   - 'source branch': Optional, may be set by the backend if the
247  *        source branch (the one that this one branched off) can be retrieved.
248  *        If given, this is a string with the original branch name.
249  *   - '[xxx]_specific': May be set by the backend to remember additional
250  *        item info. ("[xxx]" is the unique string identifier of the respective
251  *        version control system.)
252  */
253 function versioncontrol_git_get_branched_items($branch) {
254   $items = array();
255   $result = db_query('SELECT ir.item_revision_id, ir.type, ir.path, ir.revision
256                       FROM {versioncontrol_git_item_branch_points} ib
257                        INNER JOIN {versioncontrol_git_item_revisions} ir
258                         ON ib.item_revision_id = ir.item_revision_id
259                       WHERE ib.branch_op_id = %d', $branch['branch_op_id']);
261   while ($item_revision = db_fetch_object($result)) {
262     $items[] = array(
263       'type' => $item_revision->type,
264       'path' => $item_revision->path,
265       'revision' => $item_revision->revision,
266       'git_specific' => array(
267         'item_revision_id' => $item_revision->item_revision_id,
268         'selected_branch_id' => $branch['git_specific']['branch_id'],
269         'selected_date' => $branch['date'],
270       ),
271     );
272   }
273   return $items;
277  * Retrieve the set of items that were affected by a tag operation.
279  * @param $tag
280  *   The tag operation whose items should be retrieved. This is an array
281  *   like the one returned by versioncontrol_get_tag_operation().
283  * @return
284  *   An array of all items that were affected by the tagging operation.
285  *   An empty result array means that the whole repository has been tagged.
286  *   Item values are structured arrays and consist of elements
287  *   with the following keys:
289  *   - 'type': Specifies the item type, which is either
290  *        VERSIONCONTROL_ITEM_FILE or VERSIONCONTROL_ITEM_DIRECTORY.
291  *   - 'path': The path of the item at the specific revision.
292  *   - 'revision': The (file-level) revision when the item was changed.
293  *        If there is no such revision (which may be the case for
294  *        directory items) then the 'revision' element is an empty string.
295  *   - 'source branch': Optional, may be set by the backend if the
296  *        source branch (the one that this tag comes from) can be retrieved.
297  *        If given, this is a string with the original branch name.
298  *   - '[xxx]_specific': May be set by the backend to remember additional
299  *        item info. ("[xxx]" is the unique string identifier of the respective
300  *        version control system.)
301  */
302 function versioncontrol_git_get_tagged_items($tag) {
303   $items = array();
304   $result = db_query('SELECT ir.item_revision_id, ir.type, ir.path, ir.revision
305                       FROM {versioncontrol_git_item_tags} it
306                        INNER JOIN {versioncontrol_git_item_revisions} ir
307                         ON it.item_revision_id = ir.item_revision_id
308                       WHERE it.tag_op_id = %d', $tag['tag_op_id']);
310   while ($item_revision = db_fetch_object($result)) {
311     $items[] = array(
312       'type' => $item_revision->type,
313       'path' => $item_revision->path,
314       'revision' => $item_revision->revision,
315       'git_specific' => array(
316         'item_revision_id' => $item_revision->item_revision_id,
317         'selected_tag_op' => $tag,
318         'selected_date' => $tag['date'],
319       ),
320     );
321   }
322   return $items;
326  * Implementation of [versioncontrol_backend]_get_current_item_branch():
327  * Retrieve the current branch that this item is in. If this item was part of
328  * the result of versioncontrol_get_commit_actions(), this will probably be
329  * the branch that this item was committed to. The main branch ('HEAD' for GIT)
330  * is also a valid branch and should be expected as return value.
332  * @param $repository
333  *   The repository that the item is located in.
334  * @param $item
335  *   The item whose current branch should be retrieved.
337  * @return
338  *   A string containing the current item branch, or NULL if no branch
339  *   is known or applicable.
340  */
341 function versioncontrol_git_get_current_item_branch($repository, $item) {
342   if (!isset($item['git_specific']['selected_branch_id'])) {
343     return NULL;
344   }
346   $branch = versioncontrol_get_branch($item['git_specific']['selected_branch_id']);
347   if (!isset($branch)) {
348     return NULL;
349   }
350   return $branch['branch_name'];
354  * Implementation of [versioncontrol_backend]_get_current_item_tag():
355  * Retrieve the current tag of this item.
357  * @param $repository
358  *   The repository that the item is located in.
359  * @param $item
360  *   The item whose current branch should be retrieved.
362  * @return
363  *   A tag operation array like the return value
364  *   of versioncontrol_get_tag_operation(), or NULL if no tag
365  *   is known or applicable.
366  */
367 function versioncontrol_git_get_current_item_tag($repository, $item) {
368   if (!isset($item['git_specific']['selected_tag_op'])) {
369     return NULL;
370   }
371   return $item['git_specific']['selected_tag_op'];
375  * Implementation of [vcs_backend]_get_parent_item():
376  * Retrieve the parent (directory) item of a given item.
378  * @param $repository
379  *   The repository that the item is located in.
380  * @param $item
381  *   The item whose parent should be retrieved.
382  * @param $parent_path
383  *   NULL if the direct parent of the given item should be retrieved,
384  *   or a parent path that is further up the directory tree.
386  * @return
387  *   The parent directory item at the same revision as the given item.
388  *   If $parent_path is not set and the item is already the topmost one
389  *   in the repository, the item is returned as is. It also stays the same
390  *   if $parent_path is given and the same as the path of the given item.
391  *   If the given directory path does not correspond to a parent item,
392  *   NULL is returned.
393  */
394 function versioncontrol_git_get_parent_item($repository, $item, $parent_path = NULL) {
395   if (!isset($parent_path)) {
396     $item['path'] = dirname($item['path']);
397     return $item;
398   }
399   else if (strpos($item['path'] .'/', $parent_path .'/') !== FALSE) {
400     $item['path'] = $parent_path;
401     return $item;
402   }
403   return NULL;
408  * Implementation of [versioncontrol_backend]_commit():
409  * Manage (insert or delete) additional commit data in the database.
411  * @param $op
412  *   Either 'insert' when the commit is in the process of being created,
413  *   or 'delete' if it will be deleted after this function has been called.
414  * @param $commit
415  *   A single commit array, like the ones returned
416  *   by versioncontrol_get_commits().
417  * @param $commit_actions
418  *   A structured array containing the exact details of what happened to
419  *   each item in this commit. The structure of this array is the same as
420  *   the return value of versioncontrol_get_commit_actions().
421  */
422 function versioncontrol_git_commit($op, $commit, $commit_actions) {
423   switch ($op) {
424     case 'insert':
425       foreach ($commit_actions as $path => $action) {
426         $revision = '';
427         $source_revision = '';
429         // If available, get item type and revision from the contained items.
430         if (isset($action['current item'])) {
431           $type = $action['current item']['type'];
432           $revision = $action['current item']['revision'];
433         }
434         if (isset($action['source items'])) {
435           $type = $action['source items'][0]['type']; // only one source item for GIT
436           $source_revision = $action['source items'][0]['revision'];
437         }
439         $item_revision_id = db_next_id('{versioncontrol_git_item_revisions}_item_revision_id');
440         db_query(
441           "INSERT INTO {versioncontrol_git_item_revisions}
442            (item_revision_id, commit_id, type, path, revision,
443             action, lines_added, lines_removed, source_revision)
444            VALUES (%d, %d, %d, '%s', '%s', %d, %d, %d, '%s')",
445           $item_revision_id, $commit['commit_id'], $type, $path, $revision,
446           $action['action'], $action['git_specific']['lines_added'],
447           $action['git_specific']['lines_removed'], $source_revision
448         );
449       }
450       break;
452     case 'delete':
453       $result = db_query('SELECT item_revision_id
454                           FROM {versioncontrol_git_item_revisions}
455                           WHERE commit_id = %d', $commit['commit_id']);
457       while ($revision = db_fetch_object($result)) {
458         db_query('DELETE FROM {versioncontrol_git_item_tags}
459                   WHERE item_revision_id = %d',
460                   $revision->item_revision_id);
461         db_query('DELETE FROM {versioncontrol_git_item_branch_points}
462                   WHERE item_revision_id = %d',
463                   $revision->item_revision_id);
464       }
465       db_query('DELETE FROM {versioncontrol_git_item_revisions}
466                 WHERE commit_id = %d', $commit['commit_id']);
467       break;
468   }
472  * Implementation of [versioncontrol_backend]_branch_operation():
473  * Manage (insert or delete) additional branch operation data in the database.
475  * @param $op
476  *   Either 'insert' when the branch operation is in the process of being created,
477  *   or 'delete' if it will be deleted after this function has been called.
478  * @param $branch
479  *   A single branch operation array, like the one returned
480  *   by versioncontrol_get_branch_operation().
481  * @param $branched_items
482  *   An array of all items that are affected by the branching operation.
483  *   Compared to standard item arrays, the ones in here may not have the
484  *   'revision' element set (however, the GIT backend always provides those)
485  *   and can optionally contain a 'source branch' element that specifies
486  *   the original branch name of this item.
487  *   (For $op == 'delete', 'source branch' is never set.)
488  *   An empty $branched_items array means that the whole repository has been
489  *   branched (which is not used for GIT, as branches/tags are always assigned
490  *   to specific files).
491  */
492 function versioncontrol_git_branch_operation($op, $branch, $branched_items) {
493   _versioncontrol_git_branch_or_tag_operation(
494     $op, $branch, $branched_items, 'item_branch_points', 'branch_op_id'
495   );
499  * Implementation of [versioncontrol_backend]_tag_operation():
500  * Manage (insert or delete) additional tag operation data in the database.
502  * @param $op
503  *   Either 'insert' when the tag operation is in the process of being created,
504  *   or 'delete' if it will be deleted after this function has been called.
505  * @param $tag
506  *   A single tag operation array, like the one returned
507  *   by versioncontrol_get_tag_operation().
508  * @param $tagged_items
509  *   An array of all items that are affected by the tagging operation.
510  *   Compared to standard item arrays, the ones in here may not have the
511  *   'revision' element set (however, the GIT backend always provides those)
512  *   and can optionally contain a 'source branch' element that specifies
513  *   the original branch name of this item.
514  *   (For $op == 'move' or $op == 'delete', 'source branch' is never set.)
515  *   An empty $tagged_items array means that the whole repository has been
516  *   tagged (which is not used for GIT, as branches/tags are always assigned
517  *   to specific files).
518  */
519 function versioncontrol_git_tag_operation($op, $tag, $tagged_items) {
520   _versioncontrol_git_branch_or_tag_operation(
521     $op, $tag, $tagged_items, 'item_tags', 'tag_op_id'
522   );
526  * The implementation of the branch and tag operation hooks is essentially
527  * the same (just different table and primary key names),
528  * so let's share the code in this function.
529  */
530 function _versioncontrol_git_branch_or_tag_operation($op, $branch_or_tag, $items, $table_name, $primary_key_name) {
531   switch ($op) {
532     case 'insert':
533       foreach ($items as $item) {
534         $result = db_query("SELECT item_revision_id
535                             FROM {versioncontrol_git_item_revisions}
536                             WHERE path = '%s' AND revision = '%s'",
537                             $item['path'], $item['revision']);
539         while ($revision = db_fetch_object($result)) {
540           db_query('INSERT INTO {versioncontrol_git_'. $table_name .'}
541                     ('. $primary_key_name .', item_revision_id)
542                     VALUES (%d, %d)',
543                     $branch_or_tag[$primary_key_name],
544                     $revision->item_revision_id);
545         }
546       }
547       break;
549     case 'delete':
550       db_query('DELETE FROM {versioncontrol_git_'. $table_name .'}
551                 WHERE '. $primary_key_name .' = %d',
552                 $branch_or_tag[$primary_key_name]);
553       break;
554   }
558  * Implementation of [versioncontrol_backend]_account():
559  * Manage (insert, update or delete) additional GIT user account data
560  * in the database.
562  * @param $op
563  *   Either 'insert' when the account is in the process of being created,
564  *   or 'update' when username or additional module data change,
565  *   or 'delete' if it will be deleted after this function has been called.
566  * @param $uid
567  *   The Drupal user id corresponding to the VCS account.
568  * @param $username
569  *   The VCS specific username (a string).
570  * @param $repository
571  *   The repository where the user has its VCS account.
572  * @param $additional_data
573  *   An array of additional author information.
574  */
575 function versioncontrol_git_account($op, $uid, $username, $repository, $additional_data = array()) {
576   $git_specific = $additional_data['git_specific'];
578   switch ($op) {
579     case 'insert':
580       if (!isset($git_specific) || !isset($git_specific['password'])) {
581         drupal_set_message(t('Error: no GIT password given on account creation!'), 'error');
582         return;
583       }
584       db_query("INSERT INTO {versioncontrol_git_accounts}
585                 (uid, repo_id, password)
586                 VALUES (%d, %d, '%s')",
587                 $uid, $repository['repo_id'], $git_specific['password']);
588       break;
590     case 'update':
591       if (!isset($git_specific) || !isset($git_specific['password'])) {
592         return; // the user didn't update the password in the process.
593       }
594       db_query("UPDATE {versioncontrol_git_accounts}
595                 SET password = '%s'
596                 WHERE uid = %d AND repo_id = %d",
597                 $git_specific['password'], $uid, $repository['repo_id']);
599       if (!user_access('administer version control systems')) {
600         // Admins get "The account has been updated successfully" anyways.
601         drupal_set_message(t('The GIT password has been updated successfully.'));
602       }
603       break;
605     case 'delete':
606       db_query('DELETE FROM {versioncontrol_git_accounts}
607                 WHERE uid = %d AND repo_id = %d',
608                 $uid, $repository['repo_id']);
609       break;
610   }
614  * Implementation of [vcs_backend]_import_accounts():
615  * Import accounts into a repository, given text data from the accounts file.
616  * No accounts are deleted, new accounts are inserted, and existing accounts
617  * are updated with imported ones.
619  * @param $repository
620  *   The repository where the accounts will be imported.
621  * @param $data
622  *   The contents of the "account data" text area where the user has to
623  *   enter/copy the contents of the version control system's accounts file.
624  */
625 function versioncontrol_git_import_accounts($repository, $data) {
626   $lines = explode("\n", $data);
627   $names = array();
629   foreach ($lines as $line) {
630     if (preg_match('/^\s*(#.*)?$/', $line)) { // filter out empty and commented lines
631       continue;
632     }
633     // Extract the account information and create or update the user accounts.
634     list($username, $password, $run_as_user) = explode(':', $line);
635     if (!empty($username) && !empty($password)) {
636       $additional_data = array(
637         'git_specific' => array('password' => $password),
638       );
639       $uid = versioncontrol_get_account_uid_for_username($repository['repo_id'], $username, TRUE);
641       if (isset($uid)) {
642         versioncontrol_update_account($repository, $uid, $username, $additional_data);
643         $names[] = t('updated !username', array('!username' => $username));
644       }
645       else {
646         $uid = db_result(db_query("SELECT uid FROM {users} WHERE name = '%s'", $username));
647         if ($uid) {
648           versioncontrol_insert_account($repository, $uid, $username, $additional_data);
649           $names[] = t('added !username', array('!username' => $username));
650         }
651         else {
652           $names[] = t('didn\'t add !username (no matching Drupal username exists)',
653                        array('!username' => $username));
654         }
655       }
656     }
657   }
659   if (empty($names)) {
660     drupal_set_message(t('Failed to import GIT accounts.'), 'error');
661   }
662   else {
663     drupal_set_message(theme('item_list', $names, t('The import of GIT accounts has been completed successfully:')));
664   }
668  * Implementation of [vcs_backend]_export_accounts():
669  * Export accounts of a repository to text data that is suitable for
670  * copying to the version control system's accounts file.
672  * @param $repository
673  *   The repository whose accounts will be exported.
674  * @param $accounts
675  *   The list (array) of accounts that should be exported, given in the same
676  *   format as the return value of versioncontrol_get_accounts().
677  *   All accounts in this list are from the above repository.
679  * @return
680  *   The exported textual representation of the account list.
681  */
682 function versioncontrol_git_export_accounts($repository, $accounts) {
683   if (empty($accounts)) {
684     return '# '. t('no user accounts available to export');
685   }
687   $accounts_flat = array();
688   $uid_constraints = array();
689   $params = array($repository['repo_id']);
691   foreach ($accounts as $uid => $usernames_per_repository) {
692     foreach ($usernames_per_repository as $repo_id => $username) {
693       $accounts_flat[$uid] = array('uid' => $uid, 'username' => $username);
694       $uid_constraints[] = 'uid = %d';
695       $params[] = $uid;
696     }
697   }
699   $result = db_query('SELECT uid, password FROM {versioncontrol_git_accounts}
700                       WHERE repo_id = %d
701                        AND ('. implode(' OR ', $uid_constraints) .')',
702                       $params);
703   while ($account = db_fetch_object($result)) {
704     $accounts_flat[$account->uid]['password'] = $account->password;
705   }
707   $run_as_user = '';
708   if (!empty($repository['run_as_user'])) {
709     $run_as_user = ':'. $repository['run_as_user'];
710   }
711   $data = '';
712   foreach ($accounts_flat as $uid => $account) {
713     $data .= '# '. url('user/'. $uid, NULL, NULL, TRUE) ."\n";
714     $data .= $account['username'] .':'. $account['password'] . $run_as_user ."\n\n";
715   }
716   return $data;