Merge remote-tracking branch 'origin/master' into libgit2update

This commit is contained in:
Martin Marmsoler 2024-06-11 10:55:02 +02:00
commit ebb739b3bb
39 changed files with 3835 additions and 3267 deletions

View File

@ -46,9 +46,7 @@ jobs:
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
flatpak install --assumeyes org.freedesktop.Sdk.Extension.golang//23.08
- name: Show environment variables
run: >
@ -67,57 +65,53 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v3
- name: Clone flatpak manifest from Flathub
uses: GuillaumeFalourd/clone-github-repo-action@v2
with:
owner: 'flathub'
repository: 'com.github.Murmele.Gittyup'
- name: Replace git tag by the commit id on which it runs
if: github.ref_type != 'tag'
run: >
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
sed -i 's@tag: .*@commit: "${{ (github.event.pull_request && github.event.pull_request.head.sha) || github.sha }}"@' com.github.Murmele.Gittyup.yml
- name: Use correct git tag
if: github.ref_type == 'tag'
run: >
sed -i 's@tag: .*$@tag: "${{ github.ref_name }}"@' com.github.Murmele.Gittyup/com.github.Murmele.Gittyup.yml
sed -i 's@tag: .*$@tag: "${{ github.ref_name }}"@' com.github.Murmele.Gittyup.yml
- name: Replace source url
run: >
sed -i "s@url: .*Gittyup.git@url: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY@" com.github.Murmele.Gittyup/com.github.Murmele.Gittyup.yml
sed -i "s@url: .*Gittyup.git@url: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY@" com.github.Murmele.Gittyup.yml
- name: Add dev build marker to cmake options
if: github.ref_type != 'tag'
run: >
sed -i 's@config-opts: \["\(.*\)"\]@config-opts: ["\1", "-DDEV_BUILD=${{ github.ref_name }}"]@' com.github.Murmele.Gittyup/com.github.Murmele.Gittyup.yml
sed -i 's@config-opts: \["\(.*\)"\]@config-opts: ["\1", "-DDEV_BUILD=${{ github.ref_name }}"]@' com.github.Murmele.Gittyup.yml
- name: Replace desktop file name suffix
if: github.ref_type != 'tag'
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.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
sed -i 's@-DENABLE_UPDATE_OVER_GUI=OFF@-DENABLE_UPDATE_OVER_GUI=ON@' com.github.Murmele.Gittyup.yml
- name: Show Flatpak manifest
run: cat com.github.Murmele.Gittyup/com.github.Murmele.Gittyup.yml
run: cat com.github.Murmele.Gittyup.yml
- name: Build package
uses: bilelmoussaoui/flatpak-github-actions/flatpak-builder@v4
uses: bilelmoussaoui/flatpak-github-actions/flatpak-builder@v6
with:
bundle: Gittyup.flatpak
manifest-path: com.github.Murmele.Gittyup/com.github.Murmele.Gittyup.yml
manifest-path: com.github.Murmele.Gittyup.yml
cache: false
upload-artifact: true
verbose: true
branch: ${{ steps.flatpak_release_branch.outputs.value }}
- name: Publish build artifacts
uses: actions/upload-artifact@v2
with:
path: com.github.Murmele.Gittyup/*
name: Gittyup Flatpak
path: Gittyup.flatpak
name: GittyupFlatpak
build:
@ -145,7 +139,7 @@ jobs:
cmake_env: {}
- name: macos
os: macos-latest
os: macos-12
ninja_platform: mac
qt_platform: mac
openssl_arch: darwin64-x86_64-cc
@ -230,9 +224,9 @@ jobs:
modules: qtwebengine
- name: Install Ninja
uses: seanmiddleditch/gha-setup-ninja@v3
uses: seanmiddleditch/gha-setup-ninja@v4
with:
version: 1.9.0
version: 1.11.1
platform: ${{ matrix.env.ninja_platform }}
destination: ninja
@ -388,17 +382,6 @@ jobs:
uses: actions/download-artifact@v3
with:
path: artifacts
- name: list artifacts folder
run: |
echo "Show artifacts folder:"
ls artifacts
echo "Show artifacts/Gittyup Flatpak folder:"
ls "artifacts/Gittyup Flatpak"
echo "Show artifacts/Gittyup-x86_64:"
ls "artifacts/Gittyup-x86_64"
echo "Show artifacts/Gittyup VERSION"
ls "artifacts/Gittyup-VERSION"
# version is exported from cmake to file
- name: Retrieve version
@ -417,9 +400,8 @@ jobs:
**/artifacts/Gittyup win64/Gittyup*.exe
**/artifacts/Gittyup win32/Gittyup*.exe
**/artifacts/Gittyup macos/Gittyup*.dmg
**/artifacts/Gittyup Flatpak/com.github.Murmele.Gittyup.yml
**/Gittyup-x86_64/*.flatpak
**/artifacts/GittyupAppImage/*
**/artifacts/GittyupFlatpak/*.flatpak
**/artifacts/GittyupAppImage/Gittyup*.AppImage
- name: Update GitHub release (version tag)
uses: marvinpinto/action-automatic-releases@latest
@ -433,9 +415,8 @@ jobs:
**/artifacts/Gittyup win64/Gittyup*.exe
**/artifacts/Gittyup win32/Gittyup*.exe
**/artifacts/Gittyup macos/Gittyup*.dmg
**/artifacts/Gittyup Flatpak/com.github.Murmele.Gittyup.yml
**/Gittyup-x86_64/*.flatpak
**/artifacts/GittyupAppImage/*
**/artifacts/GittyupFlatpak/*.flatpak
**/artifacts/GittyupAppImage/Gittyup*.AppImage
# needed otherwise the docs folder is not available
- name: Checkout repository

2
.gitignore vendored
View File

@ -11,4 +11,4 @@ build
.idea/
.venv
compile_commands.json
tags

View File

@ -6,7 +6,7 @@ set(GITTYUP_NAME "Gittyup")
set(GITTYUP_EXECUTABLE_NAME "gittyup")
set(GITTYUP_IDENTIFIER "com.github.Murmele.Gittyup")
set(GITTYUP_VERSION_MAJOR 1)
set(GITTYUP_VERSION_MINOR 3)
set(GITTYUP_VERSION_MINOR 4)
set(GITTYUP_VERSION_PATCH 0)
set(GITTYUP_VERSION
"${GITTYUP_VERSION_MAJOR}.${GITTYUP_VERSION_MINOR}.${GITTYUP_VERSION_PATCH}"

View File

@ -1,7 +1,7 @@
MIT License
Copyright (c) 2018 Scientific Toolworks, Inc.
Copyright (c) 2021-2023 Gittyup contributors
Copyright (c) 2021-2024 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

View File

@ -92,7 +92,7 @@ How to Build
**Initialize Submodules**
git submodule init
git submodule update
git submodule update --depth 1
**Build OpenSSL**
@ -194,7 +194,7 @@ branch. Create pull requests against the `master` branch. Follow the
[seven guidelines](https://chris.beams.io/posts/git-commit/) to writing a
great commit message.
Prior to committing a change, please use `cl-format.sh` to ensure your code
Prior to committing a change, please use `cl-fmt.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.

View File

@ -0,0 +1,75 @@
app-id: com.github.Murmele.Gittyup
runtime: org.kde.Platform
runtime-version: 5.15-23.08
sdk: org.kde.Sdk
command: gittyup
desktop-file-name-suffix: "" # used to create development version
finish-args:
- --socket=wayland
- --socket=fallback-x11
- --device=dri
- --share=network
- --share=ipc
- --filesystem=home
- --filesystem=/tmp # Needed to store temporary files, which should be opened by external (non sandboxed) applications like kdiff3. Did not find another way to share a temporary file to the host (only creating a tmp folder in the home folder, I don't like)
# we use the keyring to store credentials
- --filesystem=xdg-run/keyring
# for git repos that require ssh keys
- --socket=ssh-auth
- --talk-name=org.freedesktop.secrets
- --talk-name=org.freedesktop.Notifications
- --talk-name=org.freedesktop.Flatpak
rename-icon: gittyup # Image will renamed to match the app-id konvention
rename-desktop-file: gittyup.desktop
rename-appdata-file: gittyup.appdata.xml
build-options:
append-path: /usr/lib/sdk/golang/bin
modules:
- name: git-lfs
buildsystem: simple
build-commands:
- PREFIX=${FLATPAK_DEST} ./install.sh
sources:
- type: archive
strip-components: 1
url: https://github.com/git-lfs/git-lfs/releases/download/v3.5.1/git-lfs-linux-amd64-v3.5.1.tar.gz
sha256: 6f28eb19faa7a968882dca190d92adc82493378b933958d67ceaeb9ebe4d731e
only-arches: [x86_64]
x-checker-data:
type: json
url: https://api.github.com/repos/git-lfs/git-lfs/releases/latest
url-query: .assets[] | select(.name=="git-lfs-linux-amd64-" + $version +
".tar.gz") | .browser_download_url
version-query: .tag_name
- type: archive
strip-components: 1
url: https://github.com/git-lfs/git-lfs/releases/download/v3.5.1/git-lfs-linux-arm64-v3.5.1.tar.gz
sha256: 4f8700aacaa0fd26ae5300fb0996aed14d1fd0ce1a63eb690629c132ff5163a9
only-arches: [aarch64]
x-checker-data:
type: json
url: https://api.github.com/repos/git-lfs/git-lfs/releases/latest
url-query: .assets[] | select(.name=="git-lfs-linux-arm64-" + $version +
".tar.gz") | .browser_download_url
version-query: .tag_name
- name: git
buildsystem: simple
build-commands:
- install -Dm755 $(which git) ${FLATPAK_DEST}/bin/
- install -Dm755 $(which git-cvsserver) ${FLATPAK_DEST}/bin/
- install -Dm755 $(which git-receive-pack) ${FLATPAK_DEST}/bin/
- install -Dm755 $(which git-shell) ${FLATPAK_DEST}/bin/
- install -Dm755 $(which git-upload-archive) ${FLATPAK_DEST}/bin/
- install -Dm755 $(which git-upload-pack) ${FLATPAK_DEST}/bin/
- name: Gittyup
buildsystem: cmake-ninja
config-opts: [-DCMAKE_BUILD_TYPE=Release, -DFLATPAK=ON, -DGENERATE_APPDATA=ON, -DENABLE_UPDATE_OVER_GUI=OFF, -DUSE_SYSTEM_OPENSSL:BOOL=ON]
builddir: true
sources:
- type: git
url: https://github.com/Murmele/Gittyup.git
tag: gittyup_v1.3.0

View File

@ -14,6 +14,9 @@ return {
family = family,
size = size
},
view = {
showWhitespace = false
},
indent = {
tabs = true,
width = 4,

View File

@ -1,4 +1,4 @@
### vX.X.X - 2023-04-20 (DEV)
### v1.4.0 - 2024-04-24 (DEV)
Description
@ -6,12 +6,28 @@ Description
* UI(Commit List): Added a right-click menu entry to rename branches.
* UI(Main Menu): Added a menu-entry to rename the current branch.
* UI(Diff View): Added line wrapping option in Tools - Options - Diff
* Option to open Gittyup maximized at startup
* Hotkeys for Navigating CommitList
* Referring repos with their git-dir instead of their work-tree.
* Use columns for file name, directory, and state when files are shown as a list in TreeViews.
* Possibility to change language from the settings
* Linux: Publishing Appimage bundle
* Hide Untracked Files option to DoubleTreeWidget
* Add display options to the commit list
#### 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.
* Fix: Commit highlighting
* Compact commit list: move commit message closer to commit graph
* Updating translations
* Close the branch drop-down list after the user checks out a branch.
* Fix: Delete timeline causes crash on windows
* Fix: Rebase: Use original commit message as default message
* Fix: Stage all button
----

View File

@ -79,6 +79,11 @@ See blame of the current version with an integrated timeline to see who changed
![Blame View](https://raw.githubusercontent.com/Murmele/Gittyup/master/rsrc/screenshots/BlameView.png)
### Dynamic Line Wrapping
Courtesy of Scintilla.
![Line Wrapping](/rsrc/screenshots/line-wrap-demo-2.gif)
### Single line staging
by eighter clicking on the checkboxes next to each line or by selecting the relevant code and pressing "S". For unstaging you can uncheck the checkboxes or press "U". To revert changes, select the text and press "R".

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

View File

@ -46,7 +46,6 @@
<provides>
<binary>gittyup</binary>
<id>com.github.Murmele.Gittyup</id>
</provides>
<releases>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

View File

@ -12,6 +12,7 @@ void Setting::initialize(QMap<Id, QString> &keys) {
keys[Id::IndentWidth] = "editor/indent/width";
keys[Id::TabWidth] = "editor/indent/tabwidth";
keys[Id::ShowHeatmapInBlameMargin] = "editor/blame/heatmap";
keys[Id::ShowWhitespaceInEditor] = "editor/view/showWhitespace";
keys[Id::ColorTheme] = "window/theme";
keys[Id::ShowFullRepoPath] = "window/path/full";
keys[Id::HideLogAutomatically] = "window/log/hide";
@ -19,6 +20,7 @@ void Setting::initialize(QMap<Id, QString> &keys) {
keys[Id::OpenAllReposInTabs] = "window/tabs/repository";
keys[Id::HideMenuBar] = "window/view/menuBarHidden";
keys[Id::ShowAvatars] = "window/view/avatarsVisible";
keys[Id::ShowMaximized] = "window/view/showMaximized";
keys[Id::AutoCollapseAddedFiles] = "collapse/added";
keys[Id::AutoCollapseDeletedFiles] = "collapse/deleted";
keys[Id::FilemanagerCommand] = "filemanager/command";

View File

@ -36,6 +36,7 @@ public:
IndentWidth,
TabWidth,
ShowHeatmapInBlameMargin,
ShowWhitespaceInEditor,
ColorTheme,
ShowFullRepoPath,
HideLogAutomatically,
@ -43,6 +44,7 @@ public:
OpenAllReposInTabs,
HideMenuBar,
ShowAvatars,
ShowMaximized,
AutoCollapseAddedFiles,
AutoCollapseDeletedFiles,
FilemanagerCommand,

View File

@ -25,6 +25,7 @@
namespace {
const QString kWrapLines("diff/lines/wrap");
const QString kIgnoreWsKey("diff/whitespace/ignore");
const QString kLastPathKey("lastpath");
const QString kTranslation("translation");
@ -191,6 +192,14 @@ QString Settings::hotkey(const QString &action) const {
return value("hotkeys/" + action, "").toString();
}
bool Settings::isTextEditorWrapLines() const {
return value(kWrapLines).toBool();
}
void Settings::setTextEditorWrapLines(bool wrap) {
setValue(kWrapLines, wrap, true);
}
bool Settings::isWhitespaceIgnored() const {
return value(kIgnoreWsKey).toBool();
}

View File

@ -36,6 +36,10 @@ public:
void setHotkey(const QString &action, const QString &hotkey);
QString hotkey(const QString &action) const;
// wrap lines in TextEditor in DiffView
bool isTextEditorWrapLines() const;
void setTextEditorWrapLines(bool wrapLines);
// ignore whitespace
bool isWhitespaceIgnored() const;
void setWhitespaceIgnored(bool ignored);

View File

@ -69,8 +69,16 @@ DiffPanel::DiffPanel(const git::Repository &repo, QWidget *parent)
}
});
// Wrap lines
QCheckBox *wrapLines = new QCheckBox(tr("Wrap lines"), this);
wrapLines->setChecked(Settings::instance()->isTextEditorWrapLines());
connect(wrapLines, &QCheckBox::toggled, [](bool wrap) {
Settings::instance()->setTextEditorWrapLines(wrap);
});
QFormLayout *layout = new QFormLayout(this);
layout->addRow(tr("Context lines:"), contextLayout);
layout->addRow(tr("Wrap lines:"), wrapLines);
layout->addRow(tr("Character Encoding:"), encoding);
// Remaining settings are strictly global.

View File

@ -565,6 +565,13 @@ public:
connect(showAvatars, &QCheckBox::toggled, [](bool checked) {
Settings::instance()->setValue(Setting::Id::ShowAvatars, checked);
});
QCheckBox *showMaximized =
new QCheckBox(tr("Show Window Maximized when opened"));
showMaximized->setChecked(
settings->value(Setting::Id::ShowMaximized).toBool());
connect(showMaximized, &QCheckBox::toggled, [](bool checked) {
Settings::instance()->setValue(Setting::Id::ShowMaximized, checked);
});
QString mergeText = settings->promptDescription(Prompt::Kind::Merge);
QCheckBox *merge = new QCheckBox(mergeText, this);
@ -619,6 +626,7 @@ public:
layout->addRow(QString(), repoTabs);
layout->addRow(tr("View:"), hideMenuBar);
layout->addRow(QString(), showAvatars);
layout->addRow(QString(), showMaximized);
layout->addRow(tr("Prompt:"), merge);
layout->addRow(QString(), revert);
layout->addRow(QString(), cherryPick);
@ -653,6 +661,14 @@ public:
Settings::instance()->setValue(Setting::Id::FontSize, i);
});
QCheckBox *showWhitespace = new QCheckBox(tr("Show whitespace"), this);
showWhitespace->setChecked(
settings->value(Setting::Id::ShowWhitespaceInEditor).toBool());
connect(showWhitespace, &QCheckBox::toggled, [](bool checked) {
Settings::instance()->setValue(Setting::Id::ShowWhitespaceInEditor,
checked);
});
QComboBox *indent = new QComboBox(this);
indent->addItem(tr("Tabs"));
indent->addItem(tr("Spaces"));
@ -687,6 +703,7 @@ public:
QFormLayout *layout = new QFormLayout(this);
layout->addRow(tr("Font:"), font);
layout->addRow(tr("Font size:"), fontSize);
layout->addRow(tr("Whitespace:"), showWhitespace);
layout->addRow(tr("Indent using:"), indent);
layout->addRow(tr("Indent width:"), indentWidth);
layout->addRow(tr("Tab width:"), tabWidth);

View File

@ -76,6 +76,8 @@ TextEditor::TextEditor(QWidget *parent) : ScintillaIFace(parent) {
setScrollWidthTracking(true);
setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
setWrapMode(SC_WRAP_NONE);
setMarginLeft(4);
setMarginTypeN(Staged, SC_MARGIN_SYMBOL);
setMarginTypeN(LineNumber, SC_MARGIN_NUMBER);
@ -180,6 +182,13 @@ void TextEditor::applySettings() {
setUseTabs(settings->value(Setting::Id::UseTabsForIndent).toBool());
setIndent(settings->value(Setting::Id::IndentWidth).toInt());
setTabWidth(settings->value(Setting::Id::TabWidth).toInt());
setViewWS(settings->value(Setting::Id::ShowWhitespaceInEditor).toBool());
if (Settings::instance()->isTextEditorWrapLines()) {
setWrapMode(SC_WRAP_WORD);
} else {
setWrapMode(SC_WRAP_NONE);
}
// Initialize markers.
QColor background = palette().color(QPalette::Base);

View File

@ -154,9 +154,12 @@ Repository::operator git_repository *() const { return d->repo; }
QDir Repository::dir(bool includeGitFolder) const {
QDir dir(git_repository_path(d->repo));
if (!includeGitFolder) {
assert(dir.dirName() == ".git");
assert(dir.cdUp());
if (!includeGitFolder && dir.dirName() == ".git") {
if (!dir.cdUp()) {
assert(false); // must be done explicit, because in release build the
// assert will not be executed, so assert(dir.cdUp()) does
// not work in release build
}
}
return dir;
}

View File

@ -28,6 +28,7 @@
#include "git/Signature.h"
#include "git/TagRef.h"
#include "git/Tree.h"
#include "ui/HotkeyManager.h"
#include <QAbstractListModel>
#include <QApplication>
#include <QMenu>
@ -185,8 +186,8 @@ public:
// Update status row.
bool head = (!mRef.isValid() || mRef.isHead());
bool valid = (mCleanStatus || !mStatus.isFinished() || status().isValid());
if (head && valid && mPathspec.isEmpty()) {
bool valid = (!mStatus.isFinished() || status().isValid());
if (mShowCleanStatus && head && valid && mPathspec.isEmpty()) {
QVector<Column> row;
if (mGraphVisible && mRef.isValid() && mStatus.isFinished()) {
row.append({Segment(Bottom, kTaintedColor), Segment(Dot, QColor())});
@ -238,7 +239,7 @@ public:
git::Config config = mRepo.appConfig();
mRefsAll = config.value<bool>("commit.refs.all", true);
mSortDate = config.value<bool>("commit.sort.date", true);
mCleanStatus = config.value<bool>("commit.status.clean", false);
mShowCleanStatus = config.value<bool>("commit.show.status", true);
mGraphVisible = config.value<bool>("commit.graph.visible", true);
if (walk)
@ -558,7 +559,7 @@ private:
bool mSuppressResetWalker{false};
bool mRefsAll = true;
bool mSortDate = true;
bool mCleanStatus = true;
bool mShowCleanStatus = true;
bool mGraphVisible = true;
};
@ -635,9 +636,6 @@ public:
Settings::instance()->value(Setting::Id::ShowCommitsId, true).toBool();
LayoutConstants constants = layoutConstants(compact);
// Draw background.
QStyledItemDelegate::paint(painter, opt, index);
bool active = (opt.state & QStyle::State_Active);
bool selected = (opt.state & QStyle::State_Selected);
auto group = active ? QPalette::Active : QPalette::Inactive;
@ -646,10 +644,16 @@ public:
QPalette palette = Application::theme()->commitList();
QColor text = palette.color(group, textRole);
QColor bright = palette.color(group, brightRole);
QColor highlight = palette.color(group, QPalette::Highlight);
painter->save();
painter->setRenderHints(QPainter::Antialiasing);
// Draw background.
if (selected) {
painter->fillRect(opt.rect, highlight);
}
// Draw busy indicator.
if (opt.features & QStyleOptionViewItem::HasDecoration) {
QRect rect = decorationRect(option, index);
@ -784,7 +788,16 @@ public:
// Draw content.
git::Commit commit =
index.data(CommitList::Role::CommitRole).value<git::Commit>();
if (commit.isValid()) {
if (!commit.isValid()) {
// special case for uncommitted changes
QString message = index.model()->data(index).toString();
painter->save();
QFont italic = opt.font;
italic.setItalic(true);
painter->setFont(italic);
painter->drawText(opt.rect, Qt::AlignCenter, message);
painter->restore();
} else {
const QFontMetrics &fm = opt.fontMetrics;
QRect star = rect;
@ -829,6 +842,20 @@ public:
rect.setWidth(rect.width() - timestampWidth - constants.hMargin);
}
// Draw Name.
if (showAuthor) {
QString name = commit.author().name() + " ";
painter->save();
QFont bold = opt.font;
bold.setBold(true);
painter->setFont(bold);
painter->drawText(rect, Qt::AlignRight, name);
painter->restore();
const QFontMetrics boldFm(bold);
rect.setWidth(rect.width() - boldFm.horizontalAdvance(name) -
constants.hMargin);
}
// Calculate remaining width for the references.
QRect ref = rect;
int refsWidth = ref.width() - minWidthDesc;
@ -847,20 +874,6 @@ public:
badgesWidth = Badge::paint(painter, refs, ref, &opt, Qt::AlignLeft);
rect.setX(badgesWidth); // Comes right after the badges
// Draw Name.
if (showAuthor) {
QString name = commit.author().name();
painter->save();
QFont bold = opt.font;
bold.setBold(true);
painter->setFont(bold);
painter->drawText(rect, Qt::AlignLeft, name);
painter->restore();
const QFontMetrics boldFm(bold);
rect.setX(rect.x() + boldFm.horizontalAdvance(name) +
constants.hMargin);
}
// Draw message.
painter->save();
painter->setPen(bright);
@ -1156,6 +1169,12 @@ public:
} // namespace
static Hotkey selectCommitDownHotKey = HotkeyManager::registerHotkey(
"j", "commitList/selectCommitDown", "CommitList/Select Next Commit Down");
static Hotkey selectCommitUpHotKey = HotkeyManager::registerHotkey(
"k", "commitList/selectCommitUp", "CommitList/Select Next Commit Up");
CommitList::CommitList(Index *index, QWidget *parent)
: QListView(parent), mIndex(index) {
Theme *theme = Application::theme();
@ -1211,6 +1230,15 @@ CommitList::CommitList(Index *index, QWidget *parent)
connect(this, &CommitList::entered,
[this](const QModelIndex &index) { update(index); });
QShortcut *shortcut = new QShortcut(this);
selectCommitDownHotKey.use(shortcut);
connect(shortcut, &QShortcut::activated, [this] { selectCommitRelative(1); });
shortcut = new QShortcut(this);
selectCommitUpHotKey.use(shortcut);
connect(shortcut, &QShortcut::activated,
[this] { selectCommitRelative(-1); });
#ifdef Q_OS_MAC
QFont font = this->font();
font.setPointSize(13);
@ -1333,6 +1361,19 @@ void CommitList::selectFirstCommit(bool spontaneous) {
}
}
void CommitList::selectCommitRelative(int offset) {
QModelIndexList indices = selectionModel()->selectedIndexes();
QModelIndex index = indices[0];
if (!index.isValid()) {
return;
}
QModelIndex new_index = model()->index(index.row() + offset, index.column());
if (!new_index.isValid()) {
return;
}
selectIndexes(QItemSelection(new_index, new_index), QString(), true);
}
bool CommitList::selectRange(const QString &range, const QString &file,
bool spontaneous) {
// Try to select the "status" index.

View File

@ -47,6 +47,7 @@ public:
void selectReference(const git::Reference &ref);
void resetSelection(bool spontaneous = false);
void selectFirstCommit(bool spontaneous = false);
void selectCommitRelative(int offset);
bool selectRange(const QString &range, const QString &file = QString(),
bool spontaneous = false);
void suppressResetWalker(bool suppress);

View File

@ -25,7 +25,7 @@ namespace {
const QString kRefsKey = "commit.refs.all";
const QString kSortKey = "commit.sort.date";
const QString kGraphKey = "commit.graph.visible";
const QString kStatusKey = "commit.status.clean";
const QString kStatusKey = "commit.show.status";
const QString kStyleSheet = "QToolBar {"
" border: none"
"}"
@ -155,7 +155,7 @@ CommitToolBar::CommitToolBar(QWidget *parent) : QToolBar(parent) {
QAction *status = menu->addAction(tr("Show Clean Status"));
status->setCheckable(true);
status->setChecked(config.value<bool>(kStatusKey, false));
status->setChecked(config.value<bool>(kStatusKey, true));
connect(status, &QAction::triggered, [this](bool checked) {
RepoView *view = RepoView::parentView(this);
view->repo().appConfig().setValue(kStatusKey, checked);

View File

@ -500,6 +500,7 @@ DetailView::DetailView(const git::Repository &repo, QWidget *parent)
mDetail->setVisible(false);
layout->addWidget(mDetail);
// Shown when a commit is selected
mDetail->addWidget(new CommitDetail(this));
mAuthorLabel = new QLabel(this);
@ -508,6 +509,7 @@ DetailView::DetailView(const git::Repository &repo, QWidget *parent)
&DetailView::authorLinkActivated);
updateAuthor();
// Shown when the working directory is dirty
mCommitEditor = new CommitEditor(repo, this);
auto editorFrame = new QWidget(this);

View File

@ -15,6 +15,7 @@
#include "CommentWidget.h"
#include "ui/DiffTreeModel.h"
#include "ui/DoubleTreeWidget.h"
#include "ui/HotkeyManager.h"
#include "git/Tree.h"
#include <QScrollBar>
#include <QPushButton>
@ -47,6 +48,12 @@ bool copy(const QString &source, const QDir &targetDir) {
} // namespace
static Hotkey moveHalfPageDownHotKey = HotkeyManager::registerHotkey(
"d", "diffView/moveHalfPageDownHotKey", "DiffView/Move Half Page Down");
static Hotkey moveHalfPageUpHotKey = HotkeyManager::registerHotkey(
"u", "diffView/moveHalfPageUpHotKey", "DiffView/Move Half Page Up");
DiffView::DiffView(const git::Repository &repo, QWidget *parent)
: QScrollArea(parent), mParent(parent) {
setStyleSheet(DiffViewStyle::kStyleSheet);
@ -84,6 +91,14 @@ DiffView::DiffView(const git::Repository &repo, QWidget *parent)
fetchMore();
});
}
QShortcut *shortcut = new QShortcut(this);
moveHalfPageDownHotKey.use(shortcut);
connect(shortcut, &QShortcut::activated, [this] { moveHalfPageDown(); });
shortcut = new QShortcut(this);
moveHalfPageUpHotKey.use(shortcut);
connect(shortcut, &QShortcut::activated, [this] { moveHalfPageUp(); });
}
DiffView::~DiffView() {}
@ -513,3 +528,13 @@ void DiffView::indexChanged(const QStringList &paths) {
// }
// }
}
void DiffView::moveHalfPageDown() { moveRelative(height() / 2); }
void DiffView::moveHalfPageUp() { moveRelative(-height() / 2); }
void DiffView::moveRelative(int pixelsDown) {
int oldPosition = verticalScrollBar()->sliderPosition();
int newPosition = oldPosition + pixelsDown;
verticalScrollBar()->setSliderPosition(newPosition);
}

View File

@ -103,6 +103,9 @@ public:
void diffTreeModelDataChanged(const QModelIndex &topLeft,
const QModelIndex &bottomRight,
const QVector<int> &roles);
void moveHalfPageDown();
void moveHalfPageUp();
void moveRelative(int pixelsDown);
signals:
void diagnosticAdded(TextEditor::DiagnosticKind kind);

View File

@ -35,6 +35,17 @@ const QString noNewLineAtEndOfFile =
HunkWidget::tr("No newline at end of file");
} // namespace
_HunkWidget::HunkLabel::HunkLabel(const QString &name, bool submodule,
QWidget *parent)
: QWidget(parent), mName(name) {}
void _HunkWidget::HunkLabel::paintEvent(QPaintEvent *event) {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
QFontMetrics fm = fontMetrics();
QRect rect = fm.boundingRect(0, 0, this->rect().width(), 300, 0, mName);
painter.drawText(rect, Qt::AlignLeft | Qt::ElideRight, mName);
}
_HunkWidget::Header::Header(const git::Diff &diff, const git::Patch &patch,
int index, bool lfs, bool submodule,
QWidget *parent)
@ -45,9 +56,9 @@ _HunkWidget::Header::Header(const git::Diff &diff, const git::Patch &patch,
mCheck->setVisible(diff.isStatusDiff() && !submodule &&
!patch.isConflicted());
QString header = (index >= 0) ? patch.header(index) : QString();
QString escaped = header.trimmed().toHtmlEscaped();
QLabel *label = new QLabel(DiffViewStyle::kHunkFmt.arg(escaped), this);
QString label_string = (index >= 0) ? patch.header(index) : QString();
label_string = label_string.trimmed().toHtmlEscaped();
HunkLabel *label = new HunkLabel(label_string, submodule, this);
if (patch.isConflicted()) {
mSave = new QToolButton(this);
@ -132,7 +143,7 @@ _HunkWidget::Header::Header(const git::Diff &diff, const git::Patch &patch,
QHBoxLayout *layout = new QHBoxLayout(this);
layout->setContentsMargins(4, 4, 4, 4);
layout->addWidget(mCheck);
layout->addWidget(label);
layout->addWidget(label, 1);
layout->addStretch();
layout->addLayout(buttons);

View File

@ -19,6 +19,18 @@ class DisclosureButton;
class Line;
namespace _HunkWidget {
class HunkLabel : public QWidget {
public:
HunkLabel(const QString &name, bool submodule, QWidget *parent = nullptr);
protected:
void paintEvent(QPaintEvent *event) override;
private:
QString mName;
QString mOldName;
};
class Header : public QFrame {
Q_OBJECT
public:

View File

@ -201,7 +201,8 @@ void MainWindow::setSideBarVisible(bool visible) {
splitter->setSizes({static_cast<int>(pos * value), 1});
});
connect(timeline, &QTimeLine::finished, [timeline] { delete timeline; });
connect(timeline, &QTimeLine::finished,
[timeline] { timeline->deleteLater(); });
timeline->start();
}
@ -385,7 +386,15 @@ MainWindow *MainWindow::open(const git::Repository &repo) {
// Create the window.
MainWindow *window = new MainWindow(repo);
window->show();
const bool showMaximized =
Settings::instance()->value(Setting::Id::ShowMaximized).toBool();
if (showMaximized) {
window->showMaximized();
} else {
window->show();
}
return window;
}

View File

@ -890,7 +890,8 @@ void RepoView::setLogVisible(bool visible) {
setSizes({1, static_cast<int>(pos * value)});
});
connect(timeline, &QTimeLine::finished, [timeline] { delete timeline; });
connect(timeline, &QTimeLine::finished,
[timeline] { timeline->deleteLater(); });
timeline->start();
}
@ -1466,6 +1467,9 @@ void RepoView::rebaseConflict(const git::Rebase rebase) {
if (mRebase) {
mRebase->addEntry(tr("Please resolve conflicts before continue"),
tr("Conflict"));
mDetails->setCommitMessage(rebase.commitToRebase()
.message(git::Commit::SubstituteEmoji)
.trimmed());
}
refresh(false);
}
@ -2119,6 +2123,13 @@ void RepoView::dropStash(int index) {
LogEntry *entry = addLogEntry(msg(commit), tr("Drop Stash"));
if (!mRepo.dropStash(index))
error(entry, tr("drop stash"), commit.link());
if (mRepo.stashes().size() == 0) {
// switch back to head when there are no stashes left
mCommits->setReference(mRepo.head());
} else {
mCommits->setReference(mRepo.stashRef());
}
}
void RepoView::popStash(int index) {
@ -2131,8 +2142,9 @@ void RepoView::popStash(int index) {
error(entry, tr("pop stash"), commit.link());
return;
}
refresh(false);
// switch back to head
selectReference(mRepo.head());
selectFirstCommit();
}
void RepoView::promptToAddTag(const git::Commit &commit) {

@ -1 +1 @@
Subproject commit fefb8d6acc78b5f63d5a78b86a4900e694bd2862
Subproject commit 687b5424128fd637555be4a0d18c72c4e870bd6f

View File

@ -75,6 +75,7 @@ class TestRebase : public QObject {
private slots:
void withoutConflicts();
void conflictingRebase();
void conflictingRebaseCustomMessage();
void continueExternalStartedRebase(); // must have conflicts otherwise it is
// not possible to continue
void startRebaseContinueInCLI();
@ -278,10 +279,6 @@ void TestRebase::conflictingRebase() {
filewidgets.at(0)->stageStateChanged(filewidgets.at(0)->modelIndex(),
git::Index::StagedState::Staged);
QTextEdit *editor = repoView->findChild<QTextEdit *>("MessageEditor");
QVERIFY(editor);
editor->setText("Test message");
refreshTriggered = 0;
rebaseConflict = 0;
repoView->continueRebase();
@ -293,9 +290,8 @@ void TestRebase::conflictingRebase() {
QList<Commit> parents = branch.annotatedCommit().commit().parents();
QCOMPARE(parents.count(), 1);
QCOMPARE(parents.at(0).id(), ac.commit().id());
QCOMPARE(
branch.annotatedCommit().commit().message(),
"Test message"); // custom message was used instead of the original one
QCOMPARE(branch.annotatedCommit().commit().message(),
"File.txt changed by second branch"); // original message is shown
// Check that rebase was really finished
QCOMPARE(mRepo.rebaseOngoing(), false);
@ -313,6 +309,89 @@ void TestRebase::conflictingRebase() {
QCOMPARE(rebaseConflict, 0);
}
void TestRebase::conflictingRebaseCustomMessage() {
INIT_REPO("rebaseConflicts.zip", true);
auto *detailview = repoView->findChild<DetailView *>();
QVERIFY(detailview);
auto *abortRebaseButton = detailview->findChild<QPushButton *>("AbortRebase");
QVERIFY(abortRebaseButton);
auto *continueRebaseButton =
detailview->findChild<QPushButton *>("ContinueRebase");
QVERIFY(continueRebaseButton);
QCOMPARE(continueRebaseButton->isVisible(), false);
QCOMPARE(abortRebaseButton->isVisible(), false);
const QString rebaseBranchName = "refs/heads/singleCommitConflict";
git::Reference branch = mRepo.lookupRef(rebaseBranchName);
QVERIFY(branch.isValid());
auto c = branch.annotatedCommit().commit();
// Checkout correct branch
repoView->checkout(branch);
QTest::qWait(100);
// Rebase on main
git::Reference mainBranch = mRepo.lookupRef(QString("refs/heads/main"));
QVERIFY(mainBranch.isValid());
auto ac = mainBranch.annotatedCommit();
LogEntry *entry = repoView->addLogEntry("Rebase", "Rebase", nullptr);
repoView->rebase(ac, entry);
QCOMPARE(mRepo.rebaseOngoing(), true);
// Check that buttons are visible
QTest::qWait(100);
// Resolve conflicts
diff = mRepo.status(mRepo.index(), nullptr, false);
QCOMPARE(diff.count(), 1);
QCOMPARE(diff.patch(0).isConflicted(), true);
QFile f(mRepo.workdir().filePath(diff.patch(0).name()));
QCOMPARE(f.open(QIODevice::WriteOnly), true);
QVERIFY(f.write("Test123") !=
-1); // just write something to resolve the conflict
f.close();
QTest::qWait(100);
repoView->continueRebase(); // should fail
QTest::qWait(100); // Wait until refresh is done
// Staging the file
auto filewidgets = repoView->findChildren<FileWidget *>();
QCOMPARE(filewidgets.length(), 1);
filewidgets.at(0)->stageStateChanged(filewidgets.at(0)->modelIndex(),
git::Index::StagedState::Staged);
QTextEdit *editor = repoView->findChild<QTextEdit *>("MessageEditor");
QVERIFY(editor);
editor->setText("Test message"); // modify message
repoView->continueRebase();
// Check that branch is based on "main" now
branch = mRepo.lookupRef(rebaseBranchName);
QVERIFY(branch.isValid());
QList<Commit> parents = branch.annotatedCommit().commit().parents();
QCOMPARE(parents.count(), 1);
QCOMPARE(parents.at(0).id(), ac.commit().id());
QCOMPARE(branch.annotatedCommit().commit().message(),
"Test message"); // Modified message is shown
// Check that rebase was really finished
QCOMPARE(mRepo.rebaseOngoing(), false);
QTest::qWait(100); // Wait until refresh finished
// Check that buttons are visible
QCOMPARE(continueRebaseButton->isVisible(), false);
QCOMPARE(abortRebaseButton->isVisible(), false);
}
void TestRebase::continueExternalStartedRebase() {
// INIT_REPO("rebaseConflicts.zip", true);