// Copyright 2017-2025 WIT.COM Inc. All rights reserved. // Use of this source code is governed by the GPL 3.0 package main import ( "errors" "os" "path/filepath" "go.wit.com/lib/fhelp" "go.wit.com/lib/protobuf/forgepb" "go.wit.com/lib/protobuf/gitpb" "go.wit.com/log" ) func isPatchingSafe() bool { if me.forge.Config.Mode == forgepb.ForgeMode_NORMAL { return true } log.Info("This patch command is not safe to run now") log.Info("you must reset the state of your git repositories. Run:") log.Info("") log.Info("forge normal") log.Info("") return false } // submit's current working patches func doPatchSubmit() error { pset, err := me.forge.MakeDevelPatchSet("testing") if err != nil { return err } if pset.Patches == nil { log.Info("pset.Patches == nil") return err } if pset.Patches.Len() == 0 { log.Info("did not find any patches") return nil } pset.PrintTable() _, _, err = pset.HttpPost(myServer(), "new") return err } func doPatch() error { if argv.Patch.Submit != nil { return doPatchSubmit() } if !isPatchingSafe() { return log.Errorf("not safe to work on patches") } if argv.Patch.Get != nil { psets := forgepb.NewSets() newpb, _, _ := psets.HttpPostVerbose(myServer(), "get") doPatchGet(newpb) return nil } if argv.Patch.Check != nil { log.Info("do something here to find patches merged to devel") // doMergeReport() return nil } if argv.Patch.List != nil { err := doPatchList() return err } err := doPatchList() return err } func doPatchList() error { curpatches := forgepb.NewPatches() curpatches.Filename = "/tmp/curpatches.pb" if err := curpatches.Load(); err != nil { return err } curpatches.PrintTable() for patch := range curpatches.IterAll() { repo := me.forge.Repos.FindByNamespace(patch.Namespace) if repo == nil { log.Info("no namespace", patch.PatchId, patch.Namespace, patch.Comment) continue } newId, newHash, err := isPatchIdApplied(repo, patch) if errors.Is(err, ErrorGitPullOnDirty) { log.Info("a patch with that comment couldn't be found in the repo") } else if err != nil { log.Info("err", patch.PatchId, patch.Namespace, patch.Comment, err) return patch.Error(err) } if (newId == patch.PatchId) && (newHash == patch.CommitHash) { log.Info(patch.PatchId, "patch made here", patch.Comment) continue } if newId == patch.PatchId { log.Info(patch.PatchId, "patch already applied", patch.Comment) continue } if newId != patch.PatchId { log.Info(patch.PatchId, "probably duplicate subject", patch.Comment) } log.Info("new patch", patch.PatchId, patch.Comment) if !argv.Fix { log.Info("use --fix to attempt to apply new patches") } else { if fhelp.QuestionUser("apply this patch?") { newhash, err := applyPatch(repo, patch) log.Info("apply results:", newhash, err) if err != nil { return err } } } } return nil } func applyPatch(repo *gitpb.Repo, p *forgepb.Patch) (string, error) { _, filen := filepath.Split(p.Filename) tmpname := filepath.Join("/tmp", filen) log.Info("saving as", tmpname, p.Filename) raw, err := os.OpenFile(tmpname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { return "", err } raw.Write(p.Data) raw.Close() cmd := []string{"git", "am", tmpname} err = repo.RunVerbose(cmd) if err != nil { log.Info("git am failed. run 'git am --abort' here") return "", log.Errorf("git am failed") } log.Info("Try to find hash value now") return p.NewHash, log.Errorf("did not lookup new hash") } func doPatchGet(newpb *forgepb.Sets) { // var changed bool curpatches := forgepb.NewPatches() curpatches.Filename = "/tmp/curpatches.pb" if err := curpatches.Load(); err != nil { curpatches.Save() curpatches.Save() log.Info(err) panic("no file") // return // // THIS IS NEEDED? NOTSURE curpatches = forgepb.NewPatches() curpatches.Filename = "/tmp/curpatches.pb" curpatches.Save() } // newpb.PrintTable() for pset := range newpb.IterAll() { if pset.Patches.Len() == 0 { log.Info("pset is empty", pset.Name) continue } for patch := range pset.Patches.IterAll() { if len(patch.Data) == 0 { continue } patchid, hash, err := gitpb.FindPatchIdFromGitAm(patch.Data) if err != nil { log.Info("git patchid exec err", err) continue } if hash != patch.CommitHash { log.Info("ERROR: patch commit hashes's didn't match", hash, patch.CommitHash) continue } if patchid != patch.PatchId { log.Info("ERROR: patchid's didn't match", patchid, patch.PatchId) continue } found := curpatches.FindByPatchId(patch.PatchId) if found != nil { // already have this patch continue } // gitpb.FindPatchIdFromGitAmBroken(patch.Data) // doesn't os.Exec() log.Info("adding new patch", patch.CommitHash, patch.PatchId, patch.Filename) curpatches.AppendByPatchId(patch) } } curpatches.Save() curpatches.PrintTable() // me.forge.Patchsets = newpb // me.forge.Patchsets.Save() } var ErrorGitPullOnDirty error = errors.New("git comment is not there") func isPatchIdApplied(repo *gitpb.Repo, patch *forgepb.Patch) (string, string, error) { comment := cleanSubject(patch.Comment) os.Chdir(repo.GetFullPath()) newhash, err := findCommitBySubject(comment) if err != nil { return "", "", ErrorGitPullOnDirty } patchId, err := repo.FindPatchIdByHash(newhash) if err != nil { return "", "", err } // log.Infof("%s %s found hash by comment %s \n", patchId, newhash, patch.Comment) return patchId, newhash, nil } // Shows repos that are: // - git dirty repos // - repos with 'user' branch patches not in 'devel' branch // - repos with awaiting master branch verions // // return true if any are found func showWorkRepos() bool { // always run dirty first me.forge.CheckDirtyQuiet() // if no option is given to patch, list out the // repos that have patches ready in them found := findReposWithPatches() if found.Len() == 0 { log.Info("you currently have no repos with patches") return false } else { me.forge.PrintDefaultTB(found) } return true }