Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 21 additions & 9 deletions gitcmds/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,38 @@ const (
countOfZerosIn1000 = 3
decimalBase = 10

bitSizeOfInt64 = 64
bitSizeOfInt64 = 64
refsNotes = "refs/notes/*:refs/notes/*"
LargeFileHookFilename = "large-file-hook.sh"
)

const largeFileHookContent = `
#!/bin/bash
totalsize=0
totalcnt=0
readarray -t arr2 < <(git status --porcelain | awk '{if ($1 == "??" || $1 == "A") print $2}')
for row in "${arr2[@]}";do
extension="${row##*.}"
if [ "$extension" != "wasm" ]; then
fs=$(wc -c $row | awk '{print $1}')
totalsize=$(($totalsize+$fs))
totalcnt=$(($totalcnt+1))

# Use -z to get NUL-separated records: "XY<space>path\0"
while IFS= read -r -d '' entry; do
status="${entry:0:2}" # e.g., "??", "A ", "AM", etc.
path="${entry:3}" # skip "XY " to get the path

# consider untracked (??) and added (A*) files
if [[ "$status" == "??" || "$status" == A* ]]; then
extension="${path##*.}"
if [[ "$extension" != "wasm" ]]; then
# wc -c < file gives just the count; quote the path
fs=$(wc -c < "$path")
totalsize=$((totalsize + fs))
totalcnt=$((totalcnt + 1))
fi
fi
done
done < <(git status --porcelain -z)

if (( $totalsize > 100000 )); then
echo " Attempt to commit too large files: Files size = $totalsize"
exit 1
fi

if (( $totalcnt > 200 )); then
echo " Attempt to commit too much files: Files number = $totalcnt"
exit 1
Expand Down
93 changes: 76 additions & 17 deletions gitcmds/gitcmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ const (
originSlash = "origin/"
httppref = "https"
pushYes = "y"
nochecksmsg = "no checks reported"
msgWaitingPR = "Waiting PR checks.."
MsgPreCommitError = "Attempt to commit too"
MsgCommitForNotes = "Commit for keeping notes in branch"
oneSpace = " "
Expand All @@ -51,14 +49,11 @@ const (
userNotFound = "git user name not found"
ErrAlreadyForkedMsg = "you are in fork already\nExecute 'qs dev [branch name]' to create dev branch"
ErrMsgPRNotesImpossible = "pull request without comments is impossible"
ErrTimer40Sec = "time out 40 seconds"
ErrSomethingWrong = "something went wrong"
DefaultCommitMessage = "wip"

IssuePRTtilePrefix = "Resolves issue"
IssueSign = "Resolves #"

prTimeWait = 40
minIssueNoteLength = 10
minPRTitleLength = 8
minRepoNameLength = 4
Expand Down Expand Up @@ -690,7 +685,7 @@ func Upload(cmd *cobra.Command, wd string) error {
// Push notes to origin
err = helper.Retry(func() error {
stdout, stderr, err = new(exec.PipedExec).
Command(git, push, origin, "refs/notes/*:refs/notes/*").
Command(git, push, origin, refsNotes).
WorkingDir(wd).
RunToStrings()
if err != nil {
Expand Down Expand Up @@ -855,7 +850,7 @@ func Download(wd string) error {
// Step 3: git fetch origin --force refs/notes/*:refs/notes/*
err = helper.Retry(func() error {
stdout, stderr, err = new(exec.PipedExec).
Command(git, fetch, origin, "--force", "refs/notes/*:refs/notes/*").
Command(git, fetch, origin, "--force", refsNotes).
WorkingDir(wd).
RunToStrings()
if err != nil {
Expand Down Expand Up @@ -1483,8 +1478,8 @@ func GetIssueRepoFromURL(url string) (repoName string) {
return
}

// DevIssue create link between upstream Guthub issue and dev branch
func DevIssue(wd, parentRepo, githubIssueURL string, issueNumber int, args ...string) (branch string, notes []string, err error) {
// CreateGithubLinkToIssue create a link between an upstream GitHub issue and the dev branch
func CreateGithubLinkToIssue(wd, parentRepo, githubIssueURL string, issueNumber int, args ...string) (branch string, notes []string, err error) {
repo, org, err := GetRepoAndOrgName(wd)
if err != nil {
return "", nil, fmt.Errorf("GetRepoAndOrgName failed: %w", err)
Expand Down Expand Up @@ -1805,12 +1800,12 @@ func GetIssueNameByNumber(issueNum string, parentrepo string) (string, error) {
return strings.TrimSpace(stdout), nil
}

// Dev creates dev branch and pushes it to origin
// CreateDevBranch creates dev branch and pushes it to origin
// Parameters:
// branch - branch name
// notes - notes for branch
// checkRemoteBranchExistence - if true, checks if branch already exists in remote
func Dev(wd, branchName string, notes []string, checkRemoteBranchExistence bool) error {
// checkRemoteBranchExistence - if true, checks if a branch already exists in remote
func CreateDevBranch(wd, branchName string, notes []string, checkRemoteBranchExistence bool) error {
mainBranch, err := GetMainBranch(wd)
if err != nil {
return fmt.Errorf(errMsgFailedToGetMainBranch, err)
Expand All @@ -1837,7 +1832,7 @@ func Dev(wd, branchName string, notes []string, checkRemoteBranchExistence bool)
}

if checkRemoteBranchExistence {
// check if branch already exists in remote
// check if a branch already exists in remote
stdout, stderr, err := new(exec.PipedExec).
Command(git, "ls-remote", "--heads", "origin", branchName).
WorkingDir(wd).
Expand All @@ -1854,6 +1849,7 @@ func Dev(wd, branchName string, notes []string, checkRemoteBranchExistence bool)
}
}

// Create new branch from main
err = new(exec.PipedExec).
Command(git, "checkout", "-B", branchName).
WorkingDir(wd).
Expand All @@ -1864,7 +1860,7 @@ func Dev(wd, branchName string, notes []string, checkRemoteBranchExistence bool)

// Fetch notes from origin before pushing
stdout, stderr, err = new(exec.PipedExec).
Command(git, fetch, "--force", origin, "refs/notes/*:refs/notes/*").
Command(git, fetch, origin, "--force", refsNotes).
WorkingDir(wd).
RunToStrings()
if err != nil {
Expand All @@ -1889,7 +1885,7 @@ func Dev(wd, branchName string, notes []string, checkRemoteBranchExistence bool)
// Push notes to origin with retry
err = helper.Retry(func() error {
stdout, stderr, err = new(exec.PipedExec).
Command(git, push, origin, "refs/notes/*:refs/notes/*").
Command(git, push, origin, refsNotes).
WorkingDir(wd).
RunToStrings()

Expand Down Expand Up @@ -2555,7 +2551,7 @@ func LocalPreCommitHookExist(wd string) (bool, error) {
}

func largeFileHookExist(filepath string) bool {
substring := "large-file-hook.sh"
substring := LargeFileHookFilename
_, _, err := new(exec.PipedExec).Command("grep", "-l", substring, filepath).RunToStrings()

return err == nil
Expand Down Expand Up @@ -2693,7 +2689,7 @@ func fillPreCommitFile(wd, myFilePath string) error {
if err != nil {
return err
}
fName := "/.git/hooks/large-file-hook.sh"
fName := "/.git/hooks/" + LargeFileHookFilename
lfPath := dir + fName

lf, err := os.Create(lfPath)
Expand All @@ -2719,6 +2715,69 @@ func fillPreCommitFile(wd, myFilePath string) error {
return new(exec.PipedExec).Command("chmod", "+x", myFilePath).Run(os.Stdout, os.Stdout)
}

// isLargeFileHookContentUpToDate checks if the current large-file-hook.sh content matches the expected content
func isLargeFileHookContentUpToDate(wd string) (bool, error) {
dir, err := GetRootFolder(wd)
if err != nil {
return false, err
}

hookPath := filepath.Join(dir, ".git", "hooks", LargeFileHookFilename)

// Check if the file exists
if _, err := os.Stat(hookPath); os.IsNotExist(err) {
return false, nil // File doesn't exist, so it's not up to date
}

// Read the current content
currentContent, err := os.ReadFile(hookPath)
if err != nil {
return false, fmt.Errorf("failed to read large file hook: %w", err)
}

// Compare with expected content
return string(currentContent) == largeFileHookContent, nil
}

// updateLargeFileHookContent updates the large-file-hook.sh file with the current content
func updateLargeFileHookContent(wd string) error {
dir, err := GetRootFolder(wd)
if err != nil {
return err
}

hookPath := filepath.Join(dir, ".git", "hooks", LargeFileHookFilename)

// Create or overwrite the hook file
lf, err := os.Create(hookPath)
if err != nil {
return fmt.Errorf("failed to create large file hook: %w", err)
}
defer func() {
_ = lf.Close()
}()

if _, err := lf.WriteString(largeFileHookContent); err != nil {
return fmt.Errorf("failed to write large file hook content: %w", err)
}

return nil
}

// EnsureLargeFileHookUpToDate checks and updates the large file hook if needed
func EnsureLargeFileHookUpToDate(wd string) error {
upToDate, err := isLargeFileHookContentUpToDate(wd)
if err != nil {
return err
}

if !upToDate {
return updateLargeFileHookContent(wd)
}

return nil
}

func UpstreamNotExist(wd string) bool {
return len(getRemotes(wd)) < 2
}
Expand Down
6 changes: 3 additions & 3 deletions gitcmds/pr.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func Pr(wd string, needDraft bool) error {
if branchType == notesPkg.BranchTypeDev {
// Fetch notes from origin before checking if they exist
_, _, err := new(exec.PipedExec).
Command(git, fetch, origin, "--force", "refs/notes/*:refs/notes/*").
Command(git, fetch, origin, "--force", refsNotes).
WorkingDir(wd).
RunToStrings()
if err != nil {
Expand Down Expand Up @@ -134,7 +134,7 @@ func pushPRBranch(wd, prBranchName string) error {
// Push notes to origin
err := helper.Retry(func() error {
_, stderr, err := new(exec.PipedExec).
Command("git", "push", "origin", "refs/notes/*:refs/notes/*").
Command(git, push, origin, refsNotes).
WorkingDir(wd).
RunToStrings()
if err != nil {
Expand Down Expand Up @@ -354,7 +354,7 @@ func createPRBranch(wd, devBranchName string) (string, error) {
// Step 1.1: Fetch notes from origin
err = helper.Retry(func() error {
stdout, stderr, err = new(exec.PipedExec).
Command("git", "fetch", "origin", "--force", "refs/notes/*:refs/notes/*").
Command(git, fetch, origin, "--force", refsNotes).
WorkingDir(wd).
RunToStrings()
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions internal/commands/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ const (
oneSpace = " "
EnvSkipQsVersionCheck = "QS_SKIP_QS_VERSION_CHECK"
minimumCommitMessageLen = 8
fetch = "fetch"
origin = "origin"
git = "git"
refsNotes = "refs/notes/*:refs/notes/*"
)

const (
Expand Down
16 changes: 11 additions & 5 deletions internal/commands/dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import (
"github.com/go-git/go-git/v5/plumbing"
"github.com/spf13/cobra"
"github.com/untillpro/goutils/logger"

"github.com/untillpro/qs/gitcmds"
contextPkg "github.com/untillpro/qs/internal/context"
contextpkg "github.com/untillpro/qs/internal/context"
"github.com/untillpro/qs/internal/helper"
"github.com/untillpro/qs/internal/jira"
"github.com/untillpro/qs/internal/notes"
Expand Down Expand Up @@ -101,7 +102,7 @@ func Dev(cmd *cobra.Command, wd string, args []string) error {
fmt.Print("Dev branch for issue #" + strconv.Itoa(issueNum) + " will be created. Agree?(y/n)")
_, _ = fmt.Scanln(&response)
if response == pushYes {
branch, notes, err = gitcmds.DevIssue(wd, parentRepo, githubIssueURL, issueNum, args...)
branch, notes, err = gitcmds.CreateGithubLinkToIssue(wd, parentRepo, githubIssueURL, issueNum, args...)
if err != nil {
return err
}
Expand All @@ -124,7 +125,7 @@ func Dev(cmd *cobra.Command, wd string, args []string) error {
}

// put branch name to command context
cmd.SetContext(context.WithValue(cmd.Context(), contextPkg.CtxKeyDevBranchName, branch))
cmd.SetContext(context.WithValue(cmd.Context(), contextpkg.CtxKeyDevBranchName, branch))

exists, err := branchExists(wd, branch)
if err != nil {
Expand Down Expand Up @@ -153,7 +154,7 @@ func Dev(cmd *cobra.Command, wd string, args []string) error {
}
}

if err := gitcmds.Dev(wd, branch, notes, checkRemoteBranchExistence); err != nil {
if err := gitcmds.CreateDevBranch(wd, branch, notes, checkRemoteBranchExistence); err != nil {
return err
}
default:
Expand All @@ -166,6 +167,11 @@ func Dev(cmd *cobra.Command, wd string, args []string) error {
if err := setPreCommitHook(wd); err != nil {
logger.Verbose("Error setting pre-commit hook:", err)
}

// Ensure large file hook content is up to date
if err := gitcmds.EnsureLargeFileHookUpToDate(wd); err != nil {
logger.Verbose("Error updating large file hook content:", err)
}
// Unstash changes
if stashedUncommittedChanges {
if err := gitcmds.Unstash(wd); err != nil {
Expand Down Expand Up @@ -208,7 +214,7 @@ func branchExists(wd, branchName string) (bool, error) {
func getArgStringFromClipboard(ctx context.Context) string {
var err error
// context value is first
arg, ok := ctx.Value(contextPkg.CtxKeyClipboard).(string)
arg, ok := ctx.Value(contextpkg.CtxKeyClipboard).(string)
if !ok || len(arg) == 0 {
arg, err = clipboard.ReadAll()
if err != nil {
Expand Down
7 changes: 6 additions & 1 deletion internal/commands/u.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func U(cmd *cobra.Command, cfgUpload vcs.CfgUpload, wd string) error {

// Fetch notes from origin
_, _, err = new(exec.PipedExec).
Command("git", "fetch", "origin", "--force", "refs/notes/*:refs/notes/*").
Command(git, fetch, origin, "--force", refsNotes).
WorkingDir(wd).
RunToStrings()
if err != nil {
Expand All @@ -47,6 +47,11 @@ func U(cmd *cobra.Command, cfgUpload vcs.CfgUpload, wd string) error {
return err
}

// Ensure large file hook content is up to date
if err := gitcmds.EnsureLargeFileHookUpToDate(wd); err != nil {
logger.Verbose("Error updating large file hook content:", err)
}

return gitcmds.Upload(cmd, wd)
}

Expand Down
Loading
Loading