[M] Rebrand

This commit is contained in:
2026-05-08 05:34:08 +00:00
parent ebeb045c51
commit 9a9cbba767
14 changed files with 102 additions and 99 deletions
Generated
+21 -21
View File
@@ -420,27 +420,6 @@ dependencies = [
"wasip3",
]
[[package]]
name = "git-sync"
version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"console",
"dialoguer",
"directories",
"hmac",
"regex",
"reqwest",
"serde",
"serde_json",
"sha2",
"tempfile",
"tiny_http",
"toml",
"url",
]
[[package]]
name = "hashbrown"
version = "0.15.5"
@@ -981,6 +960,27 @@ dependencies = [
"thiserror 1.0.69",
]
[[package]]
name = "refray"
version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"console",
"dialoguer",
"directories",
"hmac",
"regex",
"reqwest",
"serde",
"serde_json",
"sha2",
"tempfile",
"tiny_http",
"toml",
"url",
]
[[package]]
name = "regex"
version = "1.12.3"
+1 -1
View File
@@ -1,5 +1,5 @@
[package]
name = "git-sync"
name = "refray"
version = "0.1.0"
edition = "2024"
+33 -33
View File
@@ -1,6 +1,6 @@
# git-sync
# refray
`git-sync` mirrors repositories between Git hosting providers when you run it. It can run as a one-shot sync command, or as a webhook receiver that triggers one-repo syncs after push events.
`refray` mirrors repositories between Git hosting providers when you run it. It can run as a one-shot sync command, or as a webhook receiver that triggers one-repo syncs after push events.
Supported providers:
@@ -18,14 +18,14 @@ Forgejo uses the same API shape as Gitea.
cargo build --release
```
The binary will be at `target/release/git-sync`.
The binary will be at `target/release/refray`.
## Configure
Run the interactive configuration wizard:
```sh
git-sync config
refray config
```
The wizard creates or updates the config file. It asks for profile or organization URLs, reuses existing credentials when it can, asks for a PAT only when needed, then offers webhook setup. Webhooks are strongly recommended because they sync soon after pushes and greatly reduce the chance of divergent histories.
@@ -43,7 +43,7 @@ Example wizard flow:
PAT quick setup:
- GitHub: open `https://github.com/settings/tokens`, create a classic PAT with `repo` permissions, then copy the token.
- GitLab: open `<base-url>/-/user_settings/personal_access_tokens?name=git-sync&scopes=api,write_repository`, select `api` and `write_repository`, create the token, then copy it.
- GitLab: open `<base-url>/-/user_settings/personal_access_tokens?name=refray&scopes=api,write_repository`, select `api` and `write_repository`, create the token, then copy it.
- Gitea: open `<base-url>/user/settings/applications`, create a token with repository access, then copy it.
- Forgejo: open `<base-url>/user/settings/applications`, create a token with repository access, then copy it.
@@ -62,54 +62,54 @@ Set `api_url` in the TOML if your instance is different.
Run all configured mirror groups:
```sh
git-sync sync
refray sync
```
Run one group:
```sh
git-sync sync --group personal
refray sync --group personal
```
Preview commands without writing to Git remotes:
```sh
git-sync sync --dry-run
refray sync --dry-run
```
Sync only repositories whose names match a regex:
```sh
git-sync sync --repo-pattern '^(foo|bar)-'
refray sync --repo-pattern '^(foo|bar)-'
```
Retry only repositories that failed during the previous non-dry-run sync:
```sh
git-sync sync --retry-failed
refray sync --retry-failed
```
Control repo-level parallelism:
```sh
git-sync sync --jobs 8
refray sync --jobs 8
```
While jobs run, the bottom of the terminal shows one live status line per worker. When a repository finishes, its detailed log is printed as one complete block above those status lines. The default is 4 workers; use `--jobs 1` for serial sync.
`git-sync` stores a small ref cache in the work directory. On later runs it first checks each repository with `git ls-remote --heads --tags`; when all endpoints report the same refs as the last successful sync, or the existing local bare mirror cache already has those refs, it skips the full fetch/push pass for that repository.
`refray` stores a small ref cache in the work directory. On later runs it first checks each repository with `git ls-remote --heads --tags`; when all endpoints report the same refs as the last successful sync, or the existing local bare mirror cache already has those refs, it skips the full fetch/push pass for that repository.
Use cron or another scheduler for automatic execution:
```cron
*/15 * * * * GITHUB_TOKEN=... GITEA_TOKEN=... /path/to/git-sync sync
*/15 * * * * GITHUB_TOKEN=... GITEA_TOKEN=... /path/to/refray sync
```
## Webhooks
Webhook mode reduces the window for divergent commits by syncing a repository immediately after a provider sends a push event. It is still conservative: if two endpoints receive independent commits before webhook sync catches up, the normal divergence rules still apply.
The interactive wizard can configure webhooks for you. During setup it starts a temporary test listener on `127.0.0.1:8787`, asks for the public URL, checks that the URL is reachable from the current machine, creates a webhook secret, and can enable periodic full syncs while `git-sync serve` is running.
The interactive wizard can configure webhooks for you. During setup it starts a temporary test listener on `127.0.0.1:8787`, asks for the public URL, checks that the URL is reachable from the current machine, creates a webhook secret, and can enable periodic full syncs while `refray serve` is running.
Example config:
@@ -125,66 +125,66 @@ reachability_check_interval_minutes = 15
Start the receiver:
```sh
git-sync serve \
refray serve \
--listen 127.0.0.1:8787
```
Expose that listener with your reverse proxy or tunnel, then install repository webhooks. If `[webhook]` is configured, the URL and secret can come from config:
```sh
git-sync webhook install
refray webhook install
```
Manual `webhook install` always checks the selected repositories on the provider and repairs or records the hook state. To install or repair one repository exactly:
```sh
git-sync webhook install important-repo
refray webhook install important-repo
```
You can also pass them explicitly:
```sh
git-sync webhook install \
refray webhook install \
--url https://mirror.example.com/webhook \
--secret-env GIT_SYNC_WEBHOOK_SECRET
--secret-env REFRAY_WEBHOOK_SECRET
```
Useful install filters:
```sh
git-sync webhook install \
refray webhook install \
--url https://mirror.example.com/webhook \
--secret-env GIT_SYNC_WEBHOOK_SECRET \
--secret-env REFRAY_WEBHOOK_SECRET \
--group personal \
--repo-pattern '^important-'
```
The receiver accepts `POST /` and `POST /webhook`. It verifies GitHub/Gitea HMAC SHA-256 signatures and GitLab webhook tokens, then queues `git-sync sync --group <group> --repo-pattern '^<repo>$'` internally. Duplicate events for the same group/repo are coalesced while a job is queued or running. Sync jobs are serialized inside the receiver so the local ref and failure caches stay consistent.
The receiver accepts `POST /` and `POST /webhook`. It verifies GitHub/Gitea HMAC SHA-256 signatures and GitLab webhook tokens, then queues `refray sync --group <group> --repo-pattern '^<repo>$'` internally. Duplicate events for the same group/repo are coalesced while a job is queued or running. Sync jobs are serialized inside the receiver so the local ref and failure caches stay consistent.
When `[webhook].install = true`, normal `git-sync sync` also checks webhook installation status and installs missing webhooks for repositories that have not been recorded yet. Installation status is stored in `webhook-state.toml` under the work directory.
When `[webhook].install = true`, normal `refray sync` also checks webhook installation status and installs missing webhooks for repositories that have not been recorded yet. Installation status is stored in `webhook-state.toml` under the work directory.
To uninstall webhooks previously installed by `git-sync`:
To uninstall webhooks previously installed by `refray`:
```sh
git-sync webhook uninstall
refray webhook uninstall
```
Manual `webhook uninstall` checks repositories on the provider instead of trusting only local state. To uninstall one repository exactly:
```sh
git-sync webhook uninstall important-repo
refray webhook uninstall important-repo
```
To move installed hooks to a new public URL, use `webhook update`. It removes hooks matching the current configured `[webhook].url`, installs the new URL, updates `[webhook].url` in the config, and refreshes local webhook state:
```sh
git-sync webhook update --url https://new.example.com/webhook
refray webhook update --url https://new.example.com/webhook
```
Serve can also run periodic full syncs. The interval can be configured in `[webhook].full_sync_interval_minutes` or overridden at startup:
```sh
git-sync serve --full-sync-interval-minutes 30
refray serve --full-sync-interval-minutes 30
```
If `[webhook].reachability_check_interval_minutes` is configured, `serve` periodically checks that the public webhook URL is still reachable and logs a warning when it is not.
@@ -193,7 +193,7 @@ If `[webhook].reachability_check_interval_minutes` is configured, `serve` period
Each mirror group is treated as a set of equivalent namespaces. Repositories are matched by repository name across all endpoints.
For every repository name found in any endpoint, `git-sync` will:
For every repository name found in any endpoint, `refray` will:
1. Create missing repositories on the other endpoints when `create_missing = true`.
2. Fetch all branches and tags from each existing endpoint into a local bare mirror cache.
@@ -205,18 +205,18 @@ Branch conflict handling is intentionally conservative:
- If all endpoints agree on a branch tip, that tip is pushed everywhere.
- If one branch tip is a descendant of the others, the descendant wins and is pushed everywhere.
- If branch tips diverged, `conflict_resolution` controls what happens.
- If `allow_force = true` or `git-sync sync --force` is used, a diverged branch chooses the newest commit timestamp and force-pushes it.
- If `allow_force = true` or `refray sync --force` is used, a diverged branch chooses the newest commit timestamp and force-pushes it.
Conflict resolution strategies are configured per mirror group:
- `fail`: fail the repository sync when branch tips diverge.
- `auto_rebase`: rebase divergent commits in endpoint order into one branch history, push fast-forward updates normally, and force-push only endpoints whose original tip was rewritten. If rebase hits a file conflict, fail.
- `pull_request`: push temporary `git-sync/conflicts/...` branches and open provider pull requests/merge requests so a person can resolve the divergence.
- `pull_request`: push temporary `refray/conflicts/...` branches and open provider pull requests/merge requests so a person can resolve the divergence.
- `auto_rebase_pull_request`: try `auto_rebase` first, then fall back to pull requests if rebase hits a conflict.
When a previously opened conflict pull request is merged, the next sync sees the merged branch as the winning tip, pushes it to the other endpoints, and closes stale `git-sync/conflicts/...` pull requests for that branch.
When a previously opened conflict pull request is merged, the next sync sees the merged branch as the winning tip, pushes it to the other endpoints, and closes stale `refray/conflicts/...` pull requests for that branch.
Branch deletion is propagated only when it is safe to infer intent. If a branch existed on every endpoint in the previous successful sync, then disappears from one endpoint while the remaining endpoints still have the previous tip, `git-sync` deletes it from the remaining endpoints instead of recreating it. If the branch was deleted on one endpoint but changed elsewhere, it is treated as a conflict and skipped.
Branch deletion is propagated only when it is safe to infer intent. If a branch existed on every endpoint in the previous successful sync, then disappears from one endpoint while the remaining endpoints still have the previous tip, `refray` deletes it from the remaining endpoints instead of recreating it. If the branch was deleted on one endpoint but changed elsewhere, it is treated as a conflict and skipped.
Tags are fetched into provider-specific cache refs and pushed only when the tag object agrees across providers or exists on one side. Divergent tags are skipped and reported. Tag deletion is not propagated.
+6 -4
View File
@@ -7,6 +7,8 @@ use anyhow::{Context, Result, anyhow, bail};
use directories::ProjectDirs;
use serde::{Deserialize, Serialize};
const APP_NAME: &str = "refray";
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct Config {
#[serde(default)]
@@ -223,15 +225,15 @@ impl EndpointConfig {
}
pub fn default_config_path() -> PathBuf {
ProjectDirs::from("dev", "git-sync", "git-sync")
ProjectDirs::from("dev", APP_NAME, APP_NAME)
.map(|dirs| dirs.config_dir().join("config.toml"))
.unwrap_or_else(|| PathBuf::from("git-sync.toml"))
.unwrap_or_else(|| PathBuf::from("refray.toml"))
}
pub fn default_work_dir() -> PathBuf {
ProjectDirs::from("dev", "git-sync", "git-sync")
ProjectDirs::from("dev", APP_NAME, APP_NAME)
.map(|dirs| dirs.cache_dir().join("mirrors"))
.unwrap_or_else(|| PathBuf::from(".git-sync-cache"))
.unwrap_or_else(|| PathBuf::from(".refray-cache"))
}
fn trim_end(value: &str) -> &str {
+2 -2
View File
@@ -649,9 +649,9 @@ impl GitMirror {
"-C",
worktree.to_str().unwrap(),
"-c",
"user.name=git-sync",
"user.name=refray",
"-c",
"user.email=git-sync@example.invalid",
"user.email=refray@example.invalid",
]
.into_iter()
.chain(args),
+7 -7
View File
@@ -43,11 +43,11 @@ pub fn run_config_wizard(path: &Path) -> Result<()> {
let theme = ColorfulTheme::default();
println!();
println!("{}", style("git-sync configuration wizard").cyan().bold());
println!("{}", style("refray configuration wizard").cyan().bold());
let description = if existing_config {
"Review, add, edit, or delete sync groups."
} else {
"Enter profile or organization URLs, then git-sync will build the mirror group."
"Enter profile or organization URLs, then refray will build the mirror group."
};
println!("{}", style(description).dim());
println!();
@@ -214,7 +214,7 @@ fn prompt_webhook_setup_styled(config: &mut Config, theme: &ColorfulTheme) -> Re
style(DEFAULT_WEBHOOK_LISTEN).bold()
);
println!(
" {} If git-sync serve is already running there, you can continue.",
" {} If refray serve is already running there, you can continue.",
style("-").yellow()
);
None
@@ -280,7 +280,7 @@ fn print_webhook_url_instructions() {
println!(
" {} Start the receiver with: {}",
style("-").cyan(),
style(format!("git-sync serve --listen {DEFAULT_WEBHOOK_LISTEN}")).bold()
style(format!("refray serve --listen {DEFAULT_WEBHOOK_LISTEN}")).bold()
);
println!(
" {} The receiver accepts: {} and {}",
@@ -360,7 +360,7 @@ fn respond_demo_webhook_request(request: Request) {
let (status, body) = if path == "/" || path == "/webhook" {
(
StatusCode(200),
"git-sync webhook setup listener\nThis temporary server only confirms that your public URL reaches this machine.\nAfter saving config, run git-sync serve for real webhooks.\n",
"refray webhook setup listener\nThis temporary server only confirms that your public URL reaches this machine.\nAfter saving config, run refray serve for real webhooks.\n",
)
} else {
(StatusCode(404), "not found\n")
@@ -706,7 +706,7 @@ fn prompt_conflict_resolution_styled(
];
let default = existing.map(conflict_resolution_index).unwrap_or(3);
let index = Select::with_theme(theme)
.with_prompt("How should git-sync resolve branch conflicts?")
.with_prompt("How should refray resolve branch conflicts?")
.items(options)
.default(default)
.interact()?;
@@ -1136,7 +1136,7 @@ fn token_creation_url(provider: &ProviderKind, base_url: &str) -> String {
ProviderKind::Github => format!("{base}/settings/tokens"),
ProviderKind::Gitlab => {
format!(
"{base}/-/user_settings/personal_access_tokens?name=git-sync&scopes=api,write_repository"
"{base}/-/user_settings/personal_access_tokens?name=refray&scopes=api,write_repository"
)
}
ProviderKind::Gitea => format!("{base}/user/settings/applications"),
+1 -1
View File
@@ -21,7 +21,7 @@ use crate::webhook::{
};
#[derive(Parser, Debug)]
#[command(name = "git-sync")]
#[command(name = "refray")]
#[command(about = "Mirror repositories between Git hosting providers")]
struct Cli {
#[arg(long, global = true, value_name = "PATH")]
+1 -1
View File
@@ -848,7 +848,7 @@ impl<'a> ProviderClient<'a> {
request: reqwest::blocking::RequestBuilder,
) -> Result<reqwest::blocking::RequestBuilder> {
let mut headers = HeaderMap::new();
headers.insert(USER_AGENT, HeaderValue::from_static("git-sync/0.1"));
headers.insert(USER_AGENT, HeaderValue::from_static("refray/0.1"));
headers.insert(ACCEPT, HeaderValue::from_static("application/json"));
match self.site.provider {
ProviderKind::Github => {
+3 -3
View File
@@ -35,7 +35,7 @@ use self::state::{
};
pub const DEFAULT_JOBS: usize = 4;
const CONFLICT_BRANCH_ROOT: &str = "git-sync/conflicts/";
const CONFLICT_BRANCH_ROOT: &str = "refray/conflicts/";
#[derive(Clone, Debug)]
pub struct SyncOptions {
@@ -1018,11 +1018,11 @@ fn open_conflict_pull_requests(
};
mirror_repo.push_branch_updates(remotes, &[update])?;
let title = format!(
"Resolve git-sync conflict: {} from {}",
"Resolve refray conflict: {} from {}",
conflict.branch, source_remote
);
let body = format!(
"git-sync detected divergent branch tips and opened this pull request to merge {}@{} into {}@{}.",
"refray detected divergent branch tips and opened this pull request to merge {}@{} into {}@{}.",
source_remote,
short_sha(source_sha),
target_remote,
+15 -15
View File
@@ -2,7 +2,7 @@ use super::*;
#[test]
fn cli_config_opens_wizard() {
let cli = Cli::try_parse_from(["git-sync", "config"]).unwrap();
let cli = Cli::try_parse_from(["refray", "config"]).unwrap();
assert!(matches!(cli.command, Command::Config));
}
@@ -10,11 +10,11 @@ fn cli_config_opens_wizard() {
#[test]
fn cli_rejects_removed_config_subcommands() {
for args in [
["git-sync", "config", "wizard"].as_slice(),
["git-sync", "config", "init"].as_slice(),
["git-sync", "config", "show"].as_slice(),
["git-sync", "config", "site", "list"].as_slice(),
["git-sync", "config", "mirror", "list"].as_slice(),
["refray", "config", "wizard"].as_slice(),
["refray", "config", "init"].as_slice(),
["refray", "config", "show"].as_slice(),
["refray", "config", "site", "list"].as_slice(),
["refray", "config", "mirror", "list"].as_slice(),
] {
assert!(Cli::try_parse_from(args).is_err());
}
@@ -23,7 +23,7 @@ fn cli_rejects_removed_config_subcommands() {
#[test]
fn cli_accepts_sync_repo_pattern() {
let cli = Cli::try_parse_from([
"git-sync",
"refray",
"sync",
"--repo-pattern",
"^(foo|bar)-",
@@ -40,7 +40,7 @@ fn cli_accepts_sync_repo_pattern() {
#[test]
fn cli_accepts_sync_retry_failed() {
let cli = Cli::try_parse_from(["git-sync", "sync", "--retry-failed"]).unwrap();
let cli = Cli::try_parse_from(["refray", "sync", "--retry-failed"]).unwrap();
let Command::Sync(args) = cli.command else {
panic!("parsed unexpected command");
@@ -50,7 +50,7 @@ fn cli_accepts_sync_retry_failed() {
#[test]
fn cli_accepts_sync_jobs() {
let cli = Cli::try_parse_from(["git-sync", "sync", "--jobs", "8"]).unwrap();
let cli = Cli::try_parse_from(["refray", "sync", "--jobs", "8"]).unwrap();
let Command::Sync(args) = cli.command else {
panic!("parsed unexpected command");
@@ -61,7 +61,7 @@ fn cli_accepts_sync_jobs() {
#[test]
fn cli_accepts_webhook_serve() {
let cli = Cli::try_parse_from([
"git-sync",
"refray",
"serve",
"--listen",
"127.0.0.1:9000",
@@ -86,7 +86,7 @@ fn cli_accepts_webhook_serve() {
#[test]
fn cli_accepts_webhook_install() {
let cli = Cli::try_parse_from([
"git-sync",
"refray",
"webhook",
"install",
"repo-one",
@@ -118,7 +118,7 @@ fn cli_accepts_webhook_install() {
#[test]
fn cli_accepts_webhook_install_repo_pattern() {
let cli = Cli::try_parse_from([
"git-sync",
"refray",
"webhook",
"install",
"--url",
@@ -140,7 +140,7 @@ fn cli_accepts_webhook_install_repo_pattern() {
#[test]
fn cli_accepts_webhook_uninstall() {
let cli = Cli::try_parse_from([
"git-sync",
"refray",
"webhook",
"uninstall",
"repo-one",
@@ -170,7 +170,7 @@ fn cli_accepts_webhook_uninstall() {
#[test]
fn cli_accepts_webhook_update() {
let cli = Cli::try_parse_from([
"git-sync",
"refray",
"webhook",
"update",
"--url",
@@ -193,7 +193,7 @@ fn cli_accepts_webhook_update() {
#[test]
fn cli_rejects_scoped_webhook_update() {
let result = Cli::try_parse_from([
"git-sync",
"refray",
"webhook",
"update",
"repo-one",
+3 -3
View File
@@ -123,7 +123,7 @@ fn wizard_can_enable_webhooks() {
);
let output = String::from_utf8(output).unwrap();
assert!(output.contains("git-sync serve --listen 127.0.0.1:8787"));
assert!(output.contains("refray serve --listen 127.0.0.1:8787"));
assert!(output.contains("cloudflared tunnel --url http://127.0.0.1:8787"));
assert!(output.contains("POST / and POST /webhook"));
assert!(output.contains("temporary listener on 127.0.0.1:8787"));
@@ -539,7 +539,7 @@ fn token_creation_urls_are_provider_specific() {
);
assert_eq!(
token_creation_url(&ProviderKind::Gitlab, "https://gitlab.example.test"),
"https://gitlab.example.test/-/user_settings/personal_access_tokens?name=git-sync&scopes=api,write_repository"
"https://gitlab.example.test/-/user_settings/personal_access_tokens?name=refray&scopes=api,write_repository"
);
assert_eq!(
token_creation_url(&ProviderKind::Gitea, "gitea.example.test"),
@@ -561,6 +561,6 @@ fn demo_webhook_server_answers_reachability_checks() {
response
.text()
.unwrap()
.contains("git-sync webhook setup listener")
.contains("refray webhook setup listener")
);
}
+4 -4
View File
@@ -11,7 +11,7 @@ where
R: BufRead,
W: Write,
{
writeln!(writer, "git-sync configuration wizard")?;
writeln!(writer, "refray configuration wizard")?;
if config.mirrors.is_empty() {
add_sync_group(reader, writer, &mut config)?;
write_sync_groups(&config, writer)?;
@@ -182,7 +182,7 @@ where
)?;
writeln!(
writer,
"Start the receiver with: git-sync serve --listen 127.0.0.1:8787"
"Start the receiver with: refray serve --listen 127.0.0.1:8787"
)?;
writeln!(writer, "The receiver accepts: POST / and POST /webhook")?;
writeln!(
@@ -195,7 +195,7 @@ where
)?;
writeln!(
writer,
"During the real wizard, git-sync starts a temporary listener on 127.0.0.1:8787 so you can test the tunnel now."
"During the real wizard, refray starts a temporary listener on 127.0.0.1:8787 so you can test the tunnel now."
)?;
Ok(())
}
@@ -431,7 +431,7 @@ where
.map(conflict_resolution_value)
.unwrap_or("auto-rebase + pull-request");
loop {
writeln!(writer, "How should git-sync resolve branch conflicts?")?;
writeln!(writer, "How should refray resolve branch conflicts?")?;
writeln!(writer, " 1. fail")?;
writeln!(writer, " 2. auto-rebase and fail on file conflict")?;
writeln!(writer, " 3. pull-request")?;
+4 -4
View File
@@ -277,7 +277,7 @@ fn open_pull_request_posts_github_pull_when_missing() {
"request was {request}"
);
assert!(request.contains("Resolve conflict"));
assert!(request.contains("git-sync/conflicts/main/from-b-abc123"));
assert!(request.contains("refray/conflicts/main/from-b-abc123"));
assert!(request.contains("main"));
}
_ => unreachable!(),
@@ -305,7 +305,7 @@ fn open_pull_request_posts_github_pull_when_missing() {
&PullRequestRequest {
title: "Resolve conflict".to_string(),
body: "Body".to_string(),
head_branch: "git-sync/conflicts/main/from-b-abc123".to_string(),
head_branch: "refray/conflicts/main/from-b-abc123".to_string(),
base_branch: "main".to_string(),
},
)
@@ -321,7 +321,7 @@ fn close_pull_requests_by_head_prefix_closes_matching_github_pulls() {
vec![
(
"200 OK",
r#"[{"number":7,"head":{"ref":"git-sync/conflicts/main/from-b-abc123"}},{"number":8,"head":{"ref":"feature"}}]"#,
r#"[{"number":7,"head":{"ref":"refray/conflicts/main/from-b-abc123"}},{"number":8,"head":{"ref":"feature"}}]"#,
),
("200 OK", r#"{"number":7}"#),
],
@@ -361,7 +361,7 @@ fn close_pull_requests_by_head_prefix_closes_matching_github_pulls() {
description: None,
},
"main",
"git-sync/conflicts/main/",
"refray/conflicts/main/",
)
.unwrap();
+1
View File
@@ -182,6 +182,7 @@ fn conflict_branch_prefixes_are_reversible_not_slug_collisions() {
assert_ne!(slash_branch, dash_branch);
assert!(slash_branch.starts_with(CONFLICT_BRANCH_ROOT));
assert!(dash_branch.starts_with(CONFLICT_BRANCH_ROOT));
assert!(conflict_pr_branch("main", "gitea", "abc123").starts_with(CONFLICT_BRANCH_ROOT));
assert_eq!(
conflict_pr_base_branch(&format!("{slash_branch}from-gitea-abc123")),
Some("release/foo".to_string())