Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
261 KB
Referenced Files
None
Subscribers
None
This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/src/CommitPropertyDialog.cpp b/src/CommitPropertyDialog.cpp
index 6d1bb86..583e655 100644
--- a/src/CommitPropertyDialog.cpp
+++ b/src/CommitPropertyDialog.cpp
@@ -1,171 +1,171 @@
#include "CommitPropertyDialog.h"
#include "ui_CommitPropertyDialog.h"
#include "ApplicationGlobal.h"
#include "AvatarLoader.h"
#include "BasicMainWindow.h"
#include "common/misc.h"
#include "gpg.h"
#include "main.h"
struct CommitPropertyDialog::Private {
BasicMainWindow *mainwindow;
Git::CommitItem commit;
AvatarLoader avatar_loader;
};
void CommitPropertyDialog::init(BasicMainWindow *mw)
{
Qt::WindowFlags flags = windowFlags();
flags &= ~Qt::WindowContextHelpButtonHint;
setWindowFlags(flags);
ui->pushButton_jump->setVisible(false);
m->mainwindow = mw;
- ui->lineEdit_description->setText(m->commit.message);
+ ui->lineEdit_message->setText(m->commit.message);
ui->lineEdit_commit_id->setText(m->commit.commit_id);
ui->lineEdit_date->setText(misc::makeDateTimeString(m->commit.commit_date));
ui->lineEdit_author->setText(m->commit.author);
ui->lineEdit_mail->setText(m->commit.email);
QString text;
for (QString const &id : m->commit.parent_ids) {
text += id + '\n';
}
ui->plainTextEdit_parent_ids->setPlainText(text);
gpg::Data key;
int n1 = m->commit.fingerprint.size();
if (n1 > 0) {
QList<gpg::Data> keys;
if (gpg::listKeys(global->gpg_command, &keys)) {
for (gpg::Data const &k : keys) {
int n2 = k.fingerprint.size();
if (n2 > 0) {
int n = std::min(n1, n2);
char const *p1 = m->commit.fingerprint.data() + n1 - n;
char const *p2 = k.fingerprint.data() + n2 - n;
if (memcmp(p1, p2, n) == 0) {
key = k;
break;
}
}
}
} else {
qDebug() << "Failed to get gpg keys";
}
if (key.id.isEmpty()) {
// gpgコマンドが登録されていないなど、keyidが取得できなかったとき
key.id = tr("<Unknown>");
}
}
if (key.id.isEmpty()) {
ui->frame_sign->setVisible(false);
} else {
{
int w = ui->label_signature_icon->width();
int h = ui->label_signature_icon->width();
QIcon icon = mainwindow()->verifiedIcon(m->commit.signature);
ui->label_signature_icon->setPixmap(icon.pixmap(w, h));
}
ui->lineEdit_sign_id->setText(key.id);
ui->lineEdit_sign_name->setText(key.name);
ui->lineEdit_sign_mail->setText(key.mail);
}
m->avatar_loader.start(mainwindow());
connect(&m->avatar_loader, &AvatarLoader::updated, [&](){
updateAvatar(false);
});
updateAvatar(true);
}
void CommitPropertyDialog::updateAvatar(bool request)
{
if (!mainwindow()->isOnlineMode()) return;
auto SetAvatar = [&](QString const &email, QLabel *label){
if (mainwindow()->appsettings()->get_committer_icon) {
label->setFixedSize(QSize(48, 48));
QIcon icon = m->avatar_loader.fetch(email.toStdString(), request);
setAvatar(icon, label);
} else {
label->setVisible(false);
}
};
SetAvatar(ui->lineEdit_mail->text(), ui->label_user_avatar);
SetAvatar(ui->lineEdit_sign_mail->text(), ui->label_sign_avatar);
}
CommitPropertyDialog::CommitPropertyDialog(QWidget *parent, BasicMainWindow *mw, Git::CommitItem const *commit)
: QDialog(parent)
, ui(new Ui::CommitPropertyDialog)
, m(new Private)
{
ui->setupUi(this);
m->commit = *commit;
init(mw);
}
CommitPropertyDialog::CommitPropertyDialog(QWidget *parent, BasicMainWindow *mw, QString const &commit_id)
: QDialog(parent)
, ui(new Ui::CommitPropertyDialog)
, m(new Private)
{
ui->setupUi(this);
mw->queryCommit(commit_id, &m->commit);
init(mw);
}
CommitPropertyDialog::~CommitPropertyDialog()
{
m->avatar_loader.stop();
delete m;
delete ui;
}
BasicMainWindow *CommitPropertyDialog::mainwindow()
{
return m->mainwindow;
}
void CommitPropertyDialog::setAvatar(QIcon const &icon, QLabel *label)
{
QPixmap pm = icon.pixmap(label->size());
label->setPixmap(pm);
}
void CommitPropertyDialog::showCheckoutButton(bool f)
{
ui->pushButton_checkout->setVisible(f);
}
void CommitPropertyDialog::showJumpButton(bool f)
{
ui->pushButton_jump->setVisible(f);
}
void CommitPropertyDialog::on_pushButton_checkout_clicked()
{
mainwindow()->checkout(this, &m->commit);
done(QDialog::Rejected);
}
void CommitPropertyDialog::on_pushButton_jump_clicked()
{
mainwindow()->jumpToCommit(m->commit.commit_id);
done(QDialog::Accepted);
}
void CommitPropertyDialog::on_pushButton_details_clicked()
{
mainwindow()->execCommitViewWindow(&m->commit);
}
void CommitPropertyDialog::on_pushButton_explorer_clicked()
{
mainwindow()->execCommitExploreWindow(this, &m->commit);
}
diff --git a/src/CommitPropertyDialog.ui b/src/CommitPropertyDialog.ui
index 0560ed9..f2809aa 100644
--- a/src/CommitPropertyDialog.ui
+++ b/src/CommitPropertyDialog.ui
@@ -1,386 +1,386 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CommitPropertyDialog</class>
<widget class="QDialog" name="CommitPropertyDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>527</width>
<height>482</height>
</rect>
</property>
<property name="windowTitle">
<string>Commit Property</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
- <widget class="ReadOnlyLineEdit" name="lineEdit_description">
+ <widget class="ReadOnlyLineEdit" name="lineEdit_message">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<property name="labelAlignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="formAlignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="verticalSpacing">
<number>3</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Date</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="ReadOnlyLineEdit" name="lineEdit_date"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Author</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="ReadOnlyLineEdit" name="lineEdit_author"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Mail</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="ReadOnlyLineEdit" name="lineEdit_mail"/>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_user_avatar">
<property name="minimumSize">
<size>
<width>64</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>64</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QFrame" name="frame_sign">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>2</number>
</property>
<item>
<widget class="QLabel" name="label_signature_icon">
<property name="minimumSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="pixmap">
<pixmap resource="resources/resources.qrc">:/image/signature-good.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_10">
<property name="text">
<string>GPG Sign</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<layout class="QFormLayout" name="formLayout_2">
<property name="labelAlignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="formAlignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="verticalSpacing">
<number>3</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>ID</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="ReadOnlyLineEdit" name="lineEdit_sign_id"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="ReadOnlyLineEdit" name="lineEdit_sign_name"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Mail</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="ReadOnlyLineEdit" name="lineEdit_sign_mail"/>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_sign_avatar">
<property name="minimumSize">
<size>
<width>64</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Commit ID</string>
</property>
</widget>
</item>
<item>
<widget class="ReadOnlyLineEdit" name="lineEdit_commit_id"/>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Parent IDs</string>
</property>
</widget>
</item>
<item>
<widget class="ReadOnlyPlainTextEdit" name="plainTextEdit_parent_ids">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="pushButton_details">
<property name="text">
<string>Files...</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton_explorer">
<property name="text">
<string>Explorer</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton_checkout">
<property name="text">
<string>Checkout</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_jump">
<property name="text">
<string>Jump</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton_close">
<property name="text">
<string>Close</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ReadOnlyLineEdit</class>
<extends>QLineEdit</extends>
<header>ReadOnlyLineEdit.h</header>
</customwidget>
<customwidget>
<class>ReadOnlyPlainTextEdit</class>
<extends>QPlainTextEdit</extends>
<header>ReadOnlyPlainTextEdit.h</header>
</customwidget>
</customwidgets>
<tabstops>
- <tabstop>lineEdit_description</tabstop>
+ <tabstop>lineEdit_message</tabstop>
<tabstop>lineEdit_date</tabstop>
<tabstop>lineEdit_author</tabstop>
<tabstop>lineEdit_mail</tabstop>
<tabstop>lineEdit_commit_id</tabstop>
<tabstop>plainTextEdit_parent_ids</tabstop>
<tabstop>pushButton_checkout</tabstop>
<tabstop>pushButton_close</tabstop>
</tabstops>
<resources>
<include location="resources/resources.qrc"/>
</resources>
<connections>
<connection>
<sender>pushButton_close</sender>
<signal>clicked()</signal>
<receiver>CommitPropertyDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>368</x>
<y>347</y>
</hint>
<hint type="destinationlabel">
<x>367</x>
<y>394</y>
</hint>
</hints>
</connection>
</connections>
</ui>
diff --git a/src/FileHistoryWindow.cpp b/src/FileHistoryWindow.cpp
index c141c03..c153e91 100644
--- a/src/FileHistoryWindow.cpp
+++ b/src/FileHistoryWindow.cpp
@@ -1,251 +1,251 @@
#include "FileHistoryWindow.h"
#include "BasicMainWindow.h"
#include "ui_FileHistoryWindow.h"
#include "common/misc.h"
#include "GitDiff.h"
#include "common/joinpath.h"
#include "FileDiffWidget.h"
#include "MyTableWidgetDelegate.h"
#include <QMenu>
#include <QPainter>
#include <QStyledItemDelegate>
#include <QThread>
struct FileHistoryWindow::Private {
GitPtr g;
QString path;
Git::CommitItemList commit_item_list;
FileDiffWidget::DiffData diff_data;
FileDiffWidget::DrawData draw_data;
};
FileDiffWidget::DiffData *FileHistoryWindow::diffdata()
{
return &m->diff_data;
}
const FileDiffWidget::DiffData *FileHistoryWindow::diffdata() const
{
return &m->diff_data;
}
FileDiffWidget::DrawData *FileHistoryWindow::drawdata()
{
return &m->draw_data;
}
const FileDiffWidget::DrawData *FileHistoryWindow::drawdata() const
{
return &m->draw_data;
}
int FileHistoryWindow::totalTextLines() const
{
return diffdata()->left->lines.size();
}
int FileHistoryWindow::fileviewScrollPos() const
{
return drawdata()->v_scroll_pos;
}
FileHistoryWindow::FileHistoryWindow(BasicMainWindow *parent)
: QDialog(parent)
, ui(new Ui::FileHistoryWindow)
, m(new Private)
{
ui->setupUi(this);
ui->tableWidget_log->setItemDelegate(new MyTableWidgetDelegate(this));
Qt::WindowFlags flags = windowFlags();
flags &= ~Qt::WindowContextHelpButtonHint;
flags |= Qt::WindowMaximizeButtonHint;
setWindowFlags(flags);
ui->splitter->setSizes({100, 200});
ui->widget_diff_view->bind(mainwindow());
// connect(ui->widget_diff_view, &FileDiffWidget::moveNextItem, this, &FileHistoryWindow::onMoveNextItem);
// connect(ui->widget_diff_view, &FileDiffWidget::movePreviousItem, this, &FileHistoryWindow::onMovePreviousItem);
}
FileHistoryWindow::~FileHistoryWindow()
{
delete m;
delete ui;
}
BasicMainWindow *FileHistoryWindow::mainwindow()
{
return qobject_cast<BasicMainWindow *>(parent());
}
void FileHistoryWindow::prepare(GitPtr g, QString const &path)
{
Q_ASSERT(g);
Q_ASSERT(g->isValidWorkingCopy());
this->m->g = g;
this->m->path = path;
QString reponame = mainwindow()->currentRepositoryName();
QString brname = mainwindow()->currentBranch().name;
QString text = "%1 (%2)";
text = text.arg(reponame).arg(brname);
ui->label_repo->setText(text);
ui->label_path->setText(path);
{
OverrideWaitCursor;
m->commit_item_list = m->g->log_all(m->path, mainwindow()->limitLogCount());
}
collectFileHistory();
updateDiffView();
}
void FileHistoryWindow::collectFileHistory()
{
QStringList cols = {
tr("Commit"),
tr("Date"),
tr("Author"),
- tr("Description"),
+ 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];
QTableWidgetItem *item = new QTableWidgetItem(text);
ui->tableWidget_log->setHorizontalHeaderItem(i, item);
}
int count = m->commit_item_list.size();
ui->tableWidget_log->setRowCount(count);
for (int row = 0; row < count; row++) {
Git::CommitItem const &commit = m->commit_item_list[row];
int col = 0;
auto AddColumn = [&](QString const &text, QString const &tooltip){
QTableWidgetItem *item = new QTableWidgetItem(text);
item->setToolTip(tooltip);
ui->tableWidget_log->setItem(row, col, item);
col++;
};
QString commit_id = mainwindow()->abbrevCommitID(commit);
QString datetime = misc::makeDateTimeString(commit.commit_date);
AddColumn(commit_id, QString());
AddColumn(datetime, QString());
AddColumn(commit.author, QString());
AddColumn(commit.message, commit.message);
ui->tableWidget_log->setRowHeight(row, 24);
}
ui->tableWidget_log->resizeColumnsToContents();
ui->tableWidget_log->horizontalHeader()->setStretchLastSection(false);
ui->tableWidget_log->horizontalHeader()->setStretchLastSection(true);
ui->tableWidget_log->setFocus();
ui->tableWidget_log->setCurrentCell(0, 0);
}
class FindFileIdThread : public QThread {
private:
BasicMainWindow *mainwindow;
GitPtr g;
QString commit_id;
QString file;
public:
QString result;
FindFileIdThread(BasicMainWindow *BasicMainWindow, GitPtr g, QString const &commit_id, QString const &file)
{
this->mainwindow = BasicMainWindow;
this->g = g;
this->commit_id = commit_id;
this->file = file;
}
protected:
void run() override
{
result = mainwindow->findFileID(g, commit_id, file);
}
};
void FileHistoryWindow::updateDiffView()
{
Q_ASSERT(m->g);
Q_ASSERT(m->g->isValidWorkingCopy());
ui->widget_diff_view->clearDiffView();
int row = ui->tableWidget_log->currentRow();
if (row >= 0 && row + 1 < (int)m->commit_item_list.size()) {
Git::CommitItem const &commit_left = m->commit_item_list[row + 1]; // older
Git::CommitItem const &commit_right = m->commit_item_list[row]; // newer
FindFileIdThread left_thread(mainwindow(), m->g->dup(), commit_left.commit_id, m->path);
FindFileIdThread right_thread(mainwindow(), m->g->dup(), commit_right.commit_id, m->path);
left_thread.start();
right_thread.start();
left_thread.wait();
right_thread.wait();
QString id_left = left_thread.result;
QString id_right = right_thread.result;
ui->widget_diff_view->updateDiffView(id_left, id_right, m->path);
} else if (row >= 0 && row < (int)m->commit_item_list.size()) {
Git::CommitItem const &commit = m->commit_item_list[row]; // newer
QString id = mainwindow()->findFileID(m->g, commit.commit_id, m->path);
Git::Diff diff(id, m->path, QString());
ui->widget_diff_view->updateDiffView(diff, false);
}
}
void FileHistoryWindow::on_tableWidget_log_currentItemChanged(QTableWidgetItem * /*current*/, QTableWidgetItem * /*previous*/)
{
updateDiffView();
}
//void FileHistoryWindow::onMoveNextItem()
//{
// int row = ui->tableWidget_log->currentRow();
// int count = ui->tableWidget_log->rowCount();
// if (row + 1 < count) {
// ui->tableWidget_log->setCurrentCell(row + 1, 0, QItemSelectionModel::ClearAndSelect);
// }
//}
//void FileHistoryWindow::onMovePreviousItem()
//{
// int row = ui->tableWidget_log->currentRow();
// if (row > 0) {
// ui->tableWidget_log->setCurrentCell(row - 1, 0, QItemSelectionModel::ClearAndSelect);
// }
//}
void FileHistoryWindow::on_tableWidget_log_customContextMenuRequested(const QPoint &pos)
{
(void)pos;
Git::CommitItem const *commit = nullptr;
int row = ui->tableWidget_log->currentRow();
if (row >= 0 && row < (int)m->commit_item_list.size()) {
commit = &m->commit_item_list[row];
}
if (!commit) return;
QMenu menu;
QAction *a_property = mainwindow()->addMenuActionProperty(&menu);
QAction *a = menu.exec(QCursor::pos() + QPoint(8, -8));
if (a) {
if (a == a_property) {
mainwindow()->execCommitPropertyDialog(this, commit);
return;
}
}
}
diff --git a/src/Git.cpp b/src/Git.cpp
index dcfcf76..732b410 100644
--- a/src/Git.cpp
+++ b/src/Git.cpp
@@ -1,1453 +1,1453 @@
#include "Git.h"
#include <QDateTime>
#include <QDebug>
#include <QDir>
#include <QDirIterator>
#include <QProcess>
#include <QThread>
#include <QTimer>
#include <set>
#include "common/joinpath.h"
#include "common/misc.h"
#include "GitObjectManager.h"
#include "MyProcess.h"
#define DEBUGLOG 0
using callback_t = Git::callback_t;
struct Git::Private {
QString git_command;
std::vector<char> result;
QString error_message;
int process_exit_code = 0;
QString working_repo_dir;
callback_t fn_log_writer_callback = nullptr;
void *callback_cookie = nullptr;
};
Git::Git()
: m(new Private)
{
}
Git::Git(const Context &cx, QString const &repodir)
: m(new Private)
{
setGitCommand(cx.git_command);
setWorkingRepositoryDir(repodir);
}
Git::~Git()
{
delete m;
}
void Git::setLogCallback(callback_t func, void *cookie)
{
m->fn_log_writer_callback = func;
m->callback_cookie = cookie;
}
void Git::setWorkingRepositoryDir(QString const &repo)
{
m->working_repo_dir = repo;
}
QString const &Git::workingRepositoryDir() const
{
return m->working_repo_dir;
}
bool Git::isValidID(QString const &id)
{
int zero = 0;
int n = id.size();
if (n >= 4 && n <= GIT_ID_LENGTH) {
ushort const *p = id.utf16();
for (int i = 0; i < n; i++) {
uchar c = p[i];
if (c == '0') {
zero++;
} else if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')) {
// ok
} else {
return false;
}
}
if (zero == GIT_ID_LENGTH) { // 全部 0 の時は false を返す
return false;
}
return true; // ok
}
return false;
}
QByteArray Git::toQByteArray() const
{
if (m->result.empty()) return QByteArray();
return QByteArray(&m->result[0], m->result.size());
}
QString Git::resultText() const
{
return QString::fromUtf8(toQByteArray());
}
void Git::setGitCommand(QString const &path)
{
m->git_command = path;
}
QString Git::gitCommand() const
{
Q_ASSERT(m);
return m->git_command;
}
void Git::clearResult()
{
m->result.clear();
m->process_exit_code = 0;
m->error_message = QString();
}
QString Git::errorMessage() const
{
return m->error_message;
}
int Git::getProcessExitCode() const
{
return m->process_exit_code;
}
bool Git::chdirexec(std::function<bool()> const &fn)
{
bool ok = false;
QString cwd = QDir::currentPath();
QString dir = workingRepositoryDir();
if (isValidWorkingCopy(dir) && QDir::setCurrent(dir)) {
ok = fn();
QDir::setCurrent(cwd);
}
return ok;
}
bool Git::git(QString const &arg, bool chdir, bool errout, AbstractPtyProcess *pty)
{
QFileInfo info(gitCommand());
if (!info.isExecutable()) {
qDebug() << "Invalid git command: " << gitCommand();
return false;
}
#if DEBUGLOG
qDebug() << "exec: git " << arg;
QTime timer;
timer.start();
#endif
clearResult();
auto DoIt = [&](){
QString cmd = QString("\"%1\" --no-pager ").arg(gitCommand());
cmd += arg;
if (m->fn_log_writer_callback) {
QByteArray ba;
ba.append("> git ");
ba.append(arg);
ba.append('\n');
m->fn_log_writer_callback(m->callback_cookie, ba.data(), (int)ba.size());
}
if (pty) {
pty->start(cmd, pty->userVariant());
m->process_exit_code = 0; // バックグラウンドで実行を継続するけど、とりあえず成功したことにしておく
} else {
Process proc;
proc.start(cmd, false);
m->process_exit_code = proc.wait();
if (errout) {
m->result = proc.errbytes;
} else {
m->result = proc.outbytes;
}
m->error_message = proc.errstring();
}
return m->process_exit_code == 0;
};
bool ok = false;
if (chdir) {
if (pty) {
pty->setChangeDir(workingRepositoryDir());
ok = DoIt();
} else {
ok = chdirexec(DoIt);
}
} else {
ok = DoIt();
}
#if DEBUGLOG
qDebug() << timer.elapsed() << "ms";
#endif
return ok;
}
GitPtr Git::dup() const
{
Git *p = new Git();
p->m->git_command = m->git_command;
p->m->working_repo_dir = m->working_repo_dir;
p->m->fn_log_writer_callback = m->fn_log_writer_callback;
p->m->callback_cookie = m->callback_cookie;
return GitPtr(p);
}
bool Git::isValidWorkingCopy(QString const &dir)
{
return QFileInfo(dir).isDir() && QDir(dir / ".git").exists();
}
bool Git::isValidWorkingCopy() const
{
return isValidWorkingCopy(workingRepositoryDir());
}
QString Git::version()
{
git("--version", false);
return resultText().trimmed();
}
bool Git::init()
{
bool ok = false;
QDir cwd = QDir::current();
QString dir = workingRepositoryDir();
if (QDir::setCurrent(dir)) {
QString gitdir = dir / ".git";
if (!QFileInfo(gitdir).isDir()) {
git("init", false);
if (QFileInfo(gitdir).isDir()) {
ok = true;
}
}
QDir::setCurrent(cwd.path());
}
return ok;
}
QString Git::rev_parse(QString const &name)
{
QString cmd = "rev-parse %1";
cmd = cmd.arg(name);
if (git(cmd)) {
return resultText().trimmed();
}
return QString();
}
QStringList Git::refs()
{
QStringList lines;
{
QString path = workingRepositoryDir() / ".git/packed-refs";
QFile file(path);
if (file.open(QFile::ReadOnly)) {
while (!file.atEnd()) {
QByteArray ba = file.readLine();
QString line = QString::fromLatin1(ba).trimmed();
if (line.indexOf(" refs/") > 0) {
lines.push_back(line);
}
}
}
}
{
QString refspath = workingRepositoryDir() / ".git/refs/tags";
QDirIterator it(refspath);
while (it.hasNext()) {
it.next();
QString path = it.filePath();
if (QFileInfo(path).isFile()) {
QString name = it.fileName();
QFile file(path);
if (file.open(QFile::ReadOnly)) {
QByteArray ba = file.readAll();
QString id = QString::fromLatin1(ba).trimmed();
if (Git::isValidID(id)) {
QString line = id + " refs/tags/" + name;
lines.push_back(line);
}
}
}
}
}
return lines;
}
QList<Git::Tag> Git::tags()
{
auto MidCmp = [](QString const &line, int i, char const *ptr){
ushort const *p = line.utf16();
for (int j = 0; ptr[j]; j++) {
if (p[i + j] != ptr[j]) return false;
}
return true;
};
QList<Git::Tag> list;
QStringList lines = refs();
for (QString const &line : lines) {
Tag tag;
if (line.isEmpty()) continue;
int i = line.indexOf(' ');
if (i == GIT_ID_LENGTH) {
tag.id = line.mid(0, i);
if (MidCmp(line, i, " refs/tags/")) {
tag.name = line.mid(i + 11).trimmed();
if (tag.name.isEmpty()) continue;
list.push_back(tag);
}
}
}
return list;
}
bool Git::tag(QString const &name, QString const &id)
{
QString cmd = "tag \"%1\" %2";
cmd = cmd.arg(name).arg(id);
return git(cmd);
}
void Git::delete_tag(QString const &name, bool remote)
{
QString cmd = "tag --delete \"%1\"";
cmd = cmd.arg(name);
git(cmd);
if (remote) {
QString cmd = "push --delete origin \"%1\"";
cmd = cmd.arg(name);
git(cmd);
}
}
QString Git::diff(QString const &old_id, QString const &new_id)
{
QString cmd = "diff --full-index -a %1 %2";
cmd = cmd.arg(old_id).arg(new_id);
git(cmd);
return resultText();
}
QString Git::diff_file(QString const &old_path, QString const &new_path)
{
QString cmd = "diff --full-index -a -- %1 %2";
cmd = cmd.arg(old_path).arg(new_path);
git(cmd);
return resultText();
}
QList<Git::DiffRaw> Git::diff_raw(QString const &old_id, QString const &new_id)
{
QList<DiffRaw> list;
QString cmd = "diff --raw --abbrev=%1 %2 %3";
cmd = cmd.arg(GIT_ID_LENGTH).arg(old_id).arg(new_id);
git(cmd);
QString text = resultText();
QStringList lines = text.split('\n', QString::SkipEmptyParts);
for (QString const &line : lines) { // raw format: e.g. ":100644 100644 08bc10d... 18f0501... M src/MainWindow.cpp"
DiffRaw item;
int colon = line.indexOf(':');
int tab = line.indexOf('\t');
if (colon >= 0 && colon < tab) {
QStringList header = line.mid(colon + 1, tab - colon - 1).split(' ', QString::SkipEmptyParts); // コロンとタブの間の文字列を空白で分割
if (header.size() >= 5) {
QStringList files = line.mid(tab + 1).split('\t', QString::SkipEmptyParts); // タブより後ろはファイルパス
if (!files.empty()) {
for (QString &file : files) {
file = Git::trimPath(file);
}
item.a.id = header[2];
item.b.id = header[3];
item.a.mode = header[0];
item.b.mode = header[1];
item.state = header[4];
item.files = files;
list.push_back(item);
}
}
}
}
return list;
}
QString Git::diff_to_file(QString const &old_id, QString const &path)
{
#if 1
QString cmd = "diff --full-index -a %1 -- \"%2\"";
cmd = cmd.arg(old_id).arg(path);
git(cmd);
return resultText();
#else
return diff_(old_id, new_id);
#endif
}
QString Git::getCurrentBranchName()
{
if (isValidWorkingCopy()) {
git("rev-parse --abbrev-ref HEAD");
QString s = resultText().trimmed();
if (!s.isEmpty() && !s.startsWith("fatal:") && s.indexOf(' ') < 0) {
return s;
}
}
return QString();
}
QStringList Git::getUntrackedFiles()
{
QStringList files;
Git::FileStatusList stats = status();
for (FileStatus const &s : stats) {
if (s.code() == FileStatusCode::Untracked) {
files.push_back(s.path1());
}
}
return files;
}
void Git::parseAheadBehind(QString const &s, Branch *b)
{
ushort const *begin = s.utf16();
ushort const *end = begin + s.size();
ushort const *ptr = begin;
auto NCompare = [](ushort const *a, char const *b, size_t n){
for (size_t i = 0; i < n; i++) {
if (*a < (unsigned char)*b) return false;
if (*a > (unsigned char)*b) return false;
if (!*a) break;
a++;
b++;
}
return true;
};
auto SkipSpace = [&](){
while (ptr < end && QChar(*ptr).isSpace()) {
ptr++;
}
};
auto GetNumber = [&](){
int n = 0;
while (ptr < end && QChar(*ptr).isDigit()) {
n = n * 10 + (*ptr - '0');
ptr++;
}
return n;
};
if (*ptr == '[') {
ptr++;
ushort const *e = nullptr;
int n;
for (n = 0; ptr + n < end; n++) {
if (ptr[n] == ']') {
e = ptr + n;
break;
}
}
if (e) {
end = e;
while (1) {
if (NCompare(ptr, "ahead ", 6)) {
ptr += 6;
SkipSpace();
b->ahead = GetNumber();
} else if (NCompare(ptr, "behind ", 7)) {
ptr += 7;
SkipSpace();
b->behind = GetNumber();
} else {
ushort c = 0;
if (ptr < end) {
c = *ptr;
}
if (c == 0) break;
ptr++;
}
}
}
}
}
QList<Git::Branch> Git::branches()
{
QList<Branch> branches;
git(QString("branch -vv -a --abbrev=%1").arg(GIT_ID_LENGTH));
QString s = resultText();
#if DEBUGLOG
qDebug() << s;
#endif
QStringList lines = misc::splitLines(s);
for (QString const &line : lines) {
if (line.size() > 2) {
QString name;
QString text = line.mid(2);
int pos = 0;
if (text.startsWith('(')) {
int i, paren = 1;
for (i = 1; i < text.size(); i++) {
if (text[i] == '(') {
paren++;
} else if (text[i] == ')') {
if (paren > 0) {
paren--;
if (paren == 0) {
pos = i;
break;
}
}
}
}
// pos = text.indexOf(')');
if (pos > 1) {
name = text.mid(1, pos - 1);
pos++;
}
} else {
while (pos < text.size() && !QChar::isSpace(text.utf16()[pos])) {
pos++;
}
name = text.mid(0, pos);
}
if (!name.isEmpty()) {
while (pos < text.size() && QChar::isSpace(text.utf16()[pos])) {
pos++;
}
text = text.mid(pos);
Branch b;
if (name.startsWith("HEAD detached at ")) {
b.flags |= Branch::HeadDetachedAt;
name = name.mid(17);
}
if (name.startsWith("HEAD detached from ")) {
b.flags |= Branch::HeadDetachedFrom;
name = name.mid(19);
}
if (name.startsWith("remotes/")) {
name = name.mid(8);
int i = name.indexOf('/');
if (i > 0) {
b.remote = name.mid(0, i);
name = name.mid(i + 1);
}
}
b.name = name;
if (text.startsWith("-> ")) {
b.id = ">" + text.mid(3);
} else {
int i = text.indexOf(' ');
if (i == GIT_ID_LENGTH) {
b.id = text.mid(0, GIT_ID_LENGTH);
}
while (i < text.size() && QChar::isSpace(text.utf16()[i])) {
i++;
}
text = text.mid(i);
parseAheadBehind(text, &b);
}
if (line.startsWith('*')) {
b.flags |= Branch::Current;
}
branches.push_back(b);
}
}
}
for (int i = 0; i < branches.size(); i++) {
Branch *b = &branches[i];
if (b->id.startsWith('>')) {
QString name = b->id.mid(1);
for (int j = 0; j < branches.size(); j++) {
if (j != i) {
if (branches[j].name == name) {
branches[i].id = branches[j].id;
break;
}
}
}
}
}
return branches;
}
Git::CommitItemList Git::log_all(QString const &id, int maxcount)
{
CommitItemList items;
QString text;
QString cmd = "log --pretty=format:\"commit:%H#gpg:%G?#key:%GK#parent:%P#author:%an#mail:%ae#date:%ci##%s\" --all -%1 %2";
cmd = cmd.arg(maxcount).arg(id);
git(cmd);
if (getProcessExitCode() == 0) {
text = resultText().trimmed();
QStringList lines = misc::splitLines(text);
for (QString const &line : lines) {
int i = line.indexOf("##");
if (i > 0) {
Git::CommitItem item;
QString signed_key;
item.message = line.mid(i + 2);
QStringList atts = line.mid(0, i).split('#');
for (QString const &s : atts) {
int j = s.indexOf(':');
if (j > 0) {
QString key = s.mid(0, j);
QString val = s.mid(j + 1);
if (key == "commit") {
item.commit_id = val;
} else if (key == "gpg") {
item.signature = *val.utf16();
} else if (key == "key") {
signed_key = val;
} else if (key == "parent") {
item.parent_ids = val.split(' ', QString::SkipEmptyParts);
} else if (key == "author") {
item.author = val;
} else if (key == "mail") {
item.email = val;
} else if (key == "date") {
item.commit_date = QDateTime::fromString(val, Qt::ISODate).toLocalTime();
} else if (key == "debug") {
}
}
}
if (!signed_key.isEmpty()) {
ushort const *begin = signed_key.utf16();
int n = signed_key.size();
for (int i = 0; i + 1 < n; i += 2) {
ushort c = begin[i];
ushort d = begin[i + 1];
if (c < 0x80 && isxdigit(c) && isxdigit(d)) {
char tmp[3];
tmp[0] = c;
tmp[1] = d;
tmp[2] = 0;
int v = strtol(tmp, nullptr, 16);
item.fingerprint.push_back(v);
} else {
break;
}
}
}
items.push_back(item);
}
}
}
return items;
}
Git::CommitItemList Git::log(int maxcount)
{
return log_all(QString(), maxcount);
}
bool Git::queryCommit(QString const &id, CommitItem *out)
{
if (objectType(id) == "commit") {
out->commit_id = id;
QByteArray ba;
if (cat_file(id, &ba)) {
QStringList lines = misc::splitLines(ba, [](char const *p, size_t n){
return QString::fromUtf8(p, (int)n);
});
while (!lines.empty() && lines[lines.size() - 1].isEmpty()) {
lines.pop_back();
}
int i;
for (i = 0; i < lines.size(); i++) {
QString const &line = lines[i];
if (line.isEmpty()) {
i++;
for (; i < lines.size(); i++) {
QString const &line = lines[i];
if (!out->message.isEmpty()) {
out->message.append('\n');
}
out->message.append(line);
}
break;
}
if (line.startsWith("parent ")) {
out->parent_ids.push_back(line.mid(7));
} else if (line.startsWith("author ")) {
QStringList arr = misc::splitWords(line);
int n = arr.size();
if (n > 4) {
n -= 2;
out->commit_date = QDateTime::fromTime_t(atol(arr[n].toStdString().c_str()));
n--;
out->email = arr[n];
if (out->email.startsWith('<') && out->email.endsWith('>')) {
int n = out->email.size();
out->email = out->email.mid(1, n - 2);
}
for (int i = 1; i < n; i++) {
if (!out->author.isEmpty()) {
out->author += ' ';
}
out->author += arr[i];
}
}
}
}
}
return true;
}
return false;
}
Git::CloneData Git::preclone(QString const &url, QString const &path)
{
CloneData d;
d.url = url;
if (path.endsWith('/') || path.endsWith('\\')) {
auto GitBaseName = [](QString location){
int i = location.lastIndexOf('/');
int j = location.lastIndexOf('\\');
if (i < j) i = j;
i = i < 0 ? 0 : (i + 1);
j = location.size();
if (location.endsWith(".git")) {
j -= 4;
}
if (i < j) {
location = location.mid(i, j - i);
}
return location;
};
d.basedir = path;
d.subdir = GitBaseName(url);
} else {
QFileInfo info(path);
d.basedir = info.dir().path();
d.subdir = info.fileName();
}
return d;
}
bool Git::clone(CloneData const &data, AbstractPtyProcess *pty)
{
QString clone_to = data.basedir / data.subdir;
m->working_repo_dir = misc::normalizePathSeparator(clone_to);
bool ok = false;
QDir cwd = QDir::current();
auto DoIt = [&](){
QString cmd = "clone --progress \"%1\" \"%2\"";
cmd = cmd.arg(data.url).arg(data.subdir);
ok = git(cmd, false, true, pty);
};
if (pty) {
pty->setChangeDir(data.basedir);
DoIt();
} else {
if (QDir::setCurrent(data.basedir)) {
DoIt();
QDir::setCurrent(cwd.path());
}
}
return ok;
}
QString Git::encodeQuotedText(QString const &str)
{
std::vector<ushort> vec;
ushort const *begin = str.utf16();
ushort const *end = begin + str.size();
ushort const *ptr = begin;
vec.push_back('\"');
while (ptr < end) {
ushort c = *ptr;
ptr++;
if (c == '\"') { // triple quotes
vec.push_back(c);
vec.push_back(c);
vec.push_back(c);
} else {
vec.push_back(c);
}
}
vec.push_back('\"');
return QString::fromUtf16(&vec[0], vec.size());
}
bool Git::commit_(QString const &msg, bool amend, bool sign, AbstractPtyProcess *pty)
{
QString cmd = "commit";
if (amend) {
cmd += " --amend";
}
if (sign) {
cmd += " -S";
}
QString text = msg.trimmed();
if (text.isEmpty()) {
text = "no message";
}
text = encodeQuotedText(text);
cmd += QString(" -m %1").arg(text);
return git(cmd, true, false, pty);
}
bool Git::commit(QString const &text, bool sign, AbstractPtyProcess *pty)
{
return commit_(text, false, sign, pty);
}
bool Git::commit_amend_m(QString const &text, bool sign, AbstractPtyProcess *pty)
{
return commit_(text, true, sign, pty);
}
bool Git::revert(QString const &id)
{
QString cmd = "revert %1";
cmd = cmd.arg(id);
return git(cmd);
}
void Git::push_u(QString const &remote, QString const &branch, AbstractPtyProcess *pty)
{
if (remote.indexOf('\"') >= 0 || branch.indexOf('\"') >= 0) {
return;
}
QString cmd = "push -u \"%1\" \"%2\"";
git(cmd.arg(remote).arg(branch), true, false, pty);
}
bool Git::push_(bool tags, AbstractPtyProcess *pty)
{
QString cmd = "push";
if (tags) {
cmd += " --tags";
}
return git(cmd, true, false, pty);
}
bool Git::push(bool tags, AbstractPtyProcess *pty)
{
return push_(tags, pty);
}
Git::FileStatusList Git::status_()
{
FileStatusList files;
if (git("status -s -u --porcelain")) {
QString text = resultText();
QStringList lines = misc::splitLines(text);
for (QString const &line : lines) {
if (line.size() > 3) {
FileStatus s(line);
if (s.code() != FileStatusCode::Unknown) {
files.push_back(s);
}
}
}
}
return files;
}
Git::FileStatusList Git::status()
{
return status_();
}
QString Git::objectType(QString const &id)
{
git("cat-file -t " + id);
return resultText().trimmed();
}
QByteArray Git::cat_file_(QString const &id)
{
git("cat-file -p " + id);
return toQByteArray();
}
bool Git::cat_file(QString const &id, QByteArray *out)
{
if (isValidID(id)) {
*out = cat_file_(id);
return true;
}
return false;
}
void Git::resetFile(QString const &path)
{
git("checkout -- " + path);
}
void Git::resetAllFiles()
{
git("reset --hard HEAD");
}
void Git::removeFile(QString const &path)
{
git("rm " + path);
}
void Git::stage(QString const &path)
{
git("add " + path);
}
void Git::stage(QStringList const &paths)
{
QString cmd = "add";
for (QString const &path : paths) {
cmd += ' ';
cmd += '\"';
cmd += path;
cmd += '\"';
}
git(cmd);
}
void Git::unstage(QString const &path)
{
QString cmd = "reset HEAD \"%1\"";
git(cmd.arg(path));
}
void Git::unstage(QStringList const &paths)
{
QString cmd = "reset HEAD";
for (QString const &path : paths) {
cmd += " \"";
cmd += path;
cmd += '\"';
}
git(cmd);
}
void Git::pull(AbstractPtyProcess *pty)
{
git("pull", true, false, pty);
}
void Git::fetch(AbstractPtyProcess *pty, bool prune)
{
QString cmd = "fetch";
if (prune) {
cmd += " --prune";
}
git(cmd, true, false, pty);
}
QStringList Git::make_branch_list_()
{
QStringList list;
QStringList l = misc::splitLines(resultText());
for (QString const &s : l) {
if (s.startsWith("* ")) list.push_back(s.mid(2));
if (s.startsWith(" ")) list.push_back(s.mid(2));
}
return list;
}
void Git::createBranch(QString const &name)
{
git("branch " + name);
}
void Git::checkoutBranch(QString const &name)
{
git("checkout " + name);
}
void Git::cherrypick(QString const &name)
{
git("cherry-pick " + name);
}
void Git::mergeBranch(QString const &name)
{
git("merge " + name);
}
void Git::rebaseBranch(QString const &name)
{
git("rebase " + name);
}
QStringList Git::getRemotes()
{
QStringList ret;
git("remote");
QStringList lines = misc::splitLines(resultText());
for (QString const &line: lines) {
if (!line.isEmpty()) {
ret.push_back(line);
}
}
return ret;
}
Git::User Git::getUser(Source purpose)
{
User user;
bool global = purpose == Git::Source::Global;
bool local = purpose == Git::Source::Local;
QString arg1;
if (global) arg1 = "--global";
if (local) arg1 = "--local";
bool chdir = !global;
if (git(QString("config %1 user.name").arg(arg1), chdir)) {
user.name = resultText().trimmed();
}
if (git(QString("config %1 user.email").arg(arg1), chdir)) {
user.email = resultText().trimmed();
}
return user;
}
void Git::setUser(const User &user, bool global)
{
bool chdir = !global;
git(QString("config %1 user.name %2").arg(global ? "--global" : "").arg(encodeQuotedText(user.name)), chdir);
git(QString("config %1 user.email %2").arg(global ? "--global" : "").arg(encodeQuotedText(user.email)), chdir);
}
bool Git::reset_head1()
{
return git("reset HEAD~1");
}
bool Git::reset_hard()
{
return git("reset --hard");
}
bool Git::clean_df()
{
return git("clean -df");
}
bool Git::stash()
{
return git("stash");
}
bool Git::stash_apply()
{
return git("stash apply");
}
bool Git::stash_drop()
{
return git("stash drop");
}
bool Git::rm_cached(QString const &file)
{
QString cmd = "rm --cached \"%1\"";
return git(cmd.arg(file));
}
void Git::getRemoteURLs(QList<Remote> *out)
{
out->clear();
git("remote -v");
QStringList lines = misc::splitLines(resultText());
for (QString const &line : lines) {
int i = line.indexOf('\t');
int j = line.indexOf(" (");
if (i > 0 && i < j) {
Remote r;
r.name = line.mid(0, i);
r.url = line.mid(i + 1, j - i - 1);
r.purpose = line.mid(j + 1);
if (r.purpose.startsWith('(') && r.purpose.endsWith(')')) {
r.purpose = r.purpose.mid(1, r.purpose.size() - 2);
}
out->push_back(r);
}
}
}
void Git::setRemoteURL(QString const &name, QString const &url)
{
QString cmd = "remote set-url %1 %2";
cmd = cmd.arg(encodeQuotedText(name)).arg(encodeQuotedText(url));
git(cmd);
}
void Git::addRemoteURL(QString const &name, QString const &url)
{
QString cmd = "remote add \"%1\" \"%2\"";
cmd = cmd.arg(encodeQuotedText(name)).arg(encodeQuotedText(url));
git(cmd);
}
void Git::removeRemote(QString const &name)
{
QString cmd = "remote remove %1";
cmd = cmd.arg(encodeQuotedText(name));
git(cmd);
}
bool Git::reflog(ReflogItemList *out, int maxcount)
{
out->clear();
QString cmd = "reflog --no-abbrev --raw -n %1";
cmd = cmd.arg(maxcount);
if (!git(cmd)) return false;
QByteArray ba = toQByteArray();
if (!ba.isEmpty()) {
ReflogItem item;
char const *begin = ba.data();
char const *end = begin + ba.size();
char const *left = begin;
char const *ptr = begin;
while (1) {
int c = 0;
if (ptr < end) {
c = *ptr;
}
if (c == '\r' || c == '\n' || c == 0) {
int d = 0;
QString line = QString::fromUtf8(left, ptr - left);
if (left < ptr) {
d = *left & 0xff;
}
if (d == ':') {
// ex. ":100644 100644 bb603836fb597cca994309a1f0a52251d6b20314 d6b9798854debee375bb419f0f2ed9c8437f1932 M\tsrc/MainWindow.cpp"
int tab = line.indexOf('\t');
if (tab > 1) {
QString tmp = line.mid(1, tab - 1);
QString path = line.mid(tab + 1);
QStringList cols = misc::splitWords(tmp);
if (!path.isEmpty() && cols.size() == 5) {
ReflogItem::File file;
file.atts_a = cols[0];
file.atts_b = cols[1];
file.id_a = cols[2];
file.id_b = cols[3];
file.type = cols[4];
file.path = path;
item.files.push_back(file);
}
}
} else {
bool start = isxdigit(d);
if (start || c == 0) {
if (!item.id.isEmpty()) {
out->push_back(item);
}
}
if (start) {
- // ex. "0a2a8b6b66f48bcbf985d8a2afcd14ff41676c16 HEAD@{188}: commit: comment text"
+ // ex. "0a2a8b6b66f48bcbf985d8a2afcd14ff41676c16 HEAD@{188}: commit: message"
item = ReflogItem();
int i = line.indexOf(": ");
if (i > 0) {
int j = line.indexOf(": ", i + 2);
if (j > 2) {
item.head = line.mid(0, i);
item.command = line.mid(i + 2, j - i - 2);
- item.comment= line.mid(j + 2);
+ item.message = line.mid(j + 2);
if (item.head.size() > GIT_ID_LENGTH) {
item.id = item.head.mid(0, GIT_ID_LENGTH);
item.head = item.head.mid(GIT_ID_LENGTH + 1);
}
}
}
}
}
if (c == 0) break;
ptr++;
left = ptr;
} else {
ptr++;
}
}
}
return true;
}
// Git::FileStatus
QString Git::trimPath(QString const &s)
{
ushort const *begin = s.utf16();
ushort const *end = begin + s.size();
ushort const *left = begin;
ushort const *right = end;
while (left < right && QChar(*left).isSpace()) left++;
while (left < right && QChar(right[-1]).isSpace()) right--;
if (left + 1 < right && *left == '\"' && right[-1] == '\"') { // if quoted ?
left++;
right--;
QByteArray ba;
ushort const *ptr = left;
while (ptr < right) {
ushort c = *ptr;
ptr++;
if (c == '\\') {
c = 0;
while (ptr < right && QChar(*ptr).isDigit()) { // decode \oct
c = c * 8 + (*ptr - '0');
ptr++;
}
}
ba.push_back(c);
}
return QString::fromUtf8(ba);
}
if (left == begin && right == end) return s;
return QString::fromUtf16(left, right - left);
}
Git::FileStatusCode Git::FileStatus::parseFileStatusCode(char x, char y)
{
if (x == ' ' && (y == 'M' || y == 'D')) return FileStatusCode::NotUpdated;
if (x == 'M' && (y == 'M' || y == 'D' || y == ' ')) return FileStatusCode::UpdatedInIndex;
if (x == 'A' && (y == 'M' || y == 'D' || y == ' ')) return FileStatusCode::AddedToIndex;
if (x == 'D' && (y == 'M' || y == ' ')) return FileStatusCode::DeletedFromIndex;
if (x == 'R' && (y == 'M' || y == 'D' || y == ' ')) return FileStatusCode::RenamedInIndex;
if (x == 'C' && (y == 'M' || y == 'D' || y == ' ')) return FileStatusCode::CopiedInIndex;
if (x == 'C' && (y == 'M' || y == 'D' || y == ' ')) return FileStatusCode::CopiedInIndex;
if (x == 'D' && y == 'D') return FileStatusCode::Unmerged_BothDeleted;
if (x == 'A' && y == 'U') return FileStatusCode::Unmerged_AddedByUs;
if (x == 'U' && y == 'D') return FileStatusCode::Unmerged_DeletedByThem;
if (x == 'U' && y == 'A') return FileStatusCode::Unmerged_AddedByThem;
if (x == 'D' && y == 'U') return FileStatusCode::Unmerged_DeletedByUs;
if (x == 'A' && y == 'A') return FileStatusCode::Unmerged_BothAdded;
if (x == 'U' && y == 'U') return FileStatusCode::Unmerged_BothModified;
if (x == '?' && y == '?') return FileStatusCode::Untracked;
if (x == '!' && y == '!') return FileStatusCode::Ignored;
return FileStatusCode::Unknown;
}
void Git::FileStatus::parse(QString const &text)
{
data = Data();
if (text.size() > 3) {
ushort const *p = text.utf16();
if (p[2] == ' ') {
data.code_x = p[0];
data.code_y = p[1];
int i = text.indexOf(" -> ", 3);
if (i > 3) {
data.rawpath1 = text.mid(3, i - 3);
data.rawpath2 = text.mid(i + 4);
data.path1 = trimPath(data.rawpath1);
data.path2 = trimPath(data.rawpath2);
} else {
data.rawpath1 = text.mid(3);
data.path1 = trimPath(data.rawpath1);
data.path2 = QString();
}
data.code = parseFileStatusCode(data.code_x, data.code_y);
}
}
}
QByteArray Git::blame(QString const &path)
{
QString cmd = "blame --abbrev=40 \"%1\"";
cmd = cmd.arg(path);
if (git(cmd)) {
return toQByteArray();
}
return QByteArray();
}
QList<Git::RemoteInfo> Git::ls_remote()
{
QList<RemoteInfo> list;
QString cmd = "ls-remote";
if (git(cmd)) {
QStringList lines = misc::splitLines(resultText());
for (QString const &line : lines) {
QStringList words = misc::splitWords(line);
if (words.size() == 2 && isValidID(words[0])) {
RemoteInfo info;
info.commit_id = words[0];
info.name = words[1];
list.push_back(info);
}
}
}
return list;
}
QString Git::signingKey(Source purpose)
{
QString arg1;
if (purpose == Source::Global) arg1 = "--global";
if (purpose == Source::Local) arg1 = "--local";
QString cmd = "config %1 user.signingkey";
cmd = cmd.arg(arg1);
bool chdir = purpose != Source::Global;
if (git(cmd, chdir)) {
return resultText().trimmed();
}
return QString();
}
bool Git::setSigningKey(QString const &id, bool global)
{
for (auto i : id) {
if (!QChar(i).isLetterOrNumber()) return false;
}
QString cmd = "config %1 %2 user.signingkey %3";
cmd = cmd.arg(global ? "--global" : "--local").arg(id.isEmpty() ? "--unset" : "").arg(id);
return git(cmd, !global);
}
Git::SignPolicy Git::signPolicy(Source source)
{
QString arg1;
if (source == Source::Global) arg1 = "--global";
if (source == Source::Local) arg1 = "--local";
QString cmd = "config %1 commit.gpgsign";
cmd = cmd.arg(arg1);
bool chdir = source != Source::Global;
if (git(cmd, chdir)) {
QString t = resultText().trimmed();
if (t == "false") return SignPolicy::False;
if (t == "true") return SignPolicy::True;
}
return SignPolicy::Unset;
}
bool Git::setSignPolicy(Source source, SignPolicy policy)
{
QString arg1;
if (source == Source::Global) arg1 = "--global";
if (source == Source::Local) arg1 = "--local";
QString cmd = "config %1 %2 commit.gpgsign %3";
QString arg2;
QString arg3;
if (policy == SignPolicy::False) {
arg3 = "false";
} else if (policy == SignPolicy::True) {
arg3 = "true";
} else {
arg2 = "--unset";
}
cmd = cmd.arg(arg1).arg(arg2).arg(arg3);
bool chdir = source != Source::Global;
return git(cmd, chdir);
}
bool Git::configGpgProgram(QString const &path, bool global)
{
QString cmd = "config ";
if (global) {
cmd += "--global ";
}
if (path.isEmpty()) {
cmd += "--unset ";
}
cmd += "gpg.program ";
if (!path.isEmpty()) {
cmd += QString("\"%1\"").arg(path);
}
return git(cmd, false);
}
// Diff
void Git::Diff::makeForSingleFile(Git::Diff *diff, QString const &id_a, QString const &id_b, QString const &path, QString const &mode)
{
diff->diff = QString("diff --git a/%1 b/%2").arg(path).arg(path);
diff->index = QString("index %1..%2 %3").arg(id_a).arg(id_b).arg(0);
diff->blob.a_id = id_a;
diff->blob.b_id = id_b;
diff->path = path;
diff->mode = mode;
diff->type = Git::Diff::Type::Create;
}
//
void parseDiff(std::string const &s, Git::Diff const *info, Git::Diff *out)
{
std::vector<std::string> lines;
{
char const *begin = s.c_str();
char const *end = begin + s.size();
misc::splitLines(begin, end, &lines, false);
}
out->diff = QString("diff --git ") + ("a/" + info->path) + ' ' + ("b/" + info->path);
out->index = QString("index ") + info->blob.a_id + ".." + info->blob.b_id + ' ' + info->mode;
out->path = info->path;
out->blob = info->blob;
bool atat = false;
for (std::string const &line : lines) {
int c = line[0] & 0xff;
if (c == '@') {
if (strncmp(line.c_str(), "@@ ", 3) == 0) {
out->hunks.push_back(Git::Hunk());
out->hunks.back().at = line;
atat = true;
}
} else {
if (atat && c == '\\') { // e.g. \ No newline at end of file...
// ignore this line
} else {
if (atat) {
if (c == ' ' || c == '-' || c == '+') {
// nop
} else {
atat = false;
}
}
if (atat) {
if (!out->hunks.isEmpty()) {
out->hunks.back().lines.push_back(line);
}
}
}
}
}
}
diff --git a/src/Git.h b/src/Git.h
index 484f508..d369164 100644
--- a/src/Git.h
+++ b/src/Git.h
@@ -1,484 +1,484 @@
#ifndef GIT_H
#define GIT_H
#include "AbstractProcess.h"
#include <QDateTime>
#include <QObject>
#include <functional>
#include <QDebug>
#include <QMutex>
#include <memory>
#define SINGLE_THREAD 0
#define GIT_ID_LENGTH (40)
class Win32PtyProcess;
enum class LineSide {
Left,
Right,
};
struct TreeLine {
int index;
int depth;
int color_number = 0;
bool bend_early = false;
TreeLine(int index = -1, int depth = -1)
: index(index)
, depth(depth)
{
}
};
struct NamedCommitItem {
enum class Type {
None,
Branch,
Tag,
};
Type type = Type::None;
QString remote;
QString name;
QString id;
};
using NamedCommitList = QList<NamedCommitItem>;
class Git;
using GitPtr = std::shared_ptr<Git>;
class Git : QObject {
public:
class Context {
public:
QString git_command;
};
struct Object {
enum class Type {
UNKNOWN = 0,
COMMIT = 1,
TREE = 2,
BLOB = 3,
TAG = 4,
UNDEFINED = 5,
OFS_DELTA = 6,
REF_DELTA = 7,
};
Type type = Type::UNKNOWN;
QByteArray content;
};
class Hunk {
public:
std::string at;
std::vector<std::string> lines;
};
class Diff {
public:
enum class Type {
Unknown,
Modify,
Copy,
Rename,
Create,
Delete,
ChType,
Unmerged,
};
Type type = Type::Unknown;
QString diff;
QString index;
QString path;
QString mode;
struct BLOB_AB_ {
QString a_id;
QString b_id;
} blob;
QList<Hunk> hunks;
Diff() = default;
Diff(QString const &id, QString const &path, QString const &mode)
{
makeForSingleFile(this, QString(GIT_ID_LENGTH, '0'), id, path, mode);
}
private:
void makeForSingleFile(Git::Diff *diff, QString const &id_a, QString const &id_b, QString const &path, QString const &mode);
};
enum class SignatureGrade {
NoSignature,
Unknown,
Good,
Dubious,
Missing,
Bad,
};
static SignatureGrade evaluateSignature(char s)
{
switch (s) {
case 'G':
return SignatureGrade::Good;
case 'U':
case 'X':
case 'Y':
return SignatureGrade::Dubious;
case 'B':
case 'R':
return SignatureGrade::Bad;
case 'E':
return SignatureGrade::Missing;
case 'N':
case ' ':
case 0:
return SignatureGrade::NoSignature;
}
return SignatureGrade::Unknown;
}
struct CommitItem {
QString commit_id;
QStringList parent_ids;
QString author;
QString email;
QString message;
QDateTime commit_date;
std::vector<TreeLine> parent_lines;
QByteArray fingerprint;
char signature = 0; // git log format:%G?
bool has_child = false;
int marker_depth = -1;
bool resolved = false;
bool strange_date = false;
};
using CommitItemList = std::vector<CommitItem>;
static bool isUncommited(CommitItem const &item)
{
return item.commit_id.isEmpty();
}
struct Branch {
QString name;
QString id;
QString remote;
int ahead = 0;
int behind = 0;
enum {
None,
Current = 0x0001,
HeadDetachedAt = 0x0002,
HeadDetachedFrom = 0x0004,
};
int flags = 0;
operator bool () const
{
if (name.isEmpty()) return false;
if (id.isEmpty()) return false;
return true;
}
bool isCurrent() const
{
return flags & Current;
}
bool isHeadDetached() const
{
return flags & HeadDetachedAt;
}
};
struct Tag {
QString name;
QString id;
};
enum class FileStatusCode : unsigned int {
Unknown,
Ignored,
Untracked,
NotUpdated = 0x10000000,
Staged_ = 0x20000000,
UpdatedInIndex,
AddedToIndex,
DeletedFromIndex,
RenamedInIndex,
CopiedInIndex,
Unmerged_ = 0x40000000,
Unmerged_BothDeleted,
Unmerged_AddedByUs,
Unmerged_DeletedByThem,
Unmerged_AddedByThem,
Unmerged_DeletedByUs,
Unmerged_BothAdded,
Unmerged_BothModified,
Tracked_ = 0xf0000000
};
class FileStatus {
public:
struct Data {
char code_x = 0;
char code_y = 0;
FileStatusCode code = FileStatusCode::Unknown;
QString rawpath1;
QString rawpath2;
QString path1;
QString path2;
} data;
static FileStatusCode parseFileStatusCode(char x, char y);
bool isStaged() const
{
return (int)data.code & (int)FileStatusCode::Staged_;
}
bool isUnmerged() const
{
return (int)data.code & (int)FileStatusCode::Unmerged_;
}
bool isTracked() const
{
return (int)data.code & (int)FileStatusCode::Tracked_;
}
void parse(QString const &text);
FileStatus() = default;
FileStatus(QString const &text)
{
parse(text);
}
FileStatusCode code() const
{
return data.code;
}
int code_x() const
{
return data.code_x;
}
int code_y() const
{
return data.code_y;
}
bool isDeleted() const
{
return code_x() == 'D' || code_y() == 'D';
}
QString path1() const
{
return data.path1;
}
QString path2() const
{
return data.path2;
}
QString rawpath1() const
{
return data.rawpath1;
}
QString rawpath2() const
{
return data.rawpath2;
}
};
using FileStatusList = std::vector<FileStatus>;
static QString trimPath(QString const &s);
private:
struct Private;
Private *m;
QStringList make_branch_list_();
QByteArray cat_file_(QString const &id);
FileStatusList status_();
bool commit_(QString const &msg, bool amend, bool sign, AbstractPtyProcess *pty);
bool push_(bool tags, AbstractPtyProcess *pty);
static void parseAheadBehind(QString const &s, Branch *b);
Git();
QString encodeQuotedText(QString const &str);
QStringList refs();
public:
Git(Context const &cx, QString const &repodir);
Git(Git &&r) = delete;
~Git() override;
using callback_t = bool (*)(void *, const char *, int);
void setLogCallback(callback_t func, void *cookie);
QByteArray toQByteArray() const;
void setGitCommand(QString const &path);
QString gitCommand() const;
void clearResult();
QString resultText() const;
bool chdirexec(std::function<bool ()> const &fn);
bool git(QString const &arg, bool chdir, bool errout = false, AbstractPtyProcess *pty = nullptr);
bool git(QString const &arg)
{
return git(arg, true);
}
void setWorkingRepositoryDir(QString const &repo);
QString const &workingRepositoryDir() const;
QString getCurrentBranchName();
bool isValidWorkingCopy() const;
QString version();
bool init();
QStringList getUntrackedFiles();
CommitItemList log_all(QString const &id, int maxcount);
CommitItemList log(int maxcount);
bool queryCommit(QString const &id, CommitItem *out);
struct CloneData {
QString url;
QString basedir;
QString subdir;
};
static CloneData preclone(QString const &url, QString const &path);
bool clone(CloneData const &data, AbstractPtyProcess *pty);
FileStatusList status();
bool cat_file(QString const &id, QByteArray *out);
void resetFile(QString const &path);
void resetAllFiles();
void removeFile(QString const &path);
void stage(QString const &path);
void stage(QStringList const &paths);
void unstage(QString const &path);
void unstage(QStringList const &paths);
void pull(AbstractPtyProcess *pty = nullptr);
void fetch(AbstractPtyProcess *pty = nullptr, bool prune = false);
QList<Branch> branches();
int getProcessExitCode() const;
QString diff(QString const &old_id, QString const &new_id);
QString diff_file(QString const &old_path, QString const &new_path);
struct DiffRaw {
struct AB {
QString id;
QString mode;
} a, b;
QString state;
QStringList files;
};
struct Remote {
QString name;
QString url;
QString purpose;
};
QList<DiffRaw> diff_raw(QString const &old_id, QString const &new_id);
static bool isValidID(QString const &s);
bool commit(QString const &text, bool sign, AbstractPtyProcess *pty);
bool commit_amend_m(QString const &text, bool sign, AbstractPtyProcess *pty);
bool revert(QString const &id);
bool push(bool tags, AbstractPtyProcess *pty = nullptr);
void getRemoteURLs(QList<Remote> *out);
void createBranch(QString const &name);
void checkoutBranch(QString const &name);
void mergeBranch(QString const &name);
void rebaseBranch(QString const &name);
static bool isValidWorkingCopy(QString const &dir);
QString diff_to_file(QString const &old_id, QString const &path);
QString errorMessage() const;
GitPtr dup() const;
QString rev_parse(QString const &name);
QList<Tag> tags();
bool tag(QString const &name, QString const &id = QString());
void delete_tag(QString const &name, bool remote);
void setRemoteURL(QString const &name, QString const &url);
void addRemoteURL(QString const &name, QString const &url);
void removeRemote(QString const &name);
QStringList getRemotes();
struct User {
QString name;
QString email;
};
enum class Source {
Default,
Global,
Local,
};
User getUser(Source purpose);
void setUser(User const&user, bool global);
bool reset_head1();
bool reset_hard();
bool clean_df();
void push_u(QString const &remote, QString const &branch, AbstractPtyProcess *pty);
QString objectType(QString const &id);
bool rm_cached(QString const &file);
void cherrypick(QString const &name);
struct ReflogItem {
QString id;
QString head;
QString command;
- QString comment;
+ QString message;
struct File {
QString atts_a;
QString atts_b;
QString id_a;
QString id_b;
QString type;
QString path;
};
QList<File> files;
};
using ReflogItemList = QList<ReflogItem>;
bool reflog(ReflogItemList *out, int maxcount = 100);
QByteArray blame(QString const &path);
enum SignPolicy {
Unset,
False,
True,
};
QString signingKey(Source purpose);
bool setSigningKey(QString const &id, bool global);
SignPolicy signPolicy(Source source);
bool setSignPolicy(Source source, SignPolicy policy);
bool configGpgProgram(QString const &path, bool global);
struct RemoteInfo {
QString commit_id;
QString name;
};
QList<RemoteInfo> ls_remote();
bool stash();
bool stash_apply();
bool stash_drop();
};
void parseDiff(std::string const &s, Git::Diff const *info, Git::Diff *out);
#endif // GIT_H
diff --git a/src/LogTableWidget.cpp b/src/LogTableWidget.cpp
index 64db652..3bbe28d 100644
--- a/src/LogTableWidget.cpp
+++ b/src/LogTableWidget.cpp
@@ -1,354 +1,354 @@
#include "LogTableWidget.h"
#include <QDebug>
#include <QEvent>
#include <QPainter>
#include <QProxyStyle>
#include <cmath>
#include "MainWindow.h"
#include <QApplication>
#include "MyTableWidgetDelegate.h"
#include "common/misc.h"
struct LogTableWidget::Private {
};
class LogTableWidgetDelegate : public MyTableWidgetDelegate {
private:
MainWindow *mainwindow() const
{
auto *w = dynamic_cast<LogTableWidget *>(QStyledItemDelegate::parent());
Q_ASSERT(w);
return w->mainwindow();
}
static QColor labelColor(int kind)
{
switch (kind) {
case BasicMainWindow::Label::Head: return QColor(255, 192, 224); // blue
case BasicMainWindow::Label::LocalBranch: return QColor(192, 224, 255); // blue
case BasicMainWindow::Label::RemoteBranch: return QColor(192, 240, 224); // green
case BasicMainWindow::Label::Tag: return QColor(255, 224, 192); // orange
}
return QColor(224, 224, 224); // gray
}
static QColor hiliteColor(QColor const &color)
{
int r = color.red();
int g = color.green();
int b = color.blue();
r = 255 - (255 - r) / 2;
g = 255 - (255 - g) / 2;
b = 255 - (255 - b) / 2;
return QColor(r, g, b);
}
static QColor shadowColor(QColor const &color)
{
return QColor(color.red() / 2, color.green() / 2, color.blue() / 2);
}
void drawSignatureIcon(QPainter *painter, const QStyleOptionViewItem &opt, QModelIndex const &index) const
{
if (!opt.widget->isEnabled()) return;
Git::CommitItem const *commit = mainwindow()->commitItem(index.row());
if (commit) {
QIcon icon = mainwindow()->verifiedIcon(commit->signature);
if (!icon.isNull()) {
QRect r = opt.rect.adjusted(6, 3, 0, -3);
int h = r.height();
int w = h;
int x = r.x() + r.width() - w;
int y = r.y();
icon.paint(painter, x, y, w, h);
}
}
}
void drawAvatar(QPainter *painter, const QStyleOptionViewItem &opt, QModelIndex const &index) const
{
if (!opt.widget->isEnabled()) return;
int row = index.row();
QIcon icon = mainwindow()->committerIcon(row);
if (!icon.isNull()) {
int h = opt.rect.height();
int w = h;
int x = opt.rect.x() + opt.rect.width() - w;
int y = opt.rect.y();
painter->save();
painter->setOpacity(0.5); // 半透明で描画
icon.paint(painter, x, y, w, h);
painter->restore();
}
}
- void drawDescription(QPainter *painter, const QStyleOptionViewItem &opt, QModelIndex const &index) const
+ void drawLabels(QPainter *painter, const QStyleOptionViewItem &opt, QModelIndex const &index) const
{
int row = index.row();
QList<BasicMainWindow::Label> const *labels = mainwindow()->label(row);
if (labels) {
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
QFontMetrics fm = painter->fontMetrics();
const int space = 8;
int x = opt.rect.x() + opt.rect.width() - 3;
int x1 = x;
int y0 = opt.rect.y();
int y1 = y0 + opt.rect.height() - 1;
int i = labels->size();
while (i > 0) {
i--;
BasicMainWindow::Label const &label = labels->at(i);
QString text = misc::abbrevBranchName(label.text);
- int w = fm.size(0, text).width() + space * 2;
+ int w = fm.size(0, text).width() + space * 2; // 幅
int x0 = x1 - w;
QRect r(x0, y0, x1 - x0, y1 - y0);
painter->setPen(Qt::NoPen);
auto DrawRect = [&](int dx, int dy, QColor color){
painter->setBrush(color);
painter->drawRoundedRect(r.adjusted(lround(dx + 3), lround(dy + 3), lround(dx - 3), lround(dy - 3)), 3, 3);
};
QColor color = labelColor(label.kind);
QColor hilite = hiliteColor(color);
QColor shadow = shadowColor(color);
DrawRect(-1, -1, hilite);
DrawRect(1, 1, shadow);
DrawRect(0, 0, color);
painter->setPen(Qt::black);
painter->setBrush(Qt::NoBrush);
- qApp->style()->drawItemText(painter, r.adjusted(space, 0, 0, 0), opt.displayAlignment, opt.palette, true, text);
+ QApplication::style()->drawItemText(painter, r.adjusted(space, 0, 0, 0), opt.displayAlignment, opt.palette, true, text);
x1 = x0;
}
painter->restore();
}
}
public:
explicit LogTableWidgetDelegate(QObject *parent = Q_NULLPTR)
: MyTableWidgetDelegate(parent)
{
}
void paint(QPainter *painter, const QStyleOptionViewItem &option, QModelIndex const &index) const override
{
MyTableWidgetDelegate::paint(painter, option, index);
enum {
Graph,
CommitId,
Date,
Author,
- Description,
+ Message,
};
// signatureの描画
if (index.column() == CommitId) {
drawSignatureIcon(painter, option, index);
}
// コミット日時
if (index.column() == Date) {
Git::CommitItem const *commit = mainwindow()->commitItem(index.row());
if (commit && commit->strange_date) {
QColor color(255, 0, 0, 128);
QRect r = option.rect.adjusted(1, 1, -1, -2);
misc::drawFrame(painter, r.x(), r.y(), r.width(), r.height(), color, color);
}
}
// avatarの描画
if (index.column() == Author) {
drawAvatar(painter, option, index);
}
- // Descriptionの描画
- if (index.column() == Description) {
- drawDescription(painter, option, index);
+ // ラベルの描画
+ if (index.column() == Message) {
+ drawLabels(painter, option, index);
}
}
};
LogTableWidget::LogTableWidget(QWidget *parent)
: QTableWidget(parent)
, m(new Private)
{
setItemDelegate(new LogTableWidgetDelegate(this));
}
LogTableWidget::~LogTableWidget()
{
delete m;
}
MainWindow *LogTableWidget::mainwindow()
{
MainWindow *mw = qobject_cast<MainWindow *>(window());
Q_ASSERT(mw);
return mw;
}
void drawBranch(QPainterPath *path, double x0, double y0, double x1, double y1, double r, bool bend_early)
{
const double k = 0.55228475;
if (x0 == x1) {
path->moveTo(x0, y0);
path->lineTo(x1, y1);
} else {
double ym = bend_early ? (y0 + r) : (y1 - r);
double h = fabs(y1 - y0);
double w = fabs(x1 - x0);
if (r > h / 2) r = h / 2;
if (r > w / 2) r = w / 2;
double s = r;
if (x0 > x1) r = -r;
if (y0 > y1) s = -s;
if (0) {
path->moveTo(x0, y0);
path->lineTo(x0, ym - s);
path->cubicTo(x0, ym - s + s * k, x0 + r - r * k, ym, x0 + r, ym);
path->lineTo(x1 - r, ym);
path->cubicTo(x1 - r + r * k, ym, x1, ym + s - s * k, x1, ym + s);
path->lineTo(x1, y1);
} else {
if (bend_early) {
path->moveTo(x0, y0);
path->cubicTo(x0, ym, x1, ym, x1, ym + ym - y0);
path->lineTo(x1, y1);
} else {
path->moveTo(x0, y0);
path->lineTo(x0, ym + ym - y1);
path->cubicTo(x0, ym, x1, ym, x1, y1);
}
}
}
}
void LogTableWidget::paintEvent(QPaintEvent *e)
{
if (rowCount() < 1) return;
QTableWidget::paintEvent(e);
QPainter pr(viewport());
pr.setRenderHint(QPainter::Antialiasing);
pr.setBrush(QBrush(QColor(255, 255, 255)));
Git::CommitItemList const *list = &mainwindow()->getLogs();
int indent_span = 16;
auto ItemRect = [&](int row){
QRect r;
QTableWidgetItem *p = item(row, 0);
if (p) {
r = visualItemRect(p);
}
return r;
};
int line_width = 2;
auto ItemPoint = [&](int depth, QRect const &rect){
int h = rect.height();
double n = h / 2.0;
double x = floor(rect.x() + n + depth * indent_span);
double y = floor(rect.y() + n);
return QPointF(x, y);
};
auto SetPen = [&](QPainter *pr, int level, bool /*continued*/){
QColor c = mainwindow()->color(level + 1);
Qt::PenStyle s = Qt::SolidLine;
pr->setPen(QPen(c, line_width, s));
};
auto DrawLine = [&](size_t index, int itemrow){
QRect rc1;
if (index < list->size()) {
Git::CommitItem const &item1 = list->at(index);
rc1 = ItemRect(itemrow);
QPointF pt1 = ItemPoint(item1.marker_depth, rc1);
double halfheight = rc1.height() / 2.0;
for (TreeLine const &line : item1.parent_lines) {
if (line.depth >= 0) {
QPainterPath *path = nullptr;
Git::CommitItem const &item2 = list->at(line.index);
QRect rc2 = ItemRect(line.index);
if (index + 1 == (size_t)line.index || line.depth == item1.marker_depth || line.depth == item2.marker_depth) {
QPointF pt2 = ItemPoint(line.depth, rc2);
if (pt2.y() > 0) {
path = new QPainterPath();
drawBranch(path, pt1.x(), pt1.y(), pt2.x(), pt2.y(), halfheight, line.bend_early);
}
} else {
QPointF pt3 = ItemPoint(item2.marker_depth, rc2);
if (pt3.y() > 0) {
path = new QPainterPath();
QRect rc3 = ItemRect(itemrow + 1);
QPointF pt2 = ItemPoint(line.depth, rc3);
drawBranch(path, pt1.x(), pt1.y(), pt2.x(), pt2.y(), halfheight, true);
drawBranch(path, pt2.x(), pt2.y(), pt3.x(), pt3.y(), halfheight, false);
}
}
if (path) {
SetPen(&pr, line.color_number, false);
pr.drawPath(*path);
delete path;
}
}
}
}
return rc1.y();
};
auto DrawMark = [&](size_t index, int row){
double x, y;
y = 0;
if (index < list->size()) {
Git::CommitItem const &item = list->at(index);
QRect rc = ItemRect(row);
QPointF pt = ItemPoint(item.marker_depth, rc);
double r = 4;
x = pt.x() - r;
y = pt.y() - r;
SetPen(&pr, item.marker_depth, false);
if (item.resolved) {
// ◯
pr.drawEllipse((int)x, (int)y, int(r * 2), int(r * 2));
} else {
// ▽
QPainterPath path;
path.moveTo(pt.x(), pt.y() + r);
path.lineTo(pt.x() - r, pt.y() - r);
path.lineTo(pt.x() + r, pt.y() - r);
path.lineTo(pt.x(), pt.y() + r);
pr.drawPath(path);
}
}
return y;
};
// draw lines
pr.setOpacity(0.5);
pr.setBrush(Qt::NoBrush);
for (size_t i = 0; i < list->size(); i++) {
double y = DrawLine(i, i);
if (y >= height()) break;
}
// draw marks
pr.setOpacity(1);
pr.setBrush(mainwindow()->color(0));
for (size_t i = 0; i < list->size(); i++) {
double y = DrawMark(i, i);
if (y >= height()) break;
}
}
diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp
index fa81e16..3c925ed 100644
--- a/src/MainWindow.cpp
+++ b/src/MainWindow.cpp
@@ -1,2600 +1,2600 @@
#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::shown()
{
while (!misc::isExecutable(appsettings()->git_command) || !misc::isExecutable(appsettings()->file_command)) {
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", n).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("Description"),
+ 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 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();
setCurrentLogRow(select_row >= 0 ? select_row : selrow);
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_comment = nullptr;
+ QAction *a_edit_message = nullptr;
if (row == 0 && currentBranch().ahead > 0) {
- a_edit_comment = menu.addAction(tr("Edit comment..."));
+ 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_comment) {
+ 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&)> fn)
{
for (QString 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 = &currentRepository();
openTerminal(repo);
}
void MainWindow::on_action_explorer_triggered()
{
auto const *repo = &currentRepository();
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/ReflogWindow.cpp b/src/ReflogWindow.cpp
index b75eed8..6a59c92 100644
--- a/src/ReflogWindow.cpp
+++ b/src/ReflogWindow.cpp
@@ -1,126 +1,126 @@
#include "CommitExploreWindow.h"
#include "ReflogWindow.h"
#include "ui_ReflogWindow.h"
#include "MainWindow.h"
#include "Git.h"
#include <QMenu>
ReflogWindow::ReflogWindow(QWidget *parent, MainWindow *mainwin, Git::ReflogItemList const &reflog)
: QDialog(parent)
, ui(new Ui::ReflogWindow)
{
ui->setupUi(this);
Qt::WindowFlags flags = windowFlags();
flags &= ~Qt::WindowContextHelpButtonHint;
flags |= Qt::WindowMaximizeButtonHint;
setWindowFlags(flags);
mainwindow_ = mainwin;
reflog_ = reflog;
updateTable(reflog_);
}
ReflogWindow::~ReflogWindow()
{
delete ui;
}
void ReflogWindow::updateTable(Git::ReflogItemList const &reflog)
{
QTableWidgetItem *item;
ui->tableWidget->clear();
QStringList cols = {
tr("Commit"),
tr("Head"),
tr("Command"),
- tr("Comment"),
+ tr("Message"),
};
auto newQTableWidgetItem = [](QString const &text){
auto *item = new QTableWidgetItem(text);
return item;
};
ui->tableWidget->setColumnCount(cols.size());
ui->tableWidget->setRowCount(reflog.size());
for (int col = 0; col < cols.size(); col++) {
item = newQTableWidgetItem(cols[col]);
ui->tableWidget->setHorizontalHeaderItem(col, item);
}
int row = 0;
for (Git::ReflogItem const &t : reflog) {
QString text = t.id.mid(0, 7);
item = newQTableWidgetItem(text);
ui->tableWidget->setItem(row, 0, item);
item = newQTableWidgetItem(t.head);
ui->tableWidget->setItem(row, 1, item);
QString cmd = t.command;
int i = cmd.indexOf(' ');
if (i < 0) i = cmd.size();
if (i > 10) i = 10;
cmd = cmd.mid(0, i);
item = newQTableWidgetItem(cmd);
ui->tableWidget->setItem(row, 2, item);
- item = newQTableWidgetItem(t.comment);
+ item = newQTableWidgetItem(t.message);
ui->tableWidget->setItem(row, 3, item);
ui->tableWidget->setRowHeight(row, 24);
row++;
}
ui->tableWidget->resizeColumnsToContents();
ui->tableWidget->horizontalHeader()->setStretchLastSection(true);
}
bool ReflogWindow::currentCommit(Git::CommitItem *out)
{
bool ok = false;
*out = Git::CommitItem();
int row = ui->tableWidget->currentRow();
if (row >= 0 && row < reflog_.size()) {
Git::ReflogItem const &logitem = reflog_[row];
if (mainwindow()->queryCommit(logitem.id, out)) {
ok = true;
}
}
return ok;
}
void ReflogWindow::on_tableWidget_customContextMenuRequested(const QPoint &pos)
{
Git::CommitItem commit;
if (!currentCommit(&commit)) return;
QMenu menu;
QAction *a_checkout = menu.addAction(tr("Checkout"));
QAction *a_explorer = menu.addAction(tr("Explorer"));
QAction *a_property = mainwindow()->addMenuActionProperty(&menu);
QAction *a = menu.exec(ui->tableWidget->viewport()->mapToGlobal(pos) + QPoint(8, -8));
if (a) {
if (a == a_checkout) {
mainwindow()->checkout(this, &commit);
return;
}
if (a == a_explorer) {
mainwindow()->execCommitExploreWindow(this, &commit);
return;
}
if (a == a_property) {
mainwindow()->execCommitPropertyDialog(this, &commit);
return;
}
}
}
void ReflogWindow::on_tableWidget_itemDoubleClicked(QTableWidgetItem *item)
{
(void)item;
Git::CommitItem commit;
if (!currentCommit(&commit)) return;
mainwindow()->execCommitPropertyDialog(this, &commit);
}
diff --git a/src/resources/darktheme/graphcolor.png b/src/resources/darktheme/graphcolor.png
index 77202cd..6e9b3f6 100644
Binary files a/src/resources/darktheme/graphcolor.png and b/src/resources/darktheme/graphcolor.png differ
diff --git a/src/resources/resources.qrc b/src/resources/resources.qrc
index 0fe5bec..25cd9cb 100644
--- a/src/resources/resources.qrc
+++ b/src/resources/resources.qrc
@@ -1,44 +1,44 @@
<RCC>
<qresource prefix="/">
<file>image/stage.svg</file>
<file>image/unstage.svg</file>
<file>image/commit.svg</file>
<file>image/push.svg</file>
<file>image/selall.svg</file>
<file>image/graphcolor.png</file>
<file>image/pull.svg</file>
<file>image/clone.svg</file>
<file>image/fetch.svg</file>
<file>image/digits.png</file>
<file>image/guitar.png</file>
<file>image/Guitar.svg</file>
<file>image/folder.png</file>
<file>image/repository.png</file>
<file>image/maximize.png</file>
<file>image/transparent.png</file>
<file>image/file.png</file>
<file>image/clear.png</file>
<file>image/terminal.svg</file>
<file>image/explorer.svg</file>
<file>image/about.png</file>
<file>image/redsquare.svg</file>
<file>darktheme/button/button_normal.png</file>
<file>darktheme/button/button_press.png</file>
<file>darktheme/hsb/hsb_add_line.png</file>
<file>darktheme/hsb/hsb_page_bg.png</file>
<file>darktheme/hsb/hsb_slider.png</file>
<file>darktheme/hsb/hsb_sub_line.png</file>
<file>darktheme/progress/horz.png</file>
<file>darktheme/progress/vert.png</file>
<file>darktheme/vsb/vsb_add_line.png</file>
<file>darktheme/vsb/vsb_page_bg.png</file>
<file>darktheme/vsb/vsb_slider.png</file>
<file>darktheme/vsb/vsb_sub_line.png</file>
<file>darktheme/graphcolor.png</file>
<file>image/menu.png</file>
<file>image/signature-bad.png</file>
<file>image/signature-dubious.png</file>
<file>image/signature-good.png</file>
- <file>translations/Guitar_ja.qm</file>
+ <file>translations/Guitar_ja.qm</file>
</qresource>
</RCC>
diff --git a/src/resources/translations/Guitar_ja.qm b/src/resources/translations/Guitar_ja.qm
index f2d69e8..3f82038 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_ja.ts b/src/resources/translations/Guitar_ja.ts
index a31c61f..b23aef6 100644
--- a/src/resources/translations/Guitar_ja.ts
+++ b/src/resources/translations/Guitar_ja.ts
@@ -1,2645 +1,2746 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="ja_JP">
<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>接続を続行しますか? (yes/no)?</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>&amp;Property</source>
<translation>プロパティ(&amp;P)</translation>
</message>
<message>
- <location filename="../../BasicMainWindow.cpp" line="1186"/>
+ <location filename="../../BasicMainWindow.cpp" line="1201"/>
<source>Select %1 command</source>
<translation>%1 コマンドの選択</translation>
</message>
<message>
- <location filename="../../BasicMainWindow.cpp" line="1366"/>
+ <location filename="../../BasicMainWindow.cpp" line="1380"/>
<source>Revert all files</source>
<translation>すべてのファイルの変更を破棄</translation>
</message>
<message>
- <location filename="../../BasicMainWindow.cpp" line="1496"/>
+ <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="1500"/>
+ <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="1501"/>
+ <location filename="../../BasicMainWindow.cpp" line="1525"/>
<source>Initialize Repository</source>
<translation>リポジトリの初期化</translation>
</message>
<message>
- <location filename="../../BasicMainWindow.cpp" line="1570"/>
+ <location filename="../../BasicMainWindow.cpp" line="1663"/>
<source>No repository selected</source>
<translation>リポジトリが選択されていません</translation>
</message>
<message>
- <location filename="../../BasicMainWindow.cpp" line="1724"/>
+ <location filename="../../BasicMainWindow.cpp" line="1817"/>
<source>Repository Property</source>
<translation>リポジトリのプロパティ</translation>
</message>
<message>
- <location filename="../../BasicMainWindow.cpp" line="1724"/>
- <location filename="../../BasicMainWindow.cpp" line="1826"/>
+ <location filename="../../BasicMainWindow.cpp" line="1817"/>
+ <location filename="../../BasicMainWindow.cpp" line="1919"/>
<source>Not a valid git repository</source>
<translation>有効なリポジトリではありません</translation>
</message>
<message>
- <location filename="../../BasicMainWindow.cpp" line="1819"/>
- <location filename="../../BasicMainWindow.cpp" line="1826"/>
+ <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="1819"/>
+ <location filename="../../BasicMainWindow.cpp" line="1912"/>
<source>No such folder</source>
<translation>そのようなフォルダはありません</translation>
</message>
<message>
- <location filename="../../BasicMainWindow.cpp" line="1819"/>
+ <location filename="../../BasicMainWindow.cpp" line="1912"/>
<source>Remove from bookmark ?</source>
<translation>ブックマークから削除しますか?</translation>
</message>
<message>
- <location filename="../../BasicMainWindow.cpp" line="2038"/>
+ <location filename="../../BasicMainWindow.cpp" line="2150"/>
<source>, %1 ahead</source>
<translation></translation>
</message>
<message>
- <location filename="../../BasicMainWindow.cpp" line="2041"/>
+ <location filename="../../BasicMainWindow.cpp" line="2153"/>
<source>, %1 behind</source>
<translation></translation>
</message>
<message>
- <location filename="../../BasicMainWindow.cpp" line="2062"/>
+ <location filename="../../BasicMainWindow.cpp" line="2174"/>
<source>Confirm Remove</source>
<translation>削除の確認</translation>
</message>
<message>
- <location filename="../../BasicMainWindow.cpp" line="2062"/>
+ <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="2062"/>
+ <location filename="../../BasicMainWindow.cpp" line="2174"/>
<source>(Files will NOT be deleted)</source>
<translation>(ファルは削除されません)</translation>
</message>
<message>
- <location filename="../../BasicMainWindow.cpp" line="2092"/>
+ <location filename="../../BasicMainWindow.cpp" line="2204"/>
<source>A file with same name already exists</source>
<translation>同じ名前のファイルが既に存在しています</translation>
</message>
<message>
- <location filename="../../BasicMainWindow.cpp" line="2093"/>
- <location filename="../../BasicMainWindow.cpp" line="2098"/>
- <location filename="../../BasicMainWindow.cpp" line="2115"/>
- <location filename="../../BasicMainWindow.cpp" line="2120"/>
+ <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>クローン</translation>
</message>
<message>
- <location filename="../../BasicMainWindow.cpp" line="2097"/>
+ <location filename="../../BasicMainWindow.cpp" line="2209"/>
<source>A folder with same name already exists</source>
<translation>同じ名前のフォルダが既に存在しています</translation>
</message>
<message>
- <location filename="../../BasicMainWindow.cpp" line="2114"/>
+ <location filename="../../BasicMainWindow.cpp" line="2226"/>
<source>Invalid folder</source>
<translation>無効なフォルダ</translation>
</message>
<message>
- <location filename="../../BasicMainWindow.cpp" line="2119"/>
+ <location filename="../../BasicMainWindow.cpp" line="2231"/>
<source>No such folder. Create it now ?</source>
<translation>このフォルダはありません。作成しますか?</translation>
</message>
<message>
- <location filename="../../BasicMainWindow.cpp" line="2179"/>
+ <location filename="../../BasicMainWindow.cpp" line="2291"/>
<source>Commit</source>
<translation>コミット</translation>
</message>
<message>
- <location filename="../../BasicMainWindow.cpp" line="2179"/>
+ <location filename="../../BasicMainWindow.cpp" line="2291"/>
<source>Commit message can not be omitted.</source>
<translation>コミットメッセージを空にすることはできません。</translation>
</message>
<message>
- <location filename="../../BasicMainWindow.cpp" line="2195"/>
+ <location filename="../../BasicMainWindow.cpp" line="2308"/>
<source>Failed to commit</source>
<translation>コミット失敗</translation>
</message>
<message>
- <location filename="../../BasicMainWindow.cpp" line="2229"/>
- <location filename="../../BasicMainWindow.cpp" line="2313"/>
+ <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="2282"/>
+ <location filename="../../BasicMainWindow.cpp" line="2395"/>
<source>No remote repository is registered.</source>
<translation>リモートリポジトリが登録されていません。</translation>
</message>
<message>
- <location filename="../../BasicMainWindow.cpp" line="2304"/>
+ <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="2307"/>
+ <location filename="../../BasicMainWindow.cpp" line="2420"/>
<source>You try push --set-upstream</source>
<translation>--set-upstream を試してください</translation>
</message>
<message>
- <location filename="../../BasicMainWindow.cpp" line="2397"/>
+ <location filename="../../BasicMainWindow.cpp" line="2509"/>
<source>Failed to delete the branch &apos;%1&apos;</source>
<translation>ブランチ「%1」の削除に失敗しました</translation>
</message>
<message>
<source>Failed to delete the branch &apos;%1&apos;
</source>
<translation type="vanished">ブランチの削除に失敗しました : &apos;%1&apos;</translation>
</message>
<message>
- <location filename="../../BasicMainWindow.cpp" line="2462"/>
+ <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="2483"/>
+ <location filename="../../BasicMainWindow.cpp" line="2594"/>
<source>Reset a file</source>
<translation>ファイルをリセットします</translation>
</message>
<message>
- <location filename="../../BasicMainWindow.cpp" line="2497"/>
+ <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>コミット</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>チェックアウト</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>クローン</translation>
</message>
<message>
<location filename="../../CloneDialog.ui" line="22"/>
<source>Remote</source>
<translation>リモート</translation>
</message>
<message>
<location filename="../../CloneDialog.ui" line="39"/>
<source>&amp;Test</source>
<translation>テスト(&amp;T)</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>コミット</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>コミット</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>チェックアウト</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>&lt;Unknown&gt;</source>
<translation>&lt;不明&gt;</translation>
</message>
</context>
<context>
<name>CommitViewWindow</name>
<message>
<location filename="../../CommitViewWindow.ui" line="14"/>
<source>Commit View</source>
<translation>コミットビュー</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>有効なリポジトリが既に存在しています。</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>&amp;All branches</source>
<translation>全てのブランチ(&amp;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>は、有効なGitリポジトリではありません。</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>Gitで無視するファイルの編集</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>&amp;Test</source>
<translation>テスト(&amp;T)</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>コミット</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>概要</translation>
+ <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>&amp;Close</source>
<translation>閉じる(&amp;C)</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>
- <location filename="../../JumpDialog.ui" line="24"/>
<source>Branches and Tags</source>
- <translation>ブランチとタグ</translation>
+ <translation type="vanished">ブランチとタグ</translation>
</message>
<message>
- <location filename="../../JumpDialog.ui" line="47"/>
<source>&amp;Filter</source>
- <translation>フィルタ(&amp;F)</translation>
+ <translation type="vanished">フィルタ(&amp;F)</translation>
</message>
<message>
- <location filename="../../JumpDialog.ui" line="98"/>
<source>Find</source>
- <translation>検索</translation>
+ <translation type="vanished">検索</translation>
</message>
<message>
- <location filename="../../JumpDialog.ui" line="126"/>
<source>&amp;Checkout</source>
- <translation>チェックアウト(&amp;C)</translation>
+ <translation type="vanished">チェックアウト(&amp;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="146"/>
+ <location filename="../../JumpDialog.ui" line="71"/>
<source>OK</source>
<translation></translation>
</message>
<message>
- <location filename="../../JumpDialog.ui" line="153"/>
+ <location filename="../../JumpDialog.ui" line="81"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
<message>
- <location filename="../../JumpDialog.cpp" line="28"/>
+ <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="1344"/>
- <location filename="../../MainWindow.ui" line="1347"/>
+ <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="1473"/>
+ <location filename="../../MainWindow.ui" line="1476"/>
<source>Terminal</source>
<translation>端末</translation>
</message>
<message>
<location filename="../../MainWindow.ui" line="287"/>
- <location filename="../../MainWindow.ui" line="1482"/>
+ <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="422"/>
+ <location filename="../../MainWindow.ui" line="432"/>
+ <location filename="../../MainWindow.ui" line="1520"/>
<source>Offline</source>
<translation>オフライン</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="432"/>
+ <location filename="../../MainWindow.ui" line="422"/>
+ <location filename="../../MainWindow.ui" line="1515"/>
<source>Online</source>
<translation>オンライン</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="648"/>
- <source>PushButton</source>
- <translation></translation>
- </message>
- <message>
- <location filename="../../MainWindow.ui" line="762"/>
- <location filename="../../MainWindow.cpp" line="1607"/>
+ <location filename="../../MainWindow.ui" line="748"/>
+ <location filename="../../MainWindow.cpp" line="1594"/>
<source>Unstage</source>
<translation>除外</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="791"/>
+ <location filename="../../MainWindow.ui" line="777"/>
<source>Select all</source>
<translation>全て選択</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="826"/>
- <location filename="../../MainWindow.cpp" line="1477"/>
+ <location filename="../../MainWindow.ui" line="812"/>
+ <location filename="../../MainWindow.cpp" line="1464"/>
<source>Stage</source>
<translation>追加</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="855"/>
- <location filename="../../MainWindow.cpp" line="860"/>
+ <location filename="../../MainWindow.ui" line="841"/>
+ <location filename="../../MainWindow.cpp" line="841"/>
<source>Commit</source>
<translation>コミット</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="986"/>
+ <location filename="../../MainWindow.ui" line="972"/>
<source>&amp;File</source>
<translation>ファイル(&amp;F)</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="998"/>
+ <location filename="../../MainWindow.ui" line="983"/>
<source>&amp;View</source>
<translation>表示(&amp;V)</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1004"/>
+ <location filename="../../MainWindow.ui" line="989"/>
<source>&amp;Edit</source>
<translation>編集(&amp;E)</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1032"/>
<source>Destructive</source>
- <translation>注意を要するコマンド</translation>
+ <translation type="vanished">注意を要するコマンド</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1335"/>
+ <location filename="../../MainWindow.ui" line="1343"/>
<source>Settings</source>
<translation>設定</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1357"/>
- <location filename="../../MainWindow.cpp" line="1341"/>
+ <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="1360"/>
+ <location filename="../../MainWindow.ui" line="1368"/>
<source>Edit tags</source>
<translation>タグの編集</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1487"/>
+ <location filename="../../MainWindow.ui" line="1490"/>
<source>Clean -df</source>
<translation></translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1492"/>
+ <location filename="../../MainWindow.ui" line="1495"/>
<source>Reset --hard</source>
<translation></translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1414"/>
+ <location filename="../../MainWindow.ui" line="1422"/>
<source>Create a repository</source>
<translation>リポジトリの作成</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1423"/>
+ <location filename="../../MainWindow.ui" line="1409"/>
+ <source>Push upstream</source>
+ <translation>プッシュ upstream</translation>
+ </message>
+ <message>
+ <location filename="../../MainWindow.ui" line="1431"/>
<source>Stop process</source>
<translation>処理の停止</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1428"/>
+ <location filename="../../MainWindow.ui" line="1436"/>
<source>E&amp;xit</source>
<translation>終了(&amp;X)</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1431"/>
+ <location filename="../../MainWindow.ui" line="1439"/>
<source>Ctrl+Q</source>
<translation></translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1436"/>
+ <location filename="../../MainWindow.ui" line="1444"/>
<source>Reflog...</source>
<translation></translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1441"/>
+ <location filename="../../MainWindow.ui" line="1449"/>
<source>Property...</source>
<translation>プロパティ...</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1446"/>
- <location filename="../../MainWindow.ui" line="1449"/>
+ <location filename="../../MainWindow.ui" line="1454"/>
+ <location filename="../../MainWindow.ui" line="1457"/>
<source>Set GPG signing</source>
<translation>GPS署名の指定</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1464"/>
+ <location filename="../../MainWindow.ui" line="1467"/>
<source>Fetch --prune</source>
<translation></translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1016"/>
+ <location filename="../../MainWindow.ui" line="1001"/>
<source>&amp;Help</source>
<translation>ヘルプ(&amp;H)</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1022"/>
+ <location filename="../../MainWindow.ui" line="605"/>
+ <source>...</source>
+ <translation></translation>
+ </message>
+ <message>
+ <location filename="../../MainWindow.ui" line="1007"/>
<source>&amp;Window</source>
<translation>ウィンドウ(&amp;W)</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1028"/>
+ <location filename="../../MainWindow.ui" line="1013"/>
<source>&amp;Repository</source>
<translation>リポジトリ(&amp;R)</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1081"/>
+ <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&amp;mote</source>
+ <translation>リモート(&amp;M)</translation>
+ </message>
+ <message>
+ <location filename="../../MainWindow.ui" line="1055"/>
+ <source>&amp;Destructive</source>
+ <translation>要注意(&amp;D)</translation>
+ </message>
+ <message>
+ <location filename="../../MainWindow.ui" line="1086"/>
<source>Log</source>
<translation>ログ</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1248"/>
+ <location filename="../../MainWindow.ui" line="1256"/>
<source>&amp;Open existing working copy...</source>
<translation>既存の作業コピーを開く(&amp;O)...</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1251"/>
- <location filename="../../MainWindow.cpp" line="1670"/>
+ <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="1256"/>
- <location filename="../../MainWindow.ui" line="1259"/>
+ <location filename="../../MainWindow.ui" line="1264"/>
+ <location filename="../../MainWindow.ui" line="1267"/>
<source>Refresh</source>
<translation>更新</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1262"/>
+ <location filename="../../MainWindow.ui" line="1270"/>
<source>F5</source>
<translation></translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1271"/>
+ <location filename="../../MainWindow.ui" line="1279"/>
<source>&amp;Commit</source>
<translation>コミット(&amp;C)</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1280"/>
+ <location filename="../../MainWindow.ui" line="1288"/>
<source>&amp;Push</source>
<translation>プッシュ(&amp;P)</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1288"/>
+ <location filename="../../MainWindow.ui" line="1296"/>
<source>test</source>
<translation></translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1291"/>
+ <location filename="../../MainWindow.ui" line="1299"/>
<source>Ctrl+T</source>
<translation></translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1300"/>
+ <location filename="../../MainWindow.ui" line="1308"/>
<source>Pu&amp;ll</source>
<translation>プル(&amp;L)</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1309"/>
+ <location filename="../../MainWindow.ui" line="1317"/>
<source>&amp;Fetch</source>
<translation>フェッチ(&amp;F)</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1314"/>
- <location filename="../../MainWindow.ui" line="1317"/>
+ <location filename="../../MainWindow.ui" line="1322"/>
+ <location filename="../../MainWindow.ui" line="1325"/>
<source>Edit global .gitconfig</source>
<translation>グローバル .gitignore を編集</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1322"/>
+ <location filename="../../MainWindow.ui" line="1330"/>
<source>Edit .git/config</source>
<translation>.git/config を編集</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1327"/>
+ <location filename="../../MainWindow.ui" line="1335"/>
<source>Edit .gitignore</source>
<translation>.gitignore を編集</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1332"/>
+ <location filename="../../MainWindow.ui" line="1340"/>
<source>&amp;Settings...</source>
<translation>設定(&amp;S)...</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1383"/>
+ <location filename="../../MainWindow.ui" line="1391"/>
<source>&amp;Jump...</source>
<translation>移動(&amp;J)...</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1386"/>
+ <location filename="../../MainWindow.ui" line="1394"/>
<source>Ctrl+J</source>
<translation></translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1391"/>
+ <location filename="../../MainWindow.ui" line="1399"/>
<source>Check&amp;out...</source>
<translation>チェックアウト(&amp;O)...</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1396"/>
- <location filename="../../MainWindow.cpp" line="1353"/>
+ <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="1401"/>
- <source>Push --set-upstream</source>
+ <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="1406"/>
- <location filename="../../MainWindow.ui" line="1409"/>
+ <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="1454"/>
- <location filename="../../MainWindow.cpp" line="1354"/>
+ <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="1459"/>
- <source>rebase onto</source>
- <translation></translation>
- </message>
- <message>
- <location filename="../../MainWindow.ui" line="1352"/>
+ <location filename="../../MainWindow.ui" line="1360"/>
<source>&amp;About</source>
<translation>Guitarについて(&amp;A)</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1365"/>
+ <location filename="../../MainWindow.ui" line="1373"/>
<source>Push all tags</source>
<translation>全てのタグをプッシュ</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1370"/>
+ <location filename="../../MainWindow.ui" line="1378"/>
<source>Set config user</source>
<translation>ユーザー情報を設定</translation>
</message>
<message>
- <location filename="../../MainWindow.ui" line="1378"/>
+ <location filename="../../MainWindow.ui" line="1386"/>
<source>&amp;Log</source>
<translation>ログ(&amp;L)</translation>
</message>
<message>
<source>Unnamed</source>
<translation type="vanished">無名</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="662"/>
+ <location filename="../../MainWindow.cpp" line="643"/>
<source>Default</source>
<translation></translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="859"/>
+ <location filename="../../MainWindow.cpp" line="840"/>
<source>Graph</source>
<translation>樹形図</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="861"/>
+ <location filename="../../MainWindow.cpp" line="842"/>
<source>Date</source>
<translation>日付</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="862"/>
+ <location filename="../../MainWindow.cpp" line="843"/>
<source>Author</source>
<translation>名前</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="863"/>
<source>Description</source>
- <translation>概要</translation>
+ <translation type="vanished">概要</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="1013"/>
- <location filename="../../MainWindow.cpp" line="1137"/>
+ <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="1238"/>
+ <location filename="../../MainWindow.cpp" line="1218"/>
<source>&amp;Add new group</source>
<translation>新しいグループを追加(&amp;A)</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="1239"/>
+ <location filename="../../MainWindow.cpp" line="1219"/>
<source>&amp;Delete group</source>
<translation>グループを削除(&amp;D)</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="1240"/>
+ <location filename="../../MainWindow.cpp" line="1220"/>
<source>&amp;Rename group</source>
<translation>グループ名の変更(&amp;R)</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="1245"/>
+ <location filename="../../MainWindow.cpp" line="1225"/>
<source>New group</source>
<translation>新しいグループ</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="1269"/>
+ <location filename="../../MainWindow.cpp" line="1249"/>
<source>Open &amp;terminal</source>
<translation>端末を開く(&amp;T)</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="1270"/>
+ <location filename="../../MainWindow.cpp" line="1250"/>
<source>Open command promp&amp;t</source>
<translation>コマンドプロンプトを開く(&amp;T)</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="1272"/>
+ <location filename="../../MainWindow.cpp" line="1252"/>
<source>&amp;Open</source>
<translation>開く(&amp;O)</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="1281"/>
+ <location filename="../../MainWindow.cpp" line="1263"/>
<source>Open &amp;folder</source>
<translation>フォルダを開く(&amp;F)</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="1283"/>
+ <location filename="../../MainWindow.cpp" line="1268"/>
<source>&amp;Remove</source>
<translation>削除(&amp;R)</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="1324"/>
+ <location filename="../../MainWindow.cpp" line="1311"/>
<source>Copy commit id (7 letters)</source>
<translation>コミットIDをコピー(7文字)</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="1325"/>
+ <location filename="../../MainWindow.cpp" line="1312"/>
<source>Copy commit id (completely)</source>
<translation>コミットIDをコピー(すべて)</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="1335"/>
<source>Edit comment...</source>
- <translation>コメントを編集...</translation>
+ <translation type="vanished">コメントを編集...</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="1339"/>
- <location filename="../../MainWindow.cpp" line="2143"/>
+ <location filename="../../MainWindow.cpp" line="1326"/>
+ <location filename="../../MainWindow.cpp" line="2130"/>
<source>Rebase</source>
<translation>リベース</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="1358"/>
+ <location filename="../../MainWindow.cpp" line="1345"/>
<source>Explore</source>
<translation>探索</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="1347"/>
+ <location filename="../../MainWindow.cpp" line="1334"/>
<source>Reset HEAD</source>
<translation>HEADをリセット</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="1433"/>
- <location filename="../../MainWindow.cpp" line="1482"/>
- <location filename="../../MainWindow.cpp" line="1608"/>
+ <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="2475"/>
+ <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="1342"/>
+ <location filename="../../MainWindow.cpp" line="1329"/>
<source>Revert</source>
<translation>変更を破棄する</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>&amp;Property</source>
<translation type="vanished">プロパティ(&amp;P)</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="1329"/>
+ <location filename="../../MainWindow.cpp" line="1316"/>
<source>Checkout/Branch...</source>
<translation>チェックアウト/ブランチ...</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="1338"/>
+ <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="1340"/>
+ <location filename="../../MainWindow.cpp" line="1327"/>
<source>Cherry-pick</source>
<translation>チェリーピック</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="1432"/>
- <location filename="../../MainWindow.cpp" line="1481"/>
+ <location filename="../../MainWindow.cpp" line="1419"/>
+ <location filename="../../MainWindow.cpp" line="1468"/>
<source>Untrack</source>
<translation>追跡しない</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="1434"/>
- <location filename="../../MainWindow.cpp" line="1483"/>
- <location filename="../../MainWindow.cpp" line="1609"/>
+ <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="1442"/>
+ <location filename="../../MainWindow.cpp" line="1429"/>
<source>Delete selected files.</source>
<translation>選択されたファイルを削除します。</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="1453"/>
+ <location filename="../../MainWindow.cpp" line="1440"/>
<source>rm --cached files</source>
<translation></translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="1478"/>
+ <location filename="../../MainWindow.cpp" line="1465"/>
<source>Reset</source>
<translation>リセット</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="1479"/>
+ <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="2140"/>
+ <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="1431"/>
- <location filename="../../MainWindow.cpp" line="1480"/>
+ <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="2092"/>
+ <location filename="../../MainWindow.cpp" line="2088"/>
+ <location filename="../../MainWindow.cpp" line="2102"/>
<source>Jump</source>
<translation>移動</translation>
</message>
<message>
- <location filename="../../MainWindow.cpp" line="2092"/>
<source>That commmit has not foud or not read yet</source>
- <translation>そのコミットは、見つからないか、まだ読み込まれていません</translation>
+ <translation type="vanished">そのコミットは、見つからないか、まだ読み込まれていません</translation>
</message>
<message>
<source>Failed to delete the branch &apos;%1&apos;
</source>
<translation type="vanished">ブランチの削除に失敗しました : &apos;%1&apos;</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>コミット</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>コメント</translation>
+ <translation type="vanished">コメント</translation>
</message>
<message>
<location filename="../../ReflogWindow.cpp" line="98"/>
<source>Checkout</source>
<translation>チェックアウト</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>&amp;Remote menu</source>
<translation>リモートメニュー(&amp;R)</translation>
</message>
<message>
<location filename="../../RepositoryPropertyDialog.ui" line="189"/>
<source>Close</source>
<translation>閉じる</translation>
</message>
<message>
- <location filename="../../RepositoryPropertyDialog.cpp" line="128"/>
+ <location filename="../../RepositoryPropertyDialog.cpp" line="132"/>
<source>Confirm Remove</source>
<translation>削除の確認</translation>
</message>
<message>
- <location filename="../../RepositoryPropertyDialog.cpp" line="128"/>
+ <location filename="../../RepositoryPropertyDialog.cpp" line="132"/>
<source>Are you sure you want to remove the remote &apos;%1&apos; from the repository &apos;%2&apos; ?</source>
<translation>リポジトリ「%2」からリモート「%1」を削除してよろしいですか?</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>&amp;Browse...</source>
<translation>参照(&amp;B)...</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 &apos;%1&apos; command you want to use.</source>
<translation>使用したい &apos;%1&apos; コマンドを選択してください。</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>&amp;Test</source>
<translation>テスト(&amp;T)</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&apos;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>GPG署名ポリシー</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>高精細画面のスケーリングを行う</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>&lt;&lt; Prev</source>
<translation>&lt;&lt; 戻る</translation>
</message>
<message>
<location filename="../../WelcomeWizardDialog.ui" line="433"/>
<location filename="../../WelcomeWizardDialog.cpp" line="148"/>
<source>Next &gt;&gt;</source>
<translation>次へ &gt;&gt;</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

Mime Type
text/x-diff
Expires
Fri, Jun 12, 12:51 PM (3 w, 1 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
70253
Default Alt Text
(261 KB)

Event Timeline