Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

git checkout --orphan causes hook to panic #1455

Open
getchoo opened this issue Nov 29, 2024 · 1 comment
Open

git checkout --orphan causes hook to panic #1455

getchoo opened this issue Nov 29, 2024 · 1 comment
Labels
bug Something isn't working

Comments

@getchoo
Copy link

getchoo commented Nov 29, 2024

Description of the bug

When running git checkout --orphan <branch-name, the Git hook git-branchless installs panics

Full Log
seth@glados-wsl ~ > git init git-branchless-repro
Initialized empty Git repository in /home/seth/git-branchless-repro/.git/

seth@glados-wsl ~ > cd git-branchless-repro/

seth@glados-wsl ~/git-branchless-repro > git commit --allow-empty -m "initial commit"
[main (root-commit) e9d3ae5] initial commit

seth@glados-wsl ~/git-branchless-repro > git branchless init
Created config file at /home/seth/git-branchless-repro/.git/branchless/config
Auto-detected your main branch as: main
If this is incorrect, run: git branchless init --main-branch <branch>
Installing hooks: post-applypatch, post-checkout, post-commit, post-merge, post-rewrite, pre-auto-gc, reference-transaction
Successfully installed git-branchless.
To uninstall, run: git branchless init --uninstall

seth@glados-wsl ~/git-branchless-repro > git checkout --orphan orphan-branch
branchless: processing 1 update: ref HEAD
The application panicked (crashed).
Message:  A fatal error occurred:
   0: could not find reference 'refs/heads/orphan-branch': reference 'refs/heads/orphan-branch' not found; class=Reference (4); code=NotFound (-3)
   1: reference 'refs/heads/orphan-branch' not found; class=Reference (4); code=NotFound (-3)

Location:
   git-branchless-hook/src/lib.rs:224

  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ SPANTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

   0: git_branchless_hook::reference_transaction::as_oid with self=Symbolic { name: ReferenceName("refs/heads/orphan-branch") } repo=<Git repository at: "/home/seth/git-branchless-repro/.git/">
      at git-branchless-hook/src/lib.rs:220
   1: git_branchless_hook::hook_reference_transaction with effects=<Output fancy=true> transaction_state="committed"
      at git-branchless-hook/src/lib.rs:503
   2: git_branchless_hook::command_main with ctx=CommandContext { effects: <Output fancy=true>, git_run_info: <GitRunInfo path_to_git="git" working_directory="/home/seth/git-branchless-repro" env=not shown> } args=HookArgs { subcommand: ReferenceTransaction { transaction_state: "committed" } }
      at git-branchless-hook/src/lib.rs:613

Backtrace omitted. Run with RUST_BACKTRACE=1 environment variable to display it.
Run with RUST_BACKTRACE=full to include source snippets.
Location: git-branchless/src/commands/mod.rs:235

Backtrace omitted. Run with RUST_BACKTRACE=1 environment variable to display it.
Run with RUST_BACKTRACE=full to include source snippets.
branchless: Failed to process reference transaction!
branchless: Some events (e.g. branch updates) may have been lost.
branchless: This is a bug. Please report it.
Switched to a new branch 'orphan-branch'
branchless: processing checkout

Expected behavior

A clean checkout of my new branch

Actual behavior

The hook panicked

Version of rustc

No response

Automated bug report

Software version

git-branchless 0.10.0

Operating system

Linux 5.15.167.4-microsoft-standard-WSL2

Command-line

/etc/profiles/per-user/seth/bin/git-branchless bug-report

Environment variables

SHELL=/run/current-system/sw/bin/fish
EDITOR=nvim

Git version

> git version
git version 2.47.0

Hooks

Hooks directory: /home/seth/git-branchless-repro/.git/hooks

Show 7 hooks
Hook post-applypatch
#!/bin/sh
## START BRANCHLESS CONFIG

git branchless hook post-applypatch "$@"

## END BRANCHLESS CONFIG
Hook post-checkout
#!/bin/sh
## START BRANCHLESS CONFIG

git branchless hook post-checkout "$@"

## END BRANCHLESS CONFIG
Hook post-commit
#!/bin/sh
## START BRANCHLESS CONFIG

git branchless hook post-commit "$@"

## END BRANCHLESS CONFIG
Hook post-merge
#!/bin/sh
## START BRANCHLESS CONFIG

git branchless hook post-merge "$@"

## END BRANCHLESS CONFIG
Hook post-rewrite
#!/bin/sh
## START BRANCHLESS CONFIG

git branchless hook post-rewrite "$@"

## END BRANCHLESS CONFIG
Hook pre-auto-gc
#!/bin/sh
## START BRANCHLESS CONFIG

git branchless hook pre-auto-gc "$@"

## END BRANCHLESS CONFIG
Hook reference-transaction
#!/bin/sh
## START BRANCHLESS CONFIG

# Avoid canceling the reference transaction in the case that `branchless` fails
# for whatever reason.
git branchless hook reference-transaction "$@" || (
echo 'branchless: Failed to process reference transaction!'
echo 'branchless: Some events (e.g. branch updates) may have been lost.'
echo 'branchless: This is a bug. Please report it.'
)

## END BRANCHLESS CONFIG

Events

Show 5 events
Event ID: 1, transaction ID: 4 (message: hook-post-checkout)
  1. RefUpdateEvent { timestamp: 1732840114.4923446, event_tx_id: Id(4), ref_name: ReferenceName("HEAD"), old_oid: e9d3ae555b94ec97055ba4f29edd419cbbbb395c, new_oid: e9d3ae555b94ec97055ba4f29edd419cbbbb395c, message: None }
O e9d3ae5 2m (main) xxxxxxx xxxxxx

There are no previous available events.

O e9d3ae5 2m (main) xxxxxxx xxxxxx

There are no previous available events.

O e9d3ae5 2m (main) xxxxxxx xxxxxx

There are no previous available events.

O e9d3ae5 2m (main) xxxxxxx xxxxxx

There are no previous available events.

O e9d3ae5 2m (main) xxxxxxx xxxxxx

Version of git-branchless

No response

Version of git

No response

@getchoo getchoo added the bug Something isn't working label Nov 29, 2024
@arxanas
Copy link
Owner

arxanas commented Nov 29, 2024

Thanks for reporting. git-branchless supports orphaning/unborn branches in some places, namely here:

/// A snapshot of information about a certain reference. Updates to the
/// reference after this value is obtained are not reflected.
///
/// `HEAD` is typically a symbolic reference, which means that it's a reference
/// that points to another reference. Usually, the other reference is a branch.
/// In this way, you can check out a branch and move the branch (e.g. by
/// committing) and `HEAD` is also effectively updated (you can traverse the
/// pointed-to reference and get the current commit OID).
///
/// There are a couple of interesting edge cases to worry about:
///
/// - `HEAD` is detached. This means that it's pointing directly to a commit and
/// is not a symbolic reference for the time being. This is uncommon in normal
/// Git usage, but very common in `git-branchless` usage.
/// - `HEAD` is unborn. This means that it doesn't even exist yet. This happens
/// when a repository has been freshly initialized, but no commits have been
/// made, for example.
#[derive(Debug, PartialEq, Eq)]
pub struct ResolvedReferenceInfo {
/// The OID of the commit that `HEAD` points to. If `HEAD` is unborn, then
/// this is `None`.
pub oid: Option<NonZeroOid>,
/// The name of the reference that `HEAD` points to symbolically. If `HEAD`
/// is detached, then this is `None`.
pub reference_name: Option<ReferenceName>,
}

It looks like the spantrace you provided calls

/// Attempt to convert the provided reference target into an OID hash value.
///
/// For [ReferenceTarget::Direct] types, this always succeeds, and just
/// returns the wrapped OID.
///
/// For [ReferenceTarget::Symbolic] types, this attempts to convert the
/// provided symbolic ref name into an OID hash using the provided
/// [Repo].
#[instrument]
pub fn as_oid(&self, repo: &Repo) -> eyre::Result<MaybeZeroOid> {
match self {
ReferenceTarget::Direct { oid } => Ok(*oid),
ReferenceTarget::Symbolic { name } => Ok(repo.reference_name_to_oid(name)?),
}
}

which calls

/// Get the OID for a given [ReferenceName] if it exists.
#[instrument]
pub fn reference_name_to_oid(&self, name: &ReferenceName) -> Result<MaybeZeroOid> {
match self.inner.refname_to_id(name.as_str()) {
Ok(git2_oid) => Ok(MaybeZeroOid::from(git2_oid)),
Err(source) => Err(Error::FindReference {
source,
name: name.clone(),
}),
}
}

In those cases, the reference is "not found" (according to libgit2?), which I guess is technically correct at some level, but it's meaningfully different if HEAD points to a non-existent branch (implying it's unborn) vs some other caller trying to access a non-existent reference directly.

In this case, the best solution might be to have ReferenceTarget::as_oid return MaybeZeroOid::Zero if the reference doesn't seem to exist. I don't plan to work on this, but would accept a PR implementing it.

Unfortunately, that might also be a bit of a hack, in the sense that Event::RefUpdateEvent perhaps can't distinguish between an extant unborn branch vs a deleted branch... The ideal solution would be to delete the reference-transaction hook as per #1334 and start to rely on working copy snapshots as a higher level of abstraction than individual events.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants