Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
G

gopatch

> 编程语言
Open source

Refactoring and code transformation tool for Go.

1.0K stars0 likes0 views
WebsiteGitHub

About

Refactoring and code transformation tool for Go.

gopatch

gopatch is a tool to match and transform Go code. It is meant to aid in refactoring and restyling.

Table of contents

  • Introduction
  • Getting started
    • Installation
    • Your first patch
    • Apply the patch
    • Next steps
  • Usage
    • Options
  • Patches
    • Metavariables
    • Statements
    • Elision
    • Comments
      • Description comments
      • Usage in diff mode
  • Examples
  • Project status
    • Goals
    • Known issues
    • Upcoming
  • Similar Projects
  • Credits

Introduction

gopatch operates like the Unix patch tool: given a patch file and another file as input, it applies the changes specified in the patch to the provided file.

 .-------.                      .-------.
/_|      |.                    /_|      |.
|        ||.    +---------+    |        ||.
|   .go  |||>-->| gopatch |>-->|   .go  |||
|        |||    +---------+    |        |||
'--------'||      ^            '--------'||
 '--------'|      |             '--------'|
  '--------'      |              '--------'
     .-------.    |
    /_|      |    |
    |        +----'
    | .patch |
    |        |
    '--------'

What specifically differentiates it from patch is that unlike plain text transformations, it can be smarter because it understands Go syntax.

Getting started

Installation

Download a pre-built binary of gopatch from the Releases page or by running the following command in your terminal and place it on your $PATH.

bash
VERSION=0.4.0
URL="https://github.com/uber-go/gopatch/releases/download/v$VERSION/gopatch_${VERSION}_$(uname -s)_$(uname -m).tar.gz"
curl -L "$URL" | tar xzv gopatch

Alternatively, if you have Go installed, build it from source and install it with the following command.

bash
go install github.com/uber-go/gopatch@latest

Note: If you're using Go ~/s1028.patch

Replace redundant fmt.Sprintf with fmt.Errorf

@@ @@ -import "errors"

-errors.New(fmt.Sprintf(...)) +fmt.Errorf(...)


This patch is a fix for staticcheck [S1028]. It searches for uses of
[`fmt.Sprintf`] with [`errors.New`], and simplifies them by replacing them
with [`fmt.Errorf`].

  [S1028]: https://staticcheck.io/docs/checks#S1028
  [`fmt.Sprintf`]: https://golang.org/pkg/fmt/#Sprintf
  [`errors.New`]: https://golang.org/pkg/errors/#New
  [`fmt.Errorf`]: https://golang.org/pkg/fmt/#Errorf

For example,

```go
return errors.New(fmt.Sprintf("invalid port: %v", err))
// becomes
return fmt.Errorf("invalid port: %v", err)

Apply the patch

  • cd to your Go project's directory.
bash
$ cd ~/go/src/example.com/myproject

Run gopatch on the project, supplying the previously written patch with the -p flag.

bash
$ gopatch -p ~/s1028.patch ./...

This will apply the patch on all Go code in your project.

Check if there were any instances of this issue in your code by running git diff.

  • Instead, cd to your Go project's directory.

    bash
    $ cd ~/go/src/example.com/myproject

    Run gopatch on the project, supplying the previously written patch with the -p flag along with '-d' flag.

    bash
    $ gopatch -d -p ~/s1028.patch ./...

    This will turn on diff mode and will write the diff to stdout instead of modifying all the Go code in your project. To provide more context on what the patch does, if there were description comments in the patch, they will also get displayed at the top. To learn more about description comments jump to section here

    For example if we applied patch ~/s1028 to our testfile error.go

    bash
    $ gopatch -d -p ~/s1028.patch ./testdata/test_files/diff_example/

    Output would be :

    gopatch/testdata/test_files/diff_example/error.go:Replace redundant fmt.Sprintf with fmt.Errorf
    --- gopatch/testdata/test_files/diff_example/error.go
    +++ gopatch/testdata/test_files/diff_example/error.go
    @@ -7,7 +7,7 @@
    
    func foo() error {
            err := errors.New("test")
    -       return errors.New(fmt.Sprintf("error: %v", err))
    +       return fmt.Errorf("error: %v", err)
    }
    
     func main() {
    

    Note: Only the description comments of patches that actually apply are displayed.

Next steps

To learn how to write your own patches, move on to the Patches section. To dive deeper into patches, check out Patches in depth.

To experiment with other sample patches, check out the Examples section.

Usage

To use the gopatch command line tool, provide the following arguments.

gopatch [options] pattern ...

Where pattern specifies one or more Go files, or directories containing Go files. For directories, all Go code inside them and their descendants will be considered by gopatch.

Options

gopatch supports the following command line options.

  • -p file, --patch=file

    Path to a patch file specifying a transformation. Read more about the patch file format in Patches.

    Provide this flag multiple times to apply multiple patches in-order.

    bash
    $ gopatch -p foo.patch -p bar.patch path/to/my/project

    If this flag is omitted, a patch is expected on stdin.

    bash
    $ gopatch path/to/my/project  **What is a Go expression?**

Expressions usually refer to code that has value. You can pass these as arguments to functions. These include x, foo(), user.Name, etc.

Check the Identifiers vs expressions vs statements section of the appendix for more.

So the following patch will search for invocations of foo with a single argument---any argument---and replace them with invocations of bar with the same argument.

diff
@@
var x expression
@@
-foo(x)
+bar(x)
Input Output
foo(42) bar(42)
foo(answer) bar(answer)
foo(getAnswer()) bar(getAnswer())

Metavariables hold the entire matched value, so we can add code around them without risk of breaking anything.

diff
@@
var x expression
@@
-foo(x)
+bar(x + 3, true)
Input Output
foo(42) bar(42 + 3, true)
foo(answer) bar(answer + 3, true)
foo(getAnswer()) bar(getAnswer() + 3, true)

For more on metavariables see Patches in depth/Metavariables.

Statements

gopatch patches are not limited to transforming basic expressions. You can also transform statements.

What is a Go statements?

Statements are instructions to do things, and do not have value. They cannot be passed as parameters to other functions. These include assignments (foo := bar()), if statements (if foo { bar() }), variable declarations (var foo Bar), and so on.

Check the Identifiers vs expressions vs statements section of the appendix for more.

For example, consider the following patch.

diff
@@
var f expression
var err identifier
@@
-err = f
-if err != nil {
+if err := f; err != nil {
   return err
 }

The patch declares two metavariables:

  • f: This represents an operation that possibly returns an error
  • err: This represents the name of the error variable

The patch will search for code that assigns to an error variable immediately before returning it, and inlines the assignment into the if statement. This effectively reduces the scope of the variable to just the if statement.

InputOutput

go
err = foo(bar, baz)
if err != nil {
   return err
}
go
if err := foo(bar, baz); err != nil {
   return err
}
go
err = comment.Submit(ctx)
if err != nil {
  return err
}
go
if err := comment.Submit(ctx); err != nil {
  return err
}

For more on transforming statements, see Patches In Depth/Statements.

Elision

Matching a single argument is still too selective and we may want to match a wider criteria.

For this, gopatch supports elision of code by adding ... in many places. For example,

diff
@@
@@
-foo(...)
+bar(...)

The patch above looks for all calls to the function foo and replaces them with calls to the function bar, regardless of the number of arguments they have.

Input Output
foo(42) bar(42)
foo(42, true, 1) bar(42, true, 1)
foo(getAnswer(), x(y())) bar(getAnswer(), x(y()))

Going back to the patch from Statements, we can instead write the following patch.

diff
@@
var f expression
var err identifier
@@
-err = f
-if err != nil {
+if err := f; err != nil {
   return ..., err
 }

This patch is almost exactly the same as before except the return statement was changed to return ..., err. This will allow the patch to operate even on functions that return multiple values.

InputOutput

go
err = foo()
if err != nil {
   return false, err
}
go
if err := foo(); err != nil {
   return false, err
}

For more on elision, see Patches in depth/Elision.

Comments

Patches come with comments to give more context about what they do.

Comments are prefixed by '#'

For example:

# Replace time.Now().Sub(x) with time.Since(x)
@@
# var x is in the metavariable section 
var x identifier
@@

-time.Now().Sub(x)
+time.Since(x)
# We replace time.Now().Sub(x)
# with time.Since(x)

Description comments

Description comments are comments that appear directly above a patch's first @@ line. gopatch will record these descriptions and display them to users with use of the --diff or --print-only flags.

For example,

# Replace time.Now().Sub(x) with time.Since(x)
@@
# Not a description comment
var x identifier
@@

-time.Now().Sub(x)
+time.Since(x)
# Not a description comment
# Not a description comment

Patch files with multiple patches can have a separate description for each patch.

# Replace redundant fmt.Sprintf with fmt.Errorf
@@
@@

-import "errors"
-errors.New(fmt.Sprintf(...))
+fmt.Errorf(...)

# Replace time.Now().Sub(x) with time.Since(x)
@@
var x identifier
@@

-time.Now().Sub(x)
+time.Since(x)
# Not a description comment

As these are messages that will be printed to users of the patch, we recommend the following best practices for description comments.

  • Keep them short and on a single-line
  • Use imperative mood ("replace X with Y", not "replaces X with Y")

Usage with --diff

When diff mode is turned on by the -d/--diff flag, gopatch will print description comments for patches that matched different files to stderr.

bash
$ gopatch -d -p ~/s1028.patch testdata/test_files/diff_example/error.go
error.go:Replace redundant fmt.Sprintf with fmt.Errorf
--- error.go
+++ error.go
@@ -7,7 +7,7 @@

func foo() error {
        err := errors.New("test")
-

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

Gogogolangrefactoring

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言