{"id":13499211,"url":"https://github.com/uber-go/gopatch","last_synced_at":"2025-05-15T00:11:06.801Z","repository":{"id":38085911,"uuid":"317234849","full_name":"uber-go/gopatch","owner":"uber-go","description":"Refactoring and code transformation tool for Go.","archived":false,"fork":false,"pushed_at":"2025-02-11T08:36:20.000Z","size":720,"stargazers_count":988,"open_issues_count":31,"forks_count":37,"subscribers_count":13,"default_branch":"main","last_synced_at":"2025-04-13T21:34:03.113Z","etag":null,"topics":["go","golang","refactoring"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/uber-go.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-11-30T13:37:43.000Z","updated_at":"2025-04-10T11:40:58.000Z","dependencies_parsed_at":"2023-12-05T20:23:38.258Z","dependency_job_id":"6f12c3b7-0837-422a-b04c-dad7d71b501e","html_url":"https://github.com/uber-go/gopatch","commit_stats":{"total_commits":215,"total_committers":10,"mean_commits":21.5,"dds":0.4651162790697675,"last_synced_commit":"2ae6b0237ebe9abd59e5a773eb09be333d907b62"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/uber-go%2Fgopatch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/uber-go%2Fgopatch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/uber-go%2Fgopatch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/uber-go%2Fgopatch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/uber-go","download_url":"https://codeload.github.com/uber-go/gopatch/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254249206,"owners_count":22039029,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","golang","refactoring"],"created_at":"2024-07-31T22:00:30.866Z","updated_at":"2025-05-15T00:11:01.792Z","avatar_url":"https://github.com/uber-go.png","language":"Go","funding_links":[],"categories":["Write code to change code","Go"],"sub_categories":["golang"],"readme":"# gopatch [![Go](https://github.com/uber-go/gopatch/actions/workflows/go.yml/badge.svg)](https://github.com/uber-go/gopatch/actions/workflows/go.yml) [![codecov](https://codecov.io/gh/uber-go/gopatch/branch/main/graph/badge.svg?token=tFsx23GSTB)](https://codecov.io/gh/uber-go/gopatch)\n\ngopatch is a tool to match and transform Go code. It is meant to aid in\nrefactoring and restyling.\n\n# Table of contents\n\n- [Introduction](#introduction)\n- [Getting started](#getting-started)\n  - [Installation](#installation)\n  - [Your first patch](#your-first-patch)\n  - [Apply the patch](#apply-the-patch)\n  - [Next steps](#next-steps)\n- [Usage](#usage)\n  - [Options](#options)\n- [Patches](#patches)\n  - [Metavariables](#metavariables)\n  - [Statements](#statements)\n  - [Elision](#elision)\n  - [Comments](#comments)\n    - [Description comments](#description-comments)\n    - [Usage in diff mode](#usage-during-diff-mode)\n- [Examples](#examples)\n- [Project status](#project-status)\n  - [Goals](#goals)\n  - [Known issues](#known-issues)\n  - [Upcoming](#upcoming)\n- [Similar Projects](#similar-projects)\n- [Credits](#credits)\n\n# Introduction\n\ngopatch operates like the Unix `patch` tool: given a patch file and another\nfile as input, it applies the changes specified in the patch to the provided\nfile.\n\n```\n .-------.                      .-------.\n/_|      |.                    /_|      |.\n|        ||.    +---------+    |        ||.\n|   .go  |||\u003e--\u003e| gopatch |\u003e--\u003e|   .go  |||\n|        |||    +---------+    |        |||\n'--------'||      ^            '--------'||\n '--------'|      |             '--------'|\n  '--------'      |              '--------'\n     .-------.    |\n    /_|      |    |\n    |        +----'\n    | .patch |\n    |        |\n    '--------'\n```\n\nWhat specifically differentiates it from `patch` is that unlike plain text\ntransformations, it can be smarter because it understands Go syntax.\n\n# Getting started\n\n## Installation\n\nDownload a **pre-built binary** of gopatch from the [Releases page] or by\nrunning the following command in your terminal and place it on your `$PATH`.\n\n  [Releases page]: https://github.com/uber-go/gopatch/releases\n\n```bash\nVERSION=0.4.0\nURL=\"https://github.com/uber-go/gopatch/releases/download/v$VERSION/gopatch_${VERSION}_$(uname -s)_$(uname -m).tar.gz\"\ncurl -L \"$URL\" | tar xzv gopatch\n```\n\nAlternatively, if you have Go installed, **build it from source** and install\nit with the following command.\n\n```bash\ngo install github.com/uber-go/gopatch@latest\n```\nNote: If you're using Go \u003c 1.16, use `go get github.com/uber-go/gopatch@latest` instead.\n\n## Your first patch\n\nWrite your first patch.\n\n```shell\n$ cat \u003e ~/s1028.patch\n# Replace redundant fmt.Sprintf with fmt.Errorf\n@@\n@@\n-import \"errors\"\n\n-errors.New(fmt.Sprintf(...))\n+fmt.Errorf(...)\n```\n\nThis patch is a fix for staticcheck [S1028]. It searches for uses of\n[`fmt.Sprintf`] with [`errors.New`], and simplifies them by replacing them\nwith [`fmt.Errorf`].\n\n  [S1028]: https://staticcheck.io/docs/checks#S1028\n  [`fmt.Sprintf`]: https://golang.org/pkg/fmt/#Sprintf\n  [`errors.New`]: https://golang.org/pkg/errors/#New\n  [`fmt.Errorf`]: https://golang.org/pkg/fmt/#Errorf\n\nFor example,\n\n```go\nreturn errors.New(fmt.Sprintf(\"invalid port: %v\", err))\n// becomes\nreturn fmt.Errorf(\"invalid port: %v\", err)\n```\n\n## Apply the patch\n\n-  `cd` to your Go project's directory.\n\n  ```shell\n  $ cd ~/go/src/example.com/myproject\n  ```\n\n  Run `gopatch` on the project, supplying the previously written patch with the\n  `-p` flag.\n\n  ```shell\n  $ gopatch -p ~/s1028.patch ./...\n  ```\n\n  This will apply the patch on all Go code in your project.\n\n  Check if there were any instances of this issue in your code by running\n  `git diff`.\n- Instead, `cd` to your Go project's directory.\n\n  ```shell\n  $ cd ~/go/src/example.com/myproject\n  ```\n\n  Run `gopatch` on the project, supplying the previously written patch with the\n  `-p` flag along with '-d' flag.\n\n  ```shell\n  $ gopatch -d -p ~/s1028.patch ./...\n  ```\n\n  This will turn on diff mode and will write the diff to stdout instead of modifying all\n  the Go code in your project. To provide more context on what the patch does, if\n  there were description comments in the patch, they will also get displayed at \n  the top. To learn more about description comments jump to section [here](#description-comments)\n  \n  For example if we applied patch ~/s1028 to our testfile error.go\n  ```shell\n  $ gopatch -d -p ~/s1028.patch ./testdata/test_files/diff_example/\n  ```\n  Output would be : \n  ```\n  gopatch/testdata/test_files/diff_example/error.go:Replace redundant fmt.Sprintf with fmt.Errorf\n  --- gopatch/testdata/test_files/diff_example/error.go\n  +++ gopatch/testdata/test_files/diff_example/error.go\n  @@ -7,7 +7,7 @@\n \n  func foo() error {\n          err := errors.New(\"test\")\n  -       return errors.New(fmt.Sprintf(\"error: %v\", err))\n  +       return fmt.Errorf(\"error: %v\", err)\n  }\n \n   func main() {\n\n  ```\n  Note: Only the description comments of patches that actually **apply** are displayed.\n\n## Next steps\n\nTo learn how to write your own patches, move on to the [Patches] section. To\ndive deeper into patches, check out [Patches in depth].\n\n  [Patches in depth]: docs/PatchesInDepth.md\n\nTo experiment with other sample patches, check out the [Examples] section.\n\n  [Patches]: #patches\n  [Examples]: #examples\n\n# Usage\n\nTo use the gopatch command line tool, provide the following arguments.\n\n```\ngopatch [options] pattern ...\n```\n\nWhere pattern specifies one or more Go files, or directories containing Go\nfiles. For directories, all Go code inside them and their descendants will be\nconsidered by gopatch.\n\n## Options\n\ngopatch supports the following command line options.\n\n- `-p file`, `--patch=file`\n\n    Path to a patch file specifying a transformation. Read more about the\n    patch file format in [Patches].\n\n    Provide this flag multiple times to apply multiple patches in-order.\n\n    ```shell\n    $ gopatch -p foo.patch -p bar.patch path/to/my/project\n    ```\n\n    If this flag is omitted, a patch is expected on stdin.\n\n    ```shell\n    $ gopatch path/to/my/project \u003c\u003c EOF\n    @@\n    @@\n    -foo\n    +bar\n    EOF\n    ```\n- `-d`, `--diff`\n\n    Flag to turn on diff mode. Provide this flag to write the diff to stdout instead\n    of modifying the file and display applied patches' [description comments](#description-comments) if they exist. \n    Use in conjunction with -p to provide patch file.\n    \n    Only need to apply the flag once to turn on diff mode\n\n    ```shell\n    $ gopatch -d -p foo.patch -p bar.patch path/to/my/project\n    ```\n\n    If this flag is omitted, normal patching occurs which modifies the\n    file instead.\n- `--print-only`\n  \n  Flag to turn on print-only mode. Provide this flag to write the changed code to stdout instead of modifying the\n  file and display applied patches' description comments to stderr if they exist.\n  \n    ```shell\n    $ gopatch --print-only -p foo.patch -p bar.patch path/to/my/project\n    ```  \n- `--skip-import-processing`\n  \n  Flag to turn on skip-import-processing mode. Provide this flag to disable\n  import formatting for imports that were not part of the patch changes.\n    ```shell\n    $ gopatch --skip-import-processing -p foo.patch -p bar.patch path/to/my/project\n    ```\n\n- `--skip-generated`\n  \n  Flag to turn on skip-generated code mode. Provide this flag to skip running the\n  tool on generated code. A file is considered containing generated code if it \n  has `@generated` or `^// Code generated .* DO NOT EDIT\\.$` in the comment\n  header.\n    ```shell\n    $ gopatch --skip-generated -p foo.patch -p bar.patch path/to/my/project\n    ```\n\n# Patches\n\nPatch files are the input to gopatch that specify how to transform code. Each\npatch file contains one or more patches. This section provides an introduction\nto writing patches; look at [Patches in depth] for a more detailed\nexplanation.\n\nEach patch specifies a code transformation. These are formatted like unified\ndiffs: lines prefixed with `-` specify matching code should be deleted, and\nlines prefixed with `+` specify that new code should be added.\n\nConsider the following patch.\n\n```diff\n@@\n@@\n-foo\n+bar\n```\n\nIt specifies that we want to search for references to the identifier `foo` and\nreplace them with references to `bar`. (Ignore the lines with `@@` for now.\nWe will cover those below.)\n\nA more selective version of this patch will search for uses of `foo` where it\nis called as a function with specific arguments.\n\n```diff\n@@\n@@\n-foo(42)\n+bar(42)\n```\n\nThis will search for invocations of `foo` as a function with the specified\nargument, and replace only those with `bar`.\n\ngopatch understands Go syntax, so the above is equivalent to the following.\n\n```diff\n@@\n@@\n-foo(\n+bar(\n  42,\n )\n```\n\n## Metavariables\n\nSearching for hard-coded exact parameters is limited. We should be able to\ngeneralize our patches.\n\nThe previously ignored `@@` section of patches is referred to as the\n**metavariable section**. That is where we specify **metavariables** for the\npatch.\n\nMetavariables will match any code, to be reproduced later. Think of them like\nholes to be filled by the code we match. For example,\n\n```diff\n@@\nvar x expression\n@@\n# rest of the patch\n```\n\nThis specifies that `x` should match any Go expression and record its match\nfor later reuse.\n\n\u003e **What is a Go expression?**\n\u003e\n\u003e Expressions usually refer to code that has value. You can pass these as\n\u003e arguments to functions. These include `x`, `foo()`, `user.Name`, etc.\n\u003e\n\u003e Check the [Identifiers vs expressions vs statements] section of the appendix\n\u003e for more.\n\n  [Identifiers vs expressions vs statements]: docs/Appendix.md#identifiers-vs-expressions-vs-statements\n\nSo the following patch will search for invocations of `foo` with a single\nargument---any argument---and replace them with invocations of `bar` with the\nsame argument.\n\n```diff\n@@\nvar x expression\n@@\n-foo(x)\n+bar(x)\n```\n\n| Input              | Output             |\n|--------------------|--------------------|\n| `foo(42)`          | `bar(42)`          |\n| `foo(answer)`      | `bar(answer)`      |\n| `foo(getAnswer())` | `bar(getAnswer())` |\n\n\nMetavariables hold the entire matched value, so we can add code around them\nwithout risk of breaking anything.\n\n```diff\n@@\nvar x expression\n@@\n-foo(x)\n+bar(x + 3, true)\n```\n\n| Input              | Output                       |\n|--------------------|------------------------------|\n| `foo(42)`          | `bar(42 + 3, true)`          |\n| `foo(answer)`      | `bar(answer + 3, true)`      |\n| `foo(getAnswer())` | `bar(getAnswer() + 3, true)` |\n\nFor more on metavariables see [Patches in depth/Metavariables].\n\n  [Patches in depth/Metavariables]: docs/PatchesInDepth.md#metavariables\n\n## Statements\n\ngopatch patches are not limited to transforming basic expressions. You can\nalso transform statements.\n\n\u003e **What is a Go statements?**\n\u003e\n\u003e Statements are instructions to do things, and do not have value. They cannot\n\u003e be passed as parameters to other functions. These include assignments\n\u003e (`foo := bar()`), if statements (`if foo { bar() }`), variable declarations\n\u003e (`var foo Bar`), and so on.\n\u003e\n\u003e Check the [Identifiers vs expressions vs statements] section of the appendix\n\u003e for more.\n\nFor example, consider the following patch.\n\n```diff\n@@\nvar f expression\nvar err identifier\n@@\n-err = f\n-if err != nil {\n+if err := f; err != nil {\n   return err\n }\n```\n\nThe patch declares two metavariables:\n\n- `f`: This represents an operation that possibly returns an `error`\n- `err`: This represents the name of the `error` variable\n\nThe patch will search for code that assigns to an error variable immediately\nbefore returning it, and inlines the assignment into the `if` statement. This\neffectively [reduces the scope of the variable] to just the `if` statement.\n\n  [reduces the scope of the variable]: https://github.com/uber-go/guide/blob/master/style.md#reduce-scope-of-variables\n\n\u003ctable\u003e\n\u003cthead\u003e\u003ctr\u003e\u003cth\u003eInput\u003c/th\u003e\u003cth\u003eOutput\u003c/th\u003e\u003c/tr\u003e\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\u003ctd\u003e\n\n```go\nerr = foo(bar, baz)\nif err != nil {\n   return err\n}\n```\n\n\u003c/td\u003e\u003ctd\u003e\n\n```go\nif err := foo(bar, baz); err != nil {\n   return err\n}\n```\n\n\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e\n\n```go\nerr = comment.Submit(ctx)\nif err != nil {\n  return err\n}\n```\n\n\u003c/td\u003e\u003ctd\u003e\n\n```go\nif err := comment.Submit(ctx); err != nil {\n  return err\n}\n```\n\n\u003c/td\u003e\u003c/tr\u003e\n\u003c/tbody\u003e\u003c/table\u003e\n\nFor more on transforming statements, see [Patches In Depth/Statements].\n\n  [Patches In Depth/Statements]: docs/PatchesInDepth.md#statements\n\n## Elision\n\nMatching a single argument is still too selective and we may want to match a\nwider criteria.\n\nFor this, gopatch supports **elision** of code by adding `...` in many places.\nFor example,\n\n```diff\n@@\n@@\n-foo(...)\n+bar(...)\n```\n\nThe patch above looks for all calls to the function `foo` and replaces them\nwith calls to the function `bar`, regardless of the number of arguments they\nhave.\n\n| Input                      | Output                     |\n|----------------------------|----------------------------|\n| `foo(42)`                  | `bar(42)`                  |\n| `foo(42, true, 1)`         | `bar(42, true, 1)`         |\n| `foo(getAnswer(), x(y()))` | `bar(getAnswer(), x(y()))` |\n\nGoing back to the patch from [Statements], we can instead write the following\npatch.\n\n  [Statements]: #statements\n\n```diff\n@@\nvar f expression\nvar err identifier\n@@\n-err = f\n-if err != nil {\n+if err := f; err != nil {\n   return ..., err\n }\n```\n\nThis patch is almost exactly the same as before except the `return` statement\nwas changed to `return ..., err`. This will allow the patch to operate even on\nfunctions that return multiple values.\n\n\u003ctable\u003e\n\u003cthead\u003e\u003ctr\u003e\u003cth\u003eInput\u003c/th\u003e\u003cth\u003eOutput\u003c/th\u003e\u003c/tr\u003e\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\u003ctd\u003e\n\n```go\nerr = foo()\nif err != nil {\n   return false, err\n}\n```\n\n\u003c/td\u003e\u003ctd\u003e\n\n```go\nif err := foo(); err != nil {\n   return false, err\n}\n```\n\n\u003c/td\u003e\u003c/tr\u003e\n\u003c/tbody\u003e\u003c/table\u003e\n\nFor more on elision, see [Patches in depth/Elision].\n\n  [Patches in depth/Elision]: docs/PatchesInDepth.md#elision\n\n## Comments\n\nPatches come with comments to give more context about what they do.\n\nComments are prefixed by '#'\n\nFor example:\n\n```\n# Replace time.Now().Sub(x) with time.Since(x)\n@@\n# var x is in the metavariable section \nvar x identifier\n@@\n\n-time.Now().Sub(x)\n+time.Since(x)\n# We replace time.Now().Sub(x)\n# with time.Since(x)\n```\n\n#### Description comments\n\nDescription comments are comments that appear directly above a patch's first\n`@@` line.\ngopatch will record these descriptions and display them to users with use of\nthe `--diff` or `--print-only` flags.\n\nFor example,\n\n```\n# Replace time.Now().Sub(x) with time.Since(x)\n@@\n# Not a description comment\nvar x identifier\n@@\n\n-time.Now().Sub(x)\n+time.Since(x)\n# Not a description comment\n# Not a description comment\n```\n\nPatch files with multiple patches can have a separate description for each\npatch.\n\n```\n# Replace redundant fmt.Sprintf with fmt.Errorf\n@@\n@@\n\n-import \"errors\"\n-errors.New(fmt.Sprintf(...))\n+fmt.Errorf(...)\n\n# Replace time.Now().Sub(x) with time.Since(x)\n@@\nvar x identifier\n@@\n\n-time.Now().Sub(x)\n+time.Since(x)\n# Not a description comment\n```\n\nAs these are messages that will be printed to users of the patch,\nwe recommend the following best practices for description comments.\n\n- Keep them short and on a single-line\n- Use imperative mood (\"replace X with Y\", not \"replaces X with Y\")\n\n#### Usage with `--diff`\n\nWhen diff mode is turned on by the `-d`/`--diff` flag, gopatch will print\ndescription comments for patches that matched different files to stderr.\n\n```shell\n$ gopatch -d -p ~/s1028.patch testdata/test_files/diff_example/error.go\nerror.go:Replace redundant fmt.Sprintf with fmt.Errorf\n--- error.go\n+++ error.go\n@@ -7,7 +7,7 @@\n\nfunc foo() error {\n        err := errors.New(\"test\")\n-       return errors.New(fmt.Sprintf(\"error: %v\", err))\n+       return fmt.Errorf(\"error: %v\", err)\n}\n\n func main() {\n```\n\nNote that gopatch will print only the description comments in diff mode.\nOther comments will be ignored.\n\n# Examples\n\nThis section lists various example patches you can try in your code.\nNote that some of these patches are not perfect and may have false positives.\n\n- [s1012.patch](examples/s1012.patch): Fix for staticcheck [S1012](https://staticcheck.io/docs/checks#S1012).\n- [s1028.patch](examples/s1028.patch): Fix for staticcheck [S1028](https://staticcheck.io/docs/checks#S1028).\n- [s1038.patch](examples/s1038.patch): Fix for staticcheck [S1038](https://staticcheck.io/docs/checks#S1038).\n- [gomock-v1.5.0.patch](examples/gomock-v1.5.0.patch): Drops unnecessary call to `Finish` method for users of gomock.\n- [destutter.patch](examples/destutter.patch): Demonstrates renaming a type and updating its consumers.\n\n# Project status\n\nThe project is currently is in a beta state. It works but significant features\nare planned that may result in breaking changes to the patch format.\n\n## Goals\n\ngopatch aims to be a generic power tool that you can use in lieu of simple\nsearch-and-replace.\n\ngopatch will attempt to do 80% of the work for you in a transformation, but it\ncannot guarantee 100% correctness or completeness. Part of this is owing to\nthe decision that gopatch must be able to operate on code that doesn't yet\ncompile, which can often be the case in the middle of a refactor. We may add\nfeatures in the future that require compilable code, but we plan to always\nsupport transformation of partially-valid Go code.\n\n## Known issues\n\nBeyond the known issues highlighted above, there are a handful of other issues\nwith using gopatch today.\n\n- It's very quiet, so there's no indication of progress. [#7]\n- Error messages for invalid patch files are hard to decipher. [#8]\n- Matching elisions between the `-` and `+` sections does not always work in a\n  desirable way. We may consider replacing anonymous `...` elision with a\n  different named elision syntax to address this issue. [#9]\n- When elision is used, gopatch stops replacing after the first instance in\n  the given scope which is often not what you want. [#10]\n- Formatting of output generated by gopatch isn't always perfect.\n\n  [#7]: https://github.com/uber-go/gopatch/issues/7\n  [#8]: https://github.com/uber-go/gopatch/issues/8\n  [#9]: https://github.com/uber-go/gopatch/issues/9\n  [#10]: https://github.com/uber-go/gopatch/issues/10\n\n## Upcoming\n\nBesides addressing the various limitations and issues we've already mentioned,\nwe have a number of features planned for gopatch.\n\n- Contextual matching: match context (like a function declaration), and then\n  run a transformation inside the function body repeatedly, at any depth. [#11]\n- Collateral changes: Match and capture values in one patch, and use those in\n  a following patch in the same file.\n- Metavariable constraints: Specify constraints on metavariables, e.g.\n  matching a string, or part of another metavariable.\n- Condition elision: An elision should match only if a specified condition is\n  also true.\n\n  [#11]: https://github.com/uber-go/gopatch/issues/11\n\n# Contributing\n\nIf you'd like to contribute to gopatch, you may find the following documents\nuseful:\n\n- [HACKING](docs/HACKING.md) documents the architecture, code organization, and\n  other information necessary to contribute to the project.\n- [RELEASE](docs/RELEASE.md) documents the process for releasing a new version\n  of gopatch.\n\n# Similar Projects\n\n- [rf] is a refactoring tool with a custom DSL\n- [gofmt rewrite rules] support simple transformations on expressions\n- [eg] supports basic example-based refactoring\n- [Coccinelle] is a tool for C from which gopatch takes inspiration heavily\n- [Semgrep] is a cross-language semantic search tool\n- [Comby] is a language-agnostic search and transformation tool\n\n  [gofmt rewrite rules]: https://golang.org/cmd/gofmt/\n  [eg]: https://godoc.org/golang.org/x/tools/cmd/eg\n  [Coccinelle]: https://coccinelle.gitlabpages.inria.fr/website/\n  [Semgrep]: https://semgrep.dev/\n  [Comby]: https://comby.dev/\n  [rf]: https://github.com/rsc/rf\n\n# Credits\n\ngopatch is heavily inspired by [Coccinelle].\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fuber-go%2Fgopatch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fuber-go%2Fgopatch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fuber-go%2Fgopatch/lists"}