mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-25 10:33:21 +03:00
Merged origin/master into keyboard-shortcuts
This commit is contained in:
commit
5352eb1717
@ -1,7 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
/* eslint svelte/valid-compile: "off" */
|
/* eslint svelte/valid-compile: "off" */
|
||||||
|
/* - Required because spreading in prop destructuring still throws eslint errors */
|
||||||
import { renderers } from '$lib/utils/markdownRenderers';
|
import { renderers } from '$lib/utils/markdownRenderers';
|
||||||
import type { Tokens, Token } from 'marked';
|
import type { Tokens, Token } from 'marked';
|
||||||
|
import type { Component } from 'svelte';
|
||||||
|
|
||||||
type Props =
|
type Props =
|
||||||
| { type: 'init'; tokens: Token[] }
|
| { type: 'init'; tokens: Token[] }
|
||||||
@ -16,34 +18,33 @@
|
|||||||
| Tokens.ListItem
|
| Tokens.ListItem
|
||||||
| Tokens.List;
|
| Tokens.List;
|
||||||
|
|
||||||
let { type, ...rest }: Props = $props();
|
const { type, ...rest }: Props = $props();
|
||||||
|
|
||||||
// @ts-expect-error indexing on string union is having trouble
|
const CurrentComponent = renderers[type] as Component<Props>;
|
||||||
const CurrentComponent = renderers[type as Props['type']];
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if type && CurrentComponent}
|
{#if (!type || type === 'init') && 'tokens' in rest && rest.tokens}
|
||||||
|
{#each rest.tokens as token}
|
||||||
|
<svelte:self {...token} />
|
||||||
|
{/each}
|
||||||
|
{:else if renderers[type]}
|
||||||
{#if type === 'list'}
|
{#if type === 'list'}
|
||||||
{@const listItems = (rest as Extract<Props, { type: typeof type }>).items}
|
{@const listItems = (rest as Extract<Props, { type: 'list' }>).items}
|
||||||
<CurrentComponent {...rest}>
|
<CurrentComponent {...rest}>
|
||||||
{#each listItems as item}
|
{#each listItems as item}
|
||||||
{@const ChildComponent = renderers[item.type]}
|
{@const ChildComponent = renderers[item.type]}
|
||||||
<ChildComponent {...item}>
|
<ChildComponent {...item}>
|
||||||
<svelte:self tokens={item.tokens} {renderers} />
|
<svelte:self tokens={item.tokens} />
|
||||||
</ChildComponent>
|
</ChildComponent>
|
||||||
{/each}
|
{/each}
|
||||||
</CurrentComponent>
|
</CurrentComponent>
|
||||||
{:else}
|
{:else}
|
||||||
<CurrentComponent {...rest}>
|
<CurrentComponent this={renderers[type]} {...rest}>
|
||||||
{#if 'tokens' in rest}
|
{#if 'tokens' in rest && rest.tokens}
|
||||||
<svelte:self tokens={rest.tokens} />
|
<svelte:self tokens={rest.tokens} />
|
||||||
|
{:else if 'raw' in rest}
|
||||||
|
{rest.raw}
|
||||||
{/if}
|
{/if}
|
||||||
</CurrentComponent>
|
</CurrentComponent>
|
||||||
{/if}
|
{/if}
|
||||||
{:else if 'tokens' in rest && rest.tokens}
|
|
||||||
{#each rest.tokens as token}
|
|
||||||
<svelte:self {...token} />
|
|
||||||
{/each}
|
|
||||||
{:else if 'raw' in rest}
|
|
||||||
{@html rest.raw?.replaceAll('\n', '') ?? ''}
|
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
interface Props {
|
||||||
|
raw: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { raw }: Props = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{@html raw}
|
@ -1,11 +1,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { type Snippet } from 'svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
text: string;
|
children: Snippet;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { text }: Props = $props();
|
const { children }: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<span>
|
<span>
|
||||||
{text}
|
{@render children()}
|
||||||
</span>
|
</span>
|
||||||
|
@ -2,6 +2,7 @@ import Blockquote from '$lib/components/markdownRenderers/Blockquote.svelte';
|
|||||||
import Code from '$lib/components/markdownRenderers/Code.svelte';
|
import Code from '$lib/components/markdownRenderers/Code.svelte';
|
||||||
import Codespan from '$lib/components/markdownRenderers/Codespan.svelte';
|
import Codespan from '$lib/components/markdownRenderers/Codespan.svelte';
|
||||||
import Heading from '$lib/components/markdownRenderers/Heading.svelte';
|
import Heading from '$lib/components/markdownRenderers/Heading.svelte';
|
||||||
|
import Html from '$lib/components/markdownRenderers/Html.svelte';
|
||||||
import Image from '$lib/components/markdownRenderers/Image.svelte';
|
import Image from '$lib/components/markdownRenderers/Image.svelte';
|
||||||
import List from '$lib/components/markdownRenderers/List.svelte';
|
import List from '$lib/components/markdownRenderers/List.svelte';
|
||||||
import ListItem from '$lib/components/markdownRenderers/ListItem.svelte';
|
import ListItem from '$lib/components/markdownRenderers/ListItem.svelte';
|
||||||
@ -16,10 +17,12 @@ export const renderers = {
|
|||||||
code: Code,
|
code: Code,
|
||||||
codespan: Codespan,
|
codespan: Codespan,
|
||||||
text: Text,
|
text: Text,
|
||||||
|
html: Html,
|
||||||
list: List,
|
list: List,
|
||||||
list_item: ListItem,
|
list_item: ListItem,
|
||||||
heading: Heading,
|
heading: Heading,
|
||||||
paragraph: Paragraph
|
paragraph: Paragraph,
|
||||||
|
init: null
|
||||||
};
|
};
|
||||||
|
|
||||||
export const options = {
|
export const options = {
|
||||||
|
@ -159,7 +159,7 @@ pub fn unapply_ownership(
|
|||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let final_tree_oid = gitbutler_diff::write::hunks_onto_tree(ctx, &final_tree, diff)?;
|
let final_tree_oid = gitbutler_diff::write::hunks_onto_tree(ctx, &final_tree, diff, true)?;
|
||||||
let final_tree = repo
|
let final_tree = repo
|
||||||
.find_tree(final_tree_oid)
|
.find_tree(final_tree_oid)
|
||||||
.context("failed to find tree")?;
|
.context("failed to find tree")?;
|
||||||
|
@ -78,7 +78,7 @@ impl GitHunk {
|
|||||||
new_lines: 0,
|
new_lines: 0,
|
||||||
diff_lines: Default::default(),
|
diff_lines: Default::default(),
|
||||||
binary: false,
|
binary: false,
|
||||||
change_type: ChangeType::Modified,
|
change_type: ChangeType::Added,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -387,6 +387,11 @@ fn reverse_patch(patch: &BStr) -> Option<BString> {
|
|||||||
|
|
||||||
// returns `None` if the reversal failed
|
// returns `None` if the reversal failed
|
||||||
pub fn reverse_hunk(hunk: &GitHunk) -> Option<GitHunk> {
|
pub fn reverse_hunk(hunk: &GitHunk) -> Option<GitHunk> {
|
||||||
|
let new_change_type = match hunk.change_type {
|
||||||
|
ChangeType::Added => ChangeType::Deleted,
|
||||||
|
ChangeType::Deleted => ChangeType::Added,
|
||||||
|
ChangeType::Modified => ChangeType::Modified,
|
||||||
|
};
|
||||||
if hunk.binary {
|
if hunk.binary {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@ -397,7 +402,7 @@ pub fn reverse_hunk(hunk: &GitHunk) -> Option<GitHunk> {
|
|||||||
new_lines: hunk.old_lines,
|
new_lines: hunk.old_lines,
|
||||||
diff_lines: diff.into(),
|
diff_lines: diff.into(),
|
||||||
binary: hunk.binary,
|
binary: hunk.binary,
|
||||||
change_type: hunk.change_type,
|
change_type: new_change_type,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
use std::os::unix::prelude::PermissionsExt;
|
use std::os::unix::prelude::PermissionsExt;
|
||||||
use std::{borrow::Borrow, path::PathBuf};
|
use std::{borrow::Borrow, fs, path::PathBuf};
|
||||||
|
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use bstr::{BString, ByteSlice, ByteVec};
|
use bstr::{BString, ByteSlice, ByteVec};
|
||||||
@ -38,13 +38,14 @@ where
|
|||||||
let head_commit = git_repository.find_commit(commit_oid)?;
|
let head_commit = git_repository.find_commit(commit_oid)?;
|
||||||
let base_tree = head_commit.tree()?;
|
let base_tree = head_commit.tree()?;
|
||||||
|
|
||||||
hunks_onto_tree(ctx, &base_tree, files)
|
hunks_onto_tree(ctx, &base_tree, files, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hunks_onto_tree<T>(
|
pub fn hunks_onto_tree<T>(
|
||||||
ctx: &CommandContext,
|
ctx: &CommandContext,
|
||||||
base_tree: &git2::Tree,
|
base_tree: &git2::Tree,
|
||||||
files: impl IntoIterator<Item = (impl Borrow<PathBuf>, impl Borrow<Vec<T>>)>,
|
files: impl IntoIterator<Item = (impl Borrow<PathBuf>, impl Borrow<Vec<T>>)>,
|
||||||
|
allow_new_file: bool,
|
||||||
) -> Result<git2::Oid>
|
) -> Result<git2::Oid>
|
||||||
where
|
where
|
||||||
T: Into<GitHunk> + Clone,
|
T: Into<GitHunk> + Clone,
|
||||||
@ -62,7 +63,21 @@ where
|
|||||||
&& hunks[0].diff_lines.contains_str(b"Subproject commit");
|
&& hunks[0].diff_lines.contains_str(b"Subproject commit");
|
||||||
|
|
||||||
// if file exists
|
// if file exists
|
||||||
if full_path.exists() {
|
let full_path_exists = full_path.exists();
|
||||||
|
let discard_hunk = (hunks.len() == 1).then(|| &hunks[0]);
|
||||||
|
if full_path_exists || allow_new_file {
|
||||||
|
if discard_hunk.map_or(false, |hunk| hunk.change_type == crate::ChangeType::Deleted) {
|
||||||
|
// File was created but now that hunk is being discarded with an inversed hunk
|
||||||
|
builder.remove(rel_path);
|
||||||
|
fs::remove_file(full_path.clone()).or_else(|err| {
|
||||||
|
if err.kind() == std::io::ErrorKind::NotFound {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// if file is executable, use 755, otherwise 644
|
// if file is executable, use 755, otherwise 644
|
||||||
let mut filemode = git2::FileMode::Blob;
|
let mut filemode = git2::FileMode::Blob;
|
||||||
// check if full_path file is executable
|
// check if full_path file is executable
|
||||||
@ -115,7 +130,7 @@ where
|
|||||||
)?;
|
)?;
|
||||||
builder.upsert(rel_path, blob_oid, filemode);
|
builder.upsert(rel_path, blob_oid, filemode);
|
||||||
} else if let Ok(tree_entry) = base_tree.get_path(rel_path) {
|
} else if let Ok(tree_entry) = base_tree.get_path(rel_path) {
|
||||||
if hunks.len() == 1 && hunks[0].binary {
|
if discard_hunk.map_or(false, |hunk| hunk.binary) {
|
||||||
let new_blob_oid = &hunks[0].diff_lines;
|
let new_blob_oid = &hunks[0].diff_lines;
|
||||||
// convert string to Oid
|
// convert string to Oid
|
||||||
let new_blob_oid = new_blob_oid
|
let new_blob_oid = new_blob_oid
|
||||||
@ -178,6 +193,20 @@ where
|
|||||||
let new_blob_oid = git_repository.blob(blob_contents.as_bytes())?;
|
let new_blob_oid = git_repository.blob(blob_contents.as_bytes())?;
|
||||||
// upsert into the builder
|
// upsert into the builder
|
||||||
builder.upsert(rel_path, new_blob_oid, filemode);
|
builder.upsert(rel_path, new_blob_oid, filemode);
|
||||||
|
} else if !full_path_exists
|
||||||
|
&& discard_hunk.map_or(false, |hunk| hunk.change_type == crate::ChangeType::Added)
|
||||||
|
{
|
||||||
|
// File was deleted but now that hunk is being discarded with an inversed hunk
|
||||||
|
let mut all_diffs = BString::default();
|
||||||
|
for hunk in hunks {
|
||||||
|
all_diffs.push_str(&hunk.diff_lines);
|
||||||
|
}
|
||||||
|
let patch = Patch::from_bytes(&all_diffs)?;
|
||||||
|
let blob_contents =
|
||||||
|
apply([], &patch).context(format!("failed to apply {}", all_diffs))?;
|
||||||
|
|
||||||
|
let new_blob_oid = git_repository.blob(&blob_contents)?;
|
||||||
|
builder.upsert(rel_path, new_blob_oid, filemode);
|
||||||
} else {
|
} else {
|
||||||
// create a git blob from a file on disk
|
// create a git blob from a file on disk
|
||||||
let blob_oid = git_repository
|
let blob_oid = git_repository
|
||||||
|
Loading…
Reference in New Issue
Block a user