renamed gitcomet-app to gitcomet

This commit is contained in:
Sampo Kivistö 2026-03-17 17:01:25 +02:00
parent 6703f2543d
commit edcfe68bf4
No known key found for this signature in database
GPG key ID: 3B426F446F481CFF
38 changed files with 236 additions and 191 deletions

View file

@ -142,14 +142,14 @@ jobs:
- name: Build release binary
shell: pwsh
run: |
cargo build -p gitcomet-app --release --locked --features ui-gpui,gix --bins
cargo build -p gitcomet --release --locked --features ui-gpui,gix --bins
- name: Package portable ZIP
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
New-Item -ItemType Directory -Path dist\portable -Force | Out-Null
Copy-Item target\release\gitcomet-app.exe dist\portable\gitcomet-app.exe -Force
Copy-Item target\release\gitcomet.exe dist\portable\gitcomet.exe -Force
Copy-Item README.md dist\portable\README.md -Force
Copy-Item LICENSE-AGPL-3.0 dist\portable\LICENSE-AGPL-3.0 -Force
Copy-Item NOTICE dist\portable\NOTICE -Force
@ -164,11 +164,11 @@ jobs:
$ErrorActionPreference = "Stop"
choco install wixtoolset --version "${env:WIXTOOLSET_VERSION}" --yes --no-progress
cargo install cargo-wix --version "${env:CARGO_WIX_VERSION}" --locked
if (!(Test-Path "crates\gitcomet-app\wix\main.wxs")) {
cargo wix init --package gitcomet-app
if (!(Test-Path "crates\gitcomet\wix\main.wxs")) {
cargo wix init --package gitcomet
}
$msiName = "gitcomet-v${env:VERSION}-windows-x86_64.msi"
cargo wix --package gitcomet-app --profile release --nocapture --no-build --output "dist\$msiName"
cargo wix --package gitcomet --profile release --nocapture --no-build --output "dist\$msiName"
- name: Upload Windows artifacts
uses: actions/upload-artifact@v7
@ -212,7 +212,7 @@ jobs:
uses: Swatinem/rust-cache@v2
- name: Build release binary
run: cargo build -p gitcomet-app --release --locked --features ui-gpui,gix
run: cargo build -p gitcomet --release --locked --features ui-gpui,gix
- name: Normalize desktop entry metadata
run: |
@ -227,7 +227,7 @@ jobs:
set -euo pipefail
root="gitcomet-v${VERSION}-linux-x86_64"
mkdir -p "dist/stage/${root}"
install -m755 target/release/gitcomet-app "dist/stage/${root}/gitcomet-app"
install -m755 target/release/gitcomet "dist/stage/${root}/gitcomet"
install -m644 README.md "dist/stage/${root}/README.md"
install -m644 LICENSE-AGPL-3.0 "dist/stage/${root}/LICENSE-AGPL-3.0"
install -m644 NOTICE "dist/stage/${root}/NOTICE"
@ -245,7 +245,7 @@ jobs:
mkdir -p "${pkg_root}/usr/share/applications"
mkdir -p "${pkg_root}/usr/share/icons/hicolor/512x512/apps"
install -m755 target/release/gitcomet-app "${pkg_root}/usr/bin/gitcomet-app"
install -m755 target/release/gitcomet "${pkg_root}/usr/bin/gitcomet"
install -m644 dist/gitcomet.desktop "${pkg_root}/usr/share/applications/gitcomet.desktop"
install -m644 assets/gitcomet-512.png "${pkg_root}/usr/share/icons/hicolor/512x512/apps/gitcomet.png"
@ -267,7 +267,7 @@ jobs:
set -euo pipefail
appdir="dist/AppDir"
mkdir -p "${appdir}/usr/bin"
install -m755 target/release/gitcomet-app "${appdir}/usr/bin/gitcomet-app"
install -m755 target/release/gitcomet "${appdir}/usr/bin/gitcomet"
install -m644 dist/gitcomet.desktop "${appdir}/gitcomet.desktop"
install -m644 assets/gitcomet-512.png "${appdir}/gitcomet.png"
@ -275,7 +275,7 @@ jobs:
echo '#!/usr/bin/env bash'
echo 'set -euo pipefail'
echo 'HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"'
echo 'exec "${HERE}/usr/bin/gitcomet-app" "$@"'
echo 'exec "${HERE}/usr/bin/gitcomet" "$@"'
} > "${appdir}/AppRun"
chmod +x "${appdir}/AppRun"

View file

@ -163,8 +163,8 @@ jobs:
echo "XDG_CURRENT_DESKTOP=${XDG_CURRENT_DESKTOP:-<unset>}"
- name: Run display-selection integration tests
run: |
cargo test -p gitcomet-app $APP_FEATURES --test mergetool_git_integration gui_default --verbose
cargo test -p gitcomet-app $APP_FEATURES --test difftool_git_integration gui_default --verbose
cargo test -p gitcomet $APP_FEATURES --test mergetool_git_integration gui_default --verbose
cargo test -p gitcomet $APP_FEATURES --test difftool_git_integration gui_default --verbose
- name: Run UI smoke test under selected profile
run: cargo test -p gitcomet-ui-gpui smoke_tests::smoke_view_renders_without_panicking -- --exact
@ -200,9 +200,9 @@ jobs:
--verbose
- name: Gatekeeper and code-signing check (informational)
run: |
cargo build -p gitcomet-app $APP_FEATURES --release --locked
codesign --verify --verbose target/release/gitcomet-app || true
spctl --assess --type execute --verbose target/release/gitcomet-app || true
cargo build -p gitcomet $APP_FEATURES --release --locked
codesign --verify --verbose target/release/gitcomet || true
spctl --assess --type execute --verbose target/release/gitcomet || true
windows-tests:
name: Windows Tests (PowerShell + CMD)
@ -235,7 +235,7 @@ jobs:
--verbose
- name: Run standalone smoke test in CMD
shell: cmd
run: cargo test -p gitcomet-app --no-default-features --features gix --test standalone_tool_mode_integration help_flag_exits_zero -- --exact
run: cargo test -p gitcomet --no-default-features --features gix --test standalone_tool_mode_integration help_flag_exits_zero -- --exact
- name: Show path behavior (CMD + Git Bash)
shell: cmd
run: |

View file

@ -93,6 +93,9 @@ jobs:
ruby -c dist/deployment-ci/gitcomet.rb
grep -q "on_linux do" dist/deployment-ci/gitcomet.rb
grep -q "gitcomet-v0.0.0-ci-linux-x86_64.tar.gz" dist/deployment-ci/gitcomet.rb
grep -q 'libexec.install "GitComet.app"' dist/deployment-ci/gitcomet.rb
grep -q 'bin.install_symlink libexec/"GitComet.app/Contents/MacOS/gitcomet"' dist/deployment-ci/gitcomet.rb
grep -q 'bin.install "gitcomet"' dist/deployment-ci/gitcomet.rb
- name: Validate Homebrew tap initial publish detection
run: |
@ -135,8 +138,8 @@ jobs:
pkg_root="dist/apt-ci/pkg"
mkdir -p "${pkg_root}/DEBIAN" "${pkg_root}/usr/bin"
printf '%s\n' '#!/usr/bin/env bash' 'echo "GitComet CI"' > "${pkg_root}/usr/bin/gitcomet-app"
chmod +x "${pkg_root}/usr/bin/gitcomet-app"
printf '%s\n' '#!/usr/bin/env bash' 'echo "GitComet CI"' > "${pkg_root}/usr/bin/gitcomet"
chmod +x "${pkg_root}/usr/bin/gitcomet"
{
echo "Package: gitcomet"
@ -231,7 +234,7 @@ jobs:
uses: Swatinem/rust-cache@v2
- name: Build release binary
run: cargo build -p gitcomet-app --release --locked --features ui-gpui,gix --bins
run: cargo build -p gitcomet --release --locked --features ui-gpui,gix --bins
- name: Package macOS artifacts
run: |
@ -248,7 +251,7 @@ jobs:
set -euo pipefail
tarball="dist/gitcomet-v0.0.0-ci-macos-${{ matrix.arch }}.tar.gz"
test -f "$tarball"
tar -tzf "$tarball" | grep -q "GitComet.app/Contents/MacOS/gitcomet-app$"
tar -tzf "$tarball" | grep -q "GitComet.app/Contents/MacOS/gitcomet$"
- name: Verify DMG launches binary
run: |
@ -262,5 +265,5 @@ jobs:
exit 1
fi
"${mount_point}/GitComet.app/Contents/MacOS/gitcomet-app" --help >/dev/null
"${mount_point}/GitComet.app/Contents/MacOS/gitcomet" --help >/dev/null
hdiutil detach "$mount_point"

View file

@ -18,7 +18,7 @@ concurrency:
env:
CARGO_TERM_COLOR: always
# Build gitcomet-app in headless mode (no GPUI system deps required).
# Build gitcomet in headless mode (no GPUI system deps required).
# The UI-only code paths are behind #[cfg(feature = "ui-gpui")] guards.
APP_FEATURES: "--no-default-features --features gix"
@ -77,7 +77,7 @@ jobs:
echo "Skipping cargo clippy -p gitcomet-ui-gpui in this job."
echo "Reason: GPUI requires native windowing/system dependencies that are not available in headless CI."
- name: Clippy (app — headless)
run: cargo clippy -p gitcomet-app $APP_FEATURES -- -D warnings
run: cargo clippy -p gitcomet $APP_FEATURES -- -D warnings
build:
name: Build
@ -92,7 +92,7 @@ jobs:
- name: Build (core + state + backend)
run: cargo build -p gitcomet-core -p gitcomet-state -p gitcomet-git-gix --verbose
- name: Build (app — headless)
run: cargo build -p gitcomet-app $APP_FEATURES --verbose
run: cargo build -p gitcomet $APP_FEATURES --verbose
# Core merge algorithm correctness — Phase 1A/1B/1C portability tests
merge-algorithm:
@ -148,16 +148,16 @@ jobs:
uses: dtolnay/rust-toolchain@stable
- name: Cache Rust artifacts
uses: Swatinem/rust-cache@v2
- name: Build gitcomet-app binary (headless)
run: cargo build -p gitcomet-app $APP_FEATURES
- name: Build gitcomet binary (headless)
run: cargo build -p gitcomet $APP_FEATURES
- name: Git mergetool E2E (Phase 4A — t7610 parity)
run: cargo test -p gitcomet-app $APP_FEATURES --test mergetool_git_integration --verbose
run: cargo test -p gitcomet $APP_FEATURES --test mergetool_git_integration --verbose
- name: Git difftool E2E (Phase 4B — t7800 parity)
run: cargo test -p gitcomet-app $APP_FEATURES --test difftool_git_integration --verbose
run: cargo test -p gitcomet $APP_FEATURES --test difftool_git_integration --verbose
- name: Standalone tool-mode E2E (exit codes + validation)
run: cargo test -p gitcomet-app $APP_FEATURES --test standalone_tool_mode_integration --verbose
run: cargo test -p gitcomet $APP_FEATURES --test standalone_tool_mode_integration --verbose
- name: Mergetool/difftool runtime unit tests (bin target)
run: cargo test -p gitcomet-app $APP_FEATURES --bin gitcomet-app --verbose
run: cargo test -p gitcomet $APP_FEATURES --bin gitcomet --verbose
# Backend integration — mergetool launcher, status, conflict checkout
backend-integration:

View file

@ -8,7 +8,7 @@
- `crates/gitcomet-state`: MVU state store, reducers, effects, conflict session management.
- `crates/gitcomet-ui`: UI model/state (toolkit-independent).
- `crates/gitcomet-ui-gpui`: gpui views/components (focused diff/merge windows, conflict resolver, word diff).
- `crates/gitcomet-app`: binary entrypoint, CLI (clap), difftool/mergetool/setup/uninstall modes.
- `crates/gitcomet`: binary entrypoint, CLI (clap), difftool/mergetool/setup/uninstall modes.
### Getting started
@ -28,19 +28,19 @@ cargo build
To build the actual app you'll enable features (requires network for dependencies):
```bash
cargo build -p gitcomet-app --features ui,gix
cargo build -p gitcomet --features ui,gix
```
To also compile the gpui-based UI crate:
```bash
cargo build -p gitcomet-app --features ui-gpui,gix
cargo build -p gitcomet --features ui-gpui,gix
```
Run (opens the repo passed as the first arg, or falls back to the current directory):
```bash
cargo run -p gitcomet-app --features ui-gpui,gix -- /path/to/repo
cargo run -p gitcomet --features ui-gpui,gix -- /path/to/repo
```
### Testing

2
Cargo.lock generated
View file

@ -2239,7 +2239,7 @@ dependencies = [
]
[[package]]
name = "gitcomet-app"
name = "gitcomet"
version = "0.1.1"
dependencies = [
"clap",

View file

@ -1,7 +1,7 @@
[workspace]
resolver = "3"
members = [
"crates/gitcomet-app",
"crates/gitcomet",
"crates/gitcomet-core",
"crates/gitcomet-git",
"crates/gitcomet-git-gix",
@ -11,7 +11,7 @@ members = [
]
default-members = [
"crates/gitcomet-app",
"crates/gitcomet",
"crates/gitcomet-core",
"crates/gitcomet-git",
"crates/gitcomet-git-gix",

View file

@ -76,8 +76,8 @@ Measured on Linux 6.19.6-zen (x64), Ryzen 5950x, 128GB DDR4. Detailed test steps
### Build from source
```bash
cargo build -p gitcomet-app --features ui-gpui,gix
cargo run -p gitcomet-app --features ui-gpui,gix -- /path/to/repo
cargo build -p gitcomet --features ui-gpui,gix
cargo run -p gitcomet --features ui-gpui,gix -- /path/to/repo
```
### Contributing
@ -92,10 +92,10 @@ GitComet can be used as a standalone diff and merge tool invoked by `git difftoo
```bash
# Configure Git globally to use GitComet for both difftool + mergetool
gitcomet-app setup
gitcomet setup
# Remove GitComet integration safely
gitcomet-app uninstall
gitcomet uninstall
```
- Use `--local` to target only the current repository instead of global config.
@ -110,7 +110,7 @@ This setup registers both headless and GUI variants with `guiDefault=auto`, so G
Built-in `setup` writes these Git config entries:
```bash
GITCOMET_BIN="/absolute/path/to/gitcomet-app"
GITCOMET_BIN="/absolute/path/to/gitcomet"
# Headless tool: algorithm-only merge/diff for CI, scripts, and no-display environments
git config --global merge.tool gitcomet
@ -153,7 +153,7 @@ Built-in `uninstall` restores those backups only when the key still has the setu
**Difftool:**
```bash
gitcomet-app difftool --local <path> --remote <path> [--path <display_name>] [--label-left <label>] [--label-right <label>]
gitcomet difftool --local <path> --remote <path> [--path <display_name>] [--label-left <label>] [--label-right <label>]
```
Also reads `LOCAL`/`REMOTE` from environment as a fallback when invoked by Git.
@ -161,7 +161,7 @@ Also reads `LOCAL`/`REMOTE` from environment as a fallback when invoked by Git.
**Mergetool:**
```bash
gitcomet-app mergetool --local <path> --remote <path> --merged <path> [--base <path>] [--label-local <label>] [--label-remote <label>] [--label-base <label>]
gitcomet mergetool --local <path> --remote <path> --merged <path> [--base <path>] [--label-local <label>] [--label-remote <label>] [--label-base <label>]
```
Also reads `LOCAL`/`REMOTE`/`MERGED`/`BASE` from environment. Base is optional for add/add conflicts.

View file

@ -2,7 +2,7 @@
Type=Application
Name=GitComet
Comment=Git UI built with GPUI
Exec=gitcomet-app
Exec=gitcomet
Icon=gitcomet
StartupWMClass=gitcomet
Terminal=false

View file

@ -684,7 +684,7 @@ mod tests {
"/tmp/target/debug/gitcomet_ui_gpui-3ad1b0fd3f0c0d3e"
)));
assert!(!looks_like_test_binary(Path::new(
"/tmp/target/debug/gitcomet-app"
"/tmp/target/debug/gitcomet"
)));
}

View file

@ -1,4 +1,4 @@
//! Focused diff window for standalone `gitcomet-app difftool` invocation.
//! Focused diff window for standalone `gitcomet difftool` invocation.
//!
//! Opens a GPUI window that displays a unified diff with color-coded lines.
//! The user reviews the diff and closes the window (exit 0).

View file

@ -1,5 +1,5 @@
[package]
name = "gitcomet-app"
name = "gitcomet"
version.workspace = true
edition.workspace = true
authors.workspace = true

View file

@ -16,7 +16,7 @@ fn main() {
let Some(install_dir) = current_exe.parent() else {
return;
};
let app_exe = install_dir.join("gitcomet-app.exe");
let app_exe = install_dir.join("gitcomet.exe");
let mut cmd = Command::new(app_exe);
cmd.current_dir(install_dir);

View file

@ -1,4 +1,4 @@
//! CLI argument parsing for gitcomet-app.
//! CLI argument parsing for gitcomet.
//!
//! Supports six modes:
//! - Default (no subcommand): open the full repository browser
@ -29,7 +29,7 @@ pub mod exit_code {
// ── Raw CLI argument structs (clap) ──────────────────────────────────
#[derive(Parser, Debug)]
#[command(name = "gitcomet-app", about = "Git GUI built with GPUI", version)]
#[command(name = "gitcomet", about = "Git GUI built with GPUI", version)]
pub struct Cli {
#[command(subcommand)]
pub command: Option<Command>,

View file

@ -54,7 +54,7 @@ fn install_linux_script_does_not_use_invalid_debug_flag() {
.unwrap_or_else(|err| panic!("failed to read {}: {err}", script_path.display()));
assert!(
!script.contains("cargo build -p gitcomet-app --${mode}"),
!script.contains("cargo build -p gitcomet --${mode}"),
"install script should not forward mode directly as a cargo flag"
);
}
@ -68,7 +68,7 @@ fn parse_mode_mergetool_drops_empty_base_value_before_clap() {
let mode = parse_mode_for_test(
vec![
"gitcomet-app".into(),
"gitcomet".into(),
"mergetool".into(),
"--base".into(),
"".into(),
@ -100,7 +100,7 @@ fn parse_mode_mergetool_drops_empty_attached_base_value_before_clap() {
let mode = parse_mode_for_test(
vec![
"gitcomet-app".into(),
"gitcomet".into(),
"mergetool".into(),
"--base=".into(),
"--local".into(),
@ -1437,7 +1437,7 @@ fn mergetool_env_only_resolution_without_base() {
#[test]
fn clap_parses_difftool_subcommand() {
let cli = Cli::try_parse_from([
"gitcomet-app",
"gitcomet",
"difftool",
"--local",
"/tmp/a",
@ -1461,7 +1461,7 @@ fn clap_parses_difftool_subcommand() {
#[test]
fn clap_parses_mergetool_subcommand() {
let cli = Cli::try_parse_from([
"gitcomet-app",
"gitcomet",
"mergetool",
"--merged",
"/tmp/m",
@ -1498,7 +1498,7 @@ fn clap_parses_mergetool_subcommand() {
fn clap_parses_mergetool_output_aliases() {
for merged_flag in ["-o", "--output", "--out"] {
let cli = Cli::try_parse_from([
"gitcomet-app",
"gitcomet",
"mergetool",
merged_flag,
"/tmp/m",
@ -1523,7 +1523,7 @@ fn clap_parses_mergetool_output_aliases() {
#[test]
fn clap_parses_mergetool_kdiff3_label_aliases() {
let cli = Cli::try_parse_from([
"gitcomet-app",
"gitcomet",
"mergetool",
"--merged",
"/tmp/m",
@ -1552,7 +1552,7 @@ fn clap_parses_mergetool_kdiff3_label_aliases() {
#[test]
fn clap_parses_setup_subcommand() {
let cli = Cli::try_parse_from(["gitcomet-app", "setup", "--dry-run", "--local"]).unwrap();
let cli = Cli::try_parse_from(["gitcomet", "setup", "--dry-run", "--local"]).unwrap();
match cli.command {
Some(Command::Setup(args)) => {
@ -1565,7 +1565,7 @@ fn clap_parses_setup_subcommand() {
#[test]
fn clap_parses_uninstall_subcommand() {
let cli = Cli::try_parse_from(["gitcomet-app", "uninstall", "--dry-run", "--local"]).unwrap();
let cli = Cli::try_parse_from(["gitcomet", "uninstall", "--dry-run", "--local"]).unwrap();
match cli.command {
Some(Command::Uninstall(args)) => {
@ -1581,7 +1581,7 @@ fn uninstall_mode_resolves_into_app_mode() {
let env = TestEnv::new();
let mode = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("uninstall"),
OsString::from("--dry-run"),
OsString::from("--local"),
@ -1601,14 +1601,14 @@ fn uninstall_mode_resolves_into_app_mode() {
#[test]
fn clap_parses_no_subcommand_as_browser() {
let cli = Cli::try_parse_from(["gitcomet-app"]).unwrap();
let cli = Cli::try_parse_from(["gitcomet"]).unwrap();
assert!(cli.command.is_none());
assert!(cli.path.is_none());
}
#[test]
fn clap_parses_path_argument() {
let cli = Cli::try_parse_from(["gitcomet-app", "/some/repo"]).unwrap();
let cli = Cli::try_parse_from(["gitcomet", "/some/repo"]).unwrap();
assert!(cli.command.is_none());
assert_eq!(
cli.path.as_deref(),
@ -1625,7 +1625,7 @@ fn compat_parses_positional_difftool_invocation() {
let mode = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
local.into_os_string(),
remote.into_os_string(),
],
@ -1653,7 +1653,7 @@ fn compat_parses_kdiff3_style_difftool_labels() {
let mode = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("--L1"),
OsString::from("LEFT_LABEL"),
OsString::from("--L2"),
@ -1683,7 +1683,7 @@ fn compat_parses_kdiff3_style_difftool_short_numbered_equals_labels() {
let mode = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("-L1=LEFT_LABEL"),
OsString::from("-L2=RIGHT_LABEL"),
local.into_os_string(),
@ -1711,7 +1711,7 @@ fn compat_parses_meld_style_difftool_short_labels() {
let mode = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("-L"),
OsString::from("LEFT_LABEL"),
OsString::from("--label"),
@ -1741,7 +1741,7 @@ fn compat_parses_meld_style_difftool_attached_labels() {
let mode = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("-LLEFT_LABEL"),
OsString::from("--label=RIGHT_LABEL"),
local.into_os_string(),
@ -1771,7 +1771,7 @@ fn compat_parses_kdiff3_style_mergetool_with_base() {
let mode = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("--auto"),
OsString::from("--L1"),
OsString::from("BASE_LABEL"),
@ -1814,7 +1814,7 @@ fn compat_parses_kdiff3_style_mergetool_with_base_flag() {
let mode = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("--auto"),
OsString::from("--L1=BASE_LABEL"),
OsString::from("--L2=LOCAL_LABEL"),
@ -1855,7 +1855,7 @@ fn compat_parses_kdiff3_style_mergetool_with_short_numbered_label_flags() {
let mode = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("--auto"),
OsString::from("-L1"),
OsString::from("BASE_LABEL"),
@ -1898,7 +1898,7 @@ fn compat_parses_kdiff3_style_mergetool_with_attached_short_numbered_label_flags
let mode = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("--auto"),
OsString::from("-L1BASE_LABEL"),
OsString::from("-L2=LOCAL_LABEL"),
@ -1938,7 +1938,7 @@ fn compat_parses_kdiff3_style_mergetool_with_attached_output_and_base_flags() {
let mode = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("--auto"),
OsString::from("--L1=BASE_LABEL"),
OsString::from("--L2=LOCAL_LABEL"),
@ -1976,7 +1976,7 @@ fn compat_parses_kdiff3_style_mergetool_without_base() {
let mode = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("--auto"),
OsString::from("--L1"),
OsString::from("LOCAL_LABEL"),
@ -2016,7 +2016,7 @@ fn compat_mergetool_applies_merge_conflictstyle_from_git_config() {
let mode = parse_mode_for_test_with_config(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("--auto"),
OsString::from("-o"),
merged.into_os_string(),
@ -2052,7 +2052,7 @@ fn compat_mergetool_applies_diff_algorithm_from_git_config() {
let mode = parse_mode_for_test_with_config(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("--auto"),
OsString::from("-o"),
merged.into_os_string(),
@ -2088,7 +2088,7 @@ fn compat_parses_meld_style_mergetool_with_output() {
let mode = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("--output"),
merged.clone().into_os_string(),
local.clone().into_os_string(),
@ -2124,7 +2124,7 @@ fn compat_parses_meld_style_mergetool_labels() {
let mode = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("--output"),
merged.clone().into_os_string(),
OsString::from("--label=LOCAL_LABEL"),
@ -2164,7 +2164,7 @@ fn compat_parses_meld_style_mergetool_with_auto_merge_flag() {
let mode = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("--auto-merge"),
OsString::from("--output"),
merged.clone().into_os_string(),
@ -2197,7 +2197,7 @@ fn compat_auto_merge_requires_output_path() {
let err = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("--auto-merge"),
local.into_os_string(),
base.into_os_string(),
@ -2224,7 +2224,7 @@ fn compat_rejects_too_many_label_flags() {
let err = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("--output"),
merged.into_os_string(),
OsString::from("--label"),
@ -2258,7 +2258,7 @@ fn compat_auto_requires_output_path() {
let err = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("--auto"),
local.into_os_string(),
remote.into_os_string(),
@ -2282,7 +2282,7 @@ fn compat_merge_requires_two_or_three_positionals_after_output_flag() {
let err = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("--output"),
merged.into_os_string(),
local.into_os_string(),
@ -2309,7 +2309,7 @@ fn compat_merge_rejects_too_many_positionals() {
let err = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("--out"),
merged.into_os_string(),
base.into_os_string(),
@ -2338,7 +2338,7 @@ fn compat_merge_rejects_base_flag_with_extra_positionals() {
let err = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("--base"),
base.into_os_string(),
OsString::from("--out"),
@ -2368,7 +2368,7 @@ fn compat_merge_without_base_rejects_l3_label() {
let err = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("--out"),
merged.into_os_string(),
OsString::from("--L3"),
@ -2395,7 +2395,7 @@ fn compat_diff_rejects_l3_without_output_path() {
let err = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("--L3"),
OsString::from("REMOTE"),
local.into_os_string(),
@ -2421,7 +2421,7 @@ fn compat_diff_rejects_base_without_output_path() {
let err = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("--base"),
base.into_os_string(),
local.into_os_string(),
@ -2447,7 +2447,7 @@ fn compat_diff_rejects_too_many_positionals() {
let err = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
local.into_os_string(),
remote.into_os_string(),
extra.into_os_string(),
@ -2594,7 +2594,7 @@ fn compat_base_flag_requires_two_positionals_when_output_present() {
let err = parse_mode_for_test(
vec![
OsString::from("gitcomet-app"),
OsString::from("gitcomet"),
OsString::from("--base"),
base.into_os_string(),
OsString::from("--out"),
@ -2834,7 +2834,7 @@ fn mergetool_marker_size_zero_errors() {
#[test]
fn clap_parses_conflict_style_and_diff_algorithm() {
let cli = Cli::try_parse_from([
"gitcomet-app",
"gitcomet",
"mergetool",
"--merged",
"/tmp/m",
@ -2996,7 +2996,7 @@ fn git_config_fallback_combined_style_and_algorithm() {
#[test]
fn clap_parses_mergetool_auto_flag() {
let cli = Cli::try_parse_from([
"gitcomet-app",
"gitcomet",
"mergetool",
"--merged",
"/tmp/m",
@ -3019,7 +3019,7 @@ fn clap_parses_mergetool_auto_flag() {
#[test]
fn clap_parses_mergetool_auto_merge_alias_flag() {
let cli = Cli::try_parse_from([
"gitcomet-app",
"gitcomet",
"mergetool",
"--merged",
"/tmp/m",
@ -3133,7 +3133,7 @@ fn compat_auto_merge_flag_propagates_to_config() {
#[test]
fn clap_parses_extract_merge_fixtures_subcommand() {
let cli = Cli::try_parse_from([
"gitcomet-app",
"gitcomet",
"extract-merge-fixtures",
"--repo",
"/tmp/repo",
@ -3162,7 +3162,7 @@ fn extract_merge_fixtures_mode_resolves_into_app_mode() {
let env = TestEnv::new();
let mode = parse_mode_for_test(
vec![
"gitcomet-app".into(),
"gitcomet".into(),
"extract-merge-fixtures".into(),
"--repo".into(),
"/tmp/repo".into(),

View file

@ -568,7 +568,7 @@ mod tests {
fn parse_crash_log_extracts_fields() {
let log = r#"=== GitComet crash (panic) ===
timestamp_unix_ms=123
crate=gitcomet-app version=0.1.0
crate=gitcomet version=0.1.0
thread=main
location=src/main.rs#L42
message=boom happened
@ -580,7 +580,7 @@ frame 2
let parsed = parse_crash_log(log);
assert_eq!(parsed.timestamp_unix_ms.as_deref(), Some("123"));
assert_eq!(parsed.crate_name.as_deref(), Some("gitcomet-app"));
assert_eq!(parsed.crate_name.as_deref(), Some("gitcomet"));
assert_eq!(parsed.crate_version.as_deref(), Some("0.1.0"));
assert_eq!(parsed.thread.as_deref(), Some("main"));
assert_eq!(parsed.location.as_deref(), Some("src/main.rs#L42"));
@ -602,7 +602,7 @@ frame 2
#[test]
fn build_startup_report_populates_issue_url_and_summary() {
let log = r#"timestamp_unix_ms=123
crate=gitcomet-app version=0.1.0
crate=gitcomet version=0.1.0
thread=main
location=src/main.rs#L42
message=boom happened
@ -633,7 +633,7 @@ frame 2
let dir = tempdir().expect("temp dir");
let crash_log_path = dir.path().join("panic.log");
let crash_log = r#"timestamp_unix_ms=123
crate=gitcomet-app version=0.1.0
crate=gitcomet version=0.1.0
thread=main
location=src/main.rs#L42
message=boom happened

View file

@ -148,7 +148,7 @@ fn main() {
#[cfg(not(feature = "ui-gpui-runtime"))]
if config.gui {
eprintln!(
"GUI difftool mode is unavailable in this build. Rebuild with `-p gitcomet-app --features ui-gpui`."
"GUI difftool mode is unavailable in this build. Rebuild with `-p gitcomet --features ui-gpui`."
);
std::process::exit(exit_code::ERROR);
}
@ -234,7 +234,7 @@ fn main() {
#[cfg(not(feature = "ui"))]
{
let _ = path;
eprintln!("GitComet UI is disabled. Build with `-p gitcomet-app --features ui`.");
eprintln!("GitComet UI is disabled. Build with `-p gitcomet --features ui`.");
std::process::exit(exit_code::ERROR);
}
}
@ -242,7 +242,7 @@ fn main() {
#[cfg(not(feature = "ui-gpui-runtime"))]
if config.gui {
eprintln!(
"GUI mergetool mode is unavailable in this build. Rebuild with `-p gitcomet-app --features ui-gpui`."
"GUI mergetool mode is unavailable in this build. Rebuild with `-p gitcomet --features ui-gpui`."
);
std::process::exit(exit_code::ERROR);
}
@ -284,6 +284,16 @@ const MACOS_BUNDLE_RELAUNCH_ENV: &str = "GITCOMET_SKIP_APP_BUNDLE_RELAUNCH";
#[cfg(all(target_os = "macos", feature = "ui-gpui-runtime"))]
const MACOS_APP_ICON_PNG: &[u8] = include_bytes!("../../../assets/gitcomet-512.png");
#[cfg(feature = "ui-gpui-runtime")]
fn resolve_executable_path_for_bundle_detection(path: &std::path::Path) -> std::path::PathBuf {
path.canonicalize().unwrap_or_else(|_| path.to_path_buf())
}
#[cfg(feature = "ui-gpui-runtime")]
fn is_macos_app_bundle_executable(path: &std::path::Path) -> bool {
path.to_string_lossy().contains(".app/Contents/MacOS/")
}
#[cfg(all(target_os = "macos", feature = "ui-gpui-runtime"))]
fn maybe_relaunch_browser_from_macos_app_bundle() -> bool {
if std::env::var_os(MACOS_BUNDLE_RELAUNCH_ENV).is_some() {
@ -293,21 +303,19 @@ fn maybe_relaunch_browser_from_macos_app_bundle() -> bool {
let Ok(current_exe) = std::env::current_exe() else {
return false;
};
if current_exe
.to_string_lossy()
.contains(".app/Contents/MacOS/")
{
let resolved_exe = resolve_executable_path_for_bundle_detection(&current_exe);
if is_macos_app_bundle_executable(&resolved_exe) {
return false;
}
let Some(bin_dir) = current_exe.parent() else {
let Some(bin_dir) = resolved_exe.parent() else {
return false;
};
let app_bundle = bin_dir.join("GitComet.app");
let app_exe = match ensure_macos_dev_app_bundle(&current_exe, &app_bundle) {
let app_exe = match ensure_macos_dev_app_bundle(&resolved_exe, &app_bundle) {
Ok(path) => path,
Err(err) => {
eprintln!("Failed to prepare macOS app bundle icon: {err}");
eprintln!("Failed to prepare macOS app bundle: {err}");
return false;
}
};
@ -336,7 +344,7 @@ fn ensure_macos_dev_app_bundle(
std::fs::create_dir_all(&resources)
.map_err(|e| format!("failed to create Resources dir: {e}"))?;
let app_exe = macos.join("gitcomet-app");
let app_exe = macos.join("gitcomet");
std::fs::copy(current_exe, &app_exe)
.map_err(|e| format!("failed to copy executable into bundle: {e}"))?;
@ -372,7 +380,7 @@ fn ensure_macos_dev_app_bundle(
<key>CFBundleDisplayName</key>
<string>GitComet</string>
<key>CFBundleExecutable</key>
<string>gitcomet-app</string>
<string>gitcomet</string>
<key>CFBundleIdentifier</key>
<string>ai.autoexplore.gitcomet.dev</string>
<key>CFBundleIconFile</key>
@ -680,6 +688,39 @@ mod tests {
assert!(err.contains(&merged.display().to_string()));
}
#[test]
#[cfg(feature = "ui-gpui-runtime")]
fn detects_macos_app_bundle_executable_paths() {
assert!(is_macos_app_bundle_executable(std::path::Path::new(
"/tmp/GitComet.app/Contents/MacOS/gitcomet"
)));
assert!(!is_macos_app_bundle_executable(std::path::Path::new(
"/opt/homebrew/bin/gitcomet"
)));
}
#[test]
#[cfg(all(feature = "ui-gpui-runtime", unix))]
fn canonicalized_symlink_resolves_to_macos_app_bundle_executable() {
use std::os::unix::fs::symlink;
let temp = tempfile::tempdir().unwrap();
let app_exe = temp.path().join("GitComet.app/Contents/MacOS/gitcomet");
fs::create_dir_all(app_exe.parent().unwrap()).unwrap();
fs::write(&app_exe, b"#!/bin/sh\n").unwrap();
let bin_dir = temp.path().join("bin");
fs::create_dir_all(&bin_dir).unwrap();
let symlink_path = bin_dir.join("gitcomet");
symlink(&app_exe, &symlink_path).unwrap();
assert!(!is_macos_app_bundle_executable(&symlink_path));
let resolved = resolve_executable_path_for_bundle_detection(&symlink_path);
assert_eq!(resolved, app_exe);
assert!(is_macos_app_bundle_executable(&resolved));
}
#[test]
fn emit_result_writes_stdout_stderr_and_flushes() {
let result = Ok(TestRunResult {

View file

@ -1,4 +1,4 @@
//! `gitcomet-app setup` / `gitcomet-app uninstall` support.
//! `gitcomet setup` / `gitcomet uninstall` support.
//!
//! Setup writes the recommended global (or local) git config entries so that
//! `git difftool` and `git mergetool` invoke gitcomet automatically.
@ -91,7 +91,7 @@ fn shell_single_quote(value: &str) -> String {
fn current_exe_path() -> Result<PathBuf, String> {
std::env::current_exe()
.and_then(|p| p.canonicalize())
.map_err(|e| format!("Cannot determine gitcomet-app binary path: {e}"))
.map_err(|e| format!("Cannot determine gitcomet binary path: {e}"))
}
fn executable_path_for_shell(bin_path: &Path) -> Result<String, String> {
@ -920,7 +920,7 @@ mod tests {
#[test]
fn build_config_entries_contains_all_required_keys() {
let entries = build_config_entries("/usr/bin/gitcomet-app");
let entries = build_config_entries("/usr/bin/gitcomet");
let keys: Vec<&str> = entries.iter().map(|e| e.key).collect();
// Headless tool
@ -948,7 +948,7 @@ mod tests {
#[test]
fn gui_tool_uses_separate_tool_name() {
let entries = build_config_entries("/usr/bin/gitcomet-app");
let entries = build_config_entries("/usr/bin/gitcomet");
let merge_guitool = entries.iter().find(|e| e.key == "merge.guitool").unwrap();
let diff_guitool = entries.iter().find(|e| e.key == "diff.guitool").unwrap();
@ -1021,14 +1021,14 @@ mod tests {
#[test]
fn mergetool_cmd_escapes_single_quote_in_binary_path() {
let entries = build_config_entries("/tmp/it's/gitcomet-app");
let entries = build_config_entries("/tmp/it's/gitcomet");
let cmd = entries
.iter()
.find(|e| e.key == "mergetool.gitcomet.cmd")
.unwrap();
assert!(
cmd.value.starts_with("'/tmp/it'\"'\"'s/gitcomet-app'"),
cmd.value.starts_with("'/tmp/it'\"'\"'s/gitcomet'"),
"unexpected cmd quoting: {}",
cmd.value
);
@ -1076,7 +1076,7 @@ mod tests {
#[test]
fn format_commands_global_scope() {
let entries = build_config_entries("/bin/gitcomet-app");
let entries = build_config_entries("/bin/gitcomet");
let output = format_commands(&entries, "--global");
// Headless mergetool entries
@ -1109,14 +1109,14 @@ mod tests {
assert!(output.contains("git config --global difftool.guiDefault"));
assert!(
!output.contains("''/bin/gitcomet-app'"),
!output.contains("''/bin/gitcomet'"),
"dry-run output should not contain broken nested quoting:\n{output}"
);
}
#[test]
fn format_commands_local_scope() {
let entries = build_config_entries("/bin/gitcomet-app");
let entries = build_config_entries("/bin/gitcomet");
let output = format_commands(&entries, "--local");
assert!(output.contains("git config --local merge.tool"));
@ -1155,7 +1155,7 @@ mod tests {
.unwrap();
assert!(init.status.success());
let entries = build_config_entries("/test/gitcomet-app");
let entries = build_config_entries("/test/gitcomet");
let result = std::process::Command::new("git")
.arg("-C")
.arg(dir.path())

View file

@ -59,7 +59,7 @@ fn require_git_shell_for_tool_tests() -> bool {
}
fn gitcomet_bin() -> PathBuf {
for env_key in ["CARGO_BIN_EXE_gitcomet-app", "CARGO_BIN_EXE_gitcomet_app"] {
for env_key in ["CARGO_BIN_EXE_gitcomet"] {
if let Some(path) = std::env::var_os(env_key).map(PathBuf::from) {
if path.is_file() {
return path;
@ -72,8 +72,7 @@ fn gitcomet_bin() -> PathBuf {
}
panic!(
"gitcomet-app binary path was not found. Tried CARGO_BIN_EXE_gitcomet-app, \
CARGO_BIN_EXE_gitcomet_app, and a fallback relative to current test executable"
"gitcomet binary path was not found. Tried CARGO_BIN_EXE_gitcomet and a fallback relative to current test executable"
);
}
@ -83,7 +82,7 @@ fn gitcomet_bin_from_current_exe() -> Option<PathBuf> {
let profile_dir = deps_dir.parent()?;
let exe_suffix = std::env::consts::EXE_SUFFIX;
for bin_name in ["gitcomet-app", "gitcomet_app"] {
for bin_name in ["gitcomet"] {
let candidate = profile_dir.join(format!("{bin_name}{exe_suffix}"));
if candidate.is_file() {
return Some(candidate);
@ -264,7 +263,7 @@ fn output_text(output: &Output) -> String {
}
#[test]
fn git_difftool_invokes_gitcomet_app_for_basic_diff() {
fn git_difftool_invokes_gitcomet_for_basic_diff() {
if !require_git_shell_for_tool_tests() {
return;
}

View file

@ -17,7 +17,7 @@ fn apply_isolated_git_config_env(cmd: &mut Command) {
}
fn gitcomet_bin() -> PathBuf {
for env_key in ["CARGO_BIN_EXE_gitcomet-app", "CARGO_BIN_EXE_gitcomet_app"] {
for env_key in ["CARGO_BIN_EXE_gitcomet"] {
if let Some(path) = std::env::var_os(env_key).map(PathBuf::from) {
if path.is_file() {
return path;
@ -30,8 +30,7 @@ fn gitcomet_bin() -> PathBuf {
}
panic!(
"gitcomet-app binary path was not found. Tried CARGO_BIN_EXE_gitcomet-app, \
CARGO_BIN_EXE_gitcomet_app, and a fallback relative to current test executable"
"gitcomet binary path was not found. Tried CARGO_BIN_EXE_gitcomet and a fallback relative to current test executable"
);
}
@ -41,7 +40,7 @@ fn gitcomet_bin_from_current_exe() -> Option<PathBuf> {
let profile_dir = deps_dir.parent()?;
let exe_suffix = std::env::consts::EXE_SUFFIX;
for bin_name in ["gitcomet-app", "gitcomet_app"] {
for bin_name in ["gitcomet"] {
let candidate = profile_dir.join(format!("{bin_name}{exe_suffix}"));
if candidate.is_file() {
return Some(candidate);
@ -59,7 +58,7 @@ where
Command::new(gitcomet_bin())
.args(args)
.output()
.expect("gitcomet-app command to run")
.expect("gitcomet command to run")
}
fn run_git_capture(repo: &Path, args: &[&str]) -> Output {

View file

@ -62,7 +62,7 @@ fn require_git_shell_for_tool_tests() -> bool {
}
fn gitcomet_bin() -> PathBuf {
for env_key in ["CARGO_BIN_EXE_gitcomet-app", "CARGO_BIN_EXE_gitcomet_app"] {
for env_key in ["CARGO_BIN_EXE_gitcomet"] {
if let Some(path) = std::env::var_os(env_key).map(PathBuf::from) {
if path.is_file() {
return path;
@ -75,8 +75,7 @@ fn gitcomet_bin() -> PathBuf {
}
panic!(
"gitcomet-app binary path was not found. Tried CARGO_BIN_EXE_gitcomet-app, \
CARGO_BIN_EXE_gitcomet_app, and a fallback relative to current test executable"
"gitcomet binary path was not found. Tried CARGO_BIN_EXE_gitcomet and a fallback relative to current test executable"
);
}
@ -86,7 +85,7 @@ fn gitcomet_bin_from_current_exe() -> Option<PathBuf> {
let profile_dir = deps_dir.parent()?;
let exe_suffix = std::env::consts::EXE_SUFFIX;
for bin_name in ["gitcomet-app", "gitcomet_app"] {
for bin_name in ["gitcomet"] {
let candidate = profile_dir.join(format!("{bin_name}{exe_suffix}"));
if candidate.is_file() {
return Some(candidate);
@ -650,11 +649,11 @@ fn git_mergetool_accepts_kdiff3_alias_flags_in_cmd() {
&& !text.contains("unexpected argument '--L1'")
&& !text.contains("unexpected argument '--L2'")
&& !text.contains("unexpected argument '--L3'"),
"expected alias flags to be accepted by gitcomet-app mergetool\noutput:\n{text}"
"expected alias flags to be accepted by gitcomet mergetool\noutput:\n{text}"
);
assert!(
text.contains("Auto-merging file.txt"),
"expected gitcomet-app mergetool to run\noutput:\n{text}"
"expected gitcomet mergetool to run\noutput:\n{text}"
);
assert!(
merged.contains("LOCAL") || merged.contains("REMOTE") || merged.contains("<<<<<<<"),
@ -1180,7 +1179,7 @@ fn git_mergetool_trust_exit_code_conflict_preserves_unmerged_state() {
}
// When our tool exits 1 (unresolved conflict) with trustExitCode=true,
// git should leave the file as unmerged. This verifies the exit code
// contract between gitcomet-app and git mergetool.
// contract between gitcomet and git mergetool.
let tmp = tempfile::tempdir().unwrap();
let repo = tmp.path();
@ -3677,7 +3676,7 @@ fn gitcomet_mergetool_reads_conflictstyle_from_repo_when_cwd_is_outside_repo() {
.arg("--merged")
.arg(&merged)
.output()
.expect("gitcomet-app mergetool command to run");
.expect("gitcomet mergetool command to run");
let text = output_text(&output);
let merged_text = fs::read_to_string(&merged).unwrap_or_else(|e| {

View file

@ -6,7 +6,7 @@ use std::process::{Command, Output};
use std::sync::OnceLock;
fn gitcomet_bin() -> PathBuf {
for env_key in ["CARGO_BIN_EXE_gitcomet-app", "CARGO_BIN_EXE_gitcomet_app"] {
for env_key in ["CARGO_BIN_EXE_gitcomet"] {
if let Some(path) = std::env::var_os(env_key).map(PathBuf::from) {
if path.is_file() {
return path;
@ -19,8 +19,7 @@ fn gitcomet_bin() -> PathBuf {
}
panic!(
"gitcomet-app binary path was not found. Tried CARGO_BIN_EXE_gitcomet-app, \
CARGO_BIN_EXE_gitcomet_app, and a fallback relative to current test executable"
"gitcomet binary path was not found. Tried CARGO_BIN_EXE_gitcomet and a fallback relative to current test executable"
);
}
@ -30,7 +29,7 @@ fn gitcomet_bin_from_current_exe() -> Option<PathBuf> {
let profile_dir = deps_dir.parent()?;
let exe_suffix = std::env::consts::EXE_SUFFIX;
for bin_name in ["gitcomet-app", "gitcomet_app"] {
for bin_name in ["gitcomet"] {
let candidate = profile_dir.join(format!("{bin_name}{exe_suffix}"));
if candidate.is_file() {
return Some(candidate);
@ -48,7 +47,7 @@ where
Command::new(gitcomet_bin())
.args(args)
.output()
.expect("gitcomet-app command to run")
.expect("gitcomet command to run")
}
fn run_gitcomet_in_dir<I, S>(dir: &Path, args: I) -> Output
@ -60,7 +59,7 @@ where
.current_dir(dir)
.args(args)
.output()
.expect("gitcomet-app command to run")
.expect("gitcomet command to run")
}
fn run_gitcomet_in_dir_with_global_env<I, S>(
@ -78,7 +77,7 @@ where
.current_dir(dir)
.args(args)
.output()
.expect("gitcomet-app command to run")
.expect("gitcomet command to run")
}
fn git_config_get(repo_dir: &Path, key: &str) -> Option<String> {
@ -2447,7 +2446,7 @@ fn standalone_mergetool_auto_crlf_subchunk_preserves_line_endings() {
// ── Setup E2E integration ────────────────────────────────────────────
//
// These tests verify that `gitcomet-app setup --local` produces config that
// These tests verify that `gitcomet setup --local` produces config that
// actually works when Git invokes `git mergetool` / `git difftool`. This
// closes the gap between "config keys are written" and "the configured tool
// is invoked end-to-end" — directly validating acceptance criteria 2-3 from
@ -2592,12 +2591,12 @@ fn git_config_get_global_with_env(env: &IsolatedGlobalGitEnv, key: &str) -> Opti
}
}
/// After `gitcomet-app setup --local`, `git mergetool` should invoke
/// gitcomet-app's built-in 3-way merge for conflicted files.
/// After `gitcomet setup --local`, `git mergetool` should invoke
/// gitcomet's built-in 3-way merge for conflicted files.
///
/// For a true content conflict, gitcomet-app exits 1 and git mergetool
/// For a true content conflict, gitcomet exits 1 and git mergetool
/// restores the original file (expected behavior with trustExitCode=true).
/// We verify the tool was invoked by checking for gitcomet-app's specific
/// We verify the tool was invoked by checking for gitcomet's specific
/// stderr messages, which differ from git's own merge output.
#[test]
fn setup_local_enables_git_mergetool_end_to_end() {
@ -2631,33 +2630,33 @@ fn setup_local_enables_git_mergetool_end_to_end() {
"expected merge conflict but git merge succeeded"
);
// 3. Run `git mergetool` — should invoke gitcomet-app via setup config.
// 3. Run `git mergetool` — should invoke gitcomet via setup config.
// DISPLAY is removed so guiDefault=auto selects the headless tool.
let mt = setup_e2e_git_capture(repo, &["mergetool"]);
let mt_stderr = String::from_utf8_lossy(&mt.stderr);
// 4. Verify gitcomet-app was invoked by checking for its specific stderr
// messages. "conflict(s) remain" is emitted by gitcomet-app's mergetool
// 4. Verify gitcomet was invoked by checking for its specific stderr
// messages. "conflict(s) remain" is emitted by gitcomet's mergetool
// mode and is NOT part of git's own output (git says "fix conflicts and
// then commit the result" instead).
assert!(
mt_stderr.contains("conflict(s) remain"),
"expected gitcomet-app's conflict message in mergetool stderr\n\
"expected gitcomet's conflict message in mergetool stderr\n\
stdout: {}\nstderr: {}",
String::from_utf8_lossy(&mt.stdout),
mt_stderr,
);
// Also verify gitcomet-app's "CONFLICT (content)" message is present.
// Also verify gitcomet's "CONFLICT (content)" message is present.
assert!(
mt_stderr.contains("CONFLICT (content)"),
"expected CONFLICT marker from gitcomet-app\nstderr: {}",
"expected CONFLICT marker from gitcomet\nstderr: {}",
mt_stderr,
);
}
/// After `gitcomet-app setup --local`, `git difftool` should invoke
/// gitcomet-app's built-in diff and produce unified diff output.
/// After `gitcomet setup --local`, `git difftool` should invoke
/// gitcomet's built-in diff and produce unified diff output.
#[test]
fn setup_local_enables_git_difftool_end_to_end() {
if !require_git_shell_for_setup_integration_tests() {
@ -2677,7 +2676,7 @@ fn setup_local_enables_git_difftool_end_to_end() {
setup_e2e_commit(repo, "initial");
write_file(&repo.join("file.txt"), "line1\nMODIFIED\nline3\n");
// 3. Run `git difftool` — should invoke gitcomet-app difftool.
// 3. Run `git difftool` — should invoke gitcomet difftool.
// DISPLAY is removed so guiDefault=auto selects the headless tool.
let dt = setup_e2e_git_capture(repo, &["difftool"]);
let dt_text = output_text(&dt);
@ -2688,7 +2687,7 @@ fn setup_local_enables_git_difftool_end_to_end() {
"git difftool should exit 0\n{dt_text}"
);
// gitcomet-app difftool produces unified diff output.
// gitcomet difftool produces unified diff output.
let stdout = String::from_utf8_lossy(&dt.stdout);
assert!(
stdout.contains("@@"),
@ -2704,7 +2703,7 @@ fn setup_local_enables_git_difftool_end_to_end() {
);
}
/// After `gitcomet-app setup --local`, quoted stage variables in the generated
/// After `gitcomet setup --local`, quoted stage variables in the generated
/// mergetool command must preserve paths containing spaces/unicode.
#[test]
fn setup_local_mergetool_handles_spaced_unicode_path_end_to_end() {
@ -2760,7 +2759,7 @@ fn setup_local_mergetool_handles_spaced_unicode_path_end_to_end() {
);
}
/// After `gitcomet-app setup --local`, quoted stage variables in the generated
/// After `gitcomet setup --local`, quoted stage variables in the generated
/// difftool command must preserve paths containing spaces/unicode.
#[test]
fn setup_local_difftool_handles_spaced_unicode_path_end_to_end() {
@ -2808,7 +2807,7 @@ fn setup_local_difftool_handles_spaced_unicode_path_end_to_end() {
);
}
/// `gitcomet-app setup` (global scope) should configure an isolated global
/// `gitcomet setup` (global scope) should configure an isolated global
/// gitconfig so `git mergetool` works end-to-end without local repo config.
#[test]
fn setup_global_enables_git_mergetool_end_to_end_with_isolated_global_config() {
@ -2885,7 +2884,7 @@ fn setup_global_enables_git_mergetool_end_to_end_with_isolated_global_config() {
);
}
/// `gitcomet-app setup` (global scope) should configure an isolated global
/// `gitcomet setup` (global scope) should configure an isolated global
/// gitconfig so `git difftool` works end-to-end without local repo config.
#[test]
fn setup_global_enables_git_difftool_end_to_end_with_isolated_global_config() {
@ -2942,7 +2941,7 @@ fn setup_global_enables_git_difftool_end_to_end_with_isolated_global_config() {
);
}
/// `gitcomet-app setup` (global scope) should make both headless and GUI
/// `gitcomet setup` (global scope) should make both headless and GUI
/// mergetool entries discoverable via `git mergetool --tool-help`.
#[test]
fn setup_global_mergetool_tool_help_lists_headless_and_gui_entries() {
@ -2994,7 +2993,7 @@ fn setup_global_mergetool_tool_help_lists_headless_and_gui_entries() {
);
}
/// `gitcomet-app setup` (global scope) should make both headless and GUI
/// `gitcomet setup` (global scope) should make both headless and GUI
/// difftool entries discoverable via `git difftool --tool-help`.
#[test]
fn setup_global_difftool_tool_help_lists_headless_and_gui_entries() {
@ -3058,7 +3057,7 @@ fn help_flag_exits_zero() {
);
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("gitcomet-app"),
stdout.contains("gitcomet"),
"help output should mention the binary name"
);
}
@ -3073,7 +3072,7 @@ fn version_flag_exits_zero() {
);
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("gitcomet-app"),
stdout.contains("gitcomet"),
"version output should mention the binary name"
);
}

View file

@ -60,16 +60,16 @@
<Component Id='MainBinary' Guid='*'>
<File
Id='MainExeFile'
Name='gitcomet-app.exe'
Name='gitcomet.exe'
DiskId='1'
Source='$(var.CargoTargetBinDir)\gitcomet-app.exe'
Source='$(var.CargoTargetBinDir)\gitcomet.exe'
KeyPath='yes'/>
</Component>
<Component Id='LauncherBinary' Guid='*'>
<File
Id='LauncherExeFile'
Name='GitComet.exe'
Name='gitcomet-launcher.exe'
DiskId='1'
Source='$(var.CargoTargetBinDir)\gitcomet-launcher.exe'
KeyPath='yes'/>
@ -86,7 +86,7 @@
Directory='GitCometProgramMenuDir'
Name='GitComet'
Description='Git GUI built with GPUI'
Target='[Bin]GitComet.exe'
Target='[Bin]gitcomet-launcher.exe'
WorkingDirectory='APPLICATIONFOLDER'
Icon='ProductICO'
IconIndex='0'

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash
set -euo pipefail
RUSTFLAGS="-C symbol-mangling-version=v0" cargo build -p gitcomet-app --all-targets --profile=release-with-debug
RUSTFLAGS="-C symbol-mangling-version=v0" cargo build -p gitcomet --all-targets --profile=release-with-debug

View file

@ -136,11 +136,16 @@ class Gitcomet < Formula
end
def install
bin.install Dir["**/gitcomet-app"].fetch(0)
if OS.mac?
libexec.install "GitComet.app"
bin.install_symlink libexec/"GitComet.app/Contents/MacOS/gitcomet"
else
bin.install "gitcomet"
end
end
test do
assert_match "Usage", shell_output("#{bin}/gitcomet-app --help")
assert_match "Usage", shell_output("#{bin}/gitcomet --help")
end
end
EOF2

View file

@ -6,7 +6,7 @@ usage() {
Usage: scripts/install-linux.sh [--release|--debug] [--prefix PATH] [--no-build]
Installs:
- binary to <prefix>/bin/gitcomet-app
- binary to <prefix>/bin/gitcomet
- desktop entry to ~/.local/share/applications/gitcomet.desktop
- icons to ~/.local/share/icons/hicolor/<size>x<size>/apps/gitcomet.png
sizes: 32, 48, 128, 256, 512
@ -32,14 +32,14 @@ while [[ $# -gt 0 ]]; do
done
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
bin_src="${repo_root}/target/${mode}/gitcomet-app"
bin_src="${repo_root}/target/${mode}/gitcomet"
if [[ $build -eq 1 && ! -x "$bin_src" ]]; then
cargo_mode_flag=()
if [[ "$mode" == "release" ]]; then
cargo_mode_flag=(--release)
fi
(cd "$repo_root" && cargo build -p gitcomet-app "${cargo_mode_flag[@]}")
(cd "$repo_root" && cargo build -p gitcomet "${cargo_mode_flag[@]}")
fi
if [[ ! -x "$bin_src" ]]; then
@ -53,12 +53,12 @@ appdir="${XDG_DATA_HOME:-${HOME}/.local/share}/applications"
iconsroot="${XDG_DATA_HOME:-${HOME}/.local/share}/icons/hicolor"
icon_sizes=(32 48 128 256 512)
install -Dm755 "$bin_src" "${bindir}/gitcomet-app"
install -Dm755 "$bin_src" "${bindir}/gitcomet"
# Install desktop file with absolute Exec path so it works even if ~/.local/bin isn't on PATH.
tmp_desktop="$(mktemp)"
trap 'rm -f "$tmp_desktop"' EXIT
sed "s|^Exec=.*$|Exec=${bindir}/gitcomet-app|g" \
sed "s|^Exec=.*$|Exec=${bindir}/gitcomet|g" \
"${repo_root}/assets/linux/gitcomet.desktop" >"$tmp_desktop"
install -Dm644 "$tmp_desktop" "${appdir}/gitcomet.desktop"
@ -71,7 +71,7 @@ command -v update-desktop-database >/dev/null 2>&1 && update-desktop-database "$
command -v gtk-update-icon-cache >/dev/null 2>&1 && gtk-update-icon-cache "${iconsroot}" >/dev/null 2>&1 || true
echo "Installed GitComet:"
echo " ${bindir}/gitcomet-app"
echo " ${bindir}/gitcomet"
echo " ${appdir}/gitcomet.desktop"
for size in "${icon_sizes[@]}"; do
echo " ${iconsroot}/${size}x${size}/apps/gitcomet.png"

View file

@ -94,14 +94,14 @@ if [[ "$arch" != "$host_arch" ]]; then
fi
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
bin_src="${repo_root}/target/${mode}/gitcomet-app"
bin_src="${repo_root}/target/${mode}/gitcomet"
if [[ $build -eq 1 && ! -x "$bin_src" ]]; then
cargo_mode_flag=()
if [[ "$mode" == "release" ]]; then
cargo_mode_flag=(--release)
fi
(cd "$repo_root" && cargo build -p gitcomet-app "${cargo_mode_flag[@]}" --locked --features ui-gpui,gix --bins)
(cd "$repo_root" && cargo build -p gitcomet "${cargo_mode_flag[@]}" --locked --features ui-gpui,gix --bins)
fi
if [[ ! -x "$bin_src" ]]; then
@ -129,8 +129,8 @@ resources_dir="${contents_dir}/Resources"
rm -rf "$release_dir"
mkdir -p "$macos_dir" "$resources_dir"
install -m755 "$bin_src" "${macos_dir}/gitcomet-app"
install -m755 "$bin_src" "${release_dir}/gitcomet-app"
install -m755 "$bin_src" "${macos_dir}/gitcomet"
install -m755 "$bin_src" "${release_dir}/gitcomet"
install -m644 "${repo_root}/README.md" "${release_dir}/README.md"
install -m644 "${repo_root}/LICENSE-AGPL-3.0" "${release_dir}/LICENSE-AGPL-3.0"
install -m644 "${repo_root}/NOTICE" "${release_dir}/NOTICE"
@ -158,7 +158,7 @@ cat > "${contents_dir}/Info.plist" <<PLIST
<key>CFBundleDisplayName</key>
<string>GitComet</string>
<key>CFBundleExecutable</key>
<string>gitcomet-app</string>
<string>gitcomet</string>
<key>CFBundleIdentifier</key>
<string>ai.autoexplore.gitcomet</string>
<key>CFBundleIconFile</key>

View file

@ -1 +1 @@
cargo wix crates\gitcomet-app\Cargo.toml -p gitcomet-app --profile release --nocapture --output dist\gitcomet-local-test.msi
cargo wix crates\gitcomet\Cargo.toml -p gitcomet --profile release --nocapture --output dist\gitcomet-local-test.msi

View file

@ -18,7 +18,7 @@ appdir="${XDG_DATA_HOME:-${HOME}/.local/share}/applications"
iconsroot="${XDG_DATA_HOME:-${HOME}/.local/share}/icons/hicolor"
icon_sizes=(32 48 128 256 512)
rm -f "${bindir}/gitcomet-app"
rm -f "${bindir}/gitcomet"
rm -f "${appdir}/gitcomet.desktop"
for size in "${icon_sizes[@]}"; do
rm -f "${iconsroot}/${size}x${size}/apps/gitcomet.png"

View file

@ -7,7 +7,7 @@ Usage: scripts/valgrind_cdp <run|on|off|open> [path]
Commands:
run [binary] Run a binary under callgrind.
Default: ./target/release-with-debug/gitcomet-app
Default: ./target/release-with-debug/gitcomet
on Enable callgrind instrumentation.
off Disable callgrind instrumentation.
open [outfile] Open a callgrind output file in kcachegrind.
@ -18,7 +18,7 @@ EOF
cmd="${1:-run}"
case "$cmd" in
run)
binary="${2:-./target/release-with-debug/gitcomet-app}"
binary="${2:-./target/release-with-debug/gitcomet}"
valgrind \
--tool=callgrind \
--callgrind-out-file=callgrind.out \