Compare commits

...

91 Commits

Author SHA1 Message Date
Jeff Carr e0520ca96d apply patches 2025-09-23 14:50:59 -05:00
Jeff Carr 6d7d74feb4 fixing against new .proto files 2025-09-23 14:21:58 -05:00
Jeff Carr 9c87e1a040 print the table of the patches 2025-09-23 09:36:05 -05:00
Jeff Carr bda5fb4fbe set PB tables are working 2025-09-23 09:02:00 -05:00
Jeff Carr 1e3f4a3b9f new .proto files 2025-09-23 07:43:30 -05:00
Jeff Carr e300719241 cleanup tag list 2025-09-22 23:02:59 -05:00
Jeff Carr a9c4b21b35 try doc again 2025-09-22 21:07:24 -05:00
Jeff Carr 37aebd9d73 fix merge options 2025-09-22 20:40:30 -05:00
Jeff Carr 5a54f9c0b2 good grief 2025-09-22 20:14:35 -05:00
Jeff Carr 31db2f96f6 more work on autocomplete 2025-09-22 19:21:41 -05:00
Jeff Carr 3a967eac13 cleanup mode handling 2025-09-22 19:03:00 -05:00
Jeff Carr 17a62eb8da show the user name 2025-09-22 09:29:46 -05:00
Jeff Carr ba2f156c3d unused field 2025-09-21 20:41:17 -05:00
Jeff Carr 3c922f1277 wow. really? this works? 2025-09-18 16:53:36 -05:00
Jeff Carr ac16ef7127 wow. this worked. thanks Alex Flint 2025-09-18 16:48:03 -05:00
Jeff Carr 2ce32a0f2f more autocomplete cleanups 2025-09-18 16:03:11 -05:00
Jeff Carr 4d4dcf31cb slowly working out autocomplete kinks 2025-09-18 15:24:20 -05:00
Jeff Carr 19479f312f still thinking this out 2025-09-18 08:54:24 -05:00
Jeff Carr 58ce9ca53c more debugging of line feeds in bash 2025-09-18 07:29:53 -05:00
Jeff Carr 62e8d457f1 better command line handling 2025-09-17 22:36:11 -05:00
Jeff Carr f936a17bc0 compiles 2025-09-17 22:01:48 -05:00
Jeff Carr 9bc6d030e5 something to debug if under 400ms 2025-09-17 20:54:52 -05:00
Jeff Carr b8252f5caa use an auto complete PB 2025-09-17 17:03:43 -05:00
Jeff Carr ce0fd10064 common argv handling 2025-09-17 01:08:30 -05:00
Jeff Carr ae2cbf1886 all are returning AnyCol 2025-09-16 23:13:00 -05:00
Jeff Carr 29f2084e25 --gui bash completion 2025-09-16 17:33:57 -05:00
Jeff Carr 02d34d3e55 misc 2025-09-16 09:32:17 -05:00
Jeff Carr e14f7b93d1 more work on 'any' 2025-09-15 05:10:29 -05:00
Jeff Carr 7fdd7075fd testing showing tag age 2025-09-15 03:51:22 -05:00
Jeff Carr 179c19147e working len(rows) 2025-09-14 11:06:13 -05:00
Jeff Carr 0aafe6bb86 make generic print table PB to STDOUT 2025-09-14 05:50:23 -05:00
Jeff Carr 03b8e58451 quiet output 2025-09-13 08:39:12 -05:00
Jeff Carr 5637809f5c ooops 2025-09-13 08:31:45 -05:00
Jeff Carr 86306aa887 stuff 2025-09-13 07:45:04 -05:00
Jeff Carr 30ee1fcdf7 more lame fixes 2025-09-13 07:19:28 -05:00
Jeff Carr 9cdfface3c moved to new dir scan 2025-09-13 07:09:13 -05:00
Jeff Carr c463ec70f0 new dir scanner 2025-09-13 06:26:44 -05:00
Jeff Carr f4d60d1fb9 work on a new repo Scan() function 2025-09-13 05:32:19 -05:00
Jeff Carr 72d728d4e7 don't do anything if it hasn't changed 2025-09-13 01:52:44 -05:00
Jeff Carr 20fe78266c better "ForgeMode" handling 2025-09-13 01:46:53 -05:00
Jeff Carr da0fc653ae checking if commit() triggers save() 2025-09-13 01:06:55 -05:00
Jeff Carr 405ddb6994 work on using repo.Reload() more smarter 2025-09-13 00:51:22 -05:00
Jeff Carr b5df8f2dad add delete tag 2025-09-12 14:52:05 -05:00
Jeff Carr 7c520aae88 add tag handling 2025-09-12 14:30:26 -05:00
Jeff Carr 96a8f66138 minor 2025-09-12 10:12:00 -05:00
Jeff Carr bd951e4817 stop using GoSrc() 2025-09-11 22:14:30 -05:00
Jeff Carr fa3e6c3cd5 cleaning out old file 2025-09-11 21:39:57 -05:00
Jeff Carr 101e9bd0d2 minor 2025-09-11 20:54:42 -05:00
Jeff Carr 40db2d84ef move into forgepb 2025-09-11 06:57:29 -05:00
Jeff Carr f44aef8926 more stuff 2025-09-11 06:37:46 -05:00
Jeff Carr 0785f0e97f minor 2025-09-11 06:32:50 -05:00
Jeff Carr 363460290d finally detecting state changes again 2025-09-11 06:31:24 -05:00
Jeff Carr 309fcffc86 code to double check remote branches are in sync 2025-09-11 05:39:32 -05:00
Jeff Carr 2471b1ea4c check these every time 2025-09-11 05:18:09 -05:00
Jeff Carr efb966b3f8 common forge.Init() 2025-09-11 04:42:24 -05:00
Jeff Carr b30ecc89fb minor 2025-09-11 02:45:31 -05:00
Jeff Carr 7ab21831a0 notsure. did things break somehow? 2025-09-11 02:31:45 -05:00
Jeff Carr 1b9e9c7518 using new config package 2025-09-11 02:19:47 -05:00
Jeff Carr 602e1fc4ae redo config handling 2025-09-11 01:53:14 -05:00
Jeff Carr 2b3dfe540c lint 2025-09-09 06:25:51 -05:00
Jeff Carr 14bd2c4b0e okay something 2025-09-09 05:07:36 -05:00
Jeff Carr 860290fc26 common gui code 2025-09-09 04:37:50 -05:00
Jeff Carr c0ec2f359a rm old code 2025-09-09 03:54:07 -05:00
Jeff Carr f6bad20818 rm old code 2025-09-09 03:16:20 -05:00
Jeff Carr a991a4c187 argv autocomplete is fun! 2025-09-09 02:04:35 -05:00
Jeff Carr ad2c2ff2ec old code nope 2025-09-08 23:14:15 -05:00
Jeff Carr 297355f27e minor code reorder 2025-09-08 22:45:51 -05:00
Jeff Carr e62d74f0e6 language change 2025-09-08 16:44:40 -05:00
Jeff Carr 6c6af8707a finally back to one file again 2025-09-08 14:57:46 -05:00
Jeff Carr ce813a3450 common code. add "gui" to open the gui 2025-09-08 13:48:06 -05:00
Jeff Carr 11bf5481c7 common patch submit code 2025-09-08 13:19:40 -05:00
Jeff Carr 66802a287f try to figure out what to do with these 2025-09-08 09:25:20 -05:00
Jeff Carr c3afc8c2a1 testing repo submit 2025-09-08 08:13:27 -05:00
Jeff Carr cd3ca5dc82 minor fixes 2025-09-08 05:24:54 -05:00
Jeff Carr 2ddba9924c always unset 'normal' on merge master 2025-09-08 04:46:14 -05:00
Jeff Carr d6a562849c changes for autogenpb http functions 2025-09-08 04:03:13 -05:00
Jeff Carr c55e807262 fixed pull 2025-09-08 00:06:55 -05:00
Jeff Carr 2c7d1de637 redo doPull() 2025-09-08 00:03:54 -05:00
Jeff Carr 99de9e31bc merge turns off 'normal' development mode 2025-09-07 22:58:42 -05:00
Jeff Carr 86eb446408 this can bypass the normal check 2025-09-07 22:55:32 -05:00
Jeff Carr b71471c61e more general work for patches 2025-09-07 22:49:23 -05:00
Jeff Carr ca3a70d623 move to new httppd package 2025-09-07 21:40:54 -05:00
Jeff Carr db758bbed2 work on "forge pull" 2025-09-07 12:05:36 -05:00
Jeff Carr 3df2601f27 don't do patches unless in 'normal' state 2025-09-06 22:50:20 -05:00
Jeff Carr 5c6bbcdbb2 cli user interface 2025-09-06 21:50:43 -05:00
Jeff Carr f6a79c3be8 apply patches working more smoothly 2025-09-06 20:08:28 -05:00
Jeff Carr a6c0edb89d minor fix 2025-09-06 19:26:49 -05:00
Jeff Carr d99eb81385 start checking the "normal" state every time 2025-09-06 19:21:00 -05:00
Jeff Carr 1087b39f9c start reporting applied patches 2025-09-06 17:14:46 -05:00
Jeff Carr 893c88bbf5 patches are starting to work 2025-09-06 16:05:15 -05:00
Jeff Carr e5a2bec217 cleanup after crazy "http://foo.com//blah" issue 2025-09-06 15:21:36 -05:00
28 changed files with 909 additions and 869 deletions

View File

@ -6,8 +6,10 @@ BUILDTIME = $(shell date +%Y.%m.%d_%H%M)
# make gocui # try the ncurses gui plugin
# make andlabs # try the andlabs gui plugin (uses GTK)
default: install-verbose
forge patch list
default: install-verbose tag
tag:
forge tag list
vet:
@GO111MODULE=off go vet
@ -64,21 +66,19 @@ devel:
forge clean devel --force --verbose
pull: install
# forge pull dirty
# FORGE_URL="https://forge.grid.wit.com/" forge pull dirty
# FORGE_URL="https://forge.grid.wit.com/" forge pull patches
FORGE_URL="https://forge.grid.wit.com/" forge pull check
# forge pull patches
# cloudflare blocks POST due to captcha checks / human detection?
# POST must be direct socket. probably for the best anyway
submit:
FORGE_URL="https://forge.grid.wit.com/" forge patch submit "forge auto commit"
# forge patch --submit "forge auto commit"
commit:
FORGE_URL="https://forge.grid.wit.com/" forge commit --all
check: install
FORGE_URL="https://forge.grid.wit.com/" forge patch check
doc:
echo "/*" > doc.go
forge -h >> doc.go

View File

@ -12,39 +12,6 @@ func savePatchset(pset *forgepb.Patchset) error {
log.Info("savePatches() GIT_AUTHOR_EMAIL", pset.GetGitAuthorEmail())
log.Info("savePatches() Branch Name", pset.GetStartBranchName())
log.Info("savePatches() Start Hash", pset.GetStartBranchHash())
var count int
var bad int
var lasterr error
all := pset.Patches.SortByFilename()
for all.Scan() {
p := all.Next()
basedir := filepath.Join(os.Getenv("FORGE_CONFIG"), "currentpatches")
if fullname, err := savePatchFile(p, basedir); err != nil {
log.Info(fullname, "save failed", err)
bad += 1
lasterr = err
}
count += 1
}
log.Info("pset has", count, "total patches, ", bad, "bad save patches")
if bad == 0 {
return lasterr
}
return nil
}
*/
/*
// re-run git CheckDirty() on everything
func IsAnythingDirty() bool {
doCheckDirtyAndConfigSave()
found := me.forge.FindDirty()
if found.Len() == 0 {
return false
} else {
return true
}
}
*/
@ -62,43 +29,6 @@ func countCurrentPatches(repo *gitpb.Repo) int {
return len(result.Stdout)
}
func savePatchFile(p *forgepb.Patch, basedir string) (string, error) {
basepath, filename := filepath.Split(p.Filename)
fulldir := filepath.Join(basedir, basepath)
err := os.MkdirAll(fulldir, os.ModePerm)
if err != nil {
log.Info("applyPathces() MkdirAll failed for", fulldir)
log.Info("applyPathces() MkdirAll failed err", err)
return "", err
}
tmpname := filepath.Join(fulldir, filename)
log.Info("pset filename FILENAME IS REAL?", tmpname)
raw, _ := os.OpenFile(tmpname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
raw.Write(p.Data)
raw.Close()
return tmpname, nil
}
func readPatchFile(pbfile string) (*forgepb.Patchset, error) {
bytes, err := os.ReadFile(pbfile)
if err != nil {
log.Info("readfile error", pbfile, err)
return nil, err
}
return handleBytes(bytes)
}
func handleBytes(bytes []byte) (*forgepb.Patchset, error) {
var pset *forgepb.Patchset
pset = new(forgepb.Patchset)
err := pset.Unmarshal(bytes)
if err != nil {
log.Info("Unmarshal failed", err)
return nil, err
}
return pset, nil
}
func doRegister(newurl string) error {
var url string
url = me.urlbase + "/register?url=" + newurl

95
argv.go
View File

@ -6,6 +6,8 @@ package main
import (
"fmt"
"os"
"go.wit.com/lib/gui/prep"
)
/*
@ -20,14 +22,15 @@ type args struct {
Clean *CleanCmd `arg:"subcommand:clean" help:"start over at the beginning"`
Commit *CommitCmd `arg:"subcommand:commit" help:"'git commit' but errors out if on wrong branch"`
Config *ConfigCmd `arg:"subcommand:config" help:"show your .config/forge/ settings"`
Debug *EmptyCmd `arg:"subcommand:debug" help:"debug forge"`
Dirty *DirtyCmd `arg:"subcommand:dirty" help:"show dirty git repos"`
GitFetch *FindCmd `arg:"subcommand:fetch" help:"run 'git fetch master'"`
Gui *EmptyCmd `arg:"subcommand:gui" help:"open the gui"`
List *FindCmd `arg:"subcommand:list" help:"print a table of the current repos"`
Merge *MergeCmd `arg:"subcommand:merge" help:"merge branches"`
Normal *EmptyCmd `arg:"subcommand:normal" help:"set every repo to the default state for software development"`
Normal *NormalCmd `arg:"subcommand:normal" help:"set every repo to the default state for software development"`
Patch *PatchCmd `arg:"subcommand:patch" help:"make patchsets"`
Pull *PullCmd `arg:"subcommand:pull" help:"run 'git pull'"`
Tag *TagCmd `arg:"subcommand:tag" help:"manage git tags"`
URL string `arg:"--connect" help:"forge url"`
All bool `arg:"--all" help:"git commit --all"`
Build string `arg:"--build" help:"build a repo"`
@ -35,13 +38,16 @@ type args struct {
BuildForge bool `arg:"--forge-rebuild" help:"download and rebuild forge"`
Force bool `arg:"--force" help:"try to strong arm things"`
Verbose bool `arg:"--verbose" help:"show more output"`
Bash bool `arg:"--bash" help:"generate bash completion"`
BashAuto []string `arg:"--auto-complete" help:"todo: move this to go-arg"`
}
type EmptyCmd struct {
}
type NormalCmd struct {
On *EmptyCmd `arg:"subcommand:on" help:"turn normal mode on"`
Off *EmptyCmd `arg:"subcommand:off" help:"turn normal mode off"`
}
type CommitCmd struct {
Submit bool `arg:"--submit" default:"true" help:"submit the patches to forge"`
}
@ -49,13 +55,8 @@ type CommitCmd struct {
type testCmd string
type CleanCmd struct {
Delete *EmptyCmd `arg:"subcommand:delete" help:"rescan repo"`
Devel *CleanDevelCmd `arg:"subcommand:devel" help:"clean and verify the devel branches"`
Force *EmptyCmd `arg:"subcommand:force" help:"do destructive stuff"`
GitReset *EmptyCmd `arg:"subcommand:git-reset" help:"git reset --hard"`
Pub *EmptyCmd `arg:"subcommand:pub" help:"clean target version numbers"`
User *EmptyCmd `arg:"subcommand:user" help:"clean the user branches"`
Repo string `arg:"--repo" help:"which repo to look at"`
Verify *EmptyCmd `arg:"subcommand:verify" help:"rescan repo"`
Repo string `arg:"--repo" help:"which repo to look at"`
}
type CleanDevelCmd struct {
@ -81,6 +82,12 @@ type PullCmd struct {
Patches *EmptyCmd `arg:"subcommand:patches" help:"only check repos with patches"`
}
type TagCmd struct {
List *EmptyCmd `arg:"subcommand:list" help:"list the tags"`
Clean *EmptyCmd `arg:"subcommand:clean" help:"clean out old and duplicate tags"`
Delete string `arg:"--delete" help:"delete a tag"`
}
type ConfigAddCmd struct {
Path string `arg:"--path" help:"absolute path of the git repo"`
GoPath string `arg:"--gopath" help:"GO path of the git repo"`
@ -142,19 +149,57 @@ forge -- a tool to manage lots of git repos. forge includes a GUI and TUI.
`
}
func (args) doBashHelpDebug() {
fmt.Fprintln(os.Stderr, "")
fmt.Fprintln(os.Stderr, "hello world")
var more string
p0 := "0" + argv.BashAuto[0]
p1 := "1" + argv.BashAuto[1]
p2 := "2" + argv.BashAuto[2]
if len(argv.BashAuto[1]) >= 0 {
more = "more"
} else {
more = "less"
// handles shell autocomplete
func DoAutoComplete(pb *prep.Auto) {
switch pb.Cmd {
case "checkout":
pb.Autocomplete2("devel master user")
case "clean":
pb.Autocomplete2("")
case "commit":
pb.Autocomplete2("--all")
case "config":
fmt.Println("add fix list")
case "dirty":
fmt.Println("")
case "gui":
fmt.Println("")
case "--gui":
pb.Autocomplete2("andlabs gocui")
case "list":
pb.Autocomplete2("--mine --favorites --dirty")
case "merge":
pb.Autocomplete2("devel master --all")
case "normal":
pb.Autocomplete2("on off")
case "pull":
pb.Autocomplete2("--force check")
case "patch":
fmt.Println("check get list repos submit show")
case "tag":
fmt.Println("list --delete clean")
default:
if pb.Cmd == "" {
pb.Autocomplete2("help list checkout clean commit dirty fetch gui normal merge patch pull tag --gui")
} else {
pb.Autocomplete2("list checkout clean commit dirty normal merge tag")
}
}
p1a := fmt.Sprintf("1a.%s.%+v.\n", argv.BashAuto[1], len(argv.BashAuto[1]))
fmt.Fprintln(os.Stderr, "pull something else", argv.BashAuto, len(argv.BashAuto), p0, p1, p2, p1a, "end", more)
fmt.Fprintln(os.Stderr, "")
os.Exit(0)
}
func (args) Appname() string {
return ARGNAME
}
func ifBlank(arg string) bool {
if arg == "''" {
// if empty, the user has not typed something
return true
}
return false
}
func (a args) DoAutoComplete(autoArgv *prep.Auto) {
DoAutoComplete(autoArgv)
}

View File

@ -1,115 +0,0 @@
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
// Use of this source code is governed by the GPL 3.0
package main
import (
"fmt"
"os"
)
/*
handles shell autocomplete
*/
// used for shell auto completion
// var ARGNAME string = "forge" // todo: get this from $0 ?
func deleteMatch() {
// f := forgedb.InitSimple()
fmt.Println("go.wit.com/lib/gui/repostatus todo: need to do this")
}
func (args) doBashAuto() {
// argv.doBashHelp()
switch argv.BashAuto[0] {
case "checkout":
fmt.Println("devel master user")
case "clean":
// me.pp.WriteHelp(os.Stderr)
// me.pp.WriteUsageForSubcommand(os.Stderr, me.pp.SubcommandNames()...)
// me.pp.WriteHelpForSubcommand(os.Stderr, me.pp.SubcommandNames()...)
// me.pp.WriteHelpForSubcommand(os.Stderr, "clean")
fmt.Println("--force")
case "commit":
fmt.Println("--all")
case "config":
fmt.Println("add fix list debug")
case "delete":
deleteMatch()
case "dirty":
fmt.Println("")
case "list":
fmt.Println("--full")
case "merge":
fmt.Println("devel master")
case "pull":
fmt.Println("dirty clean list patches --force")
case "patch":
fmt.Println("check get list repos submit show")
case "user":
fmt.Println("--force")
case "devel":
fmt.Println("--force")
case "master":
fmt.Println("")
case "verify":
fmt.Println("user devel master")
default:
if argv.BashAuto[0] == ARGNAME {
// list the subcommands here
fmt.Println("help list checkout clean commit dirty debug fetch normal merge patch pull")
}
}
os.Exit(0)
}
// prints help to STDERR // TODO: move everything below this to go-args
func (args) doBashHelp() {
if argv.BashAuto[1] != "''" {
// if this is not blank, then the user has typed something
return
}
if argv.BashAuto[0] != ARGNAME {
// if this is not the name of the command, the user already started doing something
return
}
if argv.BashAuto[0] == ARGNAME {
me.pp.WriteHelp(os.Stderr)
return
}
fmt.Fprintln(os.Stderr, "")
fmt.Fprintln(os.Stderr, "hello world")
fmt.Fprintln(os.Stderr, "")
}
// complete -F forge --bash forge
func (args) doBash() {
fmt.Println("# add this in your bashrc:")
fmt.Println("")
fmt.Println("# todo: add this to go-arg as a 'hidden' go-arg option --bash")
fmt.Println("#")
fmt.Println("# todo: can this output work/parse with:")
fmt.Println("# complete -C `" + ARGNAME + " --bash` " + ARGNAME)
fmt.Println("")
fmt.Println("_" + ARGNAME + "_complete()")
fmt.Println("{")
fmt.Println(" # sets local to this func vars")
fmt.Println(" local cur prev all")
fmt.Println(" cur=${COMP_WORDS[COMP_CWORD]}")
fmt.Println(" prev=${COMP_WORDS[COMP_CWORD-1]}")
fmt.Println(" all=${COMP_WORDS[@]}")
fmt.Println("")
fmt.Println(" # this is where we generate the go-arg output")
fmt.Println(" GOARGS=$(" + ARGNAME + " --auto-complete $prev \\'$cur\\' $all)")
fmt.Println("")
fmt.Println(" # this compares the command line input from the user")
fmt.Println(" # to whatever strings we output")
fmt.Println(" COMPREPLY=( $(compgen -W \"$GOARGS\" -- $cur) ) # THIS WORKS")
fmt.Println(" return 0")
fmt.Println("}")
fmt.Println("complete -F _" + ARGNAME + "_complete " + ARGNAME)
fmt.Println("")
fmt.Println("# copy and paste the above into your bash shell should work")
os.Exit(0)
}

36
config.go Normal file
View File

@ -0,0 +1,36 @@
package main
// functions to import and export the protobuf
// data to and from config files
import (
"fmt"
"go.wit.com/lib/config"
"go.wit.com/lib/protobuf/forgepb"
"go.wit.com/log"
)
func forgeConfigSave() error {
return me.forge.Config.ConfigSave()
}
func setForgeMode(fmode forgepb.ForgeMode) {
if me.forge.Config.Mode == fmode {
return
}
log.Info("changing mode", me.forge.Config.Mode, fmode)
me.forge.Config.Mode = fmode
config.SetChanged("forge", true)
me.forge.Config.ConfigSave()
}
func sampleConfig(all *forgepb.ForgeConfigs) {
new1 := new(forgepb.ForgeConfig)
new1.GoPath = "go.wit.com"
new1.Writable = true
new1.Directory = true
all.Append(new1)
fmt.Println("first time user. adding an example config file with", len(all.ForgeConfigs), "repos")
}

88
debug.go Normal file
View File

@ -0,0 +1,88 @@
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
// Use of this source code is governed by the GPL 3.0
package main
// An app to submit patches for the 30 GO GUI repos
import (
"fmt"
"time"
"go.wit.com/gui"
"go.wit.com/lib/debugger"
"go.wit.com/lib/gui/shell"
"go.wit.com/lib/protobuf/gitpb"
"go.wit.com/log"
)
func init() {
if debugger.ArgDebug() {
log.Info("cmd line --debugger == true")
go func() {
log.Sleep(2)
debugger.DebugWindow()
}()
}
}
func debug() {
defer func() {
if r := recover(); r != nil {
gui.Crash(r, "forge debug()")
}
}()
time.Sleep(2 * time.Second)
for {
now := time.Now()
if me.repoAllB != nil {
tmp := fmt.Sprintf("All (%d)", me.forge.Repos.Len())
me.repoAllB.SetLabel(tmp)
}
if me.repoDevelMergeB != nil {
found := findMergeToDevel()
tmp := fmt.Sprintf("needs merge to devel (%d)", found.Len())
me.repoDevelMergeB.SetLabel(tmp)
}
if me.repoWritableB != nil {
found := gitpb.NewRepos()
for repo := range me.forge.Repos.IterByFullPath() {
if me.forge.Config.IsReadOnly(repo.GetGoPath()) {
continue
}
found.AppendByFullPath(repo)
}
tmp := fmt.Sprintf("writable (%d)", found.Len())
me.repoWritableB.SetLabel(tmp)
}
dirty := me.forge.CheckDirty()
if me.repoDirtyB != nil {
tmp := fmt.Sprintf("dirty (%d)", dirty.Len())
me.repoDirtyB.SetLabel(tmp)
}
if me.reposWinB != nil {
tmp := fmt.Sprintf("Repos (%d)", me.forge.Repos.Len())
if dirty.Len() > 0 {
tmp = fmt.Sprintf("Repos (%d) (%d dirty)", me.forge.Repos.Len(), dirty.Len())
}
me.reposWinB.SetLabel(tmp)
}
// check for new patches
log.Info("should check for packages here")
// if err := me.forge.ListPatches(); err != nil {
// log.Info("List Patchsets Failed", err)
// }
log.Printf("finished a forge scan here in (%s)\n", shell.FormatDuration(time.Since(now)))
time.Sleep(90 * time.Second)
}
}

View File

@ -8,6 +8,7 @@ import (
"time"
"go.wit.com/lib/gui/shell"
"go.wit.com/lib/protobuf/forgepb"
"go.wit.com/log"
)
@ -26,6 +27,7 @@ func doCheckout() error {
}
if argv.Checkout.Devel != nil {
// setForgeMode(forgepb.ForgeMode_DEVEL)
if err := me.forge.DoAllCheckoutDevelNew(argv.Force); err != nil {
badExit(err)
}
@ -33,6 +35,8 @@ func doCheckout() error {
}
if argv.Checkout.Master != nil {
setForgeMode(forgepb.ForgeMode_MASTER) // disable "normal" mode if set
if err := me.forge.DoAllCheckoutMaster(); err != nil {
badExit(err)
}

View File

@ -6,14 +6,50 @@ package main
import (
"fmt"
"path/filepath"
"time"
"go.wit.com/lib/gui/shell"
"go.wit.com/lib/protobuf/forgepb"
"go.wit.com/lib/protobuf/gitpb"
"go.wit.com/log"
)
func checkRemoteBranches(repo *gitpb.Repo) error {
if err := repo.ReloadCheck(); err != nil {
log.Info("need to reload", repo.FullPath)
}
if repo.VerifyRemoteAndLocalBranches(repo.GetDevelBranchName()) {
} else {
return log.Errorf("remote devel is out of sync with local: todo: git pull or git fetch")
}
if repo.VerifyRemoteAndLocalBranches(repo.GetMasterBranchName()) {
} else {
return log.Errorf("remote master is out of sync with local: todo: git pull or git fetch")
}
return nil
}
// reverts all repos back to the original master branches
// automatically deletes local devel and user branches
func doClean() error {
setForgeMode(forgepb.ForgeMode_CLEAN)
if argv.Clean.Verify != nil {
stats := me.forge.RillRepos(checkRemoteBranches)
for path, stat := range stats {
if stat.Err == nil {
continue
}
dur := stat.End.Sub(stat.Start)
if dur > time.Second {
log.Infof("%s checkRemoteBranches() took a long time (%s) (err=%v)\n", path, shell.FormatDuration(dur), stat.Err)
}
}
// log.Infof("%-60s, %-60s %v %s\n", stat.Start, stat.End.String(), dur, path)
// log.Infof("%-30v %s %v\n", dur, path, stat.Err)
return nil
}
// fix this to work, then delete all the other options for "forge clean'
if err := me.forge.DoAllCheckoutMaster(); err != nil {
// badExit(err)
@ -213,7 +249,7 @@ func justDeleteTheDevelBranchAlready(repo *gitpb.Repo) error {
return err
}
cmd := []string{"git", "merge something somehow"}
log.Info("DEVEL LOCAL NEEDS GIT MERGE TO MASTER", repo.GetGoPath(), cmd, b1)
log.Info("devel local, remote and master branches are wrong", repo.GetGoPath(), cmd, b1)
// _, err := repo.RunVerbose(cmd)
return nil
}

View File

@ -6,12 +6,13 @@ package main
import (
"os"
"go.wit.com/lib/config"
"go.wit.com/lib/gui/shell"
"go.wit.com/lib/protobuf/gitpb"
"go.wit.com/log"
)
func doCommit() {
func doCommit() error {
if argv.All {
found := me.forge.CheckDirty()
var newpatches bool
@ -21,22 +22,16 @@ func doCommit() {
badExit(err)
}
newpatches = true
}
if !argv.Commit.Submit {
okExit("")
repo.CheckDirty()
}
if newpatches {
patchSubmitAndExit()
config.SetChanged("repos", true)
return doPatchSubmit()
}
okExit("")
}
pwd, _ := os.Getwd()
repo := me.forge.Repos.FindByFullPath(pwd)
if repo == nil {
log.Info("todo: forge doesn't know how to work here yet")
okExit("")
}
repo := findCurrentPwdRepoOrDie()
if !repo.CheckDirty() {
okExit(log.Sprintf("this repo %s is not dirty.\n\n--all # commit all changes in all repos", repo.GetFullPath()))
@ -69,23 +64,7 @@ func doCommit() {
badExit(err)
}
patchSubmitAndExit()
}
func patchSubmitAndExit() {
_, err := me.forge.SubmitDevelPatchSet("forge auto commit")
if err != nil {
// TRY different URL
me.forge.SetForgeURL("https://forge.grid.wit.com/")
log.Info("GOING TO TRY AGAIN", me.forge.GetForgeURL())
}
_, err = me.forge.SubmitDevelPatchSet("forge auto commit")
if err != nil {
badExit(err)
}
okExit("git commit ok. forge done")
return doPatchSubmit()
}
func doCommitRepo(repo *gitpb.Repo) error {

View File

@ -55,7 +55,6 @@ func doConfig() {
}
log.Info("config.PathLock =", me.forge.Config.PathLock)
log.Info("config.GoSrc =", me.forge.Config.GoSrc)
me.forge.ConfigPrintTable()
okExit("")

View File

@ -1,168 +0,0 @@
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
// Use of this source code is governed by the GPL 3.0
package main
import (
"bytes"
"fmt"
"io"
"log"
"reflect"
"unicode/utf8"
"go.wit.com/lib/protobuf/bugpb"
"go.wit.com/lib/protobuf/forgepb"
"golang.org/x/text/encoding/charmap"
"google.golang.org/protobuf/proto"
)
func doDebug() {
me.forge = forgepb.InitPB()
me.forge.ScanGoSrc()
if err := me.forge.ConfigSave(); err != nil {
if err := me.forge.Repos.ConfigSave(); err != nil {
err := ValidateProtoUTF8(me.forge.Repos)
if err != nil {
log.Printf("Protobuf UTF-8 validation failed: %v\n", err)
}
if err := bugpb.SanitizeProtoUTF8(me.forge.Repos); err != nil {
log.Fatalf("Sanitization failed: %v", err)
}
}
// badExit(err)
}
me.forge.SetConfigSave(true)
me.forge.Exit()
okExit("this never runs")
}
// ValidateProtoUTF8 checks all string fields in a proto.Message recursively.
func ValidateProtoUTF8(msg proto.Message) error {
return validateValue(reflect.ValueOf(msg), "")
}
func validateValue(val reflect.Value, path string) error {
if !val.IsValid() {
return nil
}
if val.Kind() == reflect.Ptr {
if val.IsNil() {
return nil
}
return validateValue(val.Elem(), path)
}
switch val.Kind() {
case reflect.Struct:
for i := 0; i < val.NumField(); i++ {
field := val.Field(i)
fieldType := val.Type().Field(i)
fieldPath := fmt.Sprintf("%s.%s", path, fieldType.Name)
if err := validateValue(field, fieldPath); err != nil {
return err
}
}
case reflect.String:
s := val.String()
if !utf8.ValidString(s) {
return fmt.Errorf("invalid UTF-8 string at %s: %q", path, s)
}
case reflect.Slice:
if val.Type().Elem().Kind() == reflect.Uint8 {
return nil // skip []byte
}
for i := 0; i < val.Len(); i++ {
if err := validateValue(val.Index(i), fmt.Sprintf("%s[%d]", path, i)); err != nil {
return err
}
}
case reflect.Map:
for _, key := range val.MapKeys() {
valItem := val.MapIndex(key)
if err := validateValue(valItem, fmt.Sprintf("%s[%v]", path, key)); err != nil {
return err
}
}
}
return nil
}
// SanitizeProtoUTF8 fixes all invalid UTF-8 strings in a proto.Message recursively.
func SanitizeProtoUTF8(msg proto.Message) error {
return sanitizeValue(reflect.ValueOf(msg), "")
}
func sanitizeValue(val reflect.Value, path string) error {
if !val.IsValid() {
return nil
}
if val.Kind() == reflect.Ptr {
if val.IsNil() {
return nil
}
return sanitizeValue(val.Elem(), path)
}
switch val.Kind() {
case reflect.Struct:
for i := 0; i < val.NumField(); i++ {
field := val.Field(i)
fieldType := val.Type().Field(i)
if !field.CanSet() {
continue
}
if err := sanitizeValue(field, fmt.Sprintf("%s.%s", path, fieldType.Name)); err != nil {
return err
}
}
case reflect.String:
s := val.String()
if !utf8.ValidString(s) {
utf8Str, err := latin1ToUTF8(s)
if err != nil {
return fmt.Errorf("failed to convert %s to UTF-8: %v", path, err)
}
val.SetString(utf8Str)
}
case reflect.Slice:
if val.Type().Elem().Kind() == reflect.Uint8 {
return nil // skip []byte
}
for i := 0; i < val.Len(); i++ {
if err := sanitizeValue(val.Index(i), fmt.Sprintf("%s[%d]", path, i)); err != nil {
return err
}
}
case reflect.Map:
for _, key := range val.MapKeys() {
valItem := val.MapIndex(key)
newItem := reflect.New(valItem.Type()).Elem()
newItem.Set(valItem)
if err := sanitizeValue(newItem, fmt.Sprintf("%s[%v]", path, key)); err != nil {
return err
}
val.SetMapIndex(key, newItem)
}
}
return nil
}
func latin1ToUTF8(input string) (string, error) {
reader := charmap.ISO8859_1.NewDecoder().Reader(bytes.NewReader([]byte(input)))
result, err := io.ReadAll(reader)
if err != nil {
return "", err
}
return string(result), nil
}

View File

@ -170,6 +170,14 @@ func findReposWithPatches() *gitpb.Repos {
// show anything that differs between 'devel' & 'master' branches
if repo.GetDevelVersion() != repo.GetMasterVersion() {
// this repo.State code isn't great, but it got me here quickly
// I'll defend my code by saying it's faster for me if I do dumb things
// sometimes and fix them later. Probably some employee will have to
// fix this. if that is the case I owe you lunch. or stock options
if repo.State == "DEVEL behind MASTER" {
// log.Info("repo state", repo.FullPath, repo.State)
continue
}
found.AppendByFullPath(repo)
continue
}

124
doGui.go
View File

@ -11,7 +11,6 @@ import (
"time"
"go.wit.com/gui"
"go.wit.com/lib/fhelp"
"go.wit.com/lib/gadgets"
"go.wit.com/lib/gui/shell"
"go.wit.com/lib/protobuf/forgepb"
@ -19,114 +18,20 @@ import (
"go.wit.com/log"
)
func debug() {
defer func() {
if r := recover(); r != nil {
gui.Crash(r, "forge debug()")
}
}()
time.Sleep(2 * time.Second)
for {
now := time.Now()
if me.repoAllB != nil {
tmp := fmt.Sprintf("All (%d)", me.forge.Repos.Len())
me.repoAllB.SetLabel(tmp)
}
if me.repoDevelMergeB != nil {
found := findMergeToDevel()
tmp := fmt.Sprintf("needs merge to devel (%d)", found.Len())
me.repoDevelMergeB.SetLabel(tmp)
}
if me.repoWritableB != nil {
found := gitpb.NewRepos()
for repo := range me.forge.Repos.IterByFullPath() {
if me.forge.Config.IsReadOnly(repo.GetGoPath()) {
continue
}
found.AppendByFullPath(repo)
}
tmp := fmt.Sprintf("writable (%d)", found.Len())
me.repoWritableB.SetLabel(tmp)
}
dirty := me.forge.CheckDirty()
if me.repoDirtyB != nil {
tmp := fmt.Sprintf("dirty (%d)", dirty.Len())
me.repoDirtyB.SetLabel(tmp)
}
if me.reposWinB != nil {
tmp := fmt.Sprintf("Repos (%d)", me.forge.Repos.Len())
if dirty.Len() > 0 {
tmp = fmt.Sprintf("Repos (%d) (%d dirty)", me.forge.Repos.Len(), dirty.Len())
}
me.reposWinB.SetLabel(tmp)
}
// check for new patches
log.Info("should check for packages here")
// if err := me.forge.ListPatches(); err != nil {
// log.Info("List Patchsets Failed", err)
// }
log.Printf("finished a forge scan here in (%s)\n", shell.FormatDuration(time.Since(now)))
time.Sleep(90 * time.Second)
}
}
func doGui() {
if me.forge.Config.GetDefaultGui() == "" {
me.forge.Config.DefaultGui = "gocui"
me.forge.ConfigSave()
}
me.myGui = gui.New()
me.myGui.InitEmbed(resources)
me.myGui.SetAppDefaultPlugin(me.forge.Config.DefaultGui) // sets the default GUI plugin to use
if pname, err := me.myGui.Default(); err != nil {
if !fhelp.BuildPlugin("gocui") {
log.Info("You can't run the forge GUI since the plugins did not build", pname)
okExit("")
} else {
if err := me.myGui.LoadToolkitNew("gocui"); err != nil {
log.Info("The plugins built, but still failed to load", pname)
badExit(err)
}
log.Info("The plugins built and loaded!", pname)
}
}
mainWindow := gadgets.NewGenericWindow("Forge: A federated git development tool by WIT.COM", "Current Settings")
mainWindow.Custom = func() {
win := gadgets.NewGenericWindow("Forge: A federated git development tool by WIT.COM", "Current Settings")
win.Custom = func() {
log.Warn("MAIN WINDOW CLOSE")
now := time.Now()
count := me.forge.RillReload()
log.Info("Repo Reload count =", count)
if count != 0 {
me.forge.ConfigSave()
}
log.Printf("rill repos.Reload() took (%s)\n", shell.FormatDuration(time.Since(now)))
okExit("")
}
drawWindow(mainWindow)
// sits here forever
debug()
}
func drawWindow(win *gadgets.GenericWindow) {
grid := win.Group.RawGrid()
if me.forge.Config.GetPathLock() {
me.goSrcPwd = gadgets.NewOneLiner(grid, "Working Directory")
me.goSrcPwd.SetText(me.forge.GetGoSrc())
me.goSrcPwd.SetText(me.forge.Config.ReposDir)
} else {
me.goSrcEdit = gadgets.NewBasicEntry(grid, "Working Directory")
me.goSrcEdit.SetText(me.forge.GetGoSrc())
me.goSrcEdit.SetText(me.forge.Config.ReposDir)
me.goSrcEdit.Custom = func() {
log.Info("updating text to", me.goSrcEdit.String())
}
@ -175,11 +80,16 @@ func drawWindow(win *gadgets.GenericWindow) {
})
var patchesWin *stdPatchTableWin
gridM.NewButton("Your patches", func() {
var patchButton *gui.Node
patchButton = gridM.NewButton("Your patches", func() {
if patchesWin != nil {
patchesWin.Toggle()
return
}
if !isPatchingSafe() {
patchButton.SetLabel("not safe yet")
return
}
// patchesWin = makePatchesWin(me.forge.Patchsets)
notdone := new(forgepb.Patches)
@ -222,13 +132,13 @@ func makeStandardReposGrid(pb *gitpb.Repos) *gitpb.ReposTable {
t := pb.NewTable("testDirty")
t.NewUuid()
sf := t.AddStringFunc("repo", func(r *gitpb.Repo) string {
return r.GetGoPath()
return r.GetNamespace()
})
// t.Custom = func() {
// log.Info("close grid?")
// }
sf.Custom = func(r *gitpb.Repo) {
log.Info("do button click on", r.GetGoPath())
log.Info("do button click on", r.GetNamespace())
}
t.AddTimeFunc("age", func(repo *gitpb.Repo) time.Time {
return repo.NewestTime()
@ -287,7 +197,7 @@ func findMergeToMaster() *gitpb.Repos {
for all.Scan() {
repo := all.Next()
if me.forge.Config.IsReadOnly(repo.GetGoPath()) {
if me.forge.Config.IsReadOnly(repo.GetNamespace()) {
continue
}
/*
@ -305,7 +215,7 @@ func findMergeToMaster() *gitpb.Repos {
// everything is normal
} else {
repo.State = "DEVEL < MASTER"
log.Info("SERIOUS ERROR. DEVEL BRANCH IS BEHIND MASTER", repo.GetGoPath())
log.Info("SERIOUS ERROR. DEVEL BRANCH IS BEHIND MASTER", repo.GetNamespace())
}
// this sees if devel has patches for master. If it does, add it to me.found
@ -337,7 +247,7 @@ func mergeDevelToMaster(doit bool) {
all := found.SortByFullPath()
for all.Scan() {
repo := all.Next()
log.Info("repo:", repo.GetGoPath())
log.Info("repo:", repo.GetNamespace())
if result, err := repo.MergeToMaster(); err == nil {
log.Warn("THINGS SEEM OK", repo.GetFullPath())
for _, line := range result.Stdout {
@ -388,10 +298,10 @@ func mergeUserToDevel(doit bool) {
b1 := repo.CountDiffObjects(bruser, brdevel) // should be zero
if b1 == 0 {
// log.Info("User is already merged into Devel", repo.GetGoPath(), cmd)
// log.Info("User is already merged into Devel", repo.GetNamespace(), cmd)
return
}
log.Info("merging user into devel repo:", repo.GetGoPath())
log.Info("merging user into devel repo:", repo.GetNamespace())
if result, err := repo.MergeToDevel(); err == nil {
log.Warn("THINGS SEEM OK", repo.GetFullPath())
for _, line := range result.Stdout {

View File

@ -4,26 +4,91 @@
package main
import (
"fmt"
"time"
"go.wit.com/lib/config"
"go.wit.com/lib/gui/shell"
"go.wit.com/lib/protobuf/forgepb"
"go.wit.com/lib/protobuf/gitpb"
"go.wit.com/log"
)
func doMerge() error {
if argv.All == true {
start := time.Now()
repos, err := doMergeDevel()
dur := time.Since(start)
if err != nil {
badExit(err)
}
log.Printf("Merged %d devel branches in %s\n", repos.Len(), shell.FormatDuration(dur))
start = time.Now()
repos, err = doMergeMaster()
dur = time.Since(start)
if err != nil {
badExit(err)
}
log.Printf("Merged %d master branches in %s\n", repos.Len(), shell.FormatDuration(dur))
okExit("")
}
if argv.Merge.Devel != nil {
start := time.Now()
repos, err := doMergeDevel()
dur := time.Since(start)
if err != nil {
badExit(err)
}
log.Printf("Merged %d devel branches in %s\n", repos.Len(), shell.FormatDuration(dur))
okExit("")
}
if argv.Merge.Master != nil {
start := time.Now()
repos, err := doMergeMaster()
dur := time.Since(start)
if err != nil {
badExit(err)
}
log.Printf("Merged %d master branches in %s\n", repos.Len(), shell.FormatDuration(dur))
okExit("")
}
repo := findCurrentPwdRepoOrDie()
if err := repoMergeToDevel(repo); err != nil {
badRepoExit(repo, err)
}
return nil
}
func doMergeReport() *forgepb.Patches {
found := forgepb.NewPatches()
for repo := range me.forge.Repos.IterAll() {
if repo.GetDevelVersion() == repo.GetMasterVersion() {
continue
}
tmp := log.Sprintf("%s..%s", repo.GetMasterVersion(), repo.GetDevelVersion())
r, err := repo.RunStrict([]string{"git", "log", "--pretty=format:%H", tmp})
_ = err
for i, line := range r.Stdout {
log.Info(i, line, repo.FullPath)
}
}
return found
}
func doMergeDevel() (*gitpb.Repos, error) {
var err error
done := gitpb.NewRepos()
found := findMergeToDevel()
for repo := range found.IterAll() {
if repo.CheckDirty() {
log.Info("repo is dirty", repo.GetGoPath())
log.Info("repo is dirty", repo.GetFullPath())
continue
}
log.Infof("%s starting git merge", repo.FullPath)
log.Infof("%s starting git merge\n", repo.FullPath)
if repo.CheckoutDevel() {
log.Info("checkout devel failed", repo.GetGoPath())
err = fmt.Errorf("checkout devel failed")
break
err = log.Errorf("checkout devel failed")
badExit(err)
}
// hash differences when merging user into devel branch
out := repo.GetBranchDifferences(repo.GetDevelBranchName(), repo.GetUserBranchName())
@ -32,21 +97,47 @@ func doMergeDevel() (*gitpb.Repos, error) {
}
if _, err := repo.MergeToDevel(); err != nil {
log.Info("merge from user failed", repo.GetGoPath(), err)
err = fmt.Errorf("merge from user failed")
err = log.Errorf("merge from user failed")
// log.Info(strings.Join(r.Stdout, "\n"))
// log.Info(strings.Join(r.Stderr, "\n"))
break
badExit(err)
}
done.Append(repo)
// only do one at a time. to debug this
break
config.SetChanged("repos", true)
}
configSave = true
return done, err
}
func repoMergeToDevel(repo *gitpb.Repo) error {
if repo.CheckDirty() {
return log.Errorf("can not merge. repo is dirty")
}
log.Infof("%s starting git merge\n", repo.FullPath)
if repo.CheckoutDevel() {
log.Info("checkout devel failed", repo.GetGoPath())
err := log.Errorf("checkout devel failed")
badExit(err)
}
// hash differences when merging user into devel branch
out := repo.GetBranchDifferences(repo.GetDevelBranchName(), repo.GetUserBranchName())
for i, hash := range out {
log.Info("MERGE HASH FROM USER TO DEVEL", i, hash)
}
if _, err := repo.MergeToDevel(); err != nil {
log.Info("merge from user failed", repo.GetGoPath(), err)
// err := log.Errorf("merge from user failed")
// log.Info(strings.Join(r.Stdout, "\n"))
// log.Info(strings.Join(r.Stderr, "\n"))
badExit(err)
}
config.SetChanged("repos", true)
return nil
}
func doMergeMaster() (*gitpb.Repos, error) {
var err error
setForgeMode(forgepb.ForgeMode_MASTER)
done := gitpb.NewRepos()
found := findMergeToMaster()
for repo := range found.IterAll() {
@ -58,19 +149,20 @@ func doMergeMaster() (*gitpb.Repos, error) {
log.Info("Starting merge on", repo.GetGoPath())
if repo.CheckoutMaster() {
log.Info("checkout devel failed", repo.GetGoPath())
err = fmt.Errorf("checkout devel failed")
break
err = log.Errorf("checkout devel failed")
badExit(err)
}
if _, err := repo.MergeToMaster(); err != nil {
log.Info("merge from user failed", repo.GetGoPath(), err)
err = fmt.Errorf("merge from user failed")
err = log.Errorf("merge from user failed")
// log.Info(strings.Join(r.Stdout, "\n"))
// log.Info(strings.Join(r.Stderr, "\n"))
break
badExit(err)
}
done.Append(repo)
config.SetChanged("repos", true)
}
return done, err
}

View File

@ -6,8 +6,11 @@ package main
// checks that repos are in a "normal" state
import (
"path/filepath"
"strings"
"time"
"go.wit.com/lib/config"
"go.wit.com/lib/gui/shell"
"go.wit.com/lib/protobuf/gitpb"
"go.wit.com/log"
@ -33,6 +36,9 @@ func doNormal() bool {
}
if count > 0 {
log.Info("Some repos are not in a 'normal' state. error count =", count)
log.Info("TODO: list the repos here. forge patch repos?")
dumpWorkRepos()
config.SetChanged("repos", true)
return false
}
return true
@ -44,27 +50,63 @@ func doNormal() bool {
// this needs to run each time in case repos were added manually by the user
// this also verifies that
func checkNormalRepoState(repo *gitpb.Repo) error {
var err error
tmp := filepath.Join(me.forge.Config.ReposDir, repo.GetNamespace())
if tmp != repo.FullPath {
log.Infof("%s != %s\n", repo.FullPath, tmp)
if strings.HasPrefix(repo.FullPath, me.forge.Config.ReposDir) {
tmp = strings.TrimPrefix(repo.FullPath, me.forge.Config.ReposDir)
tmp = strings.Trim(tmp, "/")
repo.Namespace = tmp
err = log.Errorf("namespace set to filepath")
}
} else {
// log.Infof("%s == %s\n", repo.FullPath, tmp)
}
tmp = strings.Trim(repo.Namespace, "/")
if tmp != repo.Namespace {
err = log.Errorf("junk in ns %s", repo.Namespace)
repo.Namespace = tmp
}
if repo.GetMasterBranchName() == "" {
me.forge.VerifyBranchNames(repo)
configSave = true
log.Info("ABNORMAL: master branch name was blank in", repo.GetFullPath())
}
if repo.GetMasterBranchName() == "" {
return log.Errorf("master branch name blank")
me.forge.VerifyBranchNames(repo)
err = log.Errorf("master branch name blank")
}
if repo.GetDevelBranchName() == "" {
return log.Errorf("devel branch name blank")
me.forge.VerifyBranchNames(repo)
err = log.Errorf("devel branch name blank")
}
if repo.GetUserBranchName() == "" {
return log.Errorf("user branch name blank")
me.forge.VerifyBranchNames(repo)
err = log.Errorf("user branch name blank")
}
if repo.GetGoPath() == repo.GetNamespace() {
// log.Info(repo.FullPath, "gopath == namespace", repo.GetGoPath(), repo.GetNamespace())
} else {
log.Info(repo.FullPath, "gopath != namespace", repo.GetGoPath(), repo.GetNamespace())
}
repo.MakeLocalDevelBranch()
repo.VerifyRemoteAndLocalBranches(repo.GetDevelBranchName())
repo.VerifyRemoteAndLocalBranches(repo.GetMasterBranchName())
if repo.GetCurrentBranchName() != repo.GetUserBranchName() {
configSave = true
log.Infof("changing to user(%s) branch: %s\n", repo.GetUserBranchName(), repo.FullPath)
repo.CheckoutUser()
repo.Reload()
return log.Errorf("now on user branch")
repo.ReloadCheck()
err = log.Errorf("now on user branch")
}
return nil
if me.forge.Config.IsReadOnly(repo.GetGoPath()) != repo.GetReadOnly() {
repo.ReadOnly = me.forge.Config.IsReadOnly(repo.GetGoPath())
log.Info("damnit", repo.FullPath)
err = log.Errorf("readonly bit wrong")
}
return err
}

View File

@ -6,13 +6,18 @@ package main
import (
"path/filepath"
"go.wit.com/lib/fhelp"
"go.wit.com/lib/protobuf/forgepb"
"go.wit.com/log"
)
func doPatchInit() {
if me.forge.Patchsets != nil {
log.Info("IGNORE: patches already initalized")
if me.forge.Patchsets.Len() == 0 {
// log.Info("IGNORE: patches are empty")
} else {
log.Info("IGNORE: patches already initalized len =", me.forge.Patchsets.Len())
}
}
if err := me.forge.LoadPatchsets(); err != nil {
log.Info("patches failed to open", err)
@ -22,78 +27,169 @@ func doPatchInit() {
}
}
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 {
_, err := me.forge.SubmitDevelPatchSet(argv.Patch.Submit.Match)
if err != nil {
return err
}
if argv.Patch.Repos != nil {
dumpWorkRepos()
return nil
}
if argv.Patch.Get != nil {
if err := me.forge.GetPatches(); err != nil {
log.Info("Get Patchsets Failed", err)
return err
}
if argv.Patch.Submit != nil {
return doPatchSubmit()
}
old := findExpired()
// old.PrintTable()
for p := range old.IterAll() {
log.Info("patch", p.Filename, p.Namespace)
}
me.forge.SubmitPatchesNew(old, "oldpatchset")
log.Infof("submitted %d old patches\n", old.Len())
if !isPatchingSafe() {
return log.Errorf("not safe to work on patches")
}
if argv.Patch.Get != nil {
psets := forgepb.NewSets()
newpb, _, _ := psets.HttpPostVerbose(myServer(), "get")
newpb.PrintTable()
me.forge.Patchsets = newpb
me.forge.SavePatchsets()
return nil
}
if argv.Patch.Check != nil {
log.Info("remove this option")
/*
old := findExpired()
// old.PrintTable()
for p := range old.IterAll() {
log.Info("patch", p.Filename, p.Namespace)
}
newpb, err := old.HttpPostVerbose(myServer(), "check")
if err != nil {
return err
}
newpb.PrintTable()
*/
log.Info("do something here to find patches merged to devel")
doMergeReport()
return nil
}
if argv.Patch.List != nil {
var changed bool
newpatches := new(forgepb.Set)
newpatches.Patches = forgepb.NewPatches()
for pset := range me.forge.Patchsets.IterAll() {
log.Info(pset.Uuid)
pset.PrintTable()
for patch := range pset.Patches.IterAll() {
if setNewCommitHash(patch) {
changed = true
changed = true
if patch.NewHash == "" || patch.NewHash == "na" {
if newpatches.Patches.AppendByPatchId(patch) {
log.Info("patchId added here", patch.PatchId)
} else {
log.Info("patchId already here", patch.PatchId)
}
} else {
if err := setNewCommitHash(patch); err != nil {
log.Infof("%s bad check on patch failure %v\n", patch.Filename, err)
return err
}
log.Info("newhash set already here", patch.PatchId, patch.NewHash)
}
}
/*
for patch := range pset.Patches.IterAll() {
if repo, ok := me.forge.IsPatchApplied(patch); ok {
log.Info("\tfound patch in repo", repo.Namespace, patch.Filename)
} else {
log.Info("\tdid not find patch", patch.CommitHash, patch.NewHash, patch.Filename)
}
}
*/
}
if changed {
if err := me.forge.SavePatchsets(); err != nil {
log.Warn("savePatchsets() failed", err)
}
}
me.forge.Patchsets.PrintTable()
// return doPatchList()
log.Info("NEW PATCHES TABLE")
newpatches.PrintTable()
for patch := range newpatches.Patches.IterAll() {
if err := setNewCommitHash(patch); err == nil {
log.Info("newhash set already here", patch.PatchId, patch.NewHash)
continue
}
log.Infof("%s is new\n", patch.Filename)
repo := me.forge.FindByGoPath(patch.Namespace)
if repo == nil {
log.Info("\tCould not find namespace:", patch.Namespace)
continue
}
if fhelp.QuestionUser("apply this patch?") {
newhash, err := applyAndTrackPatch(repo, patch)
log.Info("apply results:", newhash, err)
}
}
return nil
}
/*
if newpatches.Len() != 0 {
for patch := range newpatches.IterAll() {
log.Info("new patch:", patch.CommitHash, patch.NewHash, patch.Filename)
repo := me.forge.FindByGoPath(patch.Namespace)
if repo == nil {
log.Info("\tCould not find namespace:", patch.Namespace)
continue
}
}
return log.Errorf("patches need to be applied")
}
if argv.Patch.Repos != nil {
dumpDirtyRepos()
return nil
// return doPatchList()
applied := findApplied()
if applied == nil || applied.Len() == 0 {
log.Info("no patches have been appled to the devel branch yet")
return nil
}
// for patch := range applied.IterAll() {
// log.Info("SEND APPLIED: newhash:", patch.NewHash, "commithash:", patch.CommitHash, "patch", patch.Namespace)
// }
newpb, _, err := applied.HttpPostVerbose(myServer(), "applied")
if err != nil {
return err
}
newpb.PrintTable()
return nil
*/
}
// if nothing, show patches & dirty repos
me.forge.Patchsets.PrintTable()
dumpDirtyRepos()
dumpWorkRepos()
return nil
}
func dumpDirtyRepos() {
// 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 dumpWorkRepos() bool {
// always run dirty first
me.forge.CheckDirtyQuiet()
@ -102,13 +198,16 @@ func dumpDirtyRepos() {
found := findReposWithPatches()
if found.Len() == 0 {
log.Info("you currently have no repos with patches")
return false
} else {
me.forge.PrintHumanTable(found)
}
me.forge.PrintHumanTable(found)
return true
}
// returns bad if patches can not be applied
// logic is not great here but it was a first pass
func dumpPatchset(pset *forgepb.Patchset) bool {
func dumpPatchset(pset *forgepb.Set) bool {
// don't even bother to continue if we already know it's broken
if pset.State == "BROKEN" {
log.Printf("Patchset Name: %-24s Author: %s <%s> IS BAD\n", pset.Name, pset.GetGitAuthorName(), pset.GetGitAuthorEmail())
@ -147,12 +246,12 @@ func IsValidPatch(p *forgepb.Patch) bool {
log.Info("can not apply patch! repo not found", basepath, filename)
return false
}
if repo.DevelHash() != p.StartHash {
if repo.ActualDevelHash() != p.StartHash {
log.Info("can not apply patch! devel hash mismatch", basepath, filename)
return false
}
if repo.DevelHash() == p.StartHash {
log.Info("local devel hash:", repo.DevelHash(), "matches patch hash", p.StartHash, "and can be applied")
if repo.ActualDevelHash() == p.StartHash {
log.Info("local devel hash:", repo.ActualDevelHash(), "matches patch hash", p.StartHash, "and can be applied")
}
log.Info("start:", p.StartHash, "end:", p.CommitHash, "file:", basepath, filename, "devel version", repo.GetDevelVersion())
for _, line := range p.Files {

148
doPull.go
View File

@ -4,14 +4,61 @@
package main
import (
"fmt"
"time"
"go.wit.com/lib/gui/shell"
"go.wit.com/lib/protobuf/forgepb"
"go.wit.com/lib/protobuf/gitpb"
"go.wit.com/log"
)
// is every repo on the devel branch?
func doPull() error {
if argv.Pull.Check != nil {
// stats := me.forge.RillFuncError(rillPull)
log.Info("TODO: actually git pull here? this is a bad idea. stopping.")
submit := gitpb.NewRepos()
for repo := range me.forge.Repos.IterByFullPath() {
newrepo := new(gitpb.Repo)
newrepo.MasterHash = repo.MasterHash
newrepo.DevelHash = repo.DevelHash
newrepo.Namespace = repo.Namespace
newrepo.URL = repo.URL
submit.Append(newrepo)
}
updatepb, regPB, err := submit.HttpPost(myServer(), "check")
if regPB == nil || err != nil {
log.Info("regPB==nil or err:", err)
return nil
}
log.Infof("pull check %s pb.Len()=%d client.Len()=%d server.Len()=%d err=%v\n", regPB.URL, updatepb.Len(), regPB.ClientDataLen, regPB.ServerDataLen, err)
return nil
}
// below this, you must not be in 'normal' mode
if me.forge.Config.Mode == forgepb.ForgeMode_NORMAL {
log.Info("you must check out the devel or master branches")
return nil
}
if argv.Force == true {
now := time.Now()
stats := me.forge.RillFuncError(rillPull)
count := me.forge.RillReload()
if count != 0 {
me.forge.ConfigSave()
}
total, count, nope, _ := me.forge.IsEverythingOnMaster()
log.Printf("Master branch check. %d total repos. (%d git pulled) (%d not on master branch) (%s) git pull total=FIXME%d\n", total, count, nope, shell.FormatDuration(time.Since(now)), len(stats))
return nil
}
log.Info("do a pull check here?")
return nil
}
func rillPull(repo *gitpb.Repo) error {
if repo.IsDirty() {
// never do dirty repos
@ -41,103 +88,7 @@ func rillPull(repo *gitpb.Repo) error {
return nil
}
// is every repo on the devel branch?
func doGitPull() error {
if argv.Pull == nil {
return fmt.Errorf("not really 'fetch pull'")
}
if argv.Force {
now := time.Now()
stats := me.forge.RillFuncError(rillPull)
count := me.forge.RillReload()
if count != 0 {
me.forge.ConfigSave()
}
total, count, nope, _ := me.forge.IsEverythingOnMaster()
log.Printf("Master branch check. %d total repos. (%d git pulled) (%d not on master branch) (%s) git pull total=FIXME%d\n", total, count, nope, shell.FormatDuration(time.Since(now)), len(stats))
return nil
}
check := gitpb.NewRepos()
if argv.Pull.Dirty != nil {
check = me.forge.FindDirty()
}
if argv.Pull.Patches != nil {
check = findReposWithPatches()
}
if argv.Pull.Check != nil {
// TODO: never wrote this yet
// update, err := me.forge.CheckVersions()
// return err
return nil
}
if check.Len() == 0 {
// check = doFind()
check = findAll()
// check = find50()
// check = findMine()
}
me.forge.PrintHumanTableFull(check)
if argv.Pull.Dirty != nil {
log.Info("dirty count =", check.Len())
return nil
}
found, err := me.forge.LookupPB(check)
if err != nil {
log.Info("LookupPB() failed", err, "len(check)=", check.Len())
return err
}
// me.forge.PrintHumanTableFull(found)
// check to see if the repos need to be updated
update := gitpb.NewRepos()
if found.Len() == 0 {
return nil
}
log.Info("found.Len() ==", found.Len())
for repo := range found.IterAll() {
masterb := repo.GetMasterBranchName()
if masterb == "" {
log.Info(repo.GetNamespace(), "master branch blank")
continue
}
a := repo.GetLocalHash(masterb)
ns := repo.GetNamespace()
repo2 := me.forge.Repos.FindByNamespace(ns)
if repo2 == nil {
log.Info("repo namespace does not exist", a, repo.Namespace)
continue
}
b := repo2.GetLocalHash(repo2.GetMasterBranchName())
if b == a {
continue
}
log.Info(a, "!=", b, repo.Namespace)
update.AppendByNamespace(repo2)
}
if update.Len() == 0 {
// nothing to update
return nil
}
if _, err := me.forge.UpdatePB(update); err != nil {
log.Info("UpdatePB() failed", err, "len(check)=", update.Len())
return err
}
return nil
}
/*
// git fetch origin master:master
func rillFetchMaster(repo *gitpb.Repo) error {
if repo.GetCurrentBranchName() != repo.GetUserBranchName() {
@ -157,3 +108,4 @@ func doGitFetch() {
me.forge.ConfigSave()
}
}
*/

125
doTag.go Normal file
View File

@ -0,0 +1,125 @@
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
// Use of this source code is governed by the GPL 3.0
package main
// checks that repos are in a "normal" state
import (
"os"
"path/filepath"
"time"
"go.wit.com/lib/fhelp"
"go.wit.com/lib/protobuf/gitpb"
"go.wit.com/log"
)
func FindRepoByFullPath(wd string) *gitpb.Repo {
for repo := range me.forge.Repos.IterAll() {
if repo.FullPath == wd {
return repo
}
}
return nil
}
func findCurrentPwdRepoOrDie() *gitpb.Repo {
wd, err := os.Getwd()
repo := FindRepoByFullPath(wd)
if repo == nil {
log.Info("Could not find repo:", wd)
badExit(err)
}
return repo
}
func doTag() error {
wd, _ := os.Getwd()
if argv.Tag.List != nil {
repo := findCurrentPwdRepoOrDie()
tagTablePB := makeTagTablePB(repo, repo.Tags)
// tbox := win.Bottom.Box().SetProgName("TBOX")
// t.SetParent(tbox)
tagTablePB.MakeTable()
tagTablePB.PrintTable()
log.Info("list tags here", repo.Namespace)
return nil
}
if argv.Tag.Delete != "" {
repo := FindRepoByFullPath(wd)
if repo == nil {
log.Info("Could not find repo:", wd)
return nil
}
// check if the git tag already exists somehow
/*
if !repo.LocalTagExists(testtag) {
log.Info("Tag", testtag, "does not exist")
return log.Errorf("%s TAG DOES NOT EXIST %s", repo.FullPath, testtag)
}
*/
testtag := argv.Tag.Delete
if !argv.Force {
if !fhelp.QuestionUser(log.Sprintf("delete tag '%s'?", testtag)) {
return nil
}
}
log.Info("Delete tag here", testtag)
// delete local and remote tag
repo.RunVerbose([]string{"git", "tag", "--delete", testtag})
repo.RunVerbose([]string{"git", "push", "--delete", "origin", testtag})
return nil
}
log.Info("do other tag stuff here")
return nil
}
func makeTagTablePB(repo *gitpb.Repo, pb *gitpb.GitTags) *gitpb.GitTagsTable {
t := pb.NewTable("tagList")
t.NewUuid()
col := t.AddHash()
col.Width = 12
col = t.AddStringFunc("bashash", func(tag *gitpb.GitTag) string {
_, base := filepath.Split(tag.Refname)
cmd, err := repo.RunStrict([]string{"git", "log", "-1", base, "--format=%H"})
if err != nil {
return "err"
}
if len(cmd.Stdout) == 0 {
return ""
}
return cmd.Stdout[0]
})
col.Width = 12
col = t.AddTimeFunc("ctime", func(tag *gitpb.GitTag) time.Time {
// todo
return tag.Creatordate.AsTime()
})
col.Width = 4
col = t.AddTimeFunc("age", func(repo *gitpb.GitTag) time.Time {
// todo
return time.Now()
})
col.Width = 4
col = t.AddStringFunc("Ref Name", func(r *gitpb.GitTag) string {
_, ref := filepath.Split(r.GetRefname())
return ref
})
col.Width = 16
col = t.AddSubject()
col.Width = -1
return t
}

19
doc.go
View File

@ -3,20 +3,14 @@ forge -- a tool to manage lots of git repos. forge includes a GUI and TUI.
forge only executes the 'git' command. Everything it does, you can run by hand with 'git'.
forge v0.22.138-6-gaea7f16 Built on 2025.09.03_1935
Usage: forge [--debugger] [--logger] [--no-gui] [--gui GUI] [--gui-file GUI-FILE] [--gui-build] [--gui-verbose] [--gui-check-plugin GUI-CHECK-PLUGIN] [--connect CONNECT] [--all] [--build BUILD] [--install INSTALL] [--forge-rebuild] [--force] [--verbose] [--bash] [--auto-complete AUTO-COMPLETE] <command> [<args>]
Options:
--debugger open the debugger window
--logger open the log.* control window
--no-gui ignore all these gui problems
--gui GUI Use this gui toolkit [andlabs,gocui,nocui,stdin]
--gui-file GUI-FILE Use a specific plugin.so file
--gui-build attempt to build the GUI plugins
--gui GUI select the plugin (andlabs,gocui,etc)
--gui-verbose enable all logging
--gui-check-plugin GUI-CHECK-PLUGIN
hack to verify GO plugins load
--bash generate bash completion
--bash generate bash completion
--connect CONNECT forge url
--all git commit --all
--build BUILD build a repo
@ -24,25 +18,24 @@ Options:
--forge-rebuild download and rebuild forge
--force try to strong arm things
--verbose show more output
--bash generate bash completion
--auto-complete AUTO-COMPLETE
todo: move this to go-arg
--help, -h display this help and exit
--version display version and exit
Commands:
help New to forge? This is for you.'
checkout switch branches using 'git checkout'
clean start over at the beginning
commit 'git commit' but errors out if on wrong branch
config show your .config/forge/ settings
debug debug forge
dirty show dirty git repos
fetch run 'git fetch master'
gui open the gui
list print a table of the current repos
merge merge branches
normal set every repo to the default state for software development
patch make patchsets
pull run 'git pull'
tag manage git tags
*/
package main

View File

@ -23,11 +23,11 @@ func okExit(thing string) {
}
func badExit(err error) {
log.Info("forge failed: ", err, me.forge.GetGoSrc())
log.Info("forge failed: ", err, me.forge.Config.ReposDir)
os.Exit(-1)
}
func badRepoExit(repo *gitpb.Repo, err error) {
log.Printf("forge failed on %s with %v\n", repo.GetGoPath(), err)
log.Printf("%s FAILED: %v\n", repo.GetNamespace(), err)
os.Exit(-1)
}

View File

@ -12,15 +12,33 @@ import (
"strings"
"go.wit.com/lib/protobuf/forgepb"
"go.wit.com/lib/protobuf/gitpb"
"go.wit.com/log"
)
func makeReposTablePB(pb *gitpb.Repos) *gitpb.ReposTable {
t := pb.NewTable("quickListRepos")
t.NewUuid()
sf := t.AddStringFunc("Namespace", func(r *gitpb.Repo) string {
return r.GetNamespace()
})
sf.Width = 16
userVer := t.AddStringFunc("user", func(repo *gitpb.Repo) string {
ver := repo.GetUserVersion()
return ver
})
userVer.Width = 4
return t
}
/*
type stdPatchsetTableWin struct {
sync.Mutex
win *gadgets.GenericWindow // the machines gui window
box *gui.Node // the machines gui parent box widget
TB *forgepb.PatchsetsTable // the gui table buffer
TB *forgepb.SetsTable // the gui table buffer
update bool // if the window should be updated
}
@ -36,7 +54,7 @@ func (w *stdPatchsetTableWin) Toggle() {
*/
/*
etimef := func(e *forgepb.Patchset) string {
etimef := func(e *forgepb.Set) string {
etime := e.Etime.AsTime()
s := etime.Format("2006/01/02 15:04")
if strings.HasPrefix(s, "1970/") {
@ -49,14 +67,14 @@ func (w *stdPatchsetTableWin) Toggle() {
*/
/*
ctimef := func(p *forgepb.Patchset) string {
ctimef := func(p *forgepb.Set) string {
ctime := p.Ctime.AsTime()
return ctime.Format("2006/01/02 15:04")
}
}
*/
func setPatchsetState(p *forgepb.Patchset) {
func setPatchsetState(p *forgepb.Set) {
var bad bool
var good bool
var done bool = true
@ -155,53 +173,33 @@ func findCommitBySubject(subject string) (string, error) {
}
// returns true if PB changed
func setNewCommitHash(patch *forgepb.Patch) bool {
// parts := strings.Fields(patch.Comment)
func setNewCommitHash(patch *forgepb.Patch) error {
repo := me.forge.FindByGoPath(patch.Namespace)
if repo == nil {
log.Info("could not find repo", patch.Namespace)
return false
return log.Errorf("could not find repo %s", patch.Namespace)
}
comment := cleanSubject(patch.Comment)
if patch.NewHash == "" {
log.Info("init() new patch to 'na' ", patch.NewHash, "commithash:", patch.CommitHash, patch.Namespace, comment)
patch.NewHash = "na"
return true
}
os.Chdir(repo.GetFullPath())
newhash, err := findCommitBySubject(comment)
if err != nil {
log.Info("patch: not found hash:", patch.CommitHash, patch.Namespace, comment, newhash, err)
return false
return log.Errorf("patch: not found hash: %s %s %s %s %v", patch.CommitHash, patch.Namespace, comment, newhash, err)
}
if patch.NewHash == newhash {
// patch was already set
return false
}
if patch.NewHash != "na" {
log.Infof("patch: hash MISMATCH %s old=%s new=%s name=%s\n", patch.Namespace, patch.NewHash, newhash, comment)
return false
patchId, err := repo.FindPatchId(newhash)
if err != nil {
return err
}
patch.PatchId = patchId
patch.NewHash = newhash
log.Info("patch: found hash:", patch.CommitHash, newhash, patch.Namespace, comment)
return true
return nil
}
/*
func setNewCommitHashLoop(p *forgepb.Patchset) bool {
var done bool = true
for patch := range p.Patches.IterAll() {
setNewCommitHashLoop(patch)
}
return done
}
*/
func AddAllPatches(notdone *forgepb.Patches, pset *forgepb.Patchset, full bool) {
func AddAllPatches(notdone *forgepb.Patches, pset *forgepb.Set, full bool) {
for patch := range pset.Patches.IterAll() {
comment := cleanSubject(patch.Comment)
@ -215,7 +213,7 @@ func AddAllPatches(notdone *forgepb.Patches, pset *forgepb.Patchset, full bool)
}
}
func AddNotDonePatches(notdone *forgepb.Patches, pset *forgepb.Patchset, full bool) {
func AddNotDonePatches(notdone *forgepb.Patches, pset *forgepb.Set, full bool) {
for patch := range pset.Patches.IterAll() {
comment := cleanSubject(patch.Comment)
@ -233,7 +231,7 @@ func AddNotDonePatches(notdone *forgepb.Patches, pset *forgepb.Patchset, full bo
continue
}
if patch.NewHash != "na" {
if patch.NewHash != "" {
log.Info("already applied patch", patch.Namespace, ": newhash:", patch.NewHash, "commithash:", patch.CommitHash, comment)
continue
}
@ -283,7 +281,7 @@ func findExpired() *forgepb.Patches {
continue
}
if patch.NewHash != "na" {
if patch.NewHash != "" {
log.Info("already applied patch", patch.Namespace, ": newhash:", patch.NewHash, "commithash:", patch.CommitHash, comment)
found.AppendByCommitHash(patch) // double check to ensure the commit hash isn't added twice
continue
@ -309,3 +307,34 @@ func findExpired() *forgepb.Patches {
return found
}
func findApplied() *forgepb.Patches {
var pset *forgepb.Patches
for found := range me.forge.Patchsets.IterAll() {
if found.Name == "forge auto commit" {
pset = found.Patches
}
}
if pset == nil {
log.Info("failed to find 'forge auto commit'")
return pset
}
found := forgepb.NewPatches()
for patch := range pset.IterAll() {
cmd := []string{"git", "merge-base", "--is-ancestor", patch.NewHash, "devel"}
repo := me.forge.Repos.FindByNamespace(patch.Namespace)
_, err := repo.RunStrict(cmd)
if err != nil {
// log.Info("NOT APPLIED", patch.Namespace, result, err)
// log.Info("NOT APPLIED newhash:", patch.NewHash, "commithash:", patch.CommitHash, "patch", patch.Namespace)
} else {
// log.Info("APPLIED newhash:", patch.NewHash, "commithash:", patch.CommitHash, "patch", patch.Namespace)
found.Append(patch)
}
}
return found
}

161
main.go
View File

@ -7,14 +7,9 @@ package main
import (
"embed"
"fmt"
"os"
"strings"
"time"
"go.wit.com/dev/alexflint/arg"
"go.wit.com/gui"
"go.wit.com/lib/gui/shell"
"go.wit.com/lib/gui/prep"
"go.wit.com/lib/protobuf/forgepb"
"go.wit.com/lib/protobuf/gitpb"
"go.wit.com/log"
@ -46,38 +41,15 @@ func getVersion(repo *gitpb.Repo, name string) string {
func main() {
me = new(mainType)
gui.InitArg()
me.pp = arg.MustParse(&argv)
me.myGui = prep.Gui() // prepares the GUI package for go-args
me.auto = prep.Bash3(&argv) // this line should be: prep.Bash(&argv)
if argv.Bash {
argv.doBash()
os.Exit(0)
}
if len(argv.BashAuto) != 0 {
argv.doBashAuto()
os.Exit(0)
}
me.urlbase = argv.URL
if me.urlbase == "" {
me.urlbase = "https://go.wit.com/"
}
if os.Getenv("FORGE_URL") != "" {
me.urlbase = os.Getenv("FORGE_URL")
log.Info("got forge url", me.urlbase)
}
me.urlbase = strings.Trim(me.urlbase, "/") // track down why trailing '/' makes http POST not work
// me.auto = prep.Bash3(argv.DoAutoComplete, &argv) // this line should be: prep.Bash(&argv)
// arg.MustParse(&argv) // these three lines are becoming terrible syntax
// me.auto = prep.MustParse(&argv) // try to make this work?
// internally debugging can be triggered here before Init()
if argv.Debug != nil {
doDebug()
okExit("")
}
if forgepb.FirstTimeUser() {
log.Info("You are running forge for the first time here")
}
// load the ~/.config/forge/ config
me.forge = forgepb.Init()
me.forge = forgepb.Init() // init forge.pb
me.forge.ScanRepoDir() // looks for new dirs, checks existing repos for changes
// initialize patches
doPatchInit()
@ -120,16 +92,6 @@ func main() {
}
if argv.Clean != nil {
if argv.Clean.Repo != "" {
log.Info("only looking at repo:", argv.Clean.Repo)
okExit("")
}
if argv.Clean.GitReset != nil {
doGitReset()
okExit("reset")
}
if err := doClean(); err != nil {
badExit(err)
}
@ -137,11 +99,8 @@ func main() {
okExit("")
}
if argv.Normal != nil {
if doNormal() {
log.Infof("all %d repos are on your user branch. It is safe to write code now.\n", me.forge.Repos.Len())
okExit("")
}
if argv.Help != nil {
doHelp()
okExit("")
}
@ -150,38 +109,60 @@ func main() {
okExit("")
}
if argv.GitFetch != nil {
doGitFetch()
if argv.Tag != nil {
doTag()
okExit("")
}
if argv.Merge != nil {
if argv.Merge.Devel != nil {
start := time.Now()
repos, err := doMergeDevel()
dur := time.Since(start)
if err != nil {
badExit(err)
if argv.Normal != nil {
if argv.Normal.On != nil {
if me.forge.Config.Mode == forgepb.ForgeMode_NORMAL {
log.Info("you are already in the normal state")
okExit("")
}
log.Printf("Merged %d devel branches in %s\n", repos.Len(), shell.FormatDuration(dur))
setForgeMode(forgepb.ForgeMode_NORMAL)
log.Info("normal mode on")
okExit("")
}
if argv.Merge.Master != nil {
start := time.Now()
repos, err := doMergeMaster()
dur := time.Since(start)
if err != nil {
badExit(err)
if argv.Normal.Off != nil {
if me.forge.Config.Mode != forgepb.ForgeMode_NORMAL {
log.Info("you were aleady not in the normal state")
okExit("")
}
log.Printf("Merged %d master branches in %s\n", repos.Len(), shell.FormatDuration(dur))
setForgeMode(forgepb.ForgeMode_DEVEL)
log.Info("normal mode off")
okExit("")
}
badExit(fmt.Errorf("You must choose which branch to merge to (devel or master)"))
if doNormal() {
log.Infof("all %d repos are on your user branch. It is safe to write code now.\n", me.forge.Repos.Len())
if me.forge.Config.Mode != forgepb.ForgeMode_NORMAL {
log.Infof("Forge has set the mode to 'Normal'\n")
setForgeMode(forgepb.ForgeMode_NORMAL)
}
okExit("")
}
okExit("")
}
// if you are in "normal" mode, always run normal every time to catch accidental errors
// for example, if you accidentally changed branches from your user branch
if me.forge.Config.Mode == forgepb.ForgeMode_NORMAL {
if doNormal() {
log.Infof("all your %d repos are in a normal stete for development\n", me.forge.Repos.Len())
}
}
if argv.Merge != nil {
if err := doMerge(); err != nil {
badExit(err)
}
okExit("")
}
if argv.Pull != nil {
doGitPull()
doPull()
okExit("")
}
@ -203,32 +184,26 @@ func main() {
okExit("")
}
if argv.Help != nil {
doHelp()
okExit("")
}
// todo: redo this logic using forgepb
if configSave {
me.forge.ConfigSave()
configSave = false
}
// if the user doesn't want to open the GUI and
// nothing else was specified to be done,
// then just list the table to stdout
if gui.NoGui() {
found := doFind()
me.forge.PrintHumanTable(found)
okExit("")
}
// open the gui unless the user performed some other
// basically, if you run just 'forge' it should open the GUI
// if opening the GUI, always check git for dirty repos
me.forge.CheckDirty()
doGui()
if argv.Gui != nil {
// if opening the GUI, always check git for dirty repos
me.forge.CheckDirty()
me.myGui.Start() // loads the GUI toolkit
doGui() // start making our forge GUI
debug() // sits here forever
}
// got to the end with nothing to do (?)
if dumpWorkRepos() {
// found some repos at least
} else {
// every repo is in a really clean state. no extra files anywhere
// no dirty repos, no repos that need to be published
// nothing different between user and master branch version. not common
log.Info("All of your git repositories appear to be in perfect shape")
}
okExit("")
}

View File

@ -4,9 +4,9 @@
package main
import (
"go.wit.com/dev/alexflint/arg"
"go.wit.com/gui"
"go.wit.com/lib/gadgets"
"go.wit.com/lib/gui/prep"
"go.wit.com/lib/protobuf/forgepb"
)
@ -20,11 +20,17 @@ func (b *mainType) Enable() {
b.mainbox.Enable()
}
// returns the server to connect to
func myServer() string {
return me.forge.GetForgeURL()
}
// this app's variables
type mainType struct {
pp *arg.Parser // for parsing the command line args. Yay to alexf lint!
// pp *arg.Parser // for parsing the command line args. Yay to alexflint!
auto *prep.Auto // more experiments for bash handling
forge *forgepb.Forge // for holding the forge protobuf files
myGui *gui.Node // the gui toolkit handle
myGui *prep.GuiPrep // for initializing the GUI toolkits
foundPaths []string // stores gopaths to act on (when doing go-clone)
configSave bool // if the config file should be saved after finishing
urlbase string // base URL

0
test Normal file
View File

View File

@ -41,10 +41,10 @@ func makeHowtoWin() *gadgets.GenericWindow {
grid.NewLabel("") // a stupid way to add padding
grid.NextRow()
// howtoWin.Group.NewLabel("Working dir: " + me.forge.GetGoSrc())
// howtoWin.Group.NewLabel("Working dir: " + me.forge.Config.ReposDir)
grid = howtoWin.Group.RawGrid()
grid.NewButton("Download into "+me.forge.GetGoSrc(), func() {
grid.NewButton("Download into "+me.forge.Config.ReposDir, func() {
howtoWin.Disable()
defer howtoWin.Enable()
downloadForge()

View File

@ -58,34 +58,12 @@ func makePatchesWin(patches *forgepb.Patches) *stdPatchTableWin {
grid.NewLabel(fmt.Sprintf("total repos"))
grid.NextRow()
grid.NewButton("Update", func() {
log.Info("TODO: doesn't update this window")
me.forge.GetPatches()
dwin.win.Custom()
// loadUpstreamPatchsets()
})
grid.NewButton("Apply All", func() {
var count int
all := patches.SortByFilename()
for all.Scan() {
p := all.Next()
applyPatchNew(p)
/*
rn := p.Namespace
repo := me.forge.FindByGoPath(rn)
if repo == nil {
log.Info("Could not figure out repo path", rn)
return
}
count += 1
if _, err := applyAndTrackPatch(repo, p); err != nil {
cmd := []string{"git", "am", "--abort"}
err := repo.RunVerbose(cmd)
log.Info("warn user of git am error", err)
return
}
*/
}
log.Info("ALL PATCHES WORKED! count =", count)
})
@ -139,11 +117,8 @@ func applyPatchLabel(p *forgepb.Patch) string {
// log.Info("Could not figure out repo path", rn)
return ""
}
if p.NewHash == "na" {
return "git am"
}
if p.NewHash == "" {
return "new"
return "git am"
}
return "done"
}

View File

@ -99,7 +99,7 @@ func makeReposWin() *stdReposTableWin {
cmd = []string{"git", "branch", "--delete", "--remote", "origin/" + brname}
log.Info(repo.GetGoPath(), cmd)
repo.RunVerbose(cmd)
repo.Reload()
repo.ReloadCheck()
}
me.forge.SetConfigSave(true)
me.forge.ConfigSave()

View File

@ -21,8 +21,8 @@ type repoPatchWindow struct {
shelf *gui.Node // the first box in the stack, set as horizontal
grid *gui.Node // the list of available patches
// summary *patchSummary // summary of current patches
setgrid *gui.Node // the list of each patchset
pset *forgepb.Patchset // the patchset in question
setgrid *gui.Node // the list of each patchset
pset *forgepb.Set // the patchset in question
}
// todo: autogenerate these or make them standared 'gui' package functions
@ -109,7 +109,7 @@ func makeRepoPatchWindow(repo *gitpb.Repo, fset []*forgepb.Patch) *repoPatchWind
return pw
}
func (r *repoPatchWindow) addPatchset(grid *gui.Node, pset *forgepb.Patchset) {
func (r *repoPatchWindow) addPatchset(grid *gui.Node, pset *forgepb.Set) {
repomap := make(map[*gitpb.Repo][]*forgepb.Patch)
repohash := make(map[*gitpb.Repo]string)