Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F130724
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
188 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp
index aca1cdd..16901f9 100644
--- a/src/MainWindow.cpp
+++ b/src/MainWindow.cpp
@@ -1,2604 +1,2609 @@
#include "MainWindow.h"
#include "ui_MainWindow.h"
#include "AboutDialog.h"
#include "ApplicationGlobal.h"
#include "AreYouSureYouWantToContinueConnectingDialog.h"
#include "AvatarLoader.h"
#include "BlameWindow.h"
#include "CloneFromGitHubDialog.h"
#include "CommitPropertyDialog.h"
#include "DeleteBranchDialog.h"
#include "EditGitIgnoreDialog.h"
#include "EditTagsDialog.h"
#include "FileDiffWidget.h"
#include "GitDiff.h"
#include "JumpDialog.h"
#include "LineEditDialog.h"
#include "MySettings.h"
#include "ObjectBrowserDialog.h"
#include "ReflogWindow.h"
#include "RemoteWatcher.h"
#include "SetGpgSigningDialog.h"
#include "SettingsDialog.h"
#include "StatusLabel.h"
#include "TextEditDialog.h"
#include "common/joinpath.h"
#include "common/misc.h"
#include "platform.h"
#include "webclient.h"
#include <QClipboard>
#include <QDirIterator>
#include <QFileDialog>
#include <QFileIconProvider>
#include <QMessageBox>
#include <QMimeData>
#include <QPainter>
#include <QStandardPaths>
#include <QTimer>
FileDiffWidget::DrawData::DrawData()
{
bgcolor_text = QColor(255, 255, 255);
bgcolor_gray = QColor(224, 224, 224);
bgcolor_add = QColor(192, 240, 192);
bgcolor_del = QColor(255, 224, 224);
bgcolor_add_dark = QColor(64, 192, 64);
bgcolor_del_dark = QColor(240, 64, 64);
}
struct MainWindow::Private {
bool is_online_mode = true;
QTimer interval_10ms_timer;
QTimer remote_watcher_timer;
QImage graph_color;
QPixmap digits;
StatusLabel *status_bar_label;
QObject *last_focused_file_list = nullptr;
QListWidgetItem *last_selected_file_item = nullptr;
RemoteWatcher remote_watcher;
int repos_panel_width = 0;
};
MainWindow::MainWindow(QWidget *parent)
: BasicMainWindow(parent)
, ui(new Ui::MainWindow)
, m(new Private)
{
ui->setupUi(this);
#ifdef Q_OS_MACX
ui->action_about->setText("About Guitar...");
ui->action_edit_settings->setText("Settings...");
#endif
ui->splitter_v->setSizes({100, 400});
ui->splitter_h->setSizes({200, 100, 200});
m->status_bar_label = new StatusLabel(this);
ui->statusBar->addWidget(m->status_bar_label);
ui->widget_diff_view->bind(this);
qApp->installEventFilter(this);
ui->widget_log->setupForLogWidget(ui->verticalScrollBar_log, ui->horizontalScrollBar_log, themeForTextEditor());
onLogVisibilityChanged();
initNetworking();
showFileList(FilesListType::SingleList);
m->digits.load(":/image/digits.png");
m->graph_color = global->theme->graphColorMap();
prepareLogTableWidget();
#ifdef Q_OS_WIN
{
QFont font;
font = ui->label_repo_name->font();
font.setFamily("Meiryo");
ui->label_repo_name->setFont(font);
font = ui->label_branch_name->font();
font.setFamily("Meiryo");
ui->label_branch_name->setFont(font);
}
#endif
connect(this, &BasicMainWindow::signalWriteLog, this, &BasicMainWindow::writeLog_);
connect(ui->dockWidget_log, &QDockWidget::visibilityChanged, this, &MainWindow::onLogVisibilityChanged);
connect(ui->widget_log, &TextEditorWidget::idle, this, &MainWindow::onLogIdle);
connect(ui->treeWidget_repos, &RepositoriesTreeWidget::dropped, this, &MainWindow::onRepositoriesTreeDropped);
connect((AbstractPtyProcess *)getPtyProcess(), &AbstractPtyProcess::completed, this, &MainWindow::onPtyProcessCompleted);
connect(this, &BasicMainWindow::remoteInfoChanged, [&](){
ui->lineEdit_remote->setText(currentRemoteName());
});
// リモート監視
connect(this, &BasicMainWindow::signalCheckRemoteUpdate, &m->remote_watcher, &RemoteWatcher::checkRemoteUpdate);
connect(&m->remote_watcher_timer, &QTimer::timeout, &m->remote_watcher, &RemoteWatcher::checkRemoteUpdate);
connect(this, &MainWindow::updateButton, [&](){
doUpdateButton();
});
m->remote_watcher.start(this);
setRemoteMonitoringEnabled(true);
connect(this, &MainWindow::signalSetRemoteChanged, [&](bool f){
setRemoteChanged(f);
updateButton();
});
//
QString path = getBookmarksFilePath();
*getReposPtr() = RepositoryBookmark::load(path);
updateRepositoriesList();
webContext()->set_keep_alive_enabled(true);
getAvatarLoader()->start(this);
connect(getAvatarLoader(), &AvatarLoader::updated, this, &MainWindow::onAvatarUpdated);
*ptrUpdateFilesListCounter() = 0;
connect(ui->widget_diff_view, &FileDiffWidget::textcodecChanged, [&](){ updateDiffView(); });
if (!global->start_with_shift_key && appsettings()->remember_and_restore_window_position) {
Qt::WindowStates state = windowState();
MySettings settings;
settings.beginGroup("MainWindow");
bool maximized = settings.value("Maximized").toBool();
restoreGeometry(settings.value("Geometry").toByteArray());
settings.endGroup();
if (maximized) {
state |= Qt::WindowMaximized;
setWindowState(state);
}
}
// setTabOrder(ui->treeWidget_repos, ui->widget_log);
startTimers();
}
MainWindow::~MainWindow()
{
stopPtyProcess();
getAvatarLoader()->stop();
m->remote_watcher.quit();
m->remote_watcher.wait();
delete m;
delete ui;
}
void MainWindow::notifyRemoteChanged(bool f)
{
postUserFunctionEvent([&](QVariant const &v){
setRemoteChanged(v.toBool());
updateButton();
}, QVariant(f));
}
+bool MainWindow::isUninitialized()
+{
+ return !misc::isExecutable(appsettings()->git_command) || !misc::isExecutable(appsettings()->file_command);
+}
+
bool MainWindow::shown()
{
- while (!misc::isExecutable(appsettings()->git_command) || !misc::isExecutable(appsettings()->file_command)) {
+ while (isUninitialized()) {
if (!execWelcomeWizardDialog()) {
return false;
}
}
m->repos_panel_width = ui->stackedWidget_leftpanel->width();
ui->stackedWidget_leftpanel->setCurrentWidget(ui->page_repos);
ui->action_repositories_panel->setChecked(true);
setGitCommand(appsettings()->git_command, false);
setFileCommand(appsettings()->file_command, false);
writeLog(AboutDialog::appVersion() + '\n'); // print application version
logGitVersion(); // print git command version
setGpgCommand(appsettings()->gpg_command, false);
{
MySettings settings;
{
settings.beginGroup("Remote");
bool f = settings.value("Online", true).toBool();
settings.endGroup();
setRemoteOnline(f);
}
{
settings.beginGroup("MainWindow");
int n = settings.value("FirstColumnWidth", 50).toInt();
if (n < 10) n = 50;
ui->tableWidget_log->setColumnWidth(0, n);
settings.endGroup();
}
}
setUnknownRepositoryInfo();
checkUser();
return true;
}
void MainWindow::startTimers()
{
// interval 10ms
connect(&m->interval_10ms_timer, &QTimer::timeout, [&](){
const int ms = 10;
auto *p1 = ptrUpdateCommitTableCounter();
if (*p1 > 0) {
if (*p1 > ms) {
*p1 -= ms;
} else {
*p1 = 0;
ui->tableWidget_log->viewport()->update();
}
}
auto *p2 = ptrUpdateFilesListCounter();
if (*p2 > 0) {
if (*p2 > ms) {
*p2 -= ms;
} else {
*p2 = 0;
updateCurrentFilesList();
}
}
});
m->interval_10ms_timer.setInterval(10);
m->interval_10ms_timer.start();
startTimer(10);
}
void MainWindow::setCurrentLogRow(int row)
{
if (row >= 0 && row < ui->tableWidget_log->rowCount()) {
ui->tableWidget_log->setCurrentCell(row, 2);
ui->tableWidget_log->setFocus();
updateStatusBarText();
}
}
bool MainWindow::event(QEvent *event)
{
QEvent::Type et = event->type();
if (et == QEvent::WindowActivate) {
checkRemoteUpdate();
} else if (et == QEvent::KeyPress) {
auto *e = dynamic_cast<QKeyEvent *>(event);
Q_ASSERT(e);
int k = e->key();
if (k == Qt::Key_Escape) {
emit onEscapeKeyPressed();
} else if (k == Qt::Key_Delete) {
if (qApp->focusWidget() == ui->treeWidget_repos) {
removeSelectedRepositoryFromBookmark(true);
return true;
}
}
}
return BasicMainWindow::event(event);
}
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
QEvent::Type et = event->type();
if (et == QEvent::KeyPress) {
if (QApplication::activeModalWidget()) {
// thru
} else {
auto *e = dynamic_cast<QKeyEvent *>(event);
Q_ASSERT(e);
int k = e->key();
if (k == Qt::Key_Escape) {
if (centralWidget()->isAncestorOf(qApp->focusWidget())) {
ui->treeWidget_repos->setFocus();
return true;
}
}
if (e->modifiers() & Qt::ControlModifier) {
if (k == Qt::Key_Up || k == Qt::Key_Down) {
int rows = ui->tableWidget_log->rowCount();
int row = ui->tableWidget_log->currentRow();
if (k == Qt::Key_Up) {
if (row > 0) {
row--;
}
} else if (k == Qt::Key_Down) {
if (row + 1 < rows) {
row++;
}
}
ui->tableWidget_log->setCurrentCell(row, 0);
return true;
}
}
if (watched == ui->treeWidget_repos) {
if (k == Qt::Key_Enter || k == Qt::Key_Return) {
openSelectedRepository();
return true;
}
if (!(e->modifiers() & Qt::ControlModifier)) {
if (k >= 0 && k < 128 && QChar((uchar)k).isLetterOrNumber()) {
appendCharToRepoFilter(k);
return true;
}
if (k == Qt::Key_Backspace) {
backspaceRepoFilter();
return true;
}
if (k == Qt::Key_Escape) {
clearRepoFilter();
return true;
}
}
} else if (watched == ui->tableWidget_log) {
if (k == Qt::Key_Home) {
setCurrentLogRow(0);
return true;
}
if (k == Qt::Key_Escape) {
ui->treeWidget_repos->setFocus();
return true;
}
} else if (watched == ui->listWidget_files || watched == ui->listWidget_unstaged || watched == ui->listWidget_staged) {
if (k == Qt::Key_Escape) {
ui->tableWidget_log->setFocus();
return true;
}
}
}
} else if (et == QEvent::FocusIn) {
auto SelectItem = [](QListWidget *w){
int row = w->currentRow();
if (row < 0) {
row = 0;
w->setCurrentRow(row);
}
w->setItemSelected(w->item(row), true);
w->viewport()->update();
};
// ファイルリストがフォーカスを得たとき、diffビューを更新する。(コンテキストメニュー対応)
if (watched == ui->listWidget_unstaged) {
m->last_focused_file_list = watched;
updateStatusBarText();
updateUnstagedFileCurrentItem();
SelectItem(ui->listWidget_unstaged);
return true;
}
if (watched == ui->listWidget_staged) {
m->last_focused_file_list = watched;
updateStatusBarText();
updateStagedFileCurrentItem();
SelectItem(ui->listWidget_staged);
return true;
}
if (watched == ui->listWidget_files) {
m->last_focused_file_list = watched;
SelectItem(ui->listWidget_files);
return true;
}
}
return false;
}
void MainWindow::closeEvent(QCloseEvent *event)
{
MySettings settings;
if (appsettings()->remember_and_restore_window_position) {
setWindowOpacity(0);
Qt::WindowStates state = windowState();
bool maximized = (state & Qt::WindowMaximized) != 0;
if (maximized) {
state &= ~Qt::WindowMaximized;
setWindowState(state);
}
{
settings.beginGroup("MainWindow");
settings.setValue("Maximized", maximized);
settings.setValue("Geometry", saveGeometry());
settings.endGroup();
}
}
{
settings.beginGroup("MainWindow");
settings.setValue("FirstColumnWidth", ui->tableWidget_log->columnWidth(0));
settings.endGroup();
}
QMainWindow::closeEvent(event);
}
void MainWindow::setStatusBarText(QString const &text)
{
m->status_bar_label->setText(text);
}
void MainWindow::clearStatusBarText()
{
setStatusBarText(QString());
}
void MainWindow::onLogVisibilityChanged()
{
ui->action_window_log->setChecked(ui->dockWidget_log->isVisible());
}
void MainWindow::internalWriteLog(char const *ptr, int len)
{
ui->widget_log->logicalMoveToBottom();
ui->widget_log->write(ptr, len, false);
ui->widget_log->setChanged(false);
setInteractionCanceled(false);
}
void MainWindow::buildRepoTree(QString const &group, QTreeWidgetItem *item, QList<RepositoryItem> *repos)
{
QString name = item->text(0);
if (isGroupItem(item)) {
int n = item->childCount();
for (int i = 0; i < n; i++) {
QTreeWidgetItem *child = item->child(i);
QString sub = group / name;
buildRepoTree(sub, child, repos);
}
} else {
RepositoryItem const *repo = repositoryItem(item);
if (repo) {
RepositoryItem newrepo = *repo;
newrepo.name = name;
newrepo.group = group;
item->setData(0, IndexRole, repos->size());
repos->push_back(newrepo);
}
}
}
void MainWindow::refrectRepositories()
{
QList<RepositoryItem> newrepos;
int n = ui->treeWidget_repos->topLevelItemCount();
for (int i = 0; i < n; i++) {
QTreeWidgetItem *item = ui->treeWidget_repos->topLevelItem(i);
buildRepoTree(QString(), item, &newrepos);
}
*getReposPtr() = std::move(newrepos);
saveRepositoryBookmarks();
}
void MainWindow::onRepositoriesTreeDropped()
{
refrectRepositories();
QTreeWidgetItem *item = ui->treeWidget_repos->currentItem();
if (item) item->setExpanded(true);
}
const QPixmap &MainWindow::digitsPixmap() const
{
return m->digits;
}
int MainWindow::digitWidth() const
{
return 5;
}
int MainWindow::digitHeight() const
{
return 7;
}
void MainWindow::drawDigit(QPainter *pr, int x, int y, int n) const
{
int w = digitWidth();
int h = digitHeight();
pr->drawPixmap(x, y, w, h, m->digits, n * w, 0, w, h);
}
QString BasicMainWindow::defaultWorkingDir() const
{
return appsettings()->default_working_dir;
}
QColor MainWindow::color(unsigned int i)
{
unsigned int n = m->graph_color.width();
if (n > 0) {
n--;
if (i > n) i = n;
QRgb const *p = (QRgb const *)m->graph_color.scanLine(0);
return QColor(qRed(p[i]), qGreen(p[i]), qBlue(p[i]));
}
return Qt::black;
}
QString MainWindow::currentWorkingCopyDir() const
{
QString workdir = BasicMainWindow::currentWorkingCopyDir();
if (workdir.isEmpty()) {
RepositoryItem const *repo = selectedRepositoryItem();
if (repo) {
workdir = repo->local_dir;
return workdir;
}
}
return workdir;
}
RepositoryItem const *BasicMainWindow::findRegisteredRepository(QString *workdir) const
{
*workdir = QDir(*workdir).absolutePath();
workdir->replace('\\', '/');
if (Git::isValidWorkingCopy(*workdir)) {
for (RepositoryItem const &item : getRepos()) {
Qt::CaseSensitivity cs = Qt::CaseSensitive;
#ifdef Q_OS_WIN
cs = Qt::CaseInsensitive;
#endif
if (workdir->compare(item.local_dir, cs) == 0) {
return &item;
}
}
}
return nullptr;
}
int MainWindow::repositoryIndex_(QTreeWidgetItem const *item) const
{
if (item) {
int i = item->data(0, IndexRole).toInt();
if (i >= 0 && i < getRepos().size()) {
return i;
}
}
return -1;
}
RepositoryItem const *MainWindow::repositoryItem(QTreeWidgetItem const *item) const
{
int row = repositoryIndex_(item);
auto const &repos = getRepos();
return (row >= 0 && row < repos.size()) ? &repos[row] : nullptr;
}
RepositoryItem const *MainWindow::selectedRepositoryItem() const
{
return repositoryItem(ui->treeWidget_repos->currentItem());
}
static QTreeWidgetItem *newQTreeWidgetItem()
{
auto *item = new QTreeWidgetItem;
item->setSizeHint(0, QSize(20, 20));
return item;
}
QTreeWidgetItem *MainWindow::newQTreeWidgetFolderItem(QString const &name)
{
QTreeWidgetItem *item = newQTreeWidgetItem();
item->setText(0, name);
item->setData(0, IndexRole, GroupItem);
item->setIcon(0, getFolderIcon());
item->setFlags(item->flags() | Qt::ItemIsEditable);
return item;
}
void MainWindow::updateRepositoriesList()
{
QString path = getBookmarksFilePath();
auto *repos = getReposPtr();
*repos = RepositoryBookmark::load(path);
QString filter = getRepositoryFilterText();
ui->treeWidget_repos->clear();
std::map<QString, QTreeWidgetItem *> parentmap;
for (int i = 0; i < repos->size(); i++) {
RepositoryItem const &repo = repos->at(i);
if (!filter.isEmpty() && repo.name.indexOf(filter, 0, Qt::CaseInsensitive) < 0) {
continue;
}
QTreeWidgetItem *parent = nullptr;
{
QString group = repo.group;
if (group.startsWith('/')) {
group = group.mid(1);
}
auto it = parentmap.find(group);
if (it != parentmap.end()) {
parent = it->second;
}
if (!parent) {
QStringList list = group.split('/', QString::SkipEmptyParts);
if (list.isEmpty()) {
list.push_back(tr("Default"));
}
for (QString const &name : list) {
if (name.isEmpty()) continue;
if (!parent) {
auto it = parentmap.find(name);
if (it != parentmap.end()) {
parent = it->second;
} else {
parent = newQTreeWidgetFolderItem(name);
ui->treeWidget_repos->addTopLevelItem(parent);
}
} else {
QTreeWidgetItem *child = newQTreeWidgetFolderItem(name);
parent->addChild(child);
parent = child;
}
parent->setExpanded(true);
}
Q_ASSERT(parent);
parentmap[group] = parent;
}
parent->setData(0, FilePathRole, "");
}
QTreeWidgetItem *child = newQTreeWidgetItem();
child->setText(0, repo.name);
child->setData(0, IndexRole, i);
child->setIcon(0, getRepositoryIcon());
child->setFlags(child->flags() & ~Qt::ItemIsDropEnabled);
parent->addChild(child);
parent->setExpanded(true);
}
}
void MainWindow::showFileList(FilesListType files_list_type)
{
switch (files_list_type) {
case FilesListType::SingleList:
ui->stackedWidget_filelist->setCurrentWidget(ui->page_files);
break;
case FilesListType::SideBySide:
ui->stackedWidget_filelist->setCurrentWidget(ui->page_uncommited);
break;
}
}
void MainWindow::clearFileList()
{
showFileList(FilesListType::SingleList);
ui->listWidget_unstaged->clear();
ui->listWidget_staged->clear();
ui->listWidget_files->clear();
}
void MainWindow::clearDiffView()
{
ui->widget_diff_view->clearDiffView();
}
void MainWindow::clearRepositoryInfo()
{
internalClearRepositoryInfo();
ui->label_repo_name->setText(QString());
ui->label_branch_name->setText(QString());
}
void MainWindow::setRepositoryInfo(QString const &reponame, QString const &brname)
{
ui->label_repo_name->setText(reponame);
ui->label_branch_name->setText(brname);
}
void MainWindow::updateFilesList(QString id, bool wait)
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
if (!wait) return;
clearFileList();
Git::FileStatusList stats = g->status();
setUncommitedChanges(!stats.empty());
FilesListType files_list_type = FilesListType::SingleList;
bool staged = false;
auto AddItem = [&](QString const &filename, QString header, int idiff){
if (header.isEmpty()) {
header = "(??\?) "; // damn trigraph
}
QListWidgetItem *item = new QListWidgetItem(header + filename);
item->setData(FilePathRole, filename);
item->setData(DiffIndexRole, idiff);
item->setData(HunkIndexRole, -1);
switch (files_list_type) {
case FilesListType::SingleList:
ui->listWidget_files->addItem(item);
break;
case FilesListType::SideBySide:
if (staged) {
ui->listWidget_staged->addItem(item);
} else {
ui->listWidget_unstaged->addItem(item);
}
break;
}
};
if (id.isEmpty()) {
bool uncommited = isThereUncommitedChanges();
if (uncommited) {
files_list_type = FilesListType::SideBySide;
}
if (!makeDiff(uncommited ? QString() : id, diffResult())) {
return;
}
std::map<QString, int> diffmap;
for (int idiff = 0; idiff < diffResult()->size(); idiff++) {
Git::Diff const &diff = diffResult()->at(idiff);
QString filename = diff.path;
if (!filename.isEmpty()) {
diffmap[filename] = idiff;
}
}
showFileList(files_list_type);
for (Git::FileStatus const &s : stats) {
staged = (s.isStaged() && s.code_y() == ' ');
int idiff = -1;
QString header;
auto it = diffmap.find(s.path1());
if (it != diffmap.end()) {
idiff = it->second;
}
QString path = s.path1();
if (s.code() == Git::FileStatusCode::Unknown) {
qDebug() << "something wrong...";
} else if (s.code() == Git::FileStatusCode::Untracked) {
// nop
} else if (s.isUnmerged()) {
header += "(unmerged) ";
} else if (s.code() == Git::FileStatusCode::AddedToIndex) {
header = "(add) ";
} else if (s.code_x() == 'D' || s.code_y() == 'D' || s.code() == Git::FileStatusCode::DeletedFromIndex) {
header = "(del) ";
} else if (s.code_x() == 'R' || s.code() == Git::FileStatusCode::RenamedInIndex) {
header = "(ren) ";
path = s.path2(); // renamed newer path
} else if (s.code_x() == 'M' || s.code_y() == 'M') {
header = "(chg) ";
}
AddItem(path, header, idiff);
}
} else {
if (!makeDiff(id, diffResult())) {
return;
}
showFileList(files_list_type);
addDiffItems(diffResult(), AddItem);
}
for (Git::Diff const &diff : *diffResult()) {
QString key = GitDiff::makeKey(diff);
(*getDiffCacheMap())[key] = diff;
}
}
void MainWindow::updateFilesList(Git::CommitItem const &commit, bool wait)
{
QString id;
if (Git::isUncommited(commit)) {
// empty id for uncommited changes
} else {
id = commit.commit_id;
}
updateFilesList(id, wait);
}
void MainWindow::updateCurrentFilesList()
{
auto logs = getLogs();
QTableWidgetItem *item = ui->tableWidget_log->item(selectedLogIndex(), 0);
if (!item) return;
int row = item->data(IndexRole).toInt();
int count = (int)logs.size();
if (row < count) {
updateFilesList(logs[row], true);
}
}
void MainWindow::prepareLogTableWidget()
{
QStringList cols = {
tr("Graph"),
tr("Commit"),
tr("Date"),
tr("Author"),
tr("Message"),
};
int n = cols.size();
ui->tableWidget_log->setColumnCount(n);
ui->tableWidget_log->setRowCount(0);
for (int i = 0; i < n; i++) {
QString const &text = cols[i];
auto *item = new QTableWidgetItem(text);
ui->tableWidget_log->setHorizontalHeaderItem(i, item);
}
updateCommitGraph(); // コミットグラフを更新
}
void MainWindow::detectGitServerType(GitPtr const &g)
{
setServerType(ServerType::Standard);
*ptrGitHub() = GitHubRepositoryInfo();
QString push_url;
QList<Git::Remote> remotes;
g->getRemoteURLs(&remotes);
for (Git::Remote const &r : remotes) {
if (r.purpose == "push") {
push_url = r.url;
}
}
auto Check = [&](QString const &s){
int i = push_url.indexOf(s);
if (i > 0) return i + s.size();
return 0;
};
// check GitHub
int pos = Check("@github.com:");
if (pos == 0) {
pos = Check("://github.com/");
}
if (pos > 0) {
int end = push_url.size();
{
QString s = ".git";
if (push_url.endsWith(s)) {
end -= s.size();
}
}
QString s = push_url.mid(pos, end - pos);
int i = s.indexOf('/');
if (i > 0) {
auto *p = ptrGitHub();
QString user = s.mid(0, i);
QString repo = s.mid(i + 1);
p->owner_account_name = user;
p->repository_name = repo;
}
setServerType(ServerType::GitHub);
}
}
void MainWindow::clearLog()
{
clearLogs();
clearLabelMap();
setUncommitedChanges(false);
ui->tableWidget_log->clearContents();
ui->tableWidget_log->scrollToTop();
}
void MainWindow::openRepository_(GitPtr g, bool keep_selection)
{
getObjCache()->setup(g);
int scroll_pos = -1;
int select_row = -1;
if (keep_selection) {
scroll_pos = ui->tableWidget_log->verticalScrollBar()->value();
select_row = ui->tableWidget_log->currentRow();
}
if (isValidWorkingCopy(g)) {
bool do_fetch = isOnlineMode() && (getForceFetch() || appsettings()->automatically_fetch_when_opening_the_repository);
setForceFetch(false);
if (do_fetch) {
if (!fetch(g, false)) {
return;
}
}
clearLog();
clearRepositoryInfo();
detectGitServerType(g);
updateFilesList(QString(), true);
bool canceled = false;
ui->tableWidget_log->setEnabled(false);
// ログを取得
setLogs(retrieveCommitLog(g));
// ブランチを取得
queryBranches(g);
// タグを取得
ptrTagMap()->clear();
QList<Git::Tag> tags = g->tags();
for (Git::Tag const &tag : tags) {
Git::Tag t = tag;
t.id = getObjCache()->getCommitIdFromTag(t.id);
(*ptrTagMap())[t.id].push_back(t);
}
ui->tableWidget_log->setEnabled(true);
updateCommitTableLater();
if (canceled) return;
QString branch_name;
if (currentBranch().flags & Git::Branch::HeadDetachedAt) {
branch_name += QString("(HEAD detached at %1)").arg(currentBranchName());
}
if (currentBranch().flags & Git::Branch::HeadDetachedFrom) {
branch_name += QString("(HEAD detached from %1)").arg(currentBranchName());
}
if (branch_name.isEmpty()) {
branch_name = currentBranchName();
}
QString repo_name = currentRepositoryName();
setRepositoryInfo(repo_name, branch_name);
} else {
clearLog();
clearRepositoryInfo();
}
updateRemoteInfo();
updateWindowTitle(g);
setHeadId(getObjCache()->revParse("HEAD"));
if (isThereUncommitedChanges()) {
Git::CommitItem item;
item.parent_ids.push_back(currentBranch().id);
item.message = tr("Uncommited changes");
auto p = getLogsPtr();
p->insert(p->begin(), item);
}
prepareLogTableWidget();
auto const &logs = getLogs();
const int count = logs.size();
ui->tableWidget_log->setRowCount(count);
int selrow = 0;
for (int row = 0; row < count; row++) {
Git::CommitItem const *commit = &logs[row];
{
auto *item = new QTableWidgetItem;
item->setData(IndexRole, row);
ui->tableWidget_log->setItem(row, 0, item);
}
int col = 1; // カラム0はコミットグラフなので、その次から
auto AddColumn = [&](QString const &text, bool bold, QString const &tooltip){
auto *item = new QTableWidgetItem(text);
if (!tooltip.isEmpty()) {
QString tt = tooltip;
tt.replace('\n', ' ');
tt = tt.toHtmlEscaped();
tt = "<p style='white-space: pre'>" + tt + "</p>";
item->setToolTip(tt);
}
if (bold) {
QFont font = item->font();
font.setBold(true);
item->setFont(font);
}
ui->tableWidget_log->setItem(row, col, item);
col++;
};
QString commit_id;
QString datetime;
QString author;
QString message;
QString message_ex;
bool isHEAD = (commit->commit_id == getHeadId());
bool bold = false;
{
if (Git::isUncommited(*commit)) { // 未コミットの時
bold = true; // 太字
selrow = row;
} else {
if (isHEAD && !isThereUncommitedChanges()) { // HEADで、未コミットがないとき
bold = true; // 太字
selrow = row;
}
commit_id = abbrevCommitID(*commit);
}
datetime = misc::makeDateTimeString(commit->commit_date);
author = commit->author;
message = commit->message;
message_ex = makeCommitInfoText(row, &(*getLabelMap())[row]);
}
AddColumn(commit_id, false, QString());
AddColumn(datetime, false, QString());
AddColumn(author, false, QString());
AddColumn(message, bold, message + message_ex);
ui->tableWidget_log->setRowHeight(row, 24);
}
int t = ui->tableWidget_log->columnWidth(0);
ui->tableWidget_log->resizeColumnsToContents();
ui->tableWidget_log->setColumnWidth(0, t);
ui->tableWidget_log->horizontalHeader()->setStretchLastSection(false);
ui->tableWidget_log->horizontalHeader()->setStretchLastSection(true);
m->last_focused_file_list = nullptr;
ui->tableWidget_log->setFocus();
if (select_row < 0) {
setCurrentLogRow(selrow);
} else {
setCurrentLogRow(select_row);
ui->tableWidget_log->verticalScrollBar()->setValue(scroll_pos >= 0 ? scroll_pos : 0);
}
m->remote_watcher.setCurrent(currentRemoteName(), currentBranchName());
checkRemoteUpdate();
doUpdateButton();
}
void MainWindow::removeSelectedRepositoryFromBookmark(bool ask)
{
int i = indexOfRepository(ui->treeWidget_repos->currentItem());
removeRepositoryFromBookmark(i, ask);
}
void MainWindow::doUpdateButton()
{
setNetworkingCommandsEnabled(isOnlineMode());
ui->toolButton_fetch->setDot(getRemoteChanged());
Git::Branch b = currentBranch();
ui->toolButton_push->setNumber(b.ahead > 0 ? b.ahead : -1);
ui->toolButton_pull->setNumber(b.behind > 0 ? b.behind : -1);
}
void MainWindow::updateStatusBarText()
{
QString text;
QWidget *w = qApp->focusWidget();
if (w == ui->treeWidget_repos) {
RepositoryItem const *repo = selectedRepositoryItem();
if (repo) {
text = QString("%1 : %2")
.arg(repo->name)
.arg(misc::normalizePathSeparator(repo->local_dir))
;
}
} else if (w == ui->tableWidget_log) {
QTableWidgetItem *item = ui->tableWidget_log->item(selectedLogIndex(), 0);
if (item) {
auto const &logs = getLogs();
int row = item->data(IndexRole).toInt();
if (row < (int)logs.size()) {
Git::CommitItem const &commit = logs[row];
if (Git::isUncommited(commit)) {
text = tr("Uncommited changes");
} else {
QString id = commit.commit_id;
text = QString("%1 : %2%3")
.arg(id.mid(0, 7))
.arg(commit.message)
.arg(makeCommitInfoText(row, nullptr))
;
}
}
}
}
setStatusBarText(text);
}
void MainWindow::on_action_commit_triggered()
{
commit();
}
void MainWindow::on_action_fetch_triggered()
{
if (isOnlineMode()) {
reopenRepository(true, [&](GitPtr g){
fetch(g, false);
});
} else {
updateRepository();
}
}
void MainWindow::on_action_fetch_prune_triggered()
{
if (!isOnlineMode()) return;
reopenRepository(true, [&](GitPtr g){
fetch(g, true);
});
}
void MainWindow::on_action_push_triggered()
{
push();
}
void MainWindow::on_action_pull_triggered()
{
if (!isOnlineMode()) return;
reopenRepository(true, [&](GitPtr g){
setPtyCondition(PtyCondition::Pull);
setPtyProcessOk(true);
g->pull(getPtyProcess());
while (1) {
if (getPtyProcess()->wait(1)) break;
QApplication::processEvents();
}
});
}
void MainWindow::on_toolButton_push_clicked()
{
ui->action_push->trigger();
}
void MainWindow::on_toolButton_pull_clicked()
{
ui->action_pull->trigger();
}
void MainWindow::on_treeWidget_repos_currentItemChanged(QTreeWidgetItem * /*current*/, QTreeWidgetItem * /*previous*/)
{
updateStatusBarText();
}
void MainWindow::on_treeWidget_repos_itemDoubleClicked(QTreeWidgetItem * /*item*/, int /*column*/)
{
openSelectedRepository();
}
void BasicMainWindow::execCommitPropertyDialog(QWidget *parent, Git::CommitItem const *commit)
{
CommitPropertyDialog dlg(parent, this, commit);
dlg.exec();
}
int MainWindow::indexOfRepository(QTreeWidgetItem const *treeitem) const
{
if (!treeitem) return -1;
return treeitem->data(0, IndexRole).toInt();
}
void MainWindow::on_treeWidget_repos_customContextMenuRequested(const QPoint &pos)
{
QTreeWidgetItem *treeitem = ui->treeWidget_repos->currentItem();
if (!treeitem) return;
RepositoryItem const *repo = repositoryItem(treeitem);
int index = indexOfRepository(treeitem);
if (isGroupItem(treeitem)) { // group item
QMenu menu;
QAction *a_add_new_group = menu.addAction(tr("&Add new group"));
QAction *a_delete_group = menu.addAction(tr("&Delete group"));
QAction *a_rename_group = menu.addAction(tr("&Rename group"));
QPoint pt = ui->treeWidget_repos->mapToGlobal(pos);
QAction *a = menu.exec(pt + QPoint(8, -8));
if (a) {
if (a == a_add_new_group) {
QTreeWidgetItem *child = newQTreeWidgetFolderItem(tr("New group"));
treeitem->addChild(child);
child->setFlags(child->flags() | Qt::ItemIsEditable);
ui->treeWidget_repos->setCurrentItem(child);
return;
}
if (a == a_delete_group) {
QTreeWidgetItem *parent = treeitem->parent();
if (parent) {
int i = parent->indexOfChild(treeitem);
delete parent->takeChild(i);
} else {
int i = ui->treeWidget_repos->indexOfTopLevelItem(treeitem);
delete ui->treeWidget_repos->takeTopLevelItem(i);
}
refrectRepositories();
return;
}
if (a == a_rename_group) {
ui->treeWidget_repos->editItem(treeitem);
return;
}
}
} else if (repo) { // repository item
QString open_terminal = tr("Open &terminal");
QString open_commandprompt = tr("Open command promp&t");
QMenu menu;
QAction *a_open = menu.addAction(tr("&Open"));
menu.addSeparator();
#ifdef Q_OS_WIN
QAction *a_open_terminal = menu.addAction(open_commandprompt);
(void)open_terminal;
#else
QAction *a_open_terminal = menu.addAction(open_terminal);
(void)open_commandprompt;
#endif
a_open_terminal->setIcon(QIcon(":/image/terminal.svg"));
QAction *a_open_folder = menu.addAction(tr("Open &folder"));
a_open_folder->setIcon(QIcon(":/image/explorer.svg"));
menu.addSeparator();
QAction *a_remove = menu.addAction(tr("&Remove"));
menu.addSeparator();
QAction *a_properties = addMenuActionProperty(&menu);
QPoint pt = ui->treeWidget_repos->mapToGlobal(pos);
QAction *a = menu.exec(pt + QPoint(8, -8));
if (a) {
if (a == a_open) {
openSelectedRepository();
return;
}
if (a == a_open_folder) {
openExplorer(repo);
return;
}
if (a == a_open_terminal) {
openTerminal(repo);
return;
}
if (a == a_remove) {
removeRepositoryFromBookmark(index, true);
return;
}
if (a == a_properties) {
execRepositoryPropertyDialog(repo->local_dir);
return;
}
}
}
}
void MainWindow::on_tableWidget_log_customContextMenuRequested(const QPoint &pos)
{
Git::CommitItem const *commit = selectedCommitItem();
if (commit) {
bool is_valid_commit_id = Git::isValidID(commit->commit_id);
int row = selectedLogIndex();
QMenu menu;
QAction *a_copy_id_7_letters = menu.addAction(tr("Copy commit id (7 letters)"));
QAction *a_copy_id_complete = menu.addAction(tr("Copy commit id (completely)"));
menu.addSeparator();
QAction *a_checkout = menu.addAction(tr("Checkout/Branch..."));
menu.addSeparator();
QAction *a_edit_message = nullptr;
if (row == 0 && currentBranch().ahead > 0) {
a_edit_message = menu.addAction(tr("Edit message..."));
}
QAction *a_merge = is_valid_commit_id ? menu.addAction(tr("Merge")) : nullptr;
QAction *a_rebase = is_valid_commit_id ? menu.addAction(tr("Rebase")) : nullptr;
QAction *a_cherrypick = is_valid_commit_id ? menu.addAction(tr("Cherry-pick")) : nullptr;
QAction *a_edit_tags = is_valid_commit_id ? menu.addAction(tr("Edit tags...")) : nullptr;
QAction *a_revert = is_valid_commit_id ? menu.addAction(tr("Revert")) : nullptr;
QAction *a_reset_head = nullptr;
#if 0 // 下手に使うと危険なので、とりあえず無効にしておく
if (is_valid_commit_id && commit->commit_id == head_id_) {
a_reset_head = menu.addAction(tr("Reset HEAD"));
}
#endif
menu.addSeparator();
QAction *a_delbranch = is_valid_commit_id ? menu.addAction(tr("Delete branch...")) : nullptr;
QAction *a_delrembranch = remoteBranches(commit->commit_id, nullptr).isEmpty() ? nullptr : menu.addAction(tr("Delete remote branch..."));
menu.addSeparator();
QAction *a_explore = is_valid_commit_id ? menu.addAction(tr("Explore")) : nullptr;
QAction *a_properties = addMenuActionProperty(&menu);
QAction *a = menu.exec(ui->tableWidget_log->viewport()->mapToGlobal(pos) + QPoint(8, -8));
if (a) {
if (a == a_copy_id_7_letters) {
qApp->clipboard()->setText(commit->commit_id.mid(0, 7));
return;
}
if (a == a_copy_id_complete) {
qApp->clipboard()->setText(commit->commit_id);
return;
}
if (a == a_properties) {
execCommitPropertyDialog(this, commit);
return;
}
if (a == a_edit_message) {
commitAmend();
return;
}
if (a == a_checkout) {
checkout(this, commit);
return;
}
if (a == a_delbranch) {
deleteBranch(commit);
return;
}
if (a == a_delrembranch) {
deleteRemoteBranch(commit);
return;
}
if (a == a_merge) {
mergeBranch(commit);
return;
}
if (a == a_rebase) {
rebaseBranch(commit);
return;
}
if (a == a_cherrypick) {
cherrypick(commit);
return;
}
if (a == a_edit_tags) {
ui->action_edit_tags->trigger();
return;
}
if (a == a_revert) {
revertCommit();
return;
}
if (a == a_explore) {
execCommitExploreWindow(this, commit);
return;
}
if (a == a_reset_head) {
reopenRepository(false, [](GitPtr g){
g->reset_head1();
});
return;
}
}
}
}
void MainWindow::on_listWidget_files_customContextMenuRequested(const QPoint &pos)
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
QMenu menu;
QAction *a_delete = menu.addAction(tr("Delete"));
QAction *a_untrack = menu.addAction(tr("Untrack"));
QAction *a_history = menu.addAction(tr("History"));
QAction *a_blame = menu.addAction(tr("Blame"));
QAction *a_properties = addMenuActionProperty(&menu);
QPoint pt = ui->listWidget_files->mapToGlobal(pos) + QPoint(8, -8);
QAction *a = menu.exec(pt);
if (a) {
QListWidgetItem *item = ui->listWidget_files->currentItem();
if (a == a_delete) {
if (askAreYouSureYouWantToRun("Delete", tr("Delete selected files."))) {
for_each_selected_files([&](QString const &path){
g->removeFile(path);
g->chdirexec([&](){
QFile(path).remove();
return true;
});
});
openRepository(false);
}
} else if (a == a_untrack) {
if (askAreYouSureYouWantToRun("Untrack", tr("rm --cached files"))) {
for_each_selected_files([&](QString const &path){
g->rm_cached(path);
});
openRepository(false);
}
} else if (a == a_history) {
execFileHistory(item);
} else if (a == a_blame) {
blame(item);
} else if (a == a_properties) {
execFilePropertyDialog(item);
}
}
}
void MainWindow::on_listWidget_unstaged_customContextMenuRequested(const QPoint &pos)
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
QList<QListWidgetItem *> items = ui->listWidget_unstaged->selectedItems();
if (!items.isEmpty()) {
QMenu menu;
QAction *a_stage = menu.addAction(tr("Stage"));
QAction *a_reset_file = menu.addAction(tr("Reset"));
QAction *a_ignore = menu.addAction(tr("Ignore"));
QAction *a_delete = menu.addAction(tr("Delete"));
QAction *a_untrack = menu.addAction(tr("Untrack"));
QAction *a_history = menu.addAction(tr("History"));
QAction *a_blame = menu.addAction(tr("Blame"));
QAction *a_properties = addMenuActionProperty(&menu);
QPoint pt = ui->listWidget_unstaged->mapToGlobal(pos) + QPoint(8, -8);
QAction *a = menu.exec(pt);
if (a) {
QListWidgetItem *item = ui->listWidget_unstaged->currentItem();
if (a == a_stage) {
for_each_selected_files([&](QString const &path){
g->stage(path);
});
updateCurrentFilesList();
return;
}
if (a == a_reset_file) {
QStringList paths;
for_each_selected_files([&](QString const &path){
paths.push_back(path);
});
resetFile(paths);
return;
}
if (a == a_ignore) {
QString gitignore_path = currentWorkingCopyDir() / ".gitignore";
if (items.size() == 1) {
QString file = getFilePath(items[0]);
EditGitIgnoreDialog dlg(this, gitignore_path, file);
if (dlg.exec() == QDialog::Accepted) {
QString appending = dlg.text();
if (!appending.isEmpty()) {
QString text;
QString path = gitignore_path;
path.replace('/', QDir::separator());
{
QFile file(path);
if (file.open(QFile::ReadOnly)) {
text += QString::fromUtf8(file.readAll());
}
}
size_t n = text.size();
if (n > 0 && text[(int)n - 1] != '\n') {
text += '\n'; // 最後に改行を追加
}
text += appending + '\n';
{
QFile file(path);
if (file.open(QFile::WriteOnly)) {
file.write(text.toUtf8());
}
}
updateCurrentFilesList();
return;
}
} else {
return;
}
}
QString append;
for_each_selected_files([&](QString const &path){
if (path == ".gitignore") {
// skip
} else {
append += path + '\n';
}
});
if (TextEditDialog::editFile(this, gitignore_path, ".gitignore", append)) {
updateCurrentFilesList();
}
return;
}
if (a == a_delete) {
if (askAreYouSureYouWantToRun("Delete", "Delete selected files.")) {
for_each_selected_files([&](QString const &path){
g->removeFile(path);
g->chdirexec([&](){
QFile(path).remove();
return true;
});
});
openRepository(false);
}
return;
}
if (a == a_untrack) {
if (askAreYouSureYouWantToRun("Untrack", "rm --cached")) {
for_each_selected_files([&](QString const &path){
g->rm_cached(path);
});
openRepository(false);
}
return;
}
if (a == a_history) {
execFileHistory(item);
return;
}
if (a == a_blame) {
blame(item);
return;
}
if (a == a_properties) {
execFilePropertyDialog(item);
return;
}
}
}
}
void MainWindow::on_listWidget_staged_customContextMenuRequested(const QPoint &pos)
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
QListWidgetItem *item = ui->listWidget_staged->currentItem();
if (item) {
QString path = getFilePath(item);
QString fullpath = currentWorkingCopyDir() / path;
if (QFileInfo(fullpath).isFile()) {
QMenu menu;
QAction *a_unstage = menu.addAction(tr("Unstage"));
QAction *a_history = menu.addAction(tr("History"));
QAction *a_blame = menu.addAction(tr("Blame"));
QAction *a_properties = addMenuActionProperty(&menu);
QPoint pt = ui->listWidget_staged->mapToGlobal(pos) + QPoint(8, -8);
QAction *a = menu.exec(pt);
if (a) {
QListWidgetItem *item = ui->listWidget_unstaged->currentItem();
if (a == a_unstage) {
g->unstage(path);
openRepository(false);
} else if (a == a_history) {
execFileHistory(item);
} else if (a == a_blame) {
blame(item);
} else if (a == a_properties) {
execFilePropertyDialog(item);
}
}
}
}
}
QStringList MainWindow::selectedFiles_(QListWidget *listwidget) const
{
QStringList list;
QList<QListWidgetItem *> items = listwidget->selectedItems();
for (QListWidgetItem *item : items) {
QString path = getFilePath(item);
list.push_back(path);
}
return list;
}
QStringList MainWindow::selectedFiles() const
{
if (m->last_focused_file_list == ui->listWidget_files) return selectedFiles_(ui->listWidget_files);
if (m->last_focused_file_list == ui->listWidget_staged) return selectedFiles_(ui->listWidget_staged);
if (m->last_focused_file_list == ui->listWidget_unstaged) return selectedFiles_(ui->listWidget_unstaged);
return QStringList();
}
void MainWindow::for_each_selected_files(std::function<void(QString const&)> const &fn)
{
for (QString const &path : selectedFiles()) {
fn(path);
}
}
void BasicMainWindow::execFileHistory(QListWidgetItem *item)
{
if (item) {
QString path = getFilePath(item);
if (!path.isEmpty()) {
execFileHistory(path);
}
}
}
void MainWindow::on_action_open_existing_working_copy_triggered()
{
QString dir = defaultWorkingDir();
dir = QFileDialog::getExistingDirectory(this, tr("Add existing working copy"), dir);
addWorkingCopyDir(dir, false);
}
void MainWindow::on_action_view_refresh_triggered()
{
openRepository(true);
}
void MainWindow::on_tableWidget_log_currentItemChanged(QTableWidgetItem * /*current*/, QTableWidgetItem * /*previous*/)
{
clearFileList();
QTableWidgetItem *item = ui->tableWidget_log->item(selectedLogIndex(), 0);
if (!item) return;
auto const &logs = getLogs();
int row = item->data(IndexRole).toInt();
if (row < (int)logs.size()) {
updateStatusBarText();
*ptrUpdateFilesListCounter() = 200;
}
}
void MainWindow::on_toolButton_stage_clicked()
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
g->stage(selectedFiles());
updateCurrentFilesList();
}
void MainWindow::on_toolButton_unstage_clicked()
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
g->unstage(selectedFiles());
updateCurrentFilesList();
}
void MainWindow::on_toolButton_select_all_clicked()
{
if (ui->listWidget_unstaged->count() > 0) {
ui->listWidget_unstaged->setFocus();
ui->listWidget_unstaged->selectAll();
} else if (ui->listWidget_staged->count() > 0) {
ui->listWidget_staged->setFocus();
ui->listWidget_staged->selectAll();
}
}
void MainWindow::on_toolButton_commit_clicked()
{
ui->action_commit->trigger();
}
void MainWindow::on_action_edit_global_gitconfig_triggered()
{
QString dir = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
QString path = dir / ".gitconfig";
editFile(path, ".gitconfig");
}
void MainWindow::on_action_edit_git_config_triggered()
{
QString dir = currentWorkingCopyDir();
QString path = dir / ".git/config";
editFile(path, ".git/config");
}
void MainWindow::on_action_edit_gitignore_triggered()
{
QString dir = currentWorkingCopyDir();
QString path = dir / ".gitignore";
if (editFile(path, ".gitignore")) {
updateCurrentFilesList();
}
}
int MainWindow::selectedLogIndex() const
{
auto const &logs = getLogs();
int i = ui->tableWidget_log->currentRow();
if (i >= 0 && i < (int)logs.size()) {
return i;
}
return -1;
}
void MainWindow::updateDiffView(QListWidgetItem *item)
{
clearDiffView();
m->last_selected_file_item = item;
if (!item) return;
int idiff = indexOfDiff(item);
if (idiff >= 0 && idiff < diffResult()->size()) {
Git::Diff const &diff = diffResult()->at(idiff);
QString key = GitDiff::makeKey(diff);
auto it = getDiffCacheMap()->find(key);
if (it != getDiffCacheMap()->end()) {
auto const &logs = getLogs();
int row = ui->tableWidget_log->currentRow();
bool uncommited = (row >= 0 && row < (int)logs.size() && Git::isUncommited(logs[row]));
ui->widget_diff_view->updateDiffView(it->second, uncommited);
}
}
}
void MainWindow::updateDiffView()
{
updateDiffView(m->last_selected_file_item);
}
void MainWindow::updateUnstagedFileCurrentItem()
{
updateDiffView(ui->listWidget_unstaged->currentItem());
}
void MainWindow::updateStagedFileCurrentItem()
{
updateDiffView(ui->listWidget_staged->currentItem());
}
void MainWindow::on_listWidget_unstaged_currentRowChanged(int /*currentRow*/)
{
updateUnstagedFileCurrentItem();
}
void MainWindow::on_listWidget_staged_currentRowChanged(int /*currentRow*/)
{
updateStagedFileCurrentItem();
}
void MainWindow::on_listWidget_files_currentRowChanged(int /*currentRow*/)
{
updateDiffView(ui->listWidget_files->currentItem());
}
void MainWindow::setWatchRemoteInterval(int mins)
{
if (mins > 0) {
m->remote_watcher_timer.start(mins * 60000);
} else {
m->remote_watcher_timer.stop();
}
}
void MainWindow::setRemoteMonitoringEnabled(bool enable)
{
if (enable) {
setWatchRemoteInterval(appsettings()->watch_remote_changes_every_mins);
} else {
setWatchRemoteInterval(0);
}
}
void MainWindow::dragEnterEvent(QDragEnterEvent *event)
{
if (QApplication::modalWindow()) return;
if (event->mimeData()->hasUrls()) {
event->acceptProposedAction();
event->accept();
}
}
void MainWindow::timerEvent(QTimerEvent *)
{
bool running = getPtyProcess()->isRunning();
if (ui->toolButton_stop_process->isEnabled() != running) {
ui->toolButton_stop_process->setEnabled(running);
ui->action_stop_process->setEnabled(running);
setNetworkingCommandsEnabled(!running);
}
if (!running) {
setInteractionMode(InteractionMode::None);
}
while (1) {
char tmp[1024];
int len = getPtyProcess()->readOutput(tmp, sizeof(tmp));
if (len < 1) break;
writeLog(tmp, len);
}
}
void MainWindow::keyPressEvent(QKeyEvent *event)
{
int c = event->key();
if (c == Qt::Key_T && (event->modifiers() & Qt::ControlModifier)) {
test();
return;
}
if (QApplication::focusWidget() == ui->widget_log) {
auto write_char = [&](char c){
if (getPtyProcess()->isRunning()) {
getPtyProcess()->writeInput(&c, 1);
}
};
auto write_text = [&](QString const &str){
std::string s = str.toStdString();
for (char i : s) {
write_char(i);
}
};
if (c == Qt::Key_Return || c == Qt::Key_Enter) {
write_char('\n');
} else {
QString text = event->text();
write_text(text);
}
}
}
void MainWindow::on_action_edit_settings_triggered()
{
SettingsDialog dlg(this);
if (dlg.exec() == QDialog::Accepted) {
ApplicationSettings const &newsettings = dlg.settings();
setAppSettings(newsettings);
setGitCommand(appsettings()->git_command, false);
setFileCommand(appsettings()->file_command, false);
setGpgCommand(appsettings()->gpg_command, false);
setRemoteMonitoringEnabled(true);
}
}
void MainWindow::onCloneCompleted(bool success, QVariant const &userdata)
{
if (success) {
RepositoryItem r = userdata.value<RepositoryItem>();
saveRepositoryBookmark(r);
setCurrentRepository(r, false);
openRepository(true);
}
}
void MainWindow::onPtyProcessCompleted(bool /*ok*/, QVariant const &userdata)
{
switch (getPtyCondition()) {
case PtyCondition::Clone:
onCloneCompleted(getPtyProcessOk(), userdata);
break;
}
setPtyCondition(PtyCondition::None);
}
void MainWindow::on_action_clone_triggered()
{
clone();
}
void MainWindow::on_action_about_triggered()
{
AboutDialog dlg(this);
dlg.exec();
}
void MainWindow::on_toolButton_clone_clicked()
{
ui->action_clone->trigger();
}
void MainWindow::on_toolButton_fetch_clicked()
{
ui->action_fetch->trigger();
}
void MainWindow::clearRepoFilter()
{
ui->lineEdit_filter->clear();
}
void MainWindow::appendCharToRepoFilter(ushort c)
{
if (QChar(c).isLetter()) {
c = QChar(c).toLower().unicode();
}
ui->lineEdit_filter->setText(getRepositoryFilterText() + c);
}
void MainWindow::backspaceRepoFilter()
{
QString s = getRepositoryFilterText();
int n = s.size();
if (n > 0) {
s = s.mid(0, n - 1);
}
ui->lineEdit_filter->setText(s);
}
void MainWindow::on_lineEdit_filter_textChanged(QString const &text)
{
setRepositoryFilterText(text);
updateRepositoriesList();
}
void MainWindow::on_toolButton_erase_filter_clicked()
{
clearRepoFilter();
ui->lineEdit_filter->setFocus();
}
void MainWindow::deleteTags(QStringList const &tagnames)
{
int row = ui->tableWidget_log->currentRow();
internalDeleteTags(tagnames);
ui->tableWidget_log->selectRow(row);
}
void MainWindow::revertCommit()
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
Git::CommitItem const *commit = selectedCommitItem();
if (commit) {
g->revert(commit->commit_id);
openRepository(false);
}
}
bool MainWindow::addTag(QString const &name)
{
int row = ui->tableWidget_log->currentRow();
bool ok = internalAddTag(name);
ui->tableWidget_log->selectRow(row);
return ok;
}
void MainWindow::on_action_push_all_tags_triggered()
{
reopenRepository(false, [&](GitPtr g){
g->push(true);
});
}
void MainWindow::on_tableWidget_log_itemDoubleClicked(QTableWidgetItem *)
{
Git::CommitItem const *commit = selectedCommitItem();
if (commit) {
execCommitPropertyDialog(this, commit);
}
}
void MainWindow::on_listWidget_unstaged_itemDoubleClicked(QListWidgetItem * item)
{
execFilePropertyDialog(item);
}
void MainWindow::on_listWidget_staged_itemDoubleClicked(QListWidgetItem *item)
{
execFilePropertyDialog(item);
}
void MainWindow::on_listWidget_files_itemDoubleClicked(QListWidgetItem *item)
{
execFilePropertyDialog(item);
}
QListWidgetItem *MainWindow::currentFileItem() const
{
QListWidget *listwidget = nullptr;
if (ui->stackedWidget_filelist->currentWidget() == ui->page_uncommited) {
QWidget *w = qApp->focusWidget();
if (w == ui->listWidget_unstaged) {
listwidget = ui->listWidget_unstaged;
} else if (w == ui->listWidget_staged) {
listwidget = ui->listWidget_staged;
}
} else {
listwidget = ui->listWidget_files;
}
if (listwidget) {
return listwidget->currentItem();
}
return nullptr;
}
void MainWindow::on_action_set_config_user_triggered()
{
Git::User global_user;
Git::User repo_user;
GitPtr g = git();
if (isValidWorkingCopy(g)) {
repo_user = g->getUser(Git::Source::Local);
}
global_user = g->getUser(Git::Source::Global);
execSetUserDialog(global_user, repo_user, currentRepositoryName());
}
void MainWindow::on_action_window_log_triggered(bool checked)
{
ui->dockWidget_log->setVisible(checked);
}
void MainWindow::on_action_repo_jump_triggered()
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
NamedCommitList items = namedCommitItems(Branches | Tags | Remotes);
{
NamedCommitItem head;
head.name = "HEAD";
head.id = getHeadId();
items.push_front(head);
}
JumpDialog dlg(this, items);
if (dlg.exec() == QDialog::Accepted) {
QString text = dlg.text();
if (text.isEmpty()) return;
QString id = g->rev_parse(text);
if (id.isEmpty() && Git::isValidID(text)) {
QStringList list = findGitObject(text);
if (list.isEmpty()) {
QMessageBox::warning(this, tr("Jump"), QString("%1\n\n").arg(text) + tr("No such commit"));
return;
}
ObjectBrowserDialog dlg2(this, list);
if (dlg2.exec() == QDialog::Accepted) {
id = dlg2.text();
if (id.isEmpty()) return;
}
}
if (g->objectType(id) == "tag") {
id = getObjCache()->getCommitIdFromTag(id);
}
int row = rowFromCommitId(id);
if (row < 0) {
QMessageBox::warning(this, tr("Jump"), QString("%1\n(%2)\n\n").arg(text).arg(id) + tr("No such commit"));
} else {
setCurrentLogRow(row);
}
}
}
void MainWindow::mergeBranch(Git::CommitItem const *commit)
{
if (!commit) return;
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
g->mergeBranch(commit->commit_id);
openRepository(true);
}
void MainWindow::rebaseBranch(Git::CommitItem const *commit)
{
if (!commit) return;
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
QString text = tr("Are you sure you want to rebase the commit ?");
text += "\n\n";
text += "> git rebase " + commit->commit_id;
int r = QMessageBox::information(this, tr("Rebase"), text, QMessageBox::Ok, QMessageBox::Cancel);
if (r == QMessageBox::Ok) {
g->rebaseBranch(commit->commit_id);
openRepository(true);
}
}
void MainWindow::cherrypick(Git::CommitItem const *commit)
{
if (!commit) return;
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
g->cherrypick(commit->commit_id);
openRepository(true);
}
void MainWindow::on_action_repo_checkout_triggered()
{
checkout();
}
void MainWindow::on_action_delete_branch_triggered()
{
deleteBranch();
}
void MainWindow::on_toolButton_terminal_clicked()
{
openTerminal(nullptr);
}
void MainWindow::on_toolButton_explorer_clicked()
{
openExplorer(nullptr);
}
void MainWindow::on_action_reset_HEAD_1_triggered()
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
g->reset_head1();
openRepository(false);
}
void MainWindow::on_action_create_a_repository_triggered()
{
createRepository(QString());
}
void MainWindow::setNetworkingCommandsEnabled(bool f)
{
ui->action_clone->setEnabled(f);
ui->toolButton_clone->setEnabled(f);
if (!Git::isValidWorkingCopy(currentWorkingCopyDir())) {
f = false;
}
bool opened = !currentRepository().name.isEmpty();
ui->action_fetch->setEnabled(f || opened);
ui->toolButton_fetch->setEnabled(f || opened);
if (isOnlineMode()) {
ui->action_fetch->setText(tr("Fetch"));
ui->toolButton_fetch->setText(tr("Fetch"));
} else {
ui->action_fetch->setText(tr("Update"));
ui->toolButton_fetch->setText(tr("Update"));
}
ui->action_fetch_prune->setEnabled(f);
ui->action_pull->setEnabled(f);
ui->action_push->setEnabled(f);
ui->action_push_u->setEnabled(f);
ui->action_push_all_tags->setEnabled(f);
ui->toolButton_pull->setEnabled(f);
ui->toolButton_push->setEnabled(f);
}
bool MainWindow::isOnlineMode() const
{
return m->is_online_mode;
}
void MainWindow::setRemoteOnline(bool f)
{
m->is_online_mode = f;
QRadioButton *rb = nullptr;
rb = f ? ui->radioButton_remote_online : ui->radioButton_remote_offline;
rb->blockSignals(true);
rb->click();
rb->blockSignals(false);
ui->action_online->setCheckable(true);
ui->action_offline->setCheckable(true);
ui->action_online->setChecked(f);
ui->action_offline->setChecked(!f);
setNetworkingCommandsEnabled(f);
MySettings s;
s.beginGroup("Remote");
s.setValue("Online", f);
s.endGroup();
}
void MainWindow::on_radioButton_remote_online_clicked()
{
setRemoteOnline(true);
}
void MainWindow::on_radioButton_remote_offline_clicked()
{
setRemoteOnline(false);
}
void MainWindow::on_verticalScrollBar_log_valueChanged(int)
{
ui->widget_log->refrectScrollBar();
}
void MainWindow::on_horizontalScrollBar_log_valueChanged(int)
{
ui->widget_log->refrectScrollBar();
}
void MainWindow::on_toolButton_stop_process_clicked()
{
abortPtyProcess();
}
void MainWindow::on_action_stop_process_triggered()
{
abortPtyProcess();
}
void MainWindow::on_action_exit_triggered()
{
close();
}
void MainWindow::on_action_reflog_triggered()
{
GitPtr g = git();
Git::ReflogItemList reflog;
g->reflog(&reflog);
ReflogWindow dlg(this, this, reflog);
dlg.exec();
}
void MainWindow::blame(QListWidgetItem *item)
{
QList<BlameItem> list;
QString path = getFilePath(item);
{
GitPtr g = git();
QByteArray ba = g->blame(path);
if (!ba.isEmpty()) {
char const *begin = ba.data();
char const *end = begin + ba.size();
list = BlameWindow::parseBlame(begin, end);
}
}
if (!list.isEmpty()) {
qApp->setOverrideCursor(Qt::WaitCursor);
BlameWindow win(this, path, list);
qApp->restoreOverrideCursor();
win.exec();
}
}
void MainWindow::blame()
{
blame(currentFileItem());
}
void MainWindow::on_action_repository_property_triggered()
{
execRepositoryPropertyDialog(currentWorkingCopyDir());
}
void MainWindow::on_action_set_gpg_signing_triggered()
{
GitPtr g = git();
QString global_key_id = g->signingKey(Git::Source::Global);
QString repository_key_id;
if (g->isValidWorkingCopy()) {
repository_key_id = g->signingKey(Git::Source::Local);
}
SetGpgSigningDialog dlg(this, currentRepositoryName(), global_key_id, repository_key_id);
if (dlg.exec() == QDialog::Accepted) {
g->setSigningKey(dlg.id(), dlg.isGlobalChecked());
}
}
void MainWindow::execAreYouSureYouWantToContinueConnectingDialog()
{
using TheDlg = AreYouSureYouWantToContinueConnectingDialog;
setInteractionMode(InteractionMode::Busy);
QApplication::restoreOverrideCursor();
TheDlg dlg(this);
if (dlg.exec() == QDialog::Accepted) {
TheDlg::Result r = dlg.result();
if (r == TheDlg::Result::Yes) {
getPtyProcess()->writeInput("yes\n", 4);
} else {
setPtyProcessOk(false); // abort
getPtyProcess()->writeInput("no\n", 3);
QThread::msleep(300);
stopPtyProcess();
}
} else {
ui->widget_log->setFocus();
setInteractionCanceled(true);
}
setInteractionMode(InteractionMode::Busy);
}
void MainWindow::onLogIdle()
{
if (interactionCanceled()) return;
if (interactionMode() != InteractionMode::None) return;
static char const are_you_sure_you_want_to_continue_connecting[] = "Are you sure you want to continue connecting (yes/no)?";
static char const enter_passphrase[] = "Enter passphrase: ";
static char const enter_passphrase_for_key[] = "Enter passphrase for key '";
static char const fatal_authentication_failed_for[] = "fatal: Authentication failed for '";
std::vector<char> vec;
ui->widget_log->retrieveLastText(&vec, 100);
if (!vec.empty()) {
std::string line;
int n = (int)vec.size();
int i = n;
while (i > 0) {
i--;
if (i + 1 < n && vec[i] == '\n') {
i++;
line.assign(&vec[i], n - i);
break;
}
}
if (!line.empty()) {
auto ExecLineEditDialog = [&](QWidget *parent, QString const &title, QString const &prompt, QString const &val, bool password){
LineEditDialog dlg(parent, title, prompt, val, password);
if (dlg.exec() == QDialog::Accepted) {
std::string ret = dlg.text().toStdString();
std::string str = ret + '\n';
getPtyProcess()->writeInput(str.c_str(), str.size());
return ret;
}
abortPtyProcess();
return std::string();
};
auto Match = [&](char const *str){
int n = strlen(str);
if (strncmp(line.c_str(), str, n) == 0) {
char const *p = line.c_str() + n;
while (1) {
if (!*p) return true;
if (!isspace((unsigned char)*p)) break;
p++;
}
}
return false;
};
auto StartsWith = [&](char const *str){
char const *p = line.c_str();
while (*str) {
if (*p != *str) return false;
str++;
p++;
}
return true;
};
if (Match(are_you_sure_you_want_to_continue_connecting)) {
execAreYouSureYouWantToContinueConnectingDialog();
return;
}
if (line == enter_passphrase) {
ExecLineEditDialog(this, "Passphrase", QString::fromStdString(line), QString(), true);
return;
}
if (StartsWith(enter_passphrase_for_key)) {
std::string keyfile;
{
int i = strlen(enter_passphrase_for_key);
char const *p = line.c_str() + i;
char const *q = strrchr(p, ':');
if (q && p + 2 < q && q[-1] == '\'') {
keyfile.assign(p, q - 1);
}
}
if (!keyfile.empty()) {
if (keyfile == sshPassphraseUser() && !sshPassphrasePass().empty()) {
std::string text = sshPassphrasePass() + '\n';
getPtyProcess()->writeInput(text.c_str(), text.size());
} else {
std::string secret = ExecLineEditDialog(this, "Passphrase for key", QString::fromStdString(line), QString(), true);
sshSetPassphrase(keyfile, secret);
}
return;
}
}
char const *begin = line.c_str();
char const *end = line.c_str() + line.size();
auto Input = [&](QString const &title, bool password, std::string *value){
Q_ASSERT(value);
std::string header = QString("%1 for '").arg(title).toStdString();
if (strncmp(begin, header.c_str(), header.size()) == 0) {
QString msg;
if (memcmp(end - 2, "':", 2) == 0) {
msg = QString::fromUtf8(begin, end - begin - 1);
} else if (memcmp(end - 3, "': ", 3) == 0) {
msg = QString::fromUtf8(begin, end - begin - 2);
}
if (!msg.isEmpty()) {
std::string s = ExecLineEditDialog(this, title, msg, value ? QString::fromStdString(*value) : QString(), password);
*value = s;
return true;
}
}
return false;
};
std::string uid = httpAuthenticationUser();
std::string pwd = httpAuthenticationPass();
bool ok = false;
if (Input("Username", false, &uid)) ok = true;
if (Input("Password", true, &pwd)) ok = true;
if (ok) {
httpSetAuthentication(uid, pwd);
return;
}
if (StartsWith(fatal_authentication_failed_for)) {
QMessageBox::critical(this, tr("Authentication Failed"), QString::fromStdString(line));
abortPtyProcess();
return;
}
}
}
}
void MainWindow::on_action_edit_tags_triggered()
{
Git::CommitItem const *commit = selectedCommitItem();
if (commit && Git::isValidID(commit->commit_id)) {
EditTagsDialog dlg(this, commit);
dlg.exec();
}
}
void MainWindow::on_action_push_u_triggered()
{
pushSetUpstream(false);
}
void MainWindow::on_action_delete_remote_branch_triggered()
{
deleteRemoteBranch(selectedCommitItem());
}
void MainWindow::on_action_terminal_triggered()
{
auto const *repo = ¤tRepository();
openTerminal(repo);
}
void MainWindow::on_action_explorer_triggered()
{
auto const *repo = ¤tRepository();
openExplorer(repo);
}
void MainWindow::on_action_reset_hard_triggered()
{
doGitCommand([&](GitPtr g){
g->reset_hard();
});
}
void MainWindow::on_action_clean_df_triggered()
{
doGitCommand([&](GitPtr g){
g->clean_df();
});
}
void MainWindow::postOpenRepositoryFromGitHub(QString const &username, QString const &reponame)
{
QVariantList list;
list.push_back(username);
list.push_back(reponame);
postUserFunctionEvent([&](QVariant const &v){
QVariantList l = v.toList();
QString uname = l[0].toString();
QString rname = l[1].toString();
CloneFromGitHubDialog dlg(this, uname, rname);
if (dlg.exec() == QDialog::Accepted) {
clone(dlg.url());
}
}, QVariant(list));
}
void MainWindow::on_action_stash_triggered()
{
doGitCommand([&](GitPtr g){
g->stash();
});
}
void MainWindow::on_action_stash_apply_triggered()
{
doGitCommand([&](GitPtr g){
g->stash_apply();
});
}
void MainWindow::on_action_stash_drop_triggered()
{
doGitCommand([&](GitPtr g){
g->stash_drop();
});
}
void MainWindow::on_action_online_triggered()
{
ui->radioButton_remote_online->click();
}
void MainWindow::on_action_offline_triggered()
{
ui->radioButton_remote_offline->click();
}
void MainWindow::on_action_repositories_panel_triggered()
{
bool checked = ui->action_repositories_panel->isChecked();
ui->stackedWidget_leftpanel->setCurrentWidget(checked ? ui->page_repos : ui->page_collapsed);
if (checked) {
ui->stackedWidget_leftpanel->setFixedWidth(m->repos_panel_width);
ui->stackedWidget_leftpanel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
ui->stackedWidget_leftpanel->setMinimumWidth(QWIDGETSIZE_MAX);
ui->stackedWidget_leftpanel->setMaximumWidth(QWIDGETSIZE_MAX);
} else {
m->repos_panel_width = ui->stackedWidget_leftpanel->width();
ui->stackedWidget_leftpanel->setFixedWidth(24);
}
}
void MainWindow::test()
{
}
diff --git a/src/MainWindow.h b/src/MainWindow.h
index 6efa898..d3d907a 100644
--- a/src/MainWindow.h
+++ b/src/MainWindow.h
@@ -1,205 +1,206 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include "BasicMainWindow.h"
namespace Ui {
class MainWindow;
}
class QTableWidgetItem;
class HunkItem {
public:
int hunk_number = -1;
size_t pos, len;
std::vector<std::string> lines;
};
class MainWindow : public BasicMainWindow {
Q_OBJECT
friend class ImageViewWidget;
friend class FileDiffSliderWidget;
friend class FileHistoryWindow;
friend class FileDiffWidget;
friend class AboutDialog;
private:
struct Private;
Private *m;
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow() override;
QPixmap const &digitsPixmap() const;
QString currentWorkingCopyDir() const override;
QColor color(unsigned int i);
bool isOnlineMode() const override;
private:
Ui::MainWindow *ui;
void updateFilesList(QString id, bool wait) override;
void updateFilesList(Git::CommitItem const &commit, bool wait);
void updateRepositoriesList() override;
void openRepository_(GitPtr g, bool keep_selection = false) override;
void prepareLogTableWidget();
QStringList selectedFiles_(QListWidget *listwidget) const;
QStringList selectedFiles() const;
void for_each_selected_files(std::function<void (QString const &)> const &fn);
void showFileList(FilesListType files_list_type);
void clearLog();
void clearFileList() override;
void clearDiffView();
void clearRepositoryInfo();
int repositoryIndex_(const QTreeWidgetItem *item) const;
RepositoryItem const *repositoryItem(const QTreeWidgetItem *item) const;
int selectedLogIndex() const override;
QTreeWidgetItem *newQTreeWidgetFolderItem(QString const &name);
void buildRepoTree(QString const &group, QTreeWidgetItem *item, QList<RepositoryItem> *repos);
void refrectRepositories();
void updateDiffView(QListWidgetItem *item);
void updateDiffView();
void updateUnstagedFileCurrentItem();
void updateStagedFileCurrentItem();
void updateStatusBarText() override;
void setRepositoryInfo(QString const &reponame, QString const &brname) override;
int indexOfRepository(const QTreeWidgetItem *treeitem) const;
void clearRepoFilter();
void appendCharToRepoFilter(ushort c);
void backspaceRepoFilter();
void revertCommit();
void cherrypick(Git::CommitItem const *commit);
void mergeBranch(Git::CommitItem const *commit);
void rebaseBranch(Git::CommitItem const *commit);
void detectGitServerType(const GitPtr &g);
void setRemoteOnline(bool f);
void startTimers();
void onCloneCompleted(bool success, const QVariant &userdata);
void setNetworkingCommandsEnabled(bool f);
void blame(QListWidgetItem *item);
void blame();
QListWidgetItem *currentFileItem() const;
void execAreYouSureYouWantToContinueConnectingDialog();
void deleteRemoteBranch(Git::CommitItem const *commit);
QStringList remoteBranches(QString const &id, QStringList *all);
void setWatchRemoteInterval(int mins);
void test();
+ bool isUninitialized();
protected:
void dragEnterEvent(QDragEnterEvent *event) override;
void timerEvent(QTimerEvent *) override;
void keyPressEvent(QKeyEvent *event) override;
bool event(QEvent *event) override;
bool eventFilter(QObject *watched, QEvent *event) override;
public:
void drawDigit(QPainter *pr, int x, int y, int n) const;
int digitWidth() const;
int digitHeight() const;
void setStatusBarText(QString const &text);
void clearStatusBarText();
void setCurrentLogRow(int row) override;
bool shown();
void deleteTags(QStringList const &tagnames) override;
bool addTag(QString const &name);
void updateCurrentFilesList();
void notifyRemoteChanged(bool f);
void postOpenRepositoryFromGitHub(const QString &username, const QString &reponame);
private slots:
void doUpdateButton();
void onLogVisibilityChanged();
void onPtyProcessCompleted(bool ok, const QVariant &userdata);
void onRepositoriesTreeDropped();
void on_action_about_triggered();
void on_action_clean_df_triggered();
void on_action_clone_triggered();
void on_action_commit_triggered();
void on_action_create_a_repository_triggered();
void on_action_delete_branch_triggered();
void on_action_delete_remote_branch_triggered();
void on_action_edit_git_config_triggered();
void on_action_edit_gitignore_triggered();
void on_action_edit_global_gitconfig_triggered();
void on_action_edit_settings_triggered();
void on_action_edit_tags_triggered();
void on_action_exit_triggered();
void on_action_explorer_triggered();
void on_action_fetch_prune_triggered();
void on_action_fetch_triggered();
void on_action_offline_triggered();
void on_action_online_triggered();
void on_action_open_existing_working_copy_triggered();
void on_action_pull_triggered();
void on_action_push_all_tags_triggered();
void on_action_push_triggered();
void on_action_push_u_triggered();
void on_action_reflog_triggered();
void on_action_repo_checkout_triggered();
void on_action_repo_jump_triggered();
void on_action_repository_property_triggered();
void on_action_reset_HEAD_1_triggered();
void on_action_reset_hard_triggered();
void on_action_set_config_user_triggered();
void on_action_set_gpg_signing_triggered();
void on_action_stash_apply_triggered();
void on_action_stash_drop_triggered();
void on_action_stash_triggered();
void on_action_stop_process_triggered();
void on_action_terminal_triggered();
void on_action_view_refresh_triggered();
void on_action_window_log_triggered(bool checked);
void on_horizontalScrollBar_log_valueChanged(int);
void on_lineEdit_filter_textChanged(QString const &text);
void on_listWidget_files_currentRowChanged(int currentRow);
void on_listWidget_files_customContextMenuRequested(const QPoint &pos);
void on_listWidget_files_itemDoubleClicked(QListWidgetItem *item);
void on_listWidget_staged_currentRowChanged(int currentRow);
void on_listWidget_staged_customContextMenuRequested(const QPoint &pos);
void on_listWidget_staged_itemDoubleClicked(QListWidgetItem *item);
void on_listWidget_unstaged_currentRowChanged(int currentRow);
void on_listWidget_unstaged_customContextMenuRequested(const QPoint &pos);
void on_listWidget_unstaged_itemDoubleClicked(QListWidgetItem *item);
void on_radioButton_remote_offline_clicked();
void on_radioButton_remote_online_clicked();
void on_tableWidget_log_currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous);
void on_tableWidget_log_customContextMenuRequested(const QPoint &pos);
void on_tableWidget_log_itemDoubleClicked(QTableWidgetItem *);
void on_toolButton_clone_clicked();
void on_toolButton_commit_clicked();
void on_toolButton_erase_filter_clicked();
void on_toolButton_explorer_clicked();
void on_toolButton_fetch_clicked();
void on_toolButton_pull_clicked();
void on_toolButton_push_clicked();
void on_toolButton_select_all_clicked();
void on_toolButton_stage_clicked();
void on_toolButton_stop_process_clicked();
void on_toolButton_terminal_clicked();
void on_toolButton_unstage_clicked();
void on_treeWidget_repos_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
void on_treeWidget_repos_customContextMenuRequested(const QPoint &pos);
void on_treeWidget_repos_itemDoubleClicked(QTreeWidgetItem *item, int column);
void on_verticalScrollBar_log_valueChanged(int);
void on_action_repositories_panel_triggered();
protected:
void closeEvent(QCloseEvent *event) override;
void internalWriteLog(const char *ptr, int len) override;
RepositoryItem const *selectedRepositoryItem() const override;
void removeSelectedRepositoryFromBookmark(bool ask) override;
void setRemoteMonitoringEnabled(bool enable) override;
protected slots:
void onLogIdle();
signals:
void signalSetRemoteChanged(bool f);
void onEscapeKeyPressed();
void updateButton();
};
#endif // MAINWINDOW_H
diff --git a/src/SettingGeneralForm.cpp b/src/SettingGeneralForm.cpp
index d04e73d..7e1c5e0 100644
--- a/src/SettingGeneralForm.cpp
+++ b/src/SettingGeneralForm.cpp
@@ -1,112 +1,123 @@
#include "MySettings.h"
#include "SelectItemDialog.h"
#include "ApplicationGlobal.h"
#include "SettingGeneralForm.h"
#include "ui_SettingGeneralForm.h"
#include "common/misc.h"
#include <QFileDialog>
SettingGeneralForm::SettingGeneralForm(QWidget *parent) :
AbstractSettingForm(parent),
ui(new Ui::SettingGeneralForm)
{
ui->setupUi(this);
- langs.push_back(SelectItemDialog::Item("en", tr("English")));
- langs.push_back(SelectItemDialog::Item("ja", tr("Japanese")));
- langs.push_back(SelectItemDialog::Item("ru", tr("Russian")));
+ langs = languages();
themes.push_back(SelectItemDialog::Item("standard", tr("Standard")));
themes.push_back(SelectItemDialog::Item("dark", tr("Dark")));
updateLanguage();
updateTheme();
}
SettingGeneralForm::~SettingGeneralForm()
{
delete ui;
}
+QList<SelectItemDialog::Item> SettingGeneralForm::languages()
+{
+ QList<SelectItemDialog::Item> langs;
+ langs.push_back(SelectItemDialog::Item("en", tr("English")));
+ langs.push_back(SelectItemDialog::Item("ja", tr("Japanese")));
+ langs.push_back(SelectItemDialog::Item("ru", tr("Russian")));
+ return langs;
+}
+
void SettingGeneralForm::exchange(bool save)
{
if (save) {
settings()->remember_and_restore_window_position = ui->checkBox_save_window_pos->isChecked();
settings()->enable_high_dpi_scaling = ui->checkBox_enable_high_dpi_scaling->isChecked();
} else {
ui->checkBox_save_window_pos->setChecked(settings()->remember_and_restore_window_position);
ui->checkBox_enable_high_dpi_scaling->setChecked(settings()->enable_high_dpi_scaling);
}
}
void SettingGeneralForm::on_pushButton_browse_default_working_dir_clicked()
{
}
void SettingGeneralForm::updateLanguage()
{
QString id = global->language_id;
if (id.isEmpty()) {
id = "en";
}
for (SelectItemDialog::Item const &item : langs) {
if (item.id == id) {
ui->lineEdit_language->setText(item.text);
return;
}
}
}
void SettingGeneralForm::updateTheme()
{
QString id = global->theme_id;
if (id.isEmpty()) {
id = "standard";
}
for (SelectItemDialog::Item const &item : themes) {
if (item.id == id) {
ui->lineEdit_theme->setText(item.text);
return;
}
}
}
-void SettingGeneralForm::on_pushButton_change_language_clicked()
+void SettingGeneralForm::execSelectLanguageDialog(QWidget *parent, QList<SelectItemDialog::Item> const &langs, std::function<void()> const &done)
{
-
- SelectItemDialog dlg(this);
+ SelectItemDialog dlg(parent);
dlg.setWindowTitle(tr("Select Language"));
for (SelectItemDialog::Item const &item : langs) {
dlg.addItem(item.id, item.text);
}
dlg.select(global->language_id.isEmpty() ? "en" : global->language_id);
if (dlg.exec() == QDialog::Accepted) {
SelectItemDialog::Item item = dlg.item();
global->language_id = item.id;
MySettings s;
s.beginGroup("UI");
s.setValue("Language", global->language_id);
s.endGroup();
- updateLanguage();
+ done();
}
}
+void SettingGeneralForm::on_pushButton_change_language_clicked()
+{
+ execSelectLanguageDialog(this, langs, [&](){updateLanguage();});
+}
+
void SettingGeneralForm::on_pushButton_change_theme_clicked()
{
SelectItemDialog dlg(this);
dlg.setWindowTitle(tr("Select Theme"));
for (SelectItemDialog::Item const &item : themes) {
dlg.addItem(item.id, item.text);
}
dlg.select(global->theme_id.isEmpty() ? "standard" : global->theme_id);
if (dlg.exec() == QDialog::Accepted) {
SelectItemDialog::Item item = dlg.item();
global->theme_id = item.id;
MySettings s;
s.beginGroup("UI");
s.setValue("Theme", global->theme_id);
s.endGroup();
updateTheme();
}
}
diff --git a/src/SettingGeneralForm.h b/src/SettingGeneralForm.h
index 25e3560..0400d2c 100644
--- a/src/SettingGeneralForm.h
+++ b/src/SettingGeneralForm.h
@@ -1,34 +1,36 @@
#ifndef SETTINGGENERALFORM_H
#define SETTINGGENERALFORM_H
#include "AbstractSettingForm.h"
#include "SelectItemDialog.h"
#include <QWidget>
namespace Ui {
class SettingGeneralForm;
}
class SettingGeneralForm : public AbstractSettingForm
{
Q_OBJECT
private:
Ui::SettingGeneralForm *ui;
QList<SelectItemDialog::Item> langs;
QList<SelectItemDialog::Item> themes;
void updateLanguage();
void updateTheme();
public:
explicit SettingGeneralForm(QWidget *parent = nullptr);
~SettingGeneralForm() override;
void exchange(bool save) override;
+ static QList<SelectItemDialog::Item> languages();
+ static void execSelectLanguageDialog(QWidget *parent, const QList<SelectItemDialog::Item> &langs, const std::function<void ()> &done);
private slots:
void on_pushButton_browse_default_working_dir_clicked();
void on_pushButton_change_language_clicked();
void on_pushButton_change_theme_clicked();
};
#endif // SETTINGGENERALFORM_H
diff --git a/src/main.cpp b/src/main.cpp
index 3c62526..e1aaffd 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,159 +1,167 @@
#include "main.h"
#include "ApplicationGlobal.h"
#include "MainWindow.h"
#include "MySettings.h"
#include "common/joinpath.h"
#include "common/misc.h"
#include "darktheme/DarkStyle.h"
#include "platform.h"
#include "webclient.h"
#include <QApplication>
#include <QDebug>
#include <QDir>
#include <QMessageBox>
#include <QProxyStyle>
#include <QStandardPaths>
#include <QTranslator>
#include <string>
#ifdef Q_OS_WIN
#include "win32/win32.h"
+
+#include "SettingGeneralForm.h"
#endif
#ifndef APP_GUITAR
#error APP_GUITAR is not defined.
#endif
ApplicationGlobal *global = nullptr;
ApplicationSettings ApplicationSettings::defaultSettings()
{
ApplicationSettings s;
s.proxy_server = "http://squid:3128/";
return s;
}
static bool isHighDpiScalingEnabled()
{
MySettings s;
s.beginGroup("UI");
QVariant v = s.value("EnableHighDpiScaling");
return v.isNull() || v.toBool();
}
void setEnvironmentVariable(QString const &name, QString const &value);
int main(int argc, char *argv[])
{
#ifdef Q_OS_WIN
setEnvironmentVariable("UNICODEMAP_JP", "cp932");
#else
setenv("UNICODEMAP_JP", "cp932", 1);
#endif
ApplicationGlobal g;
global = &g;
global->organization_name = ORGANIZATION_NAME;
global->application_name = APPLICATION_NAME;
global->generic_config_dir = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
global->app_config_dir = global->generic_config_dir / global->organization_name / global->application_name;
global->config_file_path = joinpath(global->app_config_dir, global->application_name + ".ini");
if (!QFileInfo(global->app_config_dir).isDir()) {
QDir().mkpath(global->app_config_dir);
}
if (isHighDpiScalingEnabled()){
#if (QT_VERSION < QT_VERSION_CHECK(5, 6, 0))
qDebug() << "High DPI scaling is not supported";
#else
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
}
QApplication a(argc, argv);
QApplication::setOrganizationName(global->organization_name);
QApplication::setApplicationName(global->application_name);
qRegisterMetaType<RepositoryItem>("RepositoryItem");
{
MySettings s;
s.beginGroup("UI");
global->language_id = s.value("Language").toString();
global->theme_id = s.value("Theme").toString();
if (global->theme_id.compare("dark", Qt::CaseInsensitive) == 0) {
global->theme = createDarkTheme();
} else {
global->theme = createStandardTheme();
}
s.endGroup();
}
QApplication::setStyle(global->theme->newStyle());
if (QApplication::queryKeyboardModifiers() & Qt::ShiftModifier) {
global->start_with_shift_key = true;
}
WebClient::initialize();
bool f_open_here = false;
QStringList args;
for (int i = 1; i < argc; i++) {
std::string arg = argv[i];
if (arg[0] == '-') {
if (arg == "--open-here") {
f_open_here = true;
}
} else {
args.push_back(QString::fromStdString(arg));
}
}
if (global->app_config_dir.isEmpty()) {
QMessageBox::warning(nullptr, qApp->applicationName(), "Preparation of data storage folder failed.");
return 1;
}
+ // 設定ファイルがないときは、言語の選択をする。
+ if (!QFileInfo(global->config_file_path).exists()) {
+ auto langs = SettingGeneralForm::languages();
+ SettingGeneralForm::execSelectLanguageDialog(nullptr, langs, [](){});
+ }
+
QTranslator translator;
{
if (global->language_id.isEmpty() || global->language_id == "en") {
// thru
} else {
QString path = ":/translations/Guitar_" + global->language_id;
bool f = translator.load(path, QApplication::applicationDirPath());
if (!f) {
qDebug() << QString("Failed to load the translation file: %1").arg(path);
}
QApplication::installTranslator(&translator);
}
}
MainWindow w;
global->panel_bg_color = w.palette().color(QPalette::Background);
w.setWindowIcon(QIcon(":/image/guitar.png"));
w.show();
w.shown();
if (f_open_here) {
QString dir = QDir::current().absolutePath();
w.autoOpenRepository(dir);
} else if (args.size() == 1) {
QString dir = args[0] / QString();
if (dir.startsWith("./") || dir.startsWith(".\\")) {
dir = QDir::current().absolutePath() / dir.mid(2);
}
QFileInfo fi(dir);
if (fi.isDir()) {
dir = fi.absolutePath();
w.autoOpenRepository(dir);
}
}
return QApplication::exec();
}
diff --git a/src/resources/translations/Guitar_ja.qm b/src/resources/translations/Guitar_ja.qm
index 3f82038..b881a92 100644
Binary files a/src/resources/translations/Guitar_ja.qm and b/src/resources/translations/Guitar_ja.qm differ
diff --git a/src/resources/translations/Guitar_ru.qm b/src/resources/translations/Guitar_ru.qm
index 08c6fff..685cba3 100644
Binary files a/src/resources/translations/Guitar_ru.qm and b/src/resources/translations/Guitar_ru.qm differ
diff --git a/src/resources/translations/Guitar_ru.ts b/src/resources/translations/Guitar_ru.ts
index 514f592..51ad387 100644
--- a/src/resources/translations/Guitar_ru.ts
+++ b/src/resources/translations/Guitar_ru.ts
@@ -1,2746 +1,2645 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="ru_RU">
<context>
<name>AboutDialog</name>
<message>
<location filename="../../AboutDialog.ui" line="14"/>
<source>Dialog</source>
<translation></translation>
</message>
<message>
<location filename="../../AboutDialog.ui" line="29"/>
<location filename="../../AboutDialog.ui" line="36"/>
<location filename="../../AboutDialog.ui" line="43"/>
<location filename="../../AboutDialog.ui" line="50"/>
<source>TextLabel</source>
<translation></translation>
</message>
<message>
<location filename="../../AboutDialog.cpp" line="25"/>
<source>About %1</source>
<translation>О %1</translation>
</message>
</context>
<context>
<name>AreYouSureYouWantToContinueConnectingDialog</name>
<message>
<location filename="../../AreYouSureYouWantToContinueConnectingDialog.ui" line="14"/>
<source>Unknown Host</source>
<translation>Узел не найден</translation>
</message>
<message>
<location filename="../../AreYouSureYouWantToContinueConnectingDialog.ui" line="20"/>
<source>Are you sure you want to continue connecting (yes/no)?</source>
<translation>Повторить попытку соединения? (Да/нет)?</translation>
</message>
<message>
<location filename="../../AreYouSureYouWantToContinueConnectingDialog.ui" line="52"/>
<source>Continue</source>
<translation>Да, продолжить</translation>
</message>
<message>
<location filename="../../AreYouSureYouWantToContinueConnectingDialog.ui" line="59"/>
<source>Close</source>
<translation>Нет, закрыть</translation>
</message>
</context>
<context>
<name>BasicMainWindow</name>
<message>
<location filename="../../BasicMainWindow.cpp" line="345"/>
<source>The URL is a valid repository</source>
<translation>URL ссылается на корректный репозиторий</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="346"/>
<source>Failed to access the URL</source>
<translation>Нет доступа по URL</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="351"/>
<source>Remote Repository</source>
<translation>Репозиторий-истоник</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="376"/>
<source>&Property</source>
<translation>&Свойства</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="1201"/>
<source>Select %1 command</source>
<translation>Выбор %1 команды</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="1380"/>
<source>Revert all files</source>
<translation>Отменить (Revert) изменения во всех файлах</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="1520"/>
<source>The folder is not a valid git repository.</source>
<translation>Папка не является репозиторием Git.</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="1524"/>
<source>Do you want to initialize it as a git repository ?</source>
<translation>Инициализировать репозиторий Git?</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="1525"/>
<source>Initialize Repository</source>
<translation>Инициализировать репозиторий</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="1663"/>
<source>No repository selected</source>
<translation>Репозиторий не выбран</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="1817"/>
<source>Repository Property</source>
<translation>Свойства репозитория</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="1817"/>
<location filename="../../BasicMainWindow.cpp" line="1919"/>
<source>Not a valid git repository</source>
<translation>Каталог не является репозиторием Git</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="1912"/>
<location filename="../../BasicMainWindow.cpp" line="1919"/>
<source>Open Repository</source>
<translation>Открыть репозиторий</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="1912"/>
<source>No such folder</source>
<translation>Каталог не существует</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="1912"/>
<source>Remove from bookmark ?</source>
<translation>Удалить из закладок?</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2150"/>
<source>, %1 ahead</source>
<translation>, %1 впереди</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2153"/>
<source>, %1 behind</source>
<translation>, %1 позади</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2174"/>
<source>Confirm Remove</source>
<translation>Подтвердите удаление</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2174"/>
<source>Are you sure you want to remove the repository from bookmarks ?</source>
<translation>Вы точно хотите удалить репозиторий из закладок?</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2174"/>
<source>(Files will NOT be deleted)</source>
<translation>(Файлы НЕ будут удалены)</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2204"/>
<source>A file with same name already exists</source>
<translation>Существует файл с тем же именем</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2205"/>
<location filename="../../BasicMainWindow.cpp" line="2210"/>
<location filename="../../BasicMainWindow.cpp" line="2227"/>
<location filename="../../BasicMainWindow.cpp" line="2232"/>
<source>Clone</source>
<translation>Клонировать (Clone)</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2209"/>
<source>A folder with same name already exists</source>
<translation>Существует папка с тем же именем</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2226"/>
<source>Invalid folder</source>
<translation>Ошибочный каталог</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2231"/>
<source>No such folder. Create it now ?</source>
<translation>Каталог не существует. Создать?</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2291"/>
<source>Commit</source>
<translation>Фиксировать (Commit)</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2291"/>
<source>Commit message can not be omitted.</source>
<translation>Комментарий к фиксации является обязательным.</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2308"/>
<source>Failed to commit</source>
<translation>Не удалось зафиксировать</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2342"/>
<location filename="../../BasicMainWindow.cpp" line="2426"/>
<source>Connection refused.</source>
<translation>Отказано в соединении.</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2395"/>
<source>No remote repository is registered.</source>
<translation>Репозиторий источника не зарегистрирован.</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2417"/>
<source>The current branch %1 has no upstream branch.</source>
<translation>Текущая ветвь %1 не содержится в источнике.</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2420"/>
<source>You try push --set-upstream</source>
<translation>Попытайтесь отправить (Push) с опцией --set-upstream</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2509"/>
<source>Failed to delete the branch '%1'</source>
<translation>Не удалось удалить ветвь '%1'</translation>
</message>
<message>
<source>Failed to delete the branch '%1'
</source>
<translation type="vanished">ブランチの削除に失敗しました : '%1'</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2573"/>
<source>Are you sure you want to run the following command ?</source>
<translation>Вы точно хотите выполнить следующую команду?</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2594"/>
<source>Reset a file</source>
<translation>Отбросить изменения (Reset) файла</translation>
</message>
<message>
<location filename="../../BasicMainWindow.cpp" line="2608"/>
<source>Unnamed</source>
<translation>Безымянный</translation>
</message>
</context>
<context>
<name>BasicRepositoryDialog</name>
<message>
<location filename="../../BasicRepositoryDialog.cpp" line="66"/>
<source>Name</source>
<translation>Имя</translation>
</message>
<message>
<location filename="../../BasicRepositoryDialog.cpp" line="67"/>
<source>Purpose</source>
<translation>Назначение</translation>
</message>
<message>
<location filename="../../BasicRepositoryDialog.cpp" line="68"/>
<source>URL</source>
<translation></translation>
</message>
</context>
<context>
<name>BigDiffWindow</name>
<message>
<location filename="../../BigDiffWindow.ui" line="14"/>
<source>Diff</source>
<translation></translation>
</message>
</context>
<context>
<name>BlameWindow</name>
<message>
<location filename="../../BlameWindow.ui" line="14"/>
<source>Blame</source>
<translation></translation>
</message>
<message>
<location filename="../../BlameWindow.ui" line="45"/>
<source>Information</source>
<translation>Информация</translation>
</message>
<message>
<location filename="../../BlameWindow.ui" line="60"/>
<source>Commit</source>
<translation>Фиксация (Commit)</translation>
</message>
<message>
<location filename="../../BlameWindow.ui" line="70"/>
<source>Date</source>
<translation>Дата</translation>
</message>
<message>
<location filename="../../BlameWindow.ui" line="90"/>
<source>Message</source>
<translation>Комментарий</translation>
</message>
<message>
<location filename="../../BlameWindow.ui" line="80"/>
<source>Author</source>
<translation>Автор</translation>
</message>
<message>
<location filename="../../BlameWindow.ui" line="118"/>
<source>Close</source>
<translation>Закрыть</translation>
</message>
</context>
<context>
<name>CheckoutDialog</name>
<message>
<location filename="../../CheckoutDialog.ui" line="14"/>
<source>Checkout</source>
<translation>Проверка (Checkout)</translation>
</message>
<message>
<location filename="../../CheckoutDialog.ui" line="20"/>
<location filename="../../CheckoutDialog.ui" line="27"/>
<location filename="../../CheckoutDialog.ui" line="34"/>
<source>RadioButton</source>
<translation></translation>
</message>
<message>
<location filename="../../CheckoutDialog.ui" line="63"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../CheckoutDialog.ui" line="70"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>CloneDialog</name>
<message>
<location filename="../../CloneDialog.ui" line="14"/>
<location filename="../../CloneDialog.ui" line="103"/>
<source>Clone</source>
<translation>Клонировать (Clone)</translation>
</message>
<message>
<location filename="../../CloneDialog.ui" line="22"/>
<source>Remote</source>
<translation>Источник</translation>
</message>
<message>
<location filename="../../CloneDialog.ui" line="39"/>
<source>&Test</source>
<translation>&Тестировать</translation>
</message>
<message>
<location filename="../../CloneDialog.ui" line="46"/>
<source>Local</source>
<translation>Локальный</translation>
</message>
<message>
<location filename="../../CloneDialog.ui" line="56"/>
<source>Browse</source>
<translation>Обзор</translation>
</message>
<message>
<location filename="../../CloneDialog.ui" line="83"/>
<source>Open existing local directory...</source>
<translation>Открыть локальную копию...</translation>
</message>
<message>
<location filename="../../CloneDialog.ui" line="113"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
<message>
<location filename="../../CloneDialog.cpp" line="40"/>
<source>Search</source>
<translation>Поиск</translation>
</message>
<message>
<location filename="../../CloneDialog.cpp" line="41"/>
<source>GitHub</source>
<translation></translation>
</message>
<message>
<location filename="../../CloneDialog.cpp" line="108"/>
<source>Checkout into</source>
<translation>Проверить в</translation>
</message>
<message>
<location filename="../../CloneDialog.cpp" line="120"/>
<source>Open existing directory</source>
<translation>Открыть существующий каталог</translation>
</message>
</context>
<context>
<name>CloneFromGitHubDialog</name>
<message>
<location filename="../../CloneFromGitHubDialog.ui" line="14"/>
<source>Clone from GitHub</source>
<translation>Клонировать из GitHub</translation>
</message>
<message>
<location filename="../../CloneFromGitHubDialog.ui" line="45"/>
<source>ssh</source>
<translation></translation>
</message>
<message>
<location filename="../../CloneFromGitHubDialog.ui" line="55"/>
<source>http</source>
<translation></translation>
</message>
<message>
<location filename="../../CloneFromGitHubDialog.ui" line="95"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../CloneFromGitHubDialog.ui" line="102"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>CommitDialog</name>
<message>
<location filename="../../CommitDialog.ui" line="14"/>
<source>Commit</source>
<translation>Фиксация (Commit)</translation>
</message>
<message>
<location filename="../../CommitDialog.ui" line="27"/>
<source>TextLabel</source>
<translation></translation>
</message>
<message>
<location filename="../../CommitDialog.ui" line="49"/>
<source>Author</source>
<translation>Автор</translation>
</message>
<message>
<location filename="../../CommitDialog.ui" line="68"/>
<location filename="../../CommitDialog.ui" line="94"/>
<location filename="../../CommitDialog.ui" line="139"/>
<location filename="../../CommitDialog.ui" line="159"/>
<location filename="../../CommitDialog.ui" line="179"/>
<source>---</source>
<translation></translation>
</message>
<message>
<location filename="../../CommitDialog.ui" line="75"/>
<location filename="../../CommitDialog.ui" line="166"/>
<source>Mail</source>
<translation></translation>
</message>
<message>
<location filename="../../CommitDialog.ui" line="111"/>
<source>GPG Signing</source>
<translation>Подпись GPG</translation>
</message>
<message>
<location filename="../../CommitDialog.ui" line="126"/>
<source>ID</source>
<translation></translation>
</message>
<message>
<location filename="../../CommitDialog.ui" line="146"/>
<source>Name</source>
<translation>Имя</translation>
</message>
<message>
<location filename="../../CommitDialog.ui" line="189"/>
<source>Configure...</source>
<translation>Настроить...</translation>
</message>
<message>
<location filename="../../CommitDialog.ui" line="198"/>
<source>Message</source>
<translation>Комментарий</translation>
</message>
<message>
<location filename="../../CommitDialog.ui" line="223"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../CommitDialog.ui" line="233"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>CommitExploreWindow</name>
<message>
<location filename="../../CommitExploreWindow.ui" line="14"/>
<source>Commit Explorer</source>
<translation>Обзор фиксации</translation>
</message>
<message>
<location filename="../../CommitExploreWindow.ui" line="65"/>
<source>Commit ID</source>
<translation>ID фиксации</translation>
</message>
<message>
<location filename="../../CommitExploreWindow.ui" line="82"/>
<source>Date</source>
<translation>Дата</translation>
</message>
<message>
<location filename="../../CommitExploreWindow.ui" line="89"/>
<source>Author</source>
<translation>Автор</translation>
</message>
<message>
<location filename="../../CommitExploreWindow.cpp" line="78"/>
<source>Commit</source>
<translation>Фиксация (Commit)</translation>
</message>
</context>
<context>
<name>CommitPropertyDialog</name>
<message>
<location filename="../../CommitPropertyDialog.ui" line="14"/>
<source>Commit Property</source>
<oldsource>Commit Properties</oldsource>
<translation>Свойства фиксации</translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="46"/>
<source>Date</source>
<translation>Дата</translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="56"/>
<source>Author</source>
<translation>Автор</translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="66"/>
<location filename="../../CommitPropertyDialog.ui" line="196"/>
<source>Mail</source>
<translation></translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="154"/>
<source>GPG Sign</source>
<translation>Подпись GPG</translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="176"/>
<source>ID</source>
<translation></translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="186"/>
<source>Name</source>
<translation>Имя</translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="233"/>
<source>Commit ID</source>
<translation>ID фиксации</translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="243"/>
<source>Parent IDs</source>
<translation>ID родительских фиксаций</translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="265"/>
<source>Files...</source>
<translation>Файлы...</translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="285"/>
<source>Explorer</source>
<translation>Обзор</translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="305"/>
<source>Checkout</source>
<translation>Проверка (Checkout)</translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="312"/>
<source>Jump</source>
<translation>Перейти</translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.ui" line="332"/>
<source>Close</source>
<translation>Закрыть</translation>
</message>
<message>
<location filename="../../CommitPropertyDialog.cpp" line="60"/>
<source><Unknown></source>
<translation><Неизвестно></translation>
</message>
</context>
<context>
<name>CommitViewWindow</name>
<message>
<location filename="../../CommitViewWindow.ui" line="14"/>
<source>Commit View</source>
<translation>Просмотр фиксации (Commit)</translation>
</message>
<message>
<location filename="../../CommitViewWindow.cpp" line="58"/>
<source>History</source>
<translation>История</translation>
</message>
</context>
<context>
<name>ConfigCredentialHelperDialog</name>
<message>
<location filename="../../ConfigCredentialHelperDialog.ui" line="14"/>
<source>Dialog</source>
<translation></translation>
</message>
<message>
<location filename="../../ConfigCredentialHelperDialog.ui" line="26"/>
<source>wincred</source>
<translation></translation>
</message>
<message>
<location filename="../../ConfigCredentialHelperDialog.ui" line="39"/>
<source>winstore</source>
<translation></translation>
</message>
<message>
<location filename="../../ConfigCredentialHelperDialog.ui" line="52"/>
<source>None</source>
<translation>Нет</translation>
</message>
<message>
<location filename="../../ConfigCredentialHelperDialog.ui" line="65"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../ConfigCredentialHelperDialog.ui" line="78"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
<message>
<location filename="../../ConfigCredentialHelperDialog.ui" line="91"/>
<source>Other</source>
<translation>Другие</translation>
</message>
</context>
<context>
<name>ConfigSigningDialog</name>
<message>
<location filename="../../ConfigSigningDialog.ui" line="14"/>
<source>Signing Policy</source>
<translation>Политика подписания</translation>
</message>
<message>
<location filename="../../ConfigSigningDialog.ui" line="20"/>
<source>Config commit.gpgsign</source>
<translation>Настройка commit.gpgsign</translation>
</message>
<message>
<location filename="../../ConfigSigningDialog.ui" line="32"/>
<source>global</source>
<translation></translation>
</message>
<message>
<location filename="../../ConfigSigningDialog.ui" line="42"/>
<source>local</source>
<translation></translation>
</message>
<message>
<location filename="../../ConfigSigningDialog.ui" line="70"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../ConfigSigningDialog.ui" line="77"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>CreateRepositoryDialog</name>
<message>
<location filename="../../CreateRepositoryDialog.ui" line="14"/>
<location filename="../../CreateRepositoryDialog.cpp" line="42"/>
<location filename="../../CreateRepositoryDialog.cpp" line="46"/>
<location filename="../../CreateRepositoryDialog.cpp" line="50"/>
<location filename="../../CreateRepositoryDialog.cpp" line="54"/>
<source>Create Repository</source>
<translation>Создать репозиторий</translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.ui" line="22"/>
<source>Path</source>
<translation>Путь</translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.ui" line="32"/>
<source>Browse</source>
<translation>Обзор</translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.ui" line="54"/>
<source>Bookmark</source>
<translation>Закладка</translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.ui" line="63"/>
<location filename="../../CreateRepositoryDialog.ui" line="88"/>
<source>Name</source>
<translation>Имя</translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.ui" line="76"/>
<source>Remote</source>
<translation>Источник</translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.ui" line="98"/>
<source>URL</source>
<translation></translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.ui" line="108"/>
<source>Test</source>
<translation>Тестировать</translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.ui" line="146"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.ui" line="153"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.cpp" line="19"/>
<source>A valid git repository already exists there.</source>
<translation>Репозиторий Git уже существует.</translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.cpp" line="42"/>
<location filename="../../CreateRepositoryDialog.cpp" line="50"/>
<source>The specified path is not a directory.</source>
<translation>Путь не является каталогом.</translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.cpp" line="54"/>
<source>Remote name is invalid.</source>
<translation>Имя источника некорректно.</translation>
</message>
<message>
<location filename="../../CreateRepositoryDialog.cpp" line="62"/>
<source>Destination Path</source>
<translation>Путь назначения</translation>
</message>
</context>
<context>
<name>DeleteBranchDialog</name>
<message>
<location filename="../../DeleteBranchDialog.ui" line="14"/>
<source>Delete Branch</source>
<translation>Удалить ветвь</translation>
</message>
<message>
<location filename="../../DeleteBranchDialog.ui" line="25"/>
<source>&All branches</source>
<translation>Все ветви (&A)</translation>
</message>
<message>
<location filename="../../DeleteBranchDialog.ui" line="45"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../DeleteBranchDialog.ui" line="52"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
<message>
<location filename="../../DeleteBranchDialog.cpp" line="23"/>
<source>Delete Remote Branch</source>
<translation>Удалить ветвь в источнике</translation>
</message>
</context>
<context>
<name>DeleteTagsDialog</name>
<message>
<location filename="../../DeleteTagsDialog.ui" line="14"/>
<source>Delete tags</source>
<translation>Удалить тэги</translation>
</message>
<message>
<location filename="../../DeleteTagsDialog.ui" line="32"/>
<source>Check all</source>
<translation>Проверить все</translation>
</message>
<message>
<location filename="../../DeleteTagsDialog.ui" line="52"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../DeleteTagsDialog.ui" line="62"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>DoYouWantToInitDialog</name>
<message>
<location filename="../../DoYouWantToInitDialog.ui" line="14"/>
<source>Init Repository</source>
<translation>Создать репозиторий</translation>
</message>
<message>
<location filename="../../DoYouWantToInitDialog.ui" line="20"/>
<source>The folder:</source>
<translation>Каталог:</translation>
</message>
<message>
<location filename="../../DoYouWantToInitDialog.ui" line="32"/>
<source>is not a valid git working copy.</source>
<translation>не является репозиторием.</translation>
</message>
<message>
<location filename="../../DoYouWantToInitDialog.ui" line="39"/>
<source>Do you want to init it as a git repository ?</source>
<translation>Инициализировать репозиторий Git?</translation>
</message>
<message>
<location filename="../../DoYouWantToInitDialog.ui" line="81"/>
<source>Yes, Please init it.</source>
<translation>Да, инициализировать.</translation>
</message>
<message>
<location filename="../../DoYouWantToInitDialog.ui" line="88"/>
<source>No, Stop it.</source>
<translation>Нет, отменить.</translation>
</message>
<message>
<location filename="../../DoYouWantToInitDialog.ui" line="140"/>
<source>Please choose option</source>
<translation>Продолжить</translation>
</message>
<message>
<location filename="../../DoYouWantToInitDialog.cpp" line="20"/>
<source>Next...</source>
<translation>Далее...</translation>
</message>
<message>
<location filename="../../DoYouWantToInitDialog.cpp" line="26"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>EditGitIgnoreDialog</name>
<message>
<location filename="../../EditGitIgnoreDialog.ui" line="14"/>
<source>Edit Git Ignore</source>
<translation>Редактировать список игнорирования</translation>
</message>
<message>
<location filename="../../EditGitIgnoreDialog.ui" line="20"/>
<location filename="../../EditGitIgnoreDialog.ui" line="27"/>
<location filename="../../EditGitIgnoreDialog.ui" line="34"/>
<location filename="../../EditGitIgnoreDialog.ui" line="41"/>
<source>RadioButton</source>
<translation></translation>
</message>
<message>
<location filename="../../EditGitIgnoreDialog.ui" line="63"/>
<source>Edit the file</source>
<translation>Редактировать</translation>
</message>
<message>
<location filename="../../EditGitIgnoreDialog.ui" line="83"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../EditGitIgnoreDialog.ui" line="90"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>EditRemoteDialog</name>
<message>
<location filename="../../EditRemoteDialog.ui" line="14"/>
<source>Edit Remote</source>
<translation>Свойства источника</translation>
</message>
<message>
<location filename="../../EditRemoteDialog.ui" line="20"/>
<source>Remote</source>
<translation>Источник</translation>
</message>
<message>
<location filename="../../EditRemoteDialog.ui" line="26"/>
<source>Name</source>
<translation>Имя</translation>
</message>
<message>
<location filename="../../EditRemoteDialog.ui" line="33"/>
<source>URL</source>
<translation></translation>
</message>
<message>
<location filename="../../EditRemoteDialog.ui" line="53"/>
<source>&Test</source>
<translation>&Проверить</translation>
</message>
<message>
<location filename="../../EditRemoteDialog.ui" line="81"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../EditRemoteDialog.ui" line="88"/>
<source>Close</source>
<translation>Закрыть</translation>
</message>
</context>
<context>
<name>EditTagsDialog</name>
<message>
<location filename="../../EditTagsDialog.ui" line="14"/>
<source>Edit Tags</source>
<translation>Редактор тэгов</translation>
</message>
<message>
<location filename="../../EditTagsDialog.ui" line="41"/>
<source>Add...</source>
<translation>Добавить...</translation>
</message>
<message>
<location filename="../../EditTagsDialog.ui" line="48"/>
<source>Delete</source>
<translation>Удалить</translation>
</message>
<message>
<location filename="../../EditTagsDialog.ui" line="68"/>
<source>Close</source>
<translation>Закрыть</translation>
</message>
</context>
<context>
<name>ExperimentDialog</name>
<message>
<location filename="../../ExperimentDialog.ui" line="14"/>
<source>Dialog</source>
<translation></translation>
</message>
</context>
<context>
<name>FileDiffWidget</name>
<message>
<location filename="../../FileDiffWidget.ui" line="14"/>
<source>Dialog</source>
<translation></translation>
</message>
</context>
<context>
<name>FileHistoryWindow</name>
<message>
<location filename="../../FileHistoryWindow.ui" line="14"/>
<source>File History</source>
<translation>История файла</translation>
</message>
<message>
<location filename="../../FileHistoryWindow.ui" line="20"/>
<location filename="../../FileHistoryWindow.ui" line="27"/>
<source>TextLabel</source>
<translation></translation>
</message>
<message>
<location filename="../../FileHistoryWindow.cpp" line="112"/>
<source>Commit</source>
<translation>Фиксация (Commit)</translation>
</message>
<message>
<location filename="../../FileHistoryWindow.cpp" line="113"/>
<source>Date</source>
<translation>Дата</translation>
</message>
<message>
<location filename="../../FileHistoryWindow.cpp" line="114"/>
<source>Author</source>
<translation>Автор</translation>
</message>
<message>
<location filename="../../FileHistoryWindow.cpp" line="115"/>
<source>Message</source>
<translation>Комментарий</translation>
</message>
<message>
<source>Description</source>
<translation type="vanished">概要</translation>
</message>
</context>
<context>
<name>FilePropertyDialog</name>
<message>
<location filename="../../FilePropertyDialog.ui" line="14"/>
<source>File Property</source>
<oldsource>File Properties</oldsource>
<translation>Свойства файла</translation>
</message>
<message>
<location filename="../../FilePropertyDialog.ui" line="78"/>
<source>&Close</source>
<translation>&Закрыть</translation>
</message>
</context>
<context>
<name>FileViewWidget</name>
<message>
<location filename="../../FileViewWidget.cpp" line="31"/>
<source>Form</source>
<translation></translation>
</message>
</context>
<context>
<name>InputNewTagDialog</name>
<message>
<location filename="../../InputNewTagDialog.ui" line="14"/>
<source>Edit tag</source>
<translation>Редактор тэга</translation>
</message>
<message>
<location filename="../../InputNewTagDialog.ui" line="31"/>
<source>Tag</source>
<translation>Тэг</translation>
</message>
<message>
<location filename="../../InputNewTagDialog.ui" line="58"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../InputNewTagDialog.ui" line="65"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>JumpDialog</name>
<message>
<location filename="../../JumpDialog.ui" line="14"/>
<source>Jump</source>
<translation>Перейти</translation>
</message>
<message>
<source>Branches and Tags</source>
<translation type="vanished">ブランチとタグ</translation>
</message>
<message>
<source>&Filter</source>
<translation type="vanished">フィルタ(&F)</translation>
</message>
<message>
<source>Find</source>
<translation type="vanished">検索</translation>
</message>
<message>
<source>&Checkout</source>
<translation type="vanished">チェックアウト(&C)</translation>
</message>
<message>
<location filename="../../JumpDialog.ui" line="20"/>
<source>Branch, Tag or Commit-ID :</source>
<translation>Ветвь, Тэг или ID фиксации :</translation>
</message>
<message>
<location filename="../../JumpDialog.ui" line="71"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../JumpDialog.ui" line="81"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
<message>
<location filename="../../JumpDialog.cpp" line="38"/>
<source>Name</source>
<translation>Имя</translation>
</message>
</context>
<context>
<name>LineEditDialog</name>
<message>
<location filename="../../LineEditDialog.ui" line="14"/>
<source>Dialog</source>
<translation></translation>
</message>
<message>
<location filename="../../LineEditDialog.ui" line="20"/>
<source>TextLabel</source>
<translation></translation>
</message>
<message>
<location filename="../../LineEditDialog.ui" line="45"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../LineEditDialog.ui" line="52"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="../../MainWindow.ui" line="14"/>
<source>Guitar</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="96"/>
<location filename="../../MainWindow.ui" line="1352"/>
<location filename="../../MainWindow.ui" line="1355"/>
<source>Clone</source>
<translation>Клонировать</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="131"/>
<location filename="../../MainWindow.cpp" line="2197"/>
<location filename="../../MainWindow.cpp" line="2198"/>
<source>Fetch</source>
<translation>Загрузить</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="166"/>
<source>Pull</source>
<translation>Обновить</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="201"/>
<source>Push</source>
<translation>Выгрузить</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="252"/>
<location filename="../../MainWindow.ui" line="1476"/>
<source>Terminal</source>
<translation>Терминал</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="287"/>
<location filename="../../MainWindow.ui" line="1485"/>
<source>Explorer</source>
<translation>Обзор</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="373"/>
<source>Repository</source>
<translation>Репозиторий</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="380"/>
<source>Branch Name</source>
<translation>Имя ветви</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="432"/>
<location filename="../../MainWindow.ui" line="1520"/>
<source>Offline</source>
<translation>Офлайн</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="422"/>
<location filename="../../MainWindow.ui" line="1515"/>
<source>Online</source>
<translation>Онлайн</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="748"/>
<location filename="../../MainWindow.cpp" line="1594"/>
<source>Unstage</source>
<translation>Не отслеживать</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="777"/>
<source>Select all</source>
<translation>Выбрать всё</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="812"/>
<location filename="../../MainWindow.cpp" line="1464"/>
<source>Stage</source>
<translation>Отслеживать</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="841"/>
<location filename="../../MainWindow.cpp" line="841"/>
<source>Commit</source>
<translation>Фиксация</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="972"/>
<source>&File</source>
<translation>&Файл</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="983"/>
<source>&View</source>
<translation>Вид(&V)</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="989"/>
<source>&Edit</source>
<translation>Редактировать(&E)</translation>
</message>
<message>
<source>Destructive</source>
<translation type="vanished">注意を要するコマンド</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1343"/>
<source>Settings</source>
<translation>Настройки</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1365"/>
<location filename="../../MainWindow.cpp" line="1328"/>
<source>Edit tags...</source>
<translation>Правка тэгов...</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1368"/>
<source>Edit tags</source>
<translation>Правка тэгов</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1490"/>
<source>Clean -df</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1495"/>
<source>Reset --hard</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1422"/>
<source>Create a repository</source>
<translation>Создание репозитория</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1409"/>
<source>Push upstream</source>
<translation>Выгрузить в источник</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1431"/>
<source>Stop process</source>
<translation>Остановить</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1436"/>
<source>E&xit</source>
<translation>В&ыход</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1439"/>
<source>Ctrl+Q</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1444"/>
<source>Reflog...</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1449"/>
<source>Property...</source>
<translation>Свойства...</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1454"/>
<location filename="../../MainWindow.ui" line="1457"/>
<source>Set GPG signing</source>
<translation>Настройка подписей GPG</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1467"/>
<source>Fetch --prune</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1001"/>
<source>&Help</source>
<translation>Помощь (&H)</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="605"/>
<source>...</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1007"/>
<source>&Window</source>
<translation>Окно(&W)</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1013"/>
<source>&Repository</source>
<translation>Репозиторий(&R)</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1017"/>
<location filename="../../MainWindow.ui" line="1500"/>
<source>Stash</source>
<translation>Спрятать</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1040"/>
<source>Re&mote</source>
<translation>Источник (&M)</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1055"/>
<source>&Destructive</source>
<translation>Разрушительные (&D)</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1086"/>
<source>Log</source>
<translation>Журнал</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1256"/>
<source>&Open existing working copy...</source>
<translation>Открыть существующий репозиторий (&O)...</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1259"/>
<location filename="../../MainWindow.cpp" line="1657"/>
<source>Add existing working copy</source>
<translation>Добавить существующий репозиторий</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1264"/>
<location filename="../../MainWindow.ui" line="1267"/>
<source>Refresh</source>
<translation>Обновить</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1270"/>
<source>F5</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1279"/>
<source>&Commit</source>
<translation>Фиксация (&Commit)</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1288"/>
<source>&Push</source>
<translation>Выгрузить (&Push)</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1296"/>
<source>test</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1299"/>
<source>Ctrl+T</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1308"/>
<source>Pu&ll</source>
<translation>Обновить (Pu&ll)</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1317"/>
<source>&Fetch</source>
<translation>Загрузить (&Fetch)</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1322"/>
<location filename="../../MainWindow.ui" line="1325"/>
<source>Edit global .gitconfig</source>
<translation>Прака глобального .gitconfig</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1330"/>
<source>Edit .git/config</source>
<translation>Правка .git/config</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1335"/>
<source>Edit .gitignore</source>
<translation>Правка .gitignore</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1340"/>
<source>&Settings...</source>
<translation>Настройки (&S)...</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1391"/>
<source>&Jump...</source>
<translation>Переход (&Jump)...</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1394"/>
<source>Ctrl+J</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1399"/>
<source>Check&out...</source>
<translation>Проверка (C&heckout)...</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1404"/>
<location filename="../../MainWindow.cpp" line="1340"/>
<source>Delete branch...</source>
<translation>Удалить ветвь...</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1505"/>
<source>Apply</source>
<translation>Применить</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1510"/>
<source>Drop</source>
<translation>Отменить</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1528"/>
<source>Repositories panel</source>
<translation>Панель репозиториев</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1531"/>
<source>Ctrl+R</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1414"/>
<location filename="../../MainWindow.ui" line="1417"/>
<source>Reset HEAD~1</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1462"/>
<location filename="../../MainWindow.cpp" line="1341"/>
<source>Delete remote branch...</source>
<translation>Удалить ветвь источника...</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1360"/>
<source>&About</source>
<translation>&О программе</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1373"/>
<source>Push all tags</source>
<translation>Выгрузить все тэги</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1378"/>
<source>Set config user</source>
<translation>Выбрать пользователя</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="1386"/>
<source>&Log</source>
<translation>Журнал (&Log)</translation>
</message>
<message>
<source>Unnamed</source>
<translation type="vanished">無名</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="643"/>
<source>Default</source>
<translation>По-умолчанию</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="840"/>
<source>Graph</source>
<translation>Граф</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="842"/>
<source>Date</source>
<translation>Дата</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="843"/>
<source>Author</source>
<translation>Автор</translation>
</message>
<message>
<source>Description</source>
<translation type="vanished">概要</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="990"/>
<location filename="../../MainWindow.cpp" line="1115"/>
<source>Uncommited changes</source>
<translation>Незафиксированные изменения</translation>
</message>
<message>
<source>Are you sure you want to remove the repository from bookmarks ?</source>
<translation type="vanished">リポジトリをブックマークから削除してよろしいですか?</translation>
</message>
<message>
<source>(Files will NOT be deleted)</source>
<translation type="vanished">(ファルは削除されません)</translation>
</message>
<message>
<source>Open Repository</source>
<translation type="vanished">リポジトリを開く</translation>
</message>
<message>
<source>No such folder</source>
<translation type="vanished">そのようなフォルダはありません</translation>
</message>
<message>
<source>Not a valid git repository</source>
<translation type="vanished">有効なリポジトリではありません</translation>
</message>
<message>
<source>Commit message can not be omitted.</source>
<translation type="vanished">コミットメッセージを空にすることはできません。</translation>
</message>
<message>
<source>Repository Property</source>
<translation type="vanished">リポジトリのプロパティ</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1218"/>
<source>&Add new group</source>
<translation>Новая &группа</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1219"/>
<source>&Delete group</source>
<translation>Удалить &группу</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1220"/>
<source>&Rename group</source>
<translation>Переименовать группу(&R)</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1225"/>
<source>New group</source>
<translation>Новая группа</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1249"/>
<source>Open &terminal</source>
<translation>Открыть терминал (&T)</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1250"/>
<source>Open command promp&t</source>
<translation>Открыть терминал (&T)</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1252"/>
<source>&Open</source>
<translation>Открыть (&Open)</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1263"/>
<source>Open &folder</source>
<translation>Открыть каталог (&F)</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1268"/>
<source>&Remove</source>
<translation>Удалить (&Remove)</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1311"/>
<source>Copy commit id (7 letters)</source>
<translation>Скопируйте ID фиксации(7 символов)</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1312"/>
<source>Copy commit id (completely)</source>
<translation>Скопируйте ID фиксации(полностью)</translation>
</message>
<message>
<source>Edit comment...</source>
<translation type="vanished">コメントを編集...</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1326"/>
<location filename="../../MainWindow.cpp" line="2130"/>
<source>Rebase</source>
<translation>Перебазирование</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1345"/>
<source>Explore</source>
<translation>Обзор</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1334"/>
<source>Reset HEAD</source>
<translation>Откат HEAD</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="844"/>
<source>Message</source>
<translation>Комментарий</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1420"/>
<location filename="../../MainWindow.cpp" line="1469"/>
<location filename="../../MainWindow.cpp" line="1595"/>
<source>History</source>
<translation>История</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="2088"/>
<location filename="../../MainWindow.cpp" line="2102"/>
<source>No such commit</source>
<translation>Фиксация не существует</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="2200"/>
<location filename="../../MainWindow.cpp" line="2201"/>
<source>Update</source>
<translation>Обновить</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="2481"/>
<source>Authentication Failed</source>
<translation>Авторизация не удалась</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1329"/>
<source>Revert</source>
<translation>Отмена (Revert)</translation>
</message>
<message>
<source>Failed to commit</source>
<translation type="vanished">コミット失敗</translation>
</message>
<message>
<source>No remote repository is registered.</source>
<translation type="vanished">リモートリポジトリが登録されていません。</translation>
</message>
<message>
<source>The current branch %1 has no upstream branch.</source>
<translation type="vanished">現在のブランチ「%1」には上流ブランチがありません。</translation>
</message>
<message>
<source>You try push --set-upstream</source>
<translation type="vanished">--set-upstream を試してください</translation>
</message>
<message>
<source>Connection refused.</source>
<translation type="vanished">接続が拒否されました。</translation>
</message>
<message>
<source>&Property</source>
<translation type="vanished">プロパティ(&P)</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1316"/>
<source>Checkout/Branch...</source>
<translation>Проверка/ветвь...</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1322"/>
<source>Edit message...</source>
<translation>Правка комментария...</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1325"/>
<source>Merge</source>
<translation>Слияние</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1327"/>
<source>Cherry-pick</source>
<translation>Выборочное применение (Cherry-pick)</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1419"/>
<location filename="../../MainWindow.cpp" line="1468"/>
<source>Untrack</source>
<translation>Не отслеживать</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1421"/>
<location filename="../../MainWindow.cpp" line="1470"/>
<location filename="../../MainWindow.cpp" line="1596"/>
<source>Blame</source>
<translation>Обращение</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1429"/>
<source>Delete selected files.</source>
<translation>Удалить выбранные файлы.</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1440"/>
<source>rm --cached files</source>
<translation></translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1465"/>
<source>Reset</source>
<translation>Откат (Reset)</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1466"/>
<source>Ignore</source>
<translation>Игнорировать</translation>
</message>
<message>
<source>Reset a file</source>
<translation type="vanished">ファイルをリセットします</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="2127"/>
<source>Are you sure you want to rebase the commit ?</source>
<translation>Вы уверены, что хотите переместить фиксацию?</translation>
</message>
<message>
<source>No repository selected</source>
<translation type="vanished">リポジトリが選択されていません</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="1418"/>
<location filename="../../MainWindow.cpp" line="1467"/>
<source>Delete</source>
<translation>Удалить</translation>
</message>
<message>
<source>Are you sure you want to run the following command ?</source>
<translation type="vanished">次のコマンドを実行してよろしいですか?</translation>
</message>
<message>
<source>Revert all files</source>
<translation type="vanished">すべてのファイルの変更を破棄</translation>
</message>
<message>
<source>Select %1 command</source>
<translation type="vanished">%1 コマンドの選択</translation>
</message>
<message>
<source>A file with same name already exists</source>
<translation type="vanished">同じ名前のファイルが既に存在しています</translation>
</message>
<message>
<source>A folder with same name already exists</source>
<translation type="vanished">同じ名前のフォルダが既に存在しています</translation>
</message>
<message>
<source>Invalid folder</source>
<translation type="vanished">無効なフォルダ</translation>
</message>
<message>
<source>No such folder. Create it now ?</source>
<translation type="vanished">このフォルダはありません。作成しますか?</translation>
</message>
<message>
<source>The URL is a valid repository</source>
<translation type="vanished">このURLは有効なリポジトリです</translation>
</message>
<message>
<source>Failed to access the URL</source>
<translation type="vanished">このURLへのアクセスに失敗しました</translation>
</message>
<message>
<source>Remote Repository</source>
<translation type="vanished">リモートリポジトリ</translation>
</message>
<message>
<location filename="../../MainWindow.cpp" line="2088"/>
<location filename="../../MainWindow.cpp" line="2102"/>
<source>Jump</source>
<translation>Перейти</translation>
</message>
<message>
<source>That commmit has not foud or not read yet</source>
<translation type="vanished">そのコミットは、見つからないか、まだ読み込まれていません</translation>
</message>
<message>
<source>Failed to delete the branch '%1'
</source>
<translation type="vanished">ブランチの削除に失敗しました : '%1'</translation>
</message>
</context>
<context>
<name>MergeBranchDialog</name>
<message>
<location filename="../../MergeBranchDialog.ui" line="14"/>
<source>Merge</source>
<translation>Слияние</translation>
</message>
<message>
<location filename="../../MergeBranchDialog.ui" line="26"/>
<source>Current branch :</source>
<translation>Текущая ветвь :</translation>
</message>
<message>
<location filename="../../MergeBranchDialog.ui" line="39"/>
<source>TextLabel</source>
<translation></translation>
</message>
<message>
<location filename="../../MergeBranchDialog.ui" line="62"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../MergeBranchDialog.ui" line="75"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>MyImageViewWidget</name>
<message>
<location filename="../../MyImageViewWidget.cpp" line="35"/>
<source>Save as...</source>
<translation>Сохранить как...</translation>
</message>
<message>
<location filename="../../MyImageViewWidget.cpp" line="43"/>
<source>Save as</source>
<translation>Сохранить как</translation>
</message>
</context>
<context>
<name>MyTextEditorWidget</name>
<message>
<location filename="../../MyTextEditorWidget.cpp" line="35"/>
<source>Save as...</source>
<translation>Сохранить как...</translation>
</message>
<message>
<location filename="../../MyTextEditorWidget.cpp" line="36"/>
<source>Copy</source>
<translation>Копировать</translation>
</message>
<message>
<location filename="../../MyTextEditorWidget.cpp" line="43"/>
<source>Save as</source>
<translation>Сохранить как</translation>
</message>
</context>
<context>
<name>ObjectBrowserDialog</name>
<message>
<location filename="../../ObjectBrowserDialog.ui" line="14"/>
<source>Object Browser</source>
<translation>Обозреватель объектов</translation>
</message>
<message>
<location filename="../../ObjectBrowserDialog.ui" line="38"/>
<source>Inspect</source>
<translation>Проверка</translation>
</message>
<message>
<location filename="../../ObjectBrowserDialog.ui" line="58"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../ObjectBrowserDialog.ui" line="65"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
<message>
<location filename="../../ObjectBrowserDialog.cpp" line="23"/>
<source>ID</source>
<translation></translation>
</message>
<message>
<location filename="../../ObjectBrowserDialog.cpp" line="24"/>
<source>Type</source>
<translation>Тип</translation>
</message>
<message>
<location filename="../../ObjectBrowserDialog.cpp" line="119"/>
<source>Object Inspection</source>
<translation>Проверка объектов</translation>
</message>
</context>
<context>
<name>PushDialog</name>
<message>
<location filename="../../PushDialog.ui" line="14"/>
<source>Push</source>
<translation>Выгрузить</translation>
</message>
<message>
<location filename="../../PushDialog.ui" line="20"/>
<source>push --set-upstream</source>
<translation>Выгрузить в источник</translation>
</message>
<message>
<location filename="../../PushDialog.ui" line="26"/>
<source>Remote</source>
<translation>Источник</translation>
</message>
<message>
<location filename="../../PushDialog.ui" line="33"/>
<source>Branch</source>
<translation>Ветвь</translation>
</message>
<message>
<location filename="../../PushDialog.ui" line="64"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../PushDialog.ui" line="71"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>RebaseOntoDialog</name>
<message>
<source>Branch</source>
<translation type="vanished">ブランチ</translation>
</message>
<message>
<source>Cancel</source>
<translation type="vanished">キャンセル</translation>
</message>
</context>
<context>
<name>ReflogWindow</name>
<message>
<location filename="../../ReflogWindow.ui" line="14"/>
<source>Reflog</source>
<translation></translation>
</message>
<message>
<location filename="../../ReflogWindow.ui" line="57"/>
<source>Close</source>
<translation>Закрыть</translation>
</message>
<message>
<location filename="../../ReflogWindow.cpp" line="35"/>
<source>Commit</source>
<translation>Фиксация (Commit)</translation>
</message>
<message>
<location filename="../../ReflogWindow.cpp" line="36"/>
<source>Head</source>
<translation>Текущая</translation>
</message>
<message>
<location filename="../../ReflogWindow.cpp" line="37"/>
<source>Command</source>
<translation>Команда</translation>
</message>
<message>
<location filename="../../ReflogWindow.cpp" line="38"/>
<source>Message</source>
<translation>Комментарий</translation>
</message>
<message>
<source>Comment</source>
<translation type="vanished">コメント</translation>
</message>
<message>
<location filename="../../ReflogWindow.cpp" line="98"/>
<source>Checkout</source>
<translation>Проверка (Checkout)</translation>
</message>
<message>
<location filename="../../ReflogWindow.cpp" line="99"/>
<source>Explorer</source>
<translation>Обзор</translation>
</message>
</context>
<context>
<name>RemoteRepositoriesTableWidget</name>
<message>
<location filename="../../RemoteRepositoriesTableWidget.cpp" line="27"/>
<source>Copy URL</source>
<translation>Копировать URL</translation>
</message>
</context>
<context>
<name>RepositoryPropertyDialog</name>
<message>
<location filename="../../RepositoryPropertyDialog.ui" line="14"/>
<source>Repository Property</source>
<oldsource>Repository Properties</oldsource>
<translation>Свойства репозитория</translation>
</message>
<message>
<location filename="../../RepositoryPropertyDialog.ui" line="65"/>
<source>TextLabel</source>
<translation></translation>
</message>
<message>
<location filename="../../RepositoryPropertyDialog.ui" line="95"/>
<source>Local dir :</source>
<translation>Локальный каталог:</translation>
</message>
<message>
<location filename="../../RepositoryPropertyDialog.ui" line="114"/>
<source>Remote URLs</source>
<translation>URL источников</translation>
</message>
<message>
<location filename="../../RepositoryPropertyDialog.ui" line="124"/>
<source>Remote</source>
<translation>Источник</translation>
</message>
<message>
<location filename="../../RepositoryPropertyDialog.ui" line="130"/>
<source>Add</source>
<translation>Добавить</translation>
</message>
<message>
<location filename="../../RepositoryPropertyDialog.ui" line="137"/>
<source>Edit</source>
<translation>Редактировать</translation>
</message>
<message>
<location filename="../../RepositoryPropertyDialog.ui" line="157"/>
<source>Remove</source>
<translation>Удалить</translation>
</message>
<message>
<location filename="../../RepositoryPropertyDialog.ui" line="169"/>
<source>&Remote menu</source>
<translation>Меню источника (&R)</translation>
</message>
<message>
<location filename="../../RepositoryPropertyDialog.ui" line="189"/>
<source>Close</source>
<translation>Закрыть</translation>
</message>
<message>
<location filename="../../RepositoryPropertyDialog.cpp" line="132"/>
<source>Confirm Remove</source>
<translation>Подтвердите удаление</translation>
</message>
<message>
<location filename="../../RepositoryPropertyDialog.cpp" line="132"/>
<source>Are you sure you want to remove the remote '%1' from the repository '%2' ?</source>
<translation>Вы точно хотите удалить источник '%1' из репозитория '%2' ?</translation>
</message>
</context>
<context>
<name>SearchFromGitHubDialog</name>
<message>
<location filename="../../SearchFromGitHubDialog.ui" line="14"/>
<source>Search From GitHub</source>
<translation>Поиск на GitHub</translation>
</message>
<message>
<location filename="../../SearchFromGitHubDialog.ui" line="25"/>
<source>Search</source>
<translation>Поиск</translation>
</message>
<message>
<location filename="../../SearchFromGitHubDialog.ui" line="78"/>
<source>ssh</source>
<translation></translation>
</message>
<message>
<location filename="../../SearchFromGitHubDialog.ui" line="88"/>
<source>http</source>
<translation></translation>
</message>
<message>
<location filename="../../SearchFromGitHubDialog.ui" line="115"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../SearchFromGitHubDialog.ui" line="122"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
<message>
<location filename="../../SearchFromGitHubDialog.cpp" line="58"/>
<source>Name</source>
<translation>Имя</translation>
</message>
<message>
<location filename="../../SearchFromGitHubDialog.cpp" line="59"/>
<source>Owner</source>
<translation>Владелец</translation>
</message>
<message>
<location filename="../../SearchFromGitHubDialog.cpp" line="60"/>
<source>Score</source>
<translation>Популярность</translation>
</message>
<message>
<location filename="../../SearchFromGitHubDialog.cpp" line="61"/>
<source>Description</source>
<translation>Описание</translation>
</message>
</context>
<context>
<name>SelectCommandDialog</name>
<message>
<location filename="../../SelectCommandDialog.ui" line="14"/>
<source>Select git command</source>
<translation>Выбрать команду git</translation>
</message>
<message>
<location filename="../../SelectCommandDialog.ui" line="20"/>
<source>TextLabel</source>
<translation></translation>
</message>
<message>
<location filename="../../SelectCommandDialog.ui" line="32"/>
<source>&Browse...</source>
<translation>&Обзор...</translation>
</message>
<message>
<location filename="../../SelectCommandDialog.ui" line="52"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../SelectCommandDialog.ui" line="59"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
<message>
<location filename="../../SelectCommandDialog.cpp" line="29"/>
<source>Please select the '%1' command you want to use.</source>
<translation>Выберите '%1' команду, которую вы хотите выполнить.</translation>
</message>
<message>
<location filename="../../SelectCommandDialog.cpp" line="58"/>
<location filename="../../SelectCommandDialog.cpp" line="65"/>
<source>%1 command (%2);;</source>
<translation>%1 команда (%2);;</translation>
</message>
<message>
<location filename="../../SelectCommandDialog.cpp" line="60"/>
<source>Executable files (*.exe)</source>
<translation>Исполняемые файлы (*.exe)</translation>
</message>
<message>
<location filename="../../SelectCommandDialog.cpp" line="71"/>
<source>%1 command</source>
<translation>%1 команда</translation>
</message>
</context>
<context>
<name>SelectGpgKeyDialog</name>
<message>
<location filename="../../SelectGpgKeyDialog.ui" line="14"/>
<source>Select GPG Key</source>
<translation>Выбор ключа GPG</translation>
</message>
<message>
<location filename="../../SelectGpgKeyDialog.ui" line="57"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../SelectGpgKeyDialog.ui" line="64"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
<message>
<location filename="../../SelectGpgKeyDialog.cpp" line="31"/>
<source>ID</source>
<translation></translation>
</message>
<message>
<location filename="../../SelectGpgKeyDialog.cpp" line="32"/>
<source>Name</source>
<translation>Имя</translation>
</message>
<message>
<location filename="../../SelectGpgKeyDialog.cpp" line="33"/>
<source>Mail</source>
<translation></translation>
</message>
</context>
<context>
<name>SelectItemDialog</name>
<message>
<location filename="../../SelectItemDialog.ui" line="14"/>
<source>Dialog</source>
<translation></translation>
</message>
<message>
<location filename="../../SelectItemDialog.ui" line="25"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../SelectItemDialog.ui" line="32"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>SetGlobalUserDialog</name>
<message>
<location filename="../../SetGlobalUserDialog.ui" line="14"/>
<source>Global User Setting</source>
<translation>Глобальная настройка пользователя</translation>
</message>
<message>
<location filename="../../SetGlobalUserDialog.ui" line="31"/>
<source>Name</source>
<translation>Имя</translation>
</message>
<message>
<location filename="../../SetGlobalUserDialog.ui" line="41"/>
<source>Mail</source>
<translation></translation>
</message>
<message>
<location filename="../../SetGlobalUserDialog.ui" line="68"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../SetGlobalUserDialog.ui" line="78"/>
<source>Skip</source>
<translation>Пропустить</translation>
</message>
</context>
<context>
<name>SetGpgSigningDialog</name>
<message>
<location filename="../../SetGpgSigningDialog.ui" line="14"/>
<source>Set GPG Signing</source>
<translation>Настройка подписей GPG</translation>
</message>
<message>
<location filename="../../SetGpgSigningDialog.ui" line="26"/>
<source>Global</source>
<translation>Глобально</translation>
</message>
<message>
<location filename="../../SetGpgSigningDialog.ui" line="39"/>
<location filename="../../SetGpgSigningDialog.cpp" line="29"/>
<source>Repository</source>
<translation>Репозиторий</translation>
</message>
<message>
<location filename="../../SetGpgSigningDialog.ui" line="61"/>
<source>ID</source>
<translation></translation>
</message>
<message>
<location filename="../../SetGpgSigningDialog.ui" line="71"/>
<source>Name</source>
<translation>Имя</translation>
</message>
<message>
<location filename="../../SetGpgSigningDialog.ui" line="81"/>
<source>Mail</source>
<translation></translation>
</message>
<message>
<location filename="../../SetGpgSigningDialog.ui" line="93"/>
<source>Select</source>
<translation>Выбрать</translation>
</message>
<message>
<location filename="../../SetGpgSigningDialog.ui" line="113"/>
<source>Clear</source>
<translation>Очистить</translation>
</message>
<message>
<location filename="../../SetGpgSigningDialog.ui" line="127"/>
<source>Configure...</source>
<translation>Настроить...</translation>
</message>
<message>
<location filename="../../SetGpgSigningDialog.ui" line="147"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../SetGpgSigningDialog.ui" line="154"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>SetRemoteUrlDialog</name>
<message>
<location filename="../../SetRemoteUrlDialog.ui" line="14"/>
<source>Set Remote URL</source>
<translation>Установка URL источника</translation>
</message>
<message>
<location filename="../../SetRemoteUrlDialog.ui" line="20"/>
<source>Current URLs</source>
<translation>Текущий URL</translation>
</message>
<message>
<location filename="../../SetRemoteUrlDialog.ui" line="30"/>
<source>New URL</source>
<translation>Новый URL</translation>
</message>
<message>
<location filename="../../SetRemoteUrlDialog.ui" line="36"/>
<source>Name</source>
<translation>Имя</translation>
</message>
<message>
<location filename="../../SetRemoteUrlDialog.ui" line="91"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../SetRemoteUrlDialog.ui" line="98"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
<message>
<location filename="../../SetRemoteUrlDialog.ui" line="43"/>
<source>URL</source>
<translation></translation>
</message>
<message>
<location filename="../../SetRemoteUrlDialog.ui" line="63"/>
<source>&Test</source>
<translation>&Тестировать</translation>
</message>
</context>
<context>
<name>SetUserDialog</name>
<message>
<location filename="../../SetUserDialog.ui" line="14"/>
<source>Set User</source>
<translation>Выбрать пользователя</translation>
</message>
<message>
<location filename="../../SetUserDialog.ui" line="20"/>
<source>Global</source>
<translation>Глобально</translation>
</message>
<message>
<location filename="../../SetUserDialog.ui" line="27"/>
<location filename="../../SetUserDialog.cpp" line="31"/>
<source>Repository</source>
<translation>Репозиторий</translation>
</message>
<message>
<location filename="../../SetUserDialog.ui" line="42"/>
<source>Name</source>
<translation>Имя</translation>
</message>
<message>
<location filename="../../SetUserDialog.ui" line="52"/>
<source>Mail</source>
<translation></translation>
</message>
<message>
<location filename="../../SetUserDialog.ui" line="66"/>
<source>Get icon from Gravatar</source>
<translation>Получить аватар с Gravatar</translation>
</message>
<message>
<location filename="../../SetUserDialog.ui" line="122"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../SetUserDialog.ui" line="132"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>SettingBehaviorForm</name>
<message>
<location filename="../../SettingBehaviorForm.ui" line="14"/>
<source>Behavior</source>
<translation>Поведение</translation>
</message>
<message>
<location filename="../../SettingBehaviorForm.ui" line="20"/>
<location filename="../../SettingBehaviorForm.cpp" line="77"/>
<source>Default working folder</source>
<translation>Рабочий каталог по-умолчанию</translation>
</message>
<message>
<location filename="../../SettingBehaviorForm.ui" line="29"/>
<source>Browse...</source>
<translation>Обзор...</translation>
</message>
<message>
<location filename="../../SettingBehaviorForm.ui" line="39"/>
<source>Automatically fetch when opening the repository</source>
<translation>Автоматически загружать при открытии репозитория</translation>
</message>
<message>
<location filename="../../SettingBehaviorForm.ui" line="46"/>
<source>Get committer's icon from gravatar.com</source>
<translation>Получить аватар коммитера с gravatar.com</translation>
</message>
<message>
<location filename="../../SettingBehaviorForm.ui" line="61"/>
<source>Maximum number of commit item acquisitions</source>
<translation>Максимальное число загружаемых коммитов</translation>
</message>
<message>
<location filename="../../SettingBehaviorForm.ui" line="88"/>
<source>Watch remote changes periodically</source>
<oldsource>Periodically watch remote updates</oldsource>
<translation>Отслеживать изменения в источнике</translation>
</message>
<message>
<location filename="../../SettingBehaviorForm.ui" line="108"/>
<source>interval</source>
<oldsource>interval in min:</oldsource>
<translation>интервал</translation>
</message>
<message>
<location filename="../../SettingBehaviorForm.ui" line="128"/>
<source>GPG signing policy</source>
<oldsource>GPG Signing Policy</oldsource>
<translation>Политика подписания</translation>
</message>
<message>
<location filename="../../SettingBehaviorForm.ui" line="135"/>
<source>Configure...</source>
<translation>Настроить...</translation>
</message>
<message>
<location filename="../../SettingBehaviorForm.cpp" line="38"/>
<source>Disable</source>
<translation>Выключить</translation>
</message>
<message>
<location filename="../../SettingBehaviorForm.cpp" line="40"/>
<source>1 min</source>
<translation>1 минута</translation>
</message>
<message>
<location filename="../../SettingBehaviorForm.cpp" line="42"/>
<source>%1 mins</source>
<translation>%1 минут</translation>
</message>
</context>
<context>
<name>SettingExampleForm</name>
<message>
<location filename="../../SettingExampleForm.ui" line="14"/>
<source>Example</source>
<translation></translation>
</message>
<message>
<location filename="../../SettingExampleForm.ui" line="20"/>
<source>Underconstruction</source>
<translation></translation>
</message>
</context>
<context>
<name>SettingGeneralForm</name>
<message>
<location filename="../../SettingGeneralForm.ui" line="14"/>
<source>General</source>
<translation>Общие</translation>
</message>
<message>
<location filename="../../SettingGeneralForm.ui" line="20"/>
<source>Language</source>
<translation>Язык</translation>
</message>
<message>
<location filename="../../SettingGeneralForm.ui" line="31"/>
<source>Change Language...</source>
<translation>Изменить язык...</translation>
</message>
<message>
<location filename="../../SettingGeneralForm.ui" line="54"/>
<source>Change Theme...</source>
<translation>Изменить тему...</translation>
</message>
<message>
<location filename="../../SettingGeneralForm.ui" line="66"/>
<source>Remember and restore window position</source>
<translation>Помнить позицию и размер окна</translation>
</message>
<message>
<location filename="../../SettingGeneralForm.ui" line="73"/>
<source>Enable high DPI scaling</source>
<translation>Включить highDPI масштабирование</translation>
</message>
<message>
<location filename="../../SettingGeneralForm.ui" line="43"/>
<source>Theme</source>
<translation>Тема</translation>
</message>
<message>
<location filename="../../SettingGeneralForm.cpp" line="16"/>
<source>English</source>
<translation></translation>
</message>
<message>
<location filename="../../SettingGeneralForm.cpp" line="17"/>
<source>Japanese</source>
<translation>Японский</translation>
</message>
<message>
<location filename="../../SettingGeneralForm.cpp" line="19"/>
<source>Standard</source>
<translation>Стандартная</translation>
</message>
<message>
<location filename="../../SettingGeneralForm.cpp" line="20"/>
<source>Dark</source>
<translation>Тёмная</translation>
</message>
<message>
<location filename="../../SettingGeneralForm.cpp" line="78"/>
<source>Select Language</source>
<translation>Выбор языка</translation>
</message>
<message>
<location filename="../../SettingGeneralForm.cpp" line="97"/>
<source>Select Theme</source>
<translation>Выбор темы</translation>
</message>
<message>
<location filename="../../SettingGeneralForm.ui" line="93"/>
<source>(Changes are applied at next run)</source>
<translation>(Изменения применятся при следующем запуске)</translation>
</message>
</context>
<context>
<name>SettingNetworkForm</name>
<message>
<location filename="../../SettingNetworkForm.ui" line="14"/>
<source>Network</source>
<translation>Сеть</translation>
</message>
<message>
<location filename="../../SettingNetworkForm.ui" line="20"/>
<source>Proxy server</source>
<oldsource>Proxy Server</oldsource>
<translation>Прокси-сервер</translation>
</message>
<message>
<location filename="../../SettingNetworkForm.ui" line="26"/>
<source>No proxy</source>
<oldsource>No Proxy</oldsource>
<translation>Без прокси</translation>
</message>
<message>
<location filename="../../SettingNetworkForm.ui" line="33"/>
<source>Auto detect</source>
<oldsource>Auto Detect</oldsource>
<translation>Авто-определение</translation>
</message>
<message>
<location filename="../../SettingNetworkForm.ui" line="40"/>
<source>Manual</source>
<translation>Вручную</translation>
</message>
</context>
<context>
<name>SettingProgramsForm</name>
<message>
<location filename="../../SettingProgramsForm.ui" line="14"/>
<source>Programs</source>
<translation>Программы</translation>
</message>
<message>
<location filename="../../SettingProgramsForm.ui" line="20"/>
<source>Git command</source>
<translation>путь к git</translation>
</message>
<message>
<location filename="../../SettingProgramsForm.ui" line="29"/>
<location filename="../../SettingProgramsForm.ui" line="48"/>
<location filename="../../SettingProgramsForm.ui" line="67"/>
<source>Browse...</source>
<translation>Обзор...</translation>
</message>
<message>
<location filename="../../SettingProgramsForm.ui" line="39"/>
<source>File command</source>
<translation>путь к file</translation>
</message>
<message>
<location filename="../../SettingProgramsForm.ui" line="58"/>
<source>GPG command (option)</source>
<translation>путь к gpg (опционально)</translation>
</message>
</context>
<context>
<name>SettingsDialog</name>
<message>
<location filename="../../SettingsDialog.ui" line="14"/>
<source>Settings</source>
<translation>Настройки</translation>
</message>
<message>
<location filename="../../SettingsDialog.ui" line="141"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../SettingsDialog.ui" line="148"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
<context>
<name>TextEditDialog</name>
<message>
<location filename="../../TextEditDialog.ui" line="14"/>
<source>Dialog</source>
<translation></translation>
</message>
<message>
<location filename="../../TextEditDialog.ui" line="48"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../../TextEditDialog.ui" line="58"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
</context>
-<context>
- <name>WelcomeWizardDialog</name>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="14"/>
- <source>Welcome to the Guitar Wizard</source>
- <translation>Guitarウィザードへようこそ</translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="45"/>
- <source>Helper Tools</source>
- <translation>ヘルパープログラム</translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="61"/>
- <location filename="../../WelcomeWizardDialog.ui" line="338"/>
- <source>git</source>
- <translation></translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="71"/>
- <location filename="../../WelcomeWizardDialog.ui" line="88"/>
- <location filename="../../WelcomeWizardDialog.ui" line="248"/>
- <source>Browse</source>
- <translation>参照</translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="78"/>
- <location filename="../../WelcomeWizardDialog.ui" line="352"/>
- <source>file</source>
- <translation></translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="120"/>
- <source>Global User Information</source>
- <translation>グローバルユーザー情報</translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="142"/>
- <source>git config --global user.name</source>
- <translation></translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="152"/>
- <source>git config --global user.email</source>
- <translation></translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="168"/>
- <source>Get icon from Gravatar</source>
- <translation>Gravatarからアイコンを取得</translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="222"/>
- <location filename="../../WelcomeWizardDialog.cpp" line="155"/>
- <source>Default Working Folder</source>
- <translation>規定の作業フォルダ</translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="238"/>
- <location filename="../../WelcomeWizardDialog.ui" line="324"/>
- <source>folder</source>
- <translation>フォルダ</translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="280"/>
- <source>Ready to play the Guitar !</source>
- <translation>Guitarの準備ができました!</translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="296"/>
- <source>user</source>
- <translation>ユーザー</translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="310"/>
- <source>email</source>
- <translation>メール</translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="426"/>
- <location filename="../../WelcomeWizardDialog.cpp" line="147"/>
- <source><< Prev</source>
- <translation><< 戻る</translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.ui" line="433"/>
- <location filename="../../WelcomeWizardDialog.cpp" line="148"/>
- <source>Next >></source>
- <translation>次へ >></translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.cpp" line="126"/>
- <source>Cancel</source>
- <translation>キャンセル</translation>
- </message>
- <message>
- <location filename="../../WelcomeWizardDialog.cpp" line="145"/>
- <source>Finish</source>
- <translation>完了</translation>
- </message>
-</context>
</TS>
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Jun 15, 11:34 PM (2 w, 2 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
70112
Default Alt Text
(188 KB)
Attached To
Mode
R77 Guitar
Attached
Detach File
Event Timeline