mirror of
https://github.com/fiatjaf/nak.git
synced 2025-12-08 16:48:51 +00:00
Compare commits
3 Commits
c3cb59a94a
...
8df130a822
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8df130a822 | ||
|
|
e04861fcee | ||
|
|
73d80203a0 |
@@ -111,7 +111,7 @@ var decrypt = &cli.Command{
|
||||
}
|
||||
plaintext, err := nip04.Decrypt(ciphertext, ss)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to encrypt as nip04: %w", err)
|
||||
return fmt.Errorf("failed to decrypt as nip04: %w", err)
|
||||
}
|
||||
stdout(plaintext)
|
||||
}
|
||||
|
||||
178
git.go
178
git.go
@@ -302,7 +302,7 @@ aside from those, there is also:
|
||||
fetchFromRemotes(ctx, targetDir, repo)
|
||||
|
||||
// if we have a state with a HEAD, try to reset to it
|
||||
if state.Event.ID != nostr.ZeroID && state.HEAD != "" {
|
||||
if state != nil && state.HEAD != "" {
|
||||
if headCommit, ok := state.Branches[state.HEAD]; ok {
|
||||
// check if we have that commit
|
||||
checkCmd := exec.Command("git", "cat-file", "-e", headCommit)
|
||||
@@ -458,6 +458,18 @@ aside from those, there is also:
|
||||
Name: "rebase",
|
||||
Usage: "rebase instead of merge",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "ff-only",
|
||||
Usage: "only allow fast-forward merges",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "ff",
|
||||
Usage: "allow fast-forward merges",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "no-ff",
|
||||
Usage: "always perform a merge instead of fast-forwarding",
|
||||
},
|
||||
},
|
||||
Action: func(ctx context.Context, c *cli.Command) error {
|
||||
// sync to fetch latest state and metadata
|
||||
@@ -488,8 +500,51 @@ aside from those, there is also:
|
||||
return fmt.Errorf("commit %s not found locally, try 'nak git fetch' first", targetCommit)
|
||||
}
|
||||
|
||||
// merge or rebase
|
||||
// determine merge strategy
|
||||
var strategy string
|
||||
strategiesSpecified := 0
|
||||
if c.Bool("rebase") {
|
||||
strategy = "rebase"
|
||||
strategiesSpecified++
|
||||
}
|
||||
if c.Bool("ff-only") {
|
||||
strategy = "ff-only"
|
||||
strategiesSpecified++
|
||||
}
|
||||
if c.Bool("no-ff") {
|
||||
strategy = "no-ff"
|
||||
strategiesSpecified++
|
||||
}
|
||||
if c.Bool("ff") {
|
||||
strategy = "ff"
|
||||
strategiesSpecified++
|
||||
}
|
||||
|
||||
if strategiesSpecified > 1 {
|
||||
return fmt.Errorf("flags --rebase, --ff-only, --ff, --no-ff are mutually exclusive")
|
||||
}
|
||||
|
||||
if strategy == "" {
|
||||
// check git config for pull.rebase
|
||||
cmd := exec.Command("git", "config", "--get", "pull.rebase")
|
||||
output, err := cmd.Output()
|
||||
if err == nil && strings.TrimSpace(string(output)) == "true" {
|
||||
strategy = "rebase"
|
||||
} else if err == nil && strings.TrimSpace(string(output)) == "false" {
|
||||
strategy = "ff"
|
||||
} else {
|
||||
// check git config for pull.ff
|
||||
cmd := exec.Command("git", "config", "--get", "pull.ff")
|
||||
output, err := cmd.Output()
|
||||
if err == nil && strings.TrimSpace(string(output)) == "only" {
|
||||
strategy = "ff-only"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// execute the merge or rebase
|
||||
switch strategy {
|
||||
case "rebase":
|
||||
log("rebasing %s onto %s...\n", color.CyanString(localBranch), color.CyanString(targetCommit))
|
||||
rebaseCmd := exec.Command("git", "rebase", targetCommit)
|
||||
rebaseCmd.Stderr = os.Stderr
|
||||
@@ -497,14 +552,52 @@ aside from those, there is also:
|
||||
if err := rebaseCmd.Run(); err != nil {
|
||||
return fmt.Errorf("rebase failed: %w", err)
|
||||
}
|
||||
} else {
|
||||
log("merging %s into %s...\n", color.CyanString(targetCommit), color.CyanString(localBranch))
|
||||
mergeCmd := exec.Command("git", "merge", targetCommit)
|
||||
case "ff-only":
|
||||
log("pulling %s into %s (fast-forward only)...\n", color.CyanString(targetCommit), color.CyanString(localBranch))
|
||||
mergeCmd := exec.Command("git", "merge", "--ff-only", targetCommit)
|
||||
mergeCmd.Stderr = os.Stderr
|
||||
mergeCmd.Stdout = os.Stdout
|
||||
if err := mergeCmd.Run(); err != nil {
|
||||
return fmt.Errorf("merge failed: %w", err)
|
||||
}
|
||||
case "no-ff":
|
||||
log("pulling %s into %s (no fast-forward)...\n", color.CyanString(targetCommit), color.CyanString(localBranch))
|
||||
mergeCmd := exec.Command("git", "merge", "--no-ff", targetCommit)
|
||||
mergeCmd.Stderr = os.Stderr
|
||||
mergeCmd.Stdout = os.Stdout
|
||||
if err := mergeCmd.Run(); err != nil {
|
||||
return fmt.Errorf("merge failed: %w", err)
|
||||
}
|
||||
case "ff":
|
||||
log("pulling %s into %s...\n", color.CyanString(targetCommit), color.CyanString(localBranch))
|
||||
mergeCmd := exec.Command("git", "merge", "--ff", targetCommit)
|
||||
mergeCmd.Stderr = os.Stderr
|
||||
mergeCmd.Stdout = os.Stdout
|
||||
if err := mergeCmd.Run(); err != nil {
|
||||
return fmt.Errorf("merge failed: %w", err)
|
||||
}
|
||||
default:
|
||||
// get current commit
|
||||
res, err := exec.Command("git", "rev-parse", localBranch).Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get current commit for branch %s: %w", localBranch, err)
|
||||
}
|
||||
currentCommit := strings.TrimSpace(string(res))
|
||||
|
||||
// check if fast-forward possible
|
||||
cmd := exec.Command("git", "merge-base", "--is-ancestor", currentCommit, targetCommit)
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("fast-forward merge not possible, specify --rebase, --ff-only, --ff, or --no-ff; or use git config")
|
||||
}
|
||||
|
||||
// do fast-forward
|
||||
log("fast-forwarding to %s...\n", color.CyanString(targetCommit))
|
||||
mergeCmd := exec.Command("git", "merge", "--ff-only", targetCommit)
|
||||
mergeCmd.Stderr = os.Stderr
|
||||
mergeCmd.Stdout = os.Stdout
|
||||
if err := mergeCmd.Run(); err != nil {
|
||||
return fmt.Errorf("fast-forward failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
log("pull complete\n")
|
||||
@@ -703,7 +796,7 @@ func gitSync(ctx context.Context, signer nostr.Keyer) (nip34.Repository, *nip34.
|
||||
|
||||
// fetch repository announcement and state from relays
|
||||
repo, state, err := fetchRepositoryAndState(ctx, owner, localConfig.Identifier, localConfig.GraspServers)
|
||||
if err != nil {
|
||||
if err != nil && repo.Event.ID == nostr.ZeroID {
|
||||
log("couldn't fetch repository metadata (%s), will publish now\n", err)
|
||||
// create a local repository object from config and publish it
|
||||
localRepo := localConfig.ToRepository()
|
||||
@@ -735,6 +828,15 @@ func gitSync(ctx context.Context, signer nostr.Keyer) (nip34.Repository, *nip34.
|
||||
return repo, nil, fmt.Errorf("no signer provided to publish repository (run 'nak git sync' with the '--sec' flag)")
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
if _, ok := err.(StateErr); ok {
|
||||
// some error with the state, just do nothing and proceed
|
||||
} else {
|
||||
// actually fail with this error we don't know about
|
||||
return repo, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// check if local config differs from remote announcement
|
||||
// construct local repo from config for comparison
|
||||
localRepo := localConfig.ToRepository()
|
||||
@@ -840,39 +942,43 @@ func gitSetupRemotes(ctx context.Context, dir string, repo nip34.Repository) {
|
||||
|
||||
// delete all nip34/grasp/ remotes
|
||||
remotes := strings.Split(strings.TrimSpace(string(output)), "\n")
|
||||
for _, remote := range remotes {
|
||||
for i, remote := range remotes {
|
||||
remote = strings.TrimSpace(remote)
|
||||
remotes[i] = remote
|
||||
|
||||
if strings.HasPrefix(remote, "nip34/grasp/") {
|
||||
delCmd := exec.Command("git", "remote", "remove", remote)
|
||||
if dir != "" {
|
||||
delCmd.Dir = dir
|
||||
}
|
||||
if err := delCmd.Run(); err != nil {
|
||||
logverbose("failed to remove remote %s: %v\n", remote, err)
|
||||
if !slices.Contains(repo.Relays, nostr.NormalizeURL(remote[12:])) {
|
||||
delCmd := exec.Command("git", "remote", "remove", remote)
|
||||
if dir != "" {
|
||||
delCmd.Dir = dir
|
||||
}
|
||||
if err := delCmd.Run(); err != nil {
|
||||
logverbose("failed to remove remote %s: %v\n", remote, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create new remotes for each grasp server
|
||||
for _, relay := range repo.Relays {
|
||||
relayURL := nostr.NormalizeURL(relay)
|
||||
remoteName := "nip34/grasp/" + strings.TrimPrefix(relayURL, "wss://")
|
||||
remoteName = strings.TrimPrefix(remoteName, "ws://")
|
||||
remote := "nip34/grasp/" + strings.TrimPrefix(relay, "wss://")
|
||||
|
||||
// construct the git URL
|
||||
gitURL := fmt.Sprintf("http%s/%s/%s.git",
|
||||
relayURL[2:], nip19.EncodeNpub(repo.PubKey), repo.ID)
|
||||
if !slices.Contains(remotes, remote) {
|
||||
// construct the git URL
|
||||
gitURL := fmt.Sprintf("http%s/%s/%s.git",
|
||||
relay[2:], nip19.EncodeNpub(repo.PubKey), repo.ID)
|
||||
|
||||
addCmd := exec.Command("git", "remote", "add", remoteName, gitURL)
|
||||
if dir != "" {
|
||||
addCmd.Dir = dir
|
||||
}
|
||||
if out, err := addCmd.Output(); err != nil {
|
||||
var stderr string
|
||||
if exiterr, ok := err.(*exec.ExitError); ok {
|
||||
stderr = string(exiterr.Stderr)
|
||||
addCmd := exec.Command("git", "remote", "add", remote, gitURL)
|
||||
if dir != "" {
|
||||
addCmd.Dir = dir
|
||||
}
|
||||
if out, err := addCmd.Output(); err != nil {
|
||||
var stderr string
|
||||
if exiterr, ok := err.(*exec.ExitError); ok {
|
||||
stderr = string(exiterr.Stderr)
|
||||
}
|
||||
logverbose("failed to add remote %s: %s %s\n", remote, stderr, string(out))
|
||||
}
|
||||
logverbose("failed to add remote %s: %s %s\n", remoteName, stderr, string(out))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -955,7 +1061,7 @@ func fetchRepositoryAndState(
|
||||
}
|
||||
|
||||
// fetch repository state (30618)
|
||||
var stateErr error
|
||||
var stateErr *StateErr
|
||||
for ie := range sys.Pool.FetchMany(ctx, repo.Relays, nostr.Filter{
|
||||
Kinds: []nostr.Kind{30618},
|
||||
Authors: []nostr.PubKey{pubkey},
|
||||
@@ -966,18 +1072,18 @@ func fetchRepositoryAndState(
|
||||
}, nostr.SubscriptionOptions{Label: "nak-git"}) {
|
||||
if state == nil || ie.Event.CreatedAt > state.CreatedAt {
|
||||
state_ := nip34.ParseRepositoryState(ie.Event)
|
||||
state = &state_
|
||||
|
||||
if state.HEAD == "" {
|
||||
stateErr = fmt.Errorf("state is missing HEAD")
|
||||
if state_.HEAD == "" {
|
||||
stateErr = &StateErr{"state is missing HEAD"}
|
||||
continue
|
||||
}
|
||||
if _, ok := state.Branches[state.HEAD]; !ok {
|
||||
stateErr = fmt.Errorf("state is missing commit for HEAD branch '%s'", state.HEAD)
|
||||
if _, ok := state_.Branches[state_.HEAD]; !ok {
|
||||
stateErr = &StateErr{fmt.Sprintf("state is missing commit for HEAD branch '%s'", state_.HEAD)}
|
||||
continue
|
||||
}
|
||||
|
||||
stateErr = nil
|
||||
state = &state_
|
||||
}
|
||||
}
|
||||
if stateErr != nil {
|
||||
@@ -987,6 +1093,10 @@ func fetchRepositoryAndState(
|
||||
return repo, state, nil
|
||||
}
|
||||
|
||||
type StateErr struct{ string }
|
||||
|
||||
func (s StateErr) Error() string { return string(s.string) }
|
||||
|
||||
func findGitRoot(startDir string) string {
|
||||
if startDir == "" {
|
||||
var err error
|
||||
|
||||
Reference in New Issue
Block a user