initial version

This commit is contained in:
Carney Wu
2022-01-19 17:10:17 +08:00
parent b4a0409675
commit aec73246ec
216 changed files with 9268 additions and 1666 deletions

74
README.md Normal file
View File

@@ -0,0 +1,74 @@
# 欢迎使用 CodeFever Community 版本
英文版本文档正在完善中, 欢迎 [提交文档](doc/zh-cn/contribute/doc_pr.md)
[English](doc/en-us/start/welcome.md)
### 关于 CodeFever
`CodeFever` 项目起初由 蒲公英开发者服务平台 开发和维护,项目于 2020 年 6 月上线 [https://codefever.pgyer.com/](https://codefever.pgyer.com/) 。 经过将近两年时间打磨和稳定性验证,于 2022 年 2 月开源,接受社区的考验。
`CodeFever` 开源后,开源版本称为 `CodeFever Conmmunity` 版本。同时公有云版本继续服务,继续称为 `CodeFever。`
`CodeFever Community``英蒲公英开发者服务平台` 团队完整自主研发并基于 `MIT` 协议进行完整开源,拥有完全自主的知识产权,因此您可以放心使用而不必考虑是否侵犯他人权利。
`CodeFever Community` 保留了 `CodeFever` 的大部分功能,并且进行了更适合单机部署的优化。`CodeFever Community` 也会随 `CodeFever` 更新的同时接受来自社区的提交。
### 如何安装
`CodeFever` 提供 `从头开始安装``Docker 镜像安装` 两种安装方式, 可以根据自己的实际需要选择安装方式。
满足以下要求的用户和选择 `从头开始安装` 的方式进行安装。
- 学习和技术交流
- 需要做定制化修改
- `Docker 镜像安装` 不能满足处理 `Bug` 和提交 `PR` 的需求
- Docker 镜像不能在当前 `操作系统``硬件架构` 上使用
参照: [从头开始安装](doc/zh-cn/installation/install_from_scratch.md)
如果不满足上述要求,你可以选择使用 `Docker 镜像安装` 方式安装 `CodeFever Community`
参照: [Docker 镜像安装](doc/zh-cn/installation/install_via_docker.md)
### 使用
[仓库](doc/zh-cn/reposiotry)
[仓库组](doc/zh-cn/reposiotry_group)
[管理员设置](doc/zh-cn/admin)
[Git](doc/zh-cn/git)
[常见问题](doc/zh-cn/common)
### 问题反馈
如果你在使用过程中遇到期望外的结果,欢迎提交 `Issue`
参照: [提交问题](doc/zh-cn/contribute/bug_fix_issue.md)
如果你希望在 `CodeFever Community` 添加一些特性,也欢迎提交 `Issue`
参照: [提交特性请求](doc/zh-cn/contribute/request_feature_issue.md)
### 贡献代码
欢迎提交 `PR`, 请确定修复 `Bug` 后或者新增 `Feature` 后进行适当的测试。
如果 `PR` 内容是问题,请先提交 `Issue` 并在提交 PR 时引用该 `Issue`
参照: [提交问题修复](doc/zh-cn/contribute/bug_fix_pr.md)
如果 `PR` 内容是新特性,请在 `PR` 中请尽量详细描述此特性的内容,如果此 PR 是针对某个特性请求的提交,在提交 PR 时引用该 `Issue`
参照: [提交新特性](doc/zh-cn/contribute/new_feature_pr.md)
### 贡献文档
欢迎指正文档中的错误或参加翻译工作
提交 `PR` 前请确定文档内容表述是否准确
参照: [提交文档](doc/zh-cn/contribute/doc_pr.md)

View File

@@ -8,5 +8,5 @@ filepath="$filepath/www/index.php"
RUNNING_STATUS=$(ps aux | grep 'codefever_schedule' | grep -v 'grep' | grep -v 'sh' | wc -l)
if [ $RUNNING_STATUS -lt 1 ]
then
nohup /usr/local/php7/bin/php $filepath backend/codefever_schedule run > /dev/null &
nohup /usr/local/php/bin/php $filepath backend/codefever_schedule run > /dev/null &
fi

View File

@@ -19,12 +19,7 @@ define('DIR_READ_MODE', 0755);
define('DIR_WRITE_MODE', 0777);
if (ENVIRONMENT == 'development') {
// Api-Key
define('API_TOKEN_GATEWAY', 'codefever_community_api_token');
} else if (ENVIRONMENT == 'testing') {
// Api-Key
define('API_TOKEN_GATEWAY', 'c106071b24e81263b2a43280bc477e0f');
// unit test: user test, group codefever, repository codefever
define('TESTING_USER_KEY', 'f4a9c54adef17599d4f709a1167f0fcd');
define('TESTING_GROUP_KEY', 'e6508c1a66e86f697c96bdffb30c1ae3');
@@ -49,8 +44,6 @@ if (ENVIRONMENT == 'development') {
define('TESTING_COMMON_PAGE', 1);
define('TESTING_COMMON_PERPAGE', 10);
} else {
// Api-Key
define('API_TOKEN_GATEWAY', 'cca45617074ca8b04c41a316a453e1ff');
}
/*
@@ -138,7 +131,7 @@ define('PRESERVE_URI', [
'/^(api|apiv\d+|apis)$/i',
'/^(userInfo|userInfos)$/i',
'/^(pay|payment|transaction|order|coupon|invoice)$/i',
'/^(group|groups|repository|repositories|setting|settings|mergerequest|mergerequests)$/i',
'/^(group|groups|repository|repositories|setting|settings|mergerequest|mergerequests|admin)$/i',
]);
// Git Command type

View File

@@ -48,7 +48,7 @@
$active_group = 'default';
$active_record = TRUE;
$db['default']['hostname'] = YAML_MYSQL_HOST;
$db['default']['hostname'] = YAML_MYSQL_HOST . ':' . YAML_MYSQL_PORT;
$db['default']['port'] = YAML_MYSQL_PORT;
$db['default']['username'] = YAML_MYSQL_USERNAME;
$db['default']['password'] = YAML_MYSQL_PASSWORD;

View File

@@ -108,15 +108,21 @@ class User extends Base
$userInfo = Request::parse()->authData['userData'];
$data = Request::parse()->parsed;
$uKey = $userInfo['u_key'];
$field = $data['field'];
$value = $data['value'];
if (in_array($field, ['name', 'email']) && !$value) {
$name = $data['name'];
$email = $data['email'];
$team = $data['team'];
$role = $data['role'];
if (!$name || !$email) {
Response::reject(0x0201);
}
$updateData = [];
$updateData['u_' . $field] = $value;
$updateData['u_name'] = $name;
$updateData['u_email'] = $email;
$updateData['u_team'] = $team;
$updateData['u_role'] = $role;
$updateData['u_updated'] = date("Y-m-d H:i:s");
$this->db->where('u_key', $uKey);
$this->db->update('users', $updateData);

View File

@@ -58,13 +58,17 @@ class Internal extends Base
// search member role in repository and group
$roleID = $this->service->getRepositoryRole($repositoryData['r_key'], $data['userID']);
$action = $roleID && UserAccessController::checkRepositoryAction($roleID, $data['action']);
Response::output([
'uid' => implode('|', [$data['userID'], $repositoryData['r_key']]),
'path' => $this->_storagePath . $repositoryData['r_path'],
'action' => $action ? '1' : '0',
]);
if ($roleID) {
if (UserAccessController::checkRepositoryAction($roleID, $data['action'])) {
Response::output([
'uid' => implode('|', [$data['userID'], $repositoryData['r_key']]),
'path' => $this->_storagePath . $repositoryData['r_path'],
]);
}
}
// member not found
Response::reject(0x0106);
}
}
// group or repository not found

134
application/controllers/doc.php Executable file
View File

@@ -0,0 +1,134 @@
<?php
require_once APPPATH . '/controllers/base.php';
class Doc extends Base
{
public function __construct()
{
$path = $_SERVER['PATH_INFO'];
if (!preg_match('/\.[a-z0-9]+$/i', $path)) {
if ($path === '/doc' || $path === '/doc/') {
header("Location: /doc/cn/");
} else if (substr($path, -1) !== '/') {
header("Location: {$path}/");
exit();
}
}
parent::__construct();
}
public function index ()
{
// doing nothing
}
public function cn()
{
$this->_detail('zh-cn');
}
public function en()
{
$this->_detail('en-us');
}
private function _detail(string $docPath)
{
$path = $this->uri->uri_string;
$segments = explode('/', $path);
$fileName = $segments[count($segments) - 1];
$rootDir = implode('/', [dirname(APPPATH), 'doc', $docPath]) . '/';
$docDir = array_slice($segments, 2);
$docFile = $rootDir . implode('/', $docDir);
if (preg_match('/^\w{32}\.png$/', $fileName)) {
$this->_assets($docFile);
exit;
}
if (!file_exists($docFile)) {
header("Location: /doc/{$segments[1]}/");
exit;
}
if (strpos($fileName, '.md')) {
$docDir = array_slice($segments, 2, -1);
}
$menuFile = $rootDir . 'index.md';
$menu = $this->_extractMenu($menuFile);
$menuOptions = $menu;
foreach ($menu as &$item) {
$menuFile = $rootDir . $item['path'] . '/index.md';
if (!file_exists($menuFile)) {
continue;
}
$submenu = $this->_extractMenu($menuFile);
foreach ($submenu as &$item2) {
$item2['path'] = $item['path'] . '/' . $item2['path'];
}
$item['submenu'] = $submenu;
}
if (!preg_match('/\.[a-z0-9]+/i', $docFile)) {
$menuOptions = $this->_extractMenu($docFile . '/index.md');
$pathinfo = $_SERVER['PATH_INFO'];
while ($menu && count($menuOptions) > 0) {
if (strpos($menuOptions[0]['path'], '.') > 0) {
$pathinfo = rtrim($pathinfo, '/');
$pathinfo .= '/' . $menuOptions[0]['path'];
header("Location: $pathinfo");
break;
} else {
$pathinfo = rtrim($pathinfo, '/');
$pathinfo .= '/' . $menuOptions[0]['path'];
$docFile .= '/' . $menuOptions[0]['path'];
$menuOptions = $this->_extractMenu($docFile . '/index.md');
}
}
}
$this->load->view('doc/detail', [
'segments' => $segments,
'menu' => $menu,
'doc' => str_replace('`', '\`', file_get_contents($docFile)),
]);
}
private function _extractMenu(string $menuFilePath)
{
$data = file_get_contents($menuFilePath);
$output = [];
$lines = explode("\n", $data);
foreach ($lines as $line) {
$matches = [];
if (preg_match('/\[([^\]]+)\]\(([^\)]+)\)/', $line, $matches)) {
array_push($output, ['name' => $matches[1], 'path' => trim($matches[2])]);
}
}
return $output;
}
private function _assets($path)
{
if (!file_exists($path)) {
exit;
}
$finfo = finfo_open(FILEINFO_MIME); // 返回 mime 类型
$mimeType = finfo_file($finfo, $path);
$contentType = explode(';', $mimeType)[0];
finfo_close($finfo);
header('Content-type: ' . $contentType);
$file = fopen($path, 'r');
echo fread($file, filesize($path));
fclose($file);
}
}

View File

@@ -11,10 +11,6 @@ class Home extends Base
public function index()
{
if (REQUEST_PROTOCOL != 'https' && ENVIRONMENT == 'production') {
return header('Location: ' . YAML_HOST);
}
if ($this->getLoggedKey()) {
return header('Location: /repositories');
}

View File

@@ -56,6 +56,8 @@ class ActivityHandler extends EventHandler
function capture(Event $event)
{
$this->CI->load->model('Repository_model', 'repositoryModel');
// filter event type
$eventType = $event->type;
if(in_array($eventType, $this->ListenedEventList)) {
@@ -136,7 +138,6 @@ class ActivityHandler extends EventHandler
$insertData['u_key'] = $event->user;
$insertData['a_relative_r_key'] = $event->data->rKey;
$this->CI->load->model('Repository_model', 'repositoryModel');
$repositoryData = $this->CI->repositoryModel->get($insertData['a_relative_r_key']);
$insertData['a_relative_g_key'] = $repositoryData['g_key'];
@@ -160,6 +161,7 @@ class ActivityHandler extends EventHandler
]);
$this->CI->db->insert('activities', $insertData);
$this->CI->repositoryModel->update($insertData['a_relative_r_key'], ['r_updated' => date('Y-m-d H:i:s')]);
return TRUE;
}
@@ -309,6 +311,7 @@ class ActivityHandler extends EventHandler
}
$this->CI->db->insert('activities', $insertData);
$this->CI->repositoryModel->update($event->data->rKey, ['r_updated' => date('Y-m-d H:i:s')]);
return TRUE;
}

View File

@@ -1,7 +1,10 @@
<?php
$lang['base_cn'] = '中文';
$lang['base_en'] = 'English';
$lang['base_message'] = 'Message';
$lang['base_error_message'] = 'Error message';
$lang['base_error_message_tips'] = 'Sorry, please try again to visit the site, or refresh retry';
$lang['base_go_home'] = 'Home';
$lang['base_go_prepage'] = 'Return to previous page';
$lang['base_confirm'] = 'Confirm';
$lang['base_directory'] = 'Directory';

View File

@@ -1,7 +1,10 @@
<?php
$lang['base_cn'] = '中文';
$lang['base_en'] = 'English';
$lang['base_message'] = '消息';
$lang['base_error_message'] = '错误提示';
$lang['base_error_message_tips'] = '抱歉,请重新尝试访问网站,或者刷新重试';
$lang['base_go_home'] = '返回主页';
$lang['base_go_prepage'] = '返回上一页';
$lang['base_confirm'] = '确定';
$lang['base_directory'] = '目录';

Binary file not shown.

View File

@@ -15,7 +15,7 @@ class APIAuth
static function auth(array $authTypes)
{
if (in_array(self::AUTH_TYPE_GATEWAY, $authTypes)) {
if (Request::parse()->token === API_TOKEN_GATEWAY) {
if (Request::parse()->token === YAML_GATEWAY_TOKEN) {
Request::setAuthData([]);
return TRUE;
}

View File

@@ -1,68 +0,0 @@
<?php
namespace service\Utility;
class QueryParse {
static public $handler = [];
static public function registerHandler(string $name, $handler)
{
self::$handler[$name] = $handler;
}
static public function parse(array $query)
{
$result = [];
foreach ($query as $key => $conditionStr) {
$result[$key] = null;
if (is_array($conditionStr)) {
$result[$key] = self::parse($conditionStr);
} else if (is_string($conditionStr)) {
$condition = self::_parseCondition($conditionStr, $result);
if ($condition && self::$handler[$condition[0]]) {
$result[$key] = self::$handler[$condition[0]]->handle($condition[1]);
}
}
}
return $result;
}
static private function _parseCondition(string $conditionStr, array $data)
{
if (!preg_match('/^([a-z_$][a-z0-9_$]*)\((.*)\)$/i', $conditionStr, $match)) {
return FALSE;
}
$result = [$match[1], []];
$conditions = explode('|', $match[2]);
foreach ($conditions as $item) {
$keyValue = explode('=', $item);
if (count($keyValue) != 2) {
continue;
}
if (strpos($keyValue[1], '.') !== FALSE) {
$vals = explode('.', $keyValue[1]);
$keyValue[1] = $data;
foreach ($vals as $item) {
if (!$keyValue[1][$item]) {
break;
}
$keyValue[1] = $keyValue[1][$item];
}
} else if (preg_match('/^\[(.*)\]$/', $keyValue[1], $match)) {
$keyValue[1] = explode(',', $match[1]);
}
$result[1][$keyValue[0]] = $keyValue[1];
}
return $result;
}
}

View File

@@ -14,9 +14,6 @@ spl_autoload_register(function ($className) {
} else if (preg_match( '/^service\\\\EventHandler\\\\(\w+)$/', $className, $matches)) {
// load event handler
$file = APPPATH . 'event_handlers/'.$matches[1].'.php';
} else if (preg_match( '/^service\\\\QueryHandler\\\\(\w+)$/', $className, $matches)) {
// load event handler
$file = APPPATH . 'query_handlers/'.$matches[1].'.php';
} else if(preg_match( '/^service/', $className)) {
// load library file
$file = APPPATH . 'libraries/' . str_replace('\\', '/', $className);

View File

@@ -346,6 +346,7 @@ class Group_model extends CI_Model
$this->db->from('groups');
$keyword && $this->db->like('g_display_name', $keyword);
$this->db->where('g_status', COMMON_STATUS_NORMAL);
if ($count) {
return $this->db->count_all_results();

View File

@@ -915,7 +915,8 @@ class Repository_model extends CI_Model
'r_display_name' => $displayName,
'r_name' => $name,
'r_path' => '/' . UUID::getKey(),
'r_description' => $description
'r_description' => $description,
'r_updated' => date('Y-m-d H:i:s'),
];
$result = $this->db->insert('repositories', $insertData);
@@ -2067,6 +2068,7 @@ class Repository_model extends CI_Model
$this->db->from('repositories');
$keyword && $this->db->like('r_display_name', $keyword);
$this->db->where('r_status', COMMON_STATUS_NORMAL);
if ($count) {
return $this->db->count_all_results();

View File

@@ -1,63 +0,0 @@
<?php
namespace service\QueryHandler;
class JoinGroupRepoQueryHandler extends QueryHandler {
protected $table_name = 'groups';
protected $primary_key = 'g_key';
protected $field_map = [
'g_key' => 'gid',
'u_key' => 'uid',
'g_created' => 'create',
'g_status' => 'status',
'group_members.u_key' => 'groupMembers.uid'
];
public function custom($condition)
{
$this->CI->db->select('groups.*');
$this->CI->db->join('group_members', 'group_members.g_key=groups.g_key', 'LEFT');
$this->CI->db->where('g_status !=', COMMON_STATUS_DELETE);
$this->CI->db->where('gm_status', COMMON_STATUS_NORMAL);
}
public function after(array $data)
{
if (!$data) {
return $data;
}
$this->CI->load->model('Repository_model', 'repositoryModel');
$repositories = [];
foreach ($data as $item) {
$repositories = array_merge($repositories, $this->CI->repositoryModel->listInGroup($item['gid']));
}
$final = [];
foreach ($repositories as &$repository) {
$repo = [
'rid' => $repository['r_key'],
'uid' => $repository['u_key'],
'name' => $repository['r_display_name'],
'created' => $repository['r_created'],
'updated' => $repository['r_updated'],
'defaultBranchName' => $repository['r_default_branch_name'],
];
$branches = [];
if (!$repo['defaultBranchName']) {
$branches = $this->CI->repositoryModel->getBranchList($repo['rid'], $repo['uid']);
}
$repo['commit'] = (int) $this->CI->repositoryModel->getCommitCount(
$repo['rid'],
$repo['uid'],
$repo['defaultBranchName'] ? $repo['defaultBranchName'] : ($branches[0] ? $branches[0] : '')
);
array_push($final, $repo);
}
return $final;
}
}

View File

@@ -1,49 +0,0 @@
<?php
namespace service\QueryHandler;
class JoinRepoQueryHandler extends QueryHandler {
protected $table_name = 'repositories';
protected $primary_key = 'r_key';
protected $field_map = [
'r_key' => 'rid',
'u_key' => 'uid',
'r_display_name' => 'name',
'r_default_branch_name' => 'defaultBranchName',
'r_created' => 'created',
'r_updated' => 'updated',
'r_status' => 'status',
'repository_members.u_key' => 'repositoryMembers.uid'
];
public function custom($condition)
{
$this->CI->db->select('repositories.*');
$this->CI->db->join('repository_members', 'repository_members.r_key=repositories.r_key', 'left');
$this->CI->db->where('r_status !=', COMMON_STATUS_DELETE);
$this->CI->db->where('rm_status', COMMON_STATUS_NORMAL);
}
public function after(array $data)
{
if (!$data) {
return $data;
}
$this->CI->load->model('Repository_model', 'repositoryModel');
foreach ($data as &$repository) {
$branches = [];
if (!$repository['defaultBranchName']) {
$branches = $this->CI->repositoryModel->getBranchList($repository['rid'], $repository['uid']);
}
$repository['commit'] = (int) $this->CI->repositoryModel->getCommitCount(
$repository['rid'],
$repository['uid'],
$repository['defaultBranchName'] ? $repository['defaultBranchName'] : ($branches[0] ? $branches[0] : '')
);
}
return $data;
}
}

View File

@@ -1,59 +0,0 @@
<?php
namespace service\QueryHandler;
class OwnGroupRepoQueryHandler extends QueryHandler {
protected $table_name = 'groups';
protected $primary_key = 'g_key';
protected $field_map = [
'g_key' => 'gid',
'u_key' => 'uid',
'g_created' => 'create',
'g_status' => 'status',
];
public function custom($condition)
{
$this->CI->db->where('g_status !=', COMMON_STATUS_DELETE);
}
public function after(array $data)
{
if (!$data) {
return $data;
}
$this->CI->load->model('Repository_model', 'repositoryModel');
$repositories = [];
foreach ($data as $item) {
$repositories = array_merge($repositories, $this->CI->repositoryModel->listInGroup($item['gid']));
}
$final = [];
foreach ($repositories as &$repository) {
$repo = [
'rid' => $repository['r_key'],
'uid' => $repository['u_key'],
'name' => $repository['r_display_name'],
'created' => $repository['r_created'],
'updated' => $repository['r_updated'],
'defaultBranchName' => $repository['r_default_branch_name'],
];
$branches = [];
if (!$repo['defaultBranchName']) {
$branches = $this->CI->repositoryModel->getBranchList($repo['rid'], $repo['uid']);
}
$repo['commit'] = (int) $this->CI->repositoryModel->getCommitCount(
$repo['rid'],
$repo['uid'],
$repo['defaultBranchName'] ? $repo['defaultBranchName'] : ($branches[0] ? $branches[0] : '')
);
array_push($final, $repo);
}
return $final;
}
}

View File

@@ -1,45 +0,0 @@
<?php
namespace service\QueryHandler;
class OwnRepoQueryHandler extends QueryHandler {
protected $table_name = 'repositories';
protected $primary_key = 'r_key';
protected $field_map = [
'r_key' => 'rid',
'u_key' => 'uid',
'r_display_name' => 'name',
'r_default_branch_name' => 'defaultBranchName',
'r_created' => 'created',
'r_updated' => 'updated',
'r_status' => 'status',
];
public function custom($condition)
{
$this->CI->db->where('r_status !=', COMMON_STATUS_DELETE);
}
public function after(array $data)
{
if (!$data) {
return $data;
}
$this->CI->load->model('Repository_model', 'repositoryModel');
foreach ($data as &$repository) {
$branches = [];
if (!$repository['defaultBranchName']) {
$branches = $this->CI->repositoryModel->getBranchList($repository['rid'], $repository['uid']);
}
$repository['commit'] = (int) $this->CI->repositoryModel->getCommitCount(
$repository['rid'],
$repository['uid'],
$repository['defaultBranchName'] ? $repository['defaultBranchName'] : ($branches[0] ? $branches[0] : '')
);
}
return $data;
}
}

View File

@@ -1,89 +0,0 @@
<?php
namespace service\QueryHandler;
class QueryHandler {
protected $table_name = '';
protected $primary_key = '';
protected $field_map = [];
protected $CI = null;
// "user":"user(id=xxx)"
// "app":"app(user=user.id|limit=10order_by=create,DESC)"
// "userList":"user(id=[xxx,yyy,zzz])"
public function handle(array $condition)
{
$isList = array_keys($condition)[0] != $this->primary_key || strlen($condition[$this->primary_key]) != 32;
$this->CI = & get_instance();
foreach ($condition as $key => $item) {
$key = $this->_getActualField($key);
if ($key === 'limit') {
$limit = explode(',', $item);
$this->CI->db->limit($limit[0], $limit[1] ? $limit[1] : 0);
} else if ($key === 'order_by') {
$orderby = explode(',', $item);
$orderby[0] = $this->_getActualField($orderby[0]);
$this->CI->db->order_by($orderby[0], $orderby[1] ? $orderby[1] : 'ASC');
} else if (is_array($item)) {
$this->CI->db->where_in($key, $item);
} else {
$this->CI->db->where($key, $item);
}
}
$this->custom($condition);
$query = $this->CI->db->get($this->table_name);
if ($isList) {
$result = $query->result_array();
} else {
$result = $query->row_array();
}
$result = $this->normalize($result, $isList);
$result = $this->after($result);
return $result;
}
private function _getActualField(string $key)
{
if (!$key || !$this->field_map || !in_array($key, $this->field_map)) {
return $key;
}
return array_search($key, $this->field_map);
}
public function normalize(array $data, bool $isList) {
if (!$data) {
return $data;
}
if (!$isList) {
$data = [$data];
}
$final = [];
foreach ($data as $item) {
$itemFinal = [];
foreach ($item as $key => $val) {
if ($this->field_map[$key]) {
$itemFinal[$this->field_map[$key]] = $val;
}
}
array_push($final, $itemFinal);
}
return $isList ? $final : $final[0];
}
public function custom($condition) {}
public function after(array $data) {
return $data;
}
}

130
application/views/doc/detail.php Executable file
View File

@@ -0,0 +1,130 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Codefever community - Doc</title>
<?php include TPLPATH . 'newpage/header_include.php'; ?>
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-2 bg-EFF3F8 color-2F354D doc-left">
<div><img class="doc-logo" src="/static/images/logo-community-doc.png" /></div>
<p class="weight-600"><?php echo lang('base_directory'); ?></p>
<?php foreach ($menu as $item) { ?>
<div class="menu opened">
<div class="menu-first flexRowRowCenter">
<span class="switch"></span>
<span class="ml-16"><?php echo $item['name']; ?></span>
</div>
<?php if ($item['submenu']) { ?>
<div class="menu-second">
<?php foreach ($item['submenu'] as $item2) { ?>
<div class="ml-50"><a class="normal-link" href="/doc/<?php echo $segments[1] . '/' . $item2['path']; ?>"><?php echo $item2['name']; ?></a></div>
<?php } ?>
</div>
<?php } ?>
</div>
<?php } ?>
</div>
<div class="col-md-10 doc-right">
<div class="header flexRowCenter justifyContent">
<div class="title"><h1></h1></div>
<div>
<span class="lang mr-50">
<div class="flexRowCenter justifyContent">
<span><?php echo $segments[1] == 'cn' ? lang('base_cn') : lang('base_en'); ?></span>
<span class="switch"></span>
</div>
<div class="lang-menu">
<div><a class="normal-link <?php echo $segments[1] == 'cn' ? 'active' : ''; ?>" href="<?php echo '/doc/cn/' . implode('/', array_slice($segments, 2)); ?>"><?php echo lang('base_cn'); ?></a></div>
<div><a class="normal-link <?php echo $segments[1] == 'en' ? 'active' : ''; ?>" href="<?php echo '/doc/en/' . implode('/', array_slice($segments, 2)); ?>"><?php echo lang('base_en'); ?></a></div>
</div>
</span>
<a class="btn" href="/user/login"><?php echo lang('user_form_page_title'); ?></a>
<a class="btn btn-self-primary" href="/user/register"><?php echo lang('user_form_register_page_title'); ?></a>
</div>
</div>
<div class="doc-content flexRowCenter justifyContent alignStart">
<div class="doc-content-left">
<div class="container doc markdown-body"></div>
</div>
<div class="doc-nav"></div>
</div>
</div>
</div>
</div>
<?php include TPLPATH . 'newpage/footer_include.php'; ?>
<script>
$(function () {
var tops = [];
var scrollType = 0;
var doc = `<?php echo $doc; ?>`;
$('.doc').append(marked.marked(doc));
$('.menu-first').on('click', function () {
$(this).parent().toggleClass('opened');
});
$('.lang').on('mouseenter', function () {
$(this).addClass('opened');
}).on('mouseleave', function () {
$(this).removeClass('opened');
});
if (doc) {
$('.title h1').text($('.doc h1').text());
$('.doc h3').each(function (index, item) {
$('.doc-nav').append('<div><a href="#' + $(item).attr('id') + '">' + item.innerText + '</a></div>');
tops[index] = $(this).position().top;
});
$('.doc-nav a').on('click', function () {
scrollType = 1;
$('.doc-nav a').removeClass('active');
$(this).addClass('active');
}).eq(0).addClass('active');
$('.doc-right').on('scroll', function () {
var height = $(this).innerHeight();
var contentHeight = $('.doc-content').innerHeight() + 64;
var scrollTop = $(this).scrollTop();
var progress = scrollTop / (contentHeight - height) * 100;
$('.title h1').css('display', scrollTop > 64 ? 'block' : 'none');
scrollTop > 0 ? $('.header').addClass('shadow') : $('.header').removeClass('shadow');
if (scrollType == 1) {
$(this).scrollTop(scrollTop - 64);
scrollType = 2;
return true;
} else if (scrollType == 2) {
scrollType = 0;
return true;
}
for (var i = 0; i < tops.length - 1; i++) {
var progress2 = tops[i] / contentHeight * 100;
if (progress2 > progress) {
break;
}
}
$('.doc-nav a').removeClass('active');
$('.doc-nav a').eq(i).addClass('active');
});
}
});
</script>
</body>
</html>

View File

@@ -5,6 +5,7 @@
<script src="/static/<?php echo RELEASENUMBER;?>/script/scrollAnimation.js" type="text/javascript"></script>
<script src="/static/vendor/intltel/js/intlTelInput.js"></script>
<script src="/static/vendor/toastr/toastr.js" type="text/javascript"></script>
<script src="/static/<?php echo RELEASENUMBER;?>/script/marked.min.js" type="text/javascript"></script>
<script>
function pregEmail(email) {

View File

@@ -3,11 +3,11 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no minimal-ui">
<meta name="description" content="CodeFever - 轻量级版本控制系统">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no minimal-ui">
<meta name="description" content="CodeFever Community">
<meta name="keywords" content="CodeFever,subversion,git,svn,仓库,项目仓库,代码仓库,版本控制,版本控制系统,版本控制工具,轻量级版本控制系统" />
<link rel="dns-prefetch" href="<?php echo YAML_HOST;?>">
<?php /* <link rel="dns-prefetch" href="<?php echo APPICON_HOST; ?>"> */?>
<?php /* <link rel="dns-prefetch" href="<?php echo APPSCREENSHOT_HOST; ?>"> */?>
@@ -23,6 +23,7 @@
<link href="/static/<?php echo RELEASENUMBER;?>/css/main.css" rel="stylesheet" type="text/css"/>
<link href="/static/<?php echo RELEASENUMBER;?>/css/asset.css" rel="stylesheet" type="text/css"/>
<link href="/static/<?php echo RELEASENUMBER;?>/css/scroll-animation.css" rel="stylesheet" type="text/css"/>
<link href="/static/<?php echo RELEASENUMBER;?>/css/markdown.css" rel="stylesheet" type="text/css"/>
<!-- CORE CSS TEMPLATE - END -->
<link rel="shortcut icon" href="/favicon.ico" />

View File

@@ -1,5 +1,5 @@
---
host: <host>
host: http://localhost
allowRegister: true
email:
name: CodeFever Community

View File

@@ -0,0 +1,79 @@
# 概览和系统服务
此页面没有管理功能, 仅用于监视 `系统资源使用量``服务状态`
### 进入概览页面
能查看 `概览页面` 需要满足以下条件
- `CodeFever` 系统 `管理员`
进入 `概览页面` 的步骤:
1.`CodeFever` 导航栏右侧找到 `管理后台` 按钮
1. 点击 `管理后台` 按钮进入 `管理后台`
1. 点击左侧菜单栏 `概览` 选项
### 统计
统计部分用于展示 `CodeFever` 使用量
> `CodeFever` 对于使用量没有限制
### 系统资源
统计部分用于展示 `系统资源` 使用量
> 当出现性能问题时, 需要查看此面板确定有足够的 `系统资源` 可以使用
### 磁盘使用量
统计部分用于展示 `磁盘` 使用量
> CodeFever 的 Git 仓库均保存在 `/data/www/codefever-community/git-storage` 目录下, 如果磁盘空间剩余不足,请考 `扩容磁盘` 或 将 Git 仓库目录链接保存到其他磁盘上。
### 系统服务状态及维护
`CodeFever` 有三个主要的系统服务, 分别是 `PHP` , `Nginx``CodeFever` 。如果某个服务异常需要手动重新启动。
具体服务启动方式如下:
#### PHP
说明: 脚本引擎, 用于程序运行
支持的维护命令:
```shell
service php-fpm start # 启动
service php-fpm stop # 停止
service php-fpm restart # 重新启动
service php-fpm reload # 重新启动
service php-fpm status # 查看状态
```
#### Nginx
说明: 服务器软件, 用于 HTTP 反向代理服务
支持的维护命令:
```shell
service nginx start # 启动
service nginx stop # 停止
service nginx restart # 重新启动
service nginx reload # 重新启动
```
#### CodeFever
说明: 基础服务, 用于 Git 客户端 `HTTP` 连接和 `SSH` 连接相关服务
支持的维护命令:
```shell
service codefever start # 启动
service codefever stop # 停止
service codefever restart # 重新启动
service codefever status # 查看状态
```

7
doc/en-us/admin/index.md Normal file
View File

@@ -0,0 +1,7 @@
[Dashboard & Services](dashboard.md)
[Users](users.md)
[Repositories & Repository Groups](repository_and_groups.md)
[Settings](settings.md)

View File

@@ -0,0 +1,36 @@
# 仓库和仓库组
`管理后台` 中对于 `仓库``仓库组` 的管理均为对其成员进行管里。如果你需要更近一步的管理操作, 你可以将自己添加为 `仓库``仓库组` 的所有者进行管理。
### 仓库组成员
能查看 `仓库组列表页` 需要满足以下条件
- `CodeFever` 系统 `管理员`
进入 `仓库组列表页` 的步骤:
1.`CodeFever` 导航栏右侧找到 `管理后台` 按钮
1. 点击 `管理后台` 按钮进入 `管理后台`
1. 点击左侧菜单栏 `仓库组` 选项即可进入 `仓库组列表页`
1.`仓库组列表页` 中找到需要管理成员的的 `仓库组` 项目
1. 点击该项目右边的设置按钮即可以管理 `仓库组成员`
> 如果你需要对 `仓库组` 进行更近一步的管理操作, 你可以将自己添加为 `仓库组` 的所有者进行管理。
### 仓库成员
能查看 `仓库列表页` 需要满足以下条件
- `CodeFever` 系统 `管理员`
进入 `仓库列表页` 的步骤:
1.`CodeFever` 导航栏右侧找到 `管理后台` 按钮
1. 点击 `管理后台` 按钮进入 `管理后台`
1. 点击左侧菜单栏 `仓库` 选项即可进入 `仓库列表页`
1.`仓库列表页` 中找到需要管理成员的的 `仓库` 项目
1. 点击该项目右边的设置按钮即可以管理 `仓库成员`
> 如果你需要对 `仓库` 进行更近一步的管理操作, 你可以将自己添加为 `仓库` 的所有者进行管理。

View File

@@ -0,0 +1,74 @@
# 设置
管理员设置中的 设置 选项用于对 CodeFever 全局辅助功能进行设置。如果 `邮件发送` , `主机名``开放注册` 等。
`仓库组主页` 中左侧菜单中点击 `设置` 选项即可展开设置菜单
### 主机
`主机` 用于修改 `主机名`
此设置将会影响:
- 仓库 `clone 面板``仓库 URL``主机名` (包含 `HTTP``SSH`)
- 空仓库首页的提示中 `仓库 URL``主机名`
> 此设置项应填写为 `http://<ip address>` , `http://<domain name>` , `http://<host name>` 这三种格式中的一种。不要以 `/` 结尾
更换 `主机名` 需要以下操作步骤:
1.`CodeFever` 导航栏右侧找到 `管理后台` 按钮
1. 点击 `管理后台` 按钮进入 `管理后台`
1. 点击左侧菜单栏 `设置` 选项即可进入 `设置页面`
1.`设置页面` 中找到 `主机` 这一节
1.`URL` 输入框中输入你的 `主机` 设置后点击页面上的 `保存` 按钮即可
### SMTP
`SMTP` 用于进行 `发件设置`
此设置将会影响:
- 所有邮件通知里的邮件是否能够送达
> 由于收件方邮件服务商可能有很强的反垃圾邮件策略, 邮件可能会被投递到垃圾收件箱
更换 `SMTP` 需要以下操作步骤:
1.`CodeFever` 导航栏右侧找到 `管理后台` 按钮
1. 点击 `管理后台` 按钮进入 `管理后台`
1. 点击左侧菜单栏 `设置` 选项即可进入 `设置页面`
1.`设置页面` 中找到 `SMTP` 这一节
1.`发件人名称``发件人地址` 输入框中输入你的 `SMTP` 设置后点击页面上的 `保存` 按钮即可
#### 关于 `发件人名称` 和 `发件人地址` 设置的说明
由于收件服务器可能安装有强大的反垃圾邮件扩展, 因此仅设置 `SMTP` 是不够的, 你还需要对 `发件域名` 进行以下操作:
- 如果 `CodeFever` 服务器托管在 `云服务商` 上, 请联系你的 `云服务商` 确保你的 `主机` 可以访问 `外网主机``25` 端口
- 可能需要对 `发件域名` 解析进行如下设置:
```plain
mail.<发件域名> A <CodeFever 主机 IP>
<发件域名> MX mail.<发件域名>
```
- `发件人名称` 随意填写, `发件人地址` 填写 `<发件域名>` 即可
### 注册
`注册` 用于设置 `开放注册`
此设置将会影响:
- 新用户是否可以通过注册加入 `CodeFever`
更改 `开放注册` 选项需要以下操作步骤:
1. 在 `CodeFever` 导航栏右侧找到 `管理后台` 按钮
1. 点击 `管理后台` 按钮进入 `管理后台`
1. 点击左侧菜单栏 `设置` 选项即可进入 `设置页面`
1. 在 `设置页面` 中找到 `注册` 这一节
1. 调整 `开放注册` 开关设置后点击页面上的 `保存` 按钮即可
> 出于安全考虑,我们强烈推荐在公网上进行私有化部署的使用者关闭开放注册选项

50
doc/en-us/admin/users.md Normal file
View File

@@ -0,0 +1,50 @@
# 用户
此页面用于管理 `CodeFever` 中所有的用户的 `状态``属性`
### 进入用户管理
能查看 `用户管理` 需要满足以下条件
- `CodeFever` 系统 `管理员`
进入 `用户管理` 的步骤:
1.`CodeFever` 导航栏右侧找到 `管理后台` 按钮
1. 点击 `管理后台` 按钮进入 `管理后台`
1. 点击左侧菜单栏 `用户` 选项
### 添加用户
CodeFever 对于创建用户的策略有以下几种:
- 由用户自行注册 (需在 `管理后台` - `设置` 中打开 `开放注册` 选项)
- 由管理员添加用户 (需在 `管理后台` - `用户``添加用户`)
添加用户的步骤如下:
1. 进入 `用户管理` 页面
1. 点击 `用户列表` 上方右侧 `添加用户` 按钮
1. 在弹窗中输入 `用户名``电子邮件地址` 后点击 `确定` 按钮
1. 记录生成的用户 `密码` 即可。
1. 将用户 `邮箱``密码` 发送给 `用户` 即可
> - 出于安全考虑,我们强烈推荐在公网上进行私有化部署的使用者关闭开放注册选项 (参照 [管理员设置/设置](settings.md) 中 `注册` 一节)
> - 新用户密码由系统自动产生, 用户在登录后可以自行修改密码
> - 新用户默认为普通用户, 如果想指派为管理员参照本文档 `管理用户` 章节
### 管理用户
`管理员` 可以对用户进行以下管理操作:
- 禁用 (禁止用户登录)
- 重置密码
- 关闭 MFA 认证 (用户已经绑定 MFA 设备)
- 设置为管理员
管理用户步骤如下:
1. 进入 `用户管理` 页面
1. 点击 `用户列表` 中找到需要管理的用户的项目
1. 点击该项目最右边 `设置` 按钮
1. 在弹出的菜单中选择相应的管理项目

View File

@@ -0,0 +1,13 @@
# HTTP 和 SSH 的选择
在克隆仓库到本地时, 一般会遇到选择使用 `HTTP` 还是 `SSH` 方式来连接远端仓库的问题。
`CodeFever` 也提供了这两种方式供用户选择
### 两种方式的比较
`SSH` 方式的优点在于, 当用户在 `CodeFever` 的个人设置中设置好 `SSH Key` 以后,每次使用 `SSH` 连接远端仓库均不需要再进行任何人工的认证方式。但是使用 `SSH` 方式对于用户有一定的技术 要求, 用户必须自己生成 `SSH Key` 并完成配置。
相比之下 `HTTP` 方式更适合新手和不熟悉操作系统的用户, 不需要生成并设置 `SSH Key`。 但是在使用 `HTTPS` 方式连接远端仓库时,每次连接都需要输入邮箱和密码进行身份认证。
> 关于如何生成 `SSH Key`。 参照: [获取并设置 SSH Key](ssh_key.md)

View File

@@ -0,0 +1,15 @@
# 如何追溯文件修改
`CodeFever` 提供两种方式进行文件修改的追溯, 分别是 `历史``Blame`
### 历史 和 Blame 的区别
`历史`: 是指文件从创建到当前状态所有的变更记录的列表。
`Blame`: 是指文件当前状态每一行最后是有谁编辑的。
一般来说,`历史` 用于追溯文件的变化过程,而 `Blame` 用于查看当前文件每一行的最后编辑者。
历史 和 Blame 按钮可以在具体文件的文件内容展示标题栏找到。
参考: [仓库/查看文件](../repository/view_files.md) 中 `查看文件历史` 一节

11
doc/en-us/common/index.md Normal file
View File

@@ -0,0 +1,11 @@
[Generate & Set SSH Key](ssh_key.md)
[HTTP or SSH](clone_method.md)
[Member Roles](role.md)
[Track Multiple Email for One User](multiple_email.md)
[Git Workflows](workflow.md)
[Track File Changes](history.md)

View File

@@ -0,0 +1,19 @@
# 多个邮箱地址提交的文件变更是否能被追溯
一般情况, 每个用户在自己的 Git 客户端中设置的邮箱地址是一致的。当使用一致的邮箱提交文件时, 提交信息会被识别成相应的用户。
但是, 在某些情况下, 当同一个用户在不同的 Git 客户端设置的提交邮箱不一致时, 则可能出现在同一个项目中同一个人的提交会以多个身份的形式呈现出来的情况。
当有一个用户拥有多个提交邮箱时并且希望这些提交邮箱对应的提交都以自己的身份标记时。则需要进行 `多邮箱` 设置。
### 多邮箱设置
当需要将多个提交身份映射成自己的身份时, 需要进行 `多邮箱` 设置。
具体设置步骤如下:
1.`导航栏` 右侧找到自己的 `头像` 并点击
1. 在展开的菜单中点击 `多邮箱` 选项打开 `邮箱管理页面`
1.`邮箱管理页面` 找到并点击 `新建邮箱地址` 按钮在新出现的输入框中输入新的邮箱并点击右侧的 `确定` 按钮即可
> - 只有 `主邮箱` 地址才能作为 `登录邮箱`

21
doc/en-us/common/role.md Normal file
View File

@@ -0,0 +1,21 @@
# 成员角色的选择
### 角色的选择
`CodeFever` 内置五种用户角色,分别是 `访客` , `监督者` , `开发者` , `维护者``所有者`
五类角色具体描述为:
`访客`: 仓库中权限最低的角色,对整个仓库所有的项目只有读取权限,一般用于设置给项目外成员读取代码使用。
`监督者`: 对仓库有读取权限,同时可以跟进 **合并请求** 的进度,一般用于项目组内 QA 、测试人员 项目经理 等 角色。
`开发者`: 对于仓库代码有读写权限,可以推送代码,可以提交和查看 `合并请求。一般用于项目的开发人员。`
`维护者`: 对于仓库代码和仓库管理有基础权限,可以合并和关闭 `合并请求` 。一般用与项目的技术管理人员或技术主管。
`所有者`: 具有仓库的所有权限。
### 角色权限
角色权限列表参见 [仓库/成员管理](../repository/members.md) 中的 `角色与权限` 一节

View File

@@ -0,0 +1,41 @@
# 获取并设置 SSH Key
`SSH Key` 用于 Git 客户端通过 `SSH` 协议与远端仓库通信的身份认证。当在 `CodeFever` 中设置 `SSH Key` 之后,使用 `SSH` 方式连接位于 `CodeFever` 上的仓库时不需要再输入用户和密码进行认证。
### 获取 SSH Key
当使用 `Linux``MacOS` 系统时,系统会默认安装 `ssh` 相关组件。此时, `SSH Key` 存在于家目录下的 `.ssh` 目录下。当使用 `Windows` 操作系统时,需要安装 `Git Bash`, 文中提到的所有命令需要在 `Git Bash` 中输入。
获取 `SSH Key` 的步骤如下:
1. 在终端输入以下命令即可查看该目录
```shell
ls -al ~/.ssh
```
1. 如果目录中包含 `id_rsa.pub` 或 `id_dsa.pub` 文件时, 则不需要新产生 `SSH Key`; 否则, 需要手动生成 `SSH Key`。在终端输入以下命令可以生成 SSH Key
```shell
ssh-keygen -t rsa -C ”<name or comment>“
```
> 注意: 参数 `-C` 后面可以输入任何你希望标识该 SSH Key 的名称
1. 此时, `~/.ssh` 目录下应该会新增一个名为 `id_rsa.pub` 的文件, 这个文件里面存储的就是 `SSH Key`。在终端输入以下命令可以查看 `SSH Key` 内容。
```shell
cat ~/.ssh/id_rsa.pub
```
### 设置 SSH Key
当获取到 SSH Key 之后, 需要将 SSH Key 设置到 CodeFever 上。
具体设置步骤如下:
1. 在 `导航栏` 右侧找到自己的 `头像` 并点击
1. 在展开的菜单中点击 `SSH Key` 选项打开 `SSH Key 设置页面`
1. 在 `SSH Key 设置页面` 输入 `SSH Key` 点击 `新增 SSH Key` 按钮即可
> - 由于 `SSH Key` 用于鉴别用户身份, 因此每个 `SSH Key` 只能添加到一个账号里, 否则会提示 `SSH Key` 已经添加

View File

@@ -0,0 +1,31 @@
# 何选择 Git 工作流 ?
选择 `CodeFever` 则相当于选择了 `分布式` Git 工作流程, 常见的分布式流程有 `集中式工作流` , `集成管理者工作流``主管副主管工作流` 每一种工作流都有自己的特点, 这里需要根据自己项目的特点来判断。
`CodeFever` 提供了 `分支` , `合并请求``Fork` 功能,因此可以轻松支持这三种工作流。
### 集中式工作流
集中式系统中通常使用的是单点协作模型——集中式工作流。 一个中心 `仓库`, 可以接受代码, 所有人将自己的工作与之同步。 若干个开发者则作为节点, 即中心仓库的消费者与中心仓库同步。
这意味着如果两个开发者从中心仓库克隆代码下来, 同时做了一些修改, 那么只有第一个开发者可以顺利地把数据推送回共享服务器。 第二个开发者在推送修改之前, 必须先将第一个人的工作合并进来, 这样才不会覆盖第一个人的修改。 这和 Subversion (或任何 CVCS中的概念一样, 而且这个模式也可以很好地运用到 Git 中。
如果在公司或者团队中, 你已经习惯了使用这种集中式工作流程, 完全可以继续采用这种简单的模式。 只需要搭建好一个中心仓库, 并给开发团队中的每个人推送数据的权限, 就可以开展工作了。Git 不会让用户覆盖彼此的修改。
当然这并不局限于小团队。 利用 Git 的分支模型, 通过同时在多个分支上工作的方式, 即使是上百人的开发团队也可以很好地在单个项目上协作。
### 集成管理者工作流
Git 允许多个远程仓库存在, 使得这样一种工作流成为可能: 每个开发者拥有自己仓库的写权限和其他所有人仓库的读权限。 这种情形下通常会有个代表“官方”项目的权威的仓库。 要为这个项目做贡献, 你需要从该项目克隆出一个自己的公开仓库, 然后将自己的修改推送上去。 接着你可以请求官方仓库的维护者拉取更新合并到主项目。 维护者可以将你的仓库作为远程仓库添加进来, 在本地测试你的变更, 将其合并入他们的分支并推送回官方仓库。
这是 GitHub 和 GitLab 等集线器式hub-based工具最常用的工作流程。人们可以容易地将某个项目派生成为自己的公开仓库, 向这个仓库推送自己的修改, 并为每个人所见。 这么做最主要的优点之一是你可以持续地工作, 而主仓库的维护者可以随时拉取你的修改。 贡献者不必等待维护者处理完提交的更新——每一方都可以按照自己的节奏工作。
### 主管与副主管工作流
这其实是多仓库工作流程的变种。 一般拥有数百位协作开发者的超大型项目才会用到这样的工作方式。 被称为 `副主管lieutenant` 的各个集成管理者分别负责集成项目中的特定部分。 所有这些副主管头上还有一位称为 `主管dictator` 的总集成管理者负责统筹。 主管维护的仓库作为参考仓库, 为所有协作者提供他们需要拉取的项目代码。
这种工作流程并不常用, 只有当项目极为庞杂, 或者需要多级别管理时, 才会体现出优势。 利用这种方式, 项目总负责人(即主管)可以把大量分散的集成工作委托给不同的小组负责人分别处理, 然后在不同时刻将大块的代码子集统筹起来, 用于之后的整合。
> 详细请阅读参考文章: [分布式 Git - 分布式工作流程](https://git-scm.com/book/en/v2/Distributed-Git-Distributed-Workflows)

View File

@@ -0,0 +1,19 @@
# 提交问题
当在使用 CodeFever 过程遇到问题时, 可以通过在 Github 项目上提交问题来得到帮助。
Github 项目问题提交地址: [issues - PGYRE/codefever](https://github.com/PGYER/codefever/issues)
问题的类型可以是:
- `Bug` 修复
- 易用性改进
- 特性建议
- 文档或文案修改
在提交问题之前, 请确定以下工作已经完成:
- 问题可被修改
- 新特性符合基本逻辑
- 已搜索项目 `issue` 列表, 确定没有类似或相似问题
- 如果内容是 `Bug` 修复, 需要给出简单的测试用例

View File

@@ -0,0 +1,13 @@
# 提交问题修复
### 代码贡献
当使用 `CodeFever` 过程中发现有 `Bug` 并且你有能力进行补充或修复时, 欢迎向我们提交问题修复 `PR`
提交问题修复 `PR` 的步骤如下:
1. `Fork` 本项目
1. 完成 `Bug` 修复工作
1. 提交 `PR` 前请到 `issue` 中搜索您的修改是否解决了某些 `issue` , 如果解决了, 请在提交 `PR` 时引用
1. 如果可能,准备一段可以复现该 `Bug` 的测试用例, 添加到 `PR` 描述中
1. 提交 `PR``dev/master` 分支

View File

@@ -0,0 +1,32 @@
# 提交文档
### 文档贡献
当阅读 `CodeFever` 文档过程中发现文档中有缺失或错误时, 并且你有能力进行补充或修复时, 欢迎向我们提交文档 `PR`
文档 `PR` 接收的内容:
- 文档中的错误
- 文档中缺失的描述
- 新增更详细的文档内容
- 翻译工作
满足以上任何一条的 `PR` 均可以被合并。
提交文档 `PR` 的步骤如下:
1. `Fork` 本项目
1. 在自己的仓库下完成文档编辑工作
1. 提交 `PR` 前请到 `issue` 中搜索您的修改是否解决了某些 `issue` , 如果解决了, 请在提交 `PR` 时引用
1. 提交 `PR``dev/master` 分支
> 在正式贡献文档之前, 建议阅读下一节内容: `文档要求`
### 文档要求
- `CodeFever` 文档时树形结构, 以 `/doc` 为根目录 `/doc/zh-cn` 是中文文档目录 `/doc/en-us` 是英文文档目录。 目录按照文档内容区分并分级, 每级目录下的 `index.md` 文件为目录文件, 所有文档均为 `Markdown` 文档。
- 目录文件每行是一个 `Markdown` 链接, 使用相对路径链接其他文档。
- 文档文件名由 `单词``下划线` 组成; 使用 `Markdown` 语法; 图片和附件均保存在文档当前目录的 `assets` 目录下, 使用相对路径引用。

View File

@@ -0,0 +1,9 @@
[Contribute Docs](../contribute/doc_pr.md)
[Issue](../contribute/bug_fix_issue.md)
[Feature Request](../contribute/request_feature_issue.md)
[Bug Fix PR](../contribute/bug_fix_pr.md)
[Feature PR](../contribute/new_feature_pr.md)

View File

@@ -0,0 +1,13 @@
# 提交新特性
### 代码贡献
当使用 `CodeFever` 过程中发现有缺失的功能, 并且你有能力进行开发时, 欢迎向我们提交新特性 `PR`
提交新特性 `PR` 的步骤如下:
1. `Fork` 本项目
1. 完成新特性研发工作
1. 提交 `PR` 前请到 `issue` 中搜索您的修改是否解决了某些 `issue` , 如果解决了, 请在提交 `PR` 时引用
1. 如果可能,准备一段可以复现该特性的测试用例, 添加到 `PR` 描述中
1. 提交 `PR``dev/master` 分支

View File

@@ -0,0 +1,12 @@
# 提交特性请求
当使用 CodeFever 过程中发现缺失功能时, 你可以通过提交 `特性请求` 来要求开发和维护团队为 `CodeFever` 添加新特性。
Github 项目特性请求提交地址: [issues - PGYRE/codefever](https://github.com/PGYER/codefever/issues)
在提交特性请求之前, 请确定以下工作已经完成:
- 新特性符合基本逻辑
- 已搜索项目 `issue` 列表, 确定没有类似或相似的特性请求
- 尽量链接此 `特性请求` 可能关联的 `issue`

21
doc/en-us/git/checkout.md Normal file
View File

@@ -0,0 +1,21 @@
# 回退文件到指定版本
### 回退文件到指定版本
如果需要回退某个文件到指定版本,则需要:
```shell
git checkout <commit hash> <file path>
```
命令执行成功后, 工作目录的文件内容回退到指定版本的文件内容, 同时已保存至暂存区。
还可以指定回退的版本数来进行回退操作,例如:
```shell
git checkout master~<num> <file path>
```
命令执行成功后, 工作目录中的文件内容回退到 `master` 分支最近的第 `<num> + 1` 次提交的内容, 并保存至暂存区。
参照: [Git 常用命令参考](git_command_reference.md)

20
doc/en-us/git/clone.md Normal file
View File

@@ -0,0 +1,20 @@
# 克隆仓库到本地
当仓库在 `CodeFever` 上创建完成后,需要 `克隆` 到本地才能继续操作。
### 如何 clone 一个仓库
空仓库具体操作如下:
1. 进入 `仓库首页`
1.`仓库首页` 右上角点击 `克隆` 按钮
1. 根据你需要的方式来复制仓库地址,默认为 `HTTP`
1. 使用如下操作来完成 `克隆`
```shell
git clone <repository url> <target path>
```
> - 关于 HTTP 和 SSH 两种形式如何选择,参见 [常见问题/HTTP 和 SSH 的选择](../common/clone_method.md)
> - 如果需要使用 SSH 方式 克隆, 需要提前设置 SSH Key 。 参见 [常见问题/获取并设置 SSH Key](../common/ssh_key.md)

View File

@@ -0,0 +1,39 @@
# 创建第一个分支
在 CodeFever 新建的仓库没有任何内容,需要在本地创建仓库的第一个分支,并推送到远程仓库。
1. 克隆远程仓库到本地(这里使用 `SSH` 的方式克隆,克隆前需要在 `SSH Key` 设置中添加本地 `SSH` 信息),并切换到仓库根目录,例如:
```shell
git clone ssh://git@your.domain:group_name/project_name.git
cd project_name
```
克隆完成后,工作目录已经切换到默认的 `master` 分支,也可以创建其他的分支,例如:
```shell
git checkout -b branch_name
```
2. 添加内容,创建一次提交,例如:
```shell
echo 'init' > readme.md
git add readme.md
git commit -m 'init commit'
```
3. 推送到远程仓库,例如:
```shell
git push origin branch_name
```
参照:
[git clone](git_command_reference.md#git-clone)
[git checkout](git_command_reference.md#git-checkout)
[git add](git_command_reference.md#git-add)
[git commit](git_command_reference.md#git-commit)
[git push](git_command_reference.md#git-push)

View File

@@ -0,0 +1,128 @@
# Git 常用命令参考
### git
作用: 传入参数以完成 git 相关操作。
参照: [git](https://git-scm.com/docs/git)
### git config
作用: 查看和设置仓库设置和全局设置的设定
参照: [git config](https://git-scm.com/docs/git-config)
### git help
作用: 查看帮助选项
参照: [git help](https://git-scm.com/docs/git-help)
### git init
作用: 从当前目录初始化一个仓库
参照: [git init](https://git-scm.com/docs/git-init)
### git clone
作用: 复制一个仓库
参照: [git clone](https://git-scm.com/docs/git-clone)
### git add
作用: 添加文件内容到索引中
参照: [git add](https://git-scm.com/docs/git-add)
### git status
作用: 显示 `工作区` 状态
参照: [git status](https://git-scm.com/docs/git-status)
### git diff
作用: 查看 `commit` 之间或 `commit``工作区` 之间的差异
参照: [git diff](https://git-scm.com/docs/git-diff)
### git commit
作用: 记录仓库文件的变更
参照: [git commit](https://git-scm.com/docs/git-commit)
### git notes
作用: 添加或查看对象的文本记录
参照: [git notes](https://git-scm.com/docs/git-notes)
### git restore
作用: 重置工作区
参照: [git restore](https://git-scm.com/docs/git-restore)
### git reset
作用: 重置当前工作区到某一特定状态
参照: [git reset](https://git-scm.com/docs/git-reset)
### git rm
作用: 从工作区和索引中移除某个文件
参照: [git rm](https://git-scm.com/docs/git-rm)
### git mv
作用: 对文件、目录或者链接进行移动或重命名操作
参照: [git mv](https://git-scm.com/docs/git-mv)
### git branch
作用: 查看、删除、创建分支
参照: [git branch](https://git-scm.com/docs/git-branch)
### git checkout
作用: 切换分支或重置工作区
参照: [git checkout](https://git-scm.com/docs/git-checkout)
### git switch
作用: 切换分支
参照: [git switch](https://git-scm.com/docs/git-switch)
### git merge
作用: 将两个或多个开发历史合并
参照: [git merge](https://git-scm.com/docs/git-merge)
### git mergetool
作用: 运行解决冲突工具来解决合并冲突
参照: [git mergetool](https://git-scm.com/docs/git-mergetool)
### git log
作用: 显示提交记录
参照: [git log](https://git-scm.com/docs/git-log)
### git stash
作用: 隐藏工作区未被记录的修改
参照: [git stash](https://git-scm.com/docs/git-stash)
### git tag
作用: 查看、删除、创建标签
参照: [git tag](https://git-scm.com/docs/git-tag)
### git worktree
作用: 管理工作区
参照: [git worktree](https://git-scm.com/docs/git-worktree)
### git fetch
作用: 从其他仓库下载仓库更新记录
参照: [git fetch](https://git-scm.com/docs/git-fetch)
### git pull
作用: 从其他仓库或分支下载仓库更新记录并合并到当前分支
参照: [git pull](https://git-scm.com/docs/git-pull)
### git push
作用: 上传仓库更新记录
参照: [git push](https://git-scm.com/docs/git-push)
### git remote
作用: 管理远端仓库
参照: [git remote](https://git-scm.com/docs/git-remote)
### git submoudle
作用: 初始化、更新和查看子模块
参照: [git submoudle](https://git-scm.com/docs/git-submoudle)
### git cherry-pick
作用: 使用指定的变更记录来应用更改
参照: [git cherry-pick](https://git-scm.com/docs/git-cherry-pick)
### git rebase
作用: 从新的基础点应用提交的变更记录
参照: [git rebase](https://git-scm.com/docs/git-rebase)
### git revert
作用: 回滚已经提交的变更记录
参照: [git revert](https://git-scm.com/docs/git-revert)
### 其他命令
参照: [Git - Reference](https://git-scm.com/docs)

15
doc/en-us/git/index.md Normal file
View File

@@ -0,0 +1,15 @@
[Clone to Local](clone.md)
[Create First Branch](create_branch.md)
[Push a Branch](push_branch.md)
[Rstore Changes](revert.md)
[Checkout](checkout.md)
[Remote Repository](remote.md)
[Merge Localy](merge_branch.md)
[Git Command Refrence](git_command_reference.md)

View File

@@ -0,0 +1,88 @@
# 本地的分支合并
`CodeFever` 上合并分支时,会遇到合并冲突的情况,目前 `CodeFever` 暂时不支持在线解决冲突,可以使用下面的流程完成冲突情况下的分支合并。这些流程不局限于解决冲突,也可以作为正常合并分支的流程。
### 同仓库内的分支合并
在同一个仓库内,在 `CodeFever` 上从 `<source branch>` 分支往 `<target branch>` 分支合并时需要在本地进行同仓库内的分支合并
同仓库内的分支合并操作步骤:
1. 在本地,先切换工作目录到分支 `<target branch>`,例如:
```shell
git checkout <target branch>
```
1. 合并 `<source branch>` 的改动到当前分支 `<target branch>`,例如:
```shell
git merge <source branch>
```
1. 修改有冲突的文件,解决冲突
1. 创建新提交
```shell
git add <changed files>
git commit -m 'memo'
```
1. 将解决冲突的改动推送到远程仓库
```shell
git push <remote name> <target branch>
```
### 不同仓库间进行分支合并
在 `CodeFever` 上,从 `<source repo>` 仓库的 `<source branch>` 分支往 `<target repo>` 仓库的 `<target branch>` 分支合并时,如果遇到冲突需要在本地进行不同仓库间进行分支合并
本地解决不同分支合并步骤:
1. 在 `<target repo>` 的本地仓库内,切换工作目录到分支 `<target branch>`
```shell
git checkout <target branch>
```
1. 为本地的 `<target repo>` 仓库添加远程仓库 `<source repo>`
```shell
> git remote add <source repo name> <source repo>
```
1. 拉取远程仓库分支 `<source repo>` 的改动
```shell
git fetch <source repo name> <source branch>
```
1. 合并 `<source repo name>/<source branch>` 的改动到当前分支 `<target branch>`
```shell
git fetch <source repo name>/<source branch>
```
1. 修改有冲突的文件,解决合并冲突
1. 创建新提交
```shell
git add <changed files>
git commit -m 'memo'
```
1. 将解决冲突的改动推送到远程仓库 `<target branch>`
```shell
git push <target remote name> <target branch>
```
1. 删除远程仓库 (可选)
```shell
git remote remove <source remote name>
```
参照: [Git 常用命令参考](git_command_reference.md)

View File

@@ -0,0 +1,13 @@
# 推送一个新分支
### 推送新分支
如果推送的分支在远端仓库中不存在,则会被认定为一个新分支。
此时,推送的命令需要添加 `-u` 的参数来完成新分支的推送
```shell
git push -u <remote name>/<new branch>
```
参照: [Git 常用命令参考](git_command_reference.md)

31
doc/en-us/git/remote.md Normal file
View File

@@ -0,0 +1,31 @@
# 远程仓库源管理
### 添加远程仓库
如果需要快速推送本地仓库到远端仓库, 则需要手动添加远程仓库:
```shell
> git remote add <remote name> <repo url>
```
添加成功后,可以使用 `git push <remote name> <branch>` 向远程仓库推送新改动, 使用 `git pull <remote name> <branch>` 拉取新的改动。
> 当使用 `clone` 复制仓库时, 仓库的 `remote` 列表中会自动包含一条叫做 `origin` 的记录, 此记录只想 `clone` 仓库时的仓库地址。
### 查看远程仓库列表
可以使用以下命令查看远程仓库列表:
```shell
git remote -v
```
### 删除远程仓库
可以使用以下命令删除远程仓库:
```shell
git remote remove <remote name>
```
参照: [Git 常用命令参考](git_command_reference.md)

35
doc/en-us/git/revert.md Normal file
View File

@@ -0,0 +1,35 @@
# 还原提交的改动
### 使用 git revert 来回滚
有时需要还原某一次提交的改动, 可以使用 `git revert` 命令实现, 例如:
```shell
git revert <commit hash>
```
命令执行成功后, 仓库会还原到提交哈希为 `<commit hash>` 的改动, 如果没有冲突, 会自动创建一次新的提交。如果不希望自动创建提交, 加上 `-n` 选项。
还可以以当前 `HEAD` 为基准, 还原之前的某次提交, 例如:
```shell
git revert HEAD~<num>
```
命令执行成功后, 仓库会还原最近 `<num> + 1` 次提交的改动, 如果没有冲突, 会自动创建一次新的提交。
还可以指定一个范围还原某几次提交的改动,例如:
```shell
git revert -n <master>~<num1> .. <master>~<num2>
```
命令执行成功后, 仓库会还原最近的第 `<num1>` 次(包括)提交到最近的 `<num1>` 次(包括)提交的改动。
因为使用了 `-n` 选项,还原的结果保存到了暂存区中, 需要手动创建新的提交。
在使用 `-n` 选项时, 如果还原操作的结果不符合预期, 在创建新的提交之前, 可以撤销本次还原操作, 例如:
```shell
git revert --abort
```

16
doc/en-us/index.md Normal file
View File

@@ -0,0 +1,16 @@
[Start](start)
[Installation](installation)
[Repository](repository)
[Repository Group](repository_group)
[Admin Area](admin)
[Git](git)
[FAQ](common)
[Contribution](contribute)

View File

@@ -0,0 +1,3 @@
[Install From Scratch](install_from_scratch.md)
[Install via Docker Image](install_via_docker.md)

View File

@@ -0,0 +1,228 @@
# 从零开始安装
## 此文档适用条件
> 如果你的情况符合以下条件, 你可以使用 `从零开始安装` 的方式安装 `CodeFever` 否则请使用 `Docker 镜像安装` 方式安装。
- 学习和技术交流
- 需要做定制化修改
- `Docker 镜像安装` 不能满足处理 `Bug` 和提交 `PR` 的需求
- Docker 镜像不能在当前 `操作系统``硬件架构` 上使用
## 安装步骤
### 0. 安装前的说明
此安装步骤是推荐的 `从头开始安装` 步骤, 举例的脚本适用于大部分 `Linux` 操作系统。
此安装步骤详细指导均以 `CentOS 7.x Linux` 操作系统为例,不同操作系统可能会有不同差别,需要用户自行对应到相应操作。
整个安装过程需要使用 `root` 操作系统用户来完成。
> `步骤1` - `步骤8` 均为软件环境安装步骤。如果你的操作已经具备当前步骤的软件或环境,此步骤可以跳过。
### 1. 准备操作系统环境
选择合适的 `Linux` 发行版本,推荐使用 `CentOS 7.x`
* 执行编译安装时需要保证机器至少有 `1500 MB` 内存,如果内存不足 `1500 MB` 请临时添加交换分区使可用内存到达 `1500 MB`
```shell
# 临时增加 512 MB Swap 空间
dd if=/dev/zero of=/root/swap bs=1024 count=500000
mkswap /root/swap
swapon /root/swap
```
为了顺利的设置必要的软件环境,以下的软件包必须安装到系统上。
```shell
# 安装基础软件包
yum install -y autoconf cmake3 yaml pcre pcre-devel libxml2 libxml2-devel openssl openssl-devel sqlite sqlite-devel libpng libpng-devel libwebp libwebp-devel libjpeg libjpeg-devel libXpm libXpm-devel freetype freetype-devel oniguruma oniguruma-devel libyaml libyaml-devel
```
除此之外,你还需要安装 `libzip 1.7+` 库,需要去官网下载源码包解压后安装。
下载地址: [https://libzip.org/download/](https://libzip.org/download/)
```shell
# 编译安装 libzip
cd libzip-1.x.x
mkdir build
cd build
cmake3 ../
make && make install
export PKG_CONFIG_PATH="/usr/local/lib64/pkgconfig/:/usr/local/lib/pkgconfig/"
```
### 2. 安装 nginx
访问 [http://nginx.org/en/download.html](http://nginx.org/en/download.html) 下载 `Nginx` 源码解压后编译并安装安装到 `/usr/local/nginx` 目录下:
```shell
# 安装 nginx 到 /usr/local/nginx 目录
cd nginx-1.x.x
./configure --prefix=/usr/local/nginx
make && make install
```
### 3. 安装 PHP
访问 [https://www.php.net/downloads](https://www.php.net/downloads) 下载 `PHP` 源码 (推荐 `7.4` 版本),解压后编译并安装到 `/usr/local/php` 目录下
> 配置检查过程可能会提示缺少某些软件包,需要根据配置检查过程提示自行安装后再重复配置检查过程
```shell
# 安装 php 到 /usr/local/php 目录
cd php-7.4.x
./configure --prefix=/usr/local/php --with-config-file-path=/usr/local/php/etc --enable-fpm --enable-bcmath=shared --with-pdo_sqlite --with-gettext=shared --with-iconv --enable-ftp=shared --with-sqlite3 --enable-mbstring=shared --enable-sockets=shared --enable-soap=shared --with-openssl --with-zlib --with-curl=shared --enable-gd --with-freetype --with-jpeg --with-xpm --with-webp --with-mhash --enable-opcache --with-mysqli --without-pear --with-libdir=lib64 --with-zip --enable-mbstring --enable-pcntl
make && make install
```
> `Codefever` 还需要以下 `PHP 扩展` 才能良好工作,按照下面地址下载并安装
- yaml : https://pecl.php.net/package/yaml
```shell
# 安装 php yaml 扩展
cd yaml-2.x.x
/usr/local/php/bin/phpize
./configure --with-php-config=/usr/local/php/bin/php-config
make && make install
```
### 4. 安装 Git
访问 [https://mirrors.edge.kernel.org/pub/software/scm/git/](https://mirrors.edge.kernel.org/pub/software/scm/git/) 下载 `Git v2` 的源码,解压后编译并安装到 `/usr/local/git` 目录下
```shell
# 安装 git v2 到 /usr/local/git 目录下
cd git-2.x.x
./configure --prefix=/usr/local/git
make && make install
```
连接可执行二进制文件到 `/usr/local/bin` 目录下
```shell
# 链接可执行文件
ln -s /usr/local/git/bin/git /usr/local/bin/
```
### 5. 安装 Go (无需编译, 二进制安装)
访问 [https://golang.google.cn/dl/](https://golang.google.cn/dl/) 下载 1.16 版本以上的二进制安装包解压后复制到 `/usr/local/go` 目录下
```shell
# 复制到 /usr/local 目录下
cp -R go /usr/local
```
连接可执行二进制文件到 `/usr/local/bin` 目录下
```shell
# 链接可执行文件
ln -s /usr/local/go/bin/go /usr/local/bin/go
ln -s /usr/local/go/bin/gofmt /usr/local/bin/gofmt
```
### 6. 安装 NodeJS (无需编译, 二进制安装, 开发或修改前端页面时使用)
访问 [https://nodejs.org/en/download/](https://nodejs.org/en/download/) 下载 `16.10` 以上 `LTS` 版本二进制安装包解压后复制到 `/usr/local/node` 目录下
```shell
# 复制到 /usr/local 目录下
cp -R node-v16.x.x-os-arch /usr/local/node
```
连接可执行二进制文件到 `/usr/local/bin/` 目录下
```shell
# 链接可执行文件
ln -s /usr/local/node/bin/node /usr/local/bin/node
ln -s /usr/local/node/bin/npm /usr/local/bin/npm
ln -s /usr/local/node/bin/npx /usr/local/bin/npx
ln -s /usr/local/node/bin/corepack /usr/local/bin/corepack
```
### 7. 安装 Yarn
访问 [https://yarnpkg.com/getting-started/install](https://yarnpkg.com/getting-started/install) 按照页面指导安装 `Yarn`
```shell
# NodeJS v16.10 以上打开 corepack 即可使用 Yarn
corepack enable
```
### 8. 安装 MySQL/MariaDB (无需编译, 镜像源安装)
MySQL 不需要单独安装,直接使用系统自带软件工具安装软件包即可。需要安装于 `MySQL 5.7` 以上的相当版本。
去官网按照指导使用镜像源安装二进制版本 (https://mariadb.org/download/?t=repo-config),如果你使用使用云数据库,你可以跳过此步骤。
启动服务后使用 `mysql_secure_installation``mariadb-secure-installation` 初始化数据库
*当设置 `root` 密码为 `123456` 时,不需要在下一步中修改 `env.yaml` 中的数据库设置。
如果你使用 `MySQL 5.7` 版本数据,需要修改 `SQL MODE` 变量,否则创建数据库时会报错,如果使用 `MariaDB` 可以忽略此选项。
```SQL
set global sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
```
### 9. 下载源码并安装
去 `Github` 上下载源码并安装 `/data/www/codefever-comminuty` 目录下
```shell
mkdir /data/www
cd /data/www
git clone https://github.com/PGYER/codefever.git codefever-community
cd codefever-community
```
* 如果 `Github` 的 `HTTP` 服务访问速度较慢,可以尝试使用 `SSH` 服务(需要提前设置 `SSH Key`
```shell
git clone ssh://git@github.com:PGYER/codefever.git codefever-community
```
编译 `HTTP` 网关服务
```shell
cd /data/www/codefever-community/http-gateway
export GO111MODULE=off
export GOPROXY=https://mirrors.aliyun.com/goproxy
go get gopkg.in/yaml.v2
go build main.go
```
编译 `SSH` 网关服务
```shell
cd /data/www/codefever-community/ssh-gateway/shell
export GO111MODULE=off
export GOPROXY=https://mirrors.aliyun.com/goproxy
go get gopkg.in/yaml.v2
go build main.go
```
执行安装脚本
```shell
cd /data/www/codefever-community/misc
sh ./install.sh
```
按照 `install.sh` 运行后提示修改 `env.yaml` 文件设置参数
运行数据库迁移脚本
```shell
cd /data/www/codefever-community/misc
sh ./create_db.sh
```
尝试访问 `http://127.0.0.1` 或 `http://<server ip>` 来登录
默认管理员用户: `root@codefever.cn`, 密码: `123456`。登录后请修改密码并绑定 MFA 设备。

View File

@@ -0,0 +1,3 @@
# Docker 镜像安装
> 文档正在准备中

View File

@@ -0,0 +1,7 @@
# 提交代码
当有需要仓库记录变更的时候,需要向仓库来提交变更。
具体可以参照文档中 `Git/创建第一个分支` 中的 `步骤 2` .
参照: [创建第一个分支](../git/create_branch.md)

View File

@@ -0,0 +1,22 @@
# 创建分支
`CodeFever` 中仓库的分支可以通过以下方式创建。
### 1. 在 Web 界面直接创建
直接创建分支适用于大部分情况。创建步骤如下:
1. 进入`仓库主页`
1. 在主页菜单中选择 `分支` 选项即可进入 `分支列表` 页面
1.`分支列表` 页面的分支列表右上方点击 `新建分支` 即可进入 `创建分支页面`
1. 选择 `分支来源` 后输入新的 `分支名称` 后点击 `确定` 按钮即可完成分支创建
### 2. 通过命令行创建
当需要在本地同步仓库创建新分支时,则需要使用命令行方式创建。
1. 进入本地仓库目录
1. 使用 `git checkout <source ref>` 来切换到 `来源分支`
1. 使用 `git checkout -b <new branch>` 创建并切换到新分支
参考: [Git 常用命令参考](../git/git_command_reference.md)

View File

@@ -0,0 +1,27 @@
# 创建仓库
`CodeFever``仓库` 可以通过以 `直接创建``通过 Fork 创建` 两种方式进行创建。
### 直接创建
直接创建仓库适用于大部分情况,通过直接创建仓库可以创建一个空白仓库。
创建仓库的途径有以下几种方式:
- 在导航栏中点 `+` 按钮选择 `创建仓库` 选项
- 在 [CodeFever 首页](/repositories) 点击 `创建仓库` 按钮
- 在需要创建的仓库组的 `仓库组首页` 点击 `创建仓库` 按钮
- 点击 [这里](/repositories/new) 直接创建 `仓库`
通过上述方式可以进入到 `创建仓库页面` ,完成仓库设置点击 `确定` 按钮即可创建仓库。
### 通过 `Fork` 创建
当需要创建一个与另一个仓库(后文统称 `主仓库` )内容一致的仓库时可以通过 `Fork` 的方式来创建。当新仓库创建好后,可以将新仓库中产生的新提交通过合并请求的方式合并到`主仓库`中。
可以通过以下步骤来 `Fork` 仓库:
1. 进入要 `Fork` 的仓库主页 (即上文提到的 `主仓库` )
2. 点击`仓库主页`上的 `Fork` 按钮
通过上述步骤可以进入到 `Fork 仓库页面` ,完成仓库设置点击 `确定` 按钮即可 `Fork` 仓库。

View File

@@ -0,0 +1,22 @@
# 创建标签
`CodeFever` 中仓库的标签可以通过以下方式创建。
### 1. 在 Web 界面直接创建
直接创建标签适用于大部分情况。创建步骤如下:
1. 进入`仓库主页`
1. 在主页菜单中选择 `标签` 选项即可进入 `标签列表` 页面
1.`标签列表` 页面的标签列表右上方点击 `新建标签` 即可进入 `创建标签页面`
1. 选择 `标签创建来源` 后输入 `标签名称` 后点击 `确定` 按钮即可完成标签创建
### 2. 通过命令行创建
当需要在本地同步仓库创建新标签时,则需要使用命令行方式创建。
1. 进入本地仓库目录
1. 使用 `git checkout <source branch>` 来切换到 `标签创建来源`
1. 使用 `git tag -a <tag> -m "<memo>"` 创建标签
参考: [Git 常用命令参考](../git/git_command_reference.md)

View File

@@ -0,0 +1,7 @@
# Fork 仓库
当需要创建一个与某个仓库内容一致的仓库时,则需要使用 `Fork` 方式创建仓库。
具体可以参照文档中 `仓库/创建仓库` 中的 `通过 Fork 创建` 一节.
参照: [创建仓库](create_repository.md)

View File

@@ -0,0 +1,23 @@
[Create Repository](create_repository.md)
[Fork](fork_repository.md)
[Commit](commit.md)
[Reveal Commits](view_commits.md)
[Reveal Files](view_files.md)
[View File Change History](view_history.md)
[Create Branch](create_branch.md)
[Protected Branch](protected_branch.md)
[Create Tag](create_tag.md)
[Merge Request](merge_request.md)
[Members](members.md)
[Settings](settings.md)

View File

@@ -0,0 +1,59 @@
# 成员管理
`仓库成员` 管理仓库成员对于仓库的操作权限。当用户操作仓库时, 会优先检查用户是否在 `仓库成员` 中, 如果用户在 `仓库成员` 列表中则按照用户 `仓库角色` 来鉴权; 如果用户不在仓库则会检查用户是否在此仓库的 `仓库组成员` 列表中, 如果用户在 `仓库组成员` 列表中则按照用户的 `仓库组角色` 来鉴权。
### 邀请成员
邀请成员步骤如下:
1. 进入`仓库主页`
1. 在主页菜单中选择 `成员` 选项即可进入 `成员列表` 页面
1.`成员列表` 页面的分支列表右上方输入框输入新成员邮箱后点击 `邀请成员` 即可邀请成员
> - 新邀请的成员角色默认为 `访客`
> - 如果想对成员角色做调整请参照此文档 `管理成员权限` 章节
> - 如果想了解角色具体权限请参照此文档 `角色与权限` 章节
### 管理成员
管理成员步骤如下:
1. 参照此文档 `邀请成员` 章节进入 `成员列表` 页面
1.`成员列表` 页面, 即可通过列表项目最右边的下拉菜单进行管理
> - 如果你是具备 `仓库组管理` 权限,可以再次页面管理 `仓库组成员`
> - 如果 `仓库成员角色` 在此仓库范围内可以覆盖 `仓库组成员角色`
### 角色与权限
`CodeFever` 角色分为 `访客`, `监督者`, `开发者`, `维护者``所有者` 五种。 每种具体权限如下:
| 权限名称 | 访客 | 监督者 | 开发者 | 维护者 | 所有者 |
| -:| :-: | :-: | :-: | :-: | :-: |
| **仓库** | - | - | - | - | - |
| Pull | O | O | O | O | O |
| Push | X | X | O | O | O |
| 删除仓库 | X | X | X | X | O |
| 修改仓库成员 | X | X | X | O | O |
| 修改仓库信息 | X | X | X | O | O |
| 变更所有者 | X | X | X | X | O |
| 修改仓库 URL | X | X | X | X | O |
| 分支操作 | X | X | X | O | O |
| 标签操作 | X | X | X | O | O |
| 保护分支操作 | X | X | X | O | O |
| 默认分支操作 | X | X | X | O | O |
| **合并请求** | - | - | - | - | - |
| 查看 | X | O | O | O | O |
| 提交 | X | X | O | O | O |
| 关闭 | X | X | X | O | O |
| 合并 | X | X | X | X | O |
| **仓库组** | - | - | - | - | - |
| 创建仓库 | X | X | X | O | O |
| 删除仓库 | X | X | X | X | O |
| 修改仓库组成员 | X | X | X | O | O |
| 修改仓库组信息 | X | X | X | O | O |
| 修改仓库组 URL | X | X | X | X | O |
| 修改仓库组所有者 | X | X | X | X | O |
> - `仓库` 和 `仓库组` 的 `创建者` 不受 `成员列表` 约束, 并且拥有 `所有者` 角色。
> - 表格中: `X` 表示 `无权限`; `O` 表示 `有权限`; `-` 表示 `不适用`;

View File

@@ -0,0 +1,40 @@
# 合并请求
在 CodeFever 中, 两个分支 (同仓库或不同仓库) 的 `合并` 是通过 `合并请求` 来完成的。你也可以在本地使用 `git merge` 来完成 `合并` 操作,但是我们并不推荐这么做。
### 创建合并请求
当两个分支同时满足以下条件时, 可以使用 `合并请求``Web 页面``合并` , 并可以查看合并差异。
- 如果两个分支在同一仓库, 这两个分支名称不同
- 如果两个分支在不同仓库, 这两个仓库之间是 `fork` 关系
创建合并请求的步骤:
* 在本例中, 假设的操作是将 `<source branch>` 合并到 `<target branch>` 中。`<source branch>``<target branch>` 是否在同一个仓库都适用于此步骤。
1. 进入 `<source branch>` 所在的仓库的 `仓库主页`
1. 在主页菜单中选择 `合并请求` 选项即可进入 `合并请求` 页面
1.`合并请求` 页面右上方点击 `创建合并请求` 按钮即可进入 `选择分支页面`
1. 选择 `源分支` 为本仓库的 `<source branch>` 后 选择 `目标分支``<target branch>` 所在仓库的 `<target branch>` 后点击 `对比分支并继续` 按钮进入 `创建合并请求页面`
1.`创建合并请求页面` 你可以对比两个分支的差异, 同时可以为你的合并一位 `代码评审员``代码评审员` 在评审后差异后所做的操作将会作为 `合并人员` 合并操作的参考。
1.`创建合并请求页面` 填写合并请求相关的 `标题``描述` 后点击页面上的 `提交` 按钮即可完成合并请求操作。
### 处理合并请求
> 如果你具有处理 `合并请求` 权限 (参照:[成员管理](members.md)), 则可以在合并请求详情页面按到合并按钮。
处理一个合并请求的步骤:
1. 进入合并请求所在仓库的 `仓库主页`
1. 在主页菜单中选择 `合并请求` 选项即可进入 `合并请求` 页面
1.`合并请求列表` 上放的选项卡上选择 `已打开` 选项卡
1.`合并请求列表` 中选择需要合并的项目并点击进入 `合并请求详情` 页面
1. 确认 `代码差异`, `提交动态``合并请求` 选项卡显示的内容是否达到合并标准
1. 点击 合并 按钮完成合并
> 当合并过程中出现 `冲突` 的时候, 合并请求详情页面会有文档提示引导你在本地完成合并后再 `push` 到被合并的的分支上。 (参照:[本地分支合并](../git/merge_branch.md))

View File

@@ -0,0 +1,7 @@
# 保护分支
当需要对某个分支按照角色限制更改时,可以使用 `保护分支` 功能对特定分支按照角色设置写权限。
具体可以参照文档中 `仓库/仓库设置` 中的 `分支 - 保护分支` 一节.
参照: [仓库设置](settings.md)

View File

@@ -0,0 +1,103 @@
# 仓库设置
仓库设置用于对当前仓库的属性,成员,分支等一些列操作进行设置
`项目首页` 中左侧菜单中点击 `设置` 选项即可展开设置菜单
### 常规
常规设置用于设置 `仓库图标`, `仓库名称``仓库描述`
### 分支
分支设置用于设置 `默认分支``保护分支`
### 分支 - 默认分支
`默认分支` 用于设置仓库默认分支, 此设置将会影响:
- 每次进入 `仓库首页` 时, 仓库分支的默认显示
- 每次提交 `合并请求` 时, 目标分支的默认显示
修改 `默认分支` 需要以下操作步骤:
1. 进入 `设置` - `分支` 页面
1. 找到 `默认分支` 这一节
1. 选择需要设置为 `默认分支` 的分支
1. 点击 `设置默认分支` 按钮
### 分支 - 保护分支
`保护分支` 用于设置仓库内特定分支的写权限, 此设置将会影响:
- 每次处理 `合并请求` 时,向指定分支 `合并` 时会对操作者身份按照 `保护分支规则` 进行检查
- 客户端每次 `push` 时,向指定分支 `push` 时会对操作者身份按照 `保护分支规则` 进行检查
创建 `保护分支规则` 需要以下操作步骤:
1. 进入 `设置` - `分支` 页面
1. 找到 `保护分支` 这一节
1. 点击 `新建保护分支规则` 按钮
1. 设置完 `规则名称`, `允许推送``允许合并` 后在 `操作` 栏点击 `确定` 按钮即可
### 高级
仓库高级设置用于设置仓库的一些危险操作,包含 `更新创建者`, `更新仓库 URL``删除仓库`
### 高级 - 更新创建者
`更新创建者` 用于修改 `仓库创建者`
此设置将会影响:
- 仓库的创建者
- 新设置的 `仓库创建者` 将会拥有该仓库的 `创建者` 身份
- 旧的 `仓库创建者` 将会拥有该仓库的 `所有者` 身份
> 如果你希望旧的 `仓库创建者` 不再拥有该仓库控制权,请在更换完仓库创建者后将旧的 `仓库创建者` 从仓库成员里移除。 参见: [成员管理](members.md)
更换 `更新创建者` 需要以下操作步骤:
1. 进入 `设置` - `高级` 页面
1. 找到 `更新创建者` 这一节
1. 选择新的 `仓库创建者`
1. 认真阅读新弹出的 `危险操作` 对话框中内容,确定要进行此操作后点击 `确定` 按钮即可
### 高级 - 更新仓库 URL
`更新仓库 URL` 用于修改 仓库的 URL
此设置将会影响:
- 仓库 `Web 页面` 的的访问地址
- Git 客户端连接仓库使用的地址 (包含 `HTTP` 和 SSH `协议`)
> 如果 `仓库 URL` 发生改变, Git 客户端在没更改成新的 URL 之前将无法连接到仓库。 需要使用 `git remote` 系列命令在为客户端修改仓库 URL。参见: [Git 常用命令参考](../git/git_command_reference.md)
修改 `仓库 URL` 需要以下操作步骤:
1. 进入 `设置` - `高级` 页面
1. 找到 `更新仓库 URL` 这一节
1. 输入新的 `仓库 URL`
1. 认真阅读新弹出的 `危险操作` 对话框中内容,确定要进行此操作后点击 `确定` 按钮即可
### 高级 - 删除仓库
`删除仓库` 删除 Git 仓库
此设置将会影响:
- 仓库 `Web 页面` 的无法继续访问
- Git 客户端无法继续连接该仓库
- 服务器端仓库数据被删除
- 客户端仓库数据保留最后一次同步的数据
注意:
> 如果仓库被删除,则数据无法恢复
`删除仓库` 需要以下操作步骤:
1. 进入 `设置` - `高级` 页面
1. 找到 `删除仓库` 这一节
1. 认真阅读新弹出的 `危险操作` 对话框中内容,确定要进行此操作后点击 `确定` 按钮即可

View File

@@ -0,0 +1,17 @@
# 查看提交
`CodeFever` 会记录所有的提交的容,并且在 `Web 界面` 上可以查看。
### 查看提交记录
查看提交记录的步骤如下:
1. 进入`仓库主页`
1. 在主页菜单中选择 `提交` 选项即可进入 `提交列表` 页面
### 查看特定提交的内容
查看某个提交内容的步骤如下:
1.`提交列表` 页面下选择你需要查看的提交即可进入 `提交详情` 页面

View File

@@ -0,0 +1,24 @@
# 查看文件
`CodeFever` 会记录所有的文件的容,并且在 `Web 界面` 上可以查看。
### 查看文件内容
查看提交记录的步骤如下:
1. 进入`仓库主页`
1. 在主页菜单中选择 `文件` 选项即可进入 `文件列表` 页面
1. 点击文件项目即可进入 `文件查看页面`
> `Markdown` 格式的文件会被预先解析,如果需要查看源代码请点击文件展示区域上方的 `展示源码` 按钮
### 查看文件历史
当在 `文件查看页面` 上查看文件时,还可以通过文件展示区域上方的 `历史``Blame` 按钮追溯文件历史变更记录。
### 可查看文件的限制
`CodeFever` 对于可以在 `Web 界面` 查看的文件做了以下限制,同时符合以下条件的文件可以查看。
- 文件尺寸小于 `2MB`
- 文件类型是 `文本``图片`

View File

@@ -0,0 +1,5 @@
# 查看文件修改历史
`文件查看页面` 点击 件展示区域上方的 `历史``Blame` 按钮追溯文件历史变更记录。
参照: [查看文件](view_files.md)

View File

@@ -0,0 +1,25 @@
# 创建仓库组
`CodeFever``仓库组``仓库` 的容器。 为方便管理, 所有 `仓库` 均存在于 `仓库组` 中。
### 创建默认仓库组
当新用户进入 CodeFever 时, 必须要创建一个 默认仓库组以承载仓库。
创建默认仓库组可以有以下几种方式:
- 直接创建一个 `仓库组` , 系统会自动将此 `仓库组` 设置为 `默认仓库组` (参照此文档 `创建仓库组` 章节)
- 直接创建一个 `仓库` , 系统会自动引导创建 `默认仓库组` (参照文档: [仓库/创建仓库](../repository/create_repository.md))
通过上述方式可以进入到 `创建仓库组页面` ,完成仓库组设置点击 `确定` 按钮即可创建默认仓库组。
### 创建仓库组
当用户已经拥有 `默认仓库组` 时,即可直接创建 `仓库组`
创建仓库组可以有以下几种方式:
- 在导航栏中点 `+` 按钮选择 `创建仓库组` 选项
- 点击 [这里](/groups/new) 直接创建 `仓库组`
通过上述方式可以进入到 `创建仓库组页面` ,完成仓库组设置点击 `确定` 按钮即可创建仓库组。

View File

@@ -0,0 +1,5 @@
[Create Repository Group](create_repository_group.md)
[Members](members.md)
[Settings](settings.md)

View File

@@ -0,0 +1,26 @@
# 成员管理
`仓库组成员` 管理 `仓库组成员` 对于 `仓库组` 及组内 `仓库` 的操作权限。
### 邀请成员
邀请成员步骤如下:
1. 进入`仓库组主页`
1. 在主页菜单中选择 `成员` 选项即可进入 `成员列表` 页面
1.`成员列表` 页面的分支列表右上方输入框输入新成员邮箱后点击 `邀请成员` 即可邀请成员
> - 新邀请的成员角色默认为 `访客`
> - 如果想对成员角色做调整请参照此文档 `管理成员权限` 章节
> - 如果想了解角色具体权限请参照 [仓库/成员管理](../repository/members.md) 中 `角色与权限` 章节
### 管理成员
管理成员步骤如下:
1. 参照此文档 `邀请成员` 章节进入 `成员列表` 页面
1.`成员列表` 页面, 即可通过列表项目最右边的下拉菜单进行管理
### 角色与权限
> 请参照 [仓库/成员管理](../repository/members.md) 中 `角色与权限` 章节

View File

@@ -0,0 +1,71 @@
# 仓库组设置
仓库组设置用于对当前仓库只有的属性,成员等一些列操作进行设置
`仓库组主页` 中左侧菜单中点击 `设置` 选项即可展开设置菜单
### 常规
常规设置用于设置 `仓库组图标`, `仓库组名称``仓库组描述`
### 高级
仓库组高级设置用于设置仓库组的一些危险操作,包含 `更新创建者`, `更新仓库组 URL``删除仓库组`
### 高级 - 更新创建者
`更新创建者` 用于修改 `仓库组创建者`
此设置将会影响:
- 仓库组的创建者
- 新设置的 `仓库组创建者` 将会拥有该 `仓库组``创建者` 身份
- 旧的 `仓库组创建者` 将会拥有该 `仓库组``所有者` 身份
> 如果你希望旧的 `仓库组创建者` 不再拥有该 `仓库组` 控制权,请在更换完 `仓库创建者` 后将旧的 `仓库创建者` 从 `仓库组成员` 里移除。 参见: [成员管理](members.md)
更换 `更新创建者` 需要以下操作步骤:
1. 进入 `设置` - `高级` 页面
1. 找到 `更新创建者` 这一节
1. 选择新的 `仓库组创建者`
1. 认真阅读新弹出的 `危险操作` 对话框中内容,确定要进行此操作后点击 `确定` 按钮即可
### 高级 - 更新仓库组 URL
`更新仓库 URL` 用于修改 `仓库的 URL`
此设置将会影响:
- `仓库组``组内仓库` `Web 页面` 的的访问地址
- Git 客户端连接该 `仓库组组内仓库` 使用的地址 (包含 `HTTP` 和 SSH `协议`)
> 如果 `仓库组 URL` 发生改变, Git 客户端在没更改成新的 URL 之前将无法连接到该 `仓库组组内仓库` 。 需要使用 `git remote` 系列命令在为客户端修改 `仓库 URL`。参见: [Git 常用命令参考](../git/git_command_reference.md)
修改 `仓库组 URL` 需要以下操作步骤:
1. 进入 `设置` - `高级` 页面
1. 找到 `更新仓库组 URL` 这一节
1. 输入新的 `仓库组 URL`
1. 认真阅读新弹出的 `危险操作` 对话框中内容,确定要进行此操作后点击 `确定` 按钮即可
### 高级 - 删除仓库组
此设置将会影响:
- 仓库 `Web 页面` 的无法继续访问
- Git 客户端无法继续连接该组组内仓库
- 服务器端仓库组数据被删除
- 客户端该组组内仓库数据保留最后一次同步的数据
注意:
> - 如果仓库组被删除,则数据无法恢复
> - `默认仓库组` 不能被删除
> - 需要手动删除掉该组组内所有 `仓库` 才能进行此操作
`删除仓库组` 需要以下操作步骤:
1. 进入 `设置` - `高级` 页面
1. 找到 `删除仓库组` 这一节
1. 认真阅读新弹出的 `危险操作` 对话框中内容,确定要进行此操作后点击 `确定` 按钮即可

1
doc/en-us/start/index.md Normal file
View File

@@ -0,0 +1 @@
[Welcome](welcome.md)

View File

@@ -0,0 +1,70 @@
# 欢迎使用 CodeFever Community 版本
### 关于 CodeFever
`CodeFever` 项目起初由 蒲公英开发者服务平台 开发和维护,项目于 2020 年 6 月上线 [https://codefever.pgyer.com/](https://codefever.pgyer.com/) 。 经过将近两年时间打磨和稳定性验证,于 2022 年 2 月开源,接受社区的考验。
`CodeFever` 开源后,开源版本称为 `CodeFever Conmmunity` 版本。同时公有云版本继续服务,继续称为 `CodeFever。`
`CodeFever Community``英蒲公英开发者服务平台` 团队完整自主研发并基于 `MIT` 协议进行完整开源,拥有完全自主的知识产权,因此您可以放心使用而不必考虑是否侵犯他人权利。
`CodeFever Community` 保留了 `CodeFever` 的大部分功能,并且进行了更适合单机部署的优化。`CodeFever Community` 也会随 `CodeFever` 更新的同时接受来自社区的提交。
### 如何安装
`CodeFever` 提供 `从头开始安装``Docker 镜像安装` 两种安装方式, 可以根据自己的实际需要选择安装方式。
满足以下要求的用户和选择 `从头开始安装` 的方式进行安装。
- 学习和技术交流
- 需要做定制化修改
- `Docker 镜像安装` 不能满足处理 `Bug` 和提交 `PR` 的需求
- Docker 镜像不能在当前 `操作系统``硬件架构` 上使用
参照: [从头开始安装](../installation/install_from_scratch.md)
如果不满足上述要求,你可以选择使用 `Docker 镜像安装` 方式安装 `CodeFever Community`
参照: [Docker 镜像安装](../installation/install_via_docker.md)
### 使用
[仓库](../reposiotry)
[仓库组](../reposiotry_group)
[管理员设置](../admin)
[Git](../git)
[常见问题](../common)
### 问题反馈
如果你在使用过程中遇到期望外的结果,欢迎提交 `Issue`
参照: [提交问题](../contribute/bug_fix_issue.md)
如果你希望在 `CodeFever Community` 添加一些特性,也欢迎提交 `Issue`
参照: [提交特性请求](../contribute/request_feature_issue.md)
### 贡献代码
欢迎提交 `PR`, 请确定修复 `Bug` 后或者新增 `Feature` 后进行适当的测试。
如果 `PR` 内容是问题,请先提交 `Issue` 并在提交 PR 时引用该 `Issue`
参照: [提交问题修复](../contribute/bug_fix_pr.md)
如果 `PR` 内容是新特性,请在 `PR` 中请尽量详细描述此特性的内容,如果此 PR 是针对某个特性请求的提交,在提交 PR 时引用该 `Issue`
参照: [提交新特性](../contribute/new_feature_pr.md)
### 贡献文档
欢迎指正文档中的错误或参加翻译工作
提交 `PR` 前请确定文档内容表述是否准确
参照: [提交文档](../contribute/doc_pr.md)

View File

@@ -0,0 +1,79 @@
# 概览和系统服务
此页面没有管理功能, 仅用于监视 `系统资源使用量``服务状态`
### 进入概览页面
能查看 `概览页面` 需要满足以下条件
- `CodeFever` 系统 `管理员`
进入 `概览页面` 的步骤:
1.`CodeFever` 导航栏右侧找到 `管理后台` 按钮
1. 点击 `管理后台` 按钮进入 `管理后台`
1. 点击左侧菜单栏 `概览` 选项
### 统计
统计部分用于展示 `CodeFever` 使用量
> `CodeFever` 对于使用量没有限制
### 系统资源
统计部分用于展示 `系统资源` 使用量
> 当出现性能问题时, 需要查看此面板确定有足够的 `系统资源` 可以使用
### 磁盘使用量
统计部分用于展示 `磁盘` 使用量
> CodeFever 的 Git 仓库均保存在 `/data/www/codefever-community/git-storage` 目录下, 如果磁盘空间剩余不足,请考 `扩容磁盘` 或 将 Git 仓库目录链接保存到其他磁盘上。
### 系统服务状态及维护
`CodeFever` 有三个主要的系统服务, 分别是 `PHP` , `Nginx``CodeFever` 。如果某个服务异常需要手动重新启动。
具体服务启动方式如下:
#### PHP
说明: 脚本引擎, 用于程序运行
支持的维护命令:
```shell
service php-fpm start # 启动
service php-fpm stop # 停止
service php-fpm restart # 重新启动
service php-fpm reload # 重新启动
service php-fpm status # 查看状态
```
#### Nginx
说明: 服务器软件, 用于 HTTP 反向代理服务
支持的维护命令:
```shell
service nginx start # 启动
service nginx stop # 停止
service nginx restart # 重新启动
service nginx reload # 重新启动
```
#### CodeFever
说明: 基础服务, 用于 Git 客户端 `HTTP` 连接和 `SSH` 连接相关服务
支持的维护命令:
```shell
service codefever start # 启动
service codefever stop # 停止
service codefever restart # 重新启动
service codefever status # 查看状态
```

7
doc/zh-cn/admin/index.md Normal file
View File

@@ -0,0 +1,7 @@
[概览和系统服务](dashboard.md)
[用户](users.md)
[仓库和仓库组](repository_and_groups.md)
[设置](settings.md)

View File

@@ -0,0 +1,36 @@
# 仓库和仓库组
`管理后台` 中对于 `仓库``仓库组` 的管理均为对其成员进行管里。如果你需要更近一步的管理操作, 你可以将自己添加为 `仓库``仓库组` 的所有者进行管理。
### 仓库组成员
能查看 `仓库组列表页` 需要满足以下条件
- `CodeFever` 系统 `管理员`
进入 `仓库组列表页` 的步骤:
1.`CodeFever` 导航栏右侧找到 `管理后台` 按钮
1. 点击 `管理后台` 按钮进入 `管理后台`
1. 点击左侧菜单栏 `仓库组` 选项即可进入 `仓库组列表页`
1.`仓库组列表页` 中找到需要管理成员的的 `仓库组` 项目
1. 点击该项目右边的设置按钮即可以管理 `仓库组成员`
> 如果你需要对 `仓库组` 进行更近一步的管理操作, 你可以将自己添加为 `仓库组` 的所有者进行管理。
### 仓库成员
能查看 `仓库列表页` 需要满足以下条件
- `CodeFever` 系统 `管理员`
进入 `仓库列表页` 的步骤:
1.`CodeFever` 导航栏右侧找到 `管理后台` 按钮
1. 点击 `管理后台` 按钮进入 `管理后台`
1. 点击左侧菜单栏 `仓库` 选项即可进入 `仓库列表页`
1.`仓库列表页` 中找到需要管理成员的的 `仓库` 项目
1. 点击该项目右边的设置按钮即可以管理 `仓库成员`
> 如果你需要对 `仓库` 进行更近一步的管理操作, 你可以将自己添加为 `仓库` 的所有者进行管理。

View File

@@ -0,0 +1,74 @@
# 设置
管理员设置中的 设置 选项用于对 CodeFever 全局辅助功能进行设置。如果 `邮件发送` , `主机名``开放注册` 等。
`仓库组主页` 中左侧菜单中点击 `设置` 选项即可展开设置菜单
### 主机
`主机` 用于修改 `主机名`
此设置将会影响:
- 仓库 `clone 面板``仓库 URL``主机名` (包含 `HTTP``SSH`)
- 空仓库首页的提示中 `仓库 URL``主机名`
> 此设置项应填写为 `http://<ip address>` , `http://<domain name>` , `http://<host name>` 这三种格式中的一种。不要以 `/` 结尾
更换 `主机名` 需要以下操作步骤:
1.`CodeFever` 导航栏右侧找到 `管理后台` 按钮
1. 点击 `管理后台` 按钮进入 `管理后台`
1. 点击左侧菜单栏 `设置` 选项即可进入 `设置页面`
1.`设置页面` 中找到 `主机` 这一节
1.`URL` 输入框中输入你的 `主机` 设置后点击页面上的 `保存` 按钮即可
### SMTP
`SMTP` 用于进行 `发件设置`
此设置将会影响:
- 所有邮件通知里的邮件是否能够送达
> 由于收件方邮件服务商可能有很强的反垃圾邮件策略, 邮件可能会被投递到垃圾收件箱
更换 `SMTP` 需要以下操作步骤:
1.`CodeFever` 导航栏右侧找到 `管理后台` 按钮
1. 点击 `管理后台` 按钮进入 `管理后台`
1. 点击左侧菜单栏 `设置` 选项即可进入 `设置页面`
1.`设置页面` 中找到 `SMTP` 这一节
1.`发件人名称``发件人地址` 输入框中输入你的 `SMTP` 设置后点击页面上的 `保存` 按钮即可
#### 关于 `发件人名称` 和 `发件人地址` 设置的说明
由于收件服务器可能安装有强大的反垃圾邮件扩展, 因此仅设置 `SMTP` 是不够的, 你还需要对 `发件域名` 进行以下操作:
- 如果 `CodeFever` 服务器托管在 `云服务商` 上, 请联系你的 `云服务商` 确保你的 `主机` 可以访问 `外网主机``25` 端口
- 可能需要对 `发件域名` 解析进行如下设置:
```plain
mail.<发件域名> A <CodeFever 主机 IP>
<发件域名> MX mail.<发件域名>
```
- `发件人名称` 随意填写, `发件人地址` 填写 `<发件域名>` 即可
### 注册
`注册` 用于设置 `开放注册`
此设置将会影响:
- 新用户是否可以通过注册加入 `CodeFever`
更改 `开放注册` 选项需要以下操作步骤:
1. 在 `CodeFever` 导航栏右侧找到 `管理后台` 按钮
1. 点击 `管理后台` 按钮进入 `管理后台`
1. 点击左侧菜单栏 `设置` 选项即可进入 `设置页面`
1. 在 `设置页面` 中找到 `注册` 这一节
1. 调整 `开放注册` 开关设置后点击页面上的 `保存` 按钮即可
> 出于安全考虑,我们强烈推荐在公网上进行私有化部署的使用者关闭开放注册选项

50
doc/zh-cn/admin/users.md Normal file
View File

@@ -0,0 +1,50 @@
# 用户
此页面用于管理 `CodeFever` 中所有的用户的 `状态``属性`
### 进入用户管理
能查看 `用户管理` 需要满足以下条件
- `CodeFever` 系统 `管理员`
进入 `用户管理` 的步骤:
1.`CodeFever` 导航栏右侧找到 `管理后台` 按钮
1. 点击 `管理后台` 按钮进入 `管理后台`
1. 点击左侧菜单栏 `用户` 选项
### 添加用户
CodeFever 对于创建用户的策略有以下几种:
- 由用户自行注册 (需在 `管理后台` - `设置` 中打开 `开放注册` 选项)
- 由管理员添加用户 (需在 `管理后台` - `用户``添加用户`)
添加用户的步骤如下:
1. 进入 `用户管理` 页面
1. 点击 `用户列表` 上方右侧 `添加用户` 按钮
1. 在弹窗中输入 `用户名``电子邮件地址` 后点击 `确定` 按钮
1. 记录生成的用户 `密码` 即可。
1. 将用户 `邮箱``密码` 发送给 `用户` 即可
> - 出于安全考虑,我们强烈推荐在公网上进行私有化部署的使用者关闭开放注册选项 (参照 [管理员设置/设置](settings.md) 中 `注册` 一节)
> - 新用户密码由系统自动产生, 用户在登录后可以自行修改密码
> - 新用户默认为普通用户, 如果想指派为管理员参照本文档 `管理用户` 章节
### 管理用户
`管理员` 可以对用户进行以下管理操作:
- 禁用 (禁止用户登录)
- 重置密码
- 关闭 MFA 认证 (用户已经绑定 MFA 设备)
- 设置为管理员
管理用户步骤如下:
1. 进入 `用户管理` 页面
1. 点击 `用户列表` 中找到需要管理的用户的项目
1. 点击该项目最右边 `设置` 按钮
1. 在弹出的菜单中选择相应的管理项目

View File

@@ -0,0 +1,13 @@
# HTTP 和 SSH 的选择
在克隆仓库到本地时, 一般会遇到选择使用 `HTTP` 还是 `SSH` 方式来连接远端仓库的问题。
`CodeFever` 也提供了这两种方式供用户选择
### 两种方式的比较
`SSH` 方式的优点在于, 当用户在 `CodeFever` 的个人设置中设置好 `SSH Key` 以后,每次使用 `SSH` 连接远端仓库均不需要再进行任何人工的认证方式。但是使用 `SSH` 方式对于用户有一定的技术 要求, 用户必须自己生成 `SSH Key` 并完成配置。
相比之下 `HTTP` 方式更适合新手和不熟悉操作系统的用户, 不需要生成并设置 `SSH Key`。 但是在使用 `HTTPS` 方式连接远端仓库时,每次连接都需要输入邮箱和密码进行身份认证。
> 关于如何生成 `SSH Key`。 参照: [获取并设置 SSH Key](ssh_key.md)

View File

@@ -0,0 +1,15 @@
# 如何追溯文件修改
`CodeFever` 提供两种方式进行文件修改的追溯, 分别是 `历史``Blame`
### 历史 和 Blame 的区别
`历史`: 是指文件从创建到当前状态所有的变更记录的列表。
`Blame`: 是指文件当前状态每一行最后是有谁编辑的。
一般来说,`历史` 用于追溯文件的变化过程,而 `Blame` 用于查看当前文件每一行的最后编辑者。
历史 和 Blame 按钮可以在具体文件的文件内容展示标题栏找到。
参考: [仓库/查看文件](../repository/view_files.md) 中 `查看文件历史` 一节

11
doc/zh-cn/common/index.md Normal file
View File

@@ -0,0 +1,11 @@
[获取并设置 SSH Key](ssh_key.md)
[HTTP 和 SSH 的选择](clone_method.md)
[成员角色的选择](role.md)
[多个邮箱地址提交的文件变更是否能被追溯 ?](multiple_email.md)
[何选择 Git 工作流 ?](workflow.md)
[如何追溯文件修改 ?](history.md)

View File

@@ -0,0 +1,19 @@
# 多个邮箱地址提交的文件变更是否能被追溯
一般情况, 每个用户在自己的 Git 客户端中设置的邮箱地址是一致的。当使用一致的邮箱提交文件时, 提交信息会被识别成相应的用户。
但是, 在某些情况下, 当同一个用户在不同的 Git 客户端设置的提交邮箱不一致时, 则可能出现在同一个项目中同一个人的提交会以多个身份的形式呈现出来的情况。
当有一个用户拥有多个提交邮箱时并且希望这些提交邮箱对应的提交都以自己的身份标记时。则需要进行 `多邮箱` 设置。
### 多邮箱设置
当需要将多个提交身份映射成自己的身份时, 需要进行 `多邮箱` 设置。
具体设置步骤如下:
1.`导航栏` 右侧找到自己的 `头像` 并点击
1. 在展开的菜单中点击 `多邮箱` 选项打开 `邮箱管理页面`
1.`邮箱管理页面` 找到并点击 `新建邮箱地址` 按钮在新出现的输入框中输入新的邮箱并点击右侧的 `确定` 按钮即可
> - 只有 `主邮箱` 地址才能作为 `登录邮箱`

21
doc/zh-cn/common/role.md Normal file
View File

@@ -0,0 +1,21 @@
# 成员角色的选择
### 角色的选择
`CodeFever` 内置五种用户角色,分别是 `访客` , `监督者` , `开发者` , `维护者``所有者`
五类角色具体描述为:
`访客`: 仓库中权限最低的角色,对整个仓库所有的项目只有读取权限,一般用于设置给项目外成员读取代码使用。
`监督者`: 对仓库有读取权限,同时可以跟进 **合并请求** 的进度,一般用于项目组内 QA 、测试人员 项目经理 等 角色。
`开发者`: 对于仓库代码有读写权限,可以推送代码,可以提交和查看 `合并请求。一般用于项目的开发人员。`
`维护者`: 对于仓库代码和仓库管理有基础权限,可以合并和关闭 `合并请求` 。一般用与项目的技术管理人员或技术主管。
`所有者`: 具有仓库的所有权限。
### 角色权限
角色权限列表参见 [仓库/成员管理](../repository/members.md) 中的 `角色与权限` 一节

View File

@@ -0,0 +1,41 @@
# 获取并设置 SSH Key
`SSH Key` 用于 Git 客户端通过 `SSH` 协议与远端仓库通信的身份认证。当在 `CodeFever` 中设置 `SSH Key` 之后,使用 `SSH` 方式连接位于 `CodeFever` 上的仓库时不需要再输入用户和密码进行认证。
### 获取 SSH Key
当使用 `Linux``MacOS` 系统时,系统会默认安装 `ssh` 相关组件。此时, `SSH Key` 存在于家目录下的 `.ssh` 目录下。当使用 `Windows` 操作系统时,需要安装 `Git Bash`, 文中提到的所有命令需要在 `Git Bash` 中输入。
获取 `SSH Key` 的步骤如下:
1. 在终端输入以下命令即可查看该目录
```shell
ls -al ~/.ssh
```
1. 如果目录中包含 `id_rsa.pub` 或 `id_dsa.pub` 文件时, 则不需要新产生 `SSH Key`; 否则, 需要手动生成 `SSH Key`。在终端输入以下命令可以生成 SSH Key
```shell
ssh-keygen -t rsa -C ”<name or comment>“
```
> 注意: 参数 `-C` 后面可以输入任何你希望标识该 SSH Key 的名称
1. 此时, `~/.ssh` 目录下应该会新增一个名为 `id_rsa.pub` 的文件, 这个文件里面存储的就是 `SSH Key`。在终端输入以下命令可以查看 `SSH Key` 内容。
```shell
cat ~/.ssh/id_rsa.pub
```
### 设置 SSH Key
当获取到 SSH Key 之后, 需要将 SSH Key 设置到 CodeFever 上。
具体设置步骤如下:
1. 在 `导航栏` 右侧找到自己的 `头像` 并点击
1. 在展开的菜单中点击 `SSH Key` 选项打开 `SSH Key 设置页面`
1. 在 `SSH Key 设置页面` 输入 `SSH Key` 点击 `新增 SSH Key` 按钮即可
> - 由于 `SSH Key` 用于鉴别用户身份, 因此每个 `SSH Key` 只能添加到一个账号里, 否则会提示 `SSH Key` 已经添加

View File

@@ -0,0 +1,31 @@
# 何选择 Git 工作流 ?
选择 `CodeFever` 则相当于选择了 `分布式` Git 工作流程, 常见的分布式流程有 `集中式工作流` , `集成管理者工作流``主管副主管工作流` 每一种工作流都有自己的特点, 这里需要根据自己项目的特点来判断。
`CodeFever` 提供了 `分支` , `合并请求``Fork` 功能,因此可以轻松支持这三种工作流。
### 集中式工作流
集中式系统中通常使用的是单点协作模型——集中式工作流。 一个中心 `仓库`, 可以接受代码, 所有人将自己的工作与之同步。 若干个开发者则作为节点, 即中心仓库的消费者与中心仓库同步。
这意味着如果两个开发者从中心仓库克隆代码下来, 同时做了一些修改, 那么只有第一个开发者可以顺利地把数据推送回共享服务器。 第二个开发者在推送修改之前, 必须先将第一个人的工作合并进来, 这样才不会覆盖第一个人的修改。 这和 Subversion (或任何 CVCS中的概念一样, 而且这个模式也可以很好地运用到 Git 中。
如果在公司或者团队中, 你已经习惯了使用这种集中式工作流程, 完全可以继续采用这种简单的模式。 只需要搭建好一个中心仓库, 并给开发团队中的每个人推送数据的权限, 就可以开展工作了。Git 不会让用户覆盖彼此的修改。
当然这并不局限于小团队。 利用 Git 的分支模型, 通过同时在多个分支上工作的方式, 即使是上百人的开发团队也可以很好地在单个项目上协作。
### 集成管理者工作流
Git 允许多个远程仓库存在, 使得这样一种工作流成为可能: 每个开发者拥有自己仓库的写权限和其他所有人仓库的读权限。 这种情形下通常会有个代表“官方”项目的权威的仓库。 要为这个项目做贡献, 你需要从该项目克隆出一个自己的公开仓库, 然后将自己的修改推送上去。 接着你可以请求官方仓库的维护者拉取更新合并到主项目。 维护者可以将你的仓库作为远程仓库添加进来, 在本地测试你的变更, 将其合并入他们的分支并推送回官方仓库。
这是 GitHub 和 GitLab 等集线器式hub-based工具最常用的工作流程。人们可以容易地将某个项目派生成为自己的公开仓库, 向这个仓库推送自己的修改, 并为每个人所见。 这么做最主要的优点之一是你可以持续地工作, 而主仓库的维护者可以随时拉取你的修改。 贡献者不必等待维护者处理完提交的更新——每一方都可以按照自己的节奏工作。
### 主管与副主管工作流
这其实是多仓库工作流程的变种。 一般拥有数百位协作开发者的超大型项目才会用到这样的工作方式。 被称为 `副主管lieutenant` 的各个集成管理者分别负责集成项目中的特定部分。 所有这些副主管头上还有一位称为 `主管dictator` 的总集成管理者负责统筹。 主管维护的仓库作为参考仓库, 为所有协作者提供他们需要拉取的项目代码。
这种工作流程并不常用, 只有当项目极为庞杂, 或者需要多级别管理时, 才会体现出优势。 利用这种方式, 项目总负责人(即主管)可以把大量分散的集成工作委托给不同的小组负责人分别处理, 然后在不同时刻将大块的代码子集统筹起来, 用于之后的整合。
> 详细请阅读参考文章: [分布式 Git - 分布式工作流程](https://git-scm.com/book/en/v2/Distributed-Git-Distributed-Workflows)

View File

@@ -0,0 +1,19 @@
# 提交问题
当在使用 CodeFever 过程遇到问题时, 可以通过在 Github 项目上提交问题来得到帮助。
Github 项目问题提交地址: [issues - PGYRE/codefever](https://github.com/PGYER/codefever/issues)
问题的类型可以是:
- `Bug` 修复
- 易用性改进
- 特性建议
- 文档或文案修改
在提交问题之前, 请确定以下工作已经完成:
- 问题可被修改
- 新特性符合基本逻辑
- 已搜索项目 `issue` 列表, 确定没有类似或相似问题
- 如果内容是 `Bug` 修复, 需要给出简单的测试用例

View File

@@ -0,0 +1,13 @@
# 提交问题修复
### 代码贡献
当使用 `CodeFever` 过程中发现有 `Bug` 并且你有能力进行补充或修复时, 欢迎向我们提交问题修复 `PR`
提交问题修复 `PR` 的步骤如下:
1. `Fork` 本项目
1. 完成 `Bug` 修复工作
1. 提交 `PR` 前请到 `issue` 中搜索您的修改是否解决了某些 `issue` , 如果解决了, 请在提交 `PR` 时引用
1. 如果可能,准备一段可以复现该 `Bug` 的测试用例, 添加到 `PR` 描述中
1. 提交 `PR``dev/master` 分支

View File

@@ -0,0 +1,32 @@
# 提交文档
### 文档贡献
当阅读 `CodeFever` 文档过程中发现文档中有缺失或错误时, 并且你有能力进行补充或修复时, 欢迎向我们提交文档 `PR`
文档 `PR` 接收的内容:
- 文档中的错误
- 文档中缺失的描述
- 新增更详细的文档内容
- 翻译工作
满足以上任何一条的 `PR` 均可以被合并。
提交文档 `PR` 的步骤如下:
1. `Fork` 本项目
1. 在自己的仓库下完成文档编辑工作
1. 提交 `PR` 前请到 `issue` 中搜索您的修改是否解决了某些 `issue` , 如果解决了, 请在提交 `PR` 时引用
1. 提交 `PR``dev/master` 分支
> 在正式贡献文档之前, 建议阅读下一节内容: `文档要求`
### 文档要求
- `CodeFever` 文档时树形结构, 以 `/doc` 为根目录 `/doc/zh-cn` 是中文文档目录 `/doc/en-us` 是英文文档目录。 目录按照文档内容区分并分级, 每级目录下的 `index.md` 文件为目录文件, 所有文档均为 `Markdown` 文档。
- 目录文件每行是一个 `Markdown` 链接, 使用相对路径链接其他文档。
- 文档文件名由 `单词``下划线` 组成; 使用 `Markdown` 语法; 图片和附件均保存在文档当前目录的 `assets` 目录下, 使用相对路径引用。

View File

@@ -0,0 +1,9 @@
[提交文档](../contribute/doc_pr.md)
[提交问题](../contribute/bug_fix_issue.md)
[提交特性请求](../contribute/request_feature_issue.md)
[提交问题修复](../contribute/bug_fix_pr.md)
[提交新特性](../contribute/new_feature_pr.md)

View File

@@ -0,0 +1,13 @@
# 提交新特性
### 代码贡献
当使用 `CodeFever` 过程中发现有缺失的功能, 并且你有能力进行开发时, 欢迎向我们提交新特性 `PR`
提交新特性 `PR` 的步骤如下:
1. `Fork` 本项目
1. 完成新特性研发工作
1. 提交 `PR` 前请到 `issue` 中搜索您的修改是否解决了某些 `issue` , 如果解决了, 请在提交 `PR` 时引用
1. 如果可能,准备一段可以复现该特性的测试用例, 添加到 `PR` 描述中
1. 提交 `PR``dev/master` 分支

View File

@@ -0,0 +1,12 @@
# 提交特性请求
当使用 CodeFever 过程中发现缺失功能时, 你可以通过提交 `特性请求` 来要求开发和维护团队为 `CodeFever` 添加新特性。
Github 项目特性请求提交地址: [issues - PGYRE/codefever](https://github.com/PGYER/codefever/issues)
在提交特性请求之前, 请确定以下工作已经完成:
- 新特性符合基本逻辑
- 已搜索项目 `issue` 列表, 确定没有类似或相似的特性请求
- 尽量链接此 `特性请求` 可能关联的 `issue`

21
doc/zh-cn/git/checkout.md Normal file
View File

@@ -0,0 +1,21 @@
# 回退文件到指定版本
### 回退文件到指定版本
如果需要回退某个文件到指定版本,则需要:
```shell
git checkout <commit hash> <file path>
```
命令执行成功后, 工作目录的文件内容回退到指定版本的文件内容, 同时已保存至暂存区。
还可以指定回退的版本数来进行回退操作,例如:
```shell
git checkout master~<num> <file path>
```
命令执行成功后, 工作目录中的文件内容回退到 `master` 分支最近的第 `<num> + 1` 次提交的内容, 并保存至暂存区。
参照: [Git 常用命令参考](git_command_reference.md)

20
doc/zh-cn/git/clone.md Normal file
View File

@@ -0,0 +1,20 @@
# 克隆仓库到本地
当仓库在 `CodeFever` 上创建完成后,需要 `克隆` 到本地才能继续操作。
### 如何 clone 一个仓库
空仓库具体操作如下:
1. 进入 `仓库首页`
1.`仓库首页` 右上角点击 `克隆` 按钮
1. 根据你需要的方式来复制仓库地址,默认为 `HTTP`
1. 使用如下操作来完成 `克隆`
```shell
git clone <repository url> <target path>
```
> - 关于 HTTP 和 SSH 两种形式如何选择,参见 [常见问题/HTTP 和 SSH 的选择](../common/clone_method.md)
> - 如果需要使用 SSH 方式 克隆, 需要提前设置 SSH Key 。 参见 [常见问题/获取并设置 SSH Key](../common/ssh_key.md)

View File

@@ -0,0 +1,39 @@
# 创建第一个分支
在 CodeFever 新建的仓库没有任何内容,需要在本地创建仓库的第一个分支,并推送到远程仓库。
1. 克隆远程仓库到本地(这里使用 `SSH` 的方式克隆,克隆前需要在 `SSH Key` 设置中添加本地 `SSH` 信息),并切换到仓库根目录,例如:
```shell
git clone ssh://git@your.domain:group_name/project_name.git
cd project_name
```
克隆完成后,工作目录已经切换到默认的 `master` 分支,也可以创建其他的分支,例如:
```shell
git checkout -b branch_name
```
2. 添加内容,创建一次提交,例如:
```shell
echo 'init' > readme.md
git add readme.md
git commit -m 'init commit'
```
3. 推送到远程仓库,例如:
```shell
git push origin branch_name
```
参照:
[git clone](git_command_reference.md#git-clone)
[git checkout](git_command_reference.md#git-checkout)
[git add](git_command_reference.md#git-add)
[git commit](git_command_reference.md#git-commit)
[git push](git_command_reference.md#git-push)

View File

@@ -0,0 +1,128 @@
# Git 常用命令参考
### git
作用: 传入参数以完成 git 相关操作。
参照: [git](https://git-scm.com/docs/git)
### git config
作用: 查看和设置仓库设置和全局设置的设定
参照: [git config](https://git-scm.com/docs/git-config)
### git help
作用: 查看帮助选项
参照: [git help](https://git-scm.com/docs/git-help)
### git init
作用: 从当前目录初始化一个仓库
参照: [git init](https://git-scm.com/docs/git-init)
### git clone
作用: 复制一个仓库
参照: [git clone](https://git-scm.com/docs/git-clone)
### git add
作用: 添加文件内容到索引中
参照: [git add](https://git-scm.com/docs/git-add)
### git status
作用: 显示 `工作区` 状态
参照: [git status](https://git-scm.com/docs/git-status)
### git diff
作用: 查看 `commit` 之间或 `commit``工作区` 之间的差异
参照: [git diff](https://git-scm.com/docs/git-diff)
### git commit
作用: 记录仓库文件的变更
参照: [git commit](https://git-scm.com/docs/git-commit)
### git notes
作用: 添加或查看对象的文本记录
参照: [git notes](https://git-scm.com/docs/git-notes)
### git restore
作用: 重置工作区
参照: [git restore](https://git-scm.com/docs/git-restore)
### git reset
作用: 重置当前工作区到某一特定状态
参照: [git reset](https://git-scm.com/docs/git-reset)
### git rm
作用: 从工作区和索引中移除某个文件
参照: [git rm](https://git-scm.com/docs/git-rm)
### git mv
作用: 对文件、目录或者链接进行移动或重命名操作
参照: [git mv](https://git-scm.com/docs/git-mv)
### git branch
作用: 查看、删除、创建分支
参照: [git branch](https://git-scm.com/docs/git-branch)
### git checkout
作用: 切换分支或重置工作区
参照: [git checkout](https://git-scm.com/docs/git-checkout)
### git switch
作用: 切换分支
参照: [git switch](https://git-scm.com/docs/git-switch)
### git merge
作用: 将两个或多个开发历史合并
参照: [git merge](https://git-scm.com/docs/git-merge)
### git mergetool
作用: 运行解决冲突工具来解决合并冲突
参照: [git mergetool](https://git-scm.com/docs/git-mergetool)
### git log
作用: 显示提交记录
参照: [git log](https://git-scm.com/docs/git-log)
### git stash
作用: 隐藏工作区未被记录的修改
参照: [git stash](https://git-scm.com/docs/git-stash)
### git tag
作用: 查看、删除、创建标签
参照: [git tag](https://git-scm.com/docs/git-tag)
### git worktree
作用: 管理工作区
参照: [git worktree](https://git-scm.com/docs/git-worktree)
### git fetch
作用: 从其他仓库下载仓库更新记录
参照: [git fetch](https://git-scm.com/docs/git-fetch)
### git pull
作用: 从其他仓库或分支下载仓库更新记录并合并到当前分支
参照: [git pull](https://git-scm.com/docs/git-pull)
### git push
作用: 上传仓库更新记录
参照: [git push](https://git-scm.com/docs/git-push)
### git remote
作用: 管理远端仓库
参照: [git remote](https://git-scm.com/docs/git-remote)
### git submoudle
作用: 初始化、更新和查看子模块
参照: [git submoudle](https://git-scm.com/docs/git-submoudle)
### git cherry-pick
作用: 使用指定的变更记录来应用更改
参照: [git cherry-pick](https://git-scm.com/docs/git-cherry-pick)
### git rebase
作用: 从新的基础点应用提交的变更记录
参照: [git rebase](https://git-scm.com/docs/git-rebase)
### git revert
作用: 回滚已经提交的变更记录
参照: [git revert](https://git-scm.com/docs/git-revert)
### 其他命令
参照: [Git - Reference](https://git-scm.com/docs)

15
doc/zh-cn/git/index.md Normal file
View File

@@ -0,0 +1,15 @@
[克隆仓库到本地](clone.md)
[创建第一个分支](create_branch.md)
[推送一个新分支](push_branch.md)
[还原提交的改动](revert.md)
[回退文件到指定版本](checkout.md)
[远程仓库源管理](remote.md)
[本地的分支合并](merge_branch.md)
[Git 常用命令参考](git_command_reference.md)

View File

@@ -0,0 +1,88 @@
# 本地的分支合并
`CodeFever` 上合并分支时,会遇到合并冲突的情况,目前 `CodeFever` 暂时不支持在线解决冲突,可以使用下面的流程完成冲突情况下的分支合并。这些流程不局限于解决冲突,也可以作为正常合并分支的流程。
### 同仓库内的分支合并
在同一个仓库内,在 `CodeFever` 上从 `<source branch>` 分支往 `<target branch>` 分支合并时需要在本地进行同仓库内的分支合并
同仓库内的分支合并操作步骤:
1. 在本地,先切换工作目录到分支 `<target branch>`,例如:
```shell
git checkout <target branch>
```
1. 合并 `<source branch>` 的改动到当前分支 `<target branch>`,例如:
```shell
git merge <source branch>
```
1. 修改有冲突的文件,解决冲突
1. 创建新提交
```shell
git add <changed files>
git commit -m 'memo'
```
1. 将解决冲突的改动推送到远程仓库
```shell
git push <remote name> <target branch>
```
### 不同仓库间进行分支合并
在 `CodeFever` 上,从 `<source repo>` 仓库的 `<source branch>` 分支往 `<target repo>` 仓库的 `<target branch>` 分支合并时,如果遇到冲突需要在本地进行不同仓库间进行分支合并
本地解决不同分支合并步骤:
1. 在 `<target repo>` 的本地仓库内,切换工作目录到分支 `<target branch>`
```shell
git checkout <target branch>
```
1. 为本地的 `<target repo>` 仓库添加远程仓库 `<source repo>`
```shell
> git remote add <source repo name> <source repo>
```
1. 拉取远程仓库分支 `<source repo>` 的改动
```shell
git fetch <source repo name> <source branch>
```
1. 合并 `<source repo name>/<source branch>` 的改动到当前分支 `<target branch>`
```shell
git fetch <source repo name>/<source branch>
```
1. 修改有冲突的文件,解决合并冲突
1. 创建新提交
```shell
git add <changed files>
git commit -m 'memo'
```
1. 将解决冲突的改动推送到远程仓库 `<target branch>`
```shell
git push <target remote name> <target branch>
```
1. 删除远程仓库 (可选)
```shell
git remote remove <source remote name>
```
参照: [Git 常用命令参考](git_command_reference.md)

Some files were not shown because too many files have changed in this diff Show More