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 runs-on: ubuntu-latest
container: container:
# image: bilelmoussaoui/flatpak-github-actions:kde-5.15-21.08 # image: bilelmoussaoui/flatpak-github-actions:kde-5.15-21.08
image: exactlyonekas/gittyup-flatpak-builder:latest image: archlinux:latest
options: --privileged options: --privileged
steps: 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 - name: Show environment variables
run: > run: >
echo IS_RELEASE: ${{ env.IS_RELEASE }} echo IS_RELEASE: ${{ env.IS_RELEASE }}
@ -67,7 +76,7 @@ jobs:
- name: Replace git tag by the commit id on which it runs - name: Replace git tag by the commit id on which it runs
if: github.ref_type != 'tag' if: github.ref_type != 'tag'
run: > 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 - name: Use correct git tag
if: github.ref_type == 'tag' if: github.ref_type == 'tag'
@ -88,6 +97,11 @@ jobs:
run: > run: >
sed -i 's@desktop-file-name-suffix: ""@desktop-file-name-suffix: " (Development)"@' com.github.Murmele.Gittyup/com.github.Murmele.Gittyup.yml 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 - name: Show Flatpak manifest
run: cat com.github.Murmele.Gittyup/com.github.Murmele.Gittyup.yml run: cat com.github.Murmele.Gittyup/com.github.Murmele.Gittyup.yml
@ -123,8 +137,11 @@ jobs:
os: ubuntu-latest os: ubuntu-latest
ninja_platform: linux ninja_platform: linux
qt_platform: linux qt_platform: linux
qt_arch: gcc_64
openssl_arch: linux-x86_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: {} cmake_env: {}
- name: macos - name: macos
@ -193,7 +210,7 @@ jobs:
perl-version: '5.30' perl-version: '5.30'
- name: Install Qt - name: Install Qt
uses: jurplel/install-qt-action@v2.13.0 uses: jurplel/install-qt-action@v3.3.0
timeout-minutes: 10 timeout-minutes: 10
if: "!matrix.qt.check_only" if: "!matrix.qt.check_only"
with: with:
@ -205,7 +222,7 @@ jobs:
modules: qtwebengine modules: qtwebengine
- name: Install Qt - name: Install Qt
uses: jurplel/install-qt-action@v2.13.0 uses: jurplel/install-qt-action@v3.3.0
timeout-minutes: 10 timeout-minutes: 10
if: matrix.qt.check_only if: matrix.qt.check_only
with: with:
@ -264,7 +281,7 @@ jobs:
run: | run: |
mkdir -p build/release mkdir -p build/release
cd 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 - name: Build Information
run: | run: |
@ -301,15 +318,20 @@ jobs:
if: matrix.env.pack && !matrix.qt.check_only if: matrix.env.pack && !matrix.qt.check_only
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
path: build/release/VERSION.txt path: build/release/Version.txt
name: Gittyup-VERSION name: Gittyup-VERSION
- name: Check Version file
run: |
cd build/release
cat ./Version.txt
- name: Test - name: Test
if: matrix.env.ninja_platform != 'win' && matrix.env.ninja_platform != 'mac' if: matrix.env.ninja_platform != 'win' && matrix.env.ninja_platform != 'mac'
uses: GabrielBB/xvfb-action@v1 uses: GabrielBB/xvfb-action@v1
with: with:
working-directory: build/release working-directory: build/release
run: ninja check run: ninja check --verbose
- name: Test (Windows) - name: Test (Windows)
if: matrix.env.ninja_platform == 'win' if: matrix.env.ninja_platform == 'win'
@ -317,6 +339,44 @@ jobs:
cd build/release cd build/release
ninja check_no_win32_offscreen 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: publish:
# https://github.com/marvinpinto/actions/issues/177 # https://github.com/marvinpinto/actions/issues/177
needs: [flatpak, build] needs: [flatpak, build]
@ -347,7 +407,7 @@ jobs:
# version is exported from cmake to file # version is exported from cmake to file
- name: Retrieve version - name: Retrieve version
run: | 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 id: version
- name: Update GitHub release (latest tag) - name: Update GitHub release (latest tag)
@ -363,6 +423,7 @@ jobs:
**/artifacts/Gittyup macos/Gittyup*.dmg **/artifacts/Gittyup macos/Gittyup*.dmg
**/artifacts/Gittyup Flatpak/com.github.Murmele.Gittyup.yml **/artifacts/Gittyup Flatpak/com.github.Murmele.Gittyup.yml
**/Gittyup-x86_64/*.flatpak **/Gittyup-x86_64/*.flatpak
**/artifacts/GittyupAppImage/*
- name: Update GitHub release (version tag) - name: Update GitHub release (version tag)
uses: marvinpinto/action-automatic-releases@latest uses: marvinpinto/action-automatic-releases@latest
@ -378,6 +439,7 @@ jobs:
**/artifacts/Gittyup macos/Gittyup*.dmg **/artifacts/Gittyup macos/Gittyup*.dmg
**/artifacts/Gittyup Flatpak/com.github.Murmele.Gittyup.yml **/artifacts/Gittyup Flatpak/com.github.Murmele.Gittyup.yml
**/Gittyup-x86_64/*.flatpak **/Gittyup-x86_64/*.flatpak
**/artifacts/GittyupAppImage/*
# needed otherwise the docs folder is not available # needed otherwise the docs folder is not available
- name: Checkout repository - name: Checkout repository

8
.gitignore vendored
View File

@ -1,5 +1,13 @@
build build
.cache
.DS_Store .DS_Store
.project .project
.vscode/ .vscode/
CMakeLists.txt.user 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) project(Gittyup)
# Set name and version. # Set name and version.
set(GITTYUP_NAME "Gittyup") set(GITTYUP_NAME "Gittyup")
set(GITTYUP_EXECUTABLE_NAME "gittyup")
set(GITTYUP_IDENTIFIER "com.github.Murmele.Gittyup")
set(GITTYUP_VERSION_MAJOR 1) set(GITTYUP_VERSION_MAJOR 1)
set(GITTYUP_VERSION_MINOR 2) set(GITTYUP_VERSION_MINOR 3)
set(GITTYUP_VERSION_PATCH 1) set(GITTYUP_VERSION_PATCH 0)
set(GITTYUP_VERSION set(GITTYUP_VERSION
"${GITTYUP_VERSION_MAJOR}.${GITTYUP_VERSION_MINOR}.${GITTYUP_VERSION_PATCH}" "${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 # Write version to file so it can be used also from external, for example in the
# github manifest # github manifest
file(WRITE "${CMAKE_BINARY_DIR}/Version.txt" ${GITTYUP_VERSION}) 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(FLATPAK "Building for flatpak" OFF)
option(DEBUG_FLATPAK "Building but using flatpak urls for testing" 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_OPENSSL "Use the system-wide OpenSSL installation" OFF)
option( option(
USE_SYSTEM_LIBGIT2 USE_SYSTEM_LIBGIT2
@ -68,7 +76,10 @@ set(LUA_MODULES_PATH
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE) 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) set(CMAKE_EXE_LINKER_FLAGS -ldl)
endif() endif()
@ -86,9 +97,20 @@ if(UNIX)
set(QT_MODULES ${QT_MODULES} DBus) set(QT_MODULES ${QT_MODULES} DBus)
endif() 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) if(NOT DEBUG_OUTPUT)
add_compile_definitions(QT_NO_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() endif()
find_package( find_package(
@ -103,6 +125,10 @@ if(FLATPAK)
add_compile_definitions(FLATPAK) add_compile_definitions(FLATPAK)
endif() endif()
if(ENABLE_UPDATE_OVER_GUI)
add_compile_definitions(ENABLE_UPDATE_OVER_GUI)
endif()
if(DEBUG_FLATPAK) if(DEBUG_FLATPAK)
add_compile_definitions(DEBUG_FLATPAK) add_compile_definitions(DEBUG_FLATPAK)
endif() endif()
@ -125,6 +151,18 @@ if(APPLE)
endforeach() endforeach()
endif() 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(dep)
add_subdirectory(src) add_subdirectory(src)
add_subdirectory(l10n) add_subdirectory(l10n)

View File

@ -1,7 +1,7 @@
MIT License MIT License
Copyright (c) 2018 Scientific Toolworks, Inc. 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 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal 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. `bin`, `lib`, etc.
**Build** **Build**
```
ninja 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 How to Install
----------------- -----------------
@ -177,7 +180,6 @@ function run_disown_silence(){
run_disown_silence flatpak run com.github.Murmele.Gittyup run_disown_silence flatpak run com.github.Murmele.Gittyup
``` ```
How to Contribute 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 [seven guidelines](https://chris.beams.io/posts/git-commit/) to writing a
great commit message. 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 License
------- -------

View File

@ -1,18 +1,18 @@
#!/bin/bash #!/bin/bash
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
cd "`dirname "$0"`" cd "`dirname "$0"`"
# Variable that will hold the name of the clang-format command # Variable that will hold the name of the clang-format command
FMT="" FMT=""
FOLDERS=("./src" "./test") FOLDERS=("./src" "./test" "./l10n")
# Some distros just call it clang-format. Others (e.g. Ubuntu) are insistent # We specifically require clang-format v13. Some distros include the version
# that the version number be part of the command. We prefer clang-format if # number in the name, others don't. Prefer the specifically-named version.
# that's present, otherwise we work backwards from highest version to lowest for clangfmt in clang-format-13 clang-format
# version but at least 13. do
for clangfmt in clang-format{,-{1,2,3}{9,8,7,6,5,4,3}}; do if command -v "$clangfmt" &>/dev/null; then
if which "$clangfmt" &>/dev/null; then
FMT="$clangfmt" FMT="$clangfmt"
break break
fi fi
@ -24,6 +24,14 @@ if [ -z "$FMT" ]; then
exit 1 exit 1
fi 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() { 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 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}"; echo "format ${f}";
@ -41,9 +49,18 @@ for dir in ${FOLDERS[@]}; do
fi fi
done 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" 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 . \ find . \
\( -type d -path './test/dep/*' -prune \) \ \( -type d -path './test/dep/*' -prune \) \
-o \( -type d -path './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 # add release notes to the appdata file
file(READ "${CHANGELOG_HTML}" HTML_CHANGELOGS) file(READ "${CHANGELOG_HTML}" HTML_CHANGELOGS)
# it is not allowed to have multiple texts without being in an environment # 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 "<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(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(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}) string(REGEX REPLACE "<hr \\/>" "</description>\n\t</release>" RELEASES ${RELEASES})
configure_file(${APPDATA_CONF} ${APPDATA}) configure_file(${APPDATA_CONF} ${APPDATA})

View File

@ -56,13 +56,13 @@ theme['button'] = {
-- commit list colors -- commit list colors
-- { default, active, inactive, disabled } -- { default, active, inactive, disabled }
theme['commits'] = { theme['commits'] = {
text = '#E1E5F2', text = '#AAB2BE',
bright_text = '#AAB2BE', bright_text = '#E1E5F2',
background = '#2D2E34', background = '#2D2E34',
alternate = '#2D2E34', -- an alternate background color for list rows alternate = '#2D2E34', -- an alternate background color for list rows
highlight = { active = '#2A82DA', inactive = '#1B5B9B' }, highlight = { active = '#2A82DA', inactive = '#1B5B9B' },
highlighted_text = { active = '#E1E5F2', inactive = '#E1E5F2' }, highlighted_text = { active = '#A6CBF0', inactive = '#E1E5F2' },
highlighted_bright_text = { active = '#A6CBF0', inactive = '#9090A5' } highlighted_bright_text = { active = '#E1E5F2', inactive = '#9090A5' }
} }
-- status badge colors -- status badge colors
@ -77,7 +77,12 @@ theme['badge'] = {
selected = '#E1E5F2', -- the color when a list item is selected selected = '#E1E5F2', -- the color when a list item is selected
conflicted = '#DA2ADA', -- the color of conflicted items conflicted = '#DA2ADA', -- the color of conflicted items
head = '#52A500', -- a bolder color to indicate the HEAD 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 } -- { normal, selected, conflicted, head, notification }
theme['badge'] = { theme['badge'] = {
foreground = { foreground = {
normal = '#FFFFFF', normal = '#000000',
selected = '#6C6C6C' selected = '#6C6C6C'
}, },
background = { background = {
@ -77,7 +77,12 @@ theme['badge'] = {
selected = '#FFFFFF', -- the color when a list item is selected selected = '#FFFFFF', -- the color when a list item is selected
conflicted = '#D22222', -- the color of conflicted items conflicted = '#D22222', -- the color of conflicted items
head = '#6F7379', -- a bolder color to indicate the HEAD 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) macro(add_helper NAME)
set(TARGET git-credential-${NAME}) set(TARGET git-credential-${NAME})
add_executable(${TARGET} ${PATH}/${NAME}/${TARGET}.c) add_executable(${TARGET} ${PATH}/${NAME}/${TARGET}.c)
set_target_properties(${TARGET} PROPERTIES RUNTIME_OUTPUT_DIRECTORY set_target_properties(
$<TARGET_FILE_DIR:gittyup>) ${TARGET} PROPERTIES RUNTIME_OUTPUT_DIRECTORY
$<TARGET_FILE_DIR:gittyup>/credential-helpers)
if(${ARGC} GREATER 1) if(${ARGC} GREATER 1)
target_link_libraries(${TARGET} ${ARGV1}) target_link_libraries(${TARGET} ${ARGV1})
@ -14,7 +15,7 @@ if(NOT USE_SYSTEM_GIT)
if(NOT APPLE) if(NOT APPLE)
install( install(
TARGETS ${TARGET} TARGETS ${TARGET}
DESTINATION . DESTINATION ${CMAKE_INSTALL_BINDIR}/credential-helpers
COMPONENT ${GITTYUP_NAME}) COMPONENT ${GITTYUP_NAME})
endif() endif()
endmacro() 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_target_properties(scintilla PROPERTIES AUTOMOC ON)
set(SCINTILLUA_LEXERS_DIR set(SCINTILLUA_LEXERS_DIR
${RESOURCES_DIR}/lexers
CACHE INTERNAL "")
set(SRC_SCINTILLUA_LEXERS_DIR
${CMAKE_CURRENT_SOURCE_DIR}/scintillua/lexers ${CMAKE_CURRENT_SOURCE_DIR}/scintillua/lexers
CACHE INTERNAL "") 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 Bug fix release
#### Changed #### 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 download url for flatpak and macos
* Fix Segmentation fault when ignoring files * 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 ### 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. Remember to search for existing issues before creating a new one.
If you still need help, check out our Matrix channel 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 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) ![Rebase Conflicts](https://raw.githubusercontent.com/Murmele/Gittyup/master/rsrc/screenshots/RebaseConflicts.png)
### Staring commits ### Starring commits
to find specific commits much faster 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 ### Tag selection
Use an existing tag as template for your next tag. So you never have to look which is your latest tag 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) ![Tag selection](https://raw.githubusercontent.com/Murmele/Gittyup/master/rsrc/screenshots/tag_selection.png)
### Commit message template ### 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) ![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) set(SRC_DIR ${CMAKE_SOURCE_DIR}/src)
file(GLOB_RECURSE SOURCE_FILES ${SRC_DIR}/*.h ${SRC_DIR}/*.cpp ${SRC_DIR}/*.mm) 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}) foreach(LANGUAGE ${LANGUAGES})
set(TS_FILES ${TS_FILES} gittyup_${LANGUAGE}.ts) set(TS_FILES ${TS_FILES} gittyup_${LANGUAGE}.ts)
set(SUPPORTED_LANGUAGES
"${SUPPORTED_LANGUAGES} {\"${LANGUAGE}\", \"${LANGUAGE}\"},")
endforeach() 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) if(UPDATE_TRANSLATIONS)
# FIXME: Clean removes the .ts files. # FIXME: Clean removes the .ts files.
qt5_create_translation(QM_FILES ${SOURCE_FILES} ${TS_FILES}) qt5_create_translation(QM_FILES ${SOURCE_FILES} ${TS_FILES})
@ -28,12 +36,11 @@ endif()
add_custom_target(translations DEPENDS ${QM_FILES}) add_custom_target(translations DEPENDS ${QM_FILES})
add_dependencies(gittyup translations) 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 # 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}) foreach(LANGUAGE ${LANGUAGES})
set(QT_QM_FILES ${QT_QM_FILES} ${QT_TRANSLATIONS_DIR}/qtbase_${LANGUAGE}.qm) set(QT_QM_FILES ${QT_QM_FILES} ${QT_TRANSLATIONS_DIR}/qtbase_${LANGUAGE}.qm)
@ -44,19 +51,22 @@ foreach(LANGUAGE ${LANGUAGES})
TARGET translations TARGET translations
POST_BUILD POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory COMMAND ${CMAKE_COMMAND} -E make_directory
${DIR}/${CONTENTS_DIR}/Resources/${LANGUAGE}.lproj) ${DIR}/${RESOURCES_DIR}/${LANGUAGE}.lproj)
endif() endif()
endforeach() endforeach()
if(APPLE OR UNIX) foreach(QM_FILE ${QT_QM_FILES})
set(RESOURCES_PREFIX ${CONTENTS_DIR}/)
endif()
foreach(QM_FILE ${QM_FILES} ${QT_QM_FILES})
if(EXISTS ${QM_FILE}) if(EXISTS ${QM_FILE})
install( install(
FILES ${QM_FILE} FILES ${QM_FILE}
DESTINATION ${RESOURCES_PREFIX}Resources/l10n DESTINATION ${L10N_INSTALL_DIR}
COMPONENT ${GITTYUP_NAME}) COMPONENT ${GITTYUP_NAME})
endif() endif()
endforeach() 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(RSRC_DIR ${CMAKE_SOURCE_DIR}/rsrc)
set(MAC $<PLATFORM_ID:Darwin>) set(MAC $<PLATFORM_ID:Darwin>)
set(CONTENTS_DIR ${GITTYUP_NAME}.app/Contents)
# Install Qt plugins. # Install Qt plugins.
set(QT_PLUGINS QJpegPlugin) set(QT_PLUGINS QJpegPlugin)
@ -17,6 +16,14 @@ else()
QComposePlatformInputContextPlugin) QComposePlatformInputContextPlugin)
endif() 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) if(FLATPAK)
qt_import_plugins(gittyup INCLUDE ${QT_PLUGINS}) qt_import_plugins(gittyup INCLUDE ${QT_PLUGINS})
elseif(NOT USE_SYSTEM_QT) elseif(NOT USE_SYSTEM_QT)
@ -35,7 +42,7 @@ elseif(NOT USE_SYSTEM_QT)
install( install(
FILES ${PLUGIN} FILES ${PLUGIN}
DESTINATION $<${MAC}:${CONTENTS_DIR}/>Plugins/${DIR_NAME} DESTINATION ${INSTALL_LIBDIR}/Plugins/${DIR_NAME}
PERMISSIONS PERMISSIONS
OWNER_READ OWNER_READ
OWNER_WRITE OWNER_WRITE
@ -100,7 +107,7 @@ elseif(NOT USE_SYSTEM_QT)
install( install(
FILES ${QT_LIBRARY} FILES ${QT_LIBRARY}
DESTINATION . DESTINATION ${INSTALL_LIBDIR}
PERMISSIONS PERMISSIONS
OWNER_READ OWNER_READ
OWNER_WRITE OWNER_WRITE
@ -131,7 +138,7 @@ if(UNIX AND NOT APPLE)
install( install(
FILES "${LIB_PATH}/${LIB_NAME}${LIB_EXT}" FILES "${LIB_PATH}/${LIB_NAME}${LIB_EXT}"
DESTINATION . DESTINATION ${CMAKE_INSTALL_LIBDIR}
PERMISSIONS PERMISSIONS
OWNER_READ OWNER_READ
OWNER_WRITE OWNER_WRITE
@ -166,7 +173,7 @@ if(NOT APPLE)
install( install(
FILES ${SSL_LIB} FILES ${SSL_LIB}
DESTINATION . DESTINATION ${INSTALL_LIBDIR}
PERMISSIONS PERMISSIONS
OWNER_READ OWNER_READ
OWNER_WRITE OWNER_WRITE
@ -186,16 +193,25 @@ if(NOT APPLE)
set(QT_CONF ${CONF_DIR}/qt.conf) set(QT_CONF ${CONF_DIR}/qt.conf)
install( install(
FILES ${QT_CONF} FILES ${QT_CONF}
DESTINATION $<${MAC}:${CONTENTS_DIR}/Resources/>. DESTINATION
$<${MAC}:${RESOURCES_DIR}>$<$<PLATFORM_ID:Windows>:${INSTALL_LIBDIR}>
COMPONENT ${GITTYUP_NAME}) COMPONENT ${GITTYUP_NAME})
endif() endif()
endif() endif()
if(UNIX AND NOT APPLE) 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( install(
DIRECTORY ${RSRC_DIR}/Gittyup.iconset FILES ${RSRC_DIR}/Gittyup.iconset/gittyup_logo.svg
DESTINATION "Resources" DESTINATION share/icons/hicolor/scalable/apps
COMPONENT ${GITTYUP_NAME}) COMPONENT ${GITTYUP_NAME}
RENAME ${GITTYUP_EXECUTABLE_NAME}.svg)
endif() endif()
# Sign bundle on macOS. # Sign bundle on macOS.
@ -204,7 +220,7 @@ if(APPLE AND CODESIGN_IDENTITY)
CODE "execute_process(COMMAND CODE "execute_process(COMMAND
codesign --deep --timestamp --options runtime codesign --deep --timestamp --options runtime
-s \"${CODESIGN_IDENTITY}\" -s \"${CODESIGN_IDENTITY}\"
\${CMAKE_INSTALL_PREFIX}/${GITTYUP_NAME}.app \${CMAKE_INSTALL_PREFIX}/${GITTYUP_EXECUTABLE_NAME}.app
)" )"
COMPONENT ${GITTYUP_NAME}) COMPONENT ${GITTYUP_NAME})
endif() endif()
@ -220,9 +236,10 @@ elseif(WIN32)
set(CPACK_NSIS_CREATE_ICONS_EXTRA set(CPACK_NSIS_CREATE_ICONS_EXTRA
"CreateShortCut \ "CreateShortCut \
\\\"$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\${GITTYUP_NAME}.lnk\\\" \ \\\"$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\${GITTYUP_NAME}.lnk\\\" \
\\\"$INSTDIR\\\\${GITTYUP_NAME}.exe\\\"") \\\"$INSTDIR\\\\${CMAKE_INSTALL_BINDIR}\\\\${GITTYUP_EXECUTABLE_NAME}.exe\\\""
set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".") )
set(CPACK_NSIS_MUI_FINISHPAGE_RUN ${GITTYUP_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) if(CMAKE_SIZEOF_VOID_P EQUAL 8)
install( install(
@ -262,11 +279,11 @@ if(WIN32)
set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS
"${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' '' 'Open with Gittyup'
WriteRegStr HKCR 'Directory\\\\Background\\\\shell\\\\Gittyup' 'Icon' '\\\"$INSTDIR\\\\gittyup.exe\\\"' WriteRegStr HKCR 'Directory\\\\Background\\\\shell\\\\Gittyup' 'Icon' '\\\"$INSTDIR\\\\${CMAKE_INSTALL_BINDIR}\\\\${GITTYUP_EXECUTABLE_NAME}.exe\\\"'
WriteRegStr HKCR 'Directory\\\\Background\\\\shell\\\\Gittyup\\\\command' '' '\\\"$INSTDIR\\\\gittyup.exe\\\" \\\"%V\\\"' 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' '' 'Open with Gittyup'
WriteRegStr HKCR 'Directory\\\\shell\\\\Gittyup' 'Icon' '\\\"$INSTDIR\\\\gittyup.exe\\\"' WriteRegStr HKCR 'Directory\\\\shell\\\\Gittyup' 'Icon' '\\\"$INSTDIR\\\\${CMAKE_INSTALL_BINDIR}\\\\${GITTYUP_EXECUTABLE_NAME}.exe\\\"'
WriteRegStr HKCR 'Directory\\\\shell\\\\Gittyup\\\\command' '' '\\\"$INSTDIR\\\\gittyup.exe\\\" \\\"%L\\\"'" WriteRegStr HKCR 'Directory\\\\shell\\\\Gittyup\\\\command' '' '\\\"$INSTDIR\\\\${CMAKE_INSTALL_BINDIR}\\\\${GITTYUP_EXECUTABLE_NAME}.exe\\\" \\\"%L\\\"'"
) )
set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS
"${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_FILE_NAME ${PACKAGE_FILE_NAME})
set(CPACK_PACKAGE_DIRECTORY ${CMAKE_BINARY_DIR}/pack) set(CPACK_PACKAGE_DIRECTORY ${CMAKE_BINARY_DIR}/pack)
set(CPACK_PACKAGE_INSTALL_DIRECTORY ${GITTYUP_NAME}) 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_MAJOR ${GITTYUP_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${GITTYUP_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_MINOR ${GITTYUP_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${GITTYUP_VERSION_PATCH}) set(CPACK_PACKAGE_VERSION_PATCH ${GITTYUP_VERSION_PATCH})
if(WIN32) if(WIN32)
set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_SOURCE_DIR}/LICENSE.md) 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() endif()
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) 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 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) 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> <string>6.0</string>
<key>CFBundleLongVersionString</key> <key>CFBundleLongVersionString</key>
<string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string> <string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
<key>CFBundleDisplayName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string> <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundlePackageType</key> <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> <url type="donation">https://liberapay.com/Gittyup/donate</url>
<provides> <provides>
<binary>Gittyup</binary> <binary>gittyup</binary>
<id>com.github.Murmele.Gittyup</id> <id>com.github.Murmele.Gittyup</id>
</provides> </provides>

View File

@ -2,11 +2,10 @@
Encoding=UTF-8 Encoding=UTF-8
Name=Gittyup Name=Gittyup
Comment=Graphical Git client for Windows, Linux and macOS Comment=Graphical Git client for Windows, Linux and macOS
Exec=Gittyup Exec=gittyup
StartupWMClass=gittyup StartupWMClass=com.github.Murmele.Gittyup
Icon=gittyup Icon=gittyup
Terminal=false Terminal=false
Type=Application Type=Application
Categories=Development; Categories=Development;
X-Desktop-File-Install-Version=0.1 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}) 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(cli)
add_subdirectory(conf) add_subdirectory(conf)
add_subdirectory(cred) add_subdirectory(cred)
@ -13,7 +21,6 @@ add_subdirectory(plugins)
add_subdirectory(tools) add_subdirectory(tools)
add_subdirectory(ui) add_subdirectory(ui)
add_subdirectory(update) add_subdirectory(update)
add_subdirectory(util)
add_subdirectory(watcher) add_subdirectory(watcher)
# Add executable last. # Add executable last.

View File

@ -16,6 +16,8 @@
#include "ui/RepoView.h" #include "ui/RepoView.h"
#include "ui/TabWidget.h" #include "ui/TabWidget.h"
#include "update/Updater.h" #include "update/Updater.h"
#include "languages.h"
#include "util/Debug.h"
#include <QCloseEvent> #include <QCloseEvent>
#include <QCommandLineParser> #include <QCommandLineParser>
#include <QDesktopServices> #include <QDesktopServices>
@ -132,6 +134,18 @@ Application::Application(int &argc, char **argv, bool haltOnParseError)
// Set debug menu option. // Set debug menu option.
MenuBar::setDebugMenuVisible(parser.isSet("debug-menu")); 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. // Set pathspec filter.
mPathspec = parser.value("filter"); mPathspec = parser.value("filter");
@ -159,6 +173,12 @@ Application::Application(int &argc, char **argv, bool haltOnParseError)
.toBool()) && .toBool()) &&
(!parser.isSet("no-translation"))) { (!parser.isSet("no-translation"))) {
// Load translation files. // Load translation files.
const auto &language =
Settings::instance()->value(Setting::Id::Language).toString();
if (language != Languages::system)
QLocale::setDefault(QLocale(language));
QLocale locale; QLocale locale;
QDir l10n = Settings::l10nDir(); QDir l10n = Settings::l10nDir();
QString name = QString(GITTYUP_NAME).toLower(); QString name = QString(GITTYUP_NAME).toLower();
@ -310,9 +330,9 @@ static MainWindow *openOrSwitch(QDir repo) {
} }
#if defined(Q_OS_LINUX) #if defined(Q_OS_LINUX)
#define DBUS_SERVICE_NAME "com.github.Murmele.Gittyup" #define DBUS_SERVICE_NAME GITTYUP_IDENTIFIER
#define DBUS_INTERFACE_NAME "com.github.Murmele.Gittyup.Application" #define DBUS_INTERFACE_NAME GITTYUP_DBUS_INTERFACE_NAME
#define DBUS_OBJECT_PATH "/com/github/Murmele/Gittyup/Application" #define DBUS_OBJECT_PATH GITTYUP_DBUS_OBJECT_PATH
DBusGittyup::DBusGittyup(QObject *parent) : QObject(parent) {} DBusGittyup::DBusGittyup(QObject *parent) : QObject(parent) {}
@ -488,4 +508,4 @@ void Application::handleSslErrors(QNetworkReply *reply,
reply->ignoreSslErrors(errors); reply->ignoreSslErrors(errors);
settings.setValue("ssl/ignore", true); 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 file(GLOB DICTIONARIES ${CONF_DIR}/dictionaries/*.aff
${CONF_DIR}/dictionaries/*.dic ${CONF_DIR}/dictionaries/*.txt) ${CONF_DIR}/dictionaries/*.dic ${CONF_DIR}/dictionaries/*.txt)
file(GLOB THEMES ${CONF_DIR}/themes/*.lua) 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) file(GLOB LUA_PLUGINS ${CONF_DIR}/plugins/*.lua ${CONF_DIR}/plugins/*.txt)
# Build resources. # Build resources.
@ -59,11 +59,22 @@ endif()
target_compile_definitions( target_compile_definitions(
app 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}" PRIVATE QT_TRANSLATIONS_DIR="${QT_TRANSLATIONS_DIR}"
BUILD_DESCRIPTION="${BUILD_DESCRIPTION}") 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) set_target_properties(app PROPERTIES AUTOMOC ON)
@ -103,8 +114,10 @@ endif()
target_link_libraries(gittyup app) target_link_libraries(gittyup app)
set_target_properties( set_target_properties(
gittyup PROPERTIES OUTPUT_NAME ${GITTYUP_NAME} RUNTIME_OUTPUT_DIRECTORY gittyup
${CMAKE_BINARY_DIR}) PROPERTIES OUTPUT_NAME ${GITTYUP_EXECUTABLE_NAME}
MACOSX_BUNDLE_BUNDLE_NAME ${GITTYUP_NAME}
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
if(WIN32) if(WIN32)
target_link_libraries(app Dbghelp.lib) target_link_libraries(app Dbghelp.lib)
@ -118,7 +131,7 @@ elseif(APPLE)
MACOSX_BUNDLE_BUNDLE_VERSION ${GITTYUP_VERSION} MACOSX_BUNDLE_BUNDLE_VERSION ${GITTYUP_VERSION}
MACOSX_BUNDLE_LONG_VERSION_STRING ${GITTYUP_VERSION} MACOSX_BUNDLE_LONG_VERSION_STRING ${GITTYUP_VERSION}
MACOSX_BUNDLE_SHORT_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. # Copy config files into bundle.
set_source_files_properties( set_source_files_properties(
@ -152,19 +165,10 @@ endif()
add_dependencies(gittyup indexer relauncher) add_dependencies(gittyup indexer relauncher)
if(FLATPAK) install(
# Install application. TARGETS gittyup
install( DESTINATION $<IF:$<PLATFORM_ID:Darwin>,.,${CMAKE_INSTALL_BINDIR}>
TARGETS gittyup COMPONENT ${GITTYUP_NAME})
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()
if(APPLE) if(APPLE)
get_target_property(QT_LIBRARY Qt5::Core LOCATION) get_target_property(QT_LIBRARY Qt5::Core LOCATION)
@ -172,7 +176,7 @@ if(APPLE)
get_filename_component(RPATH ${QT_FRAMEWORK} DIRECTORY) get_filename_component(RPATH ${QT_FRAMEWORK} DIRECTORY)
# Fixup relauncher RPATH. # Fixup relauncher RPATH.
set(RELAUNCHER ${GITTYUP_NAME}.app/Contents/MacOS/relauncher) set(RELAUNCHER ${CONTENTS_DIR}/MacOS/relauncher)
install( install(
CODE "execute_process(COMMAND CODE "execute_process(COMMAND
${CMAKE_INSTALL_NAME_TOOL} -delete_rpath \"${RPATH}\" ${CMAKE_INSTALL_NAME_TOOL} -delete_rpath \"${RPATH}\"
@ -189,7 +193,7 @@ execute_process(COMMAND
COMPONENT ${GITTYUP_NAME}) COMPONENT ${GITTYUP_NAME})
# Fixup indexer RPATH. # Fixup indexer RPATH.
set(INDEXER ${GITTYUP_NAME}.app/Contents/MacOS/indexer) set(INDEXER ${CONTENTS_DIR}/MacOS/indexer)
install( install(
CODE "execute_process(COMMAND CODE "execute_process(COMMAND
${CMAKE_INSTALL_NAME_TOOL} -delete_rpath \"${RPATH}\" ${CMAKE_INSTALL_NAME_TOOL} -delete_rpath \"${RPATH}\"
@ -207,13 +211,16 @@ execute_process(COMMAND
else() else()
# Windows and UNIX must be ./ otherwise it is interpreted as absolute path # Windows and UNIX must be ./ otherwise it is interpreted as absolute path
if(UNIX) if(UNIX)
set(RESOURCES_INSTALL_DIR install(
"${CMAKE_INSTALL_DATADIR}/gittyup/Resources" FILES ${CMAKE_SOURCE_DIR}/rsrc/linux/com.github.Murmele.Gittyup.desktop
CACHE STRING "The path for installing resource files") DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
else() RENAME ${GITTYUP_EXECUTABLE_NAME}.desktop)
set(RESOURCES_INSTALL_DIR if(${GENERATE_APPDATA})
"Resources" install(
CACHE STRING "The path for installing resource files") FILES ${APPDATA}
DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo
RENAME ${GITTYUP_EXECUTABLE_NAME}.appdata.xml)
endif()
endif() endif()
# Install config files. # Install config files.
@ -224,13 +231,13 @@ else()
install( install(
FILES ${CHANGELOG_HTML} ${ACKNOWLEDGMENTS_HTML} ${PRIVACY_HTML} FILES ${CHANGELOG_HTML} ${ACKNOWLEDGMENTS_HTML} ${PRIVACY_HTML}
${PLUGIN_HTML} ${PLUGIN_HTML}
DESTINATION ${RESOURCES_INSTALL_DIR} DESTINATION ${RESOURCES_DIR}
COMPONENT ${GITTYUP_NAME}) COMPONENT ${GITTYUP_NAME})
endif() endif()
install( install(
FILES ${EMOJI} ${MERGETOOLS} FILES ${EMOJI} ${MERGETOOLS}
DESTINATION ${RESOURCES_INSTALL_DIR} DESTINATION ${RESOURCES_DIR}
COMPONENT ${GITTYUP_NAME}) COMPONENT ${GITTYUP_NAME})
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different ${EMOJI} execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different ${EMOJI}
@ -241,7 +248,7 @@ else()
foreach(CONFIG_FILE ${CONFIG_FILES}) foreach(CONFIG_FILE ${CONFIG_FILES})
install( install(
FILES ${CONFIG_FILE} FILES ${CONFIG_FILE}
DESTINATION ${RESOURCES_INSTALL_DIR} DESTINATION ${RESOURCES_DIR}
COMPONENT ${GITTYUP_NAME}) COMPONENT ${GITTYUP_NAME})
file(RELATIVE_PATH CONFIG_FILE_DEST ${CONF_DIR} ${CONFIG_FILE}) file(RELATIVE_PATH CONFIG_FILE_DEST ${CONF_DIR} ${CONFIG_FILE})
@ -252,7 +259,7 @@ else()
install( install(
FILES ${DICTIONARIES} FILES ${DICTIONARIES}
DESTINATION ${RESOURCES_INSTALL_DIR}/dictionaries DESTINATION ${RESOURCES_DIR}/dictionaries
COMPONENT ${GITTYUP_NAME}) COMPONENT ${GITTYUP_NAME})
foreach(DICTIONARY ${DICTIONARIES}) foreach(DICTIONARY ${DICTIONARIES})
@ -263,7 +270,7 @@ else()
install( install(
FILES ${THEMES} FILES ${THEMES}
DESTINATION ${RESOURCES_INSTALL_DIR}/themes DESTINATION ${RESOURCES_DIR}/themes
COMPONENT ${GITTYUP_NAME}) COMPONENT ${GITTYUP_NAME})
foreach(THEME ${THEMES}) foreach(THEME ${THEMES})
@ -274,7 +281,7 @@ else()
install( install(
FILES ${LUA_PLUGINS} FILES ${LUA_PLUGINS}
DESTINATION ${RESOURCES_INSTALL_DIR}/plugins DESTINATION ${RESOURCES_DIR}/plugins
COMPONENT ${GITTYUP_NAME}) COMPONENT ${GITTYUP_NAME})
foreach(LUA_PLUGIN ${LUA_PLUGINS}) foreach(LUA_PLUGIN ${LUA_PLUGINS})
@ -286,7 +293,7 @@ else()
foreach(SCINTILLUA_LEXER ${SCINTILLUA_LEXERS}) foreach(SCINTILLUA_LEXER ${SCINTILLUA_LEXERS})
install( install(
FILES ${SCINTILLUA_LEXER} FILES ${SCINTILLUA_LEXER}
DESTINATION ${RESOURCES_INSTALL_DIR}/lexers DESTINATION ${RESOURCES_DIR}/lexers
COMPONENT ${GITTYUP_NAME}) COMPONENT ${GITTYUP_NAME})
get_filename_component(SCINTILLUA_LEXER_DEST ${SCINTILLUA_LEXER} 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: case BadgeState::Notification:
stateKey = "notification"; stateKey = "notification";
break; 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(); QVariantMap badge = mMap.value("badge").toMap();
@ -421,6 +436,8 @@ QColor CustomTheme::commitEditor(CommitEditor color) {
case CommitEditor::LengthWarning: case CommitEditor::LengthWarning:
return commitEditor.value("lengthwarning").value<QColor>(); return commitEditor.value("lengthwarning").value<QColor>();
} }
throw std::runtime_error("unreachable; value=" +
std::to_string(static_cast<int>(color)));
} }
QColor CustomTheme::diff(Diff color) { QColor CustomTheme::diff(Diff color) {
@ -450,6 +467,8 @@ QColor CustomTheme::diff(Diff color) {
case Diff::Error: case Diff::Error:
return diff.value("error").value<QColor>(); return diff.value("error").value<QColor>();
} }
throw std::runtime_error("unreachable; value=" +
std::to_string(static_cast<int>(color)));
} }
QColor CustomTheme::heatMap(HeatMap color) { QColor CustomTheme::heatMap(HeatMap color) {
@ -461,6 +480,8 @@ QColor CustomTheme::heatMap(HeatMap color) {
case HeatMap::Cold: case HeatMap::Cold:
return QColor(heatmap.value("cold").toString()); return QColor(heatmap.value("cold").toString());
} }
throw std::runtime_error("unreachable; value=" +
std::to_string(static_cast<int>(color)));
} }
QColor CustomTheme::remoteComment(Comment color) { QColor CustomTheme::remoteComment(Comment color) {
@ -476,6 +497,8 @@ QColor CustomTheme::remoteComment(Comment color) {
case Comment::Timestamp: case Comment::Timestamp:
return QColor(comment.value("timestamp").toString()); return QColor(comment.value("timestamp").toString());
} }
throw std::runtime_error("unreachable; value=" +
std::to_string(static_cast<int>(color)));
} }
QColor CustomTheme::star() { QColor CustomTheme::star() {

View File

@ -101,7 +101,14 @@ QColor Theme::badge(BadgeRole role, BadgeState state) {
case BadgeState::Head: case BadgeState::Head:
return QPalette().color(QPalette::HighlightedText); 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); return QPalette().color(QPalette::WindowText);
} }
@ -123,8 +130,20 @@ QColor Theme::badge(BadgeRole role, BadgeState state) {
case BadgeState::Notification: case BadgeState::Notification:
return mDark ? "#8C2026" : "#FF0000"; 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() { QList<QColor> Theme::branchTopologyEdges() {
@ -152,6 +171,8 @@ QColor Theme::commitEditor(CommitEditor color) {
case CommitEditor::LengthWarning: case CommitEditor::LengthWarning:
return Qt::yellow; return Qt::yellow;
} }
throw std::runtime_error("unreachable; value=" +
std::to_string(static_cast<int>(color)));
} }
QColor Theme::diff(Diff color) { QColor Theme::diff(Diff color) {
@ -180,6 +201,8 @@ QColor Theme::diff(Diff color) {
case Diff::Error: case Diff::Error:
return "#7E494B"; return "#7E494B";
} }
throw std::runtime_error("unreachable; value=" +
std::to_string(static_cast<int>(color)));
} }
switch (color) { switch (color) {
@ -206,6 +229,8 @@ QColor Theme::diff(Diff color) {
case Diff::Error: case Diff::Error:
return "#FF0000"; return "#FF0000";
} }
throw std::runtime_error("unreachable; value=" +
std::to_string(static_cast<int>(color)));
} }
QColor Theme::heatMap(HeatMap color) { QColor Theme::heatMap(HeatMap color) {
@ -216,6 +241,8 @@ QColor Theme::heatMap(HeatMap color) {
return mDark ? QPalette().color(QPalette::Inactive, QPalette::Highlight) return mDark ? QPalette().color(QPalette::Inactive, QPalette::Highlight)
: QPalette().color(QPalette::Mid); : QPalette().color(QPalette::Mid);
} }
throw std::runtime_error("unreachable; value=" +
std::to_string(static_cast<int>(color)));
} }
QColor Theme::remoteComment(Comment color) { QColor Theme::remoteComment(Comment color) {
@ -229,6 +256,8 @@ QColor Theme::remoteComment(Comment color) {
case Comment::Timestamp: case Comment::Timestamp:
return QPalette().color(QPalette::WindowText); 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); } QColor Theme::star() { return QPalette().color(QPalette::Highlight); }

View File

@ -22,7 +22,18 @@ class Theme {
public: public:
enum class BadgeRole { Foreground, Background }; 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 }; enum class CommitEditor { SpellError, SpellIgnore, LengthWarning };

View File

@ -1,18 +1,15 @@
add_library(conf ConfFile.cpp Settings.cpp Setting.cpp RecentRepositories.cpp add_library(conf ConfFile.cpp Settings.cpp Setting.cpp RecentRepositories.cpp
RecentRepository.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( target_compile_definitions(
conf PRIVATE SCINTILLUA_LEXERS_DIR="${SCINTILLUA_LEXERS_DIR}") conf
PRIVATE CONF_DIR="${RESOURCES_DIR}"
if(UNIX) SRC_CONF_DIR="${CMAKE_SOURCE_DIR}/conf"
target_compile_definitions( L10N_DIR="${L10N_INSTALL_DIR}"
conf SRC_L10N_DIR="${CMAKE_BINARY_DIR}/l10n"
PRIVATE CONF_DIR="${CMAKE_INSTALL_FULL_DATADIR}/gittyup/Resources" SCINTILLUA_LEXERS_DIR="${SCINTILLUA_LEXERS_DIR}"
L10N_DIR="${CMAKE_INSTALL_FULL_DATADIR}/gittyup/Resources/l10n") SRC_SCINTILLUA_LEXERS_DIR="${SRC_SCINTILLUA_LEXERS_DIR}")
else()
target_compile_definitions(conf PRIVATE CONF_DIR="" L10N_DIR="")
endif()
set_target_properties(conf PROPERTIES AUTOMOC ON) set_target_properties(conf PROPERTIES AUTOMOC ON)

View File

@ -50,16 +50,20 @@ void RecentRepositories::remove(int index) {
emit repositoryRemoved(); 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(); emit repositoryAboutToBeAdded();
auto end = mRepos.end(); 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) { auto it = std::remove_if(mRepos.begin(), end, [repo](RecentRepository *rhs) {
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
return repo->path().compare(rhs->path(), Qt::CaseInsensitive) == 0; return repo->gitpath().compare(rhs->gitpath(), Qt::CaseInsensitive) == 0;
#else #else
return (repo->path() == rhs->path()); return (repo->gitpath() == rhs->gitpath());
#endif #endif
}); });
@ -82,7 +86,7 @@ RecentRepositories *RecentRepositories::instance() {
void RecentRepositories::store() { void RecentRepositories::store() {
QStringList paths; QStringList paths;
foreach (RecentRepository *repo, mRepos) foreach (RecentRepository *repo, mRepos)
paths.append(repo->path()); paths.append(repo->gitpath());
QSettings().setValue(kRecentKey, paths); QSettings().setValue(kRecentKey, paths);
@ -125,6 +129,13 @@ void RecentRepositories::load() {
qDeleteAll(mRepos); qDeleteAll(mRepos);
mRepos.clear(); 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) { foreach (const QString &path, paths) {
RecentRepository *repo = new RecentRepository(path, this); RecentRepository *repo = new RecentRepository(path, this);
auto functor = [repo](RecentRepository *rhs) { auto functor = [repo](RecentRepository *rhs) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,6 +8,8 @@
// //
#include "GitCredential.h" #include "GitCredential.h"
#include "qtsupport.h"
#include <QStandardPaths>
#include <QCoreApplication> #include <QCoreApplication>
#include <QDir> #include <QDir>
#include <QProcess> #include <QProcess>
@ -44,11 +46,11 @@ bool GitCredential::get(const QString &url, QString &username,
return false; return false;
QTextStream out(&process); QTextStream out(&process);
out << "protocol=" << protocol(url) << endl; out << "protocol=" << protocol(url) << Qt::endl;
out << "host=" << host(url) << endl; out << "host=" << host(url) << Qt::endl;
if (!username.isEmpty()) if (!username.isEmpty())
out << "username=" << username << endl; out << "username=" << username << Qt::endl;
out << endl; out << Qt::endl;
process.closeWriteChannel(); process.closeWriteChannel();
process.waitForFinished(); process.waitForFinished();
@ -79,11 +81,11 @@ bool GitCredential::store(const QString &url, const QString &username,
return false; return false;
QTextStream out(&process); QTextStream out(&process);
out << "protocol=" << protocol(url) << endl; out << "protocol=" << protocol(url) << Qt::endl;
out << "host=" << host(url) << endl; out << "host=" << host(url) << Qt::endl;
out << "username=" << username << endl; out << "username=" << username << Qt::endl;
out << "password=" << password << endl; out << "password=" << password << Qt::endl;
out << endl; out << Qt::endl;
process.closeWriteChannel(); process.closeWriteChannel();
process.waitForFinished(); process.waitForFinished();
@ -92,6 +94,46 @@ bool GitCredential::store(const QString &url, const QString &username,
} }
QString GitCredential::command() const { QString GitCredential::command() const {
QDir dir(QCoreApplication::applicationDirPath()); QString name = QString("git-credential-%1").arg(mName);
return dir.filePath(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 = const QString kTextFmt =
"<p style='white-space: nowrap'><b style='font-size: large'>%1 v%2</b> " "<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 " "<br>Copyright © 2016-2020 Scientific Toolworks, Inc. and "
"contributors</p><p> If you have a question that might benefit the " "contributors</p><p> If you have a question that might benefit the "
"community, consider asking it on <a href='%5'>Stack Overflow</a> by " "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); l->addWidget(m_committerInfo, Row::Committer, 0, 1, 2);
auto *lMessage = new QLabel(tr("Commit Message:"), this); 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"); m_commitMessage->setObjectName("Textlabel Commit Message");
l->addWidget(lMessage, Row::CommitMessageLabel, 0); l->addWidget(lMessage, Row::CommitMessageLabel, 0);
l->addWidget(m_commitMessage, Row::CommitMessage, 0, 1, 2); l->addWidget(m_commitMessage, Row::CommitMessage, 0, 1, 2);

View File

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

View File

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

View File

@ -47,7 +47,7 @@ DeleteBranchDialog::DeleteBranchDialog(const git::Branch &branch,
QString name = upstream.name().section('/', 1); QString name = upstream.name().section('/', 1);
QString key = kBranchMergeFmt.arg(branch.name()); 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(); git::Remote remote = upstream.remote();
QString remoteName = remote.name(); QString remoteName = remote.name();

View File

@ -24,7 +24,8 @@
#include <QTextCodec> #include <QTextCodec>
DiffPanel::DiffPanel(const git::Repository &repo, QWidget *parent) 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 // diff context
QSpinBox *context = new QSpinBox(this); QSpinBox *context = new QSpinBox(this);
QLabel *contextLabel = new QLabel(tr("lines"), this); QLabel *contextLabel = new QLabel(tr("lines"), this);

View File

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

View File

@ -90,7 +90,7 @@ RemoteDialog::RemoteDialog(Kind kind, RepoView *parent) : QDialog(parent) {
if (ref.isValid()) { if (ref.isValid()) {
QString key = QString("branch.%1.merge").arg(ref.name()); QString key = QString("branch.%1.merge").arg(ref.name());
git::Config config = git::Config config =
RepoView::parentView(this)->repo().config(); RepoView::parentView(this)->repo().gitConfig();
value = config.value<QString>(key); value = config.value<QString>(key);
} }
mRemoteRef->setText(value); 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/MainWindow.h"
#include "ui/MenuBar.h" #include "ui/MenuBar.h"
#include "ui/RepoView.h" #include "ui/RepoView.h"
#include "languages.h"
#include "update/Updater.h" #include "update/Updater.h"
#include <QAction> #include <QAction>
#include <QApplication> #include <QApplication>
@ -105,17 +106,23 @@ public:
new QCheckBox(tr("Update submodules after pull and clone"), this); new QCheckBox(tr("Update submodules after pull and clone"), this);
mAutoPrune = new QCheckBox(tr("Prune when fetching"), this); mAutoPrune = new QCheckBox(tr("Prune when fetching"), this);
mNoTranslation = new QCheckBox(tr("No translation"), 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 = mStoreCredentials =
new QCheckBox(tr("Store credentials in secure storage"), this); 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>")); QLabel *privacy = new QLabel(tr("<a href='view'>View privacy policy</a>"));
connect(privacy, &QLabel::linkActivated, connect(privacy, &QLabel::linkActivated,
[] { AboutDialog::openSharedInstance(AboutDialog::Privacy); }); [] { AboutDialog::openSharedInstance(AboutDialog::Privacy); });
mSingleInstance =
new QCheckBox(tr("Only allow a single running instance"), this);
QFormLayout *form = new QFormLayout; QFormLayout *form = new QFormLayout;
form->addRow(tr("User name:"), mName); form->addRow(tr("User name:"), mName);
form->addRow(tr("User email:"), mEmail); form->addRow(tr("User email:"), mEmail);
@ -124,11 +131,18 @@ public:
form->addRow(QString(), mPullUpdate); form->addRow(QString(), mPullUpdate);
form->addRow(QString(), mAutoPrune); form->addRow(QString(), mAutoPrune);
form->addRow(tr("Language:"), mNoTranslation); form->addRow(tr("Language:"), mNoTranslation);
form->addRow(tr("Language:"), mLanguages);
form->addRow(tr("Credentials:"), mStoreCredentials); form->addRow(tr("Credentials:"), mStoreCredentials);
form->addRow(tr("Credential store type:"), mAvailableStores);
form->addRow(QString(), privacy); form->addRow(QString(), privacy);
mSingleInstance =
new QCheckBox(tr("Only allow a single running instance"), this);
#if defined(Q_OS_LINUX) || defined(Q_OS_WIN) #if defined(Q_OS_LINUX) || defined(Q_OS_WIN)
form->addRow(tr("Single instance:"), mSingleInstance); form->addRow(tr("Single instance:"), mSingleInstance);
#elif defined(Q_OS_MACX)
mSingleInstance->setVisible(false);
#endif #endif
QVBoxLayout *layout = new QVBoxLayout(this); QVBoxLayout *layout = new QVBoxLayout(this);
@ -179,11 +193,33 @@ public:
Settings::instance()->setValue(Setting::Id::DontTranslate, checked); Settings::instance()->setValue(Setting::Id::DontTranslate, checked);
}); });
connect(mStoreCredentials, &QCheckBox::toggled, [](bool checked) { connect(mLanguages, QOverload<int>::of(&QComboBox::currentIndexChanged),
Settings::instance()->setValue(Setting::Id::StoreCredentials, checked); [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(); 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) { connect(mSingleInstance, &QCheckBox::toggled, [](bool checked) {
Settings::instance()->setValue(Setting::Id::AllowSingleInstanceOnly, Settings::instance()->setValue(Setting::Id::AllowSingleInstanceOnly,
checked); checked);
@ -196,6 +232,7 @@ public:
mEmail->setText(config.value<QString>("user.email")); mEmail->setText(config.value<QString>("user.email"));
Settings *settings = Settings::instance(); Settings *settings = Settings::instance();
mFetch->setChecked( mFetch->setChecked(
settings->value(Setting::Id::FetchAutomatically).toBool()); settings->value(Setting::Id::FetchAutomatically).toBool());
mFetchMinutes->setValue( mFetchMinutes->setValue(
@ -211,8 +248,25 @@ public:
mNoTranslation->setChecked( mNoTranslation->setChecked(
settings->value(Setting::Id::DontTranslate).toBool()); 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( mSingleInstance->setChecked(
settings->value(Setting::Id::AllowSingleInstanceOnly).toBool()); settings->value(Setting::Id::AllowSingleInstanceOnly).toBool());
@ -228,7 +282,9 @@ private:
QCheckBox *mPullUpdate; QCheckBox *mPullUpdate;
QCheckBox *mAutoPrune; QCheckBox *mAutoPrune;
QCheckBox *mNoTranslation; QCheckBox *mNoTranslation;
QComboBox *mLanguages;
QCheckBox *mStoreCredentials; QCheckBox *mStoreCredentials;
QComboBox *mAvailableStores;
QCheckBox *mSingleInstance; QCheckBox *mSingleInstance;
}; };

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -105,7 +105,7 @@ public:
QList<Diagnostic> diagnostics(int line); QList<Diagnostic> diagnostics(int line);
void addDiagnostic(int line, const Diagnostic &diag); 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. // Make wheel event public.
// FIXME: This should be an event filter? // FIXME: This should be an event filter?
@ -154,7 +154,7 @@ private:
int diagnosticMarker(int line); int diagnosticMarker(int line);
void loadMarkerIcon(Marker marker, const QIcon &icon); void loadMarkerIcon(Marker marker, const QIcon &icon);
void loadMarkerPixmap(Marker marker, const QPixmap &pixmap); 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); void ContextMenu(Scintilla::Point pt);
QString mPath; QString mPath;

View File

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

View File

@ -251,7 +251,8 @@ bool Commit::amend(const Signature &author, const Signature &committer,
return !error; 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<char *> rawPaths;
QVector<QByteArray> storage; QVector<QByteArray> storage;
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; 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(); Repository repo = this->repo();
int state = repo.state(); int state = repo.state();
int error = git_reset(repo, d.data(), type, &opts); 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) { if (repo.state() != state) {
Patch::clearConflictResolutions(repo); Patch::clearConflictResolutions(repo);
emit repo.notifier()->stateChanged(); emit repo.notifier()->stateChanged();

View File

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

View File

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

View File

@ -195,7 +195,7 @@ QByteArray Patch::header(int hidx) const {
const git_diff_hunk *hunk = nullptr; const git_diff_hunk *hunk = nullptr;
int result = git_patch_get_hunk(&hunk, nullptr, d.data(), hidx); 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 { 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; const git_diff_hunk *hunk = nullptr;
int result = git_patch_get_hunk(&hunk, nullptr, d.data(), hidx); 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 { int Patch::lineCount(int hidx) const {
@ -235,7 +235,7 @@ char Patch::lineOrigin(int hidx, int ln) const {
const git_diff_line *line = nullptr; const git_diff_line *line = nullptr;
int result = git_patch_get_line_in_hunk(&line, d.data(), hidx, ln); 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 { 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; const git_diff_line *line = nullptr;
int result = git_patch_get_line_in_hunk(&line, d.data(), hidx, int result = git_patch_get_line_in_hunk(&line, d.data(), hidx,
0); // TODO: line index 0? 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 { 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; const git_diff_line *line = nullptr;
int result = git_patch_get_line_in_hunk(&line, d.data(), hidx, ln); 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) { Patch::ConflictResolution Patch::conflictResolution(int hidx) {
@ -435,6 +436,7 @@ void Patch::apply(QList<QList<QByteArray>> &image, int hidx, int start_line,
break; break;
default: default:
// no-op
break; break;
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -93,7 +93,19 @@ Lexer::Lexeme GenericLexer::next() {
} }
break; 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); Q_ASSERT(false);
} }
} }

View File

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

View File

@ -182,16 +182,24 @@ public:
QList<git::Commit> rhs = mRhs->commits(index); QList<git::Commit> rhs = mRhs->commits(index);
QList<git::Commit> commits = mLhs->commits(index); QList<git::Commit> commits = mLhs->commits(index);
if (mKind == And) { 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); QSet<git::Commit> set = QSet<git::Commit>::fromList(rhs);
#endif
QMutableListIterator<git::Commit> it(commits); QMutableListIterator<git::Commit> it(commits);
while (it.hasNext()) { while (it.hasNext()) {
if (!set.contains(it.next())) if (!set.contains(it.next()))
it.remove(); it.remove();
} }
} else { } 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); QSet<git::Commit> set = QSet<git::Commit>::fromList(commits);
#endif
foreach (const git::Commit &commit, rhs) { foreach (const git::Commit &commit, rhs) {
if (!set.contains(commit)) if (!set.contains(commit))
commits.append(commit); commits.append(commit);

View File

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

View File

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

View File

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

View File

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

View File

@ -8,6 +8,7 @@
// //
#include "Plugin.h" #include "Plugin.h"
#include "qtsupport.h"
#include "conf/Settings.h" #include "conf/Settings.h"
#include "editor/TextEditor.h" #include "editor/TextEditor.h"
#include "git/Config.h" #include "git/Config.h"
@ -491,7 +492,7 @@ Plugin::Plugin(const QString &file, const git::Repository &repo,
// Print error messages to the console. // Print error messages to the console.
connect(this, &Plugin::error, [](const QString &msg) { connect(this, &Plugin::error, [](const QString &msg) {
QTextStream(stderr) << "plugin error: " << msg << endl; QTextStream(stderr) << "plugin error: " << msg << Qt::endl;
}); });
// Load libraries. // Load libraries.
@ -613,6 +614,8 @@ QVariant Plugin::optionValue(const QString &key) const {
case String: case String:
return config().value<QString>(kKeyFmt.arg(mName, key), value.toString()); 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 { 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 add_library(tools DiffTool.cpp EditTool.cpp ExternalTool.cpp MergeTool.cpp
ShowTool.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) set_target_properties(tools PROPERTIES AUTOMOC ON)

View File

@ -12,6 +12,7 @@
#include "git/Repository.h" #include "git/Repository.h"
#include <QProcess> #include <QProcess>
#include <QTemporaryFile> #include <QTemporaryFile>
#include <QDebug>
DiffTool::DiffTool(const QString &file, const git::Blob &localBlob, DiffTool::DiffTool(const QString &file, const git::Blob &localBlob,
const git::Blob &remoteBlob, QObject *parent) const git::Blob &remoteBlob, QObject *parent)
@ -59,8 +60,25 @@ bool DiffTool::start() {
// Destroy this after process finishes. // Destroy this after process finishes.
QProcess *process = new QProcess(this); QProcess *process = new QProcess(this);
process->setProcessChannelMode(
QProcess::ProcessChannelMode::ForwardedChannels);
auto signal = QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished); 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(); QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("LOCAL", local->fileName()); env.insert("LOCAL", local->fileName());
@ -78,9 +96,12 @@ bool DiffTool::start() {
emit error(BashNotFound); emit error(BashNotFound);
return false; return false;
} }
#endif
if (!process->waitForStarted()) if (!process->waitForStarted()) {
qDebug() << "DiffTool starting failed";
return false; return false;
}
// Detach from parent. // Detach from parent.
setParent(nullptr); setParent(nullptr);

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