Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ jobs:
- name: Test
run: cargo test --verbose

# Guard Windows portability: the code must compile and the pure unit tests
# (ident / render / engine) must pass. The git-driven integration and view
# tests assume a POSIX shell and aren't run here yet.
# Verify Windows portability: the code must compile AND the full test suite
# (including the git-driven integration + view tests, which exercise the
# `cp`/binary-based rebase editors) must pass. Tests self-skip any git feature
# the runner's git lacks.
windows:
name: Windows
runs-on: windows-latest
Expand All @@ -56,7 +57,9 @@ jobs:
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Git version
run: git --version
- name: Build
run: cargo build --verbose
- name: Test (portable unit tests)
run: cargo test --lib -- --skip view
- name: Test
run: cargo test --verbose
17 changes: 11 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ works immediately (git's standard subcommand mechanism). `git queue setup` then
walks through the optional extras interactively (see below).

Prefer a prebuilt binary? Each [GitHub
release](https://github.com/freshtonic/git-queue/releases) ships archives and a
shell installer (built by [dist](https://opensource.axo.dev/cargo-dist/)) for
Linux and macOS (x86-64 and arm64):
release](https://github.com/freshtonic/git-queue/releases) ships archives and
install scripts (built by [dist](https://opensource.axo.dev/cargo-dist/)) for
macOS, Linux, and Windows. On macOS/Linux:

```sh
curl --proto '=https' --tlsv1.2 -LsSf \
Expand All @@ -67,9 +67,14 @@ On macOS or Linux you can also use Homebrew:
brew install freshtonic/git-queue/git-queue
```

Or just download an archive, extract it, and put `git-queue` on your `PATH`.
(Windows isn't packaged as a prebuilt binary yet — build it with `cargo install
git-queue`. Windows support is still in progress; see issue #11.)
On Windows, use the PowerShell installer:

```powershell
powershell -ExecutionPolicy ByPass -c "irm https://github.com/freshtonic/git-queue/releases/latest/download/git-queue-installer.ps1 | iex"
```

Or just download an archive for your platform (macOS/Linux `.tar.xz`, Windows
`.zip`), extract it, and put `git-queue` on your `PATH`.

### `git queue setup`

Expand Down
5 changes: 2 additions & 3 deletions dist-workspace.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@ cargo-dist-version = "0.32.0"
# CI backends to support
ci = "github"
# The installers to generate for each app.
# PowerShell/Windows are omitted until git-queue is Windows-portable (issue #11).
installers = ["homebrew", "shell"]
installers = ["homebrew", "shell", "powershell"]
# Homebrew tap to publish the generated formula to. dist pushes the formula on
# release using a token in the HOMEBREW_TAP_TOKEN secret (write access to the tap).
tap = "freshtonic/homebrew-git-queue"
# Actually run the formula-publishing job (not just generate the formula locally).
publish-jobs = ["homebrew"]
# Target platforms to build apps for (Rust target-triple syntax).
targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"]
targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"]
# Path that installers should place binaries in
install-path = "CARGO_HOME"
# Where to host releases
Expand Down
12 changes: 7 additions & 5 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,10 @@ <h2 style="margin:0; font-family:'JetBrains Mono',monospace; font-size:30px; fon
<pre class="gs-scroll" style="margin:0; padding:18px 20px; overflow-x:auto; font-family:'JetBrains Mono',monospace; font-size:13px; line-height:1.9; color:var(--text);"><span style="color:var(--green);">$</span> curl --proto '=https' --tlsv1.2 -LsSf \
https://github.com/freshtonic/git-queue/releases/latest/download/git-queue-installer.sh | sh</pre>
</div>
<div data-os-panel="windows" style="background:var(--surface); border:1px solid var(--line); border-radius:12px; overflow:hidden;">
<div style="display:flex; align-items:center; justify-content:space-between; padding:11px 16px; border-bottom:1px solid var(--line2); font-family:'JetBrains Mono',monospace; font-size:11.5px; color:var(--faint); text-transform:uppercase; letter-spacing:0.1em;"><span>install script&nbsp;·&nbsp;recommended</span><button id="btn-cp-ps" style="cursor:pointer; border:none; background:transparent; color:var(--dim); font-family:inherit; font-size:11px;">copy</button></div>
<pre class="gs-scroll" style="margin:0; padding:18px 20px; overflow-x:auto; font-family:'JetBrains Mono',monospace; font-size:13px; line-height:1.9; color:var(--text);"><span style="color:var(--green);">&gt;</span> powershell -ExecutionPolicy ByPass -c "irm https://github.com/freshtonic/git-queue/releases/latest/download/git-queue-installer.ps1 | iex"</pre>
</div>
<div data-os-panel="macos linux" style="background:var(--surface); border:1px solid var(--line); border-radius:12px; overflow:hidden;">
<div style="display:flex; align-items:center; justify-content:space-between; padding:11px 16px; border-bottom:1px solid var(--line2); font-family:'JetBrains Mono',monospace; font-size:11.5px; color:var(--faint); text-transform:uppercase; letter-spacing:0.1em;"><span>homebrew</span><button id="btn-cp-brew" style="cursor:pointer; border:none; background:transparent; color:var(--dim); font-family:inherit; font-size:11px;">copy</button></div>
<pre class="gs-scroll" style="margin:0; padding:16px 20px; overflow-x:auto; font-family:'JetBrains Mono',monospace; font-size:13.5px; color:var(--text);"><span style="color:var(--green);">$</span> brew install freshtonic/git-queue/git-queue</pre>
Expand All @@ -532,12 +536,9 @@ <h2 style="margin:0; font-family:'JetBrains Mono',monospace; font-size:30px; fon
<div style="display:flex; align-items:center; justify-content:space-between; padding:11px 16px; border-bottom:1px solid var(--line2); font-family:'JetBrains Mono',monospace; font-size:11.5px; color:var(--faint); text-transform:uppercase; letter-spacing:0.1em;"><span>cargo&nbsp;·&nbsp;from crates.io</span><button id="btn-cp-cargo" style="cursor:pointer; border:none; background:transparent; color:var(--dim); font-family:inherit; font-size:11px;">copy</button></div>
<pre style="margin:0; padding:16px 20px; font-family:'JetBrains Mono',monospace; font-size:13.5px; color:var(--text);"><span style="color:var(--green);">$</span> cargo install git-queue</pre>
</div>
<div data-os-panel="macos linux" style="background:var(--surface); border:1px solid var(--line); border-radius:12px; overflow:hidden;">
<div data-os-panel="macos linux windows" style="background:var(--surface); border:1px solid var(--line); border-radius:12px; overflow:hidden;">
<div style="padding:11px 16px; border-bottom:1px solid var(--line2); font-family:'JetBrains Mono',monospace; font-size:11.5px; color:var(--faint); text-transform:uppercase; letter-spacing:0.1em;">prebuilt binary</div>
<p style="margin:0; padding:16px 20px; font-size:14px; color:var(--muted);">Grab an archive for your platform (x86-64 or arm64) from the <a href="https://github.com/freshtonic/git-queue/releases/latest" style="color:var(--green);">latest GitHub release</a>, extract it, and put <code style="color:var(--text);">git-queue</code> on your <code>PATH</code>. Archives and the install script are built by <a href="https://opensource.axo.dev/cargo-dist/" style="color:var(--dim);">dist</a>.</p>
</div>
<div data-os-panel="windows" style="border:1px solid var(--amber); border-radius:12px; padding:16px 20px; background:color-mix(in srgb, var(--amber) 8%, var(--surface));">
<p style="margin:0; font-size:14px; color:var(--muted);">Windows support is still in progress (<a href="https://github.com/freshtonic/git-queue/issues/11" style="color:var(--amber);">#11</a>) — no prebuilt Windows binary or install script yet. <code style="color:var(--text);">cargo install git-queue</code> above builds it from source.</p>
<p style="margin:0; padding:16px 20px; font-size:14px; color:var(--muted);">Grab an archive for your platform from the <a href="https://github.com/freshtonic/git-queue/releases/latest" style="color:var(--green);">latest GitHub release</a> (macOS/Linux x86-64 &amp; arm64 <code>.tar.xz</code>, Windows x86-64 <code>.zip</code>), extract it, and put <code style="color:var(--text);">git-queue</code> on your <code>PATH</code>. Archives and install scripts are built by <a href="https://opensource.axo.dev/cargo-dist/" style="color:var(--dim);">dist</a>.</p>
</div>
<p style="margin:0; font-size:14.5px; color:var(--dim);">Because the binary is named <code style="color:var(--text);">git-queue</code>, Git runs <code style="color:var(--text);">git queue …</code> for you via its standard subcommand mechanism. Then <code style="color:var(--text);">git queue setup</code> installs the man page — afterwards both <code style="color:var(--text);">man git-queue</code> and <code style="color:var(--text);">git queue --help</code> work (Git routes <code>--help</code> to the man page; use <code style="color:var(--text);">git queue help</code> for inline output).</p>
</div>
Expand Down Expand Up @@ -764,6 +765,7 @@ <h2 style="margin:0; font-family:'JetBrains Mono',monospace; font-size:30px; fon
copyBtn('btn-cp-install', 'cargo install git-queue');
copyBtn('btn-cp-sh', "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/freshtonic/git-queue/releases/latest/download/git-queue-installer.sh | sh");
copyBtn('btn-cp-brew', 'brew install freshtonic/git-queue/git-queue');
copyBtn('btn-cp-ps', 'powershell -ExecutionPolicy ByPass -c "irm https://github.com/freshtonic/git-queue/releases/latest/download/git-queue-installer.ps1 | iex"');
copyBtn('btn-cp-cargo', 'cargo install git-queue');

// install: detect the visitor's OS and show its install options (with a manual toggle)
Expand Down
26 changes: 16 additions & 10 deletions src/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,7 @@ pub(crate) fn rebase_reorder_persist(
after: Option<&str>,
) -> Result<()> {
let exe = std::env::current_exe().context("cannot locate the git-queue executable")?;
let exe = shell_path(&exe);
let mut initial = Command::new("git");
initial.args([
"-c",
Expand All @@ -570,10 +571,7 @@ pub(crate) fn rebase_reorder_persist(
quiet_git(&mut initial);
// Our own binary rewrites the todo; the spec travels via the environment.
initial
.env(
"GIT_SEQUENCE_EDITOR",
format!("\"{}\" reorder-todo", exe.display()),
)
.env("GIT_SEQUENCE_EDITOR", format!("\"{exe}\" reorder-todo"))
.env("GIT_QUEUE_MOVE_SHAS", move_shas.join(" "))
.env("GIT_QUEUE_MOVE_AFTER", after.unwrap_or(""));
let _ = initial.status().context("failed to spawn `git rebase`")?;
Expand Down Expand Up @@ -604,7 +602,7 @@ pub(crate) fn rebase_with_todo_stop(base: &str, top_branch: &str, todo: &str) ->

// git invokes `$GIT_SEQUENCE_EDITOR <todofile>`, so `cp <our-todo>` becomes
// `cp <our-todo> <todofile>` — overwriting git's generated todo with ours.
let editor = format!("cp {}", shell_single_quote(&todo_path.to_string_lossy()));
let editor = format!("cp {}", shell_single_quote(&shell_path(&todo_path)));
let mut cmd = Command::new("git");
cmd.args([
"rebase",
Expand Down Expand Up @@ -638,6 +636,14 @@ fn shell_single_quote(s: &str) -> String {
format!("'{}'", s.replace('\'', r"'\''"))
}

/// Render a filesystem path for embedding in a shell command that git will run.
/// git executes `GIT_SEQUENCE_EDITOR`/`GIT_EDITOR` through a POSIX shell — its
/// bundled `sh` on Windows — which accepts forward slashes but mishandles
/// backslash separators, so normalise them. On Unix this is a no-op.
fn shell_path(p: &std::path::Path) -> String {
p.to_string_lossy().replace('\\', "/")
}

/// Rewrite `base..top_branch` with a caller-supplied todo (one commit marked
/// `reword`) and supply the new commit message from `message`. Both are
/// installed via `cp`-based editors, so no helper binary is needed. A reword is
Expand All @@ -655,8 +661,8 @@ pub(crate) fn rebase_with_todo_message(
std::fs::write(&todo_path, todo).context("failed to stage the rebase todo")?;
std::fs::write(&msg_path, message).context("failed to stage the commit message")?;

let seq_editor = format!("cp {}", shell_single_quote(&todo_path.to_string_lossy()));
let msg_editor = format!("cp {}", shell_single_quote(&msg_path.to_string_lossy()));
let seq_editor = format!("cp {}", shell_single_quote(&shell_path(&todo_path)));
let msg_editor = format!("cp {}", shell_single_quote(&shell_path(&msg_path)));
let mut cmd = Command::new("git");
cmd.args([
"rebase",
Expand Down Expand Up @@ -700,8 +706,8 @@ pub(crate) fn rebase_squash_stop(
std::fs::write(&todo_path, todo).context("failed to stage the rebase todo")?;
std::fs::write(&msg_path, message).context("failed to stage the commit message")?;

let seq_editor = format!("cp {}", shell_single_quote(&todo_path.to_string_lossy()));
let msg_editor = format!("cp {}", shell_single_quote(&msg_path.to_string_lossy()));
let seq_editor = format!("cp {}", shell_single_quote(&shell_path(&todo_path)));
let msg_editor = format!("cp {}", shell_single_quote(&shell_path(&msg_path)));
let mut cmd = Command::new("git");
cmd.args([
"rebase",
Expand Down Expand Up @@ -799,7 +805,7 @@ fn drive_cherry_pick_to_completion(what: &str) -> Result<()> {
/// trailer and exits). Content is untouched, so no conflicts can arise.
pub(crate) fn rebase_stamp_ids(upstream: &str, branch: &str, shas: &[String]) -> Result<()> {
let exe = std::env::current_exe().context("cannot locate the git-queue executable")?;
let exe = exe.display();
let exe = shell_path(&exe);
let mut initial = Command::new("git");
initial.args(["rebase", "-i", "--update-refs", upstream, branch]);
initial
Expand Down
5 changes: 5 additions & 0 deletions tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ fn new_repo() -> TempDir {
// with a key that isn't loaded.
git(dir, &["config", "commit.gpgsign", "false"]);
git(dir, &["config", "tag.gpgsign", "false"]);
// Pin line endings so working-tree byte assertions hold cross-platform
// (git-for-windows defaults core.autocrlf=true, which would rewrite LF→CRLF
// on checkout).
git(dir, &["config", "core.autocrlf", "false"]);
git(dir, &["config", "core.eol", "lf"]);
commit(dir, "seed.txt");
tmp
}
Expand Down