Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
259 KB
Referenced Files
None
Subscribers
None
This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/Guitar.pro b/Guitar.pro
index 9c084f2..bf97b90 100644
--- a/Guitar.pro
+++ b/Guitar.pro
@@ -1,388 +1,391 @@
QT += core gui widgets svg network
win32:QT += winextras
CONFIG(debug,debug|release):TARGET = Guitard
CONFIG(release,debug|release):TARGET = Guitar
TEMPLATE = app
CONFIG += c++11
DESTDIR = $$PWD/_bin
TRANSLATIONS = Guitar_ja.ts
DEFINES += APP_GUITAR
DEFINES += USE_DARK_THEME
DEFINES += HAVE_POSIX_OPENPT
macx:DEFINES += HAVE_SYS_TIME_H
macx:DEFINES += HAVE_UTMPX
gcc:QMAKE_CXXFLAGS += -Wall -Wextra -Werror=return-type -Werror=trigraphs -Wno-switch -Wno-reorder
linux:QMAKE_RPATHDIR += $ORIGIN
macx:QMAKE_RPATHDIR += @executable_path/../Frameworks
linux:QTPLUGIN += ibusplatforminputcontextplugin
#linux:QTPLUGIN += fcitxplatforminputcontextplugin
INCLUDEPATH += $$PWD/src
INCLUDEPATH += $$PWD/src/texteditor
win32:INCLUDEPATH += $$PWD/winpty
win32:LIBS += $$PWD/winpty/winpty.lib
# OpenSSL
linux:LIBS += -lssl -lcrypto
haiku:LIBS += -lssl -lcrypto -lnetwork
macx:INCLUDEPATH += /usr/local/include
macx:LIBS += /usr/local/lib/libssl.a /usr/local/lib/libcrypto.a
win32:msvc {
INCLUDEPATH += C:\openssl\include
LIBS += -LC:\openssl\lib
}
win32:gcc {
INCLUDEPATH += C:\Qt\Tools\mingw530_32\opt\include
LIBS += -LC:\Qt\Tools\mingw530_32\opt\lib
LIBS += -lcrypto -lssl
}
# execute 'ruby prepare.rb' automatically
prepare.target = prepare
prepare.commands = cd $$PWD && ruby -W0 prepare.rb
QMAKE_EXTRA_TARGETS += prepare
PRE_TARGETDEPS += prepare
# zlib
win32:msvc {
CONFIG(debug, debug|release):LIBS += $$PWD/_bin/libz.lib
CONFIG(release, debug|release):LIBS += $$PWD/_bin/libz.lib
}
win32:gcc {
CONFIG(debug, debug|release):LIBS += $$PWD/_bin/liblibz.a
CONFIG(release, debug|release):LIBS += $$PWD/_bin/liblibz.a
}
!haiku {
unix:CONFIG(debug, debug|release):LIBS += $$PWD/_bin/libzd.a
unix:CONFIG(release, debug|release):LIBS += $$PWD/_bin/libz.a
#unix:LIBS += -lz
}
haiku:LIBS += -lz
win32 {
LIBS += -ladvapi32 -lshell32 -luser32 -lws2_32
RC_FILE = win.rc
QMAKE_SUBSYSTEM_SUFFIX=,5.01
}
macx {
QMAKE_INFO_PLIST = Info.plist
ICON += Guitar.icns
t.path=Contents/Resources
QMAKE_BUNDLE_DATA += t
}
SOURCES += \
version.c \
src/main.cpp\
src/MainWindow.cpp \
src/Git.cpp \
src/common/joinpath.cpp \
src/common/misc.cpp \
src/ConfigCredentialHelperDialog.cpp \
src/MySettings.cpp \
src/FileDiffWidget.cpp \
src/TextEditDialog.cpp \
src/LogTableWidget.cpp \
src/FileDiffSliderWidget.cpp \
src/FileUtil.cpp \
src/SettingsDialog.cpp \
src/MergeBranchDialog.cpp \
src/CloneDialog.cpp \
src/AboutDialog.cpp \
src/RepositoryInfoFrame.cpp \
src/RepositoryPropertyDialog.cpp \
src/RepositoryData.cpp \
src/MyToolButton.cpp \
src/GitDiff.cpp \
src/CommitPropertyDialog.cpp \
src/Terminal.cpp \
src/DeleteTagsDialog.cpp \
src/RepositoriesTreeWidget.cpp \
src/SelectCommandDialog.cpp \
src/ImageViewWidget.cpp \
src/FileHistoryWindow.cpp \
src/GitPackIdxV2.cpp \
src/GitPack.cpp \
src/GitObjectManager.cpp \
src/FilePropertyDialog.cpp \
src/BigDiffWindow.cpp \
src/MaximizeButton.cpp \
src/CommitExploreWindow.cpp \
src/ReadOnlyLineEdit.cpp \
src/ReadOnlyPlainTextEdit.cpp \
src/MyTableWidgetDelegate.cpp \
src/SetRemoteUrlDialog.cpp \
src/ClearButton.cpp \
src/SetUserDialog.cpp \
src/SearchFromGitHubDialog.cpp \
src/webclient.cpp \
src/charvec.cpp \
src/urlencode.cpp \
src/HyperLinkLabel.cpp \
src/JumpDialog.cpp \
src/CheckoutDialog.cpp \
src/DeleteBranchDialog.cpp \
src/BasicRepositoryDialog.cpp \
src/RemoteRepositoriesTableWidget.cpp \
src/LocalSocketReader.cpp \
src/PushDialog.cpp \
src/StatusLabel.cpp \
src/RepositoryLineEdit.cpp \
src/DirectoryLineEdit.cpp \
src/AbstractSettingForm.cpp \
src/SettingExampleForm.cpp \
src/CreateRepositoryDialog.cpp \
src/GitHubAPI.cpp \
src/MemoryReader.cpp \
src/ExperimentDialog.cpp \
src/gunzip.cpp \
src/AvatarLoader.cpp \
src/SettingNetworkForm.cpp \
src/Photoshop.cpp \
src/SettingBehaviorForm.cpp \
src/MyProcess.cpp \
src/FileViewWidget.cpp \
src/MyTextEditorWidget.cpp \
src/AbstractProcess.cpp \
src/texteditor/AbstractCharacterBasedApplication.cpp \
src/texteditor/InputMethodPopup.cpp \
src/texteditor/TextEditorTheme.cpp \
src/texteditor/TextEditorWidget.cpp \
src/texteditor/unicode.cpp \
src/texteditor/UnicodeWidth.cpp \
src/MyImageViewWidget.cpp \
src/SetGlobalUserDialog.cpp \
src/ReflogWindow.cpp \
src/Theme.cpp \
src/ApplicationGlobal.cpp \
src/BlameWindow.cpp \
src/MenuButton.cpp \
src/SettingGeneralForm.cpp \
src/WelcomeWizardDialog.cpp \
src/DialogHeaderFrame.cpp \
src/CommitViewWindow.cpp \
src/EditRemoteDialog.cpp \
src/gpg.cpp \
src/SelectGpgKeyDialog.cpp \
src/SetGpgSigningDialog.cpp \
src/CommitDialog.cpp \
src/ConfigSigningDialog.cpp \
src/AreYouSureYouWantToContinueConnectingDialog.cpp \
src/LineEditDialog.cpp \
src/SettingProgramsForm.cpp \
src/InputNewTagDialog.cpp \
src/EditTagsDialog.cpp \
darktheme/src/DarkStyle.cpp \
darktheme/src/NinePatch.cpp \
darktheme/src/StandardStyle.cpp \
darktheme/src/TraditionalWindowsStyleTreeControl.cpp \
src/EditGitIgnoreDialog.cpp \
src/SelectItemDialog.cpp \
- src/RebaseOntoDialog.cpp
+ src/RebaseOntoDialog.cpp \
+ src/DoYouWantToInitDialog.cpp
HEADERS += \
src/MainWindow.h \
src/Git.h \
src/common/joinpath.h \
src/common/misc.h \
src/ConfigCredentialHelperDialog.h \
src/MySettings.h \
src/main.h \
src/FileDiffWidget.h \
src/TextEditDialog.h \
src/LogTableWidget.h \
src/FileDiffSliderWidget.h \
src/FileUtil.h \
src/SettingsDialog.h \
src/MergeBranchDialog.h \
src/CloneDialog.h \
src/AboutDialog.h \
src/RepositoryInfoFrame.h \
src/RepositoryPropertyDialog.h \
src/RepositoryData.h \
src/MyToolButton.h \
src/GitDiff.h \
src/CommitPropertyDialog.h \
src/Terminal.h \
src/DeleteTagsDialog.h \
src/RepositoriesTreeWidget.h \
src/SelectCommandDialog.h \
src/ImageViewWidget.h \
src/FileHistoryWindow.h \
src/Debug.h \
src/GitPackIdxV2.h \
src/GitPack.h \
src/GitObjectManager.h \
src/FilePropertyDialog.h \
src/BigDiffWindow.h \
src/MaximizeButton.h \
src/CommitExploreWindow.h \
src/ReadOnlyLineEdit.h \
src/ReadOnlyPlainTextEdit.h \
src/MyTableWidgetDelegate.h \
src/SetRemoteUrlDialog.h \
myzlib.h \
src/ClearButton.h \
src/SetUserDialog.h \
src/SearchFromGitHubDialog.h \
src/webclient.h \
src/charvec.h \
src/urlencode.h \
src/HyperLinkLabel.h \
src/JumpDialog.h \
src/CheckoutDialog.h \
src/DeleteBranchDialog.h \
src/BasicRepositoryDialog.h \
src/RemoteRepositoriesTableWidget.h \
src/LocalSocketReader.h \
src/PushDialog.h \
src/StatusLabel.h \
src/RepositoryLineEdit.h \
src/DirectoryLineEdit.h \
src/AbstractSettingForm.h \
src/SettingExampleForm.h \
src/CreateRepositoryDialog.h \
src/GitHubAPI.h \
src/MemoryReader.h \
src/ExperimentDialog.h \
src/gunzip.h \
src/AvatarLoader.h \
src/SettingNetworkForm.h \
src/Photoshop.h \
src/SettingBehaviorForm.h \
src/MyProcess.h \
src/MyTextEditorWidget.h \
src/AbstractProcess.h \
src/texteditor/AbstractCharacterBasedApplication.h \
src/texteditor/InputMethodPopup.h \
src/texteditor/TextEditorTheme.h \
src/texteditor/TextEditorWidget.h \
src/texteditor/unicode.h \
src/texteditor/UnicodeWidth.h \
src/MyImageViewWidget.h \
src/SetGlobalUserDialog.h \
src/ReflogWindow.h \
src/Theme.h \
src/ApplicationGlobal.h \
src/BlameWindow.h \
src/MenuButton.h \
src/SettingGeneralForm.h \
src/WelcomeWizardDialog.h \
src/DialogHeaderFrame.h \
src/CommitViewWindow.h \
src/EditRemoteDialog.h \
src/gpg.h \
src/SelectGpgKeyDialog.h \
src/SetGpgSigningDialog.h \
src/CommitDialog.h \
src/ConfigSigningDialog.h \
src/AreYouSureYouWantToContinueConnectingDialog.h \
src/LineEditDialog.h \
src/SettingProgramsForm.h \
src/InputNewTagDialog.h \
src/EditTagsDialog.h \
darktheme/src/DarkStyle.h \
darktheme/src/NinePatch.h \
darktheme/src/StandardStyle.h \
darktheme/src/TraditionalWindowsStyleTreeControl.h \
src/EditGitIgnoreDialog.h \
src/SelectItemDialog.h \
- src/RebaseOntoDialog.h
+ src/RebaseOntoDialog.h \
+ src/DoYouWantToInitDialog.h
FORMS += \
src/MainWindow.ui \
src/ConfigCredentialHelperDialog.ui \
src/TextEditDialog.ui \
src/SettingsDialog.ui \
src/MergeBranchDialog.ui \
src/CloneDialog.ui \
src/AboutDialog.ui \
src/RepositoryPropertyDialog.ui \
src/CommitPropertyDialog.ui \
src/DeleteTagsDialog.ui \
src/SelectCommandDialog.ui \
src/FileDiffWidget.ui \
src/FileHistoryWindow.ui \
src/FilePropertyDialog.ui \
src/BigDiffWindow.ui \
src/CommitExploreWindow.ui \
src/SetRemoteUrlDialog.ui \
src/SetUserDialog.ui \
src/SearchFromGitHubDialog.ui \
src/JumpDialog.ui \
src/CheckoutDialog.ui \
src/DeleteBranchDialog.ui \
src/PushDialog.ui \
src/SettingExampleForm.ui \
src/CreateRepositoryDialog.ui \
src/ExperimentDialog.ui \
src/SettingNetworkForm.ui \
src/SettingBehaviorForm.ui \
src/SetGlobalUserDialog.ui \
src/ReflogWindow.ui \
src/BlameWindow.ui \
src/SettingGeneralForm.ui \
src/WelcomeWizardDialog.ui \
src/CommitViewWindow.ui \
src/EditRemoteDialog.ui \
src/SelectGpgKeyDialog.ui \
src/SetGpgSigningDialog.ui \
src/CommitDialog.ui \
src/ConfigSigningDialog.ui \
src/AreYouSureYouWantToContinueConnectingDialog.ui \
src/LineEditDialog.ui \
src/SettingProgramsForm.ui \
src/InputNewTagDialog.ui \
src/EditTagsDialog.ui \
src/EditGitIgnoreDialog.ui \
src/SelectItemDialog.ui \
- src/RebaseOntoDialog.ui
+ src/RebaseOntoDialog.ui \
+ src/DoYouWantToInitDialog.ui
RESOURCES += \
resources.qrc
unix {
SOURCES += \
src/unix/UnixProcess.cpp \
src/unix/UnixPtyProcess.cpp
HEADERS += \
src/unix/UnixProcess.h \
src/unix/UnixPtyProcess.h
}
win32 {
SOURCES += \
src/win32/thread.cpp \
src/win32/event.cpp \
src/win32/win32.cpp \
src/win32/Win32Process.cpp \
src/win32/Win32PtyProcess.cpp
HEADERS += \
src/win32/thread.h \
src/win32/event.h \
src/win32/mutex.h \
src/win32/win32.h \
src/win32/Win32Process.h \
src/win32/Win32PtyProcess.h
}
diff --git a/Guitar_ja.qm b/Guitar_ja.qm
index 5789eb9..f82b5ef 100644
Binary files a/Guitar_ja.qm and b/Guitar_ja.qm differ
diff --git a/Guitar_ja.ts b/Guitar_ja.ts
index ad79f36..151fc55 100644
--- a/Guitar_ja.ts
+++ b/Guitar_ja.ts
@@ -1,2377 +1,2449 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="ja_JP">
<context>
<name>AboutDialog</name>
<message>
<location filename="src/AboutDialog.ui" line="14"/>
<source>Dialog</source>
<translation></translation>
</message>
<message>
<location filename="src/AboutDialog.ui" line="29"/>
<location filename="src/AboutDialog.ui" line="36"/>
<location filename="src/AboutDialog.ui" line="43"/>
<location filename="src/AboutDialog.ui" line="50"/>
<source>TextLabel</source>
<translation></translation>
</message>
<message>
<location filename="src/AboutDialog.cpp" line="28"/>
<source>About %1</source>
<translation>%1 について</translation>
</message>
</context>
<context>
<name>AreYouSureYouWantToContinueConnectingDialog</name>
<message>
<location filename="src/AreYouSureYouWantToContinueConnectingDialog.ui" line="14"/>
<source>Unknown Host</source>
<translation>不明なホスト</translation>
</message>
<message>
<location filename="src/AreYouSureYouWantToContinueConnectingDialog.ui" line="20"/>
<source>Are you sure you want to continue connecting (yes/no)?</source>
<translation>接続を続行しますか? (yes/no)?</translation>
</message>
<message>
<location filename="src/AreYouSureYouWantToContinueConnectingDialog.ui" line="52"/>
<source>Continue</source>
<translation>続行</translation>
</message>
<message>
<location filename="src/AreYouSureYouWantToContinueConnectingDialog.ui" line="59"/>
<source>Close</source>
<translation>閉じる</translation>
</message>
</context>
<context>
<name>BasicRepositoryDialog</name>
<message>
<location filename="src/BasicRepositoryDialog.cpp" line="66"/>
<source>Name</source>
<translation>名前</translation>
</message>
<message>
<location filename="src/BasicRepositoryDialog.cpp" line="67"/>
<source>Purpose</source>
<translation>用途</translation>
</message>
<message>
<location filename="src/BasicRepositoryDialog.cpp" line="68"/>
<source>URL</source>
<translation></translation>
</message>
</context>
<context>
<name>BigDiffWindow</name>
<message>
<location filename="src/BigDiffWindow.ui" line="14"/>
<source>Diff</source>
<translation>差分</translation>
</message>
</context>
<context>
<name>BlameWindow</name>
<message>
<location filename="src/BlameWindow.ui" line="14"/>
<source>Blame</source>
<translation></translation>
</message>
<message>
<location filename="src/BlameWindow.ui" line="45"/>
<source>Information</source>
<translation>情報</translation>
</message>
<message>
- <location filename="src/BlameWindow.ui" line="54"/>
+ <location filename="src/BlameWindow.ui" line="60"/>
<source>Commit</source>
<translation>コミット</translation>
</message>
<message>
- <location filename="src/BlameWindow.ui" line="64"/>
+ <location filename="src/BlameWindow.ui" line="70"/>
<source>Date</source>
<translation>日付</translation>
</message>
<message>
- <location filename="src/BlameWindow.ui" line="74"/>
+ <location filename="src/BlameWindow.ui" line="90"/>
<source>Message</source>
<translation>メッセージ</translation>
</message>
<message>
- <location filename="src/BlameWindow.ui" line="84"/>
+ <location filename="src/BlameWindow.ui" line="80"/>
<source>Author</source>
<translation>名前</translation>
</message>
<message>
- <location filename="src/BlameWindow.ui" line="112"/>
+ <location filename="src/BlameWindow.ui" line="118"/>
<source>Close</source>
<translation>閉じる</translation>
</message>
</context>
<context>
<name>CheckoutDialog</name>
<message>
<location filename="src/CheckoutDialog.ui" line="14"/>
<source>Checkout</source>
<translation>チェックアウト</translation>
</message>
<message>
<location filename="src/CheckoutDialog.ui" line="20"/>
<location filename="src/CheckoutDialog.ui" line="27"/>
<location filename="src/CheckoutDialog.ui" line="34"/>
<source>RadioButton</source>
<translation></translation>
</message>
<message>
<location filename="src/CheckoutDialog.ui" line="63"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="src/CheckoutDialog.ui" line="70"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
</context>
<context>
<name>CloneDialog</name>
<message>
<location filename="src/CloneDialog.ui" line="14"/>
<location filename="src/CloneDialog.ui" line="103"/>
<source>Clone</source>
<translation>クローン</translation>
</message>
<message>
<location filename="src/CloneDialog.ui" line="22"/>
<source>Remote</source>
<translation>リモート</translation>
</message>
<message>
<location filename="src/CloneDialog.ui" line="39"/>
<source>&amp;Test</source>
<translation>テスト(&amp;T)</translation>
</message>
<message>
<location filename="src/CloneDialog.ui" line="46"/>
<source>Local</source>
<translation>ローカル</translation>
</message>
<message>
<location filename="src/CloneDialog.ui" line="56"/>
<source>Browse</source>
<translation>参照</translation>
</message>
<message>
<location filename="src/CloneDialog.ui" line="83"/>
<source>Open existing local directory...</source>
<translation>既存のフォルダを開く...</translation>
</message>
<message>
<location filename="src/CloneDialog.ui" line="113"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
<message>
<location filename="src/CloneDialog.cpp" line="40"/>
<source>Search</source>
<translation>検索</translation>
</message>
<message>
<location filename="src/CloneDialog.cpp" line="41"/>
<source>GitHub</source>
<translation></translation>
</message>
<message>
<location filename="src/CloneDialog.cpp" line="108"/>
<source>Checkout into</source>
<translation>ここにチェックアウト</translation>
</message>
<message>
<location filename="src/CloneDialog.cpp" line="120"/>
<source>Open existing directory</source>
<translation>既存のフォンるだを開く</translation>
</message>
</context>
<context>
<name>CommitDialog</name>
<message>
<location filename="src/CommitDialog.ui" line="14"/>
<source>Commit</source>
<translation>コミット</translation>
</message>
<message>
<location filename="src/CommitDialog.ui" line="27"/>
<source>TextLabel</source>
<translation></translation>
</message>
<message>
- <location filename="src/CommitDialog.ui" line="46"/>
+ <location filename="src/CommitDialog.ui" line="49"/>
<source>Author</source>
<translation>名前</translation>
</message>
<message>
- <location filename="src/CommitDialog.ui" line="59"/>
- <location filename="src/CommitDialog.ui" line="79"/>
- <location filename="src/CommitDialog.ui" line="124"/>
- <location filename="src/CommitDialog.ui" line="144"/>
- <location filename="src/CommitDialog.ui" line="164"/>
+ <location filename="src/CommitDialog.ui" line="68"/>
+ <location filename="src/CommitDialog.ui" line="94"/>
+ <location filename="src/CommitDialog.ui" line="139"/>
+ <location filename="src/CommitDialog.ui" line="159"/>
+ <location filename="src/CommitDialog.ui" line="179"/>
<source>---</source>
<translation></translation>
</message>
<message>
- <location filename="src/CommitDialog.ui" line="66"/>
- <location filename="src/CommitDialog.ui" line="151"/>
+ <location filename="src/CommitDialog.ui" line="75"/>
+ <location filename="src/CommitDialog.ui" line="166"/>
<source>Mail</source>
<translation>メール</translation>
</message>
<message>
- <location filename="src/CommitDialog.ui" line="96"/>
+ <location filename="src/CommitDialog.ui" line="111"/>
<source>GPG Signing</source>
<translation>GPG署名</translation>
</message>
<message>
- <location filename="src/CommitDialog.ui" line="111"/>
+ <location filename="src/CommitDialog.ui" line="126"/>
<source>ID</source>
<translation></translation>
</message>
<message>
- <location filename="src/CommitDialog.ui" line="131"/>
+ <location filename="src/CommitDialog.ui" line="146"/>
<source>Name</source>
<translation>名前</translation>
</message>
<message>
- <location filename="src/CommitDialog.ui" line="174"/>
+ <location filename="src/CommitDialog.ui" line="189"/>
<source>Configure...</source>
<translation>設定...</translation>
</message>
<message>
- <location filename="src/CommitDialog.ui" line="183"/>
+ <location filename="src/CommitDialog.ui" line="198"/>
<source>Message</source>
<translation>メッセージ</translation>
</message>
<message>
- <location filename="src/CommitDialog.ui" line="208"/>
+ <location filename="src/CommitDialog.ui" line="223"/>
<source>OK</source>
<translation></translation>
</message>
<message>
- <location filename="src/CommitDialog.ui" line="218"/>
+ <location filename="src/CommitDialog.ui" line="233"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
</context>
<context>
<name>CommitExploreWindow</name>
<message>
<location filename="src/CommitExploreWindow.ui" line="14"/>
<source>Commit Explorer</source>
<translation>コミットエクスプローラー</translation>
</message>
<message>
<location filename="src/CommitExploreWindow.ui" line="65"/>
<source>Commit ID</source>
<translation>コミットID</translation>
</message>
<message>
<location filename="src/CommitExploreWindow.ui" line="82"/>
<source>Date</source>
<translation>日付</translation>
</message>
<message>
<location filename="src/CommitExploreWindow.ui" line="89"/>
<source>Author</source>
<translation>名前</translation>
</message>
<message>
<location filename="src/CommitExploreWindow.cpp" line="80"/>
<source>Commit</source>
<translation>コミット</translation>
</message>
</context>
<context>
<name>CommitPropertyDialog</name>
<message>
<location filename="src/CommitPropertyDialog.ui" line="14"/>
<source>Commit Property</source>
<oldsource>Commit Properties</oldsource>
<translation>コミットのプロパティ</translation>
</message>
<message>
- <location filename="src/CommitPropertyDialog.ui" line="43"/>
+ <location filename="src/CommitPropertyDialog.ui" line="46"/>
<source>Date</source>
<translation>日付</translation>
</message>
<message>
- <location filename="src/CommitPropertyDialog.ui" line="53"/>
+ <location filename="src/CommitPropertyDialog.ui" line="56"/>
<source>Author</source>
<translation>名前</translation>
</message>
<message>
- <location filename="src/CommitPropertyDialog.ui" line="63"/>
- <location filename="src/CommitPropertyDialog.ui" line="187"/>
+ <location filename="src/CommitPropertyDialog.ui" line="66"/>
+ <location filename="src/CommitPropertyDialog.ui" line="196"/>
<source>Mail</source>
<translation>メール</translation>
</message>
<message>
- <location filename="src/CommitPropertyDialog.ui" line="151"/>
+ <location filename="src/CommitPropertyDialog.ui" line="154"/>
<source>GPG Sign</source>
<translation>GPG署名</translation>
</message>
<message>
- <location filename="src/CommitPropertyDialog.ui" line="167"/>
+ <location filename="src/CommitPropertyDialog.ui" line="176"/>
<source>ID</source>
<translation></translation>
</message>
<message>
- <location filename="src/CommitPropertyDialog.ui" line="177"/>
+ <location filename="src/CommitPropertyDialog.ui" line="186"/>
<source>Name</source>
<translation>名前</translation>
</message>
<message>
- <location filename="src/CommitPropertyDialog.ui" line="224"/>
+ <location filename="src/CommitPropertyDialog.ui" line="233"/>
<source>Commit ID</source>
<translation>コミットID</translation>
</message>
<message>
- <location filename="src/CommitPropertyDialog.ui" line="234"/>
+ <location filename="src/CommitPropertyDialog.ui" line="243"/>
<source>Parent IDs</source>
<translation>親ID</translation>
</message>
<message>
- <location filename="src/CommitPropertyDialog.ui" line="256"/>
+ <location filename="src/CommitPropertyDialog.ui" line="265"/>
<source>Files...</source>
<translation>ファイル...</translation>
</message>
<message>
- <location filename="src/CommitPropertyDialog.ui" line="276"/>
+ <location filename="src/CommitPropertyDialog.ui" line="285"/>
<source>Explorer</source>
<translation>エクスプローラ</translation>
</message>
<message>
- <location filename="src/CommitPropertyDialog.ui" line="296"/>
+ <location filename="src/CommitPropertyDialog.ui" line="305"/>
<source>Checkout</source>
<translation>チェックアウト</translation>
</message>
<message>
- <location filename="src/CommitPropertyDialog.ui" line="303"/>
+ <location filename="src/CommitPropertyDialog.ui" line="312"/>
<source>Jump</source>
<translation>移動</translation>
</message>
<message>
- <location filename="src/CommitPropertyDialog.ui" line="323"/>
+ <location filename="src/CommitPropertyDialog.ui" line="332"/>
<source>Close</source>
<translation>閉じる</translation>
</message>
<message>
<location filename="src/CommitPropertyDialog.cpp" line="59"/>
<source>&lt;Unknown&gt;</source>
<translation>&lt;不明&gt;</translation>
</message>
</context>
<context>
<name>CommitViewWindow</name>
<message>
<location filename="src/CommitViewWindow.ui" line="14"/>
<source>Commit View</source>
<translation>コミットビュー</translation>
</message>
<message>
<location filename="src/CommitViewWindow.cpp" line="58"/>
<source>History</source>
<translation>履歴</translation>
</message>
</context>
<context>
<name>ConfigCredentialHelperDialog</name>
<message>
<location filename="src/ConfigCredentialHelperDialog.ui" line="14"/>
<source>Dialog</source>
<translation></translation>
</message>
<message>
<location filename="src/ConfigCredentialHelperDialog.ui" line="26"/>
<source>wincred</source>
<translation></translation>
</message>
<message>
<location filename="src/ConfigCredentialHelperDialog.ui" line="39"/>
<source>winstore</source>
<translation></translation>
</message>
<message>
<location filename="src/ConfigCredentialHelperDialog.ui" line="52"/>
<source>None</source>
<translation>なし</translation>
</message>
<message>
<location filename="src/ConfigCredentialHelperDialog.ui" line="65"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="src/ConfigCredentialHelperDialog.ui" line="78"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
<message>
<location filename="src/ConfigCredentialHelperDialog.ui" line="91"/>
<source>Other</source>
<translation>その他</translation>
</message>
</context>
<context>
<name>ConfigSigningDialog</name>
<message>
<location filename="src/ConfigSigningDialog.ui" line="14"/>
<source>Signing Policy</source>
<translation>署名ポリシー</translation>
</message>
<message>
<location filename="src/ConfigSigningDialog.ui" line="20"/>
<source>Config commit.gpgsign</source>
<translation>commit.gpgsign の設定</translation>
</message>
<message>
- <location filename="src/ConfigSigningDialog.ui" line="26"/>
+ <location filename="src/ConfigSigningDialog.ui" line="32"/>
<source>global</source>
<translation></translation>
</message>
<message>
- <location filename="src/ConfigSigningDialog.ui" line="36"/>
+ <location filename="src/ConfigSigningDialog.ui" line="42"/>
<source>local</source>
<translation></translation>
</message>
<message>
- <location filename="src/ConfigSigningDialog.ui" line="64"/>
+ <location filename="src/ConfigSigningDialog.ui" line="70"/>
<source>OK</source>
<translation></translation>
</message>
<message>
- <location filename="src/ConfigSigningDialog.ui" line="71"/>
+ <location filename="src/ConfigSigningDialog.ui" line="77"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
</context>
<context>
<name>CreateRepositoryDialog</name>
<message>
<location filename="src/CreateRepositoryDialog.ui" line="14"/>
<location filename="src/CreateRepositoryDialog.cpp" line="42"/>
<location filename="src/CreateRepositoryDialog.cpp" line="46"/>
<location filename="src/CreateRepositoryDialog.cpp" line="50"/>
<location filename="src/CreateRepositoryDialog.cpp" line="54"/>
<source>Create Repository</source>
<translation>リポジトリの作成</translation>
</message>
<message>
<location filename="src/CreateRepositoryDialog.ui" line="22"/>
<source>Path</source>
<translation>パス</translation>
</message>
<message>
<location filename="src/CreateRepositoryDialog.ui" line="32"/>
<source>Browse</source>
<translation>参照</translation>
</message>
<message>
<location filename="src/CreateRepositoryDialog.ui" line="54"/>
<source>Bookmark</source>
<translation>ブックマーク</translation>
</message>
<message>
<location filename="src/CreateRepositoryDialog.ui" line="63"/>
<location filename="src/CreateRepositoryDialog.ui" line="88"/>
<source>Name</source>
<translation>名前</translation>
</message>
<message>
<location filename="src/CreateRepositoryDialog.ui" line="76"/>
<source>Remote</source>
<translation>リモート</translation>
</message>
<message>
<location filename="src/CreateRepositoryDialog.ui" line="98"/>
<source>URL</source>
<translation></translation>
</message>
<message>
<location filename="src/CreateRepositoryDialog.ui" line="108"/>
<source>Test</source>
<translation>テスト</translation>
</message>
<message>
<location filename="src/CreateRepositoryDialog.ui" line="146"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="src/CreateRepositoryDialog.ui" line="153"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
<message>
<location filename="src/CreateRepositoryDialog.cpp" line="19"/>
<source>A valid git repository already exists there.</source>
<translation>有効なリポジトリが既に存在しています。</translation>
</message>
<message>
<location filename="src/CreateRepositoryDialog.cpp" line="42"/>
<location filename="src/CreateRepositoryDialog.cpp" line="50"/>
<source>The specified path is not a directory.</source>
<translation>指定されたパスはフォルダではありません。</translation>
</message>
<message>
<location filename="src/CreateRepositoryDialog.cpp" line="54"/>
<source>Remote name is invalid.</source>
<translation>リモート名が無効です。</translation>
</message>
<message>
<location filename="src/CreateRepositoryDialog.cpp" line="62"/>
<source>Destination Path</source>
<translation>作成先のパス</translation>
</message>
</context>
<context>
<name>DeleteBranchDialog</name>
<message>
<location filename="src/DeleteBranchDialog.ui" line="14"/>
<source>Delete Branch</source>
<translation>ブランチの削除</translation>
</message>
<message>
<location filename="src/DeleteBranchDialog.ui" line="25"/>
<source>&amp;All branches</source>
<translation>全てのブランチ(&amp;A)</translation>
</message>
<message>
<location filename="src/DeleteBranchDialog.ui" line="45"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="src/DeleteBranchDialog.ui" line="52"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
<message>
<location filename="src/DeleteBranchDialog.cpp" line="23"/>
<source>Delete Remote Branch</source>
<translation>リモートブランチの削除</translation>
</message>
</context>
<context>
<name>DeleteTagsDialog</name>
<message>
<location filename="src/DeleteTagsDialog.ui" line="14"/>
<source>Delete tags</source>
<translation>タグを削除</translation>
</message>
<message>
<location filename="src/DeleteTagsDialog.ui" line="32"/>
<source>Check all</source>
<translation>全てチェック</translation>
</message>
<message>
<location filename="src/DeleteTagsDialog.ui" line="52"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="src/DeleteTagsDialog.ui" line="62"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
</context>
+<context>
+ <name>DoYouWantToInitDialog</name>
+ <message>
+ <location filename="src/DoYouWantToInitDialog.ui" line="14"/>
+ <source>Init Repository</source>
+ <translation>リポジトリの初期化</translation>
+ </message>
+ <message>
+ <location filename="src/DoYouWantToInitDialog.ui" line="20"/>
+ <source>The folder:</source>
+ <translation>フォルダ:</translation>
+ </message>
+ <message>
+ <location filename="src/DoYouWantToInitDialog.ui" line="32"/>
+ <source>is not a valid git working copy.</source>
+ <translation>は、有効なGitリポジトリではありません。</translation>
+ </message>
+ <message>
+ <location filename="src/DoYouWantToInitDialog.ui" line="39"/>
+ <source>Do you want to init it as a git repository ?</source>
+ <translation>これをGitリポジトリとして初期化しますか?</translation>
+ </message>
+ <message>
+ <location filename="src/DoYouWantToInitDialog.ui" line="81"/>
+ <source>Yes, Please init it.</source>
+ <translation>はい、初期化します。</translation>
+ </message>
+ <message>
+ <location filename="src/DoYouWantToInitDialog.ui" line="88"/>
+ <source>No, Stop it.</source>
+ <translation>いいえ、初期化しません。</translation>
+ </message>
+ <message>
+ <location filename="src/DoYouWantToInitDialog.ui" line="140"/>
+ <source>Please choose option</source>
+ <translation>選択してください</translation>
+ </message>
+ <message>
+ <location filename="src/DoYouWantToInitDialog.cpp" line="20"/>
+ <source>Next...</source>
+ <translation>次へ...</translation>
+ </message>
+ <message>
+ <location filename="src/DoYouWantToInitDialog.cpp" line="26"/>
+ <source>Cancel</source>
+ <translation>キャンセル</translation>
+ </message>
+</context>
<context>
<name>EditGitIgnoreDialog</name>
<message>
<location filename="src/EditGitIgnoreDialog.ui" line="14"/>
<source>Edit Git Ignore</source>
<translation>Gitで無視するファイルの編集</translation>
</message>
<message>
<location filename="src/EditGitIgnoreDialog.ui" line="20"/>
<location filename="src/EditGitIgnoreDialog.ui" line="27"/>
<location filename="src/EditGitIgnoreDialog.ui" line="34"/>
<location filename="src/EditGitIgnoreDialog.ui" line="41"/>
<source>RadioButton</source>
<translation></translation>
</message>
<message>
<location filename="src/EditGitIgnoreDialog.ui" line="63"/>
<source>Edit the file</source>
<translation>ファイルを編集する</translation>
</message>
<message>
<location filename="src/EditGitIgnoreDialog.ui" line="83"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="src/EditGitIgnoreDialog.ui" line="90"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
</context>
<context>
<name>EditRemoteDialog</name>
<message>
<location filename="src/EditRemoteDialog.ui" line="14"/>
<source>Edit Remote</source>
<translation>リモートの編集</translation>
</message>
<message>
<location filename="src/EditRemoteDialog.ui" line="20"/>
<source>Remote</source>
<translation>リモート</translation>
</message>
<message>
<location filename="src/EditRemoteDialog.ui" line="26"/>
<source>Name</source>
<translation>名前</translation>
</message>
<message>
<location filename="src/EditRemoteDialog.ui" line="33"/>
<source>URL</source>
<translation></translation>
</message>
<message>
<location filename="src/EditRemoteDialog.ui" line="53"/>
<source>&amp;Test</source>
<translation>テスト(&amp;T)</translation>
</message>
<message>
<location filename="src/EditRemoteDialog.ui" line="81"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="src/EditRemoteDialog.ui" line="88"/>
<source>Close</source>
<translation>閉じる</translation>
</message>
</context>
<context>
<name>EditTagsDialog</name>
<message>
<location filename="src/EditTagsDialog.ui" line="14"/>
<source>Edit Tags</source>
<translation>タグの編集</translation>
</message>
<message>
<location filename="src/EditTagsDialog.ui" line="41"/>
<source>Add...</source>
<translation>追加...</translation>
</message>
<message>
<location filename="src/EditTagsDialog.ui" line="48"/>
<source>Delete</source>
<translation>削除</translation>
</message>
<message>
<location filename="src/EditTagsDialog.ui" line="68"/>
<source>Close</source>
<translation>閉じる</translation>
</message>
</context>
<context>
<name>ExperimentDialog</name>
<message>
<location filename="src/ExperimentDialog.ui" line="14"/>
<source>Dialog</source>
<translation></translation>
</message>
</context>
<context>
<name>FileDiffWidget</name>
<message>
<location filename="src/FileDiffWidget.ui" line="14"/>
<source>Dialog</source>
<translation></translation>
</message>
</context>
<context>
<name>FileHistoryWindow</name>
<message>
<location filename="src/FileHistoryWindow.ui" line="14"/>
<source>File History</source>
<translation>ファイルの履歴</translation>
</message>
<message>
<location filename="src/FileHistoryWindow.ui" line="20"/>
<location filename="src/FileHistoryWindow.ui" line="27"/>
<source>TextLabel</source>
<translation></translation>
</message>
<message>
<location filename="src/FileHistoryWindow.cpp" line="112"/>
<source>Commit</source>
<translation>コミット</translation>
</message>
<message>
<location filename="src/FileHistoryWindow.cpp" line="113"/>
<source>Date</source>
<translation>日付</translation>
</message>
<message>
<location filename="src/FileHistoryWindow.cpp" line="114"/>
<source>Author</source>
<translation>名前</translation>
</message>
<message>
<location filename="src/FileHistoryWindow.cpp" line="115"/>
<source>Description</source>
<translation>概要</translation>
</message>
</context>
<context>
<name>FilePropertyDialog</name>
<message>
<location filename="src/FilePropertyDialog.ui" line="14"/>
<source>File Property</source>
<oldsource>File Properties</oldsource>
<translation>ファイルのプロパティ</translation>
</message>
<message>
<location filename="src/FilePropertyDialog.ui" line="78"/>
<source>&amp;Close</source>
<translation>閉じる(&amp;C)</translation>
</message>
</context>
+<context>
+ <name>FileViewWidget</name>
+ <message>
+ <location filename="src/FileViewWidget.cpp" line="31"/>
+ <source>Form</source>
+ <translation></translation>
+ </message>
+</context>
<context>
<name>InputNewTagDialog</name>
<message>
<location filename="src/InputNewTagDialog.ui" line="14"/>
<source>Edit tag</source>
<translation>タグの編集</translation>
</message>
<message>
- <location filename="src/InputNewTagDialog.ui" line="28"/>
+ <location filename="src/InputNewTagDialog.ui" line="31"/>
<source>Tag</source>
<translation>タグ</translation>
</message>
<message>
- <location filename="src/InputNewTagDialog.ui" line="55"/>
+ <location filename="src/InputNewTagDialog.ui" line="58"/>
<source>OK</source>
<translation></translation>
</message>
<message>
- <location filename="src/InputNewTagDialog.ui" line="62"/>
+ <location filename="src/InputNewTagDialog.ui" line="65"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
</context>
<context>
<name>JumpDialog</name>
<message>
<location filename="src/JumpDialog.ui" line="14"/>
<source>Jump</source>
<translation>移動</translation>
</message>
<message>
<location filename="src/JumpDialog.ui" line="24"/>
<source>Branches and Tags</source>
<translation>ブランチとタグ</translation>
</message>
<message>
<location filename="src/JumpDialog.ui" line="47"/>
<source>&amp;Filter</source>
<translation>フィルタ(&amp;F)</translation>
</message>
<message>
<location filename="src/JumpDialog.ui" line="98"/>
<source>Find</source>
<translation>検索</translation>
</message>
<message>
<location filename="src/JumpDialog.ui" line="126"/>
<source>&amp;Checkout</source>
<translation>チェックアウト(&amp;C)</translation>
</message>
<message>
<location filename="src/JumpDialog.ui" line="146"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="src/JumpDialog.ui" line="153"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
<message>
<location filename="src/JumpDialog.cpp" line="28"/>
<source>Name</source>
<translation>名前</translation>
</message>
</context>
<context>
<name>LineEditDialog</name>
<message>
<location filename="src/LineEditDialog.ui" line="14"/>
<source>Dialog</source>
<translation></translation>
</message>
<message>
<location filename="src/LineEditDialog.ui" line="20"/>
<source>TextLabel</source>
<translation></translation>
</message>
<message>
<location filename="src/LineEditDialog.ui" line="45"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="src/LineEditDialog.ui" line="52"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="src/MainWindow.ui" line="14"/>
<source>Guitar</source>
<translation></translation>
</message>
<message>
<location filename="src/MainWindow.ui" line="96"/>
- <location filename="src/MainWindow.ui" line="1329"/>
<location filename="src/MainWindow.ui" line="1332"/>
- <location filename="src/MainWindow.cpp" line="3418"/>
- <location filename="src/MainWindow.cpp" line="3423"/>
- <location filename="src/MainWindow.cpp" line="3440"/>
- <location filename="src/MainWindow.cpp" line="3445"/>
+ <location filename="src/MainWindow.ui" line="1335"/>
+ <location filename="src/MainWindow.cpp" line="3452"/>
+ <location filename="src/MainWindow.cpp" line="3457"/>
+ <location filename="src/MainWindow.cpp" line="3474"/>
+ <location filename="src/MainWindow.cpp" line="3479"/>
<source>Clone</source>
<translation>クローン</translation>
</message>
<message>
<location filename="src/MainWindow.ui" line="131"/>
<source>Fetch</source>
<translation>フェッチ</translation>
</message>
<message>
<location filename="src/MainWindow.ui" line="166"/>
<source>Pull</source>
<translation>プル</translation>
</message>
<message>
<location filename="src/MainWindow.ui" line="201"/>
<source>Push</source>
<translation>プッシュ</translation>
</message>
<message>
<location filename="src/MainWindow.ui" line="252"/>
<source>Terminal</source>
<translation>端末</translation>
</message>
<message>
<location filename="src/MainWindow.ui" line="287"/>
<source>Explorer</source>
<translation>エクスプローラ</translation>
</message>
<message>
<location filename="src/MainWindow.ui" line="373"/>
<source>Repository</source>
<translation>リポジトリ</translation>
</message>
<message>
<location filename="src/MainWindow.ui" line="380"/>
<source>Branch Name</source>
<translation>ブランチ名</translation>
</message>
<message>
<location filename="src/MainWindow.ui" line="422"/>
<source>Offline</source>
<translation>オフライン</translation>
</message>
<message>
<location filename="src/MainWindow.ui" line="432"/>
<source>Online</source>
<translation>オンライン</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="638"/>
+ <location filename="src/MainWindow.ui" line="641"/>
<source>PushButton</source>
<translation></translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="752"/>
- <location filename="src/MainWindow.cpp" line="2434"/>
+ <location filename="src/MainWindow.ui" line="755"/>
+ <location filename="src/MainWindow.cpp" line="2468"/>
<source>Unstage</source>
<translation>除外</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="781"/>
+ <location filename="src/MainWindow.ui" line="784"/>
<source>Select all</source>
<translation>全て選択</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="816"/>
- <location filename="src/MainWindow.cpp" line="2304"/>
+ <location filename="src/MainWindow.ui" line="819"/>
+ <location filename="src/MainWindow.cpp" line="2338"/>
<source>Stage</source>
<translation>追加</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="845"/>
- <location filename="src/MainWindow.cpp" line="1205"/>
- <location filename="src/MainWindow.cpp" line="1847"/>
+ <location filename="src/MainWindow.ui" line="848"/>
+ <location filename="src/MainWindow.cpp" line="1212"/>
+ <location filename="src/MainWindow.cpp" line="1876"/>
<source>Commit</source>
<translation>コミット</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="976"/>
+ <location filename="src/MainWindow.ui" line="979"/>
<source>&amp;File</source>
<translation>ファイル(&amp;F)</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="988"/>
+ <location filename="src/MainWindow.ui" line="991"/>
<source>&amp;View</source>
<translation>表示(&amp;V)</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="994"/>
+ <location filename="src/MainWindow.ui" line="997"/>
<source>&amp;Edit</source>
<translation>編集(&amp;E)</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1320"/>
+ <location filename="src/MainWindow.ui" line="1323"/>
<source>Settings</source>
<translation>設定</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1342"/>
- <location filename="src/MainWindow.cpp" line="2176"/>
+ <location filename="src/MainWindow.ui" line="1345"/>
+ <location filename="src/MainWindow.cpp" line="2206"/>
<source>Edit tags...</source>
<translation>タグの編集...</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1345"/>
+ <location filename="src/MainWindow.ui" line="1348"/>
<source>Edit tags</source>
<translation>タグの編集</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1391"/>
+ <location filename="src/MainWindow.ui" line="1394"/>
<source>reset HEAD~1</source>
<translation></translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1396"/>
+ <location filename="src/MainWindow.ui" line="1399"/>
<source>Create a repository</source>
<translation>リポジトリの作成</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1405"/>
+ <location filename="src/MainWindow.ui" line="1408"/>
<source>Stop process</source>
<translation>処理の停止</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1410"/>
+ <location filename="src/MainWindow.ui" line="1413"/>
<source>E&amp;xit</source>
<translation>終了(&amp;X)</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1413"/>
+ <location filename="src/MainWindow.ui" line="1416"/>
<source>Ctrl+Q</source>
<translation></translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1418"/>
+ <location filename="src/MainWindow.ui" line="1421"/>
<source>Reflog...</source>
<translation></translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1423"/>
+ <location filename="src/MainWindow.ui" line="1426"/>
<source>Property...</source>
<translation>プロパティ...</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1428"/>
<location filename="src/MainWindow.ui" line="1431"/>
+ <location filename="src/MainWindow.ui" line="1434"/>
<source>Set GPG signing</source>
<translation>GPS署名の指定</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1006"/>
+ <location filename="src/MainWindow.ui" line="1009"/>
<source>&amp;Help</source>
<translation>ヘルプ(&amp;H)</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1012"/>
+ <location filename="src/MainWindow.ui" line="1015"/>
<source>&amp;Window</source>
<translation>ウィンドウ(&amp;W)</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1018"/>
+ <location filename="src/MainWindow.ui" line="1021"/>
<source>&amp;Repository</source>
<translation>リポジトリ(&amp;R)</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1039"/>
+ <location filename="src/MainWindow.ui" line="1042"/>
<source>Experiment</source>
<translation></translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1066"/>
+ <location filename="src/MainWindow.ui" line="1069"/>
<source>Log</source>
<translation>ログ</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1233"/>
+ <location filename="src/MainWindow.ui" line="1236"/>
<source>&amp;Open existing working copy...</source>
<translation>既存の作業コピーを開く(&amp;O)...</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1236"/>
- <location filename="src/MainWindow.cpp" line="2582"/>
+ <location filename="src/MainWindow.ui" line="1239"/>
+ <location filename="src/MainWindow.cpp" line="2616"/>
<source>Add existing working copy</source>
<translation>既存の作業コピーを追加</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1241"/>
<location filename="src/MainWindow.ui" line="1244"/>
+ <location filename="src/MainWindow.ui" line="1247"/>
<source>Refresh</source>
<translation>更新</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1247"/>
+ <location filename="src/MainWindow.ui" line="1250"/>
<source>F5</source>
<translation></translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1256"/>
+ <location filename="src/MainWindow.ui" line="1259"/>
<source>&amp;Commit</source>
<translation>コミット(&amp;C)</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1265"/>
+ <location filename="src/MainWindow.ui" line="1268"/>
<source>&amp;Push</source>
<translation>プッシュ(&amp;P)</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1273"/>
+ <location filename="src/MainWindow.ui" line="1276"/>
<source>test</source>
<translation></translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1276"/>
+ <location filename="src/MainWindow.ui" line="1279"/>
<source>Ctrl+T</source>
<translation></translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1285"/>
+ <location filename="src/MainWindow.ui" line="1288"/>
<source>Pu&amp;ll</source>
<translation>プル(&amp;L)</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1294"/>
+ <location filename="src/MainWindow.ui" line="1297"/>
<source>&amp;Fetch</source>
<translation>フェッチ(&amp;F)</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1299"/>
<location filename="src/MainWindow.ui" line="1302"/>
+ <location filename="src/MainWindow.ui" line="1305"/>
<source>Edit global .gitconfig</source>
<translation>グローバル .gitignore を編集</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1307"/>
+ <location filename="src/MainWindow.ui" line="1310"/>
<source>Edit .git/config</source>
<translation>.git/config を編集</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1312"/>
+ <location filename="src/MainWindow.ui" line="1315"/>
<source>Edit .gitignore</source>
<translation>.gitignore を編集</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1317"/>
+ <location filename="src/MainWindow.ui" line="1320"/>
<source>&amp;Settings...</source>
<translation>設定(&amp;S)...</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1368"/>
+ <location filename="src/MainWindow.ui" line="1371"/>
<source>&amp;Jump...</source>
<translation>移動(&amp;J)...</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1371"/>
+ <location filename="src/MainWindow.ui" line="1374"/>
<source>Ctrl+J</source>
<translation></translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1376"/>
+ <location filename="src/MainWindow.ui" line="1379"/>
<source>Check&amp;out...</source>
<translation>チェックアウト(&amp;O)...</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1381"/>
- <location filename="src/MainWindow.cpp" line="2171"/>
+ <location filename="src/MainWindow.ui" line="1384"/>
+ <location filename="src/MainWindow.cpp" line="2200"/>
<source>Delete branch...</source>
<translation>ブランチの削除...</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1386"/>
+ <location filename="src/MainWindow.ui" line="1389"/>
<source>Push --set-upstream</source>
<translation></translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1436"/>
- <location filename="src/MainWindow.cpp" line="2172"/>
+ <location filename="src/MainWindow.ui" line="1439"/>
+ <location filename="src/MainWindow.cpp" line="2201"/>
<source>Delete remote branch...</source>
<translation>リモートブランチの削除...</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1441"/>
+ <location filename="src/MainWindow.ui" line="1444"/>
<source>rebase onto</source>
<translation></translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1337"/>
+ <location filename="src/MainWindow.ui" line="1340"/>
<source>&amp;About</source>
<translation>Guitarについて(&amp;A)</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1350"/>
+ <location filename="src/MainWindow.ui" line="1353"/>
<source>Push all tags</source>
<translation>全てのタグをプッシュ</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1355"/>
+ <location filename="src/MainWindow.ui" line="1358"/>
<source>Set config user</source>
<translation>ユーザー情報を設定</translation>
</message>
<message>
- <location filename="src/MainWindow.ui" line="1363"/>
+ <location filename="src/MainWindow.ui" line="1366"/>
<source>&amp;Log</source>
<translation>ログ(&amp;L)</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="671"/>
+ <location filename="src/MainWindow.cpp" line="678"/>
<source>Unnamed</source>
<translation>無名</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="927"/>
+ <location filename="src/MainWindow.cpp" line="934"/>
<source>Default</source>
<translation></translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="1204"/>
+ <location filename="src/MainWindow.cpp" line="1211"/>
<source>Graph</source>
<translation>樹形図</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="1206"/>
+ <location filename="src/MainWindow.cpp" line="1213"/>
<source>Date</source>
<translation>日付</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="1207"/>
+ <location filename="src/MainWindow.cpp" line="1214"/>
<source>Author</source>
<translation>名前</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="1208"/>
+ <location filename="src/MainWindow.cpp" line="1215"/>
<source>Description</source>
<translation>概要</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="1384"/>
+ <location filename="src/MainWindow.cpp" line="1408"/>
<source>, %1 ahead</source>
<translation></translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="1387"/>
+ <location filename="src/MainWindow.cpp" line="1411"/>
<source>, %1 behind</source>
<translation></translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="1613"/>
- <location filename="src/MainWindow.cpp" line="1900"/>
+ <location filename="src/MainWindow.cpp" line="1637"/>
+ <location filename="src/MainWindow.cpp" line="1929"/>
<source>Uncommited changes</source>
<translation>コミットされていない変更があります</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="1695"/>
+ <location filename="src/MainWindow.cpp" line="1719"/>
<source>Confirm Remove</source>
<translation></translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="1695"/>
+ <location filename="src/MainWindow.cpp" line="1719"/>
<source>Are you sure you want to remove the repository from bookmarks ?</source>
<translation>リポジトリをブックマークから削除してよろしいですか?</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="1695"/>
+ <location filename="src/MainWindow.cpp" line="1719"/>
<source>(Files will NOT be deleted)</source>
<translation>(ファルは削除されません)</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="1716"/>
- <location filename="src/MainWindow.cpp" line="1723"/>
+ <location filename="src/MainWindow.cpp" line="1740"/>
+ <location filename="src/MainWindow.cpp" line="1747"/>
<source>Open Repository</source>
<translation>リポジトリを開く</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="1716"/>
+ <location filename="src/MainWindow.cpp" line="1740"/>
<source>No such folder</source>
<translation>そのようなフォルダはありません</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="1716"/>
+ <location filename="src/MainWindow.cpp" line="1740"/>
<source>Remove from bookmark ?</source>
<translation></translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="1723"/>
+ <location filename="src/MainWindow.cpp" line="1747"/>
<source>Not a valid git repository</source>
<translation>有効なリポジトリではありません</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="1847"/>
+ <location filename="src/MainWindow.cpp" line="1876"/>
<source>Commit message can not be omitted.</source>
<translation>コミットメッセージを空にすることはできません。</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2060"/>
+ <location filename="src/MainWindow.cpp" line="2089"/>
<source>&amp;Add new group</source>
<translation>新しいグループを追加(&amp;A)</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2061"/>
+ <location filename="src/MainWindow.cpp" line="2090"/>
<source>&amp;Delete group</source>
<translation>グループを削除(&amp;D)</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2062"/>
+ <location filename="src/MainWindow.cpp" line="2091"/>
<source>&amp;Rename group</source>
<translation>グループ名の変更(&amp;R)</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2067"/>
+ <location filename="src/MainWindow.cpp" line="2096"/>
<source>New group</source>
<translation>新しいグループ</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2091"/>
+ <location filename="src/MainWindow.cpp" line="2120"/>
<source>Open &amp;terminal</source>
<translation>端末を開く(&amp;T)</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2092"/>
+ <location filename="src/MainWindow.cpp" line="2121"/>
<source>Open command promp&amp;t</source>
<translation>コマンドプロンプトを開く(&amp;T)</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2094"/>
+ <location filename="src/MainWindow.cpp" line="2123"/>
<source>&amp;Open</source>
<translation>開く(&amp;O)</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2103"/>
+ <location filename="src/MainWindow.cpp" line="2132"/>
<source>Open &amp;folder</source>
<translation>フォルダを開く(&amp;F)</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2105"/>
+ <location filename="src/MainWindow.cpp" line="2134"/>
<source>&amp;Remove</source>
<translation>削除(&amp;R)</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2151"/>
+ <location filename="src/MainWindow.cpp" line="2180"/>
<source>Copy commit id (7 letters)</source>
<translation>コミットIDをコピー(7文字)</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2152"/>
+ <location filename="src/MainWindow.cpp" line="2181"/>
<source>Copy commit id (completely)</source>
<translation>コミットIDをコピー(すべて)</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2163"/>
+ <location filename="src/MainWindow.cpp" line="2192"/>
<source>Edit comment...</source>
<translation>コメントを編集...</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2178"/>
+ <location filename="src/MainWindow.cpp" line="2203"/>
+ <location filename="src/MainWindow.cpp" line="4133"/>
+ <source>Rebase</source>
+ <translation>リベース</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="2208"/>
<source>Explore</source>
<translation>探索</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2183"/>
+ <location filename="src/MainWindow.cpp" line="2213"/>
<source>Reset HEAD</source>
<translation>HEADをリセット</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2260"/>
- <location filename="src/MainWindow.cpp" line="2309"/>
- <location filename="src/MainWindow.cpp" line="2435"/>
+ <location filename="src/MainWindow.cpp" line="2294"/>
+ <location filename="src/MainWindow.cpp" line="2343"/>
+ <location filename="src/MainWindow.cpp" line="2469"/>
<source>History</source>
<translation>履歴</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2177"/>
+ <location filename="src/MainWindow.cpp" line="4643"/>
+ <source>Authentication Failed</source>
+ <translation>認証が失敗しました</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="2207"/>
<source>Revert</source>
<translation>変更を破棄する</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="1862"/>
+ <location filename="src/MainWindow.cpp" line="1891"/>
<source>Failed to commit</source>
<translation>コミット失敗</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="1961"/>
+ <location filename="src/MainWindow.cpp" line="1990"/>
<source>No remote repository is registered.</source>
<translation>リモートリポジトリが登録されていません。</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="1983"/>
+ <location filename="src/MainWindow.cpp" line="2012"/>
<source>The current branch %1 has no upstream branch.</source>
<translation>現在のブランチ「%1」には上流ブランチがありません。</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="1986"/>
+ <location filename="src/MainWindow.cpp" line="2015"/>
<source>You try push --set-upstream</source>
<translation>--set-upstream を試してください</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="1992"/>
- <location filename="src/MainWindow.cpp" line="4205"/>
+ <location filename="src/MainWindow.cpp" line="2021"/>
+ <location filename="src/MainWindow.cpp" line="4256"/>
<source>Connection refused.</source>
<translation>接続が拒否されました。</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2041"/>
+ <location filename="src/MainWindow.cpp" line="2070"/>
<source>&amp;Property</source>
<translation>プロパティ(&amp;P)</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2170"/>
+ <location filename="src/MainWindow.cpp" line="2199"/>
<source>Checkout/Branch...</source>
<translation>チェックアウト/ブランチ...</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2173"/>
+ <location filename="src/MainWindow.cpp" line="2202"/>
<source>Merge</source>
<translation>マージ</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2174"/>
+ <location filename="src/MainWindow.cpp" line="2204"/>
<source>Cherry-pick</source>
<translation>チェリーピック</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2259"/>
- <location filename="src/MainWindow.cpp" line="2308"/>
+ <location filename="src/MainWindow.cpp" line="2293"/>
+ <location filename="src/MainWindow.cpp" line="2342"/>
<source>Untrack</source>
<translation>追跡しない</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2261"/>
- <location filename="src/MainWindow.cpp" line="2310"/>
- <location filename="src/MainWindow.cpp" line="2436"/>
+ <location filename="src/MainWindow.cpp" line="2295"/>
+ <location filename="src/MainWindow.cpp" line="2344"/>
+ <location filename="src/MainWindow.cpp" line="2470"/>
<source>Blame</source>
<translation></translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2269"/>
+ <location filename="src/MainWindow.cpp" line="2303"/>
<source>Delete selected files.</source>
<translation>選択されたファイルを削除します。</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2280"/>
+ <location filename="src/MainWindow.cpp" line="2314"/>
<source>rm --cached files</source>
<translation></translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2305"/>
+ <location filename="src/MainWindow.cpp" line="2339"/>
<source>Reset</source>
<translation>リセット</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2306"/>
+ <location filename="src/MainWindow.cpp" line="2340"/>
<source>Ignore</source>
<translation>無視する</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2475"/>
+ <location filename="src/MainWindow.cpp" line="2509"/>
<source>Reset a file</source>
<translation>ファイルをリセットします</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="4131"/>
+ <location filename="src/MainWindow.cpp" line="4130"/>
+ <source>Are you sure you want to rebase the commit ?</source>
+ <translation>コミットをリベースしてよろしいですか?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="4182"/>
<source>No repository selected</source>
<translation>リポジトリが選択されていません</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2258"/>
- <location filename="src/MainWindow.cpp" line="2307"/>
+ <location filename="src/MainWindow.cpp" line="2292"/>
+ <location filename="src/MainWindow.cpp" line="2341"/>
<source>Delete</source>
<translation>削除</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2459"/>
+ <location filename="src/MainWindow.cpp" line="2493"/>
<source>Are you sure you want to run the following command ?</source>
<translation>次のコマンドを実行してよろしいですか?</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="2490"/>
+ <location filename="src/MainWindow.cpp" line="2524"/>
<source>Revert all files</source>
<translation>すべてのファイルの変更を破棄</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="3121"/>
+ <location filename="src/MainWindow.cpp" line="3155"/>
<source>Select %1 command</source>
<translation>%1 コマンドの選択</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="3417"/>
+ <location filename="src/MainWindow.cpp" line="3451"/>
<source>A file with same name already exists</source>
<translation>同じ名前のファイルが既に存在しています</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="3422"/>
+ <location filename="src/MainWindow.cpp" line="3456"/>
<source>A folder with same name already exists</source>
<translation>同じ名前のフォルダが既に存在しています</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="3439"/>
+ <location filename="src/MainWindow.cpp" line="3473"/>
<source>Invalid folder</source>
<translation>無効なフォルダ</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="3444"/>
+ <location filename="src/MainWindow.cpp" line="3478"/>
<source>No such folder. Create it now ?</source>
<translation>このフォルダはありません。作成しますか?</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="3808"/>
+ <location filename="src/MainWindow.cpp" line="3842"/>
<source>The URL is a valid repository</source>
<translation>このURLは有効なリポジトリです</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="3809"/>
+ <location filename="src/MainWindow.cpp" line="3843"/>
<source>Failed to access the URL</source>
<translation>このURLへのアクセスに失敗しました</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="3814"/>
+ <location filename="src/MainWindow.cpp" line="3848"/>
<source>Remote Repository</source>
<translation>リモートリポジトリ</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="3947"/>
+ <location filename="src/MainWindow.cpp" line="3981"/>
<source>Jump</source>
<translation>移動</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="3947"/>
+ <location filename="src/MainWindow.cpp" line="3981"/>
<source>That commmit has not foud or not read yet</source>
<translation>そのコミットは、見つからないか、まだ読み込まれていません</translation>
</message>
<message>
- <location filename="src/MainWindow.cpp" line="4069"/>
+ <location filename="src/MainWindow.cpp" line="4103"/>
<source>Failed to delete the branch &apos;%1&apos;
</source>
<translation>ブランチの削除に失敗しました : &apos;%1&apos;</translation>
</message>
</context>
<context>
<name>MergeBranchDialog</name>
<message>
<location filename="src/MergeBranchDialog.ui" line="14"/>
<source>Merge</source>
<translation>マージ</translation>
</message>
<message>
<location filename="src/MergeBranchDialog.ui" line="26"/>
<source>Current branch :</source>
<translation>現在のブランチ :</translation>
</message>
<message>
<location filename="src/MergeBranchDialog.ui" line="39"/>
<source>TextLabel</source>
<translation></translation>
</message>
<message>
<location filename="src/MergeBranchDialog.ui" line="62"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="src/MergeBranchDialog.ui" line="75"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
</context>
<context>
<name>MyImageViewWidget</name>
<message>
<location filename="src/MyImageViewWidget.cpp" line="35"/>
<source>Save as...</source>
<translation>名前を付けて保存...</translation>
</message>
<message>
<location filename="src/MyImageViewWidget.cpp" line="42"/>
<source>Save as</source>
<translation>名前を付けて保存</translation>
</message>
</context>
<context>
<name>MyTextEditorWidget</name>
<message>
<location filename="src/MyTextEditorWidget.cpp" line="35"/>
<source>Save as...</source>
<translation>名前を付けて保存...</translation>
</message>
<message>
<location filename="src/MyTextEditorWidget.cpp" line="36"/>
<source>Copy</source>
<translation>コピー</translation>
</message>
<message>
<location filename="src/MyTextEditorWidget.cpp" line="43"/>
<source>Save as</source>
<translation>名前を付けて保存</translation>
</message>
</context>
<context>
<name>PushDialog</name>
<message>
<location filename="src/PushDialog.ui" line="14"/>
<source>Push</source>
<translation>プッシュ</translation>
</message>
<message>
<location filename="src/PushDialog.ui" line="20"/>
<source>push --set-upstream</source>
<translation></translation>
</message>
<message>
<location filename="src/PushDialog.ui" line="26"/>
<source>Remote</source>
<translation>リモート</translation>
</message>
<message>
<location filename="src/PushDialog.ui" line="33"/>
<source>Branch</source>
<translation>ブランチ</translation>
</message>
<message>
<location filename="src/PushDialog.ui" line="64"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="src/PushDialog.ui" line="71"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
</context>
<context>
<name>RebaseOntoDialog</name>
<message>
- <location filename="RebaseOntoDialog.ui" line="14"/>
+ <location filename="src/RebaseOntoDialog.ui" line="14"/>
<source>Rebase onto</source>
<translation></translation>
</message>
<message>
- <location filename="RebaseOntoDialog.ui" line="22"/>
+ <location filename="src/RebaseOntoDialog.ui" line="22"/>
<source>New base</source>
<translation></translation>
</message>
<message>
- <location filename="RebaseOntoDialog.ui" line="32"/>
+ <location filename="src/RebaseOntoDialog.ui" line="32"/>
<source>Upstream</source>
<translation></translation>
</message>
<message>
- <location filename="RebaseOntoDialog.ui" line="42"/>
+ <location filename="src/RebaseOntoDialog.ui" line="42"/>
<source>Branch</source>
<translation>ブランチ</translation>
</message>
<message>
- <location filename="RebaseOntoDialog.ui" line="82"/>
+ <location filename="src/RebaseOntoDialog.ui" line="82"/>
<source>OK</source>
<translation></translation>
</message>
<message>
- <location filename="RebaseOntoDialog.ui" line="89"/>
+ <location filename="src/RebaseOntoDialog.ui" line="89"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
</context>
<context>
<name>ReflogWindow</name>
<message>
<location filename="src/ReflogWindow.ui" line="14"/>
<source>Reflog</source>
<translation></translation>
</message>
<message>
<location filename="src/ReflogWindow.ui" line="57"/>
<source>Close</source>
<translation>閉じる</translation>
</message>
<message>
<location filename="src/ReflogWindow.cpp" line="35"/>
<source>Commit</source>
<translation>コミット</translation>
</message>
<message>
<location filename="src/ReflogWindow.cpp" line="36"/>
<source>Head</source>
<translation>ヘッド</translation>
</message>
<message>
<location filename="src/ReflogWindow.cpp" line="37"/>
<source>Command</source>
<translation>コマンド</translation>
</message>
<message>
<location filename="src/ReflogWindow.cpp" line="38"/>
<source>Comment</source>
<translation>コメント</translation>
</message>
<message>
<location filename="src/ReflogWindow.cpp" line="98"/>
<source>Checkout</source>
<translation>チェックアウト</translation>
</message>
<message>
<location filename="src/ReflogWindow.cpp" line="99"/>
<source>Explorer</source>
<translation>エクスプローラ</translation>
</message>
</context>
<context>
<name>RemoteRepositoriesTableWidget</name>
<message>
<location filename="src/RemoteRepositoriesTableWidget.cpp" line="27"/>
<source>Copy URL</source>
<translation>URLをコピー</translation>
</message>
</context>
<context>
<name>RepositoryPropertyDialog</name>
<message>
<location filename="src/RepositoryPropertyDialog.ui" line="14"/>
<source>Repository Property</source>
<oldsource>Repository Properties</oldsource>
<translation>リポジトリのプロパティ</translation>
</message>
<message>
<location filename="src/RepositoryPropertyDialog.ui" line="65"/>
<source>TextLabel</source>
<translation></translation>
</message>
<message>
- <location filename="src/RepositoryPropertyDialog.ui" line="92"/>
+ <location filename="src/RepositoryPropertyDialog.ui" line="95"/>
<source>Local dir :</source>
<translation>ローカルフォルダ :</translation>
</message>
<message>
- <location filename="src/RepositoryPropertyDialog.ui" line="111"/>
+ <location filename="src/RepositoryPropertyDialog.ui" line="114"/>
<source>Remote URLs</source>
<translation>リモートURL</translation>
</message>
<message>
- <location filename="src/RepositoryPropertyDialog.ui" line="121"/>
+ <location filename="src/RepositoryPropertyDialog.ui" line="124"/>
<source>Remote</source>
<translation>リモート</translation>
</message>
<message>
- <location filename="src/RepositoryPropertyDialog.ui" line="127"/>
+ <location filename="src/RepositoryPropertyDialog.ui" line="130"/>
<source>Add</source>
<translation>追加</translation>
</message>
<message>
- <location filename="src/RepositoryPropertyDialog.ui" line="134"/>
+ <location filename="src/RepositoryPropertyDialog.ui" line="137"/>
<source>Edit</source>
<translation>編集</translation>
</message>
<message>
- <location filename="src/RepositoryPropertyDialog.ui" line="154"/>
+ <location filename="src/RepositoryPropertyDialog.ui" line="157"/>
<source>Remove</source>
<translation>削除</translation>
</message>
<message>
- <location filename="src/RepositoryPropertyDialog.ui" line="166"/>
+ <location filename="src/RepositoryPropertyDialog.ui" line="169"/>
<source>&amp;Remote menu</source>
<translation>リモートメニュー(&amp;R)</translation>
</message>
<message>
- <location filename="src/RepositoryPropertyDialog.ui" line="186"/>
+ <location filename="src/RepositoryPropertyDialog.ui" line="189"/>
<source>Close</source>
<translation>閉じる</translation>
</message>
<message>
<location filename="src/RepositoryPropertyDialog.cpp" line="128"/>
<source>Confirm Remove</source>
<translation>削除の確認</translation>
</message>
<message>
<location filename="src/RepositoryPropertyDialog.cpp" line="128"/>
<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="src/SearchFromGitHubDialog.ui" line="14"/>
<source>Search From GitHub</source>
<translation>GitHubから検索</translation>
</message>
<message>
<location filename="src/SearchFromGitHubDialog.ui" line="25"/>
<source>Search</source>
<translation>検索</translation>
</message>
<message>
- <location filename="src/SearchFromGitHubDialog.ui" line="75"/>
+ <location filename="src/SearchFromGitHubDialog.ui" line="78"/>
<source>ssh</source>
<translation></translation>
</message>
<message>
- <location filename="src/SearchFromGitHubDialog.ui" line="85"/>
+ <location filename="src/SearchFromGitHubDialog.ui" line="88"/>
<source>http</source>
<translation></translation>
</message>
<message>
- <location filename="src/SearchFromGitHubDialog.ui" line="112"/>
+ <location filename="src/SearchFromGitHubDialog.ui" line="115"/>
<source>OK</source>
<translation></translation>
</message>
<message>
- <location filename="src/SearchFromGitHubDialog.ui" line="119"/>
+ <location filename="src/SearchFromGitHubDialog.ui" line="122"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
<message>
<location filename="src/SearchFromGitHubDialog.cpp" line="58"/>
<source>Name</source>
<translation>名前</translation>
</message>
<message>
<location filename="src/SearchFromGitHubDialog.cpp" line="59"/>
<source>Owner</source>
<translation>オーナー</translation>
</message>
<message>
<location filename="src/SearchFromGitHubDialog.cpp" line="60"/>
<source>Score</source>
<translation>スコア</translation>
</message>
<message>
<location filename="src/SearchFromGitHubDialog.cpp" line="61"/>
<source>Description</source>
<translation>概要</translation>
</message>
</context>
<context>
<name>SelectCommandDialog</name>
<message>
<location filename="src/SelectCommandDialog.ui" line="14"/>
<source>Select git command</source>
<translation>git コマンドの選択</translation>
</message>
<message>
<location filename="src/SelectCommandDialog.ui" line="20"/>
<source>TextLabel</source>
<translation></translation>
</message>
<message>
<location filename="src/SelectCommandDialog.ui" line="32"/>
<source>&amp;Browse...</source>
<translation>参照(&amp;B)...</translation>
</message>
<message>
<location filename="src/SelectCommandDialog.ui" line="52"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="src/SelectCommandDialog.ui" line="59"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
<message>
<location filename="src/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="src/SelectCommandDialog.cpp" line="58"/>
<location filename="src/SelectCommandDialog.cpp" line="65"/>
<source>%1 command (%2);;</source>
<translation>%1 コマンド (%2);;</translation>
</message>
<message>
<location filename="src/SelectCommandDialog.cpp" line="60"/>
<source>Executable files (*.exe)</source>
<translation>実行可能ファイル (*.exe)</translation>
</message>
<message>
<location filename="src/SelectCommandDialog.cpp" line="71"/>
<source>%1 command</source>
<translation>%1 コマンド</translation>
</message>
</context>
<context>
<name>SelectGpgKeyDialog</name>
<message>
<location filename="src/SelectGpgKeyDialog.ui" line="14"/>
<source>Select GPG Key</source>
<translation>GPGキーの選択</translation>
</message>
<message>
<location filename="src/SelectGpgKeyDialog.ui" line="57"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="src/SelectGpgKeyDialog.ui" line="64"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
<message>
<location filename="src/SelectGpgKeyDialog.cpp" line="31"/>
<source>ID</source>
<translation></translation>
</message>
<message>
<location filename="src/SelectGpgKeyDialog.cpp" line="32"/>
<source>Name</source>
<translation>名前</translation>
</message>
<message>
<location filename="src/SelectGpgKeyDialog.cpp" line="33"/>
<source>Mail</source>
<translation>メール</translation>
</message>
</context>
<context>
<name>SelectItemDialog</name>
<message>
<location filename="src/SelectItemDialog.ui" line="14"/>
<source>Dialog</source>
<translation></translation>
</message>
<message>
<location filename="src/SelectItemDialog.ui" line="25"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="src/SelectItemDialog.ui" line="32"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
</context>
<context>
<name>SetGlobalUserDialog</name>
<message>
<location filename="src/SetGlobalUserDialog.ui" line="14"/>
<source>Global User Setting</source>
<translation>グローバルユーザー設定</translation>
</message>
<message>
- <location filename="src/SetGlobalUserDialog.ui" line="28"/>
+ <location filename="src/SetGlobalUserDialog.ui" line="31"/>
<source>Name</source>
<translation>名前</translation>
</message>
<message>
- <location filename="src/SetGlobalUserDialog.ui" line="38"/>
+ <location filename="src/SetGlobalUserDialog.ui" line="41"/>
<source>Mail</source>
<translation>メール</translation>
</message>
<message>
- <location filename="src/SetGlobalUserDialog.ui" line="65"/>
+ <location filename="src/SetGlobalUserDialog.ui" line="68"/>
<source>OK</source>
<translation></translation>
</message>
<message>
- <location filename="src/SetGlobalUserDialog.ui" line="75"/>
+ <location filename="src/SetGlobalUserDialog.ui" line="78"/>
<source>Skip</source>
<translation>スキップ</translation>
</message>
</context>
<context>
<name>SetGpgSigningDialog</name>
<message>
<location filename="src/SetGpgSigningDialog.ui" line="14"/>
<source>Set GPG Signing</source>
<translation>GPG署名の指定</translation>
</message>
<message>
<location filename="src/SetGpgSigningDialog.ui" line="26"/>
<source>Global</source>
<translation>グローバル</translation>
</message>
<message>
<location filename="src/SetGpgSigningDialog.ui" line="39"/>
<location filename="src/SetGpgSigningDialog.cpp" line="29"/>
<source>Repository</source>
<translation>リポジトリ</translation>
</message>
<message>
- <location filename="src/SetGpgSigningDialog.ui" line="55"/>
+ <location filename="src/SetGpgSigningDialog.ui" line="61"/>
<source>ID</source>
<translation></translation>
</message>
<message>
- <location filename="src/SetGpgSigningDialog.ui" line="65"/>
+ <location filename="src/SetGpgSigningDialog.ui" line="71"/>
<source>Name</source>
<translation>名前</translation>
</message>
<message>
- <location filename="src/SetGpgSigningDialog.ui" line="75"/>
+ <location filename="src/SetGpgSigningDialog.ui" line="81"/>
<source>Mail</source>
<translation>メール</translation>
</message>
<message>
- <location filename="src/SetGpgSigningDialog.ui" line="87"/>
+ <location filename="src/SetGpgSigningDialog.ui" line="93"/>
<source>Select</source>
<translation>選択</translation>
</message>
<message>
- <location filename="src/SetGpgSigningDialog.ui" line="107"/>
+ <location filename="src/SetGpgSigningDialog.ui" line="113"/>
<source>Clear</source>
<translation>消去</translation>
</message>
<message>
- <location filename="src/SetGpgSigningDialog.ui" line="121"/>
+ <location filename="src/SetGpgSigningDialog.ui" line="127"/>
<source>Configure...</source>
<translation>設定...</translation>
</message>
<message>
- <location filename="src/SetGpgSigningDialog.ui" line="141"/>
+ <location filename="src/SetGpgSigningDialog.ui" line="147"/>
<source>OK</source>
<translation></translation>
</message>
<message>
- <location filename="src/SetGpgSigningDialog.ui" line="148"/>
+ <location filename="src/SetGpgSigningDialog.ui" line="154"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
</context>
<context>
<name>SetRemoteUrlDialog</name>
<message>
<location filename="src/SetRemoteUrlDialog.ui" line="14"/>
<source>Set Remote URL</source>
<translation>リモートURLの設定</translation>
</message>
<message>
<location filename="src/SetRemoteUrlDialog.ui" line="20"/>
<source>Current URLs</source>
<translation>現在のURL</translation>
</message>
<message>
<location filename="src/SetRemoteUrlDialog.ui" line="30"/>
<source>New URL</source>
<translation>新しいURL</translation>
</message>
<message>
<location filename="src/SetRemoteUrlDialog.ui" line="36"/>
<source>Name</source>
<translation>名前</translation>
</message>
<message>
<location filename="src/SetRemoteUrlDialog.ui" line="91"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="src/SetRemoteUrlDialog.ui" line="98"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
<message>
<location filename="src/SetRemoteUrlDialog.ui" line="43"/>
<source>URL</source>
<translation></translation>
</message>
<message>
<location filename="src/SetRemoteUrlDialog.ui" line="63"/>
<source>&amp;Test</source>
<translation>テスト(&amp;T)</translation>
</message>
</context>
<context>
<name>SetUserDialog</name>
<message>
<location filename="src/SetUserDialog.ui" line="14"/>
<source>Set User</source>
<translation>ユーザーの設定</translation>
</message>
<message>
<location filename="src/SetUserDialog.ui" line="20"/>
<source>Global</source>
<translation>グローバル</translation>
</message>
<message>
<location filename="src/SetUserDialog.ui" line="27"/>
<location filename="src/SetUserDialog.cpp" line="31"/>
<source>Repository</source>
<translation>リポジトリ</translation>
</message>
<message>
<location filename="src/SetUserDialog.ui" line="42"/>
<source>Name</source>
<translation>名前</translation>
</message>
<message>
<location filename="src/SetUserDialog.ui" line="52"/>
<source>Mail</source>
<translation>メール</translation>
</message>
<message>
<location filename="src/SetUserDialog.ui" line="66"/>
<source>Get icon from Gravatar</source>
<translation>Gravatarからアイコンを取得</translation>
</message>
<message>
- <location filename="src/SetUserDialog.ui" line="128"/>
+ <location filename="src/SetUserDialog.ui" line="122"/>
<source>OK</source>
<translation></translation>
</message>
<message>
- <location filename="src/SetUserDialog.ui" line="138"/>
+ <location filename="src/SetUserDialog.ui" line="132"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
</context>
<context>
<name>SettingBehaviorForm</name>
<message>
<location filename="src/SettingBehaviorForm.ui" line="14"/>
<source>Behavior</source>
<translation>動作</translation>
</message>
<message>
<location filename="src/SettingBehaviorForm.ui" line="20"/>
<location filename="src/SettingBehaviorForm.cpp" line="43"/>
<source>Default working folder</source>
<translation>既定の作業フォルダ</translation>
</message>
<message>
<location filename="src/SettingBehaviorForm.ui" line="29"/>
<source>Browse...</source>
<translation>参照...</translation>
</message>
<message>
<location filename="src/SettingBehaviorForm.ui" line="39"/>
<source>Automatically fetch when opening the repository</source>
<translation>リポジトリを開くとき自動的にフェッチする</translation>
</message>
<message>
<location filename="src/SettingBehaviorForm.ui" line="46"/>
<source>Get committer&apos;s icon from gravatar.com</source>
<translation>gravatar.comからアイコンを取得する</translation>
</message>
<message>
- <location filename="src/SettingBehaviorForm.ui" line="55"/>
+ <location filename="src/SettingBehaviorForm.ui" line="61"/>
<source>Maximum number of commit item acquisitions</source>
<translation>取得するコミット情報の最大個数</translation>
</message>
<message>
- <location filename="src/SettingBehaviorForm.ui" line="88"/>
+ <location filename="src/SettingBehaviorForm.ui" line="94"/>
<source>GPG signing policy</source>
<oldsource>GPG Signing Policy</oldsource>
<translation>GPG署名ポリシー</translation>
</message>
<message>
- <location filename="src/SettingBehaviorForm.ui" line="95"/>
+ <location filename="src/SettingBehaviorForm.ui" line="101"/>
<source>Configure...</source>
<translation>設定...</translation>
</message>
</context>
<context>
<name>SettingExampleForm</name>
<message>
<location filename="src/SettingExampleForm.ui" line="14"/>
<source>Example</source>
<translation></translation>
</message>
<message>
<location filename="src/SettingExampleForm.ui" line="20"/>
<source>Underconstruction</source>
<translation></translation>
</message>
</context>
<context>
<name>SettingGeneralForm</name>
<message>
<location filename="src/SettingGeneralForm.ui" line="14"/>
<source>General</source>
<translation>一般</translation>
</message>
<message>
<location filename="src/SettingGeneralForm.ui" line="20"/>
<source>Language</source>
<translation>言語</translation>
</message>
<message>
<location filename="src/SettingGeneralForm.ui" line="31"/>
<source>Change Language...</source>
<translation>言語の変更...</translation>
</message>
<message>
<location filename="src/SettingGeneralForm.ui" line="54"/>
<source>Change Theme...</source>
<translation>テーマの変更...</translation>
</message>
<message>
<location filename="src/SettingGeneralForm.ui" line="66"/>
<source>Remember and restore window position</source>
<translation>ウィンドウの位置を記憶し復元する</translation>
</message>
<message>
<location filename="src/SettingGeneralForm.ui" line="73"/>
<source>Enable high DPI scaling</source>
<translation>高精細画面のスケーリングを行う</translation>
</message>
<message>
<location filename="src/SettingGeneralForm.ui" line="43"/>
<source>Theme</source>
<translation>テーマ</translation>
</message>
<message>
<location filename="src/SettingGeneralForm.cpp" line="16"/>
<source>English</source>
<translation></translation>
</message>
<message>
<location filename="src/SettingGeneralForm.cpp" line="17"/>
<source>Japanese</source>
<translation>日本語</translation>
</message>
<message>
<location filename="src/SettingGeneralForm.cpp" line="19"/>
<source>Standard</source>
<translation>標準</translation>
</message>
<message>
<location filename="src/SettingGeneralForm.cpp" line="20"/>
<source>Dark</source>
<translation>ダーク</translation>
</message>
<message>
<location filename="src/SettingGeneralForm.cpp" line="78"/>
<source>Select Language</source>
<translation>言語の選択</translation>
</message>
<message>
<location filename="src/SettingGeneralForm.cpp" line="97"/>
<source>Select Theme</source>
<translation>テーマの選択</translation>
</message>
<message>
<location filename="src/SettingGeneralForm.ui" line="93"/>
<source>(Changes are applied at next run)</source>
<translation>(設定は次の実行時に有効になります)</translation>
</message>
</context>
<context>
<name>SettingNetworkForm</name>
<message>
<location filename="src/SettingNetworkForm.ui" line="14"/>
<source>Network</source>
<translation>ネットワーク</translation>
</message>
<message>
<location filename="src/SettingNetworkForm.ui" line="20"/>
<source>Proxy server</source>
<oldsource>Proxy Server</oldsource>
<translation>プロキシサーバー</translation>
</message>
<message>
<location filename="src/SettingNetworkForm.ui" line="26"/>
<source>No proxy</source>
<oldsource>No Proxy</oldsource>
<translation>プロキシなし</translation>
</message>
<message>
<location filename="src/SettingNetworkForm.ui" line="33"/>
<source>Auto detect</source>
<oldsource>Auto Detect</oldsource>
<translation>自動検出</translation>
</message>
<message>
<location filename="src/SettingNetworkForm.ui" line="40"/>
<source>Manual</source>
<translation>手動</translation>
</message>
</context>
<context>
<name>SettingProgramsForm</name>
<message>
<location filename="src/SettingProgramsForm.ui" line="14"/>
<source>Programs</source>
<translation>プログラム</translation>
</message>
<message>
<location filename="src/SettingProgramsForm.ui" line="20"/>
<source>Git command</source>
<translation>Git コマンド</translation>
</message>
<message>
<location filename="src/SettingProgramsForm.ui" line="29"/>
<location filename="src/SettingProgramsForm.ui" line="48"/>
<location filename="src/SettingProgramsForm.ui" line="67"/>
<source>Browse...</source>
<translation>参照...</translation>
</message>
<message>
<location filename="src/SettingProgramsForm.ui" line="39"/>
<source>File command</source>
<translation>File コマンド</translation>
</message>
<message>
<location filename="src/SettingProgramsForm.ui" line="58"/>
<source>GPG command (option)</source>
<translation>GPGコマンド(オプション)</translation>
</message>
</context>
<context>
<name>SettingsDialog</name>
<message>
<location filename="src/SettingsDialog.ui" line="14"/>
<source>Settings</source>
<translation>設定</translation>
</message>
<message>
<location filename="src/SettingsDialog.ui" line="141"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="src/SettingsDialog.ui" line="148"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
</context>
<context>
<name>TextEditDialog</name>
<message>
<location filename="src/TextEditDialog.ui" line="14"/>
<source>Dialog</source>
<translation></translation>
</message>
<message>
<location filename="src/TextEditDialog.ui" line="48"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="src/TextEditDialog.ui" line="58"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
</context>
<context>
<name>WelcomeWizardDialog</name>
<message>
<location filename="src/WelcomeWizardDialog.ui" line="14"/>
<source>Welcome to the Guitar Wizard</source>
<translation>Guitarウィザードへようこそ</translation>
</message>
<message>
<location filename="src/WelcomeWizardDialog.ui" line="45"/>
<source>Helper Tools</source>
<translation>ヘルパープログラム</translation>
</message>
<message>
<location filename="src/WelcomeWizardDialog.ui" line="61"/>
- <location filename="src/WelcomeWizardDialog.ui" line="332"/>
+ <location filename="src/WelcomeWizardDialog.ui" line="338"/>
<source>git</source>
<translation></translation>
</message>
<message>
<location filename="src/WelcomeWizardDialog.ui" line="71"/>
<location filename="src/WelcomeWizardDialog.ui" line="88"/>
- <location filename="src/WelcomeWizardDialog.ui" line="242"/>
+ <location filename="src/WelcomeWizardDialog.ui" line="248"/>
<source>Browse</source>
<translation>参照</translation>
</message>
<message>
<location filename="src/WelcomeWizardDialog.ui" line="78"/>
- <location filename="src/WelcomeWizardDialog.ui" line="346"/>
+ <location filename="src/WelcomeWizardDialog.ui" line="352"/>
<source>file</source>
<translation></translation>
</message>
<message>
<location filename="src/WelcomeWizardDialog.ui" line="120"/>
<source>Global User Information</source>
<translation>グローバルユーザー情報</translation>
</message>
<message>
- <location filename="src/WelcomeWizardDialog.ui" line="136"/>
+ <location filename="src/WelcomeWizardDialog.ui" line="142"/>
<source>git config --global user.name</source>
<translation></translation>
</message>
<message>
- <location filename="src/WelcomeWizardDialog.ui" line="146"/>
+ <location filename="src/WelcomeWizardDialog.ui" line="152"/>
<source>git config --global user.email</source>
<translation></translation>
</message>
<message>
- <location filename="src/WelcomeWizardDialog.ui" line="162"/>
+ <location filename="src/WelcomeWizardDialog.ui" line="168"/>
<source>Get icon from Gravatar</source>
<translation>Gravatarからアイコンを取得</translation>
</message>
<message>
- <location filename="src/WelcomeWizardDialog.ui" line="216"/>
+ <location filename="src/WelcomeWizardDialog.ui" line="222"/>
<location filename="src/WelcomeWizardDialog.cpp" line="156"/>
<source>Default Working Folder</source>
<translation>規定の作業フォルダ</translation>
</message>
<message>
- <location filename="src/WelcomeWizardDialog.ui" line="232"/>
- <location filename="src/WelcomeWizardDialog.ui" line="318"/>
+ <location filename="src/WelcomeWizardDialog.ui" line="238"/>
+ <location filename="src/WelcomeWizardDialog.ui" line="324"/>
<source>folder</source>
<translation>フォルダ</translation>
</message>
<message>
- <location filename="src/WelcomeWizardDialog.ui" line="274"/>
+ <location filename="src/WelcomeWizardDialog.ui" line="280"/>
<source>Ready to play the Guitar !</source>
<translation>Guitarの準備ができました!</translation>
</message>
<message>
- <location filename="src/WelcomeWizardDialog.ui" line="290"/>
+ <location filename="src/WelcomeWizardDialog.ui" line="296"/>
<source>user</source>
<translation>ユーザー</translation>
</message>
<message>
- <location filename="src/WelcomeWizardDialog.ui" line="304"/>
+ <location filename="src/WelcomeWizardDialog.ui" line="310"/>
<source>email</source>
<translation>メール</translation>
</message>
<message>
- <location filename="src/WelcomeWizardDialog.ui" line="420"/>
+ <location filename="src/WelcomeWizardDialog.ui" line="426"/>
<location filename="src/WelcomeWizardDialog.cpp" line="148"/>
<source>&lt;&lt; Prev</source>
<translation>&lt;&lt; 戻る</translation>
</message>
<message>
- <location filename="src/WelcomeWizardDialog.ui" line="427"/>
+ <location filename="src/WelcomeWizardDialog.ui" line="433"/>
<location filename="src/WelcomeWizardDialog.cpp" line="149"/>
<source>Next &gt;&gt;</source>
<translation>次へ &gt;&gt;</translation>
</message>
<message>
<location filename="src/WelcomeWizardDialog.cpp" line="127"/>
<source>Cancel</source>
<translation>キャンセル</translation>
</message>
<message>
<location filename="src/WelcomeWizardDialog.cpp" line="146"/>
<source>Finish</source>
<translation>完了</translation>
</message>
</context>
</TS>
diff --git a/src/CreateRepositoryDialog.cpp b/src/CreateRepositoryDialog.cpp
index 349de47..925d4c2 100644
--- a/src/CreateRepositoryDialog.cpp
+++ b/src/CreateRepositoryDialog.cpp
@@ -1,132 +1,132 @@
#include "CreateRepositoryDialog.h"
#include "MainWindow.h"
#include "common/misc.h"
#include "ui_CreateRepositoryDialog.h"
#include <QFileDialog>
#include <QMessageBox>
#include "Git.h"
-CreateRepositoryDialog::CreateRepositoryDialog(MainWindow *parent) :
+CreateRepositoryDialog::CreateRepositoryDialog(MainWindow *parent, const QString &dir) :
QDialog(parent),
ui(new Ui::CreateRepositoryDialog)
{
ui->setupUi(this);
Qt::WindowFlags flags = windowFlags();
flags &= ~Qt::WindowContextHelpButtonHint;
setWindowFlags(flags);
already_exists_ = tr("A valid git repository already exists there.");
+ ui->lineEdit_path->setText(dir);
ui->groupBox_remote->setChecked(false);
-
ui->lineEdit_remote_name->setText("origin");
validate(false);
}
CreateRepositoryDialog::~CreateRepositoryDialog()
{
delete ui;
}
MainWindow *CreateRepositoryDialog::mainwindow()
{
return qobject_cast<MainWindow *>(parent());
}
void CreateRepositoryDialog::accept()
{
QString path = ui->lineEdit_path->text();
if (!QFileInfo(path).isDir()) {
QMessageBox::warning(this, tr("Create Repository"), tr("The specified path is not a directory."));
return;
}
if (Git::isValidWorkingCopy(path)) {
QMessageBox::warning(this, tr("Create Repository"), already_exists_);
return;
}
if (!QFileInfo(path).isDir()) {
QMessageBox::warning(this, tr("Create Repository"), tr("The specified path is not a directory."));
return;
}
if (remoteName().indexOf('\"') >= 0 || remoteURL().indexOf('\"') >= 0) { // 手抜き
QMessageBox::warning(this, tr("Create Repository"), tr("Remote name is invalid."));
return;
}
done(QDialog::Accepted);
}
void CreateRepositoryDialog::on_pushButton_browse_path_clicked()
{
QString path = QFileDialog::getExistingDirectory(this, tr("Destination Path"), mainwindow()->defaultWorkingDir());
if (!path.isEmpty()) {
path = misc::normalizePathSeparator(path);
ui->lineEdit_path->setText(path);
}
}
QString CreateRepositoryDialog::path() const
{
return ui->lineEdit_path->text();
}
QString CreateRepositoryDialog::name() const
{
return ui->lineEdit_name->text();
}
QString CreateRepositoryDialog::remoteName() const
{
return ui->groupBox_remote->isChecked() ? ui->lineEdit_remote_name->text() : QString();
}
QString CreateRepositoryDialog::remoteURL() const
{
return ui->groupBox_remote->isChecked() ? ui->lineEdit_remote_url->text() : QString();
}
void CreateRepositoryDialog::validate(bool change_name)
{
QString path = this->path();
{
QString text;
if (Git::isValidWorkingCopy(path)) {
text = already_exists_;
}
ui->label_warning->setText(text);
}
if (change_name) {
int i = path.lastIndexOf('/');
int j = path.lastIndexOf('\\');
if (i < j) i = j;
if (i >= 0) {
QString name = path.mid(i + 1);
ui->lineEdit_name->setText(name);
}
}
}
void CreateRepositoryDialog::on_lineEdit_path_textChanged(const QString &)
{
validate(true);
}
void CreateRepositoryDialog::on_lineEdit_name_textChanged(const QString &)
{
validate(false);
}
void CreateRepositoryDialog::on_groupBox_remote_toggled(bool)
{
validate(false);
}
void CreateRepositoryDialog::on_pushButton_test_repo_clicked()
{
QString url = ui->lineEdit_remote_url->text();
mainwindow()->testRemoteRepositoryValidity(url);
validate(false);
}
diff --git a/src/CreateRepositoryDialog.h b/src/CreateRepositoryDialog.h
index 6eb2490..84b783f 100644
--- a/src/CreateRepositoryDialog.h
+++ b/src/CreateRepositoryDialog.h
@@ -1,43 +1,43 @@
#ifndef CREATEREPOSITORYDIALOG_H
#define CREATEREPOSITORYDIALOG_H
#include <QDialog>
namespace Ui {
class CreateRepositoryDialog;
}
class MainWindow;
class CreateRepositoryDialog : public QDialog
{
Q_OBJECT
private:
QString already_exists_;
public:
- explicit CreateRepositoryDialog(MainWindow *parent = 0);
+ explicit CreateRepositoryDialog(MainWindow *parent, QString const &dir = QString());
~CreateRepositoryDialog();
QString path() const;
QString name() const;
QString remoteName() const;
QString remoteURL() const;
private slots:
void on_lineEdit_path_textChanged(const QString &arg1);
void on_pushButton_browse_path_clicked();
void on_lineEdit_name_textChanged(const QString &arg1);
void on_groupBox_remote_toggled(bool arg1);
void on_pushButton_test_repo_clicked();
private:
Ui::CreateRepositoryDialog *ui;
void validate(bool change_name);
MainWindow *mainwindow();
public slots:
void accept();
};
#endif // CREATEREPOSITORYDIALOG_H
diff --git a/src/DoYouWantToInitDialog.cpp b/src/DoYouWantToInitDialog.cpp
new file mode 100644
index 0000000..fa73976
--- /dev/null
+++ b/src/DoYouWantToInitDialog.cpp
@@ -0,0 +1,33 @@
+#include "DoYouWantToInitDialog.h"
+#include "ui_DoYouWantToInitDialog.h"
+
+DoYouWantToInitDialog::DoYouWantToInitDialog(QWidget *parent, const QString &dir) :
+ QDialog(parent),
+ ui(new Ui::DoYouWantToInitDialog)
+{
+ ui->setupUi(this);
+ ui->lineEdit->setText(dir);
+ ui->pushButton->setEnabled(false);
+}
+
+DoYouWantToInitDialog::~DoYouWantToInitDialog()
+{
+ delete ui;
+}
+
+void DoYouWantToInitDialog::on_radioButton_yes_clicked()
+{
+ ui->pushButton->setText(tr("Next..."));
+ ui->pushButton->setEnabled(true);
+}
+
+void DoYouWantToInitDialog::on_radioButton_no_clicked()
+{
+ ui->pushButton->setText(tr("Cancel"));
+ ui->pushButton->setEnabled(true);
+}
+
+void DoYouWantToInitDialog::on_pushButton_clicked()
+{
+ done(ui->radioButton_yes->isChecked() ? QDialog::Accepted : QDialog::Rejected);
+}
diff --git a/src/DoYouWantToInitDialog.h b/src/DoYouWantToInitDialog.h
new file mode 100644
index 0000000..b34fd9f
--- /dev/null
+++ b/src/DoYouWantToInitDialog.h
@@ -0,0 +1,28 @@
+#ifndef DOYOUWANTTOINITDIALOG_H
+#define DOYOUWANTTOINITDIALOG_H
+
+#include <QDialog>
+
+namespace Ui {
+class DoYouWantToInitDialog;
+}
+
+class DoYouWantToInitDialog : public QDialog {
+ Q_OBJECT
+
+public:
+ explicit DoYouWantToInitDialog(QWidget *parent, QString const &dir);
+ ~DoYouWantToInitDialog();
+
+private slots:
+ void on_radioButton_yes_clicked();
+
+ void on_radioButton_no_clicked();
+
+ void on_pushButton_clicked();
+
+private:
+ Ui::DoYouWantToInitDialog *ui;
+};
+
+#endif // DOYOUWANTTOINITDIALOG_H
diff --git a/src/DoYouWantToInitDialog.ui b/src/DoYouWantToInitDialog.ui
new file mode 100644
index 0000000..78c27c9
--- /dev/null
+++ b/src/DoYouWantToInitDialog.ui
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>DoYouWantToInitDialog</class>
+ <widget class="QDialog" name="DoYouWantToInitDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>179</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Init Repository</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>The folder:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="ReadOnlyLineEdit" name="lineEdit"/>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="1">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>is not a valid git working copy.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Do you want to init it as a git repository ?</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" rowspan="2">
+ <spacer name="horizontalSpacer_5">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <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>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QRadioButton" name="radioButton_yes">
+ <property name="text">
+ <string>Yes, Please init it.</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="radioButton_no">
+ <property name="text">
+ <string>No, Stop it.</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </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>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>2</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <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">
+ <property name="text">
+ <string>Please choose option</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>ReadOnlyLineEdit</class>
+ <extends>QLineEdit</extends>
+ <header>ReadOnlyLineEdit.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp
index 6d390e0..c34a95d 100644
--- a/src/MainWindow.cpp
+++ b/src/MainWindow.cpp
@@ -1,4787 +1,4800 @@
#include "AreYouSureYouWantToContinueConnectingDialog.h"
#include "BlameWindow.h"
#include "CommitViewWindow.h"
#include "Git.h"
#include "LineEditDialog.h"
#include "MainWindow.h"
#include "ReflogWindow.h"
#include "SetGlobalUserDialog.h"
#include "EditTagsDialog.h"
#include "WelcomeWizardDialog.h"
#include "ui_MainWindow.h"
#include "EditGitIgnoreDialog.h"
+#include "DoYouWantToInitDialog.h"
#ifdef Q_OS_WIN
#include "win32/win32.h"
#else
#include <unistd.h>
#endif
#include "ApplicationGlobal.h"
#include "AboutDialog.h"
#include "AvatarLoader.h"
#include "CheckoutDialog.h"
#include "CloneDialog.h"
#include "CommitExploreWindow.h"
#include "CommitPropertyDialog.h"
#include "common/joinpath.h"
#include "common/misc.h"
#include "ConfigCredentialHelperDialog.h"
#include "CreateRepositoryDialog.h"
#include "DeleteBranchDialog.h"
#include "DeleteTagsDialog.h"
#include "InputNewTagDialog.h"
#include "FileHistoryWindow.h"
#include "FilePropertyDialog.h"
#include "FileUtil.h"
#include "Git.h"
#include "GitDiff.h"
#include "gunzip.h"
#include "JumpDialog.h"
#include "LocalSocketReader.h"
#include "main.h"
#include "MemoryReader.h"
#include "MergeBranchDialog.h"
#include "MyProcess.h"
#include "MySettings.h"
#include "PushDialog.h"
#include "RepositoryData.h"
#include "RepositoryPropertyDialog.h"
#include "SelectCommandDialog.h"
#include "SetRemoteUrlDialog.h"
#include "SettingsDialog.h"
#include "SetUserDialog.h"
#include "StatusLabel.h"
#include "Terminal.h"
#include "TextEditDialog.h"
#include "CommitDialog.h"
#include "SelectGpgKeyDialog.h"
#include "SetGpgSigningDialog.h"
#include "gpg.h"
#include "webclient.h"
#include <deque>
#include <set>
#include <stdlib.h>
#include <QBuffer>
#include <QClipboard>
#include <QDateTime>
#include <QDebug>
#include <QDesktopServices>
#include <QDirIterator>
#include <QDragEnterEvent>
#include <QFileDialog>
#include <QFileIconProvider>
#include <QKeyEvent>
#include <QLocalServer>
#include <QMessageBox>
#include <QMimeData>
#include <QPainter>
#include <QProcess>
#include <QStandardPaths>
#include <QThread>
#include <QTimer>
#include <RebaseOntoDialog.h>
#ifdef Q_OS_MAC
extern "C" char **environ;
#endif
struct GitHubRepositoryInfo {
QString owner_account_name;
QString repository_name;
};
class AsyncExecGitThread_ : public QThread {
private:
GitPtr g;
std::function<void(GitPtr g)> callback;
public:
AsyncExecGitThread_(GitPtr g, std::function<void(GitPtr g)> callback)
: g(g)
, callback(callback)
{
}
protected:
void run()
{
callback(g);
}
};
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);
}
enum {
IndexRole = Qt::UserRole,
FilePathRole,
DiffIndexRole,
HunkIndexRole,
};
enum {
GroupItem = -1,
};
static inline bool isGroupItem(QTreeWidgetItem *item)
{
if (item) {
int index = item->data(0, IndexRole).toInt();
if (index == GroupItem) {
return true;
}
}
return false;
}
static inline QString getFilePath(QListWidgetItem *item)
{
if (!item) return QString();
return item->data(FilePathRole).toString();
}
static inline int indexOfLog(QListWidgetItem *item)
{
if (!item) return -1;
return item->data(IndexRole).toInt();
}
static inline int indexOfDiff(QListWidgetItem *item)
{
if (!item) return -1;
return item->data(DiffIndexRole).toInt();
}
static inline int getHunkIndex(QListWidgetItem *item)
{
if (!item) return -1;
return item->data(HunkIndexRole).toInt();
}
enum class PtyCondition {
None,
Clone,
Fetch,
Pull,
Push,
};
enum InteractionMode {
None,
Busy,
};
struct MainWindow::Private {
QString starting_dir;
Git::Context gcx;
ApplicationSettings appsettings;
QList<RepositoryItem> repos;
RepositoryItem current_repo;
ServerType server_type = ServerType::Standard;
GitHubRepositoryInfo github;
Git::Branch current_branch;
QString head_id;
struct Diff {
QList<Git::Diff> result;
std::shared_ptr<QThread> thread;
QList<std::shared_ptr<QThread>> garbage;
} diff;
std::map<QString, Git::Diff> diff_cache;
QStringList added;
Git::CommitItemList logs;
std::map<int, QList<Label>> label_map;
bool uncommited_changes = false;
int update_files_list_counter = 0;
QTimer interval_10ms_timer;
QImage graph_color;
QStringList remotes;
std::map<QString, QList<Git::Branch>> branch_map;
std::map<QString, QList<Git::Tag>> tag_map;
QString repository_filter_text;
QPixmap digits;
QIcon repository_icon;
QIcon folder_icon;
QIcon signature_good_icon;
QIcon signature_dubious_icon;
QIcon signature_bad_icon;
unsigned int temp_file_counter = 0;
GitObjectCache objcache;
QPixmap transparent_pixmap;
StatusLabel *status_bar_label;
WebContext webcx;
AvatarLoader avatar_loader;
int update_commit_table_counter = 0;
std::map<QString, GitHubAPI::User> committer_map; // key is email
PtyProcess pty_process;
PtyCondition pty_condition = PtyCondition::None;
bool pty_process_ok = false;
RepositoryItem temp_repo;
QListWidgetItem *last_selected_file_item = nullptr;
InteractionMode interaction_mode = InteractionMode::None;
bool interaction_canceled = false;
std::string ssh_passphrase_for;
std::string ssh_passphrase_secret;
std::string http_uid;
std::string http_pwd;
};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(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
m->starting_dir = QDir::current().absolutePath();
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->treeWidget_repos->installEventFilter(this);
ui->tableWidget_log->installEventFilter(this);
ui->listWidget_staged->installEventFilter(this);
ui->listWidget_unstaged->installEventFilter(this);
ui->widget_log->setupForLogWidget(ui->verticalScrollBar_log, ui->horizontalScrollBar_log, themeForTextEditor());
onLogVisibilityChanged();
SettingsDialog::loadSettings(&m->appsettings);
initNetworking();
showFileList(FilesListType::SingleList);
QFileIconProvider icons;
m->digits.load(":/image/digits.png");
m->graph_color = global->theme->graphColorMap();
m->repository_icon = QIcon(":/image/repository.png");
m->folder_icon = icons.icon(QFileIconProvider::Folder);
m->signature_good_icon = QIcon(":/image/signature-good.png");
m->signature_bad_icon = QIcon(":/image/signature-bad.png");
m->signature_dubious_icon = QIcon(":/image/signature-dubious.png");
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(ui->dockWidget_log, SIGNAL(visibilityChanged(bool)), this, SLOT(onLogVisibilityChanged()));
connect(ui->widget_log, SIGNAL(idle()), this, SLOT(onLogIdle()));
connect(ui->treeWidget_repos, SIGNAL(dropped()), this, SLOT(onRepositoriesTreeDropped()));
connect((AbstractPtyProcess *)&m->pty_process, SIGNAL(completed()), this, SLOT(onPtyProcessCompleted()));
QString path = getBookmarksFilePath();
m->repos = RepositoryBookmark::load(path);
updateRepositoriesList();
m->webcx.set_keep_alive_enabled(true);
m->avatar_loader.start(&m->webcx);
connect(&m->avatar_loader, SIGNAL(updated()), this, SLOT(onAvatarUpdated()));
m->update_files_list_counter = 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());
// ui->splitter->restoreState(settings.value("SplitterState").toByteArray());
settings.endGroup();
if (maximized) {
state |= Qt::WindowMaximized;
setWindowState(state);
}
}
startTimers();
}
MainWindow::~MainWindow()
{
stopPtyProcess();
m->avatar_loader.interrupt();
m->avatar_loader.wait();
deleteTempFiles();
delete m;
delete ui;
}
bool MainWindow::checkGitCommand()
{
while (1) {
if (misc::isExecutable(m->gcx.git_command)) {
return true;
}
if (selectGitCommand(true).isEmpty()) {
close();
return false;
}
}
}
bool MainWindow::checkFileCommand()
{
while (1) {
if (misc::isExecutable(global->file_command)) {
return true;
}
if (selectFileCommand(true).isEmpty()) {
close();
return false;
}
}
}
ApplicationSettings *MainWindow::appsettings()
{
return &m->appsettings;
}
ApplicationSettings const *MainWindow::appsettings() const
{
return &m->appsettings;
}
bool MainWindow::execWelcomeWizardDialog()
{
WelcomeWizardDialog dlg(this);
dlg.set_git_command_path(appsettings()->git_command);
dlg.set_file_command_path(appsettings()->file_command);
dlg.set_default_working_folder(appsettings()->default_working_dir);
if (misc::isExecutable(appsettings()->git_command)) {
m->gcx.git_command = appsettings()->git_command;
Git g(m->gcx, QString());
Git::User user = g.getUser(Git::Source::Global);
dlg.set_user_name(user.name);
dlg.set_user_email(user.email);
}
if (dlg.exec() == QDialog::Accepted) {
appsettings()->git_command = m->gcx.git_command = dlg.git_command_path();
appsettings()->file_command = global->file_command = dlg.file_command_path();
appsettings()->default_working_dir = dlg.default_working_folder();
SettingsDialog::saveSettings(&m->appsettings);
if (misc::isExecutable(appsettings()->git_command)) {
GitPtr g = git();
Git::User user;
user.name = dlg.user_name();
user.email = dlg.user_email();
g->setUser(user, true);
}
return true;
}
return false;
}
bool MainWindow::shown()
{
while (!misc::isExecutable(appsettings()->git_command) || !misc::isExecutable(appsettings()->file_command)) {
if (!execWelcomeWizardDialog()) {
return false;
}
}
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 s;
s.beginGroup("Remote");
bool f = s.value("Online", true).toBool();
s.endGroup();
setRemoteOnline(f);
}
setUnknownRepositoryInfo();
checkUser();
return true;
}
WebContext *MainWindow::webContext()
{
return &m->webcx;
}
void MainWindow::startTimers()
{
// interval 10ms
connect(&m->interval_10ms_timer, &QTimer::timeout, [&](){
const int ms = 10;
if (m->update_commit_table_counter > 0) {
if (m->update_commit_table_counter > ms) {
m->update_commit_table_counter -= ms;
} else {
m->update_commit_table_counter = 0;
ui->tableWidget_log->viewport()->update();
}
}
if (m->update_files_list_counter > 0) {
if (m->update_files_list_counter > ms) {
m->update_files_list_counter -= ms;
} else {
m->update_files_list_counter = 0;
updateCurrentFilesList();
}
}
});
m->interval_10ms_timer.setInterval(10);
m->interval_10ms_timer.start();
startTimer(10);
}
TextEditorThemePtr MainWindow::themeForTextEditor()
{
return global->theme->text_editor_theme;
}
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::KeyPress) {
QKeyEvent *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 (focusWidget() == ui->treeWidget_repos) {
removeSelectedRepositoryFromBookmark(true);
return true;
}
}
}
return QMainWindow::event(event);
}
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
QEvent::Type et = event->type();
if (et == QEvent::KeyPress) {
QKeyEvent *e = dynamic_cast<QKeyEvent *>(event);
Q_ASSERT(e);
int k = e->key();
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;
}
}
if (k == Qt::Key_Tab) {
ui->tableWidget_log->setFocus();
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;
}
if (k == Qt::Key_Tab) {
// consume the event
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) {
// ファイルリストがフォーカスを得たとき、diffビューを更新する。(コンテキストメニュー対応)
if (watched == ui->listWidget_unstaged) {
updateStatusBarText();
updateUnstagedFileCurrentItem();
return true;
}
if (watched == ui->listWidget_staged) {
updateStatusBarText();
updateStagedFileCurrentItem();
return true;
}
}
return false;
}
void MainWindow::closeEvent(QCloseEvent *event)
{
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);
}
{
MySettings settings;
settings.beginGroup("MainWindow");
settings.setValue("Maximized", maximized);
settings.setValue("Geometry", saveGeometry());
// settings.setValue("SplitterState", ui->splitter->saveState());
settings.endGroup();
}
}
QMainWindow::closeEvent(event);
}
void MainWindow::setStatusBarText(QString const &text)
{
m->status_bar_label->setText(text);
}
void MainWindow::clearStatusBarText()
{
setStatusBarText(QString());
}
WebContext *MainWindow::getWebContextPtr()
{
return &m->webcx;
}
QString MainWindow::getObjectID(QListWidgetItem *item)
{
int i = indexOfDiff(item);
if (i >= 0 && i < m->diff.result.size()) {
Git::Diff const &diff = m->diff.result[i];
return diff.blob.a_id;
}
return QString();
}
void MainWindow::onLogVisibilityChanged()
{
ui->action_window_log->setChecked(ui->dockWidget_log->isVisible());
}
void MainWindow::writeLog(char const *ptr, int len)
{
ui->widget_log->logicalMoveToBottom();
ui->widget_log->write(ptr, len, false);
ui->widget_log->setChanged(false);
m->interaction_canceled = false;
}
void MainWindow::writeLog(const QString &str)
{
std::string s = str.toStdString();
writeLog(s.c_str(), s.size());
}
void MainWindow::writeLog(QByteArray ba)
{
QString s = QString::fromUtf8(ba);
writeLog(s);
}
bool MainWindow::saveRepositoryBookmarks() const
{
QString path = getBookmarksFilePath();
return RepositoryBookmark::save(path, &m->repos);
}
void MainWindow::saveRepositoryBookmark(RepositoryItem item)
{
if (item.local_dir.isEmpty()) return;
if (item.name.isEmpty()) {
item.name = tr("Unnamed");
}
bool done = false;
for (int i = 0; i < m->repos.size(); i++) {
RepositoryItem *p = &m->repos[i];
if (item.local_dir == p->local_dir) {
*p = item;
done = true;
break;
}
}
if (!done) {
m->repos.push_back(item);
}
saveRepositoryBookmarks();
updateRepositoriesList();
}
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);
}
m->repos = 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 MainWindow::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;
}
bool MainWindow::isThereUncommitedChanges() const
{
return m->uncommited_changes;
}
Git::CommitItemList const *MainWindow::logs() const
{
return &m->logs;
}
QString MainWindow::currentWorkingCopyDir() const
{
QString workdir = m->current_repo.local_dir;
if (!workdir.isEmpty()) return workdir;
QTreeWidgetItem *treeitem = ui->treeWidget_repos->currentItem();
if (treeitem) {
RepositoryItem const *repo = repositoryItem(treeitem);
if (repo) {
workdir = repo->local_dir;
return workdir;
}
}
return QString();
}
GitPtr MainWindow::git(QString const &dir) const
{
const_cast<MainWindow *>(this)->checkGitCommand();
GitPtr g = std::shared_ptr<Git>(new Git(m->gcx, dir));
g->setLogCallback(git_callback, (void *)this);
return g;
}
GitPtr MainWindow::git()
{
return git(currentWorkingCopyDir());
}
bool MainWindow::queryCommit(QString const &id, Git::CommitItem *out)
{
*out = Git::CommitItem();
GitPtr g = git();
return g->queryCommit(id, out);
}
void MainWindow::setLogEnabled(GitPtr g, bool f)
{
if (f) {
g->setLogCallback(git_callback, this);
} else {
g->setLogCallback(nullptr, nullptr);
}
}
QString MainWindow::makeRepositoryName(QString const &loc)
{
int i = loc.lastIndexOf('/');
int j = loc.lastIndexOf('\\');
if (i < j) i = j;
if (i >= 0) {
i++;
j = loc.size();
if (loc.endsWith(".git")) {
j -= 4;
}
return loc.mid(i, j - i);
}
return QString();
}
RepositoryItem const *MainWindow::findRegisteredRepository(QString *workdir) const
{
*workdir = QDir(*workdir).absolutePath();
workdir->replace('\\', '/');
- for (RepositoryItem const &item : m->repos) {
- Qt::CaseSensitivity cs = Qt::CaseSensitive;
+ if (Git::isValidWorkingCopy(*workdir)) {
+ for (RepositoryItem const &item : m->repos) {
+ Qt::CaseSensitivity cs = Qt::CaseSensitive;
#ifdef Q_OS_WIN
- cs = Qt::CaseInsensitive;
+ cs = Qt::CaseInsensitive;
#endif
- if (workdir->compare(item.local_dir, cs) == 0) {
- return &item;
+ 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 < m->repos.size()) {
return i;
}
}
return -1;
}
RepositoryItem const *MainWindow::repositoryItem(QTreeWidgetItem const *item) const
{
int row = repositoryIndex_(item);
if (row >= 0 && row < m->repos.size()) {
return &m->repos[row];
}
return nullptr;
}
static QTreeWidgetItem *newQTreeWidgetItem()
{
QTreeWidgetItem *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, m->folder_icon);
item->setFlags(item->flags() | Qt::ItemIsEditable);
return item;
}
void MainWindow::updateRepositoriesList()
{
QString path = getBookmarksFilePath();
m->repos = RepositoryBookmark::load(path);
QString filter = m->repository_filter_text;
ui->treeWidget_repos->clear();
std::map<QString, QTreeWidgetItem *> parentmap;
for (int i = 0; i < m->repos.size(); i++) {
RepositoryItem const &repo = m->repos[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, m->repository_icon);
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->setCurrentWidget(ui->page_files);
break;
case FilesListType::SideBySide:
ui->stackedWidget->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()
{
m->head_id = QString();
m->current_branch = Git::Branch();
m->server_type = ServerType::Standard;
m->github = GitHubRepositoryInfo();
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::setUnknownRepositoryInfo()
{
setRepositoryInfo("---", "");
Git g(m->gcx, QString());
Git::User user = g.getUser(Git::Source::Global);
setWindowTitle_(user);
}
bool MainWindow::makeDiff(QString id, QList<Git::Diff> *out)
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return false;
Git::FileStatusList list = g->status();
m->uncommited_changes = !list.empty();
if (id.isEmpty() && !isThereUncommitedChanges()) {
id = m->objcache.revParse("HEAD");
}
bool uncommited = (id.isEmpty() && isThereUncommitedChanges());
GitDiff dm(&m->objcache);
if (uncommited) {
dm.diff_uncommited(out);
} else {
dm.diff(id, out);
}
return true; // success
}
void MainWindow::addDiffItems(QList<Git::Diff> const *diff_list, std::function<void(QString const &filename, QString header, int idiff)> add_item)
{
for (int idiff = 0; idiff < diff_list->size(); idiff++) {
Git::Diff const &diff = diff_list->at(idiff);
QString header;
switch (diff.type) {
case Git::Diff::Type::Modify: header = "(chg) "; break;
case Git::Diff::Type::Copy: header = "(cpy) "; break;
case Git::Diff::Type::Rename: header = "(ren) "; break;
case Git::Diff::Type::Create: header = "(add) "; break;
case Git::Diff::Type::Delete: header = "(del) "; break;
case Git::Diff::Type::ChType: header = "(chg) "; break;
case Git::Diff::Type::Unmerged: header = "(unmerged) "; break;
}
add_item(diff.path, header, idiff);
}
}
void MainWindow::updateFilesList(QString id, bool wait)
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
if (!wait) return;
clearFileList();
Git::FileStatusList stats = g->status();
m->uncommited_changes = !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, &m->diff.result)) {
return;
}
std::map<QString, int> diffmap;
for (int idiff = 0; idiff < m->diff.result.size(); idiff++) {
Git::Diff const &diff = m->diff.result[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, &m->diff.result)) {
return;
}
showFileList(files_list_type);
addDiffItems(&m->diff.result, AddItem);
}
for (Git::Diff const &diff : m->diff.result) {
QString key = GitDiff::makeKey(diff);
m->diff_cache[key] = diff;
}
}
void MainWindow::updateFilesList(QString id, QList<Git::Diff> *diff_list, QListWidget *listwidget)
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
listwidget->clear();
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);
listwidget->addItem(item);
};
GitDiff dm(&m->objcache);
if (!dm.diff(id, diff_list)) return;
addDiffItems(diff_list, AddItem);
}
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()
{
QTableWidgetItem *item = ui->tableWidget_log->item(selectedLogIndex(), 0);
if (!item) return;
int row = item->data(IndexRole).toInt();
int logs = (int)m->logs.size();
if (row < logs) {
updateFilesList(m->logs[row], true);
}
}
void MainWindow::prepareLogTableWidget()
{
QStringList cols = {
tr("Graph"),
tr("Commit"),
tr("Date"),
tr("Author"),
tr("Description"),
};
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);
}
updateCommitGraph(); // コミットグラフを更新
}
void MainWindow::clearAuthentication()
{
m->ssh_passphrase_for.clear();
m->ssh_passphrase_secret.clear();
m->http_uid.clear();
m->http_pwd.clear();
}
void MainWindow::setCurrentRepository(RepositoryItem const &repo, bool clear_authentication)
{
if (clear_authentication) {
clearAuthentication();
}
m->current_repo = repo;
}
QString MainWindow::currentRepositoryName() const
{
return m->current_repo.name;
}
Git::Branch const &MainWindow::currentBranch() const
{
return m->current_branch;
}
bool MainWindow::isValidWorkingCopy(GitPtr const &g) const
{
return g && g->isValidWorkingCopy();
}
void MainWindow::queryRemotes(GitPtr g)
{
m->remotes = g->getRemotes();
std::sort(m->remotes.begin(), m->remotes.end());
}
void MainWindow::queryBranches(GitPtr g)
{
Q_ASSERT(g);
m->branch_map.clear();
QList<Git::Branch> branches = g->branches();
for (Git::Branch const &b : branches) {
if (b.isCurrent()) {
m->current_branch = b;
}
m->branch_map[b.id].append(b);
}
}
void MainWindow::updateRemoteInfo()
{
queryRemotes(git());
QString current_remote;
{
Git::Branch const &r = currentBranch();
current_remote = r.remote;
}
if (current_remote.isEmpty()) {
if (m->remotes.size() == 1) {
current_remote = m->remotes[0];
}
}
ui->lineEdit_remote->setText(current_remote);
}
QList<Git::Branch> MainWindow::findBranch(QString const &id)
{
auto it = m->branch_map.find(id);
if (it != m->branch_map.end()) {
return it->second;
}
return QList<Git::Branch>();
}
QString MainWindow::abbrevCommitID(Git::CommitItem const &commit)
{
return commit.commit_id.mid(0, 7);
}
QString MainWindow::findFileID(GitPtr /*g*/, const QString &commit_id, const QString &file)
{
return lookupFileID(&m->objcache, commit_id, file);
}
bool MainWindow::isGitHub() const
{
return m->server_type == ServerType::GitHub;
}
void MainWindow::updateCommitTableLater()
{
m->update_commit_table_counter = 200;
}
void MainWindow::onAvatarUpdated()
{
updateCommitTableLater();
}
bool MainWindow::isAvatarEnabled() const
{
return appsettings()->get_committer_icon;
}
Git::CommitItem const *MainWindow::commitItem(int row) const
{
if (row >= 0 && row < (int)m->logs.size()) {
return &m->logs[row];
}
return nullptr;
}
QIcon MainWindow::verifiedIcon(char s) const
{
Git::SignatureGrade g = Git::evaluateSignature(s);
switch (g) {
case Git::SignatureGrade::Good:
return m->signature_good_icon;
case Git::SignatureGrade::Bad:
return m->signature_bad_icon;
case Git::SignatureGrade::Unknown:
case Git::SignatureGrade::Dubious:
case Git::SignatureGrade::Missing:
return m->signature_dubious_icon;
}
return QIcon();
}
QIcon MainWindow::committerIcon(int row) const
{
QIcon icon;
if (isAvatarEnabled()) {
if (row >= 0 && row < (int)m->logs.size()) {
Git::CommitItem const &commit = m->logs[row];
if (commit.email.indexOf('@') > 0) {
std::string email = commit.email.toStdString();
icon = m->avatar_loader.fetch(email, true); // from gavatar
}
}
}
return icon;
}
QList<MainWindow::Label> const *MainWindow::label(int row)
{
auto it = m->label_map.find(row);
if (it != m->label_map.end()) {
return &it->second;
}
return nullptr;
}
QString MainWindow::makeCommitInfoText(int row, QList<Label> *label_list)
{
QString message_ex;
Git::CommitItem const *commit = &m->logs[row];
{ // branch
if (label_list) {
if (commit->commit_id == m->head_id) {
Label label(Label::Head);
label.text = "HEAD";
label_list->push_back(label);
}
}
QList<Git::Branch> list = findBranch(commit->commit_id);
for (Git::Branch const &b : list) {
if (b.flags & Git::Branch::HeadDetachedAt) continue;
if (b.flags & Git::Branch::HeadDetachedFrom) continue;
Label label(Label::LocalBranch);
label.text = b.name;
if (!b.remote.isEmpty()) {
label.kind = Label::RemoteBranch;
label.text = "remotes" / b.remote / label.text;
}
if (b.ahead > 0) {
label.text += tr(", %1 ahead").arg(b.ahead);
}
if (b.behind > 0) {
label.text += tr(", %1 behind").arg(b.behind);
}
message_ex += " {" + label.text + '}';
if (label_list) label_list->push_back(label);
}
}
{ // tag
QList<Git::Tag> list = findTag(commit->commit_id);
for (Git::Tag const &t : list) {
Label label(Label::Tag);
label.text = t.name;
message_ex += QString(" {#%1}").arg(label.text);
if (label_list) label_list->push_back(label);
}
}
return message_ex;
}
void MainWindow::setWindowTitle_(Git::User const &user)
{
if (user.name.isEmpty() && user.email.isEmpty()) {
setWindowTitle(qApp->applicationName());
} else {
setWindowTitle(QString("%1 : %2 <%3>")
.arg(qApp->applicationName())
.arg(user.name)
.arg(user.email)
);
}
}
void MainWindow::updateWindowTitle(GitPtr g)
{
if (isValidWorkingCopy(g)) {
Git::User user = g->getUser(Git::Source::Default);
setWindowTitle_(user);
} else {
setUnknownRepositoryInfo();
}
}
QStringList MainWindow::remotes() const
{
return m->remotes;
}
int MainWindow::limitLogCount() const
{
int n = appsettings()->maximum_number_of_commit_item_acquisitions;
return (n >= 1 && n <= 100000) ? n : 10000;
}
struct TemporaryCommitItem {
Git::CommitItem const *commit;
std::vector<TemporaryCommitItem *> children;
};
Git::CommitItemList MainWindow::retrieveCommitLog(GitPtr g)
{
Git::CommitItemList list = g->log(limitLogCount());
// 親子関係を調べて、順番が狂っていたら、修正する。
std::set<QString> set;
size_t i = 0;
while (i < list.size()) {
size_t newpos = -1;
for (QString const &parent : list[i].parent_ids) {
auto it = set.find(parent);
if (it != set.end()) {
for (size_t j = 0; j < i; j++) {
if (parent == list[j].commit_id) {
if (newpos == (size_t)-1 || j < newpos) {
newpos = j;
}
qDebug() << "fix commit order" << parent;
break;
}
}
}
}
set.insert(set.end(), list[i].commit_id);
if (newpos != (size_t)-1) {
Git::CommitItem t = list[newpos];
list.erase(list.begin() + newpos);
list.insert(list.begin() + i, t);
}
i++;
}
return list;
}
void MainWindow::detectGitServerType(GitPtr g)
{
m->server_type = ServerType::Standard;
m->github = 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) {
QString user = s.mid(0, i);
QString repo = s.mid(i + 1);
m->github.owner_account_name = user;
m->github.repository_name = repo;
}
m->server_type = ServerType::GitHub;
}
}
bool MainWindow::fetch(GitPtr g)
{
m->pty_condition = PtyCondition::Fetch;
m->pty_process_ok = true;
g->fetch(&m->pty_process);
while (1) {
if (m->pty_process.wait(1)) break;
QApplication::processEvents();
}
return m->pty_process_ok;
}
void MainWindow::clearLog()
{
m->logs.clear();
m->label_map.clear();
m->uncommited_changes = false;
ui->tableWidget_log->clearContents();
ui->tableWidget_log->scrollToTop();
}
void MainWindow::openRepository_(GitPtr g)
{
m->objcache.setup(g);
if (isValidWorkingCopy(g)) {
if (isRemoteOnline() && appsettings()->automatically_fetch_when_opening_the_repository) {
if (!fetch(g)) {
return;
}
}
clearLog();
clearRepositoryInfo();
detectGitServerType(g);
updateFilesList(QString(), true);
bool canceled = false;
ui->tableWidget_log->setEnabled(false);
// ログを取得
m->logs = retrieveCommitLog(g);
// ブランチを取得
queryBranches(g);
// タグを取得
m->tag_map.clear();
QList<Git::Tag> tags = g->tags();
for (Git::Tag const &tag : tags) {
Git::Tag t = tag;
t.id = m->objcache.getCommitIdFromTag(t.id);
m->tag_map[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(currentBranch().name);
}
if (currentBranch().flags & Git::Branch::HeadDetachedFrom) {
branch_name += QString("(HEAD detached from %1)").arg(currentBranch().name);
}
if (branch_name.isEmpty()) {
branch_name = currentBranch().name;
}
QString repo_name = currentRepositoryName();
setRepositoryInfo(repo_name, branch_name);
} else {
clearLog();
clearRepositoryInfo();
}
updateRemoteInfo();
updateWindowTitle(g);
m->head_id = m->objcache.revParse("HEAD");
if (isThereUncommitedChanges()) {
Git::CommitItem item;
item.parent_ids.push_back(m->current_branch.id);
item.message = tr("Uncommited changes");
m->logs.insert(m->logs.begin(), item);
}
prepareLogTableWidget();
int count = m->logs.size();
ui->tableWidget_log->setRowCount(count);
int selrow = -1;
for (int row = 0; row < count; row++) {
Git::CommitItem const *commit = &m->logs[row];
{
QTableWidgetItem *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){
QTableWidgetItem *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 == m->head_id;
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, &m->label_map[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);
}
ui->tableWidget_log->resizeColumnsToContents();
ui->tableWidget_log->horizontalHeader()->setStretchLastSection(false);
ui->tableWidget_log->horizontalHeader()->setStretchLastSection(true);
ui->tableWidget_log->setFocus();
setCurrentLogRow(0);
QTableWidgetItem *p = ui->tableWidget_log->item(selrow < 0 ? 0 : selrow, 2);
ui->tableWidget_log->setCurrentItem(p);
udpateButton();
}
void MainWindow::removeRepositoryFromBookmark(int index, bool ask)
{
if (ask) {
int r = QMessageBox::warning(this, tr("Confirm Remove"), tr("Are you sure you want to remove the repository from bookmarks ?") + '\n' + tr("(Files will NOT be deleted)"), QMessageBox::Ok, QMessageBox::Cancel);
if (r != QMessageBox::Ok) return;
}
if (index >= 0 && index < m->repos.size()) {
m->repos.erase(m->repos.begin() + index);
saveRepositoryBookmarks();
updateRepositoriesList();
}
}
void MainWindow::removeSelectedRepositoryFromBookmark(bool ask)
{
int i = indexOfRepository(ui->treeWidget_repos->currentItem());
removeRepositoryFromBookmark(i, ask);
}
void MainWindow::openRepository(bool validate, bool waitcursor)
{
if (validate) {
QString dir = currentWorkingCopyDir();
if (!QFileInfo(dir).isDir()) {
int r = QMessageBox::warning(this, tr("Open Repository"), dir + "\n\n" + tr("No such folder") + "\n\n" + tr("Remove from bookmark ?"), QMessageBox::Ok, QMessageBox::Cancel);
if (r == QMessageBox::Ok) {
removeSelectedRepositoryFromBookmark(false);
}
return;
}
if (!Git::isValidWorkingCopy(dir)) {
QMessageBox::warning(this, tr("Open Repository"), tr("Not a valid git repository") + "\n\n" + dir);
return;
}
}
if (waitcursor) {
OverrideWaitCursor;
openRepository(false, false);
return;
}
GitPtr g = git(); // ポインタの有効性チェックはしない(nullptrでも続行)
openRepository_(g);
}
void MainWindow::reopenRepository(bool log, std::function<void(GitPtr g)> callback)
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
OverrideWaitCursor;
if (log) {
setLogEnabled(g, true);
AsyncExecGitThread_ th(g, callback);
th.start();
while (1) {
if (th.wait(1)) break;
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
setLogEnabled(g, false);
} else {
callback(g);
}
openRepository_(g);
}
void MainWindow::udpateButton()
{
Git::Branch b = currentBranch();
int n;
n = b.ahead > 0 ? b.ahead : -1;
ui->toolButton_push->setNumber(n);
n = b.behind > 0 ? b.behind : -1;
ui->toolButton_pull->setNumber(n);
}
void MainWindow::autoOpenRepository(QString dir)
{
auto Open = [&](RepositoryItem const &item){
setCurrentRepository(item, true);
openRepository(false, true);
};
RepositoryItem const *repo = findRegisteredRepository(&dir);
if (repo) {
Open(*repo);
return;
}
RepositoryItem newitem;
GitPtr g = git(dir);
if (isValidWorkingCopy(g)) {
ushort const *left = dir.utf16();
ushort const *right = left + dir.size();
if (right[-1] == '/' || right[-1] == '\\') {
right--;
}
ushort const *p = right;
while (left + 1 < p && !(p[-1] == '/' || p[-1] == '\\')) p--;
if (p < right) {
newitem.local_dir = dir;
newitem.name = QString::fromUtf16(p, right - p);
saveRepositoryBookmark(newitem);
Open(newitem);
return;
}
+ } else {
+ DoYouWantToInitDialog dlg(this, dir);
+ if (dlg.exec() == QDialog::Accepted) {
+ createRepository(dir);
+ }
}
}
void MainWindow::openSelectedRepository()
{
QTreeWidgetItem *treeitem = ui->treeWidget_repos->currentItem();
RepositoryItem const *item = repositoryItem(treeitem);
if (item) {
setCurrentRepository(*item, true);
openRepository(true);
}
}
QString MainWindow::getBookmarksFilePath() const
{
return global->app_config_dir / "bookmarks.xml";
}
void MainWindow::commit(bool amend)
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
while (1) {
Git::User user = g->getUser(Git::Source::Default);
QString sign_id = g->signingKey(Git::Source::Default);
gpg::Data key;
{
QList<gpg::Data> keys;
gpg::listKeys(global->gpg_command, &keys);
for (gpg::Data const &k : keys) {
if (k.id == sign_id) {
key = k;
}
}
}
CommitDialog dlg(this, currentRepositoryName(), user, key);
if (amend) {
dlg.setText(m->logs[0].message);
}
if (dlg.exec() == QDialog::Accepted) {
QString text = dlg.text();
if (text.isEmpty()) {
QMessageBox::warning(this, tr("Commit"), tr("Commit message can not be omitted."));
continue;
}
bool sign = dlg.isSigningEnabled();
bool ok;
if (amend) {
ok = g->commit_amend_m(text, sign, &m->pty_process);
} else {
ok = g->commit(text, sign, &m->pty_process);
}
if (ok) {
openRepository(true);
} else {
QString err = g->errorMessage().trimmed();
err += "\n*** ";
err += tr("Failed to commit");
err += " ***\n";
writeLog(err);
}
break;
} else {
break;
}
}
}
void MainWindow::commit_amend()
{
commit(true);
}
void MainWindow::updateStatusBarText()
{
QString text;
QWidget *w = QWidget::focusWidget();
if (w == ui->treeWidget_repos) {
QTreeWidgetItem *ite = ui->treeWidget_repos->currentItem();
RepositoryItem const *item = repositoryItem(ite);
if (item) {
text = QString("%1 : %2")
.arg(item->name)
.arg(misc::normalizePathSeparator(item->local_dir))
;
}
} else if (w == ui->tableWidget_log) {
QTableWidgetItem *item = ui->tableWidget_log->item(selectedLogIndex(), 0);
if (item) {
int row = item->data(IndexRole).toInt();
if (row < (int)m->logs.size()) {
Git::CommitItem const &commit = m->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::execRepositoryPropertyDialog(QString workdir, bool open_repository_menu)
{
if (workdir.isEmpty()) {
workdir = currentWorkingCopyDir();
}
QString name;
RepositoryItem const *repo = findRegisteredRepository(&workdir);
if (repo) {
name = repo->name;
}
if (name.isEmpty()) {
name = makeRepositoryName(workdir);
}
GitPtr g = git(workdir);
RepositoryPropertyDialog dlg(this, g, *repo, open_repository_menu);
dlg.exec();
if (dlg.isRemoteChanged()) {
updateRemoteInfo();
}
}
void MainWindow::on_action_commit_triggered()
{
commit();
}
void MainWindow::on_action_fetch_triggered()
{
if (!isRemoteOnline()) return;
reopenRepository(true, [&](GitPtr g){
fetch(g);
});
}
void MainWindow::on_action_push_triggered()
{
if (!isRemoteOnline()) return;
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
if (g->getRemotes().isEmpty()) {
QMessageBox::warning(this, qApp->applicationName(), tr("No remote repository is registered."));
execRepositoryPropertyDialog(QString(), true);
return;
}
int exitcode = 0;
QString errormsg;
reopenRepository(true, [&](GitPtr g){
g->push(false, &m->pty_process);
while (1) {
if (m->pty_process.wait(1)) break;
QApplication::processEvents();
}
exitcode = m->pty_process.getExitCode();
errormsg = m->pty_process.getMessage();
});
if (exitcode == 128) {
if (errormsg.indexOf("no upstream branch") >= 0) {
QString brname = currentBranch().name;
QString msg = tr("The current branch %1 has no upstream branch.");
msg = msg.arg(brname);
msg += '\n';
msg += tr("You try push --set-upstream");
QMessageBox::warning(this, qApp->applicationName(), msg);
pushSetUpstream(false);
return;
}
if (errormsg.indexOf("Connection refused") >= 0) {
QMessageBox::critical(this, qApp->applicationName(), tr("Connection refused."));
return;
}
}
}
void MainWindow::on_action_pull_triggered()
{
if (!isRemoteOnline()) return;
reopenRepository(true, [&](GitPtr g){
m->pty_condition = PtyCondition::Pull;
m->pty_process_ok = true;
g->pull(&m->pty_process);
while (1) {
if (m->pty_process.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 MainWindow::execCommitPropertyDialog(QWidget *parent, Git::CommitItem const *commit)
{
CommitPropertyDialog dlg(parent, this, commit);
dlg.exec();
}
QAction *MainWindow::addMenuActionProperty(QMenu *menu)
{
return menu->addAction(tr("&Property"));
}
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
QAction *a_open_folder = menu.addAction(tr("Open &folder"));
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::execCommitExploreWindow(QWidget *parent, Git::CommitItem const *commit)
{
CommitExploreWindow win(parent, this, &m->objcache, commit);
win.exec();
}
void MainWindow::on_tableWidget_log_customContextMenuRequested(const QPoint &pos)
{
Git::CommitItem const *commit = selectedCommitItem();
if (commit) {
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_push_upstream = nullptr;
if (pushSetUpstream(true)) {
a_push_upstream = menu.addAction("push --set-upstream ...");
}
QAction *a_edit_comment = nullptr;
if (row == 0 && currentBranch().ahead > 0) {
a_edit_comment = menu.addAction(tr("Edit comment..."));
}
bool is_valid_commit_id = Git::isValidID(commit->commit_id);
QAction *a_checkout = is_valid_commit_id ? menu.addAction(tr("Checkout/Branch...")) : nullptr;
QAction *a_delbranch = is_valid_commit_id ? menu.addAction(tr("Delete branch...")) : nullptr;
QAction *a_delrembranch = remoteBranches(commit->commit_id).isEmpty() ? nullptr : menu.addAction(tr("Delete remote branch..."));
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_explore = is_valid_commit_id ? menu.addAction(tr("Explore")) : nullptr;
QAction *a_reset_head = nullptr;
#if 0 // 下手に使うと危険なので、とりあえず無効にしておく
if (is_valid_commit_id && commit->commit_id == m->head_id) {
a_reset_head = menu.addAction(tr("Reset HEAD"));
}
#endif
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_push_upstream) {
pushSetUpstream(false);
return;
}
if (a == a_edit_comment) {
commit_amend();
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);
}
}
}
}
}
bool MainWindow::askAreYouSureYouWantToRun(QString const &title, QString const &command)
{
QString message = tr("Are you sure you want to run the following command ?");
QString text = "%1\n\n%2";
text = text.arg(message).arg(command);
return QMessageBox::warning(this, title, text, QMessageBox::Ok, QMessageBox::Cancel) == QMessageBox::Ok;
}
void MainWindow::resetFile(QStringList const &paths)
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
if (paths.isEmpty()) {
// nop
} else {
QString cmd = "git checkout -- \"%1\"";
cmd = cmd.arg(paths[0]);
if (askAreYouSureYouWantToRun(tr("Reset a file"), "> " + cmd)) {
for (QString const &path : paths) {
g->resetFile(path);
}
openRepository(true);
}
}
}
void MainWindow::revertAllFiles()
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
QString cmd = "git reset --hard HEAD";
if (askAreYouSureYouWantToRun(tr("Revert all files"), "> " + cmd)) {
g->resetAllFiles();
openRepository(true);
}
}
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
{
QWidget *w = QWidget::focusWidget();
if (w == ui->listWidget_files) return selectedFiles_(ui->listWidget_files);
if (w == ui->listWidget_staged) return selectedFiles_(ui->listWidget_staged);
if (w == 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 MainWindow::execFileHistory(QString const &path)
{
if (path.isEmpty()) return;
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
FileHistoryWindow dlg(this);
dlg.prepare(g, path);
dlg.exec();
}
void MainWindow::execFileHistory(QListWidgetItem *item)
{
if (item) {
QString path = getFilePath(item);
if (!path.isEmpty()) {
execFileHistory(path);
}
}
}
void MainWindow::addWorkingCopyDir(QString dir, QString name, bool open)
{
if (dir.endsWith(".git")) {
int i = dir.size();
if (i > 4) {
ushort c = dir.utf16()[i - 5];
if (c == '/' || c == '\\') {
dir = dir.mid(0, i - 5);
}
}
}
if (!Git::isValidWorkingCopy(dir)) {
qDebug() << "Invalid working dir: " + dir;
return;
}
if (name.isEmpty()) {
name = makeRepositoryName(dir);
}
RepositoryItem item;
item.local_dir = dir;
item.name = name;
saveRepositoryBookmark(item);
if (open) {
setCurrentRepository(item, true);
GitPtr g = git(item.local_dir);
openRepository_(g);
}
}
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;
int row = item->data(IndexRole).toInt();
if (row < (int)m->logs.size()) {
updateStatusBarText();
m->update_files_list_counter = 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();
}
bool MainWindow::editFile(const QString &path, QString const &title)
{
return TextEditDialog::editFile(this, path, title);
}
struct Task {
int index = 0;
int parent = 0;
Task()
{
}
Task(int index, int parent)
: index(index)
, parent(parent)
{
}
};
struct Element {
int depth = 0;
std::vector<int> indexes;
};
void MainWindow::updateCommitGraph()
{
const size_t LogCount = m->logs.size();
// 樹形図情報を構築する
if (LogCount > 0) {
auto LogItem = [&](size_t i)->Git::CommitItem &{ return m->logs[i]; };
enum { // 有向グラフを構築するあいだ CommitItem::marker_depth をフラグとして使用する
UNKNOWN = 0,
KNOWN = 1,
};
for (Git::CommitItem &item : m->logs) {
item.marker_depth = UNKNOWN;
}
// コミットハッシュを検索して、親コミットのインデックスを求める
for (size_t i = 0; i < LogCount; i++) {
Git::CommitItem *item = &LogItem(i);
item->parent_lines.clear();
if (item->parent_ids.empty()) {
item->resolved = true;
} else {
for (int j = 0; j < item->parent_ids.size(); j++) { // 親の数だけループ
QString const &parent_id = item->parent_ids[j]; // 親のハッシュ値
for (size_t k = i + 1; k < LogCount; k++) { // 親を探す
if (LogItem(k).commit_id == parent_id) { // ハッシュ値が一致したらそれが親
item->parent_lines.push_back(k); // インデックス値を記憶
LogItem(k).has_child = true;
LogItem(k).marker_depth = KNOWN;
item->resolved = true;
break;
}
}
}
}
}
std::vector<Element> elements; // 線分リスト
{ // 線分リストを作成する
std::deque<Task> tasks; // 未処理タスクリスト
{
for (size_t i = 0; i < LogCount; i++) {
Git::CommitItem *item = &LogItem(i);
if (item->marker_depth == UNKNOWN) {
int n = item->parent_lines.size(); // 最初のコミットアイテム
for (int j = 0; j < n; j++) {
tasks.push_back(Task(i, j)); // タスクを追加
}
}
item->marker_depth = UNKNOWN;
}
}
while (!tasks.empty()) { // タスクが残っているならループ
Element e;
Task task;
{ // 最初のタスクを取り出す
task = tasks.front();
tasks.pop_front();
}
e.indexes.push_back(task.index); // 先頭のインデックスを追加
int index = LogItem(task.index).parent_lines[task.parent].index; // 開始インデックス
while (index > 0 && (size_t)index < LogCount) { // 最後に到達するまでループ
e.indexes.push_back(index); // インデックスを追加
int n = LogItem(index).parent_lines.size(); // 親の数
if (n == 0) break; // 親がないなら終了
Git::CommitItem *item = &LogItem(index);
if (item->marker_depth == KNOWN) break; // 既知のアイテムに到達したら終了
item->marker_depth = KNOWN; // 既知のアイテムにする
for (int i = 1; i < n; i++) {
tasks.push_back(Task(index, i)); // タスク追加
}
index = LogItem(index).parent_lines[0].index; // 次の親(親リストの先頭の要素)
}
if (e.indexes.size() >= 2) {
elements.push_back(e);
}
}
}
// 線情報をクリア
for (Git::CommitItem &item : m->logs) {
item.marker_depth = -1;
item.parent_lines.clear();
}
// マークと線の深さを決める
if (!elements.empty()) {
{ // 優先順位を調整する
std::sort(elements.begin(), elements.end(), [](Element const &left, Element const &right){
int i = 0;
{ // 長いものを優先して左へ
int l = left.indexes.back() - left.indexes.front();
int r = right.indexes.back() - right.indexes.front();
i = r - l; // 降順
}
if (i == 0) {
// コミットが新しいものを優先して左へ
int l = left.indexes.front();
int r = right.indexes.front();
i = l - r; // 昇順
}
return i < 0;
});
// 子の無いブランチ(タグ等)が複数連続しているとき、古いコミットを右に寄せる
{
for (size_t i = 0; i + 1 < elements.size(); i++) {
Element *e = &elements[i];
int index1 = e->indexes.front();
if (index1 > 0 && !LogItem(index1).has_child) { // 子がない
// 新しいコミットを探す
for (size_t j = i + 1; j < elements.size(); j++) { // 現在位置より後ろを探す
Element *f = &elements[j];
int index2 = f->indexes.front();
if (index1 == index2 + 1) { // 一つだけ新しいコミット
Element t = std::move(*f);
elements.erase(elements.begin() + j); // 移動元を削除
elements.insert(elements.begin() + i, std::move(t)); // 現在位置に挿入
}
}
// 古いコミットを探す
size_t j = 0;
while (j < i) { // 現在位置より前を探す
Element *f = &elements[j];
int index2 = f->indexes.front();
if (index1 + 1 == index2) { // 一つだけ古いコミット
Element t = std::move(*f);
elements.erase(elements.begin() + j); // 移動元を削除
elements.insert(elements.begin() + i, std::move(t)); // 現在位置の次に挿入
index1 = index2;
f = e;
} else {
j++;
}
}
}
}
}
}
{ // 最初の線は深さを0にする
Element *e = &elements.front();
for (size_t i = 0; i < e->indexes.size(); i++) {
int index = e->indexes[i];
LogItem(index).marker_depth = 0; // マークの深さを設定
e->depth = 0; // 線の深さを設定
}
}
// 最初以外の線分の深さを決める
for (size_t i = 1; i < elements.size(); i++) { // 最初以外をループ
Element *e = &elements[i];
int depth = 1;
while (1) { // 失敗したら繰り返し
for (size_t j = 0; j < i; j++) { // 既に処理済みの線を調べる
Element const *f = &elements[j]; // 検査対象
if (e->indexes.size() == 2) { // 二つしかない場合
int from = e->indexes[0]; // 始点
int to = e->indexes[1]; // 終点
if (LogItem(from).has_child) {
for (size_t k = 0; k + 1 < f->indexes.size(); k++) { // 検査対象の全ての線分を調べる
int curr = f->indexes[k];
int next = f->indexes[k + 1];
if (from > curr && to == next) { // 決定済みの線に直結できるか判定
e->indexes.back() = from + 1; // 現在の一行下に直結する
e->depth = elements[j].depth; // 接続先の深さ
goto DONE; // 決定
}
}
}
}
if (depth == f->depth) { // 同じ深さ
if (e->indexes.back() > f->indexes.front() && e->indexes.front() < f->indexes.back()) { // 重なっている
goto FAIL; // この深さには線を置けないのでやりなおし
}
}
}
for (size_t j = 0; j < e->indexes.size(); j++) {
int index = e->indexes[j];
Git::CommitItem *item = &LogItem(index);
if (j == 0 && item->has_child) { // 最初のポイントで子がある場合
// nop
} else if ((j > 0 && j + 1 < e->indexes.size()) || item->marker_depth < 0) { // 最初と最後以外、または、未確定の場合
item->marker_depth = depth; // マークの深さを設定
}
}
e->depth = depth; // 深さを決定
goto DONE; // 決定
FAIL:;
depth++; // 一段深くして再挑戦
}
DONE:;
}
// 線情報を生成する
for (size_t i = 0; i < elements.size(); i++) {
Element const &e = elements[i];
auto ColorNumber = [&](){ return e.depth; };
size_t count = e.indexes.size();
for (size_t j = 0; j + 1 < count; j++) {
int curr = e.indexes[j];
int next = e.indexes[j + 1];
TreeLine line(next, e.depth);
line.color_number = ColorNumber();
line.bend_early = (j + 2 < count || !LogItem(next).resolved);
if (j + 2 == count) {
if (count > 2 || !LogItem(curr).has_child) { // 直結ではない、または、子がない
line.depth = LogItem(next).marker_depth; // 合流する先のアイテムの深さと同じにする
}
}
LogItem(curr).parent_lines.push_back(line); // 線を追加
}
}
} else {
if (LogCount == 1) { // コミットが一つだけ
LogItem(0).marker_depth = 0;
}
}
}
}
QList<Git::Tag> MainWindow::findTag(QString const &id)
{
auto it = m->tag_map.find(id);
if (it != m->tag_map.end()) {
return it->second;
}
return QList<Git::Tag>();
}
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
{
int i = ui->tableWidget_log->currentRow();
if (i >= 0 && i < (int)m->logs.size()) {
return i;
}
return -1;
}
Git::CommitItem const *MainWindow::selectedCommitItem() const
{
int i = selectedLogIndex();
return commitItem(i);
}
Git::Object MainWindow::cat_file_(GitPtr g, QString const &id)
{
if (isValidWorkingCopy(g)) {
QString path_prefix = PATH_PREFIX;
if (id.startsWith(path_prefix)) {
QString path = g->workingRepositoryDir();
path = path / id.mid(path_prefix.size());
QFile file(path);
if (file.open(QFile::ReadOnly)) {
Git::Object obj;
obj.content = file.readAll();
return obj;
}
} else if (Git::isValidID(id)) {
return m->objcache.catFile(id);;
}
}
return Git::Object();
}
Git::Object MainWindow::cat_file(QString const &id)
{
return cat_file_(git(), id);
}
void MainWindow::updateDiffView(QListWidgetItem *item)
{
clearDiffView();
m->last_selected_file_item = item;
if (!item) return;
int idiff = indexOfDiff(item);
if (idiff >= 0 && idiff < m->diff.result.size()) {
Git::Diff const &diff = m->diff.result[idiff];
QString key = GitDiff::makeKey(diff);
auto it = m->diff_cache.find(key);
if (it != m->diff_cache.end()) {
int row = ui->tableWidget_log->currentRow();
bool uncommited = (row >= 0 && row < (int)m->logs.size() && Git::isUncommited(m->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::logGitVersion()
{
GitPtr g = git();
QString s = g->version();
if (!s.isEmpty()) {
s += '\n';
writeLog(s);
}
}
bool MainWindow::checkExecutable(QString const &path)
{
if (QFileInfo(path).isExecutable()) {
return true;
}
QString text = "The specified program '%1' is not executable.\n";
text = text.arg(path);
writeLog(text);
return false;
}
void MainWindow::internalSetCommand(QString const &path, bool save, QString const &name, QString *out)
{
if (!path.isEmpty() && checkExecutable(path)) {
if (save) {
MySettings s;
s.beginGroup("Global");
s.setValue(name, path);
s.endGroup();
}
*out = path;
} else {
*out = QString();
}
}
void MainWindow::setGitCommand(QString const &path, bool save)
{
internalSetCommand(path, save, "GitCommand", &m->gcx.git_command);
}
void MainWindow::setFileCommand(QString const &path, bool save)
{
internalSetCommand(path, save, "FileCommand", &global->file_command);
}
void MainWindow::setGpgCommand(QString const &path, bool save)
{
internalSetCommand(path, save, "GpgCommand", &global->gpg_command);
if (!global->gpg_command.isEmpty()) {
GitPtr g = git();
g->configGpgProgram(global->gpg_command, true);
}
}
#ifdef Q_OS_WIN
QString getWin32HttpProxy();
#endif
void MainWindow::initNetworking()
{
std::string http_proxy;
std::string https_proxy;
if (appsettings()->proxy_type == "auto") {
#ifdef Q_OS_WIN
http_proxy = misc::makeProxyServerURL(getWin32HttpProxy().toStdString());
#else
auto getienv = [](std::string const &name)->char const *{
char **p = environ;
while (*p) {
if (strncasecmp(*p, name.c_str(), name.size()) == 0) {
char const *e = *p + name.size();
if (*e == '=') {
return e + 1;
}
}
p++;
}
return nullptr;
};
char const *p;
p = getienv("http_proxy");
if (p) {
http_proxy = misc::makeProxyServerURL(std::string(p));
}
p = getienv("https_proxy");
if (p) {
https_proxy = misc::makeProxyServerURL(std::string(p));
}
#endif
} else if (appsettings()->proxy_type == "manual") {
http_proxy = appsettings()->proxy_server.toStdString();
}
m->webcx.set_http_proxy(http_proxy);
m->webcx.set_https_proxy(https_proxy);
}
QStringList MainWindow::whichCommand_(QString const &cmdfile1, const QString &cmdfile2)
{
QStringList list;
if (!cmdfile1.isEmpty()){
std::vector<std::string> vec;
FileUtil::which(cmdfile1.toStdString(), &vec);
for (std::string const &s : vec) {
list.push_back(QString::fromStdString(s));
}
}
if (!cmdfile2.isEmpty()){
std::vector<std::string> vec;
FileUtil::which(cmdfile2.toStdString(), &vec);
for (std::string const &s : vec) {
list.push_back(QString::fromStdString(s));
}
}
return list;
}
QString MainWindow::selectCommand_(QString const &cmdname, QStringList const &cmdfiles, QStringList const &list, QString path, std::function<void(QString const &)> callback)
{
QString window_title = tr("Select %1 command");
window_title = window_title.arg(cmdfiles.front());
SelectCommandDialog dlg(this, cmdname, cmdfiles, path, list);
dlg.setWindowTitle(window_title);
if (dlg.exec() == QDialog::Accepted) {
path = dlg.selectedFile();
path = misc::normalizePathSeparator(path);
QFileInfo info(path);
if (info.isExecutable()) {
callback(path);
return path;
}
}
return QString();
}
QString MainWindow::selectCommand_(QString const &cmdname, QString const &cmdfile, QStringList const &list, QString path, std::function<void(QString const &)> callback)
{
QStringList cmdfiles;
cmdfiles.push_back(cmdfile);
return selectCommand_(cmdname, cmdfiles, list, path, callback);
}
#ifdef Q_OS_WIN
#define GIT_COMMAND "git.exe"
#define FILE_COMMAND "file.exe"
#define GPG_COMMAND "gpg.exe"
#define GPG2_COMMAND "gpg2.exe"
#else
#define GIT_COMMAND "git"
#define FILE_COMMAND "file"
#define GPG_COMMAND "gpg"
#define GPG2_COMMAND "gpg2"
#endif
QString MainWindow::selectGitCommand(bool save)
{
char const *exe = GIT_COMMAND;
QString path = m->gcx.git_command;
auto fn = [&](QString const &path){
setGitCommand(path, save);
};
QStringList list = whichCommand_(exe);
#ifdef Q_OS_WIN
{
QStringList newlist;
QString suffix1 = "\\bin\\" GIT_COMMAND;
QString suffix2 = "\\cmd\\" GIT_COMMAND;
for (QString const &s : list) {
newlist.push_back(s);
auto DoIt = [&](QString const &suffix){
if (s.endsWith(suffix)) {
QString t = s.mid(0, s.size() - suffix.size());
QString t1 = t + "\\mingw64\\bin\\" GIT_COMMAND;
if (misc::isExecutable(t1)) newlist.push_back(t1);
QString t2 = t + "\\mingw\\bin\\" GIT_COMMAND;
if (misc::isExecutable(t2)) newlist.push_back(t2);
}
};
DoIt(suffix1);
DoIt(suffix2);
}
std::sort(newlist.begin(), newlist.end());
auto end = std::unique(newlist.begin(), newlist.end());
list.clear();
for (auto it = newlist.begin(); it != end; it++) {
list.push_back(*it);
}
}
#endif
return selectCommand_("Git", exe, list, path, fn);
}
QString MainWindow::selectFileCommand(bool save)
{
char const *exe = FILE_COMMAND;
QString path = global->file_command;
auto fn = [&](QString const &path){
setFileCommand(path, save);
};
QStringList list = whichCommand_(exe);
#ifdef Q_OS_WIN
QString dir = misc::getApplicationDir();
QString path1 = dir / FILE_COMMAND;
QString path2;
int i = dir.lastIndexOf('/');
int j = dir.lastIndexOf('\\');
if (i < j) i = j;
if (i > 0) {
path2 = dir.mid(0, i) / FILE_COMMAND;
}
path1 = misc::normalizePathSeparator(path1);
path2 = misc::normalizePathSeparator(path2);
if (misc::isExecutable(path1)) list.push_back(path1);
if (misc::isExecutable(path2)) list.push_back(path2);
#endif
return selectCommand_("File", exe, list, path, fn);
}
QString MainWindow::selectGpgCommand(bool save)
{
QString path = global->gpg_command;
auto fn = [&](QString const &path){
setGpgCommand(path, save);
};
QStringList list = whichCommand_(GPG_COMMAND, GPG2_COMMAND);
QStringList cmdlist;
cmdlist.push_back(GPG_COMMAND);
cmdlist.push_back(GPG2_COMMAND);
return selectCommand_("GPG", cmdlist, list, path, fn);
}
void MainWindow::checkUser()
{
Git g(m->gcx, QString());
while (1) {
Git::User user = g.getUser(Git::Source::Global);
if (!user.name.isEmpty() && !user.email.isEmpty()) {
return; // ok
}
if (!execSetGlobalUserDialog()) {
return;
}
}
}
void MainWindow::dragEnterEvent(QDragEnterEvent *event)
{
if (QApplication::modalWindow()) return;
if (event->mimeData()->hasUrls()) {
event->acceptProposedAction();
event->accept();
}
}
void MainWindow::timerEvent(QTimerEvent *)
{
bool running = m->pty_process.isRunning();
if (ui->toolButton_stop_process->isEnabled() != running) {
ui->toolButton_stop_process->setEnabled(running);
ui->action_stop_process->setEnabled(running);
setNetworkingCommandsEnabled(!running);
}
if (!running) {
m->interaction_mode = InteractionMode::None;
}
while (1) {
char tmp[1024];
int len = m->pty_process.readOutput(tmp, sizeof(tmp));
if (len < 1) break;
writeLog(tmp, len);
}
}
void MainWindow::keyPressEvent(QKeyEvent *event)
{
if (focusWidget() == ui->widget_log) {
int c = event->key();
auto write_char = [&](char c){
if (m->pty_process.isRunning()) {
m->pty_process.writeInput(&c, 1);
}
};
auto write_text = [&](QString const &str){
std::string s = str.toStdString();
for (int i = 0; i < (int)s.size(); i++) {
write_char(s[i]);
}
};
if (c == Qt::Key_Return || c == Qt::Key_Enter) {
write_char('\n');
} else {
QString text = event->text();
write_text(text);
}
}
}
bool MainWindow::saveByteArrayAs(QByteArray const &ba, QString const &dstpath)
{
QFile file(dstpath);
if (file.open(QFile::WriteOnly)) {
file.write(ba);
file.close();
return true;
} else {
QString msg = "Failed to open the file '%1' for write";
msg = msg.arg(dstpath);
qDebug() << msg;
}
return false;
}
bool MainWindow::saveFileAs(QString const &srcpath, QString const &dstpath)
{
QFile f(srcpath);
if (f.open(QFile::ReadOnly)) {
QByteArray ba = f.readAll();
if (saveByteArrayAs(ba, dstpath)) {
return true;
}
} else {
QString msg = "Failed to open the file '%1' for read";
msg = msg.arg(srcpath);
qDebug() << msg;
}
return false;
}
bool MainWindow::saveBlobAs(QString const &id, QString const &dstpath)
{
Git::Object obj = cat_file(id);
if (!obj.content.isEmpty()) {
if (saveByteArrayAs(obj.content, dstpath)) {
return true;
}
} else {
QString msg = "Failed to get the content of the object '%1'";
msg = msg.arg(id);
qDebug() << msg;
}
return false;
}
bool MainWindow::saveAs(QString const &id, QString const &dstpath)
{
if (id.startsWith(PATH_PREFIX)) {
return saveFileAs(id.mid(1), dstpath);
} else {
return saveBlobAs(id, dstpath);
}
}
QString MainWindow::saveAsTemp(QString const &id)
{
QString path = newTempFilePath();
saveAs(id, path);
return path;
}
void MainWindow::on_action_edit_settings_triggered()
{
SettingsDialog dlg(this);
if (dlg.exec() == QDialog::Accepted) {
ApplicationSettings const &newsettings = dlg.settings();
m->appsettings = newsettings;
setGitCommand(appsettings()->git_command, false);
setFileCommand(appsettings()->file_command, false);
setGpgCommand(appsettings()->gpg_command, false);
}
}
bool MainWindow::git_callback(void *cookie, char const *ptr, int len)
{
MainWindow *mw = (MainWindow *)cookie;
mw->writeLog(ptr, len);
return true;
}
void MainWindow::clone()
{
if (!isRemoteOnline()) return;
QString url;
QString dir = defaultWorkingDir();
while (1) {
CloneDialog dlg(this, url, dir);
if (dlg.exec() != QDialog::Accepted) {
return;
}
url = dlg.url();
dir = dlg.dir();
if (dlg.action() == CloneDialog::Action::Clone) {
// 既存チェック
QFileInfo info(dir);
if (info.isFile()) {
QString msg = dir + "\n\n" + tr("A file with same name already exists");
QMessageBox::warning(this, tr("Clone"), msg);
continue;
}
if (info.isDir()) {
QString msg = dir + "\n\n" + tr("A folder with same name already exists");
QMessageBox::warning(this, tr("Clone"), msg);
continue;
}
// クローン先ディレクトリを求める
Git::CloneData clone_data = Git::preclone(url, dir);
// クローン先ディレクトリの存在チェック
QString basedir = misc::normalizePathSeparator(clone_data.basedir);
if (!QFileInfo(basedir).isDir()) {
int i = basedir.indexOf('/');
int j = basedir.indexOf('\\');
if (i < j) i = j;
if (i < 0) {
QString msg = basedir + "\n\n" + tr("Invalid folder");
QMessageBox::warning(this, tr("Clone"), msg);
continue;
}
QString msg = basedir + "\n\n" + tr("No such folder. Create it now ?");
if (QMessageBox::warning(this, tr("Clone"), msg, QMessageBox::Ok, QMessageBox::Cancel) != QMessageBox::Ok) {
continue;
}
// ディレクトリを作成
QString base = basedir.mid(0, i + 1);
QString sub = basedir.mid(i + 1);
QDir(base).mkpath(sub);
}
m->temp_repo.local_dir = dir;
m->temp_repo.local_dir.replace('\\', '/');
m->temp_repo.name = makeRepositoryName(dir);
GitPtr g = git(QString());
m->pty_condition = PtyCondition::Clone;
m->pty_process_ok = true;
g->clone(clone_data, &m->pty_process);
} else if (dlg.action() == CloneDialog::Action::AddExisting) {
addWorkingCopyDir(dir, true);
}
return; // done
}
}
void MainWindow::onCloneCompleted(bool success)
{
if (success) {
saveRepositoryBookmark(m->temp_repo);
setCurrentRepository(m->temp_repo, false);
openRepository(true);
}
}
void MainWindow::onPtyProcessCompleted()
{
switch (m->pty_condition) {
case PtyCondition::Clone:
onCloneCompleted(m->pty_process_ok);
break;
}
m->pty_condition = 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(m->repository_filter_text + c);
}
void MainWindow::backspaceRepoFilter()
{
QString s = m->repository_filter_text;
int n = s.size();
if (n > 0) {
s = s.mid(0, n - 1);
}
ui->lineEdit_filter->setText(s);
}
void MainWindow::on_lineEdit_filter_textChanged(const QString &text)
{
m->repository_filter_text = text;
updateRepositoriesList();
}
void MainWindow::on_toolButton_erase_filter_clicked()
{
clearRepoFilter();
ui->lineEdit_filter->setFocus();
}
void MainWindow::deleteTags(QStringList const &tagnames)
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
int row = ui->tableWidget_log->currentRow();
if (!tagnames.isEmpty()) {
reopenRepository(false, [&](GitPtr g){
for (QString const &name : tagnames) {
g->delete_tag(name, true);
}
});
}
ui->tableWidget_log->selectRow(row);
}
void MainWindow::deleteTags(const Git::CommitItem &commit)
{
auto it = m->tag_map.find(commit.commit_id);
if (it != m->tag_map.end()) {
QStringList names;
QList<Git::Tag> const &tags = it->second;
for (Git::Tag const &tag : tags) {
names.push_back(tag.name);
}
deleteTags(names);
}
}
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)
{
if (name.isEmpty()) return false;
GitPtr g = git();
if (!isValidWorkingCopy(g)) return false;
QString commit_id;
Git::CommitItem const *commit = selectedCommitItem();
if (commit && !commit->commit_id.isEmpty()) {
commit_id = commit->commit_id;
}
if (!Git::isValidID(commit_id)) return false;
int row = ui->tableWidget_log->currentRow();
bool ok = false;
reopenRepository(false, [&](GitPtr g){
ok = g->tag(name, commit_id);
});
ui->tableWidget_log->selectRow(row);
return ok;
}
void MainWindow::on_action_tag_push_all_triggered()
{
reopenRepository(false, [&](GitPtr g){
g->push(true);
});
}
QString MainWindow::tempfileHeader() const
{
QString name = "jp_soramimi_Guitar_%1_";
return name.arg(QApplication::applicationPid());
}
void MainWindow::deleteTempFiles()
{
QString dir = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
QString name = tempfileHeader();
QDirIterator it(dir, { name + "*" });
while (it.hasNext()) {
QString path = it.next();
QFile::remove(path);
}
}
QString MainWindow::newTempFilePath()
{
QString tmpdir = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
QString path = tmpdir / tempfileHeader() + QString::number(m->temp_file_counter);
m->temp_file_counter++;
return path;
}
QString MainWindow::determinFileType_(QString const &path, bool mime, std::function<void(QString const &cmd, QByteArray *ba)> callback) const
{
const_cast<MainWindow *>(this)->checkFileCommand();
return misc::determinFileType(global->file_command, path, mime, callback);
}
QString MainWindow::determinFileType(QString const &path, bool mime)
{
return determinFileType_(path, mime, [](QString const &cmd, QByteArray *ba){
misc2::runCommand(cmd, ba);
});
}
QString MainWindow::determinFileType(QByteArray in, bool mime)
{
if (in.isEmpty()) return QString();
if (in.size() > 10) {
if (memcmp(in.data(), "\x1f\x8b\x08", 3) == 0) { // gzip
QBuffer buf;
MemoryReader reader(in.data(), in.size());
reader.open(MemoryReader::ReadOnly);
buf.open(QBuffer::WriteOnly);
gunzip z;
z.set_maximul_size(100000);
z.decode(&reader, &buf);
in = buf.buffer();
}
}
// ファイル名を "-" にすると、リダイレクトで標準入力へ流し込める。
return determinFileType_("-", mime, [&](QString const &cmd, QByteArray *ba){
int r = misc2::runCommand(cmd, &in, ba);
if (r != 0) {
ba->clear();
}
});
}
void MainWindow::on_tableWidget_log_itemDoubleClicked(QTableWidgetItem *)
{
Git::CommitItem const *commit = selectedCommitItem();
if (commit) {
execCommitPropertyDialog(this, commit);
}
}
void MainWindow::execFilePropertyDialog(QListWidgetItem *item)
{
if (item) {
QString path = getFilePath(item);
QString id = getObjectID(item);
FilePropertyDialog dlg(this);
dlg.exec(this, path, id);
}
}
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->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;
}
QPixmap MainWindow::getTransparentPixmap()
{
if (m->transparent_pixmap.isNull()) {
m->transparent_pixmap = QPixmap(":/image/transparent.png");
}
return m->transparent_pixmap;
}
QString MainWindow::getCommitIdFromTag(QString const &tag)
{
return m->objcache.getCommitIdFromTag(tag);
}
bool MainWindow::isValidRemoteURL(const QString &url)
{
if (url.indexOf('\"') >= 0) {
return false;
}
stopPtyProcess();
GitPtr g = git(QString());
QString cmd = "ls-remote \"%1\" HEAD";
bool f = g->git(cmd.arg(url), false, false, &m->pty_process);
{
QTime time;
time.start();
while (!m->pty_process.wait(10)) {
if (time.elapsed() > 10000) {
f = false;
break;
}
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
stopPtyProcess();
}
QString line = g->resultText().trimmed();
QString head;
int i = line.indexOf('\t');
if (i == GIT_ID_LENGTH) {
QString id = line.mid(0, i);
QString name = line.mid(i + 1);
qDebug() << id << name;
if (name == "HEAD" && Git::isValidID(id)) {
head = id;
}
}
if (f) {
qDebug() << "This is a valid repository.";
if (head.isEmpty()) {
qDebug() << "But HEAD not found";
}
return true;
} else {
qDebug() << "It is not a repository.";
}
return false;
}
bool MainWindow::testRemoteRepositoryValidity(QString const &url)
{
bool ok;
{
OverrideWaitCursor;
ok = isValidRemoteURL(url);
}
QString pass = tr("The URL is a valid repository");
QString fail = tr("Failed to access the URL");
QString text = "%1\n\n%2";
text = text.arg(url).arg(ok ? pass : fail);
QString title = tr("Remote Repository");
if (ok) {
QMessageBox::information(this, title, text);
} else {
QMessageBox::critical(this, title, text);
}
return ok;
}
bool MainWindow::execSetGlobalUserDialog()
{
SetGlobalUserDialog dlg(this);
if (dlg.exec() == QDialog::Accepted) {
GitPtr g = git();
Git::User user = dlg.user();
g->setUser(user, true);
updateWindowTitle(g);
return true;
}
return false;
}
void MainWindow::execSetUserDialog(Git::User const &global_user, Git::User const &repo_user, QString const &reponame)
{
SetUserDialog dlg(this, global_user, repo_user, reponame);
if (dlg.exec() == QDialog::Accepted) {
GitPtr g = git();
Git::User user = dlg.user();
if (dlg.isGlobalChecked()) {
g->setUser(user, true);
}
if (dlg.isRepositoryChecked()) {
g->setUser(user, false);
}
updateWindowTitle(g);
}
}
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);
}
NamedCommitList MainWindow::namedCommitItems(int flags)
{
NamedCommitList items;
if (flags & Branches) {
for (auto pair: m->branch_map) {
QList<Git::Branch> const &list = pair.second;
for (Git::Branch const &b : list) {
if (b.isHeadDetached()) continue;
QString key = b.name;
if (flags & NamedCommitFlag::Remotes) {
if (!b.remote.isEmpty()) {
key = "remotes" / b.remote / key;
}
} else {
if (!b.remote.isEmpty()) continue;
}
NamedCommitItem item;
item.type = NamedCommitItem::Type::Branch;
item.remote = b.remote;
item.name = b.name;
item.id = b.id;
items.push_back(item);
}
}
}
if (flags & Tags) {
for (auto pair: m->tag_map) {
QList<Git::Tag> const &list = pair.second;
for (Git::Tag const &t : list) {
NamedCommitItem item;
item.type = NamedCommitItem::Type::Tag;
item.name = t.name;
item.id = t.id;
items.push_back(item);
}
}
}
return items;
}
int MainWindow::rowFromCommitId(QString const &id)
{
for (size_t i = 0; i < m->logs.size(); i++) {
Git::CommitItem const &item = m->logs[i];
if (item.commit_id == id) {
return (int)i;
}
}
return -1;
}
void MainWindow::on_action_repo_jump_triggered()
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
NamedCommitList items = namedCommitItems(Branches | Tags | Remotes);
JumpDialog::sort(&items);
{
NamedCommitItem head;
head.name = "HEAD";
head.id = m->head_id;
items.push_front(head);
}
JumpDialog dlg(this, items);
if (dlg.exec() == QDialog::Accepted) {
JumpDialog::Action action = dlg.action();
if (action == JumpDialog::Action::BranchsAndTags) {
QString name = dlg.text();
QString id = g->rev_parse(name);
if (g->objectType(id) == "tag") {
id = m->objcache.getCommitIdFromTag(id);
}
int row = rowFromCommitId(id);
if (row < 0) {
QMessageBox::warning(this, tr("Jump"), QString("%1\n(%2)\n\n").arg(name).arg(id) + tr("That commmit has not foud or not read yet"));
} else {
setCurrentLogRow(row);
if (dlg.isCheckoutChecked()) {
NamedCommitItem item;
for (NamedCommitItem const &t : items) {
if (t.name == name) {
item = t;
break;
}
}
bool ok = false;
if (item.type == NamedCommitItem::Type::Branch) {
ok = g->git(QString("checkout \"%1\"").arg(name), true);
} else if (item.type == NamedCommitItem::Type::Tag) {
ok = g->git(QString("checkout -b \"%1\" \"%2\"").arg(name).arg(id), true);
}
if (ok) {
openRepository(true);
}
}
}
} else if (action == JumpDialog::Action::CommitId) {
QString id = dlg.text();
jumpToCommit(id);
}
}
}
void MainWindow::jumpToCommit(QString id)
{
GitPtr g = git();
id = g->rev_parse(id);
if (!id.isEmpty()) {
int row = rowFromCommitId(id);
setCurrentLogRow(row);
}
}
void MainWindow::checkout(QWidget *parent, Git::CommitItem const *commit)
{
if (!commit) return;
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
QStringList tags;
QStringList local_branches;
QStringList remote_branches;
{
NamedCommitList named_commits = namedCommitItems(Branches | Tags | Remotes);
JumpDialog::sort(&named_commits);
for (NamedCommitItem const &item : named_commits) {
if (item.id == commit->commit_id) {
QString name = item.name;
if (item.type == NamedCommitItem::Type::Tag) {
tags.push_back(name);
} else if (item.type == NamedCommitItem::Type::Branch) {
int i = name.lastIndexOf('/');
if (i < 0 && name == "HEAD") continue;
if (i > 0 && name.mid(i + 1) == "HEAD") continue;
if (item.remote.isNull()) {
local_branches.push_back(name);
} else {
remote_branches.push_back(name);
}
}
}
}
}
CheckoutDialog dlg(parent, tags, local_branches, remote_branches);
if (dlg.exec() == QDialog::Accepted) {
CheckoutDialog::Operation op = dlg.operation();
QString name = dlg.branchName();
QString id = commit->commit_id;
bool ok = false;
setLogEnabled(g, true);
if (op == CheckoutDialog::Operation::HeadDetached) {
ok = g->git(QString("checkout \"%1\"").arg(id), true);
} else if (op == CheckoutDialog::Operation::CreateLocalBranch) {
ok = g->git(QString("checkout -b \"%1\" \"%2\"").arg(name).arg(id), true);
} else if (op == CheckoutDialog::Operation::ExistingLocalBranch) {
ok = g->git(QString("checkout \"%1\"").arg(name), true);
}
if (ok) {
openRepository(true);
}
}
}
void MainWindow::deleteBranch(Git::CommitItem const *commit)
{
if (!commit) return;
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
QStringList all_local_branch_names;
QStringList current_local_branch_names;
{
NamedCommitList named_commits = namedCommitItems(Branches);
JumpDialog::sort(&named_commits);
for (NamedCommitItem const &item : named_commits) {
if (item.name == "HEAD") continue;
if (item.id == commit->commit_id) {
current_local_branch_names.push_back(item.name);
}
all_local_branch_names.push_back(item.name);
}
}
DeleteBranchDialog dlg(this, false, all_local_branch_names, current_local_branch_names);
if (dlg.exec() == QDialog::Accepted) {
setLogEnabled(g, true);
QStringList names = dlg.selectedBranchNames();
int count = 0;
for (QString const &name : names) {
if (g->git(QString("branch -D \"%1\"").arg(name))) {
count++;
} else {
writeLog(tr("Failed to delete the branch '%1'\n").arg(name));
}
}
if (count > 0) {
openRepository(true);
}
}
}
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::checkout()
{
checkout(this, selectedCommitItem());
}
void MainWindow::deleteBranch()
{
deleteBranch(selectedCommitItem());
}
void MainWindow::on_action_repo_checkout_triggered()
{
checkout();
}
void MainWindow::on_action_delete_branch_triggered()
{
deleteBranch();
}
bool MainWindow::runOnRepositoryDir(std::function<void(QString)> callback, RepositoryItem const *repo)
{
if (!repo) {
repo = &m->current_repo;
}
QString dir = repo->local_dir;
dir.replace('\\', '/');
if (QFileInfo(dir).isDir()) {
callback(dir);
return true;
}
QMessageBox::warning(this, qApp->applicationName(), tr("No repository selected"));
return false;
}
#ifdef Q_OS_MAC
namespace {
bool isValidDir(QString const &dir)
{
if (dir.indexOf('\"') >= 0 || dir.indexOf('\\') >= 0) return false;
return QFileInfo(dir).isDir();
}
}
#endif
void MainWindow::openTerminal(RepositoryItem const *repo)
{
runOnRepositoryDir([](QString dir){
#ifdef Q_OS_MAC
if (!isValidDir(dir)) return;
QString cmd = "open -n -a /Applications/Utilities/Terminal.app --args \"%1\"";
cmd = cmd.arg(dir);
QProcess::execute(cmd);
#else
Terminal::open(dir);
#endif
}, repo);
}
void MainWindow::openExplorer(RepositoryItem const *repo)
{
runOnRepositoryDir([](QString dir){
#ifdef Q_OS_MAC
if (!isValidDir(dir)) return;
QString cmd = "open \"%1\"";
cmd = cmd.arg(dir);
QProcess::execute(cmd);
#else
QDesktopServices::openUrl(dir);
#endif
}, repo);
}
void MainWindow::on_toolButton_terminal_clicked()
{
openTerminal(nullptr);
}
void MainWindow::on_toolButton_explorer_clicked()
{
openExplorer(nullptr);
}
void MainWindow::pushSetUpstream(QString const &remote, QString const &branch)
{
if (remote.isEmpty()) return;
if (branch.isEmpty()) return;
int exitcode = 0;
QString errormsg;
reopenRepository(true, [&](GitPtr g){
g->push_u(remote, branch, &m->pty_process);
while (1) {
if (m->pty_process.wait(1)) break;
QApplication::processEvents();
}
exitcode = m->pty_process.getExitCode();
errormsg = m->pty_process.getMessage();
});
if (exitcode == 128) {
if (errormsg.indexOf("Connection refused") >= 0) {
QMessageBox::critical(this, qApp->applicationName(), tr("Connection refused."));
return;
}
}
updateRemoteInfo();
}
bool MainWindow::pushSetUpstream(bool testonly)
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return false;
QStringList remotes = g->getRemotes();
QString current_branch = currentBranch().name;
QStringList branches;
for (Git::Branch const &b : g->branches()) {
branches.push_back(b.name);
}
if (remotes.isEmpty() || branches.isEmpty()) {
return false;
}
if (testonly) {
return true;
}
PushDialog dlg(this, remotes, branches, PushDialog::RemoteBranch(QString(), current_branch));
if (dlg.exec() == QDialog::Accepted) {
PushDialog::Action a = dlg.action();
if (a == PushDialog::PushSimple) {
ui->action_push->trigger();
} else if (a == PushDialog::PushSetUpstream) {
QString remote = dlg.remote();
QString branch = dlg.branch();
pushSetUpstream(remote, branch);
}
return true;
}
return false;
}
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()
+void MainWindow::createRepository(QString const &dir)
{
- CreateRepositoryDialog dlg(this);
+ CreateRepositoryDialog dlg(this, dir);
if (dlg.exec() == QDialog::Accepted) {
QString path = dlg.path();
if (QFileInfo(path).isDir()) {
if (Git::isValidWorkingCopy(path)) {
// A valid git repository already exists there.
} else {
GitPtr g = git(path);
if (g->init()) {
QString name = dlg.name();
if (!name.isEmpty()) {
addWorkingCopyDir(path, name, true);
}
QString remote_name = dlg.remoteName();
QString remote_url = dlg.remoteURL();
if (!remote_name.isEmpty() && !remote_url.isEmpty()) {
g->addRemoteURL(remote_name, remote_url);
}
}
}
} else {
// not dir
}
}
}
+void MainWindow::on_action_create_a_repository_triggered()
+{
+ createRepository(QString());
+}
+
bool MainWindow::isRemoteOnline() const
{
return ui->radioButton_remote_online->isChecked();
}
void MainWindow::setNetworkingCommandsEnabled(bool f)
{
ui->action_clone->setEnabled(f);
ui->toolButton_clone->setEnabled(f);
if (!Git::isValidWorkingCopy(currentWorkingCopyDir())) {
f = false;
}
ui->action_fetch->setEnabled(f);
ui->action_pull->setEnabled(f);
ui->action_push->setEnabled(f);
ui->toolButton_fetch->setEnabled(f);
ui->toolButton_pull->setEnabled(f);
ui->toolButton_push->setEnabled(f);
}
void MainWindow::setRemoteOnline(bool f)
{
QRadioButton *rb = nullptr;
rb = f ? ui->radioButton_remote_online : ui->radioButton_remote_offline;
rb->blockSignals(true);
rb->click();
rb->blockSignals(false);
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::stopPtyProcess()
{
m->pty_process.stop();
QDir::setCurrent(m->starting_dir);
}
void MainWindow::abortPtyProcess()
{
stopPtyProcess();
m->pty_process_ok = false;
m->interaction_canceled = true;
}
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::execCommitViewWindow(Git::CommitItem const *commit)
{
CommitViewWindow win(this, commit);
win.exec();
}
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;
m->interaction_mode = InteractionMode::Busy;
QApplication::restoreOverrideCursor();
TheDlg dlg(this);
if (dlg.exec() == QDialog::Accepted) {
TheDlg::Result r = dlg.result();
if (r == TheDlg::Result::Yes) {
m->pty_process.writeInput("yes\n", 4);
} else {
m->pty_process_ok = false; // abort
m->pty_process.writeInput("no\n", 3);
QThread::msleep(300);
stopPtyProcess();
}
} else {
ui->widget_log->setFocus();
m->interaction_canceled = true;
}
m->interaction_mode = InteractionMode::Busy;
}
void MainWindow::onLogIdle()
{
if (m->interaction_canceled) return;
if (m->interaction_mode != 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.size() > 0) {
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';
m->pty_process.writeInput(str.c_str(), str.size());
return ret;
} else {
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 == m->ssh_passphrase_for && !m->ssh_passphrase_secret.empty()) {
std::string text = m->ssh_passphrase_secret + '\n';
m->pty_process.writeInput(text.c_str(), text.size());
} else {
m->ssh_passphrase_for = keyfile;
std::string s = ExecLineEditDialog(this, "Passphrase for key", QString::fromStdString(line), QString(), true);
m->ssh_passphrase_secret = s;
}
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){
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);
if (value) *value = s;
return true;
}
}
return false;
};
std::string uid = m->http_uid;
std::string pwd = m->http_pwd;
if (Input("Username", false, &uid)) {
m->http_uid = uid;
return;
}
if (Input("Password", true, &pwd)) {
m->http_pwd = pwd;
return;
}
if (StartsWith(fatal_authentication_failed_for)) {
QMessageBox::critical(this, tr("Authentication Failed"), QString::fromStdString(line));
abortPtyProcess();
return;
}
}
}
}
QList<Git::Tag> MainWindow::queryTagList()
{
QList<Git::Tag> list;
Git::CommitItem const *commit = selectedCommitItem();
if (commit && Git::isValidID(commit->commit_id)) {
list = findTag(commit->commit_id);
}
return list;
}
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);
}
QStringList MainWindow::remoteBranches(QString const &id)
{
QStringList list;
GitPtr g = git();
if (isValidWorkingCopy(g)) {
NamedCommitList named_commits = namedCommitItems(Branches | Remotes);
JumpDialog::sort(&named_commits);
for (NamedCommitItem const &item : named_commits) {
if (item.id == id && !item.remote.isEmpty()) {
list.push_back(item.remote / item.name);
}
}
}
return list;
}
void MainWindow::deleteRemoteBranch(Git::CommitItem const *commit)
{
if (!commit) return;
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
QStringList remote_branches = remoteBranches(commit->commit_id);
if (remote_branches.isEmpty()) return;
DeleteBranchDialog dlg(this, true, QStringList(), remote_branches);
if (dlg.exec() == QDialog::Accepted) {
setLogEnabled(g, true);
QStringList names = dlg.selectedBranchNames();
for (QString const &name : names) {
int i = name.indexOf('/');
if (i > 0) {
QString remote = name.mid(0, i);
QString branch = ':' + name.mid(i + 1);
pushSetUpstream(remote, branch);
}
}
}
}
void MainWindow::on_action_delete_remote_branch_triggered()
{
deleteRemoteBranch(selectedCommitItem());
}
void MainWindow::rebaseOnto()
{
struct Commit {
QStringList parents;
QStringList children;
};
std::map<QString, Commit> commit_map;
Git::CommitItem const *commit = selectedCommitItem();
if (commit) {
for (Git::CommitItem const &item : m->logs) {
Commit c;
c.parents = item.parent_ids;
commit_map[item.commit_id] = c;
}
for (Git::CommitItem const &item : m->logs) {
for (QString const &parent : item.parent_ids) {
auto it = commit_map.find(parent);
if (it != commit_map.end()) {
it->second.children.push_back(item.commit_id);
}
}
}
QString upstream;
QString id = m->head_id;
while (1) {
auto it = commit_map.find(id);
if (it == commit_map.end()) break;
Commit const &c = it->second;
if (c.parents.size() > 1) goto done;
if (c.parents.size() != 1) break;
if (id != m->head_id) {
if (c.children.size() > 1) goto done;
if (!findTag(id).isEmpty()) goto done;
auto it2 = m->branch_map.find(id);
if (it2 != m->branch_map.end()) {
QList<Git::Branch> b = it2->second;
if (!it2->second.empty()) goto done;
}
}
id = c.parents.first();
continue;
done:;
upstream = id;
break;
}
QString newbase = commit->commit_id;
QString branch = m->head_id;
RebaseOntoDialog dlg(this);
if (dlg.exec(newbase, upstream, branch) == QDialog::Accepted) {
reopenRepository(true, [&](GitPtr g){
m->pty_condition = PtyCondition::Pull;
m->pty_process_ok = true;
g->rebaseOnto(newbase, upstream, branch, &m->pty_process);
while (1) {
if (m->pty_process.wait(1)) break;
QApplication::processEvents();
}
});
}
}
}
void MainWindow::on_action_rebase_onto_triggered()
{
rebaseOnto();
}
void MainWindow::on_action_test_triggered()
{
rebaseOnto();
}
diff --git a/src/MainWindow.h b/src/MainWindow.h
index eb9269c..99824c4 100644
--- a/src/MainWindow.h
+++ b/src/MainWindow.h
@@ -1,404 +1,405 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "Git.h"
#include "RepositoryData.h"
#include <memory>
#include <functional>
#include "GitHubAPI.h"
#include "main.h"
#include "TextEditorTheme.h"
namespace Ui {
class MainWindow;
}
class QScrollBar;
class QListWidget;
class QListWidgetItem;
class QTreeWidgetItem;
class QTableWidgetItem;
class AboutDialog;
class MySettings;
class LocalSocketReader;
class CommitList;
class WebContext;
#define PATH_PREFIX "*"
class HunkItem {
public:
int hunk_number = -1;
size_t pos, len;
std::vector<std::string> lines;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
friend class ImageViewWidget;
friend class FileDiffSliderWidget;
friend class FileHistoryWindow;
friend class FileDiffWidget;
friend class AboutDialog;
public:
struct Label {
enum {
Head,
LocalBranch,
RemoteBranch,
Tag,
};
int kind;
QString text;
Label(int kind = LocalBranch)
: kind(kind)
{
}
};
private:
struct Private;
Private *m;
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
QPixmap const &digitsPixmap() const;
QString currentWorkingCopyDir() const;
static QString makeRepositoryName(QString const &loc);
const Git::CommitItemList *logs() const;
QColor color(unsigned int i);
private:
Ui::MainWindow *ui;
enum class FilesListType {
SingleList,
SideBySide,
};
void addDiffItems(const QList<Git::Diff> *diff_list, std::function<void (const QString &, QString, int)> AddItem_);
void updateFilesList(QString id, bool wait);
void updateFilesList(const Git::CommitItem &commit, bool wait);
void updateRepositoriesList();
QString getBookmarksFilePath() const;
bool saveRepositoryBookmarks() const;
void openRepository_(GitPtr g);
void openRepository(bool validate, bool waitcursor = true);
void reopenRepository(bool log, std::function<void(GitPtr)> callback);
void openSelectedRepository();
bool askAreYouSureYouWantToRun(const QString &title, const QString &command);
void resetFile(const QStringList &path);
void revertAllFiles();
void prepareLogTableWidget();
QStringList selectedFiles_(QListWidget *listwidget) const;
QStringList selectedFiles() const;
void for_each_selected_files(std::function<void (const QString &)> fn);
bool editFile(const QString &path, const QString &title);
void updateCommitGraph();
void showFileList(FilesListType files_list_type);
bool checkGitCommand();
bool checkFileCommand();
void checkUser();
void clearLog();
void clearFileList();
void clearDiffView();
void clearRepositoryInfo();
int repositoryIndex_(const QTreeWidgetItem *item) const;
RepositoryItem const *repositoryItem(const QTreeWidgetItem *item) const;
bool makeDiff(QString id, QList<Git::Diff> *out);
void udpateButton();
void commit(bool amend = false);
void commit_amend();
void queryBranches(GitPtr g);
void updateRemoteInfo();
QList<Git::Branch> findBranch(const QString &id);
QList<Git::Tag> findTag(const QString &id);
int selectedLogIndex() const;
const Git::CommitItem *selectedCommitItem() const;
void deleteTags(const Git::CommitItem &commit);
QTreeWidgetItem *newQTreeWidgetFolderItem(const QString &name);
void buildRepoTree(const QString &group, QTreeWidgetItem *item, QList<RepositoryItem> *repos);
void refrectRepositories();
bool saveByteArrayAs(const QByteArray &ba, const QString &dstpath);
bool saveFileAs(const QString &srcpath, const QString &dstpath);
bool saveBlobAs(const QString &id, const QString &dstpath);
QString selectCommand_(const QString &cmdname, const QStringList &cmdfile, const QStringList &list, QString path, std::function<void (const QString &)> callback);
QString selectCommand_(const QString &cmdname, const QString &cmdfile, const QStringList &list, QString path, std::function<void(const QString &)> callback);
void updateDiffView(QListWidgetItem *item);
void updateDiffView();
void updateUnstagedFileCurrentItem();
void updateStagedFileCurrentItem();
// void addTag();
QStringList whichCommand_(const QString &cmdfile1, const QString &cmdfile2 = QString());
QString getObjectID(QListWidgetItem *item);
QString determinFileType_(const QString &path, bool mime, std::function<void(QString const &cmd, QByteArray *ba)> callback) const;
Git::Object cat_file_(GitPtr g, const QString &id);
Git::Object cat_file(const QString &id);
void updateStatusBarText();
void setUnknownRepositoryInfo();
void setWindowTitle_(const Git::User &user);
void setRepositoryInfo(const QString &reponame, const QString &brname);
void updateWindowTitle(GitPtr g);
void logGitVersion();
static bool git_callback(void *cookie, const char *ptr, int len);
int indexOfRepository(const QTreeWidgetItem *treeitem) const;
void removeRepositoryFromBookmark(int index, bool ask);
enum NamedCommitFlag {
Branches = 0x0001,
Tags = 0x0002,
Remotes = 0x0100,
};
NamedCommitList namedCommitItems(int flags);
void deleteBranch(const Git::CommitItem *commit);
void checkout();
void clone();
void deleteBranch();
Git::CommitItemList retrieveCommitLog(GitPtr g);
bool runOnRepositoryDir(std::function<void(QString)> callback, const RepositoryItem *repo);
void openTerminal(const RepositoryItem *repo);
void openExplorer(const RepositoryItem *repo);
void pushSetUpstream(const QString &remote, const QString &branch);
bool pushSetUpstream(bool testonly);
void clearRepoFilter();
void appendCharToRepoFilter(ushort c);
void backspaceRepoFilter();
void revertCommit();
int rowFromCommitId(const QString &id);
void cherrypick(const Git::CommitItem *commit);
void mergeBranch(const Git::CommitItem *commit);
void rebaseBranch(const Git::CommitItem *commit);
void detectGitServerType(GitPtr g);
void initNetworking();
void setRemoteOnline(bool f);
bool isRemoteOnline() const;
void startTimers();
void onCloneCompleted(bool success);
bool fetch(GitPtr g);
void stopPtyProcess();
void abortPtyProcess();
void setNetworkingCommandsEnabled(bool f);
void execSetUserDialog(const Git::User &global_user, const Git::User &repo_user, const QString &reponame);
bool execSetGlobalUserDialog();
void blame(QListWidgetItem *item);
void blame();
QListWidgetItem *currentFileItem() const;
const RepositoryItem *findRegisteredRepository(QString *workdir) const;
void execAreYouSureYouWantToContinueConnectingDialog();
void queryRemotes(GitPtr g);
void internalSetCommand(const QString &path, bool save, const QString &name, QString *out);
void deleteRemoteBranch(const Git::CommitItem *commit);
QStringList remoteBranches(const QString &id);
void rebaseOnto();
+ void createRepository(const QString &dir);
protected:
void dragEnterEvent(QDragEnterEvent *event);
void timerEvent(QTimerEvent *);
void keyPressEvent(QKeyEvent *event);
bool event(QEvent *event);
bool eventFilter(QObject *watched, QEvent *event);
public:
QString selectGitCommand(bool save);
QString selectFileCommand(bool save);
QString selectGpgCommand(bool save);
int limitLogCount() const;
void setGitCommand(const QString &path, bool save);
void setFileCommand(const QString &path, bool save);
void setGpgCommand(const QString &path, bool save);
void clearAuthentication();
void setCurrentRepository(const RepositoryItem &repo, bool clear_authentication);
QString currentRepositoryName() const;
const Git::Branch &currentBranch() const;
bool isThereUncommitedChanges() const;
QString defaultWorkingDir() const;
void autoOpenRepository(QString dir);
void saveRepositoryBookmark(RepositoryItem item);
void drawDigit(QPainter *pr, int x, int y, int n) const;
int digitWidth() const;
int digitHeight() const;
bool isValidWorkingCopy(const GitPtr &g) const;
QString tempfileHeader() const;
void deleteTempFiles();
QString newTempFilePath();
bool saveAs(const QString &id, const QString &dstpath);
QString saveAsTemp(const QString &id);
QString abbrevCommitID(const Git::CommitItem &commit);
QString findFileID(GitPtr g, const QString &commit_id, const QString &file);
QString determinFileType(const QString &path, bool mime);
QString determinFileType(QByteArray in, bool mime);
QPixmap getTransparentPixmap();
QString getCommitIdFromTag(const QString &tag);
void setStatusBarText(const QString &text);
void clearStatusBarText();
QString makeCommitInfoText(int row, QList<Label> *label_list);
void setLogEnabled(GitPtr g, bool f);
void addWorkingCopyDir(QString dir, QString name, bool open);
void addWorkingCopyDir(QString dir, bool open)
{
addWorkingCopyDir(dir, QString(), open);
}
bool isValidRemoteURL(QString const &url);
bool testRemoteRepositoryValidity(const QString &url);
void removeSelectedRepositoryFromBookmark(bool ask);
void setCurrentLogRow(int row);
GitPtr git(const QString &dir) const;
GitPtr git();
const QList<Label> *label(int row);
bool isGitHub() const;
QIcon committerIcon(int row) const;
void updateCommitTableLater();
bool isAvatarEnabled() const;
WebContext *getWebContextPtr();
bool shown();
void execFileHistory(QListWidgetItem *item);
void execFileHistory(const QString &path);
void execCommitExploreWindow(QWidget *parent, const Git::CommitItem *commit);
void execCommitPropertyDialog(QWidget *parent, const Git::CommitItem *commit);
void checkout(QWidget *parent, const Git::CommitItem *commit);
TextEditorThemePtr themeForTextEditor();
bool queryCommit(const QString &id, Git::CommitItem *out);
void jumpToCommit(QString id);
WebContext *webContext();
bool execWelcomeWizardDialog();
void updateFilesList(QString id, QList<Git::Diff> *diff_list, QListWidget *listwidget);
void execCommitViewWindow(const Git::CommitItem *commit);
QAction *addMenuActionProperty(QMenu *menu);
void execFilePropertyDialog(QListWidgetItem *item);
void execRepositoryPropertyDialog(QString workdir, bool open_repository_menu = false);
QStringList remotes() const;
QIcon verifiedIcon(char s) const;
ApplicationSettings *appsettings();
const ApplicationSettings *appsettings() const;
const Git::CommitItem *commitItem(int row) const;
bool checkExecutable(const QString &path);
void deleteTags(const QStringList &tagnames);
QList<Git::Tag> queryTagList();
bool addTag(const QString &name);
void updateCurrentFilesList();
public slots:
void writeLog(const char *ptr, int len);
void writeLog(const QString &str);
void writeLog(QByteArray ba);
private slots:
void on_action_clone_triggered();
void on_action_commit_triggered();
void on_action_edit_git_config_triggered();
void on_action_edit_gitignore_triggered();
void on_action_edit_global_gitconfig_triggered();
void on_action_edit_settings_triggered();
void on_action_fetch_triggered();
void on_action_open_existing_working_copy_triggered();
void on_action_pull_triggered();
void on_action_push_triggered();
void on_action_test_triggered();
void on_action_view_refresh_triggered();
void on_action_tag_push_all_triggered();
void on_treeWidget_repos_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
void on_treeWidget_repos_itemDoubleClicked(QTreeWidgetItem *item, int column);
void on_tableWidget_log_currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous);
void on_treeWidget_repos_customContextMenuRequested(const QPoint &pos);
void on_tableWidget_log_customContextMenuRequested(const QPoint &pos);
void on_listWidget_files_customContextMenuRequested(const QPoint &pos);
void on_listWidget_unstaged_customContextMenuRequested(const QPoint &pos);
void on_listWidget_staged_customContextMenuRequested(const QPoint &pos);
void on_listWidget_unstaged_currentRowChanged(int currentRow);
void on_listWidget_staged_currentRowChanged(int currentRow);
void on_listWidget_files_currentRowChanged(int currentRow);
void on_toolButton_commit_clicked();
void on_toolButton_pull_clicked();
void on_toolButton_push_clicked();
void on_toolButton_select_all_clicked();
void on_toolButton_stage_clicked();
void on_toolButton_unstage_clicked();
void on_action_about_triggered();
void on_toolButton_clone_clicked();
void on_toolButton_fetch_clicked();
void on_lineEdit_filter_textChanged(const QString &text);
void on_toolButton_erase_filter_clicked();
void on_tableWidget_log_itemDoubleClicked(QTableWidgetItem *);
void on_listWidget_unstaged_itemDoubleClicked(QListWidgetItem *item);
void on_listWidget_staged_itemDoubleClicked(QListWidgetItem *item);
void on_listWidget_files_itemDoubleClicked(QListWidgetItem *item);
void onRepositoriesTreeDropped();
void on_action_set_config_user_triggered();
void on_action_window_log_triggered(bool checked);
void onLogVisibilityChanged();
void on_action_repo_jump_triggered();
void on_action_repo_checkout_triggered();
void on_action_delete_branch_triggered();
void on_toolButton_terminal_clicked();
void on_toolButton_explorer_clicked();
void on_action_reset_HEAD_1_triggered();
void on_action_create_a_repository_triggered();
void onAvatarUpdated();
void on_radioButton_remote_online_clicked();
void on_radioButton_remote_offline_clicked();
void on_verticalScrollBar_log_valueChanged(int);
void on_horizontalScrollBar_log_valueChanged(int);
void onPtyProcessCompleted();
void on_toolButton_stop_process_clicked();
void on_action_stop_process_triggered();
void on_action_exit_triggered();
void on_action_reflog_triggered();
void on_action_repository_property_triggered();
void on_action_set_gpg_signing_triggered();
void on_action_edit_tags_triggered();
void on_action_push_u_triggered();
void on_action_delete_remote_branch_triggered();
void on_action_rebase_onto_triggered();
signals:
void onEscapeKeyPressed();
protected:
void closeEvent(QCloseEvent *event);
protected slots:
void onLogIdle();
};
#endif // MAINWINDOW_H

File Metadata

Mime Type
text/x-diff
Expires
Wed, Jun 17, 9:23 PM (1 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
71434
Default Alt Text
(259 KB)

Event Timeline