Open krlmlr opened 3 weeks ago
Is this something that can be done with libgit2? https://libgit2.org/libgit2/#HEAD/search
Pushing a conflicting commit fails, but not with the technology GitHub uses (perhaps some hook on the remote repo?).
# Demo for failing push with conflicts
system("git init --bare remote-repo")
system("git clone remote-repo local-repo")
system("git -C local-repo commit --allow-empty -m 'Initial commit'")
system("git -C local-repo push")
system("git clone remote-repo local-repo2")
writeLines("This is a test file", "local-repo/test.txt")
system("git -C local-repo add test.txt")
system("git -C local-repo commit -m 'Add test file'")
system("git -C local-repo push")
writeLines("This is a conflicting test file", "local-repo2/test.txt")
system("git -C local-repo2 add test.txt")
system("git -C local-repo2 commit -m 'Add conflicting test file'")
# Fails
system("git -C local-repo2 push", intern = TRUE)
#> Warning in system("git -C local-repo2 push", intern = TRUE): running command
#> 'git -C local-repo2 push' had status 1
#> character(0)
#> attr(,"status")
#> [1] 1
# Fails too
withr::with_dir("local-repo2", gert::git_push())
#> Error in libgit2::git_remote_push: cannot push because a reference that you are trying to update on the remote contains commits that are not present locally.
Created on 2024-08-18 with reprex v2.1.0
I don't know too much about libgit2 to be helpful here, but could the above example be extended by such a hook in the remote repo?
Perhaps "Server-side hooks" in https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks ?
It's interesting that I can't even create an example with gert and hooks, these seem to be circumvented:
# Demo for failing push with conflicts
unlink("remote-repo", recursive = TRUE, force = TRUE)
unlink("local-repo", recursive = TRUE, force = TRUE)
run <- function(cmd, args) {
invisible(processx::run(cmd, args, echo = TRUE))
}
run("git", c("init", "--bare", "remote-repo"))
#> Initialized empty Git repository in /private/var/folders/dj/yhk9rkx97wn_ykqtnmk18xvc0000gn/T/RtmpnNkFOo/reprex-c4aa7a5fbe6e-stout-scaup/remote-repo/
run("git", c("clone", "remote-repo", "local-repo"))
#> Cloning into 'local-repo'...
#> warning: You appear to have cloned an empty repository.
#> done.
run("git", c("-C", "local-repo", "commit", "--allow-empty", "-m", "Initial commit"))
#> [main (root-commit) 4cfc8d2] Initial commit
run("git", c("-C", "local-repo", "push", "-u", "origin", "HEAD"))
#> To /private/var/folders/dj/yhk9rkx97wn_ykqtnmk18xvc0000gn/T/RtmpnNkFOo/reprex-c4aa7a5fbe6e-stout-scaup/remote-repo
#> * [new branch] HEAD -> main
#> branch 'main' set up to track 'origin/main'.
writeLines(c("#!/bin/sh", "exit 1"), "remote-repo/hooks/update")
Sys.chmod("remote-repo/hooks/update", mode = as.octmode("755"))
writeLines("This is a test file", "local-repo/test.txt")
run("git", c("-C", "local-repo", "add", "test.txt"))
run("git", c("-C", "local-repo", "commit", "-m", "Add test file"))
#> [main d37ccf9] Add test file
#> 1 file changed, 1 insertion(+)
#> create mode 100644 test.txt
# Fails as expected
run("git", c("-C", "local-repo", "push"))
#> remote: error: hook declined to update refs/heads/main
#> To /private/var/folders/dj/yhk9rkx97wn_ykqtnmk18xvc0000gn/T/RtmpnNkFOo/reprex-c4aa7a5fbe6e-stout-scaup/remote-repo
#> ! [remote rejected] main -> main (hook declined)
#> error: failed to push some refs to '/private/var/folders/dj/yhk9rkx97wn_ykqtnmk18xvc0000gn/T/RtmpnNkFOo/reprex-c4aa7a5fbe6e-stout-scaup/remote-repo'
#> Error in "processx::run(cmd, args, echo = TRUE)": ! System command 'git' failed
# Succeeds
withr::with_dir("local-repo", gert::git_push())
# WAT?
run("git", c("-C", "local-repo", "push"))
#> Everything up-to-date
Created on 2024-08-19 with reprex v2.1.0
Add a ruleset indicating that a status check must pass, or that PRs are required, for the main branch. Then:
Created on 2024-08-18 with reprex v2.1.0
In interactive mode (can't reprex), I'm seeing:
I can work around by checking if
<remote>/<branch>
is the same as the local branch, but it would be better IMO ifgit_push()
did that for me.