diff --git a/.github/actions/init-env-node/action.yaml b/.github/actions/init-env-node/action.yaml index 7298ad720..52c8d5461 100644 --- a/.github/actions/init-env-node/action.yaml +++ b/.github/actions/init-env-node/action.yaml @@ -36,4 +36,5 @@ runs: - name: Build UI shell: bash - run: cd packages/ui && pnpm package \ No newline at end of file + run: pnpm exec turbo run package # build UI package + diff --git a/.github/actions/init-env-rust/action.yaml b/.github/actions/init-env-rust/action.yaml index efb018d57..190076e3a 100644 --- a/.github/actions/init-env-rust/action.yaml +++ b/.github/actions/init-env-rust/action.yaml @@ -3,6 +3,16 @@ description: prepare runner for rust related tasks runs: using: "composite" steps: + - name: Setup Nightly + if: runner.os == 'Windows' + shell: bash + run: | + mv rust-toolchain.toml.windows rust-toolchain.toml + - name: Setup Stable + if: runner.os != 'Windows' + shell: bash + run: | + mv rust-toolchain.toml.stable rust-toolchain.toml - name: Check versions shell: bash run: | diff --git a/.github/pr-labeler.yml b/.github/pr-labeler.yml index 5ed879bb1..54962bdcd 100644 --- a/.github/pr-labeler.yml +++ b/.github/pr-labeler.yml @@ -4,10 +4,14 @@ rust: - changed-files: - any-glob-to-any-file: crates/**/* -app: +"@gitbutler/desktop": - changed-files: - - any-glob-to-any-file: app/**/* + - any-glob-to-any-file: apps/desktop/**/* -ui: +"@gitbutler/web": +- changed-files: + - any-glob-to-any-file: app/web/**/* + +"@gitbutler/ui": - changed-files: - any-glob-to-any-file: packages/ui/**/* diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index c2e51f9b6..a38230056 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -32,7 +32,6 @@ jobs: - *workflows - 'Cargo.lock' - 'Cargo.toml' - - 'rust-toolchain.toml' rust: &any-rust - *rust - 'crates/**' @@ -111,7 +110,6 @@ jobs: - [devtools] steps: - uses: actions/checkout@v4 - - uses: ./.github/actions/init-env-rust - uses: ./.github/actions/check-crate with: features: ${{ toJson(matrix.features) }} diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index 72aebcab2..5403ad3a7 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -14,17 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 - with: - node-version: lts/* - cache: "pnpm" - cache-dependency-path: | - pnpm-lock.yaml - - name: Install dependencies - run: pnpm install - - name: Build @gitbutler/ui - run: cd packages/ui && pnpm package + - uses: ./.github/actions/init-env-node - name: Get installed Playwright version id: playwright-version run: echo "PLAYWRIGHT_VERSION=$(node -e "console.log(require('./apps/desktop/package.json').devDependencies['@playwright/test'].substring(1))")" >> $GITHUB_ENV diff --git a/.gitignore b/.gitignore index 6ac51493d..0baefbbbe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ # will have compiled rust files and executables target/ +generated-archives/ +generated-do-not-edit/ + # editors .idea @@ -40,3 +43,6 @@ playwright-report # storybook *storybook.log storybook-static + +# Vercel +.vercel diff --git a/.vscode/settings.json b/.vscode/settings.json index 4f27203b9..30b5288f9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -29,5 +29,8 @@ }, "[json]": { "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[mdx]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" } } diff --git a/Cargo.lock b/Cargo.lock index 3793d779e..ea5b47ffc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -847,6 +847,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462" dependencies = [ "clap_builder", + "clap_derive", ] [[package]] @@ -861,6 +862,18 @@ dependencies = [ "strsim", ] +[[package]] +name = "clap_derive" +version = "4.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.68", +] + [[package]] name = "clap_lex" version = "0.7.1" @@ -1033,6 +1046,21 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crc32fast" version = "1.4.2" @@ -1396,17 +1424,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "errno" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - [[package]] name = "errno" version = "0.3.9" @@ -1417,16 +1434,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "event-listener" version = "2.5.3" @@ -1600,6 +1607,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "fsevent-sys" version = "4.1.0" @@ -2011,6 +2024,7 @@ dependencies = [ "gitbutler-fs", "gitbutler-git", "gitbutler-id", + "gitbutler-operating-modes", "gitbutler-oplog", "gitbutler-project", "gitbutler-reference", @@ -2045,9 +2059,14 @@ dependencies = [ "anyhow", "chrono", "clap", + "dirs-next", + "gitbutler-branch", + "gitbutler-branch-actions", + "gitbutler-diff", "gitbutler-oplog", "gitbutler-project", - "pager", + "gitbutler-reference", + "gix", ] [[package]] @@ -2170,6 +2189,16 @@ dependencies = [ "walkdir", ] +[[package]] +name = "gitbutler-operating-modes" +version = "0.0.0" +dependencies = [ + "anyhow", + "git2", + "gitbutler-command-context", + "serde", +] + [[package]] name = "gitbutler-oplog" version = "0.0.0" @@ -2247,6 +2276,7 @@ dependencies = [ "gitbutler-time", "gitbutler-url", "gitbutler-user", + "gix", "log", "resolve-path", "serde", @@ -2342,6 +2372,7 @@ dependencies = [ "log", "once_cell", "open 5.3.0", + "parking_lot 0.12.3", "pretty_assertions", "reqwest 0.12.5", "serde", @@ -2376,8 +2407,10 @@ dependencies = [ "gitbutler-storage", "gitbutler-url", "gitbutler-user", + "gix-testtools", "keyring", "once_cell", + "parking_lot 0.12.3", "serde_json", "tempfile", ] @@ -2420,6 +2453,7 @@ dependencies = [ "gitbutler-command-context", "gitbutler-error", "gitbutler-notify-debouncer", + "gitbutler-operating-modes", "gitbutler-oplog", "gitbutler-project", "gitbutler-reference", @@ -2448,7 +2482,7 @@ dependencies = [ "gix-date", "gix-diff", "gix-dir", - "gix-discover", + "gix-discover 0.33.0", "gix-features", "gix-filter", "gix-fs", @@ -2466,7 +2500,7 @@ dependencies = [ "gix-path", "gix-pathspec", "gix-prompt", - "gix-ref", + "gix-ref 0.45.0", "gix-refspec", "gix-revision", "gix-revwalk", @@ -2570,7 +2604,7 @@ dependencies = [ "gix-features", "gix-glob", "gix-path", - "gix-ref", + "gix-ref 0.45.0", "gix-sec", "memchr", "once_cell", @@ -2641,7 +2675,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c975679aa00dd2d757bfd3ddb232e8a188c0094c3306400575a0813858b1365" dependencies = [ "bstr", - "gix-discover", + "gix-discover 0.33.0", "gix-fs", "gix-ignore", "gix-index", @@ -2654,6 +2688,22 @@ dependencies = [ "thiserror", ] +[[package]] +name = "gix-discover" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc27c699b63da66b50d50c00668bc0b7e90c3a382ef302865e891559935f3dbf" +dependencies = [ + "bstr", + "dunce", + "gix-fs", + "gix-hash", + "gix-path", + "gix-ref 0.44.1", + "gix-sec", + "thiserror", +] + [[package]] name = "gix-discover" version = "0.33.0" @@ -2665,7 +2715,7 @@ dependencies = [ "gix-fs", "gix-hash", "gix-path", - "gix-ref", + "gix-ref 0.45.0", "gix-sec", "thiserror", ] @@ -2956,6 +3006,28 @@ dependencies = [ "thiserror", ] +[[package]] +name = "gix-ref" +version = "0.44.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3394a2997e5bc6b22ebc1e1a87b41eeefbcfcff3dbfa7c4bd73cb0ac8f1f3e2e" +dependencies = [ + "gix-actor", + "gix-date", + "gix-features", + "gix-fs", + "gix-hash", + "gix-lock", + "gix-object", + "gix-path", + "gix-tempfile", + "gix-utils", + "gix-validate", + "memmap2", + "thiserror", + "winnow 0.6.13", +] + [[package]] name = "gix-ref" version = "0.45.0" @@ -3057,9 +3129,37 @@ dependencies = [ "libc", "once_cell", "parking_lot 0.12.3", + "signal-hook", + "signal-hook-registry", "tempfile", ] +[[package]] +name = "gix-testtools" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33fd7cd1816d78db635003c9e3fc667a1671689c678de2b92ce7c71ed2d58686" +dependencies = [ + "bstr", + "crc", + "fastrand 2.1.0", + "fs_extra", + "gix-discover 0.32.0", + "gix-fs", + "gix-ignore", + "gix-index", + "gix-lock", + "gix-tempfile", + "gix-worktree", + "io-close", + "is_ci", + "once_cell", + "parking_lot 0.12.3", + "tar", + "tempfile", + "winnow 0.6.13", +] + [[package]] name = "gix-trace" version = "0.1.9" @@ -3782,6 +3882,16 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "io-close" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cadcf447f06744f8ce713d2d6239bb5bde2c357a452397a9ed90c625da390bc" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "io-lifetimes" version = "1.0.11" @@ -3818,6 +3928,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "is_ci" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" + [[package]] name = "is_terminal_polyfill" version = "1.70.0" @@ -4579,9 +4695,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.66" +version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ "bitflags 2.6.0", "cfg-if", @@ -4664,16 +4780,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" -[[package]] -name = "pager" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2599211a5c97fbbb1061d3dc751fa15f404927e4846e07c643287d6d1f462880" -dependencies = [ - "errno 0.2.8", - "libc", -] - [[package]] name = "pango" version = "0.15.10" @@ -5666,7 +5772,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" dependencies = [ "bitflags 1.3.2", - "errno 0.3.9", + "errno", "io-lifetimes", "libc", "linux-raw-sys 0.3.8", @@ -5680,7 +5786,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags 2.6.0", - "errno 0.3.9", + "errno", "libc", "linux-raw-sys 0.4.14", "windows-sys 0.52.0", @@ -6072,6 +6178,16 @@ dependencies = [ "dirs 5.0.1", ] +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + [[package]] name = "signal-hook-registry" version = "1.4.2" diff --git a/Cargo.toml b/Cargo.toml index 868038db7..74524e821 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,15 +26,15 @@ members = [ "crates/gitbutler-time", "crates/gitbutler-commit", "crates/gitbutler-tagged-string", - "crates/gitbutler-url", + "crates/gitbutler-url", "crates/gitbutler-diff", + "crates/gitbutler-operating-modes", ] resolver = "2" [workspace.dependencies] # Add the `tracing` or `tracing-detail` features to see more of gitoxide in the logs. Useful to see which programs it invokes. -gix = { version = "0.64", default-features = false, features = [ -] } +gix = { version = "0.64", default-features = false, features = [] } git2 = { version = "0.18.3", features = [ "vendored-openssl", "vendored-libgit2", @@ -47,6 +47,7 @@ keyring = "2.3.3" anyhow = "1.0.86" fslock = "0.2.1" parking_lot = "0.12.3" +futures = "0.3.30" gitbutler-id = { path = "crates/gitbutler-id" } gitbutler-git = { path = "crates/gitbutler-git" } @@ -74,6 +75,7 @@ gitbutler-commit = { path = "crates/gitbutler-commit" } gitbutler-tagged-string = { path = "crates/gitbutler-tagged-string" } gitbutler-url = { path = "crates/gitbutler-url" } gitbutler-diff = { path = "crates/gitbutler-diff" } +gitbutler-operating-modes = { path = "crates/gitbutler-operating-modes" } [profile.release] codegen-units = 1 # Compile crates one after another so the compiler can optimize better diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 5e86da583..78f28ca3e 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -18,9 +18,9 @@ you right. Let's get started. - [Tokio](#tokio) - [Building](#building) - [Building on Windows](#building-on-windows) - - [File permissions](#file-permissions) - - [Perl](#perl) - - [Crosscompilation](#crosscompilation) + - [File permissions](#file-permissions) + - [Perl](#perl) + - [Crosscompilation](#crosscompilation) - [Design](#design) - [Contributing](#contributing) - [Some Other Random Notes](#some-other-random-notes) @@ -86,13 +86,13 @@ $ cargo build Now you should be able to run the app in development mode: ```bash -$ pnpm tauri dev +$ pnpm dev:desktop ``` By default it will not print debug logs to console. If you want debug logs, set `LOG_LEVEL` environment variable: ```bash -$ LOG_LEVEL=debug pnpm tauri dev +$ LOG_LEVEL=debug pnpm dev:desktop ``` ### Lint & format @@ -162,7 +162,22 @@ This will make an asset similar to our nightly build. Building on Windows is a bit of a tricky process. Here are some helpful tips. -### File permissions +#### Nightly Compiler + +As a few crates require nightly features on Windows, it's easiest to set an override +to automatically use a nightly compiler. + +```shell +rustup override add nightly-2024-07-01 +``` + +If a stable nightly isn't desired or necessary, the latest nightly compiler can also be used: + +```shell +rustup override add nightly +``` + +#### File permissions We use `pnpm`, which requires a relatively recent version of Node.js. Make sure that the latest stable version of Node.js is installed and @@ -184,7 +199,7 @@ npm config set prefix $env:APPDATA\npm Afterwards, add this folder to your PATH. -### Perl +#### Perl A Perl interpreter is required to be installed in order to configure the `openssl-sys` crate. We've used [Strawberry Perl](https://strawberryperl.com/) without issue. @@ -196,7 +211,7 @@ Note that it might appear that the build has hung or frozen on the `openssl-sys` It's not, it's just that Cargo can't report the status of a C/C++ build happening under the hood, and openssl is _large_. It'll take a while to compile. -### Crosscompilation +#### Crosscompilation This paragraph is about crosscompilation to x86_64-MSVC from ARM Windows, a configuration typical for people with Apple Silicon and Parallels VMs, diff --git a/apps/desktop/package.json b/apps/desktop/package.json index d8673bd18..6229737a8 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,5 +1,5 @@ { - "name": "@gitbutler/app", + "name": "@gitbutler/desktop", "private": true, "version": "0.0.0", "type": "module", diff --git a/apps/desktop/src/lib/baseBranch/baseBranch.ts b/apps/desktop/src/lib/baseBranch/baseBranch.ts index e22a86563..cf54c6c7f 100644 --- a/apps/desktop/src/lib/baseBranch/baseBranch.ts +++ b/apps/desktop/src/lib/baseBranch/baseBranch.ts @@ -1,4 +1,3 @@ -import { convertRemoteToWebUrl } from '$lib/utils/url'; import { Commit } from '$lib/vbranches/types'; import { Type } from 'class-transformer'; @@ -27,71 +26,7 @@ export class BaseBranch { return this.lastFetchedMs ? new Date(this.lastFetchedMs) : undefined; } - get pushRepoBaseUrl(): string { - return convertRemoteToWebUrl(this.pushRemoteUrl); - } - - get repoBaseUrl(): string { - return convertRemoteToWebUrl(this.remoteUrl); - } - - commitUrl(commitId: string): string | undefined { - // Different Git providers use different paths for the commit url: - if (this.isBitBucket) { - return `${this.pushRepoBaseUrl}/commits/${commitId}`; - } - if (this.isGitlab) { - return `${this.pushRepoBaseUrl}/-/commit/${commitId}`; - } - return `${this.pushRepoBaseUrl}/commit/${commitId}`; - } - get shortName() { return this.branchName.split('/').slice(-1)[0]; } - - branchUrl(upstreamBranchName: string | undefined) { - if (!upstreamBranchName) return undefined; - const baseBranchName = this.branchName.split('/')[1]; - const branchName = upstreamBranchName.split('/').slice(3).join('/'); - - if (this.pushRemoteName) { - if (this.isGitHub) { - // master...schacon:docs:Virtual-branch - const pushUsername = this.extractUsernameFromGitHubUrl(this.pushRemoteUrl); - const pushRepoName = this.extractRepoNameFromGitHubUrl(this.pushRemoteUrl); - return `${this.repoBaseUrl}/compare/${baseBranchName}...${pushUsername}:${pushRepoName}:${branchName}`; - } - } - - if (this.isBitBucket) { - return `${this.repoBaseUrl}/branch/${branchName}?dest=${baseBranchName}`; - } - // The following branch path is good for at least Gitlab and Github: - return `${this.repoBaseUrl}/compare/${baseBranchName}...${branchName}`; - } - - private extractUsernameFromGitHubUrl(url: string): string | null { - const regex = /github\.com[/:]([a-zA-Z0-9_-]+)\/.*/; - const match = url.match(regex); - return match ? match[1] : null; - } - - private extractRepoNameFromGitHubUrl(url: string): string | null { - const regex = /github\.com[/:]([a-zA-Z0-9_-]+)\/([a-zA-Z0-9_-]+)/; - const match = url.match(regex); - return match ? match[2] : null; - } - - private get isGitHub(): boolean { - return this.repoBaseUrl.includes('github.com'); - } - - private get isBitBucket(): boolean { - return this.repoBaseUrl.includes('bitbucket.org'); - } - - private get isGitlab(): boolean { - return this.repoBaseUrl.includes('gitlab.com'); - } } diff --git a/apps/desktop/src/lib/branch/ActiveBranchStatus.svelte b/apps/desktop/src/lib/branch/ActiveBranchStatus.svelte index d03dd76a7..b8d4492aa 100644 --- a/apps/desktop/src/lib/branch/ActiveBranchStatus.svelte +++ b/apps/desktop/src/lib/branch/ActiveBranchStatus.svelte @@ -1,24 +1,33 @@ {#if !remoteExists} @@ -82,7 +91,7 @@ outline shrinkable on:click={(e) => { - const url = $baseBranch?.branchUrl($branch.upstream?.name); + const url = gitHostBranch?.url; if (url) openExternalUrl(url); e.preventDefault(); e.stopPropagation(); diff --git a/apps/desktop/src/lib/branch/BranchCard.svelte b/apps/desktop/src/lib/branch/BranchCard.svelte index b8a520612..a53373567 100644 --- a/apps/desktop/src/lib/branch/BranchCard.svelte +++ b/apps/desktop/src/lib/branch/BranchCard.svelte @@ -1,5 +1,4 @@ - -{#if hasCommits} -
{ - if (entry.isIntersecting) { - isInViewport = true; - } else { - isInViewport = false; - } - }, - options: { - root: null, - rootMargin: '-1px', - threshold: 0 - } - }} - > - {#if canBePushed} - {#if $prompt} - - {/if} - { - isLoading = true; - try { - if (e.detail.action === BranchAction.Push) { - await branchController.pushBranch($branch.id, $branch.requiresForce); - $listingService?.refresh(); - $prMonitor?.refresh(); - $checksMonitor?.update(); - } else if (e.detail.action === BranchAction.Integrate) { - await branchController.mergeUpstream($branch.id); - } - } catch (e) { - console.error(e); - } finally { - isLoading = false; - } - }} - /> - {:else} -
- Your branch is up to date with the remote. - - - {@html emptyStateImg} - -
- {/if} -
-{/if} - - diff --git a/apps/desktop/src/lib/branch/BranchPreviewHeader.svelte b/apps/desktop/src/lib/branch/BranchPreviewHeader.svelte index e036ba5a2..b6cd78bb5 100644 --- a/apps/desktop/src/lib/branch/BranchPreviewHeader.svelte +++ b/apps/desktop/src/lib/branch/BranchPreviewHeader.svelte @@ -1,6 +1,7 @@ @@ -46,7 +48,7 @@ outline shrinkable on:click={(e) => { - const url = base?.branchUrl(branch.name); + const url = gitHostBranch?.url; if (url) openExternalUrl(url); e.preventDefault(); e.stopPropagation(); diff --git a/apps/desktop/src/lib/branches/branchListing.ts b/apps/desktop/src/lib/branches/branchListing.ts index 84cab20ce..ca56fa658 100644 --- a/apps/desktop/src/lib/branches/branchListing.ts +++ b/apps/desktop/src/lib/branches/branchListing.ts @@ -1,20 +1,44 @@ import { invoke } from '$lib/backend/ipc'; +import { Transform } from 'class-transformer'; import { plainToInstance } from 'class-transformer'; export class BranchListingService { constructor(private projectId: string) {} - async list() { + async list(filter: BranchListingFilter | undefined = undefined) { try { const branches = plainToInstance( BranchListing, - await invoke('list_branches', { projectId: this.projectId }) + await invoke('list_branches', { projectId: this.projectId, filter }) ); - console.log(branches); return branches; } catch (err: any) { console.error(err); } } + async get_branch_listing_details(branchNames: string[]) { + try { + const branches = plainToInstance( + BranchListingDetails, + await invoke('get_branch_listing_details', { + projectId: this.projectId, + branchNames + }) + ); + return branches; + } catch (err: any) { + console.error(err); + } + } +} + +/// A filter that can be applied to the branch listing +export class BranchListingFilter { + /// If the value is true, the listing will only include branches that have the same author as the current user. + /// If the value is false, the listing will include only branches that are not created by the user. + ownBranches?: boolean; + /// If the value is true, the listing will only include branches that are applied in the workspace. + /// If the value is false, the listing will only include branches that are not applied in the workspace. + applied?: boolean; } /// Represents a branch that exists for the repository @@ -30,23 +54,6 @@ export class BranchListing { remotes!: string[]; /// The branch may or may not have a virtual branch associated with it virtualBranch?: VirtualBranchReference | undefined; - /// The number of lines added within the branch - /// Since the virtual branch, local branch and the remote one can have different number of lines removed, - /// the value from the virtual branch (if present) takes the highest precedence, - /// followed by the local branch and then the remote branches (taking the max if there are multiple) - /// If this branch has a virutal branch, lines_added does NOT include the uncommitted lines. - linesAdded!: number; - /// The number of lines removed within the branch - /// Since the virtual branch, local branch and the remote one can have different number of lines removed, - /// the value from the virtual branch (if present) takes the highest precedence, - /// followed by the local branch and then the remote branches (taking the max if there are multiple) - /// If this branch has a virutal branch, lines_removed does NOT include the uncommitted lines. - linesRemoved!: number; - /// The number of files that were modified within the branch - /// Since the virtual branch, local branch and the remote one can have different number files modified, - /// the value from the virtual branch (if present) takes the highest precedence, - /// followed by the local branch and then the remote branches (taking the max if there are multiple) - numberOfFiles!: number; /// The number of commits associated with a branch /// Since the virtual branch, local branch and the remote one can have different number of commits, /// the value from the virtual branch (if present) takes the highest precedence, @@ -54,6 +61,7 @@ export class BranchListing { numberOfCommits!: number; /// Timestamp in milliseconds since the branch was last updated. /// This includes any commits, uncommited changes or even updates to the branch metadata (e.g. renaming). + @Transform((obj) => new Date(obj.value)) updatedAt!: number; /// A list of authors that have contributes commits to this branch. /// In the case of multiple remote tracking branches, it takes the full list of unique authors. @@ -80,3 +88,26 @@ export class Author { /// The email of the author as configured in the git config email?: string | undefined; } + +/// Represents a fat struct with all the data associated with a branch +export class BranchListingDetails { + /// The name of the branch (e.g. `main`, `feature/branch`), excluding the remote name + name!: string; + /// The number of lines added within the branch + /// Since the virtual branch, local branch and the remote one can have different number of lines removed, + /// the value from the virtual branch (if present) takes the highest precedence, + /// followed by the local branch and then the remote branches (taking the max if there are multiple). + /// If this branch has a virutal branch, lines_added does NOT include the uncommitted lines. + lines_added!: number; + /// The number of lines removed within the branch + /// Since the virtual branch, local branch and the remote one can have different number of lines removed, + /// the value from the virtual branch (if present) takes the highest precedence, + /// followed by the local branch and then the remote branches (taking the max if there are multiple) + /// If this branch has a virutal branch, lines_removed does NOT include the uncommitted lines. + lines_removed!: number; + /// The number of files that were modified within the branch + /// Since the virtual branch, local branch and the remote one can have different number files modified, + /// the value from the virtual branch (if present) takes the highest precedence, + /// followed by the local branch and then the remote branches (taking the max if there are multiple) + number_of_files!: number; +} diff --git a/apps/desktop/src/lib/commit/CommitAction.svelte b/apps/desktop/src/lib/commit/CommitAction.svelte new file mode 100644 index 000000000..418a7a86c --- /dev/null +++ b/apps/desktop/src/lib/commit/CommitAction.svelte @@ -0,0 +1,81 @@ + + +
{ + if (entry.isIntersecting) { + isNotInViewport = false; + } else { + isNotInViewport = true; + } + }, + options: { + root: null, + rootMargin: `-100% 0 0 0`, + threshold: 0 + } + }} +> +
+ {@render lines()} +
+
+ {@render action()} +
+
+ + diff --git a/apps/desktop/src/lib/commit/CommitList.svelte b/apps/desktop/src/lib/commit/CommitList.svelte index 46f76ad82..7c3cbb8dc 100644 --- a/apps/desktop/src/lib/commit/CommitList.svelte +++ b/apps/desktop/src/lib/commit/CommitList.svelte @@ -1,4 +1,5 @@ -
+