Merge branch 'master'

This commit is contained in:
0verEngineer 2023-12-12 12:47:25 +01:00
commit 231c3d3da4
179 changed files with 19962 additions and 10940 deletions

View File

@ -38,9 +38,18 @@ jobs:
runs-on: ubuntu-latest
container:
# image: bilelmoussaoui/flatpak-github-actions:kde-5.15-21.08
image: exactlyonekas/gittyup-flatpak-builder:latest
image: archlinux:latest
options: --privileged
steps:
- name: Update
run: |
pacman --noconfirm -Suy
pacman --noconfirm -S flatpak flatpak-builder xorg-server-xvfb
flatpak install --assumeyes org.kde.Sdk//5.15-21.08
flatpak install --assumeyes org.freedesktop.Sdk.Extension.golang//21.08
flatpak install --assumeyes org.kde.Platform//5.15-21.08
- name: Show environment variables
run: >
echo IS_RELEASE: ${{ env.IS_RELEASE }}
@ -67,7 +76,7 @@ jobs:
- name: Replace git tag by the commit id on which it runs
if: github.ref_type != 'tag'
run: >
sed -i 's@tag: gittyup_v[1-9]*.[0-9]*.[0-9]*@commit: "${{ (github.event.pull_request && github.event.pull_request.head.sha) || github.sha }}"@' com.github.Murmele.Gittyup/com.github.Murmele.Gittyup.yml
sed -i 's@tag: .*@commit: "${{ (github.event.pull_request && github.event.pull_request.head.sha) || github.sha }}"@' com.github.Murmele.Gittyup/com.github.Murmele.Gittyup.yml
- name: Use correct git tag
if: github.ref_type == 'tag'
@ -88,6 +97,11 @@ jobs:
run: >
sed -i 's@desktop-file-name-suffix: ""@desktop-file-name-suffix: " (Development)"@' com.github.Murmele.Gittyup/com.github.Murmele.Gittyup.yml
- name: Enable automatic update
if: github.ref_type != 'tag'
run: >
sed -i 's@-DENABLE_UPDATE_OVER_GUI=OFF@-DENABLE_UPDATE_OVER_GUI=ON@' com.github.Murmele.Gittyup/com.github.Murmele.Gittyup.yml
- name: Show Flatpak manifest
run: cat com.github.Murmele.Gittyup/com.github.Murmele.Gittyup.yml
@ -123,8 +137,11 @@ jobs:
os: ubuntu-latest
ninja_platform: linux
qt_platform: linux
qt_arch: gcc_64
openssl_arch: linux-x86_64
cmake_flags: "-DGENERATE_APPDATA=ON"
ld_library_arch: linux-x86-64
cmake_flags: "-DGENERATE_APPDATA=ON -DCMAKE_INSTALL_PREFIX=/usr -DUSE_SYSTEM_QT=ON -DENABLE_UPDATE_OVER_GUI=OFF"
pack: 0
cmake_env: {}
- name: macos
@ -193,7 +210,7 @@ jobs:
perl-version: '5.30'
- name: Install Qt
uses: jurplel/install-qt-action@v2.13.0
uses: jurplel/install-qt-action@v3.3.0
timeout-minutes: 10
if: "!matrix.qt.check_only"
with:
@ -205,7 +222,7 @@ jobs:
modules: qtwebengine
- name: Install Qt
uses: jurplel/install-qt-action@v2.13.0
uses: jurplel/install-qt-action@v3.3.0
timeout-minutes: 10
if: matrix.qt.check_only
with:
@ -264,7 +281,7 @@ jobs:
run: |
mkdir -p build/release
cd build/release
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DDEBUG_OUTPUT=OFF -DGITTYUP_CI_TESTS=ON ${{ env.CMAKE_FLAGS }} ${{ matrix.env.cmake_flags }} ../..
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DUPDATE_TRANSLATIONS=ON -DGITTYUP_CI_TESTS=ON ${{ env.CMAKE_FLAGS }} ${{ matrix.env.cmake_flags }} ../..
- name: Build Information
run: |
@ -301,15 +318,20 @@ jobs:
if: matrix.env.pack && !matrix.qt.check_only
uses: actions/upload-artifact@v3
with:
path: build/release/VERSION.txt
path: build/release/Version.txt
name: Gittyup-VERSION
- name: Check Version file
run: |
cd build/release
cat ./Version.txt
- name: Test
if: matrix.env.ninja_platform != 'win' && matrix.env.ninja_platform != 'mac'
uses: GabrielBB/xvfb-action@v1
with:
working-directory: build/release
run: ninja check
run: ninja check --verbose
- name: Test (Windows)
if: matrix.env.ninja_platform == 'win'
@ -317,6 +339,44 @@ jobs:
cd build/release
ninja check_no_win32_offscreen
- name: Build Appimage
if: matrix.env.ninja_platform == 'linux' && !matrix.qt.check_only
run: |
cd build/release
sudo apt -y install appstream
sudo apt -y install libfuse2
sudo apt -y install libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-shape0
sudo apt -y install libxcb-shm0 libxcb-sync1 libxcb-util1 libxcb-xinerama0 libxcb-xinput0 libxcb-xkb1 libxcb-xrm0 libxcb-xv0 libxcb-xvmc0 libxcb1
mkdir -p AppDir
DESTDIR=AppDir ninja install
export QTDIR=$RUNNER_WORKSPACE/Qt/${{ matrix.qt.version }}/${{ matrix.env.qt_arch }}
rm -rf ./AppDir/usr/include/
strip ./AppDir/usr/bin/cmark ./AppDir/usr/bin/gittyup ./AppDir/usr/bin/indexer ./AppDir/usr/bin/relauncher
wget -c https://github.com/$(wget -q https://github.com/probonopd/go-appimage/releases/expanded_assets/continuous -O - | grep "appimagetool-.*-x86_64.AppImage" | head -n 1 | cut -d '"' -f 2)
chmod +x appimagetool-*.AppImage
QTDIR=$QTDIR ./appimagetool-*.AppImage -s deploy ./AppDir/usr/share/applications/*.desktop --appimage-extract-and-run # Bundle EVERYTHING
# Modify the AppDir: move ld-linux into the same directory as the payload application
# and change AppRun accordingly; so that, e.g., Qt qApp->applicationDirPath() works
mv ./AppDir/lib64/ld-${{ matrix.env.ld_library_arch }}.so.2 ./AppDir/usr/bin/
sed -i -e 's@^LD_LINUX.*@LD_LINUX=$(find "$HERE/usr/bin" -name "ld-*.so.*" | head -n 1)@g' ./AppDir/AppRun
rm ./AppDir/usr/share/metainfo/gittyup.appdata.xml
VERSION=$(cat ./Version.txt)
VERSION="$VERSION" ./appimagetool-*.AppImage ./AppDir # turn AppDir into AppImage
#ls -lh Gittyup-*
- name: Publish Appimage
if: matrix.env.ninja_platform == 'linux' && !matrix.qt.check_only
uses: actions/upload-artifact@v3
with:
path: build/release/*.AppImage
name: GittyupAppImage
publish:
# https://github.com/marvinpinto/actions/issues/177
needs: [flatpak, build]
@ -347,7 +407,7 @@ jobs:
# version is exported from cmake to file
- name: Retrieve version
run: |
echo "::set-output name=VERSION::$(cat artifacts/Gittyup-VERSION/VERSION.txt)"
echo "::set-output name=VERSION::$(cat artifacts/Gittyup-VERSION/Version.txt)"
id: version
- name: Update GitHub release (latest tag)
@ -363,6 +423,7 @@ jobs:
**/artifacts/Gittyup macos/Gittyup*.dmg
**/artifacts/Gittyup Flatpak/com.github.Murmele.Gittyup.yml
**/Gittyup-x86_64/*.flatpak
**/artifacts/GittyupAppImage/*
- name: Update GitHub release (version tag)
uses: marvinpinto/action-automatic-releases@latest
@ -378,6 +439,7 @@ jobs:
**/artifacts/Gittyup macos/Gittyup*.dmg
**/artifacts/Gittyup Flatpak/com.github.Murmele.Gittyup.yml
**/Gittyup-x86_64/*.flatpak
**/artifacts/GittyupAppImage/*
# needed otherwise the docs folder is not available
- name: Checkout repository

8
.gitignore vendored
View File

@ -1,5 +1,13 @@
build
.cache
.DS_Store
.project
.vscode/
CMakeLists.txt.user
cmake-build-debug/
cmake-build-release/
build
.idea/
.venv
compile_commands.json

7
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,7 @@
repos:
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v13.0.1
hooks:
- id: clang-format
args: [ -i ]

View File

@ -1,15 +1,22 @@
cmake_minimum_required(VERSION 3.6.2)
cmake_minimum_required(VERSION 3.12)
project(Gittyup)
# Set name and version.
set(GITTYUP_NAME "Gittyup")
set(GITTYUP_EXECUTABLE_NAME "gittyup")
set(GITTYUP_IDENTIFIER "com.github.Murmele.Gittyup")
set(GITTYUP_VERSION_MAJOR 1)
set(GITTYUP_VERSION_MINOR 2)
set(GITTYUP_VERSION_PATCH 1)
set(GITTYUP_VERSION_MINOR 3)
set(GITTYUP_VERSION_PATCH 0)
set(GITTYUP_VERSION
"${GITTYUP_VERSION_MAJOR}.${GITTYUP_VERSION_MINOR}.${GITTYUP_VERSION_PATCH}"
)
string(TIMESTAMP CURR_YEAR "%Y")
add_compile_definitions(CURR_YEAR=${CURR_YEAR})
configure_file(${CMAKE_SOURCE_DIR}/LICENSE.md.in ${CMAKE_SOURCE_DIR}/LICENSE.md
@ONLY NEWLINE_STYLE UNIX)
# Write version to file so it can be used also from external, for example in the
# github manifest
file(WRITE "${CMAKE_BINARY_DIR}/Version.txt" ${GITTYUP_VERSION})
@ -45,6 +52,7 @@ set(BUILD_SHARED_LIBS OFF)
option(FLATPAK "Building for flatpak" OFF)
option(DEBUG_FLATPAK "Building but using flatpak urls for testing" OFF)
option(ENABLE_UPDATE_OVER_GUI "Enable updating from the Gittyup gui" ON)
option(USE_SYSTEM_OPENSSL "Use the system-wide OpenSSL installation" OFF)
option(
USE_SYSTEM_LIBGIT2
@ -68,7 +76,10 @@ set(LUA_MODULES_PATH
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
if(UNIX AND NOT APPLE)
if(HAIKU)
# qsort_r in libgit2 requires this
set(CMAKE_EXE_LINKER_FLAGS -lgnu)
elseif(UNIX AND NOT APPLE)
set(CMAKE_EXE_LINKER_FLAGS -ldl)
endif()
@ -86,9 +97,20 @@ if(UNIX)
set(QT_MODULES ${QT_MODULES} DBus)
endif()
option(DEBUG_OUTPUT "Print debug output" ON)
option(DEBUG_OUTPUT
"Print debug output (Only available if debug menu is enabled!)" ON)
option(DEBUG_OUTPUT_GENERAL "Enable general debug messages" ON)
option(DEBUG_OUTPUT_REFRESH "Enable debug messages to debug the refresh flow"
OFF)
if(NOT DEBUG_OUTPUT)
add_compile_definitions(QT_NO_DEBUG_OUTPUT)
else()
if(DEBUG_OUTPUT_GENERAL)
add_compile_definitions(DEBUG_OUTPUT_GENERAL)
endif()
if(DEBUG_OUTPUT_REFRESH)
add_compile_definitions(DEBUG_OUTPUT_REFRESH)
endif()
endif()
find_package(
@ -103,6 +125,10 @@ if(FLATPAK)
add_compile_definitions(FLATPAK)
endif()
if(ENABLE_UPDATE_OVER_GUI)
add_compile_definitions(ENABLE_UPDATE_OVER_GUI)
endif()
if(DEBUG_FLATPAK)
add_compile_definitions(DEBUG_FLATPAK)
endif()
@ -125,6 +151,18 @@ if(APPLE)
endforeach()
endif()
if(APPLE)
set(CONTENTS_DIR ${GITTYUP_EXECUTABLE_NAME}.app/Contents)
set(RESOURCES_DIR ${CONTENTS_DIR}/Resources)
set(L10N_INSTALL_DIR ${RESOURCES_DIR}/l10n)
elseif(UNIX)
set(L10N_INSTALL_DIR ${CMAKE_INSTALL_LOCALEDIR}/${GITTYUP_NAME})
set(RESOURCES_DIR ${CMAKE_INSTALL_DATADIR}/${GITTYUP_NAME})
else()
set(L10N_INSTALL_DIR Resources/l10n)
set(RESOURCES_DIR Resources)
endif()
add_subdirectory(dep)
add_subdirectory(src)
add_subdirectory(l10n)

View File

@ -1,7 +1,7 @@
MIT License
Copyright (c) 2018 Scientific Toolworks, Inc.
Copyright (c) 2021-2022 Gittyup contributors
Copyright (c) 2021-2023 Gittyup contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

22
LICENSE.md.in Normal file
View File

@ -0,0 +1,22 @@
MIT License
Copyright (c) 2018 Scientific Toolworks, Inc.
Copyright (c) 2021-@CURR_YEAR@ Gittyup contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -132,8 +132,11 @@ where `<path-to-qt>` points to the Qt install directory that contains
`bin`, `lib`, etc.
**Build**
```
ninja
```
### A Convenient Shell Script for Ubuntu is available [here](https://raw.githubusercontent.com/Murmele/Gittyup/master/pack/buildUbuntu.sh), and will install all the necessary prerequisites, and build a release version for immediate use.
How to Install
-----------------
@ -177,7 +180,6 @@ function run_disown_silence(){
run_disown_silence flatpak run com.github.Murmele.Gittyup
```
How to Contribute
-----------------
@ -192,6 +194,15 @@ branch. Create pull requests against the `master` branch. Follow the
[seven guidelines](https://chris.beams.io/posts/git-commit/) to writing a
great commit message.
Prior to committing a change, please use `cl-format.sh` to ensure your code
adheres to the formatting conventions for this project. You can also use the
`setup-env.sh` script to install a pre-commit hook which will automatically
run `clang-format` against all modified files.
Prior to pushing a change, please ensure you run the unit tests to avoid any
regressions. These are found in `<build-dir>/test` and can be run using
`ctest`.
License
-------

View File

@ -1,18 +1,18 @@
#!/bin/bash
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
cd "`dirname "$0"`"
# Variable that will hold the name of the clang-format command
FMT=""
FOLDERS=("./src" "./test")
FOLDERS=("./src" "./test" "./l10n")
# Some distros just call it clang-format. Others (e.g. Ubuntu) are insistent
# that the version number be part of the command. We prefer clang-format if
# that's present, otherwise we work backwards from highest version to lowest
# version but at least 13.
for clangfmt in clang-format{,-{1,2,3}{9,8,7,6,5,4,3}}; do
if which "$clangfmt" &>/dev/null; then
# We specifically require clang-format v13. Some distros include the version
# number in the name, others don't. Prefer the specifically-named version.
for clangfmt in clang-format-13 clang-format
do
if command -v "$clangfmt" &>/dev/null; then
FMT="$clangfmt"
break
fi
@ -24,6 +24,14 @@ if [ -z "$FMT" ]; then
exit 1
fi
# Check we have v13 of clang-format
VERSION=`$FMT --version | grep -Po 'version\s\K(\d+)'`
if [ "$VERSION" != "13" ]; then
echo "Found clang-format v$VERSION, but v13 is required. Please install v13 of clang-format and try again."
echo "On Debian-derived distributions, this can be done via: apt install clang-format-13"
exit 1
fi
function format() {
for f in $(find $@ \( -type d -path './test/dep/*' -prune \) -o \( -name '*.h' -or -name '*.m' -or -name '*.mm' -or -name '*.c' -or -name '*.cpp' \)); do
echo "format ${f}";
@ -41,9 +49,18 @@ for dir in ${FOLDERS[@]}; do
fi
done
# Format cmake files
# NOTE: requires support for python venv; on Debian-like distros, this can be
# installed using apt install python3-venv
echo "Start formatting cmake files"
pip install cmake-format==0.6.13
CMAKE_FORMAT=${SCRIPT_DIR}/.venv/bin/cmake-format
if [ ! -f "$CMAKE_FORMAT" ]; then
pushd ${SCRIPT_DIR}
python3 -m venv .venv
.venv/bin/pip install cmake-format==0.6.13
popd
fi
find . \
\( -type d -path './test/dep/*' -prune \) \
-o \( -type d -path './dep/*/*' -prune \) \
-o \( -name CMakeLists.txt -exec cmake-format --in-place {} + \)
-o \( -name CMakeLists.txt -exec "$CMAKE_FORMAT" --in-place {} + \)

View File

@ -1,3 +1,12 @@
# For testing enable
#set(CMAKE_SOURCE_DIR <PATH TO SOURCE>)
#set(CMAKE_BINARY_DIR <PATH TO BINARY DIR>)
#set(DOC_SOURCE_DIR ${CMAKE_SOURCE_DIR}/docs)
#set(DOC_BINARY_DIR ${CMAKE_BINARY_DIR}/docs)
#set(CHANGELOG_HTML ${DOC_BINARY_DIR}/changelog.html)
#set(APPDATA_CONF ${CMAKE_SOURCE_DIR}/rsrc/linux/com.github.Murmele.Gittyup.appdata.xml.in)
#set(APPDATA ${CMAKE_BINARY_DIR}/rsrc/linux/com.github.Murmele.Gittyup.appdata.xml)
# add release notes to the appdata file
file(READ "${CHANGELOG_HTML}" HTML_CHANGELOGS)
# it is not allowed to have multiple texts without being in an environment
@ -5,6 +14,7 @@ file(READ "${CHANGELOG_HTML}" HTML_CHANGELOGS)
string(REGEX REPLACE "<p>([^<]*)<\\/p>" "\\1" RELEASES ${HTML_CHANGELOGS}) # remove paragraph environment
string(REGEX REPLACE "<h4>([A-Za-z0-9]*)<\\/h4>" "<p>\\1</p>" RELEASES ${RELEASES}) # h4 is unknow to appdata so change it to a paragraph environment
string(REPLACE "\n" "\n\t" RELEASES ${RELEASES}) # add tabulator
string(REGEX REPLACE "<h3>(v[1-9]\\.[0-9]\\.[0-9]) - ([0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9])<\\/h3>" "<release version='\\1' date='\\2'>\n\t<description>" RELEASES ${RELEASES})
# For Dev Version "vX.X.X - <current date> (DEV)" can be used to show in the changelog the current progress
string(REGEX REPLACE "<h3>(v[1-9]\\.[0-9]\\.[0-9]|vX\\.X\\.X) - ([0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9])( \\(DEV\\))?<\\/h3>" "<release version='\\1' date='\\2'>\n\t<description>" RELEASES ${RELEASES})
string(REGEX REPLACE "<hr \\/>" "</description>\n\t</release>" RELEASES ${RELEASES})
configure_file(${APPDATA_CONF} ${APPDATA})

View File

@ -56,13 +56,13 @@ theme['button'] = {
-- commit list colors
-- { default, active, inactive, disabled }
theme['commits'] = {
text = '#E1E5F2',
bright_text = '#AAB2BE',
text = '#AAB2BE',
bright_text = '#E1E5F2',
background = '#2D2E34',
alternate = '#2D2E34', -- an alternate background color for list rows
highlight = { active = '#2A82DA', inactive = '#1B5B9B' },
highlighted_text = { active = '#E1E5F2', inactive = '#E1E5F2' },
highlighted_bright_text = { active = '#A6CBF0', inactive = '#9090A5' }
highlighted_text = { active = '#A6CBF0', inactive = '#E1E5F2' },
highlighted_bright_text = { active = '#E1E5F2', inactive = '#9090A5' }
}
-- status badge colors
@ -77,7 +77,12 @@ theme['badge'] = {
selected = '#E1E5F2', -- the color when a list item is selected
conflicted = '#DA2ADA', -- the color of conflicted items
head = '#52A500', -- a bolder color to indicate the HEAD
notification = '#8C2026' -- the color of toolbar notifications badges
notification = '#8C2026', -- the color of toolbar notifications badges
modified = '#91973A', -- (yellow) the color of the badge when the file is modified
added = '#394734', -- (green) the color of the badge when the file was newly added
deleted = '#5E3638', -- (red) the color of the badge when the file was deleted
untracked = '#2A4944', -- (green blue) the color of the badge when the file is untracked
renamed = '#23455E' -- (blue) the color of the badge when the file is renamed
}
}

View File

@ -69,7 +69,7 @@ theme['commits'] = {
-- { normal, selected, conflicted, head, notification }
theme['badge'] = {
foreground = {
normal = '#FFFFFF',
normal = '#000000',
selected = '#6C6C6C'
},
background = {
@ -77,7 +77,12 @@ theme['badge'] = {
selected = '#FFFFFF', -- the color when a list item is selected
conflicted = '#D22222', -- the color of conflicted items
head = '#6F7379', -- a bolder color to indicate the HEAD
notification = '#FF0000' -- the color of toolbar notifications badges
notification = '#FF0000', -- the color of toolbar notifications badges
modified = '#FFEEDB', -- (yellow) the color of the badge when the file is modified
added = '#DCFFDC', -- (green) the color of the badge when the file was newly added
deleted = '#FFDCDC', -- (red) the color of the badge when the file was deleted
untracked = '#d9ead2', -- (green blue) the color of the badge when the file is untracked
renamed = '#d2e8fc' -- (blue) the color of the badge when the file is renamed
}
}

View File

@ -4,8 +4,9 @@ if(NOT USE_SYSTEM_GIT)
macro(add_helper NAME)
set(TARGET git-credential-${NAME})
add_executable(${TARGET} ${PATH}/${NAME}/${TARGET}.c)
set_target_properties(${TARGET} PROPERTIES RUNTIME_OUTPUT_DIRECTORY
$<TARGET_FILE_DIR:gittyup>)
set_target_properties(
${TARGET} PROPERTIES RUNTIME_OUTPUT_DIRECTORY
$<TARGET_FILE_DIR:gittyup>/credential-helpers)
if(${ARGC} GREATER 1)
target_link_libraries(${TARGET} ${ARGV1})
@ -14,7 +15,7 @@ if(NOT USE_SYSTEM_GIT)
if(NOT APPLE)
install(
TARGETS ${TARGET}
DESTINATION .
DESTINATION ${CMAKE_INSTALL_BINDIR}/credential-helpers
COMPONENT ${GITTYUP_NAME})
endif()
endmacro()

@ -1 +1 @@
Subproject commit 29708a562a1887a91de0fa6ca668c71871accde9
Subproject commit 3f499b24f3bcd66db022074f7e8b4f6ee266a3ae

View File

@ -59,5 +59,8 @@ target_link_libraries(scintilla Qt5::Widgets lexilla)
set_target_properties(scintilla PROPERTIES AUTOMOC ON)
set(SCINTILLUA_LEXERS_DIR
${RESOURCES_DIR}/lexers
CACHE INTERNAL "")
set(SRC_SCINTILLUA_LEXERS_DIR
${CMAKE_CURRENT_SOURCE_DIR}/scintillua/lexers
CACHE INTERNAL "")

View File

@ -1,11 +1,74 @@
### v1.2.1 - 2022-11-10
### vX.X.X - 2023-04-20 (DEV)
Description
#### Added
* UI(Commit List): Added a right-click menu entry to rename branches.
* UI(Main Menu): Added a menu-entry to rename the current branch.
#### Changed
* UI(Commit List): Collapse multiple branch and tag right-click menu entries
into submenus. This affects the checkout and delete operations.
* Fix(Build System): Force usage of clang-format v13 to ensure consistent formatting.
----
### v1.3.0 - 2023-04-20
Performance Improvement and feature release
#### Added
* Colorized status badges
* Template: use first template as default template for the commit message
* Search function for the treeview
* Reworked credential store: add possibility to choose between different methods to store credentials
#### Changed
* Fix external diff in Flatpak build
* Fix windows credentials
* Fix force push to correct remote
* Fix tab title if more than three times a repository with the same name is opened
* Fix storing repository settings correctly, because otherwise they are not applied
* Fix language support
* Improved refresh velocity
* Fix storing and restoring current opened file when Gittyup refreshes
* Improved velocity for files with many hunks
----
### v1.2.2 - 2023-01-22
Bug fix release
#### Changed
* Fix flatpak install process
----
### v1.2.1 - 2023-01-22
Bug fix release
#### Added
* Possibility to hide avatar (Settings - Window - View - Show Avatars)
* Show log entry when a conflict during rebase happens
#### Changed
* Fix download url for flatpak and macos
* Fix Segmentation fault when ignoring files
* Fix discard of complete files and submodules
* Fix context menu entries
* Fix bytesize overflow
* Fix focus loose during scrolling in the Commitlist with the keyboard
* Do not crash when the repository is for some reason broken
* Fix crash if rebasing is not possible
----
### v1.2.0 - 2022-10-28

View File

@ -31,7 +31,26 @@ Report bugs in Gittyup by opening an issue in the
Remember to search for existing issues before creating a new one.
If you still need help, check out our Matrix channel
[Gittyup:martix.org](https://matrix.to/#/#Gittyup:matrix.org).
[Gittyup:matrix.org](https://matrix.to/#/#Gittyup:matrix.org).
Multi language support
======================
Gittyup supports the following languages:
- English (en)
- German (de)
- Spanisch (es)
- Japanese (ja)
- Portuguese (pt)
- Portuguese Brazil (pt_BR)
- Chinese (zh_CN)
- Russian (ru)
By default the system language is used. To switch to another language execute the application with the following command
```
LANG=<lang> <executable>
```
Features
========
@ -74,9 +93,9 @@ Solving rebase conflicts and continuing after conflicts are solved
![Rebase Conflicts](https://raw.githubusercontent.com/Murmele/Gittyup/master/rsrc/screenshots/RebaseConflicts.png)
### Staring commits
### Starring commits
to find specific commits much faster
![Staring commits](https://raw.githubusercontent.com/Murmele/Gittyup/master/rsrc/screenshots/starring_commits.png)
![Starring commits](https://raw.githubusercontent.com/Murmele/Gittyup/master/rsrc/screenshots/starring_commits.png)
### Tag selection
Use an existing tag as template for your next tag. So you never have to look which is your latest tag
@ -84,7 +103,7 @@ Use an existing tag as template for your next tag. So you never have to look whi
![Tag selection](https://raw.githubusercontent.com/Murmele/Gittyup/master/rsrc/screenshots/tag_selection.png)
### Commit message template
Create you commit messages according a defined template
Create you commit messages according a defined template. The first template is automatically applied to the commit message editor.
![Commit message template selection](https://raw.githubusercontent.com/Murmele/Gittyup/master/rsrc/screenshots/CommitMessageTemplateSelection.png)

View File

@ -14,10 +14,18 @@ set(LANGUAGES
set(SRC_DIR ${CMAKE_SOURCE_DIR}/src)
file(GLOB_RECURSE SOURCE_FILES ${SRC_DIR}/*.h ${SRC_DIR}/*.cpp ${SRC_DIR}/*.mm)
set(SYSTEM_LANG_KEY "System")
set(SUPPORTED_LANGUAGES "{\"${SYSTEM_LANG_KEY}\", \"${SYSTEM_LANG_KEY}\"},")
foreach(LANGUAGE ${LANGUAGES})
set(TS_FILES ${TS_FILES} gittyup_${LANGUAGE}.ts)
set(SUPPORTED_LANGUAGES
"${SUPPORTED_LANGUAGES} {\"${LANGUAGE}\", \"${LANGUAGE}\"},")
endforeach()
set(LANGUAGE_SOURCE_FILE "${CMAKE_CURRENT_BINARY_DIR}/languages.cpp")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/languages.cpp.inc"
${LANGUAGE_SOURCE_FILE} @ONLY)
if(UPDATE_TRANSLATIONS)
# FIXME: Clean removes the .ts files.
qt5_create_translation(QM_FILES ${SOURCE_FILES} ${TS_FILES})
@ -28,12 +36,11 @@ endif()
add_custom_target(translations DEPENDS ${QM_FILES})
add_dependencies(gittyup translations)
add_library(translation ${LANGUAGE_SOURCE_FILE})
target_include_directories(translation PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
target_link_libraries(translation Qt5::Core)
# install language files
if(APPLE)
set(CONTENTS_DIR ${GITTYUP_NAME}.app/Contents)
elseif(UNIX)
set(CONTENTS_DIR ${CMAKE_INSTALL_DATADIR}/gittyup)
endif()
foreach(LANGUAGE ${LANGUAGES})
set(QT_QM_FILES ${QT_QM_FILES} ${QT_TRANSLATIONS_DIR}/qtbase_${LANGUAGE}.qm)
@ -44,19 +51,22 @@ foreach(LANGUAGE ${LANGUAGES})
TARGET translations
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory
${DIR}/${CONTENTS_DIR}/Resources/${LANGUAGE}.lproj)
${DIR}/${RESOURCES_DIR}/${LANGUAGE}.lproj)
endif()
endforeach()
if(APPLE OR UNIX)
set(RESOURCES_PREFIX ${CONTENTS_DIR}/)
endif()
foreach(QM_FILE ${QM_FILES} ${QT_QM_FILES})
foreach(QM_FILE ${QT_QM_FILES})
if(EXISTS ${QM_FILE})
install(
FILES ${QM_FILE}
DESTINATION ${RESOURCES_PREFIX}Resources/l10n
DESTINATION ${L10N_INSTALL_DIR}
COMPONENT ${GITTYUP_NAME})
endif()
endforeach()
foreach(QM_FILE ${QM_FILES})
install(
FILES ${QM_FILE}
DESTINATION ${L10N_INSTALL_DIR}
COMPONENT ${GITTYUP_NAME})
endforeach()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

9
l10n/languages.cpp.inc Normal file
View File

@ -0,0 +1,9 @@
#include "languages.h"
#include <QList>
#include <QString>
namespace Languages {
const QString system = QStringLiteral("@SYSTEM_LANG_KEY@");
const QMap<const char*, const char*> languages = { @SUPPORTED_LANGUAGES@ };
}

11
l10n/languages.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef LANGUAGES_H
#define LANGUAGES_H
#include <QMap>
namespace Languages {
extern const QString system;
extern const QMap<const char *, const char *> languages;
} // namespace Languages
#endif // LANGUAGES_H

View File

@ -2,7 +2,6 @@ set(CONF_DIR ${CMAKE_SOURCE_DIR}/conf)
set(RSRC_DIR ${CMAKE_SOURCE_DIR}/rsrc)
set(MAC $<PLATFORM_ID:Darwin>)
set(CONTENTS_DIR ${GITTYUP_NAME}.app/Contents)
# Install Qt plugins.
set(QT_PLUGINS QJpegPlugin)
@ -17,6 +16,14 @@ else()
QComposePlatformInputContextPlugin)
endif()
if(WIN32)
set(INSTALL_LIBDIR ${CMAKE_INSTALL_BINDIR})
elseif(NOT APPLE)
set(INSTALL_LIBDIR ${CMAKE_INSTALL_LIBDIR})
else()
set(INSTALL_LIBDIR ${CONTENTS_DIR})
endif()
if(FLATPAK)
qt_import_plugins(gittyup INCLUDE ${QT_PLUGINS})
elseif(NOT USE_SYSTEM_QT)
@ -35,7 +42,7 @@ elseif(NOT USE_SYSTEM_QT)
install(
FILES ${PLUGIN}
DESTINATION $<${MAC}:${CONTENTS_DIR}/>Plugins/${DIR_NAME}
DESTINATION ${INSTALL_LIBDIR}/Plugins/${DIR_NAME}
PERMISSIONS
OWNER_READ
OWNER_WRITE
@ -100,7 +107,7 @@ elseif(NOT USE_SYSTEM_QT)
install(
FILES ${QT_LIBRARY}
DESTINATION .
DESTINATION ${INSTALL_LIBDIR}
PERMISSIONS
OWNER_READ
OWNER_WRITE
@ -131,7 +138,7 @@ if(UNIX AND NOT APPLE)
install(
FILES "${LIB_PATH}/${LIB_NAME}${LIB_EXT}"
DESTINATION .
DESTINATION ${CMAKE_INSTALL_LIBDIR}
PERMISSIONS
OWNER_READ
OWNER_WRITE
@ -166,7 +173,7 @@ if(NOT APPLE)
install(
FILES ${SSL_LIB}
DESTINATION .
DESTINATION ${INSTALL_LIBDIR}
PERMISSIONS
OWNER_READ
OWNER_WRITE
@ -186,16 +193,25 @@ if(NOT APPLE)
set(QT_CONF ${CONF_DIR}/qt.conf)
install(
FILES ${QT_CONF}
DESTINATION $<${MAC}:${CONTENTS_DIR}/Resources/>.
DESTINATION
$<${MAC}:${RESOURCES_DIR}>$<$<PLATFORM_ID:Windows>:${INSTALL_LIBDIR}>
COMPONENT ${GITTYUP_NAME})
endif()
endif()
if(UNIX AND NOT APPLE)
foreach(icon 16 32 64 128 256 512)
install(
FILES ${RSRC_DIR}/Gittyup.iconset/icon_${icon}x${icon}.png
DESTINATION share/icons/hicolor/${icon}x${icon}/apps
COMPONENT ${GITTYUP_NAME}
RENAME ${GITTYUP_EXECUTABLE_NAME}.png)
endforeach()
install(
DIRECTORY ${RSRC_DIR}/Gittyup.iconset
DESTINATION "Resources"
COMPONENT ${GITTYUP_NAME})
FILES ${RSRC_DIR}/Gittyup.iconset/gittyup_logo.svg
DESTINATION share/icons/hicolor/scalable/apps
COMPONENT ${GITTYUP_NAME}
RENAME ${GITTYUP_EXECUTABLE_NAME}.svg)
endif()
# Sign bundle on macOS.
@ -204,7 +220,7 @@ if(APPLE AND CODESIGN_IDENTITY)
CODE "execute_process(COMMAND
codesign --deep --timestamp --options runtime
-s \"${CODESIGN_IDENTITY}\"
\${CMAKE_INSTALL_PREFIX}/${GITTYUP_NAME}.app
\${CMAKE_INSTALL_PREFIX}/${GITTYUP_EXECUTABLE_NAME}.app
)"
COMPONENT ${GITTYUP_NAME})
endif()
@ -220,9 +236,10 @@ elseif(WIN32)
set(CPACK_NSIS_CREATE_ICONS_EXTRA
"CreateShortCut \
\\\"$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\${GITTYUP_NAME}.lnk\\\" \
\\\"$INSTDIR\\\\${GITTYUP_NAME}.exe\\\"")
set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".")
set(CPACK_NSIS_MUI_FINISHPAGE_RUN ${GITTYUP_NAME}.exe)
\\\"$INSTDIR\\\\${CMAKE_INSTALL_BINDIR}\\\\${GITTYUP_EXECUTABLE_NAME}.exe\\\""
)
set(CPACK_NSIS_EXECUTABLES_DIRECTORY ${CMAKE_INSTALL_BINDIR})
set(CPACK_NSIS_MUI_FINISHPAGE_RUN ${GITTYUP_EXECUTABLE_NAME}.exe)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
install(
@ -262,11 +279,11 @@ if(WIN32)
set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS
"${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}
WriteRegStr HKCR 'Directory\\\\Background\\\\shell\\\\Gittyup' '' 'Open with Gittyup'
WriteRegStr HKCR 'Directory\\\\Background\\\\shell\\\\Gittyup' 'Icon' '\\\"$INSTDIR\\\\gittyup.exe\\\"'
WriteRegStr HKCR 'Directory\\\\Background\\\\shell\\\\Gittyup\\\\command' '' '\\\"$INSTDIR\\\\gittyup.exe\\\" \\\"%V\\\"'
WriteRegStr HKCR 'Directory\\\\Background\\\\shell\\\\Gittyup' 'Icon' '\\\"$INSTDIR\\\\${CMAKE_INSTALL_BINDIR}\\\\${GITTYUP_EXECUTABLE_NAME}.exe\\\"'
WriteRegStr HKCR 'Directory\\\\Background\\\\shell\\\\Gittyup\\\\command' '' '\\\"$INSTDIR\\\\${CMAKE_INSTALL_BINDIR}\\\\${GITTYUP_EXECUTABLE_NAME}.exe\\\" \\\"%V\\\"'
WriteRegStr HKCR 'Directory\\\\shell\\\\Gittyup' '' 'Open with Gittyup'
WriteRegStr HKCR 'Directory\\\\shell\\\\Gittyup' 'Icon' '\\\"$INSTDIR\\\\gittyup.exe\\\"'
WriteRegStr HKCR 'Directory\\\\shell\\\\Gittyup\\\\command' '' '\\\"$INSTDIR\\\\gittyup.exe\\\" \\\"%L\\\"'"
WriteRegStr HKCR 'Directory\\\\shell\\\\Gittyup' 'Icon' '\\\"$INSTDIR\\\\${CMAKE_INSTALL_BINDIR}\\\\${GITTYUP_EXECUTABLE_NAME}.exe\\\"'
WriteRegStr HKCR 'Directory\\\\shell\\\\Gittyup\\\\command' '' '\\\"$INSTDIR\\\\${CMAKE_INSTALL_BINDIR}\\\\${GITTYUP_EXECUTABLE_NAME}.exe\\\" \\\"%L\\\"'"
)
set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS
"${CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS}
@ -277,13 +294,18 @@ endif(WIN32)
set(CPACK_PACKAGE_FILE_NAME ${PACKAGE_FILE_NAME})
set(CPACK_PACKAGE_DIRECTORY ${CMAKE_BINARY_DIR}/pack)
set(CPACK_PACKAGE_INSTALL_DIRECTORY ${GITTYUP_NAME})
set(CPACK_PACKAGE_VENDOR "Scientific Toolworks, Inc.")
set(CPACK_PACKAGE_VENDOR "Gittyup Contributors")
set(CPACK_PACKAGE_VERSION_MAJOR ${GITTYUP_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${GITTYUP_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${GITTYUP_VERSION_PATCH})
if(WIN32)
set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_SOURCE_DIR}/LICENSE.md)
else()
install(
FILES ${CMAKE_SOURCE_DIR}/LICENSE.md
DESTINATION ${CMAKE_INSTALL_DATADIR}/licenses/Gittyup
RENAME LICENSE)
endif()
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR})

View File

@ -1,2 +1,3 @@
Flatpak manifest can be found at: https://github.com/flathub/com.github.Murmele.Gittyup
Arch PKGBUILD file can be found at: TODO (currently it is in #314)
Linux (Tested On Ubuntu 22.04LTS) version is available in the buildUbuntu.sh file included in this directory.

26
pack/buildUbuntu.sh Executable file
View File

@ -0,0 +1,26 @@
sudo apt install build-essential libgl1-mesa-dev
sudo apt install cmake
sudo apt install libgit2-dev
sudo apt install cmark
sudo apt install git
sudo apt install libssh2-1-dev
sudo apt install openssl
sudo apt install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools
sudo apt install qttools5-dev
sudo apt install ninja-build
cd ../../..
git fetch
git submodule init
git submodule update
git pull
git checkout deps
cd dep/openssl/openssl/
./config -fPIC
make
cd
mkdir -vp build/release
cd build/release
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release ../..
ninja

View File

@ -16,6 +16,8 @@
<string>6.0</string>
<key>CFBundleLongVersionString</key>
<string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
<key>CFBundleDisplayName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundleName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundlePackageType</key>

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 4.4.0-Exiv2">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:exif="http://ns.adobe.com/exif/1.0/"
xmlns:xmp="http://ns.adobe.com/xap/1.0/"
xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
xmlns:darktable="http://darktable.sf.net/"
exif:DateTimeOriginal=""
xmp:Rating="1"
xmpMM:DerivedFrom="github-repository-open-graph-Gittyup.png"
darktable:import_timestamp="63810317507625743"
darktable:change_timestamp="-1"
darktable:export_timestamp="-1"
darktable:print_timestamp="-1"
darktable:xmp_version="5"
darktable:raw_params="0"
darktable:auto_presets_applied="1"
darktable:history_end="4"
darktable:iop_order_version="2"
darktable:history_basic_hash="adf93e2c7aefb698a6ff153e3fa7f0b8"
darktable:history_current_hash="adf93e2c7aefb698a6ff153e3fa7f0b8">
<darktable:masks_history>
<rdf:Seq/>
</darktable:masks_history>
<darktable:history>
<rdf:Seq>
<rdf:li
darktable:num="0"
darktable:operation="colorin"
darktable:enabled="1"
darktable:modversion="7"
darktable:params="gz48eJzjZBgFowABWAbaAaNgwAEAMNgADg=="
darktable:multi_name=""
darktable:multi_priority="0"
darktable:blendop_version="11"
darktable:blendop_params="gz14eJxjYIAACQYYOOHEgAZY0QVwggZ7CB6pfNoAAE8gGQg="/>
<rdf:li
darktable:num="1"
darktable:operation="colorout"
darktable:enabled="1"
darktable:modversion="5"
darktable:params="gz35eJxjZBgFo4CBAQAEEAAC"
darktable:multi_name=""
darktable:multi_priority="0"
darktable:blendop_version="11"
darktable:blendop_params="gz14eJxjYIAACQYYOOHEgAZY0QVwggZ7CB6pfNoAAE8gGQg="/>
<rdf:li
darktable:num="2"
darktable:operation="gamma"
darktable:enabled="1"
darktable:modversion="1"
darktable:params="0000000000000000"
darktable:multi_name=""
darktable:multi_priority="0"
darktable:blendop_version="11"
darktable:blendop_params="gz14eJxjYIAACQYYOOHEgAZY0QVwggZ7CB6pfNoAAE8gGQg="/>
<rdf:li
darktable:num="3"
darktable:operation="flip"
darktable:enabled="1"
darktable:modversion="2"
darktable:params="ffffffff"
darktable:multi_name=""
darktable:multi_priority="0"
darktable:blendop_version="11"
darktable:blendop_params="gz14eJxjYIAACQYYOOHEgAZY0QVwggZ7CB6pfNoAAE8gGQg="/>
</rdf:Seq>
</darktable:history>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>

View File

@ -45,7 +45,7 @@
<url type="donation">https://liberapay.com/Gittyup/donate</url>
<provides>
<binary>Gittyup</binary>
<binary>gittyup</binary>
<id>com.github.Murmele.Gittyup</id>
</provides>

View File

@ -2,11 +2,10 @@
Encoding=UTF-8
Name=Gittyup
Comment=Graphical Git client for Windows, Linux and macOS
Exec=Gittyup
StartupWMClass=gittyup
Exec=gittyup
StartupWMClass=com.github.Murmele.Gittyup
Icon=gittyup
Terminal=false
Type=Application
Categories=Development;
X-Desktop-File-Install-Version=0.1
Name[de_DE]=com.github.Murmele.Gittyup.desktop

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

44
setup-env.sh Executable file
View File

@ -0,0 +1,44 @@
#!/bin/bash
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
cd "`dirname "$0"`"
PRE_COMMIT=${SCRIPT_DIR}/.venv/bin/pre-commit
pre_commit() {
# Set up pre-commit hook
if [ ! -f "${PRE_COMMIT}" ]; then
pushd ${SCRIPT_DIR}
python3 -m venv .venv
.venv/bin/pip install pre-commit
popd
fi
${PRE_COMMIT} install
}
remove_pre_commt() {
if [ -f "${PRE_COMMIT}" ]; then
${PRE_COMMIT} uninstall
fi
}
if [ $# -eq 0 ]; then
set -- --help
fi
for arg in "$@"; do
case $arg in
pre_commit | pre-commit | install_pre_commit | install-pre-commit)
pre_commit
;;
remove_pre_commit | remove-pre-commit | uninstall_pre_commit | uninstall-pre-commit)
remove_pre_commit
;;
*)
echo "USAGE $0 [commands]"
echo " Commands:"
echo " install-pre-commit - installs pre-commit hooks"
echo " uninstall-pre-commit - removes pre-commit hooks"
;;
esac
done

View File

@ -1,5 +1,13 @@
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
add_compile_options(-Werror=switch)
# Uncomment to compile with more warnings add_compile_options(-Wall) Uncomment
# to compile with even more warnings add_compile_options(-Wextra) TODO:
# currently there are too many unused parameters which overwhelms the warning
# output with `-Wall`. So let's leave fixing these for later.
add_compile_options(-Wno-unused-parameter)
add_subdirectory(util)
add_subdirectory(cli)
add_subdirectory(conf)
add_subdirectory(cred)
@ -13,7 +21,6 @@ add_subdirectory(plugins)
add_subdirectory(tools)
add_subdirectory(ui)
add_subdirectory(update)
add_subdirectory(util)
add_subdirectory(watcher)
# Add executable last.

View File

@ -16,6 +16,8 @@
#include "ui/RepoView.h"
#include "ui/TabWidget.h"
#include "update/Updater.h"
#include "languages.h"
#include "util/Debug.h"
#include <QCloseEvent>
#include <QCommandLineParser>
#include <QDesktopServices>
@ -132,6 +134,18 @@ Application::Application(int &argc, char **argv, bool haltOnParseError)
// Set debug menu option.
MenuBar::setDebugMenuVisible(parser.isSet("debug-menu"));
Debug(QString("Root dir: %1").arg(Settings::rootDir().absolutePath()));
Debug(QString("App dir: %1").arg(Settings::appDir().absolutePath()));
Debug(QString("Doc dir: %1").arg(Settings::docDir().absolutePath()));
Debug(QString("Conf dir: %1").arg(Settings::confDir().absolutePath()));
Debug(QString("l10n dir: %1").arg(Settings::l10nDir().absolutePath()));
Debug(QString("dictionaries dir: %1")
.arg(Settings::dictionariesDir().absolutePath()));
Debug(QString("lexer dir: %1").arg(Settings::lexerDir().absolutePath()));
Debug(QString("themes dir: %1").arg(Settings::themesDir().absolutePath()));
Debug(
QString("pluginsDir dir: %1").arg(Settings::pluginsDir().absolutePath()));
// Set pathspec filter.
mPathspec = parser.value("filter");
@ -159,6 +173,12 @@ Application::Application(int &argc, char **argv, bool haltOnParseError)
.toBool()) &&
(!parser.isSet("no-translation"))) {
// Load translation files.
const auto &language =
Settings::instance()->value(Setting::Id::Language).toString();
if (language != Languages::system)
QLocale::setDefault(QLocale(language));
QLocale locale;
QDir l10n = Settings::l10nDir();
QString name = QString(GITTYUP_NAME).toLower();
@ -310,9 +330,9 @@ static MainWindow *openOrSwitch(QDir repo) {
}
#if defined(Q_OS_LINUX)
#define DBUS_SERVICE_NAME "com.github.Murmele.Gittyup"
#define DBUS_INTERFACE_NAME "com.github.Murmele.Gittyup.Application"
#define DBUS_OBJECT_PATH "/com/github/Murmele/Gittyup/Application"
#define DBUS_SERVICE_NAME GITTYUP_IDENTIFIER
#define DBUS_INTERFACE_NAME GITTYUP_DBUS_INTERFACE_NAME
#define DBUS_OBJECT_PATH GITTYUP_DBUS_OBJECT_PATH
DBusGittyup::DBusGittyup(QObject *parent) : QObject(parent) {}
@ -488,4 +508,4 @@ void Application::handleSslErrors(QNetworkReply *reply,
reply->ignoreSslErrors(errors);
settings.setValue("ssl/ignore", true);
}
}
}

View File

@ -18,7 +18,7 @@ file(GLOB CONFIG_FILES ${CONF_DIR}/*.lua)
file(GLOB DICTIONARIES ${CONF_DIR}/dictionaries/*.aff
${CONF_DIR}/dictionaries/*.dic ${CONF_DIR}/dictionaries/*.txt)
file(GLOB THEMES ${CONF_DIR}/themes/*.lua)
file(GLOB SCINTILLUA_LEXERS ${SCINTILLUA_LEXERS_DIR}/*.lua)
file(GLOB SCINTILLUA_LEXERS ${SRC_SCINTILLUA_LEXERS_DIR}/*.lua)
file(GLOB LUA_PLUGINS ${CONF_DIR}/plugins/*.lua ${CONF_DIR}/plugins/*.txt)
# Build resources.
@ -59,11 +59,22 @@ endif()
target_compile_definitions(
app
PUBLIC GITTYUP_NAME="${GITTYUP_NAME}" GITTYUP_VERSION="${GITTYUP_VERSION}"
PUBLIC GITTYUP_NAME="${GITTYUP_NAME}"
GITTYUP_VERSION="${GITTYUP_VERSION}"
GITTYUP_IDENTIFIER="${GITTYUP_IDENTIFIER}"
GITTYUP_DBUS_INTERFACE_NAME="${GITTYUP_IDENTIFIER}.Application"
GITTYUP_DBUS_OBJECT_PATH="/com/github/Murmele/Gittyup/Application"
PRIVATE QT_TRANSLATIONS_DIR="${QT_TRANSLATIONS_DIR}"
BUILD_DESCRIPTION="${BUILD_DESCRIPTION}")
target_link_libraries(app conf git ui update Qt5::Widgets)
target_link_libraries(
app
translation
conf
git
ui
update
Qt5::Widgets)
set_target_properties(app PROPERTIES AUTOMOC ON)
@ -103,8 +114,10 @@ endif()
target_link_libraries(gittyup app)
set_target_properties(
gittyup PROPERTIES OUTPUT_NAME ${GITTYUP_NAME} RUNTIME_OUTPUT_DIRECTORY
${CMAKE_BINARY_DIR})
gittyup
PROPERTIES OUTPUT_NAME ${GITTYUP_EXECUTABLE_NAME}
MACOSX_BUNDLE_BUNDLE_NAME ${GITTYUP_NAME}
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
if(WIN32)
target_link_libraries(app Dbghelp.lib)
@ -118,7 +131,7 @@ elseif(APPLE)
MACOSX_BUNDLE_BUNDLE_VERSION ${GITTYUP_VERSION}
MACOSX_BUNDLE_LONG_VERSION_STRING ${GITTYUP_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${GITTYUP_VERSION}
MACOSX_BUNDLE_GUI_IDENTIFIER "com.Murmele.Gittyup")
MACOSX_BUNDLE_GUI_IDENTIFIER ${GITTYUP_IDENTIFIER})
# Copy config files into bundle.
set_source_files_properties(
@ -152,19 +165,10 @@ endif()
add_dependencies(gittyup indexer relauncher)
if(FLATPAK)
# Install application.
install(
TARGETS gittyup
DESTINATION ./bin # otherwise the executable will not be found by flatpak
COMPONENT ${GITTYUP_NAME})
else()
# Install application.
install(
TARGETS gittyup
DESTINATION .
COMPONENT ${GITTYUP_NAME})
endif()
install(
TARGETS gittyup
DESTINATION $<IF:$<PLATFORM_ID:Darwin>,.,${CMAKE_INSTALL_BINDIR}>
COMPONENT ${GITTYUP_NAME})
if(APPLE)
get_target_property(QT_LIBRARY Qt5::Core LOCATION)
@ -172,7 +176,7 @@ if(APPLE)
get_filename_component(RPATH ${QT_FRAMEWORK} DIRECTORY)
# Fixup relauncher RPATH.
set(RELAUNCHER ${GITTYUP_NAME}.app/Contents/MacOS/relauncher)
set(RELAUNCHER ${CONTENTS_DIR}/MacOS/relauncher)
install(
CODE "execute_process(COMMAND
${CMAKE_INSTALL_NAME_TOOL} -delete_rpath \"${RPATH}\"
@ -189,7 +193,7 @@ execute_process(COMMAND
COMPONENT ${GITTYUP_NAME})
# Fixup indexer RPATH.
set(INDEXER ${GITTYUP_NAME}.app/Contents/MacOS/indexer)
set(INDEXER ${CONTENTS_DIR}/MacOS/indexer)
install(
CODE "execute_process(COMMAND
${CMAKE_INSTALL_NAME_TOOL} -delete_rpath \"${RPATH}\"
@ -207,13 +211,16 @@ execute_process(COMMAND
else()
# Windows and UNIX must be ./ otherwise it is interpreted as absolute path
if(UNIX)
set(RESOURCES_INSTALL_DIR
"${CMAKE_INSTALL_DATADIR}/gittyup/Resources"
CACHE STRING "The path for installing resource files")
else()
set(RESOURCES_INSTALL_DIR
"Resources"
CACHE STRING "The path for installing resource files")
install(
FILES ${CMAKE_SOURCE_DIR}/rsrc/linux/com.github.Murmele.Gittyup.desktop
DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
RENAME ${GITTYUP_EXECUTABLE_NAME}.desktop)
if(${GENERATE_APPDATA})
install(
FILES ${APPDATA}
DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo
RENAME ${GITTYUP_EXECUTABLE_NAME}.appdata.xml)
endif()
endif()
# Install config files.
@ -224,13 +231,13 @@ else()
install(
FILES ${CHANGELOG_HTML} ${ACKNOWLEDGMENTS_HTML} ${PRIVACY_HTML}
${PLUGIN_HTML}
DESTINATION ${RESOURCES_INSTALL_DIR}
DESTINATION ${RESOURCES_DIR}
COMPONENT ${GITTYUP_NAME})
endif()
install(
FILES ${EMOJI} ${MERGETOOLS}
DESTINATION ${RESOURCES_INSTALL_DIR}
DESTINATION ${RESOURCES_DIR}
COMPONENT ${GITTYUP_NAME})
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different ${EMOJI}
@ -241,7 +248,7 @@ else()
foreach(CONFIG_FILE ${CONFIG_FILES})
install(
FILES ${CONFIG_FILE}
DESTINATION ${RESOURCES_INSTALL_DIR}
DESTINATION ${RESOURCES_DIR}
COMPONENT ${GITTYUP_NAME})
file(RELATIVE_PATH CONFIG_FILE_DEST ${CONF_DIR} ${CONFIG_FILE})
@ -252,7 +259,7 @@ else()
install(
FILES ${DICTIONARIES}
DESTINATION ${RESOURCES_INSTALL_DIR}/dictionaries
DESTINATION ${RESOURCES_DIR}/dictionaries
COMPONENT ${GITTYUP_NAME})
foreach(DICTIONARY ${DICTIONARIES})
@ -263,7 +270,7 @@ else()
install(
FILES ${THEMES}
DESTINATION ${RESOURCES_INSTALL_DIR}/themes
DESTINATION ${RESOURCES_DIR}/themes
COMPONENT ${GITTYUP_NAME})
foreach(THEME ${THEMES})
@ -274,7 +281,7 @@ else()
install(
FILES ${LUA_PLUGINS}
DESTINATION ${RESOURCES_INSTALL_DIR}/plugins
DESTINATION ${RESOURCES_DIR}/plugins
COMPONENT ${GITTYUP_NAME})
foreach(LUA_PLUGIN ${LUA_PLUGINS})
@ -286,7 +293,7 @@ else()
foreach(SCINTILLUA_LEXER ${SCINTILLUA_LEXERS})
install(
FILES ${SCINTILLUA_LEXER}
DESTINATION ${RESOURCES_INSTALL_DIR}/lexers
DESTINATION ${RESOURCES_DIR}/lexers
COMPONENT ${GITTYUP_NAME})
get_filename_component(SCINTILLUA_LEXER_DEST ${SCINTILLUA_LEXER} NAME)

View File

@ -354,6 +354,21 @@ QColor CustomTheme::badge(BadgeRole role, BadgeState state) {
case BadgeState::Notification:
stateKey = "notification";
break;
case BadgeState::Modified:
stateKey = "modified";
break;
case BadgeState::Added:
stateKey = "added";
break;
case BadgeState::Deleted:
stateKey = "deleted";
break;
case BadgeState::Untracked:
stateKey = "untracked";
break;
case BadgeState::Renamed:
stateKey = "renamed";
break;
}
QVariantMap badge = mMap.value("badge").toMap();
@ -421,6 +436,8 @@ QColor CustomTheme::commitEditor(CommitEditor color) {
case CommitEditor::LengthWarning:
return commitEditor.value("lengthwarning").value<QColor>();
}
throw std::runtime_error("unreachable; value=" +
std::to_string(static_cast<int>(color)));
}
QColor CustomTheme::diff(Diff color) {
@ -450,6 +467,8 @@ QColor CustomTheme::diff(Diff color) {
case Diff::Error:
return diff.value("error").value<QColor>();
}
throw std::runtime_error("unreachable; value=" +
std::to_string(static_cast<int>(color)));
}
QColor CustomTheme::heatMap(HeatMap color) {
@ -461,6 +480,8 @@ QColor CustomTheme::heatMap(HeatMap color) {
case HeatMap::Cold:
return QColor(heatmap.value("cold").toString());
}
throw std::runtime_error("unreachable; value=" +
std::to_string(static_cast<int>(color)));
}
QColor CustomTheme::remoteComment(Comment color) {
@ -476,6 +497,8 @@ QColor CustomTheme::remoteComment(Comment color) {
case Comment::Timestamp:
return QColor(comment.value("timestamp").toString());
}
throw std::runtime_error("unreachable; value=" +
std::to_string(static_cast<int>(color)));
}
QColor CustomTheme::star() {

View File

@ -101,7 +101,14 @@ QColor Theme::badge(BadgeRole role, BadgeState state) {
case BadgeState::Head:
return QPalette().color(QPalette::HighlightedText);
default:
case BadgeState::Normal: // fall through
case BadgeState::Conflicted: // fall through
case BadgeState::Notification: // fall through
case BadgeState::Modified: // fall through
case BadgeState::Added: // fall through
case BadgeState::Deleted: // fall through
case BadgeState::Untracked: // fall through
case BadgeState::Renamed: // fall through
return QPalette().color(QPalette::WindowText);
}
@ -123,8 +130,20 @@ QColor Theme::badge(BadgeRole role, BadgeState state) {
case BadgeState::Notification:
return mDark ? "#8C2026" : "#FF0000";
case BadgeState::Modified:
return mDark ? "#91973A" : "#FFEEDB";
case BadgeState::Added:
return mDark ? "#394734" : "#DCFFDC";
case BadgeState::Deleted:
return mDark ? "#5E3638" : "#FFDCDC";
case BadgeState::Untracked:
return mDark ? "#2A4944" : "#C7FFF6";
case BadgeState::Renamed:
return mDark ? "#23455E" : "#D2E8FC";
}
}
assert(false);
return QStringLiteral("");
}
QList<QColor> Theme::branchTopologyEdges() {
@ -152,6 +171,8 @@ QColor Theme::commitEditor(CommitEditor color) {
case CommitEditor::LengthWarning:
return Qt::yellow;
}
throw std::runtime_error("unreachable; value=" +
std::to_string(static_cast<int>(color)));
}
QColor Theme::diff(Diff color) {
@ -180,6 +201,8 @@ QColor Theme::diff(Diff color) {
case Diff::Error:
return "#7E494B";
}
throw std::runtime_error("unreachable; value=" +
std::to_string(static_cast<int>(color)));
}
switch (color) {
@ -206,6 +229,8 @@ QColor Theme::diff(Diff color) {
case Diff::Error:
return "#FF0000";
}
throw std::runtime_error("unreachable; value=" +
std::to_string(static_cast<int>(color)));
}
QColor Theme::heatMap(HeatMap color) {
@ -216,6 +241,8 @@ QColor Theme::heatMap(HeatMap color) {
return mDark ? QPalette().color(QPalette::Inactive, QPalette::Highlight)
: QPalette().color(QPalette::Mid);
}
throw std::runtime_error("unreachable; value=" +
std::to_string(static_cast<int>(color)));
}
QColor Theme::remoteComment(Comment color) {
@ -229,6 +256,8 @@ QColor Theme::remoteComment(Comment color) {
case Comment::Timestamp:
return QPalette().color(QPalette::WindowText);
}
throw std::runtime_error("unreachable; value=" +
std::to_string(static_cast<int>(color)));
}
QColor Theme::star() { return QPalette().color(QPalette::Highlight); }

View File

@ -22,7 +22,18 @@ class Theme {
public:
enum class BadgeRole { Foreground, Background };
enum class BadgeState { Normal, Selected, Conflicted, Head, Notification };
enum class BadgeState {
Normal,
Selected,
Conflicted,
Head,
Notification,
Modified,
Added,
Deleted,
Untracked,
Renamed
};
enum class CommitEditor { SpellError, SpellIgnore, LengthWarning };

View File

@ -1,18 +1,15 @@
add_library(conf ConfFile.cpp Settings.cpp Setting.cpp RecentRepositories.cpp
RecentRepository.cpp)
target_link_libraries(conf lua Qt5::Core util)
target_link_libraries(conf lua Qt5::Core util translation)
# SRC_ definitions point to the source directly
target_compile_definitions(
conf PRIVATE SCINTILLUA_LEXERS_DIR="${SCINTILLUA_LEXERS_DIR}")
if(UNIX)
target_compile_definitions(
conf
PRIVATE CONF_DIR="${CMAKE_INSTALL_FULL_DATADIR}/gittyup/Resources"
L10N_DIR="${CMAKE_INSTALL_FULL_DATADIR}/gittyup/Resources/l10n")
else()
target_compile_definitions(conf PRIVATE CONF_DIR="" L10N_DIR="")
endif()
conf
PRIVATE CONF_DIR="${RESOURCES_DIR}"
SRC_CONF_DIR="${CMAKE_SOURCE_DIR}/conf"
L10N_DIR="${L10N_INSTALL_DIR}"
SRC_L10N_DIR="${CMAKE_BINARY_DIR}/l10n"
SCINTILLUA_LEXERS_DIR="${SCINTILLUA_LEXERS_DIR}"
SRC_SCINTILLUA_LEXERS_DIR="${SRC_SCINTILLUA_LEXERS_DIR}")
set_target_properties(conf PROPERTIES AUTOMOC ON)

View File

@ -50,16 +50,20 @@ void RecentRepositories::remove(int index) {
emit repositoryRemoved();
}
void RecentRepositories::add(QString path) {
/*!
* gitpath: path to the git repository, does not neccesarly need to be the
* workdir
*/
void RecentRepositories::add(QString gitpath) {
emit repositoryAboutToBeAdded();
auto end = mRepos.end();
RecentRepository *repo = new RecentRepository(path, this);
RecentRepository *repo = new RecentRepository(gitpath, this);
auto it = std::remove_if(mRepos.begin(), end, [repo](RecentRepository *rhs) {
#ifdef Q_OS_WIN
return repo->path().compare(rhs->path(), Qt::CaseInsensitive) == 0;
return repo->gitpath().compare(rhs->gitpath(), Qt::CaseInsensitive) == 0;
#else
return (repo->path() == rhs->path());
return (repo->gitpath() == rhs->gitpath());
#endif
});
@ -82,7 +86,7 @@ RecentRepositories *RecentRepositories::instance() {
void RecentRepositories::store() {
QStringList paths;
foreach (RecentRepository *repo, mRepos)
paths.append(repo->path());
paths.append(repo->gitpath());
QSettings().setValue(kRecentKey, paths);
@ -125,6 +129,13 @@ void RecentRepositories::load() {
qDeleteAll(mRepos);
mRepos.clear();
/* If two paths have the same name, increase the path segment so that they get
* unique For example: path1/anotherpath/repositoryname
* path2/anotherpath/repositoryname
*
* In this case the complete paths are shown and not only 'repositoryname',
* otherwise they are not distinguishable in the recent repository list:
*/
foreach (const QString &path, paths) {
RecentRepository *repo = new RecentRepository(path, this);
auto functor = [repo](RecentRepository *rhs) {

View File

@ -9,13 +9,13 @@
#include "RecentRepository.h"
RecentRepository::RecentRepository(const QString &path, QObject *parent)
: QObject(parent), mPath(path) {}
RecentRepository::RecentRepository(const QString &gitpath, QObject *parent)
: QObject(parent), mGitPath(gitpath) {}
QString RecentRepository::path() const { return mPath; }
QString RecentRepository::gitpath() const { return mGitPath; }
QString RecentRepository::name() const {
return mPath.section('/', -mSections);
return mGitPath.section('/', -mSections, -1);
}
void RecentRepository::increment() { ++mSections; }

View File

@ -16,15 +16,15 @@ class RecentRepository : public QObject {
Q_OBJECT
public:
RecentRepository(const QString &path, QObject *parent = nullptr);
RecentRepository(const QString &gitpath, QObject *parent = nullptr);
QString path() const;
QString gitpath() const;
QString name() const;
private:
void increment();
QString mPath;
QString mGitPath;
int mSections = 1;
friend class RecentRepositories;

View File

@ -26,7 +26,7 @@ void Setting::initialize(QMap<Id, QString> &keys) {
keys[Id::TerminalName] = "terminal/name";
keys[Id::TerminalPath] = "terminal/path";
keys[Id::DontTranslate] = "translation/disable";
keys[Id::StoreCredentials] = "credential/store";
keys[Id::Language] = "translation/language";
keys[Id::AllowSingleInstanceOnly] = "singleInstance";
keys[Id::CheckForUpdatesAutomatically] = "update/check";
keys[Id::InstallUpdatesAutomatically] = "update/download";
@ -35,8 +35,13 @@ void Setting::initialize(QMap<Id, QString> &keys) {
keys[Id::SshKeyFilePath] = "ssh/keyFilePath";
keys[Id::CommitMergeImmediately] = "merge/commit";
keys[Id::ShowCommitsInCompactMode] = "commit/compact";
keys[Id::ShowCommitsAuthor] = "commit/author";
keys[Id::ShowCommitsDate] = "commit/date";
keys[Id::ShowCommitsId] = "commit/id";
keys[Id::ShowChangedFilesAsList] = "doubletreeview/listview";
keys[Id::ShowChangedFilesInSingleView] = "doubletreeview/single";
keys[Id::ShowChangedFilesMultiColumn] = "doubletreeview/listviewmulticolumn";
keys[Id::HideUntracked] = "untracked.hide";
}
void Prompt::initialize(QMap<Kind, QString> &keys) {

View File

@ -50,7 +50,6 @@ public:
TerminalName,
TerminalPath,
DontTranslate,
StoreCredentials,
AllowSingleInstanceOnly,
CheckForUpdatesAutomatically,
InstallUpdatesAutomatically,
@ -59,8 +58,14 @@ public:
SshKeyFilePath,
CommitMergeImmediately,
ShowCommitsInCompactMode,
ShowCommitsAuthor,
ShowCommitsDate,
ShowCommitsId,
ShowChangedFilesAsList,
ShowChangedFilesMultiColumn, // For the list only
ShowChangedFilesInSingleView,
HideUntracked,
Language,
};
Q_ENUM(Id)

View File

@ -9,13 +9,14 @@
#include "Settings.h"
#include "ConfFile.h"
#include "Debug.h"
#include "qtsupport.h"
#include "languages.h"
#include <QCoreApplication>
#include <QDir>
#include <QSettings>
#include <QStandardPaths>
#include <QDebug>
#ifdef Q_OS_WIN
#define CS Qt::CaseInsensitive
#else
@ -24,18 +25,20 @@
namespace {
const QString kIgnoreWsKey = "diff/whitespace/ignore";
const QString kLastPathKey = "lastpath";
const QString kIgnoreWsKey("diff/whitespace/ignore");
const QString kLastPathKey("lastpath");
const QString kTranslation("translation");
const QString kTranslationLanguage("language");
// Look up variant at key relative to root.
QVariant lookup(const QVariantMap &root, const QString &key) {
QStringList list = key.split("/", QString::SkipEmptyParts);
QStringList list(key.split("/", Qt::SkipEmptyParts));
if (list.isEmpty())
return root;
QVariantMap map = root;
QVariantMap map(root);
while (map.contains(list.first())) {
QVariant result = map.value(list.takeFirst());
QVariant result(map.value(list.takeFirst()));
if (list.isEmpty())
return result;
map = result.toMap();
@ -45,24 +48,16 @@ QVariant lookup(const QVariantMap &root, const QString &key) {
}
QString promptKey(Prompt::Kind kind) { return Prompt::key(kind); }
QDir rootDir() {
QDir dir(QCoreApplication::applicationDirPath());
#ifdef Q_OS_MAC
dir.cdUp(); // Contents
#endif
qDebug() << "Root dir: " << dir;
return dir;
}
} // namespace
Settings::Settings(QObject *parent) : QObject(parent) {
foreach (const QFileInfo &file, confDir().entryInfoList(QStringList("*.lua")))
mDefaults[file.baseName()] = ConfFile(file.absoluteFilePath()).parse();
mDefaults[kLastPathKey] = QDir::homePath();
QVariantMap map;
map[kTranslationLanguage] = QVariant(Languages::system);
mDefaults[kTranslation] = map;
mDefaults[kTranslation].toMap()[kTranslationLanguage] = Languages::system;
mCurrentMap = mDefaults;
}
@ -76,7 +71,7 @@ QVariant Settings::value(const QString &key,
const QVariant &defaultValue) const {
QSettings settings;
settings.beginGroup(group());
QVariant result = settings.value(key, defaultValue);
QVariant result(settings.value(key, defaultValue));
settings.endGroup();
return result;
}
@ -120,13 +115,13 @@ QString Settings::lexer(const QString &filename) {
return "null";
QFileInfo info(filename);
QString name = info.fileName();
QString suffix = info.suffix().toLower();
QString name(info.fileName());
QString suffix(info.suffix().toLower());
// Try all patterns first.
QVariantMap lexers = mDefaults.value("lexers").toMap();
QVariantMap lexers(mDefaults.value("lexers").toMap());
foreach (const QString &key, lexers.keys()) {
QVariantMap map = lexers.value(key).toMap();
QVariantMap map(lexers.value(key).toMap());
if (map.contains("patterns")) {
foreach (QString pattern, map.value("patterns").toString().split(",")) {
QRegExp regExp(pattern, CS, QRegExp::Wildcard);
@ -138,7 +133,7 @@ QString Settings::lexer(const QString &filename) {
// Try to match by extension.
foreach (const QString &key, lexers.keys()) {
QVariantMap map = lexers.value(key).toMap();
QVariantMap map(lexers.value(key).toMap());
if (map.contains("extensions")) {
foreach (QString ext, map.value("extensions").toString().split(",")) {
if (suffix == ext)
@ -151,8 +146,8 @@ QString Settings::lexer(const QString &filename) {
}
QString Settings::kind(const QString &filename) {
QString key = lexer(filename);
QVariantMap lexers = mDefaults.value("lexers").toMap();
QString key(lexer(filename));
QVariantMap lexers(mDefaults.value("lexers").toMap());
return lexers.value(key).toMap().value("name").toString();
}
@ -184,6 +179,8 @@ QString Settings::promptDescription(Prompt::Kind kind) const {
case Prompt::Kind::LargeFiles:
return tr("Prompt to stage large files");
}
throw std::runtime_error("unreachable; value=" +
std::to_string(static_cast<int>(kind)));
}
void Settings::setHotkey(const QString &action, const QString &hotkey) {
@ -208,6 +205,13 @@ void Settings::setLastPath(const QString &lastPath) {
setValue(kLastPathKey, lastPath);
}
QDir Settings::rootDir() {
QDir dir(QCoreApplication::applicationDirPath());
dir.cdUp();
return dir;
}
QDir Settings::appDir() {
QDir dir(QCoreApplication::applicationDirPath());
@ -223,48 +227,62 @@ QDir Settings::appDir() {
QDir Settings::docDir() { return confDir(); }
QDir Settings::confDir() {
QDir dir = rootDir();
if (!dir.cd("Resources"))
dir = QDir(CONF_DIR);
qDebug() << "Conf dir: " << dir;
#if !defined(NDEBUG)
QDir dir(SRC_CONF_DIR);
#else
QDir dir(rootDir());
if (!dir.cd("Resources")) {
if (!dir.cd(CONF_DIR))
dir.setPath(SRC_CONF_DIR);
}
#endif
return dir;
}
QDir Settings::l10nDir() {
QDir dir = confDir();
if (!dir.cd("l10n"))
dir = QDir(L10N_DIR);
qDebug() << "l10n dir: " << dir;
#if !defined(NDEBUG)
QDir dir(QDir(SRC_L10N_DIR));
#else
QDir dir(confDir());
if (!dir.cd("l10n")) {
dir = rootDir();
if (!dir.cd(L10N_DIR))
dir.setPath(SRC_L10N_DIR);
}
#endif
return dir;
}
QDir Settings::dictionariesDir() {
QDir dir = confDir();
QDir dir(confDir());
dir.cd("dictionaries");
qDebug() << "Dictionaries dir: " << dir;
return dir;
}
QDir Settings::lexerDir() {
QDir dir = confDir();
if (!dir.cd("lexers"))
dir = QDir(SCINTILLUA_LEXERS_DIR);
qDebug() << "Lexers dir: " << dir;
#if !defined(NDEBUG)
QDir dir(SRC_SCINTILLUA_LEXERS_DIR);
#else
QDir dir(confDir());
if (!dir.cd("lexers")) {
dir = rootDir();
if (!dir.cd(SCINTILLUA_LEXERS_DIR))
dir.setPath(SRC_SCINTILLUA_LEXERS_DIR);
}
#endif
return dir;
}
QDir Settings::themesDir() {
QDir dir = confDir();
QDir dir(confDir());
dir.cd("themes");
qDebug() << "Theme dir: " << dir;
return dir;
}
QDir Settings::pluginsDir() {
QDir dir = confDir();
QDir dir(confDir());
dir.cd("plugins");
qDebug() << "Plugins dir: " << dir;
return dir;
}
@ -273,8 +291,8 @@ QDir Settings::userDir() {
}
QDir Settings::tempDir() {
QString name = QCoreApplication::applicationName();
QDir dir = QDir::temp();
QString name(QCoreApplication::applicationName());
QDir dir(QDir::temp());
dir.mkpath(name);
dir.cd(name);
return dir;

View File

@ -45,6 +45,7 @@ public:
void setLastPath(const QString &lastPath);
// settings directories
static QDir rootDir();
static QDir appDir();
static QDir docDir();
static QDir confDir();

View File

@ -1,10 +1,5 @@
if(WIN32)
set(CREDENTIAL_IMPL_FILE WinCred.cpp)
endif()
add_library(cred Cache.cpp Store.cpp CredentialHelper.cpp GitCredential.cpp)
add_library(cred Cache.cpp CredentialHelper.cpp GitCredential.cpp
${CREDENTIAL_IMPL_FILE})
target_link_libraries(cred conf Qt5::Core)
target_link_libraries(cred conf git Qt5::Core)
set_target_properties(cred PROPERTIES AUTOMOC ON)

View File

@ -10,8 +10,10 @@
#include "CredentialHelper.h"
#include "Cache.h"
#include "GitCredential.h"
#include "WinCred.h"
#include "qtsupport.h"
#include "Store.h"
#include "conf/Settings.h"
#include "git/Config.h"
#include <QLibrary>
#include <QPointer>
#include <QSettings>
@ -22,27 +24,30 @@ namespace {
const QString kLogKey = "credential/log";
const QString cacheStoreName = "cache";
const QString storeStoreName = "store";
const QString osxKeyChainStoreName = "osxkeychain";
const QString winCredStoreName = "wincred";
const QString libSecretStoreName = "libsecret";
const QString gnomeKeyringStoreName = "gnome-keyring";
} // namespace
CredentialHelper *CredentialHelper::instance() {
static QPointer<CredentialHelper> instance;
if (!instance) {
if (Settings::instance()->value(Setting::Id::StoreCredentials).toBool()) {
#if defined(Q_OS_MAC)
instance = new GitCredential("osxkeychain");
#elif defined(Q_OS_WIN)
// The git wincred helper fails for some users.
instance = new WinCred;
#else
QLibrary lib("secret-1", 0);
if (lib.load()) {
instance = new GitCredential("libsecret");
git::Config config = git::Config::global();
auto helperName = config.value<QString>("credential.helper");
if (isHelperValid(helperName)) {
if (helperName == cacheStoreName) {
instance = new Cache;
} else if (helperName == storeStoreName) {
auto path =
QString::fromLocal8Bit(qgetenv("HOME") + "/.git-credentials");
instance = new Store(path);
} else {
QLibrary lib("gnome-keyring", 0);
if (lib.load())
instance = new GitCredential("gnome-keyring");
instance = new GitCredential(helperName);
}
#endif
}
if (!instance)
@ -52,6 +57,31 @@ CredentialHelper *CredentialHelper::instance() {
return instance;
}
bool CredentialHelper::isHelperValid(const QString &name) {
return !name.isEmpty();
}
QStringList CredentialHelper::getAvailableHelperNames() {
QStringList list;
list.append(cacheStoreName);
list.append(storeStoreName);
#if defined(Q_OS_MAC)
list.append(osxKeyChainStoreName);
#elif defined(Q_OS_WIN)
list.append(winCredStoreName);
#else
QLibrary lib("secret-1", 0);
if (lib.load()) {
list.append(libSecretStoreName);
}
QLibrary lib2(gnomeKeyringStoreName, 0);
if (lib2.load()) {
list.append(gnomeKeyringStoreName);
}
#endif
return list;
}
bool CredentialHelper::isLoggingEnabled() {
return QSettings().value(kLogKey).toBool();
}
@ -69,5 +99,5 @@ void CredentialHelper::log(const QString &text) {
return;
QString time = QTime::currentTime().toString(Qt::ISODateWithMs);
QTextStream(&file) << time << " - " << text << endl;
QTextStream(&file) << time << " - " << text << Qt::endl;
}

View File

@ -33,6 +33,9 @@ public:
static bool isLoggingEnabled();
static void setLoggingEnabled(bool enabled);
static QStringList getAvailableHelperNames();
static bool isHelperValid(const QString &name);
protected:
static void log(const QString &text);
};

View File

@ -8,6 +8,8 @@
//
#include "GitCredential.h"
#include "qtsupport.h"
#include <QStandardPaths>
#include <QCoreApplication>
#include <QDir>
#include <QProcess>
@ -44,11 +46,11 @@ bool GitCredential::get(const QString &url, QString &username,
return false;
QTextStream out(&process);
out << "protocol=" << protocol(url) << endl;
out << "host=" << host(url) << endl;
out << "protocol=" << protocol(url) << Qt::endl;
out << "host=" << host(url) << Qt::endl;
if (!username.isEmpty())
out << "username=" << username << endl;
out << endl;
out << "username=" << username << Qt::endl;
out << Qt::endl;
process.closeWriteChannel();
process.waitForFinished();
@ -79,11 +81,11 @@ bool GitCredential::store(const QString &url, const QString &username,
return false;
QTextStream out(&process);
out << "protocol=" << protocol(url) << endl;
out << "host=" << host(url) << endl;
out << "username=" << username << endl;
out << "password=" << password << endl;
out << endl;
out << "protocol=" << protocol(url) << Qt::endl;
out << "host=" << host(url) << Qt::endl;
out << "username=" << username << Qt::endl;
out << "password=" << password << Qt::endl;
out << Qt::endl;
process.closeWriteChannel();
process.waitForFinished();
@ -92,6 +94,46 @@ bool GitCredential::store(const QString &url, const QString &username,
}
QString GitCredential::command() const {
QDir dir(QCoreApplication::applicationDirPath());
return dir.filePath(QString("git-credential-%1").arg(mName));
QString name = QString("git-credential-%1").arg(mName);
QDir appDir = QCoreApplication::applicationDirPath();
appDir.cd("credential-helpers");
// Prefer credential helpers directly installed into Gittyup's app dir
QString candidate =
QStandardPaths::findExecutable(name, QStringList(appDir.path()));
if (!candidate.isEmpty()) {
return candidate;
}
candidate = QStandardPaths::findExecutable(name);
if (!candidate.isEmpty()) {
return candidate;
}
#ifdef Q_OS_WIN
// Look for GIT CLI installation path
QString gitPath = QStandardPaths::findExecutable("git");
if (!gitPath.isEmpty()) {
QDir gitDir = QFileInfo(gitPath).dir();
if (gitDir.dirName() == "cmd" || gitDir.dirName() == "bin") {
gitDir.cdUp();
#ifdef Q_OS_WIN64
gitDir.cd("mingw64");
#else
gitDir.cd("mingw32");
#endif
gitDir.cd("bin");
candidate =
QStandardPaths::findExecutable(name, QStringList(gitDir.path()));
if (!candidate.isEmpty()) {
return candidate;
}
}
}
#endif
return name;
}

111
src/cred/Store.cpp Normal file
View File

@ -0,0 +1,111 @@
//
// Copyright (c) 2022, Gittyup Community
//
// This software is licensed under the MIT License. The LICENSE.md file
// describes the conditions under which this software may be distributed.
//
// Author: Hessamoddin Hediehloo(H-4ND-H)
//
#include "Store.h"
#include <QUrl>
#include <QFile>
#include <QTextStream>
namespace {
QString host(const QString &url) {
QString host = QUrl(url).host();
if (!host.isEmpty())
return host;
// Extract hostname from SSH URL.
int end = url.indexOf(':');
int begin = url.indexOf('@') + 1;
return url.mid(begin, end - begin);
}
QString protocol(const QString &url) {
QString scheme = QUrl(url).scheme();
return !scheme.isEmpty() ? scheme : "ssh";
}
} // namespace
Store::Store(const QString &path) { mPath = path; }
QMap<QString, QMap<QString, QMap<QString, QString>>> Store::readCredFile() {
QMap<QString, QMap<QString, QMap<QString, QString>>> store;
QFile file(mPath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return store;
while (!file.atEnd()) {
auto line = file.readLine();
auto urlStr = QUrl::fromPercentEncoding(line);
auto urlObj = QUrl::fromUserInput(urlStr);
store[urlObj.scheme()][urlObj.host()][urlObj.userName()] =
urlObj.password();
}
file.close();
return store;
}
bool Store::extractUserPass(const QMap<QString, QString> &map,
QString &username, QString &password) {
if (map.isEmpty())
return false;
if (username.isEmpty())
username = map.keys().first();
if (!map.contains(username))
return false;
password = map.value(username);
return !username.isEmpty() && !password.isEmpty();
}
bool Store::get(const QString &url, QString &username, QString &password) {
auto store = readCredFile();
const QMap<QString, QString> &map = store[protocol(url)][host(url)];
return extractUserPass(map, username, password);
}
bool Store::store(const QString &url, const QString &username,
const QString &password) {
auto store = readCredFile();
store[protocol(url)][host(url)][username] = password;
QFile file(mPath);
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
return false;
foreach (const auto &protocolKey, store.keys()) {
auto protocol = store[protocolKey];
foreach (const auto &hostKey, protocol.keys()) {
auto host = protocol[hostKey];
foreach (const auto &usernameKey, host.keys()) {
QUrl temp;
temp.setScheme(protocolKey);
temp.setHost(hostKey);
temp.setUserName(usernameKey);
temp.setPassword(host[usernameKey]);
auto encoded = QUrl::toPercentEncoding(temp.toString(), "@:/");
QTextStream fout(&file);
fout << encoded << "\n";
}
}
}
file.close();
return true;
}
QString Store::command() const { return ""; }

34
src/cred/Store.h Normal file
View File

@ -0,0 +1,34 @@
//
// Copyright (c) 2022, Gittyup Community
//
// This software is licensed under the MIT License. The LICENSE.md file
// describes the conditions under which this software may be distributed.
//
// Author: Hessamoddin Hediehloo(H-4ND-H)
//
#ifndef STORE_H
#define STORE_H
#include "CredentialHelper.h"
#include <QMap>
class Store : public CredentialHelper {
public:
Store(const QString &path);
bool get(const QString &url, QString &username, QString &password) override;
bool store(const QString &url, const QString &username,
const QString &password) override;
private:
QString command() const;
QMap<QString, QMap<QString, QMap<QString, QString>>> readCredFile();
bool extractUserPass(const QMap<QString, QString> &map, QString &username,
QString &password);
QString mPath;
};
#endif

View File

@ -1,128 +0,0 @@
//
// Copyright (c) 2018, Scientific Toolworks, Inc.
//
// This software is licensed under the MIT License. The LICENSE.md file
// describes the conditions under which this software may be distributed.
//
// Author: Jason Haslam
//
#include "WinCred.h"
#include <QStringList>
#include <QUrl>
#include <windows.h>
#include <wincred.h>
// Attempt to be compatible with the git wincred helper by matching
// the target name and encoding the password in UTF-16.
namespace {
const QString kNameFmt = "%1@%2";
const QString kTargetFmt = "git:https://%1";
QString buildTarget(const QString &host, const QString &name) {
return kTargetFmt.arg(name.isEmpty() ? host : kNameFmt.arg(name, host));
}
QString host(const QString &url) {
QString host = QUrl(url).host();
if (!host.isEmpty())
return host;
// Extract hostname from SSH URL.
int end = url.indexOf(':');
int begin = url.indexOf('@') + 1;
return url.mid(begin, end - begin);
}
} // namespace
WinCred::WinCred() {}
bool WinCred::get(const QString &url, QString &username, QString &password) {
log(QString("get: %1 %2").arg(url, username));
PCREDENTIAL cred;
QString target = buildTarget(host(url), username);
if (!CredRead(target.toUtf8(), CRED_TYPE_GENERIC, 0, &cred)) {
switch (DWORD error = GetLastError()) {
case ERROR_NOT_FOUND:
log(QString("get: credential not found for '%1'").arg(target));
break;
case ERROR_NO_SUCH_LOGON_SESSION:
log("get: no such logon session");
break;
case ERROR_INVALID_FLAGS:
log("get: invalid flags");
break;
default:
log(QString("get: unknown error '%1'").arg(error));
break;
}
return false;
}
username = cred->UserName;
int size = cred->CredentialBlobSize / sizeof(ushort);
password = QString::fromUtf16((ushort *)cred->CredentialBlob, size);
CredFree(cred);
return true;
}
bool WinCred::store(const QString &url, const QString &username,
const QString &password) {
log(QString("store: %1 %2").arg(url, username));
bool result = false;
QByteArray name = username.toUtf8();
QStringList names = {QString(), username};
foreach (const QString &tmp, names) {
QByteArray target = buildTarget(host(url), tmp).toUtf8();
CREDENTIAL cred;
cred.Flags = 0;
cred.Type = CRED_TYPE_GENERIC;
cred.TargetName = target.data();
cred.Comment = "Written by GitAhead";
cred.CredentialBlobSize = password.length() * sizeof(ushort);
cred.CredentialBlob = (LPBYTE)password.utf16();
cred.Persist = CRED_PERSIST_LOCAL_MACHINE;
cred.AttributeCount = 0;
cred.Attributes = nullptr;
cred.TargetAlias = nullptr;
cred.UserName = name.data();
if (CredWrite(&cred, 0)) {
result = true;
} else {
switch (DWORD error = GetLastError()) {
case ERROR_NO_SUCH_LOGON_SESSION:
log("store: no such logon session");
break;
case ERROR_INVALID_PARAMETER:
log("store: invalid parameter");
case ERROR_INVALID_FLAGS:
log("store: invalid flags");
break;
case ERROR_BAD_USERNAME:
log(QString("store: bad username '%1'").arg(username));
break;
default:
log(QString("store: unknown error '%1'").arg(error));
break;
}
}
}
return result;
}

View File

@ -1,25 +0,0 @@
//
// Copyright (c) 2018, Scientific Toolworks, Inc.
//
// This software is licensed under the MIT License. The LICENSE.md file
// describes the conditions under which this software may be distributed.
//
// Author: Jason Haslam
//
#ifndef WINCRED_H
#define WINCRED_H
#include "CredentialHelper.h"
class WinCred : public CredentialHelper {
public:
WinCred();
bool get(const QString &url, QString &username, QString &password) override;
bool store(const QString &url, const QString &username,
const QString &password) override;
};
#endif

View File

@ -42,7 +42,9 @@ const QString kSubtitleFmt = "<h4 style='margin-top: 0px; color: gray'>%2</h4>";
const QString kTextFmt =
"<p style='white-space: nowrap'><b style='font-size: large'>%1 v%2</b> "
"- %3 - %4<br>Copyright © 2021-2022 Gittyup contributors"
"- %3 - %4<br>Copyright © 2021-" +
QString::number(CURR_YEAR) +
" Gittyup contributors"
"<br>Copyright © 2016-2020 Scientific Toolworks, Inc. and "
"contributors</p><p> If you have a question that might benefit the "
"community, consider asking it on <a href='%5'>Stack Overflow</a> by "

View File

@ -172,7 +172,8 @@ AmendDialog::AmendDialog(const git::Signature &author,
l->addWidget(m_committerInfo, Row::Committer, 0, 1, 2);
auto *lMessage = new QLabel(tr("Commit Message:"), this);
m_commitMessage = new QTextEdit(commitMessage, this);
m_commitMessage = new QTextEdit(this);
m_commitMessage->setPlainText(commitMessage);
m_commitMessage->setObjectName("Textlabel Commit Message");
l->addWidget(lMessage, Row::CommitMessageLabel, 0);
l->addWidget(m_commitMessage, Row::CommitMessage, 0, 1, 2);

View File

@ -24,6 +24,7 @@ add_library(
RebaseConflictDialog.cpp
RemoteDialog.cpp
RemoteTableModel.cpp
RenameBranchDialog.cpp
SettingsDialog.cpp
StartDialog.cpp
SubmoduleDelegate.cpp

View File

@ -100,17 +100,17 @@ public:
// Connect signals after initializing fields.
connect(mName, &QLineEdit::textChanged, this, [this](const QString &text) {
git::Config config = mRepo.config();
git::Config config = mRepo.gitConfig();
config.setValue("user.name", text);
});
connect(mEmail, &QLineEdit::textChanged, this, [this](const QString &text) {
git::Config config = mRepo.config();
git::Config config = mRepo.gitConfig();
config.setValue("user.email", text);
});
connect(mFetch, &QCheckBox::toggled, view, [this, view](bool checked) {
git::Config config = mRepo.config();
git::Config config = mRepo.appConfig();
config.setValue("autofetch.enable", checked);
view->startFetchTimer();
});
@ -118,28 +118,28 @@ public:
using Signal = void (QSpinBox::*)(int);
auto signal = static_cast<Signal>(&QSpinBox::valueChanged);
connect(mFetchMinutes, signal, this, [this](int value) {
git::Config config = mRepo.config();
git::Config config = mRepo.appConfig();
config.setValue("autofetch.minutes", value);
});
connect(mPushCommit, &QCheckBox::toggled, this, [this](bool checked) {
git::Config config = mRepo.config();
git::Config config = mRepo.appConfig();
config.setValue("autopush.enable", checked);
});
connect(mPullUpdate, &QCheckBox::toggled, this, [this](bool checked) {
git::Config config = mRepo.config();
git::Config config = mRepo.appConfig();
config.setValue("autoupdate.enable", checked);
});
connect(mAutoPrune, &QCheckBox::toggled, this, [this](bool checked) {
git::Config config = mRepo.config();
git::Config config = mRepo.appConfig();
config.setValue("autoprune.enable", checked);
});
}
void init() {
git::Config config = mRepo.config();
git::Config config = mRepo.gitConfig();
mName->setText(config.value<QString>("user.name"));
mEmail->setText(config.value<QString>("user.email"));
@ -158,6 +158,7 @@ public:
git::Config app = mRepo.appConfig();
mFetch->setChecked(app.value<bool>("autofetch.enable", fetch));
mFetchMinutes->setValue(app.value<int>("autofetch.minutes", minutes));
mFetchMinutes->setEnabled(mFetch->isChecked());
mPushCommit->setChecked(app.value<bool>("autopush.enable", push));
mPullUpdate->setChecked(app.value<bool>("autoupdate.enable", update));
mAutoPrune->setChecked(app.value<bool>("autoprune.enable", prune));
@ -605,7 +606,7 @@ public:
QLineEdit *urlLineEdit =
new QLineEdit(map.value("Endpoint").section(" ", 0, 0));
connect(urlLineEdit, &QLineEdit::textChanged, [repo](const QString &text) {
git::Config config = repo.config();
git::Config config = repo.gitConfig();
config.setValue("lfs.url", text);
});
@ -614,7 +615,7 @@ public:
pruneOffsetDays->setValue(map.value("PruneOffsetDays").toInt());
auto signal = QOverload<int>::of(&QSpinBox::valueChanged);
connect(pruneOffsetDays, signal, [repo](int value) {
git::Config config = repo.config();
git::Config config = repo.gitConfig();
config.setValue("lfs.pruneoffsetdays", value);
});
QHBoxLayout *pruneOffsetLayout = new QHBoxLayout;
@ -628,7 +629,7 @@ public:
bool fetchRecentEnabled = map.value("FetchRecentAlways").contains("true");
fetchRecentAlways->setChecked(fetchRecentEnabled);
connect(fetchRecentAlways, &QCheckBox::toggled, [repo](bool checked) {
git::Config config = repo.config();
git::Config config = repo.gitConfig();
config.setValue("lfs.fetchrecentalways", checked);
});
@ -637,7 +638,7 @@ public:
fetchRecentRefsDays->setValue(map.value("FetchRecentRefsDays").toInt());
fetchRecentRefsDays->setEnabled(fetchRecentEnabled);
connect(fetchRecentRefsDays, signal, [repo](int value) {
git::Config config = repo.config();
git::Config config = repo.gitConfig();
config.setValue("lfs.fetchrecentrefsdays", value);
});
connect(fetchRecentAlways, &QCheckBox::toggled, this,
@ -655,7 +656,7 @@ public:
map.value("FetchRecentCommitsDays").toInt());
fetchRecentCommitsDays->setEnabled(fetchRecentEnabled);
connect(fetchRecentCommitsDays, signal, [repo](int value) {
git::Config config = repo.config();
git::Config config = repo.gitConfig();
config.setValue("lfs.fetchrecentcommitsdays", value);
});
connect(fetchRecentAlways, &QCheckBox::toggled, this,

View File

@ -47,7 +47,7 @@ DeleteBranchDialog::DeleteBranchDialog(const git::Branch &branch,
QString name = upstream.name().section('/', 1);
QString key = kBranchMergeFmt.arg(branch.name());
QString upstreamName = repo.config().value<QString>(key);
QString upstreamName = repo.gitConfig().value<QString>(key);
git::Remote remote = upstream.remote();
QString remoteName = remote.name();

View File

@ -24,7 +24,8 @@
#include <QTextCodec>
DiffPanel::DiffPanel(const git::Repository &repo, QWidget *parent)
: QWidget(parent), mConfig(repo ? repo.config() : git::Config::global()) {
: QWidget(parent),
mConfig(repo ? repo.gitConfig() : git::Config::global()) {
// diff context
QSpinBox *context = new QSpinBox(this);
QLabel *contextLabel = new QLabel(tr("lines"), this);

View File

@ -85,7 +85,7 @@ QVBoxLayout *ExternalToolsDialog::createUserDefinedLayout(const QString &type) {
table->resizeColumnsToContents();
});
connect(footer, &Footer::minusClicked, [this, table, model] {
connect(footer, &Footer::minusClicked, [table, model] {
QModelIndexList indexes = table->selectionModel()->selectedRows(0);
foreach (const QModelIndex &index, indexes)
model->remove(index.data(Qt::DisplayRole).toString());

View File

@ -90,7 +90,7 @@ RemoteDialog::RemoteDialog(Kind kind, RepoView *parent) : QDialog(parent) {
if (ref.isValid()) {
QString key = QString("branch.%1.merge").arg(ref.name());
git::Config config =
RepoView::parentView(this)->repo().config();
RepoView::parentView(this)->repo().gitConfig();
value = config.value<QString>(key);
}
mRemoteRef->setText(value);

View File

@ -0,0 +1,58 @@
// This software is licensed under the MIT License. The LICENSE.md file
// describes the conditions under which this software may be distributed.
//
// Author: Michael WERLE
//
#include "RenameBranchDialog.h"
#include "git/Branch.h"
#include "ui/ExpandButton.h"
#include "ui/ReferenceList.h"
#include "ui/RepoView.h"
#include <QApplication>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QFormLayout>
#include <QLineEdit>
#include <QPushButton>
#include <QVBoxLayout>
RenameBranchDialog::RenameBranchDialog(const git::Repository &repo,
const git::Branch &branch,
QWidget *parent)
: QDialog(parent) {
Q_ASSERT(branch.isValid() && branch.isLocalBranch());
setAttribute(Qt::WA_DeleteOnClose);
mName = new QLineEdit(branch.name(), this);
mName->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
mName->setMinimumWidth(QFontMetrics(mName->font()).averageCharWidth() * 40);
QFormLayout *form = new QFormLayout;
form->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
form->addRow(tr("Name:"), mName);
QDialogButtonBox *buttons = new QDialogButtonBox(this);
buttons->addButton(QDialogButtonBox::Cancel);
QPushButton *rename =
buttons->addButton(tr("Rename Branch"), QDialogButtonBox::AcceptRole);
rename->setEnabled(false);
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addLayout(form);
layout->addWidget(buttons);
// Update button when name text changes.
connect(mName, &QLineEdit::textChanged, [repo, rename](const QString &text) {
rename->setEnabled(git::Branch::isNameValid(text) &&
!repo.lookupBranch(text, GIT_BRANCH_LOCAL).isValid());
});
// Perform the rename when the button is clicked
connect(rename, &QPushButton::clicked,
[this, branch] { git::Branch(branch).rename(mName->text()); });
}
QString RenameBranchDialog::name() const { return mName->text(); }

View File

@ -0,0 +1,33 @@
// This software is licensed under the MIT License. The LICENSE.md file
// describes the conditions under which this software may be distributed.
//
// Author: Michael WERLE
//
#ifndef RENAMEBRANCHDIALOG_H
#define RENAMEBRANCHDIALOG_H
#include "git/Branch.h"
#include <QDialog>
class QLineEdit;
namespace git {
class Reference;
class Repository;
} // namespace git
class RenameBranchDialog : public QDialog {
Q_OBJECT
public:
RenameBranchDialog(const git::Repository &repo, const git::Branch &branch,
QWidget *parent = nullptr);
QString name() const;
private:
QLineEdit *mName;
};
#endif

View File

@ -25,6 +25,7 @@
#include "ui/MainWindow.h"
#include "ui/MenuBar.h"
#include "ui/RepoView.h"
#include "languages.h"
#include "update/Updater.h"
#include <QAction>
#include <QApplication>
@ -105,17 +106,23 @@ public:
new QCheckBox(tr("Update submodules after pull and clone"), this);
mAutoPrune = new QCheckBox(tr("Prune when fetching"), this);
mNoTranslation = new QCheckBox(tr("No translation"), this);
mLanguages = new QComboBox(this);
QMapIterator<const char *, const char *> i(Languages::languages);
while (i.hasNext()) {
i.next();
mLanguages->addItem(tr(i.key()), QVariant(i.value()));
}
mStoreCredentials =
new QCheckBox(tr("Store credentials in secure storage"), this);
mAvailableStores = new QComboBox(this);
QLabel *privacy = new QLabel(tr("<a href='view'>View privacy policy</a>"));
connect(privacy, &QLabel::linkActivated,
[] { AboutDialog::openSharedInstance(AboutDialog::Privacy); });
mSingleInstance =
new QCheckBox(tr("Only allow a single running instance"), this);
QFormLayout *form = new QFormLayout;
form->addRow(tr("User name:"), mName);
form->addRow(tr("User email:"), mEmail);
@ -124,11 +131,18 @@ public:
form->addRow(QString(), mPullUpdate);
form->addRow(QString(), mAutoPrune);
form->addRow(tr("Language:"), mNoTranslation);
form->addRow(tr("Language:"), mLanguages);
form->addRow(tr("Credentials:"), mStoreCredentials);
form->addRow(tr("Credential store type:"), mAvailableStores);
form->addRow(QString(), privacy);
mSingleInstance =
new QCheckBox(tr("Only allow a single running instance"), this);
#if defined(Q_OS_LINUX) || defined(Q_OS_WIN)
form->addRow(tr("Single instance:"), mSingleInstance);
#elif defined(Q_OS_MACX)
mSingleInstance->setVisible(false);
#endif
QVBoxLayout *layout = new QVBoxLayout(this);
@ -179,11 +193,33 @@ public:
Settings::instance()->setValue(Setting::Id::DontTranslate, checked);
});
connect(mStoreCredentials, &QCheckBox::toggled, [](bool checked) {
Settings::instance()->setValue(Setting::Id::StoreCredentials, checked);
connect(mLanguages, QOverload<int>::of(&QComboBox::currentIndexChanged),
[this]() {
const auto &language = mLanguages->currentData().toString();
Settings::instance()->setValue(Setting::Id::Language, language);
});
connect(mStoreCredentials, &QCheckBox::toggled, [this](bool checked) {
git::Config config = git::Config::global();
mAvailableStores->setEnabled(checked);
if (checked) {
auto store = mAvailableStores->currentText();
config.setValue("credential.helper", store);
} else {
config.remove("credential.helper");
}
delete CredentialHelper::instance();
});
connect(mAvailableStores, &QComboBox::currentTextChanged,
[](const QString &text) {
git::Config config = git::Config::global();
config.setValue("credential.helper", text);
delete CredentialHelper::instance();
});
connect(mSingleInstance, &QCheckBox::toggled, [](bool checked) {
Settings::instance()->setValue(Setting::Id::AllowSingleInstanceOnly,
checked);
@ -196,6 +232,7 @@ public:
mEmail->setText(config.value<QString>("user.email"));
Settings *settings = Settings::instance();
mFetch->setChecked(
settings->value(Setting::Id::FetchAutomatically).toBool());
mFetchMinutes->setValue(
@ -211,8 +248,25 @@ public:
mNoTranslation->setChecked(
settings->value(Setting::Id::DontTranslate).toBool());
mStoreCredentials->setChecked(
settings->value(Setting::Id::StoreCredentials).toBool());
const auto &l = settings->value(Setting::Id::Language).toString();
for (int i = 0; i < mLanguages->count(); i++) {
if (mLanguages->itemData(i).toString() == l) {
mLanguages->setCurrentIndex(i);
break;
}
}
auto currentHelper = config.value<QString>("credential.helper");
auto checked = CredentialHelper::isHelperValid(currentHelper);
mStoreCredentials->setChecked(checked);
auto availableHelpers = CredentialHelper::getAvailableHelperNames();
foreach (auto helper, availableHelpers) {
mAvailableStores->addItem(helper);
}
mAvailableStores->setEditable(true);
mAvailableStores->setCurrentText(currentHelper);
mSingleInstance->setChecked(
settings->value(Setting::Id::AllowSingleInstanceOnly).toBool());
@ -228,7 +282,9 @@ private:
QCheckBox *mPullUpdate;
QCheckBox *mAutoPrune;
QCheckBox *mNoTranslation;
QComboBox *mLanguages;
QCheckBox *mStoreCredentials;
QComboBox *mAvailableStores;
QCheckBox *mSingleInstance;
};

View File

@ -117,10 +117,10 @@ public:
RecentRepository *repo = repos->repository(index.row());
switch (role) {
case Qt::DisplayRole:
return mShowFullPath ? repo->path() : repo->name();
return mShowFullPath ? repo->gitpath() : repo->name();
case Qt::UserRole:
return repo->path();
return repo->gitpath();
}
return QVariant();
@ -698,7 +698,7 @@ void StartDialog::updateButtons() {
open->setEnabled(false);
} else {
Account *account = parent.data(AccountRole).value<Account *>();
if (Repository *repo = index.data(RepositoryRole).value<Repository *>()) {
if (nullptr != index.data(RepositoryRole).value<Repository *>()) {
if (account->repositoryPath(index.row()).isEmpty())
clone = true;
} else {

View File

@ -62,7 +62,7 @@ public:
case Theme::Dark:
Settings::instance()->setValue(Setting::Id::ColorTheme, "Dark");
break;
default:
case Theme::Default:
Settings::instance()->setValue(Setting::Id::ColorTheme, "Default");
break;
}

View File

@ -413,7 +413,7 @@ public:
virtual ~LexerLPeg() {}
/** Destroys the lexer object. */
void SCI_METHOD Release() {
void SCI_METHOD Release() override {
lua_getfield(L, LUA_REGISTRYINDEX, "sci_lexers");
lua_pushlightuserdata(L, reinterpret_cast<void *>(this));
lua_pushnil(L), lua_settable(L, -3), lua_pop(L, 1); // sci_lexers
@ -429,7 +429,7 @@ public:
* @param buffer The document interface.
*/
void SCI_METHOD Lex(Sci_PositionU startPos, Sci_Position lengthDoc,
int initStyle, IDocument *buffer) {
int initStyle, IDocument *buffer) override {
lua_pushlightuserdata(L, reinterpret_cast<void *>(&props));
lua_setfield(L, LUA_REGISTRYINDEX, "sci_props");
lua_pushlightuserdata(L, reinterpret_cast<void *>(buffer));
@ -515,7 +515,7 @@ public:
* @param buffer The document interface.
*/
void SCI_METHOD Fold(Sci_PositionU startPos, Sci_Position lengthDoc,
int initStyle, IDocument *buffer) {
int initStyle, IDocument *buffer) override {
lua_pushlightuserdata(L, reinterpret_cast<void *>(&props));
lua_setfield(L, LUA_REGISTRYINDEX, "sci_props");
lua_pushlightuserdata(L, reinterpret_cast<void *>(buffer));
@ -552,7 +552,8 @@ public:
* @param key The string keyword.
* @param val The string value.
*/
Sci_Position SCI_METHOD PropertySet(const char *key, const char *value) {
Sci_Position SCI_METHOD PropertySet(const char *key,
const char *value) override {
const char *val = *value ? value : " ";
props.Set(key, val, strlen(key), strlen(val)); // ensure property is cleared
return -1; // no need to re-lex
@ -566,7 +567,7 @@ public:
* @param arg The argument.
* @return void *data
*/
void *SCI_METHOD PrivateCall(int code, void *arg) {
void *SCI_METHOD PrivateCall(int code, void *arg) override {
switch (code) {
case SCI_GETDIRECTFUNCTION:
fn = reinterpret_cast<SciFnDirect>(arg);
@ -603,12 +604,12 @@ public:
}
}
int SCI_METHOD Version() const { return 0; }
const char *SCI_METHOD PropertyNames() { return ""; }
int SCI_METHOD PropertyType(const char *) { return 0; }
const char *SCI_METHOD DescribeProperty(const char *) { return ""; }
const char *SCI_METHOD DescribeWordListSets() { return ""; }
Sci_Position SCI_METHOD WordListSet(int, const char *) { return -1; }
int SCI_METHOD Version() const override { return 0; }
const char *SCI_METHOD PropertyNames() override { return ""; }
int SCI_METHOD PropertyType(const char *) override { return 0; }
const char *SCI_METHOD DescribeProperty(const char *) override { return ""; }
const char *SCI_METHOD DescribeWordListSets() override { return ""; }
Sci_Position SCI_METHOD WordListSet(int, const char *) override { return -1; }
int SCI_METHOD LineEndTypesSupported() noexcept override {
return SC_LINE_END_TYPE_UNICODE;
@ -636,7 +637,7 @@ public:
int SCI_METHOD GetIdentifier() override { return 0; }
const char *SCI_METHOD PropertyGet(const char *key) { return ""; }
const char *SCI_METHOD PropertyGet(const char *key) override { return ""; }
/** Constructs a new instance of the lexer. */
static ILexer5 *LexerFactoryLPeg() { return new LexerLPeg(); }

View File

@ -128,7 +128,7 @@ void SurfaceImpl::Polygon(Point *pts, size_t npts, ColourDesired fore,
PenColour(fore);
QVarLengthArray<QPoint, 8> qpts(npts);
for (int i = 0; i < npts; i++)
for (size_t i = 0; i < npts; i++)
qpts[i] = QPoint(pts[i].x, pts[i].y);
QPainter *painter = GetPainter();
@ -306,7 +306,7 @@ void SurfaceImpl::MeasureWidths(Font &font, std::string_view s,
QTextLine tl = tlay.createLine();
tlay.endLayout();
int i = 0;
size_t i = 0;
int ui = 0;
int fit = su.size();
const unsigned char *us = reinterpret_cast<const unsigned char *>(s.data());
@ -468,9 +468,6 @@ void Window::SetCursor(Cursor curs) {
case cursorText:
shape = Qt::IBeamCursor;
break;
case cursorArrow:
shape = Qt::ArrowCursor;
break;
case cursorUp:
shape = Qt::UpArrowCursor;
break;

View File

@ -185,7 +185,7 @@ void ScintillaQt::paintEvent(QPaintEvent *event) {
}
void ScintillaQt::wheelEvent(QWheelEvent *event) {
if (event->orientation() == Qt::Horizontal) {
if (event->angleDelta().x() != 0) {
if (horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOff)
event->ignore();
else
@ -194,7 +194,7 @@ void ScintillaQt::wheelEvent(QWheelEvent *event) {
if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
// Zoom! We play with the font sizes in the styles.
// Number of steps/line is ignored, we just care if sizing up or down
if (event->delta() > 0) {
if (event->angleDelta().y() > 0) {
KeyCommand(SCI_ZOOMIN);
} else {
KeyCommand(SCI_ZOOMOUT);
@ -327,8 +327,7 @@ void ScintillaQt::keyPressEvent(QKeyEvent *event) {
QString text = event->text();
if (input && !text.isEmpty() && text[0].isPrint()) {
QByteArray utext = text.toUtf8();
AddCharUTF(utext.data(), utext.size());
InsertCharacter(text.toStdString(), CharacterSource::directInput);
} else {
event->ignore();
}
@ -355,7 +354,7 @@ static int modifierTranslated(int sciModifier) {
void ScintillaQt::mousePressEvent(QMouseEvent *event) {
Point pos = PointFromQPoint(event->pos());
if (event->button() == Qt::MidButton &&
if (event->button() == Qt::MiddleButton &&
QApplication::clipboard()->supportsSelection()) {
SelectionPosition selPos =
SPositionFromLocation(pos, false, false, UserVirtualSpace());
@ -440,6 +439,7 @@ void ScintillaQt::dragEnterEvent(QDragEnterEvent *event) {
}
void ScintillaQt::dragLeaveEvent(QDragLeaveEvent *event) {
Q_UNUSED(event);
SetDragPosition(SelectionPosition(Sci::invalidPosition));
}
@ -520,9 +520,8 @@ void ScintillaQt::inputMethodEvent(QInputMethodEvent *event) {
const unsigned int ucWidth = commitStr.at(i).isHighSurrogate() ? 2 : 1;
const QString oneCharUTF16 = commitStr.mid(i, ucWidth);
const QByteArray oneChar = oneCharUTF16.toUtf8();
const int oneCharLen = oneChar.length();
AddCharUTF(oneChar.data(), oneCharLen);
InsertCharacter(oneChar.toStdString(), CharacterSource::directInput);
i += ucWidth;
}
@ -568,7 +567,7 @@ void ScintillaQt::inputMethodEvent(QInputMethodEvent *event) {
indicator = SC_INDICATOR_CONVERTED;
break;
default:
case QTextCharFormat::DashDotDotLine:
indicator = SC_INDICATOR_UNKNOWN;
}
@ -605,7 +604,7 @@ void ScintillaQt::inputMethodEvent(QInputMethodEvent *event) {
numBytes += oneCharLen;
imeCharPos[i + 1] = numBytes;
AddCharUTF(oneChar.data(), oneCharLen);
InsertCharacter(oneChar.toStdString(), CharacterSource::directInput);
#ifdef Q_OS_LINUX
// Segment marked with imeCaretPos is for target input.
@ -642,7 +641,7 @@ QVariant ScintillaQt::inputMethodQuery(Qt::InputMethodQuery query) const {
int line = send(SCI_LINEFROMPOSITION, pos);
switch (query) {
case Qt::ImMicroFocus: {
case Qt::ImCursorRectangle: {
int startPos = (preeditPos >= 0) ? preeditPos : pos;
Point pt =
const_cast<ScintillaQt *>(this)->LocationFromPosition(startPos);
@ -691,9 +690,23 @@ QVariant ScintillaQt::inputMethodQuery(Qt::InputMethodQuery query) const {
return buffer.constData();
}
default:
return QVariant();
case Qt::ImEnabled: // fall through
case Qt::ImAnchorPosition: // fall through
case Qt::ImHints: // fall through
case Qt::ImMaximumTextLength: // fall through
case Qt::ImPreferredLanguage: // fall through
case Qt::ImAbsolutePosition: // fall through
case Qt::ImTextBeforeCursor: // fall through
case Qt::ImTextAfterCursor: // fall through
case Qt::ImEnterKeyType: // fall through
case Qt::ImAnchorRectangle: // fall through
case Qt::ImInputItemClipRectangle: // fall through
case Qt::ImPlatformData: // fall through
case Qt::ImQueryInput: // fall through
case Qt::ImQueryAll: // fall through
break;
}
return QVariant();
}
void ScintillaQt::PasteFromMode(QClipboard::Mode clipboardMode) {

View File

@ -43,7 +43,9 @@ QPixmap stagedUnstagedIcon(const bool &checked, const QColor &background,
QRect(QPoint(0, 0), QSize(fontHeight - 2, fontHeight - 2)));
}
#if defined(FLATPAK)
const float textHeightFactorCheckBoxSize = 2.0;
#endif
} // namespace

View File

@ -105,7 +105,7 @@ public:
QList<Diagnostic> diagnostics(int line);
void addDiagnostic(int line, const Diagnostic &diag);
sptr_t WndProc(unsigned int message, uptr_t wParam, sptr_t lParam);
sptr_t WndProc(unsigned int message, uptr_t wParam, sptr_t lParam) override;
// Make wheel event public.
// FIXME: This should be an event filter?
@ -154,7 +154,7 @@ private:
int diagnosticMarker(int line);
void loadMarkerIcon(Marker marker, const QIcon &icon);
void loadMarkerPixmap(Marker marker, const QPixmap &pixmap);
void AddToPopUp(const char *label, int cmd = 0, bool enabled = true);
void AddToPopUp(const char *label, int cmd = 0, bool enabled = true) override;
void ContextMenu(Scintilla::Point pt);
QString mPath;

View File

@ -129,11 +129,11 @@ void Branch::remove(bool force) {
}
bool Branch::isRebase() const {
return repo().config().value<bool>(kRebaseFmt.arg(name()));
return repo().gitConfig().value<bool>(kRebaseFmt.arg(name()));
}
void Branch::setRebase(bool checked) {
repo().config().setValue(kRebaseFmt.arg(name()), checked);
repo().gitConfig().setValue(kRebaseFmt.arg(name()), checked);
}
AnnotatedCommit Branch::annotatedCommitFromFetchHead() const {

View File

@ -251,7 +251,8 @@ bool Commit::amend(const Signature &author, const Signature &committer,
return !error;
}
bool Commit::reset(git_reset_t type, const QStringList &paths) const {
bool Commit::reset(git_reset_t type, const QStringList &paths,
bool triggerReferenceUpdated) const {
QVector<char *> rawPaths;
QVector<QByteArray> storage;
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
@ -271,7 +272,9 @@ bool Commit::reset(git_reset_t type, const QStringList &paths) const {
Repository repo = this->repo();
int state = repo.state();
int error = git_reset(repo, d.data(), type, &opts);
emit repo.notifier()->referenceUpdated(repo.head());
if (triggerReferenceUpdated)
emit repo.notifier()->referenceUpdated(repo.head(),
type == git_reset_t::GIT_RESET_HARD);
if (repo.state() != state) {
Patch::clearConflictResolutions(repo);
emit repo.notifier()->stateChanged();

View File

@ -70,7 +70,8 @@ public:
// Reset HEAD to this commit.
bool reset(git_reset_t type = GIT_RESET_MIXED,
const QStringList &paths = QStringList()) const;
const QStringList &paths = QStringList(),
bool triggerReferenceUpdated = true) const;
// favorite commits
bool isStarred() const;

View File

@ -9,9 +9,9 @@
#include "Diff.h"
#include "Patch.h"
#include "Debug.h"
#include "git2/patch.h"
#include <algorithm>
#include <QDebug>
bool containsPath(QString &str, QString &occurence, Qt::CaseSensitivity cs) {
if (str.contains(occurence, cs)) {
@ -108,13 +108,13 @@ QByteArray Diff::print() {
QByteArray diff;
for (auto file : data.files) {
for (auto hunk : file.hunks) {
diff.append(hunk.header);
diff.append(hunk.header.toUtf8());
for (auto line : hunk.lines)
diff.append(line);
diff.append(line.toUtf8());
}
}
qDebug() << QString(diff);
Debug(QString(diff));
return diff;
}
@ -173,6 +173,9 @@ void Diff::findSimilar(bool untracked) {
if (untracked)
opts.flags = GIT_DIFF_FIND_FOR_UNTRACKED;
if (!isValid())
return;
git_diff_find_similar(d->diff, &opts);
d->resetMap();
}
@ -195,6 +198,8 @@ void Diff::sort(SortRole role, Qt::SortOrder order) {
: (rhsStatus < lhsStatus);
}
}
throw std::runtime_error("unreachable; value=" +
std::to_string(static_cast<int>(role)));
});
}

View File

@ -363,7 +363,10 @@ Id Index::headId(const QString &path, uint32_t *mode) const {
return Id();
git_tree_entry *entry = nullptr;
if (git_tree_entry_bypath(&entry, commit.tree(), path.toUtf8()))
const auto tree = commit.tree();
if (!tree.isValid())
return Id(); // broken repository?
if (git_tree_entry_bypath(&entry, tree, path.toUtf8()))
return Id();
if (mode)

View File

@ -195,7 +195,7 @@ QByteArray Patch::header(int hidx) const {
const git_diff_hunk *hunk = nullptr;
int result = git_patch_get_hunk(&hunk, nullptr, d.data(), hidx);
return !result ? hunk->header : QByteArray();
return (GIT_OK == result) ? hunk->header : QByteArray();
}
const git_diff_hunk *Patch::header_struct(int hidx) const {
@ -204,7 +204,7 @@ const git_diff_hunk *Patch::header_struct(int hidx) const {
const git_diff_hunk *hunk = nullptr;
int result = git_patch_get_hunk(&hunk, nullptr, d.data(), hidx);
return hunk;
return (GIT_OK == result) ? hunk : nullptr;
}
int Patch::lineCount(int hidx) const {
@ -235,7 +235,7 @@ char Patch::lineOrigin(int hidx, int ln) const {
const git_diff_line *line = nullptr;
int result = git_patch_get_line_in_hunk(&line, d.data(), hidx, ln);
return !result ? line->origin : GIT_DIFF_LINE_CONTEXT;
return (GIT_OK == result) ? line->origin : GIT_DIFF_LINE_CONTEXT;
}
int Patch::lineNumber(int hidx, int ln, Diff::File file) const {
@ -256,7 +256,7 @@ git_off_t Patch::contentOffset(int hidx) const {
const git_diff_line *line = nullptr;
int result = git_patch_get_line_in_hunk(&line, d.data(), hidx,
0); // TODO: line index 0?
return result == 0 ? line->content_offset : 0;
return (GIT_OK == result) ? line->content_offset : 0;
}
QByteArray Patch::lineContent(int hidx, int ln) const {
@ -265,7 +265,8 @@ QByteArray Patch::lineContent(int hidx, int ln) const {
const git_diff_line *line = nullptr;
int result = git_patch_get_line_in_hunk(&line, d.data(), hidx, ln);
return !result ? QByteArray(line->content, line->content_len) : QByteArray();
return (GIT_OK == result) ? QByteArray(line->content, line->content_len)
: QByteArray();
}
Patch::ConflictResolution Patch::conflictResolution(int hidx) {
@ -435,6 +436,7 @@ void Patch::apply(QList<QList<QByteArray>> &image, int hidx, int start_line,
break;
default:
// no-op
break;
}
}

View File

@ -36,8 +36,8 @@ const git_rebase_operation *Rebase::operation(size_t index) {
}
bool Rebase::hasNext() const {
int index = currentIndex();
int count = git_rebase_operation_entrycount(d.data());
size_t index = currentIndex();
size_t count = git_rebase_operation_entrycount(d.data());
return (count > 0 && (index == GIT_REBASE_NO_OPERATION || index < count - 1));
}

View File

@ -11,6 +11,7 @@
#include "Branch.h"
#include "Config.h"
#include "Id.h"
#include "qtsupport.h"
#include "TagRef.h"
#include "git2/buffer.h"
#include "git2/clone.h"
@ -423,7 +424,7 @@ int Remote::Callbacks::certificate(git_cert *cert, int valid, const char *host,
return 0;
Repository repo = reinterpret_cast<Remote::Callbacks *>(payload)->repo();
Config config = repo.isValid() ? repo.config() : Config::global();
Config config = repo.isValid() ? repo.gitConfig() : Config::global();
if (!config.value<bool>("http.sslVerify", true))
return 0;
@ -444,9 +445,10 @@ int Remote::Callbacks::transfer(const git_indexer_progress *stats,
case Resolve:
return cbs->resolve(stats->total_deltas, stats->indexed_deltas) ? 0 : -1;
default:
case Update:
return 0;
}
return 0;
}
int Remote::Callbacks::update(const char *name, const git_oid *a,
@ -596,7 +598,7 @@ Result Remote::push(Callbacks *callbacks, const Reference &src,
refspec += ":" + dst;
} else {
QString key = QString("branch.%1.merge").arg(src.name());
QString upstream = repo.config().value<QString>(key);
QString upstream = repo.gitConfig().value<QString>(key);
if (!upstream.isEmpty())
refspec += ":" + upstream;
}
@ -674,7 +676,7 @@ void Remote::log(const QString &text) {
return;
QString time = QTime::currentTime().toString(Qt::ISODateWithMs);
QTextStream(&file) << time << " - " << text << endl;
QTextStream(&file) << time << " - " << text << Qt::endl;
}
} // namespace git

View File

@ -18,6 +18,7 @@
#include "FilterList.h"
#include "Index.h"
#include "Patch.h"
#include "qtsupport.h"
#include "Rebase.h"
#include "Reference.h"
#include "Remote.h"
@ -151,7 +152,14 @@ Repository::Repository(git_repository *repo) : d(registerRepository(repo)) {}
Repository::operator git_repository *() const { return d->repo; }
QDir Repository::dir() const { return QDir(git_repository_path(d->repo)); }
QDir Repository::dir(bool includeGitFolder) const {
QDir dir(git_repository_path(d->repo));
if (!includeGitFolder) {
assert(dir.dirName() == ".git");
assert(dir.cdUp());
}
return dir;
}
QDir Repository::workdir() const {
return isBare() ? dir() : QDir(git_repository_workdir(d->repo));
@ -173,12 +181,16 @@ QString Repository::message() const {
return QString::fromUtf8(buf.ptr, buf.size);
}
Config Repository::config() const {
// Config file used for git specific configs
// config file in <Repository>/.git/config
Config Repository::gitConfig() const {
git_config *config = nullptr;
git_repository_config(&config, d->repo);
return Config(config);
}
// Config file used for app specific configs
// config file in <Repository>/.git/gittyup/config
Config Repository::appConfig() const {
Config config = Config::appGlobal();
QString path = appDir().filePath(kConfigFile);
@ -305,8 +317,11 @@ Diff Repository::status(const Index &index, Diff::Callbacks *callbacks,
Diff Repository::diffTreeToIndex(const Tree &tree, const Index &index,
bool ignoreWhitespace) const {
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_RECURSE_UNTRACKED_DIRS |
GIT_DIFF_INCLUDE_TYPECHANGE;
opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE;
if (!appConfig().value<bool>("untracked.hide", false))
opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
if (ignoreWhitespace)
opts.flags |= GIT_DIFF_IGNORE_WHITESPACE;
@ -319,8 +334,11 @@ Diff Repository::diffIndexToWorkdir(const Index &index,
Diff::Callbacks *callbacks,
bool ignoreWhitespace) const {
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
opts.flags |= (GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_RECURSE_UNTRACKED_DIRS |
GIT_DIFF_DISABLE_MMAP | GIT_DIFF_INCLUDE_TYPECHANGE);
opts.flags |= (GIT_DIFF_DISABLE_MMAP | GIT_DIFF_INCLUDE_TYPECHANGE);
if (!appConfig().value<bool>("untracked.hide", false))
opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
if (ignoreWhitespace)
opts.flags |= GIT_DIFF_IGNORE_WHITESPACE;
@ -477,7 +495,7 @@ QStringList Repository::existingTags() const {
QStringList list;
for (int i = 0; i < array.count; i++) {
for (size_t i = 0; i < array.count; i++) {
list.append(array.strings[i]);
}
@ -618,6 +636,7 @@ Commit Repository::commit(const Signature &author, const Signature &committer,
break;
default:
// no-op
break;
}
@ -734,7 +753,7 @@ QList<Remote> Repository::remotes() const {
return QList<Remote>();
QList<Remote> remotes;
for (int i = 0; i < names.count; ++i) {
for (size_t i = 0; i < names.count; ++i) {
if (Remote remote = lookupRemote(names.strings[i]))
remotes.append(remote);
}
@ -889,8 +908,10 @@ void Repository::rebase(const AnnotatedCommit &mergeHead,
git_rebase_init(&r, d->repo, nullptr, mergeHead, nullptr, &opts);
auto rebase = git::Rebase(d->repo, r, overrideUser, overrideEmail);
if (!rebase.isValid())
if (!rebase.isValid()) {
emit d->notifier->rebaseInitError();
return;
}
// start rebasing
rebaseContinue(QStringLiteral(""));
@ -920,7 +941,6 @@ void Repository::rebaseContinue(const QString &commitMessage) {
}
}
// Loop over rebase operations.
int count = r.count();
while (r.hasNext()) {
git::Commit before = r.next();
if (!before.isValid()) {
@ -1010,7 +1030,7 @@ void Repository::cleanupState() {
}
QTextCodec *Repository::codec() const {
QString encoding = config().value<QString>("gui.encoding");
QString encoding = gitConfig().value<QString>("gui.encoding");
QTextCodec *codec = QTextCodec::codecForName(encoding.toUtf8());
return codec ? codec : QTextCodec::codecForLocale();
}
@ -1040,7 +1060,7 @@ QStringList Repository::lfsEnvironment() {
Repository::LfsTracking Repository::lfsTracked() {
QString output = lfsExecute({"track"});
QStringList lines = output.split('\n', QString::SkipEmptyParts);
QStringList lines = output.split('\n', Qt::SkipEmptyParts);
if (!lines.isEmpty())
lines.removeFirst();

View File

@ -71,7 +71,7 @@ public:
RepositoryNotifier *notifier() const { return d->notifier; }
QDir dir() const;
QDir dir(bool includeGitFolder = true) const;
QDir workdir() const;
QDir appDir() const;
@ -81,7 +81,7 @@ public:
QString message() const;
// config
Config config() const;
Config gitConfig() const;
Config appConfig() const;
// bare
@ -316,7 +316,7 @@ signals:
void referenceAdded(const Reference &ref);
void referenceAboutToBeRemoved(const Reference &ref);
void referenceRemoved(const QString &name);
void referenceUpdated(const Reference &ref);
void referenceUpdated(const Reference &ref, bool restoreSelection = false);
void remoteAboutToBeAdded(const QString &name);
void remoteAdded(const Remote &remote);

View File

@ -26,7 +26,7 @@ Submodule::operator git_submodule *() const { return d.data(); }
bool Submodule::isInitialized() const {
Repository repo(git_submodule_owner(d.data()));
QString key = QString("submodule.%1.url").arg(name());
return !repo.config().value<QString>(key).isEmpty();
return !repo.gitConfig().value<QString>(key).isEmpty();
}
void Submodule::initialize() const { git_submodule_init(d.data(), false); }
@ -34,7 +34,7 @@ void Submodule::initialize() const { git_submodule_init(d.data(), false); }
void Submodule::deinitialize() const {
// Remove git config entry.
Repository repo(git_submodule_owner(d.data()));
Config config = repo.config();
Config config = repo.gitConfig();
QString regex = QString("submodule\\.%1\\..*").arg(name());
Config::Iterator it = config.glob(regex);
while (Config::Entry entry = it.next())

View File

@ -30,8 +30,8 @@ const QString kThemeIconFmt = ":/%1_%2.png";
} // namespace
Account::Account(const QString &username)
: mMgr(new QNetworkAccessManager()), mUsername(username),
mError(new AccountError(this)), mProgress(new AccountProgress(this)) {
: mUsername(username), mError(new AccountError(this)),
mProgress(new AccountProgress(this)), mMgr(new QNetworkAccessManager()) {
QObject::connect(
mMgr, &QNetworkAccessManager::sslErrors,
[this](QNetworkReply *reply, const QList<QSslError> &errors) {
@ -204,6 +204,8 @@ QString Account::helpText(Kind kind) {
case Beanstalk:
return QString();
}
throw std::runtime_error("unreachable; value=" +
std::to_string(static_cast<int>(kind)));
}
QString Account::defaultUrl(Kind kind) {
@ -219,6 +221,8 @@ QString Account::defaultUrl(Kind kind) {
case GitLab:
return GitLab::defaultUrl();
}
throw std::runtime_error("unreachable; value=" +
std::to_string(static_cast<int>(kind)));
}
Account::Kind Account::kindFromString(const QString &kind, bool *ok) {
@ -258,6 +262,8 @@ QString Account::kindToString(Kind kind) {
case GitLab:
return "gitlab";
}
throw std::runtime_error("unreachable; value=" +
std::to_string(static_cast<int>(kind)));
}
void Account::startProgress() {

View File

@ -35,16 +35,8 @@ else()
set_target_properties(indexer PROPERTIES INSTALL_RPATH "$ORIGIN")
endif()
if(FLATPAK)
# Install application.
install(
TARGETS indexer
DESTINATION ./bin # otherwise the executable will not be found by flatpak
COMPONENT ${GITTYUP_NAME})
else()
install(
TARGETS indexer
DESTINATION .
COMPONENT ${GITTYUP_NAME})
endif()
install(
TARGETS indexer
DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT ${GITTYUP_NAME})
endif()

View File

@ -93,7 +93,19 @@ Lexer::Lexeme GenericLexer::next() {
}
break;
default:
case Comment: // fall through
case Keyword: // fall through
case Operator: // fall through
case Error: // fall through
case Preprocessor: // fall through
case Constant: // fall through
case Variable: // fall through
case Function: // fall through
case Class: // fall through
case Type: // fall through
case Label: // fall through
case Regex: // fall through
case Embedded: // fall through
Q_ASSERT(false);
}
}

View File

@ -182,7 +182,7 @@ bool Index::write(PostingMap map) {
quint32 postCount = readVInt(postIn);
bool end = (newIt == newEnd || newIt.key() != it->key);
postings.reserve(postCount + (!end ? newIt.value().size() : 0));
for (int i = 0; i < postCount; ++i) {
for (quint32 i = 0; i < postCount; ++i) {
quint32 proxPos;
Posting posting;
posting.id = readVInt(postIn);
@ -243,7 +243,7 @@ QList<git::Commit> Index::commits(const QString &filter) const {
// Sort by commit date.
QList<git::Commit> commits = query->commits(this);
std::sort(commits.begin(), commits.end(),
[this](const git::Commit &lhs, const git::Commit &rhs) {
[](const git::Commit &lhs, const git::Commit &rhs) {
return (lhs.committer().date() > rhs.committer().date());
});
@ -288,7 +288,7 @@ QList<Index::Posting> Index::postings(const Term &term, bool positional) const {
// Read list.
QList<Posting> postings;
quint32 postCount = readVInt(in);
for (int i = 0; i < postCount; ++i) {
for (quint32 i = 0; i < postCount; ++i) {
quint32 proxPos;
Posting posting;
posting.id = readVInt(in);
@ -331,7 +331,7 @@ QList<Index::Posting> Index::postings(const Predicate &pred,
// Read list.
quint32 postCount = readVInt(in);
for (int i = 0; i < postCount; ++i) {
for (quint32 i = 0; i < postCount; ++i) {
quint32 proxPos;
Posting posting;
posting.id = readVInt(in);
@ -373,7 +373,7 @@ QMap<Index::Field, QStringList> Index::fieldMap(const QString &prefix) const {
// Read list.
QString name = it->key;
quint32 postCount = readVInt(in);
for (int i = 0; i < postCount; ++i) {
for (quint32 i = 0; i < postCount; ++i) {
quint8 field;
quint32 proxPos;
readVInt(in); // Discard id.
@ -443,6 +443,8 @@ QByteArray Index::fieldName(Index::Field field) {
case Index::Pathspec:
return "pathspec";
}
throw std::runtime_error("unreachable; value=" +
std::to_string(static_cast<int>(field)));
}
QDir Index::indexDir(const git::Repository &repo) {
@ -506,7 +508,7 @@ void Index::readPositions(QDataStream &in, QVector<quint32> &positions) {
quint32 prev = 0;
quint32 count = readVInt(in);
positions.reserve(count);
for (int i = 0; i < count; ++i) {
for (quint32 i = 0; i < count; ++i) {
// Convert to absolute from delta.
quint32 position = prev + readVInt(in);
positions.append(position);

View File

@ -182,16 +182,24 @@ public:
QList<git::Commit> rhs = mRhs->commits(index);
QList<git::Commit> commits = mLhs->commits(index);
if (mKind == And) {
// Remove commits that don't match the right hand side.
// Remove commits that don't match the right hand side.
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
QSet<git::Commit> set(rhs.begin(), rhs.end());
#else
QSet<git::Commit> set = QSet<git::Commit>::fromList(rhs);
#endif
QMutableListIterator<git::Commit> it(commits);
while (it.hasNext()) {
if (!set.contains(it.next()))
it.remove();
}
} else {
// Add commits that aren't already in the result set.
// Add commits that aren't already in the result set.
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
QSet<git::Commit> set(commits.begin(), commits.end());
#else
QSet<git::Commit> set = QSet<git::Commit>::fromList(commits);
#endif
foreach (const git::Commit &commit, rhs) {
if (!set.contains(commit))
commits.append(commit);

View File

@ -10,6 +10,7 @@
#include "Index.h"
#include "GenericLexer.h"
#include "LPegLexer.h"
#include "qtsupport.h"
#include "conf/Settings.h"
#include "git/Config.h"
#include "git/Index.h"
@ -105,7 +106,7 @@ void log(QFile *out, const QString &text) {
return;
QString time = QTime::currentTime().toString(Qt::ISODateWithMs);
QTextStream(out) << time << " - " << text << endl;
QTextStream(out) << time << " - " << text << Qt::endl;
}
void log(QFile *out, const QString &fmt, const git::Id &id) {
@ -148,7 +149,22 @@ void index(const Lexer::Lexeme &lexeme, Intermediate::FieldMap &fields,
case Lexer::Comment:
field |= Index::Comment;
break;
default:
case Lexer::Nothing: // fall through
case Lexer::Whitespace: // fall through
case Lexer::Number: // fall through
case Lexer::Keyword: // fall through
case Lexer::Identifier: // fall through
case Lexer::Operator: // fall through
case Lexer::Error: // fall through
case Lexer::Preprocessor: // fall through
case Lexer::Constant: // fall through
case Lexer::Variable: // fall through
case Lexer::Function: // fall through
case Lexer::Class: // fall through
case Lexer::Type: // fall through
case Lexer::Label: // fall through
case Lexer::Regex: // fall through
case Lexer::Embedded: // fall through
break;
}
@ -172,7 +188,13 @@ void index(const Lexer::Lexeme &lexeme, Intermediate::FieldMap &fields,
break;
// Ignore everything else.
default:
case Lexer::Nothing: // fall through
case Lexer::Whitespace: // fall through
case Lexer::Number: // fall through
case Lexer::Operator: // fall through
case Lexer::Error: // fall through
case Lexer::Regex: // fall through
case Lexer::Embedded: // fall through
break;
}
}
@ -331,7 +353,7 @@ private:
QFile *mOut;
int mContextLines = 3;
int mTermLimit = 1000000;
quint32 mTermLimit = 1000000;
};
class Reduce {
@ -405,7 +427,12 @@ public:
int count = 0;
QList<git::Commit> commits;
git::Commit commit = mWalker.next();
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
QSet<git::Id> ids(mIndex.ids().begin(), mIndex.ids().end());
#else
QSet<git::Id> ids = QSet<git::Id>::fromList(mIndex.ids());
#endif
while (commit.isValid() && count < 8192) {
// Don't index merge commits.
if (!commit.isMerge() && !ids.contains(commit.id())) {
@ -440,7 +467,7 @@ public:
// Write to disk.
log(mOut, "start write");
if (mIndex.write(mWatcher.result()) && mNotify)
QTextStream(stdout) << "write" << endl;
QTextStream(stdout) << "write" << Qt::endl;
log(mOut, "end write");
// Restart.
@ -450,10 +477,14 @@ public:
bool nativeEventFilter(const QByteArray &type, void *message,
long *result) override {
Q_UNUSED(result);
#ifdef Q_OS_WIN
MSG *msg = static_cast<MSG *>(message);
if (msg->message == WM_CLOSE)
cancel();
#else
Q_UNUSED(type);
Q_UNUSED(message);
#endif
return false;

View File

@ -9,6 +9,7 @@
#include "GenericLexer.h"
#include "LPegLexer.h"
#include "qtsupport.h"
#include "conf/Settings.h"
#include <QCoreApplication>
#include <QFile>
@ -29,7 +30,7 @@ void print(QTextStream &out, const Lexer::Lexeme &lexeme, int indent = 0) {
out << lexeme.text << " - " << lexeme.token;
if (lexeme.token < kStyleNames.length())
out << " (" << kStyleNames.at(lexeme.token) << ")";
out << endl;
out << Qt::endl;
}
void print(QTextStream &out, Lexer *lexer, int indent = 0) {
@ -65,7 +66,13 @@ void print(QTextStream &out, Lexer *lexer, int indent = 0) {
break;
// Ignore everything else.
default:
case Lexer::Whitespace: // fall through
case Lexer::Nothing: // fall through
case Lexer::Number: // fall through
case Lexer::Operator: // fall through
case Lexer::Error: // fall through
case Lexer::Regex: // fall through
case Lexer::Embedded:
break;
}
}
@ -102,7 +109,7 @@ int main(int argc, char *argv[]) {
// Lex buffer.
Lexer *lexer = lexers.value(name);
if (lexer->lex(buffer)) {
out << name << " - " << arg << ":" << endl;
out << name << " - " << arg << ":" << Qt::endl;
print(out, lexer);
}
}

View File

@ -79,7 +79,9 @@ void LogDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
QRect rect = decorationRect(option, index);
if (static_cast<QMetaType::Type>(variant.type()) == QMetaType::QChar) {
Badge::paint(painter, {Badge::Label(variant.toChar())}, rect, &opt);
Badge::paint(painter,
{Badge::Label(Badge::Label::Type::Log, variant.toChar())},
rect, &opt);
} else if (variant.canConvert<int>()) {
int progress = variant.toInt();
ProgressIndicator::paint(painter, rect, "#808080", progress, opt.widget);
@ -126,7 +128,9 @@ void LogDelegate::initStyleOption(QStyleOptionViewItem *option,
QStyledItemDelegate::initStyleOption(option, index);
QVariant variant = index.data(Qt::DecorationRole);
if (static_cast<QMetaType::Type>(variant.type()) == QMetaType::QChar) {
option->decorationSize = Badge::size(option->font) - ADJUSTMENT_SIZE;
option->decorationSize =
Badge::size(option->font, Badge::Label(Badge::Label::Type::Log)) -
ADJUSTMENT_SIZE;
} else if (variant.canConvert<int>()) {
option->decorationSize = ProgressIndicator::size();
}

View File

@ -9,6 +9,7 @@
#include "LogModel.h"
#include "LogEntry.h"
#include <QLocale>
#include <QStyle>
namespace {
@ -74,8 +75,8 @@ QVariant LogModel::data(const QModelIndex &index, int role) const {
QDateTime date = entry->timestamp();
QString timestamp =
(date.date() == QDate::currentDate())
? date.time().toString(Qt::DefaultLocaleShortDate)
: date.toString(Qt::DefaultLocaleShortDate);
? QLocale().toString(date.time(), QLocale::ShortFormat)
: QLocale().toString(date, QLocale::ShortFormat);
text = kTimeFmt.arg(timestamp, text);
}
}

View File

@ -8,6 +8,7 @@
//
#include "Plugin.h"
#include "qtsupport.h"
#include "conf/Settings.h"
#include "editor/TextEditor.h"
#include "git/Config.h"
@ -491,7 +492,7 @@ Plugin::Plugin(const QString &file, const git::Repository &repo,
// Print error messages to the console.
connect(this, &Plugin::error, [](const QString &msg) {
QTextStream(stderr) << "plugin error: " << msg << endl;
QTextStream(stderr) << "plugin error: " << msg << Qt::endl;
});
// Load libraries.
@ -613,6 +614,8 @@ QVariant Plugin::optionValue(const QString &key) const {
case String:
return config().value<QString>(kKeyFmt.arg(mName, key), value.toString());
}
throw std::runtime_error("unreachable; value=" +
std::to_string(static_cast<int>(optionKind(key))));
}
Plugin::OptionKind Plugin::optionKind(const QString &key) const {

17
src/qtsupport.h Normal file
View File

@ -0,0 +1,17 @@
/// Backwards compatibility for older versions of QT
/// Several symbols have been deprecated in QT5.14 and moved to new
/// namespaces. This file provides support for older versions of QT.
#include <QString>
#include <QTextStream>
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
namespace Qt {
static auto endl = ::endl;
static auto KeepEmptyParts = QString::KeepEmptyParts;
static auto SkipEmptyParts = QString::SkipEmptyParts;
} // namespace Qt
// QButtonGroup::buttonClicked is deprecated in favor of QButtonGroup::idClicked
#define idClicked buttonClicked
#endif

View File

@ -1,6 +1,6 @@
add_library(tools DiffTool.cpp EditTool.cpp ExternalTool.cpp MergeTool.cpp
ShowTool.cpp)
target_link_libraries(tools conf git Qt5::Gui)
target_link_libraries(tools conf git Qt5::Gui util)
set_target_properties(tools PROPERTIES AUTOMOC ON)

View File

@ -12,6 +12,7 @@
#include "git/Repository.h"
#include <QProcess>
#include <QTemporaryFile>
#include <QDebug>
DiffTool::DiffTool(const QString &file, const git::Blob &localBlob,
const git::Blob &remoteBlob, QObject *parent)
@ -59,8 +60,25 @@ bool DiffTool::start() {
// Destroy this after process finishes.
QProcess *process = new QProcess(this);
process->setProcessChannelMode(
QProcess::ProcessChannelMode::ForwardedChannels);
auto signal = QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished);
QObject::connect(process, signal, this, &ExternalTool::deleteLater);
QObject::connect(process, signal, [this, process] {
qDebug() << "Merge Process Exited!";
qDebug() << "Stdout: " << process->readAllStandardOutput();
qDebug() << "Stderr: " << process->readAllStandardError();
deleteLater();
});
#if defined(FLATPAK) || defined(DEBUG_FLATPAK)
QStringList arguments = {"--host", "--env=LOCAL=" + local->fileName(),
"--env=REMOTE=" + remotePath,
"--env=MERGED=" + mFile, "--env=BASE=" + mFile};
arguments.append("sh");
arguments.append("-c");
arguments.append(command);
process->start("flatpak-spawn", arguments);
#else
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("LOCAL", local->fileName());
@ -78,9 +96,12 @@ bool DiffTool::start() {
emit error(BashNotFound);
return false;
}
#endif
if (!process->waitForStarted())
if (!process->waitForStarted()) {
qDebug() << "DiffTool starting failed";
return false;
}
// Detach from parent.
setParent(nullptr);

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