Compare commits
362 Commits
Author | SHA1 | Date |
---|---|---|
|
e0520ca96d | |
|
6d7d74feb4 | |
|
9c87e1a040 | |
|
bda5fb4fbe | |
|
1e3f4a3b9f | |
|
e300719241 | |
|
a9c4b21b35 | |
|
37aebd9d73 | |
|
5a54f9c0b2 | |
|
31db2f96f6 | |
|
3a967eac13 | |
|
17a62eb8da | |
|
ba2f156c3d | |
|
3c922f1277 | |
|
ac16ef7127 | |
|
2ce32a0f2f | |
|
4d4dcf31cb | |
|
19479f312f | |
|
58ce9ca53c | |
|
62e8d457f1 | |
|
f936a17bc0 | |
|
9bc6d030e5 | |
|
b8252f5caa | |
|
ce0fd10064 | |
|
ae2cbf1886 | |
|
29f2084e25 | |
|
02d34d3e55 | |
|
e14f7b93d1 | |
|
7fdd7075fd | |
|
179c19147e | |
|
0aafe6bb86 | |
|
03b8e58451 | |
|
5637809f5c | |
|
86306aa887 | |
|
30ee1fcdf7 | |
|
9cdfface3c | |
|
c463ec70f0 | |
|
f4d60d1fb9 | |
|
72d728d4e7 | |
|
20fe78266c | |
|
da0fc653ae | |
|
405ddb6994 | |
|
b5df8f2dad | |
|
7c520aae88 | |
|
96a8f66138 | |
|
bd951e4817 | |
|
fa3e6c3cd5 | |
|
101e9bd0d2 | |
|
40db2d84ef | |
|
f44aef8926 | |
|
0785f0e97f | |
|
363460290d | |
|
309fcffc86 | |
|
2471b1ea4c | |
|
efb966b3f8 | |
|
b30ecc89fb | |
|
7ab21831a0 | |
|
1b9e9c7518 | |
|
602e1fc4ae | |
|
2b3dfe540c | |
|
14bd2c4b0e | |
|
860290fc26 | |
|
c0ec2f359a | |
|
f6bad20818 | |
|
a991a4c187 | |
|
ad2c2ff2ec | |
|
297355f27e | |
|
e62d74f0e6 | |
|
6c6af8707a | |
|
ce813a3450 | |
|
11bf5481c7 | |
|
66802a287f | |
|
c3afc8c2a1 | |
|
cd3ca5dc82 | |
|
2ddba9924c | |
|
d6a562849c | |
|
c55e807262 | |
|
2c7d1de637 | |
|
99de9e31bc | |
|
86eb446408 | |
|
b71471c61e | |
|
ca3a70d623 | |
|
db758bbed2 | |
|
3df2601f27 | |
|
5c6bbcdbb2 | |
|
f6a79c3be8 | |
|
a6c0edb89d | |
|
d99eb81385 | |
|
1087b39f9c | |
|
893c88bbf5 | |
|
e5a2bec217 | |
|
e713541b35 | |
|
7f831e90d2 | |
|
e639f7d7b7 | |
|
790c48e0d0 | |
|
809dfe7fa3 | |
|
04028e6181 | |
|
c5025d25b2 | |
|
e09849b1a2 | |
|
5dbfed7a31 | |
|
c9d732800a | |
|
13aff0d5dc | |
|
4063e03108 | |
|
f789b58389 | |
|
770e6752f1 | |
|
c381a8d179 | |
|
3a5e6ae51c | |
|
78e883a106 | |
|
19ce7eef56 | |
|
05f798dea3 | |
|
5b3764c1b7 | |
|
0517f01aa9 | |
|
7697ab186a | |
|
d2010b859a | |
|
5fec66f97c | |
|
680069d4ca | |
|
b020604931 | |
|
9a32a7fe0b | |
|
7081a04d58 | |
|
b847d9aa6c | |
|
aea7f16891 | |
|
6f4ec05ccb | |
|
e896cae995 | |
|
f6803f07f1 | |
|
30dcc8af9f | |
|
557a5853a4 | |
|
d4e65a581d | |
|
c7a12cbdd3 | |
|
6d249abb44 | |
|
f754a93493 | |
|
a84db9a788 | |
|
731a4d9da8 | |
|
c4252d2103 | |
|
9292eb18fa | |
|
b70417565a | |
|
9bcf2d968c | |
|
a21c117e5b | |
|
86c342b163 | |
|
d8b615f5b9 | |
|
d1573f83ab | |
|
d9ebcefd2f | |
|
b83a5d491f | |
|
eb9dea5dd0 | |
|
43c6af4a8b | |
|
e928cee038 | |
|
f17ccce780 | |
|
31c2a90f02 | |
|
80dd77a6c8 | |
|
12a6696960 | |
|
cf83bbf6ac | |
|
47cd26fbab | |
|
650ecc6aab | |
|
c0e82648db | |
|
2b7257fd2d | |
|
dad2e089c3 | |
|
ffe615ef2a | |
|
456de36547 | |
|
46c617a31a | |
|
ef8240da56 | |
|
d1c9436e45 | |
|
631544356a | |
|
212c706e14 | |
|
b06aea8115 | |
|
2bd007d0c0 | |
|
a21d17dda9 | |
|
5b5d262ce4 | |
|
8c25ed19a3 | |
|
2c27181bb5 | |
|
3efb520fe0 | |
|
0dea6a2553 | |
|
807049f6c7 | |
|
86e513d845 | |
|
5d57a8968d | |
|
e2d33ea496 | |
|
d3938adf63 | |
|
08b7f2406c | |
|
041be81af1 | |
|
4fc9d038ca | |
|
1ea9bdf841 | |
|
1d50f9eb69 | |
|
d962ff8db0 | |
|
d2a0aa3098 | |
|
47b7222445 | |
|
2616d0d8b4 | |
|
0219d69bfb | |
|
391d47318b | |
|
1282c17e81 | |
|
9f367cb39b | |
|
2f8da5a8be | |
|
912c5a9bb9 | |
|
715b63b1c8 | |
|
ad5d8dbb87 | |
|
8a24141803 | |
|
1c733adfce | |
|
1defa8d582 | |
|
f061bf9730 | |
|
6d62858d69 | |
|
cc8800bf60 | |
|
ef6da4aa17 | |
|
52c9fece43 | |
|
06cf0f7d84 | |
|
99e30376f0 | |
|
aed4c92713 | |
|
edc362f4b9 | |
|
033e81bb22 | |
|
682e06590f | |
|
dd2f7ed01d | |
|
205b1fe1ed | |
|
fd9cefdb76 | |
|
9aa086e7c4 | |
|
4b2ac683b7 | |
|
96aa1f09ea | |
|
6012733385 | |
|
695da486d2 | |
|
6cf3648b94 | |
|
369c252a68 | |
|
4fd06bc159 | |
|
ee52199402 | |
|
74a6d4bdfb | |
|
422d853020 | |
|
95d4e03ca4 | |
|
3870959747 | |
|
1b145d1f04 | |
|
391a988e5f | |
|
18d9a7a099 | |
|
2c5841f5ec | |
|
0baeaa5525 | |
|
62b8c685f0 | |
|
112c2d37d8 | |
|
8f4f351885 | |
|
cad4ed3d29 | |
|
52e08e6077 | |
|
31d2ac1259 | |
|
7367335143 | |
|
f01423a151 | |
|
0529b0dca0 | |
|
aa229b8778 | |
|
2098040d62 | |
|
aa04e8c122 | |
|
3b44a76cc4 | |
|
cad5321516 | |
|
6cb34ae52c | |
|
eca95a62fc | |
|
d35d0c16e7 | |
|
7364eb1ebf | |
|
2dc7e87e96 | |
|
99f80ecddb | |
|
0eb355cb12 | |
|
37c6d5a6b5 | |
|
1ea717ce36 | |
|
10a1741bd9 | |
|
9ccc3d0470 | |
|
c5b6539c3b | |
|
d953ae8db6 | |
|
948c47b7ff | |
|
e3d786a79c | |
|
2d83d9ca79 | |
|
2856d88b12 | |
|
fd41059087 | |
|
642f53dcef | |
|
1d4817f6af | |
|
ec11efc347 | |
|
ba514f7827 | |
|
3d58bc1c32 | |
|
6eb37f63f2 | |
|
79178228b3 | |
|
c1f7968f7c | |
|
844bf0e294 | |
|
df0f0a21c3 | |
|
1a255bdbf6 | |
|
66e65c7a00 | |
|
f8dd6bca59 | |
|
ce938cc73b | |
|
efd0373c21 | |
|
1e4362b29e | |
|
fbf3e66de8 | |
|
9cc63ce391 | |
|
76143a6474 | |
|
9ce889b404 | |
|
b672943d0e | |
|
a5222d3894 | |
|
9db8405954 | |
|
a2a9fbb840 | |
|
815585d343 | |
|
77da32b99a | |
|
5db0b57b31 | |
|
be3e625f15 | |
|
f735cb937f | |
|
6c6815c727 | |
|
23c730cd85 | |
|
620569a97c | |
|
ec0fe277f6 | |
|
ae13593d05 | |
|
f0922a73f3 | |
|
b2597a337c | |
|
34cc35ad7b | |
|
10f53c5f17 | |
|
7d4bb336b3 | |
|
007bf0df9c | |
|
116dafc662 | |
|
f0572a6198 | |
|
eee34ec9f0 | |
|
43c98cb819 | |
|
cb23b1f378 | |
|
88f6e8519b | |
|
ce18af897a | |
|
493b2ec72d | |
|
9e64b36566 | |
|
396161f6c1 | |
|
0ed94d57e8 | |
|
f72756b089 | |
|
b4cb43178b | |
|
d17c61ddf3 | |
|
074cf12864 | |
|
5171aca31f | |
|
d7d2e0ba1b | |
|
1dd4a2d979 | |
|
1d89d38bc3 | |
|
574bce8006 | |
|
bf66727ab7 | |
|
6aafc842ae | |
|
f8e13c6cd8 | |
|
81c72468a1 | |
|
260f4c67e7 | |
|
d608c36c28 | |
|
da82eeff92 | |
|
47bb2ae6e0 | |
|
ed686fc4d6 | |
|
7c4feea366 | |
|
8329c71b08 | |
|
fb91818c5f | |
|
5f0a271bf7 | |
|
25fe17d59c | |
|
bc15bd663e | |
|
282215e760 | |
|
b60e7aa98b | |
|
a491579a6d | |
|
3db2e7ff6c | |
|
4d4aad27e4 | |
|
f26e5e9980 | |
|
cb922fa0fa | |
|
a2c761f055 | |
|
e1f37c79ce | |
|
026d95b9c2 | |
|
0ff7e62c60 | |
|
7ea9732c8f | |
|
50a5695241 | |
|
0b6419d1a5 | |
|
bb865d4a49 | |
|
80df33888a | |
|
7779d01854 | |
|
18caa7869b | |
|
965d6cb6a9 | |
|
7fb79a4736 | |
|
2fafc0bd13 | |
|
5d4850a93d | |
|
441c1e7ad8 | |
|
c815c8b403 | |
|
76eb230ae8 | |
|
33a6037982 | |
|
71d4cf3c2d | |
|
e7d71c78e2 |
|
@ -1,4 +1,5 @@
|
|||
*.swp
|
||||
*.pb
|
||||
go.mod
|
||||
go.sum
|
||||
/resources/*.so
|
||||
|
|
53
Makefile
53
Makefile
|
@ -6,24 +6,27 @@ 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
|
||||
forge dirty --verbose
|
||||
default: install-verbose tag
|
||||
|
||||
tag:
|
||||
forge tag list
|
||||
|
||||
vet:
|
||||
@GO111MODULE=off go vet
|
||||
@echo this go binary package builds okay
|
||||
|
||||
verbose:
|
||||
GO111MODULE=off go build -v -x \
|
||||
-ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}"
|
||||
|
||||
build: goimports vet plugin
|
||||
GO111MODULE=off go build \
|
||||
-ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}"
|
||||
|
||||
install: goimports vet plugin
|
||||
install: goimports plugin
|
||||
GO111MODULE=off go install \
|
||||
-ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}"
|
||||
cp -f ~/go/bin/forge ~/go/bin/last.forge # this is a hack so that go-deb can build a .deb file for forge # TODO: remove this
|
||||
|
||||
install-verbose: goimports vet plugin
|
||||
GO111MODULE=off go install -v -x \
|
||||
-ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}"
|
||||
|
||||
install-raw: goimports vet plugin
|
||||
go install \
|
||||
|
@ -31,13 +34,19 @@ install-raw: goimports vet plugin
|
|||
|
||||
plugin:
|
||||
rm -f resources/*.so
|
||||
cp ../../toolkits/gocui/gocui.so resources/
|
||||
# -cp ../../toolkits/gocui/gocui.so resources/
|
||||
|
||||
andlabs: install
|
||||
GTK: clean install
|
||||
forge --gui andlabs
|
||||
|
||||
gocui: install
|
||||
forge --gui gocui --gui-verbose >/tmp/forge.log 2>&1
|
||||
GTK-verbose: clean install
|
||||
forge --gui andlabs --gui-verbose
|
||||
|
||||
CUI: install
|
||||
forge --gui gocui
|
||||
|
||||
CUI-verbose: install
|
||||
forge --gui gocui --gui-verbose >/tmp/forge.log 2>&1
|
||||
|
||||
goimports:
|
||||
reset
|
||||
|
@ -48,10 +57,30 @@ goimports:
|
|||
clean:
|
||||
-rm -f forge go.*
|
||||
# -rm -f ~/go/src/repos.pb
|
||||
go-mod-clean --purge
|
||||
go-mod-clean purge
|
||||
|
||||
identify-protobuf:
|
||||
autogenpb --identify ~/go/src/repos.pb
|
||||
|
||||
devel:
|
||||
forge clean devel --force --verbose
|
||||
|
||||
pull: install
|
||||
FORGE_URL="https://forge.grid.wit.com/" forge pull check
|
||||
|
||||
# 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"
|
||||
|
||||
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
|
||||
echo "*/" >> doc.go
|
||||
echo "package main" >> doc.go
|
||||
|
|
60
README.md
60
README.md
|
@ -1,5 +1,33 @@
|
|||
# forge
|
||||
|
||||
forge is a GUI front end for 'git' designed with the
|
||||
intent of simplifying federated git development.
|
||||
|
||||
## Install:
|
||||
|
||||
* go install go.wit.com/apps/forge@latest
|
||||
|
||||
## Theory
|
||||
|
||||
* Software engineering is the art of making things work.
|
||||
* Release engineering is the art of making things perfect.
|
||||
|
||||
## Rules
|
||||
|
||||
1) forge is only a GUI and wrapper around 'git'
|
||||
2) forge _only_ os.Exec()'s git. Anything forge does
|
||||
can be done on the command line using 'git' directly
|
||||
3) forge's default behavior is to use 3 branches:
|
||||
a) The git upstream master/main branch
|
||||
b) A "devel" branch that is published
|
||||
c) a "user" branch that can be local only to the developer
|
||||
|
||||
## Development Goals
|
||||
|
||||
* have a GUI that also works on the command line
|
||||
|
||||
## Notes
|
||||
|
||||
This can be used to maintain git repositories
|
||||
expiremental work on federated git
|
||||
|
||||
|
@ -9,22 +37,17 @@ expiremental work on federated git
|
|||
* uses a GUI or the console(console display needs work)
|
||||
* always wrap around 'git' -- it basically just types 'git' commands really fast
|
||||
|
||||
## Notes & Goals:
|
||||
|
||||
* use a GUI that also works on the command line
|
||||
* andlabs GTK gui plugin starts breaking around 200 repos
|
||||
|
||||
## Install:
|
||||
|
||||
* go install go.wit.com/apps/forge@latest
|
||||
|
||||
## building from sources (may work Jan 2026)
|
||||
## building from sources
|
||||
|
||||
```
|
||||
# note as of Feb 2025. Forge has an option in forge to build itself.
|
||||
# The instructions below are out of date, but provide the general idea.
|
||||
# accurate instructions are in the forge code itself
|
||||
|
||||
go install go.wit.com/apps/go-clone@latest # this tool makes it easier to 'git clone' repos and recursively 'git clone' the dependancies
|
||||
go install go.wit.com/apps/autogenpb@latest # this tool will generate the protobuf *pb.go files (also Marshal(), Sort(), etc.)
|
||||
|
||||
go-clone --recursive go.wit.com/apps/forge # this will 'git clone' about 20 repos into ~/go/src (or where your go.work file is)
|
||||
go-clone go.wit.com/apps/forge # this will 'git clone' about 20 repos into ~/go/src (or where your go.work file is)
|
||||
|
||||
cd go.wit.com/lib/protobuf/forgepb
|
||||
make # autogenpb will make .pb.go, marshal.pb.go and sort.pb.go files
|
||||
|
@ -39,3 +62,18 @@ make # this runs GO111MODULE=off go build insuring that y
|
|||
## Debian packages:
|
||||
|
||||
Instructions are on https://mirrors.wit.com/
|
||||
|
||||
## possible 'git bug' integration ideas:
|
||||
|
||||
```
|
||||
git pull origin +refs/bugs/\*:refs/bugs/\*
|
||||
git pull origin +refs/identities/\*:refs/identities/\*
|
||||
|
||||
|
||||
# remove the caches
|
||||
rm -rf .git/git-bug
|
||||
|
||||
|
||||
# rebuild the cache with any command
|
||||
git bug user
|
||||
```
|
||||
|
|
193
applyPatch.go
193
applyPatch.go
|
@ -1,16 +1,9 @@
|
|||
// Copyright 2024 WIT.COM Inc Licensed GPL 3.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 (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"go.wit.com/lib/protobuf/forgepb"
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
/*
|
||||
// saves the patches in ~/.config/forge/currentpatches/
|
||||
func savePatchset(pset *forgepb.Patchset) error {
|
||||
log.Info("savePatches() NAME", pset.Name)
|
||||
|
@ -19,88 +12,10 @@ 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
|
||||
}
|
||||
|
||||
// returns bad if patches can not be applied
|
||||
func dumpPatchset(pset *forgepb.Patchset) bool {
|
||||
log.Info("applyPatches() NAME", pset.Name)
|
||||
log.Info("applyPatches() COMMENT", pset.Comment)
|
||||
log.Info("applyPatches() GIT_AUTHOR_NAME", pset.GetGitAuthorName())
|
||||
log.Info("applyPatches() GIT_AUTHOR_EMAIL", pset.GetGitAuthorEmail())
|
||||
log.Info("applyPatches() Branch Name", pset.GetStartBranchName())
|
||||
log.Info("applyPatches() Start Hash", pset.GetStartBranchHash())
|
||||
|
||||
var count int
|
||||
var bad int
|
||||
all := pset.Patches.SortByFilename()
|
||||
for all.Scan() {
|
||||
p := all.Next()
|
||||
if IsValidPatch(p) {
|
||||
// ok
|
||||
} else {
|
||||
bad += 1
|
||||
}
|
||||
count += 1
|
||||
}
|
||||
log.Info("pset has", count, "total patches, ", bad, "bad patches")
|
||||
if bad == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsValidPatch(p *forgepb.Patch) bool {
|
||||
basepath, filename := filepath.Split(p.Filename)
|
||||
repo := me.forge.FindByGoPath(basepath)
|
||||
if repo == nil {
|
||||
log.Info("can not apply patch! repo not found", basepath, filename)
|
||||
return false
|
||||
}
|
||||
if repo.DevelHash() != p.StartHash {
|
||||
log.Info("can not apply patch! devel hash mismatch", basepath, filename)
|
||||
return false
|
||||
}
|
||||
log.Info("start:", p.StartHash, "end:", p.CommitHash, "file:", basepath, filename, "devel version", repo.GetDevelVersion())
|
||||
for _, line := range p.Files {
|
||||
log.Info("\t", line)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// re-run git CheckDirty() on everything
|
||||
func IsAnythingDirty() bool {
|
||||
me.found = new(gitpb.Repos)
|
||||
findAll() // select all the repos
|
||||
doCheckDirtyAndConfigSave()
|
||||
me.found = new(gitpb.Repos)
|
||||
findDirty()
|
||||
if len(me.found.Repos) == 0 {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
// From 18ee541f89be2e9f9a91c54873da87885e8ffdf5 Mon Sep 17 00:00:00 2001
|
||||
// From: Jeff Carr <jcarr@wit.com>
|
||||
// Date: Sun, 5 Jan 2025 01:18:47 -0600
|
||||
|
@ -114,98 +29,20 @@ func countCurrentPatches(repo *gitpb.Repo) int {
|
|||
return len(result.Stdout)
|
||||
}
|
||||
|
||||
func applyPatchset(pset *forgepb.Patchset) error {
|
||||
var everythingworked bool = true
|
||||
tmpdir, err := os.MkdirTemp("", "forge")
|
||||
func doRegister(newurl string) error {
|
||||
var url string
|
||||
url = me.urlbase + "/register?url=" + newurl
|
||||
body, err := me.forge.HttpPost(url, nil)
|
||||
if err != nil {
|
||||
log.Info("httpPost() failed:", err)
|
||||
return err
|
||||
}
|
||||
// log.Info("got to applyPatches() pset", pset)
|
||||
log.Info("applyPatches() NAME", pset.Name)
|
||||
log.Info("applyPatches() COMMENT", pset.Comment)
|
||||
log.Info("applyPatches() GIT_AUTHOR_NAME", pset.GetGitAuthorName())
|
||||
log.Info("applyPatches() GIT_AUTHOR_EMAIL", pset.GetGitAuthorEmail())
|
||||
all := pset.Patches.SortByFilename()
|
||||
for all.Scan() {
|
||||
p := all.Next()
|
||||
|
||||
basedir := me.forge.GetGoSrc()
|
||||
if fullname, err := savePatchFile(p, basedir); err != nil {
|
||||
log.Info(fullname, "save failed", err)
|
||||
continue
|
||||
} else {
|
||||
/*
|
||||
// log.Info("pset filename FILENAME IS REAL?", p.Filename, pset.Name, pset.Comment)
|
||||
basepath, filename := filepath.Split(p.Filename)
|
||||
fullpath := filepath.Join(me.forge.GetGoSrc(), basepath)
|
||||
log.Info("pset filename FILENAME IS REAL? fullpath", fullpath)
|
||||
fullTmpdir := filepath.Join(tmpdir, basepath)
|
||||
err := os.MkdirAll(fullTmpdir, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Info("applyPathces() MkdirAll failed for", fullTmpdir)
|
||||
log.Info("applyPathces() MkdirAll failed err", err)
|
||||
everythingworked = false
|
||||
continue
|
||||
}
|
||||
log.Info("pset filename FILENAME IS REAL? tmp fullTmpdir", fullTmpdir)
|
||||
tmpname := filepath.Join(fullTmpdir, filename)
|
||||
raw, _ := os.OpenFile(tmpname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
raw.Write(p.Data)
|
||||
raw.Close()
|
||||
*/
|
||||
/*
|
||||
FIX THIS
|
||||
cmd := []string{"git", "am", fullname}
|
||||
result := shell.PathRun(fullpath, cmd)
|
||||
if result.Exit != 0 {
|
||||
log.Info("cmd failed", cmd, result.Exit)
|
||||
everythingworked = false
|
||||
}
|
||||
*/
|
||||
}
|
||||
// until 'git am' works
|
||||
everythingworked = false
|
||||
test := strings.TrimSpace(string(body))
|
||||
for _, line := range strings.Split(test, "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
log.Info("server returned:", line)
|
||||
}
|
||||
if everythingworked {
|
||||
os.RemoveAll(tmpdir) // clean up
|
||||
}
|
||||
log.Info("THIS IS THE END MY FRIEND")
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
*/
|
||||
|
|
213
argv.go
213
argv.go
|
@ -1,8 +1,13 @@
|
|||
// 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"
|
||||
|
||||
"go.wit.com/lib/gui/prep"
|
||||
)
|
||||
|
||||
/*
|
||||
|
@ -12,51 +17,46 @@ import (
|
|||
var argv args
|
||||
|
||||
type args struct {
|
||||
Checkout *CheckoutCmd `arg:"subcommand:checkout" help:"switch branches using 'git checkout'"`
|
||||
Clean *CleanCmd `arg:"subcommand:clean" help:"clean git branches"`
|
||||
Commit *EmptyCmd `arg:"subcommand:commit" help:"'git commit' but errors out if on wrong branch"`
|
||||
Config *ConfigCmd `arg:"subcommand:config" help:"show your .config/forge/ settings"`
|
||||
Dirty *DirtyCmd `arg:"subcommand:dirty" help:"show repos git says are dirty"`
|
||||
GitFetch *FindCmd `arg:"subcommand:fetch" help:"run 'git fetch master'"`
|
||||
List *FindCmd `arg:"subcommand:list" help:"print a table of the current repos"`
|
||||
Patch *PatchCmd `arg:"subcommand:patch" help:"make patchsets"`
|
||||
GitPull *FindCmd `arg:"subcommand:pull" help:"run 'git pull'"`
|
||||
URL string `arg:"--connect" help:"forge url"`
|
||||
All bool `arg:"--all" help:"git commit --all"`
|
||||
Show string `arg:"--show" help:"show a repo"`
|
||||
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"`
|
||||
Help *EmptyCmd `arg:"subcommand:help" help:"New to forge? This is for you.'"`
|
||||
Checkout *CheckoutCmd `arg:"subcommand:checkout" help:"switch branches using 'git checkout'"`
|
||||
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"`
|
||||
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 *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"`
|
||||
Install string `arg:"--install" help:"install a repo"`
|
||||
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"`
|
||||
}
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
type testCmd string
|
||||
|
||||
type ExamineCmd struct {
|
||||
Fix *EmptyCmd `arg:"subcommand:fix" help:"try to auto fix branches"`
|
||||
Show *EmptyCmd `arg:"subcommand:show" help:"show a specific patch"`
|
||||
Submit string `arg:"--submit" help:"name of patchset"`
|
||||
}
|
||||
|
||||
type CleanCmd struct {
|
||||
Delete *EmptyCmd `arg:"subcommand:delete" help:"rescan repo"`
|
||||
Devel *CleanDevelCmd `arg:"subcommand:devel" help:"clean and verify the devel branches"`
|
||||
Examine *ExamineCmd `arg:"subcommand:examine" help:"examine 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"`
|
||||
Verify *VerifyCmd `arg:"subcommand:verify" help:"verify branches"`
|
||||
Repo string `arg:"--repo" help:"which repo to look at"`
|
||||
}
|
||||
|
||||
type VerifyCmd struct {
|
||||
User *FindCmd `arg:"subcommand:user" help:"verify the user branches"`
|
||||
Devel *FindCmd `arg:"subcommand:devel" help:"verify the devel branches"`
|
||||
Master *FindCmd `arg:"subcommand:master" help:"verify the master branches"`
|
||||
Verify *EmptyCmd `arg:"subcommand:verify" help:"rescan repo"`
|
||||
Repo string `arg:"--repo" help:"which repo to look at"`
|
||||
}
|
||||
|
||||
type CleanDevelCmd struct {
|
||||
|
@ -64,9 +64,28 @@ type CleanDevelCmd struct {
|
|||
}
|
||||
|
||||
type PatchCmd struct {
|
||||
List *EmptyCmd `arg:"subcommand:list" help:"list available patches"`
|
||||
Show *EmptyCmd `arg:"subcommand:show" help:"show a specific patch"`
|
||||
Submit string `arg:"--submit" help:"submit a new patchset with name"`
|
||||
Check *EmptyCmd `arg:"subcommand:check" help:"check the state of the patches"`
|
||||
List *EmptyCmd `arg:"subcommand:list" help:"your downloaded patchsets"`
|
||||
Get *EmptyCmd `arg:"subcommand:get" help:"get the new patchsets"`
|
||||
Show *EmptyCmd `arg:"subcommand:show" help:"your pending commits to your code"`
|
||||
Submit *SubmitCmd `arg:"subcommand:submit" help:"submit your commits"`
|
||||
Repos *SubmitCmd `arg:"subcommand:repos" help:"show repos with patches"`
|
||||
}
|
||||
|
||||
type SubmitCmd struct {
|
||||
Match string `arg:"positional"`
|
||||
}
|
||||
|
||||
type PullCmd struct {
|
||||
Check *EmptyCmd `arg:"subcommand:check" help:"check repo versions"`
|
||||
Dirty *EmptyCmd `arg:"subcommand:dirty" help:"only check dirty repos"`
|
||||
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 {
|
||||
|
@ -98,17 +117,23 @@ type CheckoutCmd struct {
|
|||
Master *FindCmd `arg:"subcommand:master" help:"git checkout master"`
|
||||
}
|
||||
|
||||
type MergeCmd struct {
|
||||
Devel *FindCmd `arg:"subcommand:devel" help:"merge user to devel"`
|
||||
Master *FindCmd `arg:"subcommand:master" help:"merge devel to master"`
|
||||
Publish *EmptyCmd `arg:"subcommand:publish" help:"increment versions and publish master branch"`
|
||||
}
|
||||
|
||||
type DirtyCmd struct {
|
||||
}
|
||||
|
||||
type FindCmd struct {
|
||||
Pub *EmptyCmd `arg:"subcommand:pub" help:"fix .config/forge/ and/or repos.pb protobuf file"`
|
||||
All bool `arg:"--all" help:"select every repo (the default)"`
|
||||
Mine bool `arg:"--mine" help:"your repos as defined in the forge config"`
|
||||
Favorites bool `arg:"--favorites" help:"your repos configured as favorites"`
|
||||
Private bool `arg:"--private" help:"your private repos from your .config/forge/"`
|
||||
Dirty bool `arg:"--dirty" help:"only use dirty git repos"`
|
||||
User bool `arg:"--user" help:"show repos on the user branch"`
|
||||
All bool `arg:"--all" help:"select every repo (the default)"`
|
||||
Mine bool `arg:"--mine" help:"your repos as defined in the forge config"`
|
||||
Favorites bool `arg:"--favorites" help:"your repos configured as favorites"`
|
||||
Private bool `arg:"--private" help:"your private repos from your .config/forge/"`
|
||||
Dirty bool `arg:"--dirty" help:"only use dirty git repos"`
|
||||
User bool `arg:"--user" help:"show repos on the user branch"`
|
||||
Full bool `arg:"--full" help:"show full repo names"`
|
||||
// ReadOnly bool `arg:"--readonly" help:"include read-only repos"`
|
||||
}
|
||||
|
||||
|
@ -118,47 +143,63 @@ func (args) Version() string {
|
|||
|
||||
func (a args) Description() string {
|
||||
return `
|
||||
forge -- a tool to git repos at go.wit.com
|
||||
forge -- a tool to manage lots of git repos. forge includes a GUI and TUI.
|
||||
|
||||
but you can probably use it for other things
|
||||
`
|
||||
forge only executes the 'git' command. Everything it does, you can run by hand with 'git'.
|
||||
`
|
||||
}
|
||||
|
||||
/*
|
||||
This supports GO projects so far.
|
||||
It will work from ~/go/src or where your go.work file is.
|
||||
Since I mostly use ~/go/src, that has been tested more.
|
||||
|
||||
Examples:
|
||||
forge # opens the GUI
|
||||
forge list # show every repo state
|
||||
forge dirty # check for dirty git repos
|
||||
forge pull # run 'git pull' in every repo
|
||||
forge checkout # switch git branches
|
||||
|
||||
forge config add --private --path /opt/bob # add a private repo /opt/bob
|
||||
forge config add --directory --writable \
|
||||
--gopath 'go.wit.com/user/bob' # directory contains r/w repos
|
||||
forge config add --private --writeable \
|
||||
--gopath 'go.wit.com/user/bob/work' # a GO repo that can not be published
|
||||
|
||||
`
|
||||
}
|
||||
*/
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
112
argvAutoshell.go
112
argvAutoshell.go
|
@ -1,112 +0,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("user devel master ")
|
||||
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("delete devel force examine force git-reset user verify")
|
||||
case "commit":
|
||||
fmt.Println("--all")
|
||||
case "config":
|
||||
fmt.Println("add fix list")
|
||||
case "delete":
|
||||
deleteMatch()
|
||||
case "dirty":
|
||||
fmt.Println("--verbose")
|
||||
case "examine":
|
||||
fmt.Println("fix")
|
||||
case "list":
|
||||
fmt.Println("--all --mine --favorites --private pub")
|
||||
case "pull":
|
||||
fmt.Println("--verbose")
|
||||
case "patch":
|
||||
fmt.Println("list --submit show")
|
||||
case "user":
|
||||
fmt.Println("--force")
|
||||
case "devel":
|
||||
fmt.Println("--verbose")
|
||||
case "master":
|
||||
fmt.Println("--force")
|
||||
case "verify":
|
||||
fmt.Println("user devel master")
|
||||
default:
|
||||
if argv.BashAuto[0] == ARGNAME {
|
||||
// list the subcommands here
|
||||
fmt.Println("--bash list checkout clean commit config dirty fetch 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)
|
||||
}
|
3
build
3
build
|
@ -3,3 +3,6 @@
|
|||
# include forgeConfig
|
||||
mkdir -p files/usr/bin/
|
||||
cp ../utils/wit-utils/forgeConfig/forgeConfig files/usr/bin/
|
||||
|
||||
mkdir -p files/usr/share/bash-completion/completions/
|
||||
forge --bash > files/usr/share/bash-completion/completions/forge
|
||||
|
|
|
@ -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")
|
||||
}
|
2
control
2
control
|
@ -3,6 +3,6 @@ Build-Depends: golang, protoc-gen-go, autogenpb, go-mod-clean
|
|||
Package: forge
|
||||
Maintainer: Jeff Carr <jcarr@wit.com>
|
||||
Architecture: amd64
|
||||
Depends:
|
||||
Depends: go-gui-toolkits, autogenpb, go-mod-clean, go-clone
|
||||
Recommends: go-clone
|
||||
Description: 'forge' an attempt at federated 'git'
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package main
|
||||
|
||||
/*
|
||||
enables GUI options and the debugger in your application
|
||||
*/
|
||||
|
||||
import (
|
||||
"go.wit.com/lib/debugger"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if debugger.ArgDebug() {
|
||||
log.Info("cmd line --debugger == true")
|
||||
go func() {
|
||||
log.Sleep(2)
|
||||
debugger.DebugWindow()
|
||||
}()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// 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"
|
||||
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func doBuild() error {
|
||||
v := []string{}
|
||||
if argv.Verbose {
|
||||
v = []string{"-v", "-x"}
|
||||
}
|
||||
|
||||
gopath := argv.Build
|
||||
|
||||
repo := me.forge.FindByGoPath(gopath)
|
||||
if repo == nil {
|
||||
return fmt.Errorf("rep not found: %s", gopath)
|
||||
}
|
||||
if err := me.forge.Build(repo, v); err != nil {
|
||||
log.Warn("Build failed:", repo.GetGoPath(), err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func doInstall() error {
|
||||
v := []string{}
|
||||
if argv.Verbose {
|
||||
v = []string{"-v", "-x"}
|
||||
}
|
||||
|
||||
gopath := argv.Install
|
||||
repo := me.forge.FindByGoPath(gopath)
|
||||
if repo == nil {
|
||||
return fmt.Errorf("rep not found: %s", gopath)
|
||||
}
|
||||
if err := me.forge.Install(repo, v); err != nil {
|
||||
log.Warn("Install failed", repo.GetGoPath(), err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
263
doCheckout.go
263
doCheckout.go
|
@ -1,3 +1,6 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
@ -5,264 +8,40 @@ import (
|
|||
"time"
|
||||
|
||||
"go.wit.com/lib/gui/shell"
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/lib/protobuf/forgepb"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
var ErrorNotAllReposOnMaster error = fmt.Errorf("not all repos on are on the master branch")
|
||||
var ErrorNotAllReposOnDevel error = fmt.Errorf("not all repos on are on the devel branch")
|
||||
var ErrorNotAllReposOnUser error = fmt.Errorf("not all repos on are on the user branch")
|
||||
|
||||
func IsEverythingOnMaster() (int, int, int, error) {
|
||||
var total int
|
||||
var count int
|
||||
var nope int
|
||||
|
||||
// first make sure every repo is on the master branch
|
||||
all := me.forge.Repos.All()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
total += 1
|
||||
if repo.GetMasterBranchName() == repo.GetCurrentBranchName() {
|
||||
count += 1
|
||||
} else {
|
||||
nope += 1
|
||||
}
|
||||
}
|
||||
if total != count {
|
||||
// log.Info(ErrorNotAllReposOnMaster)
|
||||
return total, count, nope, ErrorNotAllReposOnMaster
|
||||
}
|
||||
return total, count, nope, nil
|
||||
}
|
||||
|
||||
func IsEverythingOnDevel() (int, int, int, error) {
|
||||
var total int
|
||||
var count int
|
||||
var nope int
|
||||
|
||||
// first make sure every repo is on the master branch
|
||||
all := me.forge.Repos.All()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
total += 1
|
||||
if repo.GetDevelBranchName() == repo.GetCurrentBranchName() {
|
||||
count += 1
|
||||
} else {
|
||||
nope += 1
|
||||
}
|
||||
}
|
||||
if total != count {
|
||||
return total, count, nope, ErrorNotAllReposOnDevel
|
||||
}
|
||||
return total, count, nope, nil
|
||||
}
|
||||
|
||||
func IsEverythingOnUser() (int, int, int, error) {
|
||||
var total int
|
||||
var count int
|
||||
var nope int
|
||||
|
||||
// first make sure every repo is on the master branch
|
||||
all := me.forge.Repos.All()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
total += 1
|
||||
if repo.GetCurrentBranchName() == repo.GetUserBranchName() {
|
||||
count += 1
|
||||
} else {
|
||||
nope += 1
|
||||
}
|
||||
}
|
||||
if total != count {
|
||||
return total, count, nope, ErrorNotAllReposOnUser
|
||||
}
|
||||
return total, count, nope, nil
|
||||
}
|
||||
|
||||
func doGitReset() {
|
||||
all := me.found.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if me.forge.Config.IsReadOnly(repo.GetGoPath()) {
|
||||
// log.Info("is readonly", repo.GetGoPath())
|
||||
if repo.CheckDirty() {
|
||||
log.Info("is readonly and dirty", repo.GetGoPath())
|
||||
cmd := []string{"git", "reset", "--hard"}
|
||||
repo.RunRealtime(cmd)
|
||||
}
|
||||
} else {
|
||||
// log.Info("is not readonly", repo.GetGoPath())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func rillCheckoutUser(repo *gitpb.Repo) error {
|
||||
if repo.IsDirty() {
|
||||
// never do dirty repos
|
||||
return nil
|
||||
}
|
||||
if repo.GetCurrentBranchName() == repo.GetMasterBranchName() {
|
||||
// repo is already on devel branch. have to move them there first for now
|
||||
// return repo.CheckoutDevel()
|
||||
}
|
||||
if repo.GetCurrentBranchName() == repo.GetUserBranchName() {
|
||||
// repo is already on user branch
|
||||
return nil
|
||||
}
|
||||
if err := repo.CheckoutUser(); err != nil {
|
||||
log.Info(repo.GetFullPath(), err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// trys to figure out if there is still something to update
|
||||
func doAllCheckoutUser() error {
|
||||
now := time.Now()
|
||||
me.forge.RillFuncError(rillCheckoutUser)
|
||||
count := me.forge.RillReload()
|
||||
if count != 0 {
|
||||
me.forge.ConfigSave()
|
||||
}
|
||||
|
||||
total, count, nope, err := IsEverythingOnUser()
|
||||
log.Printf("User branch check. %d total repos. (%d ok) (%d not on user branch) (%s)\n", total, count, nope, shell.FormatDuration(time.Since(now)))
|
||||
if err != nil {
|
||||
// display all repos not on user
|
||||
me.found = new(gitpb.Repos)
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if repo.GetCurrentBranchName() != repo.GetUserBranchName() {
|
||||
me.found.AppendByGoPath(repo)
|
||||
}
|
||||
}
|
||||
me.forge.PrintHumanTable(me.found)
|
||||
log.Printf("There are %d repos that are NOT on the user branch\n", me.found.Len())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func rillCheckoutDevel(repo *gitpb.Repo) error {
|
||||
if repo.IsDirty() {
|
||||
// never do dirty repos
|
||||
return nil
|
||||
}
|
||||
if repo.GetCurrentBranchName() == repo.GetDevelBranchName() {
|
||||
// repo is already on devel branch
|
||||
return nil
|
||||
}
|
||||
repo.CheckoutDevel()
|
||||
return nil
|
||||
}
|
||||
|
||||
// is every repo on the devel branch?
|
||||
func doAllCheckoutDevel() error {
|
||||
now := time.Now()
|
||||
me.forge.RillFuncError(rillCheckoutDevel)
|
||||
count := me.forge.RillReload()
|
||||
if count != 0 {
|
||||
me.forge.ConfigSave()
|
||||
}
|
||||
|
||||
total, count, nope, err := IsEverythingOnDevel()
|
||||
log.Printf("Devel branch check. %d total repos. (%d ok) (%d not on user branch) (%s)\n", total, count, nope, shell.FormatDuration(time.Since(now)))
|
||||
if err != nil {
|
||||
// display all repos not on user
|
||||
me.found = new(gitpb.Repos)
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if repo.GetCurrentBranchName() != repo.GetDevelBranchName() {
|
||||
me.found.AppendByGoPath(repo)
|
||||
}
|
||||
}
|
||||
me.forge.PrintHumanTable(me.found)
|
||||
log.Printf("There are %d repos that are NOT on the devel branch\n", me.found.Len())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func rillCheckoutMaster(repo *gitpb.Repo) error {
|
||||
if repo.IsDirty() {
|
||||
// never do dirty repos
|
||||
return nil
|
||||
}
|
||||
if repo.GetCurrentBranchName() == repo.GetMasterBranchName() {
|
||||
// repo is already on master
|
||||
return nil
|
||||
}
|
||||
if repo.GetUserVersion() == "uerr" {
|
||||
repo.CheckoutMaster()
|
||||
return nil
|
||||
}
|
||||
if me.forge.Config.IsReadOnly(repo.GetGoPath()) {
|
||||
// skip other checks for readonly repos
|
||||
repo.CheckoutMaster()
|
||||
return nil
|
||||
}
|
||||
/*
|
||||
if repo.GetUserVersion() != repo.GetDevelVersion() {
|
||||
// don't switch branches if the user branch has uncommitted patches
|
||||
return nil
|
||||
}
|
||||
if repo.GetDevelVersion() != repo.GetMasterVersion() {
|
||||
// don't switch braches if the devel branch does not match master (needs merge)
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
repo.CheckoutMaster()
|
||||
return nil
|
||||
}
|
||||
|
||||
// trys to figure out if there is still something to update
|
||||
func doAllCheckoutMaster() error {
|
||||
now := time.Now()
|
||||
me.forge.RillFuncError(rillCheckoutMaster)
|
||||
count := me.forge.RillReload()
|
||||
if count != 0 {
|
||||
me.forge.ConfigSave()
|
||||
}
|
||||
|
||||
total, count, nope, err := IsEverythingOnMaster()
|
||||
log.Printf("Master branch check. %d total repos. (%d ok) (%d not on master branch) (%s)\n", total, count, nope, shell.FormatDuration(time.Since(now)))
|
||||
if err != nil {
|
||||
// display all repos not on master
|
||||
me.found = new(gitpb.Repos)
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if repo.GetCurrentBranchName() != repo.GetMasterBranchName() {
|
||||
me.found.AppendByGoPath(repo)
|
||||
}
|
||||
}
|
||||
me.forge.PrintHumanTable(me.found)
|
||||
log.Printf("There are %d repos that are NOT on the master branch\n", me.found.Len())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// trys to figure out if there is still something to update
|
||||
// todo: redo this logic as it is terrible
|
||||
|
||||
func doCheckout() error {
|
||||
if argv.Checkout.User != nil {
|
||||
doAllCheckoutUser()
|
||||
start := time.Now()
|
||||
err := me.forge.DoAllCheckoutUser(argv.Force)
|
||||
dur := time.Since(start)
|
||||
log.Printf("Checked out %d user braches in %s\n", me.forge.Repos.Len(), shell.FormatDuration(dur))
|
||||
if err != nil {
|
||||
badExit(err)
|
||||
}
|
||||
okExit("")
|
||||
}
|
||||
|
||||
if argv.Checkout.Devel != nil {
|
||||
doAllCheckoutDevel()
|
||||
// setForgeMode(forgepb.ForgeMode_DEVEL)
|
||||
if err := me.forge.DoAllCheckoutDevelNew(argv.Force); err != nil {
|
||||
badExit(err)
|
||||
}
|
||||
okExit("")
|
||||
}
|
||||
|
||||
if argv.Checkout.Master != nil {
|
||||
doAllCheckoutMaster()
|
||||
setForgeMode(forgepb.ForgeMode_MASTER) // disable "normal" mode if set
|
||||
|
||||
if err := me.forge.DoAllCheckoutMaster(); err != nil {
|
||||
badExit(err)
|
||||
}
|
||||
okExit("")
|
||||
}
|
||||
badExit(fmt.Errorf("did not specify what branch to checkout"))
|
||||
return nil
|
||||
}
|
||||
|
|
767
doClean.go
767
doClean.go
|
@ -1,559 +1,272 @@
|
|||
// 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"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"go.wit.com/lib/gui/shell"
|
||||
"go.wit.com/lib/protobuf/forgepb"
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
var ErrorReposHasLocalBranches error = fmt.Errorf("repo still has local branches")
|
||||
var ErrorMergeBranch error = fmt.Errorf("trunk has things not in the branch")
|
||||
var ErrorMergeTrunk error = fmt.Errorf("branch has things not in trunk")
|
||||
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 {
|
||||
if argv.Clean.Pub != nil {
|
||||
if err := doCleanPub(); err != nil {
|
||||
badExit(err)
|
||||
}
|
||||
log.Info("finished attempt at cleaning devel branches")
|
||||
return nil
|
||||
}
|
||||
if argv.Clean.Devel != nil {
|
||||
if err := doCleanDevel(); err != nil {
|
||||
badExit(err)
|
||||
}
|
||||
log.Info("finished attempt at cleaning devel branches")
|
||||
return nil
|
||||
}
|
||||
if argv.Clean.User != nil {
|
||||
if err := doCleanUser(); err != nil {
|
||||
log.Info(err)
|
||||
okExit("")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
setForgeMode(forgepb.ForgeMode_CLEAN)
|
||||
|
||||
func doCleanUser() error {
|
||||
if _, count, _, err := IsEverythingOnMaster(); err != nil {
|
||||
if count == 0 {
|
||||
log.Info("No repos are on the master branch")
|
||||
return nil
|
||||
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.Info("Not all repos are on the master branch")
|
||||
// return 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)
|
||||
}
|
||||
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if err := doCleanUserRepo(repo); err != nil {
|
||||
log.Info(repo.GetGoPath(), err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func doCleanDevel() error {
|
||||
var total int
|
||||
var count int
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if argv.Verbose {
|
||||
// log.Info("Cleaning:", repo.GetGoPath())
|
||||
}
|
||||
total += 1
|
||||
if repo.GetCurrentBranchName() != repo.GetMasterBranchName() {
|
||||
// repos must be in the master branch to clean the devel branch
|
||||
return nil
|
||||
continue
|
||||
}
|
||||
if repo.IsDirty() {
|
||||
return nil
|
||||
}
|
||||
count += 1
|
||||
if err := doCleanDevelRepo(repo); err != nil {
|
||||
log.Info(repo.GetGoPath(), err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
log.Printf("attempted cleaning %d branches of %d total branches\n", count, total)
|
||||
return nil
|
||||
}
|
||||
|
||||
func exactDevelRepo(repo *gitpb.Repo) error {
|
||||
devel := repo.GetDevelBranchName()
|
||||
master := repo.GetMasterBranchName()
|
||||
err := isBranchSubsetOfTrunk(repo, devel, master)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkhashes(repo *gitpb.Repo, hashes []string, refpath string) ([]string, error) {
|
||||
if !repo.Exists(refpath) {
|
||||
return hashes, nil
|
||||
}
|
||||
r, err := repo.RunStrict([]string{"cat", refpath})
|
||||
if err != nil {
|
||||
return hashes, err
|
||||
}
|
||||
newhash := r.Stdout[0]
|
||||
for _, hash := range hashes {
|
||||
if newhash != hash {
|
||||
return hashes, fmt.Errorf("%s hash broke %s %s", repo.GetGoPath(), newhash, hash)
|
||||
}
|
||||
}
|
||||
hashes = append(hashes, newhash)
|
||||
return hashes, nil
|
||||
}
|
||||
|
||||
func doCleanDevelRepo(repo *gitpb.Repo) error {
|
||||
var hashes []string
|
||||
devel := repo.GetDevelBranchName()
|
||||
if argv.Verbose {
|
||||
log.Printf("Start clean devel branch: %s %s\n", repo.GetGoPath(), devel)
|
||||
}
|
||||
|
||||
// check if devel branch exists in remote repo
|
||||
if repo.Exists(filepath.Join(".git/refs/remotes/origin", devel)) {
|
||||
if argv.Verbose {
|
||||
var err error
|
||||
if hashes, err = checkhashes(repo, hashes, filepath.Join(".git/refs/remotes/origin", devel)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
remote := filepath.Join("origin", devel)
|
||||
if err := isBranchSubsetOfTrunk(repo, devel, remote); err != nil {
|
||||
if err == ErrorMergeBranch {
|
||||
log.Info("can not do this yet. need push to upstream", repo.GetGoPath())
|
||||
if argv.Force {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
// log.Info("todo: verify against remote devel branch", repo.GetGoPath())
|
||||
}
|
||||
|
||||
// verify devel branch is subset of master branch
|
||||
master := repo.GetMasterBranchName()
|
||||
if argv.Verbose {
|
||||
var err error
|
||||
if hashes, err = checkhashes(repo, hashes, filepath.Join(".git/refs/heads", devel)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if argv.Verbose {
|
||||
var err error
|
||||
if hashes, err = checkhashes(repo, hashes, filepath.Join(".git/refs/heads", devel)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if argv.Verbose {
|
||||
log.Info("repo hashes all match. incredible", hashes, repo.GetGoPath())
|
||||
}
|
||||
if err := isBranchSubsetOfTrunk(repo, devel, master); err != nil {
|
||||
if err == ErrorMergeBranch {
|
||||
if argv.Force {
|
||||
if repo.GetCurrentBranchName() == devel {
|
||||
cmd := []string{"git", "merge", master}
|
||||
// only run this if branch is local
|
||||
_, err := repo.RunVerbose(cmd)
|
||||
return err
|
||||
} else {
|
||||
cmd := []string{"git", "merge", master}
|
||||
log.Info("can't run. on wrong branch.", cmd, repo.GetGoPath(), "current branch =", repo.GetCurrentBranchName())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
// log.Info("todo: verify against remote devel branch", repo.GetGoPath())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// removes all local branches
|
||||
func doCleanUserRepo(repo *gitpb.Repo) error {
|
||||
var hasLocal bool
|
||||
if repo.GitConfig == nil {
|
||||
return fmt.Errorf("GitConfig == nil")
|
||||
}
|
||||
if repo.GetCurrentBranchName() != repo.GetMasterBranchName() {
|
||||
// skip this while in devel
|
||||
return nil
|
||||
}
|
||||
if repo.IsDirty() {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, l := range repo.GitConfig.Local {
|
||||
log.Info("\tlocal branch name:", l.Name)
|
||||
}
|
||||
if argv.Clean.User != nil {
|
||||
base := repo.GetUserBranchName()
|
||||
if repo.Exists(filepath.Join(".git/refs/heads", base)) {
|
||||
log.Info("Delete local branch:", base, repo.GetGoPath())
|
||||
err := forceDeleteUserBranch(repo, base)
|
||||
repo.Reload()
|
||||
if err != nil {
|
||||
log.Info("Delete local branch ERROR:", err)
|
||||
return err
|
||||
}
|
||||
// return fmt.Errorf("todo")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
for name, b := range repo.GitConfig.Branches {
|
||||
if b.Name == "" {
|
||||
b.Name = name
|
||||
}
|
||||
if name == repo.GetMasterBranchName() {
|
||||
// never delete the master branch
|
||||
// todo: make sure the master branch is in sync with remote master
|
||||
continue
|
||||
}
|
||||
if name == repo.GetUserBranchName() {
|
||||
hasLocal = true
|
||||
if err := doCleanUserBranch(repo, b); err != nil {
|
||||
log.Info("\tLOCAL BRANCH ERROR user =", name)
|
||||
return err
|
||||
}
|
||||
log.Info("\tLOCAL BRANCH user =", name)
|
||||
continue
|
||||
}
|
||||
if name == repo.GetDevelBranchName() {
|
||||
continue
|
||||
}
|
||||
if name == repo.GetMasterBranchName() {
|
||||
continue
|
||||
}
|
||||
if err := verifyLocalBranchIsMerged(repo, b); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if argv.Clean.Force == nil {
|
||||
if hasLocal {
|
||||
return ErrorReposHasLocalBranches
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyLocalBranchIsMerged(repo *gitpb.Repo, branch *gitpb.GitBranch) error {
|
||||
base := filepath.Base(branch.Name)
|
||||
log.Info("local branch name unknown:", base, branch.Name, branch.Merge, branch.Remote, repo.GetGoPath())
|
||||
|
||||
// check if it exists in the origin
|
||||
if repo.Exists(filepath.Join(".git/refs/remotes/origin", base)) {
|
||||
err := fmt.Errorf("repo %s ERROR. branch is also remote %s", repo.GetGoPath(), branch.Name)
|
||||
log.Info(err)
|
||||
if err := isSafeToDelete(repo, base); err != nil {
|
||||
log.Info(err)
|
||||
return err
|
||||
}
|
||||
if err := requiresGitPush(repo, base); err != nil {
|
||||
log.Info(err)
|
||||
return err
|
||||
}
|
||||
err = fmt.Errorf("repo %s BRANCH AND REMOTE CAN BE DELETED %s", repo.GetGoPath(), branch.Name)
|
||||
log.Info(err)
|
||||
if argv.Clean.Force != nil {
|
||||
err = BADforceDeleteBranch(repo, base)
|
||||
repo.Reload()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if !repo.Exists(filepath.Join(".git/refs/heads", base)) {
|
||||
log.Info("GitConfig() parse logic error. not really a local branch", base)
|
||||
me.forge.Repos.Delete(repo)
|
||||
configSave = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// this is only a local branch
|
||||
if err := isSafeToDelete(repo, base); err != nil {
|
||||
log.Info(err)
|
||||
return err
|
||||
}
|
||||
err := fmt.Errorf("repo %s BRANCH CAN PROBABLY BE DELETED base=%s fullname=%s", repo.GetGoPath(), base, branch.Name)
|
||||
log.Info(err)
|
||||
if argv.Clean.Force != nil {
|
||||
err = BADforceDeleteBranch(repo, base)
|
||||
repo.Reload()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func doCleanUserBranch(repo *gitpb.Repo, branch *gitpb.GitBranch) error {
|
||||
if branch.Name != repo.GetUserBranchName() {
|
||||
return fmt.Errorf("repo %s was not user branch %s", repo.GetGoPath(), branch.Name)
|
||||
}
|
||||
/*
|
||||
if bran == repo.GetUserBranchName() {
|
||||
log.Info("The user branch also has a remote branch", repo.CurrentTag.Refname)
|
||||
log.Info("TODO: verify the remote branch is out of date", repo.CurrentTag.Refname)
|
||||
log.Info("TODO: delete the remote branch", repo.CurrentTag.Refname)
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
|
||||
if !repo.Exists(filepath.Join(".git/refs/heads", branch.Name)) {
|
||||
err := fmt.Errorf("BAD FORGE LOGIC. DELETE REPO %s", repo.GetGoPath())
|
||||
log.Warn(err)
|
||||
me.forge.Repos.Repos = slices.DeleteFunc(me.forge.Repos.Repos, func(r *gitpb.Repo) bool {
|
||||
if repo.GetGoPath() == r.GetGoPath() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// me.forge.Repos.Delete(repo)
|
||||
me.forge.ConfigSave()
|
||||
// os.Exit(0)
|
||||
return nil
|
||||
}
|
||||
|
||||
if repo.Exists(filepath.Join(".git/refs/remotes/origin", branch.Name)) {
|
||||
log.Info("why is this non-localonly branch a problem?", branch.Name)
|
||||
repo.RunVerbose([]string{"ls", "-l", ".git/refs/remotes/origin"})
|
||||
repo.RunVerbose([]string{"cat", filepath.Join(".git/refs/remotes/origin", branch.Name)})
|
||||
repo.RunVerbose([]string{"cat", filepath.Join(".git/refs/heads", branch.Name)})
|
||||
if err := userToDevelRequiresGitPush(repo, branch.Name); err != nil {
|
||||
log.Info(err)
|
||||
log.Info("THIS USER BRANCH MUST BE PUSHED TO DEVEL", branch.Name)
|
||||
return err
|
||||
}
|
||||
log.Info("THIS USER BRANCH IS CLEAN TO DELETE", branch.Name)
|
||||
if argv.Clean.Force != nil {
|
||||
err := BADforceDeleteBranch(repo, branch.Name)
|
||||
repo.Reload()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Info("why is this local only branch a problem?", branch.Name)
|
||||
repo.RunVerbose([]string{"ls", "-l", ".git/refs/remotes/origin"})
|
||||
r, err := repo.RunVerbose([]string{"cat", filepath.Join(".git/refs/heads", branch.Name)})
|
||||
if err == nil {
|
||||
cmd := []string{"git", "show", "-s", "--format=\"%H %ae %as %s\"", r.Stdout[0]}
|
||||
repo.RunVerbose(cmd)
|
||||
log.Info(cmd)
|
||||
}
|
||||
if err := userToDevelRequiresGitPush(repo, branch.Name); err != nil {
|
||||
log.Info(err)
|
||||
log.Info("THIS USER BRANCH MUST BE PUSHED TO DEVEL", branch.Name)
|
||||
return err
|
||||
}
|
||||
log.Info("THIS USER BRANCH IS CLEAN TO DELETE", branch.Name)
|
||||
if argv.Clean.Force != nil {
|
||||
cmd := []string{"git", "branch", "-D", branch.Name}
|
||||
if _, err := repo.RunVerbose(cmd); err != nil {
|
||||
log.Info("THE GIT BRANCH DELETE ERROR IS:", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// return fmt.Errorf("%s repo.CurrentTag is not local: %s. Don't proceed yet", repo.GetGoPath(), repo.CurrentTag.Refname)
|
||||
log.Printf("Do something %s on branch name:%s merge:%s remote:%s\n", repo.GetGoPath(), branch.Name, branch.Merge, branch.Remote)
|
||||
return nil
|
||||
}
|
||||
|
||||
func userToDevelRequiresGitPush(repo *gitpb.Repo, branchName string) error {
|
||||
devel := repo.GetDevelBranchName()
|
||||
missing := countDiffObjects(repo, branchName, "origin/"+devel)
|
||||
b2 := countDiffObjects(repo, "origin/"+devel, branchName)
|
||||
log.Info("user vs devel count", missing, b2)
|
||||
if missing == 0 && b2 == 0 {
|
||||
return nil
|
||||
}
|
||||
if missing != 0 {
|
||||
log.Info("user vs devel count missing != 0, b2 ==", missing, b2)
|
||||
log.Info("THIS MEANS THE LOCAL BRANCH NEEDS GIT PUSH TO ORIGIN BRANCH ==", missing)
|
||||
// if argv.Examine.Fix != nil {
|
||||
// return gitPushStrict(repo, branchName)
|
||||
// }
|
||||
return fmt.Errorf("user branch not clean to delete %d %d", missing, b2)
|
||||
}
|
||||
if missing == 0 {
|
||||
// log.Info("THIS MEANS THE LOCAL BRANCH IS OK TO DELETE missing =", missing)
|
||||
log.Info("THIS IS REALLY BAD RIGHT NOW. must to git push / git merge missing =", missing, b2)
|
||||
}
|
||||
return fmt.Errorf("user branch not clean to delete. maybe it is? devel might be ahead of user branch. %d %d", missing, b2)
|
||||
}
|
||||
|
||||
// checks against upstream master
|
||||
func isSafeToDelete(repo *gitpb.Repo, old string) error {
|
||||
var head string
|
||||
head = filepath.Join("origin", repo.GetMasterBranchName())
|
||||
|
||||
if !repo.Exists(filepath.Join(".git/refs/remotes/", head)) {
|
||||
head = filepath.Join("origin", "HEAD")
|
||||
}
|
||||
|
||||
b1 := countDiffObjects(repo, old, head)
|
||||
b2 := countDiffObjects(repo, head, old)
|
||||
log.Info(old, "vs origin count", b1, b2)
|
||||
if b1 == 0 && b2 == 0 {
|
||||
log.Info("isSafeToDelete() SAFE TO DELETE ==", old, b1, head, b2)
|
||||
return nil
|
||||
}
|
||||
if b1 == 0 {
|
||||
log.Info("isSafeToDelete() SAFE TO DELETE ==", old, b1, head, b2)
|
||||
return nil
|
||||
}
|
||||
if b1 != 0 {
|
||||
log.Info(old, "vs", head, " count b1 != 0, b2 ==", b2, "b1 =", b1)
|
||||
log.Info("THIS MEANS THE LOCAL BRANCH NEEDS GIT PUSH TO ORIGIN BRANCH ==", b1)
|
||||
cmd := repo.ConstructGitDiffLog(old, head)
|
||||
log.Info("cmd", cmd)
|
||||
cmd = repo.ConstructGitDiffLog(head, old)
|
||||
log.Info("cmd", cmd)
|
||||
if argv.Clean.Force != nil {
|
||||
// return gitPushStrict(repo, branchName)
|
||||
}
|
||||
return fmt.Errorf("user branch not clean to delete. needs git push %d %d", b1, b2)
|
||||
}
|
||||
return fmt.Errorf("user branch not clean to delete. needs git push %d %d", b1, b2)
|
||||
}
|
||||
|
||||
// literally ignore all errors. delete everthing with no checks for now
|
||||
func forceDeleteUserBranch(repo *gitpb.Repo, branch string) error {
|
||||
if repo.IsDirty() {
|
||||
log.Info(repo.GetGoPath(), "is dirty")
|
||||
return nil
|
||||
}
|
||||
if repo.GetUserBranchName() != branch {
|
||||
log.Info(repo.GetGoPath(), branch, "is not the user branch", repo.GetUserBranchName())
|
||||
return nil
|
||||
}
|
||||
if err := isSafeToDelete(repo, branch); err != nil {
|
||||
log.Info(err)
|
||||
return err
|
||||
}
|
||||
if repo.Exists(filepath.Join(".git/refs/remotes/origin/", branch)) {
|
||||
if err := requiresGitPush(repo, branch); err != nil {
|
||||
log.Info(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
configSave = true
|
||||
|
||||
cmd := []string{"git", "branch", "-D", branch}
|
||||
if _, err := repo.RunVerbose(cmd); err != nil {
|
||||
log.Info("THE GIT BRANCH DELETE ERROR IS:", err)
|
||||
// return err
|
||||
}
|
||||
log.Info("THIS USER REMOTE BRANCH MUST BE DELETED HERE", branch)
|
||||
if repo.Exists(filepath.Join(".git/refs/remote/origin", branch)) {
|
||||
cmd = []string{"git", "push", "origin", "--delete", branch}
|
||||
if _, err := repo.RunVerbose(cmd); err != nil {
|
||||
log.Info("THE GIT BRANCH DELETE ERROR IS:", err)
|
||||
// return err
|
||||
}
|
||||
}
|
||||
cmd = []string{"git", "branch", "-D", "--remote", "origin/" + branch}
|
||||
if _, err := repo.RunVerbose(cmd); err != nil {
|
||||
log.Info("THE GIT BRANCH DELETE ERROR IS:", err)
|
||||
// return err
|
||||
}
|
||||
// return fmt.Errorf("one at a time %s", repo.GetGoPath())
|
||||
return nil
|
||||
}
|
||||
|
||||
// literally ignore all errors. delete everthing with no checks for now
|
||||
func BADforceDeleteBranch(repo *gitpb.Repo, branch string) error {
|
||||
if repo.IsDirty() {
|
||||
log.Info(repo.GetGoPath(), "is dirty")
|
||||
return nil
|
||||
}
|
||||
configSave = true
|
||||
|
||||
cmd := []string{"git", "branch", "-D", branch}
|
||||
if _, err := repo.RunVerbose(cmd); err != nil {
|
||||
log.Info("THE GIT BRANCH DELETE ERROR IS:", err)
|
||||
// return err
|
||||
}
|
||||
log.Info("THIS USER REMOTE BRANCH MUST BE DELETED HERE", branch)
|
||||
if repo.Exists(filepath.Join(".git/refs/remote/origin", branch)) {
|
||||
cmd = []string{"git", "push", "origin", "--delete", branch}
|
||||
if _, err := repo.RunVerbose(cmd); err != nil {
|
||||
log.Info("THE GIT BRANCH DELETE ERROR IS:", err)
|
||||
// return err
|
||||
}
|
||||
}
|
||||
cmd = []string{"git", "branch", "-D", "--remote", "origin/" + branch}
|
||||
if _, err := repo.RunVerbose(cmd); err != nil {
|
||||
log.Info("THE GIT BRANCH DELETE ERROR IS:", err)
|
||||
// return err
|
||||
}
|
||||
// return fmt.Errorf("one at a time %s", repo.GetGoPath())
|
||||
return nil
|
||||
}
|
||||
|
||||
// verifies that the branch is a pure subset of the other branch
|
||||
// sorry about the 'master' 'slave' nameing thing. I guess that isn't
|
||||
// 'cool' to use anymore. I can't think of other terms that aren't reserved words.
|
||||
func isBranchSubsetOfTrunk(repo *gitpb.Repo, branch string, trunk string) error {
|
||||
b1 := countGitDiffLog(repo, branch, trunk) // should be zero
|
||||
b2 := countGitDiffLog(repo, trunk, branch) // can be greater than 1
|
||||
// log.Info(branch, "vs", trunk, "count", b1, b2)
|
||||
if b1 == 0 && b2 == 0 {
|
||||
// log.Info("branch and trunk are identical ==", branch, b1, trunk, b2)
|
||||
return nil
|
||||
}
|
||||
if argv.Verbose {
|
||||
log.Printf("%-40s NOT EXACT %s %s (%d) (%d)\n", repo.GetGoPath(), branch, trunk, b1, b2)
|
||||
}
|
||||
if b1 == 0 {
|
||||
cmd := []string{"git", "merge", trunk}
|
||||
log.Printf("%-40s branch %s needs merge with trunk %s len(%d) %s\n", repo.GetGoPath(), branch, trunk, b2, cmd)
|
||||
return ErrorMergeBranch
|
||||
}
|
||||
if b2 == 0 {
|
||||
log.Printf("%-40s trunk %s needs merge with branch %s len(%d)\n", repo.GetGoPath(), branch, trunk, b2)
|
||||
return ErrorMergeTrunk
|
||||
}
|
||||
return fmt.Errorf("branch not clean to delete. needs merge %d %d", b1, b2)
|
||||
}
|
||||
|
||||
// count all objects only in branch1
|
||||
// if zero, that means branch1 is entirely contained in branch2 and can be safely deleted
|
||||
func countGitDiffLog(repo *gitpb.Repo, branch1, branch2 string) int {
|
||||
cmd := repo.ConstructGitDiffLog(branch1, branch2)
|
||||
r, err := repo.RunStrict(cmd)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
// log.Info("countDiffObjects()", cmd, len(r.Stdout), strings.Join(r.Stdout, " "))
|
||||
return len(r.Stdout)
|
||||
}
|
||||
|
||||
func doCleanPub() error {
|
||||
total := 0
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
// when publishing, clean out the details of that if it's still there
|
||||
if repo.GetTargetVersion() != "" {
|
||||
repo.SetTargetVersion("")
|
||||
configSave = true
|
||||
total += 1
|
||||
}
|
||||
|
||||
// try to delete user
|
||||
if err := doRepoCleanUser(repo); err != nil {
|
||||
log.Info(repo.GetGoPath(), err)
|
||||
}
|
||||
|
||||
// try to delete devel
|
||||
doRepoCleanDevel(repo)
|
||||
}
|
||||
|
||||
found := gitpb.NewRepos()
|
||||
|
||||
total := 0
|
||||
// find all repos that aren't "clean"
|
||||
all = me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
total += 1
|
||||
|
||||
// find repos not on master branch
|
||||
if repo.GetCurrentBranchName() != repo.GetMasterBranchName() {
|
||||
found.AppendByFullPath(repo)
|
||||
continue
|
||||
}
|
||||
|
||||
// find dirty repos
|
||||
if repo.IsDirty() {
|
||||
found.AppendByFullPath(repo)
|
||||
continue
|
||||
}
|
||||
|
||||
// find repos that still have a local user branch
|
||||
if repo.IsLocalBranch(repo.GetUserBranchName()) {
|
||||
found.AppendByFullPath(repo)
|
||||
continue
|
||||
}
|
||||
|
||||
// find repos that still have a local devel branch
|
||||
if repo.IsLocalBranch(repo.GetDevelBranchName()) {
|
||||
found.AppendByFullPath(repo)
|
||||
continue
|
||||
}
|
||||
}
|
||||
log.Printf("clearing %d total repos\n", total)
|
||||
|
||||
// display all the repos that aren't clean to the user
|
||||
log.Printf("\n") // padding for now
|
||||
if argv.Verbose {
|
||||
me.forge.PrintHumanTableDirty(found)
|
||||
} else {
|
||||
me.forge.PrintHumanTable(found)
|
||||
}
|
||||
log.Printf("\n") // padding for now
|
||||
|
||||
var hmm int
|
||||
hmm = found.Len()
|
||||
if hmm == 0 {
|
||||
log.Printf("%d repos are not clean\n", hmm)
|
||||
} else {
|
||||
log.Info("All repos are clean", total, hmm)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
func doesLocalBranchExist(repo *gitpb.Repo, branch string) bool {
|
||||
return repo.Exists(filepath.Join(".git/refs/heads", branch))
|
||||
}
|
||||
*/
|
||||
func doRepoCleanDevel(repo *gitpb.Repo) error {
|
||||
if !repo.IsLocalBranch(repo.GetDevelBranchName()) {
|
||||
// there is no local branch named 'devel'
|
||||
return nil
|
||||
}
|
||||
|
||||
if repo.GetCurrentBranchName() != repo.GetMasterBranchName() {
|
||||
return log.Errorf("%s not on master branch:", repo.GetFullPath())
|
||||
}
|
||||
if repo.IsDirty() {
|
||||
return log.Errorf("%s is dirty:", repo.GetFullPath())
|
||||
}
|
||||
if err := justDeleteTheDevelBranchAlready(repo); err != nil {
|
||||
log.Info("justDeleteTheDevel() err", repo.GetGoPath(), err)
|
||||
configSave = true
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// removes all local user branches
|
||||
func doRepoCleanUser(repo *gitpb.Repo) error {
|
||||
if repo.IsDirty() {
|
||||
return nil
|
||||
}
|
||||
|
||||
bruser := repo.GetUserBranchName()
|
||||
brdevel := repo.GetDevelBranchName()
|
||||
|
||||
if repo.IsBranchRemote(bruser) {
|
||||
log.Info("forge is designed to always have local only user branches", bruser)
|
||||
return fmt.Errorf("forge is designed to always have local only user branches")
|
||||
}
|
||||
|
||||
if !repo.IsLocalBranch(bruser) {
|
||||
// there is no local user branch
|
||||
return nil
|
||||
}
|
||||
|
||||
// will you loose work if you delete your user branch?
|
||||
// if DevelBranchExists()
|
||||
// then if UserBranchCommits exist in DevelBranch
|
||||
// DeleteUserBranch is safe
|
||||
if repo.IsLocalBranch(brdevel) {
|
||||
b1 := repo.CountDiffObjects(bruser, "refs/heads/"+brdevel) // should be zero
|
||||
if b1 == 0 {
|
||||
// every user branch exists in devel. delete user branch
|
||||
cmd := []string{"git", "branch", "-D", bruser}
|
||||
// log.Info("USER IS IN DEVEL", repo.GetGoPath(), cmd)
|
||||
_, err := repo.RunVerboseOnError(cmd)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
brmaster := repo.GetMasterBranchName()
|
||||
|
||||
// will you loose work if you delete your user branch?
|
||||
// if master branch exists()
|
||||
// then if all user commits exist in master
|
||||
// delete user branch is safe
|
||||
if repo.IsLocalBranch(brmaster) {
|
||||
b1 := repo.CountDiffObjects(bruser, "refs/heads/"+brmaster) // should be zero
|
||||
if b1 == 0 {
|
||||
cmd := []string{"git", "branch", "-D", bruser}
|
||||
// log.Info("USER IS IN DEVEL", repo.GetGoPath(), cmd)
|
||||
_, err := repo.RunVerboseOnError(cmd)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("%s branch has unique commits", bruser)
|
||||
}
|
||||
|
||||
// if you call this, there is no going back. no checks anymore. nothing
|
||||
// it deletes the 'devel' branch. git branch -D "devel". END OF STORY
|
||||
func justDeleteTheDevelBranchAlready(repo *gitpb.Repo) error {
|
||||
branch := repo.GetDevelBranchName()
|
||||
remote := filepath.Join("origin", branch)
|
||||
|
||||
if me.forge.Config.IsReadOnly(repo.GetGoPath()) {
|
||||
if repo.IsDevelRemote() {
|
||||
// just make sure the remote & local branches are the same
|
||||
return repo.DeleteLocalDevelBranch()
|
||||
}
|
||||
}
|
||||
|
||||
// check against remote if it exists
|
||||
if repo.IsDevelRemote() {
|
||||
b1 := repo.CountDiffObjects(branch, remote) // should be zero
|
||||
if b1 == 0 {
|
||||
cmd := []string{"git", "branch", "-D", repo.GetDevelBranchName()}
|
||||
// log.Info("DEVEL IS IN REMOTE", repo.GetGoPath(), cmd)
|
||||
_, err := repo.RunVerboseOnError(cmd)
|
||||
return err
|
||||
}
|
||||
cmd := []string{"git", "push"}
|
||||
log.Info("DEVEL LOCAL NEEDS GIT PUSH TO REMOTE", repo.GetGoPath(), cmd)
|
||||
err := repo.RunVerbose(cmd)
|
||||
return err
|
||||
}
|
||||
|
||||
// remote doesn't exist, check against master
|
||||
master := repo.GetMasterBranchName()
|
||||
b1 := repo.CountDiffObjects(branch, "refs/heads/"+master) // should be zero
|
||||
if b1 == 0 {
|
||||
cmd := []string{"git", "branch", "-D", repo.GetDevelBranchName()}
|
||||
// log.Info("DEVEL IS IN REMOTE", repo.GetGoPath(), cmd)
|
||||
_, err := repo.RunVerboseOnError(cmd)
|
||||
return err
|
||||
}
|
||||
cmd := []string{"git", "merge something somehow"}
|
||||
log.Info("devel local, remote and master branches are wrong", repo.GetGoPath(), cmd, b1)
|
||||
// _, err := repo.RunVerbose(cmd)
|
||||
return nil
|
||||
}
|
||||
|
||||
func doGitReset() {
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if me.forge.Config.IsReadOnly(repo.GetGoPath()) {
|
||||
// log.Info("is readonly", repo.GetGoPath())
|
||||
if repo.CheckDirty() {
|
||||
log.Info("is readonly and dirty", repo.GetGoPath())
|
||||
cmd := []string{"git", "reset", "--hard"}
|
||||
repo.RunRealtime(cmd)
|
||||
}
|
||||
} else {
|
||||
// log.Info("is not readonly", repo.GetGoPath())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
77
doCommit.go
77
doCommit.go
|
@ -1,38 +1,99 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
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() {
|
||||
pwd, _ := os.Getwd()
|
||||
repo := me.forge.Repos.FindByFullPath(pwd)
|
||||
if repo == nil {
|
||||
log.Info("todo: forge doesn't know how to work here yet")
|
||||
func doCommit() error {
|
||||
if argv.All {
|
||||
found := me.forge.CheckDirty()
|
||||
var newpatches bool
|
||||
for repo := range found.IterAll() {
|
||||
log.Info("do a commit on repo", repo.GetGoPath())
|
||||
if err := doCommitRepo(repo); err != nil {
|
||||
badExit(err)
|
||||
}
|
||||
newpatches = true
|
||||
repo.CheckDirty()
|
||||
}
|
||||
if newpatches {
|
||||
config.SetChanged("repos", true)
|
||||
return doPatchSubmit()
|
||||
}
|
||||
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()))
|
||||
} else {
|
||||
log.Info("repo is dirty", repo.GetFullPath())
|
||||
}
|
||||
|
||||
if repo.GetCurrentBranchName() != repo.GetUserBranchName() {
|
||||
me.found.Append(repo)
|
||||
me.forge.PrintHumanTable(me.found)
|
||||
found := new(gitpb.Repos)
|
||||
found.Append(repo)
|
||||
me.forge.PrintHumanTable(found)
|
||||
log.Info("")
|
||||
log.Info("wrong branch. Can not commit on", repo.GetCurrentBranchName())
|
||||
log.Info("")
|
||||
okExit("")
|
||||
}
|
||||
|
||||
os.Setenv("LESS", "-XR")
|
||||
if err := shell.Exec([]string{"git", "diff"}); err != nil {
|
||||
badExit(err)
|
||||
}
|
||||
|
||||
if argv.All {
|
||||
if err := shell.ExecCheck([]string{"git", "add", "--all"}); err != nil {
|
||||
badExit(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := shell.ExecCheck([]string{"git", "commit", "--all"}); err != nil {
|
||||
badExit(err)
|
||||
}
|
||||
log.Info("git commit ok. forge done")
|
||||
|
||||
return doPatchSubmit()
|
||||
}
|
||||
|
||||
func doCommitRepo(repo *gitpb.Repo) error {
|
||||
if repo.GetCurrentBranchName() != repo.GetUserBranchName() {
|
||||
found := new(gitpb.Repos)
|
||||
found.Append(repo)
|
||||
me.forge.PrintHumanTable(found)
|
||||
log.Info("")
|
||||
log.Info("wrong branch. Can not commit on", repo.GetCurrentBranchName())
|
||||
log.Info("")
|
||||
return nil
|
||||
}
|
||||
os.Chdir(repo.GetFullPath())
|
||||
|
||||
os.Setenv("LESS", "-XR")
|
||||
if err := shell.Exec([]string{"git", "diff"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if argv.All {
|
||||
if err := shell.ExecCheck([]string{"git", "add", "--all"}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := shell.ExecCheck([]string{"git", "commit", "--all"}); err != nil {
|
||||
shell.ExecCheck([]string{"git", "reset"})
|
||||
return err
|
||||
}
|
||||
log.Info("git commit ok. forge done")
|
||||
return nil
|
||||
}
|
||||
|
|
20
doConfig.go
20
doConfig.go
|
@ -1,3 +1,6 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
@ -18,13 +21,16 @@ func doConfig() {
|
|||
log.Info("todo")
|
||||
okExit("")
|
||||
}
|
||||
if argv.Config.Register != "" {
|
||||
if err := doRegister(argv.Config.Register); err == nil {
|
||||
okExit("attempting to register " + argv.Config.Register)
|
||||
} else {
|
||||
badExit(err)
|
||||
|
||||
/*
|
||||
if argv.Config.Register != "" {
|
||||
if err := doRegister(argv.Config.Register); err == nil {
|
||||
okExit("attempting to register " + argv.Config.Register)
|
||||
} else {
|
||||
badExit(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// try to add, then save config and exit
|
||||
if argv.Config.Add != nil {
|
||||
|
@ -48,6 +54,8 @@ func doConfig() {
|
|||
os.Exit(0)
|
||||
}
|
||||
|
||||
log.Info("config.PathLock =", me.forge.Config.PathLock)
|
||||
|
||||
me.forge.ConfigPrintTable()
|
||||
okExit("")
|
||||
}
|
||||
|
|
71
doDirty.go
71
doDirty.go
|
@ -1,69 +1,16 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.wit.com/lib/gui/shell"
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func doDirty() {
|
||||
findAll() // select all the repos
|
||||
doCheckDirtyAndConfigSave()
|
||||
me.found = new(gitpb.Repos)
|
||||
findDirty()
|
||||
found := me.forge.CheckDirty()
|
||||
if found.Len() == 0 {
|
||||
return
|
||||
}
|
||||
if argv.Verbose {
|
||||
me.forge.PrintHumanTableDirty(me.found)
|
||||
me.forge.PrintHumanTableDirty(found)
|
||||
} else {
|
||||
me.forge.PrintHumanTable(me.found)
|
||||
}
|
||||
}
|
||||
|
||||
func straightCheckDirty() int {
|
||||
var count int
|
||||
// var total int
|
||||
// now := time.Now()
|
||||
all := me.found.All()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
// total += 1
|
||||
if repo.IsDirty() {
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
// log.Printf("rill dirty check (%d dirty repos) (%d total repos) took:%s\n", count, total, shell.FormatDuration(time.Since(now)))
|
||||
return count
|
||||
}
|
||||
|
||||
func doCheckDirty(repo *gitpb.Repo) error {
|
||||
repo.CheckDirty()
|
||||
// reset these in here for now
|
||||
if repo.GetTargetVersion() != "" {
|
||||
repo.TargetVersion = ""
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func doCheckDirtyAndConfigSave() {
|
||||
start := straightCheckDirty()
|
||||
// log.Info("before findAll()")
|
||||
/*
|
||||
all := me.found.All()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
repo.CheckDirty()
|
||||
}
|
||||
*/
|
||||
// this might work?
|
||||
now := time.Now()
|
||||
me.forge.RillFuncError(doCheckDirty)
|
||||
end := straightCheckDirty()
|
||||
log.Printf("dirty check (%d dirty repos) (%d total repos) took:%s\n", end, me.found.Len(), shell.FormatDuration(time.Since(now)))
|
||||
|
||||
if start != end {
|
||||
// todo: use internal forgepb configsave flag. should work?
|
||||
me.forge.SetConfigSave(true)
|
||||
me.forge.ConfigSave()
|
||||
me.forge.PrintHumanTable(found)
|
||||
}
|
||||
}
|
||||
|
|
277
doExamine.go
277
doExamine.go
|
@ -1,277 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.wit.com/lib/gui/shell"
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func doExamine() error {
|
||||
me.found = new(gitpb.Repos)
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if tag := repo.ExamineBranches(); tag != nil {
|
||||
me.found.AppendByGoPath(repo)
|
||||
// ctime := tag.Creatordate.AsTime()
|
||||
// dur := time.Since(ctime)
|
||||
// log.Printf("UNKNOWN BRANCH %-50s %s %4s %s\n", repo.GetFullPath(), tag.Hash, shell.FormatDuration(dur), tag.Refname)
|
||||
repo.CurrentTag = tag
|
||||
} else {
|
||||
repo.CurrentTag = nil
|
||||
}
|
||||
}
|
||||
if len(me.found.Repos) == 0 {
|
||||
return nil
|
||||
}
|
||||
// slices.Reverse(me.found.Repos)
|
||||
slices.SortFunc(me.found.Repos, func(a, b *gitpb.Repo) int {
|
||||
atime := a.CurrentTag.Creatordate.AsTime()
|
||||
btime := b.CurrentTag.Creatordate.AsTime()
|
||||
if atime.Before(btime) {
|
||||
// log.Info("atime vs btime is 0", atime, btime)
|
||||
return 1
|
||||
}
|
||||
// log.Info("atime vs btime is 1", atime, btime)
|
||||
return -1
|
||||
})
|
||||
all = me.found.All()
|
||||
me.found = new(gitpb.Repos)
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if isNormal(repo) {
|
||||
continue
|
||||
}
|
||||
|
||||
tag := repo.CurrentTag
|
||||
ctime := tag.Creatordate.AsTime()
|
||||
dur := time.Since(ctime)
|
||||
log.Printf("UNKNOWN BRANCH %-50s %s %4s %s\n", repo.GetFullPath(), tag.Hash, shell.FormatDuration(dur), tag.Refname)
|
||||
err := examineBranch(repo)
|
||||
if argv.Clean.Examine.Fix != nil {
|
||||
if err != nil {
|
||||
badExit(err)
|
||||
}
|
||||
}
|
||||
|
||||
me.found.AppendByGoPath(repo)
|
||||
}
|
||||
me.forge.PrintHumanTableDirty(me.found)
|
||||
return nil
|
||||
}
|
||||
|
||||
func isNormal(repo *gitpb.Repo) bool {
|
||||
if repo.IsDirty() {
|
||||
return true
|
||||
}
|
||||
if repo.GetUserVersion() != repo.GetDevelVersion() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func examineBranch(repo *gitpb.Repo) error {
|
||||
if !isLocal(repo) {
|
||||
base := filepath.Base(repo.CurrentTag.Refname)
|
||||
if base == repo.GetUserBranchName() {
|
||||
log.Info("The user branch also has a remote branch", repo.CurrentTag.Refname)
|
||||
log.Info("TODO: verify the remote branch is out of date", repo.CurrentTag.Refname)
|
||||
log.Info("TODO: delete the remote branch", repo.CurrentTag.Refname)
|
||||
return nil
|
||||
}
|
||||
|
||||
if repo.Exists(filepath.Join(".git/refs/heads", base)) {
|
||||
repo.RunVerbose([]string{"ls", "-l", ".git/refs/remotes/origin"})
|
||||
repo.RunVerbose([]string{"cat", filepath.Join(".git/refs/remotes/origin", base)})
|
||||
repo.RunVerbose([]string{"cat", filepath.Join(".git/refs/heads", base)})
|
||||
log.Info("why is this local branch a problem?", repo.CurrentTag.Refname)
|
||||
} else {
|
||||
repo.RunVerbose([]string{"ls", "-l", ".git/refs/remotes/origin"})
|
||||
log.Info("why is this non-local branch a problem?", repo.CurrentTag.Refname)
|
||||
r, err := repo.RunVerbose([]string{"cat", filepath.Join(".git/refs/remotes/origin", base)})
|
||||
if err == nil {
|
||||
cmd := []string{"git", "show", "-s", "--format=\"%H %ae %as %s\"", r.Stdout[0]}
|
||||
repo.RunVerbose(cmd)
|
||||
log.Info(cmd)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("%s repo.CurrentTag is not local: %s. Don't proceed yet", repo.GetGoPath(), repo.CurrentTag.Refname)
|
||||
}
|
||||
dcount, err := showNotMaster(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if repo.CurrentTag == nil {
|
||||
return fmt.Errorf("repo.CurrentTag == nil")
|
||||
}
|
||||
|
||||
userbranch := repo.GetUserBranchName()
|
||||
|
||||
if repo.CurrentTag.Refname == userbranch {
|
||||
return requiresGitPush(repo, userbranch)
|
||||
}
|
||||
|
||||
if repo.CurrentTag.Refname == "origin/"+userbranch {
|
||||
return requiresGitPush(repo, userbranch)
|
||||
}
|
||||
|
||||
if len(dcount) == 0 {
|
||||
if repo.CurrentTag.Refname == repo.GetMasterBranchName() {
|
||||
err = fmt.Errorf("examineBranch() SPECIAL CASE. %s is the master branch", repo.CurrentTag.Refname)
|
||||
log.Info(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
err = fmt.Errorf("examineBranch() branch differs. patch diff len == 0. PROBABLY DELETE BRANCH %s", repo.CurrentTag.Refname)
|
||||
log.Info(err)
|
||||
cmd := repo.ConstructGitDiffLog(repo.CurrentTag.Refname, repo.GetMasterBranchName())
|
||||
if argv.Clean.Examine.Fix == nil {
|
||||
log.Info(repo.GetGoPath(), cmd)
|
||||
} else {
|
||||
if _, err := repo.RunVerbose(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
cmd = repo.ConstructGitDiffLog(repo.GetMasterBranchName(), repo.CurrentTag.Refname)
|
||||
if argv.Clean.Examine.Fix == nil {
|
||||
log.Info(repo.GetGoPath(), cmd)
|
||||
} else {
|
||||
if _, err := repo.RunVerbose(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
cmd = []string{"git", "branch", "-D", repo.CurrentTag.Refname}
|
||||
log.Info(repo.GetGoPath(), "TRY THIS:", cmd)
|
||||
if argv.Clean.Examine.Fix == nil {
|
||||
log.Info(repo.GetGoPath(), "TODO: CHECK REMOTE BRANCH DOES NOT EXIST", repo.CurrentTag.Refname)
|
||||
repo.RunVerbose([]string{"ls", "-l", ".git/refs/remotes/origin"})
|
||||
} else {
|
||||
log.Info(repo.GetGoPath(), "TODO: CHECK REMOTE BRANCH DOES NOT EXIST", repo.CurrentTag.Refname)
|
||||
if _, err := repo.RunVerbose(cmd); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
err = fmt.Errorf("examineBranch() branch differs, but not sure how to fix it yet == %d", len(dcount))
|
||||
log.Info(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkJcarr(repo *gitpb.Repo) int {
|
||||
b1 := countDiffObjects(repo, "jcarr", "origin/jcarr")
|
||||
b2 := countDiffObjects(repo, "origin/jcarr", "jcarr")
|
||||
log.Info("jcarr vs origin count", b1, b2)
|
||||
if b1 == 0 && b2 == 0 {
|
||||
return 0
|
||||
}
|
||||
if b1 != 0 {
|
||||
log.Info("jcarr vs origin count b1 != 0, b2 ==", b1, b2)
|
||||
log.Info("THIS MEANS THE LOCAL BRANCH NEEDS GIT PUSH TO ORIGIN BRANCH ==", b1, b2)
|
||||
return b1
|
||||
}
|
||||
if b2 != 0 {
|
||||
log.Info("jcarr vs origin count b2 != 0, b1 ==", b2, b1)
|
||||
return b2
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func isLocal(repo *gitpb.Repo) bool {
|
||||
base, name := filepath.Split(repo.CurrentTag.Refname)
|
||||
if name == "" {
|
||||
return false
|
||||
}
|
||||
if base == "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func showNotMaster(repo *gitpb.Repo) ([]string, error) {
|
||||
var cmd []string
|
||||
cmd = append(cmd, "git")
|
||||
cmd = append(cmd, "log")
|
||||
cmd = append(cmd, "--format=\"%H\"")
|
||||
cmd = append(cmd, repo.CurrentTag.Hash)
|
||||
cmd = append(cmd, "--not")
|
||||
cmd = append(cmd, repo.GetMasterBranchName())
|
||||
r, err := repo.RunVerboseOnError(cmd)
|
||||
return r.Stdout, err
|
||||
}
|
||||
|
||||
func showNotDevel(repo *gitpb.Repo) ([]string, error) {
|
||||
var cmd []string
|
||||
cmd = append(cmd, "git")
|
||||
cmd = append(cmd, "log")
|
||||
cmd = append(cmd, "--format=\"%H\"")
|
||||
cmd = append(cmd, repo.CurrentTag.Hash)
|
||||
cmd = append(cmd, "--not")
|
||||
cmd = append(cmd, "devel")
|
||||
r, err := repo.RunVerboseOnError(cmd)
|
||||
return r.Stdout, err
|
||||
}
|
||||
|
||||
// count all objects only in branch1
|
||||
func countDiffObjects(repo *gitpb.Repo, branch1, branch2 string) int {
|
||||
cmd := repo.ConstructGitDiffLog(branch1, branch2)
|
||||
r, err := repo.RunVerboseOnError(cmd)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
log.Info("countDiffObjects()", cmd, len(r.Stdout), strings.Join(r.Stdout, " "))
|
||||
return len(r.Stdout)
|
||||
}
|
||||
|
||||
/*
|
||||
func constructGitDiffLog(repo *gitpb.Repo, branch1, branch2 string) []string {
|
||||
var cmd []string
|
||||
cmd = append(cmd, "git")
|
||||
cmd = append(cmd, "log")
|
||||
cmd = append(cmd, "--format=\"%H\"")
|
||||
cmd = append(cmd, branch1)
|
||||
cmd = append(cmd, "--not")
|
||||
cmd = append(cmd, branch2)
|
||||
return cmd
|
||||
}
|
||||
*/
|
||||
|
||||
// count all objects only in branch1
|
||||
func gitPushStrict(repo *gitpb.Repo, branchName string) error {
|
||||
var cmd []string
|
||||
cmd = append(cmd, "git")
|
||||
cmd = append(cmd, "push")
|
||||
_, err := repo.RunVerbose(cmd)
|
||||
if err != nil {
|
||||
cmd = []string{"git", "whatchanged", repo.CurrentTag.Hash, "-1"}
|
||||
repo.RunVerbose(cmd)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func requiresGitPush(repo *gitpb.Repo, branchName string) error {
|
||||
b1 := countDiffObjects(repo, branchName, "origin/"+branchName)
|
||||
b2 := countDiffObjects(repo, "origin/"+branchName, branchName)
|
||||
log.Info(branchName, "vs origin count", b1, b2)
|
||||
if b1 == 0 && b2 == 0 {
|
||||
return nil
|
||||
}
|
||||
if b1 != 0 {
|
||||
log.Info(branchName, "vs origin count b1 != 0, b2 ==", b1, b2)
|
||||
log.Info("THIS MEANS THE LOCAL BRANCH NEEDS GIT PUSH TO ORIGIN BRANCH ==", b1)
|
||||
if argv.Clean.Examine.Fix != nil {
|
||||
return gitPushStrict(repo, branchName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
)
|
||||
|
||||
// this populates a slice of protobuf records representing each git repo
|
||||
// var found []*gitpb.Repo
|
||||
//
|
||||
// so, it makes a subset of repos that are then used performing actions on
|
||||
//
|
||||
// by default, it adds every repo
|
||||
|
||||
func doFind() *gitpb.Repos {
|
||||
if argv.List == nil {
|
||||
return findAll()
|
||||
}
|
||||
|
||||
if argv.List.Mine {
|
||||
return findMine()
|
||||
}
|
||||
|
||||
if argv.List.Dirty {
|
||||
return me.forge.FindDirty()
|
||||
}
|
||||
|
||||
return findAll()
|
||||
}
|
||||
|
||||
func (f *FindCmd) findRepos() *gitpb.Repos {
|
||||
if f == nil {
|
||||
return findMine()
|
||||
}
|
||||
|
||||
if f.All {
|
||||
return findAll()
|
||||
}
|
||||
|
||||
if f.Private {
|
||||
return findPrivate()
|
||||
}
|
||||
|
||||
if f.Mine {
|
||||
return findMine()
|
||||
}
|
||||
|
||||
if f.Favorites {
|
||||
return findFavorites()
|
||||
}
|
||||
|
||||
if f.Dirty {
|
||||
return me.forge.FindDirty()
|
||||
}
|
||||
|
||||
if f.User {
|
||||
return findUser()
|
||||
}
|
||||
|
||||
return findAll()
|
||||
}
|
||||
|
||||
func findPrivate() *gitpb.Repos {
|
||||
found := gitpb.NewRepos()
|
||||
for repo := range me.forge.Repos.IterByFullPath() {
|
||||
if me.forge.Config.IsPrivate(repo.GetGoPath()) {
|
||||
found.AppendByFullPath(repo)
|
||||
}
|
||||
}
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
// finds repos that are writable
|
||||
func findMine() *gitpb.Repos {
|
||||
found := gitpb.NewRepos()
|
||||
|
||||
// log.Printf("get mine %s\n", me.forge.GetGoSrc())
|
||||
for repo := range me.forge.Repos.IterByFullPath() {
|
||||
|
||||
if me.forge.Config.IsWritable(repo.GetGoPath()) {
|
||||
found.AppendByFullPath(repo)
|
||||
}
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
// finds repos the user has marked as favorites in the forge .config
|
||||
func findFavorites() *gitpb.Repos {
|
||||
found := gitpb.NewRepos()
|
||||
|
||||
// log.Printf("get favorites %s\n", me.forge.GetGoSrc())
|
||||
for repo := range me.forge.Repos.IterByFullPath() {
|
||||
|
||||
if me.forge.Config.IsFavorite(repo.GetGoPath()) {
|
||||
found.AppendByFullPath(repo)
|
||||
}
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
func findAll() *gitpb.Repos {
|
||||
found := gitpb.NewRepos()
|
||||
for repo := range me.forge.Repos.IterByFullPath() {
|
||||
found.AppendByFullPath(repo)
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
func find50() *gitpb.Repos {
|
||||
count := 0
|
||||
found := gitpb.NewRepos()
|
||||
for repo := range me.forge.Repos.IterByFullPath() {
|
||||
found.AppendByFullPath(repo)
|
||||
if count > 50 {
|
||||
return found
|
||||
}
|
||||
count += 1
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
func findUser() *gitpb.Repos {
|
||||
found := gitpb.NewRepos()
|
||||
|
||||
for repo := range me.forge.Repos.IterByFullPath() {
|
||||
if repo.GetCurrentBranchName() == repo.GetUserBranchName() {
|
||||
found.AppendByFullPath(repo)
|
||||
}
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
func findPublishable() *gitpb.Repos {
|
||||
found := gitpb.NewRepos()
|
||||
|
||||
for repo := range me.forge.Repos.IterByFullPath() {
|
||||
if repo.GetTargetVersion() == "" {
|
||||
continue
|
||||
}
|
||||
found.AppendByFullPath(repo)
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
func findReposWithPatches() *gitpb.Repos {
|
||||
found := gitpb.NewRepos()
|
||||
|
||||
for repo := range me.forge.Repos.IterByFullPath() {
|
||||
if repo.GetTargetVersion() != "" {
|
||||
// add everything that has a target version set
|
||||
found.AppendByFullPath(repo)
|
||||
continue
|
||||
}
|
||||
if repo.IsDirty() {
|
||||
// always add dirty branches
|
||||
found.AppendByFullPath(repo)
|
||||
continue
|
||||
}
|
||||
if repo.GetUserVersion() == "" || repo.GetUserVersion() == "uerr" {
|
||||
// skip anything without a user branch
|
||||
continue
|
||||
}
|
||||
if repo.GetUserVersion() != repo.GetDevelVersion() {
|
||||
found.AppendByFullPath(repo)
|
||||
continue
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// ignore read-only repos for checks below here
|
||||
if me.forge.Config.IsReadOnly(repo.GetGoPath()) {
|
||||
continue
|
||||
}
|
||||
|
||||
// this is an old test to see if the current 'last tag' is accurate and should be removed
|
||||
if repo.GetLastTag() != repo.GetMasterVersion() {
|
||||
found.AppendByFullPath(repo)
|
||||
repo.FindLastTag()
|
||||
continue
|
||||
}
|
||||
}
|
||||
return found
|
||||
}
|
440
doGui.go
440
doGui.go
|
@ -1,96 +1,55 @@
|
|||
// 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"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"go.wit.com/gui"
|
||||
"go.wit.com/lib/debugger"
|
||||
"go.wit.com/lib/gadgets"
|
||||
"go.wit.com/lib/gui/shell"
|
||||
"go.wit.com/lib/protobuf/forgepb"
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func debug() {
|
||||
log.Info("cmd line --debugger == true")
|
||||
go func() {
|
||||
log.Sleep(3)
|
||||
log.Info("cmd line --debugger == true")
|
||||
log.Info("cmd line --debugger == true")
|
||||
log.Info("cmd line --debugger == true")
|
||||
log.Info("cmd line --debugger == true")
|
||||
debugger.DebugWindow()
|
||||
}()
|
||||
}
|
||||
|
||||
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
|
||||
me.myGui.Default()
|
||||
|
||||
// debug()
|
||||
|
||||
me.patchWin = new(patchesWindow)
|
||||
|
||||
me.mainWindow = gadgets.RawBasicWindow("Forge: (this kinda works sometimes)")
|
||||
me.mainWindow.Make()
|
||||
me.mainWindow.Show()
|
||||
me.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")
|
||||
count := me.forge.RillReload()
|
||||
log.Info("Repo Reload count =", count)
|
||||
if count != 0 {
|
||||
me.forge.ConfigSave()
|
||||
okExit("")
|
||||
}
|
||||
|
||||
grid := win.Group.RawGrid()
|
||||
if me.forge.Config.GetPathLock() {
|
||||
me.goSrcPwd = gadgets.NewOneLiner(grid, "Working Directory")
|
||||
me.goSrcPwd.SetText(me.forge.Config.ReposDir)
|
||||
} else {
|
||||
me.goSrcEdit = gadgets.NewBasicEntry(grid, "Working Directory")
|
||||
me.goSrcEdit.SetText(me.forge.Config.ReposDir)
|
||||
me.goSrcEdit.Custom = func() {
|
||||
log.Info("updating text to", me.goSrcEdit.String())
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
lockpath := grid.NewCheckbox("Lock").SetChecked(me.forge.Config.PathLock)
|
||||
lockpath.Custom = func() {
|
||||
if lockpath.IsChecked() {
|
||||
log.Info("lock working directory")
|
||||
me.forge.Config.PathLock = true
|
||||
} else {
|
||||
log.Info("unlock working directory")
|
||||
me.forge.Config.PathLock = false
|
||||
}
|
||||
me.forge.Config.ConfigSave()
|
||||
okExit("you must restart forge after changing the Path Lock")
|
||||
}
|
||||
|
||||
me.mainbox = me.mainWindow.Box()
|
||||
|
||||
// disable the interface while everything is scanned
|
||||
me.Disable()
|
||||
|
||||
vbox2 := me.mainbox.NewVerticalBox("BOX2")
|
||||
globalBuildOptions(vbox2)
|
||||
|
||||
me.repos = makeRepoView()
|
||||
|
||||
me.Enable()
|
||||
|
||||
// intermittently scans the status indefinitly
|
||||
me.repos.View.Watchdog(func() {
|
||||
log.Info("Watchdog in doGui()")
|
||||
// processing is done. update the repo summary box
|
||||
// me.summary.Update()
|
||||
})
|
||||
}
|
||||
|
||||
func globalBuildOptions(vbox *gui.Node) {
|
||||
group1 := vbox.NewGroup("Forge Settings")
|
||||
grid := group1.NewGrid("buildOptions", 0, 0)
|
||||
|
||||
// me.autoWorkingPwd = gadgets.NewOneLiner(grid, "working directory (pwd)")
|
||||
me.userHomePwd = gadgets.NewOneLiner(grid, "user home")
|
||||
grid.NextRow()
|
||||
me.goSrcPwd = gadgets.NewOneLiner(grid, "go src home")
|
||||
grid.NextRow()
|
||||
|
||||
usr, _ := user.Current()
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
log.Warn("Error getting home directory:", err)
|
||||
homeDir = "/home/autotypist"
|
||||
}
|
||||
me.userHomePwd.SetText(homeDir)
|
||||
srcDir := filepath.Join(homeDir, "go/src")
|
||||
me.goSrcPwd.SetText(srcDir)
|
||||
|
||||
// use ENV GIT_AUTHOR
|
||||
me.gitAuthor = gadgets.NewOneLiner(grid, "Git Author")
|
||||
|
@ -104,49 +63,300 @@ func globalBuildOptions(vbox *gui.Node) {
|
|||
me.gitAuthor.SetText(author)
|
||||
}
|
||||
|
||||
// select the branch you want to test, build and develop against
|
||||
// this lets you select your user branch, but, when you are happy
|
||||
// you can merge everything into the devel branch and make sure it actually
|
||||
// works. Then, when that is good, merge and version everything in master
|
||||
me.setBranchB = grid.NewButton("set current branches to:", func() {
|
||||
targetName := me.newBranch.String()
|
||||
log.Warn("setting all branches to", targetName)
|
||||
if targetName == "devel" {
|
||||
if err := doAllCheckoutDevel(); err != nil {
|
||||
log.Info("switching to devel branches failed")
|
||||
}
|
||||
return
|
||||
}
|
||||
if targetName == "master" {
|
||||
if err := doAllCheckoutMaster(); err != nil {
|
||||
log.Info("switching to master branches failed")
|
||||
}
|
||||
return
|
||||
}
|
||||
// just assume user
|
||||
if err := doAllCheckoutUser(); err != nil {
|
||||
log.Info("switching to user branches failed")
|
||||
}
|
||||
})
|
||||
me.newBranch = grid.NewCombobox()
|
||||
me.newBranch.AddText("master")
|
||||
me.newBranch.AddText("devel")
|
||||
me.newBranch.AddText(usr.Username)
|
||||
me.newBranch.SetText(usr.Username)
|
||||
|
||||
// checking this will automatically make the branches off of devel
|
||||
me.autoCreateBranches = grid.NewCheckbox("create if missing").SetChecked(true)
|
||||
grid.NextRow()
|
||||
|
||||
grid.NewButton("Repo Window", func() {
|
||||
if me.repos.Hidden() {
|
||||
me.repos.Show()
|
||||
} else {
|
||||
me.repos.Hide()
|
||||
gridM := win.Stack.RawGrid()
|
||||
|
||||
var insertWin *gadgets.GenericWindow
|
||||
s := fmt.Sprintf("Repos (%d)", me.forge.Repos.Len())
|
||||
me.reposWinB = gridM.NewButton(s, func() {
|
||||
// if the window exists, just toggle it open or closed
|
||||
if insertWin != nil {
|
||||
insertWin.Toggle()
|
||||
return
|
||||
}
|
||||
|
||||
insertWin = makeReposWinNew()
|
||||
})
|
||||
grid.NewButton("Patches Window", func() {
|
||||
me.patchWin.once.Do(me.patchWin.initWindow)
|
||||
me.patchWin.Toggle()
|
||||
|
||||
var patchesWin *stdPatchTableWin
|
||||
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)
|
||||
|
||||
all := me.forge.Patchsets.All()
|
||||
for all.Scan() {
|
||||
pset := all.Next()
|
||||
if pset.State == "DONE" {
|
||||
// skip old patchsets
|
||||
continue
|
||||
}
|
||||
AddAllPatches(notdone, pset, false)
|
||||
// AddNotDonePatches(notdone, pset, false)
|
||||
}
|
||||
notdone.PrintTable()
|
||||
|
||||
patchesWin = makePatchesWin(notdone)
|
||||
})
|
||||
|
||||
var pubWin *gadgets.GenericWindow
|
||||
gridM.NewButton("Publish", func() {
|
||||
if pubWin != nil {
|
||||
pubWin.Toggle()
|
||||
return
|
||||
}
|
||||
pubWin = makePublishWindow()
|
||||
})
|
||||
|
||||
var oldWin *gadgets.GenericWindow
|
||||
gridM.NewButton("old", func() {
|
||||
if oldWin != nil {
|
||||
oldWin.Toggle()
|
||||
return
|
||||
}
|
||||
oldWin = makeOldStuff()
|
||||
})
|
||||
}
|
||||
|
||||
// this is the magic that generates a window directly from the protocol buffer
|
||||
func makeStandardReposGrid(pb *gitpb.Repos) *gitpb.ReposTable {
|
||||
t := pb.NewTable("testDirty")
|
||||
t.NewUuid()
|
||||
sf := t.AddStringFunc("repo", func(r *gitpb.Repo) string {
|
||||
return r.GetNamespace()
|
||||
})
|
||||
// t.Custom = func() {
|
||||
// log.Info("close grid?")
|
||||
// }
|
||||
sf.Custom = func(r *gitpb.Repo) {
|
||||
log.Info("do button click on", r.GetNamespace())
|
||||
}
|
||||
t.AddTimeFunc("age", func(repo *gitpb.Repo) time.Time {
|
||||
return repo.NewestTime()
|
||||
})
|
||||
t.AddMasterVersion()
|
||||
t.AddDevelVersion()
|
||||
t.AddUserVersion()
|
||||
t.AddCurrentBranchName()
|
||||
t.AddState()
|
||||
return t
|
||||
}
|
||||
|
||||
// this is the magic that generates a window directly from the protocol buffer
|
||||
func makeStandardReposWindow(title string, pb *gitpb.Repos) (*gitpb.ReposTable, *gui.Node) {
|
||||
win := gadgets.RawBasicWindow(title)
|
||||
win.Make()
|
||||
win.Show()
|
||||
win.Custom = func() {
|
||||
// sets the hidden flag to false so Toggle() works
|
||||
win.Hide()
|
||||
}
|
||||
box := win.Box().NewBox("bw vbox", false)
|
||||
|
||||
t := makeStandardReposGrid(pb)
|
||||
t.SetParent(box)
|
||||
t.ShowTable()
|
||||
return t, box
|
||||
}
|
||||
|
||||
func findMergeToDevel() *gitpb.Repos {
|
||||
found := gitpb.NewRepos()
|
||||
|
||||
for repo := range me.forge.Repos.IterByFullPath() {
|
||||
// this sees if user has patches for devel. If it does, add it to found
|
||||
if repo.CountDiffObjects(repo.GetUserBranchName(), repo.GetDevelBranchName()) > 0 {
|
||||
found.AppendByFullPath(repo)
|
||||
}
|
||||
}
|
||||
now := time.Now()
|
||||
if found.Len() == 0 {
|
||||
log.Info("nothing to merge with devel")
|
||||
return found
|
||||
}
|
||||
// me.forge.PrintHumanTable(found)
|
||||
|
||||
// check for merges from devel
|
||||
total, count, nope, _ := me.forge.IsEverythingOnDevel()
|
||||
log.Printf("devel branch check. %d total repos. (%d ok) (%d not on devel branch) (%s)\n", total, count, nope, shell.FormatDuration(time.Since(now)))
|
||||
return found
|
||||
}
|
||||
|
||||
func findMergeToMaster() *gitpb.Repos {
|
||||
found := new(gitpb.Repos)
|
||||
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
|
||||
if me.forge.Config.IsReadOnly(repo.GetNamespace()) {
|
||||
continue
|
||||
}
|
||||
/*
|
||||
if repo.IsDirty() {
|
||||
continue
|
||||
}
|
||||
if repo.GetMasterVersion() != repo.GetDevelVersion() {
|
||||
me.found.AppendByFullPath(repo)
|
||||
continue
|
||||
}
|
||||
*/
|
||||
|
||||
// this sees if devel is behind master. IT SHOULD NOT BE
|
||||
if repo.CountDiffObjects(repo.GetMasterBranchName(), repo.GetDevelBranchName()) == 0 {
|
||||
// everything is normal
|
||||
} else {
|
||||
repo.State = "DEVEL < MASTER"
|
||||
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
|
||||
if repo.CountDiffObjects(repo.GetDevelBranchName(), repo.GetMasterBranchName()) > 0 {
|
||||
found.AppendByFullPath(repo)
|
||||
}
|
||||
}
|
||||
now := time.Now()
|
||||
if found.Len() == 0 {
|
||||
log.Info("nothing to merge with master")
|
||||
return found
|
||||
}
|
||||
me.forge.PrintHumanTable(found)
|
||||
|
||||
// check for merges from devel
|
||||
total, count, nope, _ := me.forge.IsEverythingOnMaster()
|
||||
log.Printf("Master branch check. %d total repos. (%d ok) (%d not on master branch) (%s)\n", total, count, nope, shell.FormatDuration(time.Since(now)))
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
func mergeDevelToMaster(doit bool) {
|
||||
found := findMergeToMaster()
|
||||
|
||||
if !doit {
|
||||
return
|
||||
}
|
||||
|
||||
all := found.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
log.Info("repo:", repo.GetNamespace())
|
||||
if result, err := repo.MergeToMaster(); err == nil {
|
||||
log.Warn("THINGS SEEM OK", repo.GetFullPath())
|
||||
for _, line := range result.Stdout {
|
||||
log.Warn("stdout:", line)
|
||||
}
|
||||
for _, line := range result.Stderr {
|
||||
log.Warn("stderr:", line)
|
||||
}
|
||||
} else {
|
||||
log.Warn("THINGS FAILED ", repo.GetFullPath())
|
||||
log.Warn("err", err)
|
||||
if result == nil {
|
||||
break
|
||||
}
|
||||
for _, line := range result.Stdout {
|
||||
log.Warn("stdout:", line)
|
||||
}
|
||||
for _, line := range result.Stderr {
|
||||
log.Warn("stderr:", line)
|
||||
}
|
||||
log.Warn("THINGS FAILED ", repo.GetFullPath())
|
||||
break
|
||||
}
|
||||
me.forge.SetConfigSave(true)
|
||||
// view.Update()
|
||||
}
|
||||
me.forge.ConfigSave()
|
||||
}
|
||||
|
||||
func mergeUserToDevel(doit bool) {
|
||||
found := findMergeToDevel()
|
||||
|
||||
if !doit {
|
||||
return
|
||||
}
|
||||
|
||||
all := found.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
bruser := repo.GetUserBranchName()
|
||||
brdevel := repo.GetDevelBranchName()
|
||||
|
||||
if repo.GetUserVersion() == "uerr" {
|
||||
// no user branch
|
||||
return
|
||||
}
|
||||
log.Info("trying", bruser, repo.GetUserVersion())
|
||||
|
||||
b1 := repo.CountDiffObjects(bruser, brdevel) // should be zero
|
||||
if b1 == 0 {
|
||||
// log.Info("User is already merged into Devel", repo.GetNamespace(), cmd)
|
||||
return
|
||||
}
|
||||
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 {
|
||||
log.Warn("stdout:", line)
|
||||
}
|
||||
for _, line := range result.Stderr {
|
||||
log.Warn("stderr:", line)
|
||||
}
|
||||
} else {
|
||||
log.Warn("THINGS FAILED ", repo.GetFullPath())
|
||||
log.Warn("err", err)
|
||||
if result == nil {
|
||||
break
|
||||
}
|
||||
for _, line := range result.Stdout {
|
||||
log.Warn("stdout:", line)
|
||||
}
|
||||
for _, line := range result.Stderr {
|
||||
log.Warn("stderr:", line)
|
||||
}
|
||||
break
|
||||
}
|
||||
me.forge.SetConfigSave(true)
|
||||
// view.Update()
|
||||
}
|
||||
me.forge.ConfigSave()
|
||||
}
|
||||
|
||||
// old things before they are removed, deprecated, fixed, etc
|
||||
func makeOldStuff() *gadgets.GenericWindow {
|
||||
oldWin := gadgets.NewGenericWindow("old code", "old code on it's way out")
|
||||
|
||||
grid := oldWin.Group.RawGrid()
|
||||
|
||||
// var reposWin *gadgets.GenericWindow
|
||||
var reposWin *stdReposTableWin
|
||||
grid.NewButton("Fix Repos", func() {
|
||||
if reposWin != nil {
|
||||
reposWin.Toggle()
|
||||
return
|
||||
}
|
||||
reposWin = makeReposWin()
|
||||
})
|
||||
|
||||
var howtoWin *gadgets.GenericWindow
|
||||
grid.NewButton("Tutorial", func() {
|
||||
if howtoWin != nil {
|
||||
howtoWin.Toggle()
|
||||
return
|
||||
}
|
||||
howtoWin = makeHowtoWin()
|
||||
})
|
||||
|
||||
grid.NextRow()
|
||||
grid.NewLabel("")
|
||||
|
||||
return oldWin
|
||||
}
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"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.GetFullPath())
|
||||
continue
|
||||
}
|
||||
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)
|
||||
}
|
||||
done.Append(repo)
|
||||
config.SetChanged("repos", 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() {
|
||||
if repo.CheckDirty() {
|
||||
log.Info("repo is dirty", repo.GetGoPath())
|
||||
continue
|
||||
}
|
||||
|
||||
log.Info("Starting merge on", repo.GetGoPath())
|
||||
if repo.CheckoutMaster() {
|
||||
log.Info("checkout devel failed", repo.GetGoPath())
|
||||
err = log.Errorf("checkout devel failed")
|
||||
badExit(err)
|
||||
}
|
||||
|
||||
if _, err := repo.MergeToMaster(); 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)
|
||||
}
|
||||
|
||||
done.Append(repo)
|
||||
config.SetChanged("repos", true)
|
||||
}
|
||||
return done, err
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
// 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 (
|
||||
"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"
|
||||
)
|
||||
|
||||
func doNormal() bool {
|
||||
me.forge.CheckDirtyQuiet()
|
||||
|
||||
var count int
|
||||
stats := me.forge.RillRepos(checkNormalRepoState)
|
||||
for path, stat := range stats {
|
||||
dur := stat.End.Sub(stat.Start)
|
||||
if dur > 10*time.Second {
|
||||
log.Infof("%s checkNormalRepoState() took a long time (%s)\n", path, shell.FormatDuration(dur))
|
||||
}
|
||||
if stat.Err == nil {
|
||||
continue
|
||||
}
|
||||
// log.Infof("%-60s, %-60s %v %s\n", stat.Start, stat.End.String(), dur, path)
|
||||
// log.Infof("%-30v %s %v\n", dur, path, stat.Err)
|
||||
// log.Info("got path", path, stat.Err)
|
||||
count += 1
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// 99% of the time, the repos during development should be on your user branch.
|
||||
// error out if it's not
|
||||
// this checks to see if a devel and user branch exist
|
||||
// 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)
|
||||
log.Info("ABNORMAL: master branch name was blank in", repo.GetFullPath())
|
||||
}
|
||||
if repo.GetMasterBranchName() == "" {
|
||||
me.forge.VerifyBranchNames(repo)
|
||||
err = log.Errorf("master branch name blank")
|
||||
}
|
||||
if repo.GetDevelBranchName() == "" {
|
||||
me.forge.VerifyBranchNames(repo)
|
||||
err = log.Errorf("devel branch name blank")
|
||||
}
|
||||
if repo.GetUserBranchName() == "" {
|
||||
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() {
|
||||
log.Infof("changing to user(%s) branch: %s\n", repo.GetUserBranchName(), repo.FullPath)
|
||||
repo.CheckoutUser()
|
||||
repo.ReloadCheck()
|
||||
err = log.Errorf("now on user branch")
|
||||
}
|
||||
|
||||
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
|
||||
}
|
|
@ -0,0 +1,261 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
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 {
|
||||
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)
|
||||
if err := me.forge.SavePatchsets(); err != nil {
|
||||
log.Warn("savePatchsets() failed", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.Repos != nil {
|
||||
dumpWorkRepos()
|
||||
return nil
|
||||
}
|
||||
|
||||
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")
|
||||
newpb.PrintTable()
|
||||
me.forge.Patchsets = newpb
|
||||
me.forge.SavePatchsets()
|
||||
return nil
|
||||
}
|
||||
|
||||
if argv.Patch.Check != nil {
|
||||
/*
|
||||
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() {
|
||||
pset.PrintTable()
|
||||
for patch := range pset.Patches.IterAll() {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
if err := me.forge.SavePatchsets(); err != nil {
|
||||
log.Warn("savePatchsets() failed", err)
|
||||
}
|
||||
}
|
||||
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")
|
||||
}
|
||||
|
||||
// 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()
|
||||
dumpWorkRepos()
|
||||
return 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 dumpWorkRepos() 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.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.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())
|
||||
return false
|
||||
} else {
|
||||
log.Printf("Patchset Name: %-24s Author: %s <%s> IS GOOD\n", pset.Name, pset.GetGitAuthorName(), pset.GetGitAuthorEmail())
|
||||
}
|
||||
|
||||
var count int
|
||||
var bad int
|
||||
all := pset.Patches.SortByFilename()
|
||||
for all.Scan() {
|
||||
p := all.Next()
|
||||
if IsValidPatch(p) {
|
||||
// ok
|
||||
} else {
|
||||
pset.State = "BROKEN"
|
||||
bad += 1
|
||||
}
|
||||
count += 1
|
||||
}
|
||||
log.Info("pset has", count, "total patches, ", bad, "bad patches")
|
||||
if bad == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsValidPatch(p *forgepb.Patch) bool {
|
||||
basepath, filename := filepath.Split(p.Filename)
|
||||
repo := me.forge.FindByGoPath(basepath)
|
||||
if argv.Verbose {
|
||||
log.Info("start:", p.StartHash, "end:", p.CommitHash, "file:", basepath, filename, "devel version", repo.GetDevelVersion())
|
||||
}
|
||||
if repo == nil {
|
||||
log.Info("can not apply patch! repo not found", basepath, filename)
|
||||
return false
|
||||
}
|
||||
if repo.ActualDevelHash() != p.StartHash {
|
||||
log.Info("can not apply patch! devel hash mismatch", basepath, filename)
|
||||
return false
|
||||
}
|
||||
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 {
|
||||
log.Info("\t", line)
|
||||
}
|
||||
return true
|
||||
}
|
96
doPull.go
96
doPull.go
|
@ -1,13 +1,64 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"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
|
||||
|
@ -30,54 +81,14 @@ func rillPull(repo *gitpb.Repo) error {
|
|||
|
||||
var cmd []string
|
||||
cmd = append(cmd, "git", "pull")
|
||||
_, err := repo.RunVerbose(cmd)
|
||||
err := repo.RunVerbose(cmd)
|
||||
if err != nil {
|
||||
log.Info(repo.GetFullPath(), "git pull err:", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// is every repo on the devel branch?
|
||||
|
||||
func doGitPullNew() {
|
||||
now := time.Now()
|
||||
me.forge.RillFuncError(rillPull)
|
||||
count := me.forge.RillReload()
|
||||
if count != 0 {
|
||||
me.forge.ConfigSave()
|
||||
}
|
||||
|
||||
total, count, nope, _ := IsEverythingOnMaster()
|
||||
log.Printf("Master branch check. %d total repos. (%d git pulled) (%d not on master branch) (%s)\n", total, count, nope, shell.FormatDuration(time.Since(now)))
|
||||
}
|
||||
|
||||
/*
|
||||
func doGitPull() {
|
||||
allerr := me.found.RillGitPull(40, 5)
|
||||
|
||||
all := me.found.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
result := allerr[repo]
|
||||
if result.Error == gitpb.ErrorGitPullOnDirty {
|
||||
log.Info("skip git pull. repo is dirty", repo.GetGoPath())
|
||||
continue
|
||||
}
|
||||
if result.Error == gitpb.ErrorGitPullOnLocal {
|
||||
log.Info("skip git pull. local branch ", repo.GetGoPath())
|
||||
continue
|
||||
}
|
||||
if result.Exit == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Info("git pull error:", repo.GetGoPath(), result.Error)
|
||||
log.Info("git pull error:", repo.GetGoPath(), result.Stdout)
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
// git fetch origin master:master
|
||||
func rillFetchMaster(repo *gitpb.Repo) error {
|
||||
if repo.GetCurrentBranchName() != repo.GetUserBranchName() {
|
||||
|
@ -86,7 +97,7 @@ func rillFetchMaster(repo *gitpb.Repo) error {
|
|||
}
|
||||
branch := repo.GetMasterBranchName()
|
||||
cmd := []string{"git", "fetch", "origin", branch + ":" + branch}
|
||||
_, err := repo.RunVerbose(cmd)
|
||||
err := repo.RunVerbose(cmd)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -97,3 +108,4 @@ func doGitFetch() {
|
|||
me.forge.ConfigSave()
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
// trys to figure out if there is still something to update
|
||||
|
||||
/*
|
||||
func doSync() error {
|
||||
if argv.Pull.Sync.Clean != nil {
|
||||
return doSyncClean()
|
||||
}
|
||||
if argv.Pull.Sync.User != nil {
|
||||
return doSyncUser()
|
||||
}
|
||||
|
||||
return fmt.Errorf("nothing to do")
|
||||
}
|
||||
|
||||
func doSyncClean() error {
|
||||
log.Printf("todo: fix this?")
|
||||
return nil
|
||||
}
|
||||
|
||||
func doSyncUser() error {
|
||||
me.forge.ConfigRill(10, 20)
|
||||
if allerr := me.forge.RillRepos(syncDevelBranch); len(allerr) != 0 {
|
||||
log.Info("RillFunc() failed", allerr)
|
||||
return fmt.Errorf("Rill doSyncUser() error count = %d", len(allerr))
|
||||
} else {
|
||||
log.Info("Rill syncDevelBranch() ok count =", len(allerr))
|
||||
}
|
||||
|
||||
argv.Force = true
|
||||
if err := doAllCheckoutUser(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func syncDevelBranch(repo *gitpb.Repo) error {
|
||||
branch := repo.GetDevelBranchName()
|
||||
if repo.Exists(filepath.Join(".git/refs/heads", branch)) {
|
||||
return nil
|
||||
}
|
||||
if repo.Exists(filepath.Join(".git/refs/remotes/origin", branch)) {
|
||||
cmd := []string{"git", "checkout", branch}
|
||||
err := repo.RunVerbose(cmd)
|
||||
return err
|
||||
}
|
||||
cmd := []string{"git", "branch", branch}
|
||||
repo.RunVerbose(cmd)
|
||||
cmd = []string{"git", "checkout", branch}
|
||||
err := repo.RunVerbose(cmd)
|
||||
return err
|
||||
}
|
||||
|
||||
func syncDevelBranches() error {
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
branch := repo.GetDevelBranchName()
|
||||
if repo.Exists(filepath.Join(".git/refs/heads", branch)) {
|
||||
continue
|
||||
}
|
||||
if repo.Exists(filepath.Join(".git/refs/remotes/origin", branch)) {
|
||||
cmd := []string{"git", "checkout", branch}
|
||||
repo.RunVerbose(cmd)
|
||||
continue
|
||||
}
|
||||
cmd := []string{"git", "branch", branch}
|
||||
repo.RunVerbose(cmd)
|
||||
cmd = []string{"git", "checkout", branch}
|
||||
repo.RunVerbose(cmd)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
*/
|
|
@ -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
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func doVerifyDevel() error {
|
||||
me.found = new(gitpb.Repos)
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if repo.IsDirty() {
|
||||
log.Info(repo.GetGoPath(), "is dirty")
|
||||
continue
|
||||
}
|
||||
if repo.GetDevelBranchName() == "" {
|
||||
continue
|
||||
}
|
||||
if repo.GetMasterBranchName() != repo.GetCurrentBranchName() {
|
||||
log.Info(repo.GetGoPath(), "is not on master branch")
|
||||
continue
|
||||
}
|
||||
|
||||
devel := repo.GetDevelBranchName()
|
||||
if argv.Verbose {
|
||||
log.Printf("Start clean devel branch: %s %s\n", repo.GetGoPath(), devel)
|
||||
}
|
||||
|
||||
// check if devel branch exists in remote repo
|
||||
if repo.IsBranchRemote(devel) {
|
||||
if err := doCleanDevelRepo(repo); err != nil {
|
||||
log.Info(repo.GetGoPath(), "verify clean failed")
|
||||
}
|
||||
// can not continue
|
||||
continue
|
||||
}
|
||||
// devel branch is only local
|
||||
/*
|
||||
todo: something?
|
||||
devname := repo.GetDevelBranchName()
|
||||
if err := requiresGitPush(repo, devname); err != nil {
|
||||
log.Info(repo.GetGoPath(), "is out of sync with upstream")
|
||||
return err
|
||||
}
|
||||
*/
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
err = fmt.Errorf("examineBranch() branch differs. patch diff len == 0. PROBABLY DELETE BRANCH %s", repo.CurrentTag.Refname)
|
||||
log.Info(err)
|
||||
cmd := repo.ConstructGitDiffLog(repo.CurrentTag.Refname, repo.GetMasterBranchName())
|
||||
if argv.Clean.Examine.Fix == nil {
|
||||
log.Info(repo.GetGoPath(), cmd)
|
||||
} else {
|
||||
if _, err := repo.RunVerbose(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
cmd = repo.ConstructGitDiffLog(repo.GetMasterBranchName(), repo.CurrentTag.Refname)
|
||||
if argv.Clean.Examine.Fix == nil {
|
||||
log.Info(repo.GetGoPath(), cmd)
|
||||
} else {
|
||||
if _, err := repo.RunVerbose(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
cmd = []string{"git", "branch", "-D", repo.CurrentTag.Refname}
|
||||
log.Info(repo.GetGoPath(), "TRY THIS:", cmd)
|
||||
if argv.Clean.Examine.Fix == nil {
|
||||
log.Info(repo.GetGoPath(), "TODO: CHECK REMOTE BRANCH DOES NOT EXIST", repo.CurrentTag.Refname)
|
||||
repo.RunVerbose([]string{"ls", "-l", ".git/refs/remotes/origin"})
|
||||
} else {
|
||||
log.Info(repo.GetGoPath(), "TODO: CHECK REMOTE BRANCH DOES NOT EXIST", repo.CurrentTag.Refname)
|
||||
if _, err := repo.RunVerbose(cmd); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
err = fmt.Errorf("examineBranch() branch differs, but not sure how to fix it yet == %d", len(dcount))
|
||||
log.Info(err)
|
||||
return nil
|
||||
*/
|
|
@ -1,55 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func doVerifyUser() error {
|
||||
me.found = new(gitpb.Repos)
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if repo.IsDirty() {
|
||||
log.Info(repo.GetGoPath(), "is dirty")
|
||||
me.found.AppendByGoPath(repo)
|
||||
continue
|
||||
}
|
||||
if repo.ExistsUserBranchRemote() {
|
||||
}
|
||||
|
||||
if repo.ExistsUserBranch() {
|
||||
}
|
||||
if repo.GetMasterBranchName() == repo.GetCurrentBranchName() {
|
||||
log.Info(repo.GetGoPath(), "is not on master branch")
|
||||
continue
|
||||
}
|
||||
|
||||
devel := repo.GetDevelBranchName()
|
||||
if argv.Verbose {
|
||||
log.Printf("Start clean devel branch: %s %s\n", repo.GetGoPath(), devel)
|
||||
}
|
||||
|
||||
// check if devel branch exists in remote repo
|
||||
if repo.IsBranchRemote(devel) {
|
||||
if err := doCleanDevelRepo(repo); err != nil {
|
||||
log.Info(repo.GetGoPath(), "verify clean failed")
|
||||
}
|
||||
// can not continue
|
||||
continue
|
||||
}
|
||||
// devel branch is only local
|
||||
/*
|
||||
todo: something?
|
||||
devname := repo.GetDevelBranchName()
|
||||
if err := requiresGitPush(repo, devname); err != nil {
|
||||
log.Info(repo.GetGoPath(), "is out of sync with upstream")
|
||||
return err
|
||||
}
|
||||
*/
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyRemoteUserBranch(repo *gitpb.Repo) {
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
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'.
|
||||
|
||||
Options:
|
||||
|
||||
--debugger open the debugger window
|
||||
--logger open the log.* control window
|
||||
--gui GUI select the plugin (andlabs,gocui,etc)
|
||||
--gui-verbose enable all logging
|
||||
--bash generate bash completion
|
||||
--bash generate bash completion
|
||||
--connect CONNECT forge url
|
||||
--all git commit --all
|
||||
--build BUILD build a repo
|
||||
--install INSTALL install a repo
|
||||
--forge-rebuild download and rebuild forge
|
||||
--force try to strong arm things
|
||||
--verbose show more output
|
||||
--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
|
||||
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
|
16
exit.go
16
exit.go
|
@ -1,29 +1,33 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"go.wit.com/gui"
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func okExit(thing string) {
|
||||
if thing != "" {
|
||||
log.Info("forge exit:", thing, "ok")
|
||||
}
|
||||
gui.UnloadToolkits()
|
||||
if configSave {
|
||||
me.forge.SetConfigSave(configSave)
|
||||
}
|
||||
// log.Info("Finished go-clean on", check.GetGoPath(), "ok")
|
||||
if thing != "" {
|
||||
log.Info("forge exit:", thing, "ok")
|
||||
}
|
||||
me.forge.Exit()
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
149
find.go
149
find.go
|
@ -1,149 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
)
|
||||
|
||||
// this populates a slice of protobuf records representing each git repo
|
||||
// var me.found []*gitpb.Repo
|
||||
//
|
||||
// so, it makes a subset of repos that are then used performing actions on
|
||||
//
|
||||
// by default, it adds every repo
|
||||
|
||||
func (f *FindCmd) findRepos() {
|
||||
if f == nil {
|
||||
findMine()
|
||||
return
|
||||
}
|
||||
|
||||
if f.Pub != nil {
|
||||
findPublishable()
|
||||
return
|
||||
}
|
||||
|
||||
if f.All {
|
||||
findAll()
|
||||
return
|
||||
}
|
||||
|
||||
if f.Private {
|
||||
findPrivate()
|
||||
return
|
||||
}
|
||||
|
||||
if f.Mine {
|
||||
findMine()
|
||||
return
|
||||
}
|
||||
|
||||
if f.Favorites {
|
||||
findFavorites()
|
||||
return
|
||||
}
|
||||
|
||||
if f.Dirty {
|
||||
findDirty()
|
||||
return
|
||||
}
|
||||
|
||||
if f.User {
|
||||
findUser()
|
||||
return
|
||||
}
|
||||
|
||||
findAll()
|
||||
}
|
||||
|
||||
func findPrivate() {
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if me.forge.Config.IsPrivate(repo.GetGoPath()) {
|
||||
me.found.AppendByGoPath(repo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// finds repos that are writable
|
||||
func findMine() {
|
||||
// log.Printf("get mine %s\n", me.forge.GetGoSrc())
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if me.forge.Config.IsWritable(repo.GetGoPath()) {
|
||||
me.found.AppendByGoPath(repo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// finds repos the user has marked as favorites in the forge .config
|
||||
func findFavorites() {
|
||||
// log.Printf("get favorites %s\n", me.forge.GetGoSrc())
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if me.forge.Config.IsFavorite(repo.GetGoPath()) {
|
||||
me.found.AppendByGoPath(repo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// finds repos that git is reporting as dirty
|
||||
func findDirty() {
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
var repo *gitpb.Repo
|
||||
repo = all.Next()
|
||||
if repo.IsDirty() {
|
||||
me.found.AppendByGoPath(repo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func findAll() {
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
me.found.AppendByGoPath(repo)
|
||||
}
|
||||
}
|
||||
|
||||
func findUser() {
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if repo.GetCurrentBranchName() == repo.GetUserBranchName() {
|
||||
me.found.AppendByGoPath(repo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func findPublishable() {
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if repo.GetTargetVersion() == "" {
|
||||
continue
|
||||
}
|
||||
me.found.AppendByGoPath(repo)
|
||||
}
|
||||
}
|
||||
|
||||
func findReposWithPatches() {
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if repo.GetUserVersion() == "" || repo.GetUserVersion() == "uerr" {
|
||||
continue
|
||||
}
|
||||
if repo.IsDirty() {
|
||||
me.found.AppendByGoPath(repo)
|
||||
continue
|
||||
}
|
||||
if repo.GetUserVersion() != repo.GetDevelVersion() {
|
||||
me.found.AppendByGoPath(repo)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,340 @@
|
|||
// 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"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"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.SetsTable // the gui table buffer
|
||||
update bool // if the window should be updated
|
||||
}
|
||||
|
||||
func (w *stdPatchsetTableWin) Toggle() {
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
if w.win == nil {
|
||||
return
|
||||
}
|
||||
w.win.Toggle()
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
etimef := func(e *forgepb.Set) string {
|
||||
etime := e.Etime.AsTime()
|
||||
s := etime.Format("2006/01/02 15:04")
|
||||
if strings.HasPrefix(s, "1970/") {
|
||||
// just show a blank if it's not set
|
||||
return ""
|
||||
}
|
||||
return s
|
||||
}
|
||||
t.AddStringFunc("etime", etimef)
|
||||
*/
|
||||
|
||||
/*
|
||||
ctimef := func(p *forgepb.Set) string {
|
||||
ctime := p.Ctime.AsTime()
|
||||
return ctime.Format("2006/01/02 15:04")
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func setPatchsetState(p *forgepb.Set) {
|
||||
var bad bool
|
||||
var good bool
|
||||
var done bool = true
|
||||
|
||||
all := p.Patches.All()
|
||||
for all.Scan() {
|
||||
patch := all.Next()
|
||||
// log.Info("patch:", patch.StartHash, patch.CommitHash, patch.Namespace, patch.Filename)
|
||||
repo := me.forge.FindByGoPath(patch.Namespace)
|
||||
if repo == nil {
|
||||
log.Info("could not find repo", patch.Namespace)
|
||||
bad = true
|
||||
continue
|
||||
}
|
||||
if _, err := repo.GetHashName(patch.CommitHash); err == nil {
|
||||
// this patch has been applied
|
||||
patch.Applied = true
|
||||
done = true
|
||||
continue
|
||||
}
|
||||
if name, err := repo.GetHashName(patch.StartHash); err == nil {
|
||||
// it might be possible to apply this patch
|
||||
log.Info("patch may be good:", patch.Namespace, name, patch.CommitHash, patch.Filename)
|
||||
good = true
|
||||
} else {
|
||||
// probably screwed up git trees
|
||||
log.Info("patch with unknown origin:", patch.Namespace, name, err, patch.CommitHash, patch.Filename)
|
||||
bad = true
|
||||
}
|
||||
}
|
||||
if bad {
|
||||
p.State = "BAD"
|
||||
return
|
||||
}
|
||||
if good {
|
||||
p.State = "TRY"
|
||||
return
|
||||
}
|
||||
if done {
|
||||
p.State = "DONE"
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func cleanSubject(line string) string {
|
||||
// Regular expression to remove "Subject:" and "[PATCH...]" patterns
|
||||
re := regexp.MustCompile(`(?i)^Subject:\s*(\[\s*PATCH[^\]]*\]\s*)?`)
|
||||
cleaned := re.ReplaceAllString(line, "")
|
||||
return strings.TrimSpace(cleaned)
|
||||
}
|
||||
|
||||
// jcarr@framebook:~/go/src/go.wit.com/lib/protobuf/forgepb$ git branch --contains 4a27e7702b9b975b066ec9d2ee7ac932d86552e3
|
||||
// * jcarr
|
||||
// jcarr@framebook:~/go/src/go.wit.com/lib/protobuf/forgepb$ git merge-base --is-ancestor "4a27e7702b9b975b066ec9d2ee7ac932d86552e3" "devel" ; echo $?
|
||||
// 1
|
||||
// jcarr@framebook:~/go/src/go.wit.com/lib/protobuf/forgepb$ git merge-base --is-ancestor "4a27e7702b9b975b066ec9d2ee7ac932d86552e3" "jcarr" ; echo $?
|
||||
// 0
|
||||
|
||||
func findCommitByHash(hash string, subject string) (string, error) {
|
||||
cmd := exec.Command("git", "log", "--pretty=format:%H %s")
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
lines := strings.Split(out.String(), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(strings.ToLower(line), strings.ToLower(subject)) {
|
||||
return strings.Fields(line)[0], nil // return the commit hash
|
||||
}
|
||||
if strings.Fields(line)[0] == hash {
|
||||
return "", fmt.Errorf("start commit found: %s", hash)
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("no commit found for subject: %s", subject)
|
||||
}
|
||||
|
||||
func findCommitBySubject(subject string) (string, error) {
|
||||
cmd := exec.Command("git", "log", "--pretty=format:%H %s", "--grep="+subject, "-i")
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
lines := strings.Split(out.String(), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(strings.ToLower(line), strings.ToLower(subject)) {
|
||||
return strings.Fields(line)[0], nil // return the commit hash
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("no commit found for subject: %s", subject)
|
||||
}
|
||||
|
||||
// returns true if PB changed
|
||||
func setNewCommitHash(patch *forgepb.Patch) error {
|
||||
repo := me.forge.FindByGoPath(patch.Namespace)
|
||||
if repo == nil {
|
||||
return log.Errorf("could not find repo %s", patch.Namespace)
|
||||
}
|
||||
|
||||
comment := cleanSubject(patch.Comment)
|
||||
|
||||
os.Chdir(repo.GetFullPath())
|
||||
newhash, err := findCommitBySubject(comment)
|
||||
if err != nil {
|
||||
return log.Errorf("patch: not found hash: %s %s %s %s %v", patch.CommitHash, patch.Namespace, comment, newhash, err)
|
||||
}
|
||||
|
||||
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 nil
|
||||
}
|
||||
|
||||
func AddAllPatches(notdone *forgepb.Patches, pset *forgepb.Set, full bool) {
|
||||
for patch := range pset.Patches.IterAll() {
|
||||
comment := cleanSubject(patch.Comment)
|
||||
|
||||
if found := notdone.FindByCommitHash(patch.CommitHash); found != nil {
|
||||
log.Info("duplicate commit hash", patch.Namespace, "patch:", patch.NewHash, "commithash:", patch.CommitHash, comment)
|
||||
// continue
|
||||
}
|
||||
|
||||
// log.Info("adding patch:", patch.Namespace, patch.CommitHash, comment, newhash)
|
||||
notdone.AppendByCommitHash(patch) // double check to ensure the commit hash isn't added twice
|
||||
}
|
||||
}
|
||||
|
||||
func AddNotDonePatches(notdone *forgepb.Patches, pset *forgepb.Set, full bool) {
|
||||
for patch := range pset.Patches.IterAll() {
|
||||
comment := cleanSubject(patch.Comment)
|
||||
|
||||
if found := notdone.FindByCommitHash(patch.CommitHash); found != nil {
|
||||
log.Info("duplicate notdone", patch.Namespace, "patch:", patch.NewHash, "commithash:", patch.CommitHash, comment)
|
||||
continue
|
||||
}
|
||||
|
||||
repo := me.forge.FindByGoPath(patch.Namespace)
|
||||
if repo == nil {
|
||||
log.Info("could not find repo", patch.Namespace)
|
||||
if full {
|
||||
notdone.AppendByCommitHash(patch) // double check to ensure the commit hash isn't added twice
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if patch.NewHash != "" {
|
||||
log.Info("already applied patch", patch.Namespace, ": newhash:", patch.NewHash, "commithash:", patch.CommitHash, comment)
|
||||
continue
|
||||
}
|
||||
os.Chdir(repo.GetFullPath())
|
||||
newhash, err := findCommitByHash(patch.StartHash, comment)
|
||||
if err != nil {
|
||||
// this patch has not been applied yet
|
||||
log.Info("patch: not found hash:", patch.Namespace, patch.CommitHash, comment, err)
|
||||
notdone.AppendByCommitHash(patch) // double check to ensure the commit hash isn't added twice
|
||||
continue
|
||||
}
|
||||
|
||||
newhash, err = findCommitBySubject(comment)
|
||||
if err == nil {
|
||||
patch.NewHash = newhash
|
||||
log.Info("patch: found hash:", patch.Namespace, "commit patch", patch.CommitHash, "new hash", newhash, "start hash", patch.StartHash, comment)
|
||||
continue
|
||||
}
|
||||
|
||||
// this patch has not been applied yet
|
||||
log.Info("patch: not found hash:", patch.Namespace, patch.CommitHash, comment, newhash, err)
|
||||
notdone.AppendByCommitHash(patch) // double check to ensure the commit hash isn't added twice
|
||||
}
|
||||
}
|
||||
|
||||
func findExpired() *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 forgepb.NewPatches()
|
||||
}
|
||||
|
||||
found := forgepb.NewPatches()
|
||||
|
||||
for patch := range pset.IterAll() {
|
||||
comment := cleanSubject(patch.Comment)
|
||||
|
||||
repo := me.forge.FindByGoPath(patch.Namespace)
|
||||
if repo == nil {
|
||||
log.Info("could not find repo", patch.Namespace)
|
||||
continue
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
os.Chdir(repo.GetFullPath())
|
||||
_, err := findCommitByHash(patch.StartHash, comment)
|
||||
if err == nil {
|
||||
log.Info("found 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
|
||||
}
|
||||
/*
|
||||
|
||||
newhash, err = findCommitBySubject(comment)
|
||||
if err == nil {
|
||||
patch.NewHash = newhash
|
||||
log.Info("patch: found hash:", patch.Namespace, "commit patch", patch.CommitHash, "new hash", newhash, "start hash", patch.StartHash, comment)
|
||||
found.AppendByCommitHash(patch) // double check to ensure the commit hash isn't added twice
|
||||
continue
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
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
|
||||
}
|
235
main.go
235
main.go
|
@ -1,14 +1,15 @@
|
|||
// 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 (
|
||||
"embed"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"go.wit.com/dev/alexflint/arg"
|
||||
"go.wit.com/gui"
|
||||
"go.wit.com/lib/gui/prep"
|
||||
"go.wit.com/lib/protobuf/forgepb"
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/log"
|
||||
|
@ -40,25 +41,18 @@ func getVersion(repo *gitpb.Repo, name string) string {
|
|||
|
||||
func main() {
|
||||
me = new(mainType)
|
||||
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/"
|
||||
}
|
||||
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?
|
||||
|
||||
// load the ~/.config/forge/ config
|
||||
me.forge = forgepb.Init()
|
||||
me.found = new(gitpb.Repos)
|
||||
me.forge = forgepb.Init() // init forge.pb
|
||||
me.forge.ScanRepoDir() // looks for new dirs, checks existing repos for changes
|
||||
|
||||
// initialize patches
|
||||
doPatchInit()
|
||||
|
||||
// first find the repos or gopaths to operate on
|
||||
if argv.Config != nil {
|
||||
|
@ -71,6 +65,11 @@ func main() {
|
|||
okExit("")
|
||||
}
|
||||
|
||||
if argv.BuildForge {
|
||||
buildForge()
|
||||
okExit("")
|
||||
}
|
||||
|
||||
if argv.Checkout != nil {
|
||||
if err := doCheckout(); err != nil {
|
||||
badExit(err)
|
||||
|
@ -78,32 +77,30 @@ func main() {
|
|||
okExit("")
|
||||
}
|
||||
|
||||
if argv.Build != "" {
|
||||
if err := doBuild(); err != nil {
|
||||
badExit(err)
|
||||
}
|
||||
okExit("")
|
||||
}
|
||||
|
||||
if argv.Install != "" {
|
||||
if err := doInstall(); err != nil {
|
||||
badExit(err)
|
||||
}
|
||||
okExit("")
|
||||
}
|
||||
|
||||
if argv.Clean != nil {
|
||||
if argv.Clean.Repo != "" {
|
||||
log.Info("only looking at repo:", argv.Clean.Repo)
|
||||
okExit("")
|
||||
}
|
||||
if argv.Clean.Examine != nil {
|
||||
if err := doExamine(); err != nil {
|
||||
badExit(err)
|
||||
}
|
||||
okExit("")
|
||||
}
|
||||
|
||||
if argv.Clean.GitReset != nil {
|
||||
findAll() // select all the repos
|
||||
doGitReset()
|
||||
okExit("reset")
|
||||
}
|
||||
|
||||
if argv.Clean.Verify != nil {
|
||||
doVerifyDevel()
|
||||
okExit("verify")
|
||||
}
|
||||
|
||||
if err := doClean(); err != nil {
|
||||
badExit(err)
|
||||
}
|
||||
me.forge.ConfigSave()
|
||||
okExit("")
|
||||
}
|
||||
|
||||
if argv.Help != nil {
|
||||
doHelp()
|
||||
okExit("")
|
||||
}
|
||||
|
||||
|
@ -112,90 +109,122 @@ func main() {
|
|||
okExit("")
|
||||
}
|
||||
|
||||
if argv.Show != "" {
|
||||
repo := me.forge.FindByGoPath(argv.Show)
|
||||
me.forge.HumanPrintRepo(repo)
|
||||
// newt := repo.Times.LastUpdate.AsTime()
|
||||
// oldt := repo.Times.MtimeHead.AsTime()
|
||||
if repo.Times.LastUpdate == nil {
|
||||
log.Info("SHOULD RUN Reload() here")
|
||||
repo.Reload()
|
||||
me.forge.HumanPrintRepo(repo)
|
||||
} else {
|
||||
if repo.Times.LastUpdate.Seconds < repo.Times.MtimeHead.Seconds {
|
||||
log.Info("SHOULD RUN Reload() here")
|
||||
if argv.Tag != nil {
|
||||
doTag()
|
||||
okExit("")
|
||||
}
|
||||
|
||||
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("")
|
||||
}
|
||||
setForgeMode(forgepb.ForgeMode_NORMAL)
|
||||
log.Info("normal mode on")
|
||||
okExit("")
|
||||
}
|
||||
|
||||
if argv.Normal.Off != nil {
|
||||
if me.forge.Config.Mode != forgepb.ForgeMode_NORMAL {
|
||||
log.Info("you were aleady not in the normal state")
|
||||
okExit("")
|
||||
}
|
||||
setForgeMode(forgepb.ForgeMode_DEVEL)
|
||||
log.Info("normal mode off")
|
||||
okExit("")
|
||||
}
|
||||
|
||||
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 argv.GitFetch != nil {
|
||||
// argv.GitPull.findRepos()
|
||||
doGitFetch()
|
||||
// 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.GitPull != nil {
|
||||
// argv.GitPull.findRepos()
|
||||
doGitPullNew()
|
||||
if argv.Pull != nil {
|
||||
doPull()
|
||||
okExit("")
|
||||
}
|
||||
|
||||
if argv.List != nil {
|
||||
argv.List.findRepos()
|
||||
found := argv.List.findRepos()
|
||||
// print out the repos
|
||||
me.forge.PrintHumanTable(me.found)
|
||||
if argv.List.Full {
|
||||
me.forge.PrintHumanTableFull(found)
|
||||
} else {
|
||||
me.forge.PrintHumanTable(found)
|
||||
}
|
||||
okExit("")
|
||||
}
|
||||
|
||||
if argv.Patch != nil {
|
||||
if argv.Patch.Submit != "" {
|
||||
_, err := me.forge.SubmitDevelPatchSet(argv.Patch.Submit)
|
||||
if err != nil {
|
||||
badExit(err)
|
||||
}
|
||||
okExit("")
|
||||
if err := doPatch(); err != nil {
|
||||
badExit(err)
|
||||
}
|
||||
|
||||
if argv.Patch.List != nil {
|
||||
lines, err := listPatches()
|
||||
if err != nil {
|
||||
badExit(err)
|
||||
}
|
||||
for i, line := range lines {
|
||||
log.Info(i, line)
|
||||
}
|
||||
}
|
||||
findReposWithPatches()
|
||||
if me.found.Len() == 0 {
|
||||
log.Info("you have no patches in your user branches")
|
||||
okExit("patch list empty")
|
||||
}
|
||||
me.forge.PrintHumanTable(me.found)
|
||||
|
||||
okExit("patch list")
|
||||
}
|
||||
|
||||
// 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() {
|
||||
me.forge.PrintHumanTable(me.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
|
||||
findAll() // select all the repos
|
||||
doCheckDirtyAndConfigSave()
|
||||
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("")
|
||||
}
|
||||
|
||||
// keep this small
|
||||
func doHelp() {
|
||||
log.Info("")
|
||||
log.Info("forge -h : to see the available options")
|
||||
log.Info("forge --bash : will create a bash autocomplete file")
|
||||
log.Info("forge : with no arguements, forge tries to load a GO GUI plugin")
|
||||
log.Info(" : there are two GUI plugins. terminal & GTK")
|
||||
log.Info("")
|
||||
log.Info("forge list : shows a table of all your repos")
|
||||
log.Info("forge checkout : checks out all your repos to the same branch")
|
||||
log.Info(" : the default is your user branch")
|
||||
log.Info("forge clean : reverts all repos to the master branch")
|
||||
log.Info("forge dirty : show all repos git reports as dirty")
|
||||
log.Info("")
|
||||
okExit("")
|
||||
}
|
||||
|
||||
func doHelpPatches() {
|
||||
log.Info("TODO: ?")
|
||||
okExit("")
|
||||
}
|
||||
|
|
171
send.go
171
send.go
|
@ -1,171 +0,0 @@
|
|||
// Copyright 2024 WIT.COM Inc Licensed GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"go.wit.com/lib/protobuf/forgepb"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func submitPatches(pset *forgepb.Patchset) error {
|
||||
var url string
|
||||
url = me.urlbase + "/patchset"
|
||||
msg, err := pset.Marshal()
|
||||
if err != nil {
|
||||
log.Info("proto.Marshal() failed:", err)
|
||||
return err
|
||||
}
|
||||
log.Info("proto.Marshal() msg len", len(msg))
|
||||
body, err := me.forge.HttpPost(url, msg)
|
||||
if err != nil {
|
||||
log.Info("httpPost() failed:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
test := strings.TrimSpace(string(body))
|
||||
for _, line := range strings.Split(test, "\n") {
|
||||
log.Info("got back:", line)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func listPatches() ([]string, error) {
|
||||
var url string
|
||||
url = me.urlbase + "/patchsetlist"
|
||||
body, err := me.forge.HttpPost(url, nil)
|
||||
if err != nil {
|
||||
log.Info("httpPost() failed:", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
/*
|
||||
var last string
|
||||
test := strings.TrimSpace(string(body))
|
||||
for _, line := range strings.Split(test, "\n") {
|
||||
log.Info("patchset:", line)
|
||||
last = strings.TrimSpace(line)
|
||||
}
|
||||
parts := strings.Fields(last)
|
||||
if len(parts) == 0 {
|
||||
return fmt.Errorf("listPatches() there are no patchsets at this time")
|
||||
}
|
||||
getPatch(parts[0])
|
||||
return nil
|
||||
*/
|
||||
test := strings.TrimSpace(string(body))
|
||||
return strings.Split(test, "\n"), nil
|
||||
}
|
||||
|
||||
func lastPatch() string {
|
||||
var url string
|
||||
url = me.urlbase + "/patchsetlist"
|
||||
body, err := me.forge.HttpPost(url, nil)
|
||||
if err != nil {
|
||||
log.Info("httpPost() failed:", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
var last string
|
||||
test := strings.TrimSpace(string(body))
|
||||
for _, line := range strings.Split(test, "\n") {
|
||||
log.Info("patchset:", line)
|
||||
last = strings.TrimSpace(line)
|
||||
}
|
||||
parts := strings.Fields(last)
|
||||
return parts[0]
|
||||
}
|
||||
|
||||
func doRegister(newurl string) error {
|
||||
var url string
|
||||
url = me.urlbase + "/register?url=" + newurl
|
||||
body, err := me.forge.HttpPost(url, nil)
|
||||
if err != nil {
|
||||
log.Info("httpPost() failed:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
test := strings.TrimSpace(string(body))
|
||||
for _, line := range strings.Split(test, "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
log.Info("server returned:", line)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// gets the patch
|
||||
// todo: move to forgepb
|
||||
func getPatch(pbfile string) (*forgepb.Patchset, error) {
|
||||
url := me.urlbase + "/patchsetget?filename=" + pbfile
|
||||
log.Info("getPatch() url", url)
|
||||
body, err := me.forge.HttpPost(url, nil)
|
||||
if err != nil {
|
||||
log.Info("httpPost() failed:", err)
|
||||
return nil, err
|
||||
}
|
||||
log.Info("getPatch() len(body)", len(body))
|
||||
var pset *forgepb.Patchset
|
||||
pset = new(forgepb.Patchset)
|
||||
err = pset.Unmarshal(body)
|
||||
if err != nil {
|
||||
log.Info("Unmarshal failed", err)
|
||||
return nil, err
|
||||
}
|
||||
return pset, nil
|
||||
}
|
||||
|
||||
func savePatch(pbfile string) (*forgepb.Patchset, error) {
|
||||
url := me.urlbase + "/patchsetget?filename=" + pbfile
|
||||
log.Info("getPatch() url", url)
|
||||
body, err := me.forge.HttpPost(url, nil)
|
||||
if err != nil {
|
||||
log.Info("httpPost() failed:", err)
|
||||
return nil, err
|
||||
}
|
||||
log.Info("getPatch() len(body)", len(body))
|
||||
var pset *forgepb.Patchset
|
||||
pset = new(forgepb.Patchset)
|
||||
err = pset.Unmarshal(body)
|
||||
if err != nil {
|
||||
log.Info("Unmarshal failed", err)
|
||||
return nil, err
|
||||
}
|
||||
filename := filepath.Join("/tmp", pbfile)
|
||||
f, _ := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
f.Write(body)
|
||||
f.Close()
|
||||
return pset, nil
|
||||
}
|
||||
|
||||
/*
|
||||
func sendDevelDiff(name string) error {
|
||||
pset, err := me.forge.MakeDevelPatchSet(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sendPatches(pset); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
|
||||
func sendMasterDiff() {
|
||||
pset, err := me.forge.MakeMasterPatchSet()
|
||||
if err != nil {
|
||||
badExit(err)
|
||||
}
|
||||
all := pset.Patches.SortByFilename()
|
||||
for all.Scan() {
|
||||
p := all.Next()
|
||||
log.Info("read in patch:", p.Filename)
|
||||
}
|
||||
err = me.forge.SendPatchSet(pset)
|
||||
if err != nil {
|
||||
badExit(err)
|
||||
}
|
||||
}
|
70
structs.go
70
structs.go
|
@ -1,11 +1,13 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
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"
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
)
|
||||
|
||||
var me *mainType
|
||||
|
@ -18,58 +20,32 @@ 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
|
||||
found *gitpb.Repos // stores the list of repos to process things on
|
||||
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
|
||||
|
||||
// our view of the repositories
|
||||
repos *repoWindow
|
||||
patchWin *patchesWindow
|
||||
|
||||
mainWindow *gadgets.BasicWindow
|
||||
mainbox *gui.Node // the main box. enable/disable this
|
||||
autoDryRun *gui.Node // checkbox for --dry-run
|
||||
goSrcPwd *gadgets.OneLiner // what is being used as primary directory for your work
|
||||
goSrcEdit *gadgets.BasicEntry // what is being used as primary directory for your work
|
||||
gitAuthor *gadgets.OneLiner // ENV GIT_AUTHOR NAME and EMAIL
|
||||
|
||||
// the main box. enable/disable this
|
||||
mainbox *gui.Node
|
||||
|
||||
// the window from the /lib/gui/gowit package
|
||||
// lw *gadgets.BasicWindow
|
||||
|
||||
// #### Sorting options for the repolist
|
||||
// autoHidePerfect *gui.Node
|
||||
// autoHideReadOnly *gui.Node
|
||||
|
||||
// checkbox for --dry-run
|
||||
autoDryRun *gui.Node
|
||||
|
||||
// checkbox to enable intermittent scanning
|
||||
// if checked, it will check all your repos for changes
|
||||
autoScanReposCB *gui.Node
|
||||
|
||||
// what is being used as your home dir
|
||||
userHomePwd *gadgets.OneLiner
|
||||
|
||||
// what is being used as ~/go/src
|
||||
goSrcPwd *gadgets.OneLiner
|
||||
|
||||
// ENV GIT_AUTHOR NAME and EMAIL
|
||||
gitAuthor *gadgets.OneLiner
|
||||
|
||||
// displays a summary of all the repos
|
||||
// has total dirty, total read-only
|
||||
// total patches, etc
|
||||
// summary *patchSummary
|
||||
|
||||
// when switch to user or devel branches, autocreate them
|
||||
autoCreateBranches *gui.Node
|
||||
|
||||
// these hold the branches that the user can switch all
|
||||
// the repositories to them
|
||||
newBranch *gui.Node
|
||||
setBranchB *gui.Node
|
||||
// these hold the branches that the user can switch all the repositories to them
|
||||
reposWinB *gui.Node // button that opens the repos window
|
||||
repoAllB *gui.Node // "all" repos button
|
||||
repoDirtyB *gui.Node // "dirty" repos button
|
||||
repoDevelMergeB *gui.Node // "merge to devel" repos button
|
||||
repoWritableB *gui.Node // "what repos are writable" repos button
|
||||
}
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
// 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 (
|
||||
"os"
|
||||
|
||||
"go.wit.com/lib/fhelp"
|
||||
"go.wit.com/lib/gadgets"
|
||||
"go.wit.com/lib/gui/shell"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func makeHowtoWin() *gadgets.GenericWindow {
|
||||
howtoWin := gadgets.NewGenericWindow("Howto", "forge -- a GUI tool for git repostories")
|
||||
tmp := `A good way to see how forge works is to download forge
|
||||
|
||||
This will 'git clone' a few things (~50 repos):
|
||||
`
|
||||
howtoWin.Group.NewLabel(tmp)
|
||||
grid := howtoWin.Group.RawGrid()
|
||||
grid.NewLabel("forge")
|
||||
grid.NewLabel("the sources for forge")
|
||||
grid.NextRow()
|
||||
|
||||
grid.NewLabel("autogenpb")
|
||||
grid.NewLabel("generates needed code for working with the protobuf files")
|
||||
grid.NextRow()
|
||||
|
||||
grid.NewLabel("go-clone")
|
||||
grid.NewLabel("recursively 'git clone' dependencies based on go.mod")
|
||||
grid.NextRow()
|
||||
|
||||
grid.NewLabel("the GUI")
|
||||
grid.NewLabel("GO plugins for libcurses and GTK")
|
||||
grid.NextRow()
|
||||
|
||||
grid.NewLabel("") // a stupid way to add padding
|
||||
grid.NextRow()
|
||||
|
||||
// howtoWin.Group.NewLabel("Working dir: " + me.forge.Config.ReposDir)
|
||||
|
||||
grid = howtoWin.Group.RawGrid()
|
||||
grid.NewButton("Download into "+me.forge.Config.ReposDir, func() {
|
||||
howtoWin.Disable()
|
||||
defer howtoWin.Enable()
|
||||
downloadForge()
|
||||
})
|
||||
grid.NewButton("Build forge & GUI GO plugins", func() {
|
||||
howtoWin.Disable()
|
||||
defer howtoWin.Enable()
|
||||
buildForge()
|
||||
})
|
||||
return howtoWin
|
||||
}
|
||||
|
||||
func downloadForge() {
|
||||
log.Info("download here")
|
||||
if path, err := fhelp.CheckCmd("go-clone"); err != nil {
|
||||
log.Info("go-clone missing", path, err)
|
||||
cmd := []string{"go", "install", "go.wit.com/apps/go-clone@latest"}
|
||||
shell.RunRealtime(cmd)
|
||||
}
|
||||
if _, err := fhelp.CheckCmd("autogenpb"); err != nil {
|
||||
cmd := []string{"go", "install", "go.wit.com/apps/autogenpb@latest"}
|
||||
shell.RunRealtime(cmd)
|
||||
}
|
||||
if _, err := fhelp.CheckCmd("go-mod-clean"); err != nil {
|
||||
cmd := []string{"go", "install", "go.wit.com/apps/go-mod-clean@latest"}
|
||||
shell.RunRealtime(cmd)
|
||||
}
|
||||
if path, err := fhelp.CheckCmd("go-clone"); err != nil {
|
||||
log.Info("can't prep build. you probably need ~/go/bin in your PATH", path, err)
|
||||
return
|
||||
}
|
||||
var basecmd []string
|
||||
var cmd []string
|
||||
if me.forge.IsGoWork() {
|
||||
log.Info("Using go.work directory")
|
||||
basecmd = []string{"go-clone", "--work", "--recursive"}
|
||||
} else {
|
||||
basecmd = []string{"go-clone", "--recursive"}
|
||||
}
|
||||
// log.Info("Running:", cmd)
|
||||
// shell.RunRealtime(cmd)
|
||||
|
||||
cmd = append(basecmd, "go.wit.com/apps/go-mod-clean")
|
||||
log.Info("Running:", cmd)
|
||||
shell.RunRealtime(cmd)
|
||||
os.Exit(-1)
|
||||
|
||||
cmd = append(basecmd, "go.wit.com/apps/forge")
|
||||
log.Info("Running:", cmd)
|
||||
shell.RunRealtime(cmd)
|
||||
|
||||
cmd = append(basecmd, "go.wit.com/toolkits/gocui")
|
||||
log.Info("Running:", cmd)
|
||||
shell.RunRealtime(cmd)
|
||||
|
||||
cmd = append(basecmd, "go.wit.com/toolkits/andlabs")
|
||||
log.Info("Running:", cmd)
|
||||
shell.RunRealtime(cmd)
|
||||
|
||||
// should, after go-clone, be able to go build
|
||||
/*
|
||||
repo := me.forge.FindByGoPath("go.wit.com/lib/proto/forgepb")
|
||||
if repo != nil {
|
||||
repo.RunVerbose([]string{"autogenpb"})
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
func buildForge() {
|
||||
log.Info("buildForge() not done yet")
|
||||
}
|
321
windowPatches.go
321
windowPatches.go
|
@ -1,138 +1,217 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strings"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"go.wit.com/gui"
|
||||
"go.wit.com/lib/gadgets"
|
||||
"go.wit.com/lib/protobuf/forgepb"
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/log"
|
||||
|
||||
"go.wit.com/gui"
|
||||
)
|
||||
|
||||
type patchesWindow struct {
|
||||
once sync.Once // only init() the window once
|
||||
win *gadgets.BasicWindow // the patches window
|
||||
stack *gui.Node // the top box set as vertical
|
||||
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
|
||||
setlist map[string]*forgepb.Patchset // a map of the patch names to the protobuf
|
||||
setwin map[string]*patchWindow // a map of the patch names to the protobuf
|
||||
type stdPatchTableWin struct {
|
||||
sync.Mutex
|
||||
win *gadgets.GenericWindow // the machines gui window
|
||||
box *gui.Node // the machines gui parent box widget
|
||||
TB *forgepb.PatchesTable // the gui table buffer
|
||||
update bool // if the window should be updated
|
||||
}
|
||||
|
||||
func (r *patchesWindow) Hidden() bool {
|
||||
return r.win.Hidden()
|
||||
}
|
||||
|
||||
func (r *patchesWindow) Toggle() {
|
||||
if r.Hidden() {
|
||||
r.Show()
|
||||
} else {
|
||||
r.Hide()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *patchesWindow) Show() {
|
||||
r.win.Show()
|
||||
}
|
||||
|
||||
func (r *patchesWindow) Hide() {
|
||||
r.win.Hide()
|
||||
}
|
||||
|
||||
func (r *patchesWindow) Disable() {
|
||||
r.stack.Disable()
|
||||
}
|
||||
|
||||
func (r *patchesWindow) Enable() {
|
||||
r.stack.Enable()
|
||||
}
|
||||
|
||||
// you can only have one of these
|
||||
func (r *patchesWindow) initWindow() {
|
||||
// sync.Once()
|
||||
r.win = gadgets.RawBasicWindow("Forge Patchesets")
|
||||
r.win.Make()
|
||||
|
||||
r.stack = r.win.Box().NewBox("bw vbox", false)
|
||||
// me.reposwin.Draw()
|
||||
r.win.Custom = func() {
|
||||
log.Warn("Patchset Window close. setting hidden=true")
|
||||
// sets the hidden flag to false so Toggle() works
|
||||
r.win.Hide()
|
||||
}
|
||||
|
||||
r.grid = r.stack.NewGrid("", 0, 0)
|
||||
|
||||
/*
|
||||
r.shelf = r.initGroup()
|
||||
group1 := r.stack.NewGroup("stuff")
|
||||
vbox := group1.Box()
|
||||
vbox.Vertical()
|
||||
*/
|
||||
|
||||
r.summary = r.submitPatchesBox(r.stack)
|
||||
|
||||
// update the stats about the repos and patches
|
||||
r.summary.Update()
|
||||
|
||||
g := r.stack.NewGroup("PatchSet List")
|
||||
|
||||
// add the grid
|
||||
r.setgrid = g.NewGrid("", 0, 0)
|
||||
r.setlist = make(map[string]*forgepb.Patchset)
|
||||
r.setwin = make(map[string]*patchWindow)
|
||||
|
||||
// query for current patchsets
|
||||
lines, err := listPatches()
|
||||
if err != nil {
|
||||
log.Info(err)
|
||||
func (w *stdPatchTableWin) Toggle() {
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
slices.Reverse(lines)
|
||||
count := 0
|
||||
for i, line := range lines {
|
||||
log.Info(i, line)
|
||||
count += 1
|
||||
if count < 10 {
|
||||
r.addPatchset(line)
|
||||
if w.win == nil {
|
||||
return
|
||||
}
|
||||
w.win.Toggle()
|
||||
}
|
||||
|
||||
func makePatchesWin(patches *forgepb.Patches) *stdPatchTableWin {
|
||||
dwin := new(stdPatchTableWin)
|
||||
dwin.win = gadgets.NewGenericWindow("current patches", "patching options")
|
||||
dwin.win.Custom = func() {
|
||||
log.Info("test delete window here")
|
||||
dwin.win.Hide()
|
||||
// dwin = nil
|
||||
}
|
||||
grid := dwin.win.Group.RawGrid()
|
||||
|
||||
grid.NewLabel(fmt.Sprintf("%d", patches.Len()))
|
||||
grid.NewLabel(fmt.Sprintf("total patches"))
|
||||
grid.NextRow()
|
||||
|
||||
repomap := make(map[string]int)
|
||||
all := patches.All()
|
||||
for all.Scan() {
|
||||
patch := all.Next()
|
||||
repomap[patch.Namespace] += 1
|
||||
}
|
||||
grid.NewLabel(fmt.Sprintf("%d", len(repomap)))
|
||||
grid.NewLabel(fmt.Sprintf("total repos"))
|
||||
grid.NextRow()
|
||||
|
||||
grid.NewButton("Apply All", func() {
|
||||
var count int
|
||||
all := patches.SortByFilename()
|
||||
for all.Scan() {
|
||||
p := all.Next()
|
||||
applyPatchNew(p)
|
||||
}
|
||||
log.Info("ALL PATCHES WORKED! count =", count)
|
||||
})
|
||||
|
||||
// make a box at the bottom of the window for the protobuf table
|
||||
dwin.box = dwin.win.Bottom.Box().SetProgName("TBOX")
|
||||
|
||||
if patches != nil {
|
||||
dwin.doPatchesTable(patches)
|
||||
}
|
||||
|
||||
return dwin
|
||||
}
|
||||
|
||||
func applyPatchNew(p *forgepb.Patch) error {
|
||||
rn := p.Namespace
|
||||
repo := me.forge.FindByGoPath(rn)
|
||||
if repo == nil {
|
||||
log.Info("Could not figure out repo path", rn)
|
||||
return log.Errorf("%s namespace?\n", rn)
|
||||
}
|
||||
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 err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dwin *stdPatchTableWin) doPatchesTable(currentPatches *forgepb.Patches) {
|
||||
dwin.Lock()
|
||||
defer dwin.Unlock()
|
||||
if dwin.TB != nil {
|
||||
dwin.TB.Delete()
|
||||
dwin.TB = nil
|
||||
}
|
||||
|
||||
// display the protobuf
|
||||
dwin.TB = AddPatchesPB(dwin.box, currentPatches)
|
||||
f := func(p *forgepb.Patch) {
|
||||
log.Info("do something with patch", p.Filename)
|
||||
}
|
||||
dwin.TB.Custom(f)
|
||||
}
|
||||
|
||||
// used by the PB table
|
||||
func applyPatchLabel(p *forgepb.Patch) string {
|
||||
rn := p.Namespace
|
||||
if repo := me.forge.FindByGoPath(rn); repo == nil {
|
||||
// log.Info("Could not figure out repo path", rn)
|
||||
return ""
|
||||
}
|
||||
if p.NewHash == "" {
|
||||
return "git am"
|
||||
}
|
||||
return "done"
|
||||
}
|
||||
|
||||
func applyPatchClick(p *forgepb.Patch) {
|
||||
if err := applyPatchNew(p); err != nil {
|
||||
log.Info("git am failed on file", p.Filename, "with error", err)
|
||||
return
|
||||
}
|
||||
log.Info("ran: git am", p.Filename, "ok")
|
||||
}
|
||||
|
||||
// define what rows to have in the protobuf table
|
||||
func AddPatchesPB(tbox *gui.Node, pb *forgepb.Patches) *forgepb.PatchesTable {
|
||||
t := pb.NewTable("PatchesPB")
|
||||
t.NewUuid()
|
||||
t.SetParent(tbox)
|
||||
|
||||
gitam := t.AddButtonFunc("apply", applyPatchLabel)
|
||||
gitam.Custom = applyPatchClick
|
||||
|
||||
t.AddCommitHash()
|
||||
t.AddNamespace()
|
||||
// t.AddFilename()
|
||||
t.AddStringFunc("file", func(p *forgepb.Patch) string {
|
||||
_, fname := filepath.Split(p.Filename)
|
||||
return fname
|
||||
})
|
||||
t.AddCommitHash()
|
||||
|
||||
t.ShowTable()
|
||||
return t
|
||||
}
|
||||
|
||||
func applyPatch(repo *gitpb.Repo, filename string) error {
|
||||
cmd := []string{"git", "am", filename}
|
||||
err := repo.RunVerbose(cmd)
|
||||
return err
|
||||
}
|
||||
|
||||
func savePatch(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()
|
||||
|
||||
return tmpname, nil
|
||||
}
|
||||
|
||||
func applyAndTrackPatch(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")
|
||||
|
||||
p.NewHash = "fixme applyAndTrack"
|
||||
if setNewHash(p, p.NewHash) {
|
||||
log.Info("setting NewHash worked", p.NewHash)
|
||||
}
|
||||
me.forge.SavePatchsets()
|
||||
|
||||
return p.NewHash, log.Errorf("did not lookup new hash")
|
||||
}
|
||||
|
||||
func setNewHash(p *forgepb.Patch, hash string) bool {
|
||||
for pset := range me.forge.Patchsets.IterAll() {
|
||||
for patch := range pset.Patches.IterAll() {
|
||||
if patch.CommitHash == hash {
|
||||
patch.NewHash = hash
|
||||
log.Info("found patch in repo")
|
||||
me.forge.SavePatchsets()
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Info("Total patchsets:", count)
|
||||
}
|
||||
|
||||
func (r *patchesWindow) addPatchset(line string) {
|
||||
parts := strings.Split(line, "Author:")
|
||||
author := parts[1]
|
||||
parts = strings.Fields(parts[0])
|
||||
name := parts[0]
|
||||
subject := strings.Join(parts[1:], " ")
|
||||
r.setgrid.NewLabel(name)
|
||||
r.setgrid.NewLabel(subject)
|
||||
r.setgrid.NewLabel(author)
|
||||
|
||||
r.setgrid.NewButton("View", func() {
|
||||
// has the window already been created?
|
||||
win := r.setwin[name]
|
||||
if win != nil {
|
||||
win.Toggle()
|
||||
log.Info("TRYING TO TOGGLE WINDOW")
|
||||
return
|
||||
}
|
||||
|
||||
// get the patch and make the window
|
||||
pset, err := getPatch(name)
|
||||
if err != nil {
|
||||
log.Info(name, "failed to download", err)
|
||||
return
|
||||
}
|
||||
r.setlist[name] = pset
|
||||
r.setwin[name] = makePatchWindow(pset)
|
||||
r.setwin[name].Show()
|
||||
})
|
||||
r.setgrid.NextRow()
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -1,120 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"go.wit.com/gui"
|
||||
"go.wit.com/lib/gadgets"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
type patchSummary struct {
|
||||
grid *gui.Node
|
||||
updateB *gui.Node
|
||||
docsB *gui.Node
|
||||
gitPushB *gui.Node
|
||||
gitPullB *gui.Node
|
||||
checkB *gui.Node
|
||||
totalOL *gadgets.OneLiner
|
||||
dirtyOL *gadgets.OneLiner
|
||||
readonlyOL *gadgets.OneLiner
|
||||
rw *gadgets.OneLiner
|
||||
totalPatchesOL *gadgets.OneLiner
|
||||
totalUserRepos *gui.Node
|
||||
totalDevelRepos *gui.Node
|
||||
totalMasterRepos *gui.Node
|
||||
totalUserPatches *gui.Node
|
||||
totalDevelPatches *gui.Node
|
||||
totalMasterPatches *gui.Node
|
||||
fileCount *gui.Node
|
||||
unknownOL *gadgets.BasicEntry
|
||||
unknownSubmitB *gui.Node
|
||||
reason *gadgets.BasicEntry
|
||||
submitB *gui.Node
|
||||
// allp []*repolist.Patch
|
||||
}
|
||||
|
||||
func (r *patchesWindow) submitPatchesBox(box *gui.Node) *patchSummary {
|
||||
s := new(patchSummary)
|
||||
group1 := box.NewGroup("Patch Summary")
|
||||
s.grid = group1.RawGrid()
|
||||
|
||||
s.totalOL = gadgets.NewOneLiner(s.grid, "Total")
|
||||
_ = s.grid.NewLabel("total changes")
|
||||
_ = s.grid.NewLabel("user to devel")
|
||||
s.grid.NextRow()
|
||||
|
||||
s.dirtyOL = gadgets.NewOneLiner(s.grid, "dirty")
|
||||
_ = s.grid.NewLabel("") // skip a column
|
||||
s.totalUserRepos = s.grid.NewLabel("x go repos")
|
||||
s.grid.NextRow()
|
||||
|
||||
s.readonlyOL = gadgets.NewOneLiner(s.grid, "read-only")
|
||||
_ = s.grid.NewLabel("") // skip a column
|
||||
s.totalUserPatches = s.grid.NewLabel("x patches")
|
||||
s.grid.NextRow()
|
||||
|
||||
s.rw = gadgets.NewOneLiner(s.grid, "r/w")
|
||||
_ = s.grid.NewLabel("") // skip a column
|
||||
s.fileCount = s.grid.NewLabel("x files")
|
||||
s.grid.NextRow()
|
||||
|
||||
group1 = box.NewGroup("PatchSet Create")
|
||||
s.grid = group1.RawGrid()
|
||||
|
||||
s.grid.NewButton("update patch summary", func() {
|
||||
s.Update()
|
||||
})
|
||||
|
||||
s.reason = gadgets.NewBasicEntry(s.grid, "set name:")
|
||||
s.reason.Custom = func() {
|
||||
if s.reason.String() != "" {
|
||||
s.submitB.Enable()
|
||||
} else {
|
||||
s.submitB.Disable()
|
||||
}
|
||||
}
|
||||
s.submitB = s.grid.NewButton("Submit", func() {
|
||||
pset, err := me.forge.SubmitDevelPatchSet(s.reason.String())
|
||||
if err != nil {
|
||||
log.Info(err)
|
||||
return
|
||||
}
|
||||
line := "somedate " + s.reason.String() + " Author: me" + pset.GitAuthorEmail
|
||||
me.patchWin.addPatchset(line)
|
||||
})
|
||||
|
||||
// disable these until there are not dirty repos
|
||||
// s.reason.Disable()
|
||||
s.submitB.Disable()
|
||||
s.grid.NextRow()
|
||||
return s
|
||||
}
|
||||
|
||||
// does not run any commands
|
||||
func (s *patchSummary) Update() {
|
||||
var total, dirty, readonly, rw int
|
||||
var userT int // , develT, masterT int
|
||||
// var userP, develP, masterP int
|
||||
// broken after move to forge protobuf
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
total += 1
|
||||
if repo.IsDirty() {
|
||||
dirty += 1
|
||||
}
|
||||
if me.forge.Config.IsReadOnly(repo.GetGoPath()) {
|
||||
readonly += 1
|
||||
} else {
|
||||
rw += 1
|
||||
}
|
||||
}
|
||||
s.totalOL.SetText(strconv.Itoa(total) + " repos")
|
||||
s.dirtyOL.SetText(strconv.Itoa(dirty) + " repos")
|
||||
s.readonlyOL.SetText(strconv.Itoa(readonly) + " repos")
|
||||
s.rw.SetText(fmt.Sprintf("%d repos", rw))
|
||||
|
||||
s.totalUserRepos.SetText(strconv.Itoa(userT) + " repos")
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// 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 (
|
||||
"go.wit.com/lib/gadgets"
|
||||
)
|
||||
|
||||
// Publish Window
|
||||
func makePublishWindow() *gadgets.GenericWindow {
|
||||
pubWin := gadgets.NewGenericWindow("publish code", "tasks for merging, versioning and publishing code")
|
||||
|
||||
grid := pubWin.Group.RawGrid()
|
||||
|
||||
grid.NewButton("merge all patches to master", func() {
|
||||
/*
|
||||
pubWin.Disable()
|
||||
defer pubWin.Enable()
|
||||
|
||||
if err := doAllCheckoutDevel(); err != nil {
|
||||
log.Info("checkout error:", err)
|
||||
} else {
|
||||
log.Info("checkout was ok")
|
||||
}
|
||||
|
||||
mergeUserToDevel(true)
|
||||
|
||||
if err := doAllCheckoutMaster(); err != nil {
|
||||
log.Info("checkout error:", err)
|
||||
} else {
|
||||
log.Info("checkout was ok")
|
||||
}
|
||||
|
||||
mergeDevelToMaster(true)
|
||||
*/
|
||||
})
|
||||
|
||||
return pubWin
|
||||
}
|
284
windowRepos.go
284
windowRepos.go
|
@ -1,284 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"go.wit.com/lib/gadgets"
|
||||
"go.wit.com/lib/gui/repolist"
|
||||
"go.wit.com/lib/protobuf/forgepb"
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/log"
|
||||
|
||||
"go.wit.com/gui"
|
||||
)
|
||||
|
||||
type repoWindow struct {
|
||||
win *gadgets.BasicWindow
|
||||
box *gui.Node
|
||||
|
||||
// the top box of the repolist window
|
||||
topbox *gui.Node
|
||||
|
||||
View *repolist.RepoList
|
||||
}
|
||||
|
||||
func (r *repoWindow) Hidden() bool {
|
||||
return r.win.Hidden()
|
||||
}
|
||||
|
||||
func (r *repoWindow) Show() {
|
||||
r.win.Show()
|
||||
}
|
||||
|
||||
func (r *repoWindow) Hide() {
|
||||
r.win.Hide()
|
||||
}
|
||||
|
||||
func (r *repoWindow) Disable() {
|
||||
r.box.Disable()
|
||||
}
|
||||
|
||||
func (r *repoWindow) Enable() {
|
||||
r.box.Enable()
|
||||
}
|
||||
|
||||
// you can only have one of these
|
||||
func makeRepoView() *repoWindow {
|
||||
if me.repos != nil {
|
||||
return me.repos
|
||||
}
|
||||
r := new(repoWindow)
|
||||
r.win = gadgets.RawBasicWindow("All git repositories in ~/go/src/")
|
||||
r.win.Make()
|
||||
|
||||
r.box = r.win.Box().NewBox("bw vbox", false)
|
||||
// me.reposwin.Draw()
|
||||
r.win.Custom = func() {
|
||||
log.Warn("Repo Window close. hidden=true")
|
||||
// sets the hidden flag to false so Toggle() works
|
||||
r.win.Hide()
|
||||
}
|
||||
|
||||
r.topbox = r.repoMenu()
|
||||
|
||||
r.View = repolist.InitBox(me.forge, r.box)
|
||||
r.View.Enable()
|
||||
|
||||
r.View.ScanRepositories()
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *repoWindow) reInitForge() {
|
||||
// re-read everything
|
||||
me.forge = forgepb.Init()
|
||||
me.found = new(gitpb.Repos)
|
||||
me.repos = makeRepoView()
|
||||
me.repos.Show()
|
||||
|
||||
// update the protobuf pointers
|
||||
loop := me.forge.Repos.All()
|
||||
for loop.Scan() {
|
||||
repo := loop.Next()
|
||||
vrepo := me.repos.View.FindByPath(repo.GetGoPath())
|
||||
if vrepo != nil {
|
||||
vrepo.UpdatePb(repo)
|
||||
}
|
||||
}
|
||||
|
||||
// now update the gui
|
||||
vloop := me.repos.View.ReposSortByName()
|
||||
for vloop.Scan() {
|
||||
var repo *repolist.RepoRow
|
||||
repo = vloop.Repo()
|
||||
repo.NewScan()
|
||||
}
|
||||
|
||||
i, s := me.repos.View.ScanRepositories()
|
||||
log.Info("re-scanning done", i, "repos in", s)
|
||||
}
|
||||
|
||||
func (r *repoWindow) repoMenu() *gui.Node {
|
||||
// reposbox.SetExpand(false)
|
||||
group1 := r.box.NewGroup("Filter:")
|
||||
|
||||
hbox := group1.Box()
|
||||
// hbox.Horizontal()
|
||||
hbox.Vertical()
|
||||
|
||||
box2 := hbox.Box().Horizontal()
|
||||
/*
|
||||
*/
|
||||
|
||||
dirty := box2.NewCheckbox("dirty")
|
||||
dirty.Custom = func() {
|
||||
log.Info("filter dirty =", dirty.Checked())
|
||||
}
|
||||
|
||||
box2.NewButton("merge user to devel", func() {
|
||||
r.Disable()
|
||||
defer r.Enable()
|
||||
if IsAnythingDirty() {
|
||||
log.Info("You can't apply patches when repos are dirty")
|
||||
me.forge.PrintHumanTable(me.found)
|
||||
return
|
||||
}
|
||||
if !r.mergeAllUserToDevel() {
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
box2.NewButton("test master merge", func() {
|
||||
r.Disable()
|
||||
r.mergeAllDevelToMain()
|
||||
// r.reInitForge()
|
||||
r.Enable()
|
||||
})
|
||||
|
||||
box2.NewButton("show apps", func() {
|
||||
loop := me.repos.View.ReposSortByName()
|
||||
for loop.Scan() {
|
||||
var repo *repolist.RepoRow
|
||||
repo = loop.Repo()
|
||||
if repo.IsBinary() {
|
||||
// log.Info(repo.Status.Path(), "compile here. Show()")
|
||||
repo.Show()
|
||||
} else {
|
||||
// log.Info(repo.Status.Path(), "library here. Hide()")
|
||||
repo.Hide()
|
||||
}
|
||||
}
|
||||
})
|
||||
box2.NewButton("re-init forge", func() {
|
||||
log.Info("re-scanning now")
|
||||
r.reInitForge()
|
||||
})
|
||||
box2.NewButton("ConfigSave()", func() {
|
||||
me.forge.ConfigSave()
|
||||
})
|
||||
box2.NewButton("Table()", func() {
|
||||
me.found = new(gitpb.Repos)
|
||||
loop := me.forge.Repos.All()
|
||||
for loop.Scan() {
|
||||
repo := loop.Next()
|
||||
me.found.AppendByGoPath(repo)
|
||||
}
|
||||
me.forge.PrintHumanTable(me.found)
|
||||
})
|
||||
box2.NewButton("Prep for release()", func() {
|
||||
r.Disable()
|
||||
defer r.Enable()
|
||||
if IsAnythingDirty() {
|
||||
log.Info("You can't apply patches when repos are dirty")
|
||||
me.forge.PrintHumanTable(me.found)
|
||||
return
|
||||
}
|
||||
if !r.mergeAllUserToDevel() {
|
||||
return
|
||||
}
|
||||
if !r.mergeAllDevelToMain() {
|
||||
return
|
||||
}
|
||||
doAllCheckoutMaster()
|
||||
os.Exit(0)
|
||||
|
||||
})
|
||||
|
||||
return box2
|
||||
}
|
||||
|
||||
func (r *repoWindow) mergeAllDevelToMain() bool {
|
||||
var count int
|
||||
log.Info("merge all here")
|
||||
loop := me.forge.Repos.All()
|
||||
for loop.Scan() {
|
||||
repo := loop.Next()
|
||||
if me.forge.Config.IsReadOnly(repo.GetGoPath()) {
|
||||
log.Info("skipping readonly", repo.GetFullPath())
|
||||
continue
|
||||
}
|
||||
if repo.IsDirty() {
|
||||
log.Info("skipping dirty", repo.GetFullPath())
|
||||
continue
|
||||
}
|
||||
if repo.GetDevelVersion() != repo.GetUserVersion() {
|
||||
log.Info("devel and user branch are different", repo.GetFullPath())
|
||||
continue
|
||||
}
|
||||
if repo.GetDevelVersion() == repo.GetMasterVersion() {
|
||||
log.Info("devel and master branch are the same", repo.GetFullPath())
|
||||
continue
|
||||
}
|
||||
count += 1
|
||||
if result, err := repo.MergeDevelToMaster(); err == nil {
|
||||
log.Warn("MERGE WORKED", repo.GetFullPath())
|
||||
repo.Reload()
|
||||
vrepo := me.repos.View.FindByPath(repo.GetGoPath())
|
||||
if vrepo != nil {
|
||||
vrepo.UpdatePb(repo)
|
||||
vrepo.NewScan()
|
||||
}
|
||||
me.forge.SetConfigSave(true)
|
||||
// continue
|
||||
continue
|
||||
} else {
|
||||
log.Warn("THINGS FAILED ", repo.GetFullPath())
|
||||
log.Warn("err", err)
|
||||
for _, line := range result.Stdout {
|
||||
log.Warn("stdout:", line)
|
||||
}
|
||||
for _, line := range result.Stderr {
|
||||
log.Warn("stderr:", line)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
log.Warn("EVERYTHING WORKED count =", count)
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *repoWindow) mergeAllUserToDevel() bool {
|
||||
log.Info("merge all here")
|
||||
loop := me.forge.Repos.All()
|
||||
for loop.Scan() {
|
||||
repo := loop.Next()
|
||||
if me.forge.Config.IsReadOnly(repo.GetGoPath()) {
|
||||
// log.Info("skipping readonly", repo.GetFullPath())
|
||||
continue
|
||||
}
|
||||
if repo.IsDirty() {
|
||||
log.Info("skipping dirty", repo.GetFullPath())
|
||||
continue
|
||||
}
|
||||
if repo.GetCurrentBranchName() != repo.GetUserBranchName() {
|
||||
log.Info("not on user branch", repo.GetFullPath())
|
||||
continue
|
||||
}
|
||||
if repo.GetDevelVersion() == repo.GetUserVersion() {
|
||||
log.Info("devel and user branch are the same", repo.GetFullPath())
|
||||
continue
|
||||
}
|
||||
if result, err := repo.MergeUserToDevel(); err == nil {
|
||||
log.Warn("THINGS SEEM OK", repo.GetFullPath())
|
||||
repo.Reload()
|
||||
vrepo := me.repos.View.FindByPath(repo.GetGoPath())
|
||||
if vrepo != nil {
|
||||
vrepo.UpdatePb(repo)
|
||||
vrepo.NewScan()
|
||||
}
|
||||
me.forge.SetConfigSave(true)
|
||||
continue
|
||||
} else {
|
||||
log.Warn("THINGS FAILED ", repo.GetFullPath())
|
||||
log.Warn("err", err)
|
||||
for _, line := range result.Stdout {
|
||||
log.Warn("stdout:", line)
|
||||
}
|
||||
for _, line := range result.Stderr {
|
||||
log.Warn("stderr:", line)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
log.Warn("EVERYTHING WORKED")
|
||||
return true
|
||||
}
|
|
@ -0,0 +1,393 @@
|
|||
// 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"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"go.wit.com/lib/debugger"
|
||||
"go.wit.com/lib/gadgets"
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func makeReposWin() *stdReposTableWin {
|
||||
rwin := new(stdReposTableWin)
|
||||
win := gadgets.NewGenericWindow("find errors and try to fix them", "types of errors of some sort or another")
|
||||
rwin.win = win
|
||||
grid := win.Group.RawGrid()
|
||||
|
||||
// win.Top.NewGroup("misc (works in progress)")
|
||||
|
||||
// grid = win.Top.RawGrid()
|
||||
grid = win.Group.RawGrid()
|
||||
var found *gitpb.Repos
|
||||
var txt string
|
||||
|
||||
found = develBehindMasterProblem()
|
||||
txt = fmt.Sprintf("devel is behind master (%d)", found.Len())
|
||||
grid.NewButton(txt, func() {
|
||||
win := gadgets.RawBasicWindow("devel branches that are out of sync with master")
|
||||
win.Make()
|
||||
win.Show()
|
||||
win.Custom = func() {
|
||||
// sets the hidden flag to false so Toggle() works
|
||||
win.Hide()
|
||||
}
|
||||
box := win.Box().NewBox("bw vbox", false)
|
||||
|
||||
found := develBehindMasterProblem()
|
||||
group := box.NewGroup("test buttons")
|
||||
hbox := group.Box().Horizontal()
|
||||
hbox.NewButton("git merge master devel", func() {
|
||||
all := found.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
mname := repo.GetMasterBranchName()
|
||||
dname := repo.GetDevelBranchName()
|
||||
if dname != repo.GetCurrentBranchName() {
|
||||
log.Info("Repo is not on the devel branch", repo.GetGoPath())
|
||||
}
|
||||
cmd := []string{"git", "merge", mname}
|
||||
log.Info(repo.GetGoPath(), cmd)
|
||||
repo.RunVerbose(cmd)
|
||||
}
|
||||
})
|
||||
hbox.NewButton("test", func() {
|
||||
})
|
||||
|
||||
t := makeDevelBehindMaster(found)
|
||||
t.SetParent(box)
|
||||
t.ShowTable()
|
||||
})
|
||||
|
||||
found = remoteUserBranchProblem()
|
||||
txt = fmt.Sprintf("user branch is remote (%d)", found.Len())
|
||||
grid.NewButton(txt, func() {
|
||||
win := gadgets.RawBasicWindow("repos that seem to have remote user branches")
|
||||
win.Make()
|
||||
win.Show()
|
||||
win.Custom = func() {
|
||||
// sets the hidden flag to false so Toggle() works
|
||||
win.Hide()
|
||||
}
|
||||
box := win.Box().NewBox("bw vbox", false)
|
||||
|
||||
found := remoteUserBranchProblem()
|
||||
group := box.NewGroup("test buttons")
|
||||
hbox := group.Box().Horizontal()
|
||||
hbox.NewButton("git branch delete", func() {
|
||||
win.Disable()
|
||||
defer win.Enable()
|
||||
all := found.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
brname := repo.GetUserBranchName()
|
||||
// git push origin --delete jcarr
|
||||
os.Setenv("GIT_TERMINAL_PROMPT", "0")
|
||||
cmd := []string{"git", "push", "origin", "--delete", brname}
|
||||
log.Info("You may want to run:", repo.GetGoPath(), cmd)
|
||||
repo.RunVerbose(cmd)
|
||||
os.Unsetenv("GIT_TERMINAL_PROMPT")
|
||||
|
||||
// git branch --delete --remote origin/jcarr
|
||||
cmd = []string{"git", "branch", "--delete", "--remote", "origin/" + brname}
|
||||
log.Info(repo.GetGoPath(), cmd)
|
||||
repo.RunVerbose(cmd)
|
||||
repo.ReloadCheck()
|
||||
}
|
||||
me.forge.SetConfigSave(true)
|
||||
me.forge.ConfigSave()
|
||||
})
|
||||
|
||||
t := makeStandardReposGrid(found)
|
||||
t.SetParent(box)
|
||||
t.ShowTable()
|
||||
})
|
||||
|
||||
rwin.boxTB = win.Bottom.Box()
|
||||
|
||||
grid.NextRow()
|
||||
|
||||
found = develRemoteProblem()
|
||||
txt = fmt.Sprintf("remote devel != local devel (%d)", found.Len())
|
||||
grid.NewButton(txt, func() {
|
||||
found := develRemoteProblem()
|
||||
makeStandardReposWindow(txt, found)
|
||||
})
|
||||
|
||||
found = masterRemoteProblem()
|
||||
txt = fmt.Sprintf("remote master != local master (%d)", found.Len())
|
||||
grid.NewButton(txt, func() {
|
||||
found := masterRemoteProblem()
|
||||
makeStandardReposWindow(txt, found)
|
||||
})
|
||||
grid.NextRow()
|
||||
|
||||
makeHackModeWindow(rwin)
|
||||
return rwin
|
||||
}
|
||||
|
||||
// table of devel errors behind master
|
||||
func makeDevelBehindMaster(pb *gitpb.Repos) *gitpb.ReposTable {
|
||||
t := pb.NewTable("testDirty")
|
||||
t.NewUuid()
|
||||
sf := t.AddStringFunc("repo", func(r *gitpb.Repo) string {
|
||||
return r.GetGoPath()
|
||||
})
|
||||
sf.Custom = func(r *gitpb.Repo) {
|
||||
log.Info("merge master into devel here", r.GetGoPath())
|
||||
}
|
||||
t.AddTimeFunc("age", func(repo *gitpb.Repo) time.Time {
|
||||
return repo.NewestTime()
|
||||
})
|
||||
t.AddMasterVersion()
|
||||
t.AddDevelVersion()
|
||||
t.AddState()
|
||||
return t
|
||||
}
|
||||
|
||||
// default window for active running droplets
|
||||
func (rwin *stdReposTableWin) doReposTable(pb *gitpb.Repos) {
|
||||
rwin.Lock()
|
||||
defer rwin.Unlock()
|
||||
if rwin.TB != nil {
|
||||
rwin.TB.Delete()
|
||||
rwin.TB = nil
|
||||
}
|
||||
|
||||
rwin.pb = pb
|
||||
|
||||
t := makeStandardReposGrid(pb)
|
||||
t.SetParent(rwin.boxTB)
|
||||
t.ShowTable()
|
||||
rwin.TB = t
|
||||
}
|
||||
|
||||
func makeHackModeWindow(stdwin *stdReposTableWin) {
|
||||
group := stdwin.win.Top.NewGroup("This is a work in progress")
|
||||
grid := group.RawGrid()
|
||||
grid.NewButton("git pull", func() {
|
||||
log.Info("todo: run git pull on each repo")
|
||||
})
|
||||
|
||||
me.repoDevelMergeB = grid.NewButton("merge", func() {
|
||||
found := findMergeToDevel()
|
||||
_, box := makeStandardReposWindow("repos to merge from user to devel", found)
|
||||
hbox := box.Box().Horizontal()
|
||||
hbox.NewButton("merge all", func() {
|
||||
stdwin.win.Disable()
|
||||
defer stdwin.win.Enable()
|
||||
all := found.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if repo.CheckDirty() {
|
||||
log.Info("repo is dirty", repo.GetGoPath())
|
||||
continue
|
||||
}
|
||||
log.Info("Starting merge on", repo.GetGoPath())
|
||||
if repo.CheckoutDevel() {
|
||||
log.Info("checkout devel failed", repo.GetGoPath())
|
||||
return
|
||||
}
|
||||
if _, err := repo.MergeToDevel(); err != nil {
|
||||
log.Info("merge from user failed", repo.GetGoPath(), err)
|
||||
// log.Info(strings.Join(r.Stdout, "\n"))
|
||||
// log.Info(strings.Join(r.Stderr, "\n"))
|
||||
return
|
||||
}
|
||||
if repo.CheckoutMaster() {
|
||||
log.Info("checkout master failed", repo.GetGoPath())
|
||||
return
|
||||
}
|
||||
if _, err := repo.MergeToMaster(); err != nil {
|
||||
log.Info("merge from devel failed", repo.GetGoPath(), err)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
})
|
||||
grid.NextRow()
|
||||
|
||||
group2 := stdwin.win.Top.NewGroup("Merge")
|
||||
grid = group2.RawGrid()
|
||||
|
||||
/*
|
||||
grid.NewButton("merge to devel", func() {
|
||||
stdwin.win.Disable()
|
||||
defer stdwin.win.Enable()
|
||||
|
||||
mergeUserToDevel(true)
|
||||
})
|
||||
|
||||
grid.NewButton("merge to master", func() {
|
||||
stdwin.win.Disable()
|
||||
defer stdwin.win.Enable()
|
||||
|
||||
mergeDevelToMaster(true)
|
||||
})
|
||||
|
||||
grid.NewButton("merge all", func() {
|
||||
stdwin.win.Disable()
|
||||
defer stdwin.win.Enable()
|
||||
|
||||
if err := doAllCheckoutDevel(); err != nil {
|
||||
log.Info("checkout error:", err)
|
||||
} else {
|
||||
log.Info("checkout was ok")
|
||||
}
|
||||
|
||||
mergeUserToDevel(true)
|
||||
|
||||
if err := doAllCheckoutMaster(); err != nil {
|
||||
log.Info("checkout error:", err)
|
||||
} else {
|
||||
log.Info("checkout was ok")
|
||||
}
|
||||
|
||||
mergeDevelToMaster(true)
|
||||
})
|
||||
*/
|
||||
|
||||
grid.NewButton("show dirty repos on win.Bottom", func() {
|
||||
log.Info("try to show dirty repos on bottom")
|
||||
found := me.forge.FindDirty()
|
||||
stdwin.doReposTable(found)
|
||||
})
|
||||
|
||||
group3 := stdwin.win.Top.NewGroup("work in progress")
|
||||
grid = group3.RawGrid()
|
||||
|
||||
grid.NewButton("forge ConfigSave()", func() {
|
||||
me.forge.ConfigSave()
|
||||
})
|
||||
|
||||
grid.NewButton("debugger()", func() {
|
||||
debugger.DebugWindow()
|
||||
})
|
||||
}
|
||||
|
||||
func develBehindMasterProblem() *gitpb.Repos {
|
||||
found := new(gitpb.Repos)
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if repo.GetDevelVersion() == repo.GetMasterVersion() {
|
||||
continue
|
||||
}
|
||||
found.AppendByFullPath(repo)
|
||||
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
func remoteUserBranchProblem() *gitpb.Repos {
|
||||
found := new(gitpb.Repos)
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
username := repo.GetUserBranchName()
|
||||
if repo.IsBranchRemote(username) {
|
||||
found.AppendByFullPath(repo)
|
||||
}
|
||||
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
func develRemoteProblem() *gitpb.Repos {
|
||||
found := new(gitpb.Repos)
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
brname := repo.GetDevelBranchName()
|
||||
if !repo.IsBranchRemote(brname) {
|
||||
// log.Info("repo does not have remote devel branch", repo.GetGoPath())
|
||||
continue
|
||||
}
|
||||
lhash := repo.GetLocalHash(brname)
|
||||
rhash := repo.GetRemoteHash(brname)
|
||||
// log.Info(lhash, rhash, repo.GetGoPath())
|
||||
if lhash == "" || rhash == "" {
|
||||
// something is wrong if either of these are blank
|
||||
found.AppendByFullPath(repo)
|
||||
continue
|
||||
}
|
||||
if lhash == rhash {
|
||||
continue
|
||||
}
|
||||
found.AppendByFullPath(repo)
|
||||
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
func masterRemoteProblem() *gitpb.Repos {
|
||||
found := new(gitpb.Repos)
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
brname := repo.GetMasterBranchName()
|
||||
if !repo.IsBranchRemote(brname) {
|
||||
// log.Info("repo does not have remote devel branch", repo.GetGoPath())
|
||||
continue
|
||||
}
|
||||
lhash := repo.GetLocalHash(brname)
|
||||
rhash := repo.GetRemoteHash(brname)
|
||||
// log.Info(lhash, rhash, repo.GetGoPath())
|
||||
if lhash == "" || rhash == "" {
|
||||
// something is wrong if either of these are blank
|
||||
found.AppendByFullPath(repo)
|
||||
continue
|
||||
}
|
||||
if lhash == rhash {
|
||||
continue
|
||||
}
|
||||
found.AppendByFullPath(repo)
|
||||
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
func makeWritableWindow(pb *gitpb.Repos) (*gadgets.GenericWindow, *gitpb.ReposTable) {
|
||||
win := gadgets.NewGenericWindow("Repos You have write access to", "Configure")
|
||||
t := pb.NewTable("testForgeRepos")
|
||||
t.NewUuid()
|
||||
|
||||
grid := win.Group.RawGrid()
|
||||
grid.NewButton("git pull", func() {
|
||||
log.Info("todo: run git pull on each repo")
|
||||
})
|
||||
|
||||
/*
|
||||
grid.NewButton("do repos.ReScan()", func() {
|
||||
t.Update()
|
||||
})
|
||||
*/
|
||||
|
||||
tbox := win.Bottom.Box()
|
||||
t.SetParent(tbox)
|
||||
|
||||
sf := t.AddStringFunc("repo", func(r *gitpb.Repo) string {
|
||||
return r.GetGoPath()
|
||||
})
|
||||
sf.Custom = func(r *gitpb.Repo) {
|
||||
log.Info("do button click on", r.GetGoPath())
|
||||
}
|
||||
t.AddTimeFunc("age", func(repo *gitpb.Repo) time.Time {
|
||||
return repo.NewestTime()
|
||||
})
|
||||
t.AddMasterVersion()
|
||||
t.AddDevelVersion()
|
||||
t.AddUserVersion()
|
||||
t.AddCurrentBranchName()
|
||||
t.AddState()
|
||||
t.ShowTable()
|
||||
return win, t
|
||||
}
|
|
@ -0,0 +1,185 @@
|
|||
// 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 (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.wit.com/gui"
|
||||
"go.wit.com/lib/gadgets"
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
type stdReposTableWin struct {
|
||||
sync.Mutex
|
||||
win *gadgets.GenericWindow // the machines gui window
|
||||
boxTB *gui.Node // the machines gui parent box widget
|
||||
TB *gitpb.ReposTable // the gui table buffer
|
||||
pb *gitpb.Repos // the current repos protobuf
|
||||
update bool // if the window should be updated
|
||||
}
|
||||
|
||||
func (w *stdReposTableWin) Toggle() {
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
if w.win == nil {
|
||||
return
|
||||
}
|
||||
w.win.Toggle()
|
||||
}
|
||||
|
||||
func makeWindowForPB() *gadgets.GenericWindow {
|
||||
win := gadgets.NewGenericWindow("Forge Repos Protobuf View", "Display Git Repositories")
|
||||
|
||||
return win
|
||||
}
|
||||
|
||||
func makeReposWinNew() *gadgets.GenericWindow {
|
||||
insertWin := makeWindowForPB()
|
||||
insertWin.Win.Custom = func() {
|
||||
log.Info("test delete window here")
|
||||
}
|
||||
grid := insertWin.Group.RawGrid()
|
||||
|
||||
var t *gitpb.ReposTable
|
||||
grid.NewButton("dirty", func() {
|
||||
if t != nil {
|
||||
t.Delete()
|
||||
t = nil
|
||||
}
|
||||
found := me.forge.FindDirty()
|
||||
|
||||
// display the protobuf
|
||||
t = addWindowPB(insertWin, found)
|
||||
f := func(repo *gitpb.Repo) {
|
||||
log.Info("got to ReposTable.Custom() id =", repo.GetGoPath(), repo.GetCurrentVersion())
|
||||
}
|
||||
t.Custom(f)
|
||||
log.Info("table has uuid", t.GetUuid())
|
||||
})
|
||||
|
||||
grid.NewButton("to publish", func() {
|
||||
if t != nil {
|
||||
t.Delete()
|
||||
t = nil
|
||||
}
|
||||
found := findReposWithPatches()
|
||||
me.forge.PrintHumanTable(found)
|
||||
|
||||
// make the window for the first time
|
||||
t = addWindowPB(insertWin, found)
|
||||
f := func(repo *gitpb.Repo) {
|
||||
log.Info("got to ReposTable.Custom() id =", repo.GetGoPath(), repo.GetCurrentVersion())
|
||||
}
|
||||
t.Custom(f)
|
||||
log.Info("table has uuid", t.GetUuid())
|
||||
})
|
||||
|
||||
grid.NewButton("favorites", func() {
|
||||
if t != nil {
|
||||
t.Delete()
|
||||
t = nil
|
||||
}
|
||||
found := new(gitpb.Repos)
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if !me.forge.Config.IsFavorite(repo.GetGoPath()) {
|
||||
continue
|
||||
}
|
||||
found.AppendByFullPath(repo)
|
||||
}
|
||||
|
||||
// make the window for the first time
|
||||
t = addWindowPB(insertWin, found)
|
||||
f := func(repo *gitpb.Repo) {
|
||||
log.Info("got to ReposTable.Custom() id =", repo.GetGoPath(), repo.GetCurrentVersion())
|
||||
}
|
||||
t.Custom(f)
|
||||
log.Info("table has uuid", t.GetUuid())
|
||||
})
|
||||
|
||||
grid.NewButton("writeable", func() {
|
||||
if t != nil {
|
||||
t.Delete()
|
||||
t = nil
|
||||
}
|
||||
found := new(gitpb.Repos)
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if me.forge.Config.IsReadOnly(repo.GetGoPath()) {
|
||||
continue
|
||||
}
|
||||
found.AppendByFullPath(repo)
|
||||
}
|
||||
|
||||
// make the window for the first time
|
||||
t = addWindowPB(insertWin, found)
|
||||
f := func(repo *gitpb.Repo) {
|
||||
log.Info("got to ReposTable.Custom() id =", repo.GetGoPath(), repo.GetCurrentVersion())
|
||||
}
|
||||
t.Custom(f)
|
||||
log.Info("table has uuid", t.GetUuid())
|
||||
})
|
||||
|
||||
grid.NewButton("all", func() {
|
||||
if t != nil {
|
||||
t.Delete()
|
||||
t = nil
|
||||
}
|
||||
found := new(gitpb.Repos)
|
||||
all := me.forge.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
found.AppendByFullPath(repo)
|
||||
|
||||
}
|
||||
// display the protobuf
|
||||
t = addWindowPB(insertWin, found)
|
||||
f := func(repo *gitpb.Repo) {
|
||||
log.Info("got to ReposTable.Custom() id =", repo.GetGoPath(), repo.GetCurrentVersion())
|
||||
}
|
||||
t.Custom(f)
|
||||
log.Info("table has uuid", t.GetUuid())
|
||||
})
|
||||
|
||||
return insertWin
|
||||
}
|
||||
|
||||
func addWindowPB(win *gadgets.GenericWindow, pb *gitpb.Repos) *gitpb.ReposTable {
|
||||
t := pb.NewTable("testForgeRepos")
|
||||
t.NewUuid()
|
||||
tbox := win.Bottom.Box().SetProgName("TBOX")
|
||||
t.SetParent(tbox)
|
||||
|
||||
sf := t.AddStringFunc("repo", func(r *gitpb.Repo) string {
|
||||
return r.GetGoPath()
|
||||
})
|
||||
sf.Custom = func(r *gitpb.Repo) {
|
||||
log.Info("do button click on", r.GetGoPath())
|
||||
}
|
||||
t.AddTimeFunc("age", func(repo *gitpb.Repo) time.Time {
|
||||
return repo.NewestTime()
|
||||
})
|
||||
t.AddMasterVersion()
|
||||
// hmm := t.AddMasterVersion()
|
||||
// hmm.SetTitle("Master")
|
||||
t.AddDevelVersion()
|
||||
t.AddUserVersion()
|
||||
t.AddCurrentBranchName()
|
||||
t.AddState()
|
||||
f := func(repo *gitpb.Repo) string {
|
||||
log.Info("repo =", repo.GetGoPath(), repo.GetCurrentVersion())
|
||||
return repo.GetGoPath()
|
||||
}
|
||||
t.AddButtonFunc("cur version", f)
|
||||
t.ShowTable()
|
||||
return t
|
||||
}
|
|
@ -1,169 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"go.wit.com/lib/gadgets"
|
||||
"go.wit.com/lib/protobuf/forgepb"
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/log"
|
||||
|
||||
"go.wit.com/gui"
|
||||
)
|
||||
|
||||
type patchWindow struct {
|
||||
once sync.Once // only init() the window once
|
||||
win *gadgets.BasicWindow // the patches window
|
||||
stack *gui.Node // the top box set as vertical
|
||||
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
|
||||
}
|
||||
|
||||
// todo: autogenerate these or make them standared 'gui' package functions
|
||||
// make this an go interface somehow
|
||||
|
||||
// is the window hidden right now?
|
||||
func (w *patchWindow) Hidden() bool {
|
||||
return w.win.Hidden()
|
||||
}
|
||||
|
||||
// switches between the window being visable or hidden on the desktop
|
||||
func (w *patchWindow) Toggle() {
|
||||
if w.Hidden() {
|
||||
w.Show()
|
||||
} else {
|
||||
w.Hide()
|
||||
}
|
||||
}
|
||||
|
||||
// hides the window completely
|
||||
func (w *patchWindow) Show() {
|
||||
w.win.Show()
|
||||
}
|
||||
|
||||
func (w *patchWindow) Hide() {
|
||||
w.win.Hide()
|
||||
}
|
||||
|
||||
// should be the first box/widget in the window
|
||||
// greys out the window to the user
|
||||
func (w *patchWindow) Disable() {
|
||||
w.stack.Disable()
|
||||
}
|
||||
|
||||
func (w *patchWindow) Enable() {
|
||||
w.stack.Enable()
|
||||
}
|
||||
|
||||
// you can only have one of these
|
||||
func makePatchWindow(pset *forgepb.Patchset) *patchWindow {
|
||||
pw := new(patchWindow)
|
||||
|
||||
// sync.Once()
|
||||
pw.win = gadgets.RawBasicWindow("Patcheset for " + pset.Name + " (" + pset.Comment + ")")
|
||||
pw.win.Make()
|
||||
|
||||
pw.stack = pw.win.Box().NewBox("bw vbox", false)
|
||||
// me.reposwin.Draw()
|
||||
pw.win.Custom = func() {
|
||||
// sets the hidden flag to false so Toggle() works
|
||||
pw.win.Hide()
|
||||
}
|
||||
|
||||
grid := pw.stack.NewGrid("", 0, 0)
|
||||
|
||||
grid.NewLabel(pset.GitAuthorName)
|
||||
grid.NewLabel(pset.GitAuthorEmail)
|
||||
grid.NextRow()
|
||||
grid.NewLabel("start branch: " + pset.StartBranchName)
|
||||
grid.NewLabel(pset.StartBranchHash)
|
||||
grid.NextRow()
|
||||
grid.NewLabel("end branch: " + pset.EndBranchName)
|
||||
grid.NewLabel(pset.EndBranchHash)
|
||||
grid.NextRow()
|
||||
|
||||
grid.NewButton("Extract files to disk", func() {
|
||||
if err := savePatchset(pset); err != nil {
|
||||
log.Info("Save err:", err)
|
||||
return
|
||||
}
|
||||
})
|
||||
grid.NewButton("Apply with git am", func() {
|
||||
if _, _, _, err := IsEverythingOnDevel(); err != nil {
|
||||
log.Info("You can only apply patches to the devel branch")
|
||||
return
|
||||
}
|
||||
if IsAnythingDirty() {
|
||||
log.Info("You can't apply patches when repos are dirty")
|
||||
me.forge.PrintHumanTable(me.found)
|
||||
return
|
||||
}
|
||||
if argv.Force {
|
||||
applyPatchset(pset)
|
||||
}
|
||||
})
|
||||
|
||||
g := pw.stack.NewGroup("PatchSet List")
|
||||
|
||||
// make a grid and a header
|
||||
filegrid := g.NewGrid("", 0, 0)
|
||||
filegrid.NewLabel("repo")
|
||||
filegrid.NewLabel("patch name")
|
||||
filegrid.NewLabel("Applied in current branch?")
|
||||
filegrid.NewLabel("start hash")
|
||||
filegrid.NextRow()
|
||||
|
||||
// add the patches to the grid
|
||||
pw.addPatchset(filegrid, pset)
|
||||
return pw
|
||||
}
|
||||
|
||||
func (r *patchWindow) addPatchset(grid *gui.Node, pset *forgepb.Patchset) {
|
||||
repomap := make(map[*gitpb.Repo][]*forgepb.Patch)
|
||||
repohash := make(map[*gitpb.Repo]string)
|
||||
|
||||
// sort patches by repo namespace
|
||||
all := pset.Patches.SortByFilename()
|
||||
for all.Scan() {
|
||||
p := all.Next()
|
||||
s := p.RepoNamespace
|
||||
repo := me.forge.FindByGoPath(s)
|
||||
if repo == nil {
|
||||
log.Info("COULD NOT FIND", s)
|
||||
continue
|
||||
}
|
||||
repomap[repo] = append(repomap[repo], p)
|
||||
repohash[repo] = p.StartHash
|
||||
}
|
||||
|
||||
// var repo *gitpb.Repo
|
||||
|
||||
for repo, patches := range repomap {
|
||||
log.Info(repo.GetGoPath())
|
||||
grid.NewLabel(repo.GetGoPath())
|
||||
|
||||
for i, p := range patches {
|
||||
log.Info(i, p.Filename)
|
||||
grid.NewLabel(p.Comment)
|
||||
grid.NewLabel("in current branch?")
|
||||
break
|
||||
}
|
||||
hash := repohash[repo]
|
||||
grid.NewLabel(hash)
|
||||
var win *repoPatchWindow
|
||||
grid.NewButton("View", func() {
|
||||
if win != nil {
|
||||
win.Toggle()
|
||||
log.Info("TRYING TO TOGGLE WINDOW")
|
||||
return
|
||||
}
|
||||
|
||||
win = makeRepoPatchWindow(repo, patches)
|
||||
win.Show()
|
||||
})
|
||||
grid.NextRow()
|
||||
}
|
||||
}
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
@ -12,14 +15,14 @@ import (
|
|||
)
|
||||
|
||||
type repoPatchWindow struct {
|
||||
once sync.Once // only init() the window once
|
||||
win *gadgets.BasicWindow // the patches window
|
||||
stack *gui.Node // the top box set as vertical
|
||||
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
|
||||
once sync.Once // only init() the window once
|
||||
win *gadgets.BasicWindow // the patches window
|
||||
stack *gui.Node // the top box set as vertical
|
||||
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.Set // the patchset in question
|
||||
}
|
||||
|
||||
// todo: autogenerate these or make them standared 'gui' package functions
|
||||
|
@ -78,11 +81,6 @@ func makeRepoPatchWindow(repo *gitpb.Repo, fset []*forgepb.Patch) *repoPatchWind
|
|||
grid.NewLabel(repo.GetGoPath())
|
||||
grid.NewLabel(repo.GetCurrentBranchName())
|
||||
|
||||
grid.NewButton("Apply all with git am", func() {
|
||||
})
|
||||
grid.NewButton("Squash", func() {
|
||||
})
|
||||
|
||||
g := pw.stack.NewGroup("PatchSet List")
|
||||
|
||||
// make a grid and a header
|
||||
|
@ -91,6 +89,10 @@ func makeRepoPatchWindow(repo *gitpb.Repo, fset []*forgepb.Patch) *repoPatchWind
|
|||
filegrid.NewLabel("Applied in current branch?")
|
||||
filegrid.NewLabel("original hash")
|
||||
filegrid.NewLabel("new hash")
|
||||
filegrid.NewButton("git squash", func() {
|
||||
})
|
||||
filegrid.NewButton("git am", func() {
|
||||
})
|
||||
filegrid.NextRow()
|
||||
|
||||
for _, p := range fset {
|
||||
|
@ -98,6 +100,8 @@ func makeRepoPatchWindow(repo *gitpb.Repo, fset []*forgepb.Patch) *repoPatchWind
|
|||
filegrid.NewLabel("yes?")
|
||||
filegrid.NewLabel(p.CommitHash)
|
||||
filegrid.NewLabel(p.NewHash)
|
||||
filegrid.NewCheckbox("").SetChecked(true)
|
||||
filegrid.NewCheckbox("").SetChecked(true)
|
||||
filegrid.NextRow()
|
||||
}
|
||||
// add the patches to the grid
|
||||
|
@ -105,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)
|
||||
|
||||
|
@ -113,7 +117,7 @@ func (r *repoPatchWindow) addPatchset(grid *gui.Node, pset *forgepb.Patchset) {
|
|||
all := pset.Patches.SortByFilename()
|
||||
for all.Scan() {
|
||||
p := all.Next()
|
||||
s := p.RepoNamespace
|
||||
s := p.Namespace
|
||||
repo := me.forge.FindByGoPath(s)
|
||||
if repo == nil {
|
||||
log.Info("COULD NOT FIND", s)
|
||||
|
@ -123,8 +127,6 @@ func (r *repoPatchWindow) addPatchset(grid *gui.Node, pset *forgepb.Patchset) {
|
|||
repohash[repo] = p.StartHash
|
||||
}
|
||||
|
||||
// var repo *gitpb.Repo
|
||||
|
||||
for repo, patches := range repomap {
|
||||
log.Info(repo.GetGoPath())
|
||||
grid.NewLabel(repo.GetGoPath())
|
||||
|
|
Loading…
Reference in New Issue