Skip to content

Commit

Permalink
Merge pull request #621 from mwerle/feature/rename_branch_menu
Browse files Browse the repository at this point in the history
Feature/rename branch menu
  • Loading branch information
Murmele authored Aug 30, 2023
2 parents cc78ce2 + 4ef8b60 commit e6b5f83
Show file tree
Hide file tree
Showing 11 changed files with 205 additions and 30 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ build
CMakeLists.txt.user
cmake-build-debug/
cmake-build-release/
build
.idea/
.venv
33 changes: 25 additions & 8 deletions cl-fmt.sh
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
#!/bin/bash

SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
cd "`dirname "$0"`"

# Variable that will hold the name of the clang-format command
FMT=""

FOLDERS=("./src" "./test")

# Some distros just call it clang-format. Others (e.g. Ubuntu) are insistent
# that the version number be part of the command. We prefer clang-format if
# that's present, otherwise we work backwards from highest version to lowest
# version but at least 13.
for clangfmt in clang-format{,-{1,2,3}{9,8,7,6,5,4,3}}; do
if which "$clangfmt" &>/dev/null; then
# We specifically require clang-format v13. Some distros include the version
# number in the name, others don't. Prefer the specifically-named version.
for clangfmt in clang-format-13 clang-format
do
if command -v "$clangfmt" &>/dev/null; then
FMT="$clangfmt"
break
fi
Expand All @@ -24,6 +24,14 @@ if [ -z "$FMT" ]; then
exit 1
fi

# Check we have v13 of clang-format
VERSION=`$FMT --version | grep -Po 'version\s\K(\d+)'`
if [ "$VERSION" != "13" ]; then
echo "Found clang-format v$VERSION, but v13 is required. Please install v13 of clang-format and try again."
echo "On Debian-derived distributions, this can be done via: apt install clang-format-13"
exit 1
fi

function format() {
for f in $(find $@ \( -type d -path './test/dep/*' -prune \) -o \( -name '*.h' -or -name '*.m' -or -name '*.mm' -or -name '*.c' -or -name '*.cpp' \)); do
echo "format ${f}";
Expand All @@ -41,9 +49,18 @@ for dir in ${FOLDERS[@]}; do
fi
done

# Format cmake files
# NOTE: requires support for python venv; on Debian-like distros, this can be
# installed using apt install python3-venv
echo "Start formatting cmake files"
pip install cmake-format==0.6.13
CMAKE_FORMAT=${SCRIPT_DIR}/.venv/bin/cmake-format
if [ ! -f "$CMAKE_FORMAT" ]; then
pushd ${SCRIPT_DIR}
python3 -m venv .venv
.venv/bin/pip install cmake-format==0.6.13
popd
fi
find . \
\( -type d -path './test/dep/*' -prune \) \
-o \( -type d -path './dep/*/*' -prune \) \
-o \( -name CMakeLists.txt -exec cmake-format --in-place {} + \)
-o \( -name CMakeLists.txt -exec "$CMAKE_FORMAT" --in-place {} + \)
7 changes: 7 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,15 @@ 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
Expand Down
1 change: 1 addition & 0 deletions src/dialogs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ add_library(
RebaseConflictDialog.cpp
RemoteDialog.cpp
RemoteTableModel.cpp
RenameBranchDialog.cpp
SettingsDialog.cpp
StartDialog.cpp
SubmoduleDelegate.cpp
Expand Down
58 changes: 58 additions & 0 deletions src/dialogs/RenameBranchDialog.cpp
Original file line number Diff line number Diff line change
@@ -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(); }
33 changes: 33 additions & 0 deletions src/dialogs/RenameBranchDialog.h
Original file line number Diff line number Diff line change
@@ -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
81 changes: 59 additions & 22 deletions src/ui/CommitList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1426,6 +1426,24 @@ void CommitList::setModel(QAbstractItemModel *model) {
restoreSelection();
}

/// @brief Helper function to add a list of items to a menu.
/// A single item is added directly to the menu, whereas multiple items will
/// be added to a sub-menu.
static void addMenuEntries(QMenu &menu, const QString &operation,
const QList<git::Reference> &items,
std::function<void(const git::Reference &)> action) {
QMenu *submenu = &menu;
QString entryName(operation + " %1");
if (items.count() > 1) {
submenu = menu.addMenu(operation);
entryName = QString("%1");
}
for (const git::Reference &ref : items) {
submenu->addAction(entryName.arg(ref.name()),
[action, ref] { action(ref); });
}
}

void CommitList::contextMenuEvent(QContextMenuEvent *event) {
QModelIndex index = indexAt(event->pos());
if (!index.isValid())
Expand Down Expand Up @@ -1496,26 +1514,41 @@ void CommitList::contextMenuEvent(QContextMenuEvent *event) {
menu.addAction(tr("New Branch..."),
[view, commit] { view->promptToCreateBranch(commit); });

bool separator = true;
foreach (const git::Reference &ref, commit.refs()) {
// Add operations on existing references; there may be 0, 1, or multiple
// of each type of reference on a commit.
QList<git::Reference> rename_branches;
QList<git::Reference> tags;
QList<git::Reference> delete_branches;
QList<git::Reference> all_branches; // used later
for (const git::Reference &ref : commit.refs()) {
if (ref.isTag()) {
if (separator) {
menu.addSeparator();
separator = false;
}
menu.addAction(tr("Delete Tag %1").arg(ref.name()),
[view, ref] { view->promptToDeleteTag(ref); });
}
if (ref.isLocalBranch() && (view->repo().head().name() != ref.name())) {
if (separator) {
menu.addSeparator();
separator = false;
tags.append(ref);
} else if (ref.isBranch()) {
all_branches.append(ref);
if (ref.isLocalBranch()) {
rename_branches.append(ref);
if (view->repo().head().name() != ref.name()) {
delete_branches.append(ref);
}
}
menu.addAction(tr("Delete Branch %1").arg(ref.name()),
[view, ref] { view->promptToDeleteBranch(ref); });
}
}

if (rename_branches.count() > 0 || delete_branches.count() > 0 ||
tags.count() > 0) {
menu.addSeparator();
}
addMenuEntries(menu, tr("Rename Branch"), rename_branches,
std::bind(&RepoView::promptToRenameBranch, view,
std::placeholders::_1));

addMenuEntries(menu, tr("Delete Branch"), delete_branches,
std::bind(&RepoView::promptToDeleteBranch, view,
std::placeholders::_1));

addMenuEntries(
menu, tr("Delete Tag"), tags,
std::bind(&RepoView::promptToDeleteTag, view, std::placeholders::_1));
menu.addSeparator();

menu.addAction(tr("Merge..."), [view, commit] {
Expand Down Expand Up @@ -1573,19 +1606,23 @@ void CommitList::contextMenuEvent(QContextMenuEvent *event) {
menu.addSeparator();

git::Reference head = view->repo().head();
foreach (const git::Reference &ref, commit.refs()) {
auto submenu = &menu;
auto entryName = tr("Checkout %1");
if (all_branches.count() > 1) {
submenu = menu.addMenu(tr("Checkout"));
entryName = QString("%1");
}
for (const git::Reference &ref : all_branches) {
if (ref.isLocalBranch()) {
QAction *checkout =
menu.addAction(tr("Checkout %1").arg(ref.name()),
[view, ref] { view->checkout(ref); });
QAction *checkout = submenu->addAction(
entryName.arg(ref.name()), [view, ref] { view->checkout(ref); });

checkout->setEnabled(head.isValid() &&
head.qualifiedName() != ref.qualifiedName() &&
!view->repo().isBare());
} else if (ref.isRemoteBranch()) {
QAction *checkout =
menu.addAction(tr("Checkout %1").arg(ref.name()),
[view, ref] { view->checkout(ref); });
QAction *checkout = submenu->addAction(
entryName.arg(ref.name()), [view, ref] { view->checkout(ref); });

// Calculate local branch name in the same way as checkout() does
QString local = ref.name().section('/', 1);
Expand Down
10 changes: 10 additions & 0 deletions src/ui/MenuBar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,9 @@ static Hotkey configureBranchesHotkey = HotkeyManager::registerHotkey(
static Hotkey newBranchHotkey =
HotkeyManager::registerHotkey(nullptr, "branch/new", "Branch/New");

static Hotkey renameBranchHotkey =
HotkeyManager::registerHotkey(nullptr, "branch/rename", "Branch/Rename");

static Hotkey checkoutCurrentHotkey = HotkeyManager::registerHotkey(
"Ctrl+Shift+Alt+H", "branch/checkoutCurrent", "Branch/Checkout Current");

Expand Down Expand Up @@ -640,6 +643,12 @@ MenuBar::MenuBar(QWidget *parent) : QMenuBar(parent) {
connect(mNewBranch, &QAction::triggered,
[this] { view()->promptToCreateBranch(); });

mRenameBranch = branch->addAction(tr("Rename Branch"));
renameBranchHotkey.use(mRenameBranch);
connect(mRenameBranch, &QAction::triggered, [this] {
this->view()->promptToRenameBranch(this->view()->reference());
});

branch->addSeparator();

mCheckoutCurrent = branch->addAction(tr("Checkout Current"));
Expand Down Expand Up @@ -1050,6 +1059,7 @@ void MenuBar::updateBranch() {
mCheckoutCurrent->setEnabled(ref.isValid() && head.isValid() &&
ref.qualifiedName() != head.qualifiedName());
mCheckout->setEnabled(head.isValid() && !view->repo().isBare());
mRenameBranch->setEnabled(ref.isLocalBranch());
mNewBranch->setEnabled(head.isValid());

mMerge->setEnabled(head.isValid());
Expand Down
1 change: 1 addition & 0 deletions src/ui/MenuBar.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ class MenuBar : public QMenuBar {
QAction *mNewBranch;
QAction *mCheckoutCurrent;
QAction *mCheckout;
QAction *mRenameBranch;
QAction *mMerge;
QAction *mRebase;
QAction *mSquash;
Expand Down
8 changes: 8 additions & 0 deletions src/ui/RepoView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "dialogs/NewBranchDialog.h"
#include "dialogs/RebaseConflictDialog.h"
#include "dialogs/RemoteDialog.h"
#include "dialogs/RenameBranchDialog.h"
#include "dialogs/SettingsDialog.h"
#include "dialogs/TagDialog.h"
#include "editor/TextEditor.h"
Expand Down Expand Up @@ -2051,6 +2052,13 @@ void RepoView::promptToDeleteBranch(const git::Reference &ref) {
dialog->open();
}

void RepoView::promptToRenameBranch(const git::Branch &branch) {
Q_ASSERT(branch.isValid() && branch.isLocalBranch());
RenameBranchDialog *dialog = new RenameBranchDialog(mRepo, branch, this);
// The dialog contains the code which performs the rename
dialog->open();
}

void RepoView::promptToStash() {
// Prompt to edit stash commit message.
if (!Settings::instance()->prompt(Prompt::Kind::Stash)) {
Expand Down
1 change: 1 addition & 0 deletions src/ui/RepoView.h
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ class RepoView : public QSplitter {
const git::Branch &upstream = git::Branch(),
bool checkout = false, bool force = false);
void promptToDeleteBranch(const git::Reference &ref);
void promptToRenameBranch(const git::Branch &branch);

// stash
void promptToStash();
Expand Down

0 comments on commit e6b5f83

Please sign in to comment.